From 768a078250fe39f00b034a6e8ec2dbff500f36f7 Mon Sep 17 00:00:00 2001 From: ZeroFault Date: Mon, 30 Apr 2018 02:41:57 -0600 Subject: [PATCH] [d3d11] Implement state block normalization (#333) * [d3d11] implement stateblock normalization * add const to default state description object * fix code formatting * Correct the blend state normalization * add missing error return * code cleanup and refactoring * remove unecessary const qualifier and fix code formatting * [d3d11] Cosmetic changes --- src/d3d11/d3d11_blend.cpp | 120 ++++++++++++++++++++++++++---- src/d3d11/d3d11_blend.h | 23 +++++- src/d3d11/d3d11_depth_stencil.cpp | 70 +++++++++++++++-- src/d3d11/d3d11_depth_stencil.h | 13 ++++ src/d3d11/d3d11_rasterizer.cpp | 31 +++++++- src/d3d11/d3d11_sampler.cpp | 49 +++++++++++- src/d3d11/d3d11_sampler.h | 6 ++ 7 files changed, 286 insertions(+), 26 deletions(-) diff --git a/src/d3d11/d3d11_blend.cpp b/src/d3d11/d3d11_blend.cpp index cfab5b8c..accfd730 100644 --- a/src/d3d11/d3d11_blend.cpp +++ b/src/d3d11/d3d11_blend.cpp @@ -148,8 +148,63 @@ namespace dxvk { HRESULT D3D11BlendState::NormalizeDesc(D3D11_BLEND_DESC1* pDesc) { - // TODO validate - // TODO clear unused values + const D3D11_BLEND_DESC1 defaultDesc = DefaultDesc(); + + if (pDesc->AlphaToCoverageEnable) + pDesc->AlphaToCoverageEnable = TRUE; + + if (pDesc->IndependentBlendEnable) + pDesc->IndependentBlendEnable = TRUE; + + const uint32_t numRenderTargets = pDesc->IndependentBlendEnable ? 8 : 1; + + for (uint32_t i = 0; i < numRenderTargets; i++) { + D3D11_RENDER_TARGET_BLEND_DESC1* rt = &pDesc->RenderTarget[i]; + + if (rt->BlendEnable) { + rt->BlendEnable = TRUE; + + if (rt->LogicOpEnable) + return E_INVALIDARG; + + if (!ValidateBlendOperations( + rt->SrcBlend, rt->SrcBlendAlpha, + rt->DestBlend, rt->DestBlendAlpha, + rt->BlendOp, rt->BlendOpAlpha)) + return E_INVALIDARG; + } else { + rt->SrcBlend = defaultDesc.RenderTarget[0].SrcBlend; + rt->DestBlend = defaultDesc.RenderTarget[0].DestBlend; + rt->BlendOp = defaultDesc.RenderTarget[0].BlendOp; + rt->SrcBlendAlpha = defaultDesc.RenderTarget[0].SrcBlendAlpha; + rt->DestBlendAlpha = defaultDesc.RenderTarget[0].DestBlendAlpha; + rt->BlendOpAlpha = defaultDesc.RenderTarget[0].BlendOpAlpha; + } + + if (rt->LogicOpEnable) { + rt->LogicOpEnable = TRUE; + + // Blending must be disabled + // if the logic op is enabled + if (rt->BlendEnable + || pDesc->IndependentBlendEnable + || !ValidateLogicOp(rt->LogicOp)) + return E_INVALIDARG; + } else { + rt->LogicOp = defaultDesc.RenderTarget[0].LogicOp; + } + + if (rt->RenderTargetWriteMask > D3D11_COLOR_WRITE_ENABLE_ALL) + return E_INVALIDARG; + } + + for (uint32_t i = numRenderTargets; i < 8; i++) { + // Render targets blend operations are the same + // across all render targets when blend is enabled + // on rendertarget[0] with independent blend disabled + pDesc->RenderTarget[i] = pDesc->RenderTarget[0]; + } + return S_OK; } @@ -188,11 +243,8 @@ namespace dxvk { case D3D11_BLEND_INV_SRC1_COLOR: return VK_BLEND_FACTOR_ONE_MINUS_SRC1_COLOR; case D3D11_BLEND_SRC1_ALPHA: return VK_BLEND_FACTOR_SRC1_ALPHA; case D3D11_BLEND_INV_SRC1_ALPHA: return VK_BLEND_FACTOR_ONE_MINUS_SRC1_ALPHA; + default: return VK_BLEND_FACTOR_ZERO; } - - if (BlendFactor != 0) // prevent log spamming when apps use ZeroMemory - Logger::err(str::format("D3D11: Invalid blend factor: ", BlendFactor)); - return VK_BLEND_FACTOR_ZERO; } @@ -203,11 +255,8 @@ namespace dxvk { case D3D11_BLEND_OP_REV_SUBTRACT: return VK_BLEND_OP_REVERSE_SUBTRACT; case D3D11_BLEND_OP_MIN: return VK_BLEND_OP_MIN; case D3D11_BLEND_OP_MAX: return VK_BLEND_OP_MAX; + default: return VK_BLEND_OP_ADD; } - - if (BlendOp != 0) // prevent log spamming when apps use ZeroMemory - Logger::err(str::format("D3D11: Invalid blend op: ", BlendOp)); - return VK_BLEND_OP_ADD; } @@ -229,11 +278,54 @@ namespace dxvk { case D3D11_LOGIC_OP_AND_INVERTED: return VK_LOGIC_OP_AND_INVERTED; case D3D11_LOGIC_OP_OR_REVERSE: return VK_LOGIC_OP_OR_REVERSE; case D3D11_LOGIC_OP_OR_INVERTED: return VK_LOGIC_OP_OR_INVERTED; + default: return VK_LOGIC_OP_NO_OP; } - - if (LogicOp != 0) - Logger::err(str::format("D3D11: Invalid logic op: ", LogicOp)); - return VK_LOGIC_OP_NO_OP; + } + + + bool D3D11BlendState::ValidateBlendFactor(D3D11_BLEND Blend) { + return Blend >= D3D11_BLEND_ZERO + && Blend <= D3D11_BLEND_INV_SRC1_ALPHA; + } + + + bool D3D11BlendState::ValidateBlendFactorAlpha(D3D11_BLEND BlendAlpha) { + return BlendAlpha >= D3D11_BLEND_ZERO + && BlendAlpha <= D3D11_BLEND_INV_SRC1_ALPHA + && BlendAlpha != D3D11_BLEND_SRC_COLOR + && BlendAlpha != D3D11_BLEND_INV_SRC_COLOR + && BlendAlpha != D3D11_BLEND_DEST_COLOR + && BlendAlpha != D3D11_BLEND_INV_DEST_COLOR + && BlendAlpha != D3D11_BLEND_SRC1_COLOR + && BlendAlpha != D3D11_BLEND_INV_SRC1_COLOR; + } + + + bool D3D11BlendState::ValidateBlendOp(D3D11_BLEND_OP BlendOp) { + return BlendOp >= D3D11_BLEND_OP_ADD + && BlendOp <= D3D11_BLEND_OP_MAX; + } + + + bool D3D11BlendState::ValidateLogicOp(D3D11_LOGIC_OP LogicOp) { + return LogicOp >= D3D11_LOGIC_OP_CLEAR + && LogicOp <= D3D11_LOGIC_OP_OR_INVERTED; + } + + + bool D3D11BlendState::ValidateBlendOperations( + D3D11_BLEND SrcBlend, + D3D11_BLEND SrcBlendAlpha, + D3D11_BLEND DestBlend, + D3D11_BLEND DestBlendAlpha, + D3D11_BLEND_OP BlendOp, + D3D11_BLEND_OP BlendOpAlpha) { + return ValidateBlendOp(BlendOp) + && ValidateBlendOp(BlendOpAlpha) + && ValidateBlendFactor(SrcBlend) + && ValidateBlendFactor(DestBlend) + && ValidateBlendFactorAlpha(SrcBlendAlpha) + && ValidateBlendFactorAlpha(DestBlendAlpha); } } diff --git a/src/d3d11/d3d11_blend.h b/src/d3d11/d3d11_blend.h index c142b962..3b505414 100644 --- a/src/d3d11/d3d11_blend.h +++ b/src/d3d11/d3d11_blend.h @@ -44,6 +44,8 @@ namespace dxvk { static HRESULT NormalizeDesc( D3D11_BLEND_DESC1* pDesc); + + private: @@ -66,7 +68,26 @@ namespace dxvk { static VkLogicOp DecodeLogicOp( D3D11_LOGIC_OP LogicOp); - + + static bool ValidateBlendFactor( + D3D11_BLEND Blend); + + static bool ValidateBlendFactorAlpha( + D3D11_BLEND BlendAlpha); + + static bool ValidateBlendOp( + D3D11_BLEND_OP BlendOp); + + static bool ValidateLogicOp( + D3D11_LOGIC_OP LogicOp); + + static bool ValidateBlendOperations( + D3D11_BLEND SrcBlend, + D3D11_BLEND SrcBlendAlpha, + D3D11_BLEND SestBlend, + D3D11_BLEND DestBlendAlpha, + D3D11_BLEND_OP BlendOp, + D3D11_BLEND_OP BlendOpAlpha); }; } diff --git a/src/d3d11/d3d11_depth_stencil.cpp b/src/d3d11/d3d11_depth_stencil.cpp index 87add538..5be84231 100644 --- a/src/d3d11/d3d11_depth_stencil.cpp +++ b/src/d3d11/d3d11_depth_stencil.cpp @@ -50,8 +50,7 @@ namespace dxvk { } - void D3D11DepthStencilState::BindToContext( - const Rc& ctx) { + void D3D11DepthStencilState::BindToContext(const Rc& ctx) { ctx->setDepthStencilState(m_state); } @@ -77,8 +76,42 @@ namespace dxvk { HRESULT D3D11DepthStencilState::NormalizeDesc(D3D11_DEPTH_STENCIL_DESC* pDesc) { - // TODO validate - // TODO clear unused values + const D3D11_DEPTH_STENCIL_DESC defaultDesc = DefaultDesc(); + + if (pDesc->DepthEnable) { + pDesc->DepthEnable = TRUE; + + if (!ValidateDepthFunc(pDesc->DepthFunc)) + return E_INVALIDARG; + } else { + pDesc->DepthFunc = defaultDesc.DepthFunc; + pDesc->DepthWriteMask = defaultDesc.DepthWriteMask; + } + + if (!ValidateDepthWriteMask(pDesc->DepthWriteMask)) + return E_INVALIDARG; + + if (pDesc->StencilEnable) { + pDesc->StencilEnable = TRUE; + + if (!ValidateStencilFunc(pDesc->FrontFace.StencilFunc) + || !ValidateStencilOp(pDesc->FrontFace.StencilFailOp) + || !ValidateStencilOp(pDesc->FrontFace.StencilDepthFailOp) + || !ValidateStencilOp(pDesc->FrontFace.StencilPassOp)) + return E_INVALIDARG; + + if (!ValidateStencilFunc(pDesc->BackFace.StencilFunc) + || !ValidateStencilOp(pDesc->BackFace.StencilFailOp) + || !ValidateStencilOp(pDesc->BackFace.StencilDepthFailOp) + || !ValidateStencilOp(pDesc->BackFace.StencilPassOp)) + return E_INVALIDARG; + } else { + pDesc->FrontFace = defaultDesc.FrontFace; + pDesc->BackFace = defaultDesc.BackFace; + pDesc->StencilReadMask = defaultDesc.StencilReadMask; + pDesc->StencilWriteMask = defaultDesc.StencilWriteMask; + } + return S_OK; } @@ -116,11 +149,32 @@ namespace dxvk { case D3D11_STENCIL_OP_INVERT: return VK_STENCIL_OP_INVERT; case D3D11_STENCIL_OP_INCR: return VK_STENCIL_OP_INCREMENT_AND_WRAP; case D3D11_STENCIL_OP_DECR: return VK_STENCIL_OP_DECREMENT_AND_WRAP; + default: return VK_STENCIL_OP_KEEP; } - - if (Op != 0) - Logger::err(str::format("D3D11: Invalid stencil op: ", Op)); - return VK_STENCIL_OP_KEEP; + } + + + bool D3D11DepthStencilState::ValidateDepthFunc(D3D11_COMPARISON_FUNC Comparison) { + return Comparison >= D3D11_COMPARISON_NEVER + && Comparison <= D3D11_COMPARISON_ALWAYS; + } + + + bool D3D11DepthStencilState::ValidateStencilFunc(D3D11_COMPARISON_FUNC Comparison) { + return Comparison >= D3D11_COMPARISON_NEVER + && Comparison <= D3D11_COMPARISON_ALWAYS; + } + + + bool D3D11DepthStencilState::ValidateStencilOp(D3D11_STENCIL_OP StencilOp) { + return StencilOp >= D3D11_STENCIL_OP_KEEP + && StencilOp <= D3D11_STENCIL_OP_DECR; + } + + + bool D3D11DepthStencilState::ValidateDepthWriteMask(D3D11_DEPTH_WRITE_MASK Mask) { + return Mask == D3D11_DEPTH_WRITE_MASK_ZERO + || Mask == D3D11_DEPTH_WRITE_MASK_ALL; } } diff --git a/src/d3d11/d3d11_depth_stencil.h b/src/d3d11/d3d11_depth_stencil.h index 3ceb7008..5e3f7c33 100644 --- a/src/d3d11/d3d11_depth_stencil.h +++ b/src/d3d11/d3d11_depth_stencil.h @@ -37,6 +37,8 @@ namespace dxvk { static HRESULT NormalizeDesc( D3D11_DEPTH_STENCIL_DESC* pDesc); + + private: @@ -51,6 +53,17 @@ namespace dxvk { VkStencilOp DecodeStencilOp( D3D11_STENCIL_OP Op) const; + static bool ValidateDepthFunc( + D3D11_COMPARISON_FUNC Comparison); + + static bool ValidateStencilFunc( + D3D11_COMPARISON_FUNC Comparison); + + static bool ValidateStencilOp( + D3D11_STENCIL_OP StencilOp); + + static bool ValidateDepthWriteMask( + D3D11_DEPTH_WRITE_MASK Mask); }; } diff --git a/src/d3d11/d3d11_rasterizer.cpp b/src/d3d11/d3d11_rasterizer.cpp index 2cc5e542..034fd19c 100644 --- a/src/d3d11/d3d11_rasterizer.cpp +++ b/src/d3d11/d3d11_rasterizer.cpp @@ -9,7 +9,7 @@ namespace dxvk { : m_device(device), m_desc(desc) { // State that is not supported in D3D11 - m_state.enableDiscard = VK_FALSE; + m_state.enableDiscard = VK_FALSE; // Polygon mode. Determines whether the rasterizer fills // a polygon or renders lines connecting the vertices. @@ -151,7 +151,34 @@ namespace dxvk { HRESULT D3D11RasterizerState::NormalizeDesc( D3D11_RASTERIZER_DESC1* pDesc) { - // TODO validate + if (pDesc->FillMode < D3D11_FILL_WIREFRAME + || pDesc->FillMode > D3D11_FILL_SOLID) + return E_INVALIDARG; + + if (pDesc->CullMode < D3D11_CULL_NONE + || pDesc->CullMode > D3D11_CULL_BACK) + return E_INVALIDARG; + + if (pDesc->FrontCounterClockwise) + pDesc->FrontCounterClockwise = TRUE; + + if (pDesc->DepthClipEnable) + pDesc->DepthClipEnable = TRUE; + + if (pDesc->ScissorEnable) + pDesc->ScissorEnable = TRUE; + + if (pDesc->MultisampleEnable) + pDesc->MultisampleEnable = TRUE; + + if (pDesc->AntialiasedLineEnable) + pDesc->AntialiasedLineEnable = TRUE; + + if (pDesc->ForcedSampleCount != 0) { + if (FAILED(DecodeSampleCount(pDesc->ForcedSampleCount, nullptr))) + return E_INVALIDARG; + } + return S_OK; } diff --git a/src/d3d11/d3d11_sampler.cpp b/src/d3d11/d3d11_sampler.cpp index 9e427254..dacfcd06 100644 --- a/src/d3d11/d3d11_sampler.cpp +++ b/src/d3d11/d3d11_sampler.cpp @@ -82,11 +82,58 @@ namespace dxvk { const uint32_t filterBits = static_cast(pDesc->Filter); if (filterBits & 0xFFFFFF2A) { - Logger::err(str::format("D3D11SamplerState: Unhandled filter: ", filterBits)); + Logger::err(str::format( + "D3D11SamplerState: Unhandled filter: ", filterBits)); return E_INVALIDARG; } + if (filterBits & 0x40 /* anisotropic */) { + if (pDesc->MaxAnisotropy < 1 + || pDesc->MaxAnisotropy > 16) + return E_INVALIDARG; + } else if (pDesc->MaxAnisotropy < 0 + || pDesc->MaxAnisotropy > 16) { + return E_INVALIDARG; + } else { + // Reset anisotropy if it is not used + pDesc->MaxAnisotropy = 0; + } + + if (filterBits & 0x80 /* compare-to-depth */) { + if (!ValidateComparisonFunc(pDesc->ComparisonFunc)) + return E_INVALIDARG; + } else { + // Reset compare func if it is not used + pDesc->ComparisonFunc = D3D11_COMPARISON_NEVER; + } + + if (!ValidateAddressMode(pDesc->AddressU) + || !ValidateAddressMode(pDesc->AddressV) + || !ValidateAddressMode(pDesc->AddressW)) + return E_INVALIDARG; + + // Clear BorderColor to 0 if none of the address + // modes are D3D11_TEXTURE_ADDRESS_BORDER + if (pDesc->AddressU != D3D11_TEXTURE_ADDRESS_BORDER + && pDesc->AddressV != D3D11_TEXTURE_ADDRESS_BORDER + && pDesc->AddressW != D3D11_TEXTURE_ADDRESS_BORDER) { + for (int i = 0; i < 4; i++) + pDesc->BorderColor[i] = 0.0f; + } + return S_OK; } + + bool D3D11SamplerState::ValidateAddressMode(D3D11_TEXTURE_ADDRESS_MODE Mode) { + return Mode >= D3D11_TEXTURE_ADDRESS_WRAP + && Mode <= D3D11_TEXTURE_ADDRESS_MIRROR_ONCE; + } + + + bool D3D11SamplerState::ValidateComparisonFunc(D3D11_COMPARISON_FUNC Comparison) { + return Comparison >= D3D11_COMPARISON_NEVER + && Comparison <= D3D11_COMPARISON_ALWAYS; + } + } diff --git a/src/d3d11/d3d11_sampler.h b/src/d3d11/d3d11_sampler.h index e7f9eb08..7343a123 100644 --- a/src/d3d11/d3d11_sampler.h +++ b/src/d3d11/d3d11_sampler.h @@ -41,6 +41,12 @@ namespace dxvk { D3D11Device* const m_device; D3D11_SAMPLER_DESC m_desc; Rc m_sampler; + + static bool ValidateAddressMode( + D3D11_TEXTURE_ADDRESS_MODE Mode); + + static bool ValidateComparisonFunc( + D3D11_COMPARISON_FUNC Comparison); };