mirror of
https://github.com/hyprwm/Hyprland
synced 2024-11-26 13:05:58 +01:00
renderer: Fix mirrored displays when transformed and preserve aspect ratio (#5697)
* renderer: transform mirror buffer and preserve mirror aspect ratio * renderer: render mirrors directly from offloadFB * renderer: fix formatting * renderer: use monitorMirrorFB again, but properly damage mirrors * renderer: clean mirrors after reload and support cursor zoom mirroring
This commit is contained in:
parent
8aecd4f253
commit
9fe409800b
4 changed files with 64 additions and 21 deletions
|
@ -826,6 +826,10 @@ void CConfigManager::postConfigReload(const Hyprlang::CParseResult& result) {
|
||||||
|
|
||||||
// Force the compositor to fully re-render all monitors
|
// Force the compositor to fully re-render all monitors
|
||||||
m->forceFullFrames = 2;
|
m->forceFullFrames = 2;
|
||||||
|
|
||||||
|
// also force mirrors, as the aspect ratio could've changed
|
||||||
|
for (auto& mirror : m->mirrors)
|
||||||
|
mirror->forceFullFrames = 3;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Reset no monitor reload
|
// Reset no monitor reload
|
||||||
|
|
|
@ -152,6 +152,12 @@ bool CHyprOpenGLImpl::passRequiresIntrospection(CMonitor* pMonitor) {
|
||||||
if (m_RenderData.mouseZoomFactor != 1.0 || g_pHyprRenderer->m_bCrashingInProgress)
|
if (m_RenderData.mouseZoomFactor != 1.0 || g_pHyprRenderer->m_bCrashingInProgress)
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
|
// mirrors should not be offloaded (as we then would basically copy the same data twice)
|
||||||
|
// yes, this breaks mirrors of mirrors
|
||||||
|
if (pMonitor->isMirror())
|
||||||
|
return false;
|
||||||
|
|
||||||
|
// monitors that are mirrored however must be offloaded because we cannot copy from output FBs
|
||||||
if (!pMonitor->mirrors.empty())
|
if (!pMonitor->mirrors.empty())
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
|
@ -336,14 +342,10 @@ void CHyprOpenGLImpl::end() {
|
||||||
|
|
||||||
TRACY_GPU_ZONE("RenderEnd");
|
TRACY_GPU_ZONE("RenderEnd");
|
||||||
|
|
||||||
if (!m_RenderData.pMonitor->mirrors.empty() && !m_bFakeFrame)
|
|
||||||
saveBufferForMirror(); // save with original damage region
|
|
||||||
|
|
||||||
// end the render, copy the data to the WLR framebuffer
|
// end the render, copy the data to the WLR framebuffer
|
||||||
if (m_bOffloadedFramebuffer) {
|
if (m_bOffloadedFramebuffer) {
|
||||||
m_RenderData.damage = m_RenderData.finalDamage;
|
m_RenderData.damage = m_RenderData.finalDamage;
|
||||||
|
m_bEndFrame = true;
|
||||||
m_RenderData.outFB->bind();
|
|
||||||
|
|
||||||
CBox monbox = {0, 0, m_RenderData.pMonitor->vecTransformedSize.x, m_RenderData.pMonitor->vecTransformedSize.y};
|
CBox monbox = {0, 0, m_RenderData.pMonitor->vecTransformedSize.x, m_RenderData.pMonitor->vecTransformedSize.y};
|
||||||
|
|
||||||
|
@ -364,11 +366,16 @@ void CHyprOpenGLImpl::end() {
|
||||||
monbox.y = m_RenderData.pMonitor->vecTransformedSize.y - monbox.height;
|
monbox.y = m_RenderData.pMonitor->vecTransformedSize.y - monbox.height;
|
||||||
}
|
}
|
||||||
|
|
||||||
m_bEndFrame = true;
|
|
||||||
m_bApplyFinalShader = !m_RenderData.blockScreenShader;
|
m_bApplyFinalShader = !m_RenderData.blockScreenShader;
|
||||||
if (m_RenderData.mouseZoomUseMouse)
|
if (m_RenderData.mouseZoomUseMouse)
|
||||||
m_RenderData.useNearestNeighbor = true;
|
m_RenderData.useNearestNeighbor = true;
|
||||||
|
|
||||||
|
// copy the damaged areas into the mirror buffer
|
||||||
|
// we can't use the offloadFB for mirroring, as it contains artifacts from blurring
|
||||||
|
if (!m_RenderData.pMonitor->mirrors.empty() && !m_bFakeFrame)
|
||||||
|
saveBufferForMirror(&monbox);
|
||||||
|
|
||||||
|
m_RenderData.outFB->bind();
|
||||||
blend(false);
|
blend(false);
|
||||||
|
|
||||||
if (m_sFinalScreenShader.program < 1 && !g_pHyprRenderer->m_bCrashingInProgress)
|
if (m_sFinalScreenShader.program < 1 && !g_pHyprRenderer->m_bCrashingInProgress)
|
||||||
|
@ -1971,18 +1978,16 @@ void CHyprOpenGLImpl::renderRoundedShadow(CBox* box, int round, int range, const
|
||||||
glDisableVertexAttribArray(m_RenderData.pCurrentMonData->m_shSHADOW.texAttrib);
|
glDisableVertexAttribArray(m_RenderData.pCurrentMonData->m_shSHADOW.texAttrib);
|
||||||
}
|
}
|
||||||
|
|
||||||
void CHyprOpenGLImpl::saveBufferForMirror() {
|
void CHyprOpenGLImpl::saveBufferForMirror(CBox* box) {
|
||||||
|
|
||||||
if (!m_RenderData.pCurrentMonData->monitorMirrorFB.isAllocated())
|
if (!m_RenderData.pCurrentMonData->monitorMirrorFB.isAllocated())
|
||||||
m_RenderData.pCurrentMonData->monitorMirrorFB.alloc(m_RenderData.pMonitor->vecPixelSize.x, m_RenderData.pMonitor->vecPixelSize.y, m_RenderData.pMonitor->drmFormat);
|
m_RenderData.pCurrentMonData->monitorMirrorFB.alloc(m_RenderData.pMonitor->vecPixelSize.x, m_RenderData.pMonitor->vecPixelSize.y, m_RenderData.pMonitor->drmFormat);
|
||||||
|
|
||||||
m_RenderData.pCurrentMonData->monitorMirrorFB.bind();
|
m_RenderData.pCurrentMonData->monitorMirrorFB.bind();
|
||||||
|
|
||||||
CBox monbox = {0, 0, m_RenderData.pMonitor->vecPixelSize.x, m_RenderData.pMonitor->vecPixelSize.y};
|
|
||||||
|
|
||||||
blend(false);
|
blend(false);
|
||||||
|
|
||||||
renderTexture(m_RenderData.currentFB->m_cTex, &monbox, 1.f, 0, false, false);
|
renderTexture(m_RenderData.currentFB->m_cTex, box, 1.f, 0, false, false);
|
||||||
|
|
||||||
blend(true);
|
blend(true);
|
||||||
|
|
||||||
|
@ -1990,14 +1995,37 @@ void CHyprOpenGLImpl::saveBufferForMirror() {
|
||||||
}
|
}
|
||||||
|
|
||||||
void CHyprOpenGLImpl::renderMirrored() {
|
void CHyprOpenGLImpl::renderMirrored() {
|
||||||
CBox monbox = {0, 0, m_RenderData.pMonitor->vecPixelSize.x, m_RenderData.pMonitor->vecPixelSize.y};
|
|
||||||
|
|
||||||
const auto PFB = &m_mMonitorRenderResources[m_RenderData.pMonitor->pMirrorOf].monitorMirrorFB;
|
auto monitor = m_RenderData.pMonitor;
|
||||||
|
auto mirrored = monitor->pMirrorOf;
|
||||||
|
|
||||||
|
double scale = std::min(monitor->vecTransformedSize.x / mirrored->vecTransformedSize.x, monitor->vecTransformedSize.y / mirrored->vecTransformedSize.y);
|
||||||
|
CBox monbox = {0, 0, mirrored->vecTransformedSize.x * scale, mirrored->vecTransformedSize.y * scale};
|
||||||
|
|
||||||
|
// transform box as it will be drawn on a transformed projection
|
||||||
|
monbox.transform(mirrored->transform, mirrored->vecTransformedSize.x * scale, mirrored->vecTransformedSize.y * scale);
|
||||||
|
|
||||||
|
monbox.x = (monitor->vecTransformedSize.x - monbox.w) / 2;
|
||||||
|
monbox.y = (monitor->vecTransformedSize.y - monbox.h) / 2;
|
||||||
|
|
||||||
|
const auto PFB = &m_mMonitorRenderResources[mirrored].monitorMirrorFB;
|
||||||
if (!PFB->isAllocated() || PFB->m_cTex.m_iTexID <= 0)
|
if (!PFB->isAllocated() || PFB->m_cTex.m_iTexID <= 0)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
// replace monitor projection to undo the mirrored monitor's projection
|
||||||
|
wlr_matrix_identity(monitor->projMatrix.data());
|
||||||
|
wlr_matrix_translate(monitor->projMatrix.data(), monitor->vecPixelSize.x / 2.0, monitor->vecPixelSize.y / 2.0);
|
||||||
|
wlr_matrix_transform(monitor->projMatrix.data(), monitor->transform);
|
||||||
|
wlr_matrix_transform(monitor->projMatrix.data(), wlr_output_transform_invert(mirrored->transform));
|
||||||
|
wlr_matrix_translate(monitor->projMatrix.data(), -monitor->vecTransformedSize.x / 2.0, -monitor->vecTransformedSize.y / 2.0);
|
||||||
|
|
||||||
|
// clear stuff outside of mirrored area (e.g. when changing to mirrored)
|
||||||
|
clear(CColor(0, 0, 0, 0));
|
||||||
|
|
||||||
renderTexture(PFB->m_cTex, &monbox, 1.f, 0, false, false);
|
renderTexture(PFB->m_cTex, &monbox, 1.f, 0, false, false);
|
||||||
|
|
||||||
|
// reset matrix for further drawing
|
||||||
|
monitor->updateMatrix();
|
||||||
}
|
}
|
||||||
|
|
||||||
void CHyprOpenGLImpl::renderSplash(cairo_t* const CAIRO, cairo_surface_t* const CAIROSURFACE, double offsetY, const Vector2D& size) {
|
void CHyprOpenGLImpl::renderSplash(cairo_t* const CAIRO, cairo_surface_t* const CAIROSURFACE, double offsetY, const Vector2D& size) {
|
||||||
|
|
|
@ -67,7 +67,7 @@ struct SMonitorRenderData {
|
||||||
CFramebuffer mirrorSwapFB; // etc
|
CFramebuffer mirrorSwapFB; // etc
|
||||||
CFramebuffer offMainFB;
|
CFramebuffer offMainFB;
|
||||||
|
|
||||||
CFramebuffer monitorMirrorFB; // used for mirroring outputs
|
CFramebuffer monitorMirrorFB; // used for mirroring outputs, does not contain artifacts like offloadFB
|
||||||
|
|
||||||
CTexture stencilTex;
|
CTexture stencilTex;
|
||||||
|
|
||||||
|
@ -172,7 +172,7 @@ class CHyprOpenGLImpl {
|
||||||
bool preBlurQueued();
|
bool preBlurQueued();
|
||||||
void preRender(CMonitor*);
|
void preRender(CMonitor*);
|
||||||
|
|
||||||
void saveBufferForMirror();
|
void saveBufferForMirror(CBox*);
|
||||||
void renderMirrored();
|
void renderMirrored();
|
||||||
|
|
||||||
void applyScreenShader(const std::string& path);
|
void applyScreenShader(const std::string& path);
|
||||||
|
|
|
@ -1246,9 +1246,7 @@ void CHyprRenderer::renderMonitor(CMonitor* pMonitor) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// if we have no tracking or full tracking, invalidate the entire monitor
|
// if we have no tracking or full tracking, invalidate the entire monitor
|
||||||
if (*PDAMAGETRACKINGMODE == DAMAGE_TRACKING_NONE || *PDAMAGETRACKINGMODE == DAMAGE_TRACKING_MONITOR || pMonitor->forceFullFrames > 0 || damageBlinkCleanup > 0 ||
|
if (*PDAMAGETRACKINGMODE == DAMAGE_TRACKING_NONE || *PDAMAGETRACKINGMODE == DAMAGE_TRACKING_MONITOR || pMonitor->forceFullFrames > 0 || damageBlinkCleanup > 0) {
|
||||||
pMonitor->isMirror() /* why??? */) {
|
|
||||||
|
|
||||||
damage = {0, 0, (int)pMonitor->vecTransformedSize.x * 10, (int)pMonitor->vecTransformedSize.y * 10};
|
damage = {0, 0, (int)pMonitor->vecTransformedSize.x * 10, (int)pMonitor->vecTransformedSize.y * 10};
|
||||||
finalDamage = damage;
|
finalDamage = damage;
|
||||||
} else {
|
} else {
|
||||||
|
@ -1838,11 +1836,24 @@ void CHyprRenderer::damageRegion(const CRegion& rg) {
|
||||||
|
|
||||||
void CHyprRenderer::damageMirrorsWith(CMonitor* pMonitor, const CRegion& pRegion) {
|
void CHyprRenderer::damageMirrorsWith(CMonitor* pMonitor, const CRegion& pRegion) {
|
||||||
for (auto& mirror : pMonitor->mirrors) {
|
for (auto& mirror : pMonitor->mirrors) {
|
||||||
Vector2D scale = {mirror->vecSize.x / pMonitor->vecSize.x, mirror->vecSize.y / pMonitor->vecSize.y};
|
|
||||||
|
|
||||||
CRegion rg{pRegion};
|
// transform the damage here, so it won't get clipped by the monitor damage ring
|
||||||
wlr_region_scale_xy(rg.pixman(), rg.pixman(), scale.x, scale.y);
|
auto monitor = mirror;
|
||||||
pMonitor->addDamage(&rg);
|
auto mirrored = pMonitor;
|
||||||
|
|
||||||
|
CRegion transformed{pRegion};
|
||||||
|
|
||||||
|
// we want to transform to the same box as in CHyprOpenGLImpl::renderMirrored
|
||||||
|
double scale = std::min(monitor->vecTransformedSize.x / mirrored->vecTransformedSize.x, monitor->vecTransformedSize.y / mirrored->vecTransformedSize.y);
|
||||||
|
CBox monbox = {0, 0, mirrored->vecTransformedSize.x * scale, mirrored->vecTransformedSize.y * scale};
|
||||||
|
monbox.x = (monitor->vecTransformedSize.x - monbox.w) / 2;
|
||||||
|
monbox.y = (monitor->vecTransformedSize.y - monbox.h) / 2;
|
||||||
|
|
||||||
|
wlr_region_scale(transformed.pixman(), transformed.pixman(), scale);
|
||||||
|
transformed.transform(mirrored->transform, mirrored->vecPixelSize.x * scale, mirrored->vecPixelSize.y * scale);
|
||||||
|
transformed.translate(Vector2D(monbox.x, monbox.y));
|
||||||
|
|
||||||
|
mirror->addDamage(&transformed);
|
||||||
|
|
||||||
g_pCompositor->scheduleFrameForMonitor(mirror);
|
g_pCompositor->scheduleFrameForMonitor(mirror);
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue