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>
This commit is contained in:
parent
fb70893dd5
commit
401edea982
@ -417,6 +417,11 @@ struct QMetalSwapChainData
|
|||||||
id<MTLTexture> msaaTex[QMTL_FRAMES_IN_FLIGHT];
|
id<MTLTexture> msaaTex[QMTL_FRAMES_IN_FLIGHT];
|
||||||
QRhiTexture::Format rhiColorFormat;
|
QRhiTexture::Format rhiColorFormat;
|
||||||
MTLPixelFormat colorFormat;
|
MTLPixelFormat colorFormat;
|
||||||
|
#ifdef Q_OS_MACOS
|
||||||
|
bool liveResizeObserverSet = false;
|
||||||
|
QMacNotificationObserver liveResizeStartObserver;
|
||||||
|
QMacNotificationObserver liveResizeEndObserver;
|
||||||
|
#endif
|
||||||
};
|
};
|
||||||
|
|
||||||
QRhiMetal::QRhiMetal(QRhiMetalInitParams *params, QRhiMetalNativeHandles *importDevice)
|
QRhiMetal::QRhiMetal(QRhiMetalInitParams *params, QRhiMetalNativeHandles *importDevice)
|
||||||
@ -2291,26 +2296,29 @@ QRhi::FrameOpResult QRhiMetal::endFrame(QRhiSwapChain *swapChain, QRhi::EndFrame
|
|||||||
QMetalSwapChain *swapChainD = QRHI_RES(QMetalSwapChain, swapChain);
|
QMetalSwapChain *swapChainD = QRHI_RES(QMetalSwapChain, swapChain);
|
||||||
Q_ASSERT(currentSwapChain == swapChainD);
|
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);
|
const bool needsPresent = !flags.testFlag(QRhi::SkipPresent);
|
||||||
if (needsPresent) {
|
const bool presentsWithTransaction = swapChainD->d->layer.presentsWithTransaction;
|
||||||
// beginFrame-endFrame without a render pass inbetween means there is no
|
if (!presentsWithTransaction && needsPresent) {
|
||||||
// drawable, handle this gracefully because presentDrawable does not like
|
// beginFrame-endFrame without a render pass inbetween means there is no drawable.
|
||||||
// null arguments.
|
if (id<CAMetalDrawable> drawable = swapChainD->d->curDrawable)
|
||||||
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
|
|
||||||
[swapChainD->cbWrapper.d->cb presentDrawable: drawable];
|
[swapChainD->cbWrapper.d->cb presentDrawable: drawable];
|
||||||
#else
|
}
|
||||||
[swapChainD->cbWrapper.d->cb addScheduledHandler:^(id<MTLCommandBuffer>) {
|
|
||||||
[drawable present];
|
[swapChainD->cbWrapper.d->cb commit];
|
||||||
}];
|
|
||||||
#endif
|
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 release];
|
||||||
swapChainD->d->curDrawable = nil;
|
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];
|
[d->captureScope endScope];
|
||||||
|
|
||||||
if (needsPresent)
|
if (needsPresent)
|
||||||
@ -5974,6 +5975,12 @@ void QMetalSwapChain::destroy()
|
|||||||
d->msaaTex[i] = nil;
|
d->msaaTex[i] = nil;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifdef Q_OS_MACOS
|
||||||
|
d->liveResizeStartObserver.remove();
|
||||||
|
d->liveResizeEndObserver.remove();
|
||||||
|
d->liveResizeObserverSet = false;
|
||||||
|
#endif
|
||||||
|
|
||||||
d->layer = nullptr;
|
d->layer = nullptr;
|
||||||
|
|
||||||
[d->curDrawable release];
|
[d->curDrawable release];
|
||||||
@ -6163,6 +6170,34 @@ bool QMetalSwapChain::createOrResize()
|
|||||||
|
|
||||||
[d->layer setDevice: rhiD->d->dev];
|
[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 release];
|
||||||
d->curDrawable = nil;
|
d->curDrawable = nil;
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user