[Vulkan] Alpha test
This commit is contained in:
parent
6772c88141
commit
f8b351138e
|
@ -231,6 +231,8 @@ void SpirvShaderTranslator::StartTranslation() {
|
||||||
offsetof(SystemConstants, texture_swizzled_signs), type_uint4_array_2},
|
offsetof(SystemConstants, texture_swizzled_signs), type_uint4_array_2},
|
||||||
{"texture_swizzles", offsetof(SystemConstants, texture_swizzles),
|
{"texture_swizzles", offsetof(SystemConstants, texture_swizzles),
|
||||||
type_uint4_array_4},
|
type_uint4_array_4},
|
||||||
|
{"alpha_test_reference", offsetof(SystemConstants, alpha_test_reference),
|
||||||
|
type_float_},
|
||||||
{"color_exp_bias", offsetof(SystemConstants, color_exp_bias),
|
{"color_exp_bias", offsetof(SystemConstants, color_exp_bias),
|
||||||
type_float4_},
|
type_float4_},
|
||||||
};
|
};
|
||||||
|
@ -606,6 +608,10 @@ std::vector<uint8_t> SpirvShaderTranslator::CompleteTranslation() {
|
||||||
execution_model = spv::ExecutionModelFragment;
|
execution_model = spv::ExecutionModelFragment;
|
||||||
builder_->addExecutionMode(function_main_,
|
builder_->addExecutionMode(function_main_,
|
||||||
spv::ExecutionModeOriginUpperLeft);
|
spv::ExecutionModeOriginUpperLeft);
|
||||||
|
if (IsExecutionModeEarlyFragmentTests()) {
|
||||||
|
builder_->addExecutionMode(function_main_,
|
||||||
|
spv::ExecutionModeEarlyFragmentTests);
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
assert_true(is_vertex_shader());
|
assert_true(is_vertex_shader());
|
||||||
execution_model = IsSpirvTessEvalShader()
|
execution_model = IsSpirvTessEvalShader()
|
||||||
|
|
|
@ -34,7 +34,16 @@ class SpirvShaderTranslator : public ShaderTranslator {
|
||||||
// TODO(Triang3l): Change to 0xYYYYMMDD once it's out of the rapid
|
// TODO(Triang3l): Change to 0xYYYYMMDD once it's out of the rapid
|
||||||
// prototyping stage (easier to do small granular updates with an
|
// prototyping stage (easier to do small granular updates with an
|
||||||
// incremental counter).
|
// 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 {
|
struct {
|
||||||
// Dynamically indexable register count from SQ_PROGRAM_CNTL.
|
// 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
|
// must not be set for other primitive types - enables the point sprite
|
||||||
// coordinates input, and also effects the flag bits in PsParamGen.
|
// coordinates input, and also effects the flag bits in PsParamGen.
|
||||||
uint32_t param_gen_point : 1;
|
uint32_t param_gen_point : 1;
|
||||||
|
// For host render targets - depth / stencil output mode.
|
||||||
|
DepthStencilMode depth_stencil_mode : 3;
|
||||||
} pixel;
|
} pixel;
|
||||||
uint64_t value = 0;
|
uint64_t value = 0;
|
||||||
|
|
||||||
|
@ -64,6 +75,9 @@ class SpirvShaderTranslator : public ShaderTranslator {
|
||||||
kSysFlag_WNotReciprocal_Shift,
|
kSysFlag_WNotReciprocal_Shift,
|
||||||
kSysFlag_PrimitivePolygonal_Shift,
|
kSysFlag_PrimitivePolygonal_Shift,
|
||||||
kSysFlag_PrimitiveLine_Shift,
|
kSysFlag_PrimitiveLine_Shift,
|
||||||
|
kSysFlag_AlphaPassIfLess_Shift,
|
||||||
|
kSysFlag_AlphaPassIfEqual_Shift,
|
||||||
|
kSysFlag_AlphaPassIfGreater_Shift,
|
||||||
kSysFlag_ConvertColor0ToGamma_Shift,
|
kSysFlag_ConvertColor0ToGamma_Shift,
|
||||||
kSysFlag_ConvertColor1ToGamma_Shift,
|
kSysFlag_ConvertColor1ToGamma_Shift,
|
||||||
kSysFlag_ConvertColor2ToGamma_Shift,
|
kSysFlag_ConvertColor2ToGamma_Shift,
|
||||||
|
@ -76,6 +90,9 @@ class SpirvShaderTranslator : public ShaderTranslator {
|
||||||
kSysFlag_WNotReciprocal = 1u << kSysFlag_WNotReciprocal_Shift,
|
kSysFlag_WNotReciprocal = 1u << kSysFlag_WNotReciprocal_Shift,
|
||||||
kSysFlag_PrimitivePolygonal = 1u << kSysFlag_PrimitivePolygonal_Shift,
|
kSysFlag_PrimitivePolygonal = 1u << kSysFlag_PrimitivePolygonal_Shift,
|
||||||
kSysFlag_PrimitiveLine = 1u << kSysFlag_PrimitiveLine_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_ConvertColor0ToGamma = 1u << kSysFlag_ConvertColor0ToGamma_Shift,
|
||||||
kSysFlag_ConvertColor1ToGamma = 1u << kSysFlag_ConvertColor1ToGamma_Shift,
|
kSysFlag_ConvertColor1ToGamma = 1u << kSysFlag_ConvertColor1ToGamma_Shift,
|
||||||
kSysFlag_ConvertColor2ToGamma = 1u << kSysFlag_ConvertColor2ToGamma_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).
|
// swizzles for 2 texture fetch constants (in bits 0:11 and 12:23).
|
||||||
uint32_t texture_swizzles[16];
|
uint32_t texture_swizzles[16];
|
||||||
|
|
||||||
|
float alpha_test_reference;
|
||||||
|
float padding_alpha_test_reference[3];
|
||||||
|
|
||||||
float color_exp_bias[4];
|
float color_exp_bias[4];
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -311,6 +331,14 @@ class SpirvShaderTranslator : public ShaderTranslator {
|
||||||
GetSpirvShaderModification().vertex.host_vertex_shader_type);
|
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.
|
// Returns UINT32_MAX if PsParamGen doesn't need to be written.
|
||||||
uint32_t GetPsParamGenInterpolator() const;
|
uint32_t GetPsParamGenInterpolator() const;
|
||||||
|
|
||||||
|
@ -528,6 +556,7 @@ class SpirvShaderTranslator : public ShaderTranslator {
|
||||||
kSystemConstantNdcOffset,
|
kSystemConstantNdcOffset,
|
||||||
kSystemConstantTextureSwizzledSigns,
|
kSystemConstantTextureSwizzledSigns,
|
||||||
kSystemConstantTextureSwizzles,
|
kSystemConstantTextureSwizzles,
|
||||||
|
kSystemConstantAlphaTestReference,
|
||||||
kSystemConstantColorExpBias,
|
kSystemConstantColorExpBias,
|
||||||
};
|
};
|
||||||
spv::Id uniform_system_constants_;
|
spv::Id uniform_system_constants_;
|
||||||
|
|
|
@ -433,6 +433,129 @@ void SpirvShaderTranslator::CompleteFragmentShaderInMain() {
|
||||||
uniform_system_constants_, id_vector_temp_),
|
uniform_system_constants_, id_vector_temp_),
|
||||||
spv::NoPrecision);
|
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_targets_remaining = current_shader().writes_color_targets();
|
||||||
uint32_t color_target_index;
|
uint32_t color_target_index;
|
||||||
while (xe::bit_scan_forward(color_targets_remaining, &color_target_index)) {
|
while (xe::bit_scan_forward(color_targets_remaining, &color_target_index)) {
|
||||||
|
|
|
@ -3377,6 +3377,8 @@ void VulkanCommandProcessor::UpdateSystemConstantValues(
|
||||||
|
|
||||||
const RegisterFile& regs = *register_file_;
|
const RegisterFile& regs = *register_file_;
|
||||||
auto pa_cl_vte_cntl = regs.Get<reg::PA_CL_VTE_CNTL>();
|
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>();
|
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);
|
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)) {
|
if (draw_util::IsPrimitiveLine(regs)) {
|
||||||
flags |= SpirvShaderTranslator::kSysFlag_PrimitiveLine;
|
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.
|
// Gamma writing.
|
||||||
// TODO(Triang3l): Gamma as sRGB check.
|
// TODO(Triang3l): Gamma as sRGB check.
|
||||||
for (uint32_t i = 0; i < xenos::kMaxColorRenderTargets; ++i) {
|
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.
|
// Color exponent bias.
|
||||||
for (uint32_t i = 0; i < xenos::kMaxColorRenderTargets; ++i) {
|
for (uint32_t i = 0; i < xenos::kMaxColorRenderTargets; ++i) {
|
||||||
reg::RB_COLOR_INFO color_info = color_infos[i];
|
reg::RB_COLOR_INFO color_info = color_infos[i];
|
||||||
|
|
|
@ -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;
|
return modification;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue