From f37df19da2bd9df443f08714ad96c35482065628 Mon Sep 17 00:00:00 2001 From: Philip Rebohle Date: Tue, 11 Mar 2025 00:42:28 +0100 Subject: [PATCH] [dxvk,d3d11,d3d9] Refactor viewport updates Might as well use the CS chunk array functionality for this. --- src/d3d11/d3d11_context.cpp | 131 +++++++++++----------------------- src/d3d11/d3d11_video.cpp | 4 +- src/d3d9/d3d9_device.cpp | 18 ++--- src/dxvk/dxvk_context.cpp | 13 ++-- src/dxvk/dxvk_context.h | 8 +-- src/dxvk/dxvk_context_state.h | 10 ++- 6 files changed, 68 insertions(+), 116 deletions(-) diff --git a/src/d3d11/d3d11_context.cpp b/src/d3d11/d3d11_context.cpp index 1f0b605df..b7fe38e81 100644 --- a/src/d3d11/d3d11_context.cpp +++ b/src/d3d11/d3d11_context.cpp @@ -2469,8 +2469,8 @@ namespace dxvk { // In D3D11, the rasterizer state defines whether the scissor test is // enabled, so if that changes, we need to update scissor rects as well. - bool currScissorEnable = currRasterizerState != nullptr ? currRasterizerState->Desc()->ScissorEnable : false; - bool nextScissorEnable = nextRasterizerState != nullptr ? nextRasterizerState->Desc()->ScissorEnable : false; + bool currScissorEnable = currRasterizerState && currRasterizerState->Desc()->ScissorEnable; + bool nextScissorEnable = nextRasterizerState && nextRasterizerState->Desc()->ScissorEnable; if (currScissorEnable != nextScissorEnable) ApplyViewportState(); @@ -2545,13 +2545,8 @@ namespace dxvk { } } - if (m_state.rs.state != nullptr && dirty) { - D3D11_RASTERIZER_DESC rsDesc; - m_state.rs.state->GetDesc(&rsDesc); - - if (rsDesc.ScissorEnable) - ApplyViewportState(); - } + if (dirty && m_state.rs.state && m_state.rs.state->Desc()->ScissorEnable) + ApplyViewportState(); } @@ -3487,92 +3482,52 @@ namespace dxvk { template void D3D11CommonContext::ApplyViewportState() { - std::array viewports; - std::array scissors; - - // The backend can't handle a viewport count of zero, - // so we should at least specify one empty viewport uint32_t viewportCount = m_state.rs.numViewports; - if (unlikely(!viewportCount)) { - viewportCount = 1; - viewports[0] = VkViewport(); - scissors [0] = VkRect2D(); - } + if (likely(viewportCount)) { + EmitCsCmd(D3D11CmdType::None, viewportCount, + [] (DxvkContext* ctx, const DxvkViewport* viewports, size_t count) { + ctx->setViewports(count, viewports); + }); - // D3D11's coordinate system has its origin in the bottom left, - // but the viewport coordinates are aligned to the top-left - // corner so we can get away with flipping the viewport. - for (uint32_t i = 0; i < m_state.rs.numViewports; i++) { - const D3D11_VIEWPORT& vp = m_state.rs.viewports[i]; + // Vulkan does not provide an easy way to disable the scissor test, + // Set scissor rects that are at least as large as the framebuffer. + bool enableScissorTest = m_state.rs.state && m_state.rs.state->Desc()->ScissorEnable; - viewports[i] = VkViewport { - vp.TopLeftX, vp.Height + vp.TopLeftY, - vp.Width, -vp.Height, - vp.MinDepth, vp.MaxDepth, - }; - } + // D3D11's coordinate system has its origin in the bottom left, + // but the viewport coordinates are aligned to the top-left + // corner so we can get away with flipping the viewport. + for (uint32_t i = 0; i < viewportCount; i++) { + const auto& vp = m_state.rs.viewports[i]; - // Scissor rectangles. Vulkan does not provide an easy way - // to disable the scissor test, so we'll have to set scissor - // rects that are at least as large as the framebuffer. - bool enableScissorTest = false; - - if (m_state.rs.state != nullptr) { - D3D11_RASTERIZER_DESC rsDesc; - m_state.rs.state->GetDesc(&rsDesc); - enableScissorTest = rsDesc.ScissorEnable; - } + auto* dst = new (m_csData->at(i)) DxvkViewport(); + dst->viewport.x = vp.TopLeftX; + dst->viewport.y = vp.Height + vp.TopLeftY; + dst->viewport.width = vp.Width; + dst->viewport.height = -vp.Height; + dst->viewport.minDepth = vp.MinDepth; + dst->viewport.maxDepth = vp.MaxDepth; - for (uint32_t i = 0; i < m_state.rs.numViewports; i++) { - if (!enableScissorTest) { - scissors[i] = VkRect2D { - VkOffset2D { 0, 0 }, - VkExtent2D { + if (!enableScissorTest) { + dst->scissor.offset = VkOffset2D { 0, 0 }; + dst->scissor.extent = VkExtent2D { D3D11_VIEWPORT_BOUNDS_MAX, - D3D11_VIEWPORT_BOUNDS_MAX } }; - } else if (i >= m_state.rs.numScissors) { - scissors[i] = VkRect2D { - VkOffset2D { 0, 0 }, - VkExtent2D { 0, 0 } }; - } else { - D3D11_RECT sr = m_state.rs.scissors[i]; + D3D11_VIEWPORT_BOUNDS_MAX }; + } else if (i < m_state.rs.numScissors) { + const auto& sr = m_state.rs.scissors[i]; - VkOffset2D srPosA; - srPosA.x = std::max(0, sr.left); - srPosA.y = std::max(0, sr.top); - - VkOffset2D srPosB; - srPosB.x = std::max(srPosA.x, sr.right); - srPosB.y = std::max(srPosA.y, sr.bottom); - - VkExtent2D srSize; - srSize.width = uint32_t(srPosB.x - srPosA.x); - srSize.height = uint32_t(srPosB.y - srPosA.y); - - scissors[i] = VkRect2D { srPosA, srSize }; + dst->scissor.offset = VkOffset2D { sr.left, sr.top }; + dst->scissor.extent = VkExtent2D { + uint32_t(std::max(sr.left, sr.right) - sr.left), + uint32_t(std::max(sr.top, sr.bottom) - sr.top) }; + } } - } - - if (likely(viewportCount == 1)) { - EmitCs([ - cViewport = viewports[0], - cScissor = scissors[0] - ] (DxvkContext* ctx) { - ctx->setViewports(1, - &cViewport, - &cScissor); - }); } else { - EmitCs([ - cViewportCount = viewportCount, - cViewports = viewports, - cScissors = scissors - ] (DxvkContext* ctx) { - ctx->setViewports( - cViewportCount, - cViewports.data(), - cScissors.data()); + // The backend can't handle a viewport count of zero, + // so we should at least specify one empty viewport + EmitCs([] (DxvkContext* ctx) { + DxvkViewport viewport = { }; + ctx->setViewports(1, &viewport); }); } } @@ -4802,10 +4757,8 @@ namespace dxvk { ctx->setStencilReference(D3D11_DEFAULT_STENCIL_REFERENCE); // Reset viewports - auto viewport = VkViewport(); - auto scissor = VkRect2D(); - - ctx->setViewports(1, &viewport, &scissor); + DxvkViewport viewport = { }; + ctx->setViewports(1, &viewport); // Unbind indirect draw buffer ctx->bindDrawBuffers(DxvkBufferSlice(), DxvkBufferSlice()); diff --git a/src/d3d11/d3d11_video.cpp b/src/d3d11/d3d11_video.cpp index 7522b2df7..6b4899dcd 100644 --- a/src/d3d11/d3d11_video.cpp +++ b/src/d3d11/d3d11_video.cpp @@ -1293,8 +1293,10 @@ namespace dxvk { Rc uboSlice = m_ubo->allocateStorage(); memcpy(uboSlice->mapPtr(), &uboData, sizeof(uboData)); + DxvkViewport vp = { viewport, scissor }; + ctx->invalidateBuffer(m_ubo, std::move(uboSlice)); - ctx->setViewports(1, &viewport, &scissor); + ctx->setViewports(1, &vp); ctx->bindShader(Rc(m_vs)); ctx->bindShader(Rc(m_fs)); diff --git a/src/d3d9/d3d9_device.cpp b/src/d3d9/d3d9_device.cpp index ca328179b..d30698ce4 100644 --- a/src/d3d9/d3d9_device.cpp +++ b/src/d3d9/d3d9_device.cpp @@ -6732,9 +6732,6 @@ namespace dxvk { void D3D9DeviceEx::BindViewportAndScissor() { m_flags.clr(D3D9DeviceFlag::DirtyViewportScissor); - VkViewport viewport; - VkRect2D scissor; - // D3D9's coordinate system has its origin in the bottom left, // but the viewport coordinates are aligned to the top-left // corner so we can get away with flipping the viewport. @@ -6753,7 +6750,8 @@ namespace dxvk { zBias = 0.001f; } - viewport = VkViewport{ + DxvkViewport state = { }; + state.viewport = VkViewport{ float(vp.X) + cf, float(vp.Height + vp.Y) + cf, float(vp.Width), -float(vp.Height), std::clamp(vp.MinZ, 0.0f, 1.0f), @@ -6784,22 +6782,18 @@ namespace dxvk { srSize.width = uint32_t(srPosB.x - srPosA.x); srSize.height = uint32_t(srPosB.y - srPosA.y); - scissor = VkRect2D{ srPosA, srSize }; + state.scissor = VkRect2D{ srPosA, srSize }; } else { - scissor = VkRect2D{ + state.scissor = VkRect2D{ VkOffset2D { int32_t(vp.X), int32_t(vp.Y) }, VkExtent2D { vp.Width, vp.Height }}; } EmitCs([ - cViewport = viewport, - cScissor = scissor + cViewport = state ] (DxvkContext* ctx) { - ctx->setViewports( - 1, - &cViewport, - &cScissor); + ctx->setViewports(1, &cViewport); }); } diff --git a/src/dxvk/dxvk_context.cpp b/src/dxvk/dxvk_context.cpp index 964cae404..e2eed411e 100644 --- a/src/dxvk/dxvk_context.cpp +++ b/src/dxvk/dxvk_context.cpp @@ -2579,16 +2579,15 @@ namespace dxvk { void DxvkContext::setViewports( uint32_t viewportCount, - const VkViewport* viewports, - const VkRect2D* scissorRects) { + const DxvkViewport* viewports) { for (uint32_t i = 0; i < viewportCount; i++) { - m_state.vp.viewports[i] = viewports[i]; - m_state.vp.scissorRects[i] = scissorRects[i]; - + m_state.vp.viewports[i] = viewports[i].viewport; + m_state.vp.scissorRects[i] = viewports[i].scissor; + // Vulkan viewports are not allowed to have a width or // height of zero, so we fall back to a dummy viewport // and instead set an empty scissor rect, which is legal. - if (viewports[i].width == 0.0f || viewports[i].height == 0.0f) { + if (viewports[i].viewport.width <= 0.0f || viewports[i].viewport.height == 0.0f) { m_state.vp.viewports[i] = VkViewport { 0.0f, 0.0f, 1.0f, 1.0f, 0.0f, 1.0f }; m_state.vp.scissorRects[i] = VkRect2D { @@ -2596,7 +2595,7 @@ namespace dxvk { VkExtent2D { 0, 0 } }; } } - + m_state.vp.viewportCount = viewportCount; m_flags.set(DxvkContextFlag::GpDirtyViewport); } diff --git a/src/dxvk/dxvk_context.h b/src/dxvk/dxvk_context.h index 040ed049a..de223e323 100644 --- a/src/dxvk/dxvk_context.h +++ b/src/dxvk/dxvk_context.h @@ -1124,14 +1124,12 @@ namespace dxvk { * \brief Sets viewports * * \param [in] viewportCount Number of viewports - * \param [in] viewports The viewports - * \param [in] scissorRects Schissor rectangles + * \param [in] viewports The viewports and scissors */ void setViewports( uint32_t viewportCount, - const VkViewport* viewports, - const VkRect2D* scissorRects); - + const DxvkViewport* viewports); + /** * \brief Sets blend constants * diff --git a/src/dxvk/dxvk_context_state.h b/src/dxvk/dxvk_context_state.h index bf9ef18c0..093452f1c 100644 --- a/src/dxvk/dxvk_context_state.h +++ b/src/dxvk/dxvk_context_state.h @@ -114,8 +114,14 @@ namespace dxvk { std::array vertexStrides = { }; std::array vertexExtents = { }; }; - - + + + struct DxvkViewport { + VkViewport viewport = { }; + VkRect2D scissor = { }; + }; + + struct DxvkViewportState { uint32_t viewportCount = 0; std::array viewports = { };