[d3d9,dxso] Factor out common alpha test code

This commit is contained in:
Philip Rebohle 2022-08-12 16:58:07 +02:00 committed by Philip Rebohle
parent 3806bd44d8
commit 2c713a34c9
4 changed files with 105 additions and 187 deletions

View File

@ -189,6 +189,86 @@ namespace dxvk {
}
void DoFixedFunctionAlphaTest(SpirvModule& spvModule, const D3D9AlphaTestContext& ctx) {
// Labels for the alpha test
std::array<SpirvSwitchCaseLabel, 8> 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<SpirvPhiLabel, 8> 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<SpirvSwitchCaseLabel, 8> 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<SpirvPhiLabel, 8> 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);
}

View File

@ -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);

View File

@ -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;

View File

@ -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<SpirvSwitchCaseLabel, 8> 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<SpirvPhiLabel, 8> 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);
}
}