mirror of
https://github.com/xenia-project/xenia.git
synced 2024-11-27 13:30:44 +00:00
[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},
|
||||
{"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()
|
||||
|
@ -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_;
|
||||
|
@ -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)) {
|
||||
|
@ -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];
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user