Skip to content

Commit e91ea02

Browse files
committed
fix: waylib: back off failed Vulkan render imports under load
Avoid treating failed Vulkan render attempts as completed frames when the Vulkan+GL bridge is under pressure. If acquiring the output render target fails, skip the current frame without committing or clearing the dirty state. If output commit fails after rendering a buffer, mark the output dirty again and retry with a short frame-based backoff. Also validate GL texture creation for Vulkan dmabuf imports and readback fallbacks before handing them to Qt RHI, preventing invalid texture IDs from reaching QQuickRenderTarget. Surface texture updates now keep the previous texture and briefly back off Vulkan-only retries when imports fail, avoiding tight retry loops during heavy CPU/GPU pressure. The GLES2 path is still left unchanged.
1 parent e1c334d commit e91ea02

3 files changed

Lines changed: 155 additions & 4 deletions

File tree

waylib/src/server/qtquick/woutputrenderwindow.cpp

Lines changed: 69 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -204,6 +204,42 @@ class Q_DECL_HIDDEN OutputHelper : public WOutputHelper
204204
return output()->output()->handle();
205205
}
206206

207+
#ifdef ENABLE_VULKAN_RENDER
208+
inline bool isVulkanRenderer() const {
209+
return m_output && m_output->output() && m_output->output()->renderer()
210+
&& wlr_renderer_is_vk(m_output->output()->renderer()->handle());
211+
}
212+
213+
inline bool shouldDeferVulkanRenderRetry() {
214+
if (!isVulkanRenderer() || m_vulkanRenderRetryDelayFrames <= 0)
215+
return false;
216+
217+
--m_vulkanRenderRetryDelayFrames;
218+
scheduleFrame();
219+
return true;
220+
}
221+
222+
inline bool noteVulkanRenderFailed() {
223+
if (!isVulkanRenderer())
224+
return false;
225+
226+
m_vulkanRenderFailureBackoffFrames = m_vulkanRenderFailureBackoffFrames
227+
? qMin(m_vulkanRenderFailureBackoffFrames * 2, 4)
228+
: 1;
229+
m_vulkanRenderRetryDelayFrames = m_vulkanRenderFailureBackoffFrames;
230+
scheduleFrame();
231+
return true;
232+
}
233+
234+
inline void noteVulkanRenderSucceeded() {
235+
if (!isVulkanRenderer())
236+
return;
237+
238+
m_vulkanRenderFailureBackoffFrames = 0;
239+
m_vulkanRenderRetryDelayFrames = 0;
240+
}
241+
#endif
242+
207243
inline WOutputRenderWindow *renderWindow() const {
208244
return static_cast<WOutputRenderWindow*>(parent());
209245
}
@@ -273,6 +309,10 @@ class Q_DECL_HIDDEN OutputHelper : public WOutputHelper
273309
BufferRendererProxy *m_cursorLayerProxy = nullptr;
274310
bool m_cursorDirty = false;
275311
bool m_hardwareCursorRenderComplete = false;
312+
#ifdef ENABLE_VULKAN_RENDER
313+
int m_vulkanRenderFailureBackoffFrames = 0;
314+
int m_vulkanRenderRetryDelayFrames = 0;
315+
#endif
276316

