I'm trying to create a cmake function that automatically recompiles glsl to spirv upon changes to the shader files. Right now direct dependencies work, ie the shaders I use as compile arguments. However I make heavy use of #include
feature that glslc provides, and by default I can't get changes in that stuff to trigger recompile. I made sure that I'm using the Ninja
Right now I have the following CMake function and arguments:
cmake -DCMAKE_BUILD_TYPE=Debug "-DCMAKE_MAKE_PROGRAM=JETBRAINSPATH/bin/ninja/win/ninja.exe" -G Ninja "PATH_TO_CURRENT_DIRECTORY"
function
set(GLSLC "$ENV{VULKAN_SDK}/Bin/glslc")
function(target_shader_function SHADER_TARGET)
foreach (SHADER_SOURCE_FILEPATH ${ARGN})
get_filename_component(SHADER_SOURCE_FILENAME ${SHADER_SOURCE_FILEPATH} NAME)
get_filename_component(SHADER_SOURCE_DIRECTORY ${SHADER_SOURCE_FILEPATH} DIRECTORY)
set(SHADER_TARGET_NAME "${SHADER_TARGET}_${SHADER_SOURCE_FILENAME}")
set(SHADER_BINARY_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/spirv")
set(SHADER_FINAL_BINARY_FILEPATH "${SHADER_BINARY_DIRECTORY}/${SHADER_SOURCE_FILENAME}.spv")
#we can use depfiles instead
#https://stackoverflow.com/questions/60420700/cmake-invocation-of-glslc-with-respect-to-includes-dependencies
add_custom_command(
OUTPUT ${SHADER_FINAL_BINARY_FILEPATH}
DEPENDS ${SHADER_SOURCE_FILEPATH}
DEPFILE ${SHADER_SOURCE_FILEPATH}.d
COMMAND ${CMAKE_COMMAND} -E make_directory ${SHADER_BINARY_DIRECTORY}
COMMAND ${GLSLC} -MD -MF ${SHADER_SOURCE_FILEPATH}.d -O ${SHADER_SOURCE_FILEPATH} -o ${SHADER_FINAL_BINARY_FILEPATH} --target-env=vulkan1.2 -I ${CMAKE_SOURCE_DIR}/shaderutils
DEPENDS ${SHADER_SOURCE_FILEPATH}
# BYPRODUCTS ${SHADER_FINAL_BINARY_FILEPATH} ${SHADER_SOURCE_FILEPATH}.d causes ninja to no longer work
COMMENT "Compiling SPIRV for \nsource: \n\t${SHADER_SOURCE_FILEPATH} \nbinary: \n\t${SHADER_FINAL_BINARY_FILEPATH} \n"
)
add_custom_target(${SHADER_TARGET_NAME} DEPENDS ${SHADER_FINAL_BINARY_FILEPATH} ${SHADER_SOURCE_FILEPATH}.d)
add_dependencies(${SHADER_TARGET} ${SHADER_TARGET_NAME})
endforeach (SHADER_SOURCE_FILEPATH)
endfunction()
and I use it like this:
cmake_minimum_required(VERSION 3.21)
cmake_policy(SET CMP0116 NEW)
project(my_workspace)
add_executable(my_target main.cpp)
...
target_shader_function(my_target
${CMAKE_CURRENT_SOURCE_DIR}/shaders/example.comp
)
main.cpp
#include <iostream>
int main(){
std::cout << "hello world!" << std::endl;
return 0;
}
Again, everything works fine if I change, for example, example.comp
.
However, lets say I have the following shader (lets say that this is example.comp
):
#version 460
#include "fooutils.glsl"
#define WORKGROUP_SIZE 1024
layout (local_size_x = WORKGROUP_SIZE, local_size_y = 1, local_size_z = 1) in;
layout(set = 0, binding = 0) buffer MyBufferBlock{
float data[];
}
void main(){
uint tidx = gl_GlobalInvocationID.x;
data[tidx] += foo(tidx);
}
and I include the following:
#ifndef FOOUTILS_GLSL
#define FOOUTILS_GLSL
float foo(uint tidx){
return mod(tidx, 4.51);
}
#endif //FOOUTILS_GLSL
and I change fooutils.glsl
after everything is compiled once (for example in a way that stops it from compiling),
#ifndef FOOUTILS_GLSL
#define FOOUTILS_GLSL
float foo(uint tidx){
return x;
return mod(tidx, 4.51);
}
#endif //FOOUTILS_GLSL
I don't get a recompile triggered. I had assumed that ninja would use this info to accomplish this, but I haven't seen it happen.
How do I use this depfile to force a recompile when an include dependency changes?