From 33044c3816ce99db849aa50a9c97353056535968 Mon Sep 17 00:00:00 2001 From: davc0n <57829860+davc0n@users.noreply.github.com> Date: Mon, 4 May 2026 17:08:27 +0200 Subject: [PATCH] render: scale background to monitor resolution (#14250) * render: scale background to monitor resolution Currently the background is not scaled, e.g. 8k size image always uses ~128Mb of VRAM. We could reduce the consumption for low-end devices and monitors with lower resolution: - ~8Mb for 1080p - ~16Mb for 2k - ~32Mb for 4k * render: use gpu to scale bgtex and fix missing reset on size change --- src/helpers/Monitor.cpp | 17 +++++++------ src/render/Renderer.cpp | 56 +++++++++++++++++++++++++++++++++++++---- 2 files changed, 61 insertions(+), 12 deletions(-) diff --git a/src/helpers/Monitor.cpp b/src/helpers/Monitor.cpp index a5195a94f..b2e94a312 100644 --- a/src/helpers/Monitor.cpp +++ b/src/helpers/Monitor.cpp @@ -7,12 +7,10 @@ #include "../protocols/ColorManagement.hpp" #include "../Compositor.hpp" #include "../config/ConfigValue.hpp" -#include "../config/ConfigManager.hpp" #include "../config/shared/monitor/MonitorRuleManager.hpp" #include "../config/shared/workspace/WorkspaceRuleManager.hpp" #include "../config/shared/animation/AnimationTree.hpp" #include "../protocols/GammaControl.hpp" -#include "../devices/ITouch.hpp" #include "../protocols/LayerShell.hpp" #include "../protocols/PresentationTime.hpp" #include "../protocols/DRMLease.hpp" @@ -34,7 +32,6 @@ #include "../layout/LayoutManager.hpp" #include "../i18n/Engine.hpp" #include "../helpers/cm/ColorManagement.hpp" -#include "sync/SyncTimeline.hpp" #include "time/Time.hpp" #include "../desktop/view/LayerSurface.hpp" #include "../desktop/state/FocusState.hpp" @@ -799,9 +796,10 @@ bool CMonitor::applyMonitorRule(Config::CMonitorRule&& pMonitorRule, bool force) } } - const auto WAS10B = m_enabled10bit; - const auto OLDRES = m_pixelSize; - bool success = false; + const auto WAS10B = m_enabled10bit; + const auto OLDPIXELSIZE = m_pixelSize; + const auto OLDTRANSFORMEDSIZE = m_transformedSize; + bool success = false; // Needed in case we are switching from a custom modeline to a standard mode m_customDrmMode = {}; @@ -1067,13 +1065,18 @@ bool CMonitor::applyMonitorRule(Config::CMonitorRule&& pMonitorRule, bool force) updateMatrix(); - if ((WAS10B != m_enabled10bit || OLDRES != m_pixelSize)) { + if ((WAS10B != m_enabled10bit || OLDPIXELSIZE != m_pixelSize)) { m_resources.reset(); // TODO skip for 10bit change and fp16? if (g_pHyprRenderer && g_pHyprRenderer->glBackend()) g_pHyprRenderer->glBackend()->destroyMonitorResources(m_self); } + if (m_background && (OLDPIXELSIZE != m_pixelSize || OLDTRANSFORMEDSIZE != m_transformedSize)) { + Log::logger->log(Log::DEBUG, "{} reset BGTex: pixelSize {} -> {}, transformedSize {} -> {}", m_name, OLDPIXELSIZE, m_pixelSize, OLDTRANSFORMEDSIZE, m_transformedSize); + m_background.reset(); + } + g_pCompositor->scheduleMonitorStateRecheck(); m_damage.setSize(m_transformedSize); diff --git a/src/render/Renderer.cpp b/src/render/Renderer.cpp index 6800c1857..635bef556 100644 --- a/src/render/Renderer.cpp +++ b/src/render/Renderer.cpp @@ -23,7 +23,6 @@ #include "../protocols/core/Compositor.hpp" #include "../protocols/DRMSyncobj.hpp" #include "../protocols/LinuxDMABUF.hpp" -#include "../helpers/sync/SyncTimeline.hpp" #include "../errorOverlay/Overlay.hpp" #include "../debug/Overlay.hpp" #include "../notification/NotificationOverlay.hpp" @@ -36,7 +35,6 @@ #include "../helpers/MainLoopExecutor.hpp" #include "../helpers/Monitor.hpp" #include "macros.hpp" -#include "../managers/screenshare/ScreenshareManager.hpp" #include "pass/TexPassElement.hpp" #include "pass/ClearPassElement.hpp" #include "pass/RectPassElement.hpp" @@ -45,7 +43,6 @@ #include "../debug/log/Logger.hpp" #include "../protocols/ColorManagement.hpp" #include "../protocols/types/ContentType.hpp" -#include "../helpers/MiscFunctions.hpp" #include "AsyncResourceGatherer.hpp" #include "ElementRenderer.hpp" #include "Framebuffer.hpp" @@ -1237,9 +1234,58 @@ SP IHyprRenderer::getBackground(PHLMONITOR pMonitor) { Log::logger->log(Log::DEBUG, "Creating a texture for BGTex"); SP backgroundTexture = createTexture(m_backgroundResource->m_asset.cairoSurface->cairo()); - if (!backgroundTexture->ok()) + + if (!backgroundTexture || !backgroundTexture->ok()) return nullptr; - Log::logger->log(Log::DEBUG, "Background created for monitor {}", pMonitor->m_name); + + Log::logger->log(Log::DEBUG, "BGTex created for monitor {}", pMonitor->m_name); + + const int monW = (int)std::round(pMonitor->m_transformedSize.x); + const int monH = (int)std::round(pMonitor->m_transformedSize.y); + const int origW = backgroundTexture->m_size.x; + const int origH = backgroundTexture->m_size.y; + + if (monW > 0 && monH > 0) { + const double scaleX = (double)monW / origW; + const double scaleY = (double)monH / origH; + const double scale = std::max(scaleX, scaleY); + + // scale the background if it's larger than the monitor + if (scale < 1.0) { + auto fb = createFB("BGTex scale"); + fb->alloc(monW, monH); + + auto guard = bindTempFB(fb); + + const auto oldProjType = m_renderData.projectionType; + const auto oldFbSize = m_renderData.fbSize; + const auto oldTransformDmg = m_renderData.transformDamage; + + m_renderData.fbSize = Vector2D{monW, monH}; + setProjectionType(RPT_EXPORT); + m_renderData.transformDamage = false; + setViewport(0, 0, monW, monH); + + draw(CClearPassElement::SClearData{{0.F, 0.F, 0.F, 0.F}}); + + const double texW = origW * scale; + const double texH = origH * scale; + const double offX = (monW - texW) / 2.0; + const double offY = (monH - texH) / 2.0; + + CRegion fullDamage = {0, 0, monW, monH}; + draw(CTexPassElement::SRenderData{.tex = backgroundTexture, .box = CBox{offX, offY, texW, texH}, .damage = fullDamage}, fullDamage); + + m_renderData.fbSize = oldFbSize; + m_renderData.transformDamage = oldTransformDmg; + setProjectionType(oldProjType); + setViewport(0, 0, (int)pMonitor->m_pixelSize.x, (int)pMonitor->m_pixelSize.y); + + backgroundTexture = fb->getTexture(); + + Log::logger->log(Log::INFO, "BGTex scaled from {}x{} to {}x{} for monitor {}", origW, origH, monW, monH, pMonitor->m_name); + } + } // clear the resource after we're done using it g_pEventLoopManager->doLater([this] { m_backgroundResource.reset(); });