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:
parent
f43cb31ba0
commit
913146ccd4
@ -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
|
||||
|
@ -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_,
|
||||
|
@ -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])
|
||||
|
@ -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();
|
||||
|
@ -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)
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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));
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -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));
|
||||
|
@ -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();
|
||||
|
@ -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;
|
||||
|
@ -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
|
||||
|
Loading…
Reference in New Issue
Block a user