[Vulkan] Alpha test

This commit is contained in:
Triang3l 2022-06-30 22:20:51 +03:00
parent 6772c88141
commit f8b351138e
5 changed files with 182 additions and 1 deletions

View File

@ -231,6 +231,8 @@ void SpirvShaderTranslator::StartTranslation() {
offsetof(SystemConstants, texture_swizzled_signs), type_uint4_array_2},
{"texture_swizzles", offsetof(SystemConstants, texture_swizzles),
type_uint4_array_4},
{"alpha_test_reference", offsetof(SystemConstants, alpha_test_reference),
type_float_},
{"color_exp_bias", offsetof(SystemConstants, color_exp_bias),
type_float4_},
};
@ -606,6 +608,10 @@ std::vector<uint8_t> SpirvShaderTranslator::CompleteTranslation() {
execution_model = spv::ExecutionModelFragment;
builder_->addExecutionMode(function_main_,
spv::ExecutionModeOriginUpperLeft);
if (IsExecutionModeEarlyFragmentTests()) {
builder_->addExecutionMode(function_main_,
spv::ExecutionModeEarlyFragmentTests);
}
} else {
assert_true(is_vertex_shader());
execution_model = IsSpirvTessEvalShader()

View File

@ -34,7 +34,16 @@ class SpirvShaderTranslator : public ShaderTranslator {
// TODO(Triang3l): Change to 0xYYYYMMDD once it's out of the rapid
// prototyping stage (easier to do small granular updates with an
// incremental counter).
static constexpr uint32_t kVersion = 3;
static constexpr uint32_t kVersion = 4;
enum class DepthStencilMode : uint32_t {
kNoModifiers,
// Early fragment tests - enable if alpha test and alpha to coverage are
// disabled; ignored if anything in the shader blocks early Z writing.
kEarlyHint,
// TODO(Triang3l): Unorm24 (rounding) and float24 (truncating and
// rounding) output modes.
};
struct {
// Dynamically indexable register count from SQ_PROGRAM_CNTL.
@ -52,6 +61,8 @@ class SpirvShaderTranslator : public ShaderTranslator {
// must not be set for other primitive types - enables the point sprite
// coordinates input, and also effects the flag bits in PsParamGen.
uint32_t param_gen_point : 1;
// For host render targets - depth / stencil output mode.
DepthStencilMode depth_stencil_mode : 3;
} pixel;
uint64_t value = 0;
@ -64,6 +75,9 @@ class SpirvShaderTranslator : public ShaderTranslator {
kSysFlag_WNotReciprocal_Shift,
kSysFlag_PrimitivePolygonal_Shift,
kSysFlag_PrimitiveLine_Shift,
kSysFlag_AlphaPassIfLess_Shift,
kSysFlag_AlphaPassIfEqual_Shift,
kSysFlag_AlphaPassIfGreater_Shift,
kSysFlag_ConvertColor0ToGamma_Shift,
kSysFlag_ConvertColor1ToGamma_Shift,
kSysFlag_ConvertColor2ToGamma_Shift,
@ -76,6 +90,9 @@ class SpirvShaderTranslator : public ShaderTranslator {
kSysFlag_WNotReciprocal = 1u << kSysFlag_WNotReciprocal_Shift,
kSysFlag_PrimitivePolygonal = 1u << kSysFlag_PrimitivePolygonal_Shift,
kSysFlag_PrimitiveLine = 1u << kSysFlag_PrimitiveLine_Shift,
kSysFlag_AlphaPassIfLess = 1u << kSysFlag_AlphaPassIfLess_Shift,
kSysFlag_AlphaPassIfEqual = 1u << kSysFlag_AlphaPassIfEqual_Shift,
kSysFlag_AlphaPassIfGreater = 1u << kSysFlag_AlphaPassIfGreater_Shift,
kSysFlag_ConvertColor0ToGamma = 1u << kSysFlag_ConvertColor0ToGamma_Shift,
kSysFlag_ConvertColor1ToGamma = 1u << kSysFlag_ConvertColor1ToGamma_Shift,
kSysFlag_ConvertColor2ToGamma = 1u << kSysFlag_ConvertColor2ToGamma_Shift,
@ -108,6 +125,9 @@ class SpirvShaderTranslator : public ShaderTranslator {
// swizzles for 2 texture fetch constants (in bits 0:11 and 12:23).
uint32_t texture_swizzles[16];
float alpha_test_reference;
float padding_alpha_test_reference[3];
float color_exp_bias[4];
};
@ -311,6 +331,14 @@ class SpirvShaderTranslator : public ShaderTranslator {
GetSpirvShaderModification().vertex.host_vertex_shader_type);
}
bool IsExecutionModeEarlyFragmentTests() const {
// TODO(Triang3l): Not applicable to fragment shader interlock.
return is_pixel_shader() &&
GetSpirvShaderModification().pixel.depth_stencil_mode ==
Modification::DepthStencilMode::kEarlyHint &&
current_shader().implicit_early_z_write_allowed();
}
// Returns UINT32_MAX if PsParamGen doesn't need to be written.
uint32_t GetPsParamGenInterpolator() const;
@ -528,6 +556,7 @@ class SpirvShaderTranslator : public ShaderTranslator {
kSystemConstantNdcOffset,
kSystemConstantTextureSwizzledSigns,
kSystemConstantTextureSwizzles,
kSystemConstantAlphaTestReference,
kSystemConstantColorExpBias,
};
spv::Id uniform_system_constants_;

View File

@ -433,6 +433,129 @@ void SpirvShaderTranslator::CompleteFragmentShaderInMain() {
uniform_system_constants_, id_vector_temp_),
spv::NoPrecision);
if (current_shader().writes_color_target(0) &&
!IsExecutionModeEarlyFragmentTests()) {
// Alpha test.
// TODO(Triang3l): Check how alpha test works with NaN on Direct3D 9.
// Extract the comparison function (less, equal, greater bits).
spv::Id alpha_test_function = builder_->createTriOp(
spv::OpBitFieldUExtract, type_uint_, main_system_constant_flags_,
builder_->makeUintConstant(kSysFlag_AlphaPassIfLess_Shift),
builder_->makeUintConstant(3));
// Check if the comparison function is not "always" - that should pass even
// for NaN likely, unlike "less, equal or greater".
spv::Id alpha_test_function_is_non_always = builder_->createBinOp(
spv::OpINotEqual, type_bool_, alpha_test_function,
builder_->makeUintConstant(uint32_t(xenos::CompareFunction::kAlways)));
spv::Block& block_alpha_test = builder_->makeNewBlock();
spv::Block& block_alpha_test_merge = builder_->makeNewBlock();
SpirvCreateSelectionMerge(block_alpha_test_merge.getId(),
spv::SelectionControlDontFlattenMask);
builder_->createConditionalBranch(alpha_test_function_is_non_always,
&block_alpha_test,
&block_alpha_test_merge);
builder_->setBuildPoint(&block_alpha_test);
{
id_vector_temp_.clear();
id_vector_temp_.push_back(builder_->makeIntConstant(3));
spv::Id alpha_test_alpha =
builder_->createLoad(builder_->createAccessChain(
spv::StorageClassOutput,
output_fragment_data_[0], id_vector_temp_),
spv::NoPrecision);
id_vector_temp_.clear();
id_vector_temp_.push_back(
builder_->makeIntConstant(kSystemConstantAlphaTestReference));
spv::Id alpha_test_reference =
builder_->createLoad(builder_->createAccessChain(
spv::StorageClassUniform,
uniform_system_constants_, id_vector_temp_),
spv::NoPrecision);
// The comparison function is not "always" - perform the alpha test.
// Handle "not equal" specially (specifically as "not equal" so it's true
// for NaN, not "less or greater" which is false for NaN).
spv::Id alpha_test_function_is_not_equal = builder_->createBinOp(
spv::OpIEqual, type_bool_, alpha_test_function,
builder_->makeUintConstant(
uint32_t(xenos::CompareFunction::kNotEqual)));
spv::Block& block_alpha_test_not_equal = builder_->makeNewBlock();
spv::Block& block_alpha_test_non_not_equal = builder_->makeNewBlock();
spv::Block& block_alpha_test_not_equal_merge = builder_->makeNewBlock();
SpirvCreateSelectionMerge(block_alpha_test_not_equal_merge.getId(),
spv::SelectionControlDontFlattenMask);
builder_->createConditionalBranch(alpha_test_function_is_not_equal,
&block_alpha_test_not_equal,
&block_alpha_test_non_not_equal);
spv::Id alpha_test_result_not_equal, alpha_test_result_non_not_equal;
builder_->setBuildPoint(&block_alpha_test_not_equal);
{
// "Not equal" function.
alpha_test_result_not_equal =
builder_->createBinOp(spv::OpFUnordNotEqual, type_bool_,
alpha_test_alpha, alpha_test_reference);
builder_->createBranch(&block_alpha_test_not_equal_merge);
}
builder_->setBuildPoint(&block_alpha_test_non_not_equal);
{
// Function other than "not equal".
static const spv::Op kAlphaTestOps[] = {
spv::OpFOrdLessThan, spv::OpFOrdEqual, spv::OpFOrdGreaterThan};
for (uint32_t i = 0; i < 3; ++i) {
spv::Id alpha_test_comparison_result = builder_->createBinOp(
spv::OpLogicalAnd, type_bool_,
builder_->createBinOp(kAlphaTestOps[i], type_bool_,
alpha_test_alpha, alpha_test_reference),
builder_->createBinOp(
spv::OpINotEqual, type_bool_,
builder_->createBinOp(
spv::OpBitwiseAnd, type_uint_, alpha_test_function,
builder_->makeUintConstant(UINT32_C(1) << i)),
const_uint_0_));
if (i) {
alpha_test_result_non_not_equal = builder_->createBinOp(
spv::OpLogicalOr, type_bool_, alpha_test_result_non_not_equal,
alpha_test_comparison_result);
} else {
alpha_test_result_non_not_equal = alpha_test_comparison_result;
}
}
builder_->createBranch(&block_alpha_test_not_equal_merge);
}
builder_->setBuildPoint(&block_alpha_test_not_equal_merge);
spv::Id alpha_test_result;
{
std::unique_ptr<spv::Instruction> alpha_test_result_phi_op =
std::make_unique<spv::Instruction>(builder_->getUniqueId(),
type_bool_, spv::OpPhi);
alpha_test_result_phi_op->addIdOperand(alpha_test_result_not_equal);
alpha_test_result_phi_op->addIdOperand(
block_alpha_test_not_equal.getId());
alpha_test_result_phi_op->addIdOperand(alpha_test_result_non_not_equal);
alpha_test_result_phi_op->addIdOperand(
block_alpha_test_non_not_equal.getId());
alpha_test_result = alpha_test_result_phi_op->getResultId();
builder_->getBuildPoint()->addInstruction(
std::move(alpha_test_result_phi_op));
}
// Discard the pixel if the alpha test has failed. Creating a merge block
// even though it will contain just one OpBranch since SPIR-V requires
// structured control flow in shaders.
spv::Block& block_alpha_test_kill = builder_->makeNewBlock();
spv::Block& block_alpha_test_kill_merge = builder_->makeNewBlock();
SpirvCreateSelectionMerge(block_alpha_test_kill_merge.getId(),
spv::SelectionControlDontFlattenMask);
builder_->createConditionalBranch(alpha_test_result,
&block_alpha_test_kill_merge,
&block_alpha_test_kill);
builder_->setBuildPoint(&block_alpha_test_kill);
builder_->createNoResultOp(spv::OpKill);
// OpKill terminates the block.
builder_->setBuildPoint(&block_alpha_test_kill_merge);
builder_->createBranch(&block_alpha_test_merge);
}
builder_->setBuildPoint(&block_alpha_test_merge);
}
uint32_t color_targets_remaining = current_shader().writes_color_targets();
uint32_t color_target_index;
while (xe::bit_scan_forward(color_targets_remaining, &color_target_index)) {

View File

@ -3377,6 +3377,8 @@ void VulkanCommandProcessor::UpdateSystemConstantValues(
const RegisterFile& regs = *register_file_;
auto pa_cl_vte_cntl = regs.Get<reg::PA_CL_VTE_CNTL>();
float rb_alpha_ref = regs[XE_GPU_REG_RB_ALPHA_REF].f32;
auto rb_colorcontrol = regs.Get<reg::RB_COLORCONTROL>();
auto vgt_draw_initiator = regs.Get<reg::VGT_DRAW_INITIATOR>();
int32_t vgt_indx_offset = int32_t(regs[XE_GPU_REG_VGT_INDX_OFFSET].u32);
@ -3416,6 +3418,12 @@ void VulkanCommandProcessor::UpdateSystemConstantValues(
if (draw_util::IsPrimitiveLine(regs)) {
flags |= SpirvShaderTranslator::kSysFlag_PrimitiveLine;
}
// Alpha test.
xenos::CompareFunction alpha_test_function =
rb_colorcontrol.alpha_test_enable ? rb_colorcontrol.alpha_func
: xenos::CompareFunction::kAlways;
flags |= uint32_t(alpha_test_function)
<< SpirvShaderTranslator::kSysFlag_AlphaPassIfLess_Shift;
// Gamma writing.
// TODO(Triang3l): Gamma as sRGB check.
for (uint32_t i = 0; i < xenos::kMaxColorRenderTargets; ++i) {
@ -3492,6 +3500,10 @@ void VulkanCommandProcessor::UpdateSystemConstantValues(
}
}
// Alpha test.
dirty |= system_constants_.alpha_test_reference != rb_alpha_ref;
system_constants_.alpha_test_reference = rb_alpha_ref;
// Color exponent bias.
for (uint32_t i = 0; i < xenos::kMaxColorRenderTargets; ++i) {
reg::RB_COLOR_INFO color_info = color_infos[i];

View File

@ -155,6 +155,17 @@ VulkanPipelineCache::GetCurrentPixelShaderModification(
}
}
using DepthStencilMode =
SpirvShaderTranslator::Modification::DepthStencilMode;
if (shader.implicit_early_z_write_allowed() &&
(!shader.writes_color_target(0) ||
!draw_util::DoesCoverageDependOnAlpha(
regs.Get<reg::RB_COLORCONTROL>()))) {
modification.pixel.depth_stencil_mode = DepthStencilMode::kEarlyHint;
} else {
modification.pixel.depth_stencil_mode = DepthStencilMode::kNoModifiers;
}
return modification;
}