From 05cfd9e85c566dfe714738179248f0aec39b3625 Mon Sep 17 00:00:00 2001 From: Skyth <19259897+blueskythlikesclouds@users.noreply.github.com> Date: Tue, 14 Jan 2025 23:57:01 +0300 Subject: [PATCH] Implement dynamic depth bias. --- UnleashedRecomp/gpu/rhi/plume_d3d12.cpp | 18 +++++ UnleashedRecomp/gpu/rhi/plume_d3d12.h | 3 +- .../gpu/rhi/plume_render_interface.h | 1 + .../gpu/rhi/plume_render_interface_types.h | 2 + UnleashedRecomp/gpu/rhi/plume_vulkan.cpp | 14 +++- UnleashedRecomp/gpu/rhi/plume_vulkan.h | 1 + UnleashedRecomp/gpu/video.cpp | 67 +++++++++++++++---- 7 files changed, 90 insertions(+), 16 deletions(-) diff --git a/UnleashedRecomp/gpu/rhi/plume_d3d12.cpp b/UnleashedRecomp/gpu/rhi/plume_d3d12.cpp index 336ef6b2..515d4331 100644 --- a/UnleashedRecomp/gpu/rhi/plume_d3d12.cpp +++ b/UnleashedRecomp/gpu/rhi/plume_d3d12.cpp @@ -1815,6 +1815,11 @@ namespace plume { } } + void D3D12CommandList::setDepthBias(float depthBias, float depthBiasClamp, float slopeScaledDepthBias) { + assert(device->capabilities.dynamicDepthBias && "Dynamic depth bias is unsupported on this device."); + d3d->RSSetDepthBias(depthBias, depthBiasClamp, slopeScaledDepthBias); + } + void D3D12CommandList::clearColor(uint32_t attachmentIndex, RenderColor colorValue, const RenderRect *clearRects, uint32_t clearRectsCount) { assert(targetFramebuffer != nullptr); assert(attachmentIndex < targetFramebuffer->colorTargets.size()); @@ -2718,6 +2723,10 @@ namespace plume { psoDesc.RasterizerState.DepthBias = desc.depthBias; psoDesc.RasterizerState.SlopeScaledDepthBias = desc.slopeScaledDepthBias; + if (desc.dynamicDepthBiasEnabled) { + psoDesc.Flags |= D3D12_PIPELINE_STATE_FLAG_DYNAMIC_DEPTH_BIAS; + } + switch (desc.cullMode) { case RenderCullMode::NONE: psoDesc.RasterizerState.CullMode = D3D12_CULL_MODE_NONE; @@ -3307,6 +3316,14 @@ namespace plume { triangleFanSupportOption = d3d12Options15.TriangleFanSupported; } + // Check if dynamic depth bias is supported. + bool dynamicDepthBiasOption = false; + D3D12_FEATURE_DATA_D3D12_OPTIONS16 d3d12Options16 = {}; + res = deviceOption->CheckFeatureSupport(D3D12_FEATURE_D3D12_OPTIONS16, &d3d12Options16, sizeof(d3d12Options16)); + if (SUCCEEDED(res)) { + dynamicDepthBiasOption = d3d12Options16.DynamicDepthBiasSupported; + } + // Pick this adapter and device if it has better feature support than the current one. bool preferOverNothing = (adapter == nullptr) || (d3d == nullptr); bool preferVideoMemory = adapterDesc.DedicatedVideoMemory > description.dedicatedVideoMemory; @@ -3328,6 +3345,7 @@ namespace plume { capabilities.raytracingStateUpdate = rtStateUpdateSupportOption; capabilities.sampleLocations = samplePositionsOption; capabilities.triangleFan = triangleFanSupportOption; + capabilities.dynamicDepthBias = dynamicDepthBiasOption; description.name = Utf16ToUtf8(adapterDesc.Description); description.dedicatedVideoMemory = adapterDesc.DedicatedVideoMemory; diff --git a/UnleashedRecomp/gpu/rhi/plume_d3d12.h b/UnleashedRecomp/gpu/rhi/plume_d3d12.h index bc1b4ea9..b1a8645f 100644 --- a/UnleashedRecomp/gpu/rhi/plume_d3d12.h +++ b/UnleashedRecomp/gpu/rhi/plume_d3d12.h @@ -145,7 +145,7 @@ namespace plume { }; struct D3D12CommandList : RenderCommandList { - ID3D12GraphicsCommandList4 *d3d = nullptr; + ID3D12GraphicsCommandList9 *d3d = nullptr; ID3D12CommandAllocator *commandAllocator = nullptr; D3D12Device *device = nullptr; RenderCommandListType type = RenderCommandListType::UNKNOWN; @@ -184,6 +184,7 @@ namespace plume { void setViewports(const RenderViewport *viewports, uint32_t count) override; void setScissors(const RenderRect *scissorRects, uint32_t count) override; void setFramebuffer(const RenderFramebuffer *framebuffer) override; + void setDepthBias(float depthBias, float depthBiasClamp, float slopeScaledDepthBias) override; void clearColor(uint32_t attachmentIndex, RenderColor colorValue, const RenderRect *clearRects, uint32_t clearRectsCount) override; void clearDepth(bool clearDepth, float depthValue, const RenderRect *clearRects, uint32_t clearRectsCount) override; void copyBufferRegion(RenderBufferReference dstBuffer, RenderBufferReference srcBuffer, uint64_t size) override; diff --git a/UnleashedRecomp/gpu/rhi/plume_render_interface.h b/UnleashedRecomp/gpu/rhi/plume_render_interface.h index 45b382ae..ef2a5ed6 100644 --- a/UnleashedRecomp/gpu/rhi/plume_render_interface.h +++ b/UnleashedRecomp/gpu/rhi/plume_render_interface.h @@ -135,6 +135,7 @@ namespace plume { virtual void setViewports(const RenderViewport *viewports, uint32_t count) = 0; virtual void setScissors(const RenderRect *scissorRects, uint32_t count) = 0; virtual void setFramebuffer(const RenderFramebuffer *framebuffer) = 0; + virtual void setDepthBias(float depthBias, float depthBiasClamp, float slopeScaledDepthBias) = 0; virtual void clearColor(uint32_t attachmentIndex = 0, RenderColor colorValue = RenderColor(), const RenderRect *clearRects = nullptr, uint32_t clearRectsCount = 0) = 0; virtual void clearDepth(bool clearDepth = true, float depthValue = 1.0f, const RenderRect *clearRects = nullptr, uint32_t clearRectsCount = 0) = 0; virtual void copyBufferRegion(RenderBufferReference dstBuffer, RenderBufferReference srcBuffer, uint64_t size) = 0; diff --git a/UnleashedRecomp/gpu/rhi/plume_render_interface_types.h b/UnleashedRecomp/gpu/rhi/plume_render_interface_types.h index c4e87727..ad22251b 100644 --- a/UnleashedRecomp/gpu/rhi/plume_render_interface_types.h +++ b/UnleashedRecomp/gpu/rhi/plume_render_interface_types.h @@ -1171,6 +1171,7 @@ namespace plume { bool depthClipEnabled = false; int32_t depthBias = 0; float slopeScaledDepthBias = 0.0f; + bool dynamicDepthBiasEnabled = false; bool depthEnabled = false; bool depthWriteEnabled = false; RenderMultisampling multisampling; @@ -1778,6 +1779,7 @@ namespace plume { // Draw. bool triangleFan = false; + bool dynamicDepthBias = false; }; struct RenderInterfaceCapabilities { diff --git a/UnleashedRecomp/gpu/rhi/plume_vulkan.cpp b/UnleashedRecomp/gpu/rhi/plume_vulkan.cpp index b6b18f2b..8d7c2ca7 100644 --- a/UnleashedRecomp/gpu/rhi/plume_vulkan.cpp +++ b/UnleashedRecomp/gpu/rhi/plume_vulkan.cpp @@ -1432,7 +1432,10 @@ namespace plume { rasterization.cullMode = toVk(desc.cullMode); rasterization.frontFace = VK_FRONT_FACE_CLOCKWISE; - if (desc.depthBias != 0 || desc.slopeScaledDepthBias != 0.0f) { + if (desc.dynamicDepthBiasEnabled) { + rasterization.depthBiasEnable = true; + } + else if (desc.depthBias != 0 || desc.slopeScaledDepthBias != 0.0f) { rasterization.depthBiasEnable = true; rasterization.depthBiasConstantFactor = float(desc.depthBias); rasterization.depthBiasSlopeFactor = desc.slopeScaledDepthBias; @@ -1510,6 +1513,10 @@ namespace plume { dynamicStates.emplace_back(VK_DYNAMIC_STATE_VIEWPORT); dynamicStates.emplace_back(VK_DYNAMIC_STATE_SCISSOR); + if (desc.dynamicDepthBiasEnabled) { + dynamicStates.emplace_back(VK_DYNAMIC_STATE_DEPTH_BIAS); + } + VkPipelineDynamicStateCreateInfo dynamicState = {}; dynamicState.sType = VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO; dynamicState.pDynamicStates = dynamicStates.data(); @@ -2848,6 +2855,10 @@ namespace plume { } } + void VulkanCommandList::setDepthBias(float depthBias, float depthBiasClamp, float slopeScaledDepthBias) { + vkCmdSetDepthBias(vk, depthBias, depthBiasClamp, slopeScaledDepthBias); + } + static void clearCommonRectVector(uint32_t width, uint32_t height, const RenderRect *clearRects, uint32_t clearRectsCount, std::vector &rectVector) { rectVector.clear(); @@ -3783,6 +3794,7 @@ namespace plume { capabilities.displayTiming = supportedOptionalExtensions.find(VK_GOOGLE_DISPLAY_TIMING_EXTENSION_NAME) != supportedOptionalExtensions.end(); capabilities.preferHDR = memoryHeapSize > (512 * 1024 * 1024); capabilities.triangleFan = true; + capabilities.dynamicDepthBias = true; // Fill Vulkan-only capabilities. loadStoreOpNoneSupported = supportedOptionalExtensions.find(VK_EXT_LOAD_STORE_OP_NONE_EXTENSION_NAME) != supportedOptionalExtensions.end(); diff --git a/UnleashedRecomp/gpu/rhi/plume_vulkan.h b/UnleashedRecomp/gpu/rhi/plume_vulkan.h index c4184a4d..122ffcf3 100644 --- a/UnleashedRecomp/gpu/rhi/plume_vulkan.h +++ b/UnleashedRecomp/gpu/rhi/plume_vulkan.h @@ -307,6 +307,7 @@ namespace plume { void setViewports(const RenderViewport *viewports, uint32_t count) override; void setScissors(const RenderRect *scissorRects, uint32_t count) override; void setFramebuffer(const RenderFramebuffer *framebuffer) override; + void setDepthBias(float depthBias, float depthBiasClamp, float slopeScaledDepthBias) override; void clearColor(uint32_t attachmentIndex, RenderColor colorValue, const RenderRect *clearRects, uint32_t clearRectsCount) override; void clearDepth(bool clearDepth, float depthValue, const RenderRect *clearRects, uint32_t clearRectsCount) override; void copyBufferRegion(RenderBufferReference dstBuffer, RenderBufferReference srcBuffer, uint64_t size) override; diff --git a/UnleashedRecomp/gpu/video.cpp b/UnleashedRecomp/gpu/video.cpp index cf1888f8..225d014e 100644 --- a/UnleashedRecomp/gpu/video.cpp +++ b/UnleashedRecomp/gpu/video.cpp @@ -131,12 +131,18 @@ struct SharedConstants float alphaThreshold{}; }; +// Depth bias values here are only used when the render device has +// dynamic depth bias capability enabled. Otherwise, they get unused +// and the values get assigned in the pipeline state instead. + static GuestSurface* g_renderTarget; static GuestSurface* g_depthStencil; static RenderFramebuffer* g_framebuffer; static RenderViewport g_viewport(0.0f, 0.0f, 1280.0f, 720.0f); static bool g_halfPixel = true; static PipelineState g_pipelineState; +static int32_t g_depthBias; +static float g_slopeScaledDepthBias; static SharedConstants g_sharedConstants; static RenderSamplerDesc g_samplerDescs[16]; static bool g_scissorTestEnable = false; @@ -150,6 +156,7 @@ struct DirtyStates bool renderTargetAndDepthStencil; bool viewport; bool pipelineState; + bool depthBias; bool sharedConstants; bool scissorRect; bool vertexShaderConstants; @@ -162,6 +169,7 @@ struct DirtyStates : renderTargetAndDepthStencil(value) , viewport(value) , pipelineState(value) + , depthBias(value) , sharedConstants(value) , scissorRect(value) , vertexShaderConstants(value) @@ -194,7 +202,7 @@ static constexpr bool g_vulkan = true; static std::unique_ptr g_interface; static std::unique_ptr g_device; -static bool g_triangleFanSupported; +static RenderDeviceCapabilities g_capabilities; static constexpr size_t NUM_FRAMES = 2; @@ -1019,12 +1027,20 @@ static void ProcSetRenderState(const RenderCommand& cmd) } case D3DRS_SLOPESCALEDEPTHBIAS: { - SetDirtyValue(g_dirtyStates.pipelineState, g_pipelineState.slopeScaledDepthBias, *reinterpret_cast(&value)); + if (g_capabilities.dynamicDepthBias) + SetDirtyValue(g_dirtyStates.depthBias, g_slopeScaledDepthBias, *reinterpret_cast(&value)); + else + SetDirtyValue(g_dirtyStates.pipelineState, g_pipelineState.slopeScaledDepthBias, *reinterpret_cast(&value)); + break; } case D3DRS_DEPTHBIAS: { - SetDirtyValue(g_dirtyStates.pipelineState, g_pipelineState.depthBias, int32_t(*reinterpret_cast(&value) * (1 << 24))); + if (g_capabilities.dynamicDepthBias) + SetDirtyValue(g_dirtyStates.depthBias, g_depthBias, int32_t(*reinterpret_cast(&value) * (1 << 24))); + else + SetDirtyValue(g_dirtyStates.pipelineState, g_pipelineState.depthBias, int32_t(*reinterpret_cast(&value)* (1 << 24))); + break; } case D3DRS_SRCBLENDALPHA: @@ -1430,7 +1446,7 @@ void Video::CreateHostDevice(const char *sdlVideoDriver) g_device = g_interface->createDevice(); - g_triangleFanSupported = g_device->getCapabilities().triangleFan; + g_capabilities = g_device->getCapabilities(); g_queue = g_device->createCommandQueue(RenderCommandListType::DIRECT); @@ -1991,9 +2007,8 @@ static void DrawProfiler() ImGui::Text("Physical Heap Allocated: %d MB", int32_t(physicalDiagnostics.allocated / (1024 * 1024))); ImGui::NewLine(); - auto capabilities = g_device->getCapabilities(); - ImGui::Text("Present Wait: %s", capabilities.presentWait ? "Supported" : "Unsupported"); - ImGui::Text("Triangle Fan: %s", capabilities.triangleFan ? "Supported" : "Unsupported"); + ImGui::Text("Present Wait: %s", g_capabilities.presentWait ? "Supported" : "Unsupported"); + ImGui::Text("Triangle Fan: %s", g_capabilities.triangleFan ? "Supported" : "Unsupported"); ImGui::NewLine(); const char* sdlVideoDriver = SDL_GetCurrentVideoDriver(); @@ -3164,6 +3179,8 @@ static void SanitizePipelineState(PipelineState& pipelineState) pipelineState.depthStencilFormat = RenderFormat::UNKNOWN; } + assert(!g_capabilities.dynamicDepthBias || (pipelineState.depthBias == 0 && pipelineState.slopeScaledDepthBias == 0.0f)); + if (pipelineState.slopeScaledDepthBias == 0.0f) pipelineState.slopeScaledDepthBias = 0.0f; // Remove sign. @@ -3214,6 +3231,7 @@ static std::unique_ptr CreateGraphicsPipeline(const PipelineStat desc.depthWriteEnabled = pipelineState.zWriteEnable; desc.depthBias = pipelineState.depthBias; desc.slopeScaledDepthBias = pipelineState.slopeScaledDepthBias; + desc.dynamicDepthBiasEnabled = g_capabilities.dynamicDepthBias; desc.depthClipEnabled = true; desc.primitiveTopology = pipelineState.primitiveTopology; desc.cullMode = pipelineState.cullMode; @@ -3583,8 +3601,18 @@ static void FlushRenderStateForRenderThread() auto& commandList = g_commandLists[g_frame]; if (g_dirtyStates.pipelineState) + { commandList->setPipeline(CreateGraphicsPipelineInRenderThread(g_pipelineState)); + // D3D12 sets the depth bias values to the values in the pipeline. + // TODO: Put the common depth bias values to shadow pipelines to reduce redundant calls. + if (!g_vulkan && g_capabilities.dynamicDepthBias) + g_dirtyStates.depthBias |= (g_depthBias != 0) || (g_slopeScaledDepthBias != 0.0f); + } + + if (g_dirtyStates.depthBias && g_capabilities.dynamicDepthBias) + commandList->setDepthBias(g_depthBias, 0.0f, g_slopeScaledDepthBias); + if (g_dirtyStates.sharedConstants) { auto sharedConstants = g_uploadAllocators[g_frame].allocate(&g_sharedConstants, sizeof(g_sharedConstants), 0x100); @@ -3622,7 +3650,7 @@ static RenderPrimitiveTopology ConvertPrimitiveType(uint32_t primitiveType) case D3DPT_TRIANGLESTRIP: return RenderPrimitiveTopology::TRIANGLE_STRIP; case D3DPT_TRIANGLEFAN: - return g_triangleFanSupported ? RenderPrimitiveTopology::TRIANGLE_FAN : RenderPrimitiveTopology::TRIANGLE_LIST; + return g_capabilities.triangleFan ? RenderPrimitiveTopology::TRIANGLE_FAN : RenderPrimitiveTopology::TRIANGLE_LIST; default: assert(false && "Unknown primitive type"); return RenderPrimitiveTopology::UNKNOWN; @@ -3748,7 +3776,7 @@ static void ProcDrawPrimitiveUP(const RenderCommand& cmd) if (args.primitiveType == D3DPT_QUADLIST) indexCount = g_quadIndexData.prepare(args.primitiveCount); - else if (!g_triangleFanSupported && args.primitiveType == D3DPT_TRIANGLEFAN) + else if (!g_capabilities.triangleFan && args.primitiveType == D3DPT_TRIANGLEFAN) indexCount = g_triangleFanIndexData.prepare(args.primitiveCount); if (args.csdFilterState != CsdFilterState::Unknown && @@ -4994,7 +5022,7 @@ static const be g_particleTestIndexBuffer[] = bool ParticleTestIndexBufferMidAsmHook(PPCRegister& r30) { - if (!g_triangleFanSupported) + if (!g_capabilities.triangleFan) { auto buffer = CreateIndexBuffer(sizeof(g_particleTestIndexBuffer), 0, D3DFMT_INDEX16); void* memory = LockIndexBuffer(buffer, 0, 0, 0); @@ -5009,7 +5037,7 @@ bool ParticleTestIndexBufferMidAsmHook(PPCRegister& r30) void ParticleTestDrawIndexedPrimitiveMidAsmHook(PPCRegister& r7) { - if (!g_triangleFanSupported) + if (!g_capabilities.triangleFan) r7.u64 = std::size(g_particleTestIndexBuffer); } @@ -5289,8 +5317,13 @@ static void CompileMeshPipeline(const Mesh& mesh, CompilationArgs& args) pipelineState.vertexDeclaration = mesh.vertexDeclaration; pipelineState.cullMode = mesh.material->m_DoubleSided ? RenderCullMode::NONE : RenderCullMode::BACK; pipelineState.zFunc = RenderComparisonFunction::LESS_EQUAL; - pipelineState.depthBias = (1 << 24) * (*reinterpret_cast*>(g_memory.Translate(0x83302760))); - pipelineState.slopeScaledDepthBias = *reinterpret_cast*>(g_memory.Translate(0x83302764)); + + if (!g_capabilities.dynamicDepthBias) + { + pipelineState.depthBias = (1 << 24) * (*reinterpret_cast*>(g_memory.Translate(0x83302760))); + pipelineState.slopeScaledDepthBias = *reinterpret_cast*>(g_memory.Translate(0x83302764)); + } + pipelineState.colorWriteEnable = 0; pipelineState.primitiveTopology = RenderPrimitiveTopology::TRIANGLE_STRIP; pipelineState.vertexStrides[0] = mesh.vertexSize; @@ -5941,9 +5974,15 @@ static void ModelConsumerThread() pipelineState.vertexDeclaration = g_vertexDeclarations[reinterpret_cast(pipelineState.vertexDeclaration)]; } - if (!g_triangleFanSupported && pipelineState.primitiveTopology == RenderPrimitiveTopology::TRIANGLE_FAN) + if (!g_capabilities.triangleFan && pipelineState.primitiveTopology == RenderPrimitiveTopology::TRIANGLE_FAN) pipelineState.primitiveTopology = RenderPrimitiveTopology::TRIANGLE_LIST; + if (g_capabilities.dynamicDepthBias) + { + pipelineState.depthBias = 0; + pipelineState.slopeScaledDepthBias = 0.0f; + } + if (Config::GITextureFiltering == EGITextureFiltering::Bicubic) pipelineState.specConstants |= SPEC_CONSTANT_BICUBIC_GI_FILTER;