From 98a7e16756aecba56d032e7c09a1720aa2c129d3 Mon Sep 17 00:00:00 2001 From: Dynamitos Date: Mon, 7 Oct 2024 20:14:00 +0200 Subject: [PATCH] Adding basic CBT Terrain implementation --- res/shaders/TerrainPass.slang | 241 +--- res/shaders/WaterPass.slang | 8 +- res/shaders/lib/Common.slang | 7 +- res/shaders/lib/MaterialParameter.slang | 2 +- res/shaders/terrain/Bisector.slang | 46 + res/shaders/terrain/CBT.slang | 617 +++++++++ res/shaders/terrain/CBTCompute.slang | 1168 +++++++++++++++++ res/shaders/terrain/Parameters.slang | 56 + src/Editor/main.cpp | 82 +- src/Engine/Containers/Array.h | 42 +- src/Engine/Graphics/CBT/CBT.cpp | 797 +++++++++++ src/Engine/Graphics/CBT/CBT.h | 505 +++++++ src/Engine/Graphics/CBT/CMakeLists.txt | 11 + src/Engine/Graphics/CMakeLists.txt | 2 + src/Engine/Graphics/Command.h | 2 + src/Engine/Graphics/Descriptor.h | 1 + src/Engine/Graphics/Graphics.h | 2 + src/Engine/Graphics/Initializer.h | 2 +- src/Engine/Graphics/RenderPass/BasePass.cpp | 11 +- src/Engine/Graphics/RenderPass/BasePass.h | 2 +- .../Graphics/RenderPass/LightCullingPass.h | 7 - src/Engine/Graphics/RenderPass/RenderPass.cpp | 9 +- src/Engine/Graphics/RenderPass/RenderPass.h | 14 +- .../Graphics/RenderPass/TerrainRenderer.cpp | 323 ++++- .../Graphics/RenderPass/TerrainRenderer.h | 19 +- src/Engine/Graphics/StaticMeshVertexData.cpp | 4 - src/Engine/Graphics/VertexData.cpp | 2 +- src/Engine/Graphics/Vulkan/Buffer.cpp | 18 +- src/Engine/Graphics/Vulkan/Command.cpp | 9 + src/Engine/Graphics/Vulkan/Command.h | 2 + src/Engine/Graphics/Vulkan/Descriptor.cpp | 25 + src/Engine/Graphics/Vulkan/Descriptor.h | 1 + src/Engine/Graphics/Vulkan/Graphics.cpp | 21 +- src/Engine/Graphics/Vulkan/Graphics.h | 2 + src/Engine/Graphics/Vulkan/PipelineCache.cpp | 4 +- src/Engine/Graphics/Vulkan/Window.cpp | 2 +- src/Engine/Graphics/slang-compile.cpp | 78 +- test.py | 8 +- 38 files changed, 3779 insertions(+), 373 deletions(-) create mode 100644 res/shaders/terrain/Bisector.slang create mode 100644 res/shaders/terrain/CBT.slang create mode 100644 res/shaders/terrain/CBTCompute.slang create mode 100644 res/shaders/terrain/Parameters.slang create mode 100644 src/Engine/Graphics/CBT/CBT.cpp create mode 100644 src/Engine/Graphics/CBT/CBT.h create mode 100644 src/Engine/Graphics/CBT/CMakeLists.txt diff --git a/res/shaders/TerrainPass.slang b/res/shaders/TerrainPass.slang index 6206d40..17603c2 100644 --- a/res/shaders/TerrainPass.slang +++ b/res/shaders/TerrainPass.slang @@ -1,198 +1,77 @@ import Common; -import Bounding; -import Scene; -import MaterialParameter; +import Parameters; -struct TerrainPayload +// Vertex input +struct VertexInput { - float3 offset; - float extent; - uint numMeshes; + uint instanceID : SV_InstanceID; + uint vertexID : SV_VertexID; }; -struct TerrainTile +// Vertex output +struct VertexOutput { - int2 location; - float extent; - float height; + float4 positionCS : SV_POSITION; + uint triangleID : TRIANGLE_ID; }; -struct TerrainVertex +[shader("vertex")] +VertexOutput vert(VertexInput input) { - float4 position_CS : SV_Position; - float3 position_WS : POSITION0; - float2 texCoord : TEXCOORD0; - float depth : DEPTH; - uint lod : BONEINDEX; -}; + // Initialize the output structure + VertexOutput output; + output = (VertexOutput)0; -struct TerrainData -{ - StructuredBuffer tiles; - Texture2D displacement; - Texture2D colorMap; - SamplerState sampler; -}; -ParameterBlock pTerrainData; + // Evaluate the properties of this triangle + uint triangle_id = input.vertexID / 3; + uint local_vert_id = input.vertexID % 3; -[numthreads(1, 1, 1)] -[shader("amplification")] -void taskMain( - uint threadID: SV_GroupThreadID, - uint3 groupID: SV_GroupID - ) { - TerrainTile tile = pTerrainData.tiles[groupID.x]; - GroupMemoryBarrierWithGroupSync(); - AABB bounding; - bounding.minCorner = float3(tile.location.x, tile.height, tile.location.y) * tile.extent; - bounding.maxCorner = float3(tile.location.x + tile.extent, tile.height, tile.location.y + tile.extent) * tile.extent; - float3 origin = viewToWorld(float4(0, 0, 0, 1)).xyz; - const float offset = 0.0f; - float3 corners[4] = { - screenToWorld(float4(offset, offset, -1.0f, 1.0f)).xyz, - screenToWorld(float4(pViewParams.screenDimensions.x - offset, offset, -1.0f, 1.0f)).xyz, - screenToWorld(float4(offset, pViewParams.screenDimensions.y - offset, -1.0f, 1.0f)).xyz, - screenToWorld(float4(pViewParams.screenDimensions - float2(offset, offset), -1.0f, 1.0f)).xyz - }; - Frustum viewFrustum; - viewFrustum.sides[0] = computePlane(origin, corners[2], corners[0]); - viewFrustum.sides[1] = computePlane(origin, corners[1], corners[3]); - viewFrustum.sides[2] = computePlane(origin, corners[0], corners[1]); - viewFrustum.sides[3] = computePlane(origin, corners[3], corners[2]); - if(!bounding.insideFrustum(viewFrustum)) - { - return; - } - float3 median = (bounding.minCorner + bounding.maxCorner) / 2; - float distance = distance(median, pViewParams.cameraPos_WS.xyz); - float tileDistance = distance / tile.extent; - uint numMeshes = groupID.y + 1; - - if(numMeshes == 4 && tileDistance > 2) - { - return; - } - if(numMeshes == 3 && (tileDistance > 4 || tileDistance < 1)) - { - return; - } - if(numMeshes == 2 && (tileDistance > 8 || tileDistance < 3)) - { - return; - } - if(numMeshes == 1 && tileDistance < 7) - { - return; - } - TerrainPayload payload; - payload.offset = bounding.minCorner; - payload.extent = tile.extent / numMeshes; - payload.numMeshes = numMeshes; - DispatchMesh(numMeshes, numMeshes, 1, payload); + // Operate the indirection + output.triangleID = pParams.indexedBisectorBuffer[triangle_id]; + + // Which vertex should be read? + local_vert_id = local_vert_id == 0 ? 2 : (local_vert_id == 2 ? 0 : 1); + float3 positionRWS = pParams.currentVertexBuffer[output.triangleID * 3 + local_vert_id]; + + // Apply the view projection + output.positionCS = mul(pViewParams.projectionMatrix, mul(pViewParams.viewMatrix, float4(positionRWS, 1.0))); + return output; } -const static uint64_t TILE_VERTS = 144; -const static uint64_t TILE_PRIMS = 242; - -const static float2 VERT[TILE_VERTS] = {float2(0.0f / 11, 0.0f / 11), float2(0.0f / 11, 1.0f / 11), float2(0.0f / 11, 2.0f / 11), float2(0.0f / 11, 3.0f / 11), float2(0.0f / 11, 4.0f / 11), float2(0.0f / 11, 5.0f / 11), float2(0.0f / 11, -6.0f / 11), float2(0.0f / 11, 7.0f / 11), float2(0.0f / 11, 8.0f / 11), float2(0.0f / 11, 9.0f / 11), float2(0.0f / 11, 10.0f / 11), float2(0.0f / 11, 11.0f / 11), float2(1.0f / 11, 0.0f / 11), float2(1.0f / 11, 1.0f / 11), float2(1.0f / 11, 2.0f / 11), float2(1.0f / 11, 3.0f / 11), float2(1.0f / -11, 4.0f / 11), float2(1.0f / 11, 5.0f / 11), float2(1.0f / 11, 6.0f / 11), float2(1.0f / 11, 7.0f / 11), float2(1.0f / 11, 8.0f / 11), float2(1.0f / 11, 9.0f / 11), float2(1.0f / 11, 10.0f / 11), float2(1.0f / 11, 11.0f / 11), float2(2.0f / 11, 0.0f / 11), float2(2.0f / 11, 1.0f / 11), float2(2.0f / 11, 2.0f / 11), float2(2.0f / 11, 3.0f / 11), float2(2.0f / 11, 4.0f / 11), float2(2.0f / 11, 5.0f / 11), float2(2.0f / 11, 6.0f / 11), float2(2.0f / 11, 7.0f / 11), float2(2.0f / 11, 8.0f / 11), float2(2.0f / 11, 9.0f / 11), float2(2.0f / 11, 10.0f / 11), float2(2.0f / 11, 11.0f / 11), float2(3.0f / 11, 0.0f / 11), float2(3.0f / 11, 1.0f / 11), float2(3.0f / 11, 2.0f / 11), float2(3.0f / 11, 3.0f / 11), float2(3.0f / 11, 4.0f / 11), float2(3.0f / 11, 5.0f / 11), float2(3.0f / 11, 6.0f / 11), float2(3.0f / 11, 7.0f / 11), float2(3.0f / 11, 8.0f / 11), float2(3.0f / 11, 9.0f / 11), float2(3.0f / 11, 10.0f / 11), float2(3.0f / 11, 11.0f / 11), float2(4.0f / 11, 0.0f / 11), float2(4.0f -/ 11, 1.0f / 11), float2(4.0f / 11, 2.0f / 11), float2(4.0f / 11, 3.0f / 11), float2(4.0f / 11, 4.0f / 11), float2(4.0f / 11, 5.0f / 11), float2(4.0f / 11, 6.0f / 11), float2(4.0f / 11, 7.0f / 11), float2(4.0f / 11, 8.0f / 11), float2(4.0f / 11, 9.0f / 11), float2(4.0f / 11, 10.0f / 11), float2(4.0f / 11, 11.0f / 11), float2(5.0f / 11, 0.0f / 11), float2(5.0f / 11, 1.0f / 11), float2(5.0f / 11, -2.0f / 11), float2(5.0f / 11, 3.0f / 11), float2(5.0f / 11, 4.0f / 11), float2(5.0f / 11, 5.0f / 11), float2(5.0f / 11, 6.0f / 11), float2(5.0f / 11, 7.0f / 11), float2(5.0f / 11, 8.0f / 11), float2(5.0f / 11, 9.0f / 11), float2(5.0f / 11, 10.0f / 11), float2(5.0f / 11, 11.0f / 11), float2(6.0f / -11, 0.0f / 11), float2(6.0f / 11, 1.0f / 11), float2(6.0f / 11, 2.0f / 11), float2(6.0f / 11, 3.0f / 11), float2(6.0f / 11, 4.0f / 11), float2(6.0f / 11, 5.0f / 11), float2(6.0f / 11, 6.0f / 11), float2(6.0f / 11, 7.0f / 11), float2(6.0f / 11, 8.0f / 11), float2(6.0f / 11, 9.0f / 11), float2(6.0f -/ 11, 10.0f / 11), float2(6.0f / 11, 11.0f / 11), float2(7.0f / 11, 0.0f / 11), float2(7.0f / 11, 1.0f / 11), float2(7.0f / 11, 2.0f / 11), float2(7.0f / 11, 3.0f / 11), float2(7.0f / 11, 4.0f / 11), float2(7.0f / 11, 5.0f / 11), float2(7.0f / 11, 6.0f / 11), float2(7.0f / 11, 7.0f / 11), float2(7.0f / 11, 8.0f / 11), float2(7.0f / 11, 9.0f / 11), float2(7.0f / 11, 10.0f / 11), float2(7.0f / 11, 11.0f / 11), float2(8.0f / 11, 0.0f / 11), float2(8.0f / 11, 1.0f / 11), float2(8.0f / 11, 2.0f / 11), float2(8.0f / 11, 3.0f / 11), float2(8.0f / 11, 4.0f / 11), float2(8.0f / 11, 5.0f / 11), float2(8.0f / 11, 6.0f / 11), float2(8.0f / 11, 7.0f / 11), float2(8.0f / 11, 8.0f / 11), float2(8.0f / -11, 9.0f / 11), float2(8.0f / 11, 10.0f / 11), float2(8.0f / 11, 11.0f / 11), float2(9.0f / 11, 0.0f / 11), float2(9.0f / 11, 1.0f / 11), float2(9.0f / 11, 2.0f / 11), float2(9.0f / 11, 3.0f / 11), float2(9.0f / 11, 4.0f / 11), float2(9.0f / 11, 5.0f / 11), float2(9.0f / 11, 6.0f / 11), float2(9.0f / 11, 7.0f / 11), float2(9.0f / 11, 8.0f / 11), float2(9.0f / 11, 9.0f / 11), float2(9.0f / 11, 10.0f / 11), float2(9.0f / 11, 11.0f / 11), float2(10.0f / 11, 0.0f / 11), float2(10.0f / 11, 1.0f / 11), float2(10.0f / 11, 2.0f / 11), float2(10.0f / 11, 3.0f / 11), float2(10.0f / 11, 4.0f / 11), float2(10.0f / 11, 5.0f / 11), float2(10.0f / 11, 6.0f / 11), float2(10.0f / 11, 7.0f / 11), float2(10.0f / 11, 8.0f / 11), float2(10.0f / 11, 9.0f / 11), float2(10.0f / 11, 10.0f / 11), float2(10.0f / 11, 11.0f / 11), float2(11.0f / 11, 0.0f / 11), float2(11.0f / 11, 1.0f / 11), float2(11.0f / 11, 2.0f / 11), float2(11.0f / 11, 3.0f / 11), float2(11.0f / 11, 4.0f / 11), float2(11.0f / 11, 5.0f / 11), float2(11.0f / 11, 6.0f / 11), float2(11.0f / 11, 7.0f / 11), float2(11.0f / 11, 8.0f / 11), float2(11.0f / 11, 9.0f / 11), float2(11.0f / 11, 10.0f / 11), float2(11.0f / 11, 11.0f / 11)}; - -const static uint3 IND[TILE_PRIMS] = {uint3(0, 1, 12), uint3(12, 1, 13), uint3(1, 2, 13), uint3(13, 2, 14), uint3(2, 3, 14), uint3(14, 3, 15), uint3(3, 4, 15), uint3(15, 4, 16), uint3(4, 5, 16), uint3(16, 5, 17), uint3(5, 6, 17), uint3(17, 6, 18), uint3(6, 7, 18), uint3(18, 7, 19), uint3(7, 8, 19), uint3(19, 8, 20), uint3(8, 9, 20), uint3(20, 9, 21), uint3(9, 10, 21), uint3(21, 10, 22), uint3(10, 11, 22), uint3(22, 11, 23), uint3(12, 13, 24), uint3(24, 13, 25), uint3(13, 14, 25), uint3(25, 14, 26), uint3(14, -15, 26), uint3(26, 15, 27), uint3(15, 16, 27), uint3(27, 16, 28), uint3(16, 17, 28), uint3(28, 17, 29), uint3(17, 18, 29), uint3(29, 18, 30), uint3(18, 19, 30), uint3(30, 19, 31), uint3(19, 20, 31), uint3(31, 20, 32), uint3(20, 21, 32), uint3(32, 21, 33), uint3(21, 22, 33), uint3(33, 22, 34), uint3(22, 23, 34), uint3(34, 23, 35), uint3(24, 25, 36), uint3(36, 25, 37), uint3(25, 26, -37), uint3(37, 26, 38), uint3(26, 27, 38), uint3(38, 27, 39), uint3(27, 28, 39), uint3(39, 28, 40), uint3(28, 29, 40), uint3(40, 29, 41), uint3(29, 30, 41), uint3(41, 30, 42), uint3(30, 31, 42), uint3(42, 31, 43), uint3(31, 32, 43), uint3(43, 32, 44), uint3(32, 33, 44), uint3(44, 33, 45), uint3(33, 34, 45), uint3(45, 34, 46), uint3(34, 35, 46), uint3(46, 35, 47), uint3(36, 37, 48), uint3(48, 37, 49), uint3(37, 38, 49), uint3(49, 38, 50), uint3(38, 39, 50), uint3(50, 39, 51), uint3(39, 40, 51), uint3(51, 40, 52), uint3(40, 41, 52), uint3(52, 41, 53), uint3(41, 42, 53), -uint3(53, 42, 54), uint3(42, 43, 54), uint3(54, 43, 55), uint3(43, 44, 55), uint3(55, 44, 56), uint3(44, 45, 56), uint3(56, 45, 57), uint3(45, 46, 57), uint3(57, 46, 58), uint3(46, 47, 58), uint3(58, 47, 59), uint3(48, 49, 60), uint3(60, 49, 61), uint3(49, 50, 61), uint3(61, 50, 62), uint3(50, 51, 62), uint3(62, 51, 63), uint3(51, 52, 63), uint3(63, 52, 64), uint3(52, 53, 64), uint3(64, 53, 65), uint3(53, 54, 65), uint3(65, 54, 66), uint3(54, 55, 66), uint3(66, 55, 67), uint3(55, 56, 67), uint3(67, 56, 68), uint3(56, 57, 68), uint3(68, 57, 69), uint3(57, 58, 69), uint3(69, 58, 70), uint3(58, 59, 70), uint3(70, 59, 71), uint3(60, 61, 72), uint3(72, 61, 73), uint3(61, 62, 73), uint3(73, 62, 74), uint3(62, 63, 74), uint3(74, 63, 75), uint3(63, 64, 75), uint3(75, 64, 76), uint3(64, 65, 76), uint3(76, 65, 77), uint3(65, 66, 77), uint3(77, 66, 78), uint3(66, 67, 78), uint3(78, 67, 79), uint3(67, 68, 79), uint3(79, 68, 80), uint3(68, 69, 80), uint3(80, 69, 81), uint3(69, 70, 81), uint3(81, 70, 82), uint3(70, 71, 82), uint3(82, 71, 83), uint3(72, -73, 84), uint3(84, 73, 85), uint3(73, 74, 85), uint3(85, 74, 86), uint3(74, 75, 86), uint3(86, 75, 87), uint3(75, 76, 87), uint3(87, 76, 88), uint3(76, 77, 88), uint3(88, 77, 89), uint3(77, 78, 89), uint3(89, 78, 90), uint3(78, 79, 90), uint3(90, 79, 91), uint3(79, 80, 91), uint3(91, 80, 92), uint3(80, 81, 92), uint3(92, 81, 93), uint3(81, 82, 93), uint3(93, 82, 94), uint3(82, 83, -94), uint3(94, 83, 95), uint3(84, 85, 96), uint3(96, 85, 97), uint3(85, 86, 97), uint3(97, 86, 98), uint3(86, 87, 98), uint3(98, 87, 99), uint3(87, 88, 99), uint3(99, 88, 100), uint3(88, 89, 100), uint3(100, 89, 101), uint3(89, 90, 101), uint3(101, 90, 102), uint3(90, 91, 102), uint3(102, 91, 103), uint3(91, 92, 103), uint3(103, 92, 104), uint3(92, 93, 104), uint3(104, 93, 105), uint3(93, 94, 105), uint3(105, 94, 106), uint3(94, 95, 106), uint3(106, 95, 107), uint3(96, 97, 108), uint3(108, 97, 109), uint3(97, 98, 109), uint3(109, 98, 110), uint3(98, 99, 110), uint3(110, 99, 111), uint3(99, 100, 111), uint3(111, 100, 112), uint3(100, 101, 112), uint3(112, 101, 113), uint3(101, 102, 113), uint3(113, 102, 114), uint3(102, 103, 114), uint3(114, 103, 115), uint3(103, 104, 115), uint3(115, 104, 116), uint3(104, 105, 116), uint3(116, 105, 117), uint3(105, 106, 117), uint3(117, 106, 118), uint3(106, 107, 118), uint3(118, 107, 119), uint3(108, 109, 120), uint3(120, 109, 121), uint3(109, 110, 121), uint3(121, 110, 122), uint3(110, 111, 122), uint3(122, 111, -123), uint3(111, 112, 123), uint3(123, 112, 124), uint3(112, 113, 124), uint3(124, 113, 125), uint3(113, 114, 125), uint3(125, 114, 126), uint3(114, 115, 126), uint3(126, 115, 127), uint3(115, 116, 127), uint3(127, 116, 128), uint3(116, 117, 128), uint3(128, 117, 129), uint3(117, 118, 129), uint3(129, 118, 130), uint3(118, 119, 130), uint3(130, 119, 131), uint3(120, 121, 132), uint3(132, 121, 133), uint3(121, 122, 133), uint3(133, 122, 134), uint3(122, 123, 134), uint3(134, 123, 135), uint3(123, 124, 135), uint3(135, 124, 136), uint3(124, 125, 136), uint3(136, 125, 137), uint3(125, 126, 137), uint3(137, 126, 138), uint3(126, 127, 138), uint3(138, 127, 139), uint3(127, 128, 139), uint3(139, 128, 140), uint3(128, 129, 140), uint3(140, 129, 141), uint3(129, 130, 141), uint3(141, 130, 142), uint3(130, 131, 142), uint3(142, 131, 143)}; - - -[numthreads(MESH_GROUP_SIZE, 1, 1)] -[outputtopology("triangle")] -[shader("mesh")] -void meshMain( - in uint threadID: SV_GroupThreadID, - in uint3 groupID: SV_GroupID, - in payload TerrainPayload params, - out vertices TerrainVertex vertices[TILE_VERTS], - out indices uint3 indices[TILE_PRIMS], -) { - float4x4 vp = mul(pViewParams.projectionMatrix, pViewParams.viewMatrix); - SetMeshOutputCounts(TILE_VERTS, TILE_PRIMS); - - for(uint i = threadID; i < TILE_PRIMS; i += MESH_GROUP_SIZE) - { - indices[i] = IND[i]; - } - for(uint i = threadID; i < TILE_VERTS; i += MESH_GROUP_SIZE) - { - float2 base = VERT[i]; - float3 objectPos = float3(base.x, 0, base.y); - float3 worldPos = params.offset + objectPos * params.extent + float3(groupID.x * params.extent, 0, groupID.y * params.extent); - - float lodDisplacement = 0; - float3 camPos = pViewParams.cameraPos_WS.xyz; - float cameraDistance = distance(worldPos, camPos); - float threshold = 0; - if(params.numMeshes == 3) - { - threshold = 10; - } - if(params.numMeshes == 2) - { - threshold = 30; - } - if(params.numMeshes == 1) - { - threshold = 70; - } - - if(params.numMeshes < 4) - { - if(cameraDistance > threshold) - { - lodDisplacement = clamp(exp(-(1 / (cameraDistance + threshold))), 0, 1) - 1; - } - else - { - lodDisplacement = -1; - } - } - - -// float3 displacement1 = pWaterMaterial.displacementTextures.Sample(pWaterMaterial.sampler, float3(worldPos.xz * pWaterMaterial.tile1, 0)).xyz * pWaterMaterial.contributeDisplacement1; -// float3 displacement2 = pWaterMaterial.displacementTextures.Sample(pWaterMaterial.sampler, float3(worldPos.xz * pWaterMaterial.tile2, 1)).xyz * pWaterMaterial.contributeDisplacement2; -// float3 displacement3 = pWaterMaterial.displacementTextures.Sample(pWaterMaterial.sampler, float3(worldPos.xz * pWaterMaterial.tile3, 2)).xyz * pWaterMaterial.contributeDisplacement3; -// float3 displacement4 = pWaterMaterial.displacementTextures.Sample(pWaterMaterial.sampler, float3(worldPos.xz * pWaterMaterial.tile4, 3)).xyz * pWaterMaterial.contributeDisplacement4; - float displacement = pTerrainData.displacement.SampleLevel(pTerrainData.sampler, worldPos.xz / 1000, 0) * 10; - - float4 clipPos = mul(vp, float4(worldPos, 1)); - float ndcDepth = clipPos.z / clipPos.w; - - float depth = 1 - (-ndcDepth + 1.0f) / 2.0f; - -// displacement = lerp(0.0f, displacement, pow(saturate(depth), pTerrainData.displacementDepthAttenuation)); - - worldPos.y += displacement; - worldPos.y += lodDisplacement; - - TerrainVertex v; - v.position_WS = worldPos; - v.position_CS = mul(vp, float4(worldPos, 1)); - v.texCoord = worldPos.xz; - v.depth = depth; - v.lod = params.numMeshes; - vertices[i] = v; - } -} +// Pixel input +struct PixelInput +{ + float4 positionCS : SV_POSITION; + uint triangleID : TRIANGLE_ID; +}; [shader("pixel")] -float4 fragmentMain(TerrainVertex vertex) +float4 frag(PixelInput input) : SV_Target { - return float4(pTerrainData.colorMap.Sample(pTerrainData.sampler, vertex.position_WS.xz / 1000), 1); + return float4(0, 1, 0, 1); +} + +[shader("compute")] +[numthreads(64, 1, 1)] +void deform(uint currentID: SV_DispatchThreadID) +{ + if(currentID > pParams.indirectDrawBuffer[9] * 4) + return; + + uint bisectorID = currentID / 4; + uint localVertexID = currentID % 4; + + bisectorID = pParams.indexedBisectorBuffer[bisectorID]; + + currentID = localVertexID < 3 ? bisectorID * 3 + localVertexID : 3 * pParams.geometry.totalNumElements + bisectorID; + + float3 positionWS = pParams.lebPositionBuffer[currentID]; + + float3 positionPS = positionWS; + + float3 normalPS = float3(0, 1, 0); + + float2 sampleUV = float2(0, 0); + + pParams.currentVertexBuffer[currentID] = positionWS; } \ No newline at end of file diff --git a/res/shaders/WaterPass.slang b/res/shaders/WaterPass.slang index 59ba460..bb5741f 100644 --- a/res/shaders/WaterPass.slang +++ b/res/shaders/WaterPass.slang @@ -87,7 +87,7 @@ void taskMain( bounding.minCorner = float3(tile.location.x, tile.height, tile.location.y) * tile.extent; bounding.maxCorner = float3(tile.location.x + 1, tile.height, tile.location.y + 1) * tile.extent; float3 median = (bounding.minCorner + bounding.maxCorner) / 2; - float distance = distance(median, pViewParams.cameraPos_WS.xyz); + float distance = distance(median, pViewParams.cameraPosition_WS.xyz); float tileDistance = distance / tile.extent; uint numMeshes = groupID.y + 1; @@ -179,7 +179,7 @@ void meshMain( float3 worldPos = params.offset + objectPos * params.extent + float3(groupID.x * params.extent, 0, groupID.y * params.extent); float lodDisplacement = 0; - float3 camPos = pViewParams.ameraPos_WS.xyz; + float3 camPos = pViewParams.cameraPosition_WS.xyz; float cameraDistance = distance(worldPos, camPos); float threshold = 0; if(params.numMeshes == 3) @@ -207,7 +207,7 @@ void meshMain( } } - + // TODO: LOD load float3 displacement1 = pWaterMaterial.displacementTextures.Sample(pWaterMaterial.sampler, float3(worldPos.xz * pWaterMaterial.tile1, 0)).xyz * pWaterMaterial.contributeDisplacement1; float3 displacement2 = pWaterMaterial.displacementTextures.Sample(pWaterMaterial.sampler, float3(worldPos.xz * pWaterMaterial.tile2, 1)).xyz * pWaterMaterial.contributeDisplacement2; float3 displacement3 = pWaterMaterial.displacementTextures.Sample(pWaterMaterial.sampler, float3(worldPos.xz * pWaterMaterial.tile3, 2)).xyz * pWaterMaterial.contributeDisplacement3; @@ -237,7 +237,7 @@ void meshMain( [shader("pixel")] float4 fragmentMain(WaterVertex vert) : SV_TARGET { float3 lightDir = -normalize(pWaterMaterial.sunDirection); - float3 viewDir = normalize(pViewParams.cameraPos_WS.xyz - vert.position_WS); + float3 viewDir = normalize(pViewParams.cameraPosition_WS.xyz - vert.position_WS); float3 halfwayDir = normalize(lightDir + viewDir); float depth = vert.depth; float LdotH = clamp(dot(lightDir, halfwayDir), 0, 1); diff --git a/res/shaders/lib/Common.slang b/res/shaders/lib/Common.slang index 6833859..fc09ac7 100644 --- a/res/shaders/lib/Common.slang +++ b/res/shaders/lib/Common.slang @@ -6,12 +6,17 @@ static const uint32_t MESH_GROUP_SIZE = 32; struct ViewParameter { + Frustum viewFrustum; float4x4 viewMatrix; float4x4 inverseViewMatrix; float4x4 projectionMatrix; float4x4 inverseProjection; - float4 cameraPos_WS; + float4 cameraPosition_WS; + float4 cameraForward_WS; float2 screenDimensions; + float2 invScreenDimensions; + uint frameIndex; + float time; }; layout(set = 0) ParameterBlock pViewParams; diff --git a/res/shaders/lib/MaterialParameter.slang b/res/shaders/lib/MaterialParameter.slang index 8af7971..1bd86f5 100644 --- a/res/shaders/lib/MaterialParameter.slang +++ b/res/shaders/lib/MaterialParameter.slang @@ -51,7 +51,7 @@ struct FragmentParameter float3x3 tbn = float3x3(normalize(tangent_WS), normalize(biTangent_WS), normalize(normal_WS)); result.tbn = tbn; result.position_TS = mul(tbn, position_WS); - result.viewDir_TS = mul(tbn, normalize(pViewParams.cameraPos_WS.xyz - position_WS)); + result.viewDir_TS = mul(tbn, normalize(pViewParams.cameraPosition_WS.xyz - position_WS)); result.normal_TS = mul(tbn, normal_WS); return result; } diff --git a/res/shaders/terrain/Bisector.slang b/res/shaders/terrain/Bisector.slang new file mode 100644 index 0000000..ec9a2f1 --- /dev/null +++ b/res/shaders/terrain/Bisector.slang @@ -0,0 +1,46 @@ +// Pointer to an invalid neighbor or index +const static int INVALID_POINTER = 4294967295; + +// Possible culling state +const static int BACK_FACE_CULLED =-3; +const static int FRUSTUM_CULLED =-2; +const static int TOO_SMALL =-1; +const static int UNCHANGED_ELEMENT= 0; +const static int BISECT_ELEMENT= 1; +const static int SIMPLIFY_ELEMENT= 2; +const static int MERGED_ELEMENT= 3; + +// Bisector flags +const static int VISIBLE_BISECTOR = 0x1; +const static int MODIFIED_BISECTOR = 0x2; + +struct BisectorData +{ + // Subvision that should be applied to this bisector + uint32_t subdivisionPattern; + + // Allocated indices for this bisector + uint32_t indices[3]; + + // Neighbor that should be processed + uint32_t problematicNeighbor; + + // State of this bisector (split, merge, etc) + uint32_t bisectorState; + + // Visibility and modification flags of a bisector + uint32_t flags; + + // ID used for the propagation + uint32_t propagationID; +}; + +uint heapIDDepth(uint64_t x) +{ + uint depth = 0; + while (x > 0u) { + ++depth; + x >>= 1u; + } + return depth; +} \ No newline at end of file diff --git a/res/shaders/terrain/CBT.slang b/res/shaders/terrain/CBT.slang new file mode 100644 index 0000000..95f1a5f --- /dev/null +++ b/res/shaders/terrain/CBT.slang @@ -0,0 +1,617 @@ +import Parameters; + +#ifndef WORKGROUP_SIZE +#define WORKGROUP_SIZE 64 +#endif + +// Num elements +#define OCBT_NUM_ELEMENTS 1048576 +// Tree sizes +#define OCBT_TREE_SIZE_BITS (32 * 1 + 32 * 2 + 32 * 4 + 32 * 8 + 32 * 16 + 32 * 32 + 32 * 64 + 16 * 128 + 16 * 256 + 16 * 512 + 16 * 1024 + 16* 2048 + 16 * 4096 + 8 * 8192) +#define OCBT_TREE_NUM_SLOTS (OCBT_TREE_SIZE_BITS / 32) +#define OCBT_BITFIELD_NUM_SLOTS (OCBT_NUM_ELEMENTS / 64) +#define OCBT_LAST_LEVEL_SIZE 8192 + +// Tree last level +#define TREE_LAST_LEVEL 13 +// First virtual level +#define FIRST_VIRTUAL_LEVEL 14 +// Leaf level +#define LEAF_LEVEL 20 + +// per level offset +static const uint32_t OCBT_depth_offset[21] = { 0, // Level 0 + 32 * 1, // level 1 + 32 * 1 + 32 * 2, // level 2 + 32 * 1 + 32 * 2 + 32 * 4, // level 3 + 32 * 1 + 32 * 2 + 32 * 4 + 32 * 8, // Level 4 + 32 * 1 + 32 * 2 + 32 * 4 + 32 * 8 + 32 * 16, // Level 5 + 32 * 1 + 32 * 2 + 32 * 4 + 32 * 8 + 32 * 16 + 32 * 32, // Level 6 + 32 * 1 + 32 * 2 + 32 * 4 + 32 * 8 + 32 * 16 + 32 * 32 + 32 * 64, // Level 7 + + 32 * 1 + 32 * 2 + 32 * 4 + 32 * 8 + 32 * 16 + 32 * 32 + 32 * 64 + 16 * 128, // Level 8 + 32 * 1 + 32 * 2 + 32 * 4 + 32 * 8 + 32 * 16 + 32 * 32 + 32 * 64 + 16 * 128 + 16 * 256, // Level 9 + 32 * 1 + 32 * 2 + 32 * 4 + 32 * 8 + 32 * 16 + 32 * 32 + 32 * 64 + 16 * 128 + 16 * 256 + 16 * 512, // Level 10 + 32 * 1 + 32 * 2 + 32 * 4 + 32 * 8 + 32 * 16 + 32 * 32 + 32 * 64 + 16 * 128 + 16 * 256 + 16 * 512 + 16 * 1024, // Level 11 + 32 * 1 + 32 * 2 + 32 * 4 + 32 * 8 + 32 * 16 + 32 * 32 + 32 * 64 + 16 * 128 + 16 * 256 + 16 * 512 + 16 * 1024 + 16 * 2048, // Level 12 + 32 * 1 + 32 * 2 + 32 * 4 + 32 * 8 + 32 * 16 + 32 * 32 + 32 * 64 + 16 * 128 + 16 * 256 + 16 * 512 + 16 * 1024 + 16 * 2048 + 16 * 4096, // Level 13 + + 0, // Level 14 + 0, // Level 15 + 0, // Level 16 + 0, // Level 17 + 0, // Level 18 + 0, // Level 19 + 0, // Level 20 +}; + +static const uint64_t OCBT_bit_mask[21] = { 0xffffffff, // Root 17 + 0xffffffff, // Level 16 + 0xffffffff, // level 15 + 0xffffffff, // level 14 + 0xffffffff, // level 13 + 0xffffffff, // level 12 + 0xffffffff, // level 11 + + 0xffff, // level 10 + 0xffff, // level 9 + 0xffff, // level 8 + 0xffff, // level 8 + 0xffff, // level 8 + 0xffff, // level 8 + 0xff, // level 8 + + 0xffffffffffffffff, // level 7 + 0xffffffff, // Level 6 + 0xffff, // level 5 + 0xff, // level 4 + 0xf, // level 3 + 0x3, // level 2 + 0x1, // level 1 +}; + +static const uint32_t OCBT_bit_count[21] = { 32, // Root 17 + 32, // Level 16 + 32, // level 15 + 32, // level 14 + 32, // level 13 + 32, // level 12 + 32, // level 11 + + 16, // level 10 + 16, // level 9 + 16, // level 8 + 16, // level 8 + 16, // level 8 + 16, // level 8 + 8, // level 8 + + 64, // Level 5 + 32, // Level 5 + 16, // Level 4 + 8, // level 3 + 4, // level 2 + 2, // level 1 + 1, // level 0 +}; + +// Define the remaining values +#define BUFFER_ELEMENT_PER_LANE ((OCBT_TREE_NUM_SLOTS + WORKGROUP_SIZE - 1) / WORKGROUP_SIZE) +#define BUFFER_ELEMENT_PER_LANE_NO_BITFIELD ((OCBT_TREE_NUM_SLOTS + WORKGROUP_SIZE - 1) / WORKGROUP_SIZE) +#define BITFIELD_ELEMENT_PER_LANE ((OCBT_BITFIELD_NUM_SLOTS + WORKGROUP_SIZE - 1) / WORKGROUP_SIZE) +#define WAVE_TREE_DEPTH uint(log2(OCBT_NUM_ELEMENTS)) + +uint32_t cbt_size() +{ + return OCBT_NUM_ELEMENTS; +} + +uint32_t last_level_offset() +{ + return OCBT_depth_offset[TREE_LAST_LEVEL] / 32; +} + +groupshared uint gs_cbtTree[OCBT_TREE_NUM_SLOTS]; + +// Function that sets a given bit +void set_bit(uint bitID, bool state) +{ + // Coordinates of the bit + uint32_t slot = bitID / 64; + uint32_t local_id = bitID % 64; + + if (state) + pParams.bitFieldBuffer[slot] |= 1uLL << local_id; + else + pParams.bitFieldBuffer[slot] &= ~(1uLL << local_id); +} + +void set_bit_atomic(uint bitID, bool state) +{ + // Coordinates of the bit + uint32_t slot = bitID / 64; + uint32_t local_id = bitID % 64; + + if (state) + InterlockedOr(pParams.bitFieldBuffer[slot], 1uLL << local_id); + else + InterlockedAnd(pParams.bitFieldBuffer[slot], ~(1uLL << local_id)); +} + +uint get_bit(uint bitID) +{ + uint32_t slot = bitID / 64; + uint32_t local_id = bitID % 64; + return uint((pParams.bitFieldBuffer[slot] & (1uLL << local_id)) >> local_id); +} + +uint get_heap_element(uint id) +{ + // Figure out the location of the first bit of this element + uint32_t real_heap_id = id - 1; + uint32_t depth = uint32_t(log2(real_heap_id + 1)); + uint32_t level_first_element = (1u << depth) - 1; + uint32_t id_in_level = real_heap_id - level_first_element; + uint32_t first_bit = OCBT_depth_offset[depth] + OCBT_bit_count[depth] * id_in_level; + if (depth < FIRST_VIRTUAL_LEVEL) + { + uint32_t slot = first_bit / 32; + uint32_t local_id = first_bit % 32; + uint32_t target_bits = (gs_cbtTree[slot] >> local_id) & uint32_t(OCBT_bit_mask[depth]); + return (gs_cbtTree[slot] >> local_id) & uint32_t(OCBT_bit_mask[depth]); + } + else + { + uint32_t slot = first_bit / 64; + uint32_t local_id = first_bit % 64; + uint64_t target_bits = (pParams.bitFieldBuffer[slot] >> local_id) & OCBT_bit_mask[depth]; + uint32_t high = uint(target_bits >> 32); + uint32_t low = uint(target_bits); + return countbits(high) + countbits(low); + } +} + +// Should not be called if depth > TREE_LAST_LEVEL +void set_heap_element(uint id, uint value) +{ + // Figure out the location of the first bit of this element + uint real_heap_id = id - 1; + uint depth = uint(log2(real_heap_id + 1)); + uint level_first_element = (1u << depth) - 1; + uint first_bit = OCBT_depth_offset[depth] + OCBT_bit_count[depth] * (real_heap_id - level_first_element); + + // Find the slot and the local first bit + uint slot = first_bit / 32; + uint local_id = first_bit % 32; + + // Extract the relevant bits + gs_cbtTree[slot] &= ~(uint32_t(OCBT_bit_mask[depth]) << local_id); + gs_cbtTree[slot] |= ((uint32_t(OCBT_bit_mask[depth]) & value) << local_id); +} + +// Should not be called if depth > TREE_LAST_LEVEL +void set_heap_element_atomic(uint id, uint value) +{ + // Figure out the location of the first bit of this element + uint real_heap_id = id - 1; + uint depth = uint(log2(real_heap_id + 1)); + uint level_first_element = (1u << depth) - 1; + uint first_bit = OCBT_depth_offset[depth] + OCBT_bit_count[depth] * (real_heap_id - level_first_element); + + // Find the slot and the local first bit + uint slot = first_bit / 32; + uint local_id = first_bit % 32; + + // Extract the relevant bits + InterlockedAnd(gs_cbtTree[slot], ~(uint32_t(OCBT_bit_mask[depth]) << local_id)); + InterlockedOr(gs_cbtTree[slot], ((uint32_t(OCBT_bit_mask[depth]) & value) << local_id)); +} + +// Function that returns the number of active bits +uint bit_count() +{ + return gs_cbtTree[0]; +} + +uint bit_count(uint depth, uint element) +{ + return get_heap_element((1u << depth) + element); +} + +// decodes the position of the i-th one in the bitfield +uint decode_bit(uint handle) +{ +#if defined(NAIVE_DECODE) + uint bitID = 1; + for (uint currentDepth = 0; currentDepth < WAVE_TREE_DEPTH; ++currentDepth) + { + uint heapValue = get_heap_element(2 * bitID); + uint b = handle < heapValue ? 0 : 1; + + bitID = 2 * bitID + b; + handle -= heapValue * b; + } + + return (bitID ^ OCBT_NUM_ELEMENTS); +#else + uint currentDepth = 0; + uint heapElementID = 1u; + for (currentDepth = 0; currentDepth < FIRST_VIRTUAL_LEVEL; ++currentDepth) + { + // Read the left element + uint heapValue = get_heap_element(2u * heapElementID); + + // Does it fall in the right or left subtree? + uint b = handle < heapValue ? 0u : 1u; + + // Pick a subtree + heapElementID = 2u * heapElementID + b; + + // Move the iterator to exclude the right subtree if required + handle -= heapValue * b; + } + + // Align with the internal depth + currentDepth++; + + // Ok we have our subtree, now we need to pick the right bit + uint64_t heapValue = pParams.bitFieldBuffer[heapElementID - OCBT_LAST_LEVEL_SIZE * 2]; + uint64_t mask = 0xffffffff; + uint32_t bitCount = 32; + for (; currentDepth < (WAVE_TREE_DEPTH + 1); ++currentDepth) + { + // Figure out the location of the first bit of this element + uint real_heap_id = 2 * heapElementID - 1; + uint level_first_element = (1u << currentDepth) - 1; + uint id_in_level = real_heap_id - level_first_element; + uint first_bit = bitCount * id_in_level; + uint local_id = first_bit % 64; + uint64_t target_bits = (heapValue >> local_id) & mask; + uint32_t high = uint(target_bits >> 32); + uint32_t low = uint(target_bits); + uint heapValue = countbits(high) + countbits(low); + + // Does it fall in the right or left subtree? + uint b = handle < heapValue ? 0u : 1u; + + // Pick a subtree + heapElementID = 2u * heapElementID + b; + + // Move the iterator to exclude the right subtree if required + handle -= heapValue * b; + + // Adjust the mask and bitcount + bitCount /= 2; + mask = mask >> bitCount; + } + return (heapElementID ^ OCBT_NUM_ELEMENTS); +#endif +} + +// decodes the position of the i-th zero in the bitfield +uint decode_bit_complement(uint handle) +{ +#if defined(NAIVE_DECODE) + uint bitID = 1u; + uint c = OCBT_NUM_ELEMENTS / 2u; + + while (bitID < OCBT_NUM_ELEMENTS) { + uint heapValue = c - get_heap_element(2u * bitID); + uint b = handle < heapValue ? 0u : 1u; + + bitID = 2u * bitID + b; + handle -= heapValue * b; + c /= 2u; + } + + return (bitID ^ OCBT_NUM_ELEMENTS); +#else + uint heapElementID = 1u; + uint c = OCBT_NUM_ELEMENTS / 2u; + uint currentDepth = 0; + + for (currentDepth = 0; currentDepth < FIRST_VIRTUAL_LEVEL; ++currentDepth) + { + uint heapValue = c - get_heap_element(2u * heapElementID); + uint b = handle < heapValue ? 0u : 1u; + + heapElementID = 2u * heapElementID + b; + handle -= heapValue * b; + c /= 2u; + } + + // Align with the internal depth + currentDepth++; + + // Ok we have our subtree, now we need to pick the right bit + uint64_t heapValue = pParams.bitFieldBuffer[heapElementID - OCBT_LAST_LEVEL_SIZE * 2]; + uint64_t mask = 0xffffffff; + uint32_t bitCount = 32; + for (; currentDepth < (WAVE_TREE_DEPTH + 1); ++currentDepth) + { + // Figure out the location of the first bit of this element + uint real_heap_id = 2 * heapElementID - 1; + uint level_first_element = (1u << currentDepth) - 1; + uint id_in_level = real_heap_id - level_first_element; + uint first_bit = bitCount * id_in_level; + uint local_id = first_bit % 64; + uint64_t target_bits = (heapValue >> local_id) & mask; + uint32_t high = uint(target_bits >> 32); + uint32_t low = uint(target_bits); + uint heapValue = c - (countbits(high) + countbits(low)); + + uint b = handle < heapValue ? 0u : 1u; + + heapElementID = 2u * heapElementID + b; + handle -= heapValue * b; + c /= 2u; + + // Adjust the mask and bitcount + bitCount /= 2; + mask = mask >> bitCount; + } + + return (heapElementID ^ OCBT_NUM_ELEMENTS); +#endif +} + +void reduce(uint groupIndex) +{ + // First we do a reduction until each lane has exactly one element to process + uint initial_pass_size = OCBT_NUM_ELEMENTS / WORKGROUP_SIZE; + for (uint it = initial_pass_size / 64, offset = OCBT_NUM_ELEMENTS / 64; it > 0 ; it >>=1, offset >>=1) + { + uint minHeapID = offset + (groupIndex * it); + uint maxHeapID = offset + ((groupIndex + 1) * it); + + for (uint heapID = minHeapID; heapID < maxHeapID; ++heapID) + { + set_heap_element(heapID, get_heap_element(heapID * 2) + get_heap_element(heapID * 2 + 1)); + } + } + GroupMemoryBarrierWithGroupSync(); + + for(uint s = WORKGROUP_SIZE / 2; s > 0u; s >>= 1) + { + if (groupIndex < s) + { + uint v = s + groupIndex; + set_heap_element(v, get_heap_element(v * 2) + get_heap_element(v * 2 + 1)); + } + GroupMemoryBarrierWithGroupSync(); + } +} + +void reduce_prepass(uint dispatchThreadID) +{ + // Initialize the packed sum + uint packedSum = 0; + + // Loop through the 4 pairs to process + for (uint pairIdx = 0; pairIdx < 4; ++pairIdx) + { + // First element of the pair + uint64_t target_bits = pParams.bitFieldBuffer[dispatchThreadID * 8 + 2 * pairIdx]; + uint32_t high = uint(target_bits >> 32); + uint32_t low = uint(target_bits); + uint elementC = countbits(high) + countbits(low); + + // Second element of the pair + target_bits = pParams.bitFieldBuffer[dispatchThreadID * 8 + 2 * pairIdx + 1]; + high = uint(target_bits >> 32); + low = uint(target_bits); + elementC += countbits(high) + countbits(low); + + // Store in the right bits + packedSum |= (elementC << pairIdx * 8); + } + + // Offset of the last level of the tree + const uint bufferOffset = last_level_offset(); + + // Store the result into the bitfield + pParams.cbtBuffer[bufferOffset + dispatchThreadID] = packedSum; +} + +void reduce_first_pass(uint dispatchThreadID, uint groupIndex) +{ + // Load the lowest level (and only the last level) + const uint level0Offset = OCBT_depth_offset[TREE_LAST_LEVEL] / 32; + for (uint e = 0; e < 4; ++e) + { + uint target_element = 4 * dispatchThreadID + e; + gs_cbtTree[level0Offset + target_element] = pParams.cbtBuffer[level0Offset + target_element]; + } + GroupMemoryBarrierWithGroupSync(); + + // First we do a reduction until each lane has exactly one element to process + uint initial_pass_size = OCBT_LAST_LEVEL_SIZE / 2; + uint it, offset; + for (it = initial_pass_size / 512, offset = initial_pass_size; it > 1; it >>=1, offset >>=1) + { + uint minHeapID = offset + (dispatchThreadID * it); + uint maxHeapID = offset + ((dispatchThreadID + 1) * it); + + for (uint heapID = minHeapID; heapID < maxHeapID; ++heapID) + { + set_heap_element(heapID, get_heap_element(heapID * 2) + get_heap_element(heapID * 2 + 1)); + } + } + + // Last pass needs to be atomic + uint heapID = offset + (dispatchThreadID * it); + set_heap_element_atomic(heapID, get_heap_element(heapID * 2) + get_heap_element(heapID * 2 + 1)); + + GroupMemoryBarrierWithGroupSync(); + + // Load the first reduced level + const uint level2Offset = OCBT_depth_offset[TREE_LAST_LEVEL - 1] / 32; + for (uint e = 0; e < 4; ++e) + { + uint target_element = 4 * dispatchThreadID + e; + pParams.cbtBuffer[level2Offset + target_element] = gs_cbtTree[level2Offset + target_element]; + } + + // Load the first reduced level + const uint level3Offset = OCBT_depth_offset[TREE_LAST_LEVEL - 2] / 32; + for (uint e = 0; e < 2; ++e) + { + uint target_element = 2 * dispatchThreadID + e; + pParams.cbtBuffer[level3Offset + target_element] = gs_cbtTree[level3Offset + target_element]; + } + + const uint level4Offset = OCBT_depth_offset[TREE_LAST_LEVEL - 3] / 32; + pParams.cbtBuffer[level4Offset + dispatchThreadID] = gs_cbtTree[level4Offset + dispatchThreadID]; + + const uint level5Offset = OCBT_depth_offset[TREE_LAST_LEVEL - 4] / 32; + if (groupIndex % 2 == 0) + pParams.cbtBuffer[level5Offset + dispatchThreadID / 2] = gs_cbtTree[level5Offset + dispatchThreadID / 2]; +} +void reduce_second_pass(uint groupIndex) +{ + // Load the lowest level (and only the last level) + const uint level0Offset = OCBT_depth_offset[9] / 32; + for (uint e = 0; e < 4; ++e) + { + uint target_element = 4 * groupIndex + e; + gs_cbtTree[level0Offset + target_element] = pParams.cbtBuffer[level0Offset + target_element]; + } + GroupMemoryBarrierWithGroupSync(); + + // First we do a reduction until each lane has exactly one element to process + uint initial_pass_size = 256; + for (uint it = initial_pass_size / 64, offset = initial_pass_size; it > 0 ; it >>=1, offset >>=1) + { + uint minHeapID = offset + (groupIndex * it); + uint maxHeapID = offset + ((groupIndex + 1) * it); + + for (uint heapID = minHeapID; heapID < maxHeapID; ++heapID) + { + set_heap_element(heapID, get_heap_element(heapID * 2) + get_heap_element(heapID * 2 + 1)); + } + } + GroupMemoryBarrierWithGroupSync(); + + for(uint s = WORKGROUP_SIZE / 2; s > 0u; s >>= 1) + { + if (groupIndex < s) + { + uint v = s + groupIndex; + set_heap_element(v, get_heap_element(v * 2) + get_heap_element(v * 2 + 1)); + } + GroupMemoryBarrierWithGroupSync(); + } + + // Make sure all the previous operations are done + GroupMemoryBarrierWithGroupSync(); + + // Load the bitfield to the LDS + for (uint e = 0; e < 5; ++e) + { + uint target_element = 5 * groupIndex + e; + if (target_element < 319) + pParams.cbtBuffer[target_element] = gs_cbtTree[target_element]; + } +} + +void reduce_no_bitfield(uint groupIndex) +{ + // First we do a reduction until each lane has exactly one element to process + uint initial_pass_size = OCBT_NUM_ELEMENTS / WORKGROUP_SIZE; + for (uint it = initial_pass_size / 128, offset = OCBT_NUM_ELEMENTS / 128; it > 0 ; it >>=1, offset >>=1) + { + uint minHeapID = offset + (groupIndex * it); + uint maxHeapID = offset + ((groupIndex + 1) * it); + + for (uint heapID = minHeapID; heapID < maxHeapID; ++heapID) + { + set_heap_element(heapID, get_heap_element(heapID * 2) + get_heap_element(heapID * 2 + 1)); + } + } + GroupMemoryBarrierWithGroupSync(); + + for(uint s = WORKGROUP_SIZE / 2; s > 0u; s >>= 1) + { + if (groupIndex < s) + { + uint v = s + groupIndex; + set_heap_element(v, get_heap_element(v * 2) + get_heap_element(v * 2 + 1)); + } + GroupMemoryBarrierWithGroupSync(); + } +} + +void clear_cbt(uint groupIndex) +{ + for (uint v = 0; v < BUFFER_ELEMENT_PER_LANE; ++v) + { + uint target_element = BUFFER_ELEMENT_PER_LANE * groupIndex + v; + if (target_element < OCBT_TREE_NUM_SLOTS) + gs_cbtTree[target_element] = 0; + } + + for (uint b = 0; b < BITFIELD_ELEMENT_PER_LANE; ++b) + { + uint target_element = BITFIELD_ELEMENT_PER_LANE * groupIndex + b; + if (target_element < OCBT_BITFIELD_NUM_SLOTS) + pParams.bitFieldBuffer[target_element] = 0; + } + GroupMemoryBarrierWithGroupSync(); +} + +// Importante note +// Depending on your target GPU architecture, the pattern used to load has a different performance behavior +// here is the best performant based on our tests: +// NVIDIA uint target_element = groupIndex + WORKGROUP_SIZE * e; +// AMD uint target_element = BUFFER_ELEMENT_PER_LANE * groupIndex + e; + +void load_buffer_to_shared_memory(uint groupIndex) +{ + // Load the bitfield to the LDS + for (uint e = 0; e < BUFFER_ELEMENT_PER_LANE; ++e) + { +#ifdef AMD + uint target_element = BUFFER_ELEMENT_PER_LANE * groupIndex + e; +#else + uint target_element = groupIndex + WORKGROUP_SIZE * e; +#endif + if (target_element < OCBT_TREE_NUM_SLOTS) + gs_cbtTree[target_element] = pParams.cbtBuffer[target_element]; + } + GroupMemoryBarrierWithGroupSync(); +} + +void load_shared_memory_to_buffer(uint groupIndex) +{ + // Make sure all the previous operations are done + GroupMemoryBarrierWithGroupSync(); + + // Load the bitfield to the LDS + for (uint e = 0; e < BUFFER_ELEMENT_PER_LANE; ++e) + { +#ifdef AMD + uint target_element = BUFFER_ELEMENT_PER_LANE * groupIndex + e; +#else + uint target_element = groupIndex + WORKGROUP_SIZE * e; +#endif + if (target_element < OCBT_TREE_NUM_SLOTS) + pParams.cbtBuffer[target_element] = gs_cbtTree[target_element]; + } +} + +void set_bit_atomic_buffer(uint bitID, bool state) +{ + // Coordinates of the bit + uint32_t slot = bitID / 64; + uint32_t local_id = bitID % 64; + + if (state) + InterlockedOr(pParams.bitFieldBuffer[slot], 1uLL << local_id); + else + InterlockedAnd(pParams.bitFieldBuffer[slot], ~(1uLL << local_id)); +} + +uint32_t bit_count_buffer() +{ + return pParams.cbtBuffer[0]; +} \ No newline at end of file diff --git a/res/shaders/terrain/CBTCompute.slang b/res/shaders/terrain/CBTCompute.slang new file mode 100644 index 0000000..2a9ff93 --- /dev/null +++ b/res/shaders/terrain/CBTCompute.slang @@ -0,0 +1,1168 @@ +import Common; +import Parameters; +import Bisector; +import Bounding; +import CBT; + +#ifndef WORKGROUP_SIZE +#define WORKGROUP_SIZE 64 +#endif + + +struct BisectorGeometry +{ + float3 p[4]; +} + +// global +// update +int classifyBisector(in BisectorGeometry tri, uint depth) +{ + float3 triNormal = normalize(cross(tri.p[2] - tri.p[1], tri.p[0] - tri.p[1])); + float3 triCenter = (tri.p[0] + tri.p[1] + tri.p[2]) / 3.0; + float3 viewDir = normalize(-triCenter); + float fDotV = dot(viewDir, pViewParams.cameraForward_WS.xyz); + float vDotN = dot(viewDir, triNormal); + + if(fDotV < 0.0 && vDotN < -1e-3) + return BACK_FACE_CULLED; + + + AABB aabb; + aabb.minCorner = float3(min(min(tri.p[0].x, tri.p[1].x), tri.p[2].x), min(min(tri.p[0].y, tri.p[1].y), tri.p[2].y), min(min(tri.p[0].z, tri.p[1].z), tri.p[2].z)); + aabb.maxCorner = float3(max(max(tri.p[0].x, tri.p[1].x), tri.p[2].x), max(max(tri.p[0].y, tri.p[1].y), tri.p[2].y), max(max(tri.p[0].z, tri.p[1].z), tri.p[2].z)); + + if(aabb.insideFrustum(pViewParams.viewFrustum)) + return FRUSTUM_CULLED; + + float4x4 viewProjectionMatrix = mul(pViewParams.projectionMatrix, pViewParams.viewMatrix); + + float4 p0P = mul(pViewParams.viewMatrix, float4(tri.p[0], 1.0)); + p0P.xy = p0P.xy / p0P.w; + p0P.xy = (p0P.xy * 0.5 + 0.5); + + float4 p1P = mul(viewProjectionMatrix, float4(tri.p[1], 1.0)); + p1P.xy = p1P.xy / p1P.w; + p1P.xy = (p1P.xy * 0.5 + 0.5); + + float4 p2P = mul(viewProjectionMatrix, float4(tri.p[2], 1.0)); + p2P.xy = p2P.xy / p2P.w; + p2P.xy = (p2P.xy * 0.5 + 0.5); + + float area = 0.5 * abs(p0P.x * (p2P.y - p1P.y) + p1P.x * (p0P.y - p2P.y) + p2P.x * (p1P.y - p0P.y)); + area *= pViewParams.screenDimensions.x * pViewParams.screenDimensions.y; + + float areaOverestimation = lerp(2.0, 1.0, pow(vDotN, 0.2)); + area *= areaOverestimation; + + if(pParams.update.triangleSize < area && depth < pParams.update.maxSubdivisionDepth) + { + return BISECT_ELEMENT; + } + else if((pParams.update.triangleSize * 0.5 > area) || (depth > pParams.update.maxSubdivisionDepth)) + { + float4 p3P = mul(viewProjectionMatrix, float4(tri.p[3], 1.0)); + p3P.xy = p3P.xy / p3P.w; + p3P.xy = (p3P.xy * 0.5 + 0.5); + + float areaParent = 0.5 * abs(p0P.x * (p2P.y - p3P.y) + p3P.x * (p0P.y - p2P.y) + p2P.x * (p3P.y - p0P.y)); + areaParent *= pViewParams.screenDimensions.x * pViewParams.screenDimensions.y; + areaParent *= areaOverestimation; + return ((pParams.update.triangleSize >= areaParent ) || (depth > pParams.update.maxSubdivisionDepth)) ? TOO_SMALL : UNCHANGED_ELEMENT; + } + return UNCHANGED_ELEMENT; +} + + +// Possible splits +const static uint64_t NO_SPLIT = 0x00; +const static uint64_t CENTER_SPLIT = 0x01; +const static uint64_t RIGHT_SPLIT = 0x02; +const static uint64_t LEFT_SPLIT = 0x04; +const static uint64_t RIGHT_DOUBLE_SPLIT = (CENTER_SPLIT | RIGHT_SPLIT); +const static uint64_t LEFT_DOUBLE_SPLIT = (CENTER_SPLIT | LEFT_SPLIT); +const static uint64_t TRIPLE_SPLIT = (CENTER_SPLIT | RIGHT_SPLIT | LEFT_SPLIT); + + +const static uint32_t SPLIT_COUNTER = 0; +const static uint32_t SIMPLIFY_COUNTER = 1; +const static uint32_t CLASSIFY_COUNTER_OFFSET = 2; + +// memory RW +// CBT +// classify RW +// allocate RW +// propagate RW +// simplify RW +// indirectDraw RW +[numthreads(1, 1, 1)] +void reset() +{ + pParams.memoryBuffer[0] = 0; + pParams.memoryBuffer[1] = cbt_size() - bit_count_buffer(); + + pParams.classificationBuffer[SPLIT_COUNTER] = 0; + pParams.classificationBuffer[SIMPLIFY_COUNTER] = 0; + + pParams.allocateBuffer[0] = 0; + + pParams.propagateBuffer[0] = 0; + pParams.propagateBuffer[1] = 0; + + pParams.simplifyBuffer[0] = 0; + + pParams.indirectDrawBuffer[0] = 0; + pParams.indirectDrawBuffer[1] = 1; + pParams.indirectDrawBuffer[2] = 0; + pParams.indirectDrawBuffer[3] = 0; + + pParams.indirectDrawBuffer[4] = 0; + pParams.indirectDrawBuffer[5] = 1; + pParams.indirectDrawBuffer[6] = 0; + pParams.indirectDrawBuffer[7] = 0; + + pParams.indirectDrawBuffer[8] = 0; +} + +// global +// geometry +// update +// indirectDraw R +// indexedBisector R +// currentVertex R +// headID R +// bisectorData RW +// classification RW +[numthreads(WORKGROUP_SIZE, 1, 1)] +void classify(uint dispatchID : SV_DispatchThreadID) +{ + if(dispatchID > pParams.indirectDrawBuffer[9]) + return; + + uint currentID = pParams.indexedBisectorBuffer[dispatchID]; + BisectorGeometry bis; + bis.p[0] = pParams.currentVertexBuffer[3 * currentID]; + bis.p[1] = pParams.currentVertexBuffer[3 * currentID + 1]; + bis.p[2] = pParams.currentVertexBuffer[3 * currentID + 2]; + bis.p[3] = pParams.currentVertexBuffer[3 * pParams.geometry.totalNumElements + currentID]; + + uint64_t heapID = pParams.heapIDBuffer[currentID]; + uint depth = heapIDDepth(heapID); + BisectorData cBisectorData = pParams.bisectorDataBuffer[currentID]; + + cBisectorData.subdivisionPattern = 0; + cBisectorData.bisectorState = UNCHANGED_ELEMENT; + cBisectorData.problematicNeighbor = INVALID_POINTER; + cBisectorData.flags = VISIBLE_BISECTOR; + + int currentValidity = classifyBisector(bis, depth); + if(currentValidity > UNCHANGED_ELEMENT) + { + uint targetSlot; + cBisectorData.bisectorState = BISECT_ELEMENT; + InterlockedAdd(pParams.classificationBuffer[SPLIT_COUNTER], 1, targetSlot); + pParams.classificationBuffer[CLASSIFY_COUNTER_OFFSET + targetSlot] = currentID; + } + else + { + cBisectorData.flags = currentValidity >= TOO_SMALL ? VISIBLE_BISECTOR : 0; + } + if(pParams.geometry.baseDepth != depth && currentValidity < UNCHANGED_ELEMENT) + { + cBisectorData.bisectorState = SIMPLIFY_ELEMENT; + + if(heapID % 2 == 0) + { + uint targetSlot; + InterlockedAdd(pParams.classificationBuffer[SIMPLIFY_COUNTER], 1, targetSlot); + pParams.classificationBuffer[CLASSIFY_COUNTER_OFFSET + pParams.geometry.totalNumElements + targetSlot] = currentID; + } + } + pParams.bisectorDataBuffer[currentID] = cBisectorData; +} + +// classification R +// neighbours R +// bisectorData Rw +// heapID R +// memory RW +// allocate RW +[numthreads(WORKGROUP_SIZE, 1, 1)] +void split(uint dispatchID : SV_DispatchThreadID) +{ + if(dispatchID >= pParams.classificationBuffer[SPLIT_COUNTER]) + return; + + uint currentID = pParams.classificationBuffer[CLASSIFY_COUNTER_OFFSET + dispatchID]; + + uint3 cNeighbours = pParams.neighboursBuffer[currentID]; + + if(cNeighbours.x != INVALID_POINTER) + { + uint3 xNeighbours = pParams.neighboursBuffer[cNeighbours.x]; + if(xNeighbours.z == currentID && pParams.bisectorDataBuffer[cNeighbours.x].bisectorState != UNCHANGED_ELEMENT) + return; + } + + if(cNeighbours.y != INVALID_POINTER) + { + uint3 yNeighbours = pParams.neighboursBuffer[cNeighbours.y]; + if(yNeighbours.z == currentID && pParams.bisectorDataBuffer[cNeighbours.y].bisectorState != UNCHANGED_ELEMENT) + return; + } + + uint64_t heapID = pParams.heapIDBuffer[currentID]; + uint currentDepth = heapIDDepth(heapID); + + int maxRequiredMemory = 2 * (currentDepth - pParams.geometry.baseDepth) - 1; + + uint twinID = cNeighbours.z; + + if(twinID == INVALID_POINTER) + maxRequiredMemory = 1; + else if (pParams.neighboursBuffer[twinID].z == currentID) + maxRequiredMemory = 2; + + int remainingMemory; + InterlockedAdd(pParams.memoryBuffer[1], -maxRequiredMemory, remainingMemory); + + if(remainingMemory < maxRequiredMemory) + { + InterlockedAdd(pParams.memoryBuffer[1], maxRequiredMemory, remainingMemory); + return; + } + + uint usedMemory = 1; + uint prevPattern; + InterlockedOr(pParams.bisectorDataBuffer[currentID].subdivisionPattern, CENTER_SPLIT, prevPattern); + + if(prevPattern != 0) + { + InterlockedAdd(pParams.memoryBuffer[1], maxRequiredMemory, remainingMemory); + return; + } + + int targetLocation; + InterlockedAdd(pParams.allocateBuffer[0], 1, targetLocation); + pParams.allocateBuffer[1 + targetLocation] = currentID; + + bool done = false; + while(!done) + { + if(twinID == INVALID_POINTER) + break; + + uint64_t nHeapID = pParams.heapIDBuffer[twinID]; + BisectorData nBisectorData = pParams.bisectorDataBuffer[twinID]; + uint nDepth = heapIDDepth(nHeapID); + uint3 nNeighbours = pParams.neighboursBuffer[twinID]; + + if(nDepth == currentDepth) + { + InterlockedOr(pParams.bisectorDataBuffer[twinID].subdivisionPattern, CENTER_SPLIT, prevPattern); + + if(prevPattern == 0) + { + int targetLocation; + InterlockedAdd(pParams.allocateBuffer[0], 1, targetLocation); + pParams.allocateBuffer[1 + targetLocation] = twinID; + usedMemory++; + } + + done = true; + } + else + { + if(nNeighbours[0] == currentID) + InterlockedOr(pParams.bisectorDataBuffer[twinID].subdivisionPattern, RIGHT_DOUBLE_SPLIT, prevPattern); + else + InterlockedOr(pParams.bisectorDataBuffer[twinID].subdivisionPattern, LEFT_DOUBLE_SPLIT, prevPattern); + + if(prevPattern != 0) + { + usedMemory++; + done = true; + } + else + { + int targetLocation; + InterlockedAdd(pParams.allocateBuffer[0], 1, targetLocation); + pParams.allocateBuffer[1 + targetLocation] = twinID; + + usedMemory += 2; + + currentID = twinID; + currentDepth = nDepth; + twinID = pParams.neighboursBuffer[currentID].z; + } + } + } + + InterlockedAdd(pParams.memoryBuffer[1], max(maxRequiredMemory - usedMemory, 0), remainingMemory); +} + +// indirectDispatch RW +// allocate R +[numthreads(1, 1, 1)] +void prepareIndirect(uint currentID: SV_DispatchThreadID) +{ + pParams.indirectDispatchBuffer[currentID * 3 + 0] = (pParams.allocateBuffer[currentID] + WORKGROUP_SIZE - 1) / WORKGROUP_SIZE; + pParams.indirectDispatchBuffer[currentID * 3 + 1] = 1; + pParams.indirectDispatchBuffer[currentID * 3 + 2] = 1; +} + +// cbtBuffer R +// allocateBuffer R +// memory RW +// bisectorData RW +[numthreads(WORKGROUP_SIZE, 1, 1)] +void allocate(uint groupIndex: SV_GroupIndex, uint dispatchID: SV_DispatchThreadID) +{ + load_buffer_to_shared_memory(groupIndex); + + if(dispatchID >= pParams.allocateBuffer[0]) + return; + + uint currentID = pParams.allocateBuffer[1 + dispatchID]; + + BisectorData bisectorData = pParams.bisectorDataBuffer[currentID]; + + if(bisectorData.subdivisionPattern != 0) + { + uint numSlots = countbits(bisectorData.subdivisionPattern); + + int firstBitIndex = 0; + InterlockedAdd(pParams.memoryBuffer[0], numSlots, firstBitIndex); + + for(uint bitId = 0; bitId < numSlots; ++bitId) + { + uint index = decode_bit_complement(firstBitIndex + bitId); + bisectorData.indices[bitId] = index; + } + + pParams.bisectorDataBuffer[currentID] = bisectorData; + } +} + +// bisectorData R +// neightbours R +void evaluateNeighbours(uint currentID, uint bisectorID, out uint resX, out uint resY) +{ + BisectorData nBisectorData = pParams.bisectorDataBuffer[bisectorID]; + uint3 nNeighbours = pParams.neighboursBuffer[bisectorID]; + if(nBisectorData.subdivisionPattern == CENTER_SPLIT) + { + resX = nBisectorData.indices[0]; + resY = bisectorID; + } + else if(nBisectorData.subdivisionPattern == RIGHT_DOUBLE_SPLIT) + { + if(nNeighbours[0] == currentID) + { + resX = nBisectorData.indices[1]; + resY = bisectorID; + } + else + { + resX = nBisectorData.indices[0]; + resY = nBisectorData.indices[1]; + } + } + else if(nBisectorData.subdivisionPattern == LEFT_DOUBLE_SPLIT) + { + if(nNeighbours[1] == currentID) + { + resX = nBisectorData.indices[1]; + resY = nBisectorData.indices[0]; + } + else + { + resX = nBisectorData.indices[0]; + resY = bisectorID; + } + } + else + { + if(nNeighbours[0] == currentID) + { + resX = nBisectorData.indices[1]; + resY = bisectorID; + } + else if (nNeighbours[1] == currentID) + { + resX = nBisectorData.indices[2]; + resY = nBisectorData.indices[0]; + } + else + { + resX = nBisectorData.indices[0]; + resY = nBisectorData.indices[1]; + } + } +} + +// allocate R +// heapID RW +// bisectorData RW +// neighbours R +// neighboursOutput RW +[numthreads(WORKGROUP_SIZE, 1, 1)] +void bisect(uint groupIndex : SV_GroupIndex, uint dispatchID : SV_DispatchThreadID) +{ + if(dispatchID >= pParams.allocateBuffer[0]) + return; + + uint currentID = pParams.allocateBuffer[1 + dispatchID]; + + uint64_t baseHeapID = pParams.heapIDBuffer[currentID]; + BisectorData cBisectorData = pParams.bisectorDataBuffer[currentID]; + if(baseHeapID == 0 || cBisectorData.subdivisionPattern == NO_SPLIT) + return; + + uint currentSubdiv = cBisectorData.subdivisionPattern; + + uint3 cNeighbours = pParams.neighboursBuffer[currentID]; + uint p_n0 = cNeighbours[0]; + uint p_n1 = cNeighbours[1]; + uint p_n2 = cNeighbours[2]; + + uint siblingID0 = cBisectorData.indices[0]; + uint siblingID1 = cBisectorData.indices[1]; + uint siblingID2 = cBisectorData.indices[2]; + + if(currentSubdiv == CENTER_SPLIT) + { + uint resX = INVALID_POINTER; + uint resY = INVALID_POINTER; + if(p_n2 != INVALID_POINTER) + evaluateNeighbours(currentID, p_n2, resX, resY); + + pParams.heapIDBuffer[currentID] = 2 * baseHeapID; + pParams.heapIDBuffer[siblingID0] = 2 * baseHeapID + 1; + + uint3 modifiedNeighbours; + modifiedNeighbours[0] = siblingID0; + modifiedNeighbours[1] = resX; + modifiedNeighbours[2] = p_n0; + pParams.neighboursOutputBuffer[currentID] = modifiedNeighbours; + modifiedNeighbours[0] = resY; + modifiedNeighbours[1] = currentID; + modifiedNeighbours[2] = p_n1; + pParams.neighboursOutputBuffer[siblingID0] = modifiedNeighbours; + + BisectorData modifiedBisector = cBisectorData; + modifiedBisector.propagationID = currentID; + + modifiedBisector.problematicNeighbor = INVALID_POINTER; + modifiedBisector.flags = (VISIBLE_BISECTOR | MODIFIED_BISECTOR); + pParams.bisectorDataBuffer[currentID] = modifiedBisector; + + modifiedBisector.problematicNeighbor = p_n1; + modifiedBisector.flags = (VISIBLE_BISECTOR | MODIFIED_BISECTOR); + pParams.bisectorDataBuffer[siblingID0] = modifiedBisector; + + uint targetLocation = 0; + InterlockedAdd(pParams.propagateBuffer[0], 1, targetLocation); + pParams.propagateBuffer[2 + targetLocation] = siblingID0; + } + else if (currentSubdiv == RIGHT_DOUBLE_SPLIT) + { + uint res0X = INVALID_POINTER; + uint res0Y = INVALID_POINTER; + evaluateNeighbours(currentID, p_n0, res0X, res0Y); + + uint res1X = INVALID_POINTER; + uint res1Y = INVALID_POINTER; + if(p_n2 != INVALID_POINTER) + evaluateNeighbours(currentID, p_n2, res1X, res1Y); + + pParams.heapIDBuffer[currentID] = 4 * baseHeapID; + pParams.heapIDBuffer[siblingID0] = 2 * baseHeapID + 1; + pParams.heapIDBuffer[siblingID1] = 4 * baseHeapID + 1; + + uint3 modifiedNeighbors; + modifiedNeighbors[0] = siblingID1; + modifiedNeighbors[1] = res0X; + modifiedNeighbors[2] = siblingID0; + pParams.neighboursOutputBuffer[currentID] = modifiedNeighbors; + modifiedNeighbors[0] = res1Y; + modifiedNeighbors[1] = currentID; + modifiedNeighbors[2] = p_n1; + pParams.neighboursOutputBuffer[siblingID0] = modifiedNeighbors; + modifiedNeighbors[0] = res0Y; + modifiedNeighbors[1] = currentID; + modifiedNeighbors[2] = res1X; + pParams.neighboursOutputBuffer[siblingID1] = modifiedNeighbors; + + BisectorData modifiedBisector = cBisectorData; + modifiedBisector.propagationID = currentID; + + modifiedBisector.problematicNeighbor = INVALID_POINTER; + modifiedBisector.flags = (VISIBLE_BISECTOR | MODIFIED_BISECTOR); + pParams.bisectorDataBuffer[currentID] = modifiedBisector; + + modifiedBisector.problematicNeighbor = p_n1; + modifiedBisector.flags = (VISIBLE_BISECTOR | MODIFIED_BISECTOR); + pParams.bisectorDataBuffer[siblingID0] = modifiedBisector; + + + modifiedBisector.problematicNeighbor = INVALID_POINTER; + modifiedBisector.flags = (VISIBLE_BISECTOR | MODIFIED_BISECTOR); + pParams.bisectorDataBuffer[siblingID1] = modifiedBisector; + + uint targetLocation = 0; + InterlockedAdd(pParams.propagateBuffer[0], 1, targetLocation); + pParams.propagateBuffer[2 + targetLocation] = siblingID0; + } + else if(currentSubdiv == LEFT_DOUBLE_SPLIT) + { + uint res0X = INVALID_POINTER; + uint res0Y = INVALID_POINTER; + evaluateNeighbours(currentID, p_n1, res0X, res0Y); + + uint res1X = INVALID_POINTER; + uint res1Y = INVALID_POINTER; + if(p_n2 != INVALID_POINTER) + evaluateNeighbours(currentID, p_n2, res1X, res1Y); + + pParams.heapIDBuffer[currentID] = 2 * baseHeapID; + pParams.heapIDBuffer[siblingID0] = 4 * baseHeapID + 2; + pParams.heapIDBuffer[siblingID1] = 4 * baseHeapID + 3; + + + uint3 modifiedNeighbors; + modifiedNeighbors[0] = siblingID1; + modifiedNeighbors[1] = res1X; + modifiedNeighbors[2] = p_n0; + pParams.neighboursOutputBuffer[currentID] = modifiedNeighbors; + modifiedNeighbors[0] = siblingID1; + modifiedNeighbors[1] = res0X; + modifiedNeighbors[2] = res1Y; + pParams.neighboursOutputBuffer[siblingID0] = modifiedNeighbors; + modifiedNeighbors[0] = res0Y; + modifiedNeighbors[1] = siblingID0; + modifiedNeighbors[2] = currentID; + pParams.neighboursOutputBuffer[siblingID1] = modifiedNeighbors; + + BisectorData modifiedBisector = cBisectorData; + modifiedBisector.propagationID = currentID; + + modifiedBisector.problematicNeighbor = INVALID_POINTER; + modifiedBisector.flags = (VISIBLE_BISECTOR | MODIFIED_BISECTOR); + pParams.bisectorDataBuffer[currentID] = modifiedBisector; + + modifiedBisector.problematicNeighbor = INVALID_POINTER; + modifiedBisector.flags = (VISIBLE_BISECTOR | MODIFIED_BISECTOR); + pParams.bisectorDataBuffer[siblingID0] = modifiedBisector; + + modifiedBisector.problematicNeighbor = INVALID_POINTER; + modifiedBisector.flags = (VISIBLE_BISECTOR | MODIFIED_BISECTOR); + pParams.bisectorDataBuffer[siblingID1] = modifiedBisector; + } + else if(currentSubdiv == TRIPLE_SPLIT) + { + + uint res0X = INVALID_POINTER; + uint res0Y = INVALID_POINTER; + evaluateNeighbours(currentID, p_n0, res0X, res0Y); + + uint res1X = INVALID_POINTER; + uint res1Y = INVALID_POINTER; + evaluateNeighbours(currentID, p_n1, res1X, res1Y); + + uint res2X = INVALID_POINTER; + uint res2Y = INVALID_POINTER; + if(p_n2 != INVALID_POINTER) + evaluateNeighbours(currentID, p_n2, res2X, res2Y); + + pParams.heapIDBuffer[currentID] = 4 * baseHeapID; + pParams.heapIDBuffer[siblingID0] = 4 * baseHeapID + 2; + pParams.heapIDBuffer[siblingID1] = 4 * baseHeapID + 1; + pParams.heapIDBuffer[siblingID2] = 4 * baseHeapID + 3; + + uint3 modifiedNeighbors; + modifiedNeighbors[0] = siblingID1; + modifiedNeighbors[1] = res0X; + modifiedNeighbors[2] = siblingID2; + pParams.neighboursOutputBuffer[currentID] = modifiedNeighbors; + modifiedNeighbors[0] = siblingID2; + modifiedNeighbors[1] = res1X; + modifiedNeighbors[2] = res2Y; + pParams.neighboursOutputBuffer[siblingID0] = modifiedNeighbors; + modifiedNeighbors[0] = res0Y; + modifiedNeighbors[1] = currentID; + modifiedNeighbors[2] = res2X; + pParams.neighboursOutputBuffer[siblingID1] = modifiedNeighbors; + modifiedNeighbors[0] = res1Y; + modifiedNeighbors[1] = siblingID0; + modifiedNeighbors[2] = currentID; + pParams.neighboursOutputBuffer[siblingID2] = modifiedNeighbors; + + BisectorData modifiedBisector = cBisectorData; + modifiedBisector.propagationID = currentID; + + modifiedBisector.problematicNeighbor = INVALID_POINTER; + modifiedBisector.flags = (VISIBLE_BISECTOR | MODIFIED_BISECTOR); + pParams.bisectorDataBuffer[currentID] = modifiedBisector; + + modifiedBisector.problematicNeighbor = INVALID_POINTER; + modifiedBisector.flags = (VISIBLE_BISECTOR | MODIFIED_BISECTOR); + pParams.bisectorDataBuffer[siblingID0] = modifiedBisector; + + modifiedBisector.problematicNeighbor = INVALID_POINTER; + modifiedBisector.flags = (VISIBLE_BISECTOR | MODIFIED_BISECTOR); + pParams.bisectorDataBuffer[siblingID1] = modifiedBisector; + + modifiedBisector.problematicNeighbor = INVALID_POINTER; + modifiedBisector.flags = (VISIBLE_BISECTOR | MODIFIED_BISECTOR); + pParams.bisectorDataBuffer[siblingID2] = modifiedBisector; + } + + uint numSiblings = countbits(currentSubdiv); + for(uint siblingIdx = 0; siblingIdx < numSiblings; ++siblingIdx) + { + set_bit_atomic_buffer(cBisectorData.indices[siblingIdx], true); + } +} + +// bisectorData RW +// neighbours RW +[numthreads(WORKGROUP_SIZE, 1, 1)] +void propagateBisect(uint dispatchID : SV_DispatchThreadID) +{ + if(dispatchID >= pParams.propagateBuffer[0]) + return; + + uint currentID = pParams.propagateBuffer[2 + dispatchID]; + BisectorData cBisectorData = pParams.bisectorDataBuffer[currentID]; + + uint parentID = cBisectorData.propagationID; + uint problematicNeighbor = cBisectorData.problematicNeighbor; + + BisectorData tBisectorData = pParams.bisectorDataBuffer[problematicNeighbor]; + uint3 tNeighbours = pParams.neighboursBuffer[problematicNeighbor]; + uint targetID = problematicNeighbor; + uint sibling1 = tBisectorData.indices[1]; + + if(tBisectorData.subdivisionPattern == NO_SPLIT) + { + if(tNeighbours[0] == parentID) + pParams.neighboursBuffer[targetID][0] = currentID; + if(tNeighbours[1] == parentID) + pParams.neighboursBuffer[targetID][1] = currentID; + if(tNeighbours[2] == parentID) + pParams.neighboursBuffer[targetID][2] = currentID; + } + else if(tBisectorData.subdivisionPattern == CENTER_SPLIT) + { + if (pParams.neighboursBuffer[targetID][2] == parentID) + pParams.neighboursBuffer[targetID][2] = currentID; + if (pParams.neighboursBuffer[tBisectorData.propagationID][2] == parentID) + pParams.neighboursBuffer[tBisectorData.propagationID][2] = currentID; + } + else if(tBisectorData.subdivisionPattern == RIGHT_DOUBLE_SPLIT) + { + pParams.neighboursBuffer[sibling1][2] = currentID; + } + else if(tBisectorData.subdivisionPattern == LEFT_DOUBLE_SPLIT) + { + pParams.neighboursBuffer[targetID][2] = currentID; + } + pParams.bisectorDataBuffer[currentID].problematicNeighbor = INVALID_POINTER; + pParams.bisectorDataBuffer[currentID].bisectorState = UNCHANGED_ELEMENT; +} + +// geometry +// classification R +// heapID R +// neighbours R +// bisectorData R +// simplify RW +[numthreads(WORKGROUP_SIZE, 1, 1)] +void prepareSimplify(uint dispatchID : SV_DispatchThreadID) +{ + if(dispatchID >= pParams.classificationBuffer[SIMPLIFY_COUNTER]) + return; + + uint currentID = pParams.classificationBuffer[CLASSIFY_COUNTER_OFFSET + pParams.geometry.totalNumElements + dispatchID]; + + BisectorData cBisectorData = pParams.bisectorDataBuffer[currentID]; + + uint64_t cHeapID = pParams.heapIDBuffer[currentID]; + + uint3 cNeighbours = pParams.neighboursBuffer[currentID]; + + uint currentDepth = heapIDDepth(cHeapID); + + uint pairID = cNeighbours[0]; + uint64_t pHeapID = pParams.heapIDBuffer[pairID]; + BisectorData pBisectorData = pParams.bisectorDataBuffer[pairID]; + uint3 pNeighbours = pParams.neighboursBuffer[pairID]; + + uint pairDepth = heapIDDepth(pHeapID); + + if(pairDepth != currentDepth || pBisectorData.bisectorState != SIMPLIFY_ELEMENT) + return; + + uint twinLowID = pNeighbours[0]; + uint twinHighID = cNeighbours[1]; + if(twinLowID != INVALID_POINTER) + { + uint64_t twinLowHeapID = pParams.heapIDBuffer[twinLowID]; + uint64_t twinHighHeapID = pParams.heapIDBuffer[twinHighID]; + + if(cHeapID > twinLowHeapID) + return; + + uint lowFacingDepth = heapIDDepth(twinLowHeapID); + uint highFacingDepth = heapIDDepth(twinHighHeapID); + + if(lowFacingDepth != currentDepth || highFacingDepth != currentDepth) + return; + + BisectorData twinLowBisectorData = pParams.bisectorDataBuffer[twinLowID]; + BisectorData twinHighBisectorData = pParams.bisectorDataBuffer[twinHighID]; + + if(twinLowBisectorData.bisectorState != SIMPLIFY_ELEMENT || twinHighBisectorData.bisectorState != SIMPLIFY_ELEMENT) + return; + } + + uint bisectorSlot = 0; + InterlockedAdd(pParams.simplifyBuffer[0], 1, bisectorSlot); + + pParams.simplifyBuffer[1 + bisectorSlot] = currentID; +} + +// simplify R +// bisectorData RW +// neighbours RW +// heapID RW +[numthreads(WORKGROUP_SIZE, 1, 1)] +void simplify(uint dispatchID : SV_DispatchThreadID) +{ + if(dispatchID >= pParams.simplifyBuffer[0]) + return; + + uint currentID = pParams.simplifyBuffer[1 + dispatchID]; + + BisectorData cBisectorData = pParams.bisectorDataBuffer[currentID]; + uint3 cNeighbours = pParams.neighboursBuffer[currentID]; + + uint pairID = cNeighbours[0]; + BisectorData pBisectorData = pParams.bisectorDataBuffer[pairID]; + uint3 pNeighbours = pParams.neighboursBuffer[pairID]; + + uint twinLowID = pNeighbours[0]; + uint twinHighID = cNeighbours[1]; + + pParams.heapIDBuffer[currentID] = pParams.heapIDBuffer[currentID] / 2; + pParams.heapIDBuffer[pairID] = 0; + + uint3 newNeighbours; + newNeighbours[0] = cNeighbours[2]; + newNeighbours[1] = pNeighbours[2]; + newNeighbours[2] = twinLowID; + pParams.neighboursBuffer[currentID] = newNeighbours; + + cBisectorData.propagationID = pairID; + cBisectorData.problematicNeighbor = pNeighbours[2]; + cBisectorData.bisectorState = MERGED_ELEMENT; + cBisectorData.flags = (VISIBLE_BISECTOR | MODIFIED_BISECTOR); + pParams.bisectorDataBuffer[currentID] = cBisectorData; + + if(cBisectorData.problematicNeighbor != INVALID_POINTER) + { + uint targetLocation = 0; + InterlockedAdd(pParams.propagateBuffer[1], 1, targetLocation); + pParams.propagateBuffer[2 + targetLocation] = currentID; + } + + pBisectorData.bisectorState = MERGED_ELEMENT; + pBisectorData.flags = 0; + pParams.bisectorDataBuffer[pairID] = pBisectorData; + + set_bit_atomic_buffer(pairID, false); + + if(twinLowID != INVALID_POINTER) + { + pParams.heapIDBuffer[twinLowID] = pParams.heapIDBuffer[twinLowID] / 2; + pParams.heapIDBuffer[twinHighID] = 0; + + BisectorData lowFacingBst = pParams.bisectorDataBuffer[twinLowID]; + uint3 lfNeighbours = pParams.neighboursBuffer[twinLowID]; + BisectorData highFacingBst = pParams.bisectorDataBuffer[twinHighID]; + uint3 hfNeighbours = pParams.neighboursBuffer[twinHighID]; + + newNeighbours[0] = lfNeighbours[2]; + newNeighbours[1] = hfNeighbours[2]; + newNeighbours[2] = currentID; + pParams.neighboursBuffer[twinLowID] = newNeighbours; + + lowFacingBst.propagationID = twinHighID; + lowFacingBst.problematicNeighbor = hfNeighbours[2]; + lowFacingBst.bisectorState = MERGED_ELEMENT; + lowFacingBst.flags = (VISIBLE_BISECTOR | MODIFIED_BISECTOR); + pParams.bisectorDataBuffer[twinLowID] = lowFacingBst; + + if(lowFacingBst.problematicNeighbor != INVALID_POINTER) + { + uint targetLocation = 0; + InterlockedAdd(pParams.propagateBuffer[1], 1, targetLocation); + pParams.propagateBuffer[2 + targetLocation] = twinLowID; + } + + highFacingBst.bisectorState = MERGED_ELEMENT; + highFacingBst.flags = 0; + pParams.bisectorDataBuffer[twinHighID] = highFacingBst; + + set_bit_atomic_buffer(twinHighID, false); + } +} + +// neighbours RW +// propagate R +// bisectorData RW +[numthreads(WORKGROUP_SIZE, 1, 1)] +void propagateSimplify(uint dispatchID : SV_DispatchThreadID) +{ + if(dispatchID >= pParams.propagateBuffer[1]) + return; + + uint currentID = pParams.propagateBuffer[2 + dispatchID]; + + BisectorData cBisectorData = pParams.bisectorDataBuffer[currentID]; + + uint deletedPair = cBisectorData.problematicNeighbor; + + uint neighbourID = cBisectorData.problematicNeighbor; + + BisectorData nBisectorData = pParams.bisectorDataBuffer[neighbourID]; + uint3 nNeighbours = pParams.neighboursBuffer[neighbourID]; + + if(nBisectorData.bisectorState != MERGED_ELEMENT) + { + for(uint i = 0; i < 3; ++i) + { + if(nNeighbours[i] == deletedPair) + pParams.neighboursBuffer[neighbourID][i] = currentID; + } + } + else if (nBisectorData.bisectorState == MERGED_ELEMENT) + { + if(pParams.heapIDBuffer[neighbourID] != 0) + { + for(uint i = 0; i < 3; ++i) + { + if(nNeighbours[i] == deletedPair) + pParams.neighboursBuffer[neighbourID][i] = currentID; + } + } + else + { + uint neighbourPair = nNeighbours[1]; + for(uint i = 0; i < 3; ++i) + { + if (pParams.neighboursBuffer[neighbourPair][i] == deletedPair) + pParams.neighboursBuffer[neighbourPair][i] = currentID; + } + } + } + + pParams.bisectorDataBuffer[currentID].problematicNeighbor = INVALID_POINTER; +} + +[numthreads(WORKGROUP_SIZE, 1, 1)] +void reducePrePass(uint dispatchThreadID: SV_DispatchThreadID) +{ + reduce_prepass(dispatchThreadID); +} + +[numthreads(WORKGROUP_SIZE, 1, 1)] +void reduceFirstPass(uint dispatchID: SV_DispatchThreadID, uint groupIndex : SV_GroupIndex) +{ + reduce_first_pass(dispatchID, groupIndex); +} + +[numthreads(WORKGROUP_SIZE, 1, 1)] +void reduceSecondPass(uint groupIndex: SV_GroupIndex) +{ + reduce_second_pass(groupIndex); +} + +// geometry +// heapID R +// indirectDraw RW +// bisectorIndices RW +// bisectorData R +[numthreads(WORKGROUP_SIZE, 1, 1)] +void bisectorIndexation(uint currentID: SV_DispatchThreadID) +{ + if(currentID >= pParams.geometry.totalNumElements) + return; + + uint64_t cHeapID = pParams.heapIDBuffer[currentID]; + + if(cHeapID == 0) + return; + + uint bisectorSlot; + InterlockedAdd(pParams.indirectDrawBuffer[0], 3, bisectorSlot); + pParams.bisectorIndicesBuffer[bisectorSlot / 3] = currentID; + + BisectorData cBisectorData = pParams.bisectorDataBuffer[currentID]; + + if((cBisectorData.flags & VISIBLE_BISECTOR) == 0) + return; + + InterlockedAdd(pParams.indirectDrawBuffer[4], 3, bisectorSlot); + + pParams.visibleBisectorIndices[bisectorSlot / 3] = currentID; + + if((cBisectorData.flags & MODIFIED_BISECTOR) == 0) + return; + + InterlockedAdd(pParams.indirectDrawBuffer[8], 4, bisectorSlot); + + pParams.modifiedBisectorIndices[bisectorSlot / 4] = currentID; +} + +[numthreads(1, 1, 1)] +void prepareBisectorIndirect(uint currentID: SV_DispatchThreadID) +{ + pParams.indirectDispatchBuffer[0] = (pParams.indirectDrawBuffer[0] / 3 + WORKGROUP_SIZE - 1) / WORKGROUP_SIZE; + pParams.indirectDispatchBuffer[1] = 1; + pParams.indirectDispatchBuffer[2] = 1; + + pParams.indirectDispatchBuffer[3] = (pParams.indirectDrawBuffer[0] * 4 / 3 + WORKGROUP_SIZE - 1) / WORKGROUP_SIZE; + pParams.indirectDispatchBuffer[4] = 1; + pParams.indirectDispatchBuffer[5] = 1; + + pParams.indirectDispatchBuffer[6] = (pParams.indirectDrawBuffer[8] + WORKGROUP_SIZE - 1) / WORKGROUP_SIZE; + pParams.indirectDispatchBuffer[7] = 1; + pParams.indirectDispatchBuffer[8] = 1; + + pParams.indirectDrawBuffer[9] = pParams.indirectDrawBuffer[0] / 3; +} + +[numthreads(WORKGROUP_SIZE, 1, 1)] +void validate(uint currentID: SV_DispatchThreadID) +{ + if(currentID >= pParams.geometry.totalNumElements) + return; + + uint64_t cHeapID = pParams.heapIDBuffer[currentID]; + + if(cHeapID == 0) + return; + + uint3 cNeighbours = pParams.neighboursBuffer[currentID]; + + bool failed = false; + uint targetNeighbour = INVALID_POINTER; + uint targetIdx = INVALID_POINTER; + for(uint i = 0; i < 3; ++i) + { + uint neighbourID = cNeighbours[i]; + if(neighbourID != INVALID_POINTER) + { + bool found = false; + uint3 nNeighbours = pParams.neighboursBuffer[neighbourID]; + for(uint j = 0; j < 3; ++j) + { + if(nNeighbours[j] == currentID) + found = true; + } + if(!found) + { + failed = true; + targetNeighbour = neighbourID; + targetIdx = i; + break; + } + } + } + if(failed) + { + uint preValue; + InterlockedAdd(pParams.validationBuffer[0], 1, preValue); + } +} + +[numthreads(WORKGROUP_SIZE, 1, 1)] +void clearLeb(uint currentID: SV_DispatchThreadID) +{ + if(currentID >= pParams.geometry.totalNumElements) + return; + pParams.lebPositionBuffer[currentID] = float3(0, 0, 0); +} + +const static uint64_t LEB_TABLE_DEPTH = 5; + +groupshared float3x3 gs_MatrixCache[2ULL< 0u) + { + ++depth; + heapID >>= 1u; + } + return depth - 1; +} + +void leb__IdentityMatrix3x3(out float3x3 m) +{ + m[0][0] = 1.0f; m[0][1] = 0.0f; m[0][2] = 0.0f; + m[1][0] = 0.0f; m[1][1] = 1.0f; m[1][2] = 0.0f; + m[2][0] = 0.0f; m[2][1] = 0.0f; m[2][2] = 1.0f; +} + +float3x3 leb__SplittingMatrix_out(float3x3 mat, uint64_t bitValue) +{ + float b = (float)bitValue; + float c = 1.0 - b; + float3x3 splitMatrix = { + {0.0, b, c}, + {0.5, 0.0, 0.5}, + {b, c, 0.0} + }; + return mul(splitMatrix, mat); +} + +uint64_t leb__GetBitValue(uint64_t bitField, int64_t bitID) +{ + return ((bitField >> bitID) & 1L); +} + +void leb__DecodeTransformationMatrix_parent_child_Tabulated(uint64_t heapID, out float3x3 parent, out float3x3 child) +{ + int depth = leb_depth(heapID); + leb__IdentityMatrix3x3(parent); + const uint64_t msb = (1ULL << LEB_TABLE_DEPTH); + const uint64_t mask = ~(~0ULL << LEB_TABLE_DEPTH); + uint64_t parentHeapID = heapID / 2; + while (parentHeapID > mask) + { + uint32_t index = uint32_t((parentHeapID & mask) | msb); + parent = mul(parent, gs_MatrixCache[index]); + parentHeapID >>= LEB_TABLE_DEPTH; + } + if (parentHeapID != 0) + parent = mul(parent, gs_MatrixCache[uint32_t(parentHeapID)]); + + // Evaluate the child + if (depth > 0) + child = leb__SplittingMatrix_out(parent, leb__GetBitValue(heapID, 0)); + else + child = parent; +} + +void leb_DecodeNodeAttributeArray_parent_child(uint64_t heapID, inout float3 childAttribute[3], out float3 parentAttribute[3]) +{ + float3x3 child, parent; + leb__DecodeTransformationMatrix_parent_child_Tabulated(heapID, parent, child); + int i; + for (i = 0; i < 3; ++i) + { + float3 attributeVector = childAttribute[i]; + parentAttribute[i][0] = dot(parent[0], attributeVector); + parentAttribute[i][1] = dot(parent[1], attributeVector); + parentAttribute[i][2] = dot(parent[2], attributeVector); + } + + for (i = 0; i < 3; ++i) + { + float3 attributeVector = childAttribute[i]; + childAttribute[i][0] = dot(child[0], attributeVector); + childAttribute[i][1] = dot(child[1], attributeVector); + childAttribute[i][2] = dot(child[2], attributeVector); + } +} + +void evaluateElementPosition(uint64_t heapID, uint32_t vertexDataOffset, uint minDepth, out Triangle parentTri, out Triangle childTri) +{ + // Get the depth of the element + uint depth = heapIDDepth(heapID); + + // Compute the required shift to find the original vertices + uint64_t subTreeDepth = depth - minDepth; + + // Compute the base heapID + uint64_t baseHeapID = 1u << (minDepth - 1); + uint primitiveID = uint((heapID >> subTreeDepth) - baseHeapID); + + // Grab the base positions of the element + float3 p0 = float3(pParams.currentVertexBuffer[3 * primitiveID + vertexDataOffset]); + float3 p1 = float3(pParams.currentVertexBuffer[3 * primitiveID + 1 + vertexDataOffset]); + float3 p2 = float3(pParams.currentVertexBuffer[3 * primitiveID + 2 + vertexDataOffset]); + + // Heap ID in the sub triangle + uint64_t mask = subTreeDepth != 0uL ? 0xFFFFFFFFFFFFFFFFull >> (64ull - subTreeDepth) : 0ull; + uint64_t baseHeap = (1ull << subTreeDepth); + uint64_t baseMask = (mask & heapID); + uint64_t subHeapID = baseMask + baseHeap; + + // Generate the triangle positions + float3 childArray[3] = {{p0.x, p1.x, p2.x}, {p0.y, p1.y, p2.y}, {p0.z, p1.z, p2.z}}; + float3 parentArray[3]; + + // Decode + leb_DecodeNodeAttributeArray_parent_child(subHeapID, childArray, parentArray); + + // Fill the parent triangle + parentTri.p[0] = float3(parentArray[0][0], parentArray[1][0], parentArray[2][0]); + parentTri.p[1] = float3(parentArray[0][1], parentArray[1][1], parentArray[2][1]); + parentTri.p[2] = float3(parentArray[0][2], parentArray[1][2], parentArray[2][2]); + + // Fill the child triangle + Triangle child; + childTri.p[0] = float3(childArray[0][0], childArray[1][0], childArray[2][0]); + childTri.p[1] = float3(childArray[0][1], childArray[1][1], childArray[2][1]); + childTri.p[2] = float3(childArray[0][2], childArray[1][2], childArray[2][2]); +} + +[numthreads(WORKGROUP_SIZE, 1, 1)] +void evaluateLeb(uint currentID : SV_DispatchThreadID, uint groupIndex: SV_GroupIndex) +{ + if(groupIndex < (2ULL << LEB_TABLE_DEPTH)) + gs_MatrixCache[groupIndex] = pParams.lebMatrixCache[groupIndex]; + + GroupMemoryBarrierWithGroupSync(); + + uint numBisectors; + if(pViewParams.frameIndex == -1) + { + numBisectors = pParams.indirectDrawBuffer[9]; + } + else + { + numBisectors = pParams.indirectDrawBuffer[8] / 4; + } + if(currentID >= numBisectors) + return; + + currentID = pParams.indexedBisectorBuffer[currentID]; + + uint64_t cHeapID = pParams.heapIDBuffer[currentID]; + uint depth = heapIDDepth(cHeapID); + + Triangle parentTri, childTri; + evaluateElementPosition(cHeapID, 0, pParams.geometry.baseDepth, parentTri, childTri); + + pParams.lebPositionBuffer[3 * currentID + 0] = childTri.p[0]; + pParams.lebPositionBuffer[3 * currentID + 1] = childTri.p[1]; + pParams.lebPositionBuffer[3 * currentID + 2] = childTri.p[2]; + + if(pParams.geometry.baseDepth < depth) + { + const uint parentOffset = 3 * pParams.geometry.totalNumElements; + + pParams.lebPositionBuffer[parentOffset + currentID] = cHeapID % 2 == 0 ? parentTri.p[0] : parentTri.p[2]; + } +} \ No newline at end of file diff --git a/res/shaders/terrain/Parameters.slang b/res/shaders/terrain/Parameters.slang new file mode 100644 index 0000000..23c6d4b --- /dev/null +++ b/res/shaders/terrain/Parameters.slang @@ -0,0 +1,56 @@ +import Common; +import Bisector; + +struct GeometryCB +{ + uint32_t totalNumElements; + uint32_t baseDepth; + uint32_t totalNumVertices; +}; + +struct DeformationCB +{ + float4 patchSize; + float4 patchRoughness; + float choppiness; + int attenuation; + float amplification; + uint32_t patchFlags; +}; + +struct UpdateCB +{ + float triangleSize; + uint32_t maxSubdivisionDepth; + float fov; + float farPlaneDistance; +} + +struct ComputeParams +{ + ConstantBuffer geometry; + ConstantBuffer update; + + RWStructuredBuffer currentVertexBuffer; + StructuredBuffer indexedBisectorBuffer; + RWStructuredBuffer indirectDrawBuffer; + RWStructuredBuffer heapIDBuffer; + RWStructuredBuffer bisectorDataBuffer; + RWStructuredBuffer classificationBuffer; + RWStructuredBuffer allocateBuffer; + RWStructuredBuffer indirectDispatchBuffer; + RWStructuredBuffer neighboursBuffer; + RWStructuredBuffer neighboursOutputBuffer; + RWStructuredBuffer memoryBuffer; + RWStructuredBuffer cbtBuffer; + RWStructuredBuffer bitFieldBuffer; + RWStructuredBuffer propagateBuffer; + RWStructuredBuffer simplifyBuffer; + RWStructuredBuffer validationBuffer; + RWStructuredBuffer bisectorIndicesBuffer; + RWStructuredBuffer visibleBisectorIndices; + RWStructuredBuffer modifiedBisectorIndices; + RWStructuredBuffer lebPositionBuffer; + StructuredBuffer lebMatrixCache; +}; +ParameterBlock pParams; diff --git a/src/Editor/main.cpp b/src/Editor/main.cpp index 632c048..0cbf0c1 100644 --- a/src/Editor/main.cpp +++ b/src/Editor/main.cpp @@ -22,6 +22,37 @@ using namespace Seele::Editor; // make it global so it gets deleted last and automatically static Gfx::OGraphics graphics; +struct Halfedge { + uint32 vertexID; + uint32 nextID; + uint32 prevID; + uint32 twinID; +}; +Array generateEdges() { + Array indices = {UVector(0, 1, 4), UVector(4, 1, 5), UVector(1, 2, 5), UVector(5, 2, 6), UVector(2, 3, 6), + UVector(6, 3, 7), UVector(4, 5, 8), UVector(8, 5, 9), UVector(5, 6, 9), UVector(9, 6, 10), + UVector(6, 7, 10), UVector(10, 7, 11), UVector(8, 9, 12), UVector(12, 9, 13), UVector(9, 10, 13), + UVector(13, 10, 14), UVector(10, 11, 14), UVector(14, 11, 15)}; + Array edges; + for (auto ind : indices) { + uint32 baseIndex = edges.size(); + edges.add(Halfedge{ind.x, baseIndex+1, baseIndex+2, UINT32_MAX}); + edges.add(Halfedge{ind.y, baseIndex+2, baseIndex, UINT32_MAX}); + edges.add(Halfedge{ind.z, baseIndex, baseIndex+1, UINT32_MAX}); + } + for (uint32 i = 0; i < edges.size(); ++i) { + if (edges[i].twinID == UINT32_MAX) { + for (uint32 j = 0; j < edges.size(); ++j) { + if (edges[i].vertexID == edges[edges[j].nextID].vertexID && edges[edges[i].nextID].vertexID == edges[j].vertexID) { + edges[i].twinID = j; + edges[j].twinID = i; + } + } + } + } + return edges; +} + int main() { std::string gameName = "MeshShadingDemo"; #ifdef WIN32 @@ -65,32 +96,35 @@ int main() { .filePath = sourcePath / "import/textures/skyboxsun5deg_tn.jpg", .type = TextureImportType::TEXTURE_CUBEMAP, }); - //AssetImporter::importMesh(MeshImportArgs{ - // .filePath = sourcePath / "import/models/ship.fbx", - // .importPath = "ship", - //}); - AssetImporter::importTexture(TextureImportArgs{ - .filePath = sourcePath / "import/textures/azeroth.png", - }); - AssetImporter::importTexture(TextureImportArgs{ - .filePath = sourcePath / "import/textures/azeroth_height.png", - }); - //AssetImporter::importMesh(MeshImportArgs{ - // .filePath = sourcePath / "import/models/after-the-rain-vr-sound/source/Whitechapel.glb", - // .importPath = "Whitechapel", - //}); - // AssetImporter::importMesh(MeshImportArgs{521 - // .filePath = sourcePath / "import/models/city-suburbs/source/city-suburbs.obj", - // .importPath = "suburbs", - // }); // AssetImporter::importMesh(MeshImportArgs{ - // .filePath = sourcePath / "import/models/minecraft-medieval-city.fbx", - // .importPath = "minecraft", + // .filePath = sourcePath / "import/models/ship.fbx", + // .importPath = "ship", + // }); + //AssetImporter::importTexture(TextureImportArgs{ + // .filePath = sourcePath / "import/textures/azeroth.png", + //}); + //AssetImporter::importTexture(TextureImportArgs{ + // .filePath = sourcePath / "import/textures/azeroth_height.png", + //}); + //AssetImporter::importTexture(TextureImportArgs{ + // .filePath = sourcePath / "import/textures/wgen.png", + //}); + // AssetImporter::importMesh(MeshImportArgs{ + // .filePath = sourcePath / "import/models/after-the-rain-vr-sound/source/Whitechapel.glb", + // .importPath = "Whitechapel", + // }); + // AssetImporter::importMesh(MeshImportArgs{521 + // .filePath = sourcePath / "import/models/city-suburbs/source/city-suburbs.obj", + // .importPath = "suburbs", + // }); + // AssetImporter::importMesh(MeshImportArgs{ + // .filePath = sourcePath / "import/models/minecraft-medieval-city.fbx", + // .importPath = "minecraft", + // }); + // AssetImporter::importMesh(MeshImportArgs{ + // .filePath = sourcePath / "import/models/Volvo/Volvo.fbx", + // .importPath = "Volvo", // }); - AssetImporter::importMesh(MeshImportArgs{ - .filePath = sourcePath / "import/models/Volvo/Volvo.fbx", - .importPath = "Volvo", - }); getThreadPool().waitIdle(); vd->commitMeshes(); WindowCreateInfo mainWindowInfo = { diff --git a/src/Engine/Containers/Array.h b/src/Engine/Containers/Array.h index d8cceb9..8699c58 100644 --- a/src/Engine/Containers/Array.h +++ b/src/Engine/Containers/Array.h @@ -466,25 +466,25 @@ template struct StaticArray { using reference = X&; using pointer = X*; - IteratorBase(X* x = nullptr) : p(x) {} - reference operator*() const { return *p; } - pointer operator->() const { return p; } - inline bool operator!=(const IteratorBase& other) { return p != other.p; } - inline bool operator==(const IteratorBase& other) { return p == other.p; } - IteratorBase& operator++() { + constexpr IteratorBase(X* x = nullptr) : p(x) {} + constexpr reference operator*() const { return *p; } + constexpr pointer operator->() const { return p; } + constexpr bool operator!=(const IteratorBase& other) { return p != other.p; } + constexpr bool operator==(const IteratorBase& other) { return p == other.p; } + constexpr IteratorBase& operator++() { p++; return *this; } - IteratorBase operator++(int) { + constexpr IteratorBase operator++(int) { IteratorBase tmp(*this); ++*this; return tmp; } - IteratorBase& operator--() { + constexpr IteratorBase& operator--() { p--; return *this; } - IteratorBase operator--(int) { + constexpr IteratorBase operator--(int) { IteratorBase tmp(*this); --*this; return tmp; @@ -507,18 +507,18 @@ template struct StaticArray { using reverse_iterator = std::reverse_iterator; using const_reverse_iterator = std::reverse_iterator; - StaticArray() { + constexpr StaticArray() { beginIt = iterator(_data); endIt = iterator(_data + N); } - StaticArray(T value) { + constexpr StaticArray(T value) { for (size_t i = 0; i < N; ++i) { _data[i] = value; } beginIt = iterator(_data); endIt = iterator(_data + N); } - StaticArray(std::initializer_list init) { + constexpr StaticArray(std::initializer_list init) { auto beg = init.begin(); for (size_t i = 0; i < N; ++i) { _data[i] = *beg; @@ -527,11 +527,11 @@ template struct StaticArray { } } } - ~StaticArray() {} + constexpr ~StaticArray() {} - inline size_type size() const { return N; } - inline pointer data() { return _data; } - inline const_pointer data() const { return _data; } + constexpr size_type size() const { return N; } + constexpr pointer data() { return _data; } + constexpr const_pointer data() const { return _data; } template constexpr reference operator[](I index) noexcept { return operator[](static_cast(index)); } template constexpr const_reference operator[](I index) const noexcept { return operator[](static_cast(index)); } constexpr reference operator[](size_type index) noexcept { @@ -542,13 +542,13 @@ template struct StaticArray { assert(index < N); return _data[index]; } - iterator begin() { return beginIt; } - iterator end() { return endIt; } - const_iterator begin() const { return beginIt; } - const_iterator end() const { return beginIt; } + constexpr iterator begin() { return beginIt; } + constexpr iterator end() { return endIt; } + constexpr const_iterator begin() const { return beginIt; } + constexpr const_iterator end() const { return beginIt; } private: - T _data[N]; + T _data[N] = {T()}; iterator beginIt; iterator endIt; }; diff --git a/src/Engine/Graphics/CBT/CBT.cpp b/src/Engine/Graphics/CBT/CBT.cpp new file mode 100644 index 0000000..fbea0d1 --- /dev/null +++ b/src/Engine/Graphics/CBT/CBT.cpp @@ -0,0 +1,797 @@ +#include "CBT.h" +#include "Graphics/Shader.h" + +using namespace Seele; + +Array basePoints = { + Vector4(000.0f / 3, 0, 000.0f / 3, 1), Vector4(000.0f / 3, 0, 100.0f / 3, 1), Vector4(000.0f / 3, 0, 200.0f / 3, 1), + Vector4(000.0f / 3, 0, 300.0f / 3, 1), Vector4(100.0f / 3, 0, 000.0f / 3, 1), Vector4(100.0f / 3, 0, 100.0f / 3, 1), + Vector4(100.0f / 3, 0, 200.0f / 3, 1), Vector4(100.0f / 3, 0, 300.0f / 3, 1), Vector4(200.0f / 3, 0, 000.0f / 3, 1), + Vector4(200.0f / 3, 0, 100.0f / 3, 1), Vector4(200.0f / 3, 0, 200.0f / 3, 1), Vector4(200.0f / 3, 0, 300.0f / 3, 1), + Vector4(300.0f / 3, 0, 000.0f / 3, 1), Vector4(300.0f / 3, 0, 100.0f / 3, 1), Vector4(300.0f / 3, 0, 200.0f / 3, 1), + Vector4(300.0f / 3, 0, 300.0f / 3, 1), +}; +struct Halfedge { + uint32 vertexID; + uint32 nextID; + uint32 prevID; + uint32 twinID; +}; +Array edges = { + Halfedge{0, 1, 2, 4294967295}, + Halfedge{1, 2, 0, 3}, + Halfedge{4, 0, 1, 4294967295}, + Halfedge{4, 4, 5, 1}, + Halfedge{1, 5, 3, 8}, + Halfedge{5, 3, 4, 18}, + Halfedge{1, 7, 8, 4294967295}, + Halfedge{2, 8, 6, 9}, + Halfedge{5, 6, 7, 4}, + Halfedge{5, 10, 11, 7}, + Halfedge{2, 11, 9, 14}, + Halfedge{6, 9, 10, 24}, + Halfedge{2, 13, 14, 4294967295}, + Halfedge{3, 14, 12, 15}, + Halfedge{6, 12, 13, 10}, + Halfedge{6, 16, 17, 13}, + Halfedge{3, 17, 15, 4294967295}, + Halfedge{7, 15, 16, 30}, + Halfedge{4, 19, 20, 5}, + Halfedge{5, 20, 18, 21}, + Halfedge{8, 18, 19, 4294967295}, + Halfedge{8, 22, 23, 19}, + Halfedge{5, 23, 21, 26}, + Halfedge{9, 21, 22, 36}, + Halfedge{5, 25, 26, 11}, + Halfedge{6, 26, 24, 27}, + Halfedge{9, 24, 25, 22}, + Halfedge{9, 28, 29, 25}, + Halfedge{6, 29, 27, 32}, + Halfedge{10, 27, 28, 42}, + Halfedge{6, 31, 32, 17}, + Halfedge{7, 32, 30, 33}, + Halfedge{10, 30, 31, 28}, + Halfedge{10, 34, 35, 31}, + Halfedge{7, 35, 33, 4294967295}, + Halfedge{11, 33, 34, 48}, + Halfedge{8, 37, 38, 23}, + Halfedge{9, 38, 36, 39}, + Halfedge{12, 36, 37, 4294967295}, + Halfedge{12, 40, 41, 37}, + Halfedge{9, 41, 39, 44}, + Halfedge{13, 39, 40, 4294967295}, + Halfedge{9, 43, 44, 29}, + Halfedge{10, 44, 42, 45}, + Halfedge{13, 42, 43, 40}, + Halfedge{13, 46, 47, 43}, + Halfedge{10, 47, 45, 50}, + Halfedge{14, 45, 46, 4294967295}, + Halfedge{10, 49, 50, 35}, + Halfedge{11, 50, 48, 51}, + Halfedge{14, 48, 49, 46}, + Halfedge{14, 52, 53, 49}, + Halfedge{11, 53, 51, 4294967295}, + Halfedge{15, 51, 52, 4294967295}, +}; + +uint32_t find_msb_64(uint64_t x) { + uint32_t depth = 0; + while (x > 0u) { + ++depth; + x >>= 1uLL; + } + return depth; +} + +CPUMesh Seele::generateCPUMesh(uint32 cbtNumElements) { + CPUMesh result; + result.totalNumElements = edges.size() + cbtNumElements; + + result.heapIDArray.resize(result.totalNumElements); + result.neighborsArray.resize(result.totalNumElements); + std::memset(result.heapIDArray.data(), 0, result.totalNumElements * sizeof(uint64)); + std::memset(result.neighborsArray.data(), 0, result.totalNumElements * sizeof(UVector)); + + result.minimalDepth = std::min(find_msb_64(edges.size()), 63u) + 1; + const uint64 baseHeapID = 1ull << (result.minimalDepth - 1); + + result.basePoints.resize(edges.size() * 3); + + UVector neighbours; + for (uint32 halfedgeIdx = 0; halfedgeIdx < (uint32)edges.size(); ++halfedgeIdx) { + uint32 elementID = cbtNumElements + halfedgeIdx; + + result.heapIDArray[elementID] = baseHeapID + halfedgeIdx; + + Halfedge& halfedge = edges[halfedgeIdx]; + neighbours.x = halfedge.prevID != -1 ? cbtNumElements + halfedge.prevID : UINT32_MAX; + neighbours.y = halfedge.nextID != -1 ? cbtNumElements + halfedge.nextID : UINT32_MAX; + neighbours.z = halfedge.twinID != -1 ? cbtNumElements + halfedge.twinID : UINT32_MAX; + result.neighborsArray[elementID] = UVector4(neighbours, 1); + + result.basePoints[3 * halfedgeIdx + 2] = basePoints[halfedge.vertexID]; + + Halfedge& nextHalfedge = edges[halfedge.nextID]; + result.basePoints[3 * halfedgeIdx + 0] = basePoints[nextHalfedge.vertexID]; + + Vector thirdVertex = result.basePoints[3 * halfedgeIdx + 2]; + float sumWeight = 1.0f; + uint32 currentIdx = halfedge.nextID; + while (currentIdx != halfedgeIdx) { + Halfedge currentHalfedge = edges[currentIdx]; + Vector vp = basePoints[currentHalfedge.vertexID]; + thirdVertex += vp; + sumWeight += 1.0f; + currentIdx = currentHalfedge.nextID; + } + result.basePoints[3 * halfedgeIdx + 1] = Vector4(thirdVertex / sumWeight, 1); + } + return result; +} + +Matrix3 splittingMatrix(uint32 bitValue) { + float b = float(bitValue); + float c = 1.0f - b; + + return glm::transpose(Matrix3({ + 0.0f, + b, + c, + 0.5f, + 0.0f, + 0.5f, + b, + c, + 0.0f, + })); +} + +Matrix3 decodeSubdivisionMatrix(uint64 heapID) { + Matrix3 m = Matrix3({1.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 1.0f}); + int32 depth = find_msb_64(heapID) - 1; + for (int32 bitID = depth - 1; bitID >= 0; --bitID) { + m = splittingMatrix((heapID >> bitID) & 1u) * m; + } + return m; +} + +void LebMatrixCache::init(Gfx::PGraphics graphics, uint32 depth) { + cacheDepth = depth; + uint32 matrixCount = 2ULL << cacheDepth; + struct PaddedMatrix + { + Matrix3 m; + uint8 pad[12]; + }; + std::vector table(matrixCount); + table[0].m = Matrix3({1.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 1.0f}); + for (uint64 heapID = 1ULL; heapID < (2ULL << cacheDepth); ++heapID) { + table[heapID].m = decodeSubdivisionMatrix(heapID); + } + lebMatrixBuffer = graphics->createShaderBuffer(ShaderBufferCreateInfo{ + .sourceData = + { + .size = sizeof(PaddedMatrix) * matrixCount, + .data = (uint8*)table.data(), + }, + .name = "LebMatrixCache", + }); + lebMatrixBuffer->pipelineBarrier(Gfx::SE_ACCESS_TRANSFER_WRITE_BIT, Gfx::SE_PIPELINE_STAGE_TRANSFER_BIT, + Gfx::SE_ACCESS_SHADER_READ_BIT | Gfx::SE_ACCESS_SHADER_WRITE_BIT, + Gfx::SE_PIPELINE_STAGE_COMPUTE_SHADER_BIT); +} + +void LebMatrixCache::release() {} + +void MeshUpdater::init(Gfx::PGraphics gfx, Gfx::PDescriptorLayout viewParamsLayout) { + graphics = gfx; + layout = graphics->createDescriptorLayout("pParams"); + layout->addDescriptorBinding(Gfx::DescriptorBinding{0, Gfx::SE_DESCRIPTOR_TYPE_UNIFORM_BUFFER}); + layout->addDescriptorBinding(Gfx::DescriptorBinding{1, Gfx::SE_DESCRIPTOR_TYPE_UNIFORM_BUFFER}); + layout->addDescriptorBinding(Gfx::DescriptorBinding{2, Gfx::SE_DESCRIPTOR_TYPE_STORAGE_BUFFER}); + layout->addDescriptorBinding(Gfx::DescriptorBinding{3, Gfx::SE_DESCRIPTOR_TYPE_STORAGE_BUFFER}); + layout->addDescriptorBinding(Gfx::DescriptorBinding{4, Gfx::SE_DESCRIPTOR_TYPE_STORAGE_BUFFER}); + layout->addDescriptorBinding(Gfx::DescriptorBinding{5, Gfx::SE_DESCRIPTOR_TYPE_STORAGE_BUFFER}); + layout->addDescriptorBinding(Gfx::DescriptorBinding{6, Gfx::SE_DESCRIPTOR_TYPE_STORAGE_BUFFER}); + layout->addDescriptorBinding(Gfx::DescriptorBinding{7, Gfx::SE_DESCRIPTOR_TYPE_STORAGE_BUFFER}); + layout->addDescriptorBinding(Gfx::DescriptorBinding{8, Gfx::SE_DESCRIPTOR_TYPE_STORAGE_BUFFER}); + layout->addDescriptorBinding(Gfx::DescriptorBinding{9, Gfx::SE_DESCRIPTOR_TYPE_STORAGE_BUFFER}); + layout->addDescriptorBinding(Gfx::DescriptorBinding{10, Gfx::SE_DESCRIPTOR_TYPE_STORAGE_BUFFER}); + layout->addDescriptorBinding(Gfx::DescriptorBinding{11, Gfx::SE_DESCRIPTOR_TYPE_STORAGE_BUFFER}); + layout->addDescriptorBinding(Gfx::DescriptorBinding{12, Gfx::SE_DESCRIPTOR_TYPE_STORAGE_BUFFER}); + layout->addDescriptorBinding(Gfx::DescriptorBinding{13, Gfx::SE_DESCRIPTOR_TYPE_STORAGE_BUFFER}); + layout->addDescriptorBinding(Gfx::DescriptorBinding{14, Gfx::SE_DESCRIPTOR_TYPE_STORAGE_BUFFER}); + layout->addDescriptorBinding(Gfx::DescriptorBinding{15, Gfx::SE_DESCRIPTOR_TYPE_STORAGE_BUFFER}); + layout->addDescriptorBinding(Gfx::DescriptorBinding{16, Gfx::SE_DESCRIPTOR_TYPE_STORAGE_BUFFER}); + layout->addDescriptorBinding(Gfx::DescriptorBinding{17, Gfx::SE_DESCRIPTOR_TYPE_STORAGE_BUFFER}); + layout->addDescriptorBinding(Gfx::DescriptorBinding{18, Gfx::SE_DESCRIPTOR_TYPE_STORAGE_BUFFER}); + layout->addDescriptorBinding(Gfx::DescriptorBinding{19, Gfx::SE_DESCRIPTOR_TYPE_STORAGE_BUFFER}); + layout->addDescriptorBinding(Gfx::DescriptorBinding{20, Gfx::SE_DESCRIPTOR_TYPE_STORAGE_BUFFER}); + layout->addDescriptorBinding(Gfx::DescriptorBinding{21, Gfx::SE_DESCRIPTOR_TYPE_STORAGE_BUFFER}); + layout->addDescriptorBinding(Gfx::DescriptorBinding{22, Gfx::SE_DESCRIPTOR_TYPE_STORAGE_BUFFER}); + layout->create(); + pipelineLayout = graphics->createPipelineLayout("ComputeLayout"); + pipelineLayout->addDescriptorLayout(viewParamsLayout); + pipelineLayout->addDescriptorLayout(layout); + indirectBuffer = graphics->createShaderBuffer(ShaderBufferCreateInfo{ + .sourceData = + { + .size = sizeof(uint32) * 9, + }, + .usage = Gfx::SE_BUFFER_USAGE_INDIRECT_BUFFER_BIT, + .name = "IndirectBuffer", + }); + memoryBuffer = graphics->createShaderBuffer(ShaderBufferCreateInfo{ + .sourceData = + { + .size = sizeof(int32) * 2, + }, + .name = "MemoryBuffer", + }); + validationBuffer = graphics->createShaderBuffer(ShaderBufferCreateInfo{ + .sourceData = + { + .size = sizeof(int32) * 2, + }, + .name = "ValidationBuffer", + }); + validationBufferRB = graphics->createShaderBuffer(ShaderBufferCreateInfo{ + .sourceData = + { + .size = sizeof(int32) * 2, + }, + .name = "ValidationBufferRB", + }); + occupancyBufferRB = graphics->createShaderBuffer(ShaderBufferCreateInfo{ + .sourceData = + { + .size = sizeof(uint32), + }, + .name = "OccupancyBuffer", + }); + + graphics->beginShaderCompilation(ShaderCompilationInfo{ + .name = "CBTCompute", + .modules = {"CBTCompute"}, + .entryPoints = + { + {"reset", "CBTCompute"}, + {"classify", "CBTCompute"}, + {"split", "CBTCompute"}, + {"prepareIndirect", "CBTCompute"}, + {"allocate", "CBTCompute"}, + {"bisect", "CBTCompute"}, + {"propagateBisect", "CBTCompute"}, + {"prepareSimplify", "CBTCompute"}, + {"simplify", "CBTCompute"}, + {"propagateSimplify", "CBTCompute"}, + {"reducePrePass", "CBTCompute"}, + {"reduceFirstPass", "CBTCompute"}, + {"reduceSecondPass", "CBTCompute"}, + {"bisectorIndexation", "CBTCompute"}, + {"prepareBisectorIndirect", "CBTCompute"}, + {"validate", "CBTCompute"}, + {"clearLeb", "CBTCompute"}, + {"evaluateLeb", "CBTCompute"}, + }, + .rootSignature = pipelineLayout, + }); + pipelineLayout->create(); + resetCS = graphics->createComputeShader({0}); + reset = graphics->createComputePipeline(Gfx::ComputePipelineCreateInfo{ + .computeShader = resetCS, + .pipelineLayout = pipelineLayout, + }); + classifyCS = graphics->createComputeShader({1}); + classify = graphics->createComputePipeline(Gfx::ComputePipelineCreateInfo{ + .computeShader = classifyCS, + .pipelineLayout = pipelineLayout, + }); + splitCS = graphics->createComputeShader({2}); + split = graphics->createComputePipeline(Gfx::ComputePipelineCreateInfo{ + .computeShader = splitCS, + .pipelineLayout = pipelineLayout, + }); + prepareIndirectCS = graphics->createComputeShader({3}); + prepareIndirect = graphics->createComputePipeline(Gfx::ComputePipelineCreateInfo{ + .computeShader = prepareIndirectCS, + .pipelineLayout = pipelineLayout, + }); + allocateCS = graphics->createComputeShader({4}); + allocate = graphics->createComputePipeline(Gfx::ComputePipelineCreateInfo{ + .computeShader = allocateCS, + .pipelineLayout = pipelineLayout, + }); + bisectCS = graphics->createComputeShader({5}); + bisect = graphics->createComputePipeline(Gfx::ComputePipelineCreateInfo{ + .computeShader = bisectCS, + .pipelineLayout = pipelineLayout, + }); + propagateBisectCS = graphics->createComputeShader({6}); + propagateBisect = graphics->createComputePipeline(Gfx::ComputePipelineCreateInfo{ + .computeShader = propagateBisectCS, + .pipelineLayout = pipelineLayout, + }); + prepareSimplifyCS = graphics->createComputeShader({7}); + prepareSimplify = graphics->createComputePipeline(Gfx::ComputePipelineCreateInfo{ + .computeShader = prepareSimplifyCS, + .pipelineLayout = pipelineLayout, + }); + simplifyCS = graphics->createComputeShader({8}); + simplify = graphics->createComputePipeline(Gfx::ComputePipelineCreateInfo{ + .computeShader = simplifyCS, + .pipelineLayout = pipelineLayout, + }); + propagateSimplifyCS = graphics->createComputeShader({9}); + propagateSimplify = graphics->createComputePipeline(Gfx::ComputePipelineCreateInfo{ + .computeShader = propagateSimplifyCS, + .pipelineLayout = pipelineLayout, + }); + reducePrePassCS = graphics->createComputeShader({10}); + reducePrePass = graphics->createComputePipeline(Gfx::ComputePipelineCreateInfo{ + .computeShader = reducePrePassCS, + .pipelineLayout = pipelineLayout, + }); + reduceFirstPassCS = graphics->createComputeShader({11}); + reduceFirstPass = graphics->createComputePipeline(Gfx::ComputePipelineCreateInfo{ + .computeShader = reduceFirstPassCS, + .pipelineLayout = pipelineLayout, + }); + reduceSecondPassCS = graphics->createComputeShader({12}); + reduceSecondPass = graphics->createComputePipeline(Gfx::ComputePipelineCreateInfo{ + .computeShader = reduceSecondPassCS, + .pipelineLayout = pipelineLayout, + }); + bisectorIndexationCS = graphics->createComputeShader({13}); + bisectorIndexation = graphics->createComputePipeline(Gfx::ComputePipelineCreateInfo{ + .computeShader = bisectorIndexationCS, + .pipelineLayout = pipelineLayout, + }); + prepareBisectorIndirectCS = graphics->createComputeShader({14}); + prepareBisectorIndirect = graphics->createComputePipeline(Gfx::ComputePipelineCreateInfo{ + .computeShader = prepareBisectorIndirectCS, + .pipelineLayout = pipelineLayout, + }); + validateCS = graphics->createComputeShader({15}); + validate = graphics->createComputePipeline(Gfx::ComputePipelineCreateInfo{ + .computeShader = validateCS, + .pipelineLayout = pipelineLayout, + }); + lebClearCS = graphics->createComputeShader({16}); + lebClear = graphics->createComputePipeline(Gfx::ComputePipelineCreateInfo{ + .computeShader = lebClearCS, + .pipelineLayout = pipelineLayout, + }); + lebEvaluateCS = graphics->createComputeShader({17}); + lebEvaluate = graphics->createComputePipeline(Gfx::ComputePipelineCreateInfo{ + .computeShader = lebEvaluateCS, + .pipelineLayout = pipelineLayout, + }); +} + +void MeshUpdater::release() {} + +void MeshUpdater::evaluateLeb(const BaseMesh& baseMesh, CBTMesh& mesh, Gfx::PDescriptorSet viewParamsSet, + Gfx::PUniformBuffer geometryBuffer, Gfx::PUniformBuffer updateBuffer, Gfx::PShaderBuffer lebMatrixCache, + bool clear, bool complete) { + if (clear) { + Gfx::PDescriptorSet set = layout->allocateDescriptorSet(); + set->updateBuffer(GEOMETRY_CB, 0, geometryBuffer); + set->updateBuffer(LEB_POSITION_BUFFER, 0, mesh.currentVertexBuffer); + set->writeChanges(); + Gfx::OComputeCommand clearCmd = graphics->createComputeCommand("Clear"); + clearCmd->bindPipeline(lebClear); + clearCmd->bindDescriptor(set); + clearCmd->dispatch((mesh.totalNumElements * 3 + WORKGROUP_SIZE - 1) / WORKGROUP_SIZE, 1, 1); + mesh.currentVertexBuffer->pipelineBarrier(Gfx::SE_ACCESS_SHADER_WRITE_BIT, Gfx::SE_PIPELINE_STAGE_COMPUTE_SHADER_BIT, + Gfx::SE_ACCESS_SHADER_READ_BIT, Gfx::SE_PIPELINE_STAGE_COMPUTE_SHADER_BIT); + } + Gfx::PDescriptorSet set = layout->allocateDescriptorSet(); + set->updateBuffer(GEOMETRY_CB, 0, geometryBuffer); + set->updateBuffer(UPDATE_CB, 0, updateBuffer); + set->updateBuffer(CURRENT_VERTEX_BUFFER, 0, baseMesh.vertexBuffer); + set->updateBuffer(HEAP_ID_BUFFER, 0, mesh.heapIDBuffer); + set->updateBuffer(INDEXED_BISECTOR_BUFFER, 0, complete ? mesh.indexedBisectorBuffer : mesh.modifiedIndexedBisectorBuffer); + set->updateBuffer(INDIRECT_DRAW_BUFFER, 0, mesh.indirectDrawBuffer); + set->updateBuffer(LEB_MATRIX_CACHE, 0, lebMatrixCache); + set->updateBuffer(LEB_POSITION_BUFFER, 0, mesh.lebVertexBuffer); + set->writeChanges(); + Gfx::OComputeCommand evalCmd = graphics->createComputeCommand("EvalLEB"); + evalCmd->bindPipeline(lebEvaluate); + evalCmd->bindDescriptor({set, viewParamsSet}); + evalCmd->dispatchIndirect(mesh.indirectDispatchBuffer, complete ? 0 : sizeof(uint32) * 6); + graphics->executeCommands(std::move(evalCmd)); + mesh.currentVertexBuffer->pipelineBarrier(Gfx::SE_ACCESS_SHADER_WRITE_BIT, Gfx::SE_PIPELINE_STAGE_COMPUTE_SHADER_BIT, + Gfx::SE_ACCESS_SHADER_READ_BIT, Gfx::SE_PIPELINE_STAGE_COMPUTE_SHADER_BIT); +} + +void MeshUpdater::update(CBTMesh& mesh, Gfx::PDescriptorSet viewParamsSet, Gfx::PUniformBuffer geometryCB, Gfx::PUniformBuffer updateCB) { + uint32 nextNeighborsBufferIdx = (mesh.currentNeighborsBufferIdx + 1) % 2; + Gfx::PShaderBuffer currentNeighborsBuffer = mesh.neighborsBuffers[mesh.currentNeighborsBufferIdx]; + Gfx::PShaderBuffer nextNeighborsBuffer = mesh.neighborsBuffers[nextNeighborsBufferIdx]; + + resetBuffers(mesh); + + // classify + { + Gfx::PDescriptorSet set = layout->allocateDescriptorSet(); + set->updateBuffer(GEOMETRY_CB, 0, geometryCB); + set->updateBuffer(UPDATE_CB, 0, updateCB); + + set->updateBuffer(CURRENT_VERTEX_BUFFER, 0, mesh.currentVertexBuffer); + set->updateBuffer(INDEXED_BISECTOR_BUFFER, 0, mesh.indexedBisectorBuffer); + + set->updateBuffer(INDIRECT_DRAW_BUFFER, 0, mesh.indirectDrawBuffer); + set->updateBuffer(HEAP_ID_BUFFER, 0, mesh.heapIDBuffer); + set->updateBuffer(BISECTOR_DATA_BUFFER, 0, mesh.updateBuffer); + set->updateBuffer(CLASSIFICATION_BUFFER, 0, mesh.classificationBuffer); + set->writeChanges(); + Gfx::OComputeCommand classifyCmd = graphics->createComputeCommand("Classify"); + classifyCmd->bindPipeline(classify); + classifyCmd->bindDescriptor({viewParamsSet, set}); + classifyCmd->dispatchIndirect(mesh.indirectDispatchBuffer, 0); + graphics->executeCommands(std::move(classifyCmd)); + mesh.updateBuffer->pipelineBarrier(Gfx::SE_ACCESS_SHADER_WRITE_BIT, Gfx::SE_PIPELINE_STAGE_COMPUTE_SHADER_BIT, + Gfx::SE_ACCESS_SHADER_READ_BIT, Gfx::SE_PIPELINE_STAGE_COMPUTE_SHADER_BIT); + } + + // prepare indirect pass + { + Gfx::PDescriptorSet set = layout->allocateDescriptorSet(); + set->updateBuffer(ALLOCATE_BUFFER, 0, mesh.classificationBuffer); + set->updateBuffer(INDIRECT_DISPATCH_BUFFER, 0, indirectBuffer); + set->writeChanges(); + Gfx::OComputeCommand prepareIndirectCmd = graphics->createComputeCommand("PrepareIndirect"); + prepareIndirectCmd->bindPipeline(prepareIndirect); + prepareIndirectCmd->bindDescriptor(set); + prepareIndirectCmd->dispatch(2, 1, 1); + graphics->executeCommands(std::move(prepareIndirectCmd)); + indirectBuffer->pipelineBarrier(Gfx::SE_ACCESS_SHADER_WRITE_BIT, Gfx::SE_PIPELINE_STAGE_COMPUTE_SHADER_BIT, + Gfx::SE_ACCESS_SHADER_READ_BIT, Gfx::SE_PIPELINE_STAGE_COMPUTE_SHADER_BIT); + } + + // split pass + { + Gfx::PDescriptorSet set = layout->allocateDescriptorSet(); + set->updateBuffer(GEOMETRY_CB, 0, geometryCB); + set->updateBuffer(UPDATE_CB, 0, updateCB); + + set->updateBuffer(CLASSIFICATION_BUFFER, 0, mesh.classificationBuffer); + set->updateBuffer(HEAP_ID_BUFFER, 0, mesh.heapIDBuffer); + set->updateBuffer(BISECTOR_DATA_BUFFER, 0, mesh.updateBuffer); + set->updateBuffer(NEIGHBOURS_BUFFER, 0, currentNeighborsBuffer); + set->updateBuffer(MEMORY_BUFFER, 0, memoryBuffer); + set->updateBuffer(ALLOCATE_BUFFER, 0, mesh.allocateBuffer); + set->writeChanges(); + Gfx::OComputeCommand splitCmd = graphics->createComputeCommand("Split"); + splitCmd->bindPipeline(split); + splitCmd->bindDescriptor({viewParamsSet, set}); + splitCmd->dispatchIndirect(indirectBuffer, 0); + graphics->executeCommands(std::move(splitCmd)); + mesh.updateBuffer->pipelineBarrier(Gfx::SE_ACCESS_SHADER_WRITE_BIT, Gfx::SE_PIPELINE_STAGE_COMPUTE_SHADER_BIT, + Gfx::SE_ACCESS_SHADER_READ_BIT, Gfx::SE_PIPELINE_STAGE_COMPUTE_SHADER_BIT); + } + + // prepare indirect pass + { + Gfx::PDescriptorSet set = layout->allocateDescriptorSet(); + set->updateBuffer(ALLOCATE_BUFFER, 0, mesh.allocateBuffer); + set->updateBuffer(INDIRECT_DISPATCH_BUFFER, 0, indirectBuffer); + set->writeChanges(); + Gfx::OComputeCommand prepareIndirectCmd = graphics->createComputeCommand("PrepareIndirect"); + prepareIndirectCmd->bindPipeline(prepareIndirect); + prepareIndirectCmd->bindDescriptor(set); + prepareIndirectCmd->dispatch(1, 1, 1); + graphics->executeCommands(std::move(prepareIndirectCmd)); + indirectBuffer->pipelineBarrier(Gfx::SE_ACCESS_SHADER_WRITE_BIT, Gfx::SE_PIPELINE_STAGE_COMPUTE_SHADER_BIT, + Gfx::SE_ACCESS_SHADER_READ_BIT, Gfx::SE_PIPELINE_STAGE_COMPUTE_SHADER_BIT); + } + + // Allocate Pass + { + Gfx::PDescriptorSet set = layout->allocateDescriptorSet(); + set->updateBuffer(GEOMETRY_CB, 0, geometryCB); + set->updateBuffer(UPDATE_CB, 0, updateCB); + + set->updateBuffer(CBT_BUFFER0, 0, mesh.gpuCBT.bufferArray[0]); + set->updateBuffer(CBT_BUFFER1, 0, mesh.gpuCBT.bufferArray[1]); + set->updateBuffer(ALLOCATE_BUFFER, 0, mesh.allocateBuffer); + set->updateBuffer(BISECTOR_DATA_BUFFER, 0, mesh.updateBuffer); + set->updateBuffer(MEMORY_BUFFER, 0, memoryBuffer); + set->writeChanges(); + Gfx::OComputeCommand allocateCmd = graphics->createComputeCommand("Allocate"); + allocateCmd->bindPipeline(allocate); + allocateCmd->bindDescriptor({viewParamsSet, set}); + allocateCmd->dispatchIndirect(indirectBuffer, 0); + graphics->executeCommands(std::move(allocateCmd)); + mesh.updateBuffer->pipelineBarrier(Gfx::SE_ACCESS_SHADER_WRITE_BIT, Gfx::SE_PIPELINE_STAGE_COMPUTE_SHADER_BIT, + Gfx::SE_ACCESS_SHADER_READ_BIT, Gfx::SE_PIPELINE_STAGE_COMPUTE_SHADER_BIT); + } + + // copy + currentNeighborsBuffer->pipelineBarrier(Gfx::SE_ACCESS_SHADER_WRITE_BIT, Gfx::SE_PIPELINE_STAGE_COMPUTE_SHADER_BIT, + Gfx::SE_ACCESS_TRANSFER_READ_BIT, Gfx::SE_PIPELINE_STAGE_TRANSFER_BIT); + graphics->copyBuffer(currentNeighborsBuffer, nextNeighborsBuffer); + nextNeighborsBuffer->pipelineBarrier(Gfx::SE_ACCESS_TRANSFER_WRITE_BIT, Gfx::SE_PIPELINE_STAGE_TRANSFER_BIT, + Gfx::SE_ACCESS_SHADER_READ_BIT, Gfx::SE_PIPELINE_STAGE_COMPUTE_SHADER_BIT); + + // bisect + { + Gfx::PDescriptorSet set = layout->allocateDescriptorSet(); + set->updateBuffer(GEOMETRY_CB, 0, geometryCB); + set->updateBuffer(UPDATE_CB, 0, updateCB); + + set->updateBuffer(CBT_BUFFER0, 0, mesh.gpuCBT.bufferArray[0]); + set->updateBuffer(CBT_BUFFER1, 0, mesh.gpuCBT.bufferArray[1]); + set->updateBuffer(ALLOCATE_BUFFER, 0, mesh.allocateBuffer); + set->updateBuffer(HEAP_ID_BUFFER, 0, mesh.heapIDBuffer); + set->updateBuffer(BISECTOR_DATA_BUFFER, 0, mesh.updateBuffer); + set->updateBuffer(NEIGHBOURS_BUFFER, 0, currentNeighborsBuffer); + set->updateBuffer(NEIGHBOURS_OUTPUT_BUFFER, 0, nextNeighborsBuffer); + set->updateBuffer(PROPAGATE_BUFFER, 0, mesh.propagateBuffer); + set->writeChanges(); + Gfx::OComputeCommand bisectCmd = graphics->createComputeCommand("Bisect"); + bisectCmd->bindPipeline(bisect); + bisectCmd->bindDescriptor({viewParamsSet, set}); + bisectCmd->dispatchIndirect(indirectBuffer, 0); + graphics->executeCommands(std::move(bisectCmd)); + nextNeighborsBuffer->pipelineBarrier(Gfx::SE_ACCESS_SHADER_WRITE_BIT, Gfx::SE_PIPELINE_STAGE_COMPUTE_SHADER_BIT, + Gfx::SE_ACCESS_SHADER_READ_BIT, Gfx::SE_PIPELINE_STAGE_COMPUTE_SHADER_BIT); + } + + // Prepare indirect pass + { + Gfx::PDescriptorSet set = layout->allocateDescriptorSet(); + set->updateBuffer(ALLOCATE_BUFFER, 0, mesh.propagateBuffer); + set->updateBuffer(INDIRECT_DISPATCH_BUFFER, 0, indirectBuffer); + set->writeChanges(); + Gfx::OComputeCommand prepareIndirectCmd = graphics->createComputeCommand("PrepareIndirectPropagateBisect"); + prepareIndirectCmd->bindPipeline(prepareIndirect); + prepareIndirectCmd->bindDescriptor(set); + prepareIndirectCmd->dispatch(1, 1, 1); + graphics->executeCommands(std::move(prepareIndirectCmd)); + indirectBuffer->pipelineBarrier(Gfx::SE_ACCESS_SHADER_WRITE_BIT, Gfx::SE_PIPELINE_STAGE_COMPUTE_SHADER_BIT, + Gfx::SE_ACCESS_SHADER_READ_BIT, Gfx::SE_PIPELINE_STAGE_COMPUTE_SHADER_BIT); + } + + // propagate split pass + { + Gfx::PDescriptorSet set = layout->allocateDescriptorSet(); + set->updateBuffer(GEOMETRY_CB, 0, geometryCB); + set->updateBuffer(UPDATE_CB, 0, updateCB); + + set->updateBuffer(PROPAGATE_BUFFER, 0, mesh.propagateBuffer); + set->updateBuffer(BISECTOR_DATA_BUFFER, 0, mesh.updateBuffer); + set->updateBuffer(NEIGHBOURS_BUFFER, 0, nextNeighborsBuffer); + set->writeChanges(); + Gfx::OComputeCommand propagateBisectCmd = graphics->createComputeCommand("PropagateBisect"); + propagateBisectCmd->bindPipeline(propagateBisect); + propagateBisectCmd->bindDescriptor({viewParamsSet, set}); + propagateBisectCmd->dispatchIndirect(indirectBuffer, 0); + graphics->executeCommands(std::move(propagateBisectCmd)); + mesh.updateBuffer->pipelineBarrier(Gfx::SE_ACCESS_SHADER_WRITE_BIT, Gfx::SE_PIPELINE_STAGE_COMPUTE_SHADER_BIT, + Gfx::SE_ACCESS_SHADER_READ_BIT, Gfx::SE_PIPELINE_STAGE_COMPUTE_SHADER_BIT); + } + + // prepare simplify + { + Gfx::PDescriptorSet set = layout->allocateDescriptorSet(); + set->updateBuffer(GEOMETRY_CB, 0, geometryCB); + set->updateBuffer(UPDATE_CB, 0, updateCB); + + set->updateBuffer(CLASSIFICATION_BUFFER, 0, mesh.classificationBuffer); + set->updateBuffer(HEAP_ID_BUFFER, 0, mesh.heapIDBuffer); + set->updateBuffer(BISECTOR_DATA_BUFFER, 0, mesh.updateBuffer); + set->updateBuffer(NEIGHBOURS_BUFFER, 0, nextNeighborsBuffer); + set->updateBuffer(SIMPLIFICATION_BUFFER, 0, mesh.simplificationBuffer); + set->writeChanges(); + Gfx::OComputeCommand prepareSimplifyCmd = graphics->createComputeCommand("PrepareSimplify"); + prepareSimplifyCmd->bindPipeline(prepareSimplify); + prepareSimplifyCmd->bindDescriptor({viewParamsSet, set}); + prepareSimplifyCmd->dispatchIndirect(indirectBuffer, 0); + graphics->executeCommands(std::move(prepareSimplifyCmd)); + mesh.simplificationBuffer->pipelineBarrier(Gfx::SE_ACCESS_SHADER_WRITE_BIT, Gfx::SE_PIPELINE_STAGE_COMPUTE_SHADER_BIT, + Gfx::SE_ACCESS_SHADER_READ_BIT, Gfx::SE_PIPELINE_STAGE_COMPUTE_SHADER_BIT); + } + + // Prepare Indirect Simplify + { + Gfx::PDescriptorSet set = layout->allocateDescriptorSet(); + set->updateBuffer(ALLOCATE_BUFFER, 0, mesh.simplificationBuffer); + set->updateBuffer(INDIRECT_DISPATCH_BUFFER, 0, indirectBuffer); + set->writeChanges(); + Gfx::OComputeCommand prepareIndirectCmd = graphics->createComputeCommand("PrepareIndirectSimplify"); + prepareIndirectCmd->bindPipeline(prepareIndirect); + prepareIndirectCmd->bindDescriptor(set); + prepareIndirectCmd->dispatch(1, 1, 1); + graphics->executeCommands(std::move(prepareIndirectCmd)); + indirectBuffer->pipelineBarrier(Gfx::SE_ACCESS_SHADER_WRITE_BIT, Gfx::SE_PIPELINE_STAGE_COMPUTE_SHADER_BIT, + Gfx::SE_ACCESS_SHADER_READ_BIT, Gfx::SE_PIPELINE_STAGE_COMPUTE_SHADER_BIT); + } + + // Simplify Pass + { + Gfx::PDescriptorSet set = layout->allocateDescriptorSet(); + set->updateBuffer(GEOMETRY_CB, 0, geometryCB); + set->updateBuffer(UPDATE_CB, 0, updateCB); + + set->updateBuffer(SIMPLIFICATION_BUFFER, 0, mesh.simplificationBuffer); + set->updateBuffer(BISECTOR_DATA_BUFFER, 0, mesh.updateBuffer); + set->updateBuffer(NEIGHBOURS_BUFFER, 0, nextNeighborsBuffer); + set->updateBuffer(HEAP_ID_BUFFER, 0, mesh.heapIDBuffer); + set->updateBuffer(CBT_BUFFER0, 0, mesh.gpuCBT.bufferArray[0]); + set->updateBuffer(CBT_BUFFER1, 0, mesh.gpuCBT.bufferArray[1]); + set->updateBuffer(PROPAGATE_BUFFER, 0, mesh.propagateBuffer); + set->writeChanges(); + Gfx::OComputeCommand simplifyCmd = graphics->createComputeCommand("simplify"); + simplifyCmd->bindPipeline(simplify); + simplifyCmd->bindDescriptor({viewParamsSet, set}); + simplifyCmd->dispatchIndirect(indirectBuffer, 0); + graphics->executeCommands(std::move(simplifyCmd)); + nextNeighborsBuffer->pipelineBarrier(Gfx::SE_ACCESS_SHADER_WRITE_BIT, Gfx::SE_PIPELINE_STAGE_COMPUTE_SHADER_BIT, + Gfx::SE_ACCESS_SHADER_READ_BIT, Gfx::SE_PIPELINE_STAGE_COMPUTE_SHADER_BIT); + } + + // Prepare Indirect Propagate Simplify + { + Gfx::PDescriptorSet set = layout->allocateDescriptorSet(); + set->updateBuffer(ALLOCATE_BUFFER, 0, mesh.propagateBuffer); + set->updateBuffer(INDIRECT_DISPATCH_BUFFER, 0, indirectBuffer); + set->writeChanges(); + Gfx::OComputeCommand prepareIndirectCmd = graphics->createComputeCommand("PrepareIndirectPropagateSimplify"); + prepareIndirectCmd->bindPipeline(prepareIndirect); + prepareIndirectCmd->bindDescriptor({viewParamsSet, set}); + prepareIndirectCmd->dispatch(2, 1, 1); + graphics->executeCommands(std::move(prepareIndirectCmd)); + indirectBuffer->pipelineBarrier(Gfx::SE_ACCESS_SHADER_WRITE_BIT, Gfx::SE_PIPELINE_STAGE_COMPUTE_SHADER_BIT, + Gfx::SE_ACCESS_SHADER_READ_BIT, Gfx::SE_PIPELINE_STAGE_COMPUTE_SHADER_BIT); + } + + // Propagate Simplify Pass + { + Gfx::PDescriptorSet set = layout->allocateDescriptorSet(); + set->updateBuffer(GEOMETRY_CB, 0, geometryCB); + set->updateBuffer(UPDATE_CB, 0, updateCB); + + set->updateBuffer(SIMPLIFICATION_BUFFER, 0, mesh.simplificationBuffer); + set->updateBuffer(BISECTOR_DATA_BUFFER, 0, mesh.updateBuffer); + set->updateBuffer(NEIGHBOURS_BUFFER, 0, nextNeighborsBuffer); + set->updateBuffer(HEAP_ID_BUFFER, 0, mesh.heapIDBuffer); + set->updateBuffer(CBT_BUFFER0, 0, mesh.gpuCBT.bufferArray[0]); + set->updateBuffer(CBT_BUFFER1, 0, mesh.gpuCBT.bufferArray[1]); + set->updateBuffer(PROPAGATE_BUFFER, 0, mesh.propagateBuffer); + set->writeChanges(); + Gfx::OComputeCommand simplifyCmd = graphics->createComputeCommand("simplify"); + simplifyCmd->bindPipeline(simplify); + simplifyCmd->bindDescriptor({viewParamsSet, set}); + simplifyCmd->dispatchIndirect(indirectBuffer, 0); + graphics->executeCommands(std::move(simplifyCmd)); + nextNeighborsBuffer->pipelineBarrier(Gfx::SE_ACCESS_SHADER_WRITE_BIT, Gfx::SE_PIPELINE_STAGE_COMPUTE_SHADER_BIT, + Gfx::SE_ACCESS_SHADER_READ_BIT, Gfx::SE_PIPELINE_STAGE_COMPUTE_SHADER_BIT); + } + + // Update Tree + { + Gfx::PDescriptorSet set = layout->allocateDescriptorSet(); + set->updateBuffer(CBT_BUFFER0, 0, mesh.gpuCBT.bufferArray[0]); + set->updateBuffer(CBT_BUFFER1, 0, mesh.gpuCBT.bufferArray[1]); + set->writeChanges(); + + Gfx::OComputeCommand reducePrePassCmd = graphics->createComputeCommand("ReducePrepass"); + reducePrePassCmd->bindPipeline(reducePrePass); + reducePrePassCmd->bindDescriptor(set); + reducePrePassCmd->dispatch(mesh.gpuCBT.lastLevelSize / (4 * WORKGROUP_SIZE), 1, 1); + graphics->executeCommands(std::move(reducePrePassCmd)); + mesh.gpuCBT.bufferArray[0]->pipelineBarrier(Gfx::SE_ACCESS_SHADER_WRITE_BIT, Gfx::SE_PIPELINE_STAGE_COMPUTE_SHADER_BIT, + Gfx::SE_ACCESS_SHADER_READ_BIT, Gfx::SE_PIPELINE_STAGE_COMPUTE_SHADER_BIT); + + Gfx::OComputeCommand reduceFirstPassCmd = graphics->createComputeCommand("ReduceFirstPass"); + reduceFirstPassCmd->bindPipeline(reduceFirstPass); + reduceFirstPassCmd->bindDescriptor(set); + reduceFirstPassCmd->dispatch(8, 1, 1); + graphics->executeCommands(std::move(reduceFirstPassCmd)); + mesh.gpuCBT.bufferArray[0]->pipelineBarrier(Gfx::SE_ACCESS_SHADER_WRITE_BIT, Gfx::SE_PIPELINE_STAGE_COMPUTE_SHADER_BIT, + Gfx::SE_ACCESS_SHADER_READ_BIT, Gfx::SE_PIPELINE_STAGE_COMPUTE_SHADER_BIT); + + Gfx::OComputeCommand reduceSecondPassCmd = graphics->createComputeCommand("ReduceSecondPass"); + reduceSecondPassCmd->bindPipeline(reduceSecondPass); + reduceSecondPassCmd->bindDescriptor(set); + reduceSecondPassCmd->dispatch(1, 1, 1); + graphics->executeCommands(std::move(reduceSecondPassCmd)); + mesh.gpuCBT.bufferArray[0]->pipelineBarrier(Gfx::SE_ACCESS_SHADER_WRITE_BIT, Gfx::SE_PIPELINE_STAGE_COMPUTE_SHADER_BIT, + Gfx::SE_ACCESS_SHADER_READ_BIT, Gfx::SE_PIPELINE_STAGE_COMPUTE_SHADER_BIT); + } + + mesh.currentNeighborsBufferIdx = nextNeighborsBufferIdx; + + prepareIndirection(mesh, geometryCB); +} + +void MeshUpdater::validation(const CBTMesh& mesh, Gfx::PUniformBuffer geometryCB) { + uint32 numGroups = (mesh.totalNumElements + WORKGROUP_SIZE - 1) / WORKGROUP_SIZE; + + Gfx::PDescriptorSet set = layout->allocateDescriptorSet(); + set->updateBuffer(GEOMETRY_CB, 0, geometryCB); + + set->updateBuffer(HEAP_ID_BUFFER, 0, mesh.heapIDBuffer); + set->updateBuffer(BISECTOR_DATA_BUFFER, 0, mesh.updateBuffer); + set->updateBuffer(NEIGHBOURS_BUFFER, 0, mesh.neighborsBuffers[mesh.currentNeighborsBufferIdx]); + set->updateBuffer(VALIDATION_BUFFER, 0, validationBuffer); + set->writeChanges(); + + Gfx::OComputeCommand validateCmd = graphics->createComputeCommand("Validate"); + validateCmd->bindPipeline(validate); + validateCmd->bindDescriptor(set); + validateCmd->dispatch(numGroups, 1, 1); + graphics->executeCommands(std::move(validateCmd)); + validationBuffer->pipelineBarrier(Gfx::SE_ACCESS_SHADER_WRITE_BIT, Gfx::SE_PIPELINE_STAGE_COMPUTE_SHADER_BIT, + Gfx::SE_ACCESS_TRANSFER_READ_BIT, Gfx::SE_PIPELINE_STAGE_TRANSFER_BIT); + graphics->copyBuffer(validationBuffer, validationBufferRB); +} + +void MeshUpdater::resetBuffers(const CBTMesh& mesh) { + Gfx::PDescriptorSet set = layout->allocateDescriptorSet(); + set->updateBuffer(CBT_BUFFER0, 0, mesh.gpuCBT.bufferArray[0]); + set->updateBuffer(CBT_BUFFER1, 0, mesh.gpuCBT.bufferArray[1]); + set->updateBuffer(MEMORY_BUFFER, 0, memoryBuffer); + set->updateBuffer(CLASSIFICATION_BUFFER, 0, mesh.classificationBuffer); + set->updateBuffer(ALLOCATE_BUFFER, 0, mesh.allocateBuffer); + set->updateBuffer(INDIRECT_DRAW_BUFFER, 0, mesh.indirectDrawBuffer); + set->updateBuffer(SIMPLIFICATION_BUFFER, 0, mesh.simplificationBuffer); + set->updateBuffer(PROPAGATE_BUFFER, 0, mesh.propagateBuffer); + set->writeChanges(); + + Gfx::OComputeCommand resetCmd = graphics->createComputeCommand("Reset"); + resetCmd->bindPipeline(reset); + resetCmd->bindDescriptor(set); + resetCmd->dispatch(1, 1, 1); + graphics->executeCommands(std::move(resetCmd)); + memoryBuffer->pipelineBarrier(Gfx::SE_ACCESS_SHADER_WRITE_BIT, Gfx::SE_PIPELINE_STAGE_COMPUTE_SHADER_BIT, + Gfx::SE_ACCESS_SHADER_READ_BIT, Gfx::SE_PIPELINE_STAGE_COMPUTE_SHADER_BIT); +} + +void MeshUpdater::prepareIndirection(CBTMesh& mesh, Gfx::PUniformBuffer geometryCB) { + uint32 numGroups = (mesh.totalNumElements + WORKGROUP_SIZE - 1) / WORKGROUP_SIZE; + + // Bitsector indexation + { + Gfx::PDescriptorSet set = layout->allocateDescriptorSet(); + set->updateBuffer(GEOMETRY_CB, 0, geometryCB); + set->updateBuffer(HEAP_ID_BUFFER, 0, mesh.heapIDBuffer); + set->updateBuffer(INDIRECT_DRAW_BUFFER, 0, mesh.indirectDrawBuffer); + set->updateBuffer(BISECTOR_INDICES, 0, mesh.indexedBisectorBuffer); + set->updateBuffer(BISECTOR_DATA_BUFFER, 0, mesh.updateBuffer); + set->updateBuffer(NEIGHBOURS_BUFFER, 0, mesh.neighborsBuffers[mesh.currentNeighborsBufferIdx]); + set->updateBuffer(VISIBLE_BISECTOR_INDICES, 0, mesh.visibleIndexedBisectorBuffer); + set->updateBuffer(MODIFIED_BISECTOR_INDICES, 0, mesh.modifiedIndexedBisectorBuffer); + set->writeChanges(); + Gfx::OComputeCommand bisectorIndexationCmd = graphics->createComputeCommand("BisectorIndexation"); + bisectorIndexationCmd->bindPipeline(bisectorIndexation); + bisectorIndexationCmd->bindDescriptor(set); + bisectorIndexationCmd->dispatch(numGroups, 1, 1); + graphics->executeCommands(std::move(bisectorIndexationCmd)); + } + mesh.indirectDrawBuffer->pipelineBarrier(Gfx::SE_ACCESS_SHADER_WRITE_BIT, Gfx::SE_PIPELINE_STAGE_COMPUTE_SHADER_BIT, + Gfx::SE_ACCESS_SHADER_READ_BIT, Gfx::SE_PIPELINE_STAGE_COMPUTE_SHADER_BIT); + // Prepare bisector indirect dispatch + { + Gfx::PDescriptorSet set = layout->allocateDescriptorSet(); + set->updateBuffer(INDIRECT_DRAW_BUFFER, 0, mesh.indirectDrawBuffer); + set->updateBuffer(INDIRECT_DISPATCH_BUFFER, 0, mesh.indirectDispatchBuffer); + set->writeChanges(); + + Gfx::OComputeCommand prepareBisectorIndirectCmd = graphics->createComputeCommand("PrepareBisectorIndirect"); + prepareBisectorIndirectCmd->bindPipeline(prepareBisectorIndirect); + prepareBisectorIndirectCmd->bindDescriptor(set); + prepareBisectorIndirectCmd->dispatch(1, 1, 1); + graphics->executeCommands(std::move(prepareBisectorIndirectCmd)); + mesh.indirectDispatchBuffer->pipelineBarrier(Gfx::SE_ACCESS_SHADER_WRITE_BIT, Gfx::SE_PIPELINE_STAGE_COMPUTE_SHADER_BIT, + Gfx::SE_ACCESS_SHADER_READ_BIT, Gfx::SE_PIPELINE_STAGE_COMPUTE_SHADER_BIT); + } +} + +bool MeshUpdater::checkIfValid() { return true; } + +void MeshUpdater::queryOccupancy(const CBTMesh& mesh) {} + +uint32 MeshUpdater::getOccupancy() { return 0; } diff --git a/src/Engine/Graphics/CBT/CBT.h b/src/Engine/Graphics/CBT/CBT.h new file mode 100644 index 0000000..7740084 --- /dev/null +++ b/src/Engine/Graphics/CBT/CBT.h @@ -0,0 +1,505 @@ +#pragma once +#include "Containers/Array.h" +#include "Graphics/RenderPass/RenderPass.h" +#include "MinimalEngine.h" +#include + +namespace Seele { +constexpr auto GEOMETRY_CB = 0; +constexpr auto UPDATE_CB = 1; +constexpr auto CURRENT_VERTEX_BUFFER = 2; +constexpr auto INDEXED_BISECTOR_BUFFER = 3; +constexpr auto INDIRECT_DRAW_BUFFER = 4; +constexpr auto HEAP_ID_BUFFER = 5; +constexpr auto BISECTOR_DATA_BUFFER = 6; +constexpr auto CLASSIFICATION_BUFFER = 7; +constexpr auto ALLOCATE_BUFFER = 8; +constexpr auto INDIRECT_DISPATCH_BUFFER = 9; +constexpr auto NEIGHBOURS_BUFFER = 10; +constexpr auto NEIGHBOURS_OUTPUT_BUFFER = 11; +constexpr auto MEMORY_BUFFER = 12; +constexpr auto CBT_BUFFER0 = 13; +constexpr auto CBT_BUFFER1 = 14; +constexpr auto PROPAGATE_BUFFER = 15; +constexpr auto SIMPLIFICATION_BUFFER = 16; +constexpr auto VALIDATION_BUFFER = 17; +constexpr auto BISECTOR_INDICES = 18; +constexpr auto VISIBLE_BISECTOR_INDICES = 19; +constexpr auto MODIFIED_BISECTOR_INDICES = 20; +constexpr auto LEB_POSITION_BUFFER = 21; +constexpr auto LEB_MATRIX_CACHE = 22; + +constexpr uint64 WORKGROUP_SIZE = 64; +template class CBT { + private: + struct OCBTTree { + uint64 numElements = 0; + uint64 treeSizeBits = 0; + uint64 numSlots = 0; + uint64 bitFieldNumSlots = 0; + uint64 lastLevelSize = 0; + uint64 lastLevel = 0; + uint64 firstVirtualLevel = 0; + uint64 leafLevel = 0; + StaticArray depthOffset; + StaticArray bitMask; + StaticArray bitCount; + }; + OCBTTree tree; + constexpr static OCBTTree generateVirtualLevel(OCBTTree tree, uint64 index, uint64 bits) { + tree.depthOffset[index] = 0; + tree.bitCount[index] = bits; + uint64 bitmask = 0; + for (uint32 i = 0; i < bits; ++i) { + bitmask |= 1ull << i; + } + tree.bitMask[index] = bitmask; + if (bits == 1) { + tree.numSlots = tree.treeSizeBits / 32; + tree.bitFieldNumSlots = tree.numElements / 64; + return tree; + } + return generateVirtualLevel(tree, index++, bits / 2); + } + constexpr static OCBTTree generateLevel(OCBTTree tree, uint64 remaining, uint64 index, uint64 levelElements, uint64 levelDimensions) { + if (remaining == 7) { + tree.depthOffset[index] = tree.treeSizeBits; + tree.bitMask[index] = (1ull << 8) - 1; + tree.bitCount[index] = 8; + tree.treeSizeBits += levelDimensions * 8; + tree.lastLevelSize = levelDimensions; + tree.firstVirtualLevel = std::bit_width(levelDimensions); + tree.lastLevel = tree.firstVirtualLevel - 1; + return generateVirtualLevel(tree, index++, 64); + } + uint32 levelBits; + if (levelDimensions < 128) { + levelBits = 32; + } else { + levelBits = 16; + } + tree.depthOffset[index] = tree.treeSizeBits; + tree.bitMask[index] = (1ull << levelBits) - 1; + tree.bitCount[index] = levelBits; + tree.treeSizeBits += levelDimensions * levelBits; + return generateLevel(tree, --remaining, index++, levelElements / 2, levelDimensions * 2); + } + constexpr static OCBTTree createTree(uint64 power) { + uint64 numElements = (1ull << (power - 1)); + return generateLevel( + CBT::OCBTTree{ + .numElements = numElements, + .leafLevel = power - 1, + }, + power - 1, 0, numElements, 1); + } + + public: + constexpr CBT() : tree(createTree(Power)) { + rawMemory = new uint32[tree.numSlots + tree.numElements / 32]; + packedHeap = rawMemory; + bitfield = (uint64*)(rawMemory + tree.numSlots); + clear(); + } + ~CBT() {} + + uint32 numElements() { return tree.numElements; } + + uint32 lastLevelSize() { return tree.lastLevelSize; } + + uint32 maxDepth() { return Power; } + + uint32 numInternalBuffers() { return 2; } + + char* rawBuffer(uint32 bufferIdx = 0) { return bufferIdx == 0 ? (char*)packedHeap : (char*)bitfield; } + const char* rawBuffer(uint32 bufferIdx = 0) const { return bufferIdx == 0 ? (const char*)packedHeap : (const char*)bitfield; } + + uint32 bufferSize(uint32 bufferIdx = 0) const { return bufferIdx == 0 ? treeMemoryFootprint() : bitFieldMemoryFootprint(); } + uint32 elementSize(uint32 bufferIdx) const { return bufferIdx == 0 ? sizeof(uint32) : sizeof(uint64); } + + uint32 memoryFootPrint() const { return treeMemoryFootprint() + bitFieldMemoryFootprint(); } + uint32 treeMemoryFootprint() const { return tree.numSlots * sizeof(uint32); } + uint32 bitFieldMemoryFootprint() const { return (tree.numElements / 64) * sizeof(uint64); } + + void setBit(uint32 bitID, bool state) { + // Coordinates of the bit + uint32_t slot = bitID / 64; + uint32_t local_id = bitID % 64; + + if (state) + bitfield[slot] |= 1ull << local_id; + else + bitfield[slot] &= ~(1ull << local_id); + } + uint32 getBit(uint32 bitID) const { + uint32_t slot = bitID / 64; + uint32_t local_id = bitID % 64; + return (bitfield[slot] & (1ull << local_id)) >> local_id; + } + + uint32 bitcount() const { return packedHeap[0]; } + uint32 bitCount(uint32 depth, uint32 element) { return getHeapElement((1 << depth) + element); } + + uint32 decodeBit(uint32 handle) const { + uint32_t currentDepth = 0; + uint32_t heapElementID = 1u; + for (currentDepth = 0; currentDepth < tree.firstVirtualLevel; ++currentDepth) { + // Read the left element + uint32_t heapValue = getHeapElement(2u * heapElementID); + + // Does it fall in the right or left subtree? + uint32_t b = handle < heapValue ? 0u : 1u; + + // Pick a subtree + heapElementID = 2u * heapElementID + b; + + // Move the iterator to exclude the right subtree if required + handle -= heapValue * b; + } + + // Align with the internal depth + currentDepth++; + + // Ok we have our subtree, now we need to pick the right bit + uint64_t heapValue = bitfield[heapElementID - tree.lastLevel * 2]; + + for (; currentDepth < (tree.leafLevel + 1); ++currentDepth) { + // Figure out the location of the first bit of this element + uint32_t real_heap_id = 2 * heapElementID - 1; + uint32_t level_first_element = (1 << currentDepth) - 1; + uint32_t id_in_level = real_heap_id - level_first_element; + uint32_t first_bit = tree.depthOffset[currentDepth] + tree.bitCount[currentDepth] * id_in_level; + uint32_t local_id = first_bit % 64; + uint64_t target_bits = (heapValue >> local_id) & tree.bitMask[currentDepth]; + uint32_t heapValue = std::popcount(target_bits); + + // Does it fall in the right or left subtree? + uint32_t b = handle < heapValue ? 0u : 1u; + + // Pick a subtree + heapElementID = 2u * heapElementID + b; + + // Move the iterator to exclude the right subtree if required + handle -= heapValue * b; + } + return (heapElementID ^ tree.numElements); + } + uint32 decodeBitComplement(uint32 handle) const { + uint32_t heapElementID = 1u; + uint32_t c = tree.numElements / 2u; + uint32_t currentDepth = 0; + + for (currentDepth = 0; currentDepth < tree.firstVirtualLevel; ++currentDepth) { + uint32_t heapValue = c - getHeapElement(2u * heapElementID); + uint32_t b = handle < heapValue ? 0u : 1u; + + heapElementID = 2u * heapElementID + b; + handle -= heapValue * b; + c /= 2u; + } + + // Align with the internal depth + currentDepth++; + + // Ok we have our subtree, now we need to pick the right bit + uint64_t heapValue = bitfield[heapElementID - tree.lastLevelSize * 2]; + + for (; currentDepth < (tree.leafLevel + 1); ++currentDepth) { + // Figure out the location of the first bit of this element + uint32_t real_heap_id = 2 * heapElementID - 1; + uint32_t level_first_element = (1 << currentDepth) - 1; + uint32_t id_in_level = real_heap_id - level_first_element; + uint32_t first_bit = tree.depthOffset[currentDepth] + tree.bitCount[currentDepth] * id_in_level; + uint32_t local_id = first_bit % 64; + uint64_t target_bits = (heapValue >> local_id) & tree.bitMask[currentDepth]; + uint32_t heapValue = c - countbits(target_bits); + + uint32_t b = handle < heapValue ? 0u : 1u; + + heapElementID = 2u * heapElementID + b; + handle -= heapValue * b; + c /= 2u; + } + + return (heapElementID ^ tree.numElements); + } + + uint32 getHeapElement(uint32 id) const { + // Figure out the location of the first bit of this element + uint32_t real_heap_id = id - 1; + uint32_t depth = uint32_t(log2(real_heap_id + 1)); + uint32_t level_first_element = (1 << depth) - 1; + uint32_t id_in_level = real_heap_id - level_first_element; + uint32_t first_bit = tree.depthOffset[depth] + tree.bitCount[depth] * id_in_level; + if (depth < tree.firstVirtualLevel) { + uint32_t slot = first_bit / 32; + uint32_t local_id = first_bit % 32; + uint32_t target_bits = (packedHeap[slot] >> local_id) & tree.bitMask[depth]; + return (packedHeap[slot] >> local_id) & tree.bitMask[depth]; + } else { + uint32_t slot = first_bit / 64; + uint32_t local_id = first_bit % 64; + uint64_t target_bits = (bitfield[slot] >> local_id) & tree.bitMask[depth]; + return std::popcount(target_bits); + } + } + uint32 setHeapElement(uint32 id, uint32 value) { + // Figure out the location of the first bit of this element + uint32_t real_heap_id = id - 1; + uint32_t depth = uint32_t(log2(real_heap_id + 1)); + uint32_t level_first_element = (1 << depth) - 1; + uint32_t id_in_level = real_heap_id - level_first_element; + + // If this is the tree representation + if (depth < tree.firstVirtualLevel) { + // Find the slot and the local first bit + uint32_t first_bit = tree.depthOffset[depth] + tree.bitCount[depth] * id_in_level; + uint32_t slot = first_bit / 32; + uint32_t local_id = first_bit % 32; + + // Extract the relevant bits + uint32_t& target = packedHeap[slot]; + target &= ~(tree.bitMask[depth] << (local_id)); + target |= (tree.bitMask[depth] & value) << (local_id); + } + // Should be avoided, but is supported + else if (depth == tree.leafLevel) { + setBit(id_in_level, value); + } + // Doesn't make sense + else { + assert(false); + } + } + + void reduce() { + // First reduce the last level using countbits + for (uint32_t threadID = 0; threadID < (tree.lastLevelSize / 4); ++threadID) { + // Initialize the packed sum + uint32_t packedSum = 0; + + // Loop through the 2 pairs to process + for (uint32_t pairIdx = 0; pairIdx < 4; ++pairIdx) { + // First element of the pair + uint32_t elementC = std::popcount(bitfield[threadID * 8 + 2 * pairIdx]); + + // Second element of the pair + elementC += std::popcount(bitfield[threadID * 8 + 2 * pairIdx + 1]); + + // Store in the right bits + packedSum |= (elementC << pairIdx * 8); + } + + // Offset of the last level of the tree + const uint32_t bufferOffset = tree.depthOffset[11] / 32; + + // Store the result into the bitfield + packedHeap[bufferOffset + threadID] = packedSum; + } + + // Then operate the reduction on the tree only (not the bitfield) + for (uint32_t size = tree.numElements / 128u; size > 0u; size /= 2u) { + uint32_t minHeapID = size; + uint32_t maxHeapID = size * 2u; + + for (uint32_t heapID = minHeapID; heapID < maxHeapID; ++heapID) { + uint32_t value = getHeapElement(2u * heapID) + getHeapElement(2u * heapID + 1u); + setHeapElement(heapID, value); + } + } + } + void clear() { + memset(packedHeap, 0, tree.numSlots * sizeof(uint32_t)); + memset(bitfield, 0, tree.bitFieldNumSlots * sizeof(uint64_t)); + } + + private: + uint32* rawMemory; + uint32* packedHeap; + uint64* bitfield; +}; + +struct GeometryCB { + uint32 totalNumElements; + uint32 baseDepth; + uint32 totalNumVertices; +}; +struct UpdateCB { + float triangleSize; + uint32_t maxSubdivisionDepth; + float fov; + float farPlaneDistance; +}; +struct CPUMesh { + uint32 totalNumElements = 0; + + uint32 minimalDepth = 0; + + Array heapIDArray; + Array neighborsArray; + + Array basePoints; +}; +CPUMesh generateCPUMesh(uint32 cbtNumElements); +// Pointer to an invalid neighbor or index +constexpr uint64 INVALID_POINTER = UINT32_MAX; + +// Possible culling state +constexpr int64 BACK_FACE_CULLED = -3; +constexpr int64 FRUSTUM_CULLED = -2; +constexpr int64 TOO_SMALL = -1; +constexpr int64 UNCHANGED_ELEMENT = 0; +constexpr int64 BISECT_ELEMENT = 1; +constexpr int64 SIMPLIFY_ELEMENT = 2; +constexpr int64 MERGED_ELEMENT = 3; +struct BisectorData { + uint32 subdivisionPattern; + + UVector indices; + + uint32 problematicNeighbor; + + uint32 bisectorState; + + uint32 flags; + + uint32 propagationID; +}; +struct LebMatrixCache { + void init(Gfx::PGraphics graphics, uint32 cacheDepth); + void release(); + + Gfx::PShaderBuffer getLebMatrixBuffer() const { return lebMatrixBuffer; } + + private: + Gfx::OShaderBuffer lebMatrixBuffer; + uint32 cacheDepth; +}; +struct GPU_CBT { + uint32 numElements; + + uint32 lastLevelSize = 0; + + uint32 bufferCount = 2; + + Gfx::OShaderBuffer bufferArray[2]; +}; +struct BaseMesh { + uint32 numVertices; + + Gfx::OShaderBuffer vertexBuffer; + + uint32 numElements; + + Gfx::OIndexBuffer indexBuffer; +}; +struct CBTMesh { + uint32 totalNumElements; + + uint32 numBaseVertices; + + uint32 baseDepth; + + Gfx::OShaderBuffer heapIDBuffer; + uint32 currentNeighborsBufferIdx; + + Gfx::OShaderBuffer neighborsBuffers[2]; + + Gfx::OShaderBuffer updateBuffer; + Gfx::OShaderBuffer classificationBuffer; + Gfx::OShaderBuffer simplificationBuffer; + Gfx::OShaderBuffer allocateBuffer; + Gfx::OShaderBuffer propagateBuffer; + + Gfx::OShaderBuffer indirectDrawBuffer; + Gfx::OShaderBuffer indirectDispatchBuffer; + Gfx::OShaderBuffer indexedBisectorBuffer; + Gfx::OShaderBuffer visibleIndexedBisectorBuffer; + Gfx::OShaderBuffer modifiedIndexedBisectorBuffer; + + Gfx::OShaderBuffer lebVertexBuffer; + Gfx::OShaderBuffer currentVertexBuffer; + Gfx::OShaderBuffer currentDisplacementBuffer; + + GPU_CBT gpuCBT; +}; +struct MeshUpdater { + void init(Gfx::PGraphics gfx, Gfx::PDescriptorLayout viewParamsLayout); + void release(); + + void evaluateLeb(const BaseMesh& baseMesh, CBTMesh& mesh, Gfx::PDescriptorSet viewParamsSet, + Gfx::PUniformBuffer geometryBuffer, Gfx::PUniformBuffer updateBuffer, Gfx::PShaderBuffer lebMatrixCache, bool clear, + bool complete); + + void update(CBTMesh& mesh, Gfx::PDescriptorSet viewParamsSet, Gfx::PUniformBuffer geometryCB, Gfx::PUniformBuffer updateCB); + + void validation(const CBTMesh& mesh, Gfx::PUniformBuffer geometryCB); + + void resetBuffers(const CBTMesh& mesh); + + void prepareIndirection(CBTMesh& mesh, Gfx::PUniformBuffer geometryCB); + + bool checkIfValid(); + + void queryOccupancy(const CBTMesh& mesh); + + uint32 getOccupancy(); + + Gfx::PGraphics graphics; + + Gfx::ODescriptorLayout layout; + Gfx::OPipelineLayout pipelineLayout; + + Gfx::OShaderBuffer indirectBuffer; + Gfx::OShaderBuffer memoryBuffer; + Gfx::OShaderBuffer validationBuffer; + Gfx::OShaderBuffer validationBufferRB; + Gfx::OShaderBuffer occupancyBufferRB; + + // Main update + Gfx::OComputeShader resetCS; + Gfx::PComputePipeline reset; + Gfx::OComputeShader classifyCS; + Gfx::PComputePipeline classify; + Gfx::OComputeShader splitCS; + Gfx::PComputePipeline split; + Gfx::OComputeShader prepareIndirectCS; + Gfx::PComputePipeline prepareIndirect; + Gfx::OComputeShader allocateCS; + Gfx::PComputePipeline allocate; + Gfx::OComputeShader bisectCS; + Gfx::PComputePipeline bisect; + Gfx::OComputeShader propagateBisectCS; + Gfx::PComputePipeline propagateBisect; + Gfx::OComputeShader prepareSimplifyCS; + Gfx::PComputePipeline prepareSimplify; + Gfx::OComputeShader simplifyCS; + Gfx::PComputePipeline simplify; + Gfx::OComputeShader propagateSimplifyCS; + Gfx::PComputePipeline propagateSimplify; + + // Reduction + Gfx::OComputeShader reducePrePassCS; + Gfx::PComputePipeline reducePrePass; + Gfx::OComputeShader reduceFirstPassCS; + Gfx::PComputePipeline reduceFirstPass; + Gfx::OComputeShader reduceSecondPassCS; + Gfx::PComputePipeline reduceSecondPass; + + // Indexation + Gfx::OComputeShader bisectorIndexationCS; + Gfx::PComputePipeline bisectorIndexation; + Gfx::OComputeShader prepareBisectorIndirectCS; + Gfx::PComputePipeline prepareBisectorIndirect; + + // Debug + Gfx::OComputeShader validateCS; + Gfx::PComputePipeline validate; + + // LEB + Gfx::OComputeShader lebClearCS; + Gfx::PComputePipeline lebClear; + Gfx::OComputeShader lebEvaluateCS; + Gfx::PComputePipeline lebEvaluate; +}; +}; // namespace Seele \ No newline at end of file diff --git a/src/Engine/Graphics/CBT/CMakeLists.txt b/src/Engine/Graphics/CBT/CMakeLists.txt new file mode 100644 index 0000000..5ed5fb0 --- /dev/null +++ b/src/Engine/Graphics/CBT/CMakeLists.txt @@ -0,0 +1,11 @@ +target_sources(Engine + PRIVATE + CBT.h + CBT.cpp +) + +target_sources(Engine + PUBLIC FILE_SET HEADERS + FILES + CBT.h +) \ No newline at end of file diff --git a/src/Engine/Graphics/CMakeLists.txt b/src/Engine/Graphics/CMakeLists.txt index 78a7039..c619c3d 100644 --- a/src/Engine/Graphics/CMakeLists.txt +++ b/src/Engine/Graphics/CMakeLists.txt @@ -1,3 +1,5 @@ +add_subdirectory(CBT/) + target_sources(Engine PRIVATE Buffer.h diff --git a/src/Engine/Graphics/Command.h b/src/Engine/Graphics/Command.h index 631a77d..f363fa1 100644 --- a/src/Engine/Graphics/Command.h +++ b/src/Engine/Graphics/Command.h @@ -19,6 +19,7 @@ class RenderCommand { virtual void bindIndexBuffer(Gfx::PIndexBuffer indexBuffer) = 0; virtual void pushConstants(Gfx::SeShaderStageFlags stage, uint32 offset, uint32 size, const void* data) = 0; virtual void draw(uint32 vertexCount, uint32 instanceCount, int32 firstVertex, uint32 firstInstance) = 0; + virtual void drawIndirect(Gfx::PShaderBuffer buffer, uint64 offset, uint32 drawCount, uint32 stride) = 0; virtual void drawIndexed(uint32 indexCount, uint32 instanceCount, int32 firstIndex, uint32 vertexOffset, uint32 firstInstance) = 0; virtual void drawMesh(uint32 groupX, uint32 groupY, uint32 groupZ) = 0; virtual void drawMeshIndirect(Gfx::PShaderBuffer buffer, uint64 offset, uint32 drawCount, uint32 stride) = 0; @@ -35,6 +36,7 @@ class ComputeCommand { virtual void bindDescriptor(const Array& sets) = 0; virtual void pushConstants(Gfx::SeShaderStageFlags stage, uint32 offset, uint32 size, const void* data) = 0; virtual void dispatch(uint32 threadX, uint32 threadY, uint32 threadZ) = 0; + virtual void dispatchIndirect(Gfx::PShaderBuffer buffer, uint32 offset) = 0; std::string name; }; DEFINE_REF(ComputeCommand) diff --git a/src/Engine/Graphics/Descriptor.h b/src/Engine/Graphics/Descriptor.h index 20a5942..18c03f2 100644 --- a/src/Engine/Graphics/Descriptor.h +++ b/src/Engine/Graphics/Descriptor.h @@ -65,6 +65,7 @@ class DescriptorSet { virtual void writeChanges() = 0; virtual void updateBuffer(uint32 binding, uint32 index, Gfx::PUniformBuffer uniformBuffer) = 0; virtual void updateBuffer(uint32 binding, uint32 index, Gfx::PShaderBuffer shaderBuffer) = 0; + virtual void updateBuffer(uint32 binding, uint32 index, Gfx::PVertexBuffer indexBuffer) = 0; virtual void updateBuffer(uint32 binding, uint32 index, Gfx::PIndexBuffer indexBuffer) = 0; virtual void updateSampler(uint32 binding, uint32 index, Gfx::PSampler samplerState) = 0; virtual void updateTexture(uint32 binding, uint32 index, Gfx::PTexture2D texture) = 0; diff --git a/src/Engine/Graphics/Graphics.h b/src/Engine/Graphics/Graphics.h index efd9b86..f0ab1b6 100644 --- a/src/Engine/Graphics/Graphics.h +++ b/src/Engine/Graphics/Graphics.h @@ -100,6 +100,8 @@ class Graphics { virtual void resolveTexture(PTexture source, PTexture destination) = 0; virtual void copyTexture(PTexture src, PTexture dst) = 0; + virtual void copyBuffer(Gfx::PShaderBuffer src, Gfx::PShaderBuffer dst) = 0; + bool supportMeshShading() const { return meshShadingEnabled; } // Ray Tracing diff --git a/src/Engine/Graphics/Initializer.h b/src/Engine/Graphics/Initializer.h index ccea5b8..4aa34db 100644 --- a/src/Engine/Graphics/Initializer.h +++ b/src/Engine/Graphics/Initializer.h @@ -98,7 +98,7 @@ struct ShaderBufferCreateInfo { uint64 numElements = 1; uint32 clearValue = 0; uint8 createCleared = 0; - uint8 vertexBuffer = 0; + Gfx::SeBufferUsageFlags usage = 0; std::string name = "Unnamed"; }; DECLARE_NAME_REF(Gfx, PipelineLayout) diff --git a/src/Engine/Graphics/RenderPass/BasePass.cpp b/src/Engine/Graphics/RenderPass/BasePass.cpp index dc24ea0..ae28c78 100644 --- a/src/Engine/Graphics/RenderPass/BasePass.cpp +++ b/src/Engine/Graphics/RenderPass/BasePass.cpp @@ -27,7 +27,6 @@ void Seele::addDebugVertices(Array verts) { gDebugVertices.addAll(v BasePass::BasePass(Gfx::PGraphics graphics, PScene scene) : RenderPass(graphics, scene) { //waterRenderer = new WaterRenderer(graphics, scene, viewParamsLayout); - //terrainRenderer = new TerrainRenderer(graphics, scene, viewParamsLayout); basePassLayout = graphics->createPipelineLayout("BasePassLayout"); basePassLayout->addDescriptorLayout(viewParamsLayout); @@ -100,7 +99,7 @@ void BasePass::beginFrame(const Component::Camera& cam) { transparentCulling = lightCullingLayout->allocateDescriptorSet(); //waterRenderer->beginFrame(); - //terrainRenderer->beginFrame(); + terrainRenderer->beginFrame(viewParamsSet); // Debug vertices { @@ -249,8 +248,8 @@ void BasePass::render() { } } - // commands.add(waterRenderer->render(viewParamsSet)); - // commands.add(terrainRenderer->render(viewParamsSet)); + //commands.add(waterRenderer->render(viewParamsSet)); + commands.add(terrainRenderer->render(viewParamsSet)); // Skybox { @@ -432,6 +431,8 @@ void BasePass::publishOutputs() { } void BasePass::createRenderPass() { + RenderPass::beginFrame(Component::Camera()); + terrainRenderer = new TerrainRenderer(graphics, scene, viewParamsLayout, viewParamsSet); cullingBuffer = resources->requestBuffer("CULLINGBUFFER"); timestamps = resources->requestTimestampQuery("TIMESTAMPS"); @@ -474,7 +475,7 @@ void BasePass::createRenderPass() { tLightGrid = resources->requestTexture("LIGHTCULLING_TLIGHTGRID"); //waterRenderer->setViewport(viewport, renderPass); - //terrainRenderer->setViewport(viewport, renderPass); + terrainRenderer->setViewport(viewport, renderPass); // Debug rendering { diff --git a/src/Engine/Graphics/RenderPass/BasePass.h b/src/Engine/Graphics/RenderPass/BasePass.h index fe60d17..b0a5b65 100644 --- a/src/Engine/Graphics/RenderPass/BasePass.h +++ b/src/Engine/Graphics/RenderPass/BasePass.h @@ -51,7 +51,7 @@ class BasePass : public RenderPass { Gfx::PShaderBuffer cullingBuffer; //OWaterRenderer waterRenderer; - //OTerrainRenderer terrainRenderer; + OTerrainRenderer terrainRenderer; // Debug rendering Gfx::OVertexInput debugVertexInput; diff --git a/src/Engine/Graphics/RenderPass/LightCullingPass.h b/src/Engine/Graphics/RenderPass/LightCullingPass.h index 191c38f..d6f2a71 100644 --- a/src/Engine/Graphics/RenderPass/LightCullingPass.h +++ b/src/Engine/Graphics/RenderPass/LightCullingPass.h @@ -31,13 +31,6 @@ class LightCullingPass : public RenderPass { glm::uvec3 numThreads; uint32_t pad1; } dispatchParams; - struct Plane { - Vector n; - float d; - }; - struct Frustum { - Plane planes[4]; - }; Gfx::OShaderBuffer frustumBuffer; Gfx::OUniformBuffer dispatchParamsBuffer; diff --git a/src/Engine/Graphics/RenderPass/RenderPass.cpp b/src/Engine/Graphics/RenderPass/RenderPass.cpp index 2569f9a..aa855b4 100644 --- a/src/Engine/Graphics/RenderPass/RenderPass.cpp +++ b/src/Engine/Graphics/RenderPass/RenderPass.cpp @@ -23,13 +23,18 @@ RenderPass::RenderPass(Gfx::PGraphics graphics, PScene scene) : graphics(graphic RenderPass::~RenderPass() {} void RenderPass::beginFrame(const Component::Camera& cam) { + auto screenDim = Vector2(static_cast(viewport->getWidth()), static_cast(viewport->getHeight())); viewParams = { .viewMatrix = cam.getViewMatrix(), .inverseViewMatrix = glm::inverse(cam.getViewMatrix()), .projectionMatrix = viewport->getProjectionMatrix(), .inverseProjection = glm::inverse(viewport->getProjectionMatrix()), - .cameraPosition = Vector4(cam.getCameraPosition(), 1), - .screenDimensions = Vector2(static_cast(viewport->getWidth()), static_cast(viewport->getHeight())), + .cameraPosition_WS = Vector4(cam.getCameraPosition(), 1), + .cameraForward_WS = Vector4(cam.getCameraForward(), 1), + .screenDimensions = screenDim, + .invScreenDimensions = 1.0f / screenDim, + .frameIndex = Gfx::getCurrentFrameIndex(), + .time = static_cast(Gfx::getCurrentFrameTime()), }; viewParamsBuffer->rotateBuffer(sizeof(ViewParameter)); viewParamsBuffer->updateContents(0, sizeof(ViewParameter), &viewParams); diff --git a/src/Engine/Graphics/RenderPass/RenderPass.h b/src/Engine/Graphics/RenderPass/RenderPass.h index 5e6140c..cde9968 100644 --- a/src/Engine/Graphics/RenderPass/RenderPass.h +++ b/src/Engine/Graphics/RenderPass/RenderPass.h @@ -27,13 +27,25 @@ class RenderPass { void setViewport(Gfx::PViewport _viewport); protected: + struct Plane { + Vector n; + float d; + }; + struct Frustum { + Plane planes[4]; + }; struct ViewParameter { + Frustum viewFrustum; Matrix4 viewMatrix; Matrix4 inverseViewMatrix; Matrix4 projectionMatrix; Matrix4 inverseProjection; - Vector4 cameraPosition; + Vector4 cameraPosition_WS; + Vector4 cameraForward_WS; Vector2 screenDimensions; + Vector2 invScreenDimensions; + uint32 frameIndex; + float time; } viewParams; PRenderGraphResources resources; Gfx::ODescriptorLayout viewParamsLayout; diff --git a/src/Engine/Graphics/RenderPass/TerrainRenderer.cpp b/src/Engine/Graphics/RenderPass/TerrainRenderer.cpp index d94266c..c95aa68 100644 --- a/src/Engine/Graphics/RenderPass/TerrainRenderer.cpp +++ b/src/Engine/Graphics/RenderPass/TerrainRenderer.cpp @@ -2,111 +2,298 @@ #include "Asset/AssetRegistry.h" #include "Component/TerrainTile.h" #include "Graphics/Graphics.h" +#include "Graphics/Pipeline.h" #include "Graphics/Shader.h" using namespace Seele; -TerrainRenderer::TerrainRenderer(Gfx::PGraphics graphics, PScene scene, Gfx::PDescriptorLayout viewParamsLayout) +TerrainRenderer::TerrainRenderer(Gfx::PGraphics graphics, PScene scene, Gfx::PDescriptorLayout viewParamsLayout, + Gfx::PDescriptorSet viewParamsSet) : graphics(graphics), scene(scene) { - struct TerrainTile { - IVector2 offset; - float extent; - float height; - }; - Array payloads; - for (int32 y = -100; y < 100; ++y) { - for (int32 x = -100; x < 100; ++x) { - payloads.add(TerrainTile{ - .offset = IVector2(x, y), - .extent = Component::TerrainTile::DIMENSIONS, - .height = 0, - }); - } + meshUpdater.init(graphics, viewParamsLayout); + lebCache.init(graphics, 5); + CBT<21> cbt; + CPUMesh cpuMesh = generateCPUMesh(cbt.numElements()); + plainMesh.gpuCBT.lastLevelSize = cbt.lastLevelSize(); + for (uint32 i = 0; i < 2; ++i) { + uint32 bufferSize = cbt.bufferSize(i); + uint32 elementSize = cbt.elementSize(i); + plainMesh.gpuCBT.bufferArray[i] = graphics->createShaderBuffer(ShaderBufferCreateInfo{ + .sourceData = + { + .size = bufferSize, + .data = (uint8*)cbt.rawBuffer(i), + }, + .name = "GPUCBT", + }); + plainMesh.gpuCBT.bufferArray[i]->pipelineBarrier(Gfx::SE_ACCESS_TRANSFER_WRITE_BIT, Gfx::SE_PIPELINE_STAGE_TRANSFER_BIT, + Gfx::SE_ACCESS_SHADER_READ_BIT | Gfx::SE_ACCESS_SHADER_WRITE_BIT, + Gfx::SE_PIPELINE_STAGE_COMPUTE_SHADER_BIT); } - tilesBuffer = graphics->createShaderBuffer(ShaderBufferCreateInfo{ + plainMesh.totalNumElements = cpuMesh.totalNumElements; + plainMesh.numBaseVertices = (uint32)cpuMesh.basePoints.size(); + plainMesh.baseDepth = cpuMesh.minimalDepth; + + plainMesh.heapIDBuffer = graphics->createShaderBuffer(ShaderBufferCreateInfo{ .sourceData = { - .size = sizeof(TerrainTile) * payloads.size(), - .data = (uint8*)payloads.data(), + .size = sizeof(uint64) * cpuMesh.totalNumElements, + .data = (uint8*)cpuMesh.heapIDArray.data(), }, - .numElements = payloads.size(), - .name = "TilesBuffer", + .name = "HeapID", }); - tilesBuffer->pipelineBarrier(Gfx::SE_ACCESS_TRANSFER_WRITE_BIT, Gfx::SE_PIPELINE_STAGE_TRANSFER_BIT, Gfx::SE_ACCESS_SHADER_READ_BIT, - Gfx::SE_PIPELINE_STAGE_TASK_SHADER_BIT_EXT); - colorMap = AssetRegistry::findTexture("", "azeroth")->getTexture(); - displacementMap = AssetRegistry::findTexture("", "azeroth_height")->getTexture(); + plainMesh.heapIDBuffer->pipelineBarrier(Gfx::SE_ACCESS_TRANSFER_WRITE_BIT, Gfx::SE_PIPELINE_STAGE_TRANSFER_BIT, + Gfx::SE_ACCESS_SHADER_READ_BIT | Gfx::SE_ACCESS_SHADER_WRITE_BIT, + Gfx::SE_PIPELINE_STAGE_COMPUTE_SHADER_BIT); - sampler = graphics->createSampler(SamplerCreateInfo{}); - layout = graphics->createDescriptorLayout("pTerrainData"); - layout->addDescriptorBinding(Gfx::DescriptorBinding{ - .binding = 0, - .descriptorType = Gfx::SE_DESCRIPTOR_TYPE_STORAGE_BUFFER, + plainMesh.currentNeighborsBufferIdx = 0; + plainMesh.neighborsBuffers[0] = graphics->createShaderBuffer(ShaderBufferCreateInfo{ + .sourceData = + { + .size = sizeof(UVector4) * cpuMesh.totalNumElements, + .data = (uint8*)cpuMesh.neighborsArray.data(), + }, + .name = "Neighbours0", }); - layout->addDescriptorBinding(Gfx::DescriptorBinding{ - .binding = 1, - .descriptorType = Gfx::SE_DESCRIPTOR_TYPE_SAMPLED_IMAGE, + plainMesh.neighborsBuffers[0]->pipelineBarrier(Gfx::SE_ACCESS_TRANSFER_WRITE_BIT, Gfx::SE_PIPELINE_STAGE_TRANSFER_BIT, + Gfx::SE_ACCESS_SHADER_READ_BIT | Gfx::SE_ACCESS_SHADER_WRITE_BIT, + Gfx::SE_PIPELINE_STAGE_COMPUTE_SHADER_BIT); + plainMesh.neighborsBuffers[1] = graphics->createShaderBuffer(ShaderBufferCreateInfo{ + .sourceData = + { + .size = sizeof(UVector4) * cpuMesh.totalNumElements, + }, + .name = "Neighbours1", }); - layout->addDescriptorBinding(Gfx::DescriptorBinding{ - .binding = 2, - .descriptorType = Gfx::SE_DESCRIPTOR_TYPE_SAMPLED_IMAGE, + plainMesh.neighborsBuffers[1]->pipelineBarrier(Gfx::SE_ACCESS_TRANSFER_WRITE_BIT, Gfx::SE_PIPELINE_STAGE_TRANSFER_BIT, + Gfx::SE_ACCESS_SHADER_READ_BIT | Gfx::SE_ACCESS_SHADER_WRITE_BIT, + Gfx::SE_PIPELINE_STAGE_COMPUTE_SHADER_BIT); + + plainMesh.updateBuffer = graphics->createShaderBuffer(ShaderBufferCreateInfo{ + .sourceData = + { + .size = sizeof(BisectorData) * cpuMesh.totalNumElements, + }, + .usage = Gfx::SE_BUFFER_USAGE_INDIRECT_BUFFER_BIT, + .name = "UpdateBuffer", }); - layout->addDescriptorBinding(Gfx::DescriptorBinding{ - .binding = 3, - .descriptorType = Gfx::SE_DESCRIPTOR_TYPE_SAMPLER, + plainMesh.classificationBuffer = graphics->createShaderBuffer(ShaderBufferCreateInfo{ + .sourceData = + { + .size = sizeof(uint32) * (2 + cpuMesh.totalNumElements * 2), + }, + .name = "Classification", }); - layout->create(); - pipelineLayout = graphics->createPipelineLayout("TerrainLayout"); - pipelineLayout->addDescriptorLayout(viewParamsLayout); - pipelineLayout->addDescriptorLayout(layout); - ShaderCompilationInfo s{ - .name = "TerrainShaders", + plainMesh.simplificationBuffer = graphics->createShaderBuffer(ShaderBufferCreateInfo{ + .sourceData = + { + .size = sizeof(uint32) * (1 + cpuMesh.totalNumElements), + }, + .name = "Simplification", + }); + plainMesh.allocateBuffer = graphics->createShaderBuffer(ShaderBufferCreateInfo{ + .sourceData = + { + .size = sizeof(uint32) * (1 + cpuMesh.totalNumElements), + }, + .name = "Allocation", + }); + plainMesh.propagateBuffer = graphics->createShaderBuffer(ShaderBufferCreateInfo{ + .sourceData = + { + .size = sizeof(uint32) * (2 + cpuMesh.totalNumElements), + }, + .name = "Propagate", + }); + + plainMesh.indirectDrawBuffer = graphics->createShaderBuffer(ShaderBufferCreateInfo{ + .sourceData = + { + .size = sizeof(uint32) * (4 * 2 + 2), + }, + .usage = Gfx::SE_BUFFER_USAGE_INDIRECT_BUFFER_BIT, + .name = "IndirectDraw", + }); + plainMesh.indirectDispatchBuffer = graphics->createShaderBuffer(ShaderBufferCreateInfo{ + .sourceData = + { + .size = sizeof(uint32) * 3 * 3, + }, + .usage = Gfx::SE_BUFFER_USAGE_INDIRECT_BUFFER_BIT, + .name = "IndirectDispatch", + }); + plainMesh.indexedBisectorBuffer = graphics->createShaderBuffer(ShaderBufferCreateInfo{ + .sourceData = + { + .size = sizeof(uint32) * cpuMesh.totalNumElements, + }, + .name = "IndexedBisector", + }); + plainMesh.visibleIndexedBisectorBuffer = graphics->createShaderBuffer(ShaderBufferCreateInfo{ + .sourceData = + { + .size = sizeof(uint32) * cpuMesh.totalNumElements, + }, + .name = "VisibleIndexedBisector", + }); + plainMesh.modifiedIndexedBisectorBuffer = graphics->createShaderBuffer(ShaderBufferCreateInfo{ + .sourceData = + { + .size = sizeof(uint32) * cpuMesh.totalNumElements, + }, + .name = "ModifiedIndexedBisector", + }); + + plainMesh.lebVertexBuffer = graphics->createShaderBuffer(ShaderBufferCreateInfo{ + .sourceData = + { + .size = sizeof(Vector4) * plainMesh.totalNumElements * 4, + }, + .name = "LebVertexBuffer", + }); + plainMesh.currentVertexBuffer = graphics->createShaderBuffer(ShaderBufferCreateInfo{ + .sourceData = + { + .size = sizeof(Vector4) * plainMesh.totalNumElements * 4, + }, + .usage = Gfx::SE_BUFFER_USAGE_INDIRECT_BUFFER_BIT, + .name = "CurrentVertexBuffer", + }); + plainMesh.currentDisplacementBuffer = graphics->createShaderBuffer(ShaderBufferCreateInfo{ + .sourceData = + { + .size = sizeof(Vector4) * plainMesh.totalNumElements * 3, + }, + .name = "CurrentDisplacementBuffer", + }); + uint32 numBaseVertex = (uint32)cpuMesh.basePoints.size(); + baseMesh.numVertices = numBaseVertex; + baseMesh.numElements = numBaseVertex / 3; + uint32 baseVertexBufferSize = sizeof(Vector4) * numBaseVertex; + baseMesh.vertexBuffer = graphics->createShaderBuffer(ShaderBufferCreateInfo{ + .sourceData = + { + .size = baseVertexBufferSize, + .data = (uint8*)cpuMesh.basePoints.data(), + }, + .name = "VertexBuffer", + }); + baseMesh.vertexBuffer->pipelineBarrier(Gfx::SE_ACCESS_TRANSFER_WRITE_BIT, Gfx::SE_PIPELINE_STAGE_TRANSFER_BIT, + Gfx::SE_ACCESS_SHADER_READ_BIT | Gfx::SE_ACCESS_SHADER_WRITE_BIT, + Gfx::SE_PIPELINE_STAGE_COMPUTE_SHADER_BIT); + uint32 indexBufferSize = cpuMesh.totalNumElements * sizeof(UVector); + Array indices; + for (uint32 i = 0; i < cpuMesh.totalNumElements * 3; ++i) + indices.add(i); + baseMesh.indexBuffer = graphics->createIndexBuffer(IndexBufferCreateInfo{ + .sourceData = + { + .size = sizeof(uint32) * indices.size(), + .data = (uint8*)indices.data(), + }, + .indexType = Gfx::SE_INDEX_TYPE_UINT32, + .name = "IndexBuffer", + }); + baseMesh.indexBuffer->pipelineBarrier(Gfx::SE_ACCESS_TRANSFER_WRITE_BIT, Gfx::SE_PIPELINE_STAGE_TRANSFER_BIT, + Gfx::SE_ACCESS_SHADER_READ_BIT | Gfx::SE_ACCESS_SHADER_WRITE_BIT, + Gfx::SE_PIPELINE_STAGE_COMPUTE_SHADER_BIT); + GeometryCB geometryCB = { + .totalNumElements = plainMesh.totalNumElements, + .baseDepth = plainMesh.baseDepth, + .totalNumVertices = plainMesh.totalNumElements * 3, + }; + geometryBuffer = graphics->createUniformBuffer(UniformBufferCreateInfo{ + .sourceData = + { + .size = sizeof(GeometryCB), + .data = (uint8*)&geometryCB, + }, + .name = "GeometryCB", + }); + geometryBuffer->pipelineBarrier(Gfx::SE_ACCESS_TRANSFER_WRITE_BIT, Gfx::SE_PIPELINE_STAGE_TRANSFER_BIT, Gfx::SE_ACCESS_UNIFORM_READ_BIT, + Gfx::SE_PIPELINE_STAGE_COMPUTE_SHADER_BIT); + UpdateCB updateCB = { + .triangleSize = 10.0f, + .maxSubdivisionDepth = 63, + .fov = 70.0f, + .farPlaneDistance = 1000.0f, + }; + updateBuffer = graphics->createUniformBuffer(UniformBufferCreateInfo{ + .sourceData = + { + .size = sizeof(UpdateCB), + .data = (uint8*)&updateCB, + }, + .name = "UpdateCB", + }); + updateBuffer->pipelineBarrier(Gfx::SE_ACCESS_TRANSFER_WRITE_BIT, Gfx::SE_PIPELINE_STAGE_TRANSFER_BIT, Gfx::SE_ACCESS_UNIFORM_READ_BIT, + Gfx::SE_PIPELINE_STAGE_COMPUTE_SHADER_BIT); + meshUpdater.resetBuffers(plainMesh); + meshUpdater.prepareIndirection(plainMesh, geometryBuffer); + meshUpdater.evaluateLeb(baseMesh, plainMesh, viewParamsSet, geometryBuffer, updateBuffer, lebCache.getLebMatrixBuffer(), true, true); + + graphics->beginShaderCompilation(ShaderCompilationInfo{ .modules = {"TerrainPass"}, .entryPoints = { - {"taskMain", "TerrainPass"}, - {"meshMain", "TerrainPass"}, - {"fragmentMain", "TerrainPass"}, + {"vert", "TerrainPass"}, + {"frag", "TerrainPass"}, + {"deform", "TerrainPass"}, }, - .rootSignature = pipelineLayout, - .dumpIntermediate = false, - }; - graphics->beginShaderCompilation(s); - task = graphics->createTaskShader({0}); - mesh = graphics->createMeshShader({1}); - frag = graphics->createFragmentShader({2}); - pipelineLayout->create(); + .rootSignature = meshUpdater.pipelineLayout, + }); + vert = graphics->createVertexShader({0}); + frag = graphics->createFragmentShader({1}); + deformCS = graphics->createComputeShader({2}); + deform = graphics->createComputePipeline(Gfx::ComputePipelineCreateInfo{ + .computeShader = deformCS, + .pipelineLayout = meshUpdater.pipelineLayout, + }); } TerrainRenderer::~TerrainRenderer() {} -void TerrainRenderer::beginFrame() { - set = layout->allocateDescriptorSet(); - set->updateBuffer(0, 0, tilesBuffer); - set->updateTexture(1, 0, displacementMap); - set->updateTexture(2, 0, colorMap); - set->updateSampler(3, 0, sampler); +void TerrainRenderer::beginFrame(Gfx::PDescriptorSet viewParamsSet) { + meshUpdater.update(plainMesh, viewParamsSet, geometryBuffer, updateBuffer); + meshUpdater.evaluateLeb(baseMesh, plainMesh, viewParamsSet, geometryBuffer, updateBuffer, lebCache.getLebMatrixBuffer(), false, false); + Gfx::PDescriptorSet set = meshUpdater.layout->allocateDescriptorSet(); + set->updateBuffer(GEOMETRY_CB, 0, geometryBuffer); + set->updateBuffer(INDIRECT_DRAW_BUFFER, 0, plainMesh.indirectDrawBuffer); + set->updateBuffer(INDEXED_BISECTOR_BUFFER, 0, plainMesh.indexedBisectorBuffer); + set->updateBuffer(LEB_POSITION_BUFFER, 0, plainMesh.lebVertexBuffer); + set->updateBuffer(CURRENT_VERTEX_BUFFER, 0, plainMesh.currentVertexBuffer); set->writeChanges(); + Gfx::OComputeCommand command = graphics->createComputeCommand("Deform"); + command->bindPipeline(deform); + command->bindDescriptor({viewParamsSet, set}); + command->dispatchIndirect(plainMesh.indirectDispatchBuffer, 3 * sizeof(uint32)); + graphics->executeCommands(std::move(command)); + plainMesh.currentVertexBuffer->pipelineBarrier(Gfx::SE_ACCESS_SHADER_WRITE_BIT, Gfx::SE_PIPELINE_STAGE_COMPUTE_SHADER_BIT, + Gfx::SE_ACCESS_SHADER_READ_BIT, Gfx::SE_PIPELINE_STAGE_VERTEX_SHADER_BIT); } Gfx::ORenderCommand TerrainRenderer::render(Gfx::PDescriptorSet viewParamsSet) { + Gfx::PDescriptorSet set = meshUpdater.layout->allocateDescriptorSet(); + set->updateBuffer(CURRENT_VERTEX_BUFFER, 0, plainMesh.currentVertexBuffer); + set->updateBuffer(INDEXED_BISECTOR_BUFFER, 0, plainMesh.indexedBisectorBuffer); + set->writeChanges(); Gfx::ORenderCommand command = graphics->createRenderCommand("TerrainRender"); command->setViewport(viewport); command->bindPipeline(pipeline); command->bindDescriptor({viewParamsSet, set}); - command->drawMesh(tilesBuffer->getNumElements(), 4, 1); + command->drawIndirect(plainMesh.indirectDrawBuffer, 0, 1, 0); return command; } void TerrainRenderer::setViewport(Gfx::PViewport _viewport, Gfx::PRenderPass renderPass) { viewport = _viewport; - - Gfx::MeshPipelineCreateInfo pipelineInfo = { - .taskShader = task, - .meshShader = mesh, + inp = graphics->createVertexInput({}); + Gfx::LegacyPipelineCreateInfo pipelineInfo = { + .vertexInput = inp, + .vertexShader = vert, .fragmentShader = frag, .renderPass = renderPass, - .pipelineLayout = pipelineLayout, + .pipelineLayout = meshUpdater.pipelineLayout, .multisampleState = { .samples = viewport->getSamples(), @@ -128,4 +315,4 @@ void TerrainRenderer::setViewport(Gfx::PViewport _viewport, Gfx::PRenderPass ren }, }; pipeline = graphics->createGraphicsPipeline(std::move(pipelineInfo)); -} \ No newline at end of file +} diff --git a/src/Engine/Graphics/RenderPass/TerrainRenderer.h b/src/Engine/Graphics/RenderPass/TerrainRenderer.h index b593c4d..92f1e81 100644 --- a/src/Engine/Graphics/RenderPass/TerrainRenderer.h +++ b/src/Engine/Graphics/RenderPass/TerrainRenderer.h @@ -1,12 +1,14 @@ #pragma once #include "RenderPass.h" +#include "Graphics/Buffer.h" +#include "Graphics/CBT/CBT.h" namespace Seele { class TerrainRenderer { public: - TerrainRenderer(Gfx::PGraphics graphics, PScene scene, Gfx::PDescriptorLayout viewParamsLayout); + TerrainRenderer(Gfx::PGraphics graphics, PScene scene, Gfx::PDescriptorLayout viewParamsLayout, Gfx::PDescriptorSet viewParamsSet); ~TerrainRenderer(); - void beginFrame(); + void beginFrame(Gfx::PDescriptorSet viewParamsSet); Gfx::ORenderCommand render(Gfx::PDescriptorSet viewParamsSet); void setViewport(Gfx::PViewport viewport, Gfx::PRenderPass renderPass); @@ -18,13 +20,24 @@ class TerrainRenderer { Gfx::OPipelineLayout pipelineLayout; Gfx::OTaskShader task; Gfx::OMeshShader mesh; + Gfx::OVertexInput inp; + Gfx::OVertexShader vert; Gfx::OFragmentShader frag; Gfx::PGraphicsPipeline pipeline; + Gfx::OComputeShader deformCS; + Gfx::PComputePipeline deform; Gfx::PViewport viewport; Gfx::OShaderBuffer tilesBuffer; Gfx::PTexture2D displacementMap; Gfx::PTexture2D colorMap; Gfx::OSampler sampler; + + Gfx::OUniformBuffer geometryBuffer; + Gfx::OUniformBuffer updateBuffer; + BaseMesh baseMesh; + CBTMesh plainMesh; + MeshUpdater meshUpdater; + LebMatrixCache lebCache; }; DEFINE_REF(TerrainRenderer); -} \ No newline at end of file +} // namespace Seele \ No newline at end of file diff --git a/src/Engine/Graphics/StaticMeshVertexData.cpp b/src/Engine/Graphics/StaticMeshVertexData.cpp index 26f42e8..9d59e42 100644 --- a/src/Engine/Graphics/StaticMeshVertexData.cpp +++ b/src/Engine/Graphics/StaticMeshVertexData.cpp @@ -149,7 +149,6 @@ void StaticMeshVertexData::updateBuffers() { .size = verticesAllocated * sizeof(Vector), .data = (uint8*)posData.data(), }, - .vertexBuffer = true, .name = "Positions", }); normals = graphics->createShaderBuffer(ShaderBufferCreateInfo{ @@ -158,7 +157,6 @@ void StaticMeshVertexData::updateBuffers() { .size = verticesAllocated * sizeof(Quaternion), .data = (uint8*)norData.data(), }, - .vertexBuffer = false, .name = "Normals", }); colors = graphics->createShaderBuffer(ShaderBufferCreateInfo{ @@ -167,7 +165,6 @@ void StaticMeshVertexData::updateBuffers() { .size = verticesAllocated * sizeof(U16Vector), .data = (uint8*)colData.data(), }, - .vertexBuffer = false, .name = "Colors", }); for (size_t i = 0; i < MAX_TEXCOORDS; ++i) { @@ -177,7 +174,6 @@ void StaticMeshVertexData::updateBuffers() { .size = verticesAllocated * sizeof(U16Vector2), .data = (uint8*)texData[i].data(), }, - .vertexBuffer = false, .name = "TexCoords", }); } diff --git a/src/Engine/Graphics/VertexData.cpp b/src/Engine/Graphics/VertexData.cpp index 37f850d..ff677ce 100644 --- a/src/Engine/Graphics/VertexData.cpp +++ b/src/Engine/Graphics/VertexData.cpp @@ -11,7 +11,7 @@ using namespace Seele; -constexpr static uint64 NUM_DEFAULT_ELEMENTS = 100000; +constexpr static uint64 NUM_DEFAULT_ELEMENTS = 36; uint64 VertexData::meshletCount = 0; void VertexData::resetMeshData() { diff --git a/src/Engine/Graphics/Vulkan/Buffer.cpp b/src/Engine/Graphics/Vulkan/Buffer.cpp index 83bfdf1..fe73255 100644 --- a/src/Engine/Graphics/Vulkan/Buffer.cpp +++ b/src/Engine/Graphics/Vulkan/Buffer.cpp @@ -389,8 +389,8 @@ void Buffer::pipelineBarrier(VkAccessFlags srcAccess, VkPipelineStageFlags srcSt UniformBuffer::UniformBuffer(PGraphics graphics, const UniformBufferCreateInfo& createInfo) : Gfx::UniformBuffer(graphics->getFamilyMapping(), createInfo), - Vulkan::Buffer(graphics, createInfo.sourceData.size, VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT, createInfo.sourceData.owner, - true, createInfo.name) { + Vulkan::Buffer(graphics, createInfo.sourceData.size, VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT, createInfo.sourceData.owner, true, + createInfo.name) { if (createInfo.sourceData.size > 0 && createInfo.sourceData.data != nullptr) { getAlloc()->updateContents(createInfo.sourceData.offset, createInfo.sourceData.size, createInfo.sourceData.data); } @@ -416,11 +416,7 @@ void UniformBuffer::executePipelineBarrier(VkAccessFlags srcAccess, VkPipelineSt ShaderBuffer::ShaderBuffer(PGraphics graphics, const ShaderBufferCreateInfo& createInfo) : Gfx::ShaderBuffer(graphics->getFamilyMapping(), createInfo), - Vulkan::Buffer(graphics, createInfo.sourceData.size, - VK_BUFFER_USAGE_STORAGE_BUFFER_BIT | - (createInfo.vertexBuffer ? VK_BUFFER_USAGE_ACCELERATION_STRUCTURE_BUILD_INPUT_READ_ONLY_BIT_KHR | - VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT - : 0), + Vulkan::Buffer(graphics, createInfo.sourceData.size, VK_BUFFER_USAGE_STORAGE_BUFFER_BIT | createInfo.usage, createInfo.sourceData.owner, true, createInfo.name, createInfo.createCleared, createInfo.clearValue) { if (createInfo.sourceData.size > 0 && createInfo.sourceData.data != nullptr) { getAlloc()->updateContents(createInfo.sourceData.offset, createInfo.sourceData.size, createInfo.sourceData.data); @@ -491,12 +487,12 @@ void VertexBuffer::executePipelineBarrier(VkAccessFlags srcAccess, VkPipelineSta IndexBuffer::IndexBuffer(PGraphics graphics, const IndexBufferCreateInfo& createInfo) : Gfx::IndexBuffer(graphics->getFamilyMapping(), createInfo), Vulkan::Buffer(graphics, createInfo.sourceData.size, - VK_BUFFER_USAGE_INDEX_BUFFER_BIT | VK_BUFFER_USAGE_ACCELERATION_STRUCTURE_BUILD_INPUT_READ_ONLY_BIT_KHR | - VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT | VK_BUFFER_USAGE_STORAGE_BUFFER_BIT, + VK_BUFFER_USAGE_INDEX_BUFFER_BIT | VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT | + VK_BUFFER_USAGE_STORAGE_BUFFER_BIT, createInfo.sourceData.owner, false, createInfo.name) { getAlloc()->updateContents(createInfo.sourceData.offset, createInfo.sourceData.size, createInfo.sourceData.data); - getAlloc()->pipelineBarrier(VK_ACCESS_TRANSFER_WRITE_BIT, Gfx::SE_PIPELINE_STAGE_TRANSFER_BIT, Gfx::SE_ACCESS_INDEX_READ_BIT, - Gfx::SE_PIPELINE_STAGE_ACCELERATION_STRUCTURE_BUILD_BIT_KHR | Gfx::SE_PIPELINE_STAGE_VERTEX_INPUT_BIT); + //getAlloc()->pipelineBarrier(VK_ACCESS_TRANSFER_WRITE_BIT, Gfx::SE_PIPELINE_STAGE_TRANSFER_BIT, Gfx::SE_ACCESS_INDEX_READ_BIT, + // Gfx::SE_PIPELINE_STAGE_ACCELERATION_STRUCTURE_BUILD_BIT_KHR | Gfx::SE_PIPELINE_STAGE_VERTEX_INPUT_BIT); } IndexBuffer::~IndexBuffer() {} diff --git a/src/Engine/Graphics/Vulkan/Command.cpp b/src/Engine/Graphics/Vulkan/Command.cpp index 730820f..76a5f4a 100644 --- a/src/Engine/Graphics/Vulkan/Command.cpp +++ b/src/Engine/Graphics/Vulkan/Command.cpp @@ -331,6 +331,11 @@ void RenderCommand::draw(uint32 vertexCount, uint32 instanceCount, int32 firstVe vkCmdDraw(handle, vertexCount, instanceCount, firstVertex, firstInstance); } +void RenderCommand::drawIndirect(Gfx::PShaderBuffer buffer, uint64 offset, uint32 drawCount, uint32 stride) { + assert(threadId == std::this_thread::get_id()); + vkCmdDrawIndirect(handle, buffer.cast()->getHandle(), offset, drawCount, stride); +} + void RenderCommand::drawIndexed(uint32 indexCount, uint32 instanceCount, int32 firstIndex, uint32 vertexOffset, uint32 firstInstance) { assert(threadId == std::this_thread::get_id()); vkCmdDrawIndexed(handle, indexCount, instanceCount, firstIndex, vertexOffset, firstInstance); @@ -470,6 +475,10 @@ void ComputeCommand::dispatch(uint32 threadX, uint32 threadY, uint32 threadZ) { assert(threadId == std::this_thread::get_id()); vkCmdDispatch(handle, threadX, threadY, threadZ); } +void ComputeCommand::dispatchIndirect(Gfx::PShaderBuffer buffer, uint32 offset) { + assert(threadId == std::this_thread::get_id()); + vkCmdDispatchIndirect(handle, buffer.cast()->getHandle(), offset); +} CommandPool::CommandPool(PGraphics graphics, PQueue queue) : graphics(graphics), queue(queue), queueFamilyIndex(queue->getFamilyIndex()) { VkCommandPoolCreateInfo info = { diff --git a/src/Engine/Graphics/Vulkan/Command.h b/src/Engine/Graphics/Vulkan/Command.h index 472b9dc..98a7797 100644 --- a/src/Engine/Graphics/Vulkan/Command.h +++ b/src/Engine/Graphics/Vulkan/Command.h @@ -84,6 +84,7 @@ class RenderCommand : public Gfx::RenderCommand { virtual void bindIndexBuffer(Gfx::PIndexBuffer indexBuffer) override; virtual void pushConstants(Gfx::SeShaderStageFlags stage, uint32 offset, uint32 size, const void* data) override; virtual void draw(uint32 vertexCount, uint32 instanceCount, int32 firstVertex, uint32 firstInstance) override; + virtual void drawIndirect(Gfx::PShaderBuffer buffer, uint64 offset, uint32 drawCount, uint32 stride) override; virtual void drawIndexed(uint32 indexCount, uint32 instanceCount, int32 firstIndex, uint32 vertexOffset, uint32 firstInstance) override; virtual void drawMesh(uint32 groupX, uint32 groupY, uint32 groupZ) override; virtual void drawMeshIndirect(Gfx::PShaderBuffer buffer, uint64 offset, uint32 drawCount, uint32 stride) override; @@ -118,6 +119,7 @@ class ComputeCommand : public Gfx::ComputeCommand { virtual void bindDescriptor(const Array& sets) override; virtual void pushConstants(Gfx::SeShaderStageFlags stage, uint32 offset, uint32 size, const void* data) override; virtual void dispatch(uint32 threadX, uint32 threadY, uint32 threadZ) override; + virtual void dispatchIndirect(Gfx::PShaderBuffer buffer, uint32 offset) override; private: PComputePipeline pipeline; diff --git a/src/Engine/Graphics/Vulkan/Descriptor.cpp b/src/Engine/Graphics/Vulkan/Descriptor.cpp index 4728f04..8c1246c 100644 --- a/src/Engine/Graphics/Vulkan/Descriptor.cpp +++ b/src/Engine/Graphics/Vulkan/Descriptor.cpp @@ -220,6 +220,31 @@ void DescriptorSet::updateBuffer(uint32_t binding, uint32 index, Gfx::PShaderBuf boundResources[binding][index] = vulkanBuffer->getAlloc(); } +void DescriptorSet::updateBuffer(uint32_t binding, uint32 index, Gfx::PVertexBuffer indexBuffer) { + PVertexBuffer vulkanBuffer = indexBuffer.cast(); + if (boundResources[binding][index] == vulkanBuffer->getAlloc()) { + return; + } + + bufferInfos.add(VkDescriptorBufferInfo{ + .buffer = vulkanBuffer->getHandle(), + .offset = 0, + .range = vulkanBuffer->getSize(), + }); + writeDescriptors.add(VkWriteDescriptorSet{ + .sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET, + .pNext = nullptr, + .dstSet = setHandle, + .dstBinding = binding, + .dstArrayElement = index, + .descriptorCount = 1, + .descriptorType = cast(layout->getBindings()[binding].descriptorType), + .pBufferInfo = &bufferInfos.back(), + }); + + boundResources[binding][index] = vulkanBuffer->getAlloc(); +} + void DescriptorSet::updateBuffer(uint32_t binding, uint32 index, Gfx::PIndexBuffer indexBuffer) { PIndexBuffer vulkanBuffer = indexBuffer.cast(); if (boundResources[binding][index] == vulkanBuffer->getAlloc()) { diff --git a/src/Engine/Graphics/Vulkan/Descriptor.h b/src/Engine/Graphics/Vulkan/Descriptor.h index 07cb314..5efbec9 100644 --- a/src/Engine/Graphics/Vulkan/Descriptor.h +++ b/src/Engine/Graphics/Vulkan/Descriptor.h @@ -50,6 +50,7 @@ class DescriptorSet : public Gfx::DescriptorSet, public CommandBoundResource { virtual void writeChanges() override; virtual void updateBuffer(uint32 binding, uint32 index, Gfx::PUniformBuffer uniformBuffer) override; virtual void updateBuffer(uint32 binding, uint32 index, Gfx::PShaderBuffer shaderBuffer) override; + virtual void updateBuffer(uint32 binding, uint32 index, Gfx::PVertexBuffer indexBuffer) override; virtual void updateBuffer(uint32 binding, uint32 index, Gfx::PIndexBuffer indexBuffer) override; virtual void updateSampler(uint32 binding, uint32 index, Gfx::PSampler samplerState) override; virtual void updateTexture(uint32 binding, uint32 index, Gfx::PTexture2D texture) override; diff --git a/src/Engine/Graphics/Vulkan/Graphics.cpp b/src/Engine/Graphics/Vulkan/Graphics.cpp index 36b12f4..834555c 100644 --- a/src/Engine/Graphics/Vulkan/Graphics.cpp +++ b/src/Engine/Graphics/Vulkan/Graphics.cpp @@ -377,6 +377,19 @@ void Graphics::copyTexture(Gfx::PTexture source, Gfx::PTexture destination) { Gfx::SE_ACCESS_MEMORY_READ_BIT | Gfx::SE_ACCESS_MEMORY_WRITE_BIT, Gfx::SE_PIPELINE_STAGE_TOP_OF_PIPE_BIT); } +void Graphics::copyBuffer(Gfx::PShaderBuffer srcBuffer, Gfx::PShaderBuffer dstBuffer) { + PShaderBuffer src = srcBuffer.cast(); + PShaderBuffer dst = dstBuffer.cast(); + + VkBufferCopy region = { + .srcOffset = 0, + .dstOffset = 0, + .size = src->getSize(), + }; + vkCmdCopyBuffer(getGraphicsCommands()->getCommands()->getHandle(), src->getHandle(), dst->getHandle(), 1, ®ion); +} + + Gfx::OBottomLevelAS Graphics::createBottomLevelAccelerationStructure(const Gfx::BottomLevelASCreateInfo& createInfo) { return new BottomLevelAS(this, createInfo); } @@ -740,7 +753,7 @@ void Graphics::pickPhysicalDevice() { }; meshShaderFeatures = { .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MESH_SHADER_FEATURES_EXT, - .pNext = &accelerationFeatures, + .pNext = nullptr, .taskShader = true, .meshShader = true, .meshShaderQueries = true, @@ -872,9 +885,9 @@ void Graphics::createDevice(GraphicsInitializer initializer) { #ifdef __APPLE__ initializer.deviceExtensions.add("VK_KHR_portability_subset"); #endif - initializer.deviceExtensions.add(VK_KHR_ACCELERATION_STRUCTURE_EXTENSION_NAME); - initializer.deviceExtensions.add(VK_KHR_RAY_TRACING_PIPELINE_EXTENSION_NAME); - initializer.deviceExtensions.add(VK_KHR_DEFERRED_HOST_OPERATIONS_EXTENSION_NAME); + //initializer.deviceExtensions.add(VK_KHR_ACCELERATION_STRUCTURE_EXTENSION_NAME); + //initializer.deviceExtensions.add(VK_KHR_RAY_TRACING_PIPELINE_EXTENSION_NAME); + //initializer.deviceExtensions.add(VK_KHR_DEFERRED_HOST_OPERATIONS_EXTENSION_NAME); VkDeviceCreateInfo deviceInfo = { .sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO, .pNext = &features, diff --git a/src/Engine/Graphics/Vulkan/Graphics.h b/src/Engine/Graphics/Vulkan/Graphics.h index 1c82ade..1f2036e 100644 --- a/src/Engine/Graphics/Vulkan/Graphics.h +++ b/src/Engine/Graphics/Vulkan/Graphics.h @@ -78,6 +78,8 @@ class Graphics : public Gfx::Graphics { virtual void resolveTexture(Gfx::PTexture source, Gfx::PTexture destination) override; virtual void copyTexture(Gfx::PTexture src, Gfx::PTexture dst) override; + + virtual void copyBuffer(Gfx::PShaderBuffer src, Gfx::PShaderBuffer dst) override; // Ray Tracing virtual Gfx::OBottomLevelAS createBottomLevelAccelerationStructure(const Gfx::BottomLevelASCreateInfo& createInfo) override; diff --git a/src/Engine/Graphics/Vulkan/PipelineCache.cpp b/src/Engine/Graphics/Vulkan/PipelineCache.cpp index 6bc70f3..bdd70a0 100644 --- a/src/Engine/Graphics/Vulkan/PipelineCache.cpp +++ b/src/Engine/Graphics/Vulkan/PipelineCache.cpp @@ -49,7 +49,7 @@ PGraphicsPipeline PipelineCache::createPipeline(Gfx::LegacyPipelineCreateInfo gf if (graphicsPipelines.contains(hash)) { return graphicsPipelines[hash]; } - PPipelineLayout layout = Gfx::PPipelineLayout(gfxInfo.pipelineLayout).cast(); + PPipelineLayout layout = gfxInfo.pipelineLayout.cast(); Array bindings; Array attributes; if (gfxInfo.vertexInput != nullptr) { @@ -429,7 +429,7 @@ PGraphicsPipeline PipelineCache::createPipeline(Gfx::MeshPipelineCreateInfo gfxI } PComputePipeline PipelineCache::createPipeline(Gfx::ComputePipelineCreateInfo computeInfo) { - PPipelineLayout layout = Gfx::PPipelineLayout(computeInfo.pipelineLayout).cast(); + PPipelineLayout layout = computeInfo.pipelineLayout.cast(); auto computeStage = computeInfo.computeShader.cast(); uint32 hash = layout->getHash(); diff --git a/src/Engine/Graphics/Vulkan/Window.cpp b/src/Engine/Graphics/Vulkan/Window.cpp index 0802eb7..1136075 100644 --- a/src/Engine/Graphics/Vulkan/Window.cpp +++ b/src/Engine/Graphics/Vulkan/Window.cpp @@ -14,7 +14,7 @@ double Gfx::getCurrentFrameDelta() { return currentFrameDelta; } double currentFrameTime = 0; double Gfx::getCurrentFrameTime() { return currentFrameTime; } -uint32 currentFrameIndex = 0; +uint32 currentFrameIndex = std::numeric_limits::max(); uint32 Gfx::getCurrentFrameIndex() { return currentFrameIndex; } void glfwKeyCallback(GLFWwindow* handle, int key, int, int action, int modifier) { diff --git a/src/Engine/Graphics/slang-compile.cpp b/src/Engine/Graphics/slang-compile.cpp index 4685009..3223011 100644 --- a/src/Engine/Graphics/slang-compile.cpp +++ b/src/Engine/Graphics/slang-compile.cpp @@ -33,22 +33,48 @@ void Seele::beginCompilation(const ShaderCompilationInfo& info, SlangCompileTarg } slang::SessionDesc sessionDesc; sessionDesc.flags = 0; - StaticArray option; - option[0].name = slang::CompilerOptionName::IgnoreCapabilities; - option[0].value.kind = slang::CompilerOptionValueKind::Int; - option[0].value.intValue0 = 1; - option[1].name = slang::CompilerOptionName::EmitSpirvViaGLSL; - option[1].value.kind = slang::CompilerOptionValueKind::Int; - option[1].value.intValue0 = 1; - option[2].name = slang::CompilerOptionName::DebugInformation; - option[2].value.kind = slang::CompilerOptionValueKind::Int; - option[2].value.intValue0 = SLANG_DEBUG_INFO_LEVEL_STANDARD; - option[3].name = slang::CompilerOptionName::DebugInformationFormat; - option[3].value.kind = slang::CompilerOptionValueKind::Int; - option[3].value.intValue0 = SLANG_DEBUG_INFO_FORMAT_PDB; - option[4].name = slang::CompilerOptionName::DumpIntermediates; - option[4].value.kind = slang::CompilerOptionValueKind::Int; - option[4].value.intValue0 = info.dumpIntermediate; + Array option = { + { + .name = slang::CompilerOptionName::IgnoreCapabilities, + .value = + { + .kind = slang::CompilerOptionValueKind::Int, + .intValue0 = 1, + }, + }, + { + .name = slang::CompilerOptionName::EmitSpirvViaGLSL, + .value = + { + .kind = slang::CompilerOptionValueKind::Int, + .intValue0 = 1, + }, + }, + { + .name = slang::CompilerOptionName::DebugInformation, + .value = + { + .kind = slang::CompilerOptionValueKind::Int, + .intValue0 = SLANG_DEBUG_INFO_LEVEL_STANDARD, + }, + }, + { + .name = slang::CompilerOptionName::DebugInformationFormat, + .value = + { + .kind = slang::CompilerOptionValueKind::Int, + .intValue0 = SLANG_DEBUG_INFO_FORMAT_PDB, + }, + }, + { + .name = slang::CompilerOptionName::DumpIntermediates, + .value = + { + .kind = slang::CompilerOptionValueKind::Int, + .intValue0 = info.dumpIntermediate, + }, + }, + }; sessionDesc.compilerOptionEntries = option.data(); sessionDesc.compilerOptionEntryCount = option.size(); @@ -63,11 +89,11 @@ void Seele::beginCompilation(const ShaderCompilationInfo& info, SlangCompileTarg sessionDesc.preprocessorMacroCount = macros.size(); sessionDesc.preprocessorMacros = macros.data(); slang::TargetDesc targetDesc; - targetDesc.profile = globalSession->findProfile("spirv_1_5"); + targetDesc.profile = globalSession->findProfile("GLSL_450"); targetDesc.format = target; sessionDesc.targetCount = 1; sessionDesc.targets = &targetDesc; - StaticArray searchPaths = {"shaders/", "shaders/lib/", "shaders/raytracing/", "shaders/generated/"}; + StaticArray searchPaths = {"shaders/", "shaders/lib/", "shaders/raytracing/", "shaders/terrain", "shaders/generated/"}; sessionDesc.searchPaths = searchPaths.data(); sessionDesc.searchPathCount = searchPaths.size(); @@ -113,20 +139,20 @@ void Seele::beginCompilation(const ShaderCompilationInfo& info, SlangCompileTarg slang::ProgramLayout* signature = specializedComponent->getLayout(0, diagnostics.writeRef()); CHECK_DIAGNOSTICS(); - //std::cout << info.name << std::endl; + // std::cout << info.name << std::endl; for (size_t i = 0; i < signature->getParameterCount(); ++i) { auto param = signature->getParameterByIndex(i); layout->addMapping(param->getName(), param->getBindingIndex()); - //std::cout << param->getName() << ": " << param->getBindingIndex() << std::endl; + // std::cout << param->getName() << ": " << param->getBindingIndex() << std::endl; } // workaround - //layout->addMapping("pVertexData", 1); - //layout->addMapping("pMaterial", 4); - //layout->addMapping("pLightEnv", 3); - //layout->addMapping("pRayTracingParams", 5); - //layout->addMapping("pScene", 2); - //layout->addMapping("pWaterMaterial", 1); + // layout->addMapping("pVertexData", 1); + // layout->addMapping("pMaterial", 4); + // layout->addMapping("pLightEnv", 3); + // layout->addMapping("pRayTracingParams", 5); + // layout->addMapping("pScene", 2); + // layout->addMapping("pWaterMaterial", 1); } Pair, std::string> Seele::generateShader(const ShaderCreateInfo& createInfo) { diff --git a/test.py b/test.py index 6f377ec..ae7340f 100644 --- a/test.py +++ b/test.py @@ -1,14 +1,14 @@ -i = 12 +i = 4 positions = [] for x in range(i): for y in range(i): - positions.append(f"float2({x}.0f / 11, {y}.0f / 11)") + positions.append(f"Vector({x}.0f / {i-1}, 0, {y}.0f / {i-1})") indices = [] for y in range(i - 1): for x in range(i - 1): - indices.append(f'uint3({(x) + i * (y)}, {(x + 1) + i * (y)}, {(x) + i * (y + 1)})') - indices.append(f'uint3({(x) + i * (y + 1)}, {(x + 1) + i * (y)}, {(x + 1) + i * (y + 1)})') + indices.append(f'UVector3({(x) + i * (y)}, {(x + 1) + i * (y)}, {(x) + i * (y + 1)})') + indices.append(f'UVector3({(x) + i * (y + 1)}, {(x + 1) + i * (y)}, {(x + 1) + i * (y + 1)})') print(f"Dim {i}") print(len(positions)) print(len(indices))