diff --git a/src/d3d9/d3d9_fixed_function.cpp b/src/d3d9/d3d9_fixed_function.cpp index 7c7629a9b..6f60e5ad2 100644 --- a/src/d3d9/d3d9_fixed_function.cpp +++ b/src/d3d9/d3d9_fixed_function.cpp @@ -189,6 +189,86 @@ namespace dxvk { } + void DoFixedFunctionAlphaTest(SpirvModule& spvModule, const D3D9AlphaTestContext& ctx) { + // Labels for the alpha test + std::array atestCaseLabels = {{ + { uint32_t(VK_COMPARE_OP_NEVER), spvModule.allocateId() }, + { uint32_t(VK_COMPARE_OP_LESS), spvModule.allocateId() }, + { uint32_t(VK_COMPARE_OP_EQUAL), spvModule.allocateId() }, + { uint32_t(VK_COMPARE_OP_LESS_OR_EQUAL), spvModule.allocateId() }, + { uint32_t(VK_COMPARE_OP_GREATER), spvModule.allocateId() }, + { uint32_t(VK_COMPARE_OP_NOT_EQUAL), spvModule.allocateId() }, + { uint32_t(VK_COMPARE_OP_GREATER_OR_EQUAL), spvModule.allocateId() }, + { uint32_t(VK_COMPARE_OP_ALWAYS), spvModule.allocateId() }, + }}; + + uint32_t atestBeginLabel = spvModule.allocateId(); + uint32_t atestTestLabel = spvModule.allocateId(); + uint32_t atestDiscardLabel = spvModule.allocateId(); + uint32_t atestKeepLabel = spvModule.allocateId(); + uint32_t atestSkipLabel = spvModule.allocateId(); + + // if (alpha_func != ALWAYS) { ... } + uint32_t boolType = spvModule.defBoolType(); + uint32_t isNotAlways = spvModule.opINotEqual(boolType, ctx.alphaFuncId, spvModule.constu32(VK_COMPARE_OP_ALWAYS)); + spvModule.opSelectionMerge(atestSkipLabel, spv::SelectionControlMaskNone); + spvModule.opBranchConditional(isNotAlways, atestBeginLabel, atestSkipLabel); + spvModule.opLabel(atestBeginLabel); + + // switch (alpha_func) { ... } + spvModule.opSelectionMerge(atestTestLabel, spv::SelectionControlMaskNone); + spvModule.opSwitch(ctx.alphaFuncId, + atestCaseLabels[uint32_t(VK_COMPARE_OP_ALWAYS)].labelId, + atestCaseLabels.size(), + atestCaseLabels.data()); + + std::array atestVariables; + + for (uint32_t i = 0; i < atestCaseLabels.size(); i++) { + spvModule.opLabel(atestCaseLabels[i].labelId); + + atestVariables[i].labelId = atestCaseLabels[i].labelId; + atestVariables[i].varId = [&] { + switch (VkCompareOp(atestCaseLabels[i].literal)) { + case VK_COMPARE_OP_NEVER: return spvModule.constBool(false); + case VK_COMPARE_OP_LESS: return spvModule.opFOrdLessThan (boolType, ctx.alphaId, ctx.alphaRefId); + case VK_COMPARE_OP_EQUAL: return spvModule.opFOrdEqual (boolType, ctx.alphaId, ctx.alphaRefId); + case VK_COMPARE_OP_LESS_OR_EQUAL: return spvModule.opFOrdLessThanEqual (boolType, ctx.alphaId, ctx.alphaRefId); + case VK_COMPARE_OP_GREATER: return spvModule.opFOrdGreaterThan (boolType, ctx.alphaId, ctx.alphaRefId); + case VK_COMPARE_OP_NOT_EQUAL: return spvModule.opFOrdNotEqual (boolType, ctx.alphaId, ctx.alphaRefId); + case VK_COMPARE_OP_GREATER_OR_EQUAL: return spvModule.opFOrdGreaterThanEqual(boolType, ctx.alphaId, ctx.alphaRefId); + default: + case VK_COMPARE_OP_ALWAYS: return spvModule.constBool(true); + } + }(); + + spvModule.opBranch(atestTestLabel); + } + + // end switch + spvModule.opLabel(atestTestLabel); + + uint32_t atestResult = spvModule.opPhi(boolType, + atestVariables.size(), + atestVariables.data()); + uint32_t atestDiscard = spvModule.opLogicalNot(boolType, atestResult); + + // if (do_discard) { ... } + spvModule.opSelectionMerge(atestKeepLabel, spv::SelectionControlMaskNone); + spvModule.opBranchConditional(atestDiscard, atestDiscardLabel, atestKeepLabel); + + spvModule.opLabel(atestDiscardLabel); + spvModule.opKill(); + + // end if (do_discard) + spvModule.opLabel(atestKeepLabel); + spvModule.opBranch(atestSkipLabel); + + // end if (alpha_test) + spvModule.opLabel(atestSkipLabel); + } + + uint32_t SetupRenderStateBlock(SpirvModule& spvModule, uint32_t count) { uint32_t floatType = spvModule.defFloatType(32); uint32_t vec3Type = spvModule.defVectorType(floatType, 3); @@ -2220,101 +2300,22 @@ namespace dxvk { void D3D9FFShaderCompiler::alphaTestPS() { - // Alpha testing - uint32_t boolType = m_module.defBoolType(); uint32_t floatPtr = m_module.defPointerType(m_floatType, spv::StorageClassPushConstant); - // Declare spec constants for render states - uint32_t alphaFuncId = m_spec.get(m_module, m_specUbo, SpecAlphaCompareOp); - - // Implement alpha test auto oC0 = m_ps.out.COLOR; - // Labels for the alpha test - std::array atestCaseLabels = { { - { uint32_t(VK_COMPARE_OP_NEVER), m_module.allocateId() }, - { uint32_t(VK_COMPARE_OP_LESS), m_module.allocateId() }, - { uint32_t(VK_COMPARE_OP_EQUAL), m_module.allocateId() }, - { uint32_t(VK_COMPARE_OP_LESS_OR_EQUAL), m_module.allocateId() }, - { uint32_t(VK_COMPARE_OP_GREATER), m_module.allocateId() }, - { uint32_t(VK_COMPARE_OP_NOT_EQUAL), m_module.allocateId() }, - { uint32_t(VK_COMPARE_OP_GREATER_OR_EQUAL), m_module.allocateId() }, - { uint32_t(VK_COMPARE_OP_ALWAYS), m_module.allocateId() }, - } }; - uint32_t atestBeginLabel = m_module.allocateId(); - uint32_t atestTestLabel = m_module.allocateId(); - uint32_t atestDiscardLabel = m_module.allocateId(); - uint32_t atestKeepLabel = m_module.allocateId(); - uint32_t atestSkipLabel = m_module.allocateId(); - - // if (alpha_test) { ... } - uint32_t isNotAlways = m_module.opINotEqual(boolType, alphaFuncId, m_module.constu32(VK_COMPARE_OP_ALWAYS)); - m_module.opSelectionMerge(atestSkipLabel, spv::SelectionControlMaskNone); - m_module.opBranchConditional(isNotAlways, atestBeginLabel, atestSkipLabel); - m_module.opLabel(atestBeginLabel); - - // Load alpha component uint32_t alphaComponentId = 3; - uint32_t alphaId = m_module.opCompositeExtract(m_floatType, + uint32_t alphaRefMember = m_module.constu32(uint32_t(D3D9RenderStateItem::AlphaRef)); + + D3D9AlphaTestContext alphaTestContext; + alphaTestContext.alphaFuncId = m_spec.get(m_module, m_specUbo, SpecAlphaCompareOp); + alphaTestContext.alphaRefId = m_module.opLoad(m_floatType, + m_module.opAccessChain(floatPtr, m_rsBlock, 1, &alphaRefMember)); + alphaTestContext.alphaId = m_module.opCompositeExtract(m_floatType, m_module.opLoad(m_vec4Type, oC0), 1, &alphaComponentId); - // Load alpha reference - uint32_t alphaRefMember = m_module.constu32(uint32_t(D3D9RenderStateItem::AlphaRef)); - uint32_t alphaRefId = m_module.opLoad(m_floatType, - m_module.opAccessChain(floatPtr, m_rsBlock, 1, &alphaRefMember)); - - // switch (alpha_func) { ... } - m_module.opSelectionMerge(atestTestLabel, spv::SelectionControlMaskNone); - m_module.opSwitch(alphaFuncId, - atestCaseLabels[uint32_t(VK_COMPARE_OP_ALWAYS)].labelId, - atestCaseLabels.size(), - atestCaseLabels.data()); - - std::array atestVariables; - - for (uint32_t i = 0; i < atestCaseLabels.size(); i++) { - m_module.opLabel(atestCaseLabels[i].labelId); - - atestVariables[i].labelId = atestCaseLabels[i].labelId; - atestVariables[i].varId = [&] { - switch (VkCompareOp(atestCaseLabels[i].literal)) { - case VK_COMPARE_OP_NEVER: return m_module.constBool(false); - case VK_COMPARE_OP_LESS: return m_module.opFOrdLessThan(boolType, alphaId, alphaRefId); - case VK_COMPARE_OP_EQUAL: return m_module.opFOrdEqual(boolType, alphaId, alphaRefId); - case VK_COMPARE_OP_LESS_OR_EQUAL: return m_module.opFOrdLessThanEqual(boolType, alphaId, alphaRefId); - case VK_COMPARE_OP_GREATER: return m_module.opFOrdGreaterThan(boolType, alphaId, alphaRefId); - case VK_COMPARE_OP_NOT_EQUAL: return m_module.opFOrdNotEqual(boolType, alphaId, alphaRefId); - case VK_COMPARE_OP_GREATER_OR_EQUAL: return m_module.opFOrdGreaterThanEqual(boolType, alphaId, alphaRefId); - default: - case VK_COMPARE_OP_ALWAYS: return m_module.constBool(true); - } - }(); - - m_module.opBranch(atestTestLabel); - } - - // end switch - m_module.opLabel(atestTestLabel); - - uint32_t atestResult = m_module.opPhi(boolType, - atestVariables.size(), - atestVariables.data()); - uint32_t atestDiscard = m_module.opLogicalNot(boolType, atestResult); - - // if (do_discard) { ... } - m_module.opSelectionMerge(atestKeepLabel, spv::SelectionControlMaskNone); - m_module.opBranchConditional(atestDiscard, atestDiscardLabel, atestKeepLabel); - - m_module.opLabel(atestDiscardLabel); - m_module.opKill(); - - // end if (do_discard) - m_module.opLabel(atestKeepLabel); - m_module.opBranch(atestSkipLabel); - - // end if (alpha_test) - m_module.opLabel(atestSkipLabel); + DoFixedFunctionAlphaTest(m_module, alphaTestContext); } diff --git a/src/d3d9/d3d9_fixed_function.h b/src/d3d9/d3d9_fixed_function.h index 8880f8515..492c265fd 100644 --- a/src/d3d9/d3d9_fixed_function.h +++ b/src/d3d9/d3d9_fixed_function.h @@ -38,6 +38,12 @@ namespace dxvk { uint32_t SpecUBO; }; + struct D3D9AlphaTestContext { + uint32_t alphaId; + uint32_t alphaFuncId; + uint32_t alphaRefId; + }; + struct D3D9FixedFunctionOptions { D3D9FixedFunctionOptions(const D3D9Options* options); @@ -48,6 +54,8 @@ namespace dxvk { // Returns new oColor if PS uint32_t DoFixedFunctionFog(D3D9ShaderSpecConstantManager& spec, SpirvModule& spvModule, const D3D9FogContext& fogCtx); + void DoFixedFunctionAlphaTest(SpirvModule& spvModule, const D3D9AlphaTestContext& ctx); + // Returns a render state block uint32_t SetupRenderStateBlock(SpirvModule& spvModule, uint32_t count); diff --git a/src/d3d9/d3d9_state.h b/src/d3d9/d3d9_state.h index d87b8f531..a3dd0a570 100644 --- a/src/d3d9/d3d9_state.h +++ b/src/d3d9/d3d9_state.h @@ -36,7 +36,7 @@ namespace dxvk { float fogEnd = 1.0f; float fogDensity = 1.0f; - float alphaRef = 0.0f; + uint32_t alphaRef = 0u; float pointSize = 1.0f; float pointSizeMin = 1.0f; diff --git a/src/dxso/dxso_compiler.cpp b/src/dxso/dxso_compiler.cpp index d0e5fc452..05028daa3 100644 --- a/src/dxso/dxso_compiler.cpp +++ b/src/dxso/dxso_compiler.cpp @@ -3698,8 +3698,6 @@ void DxsoCompiler::emitControlFlowGenericLoop( uint32_t floatType = m_module.defFloatType(32); uint32_t floatPtr = m_module.defPointerType(floatType, spv::StorageClassPushConstant); - uint32_t alphaFuncId = m_spec.get(m_module, m_specUbo, SpecAlphaCompareOp); - // Implement alpha test and fog DxsoRegister color0; color0.id = DxsoRegisterId{ DxsoRegisterType::ColorOut, 0 }; @@ -3709,107 +3707,18 @@ void DxsoCompiler::emitControlFlowGenericLoop( if (m_programInfo.majorVersion() < 3) emitFog(); - // Labels for the alpha test - std::array atestCaseLabels = {{ - { uint32_t(VK_COMPARE_OP_NEVER), m_module.allocateId() }, - { uint32_t(VK_COMPARE_OP_LESS), m_module.allocateId() }, - { uint32_t(VK_COMPARE_OP_EQUAL), m_module.allocateId() }, - { uint32_t(VK_COMPARE_OP_LESS_OR_EQUAL), m_module.allocateId() }, - { uint32_t(VK_COMPARE_OP_GREATER), m_module.allocateId() }, - { uint32_t(VK_COMPARE_OP_NOT_EQUAL), m_module.allocateId() }, - { uint32_t(VK_COMPARE_OP_GREATER_OR_EQUAL), m_module.allocateId() }, - { uint32_t(VK_COMPARE_OP_ALWAYS), m_module.allocateId() }, - }}; - - uint32_t atestBeginLabel = m_module.allocateId(); - uint32_t atestTestLabel = m_module.allocateId(); - uint32_t atestDiscardLabel = m_module.allocateId(); - uint32_t atestKeepLabel = m_module.allocateId(); - uint32_t atestSkipLabel = m_module.allocateId(); - - // if (alpha_func != ALWAYS) { ... } - uint32_t isNotAlways = m_module.opINotEqual(boolType, alphaFuncId, m_module.constu32(VK_COMPARE_OP_ALWAYS)); - m_module.opSelectionMerge(atestSkipLabel, spv::SelectionControlMaskNone); - m_module.opBranchConditional(isNotAlways, atestBeginLabel, atestSkipLabel); - m_module.opLabel(atestBeginLabel); - - // Load alpha component + uint32_t alphaRefMember = m_module.constu32(uint32_t(D3D9RenderStateItem::AlphaRef)); uint32_t alphaComponentId = 3; - uint32_t alphaId = m_module.opCompositeExtract(floatType, + + D3D9AlphaTestContext alphaTestContext; + alphaTestContext.alphaFuncId = m_spec.get(m_module, m_specUbo, SpecAlphaCompareOp); + alphaTestContext.alphaRefId = m_module.opLoad(floatType, + m_module.opAccessChain(floatPtr, m_rsBlock, 1, &alphaRefMember)); + alphaTestContext.alphaId = m_module.opCompositeExtract(floatType, m_module.opLoad(m_module.defVectorType(floatType, 4), oC0.id), 1, &alphaComponentId); - if (m_moduleInfo.options.alphaTestWiggleRoom) { - // NV has wonky interpolation of all 1's in a VS -> PS going to 0.999999... - // This causes garbage-looking graphics on people's clothing in EverQuest 2 as it does alpha == 1.0. - - // My testing shows the alpha test has a precision of 1/256 for all A8 and below formats, - // and around 1 / 2048 for A32F formats and 1 / 4096 for A16F formats (It makes no sense to me too) - // so anyway, we're just going to round this to a precision of 1 / 4096 and hopefully this should make things happy - // everywhere. - const uint32_t alphaSizeId = m_module.constf32(4096.0f); - - alphaId = m_module.opFMul(floatType, alphaId, alphaSizeId); - alphaId = m_module.opRound(floatType, alphaId); - alphaId = m_module.opFDiv(floatType, alphaId, alphaSizeId); - } - - // Load alpha reference - uint32_t alphaRefMember = m_module.constu32(uint32_t(D3D9RenderStateItem::AlphaRef)); - uint32_t alphaRefId = m_module.opLoad(floatType, - m_module.opAccessChain(floatPtr, m_rsBlock, 1, &alphaRefMember)); - - // switch (alpha_func) { ... } - m_module.opSelectionMerge(atestTestLabel, spv::SelectionControlMaskNone); - m_module.opSwitch(alphaFuncId, - atestCaseLabels[uint32_t(VK_COMPARE_OP_ALWAYS)].labelId, - atestCaseLabels.size(), - atestCaseLabels.data()); - - std::array atestVariables; - - for (uint32_t i = 0; i < atestCaseLabels.size(); i++) { - m_module.opLabel(atestCaseLabels[i].labelId); - - atestVariables[i].labelId = atestCaseLabels[i].labelId; - atestVariables[i].varId = [&] { - switch (VkCompareOp(atestCaseLabels[i].literal)) { - case VK_COMPARE_OP_NEVER: return m_module.constBool(false); - case VK_COMPARE_OP_LESS: return m_module.opFOrdLessThan (boolType, alphaId, alphaRefId); - case VK_COMPARE_OP_EQUAL: return m_module.opFOrdEqual (boolType, alphaId, alphaRefId); - case VK_COMPARE_OP_LESS_OR_EQUAL: return m_module.opFOrdLessThanEqual (boolType, alphaId, alphaRefId); - case VK_COMPARE_OP_GREATER: return m_module.opFOrdGreaterThan (boolType, alphaId, alphaRefId); - case VK_COMPARE_OP_NOT_EQUAL: return m_module.opFOrdNotEqual (boolType, alphaId, alphaRefId); - case VK_COMPARE_OP_GREATER_OR_EQUAL: return m_module.opFOrdGreaterThanEqual(boolType, alphaId, alphaRefId); - default: - case VK_COMPARE_OP_ALWAYS: return m_module.constBool(true); - } - }(); - - m_module.opBranch(atestTestLabel); - } - - // end switch - m_module.opLabel(atestTestLabel); - - uint32_t atestResult = m_module.opPhi(boolType, - atestVariables.size(), - atestVariables.data()); - uint32_t atestDiscard = m_module.opLogicalNot(boolType, atestResult); - - // if (do_discard) { ... } - m_module.opSelectionMerge(atestKeepLabel, spv::SelectionControlMaskNone); - m_module.opBranchConditional(atestDiscard, atestDiscardLabel, atestKeepLabel); - - m_module.opLabel(atestDiscardLabel); - m_module.opKill(); - - // end if (do_discard) - m_module.opLabel(atestKeepLabel); - m_module.opBranch(atestSkipLabel); - - // end if (alpha_test) - m_module.opLabel(atestSkipLabel); + DoFixedFunctionAlphaTest(m_module, alphaTestContext); } }