set(SHADER_MODEL "6_5") set(DXC ${PROJECT_SOURCE_DIR}/../contrib/dxc_2025_07_14/bin/x64/dxc.exe) set(SHADERS_PATH ${CMAKE_CURRENT_SOURCE_DIR}) set(BUILD_DIR "${CMAKE_CURRENT_BINARY_DIR}") # Compile HLSL to binary DXIL. # # This defines a custom command so that the shader is compiled at cmake build # instead of configure. # # hlsl_file: "foo.hlsl"; just the file name, not the path. # shader_type: "vs", "ps", "cs", etc. # entry_point: "main", etc. function(compile_shader hlsl_file shader_type entry_point dxil_file) #string(REPLACE ".hlsl" ".dxil" dxil_file ${hlsl_file}) set(hlsl_path "${SHADERS_PATH}/${hlsl_file}") set(dxil_path "${BUILD_DIR}/${dxil_file}") set(target_profile "${shader_type}_${SHADER_MODEL}") message("COMPILING ${dxil_path}") message("DXC = ${DXC}") add_custom_command( OUTPUT ${dxil_path} COMMAND ${DXC} -T ${target_profile} -E ${entry_point} ${hlsl_path} -Fo ${dxil_path} WORKING_DIRECTORY ${BUILD_DIR} DEPENDS ${hlsl_path} COMMENT "Generating ${dxil_path}") endfunction() # Inline a binary file into C code. # This is a workaround for the lack of C23 #embed in MSVC. # # identifier: Identifier to use for the global array that will contain the file, # the generated file names, and the cmake target. # file_path: Path to the binary file to embed. # # Reference: https://github.com/andoalon/embed-binaries function(generate_c file_path identifier out_header_path out_source_path) file(READ "${file_path}" file_contents HEX) string(LENGTH "${file_contents}" file_contents_length) math(EXPR file_bytes "${file_contents_length} / 2") set(bytes_per_line 32) string(REPEAT "[0-9a-f]" ${bytes_per_line} column_pattern) string(REGEX REPLACE "(${column_pattern})" "\\1\n" code "${file_contents}") string(REGEX REPLACE "([0-9a-f][0-9a-f])" "0x\\1," code "${code}") set(declaration "const uint8_t ${identifier}[${file_bytes}]") string(APPEND header "#pragma once\n\n#include \n\nextern ${declaration}\;\n") string(APPEND implementation "#include \"${identifier}.h\"\n\n${declaration} = {\n${code}}\;\n") file(WRITE "${BUILD_DIR}/${identifier}.h" ${header}) file(WRITE "${BUILD_DIR}/${identifier}.c" ${implementation}) endfunction() # Create a target that embeds the given DXIL file in a C header/source. # Like compile_shader, this just adds the target so that the embedding is done # at cmake build and not configure. function(create_c_target dxil_file identifier) set(dxil_path "${BUILD_DIR}/${dxil_file}") string(REPLACE ".dxil" ".h" header_path ${dxil_path}) string(REPLACE ".dxil" ".c" source_path ${dxil_path}) add_custom_command( OUTPUT "${header_path}" "${source_path}" COMMAND ${CMAKE_COMMAND} -D dxil_path=${dxil_path} -D identifier=${identifier} -D out_header_path=${header_path} -D out_source_path=${source_path} -P ${CMAKE_CURRENT_LIST_FILE} WORKING_DIRECTORY ${BUILD_DIR} DEPENDS ${dxil_path} COMMENT "Generating ${source_path}" ) endfunction() # Running in script mode. # https://stackoverflow.com/questions/51427538/cmake-test-if-i-am-in-scripting-mode # # When running in script mode, we embed the binary DXIL into a generated C file. if(CMAKE_SCRIPT_MODE_FILE AND NOT CMAKE_PARENT_LIST_FILE) foreach(variable "dxil_path" "identifier" "out_header_path" "out_source_path") if (NOT DEFINED ${variable}) message(FATAL_ERROR "'${variable}' is not defined") endif() endforeach() generate_c("${dxil_path}" ${identifier} "${out_header_path}" "${out_source_path}") else() compile_shader("imm.hlsl" "vs" "vs" "imm_vs.dxil") compile_shader("imm.hlsl" "ps" "ps" "imm_ps.dxil") create_c_target("imm_vs.dxil" "imm_vs") create_c_target("imm_ps.dxil" "imm_ps") add_library(shaders "${BUILD_DIR}/imm_ps.c" "${BUILD_DIR}/imm_ps.h" "${BUILD_DIR}/imm_vs.c" "${BUILD_DIR}/imm_vs.h") target_include_directories(shaders PUBLIC ${BUILD_DIR}) endif()