summaryrefslogtreecommitdiff
path: root/src/render/ShaderLoader.cpp
diff options
context:
space:
mode:
authorUjinT34 <41110182+UjinT34@users.noreply.github.com>2026-03-07 00:05:10 +0300
committerGitHub <noreply@github.com>2026-03-06 21:05:10 +0000
commita5858018d896e0e0f0db517f893cd9a0936dd881 (patch)
tree64bec75dc882ea27d5ee62a6ba25b22fa78146ae /src/render/ShaderLoader.cpp
parent8685fd7b0c2afe06c798554dea80c53f98d73894 (diff)
renderer: shader variants refactor (#13434)
Part 0 of renderer reworks.
Diffstat (limited to 'src/render/ShaderLoader.cpp')
-rw-r--r--src/render/ShaderLoader.cpp176
1 files changed, 176 insertions, 0 deletions
diff --git a/src/render/ShaderLoader.cpp b/src/render/ShaderLoader.cpp
new file mode 100644
index 00000000..0d2d0ee4
--- /dev/null
+++ b/src/render/ShaderLoader.cpp
@@ -0,0 +1,176 @@
+#include "ShaderLoader.hpp"
+#include <format>
+#include <hyprutils/memory/Casts.hpp>
+#include <hyprutils/memory/UniquePtr.hpp>
+#include <hyprutils/string/String.hpp>
+#include <hyprutils/path/Path.hpp>
+#include "../debug/log/Logger.hpp"
+#include "shaders/Shaders.hpp"
+#include "../helpers/fs/FsUtils.hpp"
+#include "Renderer.hpp"
+#include <glslang/Public/resource_limits_c.h>
+#include <string>
+#include <filesystem>
+
+using namespace Render;
+
+CShaderLoader::CShaderLoader(const std::vector<std::string> includes, const std::array<std::string, SH_FRAG_LAST>& frags, const std::string shaderPath) : m_shaderPath(shaderPath) {
+ m_callbacks = glsl_include_callbacks_t{
+ .include_local =
+ [](void* ctx, const char* header_name, const char* includer_name, size_t include_depth) {
+ auto shaderLoader = sc<CShaderLoader*>(ctx);
+ auto res = new glsl_include_result_t;
+ if (shaderLoader->m_overrideDefines.length() && std::string{header_name} == "defines.h") {
+ res->header_name = header_name;
+ res->header_data = shaderLoader->m_overrideDefines.c_str();
+ res->header_length = shaderLoader->m_overrideDefines.length();
+ } else if (shaderLoader->includes().contains(header_name)) {
+ res->header_name = header_name;
+ res->header_data = shaderLoader->includes().at(header_name).c_str();
+ res->header_length = shaderLoader->includes().at(header_name).length();
+ } else {
+ res->header_name = nullptr;
+ res->header_data = nullptr;
+ res->header_length = 0;
+ }
+
+ shaderLoader->m_includeResults.push_back(res);
+ return res;
+ },
+ .free_include_result =
+ [](void* ctx, glsl_include_result_t* result) {
+ auto shaderLoader = sc<CShaderLoader*>(ctx);
+ std::erase(shaderLoader->m_includeResults, result);
+ delete result;
+ return 0;
+ },
+ };
+
+ for (const auto& inc : includes) {
+ include(inc);
+ }
+
+ std::ranges::transform(frags, m_fragFiles.begin(), [&](const auto& filename) { return loadShader(filename); });
+}
+
+CShaderLoader::~CShaderLoader() {
+ // glslFreeIncludeResult should leave it empty by this point
+ for (const auto& res : m_includeResults) {
+ delete res;
+ }
+}
+
+void CShaderLoader::include(const std::string& filename) {
+ m_includes.insert({filename, loadShader(filename)});
+}
+
+std::string CShaderLoader::getDefines(ShaderFeatureFlags features) {
+ std::string res = "";
+ std::map<std::string, std::string> defines = {
+ {"USE_RGBA", features & SH_FEAT_RGBA ? "1" : "0"}, {"USE_DISCARD", features & SH_FEAT_DISCARD ? "1" : "0"}, {"USE_TINT", features & SH_FEAT_TINT ? "1" : "0"},
+ {"USE_ROUNDING", features & SH_FEAT_ROUNDING ? "1" : "0"}, {"USE_CM", features & SH_FEAT_CM ? "1" : "0"}, {"USE_TONEMAP", features & SH_FEAT_TONEMAP ? "1" : "0"},
+ {"USE_SDR_MOD", features & SH_FEAT_SDR_MOD ? "1" : "0"}, {"USE_BLUR", features & SH_FEAT_BLUR ? "1" : "0"}, {"USE_ICC", features & SH_FEAT_ICC ? "1" : "0"},
+ };
+ for (const auto& [name, value] : defines) {
+ res += std::format("#define {} {}\n", name, value);
+ }
+ return res;
+}
+
+std::string CShaderLoader::processSource(const std::string& source, glslang_stage_t stage) {
+ const glslang_input_t input = {
+ .language = GLSLANG_SOURCE_GLSL,
+ .stage = stage,
+ .client = GLSLANG_CLIENT_NONE,
+ .target_language = GLSLANG_TARGET_NONE,
+ .code = source.c_str(),
+ .default_version = 100,
+ .default_profile = GLSLANG_NO_PROFILE,
+ .force_default_version_and_profile = false,
+ .forward_compatible = false,
+ .messages = GLSLANG_MSG_DEFAULT_BIT,
+ .resource = glslang_default_resource(),
+ .callbacks = m_callbacks,
+ .callbacks_ctx = this,
+ };
+
+ glslang_shader_t* shader = glslang_shader_create(&input);
+
+ if (!glslang_shader_preprocess(shader, &input)) {
+ Log::logger->log(Log::ERR, "GLSL preprocessing failed");
+ Log::logger->log(Log::ERR, "{}", glslang_shader_get_info_log(shader));
+ Log::logger->log(Log::ERR, "{}", glslang_shader_get_info_debug_log(shader));
+ Log::logger->log(Log::ERR, "{}", input.code);
+ glslang_shader_delete(shader);
+ return source;
+ }
+
+ std::stringstream stream(glslang_shader_get_preprocessed_code(shader));
+ std::string code = "";
+ std::string line;
+
+ while (std::getline(stream, line)) {
+ if (!line.starts_with("#line "))
+ code += line + "\n";
+ }
+
+ return code;
+}
+
+std::string CShaderLoader::process(const std::string& filename) {
+ auto source = loadShader(filename);
+ return processSource(source, filename.ends_with(".vert") ? GLSLANG_STAGE_VERTEX : GLSLANG_STAGE_FRAGMENT);
+}
+
+std::string CShaderLoader::process(const std::string& filename, const std::map<std::string, std::string>& defines) {
+ m_overrideDefines = "";
+ for (const auto& [name, value] : defines) {
+ m_overrideDefines += std::format("#define {} {}\n", name, value);
+ }
+ const auto& res = process(filename);
+ m_overrideDefines = "";
+ return res;
+}
+
+std::string CShaderLoader::getVariantSource(ePreparedFragmentShader frag, ShaderFeatureFlags features) {
+ static const auto PCM = CConfigValue<Hyprlang::INT>("render:cm_enabled");
+ if (!*PCM)
+ features &= ~(SH_FEAT_CM | SH_FEAT_TONEMAP | SH_FEAT_SDR_MOD);
+
+ if (!m_fragVariants[frag].contains(features)) {
+ ASSERT(m_fragFiles[frag].length());
+ m_overrideDefines = getDefines(features);
+ m_fragVariants[frag][features] = processSource(m_fragFiles[frag]);
+ m_overrideDefines = "";
+ }
+
+ return m_fragVariants[frag][features];
+}
+
+const std::map<std::string, std::string>& CShaderLoader::includes() {
+ return m_includes;
+}
+
+// TODO notify user if bundled shader is newer than ~/.config override
+std::string CShaderLoader::loadShader(const std::string& filename) {
+ if (m_shaderPath.length()) {
+ std::filesystem::path path = m_shaderPath;
+ const auto src = NFsUtils::readFileAsString(path / filename);
+ if (src.has_value())
+ return src.value();
+ }
+ const auto home = Hyprutils::Path::getHome();
+ if (home.has_value()) {
+ const auto src = NFsUtils::readFileAsString(home.value() + "/hypr/shaders/" + filename);
+ if (src.has_value())
+ return src.value();
+ }
+ for (auto& e : ASSET_PATHS) {
+ const auto src = NFsUtils::readFileAsString(std::string{e} + "/hypr/shaders/" + filename);
+ if (src.has_value())
+ return src.value();
+ }
+ if (SHADERS.contains(filename))
+ return SHADERS.at(filename);
+ throw std::runtime_error(std::format("Couldn't load shader {}", filename));
+}