[SPIR-V] Some initial bits of the translator

This commit is contained in:
Triang3l 2020-10-11 20:22:15 +03:00
parent 3a308dedb3
commit b3339d7e46
3 changed files with 261 additions and 3 deletions

View File

@ -9,9 +9,12 @@
#include <cinttypes>
#include <cstring>
#include <sstream>
#include <string>
#include <utility>
#include <vector>
#include "third_party/glslang/SPIRV/disassemble.h"
#include "xenia/base/cvar.h"
#include "xenia/base/logging.h"
#include "xenia/base/main.h"
@ -19,6 +22,7 @@
#include "xenia/base/string.h"
#include "xenia/gpu/dxbc_shader_translator.h"
#include "xenia/gpu/shader_translator.h"
#include "xenia/gpu/spirv_shader_translator.h"
// For D3DDisassemble:
#if XE_PLATFORM_WIN32
@ -31,7 +35,8 @@ DEFINE_string(shader_input_type, "",
"GPU");
DEFINE_path(shader_output, "", "Output shader file path.", "GPU");
DEFINE_string(shader_output_type, "ucode",
"Translator to use: [ucode, dxbc, dxbctext].", "GPU");
"Translator to use: [ucode, spirv, spirvtext, dxbc, dxbctext].",
"GPU");
DEFINE_string(
vertex_shader_output_type, "",
"Type of the host interface to produce the vertex or domain shader for: "
@ -102,8 +107,11 @@ int shader_compiler_main(const std::vector<std::string>& args) {
shader_type, ucode_data_hash, ucode_dwords.data(), ucode_dwords.size());
std::unique_ptr<ShaderTranslator> translator;
if (cvars::shader_output_type == "dxbc" ||
cvars::shader_output_type == "dxbctext") {
if (cvars::shader_output_type == "spirv" ||
cvars::shader_output_type == "spirvtext") {
translator = std::make_unique<SpirvShaderTranslator>();
} else if (cvars::shader_output_type == "dxbc" ||
cvars::shader_output_type == "dxbctext") {
translator = std::make_unique<DxbcShaderTranslator>(
0, cvars::shader_output_bindless_resources,
cvars::shader_output_dxbc_rov);
@ -140,6 +148,20 @@ int shader_compiler_main(const std::vector<std::string>& args) {
const void* source_data = shader->translated_binary().data();
size_t source_data_size = shader->translated_binary().size();
std::string spirv_disasm;
if (cvars::shader_output_type == "spirvtext") {
std::ostringstream spirv_disasm_stream;
std::vector<unsigned int> spirv_source;
spirv_source.reserve(source_data_size / sizeof(unsigned int));
spirv_source.insert(spirv_source.cend(),
reinterpret_cast<const unsigned int*>(source_data),
reinterpret_cast<const unsigned int*>(source_data) +
source_data_size / sizeof(unsigned int));
spv::Disassemble(spirv_disasm_stream, spirv_source);
spirv_disasm = std::move(spirv_disasm_stream.str());
source_data = spirv_disasm.c_str();
source_data_size = spirv_disasm.size();
}
#if XE_PLATFORM_WIN32
ID3DBlob* dxbc_disasm_blob = nullptr;
if (cvars::shader_output_type == "dxbctext") {

View File

@ -0,0 +1,151 @@
/**
******************************************************************************
* Xenia : Xbox 360 Emulator Research Project *
******************************************************************************
* Copyright 2020 Ben Vanik. All rights reserved. *
* Released under the BSD license - see LICENSE in the root for more details. *
******************************************************************************
*/
#include "xenia/gpu/spirv_shader_translator.h"
#include <memory>
#include <vector>
#include "third_party/glslang/SPIRV/GLSL.std.450.h"
namespace xe {
namespace gpu {
SpirvShaderTranslator::SpirvShaderTranslator(bool supports_clip_distance,
bool supports_cull_distance)
: supports_clip_distance_(supports_clip_distance),
supports_cull_distance_(supports_cull_distance) {}
void SpirvShaderTranslator::Reset() {
ShaderTranslator::Reset();
builder_.reset();
}
void SpirvShaderTranslator::StartTranslation() {
// TODO(Triang3l): Once tool ID (likely 26) is registered in SPIRV-Headers,
// use it instead.
// TODO(Triang3l): Logger.
builder_ = std::make_unique<spv::Builder>(0x10000, 0xFFFF0001, nullptr);
builder_->addCapability(IsSpirvTessEvalShader() ? spv::CapabilityTessellation
: spv::CapabilityShader);
ext_inst_glsl_std_450_ = builder_->import("GLSL.std.450");
builder_->setMemoryModel(spv::AddressingModelLogical,
spv::MemoryModelGLSL450);
builder_->setSource(spv::SourceLanguageUnknown, 0);
type_void_ = builder_->makeVoidType();
type_float_ = builder_->makeFloatType(32);
type_float2_ = builder_->makeVectorType(type_float_, 2);
type_float3_ = builder_->makeVectorType(type_float_, 3);
type_float4_ = builder_->makeVectorType(type_float_, 4);
type_int_ = builder_->makeIntType(32);
if (IsSpirvVertexOrTessEvalShader()) {
StartVertexOrTessEvalShaderBeforeMain();
}
// Begin the main function.
std::vector<spv::Id> main_param_types;
std::vector<std::vector<spv::Decoration>> main_precisions;
spv::Block* main_entry;
builder_->makeFunctionEntry(spv::NoPrecision, type_void_, "main",
main_param_types, main_precisions, &main_entry);
// Begin ucode translation.
if (register_count()) {
var_main_registers_ = builder_->createVariable(
spv::NoPrecision, spv::StorageClassFunction,
builder_->makeArrayType(
type_float4_, builder_->makeUintConstant(register_count()), 0),
"xe_r");
}
}
std::vector<uint8_t> SpirvShaderTranslator::CompleteTranslation() {
if (IsSpirvVertexOrTessEvalShader()) {
CompleteVertexOrTessEvalShaderInMain();
}
// End the main function..
builder_->leaveFunction();
// TODO(Triang3l): Avoid copy?
std::vector<unsigned int> module_uints;
builder_->dump(module_uints);
std::vector<uint8_t> module_bytes;
module_bytes.reserve(sizeof(unsigned int) * module_uints.size());
module_bytes.insert(module_bytes.cend(),
reinterpret_cast<const uint8_t*>(module_uints.data()),
reinterpret_cast<const uint8_t*>(module_uints.data()) +
sizeof(unsigned int) * module_uints.size());
return module_bytes;
}
void SpirvShaderTranslator::StartVertexOrTessEvalShaderBeforeMain() {
// Create the inputs.
if (IsSpirvTessEvalShader()) {
input_vertex_index_ = builder_->createVariable(
spv::NoPrecision, spv::StorageClassInput, type_int_, "gl_PrimitiveID");
builder_->addDecoration(input_vertex_index_, spv::DecorationBuiltIn,
spv::BuiltInPrimitiveId);
} else {
input_primitive_id_ = builder_->createVariable(
spv::NoPrecision, spv::StorageClassInput, type_int_, "gl_VertexIndex");
builder_->addDecoration(input_primitive_id_, spv::DecorationBuiltIn,
spv::BuiltInVertexIndex);
}
// Create the entire GLSL 4.50 gl_PerVertex output similar to what glslang
// does. Members (like gl_PointSize) don't need to be used, and also
// ClipDistance and CullDistance may exist even if the device doesn't support
// them, as long as the capabilities aren't enabled, and nothing is stored to
// them.
if (supports_clip_distance_) {
builder_->addCapability(spv::CapabilityClipDistance);
}
if (supports_cull_distance_) {
builder_->addCapability(spv::CapabilityCullDistance);
}
std::vector<spv::Id> struct_per_vertex_members;
struct_per_vertex_members.reserve(kOutputPerVertexMemberCount);
struct_per_vertex_members.push_back(type_float4_);
struct_per_vertex_members.push_back(type_float_);
// TODO(Triang3l): Specialization constant for ucp_cull_only_ena, for 6 + 1
// or 1 + 7 array sizes.
struct_per_vertex_members.push_back(builder_->makeArrayType(
type_float_, builder_->makeUintConstant(supports_clip_distance_ ? 6 : 1),
0));
struct_per_vertex_members.push_back(
builder_->makeArrayType(type_float_, builder_->makeUintConstant(1), 0));
spv::Id type_struct_per_vertex =
builder_->makeStructType(struct_per_vertex_members, "gl_PerVertex");
builder_->addMemberDecoration(type_struct_per_vertex,
kOutputPerVertexMemberPosition,
spv::DecorationBuiltIn, spv::BuiltInPosition);
builder_->addMemberDecoration(type_struct_per_vertex,
kOutputPerVertexMemberPointSize,
spv::DecorationBuiltIn, spv::BuiltInPointSize);
builder_->addMemberDecoration(
type_struct_per_vertex, kOutputPerVertexMemberClipDistance,
spv::DecorationBuiltIn, spv::BuiltInClipDistance);
builder_->addMemberDecoration(
type_struct_per_vertex, kOutputPerVertexMemberCullDistance,
spv::DecorationBuiltIn, spv::BuiltInCullDistance);
builder_->addDecoration(type_struct_per_vertex, spv::DecorationBlock);
output_per_vertex_ =
builder_->createVariable(spv::NoPrecision, spv::StorageClassOutput,
type_struct_per_vertex, "xe_out_gl_PerVertex");
}
void SpirvShaderTranslator::CompleteVertexOrTessEvalShaderInMain() {}
} // namespace gpu
} // namespace xe

View File

@ -0,0 +1,85 @@
/**
******************************************************************************
* Xenia : Xbox 360 Emulator Research Project *
******************************************************************************
* Copyright 2020 Ben Vanik. All rights reserved. *
* Released under the BSD license - see LICENSE in the root for more details. *
******************************************************************************
*/
#ifndef XENIA_GPU_SPIRV_SHADER_TRANSLATOR_H_
#define XENIA_GPU_SPIRV_SHADER_TRANSLATOR_H_
#include <cstdint>
#include <memory>
#include <vector>
#include "third_party/glslang/SPIRV/SpvBuilder.h"
#include "xenia/gpu/shader_translator.h"
namespace xe {
namespace gpu {
class SpirvShaderTranslator : public ShaderTranslator {
public:
SpirvShaderTranslator(bool supports_clip_distance = true,
bool supports_cull_distance = true);
protected:
void Reset() override;
void StartTranslation() override;
std::vector<uint8_t> CompleteTranslation() override;
private:
// TODO(Triang3l): Depth-only pixel shader.
bool IsSpirvVertexOrTessEvalShader() const { return is_vertex_shader(); }
bool IsSpirvVertexShader() const {
return IsSpirvVertexOrTessEvalShader() &&
host_vertex_shader_type() == Shader::HostVertexShaderType::kVertex;
}
bool IsSpirvTessEvalShader() const {
return IsSpirvVertexOrTessEvalShader() &&
host_vertex_shader_type() != Shader::HostVertexShaderType::kVertex;
}
bool IsSpirvFragmentShader() const { return is_pixel_shader(); }
void StartVertexOrTessEvalShaderBeforeMain();
void CompleteVertexOrTessEvalShaderInMain();
bool supports_clip_distance_;
bool supports_cull_distance_;
std::unique_ptr<spv::Builder> builder_;
spv::Id ext_inst_glsl_std_450_;
spv::Id type_void_;
spv::Id type_float_;
spv::Id type_float2_;
spv::Id type_float3_;
spv::Id type_float4_;
spv::Id type_int_;
spv::Id type_uint_;
spv::Id input_vertex_index_;
spv::Id input_primitive_id_;
enum OutputPerVertexMember : unsigned int {
kOutputPerVertexMemberPosition,
kOutputPerVertexMemberPointSize,
kOutputPerVertexMemberClipDistance,
kOutputPerVertexMemberCullDistance,
kOutputPerVertexMemberCount,
};
spv::Id output_per_vertex_;
spv::Id function_main_;
spv::Id var_main_registers_;
};
} // namespace gpu
} // namespace xe
#endif // XENIA_GPU_SPIRV_SHADER_TRANSLATOR_H_