Replacing old Shader with TranslatedShader.
This commit is contained in:
parent
083f936bbc
commit
c86e479214
|
@ -491,12 +491,21 @@ Shader* GL4CommandProcessor::LoadShader(ShaderType shader_type,
|
|||
|
||||
// Perform translation.
|
||||
// If this fails the shader will be marked as invalid and ignored later.
|
||||
shader_ptr->Prepare(&shader_translator_);
|
||||
if (shader_translator_.Translate(shader_ptr)) {
|
||||
shader_ptr->Prepare();
|
||||
|
||||
// Dump shader files if desired.
|
||||
if (!FLAGS_dump_shaders.empty()) {
|
||||
shader_ptr->Dump(FLAGS_dump_shaders, "gl4");
|
||||
}
|
||||
} else {
|
||||
XELOGE("Shader failed translation");
|
||||
}
|
||||
|
||||
XELOGGPU("Set %s shader at %0.8X (%db):\n%s",
|
||||
shader_type == ShaderType::kVertex ? "vertex" : "pixel",
|
||||
guest_address, dword_count * 4,
|
||||
shader_ptr->translated_shader()->ucode_disassembly().c_str());
|
||||
shader_ptr->ucode_disassembly().c_str());
|
||||
}
|
||||
return shader_ptr;
|
||||
}
|
||||
|
@ -779,7 +788,6 @@ GL4CommandProcessor::UpdateStatus GL4CommandProcessor::UpdateRenderTargets() {
|
|||
// Note that write mask may be more permissive than we want, so we mix that
|
||||
// with the actual targets the pixel shader writes to.
|
||||
GLenum draw_buffers[4] = {GL_NONE, GL_NONE, GL_NONE, GL_NONE};
|
||||
auto pixel_shader = active_pixel_shader_->translated_shader();
|
||||
GLuint color_targets[4] = {kAnyTarget, kAnyTarget, kAnyTarget, kAnyTarget};
|
||||
if (enable_mode == ModeControl::kColorDepth) {
|
||||
uint32_t color_info[4] = {
|
||||
|
@ -789,7 +797,7 @@ GL4CommandProcessor::UpdateStatus GL4CommandProcessor::UpdateRenderTargets() {
|
|||
// A2XX_RB_COLOR_MASK_WRITE_* == D3DRS_COLORWRITEENABLE
|
||||
for (int n = 0; n < xe::countof(color_info); n++) {
|
||||
uint32_t write_mask = (regs.rb_color_mask >> (n * 4)) & 0xF;
|
||||
if (!write_mask || !pixel_shader->writes_color_target(n)) {
|
||||
if (!write_mask || !active_pixel_shader_->writes_color_target(n)) {
|
||||
// Unused, so keep disabled and set to wildcard so we'll take any
|
||||
// framebuffer that has it.
|
||||
continue;
|
||||
|
@ -1362,9 +1370,7 @@ GL4CommandProcessor::UpdateStatus GL4CommandProcessor::PopulateVertexBuffers() {
|
|||
auto& regs = *register_file_;
|
||||
assert_not_null(active_vertex_shader_);
|
||||
|
||||
const auto& vertex_bindings =
|
||||
active_vertex_shader_->translated_shader()->vertex_bindings();
|
||||
for (const auto& vertex_binding : vertex_bindings) {
|
||||
for (const auto& vertex_binding : active_vertex_shader_->vertex_bindings()) {
|
||||
int r = XE_GPU_REG_SHADER_CONSTANT_FETCH_00_0 +
|
||||
(vertex_binding.fetch_constant / 3) * 6;
|
||||
const auto group = reinterpret_cast<xe_gpu_fetch_group_t*>(®s.values[r]);
|
||||
|
@ -1434,9 +1440,7 @@ GL4CommandProcessor::UpdateStatus GL4CommandProcessor::PopulateSamplers() {
|
|||
bool has_setup_sampler[32] = {false};
|
||||
|
||||
// Vertex texture samplers.
|
||||
const auto& vertex_sampler_inputs =
|
||||
active_vertex_shader_->translated_shader()->texture_bindings();
|
||||
for (auto& texture_binding : vertex_sampler_inputs) {
|
||||
for (auto& texture_binding : active_vertex_shader_->texture_bindings()) {
|
||||
if (has_setup_sampler[texture_binding.fetch_constant]) {
|
||||
continue;
|
||||
}
|
||||
|
@ -1450,9 +1454,7 @@ GL4CommandProcessor::UpdateStatus GL4CommandProcessor::PopulateSamplers() {
|
|||
}
|
||||
|
||||
// Pixel shader texture sampler.
|
||||
const auto& pixel_sampler_inputs =
|
||||
active_pixel_shader_->translated_shader()->texture_bindings();
|
||||
for (auto& texture_binding : pixel_sampler_inputs) {
|
||||
for (auto& texture_binding : active_pixel_shader_->texture_bindings()) {
|
||||
if (has_setup_sampler[texture_binding.fetch_constant]) {
|
||||
continue;
|
||||
}
|
||||
|
@ -1469,7 +1471,7 @@ GL4CommandProcessor::UpdateStatus GL4CommandProcessor::PopulateSamplers() {
|
|||
}
|
||||
|
||||
GL4CommandProcessor::UpdateStatus GL4CommandProcessor::PopulateSampler(
|
||||
const TranslatedShader::TextureBinding& texture_binding) {
|
||||
const Shader::TextureBinding& texture_binding) {
|
||||
auto& regs = *register_file_;
|
||||
int r = XE_GPU_REG_SHADER_CONSTANT_FETCH_00_0 +
|
||||
texture_binding.fetch_constant * 6;
|
||||
|
|
|
@ -123,8 +123,7 @@ class GL4CommandProcessor : public CommandProcessor {
|
|||
UpdateStatus PopulateIndexBuffer(IndexBufferInfo* index_buffer_info);
|
||||
UpdateStatus PopulateVertexBuffers();
|
||||
UpdateStatus PopulateSamplers();
|
||||
UpdateStatus PopulateSampler(
|
||||
const TranslatedShader::TextureBinding& texture_binding);
|
||||
UpdateStatus PopulateSampler(const Shader::TextureBinding& texture_binding);
|
||||
bool IssueCopy() override;
|
||||
|
||||
CachedFramebuffer* GetFramebuffer(GLuint color_targets[4],
|
||||
|
|
|
@ -9,11 +9,8 @@
|
|||
|
||||
#include "xenia/gpu/gl4/gl4_shader.h"
|
||||
|
||||
#include "xenia/base/filesystem.h"
|
||||
#include "xenia/base/logging.h"
|
||||
#include "xenia/base/math.h"
|
||||
#include "xenia/gpu/gl4/gl4_gpu_flags.h"
|
||||
#include "xenia/gpu/gpu_flags.h"
|
||||
|
||||
namespace xe {
|
||||
namespace gpu {
|
||||
|
@ -30,8 +27,8 @@ GL4Shader::~GL4Shader() {
|
|||
glDeleteVertexArrays(1, &vao_);
|
||||
}
|
||||
|
||||
bool GL4Shader::Prepare(ShaderTranslator* shader_translator) {
|
||||
if (!Shader::Prepare(shader_translator)) {
|
||||
bool GL4Shader::Prepare() {
|
||||
if (!Shader::Prepare()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -51,7 +48,7 @@ bool GL4Shader::Prepare(ShaderTranslator* shader_translator) {
|
|||
bool GL4Shader::PrepareVertexArrayObject() {
|
||||
glCreateVertexArrays(1, &vao_);
|
||||
|
||||
for (const auto& vertex_binding : translated_shader_->vertex_bindings()) {
|
||||
for (const auto& vertex_binding : vertex_bindings()) {
|
||||
for (const auto& attrib : vertex_binding.attributes) {
|
||||
auto comp_count = GetVertexFormatComponentCount(
|
||||
attrib.fetch_instr.attributes.data_format);
|
||||
|
@ -126,43 +123,8 @@ bool GL4Shader::PrepareVertexArrayObject() {
|
|||
bool GL4Shader::CompileProgram() {
|
||||
assert_zero(program_);
|
||||
|
||||
auto source_str = translated_shader_->GetBinaryString();
|
||||
|
||||
// Save to disk, if we asked for it.
|
||||
auto base_path = FLAGS_dump_shaders.c_str();
|
||||
char file_name[kMaxPath];
|
||||
snprintf(file_name, xe::countof(file_name), "%s/gl4_gen_%.16llX.%s",
|
||||
base_path, data_hash_,
|
||||
shader_type_ == ShaderType::kVertex ? "vert" : "frag");
|
||||
if (!FLAGS_dump_shaders.empty()) {
|
||||
// Ensure shader dump path exists.
|
||||
auto dump_shaders_path = xe::to_wstring(FLAGS_dump_shaders);
|
||||
if (!dump_shaders_path.empty()) {
|
||||
dump_shaders_path = xe::to_absolute_path(dump_shaders_path);
|
||||
xe::filesystem::CreateFolder(dump_shaders_path);
|
||||
}
|
||||
|
||||
char bin_file_name[kMaxPath];
|
||||
snprintf(bin_file_name, xe::countof(file_name), "%s/gl4_gen_%.16llX.bin.%s",
|
||||
base_path, data_hash_,
|
||||
shader_type_ == ShaderType::kVertex ? "vert" : "frag");
|
||||
FILE* f = fopen(bin_file_name, "w");
|
||||
if (f) {
|
||||
fwrite(data_.data(), 4, data_.size(), f);
|
||||
fclose(f);
|
||||
}
|
||||
|
||||
// Note that we put the translated source first so we get good line numbers.
|
||||
f = fopen(file_name, "w");
|
||||
if (f) {
|
||||
fprintf(f, "%s", source_str.c_str());
|
||||
fprintf(f, "/*\n");
|
||||
fprintf(f, "%s", translated_shader_->ucode_disassembly().c_str());
|
||||
fprintf(f, " */\n");
|
||||
fclose(f);
|
||||
}
|
||||
}
|
||||
|
||||
// Give source to GL.
|
||||
auto source_str = GetTranslatedBinaryString();
|
||||
auto source_str_ptr = source_str.c_str();
|
||||
program_ = glCreateShaderProgramv(shader_type_ == ShaderType::kVertex
|
||||
? GL_VERTEX_SHADER
|
||||
|
@ -185,7 +147,7 @@ bool GL4Shader::CompileProgram() {
|
|||
glGetProgramInfoLog(program_, log_length, &log_length,
|
||||
const_cast<char*>(info_log.data()));
|
||||
XELOGE("Unable to link program: %s", info_log.c_str());
|
||||
error_log_ = std::move(info_log);
|
||||
host_error_log_ = std::move(info_log);
|
||||
assert_always("Unable to link generated shader");
|
||||
return false;
|
||||
}
|
||||
|
@ -194,21 +156,21 @@ bool GL4Shader::CompileProgram() {
|
|||
GLint binary_length = 0;
|
||||
glGetProgramiv(program_, GL_PROGRAM_BINARY_LENGTH, &binary_length);
|
||||
if (binary_length) {
|
||||
translated_binary_.resize(binary_length);
|
||||
host_binary_.resize(binary_length);
|
||||
GLenum binary_format;
|
||||
glGetProgramBinary(program_, binary_length, &binary_length, &binary_format,
|
||||
translated_binary_.data());
|
||||
host_binary_.data());
|
||||
|
||||
// If we are on nvidia, we can find the disassembly string.
|
||||
// I haven't been able to figure out from the format how to do this
|
||||
// without a search like this.
|
||||
const char* disasm_start = nullptr;
|
||||
size_t search_offset = 0;
|
||||
char* search_start = reinterpret_cast<char*>(translated_binary_.data());
|
||||
char* search_start = reinterpret_cast<char*>(host_binary_.data());
|
||||
while (true) {
|
||||
auto p = reinterpret_cast<char*>(
|
||||
memchr(translated_binary_.data() + search_offset, '!',
|
||||
translated_binary_.size() - search_offset));
|
||||
memchr(host_binary_.data() + search_offset, '!',
|
||||
host_binary_.size() - search_offset));
|
||||
if (!p) {
|
||||
break;
|
||||
}
|
||||
|
@ -224,19 +186,6 @@ bool GL4Shader::CompileProgram() {
|
|||
} else {
|
||||
host_disassembly_ = std::string("Shader disassembly not available.");
|
||||
}
|
||||
|
||||
// Append to shader dump.
|
||||
if (!FLAGS_dump_shaders.empty()) {
|
||||
if (disasm_start) {
|
||||
FILE* f = fopen(file_name, "a");
|
||||
fprintf(f, "\n\n/*\n");
|
||||
fprintf(f, "%s", disasm_start);
|
||||
fprintf(f, "\n*/\n");
|
||||
fclose(f);
|
||||
} else {
|
||||
XELOGW("Got program binary but unable to find disassembly");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
|
|
|
@ -13,7 +13,6 @@
|
|||
#include <string>
|
||||
|
||||
#include "xenia/gpu/shader.h"
|
||||
#include "xenia/gpu/shader_translator.h"
|
||||
#include "xenia/ui/gl/gl_context.h"
|
||||
|
||||
namespace xe {
|
||||
|
@ -29,7 +28,7 @@ class GL4Shader : public Shader {
|
|||
GLuint program() const { return program_; }
|
||||
GLuint vao() const { return vao_; }
|
||||
|
||||
bool Prepare(ShaderTranslator* shader_translator);
|
||||
bool Prepare() override;
|
||||
|
||||
protected:
|
||||
bool PrepareVertexArrayObject();
|
||||
|
|
|
@ -90,8 +90,7 @@ void GlslShaderTranslator::StartTranslation() {
|
|||
// Tons of boilerplate for shaders, here.
|
||||
// We have a large amount of shared state defining uniforms and some common
|
||||
// utility functions used in both vertex and pixel shaders.
|
||||
EmitSource(R"(
|
||||
#version 450
|
||||
EmitSource(R"(#version 450
|
||||
#extension all : warn
|
||||
#extension GL_ARB_bindless_texture : require
|
||||
#extension GL_ARB_explicit_uniform_location : require
|
||||
|
|
|
@ -10,7 +10,7 @@
|
|||
#ifndef XENIA_GPU_SAMPLER_INFO_H_
|
||||
#define XENIA_GPU_SAMPLER_INFO_H_
|
||||
|
||||
#include "xenia/gpu/shader_translator.h"
|
||||
#include "xenia/gpu/shader.h"
|
||||
#include "xenia/gpu/xenos.h"
|
||||
|
||||
namespace xe {
|
||||
|
|
|
@ -2,38 +2,83 @@
|
|||
******************************************************************************
|
||||
* Xenia : Xbox 360 Emulator Research Project *
|
||||
******************************************************************************
|
||||
* Copyright 2014 Ben Vanik. All rights reserved. *
|
||||
* Copyright 2015 Ben Vanik. All rights reserved. *
|
||||
* Released under the BSD license - see LICENSE in the root for more details. *
|
||||
******************************************************************************
|
||||
*/
|
||||
|
||||
#include "xenia/gpu/shader.h"
|
||||
|
||||
#include "xenia/base/logging.h"
|
||||
#include <cstring>
|
||||
|
||||
#include "xenia/base/filesystem.h"
|
||||
#include "xenia/base/math.h"
|
||||
#include "xenia/base/memory.h"
|
||||
#include "xenia/base/string.h"
|
||||
|
||||
namespace xe {
|
||||
namespace gpu {
|
||||
|
||||
Shader::Shader(ShaderType shader_type, uint64_t data_hash,
|
||||
const uint32_t* dword_ptr, uint32_t dword_count)
|
||||
: shader_type_(shader_type), data_hash_(data_hash) {
|
||||
data_.resize(dword_count);
|
||||
xe::copy_and_swap(data_.data(), dword_ptr, dword_count);
|
||||
Shader::Shader(ShaderType shader_type, uint64_t ucode_data_hash,
|
||||
const uint32_t* ucode_dwords, size_t ucode_dword_count)
|
||||
: shader_type_(shader_type), ucode_data_hash_(ucode_data_hash) {
|
||||
// We keep ucode data in host native format so it's easier to work with.
|
||||
ucode_data_.resize(ucode_dword_count);
|
||||
xe::copy_and_swap(ucode_data_.data(), ucode_dwords, ucode_dword_count);
|
||||
}
|
||||
|
||||
Shader::~Shader() = default;
|
||||
|
||||
bool Shader::Prepare(ShaderTranslator* shader_translator) {
|
||||
// Perform translation.
|
||||
translated_shader_ = shader_translator->Translate(shader_type_, data_hash_,
|
||||
data_.data(), data_.size());
|
||||
if (!translated_shader_) {
|
||||
XELOGE("Shader failed translation");
|
||||
return false;
|
||||
std::string Shader::GetTranslatedBinaryString() const {
|
||||
std::string result;
|
||||
result.resize(translated_binary_.size());
|
||||
std::memcpy(const_cast<char*>(result.data()), translated_binary_.data(),
|
||||
translated_binary_.size());
|
||||
return result;
|
||||
}
|
||||
|
||||
void Shader::Dump(const std::string& base_path, const char* path_prefix) {
|
||||
// Ensure target path exists.
|
||||
auto target_path = xe::to_wstring(base_path);
|
||||
if (!target_path.empty()) {
|
||||
target_path = xe::to_absolute_path(target_path);
|
||||
xe::filesystem::CreateFolder(target_path);
|
||||
}
|
||||
|
||||
return true;
|
||||
char txt_file_name[kMaxPath];
|
||||
std::snprintf(txt_file_name, xe::countof(txt_file_name),
|
||||
"%s/shader_%s_%.16llX.%s", base_path.c_str(), path_prefix,
|
||||
ucode_data_hash_,
|
||||
shader_type_ == ShaderType::kVertex ? "vert" : "frag");
|
||||
FILE* f = fopen(txt_file_name, "w");
|
||||
if (f) {
|
||||
fwrite(translated_binary_.data(), 1, translated_binary_.size(), f);
|
||||
fprintf(f, "\n\n");
|
||||
auto ucode_disasm_ptr = ucode_disassembly().c_str();
|
||||
while (*ucode_disasm_ptr) {
|
||||
auto line_end = std::strchr(ucode_disasm_ptr, '\n');
|
||||
fprintf(f, "// ");
|
||||
fwrite(ucode_disasm_ptr, 1, line_end - ucode_disasm_ptr + 1, f);
|
||||
ucode_disasm_ptr = line_end + 1;
|
||||
}
|
||||
fprintf(f, "\n\n");
|
||||
if (!host_disassembly_.empty()) {
|
||||
fprintf(f, "\n\n/*\n%s\n*/\n", host_disassembly_.c_str());
|
||||
}
|
||||
fclose(f);
|
||||
}
|
||||
|
||||
char bin_file_name[kMaxPath];
|
||||
std::snprintf(bin_file_name, xe::countof(bin_file_name),
|
||||
"%s/shader_%s_%.16llX.bin.%s", base_path.c_str(), path_prefix,
|
||||
ucode_data_hash_,
|
||||
shader_type_ == ShaderType::kVertex ? "vert" : "frag");
|
||||
f = fopen(bin_file_name, "w");
|
||||
if (f) {
|
||||
fwrite(ucode_data_.data(), 4, ucode_data_.size(), f);
|
||||
fclose(f);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace gpu
|
||||
} // namespace xe
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
******************************************************************************
|
||||
* Xenia : Xbox 360 Emulator Research Project *
|
||||
******************************************************************************
|
||||
* Copyright 2014 Ben Vanik. All rights reserved. *
|
||||
* Copyright 2015 Ben Vanik. All rights reserved. *
|
||||
* Released under the BSD license - see LICENSE in the root for more details. *
|
||||
******************************************************************************
|
||||
*/
|
||||
|
@ -13,42 +13,565 @@
|
|||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "xenia/gpu/shader_translator.h"
|
||||
#include "xenia/base/string_buffer.h"
|
||||
#include "xenia/gpu/ucode.h"
|
||||
#include "xenia/gpu/xenos.h"
|
||||
|
||||
namespace xe {
|
||||
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 GetCharForComponentIndex(int i) {
|
||||
const static char kChars[] = {'x', 'y', 'z', 'w'};
|
||||
return kChars[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];
|
||||
}
|
||||
// Returns true if any non-constant components are written.
|
||||
bool stores_non_constants() const {
|
||||
for (int i = 0; i < 4; ++i) {
|
||||
if (write_mask[i] && components[i] != SwizzleSource::k0 &&
|
||||
components[i] != SwizzleSource::k1) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
// 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; // In dwords.
|
||||
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::kAdd;
|
||||
// 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 Shader {
|
||||
public:
|
||||
struct Error {
|
||||
bool is_fatal = false;
|
||||
std::string message;
|
||||
};
|
||||
|
||||
struct VertexBinding {
|
||||
struct Attribute {
|
||||
// Attribute index, 0-based in the entire shader.
|
||||
int attrib_index;
|
||||
// Fetch instruction with all parameters.
|
||||
ParsedVertexFetchInstruction fetch_instr;
|
||||
// Size of the attribute, in words.
|
||||
uint32_t size_words;
|
||||
};
|
||||
|
||||
// Index within the vertex binding listing.
|
||||
int binding_index;
|
||||
// Fetch constant index [0-95].
|
||||
uint32_t fetch_constant;
|
||||
// Stride of the entire binding, in words.
|
||||
uint32_t stride_words;
|
||||
// Packed attributes within the binding buffer.
|
||||
std::vector<Attribute> attributes;
|
||||
};
|
||||
|
||||
struct TextureBinding {
|
||||
// Index within the texture binding listing.
|
||||
size_t binding_index;
|
||||
// Fetch constant index [0-31].
|
||||
uint32_t fetch_constant;
|
||||
// Fetch instruction with all parameters.
|
||||
ParsedTextureFetchInstruction fetch_instr;
|
||||
};
|
||||
|
||||
Shader(ShaderType shader_type, uint64_t ucode_data_hash,
|
||||
const uint32_t* ucode_dwords, size_t ucode_dword_count);
|
||||
virtual ~Shader();
|
||||
|
||||
// Whether the shader is identified as a vertex or pixel shader.
|
||||
ShaderType type() const { return shader_type_; }
|
||||
bool is_valid() const { return !!translated_shader_; }
|
||||
const std::string& host_disassembly() const { return host_disassembly_; }
|
||||
TranslatedShader* translated_shader() const {
|
||||
return translated_shader_.get();
|
||||
|
||||
// Microcode dwords in host endianness.
|
||||
const std::vector<uint32_t>& ucode_data() const { return ucode_data_; }
|
||||
const uint32_t* ucode_dwords() const { return ucode_data_.data(); }
|
||||
size_t ucode_dword_count() const { return ucode_data_.size(); }
|
||||
|
||||
// All vertex bindings used in the shader.
|
||||
// Valid for vertex shaders only.
|
||||
const std::vector<VertexBinding>& vertex_bindings() const {
|
||||
return vertex_bindings_;
|
||||
}
|
||||
|
||||
const uint32_t* data() const { return data_.data(); }
|
||||
uint32_t dword_count() const { return uint32_t(data_.size()); }
|
||||
// All texture bindings used in the shader.
|
||||
// Valid for both vertex and pixel shaders.
|
||||
const std::vector<TextureBinding>& texture_bindings() const {
|
||||
return texture_bindings_;
|
||||
}
|
||||
|
||||
virtual bool Prepare(ShaderTranslator* shader_translator);
|
||||
// Returns true if the given color target index [0-3].
|
||||
bool writes_color_target(int i) const { return writes_color_targets_[i]; }
|
||||
|
||||
// True if the shader was translated and prepared without error.
|
||||
bool is_valid() const { return is_valid_; }
|
||||
|
||||
// Errors that occurred during translation.
|
||||
const std::vector<Error>& errors() const { return errors_; }
|
||||
|
||||
// Microcode disassembly in D3D format.
|
||||
const std::string& ucode_disassembly() const { return ucode_disassembly_; }
|
||||
|
||||
// Translated shader binary (or text).
|
||||
const std::vector<uint8_t>& translated_binary() const {
|
||||
return translated_binary_;
|
||||
}
|
||||
|
||||
// Gets the translated shader binary as a string.
|
||||
// This is only valid if it is actually text.
|
||||
std::string GetTranslatedBinaryString() const;
|
||||
|
||||
// Disassembly of the translated from the host graphics layer.
|
||||
// May be empty if the host does not support disassembly.
|
||||
const std::string& host_disassembly() const { return host_disassembly_; }
|
||||
// A lot of errors that occurred during preparation of the host shader.
|
||||
const std::string& host_error_log() const { return host_error_log_; }
|
||||
// Host binary that can be saved and reused across runs.
|
||||
// May be empty if the host does not support saving binaries.
|
||||
const std::vector<uint8_t>& host_binary() const { return host_binary_; }
|
||||
|
||||
// Prepares the shader for use in the host graphics API.
|
||||
virtual bool Prepare() { return is_valid_; }
|
||||
|
||||
// Dumps the shader to a file in the given path based on ucode hash.
|
||||
// Both the ucode binary and disassembled and translated shader will be
|
||||
// written.
|
||||
void Dump(const std::string& base_path, const char* path_prefix);
|
||||
|
||||
protected:
|
||||
Shader(ShaderType shader_type, uint64_t data_hash, const uint32_t* dword_ptr,
|
||||
uint32_t dword_count);
|
||||
friend class ShaderTranslator;
|
||||
|
||||
ShaderType shader_type_;
|
||||
uint64_t data_hash_;
|
||||
std::vector<uint32_t> data_;
|
||||
std::vector<uint32_t> ucode_data_;
|
||||
uint64_t ucode_data_hash_;
|
||||
|
||||
std::string translated_disassembly_;
|
||||
std::vector<VertexBinding> vertex_bindings_;
|
||||
std::vector<TextureBinding> texture_bindings_;
|
||||
bool writes_color_targets_[4] = {false, false, false, false};
|
||||
|
||||
bool is_valid_ = false;
|
||||
std::vector<Error> errors_;
|
||||
|
||||
std::string ucode_disassembly_;
|
||||
std::vector<uint8_t> translated_binary_;
|
||||
std::string host_disassembly_;
|
||||
std::string error_log_;
|
||||
|
||||
std::unique_ptr<TranslatedShader> translated_shader_;
|
||||
std::string host_error_log_;
|
||||
std::vector<uint8_t> host_binary_;
|
||||
};
|
||||
|
||||
} // namespace gpu
|
||||
|
|
|
@ -79,6 +79,11 @@ int shader_compiler_main(const std::vector<std::wstring>& args) {
|
|||
shader_type == ShaderType::kVertex ? "vertex" : "pixel",
|
||||
ucode_dwords.size(), ucode_dwords.size() * 4);
|
||||
|
||||
// TODO(benvanik): hash? need to return the data to big-endian format first.
|
||||
uint64_t ucode_data_hash = 0;
|
||||
auto shader = std::make_unique<Shader>(
|
||||
shader_type, ucode_data_hash, ucode_dwords.data(), ucode_dwords.size());
|
||||
|
||||
std::unique_ptr<ShaderTranslator> translator;
|
||||
if (FLAGS_shader_output_type == "spirv" ||
|
||||
FLAGS_shader_output_type == "spirvtext") {
|
||||
|
@ -90,13 +95,10 @@ int shader_compiler_main(const std::vector<std::wstring>& args) {
|
|||
translator = std::make_unique<UcodeShaderTranslator>();
|
||||
}
|
||||
|
||||
// TODO(benvanik): hash? need to return the data to big-endian format first.
|
||||
uint64_t ucode_data_hash = 0;
|
||||
auto translated_shader = translator->Translate(
|
||||
shader_type, ucode_data_hash, ucode_dwords.data(), ucode_dwords.size());
|
||||
translator->Translate(shader.get());
|
||||
|
||||
const void* source_data = translated_shader->binary().data();
|
||||
size_t source_data_size = translated_shader->binary().size();
|
||||
const void* source_data = shader->translated_binary().data();
|
||||
size_t source_data_size = shader->translated_binary().size();
|
||||
|
||||
std::unique_ptr<xe::ui::spirv::SpirvDisassembler::Result> spirv_disasm_result;
|
||||
if (FLAGS_shader_output_type == "spirvtext") {
|
||||
|
|
|
@ -40,36 +40,6 @@ using namespace ucode;
|
|||
// Lots of naming comes from the disassembly spit out by the XNA GS compiler
|
||||
// and dumps of d3dcompiler and games: http://pastebin.com/i4kAv7bB
|
||||
|
||||
TranslatedShader::TranslatedShader(ShaderType shader_type,
|
||||
uint64_t ucode_data_hash,
|
||||
const uint32_t* ucode_dwords,
|
||||
size_t ucode_dword_count,
|
||||
std::vector<Error> errors)
|
||||
: shader_type_(shader_type),
|
||||
ucode_data_hash_(ucode_data_hash),
|
||||
errors_(std::move(errors)) {
|
||||
ucode_data_.resize(ucode_dword_count);
|
||||
std::memcpy(ucode_data_.data(), ucode_dwords,
|
||||
ucode_dword_count * sizeof(uint32_t));
|
||||
|
||||
is_valid_ = true;
|
||||
for (const auto& error : errors_) {
|
||||
if (error.is_fatal) {
|
||||
is_valid_ = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
TranslatedShader::~TranslatedShader() = default;
|
||||
|
||||
std::string TranslatedShader::GetBinaryString() const {
|
||||
std::string result;
|
||||
result.resize(binary_.size());
|
||||
std::memcpy(const_cast<char*>(result.data()), binary_.data(), binary_.size());
|
||||
return result;
|
||||
}
|
||||
|
||||
ShaderTranslator::ShaderTranslator() = default;
|
||||
|
||||
ShaderTranslator::~ShaderTranslator() = default;
|
||||
|
@ -87,14 +57,12 @@ void ShaderTranslator::Reset() {
|
|||
}
|
||||
}
|
||||
|
||||
std::unique_ptr<TranslatedShader> ShaderTranslator::Translate(
|
||||
ShaderType shader_type, uint64_t ucode_data_hash,
|
||||
const uint32_t* ucode_dwords, size_t ucode_dword_count) {
|
||||
bool ShaderTranslator::Translate(Shader* shader) {
|
||||
Reset();
|
||||
|
||||
shader_type_ = shader_type;
|
||||
ucode_dwords_ = ucode_dwords;
|
||||
ucode_dword_count_ = ucode_dword_count;
|
||||
shader_type_ = shader->type();
|
||||
ucode_dwords_ = shader->ucode_dwords();
|
||||
ucode_dword_count_ = shader->ucode_dword_count();
|
||||
|
||||
// Run through and gather all binding information.
|
||||
// Translators may need this before they start codegen.
|
||||
|
@ -119,17 +87,24 @@ std::unique_ptr<TranslatedShader> ShaderTranslator::Translate(
|
|||
|
||||
TranslateBlocks();
|
||||
|
||||
std::unique_ptr<TranslatedShader> translated_shader(
|
||||
new TranslatedShader(shader_type, ucode_data_hash, ucode_dwords,
|
||||
ucode_dword_count, std::move(errors_)));
|
||||
translated_shader->binary_ = CompleteTranslation();
|
||||
translated_shader->ucode_disassembly_ = ucode_disasm_buffer_.to_string();
|
||||
translated_shader->vertex_bindings_ = std::move(vertex_bindings_);
|
||||
translated_shader->texture_bindings_ = std::move(texture_bindings_);
|
||||
shader->errors_ = std::move(errors_);
|
||||
shader->translated_binary_ = CompleteTranslation();
|
||||
shader->ucode_disassembly_ = ucode_disasm_buffer_.to_string();
|
||||
shader->vertex_bindings_ = std::move(vertex_bindings_);
|
||||
shader->texture_bindings_ = std::move(texture_bindings_);
|
||||
for (size_t i = 0; i < xe::countof(writes_color_targets_); ++i) {
|
||||
translated_shader->writes_color_targets_[i] = writes_color_targets_[i];
|
||||
shader->writes_color_targets_[i] = writes_color_targets_[i];
|
||||
}
|
||||
return translated_shader;
|
||||
|
||||
shader->is_valid_ = true;
|
||||
for (const auto& error : shader->errors_) {
|
||||
if (error.is_fatal) {
|
||||
shader->is_valid_ = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return shader->is_valid_;
|
||||
}
|
||||
|
||||
void ShaderTranslator::MarkUcodeInstruction(uint32_t dword_offset) {
|
||||
|
@ -159,7 +134,7 @@ void ShaderTranslator::AppendUcodeDisasmFormat(const char* format, ...) {
|
|||
}
|
||||
|
||||
void ShaderTranslator::EmitTranslationError(const char* message) {
|
||||
TranslatedShader::Error error;
|
||||
Shader::Error error;
|
||||
error.is_fatal = true;
|
||||
error.message = message;
|
||||
// TODO(benvanik): location information.
|
||||
|
@ -167,7 +142,7 @@ void ShaderTranslator::EmitTranslationError(const char* message) {
|
|||
}
|
||||
|
||||
void ShaderTranslator::EmitUnimplementedTranslationError() {
|
||||
TranslatedShader::Error error;
|
||||
Shader::Error error;
|
||||
error.is_fatal = true;
|
||||
error.message = "Unimplemented translation";
|
||||
// TODO(benvanik): location information.
|
||||
|
@ -195,6 +170,7 @@ void ShaderTranslator::GatherBindingInformation(
|
|||
auto fetch_opcode =
|
||||
static_cast<FetchOpcode>(ucode_dwords_[instr_offset * 3] & 0x1F);
|
||||
if (fetch_opcode == FetchOpcode::kVertexFetch) {
|
||||
assert_true(is_vertex_shader());
|
||||
GatherVertexBindingInformation(
|
||||
*reinterpret_cast<const VertexFetchInstruction*>(
|
||||
ucode_dwords_ + instr_offset * 3));
|
||||
|
@ -231,7 +207,7 @@ void ShaderTranslator::GatherVertexBindingInformation(
|
|||
|
||||
// Try to allocate an attribute on an existing binding.
|
||||
// If no binding for this fetch slot is found create it.
|
||||
using VertexBinding = TranslatedShader::VertexBinding;
|
||||
using VertexBinding = Shader::VertexBinding;
|
||||
VertexBinding::Attribute* attrib = nullptr;
|
||||
for (auto& vertex_binding : vertex_bindings_) {
|
||||
if (vertex_binding.fetch_constant == op.fetch_constant_index()) {
|
||||
|
@ -244,7 +220,7 @@ void ShaderTranslator::GatherVertexBindingInformation(
|
|||
}
|
||||
if (!attrib) {
|
||||
assert_not_zero(op.stride());
|
||||
TranslatedShader::VertexBinding vertex_binding;
|
||||
VertexBinding vertex_binding;
|
||||
vertex_binding.binding_index = static_cast<int>(vertex_bindings_.size());
|
||||
vertex_binding.fetch_constant = op.fetch_constant_index();
|
||||
vertex_binding.stride_words = op.stride();
|
||||
|
@ -269,7 +245,7 @@ void ShaderTranslator::GatherTextureBindingInformation(
|
|||
// Doesn't use bindings.
|
||||
return;
|
||||
}
|
||||
TranslatedShader::TextureBinding binding;
|
||||
Shader::TextureBinding binding;
|
||||
binding.binding_index = texture_bindings_.size();
|
||||
ParseTextureFetchInstruction(op, &binding.fetch_instr);
|
||||
binding.fetch_constant = binding.fetch_instr.operands[1].storage_index;
|
||||
|
|
|
@ -15,537 +15,18 @@
|
|||
#include <vector>
|
||||
|
||||
#include "xenia/base/string_buffer.h"
|
||||
#include "xenia/gpu/shader.h"
|
||||
#include "xenia/gpu/ucode.h"
|
||||
#include "xenia/gpu/xenos.h"
|
||||
|
||||
namespace xe {
|
||||
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 GetCharForComponentIndex(int i) {
|
||||
const static char kChars[] = {'x', 'y', 'z', 'w'};
|
||||
return kChars[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];
|
||||
}
|
||||
// Returns true if any non-constant components are written.
|
||||
bool stores_non_constants() const {
|
||||
for (int i = 0; i < 4; ++i) {
|
||||
if (write_mask[i] && components[i] != SwizzleSource::k0 &&
|
||||
components[i] != SwizzleSource::k1) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
// 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; // In dwords.
|
||||
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::kAdd;
|
||||
// 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 {
|
||||
public:
|
||||
struct Error {
|
||||
bool is_fatal = false;
|
||||
std::string message;
|
||||
};
|
||||
|
||||
struct VertexBinding {
|
||||
struct Attribute {
|
||||
// Attribute index, 0-based in the entire shader.
|
||||
int attrib_index;
|
||||
// Fetch instruction with all parameters.
|
||||
ParsedVertexFetchInstruction fetch_instr;
|
||||
// Size of the attribute, in words.
|
||||
uint32_t size_words;
|
||||
};
|
||||
|
||||
// Index within the vertex binding listing.
|
||||
int binding_index;
|
||||
// Fetch constant index [0-95].
|
||||
uint32_t fetch_constant;
|
||||
// Stride of the entire binding, in words.
|
||||
uint32_t stride_words;
|
||||
// Packed attributes within the binding buffer.
|
||||
std::vector<Attribute> attributes;
|
||||
};
|
||||
|
||||
struct TextureBinding {
|
||||
// Index within the texture binding listing.
|
||||
size_t binding_index;
|
||||
// Fetch constant index [0-31].
|
||||
uint32_t fetch_constant;
|
||||
// Fetch instruction with all parameters.
|
||||
ParsedTextureFetchInstruction fetch_instr;
|
||||
};
|
||||
|
||||
~TranslatedShader();
|
||||
|
||||
ShaderType type() const { return shader_type_; }
|
||||
const std::vector<uint32_t>& ucode_data() const { return ucode_data_; }
|
||||
const uint32_t* ucode_dwords() const { return ucode_data_.data(); }
|
||||
size_t ucode_dword_count() const { return ucode_data_.size(); }
|
||||
|
||||
const std::vector<VertexBinding>& vertex_bindings() const {
|
||||
return vertex_bindings_;
|
||||
}
|
||||
const std::vector<TextureBinding>& texture_bindings() const {
|
||||
return texture_bindings_;
|
||||
}
|
||||
// Returns true if the given color target index [0-3].
|
||||
bool writes_color_target(int i) const { return writes_color_targets_[i]; }
|
||||
|
||||
bool is_valid() const { return is_valid_; }
|
||||
const std::vector<Error>& errors() const { return errors_; }
|
||||
|
||||
const std::vector<uint8_t>& binary() const { return binary_; }
|
||||
const std::string& ucode_disassembly() const { return ucode_disassembly_; }
|
||||
|
||||
std::string GetBinaryString() const;
|
||||
|
||||
private:
|
||||
friend class ShaderTranslator;
|
||||
|
||||
TranslatedShader(ShaderType shader_type, uint64_t ucode_data_hash,
|
||||
const uint32_t* ucode_dwords, size_t ucode_dword_count,
|
||||
std::vector<Error> errors);
|
||||
|
||||
ShaderType shader_type_;
|
||||
std::vector<uint32_t> ucode_data_;
|
||||
uint64_t ucode_data_hash_;
|
||||
|
||||
std::vector<VertexBinding> vertex_bindings_;
|
||||
std::vector<TextureBinding> texture_bindings_;
|
||||
bool writes_color_targets_[4] = {false, false, false, false};
|
||||
|
||||
bool is_valid_ = false;
|
||||
std::vector<Error> errors_;
|
||||
|
||||
std::string ucode_disassembly_;
|
||||
std::vector<uint8_t> binary_;
|
||||
};
|
||||
|
||||
class ShaderTranslator {
|
||||
public:
|
||||
virtual ~ShaderTranslator();
|
||||
|
||||
std::unique_ptr<TranslatedShader> Translate(ShaderType shader_type,
|
||||
uint64_t ucode_data_hash,
|
||||
const uint32_t* ucode_dwords,
|
||||
size_t ucode_dword_count);
|
||||
bool Translate(Shader* shader);
|
||||
|
||||
protected:
|
||||
ShaderTranslator();
|
||||
|
@ -558,12 +39,11 @@ class ShaderTranslator {
|
|||
// 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 {
|
||||
const std::vector<Shader::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 {
|
||||
const std::vector<Shader::TextureBinding>& texture_bindings() const {
|
||||
return texture_bindings_;
|
||||
}
|
||||
|
||||
|
@ -684,7 +164,7 @@ class ShaderTranslator {
|
|||
size_t ucode_dword_count_;
|
||||
|
||||
// Accumulated translation errors.
|
||||
std::vector<TranslatedShader::Error> errors_;
|
||||
std::vector<Shader::Error> errors_;
|
||||
|
||||
// Microcode disassembly buffer, accumulated throughout the translation.
|
||||
StringBuffer ucode_disasm_buffer_;
|
||||
|
@ -698,8 +178,8 @@ class ShaderTranslator {
|
|||
|
||||
// Detected binding information gathered before translation.
|
||||
int total_attrib_count_ = 0;
|
||||
std::vector<TranslatedShader::VertexBinding> vertex_bindings_;
|
||||
std::vector<TranslatedShader::TextureBinding> texture_bindings_;
|
||||
std::vector<Shader::VertexBinding> vertex_bindings_;
|
||||
std::vector<Shader::TextureBinding> texture_bindings_;
|
||||
bool writes_color_targets_[4] = {false, false, false, false};
|
||||
|
||||
static const AluOpcodeInfo alu_vector_opcode_infos_[0x20];
|
||||
|
|
|
@ -486,11 +486,11 @@ void TraceViewer::DrawShaderUI(Shader* shader, ShaderDisplayType display_type) {
|
|||
|
||||
switch (display_type) {
|
||||
case ShaderDisplayType::kUcode: {
|
||||
DrawMultilineString(shader->translated_shader()->ucode_disassembly());
|
||||
DrawMultilineString(shader->ucode_disassembly());
|
||||
break;
|
||||
}
|
||||
case ShaderDisplayType::kTranslated: {
|
||||
const auto& str = shader->translated_shader()->GetBinaryString();
|
||||
const auto& str = shader->GetTranslatedBinaryString();
|
||||
size_t i = 0;
|
||||
bool done = false;
|
||||
while (!done && i < str.size()) {
|
||||
|
@ -567,7 +567,7 @@ void TraceViewer::DrawBlendMode(uint32_t src_blend, uint32_t dest_blend,
|
|||
}
|
||||
|
||||
void TraceViewer::DrawTextureInfo(
|
||||
const TranslatedShader::TextureBinding& texture_binding) {
|
||||
const Shader::TextureBinding& texture_binding) {
|
||||
auto& regs = *graphics_system_->register_file();
|
||||
|
||||
int r = XE_GPU_REG_SHADER_CONSTANT_FETCH_00_0 +
|
||||
|
@ -633,15 +633,14 @@ void TraceViewer::DrawTextureInfo(
|
|||
}
|
||||
|
||||
void TraceViewer::DrawFailedTextureInfo(
|
||||
const TranslatedShader::TextureBinding& texture_binding,
|
||||
const char* message) {
|
||||
const Shader::TextureBinding& texture_binding, const char* message) {
|
||||
// TODO(benvanik): better error info/etc.
|
||||
ImGui::TextColored(kColorError, "ERROR: %s", message);
|
||||
}
|
||||
|
||||
void TraceViewer::DrawVertexFetcher(
|
||||
Shader* shader, const TranslatedShader::VertexBinding& vertex_binding,
|
||||
const xe_gpu_vertex_fetch_t* fetch) {
|
||||
void TraceViewer::DrawVertexFetcher(Shader* shader,
|
||||
const Shader::VertexBinding& vertex_binding,
|
||||
const xe_gpu_vertex_fetch_t* fetch) {
|
||||
const uint8_t* addr = memory_->TranslatePhysical(fetch->address << 2);
|
||||
uint32_t vertex_count = (fetch->size * 4) / vertex_binding.stride_words;
|
||||
int column_count = 0;
|
||||
|
@ -1411,9 +1410,7 @@ void TraceViewer::DrawStateUI() {
|
|||
if (ImGui::CollapsingHeader("Vertex Buffers")) {
|
||||
auto shader = command_processor->active_vertex_shader();
|
||||
if (shader) {
|
||||
const auto& vertex_bindings =
|
||||
shader->translated_shader()->vertex_bindings();
|
||||
for (const auto& vertex_binding : vertex_bindings) {
|
||||
for (const auto& vertex_binding : shader->vertex_bindings()) {
|
||||
int r = XE_GPU_REG_SHADER_CONSTANT_FETCH_00_0 +
|
||||
(vertex_binding.fetch_constant / 3) * 6;
|
||||
const auto group =
|
||||
|
@ -1451,8 +1448,7 @@ void TraceViewer::DrawStateUI() {
|
|||
if (ImGui::CollapsingHeader("Vertex Textures")) {
|
||||
auto shader = command_processor->active_vertex_shader();
|
||||
if (shader) {
|
||||
const auto& texture_bindings =
|
||||
shader->translated_shader()->texture_bindings();
|
||||
const auto& texture_bindings = shader->texture_bindings();
|
||||
if (!texture_bindings.empty()) {
|
||||
for (const auto& texture_binding : texture_bindings) {
|
||||
DrawTextureInfo(texture_binding);
|
||||
|
@ -1467,8 +1463,7 @@ void TraceViewer::DrawStateUI() {
|
|||
if (ImGui::CollapsingHeader("Textures")) {
|
||||
auto shader = command_processor->active_pixel_shader();
|
||||
if (shader) {
|
||||
const auto& texture_bindings =
|
||||
shader->translated_shader()->texture_bindings();
|
||||
const auto& texture_bindings = shader->texture_bindings();
|
||||
if (!texture_bindings.empty()) {
|
||||
for (const auto& texture_binding : texture_bindings) {
|
||||
DrawTextureInfo(texture_binding);
|
||||
|
|
|
@ -85,13 +85,12 @@ class TraceViewer {
|
|||
void DrawBlendMode(uint32_t src_blend, uint32_t dest_blend,
|
||||
uint32_t blend_op);
|
||||
|
||||
void DrawTextureInfo(const TranslatedShader::TextureBinding& texture_binding);
|
||||
void DrawFailedTextureInfo(
|
||||
const TranslatedShader::TextureBinding& texture_binding,
|
||||
const char* message);
|
||||
void DrawTextureInfo(const Shader::TextureBinding& texture_binding);
|
||||
void DrawFailedTextureInfo(const Shader::TextureBinding& texture_binding,
|
||||
const char* message);
|
||||
|
||||
void DrawVertexFetcher(Shader* shader,
|
||||
const TranslatedShader::VertexBinding& vertex_binding,
|
||||
const Shader::VertexBinding& vertex_binding,
|
||||
const xenos::xe_gpu_vertex_fetch_t* fetch);
|
||||
};
|
||||
|
||||
|
|
|
@ -361,25 +361,23 @@ namespace shader_playground {
|
|||
(shaderCode[6] << 8) | (shaderCode[7] << 0);
|
||||
int wordOffset = byteOffset / 4;
|
||||
|
||||
uint[] swappedCode = new uint[(shaderCode.Length - wordOffset) / sizeof(uint)];
|
||||
Buffer.BlockCopy(shaderCode, wordOffset * 4, swappedCode, 0, shaderCode.Length - wordOffset * 4);
|
||||
for (int i = 0; i < swappedCode.Length; ++i) {
|
||||
swappedCode[i] = SwapBytes(swappedCode[i]);
|
||||
}
|
||||
uint[] shaderDwords = new uint[(shaderCode.Length - wordOffset) / sizeof(uint)];
|
||||
Buffer.BlockCopy(shaderCode, wordOffset * 4, shaderDwords, 0, shaderCode.Length - wordOffset * 4);
|
||||
|
||||
var sb = new StringBuilder();
|
||||
sb.Append("const uint32_t shader_dwords[] = {");
|
||||
for (int i = 0; i < swappedCode.Length; ++i) {
|
||||
sb.AppendFormat("0x{0:X8}, ", swappedCode[i]);
|
||||
for (int i = 0; i < shaderDwords.Length; ++i) {
|
||||
sb.AppendFormat("0x{0:X8}, ", SwapByte(shaderDwords[i]));
|
||||
}
|
||||
sb.Append("};" + Environment.NewLine);
|
||||
sb.Append("shader_type = ShaderType::" + (shaderType == "vs" ? "kVertex" : "kPixel") + ";" + Environment.NewLine);
|
||||
UpdateTextBox(wordsTextBox, sb.ToString(), true);
|
||||
wordsTextBox.SelectAll();
|
||||
|
||||
return swappedCode;
|
||||
return shaderDwords;
|
||||
}
|
||||
|
||||
uint SwapBytes(uint x) {
|
||||
uint SwapByte(uint x) {
|
||||
return ((x & 0x000000ff) << 24) +
|
||||
((x & 0x0000ff00) << 8) +
|
||||
((x & 0x00ff0000) >> 8) +
|
||||
|
|
Loading…
Reference in New Issue