mirror of
https://github.com/hyprwm/Hyprland.git
synced 2024-08-16 16:50:23 +03:00
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:
parent
54a537f6de
commit
5f32ef4c04
@ -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);
|
||||
|
@ -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);
|
||||
|
@ -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};
|
||||
|
@ -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_) {
|
||||
|
@ -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();
|
||||
|
||||
|
@ -1253,16 +1253,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!");
|
||||
|
||||
@ -1287,6 +1288,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;
|
||||
@ -2742,20 +2750,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;
|
||||
}
|
||||
|
||||
@ -2764,6 +2776,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;
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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();
|
||||
|
Loading…
Reference in New Issue
Block a user