Hyprland/src/render/Shader.cpp
Vaxry 8726a7363e
config: cleanup the entire config infrastructure (#13785)
Massively refactors the config infrastructure, sorely
needed and will be req'd for the lua stuff
2026-03-20 17:52:37 +00:00

424 lines
17 KiB
C++

#include "Shader.hpp"
#include "../hyprerror/HyprError.hpp"
#include "../config/ConfigValue.hpp"
#include "OpenGL.hpp"
#define EPSILON(x, y) (std::abs((x) - (y)) < 1e-5f)
static bool compareFloat(auto a, auto b) {
if (a.size() != b.size())
return false;
for (size_t i = 0; i < a.size(); ++i)
if (std::fabs(a[i] - b[i]) > 1e-5f)
return false;
return true;
}
CShader::CShader() {
m_uniformLocations.fill(-1);
}
CShader::~CShader() {
destroy();
}
void CShader::logShaderError(const GLuint& shader, bool program, bool silent) {
GLint maxLength = 0;
if (program)
glGetProgramiv(shader, GL_INFO_LOG_LENGTH, &maxLength);
else
glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &maxLength);
std::vector<GLchar> errorLog(maxLength);
if (program)
glGetProgramInfoLog(shader, maxLength, &maxLength, errorLog.data());
else
glGetShaderInfoLog(shader, maxLength, &maxLength, errorLog.data());
std::string errorStr(errorLog.begin(), errorLog.end());
const auto FULLERROR = (program ? "Screen shader parser: Error linking program:" : "Screen shader parser: Error compiling shader: ") + errorStr;
Log::logger->log(Log::ERR, "Failed to link shader: {}", FULLERROR);
if (!silent)
g_pHyprError->queueError(FULLERROR);
}
GLuint CShader::compileShader(const GLuint& type, std::string src, bool dynamic, bool silent) {
auto shader = glCreateShader(type);
auto shaderSource = src.c_str();
glShaderSource(shader, 1, &shaderSource, nullptr);
glCompileShader(shader);
GLint ok;
glGetShaderiv(shader, GL_COMPILE_STATUS, &ok);
if (dynamic) {
if (ok == GL_FALSE) {
logShaderError(shader, false, silent);
return 0;
}
} else {
if (ok != GL_TRUE)
logShaderError(shader, false);
RASSERT(ok != GL_FALSE, "compileShader() failed! GL_COMPILE_STATUS not OK!");
}
return shader;
}
bool CShader::createProgram(const std::string& vert, const std::string& frag, bool dynamic, bool silent) {
auto vertCompiled = compileShader(GL_VERTEX_SHADER, vert, dynamic, silent);
if (dynamic) {
if (vertCompiled == 0)
return false;
} else
RASSERT(vertCompiled, "Compiling shader failed. VERTEX nullptr! Shader source:\n\n{}", vert);
auto fragCompiled = compileShader(GL_FRAGMENT_SHADER, frag, dynamic, silent);
if (dynamic) {
if (fragCompiled == 0)
return false;
} else
RASSERT(fragCompiled, "Compiling shader failed. FRAGMENT nullptr! Shader source:\n\n{}", frag);
auto prog = glCreateProgram();
glAttachShader(prog, vertCompiled);
glAttachShader(prog, fragCompiled);
glLinkProgram(prog);
glDetachShader(prog, vertCompiled);
glDetachShader(prog, fragCompiled);
glDeleteShader(vertCompiled);
glDeleteShader(fragCompiled);
GLint ok;
glGetProgramiv(prog, GL_LINK_STATUS, &ok);
if (dynamic) {
if (ok == GL_FALSE) {
logShaderError(prog, true, silent);
return false;
}
} else {
if (ok != GL_TRUE)
logShaderError(prog, true);
RASSERT(ok != GL_FALSE, "createProgram() failed! GL_LINK_STATUS not OK!");
}
m_program = prog;
getUniformLocations();
createVao();
return true;
}
// its fine to call glGet on shaders that dont have the uniform
// this however hardcodes the name now. #TODO maybe dont
void CShader::getUniformLocations() {
auto getUniform = [this](const GLchar* name) { return glGetUniformLocation(m_program, name); };
auto getAttrib = [this](const GLchar* name) { return glGetAttribLocation(m_program, name); };
m_uniformLocations[SHADER_PROJ] = getUniform("proj");
m_uniformLocations[SHADER_COLOR] = getUniform("color");
m_uniformLocations[SHADER_ALPHA_MATTE] = getUniform("texMatte");
m_uniformLocations[SHADER_TEX_TYPE] = getUniform("texType");
// shader has #include "CM.glsl"
m_uniformLocations[SHADER_SOURCE_TF] = getUniform("sourceTF");
m_uniformLocations[SHADER_TARGET_TF] = getUniform("targetTF");
m_uniformLocations[SHADER_SRC_TF_RANGE] = getUniform("srcTFRange");
m_uniformLocations[SHADER_DST_TF_RANGE] = getUniform("dstTFRange");
m_uniformLocations[SHADER_TARGET_PRIMARIES_XYZ] = getUniform("targetPrimariesXYZ");
m_uniformLocations[SHADER_MAX_LUMINANCE] = getUniform("maxLuminance");
m_uniformLocations[SHADER_SRC_REF_LUMINANCE] = getUniform("srcRefLuminance");
m_uniformLocations[SHADER_DST_MAX_LUMINANCE] = getUniform("dstMaxLuminance");
m_uniformLocations[SHADER_DST_REF_LUMINANCE] = getUniform("dstRefLuminance");
m_uniformLocations[SHADER_SDR_SATURATION] = getUniform("sdrSaturation");
m_uniformLocations[SHADER_SDR_BRIGHTNESS] = getUniform("sdrBrightnessMultiplier");
m_uniformLocations[SHADER_CONVERT_MATRIX] = getUniform("convertMatrix");
m_uniformLocations[SHADER_LUT_3D] = getUniform("iccLut3D");
m_uniformLocations[SHADER_LUT_SIZE] = getUniform("iccLutSize");
//
m_uniformLocations[SHADER_TEX] = getUniform("tex");
m_uniformLocations[SHADER_BLURRED_BG] = getUniform("blurredBG");
m_uniformLocations[SHADER_UV_SIZE] = getUniform("uvSize");
m_uniformLocations[SHADER_UV_OFFSET] = getUniform("uvOffset");
m_uniformLocations[SHADER_ALPHA] = getUniform("alpha");
m_uniformLocations[SHADER_POS_ATTRIB] = getAttrib("pos");
m_uniformLocations[SHADER_TEX_ATTRIB] = getAttrib("texcoord");
m_uniformLocations[SHADER_MATTE_TEX_ATTRIB] = getAttrib("texcoordMatte");
m_uniformLocations[SHADER_DISCARD_OPAQUE] = getUniform("discardOpaque");
m_uniformLocations[SHADER_DISCARD_ALPHA] = getUniform("discardAlpha");
m_uniformLocations[SHADER_DISCARD_ALPHA_VALUE] = getUniform("discardAlphaValue");
/* set in createVao
m_uniformLocations[SHADER_SHADER_VAO]
m_uniformLocations[SHADER_SHADER_VBO_POS]
m_uniformLocations[SHADER_SHADER_VBO_UV]
*/
m_uniformLocations[SHADER_TOP_LEFT] = getUniform("topLeft");
m_uniformLocations[SHADER_BOTTOM_RIGHT] = getUniform("bottomRight");
// compat for screenshaders
auto fullSize = getUniform("fullSize");
if (fullSize == -1)
fullSize = getUniform("screen_size");
if (fullSize == -1)
fullSize = getUniform("screenSize");
m_uniformLocations[SHADER_FULL_SIZE] = fullSize;
m_uniformLocations[SHADER_FULL_SIZE_UNTRANSFORMED] = getUniform("fullSizeUntransformed");
m_uniformLocations[SHADER_RADIUS] = getUniform("radius");
m_uniformLocations[SHADER_RADIUS_OUTER] = getUniform("radiusOuter");
m_uniformLocations[SHADER_ROUNDING_POWER] = getUniform("roundingPower");
m_uniformLocations[SHADER_THICK] = getUniform("thick");
m_uniformLocations[SHADER_HALFPIXEL] = getUniform("halfpixel");
m_uniformLocations[SHADER_RANGE] = getUniform("range");
m_uniformLocations[SHADER_SHADOW_POWER] = getUniform("shadowPower");
m_uniformLocations[SHADER_USE_ALPHA_MATTE] = getUniform("useAlphaMatte");
m_uniformLocations[SHADER_APPLY_TINT] = getUniform("applyTint");
m_uniformLocations[SHADER_TINT] = getUniform("tint");
m_uniformLocations[SHADER_GRADIENT] = getUniform("gradient");
m_uniformLocations[SHADER_GRADIENT_LENGTH] = getUniform("gradientLength");
m_uniformLocations[SHADER_GRADIENT2] = getUniform("gradient2");
m_uniformLocations[SHADER_GRADIENT2_LENGTH] = getUniform("gradient2Length");
m_uniformLocations[SHADER_ANGLE] = getUniform("angle");
m_uniformLocations[SHADER_ANGLE2] = getUniform("angle2");
m_uniformLocations[SHADER_GRADIENT_LERP] = getUniform("gradientLerp");
m_uniformLocations[SHADER_TIME] = getUniform("time");
m_uniformLocations[SHADER_DISTORT] = getUniform("distort");
m_uniformLocations[SHADER_WL_OUTPUT] = getUniform("wl_output");
m_uniformLocations[SHADER_CONTRAST] = getUniform("contrast");
m_uniformLocations[SHADER_PASSES] = getUniform("passes");
m_uniformLocations[SHADER_VIBRANCY] = getUniform("vibrancy");
m_uniformLocations[SHADER_VIBRANCY_DARKNESS] = getUniform("vibrancy_darkness");
m_uniformLocations[SHADER_BRIGHTNESS] = getUniform("brightness");
m_uniformLocations[SHADER_NOISE] = getUniform("noise");
m_uniformLocations[SHADER_POINTER] = getUniform("pointer_position");
m_uniformLocations[SHADER_POINTER_SHAPE] = getUniform("pointer_shape");
m_uniformLocations[SHADER_POINTER_SWITCH_TIME] = getUniform("pointer_switch_time");
m_uniformLocations[SHADER_POINTER_SHAPE_PREVIOUS] = getUniform("pointer_shape_previous");
m_uniformLocations[SHADER_POINTER_PRESSED_POSITIONS] = getUniform("pointer_pressed_positions");
m_uniformLocations[SHADER_POINTER_HIDDEN] = getUniform("pointer_hidden");
m_uniformLocations[SHADER_POINTER_KILLING] = getUniform("pointer_killing");
m_uniformLocations[SHADER_POINTER_PRESSED_TIMES] = getUniform("pointer_pressed_times");
m_uniformLocations[SHADER_POINTER_PRESSED_KILLED] = getUniform("pointer_pressed_killed");
m_uniformLocations[SHADER_POINTER_PRESSED_TOUCHED] = getUniform("pointer_pressed_touched");
m_uniformLocations[SHADER_POINTER_INACTIVE_TIMEOUT] = getUniform("pointer_inactive_timeout");
m_uniformLocations[SHADER_POINTER_LAST_ACTIVE] = getUniform("pointer_last_active");
m_uniformLocations[SHADER_POINTER_SIZE] = getUniform("pointer_size");
}
void CShader::createVao() {
GLuint shaderVao = 0, shaderVbo = 0;
glGenVertexArrays(1, &shaderVao);
glBindVertexArray(shaderVao);
if (m_uniformLocations[SHADER_POS_ATTRIB] != -1) {
glGenBuffers(1, &shaderVbo);
glBindBuffer(GL_ARRAY_BUFFER, shaderVbo);
glBufferData(GL_ARRAY_BUFFER, sizeof(fullVerts), fullVerts.data(), GL_DYNAMIC_DRAW);
glEnableVertexAttribArray(m_uniformLocations[SHADER_POS_ATTRIB]);
glVertexAttribPointer(m_uniformLocations[SHADER_POS_ATTRIB], 2, GL_FLOAT, GL_FALSE, sizeof(SVertex), (void*)offsetof(SVertex, x));
}
// UV VBO (dynamic, may be updated per frame)
if (m_uniformLocations[SHADER_TEX_ATTRIB] != -1 && shaderVbo != 0) {
glBindBuffer(GL_ARRAY_BUFFER, shaderVbo);
glEnableVertexAttribArray(m_uniformLocations[SHADER_TEX_ATTRIB]);
glVertexAttribPointer(m_uniformLocations[SHADER_TEX_ATTRIB], 2, GL_FLOAT, GL_FALSE, sizeof(SVertex), (void*)offsetof(SVertex, u));
}
glBindVertexArray(0);
glBindBuffer(GL_ARRAY_BUFFER, 0);
m_uniformLocations[SHADER_SHADER_VAO] = shaderVao;
m_uniformLocations[SHADER_SHADER_VBO] = shaderVbo;
RASSERT(m_uniformLocations[SHADER_SHADER_VAO] >= 0, "SHADER_SHADER_VAO could not be created");
RASSERT(m_uniformLocations[SHADER_SHADER_VBO] >= 0, "SHADER_SHADER_VBO_POS could not be created");
}
void CShader::setUniformInt(eShaderUniform location, GLint v0) {
if (m_uniformLocations.at(location) == -1)
return;
auto& cached = uniformStatus.at(location);
if (cached.index() != 0 && std::get<GLint>(cached) == v0)
return;
cached = v0;
GLCALL(glUniform1i(m_uniformLocations[location], v0));
}
void CShader::setUniformFloat(eShaderUniform location, GLfloat v0) {
if (m_uniformLocations.at(location) == -1)
return;
auto& cached = uniformStatus.at(location);
if (cached.index() != 0) {
auto val = std::get<GLfloat>(cached);
if (EPSILON(val, v0))
return;
}
cached = v0;
GLCALL(glUniform1f(m_uniformLocations[location], v0));
}
void CShader::setUniformFloat2(eShaderUniform location, GLfloat v0, GLfloat v1) {
if (m_uniformLocations.at(location) == -1)
return;
auto& cached = uniformStatus.at(location);
if (cached.index() != 0) {
auto val = std::get<std::array<GLfloat, 2>>(cached);
if (EPSILON(val[0], v0) && EPSILON(val[1], v1))
return;
}
cached = std::array<GLfloat, 2>{v0, v1};
GLCALL(glUniform2f(m_uniformLocations[location], v0, v1));
}
void CShader::setUniformFloat3(eShaderUniform location, GLfloat v0, GLfloat v1, GLfloat v2) {
if (m_uniformLocations.at(location) == -1)
return;
auto& cached = uniformStatus.at(location);
if (cached.index() != 0) {
auto val = std::get<std::array<GLfloat, 3>>(cached);
if (EPSILON(val[0], v0) && EPSILON(val[1], v1) && EPSILON(val[2], v2))
return;
}
cached = std::array<GLfloat, 3>{v0, v1, v2};
GLCALL(glUniform3f(m_uniformLocations[location], v0, v1, v2));
}
void CShader::setUniformFloat4(eShaderUniform location, GLfloat v0, GLfloat v1, GLfloat v2, GLfloat v3) {
if (m_uniformLocations.at(location) == -1)
return;
auto& cached = uniformStatus.at(location);
if (cached.index() != 0) {
auto val = std::get<std::array<GLfloat, 4>>(cached);
if (EPSILON(val[0], v0) && EPSILON(val[1], v1) && EPSILON(val[2], v2) && EPSILON(val[3], v3))
return;
}
cached = std::array<GLfloat, 4>{v0, v1, v2, v3};
GLCALL(glUniform4f(m_uniformLocations[location], v0, v1, v2, v3));
}
void CShader::setUniformMatrix3fv(eShaderUniform location, GLsizei count, GLboolean transpose, std::array<GLfloat, 9> value) {
if (m_uniformLocations.at(location) == -1)
return;
auto& cached = uniformStatus.at(location);
if (cached.index() != 0) {
auto val = std::get<SUniformMatrix3Data>(cached);
if (val.count == count && val.transpose == transpose && compareFloat(val.value, value))
return;
}
cached = SUniformMatrix3Data{.count = count, .transpose = transpose, .value = value};
GLCALL(glUniformMatrix3fv(m_uniformLocations[location], count, transpose, value.data()));
}
void CShader::setUniformMatrix4x2fv(eShaderUniform location, GLsizei count, GLboolean transpose, std::array<GLfloat, 8> value) {
if (m_uniformLocations.at(location) == -1)
return;
auto& cached = uniformStatus.at(location);
if (cached.index() != 0) {
auto val = std::get<SUniformMatrix4Data>(cached);
if (val.count == count && val.transpose == transpose && compareFloat(val.value, value))
return;
}
cached = SUniformMatrix4Data{.count = count, .transpose = transpose, .value = value};
GLCALL(glUniformMatrix4x2fv(m_uniformLocations[location], count, transpose, value.data()));
}
void CShader::setUniformfv(eShaderUniform location, GLsizei count, const std::vector<float>& value, GLsizei vec_size) {
if (m_uniformLocations.at(location) == -1)
return;
auto& cached = uniformStatus.at(location);
if (cached.index() != 0) {
auto val = std::get<SUniformVData>(cached);
if (val.count == count && compareFloat(val.value, value))
return;
}
cached = SUniformVData{.count = count, .value = value};
switch (vec_size) {
case 1: GLCALL(glUniform1fv(m_uniformLocations[location], count, value.data())); break;
case 2: GLCALL(glUniform2fv(m_uniformLocations[location], count, value.data())); break;
case 4: GLCALL(glUniform4fv(m_uniformLocations[location], count, value.data())); break;
default: UNREACHABLE();
}
}
void CShader::setUniform1fv(eShaderUniform location, GLsizei count, const std::vector<float>& value) {
setUniformfv(location, count, value, 1);
}
void CShader::setUniform2fv(eShaderUniform location, GLsizei count, const std::vector<float>& value) {
setUniformfv(location, count, value, 2);
}
void CShader::setUniform4fv(eShaderUniform location, GLsizei count, const std::vector<float>& value) {
setUniformfv(location, count, value, 4);
}
void CShader::destroy() {
uniformStatus.fill(std::monostate());
if (m_program == 0)
return;
GLuint shaderVao, shaderVbo;
shaderVao = m_uniformLocations[SHADER_SHADER_VAO] == -1 ? 0 : m_uniformLocations[SHADER_SHADER_VAO];
shaderVbo = m_uniformLocations[SHADER_SHADER_VBO] == -1 ? 0 : m_uniformLocations[SHADER_SHADER_VBO];
if (shaderVao)
glDeleteVertexArrays(1, &shaderVao);
if (shaderVbo)
glDeleteBuffers(1, &shaderVbo);
glDeleteProgram(m_program);
m_program = 0;
}
GLint CShader::getUniformLocation(eShaderUniform location) const {
return m_uniformLocations[location];
}
GLuint CShader::program() const {
return m_program;
}
int CShader::getInitialTime() const {
return m_initialTime;
}
void CShader::setInitialTime(int time) {
m_initialTime = time;
}