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>
This commit is contained in:
Laszlo Agocs 2021-10-19 14:11:20 +02:00
parent e7371fa159
commit e7a1fbfc47
24 changed files with 780 additions and 114 deletions

View File

@ -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 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 relying on VK_IMAGE_CREATE_2D_ARRAY_COMPATIBLE_BIT which is a Vulkan 1.1
feature. 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 \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 dimension. Effectively the maximum value of \c local_size_z in the compute
shader. Typically 64 or 256. 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 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 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) 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 When texture() or renderBuffer() is multisample, resolveTexture() can be
set optionally. When set, samples are resolved automatically into that set optionally. When set, samples are resolved automatically into that
@ -1700,7 +1709,9 @@ QRhiTextureSubresourceUploadDescription::QRhiTextureSubresourceUploadDescription
\class QRhiTextureUploadEntry \class QRhiTextureUploadEntry
\internal \internal
\inmodule QtGui \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 sourceTopLeft(), and destinationTopLeft() must fit the source and
destination textures, respectively. The behavior is undefined otherwise. destination textures, respectively. The behavior is undefined otherwise.
With cubemap and 3D textures one face or slice can be copied at a time. The With cubemaps, 3D textures, and texture arrays one face or slice can be
face or slice is specified by the source and destination layer indices. copied at a time. The face or slice is specified by the source and
With mipmapped textures one mip level can be copied at a time. The source destination layer indices. With mipmapped textures one mip level can be
and destination layer and mip level indices can differ, but the size and copied at a time. The source and destination layer and mip level indices can
position must be carefully controlled to avoid out of bounds copies, in differ, but the size and position must be carefully controlled to avoid out
which case the behavior is undefined. 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 \value ThreeDimensional The texture is a 3D texture. Such textures should
be created with the QRhi::newTexture() overload taking a depth in addition 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 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 multisample. When rendering into, or uploading data to a 3D texture, the \c
render target's color attachment refers to a slice in range [0..depth-1]. layer specified in the render target's color attachment or the upload
The underlying graphics API may not support 3D textures at run time. description refers to a single slice in range [0..depth-1]. The underlying
Support is indicated by the QRhi::ThreeDimensionalTextures feature. 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 \value TextureRectangleGL The texture should use the GL_TEXTURE_RECTANGLE
target with OpenGL. This flag is ignored with other graphics APIs. Just 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 native OpenGL texture objects received from the platform are wrapped in a
QRhiTexture, and the platform can only provide textures for a non-2D QRhiTexture, and the platform can only provide textures for a non-2D
texture target. 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 \internal
*/ */
QRhiTexture::QRhiTexture(QRhiImplementation *rhi, Format format_, const QSize &pixelSize_, int depth_, QRhiTexture::QRhiTexture(QRhiImplementation *rhi, Format format_, const QSize &pixelSize_, int depth_,
int sampleCount_, Flags flags_) int arraySize_, int sampleCount_, Flags flags_)
: QRhiResource(rhi), : 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 The opposite of this operation, exposing a QRhiTexture-created native
texture object to a foreign engine, is possible via nativeTexture(). 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) bool QRhiTexture::createFrom(QRhiTexture::NativeTexture src)
{ {
@ -3118,8 +3143,9 @@ void QRhiImplementation::updateLayoutDesc(QRhiShaderResourceBindings *srb)
\inmodule QtGui \inmodule QtGui
\brief Describes the shader resource for a single binding point. \brief Describes the shader resource for a single binding point.
A QRhiShaderResourceBinding cannot be constructed directly. Instead, use A QRhiShaderResourceBinding cannot be constructed directly. Instead, use the
the static functions uniformBuffer(), sampledTexture() to get an instance. 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. QRhi::MaxAsyncReadbackFrames.
A single readback operation copies one mip level of one layer (cubemap face 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 or 3D slice or texture array element) at a time. The level and layer are
fields in \a rb. specified by the respective fields in \a rb.
\sa readBackBuffer(), QRhi::resourceLimit() \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. sampleCount, and \a flags.
\note \a format specifies the requested internal and external format, \note \a format specifies the requested internal and external format,
@ -6511,12 +6537,12 @@ QRhiTexture *QRhi::newTexture(QRhiTexture::Format format,
int sampleCount, int sampleCount,
QRhiTexture::Flags flags) 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 \return a new 2D or 3D texture with the specified \a format, \a width, \a
depth, \a sampleCount, and \a flags. height, \a depth, \a sampleCount, and \a flags.
This overload is suitable for 3D textures because it allows specifying \a This overload is suitable for 3D textures because it allows specifying \a
depth. A 3D texture must have QRhiTexture::ThreeDimensional set in \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 implicitly whenever \a depth is greater than 0. For 2D and cube textures \a
depth should be set to 0. depth should be set to 0.
\note 3D textures are only functional when the ThreeDimensionalTextures
feature is reported as supported at run time.
\overload \overload
*/ */
QRhiTexture *QRhi::newTexture(QRhiTexture::Format format, QRhiTexture *QRhi::newTexture(QRhiTexture::Format format,
@ -6534,7 +6563,36 @@ QRhiTexture *QRhi::newTexture(QRhiTexture::Format format,
if (depth > 0) if (depth > 0)
flags |= QRhiTexture::ThreeDimensional; 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);
} }
/*! /*!

View File

@ -773,7 +773,8 @@ public:
UsedAsCompressedAtlas = 1 << 8, UsedAsCompressedAtlas = 1 << 8,
ExternalOES = 1 << 9, ExternalOES = 1 << 9,
ThreeDimensional = 1 << 10, ThreeDimensional = 1 << 10,
TextureRectangleGL = 1 << 11 TextureRectangleGL = 1 << 11,
TextureArray = 1 << 12
}; };
Q_DECLARE_FLAGS(Flags, Flag) Q_DECLARE_FLAGS(Flags, Flag)
@ -842,6 +843,9 @@ public:
int depth() const { return m_depth; } int depth() const { return m_depth; }
void setDepth(int depth) { m_depth = 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; } Flags flags() const { return m_flags; }
void setFlags(Flags f) { m_flags = f; } void setFlags(Flags f) { m_flags = f; }
@ -855,10 +859,11 @@ public:
protected: protected:
QRhiTexture(QRhiImplementation *rhi, Format format_, const QSize &pixelSize_, int depth_, QRhiTexture(QRhiImplementation *rhi, Format format_, const QSize &pixelSize_, int depth_,
int sampleCount_, Flags flags_); int arraySize_, int sampleCount_, Flags flags_);
Format m_format; Format m_format;
QSize m_pixelSize; QSize m_pixelSize;
int m_depth; int m_depth;
int m_arraySize;
int m_sampleCount; int m_sampleCount;
Flags m_flags; Flags m_flags;
}; };
@ -1583,7 +1588,8 @@ public:
ImageDataStride, ImageDataStride,
RenderBufferImport, RenderBufferImport,
ThreeDimensionalTextures, ThreeDimensionalTextures,
RenderTo3DTextureSlice RenderTo3DTextureSlice,
TextureArrays
}; };
enum BeginFrameFlag { enum BeginFrameFlag {
@ -1605,7 +1611,8 @@ public:
MaxThreadsPerThreadGroup, MaxThreadsPerThreadGroup,
MaxThreadGroupX, MaxThreadGroupX,
MaxThreadGroupY, MaxThreadGroupY,
MaxThreadGroupZ MaxThreadGroupZ,
TextureArraySizeMax
}; };
~QRhi(); ~QRhi();
@ -1648,6 +1655,12 @@ public:
int sampleCount = 1, int sampleCount = 1,
QRhiTexture::Flags flags = {}); 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 *newSampler(QRhiSampler::Filter magFilter,
QRhiSampler::Filter minFilter, QRhiSampler::Filter minFilter,
QRhiSampler::Filter mipmapMode, QRhiSampler::Filter mipmapMode,

View File

@ -88,6 +88,7 @@ public:
virtual QRhiTexture *createTexture(QRhiTexture::Format format, virtual QRhiTexture *createTexture(QRhiTexture::Format format,
const QSize &pixelSize, const QSize &pixelSize,
int depth, int depth,
int arraySize,
int sampleCount, int sampleCount,
QRhiTexture::Flags flags) = 0; QRhiTexture::Flags flags) = 0;
virtual QRhiSampler *createSampler(QRhiSampler::Filter magFilter, virtual QRhiSampler *createSampler(QRhiSampler::Filter magFilter,

View File

@ -543,6 +543,8 @@ bool QRhiD3D11::isFeatureSupported(QRhi::Feature feature) const
return true; return true;
case QRhi::RenderTo3DTextureSlice: case QRhi::RenderTo3DTextureSlice:
return true; return true;
case QRhi::TextureArrays:
return true;
default: default:
Q_UNREACHABLE(); Q_UNREACHABLE();
return false; return false;
@ -576,6 +578,8 @@ int QRhiD3D11::resourceLimit(QRhi::ResourceLimit limit) const
return D3D11_CS_THREAD_GROUP_MAX_Y; return D3D11_CS_THREAD_GROUP_MAX_Y;
case QRhi::MaxThreadGroupZ: case QRhi::MaxThreadGroupZ:
return D3D11_CS_THREAD_GROUP_MAX_Z; return D3D11_CS_THREAD_GROUP_MAX_Z;
case QRhi::TextureArraySizeMax:
return D3D11_REQ_TEXTURE2D_ARRAY_AXIS_DIMENSION;
default: default:
Q_UNREACHABLE(); Q_UNREACHABLE();
return 0; return 0;
@ -631,10 +635,10 @@ QRhiRenderBuffer *QRhiD3D11::createRenderBuffer(QRhiRenderBuffer::Type type, con
} }
QRhiTexture *QRhiD3D11::createTexture(QRhiTexture::Format format, QRhiTexture *QRhiD3D11::createTexture(QRhiTexture::Format format,
const QSize &pixelSize, int depth, const QSize &pixelSize, int depth, int arraySize,
int sampleCount, QRhiTexture::Flags flags) 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, 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, QD3D11Texture::QD3D11Texture(QRhiImplementation *rhi, Format format, const QSize &pixelSize, int depth,
int sampleCount, Flags flags) int arraySize, int sampleCount, Flags flags)
: QRhiTexture(rhi, format, pixelSize, depth, sampleCount, flags) : QRhiTexture(rhi, format, pixelSize, depth, arraySize, sampleCount, flags)
{ {
for (int i = 0; i < QRhi::MAX_MIP_LEVELS; ++i) for (int i = 0; i < QRhi::MAX_MIP_LEVELS; ++i)
perLevelViews[i] = nullptr; perLevelViews[i] = nullptr;
@ -2997,6 +3001,7 @@ bool QD3D11Texture::prepareCreate(QSize *adjustedSize)
const bool isDepth = isDepthTextureFormat(m_format); const bool isDepth = isDepthTextureFormat(m_format);
const bool isCube = m_flags.testFlag(CubeMap); const bool isCube = m_flags.testFlag(CubeMap);
const bool is3D = m_flags.testFlag(ThreeDimensional); const bool is3D = m_flags.testFlag(ThreeDimensional);
const bool isArray = m_flags.testFlag(TextureArray);
const bool hasMipMaps = m_flags.testFlag(MipMapped); const bool hasMipMaps = m_flags.testFlag(MipMapped);
QRHI_RES_RHI(QRhiD3D11); QRHI_RES_RHI(QRhiD3D11);
@ -3025,11 +3030,24 @@ bool QD3D11Texture::prepareCreate(QSize *adjustedSize)
qWarning("Texture cannot be both cube and 3D"); qWarning("Texture cannot be both cube and 3D");
return false; return false;
} }
if (isArray && is3D) {
qWarning("Texture cannot be both array and 3D");
return false;
}
m_depth = qMax(1, m_depth); m_depth = qMax(1, m_depth);
if (m_depth > 1 && !is3D) { if (m_depth > 1 && !is3D) {
qWarning("Texture cannot have a depth of %d when it is not 3D", m_depth); qWarning("Texture cannot have a depth of %d when it is not 3D", m_depth);
return false; 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) if (adjustedSize)
*adjustedSize = size; *adjustedSize = size;
@ -3043,6 +3061,7 @@ bool QD3D11Texture::finishCreate()
const bool isDepth = isDepthTextureFormat(m_format); const bool isDepth = isDepthTextureFormat(m_format);
const bool isCube = m_flags.testFlag(CubeMap); const bool isCube = m_flags.testFlag(CubeMap);
const bool is3D = m_flags.testFlag(ThreeDimensional); const bool is3D = m_flags.testFlag(ThreeDimensional);
const bool isArray = m_flags.testFlag(TextureArray);
D3D11_SHADER_RESOURCE_VIEW_DESC srvDesc; D3D11_SHADER_RESOURCE_VIEW_DESC srvDesc;
memset(&srvDesc, 0, sizeof(srvDesc)); memset(&srvDesc, 0, sizeof(srvDesc));
@ -3051,14 +3070,27 @@ bool QD3D11Texture::finishCreate()
srvDesc.ViewDimension = D3D11_SRV_DIMENSION_TEXTURECUBE; srvDesc.ViewDimension = D3D11_SRV_DIMENSION_TEXTURECUBE;
srvDesc.TextureCube.MipLevels = mipLevelCount; srvDesc.TextureCube.MipLevels = mipLevelCount;
} else { } else {
if (sampleDesc.Count > 1) { if (isArray) {
srvDesc.ViewDimension = D3D11_SRV_DIMENSION_TEXTURE2DMS; if (sampleDesc.Count > 1) {
} else if (is3D) { srvDesc.ViewDimension = D3D11_SRV_DIMENSION_TEXTURE2DMSARRAY;
srvDesc.ViewDimension = D3D11_SRV_DIMENSION_TEXTURE3D; srvDesc.Texture2DMSArray.FirstArraySlice = 0;
srvDesc.Texture3D.MipLevels = mipLevelCount; 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 { } else {
srvDesc.ViewDimension = D3D11_SRV_DIMENSION_TEXTURE2D; if (sampleDesc.Count > 1) {
srvDesc.Texture2D.MipLevels = mipLevelCount; 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 isDepth = isDepthTextureFormat(m_format);
const bool isCube = m_flags.testFlag(CubeMap); const bool isCube = m_flags.testFlag(CubeMap);
const bool is3D = m_flags.testFlag(ThreeDimensional); const bool is3D = m_flags.testFlag(ThreeDimensional);
const bool isArray = m_flags.testFlag(TextureArray);
uint bindFlags = D3D11_BIND_SHADER_RESOURCE; uint bindFlags = D3D11_BIND_SHADER_RESOURCE;
uint miscFlags = isCube ? D3D11_RESOURCE_MISC_TEXTURECUBE : 0; uint miscFlags = isCube ? D3D11_RESOURCE_MISC_TEXTURECUBE : 0;
@ -3108,7 +3141,7 @@ bool QD3D11Texture::create()
desc.Width = UINT(size.width()); desc.Width = UINT(size.width());
desc.Height = UINT(size.height()); desc.Height = UINT(size.height());
desc.MipLevels = mipLevelCount; desc.MipLevels = mipLevelCount;
desc.ArraySize = isCube ? 6 : 1; desc.ArraySize = isCube ? 6 : (isArray ? UINT(m_arraySize) : 1);
desc.Format = dxgiFormat; desc.Format = dxgiFormat;
desc.SampleDesc = sampleDesc; desc.SampleDesc = sampleDesc;
desc.Usage = D3D11_USAGE_DEFAULT; desc.Usage = D3D11_USAGE_DEFAULT;
@ -3147,7 +3180,7 @@ bool QD3D11Texture::create()
return false; return false;
QRHI_PROF; 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; owns = true;
rhiD->registerResource(this); rhiD->registerResource(this);
@ -3170,8 +3203,11 @@ bool QD3D11Texture::createFrom(QRhiTexture::NativeTexture src)
if (!finishCreate()) if (!finishCreate())
return false; return false;
const bool isCube = m_flags.testFlag(CubeMap);
const bool isArray = m_flags.testFlag(TextureArray);
QRHI_PROF; 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; owns = false;
QRHI_RES_RHI(QRhiD3D11); QRHI_RES_RHI(QRhiD3D11);
@ -3190,6 +3226,7 @@ ID3D11UnorderedAccessView *QD3D11Texture::unorderedAccessViewForLevel(int level)
return perLevelViews[level]; return perLevelViews[level];
const bool isCube = m_flags.testFlag(CubeMap); const bool isCube = m_flags.testFlag(CubeMap);
const bool isArray = m_flags.testFlag(TextureArray);
const bool is3D = m_flags.testFlag(ThreeDimensional); const bool is3D = m_flags.testFlag(ThreeDimensional);
D3D11_UNORDERED_ACCESS_VIEW_DESC desc; D3D11_UNORDERED_ACCESS_VIEW_DESC desc;
memset(&desc, 0, sizeof(desc)); memset(&desc, 0, sizeof(desc));
@ -3199,6 +3236,11 @@ ID3D11UnorderedAccessView *QD3D11Texture::unorderedAccessViewForLevel(int level)
desc.Texture2DArray.MipSlice = UINT(level); desc.Texture2DArray.MipSlice = UINT(level);
desc.Texture2DArray.FirstArraySlice = 0; desc.Texture2DArray.FirstArraySlice = 0;
desc.Texture2DArray.ArraySize = 6; 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) { } else if (is3D) {
desc.ViewDimension = D3D11_UAV_DIMENSION_TEXTURE3D; desc.ViewDimension = D3D11_UAV_DIMENSION_TEXTURE3D;
desc.Texture3D.MipSlice = UINT(level); desc.Texture3D.MipSlice = UINT(level);
@ -3483,6 +3525,17 @@ bool QD3D11TextureRenderTarget::create()
rtvDesc.Texture2DArray.MipSlice = UINT(colorAtt.level()); rtvDesc.Texture2DArray.MipSlice = UINT(colorAtt.level());
rtvDesc.Texture2DArray.FirstArraySlice = UINT(colorAtt.layer()); rtvDesc.Texture2DArray.FirstArraySlice = UINT(colorAtt.layer());
rtvDesc.Texture2DArray.ArraySize = 1; 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)) { } else if (texD->flags().testFlag(QRhiTexture::ThreeDimensional)) {
rtvDesc.ViewDimension = D3D11_RTV_DIMENSION_TEXTURE3D; rtvDesc.ViewDimension = D3D11_RTV_DIMENSION_TEXTURE3D;
rtvDesc.Texture3D.MipSlice = UINT(colorAtt.level()); rtvDesc.Texture3D.MipSlice = UINT(colorAtt.level());

View File

@ -102,7 +102,7 @@ struct QD3D11RenderBuffer : public QRhiRenderBuffer
struct QD3D11Texture : public QRhiTexture struct QD3D11Texture : public QRhiTexture
{ {
QD3D11Texture(QRhiImplementation *rhi, Format format, const QSize &pixelSize, int depth, QD3D11Texture(QRhiImplementation *rhi, Format format, const QSize &pixelSize, int depth,
int sampleCount, Flags flags); int arraySize, int sampleCount, Flags flags);
~QD3D11Texture(); ~QD3D11Texture();
void destroy() override; void destroy() override;
bool create() override; bool create() override;
@ -607,6 +607,7 @@ public:
QRhiTexture *createTexture(QRhiTexture::Format format, QRhiTexture *createTexture(QRhiTexture::Format format,
const QSize &pixelSize, const QSize &pixelSize,
int depth, int depth,
int arraySize,
int sampleCount, int sampleCount,
QRhiTexture::Flags flags) override; QRhiTexture::Flags flags) override;
QRhiSampler *createSampler(QRhiSampler::Filter magFilter, QRhiSampler *createSampler(QRhiSampler::Filter magFilter,

View File

@ -397,6 +397,14 @@ QT_BEGIN_NAMESPACE
#define GL_TEXTURE_RECTANGLE 0x84F5 #define GL_TEXTURE_RECTANGLE 0x84F5
#endif #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. Constructs a new QRhiGles2InitParams.
@ -678,6 +686,14 @@ bool QRhiGles2::create(QRhi::Flags flags)
caps.texture3D = caps.ctxMajor >= 3; // 3.0 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) { if (!caps.gles) {
f->glEnable(GL_VERTEX_PROGRAM_POINT_SIZE); f->glEnable(GL_VERTEX_PROGRAM_POINT_SIZE);
f->glEnable(GL_POINT_SPRITE); f->glEnable(GL_POINT_SPRITE);
@ -1074,6 +1090,8 @@ bool QRhiGles2::isFeatureSupported(QRhi::Feature feature) const
return caps.texture3D; return caps.texture3D;
case QRhi::RenderTo3DTextureSlice: case QRhi::RenderTo3DTextureSlice:
return caps.texture3D; return caps.texture3D;
case QRhi::TextureArrays:
return caps.maxTextureArraySize > 0;
default: default:
Q_UNREACHABLE(); Q_UNREACHABLE();
return false; return false;
@ -1105,6 +1123,8 @@ int QRhiGles2::resourceLimit(QRhi::ResourceLimit limit) const
return caps.maxThreadGroupsY; return caps.maxThreadGroupsY;
case QRhi::MaxThreadGroupZ: case QRhi::MaxThreadGroupZ:
return caps.maxThreadGroupsZ; return caps.maxThreadGroupsZ;
case QRhi::TextureArraySizeMax:
return 2048;
default: default:
Q_UNREACHABLE(); Q_UNREACHABLE();
return 0; return 0;
@ -1291,10 +1311,10 @@ QRhiRenderBuffer *QRhiGles2::createRenderBuffer(QRhiRenderBuffer::Type type, con
} }
QRhiTexture *QRhiGles2::createTexture(QRhiTexture::Format format, QRhiTexture *QRhiGles2::createTexture(QRhiTexture::Format format,
const QSize &pixelSize, int depth, const QSize &pixelSize, int depth, int arraySize,
int sampleCount, QRhiTexture::Flags flags) 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, 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); trackedImageBarrier(cbD, texD, QGles2Texture::AccessUpdate);
const bool isCompressed = isCompressedFormat(texD->m_format); const bool isCompressed = isCompressedFormat(texD->m_format);
const bool isCubeMap = texD->m_flags.testFlag(QRhiTexture::CubeMap); 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 faceTargetBase = isCubeMap ? GL_TEXTURE_CUBE_MAP_POSITIVE_X : texD->target;
const GLenum effectiveTarget = faceTargetBase + (isCubeMap ? uint(layer) : 0u);
const QPoint dp = subresDesc.destinationTopLeft(); const QPoint dp = subresDesc.destinationTopLeft();
const QByteArray rawData = subresDesc.data(); const QByteArray rawData = subresDesc.data();
if (!subresDesc.image().isNull()) { if (!subresDesc.image().isNull()) {
@ -1894,11 +1917,11 @@ void QRhiGles2::enqueueSubresUpload(QGles2Texture *texD, QGles2CommandBuffer *cb
} }
cmd.args.subImage.target = texD->target; cmd.args.subImage.target = texD->target;
cmd.args.subImage.texture = texD->texture; 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.level = level;
cmd.args.subImage.dx = dp.x(); cmd.args.subImage.dx = dp.x();
cmd.args.subImage.dy = dp.y(); 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.w = size.width();
cmd.args.subImage.h = size.height(); cmd.args.subImage.h = size.height();
cmd.args.subImage.glformat = texD->glformat; 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.rowLength = 0;
cmd.args.subImage.data = cbD->retainImage(img); cmd.args.subImage.data = cbD->retainImage(img);
} else if (!rawData.isEmpty() && isCompressed) { } else if (!rawData.isEmpty() && isCompressed) {
const bool is3D = texD->flags().testFlag(QRhiTexture::ThreeDimensional); if ((texD->flags().testFlag(QRhiTexture::UsedAsCompressedAtlas) || is3D || isArray)
if ((texD->flags().testFlag(QRhiTexture::UsedAsCompressedAtlas) || is3D)
&& !texD->zeroInitialized) && !texD->zeroInitialized)
{ {
// Create on first upload since glCompressedTexImage2D cannot take // 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); compressedFormatInfo(texD->m_format, texD->m_pixelSize, nullptr, &byteSize, nullptr);
if (is3D) if (is3D)
byteSize *= texD->m_depth; byteSize *= texD->m_depth;
if (isArray)
byteSize *= texD->m_arraySize;
QByteArray zeroBuf(byteSize, 0); QByteArray zeroBuf(byteSize, 0);
QGles2CommandBuffer::Command &cmd(cbD->commands.get()); QGles2CommandBuffer::Command &cmd(cbD->commands.get());
cmd.cmd = QGles2CommandBuffer::Command::CompressedImage; cmd.cmd = QGles2CommandBuffer::Command::CompressedImage;
cmd.args.compressedImage.target = texD->target; cmd.args.compressedImage.target = texD->target;
cmd.args.compressedImage.texture = texD->texture; 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.level = level;
cmd.args.compressedImage.glintformat = texD->glintformat; cmd.args.compressedImage.glintformat = texD->glintformat;
cmd.args.compressedImage.w = texD->m_pixelSize.width(); cmd.args.compressedImage.w = texD->m_pixelSize.width();
cmd.args.compressedImage.h = texD->m_pixelSize.height(); 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.size = byteSize;
cmd.args.compressedImage.data = cbD->retainData(zeroBuf); cmd.args.compressedImage.data = cbD->retainData(zeroBuf);
texD->zeroInitialized = true; texD->zeroInitialized = true;
@ -1943,11 +1967,11 @@ void QRhiGles2::enqueueSubresUpload(QGles2Texture *texD, QGles2CommandBuffer *cb
cmd.cmd = QGles2CommandBuffer::Command::CompressedSubImage; cmd.cmd = QGles2CommandBuffer::Command::CompressedSubImage;
cmd.args.compressedSubImage.target = texD->target; cmd.args.compressedSubImage.target = texD->target;
cmd.args.compressedSubImage.texture = texD->texture; 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.level = level;
cmd.args.compressedSubImage.dx = dp.x(); cmd.args.compressedSubImage.dx = dp.x();
cmd.args.compressedSubImage.dy = dp.y(); 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.w = size.width();
cmd.args.compressedSubImage.h = size.height(); cmd.args.compressedSubImage.h = size.height();
cmd.args.compressedSubImage.glintformat = texD->glintformat; cmd.args.compressedSubImage.glintformat = texD->glintformat;
@ -1958,12 +1982,12 @@ void QRhiGles2::enqueueSubresUpload(QGles2Texture *texD, QGles2CommandBuffer *cb
cmd.cmd = QGles2CommandBuffer::Command::CompressedImage; cmd.cmd = QGles2CommandBuffer::Command::CompressedImage;
cmd.args.compressedImage.target = texD->target; cmd.args.compressedImage.target = texD->target;
cmd.args.compressedImage.texture = texD->texture; 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.level = level;
cmd.args.compressedImage.glintformat = texD->glintformat; cmd.args.compressedImage.glintformat = texD->glintformat;
cmd.args.compressedImage.w = size.width(); cmd.args.compressedImage.w = size.width();
cmd.args.compressedImage.h = size.height(); 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.size = rawData.size();
cmd.args.compressedImage.data = cbD->retainData(rawData); cmd.args.compressedImage.data = cbD->retainData(rawData);
} }
@ -1977,11 +2001,11 @@ void QRhiGles2::enqueueSubresUpload(QGles2Texture *texD, QGles2CommandBuffer *cb
cmd.cmd = QGles2CommandBuffer::Command::SubImage; cmd.cmd = QGles2CommandBuffer::Command::SubImage;
cmd.args.subImage.target = texD->target; cmd.args.subImage.target = texD->target;
cmd.args.subImage.texture = texD->texture; 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.level = level;
cmd.args.subImage.dx = dp.x(); cmd.args.subImage.dx = dp.x();
cmd.args.subImage.dy = dp.y(); 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.w = size.width();
cmd.args.subImage.h = size.height(); cmd.args.subImage.h = size.height();
cmd.args.subImage.glformat = texD->glformat; cmd.args.subImage.glformat = texD->glformat;
@ -2090,21 +2114,24 @@ void QRhiGles2::enqueueResourceUpdates(QRhiCommandBuffer *cb, QRhiResourceUpdate
QGles2CommandBuffer::Command &cmd(cbD->commands.get()); QGles2CommandBuffer::Command &cmd(cbD->commands.get());
cmd.cmd = QGles2CommandBuffer::Command::CopyTex; 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.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.srcTexture = srcD->texture;
cmd.args.copyTex.srcLevel = u.desc.sourceLevel(); cmd.args.copyTex.srcLevel = u.desc.sourceLevel();
cmd.args.copyTex.srcX = sp.x(); cmd.args.copyTex.srcX = sp.x();
cmd.args.copyTex.srcY = sp.y(); 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.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.dstTexture = dstD->texture;
cmd.args.copyTex.dstLevel = u.desc.destinationLevel(); cmd.args.copyTex.dstLevel = u.desc.destinationLevel();
cmd.args.copyTex.dstX = dp.x(); cmd.args.copyTex.dstX = dp.x();
cmd.args.copyTex.dstY = dp.y(); 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.w = copySize.width();
cmd.args.copyTex.h = copySize.height(); 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.w = readImageSize.width();
cmd.args.readPixels.h = readImageSize.height(); cmd.args.readPixels.h = readImageSize.height();
cmd.args.readPixels.format = texD->m_format; 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.readTarget = texD->target;
cmd.args.readPixels.slice3D = u.rb.layer(); cmd.args.readPixels.slice3D = u.rb.layer();
} else { } else {
@ -2843,7 +2872,7 @@ void QRhiGles2::executeCommandBuffer(QRhiCommandBuffer *cb)
GLuint fbo; GLuint fbo;
f->glGenFramebuffers(1, &fbo); f->glGenFramebuffers(1, &fbo);
f->glBindFramebuffer(GL_FRAMEBUFFER, 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, f->glFramebufferTextureLayer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, cmd.args.copyTex.srcTexture,
cmd.args.copyTex.srcLevel, cmd.args.copyTex.srcZ); cmd.args.copyTex.srcLevel, cmd.args.copyTex.srcZ);
} else { } else {
@ -2851,7 +2880,7 @@ void QRhiGles2::executeCommandBuffer(QRhiCommandBuffer *cb)
cmd.args.copyTex.srcFaceTarget, cmd.args.copyTex.srcTexture, cmd.args.copyTex.srcLevel); cmd.args.copyTex.srcFaceTarget, cmd.args.copyTex.srcTexture, cmd.args.copyTex.srcLevel);
} }
f->glBindTexture(cmd.args.copyTex.dstTarget, cmd.args.copyTex.dstTexture); 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, 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.dstX, cmd.args.copyTex.dstY, cmd.args.copyTex.dstZ,
cmd.args.copyTex.srcX, cmd.args.copyTex.srcY, 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); f->glPixelStorei(GL_UNPACK_ALIGNMENT, cmd.args.subImage.rowStartAlign);
if (cmd.args.subImage.rowLength != 0) if (cmd.args.subImage.rowLength != 0)
f->glPixelStorei(GL_UNPACK_ROW_LENGTH, cmd.args.subImage.rowLength); 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, 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.dx, cmd.args.subImage.dy, cmd.args.subImage.dz,
cmd.args.subImage.w, cmd.args.subImage.h, 1, cmd.args.subImage.w, cmd.args.subImage.h, 1,
@ -2971,7 +3000,7 @@ void QRhiGles2::executeCommandBuffer(QRhiCommandBuffer *cb)
break; break;
case QGles2CommandBuffer::Command::CompressedImage: case QGles2CommandBuffer::Command::CompressedImage:
f->glBindTexture(cmd.args.compressedImage.target, cmd.args.compressedImage.texture); 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, f->glCompressedTexImage3D(cmd.args.compressedImage.target, cmd.args.compressedImage.level,
cmd.args.compressedImage.glintformat, cmd.args.compressedImage.glintformat,
cmd.args.compressedImage.w, cmd.args.compressedImage.h, cmd.args.compressedImage.depth, cmd.args.compressedImage.w, cmd.args.compressedImage.h, cmd.args.compressedImage.depth,
@ -2985,7 +3014,7 @@ void QRhiGles2::executeCommandBuffer(QRhiCommandBuffer *cb)
break; break;
case QGles2CommandBuffer::Command::CompressedSubImage: case QGles2CommandBuffer::Command::CompressedSubImage:
f->glBindTexture(cmd.args.compressedSubImage.target, cmd.args.compressedSubImage.texture); 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, 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.dx, cmd.args.compressedSubImage.dy, cmd.args.compressedSubImage.dz,
cmd.args.compressedSubImage.w, cmd.args.compressedSubImage.h, 1, 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, f->glFramebufferRenderbuffer(GL_READ_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
GL_RENDERBUFFER, cmd.args.blitFromRb.renderbuffer); GL_RENDERBUFFER, cmd.args.blitFromRb.renderbuffer);
f->glBindFramebuffer(GL_DRAW_FRAMEBUFFER, fbo[1]); f->glBindFramebuffer(GL_DRAW_FRAMEBUFFER, fbo[1]);
if (cmd.args.blitFromRb.target == GL_TEXTURE_3D || cmd.args.blitFromRb.target == GL_TEXTURE_2D_ARRAY) {
f->glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, cmd.args.blitFromRb.target, f->glFramebufferTextureLayer(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
cmd.args.blitFromRb.texture, cmd.args.blitFromRb.dstLevel); 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, f->glBlitFramebuffer(0, 0, cmd.args.blitFromRb.w, cmd.args.blitFromRb.h,
0, 0, cmd.args.blitFromRb.w, cmd.args.blitFromRb.h, 0, 0, cmd.args.blitFromRb.w, cmd.args.blitFromRb.h,
GL_COLOR_BUFFER_BIT, GL_COLOR_BUFFER_BIT,
@ -3755,11 +3788,15 @@ void QRhiGles2::endPass(QRhiCommandBuffer *cb, QRhiResourceUpdateBatch *resource
cmd.args.blitFromRb.w = size.width(); cmd.args.blitFromRb.w = size.width();
cmd.args.blitFromRb.h = size.height(); cmd.args.blitFromRb.h = size.height();
QGles2Texture *colorTexD = QRHI_RES(QGles2Texture, colorAtt.resolveTexture()); QGles2Texture *colorTexD = QRHI_RES(QGles2Texture, colorAtt.resolveTexture());
const GLenum faceTargetBase = colorTexD->m_flags.testFlag(QRhiTexture::CubeMap) ? GL_TEXTURE_CUBE_MAP_POSITIVE_X if (colorTexD->m_flags.testFlag(QRhiTexture::CubeMap))
: colorTexD->target; cmd.args.blitFromRb.target = GL_TEXTURE_CUBE_MAP_POSITIVE_X + uint(colorAtt.resolveLayer());
cmd.args.blitFromRb.target = faceTargetBase + uint(colorAtt.resolveLayer()); else
cmd.args.blitFromRb.target = colorTexD->target;
cmd.args.blitFromRb.texture = colorTexD->texture; cmd.args.blitFromRb.texture = colorTexD->texture;
cmd.args.blitFromRb.dstLevel = colorAtt.resolveLevel(); 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, QGles2Texture::QGles2Texture(QRhiImplementation *rhi, Format format, const QSize &pixelSize, int depth,
int sampleCount, Flags flags) int arraySize, int sampleCount, Flags flags)
: QRhiTexture(rhi, format, pixelSize, depth, sampleCount, 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 QSize size = m_pixelSize.isEmpty() ? QSize(1, 1) : m_pixelSize;
const bool isCube = m_flags.testFlag(CubeMap); const bool isCube = m_flags.testFlag(CubeMap);
const bool isArray = m_flags.testFlag(QRhiTexture::TextureArray);
const bool is3D = m_flags.testFlag(ThreeDimensional); const bool is3D = m_flags.testFlag(ThreeDimensional);
const bool hasMipMaps = m_flags.testFlag(MipMapped); const bool hasMipMaps = m_flags.testFlag(MipMapped);
const bool isCompressed = rhiD->isCompressedFormat(m_format); const bool isCompressed = rhiD->isCompressedFormat(m_format);
@ -4634,14 +4672,28 @@ bool QGles2Texture::prepareCreate(QSize *adjustedSize)
qWarning("Texture cannot be both cube and 3D"); qWarning("Texture cannot be both cube and 3D");
return false; return false;
} }
if (isArray && is3D) {
qWarning("Texture cannot be both array and 3D");
return false;
}
m_depth = qMax(1, m_depth); m_depth = qMax(1, m_depth);
if (m_depth > 1 && !is3D) { if (m_depth > 1 && !is3D) {
qWarning("Texture cannot have a depth of %d when it is not 3D", m_depth); qWarning("Texture cannot have a depth of %d when it is not 3D", m_depth);
return false; 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 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)) if (m_flags.testFlag(ExternalOES))
target = GL_TEXTURE_EXTERNAL_OES; target = GL_TEXTURE_EXTERNAL_OES;
else if (m_flags.testFlag(TextureRectangleGL)) else if (m_flags.testFlag(TextureRectangleGL))
@ -4687,21 +4739,23 @@ bool QGles2Texture::create()
rhiD->f->glGenTextures(1, &texture); rhiD->f->glGenTextures(1, &texture);
const bool isCube = m_flags.testFlag(CubeMap); const bool isCube = m_flags.testFlag(CubeMap);
const bool isArray = m_flags.testFlag(QRhiTexture::TextureArray);
const bool is3D = m_flags.testFlag(ThreeDimensional); const bool is3D = m_flags.testFlag(ThreeDimensional);
const bool hasMipMaps = m_flags.testFlag(MipMapped); const bool hasMipMaps = m_flags.testFlag(MipMapped);
const bool isCompressed = rhiD->isCompressedFormat(m_format); const bool isCompressed = rhiD->isCompressedFormat(m_format);
if (!isCompressed) { if (!isCompressed) {
rhiD->f->glBindTexture(target, texture); rhiD->f->glBindTexture(target, texture);
if (!m_flags.testFlag(UsedWithLoadStore)) { if (!m_flags.testFlag(UsedWithLoadStore)) {
if (is3D) { if (is3D || isArray) {
const int layerCount = is3D ? m_depth : m_arraySize;
if (hasMipMaps) { if (hasMipMaps) {
for (int level = 0; level != mipLevelCount; ++level) { for (int level = 0; level != mipLevelCount; ++level) {
const QSize mipSize = rhiD->q->sizeForMipLevel(level, size); 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); 0, glformat, gltype, nullptr);
} }
} else { } 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); 0, glformat, gltype, nullptr);
} }
} else if (hasMipMaps || isCube) { } else if (hasMipMaps || isCube) {
@ -4722,8 +4776,8 @@ bool QGles2Texture::create()
// Must be specified with immutable storage functions otherwise // Must be specified with immutable storage functions otherwise
// bindImageTexture may fail. Also, the internal format must be a // bindImageTexture may fail. Also, the internal format must be a
// sized format here. // sized format here.
if (is3D) if (is3D || isArray)
rhiD->f->glTexStorage3D(target, mipLevelCount, glsizedintformat, size.width(), size.height(), m_depth); rhiD->f->glTexStorage3D(target, mipLevelCount, glsizedintformat, size.width(), size.height(), is3D ? m_depth : m_arraySize);
else else
rhiD->f->glTexStorage2D(target, mipLevelCount, glsizedintformat, size.width(), size.height()); rhiD->f->glTexStorage2D(target, mipLevelCount, glsizedintformat, size.width(), size.height());
} }
@ -4942,7 +4996,7 @@ bool QGles2TextureRenderTarget::create()
if (texture) { if (texture) {
QGles2Texture *texD = QRHI_RES(QGles2Texture, texture); QGles2Texture *texD = QRHI_RES(QGles2Texture, texture);
Q_ASSERT(texD->texture && texD->specified); 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, rhiD->f->glFramebufferTextureLayer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0 + uint(attIndex), texD->texture,
colorAtt.level(), colorAtt.layer()); colorAtt.level(), colorAtt.layer());
} else { } else {

View File

@ -139,7 +139,7 @@ inline bool operator!=(const QGles2SamplerData &a, const QGles2SamplerData &b)
struct QGles2Texture : public QRhiTexture struct QGles2Texture : public QRhiTexture
{ {
QGles2Texture(QRhiImplementation *rhi, Format format, const QSize &pixelSize, int depth, QGles2Texture(QRhiImplementation *rhi, Format format, const QSize &pixelSize, int depth,
int sampleCount, Flags flags); int arraySize, int sampleCount, Flags flags);
~QGles2Texture(); ~QGles2Texture();
void destroy() override; void destroy() override;
bool create() override; bool create() override;
@ -521,6 +521,7 @@ struct QGles2CommandBuffer : public QRhiCommandBuffer
GLenum target; GLenum target;
GLuint texture; GLuint texture;
int dstLevel; int dstLevel;
int dstLayer;
} blitFromRb; } blitFromRb;
struct { struct {
GLenum target; GLenum target;
@ -752,6 +753,7 @@ public:
QRhiTexture *createTexture(QRhiTexture::Format format, QRhiTexture *createTexture(QRhiTexture::Format format,
const QSize &pixelSize, const QSize &pixelSize,
int depth, int depth,
int arraySize,
int sampleCount, int sampleCount,
QRhiTexture::Flags flags) override; QRhiTexture::Flags flags) override;
QRhiSampler *createSampler(QRhiSampler::Filter magFilter, QRhiSampler *createSampler(QRhiSampler::Filter magFilter,
@ -910,6 +912,7 @@ public:
maxTextureSize(2048), maxTextureSize(2048),
maxDrawBuffers(4), maxDrawBuffers(4),
maxSamples(16), maxSamples(16),
maxTextureArraySize(0),
maxThreadGroupsPerDimension(0), maxThreadGroupsPerDimension(0),
maxThreadsPerThreadGroup(0), maxThreadsPerThreadGroup(0),
maxThreadGroupsX(0), maxThreadGroupsX(0),
@ -951,6 +954,7 @@ public:
int maxTextureSize; int maxTextureSize;
int maxDrawBuffers; int maxDrawBuffers;
int maxSamples; int maxSamples;
int maxTextureArraySize;
int maxThreadGroupsPerDimension; int maxThreadGroupsPerDimension;
int maxThreadsPerThreadGroup; int maxThreadsPerThreadGroup;
int maxThreadGroupsX; int maxThreadGroupsX;

View File

@ -613,6 +613,8 @@ bool QRhiMetal::isFeatureSupported(QRhi::Feature feature) const
return true; return true;
case QRhi::RenderTo3DTextureSlice: case QRhi::RenderTo3DTextureSlice:
return true; return true;
case QRhi::TextureArrays:
return true;
default: default:
Q_UNREACHABLE(); Q_UNREACHABLE();
return false; return false;
@ -646,6 +648,8 @@ int QRhiMetal::resourceLimit(QRhi::ResourceLimit limit) const
#else #else
return 512; return 512;
#endif #endif
case QRhi::TextureArraySizeMax:
return 2048;
default: default:
Q_UNREACHABLE(); Q_UNREACHABLE();
return 0; return 0;
@ -704,10 +708,10 @@ QRhiRenderBuffer *QRhiMetal::createRenderBuffer(QRhiRenderBuffer::Type type, con
} }
QRhiTexture *QRhiMetal::createTexture(QRhiTexture::Format format, QRhiTexture *QRhiMetal::createTexture(QRhiTexture::Format format,
const QSize &pixelSize, int depth, const QSize &pixelSize, int depth, int arraySize,
int sampleCount, QRhiTexture::Flags flags) 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, 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, QMetalTexture::QMetalTexture(QRhiImplementation *rhi, Format format, const QSize &pixelSize, int depth,
int sampleCount, Flags flags) int arraySize, int sampleCount, Flags flags)
: QRhiTexture(rhi, format, pixelSize, depth, sampleCount, flags), : QRhiTexture(rhi, format, pixelSize, depth, arraySize, sampleCount, flags),
d(new QMetalTextureData(this)) d(new QMetalTextureData(this))
{ {
for (int i = 0; i < QMTL_FRAMES_IN_FLIGHT; ++i) 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 QSize size = m_pixelSize.isEmpty() ? QSize(1, 1) : m_pixelSize;
const bool isCube = m_flags.testFlag(CubeMap); const bool isCube = m_flags.testFlag(CubeMap);
const bool is3D = m_flags.testFlag(ThreeDimensional); const bool is3D = m_flags.testFlag(ThreeDimensional);
const bool isArray = m_flags.testFlag(TextureArray);
const bool hasMipMaps = m_flags.testFlag(MipMapped); const bool hasMipMaps = m_flags.testFlag(MipMapped);
QRHI_RES_RHI(QRhiMetal); QRHI_RES_RHI(QRhiMetal);
@ -2697,11 +2702,24 @@ bool QMetalTexture::prepareCreate(QSize *adjustedSize)
qWarning("Texture cannot be both cube and 3D"); qWarning("Texture cannot be both cube and 3D");
return false; return false;
} }
if (isArray && is3D) {
qWarning("Texture cannot be both array and 3D");
return false;
}
m_depth = qMax(1, m_depth); m_depth = qMax(1, m_depth);
if (m_depth > 1 && !is3D) { if (m_depth > 1 && !is3D) {
qWarning("Texture cannot have a depth of %d when it is not 3D", m_depth); qWarning("Texture cannot have a depth of %d when it is not 3D", m_depth);
return false; 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) if (adjustedSize)
*adjustedSize = size; *adjustedSize = size;
@ -2719,12 +2737,24 @@ bool QMetalTexture::create()
const bool isCube = m_flags.testFlag(CubeMap); const bool isCube = m_flags.testFlag(CubeMap);
const bool is3D = m_flags.testFlag(ThreeDimensional); const bool is3D = m_flags.testFlag(ThreeDimensional);
if (isCube) const bool isArray = m_flags.testFlag(TextureArray);
if (isCube) {
desc.textureType = MTLTextureTypeCube; desc.textureType = MTLTextureTypeCube;
else if (is3D) } else if (is3D) {
desc.textureType = MTLTextureType3D; 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.textureType = samples > 1 ? MTLTextureType2DMultisample : MTLTextureType2D;
}
desc.pixelFormat = d->format; desc.pixelFormat = d->format;
desc.width = NSUInteger(size.width()); desc.width = NSUInteger(size.width());
desc.height = NSUInteger(size.height()); desc.height = NSUInteger(size.height());
@ -2732,6 +2762,8 @@ bool QMetalTexture::create()
desc.mipmapLevelCount = NSUInteger(mipLevelCount); desc.mipmapLevelCount = NSUInteger(mipLevelCount);
if (samples > 1) if (samples > 1)
desc.sampleCount = NSUInteger(samples); desc.sampleCount = NSUInteger(samples);
if (isArray)
desc.arrayLength = NSUInteger(m_arraySize);
desc.resourceOptions = MTLResourceStorageModePrivate; desc.resourceOptions = MTLResourceStorageModePrivate;
desc.storageMode = MTLStorageModePrivate; desc.storageMode = MTLStorageModePrivate;
desc.usage = MTLTextureUsageShaderRead; desc.usage = MTLTextureUsageShaderRead;
@ -2750,7 +2782,7 @@ bool QMetalTexture::create()
d->owns = true; d->owns = true;
QRHI_PROF; 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; lastActiveFrameSlot = -1;
generation += 1; generation += 1;
@ -2794,8 +2826,9 @@ id<MTLTexture> QMetalTextureData::viewForLevel(int level)
const MTLTextureType type = [tex textureType]; const MTLTextureType type = [tex textureType];
const bool isCube = q->m_flags.testFlag(QRhiTexture::CubeMap); 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 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; perLevelViews[level] = view;
return view; return view;

View File

@ -103,7 +103,7 @@ struct QMetalTextureData;
struct QMetalTexture : public QRhiTexture struct QMetalTexture : public QRhiTexture
{ {
QMetalTexture(QRhiImplementation *rhi, Format format, const QSize &pixelSize, int depth, QMetalTexture(QRhiImplementation *rhi, Format format, const QSize &pixelSize, int depth,
int sampleCount, Flags flags); int arraySize, int sampleCount, Flags flags);
~QMetalTexture(); ~QMetalTexture();
void destroy() override; void destroy() override;
bool create() override; bool create() override;
@ -366,6 +366,7 @@ public:
QRhiTexture *createTexture(QRhiTexture::Format format, QRhiTexture *createTexture(QRhiTexture::Format format,
const QSize &pixelSize, const QSize &pixelSize,
int depth, int depth,
int arraySize,
int sampleCount, int sampleCount,
QRhiTexture::Flags flags) override; QRhiTexture::Flags flags) override;
QRhiSampler *createSampler(QRhiSampler::Filter magFilter, QRhiSampler *createSampler(QRhiSampler::Filter magFilter,

View File

@ -162,6 +162,8 @@ int QRhiNull::resourceLimit(QRhi::ResourceLimit limit) const
return 0; return 0;
case QRhi::MaxThreadGroupZ: case QRhi::MaxThreadGroupZ:
return 0; return 0;
case QRhi::TextureArraySizeMax:
return 2048;
default: default:
Q_UNREACHABLE(); Q_UNREACHABLE();
return 0; return 0;
@ -219,10 +221,10 @@ QRhiRenderBuffer *QRhiNull::createRenderBuffer(QRhiRenderBuffer::Type type, cons
} }
QRhiTexture *QRhiNull::createTexture(QRhiTexture::Format format, QRhiTexture *QRhiNull::createTexture(QRhiTexture::Format format,
const QSize &pixelSize, int depth, const QSize &pixelSize, int depth, int arraySize,
int sampleCount, QRhiTexture::Flags flags) 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, 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, QNullTexture::QNullTexture(QRhiImplementation *rhi, Format format, const QSize &pixelSize, int depth,
int sampleCount, Flags flags) int arraySize, int sampleCount, Flags flags)
: QRhiTexture(rhi, format, pixelSize, depth, sampleCount, flags) : QRhiTexture(rhi, format, pixelSize, depth, arraySize, sampleCount, flags)
{ {
} }
@ -703,11 +705,13 @@ bool QNullTexture::create()
QRHI_RES_RHI(QRhiNull); QRHI_RES_RHI(QRhiNull);
const bool isCube = m_flags.testFlag(CubeMap); const bool isCube = m_flags.testFlag(CubeMap);
const bool is3D = m_flags.testFlag(ThreeDimensional); const bool is3D = m_flags.testFlag(ThreeDimensional);
const bool isArray = m_flags.testFlag(TextureArray);
const bool hasMipMaps = m_flags.testFlag(MipMapped); const bool hasMipMaps = m_flags.testFlag(MipMapped);
QSize size = m_pixelSize.isEmpty() ? QSize(1, 1) : m_pixelSize; QSize size = m_pixelSize.isEmpty() ? QSize(1, 1) : m_pixelSize;
m_depth = qMax(1, m_depth); m_depth = qMax(1, m_depth);
const int mipLevelCount = hasMipMaps ? rhiD->q->mipLevelsForSize(size) : 1; 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) { if (m_format == RGBA8) {
image.resize(layerCount); image.resize(layerCount);
@ -737,12 +741,13 @@ bool QNullTexture::createFrom(QRhiTexture::NativeTexture src)
QRHI_RES_RHI(QRhiNull); QRHI_RES_RHI(QRhiNull);
const bool isCube = m_flags.testFlag(CubeMap); const bool isCube = m_flags.testFlag(CubeMap);
const bool isArray = m_flags.testFlag(TextureArray);
const bool hasMipMaps = m_flags.testFlag(MipMapped); const bool hasMipMaps = m_flags.testFlag(MipMapped);
QSize size = m_pixelSize.isEmpty() ? QSize(1, 1) : m_pixelSize; QSize size = m_pixelSize.isEmpty() ? QSize(1, 1) : m_pixelSize;
const int mipLevelCount = hasMipMaps ? rhiD->q->mipLevelsForSize(size) : 1; const int mipLevelCount = hasMipMaps ? rhiD->q->mipLevelsForSize(size) : 1;
QRHI_PROF; 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); rhiD->registerResource(this);
return true; return true;

View File

@ -83,7 +83,7 @@ struct QNullRenderBuffer : public QRhiRenderBuffer
struct QNullTexture : public QRhiTexture struct QNullTexture : public QRhiTexture
{ {
QNullTexture(QRhiImplementation *rhi, Format format, const QSize &pixelSize, int depth, QNullTexture(QRhiImplementation *rhi, Format format, const QSize &pixelSize, int depth,
int sampleCount, Flags flags); int arraySize, int sampleCount, Flags flags);
~QNullTexture(); ~QNullTexture();
void destroy() override; void destroy() override;
bool create() override; bool create() override;
@ -224,6 +224,7 @@ public:
QRhiTexture *createTexture(QRhiTexture::Format format, QRhiTexture *createTexture(QRhiTexture::Format format,
const QSize &pixelSize, const QSize &pixelSize,
int depth, int depth,
int arraySize,
int sampleCount, int sampleCount,
QRhiTexture::Flags flags) override; QRhiTexture::Flags flags) override;
QRhiSampler *createSampler(QRhiSampler::Filter magFilter, QRhiSampler *createSampler(QRhiSampler::Filter magFilter,

View File

@ -3454,6 +3454,7 @@ void QRhiVulkan::enqueueResourceUpdates(QVkCommandBuffer *cbD, QRhiResourceUpdat
QVkTexture *utexD = QRHI_RES(QVkTexture, u.dst); QVkTexture *utexD = QRHI_RES(QVkTexture, u.dst);
Q_ASSERT(utexD->m_flags.testFlag(QRhiTexture::UsedWithGenerateMips)); Q_ASSERT(utexD->m_flags.testFlag(QRhiTexture::UsedWithGenerateMips));
const bool isCube = utexD->m_flags.testFlag(QRhiTexture::CubeMap); 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); const bool is3D = utexD->m_flags.testFlag(QRhiTexture::ThreeDimensional);
VkImageLayout origLayout = utexD->usageState.layout; VkImageLayout origLayout = utexD->usageState.layout;
@ -3462,7 +3463,7 @@ void QRhiVulkan::enqueueResourceUpdates(QVkCommandBuffer *cbD, QRhiResourceUpdat
if (!origStage) if (!origStage)
origStage = VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT; 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 w = utexD->m_pixelSize.width();
int h = utexD->m_pixelSize.height(); int h = utexD->m_pixelSize.height();
int depth = is3D ? utexD->m_depth : 1; int depth = is3D ? utexD->m_depth : 1;
@ -4269,6 +4270,8 @@ bool QRhiVulkan::isFeatureSupported(QRhi::Feature feature) const
return true; return true;
case QRhi::RenderTo3DTextureSlice: case QRhi::RenderTo3DTextureSlice:
return caps.texture3DSliceAs2D; return caps.texture3DSliceAs2D;
case QRhi::TextureArrays:
return true;
default: default:
Q_UNREACHABLE(); Q_UNREACHABLE();
return false; return false;
@ -4300,6 +4303,8 @@ int QRhiVulkan::resourceLimit(QRhi::ResourceLimit limit) const
return int(physDevProperties.limits.maxComputeWorkGroupSize[1]); return int(physDevProperties.limits.maxComputeWorkGroupSize[1]);
case QRhi::MaxThreadGroupZ: case QRhi::MaxThreadGroupZ:
return int(physDevProperties.limits.maxComputeWorkGroupSize[2]); return int(physDevProperties.limits.maxComputeWorkGroupSize[2]);
case QRhi::TextureArraySizeMax:
return int(physDevProperties.limits.maxImageArrayLayers);
default: default:
Q_UNREACHABLE(); Q_UNREACHABLE();
return 0; return 0;
@ -4475,10 +4480,10 @@ QRhiRenderBuffer *QRhiVulkan::createRenderBuffer(QRhiRenderBuffer::Type type, co
} }
QRhiTexture *QRhiVulkan::createTexture(QRhiTexture::Format format, QRhiTexture *QRhiVulkan::createTexture(QRhiTexture::Format format,
const QSize &pixelSize, int depth, const QSize &pixelSize, int depth, int arraySize,
int sampleCount, QRhiTexture::Flags flags) 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, QRhiSampler *QRhiVulkan::createSampler(QRhiSampler::Filter magFilter, QRhiSampler::Filter minFilter,
@ -5713,6 +5718,7 @@ bool QVkRenderBuffer::create()
backingTexture = QRHI_RES(QVkTexture, rhiD->createTexture(backingFormat(), backingTexture = QRHI_RES(QVkTexture, rhiD->createTexture(backingFormat(),
m_pixelSize, m_pixelSize,
1, 1,
0,
m_sampleCount, m_sampleCount,
QRhiTexture::RenderTarget | QRhiTexture::UsedAsTransferSource)); QRhiTexture::RenderTarget | QRhiTexture::UsedAsTransferSource));
} else { } else {
@ -5762,8 +5768,8 @@ QRhiTexture::Format QVkRenderBuffer::backingFormat() const
} }
QVkTexture::QVkTexture(QRhiImplementation *rhi, Format format, const QSize &pixelSize, int depth, QVkTexture::QVkTexture(QRhiImplementation *rhi, Format format, const QSize &pixelSize, int depth,
int sampleCount, Flags flags) int arraySize, int sampleCount, Flags flags)
: QRhiTexture(rhi, format, pixelSize, depth, sampleCount, flags) : QRhiTexture(rhi, format, pixelSize, depth, arraySize, sampleCount, flags)
{ {
for (int i = 0; i < QVK_FRAMES_IN_FLIGHT; ++i) { for (int i = 0; i < QVK_FRAMES_IN_FLIGHT; ++i) {
stagingBuffers[i] = VK_NULL_HANDLE; 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 QSize size = m_pixelSize.isEmpty() ? QSize(1, 1) : m_pixelSize;
const bool isCube = m_flags.testFlag(CubeMap); const bool isCube = m_flags.testFlag(CubeMap);
const bool isArray = m_flags.testFlag(TextureArray);
const bool is3D = m_flags.testFlag(ThreeDimensional); const bool is3D = m_flags.testFlag(ThreeDimensional);
const bool hasMipMaps = m_flags.testFlag(MipMapped); const bool hasMipMaps = m_flags.testFlag(MipMapped);
@ -5862,11 +5869,24 @@ bool QVkTexture::prepareCreate(QSize *adjustedSize)
qWarning("Texture cannot be both cube and 3D"); qWarning("Texture cannot be both cube and 3D");
return false; return false;
} }
if (isArray && is3D) {
qWarning("Texture cannot be both array and 3D");
return false;
}
m_depth = qMax(1, m_depth); m_depth = qMax(1, m_depth);
if (m_depth > 1 && !is3D) { if (m_depth > 1 && !is3D) {
qWarning("Texture cannot have a depth of %d when it is not 3D", m_depth); qWarning("Texture cannot have a depth of %d when it is not 3D", m_depth);
return false; 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.layout = VK_IMAGE_LAYOUT_PREINITIALIZED;
usageState.access = 0; usageState.access = 0;
@ -5884,13 +5904,16 @@ bool QVkTexture::finishCreate()
const auto aspectMask = aspectMaskForTextureFormat(m_format); const auto aspectMask = aspectMaskForTextureFormat(m_format);
const bool isCube = m_flags.testFlag(CubeMap); const bool isCube = m_flags.testFlag(CubeMap);
const bool isArray = m_flags.testFlag(TextureArray);
const bool is3D = m_flags.testFlag(ThreeDimensional); const bool is3D = m_flags.testFlag(ThreeDimensional);
VkImageViewCreateInfo viewInfo; VkImageViewCreateInfo viewInfo;
memset(&viewInfo, 0, sizeof(viewInfo)); memset(&viewInfo, 0, sizeof(viewInfo));
viewInfo.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO; viewInfo.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO;
viewInfo.image = image; 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.format = vkformat;
viewInfo.components.r = VK_COMPONENT_SWIZZLE_R; viewInfo.components.r = VK_COMPONENT_SWIZZLE_R;
viewInfo.components.g = VK_COMPONENT_SWIZZLE_G; viewInfo.components.g = VK_COMPONENT_SWIZZLE_G;
@ -5898,7 +5921,7 @@ bool QVkTexture::finishCreate()
viewInfo.components.a = VK_COMPONENT_SWIZZLE_A; viewInfo.components.a = VK_COMPONENT_SWIZZLE_A;
viewInfo.subresourceRange.aspectMask = aspectMask; viewInfo.subresourceRange.aspectMask = aspectMask;
viewInfo.subresourceRange.levelCount = mipLevelCount; 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); VkResult err = rhiD->df->vkCreateImageView(rhiD->dev, &viewInfo, nullptr, &imageView);
if (err != VK_SUCCESS) { if (err != VK_SUCCESS) {
@ -5922,6 +5945,7 @@ bool QVkTexture::create()
const bool isRenderTarget = m_flags.testFlag(QRhiTexture::RenderTarget); const bool isRenderTarget = m_flags.testFlag(QRhiTexture::RenderTarget);
const bool isDepth = isDepthTextureFormat(m_format); const bool isDepth = isDepthTextureFormat(m_format);
const bool isCube = m_flags.testFlag(CubeMap); const bool isCube = m_flags.testFlag(CubeMap);
const bool isArray = m_flags.testFlag(TextureArray);
const bool is3D = m_flags.testFlag(ThreeDimensional); const bool is3D = m_flags.testFlag(ThreeDimensional);
VkImageCreateInfo imageInfo; VkImageCreateInfo imageInfo;
@ -5952,7 +5976,7 @@ bool QVkTexture::create()
imageInfo.extent.height = uint32_t(size.height()); imageInfo.extent.height = uint32_t(size.height());
imageInfo.extent.depth = is3D ? m_depth : 1; imageInfo.extent.depth = is3D ? m_depth : 1;
imageInfo.mipLevels = mipLevelCount; imageInfo.mipLevels = mipLevelCount;
imageInfo.arrayLayers = isCube ? 6 : 1; imageInfo.arrayLayers = isCube ? 6 : (isArray ? m_arraySize : 1);
imageInfo.samples = samples; imageInfo.samples = samples;
imageInfo.tiling = VK_IMAGE_TILING_OPTIMAL; imageInfo.tiling = VK_IMAGE_TILING_OPTIMAL;
imageInfo.initialLayout = VK_IMAGE_LAYOUT_PREINITIALIZED; 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); rhiD->setObjectName(uint64_t(image), VK_DEBUG_REPORT_OBJECT_TYPE_IMAGE_EXT, m_objectName);
QRHI_PROF; 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; owns = true;
rhiD->registerResource(this); rhiD->registerResource(this);
@ -6010,8 +6034,11 @@ bool QVkTexture::createFrom(QRhiTexture::NativeTexture src)
if (!finishCreate()) if (!finishCreate())
return false; return false;
const bool isCube = m_flags.testFlag(CubeMap);
const bool isArray = m_flags.testFlag(TextureArray);
QRHI_PROF; 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); usageState.layout = VkImageLayout(src.layout);
@ -6039,13 +6066,16 @@ VkImageView QVkTexture::imageViewForLevel(int level)
const VkImageAspectFlags aspectMask = aspectMaskForTextureFormat(m_format); const VkImageAspectFlags aspectMask = aspectMaskForTextureFormat(m_format);
const bool isCube = m_flags.testFlag(CubeMap); const bool isCube = m_flags.testFlag(CubeMap);
const bool isArray = m_flags.testFlag(TextureArray);
const bool is3D = m_flags.testFlag(ThreeDimensional); const bool is3D = m_flags.testFlag(ThreeDimensional);
VkImageViewCreateInfo viewInfo; VkImageViewCreateInfo viewInfo;
memset(&viewInfo, 0, sizeof(viewInfo)); memset(&viewInfo, 0, sizeof(viewInfo));
viewInfo.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO; viewInfo.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO;
viewInfo.image = image; 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.format = vkformat;
viewInfo.components.r = VK_COMPONENT_SWIZZLE_R; viewInfo.components.r = VK_COMPONENT_SWIZZLE_R;
viewInfo.components.g = VK_COMPONENT_SWIZZLE_G; viewInfo.components.g = VK_COMPONENT_SWIZZLE_G;
@ -6055,7 +6085,7 @@ VkImageView QVkTexture::imageViewForLevel(int level)
viewInfo.subresourceRange.baseMipLevel = uint32_t(level); viewInfo.subresourceRange.baseMipLevel = uint32_t(level);
viewInfo.subresourceRange.levelCount = 1; viewInfo.subresourceRange.levelCount = 1;
viewInfo.subresourceRange.baseArrayLayer = 0; viewInfo.subresourceRange.baseArrayLayer = 0;
viewInfo.subresourceRange.layerCount = isCube ? 6 : 1; viewInfo.subresourceRange.layerCount = isCube ? 6 : (isArray ? m_arraySize : 1);
VkImageView v = VK_NULL_HANDLE; VkImageView v = VK_NULL_HANDLE;
QRHI_RES_RHI(QRhiVulkan); QRHI_RES_RHI(QRhiVulkan);

View File

@ -129,7 +129,7 @@ struct QVkRenderBuffer : public QRhiRenderBuffer
struct QVkTexture : public QRhiTexture struct QVkTexture : public QRhiTexture
{ {
QVkTexture(QRhiImplementation *rhi, Format format, const QSize &pixelSize, int depth, QVkTexture(QRhiImplementation *rhi, Format format, const QSize &pixelSize, int depth,
int sampleCount, Flags flags); int arraySize, int sampleCount, Flags flags);
~QVkTexture(); ~QVkTexture();
void destroy() override; void destroy() override;
bool create() override; bool create() override;
@ -682,6 +682,7 @@ public:
QRhiTexture *createTexture(QRhiTexture::Format format, QRhiTexture *createTexture(QRhiTexture::Format format,
const QSize &pixelSize, const QSize &pixelSize,
int depth, int depth,
int arraySize,
int sampleCount, int sampleCount,
QRhiTexture::Flags flags) override; QRhiTexture::Flags flags) override;
QRhiSampler *createSampler(QRhiSampler::Filter magFilter, QRhiSampler *createSampler(QRhiSampler::Filter magFilter,

View File

@ -7,3 +7,6 @@ android
# Skip 3D textures with Android emulator, the sw-based GL there is no good # Skip 3D textures with Android emulator, the sw-based GL there is no good
[threeDimTexture] [threeDimTexture]
android android
# Same here, GLES 3.0 features seem hopeless
[renderToTextureTextureArray]
android

View File

@ -113,6 +113,8 @@ private slots:
void renderToTextureMip(); void renderToTextureMip();
void renderToTextureCubemapFace_data(); void renderToTextureCubemapFace_data();
void renderToTextureCubemapFace(); void renderToTextureCubemapFace();
void renderToTextureTextureArray_data();
void renderToTextureTextureArray();
void renderToTextureTexturedQuad_data(); void renderToTextureTexturedQuad_data();
void renderToTextureTexturedQuad(); void renderToTextureTexturedQuad();
void renderToTextureArrayOfTexturedQuad_data(); void renderToTextureArrayOfTexturedQuad_data();
@ -336,10 +338,13 @@ void tst_QRhi::create()
const int texMax = rhi->resourceLimit(QRhi::TextureSizeMax); const int texMax = rhi->resourceLimit(QRhi::TextureSizeMax);
const int maxAtt = rhi->resourceLimit(QRhi::MaxColorAttachments); const int maxAtt = rhi->resourceLimit(QRhi::MaxColorAttachments);
const int framesInFlight = rhi->resourceLimit(QRhi::FramesInFlight); const int framesInFlight = rhi->resourceLimit(QRhi::FramesInFlight);
const int texArrayMax = rhi->resourceLimit(QRhi::TextureArraySizeMax);
QVERIFY(texMin >= 1); QVERIFY(texMin >= 1);
QVERIFY(texMax >= texMin); QVERIFY(texMax >= texMin);
QVERIFY(maxAtt >= 1); QVERIFY(maxAtt >= 1);
QVERIFY(framesInFlight >= 1); QVERIFY(framesInFlight >= 1);
if (rhi->isFeatureSupported(QRhi::TextureArrays))
QVERIFY(texArrayMax > 1);
QVERIFY(rhi->nativeHandles()); QVERIFY(rhi->nativeHandles());
QVERIFY(rhi->profiler()); QVERIFY(rhi->profiler());
@ -1880,6 +1885,133 @@ void tst_QRhi::renderToTextureCubemapFace()
QFAIL("Encountered a pixel that is neither red or blue"); 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()); QCOMPARE(redCount + blueCount, outputSize.width());
if (rhi->isYUpInFramebuffer() == rhi->isYUpInNDC()) if (rhi->isYUpInFramebuffer() == rhi->isYUpInNDC())

View File

@ -22,6 +22,7 @@ add_subdirectory(computeimage)
add_subdirectory(instancing) add_subdirectory(instancing)
add_subdirectory(noninstanced) add_subdirectory(noninstanced)
add_subdirectory(tex3d) add_subdirectory(tex3d)
add_subdirectory(texturearray)
if(QT_FEATURE_widgets) if(QT_FEATURE_widgets)
add_subdirectory(qrhiprof) add_subdirectory(qrhiprof)
endif() endif()

View File

@ -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.vert -o texture.vert.qsb
qsb --glsl "100 es,120" --hlsl 50 --msl 12 -c texture.frag -o texture.frag.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 "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

View File

@ -109,7 +109,7 @@ QString graphicsApiName()
case Null: case Null:
return QLatin1String("Null (no output)"); return QLatin1String("Null (no output)");
case OpenGL: case OpenGL:
return QLatin1String("OpenGL 2.x"); return QLatin1String("OpenGL");
case Vulkan: case Vulkan:
return QLatin1String("Vulkan"); return QLatin1String("Vulkan");
case D3D11: case D3D11:
@ -462,7 +462,7 @@ int main(int argc, char **argv)
cmdLineParser.addHelpOption(); cmdLineParser.addHelpOption();
QCommandLineOption nullOption({ "n", "null" }, QLatin1String("Null")); QCommandLineOption nullOption({ "n", "null" }, QLatin1String("Null"));
cmdLineParser.addOption(nullOption); cmdLineParser.addOption(nullOption);
QCommandLineOption glOption({ "g", "opengl" }, QLatin1String("OpenGL (2.x)")); QCommandLineOption glOption({ "g", "opengl" }, QLatin1String("OpenGL"));
cmdLineParser.addOption(glOption); cmdLineParser.addOption(glOption);
QCommandLineOption vkOption({ "v", "vulkan" }, QLatin1String("Vulkan")); QCommandLineOption vkOption({ "v", "vulkan" }, QLatin1String("Vulkan"));
cmdLineParser.addOption(vkOption); cmdLineParser.addOption(vkOption);
@ -475,6 +475,8 @@ int main(int argc, char **argv)
// Use this parameter for the latter. // Use this parameter for the latter.
QCommandLineOption sdOption({ "s", "self-destruct" }, QLatin1String("Self-destruct after 5 seconds.")); QCommandLineOption sdOption({ "s", "self-destruct" }, QLatin1String("Self-destruct after 5 seconds."));
cmdLineParser.addOption(sdOption); 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. // Attempt testing device lost situations on D3D at least.
QCommandLineOption tdrOption(QLatin1String("curse"), QLatin1String("Curse the graphics device. " QCommandLineOption tdrOption(QLatin1String("curse"), QLatin1String("Curse the graphics device. "
"(generate a device reset every <count> frames when on D3D11)"), "(generate a device reset every <count> frames when on D3D11)"),
@ -517,6 +519,14 @@ int main(int argc, char **argv)
QSurfaceFormat fmt; QSurfaceFormat fmt;
fmt.setDepthBufferSize(24); fmt.setDepthBufferSize(24);
fmt.setStencilBufferSize(8); 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) if (sampleCount > 1)
fmt.setSamples(sampleCount); fmt.setSamples(sampleCount);
if (scFlags.testFlag(QRhiSwapChain::NoVSync)) if (scFlags.testFlag(QRhiSwapChain::NoVSync))

View File

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

Binary file not shown.

View File

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

Binary file not shown.

View File

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

View File

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