summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--src/gui/rhi/qrhi.cpp104
-rw-r--r--src/gui/rhi/qrhi_p.h21
-rw-r--r--src/gui/rhi/qrhi_p_p.h1
-rw-r--r--src/gui/rhi/qrhid3d11.cpp81
-rw-r--r--src/gui/rhi/qrhid3d11_p_p.h3
-rw-r--r--src/gui/rhi/qrhigles2.cpp132
-rw-r--r--src/gui/rhi/qrhigles2_p_p.h6
-rw-r--r--src/gui/rhi/qrhimetal.mm51
-rw-r--r--src/gui/rhi/qrhimetal_p_p.h3
-rw-r--r--src/gui/rhi/qrhinull.cpp17
-rw-r--r--src/gui/rhi/qrhinull_p_p.h3
-rw-r--r--src/gui/rhi/qrhivulkan.cpp54
-rw-r--r--src/gui/rhi/qrhivulkan_p_p.h3
-rw-r--r--tests/auto/gui/rhi/qrhi/BLACKLIST3
-rw-r--r--tests/auto/gui/rhi/qrhi/tst_qrhi.cpp132
-rw-r--r--tests/manual/rhi/CMakeLists.txt1
-rw-r--r--tests/manual/rhi/shared/buildshaders.bat2
-rw-r--r--tests/manual/rhi/shared/examplefw.h14
-rw-r--r--tests/manual/rhi/shared/texture_arr.frag19
-rw-r--r--tests/manual/rhi/shared/texture_arr.frag.qsbbin0 -> 1448 bytes
-rw-r--r--tests/manual/rhi/shared/texture_arr.vert20
-rw-r--r--tests/manual/rhi/shared/texture_arr.vert.qsbbin0 -> 1404 bytes
-rw-r--r--tests/manual/rhi/texturearray/CMakeLists.txt26
-rw-r--r--tests/manual/rhi/texturearray/texturearray.cpp198
24 files changed, 780 insertions, 114 deletions
diff --git a/src/gui/rhi/qrhi.cpp b/src/gui/rhi/qrhi.cpp
index 82a304865e..e6bc19acde 100644
--- a/src/gui/rhi/qrhi.cpp
+++ b/src/gui/rhi/qrhi.cpp
@@ -680,6 +680,11 @@ Q_LOGGING_CATEGORY(QRHI_LOG_INFO, "qt.rhi.general")
texture is supported. This can be unsupported with Vulkan 1.0 due to
relying on VK_IMAGE_CREATE_2D_ARRAY_COMPATIBLE_BIT which is a Vulkan 1.1
feature.
+
+ \value TextureArrays Indicates that texture arrays are supported and
+ QRhi::newTextureArray() is functional. Note that even when texture arrays
+ are not supported, arrays of textures are still available as those are two
+ independent features.
*/
/*!
@@ -767,6 +772,10 @@ Q_LOGGING_CATEGORY(QRHI_LOG_INFO, "qt.rhi.general")
\value MaxThreadGroupZ The maximum size of a work/thread group in the Z
dimension. Effectively the maximum value of \c local_size_z in the compute
shader. Typically 64 or 256.
+
+ \value TextureArraySizeMax Maximum texture array size. Typically in range
+ 256 - 2048. Attempting to \l{QRhi::newTextureArray()}{create a texture
+ array} with more elements will likely fail.
*/
/*!
@@ -1503,7 +1512,7 @@ QDebug operator<<(QDebug dbg, const QRhiShaderStage &s)
When targeting a non-multisample texture, the layer() and level() indicate
the targeted layer (face index \c{0-5} for cubemaps) and mip level. For 3D
textures layer() specifies the slice (one 2D image within the 3D texture)
- to render to.
+ to render to. For texture arrays layer() is the array index.
When texture() or renderBuffer() is multisample, resolveTexture() can be
set optionally. When set, samples are resolved automatically into that
@@ -1700,7 +1709,9 @@ QRhiTextureSubresourceUploadDescription::QRhiTextureSubresourceUploadDescription
\class QRhiTextureUploadEntry
\internal
\inmodule QtGui
- \brief Describes one layer (face for cubemaps) in a texture upload operation.
+
+ \brief Describes one layer (face for cubemaps, slice for 3D textures,
+ element for texture arrays) in a texture upload operation.
*/
/*!
@@ -1840,12 +1851,12 @@ QRhiTextureUploadDescription::QRhiTextureUploadDescription(std::initializer_list
sourceTopLeft(), and destinationTopLeft() must fit the source and
destination textures, respectively. The behavior is undefined otherwise.
- With cubemap and 3D textures one face or slice can be copied at a time. The
- face or slice is specified by the source and destination layer indices.
- With mipmapped textures one mip level can be copied at a time. The source
- and destination layer and mip level indices can differ, but the size and
- position must be carefully controlled to avoid out of bounds copies, in
- which case the behavior is undefined.
+ With cubemaps, 3D textures, and texture arrays one face or slice can be
+ copied at a time. The face or slice is specified by the source and
+ destination layer indices. With mipmapped textures one mip level can be
+ copied at a time. The source and destination layer and mip level indices can
+ differ, but the size and position must be carefully controlled to avoid out
+ of bounds copies, in which case the behavior is undefined.
*/
/*!
@@ -2440,10 +2451,11 @@ bool QRhiRenderBuffer::createFrom(NativeRenderBuffer src)
\value ThreeDimensional The texture is a 3D texture. Such textures should
be created with the QRhi::newTexture() overload taking a depth in addition
to width and height. A 3D texture can have mipmaps but cannot be
- multisample. When rendering into a 3D texture, the layer specified in the
- render target's color attachment refers to a slice in range [0..depth-1].
- The underlying graphics API may not support 3D textures at run time.
- Support is indicated by the QRhi::ThreeDimensionalTextures feature.
+ multisample. When rendering into, or uploading data to a 3D texture, the \c
+ layer specified in the render target's color attachment or the upload
+ description refers to a single slice in range [0..depth-1]. The underlying
+ graphics API may not support 3D textures at run time. Support is indicated
+ by the QRhi::ThreeDimensionalTextures feature.
\value TextureRectangleGL The texture should use the GL_TEXTURE_RECTANGLE
target with OpenGL. This flag is ignored with other graphics APIs. Just
@@ -2451,6 +2463,14 @@ bool QRhiRenderBuffer::createFrom(NativeRenderBuffer src)
native OpenGL texture objects received from the platform are wrapped in a
QRhiTexture, and the platform can only provide textures for a non-2D
texture target.
+
+ \value TextureArray The texture is a texture array, i.e. a single texture
+ object that is a homogeneous array of 2D textures. Texture arrays are
+ created with QRhi::newTextureArray(). The underlying graphics API may not
+ support texture array objects at run time. Support is indicated by the
+ QRhi::TextureArrays feature. When rendering into, or uploading data to a
+ texture array, the \c layer specified in the render target's color
+ attachment or the upload description selects a single element in the array.
*/
/*!
@@ -2542,9 +2562,10 @@ bool QRhiRenderBuffer::createFrom(NativeRenderBuffer src)
\internal
*/
QRhiTexture::QRhiTexture(QRhiImplementation *rhi, Format format_, const QSize &pixelSize_, int depth_,
- int sampleCount_, Flags flags_)
+ int arraySize_, int sampleCount_, Flags flags_)
: QRhiResource(rhi),
- m_format(format_), m_pixelSize(pixelSize_), m_depth(depth_), m_sampleCount(sampleCount_), m_flags(flags_)
+ m_format(format_), m_pixelSize(pixelSize_), m_depth(depth_),
+ m_arraySize(arraySize_), m_sampleCount(sampleCount_), m_flags(flags_)
{
}
@@ -2599,6 +2620,10 @@ QRhiTexture::NativeTexture QRhiTexture::nativeTexture()
The opposite of this operation, exposing a QRhiTexture-created native
texture object to a foreign engine, is possible via nativeTexture().
+ \note When importing a 3D texture, or a texture array object, or, with
+ OpenGL ES, an external texture, it is then especially important to set the
+ corresponding flags (ThreeDimensional, TextureArray, ExternalOES) via
+ setFlags() before calling this function.
*/
bool QRhiTexture::createFrom(QRhiTexture::NativeTexture src)
{
@@ -3118,8 +3143,9 @@ void QRhiImplementation::updateLayoutDesc(QRhiShaderResourceBindings *srb)
\inmodule QtGui
\brief Describes the shader resource for a single binding point.
- A QRhiShaderResourceBinding cannot be constructed directly. Instead, use
- the static functions uniformBuffer(), sampledTexture() to get an instance.
+ A QRhiShaderResourceBinding cannot be constructed directly. Instead, use the
+ static functions such as uniformBuffer() or sampledTexture() to get an
+ instance.
*/
/*!
@@ -5432,8 +5458,8 @@ void QRhiResourceUpdateBatch::copyTexture(QRhiTexture *dst, QRhiTexture *src, co
QRhi::MaxAsyncReadbackFrames.
A single readback operation copies one mip level of one layer (cubemap face
- or 3D slice) at a time. The level and layer are specified by the respective
- fields in \a rb.
+ or 3D slice or texture array element) at a time. The level and layer are
+ specified by the respective fields in \a rb.
\sa readBackBuffer(), QRhi::resourceLimit()
*/
@@ -6496,7 +6522,7 @@ QRhiRenderBuffer *QRhi::newRenderBuffer(QRhiRenderBuffer::Type type,
}
/*!
- \return a new texture with the specified \a format, \a pixelSize, \a
+ \return a new 2D texture with the specified \a format, \a pixelSize, \a
sampleCount, and \a flags.
\note \a format specifies the requested internal and external format,
@@ -6511,12 +6537,12 @@ QRhiTexture *QRhi::newTexture(QRhiTexture::Format format,
int sampleCount,
QRhiTexture::Flags flags)
{
- return d->createTexture(format, pixelSize, 1, sampleCount, flags);
+ return d->createTexture(format, pixelSize, 1, 0, sampleCount, flags);
}
/*!
- \return a new texture with the specified \a format, \a width, \a height, \a
- depth, \a sampleCount, and \a flags.
+ \return a new 2D or 3D texture with the specified \a format, \a width, \a
+ height, \a depth, \a sampleCount, and \a flags.
This overload is suitable for 3D textures because it allows specifying \a
depth. A 3D texture must have QRhiTexture::ThreeDimensional set in \a
@@ -6524,6 +6550,9 @@ QRhiTexture *QRhi::newTexture(QRhiTexture::Format format,
implicitly whenever \a depth is greater than 0. For 2D and cube textures \a
depth should be set to 0.
+ \note 3D textures are only functional when the ThreeDimensionalTextures
+ feature is reported as supported at run time.
+
\overload
*/
QRhiTexture *QRhi::newTexture(QRhiTexture::Format format,
@@ -6534,7 +6563,36 @@ QRhiTexture *QRhi::newTexture(QRhiTexture::Format format,
if (depth > 0)
flags |= QRhiTexture::ThreeDimensional;
- return d->createTexture(format, QSize(width, height), depth, sampleCount, flags);
+ return d->createTexture(format, QSize(width, height), depth, 0, sampleCount, flags);
+}
+
+/*!
+ \return a new 2D texture array with the specified \a format, \a arraySize,
+ \a pixelSize, \a sampleCount, and \a flags.
+
+ This function implicitly sets QRhiTexture::TextureArray in \a flags.
+
+ \note Do not confuse texture arrays with arrays of textures. A QRhiTexture
+ created by this function is usable with 2D array samplers in the shader, for
+ example: \c{layout(binding = 1) uniform sampler2DArray texArr;}. Arrays of
+ textures refers to a list of textures that are exposed to the shader via
+ QRhiShaderResourceBinding::sampledTextures() and a count > 1, and declared
+ in the shader for example like this: \c{layout(binding = 1) uniform
+ sampler2D textures[4];}
+
+ \note This is only functional when the TextureArrays feature is reported as
+ supported at run time.
+
+ \sa newTexture()
+ */
+QRhiTexture *QRhi::newTextureArray(QRhiTexture::Format format,
+ int arraySize,
+ const QSize &pixelSize,
+ int sampleCount,
+ QRhiTexture::Flags flags)
+{
+ flags |= QRhiTexture::TextureArray;
+ return d->createTexture(format, pixelSize, 1, arraySize, sampleCount, flags);
}
/*!
diff --git a/src/gui/rhi/qrhi_p.h b/src/gui/rhi/qrhi_p.h
index 17b170203b..4decdd0c91 100644
--- a/src/gui/rhi/qrhi_p.h
+++ b/src/gui/rhi/qrhi_p.h
@@ -773,7 +773,8 @@ public:
UsedAsCompressedAtlas = 1 << 8,
ExternalOES = 1 << 9,
ThreeDimensional = 1 << 10,
- TextureRectangleGL = 1 << 11
+ TextureRectangleGL = 1 << 11,
+ TextureArray = 1 << 12
};
Q_DECLARE_FLAGS(Flags, Flag)
@@ -842,6 +843,9 @@ public:
int depth() const { return m_depth; }
void setDepth(int depth) { m_depth = depth; }
+ int arraySize() const { return m_arraySize; }
+ void setArraySize(int arraySize) { m_arraySize = arraySize; }
+
Flags flags() const { return m_flags; }
void setFlags(Flags f) { m_flags = f; }
@@ -855,10 +859,11 @@ public:
protected:
QRhiTexture(QRhiImplementation *rhi, Format format_, const QSize &pixelSize_, int depth_,
- int sampleCount_, Flags flags_);
+ int arraySize_, int sampleCount_, Flags flags_);
Format m_format;
QSize m_pixelSize;
int m_depth;
+ int m_arraySize;
int m_sampleCount;
Flags m_flags;
};
@@ -1583,7 +1588,8 @@ public:
ImageDataStride,
RenderBufferImport,
ThreeDimensionalTextures,
- RenderTo3DTextureSlice
+ RenderTo3DTextureSlice,
+ TextureArrays
};
enum BeginFrameFlag {
@@ -1605,7 +1611,8 @@ public:
MaxThreadsPerThreadGroup,
MaxThreadGroupX,
MaxThreadGroupY,
- MaxThreadGroupZ
+ MaxThreadGroupZ,
+ TextureArraySizeMax
};
~QRhi();
@@ -1648,6 +1655,12 @@ public:
int sampleCount = 1,
QRhiTexture::Flags flags = {});
+ QRhiTexture *newTextureArray(QRhiTexture::Format format,
+ int arraySize,
+ const QSize &pixelSize,
+ int sampleCount = 1,
+ QRhiTexture::Flags flags = {});
+
QRhiSampler *newSampler(QRhiSampler::Filter magFilter,
QRhiSampler::Filter minFilter,
QRhiSampler::Filter mipmapMode,
diff --git a/src/gui/rhi/qrhi_p_p.h b/src/gui/rhi/qrhi_p_p.h
index 0dcb8c1f6e..7a3f94846d 100644
--- a/src/gui/rhi/qrhi_p_p.h
+++ b/src/gui/rhi/qrhi_p_p.h
@@ -88,6 +88,7 @@ public:
virtual QRhiTexture *createTexture(QRhiTexture::Format format,
const QSize &pixelSize,
int depth,
+ int arraySize,
int sampleCount,
QRhiTexture::Flags flags) = 0;
virtual QRhiSampler *createSampler(QRhiSampler::Filter magFilter,
diff --git a/src/gui/rhi/qrhid3d11.cpp b/src/gui/rhi/qrhid3d11.cpp
index 8e94d70502..631e7f7adf 100644
--- a/src/gui/rhi/qrhid3d11.cpp
+++ b/src/gui/rhi/qrhid3d11.cpp
@@ -543,6 +543,8 @@ bool QRhiD3D11::isFeatureSupported(QRhi::Feature feature) const
return true;
case QRhi::RenderTo3DTextureSlice:
return true;
+ case QRhi::TextureArrays:
+ return true;
default:
Q_UNREACHABLE();
return false;
@@ -576,6 +578,8 @@ int QRhiD3D11::resourceLimit(QRhi::ResourceLimit limit) const
return D3D11_CS_THREAD_GROUP_MAX_Y;
case QRhi::MaxThreadGroupZ:
return D3D11_CS_THREAD_GROUP_MAX_Z;
+ case QRhi::TextureArraySizeMax:
+ return D3D11_REQ_TEXTURE2D_ARRAY_AXIS_DIMENSION;
default:
Q_UNREACHABLE();
return 0;
@@ -631,10 +635,10 @@ QRhiRenderBuffer *QRhiD3D11::createRenderBuffer(QRhiRenderBuffer::Type type, con
}
QRhiTexture *QRhiD3D11::createTexture(QRhiTexture::Format format,
- const QSize &pixelSize, int depth,
+ const QSize &pixelSize, int depth, int arraySize,
int sampleCount, QRhiTexture::Flags flags)
{
- return new QD3D11Texture(this, format, pixelSize, depth, sampleCount, flags);
+ return new QD3D11Texture(this, format, pixelSize, depth, arraySize, sampleCount, flags);
}
QRhiSampler *QRhiD3D11::createSampler(QRhiSampler::Filter magFilter, QRhiSampler::Filter minFilter,
@@ -2907,8 +2911,8 @@ QRhiTexture::Format QD3D11RenderBuffer::backingFormat() const
}
QD3D11Texture::QD3D11Texture(QRhiImplementation *rhi, Format format, const QSize &pixelSize, int depth,
- int sampleCount, Flags flags)
- : QRhiTexture(rhi, format, pixelSize, depth, sampleCount, flags)
+ int arraySize, int sampleCount, Flags flags)
+ : QRhiTexture(rhi, format, pixelSize, depth, arraySize, sampleCount, flags)
{
for (int i = 0; i < QRhi::MAX_MIP_LEVELS; ++i)
perLevelViews[i] = nullptr;
@@ -2997,6 +3001,7 @@ bool QD3D11Texture::prepareCreate(QSize *adjustedSize)
const bool isDepth = isDepthTextureFormat(m_format);
const bool isCube = m_flags.testFlag(CubeMap);
const bool is3D = m_flags.testFlag(ThreeDimensional);
+ const bool isArray = m_flags.testFlag(TextureArray);
const bool hasMipMaps = m_flags.testFlag(MipMapped);
QRHI_RES_RHI(QRhiD3D11);
@@ -3025,11 +3030,24 @@ bool QD3D11Texture::prepareCreate(QSize *adjustedSize)
qWarning("Texture cannot be both cube and 3D");
return false;
}
+ if (isArray && is3D) {
+ qWarning("Texture cannot be both array and 3D");
+ return false;
+ }
m_depth = qMax(1, m_depth);
if (m_depth > 1 && !is3D) {
qWarning("Texture cannot have a depth of %d when it is not 3D", m_depth);
return false;
}
+ m_arraySize = qMax(0, m_arraySize);
+ if (m_arraySize > 0 && !isArray) {
+ qWarning("Texture cannot have an array size of %d when it is not an array", m_arraySize);
+ return false;
+ }
+ if (m_arraySize < 1 && isArray) {
+ qWarning("Texture is an array but array size is %d", m_arraySize);
+ return false;
+ }
if (adjustedSize)
*adjustedSize = size;
@@ -3043,6 +3061,7 @@ bool QD3D11Texture::finishCreate()
const bool isDepth = isDepthTextureFormat(m_format);
const bool isCube = m_flags.testFlag(CubeMap);
const bool is3D = m_flags.testFlag(ThreeDimensional);
+ const bool isArray = m_flags.testFlag(TextureArray);
D3D11_SHADER_RESOURCE_VIEW_DESC srvDesc;
memset(&srvDesc, 0, sizeof(srvDesc));
@@ -3051,14 +3070,27 @@ bool QD3D11Texture::finishCreate()
srvDesc.ViewDimension = D3D11_SRV_DIMENSION_TEXTURECUBE;
srvDesc.TextureCube.MipLevels = mipLevelCount;
} else {
- if (sampleDesc.Count > 1) {
- srvDesc.ViewDimension = D3D11_SRV_DIMENSION_TEXTURE2DMS;
- } else if (is3D) {
- srvDesc.ViewDimension = D3D11_SRV_DIMENSION_TEXTURE3D;
- srvDesc.Texture3D.MipLevels = mipLevelCount;
+ if (isArray) {
+ if (sampleDesc.Count > 1) {
+ srvDesc.ViewDimension = D3D11_SRV_DIMENSION_TEXTURE2DMSARRAY;
+ srvDesc.Texture2DMSArray.FirstArraySlice = 0;
+ srvDesc.Texture2DMSArray.ArraySize = UINT(m_arraySize);
+ } else {
+ srvDesc.ViewDimension = D3D11_SRV_DIMENSION_TEXTURE2DARRAY;
+ srvDesc.Texture2DArray.MipLevels = mipLevelCount;
+ srvDesc.Texture2DArray.FirstArraySlice = 0;
+ srvDesc.Texture2DArray.ArraySize = UINT(m_arraySize);
+ }
} else {
- srvDesc.ViewDimension = D3D11_SRV_DIMENSION_TEXTURE2D;
- srvDesc.Texture2D.MipLevels = mipLevelCount;
+ if (sampleDesc.Count > 1) {
+ srvDesc.ViewDimension = D3D11_SRV_DIMENSION_TEXTURE2DMS;
+ } else if (is3D) {
+ srvDesc.ViewDimension = D3D11_SRV_DIMENSION_TEXTURE3D;
+ srvDesc.Texture3D.MipLevels = mipLevelCount;
+ } else {
+ srvDesc.ViewDimension = D3D11_SRV_DIMENSION_TEXTURE2D;
+ srvDesc.Texture2D.MipLevels = mipLevelCount;
+ }
}
}
@@ -3081,6 +3113,7 @@ bool QD3D11Texture::create()
const bool isDepth = isDepthTextureFormat(m_format);
const bool isCube = m_flags.testFlag(CubeMap);
const bool is3D = m_flags.testFlag(ThreeDimensional);
+ const bool isArray = m_flags.testFlag(TextureArray);
uint bindFlags = D3D11_BIND_SHADER_RESOURCE;
uint miscFlags = isCube ? D3D11_RESOURCE_MISC_TEXTURECUBE : 0;
@@ -3108,7 +3141,7 @@ bool QD3D11Texture::create()
desc.Width = UINT(size.width());
desc.Height = UINT(size.height());
desc.MipLevels = mipLevelCount;
- desc.ArraySize = isCube ? 6 : 1;
+ desc.ArraySize = isCube ? 6 : (isArray ? UINT(m_arraySize) : 1);
desc.Format = dxgiFormat;
desc.SampleDesc = sampleDesc;
desc.Usage = D3D11_USAGE_DEFAULT;
@@ -3147,7 +3180,7 @@ bool QD3D11Texture::create()
return false;
QRHI_PROF;
- QRHI_PROF_F(newTexture(this, true, int(mipLevelCount), isCube ? 6 : 1, int(sampleDesc.Count)));
+ QRHI_PROF_F(newTexture(this, true, int(mipLevelCount), isCube ? 6 : (isArray ? m_arraySize : 1), int(sampleDesc.Count)));
owns = true;
rhiD->registerResource(this);
@@ -3170,8 +3203,11 @@ bool QD3D11Texture::createFrom(QRhiTexture::NativeTexture src)
if (!finishCreate())
return false;
+ const bool isCube = m_flags.testFlag(CubeMap);
+ const bool isArray = m_flags.testFlag(TextureArray);
+
QRHI_PROF;
- QRHI_PROF_F(newTexture(this, false, int(mipLevelCount), m_flags.testFlag(CubeMap) ? 6 : 1, int(sampleDesc.Count)));
+ QRHI_PROF_F(newTexture(this, false, int(mipLevelCount), isCube ? 6 : (isArray ? m_arraySize : 1), int(sampleDesc.Count)));
owns = false;
QRHI_RES_RHI(QRhiD3D11);
@@ -3190,6 +3226,7 @@ ID3D11UnorderedAccessView *QD3D11Texture::unorderedAccessViewForLevel(int level)
return perLevelViews[level];
const bool isCube = m_flags.testFlag(CubeMap);
+ const bool isArray = m_flags.testFlag(TextureArray);
const bool is3D = m_flags.testFlag(ThreeDimensional);
D3D11_UNORDERED_ACCESS_VIEW_DESC desc;
memset(&desc, 0, sizeof(desc));
@@ -3199,6 +3236,11 @@ ID3D11UnorderedAccessView *QD3D11Texture::unorderedAccessViewForLevel(int level)
desc.Texture2DArray.MipSlice = UINT(level);
desc.Texture2DArray.FirstArraySlice = 0;
desc.Texture2DArray.ArraySize = 6;
+ } else if (isArray) {
+ desc.ViewDimension = D3D11_UAV_DIMENSION_TEXTURE2DARRAY;
+ desc.Texture2DArray.MipSlice = UINT(level);
+ desc.Texture2DArray.FirstArraySlice = 0;
+ desc.Texture2DArray.ArraySize = UINT(m_arraySize);
} else if (is3D) {
desc.ViewDimension = D3D11_UAV_DIMENSION_TEXTURE3D;
desc.Texture3D.MipSlice = UINT(level);
@@ -3483,6 +3525,17 @@ bool QD3D11TextureRenderTarget::create()
rtvDesc.Texture2DArray.MipSlice = UINT(colorAtt.level());
rtvDesc.Texture2DArray.FirstArraySlice = UINT(colorAtt.layer());
rtvDesc.Texture2DArray.ArraySize = 1;
+ } else if (texD->flags().testFlag(QRhiTexture::TextureArray)) {
+ if (texD->sampleDesc.Count > 1) {
+ rtvDesc.ViewDimension = D3D11_RTV_DIMENSION_TEXTURE2DMSARRAY;
+ rtvDesc.Texture2DMSArray.FirstArraySlice = UINT(colorAtt.layer());
+ rtvDesc.Texture2DMSArray.ArraySize = 1;
+ } else {
+ rtvDesc.ViewDimension = D3D11_RTV_DIMENSION_TEXTURE2DARRAY;
+ rtvDesc.Texture2DArray.MipSlice = UINT(colorAtt.level());
+ rtvDesc.Texture2DArray.FirstArraySlice = UINT(colorAtt.layer());
+ rtvDesc.Texture2DArray.ArraySize = 1;
+ }
} else if (texD->flags().testFlag(QRhiTexture::ThreeDimensional)) {
rtvDesc.ViewDimension = D3D11_RTV_DIMENSION_TEXTURE3D;
rtvDesc.Texture3D.MipSlice = UINT(colorAtt.level());
diff --git a/src/gui/rhi/qrhid3d11_p_p.h b/src/gui/rhi/qrhid3d11_p_p.h
index ddeb68a193..3a333fe8a7 100644
--- a/src/gui/rhi/qrhid3d11_p_p.h
+++ b/src/gui/rhi/qrhid3d11_p_p.h
@@ -102,7 +102,7 @@ struct QD3D11RenderBuffer : public QRhiRenderBuffer
struct QD3D11Texture : public QRhiTexture
{
QD3D11Texture(QRhiImplementation *rhi, Format format, const QSize &pixelSize, int depth,
- int sampleCount, Flags flags);
+ int arraySize, int sampleCount, Flags flags);
~QD3D11Texture();
void destroy() override;
bool create() override;
@@ -607,6 +607,7 @@ public:
QRhiTexture *createTexture(QRhiTexture::Format format,
const QSize &pixelSize,
int depth,
+ int arraySize,
int sampleCount,
QRhiTexture::Flags flags) override;
QRhiSampler *createSampler(QRhiSampler::Filter magFilter,
diff --git a/src/gui/rhi/qrhigles2.cpp b/src/gui/rhi/qrhigles2.cpp
index 1c324cfd3b..5259be270c 100644
--- a/src/gui/rhi/qrhigles2.cpp
+++ b/src/gui/rhi/qrhigles2.cpp
@@ -397,6 +397,14 @@ QT_BEGIN_NAMESPACE
#define GL_TEXTURE_RECTANGLE 0x84F5
#endif
+#ifndef GL_TEXTURE_2D_ARRAY
+#define GL_TEXTURE_2D_ARRAY 0x8C1A
+#endif
+
+#ifndef GL_MAX_ARRAY_TEXTURE_LAYERS
+#define GL_MAX_ARRAY_TEXTURE_LAYERS 0x88FF
+#endif
+
/*!
Constructs a new QRhiGles2InitParams.
@@ -678,6 +686,14 @@ bool QRhiGles2::create(QRhi::Flags flags)
caps.texture3D = caps.ctxMajor >= 3; // 3.0
+ if (caps.ctxMajor >= 3) { // 3.0 or ES 3.0
+ GLint maxArraySize = 0;
+ f->glGetIntegerv(GL_MAX_ARRAY_TEXTURE_LAYERS, &maxArraySize);
+ caps.maxTextureArraySize = maxArraySize;
+ } else {
+ caps.maxTextureArraySize = 0;
+ }
+
if (!caps.gles) {
f->glEnable(GL_VERTEX_PROGRAM_POINT_SIZE);
f->glEnable(GL_POINT_SPRITE);
@@ -1074,6 +1090,8 @@ bool QRhiGles2::isFeatureSupported(QRhi::Feature feature) const
return caps.texture3D;
case QRhi::RenderTo3DTextureSlice:
return caps.texture3D;
+ case QRhi::TextureArrays:
+ return caps.maxTextureArraySize > 0;
default:
Q_UNREACHABLE();
return false;
@@ -1105,6 +1123,8 @@ int QRhiGles2::resourceLimit(QRhi::ResourceLimit limit) const
return caps.maxThreadGroupsY;
case QRhi::MaxThreadGroupZ:
return caps.maxThreadGroupsZ;
+ case QRhi::TextureArraySizeMax:
+ return 2048;
default:
Q_UNREACHABLE();
return 0;
@@ -1291,10 +1311,10 @@ QRhiRenderBuffer *QRhiGles2::createRenderBuffer(QRhiRenderBuffer::Type type, con
}
QRhiTexture *QRhiGles2::createTexture(QRhiTexture::Format format,
- const QSize &pixelSize, int depth,
+ const QSize &pixelSize, int depth, int arraySize,
int sampleCount, QRhiTexture::Flags flags)
{
- return new QGles2Texture(this, format, pixelSize, depth, sampleCount, flags);
+ return new QGles2Texture(this, format, pixelSize, depth, arraySize, sampleCount, flags);
}
QRhiSampler *QRhiGles2::createSampler(QRhiSampler::Filter magFilter, QRhiSampler::Filter minFilter,
@@ -1878,7 +1898,10 @@ void QRhiGles2::enqueueSubresUpload(QGles2Texture *texD, QGles2CommandBuffer *cb
trackedImageBarrier(cbD, texD, QGles2Texture::AccessUpdate);
const bool isCompressed = isCompressedFormat(texD->m_format);
const bool isCubeMap = texD->m_flags.testFlag(QRhiTexture::CubeMap);
+ const bool is3D = texD->m_flags.testFlag(QRhiTexture::ThreeDimensional);
+ const bool isArray = texD->m_flags.testFlag(QRhiTexture::TextureArray);
const GLenum faceTargetBase = isCubeMap ? GL_TEXTURE_CUBE_MAP_POSITIVE_X : texD->target;
+ const GLenum effectiveTarget = faceTargetBase + (isCubeMap ? uint(layer) : 0u);
const QPoint dp = subresDesc.destinationTopLeft();
const QByteArray rawData = subresDesc.data();
if (!subresDesc.image().isNull()) {
@@ -1894,11 +1917,11 @@ void QRhiGles2::enqueueSubresUpload(QGles2Texture *texD, QGles2CommandBuffer *cb
}
cmd.args.subImage.target = texD->target;
cmd.args.subImage.texture = texD->texture;
- cmd.args.subImage.faceTarget = faceTargetBase + uint(layer);
+ cmd.args.subImage.faceTarget = effectiveTarget;
cmd.args.subImage.level = level;
cmd.args.subImage.dx = dp.x();
cmd.args.subImage.dy = dp.y();
- cmd.args.subImage.dz = texD->m_flags.testFlag(QRhiTexture::ThreeDimensional) ? layer : 0;
+ cmd.args.subImage.dz = is3D || isArray ? layer : 0;
cmd.args.subImage.w = size.width();
cmd.args.subImage.h = size.height();
cmd.args.subImage.glformat = texD->glformat;
@@ -1907,8 +1930,7 @@ void QRhiGles2::enqueueSubresUpload(QGles2Texture *texD, QGles2CommandBuffer *cb
cmd.args.subImage.rowLength = 0;
cmd.args.subImage.data = cbD->retainImage(img);
} else if (!rawData.isEmpty() && isCompressed) {
- const bool is3D = texD->flags().testFlag(QRhiTexture::ThreeDimensional);
- if ((texD->flags().testFlag(QRhiTexture::UsedAsCompressedAtlas) || is3D)
+ if ((texD->flags().testFlag(QRhiTexture::UsedAsCompressedAtlas) || is3D || isArray)
&& !texD->zeroInitialized)
{
// Create on first upload since glCompressedTexImage2D cannot take
@@ -1920,17 +1942,19 @@ void QRhiGles2::enqueueSubresUpload(QGles2Texture *texD, QGles2CommandBuffer *cb
compressedFormatInfo(texD->m_format, texD->m_pixelSize, nullptr, &byteSize, nullptr);
if (is3D)
byteSize *= texD->m_depth;
+ if (isArray)
+ byteSize *= texD->m_arraySize;
QByteArray zeroBuf(byteSize, 0);
QGles2CommandBuffer::Command &cmd(cbD->commands.get());
cmd.cmd = QGles2CommandBuffer::Command::CompressedImage;
cmd.args.compressedImage.target = texD->target;
cmd.args.compressedImage.texture = texD->texture;
- cmd.args.compressedImage.faceTarget = faceTargetBase + uint(layer);
+ cmd.args.compressedImage.faceTarget = effectiveTarget;
cmd.args.compressedImage.level = level;
cmd.args.compressedImage.glintformat = texD->glintformat;
cmd.args.compressedImage.w = texD->m_pixelSize.width();
cmd.args.compressedImage.h = texD->m_pixelSize.height();
- cmd.args.compressedImage.depth = is3D ? texD->m_depth : 0;
+ cmd.args.compressedImage.depth = is3D ? texD->m_depth : (isArray ? texD->m_arraySize : 0);
cmd.args.compressedImage.size = byteSize;
cmd.args.compressedImage.data = cbD->retainData(zeroBuf);
texD->zeroInitialized = true;
@@ -1943,11 +1967,11 @@ void QRhiGles2::enqueueSubresUpload(QGles2Texture *texD, QGles2CommandBuffer *cb
cmd.cmd = QGles2CommandBuffer::Command::CompressedSubImage;
cmd.args.compressedSubImage.target = texD->target;
cmd.args.compressedSubImage.texture = texD->texture;
- cmd.args.compressedSubImage.faceTarget = faceTargetBase + uint(layer);
+ cmd.args.compressedSubImage.faceTarget = effectiveTarget;
cmd.args.compressedSubImage.level = level;
cmd.args.compressedSubImage.dx = dp.x();
cmd.args.compressedSubImage.dy = dp.y();
- cmd.args.compressedSubImage.dz = texD->m_flags.testFlag(QRhiTexture::ThreeDimensional) ? layer : 0;
+ cmd.args.compressedSubImage.dz = is3D || isArray ? layer : 0;
cmd.args.compressedSubImage.w = size.width();
cmd.args.compressedSubImage.h = size.height();
cmd.args.compressedSubImage.glintformat = texD->glintformat;
@@ -1958,12 +1982,12 @@ void QRhiGles2::enqueueSubresUpload(QGles2Texture *texD, QGles2CommandBuffer *cb
cmd.cmd = QGles2CommandBuffer::Command::CompressedImage;
cmd.args.compressedImage.target = texD->target;
cmd.args.compressedImage.texture = texD->texture;
- cmd.args.compressedImage.faceTarget = faceTargetBase + uint(layer);
+ cmd.args.compressedImage.faceTarget = effectiveTarget;
cmd.args.compressedImage.level = level;
cmd.args.compressedImage.glintformat = texD->glintformat;
cmd.args.compressedImage.w = size.width();
cmd.args.compressedImage.h = size.height();
- cmd.args.compressedImage.depth = is3D ? texD->m_depth : 0;
+ cmd.args.compressedImage.depth = is3D ? texD->m_depth : (isArray ? texD->m_arraySize : 0);
cmd.args.compressedImage.size = rawData.size();
cmd.args.compressedImage.data = cbD->retainData(rawData);
}
@@ -1977,11 +2001,11 @@ void QRhiGles2::enqueueSubresUpload(QGles2Texture *texD, QGles2CommandBuffer *cb
cmd.cmd = QGles2CommandBuffer::Command::SubImage;
cmd.args.subImage.target = texD->target;
cmd.args.subImage.texture = texD->texture;
- cmd.args.subImage.faceTarget = faceTargetBase + uint(layer);
+ cmd.args.subImage.faceTarget = effectiveTarget;
cmd.args.subImage.level = level;
cmd.args.subImage.dx = dp.x();
cmd.args.subImage.dy = dp.y();
- cmd.args.subImage.dz = texD->m_flags.testFlag(QRhiTexture::ThreeDimensional) ? layer : 0;
+ cmd.args.subImage.dz = is3D || isArray ? layer : 0;
cmd.args.subImage.w = size.width();
cmd.args.subImage.h = size.height();
cmd.args.subImage.glformat = texD->glformat;
@@ -2090,21 +2114,24 @@ void QRhiGles2::enqueueResourceUpdates(QRhiCommandBuffer *cb, QRhiResourceUpdate
QGles2CommandBuffer::Command &cmd(cbD->commands.get());
cmd.cmd = QGles2CommandBuffer::Command::CopyTex;
+ const bool srcHasZ = srcD->m_flags.testFlag(QRhiTexture::ThreeDimensional) || srcD->m_flags.testFlag(QRhiTexture::TextureArray);
+ const bool dstHasZ = dstD->m_flags.testFlag(QRhiTexture::ThreeDimensional) || dstD->m_flags.testFlag(QRhiTexture::TextureArray);
+
cmd.args.copyTex.srcTarget = srcD->target;
- cmd.args.copyTex.srcFaceTarget = srcFaceTargetBase + uint(u.desc.sourceLayer());
+ cmd.args.copyTex.srcFaceTarget = srcFaceTargetBase + (srcHasZ ? 0u : uint(u.desc.sourceLayer()));
cmd.args.copyTex.srcTexture = srcD->texture;
cmd.args.copyTex.srcLevel = u.desc.sourceLevel();
cmd.args.copyTex.srcX = sp.x();
cmd.args.copyTex.srcY = sp.y();
- cmd.args.copyTex.srcZ = srcD->m_flags.testFlag(QRhiTexture::ThreeDimensional) ? u.desc.sourceLayer() : 0;
+ cmd.args.copyTex.srcZ = srcHasZ ? u.desc.sourceLayer() : 0;
cmd.args.copyTex.dstTarget = dstD->target;
- cmd.args.copyTex.dstFaceTarget = dstFaceTargetBase + uint(u.desc.destinationLayer());
+ cmd.args.copyTex.dstFaceTarget = dstFaceTargetBase + (dstHasZ ? 0u : uint(u.desc.destinationLayer()));
cmd.args.copyTex.dstTexture = dstD->texture;
cmd.args.copyTex.dstLevel = u.desc.destinationLevel();
cmd.args.copyTex.dstX = dp.x();
cmd.args.copyTex.dstY = dp.y();
- cmd.args.copyTex.dstZ = dstD->m_flags.testFlag(QRhiTexture::ThreeDimensional) ? u.desc.destinationLayer() : 0;
+ cmd.args.copyTex.dstZ = dstHasZ ? u.desc.destinationLayer() : 0;
cmd.args.copyTex.w = copySize.width();
cmd.args.copyTex.h = copySize.height();
@@ -2122,7 +2149,9 @@ void QRhiGles2::enqueueResourceUpdates(QRhiCommandBuffer *cb, QRhiResourceUpdate
cmd.args.readPixels.w = readImageSize.width();
cmd.args.readPixels.h = readImageSize.height();
cmd.args.readPixels.format = texD->m_format;
- if (texD->m_flags.testFlag(QRhiTexture::ThreeDimensional)) {
+ if (texD->m_flags.testFlag(QRhiTexture::ThreeDimensional)
+ || texD->m_flags.testFlag(QRhiTexture::TextureArray))
+ {
cmd.args.readPixels.readTarget = texD->target;
cmd.args.readPixels.slice3D = u.rb.layer();
} else {
@@ -2843,7 +2872,7 @@ void QRhiGles2::executeCommandBuffer(QRhiCommandBuffer *cb)
GLuint fbo;
f->glGenFramebuffers(1, &fbo);
f->glBindFramebuffer(GL_FRAMEBUFFER, fbo);
- if (cmd.args.copyTex.srcTarget == GL_TEXTURE_3D) {
+ if (cmd.args.copyTex.srcTarget == GL_TEXTURE_3D || cmd.args.copyTex.srcTarget == GL_TEXTURE_2D_ARRAY) {
f->glFramebufferTextureLayer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, cmd.args.copyTex.srcTexture,
cmd.args.copyTex.srcLevel, cmd.args.copyTex.srcZ);
} else {
@@ -2851,7 +2880,7 @@ void QRhiGles2::executeCommandBuffer(QRhiCommandBuffer *cb)
cmd.args.copyTex.srcFaceTarget, cmd.args.copyTex.srcTexture, cmd.args.copyTex.srcLevel);
}
f->glBindTexture(cmd.args.copyTex.dstTarget, cmd.args.copyTex.dstTexture);
- if (cmd.args.copyTex.dstTarget == GL_TEXTURE_3D) {
+ if (cmd.args.copyTex.dstTarget == GL_TEXTURE_3D || cmd.args.copyTex.dstTarget == GL_TEXTURE_2D_ARRAY) {
f->glCopyTexSubImage3D(cmd.args.copyTex.dstTarget, cmd.args.copyTex.dstLevel,
cmd.args.copyTex.dstX, cmd.args.copyTex.dstY, cmd.args.copyTex.dstZ,
cmd.args.copyTex.srcX, cmd.args.copyTex.srcY,
@@ -2951,7 +2980,7 @@ void QRhiGles2::executeCommandBuffer(QRhiCommandBuffer *cb)
f->glPixelStorei(GL_UNPACK_ALIGNMENT, cmd.args.subImage.rowStartAlign);
if (cmd.args.subImage.rowLength != 0)
f->glPixelStorei(GL_UNPACK_ROW_LENGTH, cmd.args.subImage.rowLength);
- if (cmd.args.subImage.target == GL_TEXTURE_3D) {
+ if (cmd.args.subImage.target == GL_TEXTURE_3D || cmd.args.subImage.target == GL_TEXTURE_2D_ARRAY) {
f->glTexSubImage3D(cmd.args.subImage.target, cmd.args.subImage.level,
cmd.args.subImage.dx, cmd.args.subImage.dy, cmd.args.subImage.dz,
cmd.args.subImage.w, cmd.args.subImage.h, 1,
@@ -2971,7 +3000,7 @@ void QRhiGles2::executeCommandBuffer(QRhiCommandBuffer *cb)
break;
case QGles2CommandBuffer::Command::CompressedImage:
f->glBindTexture(cmd.args.compressedImage.target, cmd.args.compressedImage.texture);
- if (cmd.args.compressedImage.target == GL_TEXTURE_3D) {
+ if (cmd.args.compressedImage.target == GL_TEXTURE_3D || cmd.args.compressedImage.target == GL_TEXTURE_2D_ARRAY) {
f->glCompressedTexImage3D(cmd.args.compressedImage.target, cmd.args.compressedImage.level,
cmd.args.compressedImage.glintformat,
cmd.args.compressedImage.w, cmd.args.compressedImage.h, cmd.args.compressedImage.depth,
@@ -2985,7 +3014,7 @@ void QRhiGles2::executeCommandBuffer(QRhiCommandBuffer *cb)
break;
case QGles2CommandBuffer::Command::CompressedSubImage:
f->glBindTexture(cmd.args.compressedSubImage.target, cmd.args.compressedSubImage.texture);
- if (cmd.args.compressedSubImage.target == GL_TEXTURE_3D) {
+ if (cmd.args.compressedSubImage.target == GL_TEXTURE_3D || cmd.args.compressedSubImage.target == GL_TEXTURE_2D_ARRAY) {
f->glCompressedTexSubImage3D(cmd.args.compressedSubImage.target, cmd.args.compressedSubImage.level,
cmd.args.compressedSubImage.dx, cmd.args.compressedSubImage.dy, cmd.args.compressedSubImage.dz,
cmd.args.compressedSubImage.w, cmd.args.compressedSubImage.h, 1,
@@ -3007,9 +3036,13 @@ void QRhiGles2::executeCommandBuffer(QRhiCommandBuffer *cb)
f->glFramebufferRenderbuffer(GL_READ_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
GL_RENDERBUFFER, cmd.args.blitFromRb.renderbuffer);
f->glBindFramebuffer(GL_DRAW_FRAMEBUFFER, fbo[1]);
-
- f->glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, cmd.args.blitFromRb.target,
- cmd.args.blitFromRb.texture, cmd.args.blitFromRb.dstLevel);
+ if (cmd.args.blitFromRb.target == GL_TEXTURE_3D || cmd.args.blitFromRb.target == GL_TEXTURE_2D_ARRAY) {
+ f->glFramebufferTextureLayer(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
+ cmd.args.blitFromRb.texture, cmd.args.blitFromRb.dstLevel, cmd.args.blitFromRb.dstLayer);
+ } else {
+ f->glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, cmd.args.blitFromRb.target,
+ cmd.args.blitFromRb.texture, cmd.args.blitFromRb.dstLevel);
+ }
f->glBlitFramebuffer(0, 0, cmd.args.blitFromRb.w, cmd.args.blitFromRb.h,
0, 0, cmd.args.blitFromRb.w, cmd.args.blitFromRb.h,
GL_COLOR_BUFFER_BIT,
@@ -3755,11 +3788,15 @@ void QRhiGles2::endPass(QRhiCommandBuffer *cb, QRhiResourceUpdateBatch *resource
cmd.args.blitFromRb.w = size.width();
cmd.args.blitFromRb.h = size.height();
QGles2Texture *colorTexD = QRHI_RES(QGles2Texture, colorAtt.resolveTexture());
- const GLenum faceTargetBase = colorTexD->m_flags.testFlag(QRhiTexture::CubeMap) ? GL_TEXTURE_CUBE_MAP_POSITIVE_X
- : colorTexD->target;
- cmd.args.blitFromRb.target = faceTargetBase + uint(colorAtt.resolveLayer());
+ if (colorTexD->m_flags.testFlag(QRhiTexture::CubeMap))
+ cmd.args.blitFromRb.target = GL_TEXTURE_CUBE_MAP_POSITIVE_X + uint(colorAtt.resolveLayer());
+ else
+ cmd.args.blitFromRb.target = colorTexD->target;
cmd.args.blitFromRb.texture = colorTexD->texture;
cmd.args.blitFromRb.dstLevel = colorAtt.resolveLevel();
+ const bool hasZ = colorTexD->m_flags.testFlag(QRhiTexture::ThreeDimensional)
+ || colorTexD->m_flags.testFlag(QRhiTexture::TextureArray);
+ cmd.args.blitFromRb.dstLayer = hasZ ? colorAtt.resolveLayer() : 0;
}
}
}
@@ -4576,8 +4613,8 @@ QRhiTexture::Format QGles2RenderBuffer::backingFormat() const
}
QGles2Texture::QGles2Texture(QRhiImplementation *rhi, Format format, const QSize &pixelSize, int depth,
- int sampleCount, Flags flags)
- : QRhiTexture(rhi, format, pixelSize, depth, sampleCount, flags)
+ int arraySize, int sampleCount, Flags flags)
+ : QRhiTexture(rhi, format, pixelSize, depth, arraySize, sampleCount, flags)
{
}
@@ -4622,6 +4659,7 @@ bool QGles2Texture::prepareCreate(QSize *adjustedSize)
const QSize size = m_pixelSize.isEmpty() ? QSize(1, 1) : m_pixelSize;
const bool isCube = m_flags.testFlag(CubeMap);
+ const bool isArray = m_flags.testFlag(QRhiTexture::TextureArray);
const bool is3D = m_flags.testFlag(ThreeDimensional);
const bool hasMipMaps = m_flags.testFlag(MipMapped);
const bool isCompressed = rhiD->isCompressedFormat(m_format);
@@ -4634,14 +4672,28 @@ bool QGles2Texture::prepareCreate(QSize *adjustedSize)
qWarning("Texture cannot be both cube and 3D");
return false;
}
+ if (isArray && is3D) {
+ qWarning("Texture cannot be both array and 3D");
+ return false;
+ }
m_depth = qMax(1, m_depth);
if (m_depth > 1 && !is3D) {
qWarning("Texture cannot have a depth of %d when it is not 3D", m_depth);
return false;
}
+ m_arraySize = qMax(0, m_arraySize);
+ if (m_arraySize > 0 && !isArray) {
+ qWarning("Texture cannot have an array size of %d when it is not an array", m_arraySize);
+ return false;
+ }
+ if (m_arraySize < 1 && isArray) {
+ qWarning("Texture is an array but array size is %d", m_arraySize);
+ return false;
+ }
target = isCube ? GL_TEXTURE_CUBE_MAP
- : m_sampleCount > 1 ? GL_TEXTURE_2D_MULTISAMPLE : (is3D ? GL_TEXTURE_3D : GL_TEXTURE_2D);
+ : m_sampleCount > 1 ? GL_TEXTURE_2D_MULTISAMPLE
+ : (is3D ? GL_TEXTURE_3D : (isArray ? GL_TEXTURE_2D_ARRAY : GL_TEXTURE_2D));
if (m_flags.testFlag(ExternalOES))
target = GL_TEXTURE_EXTERNAL_OES;
else if (m_flags.testFlag(TextureRectangleGL))
@@ -4687,21 +4739,23 @@ bool QGles2Texture::create()
rhiD->f->glGenTextures(1, &texture);
const bool isCube = m_flags.testFlag(CubeMap);
+ const bool isArray = m_flags.testFlag(QRhiTexture::TextureArray);
const bool is3D = m_flags.testFlag(ThreeDimensional);
const bool hasMipMaps = m_flags.testFlag(MipMapped);
const bool isCompressed = rhiD->isCompressedFormat(m_format);
if (!isCompressed) {
rhiD->f->glBindTexture(target, texture);
if (!m_flags.testFlag(UsedWithLoadStore)) {
- if (is3D) {
+ if (is3D || isArray) {
+ const int layerCount = is3D ? m_depth : m_arraySize;
if (hasMipMaps) {
for (int level = 0; level != mipLevelCount; ++level) {
const QSize mipSize = rhiD->q->sizeForMipLevel(level, size);
- rhiD->f->glTexImage3D(target, level, GLint(glintformat), mipSize.width(), mipSize.height(), m_depth,
+ rhiD->f->glTexImage3D(target, level, GLint(glintformat), mipSize.width(), mipSize.height(), layerCount,
0, glformat, gltype, nullptr);
}
} else {
- rhiD->f->glTexImage3D(target, 0, GLint(glintformat), size.width(), size.height(), m_depth,
+ rhiD->f->glTexImage3D(target, 0, GLint(glintformat), size.width(), size.height(), layerCount,
0, glformat, gltype, nullptr);
}
} else if (hasMipMaps || isCube) {
@@ -4722,8 +4776,8 @@ bool QGles2Texture::create()
// Must be specified with immutable storage functions otherwise
// bindImageTexture may fail. Also, the internal format must be a
// sized format here.
- if (is3D)
- rhiD->f->glTexStorage3D(target, mipLevelCount, glsizedintformat, size.width(), size.height(), m_depth);
+ if (is3D || isArray)
+ rhiD->f->glTexStorage3D(target, mipLevelCount, glsizedintformat, size.width(), size.height(), is3D ? m_depth : m_arraySize);
else
rhiD->f->glTexStorage2D(target, mipLevelCount, glsizedintformat, size.width(), size.height());
}
@@ -4942,7 +4996,7 @@ bool QGles2TextureRenderTarget::create()
if (texture) {
QGles2Texture *texD = QRHI_RES(QGles2Texture, texture);
Q_ASSERT(texD->texture && texD->specified);
- if (texD->flags().testFlag(QRhiTexture::ThreeDimensional)) {
+ if (texD->flags().testFlag(QRhiTexture::ThreeDimensional) || texD->flags().testFlag(QRhiTexture::TextureArray)) {
rhiD->f->glFramebufferTextureLayer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0 + uint(attIndex), texD->texture,
colorAtt.level(), colorAtt.layer());
} else {
diff --git a/src/gui/rhi/qrhigles2_p_p.h b/src/gui/rhi/qrhigles2_p_p.h
index 4d18e9c60f..48f79e3c1d 100644
--- a/src/gui/rhi/qrhigles2_p_p.h
+++ b/src/gui/rhi/qrhigles2_p_p.h
@@ -139,7 +139,7 @@ inline bool operator!=(const QGles2SamplerData &a, const QGles2SamplerData &b)
struct QGles2Texture : public QRhiTexture
{
QGles2Texture(QRhiImplementation *rhi, Format format, const QSize &pixelSize, int depth,
- int sampleCount, Flags flags);
+ int arraySize, int sampleCount, Flags flags);
~QGles2Texture();
void destroy() override;
bool create() override;
@@ -521,6 +521,7 @@ struct QGles2CommandBuffer : public QRhiCommandBuffer
GLenum target;
GLuint texture;
int dstLevel;
+ int dstLayer;
} blitFromRb;
struct {
GLenum target;
@@ -752,6 +753,7 @@ public:
QRhiTexture *createTexture(QRhiTexture::Format format,
const QSize &pixelSize,
int depth,
+ int arraySize,
int sampleCount,
QRhiTexture::Flags flags) override;
QRhiSampler *createSampler(QRhiSampler::Filter magFilter,
@@ -910,6 +912,7 @@ public:
maxTextureSize(2048),
maxDrawBuffers(4),
maxSamples(16),
+ maxTextureArraySize(0),
maxThreadGroupsPerDimension(0),
maxThreadsPerThreadGroup(0),
maxThreadGroupsX(0),
@@ -951,6 +954,7 @@ public:
int maxTextureSize;
int maxDrawBuffers;
int maxSamples;
+ int maxTextureArraySize;
int maxThreadGroupsPerDimension;
int maxThreadsPerThreadGroup;
int maxThreadGroupsX;
diff --git a/src/gui/rhi/qrhimetal.mm b/src/gui/rhi/qrhimetal.mm
index d37a130d15..3e4c0e08e2 100644
--- a/src/gui/rhi/qrhimetal.mm
+++ b/src/gui/rhi/qrhimetal.mm
@@ -613,6 +613,8 @@ bool QRhiMetal::isFeatureSupported(QRhi::Feature feature) const
return true;
case QRhi::RenderTo3DTextureSlice:
return true;
+ case QRhi::TextureArrays:
+ return true;
default:
Q_UNREACHABLE();
return false;
@@ -646,6 +648,8 @@ int QRhiMetal::resourceLimit(QRhi::ResourceLimit limit) const
#else
return 512;
#endif
+ case QRhi::TextureArraySizeMax:
+ return 2048;
default:
Q_UNREACHABLE();
return 0;
@@ -704,10 +708,10 @@ QRhiRenderBuffer *QRhiMetal::createRenderBuffer(QRhiRenderBuffer::Type type, con
}
QRhiTexture *QRhiMetal::createTexture(QRhiTexture::Format format,
- const QSize &pixelSize, int depth,
+ const QSize &pixelSize, int depth, int arraySize,
int sampleCount, QRhiTexture::Flags flags)
{
- return new QMetalTexture(this, format, pixelSize, depth, sampleCount, flags);
+ return new QMetalTexture(this, format, pixelSize, depth, arraySize, sampleCount, flags);
}
QRhiSampler *QRhiMetal::createSampler(QRhiSampler::Filter magFilter, QRhiSampler::Filter minFilter,
@@ -2617,8 +2621,8 @@ QRhiTexture::Format QMetalRenderBuffer::backingFormat() const
}
QMetalTexture::QMetalTexture(QRhiImplementation *rhi, Format format, const QSize &pixelSize, int depth,
- int sampleCount, Flags flags)
- : QRhiTexture(rhi, format, pixelSize, depth, sampleCount, flags),
+ int arraySize, int sampleCount, Flags flags)
+ : QRhiTexture(rhi, format, pixelSize, depth, arraySize, sampleCount, flags),
d(new QMetalTextureData(this))
{
for (int i = 0; i < QMTL_FRAMES_IN_FLIGHT; ++i)
@@ -2673,6 +2677,7 @@ bool QMetalTexture::prepareCreate(QSize *adjustedSize)
const QSize size = m_pixelSize.isEmpty() ? QSize(1, 1) : m_pixelSize;
const bool isCube = m_flags.testFlag(CubeMap);
const bool is3D = m_flags.testFlag(ThreeDimensional);
+ const bool isArray = m_flags.testFlag(TextureArray);
const bool hasMipMaps = m_flags.testFlag(MipMapped);
QRHI_RES_RHI(QRhiMetal);
@@ -2697,11 +2702,24 @@ bool QMetalTexture::prepareCreate(QSize *adjustedSize)
qWarning("Texture cannot be both cube and 3D");
return false;
}
+ if (isArray && is3D) {
+ qWarning("Texture cannot be both array and 3D");
+ return false;
+ }
m_depth = qMax(1, m_depth);
if (m_depth > 1 && !is3D) {
qWarning("Texture cannot have a depth of %d when it is not 3D", m_depth);
return false;
}
+ m_arraySize = qMax(0, m_arraySize);
+ if (m_arraySize > 0 && !isArray) {
+ qWarning("Texture cannot have an array size of %d when it is not an array", m_arraySize);
+ return false;
+ }
+ if (m_arraySize < 1 && isArray) {
+ qWarning("Texture is an array but array size is %d", m_arraySize);
+ return false;
+ }
if (adjustedSize)
*adjustedSize = size;
@@ -2719,12 +2737,24 @@ bool QMetalTexture::create()
const bool isCube = m_flags.testFlag(CubeMap);
const bool is3D = m_flags.testFlag(ThreeDimensional);
- if (isCube)
+ const bool isArray = m_flags.testFlag(TextureArray);
+ if (isCube) {
desc.textureType = MTLTextureTypeCube;
- else if (is3D)
+ } else if (is3D) {
desc.textureType = MTLTextureType3D;
- else
+ } else if (isArray) {
+#ifdef Q_OS_IOS
+ if (samples > 1) {
+ // would be available on iOS 14.0+ but cannot test for that with a 13 SDK
+ qWarning("Multisample 2D texture array is not supported on iOS");
+ }
+ desc.textureType = MTLTextureType2DArray;
+#else
+ desc.textureType = samples > 1 ? MTLTextureType2DMultisampleArray : MTLTextureType2DArray;
+#endif
+ } else {
desc.textureType = samples > 1 ? MTLTextureType2DMultisample : MTLTextureType2D;
+ }
desc.pixelFormat = d->format;
desc.width = NSUInteger(size.width());
desc.height = NSUInteger(size.height());
@@ -2732,6 +2762,8 @@ bool QMetalTexture::create()
desc.mipmapLevelCount = NSUInteger(mipLevelCount);
if (samples > 1)
desc.sampleCount = NSUInteger(samples);
+ if (isArray)
+ desc.arrayLength = NSUInteger(m_arraySize);
desc.resourceOptions = MTLResourceStorageModePrivate;
desc.storageMode = MTLStorageModePrivate;
desc.usage = MTLTextureUsageShaderRead;
@@ -2750,7 +2782,7 @@ bool QMetalTexture::create()
d->owns = true;
QRHI_PROF;
- QRHI_PROF_F(newTexture(this, true, mipLevelCount, isCube ? 6 : 1, samples));
+ QRHI_PROF_F(newTexture(this, true, mipLevelCount, isCube ? 6 : (isArray ? m_arraySize : 1), samples));
lastActiveFrameSlot = -1;
generation += 1;
@@ -2794,8 +2826,9 @@ id<MTLTexture> QMetalTextureData::viewForLevel(int level)
const MTLTextureType type = [tex textureType];
const bool isCube = q->m_flags.testFlag(QRhiTexture::CubeMap);
+ const bool isArray = q->m_flags.testFlag(QRhiTexture::TextureArray);
id<MTLTexture> view = [tex newTextureViewWithPixelFormat: format textureType: type
- levels: NSMakeRange(NSUInteger(level), 1) slices: NSMakeRange(0, isCube ? 6 : 1)];
+ levels: NSMakeRange(NSUInteger(level), 1) slices: NSMakeRange(0, isCube ? 6 : (isArray ? q->m_arraySize : 1))];
perLevelViews[level] = view;
return view;
diff --git a/src/gui/rhi/qrhimetal_p_p.h b/src/gui/rhi/qrhimetal_p_p.h
index 579a2063ae..f24912a640 100644
--- a/src/gui/rhi/qrhimetal_p_p.h
+++ b/src/gui/rhi/qrhimetal_p_p.h
@@ -103,7 +103,7 @@ struct QMetalTextureData;
struct QMetalTexture : public QRhiTexture
{
QMetalTexture(QRhiImplementation *rhi, Format format, const QSize &pixelSize, int depth,
- int sampleCount, Flags flags);
+ int arraySize, int sampleCount, Flags flags);
~QMetalTexture();
void destroy() override;
bool create() override;
@@ -366,6 +366,7 @@ public:
QRhiTexture *createTexture(QRhiTexture::Format format,
const QSize &pixelSize,
int depth,
+ int arraySize,
int sampleCount,
QRhiTexture::Flags flags) override;
QRhiSampler *createSampler(QRhiSampler::Filter magFilter,
diff --git a/src/gui/rhi/qrhinull.cpp b/src/gui/rhi/qrhinull.cpp
index 894caf8f35..c160958493 100644
--- a/src/gui/rhi/qrhinull.cpp
+++ b/src/gui/rhi/qrhinull.cpp
@@ -162,6 +162,8 @@ int QRhiNull::resourceLimit(QRhi::ResourceLimit limit) const
return 0;
case QRhi::MaxThreadGroupZ:
return 0;
+ case QRhi::TextureArraySizeMax:
+ return 2048;
default:
Q_UNREACHABLE();
return 0;
@@ -219,10 +221,10 @@ QRhiRenderBuffer *QRhiNull::createRenderBuffer(QRhiRenderBuffer::Type type, cons
}
QRhiTexture *QRhiNull::createTexture(QRhiTexture::Format format,
- const QSize &pixelSize, int depth,
+ const QSize &pixelSize, int depth, int arraySize,
int sampleCount, QRhiTexture::Flags flags)
{
- return new QNullTexture(this, format, pixelSize, depth, sampleCount, flags);
+ return new QNullTexture(this, format, pixelSize, depth, arraySize, sampleCount, flags);
}
QRhiSampler *QRhiNull::createSampler(QRhiSampler::Filter magFilter, QRhiSampler::Filter minFilter,
@@ -671,8 +673,8 @@ QRhiTexture::Format QNullRenderBuffer::backingFormat() const
}
QNullTexture::QNullTexture(QRhiImplementation *rhi, Format format, const QSize &pixelSize, int depth,
- int sampleCount, Flags flags)
- : QRhiTexture(rhi, format, pixelSize, depth, sampleCount, flags)
+ int arraySize, int sampleCount, Flags flags)
+ : QRhiTexture(rhi, format, pixelSize, depth, arraySize, sampleCount, flags)
{
}
@@ -703,11 +705,13 @@ bool QNullTexture::create()
QRHI_RES_RHI(QRhiNull);
const bool isCube = m_flags.testFlag(CubeMap);
const bool is3D = m_flags.testFlag(ThreeDimensional);
+ const bool isArray = m_flags.testFlag(TextureArray);
const bool hasMipMaps = m_flags.testFlag(MipMapped);
QSize size = m_pixelSize.isEmpty() ? QSize(1, 1) : m_pixelSize;
m_depth = qMax(1, m_depth);
const int mipLevelCount = hasMipMaps ? rhiD->q->mipLevelsForSize(size) : 1;
- const int layerCount = is3D ? m_depth : (isCube ? 6 : 1);
+ m_arraySize = qMax(0, m_arraySize);
+ const int layerCount = is3D ? m_depth : (isCube ? 6 : (isArray ? m_arraySize : 1));
if (m_format == RGBA8) {
image.resize(layerCount);
@@ -737,12 +741,13 @@ bool QNullTexture::createFrom(QRhiTexture::NativeTexture src)
QRHI_RES_RHI(QRhiNull);
const bool isCube = m_flags.testFlag(CubeMap);
+ const bool isArray = m_flags.testFlag(TextureArray);
const bool hasMipMaps = m_flags.testFlag(MipMapped);
QSize size = m_pixelSize.isEmpty() ? QSize(1, 1) : m_pixelSize;
const int mipLevelCount = hasMipMaps ? rhiD->q->mipLevelsForSize(size) : 1;
QRHI_PROF;
- QRHI_PROF_F(newTexture(this, false, mipLevelCount, isCube ? 6 : 1, 1));
+ QRHI_PROF_F(newTexture(this, false, mipLevelCount, isCube ? 6 : (isArray ? m_arraySize : 1), 1));
rhiD->registerResource(this);
return true;
diff --git a/src/gui/rhi/qrhinull_p_p.h b/src/gui/rhi/qrhinull_p_p.h
index 24fb426503..4d3c508044 100644
--- a/src/gui/rhi/qrhinull_p_p.h
+++ b/src/gui/rhi/qrhinull_p_p.h
@@ -83,7 +83,7 @@ struct QNullRenderBuffer : public QRhiRenderBuffer
struct QNullTexture : public QRhiTexture
{
QNullTexture(QRhiImplementation *rhi, Format format, const QSize &pixelSize, int depth,
- int sampleCount, Flags flags);
+ int arraySize, int sampleCount, Flags flags);
~QNullTexture();
void destroy() override;
bool create() override;
@@ -224,6 +224,7 @@ public:
QRhiTexture *createTexture(QRhiTexture::Format format,
const QSize &pixelSize,
int depth,
+ int arraySize,
int sampleCount,
QRhiTexture::Flags flags) override;
QRhiSampler *createSampler(QRhiSampler::Filter magFilter,
diff --git a/src/gui/rhi/qrhivulkan.cpp b/src/gui/rhi/qrhivulkan.cpp
index 42d6f7e6ec..0bd3e6e95d 100644
--- a/src/gui/rhi/qrhivulkan.cpp
+++ b/src/gui/rhi/qrhivulkan.cpp
@@ -3454,6 +3454,7 @@ void QRhiVulkan::enqueueResourceUpdates(QVkCommandBuffer *cbD, QRhiResourceUpdat
QVkTexture *utexD = QRHI_RES(QVkTexture, u.dst);
Q_ASSERT(utexD->m_flags.testFlag(QRhiTexture::UsedWithGenerateMips));
const bool isCube = utexD->m_flags.testFlag(QRhiTexture::CubeMap);
+ const bool isArray = utexD->m_flags.testFlag(QRhiTexture::TextureArray);
const bool is3D = utexD->m_flags.testFlag(QRhiTexture::ThreeDimensional);
VkImageLayout origLayout = utexD->usageState.layout;
@@ -3462,7 +3463,7 @@ void QRhiVulkan::enqueueResourceUpdates(QVkCommandBuffer *cbD, QRhiResourceUpdat
if (!origStage)
origStage = VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT;
- for (int layer = 0; layer < (isCube ? 6 : 1); ++layer) {
+ for (int layer = 0; layer < (isCube ? 6 : (isArray ? utexD->m_arraySize : 1)); ++layer) {
int w = utexD->m_pixelSize.width();
int h = utexD->m_pixelSize.height();
int depth = is3D ? utexD->m_depth : 1;
@@ -4269,6 +4270,8 @@ bool QRhiVulkan::isFeatureSupported(QRhi::Feature feature) const
return true;
case QRhi::RenderTo3DTextureSlice:
return caps.texture3DSliceAs2D;
+ case QRhi::TextureArrays:
+ return true;
default:
Q_UNREACHABLE();
return false;
@@ -4300,6 +4303,8 @@ int QRhiVulkan::resourceLimit(QRhi::ResourceLimit limit) const
return int(physDevProperties.limits.maxComputeWorkGroupSize[1]);
case QRhi::MaxThreadGroupZ:
return int(physDevProperties.limits.maxComputeWorkGroupSize[2]);
+ case QRhi::TextureArraySizeMax:
+ return int(physDevProperties.limits.maxImageArrayLayers);
default:
Q_UNREACHABLE();
return 0;
@@ -4475,10 +4480,10 @@ QRhiRenderBuffer *QRhiVulkan::createRenderBuffer(QRhiRenderBuffer::Type type, co
}
QRhiTexture *QRhiVulkan::createTexture(QRhiTexture::Format format,
- const QSize &pixelSize, int depth,
+ const QSize &pixelSize, int depth, int arraySize,
int sampleCount, QRhiTexture::Flags flags)
{
- return new QVkTexture(this, format, pixelSize, depth, sampleCount, flags);
+ return new QVkTexture(this, format, pixelSize, depth, arraySize, sampleCount, flags);
}
QRhiSampler *QRhiVulkan::createSampler(QRhiSampler::Filter magFilter, QRhiSampler::Filter minFilter,
@@ -5713,6 +5718,7 @@ bool QVkRenderBuffer::create()
backingTexture = QRHI_RES(QVkTexture, rhiD->createTexture(backingFormat(),
m_pixelSize,
1,
+ 0,
m_sampleCount,
QRhiTexture::RenderTarget | QRhiTexture::UsedAsTransferSource));
} else {
@@ -5762,8 +5768,8 @@ QRhiTexture::Format QVkRenderBuffer::backingFormat() const
}
QVkTexture::QVkTexture(QRhiImplementation *rhi, Format format, const QSize &pixelSize, int depth,
- int sampleCount, Flags flags)
- : QRhiTexture(rhi, format, pixelSize, depth, sampleCount, flags)
+ int arraySize, int sampleCount, Flags flags)
+ : QRhiTexture(rhi, format, pixelSize, depth, arraySize, sampleCount, flags)
{
for (int i = 0; i < QVK_FRAMES_IN_FLIGHT; ++i) {
stagingBuffers[i] = VK_NULL_HANDLE;
@@ -5834,6 +5840,7 @@ bool QVkTexture::prepareCreate(QSize *adjustedSize)
const QSize size = m_pixelSize.isEmpty() ? QSize(1, 1) : m_pixelSize;
const bool isCube = m_flags.testFlag(CubeMap);
+ const bool isArray = m_flags.testFlag(TextureArray);
const bool is3D = m_flags.testFlag(ThreeDimensional);
const bool hasMipMaps = m_flags.testFlag(MipMapped);
@@ -5862,11 +5869,24 @@ bool QVkTexture::prepareCreate(QSize *adjustedSize)
qWarning("Texture cannot be both cube and 3D");
return false;
}
+ if (isArray && is3D) {
+ qWarning("Texture cannot be both array and 3D");
+ return false;
+ }
m_depth = qMax(1, m_depth);
if (m_depth > 1 && !is3D) {
qWarning("Texture cannot have a depth of %d when it is not 3D", m_depth);
return false;
}
+ m_arraySize = qMax(0, m_arraySize);
+ if (m_arraySize > 0 && !isArray) {
+ qWarning("Texture cannot have an array size of %d when it is not an array", m_arraySize);
+ return false;
+ }
+ if (m_arraySize < 1 && isArray) {
+ qWarning("Texture is an array but array size is %d", m_arraySize);
+ return false;
+ }
usageState.layout = VK_IMAGE_LAYOUT_PREINITIALIZED;
usageState.access = 0;
@@ -5884,13 +5904,16 @@ bool QVkTexture::finishCreate()
const auto aspectMask = aspectMaskForTextureFormat(m_format);
const bool isCube = m_flags.testFlag(CubeMap);
+ const bool isArray = m_flags.testFlag(TextureArray);
const bool is3D = m_flags.testFlag(ThreeDimensional);
VkImageViewCreateInfo viewInfo;
memset(&viewInfo, 0, sizeof(viewInfo));
viewInfo.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO;
viewInfo.image = image;
- viewInfo.viewType = isCube ? VK_IMAGE_VIEW_TYPE_CUBE : (is3D ? VK_IMAGE_VIEW_TYPE_3D : VK_IMAGE_VIEW_TYPE_2D);
+ viewInfo.viewType = isCube ? VK_IMAGE_VIEW_TYPE_CUBE
+ : (is3D ? VK_IMAGE_VIEW_TYPE_3D
+ : (isArray ? VK_IMAGE_VIEW_TYPE_2D_ARRAY : VK_IMAGE_VIEW_TYPE_2D));
viewInfo.format = vkformat;
viewInfo.components.r = VK_COMPONENT_SWIZZLE_R;
viewInfo.components.g = VK_COMPONENT_SWIZZLE_G;
@@ -5898,7 +5921,7 @@ bool QVkTexture::finishCreate()
viewInfo.components.a = VK_COMPONENT_SWIZZLE_A;
viewInfo.subresourceRange.aspectMask = aspectMask;
viewInfo.subresourceRange.levelCount = mipLevelCount;
- viewInfo.subresourceRange.layerCount = isCube ? 6 : 1;
+ viewInfo.subresourceRange.layerCount = isCube ? 6 : (isArray ? m_arraySize : 1);
VkResult err = rhiD->df->vkCreateImageView(rhiD->dev, &viewInfo, nullptr, &imageView);
if (err != VK_SUCCESS) {
@@ -5922,6 +5945,7 @@ bool QVkTexture::create()
const bool isRenderTarget = m_flags.testFlag(QRhiTexture::RenderTarget);
const bool isDepth = isDepthTextureFormat(m_format);
const bool isCube = m_flags.testFlag(CubeMap);
+ const bool isArray = m_flags.testFlag(TextureArray);
const bool is3D = m_flags.testFlag(ThreeDimensional);
VkImageCreateInfo imageInfo;
@@ -5952,7 +5976,7 @@ bool QVkTexture::create()
imageInfo.extent.height = uint32_t(size.height());
imageInfo.extent.depth = is3D ? m_depth : 1;
imageInfo.mipLevels = mipLevelCount;
- imageInfo.arrayLayers = isCube ? 6 : 1;
+ imageInfo.arrayLayers = isCube ? 6 : (isArray ? m_arraySize : 1);
imageInfo.samples = samples;
imageInfo.tiling = VK_IMAGE_TILING_OPTIMAL;
imageInfo.initialLayout = VK_IMAGE_LAYOUT_PREINITIALIZED;
@@ -5989,7 +6013,7 @@ bool QVkTexture::create()
rhiD->setObjectName(uint64_t(image), VK_DEBUG_REPORT_OBJECT_TYPE_IMAGE_EXT, m_objectName);
QRHI_PROF;
- QRHI_PROF_F(newTexture(this, true, int(mipLevelCount), isCube ? 6 : 1, samples));
+ QRHI_PROF_F(newTexture(this, true, int(mipLevelCount), isCube ? 6 : (isArray ? m_arraySize : 1), samples));
owns = true;
rhiD->registerResource(this);
@@ -6010,8 +6034,11 @@ bool QVkTexture::createFrom(QRhiTexture::NativeTexture src)
if (!finishCreate())
return false;
+ const bool isCube = m_flags.testFlag(CubeMap);
+ const bool isArray = m_flags.testFlag(TextureArray);
+
QRHI_PROF;
- QRHI_PROF_F(newTexture(this, false, int(mipLevelCount), m_flags.testFlag(CubeMap) ? 6 : 1, samples));
+ QRHI_PROF_F(newTexture(this, false, int(mipLevelCount), isCube ? 6 : (isArray ? m_arraySize : 1), samples));
usageState.layout = VkImageLayout(src.layout);
@@ -6039,13 +6066,16 @@ VkImageView QVkTexture::imageViewForLevel(int level)
const VkImageAspectFlags aspectMask = aspectMaskForTextureFormat(m_format);
const bool isCube = m_flags.testFlag(CubeMap);
+ const bool isArray = m_flags.testFlag(TextureArray);
const bool is3D = m_flags.testFlag(ThreeDimensional);
VkImageViewCreateInfo viewInfo;
memset(&viewInfo, 0, sizeof(viewInfo));
viewInfo.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO;
viewInfo.image = image;
- viewInfo.viewType = isCube ? VK_IMAGE_VIEW_TYPE_CUBE : (is3D ? VK_IMAGE_VIEW_TYPE_3D : VK_IMAGE_VIEW_TYPE_2D);
+ viewInfo.viewType = isCube ? VK_IMAGE_VIEW_TYPE_CUBE
+ : (is3D ? VK_IMAGE_VIEW_TYPE_3D
+ : (isArray ? VK_IMAGE_VIEW_TYPE_2D_ARRAY : VK_IMAGE_VIEW_TYPE_2D));
viewInfo.format = vkformat;
viewInfo.components.r = VK_COMPONENT_SWIZZLE_R;
viewInfo.components.g = VK_COMPONENT_SWIZZLE_G;
@@ -6055,7 +6085,7 @@ VkImageView QVkTexture::imageViewForLevel(int level)
viewInfo.subresourceRange.baseMipLevel = uint32_t(level);
viewInfo.subresourceRange.levelCount = 1;
viewInfo.subresourceRange.baseArrayLayer = 0;
- viewInfo.subresourceRange.layerCount = isCube ? 6 : 1;
+ viewInfo.subresourceRange.layerCount = isCube ? 6 : (isArray ? m_arraySize : 1);
VkImageView v = VK_NULL_HANDLE;
QRHI_RES_RHI(QRhiVulkan);
diff --git a/src/gui/rhi/qrhivulkan_p_p.h b/src/gui/rhi/qrhivulkan_p_p.h
index f2a3a809d3..42ae9aef3b 100644
--- a/src/gui/rhi/qrhivulkan_p_p.h
+++ b/src/gui/rhi/qrhivulkan_p_p.h
@@ -129,7 +129,7 @@ struct QVkRenderBuffer : public QRhiRenderBuffer
struct QVkTexture : public QRhiTexture
{
QVkTexture(QRhiImplementation *rhi, Format format, const QSize &pixelSize, int depth,
- int sampleCount, Flags flags);
+ int arraySize, int sampleCount, Flags flags);
~QVkTexture();
void destroy() override;
bool create() override;
@@ -682,6 +682,7 @@ public:
QRhiTexture *createTexture(QRhiTexture::Format format,
const QSize &pixelSize,
int depth,
+ int arraySize,
int sampleCount,
QRhiTexture::Flags flags) override;
QRhiSampler *createSampler(QRhiSampler::Filter magFilter,
diff --git a/tests/auto/gui/rhi/qrhi/BLACKLIST b/tests/auto/gui/rhi/qrhi/BLACKLIST
index 68621ac66b..65c9e4834b 100644
--- a/tests/auto/gui/rhi/qrhi/BLACKLIST
+++ b/tests/auto/gui/rhi/qrhi/BLACKLIST
@@ -7,3 +7,6 @@ android
# Skip 3D textures with Android emulator, the sw-based GL there is no good
[threeDimTexture]
android
+# Same here, GLES 3.0 features seem hopeless
+[renderToTextureTextureArray]
+android
diff --git a/tests/auto/gui/rhi/qrhi/tst_qrhi.cpp b/tests/auto/gui/rhi/qrhi/tst_qrhi.cpp
index 24d6ad2d7c..eba9b9ce30 100644
--- a/tests/auto/gui/rhi/qrhi/tst_qrhi.cpp
+++ b/tests/auto/gui/rhi/qrhi/tst_qrhi.cpp
@@ -113,6 +113,8 @@ private slots:
void renderToTextureMip();
void renderToTextureCubemapFace_data();
void renderToTextureCubemapFace();
+ void renderToTextureTextureArray_data();
+ void renderToTextureTextureArray();
void renderToTextureTexturedQuad_data();
void renderToTextureTexturedQuad();
void renderToTextureArrayOfTexturedQuad_data();
@@ -336,10 +338,13 @@ void tst_QRhi::create()
const int texMax = rhi->resourceLimit(QRhi::TextureSizeMax);
const int maxAtt = rhi->resourceLimit(QRhi::MaxColorAttachments);
const int framesInFlight = rhi->resourceLimit(QRhi::FramesInFlight);
+ const int texArrayMax = rhi->resourceLimit(QRhi::TextureArraySizeMax);
QVERIFY(texMin >= 1);
QVERIFY(texMax >= texMin);
QVERIFY(maxAtt >= 1);
QVERIFY(framesInFlight >= 1);
+ if (rhi->isFeatureSupported(QRhi::TextureArrays))
+ QVERIFY(texArrayMax > 1);
QVERIFY(rhi->nativeHandles());
QVERIFY(rhi->profiler());
@@ -1880,6 +1885,133 @@ void tst_QRhi::renderToTextureCubemapFace()
QFAIL("Encountered a pixel that is neither red or blue");
}
+ QVERIFY(redCount > 0 && blueCount > 0);
+ QCOMPARE(redCount + blueCount, outputSize.width());
+
+ if (rhi->isYUpInFramebuffer() == rhi->isYUpInNDC())
+ QVERIFY(redCount < blueCount); // 100, 412
+ else
+ QVERIFY(redCount > blueCount); // 412, 100
+}
+
+void tst_QRhi::renderToTextureTextureArray_data()
+{
+ rhiTestData();
+}
+
+void tst_QRhi::renderToTextureTextureArray()
+{
+ 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 rendering");
+
+ if (!rhi->isFeatureSupported(QRhi::TextureArrays))
+ QSKIP("TextureArrays is not supported with this backend, skipping test");
+
+ const QSize outputSize(512, 256);
+ const int ARRAY_SIZE = 8;
+ QScopedPointer<QRhiTexture> texture(rhi->newTextureArray(QRhiTexture::RGBA8,
+ ARRAY_SIZE,
+ outputSize,
+ 1,
+ QRhiTexture::RenderTarget
+ | QRhiTexture::UsedAsTransferSource));
+ QVERIFY(texture->create());
+
+ const int LAYER = 5; // render into element #5
+
+ QRhiColorAttachment colorAtt(texture.data());
+ colorAtt.setLayer(LAYER);
+ QRhiTextureRenderTargetDescription rtDesc(colorAtt);
+ QScopedPointer<QRhiTextureRenderTarget> rt(rhi->newTextureRenderTarget(rtDesc));
+ QScopedPointer<QRhiRenderPassDescriptor> rpDesc(rt->newCompatibleRenderPassDescriptor());
+ rt->setRenderPassDescriptor(rpDesc.data());
+ QVERIFY(rt->create());
+
+ QCOMPARE(rt->pixelSize(), texture->pixelSize());
+ QCOMPARE(rt->pixelSize(), outputSize);
+
+ QRhiCommandBuffer *cb = nullptr;
+ QVERIFY(rhi->beginOffscreenFrame(&cb) == QRhi::FrameOpSuccess);
+ QVERIFY(cb);
+
+ QRhiResourceUpdateBatch *updates = rhi->nextResourceUpdateBatch();
+
+ static const float vertices[] = {
+ -1.0f, -1.0f,
+ 1.0f, -1.0f,
+ 0.0f, 1.0f
+ };
+ QScopedPointer<QRhiBuffer> vbuf(rhi->newBuffer(QRhiBuffer::Immutable, QRhiBuffer::VertexBuffer, sizeof(vertices)));
+ QVERIFY(vbuf->create());
+ updates->uploadStaticBuffer(vbuf.data(), vertices);
+
+ QScopedPointer<QRhiShaderResourceBindings> srb(rhi->newShaderResourceBindings());
+ QVERIFY(srb->create());
+
+ QScopedPointer<QRhiGraphicsPipeline> pipeline(rhi->newGraphicsPipeline());
+ QShader vs = loadShader(":/data/simple.vert.qsb");
+ QVERIFY(vs.isValid());
+ QShader fs = loadShader(":/data/simple.frag.qsb");
+ QVERIFY(fs.isValid());
+ pipeline->setShaderStages({ { QRhiShaderStage::Vertex, vs }, { QRhiShaderStage::Fragment, fs } });
+ QRhiVertexInputLayout inputLayout;
+ inputLayout.setBindings({ { 2 * sizeof(float) } });
+ inputLayout.setAttributes({ { 0, 0, QRhiVertexInputAttribute::Float2, 0 } });
+ pipeline->setVertexInputLayout(inputLayout);
+ pipeline->setShaderResourceBindings(srb.data());
+ pipeline->setRenderPassDescriptor(rpDesc.data());
+
+ QVERIFY(pipeline->create());
+
+ cb->beginPass(rt.data(), Qt::blue, { 1.0f, 0 }, updates);
+ cb->setGraphicsPipeline(pipeline.data());
+ cb->setViewport({ 0, 0, float(rt->pixelSize().width()), float(rt->pixelSize().height()) });
+ QRhiCommandBuffer::VertexInput vbindings(vbuf.data(), 0);
+ cb->setVertexInput(0, 1, &vbindings);
+ cb->draw(3);
+
+ QRhiReadbackResult readResult;
+ QImage result;
+ readResult.completed = [&readResult, &result] {
+ result = QImage(reinterpret_cast<const uchar *>(readResult.data.constData()),
+ readResult.pixelSize.width(), readResult.pixelSize.height(),
+ QImage::Format_RGBA8888);
+ };
+ QRhiResourceUpdateBatch *readbackBatch = rhi->nextResourceUpdateBatch();
+ QRhiReadbackDescription readbackDescription(texture.data());
+ readbackDescription.setLayer(LAYER);
+ readbackBatch->readBackTexture(readbackDescription, &readResult);
+
+ cb->endPass(readbackBatch);
+
+ rhi->endOffscreenFrame();
+
+ QCOMPARE(result.size(), outputSize);
+
+ if (impl == QRhi::Null)
+ return;
+
+ const int y = 100;
+ const quint32 *p = reinterpret_cast<const quint32 *>(result.constScanLine(y));
+ int x = result.width() - 1;
+ int redCount = 0;
+ int blueCount = 0;
+ const int maxFuzz = 1;
+ while (x-- >= 0) {
+ const QRgb c(*p++);
+ if (qRed(c) >= (255 - maxFuzz) && qGreen(c) == 0 && qBlue(c) == 0)
+ ++redCount;
+ else if (qRed(c) == 0 && qGreen(c) == 0 && qBlue(c) >= (255 - maxFuzz))
+ ++blueCount;
+ else
+ QFAIL("Encountered a pixel that is neither red or blue");
+ }
+
+ QVERIFY(redCount > 0 && blueCount > 0);
QCOMPARE(redCount + blueCount, outputSize.width());
if (rhi->isYUpInFramebuffer() == rhi->isYUpInNDC())
diff --git a/tests/manual/rhi/CMakeLists.txt b/tests/manual/rhi/CMakeLists.txt
index f8879bbd0f..11117a00ae 100644
--- a/tests/manual/rhi/CMakeLists.txt
+++ b/tests/manual/rhi/CMakeLists.txt
@@ -22,6 +22,7 @@ add_subdirectory(computeimage)
add_subdirectory(instancing)
add_subdirectory(noninstanced)
add_subdirectory(tex3d)
+add_subdirectory(texturearray)
if(QT_FEATURE_widgets)
add_subdirectory(qrhiprof)
endif()
diff --git a/tests/manual/rhi/shared/buildshaders.bat b/tests/manual/rhi/shared/buildshaders.bat
index 7974683bd7..d0212bc7a7 100644
--- a/tests/manual/rhi/shared/buildshaders.bat
+++ b/tests/manual/rhi/shared/buildshaders.bat
@@ -3,3 +3,5 @@ qsb --glsl "100 es,120" --hlsl 50 --msl 12 -c color.frag -o color.frag.qsb
qsb --glsl "100 es,120" --hlsl 50 --msl 12 -c texture.vert -o texture.vert.qsb
qsb --glsl "100 es,120" --hlsl 50 --msl 12 -c texture.frag -o texture.frag.qsb
qsb --glsl "310 es,150" --hlsl 50 --msl 12 -c texture_ms4.frag -o texture_ms4.frag.qsb
+qsb --glsl "100 es,120,150" --hlsl 50 --msl 12 -c texture_arr.vert -o texture_arr.vert.qsb
+qsb --glsl "100 es,120,150" --hlsl 50 --msl 12 -c texture_arr.frag -o texture_arr.frag.qsb
diff --git a/tests/manual/rhi/shared/examplefw.h b/tests/manual/rhi/shared/examplefw.h
index 83b5a73514..e7bc3e6b5d 100644
--- a/tests/manual/rhi/shared/examplefw.h
+++ b/tests/manual/rhi/shared/examplefw.h
@@ -109,7 +109,7 @@ QString graphicsApiName()
case Null:
return QLatin1String("Null (no output)");
case OpenGL:
- return QLatin1String("OpenGL 2.x");
+ return QLatin1String("OpenGL");
case Vulkan:
return QLatin1String("Vulkan");
case D3D11:
@@ -462,7 +462,7 @@ int main(int argc, char **argv)
cmdLineParser.addHelpOption();
QCommandLineOption nullOption({ "n", "null" }, QLatin1String("Null"));
cmdLineParser.addOption(nullOption);
- QCommandLineOption glOption({ "g", "opengl" }, QLatin1String("OpenGL (2.x)"));
+ QCommandLineOption glOption({ "g", "opengl" }, QLatin1String("OpenGL"));
cmdLineParser.addOption(glOption);
QCommandLineOption vkOption({ "v", "vulkan" }, QLatin1String("Vulkan"));
cmdLineParser.addOption(vkOption);
@@ -475,6 +475,8 @@ int main(int argc, char **argv)
// Use this parameter for the latter.
QCommandLineOption sdOption({ "s", "self-destruct" }, QLatin1String("Self-destruct after 5 seconds."));
cmdLineParser.addOption(sdOption);
+ QCommandLineOption coreProfOption({ "c", "core" }, QLatin1String("Request a core profile context for OpenGL"));
+ cmdLineParser.addOption(coreProfOption);
// Attempt testing device lost situations on D3D at least.
QCommandLineOption tdrOption(QLatin1String("curse"), QLatin1String("Curse the graphics device. "
"(generate a device reset every <count> frames when on D3D11)"),
@@ -517,6 +519,14 @@ int main(int argc, char **argv)
QSurfaceFormat fmt;
fmt.setDepthBufferSize(24);
fmt.setStencilBufferSize(8);
+ if (cmdLineParser.isSet(coreProfOption)) {
+#ifdef Q_OS_DARWIN
+ fmt.setVersion(4, 1);
+#else
+ fmt.setVersion(4, 3);
+#endif
+ fmt.setProfile(QSurfaceFormat::CoreProfile);
+ }
if (sampleCount > 1)
fmt.setSamples(sampleCount);
if (scFlags.testFlag(QRhiSwapChain::NoVSync))
diff --git a/tests/manual/rhi/shared/texture_arr.frag b/tests/manual/rhi/shared/texture_arr.frag
new file mode 100644
index 0000000000..0a2631e2bd
--- /dev/null
+++ b/tests/manual/rhi/shared/texture_arr.frag
@@ -0,0 +1,19 @@
+#version 440
+
+layout(location = 0) in vec2 v_texcoord;
+
+layout(location = 0) out vec4 fragColor;
+
+layout(std140, binding = 0) uniform buf {
+ mat4 mvp;
+ int flip;
+ int array_index;
+};
+
+layout(binding = 1) uniform sampler2DArray texArr;
+
+void main()
+{
+ vec4 c = texture(texArr, vec3(v_texcoord, float(array_index)));
+ fragColor = vec4(c.rgb * c.a, c.a);
+}
diff --git a/tests/manual/rhi/shared/texture_arr.frag.qsb b/tests/manual/rhi/shared/texture_arr.frag.qsb
new file mode 100644
index 0000000000..3674fdad1d
--- /dev/null
+++ b/tests/manual/rhi/shared/texture_arr.frag.qsb
Binary files differ
diff --git a/tests/manual/rhi/shared/texture_arr.vert b/tests/manual/rhi/shared/texture_arr.vert
new file mode 100644
index 0000000000..137263e080
--- /dev/null
+++ b/tests/manual/rhi/shared/texture_arr.vert
@@ -0,0 +1,20 @@
+#version 440
+
+layout(location = 0) in vec4 position;
+layout(location = 1) in vec2 texcoord;
+
+layout(location = 0) out vec2 v_texcoord;
+
+layout(std140, binding = 0) uniform buf {
+ mat4 mvp;
+ int flip;
+ int array_index;
+};
+
+void main()
+{
+ v_texcoord = vec2(texcoord.x, texcoord.y);
+ if (flip != 0)
+ v_texcoord.y = 1.0 - v_texcoord.y;
+ gl_Position = mvp * position;
+}
diff --git a/tests/manual/rhi/shared/texture_arr.vert.qsb b/tests/manual/rhi/shared/texture_arr.vert.qsb
new file mode 100644
index 0000000000..fe46d2c385
--- /dev/null
+++ b/tests/manual/rhi/shared/texture_arr.vert.qsb
Binary files differ
diff --git a/tests/manual/rhi/texturearray/CMakeLists.txt b/tests/manual/rhi/texturearray/CMakeLists.txt
new file mode 100644
index 0000000000..39e49d5287
--- /dev/null
+++ b/tests/manual/rhi/texturearray/CMakeLists.txt
@@ -0,0 +1,26 @@
+qt_internal_add_manual_test(texturearray
+ GUI
+ SOURCES
+ texturearray.cpp
+ PUBLIC_LIBRARIES
+ Qt::Gui
+ Qt::GuiPrivate
+)
+
+set_source_files_properties("../shared/texture_arr.vert.qsb"
+ PROPERTIES QT_RESOURCE_ALIAS "texture_arr.vert.qsb"
+)
+set_source_files_properties("../shared/texture_arr.frag.qsb"
+ PROPERTIES QT_RESOURCE_ALIAS "texture_arr.frag.qsb"
+)
+set(texturearray_resource_files
+ "../shared/texture_arr.vert.qsb"
+ "../shared/texture_arr.frag.qsb"
+)
+
+qt_internal_add_resource(texturearray "texturearray"
+ PREFIX
+ "/"
+ FILES
+ ${texturearray_resource_files}
+)
diff --git a/tests/manual/rhi/texturearray/texturearray.cpp b/tests/manual/rhi/texturearray/texturearray.cpp
new file mode 100644
index 0000000000..206730464e
--- /dev/null
+++ b/tests/manual/rhi/texturearray/texturearray.cpp
@@ -0,0 +1,198 @@
+/****************************************************************************
+**
+** Copyright (C) 2021 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the examples of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:BSD$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** BSD License Usage
+** Alternatively, you may use this file under the terms of the BSD license
+** as follows:
+**
+** "Redistribution and use in source and binary forms, with or without
+** modification, are permitted provided that the following conditions are
+** met:
+** * Redistributions of source code must retain the above copyright
+** notice, this list of conditions and the following disclaimer.
+** * Redistributions in binary form must reproduce the above copyright
+** notice, this list of conditions and the following disclaimer in
+** the documentation and/or other materials provided with the
+** distribution.
+** * Neither the name of The Qt Company Ltd nor the names of its
+** contributors may be used to endorse or promote products derived
+** from this software without specific prior written permission.
+**
+**
+** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "../shared/examplefw.h"
+#include <QElapsedTimer>
+
+// Creates a texture array object with size 4, uploads a different
+// image to each, and cycles through them on-screen.
+
+static const int ARRAY_SIZE = 4;
+static const int UBUF_SIZE = 72;
+
+static float vertexData[] =
+{ // Y up, CCW
+ -0.5f, 0.5f, 0.0f, 0.0f,
+ -0.5f, -0.5f, 0.0f, 1.0f,
+ 0.5f, -0.5f, 1.0f, 1.0f,
+ 0.5f, 0.5f, 1.0f, 0.0f
+};
+
+static quint16 indexData[] =
+{
+ 0, 1, 2, 0, 2, 3
+};
+
+struct {
+ QList<QRhiResource *> releasePool;
+ QRhiTexture *texArr = nullptr;
+ QRhiSampler *sampler = nullptr;
+ QRhiShaderResourceBindings *srb = nullptr;
+ QRhiBuffer *vbuf = nullptr;
+ QRhiBuffer *ibuf = nullptr;
+ QRhiBuffer *ubuf = nullptr;
+ QRhiGraphicsPipeline *ps = nullptr;
+ QRhiResourceUpdateBatch *initialUpdates = nullptr;
+ QMatrix4x4 winProj;
+ QElapsedTimer t;
+ int arrayIndex = 0;
+} d;
+
+void Window::customInit()
+{
+ if (!m_r->isFeatureSupported(QRhi::TextureArrays))
+ qFatal("Texture array objects are not supported by this backend");
+
+ d.texArr = m_r->newTextureArray(QRhiTexture::RGBA8, ARRAY_SIZE, QSize(512, 512));
+ d.releasePool << d.texArr;
+ d.texArr->create();
+
+ d.initialUpdates = m_r->nextResourceUpdateBatch();
+
+ QImage img(512, 512, QImage::Format_RGBA8888);
+ img.fill(Qt::red);
+ d.initialUpdates->uploadTexture(d.texArr, QRhiTextureUploadDescription(QRhiTextureUploadEntry(0, 0, QRhiTextureSubresourceUploadDescription(img))));
+ img.fill(Qt::green);
+ d.initialUpdates->uploadTexture(d.texArr, QRhiTextureUploadDescription(QRhiTextureUploadEntry(1, 0, QRhiTextureSubresourceUploadDescription(img))));
+ img.fill(Qt::blue);
+ d.initialUpdates->uploadTexture(d.texArr, QRhiTextureUploadDescription(QRhiTextureUploadEntry(2, 0, QRhiTextureSubresourceUploadDescription(img))));
+ img.fill(Qt::yellow);
+ d.initialUpdates->uploadTexture(d.texArr, QRhiTextureUploadDescription(QRhiTextureUploadEntry(3, 0, QRhiTextureSubresourceUploadDescription(img))));
+
+ d.sampler = m_r->newSampler(QRhiSampler::Linear, QRhiSampler::Linear, QRhiSampler::None,
+ QRhiSampler::ClampToEdge, QRhiSampler::ClampToEdge);
+ d.releasePool << d.sampler;
+ d.sampler->create();
+
+ d.ubuf = m_r->newBuffer(QRhiBuffer::Dynamic, QRhiBuffer::UniformBuffer, UBUF_SIZE);
+ d.releasePool << d.ubuf;
+ d.ubuf->create();
+
+ d.srb = m_r->newShaderResourceBindings();
+ d.releasePool << d.srb;
+ d.srb->setBindings({
+ QRhiShaderResourceBinding::uniformBuffer(0, QRhiShaderResourceBinding::VertexStage | QRhiShaderResourceBinding::FragmentStage, d.ubuf, 0, UBUF_SIZE),
+ QRhiShaderResourceBinding::sampledTexture(1, QRhiShaderResourceBinding::FragmentStage, d.texArr, d.sampler)
+ });
+ d.srb->create();
+
+ d.vbuf = m_r->newBuffer(QRhiBuffer::Immutable, QRhiBuffer::VertexBuffer, sizeof(vertexData));
+ d.releasePool << d.vbuf;
+ d.vbuf->create();
+
+ d.ibuf = m_r->newBuffer(QRhiBuffer::Immutable, QRhiBuffer::IndexBuffer, sizeof(indexData));
+ d.releasePool << d.ibuf;
+ d.ibuf->create();
+
+ d.initialUpdates->uploadStaticBuffer(d.vbuf, 0, sizeof(vertexData), vertexData);
+ d.initialUpdates->uploadStaticBuffer(d.ibuf, indexData);
+
+ d.ps = m_r->newGraphicsPipeline();
+ d.releasePool << d.ps;
+ d.ps->setShaderStages({
+ { QRhiShaderStage::Vertex, getShader(QLatin1String(":/texture_arr.vert.qsb")) },
+ { QRhiShaderStage::Fragment, getShader(QLatin1String(":/texture_arr.frag.qsb")) }
+ });
+ QRhiVertexInputLayout inputLayout;
+ inputLayout.setBindings({ { 4 * sizeof(float) } });
+ inputLayout.setAttributes({
+ { 0, 0, QRhiVertexInputAttribute::Float2, 0 },
+ { 0, 1, QRhiVertexInputAttribute::Float2, quint32(2 * sizeof(float)) }
+ });
+ d.ps->setVertexInputLayout(inputLayout);
+ d.ps->setShaderResourceBindings(d.srb);
+ d.ps->setRenderPassDescriptor(m_rp);
+ d.ps->create();
+
+ d.t.start();
+}
+
+void Window::customRelease()
+{
+ qDeleteAll(d.releasePool);
+ d.releasePool.clear();
+}
+
+void Window::customRender()
+{
+ QRhiCommandBuffer *cb = m_sc->currentFrameCommandBuffer();
+ QRhiResourceUpdateBatch *u = m_r->nextResourceUpdateBatch();
+ if (d.initialUpdates) {
+ u->merge(d.initialUpdates);
+ d.initialUpdates->release();
+ d.initialUpdates = nullptr;
+ }
+
+ if (d.winProj != m_proj) {
+ d.winProj = m_proj;
+ QMatrix4x4 mvp = m_proj;
+ mvp.scale(2);
+ u->updateDynamicBuffer(d.ubuf, 0, 64, mvp.constData());
+ const qint32 flip = 0;
+ u->updateDynamicBuffer(d.ubuf, 64, 4, &flip);
+ u->updateDynamicBuffer(d.ubuf, 68, 4, &d.arrayIndex);
+ }
+
+ if (d.t.elapsed() > 2000) {
+ d.t.restart();
+ d.arrayIndex = (d.arrayIndex + 1) % ARRAY_SIZE;
+ u->updateDynamicBuffer(d.ubuf, 68, 4, &d.arrayIndex);
+ }
+
+ const QSize outputSizeInPixels = m_sc->currentPixelSize();
+ cb->beginPass(m_sc->currentFrameRenderTarget(), m_clearColor, { 1.0f, 0 }, u);
+ cb->setGraphicsPipeline(d.ps);
+ cb->setViewport({ 0, 0, float(outputSizeInPixels.width()), float(outputSizeInPixels.height()) });
+ cb->setShaderResources();
+ QRhiCommandBuffer::VertexInput vbufBinding(d.vbuf, 0);
+ cb->setVertexInput(0, 1, &vbufBinding, d.ibuf, 0, QRhiCommandBuffer::IndexUInt16);
+ cb->drawIndexed(6);
+ cb->endPass();
+}