Adding basic CBT Terrain implementation
This commit is contained in:
+60
-181
@@ -1,198 +1,77 @@
|
||||
import Common;
|
||||
import Bounding;
|
||||
import Scene;
|
||||
import MaterialParameter;
|
||||
import Parameters;
|
||||
|
||||
struct TerrainPayload
|
||||
// Vertex input
|
||||
struct VertexInput
|
||||
{
|
||||
float3 offset;
|
||||
float extent;
|
||||
uint numMeshes;
|
||||
uint instanceID : SV_InstanceID;
|
||||
uint vertexID : SV_VertexID;
|
||||
};
|
||||
|
||||
struct TerrainTile
|
||||
// Vertex output
|
||||
struct VertexOutput
|
||||
{
|
||||
int2 location;
|
||||
float extent;
|
||||
float height;
|
||||
float4 positionCS : SV_POSITION;
|
||||
uint triangleID : TRIANGLE_ID;
|
||||
};
|
||||
|
||||
struct TerrainVertex
|
||||
[shader("vertex")]
|
||||
VertexOutput vert(VertexInput input)
|
||||
{
|
||||
float4 position_CS : SV_Position;
|
||||
float3 position_WS : POSITION0;
|
||||
float2 texCoord : TEXCOORD0;
|
||||
float depth : DEPTH;
|
||||
uint lod : BONEINDEX;
|
||||
};
|
||||
// Initialize the output structure
|
||||
VertexOutput output;
|
||||
output = (VertexOutput)0;
|
||||
|
||||
struct TerrainData
|
||||
{
|
||||
StructuredBuffer<TerrainTile> tiles;
|
||||
Texture2D<float> displacement;
|
||||
Texture2D<float3> colorMap;
|
||||
SamplerState sampler;
|
||||
};
|
||||
ParameterBlock<TerrainData> pTerrainData;
|
||||
// Evaluate the properties of this triangle
|
||||
uint triangle_id = input.vertexID / 3;
|
||||
uint local_vert_id = input.vertexID % 3;
|
||||
|
||||
[numthreads(1, 1, 1)]
|
||||
[shader("amplification")]
|
||||
void taskMain(
|
||||
uint threadID: SV_GroupThreadID,
|
||||
uint3 groupID: SV_GroupID
|
||||
) {
|
||||
TerrainTile tile = pTerrainData.tiles[groupID.x];
|
||||
GroupMemoryBarrierWithGroupSync();
|
||||
AABB bounding;
|
||||
bounding.minCorner = float3(tile.location.x, tile.height, tile.location.y) * tile.extent;
|
||||
bounding.maxCorner = float3(tile.location.x + tile.extent, tile.height, tile.location.y + tile.extent) * tile.extent;
|
||||
float3 origin = viewToWorld(float4(0, 0, 0, 1)).xyz;
|
||||
const float offset = 0.0f;
|
||||
float3 corners[4] = {
|
||||
screenToWorld(float4(offset, offset, -1.0f, 1.0f)).xyz,
|
||||
screenToWorld(float4(pViewParams.screenDimensions.x - offset, offset, -1.0f, 1.0f)).xyz,
|
||||
screenToWorld(float4(offset, pViewParams.screenDimensions.y - offset, -1.0f, 1.0f)).xyz,
|
||||
screenToWorld(float4(pViewParams.screenDimensions - float2(offset, offset), -1.0f, 1.0f)).xyz
|
||||
};
|
||||
Frustum viewFrustum;
|
||||
viewFrustum.sides[0] = computePlane(origin, corners[2], corners[0]);
|
||||
viewFrustum.sides[1] = computePlane(origin, corners[1], corners[3]);
|
||||
viewFrustum.sides[2] = computePlane(origin, corners[0], corners[1]);
|
||||
viewFrustum.sides[3] = computePlane(origin, corners[3], corners[2]);
|
||||
if(!bounding.insideFrustum(viewFrustum))
|
||||
{
|
||||
return;
|
||||
}
|
||||
float3 median = (bounding.minCorner + bounding.maxCorner) / 2;
|
||||
float distance = distance(median, pViewParams.cameraPos_WS.xyz);
|
||||
float tileDistance = distance / tile.extent;
|
||||
uint numMeshes = groupID.y + 1;
|
||||
|
||||
if(numMeshes == 4 && tileDistance > 2)
|
||||
{
|
||||
return;
|
||||
}
|
||||
if(numMeshes == 3 && (tileDistance > 4 || tileDistance < 1))
|
||||
{
|
||||
return;
|
||||
}
|
||||
if(numMeshes == 2 && (tileDistance > 8 || tileDistance < 3))
|
||||
{
|
||||
return;
|
||||
}
|
||||
if(numMeshes == 1 && tileDistance < 7)
|
||||
{
|
||||
return;
|
||||
}
|
||||
TerrainPayload payload;
|
||||
payload.offset = bounding.minCorner;
|
||||
payload.extent = tile.extent / numMeshes;
|
||||
payload.numMeshes = numMeshes;
|
||||
DispatchMesh(numMeshes, numMeshes, 1, payload);
|
||||
// Operate the indirection
|
||||
output.triangleID = pParams.indexedBisectorBuffer[triangle_id];
|
||||
|
||||
// Which vertex should be read?
|
||||
local_vert_id = local_vert_id == 0 ? 2 : (local_vert_id == 2 ? 0 : 1);
|
||||
float3 positionRWS = pParams.currentVertexBuffer[output.triangleID * 3 + local_vert_id];
|
||||
|
||||
// Apply the view projection
|
||||
output.positionCS = mul(pViewParams.projectionMatrix, mul(pViewParams.viewMatrix, float4(positionRWS, 1.0)));
|
||||
return output;
|
||||
}
|
||||
|
||||
const static uint64_t TILE_VERTS = 144;
|
||||
const static uint64_t TILE_PRIMS = 242;
|
||||
|
||||
const static float2 VERT[TILE_VERTS] = {float2(0.0f / 11, 0.0f / 11), float2(0.0f / 11, 1.0f / 11), float2(0.0f / 11, 2.0f / 11), float2(0.0f / 11, 3.0f / 11), float2(0.0f / 11, 4.0f / 11), float2(0.0f / 11, 5.0f / 11), float2(0.0f / 11,
|
||||
6.0f / 11), float2(0.0f / 11, 7.0f / 11), float2(0.0f / 11, 8.0f / 11), float2(0.0f / 11, 9.0f / 11), float2(0.0f / 11, 10.0f / 11), float2(0.0f / 11, 11.0f / 11), float2(1.0f / 11, 0.0f / 11), float2(1.0f / 11, 1.0f / 11), float2(1.0f / 11, 2.0f / 11), float2(1.0f / 11, 3.0f / 11), float2(1.0f /
|
||||
11, 4.0f / 11), float2(1.0f / 11, 5.0f / 11), float2(1.0f / 11, 6.0f / 11), float2(1.0f / 11, 7.0f / 11), float2(1.0f / 11, 8.0f / 11), float2(1.0f / 11, 9.0f / 11), float2(1.0f / 11, 10.0f / 11), float2(1.0f / 11, 11.0f / 11), float2(2.0f / 11, 0.0f / 11), float2(2.0f / 11, 1.0f / 11), float2(2.0f / 11, 2.0f / 11), float2(2.0f / 11, 3.0f / 11), float2(2.0f / 11, 4.0f / 11), float2(2.0f / 11, 5.0f / 11), float2(2.0f / 11, 6.0f / 11), float2(2.0f / 11, 7.0f / 11), float2(2.0f / 11, 8.0f / 11), float2(2.0f / 11, 9.0f / 11), float2(2.0f / 11, 10.0f / 11), float2(2.0f / 11, 11.0f / 11), float2(3.0f / 11, 0.0f / 11), float2(3.0f / 11, 1.0f / 11), float2(3.0f / 11, 2.0f / 11), float2(3.0f / 11, 3.0f / 11), float2(3.0f / 11, 4.0f / 11), float2(3.0f / 11, 5.0f / 11), float2(3.0f / 11, 6.0f / 11), float2(3.0f / 11, 7.0f / 11), float2(3.0f / 11, 8.0f / 11), float2(3.0f / 11, 9.0f / 11), float2(3.0f / 11, 10.0f / 11), float2(3.0f / 11, 11.0f / 11), float2(4.0f / 11, 0.0f / 11), float2(4.0f
|
||||
/ 11, 1.0f / 11), float2(4.0f / 11, 2.0f / 11), float2(4.0f / 11, 3.0f / 11), float2(4.0f / 11, 4.0f / 11), float2(4.0f / 11, 5.0f / 11), float2(4.0f / 11, 6.0f / 11), float2(4.0f / 11, 7.0f / 11), float2(4.0f / 11, 8.0f / 11), float2(4.0f / 11, 9.0f / 11), float2(4.0f / 11, 10.0f / 11), float2(4.0f / 11, 11.0f / 11), float2(5.0f / 11, 0.0f / 11), float2(5.0f / 11, 1.0f / 11), float2(5.0f / 11,
|
||||
2.0f / 11), float2(5.0f / 11, 3.0f / 11), float2(5.0f / 11, 4.0f / 11), float2(5.0f / 11, 5.0f / 11), float2(5.0f / 11, 6.0f / 11), float2(5.0f / 11, 7.0f / 11), float2(5.0f / 11, 8.0f / 11), float2(5.0f / 11, 9.0f / 11), float2(5.0f / 11, 10.0f / 11), float2(5.0f / 11, 11.0f / 11), float2(6.0f /
|
||||
11, 0.0f / 11), float2(6.0f / 11, 1.0f / 11), float2(6.0f / 11, 2.0f / 11), float2(6.0f / 11, 3.0f / 11), float2(6.0f / 11, 4.0f / 11), float2(6.0f / 11, 5.0f / 11), float2(6.0f / 11, 6.0f / 11), float2(6.0f / 11, 7.0f / 11), float2(6.0f / 11, 8.0f / 11), float2(6.0f / 11, 9.0f / 11), float2(6.0f
|
||||
/ 11, 10.0f / 11), float2(6.0f / 11, 11.0f / 11), float2(7.0f / 11, 0.0f / 11), float2(7.0f / 11, 1.0f / 11), float2(7.0f / 11, 2.0f / 11), float2(7.0f / 11, 3.0f / 11), float2(7.0f / 11, 4.0f / 11), float2(7.0f / 11, 5.0f / 11), float2(7.0f / 11, 6.0f / 11), float2(7.0f / 11, 7.0f / 11), float2(7.0f / 11, 8.0f / 11), float2(7.0f / 11, 9.0f / 11), float2(7.0f / 11, 10.0f / 11), float2(7.0f / 11, 11.0f / 11), float2(8.0f / 11, 0.0f / 11), float2(8.0f / 11, 1.0f / 11), float2(8.0f / 11, 2.0f / 11), float2(8.0f / 11, 3.0f / 11), float2(8.0f / 11, 4.0f / 11), float2(8.0f / 11, 5.0f / 11), float2(8.0f / 11, 6.0f / 11), float2(8.0f / 11, 7.0f / 11), float2(8.0f / 11, 8.0f / 11), float2(8.0f /
|
||||
11, 9.0f / 11), float2(8.0f / 11, 10.0f / 11), float2(8.0f / 11, 11.0f / 11), float2(9.0f / 11, 0.0f / 11), float2(9.0f / 11, 1.0f / 11), float2(9.0f / 11, 2.0f / 11), float2(9.0f / 11, 3.0f / 11), float2(9.0f / 11, 4.0f / 11), float2(9.0f / 11, 5.0f / 11), float2(9.0f / 11, 6.0f / 11), float2(9.0f / 11, 7.0f / 11), float2(9.0f / 11, 8.0f / 11), float2(9.0f / 11, 9.0f / 11), float2(9.0f / 11, 10.0f / 11), float2(9.0f / 11, 11.0f / 11), float2(10.0f / 11, 0.0f / 11), float2(10.0f / 11, 1.0f / 11), float2(10.0f / 11, 2.0f / 11), float2(10.0f / 11, 3.0f / 11), float2(10.0f / 11, 4.0f / 11), float2(10.0f / 11, 5.0f / 11), float2(10.0f / 11, 6.0f / 11), float2(10.0f / 11, 7.0f / 11), float2(10.0f / 11, 8.0f / 11), float2(10.0f / 11, 9.0f / 11), float2(10.0f / 11, 10.0f / 11), float2(10.0f / 11, 11.0f / 11), float2(11.0f / 11, 0.0f / 11), float2(11.0f / 11, 1.0f / 11), float2(11.0f / 11, 2.0f / 11), float2(11.0f / 11, 3.0f / 11), float2(11.0f / 11, 4.0f / 11), float2(11.0f / 11, 5.0f / 11), float2(11.0f / 11, 6.0f / 11), float2(11.0f / 11, 7.0f / 11), float2(11.0f / 11, 8.0f / 11), float2(11.0f / 11, 9.0f / 11), float2(11.0f / 11, 10.0f / 11), float2(11.0f / 11, 11.0f / 11)};
|
||||
|
||||
const static uint3 IND[TILE_PRIMS] = {uint3(0, 1, 12), uint3(12, 1, 13), uint3(1, 2, 13), uint3(13, 2, 14), uint3(2, 3, 14), uint3(14, 3, 15), uint3(3, 4, 15), uint3(15, 4, 16), uint3(4, 5, 16), uint3(16, 5, 17), uint3(5, 6, 17), uint3(17, 6, 18), uint3(6, 7, 18), uint3(18, 7, 19), uint3(7, 8, 19), uint3(19, 8, 20), uint3(8, 9, 20), uint3(20, 9, 21), uint3(9, 10, 21), uint3(21, 10, 22), uint3(10, 11, 22), uint3(22, 11, 23), uint3(12, 13, 24), uint3(24, 13, 25), uint3(13, 14, 25), uint3(25, 14, 26), uint3(14,
|
||||
15, 26), uint3(26, 15, 27), uint3(15, 16, 27), uint3(27, 16, 28), uint3(16, 17, 28), uint3(28, 17, 29), uint3(17, 18, 29), uint3(29, 18, 30), uint3(18, 19, 30), uint3(30, 19, 31), uint3(19, 20, 31), uint3(31, 20, 32), uint3(20, 21, 32), uint3(32, 21, 33), uint3(21, 22, 33), uint3(33, 22, 34), uint3(22, 23, 34), uint3(34, 23, 35), uint3(24, 25, 36), uint3(36, 25, 37), uint3(25, 26,
|
||||
37), uint3(37, 26, 38), uint3(26, 27, 38), uint3(38, 27, 39), uint3(27, 28, 39), uint3(39, 28, 40), uint3(28, 29, 40), uint3(40, 29, 41), uint3(29, 30, 41), uint3(41, 30, 42), uint3(30, 31, 42), uint3(42, 31, 43), uint3(31, 32, 43), uint3(43, 32, 44), uint3(32, 33, 44), uint3(44, 33, 45), uint3(33, 34, 45), uint3(45, 34, 46), uint3(34, 35, 46), uint3(46, 35, 47), uint3(36, 37, 48), uint3(48, 37, 49), uint3(37, 38, 49), uint3(49, 38, 50), uint3(38, 39, 50), uint3(50, 39, 51), uint3(39, 40, 51), uint3(51, 40, 52), uint3(40, 41, 52), uint3(52, 41, 53), uint3(41, 42, 53),
|
||||
uint3(53, 42, 54), uint3(42, 43, 54), uint3(54, 43, 55), uint3(43, 44, 55), uint3(55, 44, 56), uint3(44, 45, 56), uint3(56, 45, 57), uint3(45, 46, 57), uint3(57, 46, 58), uint3(46, 47, 58), uint3(58, 47, 59), uint3(48, 49, 60), uint3(60, 49, 61), uint3(49, 50, 61), uint3(61, 50, 62), uint3(50, 51, 62), uint3(62, 51, 63), uint3(51, 52, 63), uint3(63, 52, 64), uint3(52, 53, 64), uint3(64, 53, 65), uint3(53, 54, 65), uint3(65, 54, 66), uint3(54, 55, 66), uint3(66, 55, 67), uint3(55, 56, 67), uint3(67, 56, 68), uint3(56, 57, 68), uint3(68, 57, 69), uint3(57, 58, 69), uint3(69, 58, 70), uint3(58, 59, 70), uint3(70, 59, 71), uint3(60, 61, 72), uint3(72, 61, 73), uint3(61, 62, 73), uint3(73, 62, 74), uint3(62, 63, 74), uint3(74, 63, 75), uint3(63, 64, 75), uint3(75, 64, 76), uint3(64, 65, 76), uint3(76, 65, 77), uint3(65, 66, 77), uint3(77, 66, 78), uint3(66, 67, 78), uint3(78, 67, 79), uint3(67, 68, 79), uint3(79, 68, 80), uint3(68, 69, 80), uint3(80, 69, 81), uint3(69, 70, 81), uint3(81, 70, 82), uint3(70, 71, 82), uint3(82, 71, 83), uint3(72,
|
||||
73, 84), uint3(84, 73, 85), uint3(73, 74, 85), uint3(85, 74, 86), uint3(74, 75, 86), uint3(86, 75, 87), uint3(75, 76, 87), uint3(87, 76, 88), uint3(76, 77, 88), uint3(88, 77, 89), uint3(77, 78, 89), uint3(89, 78, 90), uint3(78, 79, 90), uint3(90, 79, 91), uint3(79, 80, 91), uint3(91, 80, 92), uint3(80, 81, 92), uint3(92, 81, 93), uint3(81, 82, 93), uint3(93, 82, 94), uint3(82, 83,
|
||||
94), uint3(94, 83, 95), uint3(84, 85, 96), uint3(96, 85, 97), uint3(85, 86, 97), uint3(97, 86, 98), uint3(86, 87, 98), uint3(98, 87, 99), uint3(87, 88, 99), uint3(99, 88, 100), uint3(88, 89, 100), uint3(100, 89, 101), uint3(89, 90, 101), uint3(101, 90, 102), uint3(90, 91, 102), uint3(102, 91, 103), uint3(91, 92, 103), uint3(103, 92, 104), uint3(92, 93, 104), uint3(104, 93, 105), uint3(93, 94, 105), uint3(105, 94, 106), uint3(94, 95, 106), uint3(106, 95, 107), uint3(96, 97, 108), uint3(108, 97, 109), uint3(97, 98, 109), uint3(109, 98, 110), uint3(98, 99, 110), uint3(110, 99, 111), uint3(99, 100, 111), uint3(111, 100, 112), uint3(100, 101, 112), uint3(112, 101, 113), uint3(101, 102, 113), uint3(113, 102, 114), uint3(102, 103, 114), uint3(114, 103, 115), uint3(103, 104, 115), uint3(115, 104, 116), uint3(104, 105, 116), uint3(116, 105, 117), uint3(105, 106, 117), uint3(117, 106, 118), uint3(106, 107, 118), uint3(118, 107, 119), uint3(108, 109, 120), uint3(120, 109, 121), uint3(109, 110, 121), uint3(121, 110, 122), uint3(110, 111, 122), uint3(122, 111,
|
||||
123), uint3(111, 112, 123), uint3(123, 112, 124), uint3(112, 113, 124), uint3(124, 113, 125), uint3(113, 114, 125), uint3(125, 114, 126), uint3(114, 115, 126), uint3(126, 115, 127), uint3(115, 116, 127), uint3(127, 116, 128), uint3(116, 117, 128), uint3(128, 117, 129), uint3(117, 118, 129), uint3(129, 118, 130), uint3(118, 119, 130), uint3(130, 119, 131), uint3(120, 121, 132), uint3(132, 121, 133), uint3(121, 122, 133), uint3(133, 122, 134), uint3(122, 123, 134), uint3(134, 123, 135), uint3(123, 124, 135), uint3(135, 124, 136), uint3(124, 125, 136), uint3(136, 125, 137), uint3(125, 126, 137), uint3(137, 126, 138), uint3(126, 127, 138), uint3(138, 127, 139), uint3(127, 128, 139), uint3(139, 128, 140), uint3(128, 129, 140), uint3(140, 129, 141), uint3(129, 130, 141), uint3(141, 130, 142), uint3(130, 131, 142), uint3(142, 131, 143)};
|
||||
|
||||
|
||||
[numthreads(MESH_GROUP_SIZE, 1, 1)]
|
||||
[outputtopology("triangle")]
|
||||
[shader("mesh")]
|
||||
void meshMain(
|
||||
in uint threadID: SV_GroupThreadID,
|
||||
in uint3 groupID: SV_GroupID,
|
||||
in payload TerrainPayload params,
|
||||
out vertices TerrainVertex vertices[TILE_VERTS],
|
||||
out indices uint3 indices[TILE_PRIMS],
|
||||
) {
|
||||
float4x4 vp = mul(pViewParams.projectionMatrix, pViewParams.viewMatrix);
|
||||
SetMeshOutputCounts(TILE_VERTS, TILE_PRIMS);
|
||||
|
||||
for(uint i = threadID; i < TILE_PRIMS; i += MESH_GROUP_SIZE)
|
||||
{
|
||||
indices[i] = IND[i];
|
||||
}
|
||||
for(uint i = threadID; i < TILE_VERTS; i += MESH_GROUP_SIZE)
|
||||
{
|
||||
float2 base = VERT[i];
|
||||
float3 objectPos = float3(base.x, 0, base.y);
|
||||
float3 worldPos = params.offset + objectPos * params.extent + float3(groupID.x * params.extent, 0, groupID.y * params.extent);
|
||||
|
||||
float lodDisplacement = 0;
|
||||
float3 camPos = pViewParams.cameraPos_WS.xyz;
|
||||
float cameraDistance = distance(worldPos, camPos);
|
||||
float threshold = 0;
|
||||
if(params.numMeshes == 3)
|
||||
{
|
||||
threshold = 10;
|
||||
}
|
||||
if(params.numMeshes == 2)
|
||||
{
|
||||
threshold = 30;
|
||||
}
|
||||
if(params.numMeshes == 1)
|
||||
{
|
||||
threshold = 70;
|
||||
}
|
||||
|
||||
if(params.numMeshes < 4)
|
||||
{
|
||||
if(cameraDistance > threshold)
|
||||
{
|
||||
lodDisplacement = clamp(exp(-(1 / (cameraDistance + threshold))), 0, 1) - 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
lodDisplacement = -1;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// float3 displacement1 = pWaterMaterial.displacementTextures.Sample(pWaterMaterial.sampler, float3(worldPos.xz * pWaterMaterial.tile1, 0)).xyz * pWaterMaterial.contributeDisplacement1;
|
||||
// float3 displacement2 = pWaterMaterial.displacementTextures.Sample(pWaterMaterial.sampler, float3(worldPos.xz * pWaterMaterial.tile2, 1)).xyz * pWaterMaterial.contributeDisplacement2;
|
||||
// float3 displacement3 = pWaterMaterial.displacementTextures.Sample(pWaterMaterial.sampler, float3(worldPos.xz * pWaterMaterial.tile3, 2)).xyz * pWaterMaterial.contributeDisplacement3;
|
||||
// float3 displacement4 = pWaterMaterial.displacementTextures.Sample(pWaterMaterial.sampler, float3(worldPos.xz * pWaterMaterial.tile4, 3)).xyz * pWaterMaterial.contributeDisplacement4;
|
||||
float displacement = pTerrainData.displacement.SampleLevel(pTerrainData.sampler, worldPos.xz / 1000, 0) * 10;
|
||||
|
||||
float4 clipPos = mul(vp, float4(worldPos, 1));
|
||||
float ndcDepth = clipPos.z / clipPos.w;
|
||||
|
||||
float depth = 1 - (-ndcDepth + 1.0f) / 2.0f;
|
||||
|
||||
// displacement = lerp(0.0f, displacement, pow(saturate(depth), pTerrainData.displacementDepthAttenuation));
|
||||
|
||||
worldPos.y += displacement;
|
||||
worldPos.y += lodDisplacement;
|
||||
|
||||
TerrainVertex v;
|
||||
v.position_WS = worldPos;
|
||||
v.position_CS = mul(vp, float4(worldPos, 1));
|
||||
v.texCoord = worldPos.xz;
|
||||
v.depth = depth;
|
||||
v.lod = params.numMeshes;
|
||||
vertices[i] = v;
|
||||
}
|
||||
}
|
||||
// Pixel input
|
||||
struct PixelInput
|
||||
{
|
||||
float4 positionCS : SV_POSITION;
|
||||
uint triangleID : TRIANGLE_ID;
|
||||
};
|
||||
|
||||
[shader("pixel")]
|
||||
float4 fragmentMain(TerrainVertex vertex)
|
||||
float4 frag(PixelInput input) : SV_Target
|
||||
{
|
||||
return float4(pTerrainData.colorMap.Sample(pTerrainData.sampler, vertex.position_WS.xz / 1000), 1);
|
||||
return float4(0, 1, 0, 1);
|
||||
}
|
||||
|
||||
[shader("compute")]
|
||||
[numthreads(64, 1, 1)]
|
||||
void deform(uint currentID: SV_DispatchThreadID)
|
||||
{
|
||||
if(currentID > pParams.indirectDrawBuffer[9] * 4)
|
||||
return;
|
||||
|
||||
uint bisectorID = currentID / 4;
|
||||
uint localVertexID = currentID % 4;
|
||||
|
||||
bisectorID = pParams.indexedBisectorBuffer[bisectorID];
|
||||
|
||||
currentID = localVertexID < 3 ? bisectorID * 3 + localVertexID : 3 * pParams.geometry.totalNumElements + bisectorID;
|
||||
|
||||
float3 positionWS = pParams.lebPositionBuffer[currentID];
|
||||
|
||||
float3 positionPS = positionWS;
|
||||
|
||||
float3 normalPS = float3(0, 1, 0);
|
||||
|
||||
float2 sampleUV = float2(0, 0);
|
||||
|
||||
pParams.currentVertexBuffer[currentID] = positionWS;
|
||||
}
|
||||
@@ -87,7 +87,7 @@ void taskMain(
|
||||
bounding.minCorner = float3(tile.location.x, tile.height, tile.location.y) * tile.extent;
|
||||
bounding.maxCorner = float3(tile.location.x + 1, tile.height, tile.location.y + 1) * tile.extent;
|
||||
float3 median = (bounding.minCorner + bounding.maxCorner) / 2;
|
||||
float distance = distance(median, pViewParams.cameraPos_WS.xyz);
|
||||
float distance = distance(median, pViewParams.cameraPosition_WS.xyz);
|
||||
float tileDistance = distance / tile.extent;
|
||||
uint numMeshes = groupID.y + 1;
|
||||
|
||||
@@ -179,7 +179,7 @@ void meshMain(
|
||||
float3 worldPos = params.offset + objectPos * params.extent + float3(groupID.x * params.extent, 0, groupID.y * params.extent);
|
||||
|
||||
float lodDisplacement = 0;
|
||||
float3 camPos = pViewParams.ameraPos_WS.xyz;
|
||||
float3 camPos = pViewParams.cameraPosition_WS.xyz;
|
||||
float cameraDistance = distance(worldPos, camPos);
|
||||
float threshold = 0;
|
||||
if(params.numMeshes == 3)
|
||||
@@ -207,7 +207,7 @@ void meshMain(
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// TODO: LOD load
|
||||
float3 displacement1 = pWaterMaterial.displacementTextures.Sample(pWaterMaterial.sampler, float3(worldPos.xz * pWaterMaterial.tile1, 0)).xyz * pWaterMaterial.contributeDisplacement1;
|
||||
float3 displacement2 = pWaterMaterial.displacementTextures.Sample(pWaterMaterial.sampler, float3(worldPos.xz * pWaterMaterial.tile2, 1)).xyz * pWaterMaterial.contributeDisplacement2;
|
||||
float3 displacement3 = pWaterMaterial.displacementTextures.Sample(pWaterMaterial.sampler, float3(worldPos.xz * pWaterMaterial.tile3, 2)).xyz * pWaterMaterial.contributeDisplacement3;
|
||||
@@ -237,7 +237,7 @@ void meshMain(
|
||||
[shader("pixel")]
|
||||
float4 fragmentMain(WaterVertex vert) : SV_TARGET {
|
||||
float3 lightDir = -normalize(pWaterMaterial.sunDirection);
|
||||
float3 viewDir = normalize(pViewParams.cameraPos_WS.xyz - vert.position_WS);
|
||||
float3 viewDir = normalize(pViewParams.cameraPosition_WS.xyz - vert.position_WS);
|
||||
float3 halfwayDir = normalize(lightDir + viewDir);
|
||||
float depth = vert.depth;
|
||||
float LdotH = clamp(dot(lightDir, halfwayDir), 0, 1);
|
||||
|
||||
@@ -6,12 +6,17 @@ static const uint32_t MESH_GROUP_SIZE = 32;
|
||||
|
||||
struct ViewParameter
|
||||
{
|
||||
Frustum viewFrustum;
|
||||
float4x4 viewMatrix;
|
||||
float4x4 inverseViewMatrix;
|
||||
float4x4 projectionMatrix;
|
||||
float4x4 inverseProjection;
|
||||
float4 cameraPos_WS;
|
||||
float4 cameraPosition_WS;
|
||||
float4 cameraForward_WS;
|
||||
float2 screenDimensions;
|
||||
float2 invScreenDimensions;
|
||||
uint frameIndex;
|
||||
float time;
|
||||
};
|
||||
layout(set = 0)
|
||||
ParameterBlock<ViewParameter> pViewParams;
|
||||
|
||||
@@ -51,7 +51,7 @@ struct FragmentParameter
|
||||
float3x3 tbn = float3x3(normalize(tangent_WS), normalize(biTangent_WS), normalize(normal_WS));
|
||||
result.tbn = tbn;
|
||||
result.position_TS = mul(tbn, position_WS);
|
||||
result.viewDir_TS = mul(tbn, normalize(pViewParams.cameraPos_WS.xyz - position_WS));
|
||||
result.viewDir_TS = mul(tbn, normalize(pViewParams.cameraPosition_WS.xyz - position_WS));
|
||||
result.normal_TS = mul(tbn, normal_WS);
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,46 @@
|
||||
// Pointer to an invalid neighbor or index
|
||||
const static int INVALID_POINTER = 4294967295;
|
||||
|
||||
// Possible culling state
|
||||
const static int BACK_FACE_CULLED =-3;
|
||||
const static int FRUSTUM_CULLED =-2;
|
||||
const static int TOO_SMALL =-1;
|
||||
const static int UNCHANGED_ELEMENT= 0;
|
||||
const static int BISECT_ELEMENT= 1;
|
||||
const static int SIMPLIFY_ELEMENT= 2;
|
||||
const static int MERGED_ELEMENT= 3;
|
||||
|
||||
// Bisector flags
|
||||
const static int VISIBLE_BISECTOR = 0x1;
|
||||
const static int MODIFIED_BISECTOR = 0x2;
|
||||
|
||||
struct BisectorData
|
||||
{
|
||||
// Subvision that should be applied to this bisector
|
||||
uint32_t subdivisionPattern;
|
||||
|
||||
// Allocated indices for this bisector
|
||||
uint32_t indices[3];
|
||||
|
||||
// Neighbor that should be processed
|
||||
uint32_t problematicNeighbor;
|
||||
|
||||
// State of this bisector (split, merge, etc)
|
||||
uint32_t bisectorState;
|
||||
|
||||
// Visibility and modification flags of a bisector
|
||||
uint32_t flags;
|
||||
|
||||
// ID used for the propagation
|
||||
uint32_t propagationID;
|
||||
};
|
||||
|
||||
uint heapIDDepth(uint64_t x)
|
||||
{
|
||||
uint depth = 0;
|
||||
while (x > 0u) {
|
||||
++depth;
|
||||
x >>= 1u;
|
||||
}
|
||||
return depth;
|
||||
}
|
||||
@@ -0,0 +1,617 @@
|
||||
import Parameters;
|
||||
|
||||
#ifndef WORKGROUP_SIZE
|
||||
#define WORKGROUP_SIZE 64
|
||||
#endif
|
||||
|
||||
// Num elements
|
||||
#define OCBT_NUM_ELEMENTS 1048576
|
||||
// Tree sizes
|
||||
#define OCBT_TREE_SIZE_BITS (32 * 1 + 32 * 2 + 32 * 4 + 32 * 8 + 32 * 16 + 32 * 32 + 32 * 64 + 16 * 128 + 16 * 256 + 16 * 512 + 16 * 1024 + 16* 2048 + 16 * 4096 + 8 * 8192)
|
||||
#define OCBT_TREE_NUM_SLOTS (OCBT_TREE_SIZE_BITS / 32)
|
||||
#define OCBT_BITFIELD_NUM_SLOTS (OCBT_NUM_ELEMENTS / 64)
|
||||
#define OCBT_LAST_LEVEL_SIZE 8192
|
||||
|
||||
// Tree last level
|
||||
#define TREE_LAST_LEVEL 13
|
||||
// First virtual level
|
||||
#define FIRST_VIRTUAL_LEVEL 14
|
||||
// Leaf level
|
||||
#define LEAF_LEVEL 20
|
||||
|
||||
// per level offset
|
||||
static const uint32_t OCBT_depth_offset[21] = { 0, // Level 0
|
||||
32 * 1, // level 1
|
||||
32 * 1 + 32 * 2, // level 2
|
||||
32 * 1 + 32 * 2 + 32 * 4, // level 3
|
||||
32 * 1 + 32 * 2 + 32 * 4 + 32 * 8, // Level 4
|
||||
32 * 1 + 32 * 2 + 32 * 4 + 32 * 8 + 32 * 16, // Level 5
|
||||
32 * 1 + 32 * 2 + 32 * 4 + 32 * 8 + 32 * 16 + 32 * 32, // Level 6
|
||||
32 * 1 + 32 * 2 + 32 * 4 + 32 * 8 + 32 * 16 + 32 * 32 + 32 * 64, // Level 7
|
||||
|
||||
32 * 1 + 32 * 2 + 32 * 4 + 32 * 8 + 32 * 16 + 32 * 32 + 32 * 64 + 16 * 128, // Level 8
|
||||
32 * 1 + 32 * 2 + 32 * 4 + 32 * 8 + 32 * 16 + 32 * 32 + 32 * 64 + 16 * 128 + 16 * 256, // Level 9
|
||||
32 * 1 + 32 * 2 + 32 * 4 + 32 * 8 + 32 * 16 + 32 * 32 + 32 * 64 + 16 * 128 + 16 * 256 + 16 * 512, // Level 10
|
||||
32 * 1 + 32 * 2 + 32 * 4 + 32 * 8 + 32 * 16 + 32 * 32 + 32 * 64 + 16 * 128 + 16 * 256 + 16 * 512 + 16 * 1024, // Level 11
|
||||
32 * 1 + 32 * 2 + 32 * 4 + 32 * 8 + 32 * 16 + 32 * 32 + 32 * 64 + 16 * 128 + 16 * 256 + 16 * 512 + 16 * 1024 + 16 * 2048, // Level 12
|
||||
32 * 1 + 32 * 2 + 32 * 4 + 32 * 8 + 32 * 16 + 32 * 32 + 32 * 64 + 16 * 128 + 16 * 256 + 16 * 512 + 16 * 1024 + 16 * 2048 + 16 * 4096, // Level 13
|
||||
|
||||
0, // Level 14
|
||||
0, // Level 15
|
||||
0, // Level 16
|
||||
0, // Level 17
|
||||
0, // Level 18
|
||||
0, // Level 19
|
||||
0, // Level 20
|
||||
};
|
||||
|
||||
static const uint64_t OCBT_bit_mask[21] = { 0xffffffff, // Root 17
|
||||
0xffffffff, // Level 16
|
||||
0xffffffff, // level 15
|
||||
0xffffffff, // level 14
|
||||
0xffffffff, // level 13
|
||||
0xffffffff, // level 12
|
||||
0xffffffff, // level 11
|
||||
|
||||
0xffff, // level 10
|
||||
0xffff, // level 9
|
||||
0xffff, // level 8
|
||||
0xffff, // level 8
|
||||
0xffff, // level 8
|
||||
0xffff, // level 8
|
||||
0xff, // level 8
|
||||
|
||||
0xffffffffffffffff, // level 7
|
||||
0xffffffff, // Level 6
|
||||
0xffff, // level 5
|
||||
0xff, // level 4
|
||||
0xf, // level 3
|
||||
0x3, // level 2
|
||||
0x1, // level 1
|
||||
};
|
||||
|
||||
static const uint32_t OCBT_bit_count[21] = { 32, // Root 17
|
||||
32, // Level 16
|
||||
32, // level 15
|
||||
32, // level 14
|
||||
32, // level 13
|
||||
32, // level 12
|
||||
32, // level 11
|
||||
|
||||
16, // level 10
|
||||
16, // level 9
|
||||
16, // level 8
|
||||
16, // level 8
|
||||
16, // level 8
|
||||
16, // level 8
|
||||
8, // level 8
|
||||
|
||||
64, // Level 5
|
||||
32, // Level 5
|
||||
16, // Level 4
|
||||
8, // level 3
|
||||
4, // level 2
|
||||
2, // level 1
|
||||
1, // level 0
|
||||
};
|
||||
|
||||
// Define the remaining values
|
||||
#define BUFFER_ELEMENT_PER_LANE ((OCBT_TREE_NUM_SLOTS + WORKGROUP_SIZE - 1) / WORKGROUP_SIZE)
|
||||
#define BUFFER_ELEMENT_PER_LANE_NO_BITFIELD ((OCBT_TREE_NUM_SLOTS + WORKGROUP_SIZE - 1) / WORKGROUP_SIZE)
|
||||
#define BITFIELD_ELEMENT_PER_LANE ((OCBT_BITFIELD_NUM_SLOTS + WORKGROUP_SIZE - 1) / WORKGROUP_SIZE)
|
||||
#define WAVE_TREE_DEPTH uint(log2(OCBT_NUM_ELEMENTS))
|
||||
|
||||
uint32_t cbt_size()
|
||||
{
|
||||
return OCBT_NUM_ELEMENTS;
|
||||
}
|
||||
|
||||
uint32_t last_level_offset()
|
||||
{
|
||||
return OCBT_depth_offset[TREE_LAST_LEVEL] / 32;
|
||||
}
|
||||
|
||||
groupshared uint gs_cbtTree[OCBT_TREE_NUM_SLOTS];
|
||||
|
||||
// Function that sets a given bit
|
||||
void set_bit(uint bitID, bool state)
|
||||
{
|
||||
// Coordinates of the bit
|
||||
uint32_t slot = bitID / 64;
|
||||
uint32_t local_id = bitID % 64;
|
||||
|
||||
if (state)
|
||||
pParams.bitFieldBuffer[slot] |= 1uLL << local_id;
|
||||
else
|
||||
pParams.bitFieldBuffer[slot] &= ~(1uLL << local_id);
|
||||
}
|
||||
|
||||
void set_bit_atomic(uint bitID, bool state)
|
||||
{
|
||||
// Coordinates of the bit
|
||||
uint32_t slot = bitID / 64;
|
||||
uint32_t local_id = bitID % 64;
|
||||
|
||||
if (state)
|
||||
InterlockedOr(pParams.bitFieldBuffer[slot], 1uLL << local_id);
|
||||
else
|
||||
InterlockedAnd(pParams.bitFieldBuffer[slot], ~(1uLL << local_id));
|
||||
}
|
||||
|
||||
uint get_bit(uint bitID)
|
||||
{
|
||||
uint32_t slot = bitID / 64;
|
||||
uint32_t local_id = bitID % 64;
|
||||
return uint((pParams.bitFieldBuffer[slot] & (1uLL << local_id)) >> local_id);
|
||||
}
|
||||
|
||||
uint get_heap_element(uint id)
|
||||
{
|
||||
// Figure out the location of the first bit of this element
|
||||
uint32_t real_heap_id = id - 1;
|
||||
uint32_t depth = uint32_t(log2(real_heap_id + 1));
|
||||
uint32_t level_first_element = (1u << depth) - 1;
|
||||
uint32_t id_in_level = real_heap_id - level_first_element;
|
||||
uint32_t first_bit = OCBT_depth_offset[depth] + OCBT_bit_count[depth] * id_in_level;
|
||||
if (depth < FIRST_VIRTUAL_LEVEL)
|
||||
{
|
||||
uint32_t slot = first_bit / 32;
|
||||
uint32_t local_id = first_bit % 32;
|
||||
uint32_t target_bits = (gs_cbtTree[slot] >> local_id) & uint32_t(OCBT_bit_mask[depth]);
|
||||
return (gs_cbtTree[slot] >> local_id) & uint32_t(OCBT_bit_mask[depth]);
|
||||
}
|
||||
else
|
||||
{
|
||||
uint32_t slot = first_bit / 64;
|
||||
uint32_t local_id = first_bit % 64;
|
||||
uint64_t target_bits = (pParams.bitFieldBuffer[slot] >> local_id) & OCBT_bit_mask[depth];
|
||||
uint32_t high = uint(target_bits >> 32);
|
||||
uint32_t low = uint(target_bits);
|
||||
return countbits(high) + countbits(low);
|
||||
}
|
||||
}
|
||||
|
||||
// Should not be called if depth > TREE_LAST_LEVEL
|
||||
void set_heap_element(uint id, uint value)
|
||||
{
|
||||
// Figure out the location of the first bit of this element
|
||||
uint real_heap_id = id - 1;
|
||||
uint depth = uint(log2(real_heap_id + 1));
|
||||
uint level_first_element = (1u << depth) - 1;
|
||||
uint first_bit = OCBT_depth_offset[depth] + OCBT_bit_count[depth] * (real_heap_id - level_first_element);
|
||||
|
||||
// Find the slot and the local first bit
|
||||
uint slot = first_bit / 32;
|
||||
uint local_id = first_bit % 32;
|
||||
|
||||
// Extract the relevant bits
|
||||
gs_cbtTree[slot] &= ~(uint32_t(OCBT_bit_mask[depth]) << local_id);
|
||||
gs_cbtTree[slot] |= ((uint32_t(OCBT_bit_mask[depth]) & value) << local_id);
|
||||
}
|
||||
|
||||
// Should not be called if depth > TREE_LAST_LEVEL
|
||||
void set_heap_element_atomic(uint id, uint value)
|
||||
{
|
||||
// Figure out the location of the first bit of this element
|
||||
uint real_heap_id = id - 1;
|
||||
uint depth = uint(log2(real_heap_id + 1));
|
||||
uint level_first_element = (1u << depth) - 1;
|
||||
uint first_bit = OCBT_depth_offset[depth] + OCBT_bit_count[depth] * (real_heap_id - level_first_element);
|
||||
|
||||
// Find the slot and the local first bit
|
||||
uint slot = first_bit / 32;
|
||||
uint local_id = first_bit % 32;
|
||||
|
||||
// Extract the relevant bits
|
||||
InterlockedAnd(gs_cbtTree[slot], ~(uint32_t(OCBT_bit_mask[depth]) << local_id));
|
||||
InterlockedOr(gs_cbtTree[slot], ((uint32_t(OCBT_bit_mask[depth]) & value) << local_id));
|
||||
}
|
||||
|
||||
// Function that returns the number of active bits
|
||||
uint bit_count()
|
||||
{
|
||||
return gs_cbtTree[0];
|
||||
}
|
||||
|
||||
uint bit_count(uint depth, uint element)
|
||||
{
|
||||
return get_heap_element((1u << depth) + element);
|
||||
}
|
||||
|
||||
// decodes the position of the i-th one in the bitfield
|
||||
uint decode_bit(uint handle)
|
||||
{
|
||||
#if defined(NAIVE_DECODE)
|
||||
uint bitID = 1;
|
||||
for (uint currentDepth = 0; currentDepth < WAVE_TREE_DEPTH; ++currentDepth)
|
||||
{
|
||||
uint heapValue = get_heap_element(2 * bitID);
|
||||
uint b = handle < heapValue ? 0 : 1;
|
||||
|
||||
bitID = 2 * bitID + b;
|
||||
handle -= heapValue * b;
|
||||
}
|
||||
|
||||
return (bitID ^ OCBT_NUM_ELEMENTS);
|
||||
#else
|
||||
uint currentDepth = 0;
|
||||
uint heapElementID = 1u;
|
||||
for (currentDepth = 0; currentDepth < FIRST_VIRTUAL_LEVEL; ++currentDepth)
|
||||
{
|
||||
// Read the left element
|
||||
uint heapValue = get_heap_element(2u * heapElementID);
|
||||
|
||||
// Does it fall in the right or left subtree?
|
||||
uint b = handle < heapValue ? 0u : 1u;
|
||||
|
||||
// Pick a subtree
|
||||
heapElementID = 2u * heapElementID + b;
|
||||
|
||||
// Move the iterator to exclude the right subtree if required
|
||||
handle -= heapValue * b;
|
||||
}
|
||||
|
||||
// Align with the internal depth
|
||||
currentDepth++;
|
||||
|
||||
// Ok we have our subtree, now we need to pick the right bit
|
||||
uint64_t heapValue = pParams.bitFieldBuffer[heapElementID - OCBT_LAST_LEVEL_SIZE * 2];
|
||||
uint64_t mask = 0xffffffff;
|
||||
uint32_t bitCount = 32;
|
||||
for (; currentDepth < (WAVE_TREE_DEPTH + 1); ++currentDepth)
|
||||
{
|
||||
// Figure out the location of the first bit of this element
|
||||
uint real_heap_id = 2 * heapElementID - 1;
|
||||
uint level_first_element = (1u << currentDepth) - 1;
|
||||
uint id_in_level = real_heap_id - level_first_element;
|
||||
uint first_bit = bitCount * id_in_level;
|
||||
uint local_id = first_bit % 64;
|
||||
uint64_t target_bits = (heapValue >> local_id) & mask;
|
||||
uint32_t high = uint(target_bits >> 32);
|
||||
uint32_t low = uint(target_bits);
|
||||
uint heapValue = countbits(high) + countbits(low);
|
||||
|
||||
// Does it fall in the right or left subtree?
|
||||
uint b = handle < heapValue ? 0u : 1u;
|
||||
|
||||
// Pick a subtree
|
||||
heapElementID = 2u * heapElementID + b;
|
||||
|
||||
// Move the iterator to exclude the right subtree if required
|
||||
handle -= heapValue * b;
|
||||
|
||||
// Adjust the mask and bitcount
|
||||
bitCount /= 2;
|
||||
mask = mask >> bitCount;
|
||||
}
|
||||
return (heapElementID ^ OCBT_NUM_ELEMENTS);
|
||||
#endif
|
||||
}
|
||||
|
||||
// decodes the position of the i-th zero in the bitfield
|
||||
uint decode_bit_complement(uint handle)
|
||||
{
|
||||
#if defined(NAIVE_DECODE)
|
||||
uint bitID = 1u;
|
||||
uint c = OCBT_NUM_ELEMENTS / 2u;
|
||||
|
||||
while (bitID < OCBT_NUM_ELEMENTS) {
|
||||
uint heapValue = c - get_heap_element(2u * bitID);
|
||||
uint b = handle < heapValue ? 0u : 1u;
|
||||
|
||||
bitID = 2u * bitID + b;
|
||||
handle -= heapValue * b;
|
||||
c /= 2u;
|
||||
}
|
||||
|
||||
return (bitID ^ OCBT_NUM_ELEMENTS);
|
||||
#else
|
||||
uint heapElementID = 1u;
|
||||
uint c = OCBT_NUM_ELEMENTS / 2u;
|
||||
uint currentDepth = 0;
|
||||
|
||||
for (currentDepth = 0; currentDepth < FIRST_VIRTUAL_LEVEL; ++currentDepth)
|
||||
{
|
||||
uint heapValue = c - get_heap_element(2u * heapElementID);
|
||||
uint b = handle < heapValue ? 0u : 1u;
|
||||
|
||||
heapElementID = 2u * heapElementID + b;
|
||||
handle -= heapValue * b;
|
||||
c /= 2u;
|
||||
}
|
||||
|
||||
// Align with the internal depth
|
||||
currentDepth++;
|
||||
|
||||
// Ok we have our subtree, now we need to pick the right bit
|
||||
uint64_t heapValue = pParams.bitFieldBuffer[heapElementID - OCBT_LAST_LEVEL_SIZE * 2];
|
||||
uint64_t mask = 0xffffffff;
|
||||
uint32_t bitCount = 32;
|
||||
for (; currentDepth < (WAVE_TREE_DEPTH + 1); ++currentDepth)
|
||||
{
|
||||
// Figure out the location of the first bit of this element
|
||||
uint real_heap_id = 2 * heapElementID - 1;
|
||||
uint level_first_element = (1u << currentDepth) - 1;
|
||||
uint id_in_level = real_heap_id - level_first_element;
|
||||
uint first_bit = bitCount * id_in_level;
|
||||
uint local_id = first_bit % 64;
|
||||
uint64_t target_bits = (heapValue >> local_id) & mask;
|
||||
uint32_t high = uint(target_bits >> 32);
|
||||
uint32_t low = uint(target_bits);
|
||||
uint heapValue = c - (countbits(high) + countbits(low));
|
||||
|
||||
uint b = handle < heapValue ? 0u : 1u;
|
||||
|
||||
heapElementID = 2u * heapElementID + b;
|
||||
handle -= heapValue * b;
|
||||
c /= 2u;
|
||||
|
||||
// Adjust the mask and bitcount
|
||||
bitCount /= 2;
|
||||
mask = mask >> bitCount;
|
||||
}
|
||||
|
||||
return (heapElementID ^ OCBT_NUM_ELEMENTS);
|
||||
#endif
|
||||
}
|
||||
|
||||
void reduce(uint groupIndex)
|
||||
{
|
||||
// First we do a reduction until each lane has exactly one element to process
|
||||
uint initial_pass_size = OCBT_NUM_ELEMENTS / WORKGROUP_SIZE;
|
||||
for (uint it = initial_pass_size / 64, offset = OCBT_NUM_ELEMENTS / 64; it > 0 ; it >>=1, offset >>=1)
|
||||
{
|
||||
uint minHeapID = offset + (groupIndex * it);
|
||||
uint maxHeapID = offset + ((groupIndex + 1) * it);
|
||||
|
||||
for (uint heapID = minHeapID; heapID < maxHeapID; ++heapID)
|
||||
{
|
||||
set_heap_element(heapID, get_heap_element(heapID * 2) + get_heap_element(heapID * 2 + 1));
|
||||
}
|
||||
}
|
||||
GroupMemoryBarrierWithGroupSync();
|
||||
|
||||
for(uint s = WORKGROUP_SIZE / 2; s > 0u; s >>= 1)
|
||||
{
|
||||
if (groupIndex < s)
|
||||
{
|
||||
uint v = s + groupIndex;
|
||||
set_heap_element(v, get_heap_element(v * 2) + get_heap_element(v * 2 + 1));
|
||||
}
|
||||
GroupMemoryBarrierWithGroupSync();
|
||||
}
|
||||
}
|
||||
|
||||
void reduce_prepass(uint dispatchThreadID)
|
||||
{
|
||||
// Initialize the packed sum
|
||||
uint packedSum = 0;
|
||||
|
||||
// Loop through the 4 pairs to process
|
||||
for (uint pairIdx = 0; pairIdx < 4; ++pairIdx)
|
||||
{
|
||||
// First element of the pair
|
||||
uint64_t target_bits = pParams.bitFieldBuffer[dispatchThreadID * 8 + 2 * pairIdx];
|
||||
uint32_t high = uint(target_bits >> 32);
|
||||
uint32_t low = uint(target_bits);
|
||||
uint elementC = countbits(high) + countbits(low);
|
||||
|
||||
// Second element of the pair
|
||||
target_bits = pParams.bitFieldBuffer[dispatchThreadID * 8 + 2 * pairIdx + 1];
|
||||
high = uint(target_bits >> 32);
|
||||
low = uint(target_bits);
|
||||
elementC += countbits(high) + countbits(low);
|
||||
|
||||
// Store in the right bits
|
||||
packedSum |= (elementC << pairIdx * 8);
|
||||
}
|
||||
|
||||
// Offset of the last level of the tree
|
||||
const uint bufferOffset = last_level_offset();
|
||||
|
||||
// Store the result into the bitfield
|
||||
pParams.cbtBuffer[bufferOffset + dispatchThreadID] = packedSum;
|
||||
}
|
||||
|
||||
void reduce_first_pass(uint dispatchThreadID, uint groupIndex)
|
||||
{
|
||||
// Load the lowest level (and only the last level)
|
||||
const uint level0Offset = OCBT_depth_offset[TREE_LAST_LEVEL] / 32;
|
||||
for (uint e = 0; e < 4; ++e)
|
||||
{
|
||||
uint target_element = 4 * dispatchThreadID + e;
|
||||
gs_cbtTree[level0Offset + target_element] = pParams.cbtBuffer[level0Offset + target_element];
|
||||
}
|
||||
GroupMemoryBarrierWithGroupSync();
|
||||
|
||||
// First we do a reduction until each lane has exactly one element to process
|
||||
uint initial_pass_size = OCBT_LAST_LEVEL_SIZE / 2;
|
||||
uint it, offset;
|
||||
for (it = initial_pass_size / 512, offset = initial_pass_size; it > 1; it >>=1, offset >>=1)
|
||||
{
|
||||
uint minHeapID = offset + (dispatchThreadID * it);
|
||||
uint maxHeapID = offset + ((dispatchThreadID + 1) * it);
|
||||
|
||||
for (uint heapID = minHeapID; heapID < maxHeapID; ++heapID)
|
||||
{
|
||||
set_heap_element(heapID, get_heap_element(heapID * 2) + get_heap_element(heapID * 2 + 1));
|
||||
}
|
||||
}
|
||||
|
||||
// Last pass needs to be atomic
|
||||
uint heapID = offset + (dispatchThreadID * it);
|
||||
set_heap_element_atomic(heapID, get_heap_element(heapID * 2) + get_heap_element(heapID * 2 + 1));
|
||||
|
||||
GroupMemoryBarrierWithGroupSync();
|
||||
|
||||
// Load the first reduced level
|
||||
const uint level2Offset = OCBT_depth_offset[TREE_LAST_LEVEL - 1] / 32;
|
||||
for (uint e = 0; e < 4; ++e)
|
||||
{
|
||||
uint target_element = 4 * dispatchThreadID + e;
|
||||
pParams.cbtBuffer[level2Offset + target_element] = gs_cbtTree[level2Offset + target_element];
|
||||
}
|
||||
|
||||
// Load the first reduced level
|
||||
const uint level3Offset = OCBT_depth_offset[TREE_LAST_LEVEL - 2] / 32;
|
||||
for (uint e = 0; e < 2; ++e)
|
||||
{
|
||||
uint target_element = 2 * dispatchThreadID + e;
|
||||
pParams.cbtBuffer[level3Offset + target_element] = gs_cbtTree[level3Offset + target_element];
|
||||
}
|
||||
|
||||
const uint level4Offset = OCBT_depth_offset[TREE_LAST_LEVEL - 3] / 32;
|
||||
pParams.cbtBuffer[level4Offset + dispatchThreadID] = gs_cbtTree[level4Offset + dispatchThreadID];
|
||||
|
||||
const uint level5Offset = OCBT_depth_offset[TREE_LAST_LEVEL - 4] / 32;
|
||||
if (groupIndex % 2 == 0)
|
||||
pParams.cbtBuffer[level5Offset + dispatchThreadID / 2] = gs_cbtTree[level5Offset + dispatchThreadID / 2];
|
||||
}
|
||||
void reduce_second_pass(uint groupIndex)
|
||||
{
|
||||
// Load the lowest level (and only the last level)
|
||||
const uint level0Offset = OCBT_depth_offset[9] / 32;
|
||||
for (uint e = 0; e < 4; ++e)
|
||||
{
|
||||
uint target_element = 4 * groupIndex + e;
|
||||
gs_cbtTree[level0Offset + target_element] = pParams.cbtBuffer[level0Offset + target_element];
|
||||
}
|
||||
GroupMemoryBarrierWithGroupSync();
|
||||
|
||||
// First we do a reduction until each lane has exactly one element to process
|
||||
uint initial_pass_size = 256;
|
||||
for (uint it = initial_pass_size / 64, offset = initial_pass_size; it > 0 ; it >>=1, offset >>=1)
|
||||
{
|
||||
uint minHeapID = offset + (groupIndex * it);
|
||||
uint maxHeapID = offset + ((groupIndex + 1) * it);
|
||||
|
||||
for (uint heapID = minHeapID; heapID < maxHeapID; ++heapID)
|
||||
{
|
||||
set_heap_element(heapID, get_heap_element(heapID * 2) + get_heap_element(heapID * 2 + 1));
|
||||
}
|
||||
}
|
||||
GroupMemoryBarrierWithGroupSync();
|
||||
|
||||
for(uint s = WORKGROUP_SIZE / 2; s > 0u; s >>= 1)
|
||||
{
|
||||
if (groupIndex < s)
|
||||
{
|
||||
uint v = s + groupIndex;
|
||||
set_heap_element(v, get_heap_element(v * 2) + get_heap_element(v * 2 + 1));
|
||||
}
|
||||
GroupMemoryBarrierWithGroupSync();
|
||||
}
|
||||
|
||||
// Make sure all the previous operations are done
|
||||
GroupMemoryBarrierWithGroupSync();
|
||||
|
||||
// Load the bitfield to the LDS
|
||||
for (uint e = 0; e < 5; ++e)
|
||||
{
|
||||
uint target_element = 5 * groupIndex + e;
|
||||
if (target_element < 319)
|
||||
pParams.cbtBuffer[target_element] = gs_cbtTree[target_element];
|
||||
}
|
||||
}
|
||||
|
||||
void reduce_no_bitfield(uint groupIndex)
|
||||
{
|
||||
// First we do a reduction until each lane has exactly one element to process
|
||||
uint initial_pass_size = OCBT_NUM_ELEMENTS / WORKGROUP_SIZE;
|
||||
for (uint it = initial_pass_size / 128, offset = OCBT_NUM_ELEMENTS / 128; it > 0 ; it >>=1, offset >>=1)
|
||||
{
|
||||
uint minHeapID = offset + (groupIndex * it);
|
||||
uint maxHeapID = offset + ((groupIndex + 1) * it);
|
||||
|
||||
for (uint heapID = minHeapID; heapID < maxHeapID; ++heapID)
|
||||
{
|
||||
set_heap_element(heapID, get_heap_element(heapID * 2) + get_heap_element(heapID * 2 + 1));
|
||||
}
|
||||
}
|
||||
GroupMemoryBarrierWithGroupSync();
|
||||
|
||||
for(uint s = WORKGROUP_SIZE / 2; s > 0u; s >>= 1)
|
||||
{
|
||||
if (groupIndex < s)
|
||||
{
|
||||
uint v = s + groupIndex;
|
||||
set_heap_element(v, get_heap_element(v * 2) + get_heap_element(v * 2 + 1));
|
||||
}
|
||||
GroupMemoryBarrierWithGroupSync();
|
||||
}
|
||||
}
|
||||
|
||||
void clear_cbt(uint groupIndex)
|
||||
{
|
||||
for (uint v = 0; v < BUFFER_ELEMENT_PER_LANE; ++v)
|
||||
{
|
||||
uint target_element = BUFFER_ELEMENT_PER_LANE * groupIndex + v;
|
||||
if (target_element < OCBT_TREE_NUM_SLOTS)
|
||||
gs_cbtTree[target_element] = 0;
|
||||
}
|
||||
|
||||
for (uint b = 0; b < BITFIELD_ELEMENT_PER_LANE; ++b)
|
||||
{
|
||||
uint target_element = BITFIELD_ELEMENT_PER_LANE * groupIndex + b;
|
||||
if (target_element < OCBT_BITFIELD_NUM_SLOTS)
|
||||
pParams.bitFieldBuffer[target_element] = 0;
|
||||
}
|
||||
GroupMemoryBarrierWithGroupSync();
|
||||
}
|
||||
|
||||
// Importante note
|
||||
// Depending on your target GPU architecture, the pattern used to load has a different performance behavior
|
||||
// here is the best performant based on our tests:
|
||||
// NVIDIA uint target_element = groupIndex + WORKGROUP_SIZE * e;
|
||||
// AMD uint target_element = BUFFER_ELEMENT_PER_LANE * groupIndex + e;
|
||||
|
||||
void load_buffer_to_shared_memory(uint groupIndex)
|
||||
{
|
||||
// Load the bitfield to the LDS
|
||||
for (uint e = 0; e < BUFFER_ELEMENT_PER_LANE; ++e)
|
||||
{
|
||||
#ifdef AMD
|
||||
uint target_element = BUFFER_ELEMENT_PER_LANE * groupIndex + e;
|
||||
#else
|
||||
uint target_element = groupIndex + WORKGROUP_SIZE * e;
|
||||
#endif
|
||||
if (target_element < OCBT_TREE_NUM_SLOTS)
|
||||
gs_cbtTree[target_element] = pParams.cbtBuffer[target_element];
|
||||
}
|
||||
GroupMemoryBarrierWithGroupSync();
|
||||
}
|
||||
|
||||
void load_shared_memory_to_buffer(uint groupIndex)
|
||||
{
|
||||
// Make sure all the previous operations are done
|
||||
GroupMemoryBarrierWithGroupSync();
|
||||
|
||||
// Load the bitfield to the LDS
|
||||
for (uint e = 0; e < BUFFER_ELEMENT_PER_LANE; ++e)
|
||||
{
|
||||
#ifdef AMD
|
||||
uint target_element = BUFFER_ELEMENT_PER_LANE * groupIndex + e;
|
||||
#else
|
||||
uint target_element = groupIndex + WORKGROUP_SIZE * e;
|
||||
#endif
|
||||
if (target_element < OCBT_TREE_NUM_SLOTS)
|
||||
pParams.cbtBuffer[target_element] = gs_cbtTree[target_element];
|
||||
}
|
||||
}
|
||||
|
||||
void set_bit_atomic_buffer(uint bitID, bool state)
|
||||
{
|
||||
// Coordinates of the bit
|
||||
uint32_t slot = bitID / 64;
|
||||
uint32_t local_id = bitID % 64;
|
||||
|
||||
if (state)
|
||||
InterlockedOr(pParams.bitFieldBuffer[slot], 1uLL << local_id);
|
||||
else
|
||||
InterlockedAnd(pParams.bitFieldBuffer[slot], ~(1uLL << local_id));
|
||||
}
|
||||
|
||||
uint32_t bit_count_buffer()
|
||||
{
|
||||
return pParams.cbtBuffer[0];
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,56 @@
|
||||
import Common;
|
||||
import Bisector;
|
||||
|
||||
struct GeometryCB
|
||||
{
|
||||
uint32_t totalNumElements;
|
||||
uint32_t baseDepth;
|
||||
uint32_t totalNumVertices;
|
||||
};
|
||||
|
||||
struct DeformationCB
|
||||
{
|
||||
float4 patchSize;
|
||||
float4 patchRoughness;
|
||||
float choppiness;
|
||||
int attenuation;
|
||||
float amplification;
|
||||
uint32_t patchFlags;
|
||||
};
|
||||
|
||||
struct UpdateCB
|
||||
{
|
||||
float triangleSize;
|
||||
uint32_t maxSubdivisionDepth;
|
||||
float fov;
|
||||
float farPlaneDistance;
|
||||
}
|
||||
|
||||
struct ComputeParams
|
||||
{
|
||||
ConstantBuffer<GeometryCB> geometry;
|
||||
ConstantBuffer<UpdateCB> update;
|
||||
|
||||
RWStructuredBuffer<float3> currentVertexBuffer;
|
||||
StructuredBuffer<uint> indexedBisectorBuffer;
|
||||
RWStructuredBuffer<uint> indirectDrawBuffer;
|
||||
RWStructuredBuffer<uint64_t> heapIDBuffer;
|
||||
RWStructuredBuffer<BisectorData> bisectorDataBuffer;
|
||||
RWStructuredBuffer<uint> classificationBuffer;
|
||||
RWStructuredBuffer<int> allocateBuffer;
|
||||
RWStructuredBuffer<uint> indirectDispatchBuffer;
|
||||
RWStructuredBuffer<uint3> neighboursBuffer;
|
||||
RWStructuredBuffer<uint3> neighboursOutputBuffer;
|
||||
RWStructuredBuffer<int> memoryBuffer;
|
||||
RWStructuredBuffer<uint> cbtBuffer;
|
||||
RWStructuredBuffer<uint64_t> bitFieldBuffer;
|
||||
RWStructuredBuffer<int> propagateBuffer;
|
||||
RWStructuredBuffer<uint> simplifyBuffer;
|
||||
RWStructuredBuffer<uint> validationBuffer;
|
||||
RWStructuredBuffer<uint> bisectorIndicesBuffer;
|
||||
RWStructuredBuffer<uint> visibleBisectorIndices;
|
||||
RWStructuredBuffer<uint> modifiedBisectorIndices;
|
||||
RWStructuredBuffer<float3> lebPositionBuffer;
|
||||
StructuredBuffer<float3x3> lebMatrixCache;
|
||||
};
|
||||
ParameterBlock<ComputeParams> pParams;
|
||||
Reference in New Issue
Block a user