diff options
| author | UjinT34 <41110182+UjinT34@users.noreply.github.com> | 2026-03-07 21:21:23 +0300 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2026-03-07 18:21:23 +0000 |
| commit | 02f30ea15b349d1ed98b608ed06ec88c834592dd (patch) | |
| tree | 5148a3935c43b64f209f2b13e2a0b1bbe987444b /src/render/Renderer.cpp | |
| parent | b227efc8492468cc925014a6c8446a3d044b01e8 (diff) | |
renderer: refactor gl renderer (#13488)
Diffstat (limited to 'src/render/Renderer.cpp')
| -rw-r--r-- | src/render/Renderer.cpp | 1157 |
1 files changed, 645 insertions, 512 deletions
diff --git a/src/render/Renderer.cpp b/src/render/Renderer.cpp index 8dc2d82c..9872b83e 100644 --- a/src/render/Renderer.cpp +++ b/src/render/Renderer.cpp @@ -36,6 +36,7 @@ #include "helpers/MainLoopExecutor.hpp" #include "helpers/Monitor.hpp" #include "macros.hpp" +#include "../managers/screenshare/ScreenshareManager.hpp" #include "pass/TexPassElement.hpp" #include "pass/ClearPassElement.hpp" #include "pass/RectPassElement.hpp" @@ -49,9 +50,8 @@ #include "render/Framebuffer.hpp" #include "render/OpenGL.hpp" #include "render/Texture.hpp" -#include "render/decorations/CHyprDropShadowDecoration.hpp" -#include "render/gl/GLFramebuffer.hpp" -#include "render/gl/GLTexture.hpp" +#include "render/pass/BorderPassElement.hpp" +#include "render/pass/PreBlurElement.hpp" #include <hyprutils/math/Mat3x3.hpp> #include <hyprutils/math/Region.hpp> #include <hyprutils/math/Vector2D.hpp> @@ -76,10 +76,9 @@ static int cursorTicker(void* data) { return 0; } -CHyprRenderer::CHyprRenderer() { +IHyprRenderer::IHyprRenderer() { m_globalTimer.reset(); pushMonitorTransformEnabled(false); - initAssets(); if (g_pCompositor->m_aqBackend->hasSession()) { size_t drmDevices = 0; @@ -208,16 +207,16 @@ CHyprRenderer::CHyprRenderer() { g_pEventLoopManager->addTimer(m_renderUnfocusedTimer); } -CHyprRenderer::~CHyprRenderer() { +IHyprRenderer::~IHyprRenderer() { if (m_cursorTicker) wl_event_source_remove(m_cursorTicker); } -WP<CHyprOpenGLImpl> CHyprRenderer::glBackend() { +WP<CHyprOpenGLImpl> IHyprRenderer::glBackend() { return g_pHyprOpenGL; } -bool CHyprRenderer::shouldRenderWindow(PHLWINDOW pWindow, PHLMONITOR pMonitor) { +bool IHyprRenderer::shouldRenderWindow(PHLWINDOW pWindow, PHLMONITOR pMonitor) { if (!pWindow->visibleOnMonitor(pMonitor)) return false; @@ -283,7 +282,7 @@ bool CHyprRenderer::shouldRenderWindow(PHLWINDOW pWindow, PHLMONITOR pMonitor) { return false; } -bool CHyprRenderer::shouldRenderWindow(PHLWINDOW pWindow) { +bool IHyprRenderer::shouldRenderWindow(PHLWINDOW pWindow) { if (!validMapped(pWindow)) return false; @@ -310,7 +309,7 @@ bool CHyprRenderer::shouldRenderWindow(PHLWINDOW pWindow) { return false; } -void CHyprRenderer::renderWorkspaceWindowsFullscreen(PHLMONITOR pMonitor, PHLWORKSPACE pWorkspace, const Time::steady_tp& time) { +void IHyprRenderer::renderWorkspaceWindowsFullscreen(PHLMONITOR pMonitor, PHLWORKSPACE pWorkspace, const Time::steady_tp& time) { PHLWINDOW pWorkspaceWindow = nullptr; Event::bus()->m_events.render.stage.emit(RENDER_PRE_WINDOWS); @@ -401,7 +400,7 @@ void CHyprRenderer::renderWorkspaceWindowsFullscreen(PHLMONITOR pMonitor, PHLWOR } } -void CHyprRenderer::renderWorkspaceWindows(PHLMONITOR pMonitor, PHLWORKSPACE pWorkspace, const Time::steady_tp& time) { +void IHyprRenderer::renderWorkspaceWindows(PHLMONITOR pMonitor, PHLWORKSPACE pWorkspace, const Time::steady_tp& time) { PHLWINDOW lastWindow; Event::bus()->m_events.render.stage.emit(RENDER_PRE_WINDOWS); @@ -499,7 +498,20 @@ void CHyprRenderer::renderWorkspaceWindows(PHLMONITOR pMonitor, PHLWORKSPACE pWo } } -void CHyprRenderer::renderWindow(PHLWINDOW pWindow, PHLMONITOR pMonitor, const Time::steady_tp& time, bool decorate, eRenderPassMode mode, bool ignorePosition, bool standalone) { +void IHyprRenderer::bindOffMain() { + RASSERT(m_renderData.pMonitor->m_offMainFB->isAllocated(), "IHyprRenderer::beginRender should allocate monitor FBs") + + m_renderData.pMonitor->m_offMainFB->bind(); + draw(makeUnique<CClearPassElement>(CClearPassElement::SClearData{{0, 0, 0, 0}}), {}); + m_renderData.currentFB = m_renderData.pMonitor->m_offMainFB; +} + +void IHyprRenderer::bindBackOnMain() { + m_renderData.mainFB->bind(); + m_renderData.currentFB = m_renderData.mainFB; +} + +void IHyprRenderer::renderWindow(PHLWINDOW pWindow, PHLMONITOR pMonitor, const Time::steady_tp& time, bool decorate, eRenderPassMode mode, bool ignorePosition, bool standalone) { if (pWindow->isHidden() && !standalone) return; @@ -596,7 +608,7 @@ void CHyprRenderer::renderWindow(PHLWINDOW pWindow, PHLMONITOR pMonitor, const T const bool TRANSFORMERSPRESENT = !pWindow->m_transformers.empty(); if (TRANSFORMERSPRESENT) { - g_pHyprOpenGL->bindOffMain(); + bindOffMain(); for (auto const& t : pWindow->m_transformers) { t->preWindowRender(&renderdata); @@ -672,8 +684,8 @@ void CHyprRenderer::renderWindow(PHLWINDOW pWindow, PHLMONITOR pMonitor, const T last = t->transform(last); } - g_pHyprOpenGL->bindBackOnMain(); - g_pHyprOpenGL->renderOffToMain(dc<CGLFramebuffer*>(last)); + bindBackOnMain(); + renderOffToMain(last); } } @@ -757,134 +769,68 @@ void CHyprRenderer::renderWindow(PHLWINDOW pWindow, PHLMONITOR pMonitor, const T m_renderData.currentWindow.reset(); } -void CHyprRenderer::draw(WP<IPassElement> element, const CRegion& damage) { - if (!element) - return; +void IHyprRenderer::drawRect(CRectPassElement* element, const CRegion& damage) { + auto& data = element->m_data; - switch (element->type()) { - case EK_BORDER: draw(dc<CBorderPassElement*>(element.get()), damage); break; - case EK_CLEAR: draw(dc<CClearPassElement*>(element.get()), damage); break; - case EK_FRAMEBUFFER: draw(dc<CFramebufferElement*>(element.get()), damage); break; - case EK_PRE_BLUR: draw(dc<CPreBlurElement*>(element.get()), damage); break; - case EK_RECT: draw(dc<CRectPassElement*>(element.get()), damage); break; - case EK_HINTS: draw(dc<CRendererHintsPassElement*>(element.get()), damage); break; - case EK_SHADOW: draw(dc<CShadowPassElement*>(element.get()), damage); break; - case EK_SURFACE: draw(dc<CSurfacePassElement*>(element.get()), damage); break; - case EK_TEXTURE: draw(dc<CTexPassElement*>(element.get()), damage); break; - case EK_TEXTURE_MATTE: draw(dc<CTextureMatteElement*>(element.get()), damage); break; - default: Log::logger->log(Log::WARN, "Unimplimented draw for {}", element->passName()); - } -} - -void CHyprRenderer::draw(CBorderPassElement* element, const CRegion& damage) { - const auto& m_data = element->m_data; - if (m_data.hasGrad2) - g_pHyprOpenGL->renderBorder( - m_data.box, m_data.grad1, m_data.grad2, m_data.lerp, - {.round = m_data.round, .roundingPower = m_data.roundingPower, .borderSize = m_data.borderSize, .a = m_data.a, .outerRound = m_data.outerRound}); - else - g_pHyprOpenGL->renderBorder( - m_data.box, m_data.grad1, - {.round = m_data.round, .roundingPower = m_data.roundingPower, .borderSize = m_data.borderSize, .a = m_data.a, .outerRound = m_data.outerRound}); -} + if (data.box.w <= 0 || data.box.h <= 0) + return; -void CHyprRenderer::draw(CClearPassElement* element, const CRegion& damage) { - const auto& m_data = element->m_data; - g_pHyprOpenGL->clear(m_data.color); -}; + if (!data.clipBox.empty()) + m_renderData.clipBox = data.clipBox; -void CHyprRenderer::draw(CFramebufferElement* element, const CRegion& damage) { - const auto& m_data = element->m_data; - SP<IFramebuffer> fb = nullptr; + data.modifiedBox = data.box; + m_renderData.renderModif.applyToBox(data.modifiedBox); - if (m_data.main) { - switch (m_data.framebufferID) { - case FB_MONITOR_RENDER_MAIN: fb = m_renderData.mainFB; break; - case FB_MONITOR_RENDER_CURRENT: fb = m_renderData.currentFB; break; - case FB_MONITOR_RENDER_OUT: fb = m_renderData.outFB; break; - } + CBox transformedBox = data.box; + transformedBox.transform(Math::wlTransformToHyprutils(Math::invertTransform(m_renderData.pMonitor->m_transform)), m_renderData.pMonitor->m_transformedSize.x, + m_renderData.pMonitor->m_transformedSize.y); - if (!fb) { - Log::logger->log(Log::ERR, "BUG THIS: CFramebufferElement::draw: main but null"); - return; - } + data.TOPLEFT[0] = sc<float>(transformedBox.x); + data.TOPLEFT[1] = sc<float>(transformedBox.y); + data.FULLSIZE[0] = sc<float>(transformedBox.width); + data.FULLSIZE[1] = sc<float>(transformedBox.height); - } else { - switch (m_data.framebufferID) { - case FB_MONITOR_RENDER_EXTRA_OFFLOAD: fb = m_renderData.pMonitor->m_offloadFB; break; - case FB_MONITOR_RENDER_EXTRA_MIRROR: fb = m_renderData.pMonitor->m_mirrorFB; break; - case FB_MONITOR_RENDER_EXTRA_MIRROR_SWAP: fb = m_renderData.pMonitor->m_mirrorSwapFB; break; - case FB_MONITOR_RENDER_EXTRA_OFF_MAIN: fb = m_renderData.pMonitor->m_offMainFB; break; - case FB_MONITOR_RENDER_EXTRA_MONITOR_MIRROR: fb = m_renderData.pMonitor->m_monitorMirrorFB; break; - case FB_MONITOR_RENDER_EXTRA_BLUR: fb = m_renderData.pMonitor->m_blurFB; break; - } + data.drawRegion = data.color.a == 1.F || !data.blur ? damage : m_renderData.damage; - if (!fb) { - Log::logger->log(Log::ERR, "BUG THIS: CFramebufferElement::draw: not main but null"); - return; - } + 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}; + data.drawRegion = damageClip.intersect(data.drawRegion); } - fb->bind(); -}; + draw(element, damage); -void CHyprRenderer::draw(CPreBlurElement* element, const CRegion& damage) { - g_pHyprOpenGL->preBlurForCurrentMonitor(); -}; + m_renderData.clipBox = {}; +} -void CHyprRenderer::draw(CRectPassElement* element, const CRegion& damage) { - const auto& m_data = element->m_data; - if (m_data.box.w <= 0 || m_data.box.h <= 0) - return; +void IHyprRenderer::drawHints(CRendererHintsPassElement* element, const CRegion& damage) { + const auto m_data = element->m_data; + if (m_data.renderModif.has_value()) + m_renderData.renderModif = *m_data.renderModif; +} - if (!m_data.clipBox.empty()) - m_renderData.clipBox = m_data.clipBox; +void IHyprRenderer::drawPreBlur(CPreBlurElement* element, const CRegion& damage) { + TRACY_GPU_ZONE("RenderPreBlurForCurrentMonitor"); - if (m_data.color.a == 1.F || !m_data.blur) - g_pHyprOpenGL->renderRect(m_data.box, m_data.color, {.damage = &damage, .round = m_data.round, .roundingPower = m_data.roundingPower}); - else - g_pHyprOpenGL->renderRect(m_data.box, m_data.color, - {.round = m_data.round, .roundingPower = m_data.roundingPower, .blur = true, .blurA = m_data.blurA, .xray = m_data.xray}); + const auto SAVEDRENDERMODIF = m_renderData.renderModif; + m_renderData.renderModif = {}; // fix shit - m_renderData.clipBox = {}; -}; + // make the fake dmg + CRegion fakeDamage{0, 0, m_renderData.pMonitor->m_transformedSize.x, m_renderData.pMonitor->m_transformedSize.y}; -void CHyprRenderer::draw(CRendererHintsPassElement* element, const CRegion& damage) { - const auto& m_data = element->m_data; - if (m_data.renderModif.has_value()) - m_renderData.renderModif = *m_data.renderModif; -}; + draw(element, fakeDamage); -void CHyprRenderer::draw(CShadowPassElement* element, const CRegion& damage) { - const auto& m_data = element->m_data; - m_data.deco->render(m_renderData.pMonitor.lock(), m_data.a); -}; + m_renderData.pMonitor->m_blurFBDirty = false; + m_renderData.pMonitor->m_blurFBShouldRender = false; -void CHyprRenderer::draw(CSurfacePassElement* element, const CRegion& damage) { - const auto& m_data = element->m_data; + m_renderData.renderModif = SAVEDRENDERMODIF; +} - m_renderData.currentWindow = m_data.pWindow; - m_renderData.surface = m_data.surface; - m_renderData.currentLS = m_data.pLS; - m_renderData.clipBox = m_data.clipBox; - m_renderData.discardMode = m_data.discardMode; - m_renderData.discardOpacity = m_data.discardOpacity; - m_renderData.useNearestNeighbor = m_data.useNearestNeighbor; - pushMonitorTransformEnabled(m_data.flipEndFrame); +void IHyprRenderer::drawSurface(CSurfacePassElement* element, const CRegion& damage) { + const auto m_data = element->m_data; - CScopeGuard x = {[&]() { - m_renderData.primarySurfaceUVTopLeft = Vector2D(-1, -1); - m_renderData.primarySurfaceUVBottomRight = Vector2D(-1, -1); - m_renderData.useNearestNeighbor = false; - m_renderData.clipBox = {}; - m_renderData.clipRegion = {}; - m_renderData.discardMode = 0; - m_renderData.discardOpacity = 0; - m_renderData.useNearestNeighbor = false; - g_pHyprRenderer->popMonitorTransformEnabled(); - m_renderData.currentWindow.reset(); - m_renderData.surface.reset(); - m_renderData.currentLS.reset(); + CScopeGuard x = {[]() { + g_pHyprRenderer->m_renderData.primarySurfaceUVTopLeft = Vector2D(-1, -1); + g_pHyprRenderer->m_renderData.primarySurfaceUVBottomRight = Vector2D(-1, -1); }}; if (!m_data.texture) @@ -926,8 +872,8 @@ void CHyprRenderer::draw(CSurfacePassElement* element, const CRegion& damage) { calculateUVForSurface(m_data.pWindow, m_data.surface, m_data.pMonitor->m_self.lock(), m_data.mainSurface, windowBox.size(), PROJSIZEUNSCALED, MISALIGNEDFSV1); - auto cancelRender = false; - m_renderData.clipRegion = element->visibleRegion(cancelRender); + auto cancelRender = false; + auto clipRegion = element->visibleRegion(cancelRender); if (cancelRender) return; @@ -952,129 +898,244 @@ void CHyprRenderer::draw(CSurfacePassElement* element, const CRegion& damage) { const bool CANDISABLEBLEND = ALPHA >= 1.f && OVERALL_ALPHA >= 1.f && rounding <= 0 && WINDOWOPAQUE; if (CANDISABLEBLEND) - g_pHyprOpenGL->blend(false); + blend(false); else - g_pHyprOpenGL->blend(true); + 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 (m_data.surfaceCounter == 0 && !m_data.popup) { if (BLUR) - g_pHyprOpenGL->renderTexture(TEXTURE, windowBox, - { - .surface = m_data.surface, - .a = ALPHA, - .blur = true, - .blurA = m_data.fadeAlpha, - .overallA = OVERALL_ALPHA, - .round = rounding, - .roundingPower = roundingPower, - .allowCustomUV = true, - .blockBlurOptimization = m_data.blockBlurOptimization, - }); + draw(makeUnique<CTexPassElement>(CTexPassElement::SRenderData{ + .tex = TEXTURE, + .box = windowBox, + .a = ALPHA, + .blurA = m_data.fadeAlpha, + .overallA = OVERALL_ALPHA, + .round = rounding, + .roundingPower = roundingPower, + .blur = true, + .blockBlurOptimization = m_data.blockBlurOptimization, + .allowCustomUV = true, + .surface = m_data.surface, + .discardMode = m_data.discardMode, + .discardOpacity = m_data.discardOpacity, + .clipRegion = clipRegion, + .currentLS = m_data.pLS, + }), + m_renderData.damage.copy().intersect(windowBox)); else - g_pHyprOpenGL->renderTexture(TEXTURE, windowBox, - {.a = ALPHA * OVERALL_ALPHA, .round = rounding, .roundingPower = roundingPower, .discardActive = false, .allowCustomUV = true}); + draw(makeUnique<CTexPassElement>(CTexPassElement::SRenderData{ + .tex = TEXTURE, + .box = windowBox, + .a = ALPHA * OVERALL_ALPHA, + .round = rounding, + .roundingPower = roundingPower, + .discardActive = false, + .allowCustomUV = true, + .surface = m_data.surface, + .discardMode = m_data.discardMode, + .discardOpacity = m_data.discardOpacity, + .clipRegion = clipRegion, + .currentLS = m_data.pLS, + }), + m_renderData.damage.copy().intersect(windowBox)); } else { if (BLUR && m_data.popup) - g_pHyprOpenGL->renderTexture(TEXTURE, windowBox, - { - .surface = m_data.surface, - .a = ALPHA, - .blur = true, - .blurA = m_data.fadeAlpha, - .overallA = OVERALL_ALPHA, - .round = rounding, - .roundingPower = roundingPower, - .allowCustomUV = true, - .blockBlurOptimization = true, - }); + draw(makeUnique<CTexPassElement>(CTexPassElement::SRenderData{ + .tex = TEXTURE, + .box = windowBox, + .a = ALPHA, + .blurA = m_data.fadeAlpha, + .overallA = OVERALL_ALPHA, + .round = rounding, + .roundingPower = roundingPower, + .blur = true, + .blockBlurOptimization = true, + .allowCustomUV = true, + .surface = m_data.surface, + .discardMode = m_data.discardMode, + .discardOpacity = m_data.discardOpacity, + .clipRegion = clipRegion, + .currentLS = m_data.pLS, + }), + m_renderData.damage.copy().intersect(windowBox)); else - g_pHyprOpenGL->renderTexture(TEXTURE, windowBox, - {.a = ALPHA * OVERALL_ALPHA, .round = rounding, .roundingPower = roundingPower, .discardActive = false, .allowCustomUV = true}); - } + draw(makeUnique<CTexPassElement>(CTexPassElement::SRenderData{ + .tex = TEXTURE, + .box = windowBox, + .a = ALPHA * OVERALL_ALPHA, + .round = rounding, + .roundingPower = roundingPower, + .discardActive = false, + .allowCustomUV = true, + .surface = m_data.surface, + .discardMode = m_data.discardMode, + .discardOpacity = m_data.discardOpacity, + .clipRegion = clipRegion, + .currentLS = m_data.pLS, + }), + m_renderData.damage.copy().intersect(windowBox)); + } + + blend(true); +}; - if (!g_pHyprRenderer->m_bBlockSurfaceFeedback) - m_data.surface->presentFeedback(m_data.when, m_data.pMonitor->m_self.lock()); +void IHyprRenderer::preDrawSurface(CSurfacePassElement* element, const CRegion& damage) { + m_renderData.clipBox = element->m_data.clipBox; + m_renderData.useNearestNeighbor = element->m_data.useNearestNeighbor; + pushMonitorTransformEnabled(element->m_data.flipEndFrame); + m_renderData.currentWindow = element->m_data.pWindow; + + drawSurface(element, damage); + + if (!m_bBlockSurfaceFeedback) + element->m_data.surface->presentFeedback(element->m_data.when, element->m_data.pMonitor->m_self.lock()); // add async (dmabuf) buffers to usedBuffers so we can handle release later // sync (shm) buffers will be released in commitState, so no need to track them here - if (m_data.surface->m_current.buffer && !m_data.surface->m_current.buffer->isSynchronous()) - g_pHyprRenderer->m_usedAsyncBuffers.emplace_back(m_data.surface->m_current.buffer); + if (element->m_data.surface->m_current.buffer && !element->m_data.surface->m_current.buffer->isSynchronous()) + m_usedAsyncBuffers.emplace_back(element->m_data.surface->m_current.buffer); - g_pHyprOpenGL->blend(true); -}; + m_renderData.clipBox = {}; + m_renderData.useNearestNeighbor = false; + popMonitorTransformEnabled(); + m_renderData.currentWindow.reset(); +} -void CHyprRenderer::draw(CTexPassElement* element, const CRegion& damage) { - const auto& m_data = element->m_data; - pushMonitorTransformEnabled(m_data.flipEndFrame); +void IHyprRenderer::drawTex(CTexPassElement* element, const CRegion& damage) { + if (!element->m_data.clipBox.empty()) + m_renderData.clipBox = element->m_data.clipBox; - CScopeGuard x = {[&]() { - // + pushMonitorTransformEnabled(element->m_data.flipEndFrame); + if (element->m_data.useMirrorProjection) + setProjectionType(RPT_MIRROR); + + m_renderData.surface = element->m_data.surface; + + CScopeGuard x = {[useMirrorProjection = element->m_data.useMirrorProjection]() { g_pHyprRenderer->popMonitorTransformEnabled(); - m_renderData.clipBox = {}; - if (m_data.useMirrorProjection) - setProjectionType(RPT_MONITOR); - if (m_data.ignoreAlpha.has_value()) - m_renderData.discardMode = 0; + if (useMirrorProjection) + g_pHyprRenderer->setProjectionType(RPT_MONITOR); + g_pHyprRenderer->m_renderData.surface.reset(); }}; - if (!m_data.clipBox.empty()) - m_renderData.clipBox = m_data.clipBox; + if (element->m_data.blur) { + // make a damage region for this window + CRegion texDamage{m_renderData.damage}; + texDamage.intersect(element->m_data.box.x, element->m_data.box.y, element->m_data.box.width, element->m_data.box.height); - if (m_data.useMirrorProjection) - setProjectionType(RPT_MIRROR); + // While renderTextureInternalWithDamage will clip the blur as well, + // clipping texDamage here allows blur generation to be optimized. + if (!element->m_data.clipRegion.empty()) + texDamage.intersect(element->m_data.clipRegion); - if (m_data.ignoreAlpha.has_value()) { - m_renderData.discardMode = DISCARD_ALPHA; - m_renderData.discardOpacity = *m_data.ignoreAlpha; - } - - if (m_data.blur) { - g_pHyprOpenGL->renderTexture(m_data.tex, m_data.box, - { - .a = m_data.a, - .blur = true, - .blurA = m_data.blurA, - .overallA = 1.F, - .round = m_data.round, - .roundingPower = m_data.roundingPower, - .blockBlurOptimization = m_data.blockBlurOptimization.value_or(false), - }); - } else { - g_pHyprOpenGL->renderTexture(m_data.tex, m_data.box, - {.damage = m_data.damage.empty() ? &damage : &m_data.damage, .a = m_data.a, .round = m_data.round, .roundingPower = m_data.roundingPower}); - } -}; + if (texDamage.empty()) + return; + + m_renderData.renderModif.applyToRegion(texDamage); + + element->m_data.damage = texDamage; + + // amazing hack: the surface has an opaque region! + const auto& surface = element->m_data.surface; + const auto& box = element->m_data.box; + CRegion inverseOpaque; + if (element->m_data.a >= 1.f && surface && std::round(surface->m_current.size.x * m_renderData.pMonitor->m_scale) == box.w && + std::round(surface->m_current.size.y * m_renderData.pMonitor->m_scale) == box.h) { + pixman_box32_t surfbox = {0, 0, surface->m_current.size.x * surface->m_current.scale, surface->m_current.size.y * surface->m_current.scale}; + inverseOpaque = surface->m_current.opaque; + inverseOpaque.invert(&surfbox).intersect(0, 0, surface->m_current.size.x * surface->m_current.scale, surface->m_current.size.y * surface->m_current.scale); + + if (inverseOpaque.empty()) { + element->m_data.blur = false; + draw(element, damage); + m_renderData.clipBox = {}; + return; + } + } else + inverseOpaque = {0, 0, element->m_data.box.width, element->m_data.box.height}; + + inverseOpaque.scale(m_renderData.pMonitor->m_scale); + element->m_data.blockBlurOptimization = + element->m_data.blockBlurOptimization.value_or(false) || !shouldUseNewBlurOptimizations(element->m_data.currentLS.lock(), m_renderData.currentWindow.lock()); + + // vvv TODO: layered blur fbs? + if (element->m_data.blockBlurOptimization) { + inverseOpaque.translate(box.pos()); + m_renderData.renderModif.applyToRegion(inverseOpaque); + inverseOpaque.intersect(element->m_data.damage); + element->m_data.blurredBG = blurMainFramebuffer(element->m_data.a, &inverseOpaque); + m_renderData.currentFB->bind(); + } else + element->m_data.blurredBG = m_renderData.pMonitor->m_blurFB->getTexture(); + + draw(element, damage); + } else + draw(element, damage); -void CHyprRenderer::draw(CTextureMatteElement* element, const CRegion& damage) { - const auto& m_data = element->m_data; + m_renderData.clipBox = {}; +} + +void IHyprRenderer::drawTexMatte(CTextureMatteElement* element, const CRegion& damage) { + if (m_renderData.damage.empty()) + return; + + const auto m_data = element->m_data; if (m_data.disableTransformAndModify) { pushMonitorTransformEnabled(true); - g_pHyprOpenGL->setRenderModifEnabled(false); - g_pHyprOpenGL->renderTextureMatte(m_data.tex, m_data.box, m_data.fb); - g_pHyprOpenGL->setRenderModifEnabled(true); + m_renderData.renderModif.enabled = false; + draw(element, damage); + m_renderData.renderModif.enabled = true; popMonitorTransformEnabled(); } else - g_pHyprOpenGL->renderTextureMatte(m_data.tex, m_data.box, m_data.fb); -}; + draw(element, damage); +} + +void IHyprRenderer::draw(WP<IPassElement> element, const CRegion& damage) { + if (!element) + return; -void CHyprRenderer::pushMonitorTransformEnabled(bool enabled) { + switch (element->type()) { + case EK_BORDER: draw(dc<CBorderPassElement*>(element.get()), damage); break; + case EK_CLEAR: draw(dc<CClearPassElement*>(element.get()), damage); break; + case EK_FRAMEBUFFER: draw(dc<CFramebufferElement*>(element.get()), damage); break; + case EK_PRE_BLUR: drawPreBlur(dc<CPreBlurElement*>(element.get()), damage); break; + case EK_RECT: drawRect(dc<CRectPassElement*>(element.get()), damage); break; + case EK_HINTS: drawHints(dc<CRendererHintsPassElement*>(element.get()), damage); break; + case EK_SHADOW: draw(dc<CShadowPassElement*>(element.get()), damage); break; + case EK_SURFACE: preDrawSurface(dc<CSurfacePassElement*>(element.get()), damage); break; + case EK_TEXTURE: drawTex(dc<CTexPassElement*>(element.get()), damage); break; + case EK_TEXTURE_MATTE: drawTexMatte(dc<CTextureMatteElement*>(element.get()), damage); break; + default: Log::logger->log(Log::WARN, "Unimplimented draw for {}", element->passName()); + } +} + +bool IHyprRenderer::preBlurQueued(PHLMONITORREF pMonitor) { + static auto PBLURNEWOPTIMIZE = CConfigValue<Hyprlang::INT>("decoration:blur:new_optimizations"); + static auto PBLUR = CConfigValue<Hyprlang::INT>("decoration:blur:enabled"); + + if (!pMonitor) + return false; + return m_renderData.pMonitor->m_blurFBDirty && *PBLURNEWOPTIMIZE && *PBLUR && m_renderData.pMonitor->m_blurFBShouldRender; +} + +void IHyprRenderer::pushMonitorTransformEnabled(bool enabled) { m_monitorTransformStack.push(enabled); m_monitorTransformEnabled = enabled; } -void CHyprRenderer::popMonitorTransformEnabled() { +void IHyprRenderer::popMonitorTransformEnabled() { m_monitorTransformStack.pop(); m_monitorTransformEnabled = m_monitorTransformStack.top(); } -bool CHyprRenderer::monitorTransformEnabled() { +bool IHyprRenderer::monitorTransformEnabled() { return m_monitorTransformEnabled; } -SP<ITexture> CHyprRenderer::createTexture(const SP<Aquamarine::IBuffer> buffer, bool keepDataCopy) { +SP<ITexture> IHyprRenderer::createTexture(const SP<Aquamarine::IBuffer> buffer, bool keepDataCopy) { if (!buffer) return createTexture(); @@ -1104,7 +1165,7 @@ SP<ITexture> CHyprRenderer::createTexture(const SP<Aquamarine::IBuffer> buffer, return tex; } -void CHyprRenderer::renderLayer(PHLLS pLayer, PHLMONITOR pMonitor, const Time::steady_tp& time, bool popups, bool lockscreen) { +void IHyprRenderer::renderLayer(PHLLS pLayer, PHLMONITOR pMonitor, const Time::steady_tp& time, bool popups, bool lockscreen) { if (!pLayer) return; @@ -1117,7 +1178,7 @@ void CHyprRenderer::renderLayer(PHLLS pLayer, PHLMONITOR pMonitor, const Time::s if (*PDIMAROUND && pLayer->m_ruleApplicator->dimAround().valueOrDefault() && !m_bRenderingSnapshot && !popups) { CRectPassElement::SRectData data; - data.box = {0, 0, m_renderData.pMonitor->m_transformedSize.x, m_renderData.pMonitor->m_transformedSize.y}; + data.box = {0, 0, pMonitor->m_transformedSize.x, pMonitor->m_transformedSize.y}; data.color = CHyprColor(0, 0, 0, *PDIMAROUND * pLayer->m_alpha->value()); m_renderPass.add(makeUnique<CRectPassElement>(data)); } @@ -1199,7 +1260,7 @@ void CHyprRenderer::renderLayer(PHLLS pLayer, PHLMONITOR pMonitor, const Time::s } } -void CHyprRenderer::renderIMEPopup(CInputPopup* pPopup, PHLMONITOR pMonitor, const Time::steady_tp& time) { +void IHyprRenderer::renderIMEPopup(CInputPopup* pPopup, PHLMONITOR pMonitor, const Time::steady_tp& time) { const auto POS = pPopup->globalBox().pos(); CSurfacePassElement::SRenderData renderdata = {pMonitor, time, POS}; @@ -1239,7 +1300,7 @@ void CHyprRenderer::renderIMEPopup(CInputPopup* pPopup, PHLMONITOR pMonitor, con &renderdata); } -void CHyprRenderer::renderSessionLockSurface(WP<SSessionLockSurface> pSurface, PHLMONITOR pMonitor, const Time::steady_tp& time) { +void IHyprRenderer::renderSessionLockSurface(WP<SSessionLockSurface> pSurface, PHLMONITOR pMonitor, const Time::steady_tp& time) { CSurfacePassElement::SRenderData renderdata = {pMonitor, time, pMonitor->m_position, pMonitor->m_position}; renderdata.blur = false; @@ -1266,7 +1327,7 @@ void CHyprRenderer::renderSessionLockSurface(WP<SSessionLockSurface> pSurface, P &renderdata); } -void CHyprRenderer::renderAllClientsForWorkspace(PHLMONITOR pMonitor, PHLWORKSPACE pWorkspace, const Time::steady_tp& time, const Vector2D& translate, const float& scale) { +void IHyprRenderer::renderAllClientsForWorkspace(PHLMONITOR pMonitor, PHLWORKSPACE pWorkspace, const Time::steady_tp& time, const Vector2D& translate, const float& scale) { static auto PDIMSPECIAL = CConfigValue<Hyprlang::FLOAT>("decoration:dim_special"); static auto PBLURSPECIAL = CConfigValue<Hyprlang::INT>("decoration:blur:special"); static auto PBLUR = CConfigValue<Hyprlang::INT>("decoration:blur:enabled"); @@ -1283,10 +1344,6 @@ void CHyprRenderer::renderAllClientsForWorkspace(PHLMONITOR pMonitor, PHLWORKSPA return; } - // todo: matrices are buggy atm for some reason, but probably would be preferable in the long run - // g_pHyprOpenGL->saveMatrix(); - // g_pHyprOpenGL->setMatrixScaleTranslate(translate, scale); - SRenderModifData RENDERMODIFDATA; if (translate != Vector2D{0, 0}) RENDERMODIFDATA.modifs.emplace_back(std::make_pair<>(SRenderModifData::eRenderModifType::RMOD_TYPE_TRANSLATE, translate)); @@ -1343,7 +1400,8 @@ void CHyprRenderer::renderAllClientsForWorkspace(PHLMONITOR pMonitor, PHLWORKSPA } // pre window pass - g_pHyprOpenGL->preWindowPass(); + if (preBlurQueued(pMonitor)) + m_renderPass.add(makeUnique<CPreBlurElement>()); if UNLIKELY /* subjective? */ (pWorkspace->m_hasFullscreenWindow) renderWorkspaceWindowsFullscreen(pMonitor, pWorkspace, time); @@ -1423,22 +1481,95 @@ void CHyprRenderer::renderAllClientsForWorkspace(PHLMONITOR pMonitor, PHLWORKSPA } renderDragIcon(pMonitor, time); +} + +SP<ITexture> IHyprRenderer::getBackground(PHLMONITOR pMonitor) { + + if (m_backgroundResourceFailed) + return nullptr; + + if (!m_backgroundResource) { + // queue the asset to be created + requestBackgroundResource(); + return nullptr; + } - //g_pHyprOpenGL->restoreMatrix(); + if (!m_backgroundResource->m_ready) + return nullptr; + + Log::logger->log(Log::DEBUG, "Creating a texture for BGTex"); + SP<ITexture> backgroundTexture = createTexture(m_backgroundResource->m_asset.cairoSurface->cairo()); + if (!backgroundTexture->ok()) + return nullptr; + Log::logger->log(Log::DEBUG, "Background created for monitor {}", pMonitor->m_name); + + // clear the resource after we're done using it + g_pEventLoopManager->doLater([this] { m_backgroundResource.reset(); }); + + // set the animation to start for fading this background in nicely + pMonitor->m_backgroundOpacity->setValueAndWarp(0.F); + *pMonitor->m_backgroundOpacity = 1.F; + + return backgroundTexture; } -void CHyprRenderer::renderBackground(PHLMONITOR pMonitor) { +void IHyprRenderer::renderBackground(PHLMONITOR pMonitor) { static auto PRENDERTEX = CConfigValue<Hyprlang::INT>("misc:disable_hyprland_logo"); static auto PBACKGROUNDCOLOR = CConfigValue<Hyprlang::INT>("misc:background_color"); + static auto PNOSPLASH = CConfigValue<Hyprlang::INT>("misc:disable_splash_rendering"); if (*PRENDERTEX /* inverted cfg flag */ || pMonitor->m_backgroundOpacity->isBeingAnimated()) m_renderPass.add(makeUnique<CClearPassElement>(CClearPassElement::SClearData{CHyprColor(*PBACKGROUNDCOLOR)})); - if (!*PRENDERTEX) - g_pHyprOpenGL->clearWithTex(); // will apply the hypr "wallpaper" + if (!*PRENDERTEX) { + static auto PBACKGROUNDCOLOR = CConfigValue<Hyprlang::INT>("misc:background_color"); + + if (!pMonitor->m_background) + pMonitor->m_background = getBackground(pMonitor); + + if (!pMonitor->m_background) + m_renderPass.add(makeUnique<CClearPassElement>(CClearPassElement::SClearData{CHyprColor(*PBACKGROUNDCOLOR)})); + else { + CTexPassElement::SRenderData data; + const double MONRATIO = m_renderData.pMonitor->m_transformedSize.x / m_renderData.pMonitor->m_transformedSize.y; + const double WPRATIO = pMonitor->m_background->m_size.x / pMonitor->m_background->m_size.y; + Vector2D origin; + double scale = 1.0; + + if (MONRATIO > WPRATIO) { + scale = m_renderData.pMonitor->m_transformedSize.x / pMonitor->m_background->m_size.x; + origin.y = (m_renderData.pMonitor->m_transformedSize.y - pMonitor->m_background->m_size.y * scale) / 2.0; + } else { + scale = m_renderData.pMonitor->m_transformedSize.y / pMonitor->m_background->m_size.y; + origin.x = (m_renderData.pMonitor->m_transformedSize.x - pMonitor->m_background->m_size.x * scale) / 2.0; + } + + if (MONRATIO != WPRATIO) + m_renderPass.add(makeUnique<CClearPassElement>(CClearPassElement::SClearData{CHyprColor(*PBACKGROUNDCOLOR)})); + + data.box = {origin, pMonitor->m_background->m_size * scale}; + data.a = m_renderData.pMonitor->m_backgroundOpacity->value(); + data.tex = pMonitor->m_background; + m_renderPass.add(makeUnique<CTexPassElement>(std::move(data))); + } + } + + if (!*PNOSPLASH) { + auto monitorSize = pMonitor->m_transformedSize; + if (!pMonitor->m_splash) + pMonitor->m_splash = renderSplash([this, pMonitor](auto width, auto height, const auto DATA) { return createTexture(width, height, DATA); }, monitorSize.y / 76, + monitorSize.x, monitorSize.y); + + if (pMonitor->m_splash) { + CTexPassElement::SRenderData data; + data.box = {{(monitorSize.x - pMonitor->m_splash->m_size.x) / 2.0, monitorSize.y * 0.98 - pMonitor->m_splash->m_size.y}, pMonitor->m_splash->m_size}; + data.tex = pMonitor->m_splash; + m_renderPass.add(makeUnique<CTexPassElement>(std::move(data))); + } + } } -void CHyprRenderer::requestBackgroundResource() { +void IHyprRenderer::requestBackgroundResource() { if (m_backgroundResource) return; @@ -1492,7 +1623,7 @@ void CHyprRenderer::requestBackgroundResource() { g_pAsyncResourceGatherer->enqueue(m_backgroundResource); } -std::string CHyprRenderer::resolveAssetPath(const std::string& filename) { +std::string IHyprRenderer::resolveAssetPath(const std::string& filename) { std::string fullPath; for (auto& e : ASSET_PATHS) { std::string p = std::string{e} + "/hypr/" + filename; @@ -1513,7 +1644,7 @@ std::string CHyprRenderer::resolveAssetPath(const std::string& filename) { return fullPath; } -SP<ITexture> CHyprRenderer::loadAsset(const std::string& filename) { +SP<ITexture> IHyprRenderer::loadAsset(const std::string& filename) { const std::string fullPath = resolveAssetPath(filename); @@ -1535,11 +1666,15 @@ SP<ITexture> CHyprRenderer::loadAsset(const std::string& filename) { return tex; } -bool CHyprRenderer::shouldUseNewBlurOptimizations(PHLLS pLayer, PHLWINDOW pWindow) { +SP<ITexture> IHyprRenderer::getBlurTexture(PHLMONITORREF pMonitor) { + return pMonitor->m_blurFB->getTexture(); +} + +bool IHyprRenderer::shouldUseNewBlurOptimizations(PHLLS pLayer, PHLWINDOW pWindow) { static auto PBLURNEWOPTIMIZE = CConfigValue<Hyprlang::INT>("decoration:blur:new_optimizations"); static auto PBLURXRAY = CConfigValue<Hyprlang::INT>("decoration:blur:xray"); - if (!m_renderData.pMonitor || !m_renderData.pMonitor->m_blurFB || !m_renderData.pMonitor->m_blurFB->getTexture()) + if (!getBlurTexture(m_renderData.pMonitor)) return false; if (pWindow && pWindow->m_ruleApplicator->xray().hasValue() && !pWindow->m_ruleApplicator->xray().valueOrDefault()) @@ -1557,7 +1692,7 @@ bool CHyprRenderer::shouldUseNewBlurOptimizations(PHLLS pLayer, PHLWINDOW pWindo return false; } -void CHyprRenderer::initMissingAssetTexture() { +void IHyprRenderer::initMissingAssetTexture() { const auto CAIROSURFACE = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, 512, 512); const auto CAIRO = cairo_create(CAIROSURFACE); @@ -1584,13 +1719,13 @@ void CHyprRenderer::initMissingAssetTexture() { m_missingAssetTexture = tex; } -void CHyprRenderer::initAssets() { +void IHyprRenderer::initAssets() { initMissingAssetTexture(); m_screencopyDeniedTexture = renderText("Permission denied to share screen", Colors::WHITE, 20); } -SP<ITexture> CHyprRenderer::renderText(const std::string& text, CHyprColor col, int pt, bool italic, const std::string& fontFamily, int maxWidth, int weight) { +SP<ITexture> IHyprRenderer::renderText(const std::string& text, CHyprColor col, int pt, bool italic, const std::string& fontFamily, int maxWidth, int weight) { static auto FONT = CConfigValue<std::string>("misc:font_family"); const auto FONTFAMILY = fontFamily.empty() ? *FONT : fontFamily; @@ -1659,7 +1794,7 @@ SP<ITexture> CHyprRenderer::renderText(const std::string& text, CHyprColor col, return tex; } -void CHyprRenderer::ensureLockTexturesRendered(bool load) { +void IHyprRenderer::ensureLockTexturesRendered(bool load) { static bool loaded = false; if (loaded == load) @@ -1682,7 +1817,7 @@ void CHyprRenderer::ensureLockTexturesRendered(bool load) { } } -void CHyprRenderer::renderLockscreen(PHLMONITOR pMonitor, const Time::steady_tp& now, const CBox& geometry) { +void IHyprRenderer::renderLockscreen(PHLMONITOR pMonitor, const Time::steady_tp& now, const CBox& geometry) { TRACY_GPU_ZONE("RenderLockscreen"); const bool LOCKED = g_pSessionLockManager->isSessionLocked(); @@ -1720,7 +1855,7 @@ void CHyprRenderer::renderLockscreen(PHLMONITOR pMonitor, const Time::steady_tp& } } -void CHyprRenderer::renderSessionLockPrimer(PHLMONITOR pMonitor) { +void IHyprRenderer::renderSessionLockPrimer(PHLMONITOR pMonitor) { static auto PSESSIONLOCKXRAY = CConfigValue<Hyprlang::INT>("misc:session_lock_xray"); if (*PSESSIONLOCKXRAY) return; @@ -1732,7 +1867,7 @@ void CHyprRenderer::renderSessionLockPrimer(PHLMONITOR pMonitor) { m_renderPass.add(makeUnique<CRectPassElement>(data)); } -void CHyprRenderer::renderSessionLockMissing(PHLMONITOR pMonitor) { +void IHyprRenderer::renderSessionLockMissing(PHLMONITOR pMonitor) { const bool ANY_PRESENT = g_pSessionLockManager->anySessionLockSurfacesPresent(); // ANY_PRESENT: render image2, without instructions. Lock still "alive", unless texture dead @@ -1774,7 +1909,7 @@ static std::optional<Vector2D> getSurfaceExpectedSize(PHLWINDOW pWindow, SP<CWLS return std::nullopt; } -void CHyprRenderer::calculateUVForSurface(PHLWINDOW pWindow, SP<CWLSurfaceResource> pSurface, PHLMONITOR pMonitor, bool main, const Vector2D& projSize, +void IHyprRenderer::calculateUVForSurface(PHLWINDOW pWindow, SP<CWLSurfaceResource> pSurface, PHLMONITOR pMonitor, bool main, const Vector2D& projSize, const Vector2D& projSizeUnscaled, bool fixMisalignedFSV1) { if (!pWindow || !pWindow->m_isX11) { static auto PEXPANDEDGES = CConfigValue<Hyprlang::INT>("render:expand_undersized_textures"); @@ -1875,6 +2010,96 @@ void CHyprRenderer::calculateUVForSurface(PHLWINDOW pWindow, SP<CWLSurfaceResour } } +bool IHyprRenderer::beginRender(PHLMONITOR pMonitor, CRegion& damage, eRenderMode mode, SP<IHLBuffer> buffer, SP<IFramebuffer> fb, bool simple) { + m_renderPass.clear(); + m_renderMode = mode; + m_renderData.pMonitor = pMonitor; + + if (simple) + setProjectionType(fb ? fb->m_size : buffer->m_texture->m_size); + else + setProjectionType(RPT_MONITOR); + + if (!simple) { + const auto DRM_FORMAT = fb ? fb->m_drmFormat : pMonitor->m_output->state->state().drmFormat; + + // ensure a framebuffer for the monitor exists + if (!m_renderData.pMonitor->m_offloadFB || m_renderData.pMonitor->m_offloadFB->m_size != pMonitor->m_pixelSize || + DRM_FORMAT != m_renderData.pMonitor->m_offloadFB->m_drmFormat) { + if (!m_renderData.pMonitor->m_stencilTex || m_renderData.pMonitor->m_stencilTex->m_size != pMonitor->m_pixelSize) + m_renderData.pMonitor->m_stencilTex = createStencilTexture(m_renderData.pMonitor->m_pixelSize.x, m_renderData.pMonitor->m_pixelSize.y); + + m_renderData.pMonitor->m_offloadFB = createFB("offload"); + m_renderData.pMonitor->m_mirrorFB = createFB("mirror"); + m_renderData.pMonitor->m_mirrorSwapFB = createFB("mirrorSwap"); + m_renderData.pMonitor->m_offMainFB = createFB("offMain"); + m_renderData.pMonitor->m_monitorMirrorFB = createFB("monitorMirror"); + m_renderData.pMonitor->m_blurFB = createFB("blur"); + + // add stencil before FB allocation to avoid reallocs + m_renderData.pMonitor->m_offloadFB->addStencil(m_renderData.pMonitor->m_stencilTex); + m_renderData.pMonitor->m_mirrorFB->addStencil(m_renderData.pMonitor->m_stencilTex); + m_renderData.pMonitor->m_mirrorSwapFB->addStencil(m_renderData.pMonitor->m_stencilTex); + m_renderData.pMonitor->m_offMainFB->addStencil(m_renderData.pMonitor->m_stencilTex); + + m_renderData.pMonitor->m_offloadFB->alloc(pMonitor->m_pixelSize.x, pMonitor->m_pixelSize.y, DRM_FORMAT); + m_renderData.pMonitor->m_mirrorFB->alloc(pMonitor->m_pixelSize.x, pMonitor->m_pixelSize.y, DRM_FORMAT); + m_renderData.pMonitor->m_mirrorSwapFB->alloc(pMonitor->m_pixelSize.x, pMonitor->m_pixelSize.y, DRM_FORMAT); + m_renderData.pMonitor->m_offMainFB->alloc(pMonitor->m_pixelSize.x, pMonitor->m_pixelSize.y, DRM_FORMAT); + } + } + + const bool HAS_MIRROR_FB = g_pHyprRenderer->m_renderData.pMonitor->m_monitorMirrorFB && g_pHyprRenderer->m_renderData.pMonitor->m_monitorMirrorFB->isAllocated(); + const bool NEEDS_COPY_FB = needsACopyFB(g_pHyprRenderer->m_renderData.pMonitor.lock()); + + if (HAS_MIRROR_FB && !NEEDS_COPY_FB) + g_pHyprRenderer->m_renderData.pMonitor->m_monitorMirrorFB->release(); + else if (!HAS_MIRROR_FB && NEEDS_COPY_FB && g_pHyprRenderer->m_renderData.pMonitor->m_monitorMirrorFB) + g_pHyprRenderer->m_renderData.pMonitor->m_monitorMirrorFB->alloc(g_pHyprRenderer->m_renderData.pMonitor->m_pixelSize.x, + g_pHyprRenderer->m_renderData.pMonitor->m_pixelSize.y, + g_pHyprRenderer->m_renderData.pMonitor->m_output->state->state().drmFormat); + + if (m_renderMode == RENDER_MODE_FULL_FAKE) + return beginFullFakeRenderInternal(pMonitor, damage, fb, simple); + + int bufferAge = 0; + + if (!buffer) { + m_currentBuffer = pMonitor->m_output->swapchain->next(&bufferAge); + if (!m_currentBuffer) { + Log::logger->log(Log::ERR, "Failed to acquire swapchain buffer for {}", pMonitor->m_name); + return false; + } + } else + m_currentBuffer = buffer; + + initRender(); + + if (!initRenderBuffer(m_currentBuffer, pMonitor->m_output->state->state().drmFormat)) { + Log::logger->log(Log::ERR, "failed to start a render pass for output {}, no RBO could be obtained", pMonitor->m_name); + return false; + } + + if (m_renderMode == RENDER_MODE_NORMAL) { + damage = pMonitor->m_damage.getBufferDamage(bufferAge); + pMonitor->m_damage.rotate(); + } + + const auto res = beginRenderInternal(pMonitor, damage, simple); + static bool initial = true; + if (initial) { + initAssets(); + initial = false; + } + + return res; +} + +void IHyprRenderer::setDamage(const CRegion& damage_, std::optional<CRegion> finalDamage) { + m_renderData.damage.set(damage_); + m_renderData.finalDamage.set(finalDamage.value_or(damage_)); +} + static Mat3x3 getMirrorProjection(PHLMONITORREF monitor) { return Mat3x3::identity() .translate(monitor->m_pixelSize / 2.0) @@ -1891,12 +2116,12 @@ static Mat3x3 getFBProjection(PHLMONITORREF pMonitor, const Vector2D& size) { return Mat3x3::identity().translate(size / 2.0).transform(Math::wlTransformToHyprutils(pMonitor->m_transform)).translate(-tfmd / 2.0); } -void CHyprRenderer::setProjectionType(const Vector2D& fbSize) { +void IHyprRenderer::setProjectionType(const Vector2D& fbSize) { m_renderData.fbSize = fbSize; setProjectionType(RPT_FB); } -void CHyprRenderer::setProjectionType(eRenderProjectionType projectionType) { +void IHyprRenderer::setProjectionType(eRenderProjectionType projectionType) { m_renderData.projectionType = projectionType; switch (projectionType) { case RPT_MONITOR: m_renderData.targetProjection = m_renderData.pMonitor->getTransformMatrix(); break; @@ -1906,16 +2131,49 @@ void CHyprRenderer::setProjectionType(eRenderProjectionType projectionType) { } } -Mat3x3 CHyprRenderer::getBoxProjection(const CBox& box, std::optional<eTransform> transform) { +Mat3x3 IHyprRenderer::getBoxProjection(const CBox& box, std::optional<eTransform> transform) { return m_renderData.targetProjection.projectBox( box, transform.value_or(Math::wlTransformToHyprutils(Math::invertTransform(!monitorTransformEnabled() ? WL_OUTPUT_TRANSFORM_NORMAL : m_renderData.pMonitor->m_transform))), box.rot); } -Mat3x3 CHyprRenderer::projectBoxToTarget(const CBox& box, std::optional<eTransform> transform) { +Mat3x3 IHyprRenderer::projectBoxToTarget(const CBox& box, std::optional<eTransform> transform) { return m_renderData.pMonitor->getScaleMatrix().copy().multiply(getBoxProjection(box, transform)); } +SP<ITexture> IHyprRenderer::blurMainFramebuffer(float a, CRegion* originalDamage) { + if (!m_renderData.currentFB->getTexture()) { + Log::logger->log(Log::ERR, "BUG THIS: null fb texture while attempting to blur main fb?! (introspection off?!)"); + return m_renderData.pMonitor->m_mirrorFB->getTexture(); // return something to sample from at least + } + + return blurFramebuffer(m_renderData.currentFB, a, originalDamage); +} + +void IHyprRenderer::preBlurForCurrentMonitor(CRegion* fakeDamage) { + + const auto blurredTex = blurMainFramebuffer(1, fakeDamage); + + // render onto blurFB + m_renderData.pMonitor->m_blurFB->alloc(m_renderData.pMonitor->m_pixelSize.x, m_renderData.pMonitor->m_pixelSize.y, m_renderData.pMonitor->m_output->state->state().drmFormat); + m_renderData.pMonitor->m_blurFB->bind(); + + draw(makeUnique<CClearPassElement>(CClearPassElement::SClearData{{0, 0, 0, 0}}), {}); + + pushMonitorTransformEnabled(true); + + draw(makeUnique<CTexPassElement>(CTexPassElement::SRenderData{ + .tex = blurredTex, + .box = CBox{0, 0, m_renderData.pMonitor->m_transformedSize.x, m_renderData.pMonitor->m_transformedSize.y}, + .damage = *fakeDamage, + }), + *fakeDamage); // .noAA = true + + popMonitorTransformEnabled(); + + m_renderData.currentFB->bind(); +} + static bool isSDR2HDR(const NColorManagement::SImageDescription& imageDescription, const NColorManagement::SImageDescription& targetImageDescription) { // might be too strict return (imageDescription.transferFunction == NColorManagement::CM_TRANSFER_FUNCTION_SRGB || @@ -1932,7 +2190,7 @@ static bool isHDR2SDR(const NColorManagement::SImageDescription& imageDescriptio targetImageDescription.transferFunction == NColorManagement::CM_TRANSFER_FUNCTION_GAMMA22); } -SCMSettings CHyprRenderer::getCMSettings(const NColorManagement::PImageDescription imageDescription, const NColorManagement::PImageDescription targetImageDescription, +SCMSettings IHyprRenderer::getCMSettings(const NColorManagement::PImageDescription imageDescription, const NColorManagement::PImageDescription targetImageDescription, SP<CWLSurfaceResource> surface, bool modifySDR, float sdrMinLuminance, int sdrMaxLuminance) { const auto sdrEOTF = NTransferFunction::fromConfig(); NColorManagement::eTransferFunction srcTF; @@ -1988,7 +2246,37 @@ SCMSettings CHyprRenderer::getCMSettings(const NColorManagement::PImageDescripti }; } -void CHyprRenderer::renderMonitor(PHLMONITOR pMonitor, bool commit) { +void IHyprRenderer::renderMirrored() { + auto monitor = m_renderData.pMonitor; + auto mirrored = monitor->m_mirrorOf; + + const double scale = std::min(monitor->m_transformedSize.x / mirrored->m_transformedSize.x, monitor->m_transformedSize.y / mirrored->m_transformedSize.y); + CBox monbox = {0, 0, mirrored->m_transformedSize.x * scale, mirrored->m_transformedSize.y * scale}; + + // transform box as it will be drawn on a transformed projection + monbox.transform(Math::wlTransformToHyprutils(mirrored->m_transform), mirrored->m_transformedSize.x * scale, mirrored->m_transformedSize.y * scale); + + monbox.x = (monitor->m_transformedSize.x - monbox.w) / 2; + monbox.y = (monitor->m_transformedSize.y - monbox.h) / 2; + + if (!monitor->m_monitorMirrorFB) + monitor->m_monitorMirrorFB = createFB("monitorMirror"); + + const auto PFB = mirrored->m_monitorMirrorFB; + if (!PFB || !PFB->isAllocated() || !PFB->getTexture()) + return; + + m_renderPass.add(makeUnique<CClearPassElement>(CClearPassElement::SClearData{CHyprColor(0, 0, 0, 0)})); + + CTexPassElement::SRenderData data; + data.tex = PFB->getTexture(); + data.box = monbox; + data.useMirrorProjection = true; + + m_renderPass.add(makeUnique<CTexPassElement>(std::move(data))); +} + +void IHyprRenderer::renderMonitor(PHLMONITOR pMonitor, bool commit) { static std::chrono::high_resolution_clock::time_point renderStart = std::chrono::high_resolution_clock::now(); static std::chrono::high_resolution_clock::time_point renderStartOverlay = std::chrono::high_resolution_clock::now(); static std::chrono::high_resolution_clock::time_point endRenderOverlay = std::chrono::high_resolution_clock::now(); @@ -2118,7 +2406,7 @@ void CHyprRenderer::renderMonitor(PHLMONITOR pMonitor, bool commit) { finalDamage = damage; // update damage in renderdata as we modified it - g_pHyprOpenGL->setDamage(damage, finalDamage); + setDamage(damage, finalDamage); if (pMonitor->m_forceFullFrames > 0) { pMonitor->m_forceFullFrames -= 1; @@ -2134,9 +2422,9 @@ void CHyprRenderer::renderMonitor(PHLMONITOR pMonitor, bool commit) { renderWindow(pMonitor->m_solitaryClient.lock(), pMonitor, NOW, false, RENDER_PASS_MAIN /* solitary = no popups */); else if (!finalDamage.empty()) { if (pMonitor->isMirror()) { - g_pHyprOpenGL->blend(false); - g_pHyprOpenGL->renderMirrored(); - g_pHyprOpenGL->blend(true); + blend(false); + renderMirrored(); + blend(true); Event::bus()->m_events.render.stage.emit(RENDER_POST_MIRROR); renderCursor = false; } else { @@ -2289,7 +2577,7 @@ static hdr_output_metadata createHDRMetadata(SImageDescription settings, S }; } -bool CHyprRenderer::commitPendingAndDoExplicitSync(PHLMONITOR pMonitor) { +bool IHyprRenderer::commitPendingAndDoExplicitSync(PHLMONITOR pMonitor) { static auto PCT = CConfigValue<Hyprlang::INT>("render:send_content_type"); static auto PPASS = CConfigValue<Hyprlang::INT>("render:cm_fs_passthrough"); static auto PAUTOHDR = CConfigValue<Hyprlang::INT>("render:cm_auto_hdr"); @@ -2429,7 +2717,7 @@ bool CHyprRenderer::commitPendingAndDoExplicitSync(PHLMONITOR pMonitor) { return ok; } -void CHyprRenderer::renderWorkspace(PHLMONITOR pMonitor, PHLWORKSPACE pWorkspace, const Time::steady_tp& now, const CBox& geometry) { +void IHyprRenderer::renderWorkspace(PHLMONITOR pMonitor, PHLWORKSPACE pWorkspace, const Time::steady_tp& now, const CBox& geometry) { Vector2D translate = {geometry.x, geometry.y}; float scale = sc<float>(geometry.width) / pMonitor->m_pixelSize.x; @@ -2444,7 +2732,7 @@ void CHyprRenderer::renderWorkspace(PHLMONITOR pMonitor, PHLWORKSPACE pWorkspace renderAllClientsForWorkspace(pMonitor, pWorkspace, now, translate, scale); } -void CHyprRenderer::sendFrameEventsToWorkspace(PHLMONITOR pMonitor, PHLWORKSPACE pWorkspace, const Time::steady_tp& now) { +void IHyprRenderer::sendFrameEventsToWorkspace(PHLMONITOR pMonitor, PHLWORKSPACE pWorkspace, const Time::steady_tp& now) { for (const auto& view : Desktop::View::getViewsForWorkspace(pWorkspace)) { if (!view->aliveAndVisible()) continue; @@ -2453,7 +2741,7 @@ void CHyprRenderer::sendFrameEventsToWorkspace(PHLMONITOR pMonitor, PHLWORKSPACE } } -void CHyprRenderer::setSurfaceScanoutMode(SP<CWLSurfaceResource> surface, PHLMONITOR monitor) { +void IHyprRenderer::setSurfaceScanoutMode(SP<CWLSurfaceResource> surface, PHLMONITOR monitor) { if (!PROTO::linuxDma) return; @@ -2520,7 +2808,7 @@ static void applyExclusive(CBox& usableArea, uint32_t anchor, int32_t exclusive, } } -void CHyprRenderer::arrangeLayerArray(PHLMONITOR pMonitor, const std::vector<PHLLSREF>& layerSurfaces, bool exclusiveZone, CBox* usableArea) { +void IHyprRenderer::arrangeLayerArray(PHLMONITOR pMonitor, const std::vector<PHLLSREF>& layerSurfaces, bool exclusiveZone, CBox* usableArea) { CBox full_area = {pMonitor->m_position.x, pMonitor->m_position.y, pMonitor->m_size.x, pMonitor->m_size.y}; for (auto const& ls : layerSurfaces) { @@ -2607,7 +2895,7 @@ void CHyprRenderer::arrangeLayerArray(PHLMONITOR pMonitor, const std::vector<PHL } } -void CHyprRenderer::arrangeLayersForMonitor(const MONITORID& monitor) { +void IHyprRenderer::arrangeLayersForMonitor(const MONITORID& monitor) { const auto PMONITOR = g_pCompositor->getMonitorFromID(monitor); if (!PMONITOR || PMONITOR->m_size.x <= 0 || PMONITOR->m_size.y <= 0) @@ -2638,7 +2926,7 @@ void CHyprRenderer::arrangeLayersForMonitor(const MONITORID& monitor) { g_layoutManager->invalidateMonitorGeometries(PMONITOR); } -void CHyprRenderer::damageSurface(SP<CWLSurfaceResource> pSurface, double x, double y, double scale) { +void IHyprRenderer::damageSurface(SP<CWLSurfaceResource> pSurface, double x, double y, double scale) { if (!pSurface) return; // wut? @@ -2693,7 +2981,7 @@ void CHyprRenderer::damageSurface(SP<CWLSurfaceResource> pSurface, double x, dou damageBox.pixman()->extents.x2 - damageBox.pixman()->extents.x1, damageBox.pixman()->extents.y2 - damageBox.pixman()->extents.y1); } -void CHyprRenderer::damageWindow(PHLWINDOW pWindow, bool forceFull) { +void IHyprRenderer::damageWindow(PHLWINDOW pWindow, bool forceFull) { if (g_pCompositor->m_unsafeState) return; @@ -2720,7 +3008,7 @@ void CHyprRenderer::damageWindow(PHLWINDOW pWindow, bool forceFull) { Log::logger->log(Log::DEBUG, "Damage: Window ({}): xy: {}, {} wh: {}, {}", pWindow->m_title, windowBox.x, windowBox.y, windowBox.width, windowBox.height); } -void CHyprRenderer::damageMonitor(PHLMONITOR pMonitor) { +void IHyprRenderer::damageMonitor(PHLMONITOR pMonitor) { if (g_pCompositor->m_unsafeState || pMonitor->isMirror()) return; @@ -2733,7 +3021,7 @@ void CHyprRenderer::damageMonitor(PHLMONITOR pMonitor) { Log::logger->log(Log::DEBUG, "Damage: Monitor {}", pMonitor->m_name); } -void CHyprRenderer::damageBox(const CBox& box, bool skipFrameSchedule) { +void IHyprRenderer::damageBox(const CBox& box, bool skipFrameSchedule) { if (g_pCompositor->m_unsafeState) return; @@ -2753,16 +3041,16 @@ void CHyprRenderer::damageBox(const CBox& box, bool skipFrameSchedule) { Log::logger->log(Log::DEBUG, "Damage: Box: xy: {}, {} wh: {}, {}", box.x, box.y, box.w, box.h); } -void CHyprRenderer::damageBox(const int& x, const int& y, const int& w, const int& h) { +void IHyprRenderer::damageBox(const int& x, const int& y, const int& w, const int& h) { CBox box = {x, y, w, h}; damageBox(box); } -void CHyprRenderer::damageRegion(const CRegion& rg) { +void IHyprRenderer::damageRegion(const CRegion& rg) { rg.forEachRect([this](const auto& RECT) { damageBox(RECT.x1, RECT.y1, RECT.x2 - RECT.x1, RECT.y2 - RECT.y1); }); } -void CHyprRenderer::damageMirrorsWith(PHLMONITOR pMonitor, const CRegion& pRegion) { +void IHyprRenderer::damageMirrorsWith(PHLMONITOR pMonitor, const CRegion& pRegion) { for (auto const& mirror : pMonitor->m_mirrors) { // transform the damage here, so it won't get clipped by the monitor damage ring @@ -2786,11 +3074,11 @@ void CHyprRenderer::damageMirrorsWith(PHLMONITOR pMonitor, const CRegion& pRegio } } -void CHyprRenderer::renderDragIcon(PHLMONITOR pMonitor, const Time::steady_tp& time) { +void IHyprRenderer::renderDragIcon(PHLMONITOR pMonitor, const Time::steady_tp& time) { PROTO::data->renderDND(pMonitor, time); } -void CHyprRenderer::setCursorSurface(SP<Desktop::View::CWLSurface> surf, int hotspotX, int hotspotY, bool force) { +void IHyprRenderer::setCursorSurface(SP<Desktop::View::CWLSurface> surf, int hotspotX, int hotspotY, bool force) { m_cursorHasSurface = surf && surf->resource(); m_lastCursorData.name = ""; @@ -2804,7 +3092,7 @@ void CHyprRenderer::setCursorSurface(SP<Desktop::View::CWLSurface> surf, int hot g_pCursorManager->setCursorSurface(surf, {hotspotX, hotspotY}); } -void CHyprRenderer::setCursorFromName(const std::string& name, bool force) { +void IHyprRenderer::setCursorFromName(const std::string& name, bool force) { m_cursorHasSurface = true; if (name == m_lastCursorData.name && !force) @@ -2855,7 +3143,7 @@ void CHyprRenderer::setCursorFromName(const std::string& name, bool force) { g_pCursorManager->setCursorFromName(name); } -void CHyprRenderer::ensureCursorRenderingMode() { +void IHyprRenderer::ensureCursorRenderingMode() { static auto PINVISIBLE = CConfigValue<Hyprlang::INT>("cursor:invisible"); static auto PCURSORTIMEOUT = CConfigValue<Hyprlang::FLOAT>("cursor:inactive_timeout"); static auto PHIDEONTOUCH = CConfigValue<Hyprlang::INT>("cursor:hide_on_touch"); @@ -2897,7 +3185,7 @@ void CHyprRenderer::ensureCursorRenderingMode() { setCursorHidden(HIDE); } -void CHyprRenderer::setCursorHidden(bool hide) { +void IHyprRenderer::setCursorHidden(bool hide) { if (hide == m_cursorHidden) return; @@ -2917,11 +3205,11 @@ void CHyprRenderer::setCursorHidden(bool hide) { setCursorFromName("left_ptr", true); } -bool CHyprRenderer::shouldRenderCursor() { +bool IHyprRenderer::shouldRenderCursor() { return !m_cursorHidden && m_cursorHasSurface; } -std::tuple<float, float, float> CHyprRenderer::getRenderTimes(PHLMONITOR pMonitor) { +std::tuple<float, float, float> IHyprRenderer::getRenderTimes(PHLMONITOR pMonitor) { const auto POVERLAY = &g_pDebugOverlay->m_monitorOverlays[pMonitor]; float avgRenderTime = 0; @@ -2952,7 +3240,7 @@ static int handleCrashLoop(void* data) { return 1; } -void CHyprRenderer::initiateManualCrash() { +void IHyprRenderer::initiateManualCrash() { g_pHyprNotificationOverlay->addNotification("Manual crash initiated. Farewell...", CHyprColor(0), 5000, ICON_INFO); m_crashingLoop = wl_event_loop_add_timer(g_pCompositor->m_wlEventLoop, handleCrashLoop, nullptr); @@ -2968,11 +3256,11 @@ void CHyprRenderer::initiateManualCrash() { **PDT = 0; } -const SRenderData& CHyprRenderer::renderData() { +const SRenderData& IHyprRenderer::renderData() { return m_renderData; } -SP<IRenderbuffer> CHyprRenderer::getOrCreateRenderbuffer(SP<Aquamarine::IBuffer> buffer, uint32_t fmt) { +SP<IRenderbuffer> IHyprRenderer::getOrCreateRenderbuffer(SP<Aquamarine::IBuffer> buffer, uint32_t fmt) { auto it = std::ranges::find_if(m_renderbuffers, [&](const auto& other) { return other->m_hlBuffer == buffer; }); if (it != m_renderbuffers.end()) @@ -2987,163 +3275,35 @@ SP<IRenderbuffer> CHyprRenderer::getOrCreateRenderbuffer(SP<Aquamarine::IBuffer> return buf; } -bool CHyprRenderer::beginRender(PHLMONITOR pMonitor, CRegion& damage, eRenderMode mode, SP<IHLBuffer> buffer, SP<IFramebuffer> fb, bool simple) { - - g_pHyprOpenGL->makeEGLCurrent(); - - m_renderPass.clear(); - - m_renderMode = mode; - - m_renderData.pMonitor = pMonitor; // has to be set cuz allocs - - if (mode == RENDER_MODE_FULL_FAKE) { - RASSERT(fb, "Cannot render FULL_FAKE without a provided fb!"); - fb->bind(); - if (simple) - g_pHyprOpenGL->beginSimple(pMonitor, damage, nullptr, fb); - else - g_pHyprOpenGL->begin(pMonitor, damage, fb); - return true; - } - - int bufferAge = 0; - - if (!buffer) { - m_currentBuffer = pMonitor->m_output->swapchain->next(&bufferAge); - if (!m_currentBuffer) { - Log::logger->log(Log::ERR, "Failed to acquire swapchain buffer for {}", pMonitor->m_name); - return false; - } - } else - m_currentBuffer = buffer; - - try { - m_currentRenderbuffer = getOrCreateRenderbuffer(m_currentBuffer, pMonitor->m_output->state->state().drmFormat); - } catch (std::exception& e) { - Log::logger->log(Log::ERR, "getOrCreateRenderbuffer failed for {}", pMonitor->m_name); - return false; - } - - if (!m_currentRenderbuffer) { - Log::logger->log(Log::ERR, "failed to start a render pass for output {}, no RBO could be obtained", pMonitor->m_name); - return false; - } - - if (mode == RENDER_MODE_NORMAL) { - damage = pMonitor->m_damage.getBufferDamage(bufferAge); - pMonitor->m_damage.rotate(); - } - - m_currentRenderbuffer->bind(); - if (simple) - g_pHyprOpenGL->beginSimple(pMonitor, damage, m_currentRenderbuffer); - else - g_pHyprOpenGL->begin(pMonitor, damage); - - return true; +bool IHyprRenderer::beginFullFakeRender(PHLMONITOR pMonitor, CRegion& damage, SP<IFramebuffer> fb) { + return beginRender(pMonitor, damage, RENDER_MODE_FULL_FAKE, nullptr, fb, true); } -void CHyprRenderer::endRender(const std::function<void()>& renderingDoneCallback) { - const auto PMONITOR = m_renderData.pMonitor; - static auto PNVIDIAANTIFLICKER = CConfigValue<Hyprlang::INT>("opengl:nvidia_anti_flicker"); - - m_renderData.damage = m_renderPass.render(m_renderData.damage); - - auto cleanup = CScopeGuard([this]() { - if (m_currentRenderbuffer) - m_currentRenderbuffer->unbind(); - m_currentRenderbuffer = nullptr; - m_currentBuffer = nullptr; - }); - - if (m_renderMode != RENDER_MODE_TO_BUFFER_READ_ONLY) - g_pHyprOpenGL->end(); - else { - m_renderData.pMonitor.reset(); - m_renderData.mouseZoomFactor = 1.f; - m_renderData.mouseZoomUseMouse = true; - } - - if (m_renderMode == RENDER_MODE_FULL_FAKE) - return; - - if (m_renderMode == RENDER_MODE_NORMAL) - PMONITOR->m_output->state->setBuffer(m_currentBuffer); - - if (!g_pHyprOpenGL->explicitSyncSupported()) { - Log::logger->log(Log::TRACE, "renderer: Explicit sync unsupported, falling back to implicit in endRender"); - - // nvidia doesn't have implicit sync, so we have to explicitly wait here, llvmpipe and other software renderer seems to bug out aswell. - if ((isNvidia() && *PNVIDIAANTIFLICKER) || isSoftware()) - glFinish(); - else - glFlush(); // mark an implicit sync point - - m_usedAsyncBuffers.clear(); // release all buffer refs and hope implicit sync works - if (renderingDoneCallback) - renderingDoneCallback(); - - return; - } - - UP<CEGLSync> eglSync = CEGLSync::create(); - if LIKELY (eglSync && eglSync->isValid()) { - for (auto const& buf : m_usedAsyncBuffers) { - for (const auto& releaser : buf->m_syncReleasers) { - releaser->addSyncFileFd(eglSync->fd()); - } - } - - // release buffer refs with release points now, since syncReleaser handles actual buffer release based on EGLSync - std::erase_if(m_usedAsyncBuffers, [](const auto& buf) { return !buf->m_syncReleasers.empty(); }); - - // release buffer refs without release points when EGLSync sync_file/fence is signalled - g_pEventLoopManager->doOnReadable(eglSync->fd().duplicate(), [renderingDoneCallback, prevbfs = std::move(m_usedAsyncBuffers)]() mutable { - prevbfs.clear(); - if (renderingDoneCallback) - renderingDoneCallback(); - }); - m_usedAsyncBuffers.clear(); - - if (m_renderMode == RENDER_MODE_NORMAL) { - PMONITOR->m_inFence = eglSync->takeFd(); - PMONITOR->m_output->state->setExplicitInFence(PMONITOR->m_inFence.get()); - } - } else { - Log::logger->log(Log::ERR, "renderer: Explicit sync failed, releasing resources"); - - m_usedAsyncBuffers.clear(); // release all buffer refs and hope implicit sync works - if (renderingDoneCallback) - renderingDoneCallback(); - } +bool IHyprRenderer::beginRenderToBuffer(PHLMONITOR pMonitor, CRegion& damage, SP<IHLBuffer> buffer, bool simple) { + return beginRender(pMonitor, damage, RENDER_MODE_TO_BUFFER, buffer, nullptr, simple); } -void CHyprRenderer::onRenderbufferDestroy(IRenderbuffer* rb) { +void IHyprRenderer::onRenderbufferDestroy(IRenderbuffer* rb) { std::erase_if(m_renderbuffers, [&](const auto& rbo) { return rbo.get() == rb; }); } -SP<IRenderbuffer> CHyprRenderer::getCurrentRBO() { - return m_currentRenderbuffer; -} - -bool CHyprRenderer::isNvidia() { +bool IHyprRenderer::isNvidia() { return m_nvidia; } -bool CHyprRenderer::isIntel() { +bool IHyprRenderer::isIntel() { return m_intel; } -bool CHyprRenderer::isSoftware() { +bool IHyprRenderer::isSoftware() { return m_software; } -bool CHyprRenderer::isMgpu() { +bool IHyprRenderer::isMgpu() { return m_mgpu; } -void CHyprRenderer::addWindowToRenderUnfocused(PHLWINDOW window) { +void IHyprRenderer::addWindowToRenderUnfocused(PHLWINDOW window) { static auto PFPS = CConfigValue<Hyprlang::INT>("misc:render_unfocused_fps"); if (std::ranges::find(m_renderUnfocused, window) != m_renderUnfocused.end()) @@ -3155,7 +3315,7 @@ void CHyprRenderer::addWindowToRenderUnfocused(PHLWINDOW window) { m_renderUnfocusedTimer->updateTimeout(std::chrono::milliseconds(1000 / *PFPS)); } -void CHyprRenderer::makeSnapshot(PHLWINDOW pWindow) { +void IHyprRenderer::makeSnapshot(PHLWINDOW pWindow) { // we trust the window is valid. const auto PMONITOR = pWindow->m_monitor.lock(); @@ -3181,20 +3341,27 @@ void CHyprRenderer::makeSnapshot(PHLWINDOW pWindow) { PFRAMEBUFFER->alloc(PMONITOR->m_pixelSize.x, PMONITOR->m_pixelSize.y, DRM_FORMAT_ABGR8888); - beginRender(PMONITOR, fakeDamage, RENDER_MODE_FULL_FAKE, nullptr, PFRAMEBUFFER); + beginFullFakeRender(PMONITOR, fakeDamage, PFRAMEBUFFER); m_bRenderingSnapshot = true; - g_pHyprOpenGL->clear(CHyprColor(0, 0, 0, 0)); // JIC + draw(makeUnique<CClearPassElement>(CClearPassElement::SClearData{CHyprColor(0, 0, 0, 0)}), {}); + startRenderPass(); + + Log::logger->log(Log::DEBUG, "renderer: cleared a snapshot of {:x}", rc<uintptr_t>(pWindow.get())); renderWindow(pWindow, PMONITOR, Time::steadyNow(), !pWindow->m_X11DoesntWantBorders, RENDER_PASS_ALL); + Log::logger->log(Log::DEBUG, "renderer: rendered a snapshot of {:x}", rc<uintptr_t>(pWindow.get())); + endRender(); + Log::logger->log(Log::DEBUG, "renderer: made a snapshot of {:x}", rc<uintptr_t>(pWindow.get())); + m_bRenderingSnapshot = false; } -void CHyprRenderer::makeSnapshot(PHLLS pLayer) { +void IHyprRenderer::makeSnapshot(PHLLS pLayer) { // we trust the window is valid. const auto PMONITOR = pLayer->m_monitor.lock(); @@ -3215,21 +3382,28 @@ void CHyprRenderer::makeSnapshot(PHLLS pLayer) { PFRAMEBUFFER->alloc(PMONITOR->m_pixelSize.x, PMONITOR->m_pixelSize.y, DRM_FORMAT_ABGR8888); - beginRender(PMONITOR, fakeDamage, RENDER_MODE_FULL_FAKE, nullptr, PFRAMEBUFFER); + beginFullFakeRender(PMONITOR, fakeDamage, PFRAMEBUFFER); m_bRenderingSnapshot = true; - g_pHyprOpenGL->clear(CHyprColor(0, 0, 0, 0)); // JIC + draw(makeUnique<CClearPassElement>(CClearPassElement::SClearData{CHyprColor(0, 0, 0, 0)}), {}); + startRenderPass(); + + Log::logger->log(Log::DEBUG, "renderer: cleared a snapshot of layer {:x}", rc<uintptr_t>(pLayer.get())); // draw the layer renderLayer(pLayer, PMONITOR, Time::steadyNow()); + Log::logger->log(Log::DEBUG, "renderer: rendered a snapshot of layer {:x}", rc<uintptr_t>(pLayer.get())); + endRender(); + Log::logger->log(Log::DEBUG, "renderer: made a snapshot of layer {:x}", rc<uintptr_t>(pLayer.get())); + m_bRenderingSnapshot = false; } -void CHyprRenderer::makeSnapshot(WP<Desktop::View::CPopup> popup) { +void IHyprRenderer::makeSnapshot(WP<Desktop::View::CPopup> popup) { // we trust the window is valid. const auto PMONITOR = popup->getMonitor(); @@ -3250,11 +3424,11 @@ void CHyprRenderer::makeSnapshot(WP<Desktop::View::CPopup> popup) { PFRAMEBUFFER->alloc(PMONITOR->m_pixelSize.x, PMONITOR->m_pixelSize.y, DRM_FORMAT_ABGR8888); - beginRender(PMONITOR, fakeDamage, RENDER_MODE_FULL_FAKE, nullptr, PFRAMEBUFFER); + beginFullFakeRender(PMONITOR, fakeDamage, PFRAMEBUFFER); m_bRenderingSnapshot = true; - g_pHyprOpenGL->clear(CHyprColor(0, 0, 0, 0)); // JIC + draw(makeUnique<CClearPassElement>(CClearPassElement::SClearData{CHyprColor(0, 0, 0, 0)}), {}); CSurfacePassElement::SRenderData renderdata; renderdata.pos = popup->coordsGlobal(); @@ -3287,7 +3461,7 @@ void CHyprRenderer::makeSnapshot(WP<Desktop::View::CPopup> popup) { m_bRenderingSnapshot = false; } -void CHyprRenderer::renderSnapshot(PHLWINDOW pWindow) { +void IHyprRenderer::renderSnapshot(PHLWINDOW pWindow) { static auto PDIMAROUND = CConfigValue<Hyprlang::FLOAT>("decoration:dim_around"); PHLWINDOWREF ref{pWindow}; @@ -3334,7 +3508,7 @@ void CHyprRenderer::renderSnapshot(PHLWINDOW pWindow) { data.roundingPower = pWindow->roundingPower(); data.xray = pWindow->m_ruleApplicator->xray().valueOr(false); - m_renderPass.add(makeUnique<CRectPassElement>(std::move(data))); + m_renderPass.add(makeUnique<CRectPassElement>(data)); } CTexPassElement::SRenderData data; @@ -3347,7 +3521,7 @@ void CHyprRenderer::renderSnapshot(PHLWINDOW pWindow) { m_renderPass.add(makeUnique<CTexPassElement>(std::move(data))); } -void CHyprRenderer::renderSnapshot(PHLLS pLayer) { +void IHyprRenderer::renderSnapshot(PHLLS pLayer) { if (!pLayer->m_snapshotFB) return; @@ -3389,7 +3563,7 @@ void CHyprRenderer::renderSnapshot(PHLLS pLayer) { m_renderPass.add(makeUnique<CTexPassElement>(std::move(data))); } -void CHyprRenderer::renderSnapshot(WP<Desktop::View::CPopup> popup) { +void IHyprRenderer::renderSnapshot(WP<Desktop::View::CPopup> popup) { if (!popup->m_snapshotFB) return; @@ -3424,7 +3598,7 @@ void CHyprRenderer::renderSnapshot(WP<Desktop::View::CPopup> popup) { m_renderPass.add(makeUnique<CTexPassElement>(std::move(data))); } -NColorManagement::PImageDescription CHyprRenderer::workBufferImageDescription() { +NColorManagement::PImageDescription IHyprRenderer::workBufferImageDescription() { // TODO // const bool IS_MONITOR_ICC = m_renderData.pMonitor->m_imageDescription.valid() && m_renderData.pMonitor->m_imageDescription->value().icc.present; // const auto sdrEOTF = NTransferFunction::fromConfig(IS_MONITOR_ICC); @@ -3433,7 +3607,7 @@ NColorManagement::PImageDescription CHyprRenderer::workBufferImageDescription() return m_renderData.pMonitor->m_imageDescription; //CImageDescription::from(NColorManagement::SImageDescription{.transferFunction = CHOSEN_SDR_EOTF}); } -bool CHyprRenderer::shouldBlur(PHLLS ls) { +bool IHyprRenderer::shouldBlur(PHLLS ls) { if (m_bRenderingSnapshot) return false; @@ -3441,7 +3615,7 @@ bool CHyprRenderer::shouldBlur(PHLLS ls) { return *PBLUR && ls->m_ruleApplicator->blur().valueOrDefault(); } -bool CHyprRenderer::shouldBlur(PHLWINDOW w) { +bool IHyprRenderer::shouldBlur(PHLWINDOW w) { if (m_bRenderingSnapshot) return false; @@ -3450,115 +3624,74 @@ bool CHyprRenderer::shouldBlur(PHLWINDOW w) { return *PBLUR && !DONT_BLUR; } -bool CHyprRenderer::shouldBlur(WP<Desktop::View::CPopup> p) { +bool IHyprRenderer::shouldBlur(WP<Desktop::View::CPopup> p) { static CConfigValue PBLURPOPUPS = CConfigValue<Hyprlang::INT>("decoration:blur:popups"); static CConfigValue PBLUR = CConfigValue<Hyprlang::INT>("decoration:blur:enabled"); return *PBLURPOPUPS && *PBLUR; } -bool CHyprRenderer::reloadShaders(const std::string& path) { - return g_pHyprOpenGL->initShaders(path); -} - -SP<ITexture> CHyprRenderer::createStencilTexture(const int width, const int height) { - g_pHyprOpenGL->makeEGLCurrent(); - auto tex = makeShared<CGLTexture>(); - tex->allocate({width, height}); - - return tex; -} - -SP<ITexture> CHyprRenderer::createTexture(bool opaque) { - g_pHyprOpenGL->makeEGLCurrent(); - return makeShared<CGLTexture>(opaque); -} - -SP<ITexture> CHyprRenderer::createTexture(uint32_t drmFormat, uint8_t* pixels, uint32_t stride, const Vector2D& size, bool keepDataCopy, bool opaque) { - g_pHyprOpenGL->makeEGLCurrent(); - return makeShared<CGLTexture>(drmFormat, pixels, stride, size, keepDataCopy, opaque); -} - -SP<ITexture> CHyprRenderer::createTexture(const Aquamarine::SDMABUFAttrs& attrs, bool opaque) { - g_pHyprOpenGL->makeEGLCurrent(); - const auto image = g_pHyprOpenGL->createEGLImage(attrs); - if (!image) - return nullptr; - return makeShared<CGLTexture>(attrs, image, opaque); -} +SP<ITexture> IHyprRenderer::renderSplash(const std::function<SP<ITexture>(const int, const int, unsigned char* const)>& handleData, const int fontSize, const int maxWidth, + const int maxHeight) { + static auto PSPLASHCOLOR = CConfigValue<Hyprlang::INT>("misc:col.splash"); + static auto PSPLASHFONT = CConfigValue<std::string>("misc:splash_font_family"); + static auto FALLBACKFONT = CConfigValue<std::string>("misc:font_family"); -SP<ITexture> CHyprRenderer::createTexture(const int width, const int height, unsigned char* const data) { - g_pHyprOpenGL->makeEGLCurrent(); - SP<ITexture> tex = makeShared<CGLTexture>(); + const auto FONTFAMILY = *PSPLASHFONT != STRVAL_EMPTY ? *PSPLASHFONT : *FALLBACKFONT; + const auto COLOR = CHyprColor(*PSPLASHCOLOR); - tex->allocate({width, height}); + const auto CAIROSURFACE = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, maxWidth, maxHeight); + const auto CAIRO = cairo_create(CAIROSURFACE); - tex->m_size = {width, height}; - // copy the data to an OpenGL texture we have - const GLint glFormat = GL_RGBA; - const GLint glType = GL_UNSIGNED_BYTE; + cairo_set_antialias(CAIRO, CAIRO_ANTIALIAS_GOOD); + cairo_save(CAIRO); + cairo_set_source_rgba(CAIRO, 0, 0, 0, 0); + cairo_set_operator(CAIRO, CAIRO_OPERATOR_SOURCE); + cairo_paint(CAIRO); + cairo_restore(CAIRO); - tex->bind(); - tex->setTexParameter(GL_TEXTURE_MAG_FILTER, GL_LINEAR); - tex->setTexParameter(GL_TEXTURE_MIN_FILTER, GL_LINEAR); - tex->setTexParameter(GL_TEXTURE_SWIZZLE_R, GL_BLUE); - tex->setTexParameter(GL_TEXTURE_SWIZZLE_B, GL_RED); + PangoLayout* layoutText = pango_cairo_create_layout(CAIRO); + PangoFontDescription* pangoFD = pango_font_description_new(); - glTexImage2D(GL_TEXTURE_2D, 0, glFormat, tex->m_size.x, tex->m_size.y, 0, glFormat, glType, data); - tex->unbind(); + pango_font_description_set_family_static(pangoFD, FONTFAMILY.c_str()); + pango_font_description_set_absolute_size(pangoFD, fontSize * PANGO_SCALE); + pango_font_description_set_style(pangoFD, PANGO_STYLE_NORMAL); + pango_font_description_set_weight(pangoFD, PANGO_WEIGHT_NORMAL); + pango_layout_set_font_description(layoutText, pangoFD); - return tex; -} + cairo_set_source_rgba(CAIRO, COLOR.r, COLOR.g, COLOR.b, COLOR.a); + int textW = 0, textH = 0; + pango_layout_set_text(layoutText, g_pCompositor->m_currentSplash.c_str(), -1); + pango_layout_get_size(layoutText, &textW, &textH); + textW = std::ceil((float)textW / PANGO_SCALE + fontSize / 10.f); + textH = std::ceil((float)textH / PANGO_SCALE + fontSize / 10.f); -SP<ITexture> CHyprRenderer::createTexture(cairo_surface_t* cairo) { - g_pHyprOpenGL->makeEGLCurrent(); - const auto CAIROFORMAT = cairo_image_surface_get_format(cairo); - auto tex = makeShared<CGLTexture>(); + cairo_move_to(CAIRO, 0, 0); + pango_cairo_show_layout(CAIRO, layoutText); - tex->allocate({cairo_image_surface_get_width(cairo), cairo_image_surface_get_height(cairo)}); + pango_font_description_free(pangoFD); + g_object_unref(layoutText); - const GLint glIFormat = CAIROFORMAT == CAIRO_FORMAT_RGB96F ? GL_RGB32F : GL_RGBA; - const GLint glFormat = CAIROFORMAT == CAIRO_FORMAT_RGB96F ? GL_RGB : GL_RGBA; - const GLint glType = CAIROFORMAT == CAIRO_FORMAT_RGB96F ? GL_FLOAT : GL_UNSIGNED_BYTE; + cairo_surface_flush(CAIROSURFACE); - const auto DATA = cairo_image_surface_get_data(cairo); - tex->bind(); - tex->setTexParameter(GL_TEXTURE_MAG_FILTER, GL_LINEAR); - tex->setTexParameter(GL_TEXTURE_MIN_FILTER, GL_LINEAR); + const auto smallSurf = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, textW, textH); + const auto small = cairo_create(smallSurf); + cairo_set_source_surface(small, CAIROSURFACE, 0, 0); + cairo_rectangle(small, 0, 0, textW, textH); + cairo_set_operator(small, CAIRO_OPERATOR_SOURCE); + cairo_fill(small); + cairo_surface_flush(smallSurf); - if (CAIROFORMAT != CAIRO_FORMAT_RGB96F) { - tex->setTexParameter(GL_TEXTURE_SWIZZLE_R, GL_BLUE); - tex->setTexParameter(GL_TEXTURE_SWIZZLE_B, GL_RED); - } + auto tex = handleData(textW, textH, cairo_image_surface_get_data(smallSurf)); - glTexImage2D(GL_TEXTURE_2D, 0, glIFormat, tex->m_size.x, tex->m_size.y, 0, glFormat, glType, DATA); + cairo_surface_destroy(smallSurf); + cairo_destroy(small); + cairo_surface_destroy(CAIROSURFACE); + cairo_destroy(CAIRO); return tex; } -SP<ITexture> CHyprRenderer::createTexture(std::span<const float> lut3D, size_t N) { - g_pHyprOpenGL->makeEGLCurrent(); - return makeShared<CGLTexture>(lut3D, N); +bool IHyprRenderer::needsACopyFB(PHLMONITOR mon) { + return !mon->m_mirrors.empty() || Screenshare::mgr()->isOutputBeingSSd(mon); } - -SP<IFramebuffer> CHyprRenderer::createFB(const std::string& name) { - g_pHyprOpenGL->makeEGLCurrent(); - return makeShared<CGLFramebuffer>(name); -} - -bool CHyprRenderer::explicitSyncSupported() { - return g_pHyprOpenGL->explicitSyncSupported(); -} - -std::vector<SDRMFormat> CHyprRenderer::getDRMFormats() { - return g_pHyprOpenGL->getDRMFormats(); -} - -std::vector<uint64_t> CHyprRenderer::getDRMFormatModifiers(DRMFormat format) { - return g_pHyprOpenGL->getDRMFormatModifiers(format); -} - -SP<IRenderbuffer> CHyprRenderer::getOrCreateRenderbufferInternal(SP<Aquamarine::IBuffer> buffer, uint32_t fmt) { - g_pHyprOpenGL->makeEGLCurrent(); - return makeShared<CGLRenderbuffer>(buffer, fmt); -}
\ No newline at end of file |