277317
// for compositeLayers
278318
QPointer<WOutputViewport> m_output2;
@@ -1548,9 +1588,25 @@ WOutputRenderWindowPrivate::doRenderOutputs(qw_output *needsFrameOutput, const Q
15481588
if (!helper->output()->depends().isEmpty())
15491589
updateDirtyNodes();
15501590

1591+
#ifdef ENABLE_VULKAN_RENDER
1592+
if (helper->shouldDeferVulkanRenderRetry())
1593+
continue;
1594+
#endif
1595+
15511596
qw_buffer *buffer = helper->beginRender(helper->bufferRenderer(), helper->output()->output()->size(), format,
15521597
WBufferRenderer::RedirectOpenGLContextDefaultFrameBufferObject);
15531598
Q_ASSERT(buffer == helper->bufferRenderer()->currentBuffer());
1599+
if (!buffer) {
1600+
#ifdef ENABLE_VULKAN_RENDER
1601+
if (!helper->extraState() && helper->noteVulkanRenderFailed())
1602+
continue;
1603+
#endif
1604+
}
1605+
#ifdef ENABLE_VULKAN_RENDER
1606+
else {
1607+
helper->noteVulkanRenderSucceeded();
1608+
}
1609+
#endif
15541610
if (buffer) {
15551611
helper->render(helper->bufferRenderer(), 0, renderMatrix,
15561612
helper->output()->effectiveSourceRect(),
@@ -1635,8 +1691,13 @@ void WOutputRenderWindowPrivate::doRender(qw_output *needsFrameOutput,
16351691
if (doCommit) {
16361692
committedOutputs.reserve(needsCommit.size());
16371693
for (auto i : std::as_const(needsCommit)) {
1694+
bool commitAttempted = false;
1695+
bool commitSucceeded = false;
1696+
const bool hadCurrentBuffer = i.second->currentBuffer();
16381697
if (Q_UNLIKELY(!i.first->framePending())) {
1639-
if (Q_LIKELY(i.first->commit(i.second))) {
1698+
commitAttempted = true;
1699+
commitSucceeded = i.first->commit(i.second);
1700+
if (Q_LIKELY(commitSucceeded)) {
16401701
// Make sure the output is still valid after commit
16411702
auto output = i.first->output()->output();
16421703
if (Q_LIKELY(needsFrameOutput)) {
@@ -1649,11 +1710,17 @@ void WOutputRenderWindowPrivate::doRender(qw_output *needsFrameOutput,
16491710
}
16501711
}
16511712

1652-
if (i.second->currentBuffer()) {
1713+
if (hadCurrentBuffer) {
16531714
i.second->endRender();
16541715
}
16551716

16561717
i.first->resetState();
1718+
#ifdef ENABLE_VULKAN_RENDER
1719+
if (hadCurrentBuffer && commitAttempted && !commitSucceeded
1720+
&& i.first->noteVulkanRenderFailed()) {
1721+
i.first->update();
1722+
}
1723+
#endif
16571724
}
16581725
}
16591726

waylib/src/server/qtquick/wrenderhelper.cpp

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -177,6 +177,17 @@ static PFNGLEGLIMAGETARGETTEXTURE2DOESPROC resolveGlEGLImageTargetTexture2DOES()
177177
return proc;
178178
}
179179

180+
static void clearGlErrors()
181+
{
182+
while (glGetError() != GL_NO_ERROR) {
183+
}
184+
}
185+
186+
static bool textureUploadSucceeded()
187+
{
188+
return glGetError() == GL_NO_ERROR;
189+
}
190+
180191
Q_GLOBAL_STATIC(QMutex, s_pendingNativeTextureCleanupMutex)
181192
Q_GLOBAL_STATIC(QVector<WRenderHelper::NativeTextureCleanup>, s_pendingNativeTextureCleanups)
182193

@@ -334,12 +345,25 @@ static bool eglImportDmabufToGLTexture(EGLDisplay display,
334345
}
335346

336347
GLuint tex = 0;
348+
clearGlErrors();
337349
glGenTextures(1, &tex);
350+
if (!tex) {
351+
if (auto destroyImage = resolveEglDestroyImageKHR())
352+
destroyImage(display, image);
353+
return false;
354+
}
355+
338356
glBindTexture(GL_TEXTURE_2D, tex);
339357
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
340358
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
341359
glEGLImageTargetTexture2DOES(GL_TEXTURE_2D, image);
342360
glBindTexture(GL_TEXTURE_2D, 0);
361+
if (!textureUploadSucceeded()) {
362+
glDeleteTextures(1, &tex);
363+
if (auto destroyImage = resolveEglDestroyImageKHR())
364+
destroyImage(display, image);
365+
return false;
366+
}
343367

344368
*outImage = image;
345369
*outTex = tex;
@@ -1433,7 +1457,11 @@ bool WRenderHelper::makeTexture(QRhi *rhi, qw_texture *handle,
14331457
return false;
14341458

14351459
GLuint glTex = 0;
1460+
clearGlErrors();
14361461
glGenTextures(1, &glTex);
1462+
if (!glTex)
1463+
return false;
1464+
14371465
glBindTexture(GL_TEXTURE_2D, glTex);
14381466
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
14391467
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
@@ -1444,6 +1472,10 @@ bool WRenderHelper::makeTexture(QRhi *rhi, qw_texture *handle,
14441472
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, size.width(), size.height(), 0,
14451473
GL_BGRA, GL_UNSIGNED_BYTE, pixels.constData());
14461474
glBindTexture(GL_TEXTURE_2D, 0);
1475+
if (!textureUploadSucceeded()) {
1476+
glDeleteTextures(1, &glTex);
1477+
return false;
1478+
}
14471479

14481480
texture->setTextureFromNativeTexture(rhi, glTex, 0, 0, size, {},
14491481
QQuickWindowPrivate::TextureFromNativeTextureFlags{});

waylib/src/server/qtquick/wsurfaceitem.cpp

Lines changed: 54 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -223,6 +223,10 @@ class Q_DECL_HIDDEN WSurfaceItemContentPrivate: public QQuickItemPrivate
223223
surface = nullptr;
224224
}
225225
textureDirty = true;
226+
#ifdef ENABLE_VULKAN_RENDER
227+
textureRetryBackoffFrames = 0;
228+
textureRetryDelayFrames = 0;
229+
#endif
226230

227231
if (frameDoneConnection)
228232
QObject::disconnect(frameDoneConnection);
@@ -333,6 +337,37 @@ class Q_DECL_HIDDEN WSurfaceItemContentPrivate: public QQuickItemPrivate
333337
q->setImplicitSize(s.width(), s.height());
334338
}
335339

340+
#ifdef ENABLE_VULKAN_RENDER
341+
inline bool isVulkanRenderer() const {
342+
auto rw = qobject_cast<WOutputRenderWindow *>(window);
343+
return rw && rw->renderer() && wlr_renderer_is_vk(rw->renderer()->handle());
344+
}
345+
346+
inline bool shouldDeferTextureRetry(bool hasCachedTexture) {
347+
if (!hasCachedTexture || !isVulkanRenderer() || textureRetryDelayFrames <= 0)
348+
return false;
349+
350+
--textureRetryDelayFrames;
351+
return true;
352+
}
353+
354+
inline void noteTextureUpdateResult(bool ok) {
355+
if (!isVulkanRenderer())
356+
return;
357+
358+
if (ok) {
359+
textureRetryBackoffFrames = 0;
360+
textureRetryDelayFrames = 0;
361+
return;
362+
}
363+
364+
textureRetryBackoffFrames = textureRetryBackoffFrames
365+
? qMin(textureRetryBackoffFrames * 2, 4)
366+
: 1;
367+
textureRetryDelayFrames = textureRetryBackoffFrames;
368+
}
369+
#endif
370+
336371
inline void swapBufferIfNeeded() {
337372
if (pendingBuffer) {
338373
buffer = std::move(pendingBuffer);
@@ -377,6 +412,10 @@ class Q_DECL_HIDDEN WSurfaceItemContentPrivate: public QQuickItemPrivate
377412
bool ignoreBufferOffset = false;
378413
bool lastRendered = false;
379414
QAtomicInteger<bool> rendered = false;
415+
#ifdef ENABLE_VULKAN_RENDER
416+
int textureRetryBackoffFrames = 0;
417+
int textureRetryDelayFrames = 0;
418+
#endif
380419
};
381420

382421

@@ -516,6 +555,10 @@ void WSurfaceItemContent::setLive(bool live)
516555
if (live) {
517556
d->swapBufferIfNeeded();
518557
d->textureDirty = true;
558+
#ifdef ENABLE_VULKAN_RENDER
559+
d->textureRetryBackoffFrames = 0;
560+
d->textureRetryDelayFrames = 0;
561+
#endif
519562
update();
520563
}
521564
Q_EMIT liveChanged();
@@ -602,16 +645,25 @@ QSGNode *WSurfaceItemContent::updatePaintNode(QSGNode *oldNode, UpdatePaintNodeD
602645
W_D(WSurfaceItemContent);
603646

604647
auto tp = wTextureProvider();
605-
if ((d->live && d->textureDirty) || !tp->texture()) {
648+
const bool hasCachedTexture = tp->texture();
649+
if (((d->live && d->textureDirty) || !hasCachedTexture)
650+
#ifdef ENABLE_VULKAN_RENDER
651+
&& !d->shouldDeferTextureRetry(hasCachedTexture)
652+
#endif
653+
) {
606654
auto texture = d->surface ? d->surface->handle()->get_texture() : nullptr;
607655
bool textureUpdated = false;
608656
if (texture) {
609657
textureUpdated = tp->setTexture(qw_texture::from(texture), d->buffer.get());
610658
} else {
611659
textureUpdated = tp->setBuffer(d->buffer.get());
612660
}
613-
if (textureUpdated)
661+
#ifdef ENABLE_VULKAN_RENDER
662+
d->noteTextureUpdateResult(textureUpdated);
663+
#endif
664+
if (textureUpdated) {
614665
d->textureDirty = false;
666+
}
615667
}
616668

617669
if (!tp->texture() || width() <= 0 || height() <= 0) {

0 commit comments

Comments
 (0)