Commit 282383ca authored by Laszlo Agocs's avatar Laszlo Agocs

gl: rsh

parent 2ae78fee
......@@ -787,8 +787,8 @@ int main(int argc, char **argv)
"\n\nUsing API: ") + graphicsApiName());
info->setReadOnly(true);
layout->addWidget(info);
QCheckBox *rshCb = new QCheckBox(QLatin1String("Use QRhiResourceSharingHost for new window (use same device "
"(e.g. VkDevice+VkQueue on Vulkan) or sharing contexts (OpenGL))"));
QCheckBox *rshCb = new QCheckBox(QLatin1String("Use QRhiResourceSharingHost for new window\n(use same device, "
"e.g. VkDevice+VkQueue on Vulkan, ID3D11Device+Context on D3D; or sharing contexts on OpenGL)"));
rshCb->setChecked(false);
layout->addWidget(rshCb);
QLabel *label = new QLabel(QLatin1String("Window and thread count: 0"));
......
......@@ -227,11 +227,27 @@ QT_BEGIN_NAMESPACE
\section3 Threading
A QRhi instance can be created and used on any thread but all usage must be
limited to that one single thread. When it comes to native objects, such as
OpenGL contexts, passed in via a QRhiNativeHandles subclass, it is up to
the application to ensure they are not misused by other threads.
limited to that one single thread.
\sa {Qt Shader Tools}
When it comes to externally created native objects, such as OpenGL contexts
passed in via QRhiGles2NativeHandles, it is up to the application to ensure
they are not misused by other threads.
By default any QRhiResource created from one QRhi must only be used with
that one QRhi and must be released before the QRhi is destroyed. This rule
is relaxed when a QRhi is created with an associated
QRhiResourceSharingHost. From then on any QRhiResource that reports \c true
from \l{QRhiResource::isShareable()}{isShareable()} is usable with other
QRhi instances as well, as long as they are associated with the same
QRhiResourceSharingHost. Resources can also outlive their creating QRhi in
this case, as long as there is at least one QRhi left in the "sharing
group". When the QRhi instances live and operate on different threads,
additional synchronization may be required. When writing to resources,
additional synchronization may be required even when the QRhi instances
live on the same thread. See QRhiResourceSharingHost for further
discussion.
\sa {Qt Shader Tools}, QRhiResourceSharingHost
*/
/*!
......@@ -2607,6 +2623,13 @@ quint32 QRhiImplementation::approxByteSizeForTexture(QRhiTexture::Format format,
way that the QRhiResourceSharingHost is only destroyed after all associated
QRhi instances have been fully destroyed.
\note Having a QRhiResourceSharingHost associated with a QRhi triggers
mutex-based synchronization for native objects like the graphics queue or
device context, with backends where applicable. This allows having two or
more QRhi instances on two or more different threads with the same
QRhiResourceSharingHost and so sharing the same device, queue, or device
context.
To illustrate resource sharing, take the following code snippets.
In many cases an application will use a single QRhi. Resources created from it
......@@ -2668,8 +2691,10 @@ quint32 QRhiImplementation::approxByteSizeForTexture(QRhiTexture::Format format,
delete rsh;
\endcode
This is valid as well. After the \c{delete rhi} \c tex is in an orphaned
state so calling \c build() on it for instance would fail.
This is valid as well. After the \c{delete rhi} statement \c tex is in an
orphaned state so calling \c build() on it for example would fail. On the
other hand, it is still valid to use \c{tex} as it is for rendering, with
its existing underlying native texture.
\note Moving the \c{tex->releaseAndDestroy()} call between the \c{delete
rhi2} and \c{delete rsh} statements would be incorrect.
......
......@@ -251,8 +251,8 @@ bool QRhiGles2::create(QRhi::Flags flags)
rsh->d_gles2.dummyShareContext = new QOpenGLContext;
rsh->d_gles2.dummyShareContext->setFormat(effectiveFormat);
rsh->d_gles2.dummyShareContext->create();
shareContext = rsh->d_gles2.dummyShareContext;
}
shareContext = rsh->d_gles2.dummyShareContext;
}
if (!importedContext) {
......@@ -293,7 +293,8 @@ bool QRhiGles2::create(QRhi::Flags flags)
nativeHandlesStruct.context = ctx;
if (rsh) {
qDebug("Attached to QRhiResourceSharingHost %p, currently %d other QRhi instances", rsh, rsh->rhiCount);
qDebug("Attached to QRhiResourceSharingHost %p, currently %d other QRhi instances sharing via %p",
rsh, rsh->rhiCount, rsh->d_gles2.dummyShareContext);
rsh->rhiCount += 1;
}
......@@ -308,21 +309,28 @@ void QRhiGles2::destroy()
ensureContext();
executeDeferredReleases();
f = nullptr;
QMutexLocker lock(rsh ? &rsh->mtx : nullptr);
if (!importedContext) {
delete ctx;
ctx = nullptr;
}
if (rsh) {
if (--rsh->rhiCount == 0) {
if (rsh->d_gles2.releaseQueue) {
auto rshRelQueue = static_cast<QVector<DeferredReleaseEntry> *>(rsh->d_gles2.releaseQueue);
// with ctx still current
QRhiGles2::executeDeferredReleasesOnRshNow(rshRelQueue);
delete rshRelQueue;
}
delete rsh->d_gles2.dummyShareContext;
rsh->d_gles2.dummyShareContext = nullptr;
rsh->d_gles2.releaseQueue = nullptr;
}
}
if (!importedContext) {
delete ctx;
ctx = nullptr;
}
f = nullptr;
}
// Strictly speaking this is not necessary since we could do the deletes in
......@@ -350,12 +358,37 @@ void QRhiGles2::executeDeferredReleases()
f->glDeleteFramebuffers(1, &e.textureRenderTarget.framebuffer);
break;
default:
Q_UNREACHABLE();
break;
}
releaseQueue.removeAt(i);
}
}
void QRhiGles2::executeDeferredReleasesOnRshNow(QVector<QRhiGles2::DeferredReleaseEntry> *rshRelQueue)
{
for (int i = rshRelQueue->count() - 1; i >= 0; --i) {
const QRhiGles2::DeferredReleaseEntry &e((*rshRelQueue)[i]);
// only need to handle resources that report isShareable() == true
switch (e.type) {
case QRhiGles2::DeferredReleaseEntry::Buffer:
f->glDeleteBuffers(1, &e.buffer.buffer);
break;
case QRhiGles2::DeferredReleaseEntry::Texture:
f->glDeleteTextures(1, &e.texture.texture);
break;
case QRhiGles2::DeferredReleaseEntry::RenderBuffer:
f->glDeleteRenderbuffers(1, &e.renderbuffer.renderbuffer);
break;
// samplers have no backing GL object
default:
Q_UNREACHABLE();
break;
}
rshRelQueue->removeAt(i);
}
}
QVector<int> QRhiGles2::supportedSampleCounts() const
{
return { 1 };
......@@ -1717,11 +1750,27 @@ void QRhiGles2::endPass(QRhiCommandBuffer *cb, QRhiResourceUpdateBatch *resource
enqueueResourceUpdates(cb, resourceUpdates);
}
static void addToRshReleaseQueue(QRhiResourceSharingHostPrivate *rsh, const QRhiGles2::DeferredReleaseEntry &e)
{
QVector<QRhiGles2::DeferredReleaseEntry> *rshRelQueue =
static_cast<QVector<QRhiGles2::DeferredReleaseEntry> *>(rsh->d_gles2.releaseQueue);
if (!rshRelQueue) {
rshRelQueue = new QVector<QRhiGles2::DeferredReleaseEntry>;
rsh->d_gles2.releaseQueue = rshRelQueue;
}
rshRelQueue->append(e);
}
QGles2Buffer::QGles2Buffer(QRhiImplementation *rhi, Type type, UsageFlags usage, int size)
: QRhiBuffer(rhi, type, usage, size)
{
}
bool QGles2Buffer::isShareable() const
{
return true;
}
void QGles2Buffer::release()
{
if (!buffer)
......@@ -1734,23 +1783,29 @@ void QGles2Buffer::release()
buffer = 0;
QRHI_RES_RHI(QRhiGles2);
rhiD->releaseQueue.append(e);
QRHI_PROF;
QRHI_PROF_F(releaseBuffer(this));
rhiD->unregisterResource(this);
if (!orphanedWithRsh) {
QRHI_RES_RHI(QRhiGles2);
rhiD->releaseQueue.append(e);
QRHI_PROF;
QRHI_PROF_F(releaseBuffer(this));
rhiD->unregisterResource(this);
} else {
// associated rhi is already gone, queue the deferred release to the rsh instead
addToRshReleaseQueue(orphanedWithRsh, e);
}
}
bool QGles2Buffer::build()
{
QRHI_RES_RHI(QRhiGles2);
QRHI_PROF;
if (!QRhiImplementation::orphanCheck(this))
return false;
if (buffer)
release();
QRHI_RES_RHI(QRhiGles2);
QRHI_PROF;
if (m_usage.testFlag(QRhiBuffer::UniformBuffer)) {
// special since we do not support uniform blocks in this backend
ubuf.resize(m_size);
......@@ -1781,6 +1836,11 @@ QGles2RenderBuffer::QGles2RenderBuffer(QRhiImplementation *rhi, Type type, const
{
}
bool QGles2RenderBuffer::isShareable() const
{
return true;
}
void QGles2RenderBuffer::release()
{
if (!renderbuffer)
......@@ -1793,22 +1853,27 @@ void QGles2RenderBuffer::release()
renderbuffer = 0;
QRHI_RES_RHI(QRhiGles2);
rhiD->releaseQueue.append(e);
QRHI_PROF;
QRHI_PROF_F(releaseRenderBuffer(this));
rhiD->unregisterResource(this);
if (!orphanedWithRsh) {
QRHI_RES_RHI(QRhiGles2);
rhiD->releaseQueue.append(e);
QRHI_PROF;
QRHI_PROF_F(releaseRenderBuffer(this));
rhiD->unregisterResource(this);
} else {
// associated rhi is already gone, queue the deferred release to the rsh instead
addToRshReleaseQueue(orphanedWithRsh, e);
}
}
bool QGles2RenderBuffer::build()
{
QRHI_RES_RHI(QRhiGles2);
if (!QRhiImplementation::orphanCheck(this))
return false;
if (renderbuffer)
release();
QRHI_RES_RHI(QRhiGles2);
QRHI_PROF;
samples = qMax(1, m_sampleCount);
......@@ -1869,6 +1934,11 @@ QGles2Texture::QGles2Texture(QRhiImplementation *rhi, Format format, const QSize
{
}
bool QGles2Texture::isShareable() const
{
return true;
}
void QGles2Texture::release()
{
if (!texture)
......@@ -1883,14 +1953,17 @@ void QGles2Texture::release()
specified = false;
nativeHandlesStruct.texture = 0;
QRHI_RES_RHI(QRhiGles2);
if (owns)
rhiD->releaseQueue.append(e);
QRHI_PROF;
QRHI_PROF_F(releaseTexture(this));
rhiD->unregisterResource(this);
if (!orphanedWithRsh) {
QRHI_RES_RHI(QRhiGles2);
if (owns)
rhiD->releaseQueue.append(e);
QRHI_PROF;
QRHI_PROF_F(releaseTexture(this));
rhiD->unregisterResource(this);
} else {
// associated rhi is already gone, queue the deferred release to the rsh instead
addToRshReleaseQueue(orphanedWithRsh, e);
}
}
static inline bool isPowerOfTwo(int x)
......@@ -1901,11 +1974,13 @@ static inline bool isPowerOfTwo(int x)
bool QGles2Texture::prepareBuild(QSize *adjustedSize)
{
QRHI_RES_RHI(QRhiGles2);
if (!QRhiImplementation::orphanCheck(this))
return false;
if (texture)
release();
QRHI_RES_RHI(QRhiGles2);
if (!rhiD->ensureContext())
return false;
......@@ -1943,12 +2018,11 @@ bool QGles2Texture::prepareBuild(QSize *adjustedSize)
bool QGles2Texture::build()
{
QRHI_RES_RHI(QRhiGles2);
QSize size;
if (!prepareBuild(&size))
return false;
QRHI_RES_RHI(QRhiGles2);
rhiD->f->glGenTextures(1, &texture);
const bool isCube = m_flags.testFlag(CubeMap);
......@@ -1990,8 +2064,6 @@ bool QGles2Texture::build()
bool QGles2Texture::buildFrom(const QRhiNativeHandles *src)
{
QRHI_RES_RHI(QRhiGles2);
const QRhiGles2TextureNativeHandles *h = static_cast<const QRhiGles2TextureNativeHandles *>(src);
if (!h || !h->texture)
return false;
......@@ -2002,6 +2074,7 @@ bool QGles2Texture::buildFrom(const QRhiNativeHandles *src)
texture = h->texture;
specified = true;
QRHI_RES_RHI(QRhiGles2);
QRHI_PROF;
QRHI_PROF_F(newTexture(this, false, mipLevelCount, m_flags.testFlag(CubeMap) ? 6 : 1, 1));
......@@ -2024,6 +2097,11 @@ QGles2Sampler::QGles2Sampler(QRhiImplementation *rhi, Filter magFilter, Filter m
{
}
bool QGles2Sampler::isShareable() const
{
return true;
}
void QGles2Sampler::release()
{
// nothing to do here
......@@ -2031,6 +2109,8 @@ void QGles2Sampler::release()
bool QGles2Sampler::build()
{
// no backing GL object -> no orphanCheck() since it is never registered
glminfilter = toGlMinFilter(m_minFilter, m_mipmapMode);
glmagfilter = toGlMagFilter(m_magFilter);
glwraps = toGlWrapMode(m_addressU);
......
......@@ -55,6 +55,7 @@ class QRhiResourceSharingHostPrivate;
struct QGles2Buffer : public QRhiBuffer
{
QGles2Buffer(QRhiImplementation *rhi, Type type, UsageFlags usage, int size);
bool isShareable() const override;
void release() override;
bool build() override;
......@@ -77,6 +78,7 @@ struct QGles2RenderBuffer : public QRhiRenderBuffer
{
QGles2RenderBuffer(QRhiImplementation *rhi, Type type, const QSize &pixelSize,
int sampleCount, QRhiRenderBuffer::Flags flags);
bool isShareable() const override;
void release() override;
bool build() override;
QRhiTexture::Format backingFormat() const override;
......@@ -90,6 +92,7 @@ struct QGles2Texture : public QRhiTexture
{
QGles2Texture(QRhiImplementation *rhi, Format format, const QSize &pixelSize,
int sampleCount, Flags flags);
bool isShareable() const override;
void release() override;
bool build() override;
bool buildFrom(const QRhiNativeHandles *src) override;
......@@ -115,6 +118,7 @@ struct QGles2Sampler : public QRhiSampler
{
QGles2Sampler(QRhiImplementation *rhi, Filter magFilter, Filter minFilter, Filter mipmapMode,
AddressMode u, AddressMode v, AddressMode w);
bool isShareable() const override;
void release() override;
bool build() override;
......@@ -570,6 +574,8 @@ public:
};
QVector<DeferredReleaseEntry> releaseQueue;
void executeDeferredReleasesOnRshNow(QVector<QRhiGles2::DeferredReleaseEntry> *rshRelQueue);
struct OffscreenFrame {
OffscreenFrame(QRhiImplementation *rhi) : cbWrapper(rhi) { }
bool active = false;
......
......@@ -72,6 +72,7 @@ public:
#ifndef QT_NO_OPENGL
struct {
QOpenGLContext *dummyShareContext = nullptr;
void *releaseQueue = nullptr;
} d_gles2;
#endif
#if QT_CONFIG(vulkan)
......
res.sh.: gl
res.sh.: mtl
res.sh.: example to show read/write same texture
threading with rsh with resource - more external sync needed
......@@ -49,6 +48,7 @@ dxc for d3d as an alternative to fxc?
hlsl -> dxc -> spirv -> spirv-cross hmmm...
+++ done
res.sh.: gl
res.sh.: exercise it in multiwindow_threaded too
res.sh.: vk, d3d
res.sh.: rhi resource tracking, isSharable, orphaning, ...
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment