diff options
| author | UjinT34 <41110182+UjinT34@users.noreply.github.com> | 2026-03-29 00:41:45 +0300 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2026-03-28 21:41:45 +0000 |
| commit | 38a7f03cf0e5ee116794a78581634ba3dab508fa (patch) | |
| tree | 969891411b2af4e437a183004018cfc359a3057e /src/render | |
| parent | ef2e8dfd5f7e6ee55fa7bc48e44974e64938d14a (diff) | |
renderer: refactor part 7: api fixes (#13631)
Part 7 of ujin's refactors
---------
Co-authored-by: Vaxry <vaxry@vaxry.net>
Diffstat (limited to 'src/render')
51 files changed, 2332 insertions, 1908 deletions
diff --git a/src/render/ElementRenderer.cpp b/src/render/ElementRenderer.cpp new file mode 100644 index 00000000..8d2dedf7 --- /dev/null +++ b/src/render/ElementRenderer.cpp @@ -0,0 +1,485 @@ +#include "ElementRenderer.hpp" +#include "Renderer.hpp" +#include "../layout/LayoutManager.hpp" +#include "../desktop/view/Window.hpp" +#include <hyprutils/memory/SharedPtr.hpp> +#include <hyprutils/memory/UniquePtr.hpp> +#include <hyprutils/utils/ScopeGuard.hpp> + +using namespace Render; + +void IElementRenderer::drawElement(WP<IPassElement> element, const CRegion& damage) { + if (!element) + return; + + switch (element->type()) { + case EK_BORDER: draw(dynamicPointerCast<CBorderPassElement>(element), damage); break; + case EK_CLEAR: draw(dynamicPointerCast<CClearPassElement>(element), damage); break; + case EK_FRAMEBUFFER: draw(dynamicPointerCast<CFramebufferElement>(element), damage); break; + case EK_PRE_BLUR: drawPreBlur(dynamicPointerCast<CPreBlurElement>(element), damage); break; + case EK_RECT: drawRect(dynamicPointerCast<CRectPassElement>(element), damage); break; + case EK_HINTS: drawHints(dynamicPointerCast<CRendererHintsPassElement>(element), damage); break; + case EK_SHADOW: draw(dynamicPointerCast<CShadowPassElement>(element), damage); break; + case EK_INNER_GLOW: draw(dynamicPointerCast<CInnerGlowPassElement>(element), damage); break; + case EK_SURFACE: preDrawSurface(dynamicPointerCast<CSurfacePassElement>(element), damage); break; + case EK_TEXTURE: drawTex(dynamicPointerCast<CTexPassElement>(element), damage); break; + case EK_TEXTURE_MATTE: drawTexMatte(dynamicPointerCast<CTextureMatteElement>(element), damage); break; + case EK_CUSTOM: drawCustom(element, damage); break; + default: Log::logger->log(Log::WARN, "Unimplimented draw for {}", element->passName()); + } +} + +static std::optional<Vector2D> getSurfaceExpectedSize(PHLWINDOW pWindow, SP<CWLSurfaceResource> pSurface, PHLMONITOR pMonitor, bool main) { + const auto CAN_USE_WINDOW = pWindow && main; + const auto WINDOW_SIZE_MISALIGN = CAN_USE_WINDOW && pWindow->getReportedSize() != pWindow->wlSurface()->resource()->m_current.size; + + if (pSurface->m_current.viewport.hasDestination) + return (pSurface->m_current.viewport.destination * pMonitor->m_scale).round(); + + if (pSurface->m_current.viewport.hasSource) + return (pSurface->m_current.viewport.source.size() * pMonitor->m_scale).round(); + + if (WINDOW_SIZE_MISALIGN) + return (pSurface->m_current.size * pMonitor->m_scale).round(); + + if (CAN_USE_WINDOW) + return (pWindow->getReportedSize() * pMonitor->m_scale).round(); + + return std::nullopt; +} + +void IElementRenderer::calculateUVForSurface(PHLWINDOW pWindow, SP<CWLSurfaceResource> pSurface, PHLMONITOR pMonitor, bool main, const Vector2D& projSize, + const Vector2D& projSizeUnscaled, bool fixMisalignedFSV1) { + auto& m_renderData = g_pHyprRenderer->m_renderData; + + if (!pWindow || !pWindow->m_isX11) { + static auto PEXPANDEDGES = CConfigValue<Hyprlang::INT>("render:expand_undersized_textures"); + + Vector2D uvTL; + Vector2D uvBR = Vector2D(1, 1); + + if (pSurface->m_current.viewport.hasSource) { + // we stretch it to dest. if no dest, to 1,1 + Vector2D const& bufferSize = pSurface->m_current.bufferSize; + auto const& bufferSource = pSurface->m_current.viewport.source; + + // calculate UV for the basic src_box. Assume dest == size. Scale to dest later + uvTL = Vector2D(bufferSource.x / bufferSize.x, bufferSource.y / bufferSize.y); + uvBR = Vector2D((bufferSource.x + bufferSource.width) / bufferSize.x, (bufferSource.y + bufferSource.height) / bufferSize.y); + + if (uvBR.x < 0.01f || uvBR.y < 0.01f) { + uvTL = Vector2D(); + uvBR = Vector2D(1, 1); + } + } + + if (projSize != Vector2D{} && fixMisalignedFSV1) { + // instead of nearest_neighbor (we will repeat / skip) + // just cut off / expand surface + const Vector2D PIXELASUV = Vector2D{1, 1} / pSurface->m_current.bufferSize; + const auto& BUFFER_SIZE = pSurface->m_current.bufferSize; + + // compute MISALIGN from the adjusted UV coordinates. + const Vector2D MISALIGNMENT = (uvBR - uvTL) * BUFFER_SIZE - projSize; + + if (MISALIGNMENT != Vector2D{}) + uvBR -= MISALIGNMENT * PIXELASUV; + } else { + // if the surface is smaller than our viewport, extend its edges. + // this will break if later on xdg geometry is hit, but we really try + // to let the apps know to NOT add CSD. Also if source is there. + // there is no way to fix this if that's the case + const auto MONITOR_WL_SCALE = std::ceil(pMonitor->m_scale); + const bool SCALE_UNAWARE = pMonitor->m_scale != 1.f && (MONITOR_WL_SCALE == pSurface->m_current.scale || !pSurface->m_current.viewport.hasDestination); + const auto EXPECTED_SIZE = getSurfaceExpectedSize(pWindow, pSurface, pMonitor, main).value_or((projSize * pMonitor->m_scale).round()); + + const auto RATIO = projSize / EXPECTED_SIZE; + if (!SCALE_UNAWARE || MONITOR_WL_SCALE == 1) { + if (*PEXPANDEDGES && !SCALE_UNAWARE && (RATIO.x > 1 || RATIO.y > 1)) { + const auto FIX = RATIO.clamp(Vector2D{1, 1}, Vector2D{1000000, 1000000}); + uvBR = uvBR * FIX; + } + + // FIXME: probably do this for in anims on all views... + const auto SHOULD_SKIP = !pWindow || pWindow->m_animatingIn; + if (!SHOULD_SKIP && (RATIO.x < 1 || RATIO.y < 1)) { + const auto FIX = RATIO.clamp(Vector2D{0.0001, 0.0001}, Vector2D{1, 1}); + uvBR = uvBR * FIX; + } + } + } + + m_renderData.primarySurfaceUVTopLeft = uvTL; + m_renderData.primarySurfaceUVBottomRight = uvBR; + + if (m_renderData.primarySurfaceUVTopLeft == Vector2D() && m_renderData.primarySurfaceUVBottomRight == Vector2D(1, 1)) { + // No special UV mods needed + m_renderData.primarySurfaceUVTopLeft = Vector2D(-1, -1); + m_renderData.primarySurfaceUVBottomRight = Vector2D(-1, -1); + } + + if (!main || !pWindow) + return; + + // FIXME: this doesn't work. We always set MAXIMIZED anyways, so this doesn't need to work, but it's problematic. + + // CBox geom = pWindow->m_xdgSurface->m_current.geometry; + + // // Adjust UV based on the xdg_surface geometry + // if (geom.x != 0 || geom.y != 0 || geom.w != 0 || geom.h != 0) { + // const auto XPERC = geom.x / pSurface->m_current.size.x; + // const auto YPERC = geom.y / pSurface->m_current.size.y; + // const auto WPERC = (geom.x + geom.w ? geom.w : pSurface->m_current.size.x) / pSurface->m_current.size.x; + // const auto HPERC = (geom.y + geom.h ? geom.h : pSurface->m_current.size.y) / pSurface->m_current.size.y; + + // const auto TOADDTL = Vector2D(XPERC * (uvBR.x - uvTL.x), YPERC * (uvBR.y - uvTL.y)); + // uvBR = uvBR - Vector2D((1.0 - WPERC) * (uvBR.x - uvTL.x), (1.0 - HPERC) * (uvBR.y - uvTL.y)); + // uvTL = uvTL + TOADDTL; + // } + + m_renderData.primarySurfaceUVTopLeft = uvTL; + m_renderData.primarySurfaceUVBottomRight = uvBR; + + if (m_renderData.primarySurfaceUVTopLeft == Vector2D() && m_renderData.primarySurfaceUVBottomRight == Vector2D(1, 1)) { + // No special UV mods needed + m_renderData.primarySurfaceUVTopLeft = Vector2D(-1, -1); + m_renderData.primarySurfaceUVBottomRight = Vector2D(-1, -1); + } + } else { + m_renderData.primarySurfaceUVTopLeft = Vector2D(-1, -1); + m_renderData.primarySurfaceUVBottomRight = Vector2D(-1, -1); + } +} + +void IElementRenderer::drawRect(WP<CRectPassElement> element, const CRegion& damage) { + auto& data = element->m_data; + auto& m_renderData = g_pHyprRenderer->m_renderData; + + if (data.box.w <= 0 || data.box.h <= 0) + return; + + if (!data.clipBox.empty()) + m_renderData.clipBox = data.clipBox; + + data.modifiedBox = data.box; + m_renderData.renderModif.applyToBox(data.modifiedBox); + + 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); + + data.TOPLEFT[0] = sc<float>(transformedBox.x); + data.TOPLEFT[1] = sc<float>(transformedBox.y); + data.FULLSIZE[0] = sc<float>(transformedBox.width); + data.FULLSIZE[1] = sc<float>(transformedBox.height); + + data.drawRegion = data.color.a == 1.F || !data.blur ? damage : m_renderData.damage; + + 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); + } + + draw(element, damage); + + m_renderData.clipBox = {}; +} + +void IElementRenderer::drawHints(WP<CRendererHintsPassElement> element, const CRegion& damage) { + const auto m_data = element->m_data; + if (m_data.renderModif.has_value()) + g_pHyprRenderer->m_renderData.renderModif = *m_data.renderModif; +} + +void IElementRenderer::drawPreBlur(WP<CPreBlurElement> element, const CRegion& damage) { + TRACY_GPU_ZONE("RenderPreBlurForCurrentMonitor"); + auto& m_renderData = g_pHyprRenderer->m_renderData; + + const auto SAVEDRENDERMODIF = m_renderData.renderModif; + m_renderData.renderModif = {}; // fix shit + + // make the fake dmg + CRegion fakeDamage{0, 0, m_renderData.pMonitor->m_transformedSize.x, m_renderData.pMonitor->m_transformedSize.y}; + + draw(element, fakeDamage); + + m_renderData.pMonitor->m_blurFBDirty = false; + m_renderData.pMonitor->m_blurFBShouldRender = false; + + m_renderData.renderModif = SAVEDRENDERMODIF; +} + +void IElementRenderer::drawSurface(WP<CSurfacePassElement> element, const CRegion& damage) { + const auto m_data = element->m_data; + auto& m_renderData = g_pHyprRenderer->m_renderData; + + Hyprutils::Utils::CScopeGuard x = {[]() { + g_pHyprRenderer->m_renderData.primarySurfaceUVTopLeft = Vector2D(-1, -1); + g_pHyprRenderer->m_renderData.primarySurfaceUVBottomRight = Vector2D(-1, -1); + }}; + + if (!m_data.texture) + return; + + const auto& TEXTURE = m_data.texture; + + // this is bad, probably has been logged elsewhere. Means the texture failed + // uploading to the GPU. + if (!TEXTURE->ok()) + return; + + const auto INTERACTIVERESIZEINPROGRESS = m_data.pWindow && g_layoutManager->dragController()->target() && g_layoutManager->dragController()->mode() == MBIND_RESIZE; + TRACY_GPU_ZONE("RenderSurface"); + + auto PSURFACE = Desktop::View::CWLSurface::fromResource(m_data.surface); + + const float ALPHA = m_data.alpha * m_data.fadeAlpha * (PSURFACE ? PSURFACE->m_alphaModifier : 1.F); + const float OVERALL_ALPHA = PSURFACE ? PSURFACE->m_overallOpacity : 1.F; + const bool BLUR = m_data.blur && (!TEXTURE->m_opaque || ALPHA < 1.F || OVERALL_ALPHA < 1.F); + + auto windowBox = element->getTexBox(); + + const auto PROJSIZEUNSCALED = windowBox.size(); + + windowBox.scale(m_data.pMonitor->m_scale); + windowBox.round(); + + if (windowBox.width <= 1 || windowBox.height <= 1) { + element->discard(); + return; + } + + const bool MISALIGNEDFSV1 = std::floor(m_data.pMonitor->m_scale) != m_data.pMonitor->m_scale /* Fractional */ && m_data.surface->m_current.scale == 1 /* fs protocol */ && + windowBox.size() != m_data.surface->m_current.bufferSize /* misaligned */ && DELTALESSTHAN(windowBox.width, m_data.surface->m_current.bufferSize.x, 3) && + DELTALESSTHAN(windowBox.height, m_data.surface->m_current.bufferSize.y, 3) /* off by one-or-two */ && + (!m_data.pWindow || (!m_data.pWindow->m_realSize->isBeingAnimated() && !INTERACTIVERESIZEINPROGRESS)) /* not window or not animated/resizing */ && + (!m_data.pLS || (!m_data.pLS->m_realSize->isBeingAnimated())); /* not LS or not animated */ + + calculateUVForSurface(m_data.pWindow, m_data.surface, m_data.pMonitor->m_self.lock(), m_data.mainSurface, windowBox.size(), PROJSIZEUNSCALED, MISALIGNEDFSV1); + + auto cancelRender = false; + auto clipRegion = element->visibleRegion(cancelRender); + if (cancelRender) + return; + + // check for fractional scale surfaces misaligning the buffer size + // in those cases it's better to just force nearest neighbor + // as long as the window is not animated. During those it'd look weird. + // UV will fixup it as well + if (MISALIGNEDFSV1) + m_renderData.useNearestNeighbor = true; + + float rounding = m_data.rounding; + float roundingPower = m_data.roundingPower; + + rounding -= 1; // to fix a border issue + + if (m_data.dontRound) { + rounding = 0; + roundingPower = 2.0f; + } + + const bool WINDOWOPAQUE = m_data.pWindow && m_data.pWindow->wlSurface()->resource() == m_data.surface ? m_data.pWindow->opaque() : false; + const bool CANDISABLEBLEND = ALPHA >= 1.f && OVERALL_ALPHA >= 1.f && rounding <= 0 && WINDOWOPAQUE; + + if (CANDISABLEBLEND) + g_pHyprRenderer->blend(false); + else + g_pHyprRenderer->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) + drawElement(makeShared<CTexPassElement>(CTexPassElement::SRenderData{ + .tex = TEXTURE, + .box = windowBox, + .a = ALPHA, + .blurA = m_data.fadeAlpha, + .overallA = OVERALL_ALPHA, + .round = rounding, + .roundingPower = roundingPower, + .blur = true, + .blockBlurOptimization = m_data.blockBlurOptimization, + .allowCustomUV = true, + .surface = m_data.surface, + .discardMode = m_data.discardMode, + .discardOpacity = m_data.discardOpacity, + .clipRegion = clipRegion, + .currentLS = m_data.pLS, + }), + m_renderData.damage.copy().intersect(windowBox)); + else + drawElement(makeShared<CTexPassElement>(CTexPassElement::SRenderData{ + .tex = TEXTURE, + .box = windowBox, + .a = ALPHA * OVERALL_ALPHA, + .round = rounding, + .roundingPower = roundingPower, + .discardActive = false, + .allowCustomUV = true, + .surface = m_data.surface, + .discardMode = m_data.discardMode, + .discardOpacity = m_data.discardOpacity, + .clipRegion = clipRegion, + .currentLS = m_data.pLS, + }), + m_renderData.damage.copy().intersect(windowBox)); + } else { + if (BLUR && m_data.popup) + drawElement(makeShared<CTexPassElement>(CTexPassElement::SRenderData{ + .tex = TEXTURE, + .box = windowBox, + .a = ALPHA, + .blurA = m_data.fadeAlpha, + .overallA = OVERALL_ALPHA, + .round = rounding, + .roundingPower = roundingPower, + .blur = true, + .blockBlurOptimization = true, + .allowCustomUV = true, + .surface = m_data.surface, + .discardMode = m_data.discardMode, + .discardOpacity = m_data.discardOpacity, + .clipRegion = clipRegion, + .currentLS = m_data.pLS, + }), + m_renderData.damage.copy().intersect(windowBox)); + else + drawElement(makeShared<CTexPassElement>(CTexPassElement::SRenderData{ + .tex = TEXTURE, + .box = windowBox, + .a = ALPHA * OVERALL_ALPHA, + .round = rounding, + .roundingPower = roundingPower, + .discardActive = false, + .allowCustomUV = true, + .surface = m_data.surface, + .discardMode = m_data.discardMode, + .discardOpacity = m_data.discardOpacity, + .clipRegion = clipRegion, + .currentLS = m_data.pLS, + }), + m_renderData.damage.copy().intersect(windowBox)); + } + + g_pHyprRenderer->blend(true); +}; + +void IElementRenderer::preDrawSurface(WP<CSurfacePassElement> element, const CRegion& damage) { + auto& m_renderData = g_pHyprRenderer->m_renderData; + m_renderData.clipBox = element->m_data.clipBox; + m_renderData.useNearestNeighbor = element->m_data.useNearestNeighbor; + g_pHyprRenderer->pushMonitorTransformEnabled(element->m_data.flipEndFrame); + m_renderData.currentWindow = element->m_data.pWindow; + + drawSurface(element, damage); + + if (!g_pHyprRenderer->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 (element->m_data.surface->m_current.buffer && !element->m_data.surface->m_current.buffer->isSynchronous()) + g_pHyprRenderer->m_usedAsyncBuffers.emplace_back(element->m_data.surface->m_current.buffer); + + m_renderData.clipBox = {}; + m_renderData.useNearestNeighbor = false; + g_pHyprRenderer->popMonitorTransformEnabled(); + m_renderData.currentWindow.reset(); +} + +void IElementRenderer::drawTex(WP<CTexPassElement> element, const CRegion& damage) { + auto& m_renderData = g_pHyprRenderer->m_renderData; + if (!element->m_data.clipBox.empty()) + m_renderData.clipBox = element->m_data.clipBox; + + g_pHyprRenderer->pushMonitorTransformEnabled(element->m_data.flipEndFrame); + if (element->m_data.useMirrorProjection) + g_pHyprRenderer->setProjectionType(RPT_MIRROR); + + m_renderData.surface = element->m_data.surface; + + Hyprutils::Utils::CScopeGuard x = {[useMirrorProjection = element->m_data.useMirrorProjection]() { + g_pHyprRenderer->popMonitorTransformEnabled(); + if (useMirrorProjection) + g_pHyprRenderer->setProjectionType(RPT_MONITOR); + g_pHyprRenderer->m_renderData.surface.reset(); + g_pHyprRenderer->m_renderData.clipBox = {}; + }}; + + if (element->m_data.blur) { + // make a damage region for this window + CRegion texDamage{m_renderData.damage}; + texDamage.intersect(element->m_data.box.x, element->m_data.box.y, element->m_data.box.width, element->m_data.box.height); + + // 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); + 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) || + !g_pHyprRenderer->shouldUseNewBlurOptimizations(element->m_data.currentLS.lock(), m_renderData.currentWindow.lock()); + + // vvv TODO: layered blur fbs? + if (element->m_data.blockBlurOptimization.value_or(false)) { + inverseOpaque.translate(box.pos()); + m_renderData.renderModif.applyToRegion(inverseOpaque); + inverseOpaque.intersect(element->m_data.damage); + element->m_data.blurredBG = g_pHyprRenderer->blurMainFramebuffer(element->m_data.a, &inverseOpaque); + } else + element->m_data.blurredBG = m_renderData.pMonitor->resources()->m_blurFB->getTexture(); + + draw(element, damage); + } else + draw(element, damage); +} + +void IElementRenderer::drawTexMatte(WP<CTextureMatteElement> element, const CRegion& damage) { + if (g_pHyprRenderer->m_renderData.damage.empty()) + return; + + const auto m_data = element->m_data; + if (m_data.disableTransformAndModify) { + g_pHyprRenderer->pushMonitorTransformEnabled(true); + g_pHyprRenderer->m_renderData.renderModif.enabled = false; + draw(element, damage); + g_pHyprRenderer->m_renderData.renderModif.enabled = true; + g_pHyprRenderer->popMonitorTransformEnabled(); + } else + draw(element, damage); +} + +void IElementRenderer::drawCustom(WP<IPassElement> element, const CRegion& damage) { + const auto& elements = element->draw(); + for (const auto& el : elements) { + drawElement(el, damage); + } +} diff --git a/src/render/ElementRenderer.hpp b/src/render/ElementRenderer.hpp new file mode 100644 index 00000000..ed92b06f --- /dev/null +++ b/src/render/ElementRenderer.hpp @@ -0,0 +1,48 @@ +#pragma once + +#include "./pass/BorderPassElement.hpp" +#include "./pass/ClearPassElement.hpp" +#include "./pass/FramebufferElement.hpp" +#include "./pass/PreBlurElement.hpp" +#include "./pass/RectPassElement.hpp" +#include "./pass/RendererHintsPassElement.hpp" +#include "./pass/ShadowPassElement.hpp" +#include "./pass/SurfacePassElement.hpp" +#include "./pass/TexPassElement.hpp" +#include "./pass/TextureMatteElement.hpp" +#include "./pass/InnerGlowPassElement.hpp" +#include <hyprutils/math/Region.hpp> + +namespace Render { + class IElementRenderer { + public: + IElementRenderer() = default; + virtual ~IElementRenderer() = default; + + void drawElement(WP<IPassElement> element, const CRegion& damage); + + protected: + virtual void draw(WP<CBorderPassElement> element, const CRegion& damage) = 0; + virtual void draw(WP<CClearPassElement> element, const CRegion& damage) = 0; + virtual void draw(WP<CFramebufferElement> element, const CRegion& damage) = 0; + virtual void draw(WP<CPreBlurElement> element, const CRegion& damage) = 0; + virtual void draw(WP<CRectPassElement> element, const CRegion& damage) = 0; + virtual void draw(WP<CShadowPassElement> element, const CRegion& damage) = 0; + virtual void draw(WP<CInnerGlowPassElement> element, const CRegion& damage) = 0; + virtual void draw(WP<CTexPassElement> element, const CRegion& damage) = 0; + virtual void draw(WP<CTextureMatteElement> element, const CRegion& damage) = 0; + + private: + void calculateUVForSurface(PHLWINDOW, SP<CWLSurfaceResource>, PHLMONITOR pMonitor, bool main = false, const Vector2D& projSize = {}, const Vector2D& projSizeUnscaled = {}, + bool fixMisalignedFSV1 = false); + + void drawRect(WP<CRectPassElement> element, const CRegion& damage); + void drawHints(WP<CRendererHintsPassElement> element, const CRegion& damage); + void drawPreBlur(WP<CPreBlurElement> element, const CRegion& damage); + void drawSurface(WP<CSurfacePassElement> element, const CRegion& damage); + void preDrawSurface(WP<CSurfacePassElement> element, const CRegion& damage); + void drawTex(WP<CTexPassElement> element, const CRegion& damage); + void drawTexMatte(WP<CTextureMatteElement> element, const CRegion& damage); + void drawCustom(WP<IPassElement> element, const CRegion& damage); + }; +} diff --git a/src/render/Framebuffer.cpp b/src/render/Framebuffer.cpp index b2ff7e68..47a466c1 100644 --- a/src/render/Framebuffer.cpp +++ b/src/render/Framebuffer.cpp @@ -1,16 +1,23 @@ #include "Framebuffer.hpp" +#include "helpers/Format.hpp" +#include "helpers/cm/ColorManagement.hpp" + +using namespace Render; IFramebuffer::IFramebuffer(const std::string& name) : m_name(name) {} -bool IFramebuffer::alloc(int w, int h, uint32_t format) { +bool IFramebuffer::alloc(int w, int h, DRMFormat format) { RASSERT((w > 0 && h > 0), "cannot alloc a FB with negative / zero size! (attempted {}x{})", w, h); - const bool sizeChanged = (m_size != Vector2D(w, h)); - const bool formatChanged = (format != m_drmFormat); + const bool sizeChanged = m_size != Vector2D(w, h); + const bool formatChanged = format != m_drmFormat; if (m_fbAllocated && !sizeChanged && !formatChanged) return true; + if (sizeChanged || formatChanged) + m_tex.reset(); + m_size = {w, h}; m_drmFormat = format; m_fbAllocated = internalAlloc(w, h, format); @@ -25,6 +32,34 @@ SP<ITexture> IFramebuffer::getTexture() { return m_tex; } +SP<ITexture> IFramebuffer::getMirrorTexture() { + return m_mirrorTex; +} + SP<ITexture> IFramebuffer::getStencilTex() { return m_stencilTex; } + +void IFramebuffer::enableMirror(SP<ITexture> tex) { + if (!tex || tex == m_mirrorTex) + return; + m_mirrorTex = tex; + m_fbAllocated = internalAlloc(m_size.x, m_size.y, m_drmFormat); +} + +void IFramebuffer::disableMirror() { + if (m_mirrorTex) { + m_mirrorTex.reset(); + m_fbAllocated = internalAlloc(m_size.x, m_size.y, m_drmFormat); + } +} + +NColorManagement::PImageDescription IFramebuffer::imageDescription() { + return m_tex ? m_tex->m_imageDescription : m_imageDescription; +} + +void IFramebuffer::setImageDescription(NColorManagement::PImageDescription desc) { + m_imageDescription = desc; + if (m_tex) + m_tex->m_imageDescription = desc; +} diff --git a/src/render/Framebuffer.hpp b/src/render/Framebuffer.hpp index 7e33f227..215e5ed9 100644 --- a/src/render/Framebuffer.hpp +++ b/src/render/Framebuffer.hpp @@ -3,38 +3,49 @@ #include "../defines.hpp" #include "../helpers/Format.hpp" #include "Texture.hpp" +#include "../helpers/cm/ColorManagement.hpp" #include <cstdint> #include <drm_fourcc.h> class CHLBufferReference; -class IFramebuffer { - public: - IFramebuffer() = default; - IFramebuffer(const std::string& name); - virtual ~IFramebuffer() = default; +namespace Render { + class IFramebuffer { + public: + IFramebuffer() = default; + IFramebuffer(const std::string& name); + virtual ~IFramebuffer() = default; - virtual bool alloc(int w, int h, uint32_t format = DRM_FORMAT_ARGB8888); - virtual void release() = 0; - virtual bool readPixels(CHLBufferReference buffer, uint32_t offsetX = 0, uint32_t offsetY = 0, uint32_t width = 0, uint32_t height = 0) = 0; + virtual bool alloc(int w, int h, DRMFormat format = DRM_FORMAT_ARGB8888); + virtual void release() = 0; + virtual bool readPixels(CHLBufferReference buffer, uint32_t offsetX = 0, uint32_t offsetY = 0, uint32_t width = 0, uint32_t height = 0) = 0; - virtual void bind() = 0; + virtual void bind() = 0; - bool isAllocated(); - SP<ITexture> getTexture(); - SP<ITexture> getStencilTex(); + bool isAllocated(); + SP<ITexture> getTexture(); + SP<ITexture> getMirrorTexture(); + SP<ITexture> getStencilTex(); + void enableMirror(SP<ITexture> tex); + void disableMirror(); + NColorManagement::PImageDescription imageDescription(); + void setImageDescription(NColorManagement::PImageDescription desc); - virtual void addStencil(SP<ITexture> tex) = 0; + virtual void addStencil(SP<ITexture> tex) = 0; - Vector2D m_size; - DRMFormat m_drmFormat = DRM_FORMAT_INVALID; + Vector2D m_size; + DRMFormat m_drmFormat = DRM_FORMAT_INVALID; - protected: - virtual bool internalAlloc(int w, int h, uint32_t format = DRM_FORMAT_ARGB8888) = 0; + protected: + virtual bool internalAlloc(int w, int h, DRMFormat format = DRM_FORMAT_ARGB8888) = 0; - SP<ITexture> m_tex; - bool m_fbAllocated = false; + SP<ITexture> m_tex; + SP<ITexture> m_mirrorTex; + bool m_fbAllocated = false; - SP<ITexture> m_stencilTex; - std::string m_name; // name for logging -}; + SP<ITexture> m_stencilTex; + std::string m_name; // name for logging + + NColorManagement::PImageDescription m_imageDescription; + }; +} diff --git a/src/render/GLRenderer.cpp b/src/render/GLRenderer.cpp index afef2ba0..77fffd25 100644 --- a/src/render/GLRenderer.cpp +++ b/src/render/GLRenderer.cpp @@ -12,30 +12,35 @@ #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 "OpenGL.hpp" #include "Renderer.hpp" -#include "gl/GLFramebuffer.hpp" -#include "gl/GLTexture.hpp" -#include "decorations/CHyprDropShadowDecoration.hpp" +#include "./gl/GLElementRenderer.hpp" +#include "./gl/GLFramebuffer.hpp" +#include "./gl/GLTexture.hpp" #include <cstdint> #include <hyprutils/memory/SharedPtr.hpp> +#include <hyprutils/memory/UniquePtr.hpp> #include <hyprutils/utils/ScopeGuard.hpp> using namespace Hyprutils::Utils; using namespace Hyprutils::OS; using enum NContentType::eContentType; using namespace NColorManagement; +using namespace Render; +using namespace Render::GL; extern "C" { #include <xf86drm.h> } -CHyprGLRenderer::CHyprGLRenderer() : IHyprRenderer() {} +CHyprGLRenderer::CHyprGLRenderer() : IHyprRenderer(), m_elementRenderer(makeUnique<CGLElementRenderer>()) {} + +IHyprRenderer::eType CHyprGLRenderer::type() { + return RT_GL; +} void CHyprGLRenderer::initRender() { g_pHyprOpenGL->makeEGLCurrent(); @@ -57,7 +62,7 @@ bool CHyprGLRenderer::beginFullFakeRenderInternal(PHLMONITOR pMonitor, CRegion& initRender(); RASSERT(fb, "Cannot render FULL_FAKE without a provided fb!"); - fb->bind(); + bindFB(fb); if (simple) g_pHyprOpenGL->beginSimple(pMonitor, damage, nullptr, fb); else @@ -119,7 +124,7 @@ void CHyprGLRenderer::endRender(const std::function<void()>& renderingDoneCallba return; } - UP<CEGLSync> eglSync = CEGLSync::create(); + auto eglSync = createSyncFDManager(); if LIKELY (eglSync && eglSync->isValid()) { for (auto const& buf : m_usedAsyncBuffers) { for (const auto& releaser : buf->m_syncReleasers) { @@ -151,7 +156,7 @@ void CHyprGLRenderer::endRender(const std::function<void()>& renderingDoneCallba } } -void CHyprGLRenderer::renderOffToMain(IFramebuffer* off) { +void CHyprGLRenderer::renderOffToMain(SP<IFramebuffer> off) { g_pHyprOpenGL->renderOffToMain(off); } @@ -160,6 +165,10 @@ SP<IRenderbuffer> CHyprGLRenderer::getOrCreateRenderbufferInternal(SP<Aquamarine return makeShared<CGLRenderbuffer>(buffer, fmt); } +UP<ISyncFDManager> CHyprGLRenderer::createSyncFDManager() { + return CEGLSync::create(); +} + SP<ITexture> CHyprGLRenderer::createStencilTexture(const int width, const int height) { g_pHyprOpenGL->makeEGLCurrent(); auto tex = makeShared<CGLTexture>(); @@ -282,127 +291,8 @@ 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<IFramebuffer> 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(CInnerGlowPassElement* 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<uint32_t>(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<ITexture> CHyprGLRenderer::getBlurTexture(PHLMONITORREF pMonitor) { - if (!pMonitor->m_blurFB) - return nullptr; - return pMonitor->m_blurFB->getTexture(); + return pMonitor->resources()->m_blurFB->getTexture(); } void CHyprGLRenderer::unsetEGL() { @@ -411,3 +301,7 @@ void CHyprGLRenderer::unsetEGL() { eglMakeCurrent(g_pHyprOpenGL->m_eglDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT); } + +WP<IElementRenderer> CHyprGLRenderer::elementRenderer() { + return m_elementRenderer; +} diff --git a/src/render/GLRenderer.hpp b/src/render/GLRenderer.hpp index 6f511fc3..ee831fec 100644 --- a/src/render/GLRenderer.hpp +++ b/src/render/GLRenderer.hpp @@ -1,53 +1,51 @@ #pragma once #include "Renderer.hpp" - -class CHyprGLRenderer : public IHyprRenderer { - public: - CHyprGLRenderer(); - - void endRender(const std::function<void()>& renderingDoneCallback = {}) override; - SP<ITexture> createStencilTexture(const int width, const int height) override; - SP<ITexture> createTexture(bool opaque = false) override; - SP<ITexture> createTexture(uint32_t drmFormat, uint8_t* pixels, uint32_t stride, const Vector2D& size, bool keepDataCopy = false, bool opaque = false) override; - SP<ITexture> createTexture(const Aquamarine::SDMABUFAttrs&, bool opaque = false) override; - SP<ITexture> createTexture(const int width, const int height, unsigned char* const data) override; - SP<ITexture> createTexture(cairo_surface_t* cairo) override; - SP<ITexture> createTexture(std::span<const float> lut3D, size_t N) override; - bool explicitSyncSupported() override; - std::vector<SDRMFormat> getDRMFormats() override; - std::vector<uint64_t> getDRMFormatModifiers(DRMFormat format) override; - SP<IFramebuffer> 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<ITexture> blurFramebuffer(SP<IFramebuffer> 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<IRenderbuffer> getOrCreateRenderbufferInternal(SP<Aquamarine::IBuffer> buffer, uint32_t fmt) override; - bool beginRenderInternal(PHLMONITOR pMonitor, CRegion& damage, bool simple = false) override; - bool beginFullFakeRenderInternal(PHLMONITOR pMonitor, CRegion& damage, SP<IFramebuffer> fb, bool simple = false) override; - void initRender() override; - bool initRenderBuffer(SP<Aquamarine::IBuffer> 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(CInnerGlowPassElement* element, const CRegion& damage) override; - void draw(CTexPassElement* element, const CRegion& damage) override; - void draw(CTextureMatteElement* element, const CRegion& damage) override; - - SP<ITexture> getBlurTexture(PHLMONITORREF pMonitor) override; - - SP<IRenderbuffer> m_currentRenderbuffer = nullptr; - - friend class CHyprOpenGLImpl; -}; +#include "render/ElementRenderer.hpp" + +namespace Render::GL { + class CHyprGLRenderer : public Render::IHyprRenderer { + public: + CHyprGLRenderer(); + ~CHyprGLRenderer() = default; + + eType type() override; + void endRender(const std::function<void()>& renderingDoneCallback = {}) override; + UP<ISyncFDManager> createSyncFDManager() override; + SP<ITexture> createStencilTexture(const int width, const int height) override; + SP<ITexture> createTexture(bool opaque = false) override; + SP<ITexture> createTexture(uint32_t drmFormat, uint8_t* pixels, uint32_t stride, const Vector2D& size, bool keepDataCopy = false, bool opaque = false) override; + SP<ITexture> createTexture(const Aquamarine::SDMABUFAttrs&, bool opaque = false) override; + SP<ITexture> createTexture(const int width, const int height, unsigned char* const data) override; + SP<ITexture> createTexture(cairo_surface_t* cairo) override; + SP<ITexture> createTexture(std::span<const float> lut3D, size_t N) override; + bool explicitSyncSupported() override; + std::vector<SDRMFormat> getDRMFormats() override; + std::vector<uint64_t> getDRMFormatModifiers(DRMFormat format) override; + SP<IFramebuffer> 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<ITexture> blurFramebuffer(SP<IFramebuffer> 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(); + WP<IElementRenderer> elementRenderer() override; + + private: + void renderOffToMain(SP<IFramebuffer> off) override; + SP<IRenderbuffer> getOrCreateRenderbufferInternal(SP<Aquamarine::IBuffer> buffer, uint32_t fmt) override; + bool beginRenderInternal(PHLMONITOR pMonitor, CRegion& damage, bool simple = false) override; + bool beginFullFakeRenderInternal(PHLMONITOR pMonitor, CRegion& damage, SP<IFramebuffer> fb, bool simple = false) override; + void initRender() override; + bool initRenderBuffer(SP<Aquamarine::IBuffer> buffer, uint32_t fmt) override; + + SP<ITexture> getBlurTexture(PHLMONITORREF pMonitor) override; + + SP<IRenderbuffer> m_currentRenderbuffer; + UP<IElementRenderer> m_elementRenderer; + + friend class CHyprOpenGLImpl; + }; +}
\ No newline at end of file diff --git a/src/render/OpenGL.cpp b/src/render/OpenGL.cpp index 9aaa99d5..cfc3dc4e 100644 --- a/src/render/OpenGL.cpp +++ b/src/render/OpenGL.cpp @@ -60,6 +60,7 @@ using namespace Hyprutils::OS; using namespace NColorManagement; using namespace Render; +using namespace Render::GL; static inline void loadGLProc(void* pProc, const char* name) { void* proc = rc<void*>(eglGetProcAddress(name)); @@ -139,6 +140,12 @@ static int openRenderNode(int drmFd) { return renderFD; } +static ShaderFeatureFlags globalFeatures() { + return g_pHyprRenderer->m_renderData.pMonitor && g_pHyprRenderer->m_renderData.pMonitor->needsUnmodifiedCopy() && g_pHyprRenderer->m_renderData.currentFB->getMirrorTexture() ? + SH_FEAT_MIRROR : + 0; +} + void CHyprOpenGLImpl::initEGL(bool gbm) { std::vector<EGLint> attrs; if (m_exts.KHR_display_reference) { @@ -687,8 +694,7 @@ void CHyprOpenGLImpl::beginSimple(PHLMONITOR pMonitor, const CRegion& damage, SP m_fakeFrame = true; - g_pHyprRenderer->m_renderData.currentFB = FBO; - FBO->bind(); + g_pHyprRenderer->bindFB(FBO); m_offloadedFramebuffer = false; g_pHyprRenderer->m_renderData.mainFB = g_pHyprRenderer->m_renderData.currentFB; @@ -728,8 +734,8 @@ void CHyprOpenGLImpl::begin(PHLMONITOR pMonitor, const CRegion& damage_, SP<IFra if (!m_shadersInitialized) initShaders(); - 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 = g_pHyprRenderer->needsACopyFB(g_pHyprRenderer->m_renderData.pMonitor.lock()); + const bool HAS_MIRROR_FB = g_pHyprRenderer->m_renderData.pMonitor->resources()->hasMirrorFB(); + const bool NEEDS_COPY_FB = g_pHyprRenderer->m_renderData.pMonitor->needsACopyFB(); g_pHyprRenderer->m_renderData.transformDamage = true; if (HAS_MIRROR_FB != NEEDS_COPY_FB) { @@ -749,13 +755,28 @@ void CHyprOpenGLImpl::begin(PHLMONITOR pMonitor, const CRegion& damage_, SP<IFra applyScreenShader(*PSHADER); } - g_pHyprRenderer->m_renderData.pMonitor->m_offloadFB->bind(); - g_pHyprRenderer->m_renderData.currentFB = g_pHyprRenderer->m_renderData.pMonitor->m_offloadFB; - m_offloadedFramebuffer = true; + g_pHyprRenderer->bindFB(g_pHyprRenderer->m_renderData.pMonitor->resources()->getUnusedWorkBuffer()); + m_offloadedFramebuffer = true; g_pHyprRenderer->m_renderData.mainFB = g_pHyprRenderer->m_renderData.currentFB; g_pHyprRenderer->m_renderData.outFB = fb ? fb : dc<CHyprGLRenderer*>(g_pHyprRenderer.get())->m_currentRenderbuffer->getFB(); + if UNLIKELY (g_pHyprRenderer->m_renderData.pMonitor->needsUnmodifiedCopy() && !m_fakeFrame) { + if (!g_pHyprRenderer->m_renderData.pMonitor->resources()->m_mirrorTex) { + GLenum buffers[] = {GL_COLOR_ATTACHMENT0, GL_COLOR_ATTACHMENT1}; + glDrawBuffers(2, buffers); + g_pHyprRenderer->m_renderData.pMonitor->resources()->enableMirror(); + } + g_pHyprRenderer->m_renderData.mainFB->enableMirror(g_pHyprRenderer->m_renderData.pMonitor->resources()->m_mirrorTex); + } else { + if (g_pHyprRenderer->m_renderData.pMonitor->resources()->m_mirrorTex) { + GLenum buffers[] = {GL_COLOR_ATTACHMENT0}; + glDrawBuffers(1, buffers); + g_pHyprRenderer->m_renderData.pMonitor->resources()->disableMirror(); + } + g_pHyprRenderer->m_renderData.mainFB->disableMirror(); + } + g_pHyprRenderer->pushMonitorTransformEnabled(false); } @@ -786,19 +807,20 @@ 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 (g_pHyprRenderer->needsACopyFB(g_pHyprRenderer->m_renderData.pMonitor.lock()) && !m_fakeFrame) + if UNLIKELY (g_pHyprRenderer->m_renderData.pMonitor->needsACopyFB() && !m_fakeFrame) saveBufferForMirror(monbox); - g_pHyprRenderer->m_renderData.outFB->bind(); + const auto TEX = g_pHyprRenderer->m_renderData.currentFB->getTexture(); + g_pHyprRenderer->bindFB(g_pHyprRenderer->m_renderData.outFB); blend(false); const auto PRIMITIVE_BLOCKED = m_finalScreenShader->program() >= 1 || g_pHyprRenderer->m_crashingInProgress || g_pHyprRenderer->m_renderData.pMonitor->m_imageDescription->value() != SImageDescription{}; if LIKELY (!PRIMITIVE_BLOCKED || g_pHyprRenderer->m_renderMode != RENDER_MODE_NORMAL) - renderTexturePrimitive(g_pHyprRenderer->m_renderData.pMonitor->m_offloadFB->getTexture(), monbox); + renderTexturePrimitive(TEX, monbox); else // we need to use renderTexture if we do any CM whatsoever. - renderTexture(g_pHyprRenderer->m_renderData.pMonitor->m_offloadFB->getTexture(), monbox, {.finalMonitorCM = true}); + renderTexture(TEX, monbox, {.finalMonitorCM = true}); blend(true); @@ -807,39 +829,26 @@ void CHyprOpenGLImpl::end() { g_pHyprRenderer->popMonitorTransformEnabled(); } - // 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}); - } - // reset our data - m_renderData.pMonitor.reset(); g_pHyprRenderer->m_renderData.mouseZoomFactor = 1.f; g_pHyprRenderer->m_renderData.mouseZoomUseMouse = true; g_pHyprRenderer->m_renderData.blockScreenShader = false; - g_pHyprRenderer->m_renderData.currentFB = nullptr; - g_pHyprRenderer->m_renderData.mainFB = nullptr; - g_pHyprRenderer->m_renderData.outFB = nullptr; + g_pHyprRenderer->m_renderData.currentFB.reset(); + g_pHyprRenderer->m_renderData.mainFB.reset(); + g_pHyprRenderer->m_renderData.outFB.reset(); g_pHyprRenderer->popMonitorTransformEnabled(); - // if we dropped to offMain, release it now. - // if there is a plugin constantly using it, this might be a bit slow, - // but I haven't seen a single plugin yet use these, so it's better to drop a bit of vram. - if UNLIKELY (PMONITOR && PMONITOR->m_offMainFB && PMONITOR->m_offMainFB->isAllocated()) - PMONITOR->m_offMainFB->release(); + // invalidate our render FBs to signal to the driver we don't need them anymore + if (!g_pHyprRenderer->m_renderData.pMonitor->useFP16()) { // FIXME wtf? + g_pHyprRenderer->m_renderData.pMonitor->resources()->forEachUnusedFB( + [](const auto& fb) { + fb->bind(); + GLFB(fb)->invalidate({GL_STENCIL_ATTACHMENT, GL_COLOR_ATTACHMENT0}); + }, + false); + } + + m_renderData.pMonitor.reset(); static const auto GLDEBUG = CConfigValue<Hyprlang::INT>("debug:gl_debugging"); @@ -963,21 +972,6 @@ void CHyprOpenGLImpl::applyScreenShader(const std::string& path) { uniformRequireNoDamage(SHADER_POINTER_SHAPE_PREVIOUS, "pointer_shape_previous"); } -void CHyprOpenGLImpl::clear(const CHyprColor& color) { - RASSERT(g_pHyprRenderer->m_renderData.pMonitor, "Tried to render without begin()!"); - - TRACY_GPU_ZONE("RenderClear"); - - GLCALL(glClearColor(color.r, color.g, color.b, color.a)); - - if (!g_pHyprRenderer->m_renderData.damage.empty()) { - g_pHyprRenderer->m_renderData.damage.forEachRect([this](const auto& RECT) { - scissor(&RECT, g_pHyprRenderer->m_renderData.transformDamage); - glClear(GL_COLOR_BUFFER_BIT); - }); - } -} - void CHyprOpenGLImpl::blend(bool enabled) { if (enabled) { setCapStatus(GL_BLEND, true); @@ -1052,16 +1046,13 @@ 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 ? g_pHyprRenderer->m_renderData.pMonitor->m_blurFB->getTexture() : nullptr) : - g_pHyprRenderer->blurMainFramebuffer(data.blurA, &damage); - - g_pHyprRenderer->m_renderData.currentFB->bind(); + auto blurredBG = data.xray ? g_pHyprRenderer->m_renderData.pMonitor->resources()->m_blurFB->getTexture() : g_pHyprRenderer->blurMainFramebuffer(data.blurA, &damage); CBox MONITORBOX = {0, 0, g_pHyprRenderer->m_renderData.pMonitor->m_transformedSize.x, g_pHyprRenderer->m_renderData.pMonitor->m_transformedSize.y}; g_pHyprRenderer->pushMonitorTransformEnabled(true); const auto SAVEDRENDERMODIF = g_pHyprRenderer->m_renderData.renderModif; g_pHyprRenderer->m_renderData.renderModif = {}; // fix shit - renderTexture(POUTFB, MONITORBOX, + renderTexture(blurredBG, 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; @@ -1081,7 +1072,7 @@ void CHyprOpenGLImpl::renderRectWithDamageInternal(const CBox& box, const CHyprC const auto& glMatrix = g_pHyprRenderer->projectBoxToTarget(newBox); - auto shader = useShader(getShaderVariant(SH_FRAG_QUAD, data.round > 0 ? SH_FEAT_ROUNDING : 0)); + auto shader = useShader(getShaderVariant(SH_FRAG_QUAD, (data.round > 0 ? SH_FEAT_ROUNDING : 0) | globalFeatures())); shader->setUniformMatrix3fv(SHADER_PROJ, 1, GL_TRUE, glMatrix.getMatrix()); // premultiply the color as well as we don't work with straight alpha @@ -1144,28 +1135,9 @@ void CHyprOpenGLImpl::renderTexture(SP<ITexture> tex, const CBox& box, STextureR static std::map<std::pair<uint32_t, uint32_t>, std::array<GLfloat, 9>> primariesConversionCache; -static bool isSDR2HDR(const NColorManagement::SImageDescription& imageDescription, const NColorManagement::SImageDescription& targetImageDescription) { - // might be too strict - return (imageDescription.transferFunction == NColorManagement::CM_TRANSFER_FUNCTION_SRGB || - imageDescription.transferFunction == NColorManagement::CM_TRANSFER_FUNCTION_GAMMA22) && - (targetImageDescription.transferFunction == NColorManagement::CM_TRANSFER_FUNCTION_ST2084_PQ || - targetImageDescription.transferFunction == NColorManagement::CM_TRANSFER_FUNCTION_HLG); -} - -static bool isHDR2SDR(const NColorManagement::SImageDescription& imageDescription, const NColorManagement::SImageDescription& targetImageDescription) { - // might be too strict - return (imageDescription.transferFunction == NColorManagement::CM_TRANSFER_FUNCTION_ST2084_PQ || - imageDescription.transferFunction == NColorManagement::CM_TRANSFER_FUNCTION_HLG) && - (targetImageDescription.transferFunction == NColorManagement::CM_TRANSFER_FUNCTION_SRGB || - targetImageDescription.transferFunction == NColorManagement::CM_TRANSFER_FUNCTION_GAMMA22); -} - void CHyprOpenGLImpl::passCMUniforms(WP<CShader> shader, const NColorManagement::PImageDescription imageDescription, - const NColorManagement::PImageDescription targetImageDescription, bool modifySDR, float sdrMinLuminance, int sdrMaxLuminance) { - const auto settings = g_pHyprRenderer->getCMSettings(imageDescription, targetImageDescription, - g_pHyprRenderer->m_renderData.surface.valid() ? g_pHyprRenderer->m_renderData.surface.lock() : nullptr, modifySDR, - sdrMinLuminance, sdrMaxLuminance); - + const NColorManagement::PImageDescription targetImageDescription, bool modifySDR, float sdrMinLuminance, int sdrMaxLuminance, + const SCMSettings& settings) { shader->setUniformInt(SHADER_SOURCE_TF, settings.sourceTF); shader->setUniformInt(SHADER_TARGET_TF, settings.targetTF); shader->setUniformFloat2(SHADER_SRC_TF_RANGE, settings.srcTFRange.min, settings.srcTFRange.max); @@ -1209,6 +1181,14 @@ void CHyprOpenGLImpl::passCMUniforms(WP<CShader> shader, const NColorManagement: } } +void CHyprOpenGLImpl::passCMUniforms(WP<CShader> shader, const NColorManagement::PImageDescription imageDescription, + const NColorManagement::PImageDescription targetImageDescription, bool modifySDR, float sdrMinLuminance, int sdrMaxLuminance) { + const auto settings = g_pHyprRenderer->getCMSettings(imageDescription, targetImageDescription, + g_pHyprRenderer->m_renderData.surface.valid() ? g_pHyprRenderer->m_renderData.surface.lock() : nullptr, modifySDR, + sdrMinLuminance, sdrMaxLuminance); + passCMUniforms(shader, imageDescription, targetImageDescription, modifySDR, sdrMinLuminance, sdrMaxLuminance, settings); +} + void CHyprOpenGLImpl::passCMUniforms(WP<CShader> shader, const PImageDescription imageDescription) { passCMUniforms(shader, imageDescription, g_pHyprRenderer->workBufferImageDescription(), true, g_pHyprRenderer->m_renderData.pMonitor->m_sdrMinLuminance, g_pHyprRenderer->m_renderData.pMonitor->m_sdrMaxLuminance); @@ -1287,7 +1267,7 @@ WP<CShader> CHyprOpenGLImpl::renderToOutputInternal() { return shader; } -WP<CShader> CHyprOpenGLImpl::renderToFBInternal(const STextureRenderData& data, eTextureType texType, const CBox& newBox) { +WP<CShader> CHyprOpenGLImpl::renderToFBInternal(SP<ITexture> tex, const STextureRenderData& data, eTextureType texType, const CBox& newBox) { static const auto PPASS = CConfigValue<Hyprlang::INT>("render:cm_fs_passthrough"); static const auto PENABLECM = CConfigValue<Hyprlang::INT>("render:cm_enabled"); static auto PBLEND = CConfigValue<Hyprlang::INT>("render:use_shader_blur_blend"); @@ -1304,7 +1284,7 @@ WP<CShader> CHyprOpenGLImpl::renderToFBInternal(const STextureRenderData& data, case TEXTURE_RGBX: shaderFeatures &= ~SH_FEAT_RGBA; break; // TODO set correct features - case TEXTURE_EXTERNAL: shader = getShaderVariant(SH_FRAG_EXT, SH_FEAT_ROUNDING | SH_FEAT_DISCARD | SH_FEAT_TINT); break; // might be unused + case TEXTURE_EXTERNAL: shader = getShaderVariant(SH_FRAG_EXT, SH_FEAT_ROUNDING | SH_FEAT_DISCARD | SH_FEAT_TINT | globalFeatures()); break; // might be unused default: RASSERT(false, "tex->m_iTarget unsupported!"); } @@ -1320,13 +1300,19 @@ WP<CShader> CHyprOpenGLImpl::renderToFBInternal(const STextureRenderData& data, // chosenSdrEotf contains the valid eotf for this display const auto SOURCE_IMAGE_DESCRIPTION = [&] { + if (tex->m_imageDescription) + return tex->m_imageDescription; + // if valid CM surface, use that as a source if (g_pHyprRenderer->m_renderData.surface.valid() && g_pHyprRenderer->m_renderData.surface->m_colorManagement.valid()) return CImageDescription::from(g_pHyprRenderer->m_renderData.surface->m_colorManagement->imageDescription()); + if (data.cmBackToSRGB) + return g_pHyprRenderer->m_renderData.pMonitor->m_imageDescription; + // otherwise, if we are CM'ing back into source, use chosen, because that's what our work buffer is in // the same applies to the final monitor CM - if (data.cmBackToSRGB || data.finalMonitorCM) // NOLINTNEXTLINE + if (data.finalMonitorCM) // NOLINTNEXTLINE return WORK_BUFFER_IMAGE_DESCRIPTION; // otherwise, default @@ -1334,6 +1320,9 @@ WP<CShader> CHyprOpenGLImpl::renderToFBInternal(const STextureRenderData& data, }(); const auto TARGET_IMAGE_DESCRIPTION = [&] { + if (g_pHyprRenderer->m_renderData.currentFB->imageDescription()) + return g_pHyprRenderer->m_renderData.currentFB->imageDescription(); + // if we are CM'ing back, use default sRGB if (data.cmBackToSRGB) return DEFAULT_IMAGE_DESCRIPTION; @@ -1370,41 +1359,32 @@ WP<CShader> CHyprOpenGLImpl::renderToFBInternal(const STextureRenderData& data, shaderFeatures |= SH_FEAT_ROUNDING; if (!skipCM) { + const auto settings = g_pHyprRenderer->getCMSettings(SOURCE_IMAGE_DESCRIPTION, TARGET_IMAGE_DESCRIPTION, + g_pHyprRenderer->m_renderData.surface.valid() ? g_pHyprRenderer->m_renderData.surface.lock() : nullptr, true, + g_pHyprRenderer->m_renderData.pMonitor->m_sdrMinLuminance, g_pHyprRenderer->m_renderData.pMonitor->m_sdrMaxLuminance); + shaderFeatures |= SH_FEAT_CM; if (TARGET_IMAGE_DESCRIPTION->value().icc.present) shaderFeatures |= SH_FEAT_ICC; else { - const bool needsSDRmod = isSDR2HDR(SOURCE_IMAGE_DESCRIPTION->value(), TARGET_IMAGE_DESCRIPTION->value()); - const bool needsHDRmod = !needsSDRmod && isHDR2SDR(SOURCE_IMAGE_DESCRIPTION->value(), TARGET_IMAGE_DESCRIPTION->value()); - const float maxLuminance = needsHDRmod ? - SOURCE_IMAGE_DESCRIPTION->value().getTFMaxLuminance(-1) : - (SOURCE_IMAGE_DESCRIPTION->value().luminances.max > 0 ? SOURCE_IMAGE_DESCRIPTION->value().luminances.max : SOURCE_IMAGE_DESCRIPTION->value().luminances.reference); - const auto dstMaxLuminance = TARGET_IMAGE_DESCRIPTION->value().luminances.max > 0 ? TARGET_IMAGE_DESCRIPTION->value().luminances.max : 10000; - - if (maxLuminance >= dstMaxLuminance * 1.01) + if (settings.needsTonemap) shaderFeatures |= SH_FEAT_TONEMAP; - if (!data.finalMonitorCM && - (SOURCE_IMAGE_DESCRIPTION->value().transferFunction == CM_TRANSFER_FUNCTION_SRGB || - SOURCE_IMAGE_DESCRIPTION->value().transferFunction == CM_TRANSFER_FUNCTION_GAMMA22) && - TARGET_IMAGE_DESCRIPTION->value().transferFunction == CM_TRANSFER_FUNCTION_ST2084_PQ && - ((g_pHyprRenderer->m_renderData.pMonitor->m_sdrSaturation > 0 && g_pHyprRenderer->m_renderData.pMonitor->m_sdrSaturation != 1.0f) || - (g_pHyprRenderer->m_renderData.pMonitor->m_sdrBrightness > 0 && g_pHyprRenderer->m_renderData.pMonitor->m_sdrBrightness != 1.0f))) + if (!data.finalMonitorCM && settings.needsSDRmod) shaderFeatures |= SH_FEAT_SDR_MOD; } - } - if (!shader) - shader = getShaderVariant(SH_FRAG_SURFACE, shaderFeatures); - shader = useShader(shader); + if (!shader) + shader = getShaderVariant(SH_FRAG_SURFACE, shaderFeatures | globalFeatures()); + shader = useShader(shader); - if (!skipCM) { - if (data.finalMonitorCM || data.cmBackToSRGB) - passCMUniforms(shader, SOURCE_IMAGE_DESCRIPTION, TARGET_IMAGE_DESCRIPTION, true, g_pHyprRenderer->m_renderData.pMonitor->m_sdrMinLuminance, - g_pHyprRenderer->m_renderData.pMonitor->m_sdrMaxLuminance); - else - passCMUniforms(shader, SOURCE_IMAGE_DESCRIPTION); + passCMUniforms(shader, SOURCE_IMAGE_DESCRIPTION, TARGET_IMAGE_DESCRIPTION, true, g_pHyprRenderer->m_renderData.pMonitor->m_sdrMinLuminance, + g_pHyprRenderer->m_renderData.pMonitor->m_sdrMaxLuminance, settings); + } else { + if (!shader) + shader = getShaderVariant(SH_FRAG_SURFACE, shaderFeatures | globalFeatures()); + shader = useShader(shader); } shader->setUniformFloat(SHADER_ALPHA, alpha); @@ -1458,7 +1438,8 @@ WP<CShader> CHyprOpenGLImpl::renderToFBInternal(const STextureRenderData& data, void CHyprOpenGLImpl::renderTextureInternal(SP<ITexture> tex, const CBox& box, const STextureRenderData& data) { RASSERT(g_pHyprRenderer->m_renderData.pMonitor, "Tried to render texture without begin()!"); - RASSERT((tex->ok()), "Attempted to draw nullptr texture!"); + RASSERT(tex, "Attempted to draw nullptr texture!"); + RASSERT(tex->ok(), "Attempted to draw invalid texture!"); TRACY_GPU_ZONE("RenderTextureInternalWithDamage"); @@ -1493,7 +1474,7 @@ void CHyprOpenGLImpl::renderTextureInternal(SP<ITexture> tex, const CBox& box, c tex->setTexParameter(GL_TEXTURE_MIN_FILTER, tex->minFilter); } - auto shader = renderToOutput ? renderToOutputInternal() : renderToFBInternal(data, tex->m_type, newBox); + auto shader = renderToOutput ? renderToOutputInternal() : renderToFBInternal(tex, data, tex->m_type, newBox); shader->setUniformMatrix3fv(SHADER_PROJ, 1, GL_TRUE, glMatrix.getMatrix()); shader->setUniformInt(SHADER_TEX, 0); @@ -1608,7 +1589,7 @@ void CHyprOpenGLImpl::renderTextureMatte(SP<ITexture> tex, const CBox& box, SP<I // get transform const auto& glMatrix = g_pHyprRenderer->projectBoxToTarget(newBox); - auto shader = useShader(getShaderVariant(SH_FRAG_MATTE)); + auto shader = useShader(getShaderVariant(SH_FRAG_MATTE, globalFeatures())); shader->setUniformMatrix3fv(SHADER_PROJ, 1, GL_TRUE, glMatrix.getMatrix()); shader->setUniformInt(SHADER_TEX, 0); shader->setUniformInt(SHADER_ALPHA_MATTE, 1); @@ -1665,8 +1646,8 @@ SP<IFramebuffer> CHyprOpenGLImpl::blurFramebufferWithDamage(float a, CRegion* or damage.expand(std::clamp(*PBLURSIZE, sc<int64_t>(1), sc<int64_t>(40)) * pow(2, BLUR_PASSES)); // helper - const auto PMIRRORFB = g_pHyprRenderer->m_renderData.pMonitor->m_mirrorFB; - const auto PMIRRORSWAPFB = g_pHyprRenderer->m_renderData.pMonitor->m_mirrorSwapFB; + const auto PMIRRORFB = g_pHyprRenderer->m_renderData.pMonitor->resources()->getUnusedWorkBuffer(); + const auto PMIRRORSWAPFB = g_pHyprRenderer->m_renderData.pMonitor->resources()->getUnusedWorkBuffer(); auto currentRenderToFB = PMIRRORFB; @@ -1692,15 +1673,15 @@ SP<IFramebuffer> 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, m_renderData.pMonitor->m_imageDescription, DEFAULT_IMAGE_DESCRIPTION); + passCMUniforms(shader, g_pHyprRenderer->workBufferImageDescription(), DEFAULT_IMAGE_DESCRIPTION); shader->setUniformFloat(SHADER_SDR_SATURATION, m_renderData.pMonitor->m_sdrSaturation > 0 && - m_renderData.pMonitor->m_imageDescription->value().transferFunction == NColorManagement::CM_TRANSFER_FUNCTION_ST2084_PQ ? + g_pHyprRenderer->workBufferImageDescription()->value().transferFunction == NColorManagement::CM_TRANSFER_FUNCTION_ST2084_PQ ? m_renderData.pMonitor->m_sdrSaturation : 1.0f); shader->setUniformFloat(SHADER_SDR_BRIGHTNESS, m_renderData.pMonitor->m_sdrBrightness > 0 && - m_renderData.pMonitor->m_imageDescription->value().transferFunction == NColorManagement::CM_TRANSFER_FUNCTION_ST2084_PQ ? + g_pHyprRenderer->workBufferImageDescription()->value().transferFunction == NColorManagement::CM_TRANSFER_FUNCTION_ST2084_PQ ? m_renderData.pMonitor->m_sdrBrightness : 1.0f); } else @@ -1808,7 +1789,24 @@ SP<IFramebuffer> CHyprOpenGLImpl::blurFramebufferWithDamage(float a, CRegion* or currentTex->setTexParameter(GL_TEXTURE_MIN_FILTER, GL_LINEAR); - auto shader = useShader(getShaderVariant(SH_FRAG_BLURFINISH)); + // From FB to sRGB + const bool skipCM = !m_cmSupported || g_pHyprRenderer->workBufferImageDescription()->id() == DEFAULT_IMAGE_DESCRIPTION->id(); + if (!skipCM) { + shader = useShader(getShaderVariant(SH_FRAG_BLURFINISH, SH_FEAT_CM)); + passCMUniforms(shader, DEFAULT_IMAGE_DESCRIPTION, g_pHyprRenderer->workBufferImageDescription()); + shader->setUniformFloat(SHADER_SDR_SATURATION, + m_renderData.pMonitor->m_sdrSaturation > 0 && + g_pHyprRenderer->workBufferImageDescription()->value().transferFunction == NColorManagement::CM_TRANSFER_FUNCTION_ST2084_PQ ? + m_renderData.pMonitor->m_sdrSaturation : + 1.0f); + shader->setUniformFloat(SHADER_SDR_BRIGHTNESS, + m_renderData.pMonitor->m_sdrBrightness > 0 && + g_pHyprRenderer->workBufferImageDescription()->value().transferFunction == NColorManagement::CM_TRANSFER_FUNCTION_ST2084_PQ ? + m_renderData.pMonitor->m_sdrBrightness : + 1.0f); + } else + shader = useShader(getShaderVariant(SH_FRAG_BLURFINISH)); + shader->setUniformMatrix3fv(SHADER_PROJ, 1, GL_TRUE, glMatrix.getMatrix()); shader->setUniformFloat(SHADER_NOISE, *PBLURNOISE); shader->setUniformFloat(SHADER_BRIGHTNESS, *PBLURBRIGHTNESS); @@ -2078,10 +2076,10 @@ void CHyprOpenGLImpl::renderBorder(const CBox& box, const Config::CGradientValue const bool IS_ICC = g_pHyprRenderer->workBufferImageDescription()->value().icc.present; const bool skipCM = !m_cmSupported || g_pHyprRenderer->workBufferImageDescription()->id() == DEFAULT_IMAGE_DESCRIPTION->id(); if (!skipCM) { - shader = useShader(getShaderVariant(SH_FRAG_BORDER1, SH_FEAT_ROUNDING | SH_FEAT_CM | (IS_ICC ? SH_FEAT_ICC : SH_FEAT_TONEMAP | SH_FEAT_SDR_MOD))); + shader = useShader(getShaderVariant(SH_FRAG_BORDER1, SH_FEAT_ROUNDING | SH_FEAT_CM | (IS_ICC ? SH_FEAT_ICC : SH_FEAT_TONEMAP | SH_FEAT_SDR_MOD) | globalFeatures())); passCMUniforms(shader, DEFAULT_IMAGE_DESCRIPTION); } else - shader = useShader(getShaderVariant(SH_FRAG_BORDER1, SH_FEAT_ROUNDING)); + shader = useShader(getShaderVariant(SH_FRAG_BORDER1, SH_FEAT_ROUNDING | globalFeatures())); shader->setUniformMatrix3fv(SHADER_PROJ, 1, GL_TRUE, glMatrix.getMatrix()); shader->setUniform4fv(SHADER_GRADIENT, grad.m_colorsOkLabA.size() / 4, grad.m_colorsOkLabA); @@ -2163,10 +2161,10 @@ void CHyprOpenGLImpl::renderBorder(const CBox& box, const Config::CGradientValue const bool IS_ICC = g_pHyprRenderer->workBufferImageDescription()->value().icc.present; const bool skipCM = !m_cmSupported || g_pHyprRenderer->workBufferImageDescription()->id() == DEFAULT_IMAGE_DESCRIPTION->id(); if (!skipCM) { - shader = useShader(getShaderVariant(SH_FRAG_BORDER1, SH_FEAT_ROUNDING | SH_FEAT_CM | (IS_ICC ? SH_FEAT_ICC : SH_FEAT_TONEMAP | SH_FEAT_SDR_MOD))); + shader = useShader(getShaderVariant(SH_FRAG_BORDER1, SH_FEAT_ROUNDING | SH_FEAT_CM | (IS_ICC ? SH_FEAT_ICC : SH_FEAT_TONEMAP | SH_FEAT_SDR_MOD) | globalFeatures())); passCMUniforms(shader, DEFAULT_IMAGE_DESCRIPTION); } else - shader = useShader(getShaderVariant(SH_FRAG_BORDER1, SH_FEAT_ROUNDING)); + shader = useShader(getShaderVariant(SH_FRAG_BORDER1, SH_FEAT_ROUNDING | globalFeatures())); shader->setUniformMatrix3fv(SHADER_PROJ, 1, GL_TRUE, glMatrix.getMatrix()); shader->setUniform4fv(SHADER_GRADIENT, grad1.m_colorsOkLabA.size() / 4, grad1.m_colorsOkLabA); @@ -2240,7 +2238,7 @@ void CHyprOpenGLImpl::renderRoundedShadow(const CBox& box, int round, float roun const bool IS_ICC = g_pHyprRenderer->workBufferImageDescription()->value().icc.present; const bool skipCM = !m_cmSupported || g_pHyprRenderer->workBufferImageDescription()->id() == DEFAULT_IMAGE_DESCRIPTION->id(); - auto shader = useShader(getShaderVariant(SH_FRAG_SHADOW, skipCM ? 0 : SH_FEAT_CM | (IS_ICC ? SH_FEAT_ICC : SH_FEAT_TONEMAP | SH_FEAT_SDR_MOD))); + auto shader = useShader(getShaderVariant(SH_FRAG_SHADOW, skipCM ? 0 : SH_FEAT_CM | (IS_ICC ? SH_FEAT_ICC : SH_FEAT_TONEMAP | SH_FEAT_SDR_MOD) | globalFeatures())); if (!skipCM) passCMUniforms(shader, DEFAULT_IMAGE_DESCRIPTION); @@ -2347,16 +2345,17 @@ void CHyprOpenGLImpl::renderInnerGlow(const CBox& box, int round, float rounding } void CHyprOpenGLImpl::saveBufferForMirror(const CBox& box) { - auto& m_renderData = g_pHyprRenderer->m_renderData; - if (!g_pHyprRenderer->m_renderData.pMonitor->m_monitorMirrorFB->isAllocated()) - 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(); + const auto TEX = g_pHyprRenderer->m_renderData.pMonitor->resources()->m_mirrorTex ? g_pHyprRenderer->m_renderData.pMonitor->resources()->m_mirrorTex : + g_pHyprRenderer->m_renderData.currentFB->getTexture(); + if (!TEX) { + Log::logger->log(Log::ERR, "Invalid source texture for mirror"); + return; + } + auto guard = g_pHyprRenderer->bindTempFB(g_pHyprRenderer->m_renderData.pMonitor->resources()->mirrorFB()); blend(false); - renderTexture(g_pHyprRenderer->m_renderData.currentFB->getTexture(), box, + renderTexture(TEX, box, STextureRenderData{ .damage = &g_pHyprRenderer->m_renderData.finalDamage, .a = 1.F, @@ -2367,8 +2366,6 @@ void CHyprOpenGLImpl::saveBufferForMirror(const CBox& box) { }); blend(true); - - g_pHyprRenderer->m_renderData.currentFB->bind(); } WP<CShader> CHyprOpenGLImpl::useShader(WP<CShader> prog) { @@ -2397,7 +2394,7 @@ void CHyprOpenGLImpl::destroyMonitorResources(PHLMONITORREF pMonitor) { Log::logger->log(Log::DEBUG, "Monitor {} -> destroyed all render data", pMonitor->m_name); } -void CHyprOpenGLImpl::renderOffToMain(IFramebuffer* off) { +void CHyprOpenGLImpl::renderOffToMain(SP<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); } @@ -2579,14 +2576,6 @@ CEGLSync::~CEGLSync() { Log::logger->log(Log::ERR, "eglDestroySyncKHR failed"); } -CFileDescriptor& CEGLSync::fd() { - return m_fd; -} - -CFileDescriptor&& CEGLSync::takeFd() { - return std::move(m_fd); -} - bool CEGLSync::isValid() { return m_valid && m_sync != EGL_NO_SYNC_KHR && m_fd.isValid(); } diff --git a/src/render/OpenGL.hpp b/src/render/OpenGL.hpp index bbca01c0..78518ffe 100644 --- a/src/render/OpenGL.hpp +++ b/src/render/OpenGL.hpp @@ -16,6 +16,8 @@ #include <cairo/cairo.h> +#include "render/SyncFDManager.hpp" +#include "types.hpp" #include "Shader.hpp" #include "Texture.hpp" #include "Framebuffer.hpp" @@ -40,343 +42,318 @@ #define GLFB(ifb) dc<CGLFramebuffer*>(ifb.get()) struct gbm_device; -class IHyprRenderer; - +namespace Render { + class IHyprRenderer; +} namespace Config { class CGradientValueData; } -struct SVertex { - float x, y; // position - float u, v; // uv -}; - -constexpr std::array<SVertex, 4> fullVerts = {{ - {0.0f, 0.0f, 0.0f, 0.0f}, // top-left - {0.0f, 1.0f, 0.0f, 1.0f}, // bottom-left - {1.0f, 0.0f, 1.0f, 0.0f}, // top-right - {1.0f, 1.0f, 1.0f, 1.0f}, // bottom-right -}}; - -inline const float fanVertsFull[] = {-1.0f, -1.0f, 1.0f, -1.0f, 1.0f, 1.0f, -1.0f, 1.0f}; - -struct SRenderModifData { - enum eRenderModifType : uint8_t { - RMOD_TYPE_SCALE, /* scale by a float */ - RMOD_TYPE_SCALECENTER, /* scale by a float from the center */ - RMOD_TYPE_TRANSLATE, /* translate by a Vector2D */ - RMOD_TYPE_ROTATE, /* rotate by a float in rad from top left */ - RMOD_TYPE_ROTATECENTER, /* rotate by a float in rad from center */ +namespace Render::GL { + + struct SVertex { + float x, y; // position + float u, v; // uv + }; + + constexpr std::array<SVertex, 4> fullVerts = {{ + {0.0f, 0.0f, 0.0f, 0.0f}, // top-left + {0.0f, 1.0f, 0.0f, 1.0f}, // bottom-left + {1.0f, 0.0f, 1.0f, 0.0f}, // top-right + {1.0f, 1.0f, 1.0f, 1.0f}, // bottom-right + }}; + + inline const float fanVertsFull[] = {-1.0f, -1.0f, 1.0f, -1.0f, 1.0f, 1.0f, -1.0f, 1.0f}; + + enum eMonitorRenderFBs : uint8_t { + FB_MONITOR_RENDER_MAIN = 0, + FB_MONITOR_RENDER_CURRENT = 1, + FB_MONITOR_RENDER_OUT = 2, }; - std::vector<std::pair<eRenderModifType, std::any>> modifs; - - void applyToBox(CBox& box); - void applyToRegion(CRegion& rg); - float combinedScale(); - - bool enabled = true; -}; - -enum eMonitorRenderFBs : uint8_t { - FB_MONITOR_RENDER_MAIN = 0, - FB_MONITOR_RENDER_CURRENT = 1, - FB_MONITOR_RENDER_OUT = 2, -}; - -enum eMonitorExtraRenderFBs : uint8_t { - FB_MONITOR_RENDER_EXTRA_OFFLOAD = 0, - FB_MONITOR_RENDER_EXTRA_MIRROR, - FB_MONITOR_RENDER_EXTRA_MIRROR_SWAP, - FB_MONITOR_RENDER_EXTRA_OFF_MAIN, - FB_MONITOR_RENDER_EXTRA_MONITOR_MIRROR, - FB_MONITOR_RENDER_EXTRA_BLUR, -}; - -struct SFragShaderDesc { - Render::ePreparedFragmentShader id; - const char* file; -}; - -struct SPreparedShaders { - // SPreparedShaders() { - // for (auto& f : frag) { - // f = makeShared<CShader>(); - // } - // } - - std::string TEXVERTSRC; - std::string TEXVERTSRC320; - // std::array<SP<CShader>, SH_FRAG_LAST> frag; - // std::map<uint8_t, SP<CShader>> fragVariants; - std::array<std::map<Render::ShaderFeatureFlags, SP<CShader>>, Render::SH_FRAG_LAST> fragVariants; -}; - -struct SCurrentRenderData { - PHLMONITORREF pMonitor; - Mat3x3 projection; - Mat3x3 savedProjection; - Mat3x3 monitorProjection; - - SP<IFramebuffer> currentFB = nullptr; // current rendering to - SP<IFramebuffer> mainFB = nullptr; // main to render to - SP<IFramebuffer> outFB = nullptr; // out to render to (if offloaded, etc) - - CRegion damage; - CRegion finalDamage; // damage used for funal off -> main - - SRenderModifData renderModif; - float mouseZoomFactor = 1.f; - bool mouseZoomUseMouse = true; // true by default - bool useNearestNeighbor = false; - bool blockScreenShader = false; - bool simplePass = false; - bool transformDamage = true; - bool noSimplify = false; - - Vector2D primarySurfaceUVTopLeft = Vector2D(-1, -1); - Vector2D primarySurfaceUVBottomRight = Vector2D(-1, -1); - - CBox clipBox = {}; // scaled coordinates - CRegion clipRegion; - - uint32_t discardMode = DISCARD_OPAQUE; - float discardOpacity = 0.f; - - PHLLSREF currentLS; - PHLWINDOWREF currentWindow; - WP<CWLSurfaceResource> surface; -}; - -class CEGLSync { - public: - static UP<CEGLSync> create(); - - ~CEGLSync(); - - Hyprutils::OS::CFileDescriptor& fd(); - Hyprutils::OS::CFileDescriptor&& takeFd(); - bool isValid(); - - private: - CEGLSync() = default; - - Hyprutils::OS::CFileDescriptor m_fd; - EGLSyncKHR m_sync = EGL_NO_SYNC_KHR; - bool m_valid = false; - - friend class CHyprOpenGLImpl; -}; - -class CHyprOpenGLImpl { - public: - CHyprOpenGLImpl(); - ~CHyprOpenGLImpl(); - - struct SRectRenderData { - const CRegion* damage = nullptr; - int round = 0; - float roundingPower = 2.F; - bool blur = false; - float blurA = 1.F; - bool xray = false; + enum eMonitorExtraRenderFBs : uint8_t { + FB_MONITOR_RENDER_EXTRA_OFFLOAD = 0, + FB_MONITOR_RENDER_EXTRA_MIRROR, + FB_MONITOR_RENDER_EXTRA_MIRROR_SWAP, + FB_MONITOR_RENDER_EXTRA_OFF_MAIN, + FB_MONITOR_RENDER_EXTRA_MONITOR_MIRROR, + FB_MONITOR_RENDER_EXTRA_BLUR, }; - struct STextureRenderData { - bool blur = false; - float blurA = 1.F, overallA = 1.F; - bool blockBlurOptimization = false; - SP<ITexture> blurredBG; - - const CRegion* damage = nullptr; - SP<CWLSurfaceResource> 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 finalMonitorCM = false; - SP<CMonitor> cmBackToSRGBSource; - - uint32_t discardMode = DISCARD_OPAQUE; - float discardOpacity = 0.f; - - CRegion clipRegion; - PHLLSREF currentLS; - - Vector2D primarySurfaceUVTopLeft = Vector2D(-1, -1); - Vector2D primarySurfaceUVBottomRight = Vector2D(-1, -1); + struct SFragShaderDesc { + Render::ePreparedFragmentShader id; + const char* file; }; - struct SBorderRenderData { - int round = 0; - float roundingPower = 2.F; - int borderSize = 1; - float a = 1.0; - int outerRound = -1; /* use round */ + struct SPreparedShaders { + std::string TEXVERTSRC; + std::string TEXVERTSRC320; + std::array<std::map<Render::ShaderFeatureFlags, SP<CShader>>, Render::SH_FRAG_LAST> fragVariants; }; - void makeEGLCurrent(); - void begin(PHLMONITOR, const CRegion& damage, SP<IFramebuffer> fb = nullptr, std::optional<CRegion> finalDamage = {}); - void beginSimple(PHLMONITOR, const CRegion& damage, SP<IRenderbuffer> rb = nullptr, SP<IFramebuffer> fb = nullptr); - void end(); - - void renderRect(const CBox&, const CHyprColor&, SRectRenderData data); - void renderTexture(SP<ITexture>, const CBox&, STextureRenderData data); - void renderRoundedShadow(const CBox&, int round, float roundingPower, int range, const CHyprColor& color, float a = 1.0); - void renderInnerGlow(const CBox&, int round, float roundingPower, int range, const CHyprColor& color, int glowPower, float a = 1.0); - void renderBorder(const CBox&, const Config::CGradientValueData&, SBorderRenderData data); - void renderBorder(const CBox&, const Config::CGradientValueData&, const Config::CGradientValueData&, float lerp, SBorderRenderData data); - void renderTextureMatte(SP<ITexture> tex, const CBox& pBox, SP<IFramebuffer> matte); - void renderTexturePrimitive(SP<ITexture> tex, const CBox& box); - - 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 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 preRender(PHLMONITOR); - - void saveBufferForMirror(const CBox&); - - void applyScreenShader(const std::string& path); - - void renderOffToMain(IFramebuffer* off); - - std::vector<SDRMFormat> getDRMFormats(); - std::vector<uint64_t> getDRMFormatModifiers(DRMFormat format); - EGLImageKHR createEGLImage(const Aquamarine::SDMABUFAttrs& attrs); - - bool initShaders(const std::string& path = ""); - - WP<CShader> useShader(WP<CShader> prog); - - bool explicitSyncSupported(); - WP<CShader> getShaderVariant(Render::ePreparedFragmentShader frag, Render::ShaderFeatureFlags features = 0); - - bool m_shadersInitialized = false; - SP<SPreparedShaders> m_shaders; - - Hyprutils::OS::CFileDescriptor m_gbmFD; - gbm_device* m_gbmDevice = nullptr; - EGLContext m_eglContext = nullptr; - EGLDisplay m_eglDisplay = nullptr; - EGLDeviceEXT m_eglDevice = nullptr; - - std::map<PHLMONITORREF, SP<IFramebuffer>> m_monitorBGFBs; - - struct { - PFNGLEGLIMAGETARGETRENDERBUFFERSTORAGEOESPROC glEGLImageTargetRenderbufferStorageOES = nullptr; - PFNGLEGLIMAGETARGETTEXTURE2DOESPROC glEGLImageTargetTexture2DOES = nullptr; - PFNEGLCREATEIMAGEKHRPROC eglCreateImageKHR = nullptr; - PFNEGLDESTROYIMAGEKHRPROC eglDestroyImageKHR = nullptr; - PFNEGLQUERYDMABUFFORMATSEXTPROC eglQueryDmaBufFormatsEXT = nullptr; - PFNEGLQUERYDMABUFMODIFIERSEXTPROC eglQueryDmaBufModifiersEXT = nullptr; - PFNEGLGETPLATFORMDISPLAYEXTPROC eglGetPlatformDisplayEXT = nullptr; - PFNEGLDEBUGMESSAGECONTROLKHRPROC eglDebugMessageControlKHR = nullptr; - PFNEGLQUERYDEVICESEXTPROC eglQueryDevicesEXT = nullptr; - PFNEGLQUERYDEVICESTRINGEXTPROC eglQueryDeviceStringEXT = nullptr; - PFNEGLQUERYDISPLAYATTRIBEXTPROC eglQueryDisplayAttribEXT = nullptr; - PFNEGLCREATESYNCKHRPROC eglCreateSyncKHR = nullptr; - PFNEGLDESTROYSYNCKHRPROC eglDestroySyncKHR = nullptr; - PFNEGLDUPNATIVEFENCEFDANDROIDPROC eglDupNativeFenceFDANDROID = nullptr; - PFNEGLWAITSYNCKHRPROC eglWaitSyncKHR = nullptr; - } m_proc; - - struct { - bool EXT_read_format_bgra = false; - bool EXT_image_dma_buf_import = false; - bool EXT_image_dma_buf_import_modifiers = false; - bool KHR_context_flush_control = false; - bool KHR_display_reference = false; - bool IMG_context_priority = false; - bool EXT_create_context_robustness = false; - bool EGL_ANDROID_native_fence_sync_ext = false; - } m_exts; - - enum eEGLContextVersion : uint8_t { - EGL_CONTEXT_GLES_2_0 = 0, - EGL_CONTEXT_GLES_3_0, - EGL_CONTEXT_GLES_3_2, + struct SCurrentRenderData { + PHLMONITORREF pMonitor; + Mat3x3 projection; + Mat3x3 savedProjection; + Mat3x3 monitorProjection; + + SP<IFramebuffer> currentFB = nullptr; // current rendering to + SP<IFramebuffer> mainFB = nullptr; // main to render to + SP<IFramebuffer> outFB = nullptr; // out to render to (if offloaded, etc) + + CRegion damage; + CRegion finalDamage; // damage used for funal off -> main + + Render::SRenderModifData renderModif; + float mouseZoomFactor = 1.f; + bool mouseZoomUseMouse = true; // true by default + bool useNearestNeighbor = false; + bool blockScreenShader = false; + bool simplePass = false; + bool transformDamage = true; + bool noSimplify = false; + + Vector2D primarySurfaceUVTopLeft = Vector2D(-1, -1); + Vector2D primarySurfaceUVBottomRight = Vector2D(-1, -1); + + CBox clipBox = {}; // scaled coordinates + CRegion clipRegion; + + uint32_t discardMode = DISCARD_OPAQUE; + float discardOpacity = 0.f; + + PHLLSREF currentLS; + PHLWINDOWREF currentWindow; + WP<CWLSurfaceResource> surface; }; - eEGLContextVersion m_eglContextVersion = EGL_CONTEXT_GLES_3_2; + class CEGLSync : public ISyncFDManager { + public: + static UP<CEGLSync> create(); + ~CEGLSync() override; + + bool isValid() override; - enum eCachedCapStatus : uint8_t { - CAP_STATUS_BLEND = 0, - CAP_STATUS_SCISSOR_TEST, - CAP_STATUS_STENCIL_TEST, - CAP_STATUS_END + private: + CEGLSync() : ISyncFDManager() {}; + + EGLSyncKHR m_sync = EGL_NO_SYNC_KHR; + + friend class CHyprOpenGLImpl; }; - private: - struct { - GLint x = 0; - GLint y = 0; - GLsizei width = 0; - GLsizei height = 0; - } m_lastViewport; - - std::array<bool, CAP_STATUS_END> m_capStatus = {}; - - std::vector<SDRMFormat> m_drmFormats; - bool m_hasModifiers = false; - - int m_drmFD = -1; - std::string m_extensions; - - bool m_fakeFrame = false; - bool m_applyFinalShader = false; - bool m_blend = false; - bool m_offloadedFramebuffer = false; - bool m_cmSupported = true; - - SP<CShader> m_finalScreenShader; - GLuint m_currentProgram = 0; - - void initDRMFormats(); - void initEGL(bool gbm); - EGLDeviceEXT eglDeviceFromDRMFD(int drmFD); - - // for the final shader - std::array<CTimer, POINTER_PRESSED_HISTORY_LENGTH> m_pressedHistoryTimers = {}; - std::array<Vector2D, POINTER_PRESSED_HISTORY_LENGTH> m_pressedHistoryPositions = {}; - GLint m_pressedHistoryKilled = 0; - GLint m_pressedHistoryTouched = 0; - - // - std::optional<std::vector<uint64_t>> getModsForFormat(EGLint format); - - // returns the out FB, can be either Mirror or MirrorSwap - SP<IFramebuffer> blurFramebufferWithDamage(float a, CRegion* damage, CGLFramebuffer& source); - - void passCMUniforms(WP<CShader>, const NColorManagement::PImageDescription imageDescription, const NColorManagement::PImageDescription targetImageDescription, - bool modifySDR = false, float sdrMinLuminance = -1.0f, int sdrMaxLuminance = -1); - void passCMUniforms(WP<CShader>, const NColorManagement::PImageDescription imageDescription); - 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); - WP<CShader> renderToOutputInternal(); - WP<CShader> renderToFBInternal(const STextureRenderData& data, eTextureType texType, const CBox& newBox); - void renderTextureInternal(SP<ITexture>, const CBox&, const STextureRenderData& data); - void renderTextureWithBlurInternal(SP<ITexture>, const CBox&, const STextureRenderData& data); - - friend class IHyprRenderer; - friend class CHyprGLRenderer; - friend class CTexPassElement; - friend class CPreBlurElement; - friend class CSurfacePassElement; -}; - -inline UP<CHyprOpenGLImpl> g_pHyprOpenGL; + class CHyprOpenGLImpl { + public: + CHyprOpenGLImpl(); + ~CHyprOpenGLImpl(); + + struct SRectRenderData { + const CRegion* damage = nullptr; + int round = 0; + float roundingPower = 2.F; + bool blur = false; + float blurA = 1.F; + bool xray = false; + }; + + struct STextureRenderData { + bool blur = false; + float blurA = 1.F, overallA = 1.F; + bool blockBlurOptimization = false; + SP<ITexture> blurredBG; + + const CRegion* damage = nullptr; + SP<CWLSurfaceResource> 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 finalMonitorCM = false; + SP<CMonitor> cmBackToSRGBSource; + + 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 { + int round = 0; + float roundingPower = 2.F; + int borderSize = 1; + float a = 1.0; + int outerRound = -1; /* use round */ + }; + + void makeEGLCurrent(); + void begin(PHLMONITOR, const CRegion& damage, SP<IFramebuffer> fb = nullptr, std::optional<CRegion> finalDamage = {}); + void beginSimple(PHLMONITOR, const CRegion& damage, SP<IRenderbuffer> rb = nullptr, SP<IFramebuffer> fb = nullptr); + void end(); + + void renderRect(const CBox&, const CHyprColor&, SRectRenderData data); + void renderTexture(SP<ITexture>, const CBox&, STextureRenderData data); + void renderRoundedShadow(const CBox&, int round, float roundingPower, int range, const CHyprColor& color, float a = 1.0); + void renderInnerGlow(const CBox&, int round, float roundingPower, int range, const CHyprColor& color, int glowPower, float a = 1.0); + void renderBorder(const CBox&, const Config::CGradientValueData&, SBorderRenderData data); + void renderBorder(const CBox&, const Config::CGradientValueData&, const Config::CGradientValueData&, float lerp, SBorderRenderData data); + void renderTextureMatte(SP<ITexture> tex, const CBox& pBox, SP<IFramebuffer> matte); + void renderTexturePrimitive(SP<ITexture> tex, const CBox& box); + + void setViewport(GLint x, GLint y, GLsizei width, GLsizei height); + void setCapStatus(int cap, bool status); + + void blend(bool enabled); + + 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 preRender(PHLMONITOR); + + void saveBufferForMirror(const CBox&); + + void applyScreenShader(const std::string& path); + + void renderOffToMain(SP<IFramebuffer> off); + + std::vector<SDRMFormat> getDRMFormats(); + std::vector<uint64_t> getDRMFormatModifiers(DRMFormat format); + EGLImageKHR createEGLImage(const Aquamarine::SDMABUFAttrs& attrs); + + bool initShaders(const std::string& path = ""); + + WP<CShader> useShader(WP<CShader> prog); + + bool explicitSyncSupported(); + WP<CShader> getShaderVariant(Render::ePreparedFragmentShader frag, Render::ShaderFeatureFlags features = 0); + + bool m_shadersInitialized = false; + SP<SPreparedShaders> m_shaders; + + Hyprutils::OS::CFileDescriptor m_gbmFD; + gbm_device* m_gbmDevice = nullptr; + EGLContext m_eglContext = nullptr; + EGLDisplay m_eglDisplay = nullptr; + EGLDeviceEXT m_eglDevice = nullptr; + + std::map<PHLMONITORREF, SP<IFramebuffer>> m_monitorBGFBs; + + struct { + PFNGLEGLIMAGETARGETRENDERBUFFERSTORAGEOESPROC glEGLImageTargetRenderbufferStorageOES = nullptr; + PFNGLEGLIMAGETARGETTEXTURE2DOESPROC glEGLImageTargetTexture2DOES = nullptr; + PFNEGLCREATEIMAGEKHRPROC eglCreateImageKHR = nullptr; + PFNEGLDESTROYIMAGEKHRPROC eglDestroyImageKHR = nullptr; + PFNEGLQUERYDMABUFFORMATSEXTPROC eglQueryDmaBufFormatsEXT = nullptr; + PFNEGLQUERYDMABUFMODIFIERSEXTPROC eglQueryDmaBufModifiersEXT = nullptr; + PFNEGLGETPLATFORMDISPLAYEXTPROC eglGetPlatformDisplayEXT = nullptr; + PFNEGLDEBUGMESSAGECONTROLKHRPROC eglDebugMessageControlKHR = nullptr; + PFNEGLQUERYDEVICESEXTPROC eglQueryDevicesEXT = nullptr; + PFNEGLQUERYDEVICESTRINGEXTPROC eglQueryDeviceStringEXT = nullptr; + PFNEGLQUERYDISPLAYATTRIBEXTPROC eglQueryDisplayAttribEXT = nullptr; + PFNEGLCREATESYNCKHRPROC eglCreateSyncKHR = nullptr; + PFNEGLDESTROYSYNCKHRPROC eglDestroySyncKHR = nullptr; + PFNEGLDUPNATIVEFENCEFDANDROIDPROC eglDupNativeFenceFDANDROID = nullptr; + PFNEGLWAITSYNCKHRPROC eglWaitSyncKHR = nullptr; + } m_proc; + + struct { + bool EXT_read_format_bgra = false; + bool EXT_image_dma_buf_import = false; + bool EXT_image_dma_buf_import_modifiers = false; + bool KHR_context_flush_control = false; + bool KHR_display_reference = false; + bool IMG_context_priority = false; + bool EXT_create_context_robustness = false; + bool EGL_ANDROID_native_fence_sync_ext = false; + } m_exts; + + enum eEGLContextVersion : uint8_t { + EGL_CONTEXT_GLES_2_0 = 0, + EGL_CONTEXT_GLES_3_0, + EGL_CONTEXT_GLES_3_2, + }; + + eEGLContextVersion m_eglContextVersion = EGL_CONTEXT_GLES_3_2; + + enum eCachedCapStatus : uint8_t { + CAP_STATUS_BLEND = 0, + CAP_STATUS_SCISSOR_TEST, + CAP_STATUS_STENCIL_TEST, + CAP_STATUS_END + }; + + private: + struct { + GLint x = 0; + GLint y = 0; + GLsizei width = 0; + GLsizei height = 0; + } m_lastViewport; + + std::array<bool, CAP_STATUS_END> m_capStatus = {}; + + std::vector<SDRMFormat> m_drmFormats; + bool m_hasModifiers = false; + + int m_drmFD = -1; + std::string m_extensions; + + bool m_fakeFrame = false; + bool m_applyFinalShader = false; + bool m_blend = false; + bool m_offloadedFramebuffer = false; + bool m_cmSupported = true; + + SP<CShader> m_finalScreenShader; + GLuint m_currentProgram; + + void initDRMFormats(); + void initEGL(bool gbm); + EGLDeviceEXT eglDeviceFromDRMFD(int drmFD); + + // for the final shader + std::array<CTimer, POINTER_PRESSED_HISTORY_LENGTH> m_pressedHistoryTimers = {}; + std::array<Vector2D, POINTER_PRESSED_HISTORY_LENGTH> m_pressedHistoryPositions = {}; + GLint m_pressedHistoryKilled = 0; + GLint m_pressedHistoryTouched = 0; + + // + std::optional<std::vector<uint64_t>> getModsForFormat(EGLint format); + + // returns the out FB, can be either Mirror or MirrorSwap + SP<IFramebuffer> blurFramebufferWithDamage(float a, CRegion* damage, CGLFramebuffer& source); + + void passCMUniforms(WP<CShader>, const NColorManagement::PImageDescription imageDescription, const NColorManagement::PImageDescription targetImageDescription, + bool modifySDR, float sdrMinLuminance, int sdrMaxLuminance, const SCMSettings& settings); + void passCMUniforms(WP<CShader>, const NColorManagement::PImageDescription imageDescription, const NColorManagement::PImageDescription targetImageDescription, + bool modifySDR = false, float sdrMinLuminance = -1.0f, int sdrMaxLuminance = -1); + void passCMUniforms(WP<CShader>, const NColorManagement::PImageDescription imageDescription); + 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); + WP<CShader> renderToOutputInternal(); + WP<CShader> renderToFBInternal(SP<ITexture> tex, const STextureRenderData& data, eTextureType texType, const CBox& newBox); + void renderTextureInternal(SP<ITexture>, const CBox&, const STextureRenderData& data); + void renderTextureWithBlurInternal(SP<ITexture>, const CBox&, const STextureRenderData& data); + + friend class IHyprRenderer; + friend class CHyprGLRenderer; + friend class CGLElementRenderer; + friend class CTexPassElement; + friend class CPreBlurElement; + friend class CSurfacePassElement; + }; + + inline UP<CHyprOpenGLImpl> g_pHyprOpenGL; +} diff --git a/src/render/Renderbuffer.cpp b/src/render/Renderbuffer.cpp index 5ecbe845..34ce3e6a 100644 --- a/src/render/Renderbuffer.cpp +++ b/src/render/Renderbuffer.cpp @@ -7,6 +7,8 @@ #include <dlfcn.h> +using namespace Render; + IRenderbuffer::IRenderbuffer(SP<Aquamarine::IBuffer> buffer, uint32_t format) : m_hlBuffer(buffer) { m_listeners.destroyBuffer = buffer->events.destroy.listen([this] { g_pHyprRenderer->onRenderbufferDestroy(this); }); } diff --git a/src/render/Renderbuffer.hpp b/src/render/Renderbuffer.hpp index c33144d3..e19af22f 100644 --- a/src/render/Renderbuffer.hpp +++ b/src/render/Renderbuffer.hpp @@ -5,24 +5,26 @@ #include "Framebuffer.hpp" #include <aquamarine/buffer/Buffer.hpp> -class IRenderbuffer { - public: - IRenderbuffer(SP<Aquamarine::IBuffer> buffer, uint32_t format); - virtual ~IRenderbuffer() = default; +namespace Render { + class IRenderbuffer { + public: + IRenderbuffer(SP<Aquamarine::IBuffer> buffer, uint32_t format); + virtual ~IRenderbuffer() = default; - bool good(); - SP<IFramebuffer> getFB(); + bool good(); + SP<IFramebuffer> getFB(); - virtual void bind() = 0; - virtual void unbind() = 0; + virtual void bind() = 0; + virtual void unbind() = 0; - WP<Aquamarine::IBuffer> m_hlBuffer; + WP<Aquamarine::IBuffer> m_hlBuffer; - protected: - SP<IFramebuffer> m_framebuffer; - bool m_good = false; + protected: + SP<IFramebuffer> m_framebuffer; + bool m_good = false; - struct { - CHyprSignalListener destroyBuffer; - } m_listeners; -}; + struct { + CHyprSignalListener destroyBuffer; + } m_listeners; + }; +} diff --git a/src/render/Renderer.cpp b/src/render/Renderer.cpp index 0c184238..1b735a46 100644 --- a/src/render/Renderer.cpp +++ b/src/render/Renderer.cpp @@ -47,15 +47,17 @@ #include "../protocols/types/ContentType.hpp" #include "../helpers/MiscFunctions.hpp" #include "AsyncResourceGatherer.hpp" +#include "ElementRenderer.hpp" #include "Framebuffer.hpp" #include "OpenGL.hpp" #include "Texture.hpp" -#include "pass/BorderPassElement.hpp" -#include "pass/PreBlurElement.hpp" +#include "./pass/PreBlurElement.hpp" +#include "types.hpp" #include <hyprutils/math/Mat3x3.hpp> #include <hyprutils/math/Region.hpp> #include <hyprutils/math/Vector2D.hpp> #include <hyprutils/memory/SharedPtr.hpp> +#include <hyprutils/memory/UniquePtr.hpp> #include <optional> #include <pango/pangocairo.h> @@ -65,6 +67,7 @@ using namespace Hyprutils::Utils; using namespace Hyprutils::OS; using enum NContentType::eContentType; using namespace NColorManagement; +using namespace Render; extern "C" { #include <xf86drm.h> @@ -212,8 +215,8 @@ IHyprRenderer::~IHyprRenderer() { wl_event_source_remove(m_cursorTicker); } -WP<CHyprOpenGLImpl> IHyprRenderer::glBackend() { - return g_pHyprOpenGL; +WP<Render::GL::CHyprOpenGLImpl> IHyprRenderer::glBackend() { + return Render::GL::g_pHyprOpenGL; } bool IHyprRenderer::shouldRenderWindow(PHLWINDOW pWindow, PHLMONITOR pMonitor) { @@ -499,16 +502,12 @@ void IHyprRenderer::renderWorkspaceWindows(PHLMONITOR pMonitor, PHLWORKSPACE pWo } void IHyprRenderer::bindOffMain() { - RASSERT(m_renderData.pMonitor->m_offMainFB->isAllocated(), "IHyprRenderer::beginRender should allocate monitor FBs") - - m_renderData.pMonitor->m_offMainFB->bind(); - draw(makeUnique<CClearPassElement>(CClearPassElement::SClearData{{0, 0, 0, 0}}), {}); - m_renderData.currentFB = m_renderData.pMonitor->m_offMainFB; + bindFB(m_renderData.pMonitor->resources()->getUnusedWorkBuffer()); + draw(CClearPassElement::SClearData{{0, 0, 0, 0}}); } void IHyprRenderer::bindBackOnMain() { - m_renderData.mainFB->bind(); - m_renderData.currentFB = m_renderData.mainFB; + bindFB(m_renderData.mainFB); } void IHyprRenderer::renderWindow(PHLWINDOW pWindow, PHLMONITOR pMonitor, const Time::steady_tp& time, bool decorate, eRenderPassMode mode, bool ignorePosition, bool standalone) { @@ -679,7 +678,7 @@ void IHyprRenderer::renderWindow(PHLWINDOW pWindow, PHLMONITOR pMonitor, const T } if (TRANSFORMERSPRESENT) { - IFramebuffer* last = m_renderData.currentFB.get(); + SP<IFramebuffer> last = m_renderData.currentFB; for (auto const& t : pWindow->m_transformers) { last = t->transform(last); } @@ -769,348 +768,59 @@ void IHyprRenderer::renderWindow(PHLWINDOW pWindow, PHLMONITOR pMonitor, const T m_renderData.currentWindow.reset(); } -void IHyprRenderer::drawRect(CRectPassElement* element, const CRegion& damage) { - auto& data = element->m_data; - - if (data.box.w <= 0 || data.box.h <= 0) +void IHyprRenderer::draw(WP<IPassElement> element, const CRegion& damage) { + ASSERT(element); + if (!element) return; - if (!data.clipBox.empty()) - m_renderData.clipBox = data.clipBox; - - data.modifiedBox = data.box; - m_renderData.renderModif.applyToBox(data.modifiedBox); - - 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); - - data.TOPLEFT[0] = sc<float>(transformedBox.x); - data.TOPLEFT[1] = sc<float>(transformedBox.y); - data.FULLSIZE[0] = sc<float>(transformedBox.width); - data.FULLSIZE[1] = sc<float>(transformedBox.height); - - data.drawRegion = data.color.a == 1.F || !data.blur ? damage : m_renderData.damage; - - 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); - } - - draw(element, damage); - - m_renderData.clipBox = {}; + elementRenderer()->drawElement(element, damage); } -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 IHyprRenderer::draw(const CBorderPassElement::SBorderData& data, const CRegion& damage) { + elementRenderer()->drawElement(makeUnique<CBorderPassElement>(data), damage); } -void IHyprRenderer::drawPreBlur(CPreBlurElement* element, const CRegion& damage) { - TRACY_GPU_ZONE("RenderPreBlurForCurrentMonitor"); - - const auto SAVEDRENDERMODIF = m_renderData.renderModif; - m_renderData.renderModif = {}; // fix shit - - // make the fake dmg - CRegion fakeDamage{0, 0, m_renderData.pMonitor->m_transformedSize.x, m_renderData.pMonitor->m_transformedSize.y}; - - draw(element, fakeDamage); - - m_renderData.pMonitor->m_blurFBDirty = false; - m_renderData.pMonitor->m_blurFBShouldRender = false; - - m_renderData.renderModif = SAVEDRENDERMODIF; +void IHyprRenderer::draw(const CClearPassElement::SClearData& data, const CRegion& damage) { + elementRenderer()->drawElement(makeUnique<CClearPassElement>(data), damage); } -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) - return; - - const auto& TEXTURE = m_data.texture; - - // this is bad, probably has been logged elsewhere. Means the texture failed - // uploading to the GPU. - if (!TEXTURE->ok()) - return; - - const auto INTERACTIVERESIZEINPROGRESS = m_data.pWindow && g_layoutManager->dragController()->target() && g_layoutManager->dragController()->mode() == MBIND_RESIZE; - TRACY_GPU_ZONE("RenderSurface"); - - auto PSURFACE = Desktop::View::CWLSurface::fromResource(m_data.surface); - - const float ALPHA = m_data.alpha * m_data.fadeAlpha * (PSURFACE ? PSURFACE->m_alphaModifier : 1.F); - const float OVERALL_ALPHA = PSURFACE ? PSURFACE->m_overallOpacity : 1.F; - const bool BLUR = m_data.blur && (!TEXTURE->m_opaque || ALPHA < 1.F || OVERALL_ALPHA < 1.F); - - auto windowBox = element->getTexBox(); - - const auto PROJSIZEUNSCALED = windowBox.size(); - - windowBox.scale(m_data.pMonitor->m_scale); - windowBox.round(); - - if (windowBox.width <= 1 || windowBox.height <= 1) { - element->discard(); - return; - } - - const bool MISALIGNEDFSV1 = std::floor(m_data.pMonitor->m_scale) != m_data.pMonitor->m_scale /* Fractional */ && m_data.surface->m_current.scale == 1 /* fs protocol */ && - windowBox.size() != m_data.surface->m_current.bufferSize /* misaligned */ && DELTALESSTHAN(windowBox.width, m_data.surface->m_current.bufferSize.x, 3) && - DELTALESSTHAN(windowBox.height, m_data.surface->m_current.bufferSize.y, 3) /* off by one-or-two */ && - (!m_data.pWindow || (!m_data.pWindow->m_realSize->isBeingAnimated() && !INTERACTIVERESIZEINPROGRESS)) /* not window or not animated/resizing */ && - (!m_data.pLS || (!m_data.pLS->m_realSize->isBeingAnimated())); /* not LS or not animated */ - - calculateUVForSurface(m_data.pWindow, m_data.surface, m_data.pMonitor->m_self.lock(), m_data.mainSurface, windowBox.size(), PROJSIZEUNSCALED, MISALIGNEDFSV1); - - auto cancelRender = false; - auto clipRegion = element->visibleRegion(cancelRender); - if (cancelRender) - return; - - // check for fractional scale surfaces misaligning the buffer size - // in those cases it's better to just force nearest neighbor - // as long as the window is not animated. During those it'd look weird. - // UV will fixup it as well - if (MISALIGNEDFSV1) - m_renderData.useNearestNeighbor = true; - - float rounding = m_data.rounding; - float roundingPower = m_data.roundingPower; - - rounding -= 1; // to fix a border issue - - if (m_data.dontRound) { - rounding = 0; - roundingPower = 2.0f; - } - - const bool WINDOWOPAQUE = m_data.pWindow && m_data.pWindow->wlSurface()->resource() == m_data.surface ? m_data.pWindow->opaque() : false; - const bool CANDISABLEBLEND = ALPHA >= 1.f && OVERALL_ALPHA >= 1.f && rounding <= 0 && WINDOWOPAQUE; - - if (CANDISABLEBLEND) - blend(false); - else - 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) - draw(makeUnique<CTexPassElement>(CTexPassElement::SRenderData{ - .tex = TEXTURE, - .box = windowBox, - .a = ALPHA, - .blurA = m_data.fadeAlpha, - .overallA = OVERALL_ALPHA, - .round = rounding, - .roundingPower = roundingPower, - .blur = true, - .blockBlurOptimization = m_data.blockBlurOptimization, - .allowCustomUV = true, - .surface = m_data.surface, - .discardMode = m_data.discardMode, - .discardOpacity = m_data.discardOpacity, - .clipRegion = clipRegion, - .currentLS = m_data.pLS, - }), - m_renderData.damage.copy().intersect(windowBox)); - else - draw(makeUnique<CTexPassElement>(CTexPassElement::SRenderData{ - .tex = TEXTURE, - .box = windowBox, - .a = ALPHA * OVERALL_ALPHA, - .round = rounding, - .roundingPower = roundingPower, - .discardActive = false, - .allowCustomUV = true, - .surface = m_data.surface, - .discardMode = m_data.discardMode, - .discardOpacity = m_data.discardOpacity, - .clipRegion = clipRegion, - .currentLS = m_data.pLS, - }), - m_renderData.damage.copy().intersect(windowBox)); - } else { - if (BLUR && m_data.popup) - draw(makeUnique<CTexPassElement>(CTexPassElement::SRenderData{ - .tex = TEXTURE, - .box = windowBox, - .a = ALPHA, - .blurA = m_data.fadeAlpha, - .overallA = OVERALL_ALPHA, - .round = rounding, - .roundingPower = roundingPower, - .blur = true, - .blockBlurOptimization = true, - .allowCustomUV = true, - .surface = m_data.surface, - .discardMode = m_data.discardMode, - .discardOpacity = m_data.discardOpacity, - .clipRegion = clipRegion, - .currentLS = m_data.pLS, - }), - m_renderData.damage.copy().intersect(windowBox)); - else - draw(makeUnique<CTexPassElement>(CTexPassElement::SRenderData{ - .tex = TEXTURE, - .box = windowBox, - .a = ALPHA * OVERALL_ALPHA, - .round = rounding, - .roundingPower = roundingPower, - .discardActive = false, - .allowCustomUV = true, - .surface = m_data.surface, - .discardMode = m_data.discardMode, - .discardOpacity = m_data.discardOpacity, - .clipRegion = clipRegion, - .currentLS = m_data.pLS, - }), - m_renderData.damage.copy().intersect(windowBox)); - } - - blend(true); -}; - -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 (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); - - m_renderData.clipBox = {}; - m_renderData.useNearestNeighbor = false; - popMonitorTransformEnabled(); - m_renderData.currentWindow.reset(); +void IHyprRenderer::draw(const CFramebufferElement::SFramebufferElementData& data, const CRegion& damage) { + elementRenderer()->drawElement(makeUnique<CFramebufferElement>(data), damage); } -void IHyprRenderer::drawTex(CTexPassElement* element, const CRegion& damage) { - if (!element->m_data.clipBox.empty()) - m_renderData.clipBox = element->m_data.clipBox; - - pushMonitorTransformEnabled(element->m_data.flipEndFrame); - if (element->m_data.useMirrorProjection) - setProjectionType(RPT_MIRROR); - - m_renderData.surface = element->m_data.surface; - - CScopeGuard x = {[useMirrorProjection = element->m_data.useMirrorProjection]() { - g_pHyprRenderer->popMonitorTransformEnabled(); - if (useMirrorProjection) - g_pHyprRenderer->setProjectionType(RPT_MONITOR); - g_pHyprRenderer->m_renderData.surface.reset(); - }}; - - 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); +void IHyprRenderer::draw(const CRectPassElement::SRectData& data, const CRegion& damage) { + elementRenderer()->drawElement(makeUnique<CRectPassElement>(data), damage); +} - 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.value_or(false)) { - 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 ? m_renderData.pMonitor->m_blurFB->getTexture() : nullptr; +void IHyprRenderer::draw(const CRendererHintsPassElement::SData& data, const CRegion& damage) { + elementRenderer()->drawElement(makeUnique<CRendererHintsPassElement>(data), damage); +} - draw(element, damage); - } else - draw(element, damage); +void IHyprRenderer::draw(const CShadowPassElement::SShadowData& data, const CRegion& damage) { + elementRenderer()->drawElement(makeUnique<CShadowPassElement>(data), damage); +} - m_renderData.clipBox = {}; +void IHyprRenderer::draw(const CSurfacePassElement::SRenderData& data, const CRegion& damage) { + elementRenderer()->drawElement(makeUnique<CSurfacePassElement>(data), damage); } -void IHyprRenderer::drawTexMatte(CTextureMatteElement* element, const CRegion& damage) { - if (m_renderData.damage.empty()) - return; +void IHyprRenderer::draw(const CTexPassElement::SRenderData& data, const CRegion& damage) { + elementRenderer()->drawElement(makeUnique<CTexPassElement>(data), damage); +} - const auto m_data = element->m_data; - if (m_data.disableTransformAndModify) { - pushMonitorTransformEnabled(true); - m_renderData.renderModif.enabled = false; - draw(element, damage); - m_renderData.renderModif.enabled = true; - popMonitorTransformEnabled(); - } else - draw(element, damage); +void IHyprRenderer::draw(const CTextureMatteElement::STextureMatteData& data, const CRegion& damage) { + elementRenderer()->drawElement(makeUnique<CTextureMatteElement>(data), damage); } -void IHyprRenderer::draw(WP<IPassElement> element, const CRegion& damage) { - if (!element) - return; +void IHyprRenderer::bindFB(SP<IFramebuffer> fb) { + fb->bind(); + m_renderData.currentFB = fb; +} - switch (element->type()) { - case EK_BORDER: draw(dc<CBorderPassElement*>(element.get()), damage); break; - case EK_CLEAR: draw(dc<CClearPassElement*>(element.get()), damage); break; - case EK_FRAMEBUFFER: draw(dc<CFramebufferElement*>(element.get()), damage); break; - case EK_PRE_BLUR: drawPreBlur(dc<CPreBlurElement*>(element.get()), damage); break; - case EK_RECT: drawRect(dc<CRectPassElement*>(element.get()), damage); break; - case EK_HINTS: drawHints(dc<CRendererHintsPassElement*>(element.get()), damage); break; - case EK_SHADOW: draw(dc<CShadowPassElement*>(element.get()), damage); break; - case EK_INNER_GLOW: draw(dc<CInnerGlowPassElement*>(element.get()), damage); break; - case EK_SURFACE: preDrawSurface(dc<CSurfacePassElement*>(element.get()), damage); break; - case EK_TEXTURE: drawTex(dc<CTexPassElement*>(element.get()), damage); break; - case EK_TEXTURE_MATTE: drawTexMatte(dc<CTextureMatteElement*>(element.get()), damage); break; - default: Log::logger->log(Log::WARN, "Unimplimented draw for {}", element->passName()); - } +UP<CScopeGuard> IHyprRenderer::bindTempFB(SP<IFramebuffer> fb) { + const auto oldFB = m_renderData.currentFB; + bindFB(fb); + return makeUnique<CScopeGuard>([this, oldFB] { bindFB(oldFB); }); } bool IHyprRenderer::preBlurQueued(PHLMONITORREF pMonitor) { @@ -1668,9 +1378,7 @@ SP<ITexture> IHyprRenderer::loadAsset(const std::string& filename) { } SP<ITexture> IHyprRenderer::getBlurTexture(PHLMONITORREF pMonitor) { - if (!pMonitor->m_blurFB) - return nullptr; - return pMonitor->m_blurFB->getTexture(); + return pMonitor->resources()->m_blurFB->getTexture(); } bool IHyprRenderer::shouldUseNewBlurOptimizations(PHLLS pLayer, PHLWINDOW pWindow) { @@ -1893,126 +1601,6 @@ void IHyprRenderer::renderSessionLockMissing(PHLMONITOR pMonitor) { } } -static std::optional<Vector2D> getSurfaceExpectedSize(PHLWINDOW pWindow, SP<CWLSurfaceResource> pSurface, PHLMONITOR pMonitor, bool main) { - const auto CAN_USE_WINDOW = pWindow && main; - const auto WINDOW_SIZE_MISALIGN = CAN_USE_WINDOW && pWindow->getReportedSize() != pWindow->wlSurface()->resource()->m_current.size; - - if (pSurface->m_current.viewport.hasDestination) - return (pSurface->m_current.viewport.destination * pMonitor->m_scale).round(); - - if (pSurface->m_current.viewport.hasSource) - return (pSurface->m_current.viewport.source.size() * pMonitor->m_scale).round(); - - if (WINDOW_SIZE_MISALIGN) - return (pSurface->m_current.size * pMonitor->m_scale).round(); - - if (CAN_USE_WINDOW) - return (pWindow->getReportedSize() * pMonitor->m_scale).round(); - - return std::nullopt; -} - -void IHyprRenderer::calculateUVForSurface(PHLWINDOW pWindow, SP<CWLSurfaceResource> pSurface, PHLMONITOR pMonitor, bool main, const Vector2D& projSize, - const Vector2D& projSizeUnscaled, bool fixMisalignedFSV1) { - if (!pWindow || !pWindow->m_isX11) { - static auto PEXPANDEDGES = CConfigValue<Hyprlang::INT>("render:expand_undersized_textures"); - - Vector2D uvTL; - Vector2D uvBR = Vector2D(1, 1); - - if (pSurface->m_current.viewport.hasSource) { - // we stretch it to dest. if no dest, to 1,1 - Vector2D const& bufferSize = pSurface->m_current.bufferSize; - auto const& bufferSource = pSurface->m_current.viewport.source; - - // calculate UV for the basic src_box. Assume dest == size. Scale to dest later - uvTL = Vector2D(bufferSource.x / bufferSize.x, bufferSource.y / bufferSize.y); - uvBR = Vector2D((bufferSource.x + bufferSource.width) / bufferSize.x, (bufferSource.y + bufferSource.height) / bufferSize.y); - - if (uvBR.x < 0.01f || uvBR.y < 0.01f) { - uvTL = Vector2D(); - uvBR = Vector2D(1, 1); - } - } - - if (projSize != Vector2D{} && fixMisalignedFSV1) { - // instead of nearest_neighbor (we will repeat / skip) - // just cut off / expand surface - const Vector2D PIXELASUV = Vector2D{1, 1} / pSurface->m_current.bufferSize; - const auto& BUFFER_SIZE = pSurface->m_current.bufferSize; - - // compute MISALIGN from the adjusted UV coordinates. - const Vector2D MISALIGNMENT = (uvBR - uvTL) * BUFFER_SIZE - projSize; - - if (MISALIGNMENT != Vector2D{}) - uvBR -= MISALIGNMENT * PIXELASUV; - } else { - // if the surface is smaller than our viewport, extend its edges. - // this will break if later on xdg geometry is hit, but we really try - // to let the apps know to NOT add CSD. Also if source is there. - // there is no way to fix this if that's the case - const auto MONITOR_WL_SCALE = std::ceil(pMonitor->m_scale); - const bool SCALE_UNAWARE = pMonitor->m_scale != 1.f && (MONITOR_WL_SCALE == pSurface->m_current.scale || !pSurface->m_current.viewport.hasDestination); - const auto EXPECTED_SIZE = getSurfaceExpectedSize(pWindow, pSurface, pMonitor, main).value_or((projSize * pMonitor->m_scale).round()); - - const auto RATIO = projSize / EXPECTED_SIZE; - if (!SCALE_UNAWARE || MONITOR_WL_SCALE == 1) { - if (*PEXPANDEDGES && !SCALE_UNAWARE && (RATIO.x > 1 || RATIO.y > 1)) { - const auto FIX = RATIO.clamp(Vector2D{1, 1}, Vector2D{1000000, 1000000}); - uvBR = uvBR * FIX; - } - - // FIXME: probably do this for in anims on all views... - const auto SHOULD_SKIP = !pWindow || pWindow->m_animatingIn; - if (!SHOULD_SKIP && (RATIO.x < 1 || RATIO.y < 1)) { - const auto FIX = RATIO.clamp(Vector2D{0.0001, 0.0001}, Vector2D{1, 1}); - uvBR = uvBR * FIX; - } - } - } - - m_renderData.primarySurfaceUVTopLeft = uvTL; - m_renderData.primarySurfaceUVBottomRight = uvBR; - - if (m_renderData.primarySurfaceUVTopLeft == Vector2D() && m_renderData.primarySurfaceUVBottomRight == Vector2D(1, 1)) { - // No special UV mods needed - m_renderData.primarySurfaceUVTopLeft = Vector2D(-1, -1); - m_renderData.primarySurfaceUVBottomRight = Vector2D(-1, -1); - } - - if (!main || !pWindow) - return; - - // FIXME: this doesn't work. We always set MAXIMIZED anyways, so this doesn't need to work, but it's problematic. - - // CBox geom = pWindow->m_xdgSurface->m_current.geometry; - - // // Adjust UV based on the xdg_surface geometry - // if (geom.x != 0 || geom.y != 0 || geom.w != 0 || geom.h != 0) { - // const auto XPERC = geom.x / pSurface->m_current.size.x; - // const auto YPERC = geom.y / pSurface->m_current.size.y; - // const auto WPERC = (geom.x + geom.w ? geom.w : pSurface->m_current.size.x) / pSurface->m_current.size.x; - // const auto HPERC = (geom.y + geom.h ? geom.h : pSurface->m_current.size.y) / pSurface->m_current.size.y; - - // const auto TOADDTL = Vector2D(XPERC * (uvBR.x - uvTL.x), YPERC * (uvBR.y - uvTL.y)); - // uvBR = uvBR - Vector2D((1.0 - WPERC) * (uvBR.x - uvTL.x), (1.0 - HPERC) * (uvBR.y - uvTL.y)); - // uvTL = uvTL + TOADDTL; - // } - - m_renderData.primarySurfaceUVTopLeft = uvTL; - m_renderData.primarySurfaceUVBottomRight = uvBR; - - if (m_renderData.primarySurfaceUVTopLeft == Vector2D() && m_renderData.primarySurfaceUVBottomRight == Vector2D(1, 1)) { - // No special UV mods needed - m_renderData.primarySurfaceUVTopLeft = Vector2D(-1, -1); - m_renderData.primarySurfaceUVBottomRight = Vector2D(-1, -1); - } - } else { - m_renderData.primarySurfaceUVTopLeft = Vector2D(-1, -1); - m_renderData.primarySurfaceUVBottomRight = Vector2D(-1, -1); - } -} - bool IHyprRenderer::beginRender(PHLMONITOR pMonitor, CRegion& damage, eRenderMode mode, SP<IHLBuffer> buffer, SP<IFramebuffer> fb, bool simple) { m_renderPass.clear(); m_renderMode = mode; @@ -2023,44 +1611,11 @@ bool IHyprRenderer::beginRender(PHLMONITOR pMonitor, CRegion& damage, eRenderMod 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()); + const bool HAS_MIRROR_FB = g_pHyprRenderer->m_renderData.pMonitor->resources()->hasMirrorFB(); + const bool NEEDS_COPY_FB = g_pHyprRenderer->m_renderData.pMonitor->needsACopyFB(); 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); + g_pHyprRenderer->m_renderData.pMonitor->resources()->mirrorFB()->release(); if (m_renderMode == RENDER_MODE_FULL_FAKE) return beginFullFakeRenderInternal(pMonitor, damage, fb, simple); @@ -2150,9 +1705,10 @@ Mat3x3 IHyprRenderer::projectBoxToTarget(const CBox& box, std::optional<eTransfo SP<ITexture> IHyprRenderer::blurMainFramebuffer(float a, CRegion* originalDamage) { if (!m_renderData.currentFB->getTexture()) { Log::logger->log(Log::ERR, "BUG THIS: null fb texture while attempting to blur main fb?! (introspection off?!)"); - return m_renderData.pMonitor->m_mirrorFB->getTexture(); // return something to sample from at least + return m_renderData.pMonitor->resources()->m_blurFB->getTexture(); // return something to sample from at least } + auto guard = bindTempFB(m_renderData.currentFB); // blurFramebuffer messes with FB bindings return blurFramebuffer(m_renderData.currentFB, a, originalDamage); } @@ -2161,25 +1717,25 @@ void IHyprRenderer::preBlurForCurrentMonitor(CRegion* fakeDamage) { const auto blurredTex = blurMainFramebuffer(1, fakeDamage); // render onto blurFB - if (!m_renderData.pMonitor->m_blurFB) - return; - 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(); + auto guard = bindTempFB(m_renderData.pMonitor->resources()->m_blurFB); + const auto SAVE_TRANSFORM = blurredTex->m_transform; + blurredTex->m_transform = Math::wlTransformToHyprutils(Math::invertTransform(m_renderData.pMonitor->m_transform)); - draw(makeUnique<CClearPassElement>(CClearPassElement::SClearData{{0, 0, 0, 0}}), {}); + draw(CClearPassElement::SClearData{{0, 0, 0, 0}}); pushMonitorTransformEnabled(true); - draw(makeUnique<CTexPassElement>(CTexPassElement::SRenderData{ - .tex = blurredTex, - .box = CBox{0, 0, m_renderData.pMonitor->m_transformedSize.x, m_renderData.pMonitor->m_transformedSize.y}, - .damage = *fakeDamage, - }), - *fakeDamage); // .noAA = true + draw( + 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(); + blurredTex->m_transform = SAVE_TRANSFORM; } static bool isSDR2HDR(const NColorManagement::SImageDescription& imageDescription, const NColorManagement::SImageDescription& targetImageDescription) { @@ -2187,7 +1743,9 @@ static bool isSDR2HDR(const NColorManagement::SImageDescription& imageDescriptio return (imageDescription.transferFunction == NColorManagement::CM_TRANSFER_FUNCTION_SRGB || imageDescription.transferFunction == NColorManagement::CM_TRANSFER_FUNCTION_GAMMA22) && (targetImageDescription.transferFunction == NColorManagement::CM_TRANSFER_FUNCTION_ST2084_PQ || - targetImageDescription.transferFunction == NColorManagement::CM_TRANSFER_FUNCTION_HLG); + targetImageDescription.transferFunction == NColorManagement::CM_TRANSFER_FUNCTION_HLG || + (targetImageDescription.transferFunction == NColorManagement::CM_TRANSFER_FUNCTION_EXT_LINEAR && + g_pHyprRenderer->m_renderData.pMonitor->m_imageDescription->value().transferFunction == NColorManagement::CM_TRANSFER_FUNCTION_ST2084_PQ)); } static bool isHDR2SDR(const NColorManagement::SImageDescription& imageDescription, const NColorManagement::SImageDescription& targetImageDescription) { @@ -2228,8 +1786,7 @@ SCMSettings IHyprRenderer::getCMSettings(const NColorManagement::PImageDescripti auto matrix = imageDescription->getPrimaries()->convertMatrix(targetImageDescription->getPrimaries()); auto toXYZ = targetImageDescription->getPrimaries()->value().toXYZ(); - const bool needsMod = (imageDescription->value().transferFunction == CM_TRANSFER_FUNCTION_SRGB || imageDescription->value().transferFunction == CM_TRANSFER_FUNCTION_GAMMA22) && - targetImageDescription->value().transferFunction == CM_TRANSFER_FUNCTION_ST2084_PQ && + const bool needsMod = needsSDRmod && ((m_renderData.pMonitor->m_sdrSaturation > 0 && m_renderData.pMonitor->m_sdrSaturation != 1.0f) || (m_renderData.pMonitor->m_sdrBrightness > 0 && m_renderData.pMonitor->m_sdrBrightness != 1.0f)); @@ -2255,8 +1812,12 @@ SCMSettings IHyprRenderer::getCMSettings(const NColorManagement::PImageDescripti } void IHyprRenderer::renderMirrored() { - auto monitor = m_renderData.pMonitor; - auto mirrored = monitor->m_mirrorOf; + auto monitor = m_renderData.pMonitor; + auto mirrored = monitor->m_mirrorOf; + + // saveBufferForMirror should create it + if (!mirrored->resources()->hasMirrorFB()) + return; 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}; @@ -2267,17 +1828,12 @@ void IHyprRenderer::renderMirrored() { 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; + const auto MIRROR_TEX = mirrored->resources()->getMirrorTexture(); m_renderPass.add(makeUnique<CClearPassElement>(CClearPassElement::SClearData{CHyprColor(0, 0, 0, 0)})); CTexPassElement::SRenderData data; - data.tex = PFB->getTexture(); + data.tex = MIRROR_TEX; data.box = monbox; data.useMirrorProjection = true; @@ -3354,7 +2910,7 @@ void IHyprRenderer::makeSnapshot(PHLWINDOW pWindow) { m_bRenderingSnapshot = true; - draw(makeUnique<CClearPassElement>(CClearPassElement::SClearData{CHyprColor(0, 0, 0, 0)}), {}); + draw(CClearPassElement::SClearData{CHyprColor(0, 0, 0, 0)}); startRenderPass(); Log::logger->log(Log::DEBUG, "renderer: cleared a snapshot of {:x}", rc<uintptr_t>(pWindow.get())); @@ -3395,7 +2951,7 @@ void IHyprRenderer::makeSnapshot(PHLLS pLayer) { m_bRenderingSnapshot = true; - draw(makeUnique<CClearPassElement>(CClearPassElement::SClearData{CHyprColor(0, 0, 0, 0)}), {}); + draw(CClearPassElement::SClearData{CHyprColor(0, 0, 0, 0)}); startRenderPass(); Log::logger->log(Log::DEBUG, "renderer: cleared a snapshot of layer {:x}", rc<uintptr_t>(pLayer.get())); @@ -3437,7 +2993,7 @@ void IHyprRenderer::makeSnapshot(WP<Desktop::View::CPopup> popup) { m_bRenderingSnapshot = true; - draw(makeUnique<CClearPassElement>(CClearPassElement::SClearData{CHyprColor(0, 0, 0, 0)}), {}); + draw(CClearPassElement::SClearData{CHyprColor(0, 0, 0, 0)}); CSurfacePassElement::SRenderData renderdata; renderdata.pos = popup->coordsGlobal(); @@ -3613,7 +3169,9 @@ NColorManagement::PImageDescription IHyprRenderer::workBufferImageDescription() // const auto sdrEOTF = NTransferFunction::fromConfig(IS_MONITOR_ICC); // const auto CHOSEN_SDR_EOTF = sdrEOTF != NTransferFunction::TF_SRGB ? NColorManagement::CM_TRANSFER_FUNCTION_GAMMA22 : NColorManagement::CM_TRANSFER_FUNCTION_SRGB; - return m_renderData.pMonitor->m_imageDescription; //CImageDescription::from(NColorManagement::SImageDescription{.transferFunction = CHOSEN_SDR_EOTF}); + return m_renderData.pMonitor->useFP16() ? + LINEAR_IMAGE_DESCRIPTION : + m_renderData.pMonitor->m_imageDescription; //CImageDescription::from(NColorManagement::SImageDescription{.transferFunction = CHOSEN_SDR_EOTF}); } bool IHyprRenderer::shouldBlur(PHLLS ls) { @@ -3700,7 +3258,3 @@ SP<ITexture> IHyprRenderer::renderSplash(const std::function<SP<ITexture>(const cairo_destroy(CAIRO); return tex; } - -bool IHyprRenderer::needsACopyFB(PHLMONITOR mon) { - return !mon->m_mirrors.empty() || Screenshare::mgr()->isOutputBeingSSd(mon); -} diff --git a/src/render/Renderer.hpp b/src/render/Renderer.hpp index d8056d82..ba044567 100644 --- a/src/render/Renderer.hpp +++ b/src/render/Renderer.hpp @@ -1,47 +1,41 @@ #pragma once #include "../defines.hpp" +#include <cstdint> #include <hyprgraphics/color/Color.hpp> #include <hyprutils/math/Box.hpp> +#include <hyprutils/utils/ScopeGuard.hpp> #include <list> #include <optional> +#include "OpenGL.hpp" +#include "./SyncFDManager.hpp" +#include "./pass/Pass.hpp" +#include "./pass/BorderPassElement.hpp" +#include "./pass/ClearPassElement.hpp" +#include "./pass/FramebufferElement.hpp" +#include "./pass/RectPassElement.hpp" +#include "./pass/RendererHintsPassElement.hpp" +#include "./pass/ShadowPassElement.hpp" +#include "./pass/SurfacePassElement.hpp" +#include "./pass/TexPassElement.hpp" +#include "./pass/TextureMatteElement.hpp" +#include "types.hpp" #include "../helpers/Monitor.hpp" #include "../desktop/view/LayerSurface.hpp" -#include "./pass/Pass.hpp" #include "Renderbuffer.hpp" #include "../helpers/time/Timer.hpp" #include "../helpers/math/Math.hpp" #include "../helpers/time/Time.hpp" #include "../../protocols/cursor-shape-v1.hpp" -#include "../desktop/view/Popup.hpp" +#include "desktop/view/Popup.hpp" #include "Framebuffer.hpp" #include "Texture.hpp" -#include "pass/BorderPassElement.hpp" -#include "pass/ClearPassElement.hpp" -#include "pass/FramebufferElement.hpp" -#include "pass/PreBlurElement.hpp" -#include "pass/RectPassElement.hpp" -#include "pass/RendererHintsPassElement.hpp" -#include "pass/ShadowPassElement.hpp" -#include "pass/InnerGlowPassElement.hpp" -#include "pass/SurfacePassElement.hpp" -#include "pass/TexPassElement.hpp" -#include "pass/TextureMatteElement.hpp" struct SMonitorRule; class CWorkspace; class CInputPopup; class IHLBuffer; class CEventLoopTimer; -class CRenderPass; - -const std::vector<const char*> ASSET_PATHS = { -#ifdef DATAROOTDIR - DATAROOTDIR, -#endif - "/usr/share", - "/usr/local/share", -}; class CToplevelExportProtocolManager; class CInputManager; struct SSessionLockSurface; @@ -49,330 +43,248 @@ namespace Screenshare { class CScreenshareFrame; }; -enum eDamageTrackingModes : int8_t { - DAMAGE_TRACKING_INVALID = -1, - DAMAGE_TRACKING_NONE = 0, - DAMAGE_TRACKING_MONITOR, - DAMAGE_TRACKING_FULL, -}; - -enum eRenderPassMode : uint8_t { - RENDER_PASS_ALL = 0, - RENDER_PASS_MAIN, - RENDER_PASS_POPUP -}; - -enum eRenderMode : uint8_t { - RENDER_MODE_NORMAL = 0, - RENDER_MODE_FULL_FAKE = 1, - RENDER_MODE_TO_BUFFER = 2, - RENDER_MODE_TO_BUFFER_READ_ONLY = 3, -}; - -struct SRenderWorkspaceUntilData { - PHLLS ls; - PHLWINDOW w; -}; - -enum eRenderProjectionType : uint8_t { - RPT_MONITOR, - RPT_MIRROR, - RPT_FB, - RPT_EXPORT, -}; - -struct SRenderData { - // can be private - Mat3x3 targetProjection; - - // ---------------------- - - // used by public - Vector2D fbSize = {-1, -1}; - PHLMONITORREF pMonitor; - - eRenderProjectionType projectionType = RPT_MONITOR; - - SP<IFramebuffer> currentFB = nullptr; // current rendering to - SP<IFramebuffer> mainFB = nullptr; // main to render to - SP<IFramebuffer> outFB = nullptr; // out to render to (if offloaded, etc) - - CRegion damage; - CRegion finalDamage; // damage used for funal off -> main - - SRenderModifData renderModif; - float mouseZoomFactor = 1.f; - bool mouseZoomUseMouse = true; // true by default - bool useNearestNeighbor = false; - bool blockScreenShader = false; - - Vector2D primarySurfaceUVTopLeft = Vector2D(-1, -1); - Vector2D primarySurfaceUVBottomRight = Vector2D(-1, -1); - - // TODO remove and pass directly - CBox clipBox = {}; // scaled coordinates - PHLWINDOWREF currentWindow; - WP<CWLSurfaceResource> surface; - - bool transformDamage = true; - bool noSimplify = false; -}; - -struct STFRange { - float min = 0; - float max = 80; -}; - -struct SCMSettings { - NColorManagement::eTransferFunction sourceTF = NColorManagement::CM_TRANSFER_FUNCTION_GAMMA22; - NColorManagement::eTransferFunction targetTF = NColorManagement::CM_TRANSFER_FUNCTION_GAMMA22; - STFRange srcTFRange; - STFRange dstTFRange; - float srcRefLuminance = 80; - float dstRefLuminance = 80; - std::array<std::array<double, 3>, 3> convertMatrix; - - bool needsTonemap = false; - float maxLuminance = 80; - float dstMaxLuminance = 80; - std::array<std::array<double, 3>, 3> dstPrimaries2XYZ; - bool needsSDRmod = false; - float sdrSaturation = 1.0; - float sdrBrightnessMultiplier = 1.0; -}; - -class IHyprRenderer { - public: - IHyprRenderer(); - virtual ~IHyprRenderer(); - - WP<CHyprOpenGLImpl> glBackend(); - - void renderMonitor(PHLMONITOR pMonitor, bool commit = true); - void arrangeLayersForMonitor(const MONITORID&); - void damageSurface(SP<CWLSurfaceResource>, double, double, double scale = 1.0); - void damageWindow(PHLWINDOW, bool forceFull = false); - void damageBox(const CBox&, bool skipFrameSchedule = false); - void damageBox(const int& x, const int& y, const int& w, const int& h); - void damageRegion(const CRegion&); - void damageMonitor(PHLMONITOR); - void damageMirrorsWith(PHLMONITOR, const CRegion&); - bool shouldRenderWindow(PHLWINDOW, PHLMONITOR); - bool shouldRenderWindow(PHLWINDOW); - void ensureCursorRenderingMode(); - bool shouldRenderCursor(); - void setCursorHidden(bool hide); - void calculateUVForSurface(PHLWINDOW, SP<CWLSurfaceResource>, PHLMONITOR pMonitor, bool main = false, const Vector2D& projSize = {}, const Vector2D& projSizeUnscaled = {}, - bool fixMisalignedFSV1 = false); - std::tuple<float, float, float> 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<Desktop::View::CWLSurface> 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<Desktop::View::CPopup>); - void renderSnapshot(PHLWINDOW); - void renderSnapshot(PHLLS); - void renderSnapshot(WP<Desktop::View::CPopup>); - bool beginFullFakeRender(PHLMONITOR pMonitor, CRegion& damage, SP<IFramebuffer> fb); - bool beginRenderToBuffer(PHLMONITOR pMonitor, CRegion& damage, SP<IHLBuffer> buffer, bool simple = false); - virtual void startRenderPass() {}; - virtual void endRender(const std::function<void()>& renderingDoneCallback = {}) = 0; - - NColorManagement::PImageDescription workBufferImageDescription(); - bool m_bBlockSurfaceFeedback = false; - bool m_bRenderingSnapshot = false; - PHLMONITORREF m_mostHzMonitor; - bool m_directScanoutBlocked = false; - - void setSurfaceScanoutMode(SP<CWLSurfaceResource> surface, PHLMONITOR monitor); // nullptr monitor resets - - 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<CHLBufferReference> m_usedAsyncBuffers; - - struct { - int hotspotX = 0; - int hotspotY = 0; - wpCursorShapeDeviceV1Shape shape = WP_CURSOR_SHAPE_DEVICE_V1_SHAPE_DEFAULT; - wpCursorShapeDeviceV1Shape shapePrevious = WP_CURSOR_SHAPE_DEVICE_V1_SHAPE_DEFAULT; - CTimer switchedTimer; - std::optional<SP<Desktop::View::CWLSurface>> surf; - std::string name; - } m_lastCursorData; - - CRenderPass m_renderPass; - - SP<ITexture> renderSplash(const std::function<SP<ITexture>(const int, const int, unsigned char* const)>& handleData, const int fontSize, const int maxWidth = 1024, - const int maxHeight = 1024); - - virtual SP<IRenderbuffer> getOrCreateRenderbuffer(SP<Aquamarine::IBuffer> 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<ITexture> 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 draw(WP<IPassElement> element, const CRegion& damage); - virtual SP<ITexture> createStencilTexture(const int width, const int height) = 0; - virtual SP<ITexture> createTexture(bool opaque = false) = 0; - virtual SP<ITexture> createTexture(uint32_t drmFormat, uint8_t* pixels, uint32_t stride, const Vector2D& size, bool keepDataCopy = false, bool opaque = false) = 0; - virtual SP<ITexture> createTexture(const Aquamarine::SDMABUFAttrs&, bool opaque = false) = 0; - virtual SP<ITexture> createTexture(const int width, const int height, unsigned char* const) = 0; - virtual SP<ITexture> createTexture(cairo_surface_t* cairo) = 0; - virtual SP<ITexture> createTexture(std::span<const float> lut3D, size_t N) = 0; - virtual SP<ITexture> createTexture(const SP<Aquamarine::IBuffer> buffer, bool keepDataCopy = false); - virtual SP<ITexture> renderText(const std::string& text, CHyprColor col, int pt, bool italic = false, const std::string& fontFamily = "", int maxWidth = 0, int weight = 400); - SP<ITexture> loadAsset(const std::string& filename); - virtual bool shouldUseNewBlurOptimizations(PHLLS pLayer, PHLWINDOW pWindow); - virtual bool explicitSyncSupported() = 0; - virtual std::vector<SDRMFormat> getDRMFormats() = 0; - virtual std::vector<uint64_t> getDRMFormatModifiers(DRMFormat format) = 0; - virtual SP<IFramebuffer> 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; - - bool preBlurQueued(PHLMONITORREF pMonitor); - void pushMonitorTransformEnabled(bool enabled); - void popMonitorTransformEnabled(); - bool monitorTransformEnabled(); - - void setProjectionType(const Vector2D& fbSize); - void setProjectionType(eRenderProjectionType projectionType); - Mat3x3 getBoxProjection(const CBox& box, std::optional<eTransform> transform = std::nullopt); - Mat3x3 projectBoxToTarget(const CBox& box, std::optional<eTransform> transform = std::nullopt); - - SP<ITexture> blurMainFramebuffer(float a, CRegion* originalDamage); - virtual SP<ITexture> blurFramebuffer(SP<IFramebuffer> source, float a, CRegion* originalDamage) = 0; - void preBlurForCurrentMonitor(CRegion* fakeDamage); - - SCMSettings getCMSettings(const NColorManagement::PImageDescription imageDescription, const NColorManagement::PImageDescription targetImageDescription, - SP<CWLSurfaceResource> 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<IRenderbuffer> getOrCreateRenderbufferInternal(SP<Aquamarine::IBuffer> buffer, uint32_t fmt) = 0; - void renderMirrored(); - void setDamage(const CRegion& damage_, std::optional<CRegion> 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<IHLBuffer> buffer = {}, SP<IFramebuffer> 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<IFramebuffer> fb, bool simple = false) { - return false; - }; - virtual void initRender() {}; - virtual bool initRenderBuffer(SP<Aquamarine::IBuffer> buffer, uint32_t fmt) { - return false; +namespace Render { + using CScopeGuard = Hyprutils::Utils::CScopeGuard; + + class IElementRenderer; + class CRenderPass; + + class IHyprRenderer { + public: + IHyprRenderer(); + virtual ~IHyprRenderer(); + + enum eType : uint8_t { + RT_GL = 1, + RT_VK = 2, + }; + + virtual eType type() = 0; + WP<Render::GL::CHyprOpenGLImpl> glBackend(); + + void renderMonitor(PHLMONITOR pMonitor, bool commit = true); + void arrangeLayersForMonitor(const MONITORID&); + void damageSurface(SP<CWLSurfaceResource>, double, double, double scale = 1.0); + void damageWindow(PHLWINDOW, bool forceFull = false); + void damageBox(const CBox&, bool skipFrameSchedule = false); + void damageBox(const int& x, const int& y, const int& w, const int& h); + void damageRegion(const CRegion&); + void damageMonitor(PHLMONITOR); + void damageMirrorsWith(PHLMONITOR, const CRegion&); + bool shouldRenderWindow(PHLWINDOW, PHLMONITOR); + bool shouldRenderWindow(PHLWINDOW); + void ensureCursorRenderingMode(); + bool shouldRenderCursor(); + void setCursorHidden(bool hide); + + std::tuple<float, float, float> 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<Desktop::View::CWLSurface> 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<Desktop::View::CPopup>); + void renderSnapshot(PHLWINDOW); + void renderSnapshot(PHLLS); + void renderSnapshot(WP<Desktop::View::CPopup>); + bool beginFullFakeRender(PHLMONITOR pMonitor, CRegion& damage, SP<IFramebuffer> fb); + bool beginRenderToBuffer(PHLMONITOR pMonitor, CRegion& damage, SP<IHLBuffer> buffer, bool simple = false); + virtual void startRenderPass() {}; + virtual void endRender(const std::function<void()>& renderingDoneCallback = {}) = 0; + + NColorManagement::PImageDescription workBufferImageDescription(); + bool m_bBlockSurfaceFeedback = false; + bool m_bRenderingSnapshot = false; + PHLMONITORREF m_mostHzMonitor; + bool m_directScanoutBlocked = false; + + void setSurfaceScanoutMode(SP<CWLSurfaceResource> surface, PHLMONITOR monitor); // nullptr monitor resets + + 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<CHLBufferReference> m_usedAsyncBuffers; + + struct { + int hotspotX = 0; + int hotspotY = 0; + wpCursorShapeDeviceV1Shape shape = WP_CURSOR_SHAPE_DEVICE_V1_SHAPE_DEFAULT; + wpCursorShapeDeviceV1Shape shapePrevious = WP_CURSOR_SHAPE_DEVICE_V1_SHAPE_DEFAULT; + CTimer switchedTimer; + std::optional<SP<Desktop::View::CWLSurface>> surf; + std::string name; + } m_lastCursorData; + + CRenderPass m_renderPass; + + SP<ITexture> renderSplash(const std::function<SP<ITexture>(const int, const int, unsigned char* const)>& handleData, const int fontSize, const int maxWidth = 1024, + const int maxHeight = 1024); + + virtual SP<IRenderbuffer> getOrCreateRenderbuffer(SP<Aquamarine::IBuffer> 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<ITexture> 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 draw(WP<IPassElement> element, const CRegion& damage = {}); + void draw(const CBorderPassElement::SBorderData& data, const CRegion& damage = {}); + void draw(const CClearPassElement::SClearData& data, const CRegion& damage = {}); + void draw(const CFramebufferElement::SFramebufferElementData& data, const CRegion& damage = {}); + void draw(const CRectPassElement::SRectData& data, const CRegion& damage = {}); + void draw(const CRendererHintsPassElement::SData& data, const CRegion& damage = {}); + void draw(const CShadowPassElement::SShadowData& data, const CRegion& damage = {}); + void draw(const CSurfacePassElement::SRenderData& data, const CRegion& damage = {}); + void draw(const CTexPassElement::SRenderData& data, const CRegion& damage = {}); + void draw(const CTextureMatteElement::STextureMatteData& data, const CRegion& damage = {}); + virtual void bindFB(SP<IFramebuffer> fb); + UP<CScopeGuard> bindTempFB(SP<IFramebuffer> fb); + virtual UP<ISyncFDManager> createSyncFDManager() = 0; + virtual WP<IElementRenderer> elementRenderer() = 0; + virtual SP<ITexture> createStencilTexture(const int width, const int height) = 0; + virtual SP<ITexture> createTexture(bool opaque = false) = 0; + virtual SP<ITexture> createTexture(uint32_t drmFormat, uint8_t* pixels, uint32_t stride, const Vector2D& size, bool keepDataCopy = false, bool opaque = false) = 0; + virtual SP<ITexture> createTexture(const Aquamarine::SDMABUFAttrs&, bool opaque = false) = 0; + virtual SP<ITexture> createTexture(const int width, const int height, unsigned char* const) = 0; + virtual SP<ITexture> createTexture(cairo_surface_t* cairo) = 0; + virtual SP<ITexture> createTexture(std::span<const float> lut3D, size_t N) = 0; + virtual SP<ITexture> createTexture(const SP<Aquamarine::IBuffer> buffer, bool keepDataCopy = false); + virtual SP<ITexture> renderText(const std::string& text, CHyprColor col, int pt, bool italic = false, const std::string& fontFamily = "", int maxWidth = 0, + int weight = 400); + SP<ITexture> loadAsset(const std::string& filename); + virtual bool shouldUseNewBlurOptimizations(PHLLS pLayer, PHLWINDOW pWindow); + virtual bool explicitSyncSupported() = 0; + virtual std::vector<SDRMFormat> getDRMFormats() = 0; + virtual std::vector<uint64_t> getDRMFormatModifiers(DRMFormat format) = 0; + virtual SP<IFramebuffer> 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; + + bool preBlurQueued(PHLMONITORREF pMonitor); + void pushMonitorTransformEnabled(bool enabled); + void popMonitorTransformEnabled(); + bool monitorTransformEnabled(); + + void setProjectionType(const Vector2D& fbSize); + void setProjectionType(eRenderProjectionType projectionType); + Mat3x3 getBoxProjection(const CBox& box, std::optional<eTransform> transform = std::nullopt); + Mat3x3 projectBoxToTarget(const CBox& box, std::optional<eTransform> transform = std::nullopt); + + SP<ITexture> blurMainFramebuffer(float a, CRegion* originalDamage); + virtual SP<ITexture> blurFramebuffer(SP<IFramebuffer> source, float a, CRegion* originalDamage) = 0; + void preBlurForCurrentMonitor(CRegion* fakeDamage); + + SCMSettings getCMSettings(const NColorManagement::PImageDescription imageDescription, const NColorManagement::PImageDescription targetImageDescription, + SP<CWLSurfaceResource> surface = nullptr, bool modifySDR = false, float sdrMinLuminance = -1.0f, int sdrMaxLuminance = -1); + virtual bool reloadShaders(const std::string& path = "") = 0; + + protected: + virtual void renderOffToMain(SP<IFramebuffer> off) = 0; + virtual SP<IRenderbuffer> getOrCreateRenderbufferInternal(SP<Aquamarine::IBuffer> buffer, uint32_t fmt) = 0; + void renderMirrored(); + void setDamage(const CRegion& damage_, std::optional<CRegion> 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<IHLBuffer> buffer = {}, SP<IFramebuffer> 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<IFramebuffer> fb, bool simple = false) { + return false; + }; + virtual void initRender() {}; + virtual bool initRenderBuffer(SP<Aquamarine::IBuffer> buffer, uint32_t fmt) { + return false; + }; + + SP<ITexture> getBackground(PHLMONITOR pMonitor); + virtual SP<ITexture> getBlurTexture(PHLMONITORREF pMonitor); + SP<ITexture> m_lockDeadTexture; + SP<ITexture> m_lockDead2Texture; + SP<ITexture> m_lockTtyTextTexture; + bool m_monitorTransformEnabled = false; // do not modify directly + std::stack<bool> m_monitorTransformStack; + + // old private: + void arrangeLayerArray(PHLMONITOR, const std::vector<PHLLSREF>&, 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) + void renderWorkspaceWindows(PHLMONITOR, PHLWORKSPACE, const Time::steady_tp&); // renders workspace windows (no fullscreen) (tiled, floating, pinned, but no special) + void renderAllClientsForWorkspace(PHLMONITOR pMonitor, PHLWORKSPACE pWorkspace, const Time::steady_tp& now, const Vector2D& translate = {0, 0}, const float& scale = 1.f); + void renderWindow(PHLWINDOW, PHLMONITOR, const Time::steady_tp&, bool, eRenderPassMode, bool ignorePosition = false, bool standalone = false); + void renderLayer(PHLLS, PHLMONITOR, const Time::steady_tp&, bool popups = false, bool lockscreen = false); + void renderSessionLockSurface(WP<SSessionLockSurface>, PHLMONITOR, const Time::steady_tp&); + void renderDragIcon(PHLMONITOR, const Time::steady_tp&); + void renderIMEPopup(CInputPopup*, PHLMONITOR, const Time::steady_tp&); + void sendFrameEventsToWorkspace(PHLMONITOR pMonitor, PHLWORKSPACE pWorkspace, + const Time::steady_tp& now); // sends frame displayed events but doesn't actually render anything + void renderSessionLockPrimer(PHLMONITOR pMonitor); + void renderSessionLockMissing(PHLMONITOR pMonitor); + void renderBackground(PHLMONITOR pMonitor); + void requestBackgroundResource(); + std::string resolveAssetPath(const std::string& file); + void initMissingAssetTexture(); + void initAssets(); + SP<ITexture> m_missingAssetTexture; + ASP<Hyprgraphics::CImageResource> m_backgroundResource; + bool m_backgroundResourceFailed = false; + + bool shouldBlur(PHLLS ls); + bool shouldBlur(PHLWINDOW w); + bool shouldBlur(WP<Desktop::View::CPopup> p); + + bool m_cursorHidden = false; + bool m_cursorHiddenByCondition = false; + bool m_cursorHasSurface = false; + SP<Aquamarine::IBuffer> m_currentBuffer = nullptr; + eRenderMode m_renderMode = RENDER_MODE_NORMAL; + bool m_nvidia = false; + bool m_intel = false; + bool m_software = false; + bool m_mgpu = false; + + struct { + bool hiddenOnTouch = false; + bool hiddenOnTablet = false; + bool hiddenOnTimeout = false; + bool hiddenOnKeyboard = false; + } m_cursorHiddenConditions; + + std::vector<SP<IRenderbuffer>> m_renderbuffers; + std::vector<PHLWINDOWREF> m_renderUnfocused; + SP<CEventLoopTimer> m_renderUnfocusedTimer; + + friend class CRenderPass; + friend class Render::GL::CHyprOpenGLImpl; + friend class CToplevelExportFrame; + friend class Screenshare::CScreenshareFrame; + friend class CInputManager; + friend class CPointerManager; + friend class CMonitor; + friend class CMonitorFrameScheduler; + + private: + void bindOffMain(); + void bindBackOnMain(); }; - SP<ITexture> 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(CInnerGlowPassElement* 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<ITexture> getBlurTexture(PHLMONITORREF pMonitor); - SP<ITexture> m_lockDeadTexture; - SP<ITexture> m_lockDead2Texture; - SP<ITexture> m_lockTtyTextTexture; - bool m_monitorTransformEnabled = false; // do not modify directly - std::stack<bool> m_monitorTransformStack; - - // old private: - void arrangeLayerArray(PHLMONITOR, const std::vector<PHLLSREF>&, 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) - void renderWorkspaceWindows(PHLMONITOR, PHLWORKSPACE, const Time::steady_tp&); // renders workspace windows (no fullscreen) (tiled, floating, pinned, but no special) - void renderAllClientsForWorkspace(PHLMONITOR pMonitor, PHLWORKSPACE pWorkspace, const Time::steady_tp& now, const Vector2D& translate = {0, 0}, const float& scale = 1.f); - void renderWindow(PHLWINDOW, PHLMONITOR, const Time::steady_tp&, bool, eRenderPassMode, bool ignorePosition = false, bool standalone = false); - void renderLayer(PHLLS, PHLMONITOR, const Time::steady_tp&, bool popups = false, bool lockscreen = false); - void renderSessionLockSurface(WP<SSessionLockSurface>, PHLMONITOR, const Time::steady_tp&); - void renderDragIcon(PHLMONITOR, const Time::steady_tp&); - void renderIMEPopup(CInputPopup*, PHLMONITOR, const Time::steady_tp&); - void sendFrameEventsToWorkspace(PHLMONITOR pMonitor, PHLWORKSPACE pWorkspace, const Time::steady_tp& now); // sends frame displayed events but doesn't actually render anything - void renderSessionLockPrimer(PHLMONITOR pMonitor); - void renderSessionLockMissing(PHLMONITOR pMonitor); - void renderBackground(PHLMONITOR pMonitor); - void requestBackgroundResource(); - std::string resolveAssetPath(const std::string& file); - void initMissingAssetTexture(); - void initAssets(); - SP<ITexture> m_missingAssetTexture; - ASP<Hyprgraphics::CImageResource> m_backgroundResource; - bool m_backgroundResourceFailed = false; - - bool shouldBlur(PHLLS ls); - bool shouldBlur(PHLWINDOW w); - bool shouldBlur(WP<Desktop::View::CPopup> p); - - bool m_cursorHidden = false; - bool m_cursorHiddenByCondition = false; - bool m_cursorHasSurface = false; - SP<Aquamarine::IBuffer> m_currentBuffer = nullptr; - eRenderMode m_renderMode = RENDER_MODE_NORMAL; - bool m_nvidia = false; - bool m_intel = false; - bool m_software = false; - bool m_mgpu = false; - - struct { - bool hiddenOnTouch = false; - bool hiddenOnTablet = false; - bool hiddenOnTimeout = false; - bool hiddenOnKeyboard = false; - } m_cursorHiddenConditions; - - std::vector<SP<IRenderbuffer>> m_renderbuffers; - std::vector<PHLWINDOWREF> m_renderUnfocused; - SP<CEventLoopTimer> m_renderUnfocusedTimer; - - friend class CRenderPass; - friend class CHyprOpenGLImpl; - friend class CToplevelExportFrame; - friend class Screenshare::CScreenshareFrame; - friend class CInputManager; - 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<IHyprRenderer> g_pHyprRenderer; +inline UP<Render::IHyprRenderer> g_pHyprRenderer;
\ No newline at end of file diff --git a/src/render/Shader.cpp b/src/render/Shader.cpp index ed272fcd..dfc9045a 100644 --- a/src/render/Shader.cpp +++ b/src/render/Shader.cpp @@ -5,6 +5,8 @@ #define EPSILON(x, y) (std::abs((x) - (y)) < 1e-5f) +using namespace Render::GL; + static bool compareFloat(auto a, auto b) { if (a.size() != b.size()) return false; diff --git a/src/render/ShaderLoader.cpp b/src/render/ShaderLoader.cpp index 5cef2d99..388c1d43 100644 --- a/src/render/ShaderLoader.cpp +++ b/src/render/ShaderLoader.cpp @@ -14,8 +14,6 @@ using namespace Render; -using namespace Render; - CShaderLoader::CShaderLoader(const std::vector<std::string> includes, const std::array<std::string, SH_FRAG_LAST>& frags, const std::string shaderPath) : m_shaderPath(shaderPath) { m_callbacks = glsl_include_callbacks_t{ .include_local = @@ -72,6 +70,7 @@ std::string CShaderLoader::getDefines(ShaderFeatureFlags features) { {"USE_RGBA", features & SH_FEAT_RGBA ? "1" : "0"}, {"USE_DISCARD", features & SH_FEAT_DISCARD ? "1" : "0"}, {"USE_TINT", features & SH_FEAT_TINT ? "1" : "0"}, {"USE_ROUNDING", features & SH_FEAT_ROUNDING ? "1" : "0"}, {"USE_CM", features & SH_FEAT_CM ? "1" : "0"}, {"USE_TONEMAP", features & SH_FEAT_TONEMAP ? "1" : "0"}, {"USE_SDR_MOD", features & SH_FEAT_SDR_MOD ? "1" : "0"}, {"USE_BLUR", features & SH_FEAT_BLUR ? "1" : "0"}, {"USE_ICC", features & SH_FEAT_ICC ? "1" : "0"}, + {"USE_MIRROR", features & SH_FEAT_MIRROR ? "1" : "0"}, }; for (const auto& [name, value] : defines) { res += std::format("#define {} {}\n", name, value); diff --git a/src/render/ShaderLoader.hpp b/src/render/ShaderLoader.hpp index 9f3f787d..58184659 100644 --- a/src/render/ShaderLoader.hpp +++ b/src/render/ShaderLoader.hpp @@ -20,6 +20,7 @@ namespace Render { SH_FEAT_SDR_MOD = (1 << 6), // uniforms: sdrSaturation, sdrBrightnessMultiplier; condition: SDR <-> HDR && (sdrSaturation != 1 || sdrBrightnessMultiplier != 1) SH_FEAT_BLUR = (1 << 7), // condition: render:use_shader_blur_blend SH_FEAT_ICC = (1 << 8), // + SH_FEAT_MIRROR = (1 << 9), // condition: mirror or screenshare // uniforms: targetPrimariesXYZ; condition: SH_FEAT_TONEMAP || SH_FEAT_SDR_MOD }; diff --git a/src/render/SyncFDManager.cpp b/src/render/SyncFDManager.cpp new file mode 100644 index 00000000..3895c406 --- /dev/null +++ b/src/render/SyncFDManager.cpp @@ -0,0 +1,16 @@ +#include "SyncFDManager.hpp" +#include <utility> + +using namespace Render; + +Hyprutils::OS::CFileDescriptor& ISyncFDManager::fd() { + return m_fd; +} + +Hyprutils::OS::CFileDescriptor&& ISyncFDManager::takeFd() { + return std::move(m_fd); +} + +bool ISyncFDManager::isValid() { + return m_valid && m_fd.isValid(); +} diff --git a/src/render/SyncFDManager.hpp b/src/render/SyncFDManager.hpp new file mode 100644 index 00000000..5996fbeb --- /dev/null +++ b/src/render/SyncFDManager.hpp @@ -0,0 +1,20 @@ +#pragma once + +#include <hyprutils/os/FileDescriptor.hpp> + +namespace Render { + class ISyncFDManager { + public: + virtual ~ISyncFDManager() = default; + + Hyprutils::OS::CFileDescriptor& fd(); + Hyprutils::OS::CFileDescriptor&& takeFd(); + virtual bool isValid(); + + protected: + ISyncFDManager() = default; + + Hyprutils::OS::CFileDescriptor m_fd; + bool m_valid = false; + }; +} diff --git a/src/render/Texture.cpp b/src/render/Texture.cpp index 28ae4b41..1201f044 100644 --- a/src/render/Texture.cpp +++ b/src/render/Texture.cpp @@ -1,6 +1,8 @@ #include "Texture.hpp" #include <cstring> +using namespace Render; + ITexture::ITexture(uint32_t drmFormat, uint8_t* pixels, uint32_t stride, const Vector2D& size, bool keepDataCopy, bool opaque) : m_size(size), m_opaque(opaque), m_drmFormat(drmFormat), m_keepDataCopy(keepDataCopy) { if (m_keepDataCopy && stride && pixels) { diff --git a/src/render/Texture.hpp b/src/render/Texture.hpp index 38c3ff01..473e5ad9 100644 --- a/src/render/Texture.hpp +++ b/src/render/Texture.hpp @@ -1,6 +1,7 @@ #pragma once #include "../defines.hpp" +#include "../helpers/cm/ColorManagement.hpp" #include <aquamarine/buffer/Buffer.hpp> #include <hyprutils/math/Misc.hpp> #include <span> @@ -8,51 +9,56 @@ class IHLBuffer; HYPRUTILS_FORWARD(Math, CRegion); -enum eTextureType : int8_t { - TEXTURE_INVALID = -1, // Invalid - TEXTURE_RGBA = 0, // 4 channels - TEXTURE_RGBX, // discard A - TEXTURE_3D_LUT, // 3D LUT - TEXTURE_EXTERNAL, // EGLImage -}; - -class ITexture { - public: - ITexture(ITexture&) = delete; - ITexture(ITexture&&) = delete; - ITexture(const ITexture&&) = delete; - ITexture(const ITexture&) = delete; - - virtual ~ITexture() = default; - - virtual void setTexParameter(GLenum pname, GLint param) = 0; - virtual void allocate(const Vector2D& size, uint32_t drmFormat = 0) = 0; - virtual void update(uint32_t drmFormat, uint8_t* pixels, uint32_t stride, const CRegion& damage) = 0; - virtual void bind() {}; - virtual void unbind() {}; - virtual bool ok(); - virtual bool isDMA(); - - const std::vector<uint8_t>& dataCopy(); - - eTextureType m_type = TEXTURE_RGBA; - Vector2D m_size = {}; - eTransform m_transform = HYPRUTILS_TRANSFORM_NORMAL; - bool m_opaque = false; - - uint32_t m_drmFormat = 0; // for shm - bool m_isSynchronous = false; - - // TODO move to GLTexture - GLuint m_texID = 0; - GLenum magFilter = GL_LINEAR; // useNearestNeighbor overwrites these - GLenum minFilter = GL_LINEAR; - - protected: - ITexture() = default; - ITexture(uint32_t drmFormat, uint8_t* pixels, uint32_t stride, const Vector2D& size, bool keepDataCopy = false, bool opaque = false); - ITexture(std::span<const float> lut3D, size_t N); - - bool m_keepDataCopy = false; - std::vector<uint8_t> m_dataCopy; -}; +namespace Render { + enum eTextureType : int8_t { + TEXTURE_INVALID = -1, // Invalid + TEXTURE_RGBA = 0, // 4 channels + TEXTURE_RGBX, // discard A + TEXTURE_3D_LUT, // 3D LUT + TEXTURE_EXTERNAL, // EGLImage + }; + + class ITexture { + public: + ITexture(ITexture&) = delete; + ITexture(ITexture&&) = delete; + ITexture(const ITexture&&) = delete; + ITexture(const ITexture&) = delete; + + virtual ~ITexture() = default; + + virtual void setTexParameter(GLenum pname, GLint param) = 0; + virtual void allocate(const Vector2D& size, uint32_t drmFormat = 0) = 0; + virtual void update(uint32_t drmFormat, uint8_t* pixels, uint32_t stride, const CRegion& damage) = 0; + virtual void bind() {}; + virtual void unbind() {}; + virtual bool ok(); + virtual bool isDMA(); + + const std::vector<uint8_t>& dataCopy(); + + eTextureType m_type = TEXTURE_RGBA; + Vector2D m_size = {}; + eTransform m_transform = HYPRUTILS_TRANSFORM_NORMAL; + bool m_opaque = false; + + uint32_t m_drmFormat = 0; // for shm + bool m_isSynchronous = false; + + // CM + NColorManagement::PImageDescription m_imageDescription; + + // TODO move to GLTexture + GLuint m_texID = 0; + GLenum magFilter = GL_LINEAR; // useNearestNeighbor overwrites these + GLenum minFilter = GL_LINEAR; + + protected: + ITexture() = default; + ITexture(uint32_t drmFormat, uint8_t* pixels, uint32_t stride, const Vector2D& size, bool keepDataCopy = false, bool opaque = false); + ITexture(std::span<const float> lut3D, size_t N); + + bool m_keepDataCopy = false; + std::vector<uint8_t> m_dataCopy; + }; +} diff --git a/src/render/Transformer.hpp b/src/render/Transformer.hpp index 8f401859..bebf5500 100644 --- a/src/render/Transformer.hpp +++ b/src/render/Transformer.hpp @@ -14,7 +14,7 @@ class IWindowTransformer { // called by Hyprland. For more data about what is being rendered, inspect render data. // returns the out fb. - virtual IFramebuffer* transform(IFramebuffer* in) = 0; + virtual SP<Render::IFramebuffer> transform(SP<Render::IFramebuffer> in) = 0; // called by Hyprland before a window main pass is started. virtual void preWindowRender(CSurfacePassElement::SRenderData* pRenderData); diff --git a/src/render/decorations/CHyprDropShadowDecoration.cpp b/src/render/decorations/CHyprDropShadowDecoration.cpp index 2d4eaba2..44a42d25 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 "../pass/RectPassElement.hpp" #include "../pass/TextureMatteElement.hpp" CHyprDropShadowDecoration::CHyprDropShadowDecoration(PHLWINDOW pWindow) : IHyprWindowDecoration(pWindow), m_window(pWindow) { @@ -233,50 +234,50 @@ void CHyprDropShadowDecoration::render(PHLMONITOR pMonitor, float const& a) { 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; + const auto alphaFB = g_pHyprRenderer->m_renderData.pMonitor->resources()->getUnusedWorkBuffer(); + const auto alphaSwapFB = g_pHyprRenderer->m_renderData.pMonitor->resources()->getUnusedWorkBuffer(); CBox monbox = {0, 0, pMonitor->m_transformedSize.x, pMonitor->m_transformedSize.y}; - alphaFB->bind(); + auto guard = g_pHyprRenderer->bindTempFB(alphaFB); // store current FB inside guard + // TODO not needed for 8bpc and 16fp? // 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_pHyprRenderer->draw(makeUnique<CRectPassElement>(CRectPassElement::SRectData{.box = data.fullBox, .color = CHyprColor(0, 0, 0, 1), .round = 0}), monbox); + g_pHyprRenderer->draw(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(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_pHyprRenderer->draw(makeUnique<CRectPassElement>(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); + g_pHyprRenderer->draw( + 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(); + g_pHyprRenderer->bindFB(alphaSwapFB); // alpha swap just has the shadow color. It will be the "texture" to render. - g_pHyprRenderer->draw(makeUnique<CRectPassElement>(CRectPassElement::SRectData{.box = data.fullBox, .color = PWINDOW->m_realShadowColor->value().stripA(), .round = 0}), - monbox); + g_pHyprRenderer->draw(CRectPassElement::SRectData{.box = data.fullBox, .color = PWINDOW->m_realShadowColor->value().stripA(), .round = 0}, monbox); - LASTFB->bind(); + guard.reset(); // restore FB g_pHyprRenderer->pushMonitorTransformEnabled(true); g_pHyprRenderer->m_renderData.renderModif.enabled = false; - g_pHyprRenderer->draw(makeUnique<CTextureMatteElement>(CTextureMatteElement::STextureMatteData{ - .box = monbox, - .tex = alphaSwapFB->getTexture(), - .fb = alphaFB, - }), - {}); + g_pHyprRenderer->draw( + CTextureMatteElement::STextureMatteData{ + .box = monbox, + .tex = alphaSwapFB->getTexture(), + .fb = alphaFB, + }, + {}); g_pHyprRenderer->m_renderData.renderModif.enabled = true; g_pHyprRenderer->popMonitorTransformEnabled(); @@ -303,13 +304,12 @@ void CHyprDropShadowDecoration::drawShadowInternal(const CBox& box, int round, f color.a *= a; if (*PSHADOWSHARP) - g_pHyprRenderer->draw(makeUnique<CRectPassElement>(CRectPassElement::SRectData{ - .box = box, - .color = color, - .round = round, - .roundingPower = roundingPower, - }), - {}); + g_pHyprRenderer->draw(CRectPassElement::SRectData{ + .box = box, + .color = color, + .round = round, + .roundingPower = roundingPower, + }); else g_pHyprRenderer->drawShadow(box, round, roundingPower, range, color, 1.F); } diff --git a/src/render/decorations/CHyprGroupBarDecoration.hpp b/src/render/decorations/CHyprGroupBarDecoration.hpp index 5c3f4ae5..f6581c8d 100644 --- a/src/render/decorations/CHyprGroupBarDecoration.hpp +++ b/src/render/decorations/CHyprGroupBarDecoration.hpp @@ -12,13 +12,13 @@ class CTitleTex { CTitleTex(PHLWINDOW pWindow, const Vector2D& bufferSize, const float monitorScale); ~CTitleTex() = default; - SP<ITexture> m_texActive; - SP<ITexture> m_texInactive; - SP<ITexture> m_texLockedActive; - SP<ITexture> m_texLockedInactive; - std::string m_content; + SP<Render::ITexture> m_texActive; + SP<Render::ITexture> m_texInactive; + SP<Render::ITexture> m_texLockedActive; + SP<Render::ITexture> m_texLockedInactive; + std::string m_content; - PHLWINDOWREF m_windowOwner; + PHLWINDOWREF m_windowOwner; }; void refreshGroupBarGradients(); diff --git a/src/render/decorations/CHyprInnerGlowDecoration.cpp b/src/render/decorations/CHyprInnerGlowDecoration.cpp index a60deccb..687a77b1 100644 --- a/src/render/decorations/CHyprInnerGlowDecoration.cpp +++ b/src/render/decorations/CHyprInnerGlowDecoration.cpp @@ -94,7 +94,8 @@ void CHyprInnerGlowDecoration::render(PHLMONITOR pMonitor, float const& a) { g_pHyprRenderer->blend(true); - g_pHyprOpenGL->renderInnerGlow(windowBox, ROUNDING * pMonitor->m_scale, ROUNDINGPOWER, GLOWSIZE * pMonitor->m_scale, GLOWCOLOR, GLOWPOWER, a); + // FIXME use g_pHyprRenderer API + Render::GL::g_pHyprOpenGL->renderInnerGlow(windowBox, ROUNDING * pMonitor->m_scale, ROUNDINGPOWER, GLOWSIZE * pMonitor->m_scale, GLOWCOLOR, GLOWPOWER, a); g_pHyprRenderer->m_renderData.currentWindow.reset(); } diff --git a/src/render/gl/GLElementRenderer.cpp b/src/render/gl/GLElementRenderer.cpp new file mode 100644 index 00000000..670c7886 --- /dev/null +++ b/src/render/gl/GLElementRenderer.cpp @@ -0,0 +1,138 @@ +#include "GLElementRenderer.hpp" +#include "../Renderer.hpp" +#include "../decorations/CHyprDropShadowDecoration.hpp" +#include "../OpenGL.hpp" +#include "../decorations/CHyprInnerGlowDecoration.hpp" +#include <cstdint> + +using namespace Render::GL; + +void CGLElementRenderer::draw(WP<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 CGLElementRenderer::draw(WP<CClearPassElement> element, const CRegion& damage) { + const auto& color = element->m_data.color; + RASSERT(g_pHyprRenderer->m_renderData.pMonitor, "Tried to render without begin()!"); + + TRACY_GPU_ZONE("RenderClear"); + + GLCALL(glClearColor(color.r, color.g, color.b, color.a)); + + if (!g_pHyprRenderer->m_renderData.damage.empty()) { + g_pHyprRenderer->m_renderData.damage.forEachRect([](const auto& RECT) { + g_pHyprOpenGL->scissor(&RECT, g_pHyprRenderer->m_renderData.transformDamage); + glClear(GL_COLOR_BUFFER_BIT); + }); + } +}; + +void CGLElementRenderer::draw(WP<CFramebufferElement> element, const CRegion& damage) { + Log::logger->log(Log::ERR, "Deprecated CFramebufferElement. Use g_pHyprRenderer->m_renderData and CTexPassElement instead"); + // const auto m_data = element->m_data; + // SP<IFramebuffer> 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 = g_pHyprRenderer->m_renderData.pMonitor->m_mirrorFB; break; + // case FB_MONITOR_RENDER_EXTRA_MIRROR_SWAP: fb = g_pHyprRenderer->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; + // } + // } + + // g_pHyprRenderer->bindFB(fb); +}; + +void CGLElementRenderer::draw(WP<CPreBlurElement> element, const CRegion& damage) { + auto dmg = damage; + g_pHyprRenderer->preBlurForCurrentMonitor(&dmg); +}; + +void CGLElementRenderer::draw(WP<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 CGLElementRenderer::draw(WP<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 CGLElementRenderer::draw(WP<CInnerGlowPassElement> 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 CGLElementRenderer::draw(WP<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<uint32_t>(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 CGLElementRenderer::draw(WP<CTextureMatteElement> element, const CRegion& damage) { + const auto m_data = element->m_data; + + g_pHyprOpenGL->renderTextureMatte(m_data.tex, m_data.box, m_data.fb); +};
\ No newline at end of file diff --git a/src/render/gl/GLElementRenderer.hpp b/src/render/gl/GLElementRenderer.hpp new file mode 100644 index 00000000..675714fe --- /dev/null +++ b/src/render/gl/GLElementRenderer.hpp @@ -0,0 +1,22 @@ +#pragma once + +#include "../ElementRenderer.hpp" + +namespace Render::GL { + class CGLElementRenderer : public Render::IElementRenderer { + public: + CGLElementRenderer() = default; + ~CGLElementRenderer() = default; + + private: + void draw(WP<CBorderPassElement> element, const Hyprutils::Math::CRegion& damage) override; + void draw(WP<CClearPassElement> element, const CRegion& damage) override; + void draw(WP<CFramebufferElement> element, const CRegion& damage) override; + void draw(WP<CPreBlurElement> element, const CRegion& damage) override; + void draw(WP<CRectPassElement> element, const CRegion& damage) override; + void draw(WP<CShadowPassElement> element, const CRegion& damage) override; + void draw(WP<CInnerGlowPassElement> element, const CRegion& damage) override; + void draw(WP<CTexPassElement> element, const CRegion& damage) override; + void draw(WP<CTextureMatteElement> element, const CRegion& damage) override; + }; +} diff --git a/src/render/gl/GLFramebuffer.cpp b/src/render/gl/GLFramebuffer.cpp index 27ad4a1b..0ad84e62 100644 --- a/src/render/gl/GLFramebuffer.cpp +++ b/src/render/gl/GLFramebuffer.cpp @@ -4,14 +4,14 @@ #include "macros.hpp" #include "../Framebuffer.hpp" +using namespace Render::GL; + CGLFramebuffer::CGLFramebuffer() : IFramebuffer() {} CGLFramebuffer::CGLFramebuffer(const std::string& name) : IFramebuffer(name) {} bool CGLFramebuffer::internalAlloc(int w, int h, uint32_t drmFormat) { g_pHyprOpenGL->makeEGLCurrent(); - bool firstAlloc = false; - if (!m_tex) { m_tex = g_pHyprRenderer->createTexture(); m_tex->allocate({w, h}); @@ -20,39 +20,44 @@ bool CGLFramebuffer::internalAlloc(int w, int h, uint32_t drmFormat) { m_tex->setTexParameter(GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); m_tex->setTexParameter(GL_TEXTURE_MAG_FILTER, GL_LINEAR); m_tex->setTexParameter(GL_TEXTURE_MIN_FILTER, GL_LINEAR); - firstAlloc = true; } if (!m_fbAllocated) { glGenFramebuffers(1, &m_fb); m_fbAllocated = true; - firstAlloc = true; } - if (firstAlloc) { - const auto format = NFormatUtils::getPixelFormatFromDRM(drmFormat); - m_tex->bind(); + const auto format = NFormatUtils::getPixelFormatFromDRM(drmFormat); + m_tex->bind(); + glTexImage2D(GL_TEXTURE_2D, 0, format->glInternalFormat ? format->glInternalFormat : format->glFormat, w, h, 0, format->glFormat, format->glType, nullptr); + glBindFramebuffer(GL_FRAMEBUFFER, m_fb); + glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, m_tex->m_texID, 0); + + if (m_mirrorTex) { + const auto format = NFormatUtils::getPixelFormatFromDRM(m_mirrorTex->m_drmFormat); + m_mirrorTex->bind(); glTexImage2D(GL_TEXTURE_2D, 0, format->glInternalFormat ? format->glInternalFormat : format->glFormat, w, h, 0, format->glFormat, format->glType, nullptr); glBindFramebuffer(GL_FRAMEBUFFER, m_fb); - glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, m_tex->m_texID, 0); + glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT1, GL_TEXTURE_2D, m_mirrorTex->m_texID, 0); + } else + glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT1, GL_TEXTURE_2D, 0, 0); - if (m_stencilTex && m_stencilTex->ok()) { - m_stencilTex->bind(); - glTexImage2D(GL_TEXTURE_2D, 0, GL_DEPTH24_STENCIL8, w, h, 0, GL_DEPTH_STENCIL, GL_UNSIGNED_INT_24_8, nullptr); - glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_TEXTURE_2D, m_stencilTex->m_texID, 0); + if (m_stencilTex && m_stencilTex->ok()) { + m_stencilTex->bind(); + glTexImage2D(GL_TEXTURE_2D, 0, GL_DEPTH24_STENCIL8, w, h, 0, GL_DEPTH_STENCIL, GL_UNSIGNED_INT_24_8, nullptr); + glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_TEXTURE_2D, m_stencilTex->m_texID, 0); - glDisable(GL_DEPTH_TEST); - glDepthMask(GL_FALSE); - } + glDisable(GL_DEPTH_TEST); + glDepthMask(GL_FALSE); + } - auto status = glCheckFramebufferStatus(GL_FRAMEBUFFER); - RASSERT((status == GL_FRAMEBUFFER_COMPLETE), "Framebuffer incomplete, couldn't create! (FB status: {}, GL Error: 0x{:x})", status, sc<int>(glGetError())); + auto status = glCheckFramebufferStatus(GL_FRAMEBUFFER); + RASSERT((status == GL_FRAMEBUFFER_COMPLETE), "Framebuffer incomplete, couldn't create! (FB status: {}, GL Error: 0x{:x})", status, sc<int>(glGetError())); - if (m_stencilTex && m_stencilTex->ok()) - m_stencilTex->unbind(); + if (m_stencilTex && m_stencilTex->ok()) + m_stencilTex->unbind(); - Log::logger->log(Log::DEBUG, "Framebuffer created, status {}", status); - } + Log::logger->log(Log::DEBUG, "Framebuffer \"{}\" created, status {}", m_name, status); glBindTexture(GL_TEXTURE_2D, 0); glBindFramebuffer(GL_FRAMEBUFFER, 0); @@ -86,6 +91,8 @@ void CGLFramebuffer::release() { if (m_fbAllocated) { glBindFramebuffer(GL_FRAMEBUFFER, m_fb); glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, 0, 0); + if (m_mirrorTex) + glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT1, GL_TEXTURE_2D, 0, 0); glBindFramebuffer(GL_FRAMEBUFFER, 0); glDeleteFramebuffers(1, &m_fb); diff --git a/src/render/gl/GLFramebuffer.hpp b/src/render/gl/GLFramebuffer.hpp index c171444e..1be5d0e0 100644 --- a/src/render/gl/GLFramebuffer.hpp +++ b/src/render/gl/GLFramebuffer.hpp @@ -5,26 +5,28 @@ #include "../Framebuffer.hpp" #include <drm_fourcc.h> -class CGLFramebuffer : public IFramebuffer { - public: - CGLFramebuffer(); - CGLFramebuffer(const std::string& name); - ~CGLFramebuffer(); +namespace Render::GL { + class CGLFramebuffer : public IFramebuffer { + public: + CGLFramebuffer(); + CGLFramebuffer(const std::string& name); + ~CGLFramebuffer(); - void addStencil(SP<ITexture> tex) override; - void release() override; - bool readPixels(CHLBufferReference buffer, uint32_t offsetX = 0, uint32_t offsetY = 0, uint32_t width = 0, uint32_t height = 0) override; + void addStencil(SP<ITexture> tex) override; + void release() override; + bool readPixels(CHLBufferReference buffer, uint32_t offsetX = 0, uint32_t offsetY = 0, uint32_t width = 0, uint32_t height = 0) override; - void bind() override; - void unbind(); - GLuint getFBID(); - void invalidate(const std::vector<GLenum>& attachments); + void bind() override; + void unbind(); + GLuint getFBID(); + void invalidate(const std::vector<GLenum>& attachments); - protected: - bool internalAlloc(int w, int h, uint32_t format = DRM_FORMAT_ARGB8888) override; + protected: + bool internalAlloc(int w, int h, DRMFormat format = DRM_FORMAT_ARGB8888) override; - private: - GLuint m_fb = -1; + private: + GLuint m_fb = -1; - friend class CGLRenderbuffer; -}; + friend class CGLRenderbuffer; + }; +} diff --git a/src/render/gl/GLRenderbuffer.cpp b/src/render/gl/GLRenderbuffer.cpp index aa8b5be2..208957e5 100644 --- a/src/render/gl/GLRenderbuffer.cpp +++ b/src/render/gl/GLRenderbuffer.cpp @@ -11,6 +11,8 @@ #include <dlfcn.h> +using namespace Render::GL; + CGLRenderbuffer::~CGLRenderbuffer() { if (!g_pCompositor || g_pCompositor->m_isShuttingDown || !g_pHyprRenderer) return; @@ -63,7 +65,7 @@ CGLRenderbuffer::CGLRenderbuffer(SP<Aquamarine::IBuffer> buffer, uint32_t format void CGLRenderbuffer::bind() { g_pHyprOpenGL->makeEGLCurrent(); - m_framebuffer->bind(); + g_pHyprRenderer->bindFB(m_framebuffer); } void CGLRenderbuffer::unbind() { diff --git a/src/render/gl/GLRenderbuffer.hpp b/src/render/gl/GLRenderbuffer.hpp index 8367f702..137a8e0d 100644 --- a/src/render/gl/GLRenderbuffer.hpp +++ b/src/render/gl/GLRenderbuffer.hpp @@ -6,15 +6,17 @@ class CMonitor; -class CGLRenderbuffer : public IRenderbuffer { - public: - CGLRenderbuffer(SP<Aquamarine::IBuffer> buffer, uint32_t format); - ~CGLRenderbuffer(); +namespace Render::GL { + class CGLRenderbuffer : public IRenderbuffer { + public: + CGLRenderbuffer(SP<Aquamarine::IBuffer> buffer, uint32_t format); + ~CGLRenderbuffer(); - void bind() override; - void unbind() override; + void bind() override; + void unbind() override; - private: - void* m_image = nullptr; - GLuint m_rbo = 0; -}; + private: + void* m_image = nullptr; + GLuint m_rbo = 0; + }; +} diff --git a/src/render/gl/GLTexture.cpp b/src/render/gl/GLTexture.cpp index 93f85c84..d34bdb60 100644 --- a/src/render/gl/GLTexture.cpp +++ b/src/render/gl/GLTexture.cpp @@ -5,6 +5,8 @@ #include "../Texture.hpp" #include <cstring> +using namespace Render::GL; + CGLTexture::CGLTexture(bool opaque) { m_opaque = opaque; } diff --git a/src/render/gl/GLTexture.hpp b/src/render/gl/GLTexture.hpp index 34510e90..07b51d9a 100644 --- a/src/render/gl/GLTexture.hpp +++ b/src/render/gl/GLTexture.hpp @@ -4,46 +4,49 @@ #include <aquamarine/buffer/Buffer.hpp> #include <hyprutils/math/Misc.hpp> -class CGLTexture : public ITexture { - public: - using ITexture::ITexture; - - CGLTexture(CGLTexture&) = delete; - CGLTexture(CGLTexture&&) = delete; - CGLTexture(const CGLTexture&&) = delete; - CGLTexture(const CGLTexture&) = delete; - - CGLTexture(bool opaque = false); - CGLTexture(uint32_t drmFormat, uint8_t* pixels, uint32_t stride, const Vector2D& size, bool keepDataCopy = false, bool opaque = false); - CGLTexture(const Aquamarine::SDMABUFAttrs&, void* image, bool opaque = false); - CGLTexture(std::span<const float> lut3D, size_t N); - ~CGLTexture(); - - void allocate(const Vector2D& size, uint32_t drmFormat = 0) override; - void update(uint32_t drmFormat, uint8_t* pixels, uint32_t stride, const CRegion& damage) override; - void bind() override; - void unbind() override; - void setTexParameter(GLenum pname, GLint param) override; - bool ok() override; - bool isDMA() override; - - private: - void* m_eglImage = nullptr; - - enum eTextureParam : uint8_t { - TEXTURE_PAR_WRAP_S = 0, - TEXTURE_PAR_WRAP_T, - TEXTURE_PAR_MAG_FILTER, - TEXTURE_PAR_MIN_FILTER, - TEXTURE_PAR_SWIZZLE_R, - TEXTURE_PAR_SWIZZLE_B, - TEXTURE_PAR_LAST, +namespace Render::GL { + + class CGLTexture : public ITexture { + public: + using ITexture::ITexture; + + CGLTexture(CGLTexture&) = delete; + CGLTexture(CGLTexture&&) = delete; + CGLTexture(const CGLTexture&&) = delete; + CGLTexture(const CGLTexture&) = delete; + + CGLTexture(bool opaque = false); + CGLTexture(uint32_t drmFormat, uint8_t* pixels, uint32_t stride, const Vector2D& size, bool keepDataCopy = false, bool opaque = false); + CGLTexture(const Aquamarine::SDMABUFAttrs&, void* image, bool opaque = false); + CGLTexture(std::span<const float> lut3D, size_t N); + ~CGLTexture(); + + void allocate(const Vector2D& size, uint32_t drmFormat = 0) override; + void update(uint32_t drmFormat, uint8_t* pixels, uint32_t stride, const CRegion& damage) override; + void bind() override; + void unbind() override; + void setTexParameter(GLenum pname, GLint param) override; + bool ok() override; + bool isDMA() override; + + private: + void* m_eglImage = nullptr; + + enum eTextureParam : uint8_t { + TEXTURE_PAR_WRAP_S = 0, + TEXTURE_PAR_WRAP_T, + TEXTURE_PAR_MAG_FILTER, + TEXTURE_PAR_MIN_FILTER, + TEXTURE_PAR_SWIZZLE_R, + TEXTURE_PAR_SWIZZLE_B, + TEXTURE_PAR_LAST, + }; + + GLenum m_target = GL_TEXTURE_2D; + + void swizzle(const std::array<GLint, 4>& colors); + constexpr std::optional<size_t> getCacheStateIndex(GLenum pname); + + std::array<std::optional<GLint>, TEXTURE_PAR_LAST> m_cachedStates; }; - - GLenum m_target = GL_TEXTURE_2D; - - void swizzle(const std::array<GLint, 4>& colors); - constexpr std::optional<size_t> getCacheStateIndex(GLenum pname); - - std::array<std::optional<GLint>, TEXTURE_PAR_LAST> m_cachedStates; -}; +} diff --git a/src/render/pass/Pass.cpp b/src/render/pass/Pass.cpp index cbf6ea48..a097fd59 100644 --- a/src/render/pass/Pass.cpp +++ b/src/render/pass/Pass.cpp @@ -10,6 +10,10 @@ #include "../../render/Renderer.hpp" #include "../../desktop/state/FocusState.hpp" #include "../../protocols/core/Compositor.hpp" +#include "RectPassElement.hpp" +#include "macros.hpp" + +using namespace Render; bool CRenderPass::empty() const { return false; @@ -213,15 +217,10 @@ void CRenderPass::renderDebugData() { const auto pMonitor = g_pHyprRenderer->m_renderData.pMonitor; CBox box = {{}, pMonitor->m_transformedSize}; for (const auto& rg : m_occludedRegions) { - CRectPassElement::SRectData data; - data.box = box; - data.color = Colors::RED.modifyA(0.1F); - g_pHyprRenderer->draw(makeUnique<CRectPassElement>(data), rg); + g_pHyprRenderer->draw(CRectPassElement::SRectData{.box = box, .color = Colors::RED.modifyA(0.1F)}, rg); } - CRectPassElement::SRectData data; - data.box = box; - data.color = Colors::GREEN.modifyA(0.1F); - g_pHyprRenderer->draw(makeUnique<CRectPassElement>(data), m_totalLiveBlurRegion); + + g_pHyprRenderer->draw(CRectPassElement::SRectData{.box = box, .color = Colors::GREEN.modifyA(0.1F)}, m_totalLiveBlurRegion); std::unordered_map<CWLSurfaceResource*, float> offsets; @@ -244,10 +243,7 @@ void CRenderPass::renderDebugData() { if (box.intersection(CBox{{}, pMonitor->m_size}).empty()) return; - CRectPassElement::SRectData data; - data.box = box; - data.color = color; - g_pHyprRenderer->draw(makeUnique<CRectPassElement>(data), m_damage); + g_pHyprRenderer->draw(CRectPassElement::SRectData{.box = box, .color = color}, m_damage); if (offsets.contains(surface.get())) box.translate(Vector2D{0.F, offsets[surface.get()]}); @@ -255,16 +251,16 @@ void CRenderPass::renderDebugData() { offsets[surface.get()] = 0; box = {box.pos(), texture->m_size}; - CRectPassElement::SRectData data2; - data.box = box; - data.color = color; - data.round = std::min(5.0, box.size().y); - g_pHyprRenderer->draw(makeUnique<CRectPassElement>(data2), m_damage); - CTexPassElement::SRenderData texData; - texData.tex = texture; - texData.box = box; - g_pHyprRenderer->draw(makeUnique<CTexPassElement>(texData), m_damage); + g_pHyprRenderer->draw( + CRectPassElement::SRectData{ + .box = box, + .color = color, + .round = std::min(5.0, box.size().y), + }, + m_damage); + + g_pHyprRenderer->draw(CTexPassElement::SRenderData{.tex = texture, .box = box}, m_damage); offsets[surface.get()] += texture->m_size.y; }; @@ -281,10 +277,7 @@ void CRenderPass::renderDebugData() { if (hlSurface) { auto BOX = hlSurface->getSurfaceBoxGlobal(); if (BOX) { - CRectPassElement::SRectData data; - data.box = box; - data.color = CHyprColor{0.8F, 0.8F, 0.2F, 0.4F}; - g_pHyprRenderer->draw(makeUnique<CRectPassElement>(data), m_damage); + g_pHyprRenderer->draw(CRectPassElement::SRectData{.box = box, .color = CHyprColor{0.8F, 0.8F, 0.2F, 0.4F}}, m_damage); } } } @@ -295,13 +288,13 @@ void CRenderPass::renderDebugData() { DISCARDED_ELEMENTS, pMonitor->m_pixelSize), Colors::WHITE, 12); - if (tex) { - box = CBox{{0.F, pMonitor->m_size.y - tex->m_size.y}, tex->m_size}.scale(pMonitor->m_scale); - CTexPassElement::SRenderData texData; - texData.tex = tex; - texData.box = box; - g_pHyprRenderer->draw(makeUnique<CTexPassElement>(texData), m_damage); - } + if (tex) + g_pHyprRenderer->draw( + CTexPassElement::SRenderData{ + .tex = tex, + .box = CBox{{0.F, pMonitor->m_size.y - tex->m_size.y}, tex->m_size}.scale(pMonitor->m_scale), + }, + m_damage); std::string passStructure; auto yn = [](const bool val) -> const char* { return val ? "yes" : "no"; }; @@ -315,13 +308,13 @@ void CRenderPass::renderDebugData() { passStructure.pop_back(); 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); - CTexPassElement::SRenderData texData; - texData.tex = tex; - texData.box = box; - g_pHyprRenderer->draw(makeUnique<CTexPassElement>(texData), m_damage); - } + if (tex) + g_pHyprRenderer->draw( + CTexPassElement::SRenderData{ + .tex = 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), + }, + m_damage); } float CRenderPass::oneBlurRadius() { diff --git a/src/render/pass/Pass.hpp b/src/render/pass/Pass.hpp index d407bf3c..5a85d067 100644 --- a/src/render/pass/Pass.hpp +++ b/src/render/pass/Pass.hpp @@ -3,40 +3,44 @@ #include "../../defines.hpp" #include "PassElement.hpp" -class ITexture; +class CGradientValueData; -class CRenderPass { - public: - bool empty() const; - bool single() const; +namespace Render { + class ITexture; - void add(UP<IPassElement>&& elem); - void clear(); - void removeAllOfType(const std::string& type); + class CRenderPass { + public: + bool empty() const; + bool single() const; - CRegion render(const CRegion& damage_); + void add(UP<IPassElement>&& elem); + void clear(); + void removeAllOfType(const std::string& type); - private: - CRegion m_damage; - std::vector<CRegion> m_occludedRegions; - CRegion m_totalLiveBlurRegion; + CRegion render(const CRegion& damage_); - struct SPassElementData { - CRegion elementDamage; - UP<IPassElement> element; - bool discard = false; - }; + private: + CRegion m_damage; + std::vector<CRegion> m_occludedRegions; + CRegion m_totalLiveBlurRegion; + + struct SPassElementData { + CRegion elementDamage; + UP<IPassElement> element; + bool discard = false; + }; - std::vector<UP<SPassElementData>> m_passElements; + std::vector<UP<SPassElementData>> m_passElements; - void simplify(); - float oneBlurRadius(); - void renderDebugData(); + void simplify(); + float oneBlurRadius(); + void renderDebugData(); - struct { - bool present = false; - SP<ITexture> keyboardFocusText, pointerFocusText, lastWindowText; - } m_debugData; + struct { + bool present = false; + SP<ITexture> keyboardFocusText, pointerFocusText, lastWindowText; + } m_debugData; - friend class CHyprOpenGLImpl; -}; + friend class CHyprOpenGLImpl; + }; +} diff --git a/src/render/pass/PassElement.cpp b/src/render/pass/PassElement.cpp index 3ae52ef5..ae24680a 100644 --- a/src/render/pass/PassElement.cpp +++ b/src/render/pass/PassElement.cpp @@ -19,3 +19,7 @@ void IPassElement::discard() { bool IPassElement::undiscardable() { return false; } + +std::vector<UP<IPassElement>> IPassElement::draw() { + return {}; +} diff --git a/src/render/pass/PassElement.hpp b/src/render/pass/PassElement.hpp index c262b36c..9b939f11 100644 --- a/src/render/pass/PassElement.hpp +++ b/src/render/pass/PassElement.hpp @@ -1,6 +1,7 @@ #pragma once #include "../../defines.hpp" +#include <vector> enum ePassElementType : uint8_t { EK_UNKNOWN = 0, @@ -14,13 +15,16 @@ enum ePassElementType : uint8_t { EK_SURFACE, EK_TEXTURE, EK_TEXTURE_MATTE, - EK_INNER_GLOW + EK_INNER_GLOW, + EK_CUSTOM, }; class IPassElement { public: virtual ~IPassElement() = default; + virtual std::vector<UP<IPassElement>> draw(); + // virtual bool needsLiveBlur() = 0; virtual bool needsPrecomputeBlur() = 0; virtual const char* passName() = 0; diff --git a/src/render/pass/RendererHintsPassElement.hpp b/src/render/pass/RendererHintsPassElement.hpp index a5a429b5..0a24ff6d 100644 --- a/src/render/pass/RendererHintsPassElement.hpp +++ b/src/render/pass/RendererHintsPassElement.hpp @@ -1,12 +1,11 @@ #pragma once #include "PassElement.hpp" -#include <optional> -#include "../OpenGL.hpp" +#include "../types.hpp" class CRendererHintsPassElement : public IPassElement { public: struct SData { - std::optional<SRenderModifData> renderModif; + std::optional<Render::SRenderModifData> renderModif; }; CRendererHintsPassElement(const SData& data); diff --git a/src/render/pass/SurfacePassElement.hpp b/src/render/pass/SurfacePassElement.hpp index c76b475d..ebf5561c 100644 --- a/src/render/pass/SurfacePassElement.hpp +++ b/src/render/pass/SurfacePassElement.hpp @@ -5,7 +5,9 @@ #include "../../helpers/time/Time.hpp" class CWLSurfaceResource; -class ITexture; +namespace Render { + class ITexture; +} class CSyncTimeline; class CSurfacePassElement : public IPassElement { @@ -17,7 +19,7 @@ class CSurfacePassElement : public IPassElement { void* data = nullptr; SP<CWLSurfaceResource> surface = nullptr; - SP<ITexture> texture = nullptr; + SP<Render::ITexture> texture = nullptr; bool mainSurface = true; double w = 0, h = 0; int rounding = 0; diff --git a/src/render/pass/TexPassElement.hpp b/src/render/pass/TexPassElement.hpp index 5b8c9757..6c0cfab1 100644 --- a/src/render/pass/TexPassElement.hpp +++ b/src/render/pass/TexPassElement.hpp @@ -3,7 +3,9 @@ #include <optional> class CWLSurfaceResource; -class ITexture; +namespace Render { + class ITexture; +} class CSyncTimeline; enum eDiscardMode : uint8_t { @@ -14,7 +16,7 @@ enum eDiscardMode : uint8_t { class CTexPassElement : public IPassElement { public: struct SRenderData { - SP<ITexture> tex; + SP<Render::ITexture> tex; CBox box; float a = 1.F; float blurA = 1.F; @@ -41,7 +43,7 @@ class CTexPassElement : public IPassElement { CRegion clipRegion; PHLLSREF currentLS; - SP<ITexture> blurredBG; + SP<Render::ITexture> blurredBG; }; CTexPassElement(const SRenderData& data); diff --git a/src/render/pass/TextureMatteElement.hpp b/src/render/pass/TextureMatteElement.hpp index bf3bb4bf..03526a57 100644 --- a/src/render/pass/TextureMatteElement.hpp +++ b/src/render/pass/TextureMatteElement.hpp @@ -2,15 +2,17 @@ #include "PassElement.hpp" #include "../Framebuffer.hpp" -class ITexture; +namespace Render { + class ITexture; +} class CTextureMatteElement : public IPassElement { public: struct STextureMatteData { - CBox box; - SP<ITexture> tex; - SP<IFramebuffer> fb; - bool disableTransformAndModify = false; + CBox box; + SP<Render::ITexture> tex; + SP<Render::IFramebuffer> fb; + bool disableTransformAndModify = false; }; CTextureMatteElement(const STextureMatteData& data_); diff --git a/src/render/shaders/glsl/blurFinish.glsl b/src/render/shaders/glsl/blurFinish.glsl index f3d225c3..e7e1f1c6 100644 --- a/src/render/shaders/glsl/blurFinish.glsl +++ b/src/render/shaders/glsl/blurFinish.glsl @@ -1,10 +1,26 @@ +#ifndef ALLOW_INCLUDES +#define ALLOW_INCLUDES +#extension GL_ARB_shading_language_include : enable +#endif + +#include "defines.h" + +#if USE_CM +#include "cm_helpers.glsl" +#endif + float hash(vec2 p) { vec3 p3 = fract(vec3(p.xyx) * 1689.1984); p3 += dot(p3, p3.yzx + 33.33); return fract((p3.x + p3.y) * p3.z); } -vec4 blurFinish(vec4 pixColor, vec2 v_texcoord, float noise, float brightness) { +vec4 blurFinish(vec4 pixColor, vec2 v_texcoord, float noise, float brightness +#if USE_CM + , + int sourceTF, int targetTF, mat3 convertMatrix, vec2 srcTFRange, vec2 dstTFRange +#endif +) { // noise float noiseHash = hash(v_texcoord); float noiseAmount = noiseHash - 0.5; @@ -13,5 +29,9 @@ vec4 blurFinish(vec4 pixColor, vec2 v_texcoord, float noise, float brightness) { // brightness pixColor.rgb *= min(1.0, brightness); +#if USE_CM + pixColor = doColorManagement(pixColor, sourceTF, targetTF, convertMatrix, srcTFRange, dstTFRange); +#endif + return pixColor; } diff --git a/src/render/shaders/glsl/blurfinish.frag b/src/render/shaders/glsl/blurfinish.frag index 0342646b..51eb7684 100644 --- a/src/render/shaders/glsl/blurfinish.frag +++ b/src/render/shaders/glsl/blurfinish.frag @@ -9,11 +9,23 @@ uniform sampler2D tex; uniform float noise; uniform float brightness; +#include "defines.h" +#if USE_CM +uniform int sourceTF; // eTransferFunction +uniform int targetTF; // eTransferFunction +#include "CM.glsl" +#endif + #include "blurFinish.glsl" layout(location = 0) out vec4 fragColor; void main() { vec4 pixColor = texture(tex, v_texcoord); - fragColor = blurFinish(pixColor, v_texcoord, noise, brightness); + fragColor = blurFinish(pixColor, v_texcoord, noise, brightness +#if USE_CM + , + sourceTF, targetTF, convertMatrix, srcTFRange, dstTFRange +#endif + ); } diff --git a/src/render/shaders/glsl/border.frag b/src/render/shaders/glsl/border.frag index 151593c1..9f97348b 100644 --- a/src/render/shaders/glsl/border.frag +++ b/src/render/shaders/glsl/border.frag @@ -32,29 +32,41 @@ uniform vec2 fullSize; #include "border.glsl" layout(location = 0) out vec4 fragColor; +#if USE_MIRROR +layout(location = 1) out vec4 mirrorColor; +#endif void main() { - fragColor = getBorder(v_texcoord, alpha, fullSizeUntransformed, radiusOuter, thick, radius, roundingPower, topLeft, fullSize, gradientLength, gradient, angle, gradient2Length, - gradient2, angle2, gradientLerp +#if USE_MIRROR + vec4[2] pixColors = +#else + fragColor = +#endif + getBorder(v_texcoord, alpha, fullSizeUntransformed, radiusOuter, thick, radius, roundingPower, topLeft, fullSize, gradientLength, gradient, angle, gradient2Length, + gradient2, angle2, gradientLerp #if USE_CM - , - sourceTF, targetTF, convertMatrix, srcTFRange, dstTFRange + , + sourceTF, targetTF, convertMatrix, srcTFRange, dstTFRange #if USE_ICC - , - iccLut3D, iccLutSize + , + iccLut3D, iccLutSize #else #if USE_TONEMAP || USE_SDR_MOD - , - targetPrimariesXYZ + , + targetPrimariesXYZ #endif #if USE_TONEMAP - , - maxLuminance, dstMaxLuminance, dstRefLuminance, srcRefLuminance + , + maxLuminance, dstMaxLuminance, dstRefLuminance, srcRefLuminance #endif #if USE_SDR_MOD - , - sdrSaturation, sdrBrightnessMultiplier + , + sdrSaturation, sdrBrightnessMultiplier +#endif #endif #endif + ); +#if USE_MIRROR + fragColor = pixColors[0]; + mirrorColor = pixColors[1]; #endif - ); } diff --git a/src/render/shaders/glsl/border.glsl b/src/render/shaders/glsl/border.glsl index fa2a6980..9028ad0d 100644 --- a/src/render/shaders/glsl/border.glsl +++ b/src/render/shaders/glsl/border.glsl @@ -87,30 +87,35 @@ vec4 getColorForCoord(vec2 normalizedCoord, int gradientLength, vec4 gradient[10 return okLabAToSrgb(mix(result1, result2, gradientLerp)); } -vec4 getBorder(vec2 v_texcoord, float alpha, vec2 fullSizeUntransformed, float radiusOuter, float thick, float radius, float roundingPower, vec2 topLeft, vec2 fullSize, - int gradientLength, vec4 gradient[10], float angle, int gradient2Length, vec4 gradient2[10], float angle2, float gradientLerp +#if USE_MIRROR +vec4[2] +#else +vec4 +#endif + getBorder(vec2 v_texcoord, float alpha, vec2 fullSizeUntransformed, float radiusOuter, float thick, float radius, float roundingPower, vec2 topLeft, vec2 fullSize, + int gradientLength, vec4 gradient[10], float angle, int gradient2Length, vec4 gradient2[10], float angle2, float gradientLerp #if USE_CM - , - int sourceTF, int targetTF, mat3 convertMatrix, vec2 srcTFRange, vec2 dstTFRange + , + int sourceTF, int targetTF, mat3 convertMatrix, vec2 srcTFRange, vec2 dstTFRange #if USE_ICC - , - highp sampler3D iccLut3D, float iccLutSize + , + highp sampler3D iccLut3D, float iccLutSize #else #if USE_TONEMAP || USE_SDR_MOD - , - mat3 targetPrimariesXYZ + , + mat3 targetPrimariesXYZ #endif #if USE_TONEMAP - , - float maxLuminance, float dstMaxLuminance, float dstRefLuminance, float srcRefLuminance + , + float maxLuminance, float dstMaxLuminance, float dstRefLuminance, float srcRefLuminance #endif #if USE_SDR_MOD - , - float sdrSaturation, float sdrBrightnessMultiplier + , + float sdrSaturation, float sdrBrightnessMultiplier #endif #endif #endif -) { + ) { vec2 pixCoord = vec2(gl_FragCoord); vec2 pixCoordOuter = pixCoord; vec2 originalPixCoord = v_texcoord; @@ -176,28 +181,38 @@ vec4 getBorder(vec2 v_texcoord, float alpha, vec2 fullSizeUntransformed, float r pixColor.rgb *= pixColor[3]; #if USE_CM - pixColor = doColorManagement(pixColor, sourceTF, targetTF, convertMatrix, srcTFRange, dstTFRange +#if USE_MIRROR + vec4[2] pixColors = +#else + pixColor = +#endif + doColorManagement(pixColor, sourceTF, targetTF, convertMatrix, srcTFRange, dstTFRange #if USE_ICC - , - iccLut3D, iccLutSize + , + iccLut3D, iccLutSize #else #if USE_TONEMAP || USE_SDR_MOD - , - targetPrimariesXYZ + , + targetPrimariesXYZ #endif #if USE_TONEMAP - , - maxLuminance, dstMaxLuminance, dstRefLuminance, srcRefLuminance + , + maxLuminance, dstMaxLuminance, dstRefLuminance, srcRefLuminance #endif #if USE_SDR_MOD - , - sdrSaturation, sdrBrightnessMultiplier + , + sdrSaturation, sdrBrightnessMultiplier #endif #endif - ); + ); #endif +#if USE_MIRROR + pixColors[0] *= alpha * additionalAlpha; + pixColors[1] *= alpha * additionalAlpha; + return pixColors; +#else pixColor *= alpha * additionalAlpha; - return pixColor; +#endif } diff --git a/src/render/shaders/glsl/cm_helpers.glsl b/src/render/shaders/glsl/cm_helpers.glsl index 5e0d14f6..472c6ea5 100644 --- a/src/render/shaders/glsl/cm_helpers.glsl +++ b/src/render/shaders/glsl/cm_helpers.glsl @@ -204,10 +204,15 @@ vec4 fromLinearNit(vec4 color, int tf, vec2 range) { #include "tonemap.glsl" #endif -vec4 doColorManagement(vec4 pixColor, int srcTF, int dstTF, mat3 convertMatrix, vec2 srcTFRange, vec2 dstTFRange +#if USE_MIRROR +vec4[2] +#else +vec4 +#endif + doColorManagement(vec4 pixColor, int srcTF, int dstTF, mat3 convertMatrix, vec2 srcTFRange, vec2 dstTFRange #if USE_ICC - , - highp sampler3D iccLut3D, float iccLutSize + , + highp sampler3D iccLut3D, float iccLutSize #else #if USE_TONEMAP || USE_SDR_MOD , @@ -222,7 +227,7 @@ vec4 doColorManagement(vec4 pixColor, int srcTF, int dstTF, mat3 convertMatrix, float sdrSaturation, float sdrBrightnessMultiplier #endif #endif -) { + ) { pixColor.rgb /= max(pixColor.a, 0.001); pixColor.rgb = toLinearRGB(pixColor.rgb, srcTF); #if USE_ICC @@ -235,6 +240,11 @@ vec4 doColorManagement(vec4 pixColor, int srcTF, int dstTF, mat3 convertMatrix, #if USE_TONEMAP pixColor = tonemap(pixColor, dstxyz, maxLuminance, dstMaxLuminance, dstRefLuminance, srcRefLuminance); #endif +#if USE_MIRROR + // TODO HDR -> SDR tonemap + vec4 mirrorColor = fromLinearNit(pixColor, CM_TRANSFER_FUNCTION_GAMMA22, + srcTF == CM_TRANSFER_FUNCTION_GAMMA22 || srcTF == CM_TRANSFER_FUNCTION_SRGB ? srcTFRange : vec2(SDR_MIN_LUMINANCE, SDR_MAX_LUMINANCE)); +#endif pixColor = fromLinearNit(pixColor, dstTF, dstTFRange); #if USE_SDR_MOD pixColor = saturate(pixColor, dstxyz, sdrSaturation); @@ -242,7 +252,14 @@ vec4 doColorManagement(vec4 pixColor, int srcTF, int dstTF, mat3 convertMatrix, #endif #endif +#if USE_MIRROR + vec4[2] pixColors; + pixColors[0] = pixColor; + pixColors[1] = mirrorColor; + return pixColors; +#else return pixColor; +#endif } #endif
\ No newline at end of file diff --git a/src/render/shaders/glsl/defines.h b/src/render/shaders/glsl/defines.h index 31b120a4..a04e0250 100644 --- a/src/render/shaders/glsl/defines.h +++ b/src/render/shaders/glsl/defines.h @@ -1,4 +1,7 @@ // DO NOT EDIT. Will be overwritten in runtime +// Values here are only for highlighting and static checking +// 1 assumes that a shader either supports this feature or doesn't use any code supporting it +// 0 assumes that shader uses some code supporting the feature but will never have this feature enabled #define USE_RGBA 1 #define USE_DISCARD 1 #define USE_TINT 1 @@ -7,4 +10,5 @@ #define USE_TONEMAP 1 #define USE_SDR_MOD 1 #define USE_BLUR 1 -#define USE_ICC 1 +#define USE_ICC 0 +#define USE_MIRROR 0 diff --git a/src/render/shaders/glsl/quad.frag b/src/render/shaders/glsl/quad.frag index 61895a60..dc1945af 100644 --- a/src/render/shaders/glsl/quad.frag +++ b/src/render/shaders/glsl/quad.frag @@ -16,6 +16,9 @@ uniform vec2 fullSize; #endif layout(location = 0) out vec4 fragColor; +#if USE_MIRROR +layout(location = 1) out vec4 mirrorColor; +#endif void main() { vec4 pixColor = v_color; @@ -24,4 +27,7 @@ void main() { #endif fragColor = pixColor; +#if USE_MIRROR + mirrorColor = fragColor; +#endif } diff --git a/src/render/shaders/glsl/rgbamatte.frag b/src/render/shaders/glsl/rgbamatte.frag index a7213cfe..72fdc1d6 100644 --- a/src/render/shaders/glsl/rgbamatte.frag +++ b/src/render/shaders/glsl/rgbamatte.frag @@ -1,11 +1,21 @@ #version 300 es +#define ALLOW_INCLUDES +#extension GL_ARB_shading_language_include : enable -precision highp float; -in vec2 v_texcoord; // is in 0-1 +#include "defines.h" + +precision highp float; +in vec2 v_texcoord; // is in 0-1 uniform sampler2D tex; uniform sampler2D texMatte; layout(location = 0) out vec4 fragColor; +#if USE_MIRROR +layout(location = 1) out vec4 mirrorColor; +#endif void main() { fragColor = texture(tex, v_texcoord) * texture(texMatte, v_texcoord)[0]; // I know it only uses R, but matte should be black/white anyways. +#if USE_MIRROR + mirrorColor = fragColor; +#endif } diff --git a/src/render/shaders/glsl/shadow.frag b/src/render/shaders/glsl/shadow.frag index c23ebd5d..5ba3d879 100644 --- a/src/render/shaders/glsl/shadow.frag +++ b/src/render/shaders/glsl/shadow.frag @@ -28,30 +28,41 @@ uniform float shadowPower; #include "shadow.glsl" layout(location = 0) out vec4 fragColor; +#if USE_MIRROR +layout(location = 1) out vec4 mirrorColor; +#endif void main() { vec4 pixColor = v_color; - - fragColor = getShadow(pixColor, v_texcoord, radius, roundingPower, topLeft, fullSize, range, shadowPower, bottomRight +#if USE_MIRROR + vec4[2] pixColors = +#else + fragColor = +#endif + getShadow(pixColor, v_texcoord, radius, roundingPower, topLeft, fullSize, range, shadowPower, bottomRight #if USE_CM - , - sourceTF, targetTF, convertMatrix, srcTFRange, dstTFRange + , + sourceTF, targetTF, convertMatrix, srcTFRange, dstTFRange #if USE_ICC - , - iccLut3D, iccLutSize + , + iccLut3D, iccLutSize #else #if USE_TONEMAP || USE_SDR_MOD - , - targetPrimariesXYZ + , + targetPrimariesXYZ #endif #if USE_TONEMAP - , - maxLuminance, dstMaxLuminance, dstRefLuminance, srcRefLuminance + , + maxLuminance, dstMaxLuminance, dstRefLuminance, srcRefLuminance #endif #if USE_SDR_MOD - , - sdrSaturation, sdrBrightnessMultiplier + , + sdrSaturation, sdrBrightnessMultiplier +#endif #endif #endif + ); +#if USE_MIRROR + fragColor = pixColors[0]; + mirrorColor = pixColors[1]; #endif - ); }
\ No newline at end of file diff --git a/src/render/shaders/glsl/shadow.glsl b/src/render/shaders/glsl/shadow.glsl index 48cde562..265dba00 100644 --- a/src/render/shaders/glsl/shadow.glsl +++ b/src/render/shaders/glsl/shadow.glsl @@ -23,29 +23,34 @@ float modifiedLength(vec2 a, float roundingPower) { return pow(pow(abs(a.x), roundingPower) + pow(abs(a.y), roundingPower), 1.0 / roundingPower); } -vec4 getShadow(vec4 pixColor, vec2 v_texcoord, float radius, float roundingPower, vec2 topLeft, vec2 fullSize, float range, float shadowPower, vec2 bottomRight +#if USE_MIRROR +vec4[2] +#else +vec4 +#endif + getShadow(vec4 pixColor, vec2 v_texcoord, float radius, float roundingPower, vec2 topLeft, vec2 fullSize, float range, float shadowPower, vec2 bottomRight #if USE_CM - , - int sourceTF, int targetTF, mat3 convertMatrix, vec2 srcTFRange, vec2 dstTFRange + , + int sourceTF, int targetTF, mat3 convertMatrix, vec2 srcTFRange, vec2 dstTFRange #if USE_ICC - , - highp sampler3D iccLut3D, float iccLutSize + , + highp sampler3D iccLut3D, float iccLutSize #else #if USE_TONEMAP || USE_SDR_MOD - , - mat3 targetPrimariesXYZ + , + mat3 targetPrimariesXYZ #endif #if USE_TONEMAP - , - float maxLuminance, float dstMaxLuminance, float dstRefLuminance, float srcRefLuminance + , + float maxLuminance, float dstMaxLuminance, float dstRefLuminance, float srcRefLuminance #endif #if USE_SDR_MOD - , - float sdrSaturation, float sdrBrightnessMultiplier + , + float sdrSaturation, float sdrBrightnessMultiplier #endif #endif #endif -) { + ) { float originalAlpha = pixColor[3]; bool done = false; @@ -100,27 +105,35 @@ vec4 getShadow(vec4 pixColor, vec2 v_texcoord, float radius, float roundingPower pixColor.rgb *= pixColor[3]; #if USE_CM - pixColor = doColorManagement(pixColor, sourceTF, targetTF, convertMatrix, srcTFRange, dstTFRange +#if USE_MIRROR + vec4[2] pixColors = +#else + pixColor = +#endif + doColorManagement(pixColor, sourceTF, targetTF, convertMatrix, srcTFRange, dstTFRange #if USE_ICC - , - iccLut3D, iccLutSize + , + iccLut3D, iccLutSize #else #if USE_TONEMAP || USE_SDR_MOD - , - targetPrimariesXYZ + , + targetPrimariesXYZ #endif #if USE_TONEMAP - , - maxLuminance, dstMaxLuminance, dstRefLuminance, srcRefLuminance + , + maxLuminance, dstMaxLuminance, dstRefLuminance, srcRefLuminance #endif #if USE_SDR_MOD - , - sdrSaturation, sdrBrightnessMultiplier + , + sdrSaturation, sdrBrightnessMultiplier #endif #endif - ); + ); #endif - +#if USE_MIRROR + return pixColors; +#else return pixColor; +#endif } #endif
\ No newline at end of file diff --git a/src/render/shaders/glsl/surface.frag b/src/render/shaders/glsl/surface.frag index 30023bc8..076ca62c 100644 --- a/src/render/shaders/glsl/surface.frag +++ b/src/render/shaders/glsl/surface.frag @@ -47,6 +47,9 @@ const mat3 targetPrimariesXYZ = mat3(0.0); #endif layout(location = 0) out vec4 fragColor; +#if USE_MIRROR +layout(location = 1) out vec4 mirrorColor; +#endif void main() { #if USE_RGBA vec4 pixColor = texture(tex, v_texcoord); @@ -63,25 +66,34 @@ void main() { #endif #if USE_CM - pixColor = doColorManagement(pixColor, sourceTF, targetTF, convertMatrix, srcTFRange, dstTFRange +#if USE_MIRROR + vec4[2] pixColors = +#else + pixColor = +#endif + doColorManagement(pixColor, sourceTF, targetTF, convertMatrix, srcTFRange, dstTFRange #if USE_ICC - , - iccLut3D, iccLutSize + , + iccLut3D, iccLutSize #else #if USE_TONEMAP || USE_SDR_MOD - , - targetPrimariesXYZ + , + targetPrimariesXYZ #endif #if USE_TONEMAP - , - maxLuminance, dstMaxLuminance, dstRefLuminance, srcRefLuminance + , + maxLuminance, dstMaxLuminance, dstRefLuminance, srcRefLuminance #endif #if USE_SDR_MOD - , - sdrSaturation, sdrBrightnessMultiplier + , + sdrSaturation, sdrBrightnessMultiplier +#endif #endif + ); #endif - ); +#if USE_MIRROR + pixColor = pixColors[0]; + mirrorColor = pixColors[1]; #endif #if USE_TINT @@ -101,4 +113,23 @@ void main() { #endif fragColor = pixColor * alpha; +#if USE_MIRROR +#if USE_TINT + mirrorColor.rgb = mirrorColor.rgb * tint; +#endif + +#if USE_ROUNDING + mirrorColor = rounding(mirrorColor, radius, roundingPower, topLeft, fullSize); +#endif +#if USE_BLUR +#if USE_DISCARD + mirrorColor = mix(mirrorColor, vec4(mix(texture(blurredBG, v_texcoord * uvSize + uvOffset).rgb, mirrorColor.rgb, mirrorColor.a), 1.0), + discardAlpha && (mirrorColor.a <= discardAlphaValue) ? 0.0 : 1.0); +#else + mirrorColor = vec4(mix(texture(blurredBG, v_texcoord * uvSize + uvOffset).rgb, mirrorColor.rgb, mirrorColor.a), 1.0); +#endif +#endif + + mirrorColor = mirrorColor * alpha; +#endif } diff --git a/src/render/types.hpp b/src/render/types.hpp new file mode 100644 index 00000000..a33c7b6d --- /dev/null +++ b/src/render/types.hpp @@ -0,0 +1,131 @@ +#pragma once + +#include "Framebuffer.hpp" +#include "../desktop/DesktopTypes.hpp" +#include "../helpers/cm/ColorManagement.hpp" +#include "../protocols/core/Compositor.hpp" +#include <hyprgraphics/color/Color.hpp> +#include <hyprutils/math/Box.hpp> +#include <hyprutils/math/Mat3x3.hpp> +#include <hyprutils/math/Region.hpp> +#include <hyprutils/math/Vector2D.hpp> + +namespace Render { + const std::vector<const char*> ASSET_PATHS = { +#ifdef DATAROOTDIR + DATAROOTDIR, +#endif + "/usr/share", + "/usr/local/share", + }; + + enum eDamageTrackingModes : int8_t { + DAMAGE_TRACKING_INVALID = -1, + DAMAGE_TRACKING_NONE = 0, + DAMAGE_TRACKING_MONITOR, + DAMAGE_TRACKING_FULL, + }; + + enum eRenderPassMode : uint8_t { + RENDER_PASS_ALL = 0, + RENDER_PASS_MAIN, + RENDER_PASS_POPUP + }; + + enum eRenderMode : uint8_t { + RENDER_MODE_NORMAL = 0, + RENDER_MODE_FULL_FAKE = 1, + RENDER_MODE_TO_BUFFER = 2, + RENDER_MODE_TO_BUFFER_READ_ONLY = 3, + }; + + struct SRenderWorkspaceUntilData { + PHLLS ls; + PHLWINDOW w; + }; + + enum eRenderProjectionType : uint8_t { + RPT_MONITOR, + RPT_MIRROR, + RPT_FB, + RPT_EXPORT, + }; + + struct SRenderModifData { + enum eRenderModifType : uint8_t { + RMOD_TYPE_SCALE, /* scale by a float */ + RMOD_TYPE_SCALECENTER, /* scale by a float from the center */ + RMOD_TYPE_TRANSLATE, /* translate by a Vector2D */ + RMOD_TYPE_ROTATE, /* rotate by a float in rad from top left */ + RMOD_TYPE_ROTATECENTER, /* rotate by a float in rad from center */ + }; + + std::vector<std::pair<eRenderModifType, std::any>> modifs; + + void applyToBox(Hyprutils::Math::CBox& box); + void applyToRegion(Hyprutils::Math::CRegion& rg); + float combinedScale(); + + bool enabled = true; + }; + + struct SRenderData { + // can be private + Hyprutils::Math::Mat3x3 targetProjection; + + // ---------------------- + + // used by public + Hyprutils::Math::Vector2D fbSize = {-1, -1}; + PHLMONITORREF pMonitor; + + eRenderProjectionType projectionType = RPT_MONITOR; + + SP<IFramebuffer> currentFB = nullptr; // current rendering to + SP<IFramebuffer> mainFB = nullptr; // main to render to + SP<IFramebuffer> outFB = nullptr; // out to render to (if offloaded, etc) + + CRegion damage; + CRegion finalDamage; // damage used for funal off -> main + + SRenderModifData renderModif; + float mouseZoomFactor = 1.f; + bool mouseZoomUseMouse = true; // true by default + bool useNearestNeighbor = false; + bool blockScreenShader = false; + + Vector2D primarySurfaceUVTopLeft = Vector2D(-1, -1); + Vector2D primarySurfaceUVBottomRight = Vector2D(-1, -1); + + // TODO remove and pass directly + CBox clipBox = {}; // scaled coordinates + PHLWINDOWREF currentWindow; + WP<CWLSurfaceResource> surface; + + bool transformDamage = true; + bool noSimplify = false; + }; + + struct STFRange { + float min = 0; + float max = 80; + }; + + struct SCMSettings { + NColorManagement::eTransferFunction sourceTF = NColorManagement::CM_TRANSFER_FUNCTION_GAMMA22; + NColorManagement::eTransferFunction targetTF = NColorManagement::CM_TRANSFER_FUNCTION_GAMMA22; + STFRange srcTFRange; + STFRange dstTFRange; + float srcRefLuminance = 80; + float dstRefLuminance = 80; + std::array<std::array<double, 3>, 3> convertMatrix; + + bool needsTonemap = false; + float maxLuminance = 80; + float dstMaxLuminance = 80; + std::array<std::array<double, 3>, 3> dstPrimaries2XYZ; + bool needsSDRmod = false; + float sdrSaturation = 1.0; + float sdrBrightnessMultiplier = 1.0; + }; +}
\ No newline at end of file |
