1+ # Capture the directory of this file at include-time. CMAKE_CURRENT_LIST_DIR
2+ # inside a function resolves to the caller's directory, not this file's directory,
3+ # so we must snapshot it here at file scope before the function definition.
4+ set (_Open3DAddComputeShaders_DIR "${CMAKE_CURRENT_LIST_DIR} " CACHE INTERNAL "" )
5+
6+ # open3d_add_compute_shaders(<target>
7+ # OUTPUT_DIRECTORY <dir>
8+ # SOURCES <shader1> [<shader2>...]
9+ # )
10+ #
11+ # For each standalone Vulkan GLSL compute shader source, this function:
12+ # - stages the original .comp file into <dir>
13+ # - compiles it to SPIR-V (.spv) with glslangValidator
14+ # - [on Apple] transpiles the SPIR-V to Metal Shading Language (.metal) with spirv-cross
15+ # On Apple, the .metal files are bundled into a single .metallib file for runtime loading
16+ # (newLibraryWithFile / newLibraryWithData), avoiding newLibraryWithSource.
17+ function (open3d_add_compute_shaders target )
18+ cmake_parse_arguments (PARSE_ARGV 1 ARG "" "OUTPUT_DIRECTORY" "SOURCES" )
19+
20+ if (ARG_UNPARSED_ARGUMENTS)
21+ message (FATAL_ERROR "Unknown arguments: ${ARG_UNPARSED_ARGUMENTS} " )
22+ endif ()
23+
24+ if (ARG_KEYWORDS_MISSING_VALUES)
25+ message (FATAL_ERROR "Missing values for arguments: ${ARG_KEYWORDS_MISSING_VALUES} " )
26+ endif ()
27+
28+ if (NOT ARG_OUTPUT_DIRECTORY)
29+ message (FATAL_ERROR "No output directory for compute shaders specified." )
30+ endif ()
31+
32+ if (NOT ARG_SOURCES)
33+ message (FATAL_ERROR "No compute shader files specified." )
34+ endif ()
35+
36+ get_filename_component (OUTPUT_DIRECTORY_FULL_PATH "${ARG_OUTPUT_DIRECTORY} " ABSOLUTE )
37+ file (MAKE_DIRECTORY ${OUTPUT_DIRECTORY_FULL_PATH} )
38+
39+ find_program (OPEN3D_GLSLANG_VALIDATOR glslangValidator REQUIRED )
40+ if (APPLE )
41+ find_program (OPEN3D_SPIRV_CROSS spirv-cross REQUIRED )
42+ # The scatter pass uses subgroup arithmetic (gl_SubgroupSize, subgroupAdd, etc.).
43+ # Fixing the subgroup size to 32 (Apple Silicon SIMD width) lets the Metal compiler
44+ # treat gl_SubgroupSize as a compile-time constant for loop unrolling.
45+ set (SPIRV_CROSS_EXTRA_FLAGS_gaussian_radix_sort_scatter "--msl-fixed-subgroup-size 32" )
46+ set (GAUSSIAN_METAL_BASENAMES "" )
47+ endif ()
48+
49+ # Default glslangValidator flags: Vulkan 1.3 SPIR-V + LTO.
50+ # Subgroup operations (gl_SubgroupSize, subgroupAdd, etc.) require Vulkan
51+ # SPIR-V (-V) because glslangValidator's OpenGL target (-G) does not support
52+ # them. Most compute shaders use subgroup ops and therefore need -V.
53+ set (GLSLANG_FLAGS -V --target-env vulkan1.3 -gVS)
54+
55+ foreach (shader IN LISTS ARG_SOURCES)
56+ get_filename_component (SHADER_FULL_PATH "${shader} " ABSOLUTE )
57+ get_filename_component (SHADER_NAME "${shader} " NAME )
58+ get_filename_component (SHADER_BASENAME "${shader} " NAME_WE )
59+ set (STAGED_SHADER_FULL_PATH "${OUTPUT_DIRECTORY_FULL_PATH} /${SHADER_NAME} " )
60+ set (STAGED_SPIRV_FULL_PATH "${OUTPUT_DIRECTORY_FULL_PATH} /${SHADER_BASENAME} .spv" )
61+ set (STAGED_METAL_FULL_PATH "${OUTPUT_DIRECTORY_FULL_PATH} /${SHADER_BASENAME} .metal" )
62+
63+ file (RELATIVE_PATH STAGED_SHADER_RELATIVE_PATH
64+ "${CMAKE_CURRENT_BINARY_DIR} " "${STAGED_SHADER_FULL_PATH} " )
65+ file (RELATIVE_PATH STAGED_SPIRV_RELATIVE_PATH
66+ "${CMAKE_CURRENT_BINARY_DIR} " "${STAGED_SPIRV_FULL_PATH} " )
67+ file (RELATIVE_PATH STAGED_METAL_RELATIVE_PATH
68+ "${CMAKE_CURRENT_BINARY_DIR} " "${STAGED_METAL_FULL_PATH} " )
69+
70+ add_custom_command (
71+ OUTPUT ${STAGED_SHADER_FULL_PATH} ${STAGED_SPIRV_FULL_PATH}
72+ COMMAND ${CMAKE_COMMAND} -E copy_if_different
73+ ${SHADER_FULL_PATH} ${STAGED_SHADER_FULL_PATH}
74+ COMMAND ${OPEN3D_GLSLANG_VALIDATOR}
75+ ${GLSLANG_FLAGS}
76+ ${SHADER_FULL_PATH} -o ${STAGED_SPIRV_FULL_PATH}
77+ COMMENT "Compiling compute shader ${SHADER_NAME} to SPIR-V"
78+ MAIN_DEPENDENCY ${shader}
79+ VERBATIM
80+ )
81+ if (APPLE )
82+ set (SPIRV_CROSS_EXTRA_FLAGS ${SPIRV_CROSS_EXTRA_FLAGS_${SHADER_BASENAME} })
83+ file (GENERATE OUTPUT run_spirv_cross_${SHADER_BASENAME}.sh CONTENT
84+ "${OPEN3D_SPIRV_CROSS} \" ${STAGED_SPIRV_FULL_PATH} \" --msl --msl-version 20400 --msl-decoration-binding ${SPIRV_CROSS_EXTRA_FLAGS} \
85+ --rename-entry-point main ${SHADER_BASENAME} _main comp --output \" ${STAGED_METAL_FULL_PATH} \"
86+ sed -i '' 's/#include <metal_stdlib>/#include <metal_stdlib>\\ n#include <metal_simdgroup>/g' \" ${STAGED_METAL_FULL_PATH} \" " )
87+ add_custom_command (
88+ OUTPUT ${STAGED_METAL_FULL_PATH}
89+ COMMAND sh run_spirv_cross_${SHADER_BASENAME}.sh
90+ DEPENDS ${STAGED_SPIRV_FULL_PATH} run_spirv_cross_${SHADER_BASENAME}.sh
91+ COMMENT "Transpiling compute shader ${SHADER_NAME} to Metal Shading Language and adding <metal_simdgroup>"
92+ VERBATIM
93+ )
94+ list (APPEND GAUSSIAN_METAL_BASENAMES "${SHADER_BASENAME} " )
95+ endif ()
96+
97+ list (APPEND STAGED_COMPUTE_SHADERS
98+ "${STAGED_SHADER_FULL_PATH} "
99+ "${STAGED_SPIRV_FULL_PATH} " )
100+ if (APPLE )
101+ list (APPEND STAGED_COMPUTE_SHADERS "${STAGED_METAL_FULL_PATH} " )
102+ endif ()
103+ endforeach ()
104+
105+ # Bundle SPIRV-Cross MSL into a single Metal library for runtime loading
106+ # (newLibraryWithFile / newLibraryWithData), avoiding newLibraryWithSource.
107+ if (APPLE AND GAUSSIAN_METAL_BASENAMES)
108+ set (METALLIB_OUTPUT "${OUTPUT_DIRECTORY_FULL_PATH} /gaussian_splat.metallib" )
109+ set (METAL_AIR_FILES "" )
110+ foreach (BASE IN LISTS GAUSSIAN_METAL_BASENAMES)
111+ set (STAGED_METAL_FULL_PATH "${OUTPUT_DIRECTORY_FULL_PATH} /${BASE} .metal" )
112+ set (STAGED_AIR_FULL_PATH "${OUTPUT_DIRECTORY_FULL_PATH} /${BASE} .air" )
113+ list (APPEND METAL_AIR_FILES "${STAGED_AIR_FULL_PATH} " )
114+ add_custom_command (
115+ OUTPUT ${STAGED_AIR_FULL_PATH}
116+ COMMAND xcrun -sdk macosx metal
117+ -std=macos-metal2.4
118+ -Wno-sometimes-uninitialized
119+ -c ${STAGED_METAL_FULL_PATH}
120+ -o ${STAGED_AIR_FULL_PATH}
121+ DEPENDS ${STAGED_METAL_FULL_PATH}
122+ COMMENT "Metal AIR: ${BASE} .metal"
123+ VERBATIM
124+ )
125+ endforeach ()
126+ # In Xcode 16+, the standalone 'metallib' tool was removed; the 'metal'
127+ # compiler now handles AIR -> metallib linking directly.
128+ add_custom_command (
129+ OUTPUT ${METALLIB_OUTPUT}
130+ COMMAND xcrun -sdk macosx metal ${METAL_AIR_FILES} -o ${METALLIB_OUTPUT}
131+ DEPENDS ${METAL_AIR_FILES}
132+ COMMENT "Linking gaussian_splat.metallib"
133+ VERBATIM
134+ )
135+ list (APPEND STAGED_COMPUTE_SHADERS "${METALLIB_OUTPUT} " )
136+ endif ()
137+
138+ add_custom_target (${target} ALL
139+ DEPENDS ${STAGED_COMPUTE_SHADERS}
140+ )
141+
142+ set_target_properties (${target} PROPERTIES
143+ COMPUTE_SHADER_FILES "${STAGED_COMPUTE_SHADERS} " )
144+ endfunction ()
0 commit comments