Explicit sync fixes (#6829)

* explicit sync fixes

* more logging

* reremove wlroots

* close explicit fds on rollback

* limit presentFeedback explicit sync to direct scanout mode only

* explicit sync for texture render

* cursor explicit sync initial

* common wait for explicit sync point code

* code style fixes
This commit is contained in:
UjinT34 2024-07-10 22:20:00 +03:00 committed by Vaxry
parent 4c905b1a8b
commit 805a054389
8 changed files with 104 additions and 53 deletions

View file

@ -162,13 +162,13 @@ bool CSyncTimeline::importFromSyncFileFD(uint64_t dst, int fd) {
if (drmSyncobjImportSyncFile(drmFD, syncHandle, fd)) {
Debug::log(ERR, "importFromSyncFileFD: drmSyncobjImportSyncFile failed");
drmSyncobjDestroy(drmFD, syncHandle);
return -1;
return false;
}
if (drmSyncobjTransfer(drmFD, handle, dst, syncHandle, 0, 0)) {
Debug::log(ERR, "importFromSyncFileFD: drmSyncobjTransfer failed");
drmSyncobjDestroy(drmFD, syncHandle);
return -1;
return false;
}
drmSyncobjDestroy(drmFD, syncHandle);

View file

@ -448,7 +448,7 @@ void CPointerManager::renderSoftwareCursorsFor(SP<CMonitor> pMonitor, timespec*
box.x = std::round(box.x);
box.y = std::round(box.y);
g_pHyprOpenGL->renderTextureWithDamage(texture, &box, &damage, 1.F);
g_pHyprOpenGL->renderTextureWithDamage(texture, &box, &damage, 1.F, 0, false, false, currentCursorImage.waitTimeline, currentCursorImage.waitPoint);
if (currentCursorImage.surface)
currentCursorImage.surface->resource()->frame(now);

View file

@ -147,6 +147,8 @@ class CPointerManager {
CHyprSignalListener destroySurface;
CHyprSignalListener commitSurface;
SP<CSyncTimeline> waitTimeline = nullptr;
uint64_t waitPoint = 0;
} currentCursorImage; // TODO: support various sizes per-output so we can have pixel-perfect cursors
Vector2D pointerPos = {0, 0};

View file

@ -447,21 +447,23 @@ void CWLSurfaceResource::commitPendingState() {
}
}
void CWLSurfaceResource::presentFeedback(timespec* when, CMonitor* pMonitor) {
void CWLSurfaceResource::presentFeedback(timespec* when, CMonitor* pMonitor, bool needsExplicitSync) {
frame(when);
auto FEEDBACK = makeShared<CQueuedPresentationData>(self.lock());
FEEDBACK->attachMonitor(pMonitor);
FEEDBACK->discarded();
PROTO::presentation->queueData(FEEDBACK);
if (!pMonitor || !pMonitor->outTimeline || !syncobj)
if (!pMonitor || !pMonitor->outTimeline || !syncobj || !needsExplicitSync)
return;
// attach explicit sync
g_pHyprRenderer->explicitPresented.emplace_back(self.lock());
if (syncobj->acquirePoint > pMonitor->lastWaitPoint)
if (syncobj->acquirePoint > pMonitor->lastWaitPoint) {
Debug::log(TRACE, "presentFeedback lastWaitPoint {} -> {}", pMonitor->lastWaitPoint, syncobj->acquirePoint);
pMonitor->lastWaitPoint = syncobj->acquirePoint;
}
}
CWLCompositorResource::CWLCompositorResource(SP<CWlCompositor> resource_) : resource(resource_) {

View file

@ -122,7 +122,7 @@ class CWLSurfaceResource {
void breadthfirst(std::function<void(SP<CWLSurfaceResource>, const Vector2D&, void*)> fn, void* data);
CRegion accumulateCurrentBufferDamage();
void presentFeedback(timespec* when, CMonitor* pMonitor);
void presentFeedback(timespec* when, CMonitor* pMonitor, bool needsExplicitSync = false);
void lockPendingState();
void unlockPendingState();

View file

@ -1255,16 +1255,17 @@ void CHyprOpenGLImpl::renderTexture(SP<CTexture> tex, CBox* pBox, float alpha, i
scissor((CBox*)nullptr);
}
void CHyprOpenGLImpl::renderTextureWithDamage(SP<CTexture> tex, CBox* pBox, CRegion* damage, float alpha, int round, bool discardActive, bool allowCustomUV) {
void CHyprOpenGLImpl::renderTextureWithDamage(SP<CTexture> tex, CBox* pBox, CRegion* damage, float alpha, int round, bool discardActive, bool allowCustomUV,
SP<CSyncTimeline> waitTimeline, uint64_t waitPoint) {
RASSERT(m_RenderData.pMonitor, "Tried to render texture without begin()!");
renderTextureInternalWithDamage(tex, pBox, alpha, damage, round, discardActive, false, allowCustomUV, true);
renderTextureInternalWithDamage(tex, pBox, alpha, damage, round, discardActive, false, allowCustomUV, true, waitTimeline, waitPoint);
scissor((CBox*)nullptr);
}
void CHyprOpenGLImpl::renderTextureInternalWithDamage(SP<CTexture> tex, CBox* pBox, float alpha, CRegion* damage, int round, bool discardActive, bool noAA, bool allowCustomUV,
bool allowDim) {
bool allowDim, SP<CSyncTimeline> waitTimeline, uint64_t waitPoint) {
RASSERT(m_RenderData.pMonitor, "Tried to render texture without begin()!");
RASSERT((tex->m_iTexID > 0), "Attempted to draw NULL texture!");
@ -1289,6 +1290,13 @@ void CHyprOpenGLImpl::renderTextureInternalWithDamage(SP<CTexture> tex, CBox* pB
float glMatrix[9];
matrixMultiply(glMatrix, m_RenderData.projection, matrix);
if (waitTimeline != nullptr) {
if (!waitForTimelinePoint(waitTimeline, waitPoint)) {
Debug::log(ERR, "renderTextureInternalWithDamage: failed to wait for explicit sync point {}", waitPoint);
return;
}
}
CShader* shader = nullptr;
bool usingFinalShader = false;
@ -2744,20 +2752,24 @@ std::vector<SDRMFormat> CHyprOpenGLImpl::getDRMFormats() {
SP<CEGLSync> CHyprOpenGLImpl::createEGLSync(int fenceFD) {
std::vector<EGLint> attribs;
int dupFd = fcntl(fenceFD, F_DUPFD_CLOEXEC, 0);
if (dupFd < 0) {
Debug::log(ERR, "createEGLSync: dup failed");
return nullptr;
}
int dupFd = -1;
if (fenceFD > 0) {
int dupFd = fcntl(fenceFD, F_DUPFD_CLOEXEC, 0);
if (dupFd < 0) {
Debug::log(ERR, "createEGLSync: dup failed");
return nullptr;
}
attribs.push_back(EGL_SYNC_NATIVE_FENCE_FD_ANDROID);
attribs.push_back(dupFd);
attribs.push_back(EGL_NONE);
attribs.push_back(EGL_SYNC_NATIVE_FENCE_FD_ANDROID);
attribs.push_back(dupFd);
attribs.push_back(EGL_NONE);
}
EGLSyncKHR sync = m_sProc.eglCreateSyncKHR(m_pEglDisplay, EGL_SYNC_NATIVE_FENCE_ANDROID, attribs.data());
if (sync == EGL_NO_SYNC_KHR) {
Debug::log(ERR, "eglCreateSyncKHR failed");
close(dupFd);
if (dupFd >= 0)
close(dupFd);
return nullptr;
}
@ -2766,6 +2778,28 @@ SP<CEGLSync> CHyprOpenGLImpl::createEGLSync(int fenceFD) {
return eglsync;
}
bool CHyprOpenGLImpl::waitForTimelinePoint(SP<CSyncTimeline> timeline, uint64_t point) {
int fd = timeline->exportAsSyncFileFD(point);
if (fd < 0) {
Debug::log(ERR, "waitForTimelinePoint: failed to get a fd from explicit timeline");
return false;
}
auto sync = g_pHyprOpenGL->createEGLSync(fd);
close(fd);
if (!sync) {
Debug::log(ERR, "waitForTimelinePoint: failed to get an eglsync from explicit timeline");
return false;
}
if (!sync->wait()) {
Debug::log(ERR, "waitForTimelinePoint: failed to wait on an eglsync from explicit timeline");
return false;
}
return true;
}
void SRenderModifData::applyToBox(CBox& box) {
if (!enabled)
return;

View file

@ -6,6 +6,8 @@
#include "../helpers/Timer.hpp"
#include "../helpers/math/Math.hpp"
#include "../helpers/Format.hpp"
#include "../helpers/sync/SyncTimeline.hpp"
#include <cstdint>
#include <list>
#include <unordered_map>
#include <map>
@ -152,7 +154,8 @@ class CHyprOpenGLImpl {
void renderRectWithBlur(CBox*, const CColor&, int round = 0, float blurA = 1.f, bool xray = false);
void renderRectWithDamage(CBox*, const CColor&, CRegion* damage, int round = 0);
void renderTexture(SP<CTexture>, CBox*, float a, int round = 0, bool discardActive = false, bool allowCustomUV = false);
void renderTextureWithDamage(SP<CTexture>, CBox*, CRegion* damage, float a, int round = 0, bool discardActive = false, bool allowCustomUV = false);
void renderTextureWithDamage(SP<CTexture>, CBox*, CRegion* damage, float a, int round = 0, bool discardActive = false, bool allowCustomUV = false,
SP<CSyncTimeline> waitTimeline = nullptr, uint64_t waitPoint = 0);
void renderTextureWithBlur(SP<CTexture>, CBox*, float a, SP<CWLSurfaceResource> pSurface, int round = 0, bool blockBlurOptimization = false, float blurA = 1.f);
void renderRoundedShadow(CBox*, int round, int range, const CColor& color, float a = 1.0);
void renderBorder(CBox*, const CGradientValueData&, int round, int borderSize, float a = 1.0, int outerRound = -1 /* use round */);
@ -203,6 +206,7 @@ class CHyprOpenGLImpl {
std::vector<SDRMFormat> getDRMFormats();
EGLImageKHR createEGLImage(const Aquamarine::SDMABUFAttrs& attrs);
SP<CEGLSync> createEGLSync(int fenceFD);
bool waitForTimelinePoint(SP<CSyncTimeline> timeline, uint64_t point);
SCurrentRenderData m_RenderData;
@ -284,7 +288,7 @@ class CHyprOpenGLImpl {
CFramebuffer* blurMainFramebufferWithDamage(float a, CRegion* damage);
void renderTextureInternalWithDamage(SP<CTexture>, CBox* pBox, float a, CRegion* damage, int round = 0, bool discardOpaque = false, bool noAA = false,
bool allowCustomUV = false, bool allowDim = false);
bool allowCustomUV = false, bool allowDim = false, SP<CSyncTimeline> = nullptr, uint64_t waitPoint = 0);
void renderTexturePrimitive(SP<CTexture> tex, CBox* pBox);
void renderSplash(cairo_t* const, cairo_surface_t* const, double offset, const Vector2D& size);

View file

@ -17,6 +17,7 @@
#include "../protocols/DRMSyncobj.hpp"
#include "../protocols/LinuxDMABUF.hpp"
#include "../helpers/sync/SyncTimeline.hpp"
#include "debug/Log.hpp"
extern "C" {
#include <xf86drm.h>
@ -114,21 +115,8 @@ static void renderSurface(SP<CWLSurfaceResource> surface, int x, int y, void* da
// explicit sync: wait for the timeline, if any
if (surface->syncobj && surface->syncobj->acquireTimeline) {
int fd = surface->syncobj->acquireTimeline->timeline->exportAsSyncFileFD(surface->syncobj->acquirePoint);
if (fd < 0) {
Debug::log(ERR, "Renderer: failed to get a fd from explicit timeline");
return;
}
auto sync = g_pHyprOpenGL->createEGLSync(fd);
close(fd);
if (!sync) {
Debug::log(ERR, "Renderer: failed to get an eglsync from explicit timeline");
return;
}
if (!sync->wait()) {
Debug::log(ERR, "Renderer: failed to wait on an eglsync from explicit timeline");
if (!g_pHyprOpenGL->waitForTimelinePoint(surface->syncobj->acquireTimeline->timeline, surface->syncobj->acquirePoint)) {
Debug::log(ERR, "Renderer: failed to wait for explicit timeline");
return;
}
}
@ -191,8 +179,10 @@ static void renderSurface(SP<CWLSurfaceResource> surface, int x, int y, void* da
}
if (windowBox.width <= 1 || windowBox.height <= 1) {
if (!g_pHyprRenderer->m_bBlockSurfaceFeedback)
if (!g_pHyprRenderer->m_bBlockSurfaceFeedback) {
Debug::log(TRACE, "presentFeedback for invisible surface");
surface->presentFeedback(RDATA->when, RDATA->pMonitor);
}
return; // invisible
}
@ -245,8 +235,10 @@ static void renderSurface(SP<CWLSurfaceResource> surface, int x, int y, void* da
g_pHyprOpenGL->renderTexture(TEXTURE, &windowBox, ALPHA, rounding, false, true);
}
if (!g_pHyprRenderer->m_bBlockSurfaceFeedback)
if (!g_pHyprRenderer->m_bBlockSurfaceFeedback) {
Debug::log(TRACE, "presentFeedback for visible surface");
surface->presentFeedback(RDATA->when, RDATA->pMonitor);
}
g_pHyprOpenGL->blend(true);
@ -1099,8 +1091,10 @@ bool CHyprRenderer::attemptDirectScanout(CMonitor* pMonitor) {
if (!pMonitor->mirrors.empty() || pMonitor->isMirror() || m_bDirectScanoutBlocked)
return false; // do not DS if this monitor is being mirrored. Will break the functionality.
if (g_pPointerManager->softwareLockedFor(pMonitor->self.lock()))
if (g_pPointerManager->softwareLockedFor(pMonitor->self.lock())) {
Debug::log(TRACE, "Direct scanout failed: soft locked / HW cursors failed");
return false;
}
const auto PCANDIDATE = pMonitor->solitaryClient.lock();
@ -1126,7 +1120,8 @@ bool CHyprRenderer::attemptDirectScanout(CMonitor* pMonitor) {
timespec now;
clock_gettime(CLOCK_MONOTONIC, &now);
PSURFACE->presentFeedback(&now, pMonitor);
Debug::log(TRACE, "presentFeedback for DS");
PSURFACE->presentFeedback(&now, pMonitor, true);
if (pMonitor->state.commit()) {
if (m_pLastScanout.expired()) {
@ -1458,16 +1453,27 @@ bool CHyprRenderer::commitPendingAndDoExplicitSync(CMonitor* pMonitor) {
// apply timelines for explicit sync
bool anyExplicit = !explicitPresented.empty();
if (anyExplicit) {
pMonitor->output->state->setExplicitInFence(pMonitor->inTimeline->exportAsSyncFileFD(pMonitor->lastWaitPoint));
Debug::log(TRACE, "Explicit sync presented begin");
auto inFence = pMonitor->inTimeline->exportAsSyncFileFD(pMonitor->lastWaitPoint);
if (inFence < 0)
Debug::log(ERR, "Export lastWaitPoint {} as sync explicitInFence failed", pMonitor->lastWaitPoint);
pMonitor->output->state->setExplicitInFence(inFence);
for (auto& e : explicitPresented) {
Debug::log(TRACE, "Explicit sync presented releasePoint {}", e->syncobj && e->syncobj->releaseTimeline ? e->syncobj->releasePoint : -1);
if (!e->syncobj || !e->syncobj->releaseTimeline)
continue;
e->syncobj->releaseTimeline->timeline->transfer(pMonitor->outTimeline, pMonitor->commitSeq, e->syncobj->releasePoint);
}
explicitPresented.clear();
pMonitor->output->state->setExplicitOutFence(pMonitor->outTimeline->exportAsSyncFileFD(pMonitor->commitSeq));
auto outFence = pMonitor->outTimeline->exportAsSyncFileFD(pMonitor->commitSeq);
if (outFence < 0)
Debug::log(ERR, "Export commitSeq {} as sync explicitOutFence failed", pMonitor->commitSeq);
pMonitor->output->state->setExplicitOutFence(outFence);
Debug::log(TRACE, "Explicit sync presented end");
}
pMonitor->lastWaitPoint = 0;
@ -1475,23 +1481,25 @@ bool CHyprRenderer::commitPendingAndDoExplicitSync(CMonitor* pMonitor) {
const auto COMMITTED_OUT = pMonitor->output->state->state().explicitOutFence;
const auto COMMITTED_IN = pMonitor->output->state->state().explicitInFence;
if (!pMonitor->state.commit()) {
bool commited = pMonitor->state.commit();
if (!commited) {
Debug::log(TRACE, "Monitor state commit failed");
// rollback the buffer to avoid writing to the front buffer that is being
// displayed
pMonitor->output->swapchain->rollback();
pMonitor->damage.damageEntire();
return false;
}
if (COMMITTED_IN >= 0)
close(COMMITTED_IN);
if (COMMITTED_OUT >= 0) {
pMonitor->outTimeline->importFromSyncFileFD(pMonitor->commitSeq, COMMITTED_OUT);
if (commited)
pMonitor->outTimeline->importFromSyncFileFD(pMonitor->commitSeq, COMMITTED_OUT);
close(COMMITTED_OUT);
}
return true;
return commited;
}
void CHyprRenderer::renderWorkspace(CMonitor* pMonitor, PHLWORKSPACE pWorkspace, timespec* now, const CBox& geometry) {
@ -2687,13 +2695,10 @@ void CHyprRenderer::endRender() {
if (m_eRenderMode == RENDER_MODE_FULL_FAKE)
return;
if (isNvidia() && *PNVIDIAANTIFLICKER)
glFinish();
if (m_eRenderMode == RENDER_MODE_NORMAL) {
PMONITOR->output->state->setBuffer(m_pCurrentBuffer);
if (PMONITOR->output->state->state().explicitOutFence >= 0) {
if (PMONITOR->inTimeline) {
auto sync = g_pHyprOpenGL->createEGLSync(-1);
if (!sync) {
m_pCurrentRenderbuffer->unbind();
@ -2713,7 +2718,7 @@ void CHyprRenderer::endRender() {
return;
}
bool ok = PMONITOR->outTimeline->importFromSyncFileFD(PMONITOR->commitSeq, dupedfd);
bool ok = PMONITOR->inTimeline->importFromSyncFileFD(PMONITOR->commitSeq, dupedfd);
close(dupedfd);
if (!ok) {
m_pCurrentRenderbuffer->unbind();
@ -2722,8 +2727,12 @@ void CHyprRenderer::endRender() {
Debug::log(ERR, "renderer: couldn't import from sync file fd in endRender");
return;
}
} else
glFlush();
} else {
if (isNvidia() && *PNVIDIAANTIFLICKER)
glFinish();
else
glFlush();
}
}
m_pCurrentRenderbuffer->unbind();