mirror of
https://github.com/izzy2lost/xenia-edge.git
synced 2026-07-06 00:20:26 -07:00
[Vulkan] Remove runtime dependency on SPIRV-Tools
This commit is contained in:
@@ -79,7 +79,7 @@ jobs:
|
||||
sudo apt-add-repository "deb http://apt.llvm.org/${UBUNTU_BASE}/ llvm-toolchain-${UBUNTU_BASE}-21 main"
|
||||
|
||||
sudo apt-get -y update
|
||||
sudo apt-get -y install mesa-vulkan-drivers valgrind libc++-dev libc++abi-dev libgtk-3-dev libsdl2-dev libvulkan-dev libx11-xcb-dev clang-21 lld-21 ninja-build cmake spirv-tools libxkbcommon-x11-0 libxcb-icccm4 libxcb-image0 libxcb-keysyms1 libxcb-randr0 libxcb-render-util0 libxcb-xinerama0 libxcb-cursor0 libxcb-shape0 libfontconfig1-dev libxtst-dev
|
||||
sudo apt-get -y install mesa-vulkan-drivers valgrind libc++-dev libc++abi-dev libgtk-3-dev libsdl2-dev libvulkan-dev libx11-xcb-dev clang-21 lld-21 ninja-build cmake libxkbcommon-x11-0 libxcb-icccm4 libxcb-image0 libxcb-keysyms1 libxcb-randr0 libxcb-render-util0 libxcb-xinerama0 libxcb-cursor0 libxcb-shape0 libfontconfig1-dev libxtst-dev
|
||||
|
||||
# Install Vulkan SDK
|
||||
if [ '${{ steps.cache-vulkan-sdk-linux.outputs.cache-hit }}' != 'true' ]; then
|
||||
@@ -244,7 +244,7 @@ jobs:
|
||||
|
||||
- name: Setup
|
||||
run: |
|
||||
# Install Vulkan SDK which includes spirv-tools
|
||||
# Install Vulkan SDK for headers + loader
|
||||
if (Test-Path -Path "C:\VulkanSDK") {
|
||||
echo "Vulkan SDK found in cache."
|
||||
} else {
|
||||
|
||||
+6
-6
@@ -60,11 +60,11 @@ file(MAKE_DIRECTORY "${PROJECT_SOURCE_DIR}/scratch")
|
||||
|
||||
find_package(Python3 REQUIRED COMPONENTS Interpreter)
|
||||
|
||||
# Debug/Checked/Valgrind are compiled against the release CRT (/MD) so we
|
||||
# can link the Vulkan SDK's release-only SPIRV-Tools libs. Force any
|
||||
# imported library (Qt6::*, etc.) to resolve to its Release variant in
|
||||
# those configs too — otherwise Qt6*d.dll drags in /MDd and the process
|
||||
# ends up mixing two CRTs, corrupting std::string/QString allocations.
|
||||
# Debug/Checked/Valgrind are compiled against the release CRT (/MD) so
|
||||
# we can link Qt's release-only prebuilt DLLs — Qt6*d.dll drags in /MDd
|
||||
# and mixing two CRTs in one process corrupts std::string/QString
|
||||
# allocations. Force any imported library (Qt6::*, etc.) to resolve to
|
||||
# its Release variant in those configs too.
|
||||
# NOCONFIG is the fallback for imported targets that ship without a
|
||||
# configuration suffix (FindOpenGL on Linux creates OpenGL::OpenGL this
|
||||
# way — without NOCONFIG in the map we fail to resolve it in Debug et al).
|
||||
@@ -193,7 +193,7 @@ if(MSVC)
|
||||
|
||||
# Debug: /RTC1 requires the debug CRT, so strip it — we're on /MD. Pin
|
||||
# _ITERATOR_DEBUG_LEVEL=0 so our STL object layouts match release-CRT
|
||||
# prebuilts (Vulkan SDK SPIRV-Tools) and we don't hit LNK2038.
|
||||
# prebuilts (Qt, etc.) and we don't hit LNK2038.
|
||||
string(REPLACE "/RTC1" "" CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG}")
|
||||
string(REPLACE "/RTC1" "" CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG}")
|
||||
add_compile_definitions(
|
||||
|
||||
@@ -130,20 +130,17 @@ set(_edge_game_patches_src "${PROJECT_SOURCE_DIR}/.data_repos/game-patches/patch
|
||||
# release Qt (root CMakeLists maps Debug/Checked/Valgrind imported
|
||||
# configs to Release), so deploying Qt6Cored.dll would pull in the
|
||||
# debug CRT and corrupt Qt/STL objects at runtime.
|
||||
if(WIN32)
|
||||
set(XE_QT_DIR "$ENV{QT_DIR}")
|
||||
if(XE_QT_DIR AND EXISTS "${XE_QT_DIR}/bin/windeployqt.exe")
|
||||
add_custom_command(TARGET xenia-app POST_BUILD
|
||||
COMMAND "${XE_QT_DIR}/bin/windeployqt.exe"
|
||||
--release
|
||||
--no-translations
|
||||
--no-system-d3d-compiler
|
||||
--no-opengl-sw
|
||||
--no-compiler-runtime
|
||||
"$<TARGET_FILE:xenia-app>"
|
||||
VERBATIM
|
||||
)
|
||||
endif()
|
||||
if(WIN32 AND TARGET Qt6::windeployqt)
|
||||
add_custom_command(TARGET xenia-app POST_BUILD
|
||||
COMMAND Qt6::windeployqt
|
||||
--release
|
||||
--no-translations
|
||||
--no-system-d3d-compiler
|
||||
--no-opengl-sw
|
||||
--no-compiler-runtime
|
||||
"$<TARGET_FILE:xenia-app>"
|
||||
VERBATIM
|
||||
)
|
||||
endif()
|
||||
|
||||
# Stage runtime assets (fonts, game patches, optimized settings) next to
|
||||
|
||||
@@ -4,10 +4,6 @@ target_include_directories(xenia-gpu PRIVATE
|
||||
${PROJECT_SOURCE_DIR}/third_party/Vulkan-Headers/include
|
||||
${PROJECT_SOURCE_DIR}/third_party/glslang
|
||||
)
|
||||
# Windows: SPIRV-Tools headers come from the Vulkan SDK
|
||||
if(WIN32 AND DEFINED ENV{VULKAN_SDK})
|
||||
target_include_directories(xenia-gpu PRIVATE "$ENV{VULKAN_SDK}/Include")
|
||||
endif()
|
||||
target_link_libraries(xenia-gpu PUBLIC dxbc fmt glslang-spirv snappy xxhash xenia-base xenia-ui)
|
||||
xe_target_defaults(xenia-gpu)
|
||||
|
||||
@@ -27,9 +23,6 @@ if(XENIA_BUILD_MISC)
|
||||
${PROJECT_SOURCE_DIR}/third_party/Vulkan-Headers/include
|
||||
${PROJECT_SOURCE_DIR}/third_party/glslang
|
||||
)
|
||||
if(WIN32 AND DEFINED ENV{VULKAN_SDK})
|
||||
target_include_directories(xenia-gpu-shader-compiler PRIVATE "$ENV{VULKAN_SDK}/Include")
|
||||
endif()
|
||||
target_link_libraries(xenia-gpu-shader-compiler PRIVATE
|
||||
dxbc fmt glslang-spirv snappy xenia-base xenia-gpu xenia-ui xenia-ui-vulkan
|
||||
)
|
||||
|
||||
@@ -24,7 +24,6 @@
|
||||
#include "xenia/gpu/shader_translator.h"
|
||||
#include "xenia/gpu/spirv_shader_translator.h"
|
||||
#include "xenia/gpu/xenos.h"
|
||||
#include "xenia/ui/vulkan/spirv_tools_context.h"
|
||||
|
||||
// For D3DDisassemble:
|
||||
#if XE_PLATFORM_WIN32
|
||||
@@ -197,17 +196,6 @@ 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(spirv_features.spirv_version)) {
|
||||
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();
|
||||
}
|
||||
|
||||
@@ -22,7 +22,6 @@
|
||||
#include "xenia/gpu/gpu_flags.h"
|
||||
#include "xenia/gpu/spirv_compatibility.h"
|
||||
#include "xenia/gpu/spirv_shader.h"
|
||||
#include "xenia/ui/vulkan/spirv_tools_context.h"
|
||||
|
||||
DEFINE_string(spirv_version_override, "1.0",
|
||||
"Override the SPIR-V version used in shader translation.\n"
|
||||
@@ -958,24 +957,6 @@ std::vector<uint8_t> SpirvShaderTranslator::CompleteTranslation() {
|
||||
std::vector<unsigned int> module_uints;
|
||||
builder_->dump(module_uints);
|
||||
|
||||
// Optimize the SPIR-V if optimization is enabled and tools are available
|
||||
if (spirv_optimize_ && spirv_tools_context_) {
|
||||
size_t original_size = module_uints.size();
|
||||
std::vector<uint32_t> optimized_module;
|
||||
spv_result_t result = spirv_tools_context_->Optimize(
|
||||
module_uints.data(), module_uints.size(), optimized_module, true);
|
||||
if (result == SPV_SUCCESS && !optimized_module.empty()) {
|
||||
size_t optimized_size = optimized_module.size();
|
||||
module_uints = std::move(optimized_module);
|
||||
XELOGI("SPIR-V shader optimized: {} -> {} words ({:.1f}% reduction)",
|
||||
original_size, optimized_size,
|
||||
100.0f * (1.0f - float(optimized_size) / float(original_size)));
|
||||
} else {
|
||||
XELOGW("SPIR-V shader optimization failed with error code: {}",
|
||||
static_cast<int>(result));
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<uint8_t> module_bytes;
|
||||
module_bytes.reserve(sizeof(unsigned int) * module_uints.size());
|
||||
module_bytes.insert(module_bytes.cend(),
|
||||
|
||||
@@ -23,11 +23,6 @@
|
||||
#include "xenia/ui/vulkan/vulkan_device.h"
|
||||
|
||||
namespace xe {
|
||||
namespace ui {
|
||||
namespace vulkan {
|
||||
class SpirvToolsContext;
|
||||
} // namespace vulkan
|
||||
} // namespace ui
|
||||
namespace gpu {
|
||||
|
||||
class SpirvShaderTranslator : public ShaderTranslator {
|
||||
@@ -398,21 +393,18 @@ class SpirvShaderTranslator : public ShaderTranslator {
|
||||
bool fragment_shader_barycentric;
|
||||
};
|
||||
|
||||
SpirvShaderTranslator(
|
||||
const Features& features, bool native_2x_msaa_with_attachments,
|
||||
bool native_2x_msaa_no_attachments, bool edram_fragment_shader_interlock,
|
||||
uint32_t draw_resolution_scale_x = 1,
|
||||
uint32_t draw_resolution_scale_y = 1,
|
||||
ui::vulkan::SpirvToolsContext* spirv_tools_context = nullptr,
|
||||
bool spirv_optimize = true)
|
||||
SpirvShaderTranslator(const Features& features,
|
||||
bool native_2x_msaa_with_attachments,
|
||||
bool native_2x_msaa_no_attachments,
|
||||
bool edram_fragment_shader_interlock,
|
||||
uint32_t draw_resolution_scale_x = 1,
|
||||
uint32_t draw_resolution_scale_y = 1)
|
||||
: features_(features),
|
||||
native_2x_msaa_with_attachments_(native_2x_msaa_with_attachments),
|
||||
native_2x_msaa_no_attachments_(native_2x_msaa_no_attachments),
|
||||
edram_fragment_shader_interlock_(edram_fragment_shader_interlock),
|
||||
draw_resolution_scale_x_(draw_resolution_scale_x),
|
||||
draw_resolution_scale_y_(draw_resolution_scale_y),
|
||||
spirv_tools_context_(spirv_tools_context),
|
||||
spirv_optimize_(spirv_optimize) {}
|
||||
draw_resolution_scale_y_(draw_resolution_scale_y) {}
|
||||
|
||||
uint64_t GetDefaultVertexShaderModification(
|
||||
uint32_t dynamic_addressable_register_count,
|
||||
@@ -787,8 +779,6 @@ class SpirvShaderTranslator : public ShaderTranslator {
|
||||
bool native_2x_msaa_no_attachments_;
|
||||
uint32_t draw_resolution_scale_x_;
|
||||
uint32_t draw_resolution_scale_y_;
|
||||
ui::vulkan::SpirvToolsContext* spirv_tools_context_;
|
||||
bool spirv_optimize_;
|
||||
|
||||
// For safety with different drivers (even though fragment shader interlock in
|
||||
// SPIR-V only has one control flow requirement - that both begin and end must
|
||||
|
||||
@@ -33,7 +33,6 @@
|
||||
#include "xenia/gpu/vulkan/vulkan_shader.h"
|
||||
#include "xenia/gpu/vulkan/vulkan_shared_memory.h"
|
||||
#include "xenia/gpu/xenos.h"
|
||||
#include "xenia/ui/vulkan/spirv_tools_context.h"
|
||||
#include "xenia/ui/vulkan/vulkan_util.h"
|
||||
|
||||
// Shader bytecode.
|
||||
@@ -62,13 +61,6 @@ DEFINE_int32(
|
||||
"0 to disable multithreaded pipeline creation.",
|
||||
"Vulkan");
|
||||
|
||||
DEFINE_bool(
|
||||
vulkan_spirv_optimization, false,
|
||||
"Enable SPIR-V shader optimization. When enabled, shaders are optimized "
|
||||
"on pipeline creation threads before the shader module is created. This "
|
||||
"only affects async pipeline creation and does not block the main thread.",
|
||||
"Vulkan");
|
||||
|
||||
DECLARE_bool(vulkan_dynamic_rendering);
|
||||
|
||||
namespace xe {
|
||||
@@ -98,24 +90,13 @@ bool VulkanPipelineCache::Initialize() {
|
||||
render_target_cache_.GetPath() ==
|
||||
RenderTargetCache::Path::kPixelShaderInterlock;
|
||||
|
||||
// Initialize SPIRV-Tools (used for validation and optional optimization)
|
||||
spirv_tools_context_ = std::make_unique<ui::vulkan::SpirvToolsContext>();
|
||||
if (!spirv_tools_context_->Initialize(spirv_version_)) {
|
||||
XELOGE("Failed to initialize SPIRV-Tools");
|
||||
// Continue without SPIRV-Tools
|
||||
spirv_tools_context_.reset();
|
||||
} else {
|
||||
XELOGI("SPIRV-Tools initialized successfully");
|
||||
}
|
||||
|
||||
shader_translator_ = std::make_unique<SpirvShaderTranslator>(
|
||||
SpirvShaderTranslator::Features(vulkan_device),
|
||||
render_target_cache_.msaa_2x_attachments_supported(),
|
||||
render_target_cache_.msaa_2x_no_attachments_supported(),
|
||||
edram_fragment_shader_interlock,
|
||||
render_target_cache_.draw_resolution_scale_x(),
|
||||
render_target_cache_.draw_resolution_scale_y(), nullptr,
|
||||
false); // Never optimize during initial translation
|
||||
render_target_cache_.draw_resolution_scale_y());
|
||||
|
||||
if (edram_fragment_shader_interlock) {
|
||||
std::vector<uint8_t> depth_only_fragment_shader_code =
|
||||
@@ -845,13 +826,6 @@ void VulkanPipelineCache::CreationThread() {
|
||||
creation_arguments.pixel_shader)) {
|
||||
XELOGE("Failed to translate shaders for pipeline creation");
|
||||
} else {
|
||||
// Optimize shaders on the creation thread before creating the pipeline.
|
||||
// This keeps the main thread fast while still benefiting from
|
||||
// optimization.
|
||||
OptimizeTranslationIfNeeded(*creation_arguments.vertex_shader);
|
||||
if (creation_arguments.pixel_shader) {
|
||||
OptimizeTranslationIfNeeded(*creation_arguments.pixel_shader);
|
||||
}
|
||||
if (!EnsurePipelineCreated(creation_arguments)) {
|
||||
XELOGE("Failed to create Vulkan pipeline");
|
||||
}
|
||||
@@ -905,32 +879,6 @@ bool VulkanPipelineCache::TranslateAnalyzedShader(
|
||||
return false;
|
||||
}
|
||||
|
||||
#ifndef NDEBUG
|
||||
// Validate SPIR-V before creating shader module to get detailed error
|
||||
// messages. This is a warning only - we still try to create the shader
|
||||
// module to see if the driver accepts it.
|
||||
if (spirv_tools_context_) {
|
||||
std::string validation_error;
|
||||
spv_result_t validation_result = spirv_tools_context_->Validate(
|
||||
reinterpret_cast<const uint32_t*>(
|
||||
translation.translated_binary().data()),
|
||||
translation.translated_binary().size() / sizeof(uint32_t),
|
||||
&validation_error);
|
||||
if (validation_result != SPV_SUCCESS) {
|
||||
XELOGW(
|
||||
"VulkanPipelineCache: SPIR-V validation warning for shader {:016X} "
|
||||
"modification {:016X}: {}",
|
||||
shader.ucode_data_hash(), translation.modification(),
|
||||
validation_error);
|
||||
// Dump the shader for debugging if dump path is set
|
||||
if (!cvars::dump_shaders.empty()) {
|
||||
translation.Dump(cvars::dump_shaders, "vulkan_warning");
|
||||
}
|
||||
// Continue anyway - the driver might accept it
|
||||
}
|
||||
}
|
||||
#endif // NDEBUG
|
||||
|
||||
if (translation.GetOrCreateShaderModule() == VK_NULL_HANDLE) {
|
||||
return false;
|
||||
}
|
||||
@@ -1044,8 +992,7 @@ void VulkanPipelineCache::TranslateShadersForStorage(
|
||||
// Each thread needs its own translator.
|
||||
SpirvShaderTranslator translator(
|
||||
SpirvShaderTranslator::Features(vulkan_device), msaa_2x_attachments,
|
||||
msaa_2x_no_attachments, edram_fsi_used, draw_res_x, draw_res_y, nullptr,
|
||||
false); // Don't optimize during parallel translation
|
||||
msaa_2x_no_attachments, edram_fsi_used, draw_res_x, draw_res_y);
|
||||
|
||||
while (true) {
|
||||
size_t index = translation_index.fetch_add(1);
|
||||
@@ -3173,58 +3120,6 @@ void VulkanPipelineCache::ProcessDeferredDestructions() {
|
||||
}
|
||||
}
|
||||
|
||||
void VulkanPipelineCache::OptimizeTranslationIfNeeded(
|
||||
VulkanShader::VulkanTranslation& translation) {
|
||||
// Only optimize if enabled and spirv-tools is available.
|
||||
if (!cvars::vulkan_spirv_optimization || !spirv_tools_context_) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Only optimize if the shader module hasn't been created yet.
|
||||
// Once created, we can't replace it without the complexity of the old
|
||||
// background optimization system.
|
||||
if (translation.shader_module() != VK_NULL_HANDLE) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!translation.is_valid()) {
|
||||
return;
|
||||
}
|
||||
|
||||
const std::vector<uint8_t>& unoptimized_binary =
|
||||
translation.translated_binary();
|
||||
if (unoptimized_binary.empty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Reinterpret the byte vector as uint32_t for SPIRV-Tools
|
||||
const uint32_t* spirv_words =
|
||||
reinterpret_cast<const uint32_t*>(unoptimized_binary.data());
|
||||
size_t word_count = unoptimized_binary.size() / sizeof(uint32_t);
|
||||
|
||||
std::vector<uint32_t> optimized_spirv;
|
||||
spv_result_t result = spirv_tools_context_->Optimize(spirv_words, word_count,
|
||||
optimized_spirv, true);
|
||||
|
||||
if (result == SPV_SUCCESS && !optimized_spirv.empty()) {
|
||||
// Convert back to byte vector and replace the translated binary
|
||||
std::vector<uint8_t> optimized_binary;
|
||||
optimized_binary.resize(optimized_spirv.size() * sizeof(uint32_t));
|
||||
std::memcpy(optimized_binary.data(), optimized_spirv.data(),
|
||||
optimized_binary.size());
|
||||
translation.SetOptimizedBinary(std::move(optimized_binary));
|
||||
|
||||
size_t original_size = word_count;
|
||||
size_t optimized_size = optimized_spirv.size();
|
||||
XELOGI("SPIRV optimization: {} -> {} words ({:.1f}% reduction)",
|
||||
original_size, optimized_size,
|
||||
100.0f * (1.0f - float(optimized_size) / float(original_size)));
|
||||
} else {
|
||||
XELOGW("SPIRV optimization failed with error code: {}",
|
||||
static_cast<int>(result));
|
||||
}
|
||||
}
|
||||
|
||||
void VulkanPipelineCache::InitializeShaderStorage(
|
||||
const std::filesystem::path& cache_root, uint32_t title_id, bool blocking,
|
||||
std::function<void()> completion_callback) {
|
||||
|
||||
@@ -41,11 +41,6 @@
|
||||
#include "xenia/ui/vulkan/vulkan_api.h"
|
||||
|
||||
namespace xe {
|
||||
namespace ui {
|
||||
namespace vulkan {
|
||||
class SpirvToolsContext;
|
||||
} // namespace vulkan
|
||||
} // namespace ui
|
||||
namespace gpu {
|
||||
namespace vulkan {
|
||||
|
||||
@@ -399,11 +394,6 @@ class VulkanPipelineCache {
|
||||
return EnsurePipelineCreated(creation_arguments, placeholder_pixel_shader_);
|
||||
}
|
||||
|
||||
// Optimizes a shader's SPIR-V binary if optimization is enabled and the
|
||||
// shader module hasn't been created yet. Called from creation threads.
|
||||
void OptimizeTranslationIfNeeded(
|
||||
VulkanShader::VulkanTranslation& translation);
|
||||
|
||||
VulkanCommandProcessor& command_processor_;
|
||||
const RegisterFile& register_file_;
|
||||
VulkanRenderTargetCache& render_target_cache_;
|
||||
@@ -414,8 +404,6 @@ class VulkanPipelineCache {
|
||||
|
||||
// Temporary storage for AnalyzeUcode calls on the processor thread.
|
||||
StringBuffer ucode_disasm_buffer_;
|
||||
// SPIRV-Tools context for optimizing shaders.
|
||||
std::unique_ptr<ui::vulkan::SpirvToolsContext> spirv_tools_context_;
|
||||
// Reusable shader translator on the command processor thread.
|
||||
std::unique_ptr<SpirvShaderTranslator> shader_translator_;
|
||||
|
||||
|
||||
@@ -7,16 +7,6 @@ target_include_directories(xenia-ui-vulkan PRIVATE
|
||||
${PROJECT_SOURCE_DIR}/third_party/glslang
|
||||
)
|
||||
target_link_libraries(xenia-ui-vulkan PUBLIC xenia-base xenia-ui)
|
||||
|
||||
# SPIRV-Tools: Vulkan SDK on Windows (headers + libs under $VULKAN_SDK),
|
||||
# system package on Linux (libspirv-tools-*).
|
||||
if(WIN32 AND DEFINED ENV{VULKAN_SDK})
|
||||
target_include_directories(xenia-ui-vulkan PRIVATE "$ENV{VULKAN_SDK}/Include")
|
||||
target_link_directories(xenia-ui-vulkan PUBLIC "$ENV{VULKAN_SDK}/Lib")
|
||||
target_link_libraries(xenia-ui-vulkan PUBLIC SPIRV-Tools-opt SPIRV-Tools)
|
||||
elseif(CMAKE_SYSTEM_NAME STREQUAL "Linux")
|
||||
target_link_libraries(xenia-ui-vulkan PUBLIC SPIRV-Tools-opt SPIRV-Tools)
|
||||
endif()
|
||||
xe_target_defaults(xenia-ui-vulkan)
|
||||
|
||||
if(XENIA_BUILD_MISC)
|
||||
|
||||
@@ -1,126 +0,0 @@
|
||||
/**
|
||||
******************************************************************************
|
||||
* 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 <spirv-tools/optimizer.hpp>
|
||||
#include "xenia/base/logging.h"
|
||||
#include "xenia/base/platform.h"
|
||||
|
||||
namespace xe {
|
||||
namespace ui {
|
||||
namespace vulkan {
|
||||
|
||||
bool SpirvToolsContext::Initialize(unsigned int spirv_version) {
|
||||
// Determine target environment based on SPIR-V version
|
||||
if (spirv_version >= 0x10500) {
|
||||
target_env_ = SPV_ENV_VULKAN_1_2;
|
||||
} else if (spirv_version >= 0x10400) {
|
||||
target_env_ = SPV_ENV_VULKAN_1_1_SPIRV_1_4;
|
||||
} else if (spirv_version >= 0x10300) {
|
||||
target_env_ = SPV_ENV_VULKAN_1_1;
|
||||
} else {
|
||||
target_env_ = SPV_ENV_VULKAN_1_0;
|
||||
}
|
||||
|
||||
// Create SPIR-V context
|
||||
context_ = spvContextCreate(target_env_);
|
||||
if (!context_) {
|
||||
XELOGE("SPIRV-Tools: Failed to create context for target environment");
|
||||
return false;
|
||||
}
|
||||
|
||||
XELOGI("SPIRV-Tools: Initialized successfully with static linking");
|
||||
return true;
|
||||
}
|
||||
|
||||
void SpirvToolsContext::Shutdown() {
|
||||
if (context_) {
|
||||
spvContextDestroy(context_);
|
||||
context_ = 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;
|
||||
}
|
||||
|
||||
// Create validator options with scalar block layout to match modern Vulkan
|
||||
// driver behavior. This is the most permissive layout option and matches
|
||||
// what VK_EXT_scalar_block_layout provides.
|
||||
spv_validator_options options = spvValidatorOptionsCreate();
|
||||
spvValidatorOptionsSetScalarBlockLayout(options, true);
|
||||
|
||||
// Create binary struct for the validation API
|
||||
spv_const_binary_t binary = {words, num_words};
|
||||
|
||||
spv_diagnostic diagnostic = nullptr;
|
||||
spv_result_t result =
|
||||
spvValidateWithOptions(context_, options, &binary, &diagnostic);
|
||||
|
||||
spvValidatorOptionsDestroy(options);
|
||||
|
||||
if (diagnostic) {
|
||||
if (error && diagnostic->error) {
|
||||
*error = diagnostic->error;
|
||||
}
|
||||
spvDiagnosticDestroy(diagnostic);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
spv_result_t SpirvToolsContext::Optimize(const uint32_t* words,
|
||||
size_t num_words,
|
||||
std::vector<uint32_t>& optimized_words,
|
||||
bool performance_passes) {
|
||||
optimized_words.clear();
|
||||
if (!context_) {
|
||||
return SPV_UNSUPPORTED;
|
||||
}
|
||||
|
||||
// Use the C++ optimizer API for better integration
|
||||
spvtools::Optimizer optimizer(target_env_);
|
||||
|
||||
// Set up message consumer for error reporting
|
||||
optimizer.SetMessageConsumer([](spv_message_level_t level, const char* source,
|
||||
const spv_position_t& position,
|
||||
const char* message) {
|
||||
if (level == SPV_MSG_ERROR || level == SPV_MSG_FATAL ||
|
||||
level == SPV_MSG_INTERNAL_ERROR) {
|
||||
XELOGE("SPIRV-Tools optimizer: {} {}", source ? source : "",
|
||||
message ? message : "");
|
||||
}
|
||||
});
|
||||
|
||||
// Register optimization passes
|
||||
if (performance_passes) {
|
||||
optimizer.RegisterPerformancePasses();
|
||||
} else {
|
||||
optimizer.RegisterSizePasses();
|
||||
}
|
||||
|
||||
// Run optimizer
|
||||
if (!optimizer.Run(words, num_words, &optimized_words)) {
|
||||
return SPV_ERROR_INVALID_BINARY;
|
||||
}
|
||||
|
||||
return SPV_SUCCESS;
|
||||
}
|
||||
|
||||
} // namespace vulkan
|
||||
} // namespace ui
|
||||
} // namespace xe
|
||||
@@ -1,52 +0,0 @@
|
||||
/**
|
||||
******************************************************************************
|
||||
* 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 <vector>
|
||||
|
||||
#include <spirv-tools/libspirv.h>
|
||||
#include <spirv-tools/optimizer.hpp>
|
||||
#include "xenia/base/platform.h"
|
||||
|
||||
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(unsigned int spirv_version);
|
||||
void Shutdown();
|
||||
|
||||
spv_result_t Validate(const uint32_t* words, size_t num_words,
|
||||
std::string* error) const;
|
||||
|
||||
// Optimizes SPIR-V code. Returns SPV_SUCCESS on successful optimization.
|
||||
// The optimized binary is returned in optimized_words.
|
||||
spv_result_t Optimize(const uint32_t* words, size_t num_words,
|
||||
std::vector<uint32_t>& optimized_words,
|
||||
bool performance_passes = true);
|
||||
|
||||
private:
|
||||
spv_context context_ = nullptr;
|
||||
spv_target_env target_env_ = SPV_ENV_UNIVERSAL_1_0;
|
||||
};
|
||||
|
||||
} // namespace vulkan
|
||||
} // namespace ui
|
||||
} // namespace xe
|
||||
|
||||
#endif // XENIA_UI_VULKAN_SPIRV_TOOLS_CONTEXT_H_
|
||||
Reference in New Issue
Block a user