154 lines
6.8 KiB
C++
154 lines
6.8 KiB
C++
#pragma once
|
|
/**
|
|
******************************************************************************
|
|
* Xenia : Xbox 360 Emulator Research Project *
|
|
******************************************************************************
|
|
* Copyright 2015 Ben Vanik. All rights reserved. *
|
|
* Released under the BSD license - see LICENSE in the root for more details. *
|
|
******************************************************************************
|
|
*
|
|
* @modified Tom Clay, 2026 - Adapted for ReXGlue runtime
|
|
*/
|
|
|
|
#include <memory>
|
|
#include <set>
|
|
#include <string>
|
|
#include <vector>
|
|
|
|
#include <rex/graphics/format/ucode.h>
|
|
#include <rex/graphics/pipeline/shader/shader.h>
|
|
#include <rex/graphics/registers.h>
|
|
#include <rex/graphics/xenos.h>
|
|
#include <rex/math.h>
|
|
#include <rex/string/buffer.h>
|
|
|
|
namespace rex::graphics {
|
|
|
|
class ShaderTranslator {
|
|
public:
|
|
virtual ~ShaderTranslator();
|
|
|
|
virtual uint64_t GetDefaultVertexShaderModification(
|
|
uint32_t /*dynamic_addressable_register_count*/,
|
|
Shader::HostVertexShaderType /*host_vertex_shader_type*/ =
|
|
Shader::HostVertexShaderType::kVertex) const {
|
|
return 0;
|
|
}
|
|
virtual uint64_t GetDefaultPixelShaderModification(
|
|
uint32_t /*dynamic_addressable_register_count*/) const {
|
|
return 0;
|
|
}
|
|
|
|
// AnalyzeUcode must be done on the shader before translating!
|
|
bool TranslateAnalyzedShader(Shader::Translation& translation);
|
|
|
|
protected:
|
|
ShaderTranslator();
|
|
|
|
// Resets translator state before beginning translation.
|
|
virtual void Reset();
|
|
|
|
// Shader and modification currently being translated.
|
|
Shader::Translation& current_translation() const { return *translation_; }
|
|
Shader& current_shader() const { return current_translation().shader(); }
|
|
|
|
// Register count from SQ_PROGRAM_CNTL, stored by the implementation in its
|
|
// modification bits.
|
|
virtual uint32_t GetModificationRegisterCount() const { return xenos::kMaxShaderTempRegisters; }
|
|
|
|
// True if the current shader is a vertex shader.
|
|
bool is_vertex_shader() const { return current_shader().type() == xenos::ShaderType::kVertex; }
|
|
// True if the current shader is a pixel shader.
|
|
bool is_pixel_shader() const { return current_shader().type() == xenos::ShaderType::kPixel; }
|
|
|
|
// Temporary register count, accessible via static and dynamic addressing.
|
|
uint32_t register_count() const { return register_count_; }
|
|
|
|
// Emits a translation error that will be passed back in the result.
|
|
virtual void EmitTranslationError(const char* message, bool is_fatal = true);
|
|
|
|
// Handles the start of translation.
|
|
// At this point the vertex and texture bindings have been gathered.
|
|
virtual void StartTranslation() {}
|
|
|
|
// Handles the end of translation when all ucode has been processed.
|
|
// Returns the translated shader binary.
|
|
virtual std::vector<uint8_t> CompleteTranslation() { return std::vector<uint8_t>(); }
|
|
|
|
// Handles post-translation tasks when the shader has been fully translated.
|
|
virtual void PostTranslation() {}
|
|
// Sets the host disassembly on a shader.
|
|
void set_host_disassembly(Shader::Translation& translation, std::string value) {
|
|
translation.host_disassembly_ = std::move(value);
|
|
}
|
|
|
|
// Pre-process a control-flow instruction before anything else.
|
|
virtual void PreProcessControlFlowInstructions(
|
|
std::vector<ucode::ControlFlowInstruction> /*instrs*/) {}
|
|
|
|
// Handles translation for control flow label addresses.
|
|
// This is triggered once for each label required (due to control flow
|
|
// operations) before any of the instructions within the target exec.
|
|
virtual void ProcessLabel(uint32_t /*cf_index*/) {}
|
|
|
|
// Handles translation for control flow nop instructions.
|
|
virtual void ProcessControlFlowNopInstruction(uint32_t /*cf_index*/) {}
|
|
// Handles the start of a control flow instruction at the given address.
|
|
virtual void ProcessControlFlowInstructionBegin(uint32_t /*cf_index*/) {}
|
|
// Handles the end of a control flow instruction that began at the given
|
|
// address.
|
|
virtual void ProcessControlFlowInstructionEnd(uint32_t /*cf_index*/) {}
|
|
// Handles translation for control flow exec instructions prior to their
|
|
// contained ALU/fetch instructions.
|
|
virtual void ProcessExecInstructionBegin(const ParsedExecInstruction& /*instr*/) {}
|
|
// Handles translation for control flow exec instructions after their
|
|
// contained ALU/fetch instructions.
|
|
virtual void ProcessExecInstructionEnd(const ParsedExecInstruction& /*instr*/) {}
|
|
// Handles translation for loop start instructions.
|
|
virtual void ProcessLoopStartInstruction(const ParsedLoopStartInstruction& /*instr*/) {}
|
|
// Handles translation for loop end instructions.
|
|
virtual void ProcessLoopEndInstruction(const ParsedLoopEndInstruction& /*instr*/) {}
|
|
// Handles translation for function call instructions.
|
|
virtual void ProcessCallInstruction(const ParsedCallInstruction& /*instr*/) {}
|
|
// Handles translation for function return instructions.
|
|
virtual void ProcessReturnInstruction(const ParsedReturnInstruction& /*instr*/) {}
|
|
// Handles translation for jump instructions.
|
|
virtual void ProcessJumpInstruction(const ParsedJumpInstruction& /*instr*/) {}
|
|
// Handles translation for alloc instructions. Memory exports for eM#
|
|
// indicated by export_eM must be performed, regardless of the alloc type.
|
|
virtual void ProcessAllocInstruction(const ParsedAllocInstruction& /*instr*/,
|
|
uint8_t /*export_eM*/) {}
|
|
|
|
// Handles translation for vertex fetch instructions.
|
|
virtual void ProcessVertexFetchInstruction(const ParsedVertexFetchInstruction& /*instr*/) {}
|
|
// Handles translation for texture fetch instructions.
|
|
virtual void ProcessTextureFetchInstruction(const ParsedTextureFetchInstruction& /*instr*/) {}
|
|
// Handles translation for ALU instructions.
|
|
// memexport_eM_potentially_written_before needs to be handled by `kill`
|
|
// instruction to make sure memory exports for the eM# writes earlier in
|
|
// previous execs and the current exec are done before the invocation becomes
|
|
// inactive.
|
|
virtual void ProcessAluInstruction(const ParsedAluInstruction& /*instr*/,
|
|
uint8_t /*memexport_eM_potentially_written_before*/) {}
|
|
|
|
private:
|
|
void TranslateControlFlowInstruction(const ucode::ControlFlowInstruction& cf);
|
|
void TranslateExecInstructions(const ParsedExecInstruction& instr);
|
|
|
|
// Current shader and modification being translated.
|
|
Shader::Translation* translation_ = nullptr;
|
|
|
|
// Accumulated translation errors.
|
|
std::vector<Shader::Error> errors_;
|
|
|
|
// Temporary register count, accessible via static and dynamic addressing.
|
|
uint32_t register_count_ = 0;
|
|
|
|
// Current control flow dword index.
|
|
uint32_t cf_index_ = 0;
|
|
|
|
// Kept for supporting vfetch_mini.
|
|
ucode::VertexFetchInstruction previous_vfetch_full_;
|
|
};
|
|
|
|
} // namespace rex::graphics
|