shader_decode: Implement LDG and basic cbuf tracking
This commit is contained in:
		| @@ -4,6 +4,7 @@ | ||||
|  | ||||
| #include <algorithm> | ||||
| #include <vector> | ||||
| #include <fmt/format.h> | ||||
|  | ||||
| #include "common/assert.h" | ||||
| #include "common/common_types.h" | ||||
| @@ -119,6 +120,54 @@ u32 ShaderIR::DecodeMemory(BasicBlock& bb, const BasicBlock& code, u32 pc) { | ||||
|         } | ||||
|         break; | ||||
|     } | ||||
|     case OpCode::Id::LDG: { | ||||
|         const u32 count = [&]() { | ||||
|             switch (instr.ldg.type) { | ||||
|             case Tegra::Shader::UniformType::Single: | ||||
|                 return 1; | ||||
|             case Tegra::Shader::UniformType::Double: | ||||
|                 return 2; | ||||
|             case Tegra::Shader::UniformType::Quad: | ||||
|             case Tegra::Shader::UniformType::UnsignedQuad: | ||||
|                 return 4; | ||||
|             default: | ||||
|                 UNIMPLEMENTED_MSG("Unimplemented LDG size!"); | ||||
|                 return 1; | ||||
|             } | ||||
|         }(); | ||||
|  | ||||
|         const Node addr_register = GetRegister(instr.gpr8); | ||||
|         const Node base_address = TrackCbuf(addr_register, code, static_cast<s64>(code.size())); | ||||
|         const auto cbuf = std::get_if<CbufNode>(base_address); | ||||
|         ASSERT(cbuf != nullptr); | ||||
|         const auto cbuf_offset_imm = std::get_if<ImmediateNode>(cbuf->GetOffset()); | ||||
|         ASSERT(cbuf_offset_imm != nullptr); | ||||
|         const auto cbuf_offset = cbuf_offset_imm->GetValue() * 4; | ||||
|  | ||||
|         bb.push_back(Comment( | ||||
|             fmt::format("Base address is c[0x{:x}][0x{:x}]", cbuf->GetIndex(), cbuf_offset))); | ||||
|  | ||||
|         const GlobalMemoryBase descriptor{cbuf->GetIndex(), cbuf_offset}; | ||||
|         used_global_memory_bases.insert(descriptor); | ||||
|  | ||||
|         const Node immediate_offset = | ||||
|             Immediate(static_cast<u32>(instr.ldg.immediate_offset.Value())); | ||||
|         const Node base_real_address = | ||||
|             Operation(OperationCode::UAdd, NO_PRECISE, immediate_offset, addr_register); | ||||
|  | ||||
|         for (u32 i = 0; i < count; ++i) { | ||||
|             const Node it_offset = Immediate(i * 4); | ||||
|             const Node real_address = | ||||
|                 Operation(OperationCode::UAdd, NO_PRECISE, base_real_address, it_offset); | ||||
|             const Node gmem = StoreNode(GmemNode(real_address, base_address, descriptor)); | ||||
|  | ||||
|             SetTemporal(bb, i, gmem); | ||||
|         } | ||||
|         for (u32 i = 0; i < count; ++i) { | ||||
|             SetRegister(bb, instr.gpr0.Value() + i, GetTemporal(i)); | ||||
|         } | ||||
|         break; | ||||
|     } | ||||
|     case OpCode::Id::ST_A: { | ||||
|         UNIMPLEMENTED_IF_MSG(instr.gpr8.Value() != Register::ZeroIndex, | ||||
|                              "Indirect attribute loads are not supported"); | ||||
|   | ||||
| @@ -257,6 +257,15 @@ private: | ||||
|     bool is_indirect{}; | ||||
| }; | ||||
|  | ||||
| struct GlobalMemoryBase { | ||||
|     u32 cbuf_index{}; | ||||
|     u32 cbuf_offset{}; | ||||
|  | ||||
|     bool operator<(const GlobalMemoryBase& rhs) const { | ||||
|         return std::tie(cbuf_index, cbuf_offset) < std::tie(rhs.cbuf_index, rhs.cbuf_offset); | ||||
|     } | ||||
| }; | ||||
|  | ||||
| struct MetaArithmetic { | ||||
|     bool precise{}; | ||||
| }; | ||||
| @@ -478,14 +487,26 @@ private: | ||||
| /// Global memory node | ||||
| class GmemNode final { | ||||
| public: | ||||
|     explicit constexpr GmemNode(Node address) : address{address} {} | ||||
|     explicit constexpr GmemNode(Node real_address, Node base_address, | ||||
|                                 const GlobalMemoryBase& descriptor) | ||||
|         : real_address{real_address}, base_address{base_address}, descriptor{descriptor} {} | ||||
|  | ||||
|     Node GetAddress() const { | ||||
|         return address; | ||||
|     Node GetRealAddress() const { | ||||
|         return real_address; | ||||
|     } | ||||
|  | ||||
|     Node GetBaseAddress() const { | ||||
|         return base_address; | ||||
|     } | ||||
|  | ||||
|     const GlobalMemoryBase& GetDescriptor() const { | ||||
|         return descriptor; | ||||
|     } | ||||
|  | ||||
| private: | ||||
|     const Node address; | ||||
|     const Node real_address; | ||||
|     const Node base_address; | ||||
|     const GlobalMemoryBase descriptor; | ||||
| }; | ||||
|  | ||||
| /// Commentary, can be dropped | ||||
| @@ -543,6 +564,10 @@ public: | ||||
|         return used_clip_distances; | ||||
|     } | ||||
|  | ||||
|     const std::set<GlobalMemoryBase>& GetGlobalMemoryBases() const { | ||||
|         return used_global_memory_bases; | ||||
|     } | ||||
|  | ||||
|     std::size_t GetLength() const { | ||||
|         return static_cast<std::size_t>(coverage_end * sizeof(u64)); | ||||
|     } | ||||
| @@ -734,6 +759,10 @@ private: | ||||
|     void WriteLop3Instruction(BasicBlock& bb, Tegra::Shader::Register dest, Node op_a, Node op_b, | ||||
|                               Node op_c, Node imm_lut, bool sets_cc); | ||||
|  | ||||
|     Node TrackCbuf(Node tracked, const BasicBlock& code, s64 cursor); | ||||
|  | ||||
|     std::pair<Node, s64> TrackRegister(const GprNode* tracked, const BasicBlock& code, s64 cursor); | ||||
|  | ||||
|     template <typename... T> | ||||
|     Node Operation(OperationCode code, const T*... operands) { | ||||
|         return StoreNode(OperationNode(code, operands...)); | ||||
| @@ -786,6 +815,7 @@ private: | ||||
|     std::map<u32, ConstBuffer> used_cbufs; | ||||
|     std::set<Sampler> used_samplers; | ||||
|     std::array<bool, Tegra::Engines::Maxwell3D::Regs::NumClipDistances> used_clip_distances{}; | ||||
|     std::set<GlobalMemoryBase> used_global_memory_bases; | ||||
|  | ||||
|     Tegra::Shader::Header header; | ||||
| }; | ||||
|   | ||||
							
								
								
									
										76
									
								
								src/video_core/shader/track.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										76
									
								
								src/video_core/shader/track.cpp
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,76 @@ | ||||
| // Copyright 2018 yuzu Emulator Project | ||||
| // Licensed under GPLv2 or any later version | ||||
| // Refer to the license.txt file included. | ||||
|  | ||||
| #include <algorithm> | ||||
| #include <utility> | ||||
| #include <variant> | ||||
|  | ||||
| #include "video_core/shader/shader_ir.h" | ||||
|  | ||||
| namespace VideoCommon::Shader { | ||||
|  | ||||
| namespace { | ||||
| std::pair<Node, s64> FindOperation(const BasicBlock& code, s64 cursor, | ||||
|                                    OperationCode operation_code) { | ||||
|     for (; cursor >= 0; --cursor) { | ||||
|         const Node node = code[cursor]; | ||||
|         if (const auto operation = std::get_if<OperationNode>(node)) { | ||||
|             if (operation->GetCode() == operation_code) | ||||
|                 return {node, cursor}; | ||||
|         } | ||||
|     } | ||||
|     return {}; | ||||
| } | ||||
| } // namespace | ||||
|  | ||||
| Node ShaderIR::TrackCbuf(Node tracked, const BasicBlock& code, s64 cursor) { | ||||
|     if (const auto cbuf = std::get_if<CbufNode>(tracked)) { | ||||
|         // Cbuf found, but it has to be immediate | ||||
|         return std::holds_alternative<ImmediateNode>(*cbuf->GetOffset()) ? tracked : nullptr; | ||||
|     } | ||||
|     if (const auto gpr = std::get_if<GprNode>(tracked)) { | ||||
|         if (gpr->GetIndex() == Tegra::Shader::Register::ZeroIndex) { | ||||
|             return nullptr; | ||||
|         } | ||||
|         // Reduce the cursor in one to avoid infinite loops when the instruction sets the same | ||||
|         // register that it uses as operand | ||||
|         const auto [source, new_cursor] = TrackRegister(gpr, code, cursor - 1); | ||||
|         if (!source) { | ||||
|             return nullptr; | ||||
|         } | ||||
|         return TrackCbuf(source, code, new_cursor); | ||||
|     } | ||||
|     if (const auto operation = std::get_if<OperationNode>(tracked)) { | ||||
|         for (std::size_t i = 0; i < operation->GetOperandsCount(); ++i) { | ||||
|             if (const auto found = TrackCbuf((*operation)[i], code, cursor)) { | ||||
|                 // Cbuf found in operand | ||||
|                 return found; | ||||
|             } | ||||
|         } | ||||
|         return nullptr; | ||||
|     } | ||||
|     return nullptr; | ||||
| } | ||||
|  | ||||
| std::pair<Node, s64> ShaderIR::TrackRegister(const GprNode* tracked, const BasicBlock& code, | ||||
|                                              s64 cursor) { | ||||
|     for (; cursor >= 0; --cursor) { | ||||
|         const auto [found_node, new_cursor] = FindOperation(code, cursor, OperationCode::Assign); | ||||
|         if (!found_node) { | ||||
|             return {}; | ||||
|         } | ||||
|         const auto operation = std::get_if<OperationNode>(found_node); | ||||
|         ASSERT(operation); | ||||
|  | ||||
|         const auto& target = (*operation)[0]; | ||||
|         if (const auto gpr_target = std::get_if<GprNode>(target)) { | ||||
|             if (gpr_target->GetIndex() == tracked->GetIndex()) { | ||||
|                 return {(*operation)[1], new_cursor}; | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|     return {}; | ||||
| } | ||||
|  | ||||
| } // namespace VideoCommon::Shader | ||||
		Reference in New Issue
	
	Block a user
	 ReinUsesLisp
					ReinUsesLisp