2015-11-23 01:42:08 +00:00
|
|
|
/**
|
|
|
|
******************************************************************************
|
|
|
|
* 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. *
|
|
|
|
******************************************************************************
|
|
|
|
*/
|
|
|
|
|
2015-11-25 03:49:05 +00:00
|
|
|
#ifndef XENIA_GPU_SHADER_TRANSLATOR_H_
|
|
|
|
#define XENIA_GPU_SHADER_TRANSLATOR_H_
|
|
|
|
|
2015-11-26 00:37:28 +00:00
|
|
|
#include <memory>
|
2015-12-06 18:36:07 +00:00
|
|
|
#include "xenia/gpu/shader.h"
|
2015-11-23 01:42:08 +00:00
|
|
|
|
|
|
|
namespace xe {
|
|
|
|
namespace gpu {
|
2015-11-30 00:55:42 +00:00
|
|
|
|
2015-11-25 03:49:05 +00:00
|
|
|
class ShaderTranslator {
|
|
|
|
public:
|
2015-11-29 00:19:04 +00:00
|
|
|
virtual ~ShaderTranslator();
|
2015-11-23 01:42:08 +00:00
|
|
|
|
2021-04-26 19:12:09 +00:00
|
|
|
virtual uint64_t GetDefaultVertexShaderModification(
|
2020-12-19 13:14:54 +00:00
|
|
|
uint32_t dynamic_addressable_register_count,
|
2020-12-07 19:23:54 +00:00
|
|
|
Shader::HostVertexShaderType host_vertex_shader_type =
|
|
|
|
Shader::HostVertexShaderType::kVertex) const {
|
|
|
|
return 0;
|
|
|
|
}
|
2021-04-26 19:12:09 +00:00
|
|
|
virtual uint64_t GetDefaultPixelShaderModification(
|
|
|
|
uint32_t dynamic_addressable_register_count) const {
|
|
|
|
return 0;
|
|
|
|
}
|
2020-12-07 19:23:54 +00:00
|
|
|
|
2020-12-19 13:14:54 +00:00
|
|
|
// AnalyzeUcode must be done on the shader before translating!
|
|
|
|
bool TranslateAnalyzedShader(Shader::Translation& translation);
|
2015-11-29 00:19:04 +00:00
|
|
|
|
|
|
|
protected:
|
|
|
|
ShaderTranslator();
|
|
|
|
|
2015-12-06 08:48:41 +00:00
|
|
|
// Resets translator state before beginning translation.
|
2020-12-19 13:14:54 +00:00
|
|
|
virtual void Reset();
|
2020-12-07 19:23:54 +00:00
|
|
|
|
2020-12-19 13:14:54 +00:00
|
|
|
// 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.
|
2022-06-26 11:45:49 +00:00
|
|
|
virtual uint32_t GetModificationRegisterCount() const {
|
|
|
|
return xenos::kMaxShaderTempRegisters;
|
|
|
|
}
|
2015-12-06 08:48:41 +00:00
|
|
|
|
2015-11-30 00:55:42 +00:00
|
|
|
// True if the current shader is a vertex shader.
|
2020-07-11 12:54:22 +00:00
|
|
|
bool is_vertex_shader() const {
|
2020-12-19 13:14:54 +00:00
|
|
|
return current_shader().type() == xenos::ShaderType::kVertex;
|
2020-07-11 12:54:22 +00:00
|
|
|
}
|
2015-11-30 00:55:42 +00:00
|
|
|
// True if the current shader is a pixel shader.
|
2020-07-11 12:54:22 +00:00
|
|
|
bool is_pixel_shader() const {
|
2020-12-19 13:14:54 +00:00
|
|
|
return current_shader().type() == xenos::ShaderType::kPixel;
|
2018-12-20 07:14:18 +00:00
|
|
|
}
|
2015-11-30 00:55:42 +00:00
|
|
|
|
2020-12-19 13:14:54 +00:00
|
|
|
// Temporary register count, accessible via static and dynamic addressing.
|
|
|
|
uint32_t register_count() const { return register_count_; }
|
2020-12-07 19:23:54 +00:00
|
|
|
|
2015-11-30 00:55:42 +00:00
|
|
|
// Emits a translation error that will be passed back in the result.
|
2020-06-06 16:12:34 +00:00
|
|
|
virtual void EmitTranslationError(const char* message, bool is_fatal = true);
|
2015-11-29 00:19:04 +00:00
|
|
|
|
2015-11-30 00:55:42 +00:00
|
|
|
// 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.
|
2015-11-29 00:19:04 +00:00
|
|
|
virtual std::vector<uint8_t> CompleteTranslation() {
|
|
|
|
return std::vector<uint8_t>();
|
|
|
|
}
|
|
|
|
|
2016-02-19 00:42:00 +00:00
|
|
|
// Handles post-translation tasks when the shader has been fully translated.
|
2020-12-19 13:14:54 +00:00
|
|
|
virtual void PostTranslation() {}
|
2016-02-19 00:42:00 +00:00
|
|
|
// Sets the host disassembly on a shader.
|
2020-12-07 19:23:54 +00:00
|
|
|
void set_host_disassembly(Shader::Translation& translation,
|
|
|
|
std::string value) {
|
|
|
|
translation.host_disassembly_ = std::move(value);
|
2016-02-19 00:42:00 +00:00
|
|
|
}
|
|
|
|
|
2016-02-21 00:44:37 +00:00
|
|
|
// Pre-process a control-flow instruction before anything else.
|
2016-09-05 21:57:02 +00:00
|
|
|
virtual void PreProcessControlFlowInstructions(
|
|
|
|
std::vector<ucode::ControlFlowInstruction> instrs) {}
|
2016-02-21 00:44:37 +00:00
|
|
|
|
2015-11-30 00:55:42 +00:00
|
|
|
// 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) {}
|
2015-11-29 00:19:04 +00:00
|
|
|
|
2015-11-30 00:55:42 +00:00
|
|
|
// Handles translation for control flow nop instructions.
|
2016-09-05 21:57:02 +00:00
|
|
|
virtual void ProcessControlFlowNopInstruction(uint32_t cf_index) {}
|
2015-12-06 19:44:22 +00:00
|
|
|
// 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) {}
|
2015-11-30 00:55:42 +00:00
|
|
|
// Handles translation for control flow exec instructions prior to their
|
|
|
|
// contained ALU/fetch instructions.
|
|
|
|
virtual void ProcessExecInstructionBegin(const ParsedExecInstruction& instr) {
|
2015-11-29 00:19:04 +00:00
|
|
|
}
|
2015-11-30 00:55:42 +00:00
|
|
|
// 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) {}
|
[GPU/D3D12] Memexport from anywhere in control flow + 8/16bpp memexport
There's no limit on the number of memory exports in a shader on the real
Xenos, and exports can be done anywhere, including in loops. Now, instead
of deferring the exports to the end of the shader, and assuming that export
allocs are executed only once, Xenia flushes exports when it reaches an
alloc (allocs terminate memory exports on Xenos, as well as individual ALU
instructions with `serialize`, but not handling this case for simplicity,
it's only truly mandatory to flush memory exports before starting a new
one), the end of the shader, or a pixel with outstanding exports is killed.
To know which eM# registers need to be flushed to the memory, traversing
the successors of each exec potentially writing any eM#, and specifying
that certain eM# registers might have potentially been written before each
reached control flow instruction, until a flush point or the end of the
shader is reached.
Also, some games export to sub-32bpp formats. These are now supported via
atomic AND clearing the bits of the dword to replace followed by an atomic
OR inserting the new byte/short.
2023-05-05 18:05:23 +00:00
|
|
|
// 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) {}
|
2015-11-30 00:55:42 +00:00
|
|
|
|
|
|
|
// 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.
|
[GPU/D3D12] Memexport from anywhere in control flow + 8/16bpp memexport
There's no limit on the number of memory exports in a shader on the real
Xenos, and exports can be done anywhere, including in loops. Now, instead
of deferring the exports to the end of the shader, and assuming that export
allocs are executed only once, Xenia flushes exports when it reaches an
alloc (allocs terminate memory exports on Xenos, as well as individual ALU
instructions with `serialize`, but not handling this case for simplicity,
it's only truly mandatory to flush memory exports before starting a new
one), the end of the shader, or a pixel with outstanding exports is killed.
To know which eM# registers need to be flushed to the memory, traversing
the successors of each exec potentially writing any eM#, and specifying
that certain eM# registers might have potentially been written before each
reached control flow instruction, until a flush point or the end of the
shader is reached.
Also, some games export to sub-32bpp formats. These are now supported via
atomic AND clearing the bits of the dword to replace followed by an atomic
OR inserting the new byte/short.
2023-05-05 18:05:23 +00:00
|
|
|
// 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) {}
|
2015-11-26 00:37:28 +00:00
|
|
|
|
2015-11-25 03:49:05 +00:00
|
|
|
private:
|
2015-11-29 00:19:04 +00:00
|
|
|
void TranslateControlFlowInstruction(const ucode::ControlFlowInstruction& cf);
|
2015-11-30 00:55:42 +00:00
|
|
|
void TranslateExecInstructions(const ParsedExecInstruction& instr);
|
2015-11-29 00:19:04 +00:00
|
|
|
|
2020-12-19 13:14:54 +00:00
|
|
|
// Current shader and modification being translated.
|
|
|
|
Shader::Translation* translation_ = nullptr;
|
2020-12-07 19:23:54 +00:00
|
|
|
|
2015-11-29 00:19:04 +00:00
|
|
|
// Accumulated translation errors.
|
2015-12-06 18:36:07 +00:00
|
|
|
std::vector<Shader::Error> errors_;
|
2015-11-29 00:19:04 +00:00
|
|
|
|
2020-12-19 13:14:54 +00:00
|
|
|
// Temporary register count, accessible via static and dynamic addressing.
|
|
|
|
uint32_t register_count_ = 0;
|
|
|
|
|
2015-12-06 19:44:22 +00:00
|
|
|
// Current control flow dword index.
|
|
|
|
uint32_t cf_index_ = 0;
|
|
|
|
|
2015-11-29 00:19:04 +00:00
|
|
|
// Kept for supporting vfetch_mini.
|
|
|
|
ucode::VertexFetchInstruction previous_vfetch_full_;
|
2015-11-25 03:49:05 +00:00
|
|
|
};
|
2015-11-23 01:42:08 +00:00
|
|
|
|
|
|
|
} // namespace gpu
|
|
|
|
} // namespace xe
|
2015-11-25 03:49:05 +00:00
|
|
|
|
|
|
|
#endif // XENIA_GPU_SHADER_TRANSLATOR_H_
|