232 lines
9.3 KiB
C#
232 lines
9.3 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
using UnityEngine.Experimental.Rendering.RenderGraphModule;
|
|
|
|
namespace UnityEngine.Rendering.HighDefinition
|
|
{
|
|
partial class HDDynamicShadowAtlas : HDShadowAtlas
|
|
{
|
|
readonly List<HDShadowResolutionRequest> m_ShadowResolutionRequests = new List<HDShadowResolutionRequest>();
|
|
readonly List<HDShadowRequest> m_MixedRequestsPendingBlits = new List<HDShadowRequest>();
|
|
|
|
float m_RcpScaleFactor = 1;
|
|
HDShadowResolutionRequest[] m_SortedRequestsCache;
|
|
|
|
public HDDynamicShadowAtlas(HDShadowAtlasInitParameters atlaInitParams)
|
|
: base(atlaInitParams)
|
|
{
|
|
m_SortedRequestsCache = new HDShadowResolutionRequest[Mathf.CeilToInt(atlaInitParams.maxShadowRequests)];
|
|
}
|
|
|
|
internal void ReserveResolution(HDShadowResolutionRequest shadowRequest)
|
|
{
|
|
m_ShadowResolutionRequests.Add(shadowRequest);
|
|
}
|
|
|
|
// Stable (unlike List.Sort) sorting algorithm which, unlike Linq's, doesn't use JIT (lol).
|
|
// Sorts in place. Very efficient (O(n)) for already sorted data.
|
|
void InsertionSort(HDShadowResolutionRequest[] array, int startIndex, int lastIndex)
|
|
{
|
|
int i = startIndex + 1;
|
|
|
|
while (i < lastIndex)
|
|
{
|
|
var curr = array[i];
|
|
|
|
int j = i - 1;
|
|
|
|
// Sort in descending order.
|
|
while ((j >= 0) && ((curr.resolution.x > array[j].resolution.x) ||
|
|
(curr.resolution.y > array[j].resolution.y)))
|
|
{
|
|
array[j + 1] = array[j];
|
|
j--;
|
|
}
|
|
|
|
array[j + 1] = curr;
|
|
i++;
|
|
}
|
|
}
|
|
|
|
private bool AtlasLayout(bool allowResize, HDShadowResolutionRequest[] fullShadowList, int requestsCount)
|
|
{
|
|
float curX = 0, curY = 0, curH = 0, xMax = width, yMax = height;
|
|
m_RcpScaleFactor = 1;
|
|
for (int i = 0; i < requestsCount; ++i)
|
|
{
|
|
var shadowRequest = fullShadowList[i];
|
|
// shadow atlas layouting
|
|
Rect viewport = new Rect(Vector2.zero, shadowRequest.resolution);
|
|
curH = Mathf.Max(curH, viewport.height);
|
|
|
|
if (curX + viewport.width > xMax)
|
|
{
|
|
curX = 0;
|
|
curY += curH;
|
|
curH = viewport.height;
|
|
}
|
|
if (curY + curH > yMax)
|
|
{
|
|
if (allowResize)
|
|
{
|
|
LayoutResize();
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
viewport.x = curX;
|
|
viewport.y = curY;
|
|
shadowRequest.dynamicAtlasViewport = viewport;
|
|
shadowRequest.resolution = viewport.size;
|
|
curX += viewport.width;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
internal bool Layout(bool allowResize = true)
|
|
{
|
|
int n = (m_ShadowResolutionRequests != null) ? m_ShadowResolutionRequests.Count : 0;
|
|
int i = 0;
|
|
for (; i < m_ShadowResolutionRequests.Count; ++i)
|
|
{
|
|
m_SortedRequestsCache[i] = m_ShadowResolutionRequests[i];
|
|
}
|
|
|
|
InsertionSort(m_SortedRequestsCache, 0, i);
|
|
|
|
return AtlasLayout(allowResize, m_SortedRequestsCache, requestsCount: i);
|
|
}
|
|
|
|
void LayoutResize()
|
|
{
|
|
int index = 0;
|
|
float currentX = 0;
|
|
float currentY = 0;
|
|
float currentMaxY = 0;
|
|
float currentMaxX = 0;
|
|
|
|
// Place shadows in a square shape
|
|
while (index < m_ShadowResolutionRequests.Count)
|
|
{
|
|
float y = 0;
|
|
float currentMaxXCache = currentMaxX;
|
|
do
|
|
{
|
|
Rect r = new Rect(Vector2.zero, m_ShadowResolutionRequests[index].resolution);
|
|
r.x = currentMaxX;
|
|
r.y = y;
|
|
y += r.height;
|
|
currentY = Mathf.Max(currentY, y);
|
|
currentMaxXCache = Mathf.Max(currentMaxXCache, currentMaxX + r.width);
|
|
m_ShadowResolutionRequests[index].dynamicAtlasViewport = r;
|
|
index++;
|
|
}
|
|
while (y < currentMaxY && index < m_ShadowResolutionRequests.Count);
|
|
currentMaxY = Mathf.Max(currentMaxY, currentY);
|
|
currentMaxX = currentMaxXCache;
|
|
if (index >= m_ShadowResolutionRequests.Count)
|
|
continue;
|
|
float x = 0;
|
|
float currentMaxYCache = currentMaxY;
|
|
do
|
|
{
|
|
Rect r = new Rect(Vector2.zero, m_ShadowResolutionRequests[index].resolution);
|
|
r.x = x;
|
|
r.y = currentMaxY;
|
|
x += r.width;
|
|
currentX = Mathf.Max(currentX, x);
|
|
currentMaxYCache = Mathf.Max(currentMaxYCache, currentMaxY + r.height);
|
|
m_ShadowResolutionRequests[index].dynamicAtlasViewport = r;
|
|
index++;
|
|
}
|
|
while (x < currentMaxX && index < m_ShadowResolutionRequests.Count);
|
|
currentMaxX = Mathf.Max(currentMaxX, currentX);
|
|
currentMaxY = currentMaxYCache;
|
|
}
|
|
|
|
float maxResolution = Math.Max(currentMaxX, currentMaxY);
|
|
Vector4 scale = new Vector4(width / maxResolution, height / maxResolution, width / maxResolution, height / maxResolution);
|
|
m_RcpScaleFactor = Mathf.Min(scale.x, scale.y);
|
|
|
|
// Scale down every shadow rects to fit with the current atlas size
|
|
foreach (var r in m_ShadowResolutionRequests)
|
|
{
|
|
Vector4 s = new Vector4(r.dynamicAtlasViewport.x, r.dynamicAtlasViewport.y, r.dynamicAtlasViewport.width, r.dynamicAtlasViewport.height);
|
|
Vector4 reScaled = Vector4.Scale(s, scale);
|
|
|
|
r.dynamicAtlasViewport = new Rect(reScaled.x, reScaled.y, reScaled.z, reScaled.w);
|
|
r.resolution = r.dynamicAtlasViewport.size;
|
|
}
|
|
}
|
|
|
|
public void DisplayAtlas(RTHandle atlasTexture, CommandBuffer cmd, Material debugMaterial, Rect atlasViewport, float screenX, float screenY, float screenSizeX, float screenSizeY, float minValue, float maxValue, MaterialPropertyBlock mpb)
|
|
{
|
|
base.DisplayAtlas(atlasTexture, cmd, debugMaterial, atlasViewport, screenX, screenY, screenSizeX, screenSizeY, minValue, maxValue, mpb, m_RcpScaleFactor);
|
|
}
|
|
|
|
public void AddRequestToPendingBlitFromCache(HDShadowRequest request)
|
|
{
|
|
if (request.isMixedCached)
|
|
m_MixedRequestsPendingBlits.Add(request);
|
|
}
|
|
|
|
public void ClearPendingBlitsRequests()
|
|
{
|
|
m_MixedRequestsPendingBlits.Clear();
|
|
}
|
|
|
|
public bool HasPendingBlitsRequests()
|
|
{
|
|
return m_MixedRequestsPendingBlits.Count > 0;
|
|
}
|
|
|
|
internal struct ShadowBlitParameters
|
|
{
|
|
public List<HDShadowRequest> requestsWaitingBlits;
|
|
public Material blitMaterial;
|
|
public MaterialPropertyBlock blitMaterialPropertyBlock;
|
|
public Vector2Int cachedShadowAtlasSize;
|
|
}
|
|
|
|
internal ShadowBlitParameters PrepareShadowBlitParameters(HDCachedShadowAtlas cachedAtlas, Material blitMaterial, MaterialPropertyBlock blitMpb)
|
|
{
|
|
ShadowBlitParameters parameters = new ShadowBlitParameters();
|
|
parameters.requestsWaitingBlits = m_MixedRequestsPendingBlits;
|
|
parameters.blitMaterial = blitMaterial;
|
|
parameters.blitMaterialPropertyBlock = blitMpb;
|
|
parameters.cachedShadowAtlasSize = new Vector2Int(cachedAtlas.width, cachedAtlas.height);
|
|
return parameters;
|
|
}
|
|
|
|
static internal void BlitCachedIntoAtlas(in ShadowBlitParameters parameters, RTHandle dynamicTexture, RTHandle cachedTexture, CommandBuffer cmd)
|
|
{
|
|
foreach (var request in parameters.requestsWaitingBlits)
|
|
{
|
|
cmd.SetRenderTarget(dynamicTexture);
|
|
|
|
cmd.SetViewport(request.dynamicAtlasViewport);
|
|
|
|
Vector4 sourceScaleBias = new Vector4(request.cachedAtlasViewport.width / parameters.cachedShadowAtlasSize.x,
|
|
request.cachedAtlasViewport.height / parameters.cachedShadowAtlasSize.y,
|
|
request.cachedAtlasViewport.x / parameters.cachedShadowAtlasSize.x,
|
|
request.cachedAtlasViewport.y / parameters.cachedShadowAtlasSize.y);
|
|
|
|
parameters.blitMaterialPropertyBlock.SetTexture(HDShaderIDs._CachedShadowmapAtlas, cachedTexture);
|
|
parameters.blitMaterialPropertyBlock.SetVector(HDShaderIDs._BlitScaleBias, sourceScaleBias);
|
|
CoreUtils.DrawFullScreen(cmd, parameters.blitMaterial, parameters.blitMaterialPropertyBlock, 0);
|
|
}
|
|
|
|
parameters.requestsWaitingBlits.Clear();
|
|
}
|
|
|
|
public override void Clear()
|
|
{
|
|
base.Clear();
|
|
m_ShadowResolutionRequests.Clear();
|
|
m_MixedRequestsPendingBlits.Clear();
|
|
}
|
|
}
|
|
}
|