summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorLaszlo Agocs <laszlo.agocs@qt.io>2023-03-10 17:02:48 +0100
committerLaszlo Agocs <laszlo.agocs@qt.io>2023-03-23 16:39:18 +0100
commit401edea98297ac69ed5f52f63ea044ab56dcfac8 (patch)
tree3be6009a4eb5286ad81e389af07b146011ca3da0
parentfb70893dd5e616356260fe0b391dd77007c7bfc7 (diff)
rhi: metal: macOS: Set presentsWithTransaction during resize
...when presenting from the main thread. Make resizing nice and smooth in application that render and present on the main thread. This includes Qt Quick applications when ran with QSG_RENDER_LOOP=basic, as well as QQuickWidget-based widget apps and anything using the QRhi-based backingstore flushing, and plain QWindow apps that use QRhi on the main thread. Allow opting out with an environment variable (QT_MTL_NO_TRANSACTION), following the existing patterns of some of the other backends (such as QT_D3D_NO_xxxx). This can be handy in case it turns out setting presentsWithTransaction is not desired in some very specific case. Task-number: QTBUG-107198 Change-Id: Id0f4e5a509076dd24c03d243c4f098ddb139d7af Reviewed-by: Tor Arne Vestbø <tor.arne.vestbo@qt.io>
-rw-r--r--src/gui/rhi/qrhimetal.mm85
1 files changed, 60 insertions, 25 deletions
diff --git a/src/gui/rhi/qrhimetal.mm b/src/gui/rhi/qrhimetal.mm
index e892fad3f3..196016d9ca 100644
--- a/src/gui/rhi/qrhimetal.mm
+++ b/src/gui/rhi/qrhimetal.mm
@@ -417,6 +417,11 @@ struct QMetalSwapChainData
id<MTLTexture> msaaTex[QMTL_FRAMES_IN_FLIGHT];
QRhiTexture::Format rhiColorFormat;
MTLPixelFormat colorFormat;
+#ifdef Q_OS_MACOS
+ bool liveResizeObserverSet = false;
+ QMacNotificationObserver liveResizeStartObserver;
+ QMacNotificationObserver liveResizeEndObserver;
+#endif
};
QRhiMetal::QRhiMetal(QRhiMetalInitParams *params, QRhiMetalNativeHandles *importDevice)
@@ -2291,26 +2296,29 @@ QRhi::FrameOpResult QRhiMetal::endFrame(QRhiSwapChain *swapChain, QRhi::EndFrame
QMetalSwapChain *swapChainD = QRHI_RES(QMetalSwapChain, swapChain);
Q_ASSERT(currentSwapChain == swapChainD);
+ __block int thisFrameSlot = currentFrameSlot;
+ [swapChainD->cbWrapper.d->cb addCompletedHandler: ^(id<MTLCommandBuffer>) {
+ dispatch_semaphore_signal(swapChainD->d->sem[thisFrameSlot]);
+ }];
+
const bool needsPresent = !flags.testFlag(QRhi::SkipPresent);
- if (needsPresent) {
- // beginFrame-endFrame without a render pass inbetween means there is no
- // drawable, handle this gracefully because presentDrawable does not like
- // null arguments.
- if (id<CAMetalDrawable> drawable = swapChainD->d->curDrawable) {
- // QTBUG-103415: while the docs suggest the following two approaches are
- // equivalent, there is a difference in case a frame is recorded earlier than
- // (i.e. not in response to) the next CVDisplayLink callback. Therefore, stick
- // with presentDrawable, which gives results identical to OpenGL, and all other
- // platforms, i.e. throttles to vsync as expected, meaning constant 15-17 ms with
- // a 60 Hz screen, no jumps with smaller intervals, regardless of when the frame
- // is submitted by the app)
-#if 1
+ const bool presentsWithTransaction = swapChainD->d->layer.presentsWithTransaction;
+ if (!presentsWithTransaction && needsPresent) {
+ // beginFrame-endFrame without a render pass inbetween means there is no drawable.
+ if (id<CAMetalDrawable> drawable = swapChainD->d->curDrawable)
[swapChainD->cbWrapper.d->cb presentDrawable: drawable];
-#else
- [swapChainD->cbWrapper.d->cb addScheduledHandler:^(id<MTLCommandBuffer>) {
- [drawable present];
- }];
-#endif
+ }
+
+ [swapChainD->cbWrapper.d->cb commit];
+
+ if (presentsWithTransaction && needsPresent) {
+ // beginFrame-endFrame without a render pass inbetween means there is no drawable.
+ if (id<CAMetalDrawable> drawable = swapChainD->d->curDrawable) {
+ // The layer has presentsWithTransaction set to true to avoid flicker on resizing,
+ // so here it is important to follow what the Metal docs say when it comes to the
+ // issuing the present.
+ [swapChainD->cbWrapper.d->cb waitUntilScheduled];
+ [drawable present];
}
}
@@ -2318,13 +2326,6 @@ QRhi::FrameOpResult QRhiMetal::endFrame(QRhiSwapChain *swapChain, QRhi::EndFrame
[swapChainD->d->curDrawable release];
swapChainD->d->curDrawable = nil;
- __block int thisFrameSlot = currentFrameSlot;
- [swapChainD->cbWrapper.d->cb addCompletedHandler: ^(id<MTLCommandBuffer>) {
- dispatch_semaphore_signal(swapChainD->d->sem[thisFrameSlot]);
- }];
-
- [swapChainD->cbWrapper.d->cb commit];
-
[d->captureScope endScope];
if (needsPresent)
@@ -5974,6 +5975,12 @@ void QMetalSwapChain::destroy()
d->msaaTex[i] = nil;
}
+#ifdef Q_OS_MACOS
+ d->liveResizeStartObserver.remove();
+ d->liveResizeEndObserver.remove();
+ d->liveResizeObserverSet = false;
+#endif
+
d->layer = nullptr;
[d->curDrawable release];
@@ -6163,6 +6170,34 @@ bool QMetalSwapChain::createOrResize()
[d->layer setDevice: rhiD->d->dev];
+#ifdef Q_OS_MACOS
+ // Can only use presentsWithTransaction (to get smooth resizing) when
+ // presenting from the main (gui) thread. We predict that based on the
+ // thread this function is called on since if the QRhiSwapChain is
+ // initialied on a given thread then that's almost certainly the thread on
+ // which the QRhi renders and presents.
+ const bool canUsePresentsWithTransaction = NSThread.isMainThread;
+
+ // Have an env.var. just in case it turns out presentsWithTransaction is
+ // not desired in some specific case.
+ static bool allowPresentsWithTransaction = !qEnvironmentVariableIntValue("QT_MTL_NO_TRANSACTION");
+
+ if (allowPresentsWithTransaction && canUsePresentsWithTransaction && !d->liveResizeObserverSet) {
+ d->liveResizeObserverSet = true;
+ NSView *view = reinterpret_cast<NSView *>(window->winId());
+ NSWindow *window = view.window;
+ if (window) {
+ qCDebug(QRHI_LOG_INFO, "will set presentsWithTransaction during live resize");
+ d->liveResizeStartObserver = QMacNotificationObserver(window, NSWindowWillStartLiveResizeNotification, [this] {
+ d->layer.presentsWithTransaction = true;
+ });
+ d->liveResizeEndObserver = QMacNotificationObserver(window, NSWindowDidEndLiveResizeNotification, [this] {
+ d->layer.presentsWithTransaction = false;
+ });
+ }
+ }
+#endif
+
[d->curDrawable release];
d->curDrawable = nil;