From 02f30ea15b349d1ed98b608ed06ec88c834592dd Mon Sep 17 00:00:00 2001 From: UjinT34 <41110182+UjinT34@users.noreply.github.com> Date: Sat, 7 Mar 2026 21:21:23 +0300 Subject: [PATCH] renderer: refactor gl renderer (#13488) --- src/Compositor.cpp | 7 +- src/debug/HyprDebugOverlay.hpp | 6 +- src/managers/PointerManager.cpp | 13 +- src/managers/input/InputMethodRelay.hpp | 5 +- .../screenshare/CursorshareSession.cpp | 35 +- src/managers/screenshare/ScreenshareFrame.cpp | 124 +- src/render/GLRenderer.cpp | 405 ++++++ src/render/GLRenderer.hpp | 52 + src/render/OpenGL.cpp | 705 +++------- src/render/OpenGL.hpp | 62 +- src/render/Renderer.cpp | 1185 +++++++++-------- src/render/Renderer.hpp | 235 ++-- .../decorations/CHyprBorderDecoration.cpp | 1 + .../decorations/CHyprDropShadowDecoration.cpp | 165 ++- .../decorations/CHyprDropShadowDecoration.hpp | 18 +- src/render/gl/GLFramebuffer.cpp | 9 +- src/render/pass/BorderPassElement.hpp | 1 + src/render/pass/Pass.cpp | 44 +- src/render/pass/RectPassElement.hpp | 6 + src/render/pass/SurfacePassElement.hpp | 3 +- src/render/pass/TexPassElement.hpp | 46 +- 21 files changed, 1733 insertions(+), 1394 deletions(-) create mode 100644 src/render/GLRenderer.cpp create mode 100644 src/render/GLRenderer.hpp diff --git a/src/Compositor.cpp b/src/Compositor.cpp index 400c595d2..22f79103e 100644 --- a/src/Compositor.cpp +++ b/src/Compositor.cpp @@ -32,6 +32,7 @@ #include #include "debug/HyprCtl.hpp" #include "debug/crash/CrashReporter.hpp" +#include "render/GLRenderer.hpp" #include "render/ShaderLoader.hpp" #ifdef USES_SYSTEMD #include // for SdNotify @@ -658,6 +659,9 @@ void CCompositor::initManagers(eManagersInitStage stage) { Log::logger->log(Log::DEBUG, "Creating the CHyprOpenGLImpl!"); g_pHyprOpenGL = makeUnique(); + Log::logger->log(Log::DEBUG, "Creating the HyprRenderer!"); + g_pHyprRenderer = makeUnique(); + Log::logger->log(Log::DEBUG, "Creating the ProtocolManager!"); g_pProtocolManager = makeUnique(); @@ -676,9 +680,6 @@ void CCompositor::initManagers(eManagersInitStage stage) { Log::logger->log(Log::DEBUG, "Creating the InputManager!"); g_pInputManager = makeUnique(); - Log::logger->log(Log::DEBUG, "Creating the HyprRenderer!"); - g_pHyprRenderer = makeUnique(); - Log::logger->log(Log::DEBUG, "Creating the XWaylandManager!"); g_pXWaylandManager = makeUnique(); diff --git a/src/debug/HyprDebugOverlay.hpp b/src/debug/HyprDebugOverlay.hpp index bf1883594..775ce62c4 100644 --- a/src/debug/HyprDebugOverlay.hpp +++ b/src/debug/HyprDebugOverlay.hpp @@ -6,7 +6,7 @@ #include #include -class CHyprRenderer; +class IHyprRenderer; class CHyprMonitorDebugOverlay { public: @@ -25,7 +25,7 @@ class CHyprMonitorDebugOverlay { PHLMONITORREF m_monitor; CBox m_lastDrawnBox; - friend class CHyprRenderer; + friend class IHyprRenderer; }; class CHyprDebugOverlay { @@ -45,7 +45,7 @@ class CHyprDebugOverlay { SP m_texture; friend class CHyprMonitorDebugOverlay; - friend class CHyprRenderer; + friend class IHyprRenderer; }; inline UP g_pDebugOverlay; diff --git a/src/managers/PointerManager.cpp b/src/managers/PointerManager.cpp index dac66c56f..aeea2831d 100644 --- a/src/managers/PointerManager.cpp +++ b/src/managers/PointerManager.cpp @@ -591,16 +591,21 @@ SP CPointerManager::renderHWCursorBuffer(SPbind(); - g_pHyprOpenGL->beginSimple(state->monitor.lock(), {0, 0, INT_MAX, INT_MAX}, RBO); - g_pHyprOpenGL->clear(CHyprColor{0.F, 0.F, 0.F, 0.F}); // ensure the RBO is zero initialized. + CRegion damageRegion = {0, 0, INT_MAX, INT_MAX}; + g_pHyprRenderer->beginFullFakeRender(state->monitor.lock(), damageRegion, RBO->getFB()); + g_pHyprRenderer->startRenderPass(); + g_pHyprRenderer->draw(makeUnique(CClearPassElement::SClearData{{0.F, 0.F, 0.F, 0.F}}), {}); CBox xbox = {{}, Vector2D{m_currentCursorImage.size / m_currentCursorImage.scale * state->monitor->m_scale}.round()}; Log::logger->log(Log::TRACE, "[pointer] monitor: {}, size: {}, hw buf: {}, scale: {:.2f}, monscale: {:.2f}, xbox: {}", state->monitor->m_name, m_currentCursorImage.size, cursorSize, m_currentCursorImage.scale, state->monitor->m_scale, xbox.size()); - g_pHyprOpenGL->renderTexture(texture, xbox, {.noCM = true}); + CTexPassElement::SRenderData data; + data.tex = texture; + data.box = xbox; + g_pHyprRenderer->draw(makeUnique(std::move(data)), {}); - g_pHyprOpenGL->end(); + g_pHyprRenderer->endRender(); g_pHyprRenderer->m_renderData.pMonitor.reset(); return buf; diff --git a/src/managers/input/InputMethodRelay.hpp b/src/managers/input/InputMethodRelay.hpp index cb631b12d..301d1b757 100644 --- a/src/managers/input/InputMethodRelay.hpp +++ b/src/managers/input/InputMethodRelay.hpp @@ -9,7 +9,7 @@ #include class CInputManager; -class CHyprRenderer; +class IHyprRenderer; class CTextInputV1; class CInputMethodV2; @@ -54,9 +54,8 @@ class CInputMethodRelay { CHyprSignalListener newPopup; } m_listeners; - friend class CHyprRenderer; + friend class IHyprRenderer; friend class CInputManager; friend class CTextInputV1ProtocolManager; friend class CTextInput; - friend class CHyprRenderer; }; diff --git a/src/managers/screenshare/CursorshareSession.cpp b/src/managers/screenshare/CursorshareSession.cpp index 63d4d2055..5ccc3d22b 100644 --- a/src/managers/screenshare/CursorshareSession.cpp +++ b/src/managers/screenshare/CursorshareSession.cpp @@ -116,19 +116,24 @@ void CCursorshareSession::render() { // TODO: implement a monitor independent render mode to buffer that does this in CHyprRenderer::begin() or something like that g_pHyprRenderer->m_renderData.transformDamage = false; - g_pHyprOpenGL->setViewport(0, 0, m_bufferSize.x, m_bufferSize.y); + g_pHyprRenderer->setViewport(0, 0, m_bufferSize.x, m_bufferSize.y); bool overlaps = g_pPointerManager->getCursorBoxGlobal().overlaps(m_pendingFrame.sourceBoxCallback()); + g_pHyprRenderer->startRenderPass(); if (PERM != PERMISSION_RULE_ALLOW_MODE_ALLOW || !overlaps) { // render black when not allowed - g_pHyprOpenGL->clear(Colors::BLACK); + g_pHyprRenderer->draw(makeUnique(CClearPassElement::SClearData{Colors::BLACK}), {}); } else if (!cursorImage.pBuffer || !cursorImage.surface || !cursorImage.bufferTex) { // render clear when cursor is probably hidden - g_pHyprOpenGL->clear(CHyprColor(0, 0, 0, 0)); + g_pHyprRenderer->draw(makeUnique(CClearPassElement::SClearData{{0, 0, 0, 0}}), {}); } else { // render cursor CBox texbox = {{}, cursorImage.bufferTex->m_size}; - g_pHyprOpenGL->renderTexture(cursorImage.bufferTex, texbox, {}); + g_pHyprRenderer->draw(makeUnique(CTexPassElement::SRenderData{ + .tex = cursorImage.bufferTex, + .box = texbox, + }), + {}); } g_pHyprRenderer->m_renderData.blockScreenShader = true; @@ -141,8 +146,6 @@ bool CCursorshareSession::copy() { // FIXME: this doesn't really make sense but just to be safe m_pendingFrame.callback(RESULT_TIMESTAMP); - g_pHyprOpenGL->makeEGLCurrent(); - CRegion fakeDamage = {0, 0, INT16_MAX, INT16_MAX}; if (auto attrs = m_pendingFrame.buffer->dmabuf(); attrs.success) { if (attrs.format != m_format) { @@ -150,7 +153,7 @@ bool CCursorshareSession::copy() { return false; } - if (!g_pHyprRenderer->beginRender(m_pendingFrame.monitor, fakeDamage, RENDER_MODE_TO_BUFFER, m_pendingFrame.buffer, nullptr, true)) { + if (!g_pHyprRenderer->beginRenderToBuffer(m_pendingFrame.monitor, fakeDamage, m_pendingFrame.buffer, true)) { LOGM(Log::ERR, "Can't copy: failed to begin rendering to dmabuf"); return false; } @@ -162,8 +165,7 @@ bool CCursorshareSession::copy() { callback(RESULT_COPIED); }); } else if (auto attrs = m_pendingFrame.buffer->shm(); attrs.success) { - auto [bufData, fmt, bufLen] = m_pendingFrame.buffer->beginDataPtr(0); - const auto PFORMAT = NFormatUtils::getPixelFormatFromDRM(m_format); + const auto PFORMAT = NFormatUtils::getPixelFormatFromDRM(m_format); if (attrs.format != m_format || !PFORMAT) { LOGM(Log::ERR, "Can't copy: invalid format"); @@ -173,7 +175,7 @@ bool CCursorshareSession::copy() { auto outFB = g_pHyprRenderer->createFB(); outFB->alloc(m_bufferSize.x, m_bufferSize.y, m_format); - if (!g_pHyprRenderer->beginRender(m_pendingFrame.monitor, fakeDamage, RENDER_MODE_FULL_FAKE, nullptr, outFB, true)) { + if (!g_pHyprRenderer->beginFullFakeRender(m_pendingFrame.monitor, fakeDamage, outFB)) { LOGM(Log::ERR, "Can't copy: failed to begin rendering to shm"); return false; } @@ -182,12 +184,6 @@ bool CCursorshareSession::copy() { g_pHyprRenderer->endRender(); - g_pHyprRenderer->m_renderData.pMonitor = m_pendingFrame.monitor; - outFB->bind(); - glBindFramebuffer(GL_READ_FRAMEBUFFER, GLFB(outFB)->getFBID()); - - glPixelStorei(GL_PACK_ALIGNMENT, 1); - int glFormat = PFORMAT->glFormat; if (glFormat == GL_RGBA) @@ -208,15 +204,10 @@ bool CCursorshareSession::copy() { } } - glReadPixels(0, 0, m_bufferSize.x, m_bufferSize.y, glFormat, PFORMAT->glType, bufData); + outFB->readPixels(m_pendingFrame.buffer, 0, 0, m_bufferSize.x, m_bufferSize.y); g_pHyprRenderer->m_renderData.pMonitor.reset(); - m_pendingFrame.buffer->endDataPtr(); - GLFB(outFB)->unbind(); - glPixelStorei(GL_PACK_ALIGNMENT, 4); - glBindFramebuffer(GL_READ_FRAMEBUFFER, 0); - m_pendingFrame.callback(RESULT_COPIED); } else { LOGM(Log::ERR, "Can't copy: invalid buffer type"); diff --git a/src/managers/screenshare/ScreenshareFrame.cpp b/src/managers/screenshare/ScreenshareFrame.cpp index e981296de..5a62629d9 100644 --- a/src/managers/screenshare/ScreenshareFrame.cpp +++ b/src/managers/screenshare/ScreenshareFrame.cpp @@ -10,6 +10,7 @@ #include "../../helpers/Monitor.hpp" #include "../../desktop/view/Window.hpp" #include "../../desktop/state/FocusState.hpp" +#include "render/pass/RectPassElement.hpp" #include using namespace Screenshare; @@ -170,15 +171,19 @@ void CScreenshareFrame::renderMonitor() { CBox monbox = CBox{{}, PMONITOR->m_pixelSize} .transform(Math::wlTransformToHyprutils(Math::invertTransform(PMONITOR->m_transform)), PMONITOR->m_pixelSize.x, PMONITOR->m_pixelSize.y) .translate(-m_session->m_captureBox.pos()); // vvvv kinda ass-backwards but that's how I designed the renderer... sigh. - g_pHyprRenderer->pushMonitorTransformEnabled(true); - g_pHyprOpenGL->setRenderModifEnabled(false); - g_pHyprOpenGL->renderTexture(TEXTURE, monbox, - { - .cmBackToSRGB = !IS_CM_AWARE, - .cmBackToSRGBSource = !IS_CM_AWARE ? PMONITOR : nullptr, - }); - g_pHyprOpenGL->setRenderModifEnabled(true); - g_pHyprRenderer->popMonitorTransformEnabled(); + + const auto OLD = g_pHyprRenderer->m_renderData.renderModif.enabled; + g_pHyprRenderer->m_renderData.renderModif.enabled = false; + g_pHyprRenderer->startRenderPass(); + g_pHyprRenderer->draw(makeUnique(CTexPassElement::SRenderData{ + .tex = TEXTURE, + .box = monbox, + .flipEndFrame = true, + .cmBackToSRGB = !IS_CM_AWARE, + .cmBackToSRGBSource = !IS_CM_AWARE ? PMONITOR : nullptr, + }), + monbox); + g_pHyprRenderer->m_renderData.renderModif.enabled = OLD; // render black boxes for noscreenshare auto hidePopups = [&](Vector2D popupBaseOffset) { @@ -194,7 +199,11 @@ void CScreenshareFrame::renderMonitor() { CBox{popupBaseOffset + popRel + localOff, size}.translate(PMONITOR->m_position).scale(PMONITOR->m_scale).translate(-m_session->m_captureBox.pos()); if LIKELY (surfBox.w > 0 && surfBox.h > 0) - g_pHyprOpenGL->renderRect(surfBox, Colors::BLACK, {}); + g_pHyprRenderer->draw(makeUnique(CRectPassElement::SRectData{ + .box = surfBox, + .color = Colors::BLACK, + }), + surfBox); }, nullptr); }; @@ -215,7 +224,11 @@ void CScreenshareFrame::renderMonitor() { .scale(PMONITOR->m_scale) .translate(-m_session->m_captureBox.pos()); - g_pHyprOpenGL->renderRect(noScreenShareBox, Colors::BLACK, {}); + g_pHyprRenderer->draw(makeUnique(CRectPassElement::SRectData{ + .box = noScreenShareBox, + .color = Colors::BLACK, + }), + noScreenShareBox); const auto geom = l->m_geometry; const Vector2D popupBaseOffset = REALPOS - Vector2D{geom.pos().x, geom.pos().y}; @@ -250,7 +263,13 @@ void CScreenshareFrame::renderMonitor() { const auto rounding = dontRound ? 0 : w->rounding() * PMONITOR->m_scale; const auto roundingPower = dontRound ? 2.0f : w->roundingPower(); - g_pHyprOpenGL->renderRect(noScreenShareBox, Colors::BLACK, {.round = rounding, .roundingPower = roundingPower}); + g_pHyprRenderer->draw(makeUnique(CRectPassElement::SRectData{ + .box = noScreenShareBox, + .color = Colors::BLACK, + .round = rounding, + .roundingPower = roundingPower, + }), + noScreenShareBox); if (w->m_isX11 || !w->m_popupHead) continue; @@ -281,7 +300,7 @@ void CScreenshareFrame::renderWindow() { g_pHyprRenderer->m_renderData.fbSize = m_bufferSize; g_pHyprRenderer->setProjectionType(RPT_FB); g_pHyprRenderer->m_renderData.transformDamage = false; - g_pHyprOpenGL->setViewport(0, 0, m_bufferSize.x, m_bufferSize.y); + g_pHyprRenderer->setViewport(0, 0, m_bufferSize.x, m_bufferSize.y); g_pHyprRenderer->m_bBlockSurfaceFeedback = g_pHyprRenderer->shouldRenderWindow(PWINDOW); // block the feedback to avoid spamming the surface if it's visible g_pHyprRenderer->renderWindow(PWINDOW, PMONITOR, NOW, false, RENDER_PASS_ALL, true, true); @@ -313,22 +332,32 @@ void CScreenshareFrame::renderWindow() { void CScreenshareFrame::render() { const auto PERM = g_pDynamicPermissionManager->clientPermissionMode(m_session->m_client, PERMISSION_TYPE_SCREENCOPY); + CRegion frameRegion = {0, 0, g_pHyprRenderer->m_renderData.pMonitor->m_pixelSize.x, g_pHyprRenderer->m_renderData.pMonitor->m_pixelSize.y}; if (PERM == PERMISSION_RULE_ALLOW_MODE_PENDING) { - g_pHyprOpenGL->clear(CHyprColor(0, 0, 0, 0)); + g_pHyprRenderer->draw(makeUnique(CClearPassElement::SClearData{{0, 0, 0, 0}}), frameRegion); return; } bool windowShareDenied = m_session->m_type == SHARE_WINDOW && m_session->m_window->m_ruleApplicator && m_session->m_window->m_ruleApplicator->noScreenShare().valueOrDefault(); + g_pHyprRenderer->startRenderPass(); if (PERM == PERMISSION_RULE_ALLOW_MODE_DENY || windowShareDenied) { - g_pHyprOpenGL->clear(CHyprColor(0, 0, 0, 0)); + g_pHyprRenderer->draw(makeUnique(CClearPassElement::SClearData{{0, 0, 0, 0}}), frameRegion); CBox texbox = CBox{m_bufferSize / 2.F, g_pHyprRenderer->m_screencopyDeniedTexture->m_size}.translate(-g_pHyprRenderer->m_screencopyDeniedTexture->m_size / 2.F); - g_pHyprOpenGL->renderTexture(g_pHyprRenderer->m_screencopyDeniedTexture, texbox, {}); + g_pHyprRenderer->draw(makeUnique(CTexPassElement::SRenderData{ + .tex = g_pHyprRenderer->m_screencopyDeniedTexture, + .box = texbox, + }), + texbox); return; } if (m_session->m_tempFB && m_session->m_tempFB->isAllocated()) { CBox texbox = {{}, m_bufferSize}; - g_pHyprOpenGL->renderTexture(m_session->m_tempFB->getTexture(), texbox, {}); + g_pHyprRenderer->draw(makeUnique(CTexPassElement::SRenderData{ + .tex = m_session->m_tempFB->getTexture(), + .box = texbox, + }), + texbox); m_session->m_tempFB->release(); return; } @@ -371,10 +400,7 @@ bool CScreenshareFrame::copyShm() { if (done()) return false; - g_pHyprOpenGL->makeEGLCurrent(); - - auto shm = m_buffer->shm(); - auto [pixelData, fmt, bufLen] = m_buffer->beginDataPtr(0); // no need for end, cuz it's shm + auto shm = m_buffer->shm(); const auto PFORMAT = NFormatUtils::getPixelFormatFromDRM(shm.format); if (!PFORMAT) { @@ -387,7 +413,7 @@ bool CScreenshareFrame::copyShm() { auto outFB = g_pHyprRenderer->createFB(); outFB->alloc(m_bufferSize.x, m_bufferSize.y, shm.format); - if (!g_pHyprRenderer->beginRender(PMONITOR, m_damage, RENDER_MODE_FULL_FAKE, nullptr, outFB, true)) { + if (!g_pHyprRenderer->beginFullFakeRender(PMONITOR, m_damage, outFB)) { LOGM(Log::ERR, "Can't copy: failed to begin rendering"); return false; } @@ -398,53 +424,11 @@ bool CScreenshareFrame::copyShm() { g_pHyprRenderer->endRender(); - g_pHyprRenderer->m_renderData.pMonitor = PMONITOR; - outFB->bind(); - glBindFramebuffer(GL_READ_FRAMEBUFFER, GLFB(outFB)->getFBID()); - - glPixelStorei(GL_PACK_ALIGNMENT, 1); - - uint32_t packStride = NFormatUtils::minStride(PFORMAT, m_bufferSize.x); - int glFormat = PFORMAT->glFormat; - - if (glFormat == GL_RGBA) - glFormat = GL_BGRA_EXT; - - if (glFormat != GL_BGRA_EXT && glFormat != GL_RGB) { - if (PFORMAT->swizzle.has_value()) { - std::array RGBA = SWIZZLE_RGBA; - std::array BGRA = SWIZZLE_BGRA; - if (PFORMAT->swizzle == RGBA) - glFormat = GL_RGBA; - else if (PFORMAT->swizzle == BGRA) - glFormat = GL_BGRA_EXT; - else { - LOGM(Log::ERR, "Copied frame via shm might be broken or color flipped"); - glFormat = GL_RGBA; - } - } - } - - // TODO: use pixel buffer object to not block cpu - if (packStride == sc(shm.stride)) { - m_damage.forEachRect([&](const auto& rect) { - int width = rect.x2 - rect.x1; - int height = rect.y2 - rect.y1; - glReadPixels(rect.x1, rect.y1, width, height, glFormat, PFORMAT->glType, pixelData); - }); - } else { - m_damage.forEachRect([&](const auto& rect) { - size_t width = rect.x2 - rect.x1; - size_t height = rect.y2 - rect.y1; - for (size_t i = rect.y1; i < height; ++i) { - glReadPixels(rect.x1, i, width, 1, glFormat, PFORMAT->glType, pixelData + (rect.x1 * PFORMAT->bytesPerBlock) + (i * shm.stride)); - } - }); - } - - GLFB(outFB)->unbind(); - glPixelStorei(GL_PACK_ALIGNMENT, 4); - glBindFramebuffer(GL_READ_FRAMEBUFFER, 0); + m_damage.forEachRect([&](const auto& rect) { + int width = rect.x2 - rect.x1; + int height = rect.y2 - rect.y1; + outFB->readPixels(m_buffer, rect.x1, rect.y1, width, height); + }); g_pHyprRenderer->m_renderData.pMonitor.reset(); @@ -463,7 +447,7 @@ void CScreenshareFrame::storeTempFB() { CRegion fakeDamage = {0, 0, INT16_MAX, INT16_MAX}; - if (!g_pHyprRenderer->beginRender(m_session->monitor(), fakeDamage, RENDER_MODE_FULL_FAKE, nullptr, m_session->m_tempFB, true)) { + if (!g_pHyprRenderer->beginFullFakeRender(m_session->monitor(), fakeDamage, m_session->m_tempFB)) { LOGM(Log::ERR, "Can't copy: failed to begin rendering to temp fb"); return; } diff --git a/src/render/GLRenderer.cpp b/src/render/GLRenderer.cpp new file mode 100644 index 000000000..c1f648371 --- /dev/null +++ b/src/render/GLRenderer.cpp @@ -0,0 +1,405 @@ +#include "GLRenderer.hpp" +#include +#include "../config/ConfigValue.hpp" +#include "../managers/CursorManager.hpp" +#include "../managers/PointerManager.hpp" +#include "../protocols/SessionLock.hpp" +#include "../protocols/LayerShell.hpp" +#include "../protocols/PresentationTime.hpp" +#include "../protocols/core/DataDevice.hpp" +#include "../protocols/core/Compositor.hpp" +#include "../debug/HyprDebugOverlay.hpp" +#include "helpers/Monitor.hpp" +#include "pass/TexPassElement.hpp" +#include "pass/ClearPassElement.hpp" +#include "pass/RectPassElement.hpp" +#include "pass/SurfacePassElement.hpp" +#include "debug/log/Logger.hpp" +#include "../protocols/types/ContentType.hpp" +#include "render/OpenGL.hpp" +#include "render/Renderer.hpp" +#include "render/gl/GLFramebuffer.hpp" +#include "render/gl/GLTexture.hpp" +#include "decorations/CHyprDropShadowDecoration.hpp" + +#include +#include +#include +using namespace Hyprutils::Utils; +using namespace Hyprutils::OS; +using enum NContentType::eContentType; +using namespace NColorManagement; + +extern "C" { +#include +} + +CHyprGLRenderer::CHyprGLRenderer() : IHyprRenderer() {} + +void CHyprGLRenderer::initRender() { + g_pHyprOpenGL->makeEGLCurrent(); + g_pHyprRenderer->m_renderData.pMonitor = renderData().pMonitor; +} + +bool CHyprGLRenderer::initRenderBuffer(SP buffer, uint32_t fmt) { + try { + m_currentRenderbuffer = getOrCreateRenderbuffer(m_currentBuffer, fmt); + } catch (std::exception& e) { + Log::logger->log(Log::ERR, "getOrCreateRenderbuffer failed for {}", NFormatUtils::drmFormatName(fmt)); + return false; + } + + return m_currentRenderbuffer; +} + +bool CHyprGLRenderer::beginFullFakeRenderInternal(PHLMONITOR pMonitor, CRegion& damage, SP fb, bool simple) { + initRender(); + + 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; +} + +bool CHyprGLRenderer::beginRenderInternal(PHLMONITOR pMonitor, CRegion& damage, bool simple) { + + m_currentRenderbuffer->bind(); + if (simple) + g_pHyprOpenGL->beginSimple(pMonitor, damage, m_currentRenderbuffer); + else + g_pHyprOpenGL->begin(pMonitor, damage); + + return true; +} + +void CHyprGLRenderer::endRender(const std::function& renderingDoneCallback) { + const auto PMONITOR = g_pHyprRenderer->m_renderData.pMonitor; + static auto PNVIDIAANTIFLICKER = CConfigValue("opengl:nvidia_anti_flicker"); + + g_pHyprRenderer->m_renderData.damage = m_renderPass.render(g_pHyprRenderer->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 { + g_pHyprRenderer->m_renderData.pMonitor.reset(); + g_pHyprRenderer->m_renderData.mouseZoomFactor = 1.f; + g_pHyprRenderer->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 (!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 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(); + } +} + +void CHyprGLRenderer::renderOffToMain(IFramebuffer* off) { + g_pHyprOpenGL->renderOffToMain(off); +} + +SP CHyprGLRenderer::getOrCreateRenderbufferInternal(SP buffer, uint32_t fmt) { + g_pHyprOpenGL->makeEGLCurrent(); + return makeShared(buffer, fmt); +} + +SP CHyprGLRenderer::createStencilTexture(const int width, const int height) { + g_pHyprOpenGL->makeEGLCurrent(); + auto tex = makeShared(); + tex->allocate({width, height}); + + return tex; +} + +SP CHyprGLRenderer::createTexture(bool opaque) { + g_pHyprOpenGL->makeEGLCurrent(); + return makeShared(opaque); +} + +SP CHyprGLRenderer::createTexture(uint32_t drmFormat, uint8_t* pixels, uint32_t stride, const Vector2D& size, bool keepDataCopy, bool opaque) { + g_pHyprOpenGL->makeEGLCurrent(); + return makeShared(drmFormat, pixels, stride, size, keepDataCopy, opaque); +} + +SP CHyprGLRenderer::createTexture(const Aquamarine::SDMABUFAttrs& attrs, bool opaque) { + g_pHyprOpenGL->makeEGLCurrent(); + const auto image = g_pHyprOpenGL->createEGLImage(attrs); + if (!image) + return nullptr; + return makeShared(attrs, image, opaque); +} + +SP CHyprGLRenderer::createTexture(const int width, const int height, unsigned char* const data) { + g_pHyprOpenGL->makeEGLCurrent(); + SP tex = makeShared(); + + tex->allocate({width, height}); + + 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; + + 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); + + glTexImage2D(GL_TEXTURE_2D, 0, glFormat, tex->m_size.x, tex->m_size.y, 0, glFormat, glType, data); + tex->unbind(); + + return tex; +} + +SP CHyprGLRenderer::createTexture(cairo_surface_t* cairo) { + g_pHyprOpenGL->makeEGLCurrent(); + const auto CAIROFORMAT = cairo_image_surface_get_format(cairo); + auto tex = makeShared(); + + tex->allocate({cairo_image_surface_get_width(cairo), cairo_image_surface_get_height(cairo)}); + + 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; + + 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); + + if (CAIROFORMAT != CAIRO_FORMAT_RGB96F) { + tex->setTexParameter(GL_TEXTURE_SWIZZLE_R, GL_BLUE); + tex->setTexParameter(GL_TEXTURE_SWIZZLE_B, GL_RED); + } + + glTexImage2D(GL_TEXTURE_2D, 0, glIFormat, tex->m_size.x, tex->m_size.y, 0, glFormat, glType, DATA); + + return tex; +} + +SP CHyprGLRenderer::createTexture(std::span lut3D, size_t N) { + g_pHyprOpenGL->makeEGLCurrent(); + return makeShared(lut3D, N); +} + +bool CHyprGLRenderer::explicitSyncSupported() { + return g_pHyprOpenGL->explicitSyncSupported(); +} + +std::vector CHyprGLRenderer::getDRMFormats() { + return g_pHyprOpenGL->getDRMFormats(); +} + +std::vector CHyprGLRenderer::getDRMFormatModifiers(DRMFormat format) { + return g_pHyprOpenGL->getDRMFormatModifiers(format); +} + +SP CHyprGLRenderer::createFB(const std::string& name) { + g_pHyprOpenGL->makeEGLCurrent(); + return makeShared(name); +} + +void CHyprGLRenderer::disableScissor() { + g_pHyprOpenGL->scissor(nullptr); +} + +void CHyprGLRenderer::blend(bool enabled) { + g_pHyprOpenGL->blend(enabled); +} + +void CHyprGLRenderer::drawShadow(const CBox& box, int round, float roundingPower, int range, CHyprColor color, float a) { + g_pHyprOpenGL->renderRoundedShadow(box, round, roundingPower, range, color, a); +} + +SP CHyprGLRenderer::blurFramebuffer(SP source, float a, CRegion* originalDamage) { + auto src = GLFB(source); + return g_pHyprOpenGL->blurFramebufferWithDamage(a, originalDamage, *src)->getTexture(); +} + +void CHyprGLRenderer::setViewport(int x, int y, int width, int height) { + g_pHyprOpenGL->setViewport(x, y, width, height); +} + +bool CHyprGLRenderer::reloadShaders(const std::string& path) { + return g_pHyprOpenGL->initShaders(path); +} + +void CHyprGLRenderer::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}); +}; + +void CHyprGLRenderer::draw(CClearPassElement* element, const CRegion& damage) { + g_pHyprOpenGL->clear(element->m_data.color); +}; + +void CHyprGLRenderer::draw(CFramebufferElement* element, const CRegion& damage) { + const auto m_data = element->m_data; + SP fb = nullptr; + + if (m_data.main) { + switch (m_data.framebufferID) { + case FB_MONITOR_RENDER_MAIN: fb = g_pHyprRenderer->m_renderData.mainFB; break; + case FB_MONITOR_RENDER_CURRENT: fb = g_pHyprRenderer->m_renderData.currentFB; break; + case FB_MONITOR_RENDER_OUT: fb = g_pHyprRenderer->m_renderData.outFB; break; + default: fb = nullptr; + } + + if (!fb) { + Log::logger->log(Log::ERR, "BUG THIS: CFramebufferElement::draw: main but null"); + return; + } + + } else { + switch (m_data.framebufferID) { + case FB_MONITOR_RENDER_EXTRA_OFFLOAD: fb = g_pHyprRenderer->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 = g_pHyprRenderer->m_renderData.pMonitor->m_offMainFB; break; + case FB_MONITOR_RENDER_EXTRA_MONITOR_MIRROR: fb = g_pHyprRenderer->m_renderData.pMonitor->m_monitorMirrorFB; break; + case FB_MONITOR_RENDER_EXTRA_BLUR: fb = g_pHyprRenderer->m_renderData.pMonitor->m_blurFB; break; + default: fb = nullptr; + } + + if (!fb) { + Log::logger->log(Log::ERR, "BUG THIS: CFramebufferElement::draw: not main but null"); + return; + } + } + + fb->bind(); +}; + +void CHyprGLRenderer::draw(CPreBlurElement* element, const CRegion& damage) { + auto dmg = damage; + g_pHyprRenderer->preBlurForCurrentMonitor(&dmg); +}; + +void CHyprGLRenderer::draw(CRectPassElement* element, const CRegion& damage) { + const auto m_data = element->m_data; + + 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}); +}; + +void CHyprGLRenderer::draw(CShadowPassElement* element, const CRegion& damage) { + const auto m_data = element->m_data; + m_data.deco->render(g_pHyprRenderer->m_renderData.pMonitor.lock(), m_data.a); +}; + +void CHyprGLRenderer::draw(CTexPassElement* element, const CRegion& damage) { + const auto m_data = element->m_data; + + g_pHyprOpenGL->renderTexture( // + m_data.tex, m_data.box, + { + // blur settings for m_data.blur == true + .blur = m_data.blur, + .blurA = m_data.blurA, + .overallA = m_data.overallA, + .blockBlurOptimization = m_data.blockBlurOptimization.value_or(false), + .blurredBG = m_data.blurredBG, + + // common settings + .damage = m_data.damage.empty() ? &damage : &m_data.damage, + .surface = m_data.surface, + .a = m_data.a, + .round = m_data.round, + .roundingPower = m_data.roundingPower, + .discardActive = m_data.discardActive, + .allowCustomUV = m_data.allowCustomUV, + .cmBackToSRGB = m_data.cmBackToSRGB, + .cmBackToSRGBSource = m_data.cmBackToSRGBSource, + .discardMode = m_data.ignoreAlpha.has_value() ? sc(DISCARD_ALPHA) : m_data.discardMode, + .discardOpacity = m_data.ignoreAlpha.has_value() ? *m_data.ignoreAlpha : m_data.discardOpacity, + .clipRegion = m_data.clipRegion, + .currentLS = m_data.currentLS, + + .primarySurfaceUVTopLeft = g_pHyprRenderer->m_renderData.primarySurfaceUVTopLeft, + .primarySurfaceUVBottomRight = g_pHyprRenderer->m_renderData.primarySurfaceUVBottomRight, + }); +}; + +void CHyprGLRenderer::draw(CTextureMatteElement* element, const CRegion& damage) { + const auto m_data = element->m_data; + + g_pHyprOpenGL->renderTextureMatte(m_data.tex, m_data.box, m_data.fb); +}; + +SP CHyprGLRenderer::getBlurTexture(PHLMONITORREF pMonitor) { + return pMonitor->m_blurFB->getTexture(); +} + +void CHyprGLRenderer::unsetEGL() { + if (!g_pHyprOpenGL) + return; + + eglMakeCurrent(g_pHyprOpenGL->m_eglDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT); +} diff --git a/src/render/GLRenderer.hpp b/src/render/GLRenderer.hpp new file mode 100644 index 000000000..1446e9b29 --- /dev/null +++ b/src/render/GLRenderer.hpp @@ -0,0 +1,52 @@ +#pragma once + +#include "Renderer.hpp" + +class CHyprGLRenderer : public IHyprRenderer { + public: + CHyprGLRenderer(); + + void endRender(const std::function& renderingDoneCallback = {}) override; + SP createStencilTexture(const int width, const int height) override; + SP createTexture(bool opaque = false) override; + SP createTexture(uint32_t drmFormat, uint8_t* pixels, uint32_t stride, const Vector2D& size, bool keepDataCopy = false, bool opaque = false) override; + SP createTexture(const Aquamarine::SDMABUFAttrs&, bool opaque = false) override; + SP createTexture(const int width, const int height, unsigned char* const data) override; + SP createTexture(cairo_surface_t* cairo) override; + SP createTexture(std::span lut3D, size_t N) override; + bool explicitSyncSupported() override; + std::vector getDRMFormats() override; + std::vector getDRMFormatModifiers(DRMFormat format) override; + SP createFB(const std::string& name = "") override; + void disableScissor() override; + void blend(bool enabled) override; + void drawShadow(const CBox& box, int round, float roundingPower, int range, CHyprColor color, float a) override; + SP blurFramebuffer(SP source, float a, CRegion* originalDamage) override; + void setViewport(int x, int y, int width, int height) override; + bool reloadShaders(const std::string& path = "") override; + + void unsetEGL(); + + private: + void renderOffToMain(IFramebuffer* off) override; + SP getOrCreateRenderbufferInternal(SP buffer, uint32_t fmt) override; + bool beginRenderInternal(PHLMONITOR pMonitor, CRegion& damage, bool simple = false) override; + bool beginFullFakeRenderInternal(PHLMONITOR pMonitor, CRegion& damage, SP fb, bool simple = false) override; + void initRender() override; + bool initRenderBuffer(SP buffer, uint32_t fmt) override; + + void draw(CBorderPassElement* element, const CRegion& damage) override; + void draw(CClearPassElement* element, const CRegion& damage) override; + void draw(CFramebufferElement* element, const CRegion& damage) override; + void draw(CPreBlurElement* element, const CRegion& damage) override; + void draw(CRectPassElement* element, const CRegion& damage) override; + void draw(CShadowPassElement* element, const CRegion& damage) override; + void draw(CTexPassElement* element, const CRegion& damage) override; + void draw(CTextureMatteElement* element, const CRegion& damage) override; + + SP getBlurTexture(PHLMONITORREF pMonitor) override; + + SP m_currentRenderbuffer = nullptr; + + friend class CHyprOpenGLImpl; +}; diff --git a/src/render/OpenGL.cpp b/src/render/OpenGL.cpp index 97cefb485..9cee975b2 100644 --- a/src/render/OpenGL.cpp +++ b/src/render/OpenGL.cpp @@ -1,6 +1,8 @@ #include +#include #include -#include +#include +#include #include #include #include @@ -37,6 +39,7 @@ #include "pass/RectPassElement.hpp" #include "pass/PreBlurElement.hpp" #include "pass/ClearPassElement.hpp" +#include "render/GLRenderer.hpp" #include "render/Shader.hpp" #include "AsyncResourceGatherer.hpp" #include @@ -46,10 +49,11 @@ #include #include #include +#include #include +#include "./shaders/Shaders.hpp" #include "ShaderLoader.hpp" #include "Texture.hpp" -#include #include "gl/GLFramebuffer.hpp" #include "gl/GLTexture.hpp" @@ -662,8 +666,6 @@ void CHyprOpenGLImpl::beginSimple(PHLMONITOR pMonitor, const CRegion& damage, SP setViewport(0, 0, pMonitor->m_pixelSize.x, pMonitor->m_pixelSize.y); - g_pHyprRenderer->setProjectionType(FBO->m_size); - if (!m_shadersInitialized) initShaders(); @@ -710,42 +712,12 @@ void CHyprOpenGLImpl::begin(PHLMONITOR pMonitor, const CRegion& damage_, SPm_pixelSize.x, pMonitor->m_pixelSize.y); - g_pHyprRenderer->setProjectionType(RPT_MONITOR); - - if (pMonitor && (!pMonitor->m_offloadFB || pMonitor->m_offloadFB->m_size != pMonitor->m_pixelSize)) - destroyMonitorResources(pMonitor); if (!m_shadersInitialized) initShaders(); - const auto DRM_FORMAT = fb ? fb->m_drmFormat : pMonitor->m_output->state->state().drmFormat; - - // ensure a framebuffer for the monitor exists - if (!g_pHyprRenderer->m_renderData.pMonitor->m_offloadFB || g_pHyprRenderer->m_renderData.pMonitor->m_offloadFB->m_size != pMonitor->m_pixelSize || - DRM_FORMAT != g_pHyprRenderer->m_renderData.pMonitor->m_offloadFB->m_drmFormat) { - g_pHyprRenderer->m_renderData.pMonitor->m_stencilTex = g_pHyprRenderer->createStencilTexture(pMonitor->m_pixelSize.x, pMonitor->m_pixelSize.y); - g_pHyprRenderer->m_renderData.pMonitor->m_offloadFB = g_pHyprRenderer->createFB(); - g_pHyprRenderer->m_renderData.pMonitor->m_mirrorFB = g_pHyprRenderer->createFB(); - g_pHyprRenderer->m_renderData.pMonitor->m_mirrorSwapFB = g_pHyprRenderer->createFB(); - - g_pHyprRenderer->m_renderData.pMonitor->m_offloadFB->addStencil(g_pHyprRenderer->m_renderData.pMonitor->m_stencilTex); - g_pHyprRenderer->m_renderData.pMonitor->m_mirrorFB->addStencil(g_pHyprRenderer->m_renderData.pMonitor->m_stencilTex); - g_pHyprRenderer->m_renderData.pMonitor->m_mirrorSwapFB->addStencil(g_pHyprRenderer->m_renderData.pMonitor->m_stencilTex); - - g_pHyprRenderer->m_renderData.pMonitor->m_offloadFB->alloc(pMonitor->m_pixelSize.x, pMonitor->m_pixelSize.y, DRM_FORMAT); - g_pHyprRenderer->m_renderData.pMonitor->m_mirrorFB->alloc(pMonitor->m_pixelSize.x, pMonitor->m_pixelSize.y, DRM_FORMAT); - g_pHyprRenderer->m_renderData.pMonitor->m_mirrorSwapFB->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); + const bool NEEDS_COPY_FB = g_pHyprRenderer->needsACopyFB(g_pHyprRenderer->m_renderData.pMonitor.lock()); g_pHyprRenderer->m_renderData.transformDamage = true; if (HAS_MIRROR_FB != NEEDS_COPY_FB) { @@ -770,32 +742,30 @@ void CHyprOpenGLImpl::begin(PHLMONITOR pMonitor, const CRegion& damage_, SPm_renderData.mainFB = g_pHyprRenderer->m_renderData.currentFB; - g_pHyprRenderer->m_renderData.outFB = fb ? fb : g_pHyprRenderer->getCurrentRBO()->getFB(); + g_pHyprRenderer->m_renderData.outFB = fb ? fb : dc(g_pHyprRenderer.get())->m_currentRenderbuffer->getFB(); g_pHyprRenderer->pushMonitorTransformEnabled(false); } void CHyprOpenGLImpl::end() { static auto PZOOMDISABLEAA = CConfigValue("cursor:zoom_disable_aa"); - + auto& m_renderData = g_pHyprRenderer->m_renderData; TRACY_GPU_ZONE("RenderEnd"); g_pHyprRenderer->m_renderData.currentWindow.reset(); g_pHyprRenderer->m_renderData.surface.reset(); - g_pHyprRenderer->m_renderData.currentLS.reset(); g_pHyprRenderer->m_renderData.clipBox = {}; - g_pHyprRenderer->m_renderData.clipRegion.clear(); // end the render, copy the data to the main framebuffer if LIKELY (m_offloadedFramebuffer) { g_pHyprRenderer->m_renderData.damage = g_pHyprRenderer->m_renderData.finalDamage; g_pHyprRenderer->pushMonitorTransformEnabled(true); - CBox monbox = {0, 0, g_pHyprRenderer->m_renderData.pMonitor->m_transformedSize.x, g_pHyprRenderer->m_renderData.pMonitor->m_transformedSize.y}; + CBox monbox = {0, 0, m_renderData.pMonitor->m_transformedSize.x, m_renderData.pMonitor->m_transformedSize.y}; if LIKELY (g_pHyprRenderer->m_renderMode == RENDER_MODE_NORMAL && g_pHyprRenderer->m_renderData.mouseZoomFactor == 1.0f) - g_pHyprRenderer->m_renderData.pMonitor->m_zoomController.m_resetCameraState = true; - g_pHyprRenderer->m_renderData.pMonitor->m_zoomController.applyZoomTransform(monbox, g_pHyprRenderer->m_renderData); + m_renderData.pMonitor->m_zoomController.m_resetCameraState = true; + m_renderData.pMonitor->m_zoomController.applyZoomTransform(monbox, m_renderData); m_applyFinalShader = !g_pHyprRenderer->m_renderData.blockScreenShader; if UNLIKELY (g_pHyprRenderer->m_renderData.mouseZoomFactor != 1.F && g_pHyprRenderer->m_renderData.mouseZoomUseMouse && *PZOOMDISABLEAA) @@ -803,7 +773,7 @@ void CHyprOpenGLImpl::end() { // copy the damaged areas into the mirror buffer // we can't use the offloadFB for mirroring / ss, as it contains artifacts from blurring - if UNLIKELY (needsACopyFB(g_pHyprRenderer->m_renderData.pMonitor.lock()) && !m_fakeFrame) + if UNLIKELY (g_pHyprRenderer->needsACopyFB(g_pHyprRenderer->m_renderData.pMonitor.lock()) && !m_fakeFrame) saveBufferForMirror(monbox); g_pHyprRenderer->m_renderData.outFB->bind(); @@ -825,25 +795,17 @@ void CHyprOpenGLImpl::end() { } // invalidate our render FBs to signal to the driver we don't need them anymore - if (g_pHyprRenderer->m_renderData.pMonitor->m_mirrorFB) { - g_pHyprRenderer->m_renderData.pMonitor->m_mirrorFB->bind(); - GLFB(g_pHyprRenderer->m_renderData.pMonitor->m_mirrorFB)->invalidate({GL_STENCIL_ATTACHMENT, GL_COLOR_ATTACHMENT0}); - } - if (g_pHyprRenderer->m_renderData.pMonitor->m_mirrorSwapFB) { - g_pHyprRenderer->m_renderData.pMonitor->m_mirrorSwapFB->bind(); - GLFB(g_pHyprRenderer->m_renderData.pMonitor->m_mirrorSwapFB)->invalidate({GL_STENCIL_ATTACHMENT, GL_COLOR_ATTACHMENT0}); - } - if (g_pHyprRenderer->m_renderData.pMonitor->m_offloadFB) { - g_pHyprRenderer->m_renderData.pMonitor->m_offloadFB->bind(); - GLFB(g_pHyprRenderer->m_renderData.pMonitor->m_offloadFB)->invalidate({GL_STENCIL_ATTACHMENT, GL_COLOR_ATTACHMENT0}); - } - if (g_pHyprRenderer->m_renderData.pMonitor->m_offMainFB) { - g_pHyprRenderer->m_renderData.pMonitor->m_offMainFB->bind(); - GLFB(g_pHyprRenderer->m_renderData.pMonitor->m_offMainFB)->invalidate({GL_STENCIL_ATTACHMENT, GL_COLOR_ATTACHMENT0}); - } + g_pHyprRenderer->m_renderData.pMonitor->m_mirrorFB->bind(); + GLFB(g_pHyprRenderer->m_renderData.pMonitor->m_mirrorFB)->invalidate({GL_STENCIL_ATTACHMENT, GL_COLOR_ATTACHMENT0}); + g_pHyprRenderer->m_renderData.pMonitor->m_mirrorSwapFB->bind(); + GLFB(g_pHyprRenderer->m_renderData.pMonitor->m_mirrorSwapFB)->invalidate({GL_STENCIL_ATTACHMENT, GL_COLOR_ATTACHMENT0}); + g_pHyprRenderer->m_renderData.pMonitor->m_offloadFB->bind(); + GLFB(g_pHyprRenderer->m_renderData.pMonitor->m_offloadFB)->invalidate({GL_STENCIL_ATTACHMENT, GL_COLOR_ATTACHMENT0}); + g_pHyprRenderer->m_renderData.pMonitor->m_offMainFB->bind(); + GLFB(g_pHyprRenderer->m_renderData.pMonitor->m_offMainFB)->invalidate({GL_STENCIL_ATTACHMENT, GL_COLOR_ATTACHMENT0}); // reset our data - g_pHyprRenderer->m_renderData.pMonitor.reset(); + m_renderData.pMonitor.reset(); g_pHyprRenderer->m_renderData.mouseZoomFactor = 1.f; g_pHyprRenderer->m_renderData.mouseZoomUseMouse = true; g_pHyprRenderer->m_renderData.blockScreenShader = false; @@ -870,15 +832,6 @@ void CHyprOpenGLImpl::end() { } } -bool CHyprOpenGLImpl::needsACopyFB(PHLMONITOR mon) { - return !mon->m_mirrors.empty() || Screenshare::mgr()->isOutputBeingSSd(mon); -} - -void CHyprOpenGLImpl::setDamage(const CRegion& damage_, std::optional finalDamage) { - g_pHyprRenderer->m_renderData.damage.set(damage_); - g_pHyprRenderer->m_renderData.finalDamage.set(finalDamage.value_or(damage_)); -} - static const std::vector SHADER_INCLUDES = { "defines.h", "constants.h", "cm_helpers.glsl", "rounding.glsl", "CM.glsl", "tonemap.glsl", "gain.glsl", "border.glsl", "shadow.glsl", "blurprepare.glsl", "blur1.glsl", "blur2.glsl", "blurFinish.glsl", @@ -1005,15 +958,16 @@ void CHyprOpenGLImpl::blend(bool enabled) { } void CHyprOpenGLImpl::scissor(const CBox& originalBox, bool transform) { - RASSERT(g_pHyprRenderer->m_renderData.pMonitor, "Tried to scissor without begin()!"); + auto& m_renderData = g_pHyprRenderer->m_renderData; + RASSERT(m_renderData.pMonitor, "Tried to scissor without begin()!"); // only call glScissor if the box has changed static CBox m_lastScissorBox = {}; if (transform) { CBox box = originalBox; - const auto TR = Math::wlTransformToHyprutils(Math::invertTransform(g_pHyprRenderer->m_renderData.pMonitor->m_transform)); - box.transform(TR, g_pHyprRenderer->m_renderData.pMonitor->m_transformedSize.x, g_pHyprRenderer->m_renderData.pMonitor->m_transformedSize.y); + const auto TR = Math::wlTransformToHyprutils(Math::invertTransform(m_renderData.pMonitor->m_transform)); + box.transform(TR, m_renderData.pMonitor->m_transformedSize.x, m_renderData.pMonitor->m_transformedSize.y); if (box != m_lastScissorBox) { GLCALL(glScissor(box.x, box.y, box.width, box.height)); @@ -1067,7 +1021,7 @@ void CHyprOpenGLImpl::renderRectWithBlurInternal(const CBox& box, const CHyprCol CRegion damage{g_pHyprRenderer->m_renderData.damage}; damage.intersect(box); - auto POUTFB = data.xray ? g_pHyprRenderer->m_renderData.pMonitor->m_blurFB : blurMainFramebufferWithDamage(data.blurA, &damage); + auto POUTFB = data.xray ? g_pHyprRenderer->m_renderData.pMonitor->m_blurFB->getTexture() : g_pHyprRenderer->blurMainFramebuffer(data.blurA, &damage); g_pHyprRenderer->m_renderData.currentFB->bind(); @@ -1075,7 +1029,7 @@ void CHyprOpenGLImpl::renderRectWithBlurInternal(const CBox& box, const CHyprCol g_pHyprRenderer->pushMonitorTransformEnabled(true); const auto SAVEDRENDERMODIF = g_pHyprRenderer->m_renderData.renderModif; g_pHyprRenderer->m_renderData.renderModif = {}; // fix shit - renderTexture(POUTFB->getTexture(), MONITORBOX, + renderTexture(POUTFB, MONITORBOX, STextureRenderData{.damage = &damage, .a = data.blurA, .round = data.round, .roundingPower = 2.F, .allowCustomUV = false, .allowDim = false, .noAA = false}); g_pHyprRenderer->popMonitorTransformEnabled(); g_pHyprRenderer->m_renderData.renderModif = SAVEDRENDERMODIF; @@ -1084,8 +1038,9 @@ void CHyprOpenGLImpl::renderRectWithBlurInternal(const CBox& box, const CHyprCol } void CHyprOpenGLImpl::renderRectWithDamageInternal(const CBox& box, const CHyprColor& col, const SRectRenderData& data) { + auto& m_renderData = g_pHyprRenderer->m_renderData; RASSERT((box.width > 0 && box.height > 0), "Tried to render rect with width/height < 0!"); - RASSERT(g_pHyprRenderer->m_renderData.pMonitor, "Tried to render rect without begin()!"); + RASSERT(m_renderData.pMonitor, "Tried to render rect without begin()!"); TRACY_GPU_ZONE("RenderRectWithDamage"); @@ -1101,8 +1056,8 @@ void CHyprOpenGLImpl::renderRectWithDamageInternal(const CBox& box, const CHyprC shader->setUniformFloat4(SHADER_COLOR, col.r * col.a, col.g * col.a, col.b * col.a, col.a); CBox transformedBox = box; - transformedBox.transform(Math::wlTransformToHyprutils(Math::invertTransform(g_pHyprRenderer->m_renderData.pMonitor->m_transform)), - g_pHyprRenderer->m_renderData.pMonitor->m_transformedSize.x, g_pHyprRenderer->m_renderData.pMonitor->m_transformedSize.y); + transformedBox.transform(Math::wlTransformToHyprutils(Math::invertTransform(m_renderData.pMonitor->m_transform)), m_renderData.pMonitor->m_transformedSize.x, + m_renderData.pMonitor->m_transformedSize.y); const auto TOPLEFT = Vector2D(transformedBox.x, transformedBox.y); const auto FULLSIZE = Vector2D(transformedBox.width, transformedBox.height); @@ -1231,6 +1186,8 @@ WP CHyprOpenGLImpl::renderToOutputInternal() { static const auto PDT = CConfigValue("debug:damage_tracking"); static const auto PCURSORTIMEOUT = CConfigValue("cursor:inactive_timeout"); + auto& m_renderData = g_pHyprRenderer->m_renderData; + WP shader = g_pHyprRenderer->m_crashingInProgress ? getShaderVariant(SH_FRAG_GLITCH) : (m_finalScreenShader->program() ? m_finalScreenShader : getShaderVariant(SH_FRAG_PASSTHRURGBA)); @@ -1241,8 +1198,8 @@ WP CHyprOpenGLImpl::renderToOutputInternal() { else shader->setUniformFloat(SHADER_TIME, 0.f); - shader->setUniformInt(SHADER_WL_OUTPUT, g_pHyprRenderer->m_renderData.pMonitor->m_id); - shader->setUniformFloat2(SHADER_FULL_SIZE, g_pHyprRenderer->m_renderData.pMonitor->m_pixelSize.x, g_pHyprRenderer->m_renderData.pMonitor->m_pixelSize.y); + shader->setUniformInt(SHADER_WL_OUTPUT, m_renderData.pMonitor->m_id); + shader->setUniformFloat2(SHADER_FULL_SIZE, m_renderData.pMonitor->m_pixelSize.x, m_renderData.pMonitor->m_pixelSize.y); shader->setUniformFloat(SHADER_POINTER_INACTIVE_TIMEOUT, *PCURSORTIMEOUT); shader->setUniformInt(SHADER_POINTER_HIDDEN, g_pHyprRenderer->m_cursorHiddenByCondition); shader->setUniformInt(SHADER_POINTER_KILLING, g_pInputManager->getClickMode() == CLICKMODE_KILL); @@ -1251,7 +1208,7 @@ WP CHyprOpenGLImpl::renderToOutputInternal() { shader->setUniformFloat(SHADER_POINTER_SIZE, g_pCursorManager->getScaledSize()); if (*PDT == 0) { - PHLMONITORREF pMonitor = g_pHyprRenderer->m_renderData.pMonitor; + PHLMONITORREF pMonitor = m_renderData.pMonitor; Vector2D p = ((g_pInputManager->getMouseCoordsInternal() - pMonitor->m_position) * pMonitor->m_scale); p = p.transform(Math::wlTransformToHyprutils(pMonitor->m_transform), pMonitor->m_pixelSize); shader->setUniformFloat2(SHADER_POINTER, p.x / pMonitor->m_pixelSize.x, p.y / pMonitor->m_pixelSize.y); @@ -1292,7 +1249,7 @@ WP CHyprOpenGLImpl::renderToOutputInternal() { if (g_pHyprRenderer->m_crashingInProgress) { shader->setUniformFloat(SHADER_DISTORT, g_pHyprRenderer->m_crashingDistort); - shader->setUniformFloat2(SHADER_FULL_SIZE, g_pHyprRenderer->m_renderData.pMonitor->m_pixelSize.x, g_pHyprRenderer->m_renderData.pMonitor->m_pixelSize.y); + shader->setUniformFloat2(SHADER_FULL_SIZE, m_renderData.pMonitor->m_pixelSize.x, m_renderData.pMonitor->m_pixelSize.y); } return shader; @@ -1303,6 +1260,8 @@ WP CHyprOpenGLImpl::renderToFBInternal(const STextureRenderData& data, static const auto PENABLECM = CConfigValue("render:cm_enabled"); static auto PBLEND = CConfigValue("render:use_shader_blur_blend"); + auto& m_renderData = g_pHyprRenderer->m_renderData; + float alpha = std::clamp(data.a, 0.f, 1.f); WP shader; @@ -1368,9 +1327,8 @@ WP CHyprOpenGLImpl::renderToFBInternal(const STextureRenderData& data, || g_pHyprRenderer->m_renderData.pMonitor->doesNoShaderCM() /* no shader needed */ || (SOURCE_IMAGE_DESCRIPTION->id() == TARGET_IMAGE_DESCRIPTION->id() && !CANT_CHECK_CM_EQUALITY) /* Source and target have the same image description */ || (((*PPASS && canPassHDRSurface) || - (*PPASS == 1 && !isHDRSurface && g_pHyprRenderer->m_renderData.pMonitor->m_cmType != NCMType::CM_HDR && - g_pHyprRenderer->m_renderData.pMonitor->m_cmType != NCMType::CM_HDR_EDID)) && - g_pHyprRenderer->m_renderData.pMonitor->inFullscreenMode()) /* Fullscreen window with pass cm enabled */; + (*PPASS == 1 && !isHDRSurface && m_renderData.pMonitor->m_cmType != NCMType::CM_HDR && m_renderData.pMonitor->m_cmType != NCMType::CM_HDR_EDID)) && + m_renderData.pMonitor->inFullscreenMode()) /* Fullscreen window with pass cm enabled */; if (data.allowDim && g_pHyprRenderer->m_renderData.currentWindow && (g_pHyprRenderer->m_renderData.currentWindow->m_notRespondingTint->value() > 0 || g_pHyprRenderer->m_renderData.currentWindow->m_dimPercent->value() > 0)) @@ -1421,28 +1379,25 @@ WP CHyprOpenGLImpl::renderToFBInternal(const STextureRenderData& data, if (shaderFeatures & SH_FEAT_BLUR) { shader->setUniformInt(SHADER_BLURRED_BG, 1); - // shader->setUniformFloat2(SHADER_UV_OFFSET, 0, 0); - shader->setUniformFloat2(SHADER_UV_OFFSET, newBox.x / g_pHyprRenderer->m_renderData.pMonitor->m_transformedSize.x, - newBox.y / g_pHyprRenderer->m_renderData.pMonitor->m_transformedSize.y); - shader->setUniformFloat2(SHADER_UV_SIZE, newBox.width / g_pHyprRenderer->m_renderData.pMonitor->m_transformedSize.x, - newBox.height / g_pHyprRenderer->m_renderData.pMonitor->m_transformedSize.y); + shader->setUniformFloat2(SHADER_UV_OFFSET, newBox.x / data.blurredBG->m_size.x, newBox.y / data.blurredBG->m_size.y); + shader->setUniformFloat2(SHADER_UV_SIZE, newBox.width / data.blurredBG->m_size.x, newBox.height / data.blurredBG->m_size.y); glActiveTexture(GL_TEXTURE0 + 1); data.blurredBG->bind(); } if (data.discardActive) { - shader->setUniformInt(SHADER_DISCARD_OPAQUE, !!(g_pHyprRenderer->m_renderData.discardMode & DISCARD_OPAQUE)); - shader->setUniformInt(SHADER_DISCARD_ALPHA, !!(g_pHyprRenderer->m_renderData.discardMode & DISCARD_ALPHA)); - shader->setUniformFloat(SHADER_DISCARD_ALPHA_VALUE, g_pHyprRenderer->m_renderData.discardOpacity); + shader->setUniformInt(SHADER_DISCARD_OPAQUE, !!(data.discardMode & DISCARD_OPAQUE)); + shader->setUniformInt(SHADER_DISCARD_ALPHA, !!(data.discardMode & DISCARD_ALPHA)); + shader->setUniformFloat(SHADER_DISCARD_ALPHA_VALUE, data.discardOpacity); } else { shader->setUniformInt(SHADER_DISCARD_OPAQUE, 0); shader->setUniformInt(SHADER_DISCARD_ALPHA, 0); } CBox transformedBox = newBox; - transformedBox.transform(Math::wlTransformToHyprutils(Math::invertTransform(g_pHyprRenderer->m_renderData.pMonitor->m_transform)), - g_pHyprRenderer->m_renderData.pMonitor->m_transformedSize.x, g_pHyprRenderer->m_renderData.pMonitor->m_transformedSize.y); + transformedBox.transform(Math::wlTransformToHyprutils(Math::invertTransform(m_renderData.pMonitor->m_transform)), m_renderData.pMonitor->m_transformedSize.x, + m_renderData.pMonitor->m_transformedSize.y); const auto TOPLEFT = Vector2D(transformedBox.x, transformedBox.y); const auto FULLSIZE = Vector2D(transformedBox.width, transformedBox.height); @@ -1520,11 +1475,11 @@ void CHyprOpenGLImpl::renderTextureInternal(SP tex, const CBox& box, c auto verts = fullVerts; - if (data.allowCustomUV && g_pHyprRenderer->m_renderData.primarySurfaceUVTopLeft != Vector2D(-1, -1)) { - const float u0 = g_pHyprRenderer->m_renderData.primarySurfaceUVTopLeft.x; - const float v0 = g_pHyprRenderer->m_renderData.primarySurfaceUVTopLeft.y; - const float u1 = g_pHyprRenderer->m_renderData.primarySurfaceUVBottomRight.x; - const float v1 = g_pHyprRenderer->m_renderData.primarySurfaceUVBottomRight.y; + if (data.allowCustomUV && data.primarySurfaceUVTopLeft != Vector2D(-1, -1)) { + const float u0 = data.primarySurfaceUVTopLeft.x; + const float v0 = data.primarySurfaceUVTopLeft.y; + const float u1 = data.primarySurfaceUVBottomRight.x; + const float v1 = data.primarySurfaceUVBottomRight.y; verts[0].u = u0; verts[0].v = v0; @@ -1538,14 +1493,14 @@ void CHyprOpenGLImpl::renderTextureInternal(SP tex, const CBox& box, c glBufferSubData(GL_ARRAY_BUFFER, 0, sizeof(verts), verts.data()); - if (!g_pHyprRenderer->m_renderData.clipBox.empty() || !g_pHyprRenderer->m_renderData.clipRegion.empty()) { + if (!g_pHyprRenderer->m_renderData.clipBox.empty() || !data.clipRegion.empty()) { CRegion damageClip = g_pHyprRenderer->m_renderData.clipBox; - if (!g_pHyprRenderer->m_renderData.clipRegion.empty()) { + if (!data.clipRegion.empty()) { if (g_pHyprRenderer->m_renderData.clipBox.empty()) - damageClip = g_pHyprRenderer->m_renderData.clipRegion; + damageClip = data.clipRegion; else - damageClip.intersect(g_pHyprRenderer->m_renderData.clipRegion); + damageClip.intersect(data.clipRegion); } if (!damageClip.empty()) { @@ -1615,9 +1570,6 @@ void CHyprOpenGLImpl::renderTextureMatte(SP tex, const CBox& box, SPm_renderData.damage.empty()) - return; - CBox newBox = box; g_pHyprRenderer->m_renderData.renderModif.applyToBox(newBox); @@ -1652,25 +1604,17 @@ void CHyprOpenGLImpl::renderTextureMatte(SP tex, const CBox& box, SP CHyprOpenGLImpl::blurMainFramebufferWithDamage(float a, CRegion* originalDamage) { - if (!g_pHyprRenderer->m_renderData.currentFB->getTexture()) { - Log::logger->log(Log::ERR, "BUG THIS: null fb texture while attempting to blur main fb?! (introspection off?!)"); - return g_pHyprRenderer->m_renderData.pMonitor->m_mirrorFB; // return something to sample from at least - } - - return blurFramebufferWithDamage(a, originalDamage, *GLFB(g_pHyprRenderer->m_renderData.currentFB)); -} - SP CHyprOpenGLImpl::blurFramebufferWithDamage(float a, CRegion* originalDamage, CGLFramebuffer& source) { TRACY_GPU_ZONE("RenderBlurFramebufferWithDamage"); + auto& m_renderData = g_pHyprRenderer->m_renderData; const auto BLENDBEFORE = m_blend; blend(false); setCapStatus(GL_STENCIL_TEST, false); // get transforms for the full monitor - const auto TRANSFORM = Math::wlTransformToHyprutils(Math::invertTransform(g_pHyprRenderer->m_renderData.pMonitor->m_transform)); - CBox MONITORBOX = {0, 0, g_pHyprRenderer->m_renderData.pMonitor->m_transformedSize.x, g_pHyprRenderer->m_renderData.pMonitor->m_transformedSize.y}; + const auto TRANSFORM = Math::wlTransformToHyprutils(Math::invertTransform(m_renderData.pMonitor->m_transform)); + CBox MONITORBOX = {0, 0, m_renderData.pMonitor->m_transformedSize.x, m_renderData.pMonitor->m_transformedSize.y}; const auto& glMatrix = g_pHyprRenderer->projectBoxToTarget(MONITORBOX, TRANSFORM); @@ -1684,8 +1628,8 @@ SP CHyprOpenGLImpl::blurFramebufferWithDamage(float a, CRegion* or // prep damage CRegion damage{*originalDamage}; - damage.transform(Math::wlTransformToHyprutils(Math::invertTransform(g_pHyprRenderer->m_renderData.pMonitor->m_transform)), - g_pHyprRenderer->m_renderData.pMonitor->m_transformedSize.x, g_pHyprRenderer->m_renderData.pMonitor->m_transformedSize.y); + damage.transform(Math::wlTransformToHyprutils(Math::invertTransform(m_renderData.pMonitor->m_transform)), m_renderData.pMonitor->m_transformedSize.x, + m_renderData.pMonitor->m_transformedSize.y); damage.expand(std::clamp(*PBLURSIZE, sc(1), sc(40)) * pow(2, BLUR_PASSES)); // helper @@ -1716,18 +1660,16 @@ SP CHyprOpenGLImpl::blurFramebufferWithDamage(float a, CRegion* or const bool skipCM = !m_cmSupported || g_pHyprRenderer->workBufferImageDescription()->id() == DEFAULT_IMAGE_DESCRIPTION->id(); if (!skipCM) { shader = useShader(getShaderVariant(SH_FRAG_BLURPREPARE, SH_FEAT_CM)); - passCMUniforms(shader, g_pHyprRenderer->m_renderData.pMonitor->m_imageDescription, DEFAULT_IMAGE_DESCRIPTION); + passCMUniforms(shader, m_renderData.pMonitor->m_imageDescription, DEFAULT_IMAGE_DESCRIPTION); shader->setUniformFloat(SHADER_SDR_SATURATION, - g_pHyprRenderer->m_renderData.pMonitor->m_sdrSaturation > 0 && - g_pHyprRenderer->m_renderData.pMonitor->m_imageDescription->value().transferFunction == - NColorManagement::CM_TRANSFER_FUNCTION_ST2084_PQ ? - g_pHyprRenderer->m_renderData.pMonitor->m_sdrSaturation : + m_renderData.pMonitor->m_sdrSaturation > 0 && + m_renderData.pMonitor->m_imageDescription->value().transferFunction == NColorManagement::CM_TRANSFER_FUNCTION_ST2084_PQ ? + m_renderData.pMonitor->m_sdrSaturation : 1.0f); shader->setUniformFloat(SHADER_SDR_BRIGHTNESS, - g_pHyprRenderer->m_renderData.pMonitor->m_sdrBrightness > 0 && - g_pHyprRenderer->m_renderData.pMonitor->m_imageDescription->value().transferFunction == - NColorManagement::CM_TRANSFER_FUNCTION_ST2084_PQ ? - g_pHyprRenderer->m_renderData.pMonitor->m_sdrBrightness : + m_renderData.pMonitor->m_sdrBrightness > 0 && + m_renderData.pMonitor->m_imageDescription->value().transferFunction == NColorManagement::CM_TRANSFER_FUNCTION_ST2084_PQ ? + m_renderData.pMonitor->m_sdrBrightness : 1.0f); } else shader = useShader(getShaderVariant(SH_FRAG_BLURPREPARE)); @@ -1770,14 +1712,12 @@ SP CHyprOpenGLImpl::blurFramebufferWithDamage(float a, CRegion* or shader->setUniformMatrix3fv(SHADER_PROJ, 1, GL_TRUE, glMatrix.getMatrix()); shader->setUniformFloat(SHADER_RADIUS, *PBLURSIZE * a); // this makes the blursize change with a if (frag == SH_FRAG_BLUR1) { - shader->setUniformFloat2(SHADER_HALFPIXEL, 0.5f / (g_pHyprRenderer->m_renderData.pMonitor->m_pixelSize.x / 2.f), - 0.5f / (g_pHyprRenderer->m_renderData.pMonitor->m_pixelSize.y / 2.f)); + shader->setUniformFloat2(SHADER_HALFPIXEL, 0.5f / (m_renderData.pMonitor->m_pixelSize.x / 2.f), 0.5f / (m_renderData.pMonitor->m_pixelSize.y / 2.f)); shader->setUniformInt(SHADER_PASSES, BLUR_PASSES); shader->setUniformFloat(SHADER_VIBRANCY, *PBLURVIBRANCY); shader->setUniformFloat(SHADER_VIBRANCY_DARKNESS, *PBLURVIBRANCYDARKNESS); } else - shader->setUniformFloat2(SHADER_HALFPIXEL, 0.5f / (g_pHyprRenderer->m_renderData.pMonitor->m_pixelSize.x * 2.f), - 0.5f / (g_pHyprRenderer->m_renderData.pMonitor->m_pixelSize.y * 2.f)); + shader->setUniformFloat2(SHADER_HALFPIXEL, 0.5f / (m_renderData.pMonitor->m_pixelSize.x * 2.f), 0.5f / (m_renderData.pMonitor->m_pixelSize.y * 2.f)); shader->setUniformInt(SHADER_TEX, 0); glBindVertexArray(shader->getUniformLocation(SHADER_SHADER_VAO)); @@ -1951,114 +1891,14 @@ void CHyprOpenGLImpl::preRender(PHLMONITOR pMonitor) { pMonitor->m_blurFBShouldRender = true; } -void CHyprOpenGLImpl::preBlurForCurrentMonitor() { - - TRACY_GPU_ZONE("RenderPreBlurForCurrentMonitor"); - - const auto SAVEDRENDERMODIF = g_pHyprRenderer->m_renderData.renderModif; - g_pHyprRenderer->m_renderData.renderModif = {}; // fix shit - - // make the fake dmg - CRegion fakeDamage{0, 0, g_pHyprRenderer->m_renderData.pMonitor->m_transformedSize.x, g_pHyprRenderer->m_renderData.pMonitor->m_transformedSize.y}; - const auto POUTFB = blurMainFramebufferWithDamage(1, &fakeDamage); - - // render onto blurFB - if (!g_pHyprRenderer->m_renderData.pMonitor->m_blurFB) - g_pHyprRenderer->m_renderData.pMonitor->m_blurFB = g_pHyprRenderer->createFB(); - - g_pHyprRenderer->m_renderData.pMonitor->m_blurFB->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); - g_pHyprRenderer->m_renderData.pMonitor->m_blurFB->bind(); - - clear(CHyprColor(0, 0, 0, 0)); - - g_pHyprRenderer->pushMonitorTransformEnabled(true); - renderTextureInternal(POUTFB->getTexture(), - CBox{0, 0, g_pHyprRenderer->m_renderData.pMonitor->m_transformedSize.x, g_pHyprRenderer->m_renderData.pMonitor->m_transformedSize.y}, - STextureRenderData{.damage = &fakeDamage, .a = 1, .round = 0, .roundingPower = 2.F, .discardActive = false, .allowCustomUV = false, .noAA = true}); - g_pHyprRenderer->popMonitorTransformEnabled(); - - g_pHyprRenderer->m_renderData.currentFB->bind(); - - g_pHyprRenderer->m_renderData.pMonitor->m_blurFBDirty = false; - - g_pHyprRenderer->m_renderData.renderModif = SAVEDRENDERMODIF; - - g_pHyprRenderer->m_renderData.pMonitor->m_blurFBShouldRender = false; -} - -void CHyprOpenGLImpl::preWindowPass() { - if (!preBlurQueued()) - return; - - g_pHyprRenderer->m_renderPass.add(makeUnique()); -} - -bool CHyprOpenGLImpl::preBlurQueued() { - static auto PBLURNEWOPTIMIZE = CConfigValue("decoration:blur:new_optimizations"); - static auto PBLUR = CConfigValue("decoration:blur:enabled"); - - return g_pHyprRenderer->m_renderData.pMonitor->m_blurFBDirty && *PBLURNEWOPTIMIZE && *PBLUR && g_pHyprRenderer->m_renderData.pMonitor->m_blurFBShouldRender; -} - void CHyprOpenGLImpl::renderTextureWithBlurInternal(SP tex, const CBox& box, const STextureRenderData& data) { - RASSERT(g_pHyprRenderer->m_renderData.pMonitor, "Tried to render texture with blur without begin()!"); + auto& m_renderData = g_pHyprRenderer->m_renderData; + RASSERT(m_renderData.pMonitor, "Tried to render texture with blur without begin()!"); TRACY_GPU_ZONE("RenderTextureWithBlur"); - static auto PBLEND = CConfigValue("render:use_shader_blur_blend"); - - // make a damage region for this window - CRegion texDamage{g_pHyprRenderer->m_renderData.damage}; - texDamage.intersect(box.x, box.y, box.width, box.height); - - // While renderTextureInternalWithDamage will clip the blur as well, - // clipping texDamage here allows blur generation to be optimized. - if (!g_pHyprRenderer->m_renderData.clipRegion.empty()) - texDamage.intersect(g_pHyprRenderer->m_renderData.clipRegion); - - if (texDamage.empty()) - return; - - g_pHyprRenderer->m_renderData.renderModif.applyToRegion(texDamage); - - // amazing hack: the surface has an opaque region! - CRegion inverseOpaque; - if (data.a >= 1.f && data.surface && std::round(data.surface->m_current.size.x * g_pHyprRenderer->m_renderData.pMonitor->m_scale) == box.w && - std::round(data.surface->m_current.size.y * g_pHyprRenderer->m_renderData.pMonitor->m_scale) == box.h) { - pixman_box32_t surfbox = {0, 0, data.surface->m_current.size.x * data.surface->m_current.scale, data.surface->m_current.size.y * data.surface->m_current.scale}; - inverseOpaque = data.surface->m_current.opaque; - inverseOpaque.invert(&surfbox).intersect(0, 0, data.surface->m_current.size.x * data.surface->m_current.scale, - data.surface->m_current.size.y * data.surface->m_current.scale); - - if (inverseOpaque.empty()) { - renderTextureInternal(tex, box, data); - return; - } - } else - inverseOpaque = {0, 0, box.width, box.height}; - - inverseOpaque.scale(g_pHyprRenderer->m_renderData.pMonitor->m_scale); - - // vvv TODO: layered blur fbs? - const bool USENEWOPTIMIZE = - g_pHyprRenderer->shouldUseNewBlurOptimizations(g_pHyprRenderer->m_renderData.currentLS.lock(), g_pHyprRenderer->m_renderData.currentWindow.lock()) && - !data.blockBlurOptimization; - - SP POUTFB = nullptr; - if (!USENEWOPTIMIZE) { - inverseOpaque.translate(box.pos()); - g_pHyprRenderer->m_renderData.renderModif.applyToRegion(inverseOpaque); - inverseOpaque.intersect(texDamage); - POUTFB = blurMainFramebufferWithDamage(data.a, &inverseOpaque); - } else - POUTFB = g_pHyprRenderer->m_renderData.pMonitor->m_blurFB; - - g_pHyprRenderer->m_renderData.currentFB->bind(); - - auto blurredBG = POUTFB->getTexture(); - - const auto NEEDS_STENCIL = g_pHyprRenderer->m_renderData.discardMode != 0 && (!data.blockBlurOptimization || (g_pHyprRenderer->m_renderData.discardMode & DISCARD_ALPHA)); + static auto PBLEND = CConfigValue("render:use_shader_blur_blend"); + const auto NEEDS_STENCIL = data.discardMode != 0 && (!data.blockBlurOptimization || (data.discardMode & DISCARD_ALPHA)); if (!*PBLEND) { if (NEEDS_STENCIL) { @@ -2072,14 +1912,24 @@ void CHyprOpenGLImpl::renderTextureWithBlurInternal(SP tex, const CBox glStencilOp(GL_KEEP, GL_KEEP, GL_REPLACE); glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE); + renderTexture(tex, box, - STextureRenderData{.a = data.a, - .round = data.round, - .roundingPower = data.roundingPower, - .discardActive = true, - .allowCustomUV = true, - .wrapX = data.wrapX, - .wrapY = data.wrapY}); // discard opaque and alpha < discardOpacity + STextureRenderData{ + .damage = &g_pHyprRenderer->m_renderData.damage, + .a = data.a, + .round = data.round, + .roundingPower = data.roundingPower, + .discardActive = true, + .allowCustomUV = true, + .wrapX = data.wrapX, + .wrapY = data.wrapY, + .discardMode = data.discardMode, + .discardOpacity = data.discardOpacity, + .clipRegion = data.clipRegion, + .currentLS = data.currentLS, + .primarySurfaceUVTopLeft = g_pHyprRenderer->m_renderData.primarySurfaceUVTopLeft, + .primarySurfaceUVBottomRight = g_pHyprRenderer->m_renderData.primarySurfaceUVBottomRight, + }); // discard opaque and alpha < discardOpacity glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE); @@ -2088,64 +1938,71 @@ void CHyprOpenGLImpl::renderTextureWithBlurInternal(SP tex, const CBox } // stencil done. Render everything. - const auto LASTTL = g_pHyprRenderer->m_renderData.primarySurfaceUVTopLeft; - const auto LASTBR = g_pHyprRenderer->m_renderData.primarySurfaceUVBottomRight; + CBox transformedBox = box; + transformedBox.transform(Math::wlTransformToHyprutils(Math::invertTransform(m_renderData.pMonitor->m_transform)), m_renderData.pMonitor->m_transformedSize.x, + m_renderData.pMonitor->m_transformedSize.y); - CBox transformedBox = box; - transformedBox.transform(Math::wlTransformToHyprutils(Math::invertTransform(g_pHyprRenderer->m_renderData.pMonitor->m_transform)), - g_pHyprRenderer->m_renderData.pMonitor->m_transformedSize.x, g_pHyprRenderer->m_renderData.pMonitor->m_transformedSize.y); - - CBox monitorSpaceBox = {transformedBox.pos().x / g_pHyprRenderer->m_renderData.pMonitor->m_pixelSize.x * g_pHyprRenderer->m_renderData.pMonitor->m_transformedSize.x, - transformedBox.pos().y / g_pHyprRenderer->m_renderData.pMonitor->m_pixelSize.y * g_pHyprRenderer->m_renderData.pMonitor->m_transformedSize.y, - transformedBox.width / g_pHyprRenderer->m_renderData.pMonitor->m_pixelSize.x * g_pHyprRenderer->m_renderData.pMonitor->m_transformedSize.x, - transformedBox.height / g_pHyprRenderer->m_renderData.pMonitor->m_pixelSize.y * g_pHyprRenderer->m_renderData.pMonitor->m_transformedSize.y}; - - g_pHyprRenderer->m_renderData.primarySurfaceUVTopLeft = monitorSpaceBox.pos() / g_pHyprRenderer->m_renderData.pMonitor->m_transformedSize; - g_pHyprRenderer->m_renderData.primarySurfaceUVBottomRight = (monitorSpaceBox.pos() + monitorSpaceBox.size()) / g_pHyprRenderer->m_renderData.pMonitor->m_transformedSize; + CBox monitorSpaceBox = {transformedBox.pos().x / m_renderData.pMonitor->m_pixelSize.x * m_renderData.pMonitor->m_transformedSize.x, + transformedBox.pos().y / m_renderData.pMonitor->m_pixelSize.y * m_renderData.pMonitor->m_transformedSize.y, + transformedBox.width / m_renderData.pMonitor->m_pixelSize.x * m_renderData.pMonitor->m_transformedSize.x, + transformedBox.height / m_renderData.pMonitor->m_pixelSize.y * m_renderData.pMonitor->m_transformedSize.y}; static auto PBLURIGNOREOPACITY = CConfigValue("decoration:blur:ignore_opacity"); + g_pHyprRenderer->pushMonitorTransformEnabled(true); bool renderModif = g_pHyprRenderer->m_renderData.renderModif.enabled; - if (!USENEWOPTIMIZE) - setRenderModifEnabled(false); - renderTextureInternal(blurredBG, box, + if (!data.blockBlurOptimization) + g_pHyprRenderer->m_renderData.renderModif.enabled = false; + + renderTextureInternal(data.blurredBG, box, STextureRenderData{ - .damage = &texDamage, - .a = (*PBLURIGNOREOPACITY ? data.blurA : data.a * data.blurA) * data.overallA, - .round = data.round, - .roundingPower = data.roundingPower, - .discardActive = false, - .allowCustomUV = true, - .noAA = false, - .wrapX = data.wrapX, - .wrapY = data.wrapY, + .damage = data.damage, + .a = (*PBLURIGNOREOPACITY ? data.blurA : data.a * data.blurA) * data.overallA, + .round = data.round, + .roundingPower = data.roundingPower, + .discardActive = false, + .allowCustomUV = true, + .noAA = false, + .wrapX = data.wrapX, + .wrapY = data.wrapY, + .discardMode = data.discardMode, + .discardOpacity = data.discardOpacity, + .clipRegion = data.clipRegion, + .currentLS = data.currentLS, + + .primarySurfaceUVTopLeft = monitorSpaceBox.pos() / m_renderData.pMonitor->m_transformedSize, + .primarySurfaceUVBottomRight = (monitorSpaceBox.pos() + monitorSpaceBox.size()) / m_renderData.pMonitor->m_transformedSize, }); - if (!USENEWOPTIMIZE) - setRenderModifEnabled(renderModif); + + g_pHyprRenderer->m_renderData.renderModif.enabled = renderModif; g_pHyprRenderer->popMonitorTransformEnabled(); if (NEEDS_STENCIL) setCapStatus(GL_STENCIL_TEST, false); - - g_pHyprRenderer->m_renderData.primarySurfaceUVTopLeft = LASTTL; - g_pHyprRenderer->m_renderData.primarySurfaceUVBottomRight = LASTBR; } // draw window renderTextureInternal(tex, box, STextureRenderData{ - .damage = &texDamage, - .a = data.a * data.overallA, - .blur = *PBLEND, - .round = data.round, - .roundingPower = data.roundingPower, - .discardActive = *PBLEND && NEEDS_STENCIL, - .allowCustomUV = true, - .allowDim = true, - .noAA = false, - .wrapX = data.wrapX, - .wrapY = data.wrapY, - .blurredBG = blurredBG, + .blur = *PBLEND, + .blurredBG = data.blurredBG, + .damage = data.damage, + .a = data.a * data.overallA, + .round = data.round, + .roundingPower = data.roundingPower, + .discardActive = *PBLEND && NEEDS_STENCIL, + .allowCustomUV = true, + .allowDim = true, + .noAA = false, + .wrapX = data.wrapX, + .wrapY = data.wrapY, + .discardMode = data.discardMode, + .discardOpacity = data.discardOpacity, + .clipRegion = data.clipRegion, + .currentLS = data.currentLS, + + .primarySurfaceUVTopLeft = g_pHyprRenderer->m_renderData.primarySurfaceUVTopLeft, + .primarySurfaceUVBottomRight = g_pHyprRenderer->m_renderData.primarySurfaceUVBottomRight, }); GLFB(g_pHyprRenderer->m_renderData.currentFB)->invalidate({GL_STENCIL_ATTACHMENT}); @@ -2153,8 +2010,9 @@ void CHyprOpenGLImpl::renderTextureWithBlurInternal(SP tex, const CBox } void CHyprOpenGLImpl::renderBorder(const CBox& box, const CGradientValueData& grad, SBorderRenderData data) { + auto& m_renderData = g_pHyprRenderer->m_renderData; RASSERT((box.width > 0 && box.height > 0), "Tried to render rect with width/height < 0!"); - RASSERT(g_pHyprRenderer->m_renderData.pMonitor, "Tried to render rect without begin()!"); + RASSERT(m_renderData.pMonitor, "Tried to render rect without begin()!"); TRACY_GPU_ZONE("RenderBorder"); @@ -2167,7 +2025,7 @@ void CHyprOpenGLImpl::renderBorder(const CBox& box, const CGradientValueData& gr if (data.borderSize < 1) return; - int scaledBorderSize = std::round(data.borderSize * g_pHyprRenderer->m_renderData.pMonitor->m_scale); + int scaledBorderSize = std::round(data.borderSize * m_renderData.pMonitor->m_scale); scaledBorderSize = std::round(scaledBorderSize * g_pHyprRenderer->m_renderData.renderModif.combinedScale()); // adjust box @@ -2201,8 +2059,8 @@ void CHyprOpenGLImpl::renderBorder(const CBox& box, const CGradientValueData& gr shader->setUniformInt(SHADER_GRADIENT2_LENGTH, 0); CBox transformedBox = newBox; - transformedBox.transform(Math::wlTransformToHyprutils(Math::invertTransform(g_pHyprRenderer->m_renderData.pMonitor->m_transform)), - g_pHyprRenderer->m_renderData.pMonitor->m_transformedSize.x, g_pHyprRenderer->m_renderData.pMonitor->m_transformedSize.y); + transformedBox.transform(Math::wlTransformToHyprutils(Math::invertTransform(m_renderData.pMonitor->m_transform)), m_renderData.pMonitor->m_transformedSize.x, + m_renderData.pMonitor->m_transformedSize.y); const auto TOPLEFT = Vector2D(transformedBox.x, transformedBox.y); const auto FULLSIZE = Vector2D(transformedBox.width, transformedBox.height); @@ -2238,8 +2096,9 @@ void CHyprOpenGLImpl::renderBorder(const CBox& box, const CGradientValueData& gr } void CHyprOpenGLImpl::renderBorder(const CBox& box, const CGradientValueData& grad1, const CGradientValueData& grad2, float lerp, SBorderRenderData data) { + auto& m_renderData = g_pHyprRenderer->m_renderData; RASSERT((box.width > 0 && box.height > 0), "Tried to render rect with width/height < 0!"); - RASSERT(g_pHyprRenderer->m_renderData.pMonitor, "Tried to render rect without begin()!"); + RASSERT(m_renderData.pMonitor, "Tried to render rect without begin()!"); TRACY_GPU_ZONE("RenderBorder2"); @@ -2252,7 +2111,7 @@ void CHyprOpenGLImpl::renderBorder(const CBox& box, const CGradientValueData& gr if (data.borderSize < 1) return; - int scaledBorderSize = std::round(data.borderSize * g_pHyprRenderer->m_renderData.pMonitor->m_scale); + int scaledBorderSize = std::round(data.borderSize * m_renderData.pMonitor->m_scale); scaledBorderSize = std::round(scaledBorderSize * g_pHyprRenderer->m_renderData.renderModif.combinedScale()); // adjust box @@ -2289,8 +2148,8 @@ void CHyprOpenGLImpl::renderBorder(const CBox& box, const CGradientValueData& gr shader->setUniformFloat(SHADER_GRADIENT_LERP, lerp); CBox transformedBox = newBox; - transformedBox.transform(Math::wlTransformToHyprutils(Math::invertTransform(g_pHyprRenderer->m_renderData.pMonitor->m_transform)), - g_pHyprRenderer->m_renderData.pMonitor->m_transformedSize.x, g_pHyprRenderer->m_renderData.pMonitor->m_transformedSize.y); + transformedBox.transform(Math::wlTransformToHyprutils(Math::invertTransform(m_renderData.pMonitor->m_transform)), m_renderData.pMonitor->m_transformedSize.x, + m_renderData.pMonitor->m_transformedSize.y); const auto TOPLEFT = Vector2D(transformedBox.x, transformedBox.y); const auto FULLSIZE = Vector2D(transformedBox.width, transformedBox.height); @@ -2325,7 +2184,8 @@ void CHyprOpenGLImpl::renderBorder(const CBox& box, const CGradientValueData& gr } void CHyprOpenGLImpl::renderRoundedShadow(const CBox& box, int round, float roundingPower, int range, const CHyprColor& color, float a) { - RASSERT(g_pHyprRenderer->m_renderData.pMonitor, "Tried to render shadow without begin()!"); + auto& m_renderData = g_pHyprRenderer->m_renderData; + 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!"); if (g_pHyprRenderer->m_renderData.damage.empty()) @@ -2392,13 +2252,10 @@ void CHyprOpenGLImpl::renderRoundedShadow(const CBox& box, int round, float roun } void CHyprOpenGLImpl::saveBufferForMirror(const CBox& box) { - if (!g_pHyprRenderer->m_renderData.pMonitor->m_monitorMirrorFB) - g_pHyprRenderer->m_renderData.pMonitor->m_monitorMirrorFB = g_pHyprRenderer->createFB(); - + auto& m_renderData = g_pHyprRenderer->m_renderData; if (!g_pHyprRenderer->m_renderData.pMonitor->m_monitorMirrorFB->isAllocated()) - 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); + g_pHyprRenderer->m_renderData.pMonitor->m_monitorMirrorFB->alloc(m_renderData.pMonitor->m_pixelSize.x, m_renderData.pMonitor->m_pixelSize.y, + m_renderData.pMonitor->m_output->state->state().drmFormat); g_pHyprRenderer->m_renderData.pMonitor->m_monitorMirrorFB->bind(); @@ -2419,94 +2276,6 @@ void CHyprOpenGLImpl::saveBufferForMirror(const CBox& box) { g_pHyprRenderer->m_renderData.currentFB->bind(); } -void CHyprOpenGLImpl::renderMirrored() { - - auto monitor = g_pHyprRenderer->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; - - auto PFB = mirrored->m_monitorMirrorFB; - if (!PFB->isAllocated() || !PFB->getTexture()) - return; - - g_pHyprRenderer->m_renderPass.add(makeUnique(CClearPassElement::SClearData{CHyprColor(0, 0, 0, 0)})); - - CTexPassElement::SRenderData data; - data.tex = PFB->getTexture(); - data.box = monbox; - data.useMirrorProjection = true; - - g_pHyprRenderer->m_renderPass.add(makeUnique(std::move(data))); -} - -void CHyprOpenGLImpl::renderSplash(cairo_t* const CAIRO, cairo_surface_t* const CAIROSURFACE, double offsetY, const Vector2D& size) { - static auto PSPLASHCOLOR = CConfigValue("misc:col.splash"); - static auto PSPLASHFONT = CConfigValue("misc:splash_font_family"); - static auto FALLBACKFONT = CConfigValue("misc:font_family"); - - const auto FONTFAMILY = *PSPLASHFONT != STRVAL_EMPTY ? *PSPLASHFONT : *FALLBACKFONT; - const auto FONTSIZE = sc(size.y / 76); - const auto COLOR = CHyprColor(*PSPLASHCOLOR); - - PangoLayout* layoutText = pango_cairo_create_layout(CAIRO); - PangoFontDescription* pangoFD = pango_font_description_new(); - - 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); - - 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 /= PANGO_SCALE; - textH /= PANGO_SCALE; - - cairo_move_to(CAIRO, (size.x - textW) / 2.0, size.y - textH - offsetY); - pango_cairo_show_layout(CAIRO, layoutText); - - pango_font_description_free(pangoFD); - g_object_unref(layoutText); - - cairo_surface_flush(CAIROSURFACE); -} - -SP CHyprOpenGLImpl::texFromCairo(cairo_surface_t* cairo) { - const auto CAIROFORMAT = cairo_image_surface_get_format(cairo); - auto tex = makeShared(); - - tex->allocate({cairo_image_surface_get_width(cairo), cairo_image_surface_get_height(cairo)}); - - 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; - - 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); - - if (CAIROFORMAT != CAIRO_FORMAT_RGB96F) { - tex->setTexParameter(GL_TEXTURE_SWIZZLE_R, GL_BLUE); - tex->setTexParameter(GL_TEXTURE_SWIZZLE_B, GL_RED); - } - - glTexImage2D(GL_TEXTURE_2D, 0, glIFormat, tex->m_size.x, tex->m_size.y, 0, glFormat, glType, DATA); - - return tex; -} - WP CHyprOpenGLImpl::useShader(WP prog) { if (m_currentProgram == prog->program()) return prog; @@ -2517,140 +2286,6 @@ WP CHyprOpenGLImpl::useShader(WP prog) { return prog; } -void CHyprOpenGLImpl::createBGTextureForMonitor(PHLMONITOR pMonitor) { - RASSERT(g_pHyprRenderer->m_renderData.pMonitor, "Tried to createBGTex without begin()!"); - - Log::logger->log(Log::DEBUG, "Creating a texture for BGTex"); - - static auto PRENDERTEX = CConfigValue("misc:disable_hyprland_logo"); - static auto PNOSPLASH = CConfigValue("misc:disable_splash_rendering"); - - if (*PRENDERTEX || g_pHyprRenderer->m_backgroundResourceFailed) - return; - - if (!g_pHyprRenderer->m_backgroundResource) { - // queue the asset to be created - g_pHyprRenderer->requestBackgroundResource(); - return; - } - - if (!g_pHyprRenderer->m_backgroundResource->m_ready) - return; - - if (!m_monitorBGFBs.contains(pMonitor)) - m_monitorBGFBs[pMonitor] = g_pHyprRenderer->createFB(); - - // release the last tex if exists - auto PFB = m_monitorBGFBs[pMonitor]; - PFB->release(); - - PFB->alloc(pMonitor->m_pixelSize.x, pMonitor->m_pixelSize.y, pMonitor->m_output->state->state().drmFormat); - - // create a new one with cairo - SP tex = makeShared(); - - tex->allocate(pMonitor->m_pixelSize); - - const auto CAIROSURFACE = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, pMonitor->m_pixelSize.x, pMonitor->m_pixelSize.y); - const auto CAIRO = cairo_create(CAIROSURFACE); - - 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); - - if (!*PNOSPLASH) - renderSplash(CAIRO, CAIROSURFACE, 0.02 * pMonitor->m_pixelSize.y, pMonitor->m_pixelSize); - - cairo_surface_flush(CAIROSURFACE); - - tex->m_size = pMonitor->m_pixelSize; - - // copy the data to an OpenGL texture we have - const GLint glFormat = GL_RGBA; - const GLint glType = GL_UNSIGNED_BYTE; - - const auto DATA = cairo_image_surface_get_data(CAIROSURFACE); - 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); - - glTexImage2D(GL_TEXTURE_2D, 0, glFormat, tex->m_size.x, tex->m_size.y, 0, glFormat, glType, DATA); - - cairo_surface_destroy(CAIROSURFACE); - cairo_destroy(CAIRO); - - // render the texture to our fb - PFB->bind(); - CRegion fakeDamage{0, 0, INT16_MAX, INT16_MAX}; - - blend(true); - clear(CHyprColor{0, 0, 0, 1}); - - SP backgroundTexture = texFromCairo(g_pHyprRenderer->m_backgroundResource->m_asset.cairoSurface->cairo()); - - // first render the background - if (backgroundTexture) { - const double MONRATIO = g_pHyprRenderer->m_renderData.pMonitor->m_transformedSize.x / g_pHyprRenderer->m_renderData.pMonitor->m_transformedSize.y; - const double WPRATIO = backgroundTexture->m_size.x / backgroundTexture->m_size.y; - Vector2D origin; - double scale = 1.0; - - if (MONRATIO > WPRATIO) { - scale = g_pHyprRenderer->m_renderData.pMonitor->m_transformedSize.x / backgroundTexture->m_size.x; - origin.y = (g_pHyprRenderer->m_renderData.pMonitor->m_transformedSize.y - backgroundTexture->m_size.y * scale) / 2.0; - } else { - scale = g_pHyprRenderer->m_renderData.pMonitor->m_transformedSize.y / backgroundTexture->m_size.y; - origin.x = (g_pHyprRenderer->m_renderData.pMonitor->m_transformedSize.x - backgroundTexture->m_size.x * scale) / 2.0; - } - - CBox texbox = CBox{origin, backgroundTexture->m_size * scale}; - renderTextureInternal(backgroundTexture, texbox, {.damage = &fakeDamage, .a = 1.0}); - } - - CBox monbox = {{}, pMonitor->m_pixelSize}; - renderTextureInternal(tex, monbox, {.damage = &fakeDamage, .a = 1.0}); - - // bind back - if (g_pHyprRenderer->m_renderData.currentFB) - g_pHyprRenderer->m_renderData.currentFB->bind(); - - Log::logger->log(Log::DEBUG, "Background created for monitor {}", pMonitor->m_name); - - // clear the resource after we're done using it - g_pEventLoopManager->doLater([] { g_pHyprRenderer->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; -} - -void CHyprOpenGLImpl::clearWithTex() { - RASSERT(g_pHyprRenderer->m_renderData.pMonitor, "Tried to render BGtex without begin()!"); - - static auto PBACKGROUNDCOLOR = CConfigValue("misc:background_color"); - - auto TEXIT = m_monitorBGFBs.find(g_pHyprRenderer->m_renderData.pMonitor); - - if (TEXIT == m_monitorBGFBs.end()) { - createBGTextureForMonitor(g_pHyprRenderer->m_renderData.pMonitor.lock()); - g_pHyprRenderer->m_renderPass.add(makeUnique(CClearPassElement::SClearData{CHyprColor(*PBACKGROUNDCOLOR)})); - } - - if (TEXIT != m_monitorBGFBs.end()) { - CTexPassElement::SRenderData data; - data.box = {0, 0, g_pHyprRenderer->m_renderData.pMonitor->m_transformedSize.x, g_pHyprRenderer->m_renderData.pMonitor->m_transformedSize.y}; - data.a = g_pHyprRenderer->m_renderData.pMonitor->m_backgroundOpacity->value(); - data.flipEndFrame = true; - data.tex = TEXIT->second->getTexture(); - g_pHyprRenderer->m_renderPass.add(makeUnique(std::move(data))); - } -} - void CHyprOpenGLImpl::destroyMonitorResources(PHLMONITORREF pMonitor) { makeEGLCurrent(); @@ -2667,35 +2302,11 @@ void CHyprOpenGLImpl::destroyMonitorResources(PHLMONITORREF pMonitor) { Log::logger->log(Log::DEBUG, "Monitor {} -> destroyed all render data", pMonitor->m_name); } -void CHyprOpenGLImpl::bindOffMain() { - if (!g_pHyprRenderer->m_renderData.pMonitor->m_offMainFB) - g_pHyprRenderer->m_renderData.pMonitor->m_offMainFB = g_pHyprRenderer->createFB(); - - if (!g_pHyprRenderer->m_renderData.pMonitor->m_offMainFB->isAllocated()) { - g_pHyprRenderer->m_renderData.pMonitor->m_offMainFB->addStencil(g_pHyprRenderer->m_renderData.pMonitor->m_stencilTex); - g_pHyprRenderer->m_renderData.pMonitor->m_offMainFB->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); - } - - g_pHyprRenderer->m_renderData.pMonitor->m_offMainFB->bind(); - clear(CHyprColor(0, 0, 0, 0)); - g_pHyprRenderer->m_renderData.currentFB = g_pHyprRenderer->m_renderData.pMonitor->m_offMainFB; -} - -void CHyprOpenGLImpl::renderOffToMain(CGLFramebuffer* off) { +void CHyprOpenGLImpl::renderOffToMain(IFramebuffer* off) { CBox monbox = {0, 0, g_pHyprRenderer->m_renderData.pMonitor->m_transformedSize.x, g_pHyprRenderer->m_renderData.pMonitor->m_transformedSize.y}; renderTexturePrimitive(off->getTexture(), monbox); } -void CHyprOpenGLImpl::bindBackOnMain() { - g_pHyprRenderer->m_renderData.mainFB->bind(); - g_pHyprRenderer->m_renderData.currentFB = g_pHyprRenderer->m_renderData.mainFB; -} - -void CHyprOpenGLImpl::setRenderModifEnabled(bool enabled) { - g_pHyprRenderer->m_renderData.renderModif.enabled = enabled; -} - void CHyprOpenGLImpl::setViewport(GLint x, GLint y, GLsizei width, GLsizei height) { if (m_lastViewport.x == x && m_lastViewport.y == y && m_lastViewport.width == width && m_lastViewport.height == height) return; diff --git a/src/render/OpenGL.hpp b/src/render/OpenGL.hpp index b11c1efbe..51c89e1ed 100644 --- a/src/render/OpenGL.hpp +++ b/src/render/OpenGL.hpp @@ -35,7 +35,7 @@ #include "render/ShaderLoader.hpp" #include "render/gl/GLFramebuffer.hpp" #include "render/gl/GLRenderbuffer.hpp" -#include "render/gl/GLTexture.hpp" +#include "render/pass/TexPassElement.hpp" #define GLFB(ifb) dc(ifb.get()) @@ -56,11 +56,6 @@ constexpr std::array fullVerts = {{ inline const float fanVertsFull[] = {-1.0f, -1.0f, 1.0f, -1.0f, 1.0f, 1.0f, -1.0f, 1.0f}; -enum eDiscardMode : uint8_t { - DISCARD_OPAQUE = 1, - DISCARD_ALPHA = 1 << 1 -}; - struct SRenderModifData { enum eRenderModifType : uint8_t { RMOD_TYPE_SCALE, /* scale by a float */ @@ -186,24 +181,33 @@ class CHyprOpenGLImpl { }; struct STextureRenderData { - const CRegion* damage = nullptr; - SP surface = nullptr; - float a = 1.F; - bool blur = false; + bool blur = false; float blurA = 1.F, overallA = 1.F; - int round = 0; - float roundingPower = 2.F; - bool discardActive = false; - bool allowCustomUV = false; - bool allowDim = true; - bool noAA = false; bool blockBlurOptimization = false; + SP blurredBG; + + const CRegion* damage = nullptr; + SP surface = nullptr; + float a = 1.F; + int round = 0; + float roundingPower = 2.F; + bool discardActive = false; + bool allowCustomUV = false; + bool allowDim = true; + bool noAA = false; // unused GLenum wrapX = GL_CLAMP_TO_EDGE, wrapY = GL_CLAMP_TO_EDGE; bool cmBackToSRGB = false; - bool noCM = false; bool finalMonitorCM = false; SP cmBackToSRGBSource; - SP blurredBG; + + uint32_t discardMode = DISCARD_OPAQUE; + float discardOpacity = 0.f; + + CRegion clipRegion; + PHLLSREF currentLS; + + Vector2D primarySurfaceUVTopLeft = Vector2D(-1, -1); + Vector2D primarySurfaceUVBottomRight = Vector2D(-1, -1); }; struct SBorderRenderData { @@ -227,37 +231,25 @@ class CHyprOpenGLImpl { void renderTextureMatte(SP tex, const CBox& pBox, SP matte); void renderTexturePrimitive(SP tex, const CBox& box); - void setRenderModifEnabled(bool enabled); void setViewport(GLint x, GLint y, GLsizei width, GLsizei height); void setCapStatus(int cap, bool status); void blend(bool enabled); void clear(const CHyprColor&); - void clearWithTex(); void scissor(const CBox&, bool transform = true); void scissor(const pixman_box32*, bool transform = true); void scissor(const int x, const int y, const int w, const int h, bool transform = true); void destroyMonitorResources(PHLMONITORREF); - void preWindowPass(); - bool preBlurQueued(); void preRender(PHLMONITOR); void saveBufferForMirror(const CBox&); - void renderMirrored(); void applyScreenShader(const std::string& path); - void bindOffMain(); - void renderOffToMain(CGLFramebuffer* off); - void bindBackOnMain(); - - SP texFromCairo(cairo_surface_t* cairo); - - bool needsACopyFB(PHLMONITOR mon); - void setDamage(const CRegion& damage, std::optional finalDamage = {}); + void renderOffToMain(IFramebuffer* off); std::vector getDRMFormats(); std::vector getDRMFormatModifiers(DRMFormat format); @@ -350,7 +342,6 @@ class CHyprOpenGLImpl { SP m_finalScreenShader; GLuint m_currentProgram; - void createBGTextureForMonitor(PHLMONITOR); void initDRMFormats(); void initEGL(bool gbm); EGLDeviceEXT eglDeviceFromDRMFD(int drmFD); @@ -365,13 +356,11 @@ class CHyprOpenGLImpl { std::optional> getModsForFormat(EGLint format); // returns the out FB, can be either Mirror or MirrorSwap - SP blurMainFramebufferWithDamage(float a, CRegion* damage); SP blurFramebufferWithDamage(float a, CRegion* damage, CGLFramebuffer& source); void passCMUniforms(WP, const NColorManagement::PImageDescription imageDescription, const NColorManagement::PImageDescription targetImageDescription, bool modifySDR = false, float sdrMinLuminance = -1.0f, int sdrMaxLuminance = -1); void passCMUniforms(WP, const NColorManagement::PImageDescription imageDescription); - void renderSplash(cairo_t* const, cairo_surface_t* const, double offset, const Vector2D& size); void renderRectInternal(const CBox&, const CHyprColor&, const SRectRenderData& data); void renderRectWithBlurInternal(const CBox&, const CHyprColor&, const SRectRenderData& data); void renderRectWithDamageInternal(const CBox&, const CHyprColor&, const SRectRenderData& data); @@ -380,9 +369,8 @@ class CHyprOpenGLImpl { void renderTextureInternal(SP, const CBox&, const STextureRenderData& data); void renderTextureWithBlurInternal(SP, const CBox&, const STextureRenderData& data); - void preBlurForCurrentMonitor(); - - friend class CHyprRenderer; + friend class IHyprRenderer; + friend class CHyprGLRenderer; friend class CTexPassElement; friend class CPreBlurElement; friend class CSurfacePassElement; diff --git a/src/render/Renderer.cpp b/src/render/Renderer.cpp index 8dc2d82c5..9872b83e6 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 #include #include @@ -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 CHyprRenderer::glBackend() { +WP 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::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(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 element, const CRegion& damage) { - if (!element) +void IHyprRenderer::drawRect(CRectPassElement* element, const CRegion& damage) { + auto& data = element->m_data; + + if (data.box.w <= 0 || data.box.h <= 0) return; - switch (element->type()) { - case EK_BORDER: draw(dc(element.get()), damage); break; - case EK_CLEAR: draw(dc(element.get()), damage); break; - case EK_FRAMEBUFFER: draw(dc(element.get()), damage); break; - case EK_PRE_BLUR: draw(dc(element.get()), damage); break; - case EK_RECT: draw(dc(element.get()), damage); break; - case EK_HINTS: draw(dc(element.get()), damage); break; - case EK_SHADOW: draw(dc(element.get()), damage); break; - case EK_SURFACE: draw(dc(element.get()), damage); break; - case EK_TEXTURE: draw(dc(element.get()), damage); break; - case EK_TEXTURE_MATTE: draw(dc(element.get()), damage); break; - default: Log::logger->log(Log::WARN, "Unimplimented draw for {}", element->passName()); - } -} + if (!data.clipBox.empty()) + m_renderData.clipBox = data.clipBox; -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}); -} + data.modifiedBox = data.box; + m_renderData.renderModif.applyToBox(data.modifiedBox); -void CHyprRenderer::draw(CClearPassElement* element, const CRegion& damage) { - const auto& m_data = element->m_data; - g_pHyprOpenGL->clear(m_data.color); -}; + 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); -void CHyprRenderer::draw(CFramebufferElement* element, const CRegion& damage) { - const auto& m_data = element->m_data; - SP fb = nullptr; + data.TOPLEFT[0] = sc(transformedBox.x); + data.TOPLEFT[1] = sc(transformedBox.y); + data.FULLSIZE[0] = sc(transformedBox.width); + data.FULLSIZE[1] = sc(transformedBox.height); - 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; - } + data.drawRegion = data.color.a == 1.F || !data.blur ? damage : m_renderData.damage; - if (!fb) { - Log::logger->log(Log::ERR, "BUG THIS: CFramebufferElement::draw: main but null"); - return; - } - - } 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; - } - - 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(); -}; - -void CHyprRenderer::draw(CPreBlurElement* element, const CRegion& damage) { - g_pHyprOpenGL->preBlurForCurrentMonitor(); -}; - -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; - - if (!m_data.clipBox.empty()) - m_renderData.clipBox = m_data.clipBox; - - 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}); + draw(element, damage); m_renderData.clipBox = {}; -}; +} -void CHyprRenderer::draw(CRendererHintsPassElement* element, const CRegion& damage) { - const auto& m_data = element->m_data; +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; -}; +} -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); -}; +void IHyprRenderer::drawPreBlur(CPreBlurElement* element, const CRegion& damage) { + TRACY_GPU_ZONE("RenderPreBlurForCurrentMonitor"); -void CHyprRenderer::draw(CSurfacePassElement* element, const CRegion& damage) { - const auto& m_data = element->m_data; + const auto SAVEDRENDERMODIF = m_renderData.renderModif; + m_renderData.renderModif = {}; // fix shit - 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); + // make the fake dmg + CRegion fakeDamage{0, 0, m_renderData.pMonitor->m_transformedSize.x, m_renderData.pMonitor->m_transformedSize.y}; - 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(); + draw(element, fakeDamage); + + m_renderData.pMonitor->m_blurFBDirty = false; + m_renderData.pMonitor->m_blurFBShouldRender = false; + + m_renderData.renderModif = SAVEDRENDERMODIF; +} + +void IHyprRenderer::drawSurface(CSurfacePassElement* element, const CRegion& damage) { + const auto m_data = element->m_data; + + 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::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::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::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::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)); } - if (!g_pHyprRenderer->m_bBlockSurfaceFeedback) - m_data.surface->presentFeedback(m_data.when, m_data.pMonitor->m_self.lock()); + blend(true); +}; + +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 = {[&]() { - // - g_pHyprRenderer->popMonitorTransformEnabled(); - m_renderData.clipBox = {}; - if (m_data.useMirrorProjection) - setProjectionType(RPT_MONITOR); - if (m_data.ignoreAlpha.has_value()) - m_renderData.discardMode = 0; - }}; - - if (!m_data.clipBox.empty()) - m_renderData.clipBox = m_data.clipBox; - - if (m_data.useMirrorProjection) + pushMonitorTransformEnabled(element->m_data.flipEndFrame); + if (element->m_data.useMirrorProjection) setProjectionType(RPT_MIRROR); - if (m_data.ignoreAlpha.has_value()) { - m_renderData.discardMode = DISCARD_ALPHA; - m_renderData.discardOpacity = *m_data.ignoreAlpha; - } + m_renderData.surface = element->m_data.surface; - 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}); - } -}; + CScopeGuard x = {[useMirrorProjection = element->m_data.useMirrorProjection]() { + g_pHyprRenderer->popMonitorTransformEnabled(); + if (useMirrorProjection) + g_pHyprRenderer->setProjectionType(RPT_MONITOR); + g_pHyprRenderer->m_renderData.surface.reset(); + }}; -void CHyprRenderer::draw(CTextureMatteElement* element, const CRegion& damage) { - const auto& m_data = element->m_data; + 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); + + // 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 (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); + + 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 CHyprRenderer::pushMonitorTransformEnabled(bool enabled) { +void IHyprRenderer::draw(WP element, const CRegion& damage) { + if (!element) + return; + + switch (element->type()) { + case EK_BORDER: draw(dc(element.get()), damage); break; + case EK_CLEAR: draw(dc(element.get()), damage); break; + case EK_FRAMEBUFFER: draw(dc(element.get()), damage); break; + case EK_PRE_BLUR: drawPreBlur(dc(element.get()), damage); break; + case EK_RECT: drawRect(dc(element.get()), damage); break; + case EK_HINTS: drawHints(dc(element.get()), damage); break; + case EK_SHADOW: draw(dc(element.get()), damage); break; + case EK_SURFACE: preDrawSurface(dc(element.get()), damage); break; + case EK_TEXTURE: drawTex(dc(element.get()), damage); break; + case EK_TEXTURE_MATTE: drawTexMatte(dc(element.get()), damage); break; + default: Log::logger->log(Log::WARN, "Unimplimented draw for {}", element->passName()); + } +} + +bool IHyprRenderer::preBlurQueued(PHLMONITORREF pMonitor) { + static auto PBLURNEWOPTIMIZE = CConfigValue("decoration:blur:new_optimizations"); + static auto PBLUR = CConfigValue("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 CHyprRenderer::createTexture(const SP buffer, bool keepDataCopy) { +SP IHyprRenderer::createTexture(const SP buffer, bool keepDataCopy) { if (!buffer) return createTexture(); @@ -1104,7 +1165,7 @@ SP CHyprRenderer::createTexture(const SP 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(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 pSurface, PHLMONITOR pMonitor, const Time::steady_tp& time) { +void IHyprRenderer::renderSessionLockSurface(WP 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 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("decoration:dim_special"); static auto PBLURSPECIAL = CConfigValue("decoration:blur:special"); static auto PBLUR = CConfigValue("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()); if UNLIKELY /* subjective? */ (pWorkspace->m_hasFullscreenWindow) renderWorkspaceWindowsFullscreen(pMonitor, pWorkspace, time); @@ -1423,22 +1481,95 @@ void CHyprRenderer::renderAllClientsForWorkspace(PHLMONITOR pMonitor, PHLWORKSPA } renderDragIcon(pMonitor, time); - - //g_pHyprOpenGL->restoreMatrix(); } -void CHyprRenderer::renderBackground(PHLMONITOR pMonitor) { +SP IHyprRenderer::getBackground(PHLMONITOR pMonitor) { + + if (m_backgroundResourceFailed) + return nullptr; + + if (!m_backgroundResource) { + // queue the asset to be created + requestBackgroundResource(); + return nullptr; + } + + if (!m_backgroundResource->m_ready) + return nullptr; + + Log::logger->log(Log::DEBUG, "Creating a texture for BGTex"); + SP 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 IHyprRenderer::renderBackground(PHLMONITOR pMonitor) { static auto PRENDERTEX = CConfigValue("misc:disable_hyprland_logo"); static auto PBACKGROUNDCOLOR = CConfigValue("misc:background_color"); + static auto PNOSPLASH = CConfigValue("misc:disable_splash_rendering"); if (*PRENDERTEX /* inverted cfg flag */ || pMonitor->m_backgroundOpacity->isBeingAnimated()) m_renderPass.add(makeUnique(CClearPassElement::SClearData{CHyprColor(*PBACKGROUNDCOLOR)})); - if (!*PRENDERTEX) - g_pHyprOpenGL->clearWithTex(); // will apply the hypr "wallpaper" + if (!*PRENDERTEX) { + static auto PBACKGROUNDCOLOR = CConfigValue("misc:background_color"); + + if (!pMonitor->m_background) + pMonitor->m_background = getBackground(pMonitor); + + if (!pMonitor->m_background) + m_renderPass.add(makeUnique(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::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(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(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 CHyprRenderer::loadAsset(const std::string& filename) { +SP IHyprRenderer::loadAsset(const std::string& filename) { const std::string fullPath = resolveAssetPath(filename); @@ -1535,11 +1666,15 @@ SP CHyprRenderer::loadAsset(const std::string& filename) { return tex; } -bool CHyprRenderer::shouldUseNewBlurOptimizations(PHLLS pLayer, PHLWINDOW pWindow) { +SP IHyprRenderer::getBlurTexture(PHLMONITORREF pMonitor) { + return pMonitor->m_blurFB->getTexture(); +} + +bool IHyprRenderer::shouldUseNewBlurOptimizations(PHLLS pLayer, PHLWINDOW pWindow) { static auto PBLURNEWOPTIMIZE = CConfigValue("decoration:blur:new_optimizations"); static auto PBLURXRAY = CConfigValue("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 CHyprRenderer::renderText(const std::string& text, CHyprColor col, int pt, bool italic, const std::string& fontFamily, int maxWidth, int weight) { +SP IHyprRenderer::renderText(const std::string& text, CHyprColor col, int pt, bool italic, const std::string& fontFamily, int maxWidth, int weight) { static auto FONT = CConfigValue("misc:font_family"); const auto FONTFAMILY = fontFamily.empty() ? *FONT : fontFamily; @@ -1659,7 +1794,7 @@ SP 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("misc:session_lock_xray"); if (*PSESSIONLOCKXRAY) return; @@ -1732,7 +1867,7 @@ void CHyprRenderer::renderSessionLockPrimer(PHLMONITOR pMonitor) { m_renderPass.add(makeUnique(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 getSurfaceExpectedSize(PHLWINDOW pWindow, SP pSurface, PHLMONITOR pMonitor, bool main, const Vector2D& projSize, +void IHyprRenderer::calculateUVForSurface(PHLWINDOW pWindow, SP pSurface, PHLMONITOR pMonitor, bool main, const Vector2D& projSize, const Vector2D& projSizeUnscaled, bool fixMisalignedFSV1) { if (!pWindow || !pWindow->m_isX11) { static auto PEXPANDEDGES = CConfigValue("render:expand_undersized_textures"); @@ -1875,6 +2010,96 @@ void CHyprRenderer::calculateUVForSurface(PHLWINDOW pWindow, SP buffer, SP 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 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 transform) { +Mat3x3 IHyprRenderer::getBoxProjection(const CBox& box, std::optional 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 transform) { +Mat3x3 IHyprRenderer::projectBoxToTarget(const CBox& box, std::optional transform) { return m_renderData.pMonitor->getScaleMatrix().copy().multiply(getBoxProjection(box, transform)); } +SP 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::SClearData{{0, 0, 0, 0}}), {}); + + pushMonitorTransformEnabled(true); + + draw(makeUnique(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 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::SClearData{CHyprColor(0, 0, 0, 0)})); + + CTexPassElement::SRenderData data; + data.tex = PFB->getTexture(); + data.box = monbox; + data.useMirrorProjection = true; + + m_renderPass.add(makeUnique(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("render:send_content_type"); static auto PPASS = CConfigValue("render:cm_fs_passthrough"); static auto PAUTOHDR = CConfigValue("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(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 surface, PHLMONITOR monitor) { +void IHyprRenderer::setSurfaceScanoutMode(SP 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& layerSurfaces, bool exclusiveZone, CBox* usableArea) { +void IHyprRenderer::arrangeLayerArray(PHLMONITOR pMonitor, const std::vector& 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::vectorgetMonitorFromID(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 pSurface, double x, double y, double scale) { +void IHyprRenderer::damageSurface(SP pSurface, double x, double y, double scale) { if (!pSurface) return; // wut? @@ -2693,7 +2981,7 @@ void CHyprRenderer::damageSurface(SP 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 surf, int hotspotX, int hotspotY, bool force) { +void IHyprRenderer::setCursorSurface(SP surf, int hotspotX, int hotspotY, bool force) { m_cursorHasSurface = surf && surf->resource(); m_lastCursorData.name = ""; @@ -2804,7 +3092,7 @@ void CHyprRenderer::setCursorSurface(SP 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("cursor:invisible"); static auto PCURSORTIMEOUT = CConfigValue("cursor:inactive_timeout"); static auto PHIDEONTOUCH = CConfigValue("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 CHyprRenderer::getRenderTimes(PHLMONITOR pMonitor) { +std::tuple 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 CHyprRenderer::getOrCreateRenderbuffer(SP buffer, uint32_t fmt) { +SP IHyprRenderer::getOrCreateRenderbuffer(SP 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 CHyprRenderer::getOrCreateRenderbuffer(SP return buf; } -bool CHyprRenderer::beginRender(PHLMONITOR pMonitor, CRegion& damage, eRenderMode mode, SP buffer, SP 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 fb) { + return beginRender(pMonitor, damage, RENDER_MODE_FULL_FAKE, nullptr, fb, true); } -void CHyprRenderer::endRender(const std::function& renderingDoneCallback) { - const auto PMONITOR = m_renderData.pMonitor; - static auto PNVIDIAANTIFLICKER = CConfigValue("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 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 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 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("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::SClearData{CHyprColor(0, 0, 0, 0)}), {}); + startRenderPass(); + + Log::logger->log(Log::DEBUG, "renderer: cleared a snapshot of {:x}", rc(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(pWindow.get())); + endRender(); + Log::logger->log(Log::DEBUG, "renderer: made a snapshot of {:x}", rc(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::SClearData{CHyprColor(0, 0, 0, 0)}), {}); + startRenderPass(); + + Log::logger->log(Log::DEBUG, "renderer: cleared a snapshot of layer {:x}", rc(pLayer.get())); // draw the layer renderLayer(pLayer, PMONITOR, Time::steadyNow()); + Log::logger->log(Log::DEBUG, "renderer: rendered a snapshot of layer {:x}", rc(pLayer.get())); + endRender(); + Log::logger->log(Log::DEBUG, "renderer: made a snapshot of layer {:x}", rc(pLayer.get())); + m_bRenderingSnapshot = false; } -void CHyprRenderer::makeSnapshot(WP popup) { +void IHyprRenderer::makeSnapshot(WP popup) { // we trust the window is valid. const auto PMONITOR = popup->getMonitor(); @@ -3250,11 +3424,11 @@ void CHyprRenderer::makeSnapshot(WP 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::SClearData{CHyprColor(0, 0, 0, 0)}), {}); CSurfacePassElement::SRenderData renderdata; renderdata.pos = popup->coordsGlobal(); @@ -3287,7 +3461,7 @@ void CHyprRenderer::makeSnapshot(WP popup) { m_bRenderingSnapshot = false; } -void CHyprRenderer::renderSnapshot(PHLWINDOW pWindow) { +void IHyprRenderer::renderSnapshot(PHLWINDOW pWindow) { static auto PDIMAROUND = CConfigValue("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(std::move(data))); + m_renderPass.add(makeUnique(data)); } CTexPassElement::SRenderData data; @@ -3347,7 +3521,7 @@ void CHyprRenderer::renderSnapshot(PHLWINDOW pWindow) { m_renderPass.add(makeUnique(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(std::move(data))); } -void CHyprRenderer::renderSnapshot(WP popup) { +void IHyprRenderer::renderSnapshot(WP popup) { if (!popup->m_snapshotFB) return; @@ -3424,7 +3598,7 @@ void CHyprRenderer::renderSnapshot(WP popup) { m_renderPass.add(makeUnique(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 p) { +bool IHyprRenderer::shouldBlur(WP p) { static CConfigValue PBLURPOPUPS = CConfigValue("decoration:blur:popups"); static CConfigValue PBLUR = CConfigValue("decoration:blur:enabled"); return *PBLURPOPUPS && *PBLUR; } -bool CHyprRenderer::reloadShaders(const std::string& path) { - return g_pHyprOpenGL->initShaders(path); -} +SP IHyprRenderer::renderSplash(const std::function(const int, const int, unsigned char* const)>& handleData, const int fontSize, const int maxWidth, + const int maxHeight) { + static auto PSPLASHCOLOR = CConfigValue("misc:col.splash"); + static auto PSPLASHFONT = CConfigValue("misc:splash_font_family"); + static auto FALLBACKFONT = CConfigValue("misc:font_family"); -SP CHyprRenderer::createStencilTexture(const int width, const int height) { - g_pHyprOpenGL->makeEGLCurrent(); - auto tex = makeShared(); - tex->allocate({width, height}); + const auto FONTFAMILY = *PSPLASHFONT != STRVAL_EMPTY ? *PSPLASHFONT : *FALLBACKFONT; + const auto COLOR = CHyprColor(*PSPLASHCOLOR); + const auto CAIROSURFACE = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, maxWidth, maxHeight); + const auto CAIRO = cairo_create(CAIROSURFACE); + + 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); + + PangoLayout* layoutText = pango_cairo_create_layout(CAIRO); + PangoFontDescription* pangoFD = pango_font_description_new(); + + 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); + + 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); + + cairo_move_to(CAIRO, 0, 0); + pango_cairo_show_layout(CAIRO, layoutText); + + pango_font_description_free(pangoFD); + g_object_unref(layoutText); + + cairo_surface_flush(CAIROSURFACE); + + 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); + + auto tex = handleData(textW, textH, cairo_image_surface_get_data(smallSurf)); + + cairo_surface_destroy(smallSurf); + cairo_destroy(small); + + cairo_surface_destroy(CAIROSURFACE); + cairo_destroy(CAIRO); return tex; } -SP CHyprRenderer::createTexture(bool opaque) { - g_pHyprOpenGL->makeEGLCurrent(); - return makeShared(opaque); +bool IHyprRenderer::needsACopyFB(PHLMONITOR mon) { + return !mon->m_mirrors.empty() || Screenshare::mgr()->isOutputBeingSSd(mon); } - -SP CHyprRenderer::createTexture(uint32_t drmFormat, uint8_t* pixels, uint32_t stride, const Vector2D& size, bool keepDataCopy, bool opaque) { - g_pHyprOpenGL->makeEGLCurrent(); - return makeShared(drmFormat, pixels, stride, size, keepDataCopy, opaque); -} - -SP CHyprRenderer::createTexture(const Aquamarine::SDMABUFAttrs& attrs, bool opaque) { - g_pHyprOpenGL->makeEGLCurrent(); - const auto image = g_pHyprOpenGL->createEGLImage(attrs); - if (!image) - return nullptr; - return makeShared(attrs, image, opaque); -} - -SP CHyprRenderer::createTexture(const int width, const int height, unsigned char* const data) { - g_pHyprOpenGL->makeEGLCurrent(); - SP tex = makeShared(); - - tex->allocate({width, height}); - - 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; - - 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); - - glTexImage2D(GL_TEXTURE_2D, 0, glFormat, tex->m_size.x, tex->m_size.y, 0, glFormat, glType, data); - tex->unbind(); - - return tex; -} - -SP CHyprRenderer::createTexture(cairo_surface_t* cairo) { - g_pHyprOpenGL->makeEGLCurrent(); - const auto CAIROFORMAT = cairo_image_surface_get_format(cairo); - auto tex = makeShared(); - - tex->allocate({cairo_image_surface_get_width(cairo), cairo_image_surface_get_height(cairo)}); - - 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; - - 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); - - if (CAIROFORMAT != CAIRO_FORMAT_RGB96F) { - tex->setTexParameter(GL_TEXTURE_SWIZZLE_R, GL_BLUE); - tex->setTexParameter(GL_TEXTURE_SWIZZLE_B, GL_RED); - } - - glTexImage2D(GL_TEXTURE_2D, 0, glIFormat, tex->m_size.x, tex->m_size.y, 0, glFormat, glType, DATA); - - return tex; -} - -SP CHyprRenderer::createTexture(std::span lut3D, size_t N) { - g_pHyprOpenGL->makeEGLCurrent(); - return makeShared(lut3D, N); -} - -SP CHyprRenderer::createFB(const std::string& name) { - g_pHyprOpenGL->makeEGLCurrent(); - return makeShared(name); -} - -bool CHyprRenderer::explicitSyncSupported() { - return g_pHyprOpenGL->explicitSyncSupported(); -} - -std::vector CHyprRenderer::getDRMFormats() { - return g_pHyprOpenGL->getDRMFormats(); -} - -std::vector CHyprRenderer::getDRMFormatModifiers(DRMFormat format) { - return g_pHyprOpenGL->getDRMFormatModifiers(format); -} - -SP CHyprRenderer::getOrCreateRenderbufferInternal(SP buffer, uint32_t fmt) { - g_pHyprOpenGL->makeEGLCurrent(); - return makeShared(buffer, fmt); -} \ No newline at end of file diff --git a/src/render/Renderer.hpp b/src/render/Renderer.hpp index 441a603c2..1d099ee35 100644 --- a/src/render/Renderer.hpp +++ b/src/render/Renderer.hpp @@ -109,12 +109,6 @@ struct SRenderData { // TODO remove and pass directly CBox clipBox = {}; // scaled coordinates - CRegion clipRegion; - - uint32_t discardMode = DISCARD_OPAQUE; - float discardOpacity = 0.f; - - PHLLSREF currentLS; PHLWINDOWREF currentWindow; WP surface; @@ -145,10 +139,10 @@ struct SCMSettings { float sdrBrightnessMultiplier = 1.0; }; -class CHyprRenderer { +class IHyprRenderer { public: - CHyprRenderer(); - ~CHyprRenderer(); + IHyprRenderer(); + virtual ~IHyprRenderer(); WP glBackend(); @@ -168,49 +162,45 @@ class CHyprRenderer { void setCursorHidden(bool hide); void calculateUVForSurface(PHLWINDOW, SP, PHLMONITOR pMonitor, bool main = false, const Vector2D& projSize = {}, const Vector2D& projSizeUnscaled = {}, bool fixMisalignedFSV1 = false); - std::tuple getRenderTimes(PHLMONITOR pMonitor); // avg max min - void ensureLockTexturesRendered(bool load); - void renderLockscreen(PHLMONITOR pMonitor, const Time::steady_tp& now, const CBox& geometry); - void setCursorSurface(SP surf, int hotspotX, int hotspotY, bool force = false); - void setCursorFromName(const std::string& name, bool force = false); - void onRenderbufferDestroy(IRenderbuffer* rb); - SP getCurrentRBO(); - bool isNvidia(); - bool isIntel(); - bool isSoftware(); - bool isMgpu(); - void addWindowToRenderUnfocused(PHLWINDOW window); - void makeSnapshot(PHLWINDOW); - void makeSnapshot(PHLLS); - void makeSnapshot(WP); - void renderSnapshot(PHLWINDOW); - void renderSnapshot(PHLLS); - void renderSnapshot(WP); + std::tuple getRenderTimes(PHLMONITOR pMonitor); // avg max min + void ensureLockTexturesRendered(bool load); + void renderLockscreen(PHLMONITOR pMonitor, const Time::steady_tp& now, const CBox& geometry); + void setCursorSurface(SP surf, int hotspotX, int hotspotY, bool force = false); + void setCursorFromName(const std::string& name, bool force = false); + void onRenderbufferDestroy(IRenderbuffer* rb); + bool isNvidia(); + bool isIntel(); + bool isSoftware(); + bool isMgpu(); + void addWindowToRenderUnfocused(PHLWINDOW window); + void makeSnapshot(PHLWINDOW); + void makeSnapshot(PHLLS); + void makeSnapshot(WP); + void renderSnapshot(PHLWINDOW); + void renderSnapshot(PHLLS); + void renderSnapshot(WP); + bool beginFullFakeRender(PHLMONITOR pMonitor, CRegion& damage, SP fb); + bool beginRenderToBuffer(PHLMONITOR pMonitor, CRegion& damage, SP buffer, bool simple = false); + virtual void startRenderPass() {}; + virtual void endRender(const std::function& renderingDoneCallback = {}) = 0; - // NColorManagement::PImageDescription workBufferImageDescription(); + bool m_bBlockSurfaceFeedback = false; + bool m_bRenderingSnapshot = false; + PHLMONITORREF m_mostHzMonitor; + bool m_directScanoutBlocked = false; - // if RENDER_MODE_NORMAL, provided damage will be written to. - // otherwise, it will be the one used. - bool beginRender(PHLMONITOR pMonitor, CRegion& damage, eRenderMode mode = RENDER_MODE_NORMAL, SP buffer = {}, SP fb = nullptr, bool simple = false); - void endRender(const std::function& renderingDoneCallback = {}); + void setSurfaceScanoutMode(SP surface, PHLMONITOR monitor); // nullptr monitor resets - bool m_bBlockSurfaceFeedback = false; - bool m_bRenderingSnapshot = false; - PHLMONITORREF m_mostHzMonitor; - bool m_directScanoutBlocked = false; + void initiateManualCrash(); + const SRenderData& renderData(); - void setSurfaceScanoutMode(SP surface, PHLMONITOR monitor); // nullptr monitor resets + bool m_crashingInProgress = false; + float m_crashingDistort = 0.5f; + wl_event_source* m_crashingLoop = nullptr; + wl_event_source* m_cursorTicker = nullptr; - void initiateManualCrash(); - const SRenderData& renderData(); - - bool m_crashingInProgress = false; - float m_crashingDistort = 0.5f; - wl_event_source* m_crashingLoop = nullptr; - wl_event_source* m_cursorTicker = nullptr; - - std::vector m_usedAsyncBuffers; + std::vector m_usedAsyncBuffers; struct { int hotspotX = 0; @@ -222,62 +212,97 @@ class CHyprRenderer { std::string name; } m_lastCursorData; - CRenderPass m_renderPass = {}; + CRenderPass m_renderPass; - SP getOrCreateRenderbuffer(SP buffer, uint32_t fmt); // TODO? move to protected and fix CPointerManager::renderHWCursorBuffer - SRenderData m_renderData; // TODO? move to protected and fix CRenderPass - SP m_screencopyDeniedTexture; // TODO? make readonly - uint m_failedAssetsNo = 0; // TODO? make readonly - bool m_reloadScreenShader = true; // at launch it can be set - CTimer m_globalTimer; + SP renderSplash(const std::function(const int, const int, unsigned char* const)>& handleData, const int fontSize, const int maxWidth = 1024, + const int maxHeight = 1024); - SP createStencilTexture(const int width, const int height); - SP createTexture(bool opaque = false); - SP createTexture(uint32_t drmFormat, uint8_t* pixels, uint32_t stride, const Vector2D& size, bool keepDataCopy = false, bool opaque = false); - SP createTexture(const Aquamarine::SDMABUFAttrs&, bool opaque = false); - SP createTexture(const int width, const int height, unsigned char* const); - SP createTexture(cairo_surface_t* cairo); - SP createTexture(const SP buffer, bool keepDataCopy = false); - SP createTexture(std::span lut3D, size_t N); - SP renderText(const std::string& text, CHyprColor col, int pt, bool italic = false, const std::string& fontFamily = "", int maxWidth = 0, int weight = 400); - SP loadAsset(const std::string& filename); - bool shouldUseNewBlurOptimizations(PHLLS pLayer, PHLWINDOW pWindow); - bool explicitSyncSupported(); - std::vector getDRMFormats(); - std::vector getDRMFormatModifiers(DRMFormat format); - SP createFB(const std::string& name = ""); - void pushMonitorTransformEnabled(bool enabled); - void popMonitorTransformEnabled(); - bool monitorTransformEnabled(); + virtual SP getOrCreateRenderbuffer(SP buffer, uint32_t fmt); // TODO? move to protected and fix CPointerManager::renderHWCursorBuffer + bool commitPendingAndDoExplicitSync(PHLMONITOR pMonitor); // TODO? move to protected and fix CMonitorFrameScheduler::onPresented + SRenderData m_renderData; // TODO? move to protected and fix CRenderPass + SP m_screencopyDeniedTexture; // TODO? make readonly + uint m_failedAssetsNo = 0; // TODO? make readonly + bool m_reloadScreenShader = true; // at launch it can be set + CTimer m_globalTimer; - void setProjectionType(const Vector2D& fbSize); - void setProjectionType(eRenderProjectionType projectionType); - Mat3x3 getBoxProjection(const CBox& box, std::optional transform = std::nullopt); - Mat3x3 projectBoxToTarget(const CBox& box, std::optional transform = std::nullopt); + void draw(WP element, const CRegion& damage); + virtual SP createStencilTexture(const int width, const int height) = 0; + virtual SP createTexture(bool opaque = false) = 0; + virtual SP createTexture(uint32_t drmFormat, uint8_t* pixels, uint32_t stride, const Vector2D& size, bool keepDataCopy = false, bool opaque = false) = 0; + virtual SP createTexture(const Aquamarine::SDMABUFAttrs&, bool opaque = false) = 0; + virtual SP createTexture(const int width, const int height, unsigned char* const) = 0; + virtual SP createTexture(cairo_surface_t* cairo) = 0; + virtual SP createTexture(std::span lut3D, size_t N) = 0; + virtual SP createTexture(const SP buffer, bool keepDataCopy = false); + virtual SP renderText(const std::string& text, CHyprColor col, int pt, bool italic = false, const std::string& fontFamily = "", int maxWidth = 0, int weight = 400); + SP loadAsset(const std::string& filename); + virtual bool shouldUseNewBlurOptimizations(PHLLS pLayer, PHLWINDOW pWindow); + virtual bool explicitSyncSupported() = 0; + virtual std::vector getDRMFormats() = 0; + virtual std::vector getDRMFormatModifiers(DRMFormat format) = 0; + virtual SP createFB(const std::string& name = "") = 0; + virtual void disableScissor() = 0; + virtual void blend(bool enabled) = 0; + virtual void drawShadow(const CBox& box, int round, float roundingPower, int range, CHyprColor color, float a) = 0; + virtual void setViewport(int x, int y, int width, int height) = 0; - SCMSettings getCMSettings(const NColorManagement::PImageDescription imageDescription, const NColorManagement::PImageDescription targetImageDescription, - SP surface = nullptr, bool modifySDR = false, float sdrMinLuminance = -1.0f, int sdrMaxLuminance = -1); - bool reloadShaders(const std::string& path = ""); + bool preBlurQueued(PHLMONITORREF pMonitor); + void pushMonitorTransformEnabled(bool enabled); + void popMonitorTransformEnabled(); + bool monitorTransformEnabled(); - void draw(CBorderPassElement* element, const CRegion& damage); - void draw(CClearPassElement* element, const CRegion& damage); - void draw(CFramebufferElement* element, const CRegion& damage); - void draw(CPreBlurElement* element, const CRegion& damage); - void draw(CRectPassElement* element, const CRegion& damage); - void draw(CRendererHintsPassElement* element, const CRegion& damage); - void draw(CShadowPassElement* element, const CRegion& damage); - void draw(CSurfacePassElement* element, const CRegion& damage); - void draw(CTexPassElement* element, const CRegion& damage); - void draw(CTextureMatteElement* element, const CRegion& damage); - void draw(WP element, const CRegion& damage); + void setProjectionType(const Vector2D& fbSize); + void setProjectionType(eRenderProjectionType projectionType); + Mat3x3 getBoxProjection(const CBox& box, std::optional transform = std::nullopt); + Mat3x3 projectBoxToTarget(const CBox& box, std::optional transform = std::nullopt); - SP m_lockDeadTexture; - SP m_lockDead2Texture; - SP m_lockTtyTextTexture; - bool m_monitorTransformEnabled = false; // do not modify directly - std::stack m_monitorTransformStack; + SP blurMainFramebuffer(float a, CRegion* originalDamage); + virtual SP blurFramebuffer(SP source, float a, CRegion* originalDamage) = 0; + void preBlurForCurrentMonitor(CRegion* fakeDamage); - private: + SCMSettings getCMSettings(const NColorManagement::PImageDescription imageDescription, const NColorManagement::PImageDescription targetImageDescription, + SP surface = nullptr, bool modifySDR = false, float sdrMinLuminance = -1.0f, int sdrMaxLuminance = -1); + virtual bool reloadShaders(const std::string& path = "") = 0; + + bool needsACopyFB(PHLMONITOR mon); + + protected: + virtual void renderOffToMain(IFramebuffer* off) = 0; + virtual SP getOrCreateRenderbufferInternal(SP buffer, uint32_t fmt) = 0; + void renderMirrored(); + void setDamage(const CRegion& damage_, std::optional finalDamage); + // if RENDER_MODE_NORMAL, provided damage will be written to. + // otherwise, it will be the one used. + bool beginRender(PHLMONITOR pMonitor, CRegion& damage, eRenderMode mode = RENDER_MODE_NORMAL, SP buffer = {}, SP fb = nullptr, bool simple = false); + + virtual bool beginRenderInternal(PHLMONITOR pMonitor, CRegion& damage, bool simple = false) { + return false; + }; + virtual bool beginFullFakeRenderInternal(PHLMONITOR pMonitor, CRegion& damage, SP fb, bool simple = false) { + return false; + }; + virtual void initRender() {}; + virtual bool initRenderBuffer(SP buffer, uint32_t fmt) { + return false; + }; + + SP getBackground(PHLMONITOR pMonitor); + virtual void draw(CBorderPassElement* element, const CRegion& damage) = 0; + virtual void draw(CClearPassElement* element, const CRegion& damage) = 0; + virtual void draw(CFramebufferElement* element, const CRegion& damage) = 0; + virtual void draw(CPreBlurElement* element, const CRegion& damage) = 0; + virtual void draw(CRectPassElement* element, const CRegion& damage) = 0; + virtual void draw(CShadowPassElement* element, const CRegion& damage) = 0; + virtual void draw(CTexPassElement* element, const CRegion& damage) = 0; + virtual void draw(CTextureMatteElement* element, const CRegion& damage) = 0; + virtual SP getBlurTexture(PHLMONITORREF pMonitor); + SP m_lockDeadTexture; + SP m_lockDead2Texture; + SP m_lockTtyTextTexture; + bool m_monitorTransformEnabled = false; // do not modify directly + std::stack m_monitorTransformStack; + + // old private: void arrangeLayerArray(PHLMONITOR, const std::vector&, bool, CBox*); void renderWorkspace(PHLMONITOR pMonitor, PHLWORKSPACE pWorkspace, const Time::steady_tp& now, const CBox& geometry); void renderWorkspaceWindowsFullscreen(PHLMONITOR, PHLWORKSPACE, const Time::steady_tp&); // renders workspace windows (fullscreen) (tiled, floating, pinned, but no special) @@ -292,12 +317,7 @@ class CHyprRenderer { void renderSessionLockPrimer(PHLMONITOR pMonitor); void renderSessionLockMissing(PHLMONITOR pMonitor); void renderBackground(PHLMONITOR pMonitor); - - bool commitPendingAndDoExplicitSync(PHLMONITOR pMonitor); - void requestBackgroundResource(); - // - SP getOrCreateRenderbufferInternal(SP buffer, uint32_t fmt); std::string resolveAssetPath(const std::string& file); void initMissingAssetTexture(); void initAssets(); @@ -312,7 +332,6 @@ class CHyprRenderer { bool m_cursorHidden = false; bool m_cursorHiddenByCondition = false; bool m_cursorHasSurface = false; - SP m_currentRenderbuffer = nullptr; SP m_currentBuffer = nullptr; eRenderMode m_renderMode = RENDER_MODE_NORMAL; bool m_nvidia = false; @@ -339,6 +358,18 @@ class CHyprRenderer { friend class CPointerManager; friend class CMonitor; friend class CMonitorFrameScheduler; + + private: + void bindOffMain(); + void bindBackOnMain(); + + void drawRect(CRectPassElement* element, const CRegion& damage); + void drawHints(CRendererHintsPassElement* element, const CRegion& damage); + void drawPreBlur(CPreBlurElement* element, const CRegion& damage); + void drawSurface(CSurfacePassElement* element, const CRegion& damage); + void preDrawSurface(CSurfacePassElement* element, const CRegion& damage); + void drawTex(CTexPassElement* element, const CRegion& damage); + void drawTexMatte(CTextureMatteElement* element, const CRegion& damage); }; -inline UP g_pHyprRenderer; +inline UP g_pHyprRenderer; diff --git a/src/render/decorations/CHyprBorderDecoration.cpp b/src/render/decorations/CHyprBorderDecoration.cpp index 64dde4ff1..2785a9563 100644 --- a/src/render/decorations/CHyprBorderDecoration.cpp +++ b/src/render/decorations/CHyprBorderDecoration.cpp @@ -84,6 +84,7 @@ void CHyprBorderDecoration::draw(PHLMONITOR pMonitor, float const& a) { data.roundingPower = ROUNDINGPOWER; data.a = a; data.borderSize = borderSize; + data.window = m_window; if (ANIMATED) { data.hasGrad2 = true; diff --git a/src/render/decorations/CHyprDropShadowDecoration.cpp b/src/render/decorations/CHyprDropShadowDecoration.cpp index 69caa2cd5..000e1d0f8 100644 --- a/src/render/decorations/CHyprDropShadowDecoration.cpp +++ b/src/render/decorations/CHyprDropShadowDecoration.cpp @@ -4,6 +4,7 @@ #include "../../config/ConfigValue.hpp" #include "../pass/ShadowPassElement.hpp" #include "../Renderer.hpp" +#include "render/pass/TextureMatteElement.hpp" CHyprDropShadowDecoration::CHyprDropShadowDecoration(PHLWINDOW pWindow) : IHyprWindowDecoration(pWindow), m_window(pWindow) { ; @@ -95,37 +96,46 @@ void CHyprDropShadowDecoration::draw(PHLMONITOR pMonitor, float const& a) { g_pHyprRenderer->m_renderPass.add(makeUnique(data)); } -void CHyprDropShadowDecoration::render(PHLMONITOR pMonitor, float const& a) { +bool CHyprDropShadowDecoration::canRender(PHLMONITOR pMonitor) { + static auto PSHADOWS = CConfigValue("decoration:shadow:enabled"); + if (*PSHADOWS != 1) + return false; // disabled + const auto PWINDOW = m_window.lock(); if (!validMapped(PWINDOW)) - return; + return false; if (PWINDOW->m_realShadowColor->value() == CHyprColor(0, 0, 0, 0)) - return; // don't draw invisible shadows + return false; // don't draw invisible shadows if (!PWINDOW->m_ruleApplicator->decorate().valueOrDefault()) - return; + return false; if (PWINDOW->m_ruleApplicator->noShadow().valueOrDefault()) - return; + return false; + + return true; +} + +SShadowRenderData CHyprDropShadowDecoration::getRenderData(PHLMONITOR pMonitor, float const& a) { + if (!canRender(pMonitor)) + return {}; + + const auto PWINDOW = m_window.lock(); - static auto PSHADOWS = CConfigValue("decoration:shadow:enabled"); static auto PSHADOWSIZE = CConfigValue("decoration:shadow:range"); static auto PSHADOWIGNOREWINDOW = CConfigValue("decoration:shadow:ignore_window"); static auto PSHADOWSCALE = CConfigValue("decoration:shadow:scale"); static auto PSHADOWOFFSET = CConfigValue("decoration:shadow:offset"); - if (*PSHADOWS != 1) - return; // disabled - - const auto BORDERSIZE = PWINDOW->getRealBorderSize(); - const auto ROUNDINGBASE = PWINDOW->rounding(); - const auto ROUNDINGPOWER = PWINDOW->roundingPower(); - const auto CORRECTIONOFFSET = (BORDERSIZE * (M_SQRT2 - 1) * std::max(2.0 - ROUNDINGPOWER, 0.0)); - const auto ROUNDING = ROUNDINGBASE > 0 ? (ROUNDINGBASE + BORDERSIZE) - CORRECTIONOFFSET : 0; - const auto PWORKSPACE = PWINDOW->m_workspace; - const auto WORKSPACEOFFSET = PWORKSPACE && !PWINDOW->m_pinned ? PWORKSPACE->m_renderOffset->value() : Vector2D(); + const auto BORDERSIZE = PWINDOW->getRealBorderSize(); + const auto ROUNDINGBASE = PWINDOW->rounding(); + const auto ROUNDINGPOWER = PWINDOW->roundingPower(); + const auto CORRECTIONOFFSET = (BORDERSIZE * (M_SQRT2 - 1) * std::max(2.0 - ROUNDINGPOWER, 0.0)); + const auto ROUNDING = ROUNDINGBASE > 0 ? (ROUNDINGBASE + BORDERSIZE) - CORRECTIONOFFSET : 0; + const auto PWORKSPACE = PWINDOW->m_workspace; + const auto WORKSPACEOFFSET = PWORKSPACE && !PWINDOW->m_pinned ? PWORKSPACE->m_renderOffset->value() : Vector2D(); // draw the shadow CBox fullBox = m_lastWindowBoxWithDecos; @@ -142,27 +152,31 @@ void CHyprDropShadowDecoration::render(PHLMONITOR pMonitor, float const& a) { updateWindow(PWINDOW); m_lastWindowPos += WORKSPACEOFFSET; - m_extents = {{m_lastWindowPos.x - fullBox.x - pMonitor->m_position.x + 2, m_lastWindowPos.y - fullBox.y - pMonitor->m_position.y + 2}, - {fullBox.x + fullBox.width + pMonitor->m_position.x - m_lastWindowPos.x - m_lastWindowSize.x + 2, - fullBox.y + fullBox.height + pMonitor->m_position.y - m_lastWindowPos.y - m_lastWindowSize.y + 2}}; + m_extents = { + .topLeft = + { + m_lastWindowPos.x - fullBox.x - pMonitor->m_position.x + 2, + m_lastWindowPos.y - fullBox.y - pMonitor->m_position.y + 2, + }, + .bottomRight = + { + fullBox.x + fullBox.width + pMonitor->m_position.x - m_lastWindowPos.x - m_lastWindowSize.x + 2, + fullBox.y + fullBox.height + pMonitor->m_position.y - m_lastWindowPos.y - m_lastWindowSize.y + 2, + }, + }; fullBox.translate(PWINDOW->m_floatingOffset); if (fullBox.width < 1 || fullBox.height < 1) - return; // don't draw invisible shadows + return {}; // don't draw invisible shadows - g_pHyprOpenGL->scissor(nullptr); g_pHyprRenderer->m_renderData.currentWindow = m_window; - // we'll take the liberty of using this as it should not be used rn - auto alphaFB = g_pHyprRenderer->m_renderData.pMonitor->m_mirrorFB; - auto alphaSwapFB = g_pHyprRenderer->m_renderData.pMonitor->m_mirrorSwapFB; - auto LASTFB = g_pHyprRenderer->m_renderData.currentFB; - + CBox windowBox; + CRegion saveDamage; fullBox.scale(pMonitor->m_scale).round(); - if (*PSHADOWIGNOREWINDOW) { - CBox windowBox = m_lastWindowBox; + windowBox = m_lastWindowBox; CBox withDecos = m_lastWindowBoxWithDecos; // get window box @@ -180,51 +194,98 @@ void CHyprDropShadowDecoration::render(PHLMONITOR pMonitor, float const& a) { windowBox.scale(pMonitor->m_scale).round().addExtents(scaledExtentss); if (windowBox.width < 1 || windowBox.height < 1) - return; // prevent assert failed + return {}; // prevent assert failed - CRegion saveDamage = g_pHyprRenderer->m_renderData.damage; + saveDamage = g_pHyprRenderer->m_renderData.damage; g_pHyprRenderer->m_renderData.damage = fullBox; g_pHyprRenderer->m_renderData.damage.subtract(windowBox.copy().expand(-ROUNDING * pMonitor->m_scale)).intersect(saveDamage); g_pHyprRenderer->m_renderData.renderModif.applyToRegion(g_pHyprRenderer->m_renderData.damage); + } + + return { + .ignoreWindow = *PSHADOWIGNOREWINDOW, + .valid = true, + .fullBox = fullBox, + .windowBox = windowBox, + .saveDamage = saveDamage, + .rounding = ROUNDING, + .roundingPower = ROUNDINGPOWER, + .size = *PSHADOWSIZE, + }; +} + +void CHyprDropShadowDecoration::reposition() { + if (m_extents != m_reportedExtents) + g_pDecorationPositioner->repositionDeco(this); + + g_pHyprRenderer->m_renderData.currentWindow.reset(); +} + +// TODO remove +void CHyprDropShadowDecoration::render(PHLMONITOR pMonitor, float const& a) { + auto data = getRenderData(pMonitor, a); + if (!data.valid) + return; + + const auto PWINDOW = m_window.lock(); + + g_pHyprRenderer->disableScissor(); + + if (data.ignoreWindow) { + // we'll take the liberty of using this as it should not be used rn + const auto alphaFB = g_pHyprRenderer->m_renderData.pMonitor->m_mirrorFB; + const auto alphaSwapFB = g_pHyprRenderer->m_renderData.pMonitor->m_mirrorSwapFB; + const auto LASTFB = g_pHyprRenderer->m_renderData.currentFB; + + CBox monbox = {0, 0, pMonitor->m_transformedSize.x, pMonitor->m_transformedSize.y}; alphaFB->bind(); // build the matte // 10-bit formats have dogshit alpha channels, so we have to use the matte to its fullest. // first, clear region of interest with black (fully transparent) - g_pHyprOpenGL->renderRect(fullBox, CHyprColor(0, 0, 0, 1), {.round = 0}); + g_pHyprRenderer->draw(makeUnique(CRectPassElement::SRectData{.box = data.fullBox, .color = CHyprColor(0, 0, 0, 1), .round = 0}), monbox); // render white shadow with the alpha of the shadow color (otherwise we clear with alpha later and shit it to 2 bit) - drawShadowInternal(fullBox, ROUNDING * pMonitor->m_scale, ROUNDINGPOWER, *PSHADOWSIZE * pMonitor->m_scale, CHyprColor(1, 1, 1, PWINDOW->m_realShadowColor->value().a), a); + drawShadowInternal(data.fullBox, data.rounding * pMonitor->m_scale, data.roundingPower, data.size * pMonitor->m_scale, + CHyprColor(1, 1, 1, PWINDOW->m_realShadowColor->value().a), a); // render black window box ("clip") - g_pHyprOpenGL->renderRect(windowBox, CHyprColor(0, 0, 0, 1.0), - {.round = (ROUNDING + 1 /* This fixes small pixel gaps. */) * pMonitor->m_scale, .roundingPower = ROUNDINGPOWER}); + g_pHyprRenderer->draw(makeUnique(CRectPassElement::SRectData{ + .box = data.windowBox, + .color = CHyprColor(0, 0, 0, 1), + .round = (data.rounding + 1 /* This fixes small pixel gaps. */) * pMonitor->m_scale, + .roundingPower = data.roundingPower, + }), + monbox); alphaSwapFB->bind(); // alpha swap just has the shadow color. It will be the "texture" to render. - g_pHyprOpenGL->renderRect(fullBox, PWINDOW->m_realShadowColor->value().stripA(), {.round = 0}); + g_pHyprRenderer->draw(makeUnique(CRectPassElement::SRectData{.box = data.fullBox, .color = PWINDOW->m_realShadowColor->value().stripA(), .round = 0}), + monbox); LASTFB->bind(); - CBox monbox = {0, 0, pMonitor->m_transformedSize.x, pMonitor->m_transformedSize.y}; - g_pHyprRenderer->pushMonitorTransformEnabled(true); - g_pHyprOpenGL->setRenderModifEnabled(false); - g_pHyprOpenGL->renderTextureMatte(alphaSwapFB->getTexture(), monbox, alphaFB); - g_pHyprOpenGL->setRenderModifEnabled(true); + g_pHyprRenderer->m_renderData.renderModif.enabled = false; + + g_pHyprRenderer->draw(makeUnique(CTextureMatteElement::STextureMatteData{ + .box = monbox, + .tex = alphaSwapFB->getTexture(), + .fb = alphaFB, + }), + {}); + + g_pHyprRenderer->m_renderData.renderModif.enabled = true; g_pHyprRenderer->popMonitorTransformEnabled(); - g_pHyprRenderer->m_renderData.damage = saveDamage; + g_pHyprRenderer->m_renderData.damage = data.saveDamage; } else - drawShadowInternal(fullBox, ROUNDING * pMonitor->m_scale, ROUNDINGPOWER, *PSHADOWSIZE * pMonitor->m_scale, PWINDOW->m_realShadowColor->value(), a); + drawShadowInternal(data.fullBox, data.rounding * pMonitor->m_scale, data.roundingPower, data.size * pMonitor->m_scale, PWINDOW->m_realShadowColor->value(), a); - if (m_extents != m_reportedExtents) - g_pDecorationPositioner->repositionDeco(this); - - g_pHyprRenderer->m_renderData.currentWindow.reset(); + reposition(); } eDecorationLayer CHyprDropShadowDecoration::getDecorationLayer() { @@ -237,12 +298,18 @@ void CHyprDropShadowDecoration::drawShadowInternal(const CBox& box, int round, f if (box.w < 1 || box.h < 1) return; - g_pHyprOpenGL->blend(true); + g_pHyprRenderer->blend(true); color.a *= a; if (*PSHADOWSHARP) - g_pHyprOpenGL->renderRect(box, color, {.round = round, .roundingPower = roundingPower}); + g_pHyprRenderer->draw(makeUnique(CRectPassElement::SRectData{ + .box = box, + .color = color, + .round = round, + .roundingPower = roundingPower, + }), + {}); else - g_pHyprOpenGL->renderRoundedShadow(box, round, roundingPower, range, color, 1.F); + g_pHyprRenderer->drawShadow(box, round, roundingPower, range, color, 1.F); } diff --git a/src/render/decorations/CHyprDropShadowDecoration.hpp b/src/render/decorations/CHyprDropShadowDecoration.hpp index 22887a522..d8a01edb0 100644 --- a/src/render/decorations/CHyprDropShadowDecoration.hpp +++ b/src/render/decorations/CHyprDropShadowDecoration.hpp @@ -2,6 +2,17 @@ #include "IHyprWindowDecoration.hpp" +struct SShadowRenderData { + bool ignoreWindow = false; + bool valid = false; + CBox fullBox; + CBox windowBox; + CRegion saveDamage; + float rounding = 0; + float roundingPower = 0; + int size = 0; +}; + class CHyprDropShadowDecoration : public IHyprWindowDecoration { public: CHyprDropShadowDecoration(PHLWINDOW); @@ -25,7 +36,12 @@ class CHyprDropShadowDecoration : public IHyprWindowDecoration { virtual std::string getDisplayName(); - void render(PHLMONITOR, float const& a); + bool canRender(PHLMONITOR); + SShadowRenderData getRenderData(PHLMONITOR, float const& a); + void reposition(); + + // TODO remove + void render(PHLMONITOR, float const& a); private: SBoxExtents m_extents; diff --git a/src/render/gl/GLFramebuffer.cpp b/src/render/gl/GLFramebuffer.cpp index f3952556f..27ad4a1b6 100644 --- a/src/render/gl/GLFramebuffer.cpp +++ b/src/render/gl/GLFramebuffer.cpp @@ -2,7 +2,7 @@ #include "../OpenGL.hpp" #include "../Renderer.hpp" #include "macros.hpp" -#include "render/Framebuffer.hpp" +#include "../Framebuffer.hpp" CGLFramebuffer::CGLFramebuffer() : IFramebuffer() {} CGLFramebuffer::CGLFramebuffer(const std::string& name) : IFramebuffer(name) {} @@ -71,9 +71,10 @@ void CGLFramebuffer::addStencil(SP tex) { void CGLFramebuffer::bind() { glBindFramebuffer(GL_DRAW_FRAMEBUFFER, m_fb); - if (g_pHyprOpenGL) - g_pHyprOpenGL->setViewport(0, 0, g_pHyprRenderer->m_renderData.pMonitor->m_pixelSize.x, g_pHyprRenderer->m_renderData.pMonitor->m_pixelSize.y); - else + if (g_pHyprOpenGL) { + const auto& size = g_pHyprRenderer->m_renderData.pMonitor ? g_pHyprRenderer->m_renderData.pMonitor->m_pixelSize : m_size; + g_pHyprOpenGL->setViewport(0, 0, size.x, size.y); + } else glViewport(0, 0, m_size.x, m_size.y); } diff --git a/src/render/pass/BorderPassElement.hpp b/src/render/pass/BorderPassElement.hpp index 1a458fd55..6513b74a2 100644 --- a/src/render/pass/BorderPassElement.hpp +++ b/src/render/pass/BorderPassElement.hpp @@ -13,6 +13,7 @@ class CBorderPassElement : public IPassElement { float lerp = 0.F, a = 1.F; int round = 0, borderSize = 1, outerRound = -1; float roundingPower = 2.F; + PHLWINDOWREF window; }; CBorderPassElement(const SBorderData& data_); diff --git a/src/render/pass/Pass.cpp b/src/render/pass/Pass.cpp index 901b4770d..b7923a665 100644 --- a/src/render/pass/Pass.cpp +++ b/src/render/pass/Pass.cpp @@ -213,9 +213,15 @@ void CRenderPass::renderDebugData() { const auto pMonitor = g_pHyprRenderer->m_renderData.pMonitor; CBox box = {{}, pMonitor->m_transformedSize}; for (const auto& rg : m_occludedRegions) { - g_pHyprOpenGL->renderRect(box, Colors::RED.modifyA(0.1F), {.damage = &rg}); + CRectPassElement::SRectData data; + data.box = box; + data.color = Colors::RED.modifyA(0.1F); + g_pHyprRenderer->draw(makeUnique(data), rg); } - g_pHyprOpenGL->renderRect(box, Colors::GREEN.modifyA(0.1F), {.damage = &m_totalLiveBlurRegion}); + CRectPassElement::SRectData data; + data.box = box; + data.color = Colors::GREEN.modifyA(0.1F); + g_pHyprRenderer->draw(makeUnique(data), m_totalLiveBlurRegion); std::unordered_map offsets; @@ -238,9 +244,12 @@ void CRenderPass::renderDebugData() { if (box.intersection(CBox{{}, pMonitor->m_size}).empty()) return; - static const auto FULL_REGION = CRegion{0, 0, INT32_MAX, INT32_MAX}; + static const auto FULL_REGION = CRegion{0, 0, INT32_MAX, INT32_MAX}; - g_pHyprOpenGL->renderRect(box, color, {.damage = &FULL_REGION}); + CRectPassElement::SRectData data; + data.box = box; + data.color = color; + g_pHyprRenderer->draw(makeUnique(data), FULL_REGION); if (offsets.contains(surface.get())) box.translate(Vector2D{0.F, offsets[surface.get()]}); @@ -248,8 +257,16 @@ void CRenderPass::renderDebugData() { offsets[surface.get()] = 0; box = {box.pos(), texture->m_size}; - g_pHyprOpenGL->renderRect(box, CHyprColor{0.F, 0.F, 0.F, 0.2F}, {.damage = &FULL_REGION, .round = std::min(5.0, box.size().y)}); - g_pHyprOpenGL->renderTexture(texture, box, {}); + CRectPassElement::SRectData data2; + data.box = box; + data.color = color; + data.round = std::min(5.0, box.size().y); + g_pHyprRenderer->draw(makeUnique(data2), FULL_REGION); + + CTexPassElement::SRenderData texData; + texData.tex = texture; + texData.box = box; + g_pHyprRenderer->draw(makeUnique(texData), {}); offsets[surface.get()] += texture->m_size.y; }; @@ -267,7 +284,10 @@ void CRenderPass::renderDebugData() { auto BOX = hlSurface->getSurfaceBoxGlobal(); if (BOX) { auto region = g_pSeatManager->m_state.pointerFocus->m_current.input.copy().scale(pMonitor->m_scale).translate(BOX->pos() - pMonitor->m_position); - g_pHyprOpenGL->renderRect(box, CHyprColor{0.8F, 0.8F, 0.2F, 0.4F}, {.damage = ®ion}); + CRectPassElement::SRectData data; + data.box = box; + data.color = CHyprColor{0.8F, 0.8F, 0.2F, 0.4F}; + g_pHyprRenderer->draw(makeUnique(data), region); } } } @@ -280,7 +300,10 @@ void CRenderPass::renderDebugData() { if (tex) { box = CBox{{0.F, pMonitor->m_size.y - tex->m_size.y}, tex->m_size}.scale(pMonitor->m_scale); - g_pHyprOpenGL->renderTexture(tex, box, {}); + CTexPassElement::SRenderData texData; + texData.tex = tex; + texData.box = box; + g_pHyprRenderer->draw(makeUnique(texData), {}); } std::string passStructure; @@ -297,7 +320,10 @@ void CRenderPass::renderDebugData() { tex = g_pHyprRenderer->renderText(passStructure, Colors::WHITE, 12); if (tex) { box = CBox{{pMonitor->m_size.x - tex->m_size.x, pMonitor->m_size.y - tex->m_size.y}, tex->m_size}.scale(pMonitor->m_scale); - g_pHyprOpenGL->renderTexture(tex, box, {}); + CTexPassElement::SRenderData texData; + texData.tex = tex; + texData.box = box; + g_pHyprRenderer->draw(makeUnique(texData), {}); } } diff --git a/src/render/pass/RectPassElement.hpp b/src/render/pass/RectPassElement.hpp index b6d310ad4..a61ab1558 100644 --- a/src/render/pass/RectPassElement.hpp +++ b/src/render/pass/RectPassElement.hpp @@ -12,6 +12,12 @@ class CRectPassElement : public IPassElement { bool blur = false, xray = false; float blurA = 1.F; CBox clipBox; + + // internal + CBox modifiedBox; + float TOPLEFT[2]; + float FULLSIZE[2]; + CRegion drawRegion; }; CRectPassElement(const SRectData& data); diff --git a/src/render/pass/SurfacePassElement.hpp b/src/render/pass/SurfacePassElement.hpp index bd02417dc..c76b475d0 100644 --- a/src/render/pass/SurfacePassElement.hpp +++ b/src/render/pass/SurfacePassElement.hpp @@ -1,5 +1,6 @@ #pragma once #include "PassElement.hpp" +#include "TexPassElement.hpp" #include #include "../../helpers/time/Time.hpp" @@ -41,7 +42,7 @@ class CSurfacePassElement : public IPassElement { CBox clipBox = {}; // scaled coordinates - uint32_t discardMode = 0; + uint32_t discardMode = DISCARD_OPAQUE; float discardOpacity = 0.f; bool useNearestNeighbor = false; diff --git a/src/render/pass/TexPassElement.hpp b/src/render/pass/TexPassElement.hpp index 1ac3db0ce..5b8c9757c 100644 --- a/src/render/pass/TexPassElement.hpp +++ b/src/render/pass/TexPassElement.hpp @@ -6,22 +6,42 @@ class CWLSurfaceResource; class ITexture; class CSyncTimeline; +enum eDiscardMode : uint8_t { + DISCARD_OPAQUE = 1, + DISCARD_ALPHA = 1 << 1 +}; + class CTexPassElement : public IPassElement { public: struct SRenderData { - SP tex; - CBox box; - float a = 1.F; - float blurA = 1.F; - CRegion damage; - int round = 0; - float roundingPower = 2.0f; - bool flipEndFrame = false; - bool useMirrorProjection = false; - CBox clipBox; - bool blur = false; - std::optional ignoreAlpha; - std::optional blockBlurOptimization; + SP tex; + CBox box; + float a = 1.F; + float blurA = 1.F; + float overallA = 1.F; + CRegion damage; + int round = 0; + float roundingPower = 2.0f; + bool flipEndFrame = false; + bool useMirrorProjection = false; + CBox clipBox; + bool blur = false; + std::optional ignoreAlpha; + std::optional blockBlurOptimization; + bool cmBackToSRGB = false; + SP cmBackToSRGBSource; + + bool discardActive = false; + bool allowCustomUV = false; + SP surface = nullptr; + + uint32_t discardMode = DISCARD_OPAQUE; + float discardOpacity = 0.f; + + CRegion clipRegion; + PHLLSREF currentLS; + + SP blurredBG; }; CTexPassElement(const SRenderData& data);