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 <laszlo.agocs@qt.io>
This commit is contained in:
Paul Olav Tvete 2019-11-29 12:41:38 +01:00
parent f43cb31ba0
commit 913146ccd4
14 changed files with 276 additions and 4 deletions

View File

@ -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

View File

@ -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_,

View File

@ -2764,11 +2764,39 @@ bool QD3D11Texture::buildFrom(const QRhiNativeHandles *src)
return true;
}
bool QD3D11Texture::buildFrom(QRhiTexture::NativeTexture src)
{
auto *srcTex = static_cast<ID3D11Texture2D * const *>(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])

View File

@ -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();

View File

@ -3404,11 +3404,40 @@ bool QGles2Texture::buildFrom(const QRhiNativeHandles *src)
return true;
}
bool QGles2Texture::buildFrom(QRhiTexture::NativeTexture src)
{
const uint *textureId = static_cast<const uint *>(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)

View File

@ -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);

View File

@ -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<MTLTexture>) *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<MTLTexture> QMetalTextureData::viewForLevel(int level)
{
Q_ASSERT(level >= 0 && level < int(q->mipLevelCount));

View File

@ -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);

View File

@ -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;

View File

@ -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;

View File

@ -5370,12 +5370,42 @@ bool QVkTexture::buildFrom(const QRhiNativeHandles *src)
return true;
}
bool QVkTexture::buildFrom(QRhiTexture::NativeTexture src)
{
auto *img = static_cast<const VkImage*>(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));

View File

@ -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();

View File

@ -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<QRhi> rhi(QRhi::create(impl, initParams, QRhi::Flags(), nullptr));
if (!rhi)
QSKIP("QRhi could not be created, skipping testing native texture");
QScopedPointer<QRhiTexture> 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<const VkImage *>(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<const uint *>(nativeTex.object);
QVERIFY(textureId);
QVERIFY(*textureId);
}
break;
#endif
#ifdef TST_D3D11
case QRhi::D3D11:
{
auto *texture = static_cast<void * const *>(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;

View File

@ -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<const QRhiMetalTextureNativeHandles *>(h)->texture;
qDebug() << "Metal texture: " << *(void**)nativeTexture.object;
// Now could cast to id<MTLTexture> 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