diff --git a/src/desktop/WLSurface.hpp b/src/desktop/WLSurface.hpp index 057a6f905..5f8da7156 100644 --- a/src/desktop/WLSurface.hpp +++ b/src/desktop/WLSurface.hpp @@ -87,7 +87,8 @@ class CWLSurface { float m_fAlphaModifier = 1.F; // used by the hyprland-surface protocol - float m_fOverallOpacity = 1.F; + float m_fOverallOpacity = 1.F; + CRegion m_visibleRegion; struct { CSignal destroy; diff --git a/src/managers/ProtocolManager.cpp b/src/managers/ProtocolManager.cpp index f73a6d3c3..d3270fe2b 100644 --- a/src/managers/ProtocolManager.cpp +++ b/src/managers/ProtocolManager.cpp @@ -168,7 +168,7 @@ CProtocolManager::CProtocolManager() { PROTO::singlePixel = makeUnique(&wp_single_pixel_buffer_manager_v1_interface, 1, "SinglePixel"); PROTO::securityContext = makeUnique(&wp_security_context_manager_v1_interface, 1, "SecurityContext"); PROTO::ctm = makeUnique(&hyprland_ctm_control_manager_v1_interface, 1, "CTMControl"); - PROTO::hyprlandSurface = makeUnique(&hyprland_surface_manager_v1_interface, 1, "HyprlandSurface"); + PROTO::hyprlandSurface = makeUnique(&hyprland_surface_manager_v1_interface, 2, "HyprlandSurface"); if (*PENABLEXXCM) { PROTO::colorManagement = makeUnique(&xx_color_manager_v4_interface, 1, "ColorManagement"); diff --git a/src/protocols/HyprlandSurface.cpp b/src/protocols/HyprlandSurface.cpp index ca133b782..e3716f106 100644 --- a/src/protocols/HyprlandSurface.cpp +++ b/src/protocols/HyprlandSurface.cpp @@ -3,6 +3,8 @@ #include "../render/Renderer.hpp" #include "core/Compositor.hpp" #include "hyprland-surface-v1.hpp" +#include +#include CHyprlandSurface::CHyprlandSurface(SP resource, SP surface) : m_pSurface(surface) { setResource(std::move(resource)); @@ -36,11 +38,25 @@ void CHyprlandSurface::setResource(SP resource) { m_fOpacity = fOpacity; }); + m_pResource->setSetVisibleRegion([this](CHyprlandSurfaceV1* resource, wl_resource* region) { + if (!region) { + if (!m_visibleRegion.empty()) + m_bVisibleRegionChanged = true; + + m_visibleRegion.clear(); + return; + } + + m_bVisibleRegionChanged = true; + m_visibleRegion = CWLRegionResource::fromResource(region)->region; + }); + listeners.surfaceCommitted = m_pSurface->events.commit.registerListener([this](std::any data) { auto surface = CWLSurface::fromResource(m_pSurface.lock()); - if (surface && surface->m_fOverallOpacity != m_fOpacity) { + if (surface && (surface->m_fOverallOpacity != m_fOpacity || m_bVisibleRegionChanged)) { surface->m_fOverallOpacity = m_fOpacity; + surface->m_visibleRegion = m_visibleRegion; auto box = surface->getSurfaceBoxGlobal(); if (box.has_value()) @@ -61,6 +77,11 @@ void CHyprlandSurface::destroy() { m_pResource.reset(); m_fOpacity = 1.F; + if (!m_visibleRegion.empty()) + m_bVisibleRegionChanged = true; + + m_visibleRegion.clear(); + if (!m_pSurface) PROTO::hyprlandSurface->destroySurface(this); } diff --git a/src/protocols/HyprlandSurface.hpp b/src/protocols/HyprlandSurface.hpp index a5812b891..5c1181c47 100644 --- a/src/protocols/HyprlandSurface.hpp +++ b/src/protocols/HyprlandSurface.hpp @@ -1,5 +1,6 @@ #pragma once +#include #include #include #include "WaylandProtocol.hpp" @@ -19,7 +20,9 @@ class CHyprlandSurface { private: SP m_pResource; WP m_pSurface; - float m_fOpacity = 1.0; + float m_fOpacity = 1.0; + bool m_bVisibleRegionChanged = false; + CRegion m_visibleRegion; void destroy(); diff --git a/src/render/OpenGL.cpp b/src/render/OpenGL.cpp index ceb94c814..8ae667d22 100644 --- a/src/render/OpenGL.cpp +++ b/src/render/OpenGL.cpp @@ -1556,9 +1556,15 @@ void CHyprOpenGLImpl::renderTextureInternalWithDamage(SP tex, CBox* pB glEnableVertexAttribArray(shader->posAttrib); glEnableVertexAttribArray(shader->texAttrib); - if (m_RenderData.clipBox.width != 0 && m_RenderData.clipBox.height != 0) { - CRegion damageClip{m_RenderData.clipBox.x, m_RenderData.clipBox.y, m_RenderData.clipBox.width, m_RenderData.clipBox.height}; - damageClip.intersect(damage); + if (!m_RenderData.clipBox.empty() || !m_RenderData.clipRegion.empty()) { + CRegion damageClip = m_RenderData.clipBox; + + if (!m_RenderData.clipRegion.empty()) { + if (m_RenderData.clipBox.empty()) + damageClip = m_RenderData.clipRegion; + else + damageClip.intersect(m_RenderData.clipRegion); + } if (!damageClip.empty()) { for (auto const& RECT : damageClip.getRects()) { @@ -2079,6 +2085,11 @@ void CHyprOpenGLImpl::renderTextureWithBlur(SP tex, CBox* pBox, float CRegion texDamage{m_RenderData.damage}; texDamage.intersect(pBox->x, pBox->y, pBox->width, pBox->height); + // While renderTextureInternalWithDamage will clip the blur as well, + // clipping texDamage here allows blur generation to be optimized. + if (!m_RenderData.clipRegion.empty()) + texDamage.intersect(m_RenderData.clipRegion); + if (texDamage.empty()) return; diff --git a/src/render/OpenGL.hpp b/src/render/OpenGL.hpp index 3dc3b43a7..fd84d4785 100644 --- a/src/render/OpenGL.hpp +++ b/src/render/OpenGL.hpp @@ -133,6 +133,7 @@ struct SCurrentRenderData { Vector2D primarySurfaceUVBottomRight = Vector2D(-1, -1); CBox clipBox = {}; // scaled coordinates + CRegion clipRegion; uint32_t discardMode = DISCARD_OPAQUE; float discardOpacity = 0.f; diff --git a/src/render/pass/SurfacePassElement.cpp b/src/render/pass/SurfacePassElement.cpp index d720081fb..b79407c82 100644 --- a/src/render/pass/SurfacePassElement.cpp +++ b/src/render/pass/SurfacePassElement.cpp @@ -7,6 +7,8 @@ #include "../../managers/input/InputManager.hpp" #include "../Renderer.hpp" +#include +#include #include using namespace Hyprutils::Utils; @@ -28,6 +30,7 @@ void CSurfacePassElement::draw(const CRegion& damage) { g_pHyprOpenGL->m_RenderData.primarySurfaceUVBottomRight = Vector2D(-1, -1); g_pHyprOpenGL->m_RenderData.useNearestNeighbor = false; g_pHyprOpenGL->m_RenderData.clipBox = {}; + g_pHyprOpenGL->m_RenderData.clipRegion = {}; g_pHyprOpenGL->m_RenderData.discardMode = 0; g_pHyprOpenGL->m_RenderData.discardOpacity = 0; g_pHyprOpenGL->m_RenderData.useNearestNeighbor = false; @@ -84,6 +87,11 @@ void CSurfacePassElement::draw(const CRegion& damage) { Debug::log(TRACE, "FIXME: rendering surface with color management enabled, should apply necessary transformations"); g_pHyprRenderer->calculateUVForSurface(data.pWindow, data.surface, data.pMonitor->self.lock(), data.mainSurface, windowBox.size(), PROJSIZEUNSCALED, MISALIGNEDFSV1); + auto cancelRender = false; + g_pHyprOpenGL->m_RenderData.clipRegion = visibleRegion(cancelRender); + if (cancelRender) + return; + // check for fractional scale surfaces misaligning the buffer size // in those cases it's better to just force nearest neighbor // as long as the window is not animated. During those it'd look weird. @@ -229,6 +237,48 @@ CRegion CSurfacePassElement::opaqueRegion() { return data.texture && data.texture->m_bOpaque ? boundingBox()->expand(-data.rounding) : CRegion{}; } +CRegion CSurfacePassElement::visibleRegion(bool& cancel) { + auto PSURFACE = CWLSurface::fromResource(data.surface); + if (!PSURFACE) + return {}; + + const auto& bufferSize = data.surface->current.bufferSize; + + auto visibleRegion = PSURFACE->m_visibleRegion.copy(); + if (visibleRegion.empty()) + return {}; + + visibleRegion.intersect(CBox(Vector2D(), bufferSize)); + + if (visibleRegion.empty()) { + cancel = true; + return visibleRegion; + } + + // deal with any rounding errors that might come from scaling + visibleRegion.expand(1); + + auto uvTL = g_pHyprOpenGL->m_RenderData.primarySurfaceUVTopLeft; + auto uvBR = g_pHyprOpenGL->m_RenderData.primarySurfaceUVBottomRight; + + if (uvTL == Vector2D(-1, -1)) + uvTL = Vector2D(0, 0); + + if (uvBR == Vector2D(-1, -1)) + uvBR = Vector2D(1, 1); + + visibleRegion.translate(-uvTL * bufferSize); + + auto texBox = getTexBox(); + texBox.scale(data.pMonitor->scale); + texBox.round(); + + visibleRegion.scale((Vector2D(1, 1) / (uvBR - uvTL)) * (texBox.size() / bufferSize)); + visibleRegion.translate((data.pos + data.localPos) * data.pMonitor->scale - data.pMonitor->vecPosition); + + return visibleRegion; +} + void CSurfacePassElement::discard() { if (!g_pHyprRenderer->m_bBlockSurfaceFeedback) { Debug::log(TRACE, "discard for invisible surface"); diff --git a/src/render/pass/SurfacePassElement.hpp b/src/render/pass/SurfacePassElement.hpp index 12387367e..1b6ddb0ba 100644 --- a/src/render/pass/SurfacePassElement.hpp +++ b/src/render/pass/SurfacePassElement.hpp @@ -57,6 +57,7 @@ class CSurfacePassElement : public IPassElement { virtual std::optional boundingBox(); virtual CRegion opaqueRegion(); virtual void discard(); + CRegion visibleRegion(bool& cancel); virtual const char* passName() { return "CSurfacePassElement"; diff --git a/subprojects/hyprland-protocols b/subprojects/hyprland-protocols index 455c05588..4c75dd5c0 160000 --- a/subprojects/hyprland-protocols +++ b/subprojects/hyprland-protocols @@ -1 +1 @@ -Subproject commit 455c055883d9639d4fcbfcedb4c6d12ce313791e +Subproject commit 4c75dd5c015c8a0e5a34c6d02a018a650f57feb5