Renderer: rewrite render scheduling (#8683)

This rewrites renderer scheduling. Occlusion is now unified in a new Pass type.
This commit is contained in:
Vaxry 2024-12-22 17:12:09 +01:00 committed by GitHub
parent 1cc1a46c2e
commit e536b02248
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
42 changed files with 1576 additions and 775 deletions

View file

@ -0,0 +1,21 @@
#include "BorderPassElement.hpp"
#include "../OpenGL.hpp"
CBorderPassElement::CBorderPassElement(const CBorderPassElement::SBorderData& data_) : data(data_) {
;
}
void CBorderPassElement::draw(const CRegion& damage) {
if (data.hasGrad2)
g_pHyprOpenGL->renderBorder(&data.box, data.grad1, data.grad2, data.lerp, data.round, data.borderSize, data.a, data.outerRound);
else
g_pHyprOpenGL->renderBorder(&data.box, data.grad1, data.round, data.borderSize, data.a, data.outerRound);
}
bool CBorderPassElement::needsLiveBlur() {
return false;
}
bool CBorderPassElement::needsPrecomputeBlur() {
return false;
}

View file

@ -0,0 +1,30 @@
#pragma once
#include "PassElement.hpp"
#include "../../config/ConfigDataValues.hpp"
class CGradientValueData;
class CBorderPassElement : public IPassElement {
public:
struct SBorderData {
CBox box;
CGradientValueData grad1, grad2;
bool hasGrad2 = false;
float lerp = 0.F, a = 1.F;
int round = 0, borderSize = 1, outerRound = -1;
};
CBorderPassElement(const SBorderData& data_);
virtual ~CBorderPassElement() = default;
virtual void draw(const CRegion& damage);
virtual bool needsLiveBlur();
virtual bool needsPrecomputeBlur();
virtual const char* passName() {
return "CBorderPassElement";
}
private:
SBorderData data;
};

View file

@ -0,0 +1,26 @@
#include "ClearPassElement.hpp"
#include "../OpenGL.hpp"
CClearPassElement::CClearPassElement(const CClearPassElement::SClearData& data_) : data(data_) {
;
}
void CClearPassElement::draw(const CRegion& damage) {
g_pHyprOpenGL->clear(data.color);
}
bool CClearPassElement::needsLiveBlur() {
return false;
}
bool CClearPassElement::needsPrecomputeBlur() {
return false;
}
std::optional<CBox> CClearPassElement::boundingBox() {
return CBox{{}, {INT16_MAX, INT16_MAX}};
}
CRegion CClearPassElement::opaqueRegion() {
return *boundingBox();
}

View file

@ -0,0 +1,25 @@
#pragma once
#include "PassElement.hpp"
class CClearPassElement : public IPassElement {
public:
struct SClearData {
CHyprColor color;
};
CClearPassElement(const SClearData& data);
virtual ~CClearPassElement() = default;
virtual void draw(const CRegion& damage);
virtual bool needsLiveBlur();
virtual bool needsPrecomputeBlur();
virtual std::optional<CBox> boundingBox();
virtual CRegion opaqueRegion();
virtual const char* passName() {
return "CClearPassElement";
}
private:
SClearData data;
};

View file

@ -0,0 +1,48 @@
#include "FramebufferElement.hpp"
#include "../OpenGL.hpp"
CFramebufferElement::CFramebufferElement(const CFramebufferElement::SFramebufferElementData& data_) : data(data_) {
;
}
void CFramebufferElement::draw(const CRegion& damage) {
CFramebuffer* fb = nullptr;
if (data.main) {
switch (data.framebufferID) {
case FB_MONITOR_RENDER_MAIN: fb = g_pHyprOpenGL->m_RenderData.mainFB; break;
case FB_MONITOR_RENDER_CURRENT: fb = g_pHyprOpenGL->m_RenderData.currentFB; break;
case FB_MONITOR_RENDER_OUT: fb = g_pHyprOpenGL->m_RenderData.outFB; break;
}
if (!fb) {
Debug::log(ERR, "BUG THIS: CFramebufferElement::draw: main but null");
return;
}
} else {
switch (data.framebufferID) {
case FB_MONITOR_RENDER_EXTRA_OFFLOAD: fb = &g_pHyprOpenGL->m_RenderData.pCurrentMonData->offloadFB; break;
case FB_MONITOR_RENDER_EXTRA_MIRROR: fb = &g_pHyprOpenGL->m_RenderData.pCurrentMonData->mirrorFB; break;
case FB_MONITOR_RENDER_EXTRA_MIRROR_SWAP: fb = &g_pHyprOpenGL->m_RenderData.pCurrentMonData->mirrorSwapFB; break;
case FB_MONITOR_RENDER_EXTRA_OFF_MAIN: fb = &g_pHyprOpenGL->m_RenderData.pCurrentMonData->offMainFB; break;
case FB_MONITOR_RENDER_EXTRA_MONITOR_MIRROR: fb = &g_pHyprOpenGL->m_RenderData.pCurrentMonData->monitorMirrorFB; break;
case FB_MONITOR_RENDER_EXTRA_BLUR: fb = &g_pHyprOpenGL->m_RenderData.pCurrentMonData->blurFB; break;
}
if (!fb) {
Debug::log(ERR, "BUG THIS: CFramebufferElement::draw: not main but null");
return;
}
}
fb->bind();
}
bool CFramebufferElement::needsLiveBlur() {
return false;
}
bool CFramebufferElement::needsPrecomputeBlur() {
return false;
}

View file

@ -0,0 +1,24 @@
#pragma once
#include "PassElement.hpp"
class CFramebufferElement : public IPassElement {
public:
struct SFramebufferElementData {
bool main = true;
uint8_t framebufferID = 0;
};
CFramebufferElement(const SFramebufferElementData& data_);
virtual ~CFramebufferElement() = default;
virtual void draw(const CRegion& damage);
virtual bool needsLiveBlur();
virtual bool needsPrecomputeBlur();
virtual const char* passName() {
return "CFramebufferElement";
}
private:
SFramebufferElementData data;
};

157
src/render/pass/Pass.cpp Normal file
View file

@ -0,0 +1,157 @@
#include "Pass.hpp"
#include "../OpenGL.hpp"
#include <algorithm>
#include <ranges>
#include "../../config/ConfigValue.hpp"
bool CRenderPass::empty() const {
return false;
}
bool CRenderPass::single() const {
return m_vPassElements.size() == 1;
}
bool CRenderPass::needsIntrospection() const {
return true;
}
void CRenderPass::add(SP<IPassElement> el) {
m_vPassElements.emplace_back(makeShared<SPassElementData>(CRegion{}, el));
}
void CRenderPass::simplify() {
// TODO: use precompute blur for instances where there is nothing in between
// if there is live blur, we need to NOT occlude any area where it will be influenced
const auto WILLBLUR = std::ranges::any_of(m_vPassElements, [](const auto& el) { return el->element->needsLiveBlur(); });
CRegion newDamage = damage.copy().intersect(CBox{{}, g_pHyprOpenGL->m_RenderData.pMonitor->vecTransformedSize});
for (auto& el : m_vPassElements | std::views::reverse) {
if (newDamage.empty()) {
el->discard = true;
continue;
}
el->elementDamage = newDamage;
auto bb1 = el->element->boundingBox();
if (!bb1)
continue;
auto bb = bb1->scale(g_pHyprOpenGL->m_RenderData.pMonitor->scale);
// drop if empty
if (CRegion copy = newDamage.copy(); copy.intersect(bb).empty()) {
el->discard = true;
continue;
}
auto opaque = el->element->opaqueRegion();
if (!opaque.empty()) {
opaque.scale(g_pHyprOpenGL->m_RenderData.pMonitor->scale);
// if this intersects the liveBlur region, allow live blur to operate correctly.
// do not occlude a border near it.
if (WILLBLUR) {
CRegion liveBlurRegion;
for (auto& el2 : m_vPassElements) {
// if we reach self, no problem, we can break.
// if the blur is above us, we don't care, it will work fine.
if (el2 == el)
break;
if (!el2->element->needsLiveBlur())
continue;
const auto BB = el2->element->boundingBox();
RASSERT(BB, "No bounding box for an element with live blur is illegal");
liveBlurRegion.add(*BB);
}
if (auto infringement = opaque.copy().intersect(liveBlurRegion); !infringement.empty()) {
// eh, this is not the correct solution, but it will do...
// TODO: is this *easily* fixable?
opaque.subtract(infringement.expand(oneBlurRadius()));
}
}
newDamage.subtract(opaque);
}
}
}
void CRenderPass::clear() {
m_vPassElements.clear();
}
CRegion CRenderPass::render(const CRegion& damage_) {
const auto WILLBLUR = std::ranges::any_of(m_vPassElements, [](const auto& el) { return el->element->needsLiveBlur(); });
damage = damage_.copy();
if (damage.empty()) {
g_pHyprOpenGL->m_RenderData.damage = damage;
g_pHyprOpenGL->m_RenderData.finalDamage = damage;
return damage;
}
if (WILLBLUR) {
// combine blur regions into one that will be expanded
CRegion blurRegion;
for (auto& el : m_vPassElements) {
if (!el->element->needsLiveBlur())
continue;
const auto BB = el->element->boundingBox();
RASSERT(BB, "No bounding box for an element with live blur is illegal");
blurRegion.add(*BB);
}
blurRegion.intersect(damage).expand(oneBlurRadius());
g_pHyprOpenGL->m_RenderData.finalDamage = blurRegion.copy().add(damage);
// FIXME: why does this break on * 1.F ?
// used to work when we expand all the damage... I think? Well, before pass.
// moving a window over blur shows the edges being wonk.
blurRegion.expand(oneBlurRadius() * 1.5F);
damage = blurRegion.copy().add(damage);
} else
g_pHyprOpenGL->m_RenderData.finalDamage = damage;
if (std::ranges::any_of(m_vPassElements, [](const auto& el) { return el->element->disableSimplification(); })) {
for (auto& el : m_vPassElements) {
el->elementDamage = damage;
}
} else
simplify();
g_pHyprOpenGL->m_RenderData.pCurrentMonData->blurFBShouldRender = std::ranges::any_of(m_vPassElements, [](const auto& el) { return el->element->needsPrecomputeBlur(); });
if (m_vPassElements.empty())
return {};
for (auto& el : m_vPassElements) {
if (el->discard) {
el->element->discard();
continue;
}
g_pHyprOpenGL->m_RenderData.damage = el->elementDamage;
el->element->draw(el->elementDamage);
}
g_pHyprOpenGL->m_RenderData.damage = damage;
return damage;
}
float CRenderPass::oneBlurRadius() {
// TODO: is this exact range correct?
static auto PBLURSIZE = CConfigValue<Hyprlang::INT>("decoration:blur:size");
static auto PBLURPASSES = CConfigValue<Hyprlang::INT>("decoration:blur:passes");
return *PBLURPASSES > 10 ? pow(2, 15) : std::clamp(*PBLURSIZE, (int64_t)1, (int64_t)40) * pow(2, *PBLURPASSES); // is this 2^pass? I don't know but it works... I think.
}

36
src/render/pass/Pass.hpp Normal file
View file

@ -0,0 +1,36 @@
#pragma once
#include "../../defines.hpp"
#include "PassElement.hpp"
class CGradientValueData;
class CRenderPass {
public:
bool empty() const;
bool single() const;
bool needsIntrospection() const;
void add(SP<IPassElement> elem);
void clear();
CRegion render(const CRegion& damage_);
private:
CRegion damage;
struct SPassElementData {
CRegion elementDamage;
SP<IPassElement> element;
bool discard = false;
};
std::vector<SP<SPassElementData>> m_vPassElements;
SP<IPassElement> currentPassInfo = nullptr;
void simplify();
float oneBlurRadius();
friend class CHyprOpenGLImpl;
};

View file

@ -0,0 +1,17 @@
#include "PassElement.hpp"
std::optional<CBox> IPassElement::boundingBox() {
return std::nullopt;
}
CRegion IPassElement::opaqueRegion() {
return {};
}
bool IPassElement::disableSimplification() {
return false;
}
void IPassElement::discard() {
;
}

View file

@ -0,0 +1,18 @@
#pragma once
#include "../../defines.hpp"
#include <optional>
class IPassElement {
public:
virtual ~IPassElement() = default;
virtual void draw(const CRegion& damage) = 0;
virtual bool needsLiveBlur() = 0;
virtual bool needsPrecomputeBlur() = 0;
virtual const char* passName() = 0;
virtual void discard();
virtual std::optional<CBox> boundingBox();
virtual CRegion opaqueRegion();
virtual bool disableSimplification();
};

View file

@ -0,0 +1,20 @@
#include "PreBlurElement.hpp"
#include "../OpenGL.hpp"
CPreBlurElement::CPreBlurElement() = default;
void CPreBlurElement::draw(const CRegion& damage) {
g_pHyprOpenGL->preBlurForCurrentMonitor();
}
bool CPreBlurElement::needsLiveBlur() {
return false;
}
bool CPreBlurElement::needsPrecomputeBlur() {
return false;
}
bool CPreBlurElement::disableSimplification() {
return true;
}

View file

@ -0,0 +1,17 @@
#pragma once
#include "PassElement.hpp"
class CPreBlurElement : public IPassElement {
public:
CPreBlurElement();
virtual ~CPreBlurElement() = default;
virtual void draw(const CRegion& damage);
virtual bool needsLiveBlur();
virtual bool needsPrecomputeBlur();
virtual bool disableSimplification();
virtual const char* passName() {
return "CPreBlurElement";
}
};

View file

@ -0,0 +1,29 @@
#include "RectPassElement.hpp"
#include "../OpenGL.hpp"
CRectPassElement::CRectPassElement(const CRectPassElement::SRectData& data_) : data(data_) {
;
}
void CRectPassElement::draw(const CRegion& damage) {
if (data.color.a == 1.F || !data.blur)
g_pHyprOpenGL->renderRectWithDamage(&data.box, data.color, damage, data.round);
else
g_pHyprOpenGL->renderRectWithBlur(&data.box, data.color, data.round, data.blurA, data.xray);
}
bool CRectPassElement::needsLiveBlur() {
return data.color.a < 1.F && !data.xray && data.blur;
}
bool CRectPassElement::needsPrecomputeBlur() {
return data.color.a < 1.F && data.xray && data.blur;
}
std::optional<CBox> CRectPassElement::boundingBox() {
return data.box.expand(-data.round);
}
CRegion CRectPassElement::opaqueRegion() {
return data.color.a >= 1.F ? *boundingBox() : CRegion{};
}

View file

@ -0,0 +1,29 @@
#pragma once
#include "PassElement.hpp"
class CRectPassElement : public IPassElement {
public:
struct SRectData {
CBox box;
CHyprColor color;
int round = 0;
bool blur = false, xray = false;
float blurA = 1.F;
};
CRectPassElement(const SRectData& data);
virtual ~CRectPassElement() = default;
virtual void draw(const CRegion& damage);
virtual bool needsLiveBlur();
virtual bool needsPrecomputeBlur();
virtual std::optional<CBox> boundingBox();
virtual CRegion opaqueRegion();
virtual const char* passName() {
return "CRectPassElement";
}
private:
SRectData data;
};

View file

@ -0,0 +1,19 @@
#include "ShadowPassElement.hpp"
#include "../OpenGL.hpp"
#include "../decorations/CHyprDropShadowDecoration.hpp"
CShadowPassElement::CShadowPassElement(const CShadowPassElement::SShadowData& data_) : data(data_) {
;
}
void CShadowPassElement::draw(const CRegion& damage) {
data.deco->render(g_pHyprOpenGL->m_RenderData.pMonitor.lock(), data.a);
}
bool CShadowPassElement::needsLiveBlur() {
return false;
}
bool CShadowPassElement::needsPrecomputeBlur() {
return false;
}

View file

@ -0,0 +1,26 @@
#pragma once
#include "PassElement.hpp"
class CHyprDropShadowDecoration;
class CShadowPassElement : public IPassElement {
public:
struct SShadowData {
CHyprDropShadowDecoration* deco = nullptr;
float a = 1.F;
};
CShadowPassElement(const SShadowData& data_);
virtual ~CShadowPassElement() = default;
virtual void draw(const CRegion& damage);
virtual bool needsLiveBlur();
virtual bool needsPrecomputeBlur();
virtual const char* passName() {
return "CShadowPassElement";
}
private:
SShadowData data;
};

View file

@ -0,0 +1,230 @@
#include "SurfacePassElement.hpp"
#include "../OpenGL.hpp"
#include "../../desktop/WLSurface.hpp"
#include "../../protocols/core/Compositor.hpp"
#include "../../protocols/DRMSyncobj.hpp"
#include "../../managers/input/InputManager.hpp"
#include "../Renderer.hpp"
#include <hyprutils/utils/ScopeGuard.hpp>
using namespace Hyprutils::Utils;
CSurfacePassElement::CSurfacePassElement(const CSurfacePassElement::SRenderData& data_) : data(data_) {
;
}
void CSurfacePassElement::draw(const CRegion& damage) {
g_pHyprOpenGL->m_RenderData.currentWindow = data.pWindow;
g_pHyprOpenGL->m_RenderData.currentLS = data.pLS;
g_pHyprOpenGL->m_RenderData.clipBox = data.clipBox;
g_pHyprOpenGL->m_RenderData.discardMode = data.discardMode;
g_pHyprOpenGL->m_RenderData.discardOpacity = data.discardOpacity;
g_pHyprOpenGL->m_RenderData.useNearestNeighbor = data.useNearestNeighbor;
g_pHyprOpenGL->m_bEndFrame = data.flipEndFrame;
CScopeGuard x = {[]() {
g_pHyprOpenGL->m_RenderData.primarySurfaceUVTopLeft = Vector2D(-1, -1);
g_pHyprOpenGL->m_RenderData.primarySurfaceUVBottomRight = Vector2D(-1, -1);
g_pHyprOpenGL->m_RenderData.useNearestNeighbor = false;
g_pHyprOpenGL->m_RenderData.clipBox = {};
g_pHyprOpenGL->m_RenderData.discardMode = 0;
g_pHyprOpenGL->m_RenderData.discardOpacity = 0;
g_pHyprOpenGL->m_RenderData.useNearestNeighbor = false;
g_pHyprOpenGL->m_bEndFrame = false;
g_pHyprOpenGL->m_RenderData.currentWindow.reset();
g_pHyprOpenGL->m_RenderData.currentLS.reset();
}};
if (!data.texture)
return;
const auto& TEXTURE = data.texture;
// this is bad, probably has been logged elsewhere. Means the texture failed
// uploading to the GPU.
if (!TEXTURE->m_iTexID)
return;
// explicit sync: wait for the timeline, if any
if (data.surface->syncobj && data.surface->syncobj->current.acquireTimeline) {
if (!g_pHyprOpenGL->waitForTimelinePoint(data.surface->syncobj->current.acquireTimeline->timeline, data.surface->syncobj->current.acquirePoint)) {
Debug::log(ERR, "Renderer: failed to wait for explicit timeline");
return;
}
}
const auto INTERACTIVERESIZEINPROGRESS = data.pWindow && g_pInputManager->currentlyDraggedWindow && g_pInputManager->dragMode == MBIND_RESIZE;
TRACY_GPU_ZONE("RenderSurface");
auto PSURFACE = CWLSurface::fromResource(data.surface);
const float ALPHA = data.alpha * data.fadeAlpha * (PSURFACE ? PSURFACE->m_pAlphaModifier : 1.F);
const bool BLUR = data.blur && (!TEXTURE->m_bOpaque || ALPHA < 1.F);
auto windowBox = getTexBox();
const auto PROJSIZEUNSCALED = windowBox.size();
windowBox.scale(data.pMonitor->scale);
windowBox.round();
if (windowBox.width <= 1 || windowBox.height <= 1) {
discard();
return;
}
const bool MISALIGNEDFSV1 = std::floor(data.pMonitor->scale) != data.pMonitor->scale /* Fractional */ && data.surface->current.scale == 1 /* fs protocol */ &&
windowBox.size() != data.surface->current.bufferSize /* misaligned */ && DELTALESSTHAN(windowBox.width, data.surface->current.bufferSize.x, 3) &&
DELTALESSTHAN(windowBox.height, data.surface->current.bufferSize.y, 3) /* off by one-or-two */ &&
(!data.pWindow || (!data.pWindow->m_vRealSize.isBeingAnimated() && !INTERACTIVERESIZEINPROGRESS)) /* not window or not animated/resizing */;
g_pHyprRenderer->calculateUVForSurface(data.pWindow, data.surface, data.pMonitor->self.lock(), data.mainSurface, windowBox.size(), PROJSIZEUNSCALED, MISALIGNEDFSV1);
// check for fractional scale surfaces misaligning the buffer size
// in those cases it's better to just force nearest neighbor
// as long as the window is not animated. During those it'd look weird.
// UV will fixup it as well
if (MISALIGNEDFSV1)
g_pHyprOpenGL->m_RenderData.useNearestNeighbor = true;
float rounding = data.rounding;
rounding -= 1; // to fix a border issue
if (data.dontRound)
rounding = 0;
const bool WINDOWOPAQUE = data.pWindow && data.pWindow->m_pWLSurface->resource() == data.surface ? data.pWindow->opaque() : false;
const bool CANDISABLEBLEND = ALPHA >= 1.f && rounding == 0 && WINDOWOPAQUE;
if (CANDISABLEBLEND)
g_pHyprOpenGL->blend(false);
else
g_pHyprOpenGL->blend(true);
// FIXME: This is wrong and will bug the blur out as shit if the first surface
// is a subsurface that does NOT cover the entire frame. In such cases, we probably should fall back
// to what we do for misaligned surfaces (blur the entire thing and then render shit without blur)
if (data.surfaceCounter == 0 && !data.popup) {
if (BLUR)
g_pHyprOpenGL->renderTextureWithBlur(TEXTURE, &windowBox, ALPHA, data.surface, rounding, data.blockBlurOptimization, data.fadeAlpha);
else
g_pHyprOpenGL->renderTexture(TEXTURE, &windowBox, ALPHA, rounding, false, true);
} else {
if (BLUR && data.popup)
g_pHyprOpenGL->renderTextureWithBlur(TEXTURE, &windowBox, ALPHA, data.surface, rounding, true, data.fadeAlpha);
else
g_pHyprOpenGL->renderTexture(TEXTURE, &windowBox, ALPHA, rounding, false, true);
}
if (!g_pHyprRenderer->m_bBlockSurfaceFeedback)
data.surface->presentFeedback(data.when, data.pMonitor->self.lock());
g_pHyprOpenGL->blend(true);
}
CBox CSurfacePassElement::getTexBox() {
const double outputX = -data.pMonitor->vecPosition.x, outputY = -data.pMonitor->vecPosition.y;
const auto INTERACTIVERESIZEINPROGRESS = data.pWindow && g_pInputManager->currentlyDraggedWindow && g_pInputManager->dragMode == MBIND_RESIZE;
auto PSURFACE = CWLSurface::fromResource(data.surface);
CBox windowBox;
if (data.surface && data.mainSurface) {
windowBox = {(int)outputX + data.pos.x + data.localPos.x, (int)outputY + data.pos.y + data.localPos.y, data.w, data.h};
// however, if surface buffer w / h < box, we need to adjust them
const auto PWINDOW = PSURFACE ? PSURFACE->getWindow() : nullptr;
// center the surface if it's smaller than the viewport we assign it
if (PSURFACE && !PSURFACE->m_bFillIgnoreSmall && PSURFACE->small() /* guarantees PWINDOW */) {
const auto CORRECT = PSURFACE->correctSmallVec();
const auto SIZE = PSURFACE->getViewporterCorrectedSize();
if (!INTERACTIVERESIZEINPROGRESS) {
windowBox.translate(CORRECT);
windowBox.width = SIZE.x * (PWINDOW->m_vRealSize.value().x / PWINDOW->m_vReportedSize.x);
windowBox.height = SIZE.y * (PWINDOW->m_vRealSize.value().y / PWINDOW->m_vReportedSize.y);
} else {
windowBox.width = SIZE.x;
windowBox.height = SIZE.y;
}
}
} else { // here we clamp to 2, these might be some tiny specks
windowBox = {(int)outputX + data.pos.x + data.localPos.x, (int)outputY + data.pos.y + data.localPos.y, std::max((float)data.surface->current.size.x, 2.F),
std::max((float)data.surface->current.size.y, 2.F)};
if (data.pWindow && data.pWindow->m_vRealSize.isBeingAnimated() && data.surface && !data.mainSurface && data.squishOversized /* subsurface */) {
// adjust subsurfaces to the window
windowBox.width = (windowBox.width / data.pWindow->m_vReportedSize.x) * data.pWindow->m_vRealSize.value().x;
windowBox.height = (windowBox.height / data.pWindow->m_vReportedSize.y) * data.pWindow->m_vRealSize.value().y;
}
}
if (data.squishOversized) {
if (data.localPos.x + windowBox.width > data.w)
windowBox.width = data.w - data.localPos.x;
if (data.localPos.y + windowBox.height > data.h)
windowBox.height = data.h - data.localPos.y;
}
return windowBox;
}
bool CSurfacePassElement::needsLiveBlur() {
auto PSURFACE = CWLSurface::fromResource(data.surface);
const float ALPHA = data.alpha * data.fadeAlpha * (PSURFACE ? PSURFACE->m_pAlphaModifier : 1.F);
const bool BLUR = data.blur && (!data.texture || !data.texture->m_bOpaque || ALPHA < 1.F);
if (!data.pLS && !data.pWindow)
return BLUR;
const bool NEWOPTIM = g_pHyprOpenGL->shouldUseNewBlurOptimizations(data.pLS, data.pWindow);
return BLUR && !NEWOPTIM;
}
bool CSurfacePassElement::needsPrecomputeBlur() {
auto PSURFACE = CWLSurface::fromResource(data.surface);
const float ALPHA = data.alpha * data.fadeAlpha * (PSURFACE ? PSURFACE->m_pAlphaModifier : 1.F);
const bool BLUR = data.blur && (!data.texture || !data.texture->m_bOpaque || ALPHA < 1.F);
if (!data.pLS && !data.pWindow)
return BLUR;
const bool NEWOPTIM = g_pHyprOpenGL->shouldUseNewBlurOptimizations(data.pLS, data.pWindow);
return BLUR && NEWOPTIM;
}
std::optional<CBox> CSurfacePassElement::boundingBox() {
return getTexBox();
}
CRegion CSurfacePassElement::opaqueRegion() {
auto PSURFACE = CWLSurface::fromResource(data.surface);
const float ALPHA = data.alpha * data.fadeAlpha * (PSURFACE ? PSURFACE->m_pAlphaModifier : 1.F);
if (ALPHA < 1.F)
return {};
if (data.surface && data.surface->current.size == Vector2D{data.w, data.h}) {
CRegion opaqueSurf = data.surface->current.opaque.copy().intersect(CBox{{}, {data.w, data.h}});
const auto texBox = getTexBox();
opaqueSurf.scale(texBox.size() / Vector2D{data.w, data.h});
return opaqueSurf.translate(data.pos + data.localPos - data.pMonitor->vecPosition).expand(-data.rounding);
}
return data.texture && data.texture->m_bOpaque ? boundingBox()->expand(-data.rounding) : CRegion{};
}
void CSurfacePassElement::discard() {
if (!g_pHyprRenderer->m_bBlockSurfaceFeedback) {
Debug::log(TRACE, "discard for invisible surface");
data.surface->presentFeedback(data.when, data.pMonitor->self.lock(), true);
}
}

View file

@ -0,0 +1,82 @@
#pragma once
#include "PassElement.hpp"
#include <optional>
class CWLSurfaceResource;
class CTexture;
class CSyncTimeline;
class CSurfacePassElement : public IPassElement {
public:
struct SRenderData {
PHLMONITORREF pMonitor;
timespec* when = nullptr;
Vector2D pos, localPos;
// for iters
void* data = nullptr;
SP<CWLSurfaceResource> surface = nullptr;
SP<CTexture> texture = nullptr;
bool mainSurface = true;
double w = 0, h = 0;
// for rounding
bool dontRound = true;
// for fade
float fadeAlpha = 1.f;
// for alpha settings
float alpha = 1.f;
// for decorations (border)
bool decorate = false;
// for custom round values
int rounding = -1; // -1 means not set
// for blurring
bool blur = false;
bool blockBlurOptimization = false;
// only for windows, not popups
bool squishOversized = true;
// for calculating UV
PHLWINDOW pWindow;
PHLLS pLS;
bool popup = false;
// counts how many surfaces this pass has rendered
int surfaceCounter = 0;
CBox clipBox = {}; // scaled coordinates
uint32_t discardMode = 0;
float discardOpacity = 0.f;
bool useNearestNeighbor = false;
bool flipEndFrame = false;
};
CSurfacePassElement(const SRenderData& data);
virtual ~CSurfacePassElement() = default;
virtual void draw(const CRegion& damage);
virtual bool needsLiveBlur();
virtual bool needsPrecomputeBlur();
virtual std::optional<CBox> boundingBox();
virtual CRegion opaqueRegion();
virtual void discard();
virtual const char* passName() {
return "CSurfacePassElement";
}
private:
SRenderData data;
CBox getTexBox();
};

View file

@ -0,0 +1,44 @@
#include "TexPassElement.hpp"
#include "../OpenGL.hpp"
#include <hyprutils/utils/ScopeGuard.hpp>
using namespace Hyprutils::Utils;
CTexPassElement::CTexPassElement(const CTexPassElement::SRenderData& data_) : data(data_) {
;
}
void CTexPassElement::draw(const CRegion& damage) {
g_pHyprOpenGL->m_bEndFrame = data.flipEndFrame;
CScopeGuard x = {[]() {
//
g_pHyprOpenGL->m_bEndFrame = false;
}};
if (data.replaceProjection)
g_pHyprOpenGL->m_RenderData.monitorProjection = *data.replaceProjection;
g_pHyprOpenGL->renderTextureInternalWithDamage(data.tex, &data.box, data.a, data.damage.empty() ? damage : data.damage, data.round, data.syncTimeline, data.syncPoint);
if (data.replaceProjection)
g_pHyprOpenGL->m_RenderData.monitorProjection = g_pHyprOpenGL->m_RenderData.pMonitor->projMatrix;
}
bool CTexPassElement::needsLiveBlur() {
return false; // TODO?
}
bool CTexPassElement::needsPrecomputeBlur() {
return false; // TODO?
}
std::optional<CBox> CTexPassElement::boundingBox() {
return data.box.copy().scale(1.F / g_pHyprOpenGL->m_RenderData.pMonitor->scale).round();
}
CRegion CTexPassElement::opaqueRegion() {
return {}; // TODO:
}
void CTexPassElement::discard() {
;
}

View file

@ -0,0 +1,39 @@
#pragma once
#include "PassElement.hpp"
#include <optional>
class CWLSurfaceResource;
class CTexture;
class CSyncTimeline;
class CTexPassElement : public IPassElement {
public:
struct SRenderData {
SP<CTexture> tex;
CBox box;
float a = 1.F;
CRegion damage;
int round = 0;
bool flipEndFrame = false;
SP<CSyncTimeline> syncTimeline;
int64_t syncPoint = 0;
std::optional<Mat3x3> replaceProjection;
};
CTexPassElement(const SRenderData& data);
virtual ~CTexPassElement() = default;
virtual void draw(const CRegion& damage);
virtual bool needsLiveBlur();
virtual bool needsPrecomputeBlur();
virtual std::optional<CBox> boundingBox();
virtual CRegion opaqueRegion();
virtual void discard();
virtual const char* passName() {
return "CTexPassElement";
}
private:
SRenderData data;
};

View file

@ -0,0 +1,25 @@
#include "TextureMatteElement.hpp"
#include "../OpenGL.hpp"
CTextureMatteElement::CTextureMatteElement(const CTextureMatteElement::STextureMatteData& data_) : data(data_) {
;
}
void CTextureMatteElement::draw(const CRegion& damage) {
if (data.disableTransformAndModify) {
g_pHyprOpenGL->setMonitorTransformEnabled(true);
g_pHyprOpenGL->setRenderModifEnabled(false);
g_pHyprOpenGL->renderTextureMatte(data.tex, &data.box, *data.fb);
g_pHyprOpenGL->setRenderModifEnabled(true);
g_pHyprOpenGL->setMonitorTransformEnabled(false);
} else
g_pHyprOpenGL->renderTextureMatte(data.tex, &data.box, *data.fb);
}
bool CTextureMatteElement::needsLiveBlur() {
return false;
}
bool CTextureMatteElement::needsPrecomputeBlur() {
return false;
}

View file

@ -0,0 +1,29 @@
#pragma once
#include "PassElement.hpp"
#include "../Framebuffer.hpp"
class CTexture;
class CTextureMatteElement : public IPassElement {
public:
struct STextureMatteData {
CBox box;
SP<CTexture> tex;
SP<CFramebuffer> fb;
bool disableTransformAndModify = false;
};
CTextureMatteElement(const STextureMatteData& data_);
virtual ~CTextureMatteElement() = default;
virtual void draw(const CRegion& damage);
virtual bool needsLiveBlur();
virtual bool needsPrecomputeBlur();
virtual const char* passName() {
return "CTextureMatteElement";
}
private:
STextureMatteData data;
};