OverviewFilesCommitsIssuesDocsReleasesPeers

dc-terrain / src / dc_shader_utils.h

91 lines · c ref:
#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