From 3fe8eccdb32019e6de53bc0103b9c3d33edc690f Mon Sep 17 00:00:00 2001 From: David Chavez Date: Mon, 15 Dec 2025 15:30:33 +0100 Subject: [PATCH 1/6] precompile internal clear and resolve shaders --- CMakeLists.txt | 8 ++- cmake/PlumeShaderCompilation.cmake | 95 ++++++++++++++++++++++++++ plume_metal.cpp | 53 ++++++++++++--- shaders/plume_clear.metal | 44 ++++++++++++ shaders/plume_resolve.metal | 35 ++++++++++ tools/file_to_c.cpp | 105 +++++++++++++++++++++++++++++ 6 files changed, 330 insertions(+), 10 deletions(-) create mode 100644 cmake/PlumeShaderCompilation.cmake create mode 100644 shaders/plume_clear.metal create mode 100644 shaders/plume_resolve.metal create mode 100644 tools/file_to_c.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index e782ac5..074771d 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -94,7 +94,7 @@ if(PLUME_D3D12_AGILITY_SDK_ENABLED) target_link_libraries(plume PRIVATE Microsoft::DirectX-Headers Microsoft::DirectX-Guids Microsoft::DirectX12-Agility) endif() -# Platform-specific includes +# Platform-specific includes and shader compilation if(APPLE) if(PLUME_APPLE_RETINA_ENABLED) target_compile_definitions(plume PRIVATE PLUME_APPLE_RETINA_ENABLED) @@ -103,9 +103,13 @@ if(APPLE) target_include_directories(plume PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/contrib/metal-cpp ) + + # Compile and embed Metal shaders + include(${CMAKE_CURRENT_SOURCE_DIR}/cmake/PlumeShaderCompilation.cmake) + plume_compile_metal_shaders(plume) endif() # Add examples if requested if(PLUME_BUILD_EXAMPLES) add_subdirectory(examples) -endif() +endif() diff --git a/cmake/PlumeShaderCompilation.cmake b/cmake/PlumeShaderCompilation.cmake new file mode 100644 index 0000000..fb5c627 --- /dev/null +++ b/cmake/PlumeShaderCompilation.cmake @@ -0,0 +1,95 @@ +# Plume internal shader compilation +# Compiles Metal shaders to metallib and embeds them as C arrays + +# Build the file_to_c tool for the host system +function(plume_build_file_to_c) + if(TARGET plume_file_to_c) + return() + endif() + + set(FILE_TO_C_SOURCE "${CMAKE_CURRENT_FUNCTION_LIST_DIR}/../tools/file_to_c.cpp") + + add_executable(plume_file_to_c ${FILE_TO_C_SOURCE}) + set_target_properties(plume_file_to_c PROPERTIES + RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/plume_tools" + ) + + if(APPLE) + set_target_properties(plume_file_to_c PROPERTIES + XCODE_ATTRIBUTE_CODE_SIGN_IDENTITY "-" + ) + endif() +endfunction() + +# Compile Metal shaders to embedded C headers +function(plume_compile_metal_shaders TARGET_NAME) + if(NOT APPLE) + return() + endif() + + # Build the file_to_c tool + plume_build_file_to_c() + + # Get the shader source directory + set(SHADER_DIR "${CMAKE_CURRENT_FUNCTION_LIST_DIR}/../shaders") + set(OUTPUT_DIR "${CMAKE_BINARY_DIR}/plume_shaders") + + # Create output directory + file(MAKE_DIRECTORY "${OUTPUT_DIR}") + + # List of shaders to compile + set(PLUME_METAL_SHADERS + plume_clear + plume_resolve + ) + + set(GENERATED_SOURCES "") + set(GENERATED_HEADERS "") + + foreach(SHADER_NAME ${PLUME_METAL_SHADERS}) + set(SHADER_SOURCE "${SHADER_DIR}/${SHADER_NAME}.metal") + set(IR_OUTPUT "${OUTPUT_DIR}/${SHADER_NAME}.ir") + set(METALLIB_OUTPUT "${OUTPUT_DIR}/${SHADER_NAME}.metallib") + set(C_OUTPUT "${OUTPUT_DIR}/${SHADER_NAME}.metallib.c") + set(H_OUTPUT "${OUTPUT_DIR}/${SHADER_NAME}.metallib.h") + + # Compile Metal to IR + add_custom_command( + OUTPUT ${IR_OUTPUT} + COMMAND xcrun -sdk macosx metal -o ${IR_OUTPUT} -c ${SHADER_SOURCE} + DEPENDS ${SHADER_SOURCE} + COMMENT "Compiling ${SHADER_NAME}.metal to IR" + VERBATIM + ) + + # Link IR to metallib + add_custom_command( + OUTPUT ${METALLIB_OUTPUT} + COMMAND xcrun -sdk macosx metallib ${IR_OUTPUT} -o ${METALLIB_OUTPUT} + DEPENDS ${IR_OUTPUT} + COMMENT "Linking ${SHADER_NAME} to metallib" + VERBATIM + ) + + # Generate C header with embedded data + add_custom_command( + OUTPUT ${C_OUTPUT} ${H_OUTPUT} + COMMAND plume_file_to_c ${METALLIB_OUTPUT} "${SHADER_NAME}_metallib" ${C_OUTPUT} ${H_OUTPUT} + DEPENDS ${METALLIB_OUTPUT} plume_file_to_c + COMMENT "Generating embedded header for ${SHADER_NAME}" + VERBATIM + ) + + list(APPEND GENERATED_SOURCES ${C_OUTPUT}) + list(APPEND GENERATED_HEADERS ${H_OUTPUT}) + endforeach() + + # Add generated sources to the target + target_sources(${TARGET_NAME} PRIVATE ${GENERATED_SOURCES}) + + # Add include directory for generated headers + target_include_directories(${TARGET_NAME} PRIVATE "${OUTPUT_DIR}") + + # Define that we have pre-compiled shaders + target_compile_definitions(${TARGET_NAME} PRIVATE PLUME_PRECOMPILED_METAL_SHADERS) +endfunction() diff --git a/plume_metal.cpp b/plume_metal.cpp index 8bbb865..01dc45b 100644 --- a/plume_metal.cpp +++ b/plume_metal.cpp @@ -19,6 +19,11 @@ #include "plume_metal.h" +#ifdef PLUME_PRECOMPILED_METAL_SHADERS +#include "plume_clear.metallib.h" +#include "plume_resolve.metallib.h" +#endif + namespace plume { // MARK: - Constants @@ -3992,6 +3997,21 @@ namespace plume { } void MetalDevice::createResolvePipelineState() { + NS::Error* error = nullptr; + MTL::Library *library = nullptr; + +#ifdef PLUME_PRECOMPILED_METAL_SHADERS + // Load pre-compiled metallib from embedded data + dispatch_data_t dispatchData = dispatch_data_create( + plume_resolve_metallib, + plume_resolve_metallib_size, + nullptr, + DISPATCH_DATA_DESTRUCTOR_DEFAULT + ); + library = mtl->newLibrary(dispatchData, &error); + dispatch_release(dispatchData); +#else + // Fallback: compile shader at runtime const char* resolve_shader = R"( #include using namespace metal; @@ -4019,10 +4039,13 @@ namespace plume { destination.write(color, dstPos); } )"; + library = mtl->newLibrary(NS::String::string(resolve_shader, NS::UTF8StringEncoding), nullptr, &error); +#endif - NS::Error* error = nullptr; - MTL::Library *library = mtl->newLibrary(NS::String::string(resolve_shader, NS::UTF8StringEncoding), nullptr, &error); - assert(library != nullptr && "Failed to create library"); + if (error != nullptr) { + fprintf(stderr, "Failed to create resolve shader library: %s\n", error->localizedDescription()->utf8String()); + } + assert(library != nullptr && "Failed to create resolve shader library"); MTL::Function *resolveFunction = library->newFunction(NS::String::string("msaaResolve", NS::UTF8StringEncoding)); assert(resolveFunction != nullptr && "Failed to create resolve function"); @@ -4031,12 +4054,26 @@ namespace plume { resolveTexturePipelineState = mtl->newComputePipelineState(resolveFunction, &error); assert(resolveTexturePipelineState != nullptr && "Failed to create MSAA resolve pipeline state"); - // Destroy resolveFunction->release(); library->release(); } void MetalDevice::createClearShaderLibrary() { + NS::Error* error = nullptr; + MTL::Library *clearShaderLibrary = nullptr; + +#ifdef PLUME_PRECOMPILED_METAL_SHADERS + // Load pre-compiled metallib from embedded data + dispatch_data_t dispatchData = dispatch_data_create( + plume_clear_metallib, + plume_clear_metallib_size, + nullptr, + DISPATCH_DATA_DESTRUCTOR_DEFAULT + ); + clearShaderLibrary = mtl->newLibrary(dispatchData, &error); + dispatch_release(dispatchData); +#else + // Fallback: compile shader at runtime const char* clear_shader = R"( #include using namespace metal; @@ -4076,13 +4113,13 @@ namespace plume { return out; } )"; + clearShaderLibrary = mtl->newLibrary(NS::String::string(clear_shader, NS::UTF8StringEncoding), nullptr, &error); +#endif - NS::Error* error = nullptr; - MTL::Library *clearShaderLibrary = mtl->newLibrary(NS::String::string(clear_shader, NS::UTF8StringEncoding), nullptr, &error); if (error != nullptr) { - fprintf(stderr, "Error: %s\n", error->localizedDescription()->utf8String()); + fprintf(stderr, "Failed to create clear shader library: %s\n", error->localizedDescription()->utf8String()); } - assert(clearShaderLibrary != nullptr && "Failed to create clear color library"); + assert(clearShaderLibrary != nullptr && "Failed to create clear shader library"); // Create and cache the shader functions clearVertexFunction = clearShaderLibrary->newFunction(MTLSTR("clearVert")); diff --git a/shaders/plume_clear.metal b/shaders/plume_clear.metal new file mode 100644 index 0000000..12af163 --- /dev/null +++ b/shaders/plume_clear.metal @@ -0,0 +1,44 @@ +// +// plume +// +// Copyright (c) 2024 renderbag and contributors. All rights reserved. +// Licensed under the MIT license. See LICENSE file for details. +// + +#include +using namespace metal; + +struct DepthClearFragmentOut { + float depth [[depth(any)]]; +}; + +struct VertexOutput { + float4 position [[position]]; + uint rect_index [[flat]]; +}; + +vertex VertexOutput clearVert(uint vid [[vertex_id]], + uint instance_id [[instance_id]], + constant float2* vertices [[buffer(0)]]) +{ + VertexOutput out; + out.position = float4(vertices[vid], 0, 1); + out.rect_index = instance_id; + return out; +} + +// Color clear fragment shader +fragment float4 clearColorFrag(VertexOutput in [[stage_in]], + constant float4* clearColors [[buffer(0)]]) +{ + return clearColors[in.rect_index]; +} + +// Depth clear fragment shader +fragment DepthClearFragmentOut clearDepthFrag(VertexOutput in [[stage_in]], + constant float* clearDepths [[buffer(0)]]) +{ + DepthClearFragmentOut out; + out.depth = clearDepths[in.rect_index]; + return out; +} diff --git a/shaders/plume_resolve.metal b/shaders/plume_resolve.metal new file mode 100644 index 0000000..92b8b97 --- /dev/null +++ b/shaders/plume_resolve.metal @@ -0,0 +1,35 @@ +// +// plume +// +// Copyright (c) 2024 renderbag and contributors. All rights reserved. +// Licensed under the MIT license. See LICENSE file for details. +// + +#include +using namespace metal; + +struct ResolveParams { + uint2 dstOffset; + uint2 srcOffset; + uint2 resolveSize; +}; + +kernel void msaaResolve( + texture2d_ms source [[texture(0)]], + texture2d destination [[texture(1)]], + constant ResolveParams& params [[buffer(0)]], + uint2 gid [[thread_position_in_grid]]) +{ + if (gid.x >= params.resolveSize.x || gid.y >= params.resolveSize.y) return; + + uint2 dstPos = gid + params.dstOffset; + uint2 srcPos = gid + params.srcOffset; + + float4 color = float4(0); + for (uint s = 0; s < source.get_num_samples(); s++) { + color += source.read(srcPos, s); + } + color /= float(source.get_num_samples()); + + destination.write(color, dstPos); +} diff --git a/tools/file_to_c.cpp b/tools/file_to_c.cpp new file mode 100644 index 0000000..6d43c14 --- /dev/null +++ b/tools/file_to_c.cpp @@ -0,0 +1,105 @@ +// +// plume +// +// Copyright (c) 2024 renderbag and contributors. All rights reserved. +// Licensed under the MIT license. See LICENSE file for details. +// + +#include +#include +#include +#include + +std::vector read_file(const char* path) { + std::ifstream input_file{path, std::ios::binary}; + std::vector ret{}; + + if (!input_file.good()) { + return ret; + } + + // Get the length of the file + input_file.seekg(0, std::ios::end); + ret.resize(input_file.tellg()); + + // Read the file contents into the vector + input_file.seekg(0, std::ios::beg); + input_file.read(ret.data(), ret.size()); + + return ret; +} + +void create_parent_if_needed(const char* path) { + std::filesystem::path parent_path = std::filesystem::path{path}.parent_path(); + if (!parent_path.empty()) { + std::filesystem::create_directories(parent_path); + } +} + +int main(int argc, const char** argv) { + if (argc != 5) { + printf("Usage: %s [input file] [array name] [output C file] [output C header]\n", argv[0]); + return EXIT_SUCCESS; + } + + const char* input_path = argv[1]; + const char* array_name = argv[2]; + const char* output_c_path = argv[3]; + const char* output_h_path = argv[4]; + + // Read the input file's contents + std::vector contents = read_file(input_path); + + if (contents.empty()) { + fprintf(stderr, "Failed to open file %s! (Or it's empty)\n", input_path); + return EXIT_FAILURE; + } + + // Create the output directories if they don't exist + create_parent_if_needed(output_c_path); + create_parent_if_needed(output_h_path); + + // Write the C file with the array + { + std::ofstream output_c_file{output_c_path}; + output_c_file << "// Auto-generated by plume file_to_c - do not edit\n\n"; + output_c_file << "#include \n\n"; + output_c_file << "extern const unsigned char " << array_name << "[" << contents.size() << "];\n"; + output_c_file << "extern const size_t " << array_name << "_size;\n\n"; + output_c_file << "const unsigned char " << array_name << "[" << contents.size() << "] = {"; + + for (size_t i = 0; i < contents.size(); ++i) { + if (i % 16 == 0) { + output_c_file << "\n "; + } + output_c_file << (int)(unsigned char)contents[i] << ","; + if (i % 16 != 15 && i != contents.size() - 1) { + output_c_file << " "; + } + } + + output_c_file << "\n};\n\n"; + output_c_file << "const size_t " << array_name << "_size = " << contents.size() << ";\n"; + } + + // Write the header file with the extern declarations + { + std::ofstream output_h_file{output_h_path}; + output_h_file << "// Auto-generated by plume file_to_c - do not edit\n\n"; + output_h_file << "#ifndef " << array_name << "_H\n"; + output_h_file << "#define " << array_name << "_H\n\n"; + output_h_file << "#include \n\n"; + output_h_file << + "#ifdef __cplusplus\n" + "extern \"C\" {\n" + "#endif\n\n" + "extern const unsigned char " << array_name << "[" << contents.size() << "];\n" + "extern const size_t " << array_name << "_size;\n\n" + "#ifdef __cplusplus\n" + "}\n" + "#endif\n\n" + "#endif // " << array_name << "_H\n"; + } + + return EXIT_SUCCESS; +} From fcafc7579a96bf1b01679d2134db7b4da3706a98 Mon Sep 17 00:00:00 2001 From: David Chavez Date: Mon, 15 Dec 2025 15:39:07 +0100 Subject: [PATCH 2/6] Remove fallback functionality --- cmake/PlumeShaderCompilation.cmake | 6 +- plume_metal.cpp | 93 ++---------------------------- 2 files changed, 8 insertions(+), 91 deletions(-) diff --git a/cmake/PlumeShaderCompilation.cmake b/cmake/PlumeShaderCompilation.cmake index fb5c627..b581639 100644 --- a/cmake/PlumeShaderCompilation.cmake +++ b/cmake/PlumeShaderCompilation.cmake @@ -54,9 +54,10 @@ function(plume_compile_metal_shaders TARGET_NAME) set(H_OUTPUT "${OUTPUT_DIR}/${SHADER_NAME}.metallib.h") # Compile Metal to IR + # Use -mmacosx-version-min to ensure compatibility with older macOS versions add_custom_command( OUTPUT ${IR_OUTPUT} - COMMAND xcrun -sdk macosx metal -o ${IR_OUTPUT} -c ${SHADER_SOURCE} + COMMAND xcrun -sdk macosx metal -mmacosx-version-min=11.0 -o ${IR_OUTPUT} -c ${SHADER_SOURCE} DEPENDS ${SHADER_SOURCE} COMMENT "Compiling ${SHADER_NAME}.metal to IR" VERBATIM @@ -89,7 +90,4 @@ function(plume_compile_metal_shaders TARGET_NAME) # Add include directory for generated headers target_include_directories(${TARGET_NAME} PRIVATE "${OUTPUT_DIR}") - - # Define that we have pre-compiled shaders - target_compile_definitions(${TARGET_NAME} PRIVATE PLUME_PRECOMPILED_METAL_SHADERS) endfunction() diff --git a/plume_metal.cpp b/plume_metal.cpp index 01dc45b..a547850 100644 --- a/plume_metal.cpp +++ b/plume_metal.cpp @@ -18,11 +18,8 @@ #include #include "plume_metal.h" - -#ifdef PLUME_PRECOMPILED_METAL_SHADERS #include "plume_clear.metallib.h" #include "plume_resolve.metallib.h" -#endif namespace plume { // MARK: - Constants @@ -3997,10 +3994,6 @@ namespace plume { } void MetalDevice::createResolvePipelineState() { - NS::Error* error = nullptr; - MTL::Library *library = nullptr; - -#ifdef PLUME_PRECOMPILED_METAL_SHADERS // Load pre-compiled metallib from embedded data dispatch_data_t dispatchData = dispatch_data_create( plume_resolve_metallib, @@ -4008,39 +4001,10 @@ namespace plume { nullptr, DISPATCH_DATA_DESTRUCTOR_DEFAULT ); - library = mtl->newLibrary(dispatchData, &error); - dispatch_release(dispatchData); -#else - // Fallback: compile shader at runtime - const char* resolve_shader = R"( - #include - using namespace metal; - - struct ResolveParams { - uint2 dstOffset; - uint2 srcOffset; - uint2 resolveSize; - }; - kernel void msaaResolve( - texture2d_ms source [[texture(0)]], - texture2d destination [[texture(1)]], - constant ResolveParams& params [[buffer(0)]], - uint2 gid [[thread_position_in_grid]]) - { - if (gid.x >= params.resolveSize.x || gid.y >= params.resolveSize.y) return; - uint2 dstPos = gid + params.dstOffset; - uint2 srcPos = gid + params.srcOffset; - float4 color = float4(0); - for (uint s = 0; s < source.get_num_samples(); s++) { - color += source.read(srcPos, s); - } - color /= float(source.get_num_samples()); - destination.write(color, dstPos); - } - )"; - library = mtl->newLibrary(NS::String::string(resolve_shader, NS::UTF8StringEncoding), nullptr, &error); -#endif + NS::Error* error = nullptr; + MTL::Library *library = mtl->newLibrary(dispatchData, &error); + dispatch_release(dispatchData); if (error != nullptr) { fprintf(stderr, "Failed to create resolve shader library: %s\n", error->localizedDescription()->utf8String()); @@ -4059,10 +4023,6 @@ namespace plume { } void MetalDevice::createClearShaderLibrary() { - NS::Error* error = nullptr; - MTL::Library *clearShaderLibrary = nullptr; - -#ifdef PLUME_PRECOMPILED_METAL_SHADERS // Load pre-compiled metallib from embedded data dispatch_data_t dispatchData = dispatch_data_create( plume_clear_metallib, @@ -4070,51 +4030,10 @@ namespace plume { nullptr, DISPATCH_DATA_DESTRUCTOR_DEFAULT ); - clearShaderLibrary = mtl->newLibrary(dispatchData, &error); - dispatch_release(dispatchData); -#else - // Fallback: compile shader at runtime - const char* clear_shader = R"( - #include - using namespace metal; - - struct DepthClearFragmentOut { - float depth [[depth(any)]]; - }; - - struct VertexOutput { - float4 position [[position]]; - uint rect_index [[flat]]; - }; - - vertex VertexOutput clearVert(uint vid [[vertex_id]], - uint instance_id [[instance_id]], - constant float2* vertices [[buffer(0)]]) - { - VertexOutput out; - out.position = float4(vertices[vid], 0, 1); - out.rect_index = instance_id; - return out; - } - // Color clear fragment shader - fragment float4 clearColorFrag(VertexOutput in [[stage_in]], - constant float4* clearColors [[buffer(0)]]) - { - return clearColors[in.rect_index]; - } - - // Depth clear fragment shader - fragment DepthClearFragmentOut clearDepthFrag(VertexOutput in [[stage_in]], - constant float* clearDepths [[buffer(0)]]) - { - DepthClearFragmentOut out; - out.depth = clearDepths[in.rect_index]; - return out; - } - )"; - clearShaderLibrary = mtl->newLibrary(NS::String::string(clear_shader, NS::UTF8StringEncoding), nullptr, &error); -#endif + NS::Error* error = nullptr; + MTL::Library *clearShaderLibrary = mtl->newLibrary(dispatchData, &error); + dispatch_release(dispatchData); if (error != nullptr) { fprintf(stderr, "Failed to create clear shader library: %s\n", error->localizedDescription()->utf8String()); From 9da75d655fe01a8e62cd1a9c3c8dab422379bd55 Mon Sep 17 00:00:00 2001 From: David Chavez Date: Mon, 15 Dec 2025 16:13:17 +0100 Subject: [PATCH 3/6] Set minimum macOS supported version --- CMakeLists.txt | 6 ++++++ cmake/PlumeShaderCompilation.cmake | 3 +-- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 074771d..b895ba2 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,6 +1,12 @@ cmake_minimum_required(VERSION 3.20) set(CMAKE_CXX_STANDARD 17) set(CMAKE_CXX_VISIBILITY_PRESET hidden) + +if(APPLE AND NOT CMAKE_OSX_DEPLOYMENT_TARGET) + # 10.15 because of our use of supportsFamily() + set(CMAKE_OSX_DEPLOYMENT_TARGET "10.15" CACHE STRING "Minimum macOS deployment target") +endif() + project(plume) if(APPLE) diff --git a/cmake/PlumeShaderCompilation.cmake b/cmake/PlumeShaderCompilation.cmake index b581639..a4a3134 100644 --- a/cmake/PlumeShaderCompilation.cmake +++ b/cmake/PlumeShaderCompilation.cmake @@ -54,10 +54,9 @@ function(plume_compile_metal_shaders TARGET_NAME) set(H_OUTPUT "${OUTPUT_DIR}/${SHADER_NAME}.metallib.h") # Compile Metal to IR - # Use -mmacosx-version-min to ensure compatibility with older macOS versions add_custom_command( OUTPUT ${IR_OUTPUT} - COMMAND xcrun -sdk macosx metal -mmacosx-version-min=11.0 -o ${IR_OUTPUT} -c ${SHADER_SOURCE} + COMMAND xcrun -sdk macosx metal -mmacosx-version-min=${CMAKE_OSX_DEPLOYMENT_TARGET} -o ${IR_OUTPUT} -c ${SHADER_SOURCE} DEPENDS ${SHADER_SOURCE} COMMENT "Compiling ${SHADER_NAME}.metal to IR" VERBATIM From 7a57c948156c93843944697bce1c9c4dc0e8f7f0 Mon Sep 17 00:00:00 2001 From: David Chavez Date: Mon, 15 Dec 2025 16:31:59 +0100 Subject: [PATCH 4/6] Examples can use the shared file_to_c now --- examples/CMakeLists.txt | 3 - examples/cmake/ShaderCompilation.cmake | 54 +++++++++-------- examples/tools/CMakeLists.txt | 1 - examples/tools/file_to_c/CMakeLists.txt | 14 ----- examples/tools/file_to_c/file_to_c.cpp | 80 ------------------------- tools/file_to_c.cpp | 12 ++-- 6 files changed, 33 insertions(+), 131 deletions(-) delete mode 100644 examples/tools/CMakeLists.txt delete mode 100644 examples/tools/file_to_c/CMakeLists.txt delete mode 100644 examples/tools/file_to_c/file_to_c.cpp diff --git a/examples/CMakeLists.txt b/examples/CMakeLists.txt index 91e96a8..69aca59 100644 --- a/examples/CMakeLists.txt +++ b/examples/CMakeLists.txt @@ -6,9 +6,6 @@ set(PLUME_SDL_VULKAN_ENABLED ON CACHE BOOL "Enable SDL Vulkan integration" FORCE # Find SDL2 (required for examples) find_package(SDL2 REQUIRED) -# Add tools directory first (needed for shader compilation) -add_subdirectory(tools) - # Include custom ShaderCompilation module list(APPEND CMAKE_MODULE_PATH ${CMAKE_CURRENT_SOURCE_DIR}/cmake) include(ShaderCompilation) diff --git a/examples/cmake/ShaderCompilation.cmake b/examples/cmake/ShaderCompilation.cmake index 0104218..6bb51ff 100644 --- a/examples/cmake/ShaderCompilation.cmake +++ b/examples/cmake/ShaderCompilation.cmake @@ -1,6 +1,10 @@ # Shader compilation functions for Plume # Using DXC for HLSL to SPIR-V compilation and Metal for macOS +# Build file_to_c tool (shared with plume core) +include(${CMAKE_SOURCE_DIR}/cmake/PlumeShaderCompilation.cmake) +plume_build_file_to_c() + # Fetch DXC for shader compilation include(FetchContent) FetchContent_Declare( @@ -13,7 +17,7 @@ FetchContent_MakeAvailable(dxc) # Set up DXC paths based on platform if(WIN32) set(DXC "${dxc_SOURCE_DIR}/bin/x64/dxc.exe") - + # Dependencies that must be next to the DLL if on Windows if(EXISTS "${dxc_SOURCE_DIR}/bin/x64/dxcompiler.dll") configure_file("${dxc_SOURCE_DIR}/bin/x64/dxcompiler.dll" "${CMAKE_BINARY_DIR}/bin/dxcompiler.dll" COPYONLY) @@ -30,12 +34,12 @@ elseif(APPLE) set(DXC_EXECUTABLE "${dxc_SOURCE_DIR}/bin/arm64/dxc-macos") set(DXC_LIB_DIR "${dxc_SOURCE_DIR}/lib/arm64") endif() - + # Make sure the executable is accessible and has execute permissions if(EXISTS "${DXC_EXECUTABLE}") # Set executable permission if needed execute_process(COMMAND chmod +x "${DXC_EXECUTABLE}") - + # Set DXC command with DYLD_LIBRARY_PATH set(DXC "DYLD_LIBRARY_PATH=${DXC_LIB_DIR}" "${DXC_EXECUTABLE}") else() @@ -50,7 +54,7 @@ else() set(DXC_EXECUTABLE "${dxc_SOURCE_DIR}/bin/arm64/dxc-linux") set(DXC_LIB_DIR "${dxc_SOURCE_DIR}/lib/arm64") endif() - + # Make sure the executable is accessible and has execute permissions if(EXISTS "${DXC_EXECUTABLE}") # Set executable permission if needed @@ -85,10 +89,10 @@ function(build_shader_dxc_impl TARGET_NAME SHADER_SOURCE SHADER_TYPE OUTPUT_NAME set(SHADER_OUTPUT "${CMAKE_BINARY_DIR}/shaders/${OUTPUT_NAME}.hlsl.${OUTPUT_EXT}") set(C_OUTPUT "${CMAKE_BINARY_DIR}/shaders/${OUTPUT_NAME}.hlsl.${OUTPUT_FORMAT}.c") set(H_OUTPUT "${CMAKE_BINARY_DIR}/shaders/${OUTPUT_NAME}.hlsl.${OUTPUT_FORMAT}.h") - + # Create output directory file(MAKE_DIRECTORY "${CMAKE_BINARY_DIR}/shaders") - + # Determine the shader options based on type if(SHADER_TYPE STREQUAL "vertex") set(SHADER_PROFILE "vs_6_0") @@ -107,7 +111,7 @@ function(build_shader_dxc_impl TARGET_NAME SHADER_SOURCE SHADER_TYPE OUTPUT_NAME endif() set(BLOB_NAME "${OUTPUT_NAME}Blob${BLOB_SUFFIX}") - + # Compile using DXC add_custom_command( OUTPUT ${SHADER_OUTPUT} @@ -116,18 +120,18 @@ function(build_shader_dxc_impl TARGET_NAME SHADER_SOURCE SHADER_TYPE OUTPUT_NAME DEPENDS ${SHADER_SOURCE} COMMENT "Compiling ${SHADER_TYPE} shader ${SHADER_SOURCE} to ${OUTPUT_FORMAT} using DXC" ) - + # Generate C header add_custom_command( OUTPUT "${C_OUTPUT}" "${H_OUTPUT}" - COMMAND file_to_c ${SHADER_OUTPUT} "${BLOB_NAME}" "${C_OUTPUT}" "${H_OUTPUT}" - DEPENDS ${SHADER_OUTPUT} file_to_c + COMMAND plume_file_to_c ${SHADER_OUTPUT} "${BLOB_NAME}" "${C_OUTPUT}" "${H_OUTPUT}" + DEPENDS ${SHADER_OUTPUT} plume_file_to_c COMMENT "Generating C header for ${OUTPUT_FORMAT} shader ${OUTPUT_NAME}" ) - + # Add the generated source file to the target target_sources(${TARGET_NAME} PRIVATE "${C_OUTPUT}") - + # Make sure the target can find the generated header target_include_directories(${TARGET_NAME} PRIVATE "${CMAKE_BINARY_DIR}") endfunction() @@ -149,10 +153,10 @@ function(build_shader_metal_impl TARGET_NAME SHADER_SOURCE OUTPUT_NAME) set(IR_OUTPUT "${CMAKE_BINARY_DIR}/shaders/${OUTPUT_NAME}.ir") set(METAL_C_OUTPUT "${CMAKE_BINARY_DIR}/shaders/${OUTPUT_NAME}.metal.c") set(METAL_H_OUTPUT "${CMAKE_BINARY_DIR}/shaders/${OUTPUT_NAME}.metal.h") - + # Create output directory file(MAKE_DIRECTORY "${CMAKE_BINARY_DIR}/shaders") - + # Compile Metal to IR add_custom_command( OUTPUT ${IR_OUTPUT} @@ -160,7 +164,7 @@ function(build_shader_metal_impl TARGET_NAME SHADER_SOURCE OUTPUT_NAME) DEPENDS ${SHADER_SOURCE} COMMENT "Compiling Metal shader to IR" ) - + # Compile IR to metallib add_custom_command( OUTPUT ${METALLIB_OUTPUT} @@ -168,18 +172,18 @@ function(build_shader_metal_impl TARGET_NAME SHADER_SOURCE OUTPUT_NAME) DEPENDS ${IR_OUTPUT} COMMENT "Compiling Metal IR to metallib" ) - + # Generate C header add_custom_command( OUTPUT "${METAL_C_OUTPUT}" "${METAL_H_OUTPUT}" - COMMAND file_to_c ${METALLIB_OUTPUT} "${OUTPUT_NAME}BlobMSL" "${METAL_C_OUTPUT}" "${METAL_H_OUTPUT}" - DEPENDS ${METALLIB_OUTPUT} file_to_c + COMMAND plume_file_to_c ${METALLIB_OUTPUT} "${OUTPUT_NAME}BlobMSL" "${METAL_C_OUTPUT}" "${METAL_H_OUTPUT}" + DEPENDS ${METALLIB_OUTPUT} plume_file_to_c COMMENT "Generating C header for Metal shader ${OUTPUT_NAME}" ) - + # Add the generated source file to the target target_sources(${TARGET_NAME} PRIVATE "${METAL_C_OUTPUT}") - + # Make sure the target can find the generated header target_include_directories(${TARGET_NAME} PRIVATE "${CMAKE_BINARY_DIR}") endfunction() @@ -188,7 +192,7 @@ endfunction() function(compile_shader TARGET_NAME SHADER_SOURCE SHADER_TYPE OUTPUT_NAME ENTRY_POINT) # Get the file extension to determine the shader language get_filename_component(SHADER_EXT ${SHADER_SOURCE} EXT) - + # Compile based on extension if(SHADER_EXT MATCHES ".*\\.metal$") # Compile Metal shader @@ -198,7 +202,7 @@ function(compile_shader TARGET_NAME SHADER_SOURCE SHADER_TYPE OUTPUT_NAME ENTRY_ elseif(SHADER_SOURCE MATCHES ".*\\.hlsl$") # Compile HLSL shader to SPIR-V using DXC build_shader_spirv_impl(${TARGET_NAME} ${SHADER_SOURCE} ${SHADER_TYPE} ${OUTPUT_NAME} ${ENTRY_POINT}) - + # Also compile to DXIL on Windows if(WIN32) build_shader_dxil_impl(${TARGET_NAME} ${SHADER_SOURCE} ${SHADER_TYPE} ${OUTPUT_NAME} ${ENTRY_POINT}) @@ -212,8 +216,8 @@ function(file_to_c_header INPUT_FILE OUTPUT_FILE VARIABLE_NAME) add_custom_command( OUTPUT ${OUTPUT_FILE} COMMAND ${CMAKE_COMMAND} -E make_directory ${CMAKE_BINARY_DIR}/shaders - COMMAND ${CMAKE_BINARY_DIR}/bin/file_to_c ${INPUT_FILE} ${OUTPUT_FILE} ${VARIABLE_NAME} ${VARIABLE_NAME}Size - DEPENDS ${INPUT_FILE} file_to_c + COMMAND plume_file_to_c ${INPUT_FILE} ${VARIABLE_NAME} ${OUTPUT_FILE} ${OUTPUT_FILE} + DEPENDS ${INPUT_FILE} plume_file_to_c COMMENT "Converting ${INPUT_FILE} to C header" ) -endfunction() +endfunction() diff --git a/examples/tools/CMakeLists.txt b/examples/tools/CMakeLists.txt deleted file mode 100644 index eb37de8..0000000 --- a/examples/tools/CMakeLists.txt +++ /dev/null @@ -1 +0,0 @@ -add_subdirectory(file_to_c) diff --git a/examples/tools/file_to_c/CMakeLists.txt b/examples/tools/file_to_c/CMakeLists.txt deleted file mode 100644 index e1db4ab..0000000 --- a/examples/tools/file_to_c/CMakeLists.txt +++ /dev/null @@ -1,14 +0,0 @@ -cmake_minimum_required(VERSION 3.20) -project(file_to_c) -set(CMAKE_CXX_STANDARD 17) - -add_executable(file_to_c file_to_c.cpp) - -if (APPLE) - set_target_properties(file_to_c PROPERTIES - XCODE_ATTRIBUTE_CODE_SIGN_IDENTITY "-" - XCODE_ATTRIBUTE_CODE_SIGN_ENTITLEMENTS ${CMAKE_CURRENT_SOURCE_DIR}/../../plume.entitlements - ) -endif() - -target_link_libraries(file_to_c PRIVATE) diff --git a/examples/tools/file_to_c/file_to_c.cpp b/examples/tools/file_to_c/file_to_c.cpp deleted file mode 100644 index 2da8158..0000000 --- a/examples/tools/file_to_c/file_to_c.cpp +++ /dev/null @@ -1,80 +0,0 @@ -#include -#include -#include -#include - -std::vector read_file(const char* path) { - std::ifstream input_file{path, std::ios::binary}; - std::vector ret{}; - - if (!input_file.good()) { - return ret; - } - - // Get the length of the file - input_file.seekg(0, std::ios::end); - ret.resize(input_file.tellg()); - - // Read the file contents into the vector - input_file.seekg(0, std::ios::beg); - input_file.read(ret.data(), ret.size()); - - return ret; -} - -void create_parent_if_needed(const char* path) { - std::filesystem::path parent_path = std::filesystem::path{path}.parent_path(); - if (!parent_path.empty()) { - std::filesystem::create_directories(parent_path); - } -} - -int main(int argc, const char** argv) { - if (argc != 5) { - printf("Usage: %s [input file] [array name] [output C file] [output C header]\n", argv[0]); - return EXIT_SUCCESS; - } - - const char* input_path = argv[1]; - const char* array_name = argv[2]; - const char* output_c_path = argv[3]; - const char* output_h_path = argv[4]; - - // Read the input file's contents - std::vector contents = read_file(input_path); - - if (contents.empty()) { - fprintf(stderr, "Failed to open file %s! (Or it's empty)\n", input_path); - return EXIT_FAILURE; - } - - // Create the output directories if they don't exist - create_parent_if_needed(output_c_path); - create_parent_if_needed(output_h_path); - - // Write the C file with the array - { - std::ofstream output_c_file{output_c_path}; - output_c_file << "extern const char " << array_name << "[" << contents.size() << "];\n"; - output_c_file << "const char " << array_name << "[" << contents.size() << "] = {"; - - for (char x : contents) { - output_c_file << (int)x << ", "; - } - - output_c_file << "};\n"; - } - - // Write the header file with the extern array - { - std::ofstream output_h_file{output_h_path}; - output_h_file << - "#ifdef __cplusplus\n" - " extern \"C\" {\n" - "#endif\n" - "extern const char " << array_name << "[" << contents.size() << "];\n" - "#ifdef __cplusplus\n" - " }\n" - "#endif\n"; - } -} \ No newline at end of file diff --git a/tools/file_to_c.cpp b/tools/file_to_c.cpp index 6d43c14..174145e 100644 --- a/tools/file_to_c.cpp +++ b/tools/file_to_c.cpp @@ -21,7 +21,7 @@ std::vector read_file(const char* path) { // Get the length of the file input_file.seekg(0, std::ios::end); ret.resize(input_file.tellg()); - + // Read the file contents into the vector input_file.seekg(0, std::ios::beg); input_file.read(ret.data(), ret.size()); @@ -59,13 +59,11 @@ int main(int argc, const char** argv) { create_parent_if_needed(output_c_path); create_parent_if_needed(output_h_path); - // Write the C file with the array + // Write the C file with the array and size { std::ofstream output_c_file{output_c_path}; output_c_file << "// Auto-generated by plume file_to_c - do not edit\n\n"; output_c_file << "#include \n\n"; - output_c_file << "extern const unsigned char " << array_name << "[" << contents.size() << "];\n"; - output_c_file << "extern const size_t " << array_name << "_size;\n\n"; output_c_file << "const unsigned char " << array_name << "[" << contents.size() << "] = {"; for (size_t i = 0; i < contents.size(); ++i) { @@ -86,8 +84,7 @@ int main(int argc, const char** argv) { { std::ofstream output_h_file{output_h_path}; output_h_file << "// Auto-generated by plume file_to_c - do not edit\n\n"; - output_h_file << "#ifndef " << array_name << "_H\n"; - output_h_file << "#define " << array_name << "_H\n\n"; + output_h_file << "#pragma once\n\n"; output_h_file << "#include \n\n"; output_h_file << "#ifdef __cplusplus\n" @@ -97,8 +94,7 @@ int main(int argc, const char** argv) { "extern const size_t " << array_name << "_size;\n\n" "#ifdef __cplusplus\n" "}\n" - "#endif\n\n" - "#endif // " << array_name << "_H\n"; + "#endif\n"; } return EXIT_SUCCESS; From 2a1f96ec3af5801efd8a8d3fe34180f10619dfcc Mon Sep 17 00:00:00 2001 From: David Chavez Date: Mon, 15 Dec 2025 21:54:26 +0100 Subject: [PATCH 5/6] Rename internal shader compilation file --- CMakeLists.txt | 4 ++-- ...lumeShaderCompilation.cmake => PlumeInternalShaders.cmake} | 4 ++-- examples/cmake/ShaderCompilation.cmake | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) rename cmake/{PlumeShaderCompilation.cmake => PlumeInternalShaders.cmake} (96%) diff --git a/CMakeLists.txt b/CMakeLists.txt index b895ba2..337d4a2 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -110,8 +110,8 @@ if(APPLE) ${CMAKE_CURRENT_SOURCE_DIR}/contrib/metal-cpp ) - # Compile and embed Metal shaders - include(${CMAKE_CURRENT_SOURCE_DIR}/cmake/PlumeShaderCompilation.cmake) + # Compile and embed internal Metal shaders + include(${CMAKE_CURRENT_SOURCE_DIR}/cmake/PlumeInternalShaders.cmake) plume_compile_metal_shaders(plume) endif() diff --git a/cmake/PlumeShaderCompilation.cmake b/cmake/PlumeInternalShaders.cmake similarity index 96% rename from cmake/PlumeShaderCompilation.cmake rename to cmake/PlumeInternalShaders.cmake index a4a3134..a48ea13 100644 --- a/cmake/PlumeShaderCompilation.cmake +++ b/cmake/PlumeInternalShaders.cmake @@ -1,5 +1,5 @@ -# Plume internal shader compilation -# Compiles Metal shaders to metallib and embeds them as C arrays +# PlumeInternalShaders.cmake +# Internal shader compilation for Plume's built-in shaders (clear, resolve) # Build the file_to_c tool for the host system function(plume_build_file_to_c) diff --git a/examples/cmake/ShaderCompilation.cmake b/examples/cmake/ShaderCompilation.cmake index 6bb51ff..dcfd13e 100644 --- a/examples/cmake/ShaderCompilation.cmake +++ b/examples/cmake/ShaderCompilation.cmake @@ -2,7 +2,7 @@ # Using DXC for HLSL to SPIR-V compilation and Metal for macOS # Build file_to_c tool (shared with plume core) -include(${CMAKE_SOURCE_DIR}/cmake/PlumeShaderCompilation.cmake) +include(${CMAKE_SOURCE_DIR}/cmake/PlumeInternalShaders.cmake) plume_build_file_to_c() # Fetch DXC for shader compilation From 6d68916db8a6eefe06fa06eb4612de60f3e59953 Mon Sep 17 00:00:00 2001 From: David Chavez Date: Wed, 17 Dec 2025 09:53:00 +0100 Subject: [PATCH 6/6] colocate tools folder --- cmake/PlumeInternalShaders.cmake | 2 +- {tools => cmake/tools}/file_to_c.cpp | 0 2 files changed, 1 insertion(+), 1 deletion(-) rename {tools => cmake/tools}/file_to_c.cpp (100%) diff --git a/cmake/PlumeInternalShaders.cmake b/cmake/PlumeInternalShaders.cmake index a48ea13..3a60d26 100644 --- a/cmake/PlumeInternalShaders.cmake +++ b/cmake/PlumeInternalShaders.cmake @@ -7,7 +7,7 @@ function(plume_build_file_to_c) return() endif() - set(FILE_TO_C_SOURCE "${CMAKE_CURRENT_FUNCTION_LIST_DIR}/../tools/file_to_c.cpp") + set(FILE_TO_C_SOURCE "${CMAKE_CURRENT_FUNCTION_LIST_DIR}/tools/file_to_c.cpp") add_executable(plume_file_to_c ${FILE_TO_C_SOURCE}) set_target_properties(plume_file_to_c PROPERTIES diff --git a/tools/file_to_c.cpp b/cmake/tools/file_to_c.cpp similarity index 100% rename from tools/file_to_c.cpp rename to cmake/tools/file_to_c.cpp