[SPIR-V] Main loop blocks, validation
This commit is contained in:
parent
ae7d5a1b05
commit
1de144938c
|
@ -57,5 +57,7 @@
|
|||
url = https://github.com/microsoft/DirectXShaderCompiler.git
|
||||
[submodule "third_party/glslang"]
|
||||
path = third_party/glslang
|
||||
url = https://github.com/Triang3l/glslang.git
|
||||
branch = patch-1
|
||||
url = https://github.com/KhronosGroup/glslang.git
|
||||
[submodule "third_party/SPIRV-Tools"]
|
||||
path = third_party/SPIRV-Tools
|
||||
url = https://github.com/KhronosGroup/SPIRV-Tools.git
|
||||
|
|
|
@ -30,6 +30,7 @@ project("xenia-gpu-shader-compiler")
|
|||
"glslang-spirv",
|
||||
"xenia-base",
|
||||
"xenia-gpu",
|
||||
"xenia-ui-vulkan",
|
||||
})
|
||||
defines({
|
||||
})
|
||||
|
|
|
@ -23,6 +23,7 @@
|
|||
#include "xenia/gpu/dxbc_shader_translator.h"
|
||||
#include "xenia/gpu/shader_translator.h"
|
||||
#include "xenia/gpu/spirv_shader_translator.h"
|
||||
#include "xenia/ui/vulkan/spirv_tools_context.h"
|
||||
|
||||
// For D3DDisassemble:
|
||||
#if XE_PLATFORM_WIN32
|
||||
|
@ -159,6 +160,17 @@ int shader_compiler_main(const std::vector<std::string>& args) {
|
|||
source_data_size / sizeof(unsigned int));
|
||||
spv::Disassemble(spirv_disasm_stream, spirv_source);
|
||||
spirv_disasm = std::move(spirv_disasm_stream.str());
|
||||
ui::vulkan::SpirvToolsContext spirv_tools_context;
|
||||
if (spirv_tools_context.Initialize()) {
|
||||
std::string spirv_validation_error;
|
||||
spirv_tools_context.Validate(
|
||||
reinterpret_cast<const uint32_t*>(spirv_source.data()),
|
||||
spirv_source.size(), &spirv_validation_error);
|
||||
if (!spirv_validation_error.empty()) {
|
||||
spirv_disasm.append(1, '\n');
|
||||
spirv_disasm.append(spirv_validation_error);
|
||||
}
|
||||
}
|
||||
source_data = spirv_disasm.c_str();
|
||||
source_data_size = spirv_disasm.size();
|
||||
}
|
||||
|
|
|
@ -101,7 +101,6 @@ bool ShaderTranslator::TranslateInternal(
|
|||
// Each control flow instruction is executed sequentially until the final
|
||||
// ending instruction.
|
||||
uint32_t max_cf_dword_index = static_cast<uint32_t>(ucode_dword_count_);
|
||||
std::vector<ControlFlowInstruction> cf_instructions;
|
||||
for (uint32_t i = 0; i < max_cf_dword_index; i += 3) {
|
||||
ControlFlowInstruction cf_a;
|
||||
ControlFlowInstruction cf_b;
|
||||
|
@ -121,8 +120,6 @@ bool ShaderTranslator::TranslateInternal(
|
|||
// Translators may need this before they start codegen.
|
||||
GatherInstructionInformation(cf_a);
|
||||
GatherInstructionInformation(cf_b);
|
||||
cf_instructions.push_back(cf_a);
|
||||
cf_instructions.push_back(cf_b);
|
||||
}
|
||||
|
||||
if (constant_register_map_.float_dynamic_addressing) {
|
||||
|
@ -159,8 +156,6 @@ bool ShaderTranslator::TranslateInternal(
|
|||
|
||||
StartTranslation();
|
||||
|
||||
PreProcessControlFlowInstructions(cf_instructions);
|
||||
|
||||
// Translate all instructions.
|
||||
for (uint32_t i = 0, cf_index = 0; i < max_cf_dword_index; i += 3) {
|
||||
ControlFlowInstruction cf_a;
|
||||
|
|
|
@ -136,10 +136,6 @@ class ShaderTranslator {
|
|||
shader->host_disassembly_ = std::move(value);
|
||||
}
|
||||
|
||||
// Pre-process a control-flow instruction before anything else.
|
||||
virtual void PreProcessControlFlowInstructions(
|
||||
std::vector<ucode::ControlFlowInstruction> instrs) {}
|
||||
|
||||
// Handles translation for control flow label addresses.
|
||||
// This is triggered once for each label required (due to control flow
|
||||
// operations) before any of the instructions within the target exec.
|
||||
|
|
|
@ -10,9 +10,11 @@
|
|||
#include "xenia/gpu/spirv_shader_translator.h"
|
||||
|
||||
#include <memory>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#include "third_party/glslang/SPIRV/GLSL.std.450.h"
|
||||
#include "xenia/base/assert.h"
|
||||
|
||||
namespace xe {
|
||||
namespace gpu {
|
||||
|
@ -26,13 +28,15 @@ void SpirvShaderTranslator::Reset() {
|
|||
ShaderTranslator::Reset();
|
||||
|
||||
builder_.reset();
|
||||
|
||||
// main_switch_cases_.reset();
|
||||
}
|
||||
|
||||
void SpirvShaderTranslator::StartTranslation() {
|
||||
// TODO(Triang3l): Once tool ID (likely 26) is registered in SPIRV-Headers,
|
||||
// use it instead.
|
||||
// Tool ID 26 "Xenia Emulator Microcode Translator".
|
||||
// https://github.com/KhronosGroup/SPIRV-Headers/blob/c43a43c7cc3af55910b9bec2a71e3e8a622443cf/include/spirv/spir-v.xml#L79
|
||||
// TODO(Triang3l): Logger.
|
||||
builder_ = std::make_unique<spv::Builder>(0x10000, 0xFFFF0001, nullptr);
|
||||
builder_ = std::make_unique<spv::Builder>(1 << 16, (26 << 16) | 1, nullptr);
|
||||
|
||||
builder_->addCapability(IsSpirvTessEvalShader() ? spv::CapabilityTessellation
|
||||
: spv::CapabilityShader);
|
||||
|
@ -42,11 +46,29 @@ void SpirvShaderTranslator::StartTranslation() {
|
|||
builder_->setSource(spv::SourceLanguageUnknown, 0);
|
||||
|
||||
type_void_ = builder_->makeVoidType();
|
||||
type_bool_ = builder_->makeBoolType();
|
||||
type_int_ = builder_->makeIntType(32);
|
||||
type_int4_ = builder_->makeVectorType(type_int_, 4);
|
||||
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);
|
||||
|
||||
const_int_0_ = builder_->makeIntConstant(0);
|
||||
id_vector_temp_.clear();
|
||||
id_vector_temp_.reserve(4);
|
||||
for (uint32_t i = 0; i < 4; ++i) {
|
||||
id_vector_temp_.push_back(const_int_0_);
|
||||
}
|
||||
const_int4_0_ = builder_->makeCompositeConstant(type_int4_, id_vector_temp_);
|
||||
const_float_0_ = builder_->makeFloatConstant(0.0f);
|
||||
id_vector_temp_.clear();
|
||||
id_vector_temp_.reserve(4);
|
||||
for (uint32_t i = 0; i < 4; ++i) {
|
||||
id_vector_temp_.push_back(const_float_0_);
|
||||
}
|
||||
const_float4_0_ =
|
||||
builder_->makeCompositeConstant(type_float4_, id_vector_temp_);
|
||||
|
||||
if (IsSpirvVertexOrTessEvalShader()) {
|
||||
StartVertexOrTessEvalShaderBeforeMain();
|
||||
|
@ -55,28 +77,131 @@ void SpirvShaderTranslator::StartTranslation() {
|
|||
// 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);
|
||||
spv::Block* function_main_entry;
|
||||
function_main_ = builder_->makeFunctionEntry(
|
||||
spv::NoPrecision, type_void_, "main", main_param_types, main_precisions,
|
||||
&function_main_entry);
|
||||
|
||||
// Begin ucode translation.
|
||||
if (register_count()) {
|
||||
// Begin ucode translation. Initialize everything, even without defined
|
||||
// defaults, for safety.
|
||||
var_main_predicate_ = builder_->createVariable(
|
||||
spv::NoPrecision, spv::StorageClassFunction, type_bool_,
|
||||
"xe_var_predicate", builder_->makeBoolConstant(false));
|
||||
var_main_address_absolute_ = builder_->createVariable(
|
||||
spv::NoPrecision, spv::StorageClassFunction, type_int_,
|
||||
"xe_var_address_absolute", const_int_0_);
|
||||
var_main_address_relative_ = builder_->createVariable(
|
||||
spv::NoPrecision, spv::StorageClassFunction, type_int4_,
|
||||
"xe_var_address_relative", const_int4_0_);
|
||||
uint32_t register_array_size = register_count();
|
||||
if (register_array_size) {
|
||||
id_vector_temp_.clear();
|
||||
id_vector_temp_.reserve(register_array_size);
|
||||
// TODO(Triang3l): In PS, only initialize starting from the interpolators,
|
||||
// probably manually. But not very important.
|
||||
for (uint32_t i = 0; i < register_array_size; ++i) {
|
||||
id_vector_temp_.push_back(const_float4_0_);
|
||||
}
|
||||
spv::Id type_register_array = builder_->makeArrayType(
|
||||
type_float4_, builder_->makeUintConstant(register_array_size), 0);
|
||||
var_main_registers_ = builder_->createVariable(
|
||||
spv::NoPrecision, spv::StorageClassFunction,
|
||||
builder_->makeArrayType(
|
||||
type_float4_, builder_->makeUintConstant(register_count()), 0),
|
||||
"xe_r");
|
||||
spv::NoPrecision, spv::StorageClassFunction, type_register_array,
|
||||
"xe_var_registers",
|
||||
builder_->makeCompositeConstant(type_register_array, id_vector_temp_));
|
||||
}
|
||||
|
||||
// Write the execution model-specific prologue with access to variables in the
|
||||
// main function.
|
||||
if (IsSpirvVertexOrTessEvalShader()) {
|
||||
StartVertexOrTessEvalShaderInMain();
|
||||
}
|
||||
|
||||
// Open the main loop.
|
||||
|
||||
spv::Block* main_loop_pre_header = builder_->getBuildPoint();
|
||||
main_loop_header_ = &builder_->makeNewBlock();
|
||||
spv::Block& main_loop_body = builder_->makeNewBlock();
|
||||
// Added later because the body has nested control flow, but according to the
|
||||
// specification:
|
||||
// "The order of blocks in a function must satisfy the rule that blocks appear
|
||||
// before all blocks they dominate."
|
||||
main_loop_continue_ =
|
||||
new spv::Block(builder_->getUniqueId(), *function_main_);
|
||||
main_loop_merge_ = new spv::Block(builder_->getUniqueId(), *function_main_);
|
||||
builder_->createBranch(main_loop_header_);
|
||||
|
||||
// Main loop header - based on whether it's the first iteration (entered from
|
||||
// the function or from the continuation), choose the program counter.
|
||||
builder_->setBuildPoint(main_loop_header_);
|
||||
id_vector_temp_.clear();
|
||||
id_vector_temp_.reserve(4);
|
||||
id_vector_temp_.push_back(const_int_0_);
|
||||
id_vector_temp_.push_back(main_loop_pre_header->getId());
|
||||
main_loop_pc_next_ = builder_->getUniqueId();
|
||||
id_vector_temp_.push_back(main_loop_pc_next_);
|
||||
id_vector_temp_.push_back(main_loop_continue_->getId());
|
||||
spv::Id main_loop_pc_current =
|
||||
builder_->createOp(spv::OpPhi, type_int_, id_vector_temp_);
|
||||
uint_vector_temp_.clear();
|
||||
builder_->createLoopMerge(main_loop_merge_, main_loop_continue_,
|
||||
spv::LoopControlDontUnrollMask, uint_vector_temp_);
|
||||
builder_->createBranch(&main_loop_body);
|
||||
|
||||
// Main loop body.
|
||||
builder_->setBuildPoint(&main_loop_body);
|
||||
// TODO(Triang3l): Create the switch, add the block for the case 0 and set the
|
||||
// build point to it.
|
||||
}
|
||||
|
||||
std::vector<uint8_t> SpirvShaderTranslator::CompleteTranslation() {
|
||||
// Close the main loop.
|
||||
// Break from the body after falling through the end or breaking.
|
||||
builder_->createBranch(main_loop_merge_);
|
||||
// Main loop continuation - choose the program counter based on the path
|
||||
// taken (-1 if not from a jump as a safe fallback, which would result in not
|
||||
// hitting any switch case and reaching the final break in the body).
|
||||
function_main_->addBlock(main_loop_continue_);
|
||||
builder_->setBuildPoint(main_loop_continue_);
|
||||
{
|
||||
std::unique_ptr<spv::Instruction> main_loop_pc_next_op =
|
||||
std::make_unique<spv::Instruction>(main_loop_pc_next_, type_int_,
|
||||
spv::OpCopyObject);
|
||||
// TODO(Triang3l): Phi between the continues in the switch cases and the
|
||||
// switch merge block.
|
||||
main_loop_pc_next_op->addIdOperand(builder_->makeIntConstant(-1));
|
||||
builder_->getBuildPoint()->addInstruction(std::move(main_loop_pc_next_op));
|
||||
}
|
||||
builder_->createBranch(main_loop_header_);
|
||||
// Add the main loop merge block and go back to the function.
|
||||
function_main_->addBlock(main_loop_merge_);
|
||||
builder_->setBuildPoint(main_loop_merge_);
|
||||
|
||||
if (IsSpirvVertexOrTessEvalShader()) {
|
||||
CompleteVertexOrTessEvalShaderInMain();
|
||||
}
|
||||
|
||||
// End the main function..
|
||||
// End the main function.
|
||||
builder_->leaveFunction();
|
||||
|
||||
// Make the main function the entry point.
|
||||
spv::ExecutionModel execution_model;
|
||||
if (IsSpirvFragmentShader()) {
|
||||
execution_model = spv::ExecutionModelFragment;
|
||||
builder_->addExecutionMode(function_main_,
|
||||
spv::ExecutionModeOriginUpperLeft);
|
||||
} else {
|
||||
assert_true(IsSpirvVertexOrTessEvalShader());
|
||||
execution_model = IsSpirvTessEvalShader()
|
||||
? spv::ExecutionModelTessellationEvaluation
|
||||
: spv::ExecutionModelVertex;
|
||||
}
|
||||
spv::Instruction* entry_point =
|
||||
builder_->addEntryPoint(execution_model, function_main_, "main");
|
||||
|
||||
if (IsSpirvVertexOrTessEvalShader()) {
|
||||
CompleteVertexOrTessEvalShaderAfterMain(entry_point);
|
||||
}
|
||||
|
||||
// TODO(Triang3l): Avoid copy?
|
||||
std::vector<unsigned int> module_uints;
|
||||
builder_->dump(module_uints);
|
||||
|
@ -92,14 +217,14 @@ std::vector<uint8_t> SpirvShaderTranslator::CompleteTranslation() {
|
|||
void SpirvShaderTranslator::StartVertexOrTessEvalShaderBeforeMain() {
|
||||
// Create the inputs.
|
||||
if (IsSpirvTessEvalShader()) {
|
||||
input_vertex_index_ = builder_->createVariable(
|
||||
input_primitive_id_ = builder_->createVariable(
|
||||
spv::NoPrecision, spv::StorageClassInput, type_int_, "gl_PrimitiveID");
|
||||
builder_->addDecoration(input_vertex_index_, spv::DecorationBuiltIn,
|
||||
builder_->addDecoration(input_primitive_id_, spv::DecorationBuiltIn,
|
||||
spv::BuiltInPrimitiveId);
|
||||
} else {
|
||||
input_primitive_id_ = builder_->createVariable(
|
||||
input_vertex_index_ = builder_->createVariable(
|
||||
spv::NoPrecision, spv::StorageClassInput, type_int_, "gl_VertexIndex");
|
||||
builder_->addDecoration(input_primitive_id_, spv::DecorationBuiltIn,
|
||||
builder_->addDecoration(input_vertex_index_, spv::DecorationBuiltIn,
|
||||
spv::BuiltInVertexIndex);
|
||||
}
|
||||
|
||||
|
@ -145,7 +270,23 @@ void SpirvShaderTranslator::StartVertexOrTessEvalShaderBeforeMain() {
|
|||
type_struct_per_vertex, "xe_out_gl_PerVertex");
|
||||
}
|
||||
|
||||
void SpirvShaderTranslator::StartVertexOrTessEvalShaderInMain() {
|
||||
var_main_point_size_edge_flag_kill_vertex_ = builder_->createVariable(
|
||||
spv::NoPrecision, spv::StorageClassFunction, type_float3_,
|
||||
"xe_var_point_size_edge_flag_kill_vertex");
|
||||
}
|
||||
|
||||
void SpirvShaderTranslator::CompleteVertexOrTessEvalShaderInMain() {}
|
||||
|
||||
void SpirvShaderTranslator::CompleteVertexOrTessEvalShaderAfterMain(
|
||||
spv::Instruction* entry_point) {
|
||||
if (IsSpirvTessEvalShader()) {
|
||||
entry_point->addIdOperand(input_primitive_id_);
|
||||
} else {
|
||||
entry_point->addIdOperand(input_vertex_index_);
|
||||
}
|
||||
entry_point->addIdOperand(output_per_vertex_);
|
||||
}
|
||||
|
||||
} // namespace gpu
|
||||
} // namespace xe
|
||||
|
|
|
@ -46,24 +46,38 @@ class SpirvShaderTranslator : public ShaderTranslator {
|
|||
bool IsSpirvFragmentShader() const { return is_pixel_shader(); }
|
||||
|
||||
void StartVertexOrTessEvalShaderBeforeMain();
|
||||
void StartVertexOrTessEvalShaderInMain();
|
||||
void CompleteVertexOrTessEvalShaderInMain();
|
||||
void CompleteVertexOrTessEvalShaderAfterMain(spv::Instruction* entry_point);
|
||||
|
||||
bool supports_clip_distance_;
|
||||
bool supports_cull_distance_;
|
||||
|
||||
std::unique_ptr<spv::Builder> builder_;
|
||||
|
||||
std::vector<spv::Id> id_vector_temp_;
|
||||
std::vector<unsigned int> uint_vector_temp_;
|
||||
|
||||
spv::Id ext_inst_glsl_std_450_;
|
||||
|
||||
spv::Id type_void_;
|
||||
spv::Id type_bool_;
|
||||
spv::Id type_int_;
|
||||
spv::Id type_int4_;
|
||||
spv::Id type_uint_;
|
||||
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 const_int_0_;
|
||||
spv::Id const_int4_0_;
|
||||
spv::Id const_float_0_;
|
||||
spv::Id const_float4_0_;
|
||||
|
||||
// VS as VS only - int.
|
||||
spv::Id input_vertex_index_;
|
||||
// VS as TES only - int.
|
||||
spv::Id input_primitive_id_;
|
||||
|
||||
enum OutputPerVertexMember : unsigned int {
|
||||
|
@ -75,8 +89,21 @@ class SpirvShaderTranslator : public ShaderTranslator {
|
|||
};
|
||||
spv::Id output_per_vertex_;
|
||||
|
||||
spv::Id function_main_;
|
||||
spv::Function* function_main_;
|
||||
// bool.
|
||||
spv::Id var_main_predicate_;
|
||||
// int4.
|
||||
spv::Id var_main_address_relative_;
|
||||
// int.
|
||||
spv::Id var_main_address_absolute_;
|
||||
// float4[register_count()].
|
||||
spv::Id var_main_registers_;
|
||||
// VS only - float3 (special exports).
|
||||
spv::Id var_main_point_size_edge_flag_kill_vertex_;
|
||||
spv::Block* main_loop_header_;
|
||||
spv::Block* main_loop_continue_;
|
||||
spv::Block* main_loop_merge_;
|
||||
spv::Id main_loop_pc_next_;
|
||||
};
|
||||
|
||||
} // namespace gpu
|
||||
|
|
|
@ -0,0 +1,113 @@
|
|||
/**
|
||||
******************************************************************************
|
||||
* 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/ui/vulkan/spirv_tools_context.h"
|
||||
|
||||
#include <cstdlib>
|
||||
#include <filesystem>
|
||||
#include <string>
|
||||
|
||||
#include "xenia/base/logging.h"
|
||||
#include "xenia/base/platform.h"
|
||||
|
||||
#if XE_PLATFORM_LINUX
|
||||
#include <dlfcn.h>
|
||||
#elif XE_PLATFORM_WIN32
|
||||
#include "xenia/base/platform_win.h"
|
||||
#endif
|
||||
|
||||
namespace xe {
|
||||
namespace ui {
|
||||
namespace vulkan {
|
||||
|
||||
bool SpirvToolsContext::Initialize() {
|
||||
const char* vulkan_sdk_env = std::getenv("VULKAN_SDK");
|
||||
if (!vulkan_sdk_env) {
|
||||
XELOGE("SPIRV-Tools: Failed to get the VULKAN_SDK environment variable");
|
||||
Shutdown();
|
||||
return false;
|
||||
}
|
||||
std::filesystem::path vulkan_sdk_path(vulkan_sdk_env);
|
||||
#if XE_PLATFORM_LINUX
|
||||
library_ = dlopen((vulkan_sdk_path / "bin/libSPIRV-Tools-shared.so").c_str(),
|
||||
RTLD_NOW | RTLD_LOCAL);
|
||||
if (!library_) {
|
||||
XELOGE(
|
||||
"SPIRV-Tools: Failed to load $VULKAN_SDK/bin/libSPIRV-Tools-shared.so");
|
||||
Shutdown();
|
||||
return false;
|
||||
}
|
||||
#elif XE_PLATFORM_WIN32
|
||||
library_ = LoadLibraryW(
|
||||
(vulkan_sdk_path / "Bin/SPIRV-Tools-shared.dll").wstring().c_str());
|
||||
if (!library_) {
|
||||
XELOGE(
|
||||
"SPIRV-Tools: Failed to load %VULKAN_SDK%/Bin/SPIRV-Tools-shared.dll");
|
||||
Shutdown();
|
||||
return false;
|
||||
}
|
||||
#else
|
||||
#error No SPIRV-Tools library loading provided for the target platform.
|
||||
#endif
|
||||
if (!LoadLibraryFunction(fn_spvContextCreate_, "spvContextCreate") ||
|
||||
!LoadLibraryFunction(fn_spvContextDestroy_, "spvContextDestroy") ||
|
||||
!LoadLibraryFunction(fn_spvValidateBinary_, "spvValidateBinary") ||
|
||||
!LoadLibraryFunction(fn_spvDiagnosticDestroy_, "spvDiagnosticDestroy")) {
|
||||
XELOGE("SPIRV-Tools: Failed to get library function pointers");
|
||||
Shutdown();
|
||||
return false;
|
||||
}
|
||||
context_ = fn_spvContextCreate_(SPV_ENV_VULKAN_1_0);
|
||||
if (!context_) {
|
||||
XELOGE("SPIRV-Tools: Failed to create a Vulkan 1.0 context");
|
||||
Shutdown();
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void SpirvToolsContext::Shutdown() {
|
||||
if (context_) {
|
||||
fn_spvContextDestroy_(context_);
|
||||
context_ = nullptr;
|
||||
}
|
||||
if (library_) {
|
||||
#if XE_PLATFORM_LINUX
|
||||
dlclose(library_);
|
||||
#elif XE_PLATFORM_WIN32
|
||||
FreeLibrary(library_);
|
||||
#endif
|
||||
library_ = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
spv_result_t SpirvToolsContext::Validate(const uint32_t* words,
|
||||
size_t num_words,
|
||||
std::string* error) const {
|
||||
if (error) {
|
||||
error->clear();
|
||||
}
|
||||
if (!context_) {
|
||||
return SPV_UNSUPPORTED;
|
||||
}
|
||||
spv_diagnostic diagnostic = nullptr;
|
||||
spv_result_t result =
|
||||
fn_spvValidateBinary_(context_, words, num_words, &diagnostic);
|
||||
if (diagnostic) {
|
||||
if (error && diagnostic && diagnostic->error) {
|
||||
*error = diagnostic->error;
|
||||
}
|
||||
fn_spvDiagnosticDestroy_(diagnostic);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
} // namespace vulkan
|
||||
} // namespace ui
|
||||
} // namespace xe
|
|
@ -0,0 +1,72 @@
|
|||
/**
|
||||
******************************************************************************
|
||||
* 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_UI_VULKAN_SPIRV_TOOLS_CONTEXT_H_
|
||||
#define XENIA_UI_VULKAN_SPIRV_TOOLS_CONTEXT_H_
|
||||
|
||||
#include <cstdint>
|
||||
#include <string>
|
||||
|
||||
#include "third_party/SPIRV-Tools/include/spirv-tools/libspirv.h"
|
||||
#include "xenia/base/platform.h"
|
||||
|
||||
#if XE_PLATFORM_LINUX
|
||||
#include <dlfcn.h>
|
||||
#elif XE_PLATFORM_WIN32
|
||||
#include "xenia/base/platform_win.h"
|
||||
#endif
|
||||
|
||||
namespace xe {
|
||||
namespace ui {
|
||||
namespace vulkan {
|
||||
|
||||
class SpirvToolsContext {
|
||||
public:
|
||||
SpirvToolsContext() {}
|
||||
SpirvToolsContext(const SpirvToolsContext& context) = delete;
|
||||
SpirvToolsContext& operator=(const SpirvToolsContext& context) = delete;
|
||||
~SpirvToolsContext() { Shutdown(); }
|
||||
bool Initialize();
|
||||
void Shutdown();
|
||||
|
||||
spv_result_t Validate(const uint32_t* words, size_t num_words,
|
||||
std::string* error) const;
|
||||
|
||||
private:
|
||||
#if XE_PLATFORM_LINUX
|
||||
void* library_ = nullptr;
|
||||
#elif XE_PLATFORM_WIN32
|
||||
HMODULE library_ = nullptr;
|
||||
#endif
|
||||
|
||||
template <typename FunctionPointer>
|
||||
bool LoadLibraryFunction(FunctionPointer& function, const char* name) {
|
||||
#if XE_PLATFORM_LINUX
|
||||
function = reinterpret_cast<FunctionPointer>(dlsym(library_, name));
|
||||
#elif XE_PLATFORM_WIN32
|
||||
function =
|
||||
reinterpret_cast<FunctionPointer>(GetProcAddress(library_, name));
|
||||
#else
|
||||
#error No SPIRV-Tools LoadLibraryFunction provided for the target platform.
|
||||
#endif
|
||||
return function != nullptr;
|
||||
}
|
||||
decltype(&spvContextCreate) fn_spvContextCreate_ = nullptr;
|
||||
decltype(&spvContextDestroy) fn_spvContextDestroy_ = nullptr;
|
||||
decltype(&spvValidateBinary) fn_spvValidateBinary_ = nullptr;
|
||||
decltype(&spvDiagnosticDestroy) fn_spvDiagnosticDestroy_ = nullptr;
|
||||
|
||||
spv_context context_ = nullptr;
|
||||
};
|
||||
|
||||
} // namespace vulkan
|
||||
} // namespace ui
|
||||
} // namespace xe
|
||||
|
||||
#endif // XENIA_UI_VULKAN_SPIRV_TOOLS_CONTEXT_H_
|
|
@ -0,0 +1 @@
|
|||
Subproject commit dd534e877e725c9bb6f751c427442456a05384e4
|
|
@ -1 +1 @@
|
|||
Subproject commit 5a9dfb6741ca851f8bb57abc0fe808f5a0705fa2
|
||||
Subproject commit f4f1d8a352ca1908943aea2ad8c54b39b4879080
|
Loading…
Reference in New Issue