diff options
author | Virt <[email protected]> | 2024-04-24 17:29:41 +0200 |
---|---|---|
committer | GitHub <[email protected]> | 2024-04-24 16:29:41 +0100 |
commit | 9fe409800ba5466017be8dfa16a55ca9833a416e (patch) | |
tree | bf3974d131f0a387588e938c29704085b2b70132 /src | |
parent | 8aecd4f253a102a2afa4edd3e502106eb3626d86 (diff) | |
download | Hyprland-9fe409800ba5466017be8dfa16a55ca9833a416e.tar.gz Hyprland-9fe409800ba5466017be8dfa16a55ca9833a416e.zip |
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
Diffstat (limited to 'src')
-rw-r--r-- | src/config/ConfigManager.cpp | 4 | ||||
-rw-r--r-- | src/render/OpenGL.cpp | 52 | ||||
-rw-r--r-- | src/render/OpenGL.hpp | 4 | ||||
-rw-r--r-- | src/render/Renderer.cpp | 25 |
4 files changed, 64 insertions, 21 deletions
diff --git a/src/config/ConfigManager.cpp b/src/config/ConfigManager.cpp index dda56530..4b5bbbf7 100644 --- a/src/config/ConfigManager.cpp +++ b/src/config/ConfigManager.cpp @@ -826,6 +826,10 @@ void CConfigManager::postConfigReload(const Hyprlang::CParseResult& result) { // Force the compositor to fully re-render all monitors 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 diff --git a/src/render/OpenGL.cpp b/src/render/OpenGL.cpp index 981541b2..c4884245 100644 --- a/src/render/OpenGL.cpp +++ b/src/render/OpenGL.cpp @@ -152,6 +152,12 @@ bool CHyprOpenGLImpl::passRequiresIntrospection(CMonitor* pMonitor) { if (m_RenderData.mouseZoomFactor != 1.0 || g_pHyprRenderer->m_bCrashingInProgress) 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()) return true; @@ -336,14 +342,10 @@ void CHyprOpenGLImpl::end() { 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 if (m_bOffloadedFramebuffer) { m_RenderData.damage = m_RenderData.finalDamage; - - m_RenderData.outFB->bind(); + m_bEndFrame = true; 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; } - m_bEndFrame = true; m_bApplyFinalShader = !m_RenderData.blockScreenShader; if (m_RenderData.mouseZoomUseMouse) 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); 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); } -void CHyprOpenGLImpl::saveBufferForMirror() { +void CHyprOpenGLImpl::saveBufferForMirror(CBox* box) { 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.bind(); - CBox monbox = {0, 0, m_RenderData.pMonitor->vecPixelSize.x, m_RenderData.pMonitor->vecPixelSize.y}; - 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); @@ -1990,14 +1995,37 @@ void CHyprOpenGLImpl::saveBufferForMirror() { } 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) 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); + + // reset matrix for further drawing + monitor->updateMatrix(); } void CHyprOpenGLImpl::renderSplash(cairo_t* const CAIRO, cairo_surface_t* const CAIROSURFACE, double offsetY, const Vector2D& size) { diff --git a/src/render/OpenGL.hpp b/src/render/OpenGL.hpp index 08a1583d..32e2f1eb 100644 --- a/src/render/OpenGL.hpp +++ b/src/render/OpenGL.hpp @@ -67,7 +67,7 @@ struct SMonitorRenderData { CFramebuffer mirrorSwapFB; // etc CFramebuffer offMainFB; - CFramebuffer monitorMirrorFB; // used for mirroring outputs + CFramebuffer monitorMirrorFB; // used for mirroring outputs, does not contain artifacts like offloadFB CTexture stencilTex; @@ -172,7 +172,7 @@ class CHyprOpenGLImpl { bool preBlurQueued(); void preRender(CMonitor*); - void saveBufferForMirror(); + void saveBufferForMirror(CBox*); void renderMirrored(); void applyScreenShader(const std::string& path); diff --git a/src/render/Renderer.cpp b/src/render/Renderer.cpp index 4b1e7ace..36d4f9b6 100644 --- a/src/render/Renderer.cpp +++ b/src/render/Renderer.cpp @@ -1246,9 +1246,7 @@ void CHyprRenderer::renderMonitor(CMonitor* pMonitor) { } // 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 || - pMonitor->isMirror() /* why??? */) { - + if (*PDAMAGETRACKINGMODE == DAMAGE_TRACKING_NONE || *PDAMAGETRACKINGMODE == DAMAGE_TRACKING_MONITOR || pMonitor->forceFullFrames > 0 || damageBlinkCleanup > 0) { damage = {0, 0, (int)pMonitor->vecTransformedSize.x * 10, (int)pMonitor->vecTransformedSize.y * 10}; finalDamage = damage; } else { @@ -1838,11 +1836,24 @@ void CHyprRenderer::damageRegion(const CRegion& rg) { void CHyprRenderer::damageMirrorsWith(CMonitor* pMonitor, const CRegion& pRegion) { for (auto& mirror : pMonitor->mirrors) { - Vector2D scale = {mirror->vecSize.x / pMonitor->vecSize.x, mirror->vecSize.y / pMonitor->vecSize.y}; - CRegion rg{pRegion}; - wlr_region_scale_xy(rg.pixman(), rg.pixman(), scale.x, scale.y); - pMonitor->addDamage(&rg); + // transform the damage here, so it won't get clipped by the monitor damage ring + auto monitor = mirror; + 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); } |