diff --git a/com.unity.render-pipelines.core/Runtime/BatchRenderer/RenderBRG.cs b/com.unity.render-pipelines.core/Runtime/BatchRenderer/RenderBRG.cs index 4d21f24a2ec..1beeb5cb21a 100644 --- a/com.unity.render-pipelines.core/Runtime/BatchRenderer/RenderBRG.cs +++ b/com.unity.render-pipelines.core/Runtime/BatchRenderer/RenderBRG.cs @@ -11,6 +11,7 @@ using Unity.Collections.LowLevel.Unsafe; using Unity.Jobs; using Unity.Mathematics; +using UnityEngine.Assertions; using UnityEngine.SceneManagement; namespace UnityEngine.Rendering @@ -125,6 +126,13 @@ static float4 GetSHC(SphericalHarmonicsL2 sh) } } + struct DeferredMaterialInstance + { + public int instanceIndex; + public int meshHashCode; + public GeometryPoolHandle geoPoolHandle; + } + unsafe class SceneBRG { private BatchRendererGroup m_BatchRendererGroup; @@ -145,10 +153,13 @@ unsafe class SceneBRG private NativeArray m_renderers; private int m_transformVec4InstanceBufferOffsetL2W; private int m_transformVec4InstanceBufferOffsetW2L; + private NativeList m_deferredMaterialInstances; private LightMaps m_Lightmaps; - BRGTransformUpdater m_BRGTransformUpdater = new BRGTransformUpdater(); + private BRGTransformUpdater m_BRGTransformUpdater = new BRGTransformUpdater(); + private GeometryPool m_GlobalGeoPool = null; + private List m_AddedRenderers; @@ -463,8 +474,90 @@ public JobHandle OnPerformCulling(BatchRendererGroup rendererGroup, BatchCulling return jobHandleOutput; } + private void ProcessUsedMeshAndMaterialDataFromGameObjects( + RenderPipelineAsset pipelineAsset, + int instanceIndex, + MeshRenderer renderer, + MeshFilter meshFilter, + Dictionary, Material> rendererMaterialInfos, + int deferredMaterialBufferOffset, + NativeArray deferredMaterialBuffer, + ref Mesh outMesh, + List outSubmeshIndices, + List outMaterials) + { + outSubmeshIndices.Clear(); + outMaterials.Clear(); + + var sharedMaterials = new List(); + var startSubMesh = renderer.subMeshStartIndex; + renderer.GetSharedMaterials(sharedMaterials); + Material overrideMaterial = null; + int overrideCounts = 0; + for (int matIndex = 0; matIndex < sharedMaterials.Count; ++matIndex) + { + Material matToUse; + if (!rendererMaterialInfos.TryGetValue(new Tuple(renderer, matIndex), out matToUse)) + matToUse = sharedMaterials[matIndex]; + + int targetSubmeshIndex = (int)(startSubMesh + matIndex); + if (pipelineAsset != null) + { + RenderPipelineAsset.VisibilityMaterialRendererInfo visMaterialInfo = pipelineAsset.GetVisibilityMaterialInfoForRenderer(new RenderPipelineAsset.GetVisibilityMaterialInfoForRendererArgs() + { + renderer = renderer, + submeshIndex = targetSubmeshIndex, + material = matToUse + }); + + if (visMaterialInfo.supportsVisibility && visMaterialInfo.materialOverride != null) + { + Assert.IsTrue( + overrideMaterial == null || overrideMaterial == visMaterialInfo.materialOverride, + "RenderBRG only supports one and only 1 override for an entire renderer."); + ++overrideCounts; + overrideMaterial = visMaterialInfo.materialOverride; + } + } + + outSubmeshIndices.Add(targetSubmeshIndex); + outMaterials.Add(matToUse); + } + + outMesh = meshFilter.sharedMesh; + + //Special case, if the renderer qualifies for deferred materials, go for it! + //TODO: for now we just handle 1 case, if the entire renderer can be deferred material. + if (overrideMaterial != null && overrideCounts == outMaterials.Count && m_GlobalGeoPool != null) + { + m_GlobalGeoPool.Register(outMesh, out GeometryPoolHandle geoPoolHandle); + if (!geoPoolHandle.valid) + return; + + if (!m_deferredMaterialInstances.IsCreated) + m_deferredMaterialInstances = new NativeList(1024, Allocator.Persistent); + + m_deferredMaterialInstances.Add(new DeferredMaterialInstance() + { + instanceIndex = instanceIndex, + meshHashCode = outMesh.GetHashCode(), + geoPoolHandle = geoPoolHandle + }); + + deferredMaterialBuffer[deferredMaterialBufferOffset + instanceIndex] = new Vector4((float)geoPoolHandle.index, 0.0f, 0.0f, 0.0f); + + //We succeeded! lets override the mesh / submesh index and material. + outSubmeshIndices.Clear(); + outMaterials.Clear(); + outMaterials.Add(overrideMaterial); + outSubmeshIndices.Add(geoPoolHandle.index); + outMesh = m_GlobalGeoPool.globalMesh; + + } + } + // Start is called before the first frame update - public void Initialize(List renderers) + public void Initialize(List renderers, GeometryPool geometryPool) { m_BatchRendererGroup = new BatchRendererGroup(this.OnPerformCulling, IntPtr.Zero); m_BRGTransformUpdater.Initialize(); @@ -480,6 +573,9 @@ public void Initialize(List renderers) m_drawBatches = new NativeList(Allocator.Persistent); m_drawRanges = new NativeList(Allocator.Persistent); m_AddedRenderers = new List(renderers.Count); + m_GlobalGeoPool = geometryPool; + + RenderPipelineAsset renderPipelineAsset = GraphicsSettings.renderPipelineAsset; // Fill the GPU-persistent scene data ComputeBuffer int bigDataBufferVector4Count = @@ -489,7 +585,9 @@ public void Initialize(List renderers) + 7 * m_renderers.Length /*per renderer SH*/ + 1 * m_renderers.Length /*per renderer probe occlusion*/ + 2 * m_renderers.Length /* per renderer lightmapindex + scale/offset*/ - + m_renderers.Length * 3 * 2 /*per renderer 4x3 matrix+inverse*/; + + m_renderers.Length * 3 * 2 /*per renderer 4x3 matrix+inverse*/ + + 1 * m_renderers.Length; /*per renderer, vec4 with deferredMaterialData*/ + var vectorBuffer = new NativeArray(bigDataBufferVector4Count, Allocator.Temp); // First 4xfloat4 of ComputeBuffer needed to be zero filled for default property fall back! @@ -516,6 +614,7 @@ public void Initialize(List renderers) var lightMapScaleOffset = lightMapIndexOffset + m_renderers.Length; var localToWorldOffset = lightMapScaleOffset + m_renderers.Length; var worldToLocalOffset = localToWorldOffset + m_renderers.Length * 3; + var deferredMaterialDataOffset = worldToLocalOffset + m_renderers.Length * 3; m_transformVec4InstanceBufferOffsetL2W = localToWorldOffset; //this will be used by the transform updated later. m_transformVec4InstanceBufferOffsetW2L = worldToLocalOffset; //this will be used by the transform updated later. @@ -593,7 +692,15 @@ public void Initialize(List renderers) var transformedBounds = AABB.Transform(m, meshFilter.sharedMesh.bounds.ToAABB()); m_renderers[i] = new DrawRenderer { bounds = transformedBounds }; - var mesh = m_BatchRendererGroup.RegisterMesh(meshFilter.sharedMesh); + Mesh usedMesh = null; + var usedSubmeshIndices = new List(); + var usedMaterials = new List(); + ProcessUsedMeshAndMaterialDataFromGameObjects( + renderPipelineAsset, i, renderer, meshFilter, rendererMaterialInfos, + deferredMaterialDataOffset, vectorBuffer, + ref usedMesh, usedSubmeshIndices, usedMaterials); + + var mesh = m_BatchRendererGroup.RegisterMesh(usedMesh); // Different renderer settings? -> new draw range var rangeKey = new RangeKey @@ -620,14 +727,9 @@ public void Initialize(List renderers) } // Sub-meshes... - var sharedMaterials = new List(); - renderer.GetSharedMaterials(sharedMaterials); - var startSubMesh = renderer.subMeshStartIndex; - for (int matIndex = 0; matIndex < sharedMaterials.Count; matIndex++) + for (int matIndex = 0; matIndex < usedMaterials.Count; matIndex++) { - Material matToUse; - if (!rendererMaterialInfos.TryGetValue(new Tuple(renderer, matIndex), out matToUse)) - matToUse = sharedMaterials[matIndex]; + Material matToUse = usedMaterials[matIndex]; var material = m_BatchRendererGroup.RegisterMaterial(matToUse); @@ -642,7 +744,7 @@ public void Initialize(List renderers) { material = material, meshID = mesh, - submeshIndex = (uint)(matIndex + startSubMesh), + submeshIndex = (uint)usedSubmeshIndices[matIndex], flags = flags, range = rangeKey }; @@ -671,6 +773,9 @@ public void Initialize(List renderers) } } + if (m_GlobalGeoPool != null) + m_GlobalGeoPool.SendGpuCommands(); + m_GPUPersistentInstanceData = new GraphicsBuffer(GraphicsBuffer.Target.Raw, (int)bigDataBufferVector4Count * 16 / 4, 4); m_GPUPersistentInstanceData.SetData(vectorBuffer); @@ -758,8 +863,9 @@ public void Initialize(List renderers) int SHBgID = Shader.PropertyToID("unity_SHBg"); int SHBbID = Shader.PropertyToID("unity_SHBb"); int SHCID = Shader.PropertyToID("unity_SHC"); + int deferredMaterialInstanceDataID = Shader.PropertyToID("_DeferredMaterialInstanceData"); - var batchMetadata = new NativeArray(13, Allocator.Temp); + var batchMetadata = new NativeArray(14, Allocator.Temp); batchMetadata[0] = CreateMetadataValue(objectToWorldID, localToWorldOffset * UnsafeUtility.SizeOf(), true); batchMetadata[1] = CreateMetadataValue(worldToObjectID, worldToLocalOffset * UnsafeUtility.SizeOf(), true); batchMetadata[2] = CreateMetadataValue(lightmapSTID, lightMapScaleOffset * UnsafeUtility.SizeOf(), true); @@ -773,6 +879,7 @@ public void Initialize(List renderers) batchMetadata[10] = CreateMetadataValue(SHBgID, SHBgOffset * UnsafeUtility.SizeOf(), true); batchMetadata[11] = CreateMetadataValue(SHBbID, SHBbOffset * UnsafeUtility.SizeOf(), true); batchMetadata[12] = CreateMetadataValue(SHCID, SHCOffset * UnsafeUtility.SizeOf(), true); + batchMetadata[13] = CreateMetadataValue(deferredMaterialInstanceDataID, deferredMaterialDataOffset * UnsafeUtility.SizeOf(), true); // Register batch m_batchID = m_BatchRendererGroup.AddBatch(batchMetadata, m_GPUPersistentInstanceData.bufferHandle); @@ -821,6 +928,16 @@ public void Destroy() if (added != null) added.forceRenderingOff = false; } + + if (m_deferredMaterialInstances.IsCreated) + { + foreach (var deferredInstance in m_deferredMaterialInstances) + { + m_GlobalGeoPool.UnregisterByMeshHashCode(deferredInstance.meshHashCode); + } + m_GlobalGeoPool.SendGpuCommands(); + m_deferredMaterialInstances.Dispose(); + } } } } @@ -831,8 +948,30 @@ public class RenderBRG : MonoBehaviour private Dictionary m_Scenes = new(); private CommandBuffer m_gpuCmdBuffer; + public bool EnableDeferredMaterials = false; + private GeometryPool m_GlobalGeoPool; + + public static GeometryPool FindGlobalGeometryPool() + { + RenderBRG[] brgers = Resources.FindObjectsOfTypeAll(); + if (brgers == null) + return null; + + foreach (var brg in brgers) + { + if (brg.m_GlobalGeoPool != null) + return brg.m_GlobalGeoPool; + } + + return null; + } + private void OnEnable() { + var globalGeoPool = FindGlobalGeometryPool(); + if (EnableDeferredMaterials && globalGeoPool == null && m_GlobalGeoPool == null) + m_GlobalGeoPool = new GeometryPool(GeometryPoolDesc.NewDefault()); + m_gpuCmdBuffer = new CommandBuffer(); SceneManager.sceneLoaded += OnSceneLoaded; SceneManager.sceneUnloaded += OnSceneUnloaded; @@ -909,7 +1048,7 @@ private void OnSceneLoaded(Scene scene, LoadSceneMode mode) Debug.Log("Loading scene: " + scene.name); #endif SceneBRG brg = new SceneBRG(); - brg.Initialize(renderers); + brg.Initialize(renderers, RenderBRG.FindGlobalGeometryPool()); m_Scenes[scene] = brg; } @@ -955,6 +1094,12 @@ private void OnDestroy() scene.Value?.Destroy(); m_Scenes.Clear(); + + if (m_GlobalGeoPool != null) + { + m_GlobalGeoPool.Dispose(); + m_GlobalGeoPool = null; + } } } } diff --git a/com.unity.render-pipelines.core/Runtime/Common/BlockAllocator.cs b/com.unity.render-pipelines.core/Runtime/Common/BlockAllocator.cs new file mode 100644 index 00000000000..44cec6c5707 --- /dev/null +++ b/com.unity.render-pipelines.core/Runtime/Common/BlockAllocator.cs @@ -0,0 +1,161 @@ +using Unity.Collections; + +namespace UnityEngine.Rendering +{ + public struct BlockAllocator + { + public struct Block + { + public int offset; + public int count; + + public static Block Invalid = new Block() { offset = 0, count = 0 }; + } + + public struct Allocation + { + public int handle; + public Block block; + + public static Allocation Invalid = new Allocation() { handle = -1 }; + public bool valid => handle != -1; + } + + private int m_freeElementCount; + private NativeList m_freeBlocks; + private NativeList m_usedBlocks; + private NativeList m_freeSlots; + + public int freeElementsCount => m_freeElementCount; + public int freeBlocks => m_freeBlocks.Length; + + public void Initialize(int maxElementCounts) + { + m_freeElementCount = maxElementCounts; + + if (!m_freeBlocks.IsCreated) + m_freeBlocks = new NativeList(Allocator.Persistent); + else + m_freeBlocks.Clear(); + m_freeBlocks.Add(new Block() { offset = 0, count = m_freeElementCount }); + + if (!m_usedBlocks.IsCreated) + m_usedBlocks = new NativeList(Allocator.Persistent); + else + m_usedBlocks.Clear(); + + if (!m_freeSlots.IsCreated) + m_freeSlots = new NativeList(Allocator.Persistent); + else + m_freeSlots.Clear(); + } + + public void Dispose() + { + m_freeBlocks.Dispose(); + m_usedBlocks.Dispose(); + m_freeSlots.Dispose(); + } + + public Allocation Allocate(int elementCounts) + { + if (elementCounts > m_freeElementCount || m_freeBlocks.IsEmpty) + return Allocation.Invalid; + + int selectedBlock = -1; + int currentBlockCount = 0; + for (int b = 0; b < m_freeBlocks.Length; ++b) + { + Block block = m_freeBlocks[b]; + + //simple naive allocator, we find the smallest possible space to allocate in our blocks. + if (elementCounts <= block.count && (selectedBlock == -1 || block.count < currentBlockCount)) + { + currentBlockCount = block.count; + selectedBlock = b; + } + } + + if (selectedBlock == -1) + return Allocation.Invalid; + + Block allocationBlock = m_freeBlocks[selectedBlock]; + Block split = allocationBlock; + + split.offset += elementCounts; + split.count -= elementCounts; + allocationBlock.count = elementCounts; + + if (split.count > 0) + m_freeBlocks[selectedBlock] = split; + else + m_freeBlocks.RemoveAtSwapBack(selectedBlock); + + int allocationHandle; + if (m_freeSlots.IsEmpty) + { + allocationHandle = (int)m_usedBlocks.Length; + m_usedBlocks.Add(allocationBlock); + } + else + { + allocationHandle = m_freeSlots[m_freeSlots.Length - 1]; + m_freeSlots.RemoveAtSwapBack(m_freeSlots.Length - 1); + m_usedBlocks[allocationHandle] = allocationBlock; + } + + m_freeElementCount -= elementCounts; + return new Allocation() { handle = allocationHandle, block = allocationBlock }; + } + + private int MergeBlockFrontBack(int freeBlockId) + { + Block targetBlock = m_freeBlocks[freeBlockId]; + for (int i = 0; i < m_freeBlocks.Length; ++i) + { + if (i == freeBlockId) + continue; + + Block freeBlock = m_freeBlocks[i]; + bool mergeTargetBlock = false; + if (targetBlock.offset == (freeBlock.offset + freeBlock.count)) + { + freeBlock.count += targetBlock.count; + mergeTargetBlock = true; + } + else if (freeBlock.offset == (targetBlock.offset + targetBlock.count)) + { + freeBlock.offset = targetBlock.offset; + freeBlock.count += targetBlock.count; + mergeTargetBlock = true; + } + + if (mergeTargetBlock) + { + m_freeBlocks[i] = freeBlock; + m_freeBlocks.RemoveAtSwapBack(freeBlockId); + return i == m_freeBlocks.Length ? freeBlockId : i; + } + } + + return -1; + } + + public void FreeAllocation(in Allocation allocation) + { + if (!allocation.valid) + throw new System.Exception("Cannot free invalid allocation"); + + m_freeSlots.Add(allocation.handle); + m_usedBlocks[allocation.handle] = Block.Invalid; + + int blockToMerge = m_freeBlocks.Length; + m_freeBlocks.Add(allocation.block); + + while (blockToMerge != -1) + blockToMerge = MergeBlockFrontBack(blockToMerge); + + m_freeElementCount += allocation.block.count; + } + } +} diff --git a/com.unity.render-pipelines.core/Runtime/Common/BlockAllocator.cs.meta b/com.unity.render-pipelines.core/Runtime/Common/BlockAllocator.cs.meta new file mode 100644 index 00000000000..d6d38f24093 --- /dev/null +++ b/com.unity.render-pipelines.core/Runtime/Common/BlockAllocator.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 34eff423cb3584445be78a970b58e4e5 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/com.unity.render-pipelines.core/Runtime/GeometryPool.meta b/com.unity.render-pipelines.core/Runtime/GeometryPool.meta new file mode 100644 index 00000000000..fe03a2a073f --- /dev/null +++ b/com.unity.render-pipelines.core/Runtime/GeometryPool.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: fdee3e979e01d3842acf377c3dcfa178 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/com.unity.render-pipelines.core/Runtime/GeometryPool/GeometryPool.cs b/com.unity.render-pipelines.core/Runtime/GeometryPool/GeometryPool.cs new file mode 100644 index 00000000000..d95f7b58084 --- /dev/null +++ b/com.unity.render-pipelines.core/Runtime/GeometryPool/GeometryPool.cs @@ -0,0 +1,807 @@ +using System.Collections.Generic; +using Unity.Collections; +using UnityEngine.VFX; +using System.Diagnostics; +using System.Linq; +using UnityEngine.Experimental.GlobalIllumination; +using UnityEngine.Experimental.Rendering; +using UnityEngine.Experimental.Rendering.RenderGraphModule; +using UnityEngine.Rendering.RendererUtils; +using UnityEngine; +using UnityEngine.Rendering; + +namespace UnityEngine.Rendering +{ + public struct GeometryPoolDesc + { + public int vertexPoolByteSize; + public int indexPoolByteSize; + public int subMeshLookupPoolByteSize; + public int subMeshEntryPoolByteSize; + public int maxMeshes; + + public static GeometryPoolDesc NewDefault() + { + return new GeometryPoolDesc() + { + vertexPoolByteSize = 32 * 1024 * 1024, //32 mb + indexPoolByteSize = 16 * 1024 * 1024, //16 mb + subMeshLookupPoolByteSize = 3 * 1024 * 1024, // 3mb + subMeshEntryPoolByteSize = 2 * 1024 * 1024, // 2mb + maxMeshes = 4096 + }; + } + } + + public struct GeometryPoolHandle + { + public int index; + public static GeometryPoolHandle Invalid = new GeometryPoolHandle() { index = -1 }; + public bool valid => index != -1; + } + + public class GeometryPool + { + private static class GeoPoolShaderIDs + { + public static readonly int _InputIndexBuffer = Shader.PropertyToID("_InputIndexBuffer"); + public static readonly int _InputIBCount = Shader.PropertyToID("_InputIBCount"); + public static readonly int _OutputIBOffset = Shader.PropertyToID("_OutputIBOffset"); + public static readonly int _GeoHandle = Shader.PropertyToID("_GeoHandle"); + public static readonly int _GeoVertexOffset = Shader.PropertyToID("_GeoVertexOffset"); + public static readonly int _GeoIndexOffset = Shader.PropertyToID("_GeoIndexOffset"); + public static readonly int _GeoSubMeshLookupOffset = Shader.PropertyToID("_GeoSubMeshLookupOffset"); + public static readonly int _GeoSubMeshEntryOffset = Shader.PropertyToID("_GeoSubMeshEntryOffset"); + public static readonly int _InputVBCount = Shader.PropertyToID("_InputVBCount"); + public static readonly int _OutputVBSize = Shader.PropertyToID("_OutputVBSize"); + public static readonly int _OutputVBOffset = Shader.PropertyToID("_OutputVBOffset"); + public static readonly int _InputPosBufferStride = Shader.PropertyToID("_InputPosBufferStride"); + public static readonly int _InputPosBufferOffset = Shader.PropertyToID("_InputPosBufferOffset"); + public static readonly int _InputUv0BufferStride = Shader.PropertyToID("_InputUv0BufferStride"); + public static readonly int _InputUv0BufferOffset = Shader.PropertyToID("_InputUv0BufferOffset"); + public static readonly int _InputUv1BufferStride = Shader.PropertyToID("_InputUv1BufferStride"); + public static readonly int _InputUv1BufferOffset = Shader.PropertyToID("_InputUv1BufferOffset"); + public static readonly int _InputNormalBufferStride = Shader.PropertyToID("_InputNormalBufferStride"); + public static readonly int _InputNormalBufferOffset = Shader.PropertyToID("_InputNormalBufferOffset"); + public static readonly int _InputTangentBufferStride = Shader.PropertyToID("_InputTangentBufferStride"); + public static readonly int _InputTangentBufferOffset = Shader.PropertyToID("_InputTangentBufferOffset"); + public static readonly int _InputFlags = Shader.PropertyToID("_InputFlags"); + public static readonly int _PosBuffer = Shader.PropertyToID("_PosBuffer"); + public static readonly int _Uv0Buffer = Shader.PropertyToID("_Uv0Buffer"); + public static readonly int _Uv1Buffer = Shader.PropertyToID("_Uv1Buffer"); + public static readonly int _NormalBuffer = Shader.PropertyToID("_NormalBuffer"); + public static readonly int _TangentBuffer = Shader.PropertyToID("_TangentBuffer"); + public static readonly int _OutputIndexBuffer = Shader.PropertyToID("_OutputIndexBuffer"); + public static readonly int _OutputVB = Shader.PropertyToID("_OutputVB"); + public static readonly int _OutputGeoMetadataBuffer = Shader.PropertyToID("_OutputGeoMetadataBuffer"); + public static readonly int _GeoPoolGlobalVertexBuffer = Shader.PropertyToID("_GeoPoolGlobalVertexBuffer"); + public static readonly int _GeoPoolGlobalIndexBuffer = Shader.PropertyToID("_GeoPoolGlobalIndexBuffer"); + public static readonly int _GeoPoolGlobalMetadataBuffer = Shader.PropertyToID("_GeoPoolGlobalMetadataBuffer"); + public static readonly int _GeoPoolGlobalParams = Shader.PropertyToID("_GeoPoolGlobalParams"); + public static readonly int _OutputSubMeshLookupBuffer = Shader.PropertyToID("_OutputSubMeshLookupBuffer"); + public static readonly int _OutputSubMeshEntryBuffer = Shader.PropertyToID("_OutputSubMeshEntryBuffer"); + public static readonly int _InputSubMeshIndexStart = Shader.PropertyToID("_InputSubMeshIndexStart"); + public static readonly int _InputSubMeshIndexCount = Shader.PropertyToID("_InputSubMeshIndexCount"); + public static readonly int _InputSubMeshBaseVertex = Shader.PropertyToID("_InputSubMeshBaseVertex"); + public static readonly int _InputSubMeshDestIndex = Shader.PropertyToID("_InputSubMeshDestIndex"); + public static readonly int _InputSubmeshLookupDestOffset = Shader.PropertyToID("_InputSubmeshLookupDestOffset"); + public static readonly int _InputSubmeshLookupBufferCount = Shader.PropertyToID("_InputSubmeshLookupBufferCount"); + public static readonly int _InputSubmeshLookupData = Shader.PropertyToID("_InputSubmeshLookupData"); + public static readonly int _ClearBuffer = Shader.PropertyToID("_ClearBuffer"); + public static readonly int _ClearBufferSize = Shader.PropertyToID("_ClearBufferSize"); + public static readonly int _ClearBufferOffset = Shader.PropertyToID("_ClearBufferOffset"); + public static readonly int _ClearBufferValue = Shader.PropertyToID("_ClearBufferValue"); + } + + private struct MeshSlot + { + public int refCount; + public int meshHash; + public GeometryPoolHandle geometryHandle; + } + + private struct GeometrySlot + { + public BlockAllocator.Allocation vertexAlloc; + public BlockAllocator.Allocation indexAlloc; + public BlockAllocator.Allocation subMeshLookupAlloc; + public BlockAllocator.Allocation subMeshEntryAlloc; + + public static GeometrySlot Invalid = new GeometrySlot() + { + vertexAlloc = BlockAllocator.Allocation.Invalid, + indexAlloc = BlockAllocator.Allocation.Invalid, + subMeshLookupAlloc = BlockAllocator.Allocation.Invalid, + subMeshEntryAlloc = BlockAllocator.Allocation.Invalid + }; + + public bool valid => vertexAlloc.valid && indexAlloc.valid && subMeshLookupAlloc.valid && subMeshEntryAlloc.valid; + } + + public static int GetVertexByteSize() => GeometryPoolConstants.GeoPoolVertexByteSize; + public static int GetIndexByteSize() => GeometryPoolConstants.GeoPoolIndexByteSize; + public static int GetSubMeshLookupByteSize() => 1; //1 byte. + public static int GetSubMeshEntryByteSize() => System.Runtime.InteropServices.Marshal.SizeOf(); + public static int GetGeoMetadataByteSize() => System.Runtime.InteropServices.Marshal.SizeOf(); + + private int GetFormatByteCount(VertexAttributeFormat format) + { + switch (format) + { + case VertexAttributeFormat.Float32: return 4; + case VertexAttributeFormat.Float16: return 2; + case VertexAttributeFormat.UNorm8: return 1; + case VertexAttributeFormat.SNorm8: return 1; + case VertexAttributeFormat.UNorm16: return 2; + case VertexAttributeFormat.SNorm16: return 2; + case VertexAttributeFormat.UInt8: return 1; + case VertexAttributeFormat.SInt8: return 1; + case VertexAttributeFormat.UInt16: return 2; + case VertexAttributeFormat.SInt16: return 2; + case VertexAttributeFormat.UInt32: return 4; + case VertexAttributeFormat.SInt32: return 4; + } + return 4; + } + + private static int DivUp(int x, int y) => (x + y - 1) / y; + + GeometryPoolDesc m_Desc; + + public Mesh globalMesh = null; + public GraphicsBuffer globalIndexBuffer { get { return m_GlobalIndexBuffer; } } + public ComputeBuffer globalVertexBuffer { get { return m_GlobalVertexBuffer; } } + public ComputeBuffer globalSubMeshLookupBuffer { get { return m_GlobalSubMeshLookupBuffer; } } + public ComputeBuffer globalSubMeshEntryBuffer { get { return m_GlobalSubMeshEntryBuffer; } } + public ComputeBuffer globalMetadataBuffer { get { return m_GlobalGeoMetadataBuffer; } } + + public int maxMeshes => m_Desc.maxMeshes; + public int indicesCount => m_MaxIndexCounts; + public int verticesCount => m_MaxVertCounts; + public int subMeshLookupCount => m_MaxSubMeshLookupCounts; + public int subMeshEntryCount => m_MaxSubMeshEntryCounts; + + private GraphicsBuffer m_GlobalIndexBuffer = null; + private ComputeBuffer m_GlobalVertexBuffer = null; + private ComputeBuffer m_GlobalSubMeshLookupBuffer = null; + private ComputeBuffer m_GlobalSubMeshEntryBuffer = null; + private ComputeBuffer m_GlobalGeoMetadataBuffer = null; + + private int m_MaxVertCounts; + private int m_MaxIndexCounts; + private int m_MaxSubMeshLookupCounts; + private int m_MaxSubMeshEntryCounts; + + private BlockAllocator m_VertexAllocator; + private BlockAllocator m_IndexAllocator; + private BlockAllocator m_SubMeshLookupAllocator; + private BlockAllocator m_SubMeshEntryAllocator; + + private NativeHashMap m_MeshSlots; + private NativeList m_GeoSlots; + private NativeList m_FreeGeoSlots; + + private List m_InputBufferReferences; + + private int m_UsedGeoSlots; + + private ComputeShader m_GeometryPoolKernelsCS; + private int m_KernelMainUpdateIndexBuffer16; + private int m_KernelMainUpdateIndexBuffer32; + private int m_KernelMainUpdateVertexBuffer; + private int m_KernelMainUpdateSubMeshData; + private int m_KernelMainUpdateMeshMetadata; + private int m_KernelMainClearSubMeshData; + + private CommandBuffer m_CmdBuffer; + private bool m_MustClearCmdBuffer; + private int m_PendingCmds; + + public GeometryPool(in GeometryPoolDesc desc) + { + LoadShaders(); + + m_CmdBuffer = new CommandBuffer(); + m_InputBufferReferences = new List(); + m_MustClearCmdBuffer = false; + m_PendingCmds = 0; + + m_Desc = desc; + m_MaxVertCounts = CalcVertexCount(); + m_MaxIndexCounts = CalcIndexCount(); + m_MaxSubMeshLookupCounts = CalcSubMeshLookupCount(); + m_MaxSubMeshEntryCounts = CalcSubMeshEntryCount(); + m_UsedGeoSlots = 0; + + m_GlobalVertexBuffer = new ComputeBuffer(DivUp(m_MaxVertCounts * GetVertexByteSize(), 4), 4, ComputeBufferType.Raw); + + globalMesh = new Mesh(); + globalMesh.indexBufferTarget = GraphicsBuffer.Target.Raw; + globalMesh.SetIndexBufferParams(m_MaxIndexCounts, IndexFormat.UInt32); + globalMesh.subMeshCount = desc.maxMeshes; + globalMesh.vertices = new Vector3[1]; + globalMesh.UploadMeshData(false); + m_GlobalIndexBuffer = globalMesh.GetIndexBuffer(); + + m_GlobalSubMeshLookupBuffer = new ComputeBuffer(DivUp(m_MaxSubMeshLookupCounts * GetSubMeshLookupByteSize(), 4), 4, ComputeBufferType.Raw); + m_GlobalSubMeshEntryBuffer = new ComputeBuffer(m_MaxSubMeshEntryCounts, GetSubMeshEntryByteSize(), ComputeBufferType.Structured); + m_GlobalGeoMetadataBuffer = new ComputeBuffer(m_Desc.maxMeshes, GetGeoMetadataByteSize(), ComputeBufferType.Structured); + + Assertions.Assert.IsTrue(m_GlobalIndexBuffer != null); + Assertions.Assert.IsTrue((m_GlobalIndexBuffer.target & GraphicsBuffer.Target.Raw) != 0); + + m_MeshSlots = new NativeHashMap(desc.maxMeshes, Allocator.Persistent); + m_GeoSlots = new NativeList(Allocator.Persistent); + m_FreeGeoSlots = new NativeList(Allocator.Persistent); + + m_VertexAllocator = new BlockAllocator(); + m_VertexAllocator.Initialize(m_MaxVertCounts); + + m_IndexAllocator = new BlockAllocator(); + m_IndexAllocator.Initialize(m_MaxIndexCounts); + + m_SubMeshLookupAllocator = new BlockAllocator(); + m_SubMeshLookupAllocator.Initialize(m_MaxSubMeshLookupCounts); + + m_SubMeshEntryAllocator = new BlockAllocator(); + m_SubMeshEntryAllocator.Initialize(m_MaxSubMeshEntryCounts); + } + + public void DisposeInputBuffers() + { + if (m_InputBufferReferences.Count == 0) + return; + + foreach (var b in m_InputBufferReferences) + b.Dispose(); + m_InputBufferReferences.Clear(); + } + + public void Dispose() + { + m_IndexAllocator.Dispose(); + m_VertexAllocator.Dispose(); + m_SubMeshLookupAllocator.Dispose(); + m_SubMeshEntryAllocator.Dispose(); + + m_FreeGeoSlots.Dispose(); + m_GeoSlots.Dispose(); + m_MeshSlots.Dispose(); + + m_GlobalIndexBuffer.Dispose(); + m_GlobalVertexBuffer.Release(); + m_GlobalSubMeshLookupBuffer.Dispose(); + m_GlobalSubMeshEntryBuffer.Dispose(); + m_GlobalGeoMetadataBuffer.Dispose(); + m_CmdBuffer.Release(); + + CoreUtils.Destroy(globalMesh); + globalMesh = null; + DisposeInputBuffers(); + } + + private void LoadShaders() + { + m_GeometryPoolKernelsCS = (ComputeShader)Resources.Load("GeometryPoolKernels"); + + m_KernelMainUpdateIndexBuffer16 = m_GeometryPoolKernelsCS.FindKernel("MainUpdateIndexBuffer16"); + m_KernelMainUpdateIndexBuffer32 = m_GeometryPoolKernelsCS.FindKernel("MainUpdateIndexBuffer32"); + m_KernelMainUpdateVertexBuffer = m_GeometryPoolKernelsCS.FindKernel("MainUpdateVertexBuffer"); + m_KernelMainUpdateSubMeshData = m_GeometryPoolKernelsCS.FindKernel("MainUpdateSubMeshData"); + m_KernelMainUpdateMeshMetadata = m_GeometryPoolKernelsCS.FindKernel("MainUpdateMeshMetadata"); + m_KernelMainClearSubMeshData = m_GeometryPoolKernelsCS.FindKernel("MainClearBuffer"); + } + + private int CalcVertexCount() => DivUp(m_Desc.vertexPoolByteSize, GetVertexByteSize()); + private int CalcIndexCount() => DivUp(m_Desc.indexPoolByteSize, GetIndexByteSize()); + private int CalcSubMeshLookupCount() => DivUp(m_Desc.subMeshLookupPoolByteSize, GetSubMeshLookupByteSize()); + private int CalcSubMeshEntryCount() => DivUp(m_Desc.subMeshEntryPoolByteSize, GetSubMeshEntryByteSize()); + + private void DeallocateSlot(ref GeometrySlot slot) + { + if (slot.vertexAlloc.valid) + m_VertexAllocator.FreeAllocation(slot.vertexAlloc); + + if (slot.indexAlloc.valid) + m_IndexAllocator.FreeAllocation(slot.indexAlloc); + + if (slot.subMeshLookupAlloc.valid) + m_SubMeshLookupAllocator.FreeAllocation(slot.subMeshLookupAlloc); + + if (slot.subMeshEntryAlloc.valid) + m_SubMeshEntryAllocator.FreeAllocation(slot.subMeshEntryAlloc); + + slot = GeometrySlot.Invalid; + } + + private bool AllocateGeo(int vertexCount, int indexCount, int subMeshEntries, out GeometryPoolHandle outHandle) + { + var newSlot = GeometrySlot.Invalid; + + bool allocationSuccess = true; + + if ((m_UsedGeoSlots + 1) > m_Desc.maxMeshes) + { + allocationSuccess = false; + outHandle = GeometryPoolHandle.Invalid; + return false; + } + + if (allocationSuccess) + { + newSlot.vertexAlloc = m_VertexAllocator.Allocate(vertexCount); + allocationSuccess = newSlot.vertexAlloc.valid; + } + + if (allocationSuccess) + { + newSlot.indexAlloc = m_IndexAllocator.Allocate(indexCount); + allocationSuccess = newSlot.indexAlloc.valid; + } + + if (allocationSuccess) + { + newSlot.subMeshLookupAlloc = m_SubMeshLookupAllocator.Allocate(DivUp(indexCount, 3)); + allocationSuccess = newSlot.subMeshLookupAlloc.valid; + } + + if (allocationSuccess) + { + newSlot.subMeshEntryAlloc = m_SubMeshEntryAllocator.Allocate(subMeshEntries); + allocationSuccess = newSlot.subMeshEntryAlloc.valid; + } + + if (!allocationSuccess) + { + DeallocateSlot(ref newSlot); + outHandle = GeometryPoolHandle.Invalid; + return false; + } + + if (m_FreeGeoSlots.IsEmpty) + { + outHandle.index = m_GeoSlots.Length; + m_GeoSlots.Add(newSlot); + } + else + { + outHandle = m_FreeGeoSlots[m_FreeGeoSlots.Length - 1]; + m_FreeGeoSlots.RemoveAtSwapBack(m_FreeGeoSlots.Length - 1); + Assertions.Assert.IsTrue(!m_GeoSlots[outHandle.index].valid); + m_GeoSlots[outHandle.index] = newSlot; + } + + ++m_UsedGeoSlots; + var descriptor = new SubMeshDescriptor(); + descriptor.baseVertex = 0; + descriptor.firstVertex = 0; + descriptor.indexCount = newSlot.indexAlloc.block.count; + descriptor.indexStart = newSlot.indexAlloc.block.offset; + descriptor.topology = MeshTopology.Triangles; + descriptor.vertexCount = 1; + globalMesh.SetSubMesh(outHandle.index, descriptor, MeshUpdateFlags.DontValidateIndices | MeshUpdateFlags.DontNotifyMeshUsers | MeshUpdateFlags.DontRecalculateBounds); + return true; + } + + private void DeallocateGeo(GeometryPoolHandle handle) + { + if (!handle.valid) + throw new System.Exception("Cannot free invalid geo pool handle"); + + --m_UsedGeoSlots; + m_FreeGeoSlots.Add(handle); + GeometrySlot slot = m_GeoSlots[handle.index]; + DeallocateSlot(ref slot); + m_GeoSlots[handle.index] = slot; + } + + private void UpdateGeoGpuState(Mesh mesh, GeometryPoolHandle handle) + { + var geoSlot = m_GeoSlots[handle.index]; + CommandBuffer cmdBuffer = AllocateCommandBuffer(); //clear any previous cmd buffers. + + mesh.indexBufferTarget |= GraphicsBuffer.Target.Raw; + mesh.vertexBufferTarget |= GraphicsBuffer.Target.Raw; + + //Update index buffer + GraphicsBuffer buffer = LoadIndexBuffer(cmdBuffer, mesh, out var indexBufferFormat); + Assertions.Assert.IsTrue((buffer.target & GraphicsBuffer.Target.Raw) != 0); + AddIndexUpdateCommand( + cmdBuffer, + indexBufferFormat, buffer, geoSlot.indexAlloc, m_GlobalIndexBuffer); + + //Update vertex buffer + GraphicsBuffer posBuffer = LoadVertexAttribInfo(mesh, VertexAttribute.Position, out int posStride, out int posOffset, out int _); + Assertions.Assert.IsTrue(posBuffer != null); + Assertions.Assert.IsTrue((posBuffer.target & GraphicsBuffer.Target.Raw) != 0); + + GraphicsBuffer uvBuffer = LoadVertexAttribInfo(mesh, VertexAttribute.TexCoord0, out int uvStride, out int uvOffset, out int _); + Assertions.Assert.IsTrue(uvBuffer != null); + Assertions.Assert.IsTrue((uvBuffer.target & GraphicsBuffer.Target.Raw) != 0); + + GraphicsBuffer uv1Buffer = LoadVertexAttribInfo(mesh, VertexAttribute.TexCoord1, out int uv1Stride, out int uv1Offset, out int _); + if (uv1Buffer != null) + Assertions.Assert.IsTrue((uv1Buffer.target & GraphicsBuffer.Target.Raw) != 0); + + GraphicsBuffer nBuffer = LoadVertexAttribInfo(mesh, VertexAttribute.Normal, out int nStride, out int nOffset, out int _); + Assertions.Assert.IsTrue(nBuffer != null); + Assertions.Assert.IsTrue((nBuffer.target & GraphicsBuffer.Target.Raw) != 0); + + GraphicsBuffer tBuffer = LoadVertexAttribInfo(mesh, VertexAttribute.Tangent, out int tStride, out int tOffset, out int _); + if (tBuffer != null) + Assertions.Assert.IsTrue(tBuffer != null); + + AddVertexUpdateCommand( + cmdBuffer, posBuffer, uvBuffer, uv1Buffer, nBuffer, tBuffer, + posStride, posOffset, uvStride, uvOffset, uv1Stride, uv1Offset, nStride, nOffset, tStride, tOffset, + geoSlot.vertexAlloc, m_GlobalVertexBuffer); + + { + AddClearSubMeshDataBuffer( + cmdBuffer, + geoSlot.subMeshLookupAlloc.block.offset / 4, + geoSlot.subMeshLookupAlloc.block.count / 4, + 0, + m_GlobalSubMeshLookupBuffer); + + int subMeshIndexOffset = 0; + for (int subMeshId = 0; subMeshId < mesh.subMeshCount; ++subMeshId) + { + SubMeshDescriptor submeshDescriptor = mesh.GetSubMesh(subMeshId); + //Update submeshLookup + AddSubMeshDataUpdateCommand( + cmdBuffer, + subMeshId, + subMeshIndexOffset, + submeshDescriptor, + geoSlot.subMeshLookupAlloc, + geoSlot.subMeshEntryAlloc, + m_GlobalSubMeshLookupBuffer, + m_GlobalSubMeshEntryBuffer); + + subMeshIndexOffset += submeshDescriptor.indexCount; + Assertions.Assert.IsTrue((subMeshIndexOffset / 3) <= geoSlot.subMeshLookupAlloc.block.count); + } + } + + //Update metadata buffer + AddMetadataUpdateCommand( + cmdBuffer, handle.index, + new GeoPoolMetadataEntry() + { + vertexOffset = geoSlot.vertexAlloc.block.offset, + indexOffset = geoSlot.indexAlloc.block.offset, + subMeshLookupOffset = geoSlot.subMeshLookupAlloc.block.offset, + subMeshEntryOffset = geoSlot.subMeshEntryAlloc.block.offset + }, + m_GlobalGeoMetadataBuffer); + } + + public bool Register(Mesh mesh, out GeometryPoolHandle outHandle) + { + int meshHashCode = mesh.GetHashCode(); + Assertions.Assert.IsTrue(meshHashCode != -1); + if (m_MeshSlots.TryGetValue(meshHashCode, out MeshSlot meshSlot)) + { + Assertions.Assert.IsTrue(meshHashCode == meshSlot.meshHash); + ++meshSlot.refCount; + m_MeshSlots[meshSlot.meshHash] = meshSlot; + outHandle = meshSlot.geometryHandle; + return true; + } + else + { + var newSlot = new MeshSlot() + { + refCount = 1, + meshHash = meshHashCode, + }; + + int indexCount = 0; + for (int i = 0; i < (int)mesh.subMeshCount; ++i) + indexCount += (int)mesh.GetIndexCount(i); + + if (!AllocateGeo(mesh.vertexCount, indexCount, mesh.subMeshCount, out outHandle)) + return false; + + newSlot.geometryHandle = outHandle; + if (!m_MeshSlots.TryAdd(meshHashCode, newSlot)) + { + //revert the allocation. + DeallocateGeo(outHandle); + outHandle = GeometryPoolHandle.Invalid; + return false; + } + + UpdateGeoGpuState(mesh, outHandle); + + return true; + } + } + + public void Unregister(Mesh mesh) + { + int meshHashCode = mesh.GetHashCode(); + UnregisterByMeshHashCode(meshHashCode); + } + + public void UnregisterByMeshHashCode(int meshHashCode) + { + if (!m_MeshSlots.TryGetValue(meshHashCode, out MeshSlot outSlot)) + return; + + --outSlot.refCount; + if (outSlot.refCount == 0) + { + m_MeshSlots.Remove(meshHashCode); + DeallocateGeo(outSlot.geometryHandle); + } + else + m_MeshSlots[meshHashCode] = outSlot; + } + + public GeometryPoolHandle GetHandle(Mesh mesh) + { + int meshHashCode = mesh.GetHashCode(); + if (!m_MeshSlots.TryGetValue(meshHashCode, out MeshSlot outSlot)) + return GeometryPoolHandle.Invalid; + + return outSlot.geometryHandle; + } + + public void SendGpuCommands() + { + if (m_PendingCmds != 0) + { + Graphics.ExecuteCommandBuffer(m_CmdBuffer); + m_MustClearCmdBuffer = true; + m_PendingCmds = 0; + } + + DisposeInputBuffers(); + } + + public BlockAllocator.Allocation GetIndexBufferBlock(GeometryPoolHandle handle) + { + if (handle.index < 0 || handle.index >= m_GeoSlots.Length) + throw new System.Exception("Handle utilized is invalid"); + + return m_GeoSlots[handle.index].indexAlloc; + } + + public BlockAllocator.Allocation GetVertexBufferBlock(GeometryPoolHandle handle) + { + if (handle.index < 0 || handle.index >= m_GeoSlots.Length) + throw new System.Exception("Handle utilized is invalid"); + + return m_GeoSlots[handle.index].vertexAlloc; + } + + public BlockAllocator.Allocation GetSubMeshLookupBlock(GeometryPoolHandle handle) + { + if (handle.index < 0 || handle.index >= m_GeoSlots.Length) + throw new System.Exception("Handle utilized is invalid"); + + return m_GeoSlots[handle.index].subMeshLookupAlloc; + } + + public BlockAllocator.Allocation GetSubMeshEntryBlock(GeometryPoolHandle handle) + { + if (handle.index < 0 || handle.index >= m_GeoSlots.Length) + throw new System.Exception("Handle utilized is invalid"); + + return m_GeoSlots[handle.index].subMeshEntryAlloc; + } + + private GraphicsBuffer LoadIndexBuffer(CommandBuffer cmdBuffer, Mesh mesh, out IndexFormat fmt) + { + if ((mesh.indexBufferTarget & GraphicsBuffer.Target.Raw) != 0) + { + fmt = mesh.indexFormat; + var idxBuffer = mesh.GetIndexBuffer(); + m_InputBufferReferences.Add(idxBuffer); + return idxBuffer; + } + else + { + fmt = IndexFormat.UInt32; + + int indexCount = 0; + for (int i = 0; i < (int)mesh.subMeshCount; ++i) + indexCount += (int)mesh.GetIndexCount(i); + + var idxBuffer = new GraphicsBuffer(GraphicsBuffer.Target.Index | GraphicsBuffer.Target.Raw, indexCount, 4); + m_InputBufferReferences.Add(idxBuffer); + + int indexOffset = 0; + + for (int i = 0; i < (int)mesh.subMeshCount; ++i) + { + int currentIndexCount = (int)mesh.GetIndexCount(i); + cmdBuffer.SetBufferData(idxBuffer, mesh.GetIndices(i), 0, indexOffset, currentIndexCount); + indexOffset += currentIndexCount; + } + + return idxBuffer; + } + } + + GraphicsBuffer LoadVertexAttribInfo(Mesh mesh, VertexAttribute attribute, out int streamStride, out int attributeOffset, out int attributeBytes) + { + if (!mesh.HasVertexAttribute(attribute)) + { + streamStride = attributeOffset = attributeBytes = 0; + return null; + } + + int stream = mesh.GetVertexAttributeStream(attribute); + streamStride = mesh.GetVertexBufferStride(stream); + attributeOffset = mesh.GetVertexAttributeOffset(attribute); + attributeBytes = GetFormatByteCount(mesh.GetVertexAttributeFormat(attribute)) * mesh.GetVertexAttributeDimension(attribute); + + var gb = mesh.GetVertexBuffer(stream); + m_InputBufferReferences.Add(gb); + return gb; + } + + + private CommandBuffer AllocateCommandBuffer() + { + if (m_MustClearCmdBuffer) + { + m_CmdBuffer.Clear(); + m_MustClearCmdBuffer = false; + } + + ++m_PendingCmds; + return m_CmdBuffer; + } + + private void AddMetadataUpdateCommand( + CommandBuffer cmdBuffer, + int geoHandleIndex, + in GeoPoolMetadataEntry metadataEntry, ComputeBuffer outputMetadataBuffer) + { + cmdBuffer.SetComputeIntParam(m_GeometryPoolKernelsCS, GeoPoolShaderIDs._GeoHandle, geoHandleIndex); + cmdBuffer.SetComputeIntParam(m_GeometryPoolKernelsCS, GeoPoolShaderIDs._GeoVertexOffset, metadataEntry.vertexOffset); + cmdBuffer.SetComputeIntParam(m_GeometryPoolKernelsCS, GeoPoolShaderIDs._GeoIndexOffset, metadataEntry.indexOffset); + cmdBuffer.SetComputeIntParam(m_GeometryPoolKernelsCS, GeoPoolShaderIDs._GeoSubMeshLookupOffset, metadataEntry.subMeshLookupOffset); + cmdBuffer.SetComputeIntParam(m_GeometryPoolKernelsCS, GeoPoolShaderIDs._GeoSubMeshEntryOffset, metadataEntry.subMeshEntryOffset); + + int kernel = m_KernelMainUpdateMeshMetadata; + cmdBuffer.SetComputeBufferParam(m_GeometryPoolKernelsCS, kernel, GeoPoolShaderIDs._OutputGeoMetadataBuffer, outputMetadataBuffer); + cmdBuffer.DispatchCompute(m_GeometryPoolKernelsCS, kernel, 1, 1, 1); + } + + private void AddIndexUpdateCommand( + CommandBuffer cmdBuffer, + IndexFormat inputFormat, in GraphicsBuffer inputBuffer, in BlockAllocator.Allocation location, GraphicsBuffer outputIdxBuffer) + { + if (location.block.count == 0) + return; + + cmdBuffer.SetComputeIntParam(m_GeometryPoolKernelsCS, GeoPoolShaderIDs._InputIBCount, location.block.count); + cmdBuffer.SetComputeIntParam(m_GeometryPoolKernelsCS, GeoPoolShaderIDs._OutputIBOffset, location.block.offset); + int kernel = inputFormat == IndexFormat.UInt16 ? m_KernelMainUpdateIndexBuffer16 : m_KernelMainUpdateIndexBuffer32; + cmdBuffer.SetComputeBufferParam(m_GeometryPoolKernelsCS, kernel, GeoPoolShaderIDs._InputIndexBuffer, inputBuffer); + cmdBuffer.SetComputeBufferParam(m_GeometryPoolKernelsCS, kernel, GeoPoolShaderIDs._OutputIndexBuffer, outputIdxBuffer); + int groupCountsX = DivUp(location.block.count, 64); + cmdBuffer.DispatchCompute(m_GeometryPoolKernelsCS, kernel, groupCountsX, 1, 1); + } + + private void AddVertexUpdateCommand( + CommandBuffer cmdBuffer, + in GraphicsBuffer p, in GraphicsBuffer uv0, in GraphicsBuffer uv1, in GraphicsBuffer n, in GraphicsBuffer t, + int posStride, int posOffset, int uv0Stride, int uv0Offset, int uv1Stride, int uv1Offset, int normalStride, int normalOffset, int tangentStride, int tangentOffset, + in BlockAllocator.Allocation location, + ComputeBuffer outputVertexBuffer) + { + if (location.block.count == 0) + return; + + GeoPoolInputFlags flags = + (uv1 != null ? GeoPoolInputFlags.HasUV1 : GeoPoolInputFlags.None) + | (t != null ? GeoPoolInputFlags.HasTangent : GeoPoolInputFlags.None); + + cmdBuffer.SetComputeIntParam(m_GeometryPoolKernelsCS, GeoPoolShaderIDs._InputVBCount, location.block.count); + cmdBuffer.SetComputeIntParam(m_GeometryPoolKernelsCS, GeoPoolShaderIDs._OutputVBSize, m_MaxVertCounts); + cmdBuffer.SetComputeIntParam(m_GeometryPoolKernelsCS, GeoPoolShaderIDs._OutputVBOffset, location.block.offset); + cmdBuffer.SetComputeIntParam(m_GeometryPoolKernelsCS, GeoPoolShaderIDs._InputPosBufferStride, posStride); + cmdBuffer.SetComputeIntParam(m_GeometryPoolKernelsCS, GeoPoolShaderIDs._InputPosBufferOffset, posOffset); + cmdBuffer.SetComputeIntParam(m_GeometryPoolKernelsCS, GeoPoolShaderIDs._InputUv0BufferStride, uv0Stride); + cmdBuffer.SetComputeIntParam(m_GeometryPoolKernelsCS, GeoPoolShaderIDs._InputUv0BufferOffset, uv0Offset); + cmdBuffer.SetComputeIntParam(m_GeometryPoolKernelsCS, GeoPoolShaderIDs._InputUv1BufferStride, uv1Stride); + cmdBuffer.SetComputeIntParam(m_GeometryPoolKernelsCS, GeoPoolShaderIDs._InputUv1BufferOffset, uv1Offset); + cmdBuffer.SetComputeIntParam(m_GeometryPoolKernelsCS, GeoPoolShaderIDs._InputNormalBufferStride, normalStride); + cmdBuffer.SetComputeIntParam(m_GeometryPoolKernelsCS, GeoPoolShaderIDs._InputNormalBufferOffset, normalOffset); + cmdBuffer.SetComputeIntParam(m_GeometryPoolKernelsCS, GeoPoolShaderIDs._InputTangentBufferStride, tangentStride); + cmdBuffer.SetComputeIntParam(m_GeometryPoolKernelsCS, GeoPoolShaderIDs._InputTangentBufferOffset, tangentOffset); + cmdBuffer.SetComputeIntParam(m_GeometryPoolKernelsCS, GeoPoolShaderIDs._InputFlags, (int)flags); + + int kernel = m_KernelMainUpdateVertexBuffer; + cmdBuffer.SetComputeBufferParam(m_GeometryPoolKernelsCS, kernel, GeoPoolShaderIDs._PosBuffer, p); + cmdBuffer.SetComputeBufferParam(m_GeometryPoolKernelsCS, kernel, GeoPoolShaderIDs._Uv0Buffer, uv0); + cmdBuffer.SetComputeBufferParam(m_GeometryPoolKernelsCS, kernel, GeoPoolShaderIDs._Uv1Buffer, uv1 != null ? t : p); /*unity always wants something set*/ + cmdBuffer.SetComputeBufferParam(m_GeometryPoolKernelsCS, kernel, GeoPoolShaderIDs._NormalBuffer, n); + cmdBuffer.SetComputeBufferParam(m_GeometryPoolKernelsCS, kernel, GeoPoolShaderIDs._TangentBuffer, t != null ? t : p);/*unity always wants something set*/ + + cmdBuffer.SetComputeBufferParam(m_GeometryPoolKernelsCS, kernel, GeoPoolShaderIDs._OutputVB, outputVertexBuffer); + + int groupCountsX = DivUp(location.block.count, 64); + cmdBuffer.DispatchCompute(m_GeometryPoolKernelsCS, kernel, groupCountsX, 1, 1); + } + + private void AddClearSubMeshDataBuffer( + CommandBuffer cmdBuffer, + int offset, + int size, + int clearVal, + ComputeBuffer outputBuffer) + { + if (size == 0) + return; + + cmdBuffer.SetComputeIntParam(m_GeometryPoolKernelsCS, GeoPoolShaderIDs._ClearBufferSize, size); + cmdBuffer.SetComputeIntParam(m_GeometryPoolKernelsCS, GeoPoolShaderIDs._ClearBufferOffset, offset); + cmdBuffer.SetComputeIntParam(m_GeometryPoolKernelsCS, GeoPoolShaderIDs._ClearBufferValue, clearVal); + int kernel = m_KernelMainClearSubMeshData; + cmdBuffer.SetComputeBufferParam(m_GeometryPoolKernelsCS, kernel, GeoPoolShaderIDs._ClearBuffer, outputBuffer); + int groupCountsX = DivUp(size, 64); + cmdBuffer.DispatchCompute(m_GeometryPoolKernelsCS, kernel, groupCountsX, 1, 1); + } + + private void AddSubMeshDataUpdateCommand( + CommandBuffer cmdBuffer, + int descriptorIndex, + int indexOffset, + SubMeshDescriptor submeshDescriptor, + in BlockAllocator.Allocation lookupAllocation, + in BlockAllocator.Allocation entryAllocation, + ComputeBuffer outputLookupBuffer, + ComputeBuffer outputEntryBuffer) + { + int lookupCounts = DivUp(submeshDescriptor.indexCount, 3); + if (lookupCounts == 0) + return; + + cmdBuffer.SetComputeIntParam(m_GeometryPoolKernelsCS, GeoPoolShaderIDs._InputSubMeshIndexStart, submeshDescriptor.indexStart); + cmdBuffer.SetComputeIntParam(m_GeometryPoolKernelsCS, GeoPoolShaderIDs._InputSubMeshIndexCount, submeshDescriptor.indexCount); + cmdBuffer.SetComputeIntParam(m_GeometryPoolKernelsCS, GeoPoolShaderIDs._InputSubMeshBaseVertex, submeshDescriptor.baseVertex); + cmdBuffer.SetComputeIntParam(m_GeometryPoolKernelsCS, GeoPoolShaderIDs._InputSubMeshDestIndex, entryAllocation.block.offset + descriptorIndex); + + cmdBuffer.SetComputeIntParam(m_GeometryPoolKernelsCS, GeoPoolShaderIDs._InputSubmeshLookupDestOffset, indexOffset / 3 + lookupAllocation.block.offset); + cmdBuffer.SetComputeIntParam(m_GeometryPoolKernelsCS, GeoPoolShaderIDs._InputSubmeshLookupBufferCount, lookupCounts); + cmdBuffer.SetComputeIntParam(m_GeometryPoolKernelsCS, GeoPoolShaderIDs._InputSubmeshLookupData, descriptorIndex); + + int kernel = m_KernelMainUpdateSubMeshData; + cmdBuffer.SetComputeBufferParam(m_GeometryPoolKernelsCS, kernel, GeoPoolShaderIDs._OutputSubMeshLookupBuffer, outputLookupBuffer); + cmdBuffer.SetComputeBufferParam(m_GeometryPoolKernelsCS, kernel, GeoPoolShaderIDs._OutputSubMeshEntryBuffer, outputEntryBuffer); + + int groupCountsX = DivUp(lookupCounts, 64); + cmdBuffer.DispatchCompute(m_GeometryPoolKernelsCS, kernel, groupCountsX, 1, 1); + } + + private Vector4 GetPackedGeoPoolParam0() + { + return new Vector4(m_MaxVertCounts, 0.0f, 0.0f, 0.0f); + } + + public void BindResources(CommandBuffer cmdBuffer, ComputeShader cs, int kernel) + { + cmdBuffer.SetComputeBufferParam(cs, kernel, GeoPoolShaderIDs._GeoPoolGlobalVertexBuffer, globalVertexBuffer); + cmdBuffer.SetComputeBufferParam(cs, kernel, GeoPoolShaderIDs._GeoPoolGlobalIndexBuffer, globalIndexBuffer); + cmdBuffer.SetComputeBufferParam(cs, kernel, GeoPoolShaderIDs._GeoPoolGlobalMetadataBuffer, globalMetadataBuffer); + cmdBuffer.SetComputeBufferParam(cs, kernel, GeoPoolShaderIDs._GeoPoolGlobalMetadataBuffer, globalMetadataBuffer); + cmdBuffer.SetComputeVectorParam(cs, GeoPoolShaderIDs._GeoPoolGlobalParams, GetPackedGeoPoolParam0()); + } + + public void BindResources(Material material) + { + material.SetBuffer(GeoPoolShaderIDs._GeoPoolGlobalVertexBuffer, globalVertexBuffer); + material.SetBuffer(GeoPoolShaderIDs._GeoPoolGlobalIndexBuffer, globalIndexBuffer); + material.SetBuffer(GeoPoolShaderIDs._GeoPoolGlobalMetadataBuffer, globalMetadataBuffer); + material.SetVector(GeoPoolShaderIDs._GeoPoolGlobalParams, GetPackedGeoPoolParam0()); + } + } + +} diff --git a/com.unity.render-pipelines.core/Runtime/GeometryPool/GeometryPool.cs.meta b/com.unity.render-pipelines.core/Runtime/GeometryPool/GeometryPool.cs.meta new file mode 100644 index 00000000000..0e95b0d1db5 --- /dev/null +++ b/com.unity.render-pipelines.core/Runtime/GeometryPool/GeometryPool.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: b1c967a83bc55464ebd644fecf9fa467 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/com.unity.render-pipelines.core/Runtime/GeometryPool/Resources.meta b/com.unity.render-pipelines.core/Runtime/GeometryPool/Resources.meta new file mode 100644 index 00000000000..8e1b23e2d58 --- /dev/null +++ b/com.unity.render-pipelines.core/Runtime/GeometryPool/Resources.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 39d4d4f92f217634f920615794be6469 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/com.unity.render-pipelines.core/Runtime/GeometryPool/Resources/GeometryPool.hlsl b/com.unity.render-pipelines.core/Runtime/GeometryPool/Resources/GeometryPool.hlsl new file mode 100644 index 00000000000..c87a60834c6 --- /dev/null +++ b/com.unity.render-pipelines.core/Runtime/GeometryPool/Resources/GeometryPool.hlsl @@ -0,0 +1,89 @@ +#ifndef GEOMETRY_POOL_H +#define GEOMETRY_POOL_H + +#include "Packages/com.unity.render-pipelines.core/Runtime/GeometryPool/Resources/GeometryPoolDefs.cs.hlsl" + +ByteAddressBuffer _GeoPoolGlobalVertexBuffer; +ByteAddressBuffer _GeoPoolGlobalIndexBuffer; +StructuredBuffer _GeoPoolGlobalMetadataBuffer; +float4 _GeoPoolGlobalParams; + +namespace GeometryPool +{ + +void StoreVertex( + int vertexIndex, + in GeoPoolVertex vertex, + int vertexFlags, + int outputBufferSize, + RWByteAddressBuffer output) +{ + int componentOffset = outputBufferSize * GEO_POOL_POS_BYTE_OFFSET; + output.Store3(componentOffset + vertexIndex * GEO_POOL_POS_BYTE_SIZE, asuint(vertex.pos)); + + componentOffset = outputBufferSize * GEO_POOL_UV0BYTE_OFFSET; + output.Store2(componentOffset + vertexIndex * GEO_POOL_UV0BYTE_SIZE, asuint(vertex.uv)); + + componentOffset = outputBufferSize * GEO_POOL_UV1BYTE_OFFSET; + if ((vertexFlags & GEOPOOLINPUTFLAGS_HAS_UV1) != 0) + output.Store2(componentOffset + vertexIndex * GEO_POOL_UV1BYTE_SIZE, asuint(vertex.uv1)); + + componentOffset = outputBufferSize * GEO_POOL_NORMAL_BYTE_OFFSET; + output.Store3(componentOffset + vertexIndex * GEO_POOL_NORMAL_BYTE_SIZE, asuint(vertex.N)); + + componentOffset = outputBufferSize * GEO_POOL_TANGENT_BYTE_OFFSET; + if ((vertexFlags & GEOPOOLINPUTFLAGS_HAS_TANGENT) != 0) + output.Store3(componentOffset + vertexIndex * GEO_POOL_TANGENT_BYTE_SIZE, asuint(vertex.T)); +} + +void LoadVertex( + int vertexIndex, + int vertexBufferSize, + int vertexFlags, + ByteAddressBuffer vertexBuffer, + out GeoPoolVertex outputVertex) +{ + int componentOffset = vertexBufferSize * GEO_POOL_POS_BYTE_OFFSET; + outputVertex.pos = asfloat(vertexBuffer.Load3(componentOffset + vertexIndex * GEO_POOL_POS_BYTE_SIZE)); + + componentOffset = vertexBufferSize * GEO_POOL_UV0BYTE_OFFSET; + outputVertex.uv = asfloat(vertexBuffer.Load2(componentOffset + vertexIndex * GEO_POOL_UV0BYTE_SIZE)); + + componentOffset = vertexBufferSize * GEO_POOL_UV1BYTE_OFFSET; + if ((vertexFlags & GEOPOOLINPUTFLAGS_HAS_UV1) != 0) + outputVertex.uv1 = asfloat(vertexBuffer.Load2(componentOffset + vertexIndex * GEO_POOL_UV1BYTE_SIZE)); + + componentOffset = vertexBufferSize * GEO_POOL_NORMAL_BYTE_OFFSET; + outputVertex.N = asfloat(vertexBuffer.Load3(componentOffset + vertexIndex * GEO_POOL_NORMAL_BYTE_SIZE)); + + componentOffset = vertexBufferSize * GEO_POOL_TANGENT_BYTE_OFFSET; + if ((vertexFlags & GEOPOOLINPUTFLAGS_HAS_TANGENT) != 0) + outputVertex.T = asfloat(vertexBuffer.Load3(componentOffset + vertexIndex * GEO_POOL_TANGENT_BYTE_SIZE)); +} + +void LoadVertex( + int vertexIndex, + GeoPoolMetadataEntry metadata, + out GeoPoolVertex outputVertex) +{ + LoadVertex(metadata.vertexOffset + vertexIndex, (int)_GeoPoolGlobalParams.x, 0xfffff, _GeoPoolGlobalVertexBuffer, outputVertex); +} + +void SubMeshLookupBucketShiftMask(int index, out int bucketId, out int shift, out int mask) +{ + bucketId = index >> 2; + shift = (index & 0x3) << 3; + mask = 0xff; +} + + +void PackSubMeshLookup(int index, int value, out int bucketId, out uint packedValue) +{ + int shift, mask; + SubMeshLookupBucketShiftMask(index, bucketId, shift, mask); + packedValue = (uint)(value & mask) << (uint)shift; +} + +} + +#endif diff --git a/com.unity.render-pipelines.core/Runtime/GeometryPool/Resources/GeometryPool.hlsl.meta b/com.unity.render-pipelines.core/Runtime/GeometryPool/Resources/GeometryPool.hlsl.meta new file mode 100644 index 00000000000..d9d609a5827 --- /dev/null +++ b/com.unity.render-pipelines.core/Runtime/GeometryPool/Resources/GeometryPool.hlsl.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: b7bdd69643ed80348ad9ea68979d488a +ShaderIncludeImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/com.unity.render-pipelines.core/Runtime/GeometryPool/Resources/GeometryPoolDefs.cs b/com.unity.render-pipelines.core/Runtime/GeometryPool/Resources/GeometryPoolDefs.cs new file mode 100644 index 00000000000..5b3866cf2f8 --- /dev/null +++ b/com.unity.render-pipelines.core/Runtime/GeometryPool/Resources/GeometryPoolDefs.cs @@ -0,0 +1,61 @@ +using System; + +namespace UnityEngine.Rendering +{ + + [GenerateHLSL] + public static class GeometryPoolConstants + { + public static int GeoPoolPosByteSize = 3 * 4; + public static int GeoPoolUV0ByteSize = 2 * 4; + public static int GeoPoolUV1ByteSize = 2 * 4; + public static int GeoPoolNormalByteSize = 3 * 4; + public static int GeoPoolTangentByteSize = 3 * 4; + + public static int GeoPoolPosByteOffset = 0; + public static int GeoPoolUV0ByteOffset = GeoPoolPosByteOffset + GeoPoolPosByteSize; + public static int GeoPoolUV1ByteOffset = GeoPoolUV0ByteOffset + GeoPoolUV0ByteSize; + public static int GeoPoolNormalByteOffset = GeoPoolUV1ByteOffset + GeoPoolUV1ByteSize; + public static int GeoPoolTangentByteOffset = GeoPoolNormalByteOffset + GeoPoolNormalByteSize; + + public static int GeoPoolIndexByteSize = 4; + public static int GeoPoolVertexByteSize = + GeoPoolPosByteSize + GeoPoolUV0ByteSize + GeoPoolUV1ByteSize + GeoPoolNormalByteSize + GeoPoolTangentByteSize + GeoPoolNormalByteSize; + } + + [GenerateHLSL(needAccessors = false)] + internal struct GeoPoolVertex + { + public Vector3 pos; + public Vector2 uv; + public Vector2 uv1; + public Vector3 N; + public Vector3 T; + } + + [Flags] + [GenerateHLSL] + internal enum GeoPoolInputFlags + { + None = 0, + HasUV1 = 1 << 0, + HasTangent = 1 << 1 + } + + [GenerateHLSL] + internal struct GeoPoolSubMeshEntry + { + public int baseVertex; + public int indexStart; + public int indexCount; + } + + [GenerateHLSL] + internal struct GeoPoolMetadataEntry + { + public int vertexOffset; + public int indexOffset; + public int subMeshLookupOffset; + public int subMeshEntryOffset; + } +} diff --git a/com.unity.render-pipelines.core/Runtime/GeometryPool/Resources/GeometryPoolDefs.cs.hlsl b/com.unity.render-pipelines.core/Runtime/GeometryPool/Resources/GeometryPoolDefs.cs.hlsl new file mode 100644 index 00000000000..aa9562cf822 --- /dev/null +++ b/com.unity.render-pipelines.core/Runtime/GeometryPool/Resources/GeometryPoolDefs.cs.hlsl @@ -0,0 +1,95 @@ +// +// This file was automatically generated. Please don't edit by hand. Execute Editor command [ Edit > Rendering > Generate Shader Includes ] instead +// + +#ifndef GEOMETRYPOOLDEFS_CS_HLSL +#define GEOMETRYPOOLDEFS_CS_HLSL +// +// UnityEngine.Rendering.GeometryPoolConstants: static fields +// +#define GEO_POOL_POS_BYTE_SIZE (12) +#define GEO_POOL_UV0BYTE_SIZE (8) +#define GEO_POOL_UV1BYTE_SIZE (8) +#define GEO_POOL_NORMAL_BYTE_SIZE (12) +#define GEO_POOL_TANGENT_BYTE_SIZE (12) +#define GEO_POOL_POS_BYTE_OFFSET (0) +#define GEO_POOL_UV0BYTE_OFFSET (12) +#define GEO_POOL_UV1BYTE_OFFSET (20) +#define GEO_POOL_NORMAL_BYTE_OFFSET (28) +#define GEO_POOL_TANGENT_BYTE_OFFSET (40) +#define GEO_POOL_INDEX_BYTE_SIZE (4) +#define GEO_POOL_VERTEX_BYTE_SIZE (64) + +// +// UnityEngine.Rendering.GeoPoolInputFlags: static fields +// +#define GEOPOOLINPUTFLAGS_NONE (0) +#define GEOPOOLINPUTFLAGS_HAS_UV1 (1) +#define GEOPOOLINPUTFLAGS_HAS_TANGENT (2) + +// Generated from UnityEngine.Rendering.GeoPoolVertex +// PackingRules = Exact +struct GeoPoolVertex +{ + float3 pos; + float2 uv; + float2 uv1; + float3 N; + float3 T; +}; + +// Generated from UnityEngine.Rendering.GeoPoolSubMeshEntry +// PackingRules = Exact +struct GeoPoolSubMeshEntry +{ + int baseVertex; + int indexStart; + int indexCount; +}; + +// Generated from UnityEngine.Rendering.GeoPoolMetadataEntry +// PackingRules = Exact +struct GeoPoolMetadataEntry +{ + int vertexOffset; + int indexOffset; + int subMeshLookupOffset; + int subMeshEntryOffset; +}; + +// +// Accessors for UnityEngine.Rendering.GeoPoolSubMeshEntry +// +int GetBaseVertex(GeoPoolSubMeshEntry value) +{ + return value.baseVertex; +} +int GetIndexStart(GeoPoolSubMeshEntry value) +{ + return value.indexStart; +} +int GetIndexCount(GeoPoolSubMeshEntry value) +{ + return value.indexCount; +} +// +// Accessors for UnityEngine.Rendering.GeoPoolMetadataEntry +// +int GetVertexOffset(GeoPoolMetadataEntry value) +{ + return value.vertexOffset; +} +int GetIndexOffset(GeoPoolMetadataEntry value) +{ + return value.indexOffset; +} +int GetSubMeshLookupOffset(GeoPoolMetadataEntry value) +{ + return value.subMeshLookupOffset; +} +int GetSubMeshEntryOffset(GeoPoolMetadataEntry value) +{ + return value.subMeshEntryOffset; +} + +#endif diff --git a/com.unity.render-pipelines.core/Runtime/GeometryPool/Resources/GeometryPoolDefs.cs.hlsl.meta b/com.unity.render-pipelines.core/Runtime/GeometryPool/Resources/GeometryPoolDefs.cs.hlsl.meta new file mode 100644 index 00000000000..aef447aad66 --- /dev/null +++ b/com.unity.render-pipelines.core/Runtime/GeometryPool/Resources/GeometryPoolDefs.cs.hlsl.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: 5e15f3bb38ea09147a97f821e5117718 +ShaderIncludeImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/com.unity.render-pipelines.core/Runtime/GeometryPool/Resources/GeometryPoolDefs.cs.meta b/com.unity.render-pipelines.core/Runtime/GeometryPool/Resources/GeometryPoolDefs.cs.meta new file mode 100644 index 00000000000..e7f0194552f --- /dev/null +++ b/com.unity.render-pipelines.core/Runtime/GeometryPool/Resources/GeometryPoolDefs.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 6a41e13b1e5b64e4c94ec93c9b664785 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/com.unity.render-pipelines.core/Runtime/GeometryPool/Resources/GeometryPoolKernels.compute b/com.unity.render-pipelines.core/Runtime/GeometryPool/Resources/GeometryPoolKernels.compute new file mode 100644 index 00000000000..a61ea9c8dcf --- /dev/null +++ b/com.unity.render-pipelines.core/Runtime/GeometryPool/Resources/GeometryPoolKernels.compute @@ -0,0 +1,180 @@ +#pragma kernel MainUpdateIndexBuffer16 +#pragma kernel MainUpdateIndexBuffer32 +#pragma kernel MainUpdateVertexBuffer +#pragma kernel MainUpdateSubMeshData +#pragma kernel MainUpdateMeshMetadata +#pragma kernel MainClearBuffer + +#include "Packages/com.unity.render-pipelines.core/Runtime/GeometryPool/Resources/GeometryPool.hlsl" + +//#pragma enable_d3d11_debug_symbols + +#define _InputIBOffset 0 + +#define GROUP_SIZE_X 64 +#define GROUP_SIZE_X_HALF (GROUP_SIZE_X >> 1) + +int _GeoHandle; +int _GeoVertexOffset; +int _GeoIndexOffset; +int _GeoSubMeshLookupOffset; +int _GeoSubMeshEntryOffset; +RWStructuredBuffer _OutputGeoMetadataBuffer; + +[numthreads(1,1,1)] +void MainUpdateMeshMetadata() +{ + GeoPoolMetadataEntry entry; + entry.vertexOffset = _GeoVertexOffset; + entry.indexOffset = _GeoIndexOffset; + entry.subMeshLookupOffset = _GeoSubMeshLookupOffset; + entry.subMeshEntryOffset = _GeoSubMeshEntryOffset; + _OutputGeoMetadataBuffer[_GeoHandle] = entry; +} + +int _InputIBCount; +int _OutputIBOffset; + +ByteAddressBuffer _InputIndexBuffer; +RWByteAddressBuffer _OutputIndexBuffer; + + +[numthreads(GROUP_SIZE_X,1,1)] +void MainUpdateIndexBuffer32(uint3 dispatchThreadID : SV_DispatchThreadID, int3 groupID : SV_GroupID) +{ + uint bufferOffset = dispatchThreadID.x; + if (bufferOffset >= (uint)_InputIBCount) + return; + + uint storageAddress = ((uint)_OutputIBOffset + bufferOffset) << 2u; + _OutputIndexBuffer.Store(storageAddress, _InputIndexBuffer.Load((_InputIBOffset + bufferOffset) << 2u)); +} + +groupshared uint _ldsIndexCache[GROUP_SIZE_X >> 1]; + +[numthreads(GROUP_SIZE_X,1,1)] +void MainUpdateIndexBuffer16(uint3 dispatchThreadID : SV_DispatchThreadID, uint groupIndex : SV_GroupIndex, uint3 groupID : SV_GroupID) +{ + //only load half the data. + if (groupIndex < GROUP_SIZE_X_HALF) + _ldsIndexCache[groupIndex] = _InputIndexBuffer.Load((GROUP_SIZE_X_HALF * groupID.x + groupIndex) << 2u); + + GroupMemoryBarrierWithGroupSync(); + + uint bufferOffset = dispatchThreadID.x; + if (bufferOffset >= (uint)_InputIBCount) + return; + + uint pair = _ldsIndexCache[groupIndex >> 1u]; + uint value = (groupIndex.x & 0x1) ? (pair >> 16) : (pair & 0xffff); + + uint storageAddress = ((uint)_OutputIBOffset + bufferOffset) << 2; + _OutputIndexBuffer.Store(storageAddress, value); +} + +int _InputVBCount; +int _OutputVBSize; +int _OutputVBOffset; + +int _InputPosBufferStride; +int _InputPosBufferOffset; + +int _InputUv0BufferStride; +int _InputUv0BufferOffset; + +int _InputUv1BufferStride; +int _InputUv1BufferOffset; + +int _InputNormalBufferStride; +int _InputNormalBufferOffset; + +int _InputTangentBufferStride; +int _InputTangentBufferOffset; + +int _InputFlags; + +ByteAddressBuffer _PosBuffer; +ByteAddressBuffer _Uv0Buffer; +ByteAddressBuffer _Uv1Buffer; +ByteAddressBuffer _NormalBuffer; +ByteAddressBuffer _TangentBuffer; + +RWByteAddressBuffer _OutputVB; + +[numthreads(GROUP_SIZE_X, 1, 1)] +void MainUpdateVertexBuffer(uint3 dispatchThreadID : SV_DispatchThreadID) +{ + int vertexOffset = (int)dispatchThreadID.x; + if (vertexOffset >= _InputVBCount) + return; + + GeoPoolVertex vtx; + vtx.pos = asfloat(_PosBuffer.Load3(_InputPosBufferOffset + (vertexOffset * _InputPosBufferStride))); + vtx.uv = asfloat(_Uv0Buffer.Load2(_InputUv0BufferOffset + (vertexOffset * _InputUv0BufferStride))); + + if ((_InputFlags & GEOPOOLINPUTFLAGS_HAS_UV1) != 0) + vtx.uv1 = asfloat(_Uv1Buffer.Load2(_InputUv1BufferOffset + (vertexOffset * _InputUv1BufferStride))); + else + vtx.uv1 = float2(0, 0); + + vtx.N = asfloat(_NormalBuffer.Load3(_InputNormalBufferOffset + (vertexOffset * _InputNormalBufferStride))); + + if ((_InputFlags & GEOPOOLINPUTFLAGS_HAS_TANGENT) != 0) + vtx.T = asfloat(_TangentBuffer.Load3(_InputTangentBufferOffset + (vertexOffset * _InputTangentBufferStride))); + else + vtx.T = float3(0, 0, 0); + + GeometryPool::StoreVertex(_OutputVBOffset + vertexOffset, vtx, _InputFlags, _OutputVBSize, _OutputVB); +} + +RWStructuredBuffer _OutputSubMeshEntryBuffer; +RWByteAddressBuffer _OutputSubMeshLookupBuffer; +int _InputSubMeshIndexStart; +int _InputSubMeshIndexCount; +int _InputSubMeshBaseVertex; +int _InputSubMeshDestIndex; + +int _InputSubmeshLookupDestOffset; +int _InputSubmeshLookupBufferCount; +int _InputSubmeshLookupData; + +[numthreads(GROUP_SIZE_X, 1, 1)] +void MainUpdateSubMeshData( + uint3 dispatchThreadID : SV_DispatchThreadID, + uint3 groupID : SV_GroupID) +{ + + if (all(dispatchThreadID == uint3(0,0,0))) + { + GeoPoolSubMeshEntry entry; + entry.indexStart = _InputSubMeshIndexStart; + entry.indexCount = _InputSubMeshIndexCount; + entry.baseVertex = _InputSubMeshBaseVertex; + _OutputSubMeshEntryBuffer[_InputSubMeshDestIndex] = entry; + } + + if (dispatchThreadID.x >= (uint)_InputSubmeshLookupBufferCount) + return; + + + int outputIndex = _InputSubmeshLookupDestOffset + dispatchThreadID.x; + int outputBucket = 0; + uint packedOutputValue = 0; + GeometryPool::PackSubMeshLookup(outputIndex, _InputSubmeshLookupData, outputBucket, packedOutputValue); + + uint unused; + _OutputSubMeshLookupBuffer.InterlockedOr(outputBucket << 2, packedOutputValue, unused); +} + +RWByteAddressBuffer _ClearBuffer; +int _ClearBufferSize; +int _ClearBufferOffset; +int _ClearBufferValue; +[numthreads(GROUP_SIZE_X, 1, 1)] +void MainClearBuffer(uint3 dispatchThreadID : SV_DispatchThreadID) +{ + if ((int)dispatchThreadID.x >= _ClearBufferSize) + return; + + _ClearBuffer.Store((_ClearBufferOffset + dispatchThreadID.x) << 2, _ClearBufferValue); +} diff --git a/com.unity.render-pipelines.core/Runtime/GeometryPool/Resources/GeometryPoolKernels.compute.meta b/com.unity.render-pipelines.core/Runtime/GeometryPool/Resources/GeometryPoolKernels.compute.meta new file mode 100644 index 00000000000..4d066546913 --- /dev/null +++ b/com.unity.render-pipelines.core/Runtime/GeometryPool/Resources/GeometryPoolKernels.compute.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: d89de086d40570a4ca96b240e9a1806d +ComputeShaderImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/com.unity.render-pipelines.core/Runtime/GeometryPool/Resources/VisibilityBufferCommon.hlsl b/com.unity.render-pipelines.core/Runtime/GeometryPool/Resources/VisibilityBufferCommon.hlsl new file mode 100644 index 00000000000..b1000f4b9a6 --- /dev/null +++ b/com.unity.render-pipelines.core/Runtime/GeometryPool/Resources/VisibilityBufferCommon.hlsl @@ -0,0 +1,24 @@ +#ifndef VBUFFER_COMMON_HLSL +#define VBUFFER_COMMON_HLSL + +#include "VertexBufferCompaction.cs.hlsl" + +void UnpackVisibilityBuffer(uint packedData, out uint clusterID, out uint triangleID) +{ + triangleID = packedData & 127; + // All the remaining 25 bits can be used for cluster (for a max of 33554431 (2^25 - 1) clusters) + clusterID = (packedData >> 7) & 33554431; +} + +uint PackVisBuffer(uint clusterID, uint triangleID) +{ + uint output = 0; + // Cluster size is 128, hence we need 7 bits at most for triangle ID. + output = triangleID & 127; + // All the remaining 25 bits can be used for cluster (for a max of 33554431 (2^25 - 1) clusters) + output |= (clusterID & 33554431) << 7; + return output; +} + + +#endif diff --git a/com.unity.render-pipelines.core/Runtime/GeometryPool/Resources/VisibilityBufferCommon.hlsl.meta b/com.unity.render-pipelines.core/Runtime/GeometryPool/Resources/VisibilityBufferCommon.hlsl.meta new file mode 100644 index 00000000000..b0ae85f8d0f --- /dev/null +++ b/com.unity.render-pipelines.core/Runtime/GeometryPool/Resources/VisibilityBufferCommon.hlsl.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: c4be027cc80a3754b98ba8fecf84f63c +ShaderIncludeImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/com.unity.render-pipelines.core/Tests/Editor/BlockAllocatorTests.cs b/com.unity.render-pipelines.core/Tests/Editor/BlockAllocatorTests.cs new file mode 100644 index 00000000000..d4a21b60bc4 --- /dev/null +++ b/com.unity.render-pipelines.core/Tests/Editor/BlockAllocatorTests.cs @@ -0,0 +1,111 @@ +using NUnit.Framework; +using System.Collections.Generic; +using UnityEngine.Rendering; + +namespace UnityEngine.Rendering.Tests +{ + class BlockAllocatorTests + { + //[SetUp] + //nothing to setup. + + //[TearDown] + //nothing to do tear down. + + [Test] + public void TestBlockAllocatorAddRemove() + { + BlockAllocator ba = new BlockAllocator(); + ba.Initialize(4); + + var a0 = ba.Allocate(1); + var a1 = ba.Allocate(1); + var a2 = ba.Allocate(1); + var a3 = ba.Allocate(1); + + var a4 = ba.Allocate(10); + Assert.IsTrue(!a4.valid); + + ba.FreeAllocation(a0); + ba.FreeAllocation(a1); + ba.FreeAllocation(a2); + ba.FreeAllocation(a3); + + Assert.AreEqual(ba.freeElementsCount, 4); + Assert.AreEqual(ba.freeBlocks, 1); + + ba.Dispose(); + } + + [Test] + public void TestMergeFreeBlocks() + { + BlockAllocator ba = new BlockAllocator(); + ba.Initialize(8); + var a0 = ba.Allocate(2); + var a1 = ba.Allocate(2); //These two allocations get merged when freed. + Assert.AreEqual(ba.freeElementsCount, 4); + + var a2 = ba.Allocate(2); + var a3 = ba.Allocate(2); + Assert.AreEqual(ba.freeElementsCount, 0); + + ba.FreeAllocation(a1); + ba.FreeAllocation(a2); + Assert.AreEqual(ba.freeElementsCount, 4); + + var a4 = ba.Allocate(4); + Assert.IsTrue(a4.valid); + Assert.AreEqual(ba.freeElementsCount, 0); + ba.FreeAllocation(a4); + + //Now try to merge the inverse + a1 = ba.Allocate(2); + Assert.IsTrue(a1.valid); + a2 = ba.Allocate(2); + Assert.IsTrue(a2.valid); + + ba.FreeAllocation(a2); + ba.FreeAllocation(a1); + + a4 = ba.Allocate(4); + Assert.IsTrue(a4.valid); + Assert.AreEqual(ba.freeElementsCount, 0); + ba.FreeAllocation(a4); + + ba.FreeAllocation(a0); + ba.FreeAllocation(a3); + + Assert.AreEqual(ba.freeElementsCount, 8); + Assert.AreEqual(ba.freeBlocks, 1); + ba.Dispose(); + } + + [Test] + public void TestFragmentation() + { + BlockAllocator ba = new BlockAllocator(); + ba.Initialize(8); + + var a0 = ba.Allocate(1); + var a1 = ba.Allocate(2); + var a2 = ba.Allocate(3); + var a3 = ba.Allocate(2); + + var a4 = ba.Allocate(4); + Assert.IsTrue(!a4.valid); + + ba.FreeAllocation(a1); + ba.FreeAllocation(a3); + + a4 = ba.Allocate(4); + Assert.IsTrue(!a4.valid); + + ba.FreeAllocation(a2); + a4 = ba.Allocate(7); + Assert.IsTrue(a4.valid); + + ba.Dispose(); + } + } +} diff --git a/com.unity.render-pipelines.core/Tests/Editor/BlockAllocatorTests.cs.meta b/com.unity.render-pipelines.core/Tests/Editor/BlockAllocatorTests.cs.meta new file mode 100644 index 00000000000..b57fb5d38fa --- /dev/null +++ b/com.unity.render-pipelines.core/Tests/Editor/BlockAllocatorTests.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 3b22001e25ab3ff4c82d8cc46b8a80c4 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/com.unity.render-pipelines.core/Tests/Editor/GeometryPoolTests.cs b/com.unity.render-pipelines.core/Tests/Editor/GeometryPoolTests.cs new file mode 100644 index 00000000000..7a1776e3dff --- /dev/null +++ b/com.unity.render-pipelines.core/Tests/Editor/GeometryPoolTests.cs @@ -0,0 +1,572 @@ +using NUnit.Framework; +using System; +using System.Collections.Generic; +using Unity.Collections; +using UnityEngine.Rendering; + +namespace UnityEngine.Rendering.Tests +{ + class GeometryPoolTests + { + public static Mesh sCube = null; + public static Mesh sSphere = null; + public static Mesh sCapsule = null; + public static Mesh sCube16bit = null; + public static Mesh sCapsule16bit = null; + public static Mesh sMergedCubeSphere = null; + + private static Mesh MergeMeshes(Mesh a, Mesh b) + { + Mesh newMesh = new Mesh(); + CombineInstance[] c = new CombineInstance[2]; + var ca = new CombineInstance(); + ca.transform = Matrix4x4.identity; + ca.subMeshIndex = 0; + ca.mesh = a; + + var cb = new CombineInstance(); + cb.transform = Matrix4x4.identity; + cb.subMeshIndex = 0; + cb.mesh = b; + c[0] = ca; + c[1] = cb; + + newMesh.CombineMeshes(c, false); + newMesh.UploadMeshData(false); + return newMesh; + } + + private static Mesh Create16BitIndexMesh(Mesh input) + { + Mesh newMesh = new Mesh(); + newMesh.vertices = new Vector3[input.vertexCount]; + System.Array.Copy(input.vertices, newMesh.vertices, input.vertexCount); + + newMesh.uv = new Vector2[input.vertexCount]; + System.Array.Copy(input.uv, newMesh.uv, input.vertexCount); + + newMesh.normals = new Vector3[input.vertexCount]; + System.Array.Copy(input.normals, newMesh.normals, input.vertexCount); + + newMesh.vertexBufferTarget = GraphicsBuffer.Target.Raw; + newMesh.indexBufferTarget = GraphicsBuffer.Target.Raw; + + newMesh.subMeshCount = input.subMeshCount; + + int indexCounts = 0; + for (int i = 0; i < input.subMeshCount; ++i) + indexCounts += (int)input.GetIndexCount(i); + + newMesh.SetIndexBufferParams(indexCounts, IndexFormat.UInt16); + + for (int i = 0; i < input.subMeshCount; ++i) + { + newMesh.SetSubMesh(i, input.GetSubMesh(i), MeshUpdateFlags.DontValidateIndices | MeshUpdateFlags.DontNotifyMeshUsers | MeshUpdateFlags.DontRecalculateBounds); + newMesh.SetIndices(input.GetIndices(i), MeshTopology.Triangles, i); + } + + newMesh.UploadMeshData(false); + return newMesh; + } + + internal struct GeometryPoolTestCpuData + { + CommandBuffer m_cmdBuffer; + AsyncGPUReadbackRequest m_request; + GeometryPool m_geometryPool; + + public GeometryPool geoPool { get { return m_geometryPool; } } + public NativeArray gpuIndexData; + public NativeArray gpuVertexData; + public NativeArray gpuSubMeshLookupData; + public NativeArray gpuSubMeshEntryData; + public NativeArray gpuMetadatas; + + public void Load(GeometryPool geometryPool) + { + m_cmdBuffer = new CommandBuffer(); + m_geometryPool = geometryPool; + + var indexData = new NativeArray(geometryPool.indicesCount, Allocator.Persistent); + m_cmdBuffer.RequestAsyncReadback(geometryPool.globalIndexBuffer, (AsyncGPUReadbackRequest req) => + { + if (req.done) + indexData.CopyFrom(req.GetData()); + }); + + var vertData = new NativeArray(geometryPool.verticesCount * (GeometryPool.GetVertexByteSize() / 4), Allocator.Persistent); + m_cmdBuffer.RequestAsyncReadback(geometryPool.globalVertexBuffer, (AsyncGPUReadbackRequest req) => + { + if (req.done) + vertData.CopyFrom(req.GetData()); + }); + + var subMeshLookupData = new NativeArray(geometryPool.subMeshLookupCount / 4, Allocator.Persistent); + m_cmdBuffer.RequestAsyncReadback(geometryPool.globalSubMeshLookupBuffer, (AsyncGPUReadbackRequest req) => + { + if (req.done) + subMeshLookupData.CopyFrom(req.GetData()); + }); + + var subMeshEntryData = new NativeArray(geometryPool.subMeshEntryCount, Allocator.Persistent); + m_cmdBuffer.RequestAsyncReadback(geometryPool.globalSubMeshEntryBuffer, (AsyncGPUReadbackRequest req) => + { + if (req.done) + subMeshEntryData.CopyFrom(req.GetData()); + }); + + var metaData = new NativeArray(geometryPool.maxMeshes, Allocator.Persistent); + m_cmdBuffer.RequestAsyncReadback(geometryPool.globalMetadataBuffer, (AsyncGPUReadbackRequest req) => + { + if (req.done) + metaData.CopyFrom(req.GetData()); + }); + + m_cmdBuffer.WaitAllAsyncReadbackRequests(); + + Graphics.ExecuteCommandBuffer(m_cmdBuffer); + gpuIndexData = indexData; + gpuVertexData = vertData; + gpuSubMeshLookupData = subMeshLookupData; + gpuSubMeshEntryData = subMeshEntryData; + gpuMetadatas = metaData; + } + + public void Dispose() + { + gpuIndexData.Dispose(); + gpuVertexData.Dispose(); + gpuMetadatas.Dispose(); + gpuSubMeshLookupData.Dispose(); + gpuSubMeshEntryData.Dispose(); + m_cmdBuffer.Dispose(); + } + } + + private static bool EpsilonAreEqual(Vector3 a, Vector3 b) + { + var d = a - b; + var minV = Math.Min(Math.Abs(d.x), Math.Min(Math.Abs(d.y), Math.Abs(d.z))); + return minV < Single.Epsilon; + } + + internal static void VerifyMeshInPool( + in GeometryPoolTestCpuData geopoolCpuData, + in GeometryPoolHandle handle, + Mesh mesh) + { + var gpuIndexData = geopoolCpuData.gpuIndexData; + var gpuVertexData = geopoolCpuData.gpuVertexData; + + var idxBufferBlock = geopoolCpuData.geoPool.GetIndexBufferBlock(handle).block; + var idxVertexBlock = geopoolCpuData.geoPool.GetVertexBufferBlock(handle).block; + + //validate indices + for (int smId = 0; smId < (int)mesh.subMeshCount; ++smId) + { + var indices = mesh.GetIndices(smId); + Assert.IsTrue(indices.Length <= idxBufferBlock.count); + if (indices.Length != idxBufferBlock.count) + continue; + + for (int i = 0; i < idxBufferBlock.count; ++i) + { + int expected = indices[i]; + int result = gpuIndexData[idxBufferBlock.offset + i]; + + if (expected != result) + Debug.LogError("Expected index " + expected + " but got " + result); + Assert.IsTrue(expected == result); + } + } + + var srcVertices = mesh.vertices; + var srcNormals = mesh.normals; + + int maxPoolVertCount = geopoolCpuData.geoPool.verticesCount; + + //validate vertices & normals + for (int vId = 0; vId < (int)mesh.vertexCount; ++vId) + { + + int poolVertIndex = idxVertexBlock.offset + vId; + + //sample vertex data + Vector3 srcVertex = srcVertices[vId]; + int posOffset = (maxPoolVertCount * GeometryPoolConstants.GeoPoolPosByteOffset + poolVertIndex * GeometryPoolConstants.GeoPoolPosByteSize) / 4; + + var poolVertex = new Vector3( + gpuVertexData[posOffset + 0], + gpuVertexData[posOffset + 1], + gpuVertexData[posOffset + 2]); + + Assert.IsTrue(EpsilonAreEqual(srcVertex, poolVertex)); + + + //sample normal data + Vector3 srcNormal = srcNormals[vId]; + int normalOffset = (maxPoolVertCount * GeometryPoolConstants.GeoPoolNormalByteOffset + poolVertIndex * GeometryPoolConstants.GeoPoolNormalByteSize) / 4; + + var poolNormal = new Vector3( + gpuVertexData[normalOffset + 0], + gpuVertexData[normalOffset + 1], + gpuVertexData[normalOffset + 2]); + + Assert.IsTrue(EpsilonAreEqual(srcNormal, poolNormal)); + } + + //validate submesh data + var gpuSubMeshLookup = geopoolCpuData.gpuSubMeshLookupData; + var gpuSubMeshEntry = geopoolCpuData.gpuSubMeshEntryData; + var submeshLookupBlock = geopoolCpuData.geoPool.GetSubMeshLookupBlock(handle).block; + var submeshEntryBlock = geopoolCpuData.geoPool.GetSubMeshEntryBlock(handle).block; + for (int subMeshIndex = 0; subMeshIndex < mesh.subMeshCount; ++subMeshIndex) + { + SubMeshDescriptor descriptor = mesh.GetSubMesh(subMeshIndex); + for (int index = descriptor.indexStart; index < descriptor.indexCount; ++index) + { + int lookupIndex = (submeshLookupBlock.offset + (index / 3)); + int lookupBucket = lookupIndex >> 2; + int lookupOffset = lookupIndex & 0x3; + int lookupShift = lookupOffset * 8; + int lookupValue = (gpuSubMeshLookup[lookupBucket] >> lookupShift) & 0xFF; + Assert.IsTrue(lookupValue == subMeshIndex); + } + + var subMeshEntry = gpuSubMeshEntry[submeshEntryBlock.offset + subMeshIndex]; + Assert.IsTrue(subMeshEntry.baseVertex == descriptor.baseVertex); + Assert.IsTrue(subMeshEntry.indexStart == descriptor.indexStart); + Assert.IsTrue(subMeshEntry.indexCount == descriptor.indexCount); + } + + //validate metadata + GeoPoolMetadataEntry metadataEntry = geopoolCpuData.gpuMetadatas[handle.index]; + Assert.AreEqual(metadataEntry.vertexOffset, idxVertexBlock.offset); + Assert.AreEqual(metadataEntry.indexOffset, idxBufferBlock.offset); + } + + + [SetUp] + public void SetupGeometryPoolTests() + { + sCube = GameObject.CreatePrimitive(PrimitiveType.Cube).GetComponent().sharedMesh; + sSphere = GameObject.CreatePrimitive(PrimitiveType.Sphere).GetComponent().sharedMesh; + sCapsule = GameObject.CreatePrimitive(PrimitiveType.Capsule).GetComponent().sharedMesh; + sCube16bit = Create16BitIndexMesh(sCube); + sCapsule16bit = Create16BitIndexMesh(sCapsule); + + var newCube = GameObject.CreatePrimitive(PrimitiveType.Cube).GetComponent(); + sMergedCubeSphere = MergeMeshes(sCube, sSphere); + } + + [TearDown] + public void TearDownGeometryPoolTests() + { + sCube = null; + sSphere = null; + sCapsule = null; + sCube16bit = null; + sCapsule16bit = null; + sMergedCubeSphere = null; + } + + [Test] + public void TestGeometryPoolAddRemove() + { + var geometryPool = new GeometryPool(GeometryPoolDesc.NewDefault()); + bool status; + status = geometryPool.Register(sCube, out var handle0); + Assert.IsTrue(status); + Assert.AreEqual(handle0.index, geometryPool.GetHandle(sCube).index); + + status = geometryPool.Register(sSphere, out var handle1); + Assert.IsTrue(status); + Assert.AreEqual(handle1.index, geometryPool.GetHandle(sSphere).index); + + geometryPool.Unregister(sSphere); + Assert.IsTrue(!geometryPool.GetHandle(sSphere).valid); + + geometryPool.Unregister(sCube); + Assert.IsTrue(!geometryPool.GetHandle(sCube).valid); + + geometryPool.Dispose(); + } + + [Test] + public void TestGeometryPoolRefCount() + { + var geometryPool = new GeometryPool(GeometryPoolDesc.NewDefault()); + + bool status; + + status = geometryPool.Register(sCube, out var handle0); + Assert.IsTrue(status); + status = geometryPool.Register(sCube, out var handle1); + Assert.IsTrue(status); + status = geometryPool.Register(sSphere, out var handle2); + Assert.IsTrue(status); + + Assert.AreEqual(handle0.index, handle1.index); + Assert.AreNotEqual(handle0.index, handle2.index); + + geometryPool.Unregister(sCube); + + Assert.IsTrue(geometryPool.GetHandle(sCube).valid); + + geometryPool.Unregister(sCube); + + Assert.IsTrue(!geometryPool.GetHandle(sCube).valid); + + status = geometryPool.Register(sCube, out var _); + Assert.IsTrue(status); + Assert.IsTrue(geometryPool.GetHandle(sCube).valid); + + geometryPool.Dispose(); + } + + [Test] + public void TestGeometryPoolFailedAllocByIndex() + { + int cubeIndices = 0; + for (int i = 0; i < (int)sCube.subMeshCount; ++i) + cubeIndices += (int)sCube.GetIndexCount(i); + + int sphereIndices = 0; + for (int i = 0; i < (int)sSphere.subMeshCount; ++i) + sphereIndices += (int)sSphere.GetIndexCount(i); + + int capsuleIndices = 0; + for (int i = 0; i < (int)sCapsule.subMeshCount; ++i) + capsuleIndices += (int)sCapsule.GetIndexCount(i); + + var gpdesc = GeometryPoolDesc.NewDefault(); + gpdesc.indexPoolByteSize = (cubeIndices + capsuleIndices) * GeometryPool.GetIndexByteSize(); + + var geometryPool = new GeometryPool(gpdesc); + + bool status; + status = geometryPool.Register(sCube, out var _); + Assert.IsTrue(status); + + status = geometryPool.Register(sCapsule, out var _); + Assert.IsTrue(status); + + status = geometryPool.Register(sSphere, out var _); + Assert.IsTrue(!status); + + geometryPool.Unregister(sCapsule); + + status = geometryPool.Register(sSphere, out var _); + Assert.IsTrue(status); + + geometryPool.Dispose(); + } + + [Test] + public void TestGeometryPoolFailedAllocByMaxVertex() + { + int cubeVertices = sCube.vertexCount; + int sphereVertices = sSphere.vertexCount; + int capsuleVertices = sCapsule.vertexCount; + + var gpdesc = GeometryPoolDesc.NewDefault(); + gpdesc.vertexPoolByteSize = (cubeVertices + capsuleVertices) * GeometryPool.GetVertexByteSize(); + + var geometryPool = new GeometryPool(gpdesc); + + bool status; + status = geometryPool.Register(sCube, out var _); + Assert.IsTrue(status); + + status = geometryPool.Register(sCapsule, out var _); + Assert.IsTrue(status); + + status = geometryPool.Register(sSphere, out var _); + Assert.IsTrue(!status); + + geometryPool.Unregister(sCapsule); + + status = geometryPool.Register(sSphere, out var _); + Assert.IsTrue(status); + + geometryPool.Dispose(); + } + + [Test] + public void TestGeometryPoolFailedAllocByMaxMeshes() + { + var gpdesc = GeometryPoolDesc.NewDefault(); + gpdesc.maxMeshes = 2; + + var geometryPool = new GeometryPool(gpdesc); + + bool status; + status = geometryPool.Register(sCube, out var _); + Assert.IsTrue(status); + + status = geometryPool.Register(sCapsule, out var _); + Assert.IsTrue(status); + + status = geometryPool.Register(sSphere, out var _); + Assert.IsTrue(!status); + + geometryPool.Unregister(sCapsule); + + status = geometryPool.Register(sSphere, out var _); + Assert.IsTrue(status); + + geometryPool.Dispose(); + } + + [Test] + public void TestGpuUploadToGeometryPool() + { + var geometryPool = new GeometryPool(GeometryPoolDesc.NewDefault()); + + bool status; + + status = geometryPool.Register(sCube, out var cubeHandle); + Assert.IsTrue(status); + + status = geometryPool.Register(sSphere, out var sphereHandle); + Assert.IsTrue(status); + + geometryPool.SendGpuCommands(); + + GeometryPoolTestCpuData geopoolCpuData = new GeometryPoolTestCpuData(); + geopoolCpuData.Load(geometryPool); + + VerifyMeshInPool(geopoolCpuData, cubeHandle, sCube); + VerifyMeshInPool(geopoolCpuData, sphereHandle, sSphere); + + geopoolCpuData.Dispose(); + geometryPool.Dispose(); + } + + [Test] + public void TestGpuUploadAddRemoveToGeometryPool() + { + var geometryPool = new GeometryPool(GeometryPoolDesc.NewDefault()); + + bool status; + + status = geometryPool.Register(sSphere, out var sphereHandle); + Assert.IsTrue(status); + + status = geometryPool.Register(sCube, out var cubeHandle); + Assert.IsTrue(status); + + geometryPool.SendGpuCommands(); + + geometryPool.Unregister(sSphere); + + status = geometryPool.Register(sCapsule, out var capsuleHandle); + Assert.IsTrue(status); + + geometryPool.SendGpuCommands(); + + GeometryPoolTestCpuData geopoolCpuData = new GeometryPoolTestCpuData(); + geopoolCpuData.Load(geometryPool); + VerifyMeshInPool(geopoolCpuData, cubeHandle, sCube); + VerifyMeshInPool(geopoolCpuData, capsuleHandle, sCapsule); + + geopoolCpuData.Dispose(); + geometryPool.Dispose(); + } + + [Test] + public void TestGpuUploadIndexBuffer16bitGeometryPool() + { + var geometryPool = new GeometryPool(GeometryPoolDesc.NewDefault()); + + bool status; + + status = geometryPool.Register(sCube16bit, out var cubeHandle); + Assert.IsTrue(status); + + status = geometryPool.Register(sSphere, out var sphereHandle); + Assert.IsTrue(status); + + geometryPool.SendGpuCommands(); + + GeometryPoolTestCpuData geopoolCpuData = new GeometryPoolTestCpuData(); + geopoolCpuData.Load(geometryPool); + + VerifyMeshInPool(geopoolCpuData, cubeHandle, sCube16bit); + VerifyMeshInPool(geopoolCpuData, sphereHandle, sSphere); + + geopoolCpuData.Dispose(); + geometryPool.Dispose(); + } + + [Test] + public void TestGpuUploadAddRemoveIndexBuffer16bitGeometryPool() + { + var geometryPool = new GeometryPool(GeometryPoolDesc.NewDefault()); + + bool status; + + status = geometryPool.Register(sSphere, out var sphereHandle); + Assert.IsTrue(status); + + status = geometryPool.Register(sCube16bit, out var cubeHandle); + Assert.IsTrue(status); + + geometryPool.SendGpuCommands(); + + geometryPool.Unregister(sSphere); + + status = geometryPool.Register(sCapsule16bit, out var capsuleHandle); + Assert.IsTrue(status); + + geometryPool.SendGpuCommands(); + + GeometryPoolTestCpuData geopoolCpuData = new GeometryPoolTestCpuData(); + geopoolCpuData.Load(geometryPool); + VerifyMeshInPool(geopoolCpuData, cubeHandle, sCube16bit); + VerifyMeshInPool(geopoolCpuData, capsuleHandle, sCapsule16bit); + + geopoolCpuData.Dispose(); + geometryPool.Dispose(); + } + + [Test] + public void TestGpuUploadAddRemoveMergedMeshes() + { + var geometryPool = new GeometryPool(GeometryPoolDesc.NewDefault()); + + bool status; + + status = geometryPool.Register(sSphere, out var sphereHandle); + Assert.IsTrue(status); + + + status = geometryPool.Register(sMergedCubeSphere, out var mergedCubeSphereHandle); + Assert.IsTrue(status); + + + status = geometryPool.Register(sCube16bit, out var cubeHandle); + Assert.IsTrue(status); + + geometryPool.SendGpuCommands(); + + geometryPool.Unregister(sSphere); + + status = geometryPool.Register(sCapsule16bit, out var capsuleHandle); + Assert.IsTrue(status); + + geometryPool.SendGpuCommands(); + + GeometryPoolTestCpuData geopoolCpuData = new GeometryPoolTestCpuData(); + geopoolCpuData.Load(geometryPool); + VerifyMeshInPool(geopoolCpuData, cubeHandle, sCube16bit); + VerifyMeshInPool(geopoolCpuData, capsuleHandle, sCapsule16bit); + VerifyMeshInPool(geopoolCpuData, mergedCubeSphereHandle, sMergedCubeSphere); + + + geopoolCpuData.Dispose(); + geometryPool.Dispose(); + } + + } +} diff --git a/com.unity.render-pipelines.core/Tests/Editor/GeometryPoolTests.cs.meta b/com.unity.render-pipelines.core/Tests/Editor/GeometryPoolTests.cs.meta new file mode 100644 index 00000000000..47883ac0530 --- /dev/null +++ b/com.unity.render-pipelines.core/Tests/Editor/GeometryPoolTests.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 09585c88b6196f945aec278342ce561a +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/com.unity.render-pipelines.high-definition/Editor/RenderPipelineResources/DefaultLookDevProfile.asset b/com.unity.render-pipelines.high-definition/Editor/RenderPipelineResources/DefaultLookDevProfile.asset index 65b8ca45fbe..2e7cd4e4142 100644 --- a/com.unity.render-pipelines.high-definition/Editor/RenderPipelineResources/DefaultLookDevProfile.asset +++ b/com.unity.render-pipelines.high-definition/Editor/RenderPipelineResources/DefaultLookDevProfile.asset @@ -30,7 +30,6 @@ MonoBehaviour: m_Name: AmbientOcclusion m_EditorClassIdentifier: active: 1 - m_AdvancedMode: 0 quality: m_OverrideState: 0 m_Value: 1 @@ -40,79 +39,62 @@ MonoBehaviour: intensity: m_OverrideState: 1 m_Value: 0.5 - min: 0 - max: 4 directLightingStrength: m_OverrideState: 0 m_Value: 0 - min: 0 - max: 1 radius: m_OverrideState: 1 m_Value: 1 - min: 0.25 - max: 5 spatialBilateralAggressiveness: m_OverrideState: 0 m_Value: 0.15 - min: 0 - max: 1 temporalAccumulation: m_OverrideState: 0 m_Value: 1 ghostingReduction: m_OverrideState: 0 m_Value: 0.5 - min: 0 - max: 1 blurSharpness: m_OverrideState: 0 m_Value: 0.1 - min: 0 - max: 1 layerMask: m_OverrideState: 0 m_Value: serializedVersion: 2 m_Bits: 4294967295 + occluderMotionRejection: + m_OverrideState: 0 + m_Value: 1 + receiverMotionRejection: + m_OverrideState: 0 + m_Value: 1 m_StepCount: m_OverrideState: 0 m_Value: 6 - min: 2 - max: 32 m_FullResolution: m_OverrideState: 0 m_Value: 0 m_MaximumRadiusInPixels: m_OverrideState: 0 - m_Value: 32 - min: 16 - max: 256 + m_Value: 40 m_BilateralUpsample: m_OverrideState: 0 - m_Value: 0 + m_Value: 1 m_DirectionCount: m_OverrideState: 0 m_Value: 2 - min: 1 - max: 6 m_RayLength: m_OverrideState: 0 m_Value: 3 - min: 0 m_SampleCount: m_OverrideState: 0 m_Value: 2 - min: 1 - max: 64 m_Denoise: m_OverrideState: 0 m_Value: 1 m_DenoiserRadius: m_OverrideState: 0 m_Value: 0.5 - min: 0.001 - max: 1 --- !u!114 &1902828633788537306 MonoBehaviour: m_ObjectHideFlags: 3 @@ -126,21 +108,15 @@ MonoBehaviour: m_Name: HDShadowSettings m_EditorClassIdentifier: active: 1 - m_AdvancedMode: 0 maxShadowDistance: m_OverrideState: 1 m_Value: 25 - min: 0 directionalTransmissionMultiplier: m_OverrideState: 0 m_Value: 1 - min: 0 - max: 1 cascadeShadowSplitCount: m_OverrideState: 1 m_Value: 2 - min: 1 - max: 4 cascadeShadowSplit0: m_OverrideState: 0 m_Value: 0.05 @@ -175,37 +151,27 @@ MonoBehaviour: m_Name: Bloom m_EditorClassIdentifier: active: 1 - m_AdvancedMode: 0 quality: m_OverrideState: 0 m_Value: 3 threshold: m_OverrideState: 0 m_Value: 0 - min: 0 intensity: m_OverrideState: 1 m_Value: 0.1 - min: 0 - max: 1 scatter: m_OverrideState: 0 m_Value: 0.7 - min: 0 - max: 1 tint: m_OverrideState: 0 m_Value: {r: 1, g: 1, b: 1, a: 1} - hdr: 0 - showAlpha: 0 - showEyeDropper: 1 dirtTexture: m_OverrideState: 0 m_Value: {fileID: 0} dirtIntensity: m_OverrideState: 0 m_Value: 0 - min: 0 anamorphic: m_OverrideState: 0 m_Value: 1 @@ -231,43 +197,30 @@ MonoBehaviour: m_Name: Tonemapping m_EditorClassIdentifier: active: 1 - m_AdvancedMode: 0 mode: m_OverrideState: 1 m_Value: 1 toeStrength: m_OverrideState: 0 m_Value: 0 - min: 0 - max: 1 toeLength: m_OverrideState: 0 m_Value: 0.5 - min: 0 - max: 1 shoulderStrength: m_OverrideState: 0 m_Value: 0 - min: 0 - max: 1 shoulderLength: m_OverrideState: 0 m_Value: 0.5 - min: 0 shoulderAngle: m_OverrideState: 0 m_Value: 0 - min: 0 - max: 1 gamma: m_OverrideState: 0 m_Value: 1 - min: 0.001 lutTexture: m_OverrideState: 0 m_Value: {fileID: 0} lutContribution: m_OverrideState: 0 m_Value: 1 - min: 0 - max: 1 diff --git a/com.unity.render-pipelines.high-definition/Runtime/Debug/DebugDisplay.cs b/com.unity.render-pipelines.high-definition/Runtime/Debug/DebugDisplay.cs index 65ad4de7b73..32d7f01265e 100644 --- a/com.unity.render-pipelines.high-definition/Runtime/Debug/DebugDisplay.cs +++ b/com.unity.render-pipelines.high-definition/Runtime/Debug/DebugDisplay.cs @@ -123,6 +123,8 @@ public enum FullScreenDebugMode RequestedVirtualTextureTiles, /// Black background to visualize the Lens Flare LensFlareDataDriven, + /// Display the visibility buffer. + VisibilityBuffer, /// Maximum Full Screen Rendering debug mode value (used internally). MaxRenderingFullScreenDebug, diff --git a/com.unity.render-pipelines.high-definition/Runtime/Debug/DebugDisplay.cs.hlsl b/com.unity.render-pipelines.high-definition/Runtime/Debug/DebugDisplay.cs.hlsl index 895132b22f1..a3f2c90cce5 100644 --- a/com.unity.render-pipelines.high-definition/Runtime/Debug/DebugDisplay.cs.hlsl +++ b/com.unity.render-pipelines.high-definition/Runtime/Debug/DebugDisplay.cs.hlsl @@ -35,14 +35,15 @@ #define FULLSCREENDEBUGMODE_VERTEX_DENSITY (25) #define FULLSCREENDEBUGMODE_REQUESTED_VIRTUAL_TEXTURE_TILES (26) #define FULLSCREENDEBUGMODE_LENS_FLARE_DATA_DRIVEN (27) -#define FULLSCREENDEBUGMODE_MAX_RENDERING_FULL_SCREEN_DEBUG (28) -#define FULLSCREENDEBUGMODE_MIN_MATERIAL_FULL_SCREEN_DEBUG (29) -#define FULLSCREENDEBUGMODE_VALIDATE_DIFFUSE_COLOR (30) -#define FULLSCREENDEBUGMODE_VALIDATE_SPECULAR_COLOR (31) -#define FULLSCREENDEBUGMODE_MAX_MATERIAL_FULL_SCREEN_DEBUG (32) -#define FULLSCREENDEBUGMODE_SCREEN_SPACE_REFLECTIONS_PREV (33) -#define FULLSCREENDEBUGMODE_SCREEN_SPACE_REFLECTIONS_ACCUM (34) -#define FULLSCREENDEBUGMODE_WORLD_SPACE_POSITION (35) +#define FULLSCREENDEBUGMODE_VISIBILITY_BUFFER (28) +#define FULLSCREENDEBUGMODE_MAX_RENDERING_FULL_SCREEN_DEBUG (29) +#define FULLSCREENDEBUGMODE_MIN_MATERIAL_FULL_SCREEN_DEBUG (30) +#define FULLSCREENDEBUGMODE_VALIDATE_DIFFUSE_COLOR (31) +#define FULLSCREENDEBUGMODE_VALIDATE_SPECULAR_COLOR (32) +#define FULLSCREENDEBUGMODE_MAX_MATERIAL_FULL_SCREEN_DEBUG (33) +#define FULLSCREENDEBUGMODE_SCREEN_SPACE_REFLECTIONS_PREV (34) +#define FULLSCREENDEBUGMODE_SCREEN_SPACE_REFLECTIONS_ACCUM (35) +#define FULLSCREENDEBUGMODE_WORLD_SPACE_POSITION (36) // Generated from UnityEngine.Rendering.HighDefinition.ShaderVariablesDebugDisplay // PackingRules = Exact diff --git a/com.unity.render-pipelines.high-definition/Runtime/Debug/DebugFullScreen.shader b/com.unity.render-pipelines.high-definition/Runtime/Debug/DebugFullScreen.shader index 4b1b5362fbb..ab357a04788 100644 --- a/com.unity.render-pipelines.high-definition/Runtime/Debug/DebugFullScreen.shader +++ b/com.unity.render-pipelines.high-definition/Runtime/Debug/DebugFullScreen.shader @@ -26,6 +26,7 @@ Shader "Hidden/HDRP/DebugFullScreen" #include "Packages/com.unity.render-pipelines.high-definition/Runtime/Debug/DebugDisplay.hlsl" #include "Packages/com.unity.render-pipelines.high-definition/Runtime/Debug/FullScreenDebug.hlsl" #include "Packages/com.unity.render-pipelines.high-definition/Runtime/Material/Builtin/BuiltinData.hlsl" + #include "Packages/com.unity.render-pipelines.high-definition/Runtime/Material/Visibility/Visibility.hlsl" CBUFFER_START (UnityDebug) float _FullScreenDebugMode; @@ -328,6 +329,13 @@ Shader "Hidden/HDRP/DebugFullScreen" float4 color = SAMPLE_TEXTURE2D_X(_DebugFullScreenTexture, s_point_clamp_sampler, input.texcoord) * GetCurrentExposureMultiplier(); return float4(color.rgb, 1.0f); } + if (_FullScreenDebugMode == FULLSCREENDEBUGMODE_VISIBILITY_BUFFER) + { + uint value = LOAD_TEXTURE2D_X(_VisibilityTexture, (uint2)input.positionCS.xy).x; + VisibilityData visData; + unpackVisibilityData(value, visData); + return float4(DebugVisIndexToRGB(visData.primitiveID), 1.0f); + } if (_FullScreenDebugMode == FULLSCREENDEBUGMODE_PRE_REFRACTION_COLOR_PYRAMID || _FullScreenDebugMode == FULLSCREENDEBUGMODE_FINAL_COLOR_PYRAMID) { diff --git a/com.unity.render-pipelines.high-definition/Runtime/Material/Visibility.meta b/com.unity.render-pipelines.high-definition/Runtime/Material/Visibility.meta new file mode 100644 index 00000000000..79d9d52ad11 --- /dev/null +++ b/com.unity.render-pipelines.high-definition/Runtime/Material/Visibility.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 4e22e145f7af7484397330e8c25e564d +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/com.unity.render-pipelines.high-definition/Runtime/Material/Visibility/Visibility.hlsl b/com.unity.render-pipelines.high-definition/Runtime/Material/Visibility/Visibility.hlsl new file mode 100644 index 00000000000..ed958206527 --- /dev/null +++ b/com.unity.render-pipelines.high-definition/Runtime/Material/Visibility/Visibility.hlsl @@ -0,0 +1,49 @@ +#ifndef VISIBILITY_HLSL +#define VISIBILITY_HLSL + +TEXTURE2D_X_UINT(_VisibilityTexture); + +struct VisibilityData +{ + uint DOTSInstanceIndex; + uint primitiveID; +}; + +#define InvalidVisibilityData 0xffffffff + +float3 DebugVisIndexToRGB(uint index) +{ + if (index == 0) + return float3(0, 0, 0); + + // Xorshift*32 + // Based on George Marsaglia's work: http://www.jstatsoft.org/v08/i14/paper + uint value = index; + value ^= value << 13; + value ^= value >> 17; + value ^= value << 5; + + float H = float(value & 511) / 511.0; + + //standard hue to HSV + float R = abs(H * 6 - 3) - 1; + float G = 2 - abs(H * 6 - 2); + float B = 2 - abs(H * 6 - 4); + return saturate(float3(R,G,B)); +} + +uint packVisibilityData(in VisibilityData data) +{ + uint packedData = 0; + packedData |= (data.DOTSInstanceIndex & 0xffff); + packedData |= (data.primitiveID & 0xffff) << 16; + return packedData; +} + +void unpackVisibilityData(uint packedVisData, out VisibilityData data) +{ + data.DOTSInstanceIndex = (packedVisData & 0xffff); + data.primitiveID = packedVisData >> 16; +} + +#endif diff --git a/com.unity.render-pipelines.high-definition/Runtime/Material/Visibility/Visibility.hlsl.meta b/com.unity.render-pipelines.high-definition/Runtime/Material/Visibility/Visibility.hlsl.meta new file mode 100644 index 00000000000..6d80dc999cb --- /dev/null +++ b/com.unity.render-pipelines.high-definition/Runtime/Material/Visibility/Visibility.hlsl.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: 194094c6ec44c854194322ef0ac48116 +ShaderIncludeImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/com.unity.render-pipelines.high-definition/Runtime/Material/Visibility/Visibility.shader b/com.unity.render-pipelines.high-definition/Runtime/Material/Visibility/Visibility.shader new file mode 100644 index 00000000000..1082d7a19ad --- /dev/null +++ b/com.unity.render-pipelines.high-definition/Runtime/Material/Visibility/Visibility.shader @@ -0,0 +1,70 @@ +Shader "HDRP/Visibility" +{ + Properties + { + _DeferredMaterialInstanceData ("", Vector) = (1.0, 1.0, 1.0, 1.0) + + // Mandatory or we get yelled by the runtime + _MainTex("Albedo", 2D) = "white" {} + _Color("Color", Color) = (0,0,0,0) + } + + HLSLINCLUDE + + #pragma target 4.5 + #pragma enable_d3d11_debug_symbols + + //------------------------------------------------------------------------------------- + // Variant + //------------------------------------------------------------------------------------- + + + //------------------------------------------------------------------------------------- + // Include + //------------------------------------------------------------------------------------- + + #include "Packages/com.unity.render-pipelines.core/ShaderLibrary/Common.hlsl" + #include "Packages/com.unity.render-pipelines.high-definition/Runtime/ShaderLibrary/ShaderVariables.hlsl" + #include "Packages/com.unity.render-pipelines.high-definition/Runtime/RenderPipeline/ShaderPass/FragInputs.hlsl" + #include "Packages/com.unity.render-pipelines.high-definition/Runtime/RenderPipeline/ShaderPass/ShaderPass.cs.hlsl" + + //------------------------------------------------------------------------------------- + // variable declaration + //------------------------------------------------------------------------------------- + + // #include "Packages/com.unity.render-pipelines.high-definition/Runtime/Material/Lit/Lit.cs.hlsl" + //#include "Packages/com.unity.render-pipelines.high-definition/Runtime/Material/Lit/LitProperties.hlsl" + + ENDHLSL + + SubShader + { + // This tags allow to use the shader replacement features + Tags{ "RenderPipeline"="HDRenderPipeline" } + + Pass + { + Name "VBuffer" + Tags { "LightMode" = "VBuffer" } // This will be only for opaque object based on the RenderQueue index + + Cull Back + ZTest Less + ZWrite On + + HLSLPROGRAM + + #pragma only_renderers d3d11 playstation xboxone xboxseries vulkan metal switch + //enable GPU instancing support + #pragma multi_compile_instancing + #pragma instancing_options renderinglayer + #pragma multi_compile _ DOTS_INSTANCING_ON + + #include "Packages/com.unity.render-pipelines.high-definition/Runtime/RenderPipeline/ShaderPass/ShaderPassVisibility.hlsl" + + #pragma vertex Vert + #pragma fragment Frag + + ENDHLSL + } + } +} diff --git a/com.unity.render-pipelines.high-definition/Runtime/Material/Visibility/Visibility.shader.meta b/com.unity.render-pipelines.high-definition/Runtime/Material/Visibility/Visibility.shader.meta new file mode 100644 index 00000000000..8a063e7a6a0 --- /dev/null +++ b/com.unity.render-pipelines.high-definition/Runtime/Material/Visibility/Visibility.shader.meta @@ -0,0 +1,9 @@ +fileFormatVersion: 2 +guid: 633338306378163479b295584aba76c3 +ShaderImporter: + externalObjects: {} + defaultTextures: [] + nonModifiableTextures: [] + userData: + assetBundleName: + assetBundleVariant: diff --git a/com.unity.render-pipelines.high-definition/Runtime/RenderPipeline/HDProfileId.cs b/com.unity.render-pipelines.high-definition/Runtime/RenderPipeline/HDProfileId.cs index e9572c5b651..286d1edff68 100644 --- a/com.unity.render-pipelines.high-definition/Runtime/RenderPipeline/HDProfileId.cs +++ b/com.unity.render-pipelines.high-definition/Runtime/RenderPipeline/HDProfileId.cs @@ -20,6 +20,7 @@ internal enum HDProfileId ForwardDepthPrepass, DeferredDepthPrepass, TransparentDepthPrepass, + VBuffer, GBuffer, DBufferRender, DBufferPrepareDrawData, diff --git a/com.unity.render-pipelines.high-definition/Runtime/RenderPipeline/HDRenderPipeline.Debug.cs b/com.unity.render-pipelines.high-definition/Runtime/RenderPipeline/HDRenderPipeline.Debug.cs index 931bb194939..90014d3a3fb 100644 --- a/com.unity.render-pipelines.high-definition/Runtime/RenderPipeline/HDRenderPipeline.Debug.cs +++ b/com.unity.render-pipelines.high-definition/Runtime/RenderPipeline/HDRenderPipeline.Debug.cs @@ -318,11 +318,12 @@ class ResolveFullScreenDebugPassData public ComputeBuffer depthPyramidOffsets; public TextureHandle output; public TextureHandle input; + public TextureHandle vBuffer; public TextureHandle depthPyramid; public ComputeBufferHandle fullscreenBuffer; } - TextureHandle ResolveFullScreenDebug(RenderGraph renderGraph, TextureHandle inputFullScreenDebug, TextureHandle depthPyramid, HDCamera hdCamera, GraphicsFormat rtFormat = GraphicsFormat.R16G16B16A16_SFloat) + TextureHandle ResolveFullScreenDebug(RenderGraph renderGraph, TextureHandle inputFullScreenDebug, TextureHandle vBuffer, TextureHandle depthPyramid, HDCamera hdCamera, GraphicsFormat rtFormat = GraphicsFormat.R16G16B16A16_SFloat) { using (var builder = renderGraph.AddRenderPass("ResolveFullScreenDebug", out var passData)) { @@ -330,6 +331,7 @@ TextureHandle ResolveFullScreenDebug(RenderGraph renderGraph, TextureHandle inpu passData.debugDisplaySettings = m_CurrentDebugDisplaySettings; passData.debugFullScreenMaterial = m_DebugFullScreen; passData.input = builder.ReadTexture(inputFullScreenDebug); + passData.vBuffer = builder.ReadTexture(vBuffer); passData.depthPyramid = builder.ReadTexture(depthPyramid); passData.depthPyramidMip = (int)(m_CurrentDebugDisplaySettings.data.fullscreenDebugMip * GetDepthBufferMipChainInfo().mipLevelCount); passData.depthPyramidOffsets = GetDepthBufferMipChainInfo().GetOffsetBufferData(m_DepthPyramidMipLevelOffsetsBuffer); @@ -350,6 +352,7 @@ TextureHandle ResolveFullScreenDebug(RenderGraph renderGraph, TextureHandle inpu mpb.SetTexture(HDShaderIDs._DebugFullScreenTexture, data.input); mpb.SetTexture(HDShaderIDs._CameraDepthTexture, data.depthPyramid); + mpb.SetTexture(HDShaderIDs._VisibilityTexture, data.vBuffer); mpb.SetFloat(HDShaderIDs._FullScreenDebugMode, (float)data.debugDisplaySettings.data.fullScreenDebugMode); if (data.debugDisplaySettings.data.enableDebugDepthRemap) mpb.SetVector(HDShaderIDs._FullScreenDebugDepthRemap, new Vector4(data.debugDisplaySettings.data.fullScreenDebugDepthRemap.x, data.debugDisplaySettings.data.fullScreenDebugDepthRemap.y, data.hdCamera.camera.nearClipPlane, data.hdCamera.camera.farClipPlane)); @@ -1001,6 +1004,7 @@ TextureHandle RenderExposureDebug(RenderGraph renderGraph, HDCamera hdCamera, Te TextureHandle RenderDebug(RenderGraph renderGraph, HDCamera hdCamera, TextureHandle colorBuffer, + TextureHandle vBuffer, TextureHandle depthBuffer, TextureHandle depthPyramidTexture, TextureHandle colorPickerDebugTexture, @@ -1018,7 +1022,7 @@ TextureHandle RenderDebug(RenderGraph renderGraph, if (NeedsFullScreenDebugMode() && m_FullScreenDebugPushed) { - output = ResolveFullScreenDebug(renderGraph, m_DebugFullScreenTexture, depthPyramidTexture, hdCamera, colorFormat); + output = ResolveFullScreenDebug(renderGraph, m_DebugFullScreenTexture, vBuffer, depthPyramidTexture, hdCamera, colorFormat); // If we have full screen debug, this is what we want color picked, so we replace color picker input texture with the new one. if (NeedColorPickerDebug(m_CurrentDebugDisplaySettings)) diff --git a/com.unity.render-pipelines.high-definition/Runtime/RenderPipeline/HDRenderPipeline.Prepass.cs b/com.unity.render-pipelines.high-definition/Runtime/RenderPipeline/HDRenderPipeline.Prepass.cs index 621b1509bb5..b417ed11b55 100644 --- a/com.unity.render-pipelines.high-definition/Runtime/RenderPipeline/HDRenderPipeline.Prepass.cs +++ b/com.unity.render-pipelines.high-definition/Runtime/RenderPipeline/HDRenderPipeline.Prepass.cs @@ -79,6 +79,8 @@ struct PrepassOutput public TextureHandle normalBuffer; public TextureHandle motionVectorsBuffer; + public VBufferOutput vbuffer; + // GBuffer output. Will also contain a reference to the normal buffer (as it is shared between deferred and forward objects) public GBufferOutput gbuffer; @@ -237,6 +239,8 @@ PrepassOutput RenderPrepass(RenderGraph renderGraph, RenderObjectsMotionVectors(renderGraph, cullingResults, hdCamera, decalBuffer, result); } + RenderVBuffer(renderGraph, hdCamera, cullingResults, ref result); + // If we have MSAA, we need to complete the motion vector buffer before buffer resolves, hence we need to run camera mv first. // This is always fine since shouldRenderMotionVectorAfterGBuffer is always false for forward. bool needCameraMVBeforeResolve = msaa; diff --git a/com.unity.render-pipelines.high-definition/Runtime/RenderPipeline/HDRenderPipeline.RenderGraph.cs b/com.unity.render-pipelines.high-definition/Runtime/RenderPipeline/HDRenderPipeline.RenderGraph.cs index ae1b267621a..2dc0b3e5215 100644 --- a/com.unity.render-pipelines.high-definition/Runtime/RenderPipeline/HDRenderPipeline.RenderGraph.cs +++ b/com.unity.render-pipelines.high-definition/Runtime/RenderPipeline/HDRenderPipeline.RenderGraph.cs @@ -286,6 +286,7 @@ void RecordRenderGraph(RenderRequest renderRequest, postProcessDest = RenderDebug(m_RenderGraph, hdCamera, postProcessDest, + prepassOutput.vbuffer.vbuffer, prepassOutput.resolvedDepthBuffer, prepassOutput.depthPyramidTexture, colorPickerTexture, diff --git a/com.unity.render-pipelines.high-definition/Runtime/RenderPipeline/HDRenderPipeline.Visibility.cs b/com.unity.render-pipelines.high-definition/Runtime/RenderPipeline/HDRenderPipeline.Visibility.cs new file mode 100644 index 00000000000..6434a51a4ed --- /dev/null +++ b/com.unity.render-pipelines.high-definition/Runtime/RenderPipeline/HDRenderPipeline.Visibility.cs @@ -0,0 +1,72 @@ +using System.Collections.Generic; +using UnityEngine.Experimental.Rendering; +using UnityEngine.Experimental.Rendering.RenderGraphModule; +using Unity.Collections; + +namespace UnityEngine.Rendering.HighDefinition +{ + public partial class HDRenderPipeline + { + struct VBufferOutput + { + public TextureHandle vbuffer; + } + + internal bool IsVisibilityPassEnabled() + { + return currentAsset != null && currentAsset.VisibilityMaterial != null; + } + + class VBufferPassData + { + public FrameSettings frameSettings; + public RendererListHandle rendererList; + } + + void RenderVBuffer(RenderGraph renderGraph, HDCamera hdCamera, CullingResults cull, ref PrepassOutput output) + { + output.vbuffer = new VBufferOutput(); + + var globalGeoPool = RenderBRG.FindGlobalGeometryPool(); + if (!IsVisibilityPassEnabled() || globalGeoPool == null) + { + output.vbuffer.vbuffer = renderGraph.defaultResources.blackUIntTextureXR; + return; + } + + var visibilityMaterial = currentAsset.VisibilityMaterial; + var visFormat = GraphicsFormat.R32_UInt; + using (var builder = renderGraph.AddRenderPass("VBuffer", out var passData, ProfilingSampler.Get(HDProfileId.VBuffer))) + { + builder.AllowRendererListCulling(false); + + FrameSettings frameSettings = hdCamera.frameSettings; + + passData.frameSettings = frameSettings; + + output.depthBuffer = builder.UseDepthBuffer(output.depthBuffer, DepthAccess.ReadWrite); + output.vbuffer.vbuffer = builder.UseColorBuffer(renderGraph.CreateTexture( + new TextureDesc(Vector2.one, true, true) + { + colorFormat = visFormat, + clearBuffer = true,//TODO: for now clear + clearColor = Color.clear, + name = "VisibilityBuffer" + }), 0); + + passData.rendererList = builder.UseRendererList( + renderGraph.CreateRendererList(CreateOpaqueRendererListDesc( + cull, hdCamera.camera, + HDShaderPassNames.s_VBufferName, m_CurrentRendererConfigurationBakedLighting, null, null, visibilityMaterial, excludeObjectMotionVectors: false))); + + globalGeoPool.BindResources(visibilityMaterial); + + builder.SetRenderFunc( + (VBufferPassData data, RenderGraphContext context) => + { + DrawOpaqueRendererList(context, data.frameSettings, data.rendererList); + }); + } + } + } +} diff --git a/com.unity.render-pipelines.high-definition/Runtime/RenderPipeline/HDRenderPipeline.Visibility.cs.meta b/com.unity.render-pipelines.high-definition/Runtime/RenderPipeline/HDRenderPipeline.Visibility.cs.meta new file mode 100644 index 00000000000..d03b5c61932 --- /dev/null +++ b/com.unity.render-pipelines.high-definition/Runtime/RenderPipeline/HDRenderPipeline.Visibility.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: fd4ef76fe1020bb488267eaf6d991d34 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/com.unity.render-pipelines.high-definition/Runtime/RenderPipeline/HDRenderPipelineAsset.cs b/com.unity.render-pipelines.high-definition/Runtime/RenderPipeline/HDRenderPipelineAsset.cs index e8c40b1074a..049b397e06d 100644 --- a/com.unity.render-pipelines.high-definition/Runtime/RenderPipeline/HDRenderPipelineAsset.cs +++ b/com.unity.render-pipelines.high-definition/Runtime/RenderPipeline/HDRenderPipelineAsset.cs @@ -222,5 +222,20 @@ bool UpdateDefineList(bool flagValue, string defineMacroValue) /// Indicates if virtual texturing is currently enabled for this render pipeline instance. /// public bool virtualTexturingEnabled { get { return true; } } + + private static Material s_VisibilityMaterial = null; + public Material VisibilityMaterial { get { return s_VisibilityMaterial; } } + + public override RenderPipelineAsset.VisibilityMaterialRendererInfo GetVisibilityMaterialInfoForRenderer(RenderPipelineAsset.GetVisibilityMaterialInfoForRendererArgs arguments) + { + if (s_VisibilityMaterial == null) + s_VisibilityMaterial = CoreUtils.CreateEngineMaterial(globalSettings.renderPipelineResources.shaders.visibilityPS); + + return new RenderPipelineAsset.VisibilityMaterialRendererInfo() + { + supportsVisibility = true, + materialOverride = s_VisibilityMaterial + }; + } } } diff --git a/com.unity.render-pipelines.high-definition/Runtime/RenderPipeline/HDRenderPipelineRuntimeResources.cs b/com.unity.render-pipelines.high-definition/Runtime/RenderPipeline/HDRenderPipelineRuntimeResources.cs index fb8598e0a73..2335c33596e 100644 --- a/com.unity.render-pipelines.high-definition/Runtime/RenderPipeline/HDRenderPipelineRuntimeResources.cs +++ b/com.unity.render-pipelines.high-definition/Runtime/RenderPipeline/HDRenderPipelineRuntimeResources.cs @@ -121,6 +121,9 @@ public sealed class ShaderResources [Reload("Runtime/ShaderLibrary/ResolveStencilBuffer.compute")] public ComputeShader resolveStencilCS; + [Reload("Runtime/Material/Visibility/Visibility.shader")] + public Shader visibilityPS; + // Sky [Reload("Runtime/Sky/BlitCubemap.shader")] public Shader blitCubemapPS; diff --git a/com.unity.render-pipelines.high-definition/Runtime/RenderPipeline/HDStringConstants.cs b/com.unity.render-pipelines.high-definition/Runtime/RenderPipeline/HDStringConstants.cs index 27ea6b10e65..4f60eff92ec 100644 --- a/com.unity.render-pipelines.high-definition/Runtime/RenderPipeline/HDStringConstants.cs +++ b/com.unity.render-pipelines.high-definition/Runtime/RenderPipeline/HDStringConstants.cs @@ -14,6 +14,8 @@ public static class HDShaderPassNames public static readonly string s_DepthForwardOnlyStr = "DepthForwardOnly"; /// Forward Only pass name. public static readonly string s_ForwardOnlyStr = "ForwardOnly"; + /// VBuffer pass name. + public static readonly string s_VBufferStr = "VBuffer"; /// GBuffer pass name. public static readonly string s_GBufferStr = "GBuffer"; /// GBuffer With Prepass pass name. @@ -65,6 +67,8 @@ public static class HDShaderPassNames public static readonly ShaderTagId s_DepthForwardOnlyName = new ShaderTagId(s_DepthForwardOnlyStr); /// Forward Only shader tag id. public static readonly ShaderTagId s_ForwardOnlyName = new ShaderTagId(s_ForwardOnlyStr); + /// VBuffer shader tag id. + public static readonly ShaderTagId s_VBufferName = new ShaderTagId(s_VBufferStr); /// GBuffer shader tag id. public static readonly ShaderTagId s_GBufferName = new ShaderTagId(s_GBufferStr); /// GBufferWithPrepass shader tag id. @@ -697,6 +701,9 @@ static class HDShaderIDs public static readonly int _InputBufferDimension = Shader.PropertyToID("_InputBufferDimension"); public static readonly int _OutputBufferDimension = Shader.PropertyToID("_OutputBufferDimension"); + // Visibility Buffer + public static readonly int _VisibilityTexture = Shader.PropertyToID("_VisibilityTexture"); + // Primary Visibility public static readonly int _RaytracingFlagMask = Shader.PropertyToID("_RaytracingFlagMask"); public static readonly int _RaytracingPrimaryDebug = Shader.PropertyToID("_RaytracingPrimaryDebug"); @@ -1017,6 +1024,9 @@ static class HDShaderIDs public static readonly int _SrcMip = Shader.PropertyToID("_SrcMip"); public static readonly int _SrcScale = Shader.PropertyToID("_SrcScale"); public static readonly int _SrcOffset = Shader.PropertyToID("_SrcOffset"); + + //Visibility material + public static readonly int _VisBufferInstanceData = Shader.PropertyToID("_VisBufferInstanceData"); } /// diff --git a/com.unity.render-pipelines.high-definition/Runtime/RenderPipeline/ShaderPass/ShaderPassVisibility.hlsl b/com.unity.render-pipelines.high-definition/Runtime/RenderPipeline/ShaderPass/ShaderPassVisibility.hlsl new file mode 100644 index 00000000000..22bf2fda479 --- /dev/null +++ b/com.unity.render-pipelines.high-definition/Runtime/RenderPipeline/ShaderPass/ShaderPassVisibility.hlsl @@ -0,0 +1,75 @@ +#ifndef VISIBILITY_PASS_HLSL +#define VISIBILITY_PASS_HLSL + + +#include "Packages/com.unity.render-pipelines.high-definition/Runtime/Material/Visibility/Visibility.hlsl" +#include "Packages/com.unity.render-pipelines.core/Runtime/GeometryPool/Resources/GeometryPool.hlsl" + +CBUFFER_START(UnityPerMaterial) + float4 _DeferredMaterialInstanceData; +CBUFFER_END + +#if defined(UNITY_DOTS_INSTANCING_ENABLED) +UNITY_DOTS_INSTANCING_START(MaterialPropertyMetadata) + UNITY_DOTS_INSTANCED_PROP(float4, _DeferredMaterialInstanceData) +UNITY_DOTS_INSTANCING_END(MaterialPropertyMetadata) + +#define _DeferredMaterialInstanceData UNITY_ACCESS_DOTS_INSTANCED_PROP_WITH_DEFAULT(float4, _DeferredMaterialInstanceData) + +#endif + +struct GeoPoolInput +{ + uint vertId : SV_VertexID; + UNITY_VERTEX_INPUT_INSTANCE_ID +}; + +struct VisibilityVtoP +{ + float4 pos : SV_Position; + + UNITY_VERTEX_INPUT_INSTANCE_ID + UNITY_VERTEX_OUTPUT_STEREO +}; + +struct VisibilityDrawInput +{ + uint vertexIndex : SV_VertexID; + UNITY_VERTEX_INPUT_INSTANCE_ID +}; + +VisibilityVtoP Vert(VisibilityDrawInput input) +{ + VisibilityVtoP v2p; + + UNITY_SETUP_INSTANCE_ID(input); + UNITY_TRANSFER_INSTANCE_ID(input, v2p); + + GeoPoolMetadataEntry metadata = _GeoPoolGlobalMetadataBuffer[(int)_DeferredMaterialInstanceData.x]; + + GeoPoolVertex vertexData; + GeometryPool::LoadVertex(input.vertexIndex, metadata, vertexData); + + float3 worldPos = TransformObjectToWorld(vertexData.pos); + v2p.pos = TransformWorldToHClip(worldPos); + return v2p; +} + +void Frag( + VisibilityVtoP packedInput, + uint primitiveID : SV_PrimitiveID, + out uint outVisibility : SV_Target0) +{ + UNITY_SETUP_STEREO_EYE_INDEX_POST_VERTEX(packedInput); + UNITY_SETUP_INSTANCE_ID(packedInput); + #ifdef DOTS_INSTANCING_ON + VisibilityData visData; + visData.DOTSInstanceIndex = GetDOTSInstanceIndex(); + visData.primitiveID = primitiveID; + outVisibility = packVisibilityData(visData); + #else + outVisibility = InvalidVisibilityData; + #endif +} + +#endif diff --git a/com.unity.render-pipelines.high-definition/Runtime/RenderPipeline/ShaderPass/ShaderPassVisibility.hlsl.meta b/com.unity.render-pipelines.high-definition/Runtime/RenderPipeline/ShaderPass/ShaderPassVisibility.hlsl.meta new file mode 100644 index 00000000000..2182580291f --- /dev/null +++ b/com.unity.render-pipelines.high-definition/Runtime/RenderPipeline/ShaderPass/ShaderPassVisibility.hlsl.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: d4c61e9fc44e4bb489de00093724416d +ShaderIncludeImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/com.unity.render-pipelines.high-definition/Runtime/RenderPipelineResources/HDRenderPipelineRuntimeResources.asset b/com.unity.render-pipelines.high-definition/Runtime/RenderPipelineResources/HDRenderPipelineRuntimeResources.asset index 8a3860cd341..870d3884053 100644 --- a/com.unity.render-pipelines.high-definition/Runtime/RenderPipelineResources/HDRenderPipelineRuntimeResources.asset +++ b/com.unity.render-pipelines.high-definition/Runtime/RenderPipelineResources/HDRenderPipelineRuntimeResources.asset @@ -61,6 +61,7 @@ MonoBehaviour: downsampleDepthPS: {fileID: 4800000, guid: 67d6171b0acc6554aad48c845ec7e67f, type: 3} upsampleTransparentPS: {fileID: 4800000, guid: 2ad7ce40f0dbaf64dadef1f58d8524d3, type: 3} resolveStencilCS: {fileID: 7200000, guid: 65b89cac5f286b043a31bf8041776ee7, type: 3} + visibilityPS: {fileID: 4800000, guid: 633338306378163479b295584aba76c3, type: 3} blitCubemapPS: {fileID: 4800000, guid: d05913e251bed7a4992c921c62e1b647, type: 3} buildProbabilityTablesCS: {fileID: 7200000, guid: b9f26cf340afe9145a699753531b2a4c, type: 3} computeGgxIblSampleDataCS: {fileID: 7200000, guid: 764a24bb47ef5ba4781d9ae82ca07445, type: 3}