Lots of graphics fixes.

This commit is contained in:
Justin Marshall
2026-04-23 22:07:20 -07:00
parent 7877bcb26c
commit fba147e7f8
8 changed files with 396 additions and 82 deletions
+135 -32
View File
@@ -1962,9 +1962,32 @@ static const uint GL_RAYTRACING_LIGHT_TYPE_POINT = 0;
static const uint GL_RAYTRACING_LIGHT_TYPE_RECT = 1;
static const uint GL_RAYTRACING_LIGHT_TYPE_SPOT = 2;
static const uint GEOMETRY_FLAG_NONE = 0;
static const uint GEOMETRY_FLAG_SKELETAL = 1;
static const uint GEOMETRY_FLAG_UNLIT = 2;
uint DecodeGeometryFlag(float geoFlag)
{
// position.w comes from a render target / buffer path, so do not require exact
// float equality. Values like 0.999, 1.001, 1.99, 2.02 should decode correctly.
//
// Clamp negative garbage to 0, then round to nearest integer flag.
float f = max(geoFlag, 0.0);
return (uint)floor(f + 0.5);
}
bool GeometryFlagEquals(float geoFlag, uint expectedFlag)
{
return DecodeGeometryFlag(geoFlag) == expectedFlag;
}
bool GeometryFlagHas(float geoFlag, uint expectedFlag)
{
// Supports both current single-value usage and future bitmask usage.
uint decoded = DecodeGeometryFlag(geoFlag);
return (decoded & expectedFlag) != 0u;
}
float3 LoadScenePosition(uint2 pixel)
{
float4 p = gPositionTex.Load(int3(pixel, 0));
@@ -2005,7 +2028,7 @@ float TraceShadow(float3 origin, float3 dir, float maxT)
RAY_FLAG_ACCEPT_FIRST_HIT_AND_END_SEARCH | RAY_FLAG_CULL_FRONT_FACING_TRIANGLES,
0xFF,
0,
1,
0,
0,
ray,
payload);
@@ -2290,41 +2313,91 @@ float ComputeAmbientOcclusion(float3 worldPos, float3 N, uint2 pixel)
return visibility;
}
float3 GetSkyLightDirection10AM()
{
// Direction from the shaded point TO the sky/sun.
// 10:00 AM style: angled, not straight vertical.
//
// Flip X/Y signs if you want the shadows cast the opposite horizontal way.
return normalize(float3(-0.55, -0.25, 0.80));
}
)"
R"(
float ComputeSkyVisibility(float3 worldPos, float3 N, uint2 pixel)
{
const uint SKY_SAMPLES = 8;
const float SKY_TMAX = 1000000.0;
const uint SKY_SAMPLES = 12;
const float SKY_TMAX = 1000000.0;
// Soft angular size of the sky/sun shadow cone.
// Larger = softer shadows, but more chance of light leaking.
const float SKY_SOFTNESS = 0.085;
float3 skyCenterDir = GetSkyLightDirection10AM();
float NoSky = dot(N, skyCenterDir);
// Mostly back-facing relative to the sky direction.
// Return black visibility instead of casting unstable grazing rays.
if (NoSky <= -0.35)
{
return 0.0;
}
float3 tangent, bitangent;
BuildOrthonormalBasis(N, tangent, bitangent);
BuildOrthonormalBasis(skyCenterDir, tangent, bitangent);
float rand = Hash12((float2)pixel * 1.37 + worldPos.xy + float2(worldPos.z, dot(N.xy, N.xy)));
float normalBias = lerp(gShadowBias * 4.0, gShadowBias * 1.0, saturate(NoSky));
float vis = 0.0;
float3 baseOrigin =
worldPos +
N * normalBias +
skyCenterDir * (gShadowBias * 2.0);
float visibility = 0.0;
// IMPORTANT:
// No per-pixel random rotation here.
// The old noise came from random hemisphere sky sampling.
// This keeps the soft shadow sampling pattern stable per pixel/frame.
[unroll]
for (uint i = 0; i < SKY_SAMPLES; ++i)
{
float2 xi = Hammersley2D(i, SKY_SAMPLES, rand);
float3 h = CosineSampleHemisphere(xi);
float2 xi = Hammersley2D(i, SKY_SAMPLES, 0.0);
float2 d = ConcentricSampleDisk(xi) * SKY_SOFTNESS;
float3 skyDir =
tangent * h.x +
bitangent * h.y +
N * h.z;
float3 skyDir = normalize(
skyCenterDir +
tangent * d.x +
bitangent * d.y);
skyDir = normalize(skyDir);
if (skyDir.z <= 0.05)
// Do not shoot rays below the world horizon.
if (skyDir.z <= 0.02)
{
visibility += 0.0;
continue;
}
float3 skyOrigin = worldPos + N * (gShadowBias * 2.0) + skyDir * (gShadowBias * 2.0);
vis += TraceShadow(skyOrigin, skyDir, SKY_TMAX);
float sampleFacing = dot(N, skyDir);
// Avoid very noisy grazing rays on back-facing surfaces.
if (sampleFacing <= -0.35)
{
visibility += 0.0;
continue;
}
float3 skyOrigin =
worldPos +
N * normalBias +
skyDir * (gShadowBias * 2.0);
visibility += TraceShadow(skyOrigin, skyDir, SKY_TMAX);
}
vis /= (float)SKY_SAMPLES;
return saturate(vis);
visibility /= (float)SKY_SAMPLES;
// Slightly smooth the binary ray result so it does not look harsh.
return saturate(visibility);
}
float ComputeCavity(uint2 pixel, float3 worldPos, float3 N)
@@ -2419,6 +2492,29 @@ float3 ComputeSpecular(float3 N, float3 V, float3 L, float3 lightColor, float li
}
)"
R"(
float TraceStraightUpToSky(float3 worldPos, float3 N)
{
const float SKY_TMAX = 1000000.0;
// Straight world-up sky ray.
float3 skyDir = float3(0.0, 0.0, 1.0);
float NoSky = dot(N, skyDir);
// Same style of biasing as the sky visibility code.
float normalBias = lerp(gShadowBias * 4.0, gShadowBias * 1.0, saturate(NoSky));
float3 skyOrigin =
worldPos +
N * normalBias +
skyDir * (gShadowBias * 2.0);
// TraceShadow returns:
// 1.0 = missed scene geometry, so it reached sky
// 0.0 = hit scene geometry, so sky is blocked
return TraceShadow(skyOrigin, skyDir, SKY_TMAX);
}
[shader("raygeneration")]
void RayGen()
{
@@ -2436,37 +2532,44 @@ void RayGen()
return;
}
float3 baseAlbedo = albedoSample.rgb;
float3 baseAlbedo = albedoSample.rgb;
float4 positionSample = gPositionTex.Load(int3(pixel, 0));
float3 worldPos = positionSample.xyz;
float4 normalSample = LoadSceneNormal(pixel);
float3 N = normalize(normalSample.xyz);
float3 V = normalize(gCameraPos.xyz - worldPos);
float4 normalSample = LoadSceneNormal(pixel);
float3 N = normalize(normalSample.xyz);
float3 V = normalize(gCameraPos.xyz - worldPos);
float geoFlag = positionSample.w;
float geoFlagRaw = positionSample.w;
uint geoFlag = DecodeGeometryFlag(geoFlagRaw);
bool isSkeletal = (geoFlag & GEOMETRY_FLAG_SKELETAL) != 0u;
bool isUnlit = (geoFlag & GEOMETRY_FLAG_UNLIT) != 0u;
float cavity = ComputeCavity(pixel, worldPos, N);
float microShadow = lerp(0.75, 1.0, cavity);
float3 albedo = baseAlbedo * cavity;
albedo *= microShadow;
float aoRay = ComputeAmbientOcclusion(worldPos, N, pixel);
float ao = aoRay;
float aoRay = ComputeAmbientOcclusion(worldPos, N, pixel);
float ao = aoRay;
float skyVis = ComputeSkyVisibility(worldPos, N, pixel);
float ambientSkyVis = TraceStraightUpToSky(worldPos, N);
float upness = saturate(N.z * 0.5 + 0.5);
float3 skyColorRGB = float3(0.98, 0.55, 0.35);
float3 skyColor =
float3(0.5, 0.5, 0.5) * (0.35 + 0.65 * upness);
skyColorRGB * (0.35 + 0.65 * upness);
float skyStrength = 2.0;
float skyStrength = 0.7;
float3 lightingAccum = 0.0;
float3 specularAccum = 0.0;
lightingAccum += skyColor * (skyStrength * skyVis);
lightingAccum += (ambientSkyVis * (skyColorRGB * 0.15));
if (geoFlag == GEOMETRY_FLAG_SKELETAL)
if (isSkeletal)
{
lightingAccum += 0.1;
}
@@ -2611,13 +2714,13 @@ void RayGen()
lightingAccum *= ao;
specularAccum *= ao;
if (geoFlag == GEOMETRY_FLAG_SKELETAL)
if (isSkeletal)
{
lightingAccum *= 1.2;
specularAccum *= 1.15;
}
if (geoFlag == GEOMETRY_FLAG_UNLIT)
if (isUnlit)
{
gOutputTex[pixel] = float4(baseAlbedo, albedoSample.a);
}
+245 -36
View File
@@ -260,8 +260,11 @@ enum QD3D12RTVSlotGroup
QD3D12_RTV_POSITION_RENDER = 2,
QD3D12_RTV_VELOCITY_RENDER = 3,
QD3D12_RTV_SCENE_RESOLVED = 4,
QD3D12_RTV_BACKBUFFER = 5,
QD3D12_RTV_GROUP_COUNT = 6
QD3D12_RTV_NORMAL_RESOLVED = 5,
QD3D12_RTV_POSITION_RESOLVED = 6,
QD3D12_RTV_VELOCITY_RESOLVED = 7,
QD3D12_RTV_BACKBUFFER = 8,
QD3D12_RTV_GROUP_COUNT = 9
};
static const DXGI_FORMAT QD3D12_SceneColorFormat = DXGI_FORMAT_R8G8B8A8_UNORM;
@@ -690,8 +693,9 @@ struct QD3D12Window
// RTV descriptor groups:
// SCENE_RENDER/normal/position/velocity : active low-res render targets
// (MSAA textures when enabled, resolved single-sample textures otherwise)
// SCENE_RESOLVED : single-sample scene color for lighting resolve/copy
// BACKBUFFER : swap-chain/output render target
// *_RESOLVED : single-sample outputs used after MSAA conversion.
// Scene color uses hardware resolve; normals/position/velocity are point-resolved.
// BACKBUFFER : swap-chain/output render target
std::array<ComPtr<ID3D12Resource>, QD3D12_FrameCount> sceneColorBuffers;
D3D12_RESOURCE_STATES sceneColorState[QD3D12_FrameCount] = {};
@@ -751,6 +755,18 @@ struct QD3D12Window
D3D12_CPU_DESCRIPTOR_HANDLE depthMsaaSrvCpu{};
D3D12_GPU_DESCRIPTOR_HANDLE depthMsaaSrvGpu{};
UINT normalMsaaSrvIndex[QD3D12_FrameCount] = { UINT_MAX, UINT_MAX };
D3D12_CPU_DESCRIPTOR_HANDLE normalMsaaSrvCpu[QD3D12_FrameCount]{};
D3D12_GPU_DESCRIPTOR_HANDLE normalMsaaSrvGpu[QD3D12_FrameCount]{};
UINT positionMsaaSrvIndex[QD3D12_FrameCount] = { UINT_MAX, UINT_MAX };
D3D12_CPU_DESCRIPTOR_HANDLE positionMsaaSrvCpu[QD3D12_FrameCount]{};
D3D12_GPU_DESCRIPTOR_HANDLE positionMsaaSrvGpu[QD3D12_FrameCount]{};
UINT velocityMsaaSrvIndex[QD3D12_FrameCount] = { UINT_MAX, UINT_MAX };
D3D12_CPU_DESCRIPTOR_HANDLE velocityMsaaSrvCpu[QD3D12_FrameCount]{};
D3D12_GPU_DESCRIPTOR_HANDLE velocityMsaaSrvGpu[QD3D12_FrameCount]{};
D3D12_VIEWPORT viewport{};
D3D12_RECT scissor{};
@@ -1047,6 +1063,7 @@ struct GLState
ComPtr<ID3D12PipelineState> postAdditivePSO;
ComPtr<ID3D12PipelineState> postDepthCopyPSO;
ComPtr<ID3D12PipelineState> postDepthResolveMsaaPSO;
ComPtr<ID3D12PipelineState> postGBufferPointResolveMsaaPSO;
ComPtr<ID3DBlob> vsMainBlob;
ComPtr<ID3DBlob> psMainBlob;
@@ -1060,6 +1077,7 @@ struct GLState
ComPtr<ID3DBlob> postPsAddBlob;
ComPtr<ID3DBlob> postPsDepthCopyBlob;
ComPtr<ID3DBlob> postPsDepthResolveMsaaBlob;
ComPtr<ID3DBlob> postPsGBufferPointResolveMsaaBlob;
TextureResource whiteTexture;
@@ -1211,6 +1229,24 @@ static D3D12_CPU_DESCRIPTOR_HANDLE CurrentResolvedSceneColorRTV()
return QD3D12_RtvAt(w, QD3D12_RTV_SCENE_RESOLVED, w.frameIndex);
}
static D3D12_CPU_DESCRIPTOR_HANDLE CurrentResolvedNormalRTV()
{
QD3D12Window& w = *g_currentWindow;
return QD3D12_RtvAt(w, QD3D12_RTV_NORMAL_RESOLVED, w.frameIndex);
}
static D3D12_CPU_DESCRIPTOR_HANDLE CurrentResolvedPositionRTV()
{
QD3D12Window& w = *g_currentWindow;
return QD3D12_RtvAt(w, QD3D12_RTV_POSITION_RESOLVED, w.frameIndex);
}
static D3D12_CPU_DESCRIPTOR_HANDLE CurrentResolvedVelocityRTV()
{
QD3D12Window& w = *g_currentWindow;
return QD3D12_RtvAt(w, QD3D12_RTV_VELOCITY_RESOLVED, w.frameIndex);
}
static D3D12_CPU_DESCRIPTOR_HANDLE CurrentBackBufferRTV()
{
QD3D12Window& w = *g_currentWindow;
@@ -1925,7 +1961,10 @@ float4 PSMainUntexturedColorOnly(VSOut i) : SV_Target0
static const char* kQD3D12PostHLSL = R"HLSL(
Texture2D gTex0 : register(t0);
Texture2DMS<float> gDepthMS : register(t1);
Texture2DMS<float> gDepthMS : register(t1);
Texture2DMS<float4> gNormalMS : register(t2);
Texture2DMS<float4> gPositionMS : register(t3);
Texture2DMS<float4> gVelocityMS : register(t4);
SamplerState gSamp0 : register(s0);
struct VSOut
@@ -1970,6 +2009,25 @@ float PSDepthCopy(VSOut i) : SV_Depth
return gTex0.Load(int3(int2(srcPixel), 0)).r;
}
uint QD3D12_SelectNearestDepthSample(uint2 srcPixel, uint sampleCount)
{
uint nearestSample = 0u;
float nearestDepth = 1.0;
[loop]
for (uint sampleIndex = 0; sampleIndex < sampleCount; ++sampleIndex)
{
float sampleDepth = gDepthMS.Load(int2(srcPixel), sampleIndex);
if (sampleDepth < nearestDepth)
{
nearestDepth = sampleDepth;
nearestSample = sampleIndex;
}
}
return nearestSample;
}
float PSDepthResolveMS(VSOut i) : SV_Depth
{
uint srcW, srcH, sampleCount;
@@ -1983,10 +2041,41 @@ float PSDepthResolveMS(VSOut i) : SV_Depth
float depth = 1.0;
[loop]
for (uint sampleIndex = 0; sampleIndex < sampleCount; ++sampleIndex)
depth = min(depth, gDepthMS.Load(srcPixel, sampleIndex));
depth = min(depth, gDepthMS.Load(int2(srcPixel), sampleIndex));
return depth;
}
struct PSGBufferPointResolveOut
{
float4 normal : SV_Target0;
float4 position : SV_Target1;
float4 velocity : SV_Target2;
};
PSGBufferPointResolveOut PSGBufferPointResolveMS(VSOut i)
{
uint srcW, srcH, sampleCount;
gDepthMS.GetDimensions(srcW, srcH, sampleCount);
float2 uv = saturate(i.uv);
uint2 srcPixel = min(
(uint2)(uv * float2(srcW, srcH)),
uint2(max(srcW, 1u) - 1u, max(srcH, 1u) - 1u));
uint sampleIndex = QD3D12_SelectNearestDepthSample(srcPixel, sampleCount);
PSGBufferPointResolveOut o;
// Do not hardware-resolve these buffers. Normals, positions, material flags,
// and motion vectors are discontinuous at geometry edges, so averaging them
// corrupts the deferred/DXR inputs. Pick one MSAA sample by depth instead.
o.normal = gNormalMS.Load(int2(srcPixel), sampleIndex);
o.position = gPositionMS.Load(int2(srcPixel), sampleIndex);
o.velocity = gVelocityMS.Load(int2(srcPixel), sampleIndex);
return o;
}
)HLSL";
static size_t QD3D12_TypeSize(GLenum type) {
@@ -3747,12 +3836,15 @@ static bool QD3D12_CanUseGBufferSampleCount(UINT sampleCount)
return true;
return
// Scene color/albedo is color-like, so the hardware color resolve is fine.
QD3D12_FormatSupportsSampleCount(QD3D12_SceneColorFormat, sampleCount) &&
QD3D12_FormatSupportsMsaaResolve(QD3D12_SceneColorFormat) &&
// Normals, world positions, material flags, and motion vectors are not
// hardware-resolved. They only need to support MSAA render-target + SRV use;
// QD3D12_PointResolveMsaaGBufferToSingleSample() point-loads one sample.
QD3D12_FormatSupportsSampleCount(DXGI_FORMAT_R16G16B16A16_FLOAT, sampleCount) &&
QD3D12_FormatSupportsMsaaResolve(DXGI_FORMAT_R16G16B16A16_FLOAT) &&
QD3D12_FormatSupportsSampleCount(QD3D12_VelocityFormat, sampleCount) &&
QD3D12_FormatSupportsMsaaResolve(QD3D12_VelocityFormat) &&
QD3D12_FormatSupportsSampleCount(QD3D12_DepthDsvFormat, sampleCount);
}
@@ -3963,8 +4055,8 @@ static void QD3D12_CreateRTVsForWindow(QD3D12Window& w)
w.renderWidth, w.renderHeight, msaaSamples, D3D12_RESOURCE_FLAG_ALLOW_RENDER_TARGET, D3D12_RESOURCE_STATE_RENDER_TARGET,
QD3D12_RtvAt(w, QD3D12_RTV_NORMAL_RENDER, i), true);
CreateTexture2D(w.normalBuffers[i], w.normalBufferState[i], DXGI_FORMAT_R16G16B16A16_FLOAT, normalClear, false,
w.renderWidth, w.renderHeight, 1, D3D12_RESOURCE_FLAG_NONE, D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE,
D3D12_CPU_DESCRIPTOR_HANDLE{}, false);
w.renderWidth, w.renderHeight, 1, D3D12_RESOURCE_FLAG_ALLOW_RENDER_TARGET, D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE,
QD3D12_RtvAt(w, QD3D12_RTV_NORMAL_RESOLVED, i), true);
}
else
{
@@ -3982,8 +4074,8 @@ static void QD3D12_CreateRTVsForWindow(QD3D12Window& w)
w.renderWidth, w.renderHeight, msaaSamples, D3D12_RESOURCE_FLAG_ALLOW_RENDER_TARGET, D3D12_RESOURCE_STATE_RENDER_TARGET,
QD3D12_RtvAt(w, QD3D12_RTV_POSITION_RENDER, i), true);
CreateTexture2D(w.positionBuffers[i], w.positionBufferState[i], DXGI_FORMAT_R16G16B16A16_FLOAT, positionClear, false,
w.renderWidth, w.renderHeight, 1, D3D12_RESOURCE_FLAG_NONE, D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE,
D3D12_CPU_DESCRIPTOR_HANDLE{}, false);
w.renderWidth, w.renderHeight, 1, D3D12_RESOURCE_FLAG_ALLOW_RENDER_TARGET, D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE,
QD3D12_RtvAt(w, QD3D12_RTV_POSITION_RESOLVED, i), true);
}
else
{
@@ -4001,8 +4093,8 @@ static void QD3D12_CreateRTVsForWindow(QD3D12Window& w)
w.renderWidth, w.renderHeight, msaaSamples, D3D12_RESOURCE_FLAG_ALLOW_RENDER_TARGET, D3D12_RESOURCE_STATE_RENDER_TARGET,
QD3D12_RtvAt(w, QD3D12_RTV_VELOCITY_RENDER, i), true);
CreateTexture2D(w.velocityBuffers[i], w.velocityBufferState[i], QD3D12_VelocityFormat, velocityClear, false,
w.renderWidth, w.renderHeight, 1, D3D12_RESOURCE_FLAG_NONE, D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE,
D3D12_CPU_DESCRIPTOR_HANDLE{}, false);
w.renderWidth, w.renderHeight, 1, D3D12_RESOURCE_FLAG_ALLOW_RENDER_TARGET, D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE,
QD3D12_RtvAt(w, QD3D12_RTV_VELOCITY_RESOLVED, i), true);
}
else
{
@@ -4052,6 +4144,26 @@ static void QD3D12_CreateRTVsForWindow(QD3D12Window& w)
g_gl.device->CreateShaderResourceView(res, &sd, cpu);
};
auto CreateTextureMsaaSrv = [&](ID3D12Resource* res, DXGI_FORMAT format, UINT& srvIndex,
D3D12_CPU_DESCRIPTOR_HANDLE& cpu, D3D12_GPU_DESCRIPTOR_HANDLE& gpu)
{
if (!res)
return;
if (srvIndex == UINT_MAX)
{
srvIndex = g_gl.nextSrvIndex++;
cpu = QD3D12_SrvCpu(srvIndex);
gpu = QD3D12_SrvGpu(srvIndex);
}
D3D12_SHADER_RESOURCE_VIEW_DESC sd{};
sd.ViewDimension = D3D12_SRV_DIMENSION_TEXTURE2DMS;
sd.Format = format;
sd.Shader4ComponentMapping = D3D12_DEFAULT_SHADER_4_COMPONENT_MAPPING;
g_gl.device->CreateShaderResourceView(res, &sd, cpu);
};
for (UINT i = 0; i < QD3D12_FrameCount; ++i)
CreateTextureSrv(w.sceneColorBuffers[i].Get(), QD3D12_SceneColorFormat, w.sceneColorSrvIndex[i], w.sceneColorSrvCpu[i], w.sceneColorSrvGpu[i]);
@@ -4063,6 +4175,18 @@ static void QD3D12_CreateRTVsForWindow(QD3D12Window& w)
for (UINT i = 0; i < QD3D12_FrameCount; ++i)
CreateTextureSrv(w.velocityBuffers[i].Get(), QD3D12_VelocityFormat, w.velocitySrvIndex[i], w.velocitySrvCpu[i], w.velocitySrvGpu[i]);
if (useMsaa)
{
for (UINT i = 0; i < QD3D12_FrameCount; ++i)
CreateTextureMsaaSrv(w.normalMsaaBuffers[i].Get(), DXGI_FORMAT_R16G16B16A16_FLOAT, w.normalMsaaSrvIndex[i], w.normalMsaaSrvCpu[i], w.normalMsaaSrvGpu[i]);
for (UINT i = 0; i < QD3D12_FrameCount; ++i)
CreateTextureMsaaSrv(w.positionMsaaBuffers[i].Get(), DXGI_FORMAT_R16G16B16A16_FLOAT, w.positionMsaaSrvIndex[i], w.positionMsaaSrvCpu[i], w.positionMsaaSrvGpu[i]);
for (UINT i = 0; i < QD3D12_FrameCount; ++i)
CreateTextureMsaaSrv(w.velocityMsaaBuffers[i].Get(), QD3D12_VelocityFormat, w.velocityMsaaSrvIndex[i], w.velocityMsaaSrvCpu[i], w.velocityMsaaSrvGpu[i]);
}
}
void QD3D12_CreateDSVForWindow(QD3D12Window& w)
@@ -4383,7 +4507,7 @@ static ComPtr<ID3DBlob> CompileShaderVariant(const char* entry, const char* targ
static void QD3D12_CreatePostRootSignature()
{
D3D12_DESCRIPTOR_RANGE ranges[2]{};
D3D12_DESCRIPTOR_RANGE ranges[5]{};
for (UINT i = 0; i < _countof(ranges); ++i)
{
ranges[i].RangeType = D3D12_DESCRIPTOR_RANGE_TYPE_SRV;
@@ -4393,7 +4517,7 @@ static void QD3D12_CreatePostRootSignature()
ranges[i].OffsetInDescriptorsFromTableStart = 0;
}
D3D12_ROOT_PARAMETER params[2]{};
D3D12_ROOT_PARAMETER params[5]{};
for (UINT i = 0; i < _countof(params); ++i)
{
params[i].ParameterType = D3D12_ROOT_PARAMETER_TYPE_DESCRIPTOR_TABLE;
@@ -4491,6 +4615,7 @@ static void QD3D12_CompileShaders()
g_gl.postPsAddBlob = CompileShaderSourceVariant(kQD3D12PostHLSL, "PSAdd", "ps_6_0");
g_gl.postPsDepthCopyBlob = CompileShaderSourceVariant(kQD3D12PostHLSL, "PSDepthCopy", "ps_6_0");
g_gl.postPsDepthResolveMsaaBlob = CompileShaderSourceVariant(kQD3D12PostHLSL, "PSDepthResolveMS", "ps_6_0");
g_gl.postPsGBufferPointResolveMsaaBlob = CompileShaderSourceVariant(kQD3D12PostHLSL, "PSGBufferPointResolveMS", "ps_6_0");
}
static const D3D12_INPUT_ELEMENT_DESC kGLVertexInputLayout[] =
@@ -4678,6 +4803,39 @@ static void QD3D12_CreatePSOs()
depthDesc.PS = { g_gl.postPsDepthResolveMsaaBlob->GetBufferPointer(), g_gl.postPsDepthResolveMsaaBlob->GetBufferSize() };
QD3D12_CHECK(g_gl.device->CreateGraphicsPipelineState(&depthDesc, IID_PPV_ARGS(&g_gl.postDepthResolveMsaaPSO)));
D3D12_GRAPHICS_PIPELINE_STATE_DESC gbufferResolveDesc{};
gbufferResolveDesc.pRootSignature = g_gl.postRootSig.Get();
gbufferResolveDesc.VS = { g_gl.postVsBlob->GetBufferPointer(), g_gl.postVsBlob->GetBufferSize() };
gbufferResolveDesc.PS = { g_gl.postPsGBufferPointResolveMsaaBlob->GetBufferPointer(), g_gl.postPsGBufferPointResolveMsaaBlob->GetBufferSize() };
gbufferResolveDesc.PrimitiveTopologyType = D3D12_PRIMITIVE_TOPOLOGY_TYPE_TRIANGLE;
gbufferResolveDesc.SampleDesc.Count = 1;
gbufferResolveDesc.SampleMask = UINT_MAX;
gbufferResolveDesc.NumRenderTargets = 3;
gbufferResolveDesc.RTVFormats[0] = DXGI_FORMAT_R16G16B16A16_FLOAT;
gbufferResolveDesc.RTVFormats[1] = DXGI_FORMAT_R16G16B16A16_FLOAT;
gbufferResolveDesc.RTVFormats[2] = QD3D12_VelocityFormat;
gbufferResolveDesc.RasterizerState.FillMode = D3D12_FILL_MODE_SOLID;
gbufferResolveDesc.RasterizerState.CullMode = D3D12_CULL_MODE_NONE;
gbufferResolveDesc.RasterizerState.DepthClipEnable = TRUE;
gbufferResolveDesc.BlendState.AlphaToCoverageEnable = FALSE;
gbufferResolveDesc.BlendState.IndependentBlendEnable = FALSE;
for (UINT rt = 0; rt < 3; ++rt)
{
gbufferResolveDesc.BlendState.RenderTarget[rt].BlendEnable = FALSE;
gbufferResolveDesc.BlendState.RenderTarget[rt].LogicOpEnable = FALSE;
gbufferResolveDesc.BlendState.RenderTarget[rt].SrcBlend = D3D12_BLEND_ONE;
gbufferResolveDesc.BlendState.RenderTarget[rt].DestBlend = D3D12_BLEND_ZERO;
gbufferResolveDesc.BlendState.RenderTarget[rt].BlendOp = D3D12_BLEND_OP_ADD;
gbufferResolveDesc.BlendState.RenderTarget[rt].SrcBlendAlpha = D3D12_BLEND_ONE;
gbufferResolveDesc.BlendState.RenderTarget[rt].DestBlendAlpha = D3D12_BLEND_ZERO;
gbufferResolveDesc.BlendState.RenderTarget[rt].BlendOpAlpha = D3D12_BLEND_OP_ADD;
gbufferResolveDesc.BlendState.RenderTarget[rt].LogicOp = D3D12_LOGIC_OP_NOOP;
gbufferResolveDesc.BlendState.RenderTarget[rt].RenderTargetWriteMask = D3D12_COLOR_WRITE_ENABLE_ALL;
}
gbufferResolveDesc.DepthStencilState.DepthEnable = FALSE;
gbufferResolveDesc.DepthStencilState.StencilEnable = FALSE;
QD3D12_CHECK(g_gl.device->CreateGraphicsPipelineState(&gbufferResolveDesc, IID_PPV_ARGS(&g_gl.postGBufferPointResolveMsaaPSO)));
}
static uint64_t MakePSOKey(
@@ -10219,6 +10377,72 @@ static void QD3D12_ResolveMsaaDepthToSceneDepth(QD3D12Window& w)
cl->DrawInstanced(3, 1, 0, 0);
}
static void QD3D12_PointResolveMsaaGBufferToSingleSample(QD3D12Window& w)
{
if (!QD3D12_GBufferMsaaEnabled())
return;
ID3D12GraphicsCommandList* cl = g_gl.cmdList.Get();
if (!cl || !g_gl.postGBufferPointResolveMsaaPSO)
return;
const UINT frame = w.frameIndex;
if (!w.depthMsaaBuffer ||
!w.normalMsaaBuffers[frame] || !w.positionMsaaBuffers[frame] || !w.velocityMsaaBuffers[frame] ||
!w.normalBuffers[frame] || !w.positionBuffers[frame] || !w.velocityBuffers[frame])
{
return;
}
QD3D12_TransitionResource(cl, w.depthMsaaBuffer.Get(), w.depthMsaaState, D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE);
QD3D12_TransitionResource(cl, w.normalMsaaBuffers[frame].Get(), w.normalMsaaState[frame], D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE);
QD3D12_TransitionResource(cl, w.positionMsaaBuffers[frame].Get(), w.positionMsaaState[frame], D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE);
QD3D12_TransitionResource(cl, w.velocityMsaaBuffers[frame].Get(), w.velocityMsaaState[frame], D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE);
QD3D12_TransitionResource(cl, w.normalBuffers[frame].Get(), w.normalBufferState[frame], D3D12_RESOURCE_STATE_RENDER_TARGET);
QD3D12_TransitionResource(cl, w.positionBuffers[frame].Get(), w.positionBufferState[frame], D3D12_RESOURCE_STATE_RENDER_TARGET);
QD3D12_TransitionResource(cl, w.velocityBuffers[frame].Get(), w.velocityBufferState[frame], D3D12_RESOURCE_STATE_RENDER_TARGET);
D3D12_CPU_DESCRIPTOR_HANDLE rtvs[3] =
{
CurrentResolvedNormalRTV(),
CurrentResolvedPositionRTV(),
CurrentResolvedVelocityRTV()
};
D3D12_VIEWPORT viewport{};
viewport.TopLeftX = 0.0f;
viewport.TopLeftY = 0.0f;
viewport.Width = (float)w.renderWidth;
viewport.Height = (float)w.renderHeight;
viewport.MinDepth = 0.0f;
viewport.MaxDepth = 1.0f;
D3D12_RECT scissor{};
scissor.left = 0;
scissor.top = 0;
scissor.right = (LONG)w.renderWidth;
scissor.bottom = (LONG)w.renderHeight;
ID3D12DescriptorHeap* heaps[] = { g_gl.srvHeap.Get() };
cl->SetDescriptorHeaps(_countof(heaps), heaps);
cl->SetGraphicsRootSignature(g_gl.postRootSig.Get());
cl->SetPipelineState(g_gl.postGBufferPointResolveMsaaPSO.Get());
cl->OMSetRenderTargets(3, rtvs, FALSE, nullptr);
cl->RSSetViewports(1, &viewport);
cl->RSSetScissorRects(1, &scissor);
cl->IASetPrimitiveTopology(D3D_PRIMITIVE_TOPOLOGY_TRIANGLELIST);
// t1..t4: depth + discontinuous G-buffer attributes. The shader uses
// Texture2DMS.Load(), selects a single depth-nearest sample, and never averages.
cl->SetGraphicsRootDescriptorTable(1, w.depthMsaaSrvGpu);
cl->SetGraphicsRootDescriptorTable(2, w.normalMsaaSrvGpu[frame]);
cl->SetGraphicsRootDescriptorTable(3, w.positionMsaaSrvGpu[frame]);
cl->SetGraphicsRootDescriptorTable(4, w.velocityMsaaSrvGpu[frame]);
cl->DrawInstanced(3, 1, 0, 0);
}
static void QD3D12_ResolveGBufferForCurrentFrame(QD3D12Window& w)
{
if (g_gl.gbufferResolvedThisFrame)
@@ -10254,26 +10478,11 @@ static void QD3D12_ResolveGBufferForCurrentFrame(QD3D12Window& w)
w.sceneColorState[frame],
QD3D12_SceneColorFormat);
ResolveTarget(
w.normalMsaaBuffers[frame].Get(),
w.normalMsaaState[frame],
w.normalBuffers[frame].Get(),
w.normalBufferState[frame],
DXGI_FORMAT_R16G16B16A16_FLOAT);
ResolveTarget(
w.positionMsaaBuffers[frame].Get(),
w.positionMsaaState[frame],
w.positionBuffers[frame].Get(),
w.positionBufferState[frame],
DXGI_FORMAT_R16G16B16A16_FLOAT);
ResolveTarget(
w.velocityMsaaBuffers[frame].Get(),
w.velocityMsaaState[frame],
w.velocityBuffers[frame].Get(),
w.velocityBufferState[frame],
QD3D12_VelocityFormat);
// Do not hardware-resolve discontinuous deferred attributes. Hardware resolve
// averages samples, which corrupts normals, world positions/material flags,
// and motion vectors at geometry edges. Convert those buffers by point-loading
// one MSAA sample selected from the nearest depth sample instead.
QD3D12_PointResolveMsaaGBufferToSingleSample(w);
QD3D12_ResolveMsaaDepthToSceneDepth(w);
g_gl.gbufferResolvedThisFrame = true;
-8
View File
@@ -428,14 +428,6 @@ static bool R_ParseImageProgram_r( idLexer &src, byte **pic, int *width, int *he
return false;
}
if (width2 <= 0 || height2 <= 0) {
if (pic) {
R_StaticFree(*pic);
*pic = NULL;
}
return false;
}
// process it
if ( pic ) {
R_AddNormalMaps( *pic, *width, *height, pic2, width2, height2 );
+2 -1
View File
@@ -577,6 +577,7 @@ void idRenderModelStatic::UpdateDXR(uint32_t& dxrBottomAcel, int onlySurface)
std::vector<uint32_t> indices(numDXRIndexes);
int vertexId = 0;
int indexId = 0;
for (int i = 0; i < surfaces.Num(); i++)
{
if (onlySurface != -1 && onlySurface != i)
@@ -585,7 +586,7 @@ void idRenderModelStatic::UpdateDXR(uint32_t& dxrBottomAcel, int onlySurface)
modelSurface_t* surf = &surfaces[i];
for (int d = 0; d < surf->geometry->numIndexes; ++d)
{
indices[d] = vertexId + surf->geometry->indexes[d];
indices[indexId++] = vertexId + surf->geometry->indexes[d];
}
for (int d = 0; d < surf->geometry->numVerts; ++d)
+2
View File
@@ -137,6 +137,8 @@ typedef struct srfTriangles_s {
struct vertCache_s * ambientCache; // idDrawVert
struct vertCache_s * lightingCache; // lightingCache_t
struct vertCache_s * shadowCache; // shadowCache_t
bool isSkeletal;
} srfTriangles_t;
typedef idList<srfTriangles_t *> idTriList;
+1 -1
View File
@@ -301,7 +301,7 @@ void idMD5Mesh::UpdateSurface( const struct renderEntity_s *ent, const idJointMa
tri->deformedSurface = true;
tri->tangentsCalculated = false;
tri->facePlanesCalculated = false;
tri->isSkeletal = true;
tri->numIndexes = deformInfo->numIndexes;
tri->indexes = deformInfo->indexes;
tri->silIndexes = deformInfo->silIndexes;
+1 -1
View File
@@ -182,7 +182,7 @@ idRenderModel *idRenderWorldLocal::ParseModel( idLexer *src ) {
// add the completed surface to the model
model->AddSurface( surf );
if(!surf.shader->IsSky())
if (!surf.shader->IsSky())
{
idRenderModelStatic* modelStatic = (idRenderModelStatic*)model;
dxrWorldModel_t dxrModel;
+10 -3
View File
@@ -500,8 +500,7 @@ void RB_T_FillDepthBuffer( const drawSurf_t *surf ) {
// draw the entire surface solid
// draw the entire surface solid
if (drawSolid) {
glDisable(GL_BLEND);
if (drawSolid && !shader->IsSky()) {
glColor4f(1.0f, 1.0f, 1.0f, 1.0f);
// bind the texture
GL_SelectTexture(0);
@@ -513,6 +512,15 @@ void RB_T_FillDepthBuffer( const drawSurf_t *surf ) {
shader->GetBumpImage()->Bind();
glBindNormalMapTexture(shader->GetBumpImage()->texnum);
if (!surf->geo->isSkeletal)
{
glGeometryFlagf(GEOMETRY_FLAG_NONE);
}
else
{
glGeometryFlagf(GEOMETRY_FLAG_SKELETAL);
}
// set texture matrix and texGens. The Quake 4 path needs to know this is a depth fill.
RB_PrepareStageTexturing(pStage, surf, ac);
@@ -524,7 +532,6 @@ void RB_T_FillDepthBuffer( const drawSurf_t *surf ) {
GL_SelectTexture(1);
globalImages->BindNull();
GL_SelectTexture(0);
glEnable(GL_BLEND);
}