Ring buffered the path tracer.

This commit is contained in:
Justin Marshall
2026-05-04 20:36:59 -07:00
parent e7b2d3f5a7
commit a6c6f1ed26
+437 -92
View File
@@ -332,22 +332,51 @@ static D3D12_GPU_DESCRIPTOR_HANDLE glRaytracingOffsetGpu(D3D12_GPU_DESCRIPTOR_HA
// Shared command context
// ============================================================
#ifndef GL_RAYTRACING_CMD_RING_SIZE
#define GL_RAYTRACING_CMD_RING_SIZE 4
#endif
#if GL_RAYTRACING_CMD_RING_SIZE < 2
#error GL_RAYTRACING_CMD_RING_SIZE must be at least 2
#endif
#ifndef GL_RAYTRACING_FORCE_CPU_SYNC
#define GL_RAYTRACING_FORCE_CPU_SYNC 0
#endif
struct glRaytracingCmdContext_t
{
ComPtr<ID3D12Device5> device;
ComPtr<ID3D12CommandQueue> queue;
// Active command allocator/list for the command currently being recorded.
// The ring below avoids the old every-frame CPU/GPU fence wait.
ComPtr<ID3D12CommandAllocator> cmdAlloc;
ComPtr<ID3D12GraphicsCommandList4> cmdList;
UINT64 cmdLastFenceValue;
ComPtr<ID3D12CommandAllocator> cmdAllocRing[GL_RAYTRACING_CMD_RING_SIZE];
ComPtr<ID3D12GraphicsCommandList4> cmdListRing[GL_RAYTRACING_CMD_RING_SIZE];
UINT64 cmdFenceValueRing[GL_RAYTRACING_CMD_RING_SIZE];
UINT cmdRingIndex;
UINT cmdCurrentSlot;
ComPtr<ID3D12CommandAllocator> blasCmdAlloc;
ComPtr<ID3D12GraphicsCommandList4> blasCmdList;
UINT64 blasLastFenceValue;
ComPtr<ID3D12CommandAllocator> blasCmdAllocRing[GL_RAYTRACING_CMD_RING_SIZE];
ComPtr<ID3D12GraphicsCommandList4> blasCmdListRing[GL_RAYTRACING_CMD_RING_SIZE];
UINT64 blasFenceValueRing[GL_RAYTRACING_CMD_RING_SIZE];
UINT blasRingIndex;
UINT blasCurrentSlot;
ComPtr<ID3D12CommandAllocator> tlasCmdAlloc;
ComPtr<ID3D12GraphicsCommandList4> tlasCmdList;
UINT64 tlasLastFenceValue;
ComPtr<ID3D12CommandAllocator> tlasCmdAllocRing[GL_RAYTRACING_CMD_RING_SIZE];
ComPtr<ID3D12GraphicsCommandList4> tlasCmdListRing[GL_RAYTRACING_CMD_RING_SIZE];
UINT64 tlasFenceValueRing[GL_RAYTRACING_CMD_RING_SIZE];
UINT tlasRingIndex;
UINT tlasCurrentSlot;
ComPtr<ID3D12Fence> fence;
HANDLE fenceEvent;
@@ -359,6 +388,18 @@ struct glRaytracingCmdContext_t
cmdLastFenceValue = 0;
blasLastFenceValue = 0;
tlasLastFenceValue = 0;
cmdRingIndex = GL_RAYTRACING_CMD_RING_SIZE - 1;
blasRingIndex = GL_RAYTRACING_CMD_RING_SIZE - 1;
tlasRingIndex = GL_RAYTRACING_CMD_RING_SIZE - 1;
cmdCurrentSlot = 0;
blasCurrentSlot = 0;
tlasCurrentSlot = 0;
for (UINT i = 0; i < GL_RAYTRACING_CMD_RING_SIZE; ++i)
{
cmdFenceValueRing[i] = 0;
blasFenceValueRing[i] = 0;
tlasFenceValueRing[i] = 0;
}
fenceEvent = nullptr;
nextFenceValue = 0;
initialized = false;
@@ -396,6 +437,26 @@ static void glRaytracingWaitIdle(void)
glRaytracingWaitFenceValue(value);
}
static int glRaytracingCreateDirectCommandListPair(
ID3D12Device5* device,
ComPtr<ID3D12CommandAllocator>& allocator,
ComPtr<ID3D12GraphicsCommandList4>& list)
{
GLR_CHECK(device->CreateCommandAllocator(
D3D12_COMMAND_LIST_TYPE_DIRECT,
IID_PPV_ARGS(&allocator)));
GLR_CHECK(device->CreateCommandList(
0,
D3D12_COMMAND_LIST_TYPE_DIRECT,
allocator.Get(),
nullptr,
IID_PPV_ARGS(&list)));
GLR_CHECK(list->Close());
return 1;
}
static int glRaytracingInitCmdContext(void)
{
ID3D12Device* baseDevice = QD3D12_GetDevice();
@@ -429,44 +490,39 @@ static int glRaytracingInitCmdContext(void)
GLR_CHECK(baseDevice->QueryInterface(IID_PPV_ARGS(&g_glRaytracingCmd.device)));
g_glRaytracingCmd.queue = baseQueue;
GLR_CHECK(g_glRaytracingCmd.device->CreateCommandAllocator(
D3D12_COMMAND_LIST_TYPE_DIRECT,
IID_PPV_ARGS(&g_glRaytracingCmd.cmdAlloc)));
for (UINT i = 0; i < GL_RAYTRACING_CMD_RING_SIZE; ++i)
{
if (!glRaytracingCreateDirectCommandListPair(
g_glRaytracingCmd.device.Get(),
g_glRaytracingCmd.cmdAllocRing[i],
g_glRaytracingCmd.cmdListRing[i]))
{
return 0;
}
GLR_CHECK(g_glRaytracingCmd.device->CreateCommandList(
0,
D3D12_COMMAND_LIST_TYPE_DIRECT,
g_glRaytracingCmd.cmdAlloc.Get(),
nullptr,
IID_PPV_ARGS(&g_glRaytracingCmd.cmdList)));
if (!glRaytracingCreateDirectCommandListPair(
g_glRaytracingCmd.device.Get(),
g_glRaytracingCmd.blasCmdAllocRing[i],
g_glRaytracingCmd.blasCmdListRing[i]))
{
return 0;
}
GLR_CHECK(g_glRaytracingCmd.cmdList->Close());
if (!glRaytracingCreateDirectCommandListPair(
g_glRaytracingCmd.device.Get(),
g_glRaytracingCmd.tlasCmdAllocRing[i],
g_glRaytracingCmd.tlasCmdListRing[i]))
{
return 0;
}
}
GLR_CHECK(g_glRaytracingCmd.device->CreateCommandAllocator(
D3D12_COMMAND_LIST_TYPE_DIRECT,
IID_PPV_ARGS(&g_glRaytracingCmd.blasCmdAlloc)));
GLR_CHECK(g_glRaytracingCmd.device->CreateCommandList(
0,
D3D12_COMMAND_LIST_TYPE_DIRECT,
g_glRaytracingCmd.blasCmdAlloc.Get(),
nullptr,
IID_PPV_ARGS(&g_glRaytracingCmd.blasCmdList)));
GLR_CHECK(g_glRaytracingCmd.blasCmdList->Close());
GLR_CHECK(g_glRaytracingCmd.device->CreateCommandAllocator(
D3D12_COMMAND_LIST_TYPE_DIRECT,
IID_PPV_ARGS(&g_glRaytracingCmd.tlasCmdAlloc)));
GLR_CHECK(g_glRaytracingCmd.device->CreateCommandList(
0,
D3D12_COMMAND_LIST_TYPE_DIRECT,
g_glRaytracingCmd.tlasCmdAlloc.Get(),
nullptr,
IID_PPV_ARGS(&g_glRaytracingCmd.tlasCmdList)));
GLR_CHECK(g_glRaytracingCmd.tlasCmdList->Close());
g_glRaytracingCmd.cmdAlloc = g_glRaytracingCmd.cmdAllocRing[0];
g_glRaytracingCmd.cmdList = g_glRaytracingCmd.cmdListRing[0];
g_glRaytracingCmd.blasCmdAlloc = g_glRaytracingCmd.blasCmdAllocRing[0];
g_glRaytracingCmd.blasCmdList = g_glRaytracingCmd.blasCmdListRing[0];
g_glRaytracingCmd.tlasCmdAlloc = g_glRaytracingCmd.tlasCmdAllocRing[0];
g_glRaytracingCmd.tlasCmdList = g_glRaytracingCmd.tlasCmdListRing[0];
GLR_CHECK(g_glRaytracingCmd.device->CreateFence(
0,
@@ -502,7 +558,13 @@ static void glRaytracingShutdownCmdContext(void)
static int glRaytracingBeginCmd(void)
{
glRaytracingWaitFenceValue(g_glRaytracingCmd.cmdLastFenceValue);
const UINT slot = (g_glRaytracingCmd.cmdRingIndex + 1u) % GL_RAYTRACING_CMD_RING_SIZE;
glRaytracingWaitFenceValue(g_glRaytracingCmd.cmdFenceValueRing[slot]);
g_glRaytracingCmd.cmdRingIndex = slot;
g_glRaytracingCmd.cmdCurrentSlot = slot;
g_glRaytracingCmd.cmdAlloc = g_glRaytracingCmd.cmdAllocRing[slot];
g_glRaytracingCmd.cmdList = g_glRaytracingCmd.cmdListRing[slot];
GLR_CHECK(g_glRaytracingCmd.cmdAlloc->Reset());
GLR_CHECK(g_glRaytracingCmd.cmdList->Reset(g_glRaytracingCmd.cmdAlloc.Get(), nullptr));
@@ -517,13 +579,24 @@ static int glRaytracingEndCmd(void)
g_glRaytracingCmd.queue->ExecuteCommandLists(1, lists);
g_glRaytracingCmd.cmdLastFenceValue = glRaytracingSignalQueue();
g_glRaytracingCmd.cmdFenceValueRing[g_glRaytracingCmd.cmdCurrentSlot] = g_glRaytracingCmd.cmdLastFenceValue;
#if GL_RAYTRACING_FORCE_CPU_SYNC
glRaytracingWaitFenceValue(g_glRaytracingCmd.cmdLastFenceValue);
#endif
return 1;
}
static int glRaytracingBeginBlasCmd(void)
{
glRaytracingWaitFenceValue(g_glRaytracingCmd.blasLastFenceValue);
const UINT slot = (g_glRaytracingCmd.blasRingIndex + 1u) % GL_RAYTRACING_CMD_RING_SIZE;
glRaytracingWaitFenceValue(g_glRaytracingCmd.blasFenceValueRing[slot]);
g_glRaytracingCmd.blasRingIndex = slot;
g_glRaytracingCmd.blasCurrentSlot = slot;
g_glRaytracingCmd.blasCmdAlloc = g_glRaytracingCmd.blasCmdAllocRing[slot];
g_glRaytracingCmd.blasCmdList = g_glRaytracingCmd.blasCmdListRing[slot];
GLR_CHECK(g_glRaytracingCmd.blasCmdAlloc->Reset());
GLR_CHECK(g_glRaytracingCmd.blasCmdList->Reset(g_glRaytracingCmd.blasCmdAlloc.Get(), nullptr));
@@ -538,12 +611,24 @@ static UINT64 glRaytracingEndBlasCmd(void)
g_glRaytracingCmd.queue->ExecuteCommandLists(1, lists);
g_glRaytracingCmd.blasLastFenceValue = glRaytracingSignalQueue();
g_glRaytracingCmd.blasFenceValueRing[g_glRaytracingCmd.blasCurrentSlot] = g_glRaytracingCmd.blasLastFenceValue;
#if GL_RAYTRACING_FORCE_CPU_SYNC
glRaytracingWaitFenceValue(g_glRaytracingCmd.blasLastFenceValue);
#endif
return g_glRaytracingCmd.blasLastFenceValue;
}
static int glRaytracingBeginTlasCmd(void)
{
glRaytracingWaitFenceValue(g_glRaytracingCmd.tlasLastFenceValue);
const UINT slot = (g_glRaytracingCmd.tlasRingIndex + 1u) % GL_RAYTRACING_CMD_RING_SIZE;
glRaytracingWaitFenceValue(g_glRaytracingCmd.tlasFenceValueRing[slot]);
g_glRaytracingCmd.tlasRingIndex = slot;
g_glRaytracingCmd.tlasCurrentSlot = slot;
g_glRaytracingCmd.tlasCmdAlloc = g_glRaytracingCmd.tlasCmdAllocRing[slot];
g_glRaytracingCmd.tlasCmdList = g_glRaytracingCmd.tlasCmdListRing[slot];
GLR_CHECK(g_glRaytracingCmd.tlasCmdAlloc->Reset());
GLR_CHECK(g_glRaytracingCmd.tlasCmdList->Reset(g_glRaytracingCmd.tlasCmdAlloc.Get(), nullptr));
@@ -558,6 +643,12 @@ static UINT64 glRaytracingEndTlasCmd(void)
g_glRaytracingCmd.queue->ExecuteCommandLists(1, lists);
g_glRaytracingCmd.tlasLastFenceValue = glRaytracingSignalQueue();
g_glRaytracingCmd.tlasFenceValueRing[g_glRaytracingCmd.tlasCurrentSlot] = g_glRaytracingCmd.tlasLastFenceValue;
#if GL_RAYTRACING_FORCE_CPU_SYNC
glRaytracingWaitFenceValue(g_glRaytracingCmd.tlasLastFenceValue);
#endif
return g_glRaytracingCmd.tlasLastFenceValue;
}
@@ -602,6 +693,7 @@ struct glRaytracingMeshRecord_t
int blasBuilt;
int dirty;
UINT64 blasBuildFenceValue;
int currentBlasIndex;
uint32_t materialFlags;
@@ -614,6 +706,7 @@ struct glRaytracingMeshRecord_t
blasResultSize = 0;
blasBuilt = 0;
dirty = 0;
blasBuildFenceValue = 0;
currentBlasIndex = 0;
materialFlags = 0;
}
@@ -669,6 +762,7 @@ struct glRaytracingRenderWorld_t
uint32_t nextInstanceHandle;
glRaytracingSceneUploadBuffer_t instanceDescUpload[2];
UINT64 instanceDescUploadFenceValue[2];
glRaytracingBuffer_t tlasScratch;
glRaytracingBuffer_t tlasResult[2];
@@ -688,6 +782,8 @@ struct glRaytracingRenderWorld_t
handle = 0;
alive = 0;
nextInstanceHandle = 1;
instanceDescUploadFenceValue[0] = 0;
instanceDescUploadFenceValue[1] = 0;
tlasScratchSize = 0;
tlasResultSize = 0;
activeInstanceCount = 0;
@@ -736,6 +832,8 @@ static void glRaytracingReleaseWorldResources(glRaytracingRenderWorld_t* world)
world->tlasResult[i].resource.Reset();
}
world->instanceDescUploadFenceValue[0] = 0;
world->instanceDescUploadFenceValue[1] = 0;
world->tlasScratch.resource.Reset();
world->tlasScratchSize = 0;
world->tlasResultSize = 0;
@@ -804,6 +902,8 @@ static void glRaytracingClearWorldContents(glRaytracingRenderWorld_t* world)
world->handle = handle;
world->alive = alive;
world->nextInstanceHandle = 1;
world->instanceDescUploadFenceValue[0] = 0;
world->instanceDescUploadFenceValue[1] = 0;
world->tlasScratchSize = 0;
world->tlasResultSize = 0;
world->activeInstanceCount = 0;
@@ -949,6 +1049,18 @@ static int glRaytracingEnsureTLASBuffers(
prebuild.ResultDataMaxSizeInBytes,
D3D12_RAYTRACING_ACCELERATION_STRUCTURE_BYTE_ALIGNMENT);
const bool resizingScratch = world->tlasScratch.resource &&
world->tlasScratchSize < requiredScratch;
const bool resizingResult = (world->tlasResult[0].resource || world->tlasResult[1].resource) &&
world->tlasResultSize < requiredResult;
if (resizingScratch || resizingResult)
{
// Releasing/reallocating a TLAS resource can invalidate an in-flight ray
// dispatch that still references the old resource. This resize path is rare,
// so prefer correctness over complex deferred destruction.
glRaytracingWaitIdle();
}
if (!world->tlasScratch.resource ||
world->tlasScratchSize < requiredScratch)
{
@@ -1061,6 +1173,7 @@ static int glRaytracingEnsureMeshScratch(glRaytracingMeshRecord_t* mesh, UINT64
if (!mesh->blasScratch.resource || mesh->blasScratchSize < requiredScratch)
{
glRaytracingWaitFenceValue(mesh->blasBuildFenceValue);
mesh->blasScratch.resource.Reset();
mesh->blasScratch = glRaytracingCreateBuffer(
g_glRaytracingCmd.device.Get(),
@@ -1090,6 +1203,7 @@ static int glRaytracingEnsureMeshResultBuffers(glRaytracingMeshRecord_t* mesh, U
{
if (!mesh->blasResult[i].resource || mesh->blasResultSize < requiredResult)
{
glRaytracingWaitFenceValue(mesh->blasBuildFenceValue);
mesh->blasResult[i].resource.Reset();
mesh->blasResult[i] = glRaytracingCreateBuffer(
g_glRaytracingCmd.device.Get(),
@@ -1103,8 +1217,11 @@ static int glRaytracingEnsureMeshResultBuffers(glRaytracingMeshRecord_t* mesh, U
}
}
if (!mesh->descCpu.allowUpdate)
if (!mesh->descCpu.allowUpdate && mesh->blasResult[1].resource)
{
glRaytracingWaitFenceValue(mesh->blasBuildFenceValue);
mesh->blasResult[1].resource.Reset();
}
mesh->blasResultSize = requiredResult;
return 1;
@@ -1254,6 +1371,9 @@ static int glRaytracingUploadCachedInstanceDescs(glRaytracingRenderWorld_t* worl
(UINT64)activeCount * (UINT64)sizeof(D3D12_RAYTRACING_INSTANCE_DESC),
D3D12_RAYTRACING_INSTANCE_DESCS_BYTE_ALIGNMENT);
const int uploadIndex = glRaytracingGetInactiveTLASIndex(world);
glRaytracingWaitFenceValue(world->instanceDescUploadFenceValue[uploadIndex]);
if (!glRaytracingEnsureSceneUploadBuffer(world, instBytes))
return 0;
@@ -1384,12 +1504,15 @@ static int glRaytracingBuildDirtyMeshesInternal(void)
if (!blasFenceValue)
return 0;
glRaytracingWaitFenceValue(blasFenceValue);
// Do not block the CPU here. The BLAS build was submitted before any TLAS
// build/lighting work that consumes it, and the shared D3D12 queue preserves
// that order. Command allocator reuse is protected by the ring fence in
// glRaytracingBeginBlasCmd().
for (size_t i = 0; i < builds.size(); ++i)
{
glRaytracingMeshRecord_t* mesh = builds[i].mesh;
mesh->currentBlasIndex = builds[i].newBlasIndex;
mesh->blasBuildFenceValue = blasFenceValue;
mesh->blasBuilt = 1;
mesh->dirty = 0;
}
@@ -1588,11 +1711,12 @@ static int glRaytracingBuildSceneInternal(glRaytracingRenderWorld_t* world)
if (!glRaytracingEnsureTLASBuffers(world, &inputs))
return 0;
const int buildTLASIndex = glRaytracingGetInactiveTLASIndex(world);
glRaytracingBuffer_t* dstTLAS = glRaytracingGetBuildTLASBuffer(world);
const glRaytracingBuffer_t* srcTLAS = glRaytracingGetCurrentTLASBufferConst(world);
glRaytracingWaitFenceValue(g_glRaytracingCmd.blasLastFenceValue);
// The TLAS build is queued after any pending BLAS builds on the same D3D12
// queue, so GPU ordering is sufficient and a CPU wait would stall the frame.
if (!glRaytracingBeginTlasCmd())
return 0;
@@ -1619,9 +1743,13 @@ static int glRaytracingBuildSceneInternal(glRaytracingRenderWorld_t* world)
if (!tlasFenceValue)
return 0;
glRaytracingWaitFenceValue(tlasFenceValue);
world->currentTLASIndex = glRaytracingGetInactiveTLASIndex(world);
// Keep TLAS builds asynchronous. Later ray dispatches are submitted to the
// same queue after this command list, so the GPU sees a complete TLAS before
// tracing without forcing the CPU to wait every update. The upload buffer is
// fence-tagged so the CPU does not overwrite instance descriptors still being
// consumed by an in-flight TLAS build.
world->instanceDescUploadFenceValue[buildTLASIndex] = tlasFenceValue;
world->currentTLASIndex = buildTLASIndex;
world->activeInstanceCount = activeCount;
world->builtInstanceCount = activeCount;
world->tlasBuilt = 1;
@@ -1696,6 +1824,7 @@ void glRaytracingShutdown(void)
if (!g_glRaytracingScene.initialized)
return;
glRaytracingWaitIdle();
glRaytracingClearAllSceneStateInternal();
g_glRaytracingScene = glRaytracingSceneState_t();
glRaytracingShutdownCmdContext();
@@ -1705,6 +1834,7 @@ void glRaytracingClear(void)
{
std::lock_guard<std::mutex> lock(g_glRaytracingMutex);
glRaytracingWaitIdle();
glRaytracingClearAllSceneStateInternal();
}
@@ -1737,6 +1867,7 @@ void glRaytracingClearScene(glRaytracingSceneHandle_t worldHandle)
if (!world)
return;
glRaytracingWaitIdle();
glRaytracingClearWorldContents(world);
}
@@ -1748,6 +1879,7 @@ void glRaytracingDeleteScene(glRaytracingSceneHandle_t worldHandle)
if (!world)
return;
glRaytracingWaitIdle();
glRaytracingReleaseWorldResources(world);
*world = glRaytracingRenderWorld_t();
}
@@ -1813,6 +1945,11 @@ int glRaytracingUpdateMesh(glRaytracingMeshHandle_t meshHandle, const glRaytraci
mesh->descCpu.vertices = nullptr;
mesh->descCpu.indices = nullptr;
// Updating a mesh destroys/replaces resources that an already submitted frame
// may still reference. Wait only for this destructive path; steady-state
// rendering remains asynchronous.
glRaytracingWaitIdle();
mesh->vertexBuffer.resource.Reset();
mesh->indexBuffer.resource.Reset();
mesh->blasScratch.resource.Reset();
@@ -1820,6 +1957,7 @@ int glRaytracingUpdateMesh(glRaytracingMeshHandle_t meshHandle, const glRaytraci
mesh->blasResult[1].resource.Reset();
mesh->blasScratchSize = 0;
mesh->blasResultSize = 0;
mesh->blasBuildFenceValue = 0;
mesh->blasBuilt = 0;
mesh->dirty = 1;
mesh->currentBlasIndex = 0;
@@ -1881,6 +2019,7 @@ void glRaytracingDeleteMesh(glRaytracingMeshHandle_t meshHandle)
if (!mesh)
return;
glRaytracingWaitIdle();
glRaytracingInvalidateInstancesForMesh(meshHandle, 1);
mesh->alive = 0;
@@ -1891,6 +2030,7 @@ void glRaytracingDeleteMesh(glRaytracingMeshHandle_t meshHandle)
mesh->blasResult[1].resource.Reset();
mesh->blasScratchSize = 0;
mesh->blasResultSize = 0;
mesh->blasBuildFenceValue = 0;
mesh->blasBuilt = 0;
mesh->dirty = 0;
@@ -2109,10 +2249,17 @@ struct glRaytracingLightingState_t
glRaytracingLightingConstants_t constants;
ComPtr<ID3D12DescriptorHeap> descriptorHeap;
ComPtr<ID3D12DescriptorHeap> descriptorHeapRing[GL_RAYTRACING_CMD_RING_SIZE];
UINT descriptorStride;
glRaytracingBuffer_t constantBuffer;
glRaytracingBuffer_t lightBuffer;
void* constantBufferMapped;
void* lightBufferMapped;
glRaytracingBuffer_t constantBufferRing[GL_RAYTRACING_CMD_RING_SIZE];
glRaytracingBuffer_t lightBufferRing[GL_RAYTRACING_CMD_RING_SIZE];
void* constantBufferMappedRing[GL_RAYTRACING_CMD_RING_SIZE];
void* lightBufferMappedRing[GL_RAYTRACING_CMD_RING_SIZE];
ComPtr<ID3D12RootSignature> globalRootSig;
ComPtr<ID3D12RootSignature> localRootSig;
@@ -2132,23 +2279,39 @@ struct glRaytracingLightingState_t
glRaytracingTexture_t denoiseTemp[2];
uint32_t currentHistoryIndex;
glRaytracingBuffer_t denoiseConstantBuffer[3];
glRaytracingBuffer_t denoiseConstantBufferRing[GL_RAYTRACING_CMD_RING_SIZE][3];
void* denoiseConstantBufferMapped[3];
void* denoiseConstantBufferMappedRing[GL_RAYTRACING_CMD_RING_SIZE][3];
UINT denoiseWidth;
UINT denoiseHeight;
DXGI_FORMAT denoiseFormat;
uint32_t frameCounter;
bool externalDenoiser;
bool uploadToCurrentFrameResource;
bool initialized;
glRaytracingLightingState_t()
{
memset(&constants, 0, sizeof(constants));
descriptorStride = 0;
constantBufferMapped = nullptr;
lightBufferMapped = nullptr;
for (UINT frame = 0; frame < GL_RAYTRACING_CMD_RING_SIZE; ++frame)
{
constantBufferMappedRing[frame] = nullptr;
lightBufferMappedRing[frame] = nullptr;
for (int i = 0; i < 3; ++i)
denoiseConstantBufferMappedRing[frame][i] = nullptr;
}
for (int i = 0; i < 3; ++i)
denoiseConstantBufferMapped[i] = nullptr;
denoiseWidth = 0;
denoiseHeight = 0;
denoiseFormat = DXGI_FORMAT_UNKNOWN;
currentHistoryIndex = 0;
frameCounter = 0;
externalDenoiser = false;
uploadToCurrentFrameResource = false;
initialized = false;
}
};
@@ -2769,7 +2932,7 @@ float TraceSpotShadow(float3 worldPos, float3 N, float3 toLight, float dist)
float TraceSoftShadow(float3 worldPos, float3 N, Light Lgt, float3 toLight, float dist)
{
const uint SHADOW_SAMPLES = 12;
const uint SHADOW_SAMPLES = 4;
float3 L = toLight / max(dist, 1e-6);
@@ -2815,7 +2978,7 @@ R"(
float RectLightShadow(float3 worldPos, float3 N, Light Lgt, uint2 pixel)
{
uint sampleCount = max(Lgt.samples, 1u);
sampleCount = min(sampleCount, 16u);
sampleCount = min(sampleCount, 4u);
float visibility = 0.0;
@@ -2873,7 +3036,7 @@ float RectLightShadow(float3 worldPos, float3 N, Light Lgt, uint2 pixel)
float ComputeAmbientOcclusion(float3 worldPos, float3 N, uint2 pixel)
{
const uint AO_SAMPLES = 24;
const uint AO_SAMPLES = 8;
const float AO_RADIUS = 32.0;
float3 tangent, bitangent;
@@ -2919,7 +3082,7 @@ float3 GetSkyLightDirection10AM()
R"(
float ComputeSkyVisibility(float3 worldPos, float3 N, uint2 pixel)
{
const uint SKY_SAMPLES = 12;
const uint SKY_SAMPLES = 4;
const float SKY_TMAX = 1000000.0;
// Soft angular size of the sky/sun shadow cone.
@@ -3465,7 +3628,7 @@ float3 PathTraceDirectRectLight(uint2 pixel, float3 worldPos, float3 N, float3 V
return 0.0;
uint sampleCount = max(Lgt.samples, 1u);
sampleCount = min(sampleCount, 8u);
sampleCount = min(sampleCount, 4u);
float rand = Hash12((float2)pixel + worldPos.xy + float2(centerDist, worldPos.z));
float3 diffuseAccum = 0.0;
@@ -3570,7 +3733,7 @@ float3 EstimateSingleLightVolumetricScattering(uint2 pixel, float3 cameraPos, fl
float3 viewDir = cameraToSurface / viewDist;
uint stepCount = min(max(Lgt.samples, 4u), 12u);
uint stepCount = min(max(Lgt.samples, 3u), 6u);
float stepLen = viewDist / (float)stepCount;
// Do not frame-jitter the march. This renderer has no temporal GI history,
@@ -3818,7 +3981,7 @@ float3 EstimateDirectLightingForBounceHit(uint2 hitPixel, float3 hitPos, float3
}
//if (hitIsSkeletal)
// lighting *= 1.10;
// lighting *= 1.10;
// Outgoing diffuse radiance from the bounce surface. The primary surface's
// albedo is applied later in RayGen, so only the secondary hit albedo belongs
@@ -3843,19 +4006,19 @@ float3 EstimateReactiveScreenSpaceFinalGather(uint2 pixel, float3 worldPos, floa
int2( 29, 18)
};
uint sampleBudget = 5u;
uint sampleBudget = 3u;
// Keep the pass cheap when many lights or high SPP are active. This path
// evaluates bounce lighting against the light list and traces short
// visibility rays, so adapt the sample count instead of raising ray count.
if (gLightCount > 4u)
sampleBudget = 4u;
sampleBudget = 2u;
if (gLightCount > 10u)
sampleBudget = 3u;
sampleBudget = 1u;
if (gSamplesPerPixel >= 4u)
sampleBudget = min(sampleBudget, 3u);
if (gSamplesPerPixel >= 6u)
sampleBudget = min(sampleBudget, 2u);
if (gSamplesPerPixel >= 6u)
sampleBudget = min(sampleBudget, 1u);
int2 maxPixel = int2((int)gScreenSize.x - 1, (int)gScreenSize.y - 1);
float pixelScale = max(1.0, round(min(gScreenSize.x, gScreenSize.y) / 720.0));
@@ -4092,7 +4255,7 @@ float3 ApplyPrimaryDiffusePost(float3 lightingAccum, float ao, float microShadow
lightingAccum *= ao;
lightingAccum *= microShadow;
// if (isSkeletal)
//if (isSkeletal)
// lightingAccum *= 1.2;
return max(lightingAccum, 0.0);
@@ -4137,7 +4300,7 @@ float3 PathTraceDeterministicLighting(
lightingAccum += ambientSkyVis * (skyColorRGB * 0.15);
//if (isSkeletal)
// lightingAccum += 0.1;
// lightingAccum += 0.1;
// The current direct-light evaluators are deterministic. Keep the inout RNG
// argument only because the functions share the same signature as stochastic
@@ -4700,8 +4863,10 @@ static ComPtr<IDxcBlob> glRaytracingLightingCompileLibrary(const char* src)
const wchar_t* args[] =
{
L"-T", L"lib_6_3",
#if defined(_DEBUG)
L"-Zi",
L"-Qembed_debug",
#endif
L"-O3",
L"-all_resources_bound"
};
@@ -4771,8 +4936,10 @@ static ComPtr<IDxcBlob> glRaytracingLightingCompileCompute(const char* src, cons
{
L"-E", entryPoint,
L"-T", L"cs_6_0",
#if defined(_DEBUG)
L"-Zi",
L"-Qembed_debug",
#endif
L"-O3",
L"-all_resources_bound"
};
@@ -4813,7 +4980,14 @@ static int glRaytracingLightingCreateDescriptorHeap(void)
hd.Type = D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV;
hd.Flags = D3D12_DESCRIPTOR_HEAP_FLAG_SHADER_VISIBLE;
GLR_CHECK(g_glRaytracingCmd.device->CreateDescriptorHeap(&hd, IID_PPV_ARGS(&g_glRaytracingLighting.descriptorHeap)));
for (UINT frame = 0; frame < GL_RAYTRACING_CMD_RING_SIZE; ++frame)
{
GLR_CHECK(g_glRaytracingCmd.device->CreateDescriptorHeap(
&hd,
IID_PPV_ARGS(&g_glRaytracingLighting.descriptorHeapRing[frame])));
}
g_glRaytracingLighting.descriptorHeap = g_glRaytracingLighting.descriptorHeapRing[0];
g_glRaytracingLighting.descriptorStride =
g_glRaytracingCmd.device->GetDescriptorHandleIncrementSize(D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV);
@@ -4882,39 +5056,110 @@ static int glRaytracingLightingCreateRootSignatures(void)
return 1;
}
static int glRaytracingMapUploadBufferPersistent(const glRaytracingBuffer_t& buffer, void** mapped)
{
if (!buffer.resource || !mapped)
return 0;
*mapped = nullptr;
D3D12_RANGE readRange = {};
HRESULT hr = buffer.resource->Map(0, &readRange, mapped);
if (FAILED(hr) || !*mapped)
{
glRaytracingFatal("Persistent upload Map failed 0x%08X", (unsigned)hr);
return 0;
}
return 1;
}
static void glRaytracingLightingSelectFrameResources(UINT frameSlot)
{
frameSlot %= GL_RAYTRACING_CMD_RING_SIZE;
g_glRaytracingLighting.descriptorHeap = g_glRaytracingLighting.descriptorHeapRing[frameSlot];
g_glRaytracingLighting.constantBuffer = g_glRaytracingLighting.constantBufferRing[frameSlot];
g_glRaytracingLighting.lightBuffer = g_glRaytracingLighting.lightBufferRing[frameSlot];
g_glRaytracingLighting.constantBufferMapped = g_glRaytracingLighting.constantBufferMappedRing[frameSlot];
g_glRaytracingLighting.lightBufferMapped = g_glRaytracingLighting.lightBufferMappedRing[frameSlot];
for (int i = 0; i < 3; ++i)
{
g_glRaytracingLighting.denoiseConstantBuffer[i] = g_glRaytracingLighting.denoiseConstantBufferRing[frameSlot][i];
g_glRaytracingLighting.denoiseConstantBufferMapped[i] = g_glRaytracingLighting.denoiseConstantBufferMappedRing[frameSlot][i];
}
}
static int glRaytracingLightingCreateBuffers(void)
{
const UINT64 constantsBytes = glRaytracingAlignUp(sizeof(glRaytracingLightingConstants_t), 256);
g_glRaytracingLighting.constantBuffer = glRaytracingCreateBuffer(
g_glRaytracingCmd.device.Get(),
constantsBytes,
D3D12_HEAP_TYPE_UPLOAD,
D3D12_RESOURCE_STATE_GENERIC_READ,
D3D12_RESOURCE_FLAG_NONE);
for (int i = 0; i < 3; ++i)
for (UINT frame = 0; frame < GL_RAYTRACING_CMD_RING_SIZE; ++frame)
{
g_glRaytracingLighting.denoiseConstantBuffer[i] = glRaytracingCreateBuffer(
g_glRaytracingLighting.constantBufferRing[frame] = glRaytracingCreateBuffer(
g_glRaytracingCmd.device.Get(),
constantsBytes,
D3D12_HEAP_TYPE_UPLOAD,
D3D12_RESOURCE_STATE_GENERIC_READ,
D3D12_RESOURCE_FLAG_NONE);
g_glRaytracingLighting.lightBufferRing[frame] = glRaytracingCreateBuffer(
g_glRaytracingCmd.device.Get(),
sizeof(glRaytracingLight_t) * GL_RAYTRACING_MAX_LIGHTS,
D3D12_HEAP_TYPE_UPLOAD,
D3D12_RESOURCE_STATE_GENERIC_READ,
D3D12_RESOURCE_FLAG_NONE);
if (!g_glRaytracingLighting.constantBufferRing[frame].resource ||
!g_glRaytracingLighting.lightBufferRing[frame].resource)
{
return 0;
}
for (int i = 0; i < 3; ++i)
{
g_glRaytracingLighting.denoiseConstantBufferRing[frame][i] = glRaytracingCreateBuffer(
g_glRaytracingCmd.device.Get(),
constantsBytes,
D3D12_HEAP_TYPE_UPLOAD,
D3D12_RESOURCE_STATE_GENERIC_READ,
D3D12_RESOURCE_FLAG_NONE);
if (!g_glRaytracingLighting.denoiseConstantBufferRing[frame][i].resource)
return 0;
}
// These are small UPLOAD-heap buffers updated every pass. Keeping every
// per-frame copy persistently mapped avoids Map/Unmap overhead and makes
// the async command-ring safe: the CPU never overwrites constants that an
// older in-flight command list still reads.
if (!glRaytracingMapUploadBufferPersistent(
g_glRaytracingLighting.constantBufferRing[frame],
&g_glRaytracingLighting.constantBufferMappedRing[frame]))
{
return 0;
}
if (!glRaytracingMapUploadBufferPersistent(
g_glRaytracingLighting.lightBufferRing[frame],
&g_glRaytracingLighting.lightBufferMappedRing[frame]))
{
return 0;
}
for (int i = 0; i < 3; ++i)
{
if (!glRaytracingMapUploadBufferPersistent(
g_glRaytracingLighting.denoiseConstantBufferRing[frame][i],
&g_glRaytracingLighting.denoiseConstantBufferMappedRing[frame][i]))
{
return 0;
}
}
}
g_glRaytracingLighting.lightBuffer = glRaytracingCreateBuffer(
g_glRaytracingCmd.device.Get(),
sizeof(glRaytracingLight_t) * GL_RAYTRACING_MAX_LIGHTS,
D3D12_HEAP_TYPE_UPLOAD,
D3D12_RESOURCE_STATE_GENERIC_READ,
D3D12_RESOURCE_FLAG_NONE);
return g_glRaytracingLighting.constantBuffer.resource &&
g_glRaytracingLighting.lightBuffer.resource &&
g_glRaytracingLighting.denoiseConstantBuffer[0].resource &&
g_glRaytracingLighting.denoiseConstantBuffer[1].resource &&
g_glRaytracingLighting.denoiseConstantBuffer[2].resource;
glRaytracingLightingSelectFrameResources(0);
return 1;
}
static void glRaytracingLightingUploadConstantsTo(
@@ -4924,11 +5169,37 @@ static void glRaytracingLightingUploadConstantsTo(
if (!dst.resource)
return;
void* mapped = nullptr;
if (dst.resource.Get() == g_glRaytracingLighting.constantBuffer.resource.Get())
{
mapped = g_glRaytracingLighting.constantBufferMapped;
}
else
{
for (int i = 0; i < 3; ++i)
{
if (dst.resource.Get() == g_glRaytracingLighting.denoiseConstantBuffer[i].resource.Get())
{
mapped = g_glRaytracingLighting.denoiseConstantBufferMapped[i];
break;
}
}
}
if (mapped)
{
memcpy(mapped, &constants, sizeof(constants));
return;
}
glRaytracingMapCopy(dst.resource.Get(), &constants, sizeof(constants));
}
static void glRaytracingLightingUpdateConstants(void)
{
if (!g_glRaytracingLighting.uploadToCurrentFrameResource)
return;
glRaytracingLightingUploadConstantsTo(
g_glRaytracingLighting.constantBuffer,
g_glRaytracingLighting.constants);
@@ -4936,13 +5207,61 @@ static void glRaytracingLightingUpdateConstants(void)
static void glRaytracingLightingUpdateLights(void)
{
if (!g_glRaytracingLighting.uploadToCurrentFrameResource)
return;
if (!g_glRaytracingLighting.lightBuffer.resource)
return;
if (g_glRaytracingLighting.cpuLights.empty())
return;
const size_t bytes = g_glRaytracingLighting.cpuLights.size() * sizeof(glRaytracingLight_t);
if (g_glRaytracingLighting.lightBufferMapped)
{
memcpy(g_glRaytracingLighting.lightBufferMapped, g_glRaytracingLighting.cpuLights.data(), bytes);
return;
}
glRaytracingMapCopy(
g_glRaytracingLighting.lightBuffer.resource.Get(),
g_glRaytracingLighting.cpuLights.data(),
g_glRaytracingLighting.cpuLights.size() * sizeof(glRaytracingLight_t));
bytes);
}
static void glRaytracingLightingUnmapUploadBuffers(void)
{
for (UINT frame = 0; frame < GL_RAYTRACING_CMD_RING_SIZE; ++frame)
{
if (g_glRaytracingLighting.constantBufferRing[frame].resource &&
g_glRaytracingLighting.constantBufferMappedRing[frame])
{
g_glRaytracingLighting.constantBufferRing[frame].resource->Unmap(0, nullptr);
g_glRaytracingLighting.constantBufferMappedRing[frame] = nullptr;
}
if (g_glRaytracingLighting.lightBufferRing[frame].resource &&
g_glRaytracingLighting.lightBufferMappedRing[frame])
{
g_glRaytracingLighting.lightBufferRing[frame].resource->Unmap(0, nullptr);
g_glRaytracingLighting.lightBufferMappedRing[frame] = nullptr;
}
for (int i = 0; i < 3; ++i)
{
if (g_glRaytracingLighting.denoiseConstantBufferRing[frame][i].resource &&
g_glRaytracingLighting.denoiseConstantBufferMappedRing[frame][i])
{
g_glRaytracingLighting.denoiseConstantBufferRing[frame][i].resource->Unmap(0, nullptr);
g_glRaytracingLighting.denoiseConstantBufferMappedRing[frame][i] = nullptr;
}
}
}
g_glRaytracingLighting.constantBufferMapped = nullptr;
g_glRaytracingLighting.lightBufferMapped = nullptr;
for (int i = 0; i < 3; ++i)
g_glRaytracingLighting.denoiseConstantBufferMapped[i] = nullptr;
}
static void glRaytracingLightingCreatePersistentLightSRV(void)
@@ -4956,11 +5275,21 @@ static void glRaytracingLightingCreatePersistentLightSRV(void)
srv.Buffer.StructureByteStride = sizeof(glRaytracingLight_t);
srv.Buffer.Flags = D3D12_BUFFER_SRV_FLAG_NONE;
D3D12_CPU_DESCRIPTOR_HANDLE base = g_glRaytracingLighting.descriptorHeap->GetCPUDescriptorHandleForHeapStart();
g_glRaytracingCmd.device->CreateShaderResourceView(
g_glRaytracingLighting.lightBuffer.resource.Get(),
&srv,
glRaytracingOffsetCpu(base, g_glRaytracingLighting.descriptorStride, 0));
for (UINT frame = 0; frame < GL_RAYTRACING_CMD_RING_SIZE; ++frame)
{
if (!g_glRaytracingLighting.descriptorHeapRing[frame] ||
!g_glRaytracingLighting.lightBufferRing[frame].resource)
{
continue;
}
D3D12_CPU_DESCRIPTOR_HANDLE base =
g_glRaytracingLighting.descriptorHeapRing[frame]->GetCPUDescriptorHandleForHeapStart();
g_glRaytracingCmd.device->CreateShaderResourceView(
g_glRaytracingLighting.lightBufferRing[frame].resource.Get(),
&srv,
glRaytracingOffsetCpu(base, g_glRaytracingLighting.descriptorStride, GLR_DESC_LIGHTS_SRV));
}
}
static int glRaytracingLightingCreateStateObject(void)
@@ -5191,6 +5520,16 @@ static int glRaytracingLightingEnsureDenoiseResources(UINT width, UINT height)
return 1;
}
if (g_glRaytracingLighting.pathTraceTexture.resource ||
g_glRaytracingLighting.temporalTexture.resource ||
g_glRaytracingLighting.historyTexture[0].resource ||
g_glRaytracingLighting.historyTexture[1].resource ||
g_glRaytracingLighting.denoiseTemp[0].resource ||
g_glRaytracingLighting.denoiseTemp[1].resource)
{
glRaytracingWaitIdle();
}
g_glRaytracingLighting.pathTraceTexture = glRaytracingTexture_t();
g_glRaytracingLighting.temporalTexture = glRaytracingTexture_t();
g_glRaytracingLighting.historyTexture[0] = glRaytracingTexture_t();
@@ -5382,6 +5721,11 @@ static bool glRaytracingLightingExecuteInternal(
? g_glRaytracingLighting.pathTraceTexture.resource.Get()
: pass->outputTexture;
if (!glRaytracingBeginCmd())
return false;
glRaytracingLightingSelectFrameResources(g_glRaytracingCmd.cmdCurrentSlot);
g_glRaytracingLighting.constants.screenSize[0] = (float)pass->width;
g_glRaytracingLighting.constants.screenSize[1] = (float)pass->height;
g_glRaytracingLighting.constants.screenSize[2] = 1.0f / (float)pass->width;
@@ -5390,6 +5734,7 @@ static bool glRaytracingLightingExecuteInternal(
g_glRaytracingLighting.constants.lightCount =
(uint32_t)glRaytracingClamp<size_t>(g_glRaytracingLighting.cpuLights.size(), 0, GL_RAYTRACING_MAX_LIGHTS);
g_glRaytracingLighting.uploadToCurrentFrameResource = true;
glRaytracingLightingUpdateLights();
glRaytracingLightingUpdateConstants();
@@ -5400,6 +5745,7 @@ static bool glRaytracingLightingExecuteInternal(
denoiseConstants.denoiseStepWidth = (float)(1u << passIndex);
glRaytracingLightingUploadConstantsTo(g_glRaytracingLighting.denoiseConstantBuffer[passIndex], denoiseConstants);
}
g_glRaytracingLighting.uploadToCurrentFrameResource = false;
const uint32_t historyReadIndex = g_glRaytracingLighting.currentHistoryIndex & 1u;
const uint32_t historyWriteIndex = (g_glRaytracingLighting.currentHistoryIndex ^ 1u) & 1u;
@@ -5415,9 +5761,6 @@ static bool glRaytracingLightingExecuteInternal(
g_glRaytracingLighting.historyTexture[historyWriteIndex].resource.Get(),
g_glRaytracingLighting.temporalTexture.resource.Get());
if (!glRaytracingBeginCmd())
return false;
if (useInternalDenoiser)
{
glRaytracingTransition(g_glRaytracingCmd.cmdList.Get(),
@@ -5666,7 +6009,7 @@ bool glRaytracingLightingInit(void)
g_glRaytracingLighting.constants.normalReconstructZ = 1.0f;
g_glRaytracingLighting.constants.shadowBias = 1.5f;
g_glRaytracingLighting.constants.frameIndex = 0;
g_glRaytracingLighting.constants.samplesPerPixel = 2;
g_glRaytracingLighting.constants.samplesPerPixel = 1;
g_glRaytracingLighting.constants.maxBounces = 2;
g_glRaytracingLighting.constants.enableDenoiser = 1;
g_glRaytracingLighting.constants.denoisePassIndex = 0;
@@ -5692,6 +6035,8 @@ void glRaytracingLightingShutdown(void)
if (!g_glRaytracingLighting.initialized)
return;
glRaytracingWaitIdle();
glRaytracingLightingUnmapUploadBuffers();
g_glRaytracingLighting = glRaytracingLightingState_t();
}