background: fix mismatched asset sizes and transforms (#830)

This commit is contained in:
Maximilian Seidler 2025-07-25 18:39:56 +02:00 committed by GitHub
parent d993bdc105
commit 1e5e62d6e3
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 125 additions and 84 deletions

View file

@ -16,6 +16,7 @@
CBackground::CBackground() { CBackground::CBackground() {
blurredFB = makeUnique<CFramebuffer>(); blurredFB = makeUnique<CFramebuffer>();
pendingBlurredFB = makeUnique<CFramebuffer>(); pendingBlurredFB = makeUnique<CFramebuffer>();
transformedScFB = makeUnique<CFramebuffer>();
} }
CBackground::~CBackground() { CBackground::~CBackground() {
@ -52,25 +53,25 @@ void CBackground::configure(const std::unordered_map<std::string, std::any>& pro
viewport = pOutput->getViewport(); viewport = pOutput->getViewport();
outputPort = pOutput->stringPort; outputPort = pOutput->stringPort;
transform = isScreenshot ? wlTransformToHyprutils(invertTransform(pOutput->transform)) : HYPRUTILS_TRANSFORM_NORMAL; transform = wlTransformToHyprutils(invertTransform(pOutput->transform));
scResourceID = CScreencopyFrame::getResourceId(pOutput); scResourceID = CScreencopyFrame::getResourceId(pOutput);
g_pAnimationManager->createAnimation(0.f, crossFadeProgress, g_pConfigManager->m_AnimationTree.getConfig("fadeIn")); g_pAnimationManager->createAnimation(0.f, crossFadeProgress, g_pConfigManager->m_AnimationTree.getConfig("fadeIn"));
// When the initial gather of the asyncResourceGatherer is completed (ready), all DMAFrames are available.
// Dynamic ones are tricky, because a screencopy would copy hyprlock itself.
if (g_pRenderer->asyncResourceGatherer->gathered && !g_pRenderer->asyncResourceGatherer->getAssetByID(scResourceID)) {
Debug::log(LOG, "Missing screenshot for output {}", outputPort);
scResourceID = "";
}
if (isScreenshot) { if (isScreenshot) {
resourceID = scResourceID; resourceID = scResourceID; // Fallback to solid background:color when scResourceID==""
// When the initial gather of the asyncResourceGatherer is completed (ready), all DMAFrames are available.
// Dynamic ones are tricky, because a screencopy would copy hyprlock itself.
if (g_pRenderer->asyncResourceGatherer->gathered) {
if (!g_pRenderer->asyncResourceGatherer->getAssetByID(resourceID))
resourceID = ""; // Fallback to solid color (background:color)
}
if (!g_pHyprlock->getScreencopy()) { if (!g_pHyprlock->getScreencopy()) {
Debug::log(ERR, "No screencopy support! path=screenshot won't work. Falling back to background color."); Debug::log(ERR, "No screencopy support! path=screenshot won't work. Falling back to background color.");
resourceID = ""; resourceID = "";
} }
} else if (!path.empty()) } else if (!path.empty())
resourceID = "background:" + path; resourceID = "background:" + path;
@ -93,6 +94,57 @@ void CBackground::reset() {
pendingBlurredFB->destroyBuffer(); pendingBlurredFB->destroyBuffer();
} }
void CBackground::updatePrimaryAsset() {
if (asset || resourceID.empty())
return;
asset = g_pRenderer->asyncResourceGatherer->getAssetByID(resourceID);
if (!asset)
return;
const bool NEEDFB = (isScreenshot || blurPasses > 0 || asset->texture.m_vSize != viewport) && (!blurredFB->isAllocated() || firstRender);
if (NEEDFB)
renderToFB(asset->texture, *blurredFB, blurPasses);
}
void CBackground::updatePendingAsset() {
// For crossfading a new asset
if (!pendingAsset || blurPasses == 0 || pendingBlurredFB->isAllocated())
return;
renderToFB(pendingAsset->texture, *pendingBlurredFB, blurPasses);
}
void CBackground::updateScAsset() {
if (scAsset || scResourceID.empty())
return;
// path=screenshot -> scAsset = asset
scAsset = (asset && isScreenshot) ? asset : g_pRenderer->asyncResourceGatherer->getAssetByID(scResourceID);
if (!scAsset)
return;
const bool NEEDSCTRANSFORM = transform != HYPRUTILS_TRANSFORM_NORMAL;
if (NEEDSCTRANSFORM)
renderToFB(scAsset->texture, *transformedScFB, 0, true);
}
const CTexture& CBackground::getPrimaryAssetTex() const {
// This case is only for background:path=screenshot with blurPasses=0
if (isScreenshot && blurPasses == 0 && transformedScFB->isAllocated())
return transformedScFB->m_cTex;
return (blurredFB->isAllocated()) ? blurredFB->m_cTex : asset->texture;
}
const CTexture& CBackground::getPendingAssetTex() const {
return (pendingBlurredFB->isAllocated()) ? pendingBlurredFB->m_cTex : pendingAsset->texture;
}
const CTexture& CBackground::getScAssetTex() const {
return (transformedScFB->isAllocated()) ? transformedScFB->m_cTex : scAsset->texture;
}
void CBackground::renderRect(CHyprColor color) { void CBackground::renderRect(CHyprColor color) {
CBox monbox = {0, 0, viewport.x, viewport.y}; CBox monbox = {0, 0, viewport.x, viewport.y};
g_pRenderer->renderRect(monbox, color, 0); g_pRenderer->renderRect(monbox, color, 0);
@ -110,17 +162,7 @@ static void onAssetCallback(AWP<CBackground> ref) {
PBG->startCrossFade(); PBG->startCrossFade();
} }
void CBackground::renderBlur(const CTexture& tex, CFramebuffer& fb) { static CBox getScaledBoxForTextureSize(const Vector2D& size, const Vector2D& viewport) {
if (firstRender)
firstRender = false;
// make it brah
Vector2D size = asset->texture.m_vSize;
if (transform % 2 == 1 && isScreenshot) {
size.x = asset->texture.m_vSize.y;
size.y = asset->texture.m_vSize.x;
}
CBox texbox = {{}, size}; CBox texbox = {{}, size};
float scaleX = viewport.x / size.x; float scaleX = viewport.x / size.x;
@ -135,57 +177,61 @@ void CBackground::renderBlur(const CTexture& tex, CFramebuffer& fb) {
texbox.x = -(texbox.w - viewport.x) / 2.f; texbox.x = -(texbox.w - viewport.x) / 2.f;
texbox.round(); texbox.round();
return texbox;
}
void CBackground::renderToFB(const CTexture& tex, CFramebuffer& fb, int passes, bool applyTransform) {
if (firstRender)
firstRender = false;
// make it brah
Vector2D size = tex.m_vSize;
if (applyTransform && transform % 2 == 1) {
size.x = tex.m_vSize.y;
size.y = tex.m_vSize.x;
}
const auto TEXBOX = getScaledBoxForTextureSize(size, viewport);
if (!fb.isAllocated()) if (!fb.isAllocated())
fb.alloc(viewport.x, viewport.y); // TODO 10 bit fb.alloc(viewport.x, viewport.y); // TODO 10 bit
fb.bind(); fb.bind();
g_pRenderer->renderTexture(texbox, tex, 1.0, 0, transform); g_pRenderer->renderTexture(TEXBOX, tex, 1.0, 0, applyTransform ? transform : HYPRUTILS_TRANSFORM_NORMAL);
if (blurPasses > 0) if (blurPasses > 0)
g_pRenderer->blurFB(fb, g_pRenderer->blurFB(fb,
CRenderer::SBlurParams{.size = blurSize, CRenderer::SBlurParams{
.passes = blurPasses, .size = blurSize,
.noise = noise, .passes = passes,
.contrast = contrast, .noise = noise,
.brightness = brightness, .contrast = contrast,
.vibrancy = vibrancy, .brightness = brightness,
.vibrancy_darkness = vibrancy_darkness}); .vibrancy = vibrancy,
.vibrancy_darkness = vibrancy_darkness,
});
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0); glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0);
} }
static CBox getScaledBoxForTexture(const CTexture& tex, const Vector2D& viewport) {
CBox texbox = {{}, tex.m_vSize};
Vector2D size = tex.m_vSize;
float scaleX = viewport.x / tex.m_vSize.x;
float scaleY = viewport.y / tex.m_vSize.y;
texbox.w *= std::max(scaleX, scaleY);
texbox.h *= std::max(scaleX, scaleY);
if (scaleX > scaleY)
texbox.y = -(texbox.h - viewport.y) / 2.f;
else
texbox.x = -(texbox.w - viewport.x) / 2.f;
texbox.round();
return texbox;
}
bool CBackground::draw(const SRenderData& data) { bool CBackground::draw(const SRenderData& data) {
if (!asset && !resourceID.empty()) updatePrimaryAsset();
asset = g_pRenderer->asyncResourceGatherer->getAssetByID(resourceID); updatePendingAsset();
updateScAsset();
// path=screenshot -> scAsset = asset if (asset && asset->texture.m_iType == TEXTURE_INVALID) {
if (!scAsset) g_pRenderer->asyncResourceGatherer->unloadAsset(asset);
scAsset = (asset && isScreenshot) ? asset : g_pRenderer->asyncResourceGatherer->getAssetByID(scResourceID); resourceID = "";
renderRect(color);
return false;
}
if (!asset || resourceID.empty()) { if (!asset || resourceID.empty()) {
// fade in/out with a solid color // fade in/out with a solid color
if (data.opacity < 1.0 && scAsset) { if (data.opacity < 1.0 && scAsset) {
const auto SCTEXBOX = getScaledBoxForTexture(scAsset->texture, viewport); const auto& SCTEX = getScAssetTex();
g_pRenderer->renderTexture(SCTEXBOX, scAsset->texture, 1, 0, HYPRUTILS_TRANSFORM_FLIPPED_180); const auto SCTEXBOX = getScaledBoxForTextureSize(SCTEX.m_vSize, viewport);
g_pRenderer->renderTexture(SCTEXBOX, SCTEX, 1, 0, HYPRUTILS_TRANSFORM_FLIPPED_180);
CHyprColor col = color; CHyprColor col = color;
col.a *= data.opacity; col.a *= data.opacity;
renderRect(col); renderRect(col);
@ -196,27 +242,13 @@ bool CBackground::draw(const SRenderData& data) {
return !asset && !resourceID.empty(); // resource not ready return !asset && !resourceID.empty(); // resource not ready
} }
if (asset->texture.m_iType == TEXTURE_INVALID) { const auto& TEX = getPrimaryAssetTex();
g_pRenderer->asyncResourceGatherer->unloadAsset(asset); const auto TEXBOX = getScaledBoxForTextureSize(TEX.m_vSize, viewport);
resourceID = ""; if (data.opacity < 1.0 && scAsset) {
renderRect(color); const auto& SCTEX = getScAssetTex();
return false; g_pRenderer->renderTextureMix(TEXBOX, SCTEX, TEX, 1.0, data.opacity, 0);
} } else if (crossFadeProgress->isBeingAnimated()) {
const auto& PENDINGTEX = getPendingAssetTex();
if (asset && (blurPasses > 0 || isScreenshot) && (!blurredFB->isAllocated() || firstRender))
renderBlur(asset->texture, *blurredFB);
// For crossfading a new asset
if (pendingAsset && blurPasses > 0 && !pendingBlurredFB->isAllocated())
renderBlur(pendingAsset->texture, *pendingBlurredFB);
const auto& TEX = blurredFB->isAllocated() ? blurredFB->m_cTex : asset->texture;
const auto TEXBOX = getScaledBoxForTexture(TEX, viewport);
if (data.opacity < 1.0 && scAsset)
g_pRenderer->renderTextureMix(TEXBOX, scAsset->texture, TEX, 1.0, data.opacity, 0);
else if (crossFadeProgress->isBeingAnimated()) {
const auto& PENDINGTEX = pendingBlurredFB->isAllocated() ? pendingBlurredFB->m_cTex : pendingAsset->texture;
g_pRenderer->renderTextureMix(TEXBOX, TEX, PENDINGTEX, 1.0, crossFadeProgress->value(), 0); g_pRenderer->renderTextureMix(TEXBOX, TEX, PENDINGTEX, 1.0, crossFadeProgress->value(), 0);
} else } else
g_pRenderer->renderTexture(TEXBOX, TEX, 1, 0, HYPRUTILS_TRANSFORM_FLIPPED_180); g_pRenderer->renderTexture(TEXBOX, TEX, 1, 0, HYPRUTILS_TRANSFORM_FLIPPED_180);

View file

@ -7,6 +7,7 @@
#include "../../core/Timer.hpp" #include "../../core/Timer.hpp"
#include "../Framebuffer.hpp" #include "../Framebuffer.hpp"
#include "../AsyncResourceGatherer.hpp" #include "../AsyncResourceGatherer.hpp"
#include <cstdint>
#include <hyprutils/math/Misc.hpp> #include <hyprutils/math/Misc.hpp>
#include <string> #include <string>
#include <unordered_map> #include <unordered_map>
@ -22,20 +23,27 @@ class CBackground : public IWidget {
CBackground(); CBackground();
~CBackground(); ~CBackground();
void registerSelf(const ASP<CBackground>& self); void registerSelf(const ASP<CBackground>& self);
virtual void configure(const std::unordered_map<std::string, std::any>& props, const SP<COutput>& pOutput); virtual void configure(const std::unordered_map<std::string, std::any>& props, const SP<COutput>& pOutput);
virtual bool draw(const SRenderData& data); virtual bool draw(const SRenderData& data);
void reset(); // Unload assets, remove timers, etc. void reset(); // Unload assets, remove timers, etc.
void renderRect(CHyprColor color); void updatePrimaryAsset();
void updatePendingAsset();
void updateScAsset();
void renderBlur(const CTexture& text, CFramebuffer& fb); const CTexture& getPrimaryAssetTex() const;
const CTexture& getPendingAssetTex() const;
const CTexture& getScAssetTex() const;
void onReloadTimerUpdate(); void renderRect(CHyprColor color);
void plantReloadTimer(); void renderToFB(const CTexture& text, CFramebuffer& fb, int passes, bool applyTransform = false);
void startCrossFade();
void onReloadTimerUpdate();
void plantReloadTimer();
void startCrossFade();
private: private:
AWP<CBackground> m_self; AWP<CBackground> m_self;
@ -43,6 +51,7 @@ class CBackground : public IWidget {
// if needed // if needed
UP<CFramebuffer> blurredFB; UP<CFramebuffer> blurredFB;
UP<CFramebuffer> pendingBlurredFB; UP<CFramebuffer> pendingBlurredFB;
UP<CFramebuffer> transformedScFB;
int blurSize = 10; int blurSize = 10;
int blurPasses = 3; int blurPasses = 3;