#ifndef DC_SHADER_UTILS_H
#define DC_SHADER_UTILS_H
// Reusable helpers for compiling GLSL compute shaders on a local RenderingDevice.
// Extracted from dc_compute.cpp patterns for use across dc_worker and dc_compute.
#include <godot_cpp/classes/rendering_device.hpp>
#include <godot_cpp/classes/rd_shader_source.hpp>
#include <godot_cpp/classes/rd_shader_spirv.hpp>
#include <godot_cpp/classes/file_access.hpp>
#include <godot_cpp/variant/rid.hpp>
#include <godot_cpp/variant/utility_functions.hpp>
using namespace godot;
namespace ShaderUtils {
// Load GLSL source from a file path (res:// or absolute).
// Returns empty string on failure.
inline String load_shader_source(const String &path) {
Ref<FileAccess> f = FileAccess::open(path, FileAccess::READ);
if (f.is_null()) {
UtilityFunctions::print("[ShaderUtils] Failed to open shader: ", path);
return String();
}
String src = f->get_as_text();
f->close();
return src;
}
// Compile GLSL compute source to a shader RID.
// Returns invalid RID on failure (caller must check with .is_valid()).
inline RID compile_shader(RenderingDevice *rd, const String &glsl_source, const String &label = "") {
Ref<RDShaderSource> src;
src.instantiate();
src->set_stage_source(RenderingDevice::SHADER_STAGE_COMPUTE, glsl_source);
Ref<RDShaderSPIRV> spirv = rd->shader_compile_spirv_from_source(src);
if (spirv.is_null()) {
UtilityFunctions::print("[ShaderUtils] SPIR-V compilation returned null", label.is_empty() ? "" : String(" for ") + label);
return RID();
}
String err = spirv->get_stage_compile_error(RenderingDevice::SHADER_STAGE_COMPUTE);
if (!err.is_empty()) {
UtilityFunctions::print("[ShaderUtils] Shader compile error", label.is_empty() ? "" : String(" (") + label + ")", ": ", err);
return RID();
}
RID shader = rd->shader_create_from_spirv(spirv);
if (!shader.is_valid()) {
UtilityFunctions::print("[ShaderUtils] Failed to create shader from SPIR-V", label.is_empty() ? "" : String(" for ") + label);
}
return shader;
}
// Create a compute pipeline from a shader RID.
// Returns invalid RID on failure.
inline RID create_pipeline(RenderingDevice *rd, RID shader, const String &label = "") {
RID pipeline = rd->compute_pipeline_create(shader);
if (!pipeline.is_valid()) {
UtilityFunctions::print("[ShaderUtils] Failed to create pipeline", label.is_empty() ? "" : String(" for ") + label);
}
return pipeline;
}
// Convenience: compile shader source and create pipeline in one call.
// On failure, cleans up partial state and returns {invalid, invalid}.
struct ShaderPipeline {
RID shader;
RID pipeline;
bool valid() const { return shader.is_valid() && pipeline.is_valid(); }
};
inline ShaderPipeline compile_and_create_pipeline(RenderingDevice *rd, const String &glsl_source, const String &label = "") {
ShaderPipeline sp;
sp.shader = compile_shader(rd, glsl_source, label);
if (!sp.shader.is_valid()) return sp;
sp.pipeline = create_pipeline(rd, sp.shader, label);
if (!sp.pipeline.is_valid()) {
rd->free_rid(sp.shader);
sp.shader = RID();
}
return sp;
}
} // namespace ShaderUtils
#endif // DC_SHADER_UTILS_H