From 8a2eaab11db1ae768fd1b6c4add112ae71a8f606 Mon Sep 17 00:00:00 2001 From: David Chavez Date: Thu, 18 Dec 2025 19:25:15 +0100 Subject: [PATCH 1/3] move shader compilation to examples only --- CMakeLists.txt | 48 +- cmake/PlumeShaders.cmake | 491 ------------------ examples/CMakeLists.txt | 2 +- examples/cmake/PlumeShaders.cmake | 272 ++++++++++ .../cmake}/modules/PlumeDXC.cmake | 0 .../cmake}/modules/PlumeFileToC.cmake | 0 .../cmake}/modules/PlumeSpirvCross.cmake | 0 {cmake => examples/cmake}/tools/file_to_c.cpp | 0 .../cmake}/tools/spirv_cross_msl.cpp | 0 9 files changed, 317 insertions(+), 496 deletions(-) delete mode 100644 cmake/PlumeShaders.cmake create mode 100644 examples/cmake/PlumeShaders.cmake rename {cmake => examples/cmake}/modules/PlumeDXC.cmake (100%) rename {cmake => examples/cmake}/modules/PlumeFileToC.cmake (100%) rename {cmake => examples/cmake}/modules/PlumeSpirvCross.cmake (100%) rename {cmake => examples/cmake}/tools/file_to_c.cpp (100%) rename {cmake => examples/cmake}/tools/spirv_cross_msl.cpp (100%) diff --git a/CMakeLists.txt b/CMakeLists.txt index aaf1bde..3433d71 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -13,6 +13,8 @@ if(APPLE) enable_language(OBJC OBJCXX) endif() +include(${CMAKE_CURRENT_SOURCE_DIR}/examples/cmake/modules/PlumeFileToC.cmake) + string(COMPARE EQUAL ${CMAKE_SYSTEM_NAME} "Linux" IS_LINUX) # Project options @@ -110,13 +112,51 @@ if(APPLE) ${CMAKE_CURRENT_SOURCE_DIR}/contrib/metal-cpp ) - # Compile and embed internal Metal shaders - include(${CMAKE_CURRENT_SOURCE_DIR}/cmake/PlumeShaders.cmake) plume_build_file_to_c() file(MAKE_DIRECTORY "${CMAKE_BINARY_DIR}/shaders") - _plume_compile_metal_impl(plume "${CMAKE_CURRENT_SOURCE_DIR}/shaders/plume_clear.metal" plume_clear) - _plume_compile_metal_impl(plume "${CMAKE_CURRENT_SOURCE_DIR}/shaders/plume_resolve.metal" plume_resolve) + function(plume_compile_internal_metal TARGET_NAME SHADER_SOURCE OUTPUT_NAME) + set(IR_OUTPUT "${CMAKE_BINARY_DIR}/shaders/${OUTPUT_NAME}.ir") + set(METALLIB_OUTPUT "${CMAKE_BINARY_DIR}/shaders/${OUTPUT_NAME}.metallib") + set(C_OUTPUT "${CMAKE_BINARY_DIR}/shaders/${OUTPUT_NAME}.metal.c") + set(H_OUTPUT "${CMAKE_BINARY_DIR}/shaders/${OUTPUT_NAME}.metal.h") + + if(CMAKE_OSX_DEPLOYMENT_TARGET) + set(METAL_VERSION_FLAG "-mmacosx-version-min=${CMAKE_OSX_DEPLOYMENT_TARGET}") + else() + set(METAL_VERSION_FLAG "") + endif() + + add_custom_command( + OUTPUT "${IR_OUTPUT}" + COMMAND xcrun -sdk macosx metal ${METAL_VERSION_FLAG} -o "${IR_OUTPUT}" -c "${SHADER_SOURCE}" + DEPENDS "${SHADER_SOURCE}" + COMMENT "Compiling Metal shader ${OUTPUT_NAME} to IR" + VERBATIM + ) + + add_custom_command( + OUTPUT "${METALLIB_OUTPUT}" + COMMAND xcrun -sdk macosx metallib "${IR_OUTPUT}" -o "${METALLIB_OUTPUT}" + DEPENDS "${IR_OUTPUT}" + COMMENT "Linking ${OUTPUT_NAME} to metallib" + VERBATIM + ) + + add_custom_command( + OUTPUT "${C_OUTPUT}" "${H_OUTPUT}" + COMMAND plume_file_to_c "${METALLIB_OUTPUT}" "${OUTPUT_NAME}BlobMSL" "${C_OUTPUT}" "${H_OUTPUT}" + DEPENDS "${METALLIB_OUTPUT}" plume_file_to_c + COMMENT "Generating C header for Metal shader ${OUTPUT_NAME}" + VERBATIM + ) + + target_sources(${TARGET_NAME} PRIVATE "${C_OUTPUT}") + target_include_directories(${TARGET_NAME} PRIVATE "${CMAKE_BINARY_DIR}") + endfunction() + + plume_compile_internal_metal(plume "${CMAKE_CURRENT_SOURCE_DIR}/shaders/plume_clear.metal" plume_clear) + plume_compile_internal_metal(plume "${CMAKE_CURRENT_SOURCE_DIR}/shaders/plume_resolve.metal" plume_resolve) endif() # Add examples if requested diff --git a/cmake/PlumeShaders.cmake b/cmake/PlumeShaders.cmake deleted file mode 100644 index 46408d7..0000000 --- a/cmake/PlumeShaders.cmake +++ /dev/null @@ -1,491 +0,0 @@ -# PlumeShaders.cmake -# Public shader compilation API for Plume RHI -# -# Usage: -# include(path/to/plume/cmake/PlumeShaders.cmake) -# plume_shaders_init() -# -# plume_compile_vertex_shader(my_target shaders/main.vert.hlsl mainVert VSMain) -# plume_compile_pixel_shader(my_target shaders/main.frag.hlsl mainFrag PSMain) -# plume_compile_compute_shader(my_target shaders/compute.hlsl computeShader CSMain) -# -# Advanced usage with extra options: -# plume_compile_pixel_shader(my_target shaders/main.frag.hlsl mainFrag PSMain -# EXTRA_ARGS -D MULTISAMPLING -O0 -# INCLUDE_DIRS ${CMAKE_SOURCE_DIR}/src -# SHADER_MODEL 6_3) -# -# # Spec constants mode (SPIRV + Metal only, no DXIL): -# plume_compile_pixel_shader(my_target shaders/main.frag.hlsl mainFrag PSMain SPEC_CONSTANTS) -# -# # Library shader (DXIL only, Windows): -# plume_compile_library_shader(my_target shaders/lib.hlsl libShader) -# -# Bring your own DXC/SPIRV-Cross (set before calling plume_shaders_init): -# set(PLUME_DXC_EXECUTABLE "/path/to/dxc") -# set(PLUME_DXC_LIB_DIR "/path/to/lib") # macOS/Linux only -# -# Output: -# HLSL shaders compile to: -# - SPIR-V (all platforms): {OUTPUT_NAME}BlobSPIRV in shaders/{OUTPUT_NAME}.hlsl.spirv.h -# - DXIL (Windows only): {OUTPUT_NAME}BlobDXIL in shaders/{OUTPUT_NAME}.hlsl.dxil.h -# - Metal (Apple only): {OUTPUT_NAME}BlobMSL in shaders/{OUTPUT_NAME}.metal.h (via SPIR-V cross-compilation) - -include("${CMAKE_CURRENT_LIST_DIR}/modules/PlumeFileToC.cmake") -include("${CMAKE_CURRENT_LIST_DIR}/modules/PlumeDXC.cmake") -include("${CMAKE_CURRENT_LIST_DIR}/modules/PlumeSpirvCross.cmake") - -# Initialize shader compilation infrastructure -# Call this once before using other plume_compile_* functions -# -# If you want to provide your own tools, set these before calling: -# PLUME_DXC_EXECUTABLE - Path to DXC binary -# PLUME_DXC_LIB_DIR - Path to DXC libraries (macOS/Linux only) -# PLUME_SPIRV_CROSS_LIB_DIR - Path to spirv-cross static libraries -# PLUME_SPIRV_CROSS_INCLUDE_DIR - Path to spirv-cross headers -function(plume_shaders_init) - # Fetch DXC if not already provided - if(NOT DEFINED PLUME_DXC_EXECUTABLE) - plume_fetch_dxc() - endif() - - # Fetch/build spirv-cross on Apple if not already provided - if(APPLE AND NOT TARGET plume_spirv_cross_msl) - plume_fetch_spirv_cross() - endif() - - plume_build_file_to_c() - - # Create output directory - file(MAKE_DIRECTORY "${CMAKE_BINARY_DIR}/shaders") -endfunction() - -# Internal: Compile HLSL to a specific format (spirv or dxil) -# Optional args: INCLUDE_DIRS, EXTRA_ARGS, SHADER_MODEL, OUTPUT_DIR -function(_plume_compile_hlsl_impl TARGET_NAME SHADER_SOURCE SHADER_TYPE OUTPUT_NAME OUTPUT_FORMAT ENTRY_POINT) - # Parse optional arguments - cmake_parse_arguments(PARSE_ARGV 6 ARG "" "SHADER_MODEL;OUTPUT_DIR" "INCLUDE_DIRS;EXTRA_ARGS") - - plume_get_dxc_command(DXC_CMD) - - if(OUTPUT_FORMAT STREQUAL "spirv") - set(OUTPUT_EXT "spv") - set(BLOB_SUFFIX "SPIRV") - set(FORMAT_FLAGS ${PLUME_DXC_SPV_OPTS}) - elseif(OUTPUT_FORMAT STREQUAL "dxil") - set(OUTPUT_EXT "dxil") - set(BLOB_SUFFIX "DXIL") - set(FORMAT_FLAGS ${PLUME_DXC_DXIL_OPTS}) - else() - message(FATAL_ERROR "Unknown output format: ${OUTPUT_FORMAT}") - endif() - - # Use custom output directory if provided - if(ARG_OUTPUT_DIR) - set(OUT_DIR "${ARG_OUTPUT_DIR}") - else() - set(OUT_DIR "${CMAKE_BINARY_DIR}/shaders") - endif() - file(MAKE_DIRECTORY "${OUT_DIR}") - - set(SHADER_OUTPUT "${OUT_DIR}/${OUTPUT_NAME}.hlsl.${OUTPUT_EXT}") - set(C_OUTPUT "${OUT_DIR}/${OUTPUT_NAME}.hlsl.${OUTPUT_FORMAT}.c") - set(H_OUTPUT "${OUT_DIR}/${OUTPUT_NAME}.hlsl.${OUTPUT_FORMAT}.h") - - # Use provided shader model or default to 6_0 - if(ARG_SHADER_MODEL) - set(SM_VERSION "${ARG_SHADER_MODEL}") - else() - set(SM_VERSION "6_0") - endif() - - # Determine shader profile and type-specific args - if(SHADER_TYPE STREQUAL "vertex") - set(SHADER_PROFILE "vs_${SM_VERSION}") - set(DXC_TYPE_ARGS "-fvk-invert-y") - elseif(SHADER_TYPE STREQUAL "pixel" OR SHADER_TYPE STREQUAL "fragment") - set(SHADER_PROFILE "ps_${SM_VERSION}") - set(DXC_TYPE_ARGS "") - elseif(SHADER_TYPE STREQUAL "compute") - set(SHADER_PROFILE "cs_${SM_VERSION}") - set(DXC_TYPE_ARGS "") - elseif(SHADER_TYPE STREQUAL "geometry") - set(SHADER_PROFILE "gs_${SM_VERSION}") - set(DXC_TYPE_ARGS "") - elseif(SHADER_TYPE STREQUAL "ray") - set(SHADER_PROFILE "lib_6_3") - set(DXC_TYPE_ARGS ${PLUME_DXC_RT_OPTS}) - elseif(SHADER_TYPE STREQUAL "library") - set(SHADER_PROFILE "lib_${SM_VERSION}") - set(DXC_TYPE_ARGS "-D;LIBRARY") - else() - message(FATAL_ERROR "Unknown shader type: ${SHADER_TYPE}. Use: vertex, pixel/fragment, compute, geometry, ray, or library") - endif() - - # Build include directory flags - set(INCLUDE_FLAGS "") - foreach(INCLUDE_DIR ${ARG_INCLUDE_DIRS}) - list(APPEND INCLUDE_FLAGS "-I${INCLUDE_DIR}") - endforeach() - - set(BLOB_NAME "${OUTPUT_NAME}Blob${BLOB_SUFFIX}") - - # Build entry point args (library shaders don't have entry points) - if(ENTRY_POINT STREQUAL "") - set(ENTRY_POINT_ARGS "") - else() - set(ENTRY_POINT_ARGS "-E" "${ENTRY_POINT}") - endif() - - # Compile using DXC - add_custom_command( - OUTPUT "${SHADER_OUTPUT}" - COMMAND ${DXC_CMD} ${PLUME_DXC_COMMON_OPTS} ${INCLUDE_FLAGS} ${ENTRY_POINT_ARGS} -T ${SHADER_PROFILE} - ${FORMAT_FLAGS} ${DXC_TYPE_ARGS} ${ARG_EXTRA_ARGS} -Fo "${SHADER_OUTPUT}" "${SHADER_SOURCE}" - DEPENDS "${SHADER_SOURCE}" - COMMENT "Compiling ${SHADER_TYPE} shader ${OUTPUT_NAME} to ${OUTPUT_FORMAT}" - VERBATIM - ) - - # Generate C header - add_custom_command( - OUTPUT "${C_OUTPUT}" "${H_OUTPUT}" - 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_NAME} ${OUTPUT_FORMAT}" - VERBATIM - ) - - target_sources(${TARGET_NAME} PRIVATE "${C_OUTPUT}") - target_include_directories(${TARGET_NAME} PRIVATE "${CMAKE_BINARY_DIR}") -endfunction() - -# Internal: Compile SPIR-V to Metal via spirv-cross -# Optional args: OUTPUT_DIR -# Note: For HLSL sources, OUTPUT_NAME should include .hlsl suffix for proper naming -function(_plume_compile_spirv_to_metal_impl TARGET_NAME SPIRV_FILE OUTPUT_NAME) - cmake_parse_arguments(PARSE_ARGV 3 ARG "" "OUTPUT_DIR" "") - - # Use custom output directory if provided - if(ARG_OUTPUT_DIR) - set(OUT_DIR "${ARG_OUTPUT_DIR}") - else() - set(OUT_DIR "${CMAKE_BINARY_DIR}/shaders") - endif() - file(MAKE_DIRECTORY "${OUT_DIR}") - - # Use OUTPUT_NAME.hlsl for naming to match RT64's expected paths - set(METAL_SOURCE "${OUT_DIR}/${OUTPUT_NAME}.hlsl.metal") - set(IR_OUTPUT "${OUT_DIR}/${OUTPUT_NAME}.hlsl.ir") - set(METALLIB_OUTPUT "${OUT_DIR}/${OUTPUT_NAME}.hlsl.metallib") - set(C_OUTPUT "${OUT_DIR}/${OUTPUT_NAME}.hlsl.metal.c") - set(H_OUTPUT "${OUT_DIR}/${OUTPUT_NAME}.hlsl.metal.h") - - # Get deployment target for Metal compilation - if(CMAKE_OSX_DEPLOYMENT_TARGET) - set(METAL_VERSION_FLAG "-mmacosx-version-min=${CMAKE_OSX_DEPLOYMENT_TARGET}") - else() - set(METAL_VERSION_FLAG "") - endif() - - # Convert SPIR-V to Metal source - add_custom_command( - OUTPUT "${METAL_SOURCE}" - COMMAND plume_spirv_cross_msl "${SPIRV_FILE}" "${METAL_SOURCE}" - DEPENDS "${SPIRV_FILE}" plume_spirv_cross_msl - COMMENT "Converting ${OUTPUT_NAME} SPIR-V to Metal" - VERBATIM - ) - - # Compile Metal to IR - add_custom_command( - OUTPUT "${IR_OUTPUT}" - COMMAND xcrun -sdk macosx metal ${METAL_VERSION_FLAG} -o "${IR_OUTPUT}" -c "${METAL_SOURCE}" - DEPENDS "${METAL_SOURCE}" - COMMENT "Compiling Metal shader ${OUTPUT_NAME} 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 ${OUTPUT_NAME} to metallib" - VERBATIM - ) - - # Generate C header - add_custom_command( - OUTPUT "${C_OUTPUT}" "${H_OUTPUT}" - COMMAND plume_file_to_c "${METALLIB_OUTPUT}" "${OUTPUT_NAME}BlobMSL" "${C_OUTPUT}" "${H_OUTPUT}" - DEPENDS "${METALLIB_OUTPUT}" plume_file_to_c - COMMENT "Generating C header for Metal shader ${OUTPUT_NAME}" - VERBATIM - ) - - target_sources(${TARGET_NAME} PRIVATE "${C_OUTPUT}") - target_include_directories(${TARGET_NAME} PRIVATE "${CMAKE_BINARY_DIR}") -endfunction() - -# Internal: Compile native Metal shader to metallib (for handwritten .metal files) -function(_plume_compile_metal_impl TARGET_NAME SHADER_SOURCE OUTPUT_NAME) - set(IR_OUTPUT "${CMAKE_BINARY_DIR}/shaders/${OUTPUT_NAME}.ir") - set(METALLIB_OUTPUT "${CMAKE_BINARY_DIR}/shaders/${OUTPUT_NAME}.metallib") - set(C_OUTPUT "${CMAKE_BINARY_DIR}/shaders/${OUTPUT_NAME}.metal.c") - set(H_OUTPUT "${CMAKE_BINARY_DIR}/shaders/${OUTPUT_NAME}.metal.h") - - # Get deployment target for Metal compilation - if(CMAKE_OSX_DEPLOYMENT_TARGET) - set(METAL_VERSION_FLAG "-mmacosx-version-min=${CMAKE_OSX_DEPLOYMENT_TARGET}") - else() - set(METAL_VERSION_FLAG "") - endif() - - # Compile Metal to IR - add_custom_command( - OUTPUT "${IR_OUTPUT}" - COMMAND xcrun -sdk macosx metal ${METAL_VERSION_FLAG} -o "${IR_OUTPUT}" -c "${SHADER_SOURCE}" - DEPENDS "${SHADER_SOURCE}" - COMMENT "Compiling Metal shader ${OUTPUT_NAME} 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 ${OUTPUT_NAME} to metallib" - VERBATIM - ) - - # Generate C header - add_custom_command( - OUTPUT "${C_OUTPUT}" "${H_OUTPUT}" - COMMAND plume_file_to_c "${METALLIB_OUTPUT}" "${OUTPUT_NAME}BlobMSL" "${C_OUTPUT}" "${H_OUTPUT}" - DEPENDS "${METALLIB_OUTPUT}" plume_file_to_c - COMMENT "Generating C header for Metal shader ${OUTPUT_NAME}" - VERBATIM - ) - - target_sources(${TARGET_NAME} PRIVATE "${C_OUTPUT}") - target_include_directories(${TARGET_NAME} PRIVATE "${CMAKE_BINARY_DIR}") -endfunction() - -# ============================================================================ -# Public API -# ============================================================================ - -# Compile a shader and add it to a target -# Usage: plume_compile_shader(TARGET SOURCE TYPE OUTPUT_NAME ENTRY_POINT [options]) -# TARGET - CMake target to add shader to -# SOURCE - Path to shader source file (.hlsl or .metal) -# TYPE - Shader type: vertex, pixel, compute, geometry, ray, or library -# OUTPUT_NAME - Base name for output files (e.g., "mainVert") -# ENTRY_POINT - Shader entry point function name (e.g., "VSMain") -# -# Options: -# SPEC_CONSTANTS - Only compile SPIRV + Metal (no DXIL), for specialization constants -# SHADER_MODEL - Shader model version (default: 6_0) -# INCLUDE_DIRS - Additional include directories for DXC -# EXTRA_ARGS - Additional DXC arguments (e.g., -D MULTISAMPLING -O0) -# OUTPUT_DIR - Custom output directory (default: ${CMAKE_BINARY_DIR}/shaders) -function(plume_compile_shader TARGET_NAME SHADER_SOURCE SHADER_TYPE OUTPUT_NAME ENTRY_POINT) - # Parse optional arguments - cmake_parse_arguments(ARG "SPEC_CONSTANTS" "SHADER_MODEL;OUTPUT_DIR" "INCLUDE_DIRS;EXTRA_ARGS" ${ARGN}) - - get_filename_component(SHADER_EXT "${SHADER_SOURCE}" EXT) - - if(SHADER_EXT MATCHES "\\.metal$") - if(APPLE) - _plume_compile_metal_impl(${TARGET_NAME} "${SHADER_SOURCE}" ${OUTPUT_NAME}) - endif() - elseif(SHADER_EXT MATCHES "\\.hlsl$") - # Build optional args to pass to impl - set(IMPL_ARGS "") - if(ARG_SHADER_MODEL) - list(APPEND IMPL_ARGS SHADER_MODEL "${ARG_SHADER_MODEL}") - endif() - if(ARG_INCLUDE_DIRS) - list(APPEND IMPL_ARGS INCLUDE_DIRS ${ARG_INCLUDE_DIRS}) - endif() - if(ARG_EXTRA_ARGS) - list(APPEND IMPL_ARGS EXTRA_ARGS ${ARG_EXTRA_ARGS}) - endif() - if(ARG_OUTPUT_DIR) - list(APPEND IMPL_ARGS OUTPUT_DIR "${ARG_OUTPUT_DIR}") - set(OUT_DIR "${ARG_OUTPUT_DIR}") - else() - set(OUT_DIR "${CMAKE_BINARY_DIR}/shaders") - endif() - - # Always compile to SPIR-V - _plume_compile_hlsl_impl(${TARGET_NAME} "${SHADER_SOURCE}" ${SHADER_TYPE} ${OUTPUT_NAME} "spirv" ${ENTRY_POINT} ${IMPL_ARGS}) - - # Compile to DXIL on Windows (unless SPEC_CONSTANTS mode) - if(WIN32 AND NOT ARG_SPEC_CONSTANTS) - _plume_compile_hlsl_impl(${TARGET_NAME} "${SHADER_SOURCE}" ${SHADER_TYPE} ${OUTPUT_NAME} "dxil" ${ENTRY_POINT} ${IMPL_ARGS}) - endif() - - # Compile SPIR-V to Metal on Apple (if spirv-cross is available) - if(APPLE AND TARGET plume_spirv_cross_msl) - set(SPIRV_FILE "${OUT_DIR}/${OUTPUT_NAME}.hlsl.spv") - _plume_compile_spirv_to_metal_impl(${TARGET_NAME} "${SPIRV_FILE}" ${OUTPUT_NAME} OUTPUT_DIR "${OUT_DIR}") - endif() - else() - message(WARNING "Unsupported shader extension '${SHADER_EXT}' for ${SHADER_SOURCE}. Use .hlsl or .metal") - endif() -endfunction() - -# Compile a vertex shader -# Usage: plume_compile_vertex_shader(TARGET SOURCE OUTPUT_NAME ENTRY_POINT [options]) -# Options: SPEC_CONSTANTS, SHADER_MODEL, INCLUDE_DIRS, EXTRA_ARGS (see plume_compile_shader) -function(plume_compile_vertex_shader TARGET_NAME SHADER_SOURCE OUTPUT_NAME ENTRY_POINT) - plume_compile_shader(${TARGET_NAME} "${SHADER_SOURCE}" "vertex" ${OUTPUT_NAME} ${ENTRY_POINT} ${ARGN}) -endfunction() - -# Compile a pixel/fragment shader -# Usage: plume_compile_pixel_shader(TARGET SOURCE OUTPUT_NAME ENTRY_POINT [options]) -# Options: SPEC_CONSTANTS, SHADER_MODEL, INCLUDE_DIRS, EXTRA_ARGS (see plume_compile_shader) -function(plume_compile_pixel_shader TARGET_NAME SHADER_SOURCE OUTPUT_NAME ENTRY_POINT) - plume_compile_shader(${TARGET_NAME} "${SHADER_SOURCE}" "pixel" ${OUTPUT_NAME} ${ENTRY_POINT} ${ARGN}) -endfunction() - -# Compile a compute shader -# Usage: plume_compile_compute_shader(TARGET SOURCE OUTPUT_NAME ENTRY_POINT [options]) -# Options: SPEC_CONSTANTS, SHADER_MODEL, INCLUDE_DIRS, EXTRA_ARGS (see plume_compile_shader) -function(plume_compile_compute_shader TARGET_NAME SHADER_SOURCE OUTPUT_NAME ENTRY_POINT) - plume_compile_shader(${TARGET_NAME} "${SHADER_SOURCE}" "compute" ${OUTPUT_NAME} ${ENTRY_POINT} ${ARGN}) -endfunction() - -# Compile a geometry shader -# Usage: plume_compile_geometry_shader(TARGET SOURCE OUTPUT_NAME ENTRY_POINT [options]) -# Options: SPEC_CONSTANTS, SHADER_MODEL, INCLUDE_DIRS, EXTRA_ARGS (see plume_compile_shader) -function(plume_compile_geometry_shader TARGET_NAME SHADER_SOURCE OUTPUT_NAME ENTRY_POINT) - plume_compile_shader(${TARGET_NAME} "${SHADER_SOURCE}" "geometry" ${OUTPUT_NAME} ${ENTRY_POINT} ${ARGN}) -endfunction() - -# Compile a ray tracing shader -# Usage: plume_compile_ray_shader(TARGET SOURCE OUTPUT_NAME ENTRY_POINT [options]) -# Options: SHADER_MODEL, INCLUDE_DIRS, EXTRA_ARGS (see plume_compile_shader) -function(plume_compile_ray_shader TARGET_NAME SHADER_SOURCE OUTPUT_NAME ENTRY_POINT) - plume_compile_shader(${TARGET_NAME} "${SHADER_SOURCE}" "ray" ${OUTPUT_NAME} ${ENTRY_POINT} ${ARGN}) -endfunction() - -# Compile a library shader (DXIL only, Windows) -# Usage: plume_compile_library_shader(TARGET SOURCE OUTPUT_NAME [options]) -# Options: SHADER_MODEL, INCLUDE_DIRS, EXTRA_ARGS, OUTPUT_DIR -function(plume_compile_library_shader TARGET_NAME SHADER_SOURCE OUTPUT_NAME) - # Parse optional arguments - cmake_parse_arguments(ARG "" "SHADER_MODEL;OUTPUT_DIR" "INCLUDE_DIRS;EXTRA_ARGS" ${ARGN}) - - if(NOT WIN32) - return() - endif() - - # Build optional args to pass to impl - set(IMPL_ARGS "") - if(ARG_SHADER_MODEL) - list(APPEND IMPL_ARGS SHADER_MODEL "${ARG_SHADER_MODEL}") - else() - list(APPEND IMPL_ARGS SHADER_MODEL "6_3") # Library shaders default to 6_3 - endif() - if(ARG_INCLUDE_DIRS) - list(APPEND IMPL_ARGS INCLUDE_DIRS ${ARG_INCLUDE_DIRS}) - endif() - if(ARG_EXTRA_ARGS) - list(APPEND IMPL_ARGS EXTRA_ARGS ${ARG_EXTRA_ARGS}) - endif() - if(ARG_OUTPUT_DIR) - list(APPEND IMPL_ARGS OUTPUT_DIR "${ARG_OUTPUT_DIR}") - endif() - - # Library shaders don't have an entry point - use empty string - _plume_compile_hlsl_impl(${TARGET_NAME} "${SHADER_SOURCE}" "library" ${OUTPUT_NAME} "dxil" "" ${IMPL_ARGS}) -endfunction() - -# Compile a native Metal shader (Apple only, no-op on other platforms) -# Use this for handwritten .metal files, not for cross-compiled HLSL -# Usage: plume_compile_metal_shader(TARGET SOURCE OUTPUT_NAME) -function(plume_compile_metal_shader TARGET_NAME SHADER_SOURCE OUTPUT_NAME) - if(APPLE) - _plume_compile_metal_impl(${TARGET_NAME} "${SHADER_SOURCE}" ${OUTPUT_NAME}) - endif() -endfunction() - -# Preprocess a shader header file and embed it as text -# Useful for runtime shader compilation where you need the preprocessed source -# Usage: plume_preprocess_shader(TARGET SOURCE OUTPUT_NAME [INCLUDE_DIRS dirs] [OUTPUT_DIR dir] [VAR_NAME name]) -# VAR_NAME - Optional variable name for the embedded data (defaults to OUTPUT_NAME) -function(plume_preprocess_shader TARGET_NAME SHADER_SOURCE OUTPUT_NAME) - cmake_parse_arguments(ARG "" "OUTPUT_DIR;VAR_NAME" "INCLUDE_DIRS" ${ARGN}) - - get_filename_component(SHADER_NAME "${SHADER_SOURCE}" NAME) - - # Use custom output directory if provided - if(ARG_OUTPUT_DIR) - set(OUT_DIR "${ARG_OUTPUT_DIR}") - else() - set(OUT_DIR "${CMAKE_BINARY_DIR}/shaders") - endif() - file(MAKE_DIRECTORY "${OUT_DIR}") - - # Variable name for embedded data (defaults to OUTPUT_NAME) - if(ARG_VAR_NAME) - set(VAR_NAME "${ARG_VAR_NAME}") - else() - set(VAR_NAME "${OUTPUT_NAME}") - endif() - - set(PREPROCESSED_OUTPUT "${OUT_DIR}/${OUTPUT_NAME}.rw") - set(C_OUTPUT "${OUT_DIR}/${OUTPUT_NAME}.rw.c") - set(H_OUTPUT "${OUT_DIR}/${OUTPUT_NAME}.rw.h") - - # Build include directory flags - set(INCLUDE_FLAGS "") - foreach(INCLUDE_DIR ${ARG_INCLUDE_DIRS}) - list(APPEND INCLUDE_FLAGS "-I${INCLUDE_DIR}") - endforeach() - - # Preprocess using C preprocessor - if(CMAKE_CXX_COMPILER_FRONTEND_VARIANT STREQUAL "MSVC") - if(CMAKE_CXX_COMPILER_ID STREQUAL "Clang") - add_custom_command( - OUTPUT "${PREPROCESSED_OUTPUT}" - COMMAND clang -x c -E -P "${SHADER_SOURCE}" -o "${PREPROCESSED_OUTPUT}" ${INCLUDE_FLAGS} - DEPENDS "${SHADER_SOURCE}" - COMMENT "Preprocessing shader ${SHADER_NAME}" - VERBATIM - ) - else() - add_custom_command( - OUTPUT "${PREPROCESSED_OUTPUT}" - COMMAND ${CMAKE_CXX_COMPILER} /Zs /EP "${SHADER_SOURCE}" ${INCLUDE_FLAGS} > "${PREPROCESSED_OUTPUT}" - DEPENDS "${SHADER_SOURCE}" - COMMENT "Preprocessing shader ${SHADER_NAME}" - VERBATIM - ) - endif() - else() - add_custom_command( - OUTPUT "${PREPROCESSED_OUTPUT}" - COMMAND ${CMAKE_CXX_COMPILER} -x c -E -P "${SHADER_SOURCE}" -o "${PREPROCESSED_OUTPUT}" ${INCLUDE_FLAGS} - DEPENDS "${SHADER_SOURCE}" - COMMENT "Preprocessing shader ${SHADER_NAME}" - VERBATIM - ) - endif() - - # Generate C header with text content (use --text for char type compatibility) - add_custom_command( - OUTPUT "${C_OUTPUT}" "${H_OUTPUT}" - COMMAND plume_file_to_c "${PREPROCESSED_OUTPUT}" "${VAR_NAME}Text" "${C_OUTPUT}" "${H_OUTPUT}" --text - DEPENDS "${PREPROCESSED_OUTPUT}" plume_file_to_c - COMMENT "Generating C header for preprocessed shader ${OUTPUT_NAME}" - VERBATIM - ) - - target_sources(${TARGET_NAME} PRIVATE "${C_OUTPUT}") - target_include_directories(${TARGET_NAME} PRIVATE "${CMAKE_BINARY_DIR}") -endfunction() diff --git a/examples/CMakeLists.txt b/examples/CMakeLists.txt index e1c2a18..bae0d0a 100644 --- a/examples/CMakeLists.txt +++ b/examples/CMakeLists.txt @@ -7,7 +7,7 @@ set(PLUME_SDL_VULKAN_ENABLED ON CACHE BOOL "Enable SDL Vulkan integration" FORCE find_package(SDL2 REQUIRED) # Initialize shader compilation -include(${CMAKE_SOURCE_DIR}/cmake/PlumeShaders.cmake) +include(${CMAKE_SOURCE_DIR}/examples/cmake/PlumeShaders.cmake) plume_shaders_init() # Add example subdirectories diff --git a/examples/cmake/PlumeShaders.cmake b/examples/cmake/PlumeShaders.cmake new file mode 100644 index 0000000..457e826 --- /dev/null +++ b/examples/cmake/PlumeShaders.cmake @@ -0,0 +1,272 @@ +# PlumeShaders.cmake +# Shader compilation helpers for Plume examples +# +# Usage (examples): +# include(path/to/plume/examples/cmake/PlumeShaders.cmake) +# plume_shaders_init() +# +# plume_compile_vertex_shader(my_target shaders/main.vert.hlsl mainVert VSMain) +# plume_compile_pixel_shader(my_target shaders/main.frag.hlsl mainFrag PSMain) +# plume_compile_compute_shader(my_target shaders/compute.hlsl computeShader CSMain) + +include("${CMAKE_CURRENT_LIST_DIR}/modules/PlumeFileToC.cmake") +include("${CMAKE_CURRENT_LIST_DIR}/modules/PlumeDXC.cmake") +include("${CMAKE_CURRENT_LIST_DIR}/modules/PlumeSpirvCross.cmake") + +function(_plume_embed TARGET_NAME INPUT_FILE VAR_NAME OUTPUT_C OUTPUT_H) + plume_build_file_to_c() + + get_filename_component(OUT_DIR "${OUTPUT_C}" DIRECTORY) + file(MAKE_DIRECTORY "${OUT_DIR}") + + add_custom_command( + OUTPUT "${OUTPUT_C}" "${OUTPUT_H}" + COMMAND plume_file_to_c "${INPUT_FILE}" "${VAR_NAME}" "${OUTPUT_C}" "${OUTPUT_H}" + DEPENDS "${INPUT_FILE}" plume_file_to_c + COMMENT "Embedding ${VAR_NAME} from ${INPUT_FILE}" + VERBATIM + ) + + target_sources(${TARGET_NAME} PRIVATE "${OUTPUT_C}") + target_include_directories(${TARGET_NAME} PRIVATE "${CMAKE_BINARY_DIR}") +endfunction() + +function(plume_shaders_init) + if(NOT DEFINED PLUME_DXC_EXECUTABLE) + plume_fetch_dxc() + endif() + + if(APPLE AND NOT TARGET plume_spirv_cross_msl) + plume_fetch_spirv_cross() + endif() + + plume_build_file_to_c() + file(MAKE_DIRECTORY "${CMAKE_BINARY_DIR}/shaders") +endfunction() + +function(_plume_compile_hlsl_impl TARGET_NAME SHADER_SOURCE SHADER_TYPE OUTPUT_NAME OUTPUT_FORMAT ENTRY_POINT) + cmake_parse_arguments(PARSE_ARGV 6 ARG "" "SHADER_MODEL;OUTPUT_DIR" "INCLUDE_DIRS;EXTRA_ARGS") + + plume_get_dxc_command(DXC_CMD) + + if(OUTPUT_FORMAT STREQUAL "spirv") + set(OUTPUT_EXT "spv") + set(BLOB_SUFFIX "SPIRV") + set(FORMAT_FLAGS ${PLUME_DXC_SPV_OPTS}) + elseif(OUTPUT_FORMAT STREQUAL "dxil") + set(OUTPUT_EXT "dxil") + set(BLOB_SUFFIX "DXIL") + set(FORMAT_FLAGS ${PLUME_DXC_DXIL_OPTS}) + else() + message(FATAL_ERROR "Unknown output format: ${OUTPUT_FORMAT}") + endif() + + set(SHADER_MODEL "6_0") + if(ARG_SHADER_MODEL) + set(SHADER_MODEL "${ARG_SHADER_MODEL}") + endif() + + if(ARG_OUTPUT_DIR) + set(OUT_DIR "${ARG_OUTPUT_DIR}") + else() + set(OUT_DIR "${CMAKE_BINARY_DIR}/shaders") + endif() + file(MAKE_DIRECTORY "${OUT_DIR}") + + set(PROFILE "") + if(SHADER_TYPE STREQUAL "vertex") + set(PROFILE "vs_${SHADER_MODEL}") + elseif(SHADER_TYPE STREQUAL "pixel" OR SHADER_TYPE STREQUAL "fragment") + set(PROFILE "ps_${SHADER_MODEL}") + elseif(SHADER_TYPE STREQUAL "compute") + set(PROFILE "cs_${SHADER_MODEL}") + elseif(SHADER_TYPE STREQUAL "geometry") + set(PROFILE "gs_${SHADER_MODEL}") + elseif(SHADER_TYPE STREQUAL "library") + set(PROFILE "lib_${SHADER_MODEL}") + else() + message(FATAL_ERROR "Unknown shader type: ${SHADER_TYPE}") + endif() + + set(INCLUDE_FLAGS "") + foreach(DIR ${ARG_INCLUDE_DIRS}) + list(APPEND INCLUDE_FLAGS "-I${DIR}") + endforeach() + + if(ENTRY_POINT STREQUAL "") + set(ENTRY_ARGS "") + else() + set(ENTRY_ARGS "-E" "${ENTRY_POINT}") + endif() + + set(OUTPUT_FILE "${OUT_DIR}/${OUTPUT_NAME}.hlsl.${OUTPUT_EXT}") + add_custom_command( + OUTPUT "${OUTPUT_FILE}" + COMMAND ${DXC_CMD} + ${PLUME_DXC_COMMON_OPTS} + ${INCLUDE_FLAGS} + ${ARG_EXTRA_ARGS} + ${ENTRY_ARGS} + -T ${PROFILE} + ${FORMAT_FLAGS} + -Fo "${OUTPUT_FILE}" + "${SHADER_SOURCE}" + DEPENDS "${SHADER_SOURCE}" + COMMENT "DXC: ${SHADER_SOURCE} -> ${OUTPUT_EXT}" + VERBATIM + ) + + set(C_OUTPUT "${OUT_DIR}/${OUTPUT_NAME}.hlsl.${OUTPUT_EXT}.c") + set(H_OUTPUT "${OUT_DIR}/${OUTPUT_NAME}.hlsl.${OUTPUT_EXT}.h") + _plume_embed(${TARGET_NAME} "${OUTPUT_FILE}" "${OUTPUT_NAME}Blob${BLOB_SUFFIX}" "${C_OUTPUT}" "${H_OUTPUT}") +endfunction() + +function(_plume_compile_spirv_to_metal_impl TARGET_NAME SPIRV_FILE OUTPUT_NAME) + cmake_parse_arguments(PARSE_ARGV 3 ARG "" "OUTPUT_DIR" "") + + if(ARG_OUTPUT_DIR) + set(OUT_DIR "${ARG_OUTPUT_DIR}") + else() + set(OUT_DIR "${CMAKE_BINARY_DIR}/shaders") + endif() + file(MAKE_DIRECTORY "${OUT_DIR}") + + set(METAL_SOURCE "${OUT_DIR}/${OUTPUT_NAME}.hlsl.metal") + set(IR_OUTPUT "${OUT_DIR}/${OUTPUT_NAME}.hlsl.ir") + set(METALLIB_OUTPUT "${OUT_DIR}/${OUTPUT_NAME}.hlsl.metallib") + set(C_OUTPUT "${OUT_DIR}/${OUTPUT_NAME}.hlsl.metal.c") + set(H_OUTPUT "${OUT_DIR}/${OUTPUT_NAME}.hlsl.metal.h") + + if(CMAKE_OSX_DEPLOYMENT_TARGET) + set(METAL_VERSION_FLAG "-mmacosx-version-min=${CMAKE_OSX_DEPLOYMENT_TARGET}") + else() + set(METAL_VERSION_FLAG "") + endif() + + add_custom_command( + OUTPUT "${METAL_SOURCE}" + COMMAND plume_spirv_cross_msl "${SPIRV_FILE}" "${METAL_SOURCE}" + DEPENDS "${SPIRV_FILE}" plume_spirv_cross_msl + COMMENT "SPIRV-Cross: ${SPIRV_FILE} -> Metal source" + VERBATIM + ) + + add_custom_command( + OUTPUT "${IR_OUTPUT}" + COMMAND xcrun -sdk macosx metal ${METAL_VERSION_FLAG} -o "${IR_OUTPUT}" -c "${METAL_SOURCE}" + DEPENDS "${METAL_SOURCE}" + COMMENT "Compiling Metal shader ${OUTPUT_NAME} to IR" + VERBATIM + ) + + add_custom_command( + OUTPUT "${METALLIB_OUTPUT}" + COMMAND xcrun -sdk macosx metallib "${IR_OUTPUT}" -o "${METALLIB_OUTPUT}" + DEPENDS "${IR_OUTPUT}" + COMMENT "Linking ${OUTPUT_NAME} to metallib" + VERBATIM + ) + + _plume_embed(${TARGET_NAME} "${METALLIB_OUTPUT}" "${OUTPUT_NAME}BlobMSL" + "${C_OUTPUT}" "${H_OUTPUT}") +endfunction() + +function(_plume_compile_metal_impl TARGET_NAME SHADER_SOURCE OUTPUT_NAME) + set(IR_OUTPUT "${CMAKE_BINARY_DIR}/shaders/${OUTPUT_NAME}.ir") + set(METALLIB_OUTPUT "${CMAKE_BINARY_DIR}/shaders/${OUTPUT_NAME}.metallib") + set(C_OUTPUT "${CMAKE_BINARY_DIR}/shaders/${OUTPUT_NAME}.metal.c") + set(H_OUTPUT "${CMAKE_BINARY_DIR}/shaders/${OUTPUT_NAME}.metal.h") + + if(CMAKE_OSX_DEPLOYMENT_TARGET) + set(METAL_VERSION_FLAG "-mmacosx-version-min=${CMAKE_OSX_DEPLOYMENT_TARGET}") + else() + set(METAL_VERSION_FLAG "") + endif() + + add_custom_command( + OUTPUT "${IR_OUTPUT}" + COMMAND xcrun -sdk macosx metal ${METAL_VERSION_FLAG} -o "${IR_OUTPUT}" -c "${SHADER_SOURCE}" + DEPENDS "${SHADER_SOURCE}" + COMMENT "Compiling Metal shader ${OUTPUT_NAME} to IR" + VERBATIM + ) + + add_custom_command( + OUTPUT "${METALLIB_OUTPUT}" + COMMAND xcrun -sdk macosx metallib "${IR_OUTPUT}" -o "${METALLIB_OUTPUT}" + DEPENDS "${IR_OUTPUT}" + COMMENT "Linking ${OUTPUT_NAME} to metallib" + VERBATIM + ) + + add_custom_command( + OUTPUT "${C_OUTPUT}" "${H_OUTPUT}" + COMMAND plume_file_to_c "${METALLIB_OUTPUT}" "${OUTPUT_NAME}BlobMSL" "${C_OUTPUT}" "${H_OUTPUT}" + DEPENDS "${METALLIB_OUTPUT}" plume_file_to_c + COMMENT "Generating C header for Metal shader ${OUTPUT_NAME}" + VERBATIM + ) + + target_sources(${TARGET_NAME} PRIVATE "${C_OUTPUT}") + target_include_directories(${TARGET_NAME} PRIVATE "${CMAKE_BINARY_DIR}") +endfunction() + +# Public API + +function(plume_compile_shader TARGET_NAME SHADER_SOURCE SHADER_TYPE OUTPUT_NAME ENTRY_POINT) + cmake_parse_arguments(ARG "SPEC_CONSTANTS" "SHADER_MODEL;OUTPUT_DIR" "INCLUDE_DIRS;EXTRA_ARGS" ${ARGN}) + + get_filename_component(SHADER_EXT "${SHADER_SOURCE}" EXT) + + if(SHADER_EXT MATCHES "\\.metal$") + if(APPLE) + _plume_compile_metal_impl(${TARGET_NAME} "${SHADER_SOURCE}" ${OUTPUT_NAME}) + endif() + elseif(SHADER_EXT MATCHES "\\.hlsl$") + set(IMPL_ARGS "") + if(ARG_SHADER_MODEL) + list(APPEND IMPL_ARGS SHADER_MODEL "${ARG_SHADER_MODEL}") + endif() + if(ARG_INCLUDE_DIRS) + list(APPEND IMPL_ARGS INCLUDE_DIRS ${ARG_INCLUDE_DIRS}) + endif() + if(ARG_EXTRA_ARGS) + list(APPEND IMPL_ARGS EXTRA_ARGS ${ARG_EXTRA_ARGS}) + endif() + if(ARG_OUTPUT_DIR) + list(APPEND IMPL_ARGS OUTPUT_DIR "${ARG_OUTPUT_DIR}") + set(OUT_DIR "${ARG_OUTPUT_DIR}") + else() + set(OUT_DIR "${CMAKE_BINARY_DIR}/shaders") + endif() + + _plume_compile_hlsl_impl(${TARGET_NAME} "${SHADER_SOURCE}" ${SHADER_TYPE} ${OUTPUT_NAME} "spirv" ${ENTRY_POINT} ${IMPL_ARGS}) + + if(WIN32 AND NOT ARG_SPEC_CONSTANTS) + _plume_compile_hlsl_impl(${TARGET_NAME} "${SHADER_SOURCE}" ${SHADER_TYPE} ${OUTPUT_NAME} "dxil" ${ENTRY_POINT} ${IMPL_ARGS}) + endif() + + if(APPLE AND TARGET plume_spirv_cross_msl) + set(SPIRV_FILE "${OUT_DIR}/${OUTPUT_NAME}.hlsl.spv") + _plume_compile_spirv_to_metal_impl(${TARGET_NAME} "${SPIRV_FILE}" ${OUTPUT_NAME} OUTPUT_DIR "${OUT_DIR}") + endif() + else() + message(WARNING "Unsupported shader extension '${SHADER_EXT}' for ${SHADER_SOURCE}. Use .hlsl or .metal") + endif() +endfunction() + +function(plume_compile_vertex_shader TARGET_NAME SHADER_SOURCE OUTPUT_NAME ENTRY_POINT) + plume_compile_shader(${TARGET_NAME} "${SHADER_SOURCE}" "vertex" ${OUTPUT_NAME} ${ENTRY_POINT} ${ARGN}) +endfunction() + +function(plume_compile_pixel_shader TARGET_NAME SHADER_SOURCE OUTPUT_NAME ENTRY_POINT) + plume_compile_shader(${TARGET_NAME} "${SHADER_SOURCE}" "pixel" ${OUTPUT_NAME} ${ENTRY_POINT} ${ARGN}) +endfunction() + +function(plume_compile_compute_shader TARGET_NAME SHADER_SOURCE OUTPUT_NAME ENTRY_POINT) + plume_compile_shader(${TARGET_NAME} "${SHADER_SOURCE}" "compute" ${OUTPUT_NAME} ${ENTRY_POINT} ${ARGN}) +endfunction() + +function(plume_compile_geometry_shader TARGET_NAME SHADER_SOURCE OUTPUT_NAME ENTRY_POINT) + plume_compile_shader(${TARGET_NAME} "${SHADER_SOURCE}" "geometry" ${OUTPUT_NAME} ${ENTRY_POINT} ${ARGN}) +endfunction() diff --git a/cmake/modules/PlumeDXC.cmake b/examples/cmake/modules/PlumeDXC.cmake similarity index 100% rename from cmake/modules/PlumeDXC.cmake rename to examples/cmake/modules/PlumeDXC.cmake diff --git a/cmake/modules/PlumeFileToC.cmake b/examples/cmake/modules/PlumeFileToC.cmake similarity index 100% rename from cmake/modules/PlumeFileToC.cmake rename to examples/cmake/modules/PlumeFileToC.cmake diff --git a/cmake/modules/PlumeSpirvCross.cmake b/examples/cmake/modules/PlumeSpirvCross.cmake similarity index 100% rename from cmake/modules/PlumeSpirvCross.cmake rename to examples/cmake/modules/PlumeSpirvCross.cmake diff --git a/cmake/tools/file_to_c.cpp b/examples/cmake/tools/file_to_c.cpp similarity index 100% rename from cmake/tools/file_to_c.cpp rename to examples/cmake/tools/file_to_c.cpp diff --git a/cmake/tools/spirv_cross_msl.cpp b/examples/cmake/tools/spirv_cross_msl.cpp similarity index 100% rename from cmake/tools/spirv_cross_msl.cpp rename to examples/cmake/tools/spirv_cross_msl.cpp From 37d6bcb25e9512e308aabbe660fcff0145854d9a Mon Sep 17 00:00:00 2001 From: David Chavez Date: Thu, 18 Dec 2025 19:35:16 +0100 Subject: [PATCH 2/3] =?UTF-8?q?Don=E2=80=99t=20use=20precompiled=20shaders?= =?UTF-8?q?=20for=20Metal?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- CMakeLists.txt | 48 ----------------- plume_metal.cpp | 100 ++++++++++++++++++++++++++---------- shaders/plume_clear.metal | 44 ---------------- shaders/plume_resolve.metal | 35 ------------- 4 files changed, 72 insertions(+), 155 deletions(-) delete mode 100644 shaders/plume_clear.metal delete mode 100644 shaders/plume_resolve.metal diff --git a/CMakeLists.txt b/CMakeLists.txt index 3433d71..0789c20 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -13,8 +13,6 @@ if(APPLE) enable_language(OBJC OBJCXX) endif() -include(${CMAKE_CURRENT_SOURCE_DIR}/examples/cmake/modules/PlumeFileToC.cmake) - string(COMPARE EQUAL ${CMAKE_SYSTEM_NAME} "Linux" IS_LINUX) # Project options @@ -111,52 +109,6 @@ if(APPLE) target_include_directories(plume PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/contrib/metal-cpp ) - - plume_build_file_to_c() - file(MAKE_DIRECTORY "${CMAKE_BINARY_DIR}/shaders") - - function(plume_compile_internal_metal TARGET_NAME SHADER_SOURCE OUTPUT_NAME) - set(IR_OUTPUT "${CMAKE_BINARY_DIR}/shaders/${OUTPUT_NAME}.ir") - set(METALLIB_OUTPUT "${CMAKE_BINARY_DIR}/shaders/${OUTPUT_NAME}.metallib") - set(C_OUTPUT "${CMAKE_BINARY_DIR}/shaders/${OUTPUT_NAME}.metal.c") - set(H_OUTPUT "${CMAKE_BINARY_DIR}/shaders/${OUTPUT_NAME}.metal.h") - - if(CMAKE_OSX_DEPLOYMENT_TARGET) - set(METAL_VERSION_FLAG "-mmacosx-version-min=${CMAKE_OSX_DEPLOYMENT_TARGET}") - else() - set(METAL_VERSION_FLAG "") - endif() - - add_custom_command( - OUTPUT "${IR_OUTPUT}" - COMMAND xcrun -sdk macosx metal ${METAL_VERSION_FLAG} -o "${IR_OUTPUT}" -c "${SHADER_SOURCE}" - DEPENDS "${SHADER_SOURCE}" - COMMENT "Compiling Metal shader ${OUTPUT_NAME} to IR" - VERBATIM - ) - - add_custom_command( - OUTPUT "${METALLIB_OUTPUT}" - COMMAND xcrun -sdk macosx metallib "${IR_OUTPUT}" -o "${METALLIB_OUTPUT}" - DEPENDS "${IR_OUTPUT}" - COMMENT "Linking ${OUTPUT_NAME} to metallib" - VERBATIM - ) - - add_custom_command( - OUTPUT "${C_OUTPUT}" "${H_OUTPUT}" - COMMAND plume_file_to_c "${METALLIB_OUTPUT}" "${OUTPUT_NAME}BlobMSL" "${C_OUTPUT}" "${H_OUTPUT}" - DEPENDS "${METALLIB_OUTPUT}" plume_file_to_c - COMMENT "Generating C header for Metal shader ${OUTPUT_NAME}" - VERBATIM - ) - - target_sources(${TARGET_NAME} PRIVATE "${C_OUTPUT}") - target_include_directories(${TARGET_NAME} PRIVATE "${CMAKE_BINARY_DIR}") - endfunction() - - plume_compile_internal_metal(plume "${CMAKE_CURRENT_SOURCE_DIR}/shaders/plume_clear.metal" plume_clear) - plume_compile_internal_metal(plume "${CMAKE_CURRENT_SOURCE_DIR}/shaders/plume_resolve.metal" plume_resolve) endif() # Add examples if requested diff --git a/plume_metal.cpp b/plume_metal.cpp index dc54782..44d8b84 100644 --- a/plume_metal.cpp +++ b/plume_metal.cpp @@ -18,8 +18,6 @@ #include #include "plume_metal.h" -#include "shaders/plume_clear.metal.h" -#include "shaders/plume_resolve.metal.h" namespace plume { // MARK: - Constants @@ -3995,22 +3993,37 @@ namespace plume { } void MetalDevice::createResolvePipelineState() { - // Load pre-compiled metallib from embedded data - dispatch_data_t dispatchData = dispatch_data_create( - plume_resolveBlobMSL, - plume_resolveBlobMSL_size, - nullptr, - DISPATCH_DATA_DESTRUCTOR_DEFAULT - ); + const char* resolve_shader = R"( + #include + using namespace metal; + + struct ResolveParams { + uint2 dstOffset; + uint2 srcOffset; + uint2 resolveSize; + }; - NS::Error* error = nullptr; - MTL::Library *library = mtl->newLibrary(dispatchData, &error); - dispatch_release(dispatchData); + 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); + } + )"; - 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"); + 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"); MTL::Function *resolveFunction = library->newFunction(NS::String::string("msaaResolve", NS::UTF8StringEncoding)); assert(resolveFunction != nullptr && "Failed to create resolve function"); @@ -4019,27 +4032,58 @@ 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() { - // Load pre-compiled metallib from embedded data - dispatch_data_t dispatchData = dispatch_data_create( - plume_clearBlobMSL, - plume_clearBlobMSL_size, - nullptr, - DISPATCH_DATA_DESTRUCTOR_DEFAULT - ); + const char* clear_shader = R"( + #include + using namespace metal; - NS::Error* error = nullptr; - MTL::Library *clearShaderLibrary = mtl->newLibrary(dispatchData, &error); - dispatch_release(dispatchData); + 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; + } + )"; + + NS::Error* error = nullptr; + MTL::Library *clearShaderLibrary = mtl->newLibrary(NS::String::string(clear_shader, NS::UTF8StringEncoding), nullptr, &error); if (error != nullptr) { - fprintf(stderr, "Failed to create clear shader library: %s\n", error->localizedDescription()->utf8String()); + fprintf(stderr, "Error: %s\n", error->localizedDescription()->utf8String()); } - assert(clearShaderLibrary != nullptr && "Failed to create clear shader library"); + assert(clearShaderLibrary != nullptr && "Failed to create clear color library"); // Create and cache the shader functions clearVertexFunction = clearShaderLibrary->newFunction(MTLSTR("clearVert")); diff --git a/shaders/plume_clear.metal b/shaders/plume_clear.metal deleted file mode 100644 index 12af163..0000000 --- a/shaders/plume_clear.metal +++ /dev/null @@ -1,44 +0,0 @@ -// -// 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 deleted file mode 100644 index 92b8b97..0000000 --- a/shaders/plume_resolve.metal +++ /dev/null @@ -1,35 +0,0 @@ -// -// 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); -} From c1856f60a0cf63950362a2fac880c3db0499e93c Mon Sep 17 00:00:00 2001 From: David Chavez Date: Thu, 18 Dec 2025 19:48:01 +0100 Subject: [PATCH 3/3] Fix compilation --- examples/cmake/PlumeShaders.cmake | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/examples/cmake/PlumeShaders.cmake b/examples/cmake/PlumeShaders.cmake index 457e826..85cd10e 100644 --- a/examples/cmake/PlumeShaders.cmake +++ b/examples/cmake/PlumeShaders.cmake @@ -50,7 +50,7 @@ function(_plume_compile_hlsl_impl TARGET_NAME SHADER_SOURCE SHADER_TYPE OUTPUT_N plume_get_dxc_command(DXC_CMD) if(OUTPUT_FORMAT STREQUAL "spirv") - set(OUTPUT_EXT "spv") + set(OUTPUT_EXT "spirv") set(BLOB_SUFFIX "SPIRV") set(FORMAT_FLAGS ${PLUME_DXC_SPV_OPTS}) elseif(OUTPUT_FORMAT STREQUAL "dxil") @@ -74,8 +74,10 @@ function(_plume_compile_hlsl_impl TARGET_NAME SHADER_SOURCE SHADER_TYPE OUTPUT_N file(MAKE_DIRECTORY "${OUT_DIR}") set(PROFILE "") + set(DXC_TYPE_ARGS "") if(SHADER_TYPE STREQUAL "vertex") set(PROFILE "vs_${SHADER_MODEL}") + set(DXC_TYPE_ARGS "-fvk-invert-y") elseif(SHADER_TYPE STREQUAL "pixel" OR SHADER_TYPE STREQUAL "fragment") set(PROFILE "ps_${SHADER_MODEL}") elseif(SHADER_TYPE STREQUAL "compute") @@ -105,6 +107,7 @@ function(_plume_compile_hlsl_impl TARGET_NAME SHADER_SOURCE SHADER_TYPE OUTPUT_N COMMAND ${DXC_CMD} ${PLUME_DXC_COMMON_OPTS} ${INCLUDE_FLAGS} + ${DXC_TYPE_ARGS} ${ARG_EXTRA_ARGS} ${ENTRY_ARGS} -T ${PROFILE} @@ -247,7 +250,7 @@ function(plume_compile_shader TARGET_NAME SHADER_SOURCE SHADER_TYPE OUTPUT_NAME endif() if(APPLE AND TARGET plume_spirv_cross_msl) - set(SPIRV_FILE "${OUT_DIR}/${OUTPUT_NAME}.hlsl.spv") + set(SPIRV_FILE "${OUT_DIR}/${OUTPUT_NAME}.hlsl.spirv") _plume_compile_spirv_to_metal_impl(${TARGET_NAME} "${SPIRV_FILE}" ${OUTPUT_NAME} OUTPUT_DIR "${OUT_DIR}") endif() else()