summaryrefslogtreecommitdiffstats
path: root/src/gui
diff options
context:
space:
mode:
authorLaszlo Agocs <laszlo.agocs@qt.io>2021-10-19 14:11:20 +0200
committerLaszlo Agocs <laszlo.agocs@qt.io>2021-10-29 15:57:13 +0200
commite7a1fbfc471503cd5252a4acba7277639e5b65c2 (patch)
tree324a5af076db2840eeb3c5bee0b3875b03056893 /src/gui
parente7371fa1592cf490eab6cb82bc06ed67c7d154e3 (diff)
rhi: Add texture array support
Arrays of textures have always been supported, but we will encounter cases when we need to work with texture array objects as well. Note that currently it is not possible to expose only a slice of the array to the shader, because there is no dedicated API in the SRB, and thus the same SRV/UAV (or equivalent) is used always, capturing all elements in the array. Therefore in the shader the last component of P in texture() is in range 0..array_size-1. Change-Id: I5a032ed016aeefbbcd743d5bfb9fbc49ba00a1fa Reviewed-by: Andy Nichols <andy.nichols@qt.io>
Diffstat (limited to 'src/gui')
-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
13 files changed, 367 insertions, 112 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,