You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
244 lines
11 KiB
244 lines
11 KiB
using System;
|
|
using System.Diagnostics;
|
|
using System.Collections.Generic;
|
|
using Unity.Collections;
|
|
|
|
using CellStreamingScratchBuffer = UnityEngine.Rendering.ProbeReferenceVolume.CellStreamingScratchBuffer;
|
|
using CellStreamingScratchBufferLayout = UnityEngine.Rendering.ProbeReferenceVolume.CellStreamingScratchBufferLayout;
|
|
|
|
namespace UnityEngine.Rendering
|
|
{
|
|
class ProbeVolumeScratchBufferPool
|
|
{
|
|
[DebuggerDisplay("ChunkCount = {chunkCount} ElementCount = {pool.Count}")]
|
|
class ScratchBufferPool
|
|
: IComparable<ScratchBufferPool>
|
|
{
|
|
public int chunkCount = -1;
|
|
public Stack<CellStreamingScratchBuffer> pool = new Stack<CellStreamingScratchBuffer>();
|
|
|
|
public ScratchBufferPool(int chunkCount)
|
|
{
|
|
this.chunkCount = chunkCount;
|
|
}
|
|
|
|
private ScratchBufferPool()
|
|
{
|
|
|
|
}
|
|
|
|
public int CompareTo(ScratchBufferPool other)
|
|
{
|
|
if (chunkCount < other.chunkCount)
|
|
return -1;
|
|
else if (chunkCount > other.chunkCount)
|
|
return 1;
|
|
else
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
// Size in bytes of a full SH chunk (L0+L1+L2+ValidityMask)
|
|
public int chunkSize { get; private set; }
|
|
// Maximum number of chunks that should allocated in total. This is a hint, size may differ a bit to ensure at least one scratch buffer for each required size.
|
|
public int maxChunkCount { get; private set; }
|
|
public int allocatedMemory => chunkSize * m_CurrentlyAllocatedChunkCount;
|
|
|
|
int m_L0Size;
|
|
int m_L1Size;
|
|
int m_ValiditySize;
|
|
int m_ValidityLayerCount;
|
|
int m_L2Size;
|
|
int m_SkyOcclusionSize;
|
|
int m_SkyShadingDirectionSize;
|
|
|
|
int m_CurrentlyAllocatedChunkCount = 0;
|
|
// List and not a Dictionary because we need the list sorted.
|
|
List<ScratchBufferPool> m_Pools = new List<ScratchBufferPool>();
|
|
// We store layouts separately because we might use a bigger buffer than required but we still want the layout to match the exact chunk count.
|
|
Dictionary<int, CellStreamingScratchBufferLayout> m_Layouts = new Dictionary<int, CellStreamingScratchBufferLayout>();
|
|
|
|
public ProbeVolumeScratchBufferPool(ProbeVolumeBakingSet bakingSet, ProbeVolumeSHBands shBands)
|
|
{
|
|
chunkSize = bakingSet.GetChunkGPUMemory(shBands);
|
|
maxChunkCount = bakingSet.maxSHChunkCount;
|
|
|
|
m_L0Size = bakingSet.L0ChunkSize;
|
|
m_L1Size = bakingSet.L1ChunkSize;
|
|
m_ValiditySize = bakingSet.sharedValidityMaskChunkSize;
|
|
m_ValidityLayerCount = bakingSet.bakedMaskCount;
|
|
m_SkyOcclusionSize = bakingSet.sharedSkyOcclusionL0L1ChunkSize;
|
|
m_SkyShadingDirectionSize = bakingSet.sharedSkyShadingDirectionIndicesChunkSize;
|
|
m_L2Size = bakingSet.L2TextureChunkSize;
|
|
}
|
|
|
|
CellStreamingScratchBufferLayout GetOrCreateScratchBufferLayout(int chunkCount)
|
|
{
|
|
if (m_Layouts.TryGetValue(chunkCount, out var layout))
|
|
{
|
|
return layout;
|
|
}
|
|
else
|
|
{
|
|
var bufferLayout = new CellStreamingScratchBufferLayout();
|
|
bufferLayout._L0Size = m_L0Size;
|
|
bufferLayout._L1Size = m_L1Size;
|
|
bufferLayout._ValiditySize = m_ValiditySize;
|
|
bufferLayout._ValidityProbeSize = m_ValidityLayerCount; // 1layer => 1xbyte => 4 probes at a time.
|
|
if (m_SkyOcclusionSize != 0)
|
|
{
|
|
bufferLayout._SkyOcclusionSize = m_SkyOcclusionSize;
|
|
bufferLayout._SkyOcclusionProbeSize = 8; // 4xFP16
|
|
|
|
if (m_SkyShadingDirectionSize != 0)
|
|
{
|
|
bufferLayout._SkyShadingDirectionSize = m_SkyShadingDirectionSize;
|
|
bufferLayout._SkyShadingDirectionProbeSize = 1; // 1xbyte => 4 probes at a time.
|
|
}
|
|
else
|
|
{
|
|
bufferLayout._SkyShadingDirectionSize = 0;
|
|
bufferLayout._SkyShadingDirectionProbeSize = 0;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
bufferLayout._SkyOcclusionSize = 0;
|
|
bufferLayout._SkyOcclusionProbeSize = 0;
|
|
|
|
bufferLayout._SkyShadingDirectionSize = 0;
|
|
bufferLayout._SkyShadingDirectionProbeSize = 0;
|
|
}
|
|
bufferLayout._L2Size = m_L2Size;
|
|
|
|
// TODO: Find a generic way to pass this down? (depends on the gfx format we use).
|
|
bufferLayout._L0ProbeSize = 8; // 4xFP16
|
|
bufferLayout._L1ProbeSize = 4; // 4xbyte
|
|
bufferLayout._L2ProbeSize = 4; // 4xbyte
|
|
|
|
int destChunksSize = chunkCount * sizeof(uint) * 4; // 1 Chunk == Vector4Int
|
|
// First destination chunks at offset 0 (no explicit member for this).
|
|
// Then, shared data destination chunks. Can be different from SH data destination in case of blending
|
|
// (one pool for blending and one other pool for shared data and blending destination).
|
|
bufferLayout._SharedDestChunksOffset = destChunksSize;
|
|
bufferLayout._L0L1rxOffset = bufferLayout._SharedDestChunksOffset + destChunksSize;
|
|
bufferLayout._L1GryOffset = bufferLayout._L0L1rxOffset + m_L0Size * chunkCount;
|
|
bufferLayout._L1BrzOffset = bufferLayout._L1GryOffset + m_L1Size * chunkCount;
|
|
bufferLayout._ValidityOffset = bufferLayout._L1BrzOffset + m_L1Size * chunkCount;
|
|
bufferLayout._SkyOcclusionOffset = bufferLayout._ValidityOffset + m_ValiditySize * chunkCount;
|
|
bufferLayout._SkyShadingDirectionOffset = bufferLayout._SkyOcclusionOffset + m_SkyOcclusionSize * chunkCount;
|
|
bufferLayout._L2_0Offset = bufferLayout._SkyShadingDirectionOffset + m_SkyShadingDirectionSize * chunkCount;
|
|
bufferLayout._L2_1Offset = bufferLayout._L2_0Offset + m_L2Size * chunkCount;
|
|
bufferLayout._L2_2Offset = bufferLayout._L2_1Offset + m_L2Size * chunkCount;
|
|
bufferLayout._L2_3Offset = bufferLayout._L2_2Offset + m_L2Size * chunkCount;
|
|
|
|
bufferLayout._ProbeCountInChunkLine = ProbeBrickPool.kChunkProbeCountPerDim;
|
|
bufferLayout._ProbeCountInChunkSlice = ProbeBrickPool.kChunkProbeCountPerDim * ProbeBrickPool.kBrickProbeCountPerDim;
|
|
|
|
m_Layouts.Add(chunkCount, bufferLayout);
|
|
return bufferLayout;
|
|
}
|
|
}
|
|
|
|
CellStreamingScratchBuffer CreateScratchBuffer(int chunkCount, bool allocateGraphicsBuffers)
|
|
{
|
|
var scratchBuffer = new CellStreamingScratchBuffer(chunkCount, chunkSize, allocateGraphicsBuffers);
|
|
m_CurrentlyAllocatedChunkCount += chunkCount;
|
|
|
|
return scratchBuffer;
|
|
}
|
|
|
|
// Used to avoid gcalloc in m_Pools.FindIndex
|
|
static int s_ChunkCount;
|
|
|
|
// Will return a the smallest GraphicsBuffer with at least enough space for chunkCount chunks.
|
|
public bool AllocateScratchBuffer(int chunkCount, out CellStreamingScratchBuffer scratchBuffer, out CellStreamingScratchBufferLayout layout, bool allocateGraphicsBuffers)
|
|
{
|
|
s_ChunkCount = chunkCount;
|
|
int index = m_Pools.FindIndex(0, (o) => o.chunkCount == s_ChunkCount);
|
|
// The size of buffer we return may not be the exact requested size (can be bigger).
|
|
// So we need to make sure the layout is the right one for the number of requested chunks.
|
|
layout = GetOrCreateScratchBufferLayout(chunkCount);
|
|
|
|
if (index != -1)
|
|
{
|
|
var pool = m_Pools[index].pool;
|
|
// If one is available, return it.
|
|
if (pool.Count > 0)
|
|
{
|
|
scratchBuffer = pool.Pop();
|
|
scratchBuffer.Swap();
|
|
return true;
|
|
}
|
|
else
|
|
{
|
|
// If none are available of the exact size, try to find a bigger buffer instead.
|
|
// We won't go above twice the size to avoid "wasting" bigger buffers for requests that are too small.
|
|
for (int i = index; i < m_Pools.Count; ++i)
|
|
{
|
|
var biggerPool = m_Pools[i];
|
|
// Next existing pools scratch size are too big. Break out of the loop.
|
|
// We don't want to hog a big buffer for a small request.
|
|
if (biggerPool.chunkCount >= (chunkCount * 2))
|
|
break;
|
|
else if (biggerPool.pool.Count > 0)
|
|
{
|
|
scratchBuffer = biggerPool.pool.Pop();
|
|
scratchBuffer.Swap();
|
|
return true;
|
|
}
|
|
}
|
|
|
|
// We did not find any suitable buffer. Create a new one with the exact requested size.
|
|
if ((m_CurrentlyAllocatedChunkCount + chunkCount) < maxChunkCount)
|
|
{
|
|
scratchBuffer = CreateScratchBuffer(chunkCount, allocateGraphicsBuffers);
|
|
return true;
|
|
}
|
|
// If we are above the maximum, we don't allocate more buffers.
|
|
// It's ok since we know at least one buffer will exist for any given chunkCount so we won't get impossible to fulfill requests.
|
|
else
|
|
{
|
|
scratchBuffer = null;
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// No pool of this size exists. Create a new pool of that size and return a new buffer;
|
|
ScratchBufferPool newPool = new ScratchBufferPool(chunkCount);
|
|
m_Pools.Add(newPool);
|
|
m_Pools.Sort();
|
|
|
|
scratchBuffer = CreateScratchBuffer(chunkCount, allocateGraphicsBuffers);
|
|
return true;
|
|
}
|
|
}
|
|
|
|
public void ReleaseScratchBuffer(CellStreamingScratchBuffer scratchBuffer)
|
|
{
|
|
s_ChunkCount = scratchBuffer.chunkCount;
|
|
var pool = m_Pools.Find((o) => o.chunkCount == s_ChunkCount);
|
|
Debug.Assert(pool != null);
|
|
pool.pool.Push(scratchBuffer);
|
|
}
|
|
|
|
public void Cleanup()
|
|
{
|
|
foreach (var pool in m_Pools)
|
|
{
|
|
while(pool.pool.Count > 0)
|
|
{
|
|
var scratchBuffer = pool.pool.Pop();
|
|
scratchBuffer.Dispose();
|
|
}
|
|
}
|
|
|
|
m_Pools.Clear();
|
|
m_CurrentlyAllocatedChunkCount = 0;
|
|
chunkSize = 0;
|
|
maxChunkCount = 0;
|
|
}
|
|
}
|
|
}
|