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.
|
// Perform translation.
|
||||||
// If this fails the shader will be marked as invalid and ignored later.
|
// 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",
|
XELOGGPU("Set %s shader at %0.8X (%db):\n%s",
|
||||||
shader_type == ShaderType::kVertex ? "vertex" : "pixel",
|
shader_type == ShaderType::kVertex ? "vertex" : "pixel",
|
||||||
guest_address, dword_count * 4,
|
guest_address, dword_count * 4,
|
||||||
shader_ptr->translated_shader()->ucode_disassembly().c_str());
|
shader_ptr->ucode_disassembly().c_str());
|
||||||
}
|
}
|
||||||
return shader_ptr;
|
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
|
// Note that write mask may be more permissive than we want, so we mix that
|
||||||
// with the actual targets the pixel shader writes to.
|
// with the actual targets the pixel shader writes to.
|
||||||
GLenum draw_buffers[4] = {GL_NONE, GL_NONE, GL_NONE, GL_NONE};
|
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};
|
GLuint color_targets[4] = {kAnyTarget, kAnyTarget, kAnyTarget, kAnyTarget};
|
||||||
if (enable_mode == ModeControl::kColorDepth) {
|
if (enable_mode == ModeControl::kColorDepth) {
|
||||||
uint32_t color_info[4] = {
|
uint32_t color_info[4] = {
|
||||||
|
@ -789,7 +797,7 @@ GL4CommandProcessor::UpdateStatus GL4CommandProcessor::UpdateRenderTargets() {
|
||||||
// A2XX_RB_COLOR_MASK_WRITE_* == D3DRS_COLORWRITEENABLE
|
// A2XX_RB_COLOR_MASK_WRITE_* == D3DRS_COLORWRITEENABLE
|
||||||
for (int n = 0; n < xe::countof(color_info); n++) {
|
for (int n = 0; n < xe::countof(color_info); n++) {
|
||||||
uint32_t write_mask = (regs.rb_color_mask >> (n * 4)) & 0xF;
|
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
|
// Unused, so keep disabled and set to wildcard so we'll take any
|
||||||
// framebuffer that has it.
|
// framebuffer that has it.
|
||||||
continue;
|
continue;
|
||||||
|
@ -1362,9 +1370,7 @@ GL4CommandProcessor::UpdateStatus GL4CommandProcessor::PopulateVertexBuffers() {
|
||||||
auto& regs = *register_file_;
|
auto& regs = *register_file_;
|
||||||
assert_not_null(active_vertex_shader_);
|
assert_not_null(active_vertex_shader_);
|
||||||
|
|
||||||
const auto& vertex_bindings =
|
for (const auto& vertex_binding : active_vertex_shader_->vertex_bindings()) {
|
||||||
active_vertex_shader_->translated_shader()->vertex_bindings();
|
|
||||||
for (const auto& vertex_binding : vertex_bindings) {
|
|
||||||
int r = XE_GPU_REG_SHADER_CONSTANT_FETCH_00_0 +
|
int r = XE_GPU_REG_SHADER_CONSTANT_FETCH_00_0 +
|
||||||
(vertex_binding.fetch_constant / 3) * 6;
|
(vertex_binding.fetch_constant / 3) * 6;
|
||||||
const auto group = reinterpret_cast<xe_gpu_fetch_group_t*>(®s.values[r]);
|
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};
|
bool has_setup_sampler[32] = {false};
|
||||||
|
|
||||||
// Vertex texture samplers.
|
// Vertex texture samplers.
|
||||||
const auto& vertex_sampler_inputs =
|
for (auto& texture_binding : active_vertex_shader_->texture_bindings()) {
|
||||||
active_vertex_shader_->translated_shader()->texture_bindings();
|
|
||||||
for (auto& texture_binding : vertex_sampler_inputs) {
|
|
||||||
if (has_setup_sampler[texture_binding.fetch_constant]) {
|
if (has_setup_sampler[texture_binding.fetch_constant]) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
@ -1450,9 +1454,7 @@ GL4CommandProcessor::UpdateStatus GL4CommandProcessor::PopulateSamplers() {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Pixel shader texture sampler.
|
// Pixel shader texture sampler.
|
||||||
const auto& pixel_sampler_inputs =
|
for (auto& texture_binding : active_pixel_shader_->texture_bindings()) {
|
||||||
active_pixel_shader_->translated_shader()->texture_bindings();
|
|
||||||
for (auto& texture_binding : pixel_sampler_inputs) {
|
|
||||||
if (has_setup_sampler[texture_binding.fetch_constant]) {
|
if (has_setup_sampler[texture_binding.fetch_constant]) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
@ -1469,7 +1471,7 @@ GL4CommandProcessor::UpdateStatus GL4CommandProcessor::PopulateSamplers() {
|
||||||
}
|
}
|
||||||
|
|
||||||
GL4CommandProcessor::UpdateStatus GL4CommandProcessor::PopulateSampler(
|
GL4CommandProcessor::UpdateStatus GL4CommandProcessor::PopulateSampler(
|
||||||
const TranslatedShader::TextureBinding& texture_binding) {
|
const Shader::TextureBinding& texture_binding) {
|
||||||
auto& regs = *register_file_;
|
auto& regs = *register_file_;
|
||||||
int r = XE_GPU_REG_SHADER_CONSTANT_FETCH_00_0 +
|
int r = XE_GPU_REG_SHADER_CONSTANT_FETCH_00_0 +
|
||||||
texture_binding.fetch_constant * 6;
|
texture_binding.fetch_constant * 6;
|
||||||
|
|
|
@ -123,8 +123,7 @@ class GL4CommandProcessor : public CommandProcessor {
|
||||||
UpdateStatus PopulateIndexBuffer(IndexBufferInfo* index_buffer_info);
|
UpdateStatus PopulateIndexBuffer(IndexBufferInfo* index_buffer_info);
|
||||||
UpdateStatus PopulateVertexBuffers();
|
UpdateStatus PopulateVertexBuffers();
|
||||||
UpdateStatus PopulateSamplers();
|
UpdateStatus PopulateSamplers();
|
||||||
UpdateStatus PopulateSampler(
|
UpdateStatus PopulateSampler(const Shader::TextureBinding& texture_binding);
|
||||||
const TranslatedShader::TextureBinding& texture_binding);
|
|
||||||
bool IssueCopy() override;
|
bool IssueCopy() override;
|
||||||
|
|
||||||
CachedFramebuffer* GetFramebuffer(GLuint color_targets[4],
|
CachedFramebuffer* GetFramebuffer(GLuint color_targets[4],
|
||||||
|
|
|
@ -9,11 +9,8 @@
|
||||||
|
|
||||||
#include "xenia/gpu/gl4/gl4_shader.h"
|
#include "xenia/gpu/gl4/gl4_shader.h"
|
||||||
|
|
||||||
#include "xenia/base/filesystem.h"
|
|
||||||
#include "xenia/base/logging.h"
|
#include "xenia/base/logging.h"
|
||||||
#include "xenia/base/math.h"
|
#include "xenia/base/math.h"
|
||||||
#include "xenia/gpu/gl4/gl4_gpu_flags.h"
|
|
||||||
#include "xenia/gpu/gpu_flags.h"
|
|
||||||
|
|
||||||
namespace xe {
|
namespace xe {
|
||||||
namespace gpu {
|
namespace gpu {
|
||||||
|
@ -30,8 +27,8 @@ GL4Shader::~GL4Shader() {
|
||||||
glDeleteVertexArrays(1, &vao_);
|
glDeleteVertexArrays(1, &vao_);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool GL4Shader::Prepare(ShaderTranslator* shader_translator) {
|
bool GL4Shader::Prepare() {
|
||||||
if (!Shader::Prepare(shader_translator)) {
|
if (!Shader::Prepare()) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -51,7 +48,7 @@ bool GL4Shader::Prepare(ShaderTranslator* shader_translator) {
|
||||||
bool GL4Shader::PrepareVertexArrayObject() {
|
bool GL4Shader::PrepareVertexArrayObject() {
|
||||||
glCreateVertexArrays(1, &vao_);
|
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) {
|
for (const auto& attrib : vertex_binding.attributes) {
|
||||||
auto comp_count = GetVertexFormatComponentCount(
|
auto comp_count = GetVertexFormatComponentCount(
|
||||||
attrib.fetch_instr.attributes.data_format);
|
attrib.fetch_instr.attributes.data_format);
|
||||||
|
@ -126,43 +123,8 @@ bool GL4Shader::PrepareVertexArrayObject() {
|
||||||
bool GL4Shader::CompileProgram() {
|
bool GL4Shader::CompileProgram() {
|
||||||
assert_zero(program_);
|
assert_zero(program_);
|
||||||
|
|
||||||
auto source_str = translated_shader_->GetBinaryString();
|
// Give source to GL.
|
||||||
|
auto source_str = GetTranslatedBinaryString();
|
||||||
// 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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
auto source_str_ptr = source_str.c_str();
|
auto source_str_ptr = source_str.c_str();
|
||||||
program_ = glCreateShaderProgramv(shader_type_ == ShaderType::kVertex
|
program_ = glCreateShaderProgramv(shader_type_ == ShaderType::kVertex
|
||||||
? GL_VERTEX_SHADER
|
? GL_VERTEX_SHADER
|
||||||
|
@ -185,7 +147,7 @@ bool GL4Shader::CompileProgram() {
|
||||||
glGetProgramInfoLog(program_, log_length, &log_length,
|
glGetProgramInfoLog(program_, log_length, &log_length,
|
||||||
const_cast<char*>(info_log.data()));
|
const_cast<char*>(info_log.data()));
|
||||||
XELOGE("Unable to link program: %s", info_log.c_str());
|
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");
|
assert_always("Unable to link generated shader");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -194,21 +156,21 @@ bool GL4Shader::CompileProgram() {
|
||||||
GLint binary_length = 0;
|
GLint binary_length = 0;
|
||||||
glGetProgramiv(program_, GL_PROGRAM_BINARY_LENGTH, &binary_length);
|
glGetProgramiv(program_, GL_PROGRAM_BINARY_LENGTH, &binary_length);
|
||||||
if (binary_length) {
|
if (binary_length) {
|
||||||
translated_binary_.resize(binary_length);
|
host_binary_.resize(binary_length);
|
||||||
GLenum binary_format;
|
GLenum binary_format;
|
||||||
glGetProgramBinary(program_, binary_length, &binary_length, &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.
|
// 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
|
// I haven't been able to figure out from the format how to do this
|
||||||
// without a search like this.
|
// without a search like this.
|
||||||
const char* disasm_start = nullptr;
|
const char* disasm_start = nullptr;
|
||||||
size_t search_offset = 0;
|
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) {
|
while (true) {
|
||||||
auto p = reinterpret_cast<char*>(
|
auto p = reinterpret_cast<char*>(
|
||||||
memchr(translated_binary_.data() + search_offset, '!',
|
memchr(host_binary_.data() + search_offset, '!',
|
||||||
translated_binary_.size() - search_offset));
|
host_binary_.size() - search_offset));
|
||||||
if (!p) {
|
if (!p) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -224,19 +186,6 @@ bool GL4Shader::CompileProgram() {
|
||||||
} else {
|
} else {
|
||||||
host_disassembly_ = std::string("Shader disassembly not available.");
|
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;
|
return true;
|
||||||
|
|
|
@ -13,7 +13,6 @@
|
||||||
#include <string>
|
#include <string>
|
||||||
|
|
||||||
#include "xenia/gpu/shader.h"
|
#include "xenia/gpu/shader.h"
|
||||||
#include "xenia/gpu/shader_translator.h"
|
|
||||||
#include "xenia/ui/gl/gl_context.h"
|
#include "xenia/ui/gl/gl_context.h"
|
||||||
|
|
||||||
namespace xe {
|
namespace xe {
|
||||||
|
@ -29,7 +28,7 @@ class GL4Shader : public Shader {
|
||||||
GLuint program() const { return program_; }
|
GLuint program() const { return program_; }
|
||||||
GLuint vao() const { return vao_; }
|
GLuint vao() const { return vao_; }
|
||||||
|
|
||||||
bool Prepare(ShaderTranslator* shader_translator);
|
bool Prepare() override;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
bool PrepareVertexArrayObject();
|
bool PrepareVertexArrayObject();
|
||||||
|
|
|
@ -90,8 +90,7 @@ void GlslShaderTranslator::StartTranslation() {
|
||||||
// Tons of boilerplate for shaders, here.
|
// Tons of boilerplate for shaders, here.
|
||||||
// We have a large amount of shared state defining uniforms and some common
|
// We have a large amount of shared state defining uniforms and some common
|
||||||
// utility functions used in both vertex and pixel shaders.
|
// utility functions used in both vertex and pixel shaders.
|
||||||
EmitSource(R"(
|
EmitSource(R"(#version 450
|
||||||
#version 450
|
|
||||||
#extension all : warn
|
#extension all : warn
|
||||||
#extension GL_ARB_bindless_texture : require
|
#extension GL_ARB_bindless_texture : require
|
||||||
#extension GL_ARB_explicit_uniform_location : require
|
#extension GL_ARB_explicit_uniform_location : require
|
||||||
|
|
|
@ -10,7 +10,7 @@
|
||||||
#ifndef XENIA_GPU_SAMPLER_INFO_H_
|
#ifndef XENIA_GPU_SAMPLER_INFO_H_
|
||||||
#define 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"
|
#include "xenia/gpu/xenos.h"
|
||||||
|
|
||||||
namespace xe {
|
namespace xe {
|
||||||
|
|
|
@ -2,38 +2,83 @@
|
||||||
******************************************************************************
|
******************************************************************************
|
||||||
* Xenia : Xbox 360 Emulator Research Project *
|
* 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. *
|
* Released under the BSD license - see LICENSE in the root for more details. *
|
||||||
******************************************************************************
|
******************************************************************************
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include "xenia/gpu/shader.h"
|
#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/memory.h"
|
||||||
|
#include "xenia/base/string.h"
|
||||||
|
|
||||||
namespace xe {
|
namespace xe {
|
||||||
namespace gpu {
|
namespace gpu {
|
||||||
|
|
||||||
Shader::Shader(ShaderType shader_type, uint64_t data_hash,
|
Shader::Shader(ShaderType shader_type, uint64_t ucode_data_hash,
|
||||||
const uint32_t* dword_ptr, uint32_t dword_count)
|
const uint32_t* ucode_dwords, size_t ucode_dword_count)
|
||||||
: shader_type_(shader_type), data_hash_(data_hash) {
|
: shader_type_(shader_type), ucode_data_hash_(ucode_data_hash) {
|
||||||
data_.resize(dword_count);
|
// We keep ucode data in host native format so it's easier to work with.
|
||||||
xe::copy_and_swap(data_.data(), dword_ptr, dword_count);
|
ucode_data_.resize(ucode_dword_count);
|
||||||
|
xe::copy_and_swap(ucode_data_.data(), ucode_dwords, ucode_dword_count);
|
||||||
}
|
}
|
||||||
|
|
||||||
Shader::~Shader() = default;
|
Shader::~Shader() = default;
|
||||||
|
|
||||||
bool Shader::Prepare(ShaderTranslator* shader_translator) {
|
std::string Shader::GetTranslatedBinaryString() const {
|
||||||
// Perform translation.
|
std::string result;
|
||||||
translated_shader_ = shader_translator->Translate(shader_type_, data_hash_,
|
result.resize(translated_binary_.size());
|
||||||
data_.data(), data_.size());
|
std::memcpy(const_cast<char*>(result.data()), translated_binary_.data(),
|
||||||
if (!translated_shader_) {
|
translated_binary_.size());
|
||||||
XELOGE("Shader failed translation");
|
return result;
|
||||||
return false;
|
}
|
||||||
|
|
||||||
|
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 gpu
|
||||||
} // namespace xe
|
} // namespace xe
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
******************************************************************************
|
******************************************************************************
|
||||||
* Xenia : Xbox 360 Emulator Research Project *
|
* 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. *
|
* Released under the BSD license - see LICENSE in the root for more details. *
|
||||||
******************************************************************************
|
******************************************************************************
|
||||||
*/
|
*/
|
||||||
|
@ -13,42 +13,565 @@
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
#include "xenia/gpu/shader_translator.h"
|
#include "xenia/base/string_buffer.h"
|
||||||
|
#include "xenia/gpu/ucode.h"
|
||||||
#include "xenia/gpu/xenos.h"
|
#include "xenia/gpu/xenos.h"
|
||||||
|
|
||||||
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 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 {
|
class Shader {
|
||||||
public:
|
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();
|
virtual ~Shader();
|
||||||
|
|
||||||
|
// Whether the shader is identified as a vertex or pixel shader.
|
||||||
ShaderType type() const { return shader_type_; }
|
ShaderType type() const { return shader_type_; }
|
||||||
bool is_valid() const { return !!translated_shader_; }
|
|
||||||
const std::string& host_disassembly() const { return host_disassembly_; }
|
// Microcode dwords in host endianness.
|
||||||
TranslatedShader* translated_shader() const {
|
const std::vector<uint32_t>& ucode_data() const { return ucode_data_; }
|
||||||
return translated_shader_.get();
|
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(); }
|
// All texture bindings used in the shader.
|
||||||
uint32_t dword_count() const { return uint32_t(data_.size()); }
|
// 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:
|
protected:
|
||||||
Shader(ShaderType shader_type, uint64_t data_hash, const uint32_t* dword_ptr,
|
friend class ShaderTranslator;
|
||||||
uint32_t dword_count);
|
|
||||||
|
|
||||||
ShaderType shader_type_;
|
ShaderType shader_type_;
|
||||||
uint64_t data_hash_;
|
std::vector<uint32_t> ucode_data_;
|
||||||
std::vector<uint32_t> 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::vector<uint8_t> translated_binary_;
|
||||||
std::string host_disassembly_;
|
std::string host_disassembly_;
|
||||||
std::string error_log_;
|
std::string host_error_log_;
|
||||||
|
std::vector<uint8_t> host_binary_;
|
||||||
std::unique_ptr<TranslatedShader> translated_shader_;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace gpu
|
} // namespace gpu
|
||||||
|
|
|
@ -79,6 +79,11 @@ int shader_compiler_main(const std::vector<std::wstring>& args) {
|
||||||
shader_type == ShaderType::kVertex ? "vertex" : "pixel",
|
shader_type == ShaderType::kVertex ? "vertex" : "pixel",
|
||||||
ucode_dwords.size(), ucode_dwords.size() * 4);
|
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;
|
std::unique_ptr<ShaderTranslator> translator;
|
||||||
if (FLAGS_shader_output_type == "spirv" ||
|
if (FLAGS_shader_output_type == "spirv" ||
|
||||||
FLAGS_shader_output_type == "spirvtext") {
|
FLAGS_shader_output_type == "spirvtext") {
|
||||||
|
@ -90,13 +95,10 @@ int shader_compiler_main(const std::vector<std::wstring>& args) {
|
||||||
translator = std::make_unique<UcodeShaderTranslator>();
|
translator = std::make_unique<UcodeShaderTranslator>();
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO(benvanik): hash? need to return the data to big-endian format first.
|
translator->Translate(shader.get());
|
||||||
uint64_t ucode_data_hash = 0;
|
|
||||||
auto translated_shader = translator->Translate(
|
|
||||||
shader_type, ucode_data_hash, ucode_dwords.data(), ucode_dwords.size());
|
|
||||||
|
|
||||||
const void* source_data = translated_shader->binary().data();
|
const void* source_data = shader->translated_binary().data();
|
||||||
size_t source_data_size = translated_shader->binary().size();
|
size_t source_data_size = shader->translated_binary().size();
|
||||||
|
|
||||||
std::unique_ptr<xe::ui::spirv::SpirvDisassembler::Result> spirv_disasm_result;
|
std::unique_ptr<xe::ui::spirv::SpirvDisassembler::Result> spirv_disasm_result;
|
||||||
if (FLAGS_shader_output_type == "spirvtext") {
|
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
|
// Lots of naming comes from the disassembly spit out by the XNA GS compiler
|
||||||
// and dumps of d3dcompiler and games: http://pastebin.com/i4kAv7bB
|
// 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;
|
||||||
|
|
||||||
ShaderTranslator::~ShaderTranslator() = default;
|
ShaderTranslator::~ShaderTranslator() = default;
|
||||||
|
@ -87,14 +57,12 @@ void ShaderTranslator::Reset() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
std::unique_ptr<TranslatedShader> ShaderTranslator::Translate(
|
bool ShaderTranslator::Translate(Shader* shader) {
|
||||||
ShaderType shader_type, uint64_t ucode_data_hash,
|
|
||||||
const uint32_t* ucode_dwords, size_t ucode_dword_count) {
|
|
||||||
Reset();
|
Reset();
|
||||||
|
|
||||||
shader_type_ = shader_type;
|
shader_type_ = shader->type();
|
||||||
ucode_dwords_ = ucode_dwords;
|
ucode_dwords_ = shader->ucode_dwords();
|
||||||
ucode_dword_count_ = ucode_dword_count;
|
ucode_dword_count_ = shader->ucode_dword_count();
|
||||||
|
|
||||||
// Run through and gather all binding information.
|
// Run through and gather all binding information.
|
||||||
// Translators may need this before they start codegen.
|
// Translators may need this before they start codegen.
|
||||||
|
@ -119,17 +87,24 @@ std::unique_ptr<TranslatedShader> ShaderTranslator::Translate(
|
||||||
|
|
||||||
TranslateBlocks();
|
TranslateBlocks();
|
||||||
|
|
||||||
std::unique_ptr<TranslatedShader> translated_shader(
|
shader->errors_ = std::move(errors_);
|
||||||
new TranslatedShader(shader_type, ucode_data_hash, ucode_dwords,
|
shader->translated_binary_ = CompleteTranslation();
|
||||||
ucode_dword_count, std::move(errors_)));
|
shader->ucode_disassembly_ = ucode_disasm_buffer_.to_string();
|
||||||
translated_shader->binary_ = CompleteTranslation();
|
shader->vertex_bindings_ = std::move(vertex_bindings_);
|
||||||
translated_shader->ucode_disassembly_ = ucode_disasm_buffer_.to_string();
|
shader->texture_bindings_ = std::move(texture_bindings_);
|
||||||
translated_shader->vertex_bindings_ = std::move(vertex_bindings_);
|
|
||||||
translated_shader->texture_bindings_ = std::move(texture_bindings_);
|
|
||||||
for (size_t i = 0; i < xe::countof(writes_color_targets_); ++i) {
|
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) {
|
void ShaderTranslator::MarkUcodeInstruction(uint32_t dword_offset) {
|
||||||
|
@ -159,7 +134,7 @@ void ShaderTranslator::AppendUcodeDisasmFormat(const char* format, ...) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void ShaderTranslator::EmitTranslationError(const char* message) {
|
void ShaderTranslator::EmitTranslationError(const char* message) {
|
||||||
TranslatedShader::Error error;
|
Shader::Error error;
|
||||||
error.is_fatal = true;
|
error.is_fatal = true;
|
||||||
error.message = message;
|
error.message = message;
|
||||||
// TODO(benvanik): location information.
|
// TODO(benvanik): location information.
|
||||||
|
@ -167,7 +142,7 @@ void ShaderTranslator::EmitTranslationError(const char* message) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void ShaderTranslator::EmitUnimplementedTranslationError() {
|
void ShaderTranslator::EmitUnimplementedTranslationError() {
|
||||||
TranslatedShader::Error error;
|
Shader::Error error;
|
||||||
error.is_fatal = true;
|
error.is_fatal = true;
|
||||||
error.message = "Unimplemented translation";
|
error.message = "Unimplemented translation";
|
||||||
// TODO(benvanik): location information.
|
// TODO(benvanik): location information.
|
||||||
|
@ -195,6 +170,7 @@ void ShaderTranslator::GatherBindingInformation(
|
||||||
auto fetch_opcode =
|
auto fetch_opcode =
|
||||||
static_cast<FetchOpcode>(ucode_dwords_[instr_offset * 3] & 0x1F);
|
static_cast<FetchOpcode>(ucode_dwords_[instr_offset * 3] & 0x1F);
|
||||||
if (fetch_opcode == FetchOpcode::kVertexFetch) {
|
if (fetch_opcode == FetchOpcode::kVertexFetch) {
|
||||||
|
assert_true(is_vertex_shader());
|
||||||
GatherVertexBindingInformation(
|
GatherVertexBindingInformation(
|
||||||
*reinterpret_cast<const VertexFetchInstruction*>(
|
*reinterpret_cast<const VertexFetchInstruction*>(
|
||||||
ucode_dwords_ + instr_offset * 3));
|
ucode_dwords_ + instr_offset * 3));
|
||||||
|
@ -231,7 +207,7 @@ void ShaderTranslator::GatherVertexBindingInformation(
|
||||||
|
|
||||||
// Try to allocate an attribute on an existing binding.
|
// Try to allocate an attribute on an existing binding.
|
||||||
// If no binding for this fetch slot is found create it.
|
// If no binding for this fetch slot is found create it.
|
||||||
using VertexBinding = TranslatedShader::VertexBinding;
|
using VertexBinding = Shader::VertexBinding;
|
||||||
VertexBinding::Attribute* attrib = nullptr;
|
VertexBinding::Attribute* attrib = nullptr;
|
||||||
for (auto& vertex_binding : vertex_bindings_) {
|
for (auto& vertex_binding : vertex_bindings_) {
|
||||||
if (vertex_binding.fetch_constant == op.fetch_constant_index()) {
|
if (vertex_binding.fetch_constant == op.fetch_constant_index()) {
|
||||||
|
@ -244,7 +220,7 @@ void ShaderTranslator::GatherVertexBindingInformation(
|
||||||
}
|
}
|
||||||
if (!attrib) {
|
if (!attrib) {
|
||||||
assert_not_zero(op.stride());
|
assert_not_zero(op.stride());
|
||||||
TranslatedShader::VertexBinding vertex_binding;
|
VertexBinding vertex_binding;
|
||||||
vertex_binding.binding_index = static_cast<int>(vertex_bindings_.size());
|
vertex_binding.binding_index = static_cast<int>(vertex_bindings_.size());
|
||||||
vertex_binding.fetch_constant = op.fetch_constant_index();
|
vertex_binding.fetch_constant = op.fetch_constant_index();
|
||||||
vertex_binding.stride_words = op.stride();
|
vertex_binding.stride_words = op.stride();
|
||||||
|
@ -269,7 +245,7 @@ void ShaderTranslator::GatherTextureBindingInformation(
|
||||||
// Doesn't use bindings.
|
// Doesn't use bindings.
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
TranslatedShader::TextureBinding binding;
|
Shader::TextureBinding binding;
|
||||||
binding.binding_index = texture_bindings_.size();
|
binding.binding_index = texture_bindings_.size();
|
||||||
ParseTextureFetchInstruction(op, &binding.fetch_instr);
|
ParseTextureFetchInstruction(op, &binding.fetch_instr);
|
||||||
binding.fetch_constant = binding.fetch_instr.operands[1].storage_index;
|
binding.fetch_constant = binding.fetch_instr.operands[1].storage_index;
|
||||||
|
|
|
@ -15,537 +15,18 @@
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
#include "xenia/base/string_buffer.h"
|
#include "xenia/base/string_buffer.h"
|
||||||
|
#include "xenia/gpu/shader.h"
|
||||||
#include "xenia/gpu/ucode.h"
|
#include "xenia/gpu/ucode.h"
|
||||||
#include "xenia/gpu/xenos.h"
|
#include "xenia/gpu/xenos.h"
|
||||||
|
|
||||||
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 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 {
|
class ShaderTranslator {
|
||||||
public:
|
public:
|
||||||
virtual ~ShaderTranslator();
|
virtual ~ShaderTranslator();
|
||||||
|
|
||||||
std::unique_ptr<TranslatedShader> Translate(ShaderType shader_type,
|
bool Translate(Shader* shader);
|
||||||
uint64_t ucode_data_hash,
|
|
||||||
const uint32_t* ucode_dwords,
|
|
||||||
size_t ucode_dword_count);
|
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
ShaderTranslator();
|
ShaderTranslator();
|
||||||
|
@ -558,12 +39,11 @@ class ShaderTranslator {
|
||||||
// True if the current shader is a pixel shader.
|
// True if the current shader is a pixel shader.
|
||||||
bool is_pixel_shader() const { return shader_type_ == ShaderType::kPixel; }
|
bool is_pixel_shader() const { return shader_type_ == ShaderType::kPixel; }
|
||||||
// A list of all vertex bindings, populated before translation occurs.
|
// 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_;
|
return vertex_bindings_;
|
||||||
}
|
}
|
||||||
// A list of all texture bindings, populated before translation occurs.
|
// A list of all texture bindings, populated before translation occurs.
|
||||||
const std::vector<TranslatedShader::TextureBinding>& texture_bindings()
|
const std::vector<Shader::TextureBinding>& texture_bindings() const {
|
||||||
const {
|
|
||||||
return texture_bindings_;
|
return texture_bindings_;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -684,7 +164,7 @@ class ShaderTranslator {
|
||||||
size_t ucode_dword_count_;
|
size_t ucode_dword_count_;
|
||||||
|
|
||||||
// Accumulated translation errors.
|
// Accumulated translation errors.
|
||||||
std::vector<TranslatedShader::Error> errors_;
|
std::vector<Shader::Error> errors_;
|
||||||
|
|
||||||
// Microcode disassembly buffer, accumulated throughout the translation.
|
// Microcode disassembly buffer, accumulated throughout the translation.
|
||||||
StringBuffer ucode_disasm_buffer_;
|
StringBuffer ucode_disasm_buffer_;
|
||||||
|
@ -698,8 +178,8 @@ class ShaderTranslator {
|
||||||
|
|
||||||
// Detected binding information gathered before translation.
|
// Detected binding information gathered before translation.
|
||||||
int total_attrib_count_ = 0;
|
int total_attrib_count_ = 0;
|
||||||
std::vector<TranslatedShader::VertexBinding> vertex_bindings_;
|
std::vector<Shader::VertexBinding> vertex_bindings_;
|
||||||
std::vector<TranslatedShader::TextureBinding> texture_bindings_;
|
std::vector<Shader::TextureBinding> texture_bindings_;
|
||||||
bool writes_color_targets_[4] = {false, false, false, false};
|
bool writes_color_targets_[4] = {false, false, false, false};
|
||||||
|
|
||||||
static const AluOpcodeInfo alu_vector_opcode_infos_[0x20];
|
static const AluOpcodeInfo alu_vector_opcode_infos_[0x20];
|
||||||
|
|
|
@ -486,11 +486,11 @@ void TraceViewer::DrawShaderUI(Shader* shader, ShaderDisplayType display_type) {
|
||||||
|
|
||||||
switch (display_type) {
|
switch (display_type) {
|
||||||
case ShaderDisplayType::kUcode: {
|
case ShaderDisplayType::kUcode: {
|
||||||
DrawMultilineString(shader->translated_shader()->ucode_disassembly());
|
DrawMultilineString(shader->ucode_disassembly());
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case ShaderDisplayType::kTranslated: {
|
case ShaderDisplayType::kTranslated: {
|
||||||
const auto& str = shader->translated_shader()->GetBinaryString();
|
const auto& str = shader->GetTranslatedBinaryString();
|
||||||
size_t i = 0;
|
size_t i = 0;
|
||||||
bool done = false;
|
bool done = false;
|
||||||
while (!done && i < str.size()) {
|
while (!done && i < str.size()) {
|
||||||
|
@ -567,7 +567,7 @@ void TraceViewer::DrawBlendMode(uint32_t src_blend, uint32_t dest_blend,
|
||||||
}
|
}
|
||||||
|
|
||||||
void TraceViewer::DrawTextureInfo(
|
void TraceViewer::DrawTextureInfo(
|
||||||
const TranslatedShader::TextureBinding& texture_binding) {
|
const Shader::TextureBinding& texture_binding) {
|
||||||
auto& regs = *graphics_system_->register_file();
|
auto& regs = *graphics_system_->register_file();
|
||||||
|
|
||||||
int r = XE_GPU_REG_SHADER_CONSTANT_FETCH_00_0 +
|
int r = XE_GPU_REG_SHADER_CONSTANT_FETCH_00_0 +
|
||||||
|
@ -633,15 +633,14 @@ void TraceViewer::DrawTextureInfo(
|
||||||
}
|
}
|
||||||
|
|
||||||
void TraceViewer::DrawFailedTextureInfo(
|
void TraceViewer::DrawFailedTextureInfo(
|
||||||
const TranslatedShader::TextureBinding& texture_binding,
|
const Shader::TextureBinding& texture_binding, const char* message) {
|
||||||
const char* message) {
|
|
||||||
// TODO(benvanik): better error info/etc.
|
// TODO(benvanik): better error info/etc.
|
||||||
ImGui::TextColored(kColorError, "ERROR: %s", message);
|
ImGui::TextColored(kColorError, "ERROR: %s", message);
|
||||||
}
|
}
|
||||||
|
|
||||||
void TraceViewer::DrawVertexFetcher(
|
void TraceViewer::DrawVertexFetcher(Shader* shader,
|
||||||
Shader* shader, const TranslatedShader::VertexBinding& vertex_binding,
|
const Shader::VertexBinding& vertex_binding,
|
||||||
const xe_gpu_vertex_fetch_t* fetch) {
|
const xe_gpu_vertex_fetch_t* fetch) {
|
||||||
const uint8_t* addr = memory_->TranslatePhysical(fetch->address << 2);
|
const uint8_t* addr = memory_->TranslatePhysical(fetch->address << 2);
|
||||||
uint32_t vertex_count = (fetch->size * 4) / vertex_binding.stride_words;
|
uint32_t vertex_count = (fetch->size * 4) / vertex_binding.stride_words;
|
||||||
int column_count = 0;
|
int column_count = 0;
|
||||||
|
@ -1411,9 +1410,7 @@ void TraceViewer::DrawStateUI() {
|
||||||
if (ImGui::CollapsingHeader("Vertex Buffers")) {
|
if (ImGui::CollapsingHeader("Vertex Buffers")) {
|
||||||
auto shader = command_processor->active_vertex_shader();
|
auto shader = command_processor->active_vertex_shader();
|
||||||
if (shader) {
|
if (shader) {
|
||||||
const auto& vertex_bindings =
|
for (const auto& vertex_binding : shader->vertex_bindings()) {
|
||||||
shader->translated_shader()->vertex_bindings();
|
|
||||||
for (const auto& vertex_binding : vertex_bindings) {
|
|
||||||
int r = XE_GPU_REG_SHADER_CONSTANT_FETCH_00_0 +
|
int r = XE_GPU_REG_SHADER_CONSTANT_FETCH_00_0 +
|
||||||
(vertex_binding.fetch_constant / 3) * 6;
|
(vertex_binding.fetch_constant / 3) * 6;
|
||||||
const auto group =
|
const auto group =
|
||||||
|
@ -1451,8 +1448,7 @@ void TraceViewer::DrawStateUI() {
|
||||||
if (ImGui::CollapsingHeader("Vertex Textures")) {
|
if (ImGui::CollapsingHeader("Vertex Textures")) {
|
||||||
auto shader = command_processor->active_vertex_shader();
|
auto shader = command_processor->active_vertex_shader();
|
||||||
if (shader) {
|
if (shader) {
|
||||||
const auto& texture_bindings =
|
const auto& texture_bindings = shader->texture_bindings();
|
||||||
shader->translated_shader()->texture_bindings();
|
|
||||||
if (!texture_bindings.empty()) {
|
if (!texture_bindings.empty()) {
|
||||||
for (const auto& texture_binding : texture_bindings) {
|
for (const auto& texture_binding : texture_bindings) {
|
||||||
DrawTextureInfo(texture_binding);
|
DrawTextureInfo(texture_binding);
|
||||||
|
@ -1467,8 +1463,7 @@ void TraceViewer::DrawStateUI() {
|
||||||
if (ImGui::CollapsingHeader("Textures")) {
|
if (ImGui::CollapsingHeader("Textures")) {
|
||||||
auto shader = command_processor->active_pixel_shader();
|
auto shader = command_processor->active_pixel_shader();
|
||||||
if (shader) {
|
if (shader) {
|
||||||
const auto& texture_bindings =
|
const auto& texture_bindings = shader->texture_bindings();
|
||||||
shader->translated_shader()->texture_bindings();
|
|
||||||
if (!texture_bindings.empty()) {
|
if (!texture_bindings.empty()) {
|
||||||
for (const auto& texture_binding : texture_bindings) {
|
for (const auto& texture_binding : texture_bindings) {
|
||||||
DrawTextureInfo(texture_binding);
|
DrawTextureInfo(texture_binding);
|
||||||
|
|
|
@ -85,13 +85,12 @@ class TraceViewer {
|
||||||
void DrawBlendMode(uint32_t src_blend, uint32_t dest_blend,
|
void DrawBlendMode(uint32_t src_blend, uint32_t dest_blend,
|
||||||
uint32_t blend_op);
|
uint32_t blend_op);
|
||||||
|
|
||||||
void DrawTextureInfo(const TranslatedShader::TextureBinding& texture_binding);
|
void DrawTextureInfo(const Shader::TextureBinding& texture_binding);
|
||||||
void DrawFailedTextureInfo(
|
void DrawFailedTextureInfo(const Shader::TextureBinding& texture_binding,
|
||||||
const TranslatedShader::TextureBinding& texture_binding,
|
const char* message);
|
||||||
const char* message);
|
|
||||||
|
|
||||||
void DrawVertexFetcher(Shader* shader,
|
void DrawVertexFetcher(Shader* shader,
|
||||||
const TranslatedShader::VertexBinding& vertex_binding,
|
const Shader::VertexBinding& vertex_binding,
|
||||||
const xenos::xe_gpu_vertex_fetch_t* fetch);
|
const xenos::xe_gpu_vertex_fetch_t* fetch);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -361,25 +361,23 @@ namespace shader_playground {
|
||||||
(shaderCode[6] << 8) | (shaderCode[7] << 0);
|
(shaderCode[6] << 8) | (shaderCode[7] << 0);
|
||||||
int wordOffset = byteOffset / 4;
|
int wordOffset = byteOffset / 4;
|
||||||
|
|
||||||
uint[] swappedCode = new uint[(shaderCode.Length - wordOffset) / sizeof(uint)];
|
uint[] shaderDwords = new uint[(shaderCode.Length - wordOffset) / sizeof(uint)];
|
||||||
Buffer.BlockCopy(shaderCode, wordOffset * 4, swappedCode, 0, shaderCode.Length - wordOffset * 4);
|
Buffer.BlockCopy(shaderCode, wordOffset * 4, shaderDwords, 0, shaderCode.Length - wordOffset * 4);
|
||||||
for (int i = 0; i < swappedCode.Length; ++i) {
|
|
||||||
swappedCode[i] = SwapBytes(swappedCode[i]);
|
|
||||||
}
|
|
||||||
var sb = new StringBuilder();
|
var sb = new StringBuilder();
|
||||||
sb.Append("const uint32_t shader_dwords[] = {");
|
sb.Append("const uint32_t shader_dwords[] = {");
|
||||||
for (int i = 0; i < swappedCode.Length; ++i) {
|
for (int i = 0; i < shaderDwords.Length; ++i) {
|
||||||
sb.AppendFormat("0x{0:X8}, ", swappedCode[i]);
|
sb.AppendFormat("0x{0:X8}, ", SwapByte(shaderDwords[i]));
|
||||||
}
|
}
|
||||||
sb.Append("};" + Environment.NewLine);
|
sb.Append("};" + Environment.NewLine);
|
||||||
sb.Append("shader_type = ShaderType::" + (shaderType == "vs" ? "kVertex" : "kPixel") + ";" + Environment.NewLine);
|
sb.Append("shader_type = ShaderType::" + (shaderType == "vs" ? "kVertex" : "kPixel") + ";" + Environment.NewLine);
|
||||||
UpdateTextBox(wordsTextBox, sb.ToString(), true);
|
UpdateTextBox(wordsTextBox, sb.ToString(), true);
|
||||||
wordsTextBox.SelectAll();
|
wordsTextBox.SelectAll();
|
||||||
|
|
||||||
return swappedCode;
|
return shaderDwords;
|
||||||
}
|
}
|
||||||
|
|
||||||
uint SwapBytes(uint x) {
|
uint SwapByte(uint x) {
|
||||||
return ((x & 0x000000ff) << 24) +
|
return ((x & 0x000000ff) << 24) +
|
||||||
((x & 0x0000ff00) << 8) +
|
((x & 0x0000ff00) << 8) +
|
||||||
((x & 0x00ff0000) >> 8) +
|
((x & 0x00ff0000) >> 8) +
|
||||||
|
|
Loading…
Reference in New Issue