mirror of
https://github.com/jmarshall23/DoomRTX.git
synced 2026-08-19 12:14:35 +02:00
Implemented screenspace particles.
This commit is contained in:
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user