diff --git a/src/xenia/gpu/shader_compiler_main.cc b/src/xenia/gpu/shader_compiler_main.cc index b8949f948..a342ecfdf 100644 --- a/src/xenia/gpu/shader_compiler_main.cc +++ b/src/xenia/gpu/shader_compiler_main.cc @@ -9,9 +9,12 @@ #include #include +#include #include +#include #include +#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& args) { shader_type, ucode_data_hash, ucode_dwords.data(), ucode_dwords.size()); std::unique_ptr 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(); + } else if (cvars::shader_output_type == "dxbc" || + cvars::shader_output_type == "dxbctext") { translator = std::make_unique( 0, cvars::shader_output_bindless_resources, cvars::shader_output_dxbc_rov); @@ -140,6 +148,20 @@ int shader_compiler_main(const std::vector& 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 spirv_source; + spirv_source.reserve(source_data_size / sizeof(unsigned int)); + spirv_source.insert(spirv_source.cend(), + reinterpret_cast(source_data), + reinterpret_cast(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") { diff --git a/src/xenia/gpu/spirv_shader_translator.cc b/src/xenia/gpu/spirv_shader_translator.cc new file mode 100644 index 000000000..ce232a7ad --- /dev/null +++ b/src/xenia/gpu/spirv_shader_translator.cc @@ -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 +#include + +#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(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 main_param_types; + std::vector> 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 SpirvShaderTranslator::CompleteTranslation() { + if (IsSpirvVertexOrTessEvalShader()) { + CompleteVertexOrTessEvalShaderInMain(); + } + + // End the main function.. + builder_->leaveFunction(); + + // TODO(Triang3l): Avoid copy? + std::vector module_uints; + builder_->dump(module_uints); + std::vector module_bytes; + module_bytes.reserve(sizeof(unsigned int) * module_uints.size()); + module_bytes.insert(module_bytes.cend(), + reinterpret_cast(module_uints.data()), + reinterpret_cast(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 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 diff --git a/src/xenia/gpu/spirv_shader_translator.h b/src/xenia/gpu/spirv_shader_translator.h new file mode 100644 index 000000000..5ef5dfc2c --- /dev/null +++ b/src/xenia/gpu/spirv_shader_translator.h @@ -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 +#include +#include + +#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 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 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_