diff options
Diffstat (limited to 'src')
21 files changed, 437 insertions, 48 deletions
diff --git a/src/shader_recompiler/CMakeLists.txt b/src/shader_recompiler/CMakeLists.txt index 003cbefb1..44ab929b7 100644 --- a/src/shader_recompiler/CMakeLists.txt +++ b/src/shader_recompiler/CMakeLists.txt | |||
| @@ -52,6 +52,8 @@ add_library(shader_recompiler STATIC | |||
| 52 | frontend/maxwell/control_flow.h | 52 | frontend/maxwell/control_flow.h |
| 53 | frontend/maxwell/decode.cpp | 53 | frontend/maxwell/decode.cpp |
| 54 | frontend/maxwell/decode.h | 54 | frontend/maxwell/decode.h |
| 55 | frontend/maxwell/indirect_branch_table_track.cpp | ||
| 56 | frontend/maxwell/indirect_branch_table_track.h | ||
| 55 | frontend/maxwell/instruction.h | 57 | frontend/maxwell/instruction.h |
| 56 | frontend/maxwell/location.h | 58 | frontend/maxwell/location.h |
| 57 | frontend/maxwell/maxwell.inc | 59 | frontend/maxwell/maxwell.inc |
| @@ -63,6 +65,7 @@ add_library(shader_recompiler STATIC | |||
| 63 | frontend/maxwell/structured_control_flow.h | 65 | frontend/maxwell/structured_control_flow.h |
| 64 | frontend/maxwell/translate/impl/bitfield_extract.cpp | 66 | frontend/maxwell/translate/impl/bitfield_extract.cpp |
| 65 | frontend/maxwell/translate/impl/bitfield_insert.cpp | 67 | frontend/maxwell/translate/impl/bitfield_insert.cpp |
| 68 | frontend/maxwell/translate/impl/branch_indirect.cpp | ||
| 66 | frontend/maxwell/translate/impl/common_encoding.h | 69 | frontend/maxwell/translate/impl/common_encoding.h |
| 67 | frontend/maxwell/translate/impl/common_funcs.cpp | 70 | frontend/maxwell/translate/impl/common_funcs.cpp |
| 68 | frontend/maxwell/translate/impl/common_funcs.h | 71 | frontend/maxwell/translate/impl/common_funcs.h |
| @@ -110,6 +113,7 @@ add_library(shader_recompiler STATIC | |||
| 110 | frontend/maxwell/translate/impl/integer_short_multiply_add.cpp | 113 | frontend/maxwell/translate/impl/integer_short_multiply_add.cpp |
| 111 | frontend/maxwell/translate/impl/integer_to_integer_conversion.cpp | 114 | frontend/maxwell/translate/impl/integer_to_integer_conversion.cpp |
| 112 | frontend/maxwell/translate/impl/load_constant.cpp | 115 | frontend/maxwell/translate/impl/load_constant.cpp |
| 116 | frontend/maxwell/translate/impl/load_constant.h | ||
| 113 | frontend/maxwell/translate/impl/load_effective_address.cpp | 117 | frontend/maxwell/translate/impl/load_effective_address.cpp |
| 114 | frontend/maxwell/translate/impl/load_store_attribute.cpp | 118 | frontend/maxwell/translate/impl/load_store_attribute.cpp |
| 115 | frontend/maxwell/translate/impl/load_store_local_shared.cpp | 119 | frontend/maxwell/translate/impl/load_store_local_shared.cpp |
diff --git a/src/shader_recompiler/backend/spirv/emit_spirv.h b/src/shader_recompiler/backend/spirv/emit_spirv.h index 204c5f9e0..02648d769 100644 --- a/src/shader_recompiler/backend/spirv/emit_spirv.h +++ b/src/shader_recompiler/backend/spirv/emit_spirv.h | |||
| @@ -26,6 +26,7 @@ void EmitBranchConditional(EmitContext& ctx, Id condition, Id true_label, Id fal | |||
| 26 | void EmitLoopMerge(EmitContext& ctx, Id merge_label, Id continue_label); | 26 | void EmitLoopMerge(EmitContext& ctx, Id merge_label, Id continue_label); |
| 27 | void EmitSelectionMerge(EmitContext& ctx, Id merge_label); | 27 | void EmitSelectionMerge(EmitContext& ctx, Id merge_label); |
| 28 | void EmitReturn(EmitContext& ctx); | 28 | void EmitReturn(EmitContext& ctx); |
| 29 | void EmitUnreachable(EmitContext& ctx); | ||
| 29 | void EmitDemoteToHelperInvocation(EmitContext& ctx, Id continue_label); | 30 | void EmitDemoteToHelperInvocation(EmitContext& ctx, Id continue_label); |
| 30 | void EmitPrologue(EmitContext& ctx); | 31 | void EmitPrologue(EmitContext& ctx); |
| 31 | void EmitEpilogue(EmitContext& ctx); | 32 | void EmitEpilogue(EmitContext& ctx); |
| @@ -35,6 +36,8 @@ void EmitGetPred(EmitContext& ctx); | |||
| 35 | void EmitSetPred(EmitContext& ctx); | 36 | void EmitSetPred(EmitContext& ctx); |
| 36 | void EmitSetGotoVariable(EmitContext& ctx); | 37 | void EmitSetGotoVariable(EmitContext& ctx); |
| 37 | void EmitGetGotoVariable(EmitContext& ctx); | 38 | void EmitGetGotoVariable(EmitContext& ctx); |
| 39 | void EmitSetIndirectBranchVariable(EmitContext& ctx); | ||
| 40 | void EmitGetIndirectBranchVariable(EmitContext& ctx); | ||
| 38 | Id EmitGetCbufU8(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset); | 41 | Id EmitGetCbufU8(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset); |
| 39 | Id EmitGetCbufS8(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset); | 42 | Id EmitGetCbufS8(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset); |
| 40 | Id EmitGetCbufU16(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset); | 43 | Id EmitGetCbufU16(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset); |
diff --git a/src/shader_recompiler/backend/spirv/emit_spirv_context_get_set.cpp b/src/shader_recompiler/backend/spirv/emit_spirv_context_get_set.cpp index 52dcef8a4..4a267b16c 100644 --- a/src/shader_recompiler/backend/spirv/emit_spirv_context_get_set.cpp +++ b/src/shader_recompiler/backend/spirv/emit_spirv_context_get_set.cpp | |||
| @@ -6,8 +6,6 @@ | |||
| 6 | 6 | ||
| 7 | #include "shader_recompiler/backend/spirv/emit_spirv.h" | 7 | #include "shader_recompiler/backend/spirv/emit_spirv.h" |
| 8 | 8 | ||
| 9 | #pragma optimize("", off) | ||
| 10 | |||
| 11 | namespace Shader::Backend::SPIRV { | 9 | namespace Shader::Backend::SPIRV { |
| 12 | namespace { | 10 | namespace { |
| 13 | struct AttrInfo { | 11 | struct AttrInfo { |
| @@ -74,6 +72,14 @@ void EmitGetGotoVariable(EmitContext&) { | |||
| 74 | throw NotImplementedException("SPIR-V Instruction"); | 72 | throw NotImplementedException("SPIR-V Instruction"); |
| 75 | } | 73 | } |
| 76 | 74 | ||
| 75 | void EmitSetIndirectBranchVariable(EmitContext&) { | ||
| 76 | throw NotImplementedException("SPIR-V Instruction"); | ||
| 77 | } | ||
| 78 | |||
| 79 | void EmitGetIndirectBranchVariable(EmitContext&) { | ||
| 80 | throw NotImplementedException("SPIR-V Instruction"); | ||
| 81 | } | ||
| 82 | |||
| 77 | static Id GetCbuf(EmitContext& ctx, Id result_type, Id UniformDefinitions::*member_ptr, | 83 | static Id GetCbuf(EmitContext& ctx, Id result_type, Id UniformDefinitions::*member_ptr, |
| 78 | u32 element_size, const IR::Value& binding, const IR::Value& offset) { | 84 | u32 element_size, const IR::Value& binding, const IR::Value& offset) { |
| 79 | if (!binding.IsImmediate()) { | 85 | if (!binding.IsImmediate()) { |
diff --git a/src/shader_recompiler/backend/spirv/emit_spirv_control_flow.cpp b/src/shader_recompiler/backend/spirv/emit_spirv_control_flow.cpp index 6b81f0169..335603f88 100644 --- a/src/shader_recompiler/backend/spirv/emit_spirv_control_flow.cpp +++ b/src/shader_recompiler/backend/spirv/emit_spirv_control_flow.cpp | |||
| @@ -26,6 +26,10 @@ void EmitReturn(EmitContext& ctx) { | |||
| 26 | ctx.OpReturn(); | 26 | ctx.OpReturn(); |
| 27 | } | 27 | } |
| 28 | 28 | ||
| 29 | void EmitUnreachable(EmitContext& ctx) { | ||
| 30 | ctx.OpUnreachable(); | ||
| 31 | } | ||
| 32 | |||
| 29 | void EmitDemoteToHelperInvocation(EmitContext& ctx, Id continue_label) { | 33 | void EmitDemoteToHelperInvocation(EmitContext& ctx, Id continue_label) { |
| 30 | ctx.OpDemoteToHelperInvocationEXT(); | 34 | ctx.OpDemoteToHelperInvocationEXT(); |
| 31 | ctx.OpBranch(continue_label); | 35 | ctx.OpBranch(continue_label); |
diff --git a/src/shader_recompiler/environment.h b/src/shader_recompiler/environment.h index 9415d02f6..1c50ae51e 100644 --- a/src/shader_recompiler/environment.h +++ b/src/shader_recompiler/environment.h | |||
| @@ -15,6 +15,8 @@ public: | |||
| 15 | 15 | ||
| 16 | [[nodiscard]] virtual u64 ReadInstruction(u32 address) = 0; | 16 | [[nodiscard]] virtual u64 ReadInstruction(u32 address) = 0; |
| 17 | 17 | ||
| 18 | [[nodiscard]] virtual u32 ReadCbufValue(u32 cbuf_index, u32 cbuf_offset) = 0; | ||
| 19 | |||
| 18 | [[nodiscard]] virtual TextureType ReadTextureType(u32 cbuf_index, u32 cbuf_offset) = 0; | 20 | [[nodiscard]] virtual TextureType ReadTextureType(u32 cbuf_index, u32 cbuf_offset) = 0; |
| 19 | 21 | ||
| 20 | [[nodiscard]] virtual u32 TextureBoundBuffer() const = 0; | 22 | [[nodiscard]] virtual u32 TextureBoundBuffer() const = 0; |
diff --git a/src/shader_recompiler/frontend/ir/ir_emitter.cpp b/src/shader_recompiler/frontend/ir/ir_emitter.cpp index 9b898e4e1..552472487 100644 --- a/src/shader_recompiler/frontend/ir/ir_emitter.cpp +++ b/src/shader_recompiler/frontend/ir/ir_emitter.cpp | |||
| @@ -87,6 +87,10 @@ void IREmitter::Return() { | |||
| 87 | Inst(Opcode::Return); | 87 | Inst(Opcode::Return); |
| 88 | } | 88 | } |
| 89 | 89 | ||
| 90 | void IREmitter::Unreachable() { | ||
| 91 | Inst(Opcode::Unreachable); | ||
| 92 | } | ||
| 93 | |||
| 90 | void IREmitter::DemoteToHelperInvocation(Block* continue_label) { | 94 | void IREmitter::DemoteToHelperInvocation(Block* continue_label) { |
| 91 | block->SetBranch(continue_label); | 95 | block->SetBranch(continue_label); |
| 92 | continue_label->AddImmediatePredecessor(block); | 96 | continue_label->AddImmediatePredecessor(block); |
| @@ -126,6 +130,14 @@ void IREmitter::SetGotoVariable(u32 id, const U1& value) { | |||
| 126 | Inst(Opcode::SetGotoVariable, id, value); | 130 | Inst(Opcode::SetGotoVariable, id, value); |
| 127 | } | 131 | } |
| 128 | 132 | ||
| 133 | U32 IREmitter::GetIndirectBranchVariable() { | ||
| 134 | return Inst<U32>(Opcode::GetIndirectBranchVariable); | ||
| 135 | } | ||
| 136 | |||
| 137 | void IREmitter::SetIndirectBranchVariable(const U32& value) { | ||
| 138 | Inst(Opcode::SetIndirectBranchVariable, value); | ||
| 139 | } | ||
| 140 | |||
| 129 | void IREmitter::SetPred(IR::Pred pred, const U1& value) { | 141 | void IREmitter::SetPred(IR::Pred pred, const U1& value) { |
| 130 | Inst(Opcode::SetPred, pred, value); | 142 | Inst(Opcode::SetPred, pred, value); |
| 131 | } | 143 | } |
diff --git a/src/shader_recompiler/frontend/ir/ir_emitter.h b/src/shader_recompiler/frontend/ir/ir_emitter.h index 269f367a4..17bc32fc8 100644 --- a/src/shader_recompiler/frontend/ir/ir_emitter.h +++ b/src/shader_recompiler/frontend/ir/ir_emitter.h | |||
| @@ -37,6 +37,7 @@ public: | |||
| 37 | void LoopMerge(Block* merge_block, Block* continue_target); | 37 | void LoopMerge(Block* merge_block, Block* continue_target); |
| 38 | void SelectionMerge(Block* merge_block); | 38 | void SelectionMerge(Block* merge_block); |
| 39 | void Return(); | 39 | void Return(); |
| 40 | void Unreachable(); | ||
| 40 | void DemoteToHelperInvocation(Block* continue_label); | 41 | void DemoteToHelperInvocation(Block* continue_label); |
| 41 | 42 | ||
| 42 | void Prologue(); | 43 | void Prologue(); |
| @@ -51,6 +52,9 @@ public: | |||
| 51 | [[nodiscard]] U1 GetGotoVariable(u32 id); | 52 | [[nodiscard]] U1 GetGotoVariable(u32 id); |
| 52 | void SetGotoVariable(u32 id, const U1& value); | 53 | void SetGotoVariable(u32 id, const U1& value); |
| 53 | 54 | ||
| 55 | [[nodiscard]] U32 GetIndirectBranchVariable(); | ||
| 56 | void SetIndirectBranchVariable(const U32& value); | ||
| 57 | |||
| 54 | [[nodiscard]] U32 GetCbuf(const U32& binding, const U32& byte_offset); | 58 | [[nodiscard]] U32 GetCbuf(const U32& binding, const U32& byte_offset); |
| 55 | [[nodiscard]] UAny GetCbuf(const U32& binding, const U32& byte_offset, size_t bitsize, | 59 | [[nodiscard]] UAny GetCbuf(const U32& binding, const U32& byte_offset, size_t bitsize, |
| 56 | bool is_signed); | 60 | bool is_signed); |
diff --git a/src/shader_recompiler/frontend/ir/microinstruction.cpp b/src/shader_recompiler/frontend/ir/microinstruction.cpp index 52a5e5034..c3ba6b522 100644 --- a/src/shader_recompiler/frontend/ir/microinstruction.cpp +++ b/src/shader_recompiler/frontend/ir/microinstruction.cpp | |||
| @@ -55,6 +55,7 @@ bool Inst::MayHaveSideEffects() const noexcept { | |||
| 55 | case Opcode::LoopMerge: | 55 | case Opcode::LoopMerge: |
| 56 | case Opcode::SelectionMerge: | 56 | case Opcode::SelectionMerge: |
| 57 | case Opcode::Return: | 57 | case Opcode::Return: |
| 58 | case Opcode::Unreachable: | ||
| 58 | case Opcode::DemoteToHelperInvocation: | 59 | case Opcode::DemoteToHelperInvocation: |
| 59 | case Opcode::Prologue: | 60 | case Opcode::Prologue: |
| 60 | case Opcode::Epilogue: | 61 | case Opcode::Epilogue: |
diff --git a/src/shader_recompiler/frontend/ir/opcodes.inc b/src/shader_recompiler/frontend/ir/opcodes.inc index 9b050995b..fb79e3d8d 100644 --- a/src/shader_recompiler/frontend/ir/opcodes.inc +++ b/src/shader_recompiler/frontend/ir/opcodes.inc | |||
| @@ -13,6 +13,7 @@ OPCODE(BranchConditional, Void, U1, | |||
| 13 | OPCODE(LoopMerge, Void, Label, Label, ) | 13 | OPCODE(LoopMerge, Void, Label, Label, ) |
| 14 | OPCODE(SelectionMerge, Void, Label, ) | 14 | OPCODE(SelectionMerge, Void, Label, ) |
| 15 | OPCODE(Return, Void, ) | 15 | OPCODE(Return, Void, ) |
| 16 | OPCODE(Unreachable, Void, ) | ||
| 16 | OPCODE(DemoteToHelperInvocation, Void, Label, ) | 17 | OPCODE(DemoteToHelperInvocation, Void, Label, ) |
| 17 | 18 | ||
| 18 | // Special operations | 19 | // Special operations |
| @@ -26,6 +27,8 @@ OPCODE(GetPred, U1, Pred | |||
| 26 | OPCODE(SetPred, Void, Pred, U1, ) | 27 | OPCODE(SetPred, Void, Pred, U1, ) |
| 27 | OPCODE(GetGotoVariable, U1, U32, ) | 28 | OPCODE(GetGotoVariable, U1, U32, ) |
| 28 | OPCODE(SetGotoVariable, Void, U32, U1, ) | 29 | OPCODE(SetGotoVariable, Void, U32, U1, ) |
| 30 | OPCODE(GetIndirectBranchVariable, U32, ) | ||
| 31 | OPCODE(SetIndirectBranchVariable, Void, U32, ) | ||
| 29 | OPCODE(GetCbufU8, U32, U32, U32, ) | 32 | OPCODE(GetCbufU8, U32, U32, U32, ) |
| 30 | OPCODE(GetCbufS8, U32, U32, U32, ) | 33 | OPCODE(GetCbufS8, U32, U32, U32, ) |
| 31 | OPCODE(GetCbufU16, U32, U32, U32, ) | 34 | OPCODE(GetCbufU16, U32, U32, U32, ) |
diff --git a/src/shader_recompiler/frontend/maxwell/control_flow.cpp b/src/shader_recompiler/frontend/maxwell/control_flow.cpp index 4f6707fae..1e9b8e426 100644 --- a/src/shader_recompiler/frontend/maxwell/control_flow.cpp +++ b/src/shader_recompiler/frontend/maxwell/control_flow.cpp | |||
| @@ -14,6 +14,7 @@ | |||
| 14 | #include "shader_recompiler/exception.h" | 14 | #include "shader_recompiler/exception.h" |
| 15 | #include "shader_recompiler/frontend/maxwell/control_flow.h" | 15 | #include "shader_recompiler/frontend/maxwell/control_flow.h" |
| 16 | #include "shader_recompiler/frontend/maxwell/decode.h" | 16 | #include "shader_recompiler/frontend/maxwell/decode.h" |
| 17 | #include "shader_recompiler/frontend/maxwell/indirect_branch_table_track.h" | ||
| 17 | #include "shader_recompiler/frontend/maxwell/location.h" | 18 | #include "shader_recompiler/frontend/maxwell/location.h" |
| 18 | 19 | ||
| 19 | namespace Shader::Maxwell::Flow { | 20 | namespace Shader::Maxwell::Flow { |
| @@ -252,9 +253,7 @@ CFG::AnalysisState CFG::AnalyzeInst(Block* block, FunctionId function_id, Locati | |||
| 252 | const Opcode opcode{Decode(inst.raw)}; | 253 | const Opcode opcode{Decode(inst.raw)}; |
| 253 | switch (opcode) { | 254 | switch (opcode) { |
| 254 | case Opcode::BRA: | 255 | case Opcode::BRA: |
| 255 | case Opcode::BRX: | ||
| 256 | case Opcode::JMP: | 256 | case Opcode::JMP: |
| 257 | case Opcode::JMX: | ||
| 258 | case Opcode::RET: | 257 | case Opcode::RET: |
| 259 | if (!AnalyzeBranch(block, function_id, pc, inst, opcode)) { | 258 | if (!AnalyzeBranch(block, function_id, pc, inst, opcode)) { |
| 260 | return AnalysisState::Continue; | 259 | return AnalysisState::Continue; |
| @@ -264,10 +263,6 @@ CFG::AnalysisState CFG::AnalyzeInst(Block* block, FunctionId function_id, Locati | |||
| 264 | case Opcode::JMP: | 263 | case Opcode::JMP: |
| 265 | AnalyzeBRA(block, function_id, pc, inst, IsAbsoluteJump(opcode)); | 264 | AnalyzeBRA(block, function_id, pc, inst, IsAbsoluteJump(opcode)); |
| 266 | break; | 265 | break; |
| 267 | case Opcode::BRX: | ||
| 268 | case Opcode::JMX: | ||
| 269 | AnalyzeBRX(block, pc, inst, IsAbsoluteJump(opcode)); | ||
| 270 | break; | ||
| 271 | case Opcode::RET: | 266 | case Opcode::RET: |
| 272 | block->end_class = EndClass::Return; | 267 | block->end_class = EndClass::Return; |
| 273 | break; | 268 | break; |
| @@ -302,6 +297,9 @@ CFG::AnalysisState CFG::AnalyzeInst(Block* block, FunctionId function_id, Locati | |||
| 302 | case Opcode::SSY: | 297 | case Opcode::SSY: |
| 303 | block->stack.Push(OpcodeToken(opcode), BranchOffset(pc, inst)); | 298 | block->stack.Push(OpcodeToken(opcode), BranchOffset(pc, inst)); |
| 304 | return AnalysisState::Continue; | 299 | return AnalysisState::Continue; |
| 300 | case Opcode::BRX: | ||
| 301 | case Opcode::JMX: | ||
| 302 | return AnalyzeBRX(block, pc, inst, IsAbsoluteJump(opcode), function_id); | ||
| 305 | case Opcode::EXIT: | 303 | case Opcode::EXIT: |
| 306 | return AnalyzeEXIT(block, function_id, pc, inst); | 304 | return AnalyzeEXIT(block, function_id, pc, inst); |
| 307 | case Opcode::PRET: | 305 | case Opcode::PRET: |
| @@ -407,8 +405,46 @@ void CFG::AnalyzeBRA(Block* block, FunctionId function_id, Location pc, Instruct | |||
| 407 | block->branch_true = AddLabel(block, block->stack, bra_pc, function_id); | 405 | block->branch_true = AddLabel(block, block->stack, bra_pc, function_id); |
| 408 | } | 406 | } |
| 409 | 407 | ||
| 410 | void CFG::AnalyzeBRX(Block*, Location, Instruction, bool is_absolute) { | 408 | CFG::AnalysisState CFG::AnalyzeBRX(Block* block, Location pc, Instruction inst, bool is_absolute, |
| 411 | throw NotImplementedException("{}", is_absolute ? "JMX" : "BRX"); | 409 | FunctionId function_id) { |
| 410 | const std::optional brx_table{TrackIndirectBranchTable(env, pc, block->begin)}; | ||
| 411 | if (!brx_table) { | ||
| 412 | TrackIndirectBranchTable(env, pc, block->begin); | ||
| 413 | throw NotImplementedException("Failed to track indirect branch"); | ||
| 414 | } | ||
| 415 | const IR::FlowTest flow_test{inst.branch.flow_test}; | ||
| 416 | const Predicate pred{inst.Pred()}; | ||
| 417 | if (flow_test != IR::FlowTest::T || pred != Predicate{true}) { | ||
| 418 | throw NotImplementedException("Conditional indirect branch"); | ||
| 419 | } | ||
| 420 | std::vector<u32> targets; | ||
| 421 | targets.reserve(brx_table->num_entries); | ||
| 422 | for (u32 i = 0; i < brx_table->num_entries; ++i) { | ||
| 423 | u32 target{env.ReadCbufValue(brx_table->cbuf_index, brx_table->cbuf_offset + i * 4)}; | ||
| 424 | if (!is_absolute) { | ||
| 425 | target += pc.Offset(); | ||
| 426 | } | ||
| 427 | target += brx_table->branch_offset; | ||
| 428 | target += 8; | ||
| 429 | targets.push_back(target); | ||
| 430 | } | ||
| 431 | std::ranges::sort(targets); | ||
| 432 | targets.erase(std::unique(targets.begin(), targets.end()), targets.end()); | ||
| 433 | |||
| 434 | block->indirect_branches.reserve(targets.size()); | ||
| 435 | for (const u32 target : targets) { | ||
| 436 | Block* const branch{AddLabel(block, block->stack, target, function_id)}; | ||
| 437 | block->indirect_branches.push_back(branch); | ||
| 438 | } | ||
| 439 | block->cond = IR::Condition{true}; | ||
| 440 | block->end = pc + 1; | ||
| 441 | block->end_class = EndClass::IndirectBranch; | ||
| 442 | block->branch_reg = brx_table->branch_reg; | ||
| 443 | block->branch_offset = brx_table->branch_offset + 8; | ||
| 444 | if (!is_absolute) { | ||
| 445 | block->branch_offset += pc.Offset(); | ||
| 446 | } | ||
| 447 | return AnalysisState::Branch; | ||
| 412 | } | 448 | } |
| 413 | 449 | ||
| 414 | CFG::AnalysisState CFG::AnalyzeEXIT(Block* block, FunctionId function_id, Location pc, | 450 | CFG::AnalysisState CFG::AnalyzeEXIT(Block* block, FunctionId function_id, Location pc, |
| @@ -449,7 +485,6 @@ Block* CFG::AddLabel(Block* block, Stack stack, Location pc, FunctionId function | |||
| 449 | // Block already exists and it has been visited | 485 | // Block already exists and it has been visited |
| 450 | return &*it; | 486 | return &*it; |
| 451 | } | 487 | } |
| 452 | // TODO: FIX DANGLING BLOCKS | ||
| 453 | Block* const new_block{block_pool.Create(Block{ | 488 | Block* const new_block{block_pool.Create(Block{ |
| 454 | .begin{pc}, | 489 | .begin{pc}, |
| 455 | .end{pc}, | 490 | .end{pc}, |
| @@ -494,6 +529,11 @@ std::string CFG::Dot() const { | |||
| 494 | add_branch(block.branch_false, false); | 529 | add_branch(block.branch_false, false); |
| 495 | } | 530 | } |
| 496 | break; | 531 | break; |
| 532 | case EndClass::IndirectBranch: | ||
| 533 | for (Block* const branch : block.indirect_branches) { | ||
| 534 | add_branch(branch, false); | ||
| 535 | } | ||
| 536 | break; | ||
| 497 | case EndClass::Call: | 537 | case EndClass::Call: |
| 498 | dot += fmt::format("\t\t{}->N{};\n", name, node_uid); | 538 | dot += fmt::format("\t\t{}->N{};\n", name, node_uid); |
| 499 | dot += fmt::format("\t\tN{}->{};\n", node_uid, NameOf(*block.return_block)); | 539 | dot += fmt::format("\t\tN{}->{};\n", node_uid, NameOf(*block.return_block)); |
diff --git a/src/shader_recompiler/frontend/maxwell/control_flow.h b/src/shader_recompiler/frontend/maxwell/control_flow.h index 22f134194..1e05fcb97 100644 --- a/src/shader_recompiler/frontend/maxwell/control_flow.h +++ b/src/shader_recompiler/frontend/maxwell/control_flow.h | |||
| @@ -26,6 +26,7 @@ using FunctionId = size_t; | |||
| 26 | 26 | ||
| 27 | enum class EndClass { | 27 | enum class EndClass { |
| 28 | Branch, | 28 | Branch, |
| 29 | IndirectBranch, | ||
| 29 | Call, | 30 | Call, |
| 30 | Exit, | 31 | Exit, |
| 31 | Return, | 32 | Return, |
| @@ -76,11 +77,14 @@ struct Block : boost::intrusive::set_base_hook< | |||
| 76 | union { | 77 | union { |
| 77 | Block* branch_true; | 78 | Block* branch_true; |
| 78 | FunctionId function_call; | 79 | FunctionId function_call; |
| 80 | IR::Reg branch_reg; | ||
| 79 | }; | 81 | }; |
| 80 | union { | 82 | union { |
| 81 | Block* branch_false; | 83 | Block* branch_false; |
| 82 | Block* return_block; | 84 | Block* return_block; |
| 85 | s32 branch_offset; | ||
| 83 | }; | 86 | }; |
| 87 | std::vector<Block*> indirect_branches; | ||
| 84 | }; | 88 | }; |
| 85 | 89 | ||
| 86 | struct Label { | 90 | struct Label { |
| @@ -139,7 +143,8 @@ private: | |||
| 139 | 143 | ||
| 140 | void AnalyzeBRA(Block* block, FunctionId function_id, Location pc, Instruction inst, | 144 | void AnalyzeBRA(Block* block, FunctionId function_id, Location pc, Instruction inst, |
| 141 | bool is_absolute); | 145 | bool is_absolute); |
| 142 | void AnalyzeBRX(Block* block, Location pc, Instruction inst, bool is_absolute); | 146 | AnalysisState AnalyzeBRX(Block* block, Location pc, Instruction inst, bool is_absolute, |
| 147 | FunctionId function_id); | ||
| 143 | AnalysisState AnalyzeEXIT(Block* block, FunctionId function_id, Location pc, Instruction inst); | 148 | AnalysisState AnalyzeEXIT(Block* block, FunctionId function_id, Location pc, Instruction inst); |
| 144 | 149 | ||
| 145 | /// Return the branch target block id | 150 | /// Return the branch target block id |
diff --git a/src/shader_recompiler/frontend/maxwell/indirect_branch_table_track.cpp b/src/shader_recompiler/frontend/maxwell/indirect_branch_table_track.cpp new file mode 100644 index 000000000..96453509d --- /dev/null +++ b/src/shader_recompiler/frontend/maxwell/indirect_branch_table_track.cpp | |||
| @@ -0,0 +1,108 @@ | |||
| 1 | // Copyright 2021 yuzu Emulator Project | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #include <optional> | ||
| 6 | |||
| 7 | #include "common/common_types.h" | ||
| 8 | #include "shader_recompiler/exception.h" | ||
| 9 | #include "shader_recompiler/frontend/maxwell/decode.h" | ||
| 10 | #include "shader_recompiler/frontend/maxwell/indirect_branch_table_track.h" | ||
| 11 | #include "shader_recompiler/frontend/maxwell/opcodes.h" | ||
| 12 | #include "shader_recompiler/frontend/maxwell/translate/impl/load_constant.h" | ||
| 13 | |||
| 14 | namespace Shader::Maxwell { | ||
| 15 | namespace { | ||
| 16 | union Encoding { | ||
| 17 | u64 raw; | ||
| 18 | BitField<0, 8, IR::Reg> dest_reg; | ||
| 19 | BitField<8, 8, IR::Reg> src_reg; | ||
| 20 | BitField<20, 19, u64> immediate; | ||
| 21 | BitField<56, 1, u64> is_negative; | ||
| 22 | BitField<20, 24, s64> brx_offset; | ||
| 23 | }; | ||
| 24 | |||
| 25 | template <typename Callable> | ||
| 26 | std::optional<u64> Track(Environment& env, Location block_begin, Location& pos, Callable&& func) { | ||
| 27 | while (pos >= block_begin) { | ||
| 28 | const u64 insn{env.ReadInstruction(pos.Offset())}; | ||
| 29 | --pos; | ||
| 30 | if (func(insn, Decode(insn))) { | ||
| 31 | return insn; | ||
| 32 | } | ||
| 33 | } | ||
| 34 | return std::nullopt; | ||
| 35 | } | ||
| 36 | |||
| 37 | std::optional<u64> TrackLDC(Environment& env, Location block_begin, Location& pos, | ||
| 38 | IR::Reg brx_reg) { | ||
| 39 | return Track(env, block_begin, pos, [brx_reg](u64 insn, Opcode opcode) { | ||
| 40 | const LDC::Encoding ldc{insn}; | ||
| 41 | return opcode == Opcode::LDC && ldc.dest_reg == brx_reg && ldc.size == LDC::Size::B32 && | ||
| 42 | ldc.mode == LDC::Mode::Default; | ||
| 43 | }); | ||
| 44 | } | ||
| 45 | |||
| 46 | std::optional<u64> TrackSHL(Environment& env, Location block_begin, Location& pos, | ||
| 47 | IR::Reg ldc_reg) { | ||
| 48 | return Track(env, block_begin, pos, [ldc_reg](u64 insn, Opcode opcode) { | ||
| 49 | const Encoding shl{insn}; | ||
| 50 | return opcode == Opcode::SHL_imm && shl.dest_reg == ldc_reg; | ||
| 51 | }); | ||
| 52 | } | ||
| 53 | |||
| 54 | std::optional<u64> TrackIMNMX(Environment& env, Location block_begin, Location& pos, | ||
| 55 | IR::Reg shl_reg) { | ||
| 56 | return Track(env, block_begin, pos, [shl_reg](u64 insn, Opcode opcode) { | ||
| 57 | const Encoding imnmx{insn}; | ||
| 58 | return opcode == Opcode::IMNMX_imm && imnmx.dest_reg == shl_reg; | ||
| 59 | }); | ||
| 60 | } | ||
| 61 | } // Anonymous namespace | ||
| 62 | |||
| 63 | std::optional<IndirectBranchTableInfo> TrackIndirectBranchTable(Environment& env, Location brx_pos, | ||
| 64 | Location block_begin) { | ||
| 65 | const u64 brx_insn{env.ReadInstruction(brx_pos.Offset())}; | ||
| 66 | const Opcode brx_opcode{Decode(brx_insn)}; | ||
| 67 | if (brx_opcode != Opcode::BRX && brx_opcode != Opcode::JMX) { | ||
| 68 | throw LogicError("Tracked instruction is not BRX or JMX"); | ||
| 69 | } | ||
| 70 | const IR::Reg brx_reg{Encoding{brx_insn}.src_reg}; | ||
| 71 | const s32 brx_offset{static_cast<s32>(Encoding{brx_insn}.brx_offset)}; | ||
| 72 | |||
| 73 | Location pos{brx_pos}; | ||
| 74 | const std::optional<u64> ldc_insn{TrackLDC(env, block_begin, pos, brx_reg)}; | ||
| 75 | if (!ldc_insn) { | ||
| 76 | return std::nullopt; | ||
| 77 | } | ||
| 78 | const LDC::Encoding ldc{*ldc_insn}; | ||
| 79 | const u32 cbuf_index{static_cast<u32>(ldc.index)}; | ||
| 80 | const u32 cbuf_offset{static_cast<u32>(static_cast<s32>(ldc.offset.Value()))}; | ||
| 81 | const IR::Reg ldc_reg{ldc.src_reg}; | ||
| 82 | |||
| 83 | const std::optional<u64> shl_insn{TrackSHL(env, block_begin, pos, ldc_reg)}; | ||
| 84 | if (!shl_insn) { | ||
| 85 | return std::nullopt; | ||
| 86 | } | ||
| 87 | const Encoding shl{*shl_insn}; | ||
| 88 | const IR::Reg shl_reg{shl.src_reg}; | ||
| 89 | |||
| 90 | const std::optional<u64> imnmx_insn{TrackIMNMX(env, block_begin, pos, shl_reg)}; | ||
| 91 | if (!imnmx_insn) { | ||
| 92 | return std::nullopt; | ||
| 93 | } | ||
| 94 | const Encoding imnmx{*imnmx_insn}; | ||
| 95 | if (imnmx.is_negative != 0) { | ||
| 96 | return std::nullopt; | ||
| 97 | } | ||
| 98 | const u32 imnmx_immediate{static_cast<u32>(imnmx.immediate.Value())}; | ||
| 99 | return IndirectBranchTableInfo{ | ||
| 100 | .cbuf_index{cbuf_index}, | ||
| 101 | .cbuf_offset{cbuf_offset}, | ||
| 102 | .num_entries{imnmx_immediate + 1}, | ||
| 103 | .branch_offset{brx_offset}, | ||
| 104 | .branch_reg{brx_reg}, | ||
| 105 | }; | ||
| 106 | } | ||
| 107 | |||
| 108 | } // namespace Shader::Maxwell | ||
diff --git a/src/shader_recompiler/frontend/maxwell/indirect_branch_table_track.h b/src/shader_recompiler/frontend/maxwell/indirect_branch_table_track.h new file mode 100644 index 000000000..eee5102fa --- /dev/null +++ b/src/shader_recompiler/frontend/maxwell/indirect_branch_table_track.h | |||
| @@ -0,0 +1,28 @@ | |||
| 1 | // Copyright 2021 yuzu Emulator Project | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #pragma once | ||
| 6 | |||
| 7 | #include <optional> | ||
| 8 | |||
| 9 | #include "common/bit_field.h" | ||
| 10 | #include "common/common_types.h" | ||
| 11 | #include "shader_recompiler/environment.h" | ||
| 12 | #include "shader_recompiler/frontend/ir/reg.h" | ||
| 13 | #include "shader_recompiler/frontend/maxwell/location.h" | ||
| 14 | |||
| 15 | namespace Shader::Maxwell { | ||
| 16 | |||
| 17 | struct IndirectBranchTableInfo { | ||
| 18 | u32 cbuf_index{}; | ||
| 19 | u32 cbuf_offset{}; | ||
| 20 | u32 num_entries{}; | ||
| 21 | s32 branch_offset{}; | ||
| 22 | IR::Reg branch_reg{}; | ||
| 23 | }; | ||
| 24 | |||
| 25 | std::optional<IndirectBranchTableInfo> TrackIndirectBranchTable(Environment& env, Location brx_pos, | ||
| 26 | Location block_begin); | ||
| 27 | |||
| 28 | } // namespace Shader::Maxwell | ||
diff --git a/src/shader_recompiler/frontend/maxwell/instruction.h b/src/shader_recompiler/frontend/maxwell/instruction.h index 57fd531f2..743d68d61 100644 --- a/src/shader_recompiler/frontend/maxwell/instruction.h +++ b/src/shader_recompiler/frontend/maxwell/instruction.h | |||
| @@ -7,6 +7,7 @@ | |||
| 7 | #include "common/bit_field.h" | 7 | #include "common/bit_field.h" |
| 8 | #include "common/common_types.h" | 8 | #include "common/common_types.h" |
| 9 | #include "shader_recompiler/frontend/ir/flow_test.h" | 9 | #include "shader_recompiler/frontend/ir/flow_test.h" |
| 10 | #include "shader_recompiler/frontend/ir/reg.h" | ||
| 10 | 11 | ||
| 11 | namespace Shader::Maxwell { | 12 | namespace Shader::Maxwell { |
| 12 | 13 | ||
diff --git a/src/shader_recompiler/frontend/maxwell/structured_control_flow.cpp b/src/shader_recompiler/frontend/maxwell/structured_control_flow.cpp index 9d4688390..a6e55f61e 100644 --- a/src/shader_recompiler/frontend/maxwell/structured_control_flow.cpp +++ b/src/shader_recompiler/frontend/maxwell/structured_control_flow.cpp | |||
| @@ -17,6 +17,7 @@ | |||
| 17 | #include "shader_recompiler/environment.h" | 17 | #include "shader_recompiler/environment.h" |
| 18 | #include "shader_recompiler/frontend/ir/basic_block.h" | 18 | #include "shader_recompiler/frontend/ir/basic_block.h" |
| 19 | #include "shader_recompiler/frontend/ir/ir_emitter.h" | 19 | #include "shader_recompiler/frontend/ir/ir_emitter.h" |
| 20 | #include "shader_recompiler/frontend/maxwell/decode.h" | ||
| 20 | #include "shader_recompiler/frontend/maxwell/structured_control_flow.h" | 21 | #include "shader_recompiler/frontend/maxwell/structured_control_flow.h" |
| 21 | #include "shader_recompiler/frontend/maxwell/translate/translate.h" | 22 | #include "shader_recompiler/frontend/maxwell/translate/translate.h" |
| 22 | #include "shader_recompiler/object_pool.h" | 23 | #include "shader_recompiler/object_pool.h" |
| @@ -46,12 +47,15 @@ enum class StatementType { | |||
| 46 | Break, | 47 | Break, |
| 47 | Return, | 48 | Return, |
| 48 | Kill, | 49 | Kill, |
| 50 | Unreachable, | ||
| 49 | Function, | 51 | Function, |
| 50 | Identity, | 52 | Identity, |
| 51 | Not, | 53 | Not, |
| 52 | Or, | 54 | Or, |
| 53 | SetVariable, | 55 | SetVariable, |
| 56 | SetIndirectBranchVariable, | ||
| 54 | Variable, | 57 | Variable, |
| 58 | IndirectBranchCond, | ||
| 55 | }; | 59 | }; |
| 56 | 60 | ||
| 57 | bool HasChildren(StatementType type) { | 61 | bool HasChildren(StatementType type) { |
| @@ -72,12 +76,15 @@ struct Loop {}; | |||
| 72 | struct Break {}; | 76 | struct Break {}; |
| 73 | struct Return {}; | 77 | struct Return {}; |
| 74 | struct Kill {}; | 78 | struct Kill {}; |
| 79 | struct Unreachable {}; | ||
| 75 | struct FunctionTag {}; | 80 | struct FunctionTag {}; |
| 76 | struct Identity {}; | 81 | struct Identity {}; |
| 77 | struct Not {}; | 82 | struct Not {}; |
| 78 | struct Or {}; | 83 | struct Or {}; |
| 79 | struct SetVariable {}; | 84 | struct SetVariable {}; |
| 85 | struct SetIndirectBranchVariable {}; | ||
| 80 | struct Variable {}; | 86 | struct Variable {}; |
| 87 | struct IndirectBranchCond {}; | ||
| 81 | 88 | ||
| 82 | #ifdef _MSC_VER | 89 | #ifdef _MSC_VER |
| 83 | #pragma warning(push) | 90 | #pragma warning(push) |
| @@ -96,6 +103,7 @@ struct Statement : ListBaseHook { | |||
| 96 | : cond{cond_}, up{up_}, type{StatementType::Break} {} | 103 | : cond{cond_}, up{up_}, type{StatementType::Break} {} |
| 97 | Statement(Return) : type{StatementType::Return} {} | 104 | Statement(Return) : type{StatementType::Return} {} |
| 98 | Statement(Kill) : type{StatementType::Kill} {} | 105 | Statement(Kill) : type{StatementType::Kill} {} |
| 106 | Statement(Unreachable) : type{StatementType::Unreachable} {} | ||
| 99 | Statement(FunctionTag) : children{}, type{StatementType::Function} {} | 107 | Statement(FunctionTag) : children{}, type{StatementType::Function} {} |
| 100 | Statement(Identity, IR::Condition cond_) : guest_cond{cond_}, type{StatementType::Identity} {} | 108 | Statement(Identity, IR::Condition cond_) : guest_cond{cond_}, type{StatementType::Identity} {} |
| 101 | Statement(Not, Statement* op_) : op{op_}, type{StatementType::Not} {} | 109 | Statement(Not, Statement* op_) : op{op_}, type{StatementType::Not} {} |
| @@ -103,7 +111,12 @@ struct Statement : ListBaseHook { | |||
| 103 | : op_a{op_a_}, op_b{op_b_}, type{StatementType::Or} {} | 111 | : op_a{op_a_}, op_b{op_b_}, type{StatementType::Or} {} |
| 104 | Statement(SetVariable, u32 id_, Statement* op_, Statement* up_) | 112 | Statement(SetVariable, u32 id_, Statement* op_, Statement* up_) |
| 105 | : op{op_}, id{id_}, up{up_}, type{StatementType::SetVariable} {} | 113 | : op{op_}, id{id_}, up{up_}, type{StatementType::SetVariable} {} |
| 114 | Statement(SetIndirectBranchVariable, IR::Reg branch_reg_, s32 branch_offset_) | ||
| 115 | : branch_offset{branch_offset_}, | ||
| 116 | branch_reg{branch_reg_}, type{StatementType::SetIndirectBranchVariable} {} | ||
| 106 | Statement(Variable, u32 id_) : id{id_}, type{StatementType::Variable} {} | 117 | Statement(Variable, u32 id_) : id{id_}, type{StatementType::Variable} {} |
| 118 | Statement(IndirectBranchCond, u32 location_) | ||
| 119 | : location{location_}, type{StatementType::IndirectBranchCond} {} | ||
| 107 | 120 | ||
| 108 | ~Statement() { | 121 | ~Statement() { |
| 109 | if (HasChildren(type)) { | 122 | if (HasChildren(type)) { |
| @@ -118,11 +131,14 @@ struct Statement : ListBaseHook { | |||
| 118 | IR::Condition guest_cond; | 131 | IR::Condition guest_cond; |
| 119 | Statement* op; | 132 | Statement* op; |
| 120 | Statement* op_a; | 133 | Statement* op_a; |
| 134 | u32 location; | ||
| 135 | s32 branch_offset; | ||
| 121 | }; | 136 | }; |
| 122 | union { | 137 | union { |
| 123 | Statement* cond; | 138 | Statement* cond; |
| 124 | Statement* op_b; | 139 | Statement* op_b; |
| 125 | u32 id; | 140 | u32 id; |
| 141 | IR::Reg branch_reg; | ||
| 126 | }; | 142 | }; |
| 127 | Statement* up{}; | 143 | Statement* up{}; |
| 128 | StatementType type; | 144 | StatementType type; |
| @@ -141,6 +157,8 @@ std::string DumpExpr(const Statement* stmt) { | |||
| 141 | return fmt::format("{} || {}", DumpExpr(stmt->op_a), DumpExpr(stmt->op_b)); | 157 | return fmt::format("{} || {}", DumpExpr(stmt->op_a), DumpExpr(stmt->op_b)); |
| 142 | case StatementType::Variable: | 158 | case StatementType::Variable: |
| 143 | return fmt::format("goto_L{}", stmt->id); | 159 | return fmt::format("goto_L{}", stmt->id); |
| 160 | case StatementType::IndirectBranchCond: | ||
| 161 | return fmt::format("(indirect_branch == {:x})", stmt->location); | ||
| 144 | default: | 162 | default: |
| 145 | return "<invalid type>"; | 163 | return "<invalid type>"; |
| 146 | } | 164 | } |
| @@ -182,14 +200,22 @@ std::string DumpTree(const Tree& tree, u32 indentation = 0) { | |||
| 182 | case StatementType::Kill: | 200 | case StatementType::Kill: |
| 183 | ret += fmt::format("{} kill;\n", indent); | 201 | ret += fmt::format("{} kill;\n", indent); |
| 184 | break; | 202 | break; |
| 203 | case StatementType::Unreachable: | ||
| 204 | ret += fmt::format("{} unreachable;\n", indent); | ||
| 205 | break; | ||
| 185 | case StatementType::SetVariable: | 206 | case StatementType::SetVariable: |
| 186 | ret += fmt::format("{} goto_L{} = {};\n", indent, stmt->id, DumpExpr(stmt->op)); | 207 | ret += fmt::format("{} goto_L{} = {};\n", indent, stmt->id, DumpExpr(stmt->op)); |
| 187 | break; | 208 | break; |
| 209 | case StatementType::SetIndirectBranchVariable: | ||
| 210 | ret += fmt::format("{} indirect_branch = {} + {};\n", indent, stmt->branch_reg, | ||
| 211 | stmt->branch_offset); | ||
| 212 | break; | ||
| 188 | case StatementType::Function: | 213 | case StatementType::Function: |
| 189 | case StatementType::Identity: | 214 | case StatementType::Identity: |
| 190 | case StatementType::Not: | 215 | case StatementType::Not: |
| 191 | case StatementType::Or: | 216 | case StatementType::Or: |
| 192 | case StatementType::Variable: | 217 | case StatementType::Variable: |
| 218 | case StatementType::IndirectBranchCond: | ||
| 193 | throw LogicError("Statement can't be printed"); | 219 | throw LogicError("Statement can't be printed"); |
| 194 | } | 220 | } |
| 195 | } | 221 | } |
| @@ -417,6 +443,17 @@ private: | |||
| 417 | } | 443 | } |
| 418 | break; | 444 | break; |
| 419 | } | 445 | } |
| 446 | case Flow::EndClass::IndirectBranch: | ||
| 447 | root.insert(ip, *pool.Create(SetIndirectBranchVariable{}, block.branch_reg, | ||
| 448 | block.branch_offset)); | ||
| 449 | for (Flow::Block* const branch : block.indirect_branches) { | ||
| 450 | const Node indirect_label{local_labels.at(branch)}; | ||
| 451 | Statement* cond{pool.Create(IndirectBranchCond{}, branch->begin.Offset())}; | ||
| 452 | Statement* goto_stmt{pool.Create(Goto{}, cond, indirect_label, &root_stmt)}; | ||
| 453 | gotos.push_back(root.insert(ip, *goto_stmt)); | ||
| 454 | } | ||
| 455 | root.insert(ip, *pool.Create(Unreachable{})); | ||
| 456 | break; | ||
| 420 | case Flow::EndClass::Call: { | 457 | case Flow::EndClass::Call: { |
| 421 | Flow::Function& call{cfg.Functions()[block.function_call]}; | 458 | Flow::Function& call{cfg.Functions()[block.function_call]}; |
| 422 | const Node call_return_label{local_labels.at(block.return_block)}; | 459 | const Node call_return_label{local_labels.at(block.return_block)}; |
| @@ -623,6 +660,8 @@ IR::Block* TryFindForwardBlock(const Statement& stmt) { | |||
| 623 | return ir.LogicalOr(VisitExpr(ir, *stmt.op_a), VisitExpr(ir, *stmt.op_b)); | 660 | return ir.LogicalOr(VisitExpr(ir, *stmt.op_a), VisitExpr(ir, *stmt.op_b)); |
| 624 | case StatementType::Variable: | 661 | case StatementType::Variable: |
| 625 | return ir.GetGotoVariable(stmt.id); | 662 | return ir.GetGotoVariable(stmt.id); |
| 663 | case StatementType::IndirectBranchCond: | ||
| 664 | return ir.IEqual(ir.GetIndirectBranchVariable(), ir.Imm32(stmt.location)); | ||
| 626 | default: | 665 | default: |
| 627 | throw NotImplementedException("Statement type {}", stmt.type); | 666 | throw NotImplementedException("Statement type {}", stmt.type); |
| 628 | } | 667 | } |
| @@ -670,6 +709,15 @@ private: | |||
| 670 | ir.SetGotoVariable(stmt.id, VisitExpr(ir, *stmt.op)); | 709 | ir.SetGotoVariable(stmt.id, VisitExpr(ir, *stmt.op)); |
| 671 | break; | 710 | break; |
| 672 | } | 711 | } |
| 712 | case StatementType::SetIndirectBranchVariable: { | ||
| 713 | if (!current_block) { | ||
| 714 | current_block = MergeBlock(parent, stmt); | ||
| 715 | } | ||
| 716 | IR::IREmitter ir{*current_block}; | ||
| 717 | IR::U32 address{ir.IAdd(ir.GetReg(stmt.branch_reg), ir.Imm32(stmt.branch_offset))}; | ||
| 718 | ir.SetIndirectBranchVariable(address); | ||
| 719 | break; | ||
| 720 | } | ||
| 673 | case StatementType::If: { | 721 | case StatementType::If: { |
| 674 | if (!current_block) { | 722 | if (!current_block) { |
| 675 | current_block = block_pool.Create(inst_pool); | 723 | current_block = block_pool.Create(inst_pool); |
| @@ -756,6 +804,15 @@ private: | |||
| 756 | current_block = demote_block; | 804 | current_block = demote_block; |
| 757 | break; | 805 | break; |
| 758 | } | 806 | } |
| 807 | case StatementType::Unreachable: { | ||
| 808 | if (!current_block) { | ||
| 809 | current_block = block_pool.Create(inst_pool); | ||
| 810 | block_list.push_back(current_block); | ||
| 811 | } | ||
| 812 | IR::IREmitter{*current_block}.Unreachable(); | ||
| 813 | current_block = nullptr; | ||
| 814 | break; | ||
| 815 | } | ||
| 759 | default: | 816 | default: |
| 760 | throw NotImplementedException("Statement type {}", stmt.type); | 817 | throw NotImplementedException("Statement type {}", stmt.type); |
| 761 | } | 818 | } |
diff --git a/src/shader_recompiler/frontend/maxwell/translate/impl/branch_indirect.cpp b/src/shader_recompiler/frontend/maxwell/translate/impl/branch_indirect.cpp new file mode 100644 index 000000000..371c0e0f7 --- /dev/null +++ b/src/shader_recompiler/frontend/maxwell/translate/impl/branch_indirect.cpp | |||
| @@ -0,0 +1,36 @@ | |||
| 1 | // Copyright 2021 yuzu Emulator Project | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #include "common/bit_field.h" | ||
| 6 | #include "common/common_types.h" | ||
| 7 | #include "shader_recompiler/exception.h" | ||
| 8 | #include "shader_recompiler/frontend/maxwell/translate/impl/impl.h" | ||
| 9 | |||
| 10 | namespace Shader::Maxwell { | ||
| 11 | namespace { | ||
| 12 | void Check(u64 insn) { | ||
| 13 | union { | ||
| 14 | u64 raw; | ||
| 15 | BitField<5, 1, u64> cbuf_mode; | ||
| 16 | BitField<6, 1, u64> lmt; | ||
| 17 | } const encoding{insn}; | ||
| 18 | |||
| 19 | if (encoding.cbuf_mode != 0) { | ||
| 20 | throw NotImplementedException("Constant buffer mode"); | ||
| 21 | } | ||
| 22 | if (encoding.lmt != 0) { | ||
| 23 | throw NotImplementedException("LMT"); | ||
| 24 | } | ||
| 25 | } | ||
| 26 | } // Anonymous namespace | ||
| 27 | |||
| 28 | void TranslatorVisitor::BRX(u64 insn) { | ||
| 29 | Check(insn); | ||
| 30 | } | ||
| 31 | |||
| 32 | void TranslatorVisitor::JMX(u64 insn) { | ||
| 33 | Check(insn); | ||
| 34 | } | ||
| 35 | |||
| 36 | } // namespace Shader::Maxwell | ||
diff --git a/src/shader_recompiler/frontend/maxwell/translate/impl/load_constant.cpp b/src/shader_recompiler/frontend/maxwell/translate/impl/load_constant.cpp index 39becf93c..49ccb7d62 100644 --- a/src/shader_recompiler/frontend/maxwell/translate/impl/load_constant.cpp +++ b/src/shader_recompiler/frontend/maxwell/translate/impl/load_constant.cpp | |||
| @@ -5,25 +5,11 @@ | |||
| 5 | #include "common/bit_field.h" | 5 | #include "common/bit_field.h" |
| 6 | #include "common/common_types.h" | 6 | #include "common/common_types.h" |
| 7 | #include "shader_recompiler/frontend/maxwell/translate/impl/impl.h" | 7 | #include "shader_recompiler/frontend/maxwell/translate/impl/impl.h" |
| 8 | #include "shader_recompiler/frontend/maxwell/translate/impl/load_constant.h" | ||
| 8 | 9 | ||
| 9 | namespace Shader::Maxwell { | 10 | namespace Shader::Maxwell { |
| 11 | using namespace LDC; | ||
| 10 | namespace { | 12 | namespace { |
| 11 | enum class Mode : u64 { | ||
| 12 | Default, | ||
| 13 | IL, | ||
| 14 | IS, | ||
| 15 | ISL, | ||
| 16 | }; | ||
| 17 | |||
| 18 | enum class Size : u64 { | ||
| 19 | U8, | ||
| 20 | S8, | ||
| 21 | U16, | ||
| 22 | S16, | ||
| 23 | B32, | ||
| 24 | B64, | ||
| 25 | }; | ||
| 26 | |||
| 27 | std::pair<IR::U32, IR::U32> Slot(IR::IREmitter& ir, Mode mode, const IR::U32& imm_index, | 13 | std::pair<IR::U32, IR::U32> Slot(IR::IREmitter& ir, Mode mode, const IR::U32& imm_index, |
| 28 | const IR::U32& reg, const IR::U32& imm) { | 14 | const IR::U32& reg, const IR::U32& imm) { |
| 29 | switch (mode) { | 15 | switch (mode) { |
| @@ -37,16 +23,7 @@ std::pair<IR::U32, IR::U32> Slot(IR::IREmitter& ir, Mode mode, const IR::U32& im | |||
| 37 | } // Anonymous namespace | 23 | } // Anonymous namespace |
| 38 | 24 | ||
| 39 | void TranslatorVisitor::LDC(u64 insn) { | 25 | void TranslatorVisitor::LDC(u64 insn) { |
| 40 | union { | 26 | const Encoding ldc{insn}; |
| 41 | u64 raw; | ||
| 42 | BitField<0, 8, IR::Reg> dest_reg; | ||
| 43 | BitField<8, 8, IR::Reg> src_reg; | ||
| 44 | BitField<20, 16, s64> offset; | ||
| 45 | BitField<36, 5, u64> index; | ||
| 46 | BitField<44, 2, Mode> mode; | ||
| 47 | BitField<48, 3, Size> size; | ||
| 48 | } const ldc{insn}; | ||
| 49 | |||
| 50 | const IR::U32 imm_index{ir.Imm32(static_cast<u32>(ldc.index))}; | 27 | const IR::U32 imm_index{ir.Imm32(static_cast<u32>(ldc.index))}; |
| 51 | const IR::U32 reg{X(ldc.src_reg)}; | 28 | const IR::U32 reg{X(ldc.src_reg)}; |
| 52 | const IR::U32 imm{ir.Imm32(static_cast<s32>(ldc.offset))}; | 29 | const IR::U32 imm{ir.Imm32(static_cast<s32>(ldc.offset))}; |
diff --git a/src/shader_recompiler/frontend/maxwell/translate/impl/load_constant.h b/src/shader_recompiler/frontend/maxwell/translate/impl/load_constant.h new file mode 100644 index 000000000..3074ea0e3 --- /dev/null +++ b/src/shader_recompiler/frontend/maxwell/translate/impl/load_constant.h | |||
| @@ -0,0 +1,39 @@ | |||
| 1 | // Copyright 2021 yuzu Emulator Project | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #pragma once | ||
| 6 | |||
| 7 | #include "common/bit_field.h" | ||
| 8 | #include "common/common_types.h" | ||
| 9 | #include "shader_recompiler/frontend/ir/reg.h" | ||
| 10 | |||
| 11 | namespace Shader::Maxwell::LDC { | ||
| 12 | |||
| 13 | enum class Mode : u64 { | ||
| 14 | Default, | ||
| 15 | IL, | ||
| 16 | IS, | ||
| 17 | ISL, | ||
| 18 | }; | ||
| 19 | |||
| 20 | enum class Size : u64 { | ||
| 21 | U8, | ||
| 22 | S8, | ||
| 23 | U16, | ||
| 24 | S16, | ||
| 25 | B32, | ||
| 26 | B64, | ||
| 27 | }; | ||
| 28 | |||
| 29 | union Encoding { | ||
| 30 | u64 raw; | ||
| 31 | BitField<0, 8, IR::Reg> dest_reg; | ||
| 32 | BitField<8, 8, IR::Reg> src_reg; | ||
| 33 | BitField<20, 16, s64> offset; | ||
| 34 | BitField<36, 5, u64> index; | ||
| 35 | BitField<44, 2, Mode> mode; | ||
| 36 | BitField<48, 3, Size> size; | ||
| 37 | }; | ||
| 38 | |||
| 39 | } // namespace Shader::Maxwell::LDC | ||
diff --git a/src/shader_recompiler/frontend/maxwell/translate/impl/not_implemented.cpp b/src/shader_recompiler/frontend/maxwell/translate/impl/not_implemented.cpp index b62d8ee2a..a0057a473 100644 --- a/src/shader_recompiler/frontend/maxwell/translate/impl/not_implemented.cpp +++ b/src/shader_recompiler/frontend/maxwell/translate/impl/not_implemented.cpp | |||
| @@ -53,10 +53,6 @@ void TranslatorVisitor::BRK(u64) { | |||
| 53 | ThrowNotImplemented(Opcode::BRK); | 53 | ThrowNotImplemented(Opcode::BRK); |
| 54 | } | 54 | } |
| 55 | 55 | ||
| 56 | void TranslatorVisitor::BRX(u64) { | ||
| 57 | ThrowNotImplemented(Opcode::BRX); | ||
| 58 | } | ||
| 59 | |||
| 60 | void TranslatorVisitor::CAL() { | 56 | void TranslatorVisitor::CAL() { |
| 61 | // CAL is a no-op | 57 | // CAL is a no-op |
| 62 | } | 58 | } |
| @@ -181,10 +177,6 @@ void TranslatorVisitor::JMP(u64) { | |||
| 181 | ThrowNotImplemented(Opcode::JMP); | 177 | ThrowNotImplemented(Opcode::JMP); |
| 182 | } | 178 | } |
| 183 | 179 | ||
| 184 | void TranslatorVisitor::JMX(u64) { | ||
| 185 | ThrowNotImplemented(Opcode::JMX); | ||
| 186 | } | ||
| 187 | |||
| 188 | void TranslatorVisitor::KIL() { | 180 | void TranslatorVisitor::KIL() { |
| 189 | // KIL is a no-op | 181 | // KIL is a no-op |
| 190 | } | 182 | } |
diff --git a/src/shader_recompiler/ir_opt/ssa_rewrite_pass.cpp b/src/shader_recompiler/ir_opt/ssa_rewrite_pass.cpp index bab7ca186..259233746 100644 --- a/src/shader_recompiler/ir_opt/ssa_rewrite_pass.cpp +++ b/src/shader_recompiler/ir_opt/ssa_rewrite_pass.cpp | |||
| @@ -48,8 +48,12 @@ struct GotoVariable : FlagTag { | |||
| 48 | u32 index; | 48 | u32 index; |
| 49 | }; | 49 | }; |
| 50 | 50 | ||
| 51 | struct IndirectBranchVariable { | ||
| 52 | auto operator<=>(const IndirectBranchVariable&) const noexcept = default; | ||
| 53 | }; | ||
| 54 | |||
| 51 | using Variant = std::variant<IR::Reg, IR::Pred, ZeroFlagTag, SignFlagTag, CarryFlagTag, | 55 | using Variant = std::variant<IR::Reg, IR::Pred, ZeroFlagTag, SignFlagTag, CarryFlagTag, |
| 52 | OverflowFlagTag, GotoVariable>; | 56 | OverflowFlagTag, GotoVariable, IndirectBranchVariable>; |
| 53 | using ValueMap = boost::container::flat_map<IR::Block*, IR::Value, std::less<IR::Block*>>; | 57 | using ValueMap = boost::container::flat_map<IR::Block*, IR::Value, std::less<IR::Block*>>; |
| 54 | 58 | ||
| 55 | struct DefTable { | 59 | struct DefTable { |
| @@ -65,6 +69,10 @@ struct DefTable { | |||
| 65 | return goto_vars[goto_variable.index]; | 69 | return goto_vars[goto_variable.index]; |
| 66 | } | 70 | } |
| 67 | 71 | ||
| 72 | [[nodiscard]] ValueMap& operator[](IndirectBranchVariable) { | ||
| 73 | return indirect_branch_var; | ||
| 74 | } | ||
| 75 | |||
| 68 | [[nodiscard]] ValueMap& operator[](ZeroFlagTag) noexcept { | 76 | [[nodiscard]] ValueMap& operator[](ZeroFlagTag) noexcept { |
| 69 | return zero_flag; | 77 | return zero_flag; |
| 70 | } | 78 | } |
| @@ -84,6 +92,7 @@ struct DefTable { | |||
| 84 | std::array<ValueMap, IR::NUM_USER_REGS> regs; | 92 | std::array<ValueMap, IR::NUM_USER_REGS> regs; |
| 85 | std::array<ValueMap, IR::NUM_USER_PREDS> preds; | 93 | std::array<ValueMap, IR::NUM_USER_PREDS> preds; |
| 86 | boost::container::flat_map<u32, ValueMap> goto_vars; | 94 | boost::container::flat_map<u32, ValueMap> goto_vars; |
| 95 | ValueMap indirect_branch_var; | ||
| 87 | ValueMap zero_flag; | 96 | ValueMap zero_flag; |
| 88 | ValueMap sign_flag; | 97 | ValueMap sign_flag; |
| 89 | ValueMap carry_flag; | 98 | ValueMap carry_flag; |
| @@ -102,6 +111,10 @@ IR::Opcode UndefOpcode(const FlagTag&) noexcept { | |||
| 102 | return IR::Opcode::UndefU1; | 111 | return IR::Opcode::UndefU1; |
| 103 | } | 112 | } |
| 104 | 113 | ||
| 114 | IR::Opcode UndefOpcode(IndirectBranchVariable) noexcept { | ||
| 115 | return IR::Opcode::UndefU32; | ||
| 116 | } | ||
| 117 | |||
| 105 | [[nodiscard]] bool IsPhi(const IR::Inst& inst) noexcept { | 118 | [[nodiscard]] bool IsPhi(const IR::Inst& inst) noexcept { |
| 106 | return inst.Opcode() == IR::Opcode::Phi; | 119 | return inst.Opcode() == IR::Opcode::Phi; |
| 107 | } | 120 | } |
| @@ -219,6 +232,9 @@ void VisitInst(Pass& pass, IR::Block* block, IR::Inst& inst) { | |||
| 219 | case IR::Opcode::SetGotoVariable: | 232 | case IR::Opcode::SetGotoVariable: |
| 220 | pass.WriteVariable(GotoVariable{inst.Arg(0).U32()}, block, inst.Arg(1)); | 233 | pass.WriteVariable(GotoVariable{inst.Arg(0).U32()}, block, inst.Arg(1)); |
| 221 | break; | 234 | break; |
| 235 | case IR::Opcode::SetIndirectBranchVariable: | ||
| 236 | pass.WriteVariable(IndirectBranchVariable{}, block, inst.Arg(0)); | ||
| 237 | break; | ||
| 222 | case IR::Opcode::SetZFlag: | 238 | case IR::Opcode::SetZFlag: |
| 223 | pass.WriteVariable(ZeroFlagTag{}, block, inst.Arg(0)); | 239 | pass.WriteVariable(ZeroFlagTag{}, block, inst.Arg(0)); |
| 224 | break; | 240 | break; |
| @@ -244,6 +260,9 @@ void VisitInst(Pass& pass, IR::Block* block, IR::Inst& inst) { | |||
| 244 | case IR::Opcode::GetGotoVariable: | 260 | case IR::Opcode::GetGotoVariable: |
| 245 | inst.ReplaceUsesWith(pass.ReadVariable(GotoVariable{inst.Arg(0).U32()}, block)); | 261 | inst.ReplaceUsesWith(pass.ReadVariable(GotoVariable{inst.Arg(0).U32()}, block)); |
| 246 | break; | 262 | break; |
| 263 | case IR::Opcode::GetIndirectBranchVariable: | ||
| 264 | inst.ReplaceUsesWith(pass.ReadVariable(IndirectBranchVariable{}, block)); | ||
| 265 | break; | ||
| 247 | case IR::Opcode::GetZFlag: | 266 | case IR::Opcode::GetZFlag: |
| 248 | inst.ReplaceUsesWith(pass.ReadVariable(ZeroFlagTag{}, block)); | 267 | inst.ReplaceUsesWith(pass.ReadVariable(ZeroFlagTag{}, block)); |
| 249 | break; | 268 | break; |
diff --git a/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp b/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp index 8b2816c13..6cde01491 100644 --- a/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp +++ b/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp | |||
| @@ -47,7 +47,7 @@ auto MakeSpan(Container& container) { | |||
| 47 | } | 47 | } |
| 48 | 48 | ||
| 49 | u64 MakeCbufKey(u32 index, u32 offset) { | 49 | u64 MakeCbufKey(u32 index, u32 offset) { |
| 50 | return (static_cast<u64>(index) << 32) | static_cast<u64>(offset); | 50 | return (static_cast<u64>(index) << 32) | offset; |
| 51 | } | 51 | } |
| 52 | 52 | ||
| 53 | class GenericEnvironment : public Shader::Environment { | 53 | class GenericEnvironment : public Shader::Environment { |
| @@ -114,11 +114,13 @@ public: | |||
| 114 | gpu_memory->ReadBlock(program_base + read_lowest, data.get(), code_size); | 114 | gpu_memory->ReadBlock(program_base + read_lowest, data.get(), code_size); |
| 115 | 115 | ||
| 116 | const u64 num_texture_types{static_cast<u64>(texture_types.size())}; | 116 | const u64 num_texture_types{static_cast<u64>(texture_types.size())}; |
| 117 | const u64 num_cbuf_values{static_cast<u64>(cbuf_values.size())}; | ||
| 117 | const u32 local_memory_size{LocalMemorySize()}; | 118 | const u32 local_memory_size{LocalMemorySize()}; |
| 118 | const u32 texture_bound{TextureBoundBuffer()}; | 119 | const u32 texture_bound{TextureBoundBuffer()}; |
| 119 | 120 | ||
| 120 | file.write(reinterpret_cast<const char*>(&code_size), sizeof(code_size)) | 121 | file.write(reinterpret_cast<const char*>(&code_size), sizeof(code_size)) |
| 121 | .write(reinterpret_cast<const char*>(&num_texture_types), sizeof(num_texture_types)) | 122 | .write(reinterpret_cast<const char*>(&num_texture_types), sizeof(num_texture_types)) |
| 123 | .write(reinterpret_cast<const char*>(&num_cbuf_values), sizeof(num_cbuf_values)) | ||
| 122 | .write(reinterpret_cast<const char*>(&local_memory_size), sizeof(local_memory_size)) | 124 | .write(reinterpret_cast<const char*>(&local_memory_size), sizeof(local_memory_size)) |
| 123 | .write(reinterpret_cast<const char*>(&texture_bound), sizeof(texture_bound)) | 125 | .write(reinterpret_cast<const char*>(&texture_bound), sizeof(texture_bound)) |
| 124 | .write(reinterpret_cast<const char*>(&start_address), sizeof(start_address)) | 126 | .write(reinterpret_cast<const char*>(&start_address), sizeof(start_address)) |
| @@ -130,6 +132,10 @@ public: | |||
| 130 | file.write(reinterpret_cast<const char*>(&key), sizeof(key)) | 132 | file.write(reinterpret_cast<const char*>(&key), sizeof(key)) |
| 131 | .write(reinterpret_cast<const char*>(&type), sizeof(type)); | 133 | .write(reinterpret_cast<const char*>(&type), sizeof(type)); |
| 132 | } | 134 | } |
| 135 | for (const auto [key, type] : cbuf_values) { | ||
| 136 | file.write(reinterpret_cast<const char*>(&key), sizeof(key)) | ||
| 137 | .write(reinterpret_cast<const char*>(&type), sizeof(type)); | ||
| 138 | } | ||
| 133 | if (stage == Shader::Stage::Compute) { | 139 | if (stage == Shader::Stage::Compute) { |
| 134 | const std::array<u32, 3> workgroup_size{WorkgroupSize()}; | 140 | const std::array<u32, 3> workgroup_size{WorkgroupSize()}; |
| 135 | const u32 shared_memory_size{SharedMemorySize()}; | 141 | const u32 shared_memory_size{SharedMemorySize()}; |
| @@ -212,6 +218,7 @@ protected: | |||
| 212 | 218 | ||
| 213 | std::vector<u64> code; | 219 | std::vector<u64> code; |
| 214 | std::unordered_map<u64, Shader::TextureType> texture_types; | 220 | std::unordered_map<u64, Shader::TextureType> texture_types; |
| 221 | std::unordered_map<u64, u32> cbuf_values; | ||
| 215 | 222 | ||
| 216 | u32 read_lowest = std::numeric_limits<u32>::max(); | 223 | u32 read_lowest = std::numeric_limits<u32>::max(); |
| 217 | u32 read_highest = 0; | 224 | u32 read_highest = 0; |
| @@ -267,6 +274,17 @@ public: | |||
| 267 | 274 | ||
| 268 | ~GraphicsEnvironment() override = default; | 275 | ~GraphicsEnvironment() override = default; |
| 269 | 276 | ||
| 277 | u32 ReadCbufValue(u32 cbuf_index, u32 cbuf_offset) override { | ||
| 278 | const auto& cbuf{maxwell3d->state.shader_stages[stage_index].const_buffers[cbuf_index]}; | ||
| 279 | ASSERT(cbuf.enabled); | ||
| 280 | u32 value{}; | ||
| 281 | if (cbuf_offset < cbuf.size) { | ||
| 282 | value = gpu_memory->Read<u32>(cbuf.address + cbuf_offset); | ||
| 283 | } | ||
| 284 | cbuf_values.emplace(MakeCbufKey(cbuf_index, cbuf_offset), value); | ||
| 285 | return value; | ||
| 286 | } | ||
| 287 | |||
| 270 | Shader::TextureType ReadTextureType(u32 cbuf_index, u32 cbuf_offset) override { | 288 | Shader::TextureType ReadTextureType(u32 cbuf_index, u32 cbuf_offset) override { |
| 271 | const auto& regs{maxwell3d->regs}; | 289 | const auto& regs{maxwell3d->regs}; |
| 272 | const auto& cbuf{maxwell3d->state.shader_stages[stage_index].const_buffers[cbuf_index]}; | 290 | const auto& cbuf{maxwell3d->state.shader_stages[stage_index].const_buffers[cbuf_index]}; |
| @@ -312,6 +330,18 @@ public: | |||
| 312 | 330 | ||
| 313 | ~ComputeEnvironment() override = default; | 331 | ~ComputeEnvironment() override = default; |
| 314 | 332 | ||
| 333 | u32 ReadCbufValue(u32 cbuf_index, u32 cbuf_offset) override { | ||
| 334 | const auto& qmd{kepler_compute->launch_description}; | ||
| 335 | ASSERT(((qmd.const_buffer_enable_mask.Value() >> cbuf_index) & 1) != 0); | ||
| 336 | const auto& cbuf{qmd.const_buffer_config[cbuf_index]}; | ||
| 337 | u32 value{}; | ||
| 338 | if (cbuf_offset < cbuf.size) { | ||
| 339 | value = gpu_memory->Read<u32>(cbuf.Address() + cbuf_offset); | ||
| 340 | } | ||
| 341 | cbuf_values.emplace(MakeCbufKey(cbuf_index, cbuf_offset), value); | ||
| 342 | return value; | ||
| 343 | } | ||
| 344 | |||
| 315 | Shader::TextureType ReadTextureType(u32 cbuf_index, u32 cbuf_offset) override { | 345 | Shader::TextureType ReadTextureType(u32 cbuf_index, u32 cbuf_offset) override { |
| 316 | const auto& regs{kepler_compute->regs}; | 346 | const auto& regs{kepler_compute->regs}; |
| 317 | const auto& qmd{kepler_compute->launch_description}; | 347 | const auto& qmd{kepler_compute->launch_description}; |
| @@ -386,8 +416,10 @@ public: | |||
| 386 | void Deserialize(std::ifstream& file) { | 416 | void Deserialize(std::ifstream& file) { |
| 387 | u64 code_size{}; | 417 | u64 code_size{}; |
| 388 | u64 num_texture_types{}; | 418 | u64 num_texture_types{}; |
| 419 | u64 num_cbuf_values{}; | ||
| 389 | file.read(reinterpret_cast<char*>(&code_size), sizeof(code_size)) | 420 | file.read(reinterpret_cast<char*>(&code_size), sizeof(code_size)) |
| 390 | .read(reinterpret_cast<char*>(&num_texture_types), sizeof(num_texture_types)) | 421 | .read(reinterpret_cast<char*>(&num_texture_types), sizeof(num_texture_types)) |
| 422 | .read(reinterpret_cast<char*>(&num_cbuf_values), sizeof(num_cbuf_values)) | ||
| 391 | .read(reinterpret_cast<char*>(&local_memory_size), sizeof(local_memory_size)) | 423 | .read(reinterpret_cast<char*>(&local_memory_size), sizeof(local_memory_size)) |
| 392 | .read(reinterpret_cast<char*>(&texture_bound), sizeof(texture_bound)) | 424 | .read(reinterpret_cast<char*>(&texture_bound), sizeof(texture_bound)) |
| 393 | .read(reinterpret_cast<char*>(&start_address), sizeof(start_address)) | 425 | .read(reinterpret_cast<char*>(&start_address), sizeof(start_address)) |
| @@ -403,6 +435,13 @@ public: | |||
| 403 | .read(reinterpret_cast<char*>(&type), sizeof(type)); | 435 | .read(reinterpret_cast<char*>(&type), sizeof(type)); |
| 404 | texture_types.emplace(key, type); | 436 | texture_types.emplace(key, type); |
| 405 | } | 437 | } |
| 438 | for (size_t i = 0; i < num_cbuf_values; ++i) { | ||
| 439 | u64 key; | ||
| 440 | u32 value; | ||
| 441 | file.read(reinterpret_cast<char*>(&key), sizeof(key)) | ||
| 442 | .read(reinterpret_cast<char*>(&value), sizeof(value)); | ||
| 443 | cbuf_values.emplace(key, value); | ||
| 444 | } | ||
| 406 | if (stage == Shader::Stage::Compute) { | 445 | if (stage == Shader::Stage::Compute) { |
| 407 | file.read(reinterpret_cast<char*>(&workgroup_size), sizeof(workgroup_size)) | 446 | file.read(reinterpret_cast<char*>(&workgroup_size), sizeof(workgroup_size)) |
| 408 | .read(reinterpret_cast<char*>(&shared_memory_size), sizeof(shared_memory_size)); | 447 | .read(reinterpret_cast<char*>(&shared_memory_size), sizeof(shared_memory_size)); |
| @@ -418,6 +457,14 @@ public: | |||
| 418 | return code[(address - read_lowest) / sizeof(u64)]; | 457 | return code[(address - read_lowest) / sizeof(u64)]; |
| 419 | } | 458 | } |
| 420 | 459 | ||
| 460 | u32 ReadCbufValue(u32 cbuf_index, u32 cbuf_offset) override { | ||
| 461 | const auto it{cbuf_values.find(MakeCbufKey(cbuf_index, cbuf_offset))}; | ||
| 462 | if (it == cbuf_values.end()) { | ||
| 463 | throw Shader::LogicError("Uncached read texture type"); | ||
| 464 | } | ||
| 465 | return it->second; | ||
| 466 | } | ||
| 467 | |||
| 421 | Shader::TextureType ReadTextureType(u32 cbuf_index, u32 cbuf_offset) override { | 468 | Shader::TextureType ReadTextureType(u32 cbuf_index, u32 cbuf_offset) override { |
| 422 | const auto it{texture_types.find(MakeCbufKey(cbuf_index, cbuf_offset))}; | 469 | const auto it{texture_types.find(MakeCbufKey(cbuf_index, cbuf_offset))}; |
| 423 | if (it == texture_types.end()) { | 470 | if (it == texture_types.end()) { |
| @@ -445,6 +492,7 @@ public: | |||
| 445 | private: | 492 | private: |
| 446 | std::unique_ptr<u64[]> code; | 493 | std::unique_ptr<u64[]> code; |
| 447 | std::unordered_map<u64, Shader::TextureType> texture_types; | 494 | std::unordered_map<u64, Shader::TextureType> texture_types; |
| 495 | std::unordered_map<u64, u32> cbuf_values; | ||
| 448 | std::array<u32, 3> workgroup_size{}; | 496 | std::array<u32, 3> workgroup_size{}; |
| 449 | u32 local_memory_size{}; | 497 | u32 local_memory_size{}; |
| 450 | u32 shared_memory_size{}; | 498 | u32 shared_memory_size{}; |