From 913146ccd401216f71f037ea304b5e61b7a138cf Mon Sep 17 00:00:00 2001 From: Paul Olav Tvete Date: Fri, 29 Nov 2019 12:41:38 +0100 Subject: RHI: new native texture API The new version takes/returns a value that can be unpacked and passed to other functions without knowing which backend is in use. The old API will be removed in a later change when dependent modules have been updated Task-number: QTBUG-78570 Change-Id: I18d928ceef3cb617c0c509ecccb345551a7990af Reviewed-by: Laszlo Agocs --- src/gui/rhi/qrhi.cpp | 67 ++++++++++++++++++++++++++++++ src/gui/rhi/qrhi_p.h | 7 ++++ src/gui/rhi/qrhid3d11.cpp | 28 +++++++++++++ src/gui/rhi/qrhid3d11_p_p.h | 2 + src/gui/rhi/qrhigles2.cpp | 29 +++++++++++++ src/gui/rhi/qrhigles2_p_p.h | 2 + src/gui/rhi/qrhimetal.mm | 29 +++++++++++++ src/gui/rhi/qrhimetal_p_p.h | 2 + src/gui/rhi/qrhinull.cpp | 6 +++ src/gui/rhi/qrhinull_p_p.h | 1 + src/gui/rhi/qrhivulkan.cpp | 30 +++++++++++++ src/gui/rhi/qrhivulkan_p_p.h | 2 + tests/auto/gui/rhi/qrhi/tst_qrhi.cpp | 67 ++++++++++++++++++++++++++++++ tests/manual/rhi/texuploads/texuploads.cpp | 8 ++-- 14 files changed, 276 insertions(+), 4 deletions(-) diff --git a/src/gui/rhi/qrhi.cpp b/src/gui/rhi/qrhi.cpp index ece5190ff7..58f30deb41 100644 --- a/src/gui/rhi/qrhi.cpp +++ b/src/gui/rhi/qrhi.cpp @@ -2159,6 +2159,32 @@ QRhiResource::Type QRhiRenderBuffer::resourceType() const \value ASTC_12x12 */ +/*! + \class QRhiTexture::NativeTexture + \brief Contains information about the underlying native resources of a texture. + */ + +/*! + \variable QRhiTexture::NativeTexture::object + \brief a pointer to the native object handle. + + With OpenGL, the native handle is a GLuint value, so \c object is then a + pointer to a GLuint. With Vulkan, the native handle is a VkImage, so \c + object is a pointer to a VkImage. With Direct3D 11 and Metal \c + object is a pointer to a ID3D11Texture2D or MTLTexture pointer, respectively. + + \note Pay attention to the fact that \a object is always a pointer + to the native texture handle type, even if the native type itself is a + pointer. + */ + +/*! + \variable QRhiTexture::NativeTexture::layout + \brief Specifies the current image layout for APIs like Vulkan. + + For Vulkan, \c layout contains a \c VkImageLayout value. + */ + /*! \internal */ @@ -2196,11 +2222,24 @@ QRhiResource::Type QRhiTexture::resourceType() const \sa QRhiVulkanTextureNativeHandles, QRhiD3D11TextureNativeHandles, QRhiMetalTextureNativeHandles, QRhiGles2TextureNativeHandles */ +// TODO: remove this version once QtQuick has stopped using it const QRhiNativeHandles *QRhiTexture::nativeHandles() { return nullptr; } +/*! + \return the underlying native resources for this texture. The returned value + will be empty if exposing the underlying native resources is not supported by + the backend. + + \sa buildFrom() + */ +QRhiTexture::NativeTexture QRhiTexture::nativeTexture() +{ + return {}; +} + /*! Similar to build() except that no new native textures are created. Instead, the texture from \a src is used. @@ -2224,12 +2263,40 @@ const QRhiNativeHandles *QRhiTexture::nativeHandles() \sa QRhiVulkanTextureNativeHandles, QRhiD3D11TextureNativeHandles, QRhiMetalTextureNativeHandles, QRhiGles2TextureNativeHandles */ +// TODO: remove this version once QtQuick has stopped using it bool QRhiTexture::buildFrom(const QRhiNativeHandles *src) { Q_UNUSED(src); return false; } +/*! + Similar to build() except that no new native textures are created. Instead, + the native texture resources specified by \a src is used. + + This allows importing an existing native texture object (which must belong + to the same device or sharing context, depending on the graphics API) from + an external graphics engine. + + \note format(), pixelSize(), sampleCount(), and flags() must still be set + correctly. Passing incorrect sizes and other values to QRhi::newTexture() + and then following it with a buildFrom() expecting that the native texture + object alone is sufficient to deduce such values is \b wrong and will lead + to problems. + + \note QRhiTexture does not take ownership of the texture object. release() + does not free the object or any associated memory. + + The opposite of this operation, exposing a QRhiTexture-created native + texture object to a foreign engine, is possible via nativeTexture(). + +*/ +bool QRhiTexture::buildFrom(QRhiTexture::NativeTexture src) +{ + Q_UNUSED(src); + return false; +} + /*! \class QRhiSampler \internal diff --git a/src/gui/rhi/qrhi_p.h b/src/gui/rhi/qrhi_p.h index 6fb9c44ae6..44118b2f10 100644 --- a/src/gui/rhi/qrhi_p.h +++ b/src/gui/rhi/qrhi_p.h @@ -760,6 +760,11 @@ public: ASTC_12x12 }; + struct NativeTexture { + const void *object; + int layout; + }; + QRhiResource::Type resourceType() const override; Format format() const { return m_format; } @@ -776,7 +781,9 @@ public: virtual bool build() = 0; virtual const QRhiNativeHandles *nativeHandles(); + virtual NativeTexture nativeTexture(); virtual bool buildFrom(const QRhiNativeHandles *src); + virtual bool buildFrom(NativeTexture src); protected: QRhiTexture(QRhiImplementation *rhi, Format format_, const QSize &pixelSize_, diff --git a/src/gui/rhi/qrhid3d11.cpp b/src/gui/rhi/qrhid3d11.cpp index 52109edce0..ba2488bffb 100644 --- a/src/gui/rhi/qrhid3d11.cpp +++ b/src/gui/rhi/qrhid3d11.cpp @@ -2764,11 +2764,39 @@ bool QD3D11Texture::buildFrom(const QRhiNativeHandles *src) return true; } +bool QD3D11Texture::buildFrom(QRhiTexture::NativeTexture src) +{ + auto *srcTex = static_cast(src.object); + if (!srcTex || !*srcTex) + return false; + + if (!prepareBuild()) + return false; + + tex = *srcTex; + + if (!finishBuild()) + return false; + + QRHI_PROF; + QRHI_PROF_F(newTexture(this, false, int(mipLevelCount), m_flags.testFlag(CubeMap) ? 6 : 1, int(sampleDesc.Count))); + + owns = false; + QRHI_RES_RHI(QRhiD3D11); + rhiD->registerResource(this); + return true; +} + const QRhiNativeHandles *QD3D11Texture::nativeHandles() { return &nativeHandlesStruct; } +QRhiTexture::NativeTexture QD3D11Texture::nativeTexture() +{ + return {&nativeHandlesStruct.texture, 0}; +} + ID3D11UnorderedAccessView *QD3D11Texture::unorderedAccessViewForLevel(int level) { if (perLevelViews[level]) diff --git a/src/gui/rhi/qrhid3d11_p_p.h b/src/gui/rhi/qrhid3d11_p_p.h index 13c56b1d6d..8f02c4300b 100644 --- a/src/gui/rhi/qrhid3d11_p_p.h +++ b/src/gui/rhi/qrhid3d11_p_p.h @@ -100,7 +100,9 @@ struct QD3D11Texture : public QRhiTexture void release() override; bool build() override; bool buildFrom(const QRhiNativeHandles *src) override; + bool buildFrom(NativeTexture src) override; const QRhiNativeHandles *nativeHandles() override; + NativeTexture nativeTexture() override; bool prepareBuild(QSize *adjustedSize = nullptr); bool finishBuild(); diff --git a/src/gui/rhi/qrhigles2.cpp b/src/gui/rhi/qrhigles2.cpp index b551980bb3..563d59b318 100644 --- a/src/gui/rhi/qrhigles2.cpp +++ b/src/gui/rhi/qrhigles2.cpp @@ -3404,11 +3404,40 @@ bool QGles2Texture::buildFrom(const QRhiNativeHandles *src) return true; } +bool QGles2Texture::buildFrom(QRhiTexture::NativeTexture src) +{ + const uint *textureId = static_cast(src.object); + if (!textureId || !*textureId) + return false; + + if (!prepareBuild()) + return false; + + texture = *textureId; + specified = true; + + QRHI_RES_RHI(QRhiGles2); + QRHI_PROF; + QRHI_PROF_F(newTexture(this, false, mipLevelCount, m_flags.testFlag(CubeMap) ? 6 : 1, 1)); + + owns = false; + nativeHandlesStruct.texture = texture; + + generation += 1; + rhiD->registerResource(this); + return true; +} + const QRhiNativeHandles *QGles2Texture::nativeHandles() { return &nativeHandlesStruct; } +QRhiTexture::NativeTexture QGles2Texture::nativeTexture() +{ + return {&nativeHandlesStruct.texture, 0}; +} + QGles2Sampler::QGles2Sampler(QRhiImplementation *rhi, Filter magFilter, Filter minFilter, Filter mipmapMode, AddressMode u, AddressMode v) : QRhiSampler(rhi, magFilter, minFilter, mipmapMode, u, v) diff --git a/src/gui/rhi/qrhigles2_p_p.h b/src/gui/rhi/qrhigles2_p_p.h index b0f5e340fb..0283fadb4e 100644 --- a/src/gui/rhi/qrhigles2_p_p.h +++ b/src/gui/rhi/qrhigles2_p_p.h @@ -133,7 +133,9 @@ struct QGles2Texture : public QRhiTexture void release() override; bool build() override; bool buildFrom(const QRhiNativeHandles *src) override; + bool buildFrom(NativeTexture src) override; const QRhiNativeHandles *nativeHandles() override; + NativeTexture nativeTexture() override; bool prepareBuild(QSize *adjustedSize = nullptr); diff --git a/src/gui/rhi/qrhimetal.mm b/src/gui/rhi/qrhimetal.mm index e5af1cfab1..555ed5e79f 100644 --- a/src/gui/rhi/qrhimetal.mm +++ b/src/gui/rhi/qrhimetal.mm @@ -2499,11 +2499,40 @@ bool QMetalTexture::buildFrom(const QRhiNativeHandles *src) return true; } +bool QMetalTexture::buildFrom(QRhiTexture::NativeTexture src) +{ + void * const * tex = (void * const *) src.object; + if (!tex || !*tex) + return false; + + if (!prepareBuild()) + return false; + + d->tex = (id) *tex; + + d->owns = false; + nativeHandlesStruct.texture = d->tex; + + QRHI_PROF; + QRHI_PROF_F(newTexture(this, false, mipLevelCount, m_flags.testFlag(CubeMap) ? 6 : 1, samples)); + + lastActiveFrameSlot = -1; + generation += 1; + QRHI_RES_RHI(QRhiMetal); + rhiD->registerResource(this); + return true; +} + const QRhiNativeHandles *QMetalTexture::nativeHandles() { return &nativeHandlesStruct; } +QRhiTexture::NativeTexture QMetalTexture::nativeTexture() +{ + return {&nativeHandlesStruct.texture, 0}; +} + id QMetalTextureData::viewForLevel(int level) { Q_ASSERT(level >= 0 && level < int(q->mipLevelCount)); diff --git a/src/gui/rhi/qrhimetal_p_p.h b/src/gui/rhi/qrhimetal_p_p.h index 7876539fcd..8e655fd98b 100644 --- a/src/gui/rhi/qrhimetal_p_p.h +++ b/src/gui/rhi/qrhimetal_p_p.h @@ -101,7 +101,9 @@ struct QMetalTexture : public QRhiTexture void release() override; bool build() override; bool buildFrom(const QRhiNativeHandles *src) override; + bool buildFrom(NativeTexture src) override; const QRhiNativeHandles *nativeHandles() override; + NativeTexture nativeTexture() override; bool prepareBuild(QSize *adjustedSize = nullptr); diff --git a/src/gui/rhi/qrhinull.cpp b/src/gui/rhi/qrhinull.cpp index 0baea1b9d6..80f004e049 100644 --- a/src/gui/rhi/qrhinull.cpp +++ b/src/gui/rhi/qrhinull.cpp @@ -651,6 +651,12 @@ bool QNullTexture::buildFrom(const QRhiNativeHandles *src) return true; } +bool QNullTexture::buildFrom(QRhiTexture::NativeTexture src) +{ + Q_UNUSED(src) + return buildFrom(nullptr); +} + const QRhiNativeHandles *QNullTexture::nativeHandles() { return &nativeHandlesStruct; diff --git a/src/gui/rhi/qrhinull_p_p.h b/src/gui/rhi/qrhinull_p_p.h index c96f279f7d..57c3de0418 100644 --- a/src/gui/rhi/qrhinull_p_p.h +++ b/src/gui/rhi/qrhinull_p_p.h @@ -81,6 +81,7 @@ struct QNullTexture : public QRhiTexture void release() override; bool build() override; bool buildFrom(const QRhiNativeHandles *src) override; + bool buildFrom(NativeTexture src) override; const QRhiNativeHandles *nativeHandles() override; QRhiNullTextureNativeHandles nativeHandlesStruct; diff --git a/src/gui/rhi/qrhivulkan.cpp b/src/gui/rhi/qrhivulkan.cpp index 49ca2326bc..21ae142b1d 100644 --- a/src/gui/rhi/qrhivulkan.cpp +++ b/src/gui/rhi/qrhivulkan.cpp @@ -5370,12 +5370,42 @@ bool QVkTexture::buildFrom(const QRhiNativeHandles *src) return true; } +bool QVkTexture::buildFrom(QRhiTexture::NativeTexture src) +{ + auto *img = static_cast(src.object); + if (!img || !*img) + return false; + + if (!prepareBuild()) + return false; + + image = *img; + + if (!finishBuild()) + return false; + + QRHI_PROF; + QRHI_PROF_F(newTexture(this, false, int(mipLevelCount), m_flags.testFlag(CubeMap) ? 6 : 1, samples)); + + usageState.layout = VkImageLayout(src.layout); + + owns = false; + QRHI_RES_RHI(QRhiVulkan); + rhiD->registerResource(this); + return true; +} + const QRhiNativeHandles *QVkTexture::nativeHandles() { nativeHandlesStruct.layout = usageState.layout; return &nativeHandlesStruct; } +QRhiTexture::NativeTexture QVkTexture::nativeTexture() +{ + return {&nativeHandlesStruct.image, usageState.layout}; +} + VkImageView QVkTexture::imageViewForLevel(int level) { Q_ASSERT(level >= 0 && level < int(mipLevelCount)); diff --git a/src/gui/rhi/qrhivulkan_p_p.h b/src/gui/rhi/qrhivulkan_p_p.h index ffa8c59ed5..d1b77870a1 100644 --- a/src/gui/rhi/qrhivulkan_p_p.h +++ b/src/gui/rhi/qrhivulkan_p_p.h @@ -121,7 +121,9 @@ struct QVkTexture : public QRhiTexture void release() override; bool build() override; bool buildFrom(const QRhiNativeHandles *src) override; + bool buildFrom(NativeTexture src) override; const QRhiNativeHandles *nativeHandles() override; + NativeTexture nativeTexture() override; bool prepareBuild(QSize *adjustedSize = nullptr); bool finishBuild(); diff --git a/tests/auto/gui/rhi/qrhi/tst_qrhi.cpp b/tests/auto/gui/rhi/qrhi/tst_qrhi.cpp index 302630ae15..6f88b7fab5 100644 --- a/tests/auto/gui/rhi/qrhi/tst_qrhi.cpp +++ b/tests/auto/gui/rhi/qrhi/tst_qrhi.cpp @@ -73,6 +73,8 @@ private slots: void create(); void nativeHandles_data(); void nativeHandles(); + void nativeTexture_data(); + void nativeTexture(); void resourceUpdateBatchBuffer_data(); void resourceUpdateBatchBuffer(); void resourceUpdateBatchRGBATextureUpload_data(); @@ -528,6 +530,71 @@ void tst_QRhi::nativeHandles() } } +void tst_QRhi::nativeTexture_data() +{ + rhiTestData(); +} + +void tst_QRhi::nativeTexture() +{ + QFETCH(QRhi::Implementation, impl); + QFETCH(QRhiInitParams *, initParams); + + QScopedPointer rhi(QRhi::create(impl, initParams, QRhi::Flags(), nullptr)); + if (!rhi) + QSKIP("QRhi could not be created, skipping testing native texture"); + + QScopedPointer tex(rhi->newTexture(QRhiTexture::RGBA8, QSize(512, 256))); + QVERIFY(tex->build()); + + const QRhiTexture::NativeTexture nativeTex = tex->nativeTexture(); + + switch (impl) { + case QRhi::Null: + break; +#ifdef TST_VK + case QRhi::Vulkan: + { + auto *image = static_cast(nativeTex.object); + QVERIFY(image); + QVERIFY(*image); + QVERIFY(nativeTex.layout >= 1); // VK_IMAGE_LAYOUT_GENERAL + QVERIFY(nativeTex.layout <= 8); // VK_IMAGE_LAYOUT_PREINITIALIZED + } + break; +#endif +#ifdef TST_GL + case QRhi::OpenGLES2: + { + auto *textureId = static_cast(nativeTex.object); + QVERIFY(textureId); + QVERIFY(*textureId); + } + break; +#endif +#ifdef TST_D3D11 + case QRhi::D3D11: + { + auto *texture = static_cast(nativeTex.object); + QVERIFY(texture); + QVERIFY(*texture); + } + break; +#endif +#ifdef TST_MTL + case QRhi::Metal: + { + void * const * texture = (void * const *)nativeTex.object; + QVERIFY(texture); + QVERIFY(*texture); + } + break; +#endif + default: + Q_ASSERT(false); + } +} + static bool submitResourceUpdates(QRhi *rhi, QRhiResourceUpdateBatch *batch) { QRhiCommandBuffer *cb = nullptr; diff --git a/tests/manual/rhi/texuploads/texuploads.cpp b/tests/manual/rhi/texuploads/texuploads.cpp index 091e47b9ea..a6b7d87d3e 100644 --- a/tests/manual/rhi/texuploads/texuploads.cpp +++ b/tests/manual/rhi/texuploads/texuploads.cpp @@ -237,11 +237,11 @@ void Window::customRender() // Exercise texture object export/import. if (d.testStage == 6) { - const QRhiNativeHandles *h = d.tex->nativeHandles(); - if (h) { + const QRhiTexture::NativeTexture nativeTexture = d.tex->nativeTexture(); + if (nativeTexture.object) { #if defined(Q_OS_MACOS) || defined(Q_OS_IOS) if (graphicsApi == Metal) { - qDebug() << "Metal texture: " << static_cast(h)->texture; + qDebug() << "Metal texture: " << *(void**)nativeTexture.object; // Now could cast to id and do something with // it, keeping in mind that copy operations are only done // in beginPass, while rendering into a texture may only @@ -253,7 +253,7 @@ void Window::customRender() d.importedTex = m_r->newTexture(QRhiTexture::RGBA8, d.tex->pixelSize()); d.releasePool << d.importedTex; - if (!d.importedTex->buildFrom(h)) + if (!d.importedTex->buildFrom(nativeTexture)) qWarning("Texture import failed"); // now d.tex and d.importedTex use the same MTLTexture -- cgit v1.2.3