From c050ae43b1b99e58b6e4890fd0b94fb6a8288fb9 Mon Sep 17 00:00:00 2001
From: Kaley Fischer <koningdragon@gmail.com>
Date: Fri, 24 Nov 2023 22:48:58 +0100
Subject: [PATCH] updates: added all the new updated and bugs

---
 .github/workflows/ci.yaml        |   2 +-
 CMakeLists.txt                   |   6 +-
 Makefile                         |   4 +-
 flake.lock                       |  20 ++--
 flake.nix                        |   2 +-
 src/Compositor.cpp               |   5 +-
 src/Compositor.hpp               |   1 -
 src/debug/TracyDefines.hpp       |  11 +-
 src/events/Events.hpp            |   6 +-
 src/events/Misc.cpp              |  14 ---
 src/events/Windows.cpp           |   2 +-
 src/helpers/Monitor.cpp          |   8 ++
 src/helpers/Monitor.hpp          |  47 ++++----
 src/helpers/Region.cpp           |  11 +-
 src/helpers/Region.hpp           |   2 +
 src/includes.hpp                 |   4 +-
 src/managers/XWaylandManager.cpp |  10 +-
 src/protocols/Screencopy.cpp     |  23 ++--
 src/protocols/Screencopy.hpp     |   2 +-
 src/protocols/ToplevelExport.cpp |  61 +++--------
 src/render/Framebuffer.cpp       |  17 ++-
 src/render/Framebuffer.hpp       |   3 +-
 src/render/OpenGL.cpp            | 153 ++++++++++++++++++--------
 src/render/OpenGL.hpp            |  28 +++--
 src/render/Renderbuffer.cpp      |  81 ++++++++++++++
 src/render/Renderbuffer.hpp      |  25 +++++
 src/render/Renderer.cpp          | 183 +++++++++++++++++++++++++------
 src/render/Renderer.hpp          |  46 ++++++--
 subprojects/wlroots.wrap         |   2 +-
 29 files changed, 532 insertions(+), 247 deletions(-)
 create mode 100644 src/render/Renderbuffer.cpp
 create mode 100644 src/render/Renderbuffer.hpp

diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml
index b8093491..a78b7522 100644
--- a/.github/workflows/ci.yaml
+++ b/.github/workflows/ci.yaml
@@ -40,7 +40,7 @@ jobs:
           cp ./LICENSE hyprland/
           cp build/Hyprland hyprland/
           cp build/hyprctl/hyprctl hyprland/
-          cp subprojects/wlroots/build/libwlroots.so.12032 hyprland/
+          cp subprojects/wlroots/build/libwlroots.so.13032 hyprland/
           cp build/Hyprland hyprland/
           cp -r example/ hyprland/
           cp -r assets/ hyprland/
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 3bdbb0da..3f62a65e 100755
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -57,12 +57,12 @@ ExternalProject_Add(
     wlroots
     PREFIX ${CMAKE_SOURCE_DIR}/subprojects/wlroots
     SOURCE_DIR ${CMAKE_SOURCE_DIR}/subprojects/wlroots
-    PATCH_COMMAND sed -E -i -e "s/(soversion = 12)([^032]|$$)/soversion = 12032/g" meson.build
+    PATCH_COMMAND sed -E -i -e "s/(soversion = 13)([^032]|$$)/soversion = 13032/g" meson.build
     CONFIGURE_COMMAND meson setup build --buildtype=${BUILDTYPE_LOWER} -Dwerror=false -Dexamples=false -Drenderers=gles2 $<IF:$<BOOL:${WITH_ASAN}>,-Db_sanitize=address,-Db_sanitize=none> && meson setup build --buildtype=${BUILDTYPE_LOWER} -Dwerror=false -Dexamples=false -Drenderers=gles2 $<IF:$<BOOL:${WITH_ASAN}>,-Db_sanitize=address,-Db_sanitize=none> --reconfigure
     BUILD_COMMAND ninja -C build
     BUILD_ALWAYS true
     BUILD_IN_SOURCE true
-    BUILD_BYPRODUCTS ${CMAKE_SOURCE_DIR}/subprojects/wlroots/build/libwlroots.so.12032
+    BUILD_BYPRODUCTS ${CMAKE_SOURCE_DIR}/subprojects/wlroots/build/libwlroots.so.13032
     INSTALL_COMMAND echo "wlroots: install not needed"
 )
 
