diff --git a/src/Compositor.cpp b/src/Compositor.cpp index 7b5e03247..e414659c1 100644 --- a/src/Compositor.cpp +++ b/src/Compositor.cpp @@ -8,6 +8,7 @@ #include "helpers/Splashes.hpp" #include "config/ConfigValue.hpp" #include "config/ConfigWatcher.hpp" +#include "managers/SurfaceManager.hpp" #include "managers/CursorManager.hpp" #include "managers/TokenManager.hpp" #include "managers/PointerManager.hpp" @@ -661,6 +662,9 @@ void CCompositor::initManagers(eManagersInitStage stage) { Log::logger->log(Log::DEBUG, "Creating the SeatManager!"); g_pSeatManager = makeUnique(); + + Log::logger->log(Log::DEBUG, "Creating the CSurfaceManager!"); + g_pSurfaceManager = makeUnique(); } break; case STAGE_LATE: { Log::logger->log(Log::DEBUG, "Creating CHyprCtl"); diff --git a/src/helpers/Monitor.cpp b/src/helpers/Monitor.cpp index 37e119089..c539af9c9 100644 --- a/src/helpers/Monitor.cpp +++ b/src/helpers/Monitor.cpp @@ -26,6 +26,7 @@ #include "../managers/animation/AnimationManager.hpp" #include "../managers/animation/DesktopAnimationManager.hpp" #include "../managers/input/InputManager.hpp" +#include "../managers/SurfaceManager.hpp" #include "../hyprerror/HyprError.hpp" #include "../i18n/Engine.hpp" #include "sync/SyncTimeline.hpp" @@ -68,6 +69,9 @@ CMonitor::~CMonitor() { m_events.destroy.emit(); if (g_pHyprOpenGL) g_pHyprOpenGL->destroyMonitorResources(m_self); + + if (g_pSurfaceManager) + g_pSurfaceManager->destroy(m_self); } void CMonitor::onConnect(bool noRule) { @@ -114,10 +118,8 @@ void CMonitor::onConnect(bool noRule) { ts = nullptr; } - if (!ts) - PROTO::presentation->onPresented(m_self.lock(), Time::steadyNow(), event.refresh, event.seq, event.flags); - else - PROTO::presentation->onPresented(m_self.lock(), Time::fromTimespec(event.when), event.refresh, event.seq, event.flags); + auto now = ts ? Time::fromTimespec(event.when) : Time::steadyNow(); + PROTO::presentation->onPresented(m_self.lock(), now, event.refresh, event.seq, event.flags); if (m_zoomAnimFrameCounter < 5) { m_zoomAnimFrameCounter++; @@ -140,7 +142,7 @@ void CMonitor::onConnect(bool noRule) { }); } - m_frameScheduler->onPresented(); + m_frameScheduler->onPresented(now); m_events.presented.emit(); }); diff --git a/src/helpers/MonitorFrameScheduler.cpp b/src/helpers/MonitorFrameScheduler.cpp index 804eec1e5..0fdf5711b 100644 --- a/src/helpers/MonitorFrameScheduler.cpp +++ b/src/helpers/MonitorFrameScheduler.cpp @@ -3,6 +3,7 @@ #include "../Compositor.hpp" #include "../render/Renderer.hpp" #include "../managers/eventLoop/EventLoopManager.hpp" +#include "../managers/SurfaceManager.hpp" CMonitorFrameScheduler::CMonitorFrameScheduler(PHLMONITOR m) : m_monitor(m) { ; @@ -16,7 +17,7 @@ bool CMonitorFrameScheduler::newSchedulingEnabled() { void CMonitorFrameScheduler::onSyncFired() { - if (!newSchedulingEnabled()) + if (!newSchedulingEnabled() || m_skipThird) return; // Sync fired: reset submitted state, set as rendered. Check the last render time. If we are running @@ -25,21 +26,27 @@ void CMonitorFrameScheduler::onSyncFired() { if (std::chrono::duration_cast(hrc::now() - m_lastRenderBegun).count() / 1000.F < 1000.F / m_monitor->m_refreshRate) { // we are in. Frame is valid. We can just render as normal. Log::logger->log(Log::TRACE, "CMonitorFrameScheduler: {} -> onSyncFired, didn't miss.", m_monitor->m_name); + if (!m_renderAtFrame) + g_pCompositor->scheduleFrameForMonitor(m_monitor.lock()); + m_renderAtFrame = true; return; + } else if (m_renderAtFrame) { + m_renderAtFrame = false; + g_pCompositor->scheduleFrameForMonitor(m_monitor.lock()); + return; } - Log::logger->log(Log::TRACE, "CMonitorFrameScheduler: {} -> onSyncFired, missed.", m_monitor->m_name); - + Log::logger->log(Log::TRACE, "CMonitorFrameScheduler: {} -> onSyncFired, rendering third frame.", m_monitor->m_name); // we are out. The frame is taking too long to render. Begin rendering immediately, but don't commit yet. - m_pendingThird = true; - m_renderAtFrame = false; // block frame rendering, we already scheduled + m_pendingThird = true; m_lastRenderBegun = hrc::now(); // get a ref to ourselves. renderMonitor can destroy this scheduler if it decides to perform a monitor reload // FIXME: this is horrible. "renderMonitor" should not be able to do that. auto self = m_self; + g_pSurfaceManager->sendScheduledFrames(m_monitor, Time::steadyNow()); g_pHyprRenderer->renderMonitor(m_monitor.lock(), false); @@ -49,31 +56,28 @@ void CMonitorFrameScheduler::onSyncFired() { onFinishRender(); } -void CMonitorFrameScheduler::onPresented() { +void CMonitorFrameScheduler::onPresented(const Time::steady_tp& now) { + g_pSurfaceManager->sendScheduledFrames(m_monitor, now); + if (!newSchedulingEnabled()) return; + m_skipThird = false; + if (!m_pendingThird) return; Log::logger->log(Log::TRACE, "CMonitorFrameScheduler: {} -> onPresented, missed, committing pending.", m_monitor->m_name); - m_pendingThird = false; + auto mon = m_monitor.lock(); - Log::logger->log(Log::TRACE, "CMonitorFrameScheduler: {} -> onPresented, missed, committing pending at the earliest convenience.", m_monitor->m_name); + g_pHyprRenderer->commitPendingAndDoExplicitSync(mon, true); // commit the pending frame. If it didn't fire yet (is not rendered) it doesn't matter. Syncs will wait. + m_skipThird = true; - m_pendingThird = false; - - g_pEventLoopManager->doLater([m = m_monitor.lock()] { - if (!m) - return; - g_pHyprRenderer->commitPendingAndDoExplicitSync(m); // commit the pending frame. If it didn't fire yet (is not rendered) it doesn't matter. Syncs will wait. - - // schedule a frame: we might have some missed damage, which got cleared due to the above commit. - // TODO: this is not always necessary, but doesn't hurt in general. We likely won't hit this if nothing's happening anyways. - if (m->m_damage.hasChanged()) - g_pCompositor->scheduleFrameForMonitor(m); - }); + // schedule a frame: we might have some missed damage, which got cleared due to the above commit. + // TODO: this is not always necessary, but doesn't hurt in general. We likely won't hit this if nothing's happening anyways. + if (mon->m_damage.hasChanged()) + g_pCompositor->scheduleFrameForMonitor(mon); } void CMonitorFrameScheduler::onFrame() { @@ -100,20 +104,12 @@ void CMonitorFrameScheduler::onFrame() { return; } - if (!m_renderAtFrame) { - Log::logger->log(Log::TRACE, "CMonitorFrameScheduler: {} -> frame event, but m_renderAtFrame = false.", m_monitor->m_name); - return; - } - - Log::logger->log(Log::TRACE, "CMonitorFrameScheduler: {} -> frame event, render = true, rendering normally.", m_monitor->m_name); - m_lastRenderBegun = hrc::now(); - // get a ref to ourselves. renderMonitor can destroy this scheduler if it decides to perform a monitor reload // FIXME: this is horrible. "renderMonitor" should not be able to do that. auto self = m_self; - g_pHyprRenderer->renderMonitor(m_monitor.lock()); + g_pHyprRenderer->renderMonitor(m_monitor.lock(), true, (m_renderAtFrame && !m_pendingThird)); if (!self) return; @@ -122,8 +118,12 @@ void CMonitorFrameScheduler::onFrame() { } void CMonitorFrameScheduler::onFinishRender() { - m_sync = CEGLSync::create(); // this destroys the old sync - g_pEventLoopManager->doOnReadable(m_sync->fd().duplicate(), [this, self = m_self] { + if (!m_monitor->m_inFence.isValid()) { + Log::logger->log(Log::ERR, "CMonitorFrameScheduler: {} -> onFinishRender, m_inFence is not valid.", m_monitor->m_name); + return; + } + + g_pEventLoopManager->doOnReadable(m_monitor->m_inFence.duplicate(), [this, self = m_self] { if (!self) // might've gotten destroyed return; onSyncFired(); diff --git a/src/helpers/MonitorFrameScheduler.hpp b/src/helpers/MonitorFrameScheduler.hpp index c9b45a64a..35e0225a8 100644 --- a/src/helpers/MonitorFrameScheduler.hpp +++ b/src/helpers/MonitorFrameScheduler.hpp @@ -18,7 +18,7 @@ class CMonitorFrameScheduler { CMonitorFrameScheduler& operator=(CMonitorFrameScheduler&&) = delete; void onSyncFired(); - void onPresented(); + void onPresented(const Time::steady_tp& when); void onFrame(); private: @@ -28,12 +28,10 @@ class CMonitorFrameScheduler { bool m_renderAtFrame = true; bool m_pendingThird = false; + bool m_skipThird = false; hrc::time_point m_lastRenderBegun; PHLMONITORREF m_monitor; - - UP m_sync; - WP m_self; friend class CMonitor; diff --git a/src/managers/PointerManager.cpp b/src/managers/PointerManager.cpp index 1a915506b..acda92607 100644 --- a/src/managers/PointerManager.cpp +++ b/src/managers/PointerManager.cpp @@ -17,6 +17,7 @@ #include "../desktop/state/FocusState.hpp" #include "SeatManager.hpp" #include "../helpers/time/Time.hpp" +#include "../managers/SurfaceManager.hpp" #include #include #include @@ -160,7 +161,7 @@ void CPointerManager::setCursorSurface(SP surf, const if (surf->resource()->m_current.texture) { m_currentCursorImage.size = surf->resource()->m_current.bufferSize; - surf->resource()->frame(Time::steadyNow()); + g_pSurfaceManager->scheduleForFrame(Desktop::focusState()->monitor(), surf->resource()); } } @@ -597,7 +598,8 @@ void CPointerManager::renderSoftwareCursorsFor(PHLMONITOR pMonitor, const Time:: if (!state->hardwareFailed && state->softwareLocks == 0 && !forceRender) { if (m_currentCursorImage.surface) - m_currentCursorImage.surface->resource()->frame(now); + g_pSurfaceManager->scheduleForFrame(pMonitor, m_currentCursorImage.surface->resource()); + return; } @@ -632,7 +634,7 @@ void CPointerManager::renderSoftwareCursorsFor(PHLMONITOR pMonitor, const Time:: g_pHyprRenderer->m_renderPass.add(makeUnique(std::move(data))); if (m_currentCursorImage.surface) - m_currentCursorImage.surface->resource()->frame(now); + g_pSurfaceManager->scheduleForFrame(pMonitor, m_currentCursorImage.surface->resource()); } Vector2D CPointerManager::getCursorPosForMonitor(PHLMONITOR pMonitor) { diff --git a/src/managers/SurfaceManager.cpp b/src/managers/SurfaceManager.cpp new file mode 100644 index 000000000..ed7442c75 --- /dev/null +++ b/src/managers/SurfaceManager.cpp @@ -0,0 +1,127 @@ +#include "SurfaceManager.hpp" +#include "../helpers/Monitor.hpp" +#include "managers/eventLoop/EventLoopManager.hpp" +#include "render/OpenGL.hpp" + +#include + +using namespace Hyprutils::OS; + +#if defined(__linux__) +#include +#else +struct sync_merge_data { + char name[32]; + __s32 fd2; + __s32 fence; + __u32 flags; + __u32 pad; +}; +#define SYNC_IOC_MAGIC '>' +#define SYNC_IOC_MERGE _IOWR(SYNC_IOC_MAGIC, 3, struct sync_merge_data) +#endif + +static CFileDescriptor mergeSyncFds(const CFileDescriptor& fd1, const CFileDescriptor& fd2) { + // combines the fences of both sync_fds into a dma_fence_array (https://www.kernel.org/doc/html/latest/driver-api/dma-buf.html#c.dma_fence_array_create) + // with the signal_on_any param set to false, so the new sync_fd will "signal when all fences in the array signal." + + struct sync_merge_data data{ + .name = "merged release fence", + .fd2 = fd2.get(), + .fence = -1, + }; + int err = -1; + do { + err = ioctl(fd1.get(), SYNC_IOC_MERGE, &data); + } while (err == -1 && (errno == EINTR || errno == EAGAIN)); + if (err < 0) + return CFileDescriptor{}; + else + return CFileDescriptor(data.fence); +} + +bool CSurfaceManager::addBuffer(PHLMONITORREF monitor, const CHLBufferReference& buf) { + if (!monitor) + return false; + + auto it = std::ranges::find_if(m_buffers[monitor], [&buf](auto& b) { return b == buf; }); + + if (it != m_buffers[monitor].end()) + return false; + + m_buffers[monitor].emplace_back(buf); + return true; +} + +bool CSurfaceManager::addFrameCallback(WP surf, SP cb) { + auto it = std::ranges::find_if(m_frameCallbacks[surf], [&cb](auto& c) { return c == cb; }); + + if (it != m_frameCallbacks[surf].end()) + return false; + + m_frameCallbacks[surf].emplace_back(cb); + return true; +} + +void CSurfaceManager::addFence(PHLMONITORREF monitor) { + if (g_pHyprOpenGL->explicitSyncSupported() && monitor->m_inFence.isValid()) { + for (auto& b : m_buffers[monitor]) { + if (!b->m_syncReleasers.empty()) { + for (auto& releaser : b->m_syncReleasers) { + releaser->addSyncFileFd(monitor->m_inFence); + } + } else { + if (b->m_syncDropFd.isValid()) + b->m_syncDropFd = mergeSyncFds(b->m_syncDropFd, monitor->m_inFence.duplicate()); + else + b->m_syncDropFd = monitor->m_inFence.duplicate(); + } + } + } +} + +void CSurfaceManager::dropBuffers(PHLMONITORREF monitor) { + if (g_pHyprOpenGL->explicitSyncSupported()) { + std::erase_if(m_buffers[monitor], [](auto& b) { return !b->m_syncReleasers.empty(); }); + for (auto& b : m_buffers[monitor]) { + g_pEventLoopManager->doOnReadable(b->m_syncDropFd.duplicate(), [buf = b] mutable {}); + } + } + + m_buffers[monitor].clear(); + std::erase_if(m_buffers, [](auto& b) { return !b.first; }); // if monitor is gone. +} + +void CSurfaceManager::sendFrameCallbacks(WP surf, const Time::steady_tp& now) { + if (surf) { + for (auto& cb : m_frameCallbacks[surf]) { + cb->send(now); + } + + m_frameCallbacks.erase(surf); + } + + std::erase_if(m_frameCallbacks, [](auto& b) { return !b.first; }); // if surface is gone. +} + +void CSurfaceManager::scheduleForFrame(PHLMONITORREF monitor, WP surf) { + auto it = std::ranges::find_if(m_scheduledForFrame[monitor], [&surf](auto& b) { return b == surf; }); + + if (it != m_scheduledForFrame[monitor].end()) // already scheduled + return; + + m_scheduledForFrame[monitor].emplace_back(surf); +} + +void CSurfaceManager::sendScheduledFrames(PHLMONITORREF monitor, const Time::steady_tp& now) { + for (auto& f : m_scheduledForFrame[monitor]) { + sendFrameCallbacks(f, now); + } + + std::erase_if(m_scheduledForFrame, [](auto& b) { return !b.first; }); // if monitor is gone. +} + +void CSurfaceManager::destroy(PHLMONITORREF monitor) { + m_buffers.erase(monitor); + m_scheduledForFrame.erase(monitor); +} diff --git a/src/managers/SurfaceManager.hpp b/src/managers/SurfaceManager.hpp new file mode 100644 index 000000000..d63cafda1 --- /dev/null +++ b/src/managers/SurfaceManager.hpp @@ -0,0 +1,25 @@ +#pragma once + +#include "../defines.hpp" +#include "protocols/core/Compositor.hpp" +#include "protocols/types/Buffer.hpp" + +class CSurfaceManager { + public: + CSurfaceManager() = default; + bool addBuffer(PHLMONITORREF monitor, const CHLBufferReference& buf); + bool addFrameCallback(WP, SP); + void addFence(PHLMONITORREF monitor); + void dropBuffers(PHLMONITORREF monitor); + void sendFrameCallbacks(WP surf, const Time::steady_tp& now); + void scheduleForFrame(PHLMONITORREF monitor, WP surf); + void sendScheduledFrames(PHLMONITORREF monitor, const Time::steady_tp& now); + void destroy(PHLMONITORREF monitor); + + private: + std::unordered_map> m_buffers; + std::unordered_map, std::vector>> m_frameCallbacks; + std::unordered_map>> m_scheduledForFrame; +}; + +inline UP g_pSurfaceManager; diff --git a/src/protocols/core/Compositor.cpp b/src/protocols/core/Compositor.cpp index dc4931a8f..aff869e5a 100644 --- a/src/protocols/core/Compositor.cpp +++ b/src/protocols/core/Compositor.cpp @@ -14,6 +14,8 @@ #include "../../render/Renderer.hpp" #include "config/ConfigValue.hpp" #include "../../managers/eventLoop/EventLoopManager.hpp" +#include "../../managers/SurfaceManager.hpp" +#include "../../desktop/state/FocusState.hpp" #include "protocols/types/SurfaceRole.hpp" #include "render/Texture.hpp" #include @@ -225,10 +227,8 @@ CWLSurfaceResource::CWLSurfaceResource(SP resource_) : m_resource(re m_pending.opaque = RG->m_region; }); - m_resource->setFrame([this](CWlSurface* r, uint32_t id) { - m_pending.updated.bits.frame = true; - m_pending.callbacks.emplace_back(makeShared(makeShared(m_client, 1, id))); - }); + m_resource->setFrame( + [this](CWlSurface* r, uint32_t id) { g_pSurfaceManager->addFrameCallback(m_self, makeShared(makeShared(m_client, 1, id))); }); m_resource->setOffset([this](CWlSurface* r, int32_t x, int32_t y) { m_pending.updated.bits.offset = true; @@ -328,17 +328,6 @@ void CWLSurfaceResource::sendPreferredScale(int32_t scale) { m_resource->sendPreferredBufferScale(scale); } -void CWLSurfaceResource::frame(const Time::steady_tp& now) { - if (m_current.callbacks.empty()) - return; - - for (auto const& c : m_current.callbacks) { - c->send(now); - } - - m_current.callbacks.clear(); -} - void CWLSurfaceResource::resetRole() { m_role = makeShared(); } @@ -445,7 +434,7 @@ void CWLSurfaceResource::map() { m_mapped = true; - frame(Time::steadyNow()); + g_pSurfaceManager->scheduleForFrame(Desktop::focusState()->monitor(), m_self); m_current.bufferDamage = CBox{{}, m_current.bufferSize}; m_pending.bufferDamage = CBox{{}, m_pending.bufferSize}; @@ -653,7 +642,6 @@ void CWLSurfaceResource::updateCursorShm(CRegion damage) { } void CWLSurfaceResource::presentFeedback(const Time::steady_tp& when, PHLMONITOR pMonitor, bool discarded) { - frame(when); auto FEEDBACK = makeUnique(m_self.lock()); FEEDBACK->attachMonitor(pMonitor); if (discarded) @@ -661,6 +649,7 @@ void CWLSurfaceResource::presentFeedback(const Time::steady_tp& when, PHLMONITOR else FEEDBACK->presented(); PROTO::presentation->queueData(std::move(FEEDBACK)); + g_pSurfaceManager->scheduleForFrame(pMonitor, m_self); } CWLCompositorResource::CWLCompositorResource(SP resource_) : m_resource(resource_) { diff --git a/src/protocols/core/Compositor.hpp b/src/protocols/core/Compositor.hpp index 7b295aa71..ec3efd53f 100644 --- a/src/protocols/core/Compositor.hpp +++ b/src/protocols/core/Compositor.hpp @@ -82,7 +82,6 @@ class CWLSurfaceResource { void leave(PHLMONITOR monitor); void sendPreferredTransform(wl_output_transform t); void sendPreferredScale(int32_t scale); - void frame(const Time::steady_tp& now); uint32_t id(); void map(); void unmap(); diff --git a/src/protocols/core/DataDevice.cpp b/src/protocols/core/DataDevice.cpp index 41f072734..75475c265 100644 --- a/src/protocols/core/DataDevice.cpp +++ b/src/protocols/core/DataDevice.cpp @@ -15,6 +15,7 @@ #include "../../helpers/Monitor.hpp" #include "../../render/Renderer.hpp" #include "../../xwayland/Dnd.hpp" +#include "../../managers/SurfaceManager.hpp" using namespace Hyprutils::OS; CWLDataOfferResource::CWLDataOfferResource(SP resource_, SP source_) : m_source(source_), m_resource(resource_) { @@ -836,7 +837,7 @@ void CWLDataDeviceProtocol::renderDND(PHLMONITOR pMonitor, const Time::steady_tp CBox damageBox = CBox{surfacePos, m_dnd.dndSurface->m_current.size}.expand(5); g_pHyprRenderer->damageBox(damageBox); - m_dnd.dndSurface->frame(when); + g_pSurfaceManager->scheduleForFrame(pMonitor, m_dnd.dndSurface); } bool CWLDataDeviceProtocol::dndActive() { diff --git a/src/protocols/types/Buffer.hpp b/src/protocols/types/Buffer.hpp index f85670ef8..c93de8c7e 100644 --- a/src/protocols/types/Buffer.hpp +++ b/src/protocols/types/Buffer.hpp @@ -31,6 +31,7 @@ class IHLBuffer : public Aquamarine::IBuffer { SP m_resource; std::vector> m_syncReleasers; Hyprutils::OS::CFileDescriptor m_syncFd; + Hyprutils::OS::CFileDescriptor m_syncDropFd; struct { CHyprSignalListener backendRelease; diff --git a/src/protocols/types/SurfaceState.cpp b/src/protocols/types/SurfaceState.cpp index 96e8e769a..0bd291ffe 100644 --- a/src/protocols/types/SurfaceState.cpp +++ b/src/protocols/types/SurfaceState.cpp @@ -61,7 +61,6 @@ void SSurfaceState::reset() { damage.clear(); bufferDamage.clear(); - callbacks.clear(); lockMask = LOCK_REASON_NONE; } @@ -107,9 +106,4 @@ void SSurfaceState::updateFrom(SSurfaceState& ref) { if (ref.updated.bits.acked) ackedSize = ref.ackedSize; - - if (ref.updated.bits.frame) { - callbacks.insert(callbacks.end(), std::make_move_iterator(ref.callbacks.begin()), std::make_move_iterator(ref.callbacks.end())); - ref.callbacks.clear(); - } } diff --git a/src/protocols/types/SurfaceState.hpp b/src/protocols/types/SurfaceState.hpp index dd7679622..45ba3b5a7 100644 --- a/src/protocols/types/SurfaceState.hpp +++ b/src/protocols/types/SurfaceState.hpp @@ -47,7 +47,6 @@ struct SSurfaceState { bool viewport : 1; bool acquire : 1; bool acked : 1; - bool frame : 1; } bits; } updated; @@ -68,9 +67,6 @@ struct SSurfaceState { // for xdg_shell resizing Vector2D ackedSize; - // for wl_surface::frame callbacks. - std::vector> callbacks; - // viewporter protocol surface state struct { bool hasDestination = false; diff --git a/src/render/Renderer.cpp b/src/render/Renderer.cpp index 1ee65a0fb..89a38a9a9 100644 --- a/src/render/Renderer.cpp +++ b/src/render/Renderer.cpp @@ -16,6 +16,7 @@ #include "../desktop/view/LayerSurface.hpp" #include "../desktop/view/GlobalViewMethods.hpp" #include "../desktop/state/FocusState.hpp" +#include "../managers/SurfaceManager.hpp" #include "../protocols/SessionLock.hpp" #include "../protocols/LayerShell.hpp" #include "../protocols/XDGShell.hpp" @@ -170,9 +171,10 @@ CHyprRenderer::CHyprRenderer() { if (!w->wlSurface() || !w->wlSurface()->resource() || shouldRenderWindow(w.lock())) continue; - w->wlSurface()->resource()->frame(Time::steadyNow()); + g_pSurfaceManager->scheduleForFrame(Desktop::focusState()->monitor(), w->wlSurface()->resource()); auto FEEDBACK = makeUnique(w->wlSurface()->resource()); FEEDBACK->attachMonitor(Desktop::focusState()->monitor()); + FEEDBACK->discarded(); PROTO::presentation->queueData(std::move(FEEDBACK)); } @@ -1260,7 +1262,7 @@ void CHyprRenderer::calculateUVForSurface(PHLWINDOW pWindow, SPm_tearingState.busy = true; @@ -1555,7 +1557,7 @@ static hdr_output_metadata createHDRMetadata(SImageDescription settings, S }; } -bool CHyprRenderer::commitPendingAndDoExplicitSync(PHLMONITOR pMonitor) { +bool CHyprRenderer::commitPendingAndDoExplicitSync(PHLMONITOR pMonitor, bool dropBuffers) { static auto PCT = CConfigValue("render:send_content_type"); static auto PPASS = CConfigValue("render:cm_fs_passthrough"); static auto PAUTOHDR = CConfigValue("render:cm_auto_hdr"); @@ -1674,6 +1676,28 @@ bool CHyprRenderer::commitPendingAndDoExplicitSync(PHLMONITOR pMonitor) { pMonitor->m_previousFSWindow = FS_WINDOW; + if (!g_pHyprOpenGL->explicitSyncSupported()) { + static auto PNVIDIAANTIFLICKER = CConfigValue("opengl:nvidia_anti_flicker"); + Log::logger->log(Log::TRACE, "renderer: Explicit sync unsupported, falling back to implicit in endRender"); + + // nvidia doesn't have implicit sync, so we have to explicitly wait here, llvmpipe and other software renderer seems to bug out aswell. + if ((isNvidia() && *PNVIDIAANTIFLICKER) || isSoftware()) + glFinish(); + else + glFlush(); // mark an implicit sync point + + pMonitor->m_inFence.reset(); + } else { + if (pMonitor->m_inFence.isValid()) { + if (m_renderMode == RENDER_MODE_NORMAL) { + pMonitor->m_output->state->setExplicitInFence(pMonitor->m_inFence.get()); + } + } + } + + if (dropBuffers) + g_pSurfaceManager->dropBuffers(pMonitor); + bool ok = pMonitor->m_state.commit(); if (!ok) { if (pMonitor->m_inFence.isValid()) { @@ -1714,7 +1738,7 @@ void CHyprRenderer::sendFrameEventsToWorkspace(PHLMONITOR pMonitor, PHLWORKSPACE if (!view->aliveAndVisible()) continue; - view->wlSurface()->resource()->frame(now); + g_pSurfaceManager->scheduleForFrame(pMonitor, view->wlSurface()->resource()); } } @@ -2358,42 +2382,29 @@ void CHyprRenderer::endRender(const std::function& renderingDoneCallback else glFlush(); // mark an implicit sync point - m_usedAsyncBuffers.clear(); // release all buffer refs and hope implicit sync works if (renderingDoneCallback) renderingDoneCallback(); + } else { + UP eglSync = CEGLSync::create(); + if (eglSync && eglSync->isValid()) { + if (renderingDoneCallback) { + g_pEventLoopManager->doOnReadable(eglSync->fd().duplicate(), [renderingDoneCallback]() { + if (renderingDoneCallback) + renderingDoneCallback(); + }); + } - return; - } - - UP eglSync = CEGLSync::create(); - if (eglSync && eglSync->isValid()) { - for (auto const& buf : m_usedAsyncBuffers) { - for (const auto& releaser : buf->m_syncReleasers) { - releaser->addSyncFileFd(eglSync->fd()); + if (m_renderMode == RENDER_MODE_NORMAL) { + PMONITOR->m_inFence = eglSync->takeFd(); + g_pSurfaceManager->addFence(PMONITOR); + } + } else { + Log::logger->log(Log::ERR, "renderer: Explicit sync failed, calling renderingDoneCallback without sync"); + if (renderingDoneCallback) { + Log::logger->log(Log::ERR, "renderer: Explicit sync failed, calling renderingDoneCallback without sync"); + renderingDoneCallback(); } } - - // release buffer refs with release points now, since syncReleaser handles actual buffer release based on EGLSync - std::erase_if(m_usedAsyncBuffers, [](const auto& buf) { return !buf->m_syncReleasers.empty(); }); - - // release buffer refs without release points when EGLSync sync_file/fence is signalled - g_pEventLoopManager->doOnReadable(eglSync->fd().duplicate(), [renderingDoneCallback, prevbfs = std::move(m_usedAsyncBuffers)]() mutable { - prevbfs.clear(); - if (renderingDoneCallback) - renderingDoneCallback(); - }); - m_usedAsyncBuffers.clear(); - - if (m_renderMode == RENDER_MODE_NORMAL) { - PMONITOR->m_inFence = eglSync->takeFd(); - PMONITOR->m_output->state->setExplicitInFence(PMONITOR->m_inFence.get()); - } - } else { - Log::logger->log(Log::ERR, "renderer: Explicit sync failed, releasing resources"); - - m_usedAsyncBuffers.clear(); // release all buffer refs and hope implicit sync works - if (renderingDoneCallback) - renderingDoneCallback(); } } diff --git a/src/render/Renderer.hpp b/src/render/Renderer.hpp index f2377b3bf..7ad8a45d4 100644 --- a/src/render/Renderer.hpp +++ b/src/render/Renderer.hpp @@ -51,7 +51,7 @@ class CHyprRenderer { CHyprRenderer(); ~CHyprRenderer(); - void renderMonitor(PHLMONITOR pMonitor, bool commit = true); + void renderMonitor(PHLMONITOR pMonitor, bool commit = true, bool dropBuffers = true); void arrangeLayersForMonitor(const MONITORID&); void damageSurface(SP, double, double, double scale = 1.0); void damageWindow(PHLWINDOW, bool forceFull = false); @@ -94,18 +94,16 @@ class CHyprRenderer { bool m_bBlockSurfaceFeedback = false; bool m_bRenderingSnapshot = false; - PHLMONITORREF m_mostHzMonitor; - bool m_directScanoutBlocked = false; + PHLMONITORREF m_mostHzMonitor; + bool m_directScanoutBlocked = false; - void setSurfaceScanoutMode(SP surface, PHLMONITOR monitor); // nullptr monitor resets - void initiateManualCrash(); + void setSurfaceScanoutMode(SP surface, PHLMONITOR monitor); // nullptr monitor resets + void initiateManualCrash(); - bool m_crashingInProgress = false; - float m_crashingDistort = 0.5f; - wl_event_source* m_crashingLoop = nullptr; - wl_event_source* m_cursorTicker = nullptr; - - std::vector m_usedAsyncBuffers; + bool m_crashingInProgress = false; + float m_crashingDistort = 0.5f; + wl_event_source* m_crashingLoop = nullptr; + wl_event_source* m_cursorTicker = nullptr; struct { int hotspotX = 0; @@ -135,7 +133,7 @@ class CHyprRenderer { void renderSessionLockMissing(PHLMONITOR pMonitor); void renderBackground(PHLMONITOR pMonitor); - bool commitPendingAndDoExplicitSync(PHLMONITOR pMonitor); + bool commitPendingAndDoExplicitSync(PHLMONITOR pMonitor, bool dropBuffers); bool shouldBlur(PHLLS ls); bool shouldBlur(PHLWINDOW w); diff --git a/src/render/pass/SurfacePassElement.cpp b/src/render/pass/SurfacePassElement.cpp index d30272905..c647b6c08 100644 --- a/src/render/pass/SurfacePassElement.cpp +++ b/src/render/pass/SurfacePassElement.cpp @@ -5,7 +5,9 @@ #include "../../protocols/core/Compositor.hpp" #include "../../protocols/DRMSyncobj.hpp" #include "../../managers/input/InputManager.hpp" +#include "../../managers/SurfaceManager.hpp" #include "../Renderer.hpp" +#include "../../Compositor.hpp" #include #include @@ -154,8 +156,30 @@ void CSurfacePassElement::draw(const CRegion& damage) { // 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 (m_data.surface->m_current.buffer && !m_data.surface->m_current.buffer->isSynchronous()) - g_pHyprRenderer->m_usedAsyncBuffers.emplace_back(m_data.surface->m_current.buffer); + if (m_data.surface->m_current.buffer && !m_data.surface->m_current.buffer->isSynchronous()) { + for (auto& m : g_pCompositor->m_monitors) { + if (!m) + continue; + + if (m_data.pMonitor == m || (m_data.pWindow && m_data.pWindow->visibleOnMonitor(m))) { + g_pSurfaceManager->addBuffer(m, m_data.surface->m_current.buffer); + + if (m_data.pMonitor != m) { + CBox wbox = {m_data.pWindow->m_realPosition->value(), m_data.pWindow->m_realSize->value()}; + if (m_data.pWindow->m_isFloating) + wbox = m_data.pWindow->getFullWindowBoundingBox(); + + CBox monbox = {m->m_position, m->m_size}; + CBox damageBox = wbox.intersection(monbox); + + if (!damageBox.empty()) { + m->addDamage(damageBox); + g_pCompositor->scheduleFrameForMonitor(m, Aquamarine::IOutput::AQ_SCHEDULE_NEEDS_FRAME); + } + } + } + } + } g_pHyprOpenGL->blend(true); }