From 8e7301f4d80e97eb03a21a16d0801e12a5c91c48 Mon Sep 17 00:00:00 2001 From: Triang3l Date: Thu, 16 May 2024 23:04:48 +0300 Subject: [PATCH] [SPIR-V] Use a helper class for most if/else branching Simplifies emission of the blocks themselves (including inserting blocks into the function's block list in the correct order), as well as phi after the branching. Also fixes 64bpp storing with blending in the fragment shader interlock render backend implementation (had a typo that caused the high 32 bits to overwrite the low ones). --- src/xenia/gpu/spirv_builder.cc | 102 ++ src/xenia/gpu/spirv_builder.h | 57 + src/xenia/gpu/spirv_shader_translator.cc | 298 +++--- src/xenia/gpu/spirv_shader_translator.h | 2 +- src/xenia/gpu/spirv_shader_translator_alu.cc | 83 +- .../gpu/spirv_shader_translator_fetch.cc | 774 ++++++-------- src/xenia/gpu/spirv_shader_translator_rb.cc | 983 +++++++----------- .../gpu/vulkan/vulkan_render_target_cache.cc | 23 +- 8 files changed, 998 insertions(+), 1324 deletions(-) diff --git a/src/xenia/gpu/spirv_builder.cc b/src/xenia/gpu/spirv_builder.cc index 2ba9446bc..2ed78bd65 100644 --- a/src/xenia/gpu/spirv_builder.cc +++ b/src/xenia/gpu/spirv_builder.cc @@ -13,6 +13,8 @@ #include #include +#include "xenia/base/assert.h" + namespace xe { namespace gpu { @@ -101,5 +103,105 @@ spv::Id SpirvBuilder::createTriBuiltinCall(spv::Id result_type, return result; } +SpirvBuilder::IfBuilder::IfBuilder(spv::Id condition, unsigned int control, + SpirvBuilder& builder, + unsigned int thenWeight, + unsigned int elseWeight) + : builder(builder), + condition(condition), + control(control), + thenWeight(thenWeight), + elseWeight(elseWeight), + function(builder.getBuildPoint()->getParent()) { + // Make the blocks, but only put the then-block into the function, the + // else-block and merge-block will be added later, in order, after earlier + // code is emitted. + thenBlock = new spv::Block(builder.getUniqueId(), function); + elseBlock = nullptr; + mergeBlock = new spv::Block(builder.getUniqueId(), function); + + // Save the current block, so that we can add in the flow control split when + // makeEndIf is called. + headerBlock = builder.getBuildPoint(); + + spv::Id headerBlockId = headerBlock->getId(); + thenPhiParent = headerBlockId; + elsePhiParent = headerBlockId; + + function.addBlock(thenBlock); + builder.setBuildPoint(thenBlock); +} + +void SpirvBuilder::IfBuilder::makeBeginElse(bool branchToMerge) { +#ifndef NDEBUG + assert_true(currentBranch == Branch::kThen); +#endif + + if (branchToMerge) { + // Close out the "then" by having it jump to the mergeBlock. + thenPhiParent = builder.getBuildPoint()->getId(); + builder.createBranch(mergeBlock); + } + + // Make the first else block and add it to the function. + elseBlock = new spv::Block(builder.getUniqueId(), function); + function.addBlock(elseBlock); + + // Start building the else block. + builder.setBuildPoint(elseBlock); + +#ifndef NDEBUG + currentBranch = Branch::kElse; +#endif +} + +void SpirvBuilder::IfBuilder::makeEndIf(bool branchToMerge) { +#ifndef NDEBUG + assert_true(currentBranch == Branch::kThen || currentBranch == Branch::kElse); +#endif + + if (branchToMerge) { + // Jump to the merge block. + (elseBlock ? elsePhiParent : thenPhiParent) = + builder.getBuildPoint()->getId(); + builder.createBranch(mergeBlock); + } + + // Go back to the headerBlock and make the flow control split. + builder.setBuildPoint(headerBlock); + builder.createSelectionMerge(mergeBlock, control); + { + spv::Block* falseBlock = elseBlock ? elseBlock : mergeBlock; + std::unique_ptr branch = + std::make_unique(spv::OpBranchConditional); + branch->addIdOperand(condition); + branch->addIdOperand(thenBlock->getId()); + branch->addIdOperand(falseBlock->getId()); + if (thenWeight || elseWeight) { + branch->addImmediateOperand(thenWeight); + branch->addImmediateOperand(elseWeight); + } + builder.getBuildPoint()->addInstruction(std::move(branch)); + thenBlock->addPredecessor(builder.getBuildPoint()); + falseBlock->addPredecessor(builder.getBuildPoint()); + } + + // Add the merge block to the function. + function.addBlock(mergeBlock); + builder.setBuildPoint(mergeBlock); + +#ifndef NDEBUG + currentBranch = Branch::kMerge; +#endif +} + +spv::Id SpirvBuilder::IfBuilder::createMergePhi(spv::Id then_variable, + spv::Id else_variable) const { + assert_true(builder.getBuildPoint() == mergeBlock); + return builder.createQuadOp(spv::OpPhi, builder.getTypeId(then_variable), + then_variable, getThenPhiParent(), else_variable, + getElsePhiParent()); +} + } // namespace gpu } // namespace xe diff --git a/src/xenia/gpu/spirv_builder.h b/src/xenia/gpu/spirv_builder.h index 0496aa7c4..1bb2e6851 100644 --- a/src/xenia/gpu/spirv_builder.h +++ b/src/xenia/gpu/spirv_builder.h @@ -10,7 +10,10 @@ #ifndef XENIA_GPU_SPIRV_BUILDER_H_ #define XENIA_GPU_SPIRV_BUILDER_H_ +#include + #include "third_party/glslang/SPIRV/SpvBuilder.h" +#include "xenia/base/assert.h" namespace xe { namespace gpu { @@ -42,6 +45,60 @@ class SpirvBuilder : public spv::Builder { spv::Id createTriBuiltinCall(spv::Id result_type, spv::Id builtins, int entry_point, spv::Id operand1, spv::Id operand2, spv::Id operand3); + + // Helper to use for building nested control flow with if-then-else with + // additions over SpvBuilder::If. + class IfBuilder { + public: + IfBuilder(spv::Id condition, unsigned int control, SpirvBuilder& builder, + unsigned int thenWeight = 0, unsigned int elseWeight = 0); + + ~IfBuilder() { +#ifndef NDEBUG + assert_true(currentBranch == Branch::kMerge); +#endif + } + + void makeBeginElse(bool branchToMerge = true); + void makeEndIf(bool branchToMerge = true); + + // If there's no then/else block that branches to the merge block, the phi + // parent is the header block - this simplifies then-only usage. + spv::Id getThenPhiParent() const { return thenPhiParent; } + spv::Id getElsePhiParent() const { return elsePhiParent; } + + spv::Id createMergePhi(spv::Id then_variable, spv::Id else_variable) const; + + private: + enum class Branch { + kThen, + kElse, + kMerge, + }; + + IfBuilder(const IfBuilder& ifBuilder) = delete; + IfBuilder& operator=(const IfBuilder& ifBuilder) = delete; + + SpirvBuilder& builder; + spv::Id condition; + unsigned int control; + unsigned int thenWeight; + unsigned int elseWeight; + + spv::Function& function; + + spv::Block* headerBlock; + spv::Block* thenBlock; + spv::Block* elseBlock; + spv::Block* mergeBlock; + + spv::Id thenPhiParent; + spv::Id elsePhiParent; + +#ifndef NDEBUG + Branch currentBranch = Branch::kThen; +#endif + }; }; } // namespace gpu diff --git a/src/xenia/gpu/spirv_shader_translator.cc b/src/xenia/gpu/spirv_shader_translator.cc index 8bcaa19fd..e34193219 100644 --- a/src/xenia/gpu/spirv_shader_translator.cc +++ b/src/xenia/gpu/spirv_shader_translator.cc @@ -1272,89 +1272,70 @@ void SpirvShaderTranslator::StartVertexOrTessEvalShaderInMain() { builder_->makeUintConstant(static_cast( kSysFlag_ComputeOrPrimitiveVertexIndexLoad))), const_uint_0_); - spv::Block& block_load_vertex_index_pre = *builder_->getBuildPoint(); - spv::Block& block_load_vertex_index_start = builder_->makeNewBlock(); - spv::Block& block_load_vertex_index_merge = builder_->makeNewBlock(); - builder_->createSelectionMerge(&block_load_vertex_index_merge, - spv::SelectionControlDontFlattenMask); - builder_->createConditionalBranch(load_vertex_index, - &block_load_vertex_index_start, - &block_load_vertex_index_merge); - builder_->setBuildPoint(&block_load_vertex_index_start); - // Check if the index is 32-bit. - spv::Id vertex_index_is_32bit = builder_->createBinOp( - spv::OpINotEqual, type_bool_, - builder_->createBinOp( - spv::OpBitwiseAnd, type_uint_, main_system_constant_flags_, - builder_->makeUintConstant(static_cast( - kSysFlag_ComputeOrPrimitiveVertexIndexLoad32Bit))), - const_uint_0_); - // Calculate the vertex index address in the shared memory. - id_vector_temp_.clear(); - id_vector_temp_.push_back( - builder_->makeIntConstant(kSystemConstantVertexIndexLoadAddress)); - spv::Id vertex_index_address = builder_->createBinOp( - spv::OpIAdd, type_uint_, - builder_->createLoad( - builder_->createAccessChain(spv::StorageClassUniform, - uniform_system_constants_, - id_vector_temp_), - spv::NoPrecision), - builder_->createBinOp( - spv::OpShiftLeftLogical, type_uint_, vertex_index, - builder_->createTriOp(spv::OpSelect, type_uint_, - vertex_index_is_32bit, const_uint_2, - builder_->makeUintConstant(1)))); - // Load the 32 bits containing the whole vertex index or two 16-bit - // vertex indices. - // TODO(Triang3l): Bounds checking. - spv::Id loaded_vertex_index = - LoadUint32FromSharedMemory(builder_->createUnaryOp( - spv::OpBitcast, type_int_, - builder_->createBinOp(spv::OpShiftRightLogical, type_uint_, - vertex_index_address, const_uint_2))); - // Extract the 16-bit index from the loaded 32 bits if needed. - loaded_vertex_index = builder_->createTriOp( - spv::OpSelect, type_uint_, vertex_index_is_32bit, - loaded_vertex_index, - builder_->createTriOp( - spv::OpBitFieldUExtract, type_uint_, loaded_vertex_index, - builder_->createBinOp( - spv::OpShiftLeftLogical, type_uint_, - builder_->createBinOp(spv::OpBitwiseAnd, type_uint_, - vertex_index_address, const_uint_2), - builder_->makeUintConstant(4 - 1)), - builder_->makeUintConstant(16))); - // Endian-swap the loaded index. - id_vector_temp_.clear(); - id_vector_temp_.push_back( - builder_->makeIntConstant(kSystemConstantVertexIndexEndian)); - loaded_vertex_index = EndianSwap32Uint( - loaded_vertex_index, - builder_->createLoad( - builder_->createAccessChain(spv::StorageClassUniform, - uniform_system_constants_, - id_vector_temp_), - spv::NoPrecision)); - // Get the actual build point for phi. - spv::Block& block_load_vertex_index_end = *builder_->getBuildPoint(); - builder_->createBranch(&block_load_vertex_index_merge); - // Select between the loaded index and the original index from Vulkan. - builder_->setBuildPoint(&block_load_vertex_index_merge); + SpirvBuilder::IfBuilder load_vertex_index_if( + load_vertex_index, spv::SelectionControlDontFlattenMask, *builder_); + spv::Id loaded_vertex_index; { - std::unique_ptr loaded_vertex_index_phi_op = - std::make_unique(builder_->getUniqueId(), - type_uint_, spv::OpPhi); - loaded_vertex_index_phi_op->addIdOperand(loaded_vertex_index); - loaded_vertex_index_phi_op->addIdOperand( - block_load_vertex_index_end.getId()); - loaded_vertex_index_phi_op->addIdOperand(vertex_index); - loaded_vertex_index_phi_op->addIdOperand( - block_load_vertex_index_pre.getId()); - vertex_index = loaded_vertex_index_phi_op->getResultId(); - builder_->getBuildPoint()->addInstruction( - std::move(loaded_vertex_index_phi_op)); + // Check if the index is 32-bit. + spv::Id vertex_index_is_32bit = builder_->createBinOp( + spv::OpINotEqual, type_bool_, + builder_->createBinOp( + spv::OpBitwiseAnd, type_uint_, main_system_constant_flags_, + builder_->makeUintConstant(static_cast( + kSysFlag_ComputeOrPrimitiveVertexIndexLoad32Bit))), + const_uint_0_); + // Calculate the vertex index address in the shared memory. + id_vector_temp_.clear(); + id_vector_temp_.push_back( + builder_->makeIntConstant(kSystemConstantVertexIndexLoadAddress)); + spv::Id vertex_index_address = builder_->createBinOp( + spv::OpIAdd, type_uint_, + builder_->createLoad( + builder_->createAccessChain(spv::StorageClassUniform, + uniform_system_constants_, + id_vector_temp_), + spv::NoPrecision), + builder_->createBinOp( + spv::OpShiftLeftLogical, type_uint_, vertex_index, + builder_->createTriOp(spv::OpSelect, type_uint_, + vertex_index_is_32bit, const_uint_2, + builder_->makeUintConstant(1)))); + // Load the 32 bits containing the whole vertex index or two 16-bit + // vertex indices. + // TODO(Triang3l): Bounds checking. + loaded_vertex_index = + LoadUint32FromSharedMemory(builder_->createUnaryOp( + spv::OpBitcast, type_int_, + builder_->createBinOp(spv::OpShiftRightLogical, type_uint_, + vertex_index_address, const_uint_2))); + // Extract the 16-bit index from the loaded 32 bits if needed. + loaded_vertex_index = builder_->createTriOp( + spv::OpSelect, type_uint_, vertex_index_is_32bit, + loaded_vertex_index, + builder_->createTriOp( + spv::OpBitFieldUExtract, type_uint_, loaded_vertex_index, + builder_->createBinOp( + spv::OpShiftLeftLogical, type_uint_, + builder_->createBinOp(spv::OpBitwiseAnd, type_uint_, + vertex_index_address, const_uint_2), + builder_->makeUintConstant(4 - 1)), + builder_->makeUintConstant(16))); + // Endian-swap the loaded index. + id_vector_temp_.clear(); + id_vector_temp_.push_back( + builder_->makeIntConstant(kSystemConstantVertexIndexEndian)); + loaded_vertex_index = EndianSwap32Uint( + loaded_vertex_index, + builder_->createLoad( + builder_->createAccessChain(spv::StorageClassUniform, + uniform_system_constants_, + id_vector_temp_), + spv::NoPrecision)); } + load_vertex_index_if.makeEndIf(); + // Select between the loaded index and the original index from Vulkan. + vertex_index = load_vertex_index_if.createMergePhi(loaded_vertex_index, + vertex_index); } else { // TODO(Triang3l): Close line loop primitive. // Load the unswapped index as uint for swapping, or for indirect @@ -1368,53 +1349,35 @@ void SpirvShaderTranslator::StartVertexOrTessEvalShaderInMain() { builder_->makeUintConstant( static_cast(kSysFlag_VertexIndexLoad))), const_uint_0_); - spv::Block& block_load_vertex_index_pre = *builder_->getBuildPoint(); - spv::Block& block_load_vertex_index_start = builder_->makeNewBlock(); - spv::Block& block_load_vertex_index_merge = builder_->makeNewBlock(); - builder_->createSelectionMerge(&block_load_vertex_index_merge, - spv::SelectionControlDontFlattenMask); - builder_->createConditionalBranch(load_vertex_index, - &block_load_vertex_index_start, - &block_load_vertex_index_merge); - builder_->setBuildPoint(&block_load_vertex_index_start); - // Load the 32-bit index. - // TODO(Triang3l): Bounds checking. - id_vector_temp_.clear(); - id_vector_temp_.push_back( - builder_->makeIntConstant(kSystemConstantVertexIndexLoadAddress)); - spv::Id loaded_vertex_index = - LoadUint32FromSharedMemory(builder_->createUnaryOp( - spv::OpBitcast, type_int_, - builder_->createBinOp( - spv::OpIAdd, type_uint_, - builder_->createBinOp( - spv::OpShiftRightLogical, type_uint_, - builder_->createLoad( - builder_->createAccessChain( - spv::StorageClassUniform, - uniform_system_constants_, id_vector_temp_), - spv::NoPrecision), - builder_->makeUintConstant(2)), - vertex_index))); - // Get the actual build point for phi. - spv::Block& block_load_vertex_index_end = *builder_->getBuildPoint(); - builder_->createBranch(&block_load_vertex_index_merge); - // Select between the loaded index and the original index from Vulkan. - builder_->setBuildPoint(&block_load_vertex_index_merge); + SpirvBuilder::IfBuilder load_vertex_index_if( + load_vertex_index, spv::SelectionControlDontFlattenMask, + *builder_); + spv::Id loaded_vertex_index; { - std::unique_ptr loaded_vertex_index_phi_op = - std::make_unique(builder_->getUniqueId(), - type_uint_, spv::OpPhi); - loaded_vertex_index_phi_op->addIdOperand(loaded_vertex_index); - loaded_vertex_index_phi_op->addIdOperand( - block_load_vertex_index_end.getId()); - loaded_vertex_index_phi_op->addIdOperand(vertex_index); - loaded_vertex_index_phi_op->addIdOperand( - block_load_vertex_index_pre.getId()); - vertex_index = loaded_vertex_index_phi_op->getResultId(); - builder_->getBuildPoint()->addInstruction( - std::move(loaded_vertex_index_phi_op)); + // Load the 32-bit index. + // TODO(Triang3l): Bounds checking. + id_vector_temp_.clear(); + id_vector_temp_.push_back(builder_->makeIntConstant( + kSystemConstantVertexIndexLoadAddress)); + loaded_vertex_index = + LoadUint32FromSharedMemory(builder_->createUnaryOp( + spv::OpBitcast, type_int_, + builder_->createBinOp( + spv::OpIAdd, type_uint_, + builder_->createBinOp( + spv::OpShiftRightLogical, type_uint_, + builder_->createLoad( + builder_->createAccessChain( + spv::StorageClassUniform, + uniform_system_constants_, id_vector_temp_), + spv::NoPrecision), + builder_->makeUintConstant(2)), + vertex_index))); } + load_vertex_index_if.makeEndIf(); + // Select between the loaded index and the original index from Vulkan. + vertex_index = load_vertex_index_if.createMergePhi( + loaded_vertex_index, vertex_index); } // Endian-swap the index. id_vector_temp_.clear(); @@ -2808,40 +2771,25 @@ spv::Id SpirvShaderTranslator::EndianSwap32Uint(spv::Id value, spv::Id endian) { static_cast(xenos::Endian::k8in32))); spv::Id is_8in16_or_8in32 = builder_->createBinOp(spv::OpLogicalOr, type_bool_, is_8in16, is_8in32); - spv::Block& block_pre_8in16 = *builder_->getBuildPoint(); - assert_false(block_pre_8in16.isTerminated()); - spv::Block& block_8in16 = builder_->makeNewBlock(); - spv::Block& block_8in16_merge = builder_->makeNewBlock(); - builder_->createSelectionMerge(&block_8in16_merge, - spv::SelectionControlMaskNone); - builder_->createConditionalBranch(is_8in16_or_8in32, &block_8in16, - &block_8in16_merge); - builder_->setBuildPoint(&block_8in16); - spv::Id swapped_8in16 = builder_->createBinOp( - spv::OpBitwiseOr, type, - builder_->createBinOp( - spv::OpBitwiseAnd, type, - builder_->createBinOp(spv::OpShiftRightLogical, type, value, - const_uint_8_typed), - const_uint_00ff00ff_typed), - builder_->createBinOp( - spv::OpShiftLeftLogical, type, - builder_->createBinOp(spv::OpBitwiseAnd, type, value, - const_uint_00ff00ff_typed), - const_uint_8_typed)); - builder_->createBranch(&block_8in16_merge); - builder_->setBuildPoint(&block_8in16_merge); + SpirvBuilder::IfBuilder if_8in16(is_8in16_or_8in32, + spv::SelectionControlMaskNone, *builder_); + spv::Id swapped_8in16; { - std::unique_ptr phi_op = - std::make_unique(builder_->getUniqueId(), type, - spv::OpPhi); - phi_op->addIdOperand(swapped_8in16); - phi_op->addIdOperand(block_8in16.getId()); - phi_op->addIdOperand(value); - phi_op->addIdOperand(block_pre_8in16.getId()); - value = phi_op->getResultId(); - builder_->getBuildPoint()->addInstruction(std::move(phi_op)); + swapped_8in16 = builder_->createBinOp( + spv::OpBitwiseOr, type, + builder_->createBinOp( + spv::OpBitwiseAnd, type, + builder_->createBinOp(spv::OpShiftRightLogical, type, value, + const_uint_8_typed), + const_uint_00ff00ff_typed), + builder_->createBinOp( + spv::OpShiftLeftLogical, type, + builder_->createBinOp(spv::OpBitwiseAnd, type, value, + const_uint_00ff00ff_typed), + const_uint_8_typed)); } + if_8in16.makeEndIf(); + value = if_8in16.createMergePhi(swapped_8in16, value); // 16-in-32 or another half of 8-in-32 (doing 16-in-32 swap). spv::Id is_16in32 = builder_->createBinOp( @@ -2850,32 +2798,18 @@ spv::Id SpirvShaderTranslator::EndianSwap32Uint(spv::Id value, spv::Id endian) { static_cast(xenos::Endian::k16in32))); spv::Id is_8in32_or_16in32 = builder_->createBinOp(spv::OpLogicalOr, type_bool_, is_8in32, is_16in32); - spv::Block& block_pre_16in32 = *builder_->getBuildPoint(); - spv::Block& block_16in32 = builder_->makeNewBlock(); - spv::Block& block_16in32_merge = builder_->makeNewBlock(); - builder_->createSelectionMerge(&block_16in32_merge, - spv::SelectionControlMaskNone); - builder_->createConditionalBranch(is_8in32_or_16in32, &block_16in32, - &block_16in32_merge); - builder_->setBuildPoint(&block_16in32); - spv::Id swapped_16in32 = builder_->createQuadOp( - spv::OpBitFieldInsert, type, - builder_->createBinOp(spv::OpShiftRightLogical, type, value, - const_uint_16_typed), - value, builder_->makeIntConstant(16), builder_->makeIntConstant(16)); - builder_->createBranch(&block_16in32_merge); - builder_->setBuildPoint(&block_16in32_merge); + SpirvBuilder::IfBuilder if_16in32(is_8in32_or_16in32, + spv::SelectionControlMaskNone, *builder_); + spv::Id swapped_16in32; { - std::unique_ptr phi_op = - std::make_unique(builder_->getUniqueId(), type, - spv::OpPhi); - phi_op->addIdOperand(swapped_16in32); - phi_op->addIdOperand(block_16in32.getId()); - phi_op->addIdOperand(value); - phi_op->addIdOperand(block_pre_16in32.getId()); - value = phi_op->getResultId(); - builder_->getBuildPoint()->addInstruction(std::move(phi_op)); + swapped_16in32 = builder_->createQuadOp( + spv::OpBitFieldInsert, type, + builder_->createBinOp(spv::OpShiftRightLogical, type, value, + const_uint_16_typed), + value, builder_->makeIntConstant(16), builder_->makeIntConstant(16)); } + if_16in32.makeEndIf(); + value = if_16in32.createMergePhi(swapped_16in32, value); return value; } diff --git a/src/xenia/gpu/spirv_shader_translator.h b/src/xenia/gpu/spirv_shader_translator.h index 0ed368ae4..8c4942156 100644 --- a/src/xenia/gpu/spirv_shader_translator.h +++ b/src/xenia/gpu/spirv_shader_translator.h @@ -605,7 +605,7 @@ class SpirvShaderTranslator : public ShaderTranslator { void SampleTexture(spv::Builder::TextureParameters& texture_parameters, spv::ImageOperandsMask image_operands_mask, spv::Id image_unsigned, spv::Id image_signed, - spv::Id sampler, spv::Id is_all_signed, + spv::Id sampler, spv::Id is_any_unsigned, spv::Id is_any_signed, spv::Id& result_unsigned_out, spv::Id& result_signed_out, spv::Id lerp_factor = spv::NoResult, diff --git a/src/xenia/gpu/spirv_shader_translator_alu.cc b/src/xenia/gpu/spirv_shader_translator_alu.cc index 05e41d5ab..ecc88f57b 100644 --- a/src/xenia/gpu/spirv_shader_translator_alu.cc +++ b/src/xenia/gpu/spirv_shader_translator_alu.cc @@ -40,30 +40,18 @@ spv::Id SpirvShaderTranslator::ZeroIfAnyOperandIsZero(spv::Id value, } void SpirvShaderTranslator::KillPixel(spv::Id condition) { - // Same calls as in spv::Builder::If. - spv::Function& function = builder_->getBuildPoint()->getParent(); - spv::Block* kill_block = new spv::Block(builder_->getUniqueId(), function); - spv::Block* merge_block = new spv::Block(builder_->getUniqueId(), function); - spv::Block& header_block = *builder_->getBuildPoint(); - - function.addBlock(kill_block); - builder_->setBuildPoint(kill_block); - // Kill without influencing the control flow in the translated shader. - if (var_main_kill_pixel_ != spv::NoResult) { - builder_->createStore(builder_->makeBoolConstant(true), - var_main_kill_pixel_); + SpirvBuilder::IfBuilder kill_if(condition, spv::SelectionControlMaskNone, + *builder_); + { + if (var_main_kill_pixel_ != spv::NoResult) { + builder_->createStore(builder_->makeBoolConstant(true), + var_main_kill_pixel_); + } + if (features_.demote_to_helper_invocation) { + builder_->createNoResultOp(spv::OpDemoteToHelperInvocationEXT); + } } - if (features_.demote_to_helper_invocation) { - builder_->createNoResultOp(spv::OpDemoteToHelperInvocationEXT); - } - builder_->createBranch(merge_block); - - builder_->setBuildPoint(&header_block); - builder_->createSelectionMerge(merge_block, spv::SelectionControlMaskNone); - builder_->createConditionalBranch(condition, kill_block, merge_block); - - function.addBlock(merge_block); - builder_->setBuildPoint(merge_block); + kill_if.makeEndIf(); } void SpirvShaderTranslator::ProcessAluInstruction( @@ -564,7 +552,7 @@ spv::Id SpirvShaderTranslator::ProcessVectorAluOperation( spv::Id ma_z_result[4] = {}, ma_yx_result[4] = {}; // Check if the major axis is Z (abs(z) >= abs(x) && abs(z) >= abs(y)). - spv::Builder::If ma_z_if( + SpirvBuilder::IfBuilder ma_z_if( builder_->createBinOp( spv::OpLogicalAnd, type_bool_, builder_->createBinOp(spv::OpFOrdGreaterThanEqual, type_bool_, @@ -596,14 +584,13 @@ spv::Id SpirvShaderTranslator::ProcessVectorAluOperation( } } } - spv::Block& ma_z_end_block = *builder_->getBuildPoint(); ma_z_if.makeBeginElse(); { spv::Id ma_y_result[4] = {}, ma_x_result[4] = {}; // The major axis is not Z - create an inner conditional to check if the // major axis is Y (abs(y) >= abs(x)). - spv::Builder::If ma_y_if( + SpirvBuilder::IfBuilder ma_y_if( builder_->createBinOp(spv::OpFOrdGreaterThanEqual, type_bool_, operand_abs[1], operand_abs[0]), spv::SelectionControlMaskNone, *builder_); @@ -629,7 +616,6 @@ spv::Id SpirvShaderTranslator::ProcessVectorAluOperation( } } } - spv::Block& ma_y_end_block = *builder_->getBuildPoint(); ma_y_if.makeBeginElse(); { // The major axis is X. @@ -654,7 +640,6 @@ spv::Id SpirvShaderTranslator::ProcessVectorAluOperation( } } } - spv::Block& ma_x_end_block = *builder_->getBuildPoint(); ma_y_if.makeEndIf(); // The major axis is Y or X - choose the options of the result from Y @@ -663,18 +648,10 @@ spv::Id SpirvShaderTranslator::ProcessVectorAluOperation( if (!(used_result_components & (1 << i))) { continue; } - std::unique_ptr phi_op = - std::make_unique(builder_->getUniqueId(), - type_float_, spv::OpPhi); - phi_op->addIdOperand(ma_y_result[i]); - phi_op->addIdOperand(ma_y_end_block.getId()); - phi_op->addIdOperand(ma_x_result[i]); - phi_op->addIdOperand(ma_x_end_block.getId()); - ma_yx_result[i] = phi_op->getResultId(); - builder_->getBuildPoint()->addInstruction(std::move(phi_op)); + ma_yx_result[i] = + ma_y_if.createMergePhi(ma_y_result[i], ma_x_result[i]); } } - spv::Block& ma_yx_end_block = *builder_->getBuildPoint(); ma_z_if.makeEndIf(); // Choose the result options from Z and YX cases. @@ -683,15 +660,8 @@ spv::Id SpirvShaderTranslator::ProcessVectorAluOperation( if (!(used_result_components & (1 << i))) { continue; } - std::unique_ptr phi_op = - std::make_unique(builder_->getUniqueId(), - type_float_, spv::OpPhi); - phi_op->addIdOperand(ma_z_result[i]); - phi_op->addIdOperand(ma_z_end_block.getId()); - phi_op->addIdOperand(ma_yx_result[i]); - phi_op->addIdOperand(ma_yx_end_block.getId()); - id_vector_temp_.push_back(phi_op->getResultId()); - builder_->getBuildPoint()->addInstruction(std::move(phi_op)); + id_vector_temp_.push_back( + ma_z_if.createMergePhi(ma_z_result[i], ma_yx_result[i])); } assert_true(id_vector_temp_.size() == used_result_component_count); if (used_result_components & 0b0100) { @@ -1044,10 +1014,9 @@ spv::Id SpirvShaderTranslator::ProcessScalarAluOperation( spv::OpLogicalAnd, type_bool_, condition, builder_->createBinOp(spv::OpFOrdGreaterThan, type_bool_, b, const_float_0_)); - spv::Block& pre_multiply_if_block = *builder_->getBuildPoint(); + SpirvBuilder::IfBuilder multiply_if( + condition, spv::SelectionControlMaskNone, *builder_); spv::Id product; - spv::Builder::If multiply_if(condition, spv::SelectionControlMaskNone, - *builder_); { // Multiplication case. spv::Id a = instr.scalar_operands[0].GetComponent(0) != @@ -1061,21 +1030,9 @@ spv::Id SpirvShaderTranslator::ProcessScalarAluOperation( product = ZeroIfAnyOperandIsZero( product, GetAbsoluteOperand(a, instr.scalar_operands[0]), ps_abs); } - spv::Block& multiply_end_block = *builder_->getBuildPoint(); multiply_if.makeEndIf(); // Merge - choose between the product and -FLT_MAX. - { - std::unique_ptr phi_op = - std::make_unique(builder_->getUniqueId(), - type_float_, spv::OpPhi); - phi_op->addIdOperand(product); - phi_op->addIdOperand(multiply_end_block.getId()); - phi_op->addIdOperand(const_float_max_neg); - phi_op->addIdOperand(pre_multiply_if_block.getId()); - spv::Id phi_result = phi_op->getResultId(); - builder_->getBuildPoint()->addInstruction(std::move(phi_op)); - return phi_result; - } + return multiply_if.createMergePhi(product, const_float_max_neg); } case ucode::AluScalarOpcode::kMaxs: diff --git a/src/xenia/gpu/spirv_shader_translator_fetch.cc b/src/xenia/gpu/spirv_shader_translator_fetch.cc index 265082ba1..8f5a74690 100644 --- a/src/xenia/gpu/spirv_shader_translator_fetch.cc +++ b/src/xenia/gpu/spirv_shader_translator_fetch.cc @@ -1145,31 +1145,18 @@ void SpirvShaderTranslator::ProcessTextureFetchInstruction( z_coordinate_ref = builder_->createNoContractionBinOp( spv::OpFAdd, type_float_, z_coordinate_ref, z_offset); } - spv::Block& block_dimension_head = *builder_->getBuildPoint(); - spv::Block& block_dimension_merge = builder_->makeNewBlock(); - spv::Block& block_dimension_3d = builder_->makeNewBlock(); - builder_->createSelectionMerge(&block_dimension_merge, - spv::SelectionControlDontFlattenMask); assert_true(data_is_3d != spv::NoResult); - builder_->createConditionalBranch(data_is_3d, &block_dimension_3d, - &block_dimension_merge); - builder_->setBuildPoint(&block_dimension_3d); - assert_true(z_size != spv::NoResult); - spv::Id z_3d = builder_->createNoContractionBinOp( - spv::OpFDiv, type_float_, z_coordinate_ref, z_size); - builder_->createBranch(&block_dimension_merge); - builder_->setBuildPoint(&block_dimension_merge); + SpirvBuilder::IfBuilder if_data_is_3d( + data_is_3d, spv::SelectionControlDontFlattenMask, *builder_); + spv::Id z_3d; { - std::unique_ptr z_phi_op = - std::make_unique(builder_->getUniqueId(), - type_float_, spv::OpPhi); - z_phi_op->addIdOperand(z_3d); - z_phi_op->addIdOperand(block_dimension_3d.getId()); - z_phi_op->addIdOperand(z_coordinate_ref); - z_phi_op->addIdOperand(block_dimension_head.getId()); - z_coordinate_ref = z_phi_op->getResultId(); - builder_->getBuildPoint()->addInstruction(std::move(z_phi_op)); + assert_true(z_size != spv::NoResult); + z_3d = builder_->createNoContractionBinOp(spv::OpFDiv, type_float_, + z_coordinate_ref, z_size); } + if_data_is_3d.makeEndIf(); + z_coordinate_ref = + if_data_is_3d.createMergePhi(z_3d, z_coordinate_ref); } else { // Denormalize the Z coordinate for a stacked texture, and apply the // offset. @@ -1394,63 +1381,39 @@ void SpirvShaderTranslator::ProcessTextureFetchInstruction( // OpSampledImage must be in the same block as where its result is used. if (instr.dimension == xenos::FetchOpDimension::k3DOrStacked) { // Check if the texture is 3D or stacked. - spv::Block& block_dimension_head = *builder_->getBuildPoint(); - spv::Block& block_dimension_3d_start = builder_->makeNewBlock(); - spv::Block& block_dimension_stacked_start = builder_->makeNewBlock(); - spv::Block& block_dimension_merge = builder_->makeNewBlock(); - builder_->createSelectionMerge(&block_dimension_merge, - spv::SelectionControlDontFlattenMask); assert_true(data_is_3d != spv::NoResult); - builder_->createConditionalBranch(data_is_3d, - &block_dimension_3d_start, - &block_dimension_stacked_start); - - // 3D. - builder_->setBuildPoint(&block_dimension_3d_start); - id_vector_temp_.clear(); - for (uint32_t i = 0; i < 3; ++i) { - id_vector_temp_.push_back(coordinates[i]); - } - texture_parameters.coords = - builder_->createCompositeConstruct(type_float3_, id_vector_temp_); - spv::Id lod_3d = QueryTextureLod(texture_parameters, - image_3d_unsigned, image_3d_signed, - sampler, swizzled_signs_all_signed); - // Get the actual build point for phi. - spv::Block& block_dimension_3d_end = *builder_->getBuildPoint(); - builder_->createBranch(&block_dimension_merge); - - // 2D stacked. - builder_->setBuildPoint(&block_dimension_stacked_start); - id_vector_temp_.clear(); - for (uint32_t i = 0; i < 2; ++i) { - id_vector_temp_.push_back(coordinates[i]); - } - texture_parameters.coords = - builder_->createCompositeConstruct(type_float2_, id_vector_temp_); - spv::Id lod_stacked = QueryTextureLod( - texture_parameters, image_2d_array_or_cube_unsigned, - image_2d_array_or_cube_signed, sampler, - swizzled_signs_all_signed); - // Get the actual build point for phi. - spv::Block& block_dimension_stacked_end = *builder_->getBuildPoint(); - builder_->createBranch(&block_dimension_merge); - - // Choose between the 3D and the stacked result based on the actual - // data dimensionality. - builder_->setBuildPoint(&block_dimension_merge); + SpirvBuilder::IfBuilder if_data_is_3d( + data_is_3d, spv::SelectionControlDontFlattenMask, *builder_); + spv::Id lod_3d; { - std::unique_ptr dimension_phi_op = - std::make_unique(builder_->getUniqueId(), - type_float_, spv::OpPhi); - dimension_phi_op->addIdOperand(lod_3d); - dimension_phi_op->addIdOperand(block_dimension_3d_end.getId()); - dimension_phi_op->addIdOperand(lod_stacked); - dimension_phi_op->addIdOperand(block_dimension_stacked_end.getId()); - result[0] = dimension_phi_op->getResultId(); - builder_->getBuildPoint()->addInstruction( - std::move(dimension_phi_op)); + // 3D. + id_vector_temp_.clear(); + for (uint32_t i = 0; i < 3; ++i) { + id_vector_temp_.push_back(coordinates[i]); + } + texture_parameters.coords = builder_->createCompositeConstruct( + type_float3_, id_vector_temp_); + lod_3d = QueryTextureLod(texture_parameters, image_3d_unsigned, + image_3d_signed, sampler, + swizzled_signs_all_signed); } + if_data_is_3d.makeBeginElse(); + spv::Id lod_stacked; + { + // 2D stacked. + id_vector_temp_.clear(); + for (uint32_t i = 0; i < 2; ++i) { + id_vector_temp_.push_back(coordinates[i]); + } + texture_parameters.coords = builder_->createCompositeConstruct( + type_float2_, id_vector_temp_); + lod_stacked = QueryTextureLod(texture_parameters, + image_2d_array_or_cube_unsigned, + image_2d_array_or_cube_signed, + sampler, swizzled_signs_all_signed); + } + if_data_is_3d.makeEndIf(); + result[0] = if_data_is_3d.createMergePhi(lod_3d, lod_stacked); } else { uint32_t lod_query_coordinate_component_count = instr.dimension == xenos::FetchOpDimension::kCube ? 3 : 2; @@ -1512,6 +1475,8 @@ void SpirvShaderTranslator::ProcessTextureFetchInstruction( } } } + spv::Id is_any_unsigned = builder_->createUnaryOp( + spv::OpLogicalNot, type_bool_, is_all_signed); // Load the fetch constant word 4, needed unconditionally for LOD // biasing, for result exponent biasing, and conditionally for stacked @@ -1765,273 +1730,247 @@ void SpirvShaderTranslator::ProcessTextureFetchInstruction( // component, 2 gradient components, two fetches if the Z axis is // linear-filtered). - spv::Block& block_dimension_head = *builder_->getBuildPoint(); - spv::Block& block_dimension_3d_start = builder_->makeNewBlock(); - spv::Block& block_dimension_stacked_start = builder_->makeNewBlock(); - spv::Block& block_dimension_merge = builder_->makeNewBlock(); - builder_->createSelectionMerge(&block_dimension_merge, - spv::SelectionControlDontFlattenMask); assert_true(data_is_3d != spv::NoResult); - builder_->createConditionalBranch(data_is_3d, - &block_dimension_3d_start, - &block_dimension_stacked_start); - - // 3D. - builder_->setBuildPoint(&block_dimension_3d_start); - if (use_computed_lod) { - texture_parameters.gradX = gradients_h; - texture_parameters.gradY = gradients_v; - } - id_vector_temp_.clear(); - for (uint32_t i = 0; i < 3; ++i) { - id_vector_temp_.push_back(coordinates[i]); - } - texture_parameters.coords = - builder_->createCompositeConstruct(type_float3_, id_vector_temp_); + SpirvBuilder::IfBuilder if_data_is_3d( + data_is_3d, spv::SelectionControlDontFlattenMask, *builder_); spv::Id sample_result_unsigned_3d, sample_result_signed_3d; - SampleTexture(texture_parameters, image_operands_mask, - image_3d_unsigned, image_3d_signed, sampler, - is_all_signed, is_any_signed, sample_result_unsigned_3d, - sample_result_signed_3d); - // Get the actual build point after the SampleTexture call for phi. - spv::Block& block_dimension_3d_end = *builder_->getBuildPoint(); - builder_->createBranch(&block_dimension_merge); - - // 2D stacked. - builder_->setBuildPoint(&block_dimension_stacked_start); - if (use_computed_lod) { - // Extract 2D gradients for stacked textures which are 2D arrays. - uint_vector_temp_.clear(); - uint_vector_temp_.push_back(0); - uint_vector_temp_.push_back(1); - texture_parameters.gradX = builder_->createRvalueSwizzle( - spv::NoPrecision, type_float2_, gradients_h, uint_vector_temp_); - texture_parameters.gradY = builder_->createRvalueSwizzle( - spv::NoPrecision, type_float2_, gradients_v, uint_vector_temp_); - } - // Check if linear filtering is needed. - bool vol_mag_filter_is_fetch_const = - instr.attributes.vol_mag_filter == - xenos::TextureFilter::kUseFetchConst; - bool vol_min_filter_is_fetch_const = - instr.attributes.vol_min_filter == - xenos::TextureFilter::kUseFetchConst; - bool vol_mag_filter_is_linear = - instr.attributes.vol_mag_filter == xenos::TextureFilter::kLinear; - bool vol_min_filter_is_linear = - instr.attributes.vol_min_filter == xenos::TextureFilter::kLinear; - spv::Id vol_filter_is_linear = spv::NoResult; - if (use_computed_lod && - (vol_mag_filter_is_fetch_const || vol_min_filter_is_fetch_const || - vol_mag_filter_is_linear != vol_min_filter_is_linear)) { - // Check if minifying along layers (derivative > 1 along any axis). - spv::Id layer_max_gradient = builder_->createBinBuiltinCall( - type_float_, ext_inst_glsl_std_450_, GLSLstd450NMax, - builder_->createCompositeExtract(gradients_h, type_float_, 2), - builder_->createCompositeExtract(gradients_v, type_float_, 2)); - if (!instr.attributes.unnormalized_coordinates) { - // Denormalize the gradient if provided as normalized. - assert_true(size[2] != spv::NoResult); - layer_max_gradient = builder_->createNoContractionBinOp( - spv::OpFMul, type_float_, layer_max_gradient, size[2]); + { + // 3D. + if (use_computed_lod) { + texture_parameters.gradX = gradients_h; + texture_parameters.gradY = gradients_v; } - // For NaN, considering that magnification is being done. - spv::Id is_minifying_z = builder_->createBinOp( - spv::OpFOrdLessThan, type_bool_, layer_max_gradient, - builder_->makeFloatConstant(1.0f)); - // Choose what filter is actually used, the minification or the - // magnification one. - spv::Id vol_mag_filter_is_linear_loaded = - vol_mag_filter_is_fetch_const - ? builder_->createBinOp( - spv::OpINotEqual, type_bool_, - builder_->createBinOp( - spv::OpBitwiseAnd, type_uint_, - fetch_constant_word_4, - builder_->makeUintConstant(UINT32_C(1) << 0)), - const_uint_0_) - : builder_->makeBoolConstant(vol_mag_filter_is_linear); - spv::Id vol_min_filter_is_linear_loaded = - vol_min_filter_is_fetch_const - ? builder_->createBinOp( - spv::OpINotEqual, type_bool_, - builder_->createBinOp( - spv::OpBitwiseAnd, type_uint_, - fetch_constant_word_4, - builder_->makeUintConstant(UINT32_C(1) << 1)), - const_uint_0_) - : builder_->makeBoolConstant(vol_min_filter_is_linear); - vol_filter_is_linear = - builder_->createTriOp(spv::OpSelect, type_bool_, is_minifying_z, - vol_min_filter_is_linear_loaded, - vol_mag_filter_is_linear_loaded); - } else { - // No gradients, or using the same filter overrides for magnifying - // and minifying. Assume always magnifying if no gradients (LOD 0, - // always <= 0). LOD is within 2D layers, not between them (unlike - // in 3D textures, which have mips with depth reduced), so it - // shouldn't have effect on filtering between layers. - if (vol_mag_filter_is_fetch_const) { - vol_filter_is_linear = builder_->createBinOp( - spv::OpINotEqual, type_bool_, - builder_->createBinOp( - spv::OpBitwiseAnd, type_uint_, fetch_constant_word_4, - builder_->makeUintConstant(UINT32_C(1) << 0)), - const_uint_0_); + id_vector_temp_.clear(); + for (uint32_t i = 0; i < 3; ++i) { + id_vector_temp_.push_back(coordinates[i]); } + texture_parameters.coords = builder_->createCompositeConstruct( + type_float3_, id_vector_temp_); + SampleTexture(texture_parameters, image_operands_mask, + image_3d_unsigned, image_3d_signed, sampler, + is_any_unsigned, is_any_signed, + sample_result_unsigned_3d, sample_result_signed_3d); } - spv::Id layer_coordinate = coordinates[2]; - // Linear filtering may be needed either based on a dynamic condition - // (the filtering mode is taken from the fetch constant, or it's - // different for magnification and minification), or on a static one - // (with gradients - specified in the instruction for both - // magnification and minification as linear, without gradients - - // specified for magnification as linear). - // If the filter is linear, subtract 0.5 from the Z coordinate of the - // first layer in filtering because 0.5 is in the middle of it. - if (vol_filter_is_linear != spv::NoResult) { - layer_coordinate = builder_->createTriOp( - spv::OpSelect, type_float_, vol_filter_is_linear, - builder_->createNoContractionBinOp( - spv::OpFSub, type_float_, layer_coordinate, - builder_->makeFloatConstant(0.5f)), - layer_coordinate); - } else if (vol_mag_filter_is_linear) { - layer_coordinate = builder_->createNoContractionBinOp( - spv::OpFSub, type_float_, layer_coordinate, - builder_->makeFloatConstant(0.5f)); - } - // Sample the first layer, needed regardless of whether filtering is - // needed. - // Floor the array layer (Vulkan does rounding to nearest or + 0.5 and - // floor even for the layer index, but on the Xenos, addressing is - // similar to that of 3D textures). This is needed for both point and - // linear filtering (with linear, 0.5 was subtracted previously). - spv::Id layer_0_coordinate = builder_->createUnaryBuiltinCall( - type_float_, ext_inst_glsl_std_450_, GLSLstd450Floor, - layer_coordinate); - id_vector_temp_.clear(); - id_vector_temp_.push_back(coordinates[0]); - id_vector_temp_.push_back(coordinates[1]); - id_vector_temp_.push_back(layer_0_coordinate); - texture_parameters.coords = - builder_->createCompositeConstruct(type_float3_, id_vector_temp_); + if_data_is_3d.makeBeginElse(); spv::Id sample_result_unsigned_stacked, sample_result_signed_stacked; - SampleTexture(texture_parameters, image_operands_mask, - image_2d_array_or_cube_unsigned, - image_2d_array_or_cube_signed, sampler, is_all_signed, - is_any_signed, sample_result_unsigned_stacked, - sample_result_signed_stacked); - // Sample the second layer if linear filtering is potentially needed - // (conditionally or unconditionally, depending on whether the filter - // needs to be chosen at runtime), and filter. - if (vol_filter_is_linear != spv::NoResult || - vol_mag_filter_is_linear) { - spv::Block& block_z_head = *builder_->getBuildPoint(); - spv::Block& block_z_linear = (vol_filter_is_linear != spv::NoResult) - ? builder_->makeNewBlock() - : block_z_head; - spv::Block& block_z_merge = (vol_filter_is_linear != spv::NoResult) - ? builder_->makeNewBlock() - : block_z_head; - if (vol_filter_is_linear != spv::NoResult) { - builder_->createSelectionMerge( - &block_z_merge, spv::SelectionControlDontFlattenMask); - builder_->createConditionalBranch( - vol_filter_is_linear, &block_z_linear, &block_z_merge); - builder_->setBuildPoint(&block_z_linear); + { + // 2D stacked. + if (use_computed_lod) { + // Extract 2D gradients for stacked textures which are 2D arrays. + uint_vector_temp_.clear(); + uint_vector_temp_.push_back(0); + uint_vector_temp_.push_back(1); + texture_parameters.gradX = + builder_->createRvalueSwizzle(spv::NoPrecision, type_float2_, + gradients_h, uint_vector_temp_); + texture_parameters.gradY = + builder_->createRvalueSwizzle(spv::NoPrecision, type_float2_, + gradients_v, uint_vector_temp_); } - spv::Id layer_1_coordinate = builder_->createBinOp( - spv::OpFAdd, type_float_, layer_0_coordinate, - builder_->makeFloatConstant(1.0f)); + // Check if linear filtering is needed. + bool vol_mag_filter_is_fetch_const = + instr.attributes.vol_mag_filter == + xenos::TextureFilter::kUseFetchConst; + bool vol_min_filter_is_fetch_const = + instr.attributes.vol_min_filter == + xenos::TextureFilter::kUseFetchConst; + bool vol_mag_filter_is_linear = instr.attributes.vol_mag_filter == + xenos::TextureFilter::kLinear; + bool vol_min_filter_is_linear = instr.attributes.vol_min_filter == + xenos::TextureFilter::kLinear; + spv::Id vol_filter_is_linear = spv::NoResult; + if (use_computed_lod && + (vol_mag_filter_is_fetch_const || + vol_min_filter_is_fetch_const || + vol_mag_filter_is_linear != vol_min_filter_is_linear)) { + // Check if minifying along layers (derivative > 1 along any + // axis). + spv::Id layer_max_gradient = builder_->createBinBuiltinCall( + type_float_, ext_inst_glsl_std_450_, GLSLstd450NMax, + builder_->createCompositeExtract(gradients_h, type_float_, 2), + builder_->createCompositeExtract(gradients_v, type_float_, + 2)); + if (!instr.attributes.unnormalized_coordinates) { + // Denormalize the gradient if provided as normalized. + assert_true(size[2] != spv::NoResult); + layer_max_gradient = builder_->createNoContractionBinOp( + spv::OpFMul, type_float_, layer_max_gradient, size[2]); + } + // For NaN, considering that magnification is being done. + spv::Id is_minifying_z = builder_->createBinOp( + spv::OpFOrdLessThan, type_bool_, layer_max_gradient, + builder_->makeFloatConstant(1.0f)); + // Choose what filter is actually used, the minification or the + // magnification one. + spv::Id vol_mag_filter_is_linear_loaded = + vol_mag_filter_is_fetch_const + ? builder_->createBinOp( + spv::OpINotEqual, type_bool_, + builder_->createBinOp( + spv::OpBitwiseAnd, type_uint_, + fetch_constant_word_4, + builder_->makeUintConstant(UINT32_C(1) << 0)), + const_uint_0_) + : builder_->makeBoolConstant(vol_mag_filter_is_linear); + spv::Id vol_min_filter_is_linear_loaded = + vol_min_filter_is_fetch_const + ? builder_->createBinOp( + spv::OpINotEqual, type_bool_, + builder_->createBinOp( + spv::OpBitwiseAnd, type_uint_, + fetch_constant_word_4, + builder_->makeUintConstant(UINT32_C(1) << 1)), + const_uint_0_) + : builder_->makeBoolConstant(vol_min_filter_is_linear); + vol_filter_is_linear = builder_->createTriOp( + spv::OpSelect, type_bool_, is_minifying_z, + vol_min_filter_is_linear_loaded, + vol_mag_filter_is_linear_loaded); + } else { + // No gradients, or using the same filter overrides for magnifying + // and minifying. Assume always magnifying if no gradients (LOD 0, + // always <= 0). LOD is within 2D layers, not between them (unlike + // in 3D textures, which have mips with depth reduced), so it + // shouldn't have effect on filtering between layers. + if (vol_mag_filter_is_fetch_const) { + vol_filter_is_linear = builder_->createBinOp( + spv::OpINotEqual, type_bool_, + builder_->createBinOp( + spv::OpBitwiseAnd, type_uint_, fetch_constant_word_4, + builder_->makeUintConstant(UINT32_C(1) << 0)), + const_uint_0_); + } + } + spv::Id layer_coordinate = coordinates[2]; + // Linear filtering may be needed either based on a dynamic + // condition (the filtering mode is taken from the fetch constant, + // or it's different for magnification and minification), or on a + // static one (with gradients - specified in the instruction for + // both magnification and minification as linear, without + // gradients - specified for magnification as linear). + // If the filter is linear, subtract 0.5 from the Z coordinate of + // the first layer in filtering because 0.5 is in the middle of it. + if (vol_filter_is_linear != spv::NoResult) { + layer_coordinate = builder_->createTriOp( + spv::OpSelect, type_float_, vol_filter_is_linear, + builder_->createNoContractionBinOp( + spv::OpFSub, type_float_, layer_coordinate, + builder_->makeFloatConstant(0.5f)), + layer_coordinate); + } else if (vol_mag_filter_is_linear) { + layer_coordinate = builder_->createNoContractionBinOp( + spv::OpFSub, type_float_, layer_coordinate, + builder_->makeFloatConstant(0.5f)); + } + // Sample the first layer, needed regardless of whether filtering is + // needed. + // Floor the array layer (Vulkan does rounding to nearest or + 0.5 + // and floor even for the layer index, but on the Xenos, addressing + // is similar to that of 3D textures). This is needed for both point + // and linear filtering (with linear, 0.5 was subtracted + // previously). + spv::Id layer_0_coordinate = builder_->createUnaryBuiltinCall( + type_float_, ext_inst_glsl_std_450_, GLSLstd450Floor, + layer_coordinate); id_vector_temp_.clear(); id_vector_temp_.push_back(coordinates[0]); id_vector_temp_.push_back(coordinates[1]); - id_vector_temp_.push_back(layer_1_coordinate); + id_vector_temp_.push_back(layer_0_coordinate); texture_parameters.coords = builder_->createCompositeConstruct( type_float3_, id_vector_temp_); - spv::Id layer_lerp_factor = builder_->createUnaryBuiltinCall( - type_float_, ext_inst_glsl_std_450_, GLSLstd450Fract, - layer_coordinate); - spv::Id sample_result_unsigned_stacked_filtered; - spv::Id sample_result_signed_stacked_filtered; SampleTexture( texture_parameters, image_operands_mask, image_2d_array_or_cube_unsigned, image_2d_array_or_cube_signed, - sampler, is_all_signed, is_any_signed, - sample_result_unsigned_stacked_filtered, - sample_result_signed_stacked_filtered, layer_lerp_factor, + sampler, is_any_unsigned, is_any_signed, sample_result_unsigned_stacked, sample_result_signed_stacked); - if (vol_filter_is_linear != spv::NoResult) { - // Get the actual build point after the SampleTexture call for - // phi. - spv::Block& block_z_linear_end = *builder_->getBuildPoint(); - builder_->createBranch(&block_z_merge); - builder_->setBuildPoint(&block_z_merge); - { - std::unique_ptr filter_phi_op = - std::make_unique( - builder_->getUniqueId(), type_float4_, spv::OpPhi); - filter_phi_op->addIdOperand( - sample_result_unsigned_stacked_filtered); - filter_phi_op->addIdOperand(block_z_linear_end.getId()); - filter_phi_op->addIdOperand(sample_result_unsigned_stacked); - filter_phi_op->addIdOperand(block_z_head.getId()); - sample_result_unsigned_stacked = filter_phi_op->getResultId(); - builder_->getBuildPoint()->addInstruction( - std::move(filter_phi_op)); + // Sample the second layer if linear filtering is potentially needed + // (conditionally or unconditionally, depending on whether the + // filter needs to be chosen at runtime), and filter. + if (vol_filter_is_linear != spv::NoResult || + vol_mag_filter_is_linear) { + spv::Block& block_z_head = *builder_->getBuildPoint(); + spv::Block& block_z_linear = + (vol_filter_is_linear != spv::NoResult) + ? builder_->makeNewBlock() + : block_z_head; + spv::Block& block_z_merge = + (vol_filter_is_linear != spv::NoResult) + ? builder_->makeNewBlock() + : block_z_head; + if (vol_filter_is_linear != spv::NoResult) { + builder_->createSelectionMerge( + &block_z_merge, spv::SelectionControlDontFlattenMask); + builder_->createConditionalBranch( + vol_filter_is_linear, &block_z_linear, &block_z_merge); + builder_->setBuildPoint(&block_z_linear); } - { - std::unique_ptr filter_phi_op = - std::make_unique( - builder_->getUniqueId(), type_float4_, spv::OpPhi); - filter_phi_op->addIdOperand( - sample_result_signed_stacked_filtered); - filter_phi_op->addIdOperand(block_z_linear_end.getId()); - filter_phi_op->addIdOperand(sample_result_signed_stacked); - filter_phi_op->addIdOperand(block_z_head.getId()); - sample_result_signed_stacked = filter_phi_op->getResultId(); - builder_->getBuildPoint()->addInstruction( - std::move(filter_phi_op)); + spv::Id layer_1_coordinate = builder_->createBinOp( + spv::OpFAdd, type_float_, layer_0_coordinate, + builder_->makeFloatConstant(1.0f)); + id_vector_temp_.clear(); + id_vector_temp_.push_back(coordinates[0]); + id_vector_temp_.push_back(coordinates[1]); + id_vector_temp_.push_back(layer_1_coordinate); + texture_parameters.coords = builder_->createCompositeConstruct( + type_float3_, id_vector_temp_); + spv::Id layer_lerp_factor = builder_->createUnaryBuiltinCall( + type_float_, ext_inst_glsl_std_450_, GLSLstd450Fract, + layer_coordinate); + spv::Id sample_result_unsigned_stacked_filtered; + spv::Id sample_result_signed_stacked_filtered; + SampleTexture( + texture_parameters, image_operands_mask, + image_2d_array_or_cube_unsigned, + image_2d_array_or_cube_signed, sampler, is_any_unsigned, + is_any_signed, sample_result_unsigned_stacked_filtered, + sample_result_signed_stacked_filtered, layer_lerp_factor, + sample_result_unsigned_stacked, sample_result_signed_stacked); + if (vol_filter_is_linear != spv::NoResult) { + // Get the actual build point after the SampleTexture call for + // phi. + spv::Block& block_z_linear_end = *builder_->getBuildPoint(); + builder_->createBranch(&block_z_merge); + builder_->setBuildPoint(&block_z_merge); + { + std::unique_ptr filter_phi_op = + std::make_unique( + builder_->getUniqueId(), type_float4_, spv::OpPhi); + filter_phi_op->addIdOperand( + sample_result_unsigned_stacked_filtered); + filter_phi_op->addIdOperand(block_z_linear_end.getId()); + filter_phi_op->addIdOperand(sample_result_unsigned_stacked); + filter_phi_op->addIdOperand(block_z_head.getId()); + sample_result_unsigned_stacked = filter_phi_op->getResultId(); + builder_->getBuildPoint()->addInstruction( + std::move(filter_phi_op)); + } + { + std::unique_ptr filter_phi_op = + std::make_unique( + builder_->getUniqueId(), type_float4_, spv::OpPhi); + filter_phi_op->addIdOperand( + sample_result_signed_stacked_filtered); + filter_phi_op->addIdOperand(block_z_linear_end.getId()); + filter_phi_op->addIdOperand(sample_result_signed_stacked); + filter_phi_op->addIdOperand(block_z_head.getId()); + sample_result_signed_stacked = filter_phi_op->getResultId(); + builder_->getBuildPoint()->addInstruction( + std::move(filter_phi_op)); + } + } else { + sample_result_unsigned_stacked = + sample_result_unsigned_stacked_filtered; + sample_result_signed_stacked = + sample_result_signed_stacked_filtered; } - } else { - sample_result_unsigned_stacked = - sample_result_unsigned_stacked_filtered; - sample_result_signed_stacked = - sample_result_signed_stacked_filtered; } } - // Get the actual build point for phi. - spv::Block& block_dimension_stacked_end = *builder_->getBuildPoint(); - builder_->createBranch(&block_dimension_merge); + if_data_is_3d.makeEndIf(); - // Choose between the 3D and the stacked result based on the actual - // data dimensionality. - builder_->setBuildPoint(&block_dimension_merge); - { - std::unique_ptr dimension_phi_op = - std::make_unique(builder_->getUniqueId(), - type_float4_, spv::OpPhi); - dimension_phi_op->addIdOperand(sample_result_unsigned_3d); - dimension_phi_op->addIdOperand(block_dimension_3d_end.getId()); - dimension_phi_op->addIdOperand(sample_result_unsigned_stacked); - dimension_phi_op->addIdOperand(block_dimension_stacked_end.getId()); - sample_result_unsigned = dimension_phi_op->getResultId(); - builder_->getBuildPoint()->addInstruction( - std::move(dimension_phi_op)); - } - { - std::unique_ptr dimension_phi_op = - std::make_unique(builder_->getUniqueId(), - type_float4_, spv::OpPhi); - dimension_phi_op->addIdOperand(sample_result_signed_3d); - dimension_phi_op->addIdOperand(block_dimension_3d_end.getId()); - dimension_phi_op->addIdOperand(sample_result_signed_stacked); - dimension_phi_op->addIdOperand(block_dimension_stacked_end.getId()); - sample_result_signed = dimension_phi_op->getResultId(); - builder_->getBuildPoint()->addInstruction( - std::move(dimension_phi_op)); - } + sample_result_unsigned = if_data_is_3d.createMergePhi( + sample_result_unsigned_3d, sample_result_unsigned_stacked); + sample_result_signed = if_data_is_3d.createMergePhi( + sample_result_signed_3d, sample_result_signed_stacked); } else { if (use_computed_lod) { texture_parameters.gradX = gradients_h; @@ -2045,7 +1984,7 @@ void SpirvShaderTranslator::ProcessTextureFetchInstruction( builder_->createCompositeConstruct(type_float3_, id_vector_temp_); SampleTexture(texture_parameters, image_operands_mask, image_2d_array_or_cube_unsigned, - image_2d_array_or_cube_signed, sampler, is_all_signed, + image_2d_array_or_cube_signed, sampler, is_any_unsigned, is_any_signed, sample_result_unsigned, sample_result_signed); } @@ -2095,26 +2034,20 @@ void SpirvShaderTranslator::ProcessTextureFetchInstruction( spv::OpBitwiseAnd, type_uint_, swizzle_word, builder_->makeUintConstant(swizzle_bit_0_value << 2)), const_uint_0_); - spv::Block& block_swizzle_head = *builder_->getBuildPoint(); - spv::Block& block_swizzle_constant = builder_->makeNewBlock(); - spv::Block& block_swizzle_component = builder_->makeNewBlock(); - spv::Block& block_swizzle_merge = builder_->makeNewBlock(); - builder_->createSelectionMerge( - &block_swizzle_merge, spv::SelectionControlDontFlattenMask); - builder_->createConditionalBranch(swizzle_bit_2, - &block_swizzle_constant, - &block_swizzle_component); - // Constant values. - builder_->setBuildPoint(&block_swizzle_constant); - // Bit 0 - 0 or 1. - spv::Id swizzle_result_constant = - builder_->createTriOp(spv::OpSelect, type_float_, swizzle_bit_0, - const_float_1, const_float_0_); - builder_->createBranch(&block_swizzle_merge); - // Fetched components. + SpirvBuilder::IfBuilder if_swizzle_constant( + swizzle_bit_2, spv::SelectionControlDontFlattenMask, *builder_); + spv::Id swizzle_result_constant; + { + // Constant values. + // Bit 0 - 0 or 1. + swizzle_result_constant = builder_->createTriOp( + spv::OpSelect, type_float_, swizzle_bit_0, const_float_1, + const_float_0_); + } + if_swizzle_constant.makeBeginElse(); spv::Id swizzle_result_component; { - builder_->setBuildPoint(&block_swizzle_component); + // Fetched components. // Select whether the result is signed or unsigned (or biased or // gamma-corrected) based on the post-swizzle signedness. spv::Id swizzle_sample_result = builder_->createTriOp( @@ -2146,22 +2079,11 @@ void SpirvShaderTranslator::ProcessTextureFetchInstruction( swizzle_result_component = builder_->createTriOp( spv::OpSelect, type_float_, swizzle_bit_1, swizzle_z_or_w, swizzle_x_or_y); - builder_->createBranch(&block_swizzle_merge); } + if_swizzle_constant.makeEndIf(); // Select between the constants and the fetched components. - builder_->setBuildPoint(&block_swizzle_merge); - { - std::unique_ptr swizzle_phi_op = - std::make_unique(builder_->getUniqueId(), - type_float_, spv::OpPhi); - swizzle_phi_op->addIdOperand(swizzle_result_constant); - swizzle_phi_op->addIdOperand(block_swizzle_constant.getId()); - swizzle_phi_op->addIdOperand(swizzle_result_component); - swizzle_phi_op->addIdOperand(block_swizzle_component.getId()); - result[result_component_index] = swizzle_phi_op->getResultId(); - builder_->getBuildPoint()->addInstruction( - std::move(swizzle_phi_op)); - } + result[result_component_index] = if_swizzle_constant.createMergePhi( + swizzle_result_constant, swizzle_result_component); } } @@ -2441,58 +2363,43 @@ size_t SpirvShaderTranslator::FindOrAddSamplerBinding( void SpirvShaderTranslator::SampleTexture( spv::Builder::TextureParameters& texture_parameters, spv::ImageOperandsMask image_operands_mask, spv::Id image_unsigned, - spv::Id image_signed, spv::Id sampler, spv::Id is_all_signed, + spv::Id image_signed, spv::Id sampler, spv::Id is_any_unsigned, spv::Id is_any_signed, spv::Id& result_unsigned_out, spv::Id& result_signed_out, spv::Id lerp_factor, spv::Id lerp_first_unsigned, spv::Id lerp_first_signed) { for (uint32_t i = 0; i < 2; ++i) { - spv::Block& block_sign_head = *builder_->getBuildPoint(); - spv::Block& block_sign = builder_->makeNewBlock(); - spv::Block& block_sign_merge = builder_->makeNewBlock(); - builder_->createSelectionMerge(&block_sign_merge, - spv::SelectionControlDontFlattenMask); - // Unsigned (i == 0) - if there are any non-signed components. - // Signed (i == 1) - if there are any signed components. - builder_->createConditionalBranch(i ? is_any_signed : is_all_signed, - i ? &block_sign : &block_sign_merge, - i ? &block_sign_merge : &block_sign); - builder_->setBuildPoint(&block_sign); - spv::Id image = i ? image_signed : image_unsigned; - // OpSampledImage must be in the same block as where its result is used. - texture_parameters.sampler = builder_->createBinOp( - spv::OpSampledImage, - builder_->makeSampledImageType(builder_->getTypeId(image)), image, - sampler); - spv::Id result = builder_->createTextureCall( - spv::NoPrecision, type_float4_, false, false, false, false, false, - texture_parameters, image_operands_mask); - if (lerp_factor != spv::NoResult) { - spv::Id lerp_first = i ? lerp_first_signed : lerp_first_unsigned; - if (lerp_first != spv::NoResult) { - spv::Id lerp_difference = builder_->createNoContractionBinOp( - spv::OpVectorTimesScalar, type_float4_, - builder_->createNoContractionBinOp(spv::OpFSub, type_float4_, - result, lerp_first), - lerp_factor); - result = builder_->createNoContractionBinOp(spv::OpFAdd, type_float4_, - result, lerp_difference); + SpirvBuilder::IfBuilder sign_if(i ? is_any_signed : is_any_unsigned, + spv::SelectionControlDontFlattenMask, + *builder_); + spv::Id sign_result; + { + spv::Id image = i ? image_signed : image_unsigned; + // OpSampledImage must be in the same block as where its result is used. + texture_parameters.sampler = builder_->createBinOp( + spv::OpSampledImage, + builder_->makeSampledImageType(builder_->getTypeId(image)), image, + sampler); + sign_result = builder_->createTextureCall( + spv::NoPrecision, type_float4_, false, false, false, false, false, + texture_parameters, image_operands_mask); + if (lerp_factor != spv::NoResult) { + spv::Id lerp_first = i ? lerp_first_signed : lerp_first_unsigned; + if (lerp_first != spv::NoResult) { + spv::Id lerp_difference = builder_->createNoContractionBinOp( + spv::OpVectorTimesScalar, type_float4_, + builder_->createNoContractionBinOp(spv::OpFSub, type_float4_, + sign_result, lerp_first), + lerp_factor); + sign_result = builder_->createNoContractionBinOp( + spv::OpFAdd, type_float4_, sign_result, lerp_difference); + } } } - builder_->createBranch(&block_sign_merge); - builder_->setBuildPoint(&block_sign_merge); - { - std::unique_ptr phi_op = - std::make_unique(builder_->getUniqueId(), - type_float4_, spv::OpPhi); - phi_op->addIdOperand(result); - phi_op->addIdOperand(block_sign.getId()); - phi_op->addIdOperand(const_float4_0_); - phi_op->addIdOperand(block_sign_head.getId()); - // This may overwrite the first lerp endpoint for the sign (such usage of - // this function is allowed). - (i ? result_signed_out : result_unsigned_out) = phi_op->getResultId(); - builder_->getBuildPoint()->addInstruction(std::move(phi_op)); - } + sign_if.makeEndIf(); + // This may overwrite the first lerp endpoint for the sign (such usage of + // this function is allowed). + (i ? result_signed_out : result_unsigned_out) = + sign_if.createMergePhi(sign_result, const_float4_0_); } } @@ -2500,48 +2407,33 @@ spv::Id SpirvShaderTranslator::QueryTextureLod( spv::Builder::TextureParameters& texture_parameters, spv::Id image_unsigned, spv::Id image_signed, spv::Id sampler, spv::Id is_all_signed) { // OpSampledImage must be in the same block as where its result is used. - spv::Block& block_sign_head = *builder_->getBuildPoint(); - spv::Block& block_sign_signed = builder_->makeNewBlock(); - spv::Block& block_sign_unsigned = builder_->makeNewBlock(); - spv::Block& block_sign_merge = builder_->makeNewBlock(); - builder_->createSelectionMerge(&block_sign_merge, - spv::SelectionControlDontFlattenMask); - builder_->createConditionalBranch(is_all_signed, &block_sign_signed, - &block_sign_unsigned); - builder_->setBuildPoint(&block_sign_signed); - texture_parameters.sampler = builder_->createBinOp( - spv::OpSampledImage, - builder_->makeSampledImageType(builder_->getTypeId(image_signed)), - image_signed, sampler); - spv::Id lod_signed = builder_->createCompositeExtract( - builder_->createTextureQueryCall(spv::OpImageQueryLod, texture_parameters, - false), - type_float_, 1); - builder_->createBranch(&block_sign_merge); - builder_->setBuildPoint(&block_sign_unsigned); - texture_parameters.sampler = builder_->createBinOp( - spv::OpSampledImage, - builder_->makeSampledImageType(builder_->getTypeId(image_unsigned)), - image_unsigned, sampler); - spv::Id lod_unsigned = builder_->createCompositeExtract( - builder_->createTextureQueryCall(spv::OpImageQueryLod, texture_parameters, - false), - type_float_, 1); - builder_->createBranch(&block_sign_merge); - builder_->setBuildPoint(&block_sign_merge); - spv::Id result; + SpirvBuilder::IfBuilder if_signed( + is_all_signed, spv::SelectionControlDontFlattenMask, *builder_); + spv::Id lod_signed; { - std::unique_ptr sign_phi_op = - std::make_unique(builder_->getUniqueId(), type_float_, - spv::OpPhi); - sign_phi_op->addIdOperand(lod_signed); - sign_phi_op->addIdOperand(block_sign_signed.getId()); - sign_phi_op->addIdOperand(lod_unsigned); - sign_phi_op->addIdOperand(block_sign_unsigned.getId()); - result = sign_phi_op->getResultId(); - builder_->getBuildPoint()->addInstruction(std::move(sign_phi_op)); + texture_parameters.sampler = builder_->createBinOp( + spv::OpSampledImage, + builder_->makeSampledImageType(builder_->getTypeId(image_signed)), + image_signed, sampler); + lod_signed = builder_->createCompositeExtract( + builder_->createTextureQueryCall(spv::OpImageQueryLod, + texture_parameters, false), + type_float_, 1); } - return result; + if_signed.makeBeginElse(); + spv::Id lod_unsigned; + { + texture_parameters.sampler = builder_->createBinOp( + spv::OpSampledImage, + builder_->makeSampledImageType(builder_->getTypeId(image_unsigned)), + image_unsigned, sampler); + lod_unsigned = builder_->createCompositeExtract( + builder_->createTextureQueryCall(spv::OpImageQueryLod, + texture_parameters, false), + type_float_, 1); + } + if_signed.makeEndIf(); + return if_signed.createMergePhi(lod_signed, lod_unsigned); } } // namespace gpu diff --git a/src/xenia/gpu/spirv_shader_translator_rb.cc b/src/xenia/gpu/spirv_shader_translator_rb.cc index 65a01209d..e19fdd540 100644 --- a/src/xenia/gpu/spirv_shader_translator_rb.cc +++ b/src/xenia/gpu/spirv_shader_translator_rb.cc @@ -457,22 +457,14 @@ void SpirvShaderTranslator::CompleteFragmentShaderInMain() { // Kill the pixel once the guest control flow and derivatives are not // needed anymore. assert_true(var_main_kill_pixel_ != spv::NoResult); - // Load the condition before the OpSelectionMerge, which must be the - // penultimate instruction. - spv::Id kill_pixel = - builder_->createLoad(var_main_kill_pixel_, spv::NoPrecision); - spv::Block& block_kill = builder_->makeNewBlock(); - spv::Block& block_kill_merge = builder_->makeNewBlock(); - builder_->createSelectionMerge(&block_kill_merge, - spv::SelectionControlDontFlattenMask); - builder_->createConditionalBranch(kill_pixel, &block_kill, - &block_kill_merge); - builder_->setBuildPoint(&block_kill); + SpirvBuilder::IfBuilder kill_pixel_if( + builder_->createLoad(var_main_kill_pixel_, spv::NoPrecision), + spv::SelectionControlMaskNone, *builder_); // TODO(Triang3l): Use OpTerminateInvocation when SPIR-V 1.6 is // targeted. builder_->createNoResultOp(spv::OpKill); // OpKill terminates the block. - builder_->setBuildPoint(&block_kill_merge); + kill_pixel_if.makeEndIf(false); } } } @@ -533,17 +525,11 @@ void SpirvShaderTranslator::CompleteFragmentShaderInMain() { 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(); - builder_->createSelectionMerge(&block_alpha_test_merge, - spv::SelectionControlDontFlattenMask); - builder_->createConditionalBranch(alpha_test_function_is_non_always, - &block_alpha_test, - &block_alpha_test_merge); - builder_->setBuildPoint(&block_alpha_test); + SpirvBuilder::IfBuilder if_alpha_test_function_is_non_always( + builder_->createBinOp(spv::OpINotEqual, type_bool_, alpha_test_function, + builder_->makeUintConstant( + uint32_t(xenos::CompareFunction::kAlways))), + spv::SelectionControlDontFlattenMask, *builder_); { id_vector_temp_.clear(); id_vector_temp_.push_back(builder_->makeIntConstant(3)); @@ -564,28 +550,20 @@ void SpirvShaderTranslator::CompleteFragmentShaderInMain() { // 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(); - builder_->createSelectionMerge(&block_alpha_test_not_equal_merge, - 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); + SpirvBuilder::IfBuilder if_alpha_test_function_is_not_equal( + builder_->createBinOp(spv::OpIEqual, type_bool_, alpha_test_function, + builder_->makeUintConstant(uint32_t( + xenos::CompareFunction::kNotEqual))), + spv::SelectionControlDontFlattenMask, *builder_, 1, 2); + spv::Id alpha_test_result_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); + if_alpha_test_function_is_not_equal.makeBeginElse(); + spv::Id alpha_test_result_non_not_equal; { // Function other than "not equal". static const spv::Op kAlphaTestOps[] = { @@ -609,16 +587,11 @@ void SpirvShaderTranslator::CompleteFragmentShaderInMain() { 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); - id_vector_temp_.clear(); - id_vector_temp_.push_back(alpha_test_result_not_equal); - id_vector_temp_.push_back(block_alpha_test_not_equal.getId()); - id_vector_temp_.push_back(alpha_test_result_non_not_equal); - id_vector_temp_.push_back(block_alpha_test_non_not_equal.getId()); + if_alpha_test_function_is_not_equal.makeEndIf(); spv::Id alpha_test_result = - builder_->createOp(spv::OpPhi, type_bool_, id_vector_temp_); + if_alpha_test_function_is_not_equal.createMergePhi( + alpha_test_result_not_equal, alpha_test_result_non_not_equal); // Discard the pixel if the alpha test has failed. if (edram_fragment_shader_interlock_ && !features_.demote_to_helper_invocation) { @@ -627,16 +600,11 @@ void SpirvShaderTranslator::CompleteFragmentShaderInMain() { spv::OpSelect, type_uint_, alpha_test_result, fsi_sample_mask_in_rt_0_alpha_tests, const_uint_0_); } else { - // 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(); - builder_->createSelectionMerge(&block_alpha_test_kill_merge, - spv::SelectionControlDontFlattenMask); - builder_->createConditionalBranch(alpha_test_result, - &block_alpha_test_kill_merge, - &block_alpha_test_kill); - builder_->setBuildPoint(&block_alpha_test_kill); + SpirvBuilder::IfBuilder alpha_test_kill_if( + builder_->createUnaryOp(spv::OpLogicalNot, type_bool_, + alpha_test_result), + spv::SelectionControlDontFlattenMask, *builder_); + bool branch_to_alpha_test_kill_merge = true; if (edram_fragment_shader_interlock_) { assert_true(features_.demote_to_helper_invocation); fsi_pixel_potentially_killed = true; @@ -645,18 +613,17 @@ void SpirvShaderTranslator::CompleteFragmentShaderInMain() { builder_->addExtension("SPV_EXT_demote_to_helper_invocation"); builder_->addCapability(spv::CapabilityDemoteToHelperInvocationEXT); builder_->createNoResultOp(spv::OpDemoteToHelperInvocationEXT); - builder_->createBranch(&block_alpha_test_kill_merge); } else { // TODO(Triang3l): Use OpTerminateInvocation when SPIR-V 1.6 is // targeted. builder_->createNoResultOp(spv::OpKill); // OpKill terminates the block. + branch_to_alpha_test_kill_merge = false; } - builder_->setBuildPoint(&block_alpha_test_kill_merge); - builder_->createBranch(&block_alpha_test_merge); + alpha_test_kill_if.makeEndIf(branch_to_alpha_test_kill_merge); } } - builder_->setBuildPoint(&block_alpha_test_merge); + if_alpha_test_function_is_non_always.makeEndIf(); // TODO(Triang3l): Alpha to coverage. @@ -725,18 +692,9 @@ void SpirvShaderTranslator::CompleteFragmentShaderInMain() { spv::OpBitwiseAnd, type_uint_, main_fsi_sample_mask_, builder_->makeUintConstant(uint32_t(1) << (4 + i))), const_uint_0_); - spv::Block& block_sample_late_depth_stencil_write = - builder_->makeNewBlock(); - spv::Block& block_sample_late_depth_stencil_write_merge = - builder_->makeNewBlock(); - builder_->createSelectionMerge( - &block_sample_late_depth_stencil_write_merge, - spv::SelectionControlDontFlattenMask); - builder_->createConditionalBranch( + SpirvBuilder::IfBuilder if_sample_late_depth_stencil_write_needed( sample_late_depth_stencil_write_needed, - &block_sample_late_depth_stencil_write, - &block_sample_late_depth_stencil_write_merge); - builder_->setBuildPoint(&block_sample_late_depth_stencil_write); + spv::SelectionControlDontFlattenMask, *builder_); spv::Id depth_stencil_sample_address = FSI_AddSampleOffset(main_fsi_address_depth_, i); id_vector_temp_.clear(); @@ -749,8 +707,7 @@ void SpirvShaderTranslator::CompleteFragmentShaderInMain() { ? spv::StorageClassStorageBuffer : spv::StorageClassUniform, buffer_edram_, id_vector_temp_)); - builder_->createBranch(&block_sample_late_depth_stencil_write_merge); - builder_->setBuildPoint(&block_sample_late_depth_stencil_write_merge); + if_sample_late_depth_stencil_write_needed.makeEndIf(); } if (color_targets_written) { // Only take the remaining coverage bits, not the late depth / stencil @@ -852,28 +809,10 @@ void SpirvShaderTranslator::CompleteFragmentShaderInMain() { spv::OpBitwiseAnd, type_uint_, fsi_color_targets_written, builder_->makeUintConstant(uint32_t(1) << color_target_index)), const_uint_0_); - spv::Block& fsi_color_written_if_head = *builder_->getBuildPoint(); - spv::Block& fsi_color_written_if = builder_->makeNewBlock(); - spv::Block& fsi_color_written_if_merge = builder_->makeNewBlock(); - builder_->createSelectionMerge(&fsi_color_written_if_merge, - spv::SelectionControlDontFlattenMask); - { - std::unique_ptr rt_written_branch_conditional_op = - std::make_unique(spv::OpBranchConditional); - rt_written_branch_conditional_op->addIdOperand(fsi_color_written); - rt_written_branch_conditional_op->addIdOperand( - fsi_color_written_if.getId()); - rt_written_branch_conditional_op->addIdOperand( - fsi_color_written_if_merge.getId()); - // More likely to write to the render target than not. - rt_written_branch_conditional_op->addImmediateOperand(2); - rt_written_branch_conditional_op->addImmediateOperand(1); - builder_->getBuildPoint()->addInstruction( - std::move(rt_written_branch_conditional_op)); - } - fsi_color_written_if.addPredecessor(&fsi_color_written_if_head); - fsi_color_written_if_merge.addPredecessor(&fsi_color_written_if_head); - builder_->setBuildPoint(&fsi_color_written_if); + // More likely to write to the render target than not. + SpirvBuilder::IfBuilder if_fsi_color_written( + fsi_color_written, spv::SelectionControlDontFlattenMask, *builder_, + 2, 1); // For accessing uint2 arrays of per-render-target data which are passed // as uint4 arrays due to std140 array element alignment. @@ -914,14 +853,9 @@ void SpirvShaderTranslator::CompleteFragmentShaderInMain() { const_uint32_max), builder_->createBinOp(spv::OpINotEqual, type_bool_, rt_keep_mask[1], const_uint32_max)); - spv::Block& rt_write_mask_not_empty_if = builder_->makeNewBlock(); - spv::Block& rt_write_mask_not_empty_if_merge = builder_->makeNewBlock(); - builder_->createSelectionMerge(&rt_write_mask_not_empty_if_merge, - spv::SelectionControlDontFlattenMask); - builder_->createConditionalBranch(rt_write_mask_not_empty, - &rt_write_mask_not_empty_if, - &rt_write_mask_not_empty_if_merge); - builder_->setBuildPoint(&rt_write_mask_not_empty_if); + SpirvBuilder::IfBuilder if_rt_write_mask_not_empty( + rt_write_mask_not_empty, spv::SelectionControlDontFlattenMask, + *builder_); spv::Id const_int_rt_index = builder_->makeIntConstant(color_target_index); @@ -982,17 +916,10 @@ void SpirvShaderTranslator::CompleteFragmentShaderInMain() { spv::Id rt_blend_enabled = builder_->createBinOp( spv::OpINotEqual, type_bool_, rt_blend_factors_equations, builder_->makeUintConstant(0x00010001)); - spv::Block& rt_blend_enabled_if = builder_->makeNewBlock(); - spv::Block& rt_blend_enabled_else = builder_->makeNewBlock(); - spv::Block& rt_blend_enabled_merge = builder_->makeNewBlock(); - builder_->createSelectionMerge(&rt_blend_enabled_merge, - spv::SelectionControlDontFlattenMask); - builder_->createConditionalBranch( - rt_blend_enabled, &rt_blend_enabled_if, &rt_blend_enabled_else); - - // Blending path. + SpirvBuilder::IfBuilder if_rt_blend_enabled( + rt_blend_enabled, spv::SelectionControlDontFlattenMask, *builder_); { - builder_->setBuildPoint(&rt_blend_enabled_if); + // Blending path. // Get various parameters used in blending. spv::Id rt_color_is_fixed_point = builder_->createBinOp( @@ -1097,15 +1024,9 @@ void SpirvShaderTranslator::CompleteFragmentShaderInMain() { // Blend and mask each sample. for (uint32_t i = 0; i < 4; ++i) { - spv::Block& block_sample_covered = builder_->makeNewBlock(); - spv::Block& block_sample_covered_merge = builder_->makeNewBlock(); - builder_->createSelectionMerge( - &block_sample_covered_merge, - spv::SelectionControlDontFlattenMask); - builder_->createConditionalBranch(fsi_samples_covered[i], - &block_sample_covered, - &block_sample_covered_merge); - builder_->setBuildPoint(&block_sample_covered); + SpirvBuilder::IfBuilder if_sample_covered( + fsi_samples_covered[i], spv::SelectionControlDontFlattenMask, + *builder_); spv::Id rt_sample_address = FSI_AddSampleOffset(rt_sample_0_address, i, rt_is_64bpp); @@ -1131,26 +1052,13 @@ void SpirvShaderTranslator::CompleteFragmentShaderInMain() { dest_packed[0] = builder_->createLoad(rt_access_chain_0, spv::NoPrecision); { - spv::Block& block_load_64bpp_head = *builder_->getBuildPoint(); - spv::Block& block_load_64bpp = builder_->makeNewBlock(); - spv::Block& block_load_64bpp_merge = builder_->makeNewBlock(); - builder_->createSelectionMerge( - &block_load_64bpp_merge, - spv::SelectionControlDontFlattenMask); - builder_->createConditionalBranch(rt_is_64bpp, &block_load_64bpp, - &block_load_64bpp_merge); - builder_->setBuildPoint(&block_load_64bpp); + SpirvBuilder::IfBuilder if_64bpp( + rt_is_64bpp, spv::SelectionControlDontFlattenMask, *builder_); spv::Id dest_packed_64bpp_high = builder_->createLoad(rt_access_chain_1, spv::NoPrecision); - builder_->createBranch(&block_load_64bpp_merge); - builder_->setBuildPoint(&block_load_64bpp_merge); - id_vector_temp_.clear(); - id_vector_temp_.push_back(dest_packed_64bpp_high); - id_vector_temp_.push_back(block_load_64bpp.getId()); - id_vector_temp_.push_back(const_uint_0_); - id_vector_temp_.push_back(block_load_64bpp_head.getId()); - dest_packed[1] = - builder_->createOp(spv::OpPhi, type_uint_, id_vector_temp_); + if_64bpp.makeEndIf(); + dest_packed[1] = if_64bpp.createMergePhi(dest_packed_64bpp_high, + const_uint_0_); } std::array dest_unpacked = FSI_UnpackColor(dest_packed, rt_format_with_flags); @@ -1203,35 +1111,27 @@ void SpirvShaderTranslator::CompleteFragmentShaderInMain() { result_packed[0], rt_replace_mask[0])), rt_access_chain_0); - spv::Block& block_store_64bpp = builder_->makeNewBlock(); - spv::Block& block_store_64bpp_merge = builder_->makeNewBlock(); - builder_->createSelectionMerge( - &block_store_64bpp_merge, spv::SelectionControlDontFlattenMask); - builder_->createConditionalBranch(rt_is_64bpp, &block_store_64bpp, - &block_store_64bpp_merge); - builder_->setBuildPoint(&block_store_64bpp); - builder_->createStore( - builder_->createBinOp( - spv::OpBitwiseOr, type_uint_, - builder_->createBinOp(spv::OpBitwiseAnd, type_uint_, - dest_packed[1], rt_keep_mask[1]), - builder_->createBinOp(spv::OpBitwiseAnd, type_uint_, - result_packed[1], - rt_replace_mask[1])), - rt_access_chain_0); - builder_->createBranch(&block_store_64bpp_merge); - builder_->setBuildPoint(&block_store_64bpp_merge); + SpirvBuilder::IfBuilder if_64bpp( + rt_is_64bpp, spv::SelectionControlDontFlattenMask, *builder_); + { + builder_->createStore( + builder_->createBinOp( + spv::OpBitwiseOr, type_uint_, + builder_->createBinOp(spv::OpBitwiseAnd, type_uint_, + dest_packed[1], rt_keep_mask[1]), + builder_->createBinOp(spv::OpBitwiseAnd, type_uint_, + result_packed[1], + rt_replace_mask[1])), + rt_access_chain_1); + } + if_64bpp.makeEndIf(); - builder_->createBranch(&block_sample_covered_merge); - builder_->setBuildPoint(&block_sample_covered_merge); + if_sample_covered.makeEndIf(); } - - builder_->createBranch(&rt_blend_enabled_merge); } - - // Non-blending paths. + if_rt_blend_enabled.makeBeginElse(); { - builder_->setBuildPoint(&rt_blend_enabled_else); + // Non-blending paths. // Pack the new color for all samples. std::array color_packed = @@ -1244,19 +1144,12 @@ void SpirvShaderTranslator::CompleteFragmentShaderInMain() { rt_keep_mask[0], const_uint_0_), builder_->createBinOp(spv::OpINotEqual, type_bool_, rt_keep_mask[1], const_uint_0_)); - spv::Block& rt_keep_mask_not_empty_if = builder_->makeNewBlock(); - spv::Block& rt_keep_mask_not_empty_if_else = builder_->makeNewBlock(); - spv::Block& rt_keep_mask_not_empty_if_merge = - builder_->makeNewBlock(); - builder_->createSelectionMerge(&rt_keep_mask_not_empty_if_merge, - spv::SelectionControlDontFlattenMask); - builder_->createConditionalBranch(rt_keep_mask_not_empty, - &rt_keep_mask_not_empty_if, - &rt_keep_mask_not_empty_if_else); - // Loading and masking path. + SpirvBuilder::IfBuilder if_rt_keep_mask_not_empty( + rt_keep_mask_not_empty, spv::SelectionControlDontFlattenMask, + *builder_); { - builder_->setBuildPoint(&rt_keep_mask_not_empty_if); + // Loading and masking path. std::array color_packed_masked; for (uint32_t i = 0; i < 2; ++i) { color_packed_masked[i] = builder_->createBinOp( @@ -1265,15 +1158,9 @@ void SpirvShaderTranslator::CompleteFragmentShaderInMain() { rt_keep_mask[i])); } for (uint32_t i = 0; i < 4; ++i) { - spv::Block& block_sample_covered = builder_->makeNewBlock(); - spv::Block& block_sample_covered_merge = builder_->makeNewBlock(); - builder_->createSelectionMerge( - &block_sample_covered_merge, - spv::SelectionControlDontFlattenMask); - builder_->createConditionalBranch(fsi_samples_covered[i], - &block_sample_covered, - &block_sample_covered_merge); - builder_->setBuildPoint(&block_sample_covered); + SpirvBuilder::IfBuilder if_sample_covered( + fsi_samples_covered[i], spv::SelectionControlDontFlattenMask, + *builder_); spv::Id rt_sample_address = FSI_AddSampleOffset(rt_sample_0_address, i, rt_is_64bpp); id_vector_temp_.clear(); @@ -1295,52 +1182,38 @@ void SpirvShaderTranslator::CompleteFragmentShaderInMain() { rt_keep_mask[0]), color_packed_masked[0]), rt_access_chain_0); - spv::Block& block_store_64bpp = builder_->makeNewBlock(); - spv::Block& block_store_64bpp_merge = builder_->makeNewBlock(); - builder_->createSelectionMerge( - &block_store_64bpp_merge, - spv::SelectionControlDontFlattenMask); - builder_->createConditionalBranch(rt_is_64bpp, &block_store_64bpp, - &block_store_64bpp_merge); - builder_->setBuildPoint(&block_store_64bpp); - id_vector_temp_.back() = builder_->createBinOp( - spv::OpIAdd, type_int_, rt_sample_address, fsi_const_int_1); - spv::Id rt_access_chain_1 = builder_->createAccessChain( - features_.spirv_version >= spv::Spv_1_3 - ? spv::StorageClassStorageBuffer - : spv::StorageClassUniform, - buffer_edram_, id_vector_temp_); - builder_->createStore( - builder_->createBinOp( - spv::OpBitwiseOr, type_uint_, - builder_->createBinOp( - spv::OpBitwiseAnd, type_uint_, - builder_->createLoad(rt_access_chain_1, - spv::NoPrecision), - rt_keep_mask[1]), - color_packed_masked[1]), - rt_access_chain_1); - builder_->createBranch(&block_store_64bpp_merge); - builder_->setBuildPoint(&block_store_64bpp_merge); - builder_->createBranch(&block_sample_covered_merge); - builder_->setBuildPoint(&block_sample_covered_merge); + SpirvBuilder::IfBuilder if_64bpp( + rt_is_64bpp, spv::SelectionControlDontFlattenMask, *builder_); + { + id_vector_temp_.back() = builder_->createBinOp( + spv::OpIAdd, type_int_, rt_sample_address, fsi_const_int_1); + spv::Id rt_access_chain_1 = builder_->createAccessChain( + features_.spirv_version >= spv::Spv_1_3 + ? spv::StorageClassStorageBuffer + : spv::StorageClassUniform, + buffer_edram_, id_vector_temp_); + builder_->createStore( + builder_->createBinOp( + spv::OpBitwiseOr, type_uint_, + builder_->createBinOp( + spv::OpBitwiseAnd, type_uint_, + builder_->createLoad(rt_access_chain_1, + spv::NoPrecision), + rt_keep_mask[1]), + color_packed_masked[1]), + rt_access_chain_1); + } + if_64bpp.makeEndIf(); + if_sample_covered.makeEndIf(); } - builder_->createBranch(&rt_keep_mask_not_empty_if_merge); } - - // Fully overwriting path. + if_rt_keep_mask_not_empty.makeBeginElse(); { - builder_->setBuildPoint(&rt_keep_mask_not_empty_if_else); + // Fully overwriting path. for (uint32_t i = 0; i < 4; ++i) { - spv::Block& block_sample_covered = builder_->makeNewBlock(); - spv::Block& block_sample_covered_merge = builder_->makeNewBlock(); - builder_->createSelectionMerge( - &block_sample_covered_merge, - spv::SelectionControlDontFlattenMask); - builder_->createConditionalBranch(fsi_samples_covered[i], - &block_sample_covered, - &block_sample_covered_merge); - builder_->setBuildPoint(&block_sample_covered); + SpirvBuilder::IfBuilder if_sample_covered( + fsi_samples_covered[i], spv::SelectionControlDontFlattenMask, + *builder_); spv::Id rt_sample_address = FSI_AddSampleOffset(rt_sample_0_address, i, rt_is_64bpp); id_vector_temp_.clear(); @@ -1353,40 +1226,29 @@ void SpirvShaderTranslator::CompleteFragmentShaderInMain() { ? spv::StorageClassStorageBuffer : spv::StorageClassUniform, buffer_edram_, id_vector_temp_)); - spv::Block& block_store_64bpp = builder_->makeNewBlock(); - spv::Block& block_store_64bpp_merge = builder_->makeNewBlock(); - builder_->createSelectionMerge( - &block_store_64bpp_merge, - spv::SelectionControlDontFlattenMask); - builder_->createConditionalBranch(rt_is_64bpp, &block_store_64bpp, - &block_store_64bpp_merge); - builder_->setBuildPoint(&block_store_64bpp); - id_vector_temp_.back() = builder_->createBinOp( - spv::OpIAdd, type_int_, id_vector_temp_.back(), - fsi_const_int_1); - builder_->createStore(color_packed[1], - builder_->createAccessChain( - features_.spirv_version >= spv::Spv_1_3 - ? spv::StorageClassStorageBuffer - : spv::StorageClassUniform, - buffer_edram_, id_vector_temp_)); - builder_->createBranch(&block_store_64bpp_merge); - builder_->setBuildPoint(&block_store_64bpp_merge); - builder_->createBranch(&block_sample_covered_merge); - builder_->setBuildPoint(&block_sample_covered_merge); + SpirvBuilder::IfBuilder if_64bpp( + rt_is_64bpp, spv::SelectionControlDontFlattenMask, *builder_); + { + id_vector_temp_.back() = builder_->createBinOp( + spv::OpIAdd, type_int_, id_vector_temp_.back(), + fsi_const_int_1); + builder_->createStore( + color_packed[1], builder_->createAccessChain( + features_.spirv_version >= spv::Spv_1_3 + ? spv::StorageClassStorageBuffer + : spv::StorageClassUniform, + buffer_edram_, id_vector_temp_)); + } + if_64bpp.makeEndIf(); + if_sample_covered.makeEndIf(); } - builder_->createBranch(&rt_keep_mask_not_empty_if_merge); } - - builder_->setBuildPoint(&rt_keep_mask_not_empty_if_merge); - builder_->createBranch(&rt_blend_enabled_merge); + if_rt_keep_mask_not_empty.makeEndIf(); } + if_rt_blend_enabled.makeEndIf(); - builder_->setBuildPoint(&rt_blend_enabled_merge); - builder_->createBranch(&rt_write_mask_not_empty_if_merge); - builder_->setBuildPoint(&rt_write_mask_not_empty_if_merge); - builder_->createBranch(&fsi_color_written_if_merge); - builder_->setBuildPoint(&fsi_color_written_if_merge); + if_rt_write_mask_not_empty.makeEndIf(); + if_fsi_color_written.makeEndIf(); } else { // Convert to gamma space - this is incorrect, since it must be done // after blending on the Xbox 360, but this is just one of many blending @@ -1405,24 +1267,11 @@ void SpirvShaderTranslator::CompleteFragmentShaderInMain() { builder_->makeUintConstant(kSysFlag_ConvertColor0ToGamma << color_target_index)), const_uint_0_); - spv::Block& block_gamma_head = *builder_->getBuildPoint(); - spv::Block& block_gamma = builder_->makeNewBlock(); - spv::Block& block_gamma_merge = builder_->makeNewBlock(); - builder_->createSelectionMerge(&block_gamma_merge, - spv::SelectionControlDontFlattenMask); - builder_->createConditionalBranch(is_gamma, &block_gamma, - &block_gamma_merge); - builder_->setBuildPoint(&block_gamma); + SpirvBuilder::IfBuilder if_gamma( + is_gamma, spv::SelectionControlDontFlattenMask, *builder_); spv::Id color_rgb_gamma = LinearToPWLGamma(color_rgb, false); - builder_->createBranch(&block_gamma_merge); - builder_->setBuildPoint(&block_gamma_merge); - id_vector_temp_.clear(); - id_vector_temp_.push_back(color_rgb_gamma); - id_vector_temp_.push_back(block_gamma.getId()); - id_vector_temp_.push_back(color_rgb); - id_vector_temp_.push_back(block_gamma_head.getId()); - color_rgb = - builder_->createOp(spv::OpPhi, type_float3_, id_vector_temp_); + if_gamma.makeEndIf(); + color_rgb = if_gamma.createMergePhi(color_rgb_gamma, color_rgb); { std::unique_ptr color_rgba_shuffle_op = std::make_unique( @@ -1752,15 +1601,8 @@ void SpirvShaderTranslator::FSI_DepthStencilTest( spv::OpBitwiseAnd, type_uint_, main_system_constant_flags_, builder_->makeUintConstant(kSysFlag_FSIDepthStencil)), const_uint_0_); - spv::Block& block_depth_stencil_enabled_head = *builder_->getBuildPoint(); - spv::Block& block_depth_stencil_enabled = builder_->makeNewBlock(); - spv::Block& block_depth_stencil_enabled_merge = builder_->makeNewBlock(); - builder_->createSelectionMerge(&block_depth_stencil_enabled_merge, - spv::SelectionControlDontFlattenMask); - builder_->createConditionalBranch(depth_stencil_enabled, - &block_depth_stencil_enabled, - &block_depth_stencil_enabled_merge); - builder_->setBuildPoint(&block_depth_stencil_enabled); + SpirvBuilder::IfBuilder if_depth_stencil_enabled( + depth_stencil_enabled, spv::SelectionControlDontFlattenMask, *builder_); // Load the depth in the center of the pixel and calculate the derivatives of // the depth outside non-uniform control flow. @@ -1976,14 +1818,8 @@ void SpirvShaderTranslator::FSI_DepthStencilTest( builder_->createBinOp(spv::OpBitwiseAnd, type_uint_, new_sample_mask, builder_->makeUintConstant(uint32_t(1) << i)), const_uint_0_); - spv::Block& block_sample_covered_head = *builder_->getBuildPoint(); - spv::Block& block_sample_covered = builder_->makeNewBlock(); - spv::Block& block_sample_covered_merge = builder_->makeNewBlock(); - builder_->createSelectionMerge(&block_sample_covered_merge, - spv::SelectionControlDontFlattenMask); - builder_->createConditionalBranch(sample_covered, &block_sample_covered, - &block_sample_covered_merge); - builder_->setBuildPoint(&block_sample_covered); + SpirvBuilder::IfBuilder if_sample_covered( + sample_covered, spv::SelectionControlDontFlattenMask, *builder_); // Load the original depth and stencil for the sample. spv::Id sample_address = FSI_AddSampleOffset(main_fsi_address_depth_, i); @@ -2074,21 +1910,11 @@ void SpirvShaderTranslator::FSI_DepthStencilTest( const_float_0_, const_float_1_); // Convert the new depth to 24-bit. - spv::Block& block_depth_format_float = builder_->makeNewBlock(); - spv::Block& block_depth_format_unorm = builder_->makeNewBlock(); - spv::Block& block_depth_format_merge = builder_->makeNewBlock(); - builder_->createSelectionMerge(&block_depth_format_merge, - spv::SelectionControlDontFlattenMask); - builder_->createConditionalBranch( - depth_is_float24, &block_depth_format_float, &block_depth_format_unorm); - // Float24 case. - builder_->setBuildPoint(&block_depth_format_float); + SpirvBuilder::IfBuilder depth_format_if( + depth_is_float24, spv::SelectionControlDontFlattenMask, *builder_); spv::Id sample_depth_float24 = SpirvShaderTranslator::PreClampedDepthTo20e4( *builder_, sample_depth32, true, false, ext_inst_glsl_std_450_); - builder_->createBranch(&block_depth_format_merge); - spv::Block& block_depth_format_float_end = *builder_->getBuildPoint(); - // Unorm24 case. - builder_->setBuildPoint(&block_depth_format_unorm); + depth_format_if.makeBeginElse(); // Round to the nearest even integer. This seems to be the correct // conversion, adding +0.5 and rounding towards zero results in red instead // of black in the 4D5307E6 clear shader. @@ -2099,17 +1925,10 @@ void SpirvShaderTranslator::FSI_DepthStencilTest( builder_->createNoContractionBinOp( spv::OpFMul, type_float_, sample_depth32, builder_->makeFloatConstant(float(0xFFFFFF))))); - builder_->createBranch(&block_depth_format_merge); - spv::Block& block_depth_format_unorm_end = *builder_->getBuildPoint(); + depth_format_if.makeEndIf(); // Merge between the two formats. - builder_->setBuildPoint(&block_depth_format_merge); - id_vector_temp_.clear(); - id_vector_temp_.push_back(sample_depth_float24); - id_vector_temp_.push_back(block_depth_format_float_end.getId()); - id_vector_temp_.push_back(sample_depth_unorm24); - id_vector_temp_.push_back(block_depth_format_unorm_end.getId()); - spv::Id sample_depth24 = - builder_->createOp(spv::OpPhi, type_uint_, id_vector_temp_); + spv::Id sample_depth24 = depth_format_if.createMergePhi( + sample_depth_float24, sample_depth_unorm24); // Perform the depth test. spv::Id old_depth = builder_->createBinOp( @@ -2131,206 +1950,188 @@ void SpirvShaderTranslator::FSI_DepthStencilTest( builder_->createBinOp(spv::OpUGreaterThan, type_bool_, sample_depth24, old_depth))); - // Begin the stencil test. - spv::Block& block_stencil_enabled_head = *builder_->getBuildPoint(); - spv::Block& block_stencil_enabled = builder_->makeNewBlock(); - spv::Block& block_stencil_enabled_merge = builder_->makeNewBlock(); - builder_->createSelectionMerge(&block_stencil_enabled_merge, - spv::SelectionControlDontFlattenMask); - builder_->createConditionalBranch(stencil_enabled, &block_stencil_enabled, - &block_stencil_enabled_merge); - builder_->setBuildPoint(&block_stencil_enabled); - - // Perform the stencil test. - // The read mask has zeros in the upper bits, applying it to the combined - // stencil and depth will remove the depth part. - spv::Id old_stencil_read_masked = builder_->createBinOp( - spv::OpBitwiseAnd, type_uint_, old_depth_stencil, stencil_read_mask); - spv::Id stencil_passed_if_enabled = builder_->createBinOp( - spv::OpLogicalAnd, type_bool_, stencil_pass_if_less, - builder_->createBinOp(spv::OpULessThan, type_bool_, - stencil_reference_read_masked, - old_stencil_read_masked)); - stencil_passed_if_enabled = builder_->createBinOp( - spv::OpLogicalOr, type_bool_, stencil_passed_if_enabled, - builder_->createBinOp( - spv::OpLogicalAnd, type_bool_, stencil_pass_if_equal, - builder_->createBinOp(spv::OpIEqual, type_bool_, - stencil_reference_read_masked, - old_stencil_read_masked))); - stencil_passed_if_enabled = builder_->createBinOp( - spv::OpLogicalOr, type_bool_, stencil_passed_if_enabled, - builder_->createBinOp( - spv::OpLogicalAnd, type_bool_, stencil_pass_if_greater, - builder_->createBinOp(spv::OpUGreaterThan, type_bool_, - stencil_reference_read_masked, - old_stencil_read_masked))); - spv::Id stencil_op = builder_->createTriOp( - spv::OpBitFieldUExtract, type_uint_, stencil_func_ops, - builder_->createTriOp( - spv::OpSelect, type_uint_, stencil_passed_if_enabled, - builder_->createTriOp(spv::OpSelect, type_uint_, depth_passed, - builder_->makeUintConstant(6), - builder_->makeUintConstant(9)), - builder_->makeUintConstant(3)), - builder_->makeUintConstant(3)); - spv::Block& block_stencil_op_head = *builder_->getBuildPoint(); - spv::Block& block_stencil_op_keep = builder_->makeNewBlock(); - spv::Block& block_stencil_op_zero = builder_->makeNewBlock(); - spv::Block& block_stencil_op_replace = builder_->makeNewBlock(); - spv::Block& block_stencil_op_increment_clamp = builder_->makeNewBlock(); - spv::Block& block_stencil_op_decrement_clamp = builder_->makeNewBlock(); - spv::Block& block_stencil_op_invert = builder_->makeNewBlock(); - spv::Block& block_stencil_op_increment_wrap = builder_->makeNewBlock(); - spv::Block& block_stencil_op_decrement_wrap = builder_->makeNewBlock(); - spv::Block& block_stencil_op_merge = builder_->makeNewBlock(); - builder_->createSelectionMerge(&block_stencil_op_merge, - spv::SelectionControlDontFlattenMask); + // Perform the stencil test if enabled. + SpirvBuilder::IfBuilder stencil_if( + stencil_enabled, spv::SelectionControlDontFlattenMask, *builder_); + spv::Id stencil_passed_if_enabled; + spv::Id new_stencil_and_old_depth_if_stencil_enabled; { - std::unique_ptr stencil_op_switch_op = - std::make_unique(spv::OpSwitch); - stencil_op_switch_op->addIdOperand(stencil_op); - // Make keep the default. - stencil_op_switch_op->addIdOperand(block_stencil_op_keep.getId()); - stencil_op_switch_op->addImmediateOperand( - int32_t(xenos::StencilOp::kZero)); - stencil_op_switch_op->addIdOperand(block_stencil_op_zero.getId()); - stencil_op_switch_op->addImmediateOperand( - int32_t(xenos::StencilOp::kReplace)); - stencil_op_switch_op->addIdOperand(block_stencil_op_replace.getId()); - stencil_op_switch_op->addImmediateOperand( - int32_t(xenos::StencilOp::kIncrementClamp)); - stencil_op_switch_op->addIdOperand( - block_stencil_op_increment_clamp.getId()); - stencil_op_switch_op->addImmediateOperand( - int32_t(xenos::StencilOp::kDecrementClamp)); - stencil_op_switch_op->addIdOperand( - block_stencil_op_decrement_clamp.getId()); - stencil_op_switch_op->addImmediateOperand( - int32_t(xenos::StencilOp::kInvert)); - stencil_op_switch_op->addIdOperand(block_stencil_op_invert.getId()); - stencil_op_switch_op->addImmediateOperand( - int32_t(xenos::StencilOp::kIncrementWrap)); - stencil_op_switch_op->addIdOperand( - block_stencil_op_increment_wrap.getId()); - stencil_op_switch_op->addImmediateOperand( - int32_t(xenos::StencilOp::kDecrementWrap)); - stencil_op_switch_op->addIdOperand( - block_stencil_op_decrement_wrap.getId()); - builder_->getBuildPoint()->addInstruction( - std::move(stencil_op_switch_op)); + // The read mask has zeros in the upper bits, applying it to the combined + // stencil and depth will remove the depth part. + spv::Id old_stencil_read_masked = builder_->createBinOp( + spv::OpBitwiseAnd, type_uint_, old_depth_stencil, stencil_read_mask); + stencil_passed_if_enabled = builder_->createBinOp( + spv::OpLogicalAnd, type_bool_, stencil_pass_if_less, + builder_->createBinOp(spv::OpULessThan, type_bool_, + stencil_reference_read_masked, + old_stencil_read_masked)); + stencil_passed_if_enabled = builder_->createBinOp( + spv::OpLogicalOr, type_bool_, stencil_passed_if_enabled, + builder_->createBinOp( + spv::OpLogicalAnd, type_bool_, stencil_pass_if_equal, + builder_->createBinOp(spv::OpIEqual, type_bool_, + stencil_reference_read_masked, + old_stencil_read_masked))); + stencil_passed_if_enabled = builder_->createBinOp( + spv::OpLogicalOr, type_bool_, stencil_passed_if_enabled, + builder_->createBinOp( + spv::OpLogicalAnd, type_bool_, stencil_pass_if_greater, + builder_->createBinOp(spv::OpUGreaterThan, type_bool_, + stencil_reference_read_masked, + old_stencil_read_masked))); + spv::Id stencil_op = builder_->createTriOp( + spv::OpBitFieldUExtract, type_uint_, stencil_func_ops, + builder_->createTriOp( + spv::OpSelect, type_uint_, stencil_passed_if_enabled, + builder_->createTriOp(spv::OpSelect, type_uint_, depth_passed, + builder_->makeUintConstant(6), + builder_->makeUintConstant(9)), + builder_->makeUintConstant(3)), + builder_->makeUintConstant(3)); + spv::Block& block_stencil_op_head = *builder_->getBuildPoint(); + spv::Block& block_stencil_op_keep = builder_->makeNewBlock(); + spv::Block& block_stencil_op_zero = builder_->makeNewBlock(); + spv::Block& block_stencil_op_replace = builder_->makeNewBlock(); + spv::Block& block_stencil_op_increment_clamp = builder_->makeNewBlock(); + spv::Block& block_stencil_op_decrement_clamp = builder_->makeNewBlock(); + spv::Block& block_stencil_op_invert = builder_->makeNewBlock(); + spv::Block& block_stencil_op_increment_wrap = builder_->makeNewBlock(); + spv::Block& block_stencil_op_decrement_wrap = builder_->makeNewBlock(); + spv::Block& block_stencil_op_merge = builder_->makeNewBlock(); + builder_->createSelectionMerge(&block_stencil_op_merge, + spv::SelectionControlDontFlattenMask); + { + std::unique_ptr stencil_op_switch_op = + std::make_unique(spv::OpSwitch); + stencil_op_switch_op->addIdOperand(stencil_op); + // Make keep the default. + stencil_op_switch_op->addIdOperand(block_stencil_op_keep.getId()); + stencil_op_switch_op->addImmediateOperand( + int32_t(xenos::StencilOp::kZero)); + stencil_op_switch_op->addIdOperand(block_stencil_op_zero.getId()); + stencil_op_switch_op->addImmediateOperand( + int32_t(xenos::StencilOp::kReplace)); + stencil_op_switch_op->addIdOperand(block_stencil_op_replace.getId()); + stencil_op_switch_op->addImmediateOperand( + int32_t(xenos::StencilOp::kIncrementClamp)); + stencil_op_switch_op->addIdOperand( + block_stencil_op_increment_clamp.getId()); + stencil_op_switch_op->addImmediateOperand( + int32_t(xenos::StencilOp::kDecrementClamp)); + stencil_op_switch_op->addIdOperand( + block_stencil_op_decrement_clamp.getId()); + stencil_op_switch_op->addImmediateOperand( + int32_t(xenos::StencilOp::kInvert)); + stencil_op_switch_op->addIdOperand(block_stencil_op_invert.getId()); + stencil_op_switch_op->addImmediateOperand( + int32_t(xenos::StencilOp::kIncrementWrap)); + stencil_op_switch_op->addIdOperand( + block_stencil_op_increment_wrap.getId()); + stencil_op_switch_op->addImmediateOperand( + int32_t(xenos::StencilOp::kDecrementWrap)); + stencil_op_switch_op->addIdOperand( + block_stencil_op_decrement_wrap.getId()); + builder_->getBuildPoint()->addInstruction( + std::move(stencil_op_switch_op)); + } + block_stencil_op_keep.addPredecessor(&block_stencil_op_head); + block_stencil_op_zero.addPredecessor(&block_stencil_op_head); + block_stencil_op_replace.addPredecessor(&block_stencil_op_head); + block_stencil_op_increment_clamp.addPredecessor(&block_stencil_op_head); + block_stencil_op_decrement_clamp.addPredecessor(&block_stencil_op_head); + block_stencil_op_invert.addPredecessor(&block_stencil_op_head); + block_stencil_op_increment_wrap.addPredecessor(&block_stencil_op_head); + block_stencil_op_decrement_wrap.addPredecessor(&block_stencil_op_head); + // Keep - will use the old stencil in the phi. + builder_->setBuildPoint(&block_stencil_op_keep); + builder_->createBranch(&block_stencil_op_merge); + // Zero - will use the zero constant in the phi. + builder_->setBuildPoint(&block_stencil_op_zero); + builder_->createBranch(&block_stencil_op_merge); + // Replace - will use the stencil reference in the phi. + builder_->setBuildPoint(&block_stencil_op_replace); + builder_->createBranch(&block_stencil_op_merge); + // Increment and clamp. + builder_->setBuildPoint(&block_stencil_op_increment_clamp); + spv::Id new_stencil_in_low_bits_increment_clamp = builder_->createBinOp( + spv::OpIAdd, type_uint_, + builder_->createBinBuiltinCall( + type_uint_, ext_inst_glsl_std_450_, GLSLstd450UMin, + builder_->makeUintConstant(UINT8_MAX - 1), + builder_->createBinOp(spv::OpBitwiseAnd, type_uint_, + old_depth_stencil, + builder_->makeUintConstant(UINT8_MAX))), + const_uint_1); + builder_->createBranch(&block_stencil_op_merge); + // Decrement and clamp. + builder_->setBuildPoint(&block_stencil_op_decrement_clamp); + spv::Id new_stencil_in_low_bits_decrement_clamp = builder_->createBinOp( + spv::OpISub, type_uint_, + builder_->createBinBuiltinCall( + type_uint_, ext_inst_glsl_std_450_, GLSLstd450UMax, const_uint_1, + builder_->createBinOp(spv::OpBitwiseAnd, type_uint_, + old_depth_stencil, + builder_->makeUintConstant(UINT8_MAX))), + const_uint_1); + builder_->createBranch(&block_stencil_op_merge); + // Invert. + builder_->setBuildPoint(&block_stencil_op_invert); + spv::Id new_stencil_in_low_bits_invert = + builder_->createUnaryOp(spv::OpNot, type_uint_, old_depth_stencil); + builder_->createBranch(&block_stencil_op_merge); + // Increment and wrap. + // The upper bits containing the old depth have no effect on the behavior. + builder_->setBuildPoint(&block_stencil_op_increment_wrap); + spv::Id new_stencil_in_low_bits_increment_wrap = builder_->createBinOp( + spv::OpIAdd, type_uint_, old_depth_stencil, const_uint_1); + builder_->createBranch(&block_stencil_op_merge); + // Decrement and wrap. + // The upper bits containing the old depth have no effect on the behavior. + builder_->setBuildPoint(&block_stencil_op_decrement_wrap); + spv::Id new_stencil_in_low_bits_decrement_wrap = builder_->createBinOp( + spv::OpISub, type_uint_, old_depth_stencil, const_uint_1); + builder_->createBranch(&block_stencil_op_merge); + // Select the new stencil (with undefined data in bits starting from 8) + // based on the stencil operation. + builder_->setBuildPoint(&block_stencil_op_merge); + id_vector_temp_.clear(); + id_vector_temp_.reserve(2 * 8); + id_vector_temp_.push_back(old_depth_stencil); + id_vector_temp_.push_back(block_stencil_op_keep.getId()); + id_vector_temp_.push_back(const_uint_0_); + id_vector_temp_.push_back(block_stencil_op_zero.getId()); + id_vector_temp_.push_back(stencil_reference); + id_vector_temp_.push_back(block_stencil_op_replace.getId()); + id_vector_temp_.push_back(new_stencil_in_low_bits_increment_clamp); + id_vector_temp_.push_back(block_stencil_op_increment_clamp.getId()); + id_vector_temp_.push_back(new_stencil_in_low_bits_decrement_clamp); + id_vector_temp_.push_back(block_stencil_op_decrement_clamp.getId()); + id_vector_temp_.push_back(new_stencil_in_low_bits_invert); + id_vector_temp_.push_back(block_stencil_op_invert.getId()); + id_vector_temp_.push_back(new_stencil_in_low_bits_increment_wrap); + id_vector_temp_.push_back(block_stencil_op_increment_wrap.getId()); + id_vector_temp_.push_back(new_stencil_in_low_bits_decrement_wrap); + id_vector_temp_.push_back(block_stencil_op_decrement_wrap.getId()); + spv::Id new_stencil_in_low_bits_if_enabled = + builder_->createOp(spv::OpPhi, type_uint_, id_vector_temp_); + // Merge the old depth / stencil (old depth kept from the old depth / + // stencil so the separate old depth register is not needed anymore after + // the depth test) and the new stencil based on the write mask. + new_stencil_and_old_depth_if_stencil_enabled = builder_->createBinOp( + spv::OpBitwiseOr, type_uint_, + builder_->createBinOp(spv::OpBitwiseAnd, type_uint_, + old_depth_stencil, stencil_write_keep_mask), + builder_->createBinOp(spv::OpBitwiseAnd, type_uint_, + new_stencil_in_low_bits_if_enabled, + stencil_write_mask)); } - block_stencil_op_keep.addPredecessor(&block_stencil_op_head); - block_stencil_op_zero.addPredecessor(&block_stencil_op_head); - block_stencil_op_replace.addPredecessor(&block_stencil_op_head); - block_stencil_op_increment_clamp.addPredecessor(&block_stencil_op_head); - block_stencil_op_decrement_clamp.addPredecessor(&block_stencil_op_head); - block_stencil_op_invert.addPredecessor(&block_stencil_op_head); - block_stencil_op_increment_wrap.addPredecessor(&block_stencil_op_head); - block_stencil_op_decrement_wrap.addPredecessor(&block_stencil_op_head); - // Keep - will use the old stencil in the phi. - builder_->setBuildPoint(&block_stencil_op_keep); - builder_->createBranch(&block_stencil_op_merge); - // Zero - will use the zero constant in the phi. - builder_->setBuildPoint(&block_stencil_op_zero); - builder_->createBranch(&block_stencil_op_merge); - // Replace - will use the stencil reference in the phi. - builder_->setBuildPoint(&block_stencil_op_replace); - builder_->createBranch(&block_stencil_op_merge); - // Increment and clamp. - builder_->setBuildPoint(&block_stencil_op_increment_clamp); - spv::Id new_stencil_in_low_bits_increment_clamp = builder_->createBinOp( - spv::OpIAdd, type_uint_, - builder_->createBinBuiltinCall( - type_uint_, ext_inst_glsl_std_450_, GLSLstd450UMin, - builder_->makeUintConstant(UINT8_MAX - 1), - builder_->createBinOp(spv::OpBitwiseAnd, type_uint_, - old_depth_stencil, - builder_->makeUintConstant(UINT8_MAX))), - const_uint_1); - builder_->createBranch(&block_stencil_op_merge); - // Decrement and clamp. - builder_->setBuildPoint(&block_stencil_op_decrement_clamp); - spv::Id new_stencil_in_low_bits_decrement_clamp = builder_->createBinOp( - spv::OpISub, type_uint_, - builder_->createBinBuiltinCall( - type_uint_, ext_inst_glsl_std_450_, GLSLstd450UMax, const_uint_1, - builder_->createBinOp(spv::OpBitwiseAnd, type_uint_, - old_depth_stencil, - builder_->makeUintConstant(UINT8_MAX))), - const_uint_1); - builder_->createBranch(&block_stencil_op_merge); - // Invert. - builder_->setBuildPoint(&block_stencil_op_invert); - spv::Id new_stencil_in_low_bits_invert = - builder_->createUnaryOp(spv::OpNot, type_uint_, old_depth_stencil); - builder_->createBranch(&block_stencil_op_merge); - // Increment and wrap. - // The upper bits containing the old depth have no effect on the behavior. - builder_->setBuildPoint(&block_stencil_op_increment_wrap); - spv::Id new_stencil_in_low_bits_increment_wrap = builder_->createBinOp( - spv::OpIAdd, type_uint_, old_depth_stencil, const_uint_1); - builder_->createBranch(&block_stencil_op_merge); - // Decrement and wrap. - // The upper bits containing the old depth have no effect on the behavior. - builder_->setBuildPoint(&block_stencil_op_decrement_wrap); - spv::Id new_stencil_in_low_bits_decrement_wrap = builder_->createBinOp( - spv::OpISub, type_uint_, old_depth_stencil, const_uint_1); - builder_->createBranch(&block_stencil_op_merge); - // Select the new stencil (with undefined data in bits starting from 8) - // based on the stencil operation. - builder_->setBuildPoint(&block_stencil_op_merge); - id_vector_temp_.clear(); - id_vector_temp_.reserve(2 * 8); - id_vector_temp_.push_back(old_depth_stencil); - id_vector_temp_.push_back(block_stencil_op_keep.getId()); - id_vector_temp_.push_back(const_uint_0_); - id_vector_temp_.push_back(block_stencil_op_zero.getId()); - id_vector_temp_.push_back(stencil_reference); - id_vector_temp_.push_back(block_stencil_op_replace.getId()); - id_vector_temp_.push_back(new_stencil_in_low_bits_increment_clamp); - id_vector_temp_.push_back(block_stencil_op_increment_clamp.getId()); - id_vector_temp_.push_back(new_stencil_in_low_bits_decrement_clamp); - id_vector_temp_.push_back(block_stencil_op_decrement_clamp.getId()); - id_vector_temp_.push_back(new_stencil_in_low_bits_invert); - id_vector_temp_.push_back(block_stencil_op_invert.getId()); - id_vector_temp_.push_back(new_stencil_in_low_bits_increment_wrap); - id_vector_temp_.push_back(block_stencil_op_increment_wrap.getId()); - id_vector_temp_.push_back(new_stencil_in_low_bits_decrement_wrap); - id_vector_temp_.push_back(block_stencil_op_decrement_wrap.getId()); - spv::Id new_stencil_in_low_bits_if_enabled = - builder_->createOp(spv::OpPhi, type_uint_, id_vector_temp_); - // Merge the old depth / stencil (old depth kept from the old depth / - // stencil so the separate old depth register is not needed anymore after - // the depth test) and the new stencil based on the write mask. - spv::Id new_stencil_and_old_depth_if_stencil_enabled = - builder_->createBinOp( - spv::OpBitwiseOr, type_uint_, - builder_->createBinOp(spv::OpBitwiseAnd, type_uint_, - old_depth_stencil, stencil_write_keep_mask), - builder_->createBinOp(spv::OpBitwiseAnd, type_uint_, - new_stencil_in_low_bits_if_enabled, - stencil_write_mask)); - + stencil_if.makeEndIf(); // Choose the result based on whether the stencil test was done. // All phi operations must be the first in the block. - builder_->createBranch(&block_stencil_enabled_merge); - spv::Block& block_stencil_enabled_end = *builder_->getBuildPoint(); - builder_->setBuildPoint(&block_stencil_enabled_merge); - id_vector_temp_.clear(); - id_vector_temp_.push_back(stencil_passed_if_enabled); - id_vector_temp_.push_back(block_stencil_enabled_end.getId()); - id_vector_temp_.push_back(builder_->makeBoolConstant(true)); - id_vector_temp_.push_back(block_stencil_enabled_head.getId()); - spv::Id stencil_passed = - builder_->createOp(spv::OpPhi, type_bool_, id_vector_temp_); - id_vector_temp_.clear(); - id_vector_temp_.push_back(new_stencil_and_old_depth_if_stencil_enabled); - id_vector_temp_.push_back(block_stencil_enabled_end.getId()); - id_vector_temp_.push_back(old_depth_stencil); - id_vector_temp_.push_back(block_stencil_enabled_head.getId()); - spv::Id new_stencil_and_old_depth = - builder_->createOp(spv::OpPhi, type_uint_, id_vector_temp_); + spv::Id stencil_passed = stencil_if.createMergePhi( + stencil_passed_if_enabled, builder_->makeBoolConstant(true)); + spv::Id new_stencil_and_old_depth = stencil_if.createMergePhi( + new_stencil_and_old_depth_if_stencil_enabled, old_depth_stencil); // Check whether the tests have passed, and exclude the bit from the // coverage if not. @@ -2384,37 +2185,19 @@ void SpirvShaderTranslator::FSI_DepthStencilTest( new_depth_stencil_write_condition = new_depth_stencil_different; } if (new_depth_stencil_write_condition != spv::NoResult) { - spv::Block& block_depth_stencil_write = builder_->makeNewBlock(); - spv::Block& block_depth_stencil_write_merge = builder_->makeNewBlock(); - builder_->createSelectionMerge(&block_depth_stencil_write_merge, - spv::SelectionControlDontFlattenMask); - builder_->createConditionalBranch(new_depth_stencil_write_condition, - &block_depth_stencil_write, - &block_depth_stencil_write_merge); - builder_->setBuildPoint(&block_depth_stencil_write); + SpirvBuilder::IfBuilder new_depth_stencil_write_if( + new_depth_stencil_write_condition, + spv::SelectionControlDontFlattenMask, *builder_); builder_->createStore(new_depth_stencil, sample_access_chain); - builder_->createBranch(&block_depth_stencil_write_merge); - builder_->setBuildPoint(&block_depth_stencil_write_merge); + new_depth_stencil_write_if.makeEndIf(); } - builder_->createBranch(&block_sample_covered_merge); - spv::Block& block_sample_covered_end = *builder_->getBuildPoint(); - builder_->setBuildPoint(&block_sample_covered_merge); - id_vector_temp_.clear(); - id_vector_temp_.push_back(new_sample_mask_after_sample); - id_vector_temp_.push_back(block_sample_covered_end.getId()); - id_vector_temp_.push_back(new_sample_mask); - id_vector_temp_.push_back(block_sample_covered_head.getId()); - new_sample_mask = - builder_->createOp(spv::OpPhi, type_uint_, id_vector_temp_); + if_sample_covered.makeEndIf(); + new_sample_mask = if_sample_covered.createMergePhi( + new_sample_mask_after_sample, new_sample_mask); if (is_early) { - id_vector_temp_.clear(); - id_vector_temp_.push_back(new_depth_stencil); - id_vector_temp_.push_back(block_sample_covered_end.getId()); - id_vector_temp_.push_back(const_uint_0_); - id_vector_temp_.push_back(block_sample_covered_head.getId()); late_write_depth_stencil[i] = - builder_->createOp(spv::OpPhi, type_uint_, id_vector_temp_); + if_sample_covered.createMergePhi(new_depth_stencil, const_uint_0_); } } @@ -2442,25 +2225,14 @@ void SpirvShaderTranslator::FSI_DepthStencilTest( } } } - builder_->createBranch(&block_depth_stencil_enabled_merge); - spv::Block& block_depth_stencil_enabled_end = *builder_->getBuildPoint(); - builder_->setBuildPoint(&block_depth_stencil_enabled_merge); - id_vector_temp_.clear(); - id_vector_temp_.push_back(new_sample_mask); - id_vector_temp_.push_back(block_depth_stencil_enabled_end.getId()); - id_vector_temp_.push_back(main_fsi_sample_mask_); - id_vector_temp_.push_back(block_depth_stencil_enabled_head.getId()); - main_fsi_sample_mask_ = - builder_->createOp(spv::OpPhi, type_uint_, id_vector_temp_); + if_depth_stencil_enabled.makeEndIf(); + main_fsi_sample_mask_ = if_depth_stencil_enabled.createMergePhi( + new_sample_mask, main_fsi_sample_mask_); if (is_early) { for (uint32_t i = 0; i < 4; ++i) { - id_vector_temp_.clear(); - id_vector_temp_.push_back(late_write_depth_stencil[i]); - id_vector_temp_.push_back(block_depth_stencil_enabled_end.getId()); - id_vector_temp_.push_back(const_uint_0_); - id_vector_temp_.push_back(block_depth_stencil_enabled_head.getId()); main_fsi_late_write_depth_stencil_[i] = - builder_->createOp(spv::OpPhi, type_uint_, id_vector_temp_); + if_depth_stencil_enabled.createMergePhi(late_write_depth_stencil[i], + const_uint_0_); } } } @@ -3160,32 +2932,25 @@ spv::Id SpirvShaderTranslator::FSI_FlushNaNClampAndInBlending( assert_true(builder_->getTypeId(min_value) == color_or_alpha_type); assert_true(builder_->getTypeId(max_value) == color_or_alpha_type); - spv::Block& block_is_fixed_point_head = *builder_->getBuildPoint(); - spv::Block& block_is_fixed_point_if = builder_->makeNewBlock(); - spv::Block& block_is_fixed_point_merge = builder_->makeNewBlock(); - builder_->createSelectionMerge(&block_is_fixed_point_merge, - spv::SelectionControlDontFlattenMask); - builder_->createConditionalBranch(is_fixed_point, &block_is_fixed_point_if, - &block_is_fixed_point_merge); - builder_->setBuildPoint(&block_is_fixed_point_if); - // Flush NaN to 0 even for signed (NMax would flush it to the minimum value). - spv::Id color_or_alpha_clamped = builder_->createTriBuiltinCall( - color_or_alpha_type, ext_inst_glsl_std_450_, GLSLstd450FClamp, - builder_->createTriOp( - spv::OpSelect, color_or_alpha_type, - builder_->createUnaryOp(spv::OpIsNan, - type_bool_vectors_[component_count - 1], - color_or_alpha), - const_float_vectors_0_[component_count - 1], color_or_alpha), - min_value, max_value); - builder_->createBranch(&block_is_fixed_point_merge); - builder_->setBuildPoint(&block_is_fixed_point_merge); - id_vector_temp_.clear(); - id_vector_temp_.push_back(color_or_alpha_clamped); - id_vector_temp_.push_back(block_is_fixed_point_if.getId()); - id_vector_temp_.push_back(color_or_alpha); - id_vector_temp_.push_back(block_is_fixed_point_head.getId()); - return builder_->createOp(spv::OpPhi, color_or_alpha_type, id_vector_temp_); + SpirvBuilder::IfBuilder if_fixed_point( + is_fixed_point, spv::SelectionControlDontFlattenMask, *builder_); + spv::Id color_or_alpha_clamped; + { + // Flush NaN to 0 even for signed (NMax would flush it to the minimum + // value). + color_or_alpha_clamped = builder_->createTriBuiltinCall( + color_or_alpha_type, ext_inst_glsl_std_450_, GLSLstd450FClamp, + builder_->createTriOp( + spv::OpSelect, color_or_alpha_type, + builder_->createUnaryOp(spv::OpIsNan, + type_bool_vectors_[component_count - 1], + color_or_alpha), + const_float_vectors_0_[component_count - 1], color_or_alpha), + min_value, max_value); + } + if_fixed_point.makeEndIf(); + + return if_fixed_point.createMergePhi(color_or_alpha_clamped, color_or_alpha); } spv::Id SpirvShaderTranslator::FSI_ApplyColorBlendFactor( @@ -3197,21 +2962,14 @@ spv::Id SpirvShaderTranslator::FSI_ApplyColorBlendFactor( // infinity and NaN are not potentially involved in the multiplication. // Calculate the condition before the selection merge, which must be the // penultimate instruction in the block. - spv::Id factor_not_zero = builder_->createBinOp( - spv::OpINotEqual, type_bool_, factor, - builder_->makeUintConstant(uint32_t(xenos::BlendFactor::kZero))); - spv::Block& block_not_zero_head = *builder_->getBuildPoint(); - spv::Block& block_not_zero_if = builder_->makeNewBlock(); - spv::Block& block_not_zero_merge = builder_->makeNewBlock(); - builder_->createSelectionMerge(&block_not_zero_merge, - spv::SelectionControlDontFlattenMask); - builder_->createConditionalBranch(factor_not_zero, &block_not_zero_if, - &block_not_zero_merge); + SpirvBuilder::IfBuilder factor_not_zero_if( + builder_->createBinOp( + spv::OpINotEqual, type_bool_, factor, + builder_->makeUintConstant(uint32_t(xenos::BlendFactor::kZero))), + spv::SelectionControlDontFlattenMask, *builder_); // Non-zero factor case. - builder_->setBuildPoint(&block_not_zero_if); - spv::Block& block_factor_head = *builder_->getBuildPoint(); spv::Block& block_factor_one = builder_->makeNewBlock(); std::array color_factor_blocks; @@ -3386,18 +3144,11 @@ spv::Id SpirvShaderTranslator::FSI_ApplyColorBlendFactor( builder_->createOp(spv::OpPhi, type_float3_, id_vector_temp_); spv::Id result = FSI_FlushNaNClampAndInBlending( result_unclamped, is_fixed_point, clamp_min_value, clamp_max_value); - builder_->createBranch(&block_not_zero_merge); - // Get the latest block for a non-zero factor after all the control flow. - spv::Block& block_not_zero_if_end = *builder_->getBuildPoint(); + + factor_not_zero_if.makeEndIf(); // Make the result zero if the factor is zero. - builder_->setBuildPoint(&block_not_zero_merge); - id_vector_temp_.clear(); - id_vector_temp_.push_back(result); - id_vector_temp_.push_back(block_not_zero_if_end.getId()); - id_vector_temp_.push_back(const_float3_0_); - id_vector_temp_.push_back(block_not_zero_head.getId()); - return builder_->createOp(spv::OpPhi, type_float3_, id_vector_temp_); + return factor_not_zero_if.createMergePhi(result, const_float3_0_); } spv::Id SpirvShaderTranslator::FSI_ApplyAlphaBlendFactor( @@ -3408,21 +3159,14 @@ spv::Id SpirvShaderTranslator::FSI_ApplyAlphaBlendFactor( // infinity and NaN are not potentially involved in the multiplication. // Calculate the condition before the selection merge, which must be the // penultimate instruction in the block. - spv::Id factor_not_zero = builder_->createBinOp( - spv::OpINotEqual, type_bool_, factor, - builder_->makeUintConstant(uint32_t(xenos::BlendFactor::kZero))); - spv::Block& block_not_zero_head = *builder_->getBuildPoint(); - spv::Block& block_not_zero_if = builder_->makeNewBlock(); - spv::Block& block_not_zero_merge = builder_->makeNewBlock(); - builder_->createSelectionMerge(&block_not_zero_merge, - spv::SelectionControlDontFlattenMask); - builder_->createConditionalBranch(factor_not_zero, &block_not_zero_if, - &block_not_zero_merge); + SpirvBuilder::IfBuilder factor_not_zero_if( + builder_->createBinOp( + spv::OpINotEqual, type_bool_, factor, + builder_->makeUintConstant(uint32_t(xenos::BlendFactor::kZero))), + spv::SelectionControlDontFlattenMask, *builder_); // Non-zero factor case. - builder_->setBuildPoint(&block_not_zero_if); - spv::Block& block_factor_head = *builder_->getBuildPoint(); spv::Block& block_factor_one = builder_->makeNewBlock(); std::array alpha_factor_blocks; @@ -3557,18 +3301,11 @@ spv::Id SpirvShaderTranslator::FSI_ApplyAlphaBlendFactor( builder_->createOp(spv::OpPhi, type_float_, id_vector_temp_); spv::Id result = FSI_FlushNaNClampAndInBlending( result_unclamped, is_fixed_point, clamp_min_value, clamp_max_value); - builder_->createBranch(&block_not_zero_merge); - // Get the latest block for a non-zero factor after all the control flow. - spv::Block& block_not_zero_if_end = *builder_->getBuildPoint(); + + factor_not_zero_if.makeEndIf(); // Make the result zero if the factor is zero. - builder_->setBuildPoint(&block_not_zero_merge); - id_vector_temp_.clear(); - id_vector_temp_.push_back(result); - id_vector_temp_.push_back(block_not_zero_if_end.getId()); - id_vector_temp_.push_back(const_float_0_); - id_vector_temp_.push_back(block_not_zero_head.getId()); - return builder_->createOp(spv::OpPhi, type_float_, id_vector_temp_); + return factor_not_zero_if.createMergePhi(result, const_float_0_); } spv::Id SpirvShaderTranslator::FSI_BlendColorOrAlphaWithUnclampedResult( diff --git a/src/xenia/gpu/vulkan/vulkan_render_target_cache.cc b/src/xenia/gpu/vulkan/vulkan_render_target_cache.cc index bf1cda68d..8f7887b4e 100644 --- a/src/xenia/gpu/vulkan/vulkan_render_target_cache.cc +++ b/src/xenia/gpu/vulkan/vulkan_render_target_cache.cc @@ -4156,21 +4156,16 @@ VkShaderModule VulkanRenderTargetCache::GetTransferShader( builder.createAccessChain(spv::StorageClassPushConstant, push_constants, id_vector_temp), spv::NoPrecision); - spv::Id stencil_sample_passed = builder.createBinOp( - spv::OpINotEqual, type_bool, - builder.createBinOp(spv::OpBitwiseAnd, type_uint, packed, - stencil_mask_constant), - builder.makeUintConstant(0)); - spv::Block& stencil_bit_kill_block = builder.makeNewBlock(); - spv::Block& stencil_bit_merge_block = builder.makeNewBlock(); - builder.createSelectionMerge(&stencil_bit_merge_block, - spv::SelectionControlMaskNone); - builder.createConditionalBranch(stencil_sample_passed, - &stencil_bit_merge_block, - &stencil_bit_kill_block); - builder.setBuildPoint(&stencil_bit_kill_block); + SpirvBuilder::IfBuilder stencil_kill_if( + builder.createBinOp( + spv::OpIEqual, type_bool, + builder.createBinOp(spv::OpBitwiseAnd, type_uint, packed, + stencil_mask_constant), + builder.makeUintConstant(0)), + spv::SelectionControlMaskNone, builder); builder.createNoResultOp(spv::OpKill); - builder.setBuildPoint(&stencil_bit_merge_block); + // OpKill terminates the block. + stencil_kill_if.makeEndIf(false); } } break; }