diff --git a/src/gui/rhi/qrhi.cpp b/src/gui/rhi/qrhi.cpp index ccb6db445be..cbb2280a443 100644 --- a/src/gui/rhi/qrhi.cpp +++ b/src/gui/rhi/qrhi.cpp @@ -5636,6 +5636,49 @@ bool QRhi::probe(QRhi::Implementation impl, QRhiInitParams *params) return ok; } +/*! + \struct QRhiSwapChainProxyData + \internal + \inmodule QtGui + */ + +/*! + Generates and returns a QRhiSwapChainProxyData struct containing opaque + data specific to the backend and graphics API specified by \a impl. \a + window is the QWindow a swapchain is targeting. + + The returned struct can be passed to QRhiSwapChain::setProxyData(). This + makes sense in threaded rendering systems: this static function is expected + to be called on the \b{main (gui) thread}, unlike all QRhi operations, then + transferred to the thread working with the QRhi and QRhiSwapChain and passed + on to the swapchain. This allows doing native platform queries that are + only safe to be called on the main thread, for example to query the + CAMetalLayer from a NSView, and then passing on the data to the + QRhiSwapChain living on the rendering thread. With the Metal example, doing + the view.layer access on a dedicated rendering thread causes a warning in + the Xcode Thread Checker. With the data proxy mechanism, this is avoided. + + When threads are not involved, generating and passing on the + QRhiSwapChainProxyData is not required: backends are guaranteed to be able + to query whatever is needed on their own, and if everything lives on the + main (gui) thread, that should be sufficient. + + \note \a impl should match what the QRhi is created with. For example, + calling with QRhi::Metal on a non-Apple platform will not generate any + useful data. + */ +QRhiSwapChainProxyData QRhi::updateSwapChainProxyData(QRhi::Implementation impl, QWindow *window) +{ +#if defined(Q_OS_MACOS) || defined(Q_OS_IOS) + if (impl == Metal) + return QRhiMetal::updateSwapChainProxyData(window); +#else + Q_UNUSED(impl); + Q_UNUSED(window); +#endif + return {}; +} + /*! \return the backend type for this QRhi. */ diff --git a/src/gui/rhi/qrhi_p.h b/src/gui/rhi/qrhi_p.h index 65146888365..de322d37891 100644 --- a/src/gui/rhi/qrhi_p.h +++ b/src/gui/rhi/qrhi_p.h @@ -1361,6 +1361,11 @@ Q_DECLARE_TYPEINFO(QRhiSwapChainHdrInfo, Q_RELOCATABLE_TYPE); Q_GUI_EXPORT QDebug operator<<(QDebug, const QRhiSwapChainHdrInfo &); #endif +struct QRhiSwapChainProxyData +{ + void *reserved[2] = {}; +}; + class Q_GUI_EXPORT QRhiSwapChain : public QRhiResource { public: @@ -1390,6 +1395,9 @@ public: QWindow *window() const { return m_window; } void setWindow(QWindow *window) { m_window = window; } + QRhiSwapChainProxyData proxyData() const { return m_proxyData; } + void setProxyData(const QRhiSwapChainProxyData &d) { m_proxyData = d; } + Flags flags() const { return m_flags; } void setFlags(Flags f) { m_flags = f; } @@ -1425,6 +1433,7 @@ protected: int m_sampleCount = 1; QRhiRenderPassDescriptor *m_renderPassDesc = nullptr; QSize m_currentPixelSize; + QRhiSwapChainProxyData m_proxyData; }; Q_DECLARE_OPERATORS_FOR_FLAGS(QRhiSwapChain::Flags) @@ -1810,6 +1819,8 @@ public: QRhiStats statistics() const; + static QRhiSwapChainProxyData updateSwapChainProxyData(Implementation impl, QWindow *window); + protected: QRhi(); diff --git a/src/gui/rhi/qrhi_p_p.h b/src/gui/rhi/qrhi_p_p.h index 446403f9b7c..3d249c83490 100644 --- a/src/gui/rhi/qrhi_p_p.h +++ b/src/gui/rhi/qrhi_p_p.h @@ -789,6 +789,15 @@ bool QRhiRenderTargetAttachmentTracker::isUpToDate(const QRhiTextureRenderTarget return resIdList == currentResIdList; } +template +inline T *qrhi_objectFromProxyData(QRhiSwapChainProxyData *pd, QWindow *window, QRhi::Implementation impl, uint objectIndex) +{ + Q_ASSERT(objectIndex < std::size(pd->reserved)); + if (!pd->reserved[objectIndex]) // // was not set, no other choice, do it here, whatever thread this is + *pd = QRhi::updateSwapChainProxyData(impl, window); + return static_cast(pd->reserved[objectIndex]); +} + QT_END_NAMESPACE #endif diff --git a/src/gui/rhi/qrhimetal.mm b/src/gui/rhi/qrhimetal.mm index 7b7856ec540..bd41014ba00 100644 --- a/src/gui/rhi/qrhimetal.mm +++ b/src/gui/rhi/qrhimetal.mm @@ -5501,6 +5501,9 @@ QRhiRenderTarget *QMetalSwapChain::currentFrameRenderTarget() return &rtWrapper; } +// view.layer should ideally be called on the main thread, otherwise the UI +// Thread Checker in Xcode drops a warning. Hence trying to proxy it through +// QRhiSwapChainProxyData instead of just calling this function directly. static inline CAMetalLayer *layerForWindow(QWindow *window) { Q_ASSERT(window); @@ -5513,13 +5516,24 @@ static inline CAMetalLayer *layerForWindow(QWindow *window) return static_cast(view.layer); } +// If someone calls this, it is hopefully from the main thread, and they will +// then set the returned data on the QRhiSwapChain, so it won't need to query +// the layer on its own later on. +QRhiSwapChainProxyData QRhiMetal::updateSwapChainProxyData(QWindow *window) +{ + QRhiSwapChainProxyData d; + d.reserved[0] = layerForWindow(window); + return d; +} + QSize QMetalSwapChain::surfacePixelSize() { Q_ASSERT(m_window); CAMetalLayer *layer = d->layer; if (!layer) - layer = layerForWindow(m_window); + layer = qrhi_objectFromProxyData(&m_proxyData, m_window, QRhi::Metal, 0); + Q_ASSERT(layer); int height = (int)layer.bounds.size.height; int width = (int)layer.bounds.size.width; width *= layer.contentsScale; @@ -5593,7 +5607,7 @@ bool QMetalSwapChain::createOrResize() return false; } - d->layer = layerForWindow(window); + d->layer = qrhi_objectFromProxyData(&m_proxyData, window, QRhi::Metal, 0); Q_ASSERT(d->layer); chooseFormats(); diff --git a/src/gui/rhi/qrhimetal_p_p.h b/src/gui/rhi/qrhimetal_p_p.h index 5e7e376d096..84c746e830b 100644 --- a/src/gui/rhi/qrhimetal_p_p.h +++ b/src/gui/rhi/qrhimetal_p_p.h @@ -329,6 +329,7 @@ public: ~QRhiMetal(); static bool probe(QRhiMetalInitParams *params); + static QRhiSwapChainProxyData updateSwapChainProxyData(QWindow *window); bool create(QRhi::Flags flags) override; void destroy() override;