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.
This commit is contained in:
Tom Englund
2026-08-10 16:25:38 +02:00
committed by GitHub
parent f7daeb7f79
commit e3bbfe3cfa
13 changed files with 220 additions and 115 deletions
+89 -39
View File
@@ -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::pair<uint32_t, uint32_t>, std::array<GLfloat, 9>> primaries
void CHyprOpenGLImpl::passCMUniforms(WP<CShader> 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<CShader> 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<CShader> CHyprOpenGLImpl::renderToFBInternal(SP<ITexture> 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<CShader> CHyprOpenGLImpl::renderToFBInternal(SP<ITexture> 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<ITexture> 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<ITexture> 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<ITexture> 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<ITexture> 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<ITexture> 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<ITexture> 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<ITexture> 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<ITexture> tex, const CBox& box, SP<I
shader->setUniformInt(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<IFramebuffer> 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<IFramebuffer> 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<IFramebuffer> CHyprOpenGLImpl::blurFramebufferWithDamage(float a, CRegion* or
else
PMIRRORFB->bind();
glActiveTexture(GL_TEXTURE0);
setActiveTexture(GL_TEXTURE0);
auto currentTex = currentRenderToFB->getTexture();
@@ -1969,7 +1967,7 @@ SP<IFramebuffer> CHyprOpenGLImpl::blurFramebufferWithDamage(float a, CRegion* or
else
PMIRRORFB->bind();
glActiveTexture(GL_TEXTURE0);
setActiveTexture(GL_TEXTURE0);
auto currentTex = currentRenderToFB->getTexture();
@@ -1980,10 +1978,10 @@ SP<IFramebuffer> 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<ITexture> 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<CShader> 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<CShader> CHyprOpenGLImpl::getShaderVariant(ePreparedFragmentShader frag, ShaderFeatureFlags features) {
WP<CShader> CHyprOpenGLImpl::getShaderVariant(ePreparedFragmentShader frag, ShaderFeatureFlags features, eTransferFunction sourceTF, eTransferFunction targetTF) {
return getShaderVariant(frag, SShaderVariant{.features = features, .sourceTF = sourceTF, .targetTF = targetTF});
}
WP<CShader> 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<CShader>();
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<int>(variant.sourceTF), sc<int>(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;
}
+45 -28
View File
@@ -89,9 +89,9 @@ namespace Render::GL {
};
struct SPreparedShaders {
std::string TEXVERTSRC;
std::string TEXVERTSRC320;
std::array<std::map<Render::ShaderFeatureFlags, SP<CShader>>, Render::SH_FRAG_LAST> fragVariants;
std::string TEXVERTSRC;
std::string TEXVERTSRC320;
std::array<std::map<Render::SShaderVariant, SP<CShader>>, 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<IFramebuffer> off);
void renderOffToMain(SP<IFramebuffer> off);
std::vector<SDRMFormat> getDRMFormats();
std::vector<uint64_t> getDRMFormatModifiers(DRMFormat format);
@@ -248,7 +254,10 @@ namespace Render::GL {
bool explicitSyncSupported();
bool fp16Supported();
WP<CShader> getShaderVariant(Render::ePreparedFragmentShader frag, Render::ShaderFeatureFlags features = 0);
WP<CShader> getShaderVariant(Render::ePreparedFragmentShader frag, Render::ShaderFeatureFlags features = 0,
NColorManagement::eTransferFunction sourceTF = Render::SHADER_DEFAULT_TF,
NColorManagement::eTransferFunction targetTF = Render::SHADER_DEFAULT_TF);
WP<CShader> getShaderVariant(Render::ePreparedFragmentShader frag, const Render::SShaderVariant& variant);
bool m_shadersInitialized = false;
SP<SPreparedShaders> m_shaders;
@@ -316,25 +325,33 @@ namespace Render::GL {
std::array<bool, CAP_STATUS_END> m_capStatus = {};
std::vector<SDRMFormat> 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<SDRMFormat> 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<CShader> 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<CShader> m_finalScreenShader;
GLuint m_currentProgram;
void initDRMFormats();
void initEGL(bool gbm);
EGLDeviceEXT eglDeviceFromDRMFD(int drmFD);
// for the final shader
std::array<CTimer, POINTER_PRESSED_HISTORY_LENGTH> m_pressedHistoryTimers = {};
+6 -6
View File
@@ -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);
-2
View File
@@ -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,
+22 -11
View File
@@ -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<std::pair<std::string_view, ePreparedFragmentShaderFeature>>({
{"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<int>(variant.sourceTF));
std::format_to(std::back_inserter(res), "#define TARGET_TF {}\n", sc<int>(variant.targetTF));
return res;
}
@@ -146,19 +150,26 @@ std::string CShaderLoader::process(const std::string& filename, const std::map<s
return res;
}
std::string CShaderLoader::getVariantSource(ePreparedFragmentShader frag, ShaderFeatureFlags features) {
std::string CShaderLoader::getVariantSource(ePreparedFragmentShader frag, SShaderVariant variant) {
static const auto PCM = CConfigValue<Config::INTEGER>("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<std::string, std::string>& CShaderLoader::includes() {
+19 -8
View File
@@ -1,11 +1,13 @@
#pragma once
#include <array>
#include <compare>
#include <glslang/Include/glslang_c_interface.h>
#include <string>
#include <vector>
#include <map>
#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<std::string, std::string>& defines);
std::string getVariantSource(ePreparedFragmentShader frag, ShaderFeatureFlags features);
std::string getVariantSource(ePreparedFragmentShader frag, SShaderVariant variant);
const std::map<std::string, std::string>& 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<std::string, SH_FRAG_LAST> m_fragFiles;
std::array<std::map<ShaderFeatureFlags, std::string>, SH_FRAG_LAST> m_fragVariants;
std::map<std::string, std::string> m_includes;
std::string m_shaderPath;
std::array<std::string, SH_FRAG_LAST> m_fragFiles;
std::array<std::map<SShaderVariant, std::string>, SH_FRAG_LAST> m_fragVariants;
std::map<std::string, std::string> m_includes;
std::string m_overrideDefines;
glsl_include_callbacks_t m_callbacks;
std::string m_overrideDefines;
glsl_include_callbacks_t m_callbacks;
};
inline UP<CShaderLoader> g_pShaderLoader;
+26 -12
View File
@@ -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;
}
+2 -2
View File
@@ -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
+2 -2
View File
@@ -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;
+4 -2
View File
@@ -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"
+2
View File
@@ -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
+1 -1
View File
@@ -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"
+2 -2
View File
@@ -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;