diff options
Diffstat (limited to 'src/gui')
-rw-r--r-- | src/gui/rhi/qrhi.cpp | 104 | ||||
-rw-r--r-- | src/gui/rhi/qrhi_p.h | 21 | ||||
-rw-r--r-- | src/gui/rhi/qrhi_p_p.h | 1 | ||||
-rw-r--r-- | src/gui/rhi/qrhid3d11.cpp | 81 | ||||
-rw-r--r-- | src/gui/rhi/qrhid3d11_p_p.h | 3 | ||||
-rw-r--r-- | src/gui/rhi/qrhigles2.cpp | 132 | ||||
-rw-r--r-- | src/gui/rhi/qrhigles2_p_p.h | 6 | ||||
-rw-r--r-- | src/gui/rhi/qrhimetal.mm | 51 | ||||
-rw-r--r-- | src/gui/rhi/qrhimetal_p_p.h | 3 | ||||
-rw-r--r-- | src/gui/rhi/qrhinull.cpp | 17 | ||||
-rw-r--r-- | src/gui/rhi/qrhinull_p_p.h | 3 | ||||
-rw-r--r-- | src/gui/rhi/qrhivulkan.cpp | 54 | ||||
-rw-r--r-- | src/gui/rhi/qrhivulkan_p_p.h | 3 |
13 files changed, 367 insertions, 112 deletions
diff --git a/src/gui/rhi/qrhi.cpp b/src/gui/rhi/qrhi.cpp index 82a304865e..e6bc19acde 100644 --- a/src/gui/rhi/qrhi.cpp +++ b/src/gui/rhi/qrhi.cpp @@ -680,6 +680,11 @@ Q_LOGGING_CATEGORY(QRHI_LOG_INFO, "qt.rhi.general") texture is supported. This can be unsupported with Vulkan 1.0 due to relying on VK_IMAGE_CREATE_2D_ARRAY_COMPATIBLE_BIT which is a Vulkan 1.1 feature. + + \value TextureArrays Indicates that texture arrays are supported and + QRhi::newTextureArray() is functional. Note that even when texture arrays + are not supported, arrays of textures are still available as those are two + independent features. */ /*! @@ -767,6 +772,10 @@ Q_LOGGING_CATEGORY(QRHI_LOG_INFO, "qt.rhi.general") \value MaxThreadGroupZ The maximum size of a work/thread group in the Z dimension. Effectively the maximum value of \c local_size_z in the compute shader. Typically 64 or 256. + + \value TextureArraySizeMax Maximum texture array size. Typically in range + 256 - 2048. Attempting to \l{QRhi::newTextureArray()}{create a texture + array} with more elements will likely fail. */ /*! @@ -1503,7 +1512,7 @@ QDebug operator<<(QDebug dbg, const QRhiShaderStage &s) When targeting a non-multisample texture, the layer() and level() indicate the targeted layer (face index \c{0-5} for cubemaps) and mip level. For 3D textures layer() specifies the slice (one 2D image within the 3D texture) - to render to. + to render to. For texture arrays layer() is the array index. When texture() or renderBuffer() is multisample, resolveTexture() can be set optionally. When set, samples are resolved automatically into that @@ -1700,7 +1709,9 @@ QRhiTextureSubresourceUploadDescription::QRhiTextureSubresourceUploadDescription \class QRhiTextureUploadEntry \internal \inmodule QtGui - \brief Describes one layer (face for cubemaps) in a texture upload operation. + + \brief Describes one layer (face for cubemaps, slice for 3D textures, + element for texture arrays) in a texture upload operation. */ /*! @@ -1840,12 +1851,12 @@ QRhiTextureUploadDescription::QRhiTextureUploadDescription(std::initializer_list sourceTopLeft(), and destinationTopLeft() must fit the source and destination textures, respectively. The behavior is undefined otherwise. - With cubemap and 3D textures one face or slice can be copied at a time. The - face or slice is specified by the source and destination layer indices. - With mipmapped textures one mip level can be copied at a time. The source - and destination layer and mip level indices can differ, but the size and - position must be carefully controlled to avoid out of bounds copies, in - which case the behavior is undefined. + With cubemaps, 3D textures, and texture arrays one face or slice can be + copied at a time. The face or slice is specified by the source and + destination layer indices. With mipmapped textures one mip level can be + copied at a time. The source and destination layer and mip level indices can + differ, but the size and position must be carefully controlled to avoid out + of bounds copies, in which case the behavior is undefined. */ /*! @@ -2440,10 +2451,11 @@ bool QRhiRenderBuffer::createFrom(NativeRenderBuffer src) \value ThreeDimensional The texture is a 3D texture. Such textures should be created with the QRhi::newTexture() overload taking a depth in addition to width and height. A 3D texture can have mipmaps but cannot be - multisample. When rendering into a 3D texture, the layer specified in the - render target's color attachment refers to a slice in range [0..depth-1]. - The underlying graphics API may not support 3D textures at run time. - Support is indicated by the QRhi::ThreeDimensionalTextures feature. + multisample. When rendering into, or uploading data to a 3D texture, the \c + layer specified in the render target's color attachment or the upload + description refers to a single slice in range [0..depth-1]. The underlying + graphics API may not support 3D textures at run time. Support is indicated + by the QRhi::ThreeDimensionalTextures feature. \value TextureRectangleGL The texture should use the GL_TEXTURE_RECTANGLE target with OpenGL. This flag is ignored with other graphics APIs. Just @@ -2451,6 +2463,14 @@ bool QRhiRenderBuffer::createFrom(NativeRenderBuffer src) native OpenGL texture objects received from the platform are wrapped in a QRhiTexture, and the platform can only provide textures for a non-2D texture target. + + \value TextureArray The texture is a texture array, i.e. a single texture + object that is a homogeneous array of 2D textures. Texture arrays are + created with QRhi::newTextureArray(). The underlying graphics API may not + support texture array objects at run time. Support is indicated by the + QRhi::TextureArrays feature. When rendering into, or uploading data to a + texture array, the \c layer specified in the render target's color + attachment or the upload description selects a single element in the array. */ /*! @@ -2542,9 +2562,10 @@ bool QRhiRenderBuffer::createFrom(NativeRenderBuffer src) \internal */ QRhiTexture::QRhiTexture(QRhiImplementation *rhi, Format format_, const QSize &pixelSize_, int depth_, - int sampleCount_, Flags flags_) + int arraySize_, int sampleCount_, Flags flags_) : QRhiResource(rhi), - m_format(format_), m_pixelSize(pixelSize_), m_depth(depth_), m_sampleCount(sampleCount_), m_flags(flags_) + m_format(format_), m_pixelSize(pixelSize_), m_depth(depth_), + m_arraySize(arraySize_), m_sampleCount(sampleCount_), m_flags(flags_) { } @@ -2599,6 +2620,10 @@ QRhiTexture::NativeTexture QRhiTexture::nativeTexture() The opposite of this operation, exposing a QRhiTexture-created native texture object to a foreign engine, is possible via nativeTexture(). + \note When importing a 3D texture, or a texture array object, or, with + OpenGL ES, an external texture, it is then especially important to set the + corresponding flags (ThreeDimensional, TextureArray, ExternalOES) via + setFlags() before calling this function. */ bool QRhiTexture::createFrom(QRhiTexture::NativeTexture src) { @@ -3118,8 +3143,9 @@ void QRhiImplementation::updateLayoutDesc(QRhiShaderResourceBindings *srb) \inmodule QtGui \brief Describes the shader resource for a single binding point. - A QRhiShaderResourceBinding cannot be constructed directly. Instead, use - the static functions uniformBuffer(), sampledTexture() to get an instance. + A QRhiShaderResourceBinding cannot be constructed directly. Instead, use the + static functions such as uniformBuffer() or sampledTexture() to get an + instance. */ /*! @@ -5432,8 +5458,8 @@ void QRhiResourceUpdateBatch::copyTexture(QRhiTexture *dst, QRhiTexture *src, co QRhi::MaxAsyncReadbackFrames. A single readback operation copies one mip level of one layer (cubemap face - or 3D slice) at a time. The level and layer are specified by the respective - fields in \a rb. + or 3D slice or texture array element) at a time. The level and layer are + specified by the respective fields in \a rb. \sa readBackBuffer(), QRhi::resourceLimit() */ @@ -6496,7 +6522,7 @@ QRhiRenderBuffer *QRhi::newRenderBuffer(QRhiRenderBuffer::Type type, } /*! - \return a new texture with the specified \a format, \a pixelSize, \a + \return a new 2D texture with the specified \a format, \a pixelSize, \a sampleCount, and \a flags. \note \a format specifies the requested internal and external format, @@ -6511,12 +6537,12 @@ QRhiTexture *QRhi::newTexture(QRhiTexture::Format format, int sampleCount, QRhiTexture::Flags flags) { - return d->createTexture(format, pixelSize, 1, sampleCount, flags); + return d->createTexture(format, pixelSize, 1, 0, sampleCount, flags); } /*! - \return a new texture with the specified \a format, \a width, \a height, \a - depth, \a sampleCount, and \a flags. + \return a new 2D or 3D texture with the specified \a format, \a width, \a + height, \a depth, \a sampleCount, and \a flags. This overload is suitable for 3D textures because it allows specifying \a depth. A 3D texture must have QRhiTexture::ThreeDimensional set in \a @@ -6524,6 +6550,9 @@ QRhiTexture *QRhi::newTexture(QRhiTexture::Format format, implicitly whenever \a depth is greater than 0. For 2D and cube textures \a depth should be set to 0. + \note 3D textures are only functional when the ThreeDimensionalTextures + feature is reported as supported at run time. + \overload */ QRhiTexture *QRhi::newTexture(QRhiTexture::Format format, @@ -6534,7 +6563,36 @@ QRhiTexture *QRhi::newTexture(QRhiTexture::Format format, if (depth > 0) flags |= QRhiTexture::ThreeDimensional; - return d->createTexture(format, QSize(width, height), depth, sampleCount, flags); + return d->createTexture(format, QSize(width, height), depth, 0, sampleCount, flags); +} + +/*! + \return a new 2D texture array with the specified \a format, \a arraySize, + \a pixelSize, \a sampleCount, and \a flags. + + This function implicitly sets QRhiTexture::TextureArray in \a flags. + + \note Do not confuse texture arrays with arrays of textures. A QRhiTexture + created by this function is usable with 2D array samplers in the shader, for + example: \c{layout(binding = 1) uniform sampler2DArray texArr;}. Arrays of + textures refers to a list of textures that are exposed to the shader via + QRhiShaderResourceBinding::sampledTextures() and a count > 1, and declared + in the shader for example like this: \c{layout(binding = 1) uniform + sampler2D textures[4];} + + \note This is only functional when the TextureArrays feature is reported as + supported at run time. + + \sa newTexture() + */ +QRhiTexture *QRhi::newTextureArray(QRhiTexture::Format format, + int arraySize, + const QSize &pixelSize, + int sampleCount, + QRhiTexture::Flags flags) +{ + flags |= QRhiTexture::TextureArray; + return d->createTexture(format, pixelSize, 1, arraySize, sampleCount, flags); } /*! diff --git a/src/gui/rhi/qrhi_p.h b/src/gui/rhi/qrhi_p.h index 17b170203b..4decdd0c91 100644 --- a/src/gui/rhi/qrhi_p.h +++ b/src/gui/rhi/qrhi_p.h @@ -773,7 +773,8 @@ public: UsedAsCompressedAtlas = 1 << 8, ExternalOES = 1 << 9, ThreeDimensional = 1 << 10, - TextureRectangleGL = 1 << 11 + TextureRectangleGL = 1 << 11, + TextureArray = 1 << 12 }; Q_DECLARE_FLAGS(Flags, Flag) @@ -842,6 +843,9 @@ public: int depth() const { return m_depth; } void setDepth(int depth) { m_depth = depth; } + int arraySize() const { return m_arraySize; } + void setArraySize(int arraySize) { m_arraySize = arraySize; } + Flags flags() const { return m_flags; } void setFlags(Flags f) { m_flags = f; } @@ -855,10 +859,11 @@ public: protected: QRhiTexture(QRhiImplementation *rhi, Format format_, const QSize &pixelSize_, int depth_, - int sampleCount_, Flags flags_); + int arraySize_, int sampleCount_, Flags flags_); Format m_format; QSize m_pixelSize; int m_depth; + int m_arraySize; int m_sampleCount; Flags m_flags; }; @@ -1583,7 +1588,8 @@ public: ImageDataStride, RenderBufferImport, ThreeDimensionalTextures, - RenderTo3DTextureSlice + RenderTo3DTextureSlice, + TextureArrays }; enum BeginFrameFlag { @@ -1605,7 +1611,8 @@ public: MaxThreadsPerThreadGroup, MaxThreadGroupX, MaxThreadGroupY, - MaxThreadGroupZ + MaxThreadGroupZ, + TextureArraySizeMax }; ~QRhi(); @@ -1648,6 +1655,12 @@ public: int sampleCount = 1, QRhiTexture::Flags flags = {}); + QRhiTexture *newTextureArray(QRhiTexture::Format format, + int arraySize, + const QSize &pixelSize, + int sampleCount = 1, + QRhiTexture::Flags flags = {}); + QRhiSampler *newSampler(QRhiSampler::Filter magFilter, QRhiSampler::Filter minFilter, QRhiSampler::Filter mipmapMode, diff --git a/src/gui/rhi/qrhi_p_p.h b/src/gui/rhi/qrhi_p_p.h index 0dcb8c1f6e..7a3f94846d 100644 --- a/src/gui/rhi/qrhi_p_p.h +++ b/src/gui/rhi/qrhi_p_p.h @@ -88,6 +88,7 @@ public: virtual QRhiTexture *createTexture(QRhiTexture::Format format, const QSize &pixelSize, int depth, + int arraySize, int sampleCount, QRhiTexture::Flags flags) = 0; virtual QRhiSampler *createSampler(QRhiSampler::Filter magFilter, diff --git a/src/gui/rhi/qrhid3d11.cpp b/src/gui/rhi/qrhid3d11.cpp index 8e94d70502..631e7f7adf 100644 --- a/src/gui/rhi/qrhid3d11.cpp +++ b/src/gui/rhi/qrhid3d11.cpp @@ -543,6 +543,8 @@ bool QRhiD3D11::isFeatureSupported(QRhi::Feature feature) const return true; case QRhi::RenderTo3DTextureSlice: return true; + case QRhi::TextureArrays: + return true; default: Q_UNREACHABLE(); return false; @@ -576,6 +578,8 @@ int QRhiD3D11::resourceLimit(QRhi::ResourceLimit limit) const return D3D11_CS_THREAD_GROUP_MAX_Y; case QRhi::MaxThreadGroupZ: return D3D11_CS_THREAD_GROUP_MAX_Z; + case QRhi::TextureArraySizeMax: + return D3D11_REQ_TEXTURE2D_ARRAY_AXIS_DIMENSION; default: Q_UNREACHABLE(); return 0; @@ -631,10 +635,10 @@ QRhiRenderBuffer *QRhiD3D11::createRenderBuffer(QRhiRenderBuffer::Type type, con } QRhiTexture *QRhiD3D11::createTexture(QRhiTexture::Format format, - const QSize &pixelSize, int depth, + const QSize &pixelSize, int depth, int arraySize, int sampleCount, QRhiTexture::Flags flags) { - return new QD3D11Texture(this, format, pixelSize, depth, sampleCount, flags); + return new QD3D11Texture(this, format, pixelSize, depth, arraySize, sampleCount, flags); } QRhiSampler *QRhiD3D11::createSampler(QRhiSampler::Filter magFilter, QRhiSampler::Filter minFilter, @@ -2907,8 +2911,8 @@ QRhiTexture::Format QD3D11RenderBuffer::backingFormat() const } QD3D11Texture::QD3D11Texture(QRhiImplementation *rhi, Format format, const QSize &pixelSize, int depth, - int sampleCount, Flags flags) - : QRhiTexture(rhi, format, pixelSize, depth, sampleCount, flags) + int arraySize, int sampleCount, Flags flags) + : QRhiTexture(rhi, format, pixelSize, depth, arraySize, sampleCount, flags) { for (int i = 0; i < QRhi::MAX_MIP_LEVELS; ++i) perLevelViews[i] = nullptr; @@ -2997,6 +3001,7 @@ bool QD3D11Texture::prepareCreate(QSize *adjustedSize) const bool isDepth = isDepthTextureFormat(m_format); const bool isCube = m_flags.testFlag(CubeMap); const bool is3D = m_flags.testFlag(ThreeDimensional); + const bool isArray = m_flags.testFlag(TextureArray); const bool hasMipMaps = m_flags.testFlag(MipMapped); QRHI_RES_RHI(QRhiD3D11); @@ -3025,11 +3030,24 @@ bool QD3D11Texture::prepareCreate(QSize *adjustedSize) qWarning("Texture cannot be both cube and 3D"); return false; } + if (isArray && is3D) { + qWarning("Texture cannot be both array and 3D"); + return false; + } m_depth = qMax(1, m_depth); if (m_depth > 1 && !is3D) { qWarning("Texture cannot have a depth of %d when it is not 3D", m_depth); return false; } + m_arraySize = qMax(0, m_arraySize); + if (m_arraySize > 0 && !isArray) { + qWarning("Texture cannot have an array size of %d when it is not an array", m_arraySize); + return false; + } + if (m_arraySize < 1 && isArray) { + qWarning("Texture is an array but array size is %d", m_arraySize); + return false; + } if (adjustedSize) *adjustedSize = size; @@ -3043,6 +3061,7 @@ bool QD3D11Texture::finishCreate() const bool isDepth = isDepthTextureFormat(m_format); const bool isCube = m_flags.testFlag(CubeMap); const bool is3D = m_flags.testFlag(ThreeDimensional); + const bool isArray = m_flags.testFlag(TextureArray); D3D11_SHADER_RESOURCE_VIEW_DESC srvDesc; memset(&srvDesc, 0, sizeof(srvDesc)); @@ -3051,14 +3070,27 @@ bool QD3D11Texture::finishCreate() srvDesc.ViewDimension = D3D11_SRV_DIMENSION_TEXTURECUBE; srvDesc.TextureCube.MipLevels = mipLevelCount; } else { - if (sampleDesc.Count > 1) { - srvDesc.ViewDimension = D3D11_SRV_DIMENSION_TEXTURE2DMS; - } else if (is3D) { - srvDesc.ViewDimension = D3D11_SRV_DIMENSION_TEXTURE3D; - srvDesc.Texture3D.MipLevels = mipLevelCount; + if (isArray) { + if (sampleDesc.Count > 1) { + srvDesc.ViewDimension = D3D11_SRV_DIMENSION_TEXTURE2DMSARRAY; + srvDesc.Texture2DMSArray.FirstArraySlice = 0; + srvDesc.Texture2DMSArray.ArraySize = UINT(m_arraySize); + } else { + srvDesc.ViewDimension = D3D11_SRV_DIMENSION_TEXTURE2DARRAY; + srvDesc.Texture2DArray.MipLevels = mipLevelCount; + srvDesc.Texture2DArray.FirstArraySlice = 0; + srvDesc.Texture2DArray.ArraySize = UINT(m_arraySize); + } } else { - srvDesc.ViewDimension = D3D11_SRV_DIMENSION_TEXTURE2D; - srvDesc.Texture2D.MipLevels = mipLevelCount; + if (sampleDesc.Count > 1) { + srvDesc.ViewDimension = D3D11_SRV_DIMENSION_TEXTURE2DMS; + } else if (is3D) { + srvDesc.ViewDimension = D3D11_SRV_DIMENSION_TEXTURE3D; + srvDesc.Texture3D.MipLevels = mipLevelCount; + } else { + srvDesc.ViewDimension = D3D11_SRV_DIMENSION_TEXTURE2D; + srvDesc.Texture2D.MipLevels = mipLevelCount; + } } } @@ -3081,6 +3113,7 @@ bool QD3D11Texture::create() const bool isDepth = isDepthTextureFormat(m_format); const bool isCube = m_flags.testFlag(CubeMap); const bool is3D = m_flags.testFlag(ThreeDimensional); + const bool isArray = m_flags.testFlag(TextureArray); uint bindFlags = D3D11_BIND_SHADER_RESOURCE; uint miscFlags = isCube ? D3D11_RESOURCE_MISC_TEXTURECUBE : 0; @@ -3108,7 +3141,7 @@ bool QD3D11Texture::create() desc.Width = UINT(size.width()); desc.Height = UINT(size.height()); desc.MipLevels = mipLevelCount; - desc.ArraySize = isCube ? 6 : 1; + desc.ArraySize = isCube ? 6 : (isArray ? UINT(m_arraySize) : 1); desc.Format = dxgiFormat; desc.SampleDesc = sampleDesc; desc.Usage = D3D11_USAGE_DEFAULT; @@ -3147,7 +3180,7 @@ bool QD3D11Texture::create() return false; QRHI_PROF; - QRHI_PROF_F(newTexture(this, true, int(mipLevelCount), isCube ? 6 : 1, int(sampleDesc.Count))); + QRHI_PROF_F(newTexture(this, true, int(mipLevelCount), isCube ? 6 : (isArray ? m_arraySize : 1), int(sampleDesc.Count))); owns = true; rhiD->registerResource(this); @@ -3170,8 +3203,11 @@ bool QD3D11Texture::createFrom(QRhiTexture::NativeTexture src) if (!finishCreate()) return false; + const bool isCube = m_flags.testFlag(CubeMap); + const bool isArray = m_flags.testFlag(TextureArray); + QRHI_PROF; - QRHI_PROF_F(newTexture(this, false, int(mipLevelCount), m_flags.testFlag(CubeMap) ? 6 : 1, int(sampleDesc.Count))); + QRHI_PROF_F(newTexture(this, false, int(mipLevelCount), isCube ? 6 : (isArray ? m_arraySize : 1), int(sampleDesc.Count))); owns = false; QRHI_RES_RHI(QRhiD3D11); @@ -3190,6 +3226,7 @@ ID3D11UnorderedAccessView *QD3D11Texture::unorderedAccessViewForLevel(int level) return perLevelViews[level]; const bool isCube = m_flags.testFlag(CubeMap); + const bool isArray = m_flags.testFlag(TextureArray); const bool is3D = m_flags.testFlag(ThreeDimensional); D3D11_UNORDERED_ACCESS_VIEW_DESC desc; memset(&desc, 0, sizeof(desc)); @@ -3199,6 +3236,11 @@ ID3D11UnorderedAccessView *QD3D11Texture::unorderedAccessViewForLevel(int level) desc.Texture2DArray.MipSlice = UINT(level); desc.Texture2DArray.FirstArraySlice = 0; desc.Texture2DArray.ArraySize = 6; + } else if (isArray) { + desc.ViewDimension = D3D11_UAV_DIMENSION_TEXTURE2DARRAY; + desc.Texture2DArray.MipSlice = UINT(level); + desc.Texture2DArray.FirstArraySlice = 0; + desc.Texture2DArray.ArraySize = UINT(m_arraySize); } else if (is3D) { desc.ViewDimension = D3D11_UAV_DIMENSION_TEXTURE3D; desc.Texture3D.MipSlice = UINT(level); @@ -3483,6 +3525,17 @@ bool QD3D11TextureRenderTarget::create() rtvDesc.Texture2DArray.MipSlice = UINT(colorAtt.level()); rtvDesc.Texture2DArray.FirstArraySlice = UINT(colorAtt.layer()); rtvDesc.Texture2DArray.ArraySize = 1; + } else if (texD->flags().testFlag(QRhiTexture::TextureArray)) { + if (texD->sampleDesc.Count > 1) { + rtvDesc.ViewDimension = D3D11_RTV_DIMENSION_TEXTURE2DMSARRAY; + rtvDesc.Texture2DMSArray.FirstArraySlice = UINT(colorAtt.layer()); + rtvDesc.Texture2DMSArray.ArraySize = 1; + } else { + rtvDesc.ViewDimension = D3D11_RTV_DIMENSION_TEXTURE2DARRAY; + rtvDesc.Texture2DArray.MipSlice = UINT(colorAtt.level()); + rtvDesc.Texture2DArray.FirstArraySlice = UINT(colorAtt.layer()); + rtvDesc.Texture2DArray.ArraySize = 1; + } } else if (texD->flags().testFlag(QRhiTexture::ThreeDimensional)) { rtvDesc.ViewDimension = D3D11_RTV_DIMENSION_TEXTURE3D; rtvDesc.Texture3D.MipSlice = UINT(colorAtt.level()); diff --git a/src/gui/rhi/qrhid3d11_p_p.h b/src/gui/rhi/qrhid3d11_p_p.h index ddeb68a193..3a333fe8a7 100644 --- a/src/gui/rhi/qrhid3d11_p_p.h +++ b/src/gui/rhi/qrhid3d11_p_p.h @@ -102,7 +102,7 @@ struct QD3D11RenderBuffer : public QRhiRenderBuffer struct QD3D11Texture : public QRhiTexture { QD3D11Texture(QRhiImplementation *rhi, Format format, const QSize &pixelSize, int depth, - int sampleCount, Flags flags); + int arraySize, int sampleCount, Flags flags); ~QD3D11Texture(); void destroy() override; bool create() override; @@ -607,6 +607,7 @@ public: QRhiTexture *createTexture(QRhiTexture::Format format, const QSize &pixelSize, int depth, + int arraySize, int sampleCount, QRhiTexture::Flags flags) override; QRhiSampler *createSampler(QRhiSampler::Filter magFilter, diff --git a/src/gui/rhi/qrhigles2.cpp b/src/gui/rhi/qrhigles2.cpp index 1c324cfd3b..5259be270c 100644 --- a/src/gui/rhi/qrhigles2.cpp +++ b/src/gui/rhi/qrhigles2.cpp @@ -397,6 +397,14 @@ QT_BEGIN_NAMESPACE #define GL_TEXTURE_RECTANGLE 0x84F5 #endif +#ifndef GL_TEXTURE_2D_ARRAY +#define GL_TEXTURE_2D_ARRAY 0x8C1A +#endif + +#ifndef GL_MAX_ARRAY_TEXTURE_LAYERS +#define GL_MAX_ARRAY_TEXTURE_LAYERS 0x88FF +#endif + /*! Constructs a new QRhiGles2InitParams. @@ -678,6 +686,14 @@ bool QRhiGles2::create(QRhi::Flags flags) caps.texture3D = caps.ctxMajor >= 3; // 3.0 + if (caps.ctxMajor >= 3) { // 3.0 or ES 3.0 + GLint maxArraySize = 0; + f->glGetIntegerv(GL_MAX_ARRAY_TEXTURE_LAYERS, &maxArraySize); + caps.maxTextureArraySize = maxArraySize; + } else { + caps.maxTextureArraySize = 0; + } + if (!caps.gles) { f->glEnable(GL_VERTEX_PROGRAM_POINT_SIZE); f->glEnable(GL_POINT_SPRITE); @@ -1074,6 +1090,8 @@ bool QRhiGles2::isFeatureSupported(QRhi::Feature feature) const return caps.texture3D; case QRhi::RenderTo3DTextureSlice: return caps.texture3D; + case QRhi::TextureArrays: + return caps.maxTextureArraySize > 0; default: Q_UNREACHABLE(); return false; @@ -1105,6 +1123,8 @@ int QRhiGles2::resourceLimit(QRhi::ResourceLimit limit) const return caps.maxThreadGroupsY; case QRhi::MaxThreadGroupZ: return caps.maxThreadGroupsZ; + case QRhi::TextureArraySizeMax: + return 2048; default: Q_UNREACHABLE(); return 0; @@ -1291,10 +1311,10 @@ QRhiRenderBuffer *QRhiGles2::createRenderBuffer(QRhiRenderBuffer::Type type, con } QRhiTexture *QRhiGles2::createTexture(QRhiTexture::Format format, - const QSize &pixelSize, int depth, + const QSize &pixelSize, int depth, int arraySize, int sampleCount, QRhiTexture::Flags flags) { - return new QGles2Texture(this, format, pixelSize, depth, sampleCount, flags); + return new QGles2Texture(this, format, pixelSize, depth, arraySize, sampleCount, flags); } QRhiSampler *QRhiGles2::createSampler(QRhiSampler::Filter magFilter, QRhiSampler::Filter minFilter, @@ -1878,7 +1898,10 @@ void QRhiGles2::enqueueSubresUpload(QGles2Texture *texD, QGles2CommandBuffer *cb trackedImageBarrier(cbD, texD, QGles2Texture::AccessUpdate); const bool isCompressed = isCompressedFormat(texD->m_format); const bool isCubeMap = texD->m_flags.testFlag(QRhiTexture::CubeMap); + const bool is3D = texD->m_flags.testFlag(QRhiTexture::ThreeDimensional); + const bool isArray = texD->m_flags.testFlag(QRhiTexture::TextureArray); const GLenum faceTargetBase = isCubeMap ? GL_TEXTURE_CUBE_MAP_POSITIVE_X : texD->target; + const GLenum effectiveTarget = faceTargetBase + (isCubeMap ? uint(layer) : 0u); const QPoint dp = subresDesc.destinationTopLeft(); const QByteArray rawData = subresDesc.data(); if (!subresDesc.image().isNull()) { @@ -1894,11 +1917,11 @@ void QRhiGles2::enqueueSubresUpload(QGles2Texture *texD, QGles2CommandBuffer *cb } cmd.args.subImage.target = texD->target; cmd.args.subImage.texture = texD->texture; - cmd.args.subImage.faceTarget = faceTargetBase + uint(layer); + cmd.args.subImage.faceTarget = effectiveTarget; cmd.args.subImage.level = level; cmd.args.subImage.dx = dp.x(); cmd.args.subImage.dy = dp.y(); - cmd.args.subImage.dz = texD->m_flags.testFlag(QRhiTexture::ThreeDimensional) ? layer : 0; + cmd.args.subImage.dz = is3D || isArray ? layer : 0; cmd.args.subImage.w = size.width(); cmd.args.subImage.h = size.height(); cmd.args.subImage.glformat = texD->glformat; @@ -1907,8 +1930,7 @@ void QRhiGles2::enqueueSubresUpload(QGles2Texture *texD, QGles2CommandBuffer *cb cmd.args.subImage.rowLength = 0; cmd.args.subImage.data = cbD->retainImage(img); } else if (!rawData.isEmpty() && isCompressed) { - const bool is3D = texD->flags().testFlag(QRhiTexture::ThreeDimensional); - if ((texD->flags().testFlag(QRhiTexture::UsedAsCompressedAtlas) || is3D) + if ((texD->flags().testFlag(QRhiTexture::UsedAsCompressedAtlas) || is3D || isArray) && !texD->zeroInitialized) { // Create on first upload since glCompressedTexImage2D cannot take @@ -1920,17 +1942,19 @@ void QRhiGles2::enqueueSubresUpload(QGles2Texture *texD, QGles2CommandBuffer *cb compressedFormatInfo(texD->m_format, texD->m_pixelSize, nullptr, &byteSize, nullptr); if (is3D) byteSize *= texD->m_depth; + if (isArray) + byteSize *= texD->m_arraySize; QByteArray zeroBuf(byteSize, 0); QGles2CommandBuffer::Command &cmd(cbD->commands.get()); cmd.cmd = QGles2CommandBuffer::Command::CompressedImage; cmd.args.compressedImage.target = texD->target; cmd.args.compressedImage.texture = texD->texture; - cmd.args.compressedImage.faceTarget = faceTargetBase + uint(layer); + cmd.args.compressedImage.faceTarget = effectiveTarget; cmd.args.compressedImage.level = level; cmd.args.compressedImage.glintformat = texD->glintformat; cmd.args.compressedImage.w = texD->m_pixelSize.width(); cmd.args.compressedImage.h = texD->m_pixelSize.height(); - cmd.args.compressedImage.depth = is3D ? texD->m_depth : 0; + cmd.args.compressedImage.depth = is3D ? texD->m_depth : (isArray ? texD->m_arraySize : 0); cmd.args.compressedImage.size = byteSize; cmd.args.compressedImage.data = cbD->retainData(zeroBuf); texD->zeroInitialized = true; @@ -1943,11 +1967,11 @@ void QRhiGles2::enqueueSubresUpload(QGles2Texture *texD, QGles2CommandBuffer *cb cmd.cmd = QGles2CommandBuffer::Command::CompressedSubImage; cmd.args.compressedSubImage.target = texD->target; cmd.args.compressedSubImage.texture = texD->texture; - cmd.args.compressedSubImage.faceTarget = faceTargetBase + uint(layer); + cmd.args.compressedSubImage.faceTarget = effectiveTarget; cmd.args.compressedSubImage.level = level; cmd.args.compressedSubImage.dx = dp.x(); cmd.args.compressedSubImage.dy = dp.y(); - cmd.args.compressedSubImage.dz = texD->m_flags.testFlag(QRhiTexture::ThreeDimensional) ? layer : 0; + cmd.args.compressedSubImage.dz = is3D || isArray ? layer : 0; cmd.args.compressedSubImage.w = size.width(); cmd.args.compressedSubImage.h = size.height(); cmd.args.compressedSubImage.glintformat = texD->glintformat; @@ -1958,12 +1982,12 @@ void QRhiGles2::enqueueSubresUpload(QGles2Texture *texD, QGles2CommandBuffer *cb cmd.cmd = QGles2CommandBuffer::Command::CompressedImage; cmd.args.compressedImage.target = texD->target; cmd.args.compressedImage.texture = texD->texture; - cmd.args.compressedImage.faceTarget = faceTargetBase + uint(layer); + cmd.args.compressedImage.faceTarget = effectiveTarget; cmd.args.compressedImage.level = level; cmd.args.compressedImage.glintformat = texD->glintformat; cmd.args.compressedImage.w = size.width(); cmd.args.compressedImage.h = size.height(); - cmd.args.compressedImage.depth = is3D ? texD->m_depth : 0; + cmd.args.compressedImage.depth = is3D ? texD->m_depth : (isArray ? texD->m_arraySize : 0); cmd.args.compressedImage.size = rawData.size(); cmd.args.compressedImage.data = cbD->retainData(rawData); } @@ -1977,11 +2001,11 @@ void QRhiGles2::enqueueSubresUpload(QGles2Texture *texD, QGles2CommandBuffer *cb cmd.cmd = QGles2CommandBuffer::Command::SubImage; cmd.args.subImage.target = texD->target; cmd.args.subImage.texture = texD->texture; - cmd.args.subImage.faceTarget = faceTargetBase + uint(layer); + cmd.args.subImage.faceTarget = effectiveTarget; cmd.args.subImage.level = level; cmd.args.subImage.dx = dp.x(); cmd.args.subImage.dy = dp.y(); - cmd.args.subImage.dz = texD->m_flags.testFlag(QRhiTexture::ThreeDimensional) ? layer : 0; + cmd.args.subImage.dz = is3D || isArray ? layer : 0; cmd.args.subImage.w = size.width(); cmd.args.subImage.h = size.height(); cmd.args.subImage.glformat = texD->glformat; @@ -2090,21 +2114,24 @@ void QRhiGles2::enqueueResourceUpdates(QRhiCommandBuffer *cb, QRhiResourceUpdate QGles2CommandBuffer::Command &cmd(cbD->commands.get()); cmd.cmd = QGles2CommandBuffer::Command::CopyTex; + const bool srcHasZ = srcD->m_flags.testFlag(QRhiTexture::ThreeDimensional) || srcD->m_flags.testFlag(QRhiTexture::TextureArray); + const bool dstHasZ = dstD->m_flags.testFlag(QRhiTexture::ThreeDimensional) || dstD->m_flags.testFlag(QRhiTexture::TextureArray); + cmd.args.copyTex.srcTarget = srcD->target; - cmd.args.copyTex.srcFaceTarget = srcFaceTargetBase + uint(u.desc.sourceLayer()); + cmd.args.copyTex.srcFaceTarget = srcFaceTargetBase + (srcHasZ ? 0u : uint(u.desc.sourceLayer())); cmd.args.copyTex.srcTexture = srcD->texture; cmd.args.copyTex.srcLevel = u.desc.sourceLevel(); cmd.args.copyTex.srcX = sp.x(); cmd.args.copyTex.srcY = sp.y(); - cmd.args.copyTex.srcZ = srcD->m_flags.testFlag(QRhiTexture::ThreeDimensional) ? u.desc.sourceLayer() : 0; + cmd.args.copyTex.srcZ = srcHasZ ? u.desc.sourceLayer() : 0; cmd.args.copyTex.dstTarget = dstD->target; - cmd.args.copyTex.dstFaceTarget = dstFaceTargetBase + uint(u.desc.destinationLayer()); + cmd.args.copyTex.dstFaceTarget = dstFaceTargetBase + (dstHasZ ? 0u : uint(u.desc.destinationLayer())); cmd.args.copyTex.dstTexture = dstD->texture; cmd.args.copyTex.dstLevel = u.desc.destinationLevel(); cmd.args.copyTex.dstX = dp.x(); cmd.args.copyTex.dstY = dp.y(); - cmd.args.copyTex.dstZ = dstD->m_flags.testFlag(QRhiTexture::ThreeDimensional) ? u.desc.destinationLayer() : 0; + cmd.args.copyTex.dstZ = dstHasZ ? u.desc.destinationLayer() : 0; cmd.args.copyTex.w = copySize.width(); cmd.args.copyTex.h = copySize.height(); @@ -2122,7 +2149,9 @@ void QRhiGles2::enqueueResourceUpdates(QRhiCommandBuffer *cb, QRhiResourceUpdate cmd.args.readPixels.w = readImageSize.width(); cmd.args.readPixels.h = readImageSize.height(); cmd.args.readPixels.format = texD->m_format; - if (texD->m_flags.testFlag(QRhiTexture::ThreeDimensional)) { + if (texD->m_flags.testFlag(QRhiTexture::ThreeDimensional) + || texD->m_flags.testFlag(QRhiTexture::TextureArray)) + { cmd.args.readPixels.readTarget = texD->target; cmd.args.readPixels.slice3D = u.rb.layer(); } else { @@ -2843,7 +2872,7 @@ void QRhiGles2::executeCommandBuffer(QRhiCommandBuffer *cb) GLuint fbo; f->glGenFramebuffers(1, &fbo); f->glBindFramebuffer(GL_FRAMEBUFFER, fbo); - if (cmd.args.copyTex.srcTarget == GL_TEXTURE_3D) { + if (cmd.args.copyTex.srcTarget == GL_TEXTURE_3D || cmd.args.copyTex.srcTarget == GL_TEXTURE_2D_ARRAY) { f->glFramebufferTextureLayer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, cmd.args.copyTex.srcTexture, cmd.args.copyTex.srcLevel, cmd.args.copyTex.srcZ); } else { @@ -2851,7 +2880,7 @@ void QRhiGles2::executeCommandBuffer(QRhiCommandBuffer *cb) cmd.args.copyTex.srcFaceTarget, cmd.args.copyTex.srcTexture, cmd.args.copyTex.srcLevel); } f->glBindTexture(cmd.args.copyTex.dstTarget, cmd.args.copyTex.dstTexture); - if (cmd.args.copyTex.dstTarget == GL_TEXTURE_3D) { + if (cmd.args.copyTex.dstTarget == GL_TEXTURE_3D || cmd.args.copyTex.dstTarget == GL_TEXTURE_2D_ARRAY) { f->glCopyTexSubImage3D(cmd.args.copyTex.dstTarget, cmd.args.copyTex.dstLevel, cmd.args.copyTex.dstX, cmd.args.copyTex.dstY, cmd.args.copyTex.dstZ, cmd.args.copyTex.srcX, cmd.args.copyTex.srcY, @@ -2951,7 +2980,7 @@ void QRhiGles2::executeCommandBuffer(QRhiCommandBuffer *cb) f->glPixelStorei(GL_UNPACK_ALIGNMENT, cmd.args.subImage.rowStartAlign); if (cmd.args.subImage.rowLength != 0) f->glPixelStorei(GL_UNPACK_ROW_LENGTH, cmd.args.subImage.rowLength); - if (cmd.args.subImage.target == GL_TEXTURE_3D) { + if (cmd.args.subImage.target == GL_TEXTURE_3D || cmd.args.subImage.target == GL_TEXTURE_2D_ARRAY) { f->glTexSubImage3D(cmd.args.subImage.target, cmd.args.subImage.level, cmd.args.subImage.dx, cmd.args.subImage.dy, cmd.args.subImage.dz, cmd.args.subImage.w, cmd.args.subImage.h, 1, @@ -2971,7 +3000,7 @@ void QRhiGles2::executeCommandBuffer(QRhiCommandBuffer *cb) break; case QGles2CommandBuffer::Command::CompressedImage: f->glBindTexture(cmd.args.compressedImage.target, cmd.args.compressedImage.texture); - if (cmd.args.compressedImage.target == GL_TEXTURE_3D) { + if (cmd.args.compressedImage.target == GL_TEXTURE_3D || cmd.args.compressedImage.target == GL_TEXTURE_2D_ARRAY) { f->glCompressedTexImage3D(cmd.args.compressedImage.target, cmd.args.compressedImage.level, cmd.args.compressedImage.glintformat, cmd.args.compressedImage.w, cmd.args.compressedImage.h, cmd.args.compressedImage.depth, @@ -2985,7 +3014,7 @@ void QRhiGles2::executeCommandBuffer(QRhiCommandBuffer *cb) break; case QGles2CommandBuffer::Command::CompressedSubImage: f->glBindTexture(cmd.args.compressedSubImage.target, cmd.args.compressedSubImage.texture); - if (cmd.args.compressedSubImage.target == GL_TEXTURE_3D) { + if (cmd.args.compressedSubImage.target == GL_TEXTURE_3D || cmd.args.compressedSubImage.target == GL_TEXTURE_2D_ARRAY) { f->glCompressedTexSubImage3D(cmd.args.compressedSubImage.target, cmd.args.compressedSubImage.level, cmd.args.compressedSubImage.dx, cmd.args.compressedSubImage.dy, cmd.args.compressedSubImage.dz, cmd.args.compressedSubImage.w, cmd.args.compressedSubImage.h, 1, @@ -3007,9 +3036,13 @@ void QRhiGles2::executeCommandBuffer(QRhiCommandBuffer *cb) f->glFramebufferRenderbuffer(GL_READ_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, cmd.args.blitFromRb.renderbuffer); f->glBindFramebuffer(GL_DRAW_FRAMEBUFFER, fbo[1]); - - f->glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, cmd.args.blitFromRb.target, - cmd.args.blitFromRb.texture, cmd.args.blitFromRb.dstLevel); + if (cmd.args.blitFromRb.target == GL_TEXTURE_3D || cmd.args.blitFromRb.target == GL_TEXTURE_2D_ARRAY) { + f->glFramebufferTextureLayer(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, + cmd.args.blitFromRb.texture, cmd.args.blitFromRb.dstLevel, cmd.args.blitFromRb.dstLayer); + } else { + f->glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, cmd.args.blitFromRb.target, + cmd.args.blitFromRb.texture, cmd.args.blitFromRb.dstLevel); + } f->glBlitFramebuffer(0, 0, cmd.args.blitFromRb.w, cmd.args.blitFromRb.h, 0, 0, cmd.args.blitFromRb.w, cmd.args.blitFromRb.h, GL_COLOR_BUFFER_BIT, @@ -3755,11 +3788,15 @@ void QRhiGles2::endPass(QRhiCommandBuffer *cb, QRhiResourceUpdateBatch *resource cmd.args.blitFromRb.w = size.width(); cmd.args.blitFromRb.h = size.height(); QGles2Texture *colorTexD = QRHI_RES(QGles2Texture, colorAtt.resolveTexture()); - const GLenum faceTargetBase = colorTexD->m_flags.testFlag(QRhiTexture::CubeMap) ? GL_TEXTURE_CUBE_MAP_POSITIVE_X - : colorTexD->target; - cmd.args.blitFromRb.target = faceTargetBase + uint(colorAtt.resolveLayer()); + if (colorTexD->m_flags.testFlag(QRhiTexture::CubeMap)) + cmd.args.blitFromRb.target = GL_TEXTURE_CUBE_MAP_POSITIVE_X + uint(colorAtt.resolveLayer()); + else + cmd.args.blitFromRb.target = colorTexD->target; cmd.args.blitFromRb.texture = colorTexD->texture; cmd.args.blitFromRb.dstLevel = colorAtt.resolveLevel(); + const bool hasZ = colorTexD->m_flags.testFlag(QRhiTexture::ThreeDimensional) + || colorTexD->m_flags.testFlag(QRhiTexture::TextureArray); + cmd.args.blitFromRb.dstLayer = hasZ ? colorAtt.resolveLayer() : 0; } } } @@ -4576,8 +4613,8 @@ QRhiTexture::Format QGles2RenderBuffer::backingFormat() const } QGles2Texture::QGles2Texture(QRhiImplementation *rhi, Format format, const QSize &pixelSize, int depth, - int sampleCount, Flags flags) - : QRhiTexture(rhi, format, pixelSize, depth, sampleCount, flags) + int arraySize, int sampleCount, Flags flags) + : QRhiTexture(rhi, format, pixelSize, depth, arraySize, sampleCount, flags) { } @@ -4622,6 +4659,7 @@ bool QGles2Texture::prepareCreate(QSize *adjustedSize) const QSize size = m_pixelSize.isEmpty() ? QSize(1, 1) : m_pixelSize; const bool isCube = m_flags.testFlag(CubeMap); + const bool isArray = m_flags.testFlag(QRhiTexture::TextureArray); const bool is3D = m_flags.testFlag(ThreeDimensional); const bool hasMipMaps = m_flags.testFlag(MipMapped); const bool isCompressed = rhiD->isCompressedFormat(m_format); @@ -4634,14 +4672,28 @@ bool QGles2Texture::prepareCreate(QSize *adjustedSize) qWarning("Texture cannot be both cube and 3D"); return false; } + if (isArray && is3D) { + qWarning("Texture cannot be both array and 3D"); + return false; + } m_depth = qMax(1, m_depth); if (m_depth > 1 && !is3D) { qWarning("Texture cannot have a depth of %d when it is not 3D", m_depth); return false; } + m_arraySize = qMax(0, m_arraySize); + if (m_arraySize > 0 && !isArray) { + qWarning("Texture cannot have an array size of %d when it is not an array", m_arraySize); + return false; + } + if (m_arraySize < 1 && isArray) { + qWarning("Texture is an array but array size is %d", m_arraySize); + return false; + } target = isCube ? GL_TEXTURE_CUBE_MAP - : m_sampleCount > 1 ? GL_TEXTURE_2D_MULTISAMPLE : (is3D ? GL_TEXTURE_3D : GL_TEXTURE_2D); + : m_sampleCount > 1 ? GL_TEXTURE_2D_MULTISAMPLE + : (is3D ? GL_TEXTURE_3D : (isArray ? GL_TEXTURE_2D_ARRAY : GL_TEXTURE_2D)); if (m_flags.testFlag(ExternalOES)) target = GL_TEXTURE_EXTERNAL_OES; else if (m_flags.testFlag(TextureRectangleGL)) @@ -4687,21 +4739,23 @@ bool QGles2Texture::create() rhiD->f->glGenTextures(1, &texture); const bool isCube = m_flags.testFlag(CubeMap); + const bool isArray = m_flags.testFlag(QRhiTexture::TextureArray); const bool is3D = m_flags.testFlag(ThreeDimensional); const bool hasMipMaps = m_flags.testFlag(MipMapped); const bool isCompressed = rhiD->isCompressedFormat(m_format); if (!isCompressed) { rhiD->f->glBindTexture(target, texture); if (!m_flags.testFlag(UsedWithLoadStore)) { - if (is3D) { + if (is3D || isArray) { + const int layerCount = is3D ? m_depth : m_arraySize; if (hasMipMaps) { for (int level = 0; level != mipLevelCount; ++level) { const QSize mipSize = rhiD->q->sizeForMipLevel(level, size); - rhiD->f->glTexImage3D(target, level, GLint(glintformat), mipSize.width(), mipSize.height(), m_depth, + rhiD->f->glTexImage3D(target, level, GLint(glintformat), mipSize.width(), mipSize.height(), layerCount, 0, glformat, gltype, nullptr); } } else { - rhiD->f->glTexImage3D(target, 0, GLint(glintformat), size.width(), size.height(), m_depth, + rhiD->f->glTexImage3D(target, 0, GLint(glintformat), size.width(), size.height(), layerCount, 0, glformat, gltype, nullptr); } } else if (hasMipMaps || isCube) { @@ -4722,8 +4776,8 @@ bool QGles2Texture::create() // Must be specified with immutable storage functions otherwise // bindImageTexture may fail. Also, the internal format must be a // sized format here. - if (is3D) - rhiD->f->glTexStorage3D(target, mipLevelCount, glsizedintformat, size.width(), size.height(), m_depth); + if (is3D || isArray) + rhiD->f->glTexStorage3D(target, mipLevelCount, glsizedintformat, size.width(), size.height(), is3D ? m_depth : m_arraySize); else rhiD->f->glTexStorage2D(target, mipLevelCount, glsizedintformat, size.width(), size.height()); } @@ -4942,7 +4996,7 @@ bool QGles2TextureRenderTarget::create() if (texture) { QGles2Texture *texD = QRHI_RES(QGles2Texture, texture); Q_ASSERT(texD->texture && texD->specified); - if (texD->flags().testFlag(QRhiTexture::ThreeDimensional)) { + if (texD->flags().testFlag(QRhiTexture::ThreeDimensional) || texD->flags().testFlag(QRhiTexture::TextureArray)) { rhiD->f->glFramebufferTextureLayer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0 + uint(attIndex), texD->texture, colorAtt.level(), colorAtt.layer()); } else { diff --git a/src/gui/rhi/qrhigles2_p_p.h b/src/gui/rhi/qrhigles2_p_p.h index 4d18e9c60f..48f79e3c1d 100644 --- a/src/gui/rhi/qrhigles2_p_p.h +++ b/src/gui/rhi/qrhigles2_p_p.h @@ -139,7 +139,7 @@ inline bool operator!=(const QGles2SamplerData &a, const QGles2SamplerData &b) struct QGles2Texture : public QRhiTexture { QGles2Texture(QRhiImplementation *rhi, Format format, const QSize &pixelSize, int depth, - int sampleCount, Flags flags); + int arraySize, int sampleCount, Flags flags); ~QGles2Texture(); void destroy() override; bool create() override; @@ -521,6 +521,7 @@ struct QGles2CommandBuffer : public QRhiCommandBuffer GLenum target; GLuint texture; int dstLevel; + int dstLayer; } blitFromRb; struct { GLenum target; @@ -752,6 +753,7 @@ public: QRhiTexture *createTexture(QRhiTexture::Format format, const QSize &pixelSize, int depth, + int arraySize, int sampleCount, QRhiTexture::Flags flags) override; QRhiSampler *createSampler(QRhiSampler::Filter magFilter, @@ -910,6 +912,7 @@ public: maxTextureSize(2048), maxDrawBuffers(4), maxSamples(16), + maxTextureArraySize(0), maxThreadGroupsPerDimension(0), maxThreadsPerThreadGroup(0), maxThreadGroupsX(0), @@ -951,6 +954,7 @@ public: int maxTextureSize; int maxDrawBuffers; int maxSamples; + int maxTextureArraySize; int maxThreadGroupsPerDimension; int maxThreadsPerThreadGroup; int maxThreadGroupsX; diff --git a/src/gui/rhi/qrhimetal.mm b/src/gui/rhi/qrhimetal.mm index d37a130d15..3e4c0e08e2 100644 --- a/src/gui/rhi/qrhimetal.mm +++ b/src/gui/rhi/qrhimetal.mm @@ -613,6 +613,8 @@ bool QRhiMetal::isFeatureSupported(QRhi::Feature feature) const return true; case QRhi::RenderTo3DTextureSlice: return true; + case QRhi::TextureArrays: + return true; default: Q_UNREACHABLE(); return false; @@ -646,6 +648,8 @@ int QRhiMetal::resourceLimit(QRhi::ResourceLimit limit) const #else return 512; #endif + case QRhi::TextureArraySizeMax: + return 2048; default: Q_UNREACHABLE(); return 0; @@ -704,10 +708,10 @@ QRhiRenderBuffer *QRhiMetal::createRenderBuffer(QRhiRenderBuffer::Type type, con } QRhiTexture *QRhiMetal::createTexture(QRhiTexture::Format format, - const QSize &pixelSize, int depth, + const QSize &pixelSize, int depth, int arraySize, int sampleCount, QRhiTexture::Flags flags) { - return new QMetalTexture(this, format, pixelSize, depth, sampleCount, flags); + return new QMetalTexture(this, format, pixelSize, depth, arraySize, sampleCount, flags); } QRhiSampler *QRhiMetal::createSampler(QRhiSampler::Filter magFilter, QRhiSampler::Filter minFilter, @@ -2617,8 +2621,8 @@ QRhiTexture::Format QMetalRenderBuffer::backingFormat() const } QMetalTexture::QMetalTexture(QRhiImplementation *rhi, Format format, const QSize &pixelSize, int depth, - int sampleCount, Flags flags) - : QRhiTexture(rhi, format, pixelSize, depth, sampleCount, flags), + int arraySize, int sampleCount, Flags flags) + : QRhiTexture(rhi, format, pixelSize, depth, arraySize, sampleCount, flags), d(new QMetalTextureData(this)) { for (int i = 0; i < QMTL_FRAMES_IN_FLIGHT; ++i) @@ -2673,6 +2677,7 @@ bool QMetalTexture::prepareCreate(QSize *adjustedSize) const QSize size = m_pixelSize.isEmpty() ? QSize(1, 1) : m_pixelSize; const bool isCube = m_flags.testFlag(CubeMap); const bool is3D = m_flags.testFlag(ThreeDimensional); + const bool isArray = m_flags.testFlag(TextureArray); const bool hasMipMaps = m_flags.testFlag(MipMapped); QRHI_RES_RHI(QRhiMetal); @@ -2697,11 +2702,24 @@ bool QMetalTexture::prepareCreate(QSize *adjustedSize) qWarning("Texture cannot be both cube and 3D"); return false; } + if (isArray && is3D) { + qWarning("Texture cannot be both array and 3D"); + return false; + } m_depth = qMax(1, m_depth); if (m_depth > 1 && !is3D) { qWarning("Texture cannot have a depth of %d when it is not 3D", m_depth); return false; } + m_arraySize = qMax(0, m_arraySize); + if (m_arraySize > 0 && !isArray) { + qWarning("Texture cannot have an array size of %d when it is not an array", m_arraySize); + return false; + } + if (m_arraySize < 1 && isArray) { + qWarning("Texture is an array but array size is %d", m_arraySize); + return false; + } if (adjustedSize) *adjustedSize = size; @@ -2719,12 +2737,24 @@ bool QMetalTexture::create() const bool isCube = m_flags.testFlag(CubeMap); const bool is3D = m_flags.testFlag(ThreeDimensional); - if (isCube) + const bool isArray = m_flags.testFlag(TextureArray); + if (isCube) { desc.textureType = MTLTextureTypeCube; - else if (is3D) + } else if (is3D) { desc.textureType = MTLTextureType3D; - else + } else if (isArray) { +#ifdef Q_OS_IOS + if (samples > 1) { + // would be available on iOS 14.0+ but cannot test for that with a 13 SDK + qWarning("Multisample 2D texture array is not supported on iOS"); + } + desc.textureType = MTLTextureType2DArray; +#else + desc.textureType = samples > 1 ? MTLTextureType2DMultisampleArray : MTLTextureType2DArray; +#endif + } else { desc.textureType = samples > 1 ? MTLTextureType2DMultisample : MTLTextureType2D; + } desc.pixelFormat = d->format; desc.width = NSUInteger(size.width()); desc.height = NSUInteger(size.height()); @@ -2732,6 +2762,8 @@ bool QMetalTexture::create() desc.mipmapLevelCount = NSUInteger(mipLevelCount); if (samples > 1) desc.sampleCount = NSUInteger(samples); + if (isArray) + desc.arrayLength = NSUInteger(m_arraySize); desc.resourceOptions = MTLResourceStorageModePrivate; desc.storageMode = MTLStorageModePrivate; desc.usage = MTLTextureUsageShaderRead; @@ -2750,7 +2782,7 @@ bool QMetalTexture::create() d->owns = true; QRHI_PROF; - QRHI_PROF_F(newTexture(this, true, mipLevelCount, isCube ? 6 : 1, samples)); + QRHI_PROF_F(newTexture(this, true, mipLevelCount, isCube ? 6 : (isArray ? m_arraySize : 1), samples)); lastActiveFrameSlot = -1; generation += 1; @@ -2794,8 +2826,9 @@ id<MTLTexture> QMetalTextureData::viewForLevel(int level) const MTLTextureType type = [tex textureType]; const bool isCube = q->m_flags.testFlag(QRhiTexture::CubeMap); + const bool isArray = q->m_flags.testFlag(QRhiTexture::TextureArray); id<MTLTexture> view = [tex newTextureViewWithPixelFormat: format textureType: type - levels: NSMakeRange(NSUInteger(level), 1) slices: NSMakeRange(0, isCube ? 6 : 1)]; + levels: NSMakeRange(NSUInteger(level), 1) slices: NSMakeRange(0, isCube ? 6 : (isArray ? q->m_arraySize : 1))]; perLevelViews[level] = view; return view; diff --git a/src/gui/rhi/qrhimetal_p_p.h b/src/gui/rhi/qrhimetal_p_p.h index 579a2063ae..f24912a640 100644 --- a/src/gui/rhi/qrhimetal_p_p.h +++ b/src/gui/rhi/qrhimetal_p_p.h @@ -103,7 +103,7 @@ struct QMetalTextureData; struct QMetalTexture : public QRhiTexture { QMetalTexture(QRhiImplementation *rhi, Format format, const QSize &pixelSize, int depth, - int sampleCount, Flags flags); + int arraySize, int sampleCount, Flags flags); ~QMetalTexture(); void destroy() override; bool create() override; @@ -366,6 +366,7 @@ public: QRhiTexture *createTexture(QRhiTexture::Format format, const QSize &pixelSize, int depth, + int arraySize, int sampleCount, QRhiTexture::Flags flags) override; QRhiSampler *createSampler(QRhiSampler::Filter magFilter, diff --git a/src/gui/rhi/qrhinull.cpp b/src/gui/rhi/qrhinull.cpp index 894caf8f35..c160958493 100644 --- a/src/gui/rhi/qrhinull.cpp +++ b/src/gui/rhi/qrhinull.cpp @@ -162,6 +162,8 @@ int QRhiNull::resourceLimit(QRhi::ResourceLimit limit) const return 0; case QRhi::MaxThreadGroupZ: return 0; + case QRhi::TextureArraySizeMax: + return 2048; default: Q_UNREACHABLE(); return 0; @@ -219,10 +221,10 @@ QRhiRenderBuffer *QRhiNull::createRenderBuffer(QRhiRenderBuffer::Type type, cons } QRhiTexture *QRhiNull::createTexture(QRhiTexture::Format format, - const QSize &pixelSize, int depth, + const QSize &pixelSize, int depth, int arraySize, int sampleCount, QRhiTexture::Flags flags) { - return new QNullTexture(this, format, pixelSize, depth, sampleCount, flags); + return new QNullTexture(this, format, pixelSize, depth, arraySize, sampleCount, flags); } QRhiSampler *QRhiNull::createSampler(QRhiSampler::Filter magFilter, QRhiSampler::Filter minFilter, @@ -671,8 +673,8 @@ QRhiTexture::Format QNullRenderBuffer::backingFormat() const } QNullTexture::QNullTexture(QRhiImplementation *rhi, Format format, const QSize &pixelSize, int depth, - int sampleCount, Flags flags) - : QRhiTexture(rhi, format, pixelSize, depth, sampleCount, flags) + int arraySize, int sampleCount, Flags flags) + : QRhiTexture(rhi, format, pixelSize, depth, arraySize, sampleCount, flags) { } @@ -703,11 +705,13 @@ bool QNullTexture::create() QRHI_RES_RHI(QRhiNull); const bool isCube = m_flags.testFlag(CubeMap); const bool is3D = m_flags.testFlag(ThreeDimensional); + const bool isArray = m_flags.testFlag(TextureArray); const bool hasMipMaps = m_flags.testFlag(MipMapped); QSize size = m_pixelSize.isEmpty() ? QSize(1, 1) : m_pixelSize; m_depth = qMax(1, m_depth); const int mipLevelCount = hasMipMaps ? rhiD->q->mipLevelsForSize(size) : 1; - const int layerCount = is3D ? m_depth : (isCube ? 6 : 1); + m_arraySize = qMax(0, m_arraySize); + const int layerCount = is3D ? m_depth : (isCube ? 6 : (isArray ? m_arraySize : 1)); if (m_format == RGBA8) { image.resize(layerCount); @@ -737,12 +741,13 @@ bool QNullTexture::createFrom(QRhiTexture::NativeTexture src) QRHI_RES_RHI(QRhiNull); const bool isCube = m_flags.testFlag(CubeMap); + const bool isArray = m_flags.testFlag(TextureArray); const bool hasMipMaps = m_flags.testFlag(MipMapped); QSize size = m_pixelSize.isEmpty() ? QSize(1, 1) : m_pixelSize; const int mipLevelCount = hasMipMaps ? rhiD->q->mipLevelsForSize(size) : 1; QRHI_PROF; - QRHI_PROF_F(newTexture(this, false, mipLevelCount, isCube ? 6 : 1, 1)); + QRHI_PROF_F(newTexture(this, false, mipLevelCount, isCube ? 6 : (isArray ? m_arraySize : 1), 1)); rhiD->registerResource(this); return true; diff --git a/src/gui/rhi/qrhinull_p_p.h b/src/gui/rhi/qrhinull_p_p.h index 24fb426503..4d3c508044 100644 --- a/src/gui/rhi/qrhinull_p_p.h +++ b/src/gui/rhi/qrhinull_p_p.h @@ -83,7 +83,7 @@ struct QNullRenderBuffer : public QRhiRenderBuffer struct QNullTexture : public QRhiTexture { QNullTexture(QRhiImplementation *rhi, Format format, const QSize &pixelSize, int depth, - int sampleCount, Flags flags); + int arraySize, int sampleCount, Flags flags); ~QNullTexture(); void destroy() override; bool create() override; @@ -224,6 +224,7 @@ public: QRhiTexture *createTexture(QRhiTexture::Format format, const QSize &pixelSize, int depth, + int arraySize, int sampleCount, QRhiTexture::Flags flags) override; QRhiSampler *createSampler(QRhiSampler::Filter magFilter, diff --git a/src/gui/rhi/qrhivulkan.cpp b/src/gui/rhi/qrhivulkan.cpp index 42d6f7e6ec..0bd3e6e95d 100644 --- a/src/gui/rhi/qrhivulkan.cpp +++ b/src/gui/rhi/qrhivulkan.cpp @@ -3454,6 +3454,7 @@ void QRhiVulkan::enqueueResourceUpdates(QVkCommandBuffer *cbD, QRhiResourceUpdat QVkTexture *utexD = QRHI_RES(QVkTexture, u.dst); Q_ASSERT(utexD->m_flags.testFlag(QRhiTexture::UsedWithGenerateMips)); const bool isCube = utexD->m_flags.testFlag(QRhiTexture::CubeMap); + const bool isArray = utexD->m_flags.testFlag(QRhiTexture::TextureArray); const bool is3D = utexD->m_flags.testFlag(QRhiTexture::ThreeDimensional); VkImageLayout origLayout = utexD->usageState.layout; @@ -3462,7 +3463,7 @@ void QRhiVulkan::enqueueResourceUpdates(QVkCommandBuffer *cbD, QRhiResourceUpdat if (!origStage) origStage = VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT; - for (int layer = 0; layer < (isCube ? 6 : 1); ++layer) { + for (int layer = 0; layer < (isCube ? 6 : (isArray ? utexD->m_arraySize : 1)); ++layer) { int w = utexD->m_pixelSize.width(); int h = utexD->m_pixelSize.height(); int depth = is3D ? utexD->m_depth : 1; @@ -4269,6 +4270,8 @@ bool QRhiVulkan::isFeatureSupported(QRhi::Feature feature) const return true; case QRhi::RenderTo3DTextureSlice: return caps.texture3DSliceAs2D; + case QRhi::TextureArrays: + return true; default: Q_UNREACHABLE(); return false; @@ -4300,6 +4303,8 @@ int QRhiVulkan::resourceLimit(QRhi::ResourceLimit limit) const return int(physDevProperties.limits.maxComputeWorkGroupSize[1]); case QRhi::MaxThreadGroupZ: return int(physDevProperties.limits.maxComputeWorkGroupSize[2]); + case QRhi::TextureArraySizeMax: + return int(physDevProperties.limits.maxImageArrayLayers); default: Q_UNREACHABLE(); return 0; @@ -4475,10 +4480,10 @@ QRhiRenderBuffer *QRhiVulkan::createRenderBuffer(QRhiRenderBuffer::Type type, co } QRhiTexture *QRhiVulkan::createTexture(QRhiTexture::Format format, - const QSize &pixelSize, int depth, + const QSize &pixelSize, int depth, int arraySize, int sampleCount, QRhiTexture::Flags flags) { - return new QVkTexture(this, format, pixelSize, depth, sampleCount, flags); + return new QVkTexture(this, format, pixelSize, depth, arraySize, sampleCount, flags); } QRhiSampler *QRhiVulkan::createSampler(QRhiSampler::Filter magFilter, QRhiSampler::Filter minFilter, @@ -5713,6 +5718,7 @@ bool QVkRenderBuffer::create() backingTexture = QRHI_RES(QVkTexture, rhiD->createTexture(backingFormat(), m_pixelSize, 1, + 0, m_sampleCount, QRhiTexture::RenderTarget | QRhiTexture::UsedAsTransferSource)); } else { @@ -5762,8 +5768,8 @@ QRhiTexture::Format QVkRenderBuffer::backingFormat() const } QVkTexture::QVkTexture(QRhiImplementation *rhi, Format format, const QSize &pixelSize, int depth, - int sampleCount, Flags flags) - : QRhiTexture(rhi, format, pixelSize, depth, sampleCount, flags) + int arraySize, int sampleCount, Flags flags) + : QRhiTexture(rhi, format, pixelSize, depth, arraySize, sampleCount, flags) { for (int i = 0; i < QVK_FRAMES_IN_FLIGHT; ++i) { stagingBuffers[i] = VK_NULL_HANDLE; @@ -5834,6 +5840,7 @@ bool QVkTexture::prepareCreate(QSize *adjustedSize) const QSize size = m_pixelSize.isEmpty() ? QSize(1, 1) : m_pixelSize; const bool isCube = m_flags.testFlag(CubeMap); + const bool isArray = m_flags.testFlag(TextureArray); const bool is3D = m_flags.testFlag(ThreeDimensional); const bool hasMipMaps = m_flags.testFlag(MipMapped); @@ -5862,11 +5869,24 @@ bool QVkTexture::prepareCreate(QSize *adjustedSize) qWarning("Texture cannot be both cube and 3D"); return false; } + if (isArray && is3D) { + qWarning("Texture cannot be both array and 3D"); + return false; + } m_depth = qMax(1, m_depth); if (m_depth > 1 && !is3D) { qWarning("Texture cannot have a depth of %d when it is not 3D", m_depth); return false; } + m_arraySize = qMax(0, m_arraySize); + if (m_arraySize > 0 && !isArray) { + qWarning("Texture cannot have an array size of %d when it is not an array", m_arraySize); + return false; + } + if (m_arraySize < 1 && isArray) { + qWarning("Texture is an array but array size is %d", m_arraySize); + return false; + } usageState.layout = VK_IMAGE_LAYOUT_PREINITIALIZED; usageState.access = 0; @@ -5884,13 +5904,16 @@ bool QVkTexture::finishCreate() const auto aspectMask = aspectMaskForTextureFormat(m_format); const bool isCube = m_flags.testFlag(CubeMap); + const bool isArray = m_flags.testFlag(TextureArray); const bool is3D = m_flags.testFlag(ThreeDimensional); VkImageViewCreateInfo viewInfo; memset(&viewInfo, 0, sizeof(viewInfo)); viewInfo.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO; viewInfo.image = image; - viewInfo.viewType = isCube ? VK_IMAGE_VIEW_TYPE_CUBE : (is3D ? VK_IMAGE_VIEW_TYPE_3D : VK_IMAGE_VIEW_TYPE_2D); + viewInfo.viewType = isCube ? VK_IMAGE_VIEW_TYPE_CUBE + : (is3D ? VK_IMAGE_VIEW_TYPE_3D + : (isArray ? VK_IMAGE_VIEW_TYPE_2D_ARRAY : VK_IMAGE_VIEW_TYPE_2D)); viewInfo.format = vkformat; viewInfo.components.r = VK_COMPONENT_SWIZZLE_R; viewInfo.components.g = VK_COMPONENT_SWIZZLE_G; @@ -5898,7 +5921,7 @@ bool QVkTexture::finishCreate() viewInfo.components.a = VK_COMPONENT_SWIZZLE_A; viewInfo.subresourceRange.aspectMask = aspectMask; viewInfo.subresourceRange.levelCount = mipLevelCount; - viewInfo.subresourceRange.layerCount = isCube ? 6 : 1; + viewInfo.subresourceRange.layerCount = isCube ? 6 : (isArray ? m_arraySize : 1); VkResult err = rhiD->df->vkCreateImageView(rhiD->dev, &viewInfo, nullptr, &imageView); if (err != VK_SUCCESS) { @@ -5922,6 +5945,7 @@ bool QVkTexture::create() const bool isRenderTarget = m_flags.testFlag(QRhiTexture::RenderTarget); const bool isDepth = isDepthTextureFormat(m_format); const bool isCube = m_flags.testFlag(CubeMap); + const bool isArray = m_flags.testFlag(TextureArray); const bool is3D = m_flags.testFlag(ThreeDimensional); VkImageCreateInfo imageInfo; @@ -5952,7 +5976,7 @@ bool QVkTexture::create() imageInfo.extent.height = uint32_t(size.height()); imageInfo.extent.depth = is3D ? m_depth : 1; imageInfo.mipLevels = mipLevelCount; - imageInfo.arrayLayers = isCube ? 6 : 1; + imageInfo.arrayLayers = isCube ? 6 : (isArray ? m_arraySize : 1); imageInfo.samples = samples; imageInfo.tiling = VK_IMAGE_TILING_OPTIMAL; imageInfo.initialLayout = VK_IMAGE_LAYOUT_PREINITIALIZED; @@ -5989,7 +6013,7 @@ bool QVkTexture::create() rhiD->setObjectName(uint64_t(image), VK_DEBUG_REPORT_OBJECT_TYPE_IMAGE_EXT, m_objectName); QRHI_PROF; - QRHI_PROF_F(newTexture(this, true, int(mipLevelCount), isCube ? 6 : 1, samples)); + QRHI_PROF_F(newTexture(this, true, int(mipLevelCount), isCube ? 6 : (isArray ? m_arraySize : 1), samples)); owns = true; rhiD->registerResource(this); @@ -6010,8 +6034,11 @@ bool QVkTexture::createFrom(QRhiTexture::NativeTexture src) if (!finishCreate()) return false; + const bool isCube = m_flags.testFlag(CubeMap); + const bool isArray = m_flags.testFlag(TextureArray); + QRHI_PROF; - QRHI_PROF_F(newTexture(this, false, int(mipLevelCount), m_flags.testFlag(CubeMap) ? 6 : 1, samples)); + QRHI_PROF_F(newTexture(this, false, int(mipLevelCount), isCube ? 6 : (isArray ? m_arraySize : 1), samples)); usageState.layout = VkImageLayout(src.layout); @@ -6039,13 +6066,16 @@ VkImageView QVkTexture::imageViewForLevel(int level) const VkImageAspectFlags aspectMask = aspectMaskForTextureFormat(m_format); const bool isCube = m_flags.testFlag(CubeMap); + const bool isArray = m_flags.testFlag(TextureArray); const bool is3D = m_flags.testFlag(ThreeDimensional); VkImageViewCreateInfo viewInfo; memset(&viewInfo, 0, sizeof(viewInfo)); viewInfo.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO; viewInfo.image = image; - viewInfo.viewType = isCube ? VK_IMAGE_VIEW_TYPE_CUBE : (is3D ? VK_IMAGE_VIEW_TYPE_3D : VK_IMAGE_VIEW_TYPE_2D); + viewInfo.viewType = isCube ? VK_IMAGE_VIEW_TYPE_CUBE + : (is3D ? VK_IMAGE_VIEW_TYPE_3D + : (isArray ? VK_IMAGE_VIEW_TYPE_2D_ARRAY : VK_IMAGE_VIEW_TYPE_2D)); viewInfo.format = vkformat; viewInfo.components.r = VK_COMPONENT_SWIZZLE_R; viewInfo.components.g = VK_COMPONENT_SWIZZLE_G; @@ -6055,7 +6085,7 @@ VkImageView QVkTexture::imageViewForLevel(int level) viewInfo.subresourceRange.baseMipLevel = uint32_t(level); viewInfo.subresourceRange.levelCount = 1; viewInfo.subresourceRange.baseArrayLayer = 0; - viewInfo.subresourceRange.layerCount = isCube ? 6 : 1; + viewInfo.subresourceRange.layerCount = isCube ? 6 : (isArray ? m_arraySize : 1); VkImageView v = VK_NULL_HANDLE; QRHI_RES_RHI(QRhiVulkan); diff --git a/src/gui/rhi/qrhivulkan_p_p.h b/src/gui/rhi/qrhivulkan_p_p.h index f2a3a809d3..42ae9aef3b 100644 --- a/src/gui/rhi/qrhivulkan_p_p.h +++ b/src/gui/rhi/qrhivulkan_p_p.h @@ -129,7 +129,7 @@ struct QVkRenderBuffer : public QRhiRenderBuffer struct QVkTexture : public QRhiTexture { QVkTexture(QRhiImplementation *rhi, Format format, const QSize &pixelSize, int depth, - int sampleCount, Flags flags); + int arraySize, int sampleCount, Flags flags); ~QVkTexture(); void destroy() override; bool create() override; @@ -682,6 +682,7 @@ public: QRhiTexture *createTexture(QRhiTexture::Format format, const QSize &pixelSize, int depth, + int arraySize, int sampleCount, QRhiTexture::Flags flags) override; QRhiSampler *createSampler(QRhiSampler::Filter magFilter, |