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; }