From e3bbfe3cfa7d5a09358a4a0ae6d38092029afec0 Mon Sep 17 00:00:00 2001 From: Tom Englund Date: Mon, 10 Aug 2026 16:25:38 +0200 Subject: [PATCH] shaders: make each SRC/DST TF a variant (#15792) * shaders: make each SRC/DST TF a variant by making each SRC/DS transferunction a variant we reduce the branching and instruction count by up to 60%, measured with INTEL_DEBUG=fs,gs,vs * opengl: cache activetex, blendfunc, VBO, FBO this was about 9% of a 10 second apitrace capture of redundant gl calls with same things over and over. just cache it like we do with other things. because i dont trust the driver to even act accordingly. --- src/render/OpenGL.cpp | 128 ++++++++++++++++------- src/render/OpenGL.hpp | 73 ++++++++----- src/render/Shader.cpp | 12 +-- src/render/Shader.hpp | 2 - src/render/ShaderLoader.cpp | 33 ++++-- src/render/ShaderLoader.hpp | 27 +++-- src/render/gl/GLFramebuffer.cpp | 38 ++++--- src/render/shaders/glsl/blurfinish.frag | 4 +- src/render/shaders/glsl/blurprepare.frag | 4 +- src/render/shaders/glsl/border.frag | 6 +- src/render/shaders/glsl/defines.h | 2 + src/render/shaders/glsl/shadow.frag | 2 +- src/render/shaders/glsl/surface.frag | 4 +- 13 files changed, 220 insertions(+), 115 deletions(-) diff --git a/src/render/OpenGL.cpp b/src/render/OpenGL.cpp index c04d1e88d..0c0bb2e30 100644 --- a/src/render/OpenGL.cpp +++ b/src/render/OpenGL.cpp @@ -1052,7 +1052,7 @@ void CHyprOpenGLImpl::applyScreenShader(const std::string& path) { void CHyprOpenGLImpl::blend(bool enabled) { if (enabled) { setCapStatus(GL_BLEND, true); - GLCALL(glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA)); // everything is premultiplied + blendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA); // everything is premultiplied } else setCapStatus(GL_BLEND, false); @@ -1212,8 +1212,6 @@ static std::map, std::array> primaries void CHyprOpenGLImpl::passCMUniforms(WP shader, const NColorManagement::PImageDescription imageDescription, const NColorManagement::PImageDescription targetImageDescription, bool modifySDR, float sdrMinLuminance, int sdrMaxLuminance, const SCMSettings& settings) { - shader->setUniformInt(SHADER_SOURCE_TF, settings.sourceTF); - shader->setUniformInt(SHADER_TARGET_TF, settings.targetTF); shader->setUniformFloat2(SHADER_SRC_TF_RANGE, settings.srcTFRange.min, settings.srcTFRange.max); shader->setUniformFloat2(SHADER_DST_TF_RANGE, settings.dstTFRange.min, settings.dstTFRange.max); shader->setUniformFloat(SHADER_SRC_REF_LUMINANCE, settings.srcRefLuminance); @@ -1246,13 +1244,13 @@ void CHyprOpenGLImpl::passCMUniforms(WP shader, const NColorManagement: shader->setUniformMatrix3fv(SHADER_TARGET_PRIMARIES_XYZ, 1, false, glTargetPrimariesXYZ); } else { // TODO: this sucks - GLCALL(glActiveTexture(GL_TEXTURE8)); + setActiveTexture(GL_TEXTURE8); targetImageDescription->value().icc.lutTexture->bind(); shader->setUniformInt(SHADER_LUT_3D, 8); shader->setUniformFloat(SHADER_LUT_SIZE, targetImageDescription->value().icc.lutSize); - GLCALL(glActiveTexture(GL_TEXTURE0)); + setActiveTexture(GL_TEXTURE0); } } @@ -1460,7 +1458,7 @@ WP CHyprOpenGLImpl::renderToFBInternal(SP tex, const STexture } if (!shader) - shader = getShaderVariant(SH_FRAG_SURFACE, shaderFeatures | globalFeatures()); + shader = getShaderVariant(SH_FRAG_SURFACE, shaderFeatures | globalFeatures(), settings.sourceTF, settings.targetTF); shader = useShader(shader); passCMUniforms(shader, SOURCE_IMAGE_DESCRIPTION, TARGET_IMAGE_DESCRIPTION, true, g_pHyprRenderer->m_renderData.pMonitor->m_sdrMinLuminance, @@ -1481,14 +1479,14 @@ WP CHyprOpenGLImpl::renderToFBInternal(SP tex, const STexture shader->setUniformFloat2(SHADER_UV_OFFSET, BLURUV.x, BLURUV.y); shader->setUniformFloat2(SHADER_UV_SIZE, BLURUV.w, BLURUV.h); - glActiveTexture(GL_TEXTURE0 + 1); + setActiveTexture(GL_TEXTURE0 + 1); data.blurredBG->bind(); } if (shaderFeatures & SH_FEAT_BLUR_MATTE) { shader->setUniformInt(SHADER_BLUR_ALPHA_MATTE, 2); - glActiveTexture(GL_TEXTURE0 + 2); + setActiveTexture(GL_TEXTURE0 + 2); data.blurAlphaMatte->bind(); } @@ -1572,7 +1570,7 @@ void CHyprOpenGLImpl::renderTextureInternal(SP tex, const CBox& box, c const bool useScreenShader = m_applyFinalShader; - glActiveTexture(GL_TEXTURE0); + setActiveTexture(GL_TEXTURE0); tex->bind(); tex->setTexParameter(GL_TEXTURE_WRAP_S, wrapModeToGl(data.wrapX)); @@ -1595,7 +1593,7 @@ void CHyprOpenGLImpl::renderTextureInternal(SP tex, const CBox& box, c if (CUSTOMUV && CUSTOM_VAO) { GLCALL(glBindVertexArray(CUSTOM_VAO)); - GLCALL(glBindBuffer(GL_ARRAY_BUFFER, shader->getUniformLocation(SHADER_SHADER_UV_VBO))); + bindArrayBuffer(shader->getUniformLocation(SHADER_SHADER_UV_VBO)); // Keep the old block available to previous draws while custom UVs update. glBufferData(GL_ARRAY_BUFFER, sizeof(fullVerts), nullptr, GL_DYNAMIC_DRAW); @@ -1644,7 +1642,7 @@ void CHyprOpenGLImpl::renderTextureInternal(SP tex, const CBox& box, c } GLCALL(glBindVertexArray(0)); - GLCALL(glBindBuffer(GL_ARRAY_BUFFER, 0)); + bindArrayBuffer(0); tex->unbind(); } @@ -1663,7 +1661,7 @@ void CHyprOpenGLImpl::renderTextureMesh(SP tex, const CBox& box, const const auto& glMatrix = g_pHyprRenderer->projectBoxToTarget(newBox, TRANSFORM); - glActiveTexture(GL_TEXTURE0); + setActiveTexture(GL_TEXTURE0); tex->bind(); tex->setTexParameter(GL_TEXTURE_WRAP_S, wrapModeToGl(data.wrapX)); @@ -1682,7 +1680,7 @@ void CHyprOpenGLImpl::renderTextureMesh(SP tex, const CBox& box, const shader->setUniformMatrix3fv(SHADER_PROJ, 1, GL_TRUE, glMatrix.getMatrix()); shader->setUniformInt(SHADER_TEX, 0); GLCALL(glBindVertexArray(shader->getUniformLocation(SHADER_SHADER_UV_VAO))); - GLCALL(glBindBuffer(GL_ARRAY_BUFFER, shader->getUniformLocation(SHADER_SHADER_UV_VBO))); + bindArrayBuffer(shader->getUniformLocation(SHADER_SHADER_UV_VBO)); glBufferData(GL_ARRAY_BUFFER, sizeof(SMeshRenderVertex) * vertices.size(), nullptr, GL_DYNAMIC_DRAW); glBufferSubData(GL_ARRAY_BUFFER, 0, sizeof(SMeshRenderVertex) * vertices.size(), vertices.data()); @@ -1711,7 +1709,7 @@ void CHyprOpenGLImpl::renderTextureMesh(SP tex, const CBox& box, const } GLCALL(glBindVertexArray(0)); - GLCALL(glBindBuffer(GL_ARRAY_BUFFER, 0)); + bindArrayBuffer(0); tex->unbind(); } @@ -1730,7 +1728,7 @@ void CHyprOpenGLImpl::renderTexturePrimitive(SP tex, const CBox& box) // get transform const auto& glMatrix = g_pHyprRenderer->projectBoxToTarget(newBox); - glActiveTexture(GL_TEXTURE0); + setActiveTexture(GL_TEXTURE0); tex->bind(); // ensure the final blit uses the desired sampling filter @@ -1775,10 +1773,10 @@ void CHyprOpenGLImpl::renderTextureMatte(SP tex, const CBox& box, SPsetUniformInt(SHADER_TEX, 0); shader->setUniformInt(SHADER_ALPHA_MATTE, 1); - glActiveTexture(GL_TEXTURE0); + setActiveTexture(GL_TEXTURE0); tex->bind(); - glActiveTexture(GL_TEXTURE0 + 1); + setActiveTexture(GL_TEXTURE0 + 1); auto matteTex = matte->getTexture(); matteTex->bind(); @@ -1846,7 +1844,7 @@ SP CHyprOpenGLImpl::blurFramebufferWithDamage(float a, CRegion* or PMIRRORSWAPFB->bind(); GLFB(PMIRRORSWAPFB)->clearAfterInvalidation(); - glActiveTexture(GL_TEXTURE0); + setActiveTexture(GL_TEXTURE0); auto currentTex = source.getTexture(); @@ -1858,10 +1856,10 @@ SP CHyprOpenGLImpl::blurFramebufferWithDamage(float a, CRegion* or // From FB to sRGB const bool skipCM = !m_cmSupported || !g_pHyprRenderer->workBufferImageDescription()->needsCM(getDefaultImageDescription()); if (!skipCM) { - shader = useShader(getShaderVariant(SH_FRAG_BLURPREPARE, SH_FEAT_CM)); + const auto settings = blurIntermediateCMSettings(/* toIntermediate */ true); + shader = useShader(getShaderVariant(SH_FRAG_BLURPREPARE, SH_FEAT_CM, settings.sourceTF, settings.targetTF)); - passCMUniforms(shader, g_pHyprRenderer->workBufferImageDescription(), getDefaultImageDescription(), false, -1.F, -1, - blurIntermediateCMSettings(/* toIntermediate */ true)); + passCMUniforms(shader, g_pHyprRenderer->workBufferImageDescription(), getDefaultImageDescription(), false, -1.F, -1, settings); shader->setUniformFloat(SHADER_SDR_SATURATION, m_renderData.pMonitor->m_sdrSaturation > 0 && g_pHyprRenderer->workBufferImageDescription()->value().transferFunction == NColorManagement::CM_TRANSFER_FUNCTION_ST2084_PQ ? @@ -1900,7 +1898,7 @@ SP CHyprOpenGLImpl::blurFramebufferWithDamage(float a, CRegion* or else PMIRRORFB->bind(); - glActiveTexture(GL_TEXTURE0); + setActiveTexture(GL_TEXTURE0); auto currentTex = currentRenderToFB->getTexture(); @@ -1969,7 +1967,7 @@ SP CHyprOpenGLImpl::blurFramebufferWithDamage(float a, CRegion* or else PMIRRORFB->bind(); - glActiveTexture(GL_TEXTURE0); + setActiveTexture(GL_TEXTURE0); auto currentTex = currentRenderToFB->getTexture(); @@ -1980,10 +1978,10 @@ SP CHyprOpenGLImpl::blurFramebufferWithDamage(float a, CRegion* or // From FB to sRGB const bool skipCM = !m_cmSupported || !g_pHyprRenderer->workBufferImageDescription()->needsCM(getDefaultImageDescription()); if (!skipCM) { - shader = useShader(getShaderVariant(SH_FRAG_BLURFINISH, SH_FEAT_CM)); + const auto settings = blurIntermediateCMSettings(/* toIntermediate */ false); + shader = useShader(getShaderVariant(SH_FRAG_BLURFINISH, SH_FEAT_CM, settings.sourceTF, settings.targetTF)); - passCMUniforms(shader, getDefaultImageDescription(), g_pHyprRenderer->workBufferImageDescription(), false, -1.F, -1, - blurIntermediateCMSettings(/* toIntermediate */ false)); + passCMUniforms(shader, getDefaultImageDescription(), g_pHyprRenderer->workBufferImageDescription(), false, -1.F, -1, settings); shader->setUniformFloat(SHADER_SDR_SATURATION, m_renderData.pMonitor->m_sdrSaturation > 0 && g_pHyprRenderer->workBufferImageDescription()->value().transferFunction == NColorManagement::CM_TRANSFER_FUNCTION_ST2084_PQ ? @@ -2239,10 +2237,13 @@ void CHyprOpenGLImpl::renderTextureWithBlurInternal(SP tex, const CBox scissor(nullptr); } -static ShaderFeatureFlags getDecoFeatures() { +static SShaderVariant getDecoVariant() { const bool IS_ICC = g_pHyprRenderer->workBufferImageDescription()->value().icc.present; const auto settings = g_pHyprRenderer->getCMSettings(g_pHyprRenderer->workBufferImageDescription(), getDefaultImageDescription(), nullptr, true, g_pHyprRenderer->m_renderData.pMonitor->m_sdrMinLuminance, g_pHyprRenderer->m_renderData.pMonitor->m_sdrMaxLuminance); + const auto uniformSettings = + g_pHyprRenderer->getCMSettings(getDefaultImageDescription(), g_pHyprRenderer->workBufferImageDescription(), nullptr, true, + g_pHyprRenderer->m_renderData.pMonitor->m_sdrMinLuminance, g_pHyprRenderer->m_renderData.pMonitor->m_sdrMaxLuminance); ShaderFeatureFlags features = SH_FEAT_ROUNDING | SH_FEAT_CM | globalFeatures(); if (IS_ICC) @@ -2256,7 +2257,7 @@ static ShaderFeatureFlags getDecoFeatures() { if (settings.needsSDRmod) features |= SH_FEAT_SDR_MOD; } - return features; + return {.features = features, .sourceTF = uniformSettings.sourceTF, .targetTF = uniformSettings.targetTF}; } void CHyprOpenGLImpl::renderBorder(const CBox& box, const Config::CGradientValueData& grad, SBorderRenderData data) { @@ -2296,7 +2297,7 @@ void CHyprOpenGLImpl::renderBorder(const CBox& box, const Config::CGradientValue const bool skipCM = !m_cmSupported || !g_pHyprRenderer->workBufferImageDescription()->needsCM(getDefaultImageDescription()); if (!skipCM) { - shader = useShader(getShaderVariant(SH_FRAG_BORDER1, getDecoFeatures())); + shader = useShader(getShaderVariant(SH_FRAG_BORDER1, getDecoVariant())); passCMUniforms(shader, getDefaultImageDescription()); } else shader = useShader(getShaderVariant(SH_FRAG_BORDER1, SH_FEAT_ROUNDING | globalFeatures())); @@ -2377,7 +2378,7 @@ void CHyprOpenGLImpl::renderBorder(const CBox& box, const Config::CGradientValue WP shader; const bool skipCM = !m_cmSupported || !g_pHyprRenderer->workBufferImageDescription()->needsCM(getDefaultImageDescription()); if (!skipCM) { - shader = useShader(getShaderVariant(SH_FRAG_BORDER1, getDecoFeatures())); + shader = useShader(getShaderVariant(SH_FRAG_BORDER1, getDecoVariant())); passCMUniforms(shader, getDefaultImageDescription()); } else shader = useShader(getShaderVariant(SH_FRAG_BORDER1, SH_FEAT_ROUNDING | globalFeatures())); @@ -2453,10 +2454,8 @@ void CHyprOpenGLImpl::renderRoundedShadow(const CBox& box, int round, float roun const auto TF = m_renderData.currentFB->imageDescription()->value().transferFunction; const bool needsCM = TF != CM_TRANSFER_FUNCTION_EXT_LINEAR; - auto shader = useShader(getShaderVariant(SH_FRAG_SHADOW, (needsCM ? SH_FEAT_CM : 0) | globalFeatures())); + auto shader = useShader(getShaderVariant(SH_FRAG_SHADOW, (needsCM ? SH_FEAT_CM : 0) | globalFeatures(), TF)); - if (needsCM) - shader->setUniformInt(SHADER_SOURCE_TF, TF); shader->setUniformMatrix3fv(SHADER_PROJ, 1, GL_TRUE, glMatrix.getMatrix()); CHyprColor color; @@ -2692,6 +2691,53 @@ void CHyprOpenGLImpl::setViewport(GLint x, GLint y, GLsizei width, GLsizei heigh m_lastViewport = {.x = x, .y = y, .width = width, .height = height}; } +void CHyprOpenGLImpl::setActiveTexture(GLenum texture) { + if (m_activeTexture == texture) + return; + + GLCALL(glActiveTexture(texture)); + m_activeTexture = texture; +} + +void CHyprOpenGLImpl::blendFunc(GLenum sfactor, GLenum dfactor) { + if (m_blendSFactor == sfactor && m_blendDFactor == dfactor) + return; + + GLCALL(glBlendFunc(sfactor, dfactor)); + m_blendSFactor = sfactor; + m_blendDFactor = dfactor; +} + +void CHyprOpenGLImpl::bindArrayBuffer(GLuint buffer) { + if (m_boundArrayBuffer == buffer) + return; + + GLCALL(glBindBuffer(GL_ARRAY_BUFFER, buffer)); + m_boundArrayBuffer = buffer; +} + +void CHyprOpenGLImpl::bindFramebuffer(GLenum target, GLuint fb) { + const bool DRAW = target == GL_FRAMEBUFFER || target == GL_DRAW_FRAMEBUFFER; + const bool READ = target == GL_FRAMEBUFFER || target == GL_READ_FRAMEBUFFER; + + if ((DRAW || READ) && (!DRAW || m_boundDrawFB == fb) && (!READ || m_boundReadFB == fb)) + return; + + GLCALL(glBindFramebuffer(target, fb)); + + if (DRAW) + m_boundDrawFB = fb; + if (READ) + m_boundReadFB = fb; +} + +void CHyprOpenGLImpl::onFramebufferDeleted(GLuint fb) { + if (m_boundDrawFB == fb) + m_boundDrawFB = 0; + if (m_boundReadFB == fb) + m_boundReadFB = 0; +} + void CHyprOpenGLImpl::setCapStatus(int cap, bool status) { const auto getCapIndex = [cap]() { switch (cap) { @@ -2746,21 +2792,25 @@ bool CHyprOpenGLImpl::fp16Supported() { return m_fp16Supported; } -WP CHyprOpenGLImpl::getShaderVariant(ePreparedFragmentShader frag, ShaderFeatureFlags features) { +WP CHyprOpenGLImpl::getShaderVariant(ePreparedFragmentShader frag, ShaderFeatureFlags features, eTransferFunction sourceTF, eTransferFunction targetTF) { + return getShaderVariant(frag, SShaderVariant{.features = features, .sourceTF = sourceTF, .targetTF = targetTF}); +} + +WP CHyprOpenGLImpl::getShaderVariant(ePreparedFragmentShader frag, const SShaderVariant& variant) { auto& variants = m_shaders->fragVariants[frag]; - auto it = variants.find(features); + auto it = variants.find(variant); if (it == variants.end()) { auto shader = makeShared(); - Log::logger->log(Log::INFO, "compiling feature set {} for {}", features, FRAG_SHADERS[frag]); + Log::logger->log(Log::INFO, "compiling feature set {} (TFs {} -> {}) for {}", variant.features, sc(variant.sourceTF), sc(variant.targetTF), FRAG_SHADERS[frag]); - const auto fragSrc = g_pShaderLoader->getVariantSource(frag, features); + const auto fragSrc = g_pShaderLoader->getVariantSource(frag, variant); if (!shader->createProgram(m_shaders->TEXVERTSRC, fragSrc, true, true)) - Log::logger->log(Log::ERR, "shader features {} failed for {}", features, FRAG_SHADERS[frag]); + Log::logger->log(Log::ERR, "shader features {} failed for {}", variant.features, FRAG_SHADERS[frag]); - it = variants.emplace(features, std::move(shader)).first; + it = variants.emplace(variant, std::move(shader)).first; return it->second; } diff --git a/src/render/OpenGL.hpp b/src/render/OpenGL.hpp index 777f4c1fc..1b82d2b1d 100644 --- a/src/render/OpenGL.hpp +++ b/src/render/OpenGL.hpp @@ -89,9 +89,9 @@ namespace Render::GL { }; struct SPreparedShaders { - std::string TEXVERTSRC; - std::string TEXVERTSRC320; - std::array>, Render::SH_FRAG_LAST> fragVariants; + std::string TEXVERTSRC; + std::string TEXVERTSRC320; + std::array>, Render::SH_FRAG_LAST> fragVariants; }; struct SCurrentRenderData { @@ -221,22 +221,28 @@ namespace Render::GL { void setViewport(GLint x, GLint y, GLsizei width, GLsizei height); void setCapStatus(int cap, bool status); + void setActiveTexture(GLenum texture); + void blendFunc(GLenum sfactor, GLenum dfactor); + void bindArrayBuffer(GLuint buffer); + void bindFramebuffer(GLenum target, GLuint fb); + // GL implicitly rebinds 0 on every target the deleted fb was bound to, keep the shadow in sync + void onFramebufferDeleted(GLuint fb); - void blend(bool enabled); + void blend(bool enabled); - void scissor(const CBox&, bool transform = true); - void scissor(const pixman_box32*, bool transform = true); - void scissor(const int x, const int y, const int w, const int h, bool transform = true); + void scissor(const CBox&, bool transform = true); + void scissor(const pixman_box32*, bool transform = true); + void scissor(const int x, const int y, const int w, const int h, bool transform = true); - void destroyMonitorResources(PHLMONITORREF); + void destroyMonitorResources(PHLMONITORREF); - void preRender(PHLMONITOR); + void preRender(PHLMONITOR); - bool saveBufferForMirror(const CBox&); + bool saveBufferForMirror(const CBox&); - void applyScreenShader(const std::string& path); + void applyScreenShader(const std::string& path); - void renderOffToMain(SP off); + void renderOffToMain(SP off); std::vector getDRMFormats(); std::vector getDRMFormatModifiers(DRMFormat format); @@ -248,7 +254,10 @@ namespace Render::GL { bool explicitSyncSupported(); bool fp16Supported(); - WP getShaderVariant(Render::ePreparedFragmentShader frag, Render::ShaderFeatureFlags features = 0); + WP getShaderVariant(Render::ePreparedFragmentShader frag, Render::ShaderFeatureFlags features = 0, + NColorManagement::eTransferFunction sourceTF = Render::SHADER_DEFAULT_TF, + NColorManagement::eTransferFunction targetTF = Render::SHADER_DEFAULT_TF); + WP getShaderVariant(Render::ePreparedFragmentShader frag, const Render::SShaderVariant& variant); bool m_shadersInitialized = false; SP m_shaders; @@ -316,25 +325,33 @@ namespace Render::GL { std::array m_capStatus = {}; - std::vector m_drmFormats; - bool m_hasModifiers = false; - bool m_fp16Supported = false; + // shadowed GL state, all initialized to the GL defaults + GLenum m_activeTexture = GL_TEXTURE0; + GLuint m_boundArrayBuffer = 0; + GLuint m_boundDrawFB = 0; + GLuint m_boundReadFB = 0; + GLenum m_blendSFactor = GL_ONE; + GLenum m_blendDFactor = GL_ZERO; - int m_drmFD = -1; - std::string m_extensions; + std::vector m_drmFormats; + bool m_hasModifiers = false; + bool m_fp16Supported = false; - bool m_fakeFrame = false; - bool m_applyFinalShader = false; - bool m_blend = false; - bool m_offloadedFramebuffer = false; - bool m_cmSupported = true; + int m_drmFD = -1; + std::string m_extensions; - SP m_finalScreenShader; - GLuint m_currentProgram; + bool m_fakeFrame = false; + bool m_applyFinalShader = false; + bool m_blend = false; + bool m_offloadedFramebuffer = false; + bool m_cmSupported = true; - void initDRMFormats(); - void initEGL(bool gbm); - EGLDeviceEXT eglDeviceFromDRMFD(int drmFD); + SP m_finalScreenShader; + GLuint m_currentProgram; + + void initDRMFormats(); + void initEGL(bool gbm); + EGLDeviceEXT eglDeviceFromDRMFD(int drmFD); // for the final shader std::array m_pressedHistoryTimers = {}; diff --git a/src/render/Shader.cpp b/src/render/Shader.cpp index ba7e53159..5aaafb7da 100644 --- a/src/render/Shader.cpp +++ b/src/render/Shader.cpp @@ -131,8 +131,6 @@ void CShader::getUniformLocations() { 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"); @@ -235,7 +233,7 @@ void CShader::createVao() { if (m_uniformLocations[SHADER_POS_ATTRIB] != -1) { glGenBuffers(1, &shaderVbo); - glBindBuffer(GL_ARRAY_BUFFER, shaderVbo); + g_pHyprOpenGL->bindArrayBuffer(shaderVbo); glBufferData(GL_ARRAY_BUFFER, sizeof(fullVerts), fullVerts.data(), GL_STATIC_DRAW); glEnableVertexAttribArray(m_uniformLocations[SHADER_POS_ATTRIB]); glVertexAttribPointer(m_uniformLocations[SHADER_POS_ATTRIB], 2, GL_FLOAT, GL_FALSE, sizeof(SVertex), (void*)offsetof(SVertex, x)); @@ -243,7 +241,7 @@ void CShader::createVao() { // UV VBO (static, default UVs never change) if (m_uniformLocations[SHADER_TEX_ATTRIB] != -1 && shaderVbo != 0) { - glBindBuffer(GL_ARRAY_BUFFER, shaderVbo); + g_pHyprOpenGL->bindArrayBuffer(shaderVbo); glEnableVertexAttribArray(m_uniformLocations[SHADER_TEX_ATTRIB]); glVertexAttribPointer(m_uniformLocations[SHADER_TEX_ATTRIB], 2, GL_FLOAT, GL_FALSE, sizeof(SVertex), (void*)offsetof(SVertex, u)); } @@ -254,7 +252,7 @@ void CShader::createVao() { glBindVertexArray(shaderUvVao); glGenBuffers(1, &shaderUvVbo); - glBindBuffer(GL_ARRAY_BUFFER, shaderUvVbo); + g_pHyprOpenGL->bindArrayBuffer(shaderUvVbo); 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)); @@ -263,7 +261,7 @@ void CShader::createVao() { } glBindVertexArray(0); - glBindBuffer(GL_ARRAY_BUFFER, 0); + g_pHyprOpenGL->bindArrayBuffer(0); m_uniformLocations[SHADER_SHADER_VAO] = shaderVao; m_uniformLocations[SHADER_SHADER_VBO] = shaderVbo; @@ -432,6 +430,8 @@ void CShader::destroy() { shaderUvVao = m_uniformLocations[SHADER_SHADER_UV_VAO] == -1 ? 0 : m_uniformLocations[SHADER_SHADER_UV_VAO]; shaderUvVbo = m_uniformLocations[SHADER_SHADER_UV_VBO] == -1 ? 0 : m_uniformLocations[SHADER_SHADER_UV_VBO]; + g_pHyprOpenGL->bindArrayBuffer(0); + if (shaderVao) glDeleteVertexArrays(1, &shaderVao); diff --git a/src/render/Shader.hpp b/src/render/Shader.hpp index bee4cd6ac..3df2618bd 100644 --- a/src/render/Shader.hpp +++ b/src/render/Shader.hpp @@ -10,8 +10,6 @@ enum eShaderUniform : uint8_t { SHADER_COLOR_SRGB, SHADER_ALPHA_MATTE, SHADER_TEX_TYPE, - SHADER_SOURCE_TF, - SHADER_TARGET_TF, SHADER_SRC_TF_RANGE, SHADER_DST_TF_RANGE, SHADER_TARGET_PRIMARIES_XYZ, diff --git a/src/render/ShaderLoader.cpp b/src/render/ShaderLoader.cpp index 94751a4b8..9a94bcdc4 100644 --- a/src/render/ShaderLoader.cpp +++ b/src/render/ShaderLoader.cpp @@ -64,7 +64,7 @@ void CShaderLoader::include(const std::string& filename) { m_includes.insert({filename, loadShader(filename)}); } -std::string CShaderLoader::getDefines(ShaderFeatureFlags features) { +std::string CShaderLoader::getDefines(const SShaderVariant& variant) { static constexpr auto defines = std::to_array>({ {"USE_RGBA", SH_FEAT_RGBA}, {"USE_DISCARD", SH_FEAT_DISCARD}, @@ -83,10 +83,14 @@ std::string CShaderLoader::getDefines(ShaderFeatureFlags features) { }); std::string res; - res.reserve(309); + res.reserve(351); for (const auto& [name, flag] : defines) { - std::format_to(std::back_inserter(res), "#define {} {}\n", name, (features & flag) != 0 ? '1' : '0'); + std::format_to(std::back_inserter(res), "#define {} {}\n", name, (variant.features & flag) != 0 ? '1' : '0'); } + + // eTransferFunction values, the shaders compare them against the CM_TRANSFER_FUNCTION_* in CM.glsl + std::format_to(std::back_inserter(res), "#define SOURCE_TF {}\n", sc(variant.sourceTF)); + std::format_to(std::back_inserter(res), "#define TARGET_TF {}\n", sc(variant.targetTF)); return res; } @@ -146,19 +150,26 @@ std::string CShaderLoader::process(const std::string& filename, const std::map("render:cm_enabled"); if (!*PCM) - features &= ~(SH_FEAT_CM | SH_FEAT_TONEMAP | SH_FEAT_ALT_TONEMAP | SH_FEAT_SDR_MOD); + variant.features &= ~(SH_FEAT_CM | SH_FEAT_TONEMAP | SH_FEAT_ALT_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 = ""; + // without CM the transfer functions are unused, keep them at the default so we don't cache + // several variants of identical source + if (!(variant.features & SH_FEAT_CM)) { + variant.sourceTF = SHADER_DEFAULT_TF; + variant.targetTF = SHADER_DEFAULT_TF; } - return m_fragVariants[frag][features]; + if (!m_fragVariants[frag].contains(variant)) { + ASSERT(m_fragFiles[frag].length()); + m_overrideDefines = getDefines(variant); + m_fragVariants[frag][variant] = processSource(m_fragFiles[frag]); + m_overrideDefines = ""; + } + + return m_fragVariants[frag][variant]; } const std::map& CShaderLoader::includes() { diff --git a/src/render/ShaderLoader.hpp b/src/render/ShaderLoader.hpp index 7e153dd66..032da697b 100644 --- a/src/render/ShaderLoader.hpp +++ b/src/render/ShaderLoader.hpp @@ -1,11 +1,13 @@ #pragma once #include +#include #include #include #include #include #include "../helpers/memory/Memory.hpp" +#include "../helpers/cm/ColorManagement.hpp" namespace Render { enum ePreparedFragmentShaderFeature : uint16_t { @@ -31,6 +33,15 @@ namespace Render { using ShaderFeatureFlags = uint16_t; + constexpr NColorManagement::eTransferFunction SHADER_DEFAULT_TF = NColorManagement::CM_TRANSFER_FUNCTION_SRGB; + struct SShaderVariant { + ShaderFeatureFlags features = 0; + NColorManagement::eTransferFunction sourceTF = SHADER_DEFAULT_TF; + NColorManagement::eTransferFunction targetTF = SHADER_DEFAULT_TF; + + auto operator<=>(const SShaderVariant&) const = default; + }; + enum ePreparedFragmentShader : uint8_t { SH_FRAG_QUAD = 0, SH_FRAG_PASSTHRURGBA, @@ -58,7 +69,7 @@ namespace Render { std::string process(const std::string& filename); std::string process(const std::string& filename, const std::map& defines); - std::string getVariantSource(ePreparedFragmentShader frag, ShaderFeatureFlags features); + std::string getVariantSource(ePreparedFragmentShader frag, SShaderVariant variant); const std::map& includes(); @@ -66,17 +77,17 @@ namespace Render { private: std::string loadShader(const std::string& filename); - std::string getDefines(ShaderFeatureFlags features); + std::string getDefines(const SShaderVariant& variant); std::string processSource(const std::string& source, glslang_stage_t stage = GLSLANG_STAGE_FRAGMENT); // - std::string m_shaderPath; - std::array m_fragFiles; - std::array, SH_FRAG_LAST> m_fragVariants; - std::map m_includes; + std::string m_shaderPath; + std::array m_fragFiles; + std::array, SH_FRAG_LAST> m_fragVariants; + std::map m_includes; - std::string m_overrideDefines; - glsl_include_callbacks_t m_callbacks; + std::string m_overrideDefines; + glsl_include_callbacks_t m_callbacks; }; inline UP g_pShaderLoader; diff --git a/src/render/gl/GLFramebuffer.cpp b/src/render/gl/GLFramebuffer.cpp index f8e70fe0b..98fb74ce9 100644 --- a/src/render/gl/GLFramebuffer.cpp +++ b/src/render/gl/GLFramebuffer.cpp @@ -35,14 +35,14 @@ bool CGLFramebuffer::internalAlloc(int w, int h, uint32_t drmFormat) { const auto format = getPixelFormatFromDRM(drmFormat); m_tex->bind(); glTexImage2D(GL_TEXTURE_2D, 0, format->glInternalFormat ? format->glInternalFormat : format->glFormat, w, h, 0, format->glFormat, format->glType, nullptr); - glBindFramebuffer(GL_FRAMEBUFFER, m_fb); + g_pHyprOpenGL->bindFramebuffer(GL_FRAMEBUFFER, m_fb); glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, m_tex->m_texID, 0); if (m_mirrorTex) { const auto format = getPixelFormatFromDRM(m_mirrorTex->m_drmFormat); m_mirrorTex->bind(); glTexImage2D(GL_TEXTURE_2D, 0, format->glInternalFormat ? format->glInternalFormat : format->glFormat, w, h, 0, format->glFormat, format->glType, nullptr); - glBindFramebuffer(GL_FRAMEBUFFER, m_fb); + g_pHyprOpenGL->bindFramebuffer(GL_FRAMEBUFFER, m_fb); glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT1, GL_TEXTURE_2D, m_mirrorTex->m_texID, 0); GLenum drawBuffers[] = {GL_COLOR_ATTACHMENT0, GL_COLOR_ATTACHMENT1}; glDrawBuffers(2, drawBuffers); @@ -70,13 +70,13 @@ bool CGLFramebuffer::internalAlloc(int w, int h, uint32_t drmFormat) { Log::logger->log(Log::DEBUG, "Framebuffer \"{}\" created, status {}", m_name, status); glBindTexture(GL_TEXTURE_2D, 0); - glBindFramebuffer(GL_READ_FRAMEBUFFER, 0); + g_pHyprOpenGL->bindFramebuffer(GL_READ_FRAMEBUFFER, 0); // this can run mid frame in enableMirror() in begin() restore the draw fb the renderer had bound if (g_pHyprRenderer && g_pHyprRenderer->m_renderData.currentFB) g_pHyprRenderer->m_renderData.currentFB->bind(); else - glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0); + g_pHyprOpenGL->bindFramebuffer(GL_DRAW_FRAMEBUFFER, 0); return true; } @@ -94,29 +94,43 @@ void CGLFramebuffer::bind() { // that means its a temp buffer that we have to raw bind and not change the viewport. // the temp buffer code binds this fb to add attachments themself if (m_tempBuf) { - glBindFramebuffer(GL_FRAMEBUFFER, m_fb); + if (g_pHyprOpenGL) + g_pHyprOpenGL->bindFramebuffer(GL_FRAMEBUFFER, m_fb); + else + glBindFramebuffer(GL_FRAMEBUFFER, m_fb); return; } - glBindFramebuffer(GL_DRAW_FRAMEBUFFER, m_fb); - if (g_pHyprOpenGL) + if (g_pHyprOpenGL) { + g_pHyprOpenGL->bindFramebuffer(GL_DRAW_FRAMEBUFFER, m_fb); g_pHyprOpenGL->setViewport(0, 0, m_size.x, m_size.y); - else + } else { + glBindFramebuffer(GL_DRAW_FRAMEBUFFER, m_fb); glViewport(0, 0, m_size.x, m_size.y); + } } void CGLFramebuffer::unbind() { - glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0); + if (g_pHyprOpenGL) + g_pHyprOpenGL->bindFramebuffer(GL_DRAW_FRAMEBUFFER, 0); + else + glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0); } void CGLFramebuffer::release() { if (m_fbAllocated) { - glBindFramebuffer(GL_FRAMEBUFFER, m_fb); + if (g_pHyprOpenGL) + g_pHyprOpenGL->bindFramebuffer(GL_FRAMEBUFFER, m_fb); + else + glBindFramebuffer(GL_FRAMEBUFFER, m_fb); + glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, 0, 0); if (m_mirrorTex) glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT1, GL_TEXTURE_2D, 0, 0); glDeleteFramebuffers(1, &m_fb); + if (g_pHyprOpenGL) + g_pHyprOpenGL->onFramebufferDeleted(m_fb); // releasing can happen mid frame from a temp fb, rebind the fb the renderer // had previously bound, otherwise draws continue into fb 0 and raise GL_INVALID_FRAMEBUFFER_OPERATION @@ -202,7 +216,7 @@ bool CGLFramebuffer::readPixels(CHLBufferReference buffer, uint32_t offsetX, uin } g_pHyprOpenGL->makeEGLCurrent(); - glBindFramebuffer(GL_READ_FRAMEBUFFER, getFBID()); + g_pHyprOpenGL->bindFramebuffer(GL_READ_FRAMEBUFFER, getFBID()); bind(); glPixelStorei(GL_PACK_ALIGNMENT, 1); @@ -240,7 +254,7 @@ bool CGLFramebuffer::readPixels(CHLBufferReference buffer, uint32_t offsetX, uin unbind(); glPixelStorei(GL_PACK_ALIGNMENT, 4); - glBindFramebuffer(GL_READ_FRAMEBUFFER, 0); + g_pHyprOpenGL->bindFramebuffer(GL_READ_FRAMEBUFFER, 0); return true; } diff --git a/src/render/shaders/glsl/blurfinish.frag b/src/render/shaders/glsl/blurfinish.frag index 51eb76849..298166fae 100644 --- a/src/render/shaders/glsl/blurfinish.frag +++ b/src/render/shaders/glsl/blurfinish.frag @@ -11,8 +11,8 @@ uniform float brightness; #include "defines.h" #if USE_CM -uniform int sourceTF; // eTransferFunction -uniform int targetTF; // eTransferFunction +const int sourceTF = SOURCE_TF; +const int targetTF = TARGET_TF; #include "CM.glsl" #endif diff --git a/src/render/shaders/glsl/blurprepare.frag b/src/render/shaders/glsl/blurprepare.frag index e96c54bb7..d2099025f 100644 --- a/src/render/shaders/glsl/blurprepare.frag +++ b/src/render/shaders/glsl/blurprepare.frag @@ -11,8 +11,8 @@ uniform sampler2D tex; uniform float contrast; uniform float brightness; -uniform int sourceTF; // eTransferFunction -uniform int targetTF; // eTransferFunction +const int sourceTF = SOURCE_TF; +const int targetTF = TARGET_TF; #if USE_CM uniform vec2 srcTFRange; diff --git a/src/render/shaders/glsl/border.frag b/src/render/shaders/glsl/border.frag index c57fb72e3..8b47d799d 100644 --- a/src/render/shaders/glsl/border.frag +++ b/src/render/shaders/glsl/border.frag @@ -5,8 +5,6 @@ precision highp float; in vec2 v_texcoord; -uniform int sourceTF; // eTransferFunction -uniform int targetTF; // eTransferFunction uniform mat3 targetPrimariesXYZ; uniform vec2 fullSizeUntransformed; @@ -28,6 +26,10 @@ uniform float roundingPower; uniform vec2 topLeft; uniform vec2 fullSize; #include "defines.h" + +const int sourceTF = SOURCE_TF; +const int targetTF = TARGET_TF; + #include "rounding.glsl" #include "CM.glsl" #include "border.glsl" diff --git a/src/render/shaders/glsl/defines.h b/src/render/shaders/glsl/defines.h index e1f50d6a1..64a9f971d 100644 --- a/src/render/shaders/glsl/defines.h +++ b/src/render/shaders/glsl/defines.h @@ -14,3 +14,5 @@ #define USE_ICC 0 #define USE_MIRROR 0 #define USE_BLUR_MATTE 0 +#define SOURCE_TF 9 // eTransferFunction, compiled in per variant. 9 is CM_TRANSFER_FUNCTION_SRGB +#define TARGET_TF 9 diff --git a/src/render/shaders/glsl/shadow.frag b/src/render/shaders/glsl/shadow.frag index bb83acaa5..e9c201a1c 100644 --- a/src/render/shaders/glsl/shadow.frag +++ b/src/render/shaders/glsl/shadow.frag @@ -31,7 +31,7 @@ uniform float gradientLerp; uniform float alpha; #if USE_CM -uniform int sourceTF; // eTransferFunction +const int sourceTF = SOURCE_TF; #endif #include "shadow.glsl" diff --git a/src/render/shaders/glsl/surface.frag b/src/render/shaders/glsl/surface.frag index 174c41e4c..c9a447dd6 100644 --- a/src/render/shaders/glsl/surface.frag +++ b/src/render/shaders/glsl/surface.frag @@ -51,8 +51,8 @@ uniform int motionSamples; #endif #if USE_CM -uniform int sourceTF; // eTransferFunction -uniform int targetTF; // eTransferFunction +const int sourceTF = SOURCE_TF; +const int targetTF = TARGET_TF; #if USE_TONEMAP || USE_SDR_MOD uniform mat3 targetPrimariesXYZ;