|
| 1 | +# PlumeShaders.cmake |
| 2 | +# Shader compilation helpers for Plume examples |
| 3 | +# |
| 4 | +# Usage (examples): |
| 5 | +# include(path/to/plume/examples/cmake/PlumeShaders.cmake) |
| 6 | +# plume_shaders_init() |
| 7 | +# |
| 8 | +# plume_compile_vertex_shader(my_target shaders/main.vert.hlsl mainVert VSMain) |
| 9 | +# plume_compile_pixel_shader(my_target shaders/main.frag.hlsl mainFrag PSMain) |
| 10 | +# plume_compile_compute_shader(my_target shaders/compute.hlsl computeShader CSMain) |
| 11 | + |
| 12 | +include("${CMAKE_CURRENT_LIST_DIR}/modules/PlumeFileToC.cmake") |
| 13 | +include("${CMAKE_CURRENT_LIST_DIR}/modules/PlumeDXC.cmake") |
| 14 | +include("${CMAKE_CURRENT_LIST_DIR}/modules/PlumeSpirvCross.cmake") |
| 15 | + |
| 16 | +function(_plume_embed TARGET_NAME INPUT_FILE VAR_NAME OUTPUT_C OUTPUT_H) |
| 17 | + plume_build_file_to_c() |
| 18 | + |
| 19 | + get_filename_component(OUT_DIR "${OUTPUT_C}" DIRECTORY) |
| 20 | + file(MAKE_DIRECTORY "${OUT_DIR}") |
| 21 | + |
| 22 | + add_custom_command( |
| 23 | + OUTPUT "${OUTPUT_C}" "${OUTPUT_H}" |
| 24 | + COMMAND plume_file_to_c "${INPUT_FILE}" "${VAR_NAME}" "${OUTPUT_C}" "${OUTPUT_H}" |
| 25 | + DEPENDS "${INPUT_FILE}" plume_file_to_c |
| 26 | + COMMENT "Embedding ${VAR_NAME} from ${INPUT_FILE}" |
| 27 | + VERBATIM |
| 28 | + ) |
| 29 | + |
| 30 | + target_sources(${TARGET_NAME} PRIVATE "${OUTPUT_C}") |
| 31 | + target_include_directories(${TARGET_NAME} PRIVATE "${CMAKE_BINARY_DIR}") |
| 32 | +endfunction() |
| 33 | + |
| 34 | +function(plume_shaders_init) |
| 35 | + if(NOT DEFINED PLUME_DXC_EXECUTABLE) |
| 36 | + plume_fetch_dxc() |
| 37 | + endif() |
| 38 | + |
| 39 | + if(APPLE AND NOT TARGET plume_spirv_cross_msl) |
| 40 | + plume_fetch_spirv_cross() |
| 41 | + endif() |
| 42 | + |
| 43 | + plume_build_file_to_c() |
| 44 | + file(MAKE_DIRECTORY "${CMAKE_BINARY_DIR}/shaders") |
| 45 | +endfunction() |
| 46 | + |
| 47 | +function(_plume_compile_hlsl_impl TARGET_NAME SHADER_SOURCE SHADER_TYPE OUTPUT_NAME OUTPUT_FORMAT ENTRY_POINT) |
| 48 | + cmake_parse_arguments(PARSE_ARGV 6 ARG "" "SHADER_MODEL;OUTPUT_DIR" "INCLUDE_DIRS;EXTRA_ARGS") |
| 49 | + |
| 50 | + plume_get_dxc_command(DXC_CMD) |
| 51 | + |
| 52 | + if(OUTPUT_FORMAT STREQUAL "spirv") |
| 53 | + set(OUTPUT_EXT "spirv") |
| 54 | + set(BLOB_SUFFIX "SPIRV") |
| 55 | + set(FORMAT_FLAGS ${PLUME_DXC_SPV_OPTS}) |
| 56 | + elseif(OUTPUT_FORMAT STREQUAL "dxil") |
| 57 | + set(OUTPUT_EXT "dxil") |
| 58 | + set(BLOB_SUFFIX "DXIL") |
| 59 | + set(FORMAT_FLAGS ${PLUME_DXC_DXIL_OPTS}) |
| 60 | + else() |
| 61 | + message(FATAL_ERROR "Unknown output format: ${OUTPUT_FORMAT}") |
| 62 | + endif() |
| 63 | + |
| 64 | + set(SHADER_MODEL "6_0") |
| 65 | + if(ARG_SHADER_MODEL) |
| 66 | + set(SHADER_MODEL "${ARG_SHADER_MODEL}") |
| 67 | + endif() |
| 68 | + |
| 69 | + if(ARG_OUTPUT_DIR) |
| 70 | + set(OUT_DIR "${ARG_OUTPUT_DIR}") |
| 71 | + else() |
| 72 | + set(OUT_DIR "${CMAKE_BINARY_DIR}/shaders") |
| 73 | + endif() |
| 74 | + file(MAKE_DIRECTORY "${OUT_DIR}") |
| 75 | + |
| 76 | + set(PROFILE "") |
| 77 | + set(DXC_TYPE_ARGS "") |
| 78 | + if(SHADER_TYPE STREQUAL "vertex") |
| 79 | + set(PROFILE "vs_${SHADER_MODEL}") |
| 80 | + set(DXC_TYPE_ARGS "-fvk-invert-y") |
| 81 | + elseif(SHADER_TYPE STREQUAL "pixel" OR SHADER_TYPE STREQUAL "fragment") |
| 82 | + set(PROFILE "ps_${SHADER_MODEL}") |
| 83 | + elseif(SHADER_TYPE STREQUAL "compute") |
| 84 | + set(PROFILE "cs_${SHADER_MODEL}") |
| 85 | + elseif(SHADER_TYPE STREQUAL "geometry") |
| 86 | + set(PROFILE "gs_${SHADER_MODEL}") |
| 87 | + elseif(SHADER_TYPE STREQUAL "library") |
| 88 | + set(PROFILE "lib_${SHADER_MODEL}") |
| 89 | + else() |
| 90 | + message(FATAL_ERROR "Unknown shader type: ${SHADER_TYPE}") |
| 91 | + endif() |
| 92 | + |
| 93 | + set(INCLUDE_FLAGS "") |
| 94 | + foreach(DIR ${ARG_INCLUDE_DIRS}) |
| 95 | + list(APPEND INCLUDE_FLAGS "-I${DIR}") |
| 96 | + endforeach() |
| 97 | + |
| 98 | + if(ENTRY_POINT STREQUAL "") |
| 99 | + set(ENTRY_ARGS "") |
| 100 | + else() |
| 101 | + set(ENTRY_ARGS "-E" "${ENTRY_POINT}") |
| 102 | + endif() |
| 103 | + |
| 104 | + set(OUTPUT_FILE "${OUT_DIR}/${OUTPUT_NAME}.hlsl.${OUTPUT_EXT}") |
| 105 | + add_custom_command( |
| 106 | + OUTPUT "${OUTPUT_FILE}" |
| 107 | + COMMAND ${DXC_CMD} |
| 108 | + ${PLUME_DXC_COMMON_OPTS} |
| 109 | + ${INCLUDE_FLAGS} |
| 110 | + ${DXC_TYPE_ARGS} |
| 111 | + ${ARG_EXTRA_ARGS} |
| 112 | + ${ENTRY_ARGS} |
| 113 | + -T ${PROFILE} |
| 114 | + ${FORMAT_FLAGS} |
| 115 | + -Fo "${OUTPUT_FILE}" |
| 116 | + "${SHADER_SOURCE}" |
| 117 | + DEPENDS "${SHADER_SOURCE}" |
| 118 | + COMMENT "DXC: ${SHADER_SOURCE} -> ${OUTPUT_EXT}" |
| 119 | + VERBATIM |
| 120 | + ) |
| 121 | + |
| 122 | + set(C_OUTPUT "${OUT_DIR}/${OUTPUT_NAME}.hlsl.${OUTPUT_EXT}.c") |
| 123 | + set(H_OUTPUT "${OUT_DIR}/${OUTPUT_NAME}.hlsl.${OUTPUT_EXT}.h") |
| 124 | + _plume_embed(${TARGET_NAME} "${OUTPUT_FILE}" "${OUTPUT_NAME}Blob${BLOB_SUFFIX}" "${C_OUTPUT}" "${H_OUTPUT}") |
| 125 | +endfunction() |
| 126 | + |
| 127 | +function(_plume_compile_spirv_to_metal_impl TARGET_NAME SPIRV_FILE OUTPUT_NAME) |
| 128 | + cmake_parse_arguments(PARSE_ARGV 3 ARG "" "OUTPUT_DIR" "") |
| 129 | + |
| 130 | + if(ARG_OUTPUT_DIR) |
| 131 | + set(OUT_DIR "${ARG_OUTPUT_DIR}") |
| 132 | + else() |
| 133 | + set(OUT_DIR "${CMAKE_BINARY_DIR}/shaders") |
| 134 | + endif() |
| 135 | + file(MAKE_DIRECTORY "${OUT_DIR}") |
| 136 | + |
| 137 | + set(METAL_SOURCE "${OUT_DIR}/${OUTPUT_NAME}.hlsl.metal") |
| 138 | + set(IR_OUTPUT "${OUT_DIR}/${OUTPUT_NAME}.hlsl.ir") |
| 139 | + set(METALLIB_OUTPUT "${OUT_DIR}/${OUTPUT_NAME}.hlsl.metallib") |
| 140 | + set(C_OUTPUT "${OUT_DIR}/${OUTPUT_NAME}.hlsl.metal.c") |
| 141 | + set(H_OUTPUT "${OUT_DIR}/${OUTPUT_NAME}.hlsl.metal.h") |
| 142 | + |
| 143 | + if(CMAKE_OSX_DEPLOYMENT_TARGET) |
| 144 | + set(METAL_VERSION_FLAG "-mmacosx-version-min=${CMAKE_OSX_DEPLOYMENT_TARGET}") |
| 145 | + else() |
| 146 | + set(METAL_VERSION_FLAG "") |
| 147 | + endif() |
| 148 | + |
| 149 | + add_custom_command( |
| 150 | + OUTPUT "${METAL_SOURCE}" |
| 151 | + COMMAND plume_spirv_cross_msl "${SPIRV_FILE}" "${METAL_SOURCE}" |
| 152 | + DEPENDS "${SPIRV_FILE}" plume_spirv_cross_msl |
| 153 | + COMMENT "SPIRV-Cross: ${SPIRV_FILE} -> Metal source" |
| 154 | + VERBATIM |
| 155 | + ) |
| 156 | + |
| 157 | + add_custom_command( |
| 158 | + OUTPUT "${IR_OUTPUT}" |
| 159 | + COMMAND xcrun -sdk macosx metal ${METAL_VERSION_FLAG} -o "${IR_OUTPUT}" -c "${METAL_SOURCE}" |
| 160 | + DEPENDS "${METAL_SOURCE}" |
| 161 | + COMMENT "Compiling Metal shader ${OUTPUT_NAME} to IR" |
| 162 | + VERBATIM |
| 163 | + ) |
| 164 | + |
| 165 | + add_custom_command( |
| 166 | + OUTPUT "${METALLIB_OUTPUT}" |
| 167 | + COMMAND xcrun -sdk macosx metallib "${IR_OUTPUT}" -o "${METALLIB_OUTPUT}" |
| 168 | + DEPENDS "${IR_OUTPUT}" |
| 169 | + COMMENT "Linking ${OUTPUT_NAME} to metallib" |
| 170 | + VERBATIM |
| 171 | + ) |
| 172 | + |
| 173 | + _plume_embed(${TARGET_NAME} "${METALLIB_OUTPUT}" "${OUTPUT_NAME}BlobMSL" |
| 174 | + "${C_OUTPUT}" "${H_OUTPUT}") |
| 175 | +endfunction() |
| 176 | + |
| 177 | +function(_plume_compile_metal_impl TARGET_NAME SHADER_SOURCE OUTPUT_NAME) |
| 178 | + set(IR_OUTPUT "${CMAKE_BINARY_DIR}/shaders/${OUTPUT_NAME}.ir") |
| 179 | + set(METALLIB_OUTPUT "${CMAKE_BINARY_DIR}/shaders/${OUTPUT_NAME}.metallib") |
| 180 | + set(C_OUTPUT "${CMAKE_BINARY_DIR}/shaders/${OUTPUT_NAME}.metal.c") |
| 181 | + set(H_OUTPUT "${CMAKE_BINARY_DIR}/shaders/${OUTPUT_NAME}.metal.h") |
| 182 | + |
| 183 | + if(CMAKE_OSX_DEPLOYMENT_TARGET) |
| 184 | + set(METAL_VERSION_FLAG "-mmacosx-version-min=${CMAKE_OSX_DEPLOYMENT_TARGET}") |
| 185 | + else() |
| 186 | + set(METAL_VERSION_FLAG "") |
| 187 | + endif() |
| 188 | + |
| 189 | + add_custom_command( |
| 190 | + OUTPUT "${IR_OUTPUT}" |
| 191 | + COMMAND xcrun -sdk macosx metal ${METAL_VERSION_FLAG} -o "${IR_OUTPUT}" -c "${SHADER_SOURCE}" |
| 192 | + DEPENDS "${SHADER_SOURCE}" |
| 193 | + COMMENT "Compiling Metal shader ${OUTPUT_NAME} to IR" |
| 194 | + VERBATIM |
| 195 | + ) |
| 196 | + |
| 197 | + add_custom_command( |
| 198 | + OUTPUT "${METALLIB_OUTPUT}" |
| 199 | + COMMAND xcrun -sdk macosx metallib "${IR_OUTPUT}" -o "${METALLIB_OUTPUT}" |
| 200 | + DEPENDS "${IR_OUTPUT}" |
| 201 | + COMMENT "Linking ${OUTPUT_NAME} to metallib" |
| 202 | + VERBATIM |
| 203 | + ) |
| 204 | + |
| 205 | + add_custom_command( |
| 206 | + OUTPUT "${C_OUTPUT}" "${H_OUTPUT}" |
| 207 | + COMMAND plume_file_to_c "${METALLIB_OUTPUT}" "${OUTPUT_NAME}BlobMSL" "${C_OUTPUT}" "${H_OUTPUT}" |
| 208 | + DEPENDS "${METALLIB_OUTPUT}" plume_file_to_c |
| 209 | + COMMENT "Generating C header for Metal shader ${OUTPUT_NAME}" |
| 210 | + VERBATIM |
| 211 | + ) |
| 212 | + |
| 213 | + target_sources(${TARGET_NAME} PRIVATE "${C_OUTPUT}") |
| 214 | + target_include_directories(${TARGET_NAME} PRIVATE "${CMAKE_BINARY_DIR}") |
| 215 | +endfunction() |
| 216 | + |
| 217 | +# Public API |
| 218 | + |
| 219 | +function(plume_compile_shader TARGET_NAME SHADER_SOURCE SHADER_TYPE OUTPUT_NAME ENTRY_POINT) |
| 220 | + cmake_parse_arguments(ARG "SPEC_CONSTANTS" "SHADER_MODEL;OUTPUT_DIR" "INCLUDE_DIRS;EXTRA_ARGS" ${ARGN}) |
| 221 | + |
| 222 | + get_filename_component(SHADER_EXT "${SHADER_SOURCE}" EXT) |
| 223 | + |
| 224 | + if(SHADER_EXT MATCHES "\\.metal$") |
| 225 | + if(APPLE) |
| 226 | + _plume_compile_metal_impl(${TARGET_NAME} "${SHADER_SOURCE}" ${OUTPUT_NAME}) |
| 227 | + endif() |
| 228 | + elseif(SHADER_EXT MATCHES "\\.hlsl$") |
| 229 | + set(IMPL_ARGS "") |
| 230 | + if(ARG_SHADER_MODEL) |
| 231 | + list(APPEND IMPL_ARGS SHADER_MODEL "${ARG_SHADER_MODEL}") |
| 232 | + endif() |
| 233 | + if(ARG_INCLUDE_DIRS) |
| 234 | + list(APPEND IMPL_ARGS INCLUDE_DIRS ${ARG_INCLUDE_DIRS}) |
| 235 | + endif() |
| 236 | + if(ARG_EXTRA_ARGS) |
| 237 | + list(APPEND IMPL_ARGS EXTRA_ARGS ${ARG_EXTRA_ARGS}) |
| 238 | + endif() |
| 239 | + if(ARG_OUTPUT_DIR) |
| 240 | + list(APPEND IMPL_ARGS OUTPUT_DIR "${ARG_OUTPUT_DIR}") |
| 241 | + set(OUT_DIR "${ARG_OUTPUT_DIR}") |
| 242 | + else() |
| 243 | + set(OUT_DIR "${CMAKE_BINARY_DIR}/shaders") |
| 244 | + endif() |
| 245 | + |
| 246 | + _plume_compile_hlsl_impl(${TARGET_NAME} "${SHADER_SOURCE}" ${SHADER_TYPE} ${OUTPUT_NAME} "spirv" ${ENTRY_POINT} ${IMPL_ARGS}) |
| 247 | + |
| 248 | + if(WIN32 AND NOT ARG_SPEC_CONSTANTS) |
| 249 | + _plume_compile_hlsl_impl(${TARGET_NAME} "${SHADER_SOURCE}" ${SHADER_TYPE} ${OUTPUT_NAME} "dxil" ${ENTRY_POINT} ${IMPL_ARGS}) |
| 250 | + endif() |
| 251 | + |
| 252 | + if(APPLE AND TARGET plume_spirv_cross_msl) |
| 253 | + set(SPIRV_FILE "${OUT_DIR}/${OUTPUT_NAME}.hlsl.spirv") |
| 254 | + _plume_compile_spirv_to_metal_impl(${TARGET_NAME} "${SPIRV_FILE}" ${OUTPUT_NAME} OUTPUT_DIR "${OUT_DIR}") |
| 255 | + endif() |
| 256 | + else() |
| 257 | + message(WARNING "Unsupported shader extension '${SHADER_EXT}' for ${SHADER_SOURCE}. Use .hlsl or .metal") |
| 258 | + endif() |
| 259 | +endfunction() |
| 260 | + |
| 261 | +function(plume_compile_vertex_shader TARGET_NAME SHADER_SOURCE OUTPUT_NAME ENTRY_POINT) |
| 262 | + plume_compile_shader(${TARGET_NAME} "${SHADER_SOURCE}" "vertex" ${OUTPUT_NAME} ${ENTRY_POINT} ${ARGN}) |
| 263 | +endfunction() |
| 264 | + |
| 265 | +function(plume_compile_pixel_shader TARGET_NAME SHADER_SOURCE OUTPUT_NAME ENTRY_POINT) |
| 266 | + plume_compile_shader(${TARGET_NAME} "${SHADER_SOURCE}" "pixel" ${OUTPUT_NAME} ${ENTRY_POINT} ${ARGN}) |
| 267 | +endfunction() |
| 268 | + |
| 269 | +function(plume_compile_compute_shader TARGET_NAME SHADER_SOURCE OUTPUT_NAME ENTRY_POINT) |
| 270 | + plume_compile_shader(${TARGET_NAME} "${SHADER_SOURCE}" "compute" ${OUTPUT_NAME} ${ENTRY_POINT} ${ARGN}) |
| 271 | +endfunction() |
| 272 | + |
| 273 | +function(plume_compile_geometry_shader TARGET_NAME SHADER_SOURCE OUTPUT_NAME ENTRY_POINT) |
| 274 | + plume_compile_shader(${TARGET_NAME} "${SHADER_SOURCE}" "geometry" ${OUTPUT_NAME} ${ENTRY_POINT} ${ARGN}) |
| 275 | +endfunction() |
0 commit comments