Implemented screenspace particles.

This commit is contained in:
Justin Marshall
2026-05-24 13:04:31 -07:00
parent ac9c10cc92
commit 56bb2f004f
10 changed files with 712 additions and 20 deletions
+542 -6
View File
@@ -467,11 +467,12 @@ static const UINT QD3D12_UploadBufferSize = 128 * 1024 * 1024;
// MSAA has been removed from the G-buffer path. Temporal AA now owns edge cleanup
// after lighting, so all scene/deferred render targets remain single-sample.
static const UINT QD3D12_RequestedGBufferSampleCount = 1;
static constexpr UINT QD3D12_PostSrvCount = 14;
static constexpr UINT QD3D12_PostSrvCount = 16;
static constexpr UINT QD3D12_PostRootConstants = QD3D12_PostSrvCount;
static constexpr UINT QD3D12_PostConstantDwords = 24;
static constexpr UINT QD3D12_ToneMapTileDim = 64;
static constexpr UINT QD3D12_MaxScreenSpaceDecals = 64;
static constexpr UINT QD3D12_MaxScreenSpaceParticleLights = GL_RAYTRACING_MAX_LIGHTS;
enum QD3D12RTVSlotGroup
{
@@ -544,6 +545,14 @@ struct QD3D12CameraState
bool valid = false;
};
struct QD3D12ScreenSpaceParticleBatch
{
std::vector<glScreenSpaceParticleVertex_t> vertices;
uint32_t textureId = 0;
uint32_t blendMode = 0;
float softDepth = 16.0f;
};
enum PipelineMode
{
PIPE_OPAQUE_TEX = 0,
@@ -1504,17 +1513,29 @@ struct GLState
bool enableRayAIDenoise = false;
bool enableDLSSRayReconstruction = false;
bool enableFSRRayRegeneration = false;
bool blockLightingUpscaleInputThisFrame = false;
uint32_t frameGenerationMultiplier = 0;
uint32_t pathTracingSamplesPerPixel = 1;
uint32_t pathTracingFallbackSamplesPerPixel = 2;
uint32_t pathTracingMaxBounces = 3;
glScreenSpaceDecal_t screenSpaceDecals[QD3D12_MaxScreenSpaceDecals] = {};
uint32_t screenSpaceDecalCount = 0;
std::vector<QD3D12ScreenSpaceParticleBatch> screenSpaceParticleBatches;
glRaytracingLight_t screenSpaceParticleLights[QD3D12_MaxScreenSpaceParticleLights] = {};
uint32_t screenSpaceParticleLightCount = 0;
ComPtr<ID3D12Resource> screenSpaceDecalBuffer;
void* screenSpaceDecalBufferMapped = nullptr;
UINT screenSpaceDecalSrvIndex = UINT_MAX;
D3D12_CPU_DESCRIPTOR_HANDLE screenSpaceDecalSrvCpu{};
D3D12_GPU_DESCRIPTOR_HANDLE screenSpaceDecalSrvGpu{};
ComPtr<ID3D12Resource> screenSpaceParticleLightBuffer;
void* screenSpaceParticleLightBufferMapped = nullptr;
UINT screenSpaceParticleLightSrvIndex = UINT_MAX;
D3D12_CPU_DESCRIPTOR_HANDLE screenSpaceParticleLightSrvCpu{};
D3D12_GPU_DESCRIPTOR_HANDLE screenSpaceParticleLightSrvGpu{};
UINT screenSpaceParticleTlasSrvIndex = UINT_MAX;
D3D12_CPU_DESCRIPTOR_HANDLE screenSpaceParticleTlasSrvCpu{};
D3D12_GPU_DESCRIPTOR_HANDLE screenSpaceParticleTlasSrvGpu{};
float jitterX = 0.0f;
float jitterY = 0.0f;
@@ -1696,6 +1717,8 @@ struct GLState
ComPtr<ID3D12PipelineState> postGBufferPointResolveMsaaPSO;
ComPtr<ID3D12PipelineState> postScreenDecalPSO;
ComPtr<ID3D12PipelineState> postScreenDecalStainPSO;
ComPtr<ID3D12PipelineState> postScreenParticlePSO;
ComPtr<ID3D12PipelineState> postScreenParticleAdditivePSO;
ComPtr<ID3DBlob> vsMainBlob;
ComPtr<ID3DBlob> hsMainBlob;
@@ -1718,6 +1741,8 @@ struct GLState
ComPtr<ID3DBlob> postPsDepthResolveMsaaBlob;
ComPtr<ID3DBlob> postPsGBufferPointResolveMsaaBlob;
ComPtr<ID3DBlob> postPsScreenDecalBlob;
ComPtr<ID3DBlob> postVsScreenParticleBlob;
ComPtr<ID3DBlob> postPsScreenParticleBlob;
TextureResource whiteTexture;
@@ -4448,12 +4473,40 @@ struct ScreenSpaceDecal
StructuredBuffer<ScreenSpaceDecal> gScreenDecals : register(t13);
struct ScreenParticleLight
{
float3 position;
float radius;
float3 color;
float intensity;
float3 normal;
uint type;
float3 axisU;
float halfWidth;
float3 axisV;
float halfHeight;
uint samples;
uint twoSided;
float persistant;
float volumetricScattering;
float3 pointRadius;
float pointRadiusPad;
uint iesTextureId;
float iesStrength;
float2 iesPad;
};
StructuredBuffer<ScreenParticleLight> gScreenParticleLights : register(t14);
#if defined(QD3D12_SCREEN_PARTICLE_RAYQUERY)
RaytracingAccelerationStructure gScreenParticleSceneBVH : register(t15);
#endif
cbuffer PostCB : register(b0)
{
// gPostParams0: x = sharpen strength, y = history weight, z = TAA reset flag.
// gPostParams1: x = tone-map exposure, y = white-point scale, z = display brightness.
// gPostParams2: x = fog enabled, y = fog mode, z = fog density, w = fog start.
// gPostParams3: x = fog end, y = fog camera valid.
// gPostParams3: x = fog end, y = fog camera valid, z = particle soft depth, w = particle light count.
float4 gPostParams0;
float4 gPostParams1;
float4 gPostParams2;
@@ -4475,15 +4528,37 @@ cbuffer PostCB : register(b0)
#define gPostFogStart gPostParams2.w
#define gPostFogEnd gPostParams3.x
#define gPostFogCameraValid gPostParams3.y
#define gScreenParticleSoftDepth gPostParams3.z
#define gScreenParticleLightCount gPostParams3.w
#define gScreenDecalCount gPostFogCameraWorldPos.w
#define QD3D12_TONEMAP_TILE_DIM 64u
static const uint GL_RAYTRACING_LIGHT_TYPE_POINT = 0u;
static const uint GL_RAYTRACING_LIGHT_TYPE_RECT = 1u;
static const uint GL_RAYTRACING_LIGHT_TYPE_SPOT = 2u;
struct VSOut
{
float4 pos : SV_Position;
float2 uv : TEXCOORD0;
};
struct ParticleVSIn
{
float4 clipPos : POSITION0;
float2 uv : TEXCOORD0;
float4 color : COLOR0;
float3 world : TEXCOORD1;
};
struct ParticleVSOut
{
float4 pos : SV_Position;
float2 uv : TEXCOORD0;
float4 color : COLOR0;
float3 world : TEXCOORD1;
};
VSOut VSMain(uint vid : SV_VertexID)
{
VSOut o;
@@ -4497,6 +4572,16 @@ VSOut VSMain(uint vid : SV_VertexID)
return o;
}
ParticleVSOut VSParticle(ParticleVSIn i)
{
ParticleVSOut o;
o.pos = i.clipPos;
o.uv = i.uv;
o.color = i.color;
o.world = i.world;
return o;
}
float QD3D12_PostFogFactor(float fogCoord)
{
if (gPostFogEnabled < 0.5)
@@ -4590,6 +4675,175 @@ float4 PSScreenDecals(VSOut i) : SV_Target0
return float4(decalColor, decalAlpha);
}
float3 ScreenParticleSafeNormalizeOr(float3 v, float3 fallbackDir)
{
float lenSq = dot(v, v);
return (lenSq > 1e-8) ? (v * rsqrt(lenSq)) : fallbackDir;
}
float3 ScreenParticlePointRadius(ScreenParticleLight Lgt)
{
float scalarRadius = max(abs(Lgt.radius), 1e-4);
float3 r = abs(Lgt.pointRadius);
if (max(max(r.x, r.y), r.z) <= 1e-4)
r = float3(scalarRadius, scalarRadius, scalarRadius);
return max(r, float3(1e-4, 1e-4, 1e-4));
}
float ScreenParticlePointAttenuation(float3 worldPos, ScreenParticleLight Lgt)
{
float3 radii = ScreenParticlePointRadius(Lgt);
float3 offset = worldPos - Lgt.position;
float3 axisU = ScreenParticleSafeNormalizeOr(Lgt.axisU, float3(1.0, 0.0, 0.0));
float3 axisV = ScreenParticleSafeNormalizeOr(Lgt.axisV, float3(0.0, 1.0, 0.0));
float3 axisW = ScreenParticleSafeNormalizeOr(Lgt.normal, float3(0.0, 0.0, 1.0));
float3 perAxis;
perAxis.x = 1.0 - saturate(abs(dot(offset, axisU)) / radii.x);
perAxis.y = 1.0 - saturate(abs(dot(offset, axisV)) / radii.y);
perAxis.z = 1.0 - saturate(abs(dot(offset, axisW)) / radii.z);
return perAxis.x * perAxis.y * perAxis.z;
}
float ScreenParticleBasicFalloff(float lightDist, float maxLightDist)
{
return saturate(1.0 - lightDist / max(maxLightDist, 1e-4));
}
float ScreenParticleSpotAttenuation(float3 worldPos, ScreenParticleLight Lgt)
{
float3 lightToSurface = worldPos - Lgt.position;
float nearClip = max(Lgt.pointRadius.x, 0.0);
float farClip = max(Lgt.radius, nearClip + 1e-4);
float3 spotDir = ScreenParticleSafeNormalizeOr(Lgt.normal, float3(0.0, 0.0, 1.0));
float depth = dot(lightToSurface, spotDir);
if (depth <= nearClip || depth >= farClip)
return 0.0;
float3 axisU = ScreenParticleSafeNormalizeOr(Lgt.axisU, float3(1.0, 0.0, 0.0));
float3 axisV = ScreenParticleSafeNormalizeOr(Lgt.axisV, float3(0.0, 1.0, 0.0));
float invDepth = 1.0 / max(depth, 1e-4);
float halfU = max(abs(Lgt.halfWidth), 1e-4);
float halfV = max(abs(Lgt.halfHeight), 1e-4);
float signedU = (dot(lightToSurface, axisU) * invDepth) / halfU;
float signedV = (dot(lightToSurface, axisV) * invDepth) / halfV;
if (abs(signedU) >= 1.0 || abs(signedV) >= 1.0)
return 0.0;
float3 lightVector = normalize(lightToSurface);
float maxSlope = max(halfU, halfV);
float outerAngle = rsqrt(1.0 + maxSlope * maxSlope);
float innerSlope = maxSlope * 0.65;
float innerAngle = rsqrt(1.0 + innerSlope * innerSlope);
float spotCookie = smoothstep(outerAngle, innerAngle, saturate(dot(lightVector, spotDir)));
return spotCookie * ScreenParticleBasicFalloff(depth - nearClip, farClip - nearClip);
}
float ScreenParticleTraceShadow(float3 worldPos, float3 viewDir, float3 L, float dist, ScreenParticleLight Lgt)
{
if (Lgt.samples == 0u)
return 1.0;
#if defined(QD3D12_SCREEN_PARTICLE_RAYQUERY)
RayDesc ray;
ray.Origin = worldPos + L * 5.0 + viewDir * 1.0;
ray.Direction = L;
ray.TMin = 0.001;
ray.TMax = max(dist - 8.0, 0.001);
RayQuery<RAY_FLAG_ACCEPT_FIRST_HIT_AND_END_SEARCH | RAY_FLAG_SKIP_PROCEDURAL_PRIMITIVES> q;
q.TraceRayInline(
gScreenParticleSceneBVH,
RAY_FLAG_ACCEPT_FIRST_HIT_AND_END_SEARCH | RAY_FLAG_FORCE_OPAQUE | RAY_FLAG_SKIP_PROCEDURAL_PRIMITIVES,
0xFF,
ray);
while (q.Proceed()) {}
return (q.CommittedStatus() == COMMITTED_TRIANGLE_HIT) ? 0.0 : 1.0;
#else
return 1.0;
#endif
}
float3 ScreenParticleDirectLighting(float3 worldPos)
{
uint lightCount = min((uint)gScreenParticleLightCount, 256u);
if (lightCount == 0u || gPostFogCameraValid <= 0.5)
return 1.0;
float3 viewDir = ScreenParticleSafeNormalizeOr(gPostFogCameraWorldPos.xyz - worldPos, float3(0.0, 0.0, 1.0));
float3 lighting = float3(0.16, 0.16, 0.16);
[loop]
for (uint lightIndex = 0u; lightIndex < lightCount; ++lightIndex)
{
ScreenParticleLight Lgt = gScreenParticleLights[lightIndex];
if (Lgt.type != GL_RAYTRACING_LIGHT_TYPE_POINT && Lgt.type != GL_RAYTRACING_LIGHT_TYPE_SPOT)
continue;
float3 toLight = Lgt.position - worldPos;
float dist = length(toLight);
if (dist <= 0.01)
continue;
float atten = (Lgt.type == GL_RAYTRACING_LIGHT_TYPE_POINT)
? ScreenParticlePointAttenuation(worldPos, Lgt)
: ScreenParticleSpotAttenuation(worldPos, Lgt);
if (atten <= 0.0)
continue;
float3 L = toLight / dist;
float shadow = ScreenParticleTraceShadow(worldPos, viewDir, L, dist, Lgt);
if (shadow <= 0.0)
continue;
float forwardScatter = saturate(dot(viewDir, -L) * 0.5 + 0.5);
float phase = lerp(0.65, 1.0, forwardScatter);
lighting += Lgt.color * (Lgt.intensity * atten * shadow * phase * 0.25);
}
return clamp(lighting, 0.08, 8.0);
}
float4 PSScreenParticles(ParticleVSOut i) : SV_Target0
{
float4 texel = gTex0.Sample(gSamp0, i.uv) * i.color;
float alpha = saturate(texel.a);
if (alpha <= 0.0001)
discard;
if (gPostFogCameraValid > 0.5)
{
uint2 pixel = uint2(i.pos.xy);
float sceneDepth = gFogDepth.Load(int3(pixel, 0));
float particleDepth = saturate(i.pos.z);
if (sceneDepth < 0.99999)
{
float4 positionSample = gScreenDecalPosition.Load(int3(pixel, 0));
uint geometryBits = (uint)round(max(positionSample.w, 0.0));
if ((geometryBits & 1u) == 0u)
{
float sceneDistance = length(positionSample.xyz - gPostFogCameraWorldPos.xyz);
float particleDistance = length(i.world - gPostFogCameraWorldPos.xyz);
float softDepth = max(gScreenParticleSoftDepth, 0.001);
alpha *= saturate((sceneDistance - particleDistance) / softDepth);
if (alpha <= 0.0001)
discard;
}
else if (particleDepth > sceneDepth + 0.00005)
{
discard;
}
}
}
texel.rgb *= ScreenParticleDirectLighting(i.world);
return float4(texel.rgb, alpha);
}
float4 PSCopy(VSOut i) : SV_Target0
{
return QD3D12_ApplyPostFog(gTex0.Sample(gSamp0, i.uv), i.uv);
@@ -4681,7 +4935,8 @@ float4 PSToneMapFinalMax(VSOut i) : SV_Target0
return float4(min(maxBrightness, 65504.0), 0.0, 0.0, 1.0);
}
)HLSL"
R"HLSL(
float3 QD3D12_PostSharpenHDR(float2 uv, float3 center)
{
float amount = saturate(gPostSharpness);
@@ -4825,7 +5080,8 @@ float3 QD3D12_EmissiveBloom(float2 uv)
return min(bloom * 0.62, 5.0);
}
)HLSL"
R"HLSL(
float4 PSAdd(VSOut i) : SV_Target0
{
float4 center = gTex0.Sample(gSamp0, i.uv);
@@ -7260,6 +7516,7 @@ static bool QD3D12_CanUseDLSSRayReconstructionForLighting(const QD3D12Window& w)
static bool QD3D12_UseLightingTextureAsUpscaleInput(const QD3D12Window& w)
{
return QD3D12_CanUseDLSSRayReconstructionForLighting(w) &&
!g_gl.blockLightingUpscaleInputThisFrame &&
g_gl.raytracedLightingReadyThisFrame &&
g_lightingTexture[g_lightingReadIndex] &&
g_lightingTexture[g_lightingReadIndex]->texture;
@@ -7795,7 +8052,10 @@ static void QD3D12_SetPostConstants(
float toneMapBrightness = 1.0f,
bool applySceneFog = false,
float screenDecalCount = 0.0f,
float screenDecalIndex = 0.0f)
float screenDecalIndex = 0.0f,
bool forceCameraPosition = false,
float screenParticleSoftDepth = 16.0f,
float screenParticleLightCount = 0.0f)
{
if (!cl)
return;
@@ -7824,6 +8084,15 @@ static void QD3D12_SetPostConstants(
constants[21] = g_gl.sceneFogCameraWorldPos[1];
constants[22] = g_gl.sceneFogCameraWorldPos[2];
}
else if ( forceCameraPosition && g_gl.cameraState.valid )
{
constants[13] = 1.0f;
constants[20] = g_gl.cameraState.cameraPos[0];
constants[21] = g_gl.cameraState.cameraPos[1];
constants[22] = g_gl.cameraState.cameraPos[2];
}
constants[14] = screenParticleSoftDepth;
constants[15] = screenParticleLightCount;
constants[23] = screenDecalCount;
cl->SetGraphicsRoot32BitConstants(QD3D12_PostRootConstants, QD3D12_PostConstantDwords, constants, 0);
}
@@ -7912,6 +8181,96 @@ static bool QD3D12_EnsureScreenSpaceDecalBuffer()
return true;
}
static bool QD3D12_EnsureScreenSpaceParticleLightBuffer()
{
if (!g_gl.device || !g_gl.srvHeap)
return false;
const UINT64 bufferSize = sizeof(glRaytracingLight_t) * QD3D12_MaxScreenSpaceParticleLights;
if (!g_gl.screenSpaceParticleLightBuffer)
{
D3D12_HEAP_PROPERTIES hp{};
hp.Type = D3D12_HEAP_TYPE_UPLOAD;
hp.CPUPageProperty = D3D12_CPU_PAGE_PROPERTY_UNKNOWN;
hp.MemoryPoolPreference = D3D12_MEMORY_POOL_UNKNOWN;
hp.CreationNodeMask = 1;
hp.VisibleNodeMask = 1;
D3D12_RESOURCE_DESC rd{};
rd.Dimension = D3D12_RESOURCE_DIMENSION_BUFFER;
rd.Width = bufferSize;
rd.Height = 1;
rd.DepthOrArraySize = 1;
rd.MipLevels = 1;
rd.Format = DXGI_FORMAT_UNKNOWN;
rd.SampleDesc.Count = 1;
rd.SampleDesc.Quality = 0;
rd.Layout = D3D12_TEXTURE_LAYOUT_ROW_MAJOR;
rd.Flags = D3D12_RESOURCE_FLAG_NONE;
QD3D12_CHECK(g_gl.device->CreateCommittedResource(
&hp,
D3D12_HEAP_FLAG_NONE,
&rd,
D3D12_RESOURCE_STATE_GENERIC_READ,
nullptr,
IID_PPV_ARGS(&g_gl.screenSpaceParticleLightBuffer)));
QD3D12_CHECK(g_gl.screenSpaceParticleLightBuffer->Map(0, nullptr, &g_gl.screenSpaceParticleLightBufferMapped));
}
if (g_gl.screenSpaceParticleLightSrvIndex == UINT_MAX)
{
g_gl.screenSpaceParticleLightSrvIndex = g_gl.nextSrvIndex++;
g_gl.screenSpaceParticleLightSrvCpu = QD3D12_SrvCpu(g_gl.screenSpaceParticleLightSrvIndex);
g_gl.screenSpaceParticleLightSrvGpu = QD3D12_SrvGpu(g_gl.screenSpaceParticleLightSrvIndex);
D3D12_SHADER_RESOURCE_VIEW_DESC sd{};
sd.ViewDimension = D3D12_SRV_DIMENSION_BUFFER;
sd.Format = DXGI_FORMAT_UNKNOWN;
sd.Shader4ComponentMapping = D3D12_DEFAULT_SHADER_4_COMPONENT_MAPPING;
sd.Buffer.FirstElement = 0;
sd.Buffer.NumElements = QD3D12_MaxScreenSpaceParticleLights;
sd.Buffer.StructureByteStride = sizeof(glRaytracingLight_t);
sd.Buffer.Flags = D3D12_BUFFER_SRV_FLAG_NONE;
g_gl.device->CreateShaderResourceView(g_gl.screenSpaceParticleLightBuffer.Get(), &sd, g_gl.screenSpaceParticleLightSrvCpu);
}
if (g_gl.screenSpaceParticleLightBufferMapped)
{
memset(g_gl.screenSpaceParticleLightBufferMapped, 0, bufferSize);
if (g_gl.screenSpaceParticleLightCount > 0)
{
memcpy(
g_gl.screenSpaceParticleLightBufferMapped,
g_gl.screenSpaceParticleLights,
sizeof(glRaytracingLight_t) * g_gl.screenSpaceParticleLightCount);
}
}
return true;
}
static bool QD3D12_UpdateScreenSpaceParticleTlasSrv(ID3D12Resource* tlas)
{
if (!g_gl.device || !g_gl.srvHeap || !tlas)
return false;
if (g_gl.screenSpaceParticleTlasSrvIndex == UINT_MAX)
{
g_gl.screenSpaceParticleTlasSrvIndex = g_gl.nextSrvIndex++;
g_gl.screenSpaceParticleTlasSrvCpu = QD3D12_SrvCpu(g_gl.screenSpaceParticleTlasSrvIndex);
g_gl.screenSpaceParticleTlasSrvGpu = QD3D12_SrvGpu(g_gl.screenSpaceParticleTlasSrvIndex);
}
D3D12_SHADER_RESOURCE_VIEW_DESC sd{};
sd.ViewDimension = D3D12_SRV_DIMENSION_RAYTRACING_ACCELERATION_STRUCTURE;
sd.Shader4ComponentMapping = D3D12_DEFAULT_SHADER_4_COMPONENT_MAPPING;
sd.RaytracingAccelerationStructure.Location = tlas->GetGPUVirtualAddress();
g_gl.device->CreateShaderResourceView(nullptr, &sd, g_gl.screenSpaceParticleTlasSrvCpu);
return true;
}
static void QD3D12_PostFullscreenPass(ID3D12GraphicsCommandList* cl,
ID3D12PipelineState* pso,
D3D12_GPU_DESCRIPTOR_HANDLE inputSrv,
@@ -8180,6 +8539,135 @@ void APIENTRY glDrawScreenSpaceDecalsQD3D12(void)
}
}
static void QD3D12_DrawScreenSpaceParticleBatch(const QD3D12ScreenSpaceParticleBatch& batch, ID3D12Resource* tlas)
{
const glScreenSpaceParticleVertex_t* verts = batch.vertices.empty() ? nullptr : batch.vertices.data();
const uint32_t vertexCount = (uint32_t)batch.vertices.size();
const uint32_t textureId = batch.textureId;
const uint32_t blendMode = batch.blendMode;
const float softDepth = batch.softDepth;
if (!verts || vertexCount == 0)
return;
QD3D12Window* window = g_currentWindow;
if (!window || !g_gl.device || !g_gl.cmdList || !g_gl.postScreenParticlePSO)
return;
if (!window->sceneColorBuffers[window->frameIndex] || !window->positionBuffers[window->frameIndex] || !window->depthBuffer)
return;
if (!QD3D12_EnsureScreenSpaceParticleLightBuffer() || !QD3D12_UpdateScreenSpaceParticleTlasSrv(tlas))
return;
QD3D12_EnsureFrameOpen();
QD3D12_FlushQueuedBatches();
QD3D12_ResolveGBufferForCurrentFrame(*window);
QD3D12_UpdateCameraInfoFromCurrentMatrices();
const UINT frame = window->frameIndex;
ID3D12GraphicsCommandList* cl = g_gl.cmdList.Get();
QD3D12_TransitionResource(cl, window->sceneColorBuffers[frame].Get(), window->sceneColorState[frame], D3D12_RESOURCE_STATE_RENDER_TARGET);
QD3D12_TransitionResource(cl, window->depthBuffer.Get(), window->depthState, D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE);
QD3D12_TransitionResource(cl, window->positionBuffers[frame].Get(), window->positionBufferState[frame], D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE);
const UINT vbBytes = vertexCount * sizeof(glScreenSpaceParticleVertex_t);
UploadAlloc upload = QD3D12_AllocUpload(vbBytes, 256);
memcpy(upload.cpu, verts, vbBytes);
D3D12_VERTEX_BUFFER_VIEW vbv{};
vbv.BufferLocation = upload.gpu;
vbv.SizeInBytes = vbBytes;
vbv.StrideInBytes = sizeof(glScreenSpaceParticleVertex_t);
D3D12_VIEWPORT viewport{};
viewport.TopLeftX = 0.0f;
viewport.TopLeftY = 0.0f;
viewport.Width = (float)window->renderWidth;
viewport.Height = (float)window->renderHeight;
viewport.MinDepth = 0.0f;
viewport.MaxDepth = 1.0f;
D3D12_RECT scissor{};
scissor.left = 0;
scissor.top = 0;
scissor.right = (LONG)window->renderWidth;
scissor.bottom = (LONG)window->renderHeight;
D3D12_GPU_DESCRIPTOR_HANDLE particleTextureSrv = g_gl.whiteTexture.srvGpu;
TextureResource* particleTexture = textureId ? QD3D12_FindTextureResource(textureId) : nullptr;
if (particleTexture)
{
EnsureTextureResource(*particleTexture);
if (particleTexture->texture && !particleTexture->gpuValid)
{
UploadTexture(*particleTexture);
}
if (particleTexture->texture && particleTexture->srvIndex != UINT_MAX)
{
particleTextureSrv = particleTexture->srvGpu;
}
}
ID3D12PipelineState* particlePSO = (blendMode == 1 && g_gl.postScreenParticleAdditivePSO.Get() != nullptr)
? g_gl.postScreenParticleAdditivePSO.Get()
: g_gl.postScreenParticlePSO.Get();
ID3D12DescriptorHeap* heaps[] = { g_gl.srvHeap.Get() };
cl->SetDescriptorHeaps(_countof(heaps), heaps);
cl->SetGraphicsRootSignature(g_gl.postRootSig.Get());
QD3D12_SetPostConstants(cl, 0.0f, 0.0f, 1.0f, 1.0f, 1.0f, 1.0f, false, 0.0f, 0.0f, true, softDepth, (float)g_gl.screenSpaceParticleLightCount);
cl->SetPipelineState(particlePSO);
D3D12_CPU_DESCRIPTOR_HANDLE sceneColorRtv = CurrentResolvedSceneColorRTV();
cl->OMSetRenderTargets(1, &sceneColorRtv, FALSE, nullptr);
cl->RSSetViewports(1, &viewport);
cl->RSSetScissorRects(1, &scissor);
cl->IASetPrimitiveTopology(D3D_PRIMITIVE_TOPOLOGY_TRIANGLELIST);
cl->IASetVertexBuffers(0, 1, &vbv);
cl->SetGraphicsRootDescriptorTable(0, particleTextureSrv);
cl->SetGraphicsRootDescriptorTable(10, window->depthSrvGpu);
cl->SetGraphicsRootDescriptorTable(12, window->positionSrvGpu[frame]);
cl->SetGraphicsRootDescriptorTable(14, g_gl.screenSpaceParticleLightSrvGpu);
cl->SetGraphicsRootDescriptorTable(15, g_gl.screenSpaceParticleTlasSrvGpu);
cl->DrawInstanced(vertexCount, 1, 0, 0);
}
void APIENTRY glDrawScreenSpaceParticlesQD3D12(const glScreenSpaceParticleVertex_t* verts, uint32_t vertexCount, uint32_t textureId, uint32_t blendMode, float softDepth)
{
if (!verts || vertexCount == 0)
return;
QD3D12ScreenSpaceParticleBatch batch;
batch.vertices.assign(verts, verts + vertexCount);
batch.textureId = textureId;
batch.blendMode = blendMode;
batch.softDepth = softDepth;
g_gl.screenSpaceParticleBatches.push_back(std::move(batch));
}
void APIENTRY glSetScreenSpaceParticleLightsQD3D12(const glRaytracingLight_t* lights, uint32_t lightCount)
{
g_gl.screenSpaceParticleLightCount = 0;
memset(g_gl.screenSpaceParticleLights, 0, sizeof(g_gl.screenSpaceParticleLights));
if (!lights || lightCount == 0)
return;
const uint32_t count = std::min<uint32_t>(lightCount, QD3D12_MaxScreenSpaceParticleLights);
memcpy(g_gl.screenSpaceParticleLights, lights, sizeof(glRaytracingLight_t) * count);
g_gl.screenSpaceParticleLightCount = count;
}
static void QD3D12_DrawQueuedScreenSpaceParticles(ID3D12Resource* tlas)
{
if (g_gl.screenSpaceParticleBatches.empty())
return;
for (const QD3D12ScreenSpaceParticleBatch& batch : g_gl.screenSpaceParticleBatches)
{
QD3D12_DrawScreenSpaceParticleBatch(batch, tlas);
}
g_gl.screenSpaceParticleBatches.clear();
}
static ID3D12Resource* QD3D12_PrepareStreamlineOutputForWrite(QD3D12Window& w)
{
ID3D12GraphicsCommandList* cl = g_gl.cmdList.Get();
@@ -9427,7 +9915,7 @@ static void QD3D12_DestroyUploadRingForWindow(QD3D12Window& w)
w.upload.offset = 0;
}
static ComPtr<ID3DBlob> CompileShaderSourceVariant(const char* source, const char* entry, const char* target)
static ComPtr<ID3DBlob> CompileShaderSourceVariant(const char* source, const char* entry, const char* target, LPCWSTR defineName = nullptr)
{
ComPtr<IDxcUtils> utils;
ComPtr<IDxcCompiler3> compiler;
@@ -9486,6 +9974,11 @@ static ComPtr<ID3DBlob> CompileShaderSourceVariant(const char* source, const cha
args.push_back(wTarget.c_str());
args.push_back(L"-HV");
args.push_back(L"2021");
if (defineName && defineName[0])
{
args.push_back(L"-D");
args.push_back(defineName);
}
#if defined(_DEBUG)
args.push_back(L"-Zi");
@@ -9677,6 +10170,8 @@ static void QD3D12_CompileShaders()
g_gl.postPsDepthResolveMsaaBlob = CompileShaderSourceVariant(kQD3D12PostHLSL, "PSDepthResolveMS", "ps_6_0");
g_gl.postPsGBufferPointResolveMsaaBlob = CompileShaderSourceVariant(kQD3D12PostHLSL, "PSGBufferPointResolveMS", "ps_6_0");
g_gl.postPsScreenDecalBlob = CompileShaderSourceVariant(kQD3D12PostHLSL, "PSScreenDecals", "ps_6_0");
g_gl.postVsScreenParticleBlob = CompileShaderSourceVariant(kQD3D12PostHLSL, "VSParticle", "vs_6_0");
g_gl.postPsScreenParticleBlob = CompileShaderSourceVariant(kQD3D12PostHLSL, "PSScreenParticles", "ps_6_5", L"QD3D12_SCREEN_PARTICLE_RAYQUERY=1");
}
static const D3D12_INPUT_ELEMENT_DESC kGLVertexInputLayout[] =
@@ -9701,6 +10196,14 @@ static const D3D12_INPUT_ELEMENT_DESC kGLVertexInputLayoutARB[] =
{ "BINORMAL", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, (UINT)offsetof(GLVertex, bx), D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA, 0 },
};
static const D3D12_INPUT_ELEMENT_DESC kScreenSpaceParticleInputLayout[] =
{
{ "POSITION", 0, DXGI_FORMAT_R32G32B32A32_FLOAT, 0, (UINT)offsetof(glScreenSpaceParticleVertex_t, clip), D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA, 0 },
{ "TEXCOORD", 0, DXGI_FORMAT_R32G32_FLOAT, 0, (UINT)offsetof(glScreenSpaceParticleVertex_t, st), D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA, 0 },
{ "COLOR", 0, DXGI_FORMAT_R32G32B32A32_FLOAT, 0, (UINT)offsetof(glScreenSpaceParticleVertex_t, color), D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA, 0 },
{ "TEXCOORD", 1, DXGI_FORMAT_R32G32B32_FLOAT, 0, (UINT)offsetof(glScreenSpaceParticleVertex_t, world), D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA, 0 },
};
static UINT8 BuildColorWriteMask()
{
UINT8 mask = 0;
@@ -9900,6 +10403,24 @@ static void QD3D12_CreatePSOs()
d.BlendState.RenderTarget[0].DestBlendAlpha = D3D12_BLEND_ONE;
QD3D12_CHECK(g_gl.device->CreateGraphicsPipelineState(&d, IID_PPV_ARGS(&g_gl.postScreenDecalStainPSO)));
D3D12_GRAPHICS_PIPELINE_STATE_DESC particleDesc = d;
particleDesc.VS = { g_gl.postVsScreenParticleBlob->GetBufferPointer(), g_gl.postVsScreenParticleBlob->GetBufferSize() };
particleDesc.PS = { g_gl.postPsScreenParticleBlob->GetBufferPointer(), g_gl.postPsScreenParticleBlob->GetBufferSize() };
particleDesc.InputLayout.pInputElementDescs = kScreenSpaceParticleInputLayout;
particleDesc.InputLayout.NumElements = _countof(kScreenSpaceParticleInputLayout);
particleDesc.BlendState.RenderTarget[0].BlendEnable = TRUE;
particleDesc.BlendState.RenderTarget[0].SrcBlend = D3D12_BLEND_SRC_ALPHA;
particleDesc.BlendState.RenderTarget[0].DestBlend = D3D12_BLEND_INV_SRC_ALPHA;
particleDesc.BlendState.RenderTarget[0].SrcBlendAlpha = D3D12_BLEND_ONE;
particleDesc.BlendState.RenderTarget[0].DestBlendAlpha = D3D12_BLEND_INV_SRC_ALPHA;
QD3D12_CHECK(g_gl.device->CreateGraphicsPipelineState(&particleDesc, IID_PPV_ARGS(&g_gl.postScreenParticlePSO)));
particleDesc.BlendState.RenderTarget[0].SrcBlend = D3D12_BLEND_SRC_ALPHA;
particleDesc.BlendState.RenderTarget[0].DestBlend = D3D12_BLEND_ONE;
particleDesc.BlendState.RenderTarget[0].SrcBlendAlpha = D3D12_BLEND_ONE;
particleDesc.BlendState.RenderTarget[0].DestBlendAlpha = D3D12_BLEND_ONE;
QD3D12_CHECK(g_gl.device->CreateGraphicsPipelineState(&particleDesc, IID_PPV_ARGS(&g_gl.postScreenParticleAdditivePSO)));
D3D12_GRAPHICS_PIPELINE_STATE_DESC depthDesc{};
depthDesc.pRootSignature = g_gl.postRootSig.Get();
depthDesc.VS = { g_gl.postVsBlob->GetBufferPointer(), g_gl.postVsBlob->GetBufferSize() };
@@ -10476,6 +10997,9 @@ void QD3D12_BeginFrame()
g_gl.gbufferResolvedThisFrame = false;
g_gl.raytracedLightingReadyThisFrame = false;
g_gl.sceneFogValidThisFrame = false;
g_gl.blockLightingUpscaleInputThisFrame = false;
g_gl.screenSpaceParticleBatches.clear();
g_gl.screenSpaceParticleLightCount = 0;
QD3D12_UpdateViewportState();
FrameResources& fr = w.frames[w.frameIndex];
@@ -16714,6 +17238,8 @@ PROC WINAPI qd3d12_wglGetProcAddress(LPCSTR name) {
{ "QD3D12_ResolveGBufferNow", (PROC)QD3D12_ResolveGBufferNow },
{ "glSetScreenSpaceDecalsQD3D12",(PROC)glSetScreenSpaceDecalsQD3D12 },
{ "glDrawScreenSpaceDecalsQD3D12",(PROC)glDrawScreenSpaceDecalsQD3D12 },
{ "glSetScreenSpaceParticleLightsQD3D12",(PROC)glSetScreenSpaceParticleLightsQD3D12 },
{ "glDrawScreenSpaceParticlesQD3D12",(PROC)glDrawScreenSpaceParticlesQD3D12 },
{ "QD3D12_SetUpscalerBackend", (PROC)QD3D12_SetUpscalerBackend },
{ "QD3D12_SetUpscalerQuality", (PROC)QD3D12_SetUpscalerQuality },
{ "QD3D12_SetUpscalerSharpness", (PROC)QD3D12_SetUpscalerSharpness },
@@ -17502,6 +18028,14 @@ void glLightScene(glRaytracingSceneHandle_t sceneHandle)
QD3D12_RunRayAIDenoiseIfEnabled(cl, *window, writeLightingTex->texture.Get());
if (!g_gl.screenSpaceParticleBatches.empty())
{
// The particle pass is a blended graphics pass, so it needs a render-target
// scene color surface. Keep particle frames on the composited scene-color
// path instead of feeding the raw lighting texture straight to RR/upscale.
g_gl.blockLightingUpscaleInputThisFrame = true;
}
// If the denoiser changes the written texture state, restore it to a
// shader-readable state before this slot becomes the next read target.
// restore the texture to a shader-readable state for the post/upscale pass.
@@ -17570,6 +18104,8 @@ void glLightScene(glRaytracingSceneHandle_t sceneHandle)
// guide instead of overwriting it here.
}
QD3D12_DrawQueuedScreenSpaceParticles(tlas);
QD3D12_ResolveSceneToOutputAndEnterNativePhase(*window);
}
+11
View File
@@ -1748,6 +1748,15 @@ typedef struct glScreenSpaceDecal_s
float pad1[2];
} glScreenSpaceDecal_t;
typedef struct glScreenSpaceParticleVertex_s
{
float clip[4];
float st[2];
float color[4];
float world[3];
float pad0;
} glScreenSpaceParticleVertex_t;
int glRaytracingInit(void);
void glRaytracingShutdown(void);
void glRaytracingClear(void);
@@ -1843,6 +1852,8 @@ uint32_t glRaytracingLightingGetLightCount(void);
void glLightScene(glRaytracingSceneHandle_t sceneHandle);
void glSetScreenSpaceDecalsQD3D12(const glScreenSpaceDecal_t* decals, uint32_t decalCount);
void glDrawScreenSpaceDecalsQD3D12(void);
void glSetScreenSpaceParticleLightsQD3D12(const glRaytracingLight_t* lights, uint32_t lightCount);
void glDrawScreenSpaceParticlesQD3D12(const glScreenSpaceParticleVertex_t* verts, uint32_t vertexCount, uint32_t textureId, uint32_t blendMode, float softDepth);
ID3D12Device* QD3D12_GetDevice(void);
ID3D12CommandQueue* QD3D12_GetQueue(void);