Hyprland/src/render/Shader.cpp

225 lines
7.3 KiB
C++

#include "Shader.hpp"
#include "render/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;
}
SShader::SShader() {
uniformLocations.fill(-1);
}
SShader::~SShader() {
destroy();
}
void SShader::createVao() {
GLuint shaderVao = 0, shaderVbo = 0, shaderVboUv = 0;
glGenVertexArrays(1, &shaderVao);
glBindVertexArray(shaderVao);
if (uniformLocations[SHADER_POS_ATTRIB] != -1) {
glGenBuffers(1, &shaderVbo);
glBindBuffer(GL_ARRAY_BUFFER, shaderVbo);
glBufferData(GL_ARRAY_BUFFER, sizeof(fullVerts), fullVerts, GL_STATIC_DRAW);
glEnableVertexAttribArray(uniformLocations[SHADER_POS_ATTRIB]);
glVertexAttribPointer(uniformLocations[SHADER_POS_ATTRIB], 2, GL_FLOAT, GL_FALSE, 0, nullptr);
}
// UV VBO (dynamic, may be updated per frame)
if (uniformLocations[SHADER_TEX_ATTRIB] != -1) {
glGenBuffers(1, &shaderVboUv);
glBindBuffer(GL_ARRAY_BUFFER, shaderVboUv);
glBufferData(GL_ARRAY_BUFFER, sizeof(fullVerts), fullVerts, GL_DYNAMIC_DRAW); // Initial dummy UVs
glEnableVertexAttribArray(uniformLocations[SHADER_TEX_ATTRIB]);
glVertexAttribPointer(uniformLocations[SHADER_TEX_ATTRIB], 2, GL_FLOAT, GL_FALSE, 0, nullptr);
}
glBindVertexArray(0);
glBindBuffer(GL_ARRAY_BUFFER, 0);
uniformLocations[SHADER_SHADER_VAO] = shaderVao;
uniformLocations[SHADER_SHADER_VBO_POS] = shaderVbo;
uniformLocations[SHADER_SHADER_VBO_UV] = shaderVboUv;
RASSERT(uniformLocations[SHADER_SHADER_VAO] >= 0, "SHADER_SHADER_VAO could not be created");
RASSERT(uniformLocations[SHADER_SHADER_VBO_POS] >= 0, "SHADER_SHADER_VBO_POS could not be created");
RASSERT(uniformLocations[SHADER_SHADER_VBO_UV] >= 0, "SHADER_SHADER_VBO_UV could not be created");
}
void SShader::setUniformInt(eShaderUniform location, GLint v0) {
if (uniformLocations.at(location) == -1)
return;
auto& cached = uniformStatus.at(location);
if (cached.index() != 0 && std::get<GLint>(cached) == v0)
return;
cached = v0;
glUniform1i(uniformLocations[location], v0);
}
void SShader::setUniformFloat(eShaderUniform location, GLfloat v0) {
if (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;
glUniform1f(uniformLocations[location], v0);
}
void SShader::setUniformFloat2(eShaderUniform location, GLfloat v0, GLfloat v1) {
if (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};
glUniform2f(uniformLocations[location], v0, v1);
}
void SShader::setUniformFloat3(eShaderUniform location, GLfloat v0, GLfloat v1, GLfloat v2) {
if (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};
glUniform3f(uniformLocations[location], v0, v1, v2);
}
void SShader::setUniformFloat4(eShaderUniform location, GLfloat v0, GLfloat v1, GLfloat v2, GLfloat v3) {
if (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};
glUniform4f(uniformLocations[location], v0, v1, v2, v3);
}
void SShader::setUniformMatrix3fv(eShaderUniform location, GLsizei count, GLboolean transpose, std::array<GLfloat, 9> value) {
if (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};
glUniformMatrix3fv(uniformLocations[location], count, transpose, value.data());
}
void SShader::setUniformMatrix4x2fv(eShaderUniform location, GLsizei count, GLboolean transpose, std::array<GLfloat, 8> value) {
if (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};
glUniformMatrix4x2fv(uniformLocations[location], count, transpose, value.data());
}
void SShader::setUniformfv(eShaderUniform location, GLsizei count, const std::vector<float>& value, GLsizei vec_size) {
if (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: glUniform1fv(uniformLocations[location], count, value.data()); break;
case 2: glUniform2fv(uniformLocations[location], count, value.data()); break;
case 4: glUniform4fv(uniformLocations[location], count, value.data()); break;
default: UNREACHABLE();
}
}
void SShader::setUniform1fv(eShaderUniform location, GLsizei count, const std::vector<float>& value) {
setUniformfv(location, count, value, 1);
}
void SShader::setUniform2fv(eShaderUniform location, GLsizei count, const std::vector<float>& value) {
setUniformfv(location, count, value, 2);
}
void SShader::setUniform4fv(eShaderUniform location, GLsizei count, const std::vector<float>& value) {
setUniformfv(location, count, value, 4);
}
void SShader::destroy() {
uniformStatus.fill(std::monostate());
if (program == 0)
return;
GLuint shaderVao, shaderVbo, shaderVboUv;
shaderVao = uniformLocations[SHADER_SHADER_VAO] == -1 ? 0 : uniformLocations[SHADER_SHADER_VAO];
shaderVbo = uniformLocations[SHADER_SHADER_VBO_POS] == -1 ? 0 : uniformLocations[SHADER_SHADER_VBO_POS];
shaderVboUv = uniformLocations[SHADER_SHADER_VBO_UV] == -1 ? 0 : uniformLocations[SHADER_SHADER_VBO_UV];
if (shaderVao)
glDeleteVertexArrays(1, &shaderVao);
if (shaderVbo)
glDeleteBuffers(1, &shaderVbo);
if (shaderVboUv)
glDeleteBuffers(1, &shaderVboUv);
glDeleteProgram(program);
program = 0;
}