diff options
| author | UjinT34 <41110182+UjinT34@users.noreply.github.com> | 2026-03-07 00:05:10 +0300 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2026-03-06 21:05:10 +0000 |
| commit | a5858018d896e0e0f0db517f893cd9a0936dd881 (patch) | |
| tree | 64bec75dc882ea27d5ee62a6ba25b22fa78146ae /src/render/OpenGL.cpp | |
| parent | 8685fd7b0c2afe06c798554dea80c53f98d73894 (diff) | |
renderer: shader variants refactor (#13434)
Part 0 of renderer reworks.
Diffstat (limited to 'src/render/OpenGL.cpp')
| -rw-r--r-- | src/render/OpenGL.cpp | 777 |
1 files changed, 332 insertions, 445 deletions
diff --git a/src/render/OpenGL.cpp b/src/render/OpenGL.cpp index d43e1c69..7fe001d4 100644 --- a/src/render/OpenGL.cpp +++ b/src/render/OpenGL.cpp @@ -1,5 +1,6 @@ #include <GLES3/gl32.h> #include <hyprgraphics/color/Color.hpp> +#include <hyprutils/math/Misc.hpp> #include <hyprutils/string/String.hpp> #include <hyprutils/path/Path.hpp> #include <numbers> @@ -31,6 +32,7 @@ #include "../managers/screenshare/ScreenshareManager.hpp" #include "debug/HyprNotificationOverlay.hpp" #include "hyprerror/HyprError.hpp" +#include "macros.hpp" #include "pass/TexPassElement.hpp" #include "pass/RectPassElement.hpp" #include "pass/PreBlurElement.hpp" @@ -44,20 +46,14 @@ #include <xf86drm.h> #include <fcntl.h> #include <gbm.h> -#include <filesystem> #include <cstring> -#include "./shaders/Shaders.hpp" +#include "ShaderLoader.hpp" +#include "Texture.hpp" +#include <filesystem> using namespace Hyprutils::OS; using namespace NColorManagement; - -const std::vector<const char*> ASSET_PATHS = { -#ifdef DATAROOTDIR - DATAROOTDIR, -#endif - "/usr/share", - "/usr/local/share", -}; +using namespace Render; static inline void loadGLProc(void* pProc, const char* name) { void* proc = rc<void*>(eglGetProcAddress(name)); @@ -880,124 +876,30 @@ void CHyprOpenGLImpl::setDamage(const CRegion& damage_, std::optional<CRegion> f m_renderData.finalDamage.set(finalDamage.value_or(damage_)); } -// TODO notify user if bundled shader is newer than ~/.config override -static std::string loadShader(const std::string& filename) { - 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)); -} - -static void loadShaderInclude(const std::string& filename, std::map<std::string, std::string>& includes) { - includes.insert({filename, loadShader(filename)}); -} - -static void processShaderIncludes(std::string& source, const std::map<std::string, std::string>& includes) { - for (auto it = includes.begin(); it != includes.end(); ++it) { - Hyprutils::String::replaceInString(source, "#include \"" + it->first + "\"", it->second); - } -} - -static const uint8_t MAX_INCLUDE_DEPTH = 3; +static const std::vector<std::string> SHADER_INCLUDES = { + "defines.h", "constants.h", "cm_helpers.glsl", "rounding.glsl", "CM.glsl", "tonemap.glsl", "gain.glsl", + "border.glsl", "shadow.glsl", "blurprepare.glsl", "blur1.glsl", "blur2.glsl", "blurFinish.glsl", +}; -static std::string processShader(const std::string& filename, const std::map<std::string, std::string>& includes, const uint8_t includeDepth = 1) { - auto source = loadShader(filename); - for (auto i = 0; i < includeDepth; i++) { - processShaderIncludes(source, includes); - } - return source; -} +// order matters, see ePreparedFragmentShader +const std::array<std::string, SH_FRAG_LAST> FRAG_SHADERS = { + "quad.frag", "passthru.frag", "rgbamatte.frag", "ext.frag", "blur1.frag", "blur2.frag", + "blurprepare.frag", "blurfinish.frag", "shadow.frag", "surface.frag", "border.frag", "glitch.frag", +}; -bool CHyprOpenGLImpl::initShaders() { - auto shaders = makeShared<SPreparedShaders>(); - std::map<std::string, std::string> includes; - const bool isDynamic = m_shadersInitialized; - static const auto PCM = CConfigValue<Hyprlang::INT>("render:cm_enabled"); +bool CHyprOpenGLImpl::initShaders(const std::string& path) { + auto shaders = makeShared<SPreparedShaders>(); + static const auto PCM = CConfigValue<Hyprlang::INT>("render:cm_enabled"); try { - loadShaderInclude("get_rgb_pixel.glsl", includes); - loadShaderInclude("get_rgba_pixel.glsl", includes); - loadShaderInclude("get_rgbx_pixel.glsl", includes); - loadShaderInclude("discard.glsl", includes); - loadShaderInclude("do_discard.glsl", includes); - loadShaderInclude("tint.glsl", includes); - loadShaderInclude("do_tint.glsl", includes); - loadShaderInclude("rounding.glsl", includes); - loadShaderInclude("do_rounding.glsl", includes); - loadShaderInclude("surface_CM.glsl", includes); - loadShaderInclude("CM.glsl", includes); - loadShaderInclude("do_CM.glsl", includes); - loadShaderInclude("tonemap.glsl", includes); - loadShaderInclude("do_tonemap.glsl", includes); - loadShaderInclude("sdr_mod.glsl", includes); - loadShaderInclude("do_sdr_mod.glsl", includes); - loadShaderInclude("primaries_xyz.glsl", includes); - loadShaderInclude("primaries_xyz_uniform.glsl", includes); - loadShaderInclude("primaries_xyz_const.glsl", includes); - loadShaderInclude("gain.glsl", includes); - loadShaderInclude("border.glsl", includes); - - shaders->TEXVERTSRC = processShader("tex300.vert", includes); - shaders->TEXVERTSRC320 = processShader("tex320.vert", includes); - - if (!*PCM) - m_cmSupported = false; - else { - std::vector<SFragShaderDesc> CM_SHADERS = {{ - {SH_FRAG_CM_BLURPREPARE, "CMblurprepare.frag"}, - {SH_FRAG_CM_BORDER1, "CMborder.frag"}, - }}; + auto shaderLoader = makeUnique<CShaderLoader>(SHADER_INCLUDES, FRAG_SHADERS, path); - bool success = false; - for (const auto& desc : CM_SHADERS) { - const auto fragSrc = processShader(desc.file, includes, MAX_INCLUDE_DEPTH); - - if (!(success = shaders->frag[desc.id]->createProgram(shaders->TEXVERTSRC, fragSrc, true, true))) - break; - } + shaders->TEXVERTSRC = shaderLoader->process("tex300.vert"); + shaders->TEXVERTSRC320 = shaderLoader->process("tex320.vert"); - if (m_shadersInitialized && m_cmSupported && !success) - g_pHyprNotificationOverlay->addNotification(I18n::i18nEngine()->localize(I18n::TXT_KEY_NOTIF_CM_RELOAD_FAILED), CHyprColor{}, 15000, ICON_WARNING); + m_cmSupported = *PCM; - m_cmSupported = success; - - if (!m_cmSupported) - Log::logger->log( - Log::ERR, - "WARNING: CM Shader failed compiling, color management will not work. It's likely because your GPU is an old piece of garbage, don't file bug reports " - "about this!"); - } - - std::vector<SFragShaderDesc> FRAG_SHADERS = {{ - {SH_FRAG_QUAD, "quad.frag"}, - {SH_FRAG_PASSTHRURGBA, "passthru.frag"}, - {SH_FRAG_MATTE, "rgbamatte.frag"}, - {SH_FRAG_GLITCH, "glitch.frag"}, - {SH_FRAG_EXT, "ext.frag"}, - {SH_FRAG_BLUR1, "blur1.frag"}, - {SH_FRAG_BLUR2, "blur2.frag"}, - {SH_FRAG_BLURPREPARE, "blurprepare.frag"}, - {SH_FRAG_BLURFINISH, "blurfinish.frag"}, - {SH_FRAG_SHADOW, "shadow.frag"}, - {SH_FRAG_BORDER1, "border.frag"}, - }}; - - for (const auto& desc : FRAG_SHADERS) { - const auto fragSrc = processShader(desc.file, includes, MAX_INCLUDE_DEPTH); - - if (!shaders->frag[desc.id]->createProgram(shaders->TEXVERTSRC, fragSrc, isDynamic)) - return false; - } + g_pShaderLoader = std::move(shaderLoader); } catch (const std::exception& e) { if (!m_shadersInitialized) @@ -1008,7 +910,6 @@ bool CHyprOpenGLImpl::initShaders() { } m_shaders = shaders; - m_includes = includes; m_shadersInitialized = true; Log::logger->log(Log::DEBUG, "Shaders initialized successfully."); @@ -1192,7 +1093,7 @@ void CHyprOpenGLImpl::renderRectWithDamageInternal(const CBox& box, const CHyprC newBox, Math::wlTransformToHyprutils(Math::invertTransform(!m_monitorTransformEnabled ? WL_OUTPUT_TRANSFORM_NORMAL : m_renderData.pMonitor->m_transform)), newBox.rot); Mat3x3 glMatrix = m_renderData.projection.copy().multiply(matrix); - auto shader = useShader(m_shaders->frag[SH_FRAG_QUAD]); + auto shader = useShader(getShaderVariant(SH_FRAG_QUAD, data.round > 0 ? SH_FEAT_ROUNDING : 0)); shader->setUniformMatrix3fv(SHADER_PROJ, 1, GL_TRUE, glMatrix.getMatrix()); // premultiply the color as well as we don't work with straight alpha @@ -1272,60 +1173,24 @@ static bool isHDR2SDR(const NColorManagement::SImageDescription& imageDescriptio void CHyprOpenGLImpl::passCMUniforms(WP<CShader> shader, const NColorManagement::PImageDescription imageDescription, const NColorManagement::PImageDescription targetImageDescription, bool modifySDR, float sdrMinLuminance, int sdrMaxLuminance) { - const auto sdrEOTF = NTransferFunction::fromConfig(); - - if (m_renderData.surface.valid()) { - if (m_renderData.surface->m_colorManagement.valid()) { - if (sdrEOTF == NTransferFunction::TF_FORCED_GAMMA22 && imageDescription->value().transferFunction == NColorManagement::eTransferFunction::CM_TRANSFER_FUNCTION_SRGB) - shader->setUniformInt(SHADER_SOURCE_TF, NColorManagement::eTransferFunction::CM_TRANSFER_FUNCTION_GAMMA22); - else - shader->setUniformInt(SHADER_SOURCE_TF, imageDescription->value().transferFunction); - } else if (sdrEOTF == NTransferFunction::TF_SRGB) - shader->setUniformInt(SHADER_SOURCE_TF, NColorManagement::eTransferFunction::CM_TRANSFER_FUNCTION_SRGB); - else if (sdrEOTF == NTransferFunction::TF_GAMMA22 || sdrEOTF == NTransferFunction::TF_FORCED_GAMMA22) - shader->setUniformInt(SHADER_SOURCE_TF, NColorManagement::eTransferFunction::CM_TRANSFER_FUNCTION_GAMMA22); - else - shader->setUniformInt(SHADER_SOURCE_TF, imageDescription->value().transferFunction); - } else - shader->setUniformInt(SHADER_SOURCE_TF, imageDescription->value().transferFunction); - - shader->setUniformInt(SHADER_TARGET_TF, targetImageDescription->value().transferFunction); - - const auto targetPrimaries = targetImageDescription->getPrimaries(); - const auto mat = targetPrimaries->value().toXYZ().mat(); - const std::array<GLfloat, 9> glTargetPrimariesXYZ = { - mat[0][0], mat[1][0], mat[2][0], // - mat[0][1], mat[1][1], mat[2][1], // - mat[0][2], mat[1][2], mat[2][2], // - }; - shader->setUniformMatrix3fv(SHADER_TARGET_PRIMARIES_XYZ, 1, false, glTargetPrimariesXYZ); - - const bool needsSDRmod = modifySDR && isSDR2HDR(imageDescription->value(), targetImageDescription->value()); - const bool needsHDRmod = !needsSDRmod && isHDR2SDR(imageDescription->value(), targetImageDescription->value()); - - shader->setUniformFloat2(SHADER_SRC_TF_RANGE, imageDescription->value().getTFMinLuminance(needsSDRmod ? sdrMinLuminance : -1), - imageDescription->value().getTFMaxLuminance(needsSDRmod ? sdrMaxLuminance : -1)); - shader->setUniformFloat2(SHADER_DST_TF_RANGE, targetImageDescription->value().getTFMinLuminance(needsSDRmod ? sdrMinLuminance : -1), - targetImageDescription->value().getTFMaxLuminance(needsSDRmod ? sdrMaxLuminance : -1)); - - shader->setUniformFloat(SHADER_SRC_REF_LUMINANCE, imageDescription->value().luminances.reference); - shader->setUniformFloat(SHADER_DST_REF_LUMINANCE, targetImageDescription->value().luminances.reference); - - const float maxLuminance = needsHDRmod ? - imageDescription->value().getTFMaxLuminance(-1) : - (imageDescription->value().luminances.max > 0 ? imageDescription->value().luminances.max : imageDescription->value().luminances.reference); - - shader->setUniformFloat(SHADER_MAX_LUMINANCE, maxLuminance * targetImageDescription->value().luminances.reference / imageDescription->value().luminances.reference); - shader->setUniformFloat(SHADER_DST_MAX_LUMINANCE, targetImageDescription->value().luminances.max > 0 ? targetImageDescription->value().luminances.max : 10000); - shader->setUniformFloat(SHADER_SDR_SATURATION, needsSDRmod && m_renderData.pMonitor->m_sdrSaturation > 0 ? m_renderData.pMonitor->m_sdrSaturation : 1.0f); - shader->setUniformFloat(SHADER_SDR_BRIGHTNESS, needsSDRmod && m_renderData.pMonitor->m_sdrBrightness > 0 ? m_renderData.pMonitor->m_sdrBrightness : 1.0f); + const auto settings = g_pHyprRenderer->getCMSettings(imageDescription, targetImageDescription, m_renderData.surface.valid() ? m_renderData.surface.lock() : nullptr, modifySDR, + sdrMinLuminance, sdrMaxLuminance); + + 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); + shader->setUniformFloat(SHADER_DST_REF_LUMINANCE, settings.dstRefLuminance); + shader->setUniformFloat(SHADER_MAX_LUMINANCE, settings.maxLuminance); + shader->setUniformFloat(SHADER_DST_MAX_LUMINANCE, settings.dstMaxLuminance); + shader->setUniformFloat(SHADER_SDR_SATURATION, settings.sdrSaturation); + shader->setUniformFloat(SHADER_SDR_BRIGHTNESS, settings.sdrBrightnessMultiplier); if (!targetImageDescription->value().icc.present) { const auto cacheKey = std::make_pair(imageDescription->id(), targetImageDescription->id()); - if (!primariesConversionCache.contains(cacheKey)) { - auto conversion = imageDescription->getPrimaries()->convertMatrix(targetImageDescription->getPrimaries()); - const auto mat = conversion.mat(); + const auto& mat = settings.convertMatrix; const std::array<GLfloat, 9> glConvertMatrix = { mat[0][0], mat[1][0], mat[2][0], // mat[0][1], mat[1][1], mat[2][1], // @@ -1335,12 +1200,14 @@ void CHyprOpenGLImpl::passCMUniforms(WP<CShader> shader, const NColorManagement: } shader->setUniformMatrix3fv(SHADER_CONVERT_MATRIX, 1, false, primariesConversionCache[cacheKey]); - shader->setUniformInt(SHADER_USE_ICC, 0); - shader->setUniformInt(SHADER_LUT_3D, 8); // req'd for ogl + const auto mat = settings.dstPrimaries2XYZ; + const std::array<GLfloat, 9> glTargetPrimariesXYZ = { + mat[0][0], mat[1][0], mat[2][0], // + mat[0][1], mat[1][1], mat[2][1], // + mat[0][2], mat[1][2], mat[2][2], // + }; + shader->setUniformMatrix3fv(SHADER_TARGET_PRIMARIES_XYZ, 1, false, glTargetPrimariesXYZ); } else { - // ICC path, use a 3D LUT - shader->setUniformInt(SHADER_USE_ICC, 1); - // TODO: this sucks GLCALL(glActiveTexture(GL_TEXTURE8)); targetImageDescription->value().icc.lutTexture->bind(); @@ -1353,100 +1220,108 @@ void CHyprOpenGLImpl::passCMUniforms(WP<CShader> shader, const NColorManagement: } void CHyprOpenGLImpl::passCMUniforms(WP<CShader> shader, const PImageDescription imageDescription) { - passCMUniforms(shader, imageDescription, m_renderData.pMonitor->m_imageDescription, true, m_renderData.pMonitor->m_sdrMinLuminance, m_renderData.pMonitor->m_sdrMaxLuminance); + passCMUniforms(shader, imageDescription, g_pHyprRenderer->workBufferImageDescription(), true, m_renderData.pMonitor->m_sdrMinLuminance, + m_renderData.pMonitor->m_sdrMaxLuminance); } -void CHyprOpenGLImpl::renderTextureInternal(SP<CTexture> tex, const CBox& box, const STextureRenderData& data) { - RASSERT(m_renderData.pMonitor, "Tried to render texture without begin()!"); - RASSERT((tex->m_texID > 0), "Attempted to draw nullptr texture!"); +WP<CShader> CHyprOpenGLImpl::renderToOutputInternal() { + static const auto PDT = CConfigValue<Hyprlang::INT>("debug:damage_tracking"); + static const auto PCURSORTIMEOUT = CConfigValue<Hyprlang::FLOAT>("cursor:inactive_timeout"); - TRACY_GPU_ZONE("RenderTextureInternalWithDamage"); + WP<CShader> shader = + g_pHyprRenderer->m_crashingInProgress ? getShaderVariant(SH_FRAG_GLITCH) : (m_finalScreenShader->program() ? m_finalScreenShader : getShaderVariant(SH_FRAG_PASSTHRURGBA)); - float alpha = std::clamp(data.a, 0.f, 1.f); + shader = useShader(shader); - if (data.damage->empty()) - return; + if (*PDT == 0 || g_pHyprRenderer->m_crashingInProgress) + shader->setUniformFloat(SHADER_TIME, m_globalTimer.getSeconds() - shader->getInitialTime()); + else + shader->setUniformFloat(SHADER_TIME, 0.f); + + shader->setUniformInt(SHADER_WL_OUTPUT, m_renderData.pMonitor->m_id); + shader->setUniformFloat2(SHADER_FULL_SIZE, m_renderData.pMonitor->m_pixelSize.x, m_renderData.pMonitor->m_pixelSize.y); + shader->setUniformFloat(SHADER_POINTER_INACTIVE_TIMEOUT, *PCURSORTIMEOUT); + shader->setUniformInt(SHADER_POINTER_HIDDEN, g_pHyprRenderer->m_cursorHiddenByCondition); + shader->setUniformInt(SHADER_POINTER_KILLING, g_pInputManager->getClickMode() == CLICKMODE_KILL); + shader->setUniformInt(SHADER_POINTER_SHAPE, g_pHyprRenderer->m_lastCursorData.shape); + shader->setUniformInt(SHADER_POINTER_SHAPE_PREVIOUS, g_pHyprRenderer->m_lastCursorData.shapePrevious); + shader->setUniformFloat(SHADER_POINTER_SIZE, g_pCursorManager->getScaledSize()); + + if (*PDT == 0) { + PHLMONITORREF pMonitor = m_renderData.pMonitor; + Vector2D p = ((g_pInputManager->getMouseCoordsInternal() - pMonitor->m_position) * pMonitor->m_scale); + p = p.transform(Math::wlTransformToHyprutils(pMonitor->m_transform), pMonitor->m_pixelSize); + shader->setUniformFloat2(SHADER_POINTER, p.x / pMonitor->m_pixelSize.x, p.y / pMonitor->m_pixelSize.y); - CBox newBox = box; - m_renderData.renderModif.applyToBox(newBox); + std::vector<float> pressedPos = m_pressedHistoryPositions | std::views::transform([&](const Vector2D& vec) { + Vector2D pPressed = ((vec - pMonitor->m_position) * pMonitor->m_scale); + pPressed = pPressed.transform(Math::wlTransformToHyprutils(pMonitor->m_transform), pMonitor->m_pixelSize); + return std::array<float, 2>{pPressed.x / pMonitor->m_pixelSize.x, pPressed.y / pMonitor->m_pixelSize.y}; + }) | + std::views::join | std::ranges::to<std::vector<float>>(); - static const auto PDT = CConfigValue<Hyprlang::INT>("debug:damage_tracking"); - static const auto PPASS = CConfigValue<Hyprlang::INT>("render:cm_fs_passthrough"); - static const auto PENABLECM = CConfigValue<Hyprlang::INT>("render:cm_enabled"); - static const auto PCURSORTIMEOUT = CConfigValue<Hyprlang::FLOAT>("cursor:inactive_timeout"); + shader->setUniform2fv(SHADER_POINTER_PRESSED_POSITIONS, pressedPos.size(), pressedPos); - // get the needed transform for this texture - const auto MONITOR_INVERTED = Math::wlTransformToHyprutils(Math::invertTransform(m_renderData.pMonitor->m_transform)); - Hyprutils::Math::eTransform TRANSFORM = tex->m_transform; + std::vector<float> pressedTime = + m_pressedHistoryTimers | std::views::transform([](const CTimer& timer) { return timer.getSeconds(); }) | std::ranges::to<std::vector<float>>(); - if (m_monitorTransformEnabled) - TRANSFORM = Math::composeTransform(MONITOR_INVERTED, TRANSFORM); + shader->setUniform1fv(SHADER_POINTER_PRESSED_TIMES, pressedTime.size(), pressedTime); - Mat3x3 matrix = m_renderData.monitorProjection.projectBox(newBox, TRANSFORM, newBox.rot); - Mat3x3 glMatrix = m_renderData.projection.copy().multiply(matrix); + shader->setUniformInt(SHADER_POINTER_PRESSED_KILLED, m_pressedHistoryKilled); + shader->setUniformInt(SHADER_POINTER_PRESSED_TOUCHED, m_pressedHistoryTouched); - WP<CShader> shader; + shader->setUniformFloat(SHADER_POINTER_LAST_ACTIVE, g_pInputManager->m_lastCursorMovement.getSeconds()); + shader->setUniformFloat(SHADER_POINTER_SWITCH_TIME, g_pHyprRenderer->m_lastCursorData.switchedTimer.getSeconds()); - const bool CRASHING = m_applyFinalShader && g_pHyprRenderer->m_crashingInProgress; - const bool CUSTOM_FINAL_SHADER = !CRASHING && m_applyFinalShader && m_finalScreenShader->program(); + } else { + shader->setUniformFloat2(SHADER_POINTER, 0.f, 0.f); - uint8_t shaderFeatures = 0; + static const std::vector<float> pressedPosDefault(POINTER_PRESSED_HISTORY_LENGTH * 2uz, 0.f); + static const std::vector<float> pressedTimeDefault(POINTER_PRESSED_HISTORY_LENGTH, 0.f); - if (CRASHING) - shader = m_shaders->frag[SH_FRAG_GLITCH]; - else if (CUSTOM_FINAL_SHADER) - shader = m_finalScreenShader; - else { - if (m_applyFinalShader) - shaderFeatures &= ~SH_FEAT_RGBA; - else { - switch (tex->m_type) { - case TEXTURE_RGBA: shaderFeatures |= SH_FEAT_RGBA; break; - case TEXTURE_RGBX: shaderFeatures &= ~SH_FEAT_RGBA; break; + shader->setUniform2fv(SHADER_POINTER_PRESSED_POSITIONS, pressedPosDefault.size(), pressedPosDefault); + shader->setUniform1fv(SHADER_POINTER_PRESSED_TIMES, pressedTimeDefault.size(), pressedTimeDefault); + shader->setUniformInt(SHADER_POINTER_PRESSED_KILLED, 0); - case TEXTURE_EXTERNAL: shader = m_shaders->frag[SH_FRAG_EXT]; break; // might be unused - default: RASSERT(false, "tex->m_iTarget unsupported!"); - } - } + shader->setUniformFloat(SHADER_POINTER_LAST_ACTIVE, 0.f); + shader->setUniformFloat(SHADER_POINTER_SWITCH_TIME, 0.f); } - if (data.finalMonitorCM || (m_renderData.currentWindow && m_renderData.currentWindow->m_ruleApplicator->RGBX().valueOrDefault())) - shaderFeatures &= ~SH_FEAT_RGBA; + if (g_pHyprRenderer->m_crashingInProgress) { + shader->setUniformFloat(SHADER_DISTORT, g_pHyprRenderer->m_crashingDistort); + shader->setUniformFloat2(SHADER_FULL_SIZE, m_renderData.pMonitor->m_pixelSize.x, m_renderData.pMonitor->m_pixelSize.y); + } - glActiveTexture(GL_TEXTURE0); - tex->bind(); + return shader; +} - tex->setTexParameter(GL_TEXTURE_WRAP_S, data.wrapX); - tex->setTexParameter(GL_TEXTURE_WRAP_T, data.wrapY); +WP<CShader> CHyprOpenGLImpl::renderToFBInternal(const STextureRenderData& data, eTextureType texType, const CBox& newBox) { + static const auto PPASS = CConfigValue<Hyprlang::INT>("render:cm_fs_passthrough"); + static const auto PENABLECM = CConfigValue<Hyprlang::INT>("render:cm_enabled"); + static auto PBLEND = CConfigValue<Hyprlang::INT>("render:use_shader_blur_blend"); - if (m_renderData.useNearestNeighbor) { - tex->setTexParameter(GL_TEXTURE_MAG_FILTER, GL_NEAREST); - tex->setTexParameter(GL_TEXTURE_MIN_FILTER, GL_NEAREST); - } else { - tex->setTexParameter(GL_TEXTURE_MAG_FILTER, tex->magFilter); - tex->setTexParameter(GL_TEXTURE_MIN_FILTER, tex->minFilter); - } + float alpha = std::clamp(data.a, 0.f, 1.f); - const bool isHDRSurface = m_renderData.surface.valid() && m_renderData.surface->m_colorManagement.valid() ? m_renderData.surface->m_colorManagement->isHDR() : false; - const bool canPassHDRSurface = isHDRSurface && !m_renderData.surface->m_colorManagement->isWindowsScRGB(); // windows scRGB requires CM shader + WP<CShader> shader; + ShaderFeatureFlags shaderFeatures = 0; - // Color pipeline: - // Client ---> sRGB chosen EOTF (render buf) ---> Monitor (target) - // + switch (texType) { + case TEXTURE_RGBA: shaderFeatures |= SH_FEAT_RGBA; break; + case TEXTURE_RGBX: shaderFeatures &= ~SH_FEAT_RGBA; break; - const auto CONFIG_SDR_EOTF = NTransferFunction::fromConfig(); - const bool IS_MONITOR_ICC = m_renderData.pMonitor->m_imageDescription.valid() && m_renderData.pMonitor->m_imageDescription->value().icc.present; - const auto CHOSEN_SDR_EOTF = [&] { - // if the monitor is ICC'd, use SRGB for best ΔE. - if (IS_MONITOR_ICC) - return CM_TRANSFER_FUNCTION_SRGB; + // TODO set correct features + case TEXTURE_EXTERNAL: shader = getShaderVariant(SH_FRAG_EXT, SH_FEAT_ROUNDING | SH_FEAT_DISCARD | SH_FEAT_TINT); break; // might be unused + default: RASSERT(false, "tex->m_iTarget unsupported!"); + } - // otherwise use configured - if (CONFIG_SDR_EOTF != NTransferFunction::TF_SRGB) - return CM_TRANSFER_FUNCTION_GAMMA22; - return CM_TRANSFER_FUNCTION_SRGB; - }(); - const auto WORK_BUFFER_IMAGE_DESCRIPTION = CImageDescription::from(NColorManagement::SImageDescription{.transferFunction = CHOSEN_SDR_EOTF}); + if (data.finalMonitorCM || (m_renderData.currentWindow && m_renderData.currentWindow->m_ruleApplicator->RGBX().valueOrDefault())) + shaderFeatures &= ~SH_FEAT_RGBA; + + const auto surface = m_renderData.surface; + const bool isHDRSurface = surface.valid() && surface->m_colorManagement.valid() ? surface->m_colorManagement->isHDR() : false; + const bool canPassHDRSurface = isHDRSurface && !surface->m_colorManagement->isWindowsScRGB(); // windows scRGB requires CM shader + + const auto WORK_BUFFER_IMAGE_DESCRIPTION = g_pHyprRenderer->workBufferImageDescription(); // chosenSdrEotf contains the valid eotf for this display @@ -1472,25 +1347,26 @@ void CHyprOpenGLImpl::renderTextureInternal(SP<CTexture> tex, const CBox& box, c // for final CM, use the target description if (data.finalMonitorCM) return m_renderData.pMonitor->m_imageDescription; - // otherwise, use chosen, we're drawing into the work buffer // NOLINTNEXTLINE return WORK_BUFFER_IMAGE_DESCRIPTION; }(); + if (data.blur && *PBLEND && data.blurredBG) + shaderFeatures |= SH_FEAT_BLUR; + + if (data.discardActive) + shaderFeatures |= SH_FEAT_DISCARD; + const bool CANT_CHECK_CM_EQUALITY = data.cmBackToSRGB || data.finalMonitorCM || (!m_renderData.surface || !m_renderData.surface->m_colorManagement); - const bool skipCM = data.noCM /* manual CM disable */ - || !*PENABLECM || !m_cmSupported /* CM unsupported or disabled */ + const bool skipCM = !*PENABLECM || !m_cmSupported /* CM unsupported or disabled */ || m_renderData.pMonitor->doesNoShaderCM() /* no shader needed */ || (SOURCE_IMAGE_DESCRIPTION->id() == TARGET_IMAGE_DESCRIPTION->id() && !CANT_CHECK_CM_EQUALITY) /* Source and target have the same image description */ || (((*PPASS && canPassHDRSurface) || (*PPASS == 1 && !isHDRSurface && m_renderData.pMonitor->m_cmType != NCMType::CM_HDR && m_renderData.pMonitor->m_cmType != NCMType::CM_HDR_EDID)) && m_renderData.pMonitor->inFullscreenMode()) /* Fullscreen window with pass cm enabled */; - if (data.discardActive) - shaderFeatures |= SH_FEAT_DISCARD; - if (data.allowDim && m_renderData.currentWindow && (m_renderData.currentWindow->m_notRespondingTint->value() > 0 || m_renderData.currentWindow->m_dimPercent->value() > 0)) shaderFeatures |= SH_FEAT_TINT; @@ -1500,103 +1376,52 @@ void CHyprOpenGLImpl::renderTextureInternal(SP<CTexture> tex, const CBox& box, c if (!skipCM) { shaderFeatures |= SH_FEAT_CM; - const bool needsSDRmod = isSDR2HDR(SOURCE_IMAGE_DESCRIPTION->value(), TARGET_IMAGE_DESCRIPTION->value()); - const bool needsHDRmod = !needsSDRmod && isHDR2SDR(SOURCE_IMAGE_DESCRIPTION->value(), TARGET_IMAGE_DESCRIPTION->value()); - const float maxLuminance = needsHDRmod ? - SOURCE_IMAGE_DESCRIPTION->value().getTFMaxLuminance(-1) : - (SOURCE_IMAGE_DESCRIPTION->value().luminances.max > 0 ? SOURCE_IMAGE_DESCRIPTION->value().luminances.max : SOURCE_IMAGE_DESCRIPTION->value().luminances.reference); - const auto dstMaxLuminance = TARGET_IMAGE_DESCRIPTION->value().luminances.max > 0 ? TARGET_IMAGE_DESCRIPTION->value().luminances.max : 10000; - - if (maxLuminance >= dstMaxLuminance * 1.01) - shaderFeatures |= SH_FEAT_TONEMAP; - - if (data.finalMonitorCM && - (SOURCE_IMAGE_DESCRIPTION->value().transferFunction == CM_TRANSFER_FUNCTION_SRGB || - SOURCE_IMAGE_DESCRIPTION->value().transferFunction == CM_TRANSFER_FUNCTION_GAMMA22) && - TARGET_IMAGE_DESCRIPTION->value().transferFunction == CM_TRANSFER_FUNCTION_ST2084_PQ && - ((m_renderData.pMonitor->m_sdrSaturation > 0 && m_renderData.pMonitor->m_sdrSaturation != 1.0f) || - (m_renderData.pMonitor->m_sdrBrightness > 0 && m_renderData.pMonitor->m_sdrBrightness != 1.0f))) - shaderFeatures |= SH_FEAT_SDR_MOD; + if (TARGET_IMAGE_DESCRIPTION->value().icc.present) + shaderFeatures |= SH_FEAT_ICC; + else { + const bool needsSDRmod = isSDR2HDR(SOURCE_IMAGE_DESCRIPTION->value(), TARGET_IMAGE_DESCRIPTION->value()); + const bool needsHDRmod = !needsSDRmod && isHDR2SDR(SOURCE_IMAGE_DESCRIPTION->value(), TARGET_IMAGE_DESCRIPTION->value()); + const float maxLuminance = needsHDRmod ? + SOURCE_IMAGE_DESCRIPTION->value().getTFMaxLuminance(-1) : + (SOURCE_IMAGE_DESCRIPTION->value().luminances.max > 0 ? SOURCE_IMAGE_DESCRIPTION->value().luminances.max : SOURCE_IMAGE_DESCRIPTION->value().luminances.reference); + const auto dstMaxLuminance = TARGET_IMAGE_DESCRIPTION->value().luminances.max > 0 ? TARGET_IMAGE_DESCRIPTION->value().luminances.max : 10000; + + if (maxLuminance >= dstMaxLuminance * 1.01) + shaderFeatures |= SH_FEAT_TONEMAP; + + if (data.finalMonitorCM && + (SOURCE_IMAGE_DESCRIPTION->value().transferFunction == CM_TRANSFER_FUNCTION_SRGB || + SOURCE_IMAGE_DESCRIPTION->value().transferFunction == CM_TRANSFER_FUNCTION_GAMMA22) && + TARGET_IMAGE_DESCRIPTION->value().transferFunction == CM_TRANSFER_FUNCTION_ST2084_PQ && + ((m_renderData.pMonitor->m_sdrSaturation > 0 && m_renderData.pMonitor->m_sdrSaturation != 1.0f) || + (m_renderData.pMonitor->m_sdrBrightness > 0 && m_renderData.pMonitor->m_sdrBrightness != 1.0f))) + shaderFeatures |= SH_FEAT_SDR_MOD; + } } if (!shader) - shader = getSurfaceShader(shaderFeatures); - + shader = getShaderVariant(SH_FRAG_SURFACE, shaderFeatures); shader = useShader(shader); if (!skipCM) { - if (data.finalMonitorCM) + if (data.finalMonitorCM || data.cmBackToSRGB) passCMUniforms(shader, SOURCE_IMAGE_DESCRIPTION, TARGET_IMAGE_DESCRIPTION, true, m_renderData.pMonitor->m_sdrMinLuminance, m_renderData.pMonitor->m_sdrMaxLuminance); else - passCMUniforms(shader, SOURCE_IMAGE_DESCRIPTION, TARGET_IMAGE_DESCRIPTION, true, -1, -1); - } - - shader->setUniformMatrix3fv(SHADER_PROJ, 1, GL_TRUE, glMatrix.getMatrix()); - shader->setUniformInt(SHADER_TEX, 0); - - if ((CUSTOM_FINAL_SHADER && *PDT == 0) || CRASHING) - shader->setUniformFloat(SHADER_TIME, m_globalTimer.getSeconds() - shader->getInitialTime()); - else if (CUSTOM_FINAL_SHADER) - shader->setUniformFloat(SHADER_TIME, 0.F); - - if (CUSTOM_FINAL_SHADER) { - shader->setUniformInt(SHADER_WL_OUTPUT, m_renderData.pMonitor->m_id); - shader->setUniformFloat2(SHADER_FULL_SIZE, m_renderData.pMonitor->m_pixelSize.x, m_renderData.pMonitor->m_pixelSize.y); - shader->setUniformFloat(SHADER_POINTER_INACTIVE_TIMEOUT, *PCURSORTIMEOUT); - shader->setUniformInt(SHADER_POINTER_HIDDEN, g_pHyprRenderer->m_cursorHiddenByCondition); - shader->setUniformInt(SHADER_POINTER_KILLING, g_pInputManager->getClickMode() == CLICKMODE_KILL); - shader->setUniformInt(SHADER_POINTER_SHAPE, g_pHyprRenderer->m_lastCursorData.shape); - shader->setUniformInt(SHADER_POINTER_SHAPE_PREVIOUS, g_pHyprRenderer->m_lastCursorData.shapePrevious); - shader->setUniformFloat(SHADER_POINTER_SIZE, g_pCursorManager->getScaledSize()); + passCMUniforms(shader, SOURCE_IMAGE_DESCRIPTION); } - if (CUSTOM_FINAL_SHADER && *PDT == 0) { - PHLMONITORREF pMonitor = m_renderData.pMonitor; - Vector2D p = ((g_pInputManager->getMouseCoordsInternal() - pMonitor->m_position) * pMonitor->m_scale); - p = p.transform(Math::wlTransformToHyprutils(pMonitor->m_transform), pMonitor->m_pixelSize); - shader->setUniformFloat2(SHADER_POINTER, p.x / pMonitor->m_pixelSize.x, p.y / pMonitor->m_pixelSize.y); - - std::vector<float> pressedPos = m_pressedHistoryPositions | std::views::transform([&](const Vector2D& vec) { - Vector2D pPressed = ((vec - pMonitor->m_position) * pMonitor->m_scale); - pPressed = pPressed.transform(Math::wlTransformToHyprutils(pMonitor->m_transform), pMonitor->m_pixelSize); - return std::array<float, 2>{pPressed.x / pMonitor->m_pixelSize.x, pPressed.y / pMonitor->m_pixelSize.y}; - }) | - std::views::join | std::ranges::to<std::vector<float>>(); - - shader->setUniform2fv(SHADER_POINTER_PRESSED_POSITIONS, pressedPos.size(), pressedPos); - - std::vector<float> pressedTime = - m_pressedHistoryTimers | std::views::transform([](const CTimer& timer) { return timer.getSeconds(); }) | std::ranges::to<std::vector<float>>(); - - shader->setUniform1fv(SHADER_POINTER_PRESSED_TIMES, pressedTime.size(), pressedTime); - - shader->setUniformInt(SHADER_POINTER_PRESSED_KILLED, m_pressedHistoryKilled); - shader->setUniformInt(SHADER_POINTER_PRESSED_TOUCHED, m_pressedHistoryTouched); - - shader->setUniformFloat(SHADER_POINTER_LAST_ACTIVE, g_pInputManager->m_lastCursorMovement.getSeconds()); - shader->setUniformFloat(SHADER_POINTER_SWITCH_TIME, g_pHyprRenderer->m_lastCursorData.switchedTimer.getSeconds()); - - } else if (CUSTOM_FINAL_SHADER) { - shader->setUniformFloat2(SHADER_POINTER, 0.f, 0.f); - - static const std::vector<float> pressedPosDefault(POINTER_PRESSED_HISTORY_LENGTH * 2uz, 0.f); - static const std::vector<float> pressedTimeDefault(POINTER_PRESSED_HISTORY_LENGTH, 0.f); + shader->setUniformFloat(SHADER_ALPHA, alpha); - shader->setUniform2fv(SHADER_POINTER_PRESSED_POSITIONS, pressedPosDefault.size(), pressedPosDefault); - shader->setUniform1fv(SHADER_POINTER_PRESSED_TIMES, pressedTimeDefault.size(), pressedTimeDefault); - shader->setUniformInt(SHADER_POINTER_PRESSED_KILLED, 0); + if (shaderFeatures & SH_FEAT_BLUR) { + shader->setUniformInt(SHADER_BLURRED_BG, 1); + // shader->setUniformFloat2(SHADER_UV_OFFSET, 0, 0); + shader->setUniformFloat2(SHADER_UV_OFFSET, newBox.x / m_renderData.pMonitor->m_transformedSize.x, newBox.y / m_renderData.pMonitor->m_transformedSize.y); + shader->setUniformFloat2(SHADER_UV_SIZE, newBox.width / m_renderData.pMonitor->m_transformedSize.x, newBox.height / m_renderData.pMonitor->m_transformedSize.y); - shader->setUniformFloat(SHADER_POINTER_LAST_ACTIVE, 0.f); - shader->setUniformFloat(SHADER_POINTER_SWITCH_TIME, 0.f); + glActiveTexture(GL_TEXTURE0 + 1); + data.blurredBG->bind(); } - if (CRASHING) { - shader->setUniformFloat(SHADER_DISTORT, g_pHyprRenderer->m_crashingDistort); - shader->setUniformFloat2(SHADER_FULL_SIZE, m_renderData.pMonitor->m_pixelSize.x, m_renderData.pMonitor->m_pixelSize.y); - } - - shader->setUniformFloat(SHADER_ALPHA, alpha); - if (data.discardActive) { shader->setUniformInt(SHADER_DISCARD_OPAQUE, !!(m_renderData.discardMode & DISCARD_OPAQUE)); shader->setUniformInt(SHADER_DISCARD_ALPHA, !!(m_renderData.discardMode & DISCARD_ALPHA)); @@ -1612,7 +1437,6 @@ void CHyprOpenGLImpl::renderTextureInternal(SP<CTexture> tex, const CBox& box, c const auto TOPLEFT = Vector2D(transformedBox.x, transformedBox.y); const auto FULLSIZE = Vector2D(transformedBox.width, transformedBox.height); - // Rounded corners shader->setUniformFloat2(SHADER_TOP_LEFT, TOPLEFT.x, TOPLEFT.y); shader->setUniformFloat2(SHADER_FULL_SIZE, FULLSIZE.x, FULLSIZE.y); @@ -1633,8 +1457,53 @@ void CHyprOpenGLImpl::renderTextureInternal(SP<CTexture> tex, const CBox& box, c } else shader->setUniformInt(SHADER_APPLY_TINT, 0); - glBindVertexArray(shader->getUniformLocation(SHADER_SHADER_VAO)); - glBindBuffer(GL_ARRAY_BUFFER, shader->getUniformLocation(SHADER_SHADER_VBO)); + return shader; +} + +void CHyprOpenGLImpl::renderTextureInternal(SP<CTexture> tex, const CBox& box, const STextureRenderData& data) { + RASSERT(m_renderData.pMonitor, "Tried to render texture without begin()!"); + RASSERT((tex->m_texID > 0), "Attempted to draw nullptr texture!"); + + TRACY_GPU_ZONE("RenderTextureInternalWithDamage"); + + if (data.damage->empty()) + return; + + CBox newBox = box; + m_renderData.renderModif.applyToBox(newBox); + + // get the needed transform for this texture + const auto MONITOR_INVERTED = Math::wlTransformToHyprutils(Math::invertTransform(m_renderData.pMonitor->m_transform)); + Hyprutils::Math::eTransform TRANSFORM = tex->m_transform; + + if (m_monitorTransformEnabled) + TRANSFORM = Math::composeTransform(MONITOR_INVERTED, TRANSFORM); + + Mat3x3 matrix = m_renderData.monitorProjection.projectBox(newBox, TRANSFORM, newBox.rot); + Mat3x3 glMatrix = m_renderData.projection.copy().multiply(matrix); + + const bool renderToOutput = m_applyFinalShader && g_pHyprRenderer->workBufferImageDescription()->id() == m_renderData.pMonitor->m_imageDescription->id(); + + glActiveTexture(GL_TEXTURE0); + tex->bind(); + + tex->setTexParameter(GL_TEXTURE_WRAP_S, data.wrapX); + tex->setTexParameter(GL_TEXTURE_WRAP_T, data.wrapY); + + if (m_renderData.useNearestNeighbor) { + tex->setTexParameter(GL_TEXTURE_MAG_FILTER, GL_NEAREST); + tex->setTexParameter(GL_TEXTURE_MIN_FILTER, GL_NEAREST); + } else { + tex->setTexParameter(GL_TEXTURE_MAG_FILTER, tex->magFilter); + tex->setTexParameter(GL_TEXTURE_MIN_FILTER, tex->minFilter); + } + + auto shader = renderToOutput ? renderToOutputInternal() : renderToFBInternal(data, tex->m_type, newBox); + + shader->setUniformMatrix3fv(SHADER_PROJ, 1, GL_TRUE, glMatrix.getMatrix()); + shader->setUniformInt(SHADER_TEX, 0); + GLCALL(glBindVertexArray(shader->getUniformLocation(SHADER_SHADER_VAO))); + GLCALL(glBindBuffer(GL_ARRAY_BUFFER, shader->getUniformLocation(SHADER_SHADER_VBO))); // this tells GPU can keep reading the old block for previous draws while the CPU writes to a new one. // to avoid stalls if renderTextureInternal is called multiple times on same renderpass @@ -1719,7 +1588,7 @@ void CHyprOpenGLImpl::renderTexturePrimitive(SP<CTexture> tex, const CBox& box) tex->setTexParameter(GL_TEXTURE_MIN_FILTER, tex->minFilter); } - auto shader = useShader(m_shaders->frag[SH_FRAG_PASSTHRURGBA]); + auto shader = useShader(getShaderVariant(SH_FRAG_PASSTHRURGBA)); shader->setUniformMatrix3fv(SHADER_PROJ, 1, GL_TRUE, glMatrix.getMatrix()); shader->setUniformInt(SHADER_TEX, 0); glBindVertexArray(shader->getUniformLocation(SHADER_SHADER_VAO)); @@ -1751,7 +1620,7 @@ void CHyprOpenGLImpl::renderTextureMatte(SP<CTexture> tex, const CBox& box, CFra Mat3x3 matrix = m_renderData.monitorProjection.projectBox(newBox, TRANSFORM, newBox.rot); Mat3x3 glMatrix = m_renderData.projection.copy().multiply(matrix); - auto shader = useShader(m_shaders->frag[SH_FRAG_MATTE]); + auto shader = useShader(getShaderVariant(SH_FRAG_MATTE)); shader->setUniformMatrix3fv(SHADER_PROJ, 1, GL_TRUE, glMatrix.getMatrix()); shader->setUniformInt(SHADER_TEX, 0); shader->setUniformInt(SHADER_ALPHA_MATTE, 1); @@ -1826,6 +1695,7 @@ CFramebuffer* CHyprOpenGLImpl::blurFramebufferWithDamage(float a, CRegion* origi { static auto PBLURCONTRAST = CConfigValue<Hyprlang::FLOAT>("decoration:blur:contrast"); static auto PBLURBRIGHTNESS = CConfigValue<Hyprlang::FLOAT>("decoration:blur:brightness"); + static auto PBLEND = CConfigValue<Hyprlang::INT>("render:use_shader_blur_blend"); PMIRRORSWAPFB->bind(); @@ -1838,7 +1708,26 @@ CFramebuffer* CHyprOpenGLImpl::blurFramebufferWithDamage(float a, CRegion* origi WP<CShader> shader; - shader = useShader(m_shaders->frag[SH_FRAG_BLURPREPARE]); + // From FB to sRGB + const bool skipCM = !m_cmSupported || g_pHyprRenderer->workBufferImageDescription()->id() == DEFAULT_IMAGE_DESCRIPTION->id(); + if (!skipCM) { + shader = useShader(getShaderVariant(SH_FRAG_BLURPREPARE, SH_FEAT_CM)); + passCMUniforms(shader, m_renderData.pMonitor->m_imageDescription, DEFAULT_IMAGE_DESCRIPTION); + shader->setUniformFloat(SHADER_SDR_SATURATION, + m_renderData.pMonitor->m_sdrSaturation > 0 && + m_renderData.pMonitor->m_imageDescription->value().transferFunction == NColorManagement::CM_TRANSFER_FUNCTION_ST2084_PQ ? + m_renderData.pMonitor->m_sdrSaturation : + 1.0f); + shader->setUniformFloat(SHADER_SDR_BRIGHTNESS, + m_renderData.pMonitor->m_sdrBrightness > 0 && + m_renderData.pMonitor->m_imageDescription->value().transferFunction == NColorManagement::CM_TRANSFER_FUNCTION_ST2084_PQ ? + m_renderData.pMonitor->m_sdrBrightness : + 1.0f); + } else + shader = useShader(getShaderVariant(SH_FRAG_BLURPREPARE)); + + Mat3x3 matrix = m_renderData.monitorProjection.projectBox(MONITORBOX, *PBLEND ? HYPRUTILS_TRANSFORM_NORMAL : TRANSFORM); + Mat3x3 glMatrix = m_renderData.projection.copy().multiply(matrix); shader->setUniformMatrix3fv(SHADER_PROJ, 1, GL_TRUE, glMatrix.getMatrix()); shader->setUniformFloat(SHADER_CONTRAST, *PBLURCONTRAST); shader->setUniformFloat(SHADER_BRIGHTNESS, *PBLURBRIGHTNESS); @@ -1910,13 +1799,13 @@ CFramebuffer* CHyprOpenGLImpl::blurFramebufferWithDamage(float a, CRegion* origi CRegion tempDamage{damage}; // and draw - auto shader = useShader(m_shaders->frag[SH_FRAG_BLUR1]); + auto shader = useShader(getShaderVariant(SH_FRAG_BLUR1)); for (auto i = 1; i <= BLUR_PASSES; ++i) { tempDamage = damage.copy().scale(1.f / (1 << i)); drawPass(shader, SH_FRAG_BLUR1, &tempDamage); // down } - shader = useShader(m_shaders->frag[SH_FRAG_BLUR2]); + shader = useShader(getShaderVariant(SH_FRAG_BLUR2)); for (auto i = BLUR_PASSES - 1; i >= 0; --i) { tempDamage = damage.copy().scale(1.f / (1 << i)); // when upsampling we make the region twice as big drawPass(shader, SH_FRAG_BLUR2, &tempDamage); // up @@ -1940,7 +1829,7 @@ CFramebuffer* CHyprOpenGLImpl::blurFramebufferWithDamage(float a, CRegion* origi currentTex->setTexParameter(GL_TEXTURE_MIN_FILTER, GL_LINEAR); - auto shader = useShader(m_shaders->frag[SH_FRAG_BLURFINISH]); + auto shader = useShader(getShaderVariant(SH_FRAG_BLURFINISH)); shader->setUniformMatrix3fv(SHADER_PROJ, 1, GL_TRUE, glMatrix.getMatrix()); shader->setUniformFloat(SHADER_NOISE, *PBLURNOISE); shader->setUniformFloat(SHADER_BRIGHTNESS, *PBLURBRIGHTNESS); @@ -2132,6 +2021,8 @@ void CHyprOpenGLImpl::renderTextureWithBlurInternal(SP<CTexture> tex, const CBox TRACY_GPU_ZONE("RenderTextureWithBlur"); + static auto PBLEND = CConfigValue<Hyprlang::INT>("render:use_shader_blur_blend"); + // make a damage region for this window CRegion texDamage{m_renderData.damage}; texDamage.intersect(box.x, box.y, box.width, box.height); @@ -2178,22 +2069,22 @@ void CHyprOpenGLImpl::renderTextureWithBlurInternal(SP<CTexture> tex, const CBox m_renderData.currentFB->bind(); - const auto NEEDS_STENCIL = m_renderData.discardMode != 0; + auto blurredBG = POUTFB->getTexture(); - if (NEEDS_STENCIL) { - scissor(nullptr); // allow the entire window and stencil to render - glClearStencil(0); - glClear(GL_STENCIL_BUFFER_BIT); + const auto NEEDS_STENCIL = m_renderData.discardMode != 0 && (!data.blockBlurOptimization || (m_renderData.discardMode & DISCARD_ALPHA)); + if (!*PBLEND) { - setCapStatus(GL_STENCIL_TEST, true); + if (NEEDS_STENCIL) { + scissor(nullptr); // allow the entire window and stencil to render + glClearStencil(0); + glClear(GL_STENCIL_BUFFER_BIT); - glStencilFunc(GL_ALWAYS, 1, 0xFF); - glStencilOp(GL_KEEP, GL_KEEP, GL_REPLACE); + setCapStatus(GL_STENCIL_TEST, true); - glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE); - if (USENEWOPTIMIZE && !(m_renderData.discardMode & DISCARD_ALPHA)) - renderRect(box, CHyprColor(0, 0, 0, 0), SRectRenderData{.round = data.round, .roundingPower = data.roundingPower}); - else + glStencilFunc(GL_ALWAYS, 1, 0xFF); + glStencilOp(GL_KEEP, GL_KEEP, GL_REPLACE); + + glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE); renderTexture(tex, box, STextureRenderData{.a = data.a, .round = data.round, @@ -2201,66 +2092,73 @@ void CHyprOpenGLImpl::renderTextureWithBlurInternal(SP<CTexture> tex, const CBox .discardActive = true, .allowCustomUV = true, .wrapX = data.wrapX, - .wrapY = data.wrapY}); // discard opaque - glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE); + .wrapY = data.wrapY}); // discard opaque and alpha < discardOpacity - glStencilFunc(GL_EQUAL, 1, 0xFF); - glStencilOp(GL_KEEP, GL_KEEP, GL_REPLACE); - } + glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE); - // stencil done. Render everything. - const auto LASTTL = m_renderData.primarySurfaceUVTopLeft; - const auto LASTBR = m_renderData.primarySurfaceUVBottomRight; + glStencilFunc(GL_EQUAL, 1, 0xFF); + glStencilOp(GL_KEEP, GL_KEEP, GL_REPLACE); + } - CBox transformedBox = box; - transformedBox.transform(Math::wlTransformToHyprutils(Math::invertTransform(m_renderData.pMonitor->m_transform)), m_renderData.pMonitor->m_transformedSize.x, - m_renderData.pMonitor->m_transformedSize.y); + // stencil done. Render everything. + const auto LASTTL = m_renderData.primarySurfaceUVTopLeft; + const auto LASTBR = m_renderData.primarySurfaceUVBottomRight; - CBox monitorSpaceBox = {transformedBox.pos().x / m_renderData.pMonitor->m_pixelSize.x * m_renderData.pMonitor->m_transformedSize.x, - transformedBox.pos().y / m_renderData.pMonitor->m_pixelSize.y * m_renderData.pMonitor->m_transformedSize.y, - transformedBox.width / m_renderData.pMonitor->m_pixelSize.x * m_renderData.pMonitor->m_transformedSize.x, - transformedBox.height / m_renderData.pMonitor->m_pixelSize.y * m_renderData.pMonitor->m_transformedSize.y}; + CBox transformedBox = box; + transformedBox.transform(Math::wlTransformToHyprutils(Math::invertTransform(m_renderData.pMonitor->m_transform)), m_renderData.pMonitor->m_transformedSize.x, + m_renderData.pMonitor->m_transformedSize.y); - m_renderData.primarySurfaceUVTopLeft = monitorSpaceBox.pos() / m_renderData.pMonitor->m_transformedSize; - m_renderData.primarySurfaceUVBottomRight = (monitorSpaceBox.pos() + monitorSpaceBox.size()) / m_renderData.pMonitor->m_transformedSize; + CBox monitorSpaceBox = {transformedBox.pos().x / m_renderData.pMonitor->m_pixelSize.x * m_renderData.pMonitor->m_transformedSize.x, + transformedBox.pos().y / m_renderData.pMonitor->m_pixelSize.y * m_renderData.pMonitor->m_transformedSize.y, + transformedBox.width / m_renderData.pMonitor->m_pixelSize.x * m_renderData.pMonitor->m_transformedSize.x, + transformedBox.height / m_renderData.pMonitor->m_pixelSize.y * m_renderData.pMonitor->m_transformedSize.y}; - static auto PBLURIGNOREOPACITY = CConfigValue<Hyprlang::INT>("decoration:blur:ignore_opacity"); - pushMonitorTransformEnabled(true); - if (!USENEWOPTIMIZE) - setRenderModifEnabled(false); - renderTextureInternal(POUTFB->getTexture(), box, - STextureRenderData{ - .damage = &texDamage, - .a = (*PBLURIGNOREOPACITY ? data.blurA : data.a * data.blurA) * data.overallA, - .round = data.round, - .roundingPower = data.roundingPower, - .discardActive = false, - .allowCustomUV = true, - .noAA = false, - .wrapX = data.wrapX, - .wrapY = data.wrapY, - }); - if (!USENEWOPTIMIZE) - setRenderModifEnabled(true); - popMonitorTransformEnabled(); + m_renderData.primarySurfaceUVTopLeft = monitorSpaceBox.pos() / m_renderData.pMonitor->m_transformedSize; + m_renderData.primarySurfaceUVBottomRight = (monitorSpaceBox.pos() + monitorSpaceBox.size()) / m_renderData.pMonitor->m_transformedSize; - m_renderData.primarySurfaceUVTopLeft = LASTTL; - m_renderData.primarySurfaceUVBottomRight = LASTBR; + static auto PBLURIGNOREOPACITY = CConfigValue<Hyprlang::INT>("decoration:blur:ignore_opacity"); + pushMonitorTransformEnabled(true); + bool renderModif = m_renderData.renderModif.enabled; + if (!USENEWOPTIMIZE) + setRenderModifEnabled(false); + renderTextureInternal(blurredBG, box, + STextureRenderData{ + .damage = &texDamage, + .a = (*PBLURIGNOREOPACITY ? data.blurA : data.a * data.blurA) * data.overallA, + .round = data.round, + .roundingPower = data.roundingPower, + .discardActive = false, + .allowCustomUV = true, + .noAA = false, + .wrapX = data.wrapX, + .wrapY = data.wrapY, + }); + if (!USENEWOPTIMIZE) + setRenderModifEnabled(renderModif); + popMonitorTransformEnabled(); + + if (NEEDS_STENCIL) + setCapStatus(GL_STENCIL_TEST, false); + + m_renderData.primarySurfaceUVTopLeft = LASTTL; + m_renderData.primarySurfaceUVBottomRight = LASTBR; + } // draw window - setCapStatus(GL_STENCIL_TEST, false); renderTextureInternal(tex, box, STextureRenderData{ .damage = &texDamage, .a = data.a * data.overallA, + .blur = *PBLEND, .round = data.round, .roundingPower = data.roundingPower, - .discardActive = false, + .discardActive = *PBLEND && NEEDS_STENCIL, .allowCustomUV = true, .allowDim = true, .noAA = false, .wrapX = data.wrapX, .wrapY = data.wrapY, + .blurredBG = blurredBG, }); m_renderData.currentFB->invalidate({GL_STENCIL_ATTACHMENT}); @@ -2300,7 +2198,15 @@ void CHyprOpenGLImpl::renderBorder(const CBox& box, const CGradientValueData& gr const auto BLEND = m_blend; blend(true); - WP<CShader> shader = useShader(m_shaders->frag[SH_FRAG_BORDER1]); + WP<CShader> shader; + + const bool IS_ICC = g_pHyprRenderer->workBufferImageDescription()->value().icc.present; + const bool skipCM = !m_cmSupported || g_pHyprRenderer->workBufferImageDescription()->id() == DEFAULT_IMAGE_DESCRIPTION->id(); + if (!skipCM) { + shader = useShader(getShaderVariant(SH_FRAG_BORDER1, SH_FEAT_ROUNDING | SH_FEAT_CM | (IS_ICC ? SH_FEAT_ICC : SH_FEAT_TONEMAP | SH_FEAT_SDR_MOD))); + passCMUniforms(shader, DEFAULT_IMAGE_DESCRIPTION); + } else + shader = useShader(getShaderVariant(SH_FRAG_BORDER1, SH_FEAT_ROUNDING)); shader->setUniformMatrix3fv(SHADER_PROJ, 1, GL_TRUE, glMatrix.getMatrix()); shader->setUniform4fv(SHADER_GRADIENT, grad.m_colorsOkLabA.size() / 4, grad.m_colorsOkLabA); @@ -2379,7 +2285,14 @@ void CHyprOpenGLImpl::renderBorder(const CBox& box, const CGradientValueData& gr const auto BLEND = m_blend; blend(true); - WP<CShader> shader = useShader(m_shaders->frag[SH_FRAG_BORDER1]); + WP<CShader> shader; + const bool IS_ICC = g_pHyprRenderer->workBufferImageDescription()->value().icc.present; + const bool skipCM = !m_cmSupported || g_pHyprRenderer->workBufferImageDescription()->id() == DEFAULT_IMAGE_DESCRIPTION->id(); + if (!skipCM) { + shader = useShader(getShaderVariant(SH_FRAG_BORDER1, SH_FEAT_ROUNDING | SH_FEAT_CM | (IS_ICC ? SH_FEAT_ICC : SH_FEAT_TONEMAP | SH_FEAT_SDR_MOD))); + passCMUniforms(shader, DEFAULT_IMAGE_DESCRIPTION); + } else + shader = useShader(getShaderVariant(SH_FRAG_BORDER1, SH_FEAT_ROUNDING)); shader->setUniformMatrix3fv(SHADER_PROJ, 1, GL_TRUE, glMatrix.getMatrix()); shader->setUniform4fv(SHADER_GRADIENT, grad1.m_colorsOkLabA.size() / 4, grad1.m_colorsOkLabA); @@ -2452,7 +2365,11 @@ void CHyprOpenGLImpl::renderRoundedShadow(const CBox& box, int round, float roun blend(true); - auto shader = useShader(m_shaders->frag[SH_FRAG_SHADOW]); + const bool IS_ICC = g_pHyprRenderer->workBufferImageDescription()->value().icc.present; + const bool skipCM = !m_cmSupported || g_pHyprRenderer->workBufferImageDescription()->id() == DEFAULT_IMAGE_DESCRIPTION->id(); + auto shader = useShader(getShaderVariant(SH_FRAG_SHADOW, skipCM ? 0 : SH_FEAT_CM | (IS_ICC ? SH_FEAT_ICC : SH_FEAT_TONEMAP | SH_FEAT_SDR_MOD))); + if (!skipCM) + passCMUniforms(shader, DEFAULT_IMAGE_DESCRIPTION); shader->setUniformMatrix3fv(SHADER_PROJ, 1, GL_TRUE, glMatrix.getMatrix()); shader->setUniformFloat4(SHADER_COLOR, col.r, col.g, col.b, col.a * a); @@ -3141,53 +3058,23 @@ bool CHyprOpenGLImpl::explicitSyncSupported() { return m_exts.EGL_ANDROID_native_fence_sync_ext; } -WP<CShader> CHyprOpenGLImpl::getSurfaceShader(uint8_t features) { - if (!m_shaders->fragVariants.contains(features)) { +WP<CShader> CHyprOpenGLImpl::getShaderVariant(ePreparedFragmentShader frag, ShaderFeatureFlags features) { + if (!m_shaders->fragVariants[frag].contains(features)) { + auto shader = makeShared<CShader>(); - auto shader = makeShared<CShader>(); - auto includes = m_includes; - includes["get_rgb_pixel.glsl"] = includes[features & SH_FEAT_RGBA ? "get_rgba_pixel.glsl" : "get_rgbx_pixel.glsl"]; - if (!(features & SH_FEAT_DISCARD)) { - includes["discard.glsl"] = ""; - includes["do_discard.glsl"] = ""; - } - if (!(features & SH_FEAT_TINT)) { - includes["tint.glsl"] = ""; - includes["do_tint.glsl"] = ""; - } - if (!(features & SH_FEAT_ROUNDING)) { - includes["rounding.glsl"] = ""; - includes["do_rounding.glsl"] = ""; - } - if (!(features & SH_FEAT_CM)) { - includes["surface_CM.glsl"] = ""; - includes["CM.glsl"] = ""; - includes["do_CM.glsl"] = ""; - } - if (!(features & SH_FEAT_TONEMAP)) { - includes["tonemap.glsl"] = ""; - includes["do_tonemap.glsl"] = ""; - } - if (!(features & SH_FEAT_SDR_MOD)) { - includes["sdr_mod.glsl"] = ""; - includes["do_sdr_mod.glsl"] = ""; - } - if (!(features & SH_FEAT_TONEMAP || features & SH_FEAT_SDR_MOD)) - includes["primaries_xyz.glsl"] = includes["primaries_xyz_const.glsl"]; - - Log::logger->log(Log::INFO, "getSurfaceShader: compiling feature set {}", features); - const auto fragSrc = processShader("surface.frag", includes, MAX_INCLUDE_DEPTH); - if (shader->createProgram(m_shaders->TEXVERTSRC, fragSrc, true, true)) { - m_shaders->fragVariants[features] = shader; - return shader; - } else { - Log::logger->log(Log::ERR, "getSurfaceShader failed for {}. Falling back to old branching", features); - m_shaders->fragVariants[features] = nullptr; - } + Log::logger->log(Log::INFO, "compiling feature set {} for {}", features, FRAG_SHADERS[frag]); + + const auto fragSrc = g_pShaderLoader->getVariantSource(frag, features); + + if (!shader->createProgram(m_shaders->TEXVERTSRC, fragSrc, true, true)) + Log::logger->log(Log::ERR, "shader features {} failed for {}", features, FRAG_SHADERS[frag]); + + m_shaders->fragVariants[frag][features] = shader; + return shader; } - ASSERT(m_shaders->fragVariants[features]); - return m_shaders->fragVariants[features]; + ASSERT(m_shaders->fragVariants[frag][features]); + return m_shaders->fragVariants[frag][features]; } std::vector<SDRMFormat> CHyprOpenGLImpl::getDRMFormats() { |
