diff --git a/res/shaders/TerrainPass.slang b/res/shaders/TerrainPass.slang index 46ebb43..21e2b37 100644 --- a/res/shaders/TerrainPass.slang +++ b/res/shaders/TerrainPass.slang @@ -31,7 +31,7 @@ VertexOutput vert(VertexInput input) // 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]; + float3 positionRWS = pParams.currentVertexBuffer[output.triangleID * 3 + local_vert_id].xyz; // Apply the view projection output.positionCS = mul(pViewParams.projectionMatrix, mul(pViewParams.viewMatrix, float4(positionRWS, 1.0))); @@ -53,26 +53,25 @@ float4 frag(PixelInput input) : SV_Target [shader("compute")] [numthreads(64, 1, 1)] -void deform(uint currentID: SV_DispatchThreadID) +void EvaluateDeformation(uint currentID : SV_DispatchThreadID) { - if(currentID >= pParams.indirectDrawBuffer[9] * 4) + // This thread doesn't have any work to do, we're done + if (currentID >= pParams.indirectDrawBuffer[9] * 4) return; + // Extract the bisector ID and the vertexID uint bisectorID = currentID / 4; uint localVertexID = currentID % 4; + // Operate the indirection bisectorID = pParams.indexedBisectorBuffer[bisectorID]; + // Evaluate the source vertex currentID = localVertexID < 3 ? bisectorID * 3 + localVertexID : 3 * pParams.geometry.totalNumElements + bisectorID; - float3 positionWS = pParams.lebPositionBuffer[currentID]; - float3 positionRWS = positionWS - pViewParams.cameraPosition_WS.xyz; + // Grab the position that we will be displacing + float3 positionWS = pParams.lebPositionBuffer[currentID].xyz; + float3 positionRWS = float3(positionWS - pViewParams.cameraPosition_WS.xyz); - float3 positionPS = positionWS; - - float3 normalPS = float3(0, 1, 0); - - float2 sampleUV = float2(0, 0); - - pParams.currentVertexBuffer[currentID] = positionRWS; -} \ No newline at end of file + pParams.currentVertexBuffer[currentID] = float4(positionRWS, 1); +} diff --git a/res/shaders/terrain/Bisector.slang b/res/shaders/terrain/Bisector.slang index ff3b385..980df66 100644 --- a/res/shaders/terrain/Bisector.slang +++ b/res/shaders/terrain/Bisector.slang @@ -16,12 +16,12 @@ 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]; + // Subvision that should be applied to this bisector + uint32_t subdivisionPattern; + // Neighbor that should be processed uint32_t problematicNeighbor; @@ -35,7 +35,7 @@ struct BisectorData uint32_t propagationID; }; -uint heapIDDepth(uint64_t x) +uint HeapIDDepth(uint64_t x) { uint depth = 0; while (x > 0u) { diff --git a/res/shaders/terrain/CBT.slang b/res/shaders/terrain/CBT.slang index 02bb27a..c4e28dd 100644 --- a/res/shaders/terrain/CBT.slang +++ b/res/shaders/terrain/CBT.slang @@ -563,11 +563,7 @@ 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]; } diff --git a/res/shaders/terrain/CBTCompute.slang b/res/shaders/terrain/CBTCompute.slang index d3e9d57..394f56e 100644 --- a/res/shaders/terrain/CBTCompute.slang +++ b/res/shaders/terrain/CBTCompute.slang @@ -1,1177 +1,186 @@ -import Common; +import update_utils; import Parameters; -import Bisector; -import Bounding; import CBT; -#ifndef WORKGROUP_SIZE -#define WORKGROUP_SIZE 64 -#endif +static const uint32_t WORKGROUP_SIZE = 64; - -struct BisectorGeometry -{ - float3 p[4]; -} - -// global -// update -int classifyBisector(in BisectorGeometry tri, uint depth, inout DebugStruct debug) -{ - 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); - - debug.fDotV = fDotV; - debug.vDotN = vDotN; - if(fDotV < 0.0 && vDotN < -1e-3) - { - debug.area = 420; - 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 = clipToScreen(mul(viewProjectionMatrix, float4(tri.p[0], 1.0))); - - float4 p1P = clipToScreen(mul(viewProjectionMatrix, float4(tri.p[1], 1.0))); - - float4 p2P = clipToScreen(mul(viewProjectionMatrix, float4(tri.p[2], 1.0))); - - float area = 0.5 * abs(p0P.x * (p1P.y - p2P.y) + p1P.x * (p2P.y - p0P.y) + p2P.x * (p0P.y - p1P.y)); - - float areaOverestimation = lerp(2.0, 1.0, pow(vDotN, 0.2)); - area *= areaOverestimation; - - debug.areaP[0] = p0P.xy; - debug.areaP[1] = p1P.xy; - debug.areaP[2] = p2P.xy; - debug.area = area; - - 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 = clipToScreen(mul(viewProjectionMatrix, float4(tri.p[3], 1.0))); - - float areaParent = 0.5 * abs(p0P.x * (p3P.y - p2P.y) + p3P.x * (p2P.y - p0P.y) + p2P.x * (p0P.y - p3P.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() +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; + ResetBuffers(); } -// 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) +void Classify(uint currentID : SV_DispatchThreadID) { - if(dispatchID >= pParams.indirectDrawBuffer[9]) + // This thread doesn't have any work to do, we're done + if (currentID >= pParams.indirectDrawBuffer[9]) return; - uint currentID = pParams.indexedBisectorBuffer[dispatchID]; + // Operate the indirection + currentID = pParams.indexedBisectorBuffer[currentID]; + + // Read the current geometry data 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]; + bis.p[0] = pParams.currentVertexBuffer[3 * currentID].xyz; + bis.p[1] = pParams.currentVertexBuffer[3 * currentID + 1].xyz; + bis.p[2] = pParams.currentVertexBuffer[3 * currentID + 2].xyz; + bis.p[3] = pParams.currentVertexBuffer[3 * pParams.geometry.totalNumElements + currentID].xyz; - 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; - - DebugStruct debug; - debug.sourceP[0] = float4(bis.p[0], 1); - debug.sourceP[1] = float4(bis.p[1], 1); - debug.sourceP[2] = float4(bis.p[2], 1); - int currentValidity = classifyBisector(bis, depth, debug); - debug.validity = currentValidity; - pParams.debugBuffer[dispatchID] = debug; - 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; + // Classify the element + ClassifyElement(currentID, bis, pParams.geometry.totalNumElements, pParams.geometry.baseDepth); } -// classification R -// neighbours R -// bisectorData Rw -// heapID R -// memory RW -// allocate RW [numthreads(WORKGROUP_SIZE, 1, 1)] -void split(uint dispatchID : SV_DispatchThreadID) +void Split(uint dispatchID : SV_DispatchThreadID) { - if(dispatchID >= pParams.classificationBuffer[SPLIT_COUNTER]) + if (dispatchID >= pParams.classificationBuffer[SPLIT_COUNTER]) return; + // Grab the real elementID 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); + // Split the element + SplitElement(currentID, pParams.geometry.baseDepth); } -// indirectDispatch RW -// allocate R [numthreads(1, 1, 1)] -void prepareIndirect(uint currentID: SV_DispatchThreadID) +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) +void Allocate(uint groupIndex : SV_GroupIndex, uint dispatchID : SV_DispatchThreadID) { + // Load the CBT to the LDS load_buffer_to_shared_memory(groupIndex); - if(dispatchID >= pParams.allocateBuffer[0]) + // If this element doesn't need to be processed, we're done + 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; - } + // Allocate the required bits + AllocateElement(pParams.allocateBuffer[1 + dispatchID]); } -// 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) +void Bisect(uint groupIndex : SV_GroupIndex, uint dispatchID : SV_DispatchThreadID) { - if(dispatchID >= pParams.allocateBuffer[0]) + // If this element doesn't need to be processed, we're done + 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); - } + // Operation the bisection of this element + BisectElement(pParams.allocateBuffer[1 + dispatchID]); } -// bisectorData RW -// neighbours RW [numthreads(WORKGROUP_SIZE, 1, 1)] -void propagateBisect(uint dispatchID : SV_DispatchThreadID) +void PropagateBisect(uint dispatchID : SV_DispatchThreadID) { - if(dispatchID >= pParams.propagateBuffer[0]) + // If this element doesn't need to be processed, we're done + 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; + PropagateBisectElement(pParams.propagateBuffer[2 + dispatchID]); } -// geometry -// classification R -// heapID R -// neighbours R -// bisectorData R -// simplify RW [numthreads(WORKGROUP_SIZE, 1, 1)] -void prepareSimplify(uint dispatchID : SV_DispatchThreadID) +void PrepareSimplify(uint dispatchID : SV_DispatchThreadID) { - if(dispatchID >= pParams.classificationBuffer[SIMPLIFY_COUNTER]) + // If this element doesn't need to be processed, we're done + if (dispatchID >= pParams.classificationBuffer[SIMPLIFY_COUNTER]) return; + // Grab the real elementID 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 an element + PrepareSimplifyElement(currentID); } -// simplify R -// bisectorData RW -// neighbours RW -// heapID RW [numthreads(WORKGROUP_SIZE, 1, 1)] -void simplify(uint dispatchID : SV_DispatchThreadID) +void Simplify(uint currentID : SV_DispatchThreadID) { - if(dispatchID >= pParams.simplifyBuffer[0]) + // This thread doesn't have any work to do, we're done + if (currentID >= pParams.simplifyBuffer[0]) return; - uint currentID = pParams.simplifyBuffer[1 + dispatchID]; + // Simplify an element + SimplifyElement(pParams.simplifyBuffer[1 + currentID]); +} - 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; - } +[numthreads(WORKGROUP_SIZE, 1, 1)] +void PropagateSimplify(uint dispatchID : SV_DispatchThreadID) +{ + // If this element doesn't need to be processed, we're done + if (dispatchID >= pParams.propagateBuffer[1]) + return; - 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; + PropagateElementSimplify(pParams.propagateBuffer[2 + dispatchID]); } [numthreads(WORKGROUP_SIZE, 1, 1)] -void reducePrePass(uint dispatchThreadID: SV_DispatchThreadID) +void ReducePrePass(uint dispatchThreadID : SV_DispatchThreadID) { reduce_prepass(dispatchThreadID); } [numthreads(WORKGROUP_SIZE, 1, 1)] -void reduceFirstPass(uint dispatchID: SV_DispatchThreadID, uint groupIndex : SV_GroupIndex) +void ReduceFirstPass(uint dispatchThreadID : SV_DispatchThreadID, uint groupIndex : SV_GroupIndex) { - reduce_first_pass(dispatchID, groupIndex); + // Reduce Mid Pass + reduce_first_pass(dispatchThreadID, groupIndex); } [numthreads(WORKGROUP_SIZE, 1, 1)] -void reduceSecondPass(uint groupIndex: SV_GroupIndex) +void ReduceSecondPass(uint groupIndex : SV_GroupIndex) { + // Find the ith bit set to one reduce_second_pass(groupIndex); } -// geometry -// heapID R -// indirectDraw RW -// bisectorIndices RW -// bisectorData R [numthreads(WORKGROUP_SIZE, 1, 1)] -void bisectorIndexation(uint currentID: SV_DispatchThreadID) +void BisectorIndexation(uint currentID : SV_DispatchThreadID) { - if(currentID >= pParams.geometry.totalNumElements) + // This thread doesn't have any work to do, we're done + 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; + // Indexate this bisector + BisectorElementIndexation(currentID); } [numthreads(1, 1, 1)] -void prepareBisectorIndirect(uint currentID: SV_DispatchThreadID) +void PrepareBisectorIndirect(uint currentID : SV_DispatchThreadID) { + // Indirect dispatch for each bisector pParams.indirectDispatchBuffer[0] = (pParams.indirectDrawBuffer[0] / 3 + WORKGROUP_SIZE - 1) / WORKGROUP_SIZE; pParams.indirectDispatchBuffer[1] = 1; pParams.indirectDispatchBuffer[2] = 1; - + + // Indirect dispatch for each vertex to process (3 per bisector + 1 for the parent) pParams.indirectDispatchBuffer[3] = (pParams.indirectDrawBuffer[0] * 4 / 3 + WORKGROUP_SIZE - 1) / WORKGROUP_SIZE; pParams.indirectDispatchBuffer[4] = 1; pParams.indirectDispatchBuffer[5] = 1; + // Indirect dispatch for each modified vertex to process pParams.indirectDispatchBuffer[6] = (pParams.indirectDrawBuffer[8] + WORKGROUP_SIZE - 1) / WORKGROUP_SIZE; pParams.indirectDispatchBuffer[7] = 1; pParams.indirectDispatchBuffer[8] = 1; + // Explicit counter of the number of bisectors pParams.indirectDrawBuffer[9] = pParams.indirectDrawBuffer[0] / 3; } [numthreads(WORKGROUP_SIZE, 1, 1)] -void validate(uint currentID: SV_DispatchThreadID) +void Validate(uint currentID : SV_DispatchThreadID) { - if(currentID >= pParams.geometry.totalNumElements) + // This thread doesn't have any work to do, we're done + 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); - } + ValidateBisector(currentID); } - -[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, RWStructuredBuffer vertexBuffer, 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(vertexBuffer[3 * primitiveID + vertexDataOffset]); - float3 p1 = float3(vertexBuffer[3 * primitiveID + 1 + vertexDataOffset]); - float3 p2 = float3(vertexBuffer[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]); -} - -layout(push_constant) -ConstantBuffer preRender; - -[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(preRender == 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, pParams.currentVertexBuffer, 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/CompileTest.slang b/res/shaders/terrain/CompileTest.slang new file mode 100644 index 0000000..9f99343 --- /dev/null +++ b/res/shaders/terrain/CompileTest.slang @@ -0,0 +1,50 @@ + +struct ComputeParams +{ + RWStructuredBuffer indirectDrawBuffer; + RWStructuredBuffer heapIDBuffer; + RWStructuredBuffer classificationBuffer; + RWStructuredBuffer allocateBuffer; + RWStructuredBuffer memoryBuffer; + RWStructuredBuffer propagateBuffer; + RWStructuredBuffer simplifyBuffer; +}; +ParameterBlock pParams; + +// Split buffer slots +static const uint64_t SPLIT_COUNTER = 0; +static const uint64_t SIMPLIFY_COUNTER = 1; +static const uint64_t CLASSIFY_COUNTER_OFFSET = 2; +void ResetBuffers() +{ + pParams.memoryBuffer[0] = 0; + pParams.memoryBuffer[1] = 0; + + 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; +} + +[numthreads(1, 1, 1)] +void Reset() +{ + ResetBuffers(); +} \ No newline at end of file diff --git a/res/shaders/terrain/LEB.slang b/res/shaders/terrain/LEB.slang new file mode 100644 index 0000000..8527d80 --- /dev/null +++ b/res/shaders/terrain/LEB.slang @@ -0,0 +1,332 @@ +import Parameters; +import CBT; +import Bisector; + +static const uint32_t WORKGROUP_SIZE = 64; + + // Resolution of the cache + #define LEB_TABLE_DEPTH 5ULL + + // Cache in shared memory + groupshared float3x3 g_MatrixCache[2ULL << LEB_TABLE_DEPTH]; + + void load_leb_matrix_cache_to_shared_memory(uint groupIndex) + { + if (groupIndex < (2ULL << LEB_TABLE_DEPTH)) + g_MatrixCache[groupIndex] = pParams.lebMatrixCache[groupIndex]; + GroupMemoryBarrierWithGroupSync(); + } +uint leb_depth(uint64_t heapID) +{ + uint depth = 0; + while (heapID > 0u) + { + ++depth; + heapID >>= 1u; + } + return depth - 1; +} + +/******************************************************************************* + * GetBitValue -- Returns the value of a bit stored in a 64-bit word + * + */ + +uint64_t leb__GetBitValue(uint64_t bitField, int64_t bitID) +{ + return ((bitField >> bitID) & 1L); +} + +/******************************************************************************* + * IdentityMatrix3x3 -- Sets a 3x3 matrix to identity + * + */ + +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; +} + +/******************************************************************************* + * SplittingMatrix -- Computes a LEB splitting matrix from a split bit + * + */ + +void leb__SplittingMatrix(inout float3x3 mat, uint64_t bitValue) +{ + float b = (float)bitValue; + float c = 1.0f - b; + const float3x3 splitMatrix = { + {0.0f, b, c}, + {0.5f, 0.0f, 0.5f}, + {b, c, 0.0f} + }; + mat = mul(splitMatrix, mat); +} + +/******************************************************************************* + * SplittingMatrix -- Computes a LEB splitting matrix from a split bit + * + */ + +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); +} + +/******************************************************************************* + * DecodeTransformationMatrix -- Computes the matrix associated to a LEB + * node + * +*/ + +void leb__DecodeTransformationMatrix(uint64_t heapID, out float3x3 mat) +{ + int depth = leb_depth(heapID); + leb__IdentityMatrix3x3(mat); + for (int bitID = depth - 1; bitID >= 0; --bitID) + leb__SplittingMatrix(mat, leb__GetBitValue(heapID, bitID)); +} + +#if defined(LEB_MATRIX_CACHE_BINDING_SLOT) +void leb__DecodeTransformationMatrix_Tabulated(uint64_t heapID, out float3x3 mat) +{ + leb__IdentityMatrix3x3(mat); + const uint64_t msb = (1ULL << LEB_TABLE_DEPTH); + const uint64_t mask = ~(~0ULL << LEB_TABLE_DEPTH); + while (heapID > mask) + { + uint32_t index = uint32_t((heapID & mask) | msb); + mat = mul(mat, g_MatrixCache[index]); + heapID >>= LEB_TABLE_DEPTH; + } + mat = mul(mat, g_MatrixCache[uint32_t(heapID)]); +} +#endif + +/******************************************************************************* + * DecodeTransformationMatrix -- Computes the matrix associated to a LEB + * node + * +*/ + +void leb__DecodeTransformationMatrix_parent_child(uint64_t heapID, out float3x3 parent, out float3x3 child) +{ + int depth = leb_depth(heapID); + leb__IdentityMatrix3x3(parent); + + // Evaluate the parent matrix + int bitID; + for (bitID = depth - 1; bitID > 0; --bitID) + leb__SplittingMatrix(parent, leb__GetBitValue(heapID, bitID)); + + // Evaluate the child + if (depth > 0) + child = leb__SplittingMatrix_out(parent, leb__GetBitValue(heapID, bitID)); + else + child = parent; +} + +#if defined(LEB_MATRIX_CACHE_BINDING_SLOT) +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, g_MatrixCache[index]); + parentHeapID >>= LEB_TABLE_DEPTH; + } + if (parentHeapID != 0) + parent = mul(parent, g_MatrixCache[uint32_t(parentHeapID)]); + + // Evaluate the child + if (depth > 0) + child = leb__SplittingMatrix_out(parent, leb__GetBitValue(heapID, 0)); + else + child = parent; +} + +#endif + +/******************************************************************************* + * DecodeNodeAttributeArray -- Compute the triangle attributes at the input node + * + */ + +void leb_DecodeNodeAttributeArray(uint64_t heapID, inout float3 attributeArray[2]) +{ + float3x3 m; + leb__DecodeTransformationMatrix(heapID, m); + for (int i = 0; i < 2; ++i) + { + float3 attributeVector = attributeArray[i]; + attributeArray[i][0] = dot(m[0], attributeVector); + attributeArray[i][1] = dot(m[1], attributeVector); + attributeArray[i][2] = dot(m[2], attributeVector); + } +} + +void leb_DecodeNodeAttributeArray(uint64_t heapID, inout float3 attributeArray[3]) +{ + float3x3 m; +#if defined(LEB_MATRIX_CACHE_BINDING_SLOT) + leb__DecodeTransformationMatrix_Tabulated(heapID, m); +#else + leb__DecodeTransformationMatrix(heapID, m); +#endif + for (int i = 0; i < 3; ++i) + { + float3 attributeVector = attributeArray[i]; + attributeArray[i][0] = dot(m[0], attributeVector); + attributeArray[i][1] = dot(m[1], attributeVector); + attributeArray[i][2] = dot(m[2], attributeVector); + } +} + +/******************************************************************************* + * DecodeNodeAttributeArray -- Compute the triangle attributes at the input node + * + */ + +void leb_DecodeNodeAttributeArray_parent_child(uint64_t heapID, inout float3 childAttribute[3], out float3 parentAttribute[3]) +{ + float3x3 child, parent; +#if defined(LEB_MATRIX_CACHE_BINDING_SLOT) + leb__DecodeTransformationMatrix_parent_child_Tabulated(heapID, parent, child); +#else + leb__DecodeTransformationMatrix_parent_child(heapID, parent, child); +#endif + 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); + } +} + +[numthreads(WORKGROUP_SIZE, 1, 1)] +void ClearBuffer(uint currentID : SV_DispatchThreadID) +{ + // This thread doesn't have any work to do, we're done + if (currentID >= pParams.geometry.totalNumVertices) + return; + pParams.lebPositionBuffer[currentID] = float4(0.0, 0.0, 0.0, 1.0); +} + +float3 TransformToPlanetCoordinate(float3 posPS) +{ + // Evaluate the planet position + return normalize(posPS); +} + +[[vk::push_constant]] +ConstantBuffer preRendering; +struct Triangle +{ + float3 p[3]; +}; + +void EvaluateElementPosition(uint64_t heapID, uint32_t vertexDataOffset, uint minDepth, RWStructuredBuffer vertexBuffer, 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(vertexBuffer[3 * primitiveID + vertexDataOffset].xyz); + float3 p1 = float3(vertexBuffer[3 * primitiveID + 1 + vertexDataOffset].xyz); + float3 p2 = float3(vertexBuffer[3 * primitiveID + 2 + vertexDataOffset].xyz); + + // 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 defined(LEB_MATRIX_CACHE_BINDING_SLOT) + // Make sure these are loaded to the shared memory + load_leb_matrix_cache_to_shared_memory(groupIndex); + #endif + + // This thread doesn't have any work to do, we're done + uint32_t numBisectors = bool(preRendering) ? (pParams.indirectDrawBuffer[9]) : (pParams.indirectDrawBuffer[8] / 4); + if (currentID >= numBisectors) + return; + + // Load the bisector for this element + currentID = pParams.indexedBisectorBuffer[currentID]; + + // Evaluate the depth of the element + uint64_t cHeapID = pParams.heapIDBuffer[currentID]; + uint depth = HeapIDDepth(cHeapID); + + // Evaluate the positions of the current element + Triangle parentTri, childTri; + EvaluateElementPosition(cHeapID, 0, pParams.geometry.baseDepth, pParams.currentVertexBuffer, parentTri, childTri); + + // Export the child + pParams.lebPositionBuffer[3 * currentID] = float4(TransformToPlanetCoordinate(childTri.p[0]), 1.0f); + pParams.lebPositionBuffer[3 * currentID + 1] = float4(TransformToPlanetCoordinate(childTri.p[1]), 1.0f); + pParams.lebPositionBuffer[3 * currentID + 2] = float4(TransformToPlanetCoordinate(childTri.p[2]), 1.0f); + + // Export the fourth element + if (pParams.geometry.baseDepth < depth) + { + // Offset for the parent buffer + const uint parentOffset = 3 * pParams.geometry.totalNumElements; + + // Transform the coordinate to planet space + pParams.lebPositionBuffer[parentOffset + currentID] = float4(TransformToPlanetCoordinate(cHeapID % 2 == 0 ? parentTri.p[0] : parentTri.p[2]), 1.0f); + } +} diff --git a/res/shaders/terrain/Parameters.slang b/res/shaders/terrain/Parameters.slang index 2897d87..5234cd6 100644 --- a/res/shaders/terrain/Parameters.slang +++ b/res/shaders/terrain/Parameters.slang @@ -20,6 +20,7 @@ struct DeformationCB struct UpdateCB { + float4x4 viewProjectionMatrix; float triangleSize; uint32_t maxSubdivisionDepth; float fov; @@ -28,12 +29,8 @@ struct UpdateCB struct DebugStruct { - float4 sourceP[3]; - float2 areaP[3]; - float area; - int validity; - float fDotV; - float vDotN; + uint3 neighbours; + int status; } struct ComputeParams @@ -41,7 +38,7 @@ struct ComputeParams ConstantBuffer geometry; ConstantBuffer update; - RWStructuredBuffer currentVertexBuffer; + RWStructuredBuffer currentVertexBuffer; StructuredBuffer indexedBisectorBuffer; RWStructuredBuffer indirectDrawBuffer; RWStructuredBuffer heapIDBuffer; @@ -49,8 +46,8 @@ struct ComputeParams RWStructuredBuffer classificationBuffer; RWStructuredBuffer allocateBuffer; RWStructuredBuffer indirectDispatchBuffer; - RWStructuredBuffer neighboursBuffer; - RWStructuredBuffer neighboursOutputBuffer; + RWStructuredBuffer neighboursBuffer; + RWStructuredBuffer neighboursOutputBuffer; RWStructuredBuffer memoryBuffer; RWStructuredBuffer cbtBuffer; RWStructuredBuffer bitFieldBuffer; @@ -60,7 +57,7 @@ struct ComputeParams RWStructuredBuffer bisectorIndicesBuffer; RWStructuredBuffer visibleBisectorIndices; RWStructuredBuffer modifiedBisectorIndices; - RWStructuredBuffer lebPositionBuffer; + RWStructuredBuffer lebPositionBuffer; StructuredBuffer lebMatrixCache; RWStructuredBuffer debugBuffer; }; diff --git a/res/shaders/terrain/update_utils.slang b/res/shaders/terrain/update_utils.slang new file mode 100644 index 0000000..c39f0b1 --- /dev/null +++ b/res/shaders/terrain/update_utils.slang @@ -0,0 +1,966 @@ +import Common; +import Bisector; +import CBT; +import Parameters; + +// Needs to be defined before including update_utilities +struct BisectorGeometry +{ + float3 p[4]; +}; + +// Possible splits +static const uint64_t NO_SPLIT = 0x00; +static const uint64_t CENTER_SPLIT = 0x01; +static const uint64_t RIGHT_SPLIT = 0x02; +static const uint64_t LEFT_SPLIT = 0x04; +static const uint64_t RIGHT_DOUBLE_SPLIT = (CENTER_SPLIT | RIGHT_SPLIT); +static const uint64_t LEFT_DOUBLE_SPLIT = (CENTER_SPLIT | LEFT_SPLIT); +static const uint64_t TRIPLE_SPLIT = (CENTER_SPLIT | RIGHT_SPLIT | LEFT_SPLIT); + +// Split buffer slots +static const uint64_t SPLIT_COUNTER = 0; +static const uint64_t SIMPLIFY_COUNTER = 1; +static const uint64_t CLASSIFY_COUNTER_OFFSET = 2; + +int ClassifyBisector(in BisectorGeometry tri, uint depth) +{ + // Check the triangle's visibility + 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); + + // Here we don't use 0 as it introduces stability issues at grazing angles + if (FdotV < 0.0 && VdotN < -1e-3) + return BACK_FACE_CULLED; + + // Compute the triangle's AABB + float3 aabbMin = 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)); + float3 aabbMax = 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)); + + // First we do a frustum culling pass + //if (!FrustumAABBIntersect(_FrustumPlanes, aabbMin, aabbMax)) + // return FRUSTUM_CULLED; + + // Project the points on screen + float4x4 viewProjectionMatrix = mul(pViewParams.projectionMatrix, pViewParams.viewMatrix); + float4 p0P = mul(viewProjectionMatrix, 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); + + // 2D area of the triangle + // here we didn't reverse the sign of the y coordinate at projection time, but we simply adapted the area evaluation + // The same way we don't multiply by the screen size before, but after which is equivalent + 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; + + // We over estimate the area at grazing angles + float areaOverestimation = lerp(2.0, 1.0, pow(VdotN, 0.2)); + area *= areaOverestimation; + + // If the triangle's area is bigger than the target size and the depth is not the maximal depth, subdivide + if (pParams.update.triangleSize < area && depth < pParams.update.maxSubdivisionDepth) + { + // If the area is really big, put it in high priority. + return BISECT_ELEMENT; + } + else if ((pParams.update.triangleSize * 0.5 > area) || (depth > pParams.update.maxSubdivisionDepth)) + { + // Transform the parent's point + float4 p3P = mul(viewProjectionMatrix, float4(tri.p[3], 1.0)); + p3P.xy = p3P.xy / p3P.w; + p3P.xy = (p3P.xy * 0.5 + 0.5); + + // Evaluate the parent area + 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; + + // If the depth is too high (max depth changed) or the area is too + return ((pParams.update.triangleSize >= areaParent ) || (depth > pParams.update.maxSubdivisionDepth)) ? TOO_SMALL : UNCHANGED_ELEMENT; + } + return UNCHANGED_ELEMENT; +} + +void ResetBuffers() +{ + 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; +} + +void ClassifyElement(uint currentID, BisectorGeometry bis, uint totalNumElements, uint baseDepth) +{ + // Evaluate the depth of the element + uint64_t heapID = pParams.heapIDBuffer[currentID]; + uint depth = HeapIDDepth(heapID); + BisectorData cbisectorData = pParams.bisectorDataBuffer[currentID]; + + // Reset some values + cbisectorData.subdivisionPattern = 0; + cbisectorData.bisectorState = UNCHANGED_ELEMENT; + cbisectorData.problematicNeighbor = INVALID_POINTER; + cbisectorData.flags = VISIBLE_BISECTOR; + + // Does this triangle intersect the circle? + int currentValidity = ClassifyBisector(bis, depth); + if (currentValidity > UNCHANGED_ELEMENT) + { + // This element should be bisected + uint targetSlot = 0; + 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; + + // What's the validity of the father? + if (baseDepth != depth && currentValidity < UNCHANGED_ELEMENT) + { + // Mark that it requires simplification + cbisectorData.bisectorState = SIMPLIFY_ELEMENT; + + // Only register it if it has an even heapID, the odd ones will be processed by the even ones + if (heapID % 2 == 0) + { + uint targetSlot = 0; + InterlockedAdd(pParams.classificationBuffer[SIMPLIFY_COUNTER], 1, targetSlot); + pParams.classificationBuffer[CLASSIFY_COUNTER_OFFSET + totalNumElements + targetSlot] = currentID; + } + } + + // Update the bisector data + pParams.bisectorDataBuffer[currentID] = cbisectorData; +} + +void SplitElement(uint currentID, uint baseDepth) +{ + // Get the neighbors information + uint3 cNeighbors = pParams.neighboursBuffer[currentID].xyz; + + // If there is a neighbor X + if (cNeighbors.x != INVALID_POINTER) + { + // This is on the path of it's neighbor X + uint3 xNeighbors = pParams.neighboursBuffer[cNeighbors.x].xyz; + if (xNeighbors.z == currentID && pParams.bisectorDataBuffer[cNeighbors.x].bisectorState != UNCHANGED_ELEMENT) + return; + } + + // If there is a neighbor Y + if (cNeighbors.y != INVALID_POINTER) + { + // This is on the path of it's neighbor Y + uint3 yNeighbors = pParams.neighboursBuffer[cNeighbors.y].xyz; + if (yNeighbors.z == currentID && pParams.bisectorDataBuffer[cNeighbors.y].bisectorState != UNCHANGED_ELEMENT) + return; + } + + // Depth of the current triangle + uint64_t heapID = pParams.heapIDBuffer[currentID]; + uint currentDepth = HeapIDDepth(heapID); + + // Compute the maximal required memory for this subdivision + int maxRequiredMemory = 2 * (currentDepth - baseDepth) - 1; + + // Get the twin information + uint twinID = cNeighbors.z; + + // This avoid the massive over-reservation and saves a bunch of artifacts + if (twinID == INVALID_POINTER) + maxRequiredMemory = 1; + else if (pParams.neighboursBuffer[twinID].z == currentID) + maxRequiredMemory = 2; + + // Try to reserve + int remainingMemory; + InterlockedAdd(pParams.memoryBuffer[1], -maxRequiredMemory, remainingMemory); + + // Did someone manage to sneak-in while we were trying to pick the memory, add it back and try again + if (remainingMemory < maxRequiredMemory) + { + // Then add back the required memory and stop + InterlockedAdd(pParams.memoryBuffer[1], maxRequiredMemory, remainingMemory); + return; + } + + // Let's actually count the memory that we will be using + uint usedMemory = 1; + uint prevPattern; + InterlockedOr(pParams.bisectorDataBuffer[currentID].subdivisionPattern, CENTER_SPLIT, prevPattern); + + // If this is not zero, it means an other neighbor went faster than us, we restore the memory and leave. + if (prevPattern != 0) + { + InterlockedAdd(pParams.memoryBuffer[1], maxRequiredMemory, remainingMemory); + return; + } + + // Mark this for allocation + uint targetLocation = 0; + InterlockedAdd(pParams.allocateBuffer[0], 1, targetLocation); + pParams.allocateBuffer[1 + targetLocation] = currentID; + + // While we're not done (up the tree or everything is subdivided properly) + bool done = false; + while (!done) + { + // If this neighbor is not allocated, we're done. + if (twinID == INVALID_POINTER) + break; + + // Grab the bisector of the neighbor + uint64_t nHeapID = pParams.heapIDBuffer[twinID]; + BisectorData nBisectorData = pParams.bisectorDataBuffer[twinID]; + uint nDepth = HeapIDDepth(nHeapID); + uint3 nNeighbors = pParams.neighboursBuffer[twinID].xyz; + + // If both triangles have the same depth + if (nDepth == currentDepth) + { + // Raised the center split + InterlockedOr(pParams.bisectorDataBuffer[twinID].subdivisionPattern, CENTER_SPLIT, prevPattern); + + // Only account for it if it was not raised before. + if (prevPattern == 0) + { + // Mark this for allocation + uint targetLocation = 0; + InterlockedAdd(pParams.allocateBuffer[0], 1, targetLocation); + pParams.allocateBuffer[1 + targetLocation] = twinID; + usedMemory++; + } + + // And we're done + done = true; + } + // If this node has already been subdivided, it means that we need to add the third subdivision and we're done + else + { + if (nNeighbors[0] == currentID) + InterlockedOr(pParams.bisectorDataBuffer[twinID].subdivisionPattern, RIGHT_DOUBLE_SPLIT, prevPattern); + else // if (nNeighbors[1] == currentID) + InterlockedOr(pParams.bisectorDataBuffer[twinID].subdivisionPattern, LEFT_DOUBLE_SPLIT, prevPattern); + + if (prevPattern != 0) + { + usedMemory++; + done = true; + } + else + { + // Mark this for allocation + uint targetLocation = 0; + InterlockedAdd(pParams.allocateBuffer[0], 1, targetLocation); + pParams.allocateBuffer[1 + targetLocation] = twinID; + + // Account for two splits + usedMemory += 2; + + // the new bisector that needs to be propagated + currentID = twinID; + currentDepth = nDepth; + twinID = pParams.neighboursBuffer[currentID].z; + } + } + } + + // Add back the unused memory (in case) + InterlockedAdd(pParams.memoryBuffer[1], max(maxRequiredMemory - usedMemory, 0), remainingMemory); +} + +void AllocateElement(uint currentID) +{ + // Load the bisector for this element + BisectorData bisectorData = pParams.bisectorDataBuffer[currentID]; + + // Does this guy need to be subdivided + if (bisectorData.subdivisionPattern != 0) + { + // How many bits do we need? + int numSlots = countbits(bisectorData.subdivisionPattern); + + // Request the number of bits we need using an interlock add + uint firstBitIndex = 0; + InterlockedAdd(pParams.memoryBuffer[0], numSlots, firstBitIndex); + + // llocate the bits we need + for (uint bitId = 0; bitId < numSlots; ++bitId) + { + uint index = decode_bit_complement(firstBitIndex + bitId); + bisectorData.indices[bitId] = index; + } + + // Output + pParams.bisectorDataBuffer[currentID] = bisectorData; + } +} + +#define SUBLING0_ID 0 +#define SUBLING1_ID 1 +#define SUBLING2_ID 2 +void evaluate_neighbors(uint currentID, uint bisectorID, out uint resX, out uint resY) +{ + BisectorData nBisectorData = pParams.bisectorDataBuffer[bisectorID]; + uint3 nNeighbors = pParams.neighboursBuffer[bisectorID].xyz; + if (nBisectorData.subdivisionPattern == 0x01) + { + resX = nBisectorData.indices[SUBLING0_ID]; + resY = bisectorID; + } + else if (nBisectorData.subdivisionPattern == 0x03) + { + if (nNeighbors[0] == currentID) + { + resX = nBisectorData.indices[SUBLING1_ID]; + resY = bisectorID; + } + else + { + resX = nBisectorData.indices[SUBLING0_ID]; + resY = nBisectorData.indices[SUBLING1_ID]; + } + } + else if (nBisectorData.subdivisionPattern == 0x05) + { + if (nNeighbors[1] == currentID) + { + resX = nBisectorData.indices[SUBLING1_ID]; + resY = nBisectorData.indices[SUBLING0_ID]; + } + else + { + resX = nBisectorData.indices[SUBLING0_ID]; + resY = bisectorID; + } + } + else + { + if (nNeighbors[0] == currentID) + { + resX = nBisectorData.indices[SUBLING1_ID]; + resY = bisectorID; + } + else if (nNeighbors[1] == currentID) + { + resX = nBisectorData.indices[SUBLING2_ID]; + resY = nBisectorData.indices[SUBLING0_ID]; + } + else + { + resX = nBisectorData.indices[SUBLING0_ID]; + resY = nBisectorData.indices[SUBLING1_ID]; + } + } +} + +void BisectElement(uint currentID) +{ + // If this bisector is not allocated or not subdivided, stop right away + uint64_t baseHeapID = pParams.heapIDBuffer[currentID]; + BisectorData cBisectorData = pParams.bisectorDataBuffer[currentID]; + if (baseHeapID == 0 || cBisectorData.subdivisionPattern == NO_SPLIT) + return; + + // Load the bisector data of the target triangle + uint currentSubdiv = cBisectorData.subdivisionPattern; + + // neighbors of the parent + uint3 cNeighbors = pParams.neighboursBuffer[currentID].xyz; + uint p_n0 = cNeighbors[0]; + uint p_n1 = cNeighbors[1]; + uint p_n2 = cNeighbors[2]; + + // Get the main axis subdiv + uint siblingID0 = cBisectorData.indices[0]; + uint siblingID1 = cBisectorData.indices[1]; + uint siblingID2 = cBisectorData.indices[2]; + + // Simple subdivision (along the main axis) + if (currentSubdiv == CENTER_SPLIT) + { + uint resX = INVALID_POINTER, resY = INVALID_POINTER; + if (p_n2 != INVALID_POINTER) + evaluate_neighbors(currentID, p_n2, resX, resY); + + // Set the heap IDs + pParams.heapIDBuffer[currentID] = 2 * baseHeapID; + pParams.heapIDBuffer[siblingID0] = 2 * baseHeapID + 1; + + // Update the neighbors + uint3 modifiedNeighbors; + modifiedNeighbors[0] = siblingID0; + modifiedNeighbors[1] = resX; + modifiedNeighbors[2] = p_n0; + pParams.neighboursOutputBuffer[currentID] = uint4(modifiedNeighbors, 0); + modifiedNeighbors[0] = resY; + modifiedNeighbors[1] = currentID; + modifiedNeighbors[2] = p_n1; + pParams.neighboursOutputBuffer[siblingID0] = uint4(modifiedNeighbors, 0); + + // Keep track of the parent + 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; + + // Mark this for propagation + uint targetLocation = 0; + InterlockedAdd(pParams.propagateBuffer[0], 1, targetLocation); + pParams.propagateBuffer[2 + targetLocation] = siblingID0; + } + else if (currentSubdiv == RIGHT_DOUBLE_SPLIT) + { + // Grab the bisector of the twin + uint res0X = INVALID_POINTER, res0Y = INVALID_POINTER; + evaluate_neighbors(currentID, p_n0, res0X, res0Y); + + uint res1X = INVALID_POINTER, res1Y = INVALID_POINTER; + if (p_n2 != INVALID_POINTER) + evaluate_neighbors(currentID, p_n2, res1X, res1Y); + + // Set the heap IDs + 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] = uint4(modifiedNeighbors, 0); + modifiedNeighbors[0] = res1Y; + modifiedNeighbors[1] = currentID; + modifiedNeighbors[2] = p_n1; + pParams.neighboursOutputBuffer[siblingID0] = uint4(modifiedNeighbors, 0); + modifiedNeighbors[0] = res0Y; + modifiedNeighbors[1] = currentID; + modifiedNeighbors[2] = res1X; + pParams.neighboursOutputBuffer[siblingID1] = uint4(modifiedNeighbors, 0); + + // Keep track of the parent + BisectorData modifiedBisector = cBisectorData; + modifiedBisector.propagationID = currentID; + + // Lower the element down the tree and update it's sibling + modifiedBisector.problematicNeighbor = INVALID_POINTER; + modifiedBisector.flags = (VISIBLE_BISECTOR | MODIFIED_BISECTOR); + pParams.bisectorDataBuffer[currentID] = modifiedBisector; + + // Create the sibling of the current element + modifiedBisector.problematicNeighbor = p_n1; + modifiedBisector.flags = (VISIBLE_BISECTOR | MODIFIED_BISECTOR); + pParams.bisectorDataBuffer[siblingID0] = modifiedBisector; + + // Create the sibling of the current element + modifiedBisector.problematicNeighbor = INVALID_POINTER; + modifiedBisector.flags = (VISIBLE_BISECTOR | MODIFIED_BISECTOR); + pParams.bisectorDataBuffer[siblingID1] = modifiedBisector; + + // Mark this for propagation + uint targetLocation = 0; + InterlockedAdd(pParams.propagateBuffer[0], 1, targetLocation); + pParams.propagateBuffer[2 + targetLocation] = siblingID0; + } + else if (currentSubdiv == LEFT_DOUBLE_SPLIT) + { + // Grab the bisector of the twin + uint res0X = INVALID_POINTER, res0Y = INVALID_POINTER; + evaluate_neighbors(currentID, p_n1, res0X, res0Y); + + uint res1X = INVALID_POINTER, res1Y = INVALID_POINTER; + if (p_n2 != INVALID_POINTER) + evaluate_neighbors(currentID, p_n2, res1X, res1Y); + + // Set the heap IDs + 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] = uint4(modifiedNeighbors, 0); + modifiedNeighbors[0] = siblingID1; + modifiedNeighbors[1] = res0X; + modifiedNeighbors[2] = res1Y; + pParams.neighboursOutputBuffer[siblingID0] = uint4(modifiedNeighbors, 0); + modifiedNeighbors[0] = res0Y; + modifiedNeighbors[1] = siblingID0; + modifiedNeighbors[2] = currentID; + pParams.neighboursOutputBuffer[siblingID1] = uint4(modifiedNeighbors, 0); + + // Keep track of the parent + BisectorData modifiedBisector = cBisectorData; + modifiedBisector.propagationID = currentID; + + // Lower the element down the tree and update it's sibling + modifiedBisector.problematicNeighbor = INVALID_POINTER; + modifiedBisector.flags = (VISIBLE_BISECTOR | MODIFIED_BISECTOR); + pParams.bisectorDataBuffer[currentID] = modifiedBisector; + + // Create the sibling of the current element + modifiedBisector.problematicNeighbor = INVALID_POINTER; + modifiedBisector.flags = (VISIBLE_BISECTOR | MODIFIED_BISECTOR); + pParams.bisectorDataBuffer[siblingID0] = modifiedBisector; + + // Create the sibling of the current element + modifiedBisector.problematicNeighbor = INVALID_POINTER; + modifiedBisector.flags = (VISIBLE_BISECTOR | MODIFIED_BISECTOR); + pParams.bisectorDataBuffer[siblingID1] = modifiedBisector; + } + else if (currentSubdiv == TRIPLE_SPLIT) + { + // Grab the bisector of the twin + uint res0X = INVALID_POINTER, res0Y = INVALID_POINTER; + evaluate_neighbors(currentID, p_n0, res0X, res0Y); + + uint res1X = INVALID_POINTER, res1Y = INVALID_POINTER; + evaluate_neighbors(currentID, p_n1, res1X, res1Y); + + uint res2X = INVALID_POINTER, res2Y = INVALID_POINTER; + if (p_n2 != INVALID_POINTER) + evaluate_neighbors(currentID, p_n2, res2X, res2Y); + + // Set the heap IDs + 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] = uint4(modifiedNeighbors, 0); + modifiedNeighbors[0] = siblingID2; + modifiedNeighbors[1] = res1X; + modifiedNeighbors[2] = res2Y; + pParams.neighboursOutputBuffer[siblingID0] = uint4(modifiedNeighbors, 0); + modifiedNeighbors[0] = res0Y; + modifiedNeighbors[1] = currentID; + modifiedNeighbors[2] = res2X; + pParams.neighboursOutputBuffer[siblingID1] = uint4(modifiedNeighbors, 0); + modifiedNeighbors[0] = res1Y; + modifiedNeighbors[1] = siblingID0; + modifiedNeighbors[2] = currentID; + pParams.neighboursOutputBuffer[siblingID2] = uint4(modifiedNeighbors, 0); + + // Keep track of the parent + BisectorData modifiedBisector = cBisectorData; + modifiedBisector.propagationID = currentID; + + // Lower the element down the tree and update it's sibling + modifiedBisector.problematicNeighbor = INVALID_POINTER; + modifiedBisector.flags = (VISIBLE_BISECTOR | MODIFIED_BISECTOR); + pParams.bisectorDataBuffer[currentID] = modifiedBisector; + + // Create the sibling of the current element + modifiedBisector.problematicNeighbor = INVALID_POINTER; + modifiedBisector.flags = (VISIBLE_BISECTOR | MODIFIED_BISECTOR); + pParams.bisectorDataBuffer[siblingID0] = modifiedBisector; + + // Create the sibling of the current element + modifiedBisector.problematicNeighbor = INVALID_POINTER; + modifiedBisector.flags = (VISIBLE_BISECTOR | MODIFIED_BISECTOR); + pParams.bisectorDataBuffer[siblingID1] = modifiedBisector; + + // Create the sibling of the current element + modifiedBisector.problematicNeighbor = INVALID_POINTER; + modifiedBisector.flags = (VISIBLE_BISECTOR | MODIFIED_BISECTOR); + pParams.bisectorDataBuffer[siblingID2] = modifiedBisector; + } + + // How many bits do we need to raise + uint numSiblings = countbits(currentSubdiv); + for (uint siblingIdx = 0; siblingIdx < numSiblings; ++siblingIdx) + { + set_bit_atomic_buffer(cBisectorData.indices[siblingIdx], true); + } +} + +void PropagateBisectElement(uint currentID) +{ + // Load the bisector data of the target triangle + BisectorData cBisectorData = pParams.bisectorDataBuffer[currentID]; + + // neighbors of the parent + uint parentID = cBisectorData.propagationID; + uint problematicNeighbor = cBisectorData.problematicNeighbor; + + // Read the neighbor that may have changed + BisectorData tBisectorData = pParams.bisectorDataBuffer[problematicNeighbor]; + uint3 tNeighbors = pParams.neighboursBuffer[problematicNeighbor].xyz; + uint targetID = problematicNeighbor; + uint sibling1 = tBisectorData.indices[1]; + + if (tBisectorData.subdivisionPattern == NO_SPLIT) + { + if (tNeighbors[0] == parentID) + pParams.neighboursBuffer[targetID][0] = currentID; + if (tNeighbors[1] == parentID) + pParams.neighboursBuffer[targetID][1] = currentID; + if (tNeighbors[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; + } + + // Reset the problematic neighbor and the bisection state + pParams.bisectorDataBuffer[currentID].problematicNeighbor = INVALID_POINTER; + pParams.bisectorDataBuffer[currentID].bisectorState = UNCHANGED_ELEMENT; +} + +void PrepareSimplifyElement(uint currentID) +{ + // Get the bisector + BisectorData cBisectorData = pParams.bisectorDataBuffer[currentID]; + + // Grab the current bisector + uint64_t cHeapID = pParams.heapIDBuffer[currentID]; + + // If this is not an even heap number it will be handeled by it's pair, the twin or the twin's pair + + // Neighbors of this element + uint3 cNeighbors = pParams.neighboursBuffer[currentID].xyz; + + // Evaluate the depth of this bisector + uint currentDepth = HeapIDDepth(cHeapID); + + // Grab the pair neighbor (it has to exist) + uint pairID = cNeighbors[0]; + uint64_t pHeapID = pParams.heapIDBuffer[pairID]; + BisectorData pBisectorData = pParams.bisectorDataBuffer[pairID]; + uint3 pNeighbors = pParams.neighboursBuffer[pairID].xyz; + + // Evaluate the depth of the pair + uint pairDepth = HeapIDDepth(pHeapID); + + // If they are not at the same depth or the pair is not to be simplified, we're done + if (pairDepth != currentDepth || pBisectorData.bisectorState != SIMPLIFY_ELEMENT) + return; + + // We need to identify our twin pair + uint twinLowID = pNeighbors[0]; + uint twinHighID = cNeighbors[1]; + if (twinLowID != INVALID_POINTER) + { + // Grab the two bisectors + uint64_t twinLowHeapID = pParams.heapIDBuffer[twinLowID]; + uint64_t twinHighHeapID = pParams.heapIDBuffer[twinHighID]; + + // The current bisector is not the smallest element of the neighborhood, he will be handeled by twinLowBisect if needed + if (cHeapID > twinLowHeapID) + return; + + // Compute the depth of both neighbors + uint lowFacingDepth = HeapIDDepth(twinLowHeapID); + uint highFacingDepth = HeapIDDepth(twinHighHeapID); + + // If all four elements are not on the same + if (lowFacingDepth != currentDepth || highFacingDepth != currentDepth) + return; + + // Grab the two bisectors + BisectorData twinLowBisectData = pParams.bisectorDataBuffer[twinLowID]; + BisectorData twinHighBisectData = pParams.bisectorDataBuffer[twinHighID]; + + // This element should not be doing the simplifications if: + // - One of the four elements doesn't have the same depth + // - One of the four elements isn't flagged for simplification + if (twinLowBisectData.bisectorState != SIMPLIFY_ELEMENT + || twinHighBisectData.bisectorState != SIMPLIFY_ELEMENT) + return; + } + + // This element will simplify itself, it's pair and possibilty it's twin and twin-pair. + uint bisectorSlot; + InterlockedAdd(pParams.simplifyBuffer[0], 1, bisectorSlot); + + // Log the bisector ID + pParams.simplifyBuffer[1 + bisectorSlot] = currentID; +} + +void SimplifyElement(uint currentID) +{ + // Grab the current bisector + BisectorData cBisectorData = pParams.bisectorDataBuffer[currentID]; + uint3 cNeighbors = pParams.neighboursBuffer[currentID].xyz; + + // Grab the pair neighbor (it has to exist) + uint pairID = cNeighbors[0]; + BisectorData pBisectorData = pParams.bisectorDataBuffer[pairID]; + uint3 pNeighbors = pParams.neighboursBuffer[pairID].xyz; + + // We need to indentify our twin pair + uint twinLowID = pNeighbors[0]; + uint twinHighID = cNeighbors[1]; + + // Set the heap IDs + pParams.heapIDBuffer[currentID] = pParams.heapIDBuffer[currentID] / 2; + pParams.heapIDBuffer[pairID] = 0; + + // All conditions are met for us to simplify these triangles + uint3 newNeighbors; + newNeighbors[0] = cNeighbors[2]; + newNeighbors[1] = pNeighbors[2]; + newNeighbors[2] = twinLowID; + pParams.neighboursBuffer[currentID] = uint4(newNeighbors, 0); + + // Update the bisector data + cBisectorData.propagationID = pairID; + cBisectorData.problematicNeighbor = pNeighbors[2]; + cBisectorData.bisectorState = MERGED_ELEMENT; + cBisectorData.flags = (VISIBLE_BISECTOR | MODIFIED_BISECTOR); + pParams.bisectorDataBuffer[currentID] = cBisectorData; + + // Mark this for propagation + if (cBisectorData.problematicNeighbor != INVALID_POINTER) + { + // Mark this for propagation + uint targetLocation = 0; + InterlockedAdd(pParams.propagateBuffer[1], 1, targetLocation); + pParams.propagateBuffer[2 + targetLocation] = currentID; + } + + // Clear the pair's heap for identification + pBisectorData.bisectorState = MERGED_ELEMENT; + pBisectorData.flags = 0; + pParams.bisectorDataBuffer[pairID] = pBisectorData; + + // Don't forget to free the bit + set_bit_atomic_buffer(pairID, false); + + // If there was a facing pair, simplify it aswell + if (twinLowID != INVALID_POINTER) + { + // Set the heap IDs + pParams.heapIDBuffer[twinLowID] = pParams.heapIDBuffer[twinLowID] / 2; + pParams.heapIDBuffer[twinHighID] = 0; + + // Read both bisectors + BisectorData lowFacingBst = pParams.bisectorDataBuffer[twinLowID]; + uint3 lfNeighbors = pParams.neighboursBuffer[twinLowID].xyz; + BisectorData highFacingBst = pParams.bisectorDataBuffer[twinHighID]; + uint3 hfNeighbors = pParams.neighboursBuffer[twinHighID].xyz; + + // Update the lowest ID + newNeighbors[0] = lfNeighbors[2]; + newNeighbors[1] = hfNeighbors[2]; + newNeighbors[2] = currentID; + pParams.neighboursBuffer[twinLowID] = uint4(newNeighbors, 0); + + // Update the twin bisector data + lowFacingBst.propagationID = twinHighID; + lowFacingBst.problematicNeighbor = hfNeighbors[2]; + lowFacingBst.bisectorState = MERGED_ELEMENT; + lowFacingBst.flags = (VISIBLE_BISECTOR | MODIFIED_BISECTOR); + pParams.bisectorDataBuffer[twinLowID] = lowFacingBst; + + if (lowFacingBst.problematicNeighbor != INVALID_POINTER) + { + // Mark this for propagation + uint targetLocation = 0; + InterlockedAdd(pParams.propagateBuffer[1], 1, targetLocation); + pParams.propagateBuffer[2 + targetLocation] = twinLowID; + } + + // Clear the pair's heap for identification + highFacingBst.bisectorState = MERGED_ELEMENT; + highFacingBst.flags = 0; + pParams.bisectorDataBuffer[twinHighID] = highFacingBst; + + // Don't forget to free the bit + set_bit_atomic_buffer(twinHighID, false); + } +} + +void PropagateElementSimplify(uint currentID) +{ + // Load the bisector data of the target element + BisectorData cBisectorData = pParams.bisectorDataBuffer[currentID]; + + // Id of the element before the simplification + uint deletedPair = cBisectorData.propagationID; + + // neighbors of the parent + uint neighborID = cBisectorData.problematicNeighbor; + + // Read the neighbor that may have changed + BisectorData nBisectorData = pParams.bisectorDataBuffer[neighborID]; + uint3 nNeighbors = pParams.neighboursBuffer[neighborID].xyz; + + // The neighbor has not changed, so we just need to make it point on currentID instead of the pair that was deleted + if (nBisectorData.bisectorState != MERGED_ELEMENT) + { + for (uint i = 0; i < 3; ++i) + { + if (nNeighbors[i] == deletedPair) + pParams.neighboursBuffer[neighborID][i] = currentID; + } + } + // The neighbor has had a simplification, so we need to update a different neighbor based on if it went up one depth in the tree or was deleted. + else if (nBisectorData.bisectorState == MERGED_ELEMENT) + { + // He still exist, but was simplified + if (pParams.heapIDBuffer[neighborID] != 0) + { + for (uint i = 0; i < 3; ++i) + { + if (nNeighbors[i] == deletedPair) + pParams.neighboursBuffer[neighborID][i] = currentID; + } + } + // He is gone, we need to update his pair instead of him. + else + { + uint neighborPair = nNeighbors[1]; + for (uint i = 0; i < 3; ++i) + { + if (pParams.neighboursBuffer[neighborPair][i] == deletedPair) + pParams.neighboursBuffer[neighborPair][i] = currentID; + } + } + } + + // Reset the problematic neighbor + pParams.bisectorDataBuffer[currentID].problematicNeighbor = INVALID_POINTER; +} + +void BisectorElementIndexation(uint currentID) +{ + // Grab the current heap ID + uint64_t cHeapID = pParams.heapIDBuffer[currentID]; + + // Deallocated element, we don't care + if (cHeapID == 0) + return; + + // Reserve a slot for this bisector + uint bisectorSlot; + InterlockedAdd(pParams.indirectDrawBuffer[0], 3, bisectorSlot); + + // Keep track of it's global ID + pParams.bisectorIndicesBuffer[bisectorSlot / 3] = currentID; + + // Load the bisector data of the target element + BisectorData cBisectorData = pParams.bisectorDataBuffer[currentID]; + + // Is it visible? + if ((cBisectorData.flags & VISIBLE_BISECTOR) == 0) + return; + + // Reserve a slot for this visible bisector + InterlockedAdd(pParams.indirectDrawBuffer[4], 3, bisectorSlot); + + // Keep track of it's global ID + pParams.visibleBisectorIndices[bisectorSlot / 3] = currentID; + + // Is it visible? + if ((cBisectorData.flags & MODIFIED_BISECTOR) == 0) + return; + + // Reserve a slot for this visible bisector + InterlockedAdd(pParams.indirectDrawBuffer[8], 4, bisectorSlot); + + // Keep track of it's global ID + pParams.modifiedBisectorIndices[bisectorSlot / 4] = currentID; +} + +void ValidateBisector(uint currentID) +{ + // Grab the current heap ID + uint64_t cHeapID = pParams.heapIDBuffer[currentID]; + + // Deallocated element, we don't care + if (cHeapID == 0) + return; + + // Load the bisector data of the target element + uint3 cNeighbors = pParams.neighboursBuffer[currentID].xyz; + + bool failed = false; + uint targetNeighbor = INVALID_POINTER; + uint targetIdx = INVALID_POINTER; + for (uint i = 0; i < 3; ++i) + { + uint neighborID = cNeighbors[i]; + if (neighborID != INVALID_POINTER) + { + bool found = false; + uint3 nNeighbors = pParams.neighboursBuffer[neighborID].xyz; + for (uint j = 0; j < 3; ++j) + { + if (nNeighbors[j] == currentID) + found = true; + } + if (!found) + { + failed = true; + targetNeighbor = neighborID; + targetIdx = i; + break; + } + } + } + + // Notify the failure + if (failed) + { + uint prevValue; + InterlockedAdd(pParams.validationBuffer[0], 1, prevValue); + } +} \ No newline at end of file diff --git a/src/Engine/Graphics/CBT/CBT.cpp b/src/Engine/Graphics/CBT/CBT.cpp index 3116a55..a6a0886 100644 --- a/src/Engine/Graphics/CBT/CBT.cpp +++ b/src/Engine/Graphics/CBT/CBT.cpp @@ -281,27 +281,27 @@ void MeshUpdater::init(Gfx::PGraphics gfx, Gfx::PDescriptorLayout viewParamsLayo graphics->beginShaderCompilation(ShaderCompilationInfo{ .name = "CBTCompute", - .modules = {"CBTCompute"}, + .modules = {"CBTCompute", "LEB"}, .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"}, + {"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"}, + {"ClearBuffer", "LEB"}, + {"EvaluateLEB", "LEB"}, }, .rootSignature = pipelineLayout, }); @@ -412,9 +412,11 @@ void MeshUpdater::evaluateLeb(const BaseMesh& baseMesh, CBTMesh& mesh, Gfx::PDes clearCmd->bindPipeline(lebClear); clearCmd->bindDescriptor(set); clearCmd->dispatch((mesh.totalNumElements * 3 + WORKGROUP_SIZE - 1) / WORKGROUP_SIZE, 1, 1); + graphics->executeCommands(std::move(clearCmd)); 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); } + graphics->waitDeviceIdle(); Gfx::PDescriptorSet set = layout->allocateDescriptorSet(); set->updateBuffer(GEOMETRY_CB, 0, geometryBuffer); set->updateBuffer(UPDATE_CB, 0, updateBuffer); @@ -432,7 +434,7 @@ void MeshUpdater::evaluateLeb(const BaseMesh& baseMesh, CBTMesh& mesh, Gfx::PDes evalCmd->pushConstants(Gfx::SE_SHADER_STAGE_COMPUTE_BIT, 0, sizeof(uint32), &val); 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, + mesh.lebVertexBuffer->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); } @@ -456,7 +458,6 @@ void MeshUpdater::update(CBTMesh& mesh, Gfx::PDescriptorSet viewParamsSet, Gfx:: set->updateBuffer(HEAP_ID_BUFFER, 0, mesh.heapIDBuffer); set->updateBuffer(BISECTOR_DATA_BUFFER, 0, mesh.updateBuffer); set->updateBuffer(CLASSIFICATION_BUFFER, 0, mesh.classificationBuffer); - set->updateBuffer(DEBUG_BUFFER, 0, debugBuffer); set->writeChanges(); Gfx::OComputeCommand classifyCmd = graphics->createComputeCommand("Classify"); classifyCmd->bindPipeline(classify); @@ -482,6 +483,7 @@ void MeshUpdater::update(CBTMesh& mesh, Gfx::PDescriptorSet viewParamsSet, Gfx:: Gfx::SE_ACCESS_SHADER_READ_BIT, Gfx::SE_PIPELINE_STAGE_COMPUTE_SHADER_BIT); } + graphics->waitDeviceIdle(); // split pass { Gfx::PDescriptorSet set = layout->allocateDescriptorSet(); @@ -494,6 +496,7 @@ void MeshUpdater::update(CBTMesh& mesh, Gfx::PDescriptorSet viewParamsSet, Gfx:: set->updateBuffer(NEIGHBOURS_BUFFER, 0, currentNeighborsBuffer); set->updateBuffer(MEMORY_BUFFER, 0, memoryBuffer); set->updateBuffer(ALLOCATE_BUFFER, 0, mesh.allocateBuffer); + set->updateBuffer(DEBUG_BUFFER, 0, debugBuffer); set->writeChanges(); Gfx::OComputeCommand splitCmd = graphics->createComputeCommand("Split"); splitCmd->bindPipeline(split); @@ -502,7 +505,10 @@ void MeshUpdater::update(CBTMesh& mesh, Gfx::PDescriptorSet viewParamsSet, Gfx:: 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); + mesh.allocateBuffer->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); } + graphics->waitDeviceIdle(); // prepare indirect pass { @@ -518,6 +524,7 @@ void MeshUpdater::update(CBTMesh& mesh, Gfx::PDescriptorSet viewParamsSet, Gfx:: 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); } + graphics->waitDeviceIdle(); // Allocate Pass { @@ -539,6 +546,7 @@ void MeshUpdater::update(CBTMesh& mesh, Gfx::PDescriptorSet viewParamsSet, Gfx:: 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); } + graphics->waitDeviceIdle(); // copy currentNeighborsBuffer->pipelineBarrier(Gfx::SE_ACCESS_SHADER_WRITE_BIT, Gfx::SE_PIPELINE_STAGE_COMPUTE_SHADER_BIT, @@ -546,6 +554,7 @@ void MeshUpdater::update(CBTMesh& mesh, Gfx::PDescriptorSet viewParamsSet, Gfx:: 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); + graphics->waitDeviceIdle(); // bisect { @@ -570,6 +579,7 @@ void MeshUpdater::update(CBTMesh& mesh, Gfx::PDescriptorSet viewParamsSet, Gfx:: 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); } + graphics->waitDeviceIdle(); // Prepare indirect pass { @@ -585,6 +595,7 @@ void MeshUpdater::update(CBTMesh& mesh, Gfx::PDescriptorSet viewParamsSet, Gfx:: 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); } + graphics->waitDeviceIdle(); // propagate split pass { @@ -604,6 +615,7 @@ void MeshUpdater::update(CBTMesh& mesh, Gfx::PDescriptorSet viewParamsSet, Gfx:: 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); } + graphics->waitDeviceIdle(); // prepare simplify { @@ -625,6 +637,7 @@ void MeshUpdater::update(CBTMesh& mesh, Gfx::PDescriptorSet viewParamsSet, Gfx:: 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); } + graphics->waitDeviceIdle(); // Prepare Indirect Simplify { @@ -640,6 +653,7 @@ void MeshUpdater::update(CBTMesh& mesh, Gfx::PDescriptorSet viewParamsSet, Gfx:: 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); } + graphics->waitDeviceIdle(); // Simplify Pass { @@ -663,6 +677,7 @@ void MeshUpdater::update(CBTMesh& mesh, Gfx::PDescriptorSet viewParamsSet, Gfx:: 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); } + graphics->waitDeviceIdle(); // Prepare Indirect Propagate Simplify { @@ -678,6 +693,7 @@ void MeshUpdater::update(CBTMesh& mesh, Gfx::PDescriptorSet viewParamsSet, Gfx:: 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); } + graphics->waitDeviceIdle(); // Propagate Simplify Pass { @@ -698,6 +714,7 @@ void MeshUpdater::update(CBTMesh& mesh, Gfx::PDescriptorSet viewParamsSet, Gfx:: 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); } + graphics->waitDeviceIdle(); // Update Tree { @@ -730,10 +747,12 @@ void MeshUpdater::update(CBTMesh& mesh, Gfx::PDescriptorSet viewParamsSet, Gfx:: 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); } + graphics->waitDeviceIdle(); mesh.currentNeighborsBufferIdx = nextNeighborsBufferIdx; prepareIndirection(mesh, geometryCB); + graphics->waitDeviceIdle(); } void MeshUpdater::validation(const CBTMesh& mesh, Gfx::PUniformBuffer geometryCB) { diff --git a/src/Engine/Graphics/CBT/CBT.h b/src/Engine/Graphics/CBT/CBT.h index 8ebf215..07d6442 100644 --- a/src/Engine/Graphics/CBT/CBT.h +++ b/src/Engine/Graphics/CBT/CBT.h @@ -327,6 +327,7 @@ struct GeometryCB { uint32 totalNumVertices; }; struct UpdateCB { + Matrix4 viewProjectionMatrix; float triangleSize; uint32_t maxSubdivisionDepth; float fov; @@ -355,10 +356,10 @@ constexpr int64 BISECT_ELEMENT = 1; constexpr int64 SIMPLIFY_ELEMENT = 2; constexpr int64 MERGED_ELEMENT = 3; struct BisectorData { - uint32 subdivisionPattern; - UVector indices; + uint32 subdivisionPattern; + uint32 problematicNeighbor; uint32 bisectorState; diff --git a/src/Engine/Graphics/Initializer.h b/src/Engine/Graphics/Initializer.h index 4aa34db..bd45e80 100644 --- a/src/Engine/Graphics/Initializer.h +++ b/src/Engine/Graphics/Initializer.h @@ -97,7 +97,6 @@ struct ShaderBufferCreateInfo { DataSource sourceData = DataSource(); uint64 numElements = 1; uint32 clearValue = 0; - uint8 createCleared = 0; Gfx::SeBufferUsageFlags usage = 0; std::string name = "Unnamed"; }; diff --git a/src/Engine/Graphics/RenderPass/BasePass.cpp b/src/Engine/Graphics/RenderPass/BasePass.cpp index ae28c78..df55da5 100644 --- a/src/Engine/Graphics/RenderPass/BasePass.cpp +++ b/src/Engine/Graphics/RenderPass/BasePass.cpp @@ -99,7 +99,7 @@ void BasePass::beginFrame(const Component::Camera& cam) { transparentCulling = lightCullingLayout->allocateDescriptorSet(); //waterRenderer->beginFrame(); - terrainRenderer->beginFrame(viewParamsSet); + terrainRenderer->beginFrame(viewParamsSet, cam); // Debug vertices { diff --git a/src/Engine/Graphics/RenderPass/TerrainRenderer.cpp b/src/Engine/Graphics/RenderPass/TerrainRenderer.cpp index bf0772b..7db50ad 100644 --- a/src/Engine/Graphics/RenderPass/TerrainRenderer.cpp +++ b/src/Engine/Graphics/RenderPass/TerrainRenderer.cpp @@ -10,11 +10,19 @@ using namespace Seele; TerrainRenderer::TerrainRenderer(Gfx::PGraphics graphics, PScene scene, Gfx::PDescriptorLayout viewParamsLayout, Gfx::PDescriptorSet viewParamsSet) : graphics(graphics), scene(scene) { + //Gfx::OPipelineLayout test = graphics->createPipelineLayout(); + //graphics->beginShaderCompilation(ShaderCompilationInfo{ + // .modules = {"CompileTest"}, + // .entryPoints = {{"Reset", "CompileTest"}}, + // .rootSignature = test, + //}); + //graphics->createComputeShader({0}); meshUpdater.init(graphics, viewParamsLayout); lebCache.init(graphics, 5); CBT<18> cbt; CPUMesh cpuMesh = generateCPUMesh(cbt.numElements()); plainMesh.gpuCBT.lastLevelSize = cbt.lastLevelSize(); + plainMesh.gpuCBT.bufferCount = 2; for (uint32 i = 0; i < 2; ++i) { uint32 bufferSize = cbt.bufferSize(i); uint32 elementSize = cbt.elementSize(i); @@ -74,7 +82,6 @@ TerrainRenderer::TerrainRenderer(Gfx::PGraphics graphics, PScene scene, Gfx::PDe { .size = sizeof(BisectorData) * cpuMesh.totalNumElements, }, - .usage = Gfx::SE_BUFFER_USAGE_INDIRECT_BUFFER_BIT, .name = "UpdateBuffer", }); plainMesh.classificationBuffer = graphics->createShaderBuffer(ShaderBufferCreateInfo{ @@ -151,6 +158,8 @@ TerrainRenderer::TerrainRenderer(Gfx::PGraphics graphics, PScene scene, Gfx::PDe }, .name = "LebVertexBuffer", }); + plainMesh.lebVertexBuffer->pipelineBarrier(Gfx::SE_ACCESS_TRANSFER_WRITE_BIT, Gfx::SE_PIPELINE_STAGE_TRANSFER_BIT, + Gfx::SE_ACCESS_SHADER_WRITE_BIT, Gfx::SE_PIPELINE_STAGE_COMPUTE_SHADER_BIT); plainMesh.currentVertexBuffer = graphics->createShaderBuffer(ShaderBufferCreateInfo{ .sourceData = { @@ -213,6 +222,7 @@ TerrainRenderer::TerrainRenderer(Gfx::PGraphics graphics, PScene scene, Gfx::PDe 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 = { + .viewProjectionMatrix = Matrix4(0), .triangleSize = 10.0f, .maxSubdivisionDepth = 63, .fov = 70.0f, @@ -234,7 +244,7 @@ TerrainRenderer::TerrainRenderer(Gfx::PGraphics graphics, PScene scene, Gfx::PDe { {"vert", "TerrainPass"}, {"frag", "TerrainPass"}, - {"deform", "TerrainPass"}, + {"EvaluateDeformation", "TerrainPass"}, }, .rootSignature = meshUpdater.pipelineLayout, }); @@ -245,20 +255,47 @@ TerrainRenderer::TerrainRenderer(Gfx::PGraphics graphics, PScene scene, Gfx::PDe .computeShader = deformCS, .pipelineLayout = meshUpdater.pipelineLayout, }); + graphics->waitDeviceIdle(); + meshUpdater.resetBuffers(plainMesh); + graphics->waitDeviceIdle(); meshUpdater.prepareIndirection(plainMesh, geometryBuffer); + graphics->waitDeviceIdle(); meshUpdater.evaluateLeb(baseMesh, plainMesh, viewParamsSet, geometryBuffer, updateBuffer, lebCache.getLebMatrixBuffer(), true, true); + graphics->waitDeviceIdle(); applyDeformation(viewParamsSet); + graphics->waitDeviceIdle(); } TerrainRenderer::~TerrainRenderer() {} static bool first = true; -void TerrainRenderer::beginFrame(Gfx::PDescriptorSet viewParamsSet) { +void TerrainRenderer::beginFrame(Gfx::PDescriptorSet viewParamsSet, const Component::Camera& cam) { + UpdateCB updateCB = { + .viewProjectionMatrix = viewport->getProjectionMatrix() * cam.getViewMatrix(), + .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); + graphics->waitDeviceIdle(); meshUpdater.update(plainMesh, viewParamsSet, geometryBuffer, updateBuffer); + graphics->waitDeviceIdle(); meshUpdater.evaluateLeb(baseMesh, plainMesh, viewParamsSet, geometryBuffer, updateBuffer, lebCache.getLebMatrixBuffer(), false, false); + graphics->waitDeviceIdle(); applyDeformation(viewParamsSet); + graphics->waitDeviceIdle(); } Gfx::ORenderCommand TerrainRenderer::render(Gfx::PDescriptorSet viewParamsSet) { diff --git a/src/Engine/Graphics/RenderPass/TerrainRenderer.h b/src/Engine/Graphics/RenderPass/TerrainRenderer.h index 3ea84a1..43bca35 100644 --- a/src/Engine/Graphics/RenderPass/TerrainRenderer.h +++ b/src/Engine/Graphics/RenderPass/TerrainRenderer.h @@ -8,7 +8,7 @@ class TerrainRenderer { public: TerrainRenderer(Gfx::PGraphics graphics, PScene scene, Gfx::PDescriptorLayout viewParamsLayout, Gfx::PDescriptorSet viewParamsSet); ~TerrainRenderer(); - void beginFrame(Gfx::PDescriptorSet viewParamsSet); + void beginFrame(Gfx::PDescriptorSet viewParamsSet, const Component::Camera& cam); Gfx::ORenderCommand render(Gfx::PDescriptorSet viewParamsSet); void setViewport(Gfx::PViewport viewport, Gfx::PRenderPass renderPass); diff --git a/src/Engine/Graphics/RenderPass/VisibilityPass.cpp b/src/Engine/Graphics/RenderPass/VisibilityPass.cpp index 510619c..09084e7 100644 --- a/src/Engine/Graphics/RenderPass/VisibilityPass.cpp +++ b/src/Engine/Graphics/RenderPass/VisibilityPass.cpp @@ -80,7 +80,6 @@ void VisibilityPass::publishOutputs() { cullingBuffer = graphics->createShaderBuffer(ShaderBufferCreateInfo{ .clearValue = 0xffffffff, - .createCleared = true, .name = "CullingBuffer", }); resources->registerBufferOutput("CULLINGBUFFER", cullingBuffer); diff --git a/src/Engine/Graphics/Vulkan/Buffer.cpp b/src/Engine/Graphics/Vulkan/Buffer.cpp index fe73255..dba604b 100644 --- a/src/Engine/Graphics/Vulkan/Buffer.cpp +++ b/src/Engine/Graphics/Vulkan/Buffer.cpp @@ -314,7 +314,7 @@ void Buffer::createBuffer(uint64 size, uint32 destIndex) { command->bindResource(PBufferAllocation(buffers[destIndex])); vkCmdFillBuffer(command->getHandle(), buffers[destIndex]->buffer, 0, VK_WHOLE_SIZE, clearValue); pipelineBarrier(VK_ACCESS_TRANSFER_WRITE_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT, - VK_ACCESS_MEMORY_READ_BIT | VK_ACCESS_MEMORY_WRITE_BIT, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT); + VK_ACCESS_MEMORY_READ_BIT | VK_ACCESS_MEMORY_WRITE_BIT, VK_PIPELINE_STAGE_ALL_COMMANDS_BIT); } } @@ -417,7 +417,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.usage, - createInfo.sourceData.owner, true, createInfo.name, createInfo.createCleared, createInfo.clearValue) { + createInfo.sourceData.owner, true, createInfo.name, createInfo.sourceData.data == nullptr, createInfo.clearValue) { if (createInfo.sourceData.size > 0 && createInfo.sourceData.data != nullptr) { getAlloc()->updateContents(createInfo.sourceData.offset, createInfo.sourceData.size, createInfo.sourceData.data); } diff --git a/src/Engine/Graphics/slang-compile.cpp b/src/Engine/Graphics/slang-compile.cpp index c506ecb..268c807 100644 --- a/src/Engine/Graphics/slang-compile.cpp +++ b/src/Engine/Graphics/slang-compile.cpp @@ -18,7 +18,6 @@ using namespace Seele; { \ if (diagnostics) { \ std::cout << (const char*)diagnostics->getBufferPointer() << std::endl; \ - assert(false); \ } \ } @@ -35,15 +34,15 @@ void Seele::beginCompilation(const ShaderCompilationInfo& info, SlangCompileTarg sessionDesc.flags = 0; Array option = { { - .name = slang::CompilerOptionName::IgnoreCapabilities, + .name = slang::CompilerOptionName::Capability, .value = { .kind = slang::CompilerOptionValueKind::Int, - .intValue0 = 1, + .intValue0 = globalSession->findCapability("GLSL_450"), }, }, { - .name = slang::CompilerOptionName::EmitSpirvViaGLSL, + .name = slang::CompilerOptionName::EmitSpirvDirectly, .value = { .kind = slang::CompilerOptionValueKind::Int, @@ -89,7 +88,7 @@ void Seele::beginCompilation(const ShaderCompilationInfo& info, SlangCompileTarg sessionDesc.preprocessorMacroCount = macros.size(); sessionDesc.preprocessorMacros = macros.data(); slang::TargetDesc targetDesc; - targetDesc.profile = globalSession->findProfile("GLSL_450"); + targetDesc.profile = globalSession->findProfile("glsl_450"); targetDesc.format = target; sessionDesc.targetCount = 1; sessionDesc.targets = &targetDesc;