[d3d9] Re-implement alpha test to support configurable accuracy

The current implementation always uses 12 bits of accuracy.
This commit is contained in:
Philip Rebohle 2022-08-12 17:35:19 +02:00 committed by Philip Rebohle
parent 2c713a34c9
commit 47fa3824dc
5 changed files with 87 additions and 15 deletions

View File

@ -5117,8 +5117,8 @@ namespace dxvk {
auto& rs = m_state.renderStates;
if constexpr (Item == D3D9RenderStateItem::AlphaRef) {
float alpha = float(rs[D3DRS_ALPHAREF] & 0xFF) / 255.0f;
UpdatePushConstant<offsetof(D3D9RenderStateInfo, alphaRef), sizeof(float)>(&alpha);
uint32_t alpha = rs[D3DRS_ALPHAREF];
UpdatePushConstant<offsetof(D3D9RenderStateInfo, alphaRef), sizeof(uint32_t)>(&alpha);
}
else if constexpr (Item == D3D9RenderStateItem::FogColor) {
Vector4 color;

View File

@ -215,6 +215,71 @@ namespace dxvk {
spvModule.opBranchConditional(isNotAlways, atestBeginLabel, atestSkipLabel);
spvModule.opLabel(atestBeginLabel);
// The lower 8 bits of the alpha ref contain the actual reference value
// from the API, the upper bits store the accuracy bit count minus 8.
// So if we want 12 bits of accuracy (i.e. 0-4095), that value will be 4.
uint32_t uintType = spvModule.defIntType(32, 0);
// Check if the given bit precision is supported
uint32_t precisionIntLabel = spvModule.allocateId();
uint32_t precisionFloatLabel = spvModule.allocateId();
uint32_t precisionEndLabel = spvModule.allocateId();
uint32_t useIntPrecision = spvModule.opULessThanEqual(boolType,
ctx.alphaPrecisionId, spvModule.constu32(8));
spvModule.opSelectionMerge(precisionEndLabel, spv::SelectionControlMaskNone);
spvModule.opBranchConditional(useIntPrecision, precisionIntLabel, precisionFloatLabel);
spvModule.opLabel(precisionIntLabel);
// Adjust alpha ref to the given range
uint32_t alphaRefIdInt = spvModule.opBitwiseOr(uintType,
spvModule.opShiftLeftLogical(uintType, ctx.alphaRefId, ctx.alphaPrecisionId),
spvModule.opShiftRightLogical(uintType, ctx.alphaRefId,
spvModule.opISub(uintType, spvModule.constu32(8), ctx.alphaPrecisionId)));
// Convert alpha ref to float since we'll do the comparison based on that
uint32_t floatType = spvModule.defFloatType(32);
alphaRefIdInt = spvModule.opConvertUtoF(floatType, alphaRefIdInt);
// Adjust alpha to the given range and round
uint32_t alphaFactorId = spvModule.opISub(uintType,
spvModule.opShiftLeftLogical(uintType, spvModule.constu32(256), ctx.alphaPrecisionId),
spvModule.constu32(1));
alphaFactorId = spvModule.opConvertUtoF(floatType, alphaFactorId);
uint32_t alphaIdInt = spvModule.opRoundEven(floatType,
spvModule.opFMul(floatType, ctx.alphaId, alphaFactorId));
spvModule.opBranch(precisionEndLabel);
spvModule.opLabel(precisionFloatLabel);
// If we're not using integer precision, normalize the alpha ref
uint32_t alphaRefIdFloat = spvModule.opFDiv(floatType,
spvModule.opConvertUtoF(floatType, ctx.alphaRefId),
spvModule.constf32(255.0f));
spvModule.opBranch(precisionEndLabel);
spvModule.opLabel(precisionEndLabel);
std::array<SpirvPhiLabel, 2> alphaRefLabels = {
SpirvPhiLabel { alphaRefIdInt, precisionIntLabel },
SpirvPhiLabel { alphaRefIdFloat, precisionFloatLabel },
};
uint32_t alphaRefId = spvModule.opPhi(floatType,
alphaRefLabels.size(),
alphaRefLabels.data());
std::array<SpirvPhiLabel, 2> alphaIdLabels = {
SpirvPhiLabel { alphaIdInt, precisionIntLabel },
SpirvPhiLabel { ctx.alphaId, precisionFloatLabel },
};
uint32_t alphaId = spvModule.opPhi(floatType,
alphaIdLabels.size(),
alphaIdLabels.data());
// switch (alpha_func) { ... }
spvModule.opSelectionMerge(atestTestLabel, spv::SelectionControlMaskNone);
spvModule.opSwitch(ctx.alphaFuncId,
@ -231,12 +296,12 @@ namespace dxvk {
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);
case VK_COMPARE_OP_LESS: return spvModule.opFOrdLessThan (boolType, alphaId, alphaRefId);
case VK_COMPARE_OP_EQUAL: return spvModule.opFOrdEqual (boolType, alphaId, alphaRefId);
case VK_COMPARE_OP_LESS_OR_EQUAL: return spvModule.opFOrdLessThanEqual (boolType, alphaId, alphaRefId);
case VK_COMPARE_OP_GREATER: return spvModule.opFOrdGreaterThan (boolType, alphaId, alphaRefId);
case VK_COMPARE_OP_NOT_EQUAL: return spvModule.opFOrdNotEqual (boolType, alphaId, alphaRefId);
case VK_COMPARE_OP_GREATER_OR_EQUAL: return spvModule.opFOrdGreaterThanEqual(boolType, alphaId, alphaRefId);
default:
case VK_COMPARE_OP_ALWAYS: return spvModule.constBool(true);
}
@ -271,6 +336,7 @@ namespace dxvk {
uint32_t SetupRenderStateBlock(SpirvModule& spvModule, uint32_t count) {
uint32_t floatType = spvModule.defFloatType(32);
uint32_t uintType = spvModule.defIntType(32, 0);
uint32_t vec3Type = spvModule.defVectorType(floatType, 3);
std::array<uint32_t, 11> rsMembers = {{
@ -278,7 +344,8 @@ namespace dxvk {
floatType,
floatType,
floatType,
floatType,
uintType,
floatType,
floatType,
@ -2300,7 +2367,7 @@ namespace dxvk {
void D3D9FFShaderCompiler::alphaTestPS() {
uint32_t floatPtr = m_module.defPointerType(m_floatType, spv::StorageClassPushConstant);
uint32_t uintPtr = m_module.defPointerType(m_uint32Type, spv::StorageClassPushConstant);
auto oC0 = m_ps.out.COLOR;
@ -2309,8 +2376,9 @@ namespace dxvk {
D3D9AlphaTestContext alphaTestContext;
alphaTestContext.alphaFuncId = m_spec.get(m_module, m_specUbo, SpecAlphaCompareOp);
alphaTestContext.alphaPrecisionId = m_spec.get(m_module, m_specUbo, SpecAlphaPrecisionBits);
alphaTestContext.alphaRefId = m_module.opLoad(m_floatType,
m_module.opAccessChain(floatPtr, m_rsBlock, 1, &alphaRefMember));
m_module.opAccessChain(uintPtr, m_rsBlock, 1, &alphaRefMember));
alphaTestContext.alphaId = m_module.opCompositeExtract(m_floatType,
m_module.opLoad(m_vec4Type, oC0),
1, &alphaComponentId);

View File

@ -40,6 +40,7 @@ namespace dxvk {
struct D3D9AlphaTestContext {
uint32_t alphaId;
uint32_t alphaPrecisionId;
uint32_t alphaFuncId;
uint32_t alphaRefId;
};

View File

@ -22,6 +22,7 @@ namespace dxvk {
SpecSamplerNull, // 1 bit for 20 samplers | Bits: 20
SpecProjectionType, // 1 bit for 6 PS 1.x samplers | Bits: 6
SpecAlphaPrecisionBits, // Range: 0 -> 8 or 0xF | Bits: 4
SpecVertexShaderBools, // 16 bools | Bits: 16
SpecPixelShaderBools, // 16 bools | Bits: 16
@ -56,6 +57,7 @@ namespace dxvk {
{ 2, 0, 20 }, // SamplerNull
{ 2, 20, 6 }, // ProjectionType
{ 2, 26, 4 }, // AlphaPrecisionBits
{ 3, 0, 16 }, // VertexShaderBools
{ 3, 16, 16 }, // PixelShaderBools

View File

@ -3694,9 +3694,9 @@ void DxsoCompiler::emitControlFlowGenericLoop(
void DxsoCompiler::emitPsProcessing() {
uint32_t boolType = m_module.defBoolType();
uint32_t floatType = m_module.defFloatType(32);
uint32_t floatPtr = m_module.defPointerType(floatType, spv::StorageClassPushConstant);
uint32_t uintType = m_module.defIntType(32, 0);
uint32_t uintPtr = m_module.defPointerType(uintType, spv::StorageClassPushConstant);
// Implement alpha test and fog
DxsoRegister color0;
@ -3712,8 +3712,9 @@ void DxsoCompiler::emitControlFlowGenericLoop(
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.alphaPrecisionId = m_spec.get(m_module, m_specUbo, SpecAlphaPrecisionBits);
alphaTestContext.alphaRefId = m_module.opLoad(uintType,
m_module.opAccessChain(uintPtr, m_rsBlock, 1, &alphaRefMember));
alphaTestContext.alphaId = m_module.opCompositeExtract(floatType,
m_module.opLoad(m_module.defVectorType(floatType, 4), oC0.id),
1, &alphaComponentId);