diff --git a/src/config/ConfigManager.cpp b/src/config/ConfigManager.cpp index 074213823..9b6e29e40 100644 --- a/src/config/ConfigManager.cpp +++ b/src/config/ConfigManager.cpp @@ -571,7 +571,7 @@ CConfigManager::CConfigManager() { m_pConfig->addConfigValue("xwayland:force_zero_scaling", Hyprlang::INT{0}); m_pConfig->addConfigValue("opengl:nvidia_anti_flicker", Hyprlang::INT{1}); - m_pConfig->addConfigValue("opengl:force_introspection", Hyprlang::INT{2}); + m_pConfig->addConfigValue("opengl:force_introspection", Hyprlang::INT{1}); // TODO: remove this. I don't think it does us any good to disable intro. m_pConfig->addConfigValue("cursor:no_hardware_cursors", Hyprlang::INT{2}); m_pConfig->addConfigValue("cursor:no_break_fs_vrr", Hyprlang::INT{0}); @@ -612,6 +612,7 @@ CConfigManager::CConfigManager() { m_pConfig->addConfigValue("render:explicit_sync_kms", Hyprlang::INT{2}); m_pConfig->addConfigValue("render:direct_scanout", Hyprlang::INT{0}); m_pConfig->addConfigValue("render:expand_undersized_textures", Hyprlang::INT{1}); + m_pConfig->addConfigValue("render:xp_mode", Hyprlang::INT{0}); m_pConfig->addConfigValue("ecosystem:no_update_news", Hyprlang::INT{0}); diff --git a/src/debug/HyprNotificationOverlay.cpp b/src/debug/HyprNotificationOverlay.cpp index 7bcfad829..504ad6c9b 100644 --- a/src/debug/HyprNotificationOverlay.cpp +++ b/src/debug/HyprNotificationOverlay.cpp @@ -3,6 +3,7 @@ #include "HyprNotificationOverlay.hpp" #include "../Compositor.hpp" #include "../config/ConfigValue.hpp" +#include "../render/pass/TexPassElement.hpp" static inline auto iconBackendFromLayout(PangoLayout* layout) { // preference: Nerd > FontAwesome > text @@ -241,8 +242,12 @@ void CHyprNotificationOverlay::draw(PHLMONITOR pMonitor) { glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, MONSIZE.x, MONSIZE.y, 0, GL_RGBA, GL_UNSIGNED_BYTE, DATA); - CBox pMonBox = {0, 0, MONSIZE.x, MONSIZE.y}; - g_pHyprOpenGL->renderTexture(m_pTexture, &pMonBox, 1.f); + CTexPassElement::SRenderData data; + data.tex = m_pTexture; + data.box = {0, 0, MONSIZE.x, MONSIZE.y}; + data.a = 1.F; + + g_pHyprRenderer->m_sRenderPass.add(makeShared(data)); } bool CHyprNotificationOverlay::hasAny() { diff --git a/src/desktop/LayerSurface.cpp b/src/desktop/LayerSurface.cpp index bfa77a686..c8134219b 100644 --- a/src/desktop/LayerSurface.cpp +++ b/src/desktop/LayerSurface.cpp @@ -210,7 +210,7 @@ void CLayerSurface::onUnmap() { } // make a snapshot and start fade - g_pHyprOpenGL->makeLayerSnapshot(self.lock()); + g_pHyprRenderer->makeLayerSnapshot(self.lock()); startAnimation(false); diff --git a/src/events/Windows.cpp b/src/events/Windows.cpp index 437301b7f..c17c425b4 100644 --- a/src/events/Windows.cpp +++ b/src/events/Windows.cpp @@ -680,7 +680,7 @@ void Events::listener_unmapWindow(void* owner, void* data) { g_pCompositor->setWindowFullscreenInternal(PWINDOW, FSMODE_NONE); // Allow the renderer to catch the last frame. - g_pHyprOpenGL->makeWindowSnapshot(PWINDOW); + g_pHyprRenderer->makeWindowSnapshot(PWINDOW); // swallowing if (valid(PWINDOW->m_pSwallowed)) { diff --git a/src/helpers/WLClasses.hpp b/src/helpers/WLClasses.hpp index 9f2836e7d..0a6ef55d4 100644 --- a/src/helpers/WLClasses.hpp +++ b/src/helpers/WLClasses.hpp @@ -17,47 +17,6 @@ class CWLSurfaceResource; AQUAMARINE_FORWARD(ISwitch); -struct SRenderData { - PHLMONITORREF pMonitor; - timespec* when; - double x, y; - - // for iters - void* data = nullptr; - SP surface = nullptr; - double w, h; - - // 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; - - bool popup = false; - - // counts how many surfaces this pass has rendered - int surfaceCounter = 0; -}; - struct SSwipeGesture { PHLWORKSPACE pWorkspaceBegin = nullptr; diff --git a/src/hyprerror/HyprError.cpp b/src/hyprerror/HyprError.cpp index 74a3030ca..d3cd82731 100644 --- a/src/hyprerror/HyprError.cpp +++ b/src/hyprerror/HyprError.cpp @@ -2,6 +2,7 @@ #include "HyprError.hpp" #include "../Compositor.hpp" #include "../config/ConfigValue.hpp" +#include "../render/pass/TexPassElement.hpp" #include using namespace Hyprutils::Utils; @@ -206,7 +207,12 @@ void CHyprError::draw() { m_bMonitorChanged = false; - g_pHyprOpenGL->renderTexture(m_pTexture, &monbox, m_fFadeOpacity.value(), 0); + CTexPassElement::SRenderData data; + data.tex = m_pTexture; + data.box = monbox; + data.a = m_fFadeOpacity.value(); + + g_pHyprRenderer->m_sRenderPass.add(makeShared(data)); } void CHyprError::destroy() { diff --git a/src/managers/PointerManager.cpp b/src/managers/PointerManager.cpp index 3e3ece983..bc166825a 100644 --- a/src/managers/PointerManager.cpp +++ b/src/managers/PointerManager.cpp @@ -8,6 +8,7 @@ #include "../protocols/core/Compositor.hpp" #include "../protocols/core/Seat.hpp" #include "eventLoop/EventLoopManager.hpp" +#include "../render/pass/TexPassElement.hpp" #include "SeatManager.hpp" #include #include @@ -551,7 +552,15 @@ void CPointerManager::renderSoftwareCursorsFor(PHLMONITOR pMonitor, timespec* no box.x = std::round(box.x); box.y = std::round(box.y); - g_pHyprOpenGL->renderTextureWithDamage(texture, &box, &damage, 1.F, 0, false, false, currentCursorImage.waitTimeline, currentCursorImage.waitPoint); + CTexPassElement::SRenderData data; + data.tex = texture; + data.box = box.round(); + data.syncTimeline = currentCursorImage.waitTimeline; + data.syncPoint = currentCursorImage.waitPoint; + g_pHyprRenderer->m_sRenderPass.add(makeShared(data)); + + currentCursorImage.waitTimeline.reset(); + currentCursorImage.waitPoint = 0; if (currentCursorImage.surface) currentCursorImage.surface->resource()->frame(now); diff --git a/src/protocols/core/Compositor.cpp b/src/protocols/core/Compositor.cpp index 89f2a4cbc..ee1e62244 100644 --- a/src/protocols/core/Compositor.cpp +++ b/src/protocols/core/Compositor.cpp @@ -522,11 +522,14 @@ void CWLSurfaceResource::updateCursorShm(CRegion damage) { } } -void CWLSurfaceResource::presentFeedback(timespec* when, PHLMONITOR pMonitor) { +void CWLSurfaceResource::presentFeedback(timespec* when, PHLMONITOR pMonitor, bool discarded) { frame(when); auto FEEDBACK = makeShared(self.lock()); FEEDBACK->attachMonitor(pMonitor); - FEEDBACK->presented(); + if (discarded) + FEEDBACK->discarded(); + else + FEEDBACK->presented(); PROTO::presentation->queueData(FEEDBACK); if (!pMonitor || !pMonitor->outTimeline || !syncobj) diff --git a/src/protocols/core/Compositor.hpp b/src/protocols/core/Compositor.hpp index c036041a7..2a6f96f64 100644 --- a/src/protocols/core/Compositor.hpp +++ b/src/protocols/core/Compositor.hpp @@ -124,7 +124,7 @@ class CWLSurfaceResource { void breadthfirst(std::function, const Vector2D&, void*)> fn, void* data); CRegion accumulateCurrentBufferDamage(); - void presentFeedback(timespec* when, PHLMONITOR pMonitor); + void presentFeedback(timespec* when, PHLMONITOR pMonitor, bool discarded = false); void lockPendingState(); void unlockPendingState(); diff --git a/src/protocols/core/DataDevice.cpp b/src/protocols/core/DataDevice.cpp index b4bb8b00f..9fc49a448 100644 --- a/src/protocols/core/DataDevice.cpp +++ b/src/protocols/core/DataDevice.cpp @@ -4,6 +4,7 @@ #include "../../managers/PointerManager.hpp" #include "../../managers/eventLoop/EventLoopManager.hpp" #include "../../Compositor.hpp" +#include "../../render/pass/TexPassElement.hpp" #include "Seat.hpp" #include "Compositor.hpp" #include "../../xwayland/XWayland.hpp" @@ -794,7 +795,11 @@ void CWLDataDeviceProtocol::renderDND(PHLMONITOR pMonitor, timespec* when) { const auto POS = g_pInputManager->getMouseCoordsInternal(); CBox box = CBox{POS, dnd.dndSurface->current.size}.translate(-pMonitor->vecPosition + g_pPointerManager->cursorSizeLogical() / 2.F).scale(pMonitor->scale); - g_pHyprOpenGL->renderTexture(dnd.dndSurface->current.texture, &box, 1.F); + + CTexPassElement::SRenderData data; + data.tex = dnd.dndSurface->current.texture; + data.box = box; + g_pHyprRenderer->m_sRenderPass.add(makeShared(data)); box = CBox{POS, dnd.dndSurface->current.size}.translate(g_pPointerManager->cursorSizeLogical() / 2.F).expand(5); g_pHyprRenderer->damageBox(&box); diff --git a/src/render/OpenGL.cpp b/src/render/OpenGL.cpp index 4339fd40d..d9f19feaa 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 #include #include @@ -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 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 tex, CBox* pBox, CRegion* damage, float alpha, int round, bool discardActive, bool allowCustomUV, +void CHyprOpenGLImpl::renderTextureWithDamage(SP tex, CBox* pBox, const CRegion& damage, float alpha, int round, bool discardActive, bool allowCustomUV, SP waitTimeline, uint64_t waitPoint) { RASSERT(m_RenderData.pMonitor, "Tried to render texture without begin()!"); @@ -1374,8 +1378,8 @@ void CHyprOpenGLImpl::renderTextureWithDamage(SP tex, CBox* pBox, CReg scissor((CBox*)nullptr); } -void CHyprOpenGLImpl::renderTextureInternalWithDamage(SP tex, CBox* pBox, float alpha, CRegion* damage, int round, bool discardActive, bool noAA, bool allowCustomUV, - bool allowDim, SP waitTimeline, uint64_t waitPoint) { +void CHyprOpenGLImpl::renderTextureInternalWithDamage(SP tex, CBox* pBox, float alpha, const CRegion& damage, int round, bool discardActive, bool noAA, + bool allowCustomUV, bool allowDim, SP 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 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 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 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 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 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()); } bool CHyprOpenGLImpl::preBlurQueued() { static auto PBLURNEWOPTIMIZE = CConfigValue("decoration:blur:new_optimizations"); static auto PBLUR = CConfigValue("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 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 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 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 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("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); + g_pHyprRenderer->m_sRenderPass.add(makeShared(CClearPassElement::SClearData{CHyprColor(0, 0, 0, 0)})); - // clear stuff outside of mirrored area (e.g. when changing to mirrored) - clear(CHyprColor(0, 0, 0, 0)); + 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); - renderTexture(PFB->getTexture(), &monbox, 1.f, 0, false, false); - - // reset matrix for further drawing - m_RenderData.monitorProjection = monitor->projMatrix; + g_pHyprRenderer->m_sRenderPass.add(makeShared(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(data)); } } diff --git a/src/render/OpenGL.hpp b/src/render/OpenGL.hpp index 51f38708c..0b72d4383 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 #include @@ -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 stencilTex = makeShared(); - 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, CBox*, float a, int round = 0, bool discardActive = false, bool allowCustomUV = false); - void renderTextureWithDamage(SP, CBox*, CRegion* damage, float a, int round = 0, bool discardActive = false, bool allowCustomUV = false, + void renderTextureWithDamage(SP, CBox*, const CRegion& damage, float a, int round = 0, bool discardActive = false, bool allowCustomUV = false, SP waitTimeline = nullptr, uint64_t waitPoint = 0); void renderTextureWithBlur(SP, CBox*, float a, SP 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 m_mWindowFramebuffers; std::map m_mLayerFramebuffers; std::map 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, CBox* pBox, float a, CRegion* damage, int round = 0, bool discardOpaque = false, bool noAA = false, + void renderTextureInternalWithDamage(SP, CBox* pBox, float a, const CRegion& damage, int round = 0, bool discardOpaque = false, bool noAA = false, bool allowCustomUV = false, bool allowDim = false, SP = nullptr, uint64_t waitPoint = 0); void renderTexturePrimitive(SP 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 g_pHyprOpenGL; diff --git a/src/render/Renderer.cpp b/src/render/Renderer.cpp index c873842b0..0f8db3bb1 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 @@ -154,148 +158,6 @@ CHyprRenderer::~CHyprRenderer() { wl_event_source_remove(m_pCursorTicker); } -static void renderSurface(SP 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("decoration:dim_around"); - static auto PBLUR = CConfigValue("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("decoration:dim_around"); + static auto PBLUR = CConfigValue("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(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("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(data)); renderdata.blur = false; } renderdata.surfaceCounter = 0; - pWindow->m_pWLSurface->resource()->breadthfirst([](SP s, const Vector2D& offset, void* data) { renderSurface(s, offset.x, offset.y, data); }, - &renderdata); + pWindow->m_pWLSurface->resource()->breadthfirst( + [this, &renderdata, &pWindow](SP 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(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; + const Vector2D oldPos = renderdata.pos; + renderdata.pos += pos; - popup->m_pWLSurface->resource()->breadthfirst([](SP s, const Vector2D& offset, void* data) { renderSurface(s, offset.x, offset.y, data); }, - data); + popup->m_pWLSurface->resource()->breadthfirst( + [this, &renderdata](SP s, const Vector2D& offset, void* data) { + renderdata.localPos = offset; + renderdata.texture = s->current.texture; + renderdata.surface = s; + renderdata.mainSurface = false; + m_sRenderPass.add(makeShared(renderdata)); + renderdata.surfaceCounter++; + }, + data); - rd->x = oldPos.x; - rd->y = oldPos.y; + 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("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(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(); + 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; + 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_RenderData.clipBox = CBox{0, 0, pMonitor->vecSize.x, pMonitor->vecSize.y}.scale(pMonitor->scale); - - 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 s, const Vector2D& offset, void* data) { renderSurface(s, offset.x, offset.y, data); }, &renderdata); + pLayer->surface->resource()->breadthfirst( + [this, &renderdata, &pLayer](SP 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(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(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("decoration:blur:input_methods"); static auto PBLURIGNOREA = CConfigValue("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; + renderdata.discardMode |= DISCARD_ALPHA; + renderdata.discardOpacity = *PBLURIGNOREA; } - SURF->breadthfirst([](SP 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; + SURF->breadthfirst( + [this, &renderdata, &SURF](SP 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(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 s, const Vector2D& offset, void* data) { renderSurface(s, offset.x, offset.y, data); }, &renderdata); + renderdata.surface->breadthfirst( + [this, &renderdata, &pSurface](SP 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(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("decoration:blur:enabled"); static auto PRENDERTEX = CConfigValue("misc:disable_hyprland_logo"); static auto PBACKGROUNDCOLOR = CConfigValue("misc:background_color"); + static auto PXPMODE = CConfigValue("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::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::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(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(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("decoration:blur:enabled"); + 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}; - // 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("decoration:blur:size"); - static auto PBLURPASSES = CConfigValue("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 - - 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(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(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("decoration:blur:enabled"); - static auto PBLURSIZE = CConfigValue("decoration:blur:size"); - static auto PBLURPASSES = CConfigValue("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("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(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(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(data)); +} diff --git a/src/render/Renderer.hpp b/src/render/Renderer.hpp index 717a3285a..054f0eea3 100644 --- a/src/render/Renderer.hpp +++ b/src/render/Renderer.hpp @@ -67,9 +67,6 @@ class CHyprRenderer { bool fixMisalignedFSV1 = false); std::tuple 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 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&, 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 m_pCurrentRenderbuffer = nullptr; - SP m_pCurrentBuffer; - eRenderMode m_eRenderMode = RENDER_MODE_NORMAL; - - bool m_bNvidia = false; + SP 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 c6989d2c0..7ac4ab68b 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 c54da4ddc..048b18985 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 93490f314..4a793eab7 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; - 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); + 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(); + } + + g_pHyprRenderer->m_sRenderPass.add(makeShared(data)); } eDecorationType CHyprBorderDecoration::getDecorationType() { diff --git a/src/render/decorations/CHyprDropShadowDecoration.cpp b/src/render/decorations/CHyprDropShadowDecoration.cpp index 38ced9af5..21242b570 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(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 650f8c928..fe0f89527 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 0aee6157a..f870e71b4 100644 --- a/src/render/decorations/CHyprGroupBarDecoration.cpp +++ b/src/render/decorations/CHyprGroupBarDecoration.cpp @@ -4,6 +4,8 @@ #include "managers/LayoutManager.hpp" #include #include +#include "../pass/TexPassElement.hpp" +#include "../pass/RectPassElement.hpp" // shared things to conserve VRAM static SP m_tGradientActive = makeShared(); @@ -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(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(data)); + } } if (*PRENDERTITLES) { CTitleTex* pTitleTex = textureFromTitle(m_dwGroupMembers[WINDOWINDEX]->m_szTitle); if (!pTitleTex) - pTitleTex = - m_sTitleTexs.titleTexs - .emplace_back(std::make_unique(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(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(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(); - 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 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 000000000..635533d0f --- /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 000000000..3a529640c --- /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 000000000..bafa73c7e --- /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 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 000000000..402424572 --- /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 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 000000000..268e55896 --- /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 000000000..f3d181d46 --- /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 000000000..31385acb7 --- /dev/null +++ b/src/render/pass/Pass.cpp @@ -0,0 +1,157 @@ +#include "Pass.hpp" +#include "../OpenGL.hpp" +#include +#include +#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 el) { + m_vPassElements.emplace_back(makeShared(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("decoration:blur:size"); + static auto PBLURPASSES = CConfigValue("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 000000000..7f332c19c --- /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 elem); + void clear(); + + CRegion render(const CRegion& damage_); + + private: + CRegion damage; + + struct SPassElementData { + CRegion elementDamage; + SP element; + bool discard = false; + }; + + std::vector> m_vPassElements; + + SP 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 000000000..737c6b273 --- /dev/null +++ b/src/render/pass/PassElement.cpp @@ -0,0 +1,17 @@ +#include "PassElement.hpp" + +std::optional 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 000000000..025840f4e --- /dev/null +++ b/src/render/pass/PassElement.hpp @@ -0,0 +1,18 @@ +#pragma once + +#include "../../defines.hpp" +#include + +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 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 000000000..9fa3471d8 --- /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 000000000..6c52c8158 --- /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 000000000..73727e585 --- /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 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 000000000..32111a434 --- /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 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 000000000..22910c967 --- /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 000000000..715e7bfb0 --- /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 000000000..38de52108 --- /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 +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 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 000000000..746c5a646 --- /dev/null +++ b/src/render/pass/SurfacePassElement.hpp @@ -0,0 +1,82 @@ +#pragma once +#include "PassElement.hpp" +#include + +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 surface = nullptr; + SP 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 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 000000000..05f756dc4 --- /dev/null +++ b/src/render/pass/TexPassElement.cpp @@ -0,0 +1,44 @@ +#include "TexPassElement.hpp" +#include "../OpenGL.hpp" + +#include +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 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 000000000..bf896951d --- /dev/null +++ b/src/render/pass/TexPassElement.hpp @@ -0,0 +1,39 @@ +#pragma once +#include "PassElement.hpp" +#include + +class CWLSurfaceResource; +class CTexture; +class CSyncTimeline; + +class CTexPassElement : public IPassElement { + public: + struct SRenderData { + SP tex; + CBox box; + float a = 1.F; + CRegion damage; + int round = 0; + bool flipEndFrame = false; + SP syncTimeline; + int64_t syncPoint = 0; + std::optional replaceProjection; + }; + + CTexPassElement(const SRenderData& data); + virtual ~CTexPassElement() = default; + + virtual void draw(const CRegion& damage); + virtual bool needsLiveBlur(); + virtual bool needsPrecomputeBlur(); + virtual std::optional 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 000000000..5aed1c9ac --- /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 000000000..bf673946b --- /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 tex; + SP 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