rhi: metal: Add enablers for pre-querying window stuff on the gui thread

Pick-to: 6.5
Task-number: QTBUG-97518
Change-Id: Ia8fb5128149c9f91ebedfa914d1fe3e3d49774dc
Reviewed-by: Qt CI Bot <qt_ci_bot@qt-project.org>
Reviewed-by: Laszlo Agocs <laszlo.agocs@qt.io>
This commit is contained in:
Laszlo Agocs 2022-12-12 19:37:52 +01:00
parent a2e38207da
commit f486d1f4d2
5 changed files with 80 additions and 2 deletions

View File

@ -5636,6 +5636,49 @@ bool QRhi::probe(QRhi::Implementation impl, QRhiInitParams *params)
return ok; 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. \return the backend type for this QRhi.
*/ */

View File

@ -1361,6 +1361,11 @@ Q_DECLARE_TYPEINFO(QRhiSwapChainHdrInfo, Q_RELOCATABLE_TYPE);
Q_GUI_EXPORT QDebug operator<<(QDebug, const QRhiSwapChainHdrInfo &); Q_GUI_EXPORT QDebug operator<<(QDebug, const QRhiSwapChainHdrInfo &);
#endif #endif
struct QRhiSwapChainProxyData
{
void *reserved[2] = {};
};
class Q_GUI_EXPORT QRhiSwapChain : public QRhiResource class Q_GUI_EXPORT QRhiSwapChain : public QRhiResource
{ {
public: public:
@ -1390,6 +1395,9 @@ public:
QWindow *window() const { return m_window; } QWindow *window() const { return m_window; }
void setWindow(QWindow *window) { m_window = 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; } Flags flags() const { return m_flags; }
void setFlags(Flags f) { m_flags = f; } void setFlags(Flags f) { m_flags = f; }
@ -1425,6 +1433,7 @@ protected:
int m_sampleCount = 1; int m_sampleCount = 1;
QRhiRenderPassDescriptor *m_renderPassDesc = nullptr; QRhiRenderPassDescriptor *m_renderPassDesc = nullptr;
QSize m_currentPixelSize; QSize m_currentPixelSize;
QRhiSwapChainProxyData m_proxyData;
}; };
Q_DECLARE_OPERATORS_FOR_FLAGS(QRhiSwapChain::Flags) Q_DECLARE_OPERATORS_FOR_FLAGS(QRhiSwapChain::Flags)
@ -1810,6 +1819,8 @@ public:
QRhiStats statistics() const; QRhiStats statistics() const;
static QRhiSwapChainProxyData updateSwapChainProxyData(Implementation impl, QWindow *window);
protected: protected:
QRhi(); QRhi();

View File

@ -789,6 +789,15 @@ bool QRhiRenderTargetAttachmentTracker::isUpToDate(const QRhiTextureRenderTarget
return resIdList == currentResIdList; return resIdList == currentResIdList;
} }
template<typename T>
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<T *>(pd->reserved[objectIndex]);
}
QT_END_NAMESPACE QT_END_NAMESPACE
#endif #endif

View File

@ -5501,6 +5501,9 @@ QRhiRenderTarget *QMetalSwapChain::currentFrameRenderTarget()
return &rtWrapper; 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) static inline CAMetalLayer *layerForWindow(QWindow *window)
{ {
Q_ASSERT(window); Q_ASSERT(window);
@ -5513,13 +5516,24 @@ static inline CAMetalLayer *layerForWindow(QWindow *window)
return static_cast<CAMetalLayer *>(view.layer); return static_cast<CAMetalLayer *>(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() QSize QMetalSwapChain::surfacePixelSize()
{ {
Q_ASSERT(m_window); Q_ASSERT(m_window);
CAMetalLayer *layer = d->layer; CAMetalLayer *layer = d->layer;
if (!layer) if (!layer)
layer = layerForWindow(m_window); layer = qrhi_objectFromProxyData<CAMetalLayer>(&m_proxyData, m_window, QRhi::Metal, 0);
Q_ASSERT(layer);
int height = (int)layer.bounds.size.height; int height = (int)layer.bounds.size.height;
int width = (int)layer.bounds.size.width; int width = (int)layer.bounds.size.width;
width *= layer.contentsScale; width *= layer.contentsScale;
@ -5593,7 +5607,7 @@ bool QMetalSwapChain::createOrResize()
return false; return false;
} }
d->layer = layerForWindow(window); d->layer = qrhi_objectFromProxyData<CAMetalLayer>(&m_proxyData, window, QRhi::Metal, 0);
Q_ASSERT(d->layer); Q_ASSERT(d->layer);
chooseFormats(); chooseFormats();

View File

@ -329,6 +329,7 @@ public:
~QRhiMetal(); ~QRhiMetal();
static bool probe(QRhiMetalInitParams *params); static bool probe(QRhiMetalInitParams *params);
static QRhiSwapChainProxyData updateSwapChainProxyData(QWindow *window);
bool create(QRhi::Flags flags) override; bool create(QRhi::Flags flags) override;
void destroy() override; void destroy() override;