From 6de5cf2df82e5025bd9f02e9f38922f8c7c4b3b5 Mon Sep 17 00:00:00 2001 From: Laszlo Agocs Date: Mon, 2 Sep 2019 13:08:34 +0200 Subject: rhi: metal: Avoid upsetting validation in viewport and scissor When running with the threaded render loop of Qt Quick, it could be that the drawable changes size while the render thread prepares the command buffer with setViewport and setScissor. Those have no chance to see such changes, which is normally not a big problem because the resize will get processed eventually. However, in debug builds running in XCode, Metal validation checks the viewport and scissor rects against the (more or less) actual drawable size, and so would abort Qt Quick apps from time to time when resizing the window interactively. To solve this, we just query the drawable size in setViewport/setScissor to keep validation happy. Change-Id: I451f398bd1f88e3f49ea4624fc45bbb4b70e7f07 Reviewed-by: Andy Nichols --- src/gui/rhi/qrhimetal.mm | 37 +++++++++++++++++++++++++++++++++++-- 1 file changed, 35 insertions(+), 2 deletions(-) (limited to 'src/gui/rhi') diff --git a/src/gui/rhi/qrhimetal.mm b/src/gui/rhi/qrhimetal.mm index 07753c985c..dfa79edb00 100644 --- a/src/gui/rhi/qrhimetal.mm +++ b/src/gui/rhi/qrhimetal.mm @@ -997,11 +997,44 @@ void QRhiMetal::setVertexInput(QRhiCommandBuffer *cb, } } +QSize safeOutputSize(QRhiMetal *rhiD, QMetalCommandBuffer *cbD) +{ + QSize size = cbD->currentTarget->pixelSize(); + + // So now we have the issue that the texture (drawable) size may have + // changed again since swapchain buildOrResize() was called. This can + // happen for example when interactively resizing the window a lot in one + // go, and command buffer building happens on a dedicated thread (f.ex. + // using the threaded render loop of Qt Quick). + // + // This is only an issue when running in debug mode with XCode because Metal + // validation will fail when setting viewport or scissor with the real size + // being smaller than what we think it is. So query the drawable size right + // here, in debug mode at least. + // + // In addition, we have to take the smaller of the two widths and heights + // to be safe, apparently. In some cases validation seems to think that the + // "render pass width" (or height) is the old(?) value. + +#ifdef QT_DEBUG + if (cbD->currentTarget->resourceType() == QRhiResource::RenderTarget) { + Q_ASSERT(rhiD->currentSwapChain); + const QSize otherSize = rhiD->currentSwapChain->surfacePixelSize(); + size.setWidth(qMin(size.width(), otherSize.width())); + size.setHeight(qMin(size.height(), otherSize.height())); + } +#else + Q_UNUSED(rhiD); +#endif + + return size; +} + void QRhiMetal::setViewport(QRhiCommandBuffer *cb, const QRhiViewport &viewport) { QMetalCommandBuffer *cbD = QRHI_RES(QMetalCommandBuffer, cb); Q_ASSERT(cbD->recordingPass == QMetalCommandBuffer::RenderPass); - const QSize outputSize = cbD->currentTarget->pixelSize(); + const QSize outputSize = safeOutputSize(this, cbD); // x,y is top-left in MTLViewportRect but bottom-left in QRhiViewport float x, y, w, h; @@ -1033,7 +1066,7 @@ void QRhiMetal::setScissor(QRhiCommandBuffer *cb, const QRhiScissor &scissor) QMetalCommandBuffer *cbD = QRHI_RES(QMetalCommandBuffer, cb); Q_ASSERT(cbD->recordingPass == QMetalCommandBuffer::RenderPass); Q_ASSERT(QRHI_RES(QMetalGraphicsPipeline, cbD->currentGraphicsPipeline)->m_flags.testFlag(QRhiGraphicsPipeline::UsesScissor)); - const QSize outputSize = cbD->currentTarget->pixelSize(); + const QSize outputSize = safeOutputSize(this, cbD); // x,y is top-left in MTLScissorRect but bottom-left in QRhiScissor int x, y, w, h; -- cgit v1.2.3