compositor: merge ready states in the surface queue (#15504)

* compositor: merge ready states in the surface queue

each waiter was previously added to wl_event_loop. but the
wl_event_loop has no order guarantee, so it could mean fence
A, B, C sits in the queue ready but processes B first then C, then A,
and now we commit A -> B -> C, change waiters to return a WP to the
SReadableWaiter so we can easily track them, iterate in reverse order
check if readable. then later in tryprocess merge all ready states into
one commit. meaning A merges B and C, now we commit C.

fifo is untouched because its FIFO. this is only for fences and mailbox
rendering.

* fifo: with states merging we dont want empty fifo commits

with states merging, we really dont want empty fifo commits that early
returns, just makes it a 3 place if else guard for no gain. move it all
into the fifo protocol instead. now we only fifo lock if both setSet and
setWait is called. the client wants it locked for presentation.

however we can only lock it if previous fifo barrier has been satisfied.
otherwise its going to wait for a presentation that never comes meaning we
never commit the new state that waits for the rendererer to catch it in
the render pass.

* presentation: discard directly instead of queueing

discard presentation directly instead of queuing for a presentation
event that might never come.

* syncobj: remove a O(n) resource lookup

seen in profiling as a big cpu spender
use what other protocols uses for fromResource

* fifo: dont fifo lock synced subsurfaces

The constraint must be ignored if the surface is a subsurface in synchronized mode.

also use setBarrier for setting waitingonpresentation because. protocol
states this. so any client abusing this has only itself to blame.

when the content update containing "set_barrier" was made active at a latching deadline,
it will be active for at least one refresh cycle
This commit is contained in:
Tom Englund
2026-07-27 13:16:56 +02:00
committed by GitHub
parent 94fe1706eb
commit 6484f43742
14 changed files with 295 additions and 148 deletions
-1
View File
@@ -645,7 +645,6 @@ std::vector<SP<IValue>> Values::getConfigValues() {
MS<Bool>("debug:full_cm_proto", "claims support for all cm proto features (requires restart)", false),
MS<Bool>("debug:ds_handle_same_buffer", "Special case for DS with unmodified buffer", true),
MS<Bool>("debug:ds_handle_same_buffer_fifo", "Special case for DS with unmodified buffer unlocks fifo", true),
MS<Bool>("debug:fifo_pending_workaround", "Fifo workaround for empty pending list", false),
MS<Bool>("debug:render_solitary_wo_damage", "Render solitary window with empty damage", false),
MS<Bool>("debug:vfr", "controls the VFR status of Hyprland. Do not turn off unless debugging", true),
MS<Int>("debug:invalidate_fp16", "allow fp16 buffer invalidation.", 1, {.min = 0, .max = 2, .map = OptionMap{{"disable", 0}, {"enable", 1}, {"auto", 2}}}),
+4 -6
View File
@@ -64,22 +64,20 @@ std::optional<bool> CSyncTimeline::check(uint64_t point, uint32_t flags) {
return ret == 0;
}
bool CSyncTimeline::addWaiter(std::function<void()>&& waiter, uint64_t point, uint32_t flags) {
WP<SReadableWaiter> CSyncTimeline::addWaiter(std::function<void()>&& waiter, uint64_t point, uint32_t flags) {
auto eventFd = CFileDescriptor(eventfd(0, EFD_CLOEXEC));
if (!eventFd.isValid()) {
Log::logger->log(Log::ERR, "CSyncTimeline::addWaiter: failed to acquire an eventfd");
return false;
return {};
}
if (drmSyncobjEventfd(m_drmFD, m_handle, point, eventFd.get(), flags)) {
Log::logger->log(Log::ERR, "CSyncTimeline::addWaiter: drmSyncobjEventfd failed");
return false;
return {};
}
g_pEventLoopManager->doOnReadable(std::move(eventFd), std::move(waiter));
return true;
return g_pEventLoopManager->doOnReadable(std::move(eventFd), std::move(waiter));
}
CFileDescriptor CSyncTimeline::exportAsSyncFileFD(uint64_t src) {
+2 -1
View File
@@ -13,6 +13,7 @@
*/
struct wl_event_source;
struct SReadableWaiter;
class CSyncTimeline {
public:
@@ -25,7 +26,7 @@ class CSyncTimeline {
// std::nullopt on fail
std::optional<bool> check(uint64_t point, uint32_t flags);
bool addWaiter(std::function<void()>&& waiter, uint64_t point, uint32_t flags);
WP<SReadableWaiter> addWaiter(std::function<void()>&& waiter, uint64_t point, uint32_t flags);
Hyprutils::OS::CFileDescriptor exportAsSyncFileFD(uint64_t src);
bool importFromSyncFileFD(uint64_t dst, Hyprutils::OS::CFileDescriptor& fd);
bool transfer(SP<CSyncTimeline> from, uint64_t fromPoint, uint64_t toPoint);
+13 -3
View File
@@ -73,7 +73,7 @@ static int configWatcherWrite(int fd, uint32_t mask, void* data) {
}
static int handleWaiterFD(int fd, uint32_t mask, void* data) {
auto waiter = sc<CEventLoopManager::SReadableWaiter*>(data);
auto waiter = sc<SReadableWaiter*>(data);
if (!waiter) {
Log::logger->log(Log::ERR, "handleWaiterFD: failed casting waiter");
@@ -250,14 +250,24 @@ UP<SEventLoopDoLaterLock> CEventLoopManager::doLaterLock(const std::function<voi
return makeUnique<SEventLoopDoLaterLock>(doLater(fn));
}
void CEventLoopManager::doOnReadable(CFileDescriptor fd, std::function<void()>&& fn) {
WP<SReadableWaiter> CEventLoopManager::doOnReadable(CFileDescriptor fd, std::function<void()>&& fn) {
if (!fd.isValid() || fd.isReadable()) {
fn();
return;
return nullptr;
}
auto& waiter = m_readableWaiters.emplace_back(makeUnique<SReadableWaiter>(nullptr, std::move(fd), std::move(fn)));
waiter->source = wl_event_loop_add_fd(g_pEventLoopManager->m_wayland.loop, waiter->fd.get(), WL_EVENT_READABLE, ::handleWaiterFD, waiter.get());
return waiter;
}
void CEventLoopManager::removeReadableWaiter(const WP<SReadableWaiter>& waiter) {
if (!waiter)
return;
// erasing the owning UP runs ~SReadableWaiter, which removes the wl_event_source.
std::erase_if(m_readableWaiters, [&waiter](const UP<SReadableWaiter>& w) { return waiter == w; });
}
void CEventLoopManager::syncPollFDs() {
+27 -28
View File
@@ -21,6 +21,29 @@ struct SEventLoopDoLaterLock {
uint64_t seq = 0;
};
struct SReadableWaiter {
wl_event_source* source;
Hyprutils::OS::CFileDescriptor fd;
std::function<void()> fn;
SReadableWaiter(wl_event_source* src, Hyprutils::OS::CFileDescriptor f, std::function<void()> func) : source(src), fd(std::move(f)), fn(std::move(func)) {}
~SReadableWaiter() {
if (source) {
wl_event_source_remove(source);
source = nullptr;
}
}
// copy
SReadableWaiter(const SReadableWaiter&) = delete;
SReadableWaiter& operator=(const SReadableWaiter&) = delete;
// move
SReadableWaiter(SReadableWaiter&& other) noexcept = default;
SReadableWaiter& operator=(SReadableWaiter&& other) noexcept = default;
};
class CEventLoopManager {
public:
CEventLoopManager(wl_display* display, wl_event_loop* wlEventLoop);
@@ -49,34 +72,10 @@ class CEventLoopManager {
std::vector<std::pair<uint64_t, std::function<void()>>> fns;
};
struct SReadableWaiter {
wl_event_source* source;
Hyprutils::OS::CFileDescriptor fd;
std::function<void()> fn;
SReadableWaiter(wl_event_source* src, Hyprutils::OS::CFileDescriptor f, std::function<void()> func) : source(src), fd(std::move(f)), fn(std::move(func)) {}
~SReadableWaiter() {
if (source) {
wl_event_source_remove(source);
source = nullptr;
}
}
// copy
SReadableWaiter(const SReadableWaiter&) = delete;
SReadableWaiter& operator=(const SReadableWaiter&) = delete;
// move
SReadableWaiter(SReadableWaiter&& other) noexcept = default;
SReadableWaiter& operator=(SReadableWaiter&& other) noexcept = default;
};
// schedule function to when fd is readable (WL_EVENT_READABLE / POLLIN),
// takes ownership of fd
void doOnReadable(Hyprutils::OS::CFileDescriptor fd, std::function<void()>&& fn);
void onFdReadable(SReadableWaiter* waiter);
void onFdReadableFail(SReadableWaiter* waiter);
WP<SReadableWaiter> doOnReadable(Hyprutils::OS::CFileDescriptor fd, std::function<void()>&& fn);
void removeReadableWaiter(const WP<SReadableWaiter>& waiter);
void onFdReadable(SReadableWaiter* waiter);
void onFdReadableFail(SReadableWaiter* waiter);
private:
// Manages the event sources after AQ pollFDs change.
+6 -7
View File
@@ -27,7 +27,7 @@ UP<CSyncReleaser> CDRMSyncPointState::createSyncRelease() {
return makeUnique<CSyncReleaser>(m_timeline, m_point);
}
bool CDRMSyncPointState::addWaiter(std::function<void()>&& waiter) {
WP<SReadableWaiter> CDRMSyncPointState::addWaiter(std::function<void()>&& waiter) {
m_acquireCommitted = true;
return m_timeline->addWaiter(std::move(waiter), m_point, 0u);
}
@@ -134,12 +134,9 @@ CDRMSyncobjTimelineResource::CDRMSyncobjTimelineResource(UP<CWpLinuxDrmSyncobjTi
}
WP<CDRMSyncobjTimelineResource> CDRMSyncobjTimelineResource::fromResource(wl_resource* res) {
for (const auto& r : PROTO::sync->m_timelines) {
if (r && r->m_resource && r->m_resource->resource() == res)
return r;
}
return {};
auto resource = sc<CWpLinuxDrmSyncobjTimelineV1*>(wl_resource_get_user_data(res));
auto data = resource ? sc<CDRMSyncobjTimelineResource*>(resource->data()) : nullptr;
return data ? data->m_self : WP<CDRMSyncobjTimelineResource>{};
}
bool CDRMSyncobjTimelineResource::good() {
@@ -192,6 +189,8 @@ CDRMSyncobjManagerResource::CDRMSyncobjManagerResource(UP<CWpLinuxDrmSyncobjMana
return;
}
RESOURCE->m_self = RESOURCE;
LOGM(Log::DEBUG, "New linux_drm_timeline at {:x}", (uintptr_t)RESOURCE.get());
});
}
+3 -1
View File
@@ -10,6 +10,7 @@
class CWLSurfaceResource;
class CDRMSyncobjTimelineResource;
class CSyncTimeline;
struct SReadableWaiter;
class CDRMSyncPointState {
public:
@@ -20,7 +21,7 @@ class CDRMSyncPointState {
const uint64_t& point();
WP<CSyncTimeline> timeline();
Hyprutils::Memory::CUniquePointer<CSyncReleaser> createSyncRelease();
bool addWaiter(std::function<void()>&& waiter);
WP<SReadableWaiter> addWaiter(std::function<void()>&& waiter);
bool committed();
Hyprutils::OS::CFileDescriptor exportAsFD();
void signal();
@@ -63,6 +64,7 @@ class CDRMSyncobjTimelineResource {
bool good();
WP<CDRMSyncobjTimelineResource> m_self;
Hyprutils::OS::CFileDescriptor m_fd;
SP<CSyncTimeline> m_timeline;
+76 -53
View File
@@ -1,6 +1,7 @@
#include "Fifo.hpp"
#include "Compositor.hpp"
#include "core/Compositor.hpp"
#include "core/Subcompositor.hpp"
#include "../output/Monitor.hpp"
#include "../event/EventBus.hpp"
#include "../state/MonitorState.hpp"
@@ -9,6 +10,28 @@
#include <algorithm>
#include <hyprutils/memory/WeakPtr.hpp>
// what nvidia says about the empty extra barrier commit.
/*
* If the window is not visible (occluded, monitor on standby,
* etc), then we could be waiting for an indefinite amount of time
* for the compositor to send a wp_presentation_feedback::presented
* or discarded event.
*
* But, wp_fifo_v1 is required to unblock in finite time, so we can
* send an extra dummy commit with a wp_fifo_v1::wait_barrier.
*
* If the window is visible, then the compositor will send a
* presented event as normal, and if the window is not visible,
* then the second commit will trigger a discarded event.
*
* Note that the compositor may trigger a discarded event
* immediately, so we use wp_commit_timer_v1 above to try to
* throttle things to a sane rate.
*
* Ugly as this is, Mesa relies on the same behavior, so it's
* probably safe to treat this as the "intended" behavior.
*/
CFifoResource::CFifoResource(UP<CWpFifoV1>&& resource_, SP<CWLSurfaceResource> surface) : m_resource(std::move(resource_)), m_surface(surface) {
if UNLIKELY (!m_resource->resource())
return;
@@ -23,8 +46,7 @@ CFifoResource::CFifoResource(UP<CWpFifoV1>&& resource_, SP<CWLSurfaceResource> s
return;
}
m_surface->m_pending.barrierSet = true;
m_surface->m_pending.updated.bits.fifo = true;
m_surface->m_pending.barrierSet = true;
});
m_resource->setWaitBarrier([this](CWpFifoV1* r) {
@@ -33,59 +55,61 @@ CFifoResource::CFifoResource(UP<CWpFifoV1>&& resource_, SP<CWLSurfaceResource> s
return;
}
if (!m_surface->m_current.barrierSet) {
// that might mean an empty commit with a barrier_set alone
static const auto PPEND = CConfigValue<Config::INTEGER>("debug:fifo_pending_workaround");
if (!m_surface->m_pending.fifoScheduled)
m_surface->m_pending.fifoScheduled = checkMonitors(*PPEND);
return;
}
m_surface->m_pending.surfaceLocked = true;
m_surface->m_pending.barrierWait = true;
});
m_listeners.surfaceStateCommit = m_surface->m_events.stateCommit.listen([this](auto state) {
if (!state || !state->surfaceLocked)
if (!state)
return;
static const auto PPEND = CConfigValue<Config::INTEGER>("debug:fifo_pending_workaround");
static const auto PINVIS = CConfigValue<Hyprlang::INT>("render:not_shown_fifo_lock");
//#TODO:
// this feels wrong, but if we have no pending frames, presented might never come because
// we are waiting on the barrier to unlock and no damage is around.
// unlock on timeout instead?
if (!state->fifoScheduled)
state->fifoScheduled = checkMonitors(*PPEND);
if (!state->fifoScheduled)
if (!state->barrierSet && !state->barrierWait)
return;
// only lock once its mapped and visible
if (m_surface->m_mapped) {
bool shouldLock = *PINVIS == 0 || !m_surface->m_hlSurface; // always && unknown
if (!shouldLock && m_surface->m_hlSurface) {
const auto& view = m_surface->m_hlSurface->view();
if (view) {
const auto& window = view->type() == Desktop::View::VIEW_TYPE_WINDOW ? dynamicPointerCast<Desktop::View::CWindow>(view) : nullptr;
const bool isVisible = (view && view->visible() && //
(!window || std::ranges::any_of(State::monitorState()->monitors(), [window](const auto& mon) {
return g_pHyprRenderer->shouldRenderWindow(window, mon);
})));
if (isVisible)
shouldLock = true;
else if (*PINVIS == 2) // never
shouldLock = false;
else if (window && window->m_ruleApplicator->renderUnfocused().valueOr(false))
shouldLock = false; // ignore render_unfocused
else
shouldLock = true;
} else
shouldLock = true;
// the barrier constraint must be ignored for a subsurface in synchronized mode.
if (m_surface->m_role->role() == SURFACE_ROLE_SUBSURFACE) {
const auto sub = dynamicPointerCast<CSubsurfaceRole>(m_surface->m_role);
if (sub) {
const auto subsurface = sub->m_subsurface.lock();
if (subsurface && subsurface->m_sync)
return;
}
if (shouldLock)
}
// check if entered outputs yet, and they are not tearing.
if (!checkMonitors())
return;
// only lock once its mapped and visible and actually has something waiting for a presentation.
if (m_surface->m_mapped && m_surface->m_current.waitingOnPresentation) {
bool shouldLock = false;
if (state->barrierSet && state->barrierWait) {
static const auto PINVIS = CConfigValue<Hyprlang::INT>("render:not_shown_fifo_lock");
shouldLock = *PINVIS == 0 || !m_surface->m_hlSurface; // always && unknown
if (!shouldLock && m_surface->m_hlSurface) {
const auto& view = m_surface->m_hlSurface->view();
if (view) {
const auto& window = view->type() == Desktop::View::VIEW_TYPE_WINDOW ? dynamicPointerCast<Desktop::View::CWindow>(view) : nullptr;
const bool isVisible = (view && view->visible() && //
(!window || std::ranges::any_of(State::monitorState()->monitors(), [window](const auto& mon) {
return g_pHyprRenderer->shouldRenderWindow(window, mon);
})));
if (isVisible)
shouldLock = true;
else if (*PINVIS == 2) // never
shouldLock = false;
else if (window && window->m_ruleApplicator->renderUnfocused().valueOr(false))
shouldLock = false; // ignore render_unfocused
else
shouldLock = true;
} else
shouldLock = true;
}
}
if (shouldLock) {
state->updated.bits.fifo = true;
m_surface->m_stateQueue.lock(state, LOCK_REASON_FIFO);
}
}
});
}
@@ -99,11 +123,12 @@ bool CFifoResource::good() {
}
void CFifoResource::presented() {
m_surface->m_current.barrierSet = false;
m_surface->m_current.waitingOnPresentation = false;
m_surface->m_stateQueue.unlockFirst(LOCK_REASON_FIFO);
}
bool CFifoResource::checkMonitors(bool needsSchedule) {
bool CFifoResource::checkMonitors() {
bool allowFifo = false;
if (m_surface->m_enteredOutputs.empty() && m_surface->m_hlSurface) {
for (auto& m : State::monitorState()->monitors()) {
if (!m || !m->m_enabled)
@@ -114,8 +139,7 @@ bool CFifoResource::checkMonitors(bool needsSchedule) {
if (m->m_tearingState.activelyTearing)
return false; // dont fifo lock on tearing.
if (needsSchedule)
m->scheduleFrame(Aquamarine::IOutput::AQ_SCHEDULE_NEEDS_FRAME);
allowFifo = true; // intersects.
}
}
} else {
@@ -126,12 +150,11 @@ bool CFifoResource::checkMonitors(bool needsSchedule) {
if (m->m_tearingState.activelyTearing)
return false; // dont fifo lock on tearing.
if (needsSchedule)
m->scheduleFrame(Aquamarine::IOutput::AQ_SCHEDULE_NEEDS_FRAME);
allowFifo = true;
}
}
return true;
return allowFifo;
}
CFifoManagerResource::CFifoManagerResource(UP<CWpFifoManagerV1>&& resource_) : m_resource(std::move(resource_)) {
+1 -1
View File
@@ -26,7 +26,7 @@ class CFifoResource {
} m_listeners;
void presented();
bool checkMonitors(bool needsSchedule = false);
bool checkMonitors();
friend class CFifoProtocol;
friend class CFifoManagerResource;
+27 -29
View File
@@ -591,22 +591,25 @@ CBox CWLSurfaceResource::extends() {
}
void CWLSurfaceResource::scheduleState(WP<SSurfaceState> state) {
auto whenReadable = [this, surf = m_self](auto state, auto reason) {
auto whenReadable = [this, surf = m_self](WP<SSurfaceState> state) {
if (!surf || !state)
return;
m_stateQueue.unlock(state, reason);
m_stateQueue.unlockFence(state);
};
if (state->updated.bits.acquire) {
// wait on acquire point for this surface, from explicit sync protocol
if (!state->acquire.addWaiter([state, whenReadable]() { whenReadable(state, LOCK_REASON_FENCE); })) {
Log::logger->log(Log::ERR, "Failed to addWaiter in CWLSurfaceResource::scheduleState");
whenReadable(state, LOCK_REASON_FENCE);
auto waiter = state->acquire.addWaiter([state, whenReadable]() { whenReadable(state); });
// the waiter may have fired (and dropped this state), so re check.
if (state) {
state->acquireWaiter = waiter;
// a null waiter means it either fired immediately or failed to register
if (!waiter)
whenReadable(state);
}
} else if (state->buffer && state->buffer->isSynchronous()) {
// synchronous (shm) buffers can be read immediately
m_stateQueue.unlock(state, LOCK_REASON_FENCE);
m_stateQueue.unlockFence(state);
} else if (state->buffer && !state->buffer->m_syncFds.empty()) {
// async buffer and is dmabuf, then we can wait on implicit fences
drainSyncFds(state, LOCK_REASON_FENCE);
@@ -624,32 +627,25 @@ void CWLSurfaceResource::drainSyncFds(WP<SSurfaceState> state, eLockReason reaso
if (!fds.empty()) {
auto fd = std::move(fds.front());
fds.erase(fds.begin());
g_pEventLoopManager->doOnReadable(std::move(fd), [this, surf = m_self, state, reason]() {
auto waiter = g_pEventLoopManager->doOnReadable(std::move(fd), [this, surf = m_self, state, reason]() {
if (!surf || !state)
return;
drainSyncFds(state, reason);
});
if (state)
state->acquireWaiter = waiter;
return;
}
m_stateQueue.unlock(state, reason);
m_stateQueue.unlockFence(state);
}
void CWLSurfaceResource::commitState(SSurfaceState& state) {
// TODO might be incorrect. needed for VRR with FIFO to avoid same buffer extra frames for second commit when it's used in this way:
// wp_fifo_v1#43.set_barrier()
// wp_fifo_v1#43.wait_barrier()
// wl_surface#3.commit()
// wp_fifo_v1#43.wait_barrier()
// wl_surface#3.commit()
if (!state.updated.all && m_mapped && state.fifoScheduled)
if (!state.updated.all && m_mapped)
return;
// only a new buffer supersedes the current, not yet presented content.
if (state.updated.bits.buffer)
PROTO::presentation->discardFeedbacks(m_current.presentationFeedbacks);
auto lastTexture = m_current.texture;
m_current.updateFrom(state);
@@ -824,17 +820,19 @@ void CWLSurfaceResource::presentFeedback(const Time::steady_tp& when, PHLMONITOR
if (m_current.presentationFeedbacks.empty())
return;
// discarded content will never be scanned out, so there is no present event coming.
if (discarded) {
PROTO::presentation->discardFeedbacks(m_current.presentationFeedbacks);
return;
}
auto FEEDBACK = makeUnique<CQueuedPresentationData>(m_self.lock(), std::move(m_current.presentationFeedbacks));
FEEDBACK->attachMonitor(pMonitor);
if (discarded)
FEEDBACK->discarded();
else {
FEEDBACK->presented();
if (!pMonitor->m_lastScanout.expired()) {
const auto WINDOW = m_hlSurface ? Desktop::View::CWindow::fromView(m_hlSurface->view()) : nullptr;
if (WINDOW == pMonitor->m_lastScanout)
FEEDBACK->setPresentationType(true);
}
FEEDBACK->presented();
if (!pMonitor->m_lastScanout.expired()) {
const auto WINDOW = m_hlSurface ? Desktop::View::CWindow::fromView(m_hlSurface->view()) : nullptr;
if (WINDOW == pMonitor->m_lastScanout)
FEEDBACK->setPresentationType(true);
}
PROTO::presentation->queueData(std::move(FEEDBACK));
}
+89 -4
View File
@@ -1,6 +1,8 @@
#include "SurfaceState.hpp"
#include "helpers/Format.hpp"
#include "protocols/types/Buffer.hpp"
#include "protocols/PresentationTime.hpp"
#include "managers/eventLoop/EventLoopManager.hpp"
#include "render/Renderer.hpp"
#include "render/Texture.hpp"
@@ -76,19 +78,102 @@ void SSurfaceState::reset() {
presentationFeedbacks.clear();
lockMask = LOCK_REASON_NONE;
barrierSet = false;
surfaceLocked = false;
fifoScheduled = false;
barrierSet = false;
barrierWait = false;
waitingOnPresentation = false;
pendingTimeout.reset();
commitTimingTarget.reset();
timer.reset(); // CEventLoopManager::nudgeTimers should handle it eventually
}
bool SSurfaceState::isLocked() const {
return lockMask != LOCK_REASON_NONE;
}
bool SSurfaceState::fenceSignaled() const {
if (buffer) {
for (const auto& fd : buffer->m_syncFds) {
if (!fd.isReadable())
return false;
}
}
if (acquireWaiter && !acquireWaiter->fd.isReadable())
return false;
return true;
}
void SSurfaceState::cancelFenceWaiter() {
if (acquireWaiter && g_pEventLoopManager)
g_pEventLoopManager->removeReadableWaiter(acquireWaiter);
acquireWaiter.reset();
}
void SSurfaceState::mergeFrom(SSurfaceState& ref) {
updated.all |= ref.updated.all;
if (ref.updated.bits.buffer) {
if (!presentationFeedbacks.empty())
PROTO::presentation->discardFeedbacks(presentationFeedbacks);
buffer = ref.buffer;
texture = ref.texture;
size = ref.size;
bufferSize = ref.bufferSize;
}
if (ref.updated.bits.damage) {
damage.add(ref.damage);
bufferDamage.add(ref.bufferDamage);
}
if (ref.updated.bits.input) {
input = ref.input;
inputIsInfinite = ref.inputIsInfinite;
}
if (ref.updated.bits.opaque)
opaque = ref.opaque;
if (ref.updated.bits.offset)
offset = ref.offset;
if (ref.updated.bits.scale)
scale = ref.scale;
if (ref.updated.bits.transform)
transform = ref.transform;
if (ref.updated.bits.viewport)
viewport = ref.viewport;
if (ref.updated.bits.acquire)
acquire = ref.acquire;
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();
}
if (ref.updated.bits.presentation) {
presentationFeedbacks.insert(presentationFeedbacks.end(), std::make_move_iterator(ref.presentationFeedbacks.begin()),
std::make_move_iterator(ref.presentationFeedbacks.end()));
ref.presentationFeedbacks.clear();
}
}
void SSurfaceState::updateFrom(SSurfaceState& ref) {
updated = ref.updated;
if (ref.updated.bits.buffer) {
if (!presentationFeedbacks.empty())
PROTO::presentation->discardFeedbacks(presentationFeedbacks);
buffer = ref.buffer;
texture = ref.texture;
size = ref.size;
@@ -142,5 +227,5 @@ void SSurfaceState::updateFrom(SSurfaceState& ref) {
}
if (ref.barrierSet)
barrierSet = ref.barrierSet;
waitingOnPresentation = true;
}
+12 -5
View File
@@ -12,6 +12,7 @@ namespace Render {
class CDRMSyncPointState;
class CWLCallbackResource;
class CPresentationFeedback;
struct SReadableWaiter;
enum eLockReason : uint8_t {
LOCK_REASON_NONE = 0,
@@ -92,17 +93,18 @@ struct SSurfaceState {
Vector2D sourceSize();
// drm syncobj protocol surface state
CDRMSyncPointState acquire;
eLockReason lockMask = LOCK_REASON_NONE;
CDRMSyncPointState acquire;
WP<SReadableWaiter> acquireWaiter;
eLockReason lockMask = LOCK_REASON_NONE;
// texture of surface content, used for rendering
SP<Render::ITexture> texture;
void updateSynchronousTexture(SP<Render::ITexture> lastTexture);
// fifo
bool barrierSet = false;
bool surfaceLocked = false;
bool fifoScheduled = false;
bool barrierSet = false;
bool barrierWait = false;
bool waitingOnPresentation = false;
// commit timing
std::optional<Time::steady_dur> pendingTimeout;
@@ -114,4 +116,9 @@ struct SSurfaceState {
CRegion effectiveInputRegion() const; // materializes the input region clipped to the current surface size
void updateFrom(SSurfaceState& ref); // updates this state based on a reference state.
void reset(); // resets pending state after commit
bool isLocked() const;
bool fenceSignaled() const;
void mergeFrom(SSurfaceState& ref);
void cancelFenceWaiter();
};
+34 -9
View File
@@ -6,8 +6,10 @@
CSurfaceStateQueue::CSurfaceStateQueue(WP<CWLSurfaceResource> surf) : m_surface(std::move(surf)) {}
void CSurfaceStateQueue::clear() {
for (auto& state : m_queue)
for (const auto& state : m_queue) {
state->cancelFenceWaiter();
PROTO::presentation->discardFeedbacks(state->presentationFeedbacks);
}
m_queue.clear();
}
@@ -17,10 +19,11 @@ WP<SSurfaceState> CSurfaceStateQueue::enqueue(UP<SSurfaceState>&& state) {
}
void CSurfaceStateQueue::dropState(const WP<SSurfaceState>& state) {
auto it = find(state);
const auto& it = find(state);
if (it == m_queue.end())
return;
(*it)->cancelFenceWaiter();
PROTO::presentation->discardFeedbacks((*it)->presentationFeedbacks);
m_queue.erase(it);
@@ -28,7 +31,7 @@ void CSurfaceStateQueue::dropState(const WP<SSurfaceState>& state) {
void CSurfaceStateQueue::lock(const WP<SSurfaceState>& weakState, eLockReason reason) {
ASSERT(reason != LOCK_REASON_NONE);
auto it = find(weakState);
const auto& it = find(weakState);
if (it == m_queue.end())
return;
@@ -37,7 +40,7 @@ void CSurfaceStateQueue::lock(const WP<SSurfaceState>& weakState, eLockReason re
void CSurfaceStateQueue::unlock(const WP<SSurfaceState>& state, eLockReason reason) {
ASSERT(reason != LOCK_REASON_NONE);
auto it = find(state);
const auto& it = find(state);
if (it == m_queue.end())
return;
@@ -45,9 +48,25 @@ void CSurfaceStateQueue::unlock(const WP<SSurfaceState>& state, eLockReason reas
tryProcess();
}
void CSurfaceStateQueue::unlockFence(const WP<SSurfaceState>& state) {
auto it = find(state);
if (it == m_queue.end())
return;
for (const auto& s : m_queue) {
if (!s->fenceSignaled())
continue;
s->lockMask &= ~LOCK_REASON_FENCE;
s->cancelFenceWaiter();
}
tryProcess();
}
void CSurfaceStateQueue::unlockFirst(eLockReason reason) {
ASSERT(reason != LOCK_REASON_NONE);
for (auto& it : m_queue) {
for (const auto& it : m_queue) {
if ((it->lockMask & reason) != LOCK_REASON_NONE) {
it->lockMask &= ~reason;
break;
@@ -74,11 +93,17 @@ auto CSurfaceStateQueue::find(const WP<SSurfaceState>& state) -> std::deque<UP<S
void CSurfaceStateQueue::tryProcess() {
while (!m_queue.empty()) {
auto& front = m_queue.front();
if (front->lockMask & LOCK_REASON_FIFO && !m_surface->m_current.barrierSet)
front->lockMask &= ~LOCK_REASON_FIFO;
if (front->lockMask != LOCK_REASON_NONE)
return;
if (front->isLocked())
break;
auto next = std::next(m_queue.begin());
if (next != m_queue.end() && !(*next)->isLocked()) {
front->mergeFrom(**next);
(*next)->cancelFenceWaiter();
m_queue.erase(next);
continue;
}
m_surface->commitState(*front);
m_queue.pop_front();
@@ -16,6 +16,7 @@ class CSurfaceStateQueue {
void dropState(const WP<SSurfaceState>& state);
void lock(const WP<SSurfaceState>& state, eLockReason reason);
void unlock(const WP<SSurfaceState>& state, eLockReason reason);
void unlockFence(const WP<SSurfaceState>& state);
void unlockFirst(eLockReason reason);
void tryProcess();