aboutsummaryrefslogtreecommitdiffhomepage
path: root/src/render
diff options
context:
space:
mode:
authorVaxry <[email protected]>2024-12-22 17:12:09 +0100
committerGitHub <[email protected]>2024-12-22 17:12:09 +0100
commite536b02248f370971716b744968776f6880528be (patch)
tree5e5755b7894eb44f9259cfe0e1d97899148a8a60 /src/render
parent1cc1a46c2e154a27750b81307040d3bf7ff0f64f (diff)
downloadHyprland-e536b02248f370971716b744968776f6880528be.tar.gz
Hyprland-e536b02248f370971716b744968776f6880528be.zip
Renderer: rewrite render scheduling (#8683)
This rewrites renderer scheduling. Occlusion is now unified in a new Pass type.
Diffstat (limited to 'src/render')
-rw-r--r--src/render/OpenGL.cpp322
-rw-r--r--src/render/OpenGL.hpp39
-rw-r--r--src/render/Renderer.cpp803
-rw-r--r--src/render/Renderer.hpp17
-rw-r--r--src/render/Transformer.cpp2
-rw-r--r--src/render/Transformer.hpp5
-rw-r--r--src/render/decorations/CHyprBorderDecoration.cpp24
-rw-r--r--src/render/decorations/CHyprDropShadowDecoration.cpp11
-rw-r--r--src/render/decorations/CHyprDropShadowDecoration.hpp2
-rw-r--r--src/render/decorations/CHyprGroupBarDecoration.cpp51
-rw-r--r--src/render/pass/BorderPassElement.cpp21
-rw-r--r--src/render/pass/BorderPassElement.hpp30
-rw-r--r--src/render/pass/ClearPassElement.cpp26
-rw-r--r--src/render/pass/ClearPassElement.hpp25
-rw-r--r--src/render/pass/FramebufferElement.cpp48
-rw-r--r--src/render/pass/FramebufferElement.hpp24
-rw-r--r--src/render/pass/Pass.cpp157
-rw-r--r--src/render/pass/Pass.hpp36
-rw-r--r--src/render/pass/PassElement.cpp17
-rw-r--r--src/render/pass/PassElement.hpp18
-rw-r--r--src/render/pass/PreBlurElement.cpp20
-rw-r--r--src/render/pass/PreBlurElement.hpp17
-rw-r--r--src/render/pass/RectPassElement.cpp29
-rw-r--r--src/render/pass/RectPassElement.hpp29
-rw-r--r--src/render/pass/ShadowPassElement.cpp19
-rw-r--r--src/render/pass/ShadowPassElement.hpp26
-rw-r--r--src/render/pass/SurfacePassElement.cpp230
-rw-r--r--src/render/pass/SurfacePassElement.hpp82
-rw-r--r--src/render/pass/TexPassElement.cpp44
-rw-r--r--src/render/pass/TexPassElement.hpp39
-rw-r--r--src/render/pass/TextureMatteElement.cpp25
-rw-r--r--src/render/pass/TextureMatteElement.hpp29
32 files changed, 1540 insertions, 727 deletions
diff --git a/src/render/OpenGL.cpp b/src/render/OpenGL.cpp
index 4339fd40..d9f19fea 100644
--- a/src/render/OpenGL.cpp
+++ b/src/render/OpenGL.cpp
@@ -8,6 +8,10 @@
#include "../desktop/LayerSurface.hpp"
#include "../protocols/LayerShell.hpp"
#include "../protocols/core/Compositor.hpp"
+#include "pass/TexPassElement.hpp"
+#include "pass/RectPassElement.hpp"
+#include "pass/PreBlurElement.hpp"
+#include "pass/ClearPassElement.hpp"
#include <xf86drm.h>
#include <fcntl.h>
#include <gbm.h>
@@ -584,7 +588,7 @@ GLuint CHyprOpenGLImpl::createProgram(const std::string& vert, const std::string
if (fragCompiled == 0)
return 0;
} else {
- RASSERT(fragCompiled, "Compiling shader failed. FRAGMENT nullptr! Shader source:\n\n{}", frag.c_str());
+ RASSERT(fragCompiled, "Compiling shader failed. FRAGMENT nullptr! Shader source:\n\n{}", frag);
}
auto prog = glCreateProgram();
@@ -663,7 +667,7 @@ bool CHyprOpenGLImpl::passRequiresIntrospection(PHLMONITOR pMonitor) {
if (*PBLUR == 0)
return false;
- if (m_RenderData.pCurrentMonData->blurFBShouldRender)
+ if (preBlurQueued())
return true;
if (!pMonitor->solitaryClient.expired())
@@ -1242,7 +1246,7 @@ void CHyprOpenGLImpl::scissor(const int x, const int y, const int w, const int h
void CHyprOpenGLImpl::renderRect(CBox* box, const CHyprColor& col, int round) {
if (!m_RenderData.damage.empty())
- renderRectWithDamage(box, col, &m_RenderData.damage, round);
+ renderRectWithDamage(box, col, m_RenderData.damage, round);
}
void CHyprOpenGLImpl::renderRectWithBlur(CBox* box, const CHyprColor& col, int round, float blurA, bool xray) {
@@ -1278,7 +1282,7 @@ void CHyprOpenGLImpl::renderRectWithBlur(CBox* box, const CHyprColor& col, int r
m_bEndFrame = true; // fix transformed
const auto SAVEDRENDERMODIF = m_RenderData.renderModif;
m_RenderData.renderModif = {}; // fix shit
- renderTextureInternalWithDamage(POUTFB->getTexture(), &MONITORBOX, blurA, &damage, 0, false, false, false);
+ renderTextureInternalWithDamage(POUTFB->getTexture(), &MONITORBOX, blurA, damage, 0, false, false, false);
m_bEndFrame = false;
m_RenderData.renderModif = SAVEDRENDERMODIF;
@@ -1289,10 +1293,10 @@ void CHyprOpenGLImpl::renderRectWithBlur(CBox* box, const CHyprColor& col, int r
glStencilFunc(GL_ALWAYS, 1, 0xFF);
scissor((CBox*)nullptr);
- renderRectWithDamage(box, col, &m_RenderData.damage, round);
+ renderRectWithDamage(box, col, m_RenderData.damage, round);
}
-void CHyprOpenGLImpl::renderRectWithDamage(CBox* box, const CHyprColor& col, CRegion* damage, int round) {
+void CHyprOpenGLImpl::renderRectWithDamage(CBox* box, const CHyprColor& col, const CRegion& damage, int round) {
RASSERT((box->width > 0 && box->height > 0), "Tried to render rect with width/height < 0!");
RASSERT(m_RenderData.pMonitor, "Tried to render rect without begin()!");
@@ -1337,7 +1341,7 @@ void CHyprOpenGLImpl::renderRectWithDamage(CBox* box, const CHyprColor& col, CRe
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);
+ damageClip.intersect(damage);
if (!damageClip.empty()) {
for (auto const& RECT : damageClip.getRects()) {
@@ -1346,7 +1350,7 @@ void CHyprOpenGLImpl::renderRectWithDamage(CBox* box, const CHyprColor& col, CRe
}
}
} else {
- for (auto const& RECT : damage->getRects()) {
+ for (auto const& RECT : damage.getRects()) {
scissor(&RECT);
glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
}
@@ -1360,12 +1364,12 @@ void CHyprOpenGLImpl::renderRectWithDamage(CBox* box, const CHyprColor& col, CRe
void CHyprOpenGLImpl::renderTexture(SP<CTexture> tex, CBox* pBox, float alpha, int round, bool discardActive, bool allowCustomUV) {
RASSERT(m_RenderData.pMonitor, "Tried to render texture without begin()!");
- renderTextureInternalWithDamage(tex, pBox, alpha, &m_RenderData.damage, round, discardActive, false, allowCustomUV, true);
+ renderTextureInternalWithDamage(tex, pBox, alpha, m_RenderData.damage, round, discardActive, false, allowCustomUV, true);
scissor((CBox*)nullptr);
}
-void CHyprOpenGLImpl::renderTextureWithDamage(SP<CTexture> tex, CBox* pBox, CRegion* damage, float alpha, int round, bool discardActive, bool allowCustomUV,
+void CHyprOpenGLImpl::renderTextureWithDamage(SP<CTexture> tex, CBox* pBox, const CRegion& damage, float alpha, int round, bool discardActive, bool allowCustomUV,
SP<CSyncTimeline> waitTimeline, uint64_t waitPoint) {
RASSERT(m_RenderData.pMonitor, "Tried to render texture without begin()!");
@@ -1374,8 +1378,8 @@ void CHyprOpenGLImpl::renderTextureWithDamage(SP<CTexture> tex, CBox* pBox, CReg
scissor((CBox*)nullptr);
}
-void CHyprOpenGLImpl::renderTextureInternalWithDamage(SP<CTexture> tex, CBox* pBox, float alpha, CRegion* damage, int round, bool discardActive, bool noAA, bool allowCustomUV,
- bool allowDim, SP<CSyncTimeline> waitTimeline, uint64_t waitPoint) {
+void CHyprOpenGLImpl::renderTextureInternalWithDamage(SP<CTexture> tex, CBox* pBox, float alpha, const CRegion& damage, int round, bool discardActive, bool noAA,
+ bool allowCustomUV, bool allowDim, SP<CSyncTimeline> waitTimeline, uint64_t waitPoint) {
RASSERT(m_RenderData.pMonitor, "Tried to render texture without begin()!");
RASSERT((tex->m_iTexID > 0), "Attempted to draw nullptr texture!");
@@ -1383,7 +1387,7 @@ void CHyprOpenGLImpl::renderTextureInternalWithDamage(SP<CTexture> tex, CBox* pB
alpha = std::clamp(alpha, 0.f, 1.f);
- if (damage->empty())
+ if (damage.empty())
return;
CBox newBox = *pBox;
@@ -1433,7 +1437,7 @@ void CHyprOpenGLImpl::renderTextureInternalWithDamage(SP<CTexture> tex, CBox* pB
}
}
- if (m_pCurrentWindow.lock() && m_pCurrentWindow->m_sWindowData.RGBX.valueOrDefault())
+ if (m_RenderData.currentWindow && m_RenderData.currentWindow->m_sWindowData.RGBX.valueOrDefault())
shader = &m_RenderData.pCurrentMonData->m_shRGBX;
glActiveTexture(GL_TEXTURE0);
@@ -1503,9 +1507,9 @@ void CHyprOpenGLImpl::renderTextureInternalWithDamage(SP<CTexture> tex, CBox* pB
glUniform2f(shader->fullSize, FULLSIZE.x, FULLSIZE.y);
glUniform1f(shader->radius, round);
- if (allowDim && m_pCurrentWindow.lock()) {
+ if (allowDim && m_RenderData.currentWindow) {
glUniform1i(shader->applyTint, 1);
- const auto DIM = m_pCurrentWindow->m_fDimPercent.value();
+ const auto DIM = m_RenderData.currentWindow->m_fDimPercent.value();
glUniform3f(shader->tint, 1.f - DIM, 1.f - DIM, 1.f - DIM);
} else {
glUniform1i(shader->applyTint, 0);
@@ -1532,7 +1536,7 @@ void CHyprOpenGLImpl::renderTextureInternalWithDamage(SP<CTexture> tex, CBox* pB
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);
+ damageClip.intersect(damage);
if (!damageClip.empty()) {
for (auto const& RECT : damageClip.getRects()) {
@@ -1541,7 +1545,7 @@ void CHyprOpenGLImpl::renderTextureInternalWithDamage(SP<CTexture> tex, CBox* pB
}
}
} else {
- for (auto const& RECT : damage->getRects()) {
+ for (auto const& RECT : damage.getRects()) {
scissor(&RECT);
glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
}
@@ -1993,7 +1997,7 @@ void CHyprOpenGLImpl::preBlurForCurrentMonitor() {
clear(CHyprColor(0, 0, 0, 0));
m_bEndFrame = true; // fix transformed
- renderTextureInternalWithDamage(POUTFB->getTexture(), &wholeMonitor, 1, &fakeDamage, 0, false, true, false);
+ renderTextureInternalWithDamage(POUTFB->getTexture(), &wholeMonitor, 1, fakeDamage, 0, false, true, false);
m_bEndFrame = false;
m_RenderData.currentFB->bind();
@@ -2009,15 +2013,14 @@ void CHyprOpenGLImpl::preWindowPass() {
if (!preBlurQueued())
return;
- // blur the main FB, it will be rendered onto the mirror
- preBlurForCurrentMonitor();
+ g_pHyprRenderer->m_sRenderPass.add(makeShared<CPreBlurElement>());
}
bool CHyprOpenGLImpl::preBlurQueued() {
static auto PBLURNEWOPTIMIZE = CConfigValue<Hyprlang::INT>("decoration:blur:new_optimizations");
static auto PBLUR = CConfigValue<Hyprlang::INT>("decoration:blur:enabled");
- return !(!m_RenderData.pCurrentMonData->blurFBDirty || !*PBLURNEWOPTIMIZE || !*PBLUR || !m_RenderData.pCurrentMonData->blurFBShouldRender);
+ return m_RenderData.pCurrentMonData->blurFBDirty && *PBLURNEWOPTIMIZE && *PBLUR && m_RenderData.pCurrentMonData->blurFBShouldRender;
}
bool CHyprOpenGLImpl::shouldUseNewBlurOptimizations(PHLLS pLayer, PHLWINDOW pWindow) {
@@ -2082,7 +2085,7 @@ void CHyprOpenGLImpl::renderTextureWithBlur(SP<CTexture> tex, CBox* pBox, float
inverseOpaque.scale(m_RenderData.pMonitor->scale);
// vvv TODO: layered blur fbs?
- const bool USENEWOPTIMIZE = shouldUseNewBlurOptimizations(m_pCurrentLayer, m_pCurrentWindow.lock()) && !blockBlurOptimization;
+ const bool USENEWOPTIMIZE = shouldUseNewBlurOptimizations(m_RenderData.currentLS.lock(), m_RenderData.currentWindow.lock()) && !blockBlurOptimization;
CFramebuffer* POUTFB = nullptr;
if (!USENEWOPTIMIZE) {
@@ -2091,9 +2094,8 @@ void CHyprOpenGLImpl::renderTextureWithBlur(SP<CTexture> tex, CBox* pBox, float
inverseOpaque.intersect(texDamage);
POUTFB = blurMainFramebufferWithDamage(a, &inverseOpaque);
- } else {
+ } else
POUTFB = &m_RenderData.pCurrentMonData->blurFB;
- }
m_RenderData.currentFB->bind();
@@ -2124,7 +2126,7 @@ void CHyprOpenGLImpl::renderTextureWithBlur(SP<CTexture> tex, CBox* pBox, float
setMonitorTransformEnabled(true);
if (!USENEWOPTIMIZE)
setRenderModifEnabled(false);
- renderTextureInternalWithDamage(POUTFB->getTexture(), &MONITORBOX, *PBLURIGNOREOPACITY ? blurA : a * blurA, &texDamage, 0, false, false, false);
+ renderTextureInternalWithDamage(POUTFB->getTexture(), &MONITORBOX, *PBLURIGNOREOPACITY ? blurA : a * blurA, texDamage, 0, false, false, false);
if (!USENEWOPTIMIZE)
setRenderModifEnabled(true);
setMonitorTransformEnabled(false);
@@ -2135,7 +2137,7 @@ void CHyprOpenGLImpl::renderTextureWithBlur(SP<CTexture> tex, CBox* pBox, float
// draw window
glDisable(GL_STENCIL_TEST);
- renderTextureInternalWithDamage(tex, pBox, a, &texDamage, round, false, false, true, true);
+ renderTextureInternalWithDamage(tex, pBox, a, texDamage, round, false, false, true, true);
glStencilMask(0xFF);
glStencilFunc(GL_ALWAYS, 1, 0xFF);
@@ -2148,7 +2150,7 @@ void CHyprOpenGLImpl::renderBorder(CBox* box, const CGradientValueData& grad, in
TRACY_GPU_ZONE("RenderBorder");
- if (m_RenderData.damage.empty() || (m_pCurrentWindow.lock() && m_pCurrentWindow->m_sWindowData.noBorder.valueOrDefault()))
+ if (m_RenderData.damage.empty() || (m_RenderData.currentWindow && m_RenderData.currentWindow->m_sWindowData.noBorder.valueOrDefault()))
return;
CBox newBox = *box;
@@ -2241,7 +2243,7 @@ void CHyprOpenGLImpl::renderBorder(CBox* box, const CGradientValueData& grad1, c
TRACY_GPU_ZONE("RenderBorder2");
- if (m_RenderData.damage.empty() || (m_pCurrentWindow.lock() && m_pCurrentWindow->m_sWindowData.noBorder.valueOrDefault()))
+ if (m_RenderData.damage.empty() || (m_RenderData.currentWindow && m_RenderData.currentWindow->m_sWindowData.noBorder.valueOrDefault()))
return;
CBox newBox = *box;
@@ -2332,225 +2334,10 @@ void CHyprOpenGLImpl::renderBorder(CBox* box, const CGradientValueData& grad1, c
blend(BLEND);
}
-void CHyprOpenGLImpl::makeRawWindowSnapshot(PHLWINDOW pWindow, CFramebuffer* pFramebuffer) {
- // we trust the window is valid.
- const auto PMONITOR = pWindow->m_pMonitor.lock();
-
- if (!PMONITOR || !PMONITOR->output || PMONITOR->vecPixelSize.x <= 0 || PMONITOR->vecPixelSize.y <= 0)
- return;
-
- // we need to "damage" the entire monitor
- // so that we render the entire window
- // this is temporary, doesnt mess with the actual damage
- CRegion fakeDamage{0, 0, (int)PMONITOR->vecTransformedSize.x, (int)PMONITOR->vecTransformedSize.y};
-
- g_pHyprRenderer->makeEGLCurrent();
-
- pFramebuffer->alloc(PMONITOR->vecPixelSize.x, PMONITOR->vecPixelSize.y, PMONITOR->output->state->state().drmFormat);
- pFramebuffer->addStencil(m_RenderData.pCurrentMonData->stencilTex);
-
- g_pHyprRenderer->beginRender(PMONITOR, fakeDamage, RENDER_MODE_FULL_FAKE, nullptr, pFramebuffer);
-
- clear(CHyprColor(0, 0, 0, 0)); // JIC
-
- timespec now;
- clock_gettime(CLOCK_MONOTONIC, &now);
-
- // this is a hack but it works :P
- // we need to disable blur or else we will get a black background, as the shader
- // will try to copy the bg to apply blur.
- // this isn't entirely correct, but like, oh well.
- // small todo: maybe make this correct? :P
- static auto* const PBLUR = (Hyprlang::INT* const*)(g_pConfigManager->getConfigValuePtr("decoration:blur:enabled"));
- const auto BLURVAL = **PBLUR;
- **PBLUR = 0;
-
- // TODO: how can we make this the size of the window? setting it to window's size makes the entire screen render with the wrong res forever more. odd.
- glViewport(0, 0, PMONITOR->vecPixelSize.x, PMONITOR->vecPixelSize.y);
-
- m_RenderData.currentFB = pFramebuffer;
-
- clear(CHyprColor(0, 0, 0, 0)); // JIC
-
- g_pHyprRenderer->renderWindow(pWindow, PMONITOR, &now, false, RENDER_PASS_ALL, true);
-
- **PBLUR = BLURVAL;
-
- g_pHyprRenderer->endRender();
-}
-
-void CHyprOpenGLImpl::makeWindowSnapshot(PHLWINDOW pWindow) {
- // we trust the window is valid.
- const auto PMONITOR = pWindow->m_pMonitor.lock();
-
- if (!PMONITOR || !PMONITOR->output || PMONITOR->vecPixelSize.x <= 0 || PMONITOR->vecPixelSize.y <= 0)
- return;
-
- if (!g_pHyprRenderer->shouldRenderWindow(pWindow))
- return; // ignore, window is not being rendered
-
- // we need to "damage" the entire monitor
- // so that we render the entire window
- // this is temporary, doesnt mess with the actual damage
- CRegion fakeDamage{0, 0, (int)PMONITOR->vecTransformedSize.x, (int)PMONITOR->vecTransformedSize.y};
-
- PHLWINDOWREF ref{pWindow};
-
- g_pHyprRenderer->makeEGLCurrent();
-
- const auto PFRAMEBUFFER = &m_mWindowFramebuffers[ref];
-
- PFRAMEBUFFER->alloc(PMONITOR->vecPixelSize.x, PMONITOR->vecPixelSize.y, PMONITOR->output->state->state().drmFormat);
-
- g_pHyprRenderer->beginRender(PMONITOR, fakeDamage, RENDER_MODE_FULL_FAKE, nullptr, PFRAMEBUFFER);
-
- g_pHyprRenderer->m_bRenderingSnapshot = true;
-
- clear(CHyprColor(0, 0, 0, 0)); // JIC
-
- timespec now;
- clock_gettime(CLOCK_MONOTONIC, &now);
-
- // this is a hack but it works :P
- // we need to disable blur or else we will get a black background, as the shader
- // will try to copy the bg to apply blur.
- // this isn't entirely correct, but like, oh well.
- // small todo: maybe make this correct? :P
- static auto* const PBLUR = (Hyprlang::INT* const*)(g_pConfigManager->getConfigValuePtr("decoration:blur:enabled"));
- const auto BLURVAL = **PBLUR;
- **PBLUR = 0;
-
- clear(CHyprColor(0, 0, 0, 0)); // JIC
-
- g_pHyprRenderer->renderWindow(pWindow, PMONITOR, &now, !pWindow->m_bX11DoesntWantBorders, RENDER_PASS_ALL);
-
- **PBLUR = BLURVAL;
-
- g_pHyprRenderer->endRender();
-
- g_pHyprRenderer->m_bRenderingSnapshot = false;
-}
-
-void CHyprOpenGLImpl::makeLayerSnapshot(PHLLS pLayer) {
- // we trust the window is valid.
- const auto PMONITOR = pLayer->monitor.lock();
-
- if (!PMONITOR || !PMONITOR->output || PMONITOR->vecPixelSize.x <= 0 || PMONITOR->vecPixelSize.y <= 0)
- return;
-
- // we need to "damage" the entire monitor
- // so that we render the entire window
- // this is temporary, doesnt mess with the actual damage
- CRegion fakeDamage{0, 0, (int)PMONITOR->vecTransformedSize.x, (int)PMONITOR->vecTransformedSize.y};
-
- g_pHyprRenderer->makeEGLCurrent();
-
- const auto PFRAMEBUFFER = &m_mLayerFramebuffers[pLayer];
-
- PFRAMEBUFFER->alloc(PMONITOR->vecPixelSize.x, PMONITOR->vecPixelSize.y, PMONITOR->output->state->state().drmFormat);
-
- g_pHyprRenderer->beginRender(PMONITOR, fakeDamage, RENDER_MODE_FULL_FAKE, nullptr, PFRAMEBUFFER);
-
- g_pHyprRenderer->m_bRenderingSnapshot = true;
-
- clear(CHyprColor(0, 0, 0, 0)); // JIC
-
- timespec now;
- clock_gettime(CLOCK_MONOTONIC, &now);
-
- const auto BLURLSSTATUS = pLayer->forceBlur;
- pLayer->forceBlur = false;
-
- // draw the layer
- g_pHyprRenderer->renderLayer(pLayer, PMONITOR, &now);
-
- pLayer->forceBlur = BLURLSSTATUS;
-
- g_pHyprRenderer->endRender();
-
- g_pHyprRenderer->m_bRenderingSnapshot = false;
-}
-
-void CHyprOpenGLImpl::renderSnapshot(PHLWINDOW pWindow) {
- RASSERT(m_RenderData.pMonitor, "Tried to render snapshot rect without begin()!");
-
- static auto PDIMAROUND = CConfigValue<Hyprlang::FLOAT>("decoration:dim_around");
-
- PHLWINDOWREF ref{pWindow};
-
- if (!m_mWindowFramebuffers.contains(ref))
- return;
-
- const auto FBDATA = &m_mWindowFramebuffers.at(ref);
-
- if (!FBDATA->getTexture())
- return;
-
- const auto PMONITOR = pWindow->m_pMonitor.lock();
-
- CBox windowBox;
- // some mafs to figure out the correct box
- // the originalClosedPos is relative to the monitor's pos
- Vector2D scaleXY = Vector2D((PMONITOR->scale * pWindow->m_vRealSize.value().x / (pWindow->m_vOriginalClosedSize.x * PMONITOR->scale)),
- (PMONITOR->scale * pWindow->m_vRealSize.value().y / (pWindow->m_vOriginalClosedSize.y * PMONITOR->scale)));
-
- windowBox.width = PMONITOR->vecTransformedSize.x * scaleXY.x;
- windowBox.height = PMONITOR->vecTransformedSize.y * scaleXY.y;
- windowBox.x = ((pWindow->m_vRealPosition.value().x - PMONITOR->vecPosition.x) * PMONITOR->scale) - ((pWindow->m_vOriginalClosedPos.x * PMONITOR->scale) * scaleXY.x);
- windowBox.y = ((pWindow->m_vRealPosition.value().y - PMONITOR->vecPosition.y) * PMONITOR->scale) - ((pWindow->m_vOriginalClosedPos.y * PMONITOR->scale) * scaleXY.y);
-
- CRegion fakeDamage{0, 0, PMONITOR->vecTransformedSize.x, PMONITOR->vecTransformedSize.y};
-
- if (*PDIMAROUND && pWindow->m_sWindowData.dimAround.valueOrDefault()) {
- CBox monbox = {0, 0, g_pHyprOpenGL->m_RenderData.pMonitor->vecPixelSize.x, g_pHyprOpenGL->m_RenderData.pMonitor->vecPixelSize.y};
- g_pHyprOpenGL->renderRect(&monbox, CHyprColor(0, 0, 0, *PDIMAROUND * pWindow->m_fAlpha.value()));
- g_pHyprRenderer->damageMonitor(PMONITOR);
- }
-
- m_bEndFrame = true;
-
- renderTextureInternalWithDamage(FBDATA->getTexture(), &windowBox, pWindow->m_fAlpha.value(), &fakeDamage, 0);
-
- m_bEndFrame = false;
-}
-
-void CHyprOpenGLImpl::renderSnapshot(PHLLS pLayer) {
- RASSERT(m_RenderData.pMonitor, "Tried to render snapshot rect without begin()!");
-
- if (!m_mLayerFramebuffers.contains(pLayer))
- return;
-
- const auto FBDATA = &m_mLayerFramebuffers.at(pLayer);
-
- if (!FBDATA->getTexture())
- return;
-
- const auto PMONITOR = pLayer->monitor.lock();
-
- CBox layerBox;
- // some mafs to figure out the correct box
- // the originalClosedPos is relative to the monitor's pos
- Vector2D scaleXY = Vector2D((PMONITOR->scale * pLayer->realSize.value().x / (pLayer->geometry.w * PMONITOR->scale)),
- (PMONITOR->scale * pLayer->realSize.value().y / (pLayer->geometry.h * PMONITOR->scale)));
-
- layerBox.width = PMONITOR->vecTransformedSize.x * scaleXY.x;
- layerBox.height = PMONITOR->vecTransformedSize.y * scaleXY.y;
- layerBox.x = ((pLayer->realPosition.value().x - PMONITOR->vecPosition.x) * PMONITOR->scale) - (((pLayer->geometry.x - PMONITOR->vecPosition.x) * PMONITOR->scale) * scaleXY.x);
- layerBox.y = ((pLayer->realPosition.value().y - PMONITOR->vecPosition.y) * PMONITOR->scale) - (((pLayer->geometry.y - PMONITOR->vecPosition.y) * PMONITOR->scale) * scaleXY.y);
-
- CRegion fakeDamage{0, 0, PMONITOR->vecTransformedSize.x, PMONITOR->vecTransformedSize.y};
-
- m_bEndFrame = true;
-
- renderTextureInternalWithDamage(FBDATA->getTexture(), &layerBox, pLayer->alpha.value(), &fakeDamage, 0);
-
- m_bEndFrame = false;
-}
-
void CHyprOpenGLImpl::renderRoundedShadow(CBox* box, int round, int range, const CHyprColor& color, float a) {
RASSERT(m_RenderData.pMonitor, "Tried to render shadow without begin()!");
RASSERT((box->width > 0 && box->height > 0), "Tried to render shadow with width/height < 0!");
- RASSERT(m_pCurrentWindow.lock(), "Tried to render shadow without a window!");
+ RASSERT(m_RenderData.currentWindow, "Tried to render shadow without a window!");
if (m_RenderData.damage.empty())
return;
@@ -2642,11 +2429,11 @@ void CHyprOpenGLImpl::saveBufferForMirror(CBox* box) {
void CHyprOpenGLImpl::renderMirrored() {
- auto monitor = m_RenderData.pMonitor;
- auto mirrored = monitor->pMirrorOf;
+ auto monitor = m_RenderData.pMonitor;
+ auto mirrored = monitor->pMirrorOf;
- double scale = std::min(monitor->vecTransformedSize.x / mirrored->vecTransformedSize.x, monitor->vecTransformedSize.y / mirrored->vecTransformedSize.y);
- CBox monbox = {0, 0, mirrored->vecTransformedSize.x * scale, mirrored->vecTransformedSize.y * scale};
+ const double scale = std::min(monitor->vecTransformedSize.x / mirrored->vecTransformedSize.x, monitor->vecTransformedSize.y / mirrored->vecTransformedSize.y);
+ CBox monbox = {0, 0, mirrored->vecTransformedSize.x * scale, mirrored->vecTransformedSize.y * scale};
// transform box as it will be drawn on a transformed projection
monbox.transform(wlTransformToHyprutils(mirrored->transform), mirrored->vecTransformedSize.x * scale, mirrored->vecTransformedSize.y * scale);
@@ -2658,20 +2445,18 @@ void CHyprOpenGLImpl::renderMirrored() {
if (!PFB->isAllocated() || !PFB->getTexture())
return;
- // replace monitor projection to undo the mirrored monitor's projection
- m_RenderData.monitorProjection = Mat3x3::identity()
- .translate(monitor->vecPixelSize / 2.0)
- .transform(wlTransformToHyprutils(monitor->transform))
- .transform(wlTransformToHyprutils(invertTransform(mirrored->transform)))
- .translate(-monitor->vecTransformedSize / 2.0);
-
- // clear stuff outside of mirrored area (e.g. when changing to mirrored)
- clear(CHyprColor(0, 0, 0, 0));
+ g_pHyprRenderer->m_sRenderPass.add(makeShared<CClearPassElement>(CClearPassElement::SClearData{CHyprColor(0, 0, 0, 0)}));
- renderTexture(PFB->getTexture(), &monbox, 1.f, 0, false, false);
+ CTexPassElement::SRenderData data;
+ data.tex = PFB->getTexture();
+ data.box = monbox;
+ data.replaceProjection = Mat3x3::identity()
+ .translate(monitor->vecPixelSize / 2.0)
+ .transform(wlTransformToHyprutils(monitor->transform))
+ .transform(wlTransformToHyprutils(invertTransform(mirrored->transform)))
+ .translate(-monitor->vecTransformedSize / 2.0);
- // reset matrix for further drawing
- m_RenderData.monitorProjection = monitor->projMatrix;
+ g_pHyprRenderer->m_sRenderPass.add(makeShared<CTexPassElement>(data));
}
void CHyprOpenGLImpl::renderSplash(cairo_t* const CAIRO, cairo_surface_t* const CAIROSURFACE, double offsetY, const Vector2D& size) {
@@ -3005,11 +2790,11 @@ void CHyprOpenGLImpl::createBGTextureForMonitor(PHLMONITOR pMonitor) {
}
CBox texbox = CBox{origin, m_pBackgroundTexture->m_vSize * scale};
- renderTextureInternalWithDamage(m_pBackgroundTexture, &texbox, 1.0, &fakeDamage);
+ renderTextureInternalWithDamage(m_pBackgroundTexture, &texbox, 1.0, fakeDamage);
}
CBox monbox = {{}, pMonitor->vecPixelSize};
- renderTextureInternalWithDamage(tex, &monbox, 1.0, &fakeDamage);
+ renderTextureInternalWithDamage(tex, &monbox, 1.0, fakeDamage);
// bind back
if (m_RenderData.currentFB)
@@ -3021,8 +2806,6 @@ void CHyprOpenGLImpl::createBGTextureForMonitor(PHLMONITOR pMonitor) {
void CHyprOpenGLImpl::clearWithTex() {
RASSERT(m_RenderData.pMonitor, "Tried to render BGtex without begin()!");
- TRACY_GPU_ZONE("RenderClearWithTex");
-
auto TEXIT = m_mMonitorBGFBs.find(m_RenderData.pMonitor);
if (TEXIT == m_mMonitorBGFBs.end()) {
@@ -3031,10 +2814,11 @@ void CHyprOpenGLImpl::clearWithTex() {
}
if (TEXIT != m_mMonitorBGFBs.end()) {
- CBox monbox = {0, 0, m_RenderData.pMonitor->vecTransformedSize.x, m_RenderData.pMonitor->vecTransformedSize.y};
- m_bEndFrame = true;
- renderTexture(TEXIT->second.getTexture(), &monbox, 1);
- m_bEndFrame = false;
+ CTexPassElement::SRenderData data;
+ data.box = {0, 0, m_RenderData.pMonitor->vecTransformedSize.x, m_RenderData.pMonitor->vecTransformedSize.y};
+ data.flipEndFrame = true;
+ data.tex = TEXIT->second.getTexture();
+ g_pHyprRenderer->m_sRenderPass.add(makeShared<CTexPassElement>(data));
}
}
diff --git a/src/render/OpenGL.hpp b/src/render/OpenGL.hpp
index 51f38708..0b72d438 100644
--- a/src/render/OpenGL.hpp
+++ b/src/render/OpenGL.hpp
@@ -19,6 +19,7 @@
#include "Framebuffer.hpp"
#include "Transformer.hpp"
#include "Renderbuffer.hpp"
+#include "pass/Pass.hpp"
#include <EGL/egl.h>
#include <EGL/eglext.h>
@@ -61,17 +62,31 @@ struct SRenderModifData {
bool enabled = true;
};
+enum eMonitorRenderFBs : uint8_t {
+ FB_MONITOR_RENDER_MAIN = 0,
+ FB_MONITOR_RENDER_CURRENT = 1,
+ FB_MONITOR_RENDER_OUT = 2,
+};
+
+enum eMonitorExtraRenderFBs : uint8_t {
+ FB_MONITOR_RENDER_EXTRA_OFFLOAD = 0,
+ FB_MONITOR_RENDER_EXTRA_MIRROR,
+ FB_MONITOR_RENDER_EXTRA_MIRROR_SWAP,
+ FB_MONITOR_RENDER_EXTRA_OFF_MAIN,
+ FB_MONITOR_RENDER_EXTRA_MONITOR_MIRROR,
+ FB_MONITOR_RENDER_EXTRA_BLUR,
+};
+
struct SMonitorRenderData {
CFramebuffer offloadFB;
CFramebuffer mirrorFB; // these are used for some effects,
CFramebuffer mirrorSwapFB; // etc
CFramebuffer offMainFB;
-
CFramebuffer monitorMirrorFB; // used for mirroring outputs, does not contain artifacts like offloadFB
+ CFramebuffer blurFB;
SP<CTexture> stencilTex = makeShared<CTexture>();
- CFramebuffer blurFB;
bool blurFBDirty = true;
bool blurFBShouldRender = false;
@@ -123,6 +138,9 @@ struct SCurrentRenderData {
uint32_t discardMode = DISCARD_OPAQUE;
float discardOpacity = 0.f;
+
+ PHLLSREF currentLS;
+ PHLWINDOWREF currentWindow;
};
class CEGLSync {
@@ -155,9 +173,9 @@ class CHyprOpenGLImpl {
void renderRect(CBox*, const CHyprColor&, int round = 0);
void renderRectWithBlur(CBox*, const CHyprColor&, int round = 0, float blurA = 1.f, bool xray = false);
- void renderRectWithDamage(CBox*, const CHyprColor&, CRegion* damage, int round = 0);
+ void renderRectWithDamage(CBox*, const CHyprColor&, const CRegion& damage, int round = 0);
void renderTexture(SP<CTexture>, CBox*, float a, int round = 0, bool discardActive = false, bool allowCustomUV = false);
- void renderTextureWithDamage(SP<CTexture>, CBox*, CRegion* damage, float a, int round = 0, bool discardActive = false, bool allowCustomUV = false,
+ void renderTextureWithDamage(SP<CTexture>, CBox*, const CRegion& damage, float a, int round = 0, bool discardActive = false, bool allowCustomUV = false,
SP<CSyncTimeline> waitTimeline = nullptr, uint64_t waitPoint = 0);
void renderTextureWithBlur(SP<CTexture>, CBox*, float a, SP<CWLSurfaceResource> pSurface, int round = 0, bool blockBlurOptimization = false, float blurA = 1.f);
void renderRoundedShadow(CBox*, int round, int range, const CHyprColor& color, float a = 1.0);
@@ -174,11 +192,6 @@ class CHyprOpenGLImpl {
void blend(bool enabled);
- void makeWindowSnapshot(PHLWINDOW);
- void makeRawWindowSnapshot(PHLWINDOW, CFramebuffer*);
- void makeLayerSnapshot(PHLLS);
- void renderSnapshot(PHLWINDOW);
- void renderSnapshot(PHLLS);
bool shouldUseNewBlurOptimizations(PHLLS pLayer, PHLWINDOW pWindow);
void clear(const CHyprColor&);
@@ -225,9 +238,6 @@ class CHyprOpenGLImpl {
bool m_bReloadScreenShader = true; // at launch it can be set
- PHLWINDOWREF m_pCurrentWindow; // hack to get the current rendered window
- PHLLS m_pCurrentLayer; // hack to get the current rendered layer
-
std::map<PHLWINDOWREF, CFramebuffer> m_mWindowFramebuffers;
std::map<PHLLSREF, CFramebuffer> m_mLayerFramebuffers;
std::map<PHLMONITORREF, SMonitorRenderData> m_mMonitorRenderResources;
@@ -300,7 +310,7 @@ class CHyprOpenGLImpl {
// returns the out FB, can be either Mirror or MirrorSwap
CFramebuffer* blurMainFramebufferWithDamage(float a, CRegion* damage);
- void renderTextureInternalWithDamage(SP<CTexture>, CBox* pBox, float a, CRegion* damage, int round = 0, bool discardOpaque = false, bool noAA = false,
+ void renderTextureInternalWithDamage(SP<CTexture>, CBox* pBox, float a, const CRegion& damage, int round = 0, bool discardOpaque = false, bool noAA = false,
bool allowCustomUV = false, bool allowDim = false, SP<CSyncTimeline> = nullptr, uint64_t waitPoint = 0);
void renderTexturePrimitive(SP<CTexture> tex, CBox* pBox);
void renderSplash(cairo_t* const, cairo_surface_t* const, double offset, const Vector2D& size);
@@ -310,6 +320,9 @@ class CHyprOpenGLImpl {
bool passRequiresIntrospection(PHLMONITOR pMonitor);
friend class CHyprRenderer;
+ friend class CTexPassElement;
+ friend class CPreBlurElement;
+ friend class CSurfacePassElement;
};
inline std::unique_ptr<CHyprOpenGLImpl> g_pHyprOpenGL;
diff --git a/src/render/Renderer.cpp b/src/render/Renderer.cpp
index c873842b..0f8db3bb 100644
--- a/src/render/Renderer.cpp
+++ b/src/render/Renderer.cpp
@@ -20,6 +20,10 @@
#include "../protocols/DRMSyncobj.hpp"
#include "../protocols/LinuxDMABUF.hpp"
#include "../helpers/sync/SyncTimeline.hpp"
+#include "pass/TexPassElement.hpp"
+#include "pass/ClearPassElement.hpp"
+#include "pass/RectPassElement.hpp"
+#include "pass/SurfacePassElement.hpp"
#include "debug/Log.hpp"
#include <hyprutils/utils/ScopeGuard.hpp>
@@ -154,148 +158,6 @@ CHyprRenderer::~CHyprRenderer() {
wl_event_source_remove(m_pCursorTicker);
}
-static void renderSurface(SP<CWLSurfaceResource> surface, int x, int y, void* data) {
- if (!surface->current.texture)
- return;
-
- const auto& TEXTURE = surface->current.texture;
-
- // this is bad, probably has been logged elsewhere. Means the texture failed
- // uploading to the GPU.
- if (!TEXTURE->m_iTexID)
- return;
-
- // explicit sync: wait for the timeline, if any
- if (surface->syncobj && surface->syncobj->current.acquireTimeline) {
- if (!g_pHyprOpenGL->waitForTimelinePoint(surface->syncobj->current.acquireTimeline->timeline, surface->syncobj->current.acquirePoint)) {
- Debug::log(ERR, "Renderer: failed to wait for explicit timeline");
- return;
- }
- }
-
- const auto RDATA = (SRenderData*)data;
- const auto INTERACTIVERESIZEINPROGRESS = RDATA->pWindow && g_pInputManager->currentlyDraggedWindow && g_pInputManager->dragMode == MBIND_RESIZE;
- TRACY_GPU_ZONE("RenderSurface");
-
- double outputX = -RDATA->pMonitor->vecPosition.x, outputY = -RDATA->pMonitor->vecPosition.y;
-
- auto PSURFACE = CWLSurface::fromResource(surface);
-
- const float ALPHA = RDATA->alpha * RDATA->fadeAlpha * (PSURFACE ? PSURFACE->m_pAlphaModifier : 1.F);
- const bool BLUR = RDATA->blur && (!TEXTURE->m_bOpaque || ALPHA < 1.F);
-
- CBox windowBox;
- if (RDATA->surface && surface == RDATA->surface) {
- windowBox = {(int)outputX + RDATA->x + x, (int)outputY + RDATA->y + y, RDATA->w, RDATA->h};
-
- // however, if surface buffer w / h < box, we need to adjust them
- const auto PWINDOW = PSURFACE ? PSURFACE->getWindow() : nullptr;
-
- // center the surface if it's smaller than the viewport we assign it
- if (PSURFACE && !PSURFACE->m_bFillIgnoreSmall && PSURFACE->small() /* guarantees PWINDOW */) {
- const auto CORRECT = PSURFACE->correctSmallVec();
- const auto SIZE = PSURFACE->getViewporterCorrectedSize();
-
- if (!INTERACTIVERESIZEINPROGRESS) {
- windowBox.translate(CORRECT);
-
- windowBox.width = SIZE.x * (PWINDOW->m_vRealSize.value().x / PWINDOW->m_vReportedSize.x);
- windowBox.height = SIZE.y * (PWINDOW->m_vRealSize.value().y / PWINDOW->m_vReportedSize.y);
- } else {
- windowBox.width = SIZE.x;
- windowBox.height = SIZE.y;
- }
- }
-
- } else { // here we clamp to 2, these might be some tiny specks
- windowBox = {(int)outputX + RDATA->x + x, (int)outputY + RDATA->y + y, std::max((float)surface->current.size.x, 2.F), std::max((float)surface->current.size.y, 2.F)};
- if (RDATA->pWindow && RDATA->pWindow->m_vRealSize.isBeingAnimated() && RDATA->surface && RDATA->surface != surface && RDATA->squishOversized /* subsurface */) {
- // adjust subsurfaces to the window
- windowBox.width = (windowBox.width / RDATA->pWindow->m_vReportedSize.x) * RDATA->pWindow->m_vRealSize.value().x;
- windowBox.height = (windowBox.height / RDATA->pWindow->m_vReportedSize.y) * RDATA->pWindow->m_vRealSize.value().y;
- }
- }
-
- if (RDATA->squishOversized) {
- if (x + windowBox.width > RDATA->w)
- windowBox.width = RDATA->w - x;
- if (y + windowBox.height > RDATA->h)
- windowBox.height = RDATA->h - y;
- }
-
- const auto PROJSIZEUNSCALED = windowBox.size();
-
- windowBox.scale(RDATA->pMonitor->scale);
- windowBox.round();
-
- if (windowBox.width <= 1 || windowBox.height <= 1) {
- if (!g_pHyprRenderer->m_bBlockSurfaceFeedback) {
- Debug::log(TRACE, "presentFeedback for invisible surface");
- surface->presentFeedback(RDATA->when, RDATA->pMonitor->self.lock());
- }
-
- return; // invisible
- }
-
- const bool MISALIGNEDFSV1 = std::floor(RDATA->pMonitor->scale) != RDATA->pMonitor->scale /* Fractional */ && surface->current.scale == 1 /* fs protocol */ &&
- windowBox.size() != surface->current.bufferSize /* misaligned */ && DELTALESSTHAN(windowBox.width, surface->current.bufferSize.x, 3) &&
- DELTALESSTHAN(windowBox.height, surface->current.bufferSize.y, 3) /* off by one-or-two */ &&
- (!RDATA->pWindow || (!RDATA->pWindow->m_vRealSize.isBeingAnimated() && !INTERACTIVERESIZEINPROGRESS)) /* not window or not animated/resizing */;
-
- g_pHyprRenderer->calculateUVForSurface(RDATA->pWindow, surface, RDATA->pMonitor->self.lock(), RDATA->surface == surface, windowBox.size(), PROJSIZEUNSCALED, MISALIGNEDFSV1);
-
- // 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.
- // UV will fixup it as well
- const auto NEARESTNEIGHBORSET = g_pHyprOpenGL->m_RenderData.useNearestNeighbor;
- if (MISALIGNEDFSV1)
- g_pHyprOpenGL->m_RenderData.useNearestNeighbor = true;
-
- float rounding = RDATA->rounding;
-
- rounding -= 1; // to fix a border issue
-
- if (RDATA->dontRound)
- rounding = 0;
-
- const bool WINDOWOPAQUE = RDATA->pWindow && RDATA->pWindow->m_pWLSurface->resource() == surface ? RDATA->pWindow->opaque() : false;
- const bool CANDISABLEBLEND = ALPHA >= 1.f && rounding == 0 && WINDOWOPAQUE;
-
- if (CANDISABLEBLEND)
- g_pHyprOpenGL->blend(false);
- else
- g_pHyprOpenGL->blend(true);
-
- // FIXME: This is wrong and will bug the blur out as shit if the first surface
- // is a subsurface that does NOT cover the entire frame. In such cases, we probably should fall back
- // to what we do for misaligned surfaces (blur the entire thing and then render shit without blur)
- if (RDATA->surfaceCounter == 0 && !RDATA->popup) {
- if (BLUR)
- g_pHyprOpenGL->renderTextureWithBlur(TEXTURE, &windowBox, ALPHA, surface, rounding, RDATA->blockBlurOptimization, RDATA->fadeAlpha);
- else
- g_pHyprOpenGL->renderTexture(TEXTURE, &windowBox, ALPHA, rounding, false, true);
- } else {
- if (BLUR && RDATA->popup)
- g_pHyprOpenGL->renderTextureWithBlur(TEXTURE, &windowBox, ALPHA, surface, rounding, true, RDATA->fadeAlpha);
- else
- g_pHyprOpenGL->renderTexture(TEXTURE, &windowBox, ALPHA, rounding, false, true);
- }
-
- if (!g_pHyprRenderer->m_bBlockSurfaceFeedback)
- surface->presentFeedback(RDATA->when, RDATA->pMonitor->self.lock());
-
- g_pHyprOpenGL->blend(true);
-
- // reset props
- g_pHyprOpenGL->m_RenderData.primarySurfaceUVTopLeft = Vector2D(-1, -1);
- g_pHyprOpenGL->m_RenderData.primarySurfaceUVBottomRight = Vector2D(-1, -1);
- g_pHyprOpenGL->m_RenderData.useNearestNeighbor = NEARESTNEIGHBORSET;
-
- // up the counter so that we dont blur any surfaces above this one
- RDATA->surfaceCounter++;
-}
-
bool CHyprRenderer::shouldRenderWindow(PHLWINDOW pWindow, PHLMONITOR pMonitor) {
if (!pWindow->visibleOnMonitor(pMonitor))
return false;
@@ -495,7 +357,7 @@ void CHyprRenderer::renderWorkspaceWindows(PHLMONITOR pMonitor, PHLWORKSPACE pWo
if (!shouldRenderWindow(w, pMonitor))
continue;
- windows.push_back(w);
+ windows.emplace_back(w);
}
// Non-floating main
@@ -560,7 +422,7 @@ void CHyprRenderer::renderWindow(PHLWINDOW pWindow, PHLMONITOR pMonitor, timespe
if (pWindow->m_bFadingOut) {
if (pMonitor == pWindow->m_pMonitor) // TODO: fix this
- g_pHyprOpenGL->renderSnapshot(pWindow);
+ renderSnapshot(pWindow);
return;
}
@@ -569,22 +431,22 @@ void CHyprRenderer::renderWindow(PHLWINDOW pWindow, PHLMONITOR pMonitor, timespe
TRACY_GPU_ZONE("RenderWindow");
- const auto PWORKSPACE = pWindow->m_pWorkspace;
- const auto REALPOS = pWindow->m_vRealPosition.value() + (pWindow->m_bPinned ? Vector2D{} : PWORKSPACE->m_vRenderOffset.value());
- static auto PDIMAROUND = CConfigValue<Hyprlang::FLOAT>("decoration:dim_around");
- static auto PBLUR = CConfigValue<Hyprlang::INT>("decoration:blur:enabled");
+ const auto PWORKSPACE = pWindow->m_pWorkspace;
+ const auto REALPOS = pWindow->m_vRealPosition.value() + (pWindow->m_bPinned ? Vector2D{} : PWORKSPACE->m_vRenderOffset.value());
+ static auto PDIMAROUND = CConfigValue<Hyprlang::FLOAT>("decoration:dim_around");
+ static auto PBLUR = CConfigValue<Hyprlang::INT>("decoration:blur:enabled");
- SRenderData renderdata = {pMonitor, time};
- CBox textureBox = {REALPOS.x, REALPOS.y, std::max(pWindow->m_vRealSize.value().x, 5.0), std::max(pWindow->m_vRealSize.value().y, 5.0)};
+ CSurfacePassElement::SRenderData renderdata = {pMonitor, time};
+ CBox textureBox = {REALPOS.x, REALPOS.y, std::max(pWindow->m_vRealSize.value().x, 5.0), std::max(pWindow->m_vRealSize.value().y, 5.0)};
- renderdata.x = textureBox.x;
- renderdata.y = textureBox.y;
- renderdata.w = textureBox.w;
- renderdata.h = textureBox.h;
+ renderdata.pos.x = textureBox.x;
+ renderdata.pos.y = textureBox.y;
+ renderdata.w = textureBox.w;
+ renderdata.h = textureBox.h;
if (ignorePosition) {
- renderdata.x = pMonitor->vecPosition.x;
- renderdata.y = pMonitor->vecPosition.y;
+ renderdata.pos.x = pMonitor->vecPosition.x;
+ renderdata.pos.y = pMonitor->vecPosition.y;
}
if (ignoreAllGeometry)
@@ -613,23 +475,26 @@ void CHyprRenderer::renderWindow(PHLWINDOW pWindow, PHLMONITOR pMonitor, timespe
if (pWindow->m_sWindowData.opaque.valueOrDefault())
renderdata.alpha = 1.f;
- g_pHyprOpenGL->m_pCurrentWindow = pWindow;
+ renderdata.pWindow = pWindow;
EMIT_HOOK_EVENT("render", RENDER_PRE_WINDOW);
if (*PDIMAROUND && pWindow->m_sWindowData.dimAround.valueOrDefault() && !m_bRenderingSnapshot && mode != RENDER_PASS_POPUP) {
- CBox monbox = {0, 0, g_pHyprOpenGL->m_RenderData.pMonitor->vecTransformedSize.x, g_pHyprOpenGL->m_RenderData.pMonitor->vecTransformedSize.y};
- g_pHyprOpenGL->renderRect(&monbox, CHyprColor(0, 0, 0, *PDIMAROUND * renderdata.alpha * renderdata.fadeAlpha));
+ CBox monbox = {0, 0, g_pHyprOpenGL->m_RenderData.pMonitor->vecTransformedSize.x, g_pHyprOpenGL->m_RenderData.pMonitor->vecTransformedSize.y};
+ CRectPassElement::SRectData data;
+ data.color = CHyprColor(0, 0, 0, *PDIMAROUND * renderdata.alpha * renderdata.fadeAlpha);
+ data.box = monbox;
+ m_sRenderPass.add(makeShared<CRectPassElement>(data));
}
- renderdata.x += pWindow->m_vFloatingOffset.x;
- renderdata.y += pWindow->m_vFloatingOffset.y;
+ renderdata.pos.x += pWindow->m_vFloatingOffset.x;
+ renderdata.pos.y += pWindow->m_vFloatingOffset.y;
// if window is floating and we have a slide animation, clip it to its full bb
if (!ignorePosition && pWindow->m_bIsFloating && !pWindow->isFullscreen() && PWORKSPACE->m_vRenderOffset.isBeingAnimated() && !pWindow->m_bPinned) {
CRegion rg =
pWindow->getFullWindowBoundingBox().translate(-pMonitor->vecPosition + PWORKSPACE->m_vRenderOffset.value() + pWindow->m_vFloatingOffset).scale(pMonitor->scale);
- g_pHyprOpenGL->m_RenderData.clipBox = rg.getExtents();
+ renderdata.clipBox = rg.getExtents();
}
// render window decorations first, if not fullscreen full
@@ -663,19 +528,32 @@ void CHyprRenderer::renderWindow(PHLWINDOW pWindow, PHLMONITOR pMonitor, timespe
static auto PXWLUSENN = CConfigValue<Hyprlang::INT>("xwayland:use_nearest_neighbor");
if ((pWindow->m_bIsX11 && *PXWLUSENN) || pWindow->m_sWindowData.nearestNeighbor.valueOrDefault())
- g_pHyprOpenGL->m_RenderData.useNearestNeighbor = true;
+ renderdata.useNearestNeighbor = true;
if (!pWindow->m_sWindowData.noBlur.valueOrDefault() && pWindow->m_pWLSurface->small() && !pWindow->m_pWLSurface->m_bFillIgnoreSmall && renderdata.blur && *PBLUR) {
- CBox wb = {renderdata.x - pMonitor->vecPosition.x, renderdata.y - pMonitor->vecPosition.y, renderdata.w, renderdata.h};
+ CBox wb = {renderdata.pos.x - pMonitor->vecPosition.x, renderdata.pos.y - pMonitor->vecPosition.y, renderdata.w, renderdata.h};
wb.scale(pMonitor->scale).round();
- g_pHyprOpenGL->renderRectWithBlur(&wb, CHyprColor(0, 0, 0, 0), renderdata.dontRound ? 0 : renderdata.rounding - 1, renderdata.fadeAlpha,
- g_pHyprOpenGL->shouldUseNewBlurOptimizations(nullptr, pWindow));
+ CRectPassElement::SRectData data;
+ data.color = CHyprColor(0, 0, 0, 0);
+ data.box = wb;
+ data.round = renderdata.dontRound ? 0 : renderdata.rounding - 1;
+ data.blurA = renderdata.fadeAlpha;
+ data.xray = g_pHyprOpenGL->shouldUseNewBlurOptimizations(nullptr, pWindow);
+ m_sRenderPass.add(makeShared<CRectPassElement>(data));
renderdata.blur = false;
}
renderdata.surfaceCounter = 0;
- pWindow->m_pWLSurface->resource()->breadthfirst([](SP<CWLSurfaceResource> s, const Vector2D& offset, void* data) { renderSurface(s, offset.x, offset.y, data); },
- &renderdata);
+ pWindow->m_pWLSurface->resource()->breadthfirst(
+ [this, &renderdata, &pWindow](SP<CWLSurfaceResource> s, const Vector2D& offset, void* data) {
+ renderdata.localPos = offset;
+ renderdata.texture = s->current.texture;
+ renderdata.surface = s;
+ renderdata.mainSurface = s == pWindow->m_pWLSurface->resource();
+ m_sRenderPass.add(makeShared<CSurfacePassElement>(renderdata));
+ renderdata.surfaceCounter++;
+ },
+ nullptr);
g_pHyprOpenGL->m_RenderData.useNearestNeighbor = false;
@@ -689,7 +567,6 @@ void CHyprRenderer::renderWindow(PHLWINDOW pWindow, PHLMONITOR pMonitor, timespe
}
if (TRANSFORMERSPRESENT) {
-
CFramebuffer* last = g_pHyprOpenGL->m_RenderData.currentFB;
for (auto const& t : pWindow->m_vTransformers) {
last = t->transform(last);
@@ -706,9 +583,7 @@ void CHyprRenderer::renderWindow(PHLWINDOW pWindow, PHLMONITOR pMonitor, timespe
if (!pWindow->m_bIsX11) {
CBox geom = pWindow->m_pXDGSurface->current.geometry;
- renderdata.x -= geom.x;
- renderdata.y -= geom.y;
-
+ renderdata.pos -= geom.pos();
renderdata.dontRound = true; // don't round popups
renderdata.pMonitor = pMonitor;
renderdata.squishOversized = false; // don't squish popups
@@ -719,41 +594,38 @@ void CHyprRenderer::renderWindow(PHLWINDOW pWindow, PHLMONITOR pMonitor, timespe
renderdata.blur = *PBLURPOPUPS && *PBLUR;
- const auto DM = g_pHyprOpenGL->m_RenderData.discardMode;
- const auto DA = g_pHyprOpenGL->m_RenderData.discardOpacity;
-
if (renderdata.blur) {
- g_pHyprOpenGL->m_RenderData.discardMode |= DISCARD_ALPHA;
- g_pHyprOpenGL->m_RenderData.discardOpacity = *PBLURIGNOREA;
+ renderdata.discardMode |= DISCARD_ALPHA;
+ renderdata.discardOpacity = *PBLURIGNOREA;
}
if (pWindow->m_sWindowData.nearestNeighbor.valueOrDefault())
- g_pHyprOpenGL->m_RenderData.useNearestNeighbor = true;
+ renderdata.useNearestNeighbor = true;
renderdata.surfaceCounter = 0;
pWindow->m_pPopupHead->breadthfirst(
- [](CPopup* popup, void* data) {
+ [this, &renderdata](CPopup* popup, void* data) {
if (!popup->m_pWLSurface || !popup->m_pWLSurface->resource() || !popup->m_bMapped)
return;
const auto pos = popup->coordsRelativeToParent();
- auto rd = (SRenderData*)data;
- const Vector2D oldPos = {rd->x, rd->y};
- rd->x += pos.x;
- rd->y += pos.y;
-
- popup->m_pWLSurface->resource()->breadthfirst([](SP<CWLSurfaceResource> s, const Vector2D& offset, void* data) { renderSurface(s, offset.x, offset.y, data); },
- data);
-
- rd->x = oldPos.x;
- rd->y = oldPos.y;
+ const Vector2D oldPos = renderdata.pos;
+ renderdata.pos += pos;
+
+ popup->m_pWLSurface->resource()->breadthfirst(
+ [this, &renderdata](SP<CWLSurfaceResource> s, const Vector2D& offset, void* data) {
+ renderdata.localPos = offset;
+ renderdata.texture = s->current.texture;
+ renderdata.surface = s;
+ renderdata.mainSurface = false;
+ m_sRenderPass.add(makeShared<CSurfacePassElement>(renderdata));
+ renderdata.surfaceCounter++;
+ },
+ data);
+
+ renderdata.pos = oldPos;
},
&renderdata);
-
- g_pHyprOpenGL->m_RenderData.useNearestNeighbor = false;
-
- g_pHyprOpenGL->m_RenderData.discardMode = DM;
- g_pHyprOpenGL->m_RenderData.discardOpacity = DA;
}
if (decorate) {
@@ -767,9 +639,6 @@ void CHyprRenderer::renderWindow(PHLWINDOW pWindow, PHLMONITOR pMonitor, timespe
}
EMIT_HOOK_EVENT("render", RENDER_POST_WINDOW);
-
- g_pHyprOpenGL->m_pCurrentWindow.reset();
- g_pHyprOpenGL->m_RenderData.clipBox = CBox();
}
void CHyprRenderer::renderLayer(PHLLS pLayer, PHLMONITOR pMonitor, timespec* time, bool popups) {
@@ -779,13 +648,15 @@ void CHyprRenderer::renderLayer(PHLLS pLayer, PHLMONITOR pMonitor, timespec* tim
static auto PDIMAROUND = CConfigValue<Hyprlang::FLOAT>("decoration:dim_around");
if (*PDIMAROUND && pLayer->dimAround && !m_bRenderingSnapshot && !popups) {
- CBox monbox = {0, 0, g_pHyprOpenGL->m_RenderData.pMonitor->vecTransformedSize.x, g_pHyprOpenGL->m_RenderData.pMonitor->vecTransformedSize.y};
- g_pHyprOpenGL->renderRect(&monbox, CHyprColor(0, 0, 0, *PDIMAROUND * pLayer->alpha.value()));
+ CRectPassElement::SRectData data;
+ data.box = {0, 0, g_pHyprOpenGL->m_RenderData.pMonitor->vecTransformedSize.x, g_pHyprOpenGL->m_RenderData.pMonitor->vecTransformedSize.y};
+ data.color = CHyprColor(0, 0, 0, *PDIMAROUND * pLayer->alpha.value());
+ m_sRenderPass.add(makeShared<CRectPassElement>(data));
}
if (pLayer->fadingOut) {
if (!popups)
- g_pHyprOpenGL->renderSnapshot(pLayer);
+ renderSnapshot(pLayer);
return;
}
@@ -793,32 +664,37 @@ void CHyprRenderer::renderLayer(PHLLS pLayer, PHLMONITOR pMonitor, timespec* tim
TRACY_GPU_ZONE("RenderLayer");
- const auto REALPOS = pLayer->realPosition.value();
- const auto REALSIZ = pLayer->realSize.value();
-
- SRenderData renderdata = {pMonitor, time, REALPOS.x, REALPOS.y};
- renderdata.fadeAlpha = pLayer->alpha.value();
- renderdata.blur = pLayer->forceBlur && *PBLUR;
- renderdata.surface = pLayer->surface->resource();
- renderdata.decorate = false;
- renderdata.w = REALSIZ.x;
- renderdata.h = REALSIZ.y;
- renderdata.blockBlurOptimization = pLayer->layer == ZWLR_LAYER_SHELL_V1_LAYER_BOTTOM || pLayer->layer == ZWLR_LAYER_SHELL_V1_LAYER_BACKGROUND;
+ const auto REALPOS = pLayer->realPosition.value();
+ const auto REALSIZ = pLayer->realSize.value();
- g_pHyprOpenGL->m_RenderData.clipBox = CBox{0, 0, pMonitor->vecSize.x, pMonitor->vecSize.y}.scale(pMonitor->scale);
+ CSurfacePassElement::SRenderData renderdata = {pMonitor, time, REALPOS};
+ renderdata.fadeAlpha = pLayer->alpha.value();
+ renderdata.blur = pLayer->forceBlur && *PBLUR;
+ renderdata.surface = pLayer->surface->resource();
+ renderdata.decorate = false;
+ renderdata.w = REALSIZ.x;
+ renderdata.h = REALSIZ.y;
+ renderdata.pLS = pLayer;
+ renderdata.blockBlurOptimization = pLayer->layer == ZWLR_LAYER_SHELL_V1_LAYER_BOTTOM || pLayer->layer == ZWLR_LAYER_SHELL_V1_LAYER_BACKGROUND;
- g_pHyprOpenGL->m_pCurrentLayer = pLayer;
-
- const auto DM = g_pHyprOpenGL->m_RenderData.discardMode;
- const auto DA = g_pHyprOpenGL->m_RenderData.discardOpacity;
+ renderdata.clipBox = CBox{0, 0, pMonitor->vecSize.x, pMonitor->vecSize.y}.scale(pMonitor->scale);
if (renderdata.blur && pLayer->ignoreAlpha) {
- g_pHyprOpenGL->m_RenderData.discardMode |= DISCARD_ALPHA;
- g_pHyprOpenGL->m_RenderData.discardOpacity = pLayer->ignoreAlphaValue;
+ renderdata.discardMode |= DISCARD_ALPHA;
+ renderdata.discardOpacity = pLayer->ignoreAlphaValue;
}
if (!popups)
- pLayer->surface->resource()->breadthfirst([](SP<CWLSurfaceResource> s, const Vector2D& offset, void* data) { renderSurface(s, offset.x, offset.y, data); }, &renderdata);
+ pLayer->surface->resource()->breadthfirst(
+ [this, &renderdata, &pLayer](SP<CWLSurfaceResource> s, const Vector2D& offset, void* data) {
+ renderdata.localPos = offset;
+ renderdata.texture = s->current.texture;
+ renderdata.surface = s;
+ renderdata.mainSurface = s == pLayer->surface->resource();
+ m_sRenderPass.add(makeShared<CSurfacePassElement>(renderdata));
+ renderdata.surfaceCounter++;
+ },
+ &renderdata);
renderdata.squishOversized = false; // don't squish popups
renderdata.dontRound = true;
@@ -827,28 +703,28 @@ void CHyprRenderer::renderLayer(PHLLS pLayer, PHLMONITOR pMonitor, timespec* tim
renderdata.surfaceCounter = 0;
if (popups) {
pLayer->popupHead->breadthfirst(
- [](CPopup* popup, void* data) {
+ [this, &renderdata](CPopup* popup, void* data) {
if (!popup->m_pWLSurface || !popup->m_pWLSurface->resource() || !popup->m_bMapped)
return;
- Vector2D pos = popup->coordsRelativeToParent();
- renderSurface(popup->m_pWLSurface->resource(), pos.x, pos.y, data);
+ Vector2D pos = popup->coordsRelativeToParent();
+ renderdata.localPos = pos;
+ renderdata.texture = popup->m_pWLSurface->resource()->current.texture;
+ renderdata.surface = popup->m_pWLSurface->resource();
+ renderdata.mainSurface = false;
+ m_sRenderPass.add(makeShared<CSurfacePassElement>(renderdata));
+ renderdata.surfaceCounter++;
},
&renderdata);
}
-
- g_pHyprOpenGL->m_pCurrentLayer = nullptr;
- g_pHyprOpenGL->m_RenderData.clipBox = {};
- g_pHyprOpenGL->m_RenderData.discardMode = DM;
- g_pHyprOpenGL->m_RenderData.discardOpacity = DA;
}
void CHyprRenderer::renderIMEPopup(CInputPopup* pPopup, PHLMONITOR pMonitor, timespec* time) {
- const auto POS = pPopup->globalBox().pos();
+ const auto POS = pPopup->globalBox().pos();
- SRenderData renderdata = {pMonitor, time, POS.x, POS.y};
+ CSurfacePassElement::SRenderData renderdata = {pMonitor, time, POS};
- const auto SURF = pPopup->getSurface();
+ const auto SURF = pPopup->getSurface();
renderdata.surface = SURF;
renderdata.decorate = false;
@@ -859,24 +735,26 @@ void CHyprRenderer::renderIMEPopup(CInputPopup* pPopup, PHLMONITOR pMonitor, tim
static auto PBLURIMES = CConfigValue<Hyprlang::INT>("decoration:blur:input_methods");
static auto PBLURIGNOREA = CConfigValue<Hyprlang::FLOAT>("decoration:blur:input_methods_ignorealpha");
- // TODO: make push/pop methods for this.
- const auto DM = g_pHyprOpenGL->m_RenderData.discardMode;
- const auto DA = g_pHyprOpenGL->m_RenderData.discardOpacity;
-
renderdata.blur = *PBLURIMES && *PBLUR;
if (renderdata.blur) {
- g_pHyprOpenGL->m_RenderData.discardMode |= DISCARD_ALPHA;
- g_pHyprOpenGL->m_RenderData.discardOpacity = *PBLURIGNOREA;
- }
-
- SURF->breadthfirst([](SP<CWLSurfaceResource> s, const Vector2D& offset, void* data) { renderSurface(s, offset.x, offset.y, data); }, &renderdata);
-
- g_pHyprOpenGL->m_RenderData.discardMode = DM;
- g_pHyprOpenGL->m_RenderData.discardOpacity = DA;
+ renderdata.discardMode |= DISCARD_ALPHA;
+ renderdata.discardOpacity = *PBLURIGNOREA;
+ }
+
+ SURF->breadthfirst(
+ [this, &renderdata, &SURF](SP<CWLSurfaceResource> s, const Vector2D& offset, void* data) {
+ renderdata.localPos = offset;
+ renderdata.texture = s->current.texture;
+ renderdata.surface = s;
+ renderdata.mainSurface = s == SURF;
+ m_sRenderPass.add(makeShared<CSurfacePassElement>(renderdata));
+ renderdata.surfaceCounter++;
+ },
+ &renderdata);
}
void CHyprRenderer::renderSessionLockSurface(SSessionLockSurface* pSurface, PHLMONITOR pMonitor, timespec* time) {
- SRenderData renderdata = {pMonitor, time, pMonitor->vecPosition.x, pMonitor->vecPosition.y};
+ CSurfacePassElement::SRenderData renderdata = {pMonitor, time, pMonitor->vecPosition, pMonitor->vecPosition};
renderdata.blur = false;
renderdata.surface = pSurface->surface->surface();
@@ -884,7 +762,16 @@ void CHyprRenderer::renderSessionLockSurface(SSessionLockSurface* pSurface, PHLM
renderdata.w = pMonitor->vecSize.x;
renderdata.h = pMonitor->vecSize.y;
- renderdata.surface->breadthfirst([](SP<CWLSurfaceResource> s, const Vector2D& offset, void* data) { renderSurface(s, offset.x, offset.y, data); }, &renderdata);
+ renderdata.surface->breadthfirst(
+ [this, &renderdata, &pSurface](SP<CWLSurfaceResource> s, const Vector2D& offset, void* data) {
+ renderdata.localPos = offset;
+ renderdata.texture = s->current.texture;
+ renderdata.surface = s;
+ renderdata.mainSurface = s == pSurface->surface->surface();
+ m_sRenderPass.add(makeShared<CSurfacePassElement>(renderdata));
+ renderdata.surfaceCounter++;
+ },
+ &renderdata);
}
void CHyprRenderer::renderAllClientsForWorkspace(PHLMONITOR pMonitor, PHLWORKSPACE pWorkspace, timespec* time, const Vector2D& translate, const float& scale) {
@@ -893,6 +780,7 @@ void CHyprRenderer::renderAllClientsForWorkspace(PHLMONITOR pMonitor, PHLWORKSPA
static auto PBLUR = CConfigValue<Hyprlang::INT>("decoration:blur:enabled");
static auto PRENDERTEX = CConfigValue<Hyprlang::INT>("misc:disable_hyprland_logo");
static auto PBACKGROUNDCOLOR = CConfigValue<Hyprlang::INT>("misc:background_color");
+ static auto PXPMODE = CConfigValue<Hyprlang::INT>("render:xp_mode");
SRenderModifData RENDERMODIFDATA;
if (translate != Vector2D{0, 0})
@@ -916,14 +804,11 @@ void CHyprRenderer::renderAllClientsForWorkspace(PHLMONITOR pMonitor, PHLWORKSPA
if (!pWorkspace) {
// allow rendering without a workspace. In this case, just render layers.
- g_pHyprOpenGL->blend(false);
- if (!canSkipBackBufferClear(pMonitor)) {
- if (*PRENDERTEX /* inverted cfg flag */)
- g_pHyprOpenGL->clear(CHyprColor(*PBACKGROUNDCOLOR));
- else
- g_pHyprOpenGL->clearWithTex(); // will apply the hypr "wallpaper"
- }
- g_pHyprOpenGL->blend(true);
+
+ if (*PRENDERTEX /* inverted cfg flag */)
+ m_sRenderPass.add(makeShared<CClearPassElement>(CClearPassElement::SClearData{CHyprColor(*PBACKGROUNDCOLOR)}));
+ else
+ g_pHyprOpenGL->clearWithTex(); // will apply the hypr "wallpaper"
for (auto const& ls : pMonitor->m_aLayerSurfaceLayers[ZWLR_LAYER_SHELL_V1_LAYER_BACKGROUND]) {
renderLayer(ls.lock(), pMonitor, time);
@@ -943,27 +828,11 @@ void CHyprRenderer::renderAllClientsForWorkspace(PHLMONITOR pMonitor, PHLWORKSPA
return;
}
- // for storing damage when we optimize for occlusion
- CRegion preOccludedDamage{g_pHyprOpenGL->m_RenderData.damage};
-
- // Render layer surfaces below windows for monitor
- // if we have a fullscreen, opaque window that convers the screen, we can skip this.
- // TODO: check better with solitary after MR for tearing.
- const auto PFULLWINDOW = pWorkspace ? pWorkspace->getFullscreenWindow() : nullptr;
- if (!pWorkspace->m_bHasFullscreenWindow || pWorkspace->m_efFullscreenMode != FSMODE_FULLSCREEN || !PFULLWINDOW || PFULLWINDOW->m_vRealSize.isBeingAnimated() ||
- !PFULLWINDOW->opaque() || pWorkspace->m_vRenderOffset.value() != Vector2D{} || g_pHyprOpenGL->preBlurQueued()) {
-
- if (!g_pHyprOpenGL->m_RenderData.pCurrentMonData->blurFBShouldRender)
- setOccludedForBackLayers(g_pHyprOpenGL->m_RenderData.damage, pWorkspace);
-
- g_pHyprOpenGL->blend(false);
- if (!canSkipBackBufferClear(pMonitor)) {
- if (*PRENDERTEX /* inverted cfg flag */)
- g_pHyprOpenGL->clear(CHyprColor(*PBACKGROUNDCOLOR));
- else
- g_pHyprOpenGL->clearWithTex(); // will apply the hypr "wallpaper"
- }
- g_pHyprOpenGL->blend(true);
+ if (!*PXPMODE) {
+ if (*PRENDERTEX /* inverted cfg flag */)
+ m_sRenderPass.add(makeShared<CClearPassElement>(CClearPassElement::SClearData{CHyprColor(*PBACKGROUNDCOLOR)}));
+ else
+ g_pHyprOpenGL->clearWithTex(); // will apply the hypr "wallpaper"
for (auto const& ls : pMonitor->m_aLayerSurfaceLayers[ZWLR_LAYER_SHELL_V1_LAYER_BACKGROUND]) {
renderLayer(ls.lock(), pMonitor, time);
@@ -971,22 +840,16 @@ void CHyprRenderer::renderAllClientsForWorkspace(PHLMONITOR pMonitor, PHLWORKSPA
for (auto const& ls : pMonitor->m_aLayerSurfaceLayers[ZWLR_LAYER_SHELL_V1_LAYER_BOTTOM]) {
renderLayer(ls.lock(), pMonitor, time);
}
-
- g_pHyprOpenGL->m_RenderData.damage = preOccludedDamage;
}
// pre window pass
g_pHyprOpenGL->preWindowPass();
- setOccludedForMainWorkspace(g_pHyprOpenGL->m_RenderData.damage, pWorkspace);
-
if (pWorkspace->m_bHasFullscreenWindow)
renderWorkspaceWindowsFullscreen(pMonitor, pWorkspace, time);
else
renderWorkspaceWindows(pMonitor, pWorkspace, time);
- g_pHyprOpenGL->m_RenderData.damage = preOccludedDamage;
-
// and then special
for (auto const& ws : g_pCompositor->m_vWorkspaces) {
if (ws->m_pMonitor == pMonitor && ws->m_fAlpha.value() > 0.f && ws->m_bIsSpecialWorkspace) {
@@ -994,13 +857,21 @@ void CHyprRenderer::renderAllClientsForWorkspace(PHLMONITOR pMonitor, PHLWORKSPA
const bool ANIMOUT = !pMonitor->activeSpecialWorkspace;
if (*PDIMSPECIAL != 0.f) {
- CBox monbox = {translate.x, translate.y, pMonitor->vecTransformedSize.x * scale, pMonitor->vecTransformedSize.y * scale};
- g_pHyprOpenGL->renderRect(&monbox, CHyprColor(0, 0, 0, *PDIMSPECIAL * (ANIMOUT ? (1.0 - SPECIALANIMPROGRS) : SPECIALANIMPROGRS)));
+ CRectPassElement::SRectData data;
+ data.box = {translate.x, translate.y, pMonitor->vecTransformedSize.x * scale, pMonitor->vecTransformedSize.y * scale};
+ data.color = CHyprColor(0, 0, 0, *PDIMSPECIAL * (ANIMOUT ? (1.0 - SPECIALANIMPROGRS) : SPECIALANIMPROGRS));
+
+ g_pHyprRenderer->m_sRenderPass.add(makeShared<CRectPassElement>(data));
}
if (*PBLURSPECIAL && *PBLUR) {
- CBox monbox = {translate.x, translate.y, pMonitor->vecTransformedSize.x * scale, pMonitor->vecTransformedSize.y * scale};
- g_pHyprOpenGL->renderRectWithBlur(&monbox, CHyprColor(0, 0, 0, 0), 0, (ANIMOUT ? (1.0 - SPECIALANIMPROGRS) : SPECIALANIMPROGRS));
+ CRectPassElement::SRectData data;
+ data.box = {translate.x, translate.y, pMonitor->vecTransformedSize.x * scale, pMonitor->vecTransformedSize.y * scale};
+ data.color = CHyprColor(0, 0, 0, 0);
+ data.blur = true;
+ data.blurA = (ANIMOUT ? (1.0 - SPECIALANIMPROGRS) : SPECIALANIMPROGRS);
+
+ g_pHyprRenderer->m_sRenderPass.add(makeShared<CRectPassElement>(data));
}
break;
@@ -1381,30 +1252,10 @@ void CHyprRenderer::renderMonitor(PHLMONITOR pMonitor) {
}
// if we have no tracking or full tracking, invalidate the entire monitor
- if (*PDAMAGETRACKINGMODE == DAMAGE_TRACKING_NONE || *PDAMAGETRACKINGMODE == DAMAGE_TRACKING_MONITOR || pMonitor->forceFullFrames > 0 || damageBlinkCleanup > 0) {
- damage = {0, 0, (int)pMonitor->vecTransformedSize.x * 10, (int)pMonitor->vecTransformedSize.y * 10};
- finalDamage = damage;
- } else {
- static auto PBLURENABLED = CConfigValue<Hyprlang::INT>("decoration:blur:enabled");
-
- // if we use blur we need to expand the damage for proper blurring
- // if framebuffer was not offloaded we're not doing introspection aka not blurring so this is redundant and dumb
- if (*PBLURENABLED == 1 && g_pHyprOpenGL->m_bOffloadedFramebuffer) {
- // TODO: can this be optimized?
- static auto PBLURSIZE = CConfigValue<Hyprlang::INT>("decoration:blur:size");
- static auto PBLURPASSES = CConfigValue<Hyprlang::INT>("decoration:blur:passes");
- const auto BLURRADIUS =
- *PBLURPASSES > 10 ? pow(2, 15) : std::clamp(*PBLURSIZE, (int64_t)1, (int64_t)40) * pow(2, *PBLURPASSES); // is this 2^pass? I don't know but it works... I think.
-
- // now, prep the damage, get the extended damage region
- damage.expand(BLURRADIUS); // expand for proper blurring
+ if (*PDAMAGETRACKINGMODE == DAMAGE_TRACKING_NONE || *PDAMAGETRACKINGMODE == DAMAGE_TRACKING_MONITOR || pMonitor->forceFullFrames > 0 || damageBlinkCleanup > 0)
+ damage = {0, 0, (int)pMonitor->vecTransformedSize.x * 10, (int)pMonitor->vecTransformedSize.y * 10};
- finalDamage = damage;
-
- damage.expand(BLURRADIUS); // expand for proper blurring
- } else
- finalDamage = damage;
- }
+ finalDamage = damage;
// update damage in renderdata as we modified it
g_pHyprOpenGL->setDamage(damage, finalDamage);
@@ -1446,8 +1297,10 @@ void CHyprRenderer::renderMonitor(PHLMONITOR pMonitor) {
}
if (*PDAMAGEBLINK && damageBlinkCleanup == 0) {
- CBox monrect = {0, 0, pMonitor->vecTransformedSize.x, pMonitor->vecTransformedSize.y};
- g_pHyprOpenGL->renderRect(&monrect, CHyprColor(1.0, 0.0, 1.0, 100.0 / 255.0), 0);
+ CRectPassElement::SRectData data;
+ data.box = {0, 0, pMonitor->vecTransformedSize.x, pMonitor->vecTransformedSize.y};
+ data.color = CHyprColor(1.0, 0.0, 1.0, 100.0 / 255.0);
+ m_sRenderPass.add(makeShared<CRectPassElement>(data));
damageBlinkCleanup = 1;
} else if (*PDAMAGEBLINK) {
damageBlinkCleanup++;
@@ -1456,7 +1309,7 @@ void CHyprRenderer::renderMonitor(PHLMONITOR pMonitor) {
}
}
} else
- g_pHyprRenderer->renderWindow(pMonitor->solitaryClient.lock(), pMonitor, &now, false, RENDER_PASS_MAIN /* solitary = no popups */);
+ renderWindow(pMonitor->solitaryClient.lock(), pMonitor, &now, false, RENDER_PASS_MAIN /* solitary = no popups */);
} else if (!pMonitor->isMirror()) {
sendFrameEventsToWorkspace(pMonitor, pMonitor->activeWorkspace, &now);
if (pMonitor->activeSpecialWorkspace)
@@ -1474,6 +1327,8 @@ void CHyprRenderer::renderMonitor(PHLMONITOR pMonitor) {
endRender();
+ finalDamage = g_pHyprOpenGL->m_RenderData.damage;
+
TRACY_GPU_COLLECT;
if (!pMonitor->mirrors.empty()) {
@@ -1488,7 +1343,7 @@ void CHyprRenderer::renderMonitor(PHLMONITOR pMonitor) {
if (*PDAMAGEBLINK)
frameDamage.add(damage);
- g_pHyprRenderer->damageMirrorsWith(pMonitor, frameDamage);
+ damageMirrorsWith(pMonitor, frameDamage);
pMonitor->output->state->addDamage(frameDamage);
}
@@ -1517,9 +1372,8 @@ void CHyprRenderer::renderMonitor(PHLMONITOR pMonitor) {
if (pMonitor == g_pCompositor->m_vMonitors.front()) {
const float noOverlayUs = durationUs - std::chrono::duration_cast<std::chrono::nanoseconds>(endRenderOverlay - renderStartOverlay).count() / 1000.f;
g_pDebugOverlay->renderDataNoOverlay(pMonitor, noOverlayUs);
- } else {
+ } else
g_pDebugOverlay->renderDataNoOverlay(pMonitor, durationUs);
- }
}
}
@@ -1883,7 +1737,7 @@ void CHyprRenderer::damageWindow(PHLWINDOW pWindow, bool forceFull) {
windowBox.translate(pWindow->m_vFloatingOffset);
for (auto const& m : g_pCompositor->m_vMonitors) {
- if (forceFull || g_pHyprRenderer->shouldRenderWindow(pWindow, m)) { // only damage if window is rendered on monitor
+ if (forceFull || shouldRenderWindow(pWindow, m)) { // only damage if window is rendered on monitor
CBox fixedDamageBox = {windowBox.x - m->vecPosition.x, windowBox.y - m->vecPosition.y, windowBox.width, windowBox.height};
fixedDamageBox.scale(m->scale);
m->addDamage(&fixedDamageBox);
@@ -2443,7 +2297,7 @@ void CHyprRenderer::ensureCursorRenderingMode() {
if (!g_pPointerManager->softwareLockedFor(m))
continue;
- g_pHyprRenderer->damageMonitor(m); // TODO: maybe just damage the cursor area?
+ damageMonitor(m); // TODO: maybe just damage the cursor area?
}
setCursorHidden(true);
@@ -2455,7 +2309,7 @@ void CHyprRenderer::ensureCursorRenderingMode() {
if (!g_pPointerManager->softwareLockedFor(m))
continue;
- g_pHyprRenderer->damageMonitor(m); // TODO: maybe just damage the cursor area?
+ damageMonitor(m); // TODO: maybe just damage the cursor area?
}
setCursorHidden(false);
@@ -2535,94 +2389,6 @@ void CHyprRenderer::initiateManualCrash() {
**PDT = 0;
}
-void CHyprRenderer::setOccludedForMainWorkspace(CRegion& region, PHLWORKSPACE pWorkspace) {
- CRegion rg;
-
- const auto PMONITOR = pWorkspace->m_pMonitor.lock();
-
- if (!PMONITOR->activeSpecialWorkspace)
- return;
-
- for (auto const& w : g_pCompositor->m_vWindows) {
- if (!w->m_bIsMapped || w->isHidden() || w->m_pWorkspace != PMONITOR->activeSpecialWorkspace)
- continue;
-
- if (!w->opaque())
- continue;
-
- const auto ROUNDING = w->rounding() * PMONITOR->scale;
- const Vector2D POS = w->m_vRealPosition.value() + Vector2D{ROUNDING, ROUNDING} - PMONITOR->vecPosition + (w->m_bPinned ? Vector2D{} : pWorkspace->m_vRenderOffset.value());
- const Vector2D SIZE = w->m_vRealSize.value() - Vector2D{ROUNDING * 2, ROUNDING * 2};
-
- CBox box = {POS.x, POS.y, SIZE.x, SIZE.y};
-
- box.scale(PMONITOR->scale);
-
- rg.add(box);
- }
-
- region.subtract(rg);
-}
-
-void CHyprRenderer::setOccludedForBackLayers(CRegion& region, PHLWORKSPACE pWorkspace) {
- CRegion rg;
-
- const auto PMONITOR = pWorkspace->m_pMonitor.lock();
-
- static auto PBLUR = CConfigValue<Hyprlang::INT>("decoration:blur:enabled");
- static auto PBLURSIZE = CConfigValue<Hyprlang::INT>("decoration:blur:size");
- static auto PBLURPASSES = CConfigValue<Hyprlang::INT>("decoration:blur:passes");
- const auto BLURRADIUS = *PBLUR ? (*PBLURPASSES > 10 ? pow(2, 15) : std::clamp(*PBLURSIZE, (int64_t)1, (int64_t)40) * pow(2, *PBLURPASSES)) : 0;
-
- for (auto const& w : g_pCompositor->m_vWindows) {
- if (!w->m_bIsMapped || w->isHidden() || w->m_pWorkspace != pWorkspace)
- continue;
-
- if (!w->opaque())
- continue;
-
- const auto ROUNDING = w->rounding() * PMONITOR->scale;
- const Vector2D POS = w->m_vRealPosition.value() + Vector2D{ROUNDING, ROUNDING} - PMONITOR->vecPosition + (w->m_bPinned ? Vector2D{} : pWorkspace->m_vRenderOffset.value());
- const Vector2D SIZE = w->m_vRealSize.value() - Vector2D{ROUNDING * 2, ROUNDING * 2};
-
- CBox box = {POS.x, POS.y, SIZE.x, SIZE.y};
-
- box.scale(PMONITOR->scale).expand(-BLURRADIUS);
-
- g_pHyprOpenGL->m_RenderData.renderModif.applyToBox(box);
-
- rg.add(box);
- }
-
- region.subtract(rg);
-}
-
-bool CHyprRenderer::canSkipBackBufferClear(PHLMONITOR pMonitor) {
- for (auto const& ls : pMonitor->m_aLayerSurfaceLayers[ZWLR_LAYER_SHELL_V1_LAYER_BACKGROUND]) {
- if (!ls->layerSurface)
- continue;
-
- if (ls->alpha.value() < 1.f)
- continue;
-
- if (ls->geometry.x != pMonitor->vecPosition.x || ls->geometry.y != pMonitor->vecPosition.y || ls->geometry.width != pMonitor->vecSize.x ||
- ls->geometry.height != pMonitor->vecSize.y)
- continue;
-
- // TODO: cache maybe?
- CRegion opaque = ls->layerSurface->surface->current.opaque;
- CBox lsbox = {{}, ls->layerSurface->surface->current.size};
- opaque.invert(lsbox);
-
- if (!opaque.empty())
- continue;
-
- return true;
- }
-
- return false;
-}
-
void CHyprRenderer::recheckSolitaryForMonitor(PHLMONITOR pMonitor) {
pMonitor->solitaryClient.reset(); // reset it, if we find one it will be set.
@@ -2668,11 +2434,10 @@ void CHyprRenderer::recheckSolitaryForMonitor(PHLMONITOR pMonitor) {
// check if it did not open any subsurfaces or shit
int surfaceCount = 0;
- if (PCANDIDATE->m_bIsX11) {
+ if (PCANDIDATE->m_bIsX11)
surfaceCount = 1;
- } else {
+ else
surfaceCount = PCANDIDATE->popupsCount() + PCANDIDATE->surfacesCount();
- }
if (surfaceCount > 1)
return;
@@ -2715,6 +2480,8 @@ bool CHyprRenderer::beginRender(PHLMONITOR pMonitor, CRegion& damage, eRenderMod
makeEGLCurrent();
+ m_sRenderPass.clear();
+
m_eRenderMode = mode;
g_pHyprOpenGL->m_RenderData.pMonitor = pMonitor; // has to be set cuz allocs
@@ -2774,6 +2541,8 @@ void CHyprRenderer::endRender() {
PMONITOR->commitSeq++;
+ g_pHyprOpenGL->m_RenderData.damage = m_sRenderPass.render(g_pHyprOpenGL->m_RenderData.damage);
+
auto cleanup = CScopeGuard([this]() {
if (m_pCurrentRenderbuffer)
m_pCurrentRenderbuffer->unbind();
@@ -2903,3 +2672,225 @@ void CHyprRenderer::addWindowToRenderUnfocused(PHLWINDOW window) {
if (!m_tRenderUnfocusedTimer->armed())
m_tRenderUnfocusedTimer->updateTimeout(std::chrono::milliseconds(1000 / *PFPS));
}
+
+void CHyprRenderer::makeRawWindowSnapshot(PHLWINDOW pWindow, CFramebuffer* pFramebuffer) {
+ // we trust the window is valid.
+ const auto PMONITOR = pWindow->m_pMonitor.lock();
+
+ if (!PMONITOR || !PMONITOR->output || PMONITOR->vecPixelSize.x <= 0 || PMONITOR->vecPixelSize.y <= 0)
+ return;
+
+ // we need to "damage" the entire monitor
+ // so that we render the entire window
+ // this is temporary, doesnt mess with the actual damage
+ CRegion fakeDamage{0, 0, (int)PMONITOR->vecTransformedSize.x, (int)PMONITOR->vecTransformedSize.y};
+
+ makeEGLCurrent();
+
+ pFramebuffer->alloc(PMONITOR->vecPixelSize.x, PMONITOR->vecPixelSize.y, PMONITOR->output->state->state().drmFormat);
+ pFramebuffer->addStencil(g_pHyprOpenGL->m_RenderData.pCurrentMonData->stencilTex);
+
+ beginRender(PMONITOR, fakeDamage, RENDER_MODE_FULL_FAKE, nullptr, pFramebuffer);
+
+ g_pHyprOpenGL->clear(CHyprColor(0, 0, 0, 0)); // JIC
+
+ timespec now;
+ clock_gettime(CLOCK_MONOTONIC, &now);
+
+ // this is a hack but it works :P
+ // we need to disable blur or else we will get a black background, as the shader
+ // will try to copy the bg to apply blur.
+ // this isn't entirely correct, but like, oh well.
+ // small todo: maybe make this correct? :P
+ static auto* const PBLUR = (Hyprlang::INT* const*)(g_pConfigManager->getConfigValuePtr("decoration:blur:enabled"));
+ const auto BLURVAL = **PBLUR;
+ **PBLUR = 0;
+
+ // TODO: how can we make this the size of the window? setting it to window's size makes the entire screen render with the wrong res forever more. odd.
+ glViewport(0, 0, PMONITOR->vecPixelSize.x, PMONITOR->vecPixelSize.y);
+
+ g_pHyprOpenGL->m_RenderData.currentFB = pFramebuffer;
+
+ g_pHyprOpenGL->clear(CHyprColor(0, 0, 0, 0)); // JIC
+
+ renderWindow(pWindow, PMONITOR, &now, false, RENDER_PASS_ALL, true);
+
+ **PBLUR = BLURVAL;
+
+ endRender();
+}
+
+void CHyprRenderer::makeWindowSnapshot(PHLWINDOW pWindow) {
+ // we trust the window is valid.
+ const auto PMONITOR = pWindow->m_pMonitor.lock();
+
+ if (!PMONITOR || !PMONITOR->output || PMONITOR->vecPixelSize.x <= 0 || PMONITOR->vecPixelSize.y <= 0)
+ return;
+
+ if (!shouldRenderWindow(pWindow))
+ return; // ignore, window is not being rendered
+
+ // we need to "damage" the entire monitor
+ // so that we render the entire window
+ // this is temporary, doesnt mess with the actual damage
+ CRegion fakeDamage{0, 0, (int)PMONITOR->vecTransformedSize.x, (int)PMONITOR->vecTransformedSize.y};
+
+ PHLWINDOWREF ref{pWindow};
+
+ makeEGLCurrent();
+
+ const auto PFRAMEBUFFER = &g_pHyprOpenGL->m_mWindowFramebuffers[ref];
+
+ PFRAMEBUFFER->alloc(PMONITOR->vecPixelSize.x, PMONITOR->vecPixelSize.y, PMONITOR->output->state->state().drmFormat);
+
+ beginRender(PMONITOR, fakeDamage, RENDER_MODE_FULL_FAKE, nullptr, PFRAMEBUFFER);
+
+ m_bRenderingSnapshot = true;
+
+ g_pHyprOpenGL->clear(CHyprColor(0, 0, 0, 0)); // JIC
+
+ timespec now;
+ clock_gettime(CLOCK_MONOTONIC, &now);
+
+ // this is a hack but it works :P
+ // we need to disable blur or else we will get a black background, as the shader
+ // will try to copy the bg to apply blur.
+ // this isn't entirely correct, but like, oh well.
+ // small todo: maybe make this correct? :P
+ static auto* const PBLUR = (Hyprlang::INT* const*)(g_pConfigManager->getConfigValuePtr("decoration:blur:enabled"));
+ const auto BLURVAL = **PBLUR;
+ **PBLUR = 0;
+
+ g_pHyprOpenGL->clear(CHyprColor(0, 0, 0, 0)); // JIC
+
+ renderWindow(pWindow, PMONITOR, &now, !pWindow->m_bX11DoesntWantBorders, RENDER_PASS_ALL);
+
+ **PBLUR = BLURVAL;
+
+ endRender();
+
+ m_bRenderingSnapshot = false;
+}
+
+void CHyprRenderer::makeLayerSnapshot(PHLLS pLayer) {
+ // we trust the window is valid.
+ const auto PMONITOR = pLayer->monitor.lock();
+
+ if (!PMONITOR || !PMONITOR->output || PMONITOR->vecPixelSize.x <= 0 || PMONITOR->vecPixelSize.y <= 0)
+ return;
+
+ // we need to "damage" the entire monitor
+ // so that we render the entire window
+ // this is temporary, doesnt mess with the actual damage
+ CRegion fakeDamage{0, 0, (int)PMONITOR->vecTransformedSize.x, (int)PMONITOR->vecTransformedSize.y};
+
+ makeEGLCurrent();
+
+ const auto PFRAMEBUFFER = &g_pHyprOpenGL->m_mLayerFramebuffers[pLayer];
+
+ PFRAMEBUFFER->alloc(PMONITOR->vecPixelSize.x, PMONITOR->vecPixelSize.y, PMONITOR->output->state->state().drmFormat);
+
+ beginRender(PMONITOR, fakeDamage, RENDER_MODE_FULL_FAKE, nullptr, PFRAMEBUFFER);
+
+ m_bRenderingSnapshot = true;
+
+ g_pHyprOpenGL->clear(CHyprColor(0, 0, 0, 0)); // JIC
+
+ timespec now;
+ clock_gettime(CLOCK_MONOTONIC, &now);
+
+ const auto BLURLSSTATUS = pLayer->forceBlur;
+ pLayer->forceBlur = false;
+
+ // draw the layer
+ renderLayer(pLayer, PMONITOR, &now);
+
+ pLayer->forceBlur = BLURLSSTATUS;
+
+ endRender();
+
+ m_bRenderingSnapshot = false;
+}
+
+void CHyprRenderer::renderSnapshot(PHLWINDOW pWindow) {
+ static auto PDIMAROUND = CConfigValue<Hyprlang::FLOAT>("decoration:dim_around");
+
+ PHLWINDOWREF ref{pWindow};
+
+ if (!g_pHyprOpenGL->m_mWindowFramebuffers.contains(ref))
+ return;
+
+ const auto FBDATA = &g_pHyprOpenGL->m_mWindowFramebuffers.at(ref);
+
+ if (!FBDATA->getTexture())
+ return;
+
+ const auto PMONITOR = pWindow->m_pMonitor.lock();
+
+ CBox windowBox;
+ // some mafs to figure out the correct box
+ // the originalClosedPos is relative to the monitor's pos
+ Vector2D scaleXY = Vector2D((PMONITOR->scale * pWindow->m_vRealSize.value().x / (pWindow->m_vOriginalClosedSize.x * PMONITOR->scale)),
+ (PMONITOR->scale * pWindow->m_vRealSize.value().y / (pWindow->m_vOriginalClosedSize.y * PMONITOR->scale)));
+
+ windowBox.width = PMONITOR->vecTransformedSize.x * scaleXY.x;
+ windowBox.height = PMONITOR->vecTransformedSize.y * scaleXY.y;
+ windowBox.x = ((pWindow->m_vRealPosition.value().x - PMONITOR->vecPosition.x) * PMONITOR->scale) - ((pWindow->m_vOriginalClosedPos.x * PMONITOR->scale) * scaleXY.x);
+ windowBox.y = ((pWindow->m_vRealPosition.value().y - PMONITOR->vecPosition.y) * PMONITOR->scale) - ((pWindow->m_vOriginalClosedPos.y * PMONITOR->scale) * scaleXY.y);
+
+ CRegion fakeDamage{0, 0, PMONITOR->vecTransformedSize.x, PMONITOR->vecTransformedSize.y};
+
+ if (*PDIMAROUND && pWindow->m_sWindowData.dimAround.valueOrDefault()) {
+
+ CRectPassElement::SRectData data;
+
+ data.box = {0, 0, g_pHyprOpenGL->m_RenderData.pMonitor->vecPixelSize.x, g_pHyprOpenGL->m_RenderData.pMonitor->vecPixelSize.y};
+ data.color = CHyprColor(0, 0, 0, *PDIMAROUND * pWindow->m_fAlpha.value());
+
+ m_sRenderPass.add(makeShared<CRectPassElement>(data));
+ damageMonitor(PMONITOR);
+ }
+
+ CTexPassElement::SRenderData data;
+ data.flipEndFrame = true;
+ data.tex = FBDATA->getTexture();
+ data.box = windowBox;
+ data.a = pWindow->m_fAlpha.value();
+ data.damage = fakeDamage;
+
+ m_sRenderPass.add(makeShared<CTexPassElement>(data));
+}
+
+void CHyprRenderer::renderSnapshot(PHLLS pLayer) {
+ if (!g_pHyprOpenGL->m_mLayerFramebuffers.contains(pLayer))
+ return;
+
+ const auto FBDATA = &g_pHyprOpenGL->m_mLayerFramebuffers.at(pLayer);
+
+ if (!FBDATA->getTexture())
+ return;
+
+ const auto PMONITOR = pLayer->monitor.lock();
+
+ CBox layerBox;
+ // some mafs to figure out the correct box
+ // the originalClosedPos is relative to the monitor's pos
+ Vector2D scaleXY = Vector2D((PMONITOR->scale * pLayer->realSize.value().x / (pLayer->geometry.w * PMONITOR->scale)),
+ (PMONITOR->scale * pLayer->realSize.value().y / (pLayer->geometry.h * PMONITOR->scale)));
+
+ layerBox.width = PMONITOR->vecTransformedSize.x * scaleXY.x;
+ layerBox.height = PMONITOR->vecTransformedSize.y * scaleXY.y;
+ layerBox.x = ((pLayer->realPosition.value().x - PMONITOR->vecPosition.x) * PMONITOR->scale) - (((pLayer->geometry.x - PMONITOR->vecPosition.x) * PMONITOR->scale) * scaleXY.x);
+ layerBox.y = ((pLayer->realPosition.value().y - PMONITOR->vecPosition.y) * PMONITOR->scale) - (((pLayer->geometry.y - PMONITOR->vecPosition.y) * PMONITOR->scale) * scaleXY.y);
+
+ CRegion fakeDamage{0, 0, PMONITOR->vecTransformedSize.x, PMONITOR->vecTransformedSize.y};
+
+ CTexPassElement::SRenderData data;
+ data.flipEndFrame = true;
+ data.tex = FBDATA->getTexture();
+ data.box = layerBox;
+ data.a = pLayer->alpha.value();
+ data.damage = fakeDamage;
+
+ m_sRenderPass.add(makeShared<CTexPassElement>(data));
+}
diff --git a/src/render/Renderer.hpp b/src/render/Renderer.hpp
index 717a3285..054f0eea 100644
--- a/src/render/Renderer.hpp
+++ b/src/render/Renderer.hpp
@@ -67,9 +67,6 @@ class CHyprRenderer {
bool fixMisalignedFSV1 = false);
std::tuple<float, float, float> getRenderTimes(PHLMONITOR pMonitor); // avg max min
void renderLockscreen(PHLMONITOR pMonitor, timespec* now, const CBox& geometry);
- void setOccludedForBackLayers(CRegion& region, PHLWORKSPACE pWorkspace);
- void setOccludedForMainWorkspace(CRegion& region, PHLWORKSPACE pWorkspace); // TODO: merge occlusion methods
- bool canSkipBackBufferClear(PHLMONITOR pMonitor);
void recheckSolitaryForMonitor(PHLMONITOR pMonitor);
void setCursorSurface(SP<CWLSurface> surf, int hotspotX, int hotspotY, bool force = false);
void setCursorFromName(const std::string& name, bool force = false);
@@ -80,6 +77,11 @@ class CHyprRenderer {
void unsetEGL();
SExplicitSyncSettings getExplicitSyncSettings();
void addWindowToRenderUnfocused(PHLWINDOW window);
+ void makeWindowSnapshot(PHLWINDOW);
+ void makeRawWindowSnapshot(PHLWINDOW, CFramebuffer*);
+ void makeLayerSnapshot(PHLLS);
+ void renderSnapshot(PHLWINDOW);
+ void renderSnapshot(PHLLS);
// if RENDER_MODE_NORMAL, provided damage will be written to.
// otherwise, it will be the one used.
@@ -110,6 +112,8 @@ class CHyprRenderer {
std::string name;
} m_sLastCursorData;
+ CRenderPass m_sRenderPass = {};
+
private:
void arrangeLayerArray(PHLMONITOR, const std::vector<PHLLSREF>&, bool, CBox*);
void renderWorkspaceWindowsFullscreen(PHLMONITOR, PHLWORKSPACE, timespec*); // renders workspace windows (fullscreen) (tiled, floating, pinned, but no special)
@@ -129,10 +133,9 @@ class CHyprRenderer {
bool m_bCursorHidden = false;
bool m_bCursorHasSurface = false;
SP<CRenderbuffer> m_pCurrentRenderbuffer = nullptr;
- SP<Aquamarine::IBuffer> m_pCurrentBuffer;
- eRenderMode m_eRenderMode = RENDER_MODE_NORMAL;
-
- bool m_bNvidia = false;
+ SP<Aquamarine::IBuffer> m_pCurrentBuffer = nullptr;
+ eRenderMode m_eRenderMode = RENDER_MODE_NORMAL;
+ bool m_bNvidia = false;
struct {
bool hiddenOnTouch = false;
diff --git a/src/render/Transformer.cpp b/src/render/Transformer.cpp
index c6989d2c..7ac4ab68 100644
--- a/src/render/Transformer.cpp
+++ b/src/render/Transformer.cpp
@@ -1,5 +1,5 @@
#include "Transformer.hpp"
-void IWindowTransformer::preWindowRender(SRenderData* pRenderData) {
+void IWindowTransformer::preWindowRender(CSurfacePassElement::SRenderData* pRenderData) {
;
} \ No newline at end of file
diff --git a/src/render/Transformer.hpp b/src/render/Transformer.hpp
index c54da4dd..048b1898 100644
--- a/src/render/Transformer.hpp
+++ b/src/render/Transformer.hpp
@@ -1,8 +1,7 @@
#pragma once
#include "Framebuffer.hpp"
-
-struct SRenderData;
+#include "pass/SurfacePassElement.hpp"
// A window transformer can be attached to a window.
// If any is attached, Hyprland will render the window to a separate fb, then call the transform() func with it,
@@ -18,5 +17,5 @@ class IWindowTransformer {
virtual CFramebuffer* transform(CFramebuffer* in) = 0;
// called by Hyprland before a window main pass is started.
- virtual void preWindowRender(SRenderData* pRenderData);
+ virtual void preWindowRender(CSurfacePassElement::SRenderData* pRenderData);
}; \ No newline at end of file
diff --git a/src/render/decorations/CHyprBorderDecoration.cpp b/src/render/decorations/CHyprBorderDecoration.cpp
index 93490f31..4a793eab 100644
--- a/src/render/decorations/CHyprBorderDecoration.cpp
+++ b/src/render/decorations/CHyprBorderDecoration.cpp
@@ -2,6 +2,7 @@
#include "../../Compositor.hpp"
#include "../../config/ConfigValue.hpp"
#include "../../managers/eventLoop/EventLoopManager.hpp"
+#include "../pass/BorderPassElement.hpp"
CHyprBorderDecoration::CHyprBorderDecoration(PHLWINDOW pWindow) : IHyprWindowDecoration(pWindow), m_pWindow(pWindow) {
;
@@ -66,13 +67,24 @@ void CHyprBorderDecoration::draw(PHLMONITOR pMonitor, float const& a) {
grad.m_fAngle = normalizeAngleRad(grad.m_fAngle);
}
- int borderSize = m_pWindow->getRealBorderSize();
- const auto ROUNDING = m_pWindow->rounding() * pMonitor->scale;
+ int borderSize = m_pWindow->getRealBorderSize();
+ const auto ROUNDING = m_pWindow->rounding() * pMonitor->scale;
+
+ CBorderPassElement::SBorderData data;
+ data.box = windowBox;
+ data.grad1 = grad;
+ data.round = ROUNDING;
+ data.a = a;
+ data.borderSize = borderSize;
+
+ if (ANIMATED) {
+ data.hasGrad2 = true;
+ data.grad1 = m_pWindow->m_cRealBorderColorPrevious;
+ data.grad2 = grad;
+ data.lerp = m_pWindow->m_fBorderFadeAnimationProgress.value();
+ }
- if (ANIMATED)
- g_pHyprOpenGL->renderBorder(&windowBox, m_pWindow->m_cRealBorderColorPrevious, grad, m_pWindow->m_fBorderFadeAnimationProgress.value(), ROUNDING, borderSize, a);
- else
- g_pHyprOpenGL->renderBorder(&windowBox, grad, ROUNDING, borderSize, a);
+ g_pHyprRenderer->m_sRenderPass.add(makeShared<CBorderPassElement>(data));
}
eDecorationType CHyprBorderDecoration::getDecorationType() {
diff --git a/src/render/decorations/CHyprDropShadowDecoration.cpp b/src/render/decorations/CHyprDropShadowDecoration.cpp
index 38ced9af..21242b57 100644
--- a/src/render/decorations/CHyprDropShadowDecoration.cpp
+++ b/src/render/decorations/CHyprDropShadowDecoration.cpp
@@ -2,6 +2,7 @@
#include "../../Compositor.hpp"
#include "../../config/ConfigValue.hpp"
+#include "../pass/ShadowPassElement.hpp"
CHyprDropShadowDecoration::CHyprDropShadowDecoration(PHLWINDOW pWindow) : IHyprWindowDecoration(pWindow), m_pWindow(pWindow) {
;
@@ -87,7 +88,13 @@ void CHyprDropShadowDecoration::updateWindow(PHLWINDOW pWindow) {
}
void CHyprDropShadowDecoration::draw(PHLMONITOR pMonitor, float const& a) {
+ CShadowPassElement::SShadowData data;
+ data.deco = this;
+ data.a = a;
+ g_pHyprRenderer->m_sRenderPass.add(makeShared<CShadowPassElement>(data));
+}
+void CHyprDropShadowDecoration::render(PHLMONITOR pMonitor, float const& a) {
const auto PWINDOW = m_pWindow.lock();
if (!validMapped(PWINDOW))
@@ -141,6 +148,7 @@ void CHyprDropShadowDecoration::draw(PHLMONITOR pMonitor, float const& a) {
return; // don't draw invisible shadows
g_pHyprOpenGL->scissor((CBox*)nullptr);
+ g_pHyprOpenGL->m_RenderData.currentWindow = m_pWindow;
// we'll take the liberty of using this as it should not be used rn
CFramebuffer& alphaFB = g_pHyprOpenGL->m_RenderData.pCurrentMonData->mirrorFB;
@@ -197,6 +205,7 @@ void CHyprDropShadowDecoration::draw(PHLMONITOR pMonitor, float const& a) {
LASTFB->bind();
CBox monbox = {0, 0, pMonitor->vecTransformedSize.x, pMonitor->vecTransformedSize.y};
+
g_pHyprOpenGL->setMonitorTransformEnabled(true);
g_pHyprOpenGL->setRenderModifEnabled(false);
g_pHyprOpenGL->renderTextureMatte(alphaSwapFB.getTexture(), &monbox, alphaFB);
@@ -209,6 +218,8 @@ void CHyprDropShadowDecoration::draw(PHLMONITOR pMonitor, float const& a) {
if (m_seExtents != m_seReportedExtents)
g_pDecorationPositioner->repositionDeco(this);
+
+ g_pHyprOpenGL->m_RenderData.currentWindow.reset();
}
eDecorationLayer CHyprDropShadowDecoration::getDecorationLayer() {
diff --git a/src/render/decorations/CHyprDropShadowDecoration.hpp b/src/render/decorations/CHyprDropShadowDecoration.hpp
index 650f8c92..fe0f8952 100644
--- a/src/render/decorations/CHyprDropShadowDecoration.hpp
+++ b/src/render/decorations/CHyprDropShadowDecoration.hpp
@@ -25,6 +25,8 @@ class CHyprDropShadowDecoration : public IHyprWindowDecoration {
virtual std::string getDisplayName();
+ void render(PHLMONITOR, float const& a);
+
private:
SBoxExtents m_seExtents;
SBoxExtents m_seReportedExtents;
diff --git a/src/render/decorations/CHyprGroupBarDecoration.cpp b/src/render/decorations/CHyprGroupBarDecoration.cpp
index 0aee6157..f870e71b 100644
--- a/src/render/decorations/CHyprGroupBarDecoration.cpp
+++ b/src/render/decorations/CHyprGroupBarDecoration.cpp
@@ -4,6 +4,8 @@
#include "managers/LayoutManager.hpp"
#include <ranges>
#include <pango/pangocairo.h>
+#include "../pass/TexPassElement.hpp"
+#include "../pass/RectPassElement.hpp"
// shared things to conserve VRAM
static SP<CTexture> m_tGradientActive = makeShared<CTexture>();
@@ -71,11 +73,11 @@ void CHyprGroupBarDecoration::updateWindow(PHLWINDOW pWindow) {
m_dwGroupMembers.clear();
PHLWINDOW head = pWindow->getGroupHead();
- m_dwGroupMembers.push_back(head);
+ m_dwGroupMembers.emplace_back(head);
PHLWINDOW curr = head->m_sGroupData.pNextWindow.lock();
while (curr != head) {
- m_dwGroupMembers.push_back(curr);
+ m_dwGroupMembers.emplace_back(curr);
curr = curr->m_sGroupData.pNextWindow.lock();
}
@@ -113,10 +115,9 @@ void CHyprGroupBarDecoration::draw(PHLMONITOR pMonitor, float const& a) {
m_fBarWidth = *PSTACKED ? ASSIGNEDBOX.w : (ASSIGNEDBOX.w - BAR_HORIZONTAL_PADDING * (barsToDraw - 1)) / barsToDraw;
m_fBarHeight = *PSTACKED ? ((ASSIGNEDBOX.h - 2 - BAR_PADDING_OUTER_VERT) - BAR_PADDING_OUTER_VERT * (barsToDraw)) / barsToDraw : ASSIGNEDBOX.h - BAR_PADDING_OUTER_VERT;
- const auto DESIREDHEIGHT = *PSTACKED ? (ONEBARHEIGHT * m_dwGroupMembers.size()) + 2 + BAR_PADDING_OUTER_VERT : BAR_PADDING_OUTER_VERT * 2 + ONEBARHEIGHT;
- if (DESIREDHEIGHT != ASSIGNEDBOX.h) {
+ const auto DESIREDHEIGHT = *PSTACKED ? (ONEBARHEIGHT * m_dwGroupMembers.size()) + 2 + BAR_PADDING_OUTER_VERT : BAR_PADDING_OUTER_VERT * 2L + ONEBARHEIGHT;
+ if (DESIREDHEIGHT != ASSIGNEDBOX.h)
g_pDecorationPositioner->repositionDeco(this);
- }
float xoff = 0;
float yoff = 0;
@@ -148,7 +149,10 @@ void CHyprGroupBarDecoration::draw(PHLMONITOR pMonitor, float const& a) {
CHyprColor color = m_dwGroupMembers[WINDOWINDEX].lock() == g_pCompositor->m_pLastWindow.lock() ? PCOLACTIVE->m_vColors[0] : PCOLINACTIVE->m_vColors[0];
color.a *= a;
- g_pHyprOpenGL->renderRect(&rect, color);
+ CRectPassElement::SRectData rectdata;
+ rectdata.color = color;
+ rectdata.box = rect;
+ g_pHyprRenderer->m_sRenderPass.add(makeShared<CRectPassElement>(rectdata));
rect = {ASSIGNEDBOX.x + floor(xoff) - pMonitor->vecPosition.x + m_pWindow->m_vFloatingOffset.x,
ASSIGNEDBOX.y + ASSIGNEDBOX.h - floor(yoff) - ONEBARHEIGHT - pMonitor->vecPosition.y + m_pWindow->m_vFloatingOffset.y, m_fBarWidth,
@@ -158,26 +162,33 @@ void CHyprGroupBarDecoration::draw(PHLMONITOR pMonitor, float const& a) {
if (*PGRADIENTS) {
const auto GRADIENTTEX = (m_dwGroupMembers[WINDOWINDEX] == g_pCompositor->m_pLastWindow ? (GROUPLOCKED ? m_tGradientLockedActive : m_tGradientActive) :
(GROUPLOCKED ? m_tGradientLockedInactive : m_tGradientInactive));
- if (GRADIENTTEX->m_iTexID != 0)
- g_pHyprOpenGL->renderTexture(GRADIENTTEX, &rect, 1.0);
+ if (GRADIENTTEX->m_iTexID) {
+ CTexPassElement::SRenderData data;
+ data.tex = GRADIENTTEX;
+ data.box = rect;
+ g_pHyprRenderer->m_sRenderPass.add(makeShared<CTexPassElement>(data));
+ }
}
if (*PRENDERTITLES) {
CTitleTex* pTitleTex = textureFromTitle(m_dwGroupMembers[WINDOWINDEX]->m_szTitle);
if (!pTitleTex)
- pTitleTex =
- m_sTitleTexs.titleTexs
- .emplace_back(std::make_unique<CTitleTex>(m_dwGroupMembers[WINDOWINDEX].lock(),
- Vector2D{m_fBarWidth * pMonitor->scale, (*PTITLEFONTSIZE + 2 * BAR_TEXT_PAD) * pMonitor->scale}, pMonitor->scale))
- .get();
+ pTitleTex = m_sTitleTexs.titleTexs
+ .emplace_back(std::make_unique<CTitleTex>(m_dwGroupMembers[WINDOWINDEX].lock(),
+ Vector2D{m_fBarWidth * pMonitor->scale, (*PTITLEFONTSIZE + 2L * BAR_TEXT_PAD) * pMonitor->scale},
+ pMonitor->scale))
+ .get();
rect.y += (rect.height - pTitleTex->textHeight) / 2.0;
rect.height = pTitleTex->textHeight;
rect.width = pTitleTex->textWidth;
rect.x += (m_fBarWidth * pMonitor->scale) / 2.0 - (pTitleTex->textWidth / 2.0);
rect.round();
- g_pHyprOpenGL->renderTexture(pTitleTex->tex, &rect, 1.f);
+ CTexPassElement::SRenderData data;
+ data.tex = pTitleTex->tex;
+ data.box = rect;
+ g_pHyprRenderer->m_sRenderPass.add(makeShared<CTexPassElement>(data));
}
if (*PSTACKED)
@@ -203,10 +214,8 @@ void CHyprGroupBarDecoration::invalidateTextures() {
m_sTitleTexs.titleTexs.clear();
}
-CTitleTex::CTitleTex(PHLWINDOW pWindow, const Vector2D& bufferSize, const float monitorScale) {
+CTitleTex::CTitleTex(PHLWINDOW pWindow, const Vector2D& bufferSize, const float monitorScale) : szContent(pWindow->m_szTitle), pWindowOwner(pWindow) {
tex = makeShared<CTexture>();
- szContent = pWindow->m_szTitle;
- pWindowOwner = pWindow;
const auto LAYOUTSURFACE = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, 0, 0);
const auto LAYOUTCAIRO = cairo_create(LAYOUTSURFACE);
@@ -281,9 +290,7 @@ CTitleTex::CTitleTex(PHLWINDOW pWindow, const Vector2D& bufferSize, const float
cairo_surface_destroy(CAIROSURFACE);
}
-CTitleTex::~CTitleTex() {
- tex->destroyTexture();
-}
+CTitleTex::~CTitleTex() = default;
void renderGradientTo(SP<CTexture> tex, CGradientValueData* grad) {
@@ -435,7 +442,7 @@ bool CHyprGroupBarDecoration::onEndWindowDragOnDeco(const Vector2D& pos, PHLWIND
} while (curr != members[0]);
// removes all windows
- for (PHLWINDOW w : members) {
+ for (const PHLWINDOW& w : members) {
w->m_sGroupData.pNextWindow.reset();
w->m_sGroupData.head = false;
w->m_sGroupData.locked = false;
@@ -521,7 +528,7 @@ bool CHyprGroupBarDecoration::onMouseButtonOnDeco(const Vector2D& pos, const IPo
g_pCompositor->focusWindow(pWindow);
if (pWindow->m_bIsFloating)
- g_pCompositor->changeWindowZOrder(pWindow, 1);
+ g_pCompositor->changeWindowZOrder(pWindow, true);
return true;
}
diff --git a/src/render/pass/BorderPassElement.cpp b/src/render/pass/BorderPassElement.cpp
new file mode 100644
index 00000000..635533d0
--- /dev/null
+++ b/src/render/pass/BorderPassElement.cpp
@@ -0,0 +1,21 @@
+#include "BorderPassElement.hpp"
+#include "../OpenGL.hpp"
+
+CBorderPassElement::CBorderPassElement(const CBorderPassElement::SBorderData& data_) : data(data_) {
+ ;
+}
+
+void CBorderPassElement::draw(const CRegion& damage) {
+ if (data.hasGrad2)
+ g_pHyprOpenGL->renderBorder(&data.box, data.grad1, data.grad2, data.lerp, data.round, data.borderSize, data.a, data.outerRound);
+ else
+ g_pHyprOpenGL->renderBorder(&data.box, data.grad1, data.round, data.borderSize, data.a, data.outerRound);
+}
+
+bool CBorderPassElement::needsLiveBlur() {
+ return false;
+}
+
+bool CBorderPassElement::needsPrecomputeBlur() {
+ return false;
+} \ No newline at end of file
diff --git a/src/render/pass/BorderPassElement.hpp b/src/render/pass/BorderPassElement.hpp
new file mode 100644
index 00000000..3a529640
--- /dev/null
+++ b/src/render/pass/BorderPassElement.hpp
@@ -0,0 +1,30 @@
+#pragma once
+#include "PassElement.hpp"
+#include "../../config/ConfigDataValues.hpp"
+
+class CGradientValueData;
+
+class CBorderPassElement : public IPassElement {
+ public:
+ struct SBorderData {
+ CBox box;
+ CGradientValueData grad1, grad2;
+ bool hasGrad2 = false;
+ float lerp = 0.F, a = 1.F;
+ int round = 0, borderSize = 1, outerRound = -1;
+ };
+
+ CBorderPassElement(const SBorderData& data_);
+ virtual ~CBorderPassElement() = default;
+
+ virtual void draw(const CRegion& damage);
+ virtual bool needsLiveBlur();
+ virtual bool needsPrecomputeBlur();
+
+ virtual const char* passName() {
+ return "CBorderPassElement";
+ }
+
+ private:
+ SBorderData data;
+};
diff --git a/src/render/pass/ClearPassElement.cpp b/src/render/pass/ClearPassElement.cpp
new file mode 100644
index 00000000..bafa73c7
--- /dev/null
+++ b/src/render/pass/ClearPassElement.cpp
@@ -0,0 +1,26 @@
+#include "ClearPassElement.hpp"
+#include "../OpenGL.hpp"
+
+CClearPassElement::CClearPassElement(const CClearPassElement::SClearData& data_) : data(data_) {
+ ;
+}
+
+void CClearPassElement::draw(const CRegion& damage) {
+ g_pHyprOpenGL->clear(data.color);
+}
+
+bool CClearPassElement::needsLiveBlur() {
+ return false;
+}
+
+bool CClearPassElement::needsPrecomputeBlur() {
+ return false;
+}
+
+std::optional<CBox> CClearPassElement::boundingBox() {
+ return CBox{{}, {INT16_MAX, INT16_MAX}};
+}
+
+CRegion CClearPassElement::opaqueRegion() {
+ return *boundingBox();
+}
diff --git a/src/render/pass/ClearPassElement.hpp b/src/render/pass/ClearPassElement.hpp
new file mode 100644
index 00000000..40242457
--- /dev/null
+++ b/src/render/pass/ClearPassElement.hpp
@@ -0,0 +1,25 @@
+#pragma once
+#include "PassElement.hpp"
+
+class CClearPassElement : public IPassElement {
+ public:
+ struct SClearData {
+ CHyprColor color;
+ };
+
+ CClearPassElement(const SClearData& data);
+ virtual ~CClearPassElement() = default;
+
+ virtual void draw(const CRegion& damage);
+ virtual bool needsLiveBlur();
+ virtual bool needsPrecomputeBlur();
+ virtual std::optional<CBox> boundingBox();
+ virtual CRegion opaqueRegion();
+
+ virtual const char* passName() {
+ return "CClearPassElement";
+ }
+
+ private:
+ SClearData data;
+}; \ No newline at end of file
diff --git a/src/render/pass/FramebufferElement.cpp b/src/render/pass/FramebufferElement.cpp
new file mode 100644
index 00000000..268e5589
--- /dev/null
+++ b/src/render/pass/FramebufferElement.cpp
@@ -0,0 +1,48 @@
+#include "FramebufferElement.hpp"
+#include "../OpenGL.hpp"
+
+CFramebufferElement::CFramebufferElement(const CFramebufferElement::SFramebufferElementData& data_) : data(data_) {
+ ;
+}
+
+void CFramebufferElement::draw(const CRegion& damage) {
+ CFramebuffer* fb = nullptr;
+
+ if (data.main) {
+ switch (data.framebufferID) {
+ case FB_MONITOR_RENDER_MAIN: fb = g_pHyprOpenGL->m_RenderData.mainFB; break;
+ case FB_MONITOR_RENDER_CURRENT: fb = g_pHyprOpenGL->m_RenderData.currentFB; break;
+ case FB_MONITOR_RENDER_OUT: fb = g_pHyprOpenGL->m_RenderData.outFB; break;
+ }
+
+ if (!fb) {
+ Debug::log(ERR, "BUG THIS: CFramebufferElement::draw: main but null");
+ return;
+ }
+
+ } else {
+ switch (data.framebufferID) {
+ case FB_MONITOR_RENDER_EXTRA_OFFLOAD: fb = &g_pHyprOpenGL->m_RenderData.pCurrentMonData->offloadFB; break;
+ case FB_MONITOR_RENDER_EXTRA_MIRROR: fb = &g_pHyprOpenGL->m_RenderData.pCurrentMonData->mirrorFB; break;
+ case FB_MONITOR_RENDER_EXTRA_MIRROR_SWAP: fb = &g_pHyprOpenGL->m_RenderData.pCurrentMonData->mirrorSwapFB; break;
+ case FB_MONITOR_RENDER_EXTRA_OFF_MAIN: fb = &g_pHyprOpenGL->m_RenderData.pCurrentMonData->offMainFB; break;
+ case FB_MONITOR_RENDER_EXTRA_MONITOR_MIRROR: fb = &g_pHyprOpenGL->m_RenderData.pCurrentMonData->monitorMirrorFB; break;
+ case FB_MONITOR_RENDER_EXTRA_BLUR: fb = &g_pHyprOpenGL->m_RenderData.pCurrentMonData->blurFB; break;
+ }
+
+ if (!fb) {
+ Debug::log(ERR, "BUG THIS: CFramebufferElement::draw: not main but null");
+ return;
+ }
+ }
+
+ fb->bind();
+}
+
+bool CFramebufferElement::needsLiveBlur() {
+ return false;
+}
+
+bool CFramebufferElement::needsPrecomputeBlur() {
+ return false;
+} \ No newline at end of file
diff --git a/src/render/pass/FramebufferElement.hpp b/src/render/pass/FramebufferElement.hpp
new file mode 100644
index 00000000..f3d181d4
--- /dev/null
+++ b/src/render/pass/FramebufferElement.hpp
@@ -0,0 +1,24 @@
+#pragma once
+#include "PassElement.hpp"
+
+class CFramebufferElement : public IPassElement {
+ public:
+ struct SFramebufferElementData {
+ bool main = true;
+ uint8_t framebufferID = 0;
+ };
+
+ CFramebufferElement(const SFramebufferElementData& data_);
+ virtual ~CFramebufferElement() = default;
+
+ virtual void draw(const CRegion& damage);
+ virtual bool needsLiveBlur();
+ virtual bool needsPrecomputeBlur();
+
+ virtual const char* passName() {
+ return "CFramebufferElement";
+ }
+
+ private:
+ SFramebufferElementData data;
+}; \ No newline at end of file
diff --git a/src/render/pass/Pass.cpp b/src/render/pass/Pass.cpp
new file mode 100644
index 00000000..31385acb
--- /dev/null
+++ b/src/render/pass/Pass.cpp
@@ -0,0 +1,157 @@
+#include "Pass.hpp"
+#include "../OpenGL.hpp"
+#include <algorithm>
+#include <ranges>
+#include "../../config/ConfigValue.hpp"
+
+bool CRenderPass::empty() const {
+ return false;
+}
+
+bool CRenderPass::single() const {
+ return m_vPassElements.size() == 1;
+}
+
+bool CRenderPass::needsIntrospection() const {
+ return true;
+}
+
+void CRenderPass::add(SP<IPassElement> el) {
+ m_vPassElements.emplace_back(makeShared<SPassElementData>(CRegion{}, el));
+}
+
+void CRenderPass::simplify() {
+ // TODO: use precompute blur for instances where there is nothing in between
+
+ // if there is live blur, we need to NOT occlude any area where it will be influenced
+ const auto WILLBLUR = std::ranges::any_of(m_vPassElements, [](const auto& el) { return el->element->needsLiveBlur(); });
+
+ CRegion newDamage = damage.copy().intersect(CBox{{}, g_pHyprOpenGL->m_RenderData.pMonitor->vecTransformedSize});
+ for (auto& el : m_vPassElements | std::views::reverse) {
+
+ if (newDamage.empty()) {
+ el->discard = true;
+ continue;
+ }
+
+ el->elementDamage = newDamage;
+ auto bb1 = el->element->boundingBox();
+ if (!bb1)
+ continue;
+
+ auto bb = bb1->scale(g_pHyprOpenGL->m_RenderData.pMonitor->scale);
+
+ // drop if empty
+ if (CRegion copy = newDamage.copy(); copy.intersect(bb).empty()) {
+ el->discard = true;
+ continue;
+ }
+
+ auto opaque = el->element->opaqueRegion();
+
+ if (!opaque.empty()) {
+ opaque.scale(g_pHyprOpenGL->m_RenderData.pMonitor->scale);
+
+ // if this intersects the liveBlur region, allow live blur to operate correctly.
+ // do not occlude a border near it.
+ if (WILLBLUR) {
+ CRegion liveBlurRegion;
+ for (auto& el2 : m_vPassElements) {
+ // if we reach self, no problem, we can break.
+ // if the blur is above us, we don't care, it will work fine.
+ if (el2 == el)
+ break;
+
+ if (!el2->element->needsLiveBlur())
+ continue;
+
+ const auto BB = el2->element->boundingBox();
+ RASSERT(BB, "No bounding box for an element with live blur is illegal");
+
+ liveBlurRegion.add(*BB);
+ }
+
+ if (auto infringement = opaque.copy().intersect(liveBlurRegion); !infringement.empty()) {
+ // eh, this is not the correct solution, but it will do...
+ // TODO: is this *easily* fixable?
+ opaque.subtract(infringement.expand(oneBlurRadius()));
+ }
+ }
+ newDamage.subtract(opaque);
+ }
+ }
+}
+
+void CRenderPass::clear() {
+ m_vPassElements.clear();
+}
+
+CRegion CRenderPass::render(const CRegion& damage_) {
+ const auto WILLBLUR = std::ranges::any_of(m_vPassElements, [](const auto& el) { return el->element->needsLiveBlur(); });
+
+ damage = damage_.copy();
+
+ if (damage.empty()) {
+ g_pHyprOpenGL->m_RenderData.damage = damage;
+ g_pHyprOpenGL->m_RenderData.finalDamage = damage;
+ return damage;
+ }
+
+ if (WILLBLUR) {
+ // combine blur regions into one that will be expanded
+ CRegion blurRegion;
+ for (auto& el : m_vPassElements) {
+ if (!el->element->needsLiveBlur())
+ continue;
+
+ const auto BB = el->element->boundingBox();
+ RASSERT(BB, "No bounding box for an element with live blur is illegal");
+
+ blurRegion.add(*BB);
+ }
+
+ blurRegion.intersect(damage).expand(oneBlurRadius());
+
+ g_pHyprOpenGL->m_RenderData.finalDamage = blurRegion.copy().add(damage);
+
+ // FIXME: why does this break on * 1.F ?
+ // used to work when we expand all the damage... I think? Well, before pass.
+ // moving a window over blur shows the edges being wonk.
+ blurRegion.expand(oneBlurRadius() * 1.5F);
+
+ damage = blurRegion.copy().add(damage);
+ } else
+ g_pHyprOpenGL->m_RenderData.finalDamage = damage;
+
+ if (std::ranges::any_of(m_vPassElements, [](const auto& el) { return el->element->disableSimplification(); })) {
+ for (auto& el : m_vPassElements) {
+ el->elementDamage = damage;
+ }
+ } else
+ simplify();
+
+ g_pHyprOpenGL->m_RenderData.pCurrentMonData->blurFBShouldRender = std::ranges::any_of(m_vPassElements, [](const auto& el) { return el->element->needsPrecomputeBlur(); });
+
+ if (m_vPassElements.empty())
+ return {};
+
+ for (auto& el : m_vPassElements) {
+ if (el->discard) {
+ el->element->discard();
+ continue;
+ }
+
+ g_pHyprOpenGL->m_RenderData.damage = el->elementDamage;
+ el->element->draw(el->elementDamage);
+ }
+
+ g_pHyprOpenGL->m_RenderData.damage = damage;
+ return damage;
+}
+
+float CRenderPass::oneBlurRadius() {
+ // TODO: is this exact range correct?
+ static auto PBLURSIZE = CConfigValue<Hyprlang::INT>("decoration:blur:size");
+ static auto PBLURPASSES = CConfigValue<Hyprlang::INT>("decoration:blur:passes");
+ return *PBLURPASSES > 10 ? pow(2, 15) : std::clamp(*PBLURSIZE, (int64_t)1, (int64_t)40) * pow(2, *PBLURPASSES); // is this 2^pass? I don't know but it works... I think.
+}
diff --git a/src/render/pass/Pass.hpp b/src/render/pass/Pass.hpp
new file mode 100644
index 00000000..7f332c19
--- /dev/null
+++ b/src/render/pass/Pass.hpp
@@ -0,0 +1,36 @@
+#pragma once
+
+#include "../../defines.hpp"
+#include "PassElement.hpp"
+
+class CGradientValueData;
+
+class CRenderPass {
+ public:
+ bool empty() const;
+ bool single() const;
+ bool needsIntrospection() const;
+
+ void add(SP<IPassElement> elem);
+ void clear();
+
+ CRegion render(const CRegion& damage_);
+
+ private:
+ CRegion damage;
+
+ struct SPassElementData {
+ CRegion elementDamage;
+ SP<IPassElement> element;
+ bool discard = false;
+ };
+
+ std::vector<SP<SPassElementData>> m_vPassElements;
+
+ SP<IPassElement> currentPassInfo = nullptr;
+
+ void simplify();
+ float oneBlurRadius();
+
+ friend class CHyprOpenGLImpl;
+};
diff --git a/src/render/pass/PassElement.cpp b/src/render/pass/PassElement.cpp
new file mode 100644
index 00000000..737c6b27
--- /dev/null
+++ b/src/render/pass/PassElement.cpp
@@ -0,0 +1,17 @@
+#include "PassElement.hpp"
+
+std::optional<CBox> IPassElement::boundingBox() {
+ return std::nullopt;
+}
+
+CRegion IPassElement::opaqueRegion() {
+ return {};
+}
+
+bool IPassElement::disableSimplification() {
+ return false;
+}
+
+void IPassElement::discard() {
+ ;
+}
diff --git a/src/render/pass/PassElement.hpp b/src/render/pass/PassElement.hpp
new file mode 100644
index 00000000..025840f4
--- /dev/null
+++ b/src/render/pass/PassElement.hpp
@@ -0,0 +1,18 @@
+#pragma once
+
+#include "../../defines.hpp"
+#include <optional>
+
+class IPassElement {
+ public:
+ virtual ~IPassElement() = default;
+
+ virtual void draw(const CRegion& damage) = 0;
+ virtual bool needsLiveBlur() = 0;
+ virtual bool needsPrecomputeBlur() = 0;
+ virtual const char* passName() = 0;
+ virtual void discard();
+ virtual std::optional<CBox> boundingBox();
+ virtual CRegion opaqueRegion();
+ virtual bool disableSimplification();
+};
diff --git a/src/render/pass/PreBlurElement.cpp b/src/render/pass/PreBlurElement.cpp
new file mode 100644
index 00000000..9fa3471d
--- /dev/null
+++ b/src/render/pass/PreBlurElement.cpp
@@ -0,0 +1,20 @@
+#include "PreBlurElement.hpp"
+#include "../OpenGL.hpp"
+
+CPreBlurElement::CPreBlurElement() = default;
+
+void CPreBlurElement::draw(const CRegion& damage) {
+ g_pHyprOpenGL->preBlurForCurrentMonitor();
+}
+
+bool CPreBlurElement::needsLiveBlur() {
+ return false;
+}
+
+bool CPreBlurElement::needsPrecomputeBlur() {
+ return false;
+}
+
+bool CPreBlurElement::disableSimplification() {
+ return true;
+}
diff --git a/src/render/pass/PreBlurElement.hpp b/src/render/pass/PreBlurElement.hpp
new file mode 100644
index 00000000..6c52c815
--- /dev/null
+++ b/src/render/pass/PreBlurElement.hpp
@@ -0,0 +1,17 @@
+#pragma once
+#include "PassElement.hpp"
+
+class CPreBlurElement : public IPassElement {
+ public:
+ CPreBlurElement();
+ virtual ~CPreBlurElement() = default;
+
+ virtual void draw(const CRegion& damage);
+ virtual bool needsLiveBlur();
+ virtual bool needsPrecomputeBlur();
+ virtual bool disableSimplification();
+
+ virtual const char* passName() {
+ return "CPreBlurElement";
+ }
+}; \ No newline at end of file
diff --git a/src/render/pass/RectPassElement.cpp b/src/render/pass/RectPassElement.cpp
new file mode 100644
index 00000000..73727e58
--- /dev/null
+++ b/src/render/pass/RectPassElement.cpp
@@ -0,0 +1,29 @@
+#include "RectPassElement.hpp"
+#include "../OpenGL.hpp"
+
+CRectPassElement::CRectPassElement(const CRectPassElement::SRectData& data_) : data(data_) {
+ ;
+}
+
+void CRectPassElement::draw(const CRegion& damage) {
+ if (data.color.a == 1.F || !data.blur)
+ g_pHyprOpenGL->renderRectWithDamage(&data.box, data.color, damage, data.round);
+ else
+ g_pHyprOpenGL->renderRectWithBlur(&data.box, data.color, data.round, data.blurA, data.xray);
+}
+
+bool CRectPassElement::needsLiveBlur() {
+ return data.color.a < 1.F && !data.xray && data.blur;
+}
+
+bool CRectPassElement::needsPrecomputeBlur() {
+ return data.color.a < 1.F && data.xray && data.blur;
+}
+
+std::optional<CBox> CRectPassElement::boundingBox() {
+ return data.box.expand(-data.round);
+}
+
+CRegion CRectPassElement::opaqueRegion() {
+ return data.color.a >= 1.F ? *boundingBox() : CRegion{};
+}
diff --git a/src/render/pass/RectPassElement.hpp b/src/render/pass/RectPassElement.hpp
new file mode 100644
index 00000000..32111a43
--- /dev/null
+++ b/src/render/pass/RectPassElement.hpp
@@ -0,0 +1,29 @@
+#pragma once
+#include "PassElement.hpp"
+
+class CRectPassElement : public IPassElement {
+ public:
+ struct SRectData {
+ CBox box;
+ CHyprColor color;
+ int round = 0;
+ bool blur = false, xray = false;
+ float blurA = 1.F;
+ };
+
+ CRectPassElement(const SRectData& data);
+ virtual ~CRectPassElement() = default;
+
+ virtual void draw(const CRegion& damage);
+ virtual bool needsLiveBlur();
+ virtual bool needsPrecomputeBlur();
+ virtual std::optional<CBox> boundingBox();
+ virtual CRegion opaqueRegion();
+
+ virtual const char* passName() {
+ return "CRectPassElement";
+ }
+
+ private:
+ SRectData data;
+}; \ No newline at end of file
diff --git a/src/render/pass/ShadowPassElement.cpp b/src/render/pass/ShadowPassElement.cpp
new file mode 100644
index 00000000..22910c96
--- /dev/null
+++ b/src/render/pass/ShadowPassElement.cpp
@@ -0,0 +1,19 @@
+#include "ShadowPassElement.hpp"
+#include "../OpenGL.hpp"
+#include "../decorations/CHyprDropShadowDecoration.hpp"
+
+CShadowPassElement::CShadowPassElement(const CShadowPassElement::SShadowData& data_) : data(data_) {
+ ;
+}
+
+void CShadowPassElement::draw(const CRegion& damage) {
+ data.deco->render(g_pHyprOpenGL->m_RenderData.pMonitor.lock(), data.a);
+}
+
+bool CShadowPassElement::needsLiveBlur() {
+ return false;
+}
+
+bool CShadowPassElement::needsPrecomputeBlur() {
+ return false;
+} \ No newline at end of file
diff --git a/src/render/pass/ShadowPassElement.hpp b/src/render/pass/ShadowPassElement.hpp
new file mode 100644
index 00000000..715e7bfb
--- /dev/null
+++ b/src/render/pass/ShadowPassElement.hpp
@@ -0,0 +1,26 @@
+#pragma once
+#include "PassElement.hpp"
+
+class CHyprDropShadowDecoration;
+
+class CShadowPassElement : public IPassElement {
+ public:
+ struct SShadowData {
+ CHyprDropShadowDecoration* deco = nullptr;
+ float a = 1.F;
+ };
+
+ CShadowPassElement(const SShadowData& data_);
+ virtual ~CShadowPassElement() = default;
+
+ virtual void draw(const CRegion& damage);
+ virtual bool needsLiveBlur();
+ virtual bool needsPrecomputeBlur();
+
+ virtual const char* passName() {
+ return "CShadowPassElement";
+ }
+
+ private:
+ SShadowData data;
+}; \ No newline at end of file
diff --git a/src/render/pass/SurfacePassElement.cpp b/src/render/pass/SurfacePassElement.cpp
new file mode 100644
index 00000000..38de5210
--- /dev/null
+++ b/src/render/pass/SurfacePassElement.cpp
@@ -0,0 +1,230 @@
+#include "SurfacePassElement.hpp"
+#include "../OpenGL.hpp"
+#include "../../desktop/WLSurface.hpp"
+#include "../../protocols/core/Compositor.hpp"
+#include "../../protocols/DRMSyncobj.hpp"
+#include "../../managers/input/InputManager.hpp"
+#include "../Renderer.hpp"
+
+#include <hyprutils/utils/ScopeGuard.hpp>
+using namespace Hyprutils::Utils;
+
+CSurfacePassElement::CSurfacePassElement(const CSurfacePassElement::SRenderData& data_) : data(data_) {
+ ;
+}
+
+void CSurfacePassElement::draw(const CRegion& damage) {
+ g_pHyprOpenGL->m_RenderData.currentWindow = data.pWindow;
+ g_pHyprOpenGL->m_RenderData.currentLS = data.pLS;
+ g_pHyprOpenGL->m_RenderData.clipBox = data.clipBox;
+ g_pHyprOpenGL->m_RenderData.discardMode = data.discardMode;
+ g_pHyprOpenGL->m_RenderData.discardOpacity = data.discardOpacity;
+ g_pHyprOpenGL->m_RenderData.useNearestNeighbor = data.useNearestNeighbor;
+ g_pHyprOpenGL->m_bEndFrame = data.flipEndFrame;
+
+ CScopeGuard x = {[]() {
+ g_pHyprOpenGL->m_RenderData.primarySurfaceUVTopLeft = Vector2D(-1, -1);
+ g_pHyprOpenGL->m_RenderData.primarySurfaceUVBottomRight = Vector2D(-1, -1);
+ g_pHyprOpenGL->m_RenderData.useNearestNeighbor = false;
+ g_pHyprOpenGL->m_RenderData.clipBox = {};
+ g_pHyprOpenGL->m_RenderData.discardMode = 0;
+ g_pHyprOpenGL->m_RenderData.discardOpacity = 0;
+ g_pHyprOpenGL->m_RenderData.useNearestNeighbor = false;
+ g_pHyprOpenGL->m_bEndFrame = false;
+ g_pHyprOpenGL->m_RenderData.currentWindow.reset();
+ g_pHyprOpenGL->m_RenderData.currentLS.reset();
+ }};
+
+ if (!data.texture)
+ return;
+
+ const auto& TEXTURE = data.texture;
+
+ // this is bad, probably has been logged elsewhere. Means the texture failed
+ // uploading to the GPU.
+ if (!TEXTURE->m_iTexID)
+ return;
+
+ // explicit sync: wait for the timeline, if any
+ if (data.surface->syncobj && data.surface->syncobj->current.acquireTimeline) {
+ if (!g_pHyprOpenGL->waitForTimelinePoint(data.surface->syncobj->current.acquireTimeline->timeline, data.surface->syncobj->current.acquirePoint)) {
+ Debug::log(ERR, "Renderer: failed to wait for explicit timeline");
+ return;
+ }
+ }
+
+ const auto INTERACTIVERESIZEINPROGRESS = data.pWindow && g_pInputManager->currentlyDraggedWindow && g_pInputManager->dragMode == MBIND_RESIZE;
+ TRACY_GPU_ZONE("RenderSurface");
+
+ auto PSURFACE = CWLSurface::fromResource(data.surface);
+
+ const float ALPHA = data.alpha * data.fadeAlpha * (PSURFACE ? PSURFACE->m_pAlphaModifier : 1.F);
+ const bool BLUR = data.blur && (!TEXTURE->m_bOpaque || ALPHA < 1.F);
+
+ auto windowBox = getTexBox();
+
+ const auto PROJSIZEUNSCALED = windowBox.size();
+
+ windowBox.scale(data.pMonitor->scale);
+ windowBox.round();
+
+ if (windowBox.width <= 1 || windowBox.height <= 1) {
+ discard();
+ return;
+ }
+
+ const bool MISALIGNEDFSV1 = std::floor(data.pMonitor->scale) != data.pMonitor->scale /* Fractional */ && data.surface->current.scale == 1 /* fs protocol */ &&
+ windowBox.size() != data.surface->current.bufferSize /* misaligned */ && DELTALESSTHAN(windowBox.width, data.surface->current.bufferSize.x, 3) &&
+ DELTALESSTHAN(windowBox.height, data.surface->current.bufferSize.y, 3) /* off by one-or-two */ &&
+ (!data.pWindow || (!data.pWindow->m_vRealSize.isBeingAnimated() && !INTERACTIVERESIZEINPROGRESS)) /* not window or not animated/resizing */;
+
+ g_pHyprRenderer->calculateUVForSurface(data.pWindow, data.surface, data.pMonitor->self.lock(), data.mainSurface, windowBox.size(), PROJSIZEUNSCALED, MISALIGNEDFSV1);
+
+ // 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.
+ // UV will fixup it as well
+ if (MISALIGNEDFSV1)
+ g_pHyprOpenGL->m_RenderData.useNearestNeighbor = true;
+
+ float rounding = data.rounding;
+
+ rounding -= 1; // to fix a border issue
+
+ if (data.dontRound)
+ rounding = 0;
+
+ const bool WINDOWOPAQUE = data.pWindow && data.pWindow->m_pWLSurface->resource() == data.surface ? data.pWindow->opaque() : false;
+ const bool CANDISABLEBLEND = ALPHA >= 1.f && rounding == 0 && WINDOWOPAQUE;
+
+ if (CANDISABLEBLEND)
+ g_pHyprOpenGL->blend(false);
+ else
+ g_pHyprOpenGL->blend(true);
+
+ // FIXME: This is wrong and will bug the blur out as shit if the first surface
+ // is a subsurface that does NOT cover the entire frame. In such cases, we probably should fall back
+ // to what we do for misaligned surfaces (blur the entire thing and then render shit without blur)
+ if (data.surfaceCounter == 0 && !data.popup) {
+ if (BLUR)
+ g_pHyprOpenGL->renderTextureWithBlur(TEXTURE, &windowBox, ALPHA, data.surface, rounding, data.blockBlurOptimization, data.fadeAlpha);
+ else
+ g_pHyprOpenGL->renderTexture(TEXTURE, &windowBox, ALPHA, rounding, false, true);
+ } else {
+ if (BLUR && data.popup)
+ g_pHyprOpenGL->renderTextureWithBlur(TEXTURE, &windowBox, ALPHA, data.surface, rounding, true, data.fadeAlpha);
+ else
+ g_pHyprOpenGL->renderTexture(TEXTURE, &windowBox, ALPHA, rounding, false, true);
+ }
+
+ if (!g_pHyprRenderer->m_bBlockSurfaceFeedback)
+ data.surface->presentFeedback(data.when, data.pMonitor->self.lock());
+
+ g_pHyprOpenGL->blend(true);
+}
+
+CBox CSurfacePassElement::getTexBox() {
+ const double outputX = -data.pMonitor->vecPosition.x, outputY = -data.pMonitor->vecPosition.y;
+
+ const auto INTERACTIVERESIZEINPROGRESS = data.pWindow && g_pInputManager->currentlyDraggedWindow && g_pInputManager->dragMode == MBIND_RESIZE;
+ auto PSURFACE = CWLSurface::fromResource(data.surface);
+
+ CBox windowBox;
+ if (data.surface && data.mainSurface) {
+ windowBox = {(int)outputX + data.pos.x + data.localPos.x, (int)outputY + data.pos.y + data.localPos.y, data.w, data.h};
+
+ // however, if surface buffer w / h < box, we need to adjust them
+ const auto PWINDOW = PSURFACE ? PSURFACE->getWindow() : nullptr;
+
+ // center the surface if it's smaller than the viewport we assign it
+ if (PSURFACE && !PSURFACE->m_bFillIgnoreSmall && PSURFACE->small() /* guarantees PWINDOW */) {
+ const auto CORRECT = PSURFACE->correctSmallVec();
+ const auto SIZE = PSURFACE->getViewporterCorrectedSize();
+
+ if (!INTERACTIVERESIZEINPROGRESS) {
+ windowBox.translate(CORRECT);
+
+ windowBox.width = SIZE.x * (PWINDOW->m_vRealSize.value().x / PWINDOW->m_vReportedSize.x);
+ windowBox.height = SIZE.y * (PWINDOW->m_vRealSize.value().y / PWINDOW->m_vReportedSize.y);
+ } else {
+ windowBox.width = SIZE.x;
+ windowBox.height = SIZE.y;
+ }
+ }
+
+ } else { // here we clamp to 2, these might be some tiny specks
+ windowBox = {(int)outputX + data.pos.x + data.localPos.x, (int)outputY + data.pos.y + data.localPos.y, std::max((float)data.surface->current.size.x, 2.F),
+ std::max((float)data.surface->current.size.y, 2.F)};
+ if (data.pWindow && data.pWindow->m_vRealSize.isBeingAnimated() && data.surface && !data.mainSurface && data.squishOversized /* subsurface */) {
+ // adjust subsurfaces to the window
+ windowBox.width = (windowBox.width / data.pWindow->m_vReportedSize.x) * data.pWindow->m_vRealSize.value().x;
+ windowBox.height = (windowBox.height / data.pWindow->m_vReportedSize.y) * data.pWindow->m_vRealSize.value().y;
+ }
+ }
+
+ if (data.squishOversized) {
+ if (data.localPos.x + windowBox.width > data.w)
+ windowBox.width = data.w - data.localPos.x;
+ if (data.localPos.y + windowBox.height > data.h)
+ windowBox.height = data.h - data.localPos.y;
+ }
+
+ return windowBox;
+}
+
+bool CSurfacePassElement::needsLiveBlur() {
+ auto PSURFACE = CWLSurface::fromResource(data.surface);
+
+ const float ALPHA = data.alpha * data.fadeAlpha * (PSURFACE ? PSURFACE->m_pAlphaModifier : 1.F);
+ const bool BLUR = data.blur && (!data.texture || !data.texture->m_bOpaque || ALPHA < 1.F);
+
+ if (!data.pLS && !data.pWindow)
+ return BLUR;
+
+ const bool NEWOPTIM = g_pHyprOpenGL->shouldUseNewBlurOptimizations(data.pLS, data.pWindow);
+
+ return BLUR && !NEWOPTIM;
+}
+
+bool CSurfacePassElement::needsPrecomputeBlur() {
+ auto PSURFACE = CWLSurface::fromResource(data.surface);
+
+ const float ALPHA = data.alpha * data.fadeAlpha * (PSURFACE ? PSURFACE->m_pAlphaModifier : 1.F);
+ const bool BLUR = data.blur && (!data.texture || !data.texture->m_bOpaque || ALPHA < 1.F);
+
+ if (!data.pLS && !data.pWindow)
+ return BLUR;
+
+ const bool NEWOPTIM = g_pHyprOpenGL->shouldUseNewBlurOptimizations(data.pLS, data.pWindow);
+
+ return BLUR && NEWOPTIM;
+}
+
+std::optional<CBox> CSurfacePassElement::boundingBox() {
+ return getTexBox();
+}
+
+CRegion CSurfacePassElement::opaqueRegion() {
+ auto PSURFACE = CWLSurface::fromResource(data.surface);
+
+ const float ALPHA = data.alpha * data.fadeAlpha * (PSURFACE ? PSURFACE->m_pAlphaModifier : 1.F);
+
+ if (ALPHA < 1.F)
+ return {};
+
+ if (data.surface && data.surface->current.size == Vector2D{data.w, data.h}) {
+ CRegion opaqueSurf = data.surface->current.opaque.copy().intersect(CBox{{}, {data.w, data.h}});
+ const auto texBox = getTexBox();
+ opaqueSurf.scale(texBox.size() / Vector2D{data.w, data.h});
+ return opaqueSurf.translate(data.pos + data.localPos - data.pMonitor->vecPosition).expand(-data.rounding);
+ }
+
+ return data.texture && data.texture->m_bOpaque ? boundingBox()->expand(-data.rounding) : CRegion{};
+}
+
+void CSurfacePassElement::discard() {
+ if (!g_pHyprRenderer->m_bBlockSurfaceFeedback) {
+ Debug::log(TRACE, "discard for invisible surface");
+ data.surface->presentFeedback(data.when, data.pMonitor->self.lock(), true);
+ }
+}
diff --git a/src/render/pass/SurfacePassElement.hpp b/src/render/pass/SurfacePassElement.hpp
new file mode 100644
index 00000000..746c5a64
--- /dev/null
+++ b/src/render/pass/SurfacePassElement.hpp
@@ -0,0 +1,82 @@
+#pragma once
+#include "PassElement.hpp"
+#include <optional>
+
+class CWLSurfaceResource;
+class CTexture;
+class CSyncTimeline;
+
+class CSurfacePassElement : public IPassElement {
+ public:
+ struct SRenderData {
+ PHLMONITORREF pMonitor;
+ timespec* when = nullptr;
+ Vector2D pos, localPos;
+
+ // for iters
+ void* data = nullptr;
+ SP<CWLSurfaceResource> surface = nullptr;
+ SP<CTexture> texture = nullptr;
+ bool mainSurface = true;
+ double w = 0, h = 0;
+
+ // for rounding
+ bool dontRound = true;
+
+ // for fade
+ float fadeAlpha = 1.f;
+
+ // for alpha settings
+ float alpha = 1.f;
+
+ // for decorations (border)
+ bool decorate = false;
+
+ // for custom round values
+ int rounding = -1; // -1 means not set
+
+ // for blurring
+ bool blur = false;
+ bool blockBlurOptimization = false;
+
+ // only for windows, not popups
+ bool squishOversized = true;
+
+ // for calculating UV
+ PHLWINDOW pWindow;
+ PHLLS pLS;
+
+ bool popup = false;
+
+ // counts how many surfaces this pass has rendered
+ int surfaceCounter = 0;
+
+ CBox clipBox = {}; // scaled coordinates
+
+ uint32_t discardMode = 0;
+ float discardOpacity = 0.f;
+
+ bool useNearestNeighbor = false;
+
+ bool flipEndFrame = false;
+ };
+
+ CSurfacePassElement(const SRenderData& data);
+ virtual ~CSurfacePassElement() = default;
+
+ virtual void draw(const CRegion& damage);
+ virtual bool needsLiveBlur();
+ virtual bool needsPrecomputeBlur();
+ virtual std::optional<CBox> boundingBox();
+ virtual CRegion opaqueRegion();
+ virtual void discard();
+
+ virtual const char* passName() {
+ return "CSurfacePassElement";
+ }
+
+ private:
+ SRenderData data;
+
+ CBox getTexBox();
+}; \ No newline at end of file
diff --git a/src/render/pass/TexPassElement.cpp b/src/render/pass/TexPassElement.cpp
new file mode 100644
index 00000000..05f756dc
--- /dev/null
+++ b/src/render/pass/TexPassElement.cpp
@@ -0,0 +1,44 @@
+#include "TexPassElement.hpp"
+#include "../OpenGL.hpp"
+
+#include <hyprutils/utils/ScopeGuard.hpp>
+using namespace Hyprutils::Utils;
+
+CTexPassElement::CTexPassElement(const CTexPassElement::SRenderData& data_) : data(data_) {
+ ;
+}
+
+void CTexPassElement::draw(const CRegion& damage) {
+ g_pHyprOpenGL->m_bEndFrame = data.flipEndFrame;
+
+ CScopeGuard x = {[]() {
+ //
+ g_pHyprOpenGL->m_bEndFrame = false;
+ }};
+
+ if (data.replaceProjection)
+ g_pHyprOpenGL->m_RenderData.monitorProjection = *data.replaceProjection;
+ g_pHyprOpenGL->renderTextureInternalWithDamage(data.tex, &data.box, data.a, data.damage.empty() ? damage : data.damage, data.round, data.syncTimeline, data.syncPoint);
+ if (data.replaceProjection)
+ g_pHyprOpenGL->m_RenderData.monitorProjection = g_pHyprOpenGL->m_RenderData.pMonitor->projMatrix;
+}
+
+bool CTexPassElement::needsLiveBlur() {
+ return false; // TODO?
+}
+
+bool CTexPassElement::needsPrecomputeBlur() {
+ return false; // TODO?
+}
+
+std::optional<CBox> CTexPassElement::boundingBox() {
+ return data.box.copy().scale(1.F / g_pHyprOpenGL->m_RenderData.pMonitor->scale).round();
+}
+
+CRegion CTexPassElement::opaqueRegion() {
+ return {}; // TODO:
+}
+
+void CTexPassElement::discard() {
+ ;
+}
diff --git a/src/render/pass/TexPassElement.hpp b/src/render/pass/TexPassElement.hpp
new file mode 100644
index 00000000..bf896951
--- /dev/null
+++ b/src/render/pass/TexPassElement.hpp
@@ -0,0 +1,39 @@
+#pragma once
+#include "PassElement.hpp"
+#include <optional>
+
+class CWLSurfaceResource;
+class CTexture;
+class CSyncTimeline;
+
+class CTexPassElement : public IPassElement {
+ public:
+ struct SRenderData {
+ SP<CTexture> tex;
+ CBox box;
+ float a = 1.F;
+ CRegion damage;
+ int round = 0;
+ bool flipEndFrame = false;
+ SP<CSyncTimeline> syncTimeline;
+ int64_t syncPoint = 0;
+ std::optional<Mat3x3> replaceProjection;
+ };
+
+ CTexPassElement(const SRenderData& data);
+ virtual ~CTexPassElement() = default;
+
+ virtual void draw(const CRegion& damage);
+ virtual bool needsLiveBlur();
+ virtual bool needsPrecomputeBlur();
+ virtual std::optional<CBox> boundingBox();
+ virtual CRegion opaqueRegion();
+ virtual void discard();
+
+ virtual const char* passName() {
+ return "CTexPassElement";
+ }
+
+ private:
+ SRenderData data;
+}; \ No newline at end of file
diff --git a/src/render/pass/TextureMatteElement.cpp b/src/render/pass/TextureMatteElement.cpp
new file mode 100644
index 00000000..5aed1c9a
--- /dev/null
+++ b/src/render/pass/TextureMatteElement.cpp
@@ -0,0 +1,25 @@
+#include "TextureMatteElement.hpp"
+#include "../OpenGL.hpp"
+
+CTextureMatteElement::CTextureMatteElement(const CTextureMatteElement::STextureMatteData& data_) : data(data_) {
+ ;
+}
+
+void CTextureMatteElement::draw(const CRegion& damage) {
+ if (data.disableTransformAndModify) {
+ g_pHyprOpenGL->setMonitorTransformEnabled(true);
+ g_pHyprOpenGL->setRenderModifEnabled(false);
+ g_pHyprOpenGL->renderTextureMatte(data.tex, &data.box, *data.fb);
+ g_pHyprOpenGL->setRenderModifEnabled(true);
+ g_pHyprOpenGL->setMonitorTransformEnabled(false);
+ } else
+ g_pHyprOpenGL->renderTextureMatte(data.tex, &data.box, *data.fb);
+}
+
+bool CTextureMatteElement::needsLiveBlur() {
+ return false;
+}
+
+bool CTextureMatteElement::needsPrecomputeBlur() {
+ return false;
+} \ No newline at end of file
diff --git a/src/render/pass/TextureMatteElement.hpp b/src/render/pass/TextureMatteElement.hpp
new file mode 100644
index 00000000..bf673946
--- /dev/null
+++ b/src/render/pass/TextureMatteElement.hpp
@@ -0,0 +1,29 @@
+#pragma once
+#include "PassElement.hpp"
+#include "../Framebuffer.hpp"
+
+class CTexture;
+
+class CTextureMatteElement : public IPassElement {
+ public:
+ struct STextureMatteData {
+ CBox box;
+ SP<CTexture> tex;
+ SP<CFramebuffer> fb;
+ bool disableTransformAndModify = false;
+ };
+
+ CTextureMatteElement(const STextureMatteData& data_);
+ virtual ~CTextureMatteElement() = default;
+
+ virtual void draw(const CRegion& damage);
+ virtual bool needsLiveBlur();
+ virtual bool needsPrecomputeBlur();
+
+ virtual const char* passName() {
+ return "CTextureMatteElement";
+ }
+
+ private:
+ STextureMatteData data;
+}; \ No newline at end of file