Reworking translator code to be pretty sexy.
This commit is contained in:
parent
65130edaa1
commit
d2f7cc1602
|
@ -17,13 +17,15 @@
|
||||||
#include "xenia/base/main.h"
|
#include "xenia/base/main.h"
|
||||||
#include "xenia/base/string.h"
|
#include "xenia/base/string.h"
|
||||||
#include "xenia/gpu/shader_translator.h"
|
#include "xenia/gpu/shader_translator.h"
|
||||||
|
#include "xenia/gpu/spirv_shader_translator.h"
|
||||||
|
#include "xenia/ui/spirv/spirv_disassembler.h"
|
||||||
|
|
||||||
DEFINE_string(shader_input, "", "Input shader binary file path.");
|
DEFINE_string(shader_input, "", "Input shader binary file path.");
|
||||||
DEFINE_string(shader_input_type, "",
|
DEFINE_string(shader_input_type, "",
|
||||||
"'vs', 'ps', or unspecified to infer from the given filename.");
|
"'vs', 'ps', or unspecified to infer from the given filename.");
|
||||||
DEFINE_string(shader_output, "", "Output shader file path.");
|
DEFINE_string(shader_output, "", "Output shader file path.");
|
||||||
DEFINE_string(shader_output_type, "ucode",
|
DEFINE_string(shader_output_type, "ucode",
|
||||||
"Translator to use: [ucode, spirvtext].");
|
"Translator to use: [ucode, spirv, spirvtext].");
|
||||||
|
|
||||||
namespace xe {
|
namespace xe {
|
||||||
namespace gpu {
|
namespace gpu {
|
||||||
|
@ -77,9 +79,9 @@ int shader_compiler_main(const std::vector<std::wstring>& args) {
|
||||||
ucode_dwords.size(), ucode_dwords.size() * 4);
|
ucode_dwords.size(), ucode_dwords.size() * 4);
|
||||||
|
|
||||||
std::unique_ptr<ShaderTranslator> translator;
|
std::unique_ptr<ShaderTranslator> translator;
|
||||||
if (FLAGS_shader_output_type == "spirvtext") {
|
if (FLAGS_shader_output_type == "spirv" ||
|
||||||
// TODO(benvanik): SPIRV translator.
|
FLAGS_shader_output_type == "spirvtext") {
|
||||||
translator = std::make_unique<UcodeShaderTranslator>();
|
translator = std::make_unique<SpirvShaderTranslator>();
|
||||||
} else {
|
} else {
|
||||||
translator = std::make_unique<UcodeShaderTranslator>();
|
translator = std::make_unique<UcodeShaderTranslator>();
|
||||||
}
|
}
|
||||||
|
@ -89,10 +91,20 @@ int shader_compiler_main(const std::vector<std::wstring>& args) {
|
||||||
auto translated_shader = translator->Translate(
|
auto translated_shader = translator->Translate(
|
||||||
shader_type, ucode_data_hash, ucode_dwords.data(), ucode_dwords.size());
|
shader_type, ucode_data_hash, ucode_dwords.data(), ucode_dwords.size());
|
||||||
|
|
||||||
|
const void* source_data = translated_shader->binary().data();
|
||||||
|
size_t source_data_size = translated_shader->binary().size();
|
||||||
|
|
||||||
|
if (FLAGS_shader_output_type == "spirvtext") {
|
||||||
|
// Disassemble SPIRV.
|
||||||
|
auto disasm_result = xe::ui::spirv::SpirvDisassembler().Disassemble(
|
||||||
|
reinterpret_cast<const uint32_t*>(source_data), source_data_size / 4);
|
||||||
|
source_data = disasm_result->text();
|
||||||
|
source_data_size = std::strlen(disasm_result->text()) + 1;
|
||||||
|
}
|
||||||
|
|
||||||
if (!FLAGS_shader_output.empty()) {
|
if (!FLAGS_shader_output.empty()) {
|
||||||
auto output_file = fopen(FLAGS_shader_output.c_str(), "w");
|
auto output_file = fopen(FLAGS_shader_output.c_str(), "w");
|
||||||
fwrite(translated_shader->binary().data(),
|
fwrite(source_data, source_data_size, 1, output_file);
|
||||||
translated_shader->binary().size(), 1, output_file);
|
|
||||||
fclose(output_file);
|
fclose(output_file);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -20,6 +20,428 @@
|
||||||
|
|
||||||
namespace xe {
|
namespace xe {
|
||||||
namespace gpu {
|
namespace gpu {
|
||||||
|
|
||||||
|
enum class InstructionStorageTarget {
|
||||||
|
// Result is not stored.
|
||||||
|
kNone,
|
||||||
|
// Result is stored to a temporary register indexed by storage_index [0-31].
|
||||||
|
kRegister,
|
||||||
|
// Result is stored into a vertex shader interpolant export [0-15].
|
||||||
|
kInterpolant,
|
||||||
|
// Result is stored to the position export (gl_Position).
|
||||||
|
kPosition,
|
||||||
|
// Result is stored to the point size export (gl_PointSize).
|
||||||
|
kPointSize,
|
||||||
|
// Result is stored to a color target export indexed by storage_index [0-3].
|
||||||
|
kColorTarget,
|
||||||
|
// Result is stored to the depth export (gl_FragDepth).
|
||||||
|
kDepth,
|
||||||
|
};
|
||||||
|
|
||||||
|
enum class InstructionStorageAddressingMode {
|
||||||
|
// The storage index is not dynamically addressed.
|
||||||
|
kStatic,
|
||||||
|
// The storage index is addressed by a0.
|
||||||
|
kAddressAbsolute,
|
||||||
|
// The storage index is addressed by aL.
|
||||||
|
kAddressRelative,
|
||||||
|
};
|
||||||
|
|
||||||
|
// Describes the source value of a particular component.
|
||||||
|
enum class SwizzleSource {
|
||||||
|
// Component receives the source X.
|
||||||
|
kX,
|
||||||
|
// Component receives the source Y.
|
||||||
|
kY,
|
||||||
|
// Component receives the source Z.
|
||||||
|
kZ,
|
||||||
|
// Component receives the source W.
|
||||||
|
kW,
|
||||||
|
// Component receives constant 0.
|
||||||
|
k0,
|
||||||
|
// Component receives constant 1.
|
||||||
|
k1,
|
||||||
|
};
|
||||||
|
|
||||||
|
constexpr SwizzleSource GetSwizzleFromComponentIndex(int i) {
|
||||||
|
return static_cast<SwizzleSource>(i);
|
||||||
|
}
|
||||||
|
inline char GetCharForSwizzle(SwizzleSource swizzle_source) {
|
||||||
|
const static char kChars[] = {'x', 'y', 'z', 'w', '0', '1'};
|
||||||
|
return kChars[static_cast<int>(swizzle_source)];
|
||||||
|
}
|
||||||
|
|
||||||
|
struct InstructionResult {
|
||||||
|
// Where the result is going.
|
||||||
|
InstructionStorageTarget storage_target = InstructionStorageTarget::kNone;
|
||||||
|
// Index into the storage_target, if it is indexed.
|
||||||
|
int storage_index = 0;
|
||||||
|
// How the storage index is dynamically addressed, if it is.
|
||||||
|
InstructionStorageAddressingMode storage_addressing_mode =
|
||||||
|
InstructionStorageAddressingMode::kStatic;
|
||||||
|
// True if the result is exporting from the shader.
|
||||||
|
bool is_export = false;
|
||||||
|
// True to clamp the result value to [0-1].
|
||||||
|
bool is_clamped = false;
|
||||||
|
// Defines whether each output component is written.
|
||||||
|
bool write_mask[4] = {false, false, false, false};
|
||||||
|
// Defines the source for each output component xyzw.
|
||||||
|
SwizzleSource components[4] = {SwizzleSource::kX, SwizzleSource::kY,
|
||||||
|
SwizzleSource::kZ, SwizzleSource::kW};
|
||||||
|
// Returns true if any component is written to.
|
||||||
|
bool has_any_writes() const {
|
||||||
|
return write_mask[0] || write_mask[1] || write_mask[2] || write_mask[3];
|
||||||
|
}
|
||||||
|
// Returns true if all components are written to.
|
||||||
|
bool has_all_writes() const {
|
||||||
|
return write_mask[0] && write_mask[1] && write_mask[2] && write_mask[3];
|
||||||
|
}
|
||||||
|
// True if the components are in their 'standard' swizzle arrangement (xyzw).
|
||||||
|
bool is_standard_swizzle() const {
|
||||||
|
return has_all_writes() && components[0] == SwizzleSource::kX &&
|
||||||
|
components[1] == SwizzleSource::kY &&
|
||||||
|
components[2] == SwizzleSource::kZ &&
|
||||||
|
components[3] == SwizzleSource::kW;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
enum class InstructionStorageSource {
|
||||||
|
// Source is stored in a temporary register indexed by storage_index [0-31].
|
||||||
|
kRegister,
|
||||||
|
// Source is stored in a float constant indexed by storage_index [0-511].
|
||||||
|
kConstantFloat,
|
||||||
|
// Source is stored in a float constant indexed by storage_index [0-31].
|
||||||
|
kConstantInt,
|
||||||
|
// Source is stored in a float constant indexed by storage_index [0-255].
|
||||||
|
kConstantBool,
|
||||||
|
// Source is stored in a vertex fetch constant indexed by storage_index
|
||||||
|
// [0-95].
|
||||||
|
kVertexFetchConstant,
|
||||||
|
// Source is stored in a texture fetch constant indexed by storage_index
|
||||||
|
// [0-31].
|
||||||
|
kTextureFetchConstant,
|
||||||
|
};
|
||||||
|
|
||||||
|
struct InstructionOperand {
|
||||||
|
// Where the source comes from.
|
||||||
|
InstructionStorageSource storage_source = InstructionStorageSource::kRegister;
|
||||||
|
// Index into the storage_target, if it is indexed.
|
||||||
|
int storage_index = 0;
|
||||||
|
// How the storage index is dynamically addressed, if it is.
|
||||||
|
InstructionStorageAddressingMode storage_addressing_mode =
|
||||||
|
InstructionStorageAddressingMode::kStatic;
|
||||||
|
// True to negate the operand value.
|
||||||
|
bool is_negated = false;
|
||||||
|
// True to take the absolute value of the source (before any negation).
|
||||||
|
bool is_absolute_value = false;
|
||||||
|
// Number of components taken from the source operand.
|
||||||
|
int component_count = 0;
|
||||||
|
// Defines the source for each component xyzw (up to the given
|
||||||
|
// component_count).
|
||||||
|
SwizzleSource components[4] = {SwizzleSource::kX, SwizzleSource::kY,
|
||||||
|
SwizzleSource::kZ, SwizzleSource::kW};
|
||||||
|
// True if the components are in their 'standard' swizzle arrangement (xyzw).
|
||||||
|
bool is_standard_swizzle() const {
|
||||||
|
switch (component_count) {
|
||||||
|
case 4:
|
||||||
|
return components[0] == SwizzleSource::kX &&
|
||||||
|
components[1] == SwizzleSource::kY &&
|
||||||
|
components[2] == SwizzleSource::kZ &&
|
||||||
|
components[3] == SwizzleSource::kW;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
struct ParsedExecInstruction {
|
||||||
|
// Index into the ucode dword source.
|
||||||
|
uint32_t dword_index = 0;
|
||||||
|
|
||||||
|
// Opcode for the instruction.
|
||||||
|
ucode::ControlFlowOpcode opcode;
|
||||||
|
// Friendly name of the instruction.
|
||||||
|
const char* opcode_name = nullptr;
|
||||||
|
|
||||||
|
// Instruction address where ALU/fetch instructions reside.
|
||||||
|
uint32_t instruction_address = 0;
|
||||||
|
// Number of instructions to execute.
|
||||||
|
uint32_t instruction_count = 0;
|
||||||
|
|
||||||
|
enum class Type {
|
||||||
|
// Block is always executed.
|
||||||
|
kUnconditional,
|
||||||
|
// Execution is conditional on the value of the boolean constant.
|
||||||
|
kConditional,
|
||||||
|
// Execution is predicated.
|
||||||
|
kPredicated,
|
||||||
|
};
|
||||||
|
// Condition required to execute the instructions.
|
||||||
|
Type type = Type::kUnconditional;
|
||||||
|
// Constant index used as the conditional if kConditional.
|
||||||
|
uint32_t bool_constant_index = 0;
|
||||||
|
// Required condition value of the comparision (true or false).
|
||||||
|
bool condition = false;
|
||||||
|
|
||||||
|
// Whether to reset the current predicate.
|
||||||
|
bool clean = true;
|
||||||
|
// ?
|
||||||
|
bool is_yield = false;
|
||||||
|
|
||||||
|
// Sequence bits, 2 per instruction, indicating whether ALU or fetch.
|
||||||
|
uint32_t sequence = 0;
|
||||||
|
|
||||||
|
// Disassembles the instruction into ucode assembly text.
|
||||||
|
void Disassemble(StringBuffer* out) const;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct ParsedLoopStartInstruction {
|
||||||
|
// Index into the ucode dword source.
|
||||||
|
uint32_t dword_index = 0;
|
||||||
|
|
||||||
|
// Integer constant register that holds the loop parameters.
|
||||||
|
// Byte-wise: [loop count, start, step [-128, 127], ?]
|
||||||
|
uint32_t loop_constant_index = 0;
|
||||||
|
// Whether to reuse the current aL instead of reset it to loop start.
|
||||||
|
bool is_repeat = false;
|
||||||
|
|
||||||
|
// Target address to jump to when skipping the loop.
|
||||||
|
uint32_t loop_skip_address = 0;
|
||||||
|
|
||||||
|
// Disassembles the instruction into ucode assembly text.
|
||||||
|
void Disassemble(StringBuffer* out) const;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct ParsedLoopEndInstruction {
|
||||||
|
// Index into the ucode dword source.
|
||||||
|
uint32_t dword_index = 0;
|
||||||
|
|
||||||
|
// Break from the loop if the predicate matches the expected value.
|
||||||
|
bool is_predicated_break = false;
|
||||||
|
// Required condition value of the comparision (true or false).
|
||||||
|
bool predicate_condition = false;
|
||||||
|
|
||||||
|
// Integer constant register that holds the loop parameters.
|
||||||
|
// Byte-wise: [loop count, start, step [-128, 127], ?]
|
||||||
|
uint32_t loop_constant_index = 0;
|
||||||
|
|
||||||
|
// Target address of the start of the loop body.
|
||||||
|
uint32_t loop_body_address = 0;
|
||||||
|
|
||||||
|
// Disassembles the instruction into ucode assembly text.
|
||||||
|
void Disassemble(StringBuffer* out) const;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct ParsedCallInstruction {
|
||||||
|
// Index into the ucode dword source.
|
||||||
|
uint32_t dword_index = 0;
|
||||||
|
|
||||||
|
// Target address.
|
||||||
|
uint32_t target_address = 0;
|
||||||
|
|
||||||
|
enum class Type {
|
||||||
|
// Call is always made.
|
||||||
|
kUnconditional,
|
||||||
|
// Call is conditional on the value of the boolean constant.
|
||||||
|
kConditional,
|
||||||
|
// Call is predicated.
|
||||||
|
kPredicated,
|
||||||
|
};
|
||||||
|
// Condition required to make the call.
|
||||||
|
Type type = Type::kUnconditional;
|
||||||
|
// Constant index used as the conditional if kConditional.
|
||||||
|
uint32_t bool_constant_index = 0;
|
||||||
|
// Required condition value of the comparision (true or false).
|
||||||
|
bool condition = false;
|
||||||
|
|
||||||
|
// Disassembles the instruction into ucode assembly text.
|
||||||
|
void Disassemble(StringBuffer* out) const;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct ParsedReturnInstruction {
|
||||||
|
// Index into the ucode dword source.
|
||||||
|
uint32_t dword_index = 0;
|
||||||
|
|
||||||
|
// Disassembles the instruction into ucode assembly text.
|
||||||
|
void Disassemble(StringBuffer* out) const;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct ParsedJumpInstruction {
|
||||||
|
// Index into the ucode dword source.
|
||||||
|
uint32_t dword_index = 0;
|
||||||
|
|
||||||
|
// Target address.
|
||||||
|
uint32_t target_address = 0;
|
||||||
|
|
||||||
|
enum class Type {
|
||||||
|
// Jump is always taken.
|
||||||
|
kUnconditional,
|
||||||
|
// Jump is conditional on the value of the boolean constant.
|
||||||
|
kConditional,
|
||||||
|
// Jump is predicated.
|
||||||
|
kPredicated,
|
||||||
|
};
|
||||||
|
// Condition required to make the jump.
|
||||||
|
Type type = Type::kUnconditional;
|
||||||
|
// Constant index used as the conditional if kConditional.
|
||||||
|
uint32_t bool_constant_index = 0;
|
||||||
|
// Required condition value of the comparision (true or false).
|
||||||
|
bool condition = false;
|
||||||
|
|
||||||
|
// Disassembles the instruction into ucode assembly text.
|
||||||
|
void Disassemble(StringBuffer* out) const;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct ParsedAllocInstruction {
|
||||||
|
// Index into the ucode dword source.
|
||||||
|
uint32_t dword_index = 0;
|
||||||
|
|
||||||
|
// The type of resource being allocated.
|
||||||
|
ucode::AllocType type = ucode::AllocType::kNone;
|
||||||
|
// Total count associated with the allocation.
|
||||||
|
int count = 0;
|
||||||
|
|
||||||
|
// True if this allocation is in a vertex shader.
|
||||||
|
bool is_vertex_shader = false;
|
||||||
|
|
||||||
|
// Disassembles the instruction into ucode assembly text.
|
||||||
|
void Disassemble(StringBuffer* out) const;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct ParsedVertexFetchInstruction {
|
||||||
|
// Index into the ucode dword source.
|
||||||
|
uint32_t dword_index = 0;
|
||||||
|
|
||||||
|
// Opcode for the instruction.
|
||||||
|
ucode::FetchOpcode opcode;
|
||||||
|
// Friendly name of the instruction.
|
||||||
|
const char* opcode_name = nullptr;
|
||||||
|
|
||||||
|
// True if the fetch is reusing a previous full fetch.
|
||||||
|
// The previous fetch source and constant data will be populated.
|
||||||
|
bool is_mini_fetch = false;
|
||||||
|
|
||||||
|
// True if the instruction is predicated on the specified
|
||||||
|
// predicate_condition.
|
||||||
|
bool is_predicated = false;
|
||||||
|
// Expected predication condition value if predicated.
|
||||||
|
bool predicate_condition = false;
|
||||||
|
|
||||||
|
// Describes how the instruction result is stored.
|
||||||
|
InstructionResult result;
|
||||||
|
|
||||||
|
// Number of source operands.
|
||||||
|
size_t operand_count = 0;
|
||||||
|
// Describes each source operand.
|
||||||
|
InstructionOperand operands[2];
|
||||||
|
|
||||||
|
struct Attributes {
|
||||||
|
VertexFormat data_format = VertexFormat::kUndefined;
|
||||||
|
int offset = 0;
|
||||||
|
int stride = 0;
|
||||||
|
int exp_adjust = 0;
|
||||||
|
bool is_index_rounded = false;
|
||||||
|
bool is_signed = false;
|
||||||
|
bool is_integer = false;
|
||||||
|
int prefetch_count = 0;
|
||||||
|
};
|
||||||
|
// Attributes describing the fetch operation.
|
||||||
|
Attributes attributes;
|
||||||
|
|
||||||
|
// Disassembles the instruction into ucode assembly text.
|
||||||
|
void Disassemble(StringBuffer* out) const;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct ParsedTextureFetchInstruction {
|
||||||
|
// Index into the ucode dword source.
|
||||||
|
uint32_t dword_index = 0;
|
||||||
|
|
||||||
|
// Opcode for the instruction.
|
||||||
|
ucode::FetchOpcode opcode;
|
||||||
|
// Friendly name of the instruction.
|
||||||
|
const char* opcode_name = nullptr;
|
||||||
|
// Texture dimension for opcodes that have multiple dimension forms.
|
||||||
|
TextureDimension dimension = TextureDimension::k1D;
|
||||||
|
|
||||||
|
// True if the instruction is predicated on the specified
|
||||||
|
// predicate_condition.
|
||||||
|
bool is_predicated = false;
|
||||||
|
// Expected predication condition value if predicated.
|
||||||
|
bool predicate_condition = false;
|
||||||
|
|
||||||
|
// True if the instruction has a result.
|
||||||
|
bool has_result() const {
|
||||||
|
return result.storage_target != InstructionStorageTarget::kNone;
|
||||||
|
}
|
||||||
|
// Describes how the instruction result is stored.
|
||||||
|
InstructionResult result;
|
||||||
|
|
||||||
|
// Number of source operands.
|
||||||
|
size_t operand_count = 0;
|
||||||
|
// Describes each source operand.
|
||||||
|
InstructionOperand operands[2];
|
||||||
|
|
||||||
|
struct Attributes {
|
||||||
|
bool fetch_valid_only = true;
|
||||||
|
bool unnormalized_coordinates = false;
|
||||||
|
TextureFilter mag_filter = TextureFilter::kUseFetchConst;
|
||||||
|
TextureFilter min_filter = TextureFilter::kUseFetchConst;
|
||||||
|
TextureFilter mip_filter = TextureFilter::kUseFetchConst;
|
||||||
|
AnisoFilter aniso_filter = AnisoFilter::kUseFetchConst;
|
||||||
|
bool use_computed_lod = true;
|
||||||
|
bool use_register_lod = false;
|
||||||
|
bool use_register_gradients = false;
|
||||||
|
float offset_x = 0.0f;
|
||||||
|
float offset_y = 0.0f;
|
||||||
|
float offset_z = 0.0f;
|
||||||
|
};
|
||||||
|
// Attributes describing the fetch operation.
|
||||||
|
Attributes attributes;
|
||||||
|
|
||||||
|
// Disassembles the instruction into ucode assembly text.
|
||||||
|
void Disassemble(StringBuffer* out) const;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct ParsedAluInstruction {
|
||||||
|
// Index into the ucode dword source.
|
||||||
|
uint32_t dword_index = 0;
|
||||||
|
|
||||||
|
enum class Type {
|
||||||
|
kNop,
|
||||||
|
kVector,
|
||||||
|
kScalar,
|
||||||
|
};
|
||||||
|
// Type of the instruction.
|
||||||
|
Type type = Type::kNop;
|
||||||
|
bool is_nop() const { return type == Type::kNop; }
|
||||||
|
bool is_vector_type() const { return type == Type::kVector; }
|
||||||
|
bool is_scalar_type() const { return type == Type::kScalar; }
|
||||||
|
// Opcode for the instruction if it is a vector type.
|
||||||
|
ucode::AluVectorOpcode vector_opcode = ucode::AluVectorOpcode::kADDv;
|
||||||
|
// Opcode for the instruction if it is a scalar type.
|
||||||
|
ucode::AluScalarOpcode scalar_opcode = ucode::AluScalarOpcode::kADDs;
|
||||||
|
// Friendly name of the instruction.
|
||||||
|
const char* opcode_name = nullptr;
|
||||||
|
|
||||||
|
// True if the instruction is paired with another instruction.
|
||||||
|
bool is_paired = false;
|
||||||
|
// True if the instruction is predicated on the specified
|
||||||
|
// predicate_condition.
|
||||||
|
bool is_predicated = false;
|
||||||
|
// Expected predication condition value if predicated.
|
||||||
|
bool predicate_condition = false;
|
||||||
|
|
||||||
|
// Describes how the instruction result is stored.
|
||||||
|
InstructionResult result;
|
||||||
|
|
||||||
|
// Number of source operands.
|
||||||
|
size_t operand_count = 0;
|
||||||
|
// Describes each source operand.
|
||||||
|
InstructionOperand operands[3];
|
||||||
|
|
||||||
|
// Disassembles the instruction into ucode assembly text.
|
||||||
|
void Disassemble(StringBuffer* out) const;
|
||||||
|
};
|
||||||
|
|
||||||
class TranslatedShader {
|
class TranslatedShader {
|
||||||
public:
|
public:
|
||||||
struct Error {
|
struct Error {
|
||||||
|
@ -33,15 +455,16 @@ class TranslatedShader {
|
||||||
// Fetch constant index [0-95].
|
// Fetch constant index [0-95].
|
||||||
uint32_t fetch_constant;
|
uint32_t fetch_constant;
|
||||||
// Fetch instruction with all parameters.
|
// Fetch instruction with all parameters.
|
||||||
ucode::VertexFetchInstruction op;
|
ParsedVertexFetchInstruction fetch_instr;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct TextureBinding {
|
struct TextureBinding {
|
||||||
// Index within the texture binding listing.
|
// Index within the texture binding listing.
|
||||||
size_t binding_index;
|
size_t binding_index;
|
||||||
// Fetch constant index [0-31].
|
// Fetch constant index [0-31].
|
||||||
uint32_t fetch_constant;
|
uint32_t fetch_constant;
|
||||||
// Fetch instruction with all parameters.
|
// Fetch instruction with all parameters.
|
||||||
ucode::TextureFetchInstruction op;
|
ParsedTextureFetchInstruction fetch_instr;
|
||||||
};
|
};
|
||||||
|
|
||||||
~TranslatedShader();
|
~TranslatedShader();
|
||||||
|
@ -59,9 +482,9 @@ class TranslatedShader {
|
||||||
}
|
}
|
||||||
|
|
||||||
bool is_valid() const { return is_valid_; }
|
bool is_valid() const { return is_valid_; }
|
||||||
const std::vector<Error> errors() const { return errors_; }
|
const std::vector<Error>& errors() const { return errors_; }
|
||||||
|
|
||||||
const std::vector<uint8_t> binary() const { return binary_; }
|
const std::vector<uint8_t>& binary() const { return binary_; }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
friend class ShaderTranslator;
|
friend class ShaderTranslator;
|
||||||
|
@ -95,146 +518,80 @@ class ShaderTranslator {
|
||||||
protected:
|
protected:
|
||||||
ShaderTranslator();
|
ShaderTranslator();
|
||||||
|
|
||||||
|
// True if the current shader is a vertex shader.
|
||||||
|
bool is_vertex_shader() const { return shader_type_ == ShaderType::kVertex; }
|
||||||
|
// True if the current shader is a pixel shader.
|
||||||
|
bool is_pixel_shader() const { return shader_type_ == ShaderType::kPixel; }
|
||||||
|
// A list of all vertex bindings, populated before translation occurs.
|
||||||
|
const std::vector<TranslatedShader::VertexBinding>& vertex_bindings() const {
|
||||||
|
return vertex_bindings_;
|
||||||
|
}
|
||||||
|
// A list of all texture bindings, populated before translation occurs.
|
||||||
|
const std::vector<TranslatedShader::TextureBinding>& texture_bindings()
|
||||||
|
const {
|
||||||
|
return texture_bindings_;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Current line number in the ucode disassembly.
|
||||||
size_t ucode_disasm_line_number() const { return ucode_disasm_line_number_; }
|
size_t ucode_disasm_line_number() const { return ucode_disasm_line_number_; }
|
||||||
|
// Ucode disassembly buffer accumulated during translation.
|
||||||
StringBuffer& ucode_disasm_buffer() { return ucode_disasm_buffer_; }
|
StringBuffer& ucode_disasm_buffer() { return ucode_disasm_buffer_; }
|
||||||
|
// Emits a translation error that will be passed back in the result.
|
||||||
void EmitTranslationError(const char* message);
|
void EmitTranslationError(const char* message);
|
||||||
|
|
||||||
|
// 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() {
|
virtual std::vector<uint8_t> CompleteTranslation() {
|
||||||
return std::vector<uint8_t>();
|
return std::vector<uint8_t>();
|
||||||
}
|
}
|
||||||
|
|
||||||
virtual void ProcessControlFlowNop(const ucode::ControlFlowInstruction& cf) {}
|
// Handles translation for control flow label addresses.
|
||||||
virtual void ProcessControlFlowExec(
|
// This is triggered once for each label required (due to control flow
|
||||||
const ucode::ControlFlowExecInstruction& cf) {}
|
// operations) before any of the instructions within the target exec.
|
||||||
virtual void ProcessControlFlowCondExec(
|
virtual void ProcessLabel(uint32_t cf_index) {}
|
||||||
const ucode::ControlFlowCondExecInstruction& cf) {}
|
|
||||||
virtual void ProcessControlFlowCondExecPred(
|
|
||||||
const ucode::ControlFlowCondExecPredInstruction& cf) {}
|
|
||||||
virtual void ProcessControlFlowLoopStart(
|
|
||||||
const ucode::ControlFlowLoopStartInstruction& cf) {}
|
|
||||||
virtual void ProcessControlFlowLoopEnd(
|
|
||||||
const ucode::ControlFlowLoopEndInstruction& cf) {}
|
|
||||||
virtual void ProcessControlFlowCondCall(
|
|
||||||
const ucode::ControlFlowCondCallInstruction& cf) {}
|
|
||||||
virtual void ProcessControlFlowReturn(
|
|
||||||
const ucode::ControlFlowReturnInstruction& cf) {}
|
|
||||||
virtual void ProcessControlFlowCondJmp(
|
|
||||||
const ucode::ControlFlowCondJmpInstruction& cf) {}
|
|
||||||
virtual void ProcessControlFlowAlloc(
|
|
||||||
const ucode::ControlFlowAllocInstruction& cf) {}
|
|
||||||
|
|
||||||
|
// Handles translation for control flow nop instructions.
|
||||||
|
virtual void ProcessControlFlowNopInstruction() {}
|
||||||
|
// 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.
|
||||||
|
virtual void ProcessAllocInstruction(const ParsedAllocInstruction& instr) {}
|
||||||
|
|
||||||
|
// Handles translation for vertex fetch instructions.
|
||||||
virtual void ProcessVertexFetchInstruction(
|
virtual void ProcessVertexFetchInstruction(
|
||||||
const ucode::VertexFetchInstruction& op) {}
|
const ParsedVertexFetchInstruction& instr) {}
|
||||||
virtual void ProcessTextureFetchTextureFetch(
|
// Handles translation for texture fetch instructions.
|
||||||
const ucode::TextureFetchInstruction& op) {}
|
virtual void ProcessTextureFetchInstruction(
|
||||||
virtual void ProcessTextureFetchGetTextureBorderColorFrac(
|
const ParsedTextureFetchInstruction& instr) {}
|
||||||
const ucode::TextureFetchInstruction& op) {}
|
// Handles translation for ALU instructions.
|
||||||
virtual void ProcessTextureFetchGetTextureComputedLod(
|
virtual void ProcessAluInstruction(const ParsedAluInstruction& instr) {}
|
||||||
const ucode::TextureFetchInstruction& op) {}
|
|
||||||
virtual void ProcessTextureFetchGetTextureGradients(
|
|
||||||
const ucode::TextureFetchInstruction& op) {}
|
|
||||||
virtual void ProcessTextureFetchGetTextureWeights(
|
|
||||||
const ucode::TextureFetchInstruction& op) {}
|
|
||||||
virtual void ProcessTextureFetchSetTextureLod(
|
|
||||||
const ucode::TextureFetchInstruction& op) {}
|
|
||||||
virtual void ProcessTextureFetchSetTextureGradientsHorz(
|
|
||||||
const ucode::TextureFetchInstruction& op) {}
|
|
||||||
virtual void ProcessTextureFetchSetTextureGradientsVert(
|
|
||||||
const ucode::TextureFetchInstruction& op) {}
|
|
||||||
|
|
||||||
virtual void ProcessAluNop(const ucode::AluInstruction& op) {}
|
|
||||||
|
|
||||||
virtual void ProcessAluVectorAdd(const ucode::AluInstruction& op) {}
|
|
||||||
virtual void ProcessAluVectorMul(const ucode::AluInstruction& op) {}
|
|
||||||
virtual void ProcessAluVectorMax(const ucode::AluInstruction& op) {}
|
|
||||||
virtual void ProcessAluVectorMin(const ucode::AluInstruction& op) {}
|
|
||||||
virtual void ProcessAluVectorSetEQ(const ucode::AluInstruction& op) {}
|
|
||||||
virtual void ProcessAluVectorSetGT(const ucode::AluInstruction& op) {}
|
|
||||||
virtual void ProcessAluVectorSetGE(const ucode::AluInstruction& op) {}
|
|
||||||
virtual void ProcessAluVectorSetNE(const ucode::AluInstruction& op) {}
|
|
||||||
virtual void ProcessAluVectorFrac(const ucode::AluInstruction& op) {}
|
|
||||||
virtual void ProcessAluVectorTrunc(const ucode::AluInstruction& op) {}
|
|
||||||
virtual void ProcessAluVectorFloor(const ucode::AluInstruction& op) {}
|
|
||||||
virtual void ProcessAluVectorMad(const ucode::AluInstruction& op) {}
|
|
||||||
virtual void ProcessAluVectorCndEQ(const ucode::AluInstruction& op) {}
|
|
||||||
virtual void ProcessAluVectorCndGE(const ucode::AluInstruction& op) {}
|
|
||||||
virtual void ProcessAluVectorCndGT(const ucode::AluInstruction& op) {}
|
|
||||||
virtual void ProcessAluVectorDp4(const ucode::AluInstruction& op) {}
|
|
||||||
virtual void ProcessAluVectorDp3(const ucode::AluInstruction& op) {}
|
|
||||||
virtual void ProcessAluVectorDp2Add(const ucode::AluInstruction& op) {}
|
|
||||||
virtual void ProcessAluVectorCube(const ucode::AluInstruction& op) {}
|
|
||||||
virtual void ProcessAluVectorMax4(const ucode::AluInstruction& op) {}
|
|
||||||
virtual void ProcessAluVectorPredSetEQPush(const ucode::AluInstruction& op) {}
|
|
||||||
virtual void ProcessAluVectorPredSetNEPush(const ucode::AluInstruction& op) {}
|
|
||||||
virtual void ProcessAluVectorPredSetGTPush(const ucode::AluInstruction& op) {}
|
|
||||||
virtual void ProcessAluVectorPredSetGEPush(const ucode::AluInstruction& op) {}
|
|
||||||
virtual void ProcessAluVectorKillEQ(const ucode::AluInstruction& op) {}
|
|
||||||
virtual void ProcessAluVectorKillGT(const ucode::AluInstruction& op) {}
|
|
||||||
virtual void ProcessAluVectorKillLGE(const ucode::AluInstruction& op) {}
|
|
||||||
virtual void ProcessAluVectorKillNE(const ucode::AluInstruction& op) {}
|
|
||||||
virtual void ProcessAluVectorDst(const ucode::AluInstruction& op) {}
|
|
||||||
virtual void ProcessAluVectorMaxA(const ucode::AluInstruction& op) {}
|
|
||||||
|
|
||||||
virtual void ProcessAluScalarAdd(const ucode::AluInstruction& op) {}
|
|
||||||
virtual void ProcessAluScalarAddPrev(const ucode::AluInstruction& op) {}
|
|
||||||
virtual void ProcessAluScalarMul(const ucode::AluInstruction& op) {}
|
|
||||||
virtual void ProcessAluScalarMulPrev(const ucode::AluInstruction& op) {}
|
|
||||||
virtual void ProcessAluScalarMulPrev2(const ucode::AluInstruction& op) {}
|
|
||||||
virtual void ProcessAluScalarMax(const ucode::AluInstruction& op) {}
|
|
||||||
virtual void ProcessAluScalarMin(const ucode::AluInstruction& op) {}
|
|
||||||
virtual void ProcessAluScalarSetEQ(const ucode::AluInstruction& op) {}
|
|
||||||
virtual void ProcessAluScalarSetGT(const ucode::AluInstruction& op) {}
|
|
||||||
virtual void ProcessAluScalarSetGE(const ucode::AluInstruction& op) {}
|
|
||||||
virtual void ProcessAluScalarSetNE(const ucode::AluInstruction& op) {}
|
|
||||||
virtual void ProcessAluScalarFrac(const ucode::AluInstruction& op) {}
|
|
||||||
virtual void ProcessAluScalarTrunc(const ucode::AluInstruction& op) {}
|
|
||||||
virtual void ProcessAluScalarFloor(const ucode::AluInstruction& op) {}
|
|
||||||
virtual void ProcessAluScalarExp(const ucode::AluInstruction& op) {}
|
|
||||||
virtual void ProcessAluScalarLogClamp(const ucode::AluInstruction& op) {}
|
|
||||||
virtual void ProcessAluScalarLog(const ucode::AluInstruction& op) {}
|
|
||||||
virtual void ProcessAluScalarRecipClamp(const ucode::AluInstruction& op) {}
|
|
||||||
virtual void ProcessAluScalarRecipFixedFunc(const ucode::AluInstruction& op) {
|
|
||||||
}
|
|
||||||
virtual void ProcessAluScalarRecip(const ucode::AluInstruction& op) {}
|
|
||||||
virtual void ProcessAluScalarRSqrtClamp(const ucode::AluInstruction& op) {}
|
|
||||||
virtual void ProcessAluScalarRSqrtFixedFunc(const ucode::AluInstruction& op) {
|
|
||||||
}
|
|
||||||
virtual void ProcessAluScalarRSqrt(const ucode::AluInstruction& op) {}
|
|
||||||
virtual void ProcessAluScalarMovA(const ucode::AluInstruction& op) {}
|
|
||||||
virtual void ProcessAluScalarMovAFloor(const ucode::AluInstruction& op) {}
|
|
||||||
virtual void ProcessAluScalarSub(const ucode::AluInstruction& op) {}
|
|
||||||
virtual void ProcessAluScalarSubPrev(const ucode::AluInstruction& op) {}
|
|
||||||
virtual void ProcessAluScalarPredSetEQ(const ucode::AluInstruction& op) {}
|
|
||||||
virtual void ProcessAluScalarPredSetNE(const ucode::AluInstruction& op) {}
|
|
||||||
virtual void ProcessAluScalarPredSetGT(const ucode::AluInstruction& op) {}
|
|
||||||
virtual void ProcessAluScalarPredSetGE(const ucode::AluInstruction& op) {}
|
|
||||||
virtual void ProcessAluScalarPredSetInv(const ucode::AluInstruction& op) {}
|
|
||||||
virtual void ProcessAluScalarPredSetPop(const ucode::AluInstruction& op) {}
|
|
||||||
virtual void ProcessAluScalarPredSetClear(const ucode::AluInstruction& op) {}
|
|
||||||
virtual void ProcessAluScalarPredSetRestore(const ucode::AluInstruction& op) {
|
|
||||||
}
|
|
||||||
virtual void ProcessAluScalarKillEQ(const ucode::AluInstruction& op) {}
|
|
||||||
virtual void ProcessAluScalarKillGT(const ucode::AluInstruction& op) {}
|
|
||||||
virtual void ProcessAluScalarKillGE(const ucode::AluInstruction& op) {}
|
|
||||||
virtual void ProcessAluScalarKillNE(const ucode::AluInstruction& op) {}
|
|
||||||
virtual void ProcessAluScalarKillOne(const ucode::AluInstruction& op) {}
|
|
||||||
virtual void ProcessAluScalarSqrt(const ucode::AluInstruction& op) {}
|
|
||||||
virtual void ProcessAluScalarMulConst0(const ucode::AluInstruction& op) {}
|
|
||||||
virtual void ProcessAluScalarMulConst1(const ucode::AluInstruction& op) {}
|
|
||||||
virtual void ProcessAluScalarAddConst0(const ucode::AluInstruction& op) {}
|
|
||||||
virtual void ProcessAluScalarAddConst1(const ucode::AluInstruction& op) {}
|
|
||||||
virtual void ProcessAluScalarSubConst0(const ucode::AluInstruction& op) {}
|
|
||||||
virtual void ProcessAluScalarSubConst1(const ucode::AluInstruction& op) {}
|
|
||||||
virtual void ProcessAluScalarSin(const ucode::AluInstruction& op) {}
|
|
||||||
virtual void ProcessAluScalarCos(const ucode::AluInstruction& op) {}
|
|
||||||
virtual void ProcessAluScalarRetainPrev(const ucode::AluInstruction& op) {}
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
struct AluOpcodeInfo {
|
struct AluOpcodeInfo {
|
||||||
const char* name;
|
const char* name;
|
||||||
size_t argument_count;
|
size_t argument_count;
|
||||||
int src_swizzle_component_count;
|
int src_swizzle_component_count;
|
||||||
void (ShaderTranslator::*fn)(const ucode::AluInstruction& op);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
void MarkUcodeInstruction(uint32_t dword_offset);
|
void MarkUcodeInstruction(uint32_t dword_offset);
|
||||||
|
@ -266,28 +623,22 @@ class ShaderTranslator {
|
||||||
const ucode::ControlFlowCondJmpInstruction& cf);
|
const ucode::ControlFlowCondJmpInstruction& cf);
|
||||||
void TranslateControlFlowAlloc(const ucode::ControlFlowAllocInstruction& cf);
|
void TranslateControlFlowAlloc(const ucode::ControlFlowAllocInstruction& cf);
|
||||||
|
|
||||||
void TranslateExecInstructions(uint32_t address, uint32_t count,
|
void TranslateExecInstructions(const ParsedExecInstruction& instr);
|
||||||
uint32_t sequence);
|
|
||||||
|
|
||||||
void DisasmFetchDestReg(uint32_t dest, uint32_t swizzle, bool is_relative);
|
|
||||||
void DisasmFetchSourceReg(uint32_t src, uint32_t swizzle, bool is_relative,
|
|
||||||
int component_count);
|
|
||||||
void TranslateVertexFetchInstruction(const ucode::VertexFetchInstruction& op);
|
void TranslateVertexFetchInstruction(const ucode::VertexFetchInstruction& op);
|
||||||
|
void ParseVertexFetchInstruction(const ucode::VertexFetchInstruction& op,
|
||||||
|
ParsedVertexFetchInstruction* out_instr);
|
||||||
|
|
||||||
void TranslateTextureFetchInstruction(
|
void TranslateTextureFetchInstruction(
|
||||||
const ucode::TextureFetchInstruction& op);
|
const ucode::TextureFetchInstruction& op);
|
||||||
void DisasmVertexFetchAttributes(const ucode::VertexFetchInstruction& op);
|
void ParseTextureFetchInstruction(const ucode::TextureFetchInstruction& op,
|
||||||
void DisasmTextureFetchAttributes(const ucode::TextureFetchInstruction& op);
|
ParsedTextureFetchInstruction* out_instr);
|
||||||
|
|
||||||
void TranslateAluInstruction(const ucode::AluInstruction& op);
|
void TranslateAluInstruction(const ucode::AluInstruction& op);
|
||||||
void DisasmAluVectorInstruction(const ucode::AluInstruction& op,
|
void ParseAluVectorInstruction(const ucode::AluInstruction& op,
|
||||||
const AluOpcodeInfo& opcode_info);
|
const AluOpcodeInfo& opcode_info);
|
||||||
void DisasmAluScalarInstruction(const ucode::AluInstruction& op,
|
void ParseAluScalarInstruction(const ucode::AluInstruction& op,
|
||||||
const AluOpcodeInfo& opcode_info);
|
const AluOpcodeInfo& opcode_info);
|
||||||
void DisasmAluSourceReg(const ucode::AluInstruction& op, int i,
|
|
||||||
int swizzle_component_count);
|
|
||||||
void DisasmAluSourceRegScalarSpecial(const ucode::AluInstruction& op,
|
|
||||||
uint32_t reg, bool is_temp, bool negate,
|
|
||||||
int const_slot, uint32_t swizzle);
|
|
||||||
|
|
||||||
// Input shader metadata and microcode.
|
// Input shader metadata and microcode.
|
||||||
ShaderType shader_type_;
|
ShaderType shader_type_;
|
||||||
|
|
|
@ -0,0 +1,458 @@
|
||||||
|
/**
|
||||||
|
******************************************************************************
|
||||||
|
* 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. *
|
||||||
|
******************************************************************************
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "xenia/gpu/shader_translator.h"
|
||||||
|
|
||||||
|
#include <cstdarg>
|
||||||
|
#include <set>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
#include "xenia/base/math.h"
|
||||||
|
|
||||||
|
namespace xe {
|
||||||
|
namespace gpu {
|
||||||
|
|
||||||
|
using namespace ucode;
|
||||||
|
|
||||||
|
void DisassembleResultOperand(const InstructionResult& result,
|
||||||
|
StringBuffer* out) {
|
||||||
|
bool uses_storage_index = false;
|
||||||
|
switch (result.storage_target) {
|
||||||
|
case InstructionStorageTarget::kRegister:
|
||||||
|
out->Append('r');
|
||||||
|
uses_storage_index = true;
|
||||||
|
break;
|
||||||
|
case InstructionStorageTarget::kInterpolant:
|
||||||
|
out->Append('o');
|
||||||
|
uses_storage_index = true;
|
||||||
|
break;
|
||||||
|
case InstructionStorageTarget::kPosition:
|
||||||
|
out->Append("oPos");
|
||||||
|
break;
|
||||||
|
case InstructionStorageTarget::kPointSize:
|
||||||
|
out->Append("oPts");
|
||||||
|
break;
|
||||||
|
case InstructionStorageTarget::kColorTarget:
|
||||||
|
out->AppendFormat("oC");
|
||||||
|
uses_storage_index = true;
|
||||||
|
break;
|
||||||
|
case InstructionStorageTarget::kDepth:
|
||||||
|
out->Append("oDepth");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (uses_storage_index) {
|
||||||
|
switch (result.storage_addressing_mode) {
|
||||||
|
case InstructionStorageAddressingMode::kStatic:
|
||||||
|
out->AppendFormat("%d", result.storage_index);
|
||||||
|
break;
|
||||||
|
case InstructionStorageAddressingMode::kAddressAbsolute:
|
||||||
|
out->AppendFormat("[%d+a0]", result.storage_index);
|
||||||
|
break;
|
||||||
|
case InstructionStorageAddressingMode::kAddressRelative:
|
||||||
|
out->AppendFormat("[%d+aL]", result.storage_index);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!result.has_any_writes()) {
|
||||||
|
out->Append("._");
|
||||||
|
} else if (!result.is_standard_swizzle()) {
|
||||||
|
out->Append('.');
|
||||||
|
for (int i = 0; i < 4; ++i) {
|
||||||
|
if (result.write_mask[i]) {
|
||||||
|
out->Append(GetCharForSwizzle(result.components[i]));
|
||||||
|
} else {
|
||||||
|
out->Append('_');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void DisassembleSourceOperand(const InstructionOperand& op, StringBuffer* out) {
|
||||||
|
if (op.is_negated) {
|
||||||
|
out->Append('-');
|
||||||
|
}
|
||||||
|
switch (op.storage_source) {
|
||||||
|
case InstructionStorageSource::kRegister:
|
||||||
|
out->Append('r');
|
||||||
|
break;
|
||||||
|
case InstructionStorageSource::kConstantFloat:
|
||||||
|
out->Append('c');
|
||||||
|
break;
|
||||||
|
case InstructionStorageSource::kConstantInt:
|
||||||
|
out->Append('i');
|
||||||
|
break;
|
||||||
|
case InstructionStorageSource::kConstantBool:
|
||||||
|
out->Append('b');
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
switch (op.storage_addressing_mode) {
|
||||||
|
case InstructionStorageAddressingMode::kStatic:
|
||||||
|
out->AppendFormat("%d", op.storage_index);
|
||||||
|
break;
|
||||||
|
case InstructionStorageAddressingMode::kAddressAbsolute:
|
||||||
|
out->AppendFormat("[%d+a0]", op.storage_index);
|
||||||
|
break;
|
||||||
|
case InstructionStorageAddressingMode::kAddressRelative:
|
||||||
|
out->AppendFormat("[%d+aL]", op.storage_index);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (op.is_absolute_value) {
|
||||||
|
out->Append("_abs");
|
||||||
|
}
|
||||||
|
if (!op.is_standard_swizzle()) {
|
||||||
|
out->Append('.');
|
||||||
|
if (op.component_count == 1) {
|
||||||
|
out->Append(GetCharForSwizzle(op.components[0]));
|
||||||
|
} else if (op.component_count == 2) {
|
||||||
|
out->Append(GetCharForSwizzle(op.components[0]));
|
||||||
|
out->Append(GetCharForSwizzle(op.components[1]));
|
||||||
|
} else {
|
||||||
|
for (int j = 0; j < op.component_count; ++j) {
|
||||||
|
out->Append(GetCharForSwizzle(op.components[j]));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void ParsedExecInstruction::Disassemble(StringBuffer* out) const {
|
||||||
|
switch (type) {
|
||||||
|
case Type::kUnconditional:
|
||||||
|
out->AppendFormat(" %s ", opcode_name);
|
||||||
|
break;
|
||||||
|
case Type::kPredicated:
|
||||||
|
out->Append(condition ? " (p0) " : "(!p0) ");
|
||||||
|
out->AppendFormat("%s ", opcode_name);
|
||||||
|
break;
|
||||||
|
case Type::kConditional:
|
||||||
|
out->AppendFormat(" %s ", opcode_name);
|
||||||
|
if (!condition) {
|
||||||
|
out->Append('!');
|
||||||
|
}
|
||||||
|
out->AppendFormat("b%u", bool_constant_index);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (is_yield) {
|
||||||
|
out->Append(", Yield=true");
|
||||||
|
}
|
||||||
|
if (!clean) {
|
||||||
|
out->Append(" // PredicateClean=false");
|
||||||
|
}
|
||||||
|
out->Append('\n');
|
||||||
|
}
|
||||||
|
|
||||||
|
void ParsedLoopStartInstruction::Disassemble(StringBuffer* out) const {
|
||||||
|
out->Append(" loop ");
|
||||||
|
out->AppendFormat("i%u, L%u", loop_constant_index, loop_skip_address);
|
||||||
|
if (is_repeat) {
|
||||||
|
out->Append(", Repeat=true");
|
||||||
|
}
|
||||||
|
out->Append('\n');
|
||||||
|
}
|
||||||
|
|
||||||
|
void ParsedLoopEndInstruction::Disassemble(StringBuffer* out) const {
|
||||||
|
if (is_predicated_break) {
|
||||||
|
out->Append(predicate_condition ? " (p0) " : "(!p0) ");
|
||||||
|
}
|
||||||
|
out->AppendFormat("endloop i%u, L%u", loop_constant_index, loop_body_address);
|
||||||
|
out->Append('\n');
|
||||||
|
}
|
||||||
|
|
||||||
|
void ParsedCallInstruction::Disassemble(StringBuffer* out) const {
|
||||||
|
switch (type) {
|
||||||
|
case Type::kUnconditional:
|
||||||
|
out->Append(" call ");
|
||||||
|
break;
|
||||||
|
case Type::kPredicated:
|
||||||
|
out->Append(condition ? " (p0) " : "(!p0) ");
|
||||||
|
out->Append("call ");
|
||||||
|
break;
|
||||||
|
case Type::kConditional:
|
||||||
|
out->Append(" ccall ");
|
||||||
|
if (!condition) {
|
||||||
|
out->Append('!');
|
||||||
|
}
|
||||||
|
out->AppendFormat("b%u, ", bool_constant_index);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
out->AppendFormat("L%u", target_address);
|
||||||
|
out->Append('\n');
|
||||||
|
}
|
||||||
|
|
||||||
|
void ParsedReturnInstruction::Disassemble(StringBuffer* out) const {
|
||||||
|
out->Append(" ret\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
void ParsedJumpInstruction::Disassemble(StringBuffer* out) const {
|
||||||
|
switch (type) {
|
||||||
|
case Type::kUnconditional:
|
||||||
|
out->Append(" jmp ");
|
||||||
|
break;
|
||||||
|
case Type::kPredicated:
|
||||||
|
out->Append(condition ? " (p0) " : "(!p0) ");
|
||||||
|
out->Append("jmp ");
|
||||||
|
break;
|
||||||
|
case Type::kConditional:
|
||||||
|
out->Append(" cjmp ");
|
||||||
|
if (!condition) {
|
||||||
|
out->Append('!');
|
||||||
|
}
|
||||||
|
out->AppendFormat("b%u, ", bool_constant_index);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
out->AppendFormat("L%u", target_address);
|
||||||
|
out->Append('\n');
|
||||||
|
}
|
||||||
|
|
||||||
|
void ParsedAllocInstruction::Disassemble(StringBuffer* out) const {
|
||||||
|
out->Append(" alloc ");
|
||||||
|
switch (type) {
|
||||||
|
case AllocType::kNone:
|
||||||
|
break;
|
||||||
|
case AllocType::kVsPosition:
|
||||||
|
out->AppendFormat("position");
|
||||||
|
break;
|
||||||
|
case AllocType::kVsInterpolators: // or AllocType::kPsColors
|
||||||
|
if (is_vertex_shader) {
|
||||||
|
out->Append("interpolators");
|
||||||
|
} else {
|
||||||
|
out->Append("colors");
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case AllocType::kMemory:
|
||||||
|
out->AppendFormat("export = %d", count);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
out->Append('\n');
|
||||||
|
}
|
||||||
|
|
||||||
|
void ParsedVertexFetchInstruction::Disassemble(StringBuffer* out) const {
|
||||||
|
static const struct {
|
||||||
|
const char* name;
|
||||||
|
} kVertexFetchDataFormats[0xff] = {
|
||||||
|
#define TYPE(id) {#id}
|
||||||
|
{0},
|
||||||
|
{0},
|
||||||
|
{0},
|
||||||
|
{0},
|
||||||
|
{0},
|
||||||
|
{0},
|
||||||
|
TYPE(FMT_8_8_8_8), // 6
|
||||||
|
TYPE(FMT_2_10_10_10), // 7
|
||||||
|
{0},
|
||||||
|
{0},
|
||||||
|
{0},
|
||||||
|
{0},
|
||||||
|
{0},
|
||||||
|
{0},
|
||||||
|
{0},
|
||||||
|
{0},
|
||||||
|
TYPE(FMT_10_11_11), // 16
|
||||||
|
TYPE(FMT_11_11_10), // 17
|
||||||
|
{0},
|
||||||
|
{0},
|
||||||
|
{0},
|
||||||
|
{0},
|
||||||
|
{0},
|
||||||
|
{0},
|
||||||
|
{0},
|
||||||
|
TYPE(FMT_16_16), // 25
|
||||||
|
TYPE(FMT_16_16_16_16), // 26
|
||||||
|
{0},
|
||||||
|
{0},
|
||||||
|
{0},
|
||||||
|
{0},
|
||||||
|
TYPE(FMT_16_16_FLOAT), // 31
|
||||||
|
TYPE(FMT_16_16_16_16_FLOAT), // 32
|
||||||
|
TYPE(FMT_32), // 33
|
||||||
|
TYPE(FMT_32_32), // 34
|
||||||
|
TYPE(FMT_32_32_32_32), // 35
|
||||||
|
TYPE(FMT_32_FLOAT), // 36
|
||||||
|
TYPE(FMT_32_32_FLOAT), // 37
|
||||||
|
TYPE(FMT_32_32_32_32_FLOAT), // 38
|
||||||
|
{0},
|
||||||
|
{0},
|
||||||
|
{0},
|
||||||
|
{0},
|
||||||
|
{0},
|
||||||
|
{0},
|
||||||
|
{0},
|
||||||
|
{0},
|
||||||
|
{0},
|
||||||
|
{0},
|
||||||
|
{0},
|
||||||
|
{0},
|
||||||
|
{0},
|
||||||
|
{0},
|
||||||
|
{0},
|
||||||
|
{0},
|
||||||
|
{0},
|
||||||
|
{0},
|
||||||
|
TYPE(FMT_32_32_32_FLOAT), // 57
|
||||||
|
#undef TYPE
|
||||||
|
};
|
||||||
|
out->Append(" ");
|
||||||
|
if (is_predicated) {
|
||||||
|
out->Append(predicate_condition ? " (p0) " : "(!p0) ");
|
||||||
|
} else {
|
||||||
|
out->Append(" ");
|
||||||
|
}
|
||||||
|
out->AppendFormat(opcode_name);
|
||||||
|
out->Append(' ');
|
||||||
|
DisassembleResultOperand(result, out);
|
||||||
|
if (!is_mini_fetch) {
|
||||||
|
out->Append(", ");
|
||||||
|
DisassembleSourceOperand(operands[0], out);
|
||||||
|
out->Append(", ");
|
||||||
|
out->AppendFormat("vf%d", 95 - operands[1].storage_index);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (attributes.is_index_rounded) {
|
||||||
|
out->Append(", RoundIndex=true");
|
||||||
|
}
|
||||||
|
if (attributes.exp_adjust) {
|
||||||
|
out->AppendFormat(", ExpAdjust=%d", attributes.exp_adjust);
|
||||||
|
}
|
||||||
|
if (attributes.offset) {
|
||||||
|
out->AppendFormat(", Offset=%d", attributes.offset);
|
||||||
|
}
|
||||||
|
if (attributes.data_format != VertexFormat::kUndefined) {
|
||||||
|
out->AppendFormat(
|
||||||
|
", DataFormat=%s",
|
||||||
|
kVertexFetchDataFormats[static_cast<int>(attributes.data_format)]);
|
||||||
|
}
|
||||||
|
if (attributes.stride) {
|
||||||
|
out->AppendFormat(", Stride=%d", attributes.stride);
|
||||||
|
}
|
||||||
|
if (attributes.is_signed) {
|
||||||
|
out->Append(", Signed=true");
|
||||||
|
}
|
||||||
|
if (attributes.is_integer) {
|
||||||
|
out->Append(", NumFormat=integer");
|
||||||
|
}
|
||||||
|
if (attributes.prefetch_count) {
|
||||||
|
out->AppendFormat(", PrefetchCount=%u", attributes.prefetch_count + 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
out->Append('\n');
|
||||||
|
}
|
||||||
|
|
||||||
|
void ParsedTextureFetchInstruction::Disassemble(StringBuffer* out) const {
|
||||||
|
static const char* kTextureFilterNames[] = {
|
||||||
|
"point", "linear", "BASEMAP", "keep",
|
||||||
|
};
|
||||||
|
static const char* kAnisoFilterNames[] = {
|
||||||
|
"disabled", "max1to1", "max2to1", "max4to1",
|
||||||
|
"max8to1", "max16to1", "keep",
|
||||||
|
};
|
||||||
|
|
||||||
|
out->Append(" ");
|
||||||
|
if (is_predicated) {
|
||||||
|
out->Append(predicate_condition ? " (p0) " : "(!p0) ");
|
||||||
|
} else {
|
||||||
|
out->Append(" ");
|
||||||
|
}
|
||||||
|
out->Append(opcode_name);
|
||||||
|
out->Append(' ');
|
||||||
|
bool needs_comma = false;
|
||||||
|
if (has_result()) {
|
||||||
|
DisassembleResultOperand(result, out);
|
||||||
|
needs_comma = true;
|
||||||
|
}
|
||||||
|
if (needs_comma) {
|
||||||
|
out->Append(", ");
|
||||||
|
}
|
||||||
|
DisassembleSourceOperand(operands[0], out);
|
||||||
|
if (operand_count > 1) {
|
||||||
|
if (needs_comma) {
|
||||||
|
out->Append(", ");
|
||||||
|
}
|
||||||
|
out->AppendFormat("tf%u", operands[1].storage_index);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!attributes.fetch_valid_only) {
|
||||||
|
out->Append(", FetchValidOnly=false");
|
||||||
|
}
|
||||||
|
if (attributes.unnormalized_coordinates) {
|
||||||
|
out->Append(", UnnormalizedTextureCoords=true");
|
||||||
|
}
|
||||||
|
if (attributes.mag_filter != TextureFilter::kUseFetchConst) {
|
||||||
|
out->AppendFormat(
|
||||||
|
", MagFilter=%s",
|
||||||
|
kTextureFilterNames[static_cast<int>(attributes.mag_filter)]);
|
||||||
|
}
|
||||||
|
if (attributes.min_filter != TextureFilter::kUseFetchConst) {
|
||||||
|
out->AppendFormat(
|
||||||
|
", MinFilter=%s",
|
||||||
|
kTextureFilterNames[static_cast<int>(attributes.min_filter)]);
|
||||||
|
}
|
||||||
|
if (attributes.mip_filter != TextureFilter::kUseFetchConst) {
|
||||||
|
out->AppendFormat(
|
||||||
|
", MipFilter=%s",
|
||||||
|
kTextureFilterNames[static_cast<int>(attributes.mip_filter)]);
|
||||||
|
}
|
||||||
|
if (attributes.aniso_filter != AnisoFilter::kUseFetchConst) {
|
||||||
|
out->AppendFormat(
|
||||||
|
", AnisoFilter=%s",
|
||||||
|
kAnisoFilterNames[static_cast<int>(attributes.aniso_filter)]);
|
||||||
|
}
|
||||||
|
if (!attributes.use_computed_lod) {
|
||||||
|
out->Append(", UseComputedLOD=false");
|
||||||
|
}
|
||||||
|
if (attributes.use_register_lod) {
|
||||||
|
out->Append(", UseRegisterLOD=true");
|
||||||
|
}
|
||||||
|
if (attributes.use_register_gradients) {
|
||||||
|
out->Append(", UseRegisterGradients=true");
|
||||||
|
}
|
||||||
|
int component_count = GetTextureDimensionComponentCount(dimension);
|
||||||
|
if (attributes.offset_x != 0.0f) {
|
||||||
|
out->AppendFormat(", OffsetX=%g", attributes.offset_x);
|
||||||
|
}
|
||||||
|
if (component_count > 1 && attributes.offset_y != 0.0f) {
|
||||||
|
out->AppendFormat(", OffsetY=%g", attributes.offset_y);
|
||||||
|
}
|
||||||
|
if (component_count > 2 && attributes.offset_z != 0.0f) {
|
||||||
|
out->AppendFormat(", OffsetZ=%g", attributes.offset_z);
|
||||||
|
}
|
||||||
|
|
||||||
|
out->Append('\n');
|
||||||
|
}
|
||||||
|
|
||||||
|
void ParsedAluInstruction::Disassemble(StringBuffer* out) const {
|
||||||
|
if (is_nop()) {
|
||||||
|
out->Append(" nop\n");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (is_scalar_type() && is_paired) {
|
||||||
|
out->Append(" + ");
|
||||||
|
} else {
|
||||||
|
out->Append(" ");
|
||||||
|
}
|
||||||
|
if (is_predicated) {
|
||||||
|
out->Append(predicate_condition ? " (p0) " : "(!p0) ");
|
||||||
|
} else {
|
||||||
|
out->Append(" ");
|
||||||
|
}
|
||||||
|
out->Append(opcode_name);
|
||||||
|
if (result.is_clamped) {
|
||||||
|
out->Append("_sat");
|
||||||
|
}
|
||||||
|
out->Append(' ');
|
||||||
|
|
||||||
|
DisassembleResultOperand(result, out);
|
||||||
|
|
||||||
|
for (int i = 0; i < operand_count; ++i) {
|
||||||
|
out->Append(", ");
|
||||||
|
DisassembleSourceOperand(operands[i], out);
|
||||||
|
}
|
||||||
|
out->Append('\n');
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace gpu
|
||||||
|
} // namespace xe
|
|
@ -0,0 +1,24 @@
|
||||||
|
/**
|
||||||
|
******************************************************************************
|
||||||
|
* 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. *
|
||||||
|
******************************************************************************
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "xenia/gpu/spirv_shader_translator.h"
|
||||||
|
|
||||||
|
namespace xe {
|
||||||
|
namespace gpu {
|
||||||
|
|
||||||
|
SpirvShaderTranslator::SpirvShaderTranslator() = default;
|
||||||
|
|
||||||
|
SpirvShaderTranslator::~SpirvShaderTranslator() = default;
|
||||||
|
|
||||||
|
std::vector<uint8_t> SpirvShaderTranslator::CompleteTranslation() {
|
||||||
|
return std::vector<uint8_t>();
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace gpu
|
||||||
|
} // namespace xe
|
|
@ -0,0 +1,34 @@
|
||||||
|
/**
|
||||||
|
******************************************************************************
|
||||||
|
* 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. *
|
||||||
|
******************************************************************************
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef XENIA_GPU_SPIRV_SHADER_TRANSLATOR_H_
|
||||||
|
#define XENIA_GPU_SPIRV_SHADER_TRANSLATOR_H_
|
||||||
|
|
||||||
|
#include <memory>
|
||||||
|
#include <string>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
#include "xenia/gpu/shader_translator.h"
|
||||||
|
|
||||||
|
namespace xe {
|
||||||
|
namespace gpu {
|
||||||
|
|
||||||
|
class SpirvShaderTranslator : public ShaderTranslator {
|
||||||
|
public:
|
||||||
|
SpirvShaderTranslator();
|
||||||
|
~SpirvShaderTranslator() override;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
std::vector<uint8_t> CompleteTranslation() override;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace gpu
|
||||||
|
} // namespace xe
|
||||||
|
|
||||||
|
#endif // XENIA_GPU_SPIRV_SHADER_TRANSLATOR_H_
|
|
@ -102,6 +102,22 @@ constexpr bool IsControlFlowOpcodeExec(ControlFlowOpcode opcode) {
|
||||||
opcode == ControlFlowOpcode::kCondExecPredCleanEnd;
|
opcode == ControlFlowOpcode::kCondExecPredCleanEnd;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Returns true if the given control flow opcode terminates the shader after
|
||||||
|
// executing.
|
||||||
|
constexpr bool DoesControlFlowOpcodeEndShader(ControlFlowOpcode opcode) {
|
||||||
|
return opcode == ControlFlowOpcode::kExecEnd ||
|
||||||
|
opcode == ControlFlowOpcode::kCondExecEnd ||
|
||||||
|
opcode == ControlFlowOpcode::kCondExecPredEnd ||
|
||||||
|
opcode == ControlFlowOpcode::kCondExecPredCleanEnd;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Returns true if the given control flow opcode resets the predicate prior to
|
||||||
|
// execution.
|
||||||
|
constexpr bool DoesControlFlowOpcodeCleanPredicate(ControlFlowOpcode opcode) {
|
||||||
|
return opcode == ControlFlowOpcode::kCondExecPredClean ||
|
||||||
|
opcode == ControlFlowOpcode::kCondExecPredCleanEnd;
|
||||||
|
}
|
||||||
|
|
||||||
// Determines whether addressing is based on a0 or aL.
|
// Determines whether addressing is based on a0 or aL.
|
||||||
enum class AddressingMode : uint32_t {
|
enum class AddressingMode : uint32_t {
|
||||||
// Indexing into register sets is done based on aL.
|
// Indexing into register sets is done based on aL.
|
||||||
|
@ -117,7 +133,7 @@ enum class AddressingMode : uint32_t {
|
||||||
// shader.
|
// shader.
|
||||||
enum class AllocType : uint32_t {
|
enum class AllocType : uint32_t {
|
||||||
// ?
|
// ?
|
||||||
kNoAlloc = 0,
|
kNone = 0,
|
||||||
// Vertex shader exports a position.
|
// Vertex shader exports a position.
|
||||||
kVsPosition = 1,
|
kVsPosition = 1,
|
||||||
// Vertex shader exports interpolators.
|
// Vertex shader exports interpolators.
|
||||||
|
@ -791,9 +807,7 @@ struct AluInstruction {
|
||||||
bool is_const_1_addressed() const { return data_.const_1_rel_abs == 1; }
|
bool is_const_1_addressed() const { return data_.const_1_rel_abs == 1; }
|
||||||
bool is_address_relative() const { return data_.address_absolute == 1; }
|
bool is_address_relative() const { return data_.address_absolute == 1; }
|
||||||
|
|
||||||
bool has_vector_op() const {
|
bool has_vector_op() const { return vector_write_mask() || is_export(); }
|
||||||
return vector_write_mask() || (is_export() && export_write_mask());
|
|
||||||
}
|
|
||||||
AluVectorOpcode vector_opcode() const {
|
AluVectorOpcode vector_opcode() const {
|
||||||
return static_cast<AluVectorOpcode>(data_.vector_opc);
|
return static_cast<AluVectorOpcode>(data_.vector_opc);
|
||||||
}
|
}
|
||||||
|
@ -804,7 +818,7 @@ struct AluInstruction {
|
||||||
|
|
||||||
bool has_scalar_op() const {
|
bool has_scalar_op() const {
|
||||||
return scalar_opcode() != AluScalarOpcode::kRETAIN_PREV ||
|
return scalar_opcode() != AluScalarOpcode::kRETAIN_PREV ||
|
||||||
scalar_write_mask() != 0;
|
(!is_export() && scalar_write_mask() != 0);
|
||||||
}
|
}
|
||||||
AluScalarOpcode scalar_opcode() const {
|
AluScalarOpcode scalar_opcode() const {
|
||||||
return static_cast<AluScalarOpcode>(data_.scalar_opc);
|
return static_cast<AluScalarOpcode>(data_.scalar_opc);
|
||||||
|
|
|
@ -93,6 +93,21 @@ enum class TextureDimension : uint32_t {
|
||||||
kCube = 3,
|
kCube = 3,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
inline int GetTextureDimensionComponentCount(TextureDimension dimension) {
|
||||||
|
switch (dimension) {
|
||||||
|
case TextureDimension::k1D:
|
||||||
|
return 1;
|
||||||
|
case TextureDimension::k2D:
|
||||||
|
return 2;
|
||||||
|
case TextureDimension::k3D:
|
||||||
|
case TextureDimension::kCube:
|
||||||
|
return 3;
|
||||||
|
default:
|
||||||
|
assert_unhandled_case(dimension);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
enum class SampleLocation : uint32_t {
|
enum class SampleLocation : uint32_t {
|
||||||
kCentroid = 0,
|
kCentroid = 0,
|
||||||
kCenter = 1,
|
kCenter = 1,
|
||||||
|
|
|
@ -1 +1 @@
|
||||||
Subproject commit 1ee9d71d2e3843c602ea4e547c13a1744df5fc76
|
Subproject commit e8de9fe81c334c2e166d0beb5ba3df2d7ff32715
|
|
@ -25,14 +25,14 @@
|
||||||
private void InitializeComponent() {
|
private void InitializeComponent() {
|
||||||
this.wordsTextBox = new System.Windows.Forms.TextBox();
|
this.wordsTextBox = new System.Windows.Forms.TextBox();
|
||||||
this.tableLayoutPanel1 = new System.Windows.Forms.TableLayoutPanel();
|
this.tableLayoutPanel1 = new System.Windows.Forms.TableLayoutPanel();
|
||||||
|
this.compilerTranslatedTextBox = new System.Windows.Forms.TextBox();
|
||||||
this.sourceCodeTextBox = new System.Windows.Forms.TextBox();
|
this.sourceCodeTextBox = new System.Windows.Forms.TextBox();
|
||||||
this.outputTextBox = new System.Windows.Forms.TextBox();
|
this.outputTextBox = new System.Windows.Forms.TextBox();
|
||||||
this.compilerUcodeTextBox = new System.Windows.Forms.TextBox();
|
this.compilerUcodeTextBox = new System.Windows.Forms.TextBox();
|
||||||
this.label1 = new System.Windows.Forms.Label();
|
this.label1 = new System.Windows.Forms.Label();
|
||||||
this.label2 = new System.Windows.Forms.Label();
|
this.label2 = new System.Windows.Forms.Label();
|
||||||
this.label3 = new System.Windows.Forms.Label();
|
this.label3 = new System.Windows.Forms.Label();
|
||||||
this.label4 = new System.Windows.Forms.Label();
|
this.translationComboBox = new System.Windows.Forms.ComboBox();
|
||||||
this.compilerTranslatedTextBox = new System.Windows.Forms.TextBox();
|
|
||||||
this.tableLayoutPanel1.SuspendLayout();
|
this.tableLayoutPanel1.SuspendLayout();
|
||||||
this.SuspendLayout();
|
this.SuspendLayout();
|
||||||
//
|
//
|
||||||
|
@ -65,7 +65,7 @@
|
||||||
this.tableLayoutPanel1.Controls.Add(this.label1, 0, 0);
|
this.tableLayoutPanel1.Controls.Add(this.label1, 0, 0);
|
||||||
this.tableLayoutPanel1.Controls.Add(this.label2, 1, 0);
|
this.tableLayoutPanel1.Controls.Add(this.label2, 1, 0);
|
||||||
this.tableLayoutPanel1.Controls.Add(this.label3, 2, 0);
|
this.tableLayoutPanel1.Controls.Add(this.label3, 2, 0);
|
||||||
this.tableLayoutPanel1.Controls.Add(this.label4, 3, 0);
|
this.tableLayoutPanel1.Controls.Add(this.translationComboBox, 3, 0);
|
||||||
this.tableLayoutPanel1.Location = new System.Drawing.Point(12, 12);
|
this.tableLayoutPanel1.Location = new System.Drawing.Point(12, 12);
|
||||||
this.tableLayoutPanel1.Name = "tableLayoutPanel1";
|
this.tableLayoutPanel1.Name = "tableLayoutPanel1";
|
||||||
this.tableLayoutPanel1.RowCount = 2;
|
this.tableLayoutPanel1.RowCount = 2;
|
||||||
|
@ -74,6 +74,20 @@
|
||||||
this.tableLayoutPanel1.Size = new System.Drawing.Size(1631, 639);
|
this.tableLayoutPanel1.Size = new System.Drawing.Size(1631, 639);
|
||||||
this.tableLayoutPanel1.TabIndex = 5;
|
this.tableLayoutPanel1.TabIndex = 5;
|
||||||
//
|
//
|
||||||
|
// compilerTranslatedTextBox
|
||||||
|
//
|
||||||
|
this.compilerTranslatedTextBox.AcceptsReturn = true;
|
||||||
|
this.compilerTranslatedTextBox.AcceptsTab = true;
|
||||||
|
this.compilerTranslatedTextBox.Dock = System.Windows.Forms.DockStyle.Fill;
|
||||||
|
this.compilerTranslatedTextBox.Font = new System.Drawing.Font("Consolas", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
|
||||||
|
this.compilerTranslatedTextBox.Location = new System.Drawing.Point(1224, 23);
|
||||||
|
this.compilerTranslatedTextBox.Multiline = true;
|
||||||
|
this.compilerTranslatedTextBox.Name = "compilerTranslatedTextBox";
|
||||||
|
this.compilerTranslatedTextBox.ReadOnly = true;
|
||||||
|
this.compilerTranslatedTextBox.ScrollBars = System.Windows.Forms.ScrollBars.Vertical;
|
||||||
|
this.compilerTranslatedTextBox.Size = new System.Drawing.Size(404, 613);
|
||||||
|
this.compilerTranslatedTextBox.TabIndex = 11;
|
||||||
|
//
|
||||||
// sourceCodeTextBox
|
// sourceCodeTextBox
|
||||||
//
|
//
|
||||||
this.sourceCodeTextBox.AcceptsReturn = true;
|
this.sourceCodeTextBox.AcceptsReturn = true;
|
||||||
|
@ -142,28 +156,19 @@
|
||||||
this.label3.TabIndex = 9;
|
this.label3.TabIndex = 9;
|
||||||
this.label3.Text = "xenia-gpu-shader-compiler Disassembly";
|
this.label3.Text = "xenia-gpu-shader-compiler Disassembly";
|
||||||
//
|
//
|
||||||
// label4
|
// translationComboBox
|
||||||
//
|
//
|
||||||
this.label4.AutoSize = true;
|
this.translationComboBox.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left)
|
||||||
this.label4.Location = new System.Drawing.Point(1224, 0);
|
| System.Windows.Forms.AnchorStyles.Right)));
|
||||||
this.label4.Name = "label4";
|
this.translationComboBox.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList;
|
||||||
this.label4.Size = new System.Drawing.Size(183, 13);
|
this.translationComboBox.FormattingEnabled = true;
|
||||||
this.label4.TabIndex = 10;
|
this.translationComboBox.Items.AddRange(new object[] {
|
||||||
this.label4.Text = "xenia-gpu-shader-compiler Translated";
|
"SPIRV"});
|
||||||
//
|
this.translationComboBox.Location = new System.Drawing.Point(1224, 0);
|
||||||
// compilerTranslatedTextBox
|
this.translationComboBox.Margin = new System.Windows.Forms.Padding(3, 0, 3, 0);
|
||||||
//
|
this.translationComboBox.Name = "translationComboBox";
|
||||||
this.compilerTranslatedTextBox.AcceptsReturn = true;
|
this.translationComboBox.Size = new System.Drawing.Size(404, 21);
|
||||||
this.compilerTranslatedTextBox.AcceptsTab = true;
|
this.translationComboBox.TabIndex = 12;
|
||||||
this.compilerTranslatedTextBox.Dock = System.Windows.Forms.DockStyle.Fill;
|
|
||||||
this.compilerTranslatedTextBox.Font = new System.Drawing.Font("Consolas", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
|
|
||||||
this.compilerTranslatedTextBox.Location = new System.Drawing.Point(1224, 23);
|
|
||||||
this.compilerTranslatedTextBox.Multiline = true;
|
|
||||||
this.compilerTranslatedTextBox.Name = "compilerTranslatedTextBox";
|
|
||||||
this.compilerTranslatedTextBox.ReadOnly = true;
|
|
||||||
this.compilerTranslatedTextBox.ScrollBars = System.Windows.Forms.ScrollBars.Vertical;
|
|
||||||
this.compilerTranslatedTextBox.Size = new System.Drawing.Size(404, 613);
|
|
||||||
this.compilerTranslatedTextBox.TabIndex = 11;
|
|
||||||
//
|
//
|
||||||
// Editor
|
// Editor
|
||||||
//
|
//
|
||||||
|
@ -191,7 +196,7 @@
|
||||||
private System.Windows.Forms.Label label2;
|
private System.Windows.Forms.Label label2;
|
||||||
private System.Windows.Forms.Label label3;
|
private System.Windows.Forms.Label label3;
|
||||||
private System.Windows.Forms.TextBox compilerTranslatedTextBox;
|
private System.Windows.Forms.TextBox compilerTranslatedTextBox;
|
||||||
private System.Windows.Forms.Label label4;
|
private System.Windows.Forms.ComboBox translationComboBox;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -13,6 +13,7 @@ namespace shader_playground {
|
||||||
string compilerPath_ = @"..\..\..\..\..\build\bin\Windows\Debug\xenia-gpu-shader-compiler.exe";
|
string compilerPath_ = @"..\..\..\..\..\build\bin\Windows\Debug\xenia-gpu-shader-compiler.exe";
|
||||||
|
|
||||||
FileSystemWatcher compilerWatcher_;
|
FileSystemWatcher compilerWatcher_;
|
||||||
|
bool pendingTimer_ = false;
|
||||||
|
|
||||||
public Editor() {
|
public Editor() {
|
||||||
InitializeComponent();
|
InitializeComponent();
|
||||||
|
@ -23,7 +24,20 @@ namespace shader_playground {
|
||||||
compilerWatcher_.NotifyFilter = NotifyFilters.LastWrite | NotifyFilters.Size;
|
compilerWatcher_.NotifyFilter = NotifyFilters.LastWrite | NotifyFilters.Size;
|
||||||
compilerWatcher_.Changed += (object sender, FileSystemEventArgs e) => {
|
compilerWatcher_.Changed += (object sender, FileSystemEventArgs e) => {
|
||||||
if (e.Name == Path.GetFileName(compilerPath_)) {
|
if (e.Name == Path.GetFileName(compilerPath_)) {
|
||||||
Invoke((MethodInvoker)delegate { Process(sourceCodeTextBox.Text); });
|
Invoke((MethodInvoker)delegate {
|
||||||
|
if (pendingTimer_) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
pendingTimer_ = true;
|
||||||
|
var timer = new Timer();
|
||||||
|
timer.Interval = 1000;
|
||||||
|
timer.Tick += (object sender1, EventArgs e1) => {
|
||||||
|
pendingTimer_ = false;
|
||||||
|
timer.Dispose();
|
||||||
|
Process(sourceCodeTextBox.Text);
|
||||||
|
};
|
||||||
|
timer.Start();
|
||||||
|
});
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
compilerWatcher_.EnableRaisingEvents = true;
|
compilerWatcher_.EnableRaisingEvents = true;
|
||||||
|
@ -33,13 +47,18 @@ namespace shader_playground {
|
||||||
wordsTextBox.Copy();
|
wordsTextBox.Copy();
|
||||||
};
|
};
|
||||||
|
|
||||||
this.sourceCodeTextBox.Click += (object sender, EventArgs e) => {
|
sourceCodeTextBox.Click += (object sender, EventArgs e) => {
|
||||||
Process(sourceCodeTextBox.Text);
|
Process(sourceCodeTextBox.Text);
|
||||||
};
|
};
|
||||||
sourceCodeTextBox.TextChanged += (object sender, EventArgs e) => {
|
sourceCodeTextBox.TextChanged += (object sender, EventArgs e) => {
|
||||||
Process(sourceCodeTextBox.Text);
|
Process(sourceCodeTextBox.Text);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
translationComboBox.SelectedIndex = 0;
|
||||||
|
translationComboBox.SelectedIndexChanged += (object sender, EventArgs e) => {
|
||||||
|
Process(sourceCodeTextBox.Text);
|
||||||
|
};
|
||||||
|
|
||||||
sourceCodeTextBox.Text = string.Join(
|
sourceCodeTextBox.Text = string.Join(
|
||||||
"\r\n", new string[] {
|
"\r\n", new string[] {
|
||||||
"xps_3_0",
|
"xps_3_0",
|
||||||
|
@ -59,6 +78,9 @@ namespace shader_playground {
|
||||||
"(!p0) setGradientV r1.zyx",
|
"(!p0) setGradientV r1.zyx",
|
||||||
" getGradients r5, r1.xy, tf3",
|
" getGradients r5, r1.xy, tf3",
|
||||||
" mad oC0, r0, r1.yyyy, c0",
|
" mad oC0, r0, r1.yyyy, c0",
|
||||||
|
" mad oC0._, r0, r1.yyyy, c0",
|
||||||
|
" mad oC0.x1_, r0, r1.yyyy, c0",
|
||||||
|
" mad oC0.x10w, r0, r1.yyyy, c0",
|
||||||
" mul r4.xyz, r1.xyzz, c5.xyzz",
|
" mul r4.xyz, r1.xyzz, c5.xyzz",
|
||||||
" mul r4.xyz, r1.xyzz, c[0 + aL].xyzz",
|
" mul r4.xyz, r1.xyzz, c[0 + aL].xyzz",
|
||||||
" mul r4.xyz, r1.xyzz, c[6 + aL].xyzz",
|
" mul r4.xyz, r1.xyzz, c[6 + aL].xyzz",
|
||||||
|
@ -67,6 +89,8 @@ namespace shader_playground {
|
||||||
" + adds r5.w, r0.xz",
|
" + adds r5.w, r0.xz",
|
||||||
" cos r6.w, r0.x",
|
" cos r6.w, r0.x",
|
||||||
" adds r5.w, r0.zx",
|
" adds r5.w, r0.zx",
|
||||||
|
" mul r4.xyz, r[aL+1].xyzz, c[8 + a0].xyzz",
|
||||||
|
" adds r5.w, r[aL+0].zx",
|
||||||
" jmp l5",
|
" jmp l5",
|
||||||
"ccall b1, l5",
|
"ccall b1, l5",
|
||||||
"nop",
|
"nop",
|
||||||
|
@ -151,15 +175,15 @@ namespace shader_playground {
|
||||||
void TryCompiler(string shaderType, uint[] ucodeWords) {
|
void TryCompiler(string shaderType, uint[] ucodeWords) {
|
||||||
string ucodePath = Path.Combine(Path.GetTempPath(), "shader_playground_ucode.bin." + shaderType);
|
string ucodePath = Path.Combine(Path.GetTempPath(), "shader_playground_ucode.bin." + shaderType);
|
||||||
string ucodeDisasmPath = Path.Combine(Path.GetTempPath(), "shader_playground_disasm.ucode.txt");
|
string ucodeDisasmPath = Path.Combine(Path.GetTempPath(), "shader_playground_disasm.ucode.txt");
|
||||||
string spirvDisasmPath = Path.Combine(Path.GetTempPath(), "shader_playground_disasm.spirv.txt");
|
string translatedDisasmPath = Path.Combine(Path.GetTempPath(), "shader_playground_disasm.translated.txt");
|
||||||
if (File.Exists(ucodePath)) {
|
if (File.Exists(ucodePath)) {
|
||||||
File.Delete(ucodePath);
|
File.Delete(ucodePath);
|
||||||
}
|
}
|
||||||
if (File.Exists(ucodeDisasmPath)) {
|
if (File.Exists(ucodeDisasmPath)) {
|
||||||
File.Delete(ucodeDisasmPath);
|
File.Delete(ucodeDisasmPath);
|
||||||
}
|
}
|
||||||
if (File.Exists(spirvDisasmPath)) {
|
if (File.Exists(translatedDisasmPath)) {
|
||||||
File.Delete(spirvDisasmPath);
|
File.Delete(translatedDisasmPath);
|
||||||
}
|
}
|
||||||
|
|
||||||
byte[] ucodeBytes = new byte[ucodeWords.Length * 4];
|
byte[] ucodeBytes = new byte[ucodeWords.Length * 4];
|
||||||
|
@ -190,12 +214,20 @@ namespace shader_playground {
|
||||||
compilerUcodeTextBox.Text = "COMPILER FAILURE";
|
compilerUcodeTextBox.Text = "COMPILER FAILURE";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
string outputType;
|
||||||
|
switch (translationComboBox.SelectedIndex) {
|
||||||
|
default:
|
||||||
|
case 0:
|
||||||
|
outputType = "spirvtext";
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
startInfo = new ProcessStartInfo(compilerPath_);
|
startInfo = new ProcessStartInfo(compilerPath_);
|
||||||
startInfo.Arguments = string.Join(" ", new string[]{
|
startInfo.Arguments = string.Join(" ", new string[]{
|
||||||
"--shader_input=" + ucodePath,
|
"--shader_input=" + ucodePath,
|
||||||
"--shader_input_type=" + shaderType,
|
"--shader_input_type=" + shaderType,
|
||||||
"--shader_output=" + spirvDisasmPath,
|
"--shader_output=" + translatedDisasmPath,
|
||||||
"--shader_output_type=spirvtext",
|
"--shader_output_type=" + outputType,
|
||||||
});
|
});
|
||||||
startInfo.WindowStyle = ProcessWindowStyle.Hidden;
|
startInfo.WindowStyle = ProcessWindowStyle.Hidden;
|
||||||
startInfo.CreateNoWindow = true;
|
startInfo.CreateNoWindow = true;
|
||||||
|
@ -203,7 +235,7 @@ namespace shader_playground {
|
||||||
using (var process = System.Diagnostics.Process.Start(startInfo)) {
|
using (var process = System.Diagnostics.Process.Start(startInfo)) {
|
||||||
process.WaitForExit();
|
process.WaitForExit();
|
||||||
}
|
}
|
||||||
string disasmText = File.ReadAllText(spirvDisasmPath);
|
string disasmText = File.ReadAllText(translatedDisasmPath);
|
||||||
compilerTranslatedTextBox.Text = disasmText;
|
compilerTranslatedTextBox.Text = disasmText;
|
||||||
} catch {
|
} catch {
|
||||||
compilerTranslatedTextBox.Text = "COMPILER FAILURE";
|
compilerTranslatedTextBox.Text = "COMPILER FAILURE";
|
||||||
|
|
Loading…
Reference in New Issue