From 465cf66df14c28427ba30580253be6e3908bd28c Mon Sep 17 00:00:00 2001 From: outfoxxed Date: Thu, 23 Jan 2025 13:57:33 -0800 Subject: protocols: add hyprland_surface_v1.set_visible_region implementation (#9120) --- src/render/OpenGL.cpp | 17 ++++++++++-- src/render/OpenGL.hpp | 1 + src/render/pass/SurfacePassElement.cpp | 50 ++++++++++++++++++++++++++++++++++ src/render/pass/SurfacePassElement.hpp | 1 + 4 files changed, 66 insertions(+), 3 deletions(-) (limited to 'src/render') diff --git a/src/render/OpenGL.cpp b/src/render/OpenGL.cpp index ceb94c81..8ae667d2 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 3dc3b43a..fd84d478 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 d720081f..b79407c8 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 12387367..1b6ddb0b 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"; -- cgit v1.2.3