@@ -222,7 +222,7 @@ function(protocol protoPath protoName external)
 endfunction()
 
 target_link_libraries(Hyprland
-        ${CMAKE_SOURCE_DIR}/subprojects/wlroots/build/libwlroots.so.12032 # wlroots is provided by us
+        ${CMAKE_SOURCE_DIR}/subprojects/wlroots/build/libwlroots.so.13032 # wlroots is provided by us
         OpenGL::EGL
         OpenGL::GL
         Threads::Threads
diff --git a/Makefile b/Makefile
index dc7f7329..0c0b9d10 100644
--- a/Makefile
+++ b/Makefile
@@ -50,7 +50,7 @@ install:
 	install -m644 ./docs/*.1 ${PREFIX}/share/man/man1
 
 	mkdir -p ${PREFIX}/lib/
-	cp ./subprojects/wlroots/build/libwlroots.so.12032 ${PREFIX}/lib/
+	cp ./subprojects/wlroots/build/libwlroots.so.13032 ${PREFIX}/lib/
 
 	$(MAKE) installheaders
 
@@ -58,7 +58,7 @@ uninstall:
 	rm -f ${PREFIX}/share/wayland-sessions/hyprland.desktop
 	rm -f ${PREFIX}/bin/Hyprland
 	rm -f ${PREFIX}/bin/hyprctl
-	rm -f ${PREFIX}/lib/libwlroots.so.12032
+	rm -f ${PREFIX}/lib/libwlroots.so.13032
 	rm -rf ${PREFIX}/share/hyprland
 	rm -f ${PREFIX}/share/man/man1/Hyprland.1
 	rm -f ${PREFIX}/share/man/man1/hyprctl.1
diff --git a/flake.lock b/flake.lock
index fcb03328..3ec705bf 100644
--- a/flake.lock
+++ b/flake.lock
@@ -25,11 +25,11 @@
     },
     "nixpkgs": {
       "locked": {
-        "lastModified": 1698134075,
-        "narHash": "sha256-foCD+nuKzfh49bIoiCBur4+Fx1nozo+4C/6k8BYk4sg=",
+        "lastModified": 1700612854,
+        "narHash": "sha256-yrQ8osMD+vDLGFX7pcwsY/Qr5PUd6OmDMYJZzZi0+zc=",
         "owner": "NixOS",
         "repo": "nixpkgs",
-        "rev": "8efd5d1e283604f75a808a20e6cde0ef313d07d4",
+        "rev": "19cbff58383a4ae384dea4d1d0c823d72b49d614",
         "type": "github"
       },
       "original": {
@@ -67,18 +67,18 @@
       "flake": false,
       "locked": {
         "host": "gitlab.freedesktop.org",
-        "lastModified": 1699292815,
-        "narHash": "sha256-HXu98PyBMKEWLqiTb8viuLDznud/SdkdJsx5A5CWx7I=",
+        "lastModified": 1700736101,
+        "narHash": "sha256-1Fh1xf/JX5zFbGIF9LDaffaleG6JDwwwnKby0LyiXEA=",
         "owner": "wlroots",
         "repo": "wlroots",
-        "rev": "5de9e1a99d6642c2d09d589aa37ff0a8945dcee1",
+        "rev": "f1762f428b0ef2989c81f57ea9e810403d34d946",
         "type": "gitlab"
       },
       "original": {
         "host": "gitlab.freedesktop.org",
         "owner": "wlroots",
         "repo": "wlroots",
-        "rev": "5de9e1a99d6642c2d09d589aa37ff0a8945dcee1",
+        "rev": "f1762f428b0ef2989c81f57ea9e810403d34d946",
         "type": "gitlab"
       }
     },
@@ -95,11 +95,11 @@
         ]
       },
       "locked": {
-        "lastModified": 1697981233,
-        "narHash": "sha256-y8q4XUwx+gVK7i2eLjfR32lVo7TYvEslyzrmzYEaPZU=",
+        "lastModified": 1700508250,
+        "narHash": "sha256-X4o/mifI7Nhu0UKYlxx53wIC+gYDo3pVM9L2u3PE2bE=",
         "owner": "hyprwm",
         "repo": "xdg-desktop-portal-hyprland",
-        "rev": "22e7a65ff9633e1dedfa5317fdffc49f68de2ff2",
+        "rev": "eb120ff25265ecacd0fc13d7dab12131b60d0f47",
         "type": "github"
       },
       "original": {
diff --git a/flake.nix b/flake.nix
index f2602105..716648be 100644
--- a/flake.nix
+++ b/flake.nix
@@ -12,7 +12,7 @@
       host = "gitlab.freedesktop.org";
       owner = "wlroots";
       repo = "wlroots";
-      rev = "5de9e1a99d6642c2d09d589aa37ff0a8945dcee1";
+      rev = "f1762f428b0ef2989c81f57ea9e810403d34d946";
       flake = false;
     };
 
diff --git a/src/Compositor.cpp b/src/Compositor.cpp
index f5e3c16d..dec83e4c 100644
--- a/src/Compositor.cpp
+++ b/src/Compositor.cpp
@@ -225,7 +225,6 @@ void CCompositor::initServer() {
 
     m_sWLROutputMgr = wlr_output_manager_v1_create(m_sWLDisplay);
 
-    m_sWLRInhibitMgr     = wlr_input_inhibit_manager_create(m_sWLDisplay);
     m_sWLRKbShInhibitMgr = wlr_keyboard_shortcuts_inhibit_v1_create(m_sWLDisplay);
 
     m_sWLRPointerConstraints = wlr_pointer_constraints_v1_create(m_sWLDisplay);
@@ -283,7 +282,7 @@ void CCompositor::initServer() {
 
 void CCompositor::initAllSignals() {
     addWLSignal(&m_sWLRBackend->events.new_output, &Events::listen_newOutput, m_sWLRBackend, "Backend");
-    addWLSignal(&m_sWLRXDGShell->events.new_surface, &Events::listen_newXDGSurface, m_sWLRXDGShell, "XDG Shell");
+    addWLSignal(&m_sWLRXDGShell->events.new_surface, &Events::listen_newXDGToplevel, m_sWLRXDGShell, "XDG Shell");
     addWLSignal(&m_sWLRCursor->events.motion, &Events::listen_mouseMove, m_sWLRCursor, "WLRCursor");
     addWLSignal(&m_sWLRCursor->events.motion_absolute, &Events::listen_mouseMoveAbsolute, m_sWLRCursor, "WLRCursor");
     addWLSignal(&m_sWLRCursor->events.button, &Events::listen_mouseButton, m_sWLRCursor, "WLRCursor");
@@ -312,8 +311,6 @@ void CCompositor::initAllSignals() {
     addWLSignal(&m_sWLROutputLayout->events.change, &Events::listen_change, m_sWLROutputLayout, "OutputLayout");
     addWLSignal(&m_sWLROutputMgr->events.apply, &Events::listen_outputMgrApply, m_sWLROutputMgr, "OutputMgr");
     addWLSignal(&m_sWLROutputMgr->events.test, &Events::listen_outputMgrTest, m_sWLROutputMgr, "OutputMgr");
-    addWLSignal(&m_sWLRInhibitMgr->events.activate, &Events::listen_InhibitActivate, m_sWLRInhibitMgr, "InhibitMgr");
-    addWLSignal(&m_sWLRInhibitMgr->events.deactivate, &Events::listen_InhibitDeactivate, m_sWLRInhibitMgr, "InhibitMgr");
     addWLSignal(&m_sWLRPointerConstraints->events.new_constraint, &Events::listen_newConstraint, m_sWLRPointerConstraints, "PointerConstraints");
     addWLSignal(&m_sWLRXDGDecoMgr->events.new_toplevel_decoration, &Events::listen_NewXDGDeco, m_sWLRXDGDecoMgr, "XDGDecoMgr");
     addWLSignal(&m_sWLRVirtPtrMgr->events.new_virtual_pointer, &Events::listen_newVirtPtr, m_sWLRVirtPtrMgr, "VirtPtrMgr");
diff --git a/src/Compositor.hpp b/src/Compositor.hpp
index 70559652..f7c5ca7c 100644
--- a/src/Compositor.hpp
+++ b/src/Compositor.hpp
@@ -61,7 +61,6 @@ class CCompositor {
     wlr_virtual_keyboard_manager_v1*           m_sWLRVKeyboardMgr;
     wlr_output_manager_v1*                     m_sWLROutputMgr;
     wlr_presentation*                          m_sWLRPresentation;
-    wlr_input_inhibit_manager*                 m_sWLRInhibitMgr;
     wlr_keyboard_shortcuts_inhibit_manager_v1* m_sWLRKbShInhibitMgr;
     wlr_egl*                                   m_sWLREGL;
     int                                        m_iDRMFD;
diff --git a/src/debug/TracyDefines.hpp b/src/debug/TracyDefines.hpp
index 767b1a20..e6cccd77 100644
--- a/src/debug/TracyDefines.hpp
+++ b/src/debug/TracyDefines.hpp
@@ -13,15 +13,6 @@ inline PFNGLGETQUERYOBJECTUI64VEXTPROC glGetQueryObjectui64v;
 
 #include "../../subprojects/tracy/public/tracy/TracyOpenGL.hpp"
 
-inline void loadGLProc(void* pProc, const char* name) {
-    void* proc = (void*)eglGetProcAddress(name);
-    if (proc == NULL) {
-        Debug::log(CRIT, "[Tracy GPU Profiling] eglGetProcAddress({}) failed", name);
-        abort();
-    }
-    *(void**)pProc = proc;
-}
-
 #define TRACY_GPU_CONTEXT TracyGpuContext
 #define TRACY_GPU_ZONE(e) TracyGpuZone(e)
 #define TRACY_GPU_COLLECT TracyGpuCollect
@@ -32,4 +23,4 @@ inline void loadGLProc(void* pProc, const char* name) {
 #define TRACY_GPU_ZONE(e)
 #define TRACY_GPU_COLLECT
 
-#endif
\ No newline at end of file
+#endif
diff --git a/src/events/Events.hpp b/src/events/Events.hpp
index 371586cc..777bdf9f 100644
--- a/src/events/Events.hpp
+++ b/src/events/Events.hpp
@@ -42,7 +42,7 @@ namespace Events {
     DYNLISTENFUNC(repositionPopupXDG);
 
     // Surface XDG (window)
-    LISTENER(newXDGSurface);
+    LISTENER(newXDGToplevel);
     LISTENER(activateXDG);
 
     // Window events
@@ -121,10 +121,6 @@ namespace Events {
     DYNLISTENFUNC(destroyDragIcon);
     DYNLISTENFUNC(commitDragIcon);
 
-    // Inhibit
-    LISTENER(InhibitActivate);
-    LISTENER(InhibitDeactivate);
-
     // Deco XDG
     LISTENER(NewXDGDeco);
 
diff --git a/src/events/Misc.cpp b/src/events/Misc.cpp
index 1df4be3a..91a0ebda 100644
--- a/src/events/Misc.cpp
+++ b/src/events/Misc.cpp
@@ -156,20 +156,6 @@ void Events::listener_commitDragIcon(void* owner, void* data) {
     Debug::log(LOG, "Drag icon committed.");
 }
 
-void Events::listener_InhibitActivate(wl_listener* listener, void* data) {
-    Debug::log(LOG, "Activated exclusive for {:x}.", (uintptr_t)g_pCompositor->m_sSeat.exclusiveClient);
-
-    g_pInputManager->refocus();
-    g_pCompositor->m_sSeat.exclusiveClient = g_pCompositor->m_sWLRInhibitMgr->active_client;
-}
-
-void Events::listener_InhibitDeactivate(wl_listener* listener, void* data) {
-    Debug::log(LOG, "Deactivated exclusive.");
-
-    g_pCompositor->m_sSeat.exclusiveClient = nullptr;
-    g_pInputManager->refocus();
-}
-
 void Events::listener_RendererDestroy(wl_listener* listener, void* data) {
     Debug::log(LOG, "!!Renderer destroyed!!");
 }
diff --git a/src/events/Windows.cpp b/src/events/Windows.cpp
index 39678154..556ec1b5 100644
--- a/src/events/Windows.cpp
+++ b/src/events/Windows.cpp
@@ -1169,7 +1169,7 @@ void Events::listener_surfaceXWayland(wl_listener* listener, void* data) {
     PNEWWINDOW->hyprListener_configureX11.initCallback(&XWSURFACE->events.request_configure, &Events::listener_configureX11, PNEWWINDOW, "XWayland Window");
 }
 
-void Events::listener_newXDGSurface(wl_listener* listener, void* data) {
+void Events::listener_newXDGToplevel(wl_listener* listener, void* data) {
     // A window got opened
     const auto XDGSURFACE = (wlr_xdg_surface*)data;
 
diff --git a/src/helpers/Monitor.cpp b/src/helpers/Monitor.cpp
index 20880d8d..0286d063 100644
--- a/src/helpers/Monitor.cpp
+++ b/src/helpers/Monitor.cpp
@@ -643,3 +643,11 @@ void CMonitor::moveTo(const Vector2D& pos) {
 Vector2D CMonitor::middle() {
     return vecPosition + vecSize / 2.f;
 }
+void CMonitor::updateMatrix() {
+    wlr_matrix_identity(projMatrix.data());
+    if (transform != WL_OUTPUT_TRANSFORM_NORMAL) {
+        wlr_matrix_translate(projMatrix.data(), vecPixelSize.x / 2.0, vecPixelSize.y / 2.0);
+        wlr_matrix_transform(projMatrix.data(), transform);
+        wlr_matrix_translate(projMatrix.data(), -vecTransformedSize.x / 2.0, -vecTransformedSize.y / 2.0);
+    }
+}
diff --git a/src/helpers/Monitor.hpp b/src/helpers/Monitor.hpp
index 2aa7e7eb..f42c6cad 100644
--- a/src/helpers/Monitor.hpp
+++ b/src/helpers/Monitor.hpp
@@ -50,32 +50,33 @@ class CMonitor {
     drmModeModeInfo customDrmMode = {};
 
     // WLR stuff
-    wlr_damage_ring     damage;
-    wlr_output*         output          = nullptr;
-    float               refreshRate     = 60;
-    int                 framesToSkip    = 0;
-    int                 forceFullFrames = 0;
-    bool                noFrameSchedule = false;
-    bool                scheduledRecalc = false;
-    wl_output_transform transform       = WL_OUTPUT_TRANSFORM_NORMAL;
-    bool                gammaChanged    = false;
-    float               xwaylandScale   = 1.f;
+    wlr_damage_ring      damage;
+    wlr_output*          output          = nullptr;
+    float                refreshRate     = 60;
+    int                  framesToSkip    = 0;
+    int                  forceFullFrames = 0;
+    bool                 noFrameSchedule = false;
+    bool                 scheduledRecalc = false;
+    wl_output_transform  transform       = WL_OUTPUT_TRANSFORM_NORMAL;
+    bool                 gammaChanged    = false;
+    float                xwaylandScale   = 1.f;
+    std::array<float, 9> projMatrix      = {0};
 
-    bool                dpmsStatus       = true;
-    bool                vrrActive        = false; // this can be TRUE even if VRR is not active in the case that this display does not support it.
-    bool                enabled10bit     = false; // as above, this can be TRUE even if 10 bit failed.
-    bool                createdByUser    = false;
-    uint32_t            drmFormat        = DRM_FORMAT_INVALID;
-    bool                isUnsafeFallback = false;
+    bool                 dpmsStatus       = true;
+    bool                 vrrActive        = false; // this can be TRUE even if VRR is not active in the case that this display does not support it.
+    bool                 enabled10bit     = false; // as above, this can be TRUE even if 10 bit failed.
+    bool                 createdByUser    = false;
+    uint32_t             drmFormat        = DRM_FORMAT_INVALID;
+    bool                 isUnsafeFallback = false;
 
-    bool                pendingFrame    = false; // if we schedule a frame during rendering, reschedule it after
-    bool                renderingActive = false;
+    bool                 pendingFrame    = false; // if we schedule a frame during rendering, reschedule it after
+    bool                 renderingActive = false;
 
-    wl_event_source*    renderTimer  = nullptr; // for RAT
-    bool                RATScheduled = false;
-    CTimer              lastPresentationTimer;
+    wl_event_source*     renderTimer  = nullptr; // for RAT
+    bool                 RATScheduled = false;
+    CTimer               lastPresentationTimer;
 
-    SMonitorRule        activeMonitorRule;
+    SMonitorRule         activeMonitorRule;
 
     // mirroring
     CMonitor*              pMirrorOf = nullptr;
@@ -123,6 +124,7 @@ class CMonitor {
     void     setSpecialWorkspace(const int& id);
     void     moveTo(const Vector2D& pos);
     Vector2D middle();
+    void     updateMatrix();
 
     bool     m_bEnabled             = false;
     bool     m_bRenderingInitPassed = false;
@@ -137,3 +139,4 @@ class CMonitor {
     void setupDefaultWS(const SMonitorRule&);
     int  findAvailableDefaultWS();
 };
+
diff --git a/src/helpers/Region.cpp b/src/helpers/Region.cpp
index 06b8536c..3f9c804f 100644
--- a/src/helpers/Region.cpp
+++ b/src/helpers/Region.cpp
@@ -93,6 +93,15 @@ CRegion& CRegion::translate(const Vector2D& vec) {
     return *this;
 }
 
+CRegion& CRegion::transform(const wl_output_transform t, double w, double h) {
+    wlr_region_transform(&m_rRegion, &m_rRegion, t, w, h);
+    return *this;
+}
+
+CRegion CRegion::copy() const {
+    return CRegion(*this);
+}
+
 CRegion& CRegion::scale(float scale) {
     wlr_region_scale(&m_rRegion, &m_rRegion, scale);
     return *this;
@@ -151,4 +160,4 @@ Vector2D CRegion::closestPoint(const Vector2D& vec) const {
     }
 
     return leader;
-}
\ No newline at end of file
+}
diff --git a/src/helpers/Region.hpp b/src/helpers/Region.hpp
index 1c6923df..7554aa58 100644
--- a/src/helpers/Region.hpp
+++ b/src/helpers/Region.hpp
@@ -45,12 +45,14 @@ class CRegion {
     CRegion&                    intersect(const CRegion& other);
     CRegion&                    intersect(double x, double y, double w, double h);
     CRegion&                    translate(const Vector2D& vec);
+    CRegion&                    transform(const wl_output_transform t, double w, double h);
     CRegion&                    invert(pixman_box32_t* box);
     CRegion&                    scale(float scale);
     CBox                        getExtents();
     bool                        containsPoint(const Vector2D& vec) const;
     bool                        empty() const;
     Vector2D                    closestPoint(const Vector2D& vec) const;
+    CRegion                     copy() const;
 
     std::vector<pixman_box32_t> getRects() const;
 
diff --git a/src/includes.hpp b/src/includes.hpp
index a89f0950..611e4a5c 100644
--- a/src/includes.hpp
+++ b/src/includes.hpp
@@ -70,7 +70,6 @@ extern "C" {
 #include <wlr/types/wlr_xdg_shell.h>
 #include <wlr/types/wlr_subcompositor.h>
 #include <wlr/types/wlr_damage_ring.h>
-#include <wlr/types/wlr_input_inhibitor.h>
 #include <wlr/types/wlr_keyboard_shortcuts_inhibit_v1.h>
 #include <wlr/types/wlr_virtual_pointer_v1.h>
 #include <wlr/types/wlr_foreign_toplevel_management_v1.h>
@@ -106,6 +105,9 @@ extern "C" {
 #include <wlr/types/wlr_cursor_shape_v1.h>
 #include <wlr/types/wlr_tearing_control_v1.h>
 #include <wlr/util/box.h>
+#include <wlr/util/transform.h>
+#include <wlr/render/swapchain.h>
+#include <wlr/render/egl.h>
 
 #include <libdrm/drm_fourcc.h>
 
diff --git a/src/managers/XWaylandManager.cpp b/src/managers/XWaylandManager.cpp
index 767e19f8..6d066e54 100644
--- a/src/managers/XWaylandManager.cpp
+++ b/src/managers/XWaylandManager.cpp
@@ -49,10 +49,11 @@ void CHyprXWaylandManager::activateSurface(wlr_surface* pSurface, bool activate)
             wlr_xdg_toplevel_set_activated(PSURF->toplevel, activate);
 
     } else if (wlr_xwayland_surface_try_from_wlr_surface(pSurface)) {
-        wlr_xwayland_surface_activate(wlr_xwayland_surface_try_from_wlr_surface(pSurface), activate);
+        const auto XSURF = wlr_xwayland_surface_try_from_wlr_surface(pSurface);
+        wlr_xwayland_surface_activate(XSURF, activate);
 
-        if (activate)
-            wlr_xwayland_surface_restack(wlr_xwayland_surface_try_from_wlr_surface(pSurface), nullptr, XCB_STACK_MODE_ABOVE);
+        if (activate && !XSURF->override_redirect)
+            wlr_xwayland_surface_restack(XSURF, nullptr, XCB_STACK_MODE_ABOVE);
     }
 }
 
@@ -62,7 +63,8 @@ void CHyprXWaylandManager::activateWindow(CWindow* pWindow, bool activate) {
 
         if (activate) {
             wlr_xwayland_surface_set_minimized(pWindow->m_uSurface.xwayland, false);
-            wlr_xwayland_surface_restack(pWindow->m_uSurface.xwayland, nullptr, XCB_STACK_MODE_ABOVE);
+            if (!pWindow->m_uSurface.xwayland->override_redirect)
+                wlr_xwayland_surface_restack(pWindow->m_uSurface.xwayland, nullptr, XCB_STACK_MODE_ABOVE);
         }
 
         wlr_xwayland_surface_activate(pWindow->m_uSurface.xwayland, activate);
diff --git a/src/protocols/Screencopy.cpp b/src/protocols/Screencopy.cpp
index 12170595..e8230605 100644
--- a/src/protocols/Screencopy.cpp
+++ b/src/protocols/Screencopy.cpp
@@ -448,25 +448,20 @@ bool CScreencopyProtocolManager::copyFrameDmabuf(SScreencopyFrame* frame) {
     if (!sourceTex)
         return false;
 
-    float glMatrix[9];
-    wlr_matrix_identity(glMatrix);
-    wlr_matrix_translate(glMatrix, -frame->box.x, -frame->box.y);
-    wlr_matrix_scale(glMatrix, frame->pMonitor->vecPixelSize.x, frame->pMonitor->vecPixelSize.y);
+    CRegion fakeDamage = {0, 0, frame->box.width, frame->box.height};
 
-    if (!wlr_renderer_begin_with_buffer(g_pCompositor->m_sWLRRenderer, frame->buffer)) {
-        Debug::log(ERR, "[sc] dmabuf: Client requested a copy to a buffer that failed to pass wlr_renderer_begin_with_buffer");
-        wlr_texture_destroy(sourceTex);
+    if (!g_pHyprRenderer->beginRender(frame->pMonitor, fakeDamage, RENDER_MODE_TO_BUFFER, frame->buffer))
         return false;
-    }
 
-    float color[] = {0, 0, 0, 0};
-    wlr_renderer_clear(g_pCompositor->m_sWLRRenderer, color);
-    // TODO: use hl render methods to use damage
-    wlr_render_texture_with_matrix(g_pCompositor->m_sWLRRenderer, sourceTex, glMatrix, 1.0f);
+    CBox monbox = CBox{0, 0, frame->pMonitor->vecPixelSize.x, frame->pMonitor->vecPixelSize.y}.translate({-frame->box.x, -frame->box.y});
+    g_pHyprOpenGL->setMonitorTransformEnabled(false);
+    g_pHyprOpenGL->renderTexture(sourceTex, &monbox, 1);
+    g_pHyprOpenGL->setMonitorTransformEnabled(true);
+
+    g_pHyprRenderer->endRender();
 
     wlr_texture_destroy(sourceTex);
 
-    wlr_renderer_end(g_pCompositor->m_sWLRRenderer);
-
     return true;
 }
+
diff --git a/src/protocols/Screencopy.hpp b/src/protocols/Screencopy.hpp
index e3d1e22a..cd661222 100644
--- a/src/protocols/Screencopy.hpp
+++ b/src/protocols/Screencopy.hpp
@@ -97,4 +97,4 @@ class CScreencopyProtocolManager {
     bool                           copyFrameShm(SScreencopyFrame* frame, timespec* now);
 
     friend class CScreencopyClient;
-};
\ No newline at end of file
+};
diff --git a/src/protocols/ToplevelExport.cpp b/src/protocols/ToplevelExport.cpp
index 3f442781..760b50dc 100644
--- a/src/protocols/ToplevelExport.cpp
+++ b/src/protocols/ToplevelExport.cpp
@@ -134,7 +134,8 @@ void CToplevelExportProtocolManager::removeFrame(SScreencopyFrame* frame, bool f
     std::erase_if(m_vFramesAwaitingWrite, [&](const auto& other) { return other == frame; });
 
     wl_resource_set_user_data(frame->resource, nullptr);
-    wlr_buffer_unlock(frame->buffer);
+    if (frame->buffer && frame->buffer->n_locks > 0)
+        wlr_buffer_unlock(frame->buffer);
     removeClient(frame->client, force);
     m_lFrames.remove(*frame);
 }
@@ -362,18 +363,14 @@ bool CToplevelExportProtocolManager::copyFrameShm(SScreencopyFrame* frame, times
     const auto PMONITOR = g_pCompositor->getMonitorFromID(frame->pWindow->m_iMonitorID);
     CRegion    fakeDamage{0, 0, PMONITOR->vecPixelSize.x * 10, PMONITOR->vecPixelSize.y * 10};
 
-    if (frame->overlayCursor)
-        wlr_output_lock_software_cursors(PMONITOR->output, true);
-
-    if (!wlr_output_attach_render(PMONITOR->output, nullptr)) {
-        Debug::log(ERR, "[toplevel_export] Couldn't attach render");
+    if (!g_pHyprRenderer->beginRender(PMONITOR, fakeDamage, RENDER_MODE_FULL_FAKE)) {
         wlr_buffer_end_data_ptr_access(frame->buffer);
-        if (frame->overlayCursor)
-            wlr_output_lock_software_cursors(PMONITOR->output, false);
         return false;
     }
 
-    g_pHyprOpenGL->begin(PMONITOR, &fakeDamage, true);
+    if (frame->overlayCursor)
+        wlr_output_lock_software_cursors(PMONITOR->output, true);
+
     g_pHyprOpenGL->clear(CColor(0, 0, 0, 1.0));
 
     // render client at 0,0
@@ -381,27 +378,8 @@ bool CToplevelExportProtocolManager::copyFrameShm(SScreencopyFrame* frame, times
     g_pHyprRenderer->renderWindow(frame->pWindow, PMONITOR, now, false, RENDER_PASS_ALL, true, true);
     g_pHyprRenderer->m_bBlockSurfaceFeedback = false;
 
-    if (frame->overlayCursor && wlr_renderer_begin(g_pCompositor->m_sWLRRenderer, PMONITOR->vecPixelSize.x, PMONITOR->vecPixelSize.y)) {
-        // hack le massive
-        wlr_output_cursor* cursor;
-        const auto         OFFSET = frame->pWindow->m_vRealPosition.vec() - PMONITOR->vecPosition;
-        wl_list_for_each(cursor, &PMONITOR->output->cursors, link) {
-            if (!cursor->enabled || !cursor->visible || PMONITOR->output->hardware_cursor == cursor) {
-                continue;
-            }
-            cursor->x -= OFFSET.x;
-            cursor->y -= OFFSET.y;
-        }
-        wlr_output_render_software_cursors(PMONITOR->output, NULL);
-        wl_list_for_each(cursor, &PMONITOR->output->cursors, link) {
-            if (!cursor->enabled || !cursor->visible || PMONITOR->output->hardware_cursor == cursor) {
-                continue;
-            }
-            cursor->x += OFFSET.x;
-            cursor->y += OFFSET.y;
-        }
-        wlr_renderer_end(g_pCompositor->m_sWLRRenderer);
-    }
+    if (frame->overlayCursor)
+        g_pHyprRenderer->renderSoftwareCursors(PMONITOR, fakeDamage, g_pInputManager->getMouseCoordsInternal() - frame->pWindow->m_vRealPosition.vec());
 
     // copy pixels
     const auto PFORMAT = gles2FromDRM(format);
@@ -414,13 +392,10 @@ bool CToplevelExportProtocolManager::copyFrameShm(SScreencopyFrame* frame, times
         return false;
     }
 
-    glBindFramebuffer(GL_FRAMEBUFFER, g_pHyprOpenGL->m_RenderData.pCurrentMonData->primaryFB.m_iFb);
-
+    g_pHyprOpenGL->m_RenderData.mainFB->bind();
     glReadPixels(0, 0, frame->box.width, frame->box.height, PFORMAT->gl_format, PFORMAT->gl_type, data);
 
-    g_pHyprOpenGL->end();
-
-    wlr_output_rollback(PMONITOR->output);
+    g_pHyprRenderer->endRender();
 
     wlr_buffer_end_data_ptr_access(frame->buffer);
 
@@ -431,14 +406,12 @@ bool CToplevelExportProtocolManager::copyFrameShm(SScreencopyFrame* frame, times
 }
 
 bool CToplevelExportProtocolManager::copyFrameDmabuf(SScreencopyFrame* frame, timespec* now) {
-    if (!wlr_renderer_begin_with_buffer(g_pCompositor->m_sWLRRenderer, frame->buffer))
-        return false;
-
     const auto PMONITOR = g_pCompositor->getMonitorFromID(frame->pWindow->m_iMonitorID);
 
     CRegion    fakeDamage{0, 0, INT16_MAX, INT16_MAX};
 
-    g_pHyprOpenGL->begin(PMONITOR, &fakeDamage, true);
+    if (!g_pHyprRenderer->beginRender(PMONITOR, fakeDamage, RENDER_MODE_TO_BUFFER, frame->buffer))
+        return false;
 
     g_pHyprOpenGL->clear(CColor(0, 0, 0, 1.0));
 
@@ -446,14 +419,10 @@ bool CToplevelExportProtocolManager::copyFrameDmabuf(SScreencopyFrame* frame, ti
     g_pHyprRenderer->renderWindow(frame->pWindow, PMONITOR, now, false, RENDER_PASS_ALL, true, true);
     g_pHyprRenderer->m_bBlockSurfaceFeedback = false;
 
-    g_pHyprOpenGL->bindWlrOutputFb();
+    if (frame->overlayCursor)
+        g_pHyprRenderer->renderSoftwareCursors(PMONITOR, fakeDamage, g_pInputManager->getMouseCoordsInternal() - frame->pWindow->m_vRealPosition.vec());
 
-    CBox monbox = {0, 0, PMONITOR->vecPixelSize.x, PMONITOR->vecPixelSize.y};
-    g_pHyprOpenGL->renderTexture(g_pHyprOpenGL->m_RenderData.pCurrentMonData->primaryFB.m_cTex, &monbox, 1.f);
-
-    g_pHyprOpenGL->end();
-
-    wlr_renderer_end(g_pCompositor->m_sWLRRenderer);
+    g_pHyprRenderer->endRender();
     return true;
 }
 
diff --git a/src/render/Framebuffer.cpp b/src/render/Framebuffer.cpp
index 1ebbc585..a9da9887 100644
--- a/src/render/Framebuffer.cpp
+++ b/src/render/Framebuffer.cpp
@@ -62,6 +62,21 @@ bool CFramebuffer::alloc(int w, int h, uint32_t drmFormat) {
     return true;
 }
 
+void CFramebuffer::addStencil() {
+    glBindTexture(GL_TEXTURE_2D, m_pStencilTex->m_iTexID);
+    glTexImage2D(GL_TEXTURE_2D, 0, GL_DEPTH24_STENCIL8, m_vSize.x, m_vSize.y, 0, GL_DEPTH_STENCIL, GL_UNSIGNED_INT_24_8, 0);
+
+    glBindFramebuffer(GL_FRAMEBUFFER, m_iFb);
+
+    glFramebufferTexture2D(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_TEXTURE_2D, m_pStencilTex->m_iTexID, 0);
+
+    auto status = glCheckFramebufferStatus(GL_FRAMEBUFFER);
+    RASSERT((status == GL_FRAMEBUFFER_COMPLETE), "Failed adding a stencil to fbo!", status);
+
+    glBindTexture(GL_TEXTURE_2D, 0);
+    glBindFramebuffer(GL_FRAMEBUFFER, g_pHyprOpenGL->m_iCurrentOutputFb);
+}
+
 void CFramebuffer::bind() {
 #ifndef GLES2
     glBindFramebuffer(GL_DRAW_FRAMEBUFFER, m_iFb);
@@ -89,4 +104,4 @@ CFramebuffer::~CFramebuffer() {
 
 bool CFramebuffer::isAllocated() {
     return m_iFb != (GLuint)-1;
-}
\ No newline at end of file
+}
diff --git a/src/render/Framebuffer.hpp b/src/render/Framebuffer.hpp
index 73b70431..1edeb11d 100644
--- a/src/render/Framebuffer.hpp
+++ b/src/render/Framebuffer.hpp
@@ -8,6 +8,7 @@ class CFramebuffer {
     ~CFramebuffer();
 
     bool      alloc(int w, int h, uint32_t format = GL_RGBA);
+    void      addStencil();
     void      bind();
     void      release();
     void      reset();
@@ -19,4 +20,4 @@ class CFramebuffer {
     GLuint    m_iFb = -1;
 
     CTexture* m_pStencilTex = nullptr;
-};
\ No newline at end of file
+};
diff --git a/src/render/OpenGL.cpp b/src/render/OpenGL.cpp
index 7cfe2c53..34b37f60 100644
--- a/src/render/OpenGL.cpp
+++ b/src/render/OpenGL.cpp
@@ -5,6 +5,15 @@
 #include "Shaders.hpp"
 #include <random>
 
+inline void loadGLProc(void* pProc, const char* name) {
+    void* proc = (void*)eglGetProcAddress(name);
+    if (proc == NULL) {
+        Debug::log(CRIT, "[Tracy GPU Profiling] eglGetProcAddress({}) failed", name);
+        abort();
+    }
+    *(void**)pProc = proc;
+}
+
 CHyprOpenGLImpl::CHyprOpenGLImpl() {
     RASSERT(eglMakeCurrent(wlr_egl_get_display(g_pCompositor->m_sWLREGL), EGL_NO_SURFACE, EGL_NO_SURFACE, wlr_egl_get_context(g_pCompositor->m_sWLREGL)),
             "Couldn't unset current EGL!");
@@ -23,6 +32,9 @@ CHyprOpenGLImpl::CHyprOpenGLImpl() {
     Debug::log(LOG, "Renderer: {}", (char*)glGetString(GL_RENDERER));
     Debug::log(LOG, "Supported extensions size: {}", std::count(m_szExtensions.begin(), m_szExtensions.end(), ' '));
 
+    loadGLProc(&m_sProc.glEGLImageTargetRenderbufferStorageOES, "glEGLImageTargetRenderbufferStorageOES");
+    loadGLProc(&m_sProc.eglDestroyImageKHR, "eglDestroyImageKHR");
+
 #ifdef USE_TRACY_GPU
 
     loadGLProc(&glQueryCounter, "glQueryCounterEXT");
@@ -103,34 +115,69 @@ GLuint CHyprOpenGLImpl::compileShader(const GLuint& type, std::string src, bool
     return shader;
 }
 
+bool CHyprOpenGLImpl::passRequiresIntrospection(CMonitor* pMonitor) {
+    // passes requiring introspection are the ones that need to render blur.
+
+    static auto* const PBLUR = &g_pConfigManager->getConfigValuePtr("decoration:blur:enabled")->intValue;
+    static auto* const PXRAY = &g_pConfigManager->getConfigValuePtr("decoration:blur:xray")->intValue;
+
+    if (*PBLUR == 0)
+        return false;
+
+    if (m_RenderData.pCurrentMonData->blurFBShouldRender)
+        return true;
+
+    if (pMonitor->solitaryClient)
+        return false;
+
+    for (auto& ls : pMonitor->m_aLayerSurfaceLayers[ZWLR_LAYER_SHELL_V1_LAYER_TOP]) {
+        if (ls->forceBlur && !ls->xray)
+            return true;
+    }
+
+    if (*PXRAY)
+        return false;
+
+    for (auto& w : g_pCompositor->m_vWindows) {
+        if (!w->m_bIsMapped || w->isHidden() || (!w->m_bIsFloating && !g_pCompositor->isWorkspaceSpecial(w->m_iWorkspaceID)))
+            continue;
+
+        if (!g_pHyprRenderer->shouldRenderWindow(w.get()))
+            continue;
+
+        if (w->m_sAdditionalConfigData.forceNoBlur.toUnderlying() == true || w->m_sAdditionalConfigData.xray.toUnderlying() == true)
+            continue;
+
+        if (w->opaque())
+            continue;
+
+        return true;
+    }
+
+    return false;
+}
+
 void CHyprOpenGLImpl::begin(CMonitor* pMonitor, CRegion* pDamage, bool fake) {
     m_RenderData.pMonitor = pMonitor;
 
     TRACY_GPU_ZONE("RenderBegin");
 
-    if (eglGetCurrentContext() != wlr_egl_get_context(g_pCompositor->m_sWLREGL)) {
-        eglMakeCurrent(wlr_egl_get_display(g_pCompositor->m_sWLREGL), EGL_NO_SURFACE, EGL_NO_SURFACE, wlr_egl_get_context(g_pCompositor->m_sWLREGL));
-    }
-
     glViewport(0, 0, pMonitor->vecPixelSize.x, pMonitor->vecPixelSize.y);
 
     matrixProjection(m_RenderData.projection, pMonitor->vecPixelSize.x, pMonitor->vecPixelSize.y, WL_OUTPUT_TRANSFORM_NORMAL);
 
     m_RenderData.pCurrentMonData = &m_mMonitorRenderResources[pMonitor];
 
-    glGetIntegerv(GL_FRAMEBUFFER_BINDING, &m_iCurrentOutputFb);
-    m_iWLROutputFb = m_iCurrentOutputFb;
-
     // ensure a framebuffer for the monitor exists
-    if (!m_mMonitorRenderResources.contains(pMonitor) || m_RenderData.pCurrentMonData->primaryFB.m_vSize != pMonitor->vecPixelSize) {
+    if (!m_mMonitorRenderResources.contains(pMonitor) || m_RenderData.pCurrentMonData->offloadFB.m_vSize != pMonitor->vecPixelSize) {
         m_RenderData.pCurrentMonData->stencilTex.allocate();
 
-        m_RenderData.pCurrentMonData->primaryFB.m_pStencilTex    = &m_RenderData.pCurrentMonData->stencilTex;
+        m_RenderData.pCurrentMonData->offloadFB.m_pStencilTex    = &m_RenderData.pCurrentMonData->stencilTex;
         m_RenderData.pCurrentMonData->mirrorFB.m_pStencilTex     = &m_RenderData.pCurrentMonData->stencilTex;
         m_RenderData.pCurrentMonData->mirrorSwapFB.m_pStencilTex = &m_RenderData.pCurrentMonData->stencilTex;
         m_RenderData.pCurrentMonData->offMainFB.m_pStencilTex    = &m_RenderData.pCurrentMonData->stencilTex;
 
-        m_RenderData.pCurrentMonData->primaryFB.alloc(pMonitor->vecPixelSize.x, pMonitor->vecPixelSize.y, pMonitor->drmFormat);
+        m_RenderData.pCurrentMonData->offloadFB.alloc(pMonitor->vecPixelSize.x, pMonitor->vecPixelSize.y, pMonitor->drmFormat);
         m_RenderData.pCurrentMonData->mirrorFB.alloc(pMonitor->vecPixelSize.x, pMonitor->vecPixelSize.y, pMonitor->drmFormat);
         m_RenderData.pCurrentMonData->mirrorSwapFB.alloc(pMonitor->vecPixelSize.x, pMonitor->vecPixelSize.y, pMonitor->drmFormat);
         m_RenderData.pCurrentMonData->offMainFB.alloc(pMonitor->vecPixelSize.x, pMonitor->vecPixelSize.y, pMonitor->drmFormat);
@@ -144,19 +191,37 @@ void CHyprOpenGLImpl::begin(CMonitor* pMonitor, CRegion* pDamage, bool fake) {
     if (!m_RenderData.pCurrentMonData->m_bShadersInitialized)
         initShaders();
 
-    // bind the primary Hypr Framebuffer
-    m_RenderData.pCurrentMonData->primaryFB.bind();
-
     m_RenderData.damage.set(*pDamage);
 
     m_bFakeFrame = fake;
 
-    m_RenderData.currentFB = &m_RenderData.pCurrentMonData->primaryFB;
-
     if (m_bReloadScreenShader) {
         m_bReloadScreenShader = false;
         applyScreenShader(g_pConfigManager->getString("decoration:screen_shader"));
     }
+
+    const auto PRBO = g_pHyprRenderer->getCurrentRBO();
+
+    if (m_sFinalScreenShader.program > 0 || m_bFakeFrame || m_RenderData.mouseZoomFactor != 1.0 || pMonitor->vecPixelSize != PRBO->getFB()->m_vSize ||
+        passRequiresIntrospection(pMonitor)) {
+        // we have to offload
+        // bind the primary Hypr Framebuffer
+        m_RenderData.pCurrentMonData->offloadFB.bind();
+        m_RenderData.currentFB  = &m_RenderData.pCurrentMonData->offloadFB;
+        m_bOffloadedFramebuffer = true;
+    } else {
+        // we can render to the rbo directly
+        const auto PFBO        = PRBO->getFB();
+        m_RenderData.currentFB = PFBO;
+        if (PFBO->m_pStencilTex != &m_RenderData.pCurrentMonData->stencilTex) {
+            PFBO->m_pStencilTex = &m_RenderData.pCurrentMonData->stencilTex;
+            PFBO->addStencil();
+        }
+        PRBO->bindFB();
+        m_bOffloadedFramebuffer = false;
+    }
+
+    m_RenderData.mainFB = m_RenderData.currentFB;
 }
 
 void CHyprOpenGLImpl::end() {
@@ -164,14 +229,15 @@ void CHyprOpenGLImpl::end() {
 
     TRACY_GPU_ZONE("RenderEnd");
 
+    if (!m_RenderData.pMonitor->mirrors.empty())
+        saveBufferForMirror(); // save with original damage region
+
     // end the render, copy the data to the WLR framebuffer
-    if (!m_bFakeFrame) {
+    if (m_bOffloadedFramebuffer && !m_bFakeFrame) {
         m_RenderData.damage = m_RenderData.pMonitor->lastFrameDamage;
 
-        if (!m_RenderData.pMonitor->mirrors.empty())
-            saveBufferForMirror(); // save with original damage region
+        g_pHyprRenderer->getCurrentRBO()->bindFB();
 
-        glBindFramebuffer(GL_FRAMEBUFFER, m_iWLROutputFb);
         CBox monbox = {0, 0, m_RenderData.pMonitor->vecTransformedSize.x, m_RenderData.pMonitor->vecTransformedSize.y};
 
         if (m_RenderData.mouseZoomFactor != 1.f) {
@@ -199,9 +265,9 @@ void CHyprOpenGLImpl::end() {
         blend(false);
 
         if (m_sFinalScreenShader.program < 1)
-            renderTexturePrimitive(m_RenderData.pCurrentMonData->primaryFB.m_cTex, &monbox);
+            renderTexturePrimitive(m_RenderData.pCurrentMonData->offloadFB.m_cTex, &monbox);
         else
-            renderTexture(m_RenderData.pCurrentMonData->primaryFB.m_cTex, &monbox, 1.f);
+            renderTexture(m_RenderData.pCurrentMonData->offloadFB.m_cTex, &monbox, 1.f);
 
         blend(true);
 
@@ -212,15 +278,10 @@ void CHyprOpenGLImpl::end() {
 
     // reset our data
     m_RenderData.pMonitor          = nullptr;
-    m_iWLROutputFb                 = 0;
     m_RenderData.mouseZoomFactor   = 1.f;
     m_RenderData.mouseZoomUseMouse = true;
 }
 
-void CHyprOpenGLImpl::bindWlrOutputFb() {
-    glBindFramebuffer(GL_FRAMEBUFFER, m_iWLROutputFb);
-}
-
 void CHyprOpenGLImpl::initShaders() {
     GLuint prog                                      = createProgram(QUADVERTSRC, QUADFRAGSRC);
     m_RenderData.pCurrentMonData->m_shQUAD.program   = prog;
@@ -501,8 +562,7 @@ void CHyprOpenGLImpl::renderRectWithBlur(CBox* box, const CColor& col, int round
 
     CFramebuffer* POUTFB = blurMainFramebufferWithDamage(blurA, &damage);
 
-    // bind primary
-    m_RenderData.pCurrentMonData->primaryFB.bind();
+    m_RenderData.currentFB->bind();
 
     // make a stencil for rounded corners to work with blur
     scissor((CBox*)nullptr); // allow the entire window and stencil to render
@@ -553,7 +613,7 @@ void CHyprOpenGLImpl::renderRectWithDamage(CBox* box, const CColor& col, CRegion
 
     float matrix[9];
     wlr_matrix_project_box(matrix, box->pWlr(), wlr_output_transform_invert(!m_bEndFrame ? WL_OUTPUT_TRANSFORM_NORMAL : m_RenderData.pMonitor->transform), 0,
-                           m_RenderData.pMonitor->output->transform_matrix); // TODO: write own, don't use WLR here
+                           m_RenderData.pMonitor->projMatrix.data()); // TODO: write own, don't use WLR here
 
     float glMatrix[9];
     wlr_matrix_multiply(glMatrix, m_RenderData.projection, matrix);
@@ -640,7 +700,7 @@ void CHyprOpenGLImpl::renderTextureInternalWithDamage(const CTexture& tex, CBox*
     // get transform
     const auto TRANSFORM = wlr_output_transform_invert(!m_bEndFrame ? WL_OUTPUT_TRANSFORM_NORMAL : m_RenderData.pMonitor->transform);
     float      matrix[9];
-    wlr_matrix_project_box(matrix, newBox.pWlr(), TRANSFORM, 0, m_RenderData.pMonitor->output->transform_matrix);
+    wlr_matrix_project_box(matrix, newBox.pWlr(), TRANSFORM, 0, m_RenderData.pMonitor->projMatrix.data());
 
     float glMatrix[9];
     wlr_matrix_multiply(glMatrix, m_RenderData.projection, matrix);
@@ -801,7 +861,7 @@ void CHyprOpenGLImpl::renderTexturePrimitive(const CTexture& tex, CBox* pBox) {
     // get transform
     const auto TRANSFORM = wlr_output_transform_invert(!m_bEndFrame ? WL_OUTPUT_TRANSFORM_NORMAL : m_RenderData.pMonitor->transform);
     float      matrix[9];
-    wlr_matrix_project_box(matrix, newBox.pWlr(), TRANSFORM, 0, m_RenderData.pMonitor->output->transform_matrix);
+    wlr_matrix_project_box(matrix, newBox.pWlr(), TRANSFORM, 0, m_RenderData.pMonitor->projMatrix.data());
 
     float glMatrix[9];
     wlr_matrix_multiply(glMatrix, m_RenderData.projection, matrix);
@@ -855,7 +915,7 @@ void CHyprOpenGLImpl::renderTextureMatte(const CTexture& tex, CBox* pBox, CFrame
     // get transform
     const auto TRANSFORM = wlr_output_transform_invert(!m_bEndFrame ? WL_OUTPUT_TRANSFORM_NORMAL : m_RenderData.pMonitor->transform);
     float      matrix[9];
-    wlr_matrix_project_box(matrix, newBox.pWlr(), TRANSFORM, 0, m_RenderData.pMonitor->output->transform_matrix);
+    wlr_matrix_project_box(matrix, newBox.pWlr(), TRANSFORM, 0, m_RenderData.pMonitor->projMatrix.data());
 
     float glMatrix[9];
     wlr_matrix_multiply(glMatrix, m_RenderData.projection, matrix);
@@ -914,7 +974,7 @@ CFramebuffer* CHyprOpenGLImpl::blurMainFramebufferWithDamage(float a, CRegion* o
     const auto TRANSFORM = wlr_output_transform_invert(m_RenderData.pMonitor->transform);
     float      matrix[9];
     CBox       MONITORBOX = {0, 0, m_RenderData.pMonitor->vecTransformedSize.x, m_RenderData.pMonitor->vecTransformedSize.y};
-    wlr_matrix_project_box(matrix, MONITORBOX.pWlr(), TRANSFORM, 0, m_RenderData.pMonitor->output->transform_matrix);
+    wlr_matrix_project_box(matrix, MONITORBOX.pWlr(), TRANSFORM, 0, m_RenderData.pMonitor->projMatrix.data());
 
     float glMatrix[9];
     wlr_matrix_multiply(glMatrix, m_RenderData.projection, matrix);
@@ -947,9 +1007,9 @@ CFramebuffer* CHyprOpenGLImpl::blurMainFramebufferWithDamage(float a, CRegion* o
 
         glActiveTexture(GL_TEXTURE0);
 
-        glBindTexture(m_RenderData.pCurrentMonData->primaryFB.m_cTex.m_iTarget, m_RenderData.pCurrentMonData->primaryFB.m_cTex.m_iTexID);
+        glBindTexture(m_RenderData.currentFB->m_cTex.m_iTarget, m_RenderData.currentFB->m_cTex.m_iTexID);
 
-        glTexParameteri(m_RenderData.pCurrentMonData->primaryFB.m_cTex.m_iTarget, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
+        glTexParameteri(m_RenderData.currentFB->m_cTex.m_iTarget, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
 
         glUseProgram(m_RenderData.pCurrentMonData->m_shBLURPREPARE.program);
 
@@ -1225,7 +1285,7 @@ void CHyprOpenGLImpl::preBlurForCurrentMonitor() {
     renderTextureInternalWithDamage(POUTFB->m_cTex, &wholeMonitor, 1, &fakeDamage, 0, false, true, false);
     m_bEndFrame = false;
 
-    m_RenderData.pCurrentMonData->primaryFB.bind();
+    m_RenderData.currentFB->bind();
 
     m_RenderData.pCurrentMonData->blurFBDirty = false;
 
@@ -1294,7 +1354,8 @@ void CHyprOpenGLImpl::renderTextureWithBlur(const CTexture& tex, CBox* pBox, flo
 
     // amazing hack: the surface has an opaque region!
     CRegion inverseOpaque;
-    if (a >= 1.f) {
+    if (a >= 1.f && std::round(pSurface->current.width * m_RenderData.pMonitor->scale) == pBox->w &&
+        std::round(pSurface->current.height * m_RenderData.pMonitor->scale) == pBox->h) {
         pixman_box32_t surfbox = {0, 0, pSurface->current.width * pSurface->current.scale, pSurface->current.height * pSurface->current.scale};
         inverseOpaque          = &pSurface->current.opaque;
         inverseOpaque.invert(&surfbox).intersect(0, 0, pSurface->current.width * pSurface->current.scale, pSurface->current.height * pSurface->current.scale);
@@ -1321,8 +1382,7 @@ void CHyprOpenGLImpl::renderTextureWithBlur(const CTexture& tex, CBox* pBox, flo
         POUTFB = &m_RenderData.pCurrentMonData->blurFB;
     }
 
-    // bind primary
-    m_RenderData.pCurrentMonData->primaryFB.bind();
+    m_RenderData.currentFB->bind();
 
     // make a stencil for rounded corners to work with blur
     scissor((CBox*)nullptr); // allow the entire window and stencil to render
@@ -1404,7 +1464,7 @@ void CHyprOpenGLImpl::renderBorder(CBox* box, const CGradientValueData& grad, in
 
     float matrix[9];
     wlr_matrix_project_box(matrix, box->pWlr(), wlr_output_transform_invert(!m_bEndFrame ? WL_OUTPUT_TRANSFORM_NORMAL : m_RenderData.pMonitor->transform), 0,
-                           m_RenderData.pMonitor->output->transform_matrix); // TODO: write own, don't use WLR here
+                           m_RenderData.pMonitor->projMatrix.data()); // TODO: write own, don't use WLR here
 
     float glMatrix[9];
     wlr_matrix_multiply(glMatrix, m_RenderData.projection, matrix);
@@ -1744,7 +1804,7 @@ void CHyprOpenGLImpl::renderRoundedShadow(CBox* box, int round, int range, const
 
     float              matrix[9];
     wlr_matrix_project_box(matrix, box->pWlr(), wlr_output_transform_invert(!m_bEndFrame ? WL_OUTPUT_TRANSFORM_NORMAL : m_RenderData.pMonitor->transform), 0,
-                           m_RenderData.pMonitor->output->transform_matrix); // TODO: write own, don't use WLR here
+                           m_RenderData.pMonitor->projMatrix.data()); // TODO: write own, don't use WLR here
 
     float glMatrix[9];
     wlr_matrix_multiply(glMatrix, m_RenderData.projection, matrix);
@@ -1811,11 +1871,11 @@ void CHyprOpenGLImpl::saveBufferForMirror() {
 
     blend(false);
 
-    renderTexture(m_RenderData.pCurrentMonData->primaryFB.m_cTex, &monbox, 1.f, 0, false, false);
+    renderTexture(m_RenderData.currentFB->m_cTex, &monbox, 1.f, 0, false, false);
 
     blend(true);
 
-    m_RenderData.pCurrentMonData->primaryFB.bind();
+    m_RenderData.currentFB->bind();
 }
 
 void CHyprOpenGLImpl::renderMirrored() {
@@ -1974,7 +2034,7 @@ void CHyprOpenGLImpl::destroyMonitorResources(CMonitor* pMonitor) {
     wlr_output_attach_render(pMonitor->output, nullptr);
 
     g_pHyprOpenGL->m_mMonitorRenderResources[pMonitor].mirrorFB.release();
-    g_pHyprOpenGL->m_mMonitorRenderResources[pMonitor].primaryFB.release();
+    g_pHyprOpenGL->m_mMonitorRenderResources[pMonitor].offloadFB.release();
     g_pHyprOpenGL->m_mMonitorRenderResources[pMonitor].mirrorSwapFB.release();
     g_pHyprOpenGL->m_mMonitorRenderResources[pMonitor].monitorMirrorFB.release();
     g_pHyprOpenGL->m_mMonitorRenderResources[pMonitor].blurFB.release();
@@ -2014,10 +2074,11 @@ void CHyprOpenGLImpl::renderOffToMain(CFramebuffer* off) {
 }
 
 void CHyprOpenGLImpl::bindBackOnMain() {
-    m_RenderData.pCurrentMonData->primaryFB.bind();
-    m_RenderData.currentFB = &m_RenderData.pCurrentMonData->primaryFB;
+    m_RenderData.mainFB->bind();
+    m_RenderData.currentFB = m_RenderData.mainFB;
 }
 
 void CHyprOpenGLImpl::setMonitorTransformEnabled(bool enabled) {
     m_bEndFrame = !enabled;
 }
+
diff --git a/src/render/OpenGL.hpp b/src/render/OpenGL.hpp
index 8c412302..b6c3969e 100644
--- a/src/render/OpenGL.hpp
+++ b/src/render/OpenGL.hpp
@@ -14,6 +14,9 @@
 #include "Texture.hpp"
 #include "Framebuffer.hpp"
 #include "Transformer.hpp"
+#include "Renderbuffer.hpp"
+
+#include <GLES2/gl2ext.h>
 
 #include "../debug/TracyDefines.hpp"
 
@@ -27,8 +30,7 @@ inline const float fullVerts[] = {
 };
 inline const float fanVertsFull[] = {-1.0f, -1.0f, 1.0f, -1.0f, 1.0f, 1.0f, -1.0f, 1.0f};
 
-enum eDiscardMode
-{
+enum eDiscardMode {
     DISCARD_OPAQUE = 1,
     DISCARD_ALPHA  = 1 << 1
 };
@@ -39,7 +41,7 @@ struct SRenderModifData {
 };
 
 struct SMonitorRenderData {
-    CFramebuffer primaryFB;
+    CFramebuffer offloadFB;
     CFramebuffer mirrorFB;     // these are used for some effects,
     CFramebuffer mirrorSwapFB; // etc
     CFramebuffer offMainFB;
@@ -80,6 +82,7 @@ struct SCurrentRenderData {
 
     SMonitorRenderData* pCurrentMonData = nullptr;
     CFramebuffer*       currentFB       = nullptr;
+    CFramebuffer*       mainFB          = nullptr;
 
     CRegion             damage;
 
@@ -105,7 +108,6 @@ class CHyprOpenGLImpl {
 
     void               begin(CMonitor*, CRegion*, bool fake = false);
     void               end();
-    void               bindWlrOutputFb();
 
     void               renderRect(CBox*, const CColor&, int round = 0);
     void               renderRectWithBlur(CBox*, const CColor&, int round = 0, float blurA = 1.f);
@@ -157,7 +159,6 @@ class CHyprOpenGLImpl {
     SCurrentRenderData m_RenderData;
 
     GLint              m_iCurrentOutputFb = 0;
-    GLint              m_iWLROutputFb     = 0;
 
     bool               m_bReloadScreenShader = true; // at launch it can be set
 
@@ -169,6 +170,11 @@ class CHyprOpenGLImpl {
     std::unordered_map<CMonitor*, SMonitorRenderData> m_mMonitorRenderResources;
     std::unordered_map<CMonitor*, CTexture>           m_mMonitorBGTextures;
 
+    struct {
+        PFNGLEGLIMAGETARGETRENDERBUFFERSTORAGEOESPROC glEGLImageTargetRenderbufferStorageOES = nullptr;
+        PFNEGLDESTROYIMAGEKHRPROC                     eglDestroyImageKHR                     = nullptr;
+    } m_sProc;
+
   private:
     std::list<GLuint> m_lBuffers;
     std::list<GLuint> m_lTextures;
@@ -176,10 +182,11 @@ class CHyprOpenGLImpl {
     int               m_iDRMFD;
     std::string       m_szExtensions;
 
-    bool              m_bFakeFrame        = false;
-    bool              m_bEndFrame         = false;
-    bool              m_bApplyFinalShader = false;
-    bool              m_bBlend            = false;
+    bool              m_bFakeFrame            = false;
+    bool              m_bEndFrame             = false;
+    bool              m_bApplyFinalShader     = false;
+    bool              m_bBlend                = false;
+    bool              m_bOffloadedFramebuffer = false;
 
     CShader           m_sFinalScreenShader;
     CTimer            m_tGlobalTimer;
@@ -201,7 +208,10 @@ class CHyprOpenGLImpl {
 
     bool          shouldUseNewBlurOptimizations(SLayerSurface* pLayer, CWindow* pWindow);
 
+    bool          passRequiresIntrospection(CMonitor* pMonitor);
+
     friend class CHyprRenderer;
 };
 
 inline std::unique_ptr<CHyprOpenGLImpl> g_pHyprOpenGL;
+
diff --git a/src/render/Renderbuffer.cpp b/src/render/Renderbuffer.cpp
new file mode 100644
index 00000000..93dca518
--- /dev/null
+++ b/src/render/Renderbuffer.cpp
@@ -0,0 +1,81 @@
+#include "Renderbuffer.hpp"
+#include "OpenGL.hpp"
+#include "../Compositor.hpp"
+
+#include <dlfcn.h>
+
+CRenderbuffer::~CRenderbuffer() {
+    if (eglGetCurrentContext() != wlr_egl_get_context(g_pCompositor->m_sWLREGL))
+        eglMakeCurrent(wlr_egl_get_display(g_pCompositor->m_sWLREGL), EGL_NO_SURFACE, EGL_NO_SURFACE, wlr_egl_get_context(g_pCompositor->m_sWLREGL));
+
+    m_sFramebuffer.release();
+    glDeleteRenderbuffers(1, &m_iRBO);
+
+    g_pHyprOpenGL->m_sProc.eglDestroyImageKHR(wlr_egl_get_display(g_pCompositor->m_sWLREGL), m_iImage);
+}
+
+CRenderbuffer::CRenderbuffer(wlr_buffer* buffer, uint32_t format) : m_pWlrBuffer(buffer) {
+
+    // EVIL, but we can't include a hidden header because nixos is fucking special
+    static EGLImageKHR (*PWLREGLCREATEIMAGEFROMDMABUF)(wlr_egl*, wlr_dmabuf_attributes*, bool*);
+    static bool symbolFound = false;
+    if (!symbolFound) {
+        PWLREGLCREATEIMAGEFROMDMABUF = reinterpret_cast<EGLImageKHR (*)(wlr_egl*, wlr_dmabuf_attributes*, bool*)>(dlsym(RTLD_DEFAULT, "wlr_egl_create_image_from_dmabuf"));
+
+        symbolFound = true;
+
+        RASSERT(PWLREGLCREATEIMAGEFROMDMABUF, "wlr_egl_create_image_from_dmabuf was not found in wlroots!");
+
+        Debug::log(LOG, "CRenderbuffer: wlr_egl_create_image_from_dmabuf found at {:x}", (uintptr_t)PWLREGLCREATEIMAGEFROMDMABUF);
+    }
+    // end evil hack
+
+    struct wlr_dmabuf_attributes dmabuf = {0};
+    if (!wlr_buffer_get_dmabuf(buffer, &dmabuf))
+        throw std::runtime_error("wlr_buffer_get_dmabuf failed");
+
+    bool externalOnly;
+    m_iImage = PWLREGLCREATEIMAGEFROMDMABUF(g_pCompositor->m_sWLREGL, &dmabuf, &externalOnly);
+    if (m_iImage == EGL_NO_IMAGE_KHR)
+        throw std::runtime_error("wlr_egl_create_image_from_dmabuf failed");
+
+    glGenRenderbuffers(1, &m_iRBO);
+    glBindRenderbuffer(GL_RENDERBUFFER, m_iRBO);
+    g_pHyprOpenGL->m_sProc.glEGLImageTargetRenderbufferStorageOES(GL_RENDERBUFFER, (GLeglImageOES)m_iImage);
+    glBindRenderbuffer(GL_RENDERBUFFER, 0);
+
+    glGenFramebuffers(1, &m_sFramebuffer.m_iFb);
+    m_sFramebuffer.m_vSize = {buffer->width, buffer->height};
+    m_sFramebuffer.bind();
+    glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, m_iRBO);
+
+    if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE)
+        throw std::runtime_error("rbo: glCheckFramebufferStatus failed");
+
+    glBindFramebuffer(GL_FRAMEBUFFER, 0);
+
+    hyprListener_destroyBuffer.initCallback(
+        &buffer->events.destroy, [](void* owner, void* data) { g_pHyprRenderer->onRenderbufferDestroy((CRenderbuffer*)owner); }, this, "CRenderbuffer");
+}
+
+void CRenderbuffer::bind() {
+    glBindRenderbuffer(GL_RENDERBUFFER, m_iRBO);
+    bindFB();
+}
+
+void CRenderbuffer::bindFB() {
+    m_sFramebuffer.bind();
+}
+
+void CRenderbuffer::unbind() {
+    glBindRenderbuffer(GL_RENDERBUFFER, 0);
+#ifndef GLES2
+    glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0);
+#else
+    glBindFramebuffer(GL_FRAMEBUFFER, 0);
+#endif
+}
+
+CFramebuffer* CRenderbuffer::getFB() {
+    return &m_sFramebuffer;
+}
diff --git a/src/render/Renderbuffer.hpp b/src/render/Renderbuffer.hpp
new file mode 100644
index 00000000..d5def45f
--- /dev/null
+++ b/src/render/Renderbuffer.hpp
@@ -0,0 +1,25 @@
+#pragma once
+
+#include "Framebuffer.hpp"
+
+class CMonitor;
+
+class CRenderbuffer {
+  public:
+    CRenderbuffer(wlr_buffer* buffer, uint32_t format);
+    ~CRenderbuffer();
+
+    void          bind();
+    void          bindFB();
+    void          unbind();
+    CFramebuffer* getFB();
+
+    wlr_buffer*   m_pWlrBuffer = nullptr;
+
+    DYNLISTENER(destroyBuffer);
+
+  private:
+    EGLImageKHR  m_iImage = 0;
+    GLuint       m_iRBO   = 0;
+    CFramebuffer m_sFramebuffer;
+};
diff --git a/src/render/Renderer.cpp b/src/render/Renderer.cpp
index 48979718..945b4aad 100644
--- a/src/render/Renderer.cpp
+++ b/src/render/Renderer.cpp
@@ -189,6 +189,9 @@ void CHyprRenderer::renderWorkspaceWindowsFullscreen(CMonitor* pMonitor, CWorksp
         if (w->m_bIsFullscreen || w->m_bIsFloating)
             continue;
 
+        if (pWorkspace->m_bIsSpecialWorkspace != g_pCompositor->isWorkspaceSpecial(w->m_iWorkspaceID))
+            continue;
+
         renderWindow(w.get(), pMonitor, time, true, RENDER_PASS_ALL);
     }
 
@@ -203,9 +206,16 @@ void CHyprRenderer::renderWorkspaceWindowsFullscreen(CMonitor* pMonitor, CWorksp
         if (w->m_bIsFullscreen || !w->m_bIsFloating)
             continue;
 
+        if (w->m_iMonitorID == pWorkspace->m_iMonitorID && pWorkspace->m_bIsSpecialWorkspace != g_pCompositor->isWorkspaceSpecial(w->m_iWorkspaceID))
+            continue;
+
+        if (pWorkspace->m_bIsSpecialWorkspace && w->m_iMonitorID != pWorkspace->m_iMonitorID)
+            continue; // special on another are rendered as a part of the base pass
+
         renderWindow(w.get(), pMonitor, time, true, RENDER_PASS_ALL);
     }
 
+    // TODO: this pass sucks
     for (auto& w : g_pCompositor->m_vWindows) {
         const auto PWORKSPACE = g_pCompositor->getWorkspaceByID(w->m_iWorkspaceID);
 
@@ -217,7 +227,7 @@ void CHyprRenderer::renderWorkspaceWindowsFullscreen(CMonitor* pMonitor, CWorksp
                 continue;
         }
 
-        if (w->m_iWorkspaceID == pMonitor->activeWorkspace && !w->m_bIsFullscreen)
+        if (w->m_iWorkspaceID != pMonitor->activeWorkspace || !w->m_bIsFullscreen)
             continue;
 
         renderWindow(w.get(), pMonitor, time, pWorkspace->m_efFullscreenMode != FULLSCREEN_FULL, RENDER_PASS_ALL);
@@ -236,6 +246,12 @@ void CHyprRenderer::renderWorkspaceWindowsFullscreen(CMonitor* pMonitor, CWorksp
         if (w->m_iWorkspaceID != pWorkspaceWindow->m_iWorkspaceID || (!w->m_bCreatedOverFullscreen && !w->m_bPinned) || (!w->m_bIsMapped && !w->m_bFadingOut) || w->m_bIsFullscreen)
             continue;
 
+        if (w->m_iMonitorID == pWorkspace->m_iMonitorID && pWorkspace->m_bIsSpecialWorkspace != g_pCompositor->isWorkspaceSpecial(w->m_iWorkspaceID))
+            continue;
+
+        if (pWorkspace->m_bIsSpecialWorkspace && w->m_iMonitorID != pWorkspace->m_iMonitorID)
+            continue; // special on another are rendered as a part of the base pass
+
         renderWindow(w.get(), pMonitor, time, true, RENDER_PASS_ALL);
     }
 
@@ -855,8 +871,8 @@ bool CHyprRenderer::attemptDirectScanout(CMonitor* pMonitor) {
 }
 
 void CHyprRenderer::renderMonitor(CMonitor* pMonitor) {
-    static std::chrono::high_resolution_clock::time_point startRender        = std::chrono::high_resolution_clock::now();
-    static std::chrono::high_resolution_clock::time_point startRenderOverlay = std::chrono::high_resolution_clock::now();
+    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();
 
     static auto* const                                    PDEBUGOVERLAY       = &g_pConfigManager->getConfigValuePtr("debug:overlay")->intValue;
@@ -897,7 +913,7 @@ void CHyprRenderer::renderMonitor(CMonitor* pMonitor) {
         firstLaunchAnimActive = false;
     }
 
-    startRender = std::chrono::high_resolution_clock::now();
+    renderStart = std::chrono::high_resolution_clock::now();
 
     if (*PDEBUGOVERLAY == 1) {
         g_pDebugOverlay->frameData(pMonitor);
@@ -1001,7 +1017,6 @@ void CHyprRenderer::renderMonitor(CMonitor* pMonitor) {
     // check the damage
     CRegion damage;
     bool    hasChanged = pMonitor->output->needs_frame || pixman_region32_not_empty(&pMonitor->damage.current);
-    int     bufferAge;
 
     if (!hasChanged && *PDAMAGETRACKINGMODE != DAMAGE_TRACKING_NONE && pMonitor->forceFullFrames == 0 && damageBlinkCleanup == 0)
         return;
@@ -1017,16 +1032,7 @@ void CHyprRenderer::renderMonitor(CMonitor* pMonitor) {
     if (UNLOCK_SC)
         wlr_output_lock_software_cursors(pMonitor->output, true);
 
-    if (!wlr_output_attach_render(pMonitor->output, &bufferAge)) {
-        Debug::log(ERR, "Couldn't attach render to display {} ???", pMonitor->szName);
-
-        if (UNLOCK_SC)
-            wlr_output_lock_software_cursors(pMonitor->output, false);
-
-        return;
-    }
-
-    wlr_damage_ring_get_buffer_damage(&pMonitor->damage, bufferAge, damage.pixman());
+    wlr_damage_ring_get_buffer_damage(&pMonitor->damage, m_iLastBufferAge, damage.pixman());
 
     pMonitor->renderingActive = true;
 
@@ -1072,7 +1078,25 @@ void CHyprRenderer::renderMonitor(CMonitor* pMonitor) {
 
     TRACY_GPU_ZONE("Render");
 
-    g_pHyprOpenGL->begin(pMonitor, &damage);
+    if (pMonitor == g_pCompositor->getMonitorFromCursor())
+        g_pHyprOpenGL->m_RenderData.mouseZoomFactor = std::clamp(*PZOOMFACTOR, 1.f, INFINITY);
+    else
+        g_pHyprOpenGL->m_RenderData.mouseZoomFactor = 1.f;
+
+    if (zoomInFactorFirstLaunch > 1.f) {
+        g_pHyprOpenGL->m_RenderData.mouseZoomFactor    = zoomInFactorFirstLaunch;
+        g_pHyprOpenGL->m_RenderData.mouseZoomUseMouse  = false;
+        g_pHyprOpenGL->m_RenderData.useNearestNeighbor = false;
+    }
+
+    if (!beginRender(pMonitor, damage, RENDER_MODE_NORMAL)) {
+        Debug::log(ERR, "renderer: couldn't beginRender()!");
+
+        if (UNLOCK_SC)
+            wlr_output_lock_software_cursors(pMonitor->output, false);
+
+        return;
+    }
 
     EMIT_HOOK_EVENT("render", RENDER_BEGIN);
 
@@ -1107,7 +1131,7 @@ void CHyprRenderer::renderMonitor(CMonitor* pMonitor) {
 
             // for drawing the debug overlay
             if (pMonitor == g_pCompositor->m_vMonitors.front().get() && *PDEBUGOVERLAY == 1) {
-                startRenderOverlay = std::chrono::high_resolution_clock::now();
+                renderStartOverlay = std::chrono::high_resolution_clock::now();
                 g_pDebugOverlay->draw();
                 endRenderOverlay = std::chrono::high_resolution_clock::now();
             }
@@ -1128,35 +1152,22 @@ void CHyprRenderer::renderMonitor(CMonitor* pMonitor) {
 
     renderCursor = renderCursor && shouldRenderCursor();
 
-    if (renderCursor && wlr_renderer_begin(g_pCompositor->m_sWLRRenderer, pMonitor->vecPixelSize.x, pMonitor->vecPixelSize.y)) {
+    if (renderCursor) {
         TRACY_GPU_ZONE("RenderCursor");
 
         bool lockSoftware = pMonitor == g_pCompositor->getMonitorFromCursor() && *PZOOMFACTOR != 1.f;
 
         if (lockSoftware) {
             wlr_output_lock_software_cursors(pMonitor->output, true);
-            wlr_output_render_software_cursors(pMonitor->output, NULL);
+            g_pHyprRenderer->renderSoftwareCursors(pMonitor, g_pHyprOpenGL->m_RenderData.damage);
             wlr_output_lock_software_cursors(pMonitor->output, false);
         } else
-            wlr_output_render_software_cursors(pMonitor->output, NULL);
-
-        wlr_renderer_end(g_pCompositor->m_sWLRRenderer);
-    }
-
-    if (pMonitor == g_pCompositor->getMonitorFromCursor())
-        g_pHyprOpenGL->m_RenderData.mouseZoomFactor = std::clamp(*PZOOMFACTOR, 1.f, INFINITY);
-    else
-        g_pHyprOpenGL->m_RenderData.mouseZoomFactor = 1.f;
-
-    if (zoomInFactorFirstLaunch > 1.f) {
-        g_pHyprOpenGL->m_RenderData.mouseZoomFactor    = zoomInFactorFirstLaunch;
-        g_pHyprOpenGL->m_RenderData.mouseZoomUseMouse  = false;
-        g_pHyprOpenGL->m_RenderData.useNearestNeighbor = false;
+            g_pHyprRenderer->renderSoftwareCursors(pMonitor, g_pHyprOpenGL->m_RenderData.damage);
     }
 
     EMIT_HOOK_EVENT("render", RENDER_LAST_MOMENT);
 
-    g_pHyprOpenGL->end();
+    endRender();
 
     TRACY_GPU_COLLECT;
 
@@ -1202,12 +1213,12 @@ void CHyprRenderer::renderMonitor(CMonitor* pMonitor) {
 
     pMonitor->pendingFrame = false;
 
-    const float µs = std::chrono::duration_cast<std::chrono::nanoseconds>(std::chrono::high_resolution_clock::now() - startRender).count() / 1000.f;
+    const float µs = std::chrono::duration_cast<std::chrono::nanoseconds>(std::chrono::high_resolution_clock::now() - renderStart).count() / 1000.f;
     g_pDebugOverlay->renderData(pMonitor, µs);
 
     if (*PDEBUGOVERLAY == 1) {
         if (pMonitor == g_pCompositor->m_vMonitors.front().get()) {
-            const float µsNoOverlay = µs - std::chrono::duration_cast<std::chrono::nanoseconds>(endRenderOverlay - startRenderOverlay).count() / 1000.f;
+            const float µsNoOverlay = µs - std::chrono::duration_cast<std::chrono::nanoseconds>(endRenderOverlay - renderStartOverlay).count() / 1000.f;
             g_pDebugOverlay->renderDataNoOverlay(pMonitor, µsNoOverlay);
         } else {
             g_pDebugOverlay->renderDataNoOverlay(pMonitor, µs);
@@ -1967,6 +1978,8 @@ bool CHyprRenderer::applyMonitorRule(CMonitor* pMonitor, SMonitorRule* pMonitorR
         pMonitor->vecPixelSize = Vector2D(transformedBox.width, transformedBox.height);
     }
 
+   pMonitor->updateMatrix();
+
     // update renderer (here because it will call rollback, so we cannot do this before committing)
     g_pHyprOpenGL->destroyMonitorResources(pMonitor);
 
@@ -2201,3 +2214,101 @@ void CHyprRenderer::recheckSolitaryForMonitor(CMonitor* pMonitor) {
     // found one!
     pMonitor->solitaryClient = PCANDIDATE;
 }
+
+
+void CHyprRenderer::renderSoftwareCursors(CMonitor* pMonitor, const CRegion& damage, std::optional<Vector2D> overridePos) {
+    const auto         CURSORPOS = overridePos.value_or(g_pInputManager->getMouseCoordsInternal() - pMonitor->vecPosition);
+    wlr_output_cursor* cursor;
+    wl_list_for_each(cursor, &pMonitor->output->cursors, link) {
+        if (!cursor->enabled || !cursor->visible || pMonitor->output->hardware_cursor == cursor)
+            continue;
+
+        if (!cursor->texture)
+            continue;
+
+        CBox cursorBox = CBox{CURSORPOS.x, CURSORPOS.y, cursor->width, cursor->height}.translate({-cursor->hotspot_x, -cursor->hotspot_y}).scale(pMonitor->scale);
+
+        // TODO: NVIDIA doesn't like if we use renderTexturePrimitive here. Why?
+        g_pHyprOpenGL->renderTexture(cursor->texture, &cursorBox, 1.0);
+    }
+}
+
+CRenderbuffer* CHyprRenderer::getOrCreateRenderbuffer(wlr_buffer* buffer, uint32_t fmt) {
+    auto it = std::find_if(m_vRenderbuffers.begin(), m_vRenderbuffers.end(), [&](const auto& other) { return other->m_pWlrBuffer == buffer; });
+
+    if (it != m_vRenderbuffers.end())
+        return it->get();
+
+    return m_vRenderbuffers.emplace_back(std::make_unique<CRenderbuffer>(buffer, fmt)).get();
+}
+
+bool CHyprRenderer::beginRender(CMonitor* pMonitor, CRegion& damage, eRenderMode mode, wlr_buffer* withBuffer) {
+
+    if (eglGetCurrentContext() != wlr_egl_get_context(g_pCompositor->m_sWLREGL))
+        eglMakeCurrent(wlr_egl_get_display(g_pCompositor->m_sWLREGL), EGL_NO_SURFACE, EGL_NO_SURFACE, wlr_egl_get_context(g_pCompositor->m_sWLREGL));
+
+    m_eRenderMode = mode;
+
+    g_pHyprOpenGL->m_RenderData.pMonitor = pMonitor; // has to be set cuz allocs
+
+    if (mode == RENDER_MODE_FULL_FAKE) {
+        wlr_output_attach_render(pMonitor->output, nullptr);
+        g_pHyprOpenGL->begin(pMonitor, &damage, true);
+        return true;
+    }
+
+    if (!withBuffer) {
+        if (!wlr_output_configure_primary_swapchain(pMonitor->output, &pMonitor->output->pending, &pMonitor->output->swapchain))
+            return false;
+
+        m_pCurrentWlrBuffer = wlr_swapchain_acquire(pMonitor->output->swapchain, &m_iLastBufferAge);
+        if (!m_pCurrentWlrBuffer)
+            return false;
+    } else {
+        m_pCurrentWlrBuffer = withBuffer;
+    }
+
+    try {
+        m_pCurrentRenderbuffer = getOrCreateRenderbuffer(m_pCurrentWlrBuffer, pMonitor->drmFormat);
+    } catch (std::exception& e) {
+        wlr_buffer_unlock(m_pCurrentWlrBuffer);
+        return false;
+    }
+
+    m_pCurrentRenderbuffer->bind();
+    g_pHyprOpenGL->begin(pMonitor, &damage);
+
+    return true;
+}
+
+void CHyprRenderer::endRender() {
+    const auto PMONITOR = g_pHyprOpenGL->m_RenderData.pMonitor;
+
+    g_pHyprOpenGL->end();
+
+    if (m_eRenderMode == RENDER_MODE_FULL_FAKE) {
+        wlr_output_rollback(PMONITOR->output);
+        return;
+    }
+
+    glFlush();
+
+    if (m_eRenderMode == RENDER_MODE_NORMAL)
+        wlr_output_state_set_buffer(&PMONITOR->output->pending, m_pCurrentWlrBuffer);
+
+    wlr_buffer_unlock(m_pCurrentWlrBuffer);
+
+    m_pCurrentRenderbuffer->unbind();
+
+    m_pCurrentRenderbuffer = nullptr;
+    m_pCurrentWlrBuffer    = nullptr;
+    m_iLastBufferAge       = 0;
+}
+
+void CHyprRenderer::onRenderbufferDestroy(CRenderbuffer* rb) {
+    std::erase_if(m_vRenderbuffers, [&](const auto& rbo) { return rbo.get() == rb; });
+}
+
+CRenderbuffer* CHyprRenderer::getCurrentRBO() {
+    return m_pCurrentRenderbuffer;
+}
diff --git a/src/render/Renderer.hpp b/src/render/Renderer.hpp
index f28600b4..7c9df771 100644
--- a/src/render/Renderer.hpp
+++ b/src/render/Renderer.hpp
@@ -6,6 +6,7 @@
 #include "../helpers/Workspace.hpp"
 #include "../Window.hpp"
 #include "OpenGL.hpp"
+#include "Renderbuffer.hpp"
 #include "../helpers/Timer.hpp"
 #include "../helpers/Region.hpp"
 
@@ -27,6 +28,13 @@ enum eRenderPassMode
     RENDER_PASS_POPUP
 };
 
+enum eRenderMode
+{
+    RENDER_MODE_NORMAL    = 0,
+    RENDER_MODE_FULL_FAKE = 1,
+    RENDER_MODE_TO_BUFFER = 2
+};
+
 class CToplevelExportProtocolManager;
 class CInputManager;
 struct SSessionLockSurface;
@@ -58,6 +66,12 @@ class CHyprRenderer {
     void                            recheckSolitaryForMonitor(CMonitor* pMonitor);
     void                            setCursorSurface(wlr_surface* surf, int hotspotX, int hotspotY);
     void                            setCursorFromName(const std::string& name);
+    void                            renderSoftwareCursors(CMonitor* pMonitor, const CRegion& damage, std::optional<Vector2D> overridePos = {});
+    void                            onRenderbufferDestroy(CRenderbuffer* rb);
+    CRenderbuffer*                  getCurrentRBO();
+
+    bool                            beginRender(CMonitor* pMonitor, CRegion& damage, eRenderMode mode = RENDER_MODE_NORMAL, wlr_buffer* withBuffer = nullptr);
+    void                            endRender();
 
     bool                            m_bWindowRequestedCursorHide = false;
     bool                            m_bBlockSurfaceFeedback      = false;
@@ -89,19 +103,26 @@ class CHyprRenderer {
     } m_sLastCursorData;
 
   private:
-    void arrangeLayerArray(CMonitor*, const std::vector<std::unique_ptr<SLayerSurface>>&, bool, CBox*);
-    void renderWorkspaceWindowsFullscreen(CMonitor*, CWorkspace*, timespec*); // renders workspace windows (fullscreen) (tiled, floating, pinned, but no special)
-    void renderWorkspaceWindows(CMonitor*, CWorkspace*, timespec*);           // renders workspace windows (no fullscreen) (tiled, floating, pinned, but no special)
-    void renderWindow(CWindow*, CMonitor*, timespec*, bool, eRenderPassMode, bool ignorePosition = false, bool ignoreAllGeometry = false);
-    void renderLayer(SLayerSurface*, CMonitor*, timespec*);
-    void renderSessionLockSurface(SSessionLockSurface*, CMonitor*, timespec*);
-    void renderDragIcon(CMonitor*, timespec*);
-    void renderIMEPopup(SIMEPopup*, CMonitor*, timespec*);
-    void renderWorkspace(CMonitor* pMonitor, CWorkspace* pWorkspace, timespec* now, const CBox& geometry);
-    void renderAllClientsForWorkspace(CMonitor* pMonitor, CWorkspace* pWorkspace, timespec* now, const Vector2D& translate = {0, 0}, const float& scale = 1.f);
+    void           arrangeLayerArray(CMonitor*, const std::vector<std::unique_ptr<SLayerSurface>>&, bool, CBox*);
+    void           renderWorkspaceWindowsFullscreen(CMonitor*, CWorkspace*, timespec*); // renders workspace windows (fullscreen) (tiled, floating, pinned, but no special)
+    void           renderWorkspaceWindows(CMonitor*, CWorkspace*, timespec*);           // renders workspace windows (no fullscreen) (tiled, floating, pinned, but no special)
+    void           renderWindow(CWindow*, CMonitor*, timespec*, bool, eRenderPassMode, bool ignorePosition = false, bool ignoreAllGeometry = false);
+    void           renderLayer(SLayerSurface*, CMonitor*, timespec*);
+    void           renderSessionLockSurface(SSessionLockSurface*, CMonitor*, timespec*);
+    void           renderDragIcon(CMonitor*, timespec*);
+    void           renderIMEPopup(SIMEPopup*, CMonitor*, timespec*);
+    void           renderWorkspace(CMonitor* pMonitor, CWorkspace* pWorkspace, timespec* now, const CBox& geometry);
+    void           renderAllClientsForWorkspace(CMonitor* pMonitor, CWorkspace* pWorkspace, timespec* now, const Vector2D& translate = {0, 0}, const float& scale = 1.f);
 
-    bool m_bHasARenderedCursor = true;
-    bool m_bCursorHasSurface   = false;
+    bool           m_bHasARenderedCursor  = true;
+    bool           m_bCursorHasSurface    = false;
+    CRenderbuffer* m_pCurrentRenderbuffer = nullptr;
+    wlr_buffer*    m_pCurrentWlrBuffer    = nullptr;
+    eRenderMode    m_eRenderMode          = RENDER_MODE_NORMAL;
+    int            m_iLastBufferAge       = 0;
+
+    CRenderbuffer* getOrCreateRenderbuffer(wlr_buffer* buffer, uint32_t fmt);
+    std::vector<std::unique_ptr<CRenderbuffer>> m_vRenderbuffers;
 
     friend class CHyprOpenGLImpl;
     friend class CToplevelExportProtocolManager;
@@ -109,3 +130,4 @@ class CHyprRenderer {
 };
 
 inline std::unique_ptr<CHyprRenderer> g_pHyprRenderer;
+
diff --git a/subprojects/wlroots.wrap b/subprojects/wlroots.wrap
index 7df8166b..94757838 100644
--- a/subprojects/wlroots.wrap
+++ b/subprojects/wlroots.wrap
@@ -1,7 +1,7 @@
 [wrap-git]
 directory = wlroots
 url = https://gitlab.freedesktop.org/wlroots/wlroots.git
-revision = 5de9e1a99d6642c2d09d589aa37ff0a8945dcee1
+revision = f1762f428b0ef2989c81f57ea9e810403d34d946
 depth = 1
 
 diff_files = wlroots-meson-build.patch