summaryrefslogtreecommitdiff
path: root/src/shader_recompiler/ir_opt
diff options
context:
space:
mode:
Diffstat (limited to 'src/shader_recompiler/ir_opt')
-rw-r--r--src/shader_recompiler/ir_opt/collect_shader_info_pass.cpp3
-rw-r--r--src/shader_recompiler/ir_opt/dead_code_elimination_pass.cpp98
-rw-r--r--src/shader_recompiler/ir_opt/passes.h7
-rw-r--r--src/shader_recompiler/ir_opt/position_pass.cpp77
-rw-r--r--src/shader_recompiler/ir_opt/texture_pass.cpp51
5 files changed, 145 insertions, 91 deletions
diff --git a/src/shader_recompiler/ir_opt/collect_shader_info_pass.cpp b/src/shader_recompiler/ir_opt/collect_shader_info_pass.cpp
index 7cff8ecdc..5a4195217 100644
--- a/src/shader_recompiler/ir_opt/collect_shader_info_pass.cpp
+++ b/src/shader_recompiler/ir_opt/collect_shader_info_pass.cpp
@@ -468,6 +468,9 @@ void VisitUsages(Info& info, IR::Inst& inst) {
case IR::Opcode::InvocationId:
info.uses_invocation_id = true;
break;
+ case IR::Opcode::InvocationInfo:
+ info.uses_invocation_info = true;
+ break;
case IR::Opcode::SampleId:
info.uses_sample_id = true;
break;
diff --git a/src/shader_recompiler/ir_opt/dead_code_elimination_pass.cpp b/src/shader_recompiler/ir_opt/dead_code_elimination_pass.cpp
index 9a7d47344..1bd8afd6f 100644
--- a/src/shader_recompiler/ir_opt/dead_code_elimination_pass.cpp
+++ b/src/shader_recompiler/ir_opt/dead_code_elimination_pass.cpp
@@ -1,104 +1,24 @@
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
-#include <algorithm>
-
-#include <boost/container/small_vector.hpp>
-
#include "shader_recompiler/frontend/ir/basic_block.h"
#include "shader_recompiler/frontend/ir/value.h"
#include "shader_recompiler/ir_opt/passes.h"
namespace Shader::Optimization {
-namespace {
-template <bool TEST_USES>
-void DeadInstElimination(IR::Block* const block) {
+
+void DeadCodeEliminationPass(IR::Program& program) {
// We iterate over the instructions in reverse order.
// This is because removing an instruction reduces the number of uses for earlier instructions.
- auto it{block->end()};
- while (it != block->begin()) {
- --it;
- if constexpr (TEST_USES) {
- if (it->HasUses() || it->MayHaveSideEffects()) {
- continue;
- }
- }
- it->Invalidate();
- it = block->Instructions().erase(it);
- }
-}
-
-void DeletedPhiArgElimination(IR::Program& program, std::span<const IR::Block*> dead_blocks) {
- for (IR::Block* const block : program.blocks) {
- for (IR::Inst& phi : *block) {
- if (!IR::IsPhi(phi)) {
- continue;
- }
- for (size_t i = 0; i < phi.NumArgs(); ++i) {
- if (std::ranges::find(dead_blocks, phi.PhiBlock(i)) == dead_blocks.end()) {
- continue;
- }
- // Phi operand at this index is an unreachable block
- phi.ErasePhiOperand(i);
- --i;
- }
- }
- }
-}
-
-void DeadBranchElimination(IR::Program& program) {
- boost::container::small_vector<const IR::Block*, 3> dead_blocks;
- const auto begin_it{program.syntax_list.begin()};
- for (auto node_it = begin_it; node_it != program.syntax_list.end(); ++node_it) {
- if (node_it->type != IR::AbstractSyntaxNode::Type::If) {
- continue;
- }
- IR::Inst* const cond_ref{node_it->data.if_node.cond.Inst()};
- const IR::U1 cond{cond_ref->Arg(0)};
- if (!cond.IsImmediate()) {
- continue;
- }
- if (cond.U1()) {
- continue;
- }
- // False immediate condition. Remove condition ref, erase the entire branch.
- cond_ref->Invalidate();
- // Account for nested if-statements within the if(false) branch
- u32 nested_ifs{1u};
- while (node_it->type != IR::AbstractSyntaxNode::Type::EndIf || nested_ifs > 0) {
- node_it = program.syntax_list.erase(node_it);
- switch (node_it->type) {
- case IR::AbstractSyntaxNode::Type::If:
- ++nested_ifs;
- break;
- case IR::AbstractSyntaxNode::Type::EndIf:
- --nested_ifs;
- break;
- case IR::AbstractSyntaxNode::Type::Block: {
- IR::Block* const block{node_it->data.block};
- DeadInstElimination<false>(block);
- dead_blocks.push_back(block);
- break;
- }
- default:
- break;
+ for (IR::Block* const block : program.post_order_blocks) {
+ auto it{block->end()};
+ while (it != block->begin()) {
+ --it;
+ if (!it->HasUses() && !it->MayHaveSideEffects()) {
+ it->Invalidate();
+ it = block->Instructions().erase(it);
}
}
- // Erase EndIf node of the if(false) branch
- node_it = program.syntax_list.erase(node_it);
- // Account for loop increment
- --node_it;
- }
- if (!dead_blocks.empty()) {
- DeletedPhiArgElimination(program, std::span(dead_blocks.data(), dead_blocks.size()));
- }
-}
-} // namespace
-
-void DeadCodeEliminationPass(IR::Program& program) {
- DeadBranchElimination(program);
- for (IR::Block* const block : program.post_order_blocks) {
- DeadInstElimination<true>(block);
}
}
diff --git a/src/shader_recompiler/ir_opt/passes.h b/src/shader_recompiler/ir_opt/passes.h
index 6ff8e4266..586a0668f 100644
--- a/src/shader_recompiler/ir_opt/passes.h
+++ b/src/shader_recompiler/ir_opt/passes.h
@@ -6,6 +6,10 @@
#include "shader_recompiler/environment.h"
#include "shader_recompiler/frontend/ir/program.h"
+namespace Shader {
+struct HostTranslateInfo;
+}
+
namespace Shader::Optimization {
void CollectShaderInfoPass(Environment& env, IR::Program& program);
@@ -17,7 +21,8 @@ void LowerFp16ToFp32(IR::Program& program);
void LowerInt64ToInt32(IR::Program& program);
void RescalingPass(IR::Program& program);
void SsaRewritePass(IR::Program& program);
-void TexturePass(Environment& env, IR::Program& program);
+void PositionPass(Environment& env, IR::Program& program);
+void TexturePass(Environment& env, IR::Program& program, const HostTranslateInfo& host_info);
void VerificationPass(const IR::Program& program);
// Dual Vertex
diff --git a/src/shader_recompiler/ir_opt/position_pass.cpp b/src/shader_recompiler/ir_opt/position_pass.cpp
new file mode 100644
index 000000000..3c20b7189
--- /dev/null
+++ b/src/shader_recompiler/ir_opt/position_pass.cpp
@@ -0,0 +1,77 @@
+// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#include <boost/container/small_vector.hpp>
+
+#include "shader_recompiler/frontend/ir/basic_block.h"
+#include "shader_recompiler/frontend/ir/ir_emitter.h"
+#include "shader_recompiler/frontend/ir/value.h"
+#include "shader_recompiler/ir_opt/passes.h"
+
+namespace Shader::Optimization {
+
+namespace {
+struct PositionInst {
+ IR::Inst* inst;
+ IR::Block* block;
+ IR::Attribute attr;
+};
+using PositionInstVector = boost::container::small_vector<PositionInst, 24>;
+} // Anonymous namespace
+
+void PositionPass(Environment& env, IR::Program& program) {
+ if (env.ShaderStage() != Stage::VertexB || env.ReadViewportTransformState()) {
+ return;
+ }
+
+ Info& info{program.info};
+ info.uses_render_area = true;
+
+ PositionInstVector to_replace;
+ for (IR::Block* const block : program.post_order_blocks) {
+ for (IR::Inst& inst : block->Instructions()) {
+ switch (inst.GetOpcode()) {
+ case IR::Opcode::SetAttribute: {
+ const IR::Attribute attr{inst.Arg(0).Attribute()};
+ switch (attr) {
+ case IR::Attribute::PositionX:
+ case IR::Attribute::PositionY: {
+ to_replace.push_back(PositionInst{.inst = &inst, .block = block, .attr = attr});
+ break;
+ }
+ default:
+ break;
+ }
+ break;
+ }
+ default:
+ break;
+ }
+ }
+ }
+
+ for (PositionInst& position_inst : to_replace) {
+ IR::IREmitter ir{*position_inst.block,
+ IR::Block::InstructionList::s_iterator_to(*position_inst.inst)};
+ const IR::F32 value(position_inst.inst->Arg(1));
+ const IR::F32F64 scale(ir.Imm32(2.f));
+ const IR::F32 negative_one{ir.Imm32(-1.f)};
+ switch (position_inst.attr) {
+ case IR::Attribute::PositionX: {
+ position_inst.inst->SetArg(
+ 1,
+ ir.FPFma(value, ir.FPMul(ir.FPRecip(ir.RenderAreaWidth()), scale), negative_one));
+ break;
+ }
+ case IR::Attribute::PositionY: {
+ position_inst.inst->SetArg(
+ 1,
+ ir.FPFma(value, ir.FPMul(ir.FPRecip(ir.RenderAreaHeight()), scale), negative_one));
+ break;
+ }
+ default:
+ break;
+ }
+ }
+}
+} // namespace Shader::Optimization
diff --git a/src/shader_recompiler/ir_opt/texture_pass.cpp b/src/shader_recompiler/ir_opt/texture_pass.cpp
index e8be58357..f5c86fcb1 100644
--- a/src/shader_recompiler/ir_opt/texture_pass.cpp
+++ b/src/shader_recompiler/ir_opt/texture_pass.cpp
@@ -11,6 +11,7 @@
#include "shader_recompiler/frontend/ir/basic_block.h"
#include "shader_recompiler/frontend/ir/breadth_first_search.h"
#include "shader_recompiler/frontend/ir/ir_emitter.h"
+#include "shader_recompiler/host_translate_info.h"
#include "shader_recompiler/ir_opt/passes.h"
#include "shader_recompiler/shader_info.h"
@@ -363,6 +364,14 @@ TextureType ReadTextureType(Environment& env, const ConstBufferAddr& cbuf) {
return env.ReadTextureType(lhs_raw | rhs_raw);
}
+TexturePixelFormat ReadTexturePixelFormat(Environment& env, const ConstBufferAddr& cbuf) {
+ const u32 secondary_index{cbuf.has_secondary ? cbuf.secondary_index : cbuf.index};
+ const u32 secondary_offset{cbuf.has_secondary ? cbuf.secondary_offset : cbuf.offset};
+ const u32 lhs_raw{env.ReadCbufValue(cbuf.index, cbuf.offset)};
+ const u32 rhs_raw{env.ReadCbufValue(secondary_index, secondary_offset)};
+ return env.ReadTexturePixelFormat(lhs_raw | rhs_raw);
+}
+
class Descriptors {
public:
explicit Descriptors(TextureBufferDescriptors& texture_buffer_descriptors_,
@@ -451,9 +460,41 @@ void PatchImageSampleImplicitLod(IR::Block& block, IR::Inst& inst) {
ir.FPMul(IR::F32(ir.CompositeExtract(coord, 1)),
ir.FPRecip(ir.ConvertUToF(32, 32, ir.CompositeExtract(texture_size, 1))))));
}
+
+void PatchTexelFetch(IR::Block& block, IR::Inst& inst, TexturePixelFormat pixel_format) {
+ const auto it{IR::Block::InstructionList::s_iterator_to(inst)};
+ IR::IREmitter ir{block, IR::Block::InstructionList::s_iterator_to(inst)};
+ auto get_max_value = [pixel_format]() -> float {
+ switch (pixel_format) {
+ case TexturePixelFormat::A8B8G8R8_SNORM:
+ case TexturePixelFormat::R8G8_SNORM:
+ case TexturePixelFormat::R8_SNORM:
+ return 1.f / std::numeric_limits<char>::max();
+ case TexturePixelFormat::R16G16B16A16_SNORM:
+ case TexturePixelFormat::R16G16_SNORM:
+ case TexturePixelFormat::R16_SNORM:
+ return 1.f / std::numeric_limits<short>::max();
+ default:
+ throw InvalidArgument("Invalid texture pixel format");
+ }
+ };
+
+ const IR::Value new_inst{&*block.PrependNewInst(it, inst)};
+ const IR::F32 x(ir.CompositeExtract(new_inst, 0));
+ const IR::F32 y(ir.CompositeExtract(new_inst, 1));
+ const IR::F32 z(ir.CompositeExtract(new_inst, 2));
+ const IR::F32 w(ir.CompositeExtract(new_inst, 3));
+ const IR::F16F32F64 max_value(ir.Imm32(get_max_value()));
+ const IR::Value converted =
+ ir.CompositeConstruct(ir.FPMul(ir.ConvertSToF(32, 32, ir.BitCast<IR::S32>(x)), max_value),
+ ir.FPMul(ir.ConvertSToF(32, 32, ir.BitCast<IR::S32>(y)), max_value),
+ ir.FPMul(ir.ConvertSToF(32, 32, ir.BitCast<IR::S32>(z)), max_value),
+ ir.FPMul(ir.ConvertSToF(32, 32, ir.BitCast<IR::S32>(w)), max_value));
+ inst.ReplaceUsesWith(converted);
+}
} // Anonymous namespace
-void TexturePass(Environment& env, IR::Program& program) {
+void TexturePass(Environment& env, IR::Program& program, const HostTranslateInfo& host_info) {
TextureInstVector to_replace;
for (IR::Block* const block : program.post_order_blocks) {
for (IR::Inst& inst : block->Instructions()) {
@@ -597,6 +638,14 @@ void TexturePass(Environment& env, IR::Program& program) {
} else {
inst->SetArg(0, IR::Value{});
}
+
+ if (!host_info.support_snorm_render_buffer && inst->GetOpcode() == IR::Opcode::ImageFetch &&
+ flags.type == TextureType::Buffer) {
+ const auto pixel_format = ReadTexturePixelFormat(env, cbuf);
+ if (pixel_format != TexturePixelFormat::OTHER) {
+ PatchTexelFetch(*texture_inst.block, *texture_inst.inst, pixel_format);
+ }
+ }
}
}