Files
MapLibreParticleLab/include/mln/shaders/vulkan/shader_group.hpp
Bart Louwers 2a8ebc4906 core: rename mbgl directories to mln (#4511)
Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
2026-08-22 20:50:14 +02:00

98 lines
3.8 KiB
C++

#pragma once
#include <mln/gfx/shader_group.hpp>
#include <mln/shaders/vulkan/shader_program.hpp>
#include <mln/shaders/program_parameters.hpp>
#include <mln/shaders/shader_source.hpp>
#include <mln/util/hash.hpp>
#include <mln/util/containers.hpp>
#include <numeric>
#include <string>
#include <type_traits>
namespace mln {
namespace vulkan {
class ShaderGroupBase : public gfx::ShaderGroup {
protected:
ShaderGroupBase(const ProgramParameters& parameters_)
: programParameters(parameters_) {}
using DefinesMap = mln::unordered_map<std::string, std::string>;
void addAdditionalDefines(const StringIDSetsPair& propertiesAsUniforms, DefinesMap& additionalDefines) {
additionalDefines.reserve(propertiesAsUniforms.first.size());
for (const auto name : propertiesAsUniforms.first) {
// We expect the names to be prefixed by "a_", but we need just the base here.
const auto* base = (name[0] == 'a' && name[1] == '_') ? &name[2] : name.data();
additionalDefines.insert(std::make_pair(std::string(uniformPrefix) + base, std::string()));
}
}
ProgramParameters programParameters;
private:
static constexpr auto uniformPrefix = "HAS_UNIFORM_u_";
};
template <shaders::BuiltIn ShaderID>
class ShaderGroup final : public ShaderGroupBase {
public:
ShaderGroup(const ProgramParameters& programParameters_)
: ShaderGroupBase(programParameters_) {}
~ShaderGroup() noexcept override = default;
gfx::ShaderPtr getOrCreateShader(gfx::Context& gfxContext,
const StringIDSetsPair& propertiesAsUniforms,
std::string_view /*firstAttribName*/) override {
using ShaderSource = shaders::ShaderSource<ShaderID, gfx::Backend::Type::Vulkan>;
constexpr auto& name = ShaderSource::name;
constexpr auto& prelude = ShaderSource::prelude;
constexpr auto& vert = ShaderSource::vertex;
constexpr auto& frag = ShaderSource::fragment;
std::size_t seed = 0;
mln::util::hash_combine(seed, propertyHash(propertiesAsUniforms));
mln::util::hash_combine(seed, programParameters.getDefinesHash());
const std::string shaderName = getShaderName(name, seed);
auto shader = get<vulkan::ShaderProgram>(shaderName);
if (!shader) {
DefinesMap additionalDefines;
addAdditionalDefines(propertiesAsUniforms, additionalDefines);
const std::string preludeSource(prelude);
const std::string vertexSource = preludeSource + vert;
const std::string fragmentSource = preludeSource + frag;
auto& context = static_cast<Context&>(gfxContext);
shader = context.createProgram(
ShaderID, shaderName, vertexSource, fragmentSource, programParameters, additionalDefines);
assert(shader);
if (!shader || !registerShader(shader, shaderName)) {
assert(false);
throw std::runtime_error("Failed to register " + shaderName + " with shader group!");
}
using ShaderClass = shaders::ShaderSource<ShaderID, gfx::Backend::Type::Vulkan>;
for (const auto& attrib : ShaderClass::attributes) {
if (!propertiesAsUniforms.second.contains(attrib.id)) {
shader->initVertexAttribute(attrib);
}
}
for (const auto& attrib : ShaderClass::instanceAttributes) {
if (!propertiesAsUniforms.second.contains(attrib.id)) {
shader->initInstanceAttribute(attrib);
}
}
for (const auto& texture : ShaderClass::textures) {
shader->initTexture(texture);
}
}
return shader;
}
};
} // namespace vulkan
} // namespace mln