From 5f915e1cbea024b238f9bfb90d28099407e1868c Mon Sep 17 00:00:00 2001 From: Stenzek Date: Sun, 14 Apr 2024 16:24:12 +1000 Subject: [PATCH] MetalDevice: Use shared SPIRV-Cross --- CMakeModules/DuckStationDependencies.cmake | 3 + dep/CMakeLists.txt | 4 - src/util/CMakeLists.txt | 2 +- src/util/metal_device.mm | 136 ++++++++++++++++----- 4 files changed, 109 insertions(+), 36 deletions(-) diff --git a/CMakeModules/DuckStationDependencies.cmake b/CMakeModules/DuckStationDependencies.cmake index 2196915ae..0e9c04a30 100644 --- a/CMakeModules/DuckStationDependencies.cmake +++ b/CMakeModules/DuckStationDependencies.cmake @@ -22,6 +22,9 @@ if(NOT WIN32 AND NOT ANDROID) endif() if(APPLE) + # SPIRV-Cross is currently only used on MacOS. + find_package(spirv_cross_c_shared REQUIRED) + set(CMAKE_FIND_FRAMEWORK ${FIND_FRAMEWORK_BACKUP}) endif() endif() diff --git a/dep/CMakeLists.txt b/dep/CMakeLists.txt index db9a7ba97..a3faa098d 100644 --- a/dep/CMakeLists.txt +++ b/dep/CMakeLists.txt @@ -68,7 +68,3 @@ if(WIN32) add_subdirectory(winpixeventruntime EXCLUDE_FROM_ALL) endif() -if(APPLE) - add_subdirectory(spirv-cross EXCLUDE_FROM_ALL) - disable_compiler_warnings_for_target(spirv-cross) -endif() diff --git a/src/util/CMakeLists.txt b/src/util/CMakeLists.txt index f6a2cd5b9..e3e8d3048 100644 --- a/src/util/CMakeLists.txt +++ b/src/util/CMakeLists.txt @@ -262,7 +262,7 @@ elseif(APPLE) find_library(IOK_LIBRARY IOKit REQUIRED) find_library(METAL_LIBRARY Metal) find_library(QUARTZCORE_LIBRARY QuartzCore) - target_link_libraries(util PRIVATE ${METAL_LIBRARY} ${QUARTZCORE_LIBRARY} ${IOK_LIBRARY} spirv-cross) + target_link_libraries(util PRIVATE ${METAL_LIBRARY} ${QUARTZCORE_LIBRARY} ${IOK_LIBRARY} spirv-cross-c-shared) set_source_files_properties(${MAC_SOURCES} PROPERTIES SKIP_PRECOMPILE_HEADERS TRUE) elseif(NOT ANDROID) target_sources(util PRIVATE diff --git a/src/util/metal_device.mm b/src/util/metal_device.mm index 24d32dfeb..f3d3654e0 100644 --- a/src/util/metal_device.mm +++ b/src/util/metal_device.mm @@ -9,6 +9,7 @@ #include "common/file_system.h" #include "common/log.h" #include "common/path.h" +#include "common/scoped_guard.h" #include "common/string_util.h" // TODO FIXME... @@ -16,8 +17,7 @@ #include "fmt/format.h" #include "shaderc/shaderc.hpp" -#include "spirv-cross/spirv_cross.hpp" -#include "spirv-cross/spirv_msl.hpp" +#include "spirv_cross_c.h" #include #include @@ -696,62 +696,136 @@ std::unique_ptr MetalDevice::CreateShaderFromSource(GPUShaderStage st Log_WarningFmt("Shader compiled with warnings:\n{}", result.GetErrorMessage()); } - spirv_cross::CompilerMSL compiler(result.cbegin(), std::distance(result.cbegin(), result.cend())); - spirv_cross::CompilerMSL::Options msl_options = compiler.get_msl_options(); - msl_options.pad_fragment_output_components = true; - msl_options.use_framebuffer_fetch_subpasses = m_features.framebuffer_fetch; - if (m_features.framebuffer_fetch) - msl_options.set_msl_version(2, 3); + spvc_context sctx; + spvc_result sres; + if ((sres = spvc_context_create(&sctx)) != SPVC_SUCCESS) + { + Log_ErrorFmt("spvc_context_create() failed: {}", static_cast(sres)); + return {}; + } + + const ScopedGuard sctx_guard = [&sctx]() { spvc_context_destroy(sctx); }; + + spvc_context_set_error_callback( + sctx, [](void*, const char* error) { Log_ErrorFmt("SPIRV-Cross reported an error: {}", error); }, nullptr); + + spvc_parsed_ir sir; + if ((sres = spvc_context_parse_spirv(sctx, result.cbegin(), std::distance(result.cbegin(), result.cend()), &sir)) != + SPVC_SUCCESS) + { + Log_ErrorFmt("spvc_context_parse_spirv() failed: {}", static_cast(sres)); + DumpBadShader(source, std::string_view()); + return {}; + } + + spvc_compiler scompiler; + if ((sres = spvc_context_create_compiler(sctx, SPVC_BACKEND_MSL, sir, SPVC_CAPTURE_MODE_TAKE_OWNERSHIP, + &scompiler)) != SPVC_SUCCESS) + { + Log_ErrorFmt("spvc_context_create_compiler() failed: {}", static_cast(sres)); + return {}; + } + + spvc_compiler_options soptions; + if ((sres = spvc_compiler_create_compiler_options(scompiler, &soptions)) != SPVC_SUCCESS) + { + Log_ErrorFmt("spvc_compiler_create_compiler_options() failed: {}", static_cast(sres)); + return {}; + } + + if ((sres = spvc_compiler_options_set_bool(soptions, SPVC_COMPILER_OPTION_MSL_PAD_FRAGMENT_OUTPUT_COMPONENTS, + true)) != SPVC_SUCCESS) + { + Log_ErrorFmt("spvc_compiler_options_set_bool(SPVC_COMPILER_OPTION_MSL_PAD_FRAGMENT_OUTPUT_COMPONENTS) failed: {}", + static_cast(sres)); + return {}; + } + + if ((sres = spvc_compiler_options_set_bool(soptions, SPVC_COMPILER_OPTION_MSL_FRAMEBUFFER_FETCH_SUBPASS, + m_features.framebuffer_fetch)) != SPVC_SUCCESS) + { + Log_ErrorFmt("spvc_compiler_options_set_bool(SPVC_COMPILER_OPTION_MSL_FRAMEBUFFER_FETCH_SUBPASS) failed: {}", + static_cast(sres)); + return {}; + } + + if (m_features.framebuffer_fetch && + ((sres = spvc_compiler_options_set_uint(soptions, SPVC_COMPILER_OPTION_MSL_VERSION, + SPVC_MAKE_MSL_VERSION(2, 3, 0))) != SPVC_SUCCESS)) + { + Log_ErrorFmt("spvc_compiler_options_set_uint(SPVC_COMPILER_OPTION_MSL_VERSION) failed: {}", static_cast(sres)); + return {}; + } if (stage == GPUShaderStage::Fragment) { for (u32 i = 0; i < MAX_TEXTURE_SAMPLERS; i++) { - spirv_cross::MSLResourceBinding rb; - rb.stage = spv::ExecutionModelFragment; - rb.desc_set = 1; - rb.binding = i; - rb.count = 1; - rb.msl_texture = i; - rb.msl_sampler = i; - rb.msl_buffer = i; - compiler.add_msl_resource_binding(rb); + const spvc_msl_resource_binding rb = {.stage = SpvExecutionModelFragment, + .desc_set = 1, + .binding = i, + .msl_buffer = i, + .msl_texture = i, + .msl_sampler = i}; + + if ((sres = spvc_compiler_msl_add_resource_binding(scompiler, &rb)) != SPVC_SUCCESS) + { + Log_ErrorFmt("spvc_compiler_msl_add_resource_binding() failed: {}", static_cast(sres)); + return {}; + } } if (!m_features.framebuffer_fetch) { - spirv_cross::MSLResourceBinding rb; - rb.stage = spv::ExecutionModelFragment; - rb.desc_set = 2; - rb.binding = 0; - rb.msl_texture = MAX_TEXTURE_SAMPLERS; - compiler.add_msl_resource_binding(rb); + const spvc_msl_resource_binding rb = { + .stage = SpvExecutionModelFragment, .desc_set = 2, .binding = 0, .msl_texture = MAX_TEXTURE_SAMPLERS}; + + if ((sres = spvc_compiler_msl_add_resource_binding(scompiler, &rb)) != SPVC_SUCCESS) + { + Log_ErrorFmt("spvc_compiler_msl_add_resource_binding() for FB failed: {}", static_cast(sres)); + return {}; + } } } - compiler.set_msl_options(msl_options); + if ((sres = spvc_compiler_install_compiler_options(scompiler, soptions)) != SPVC_SUCCESS) + { + Log_ErrorFmt("spvc_compiler_install_compiler_options() failed: {}", static_cast(sres)); + return {}; + } - const std::string msl = compiler.compile(); - if (msl.empty()) + const char* msl; + if ((sres = spvc_compiler_compile(scompiler, &msl)) != SPVC_SUCCESS) { - Log_ErrorPrintf("Failed to compile SPIR-V to MSL."); + Log_ErrorFmt("spvc_compiler_compile() failed: {}", static_cast(sres)); + DumpBadShader(source, std::string_view()); return {}; } + + const size_t msl_length = msl ? std::strlen(msl) : 0; + if (msl_length == 0) + { + Log_ErrorPrint("Failed to compile SPIR-V to MSL."); + DumpBadShader(source, std::string_view()); + return {}; + } + + const std::string_view mslv(msl, msl_length); if constexpr (dump_shaders) { static unsigned s_next_id = 0; ++s_next_id; DumpShader(s_next_id, "_input", source); - DumpShader(s_next_id, "_msl", msl); + DumpShader(s_next_id, "_msl", mslv); } if (out_binary) { - out_binary->resize(msl.size()); - std::memcpy(out_binary->data(), msl.data(), msl.size()); + out_binary->resize(mslv.size()); + std::memcpy(out_binary->data(), mslv.data(), mslv.size()); } - return CreateShaderFromMSL(stage, msl, "main0"); + return CreateShaderFromMSL(stage, mslv, "main0"); } MetalPipeline::MetalPipeline(id pipeline, id depth, MTLCullMode cull_mode,