diff --git a/neo/opengl/gl_d3d12raylight.cpp b/neo/opengl/gl_d3d12raylight.cpp index 300a93da..58d4dc48 100644 --- a/neo/opengl/gl_d3d12raylight.cpp +++ b/neo/opengl/gl_d3d12raylight.cpp @@ -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); } diff --git a/neo/opengl/gl_d3d12shim.cpp b/neo/opengl/gl_d3d12shim.cpp index 4a4ee2d1..fad902fc 100644 --- a/neo/opengl/gl_d3d12shim.cpp +++ b/neo/opengl/gl_d3d12shim.cpp @@ -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, 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 postAdditivePSO; ComPtr postDepthCopyPSO; ComPtr postDepthResolveMsaaPSO; + ComPtr postGBufferPointResolveMsaaPSO; ComPtr vsMainBlob; ComPtr psMainBlob; @@ -1060,6 +1077,7 @@ struct GLState ComPtr postPsAddBlob; ComPtr postPsDepthCopyBlob; ComPtr postPsDepthResolveMsaaBlob; + ComPtr 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 gDepthMS : register(t1); +Texture2DMS gDepthMS : register(t1); +Texture2DMS gNormalMS : register(t2); +Texture2DMS gPositionMS : register(t3); +Texture2DMS 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 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; diff --git a/neo/renderer/Image_program.cpp b/neo/renderer/Image_program.cpp index b284beba..5ab445d5 100644 --- a/neo/renderer/Image_program.cpp +++ b/neo/renderer/Image_program.cpp @@ -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 ); diff --git a/neo/renderer/Model.cpp b/neo/renderer/Model.cpp index a23ec64a..45f9a96b 100644 --- a/neo/renderer/Model.cpp +++ b/neo/renderer/Model.cpp @@ -577,6 +577,7 @@ void idRenderModelStatic::UpdateDXR(uint32_t& dxrBottomAcel, int onlySurface) std::vector 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) diff --git a/neo/renderer/Model.h b/neo/renderer/Model.h index 8404b198..77363972 100644 --- a/neo/renderer/Model.h +++ b/neo/renderer/Model.h @@ -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 idTriList; diff --git a/neo/renderer/Model_md5.cpp b/neo/renderer/Model_md5.cpp index f6b48b9c..a6a8b59a 100644 --- a/neo/renderer/Model_md5.cpp +++ b/neo/renderer/Model_md5.cpp @@ -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; diff --git a/neo/renderer/RenderWorld_load.cpp b/neo/renderer/RenderWorld_load.cpp index d46b8d75..5481860a 100644 --- a/neo/renderer/RenderWorld_load.cpp +++ b/neo/renderer/RenderWorld_load.cpp @@ -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; diff --git a/neo/renderer/draw_common.cpp b/neo/renderer/draw_common.cpp index a65c209a..a997a2e4 100644 --- a/neo/renderer/draw_common.cpp +++ b/neo/renderer/draw_common.cpp @@ -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); }