summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorLaszlo Agocs <laszlo.agocs@qt.io>2021-05-19 19:30:53 +0200
committerLaszlo Agocs <laszlo.agocs@qt.io>2021-05-31 17:16:57 +0200
commit51c22a1f51e2f91289c938be7754f957c9bfa47e (patch)
tree58eea1c4aeb83f4ee2ede516f321624ab5558181 /src
parent5eab5d62530f351307a01ca57af964e5c506079c (diff)
rhi: Add support for 3D textures
Supported on OpenGL (and ES) 3.0+ and everywhere else. Can also be a render target, targeting a single slice at a time. Can be mipmapped, cannot be multisample. Reading back a given slice from a 3D texture is left as a future exercise, for now it is documented to be not supported. Upload is going to be limited to one slice in one upload entry, just like we specify one face or one miplevel for cubemap and mipmapped textures. This also involves some welcome hardening of how texture subresources are described internally: as we no longer can count on a layer index between 0..5 (as is the case with cubemaps), simply arrays with MAX_LAYER==6 are no longer sufficient. Switch to sufficiently dynamic data structures where applicable. On Vulkan rendering to a slice needs Vulkan 1.1 (and 1.1 enabled on the VkInstance). Task-number: QTBUG-89703 Change-Id: Ide6c20124ec9201d94ffc339dd479cd1ece777b0 Reviewed-by: Andy Nichols <andy.nichols@qt.io>
Diffstat (limited to 'src')
-rw-r--r--src/gui/rhi/qrhi.cpp68
-rw-r--r--src/gui/rhi/qrhi_p.h21
-rw-r--r--src/gui/rhi/qrhi_p_p.h12
-rw-r--r--src/gui/rhi/qrhid3d11.cpp178
-rw-r--r--src/gui/rhi/qrhid3d11_p_p.h13
-rw-r--r--src/gui/rhi/qrhigles2.cpp183
-rw-r--r--src/gui/rhi/qrhigles2_p_p.h17
-rw-r--r--src/gui/rhi/qrhimetal.mm87
-rw-r--r--src/gui/rhi/qrhimetal_p_p.h3
-rw-r--r--src/gui/rhi/qrhinull.cpp18
-rw-r--r--src/gui/rhi/qrhinull_p_p.h5
-rw-r--r--src/gui/rhi/qrhiprofiler.cpp7
-rw-r--r--src/gui/rhi/qrhivulkan.cpp159
-rw-r--r--src/gui/rhi/qrhivulkan_p_p.h19
14 files changed, 587 insertions, 203 deletions
diff --git a/src/gui/rhi/qrhi.cpp b/src/gui/rhi/qrhi.cpp
index 242f244c1d..58962b8e35 100644
--- a/src/gui/rhi/qrhi.cpp
+++ b/src/gui/rhi/qrhi.cpp
@@ -651,6 +651,15 @@ Q_LOGGING_CATEGORY(QRHI_LOG_INFO, "qt.rhi.general")
may want to associated a renderbuffer object with an EGLImage object) it is
important to allow wrapping an existing OpenGL renderbuffer object with a
QRhiRenderBuffer.
+
+ \value ThreeDimensionalTextures Indicates that 3D textures are supported.
+ In practice this feature will be unsupported with OpenGL and OpenGL ES
+ versions lower than 3.0.
+
+ \value RenderTo3DTextureSlice Indicates that rendering to a slice in a 3D
+ 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.
*/
/*!
@@ -1477,9 +1486,10 @@ QDebug operator<<(QDebug dbg, const QRhiShaderStage &s)
support for multisample textures, but does support multisample
renderbuffers).
- When targeting a non-multisample texture, the layer() and level()
- indicate the targeted layer (face index \c{0-5} for cubemaps) and mip
- level.
+ 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.
When texture() or renderBuffer() is multisample, resolveTexture() can be
set optionally. When set, samples are resolved automatically into that
@@ -1815,6 +1825,13 @@ QRhiTextureUploadDescription::QRhiTextureUploadDescription(std::initializer_list
\note The source and destination rectangles defined by pixelSize(),
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.
*/
/*!
@@ -2405,6 +2422,15 @@ bool QRhiRenderBuffer::createFrom(NativeRenderBuffer src)
\value ExternalOES The texture should use the GL_TEXTURE_EXTERNAL_OES
target with OpenGL. This flag is ignored with other graphics APIs.
+
+ \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. Reading back the contents of a 3D texture is not currently
+ supported. 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.
*/
/*!
@@ -2495,10 +2521,10 @@ bool QRhiRenderBuffer::createFrom(NativeRenderBuffer src)
/*!
\internal
*/
-QRhiTexture::QRhiTexture(QRhiImplementation *rhi, Format format_, const QSize &pixelSize_,
+QRhiTexture::QRhiTexture(QRhiImplementation *rhi, Format format_, const QSize &pixelSize_, int depth_,
int sampleCount_, Flags flags_)
: QRhiResource(rhi),
- m_format(format_), m_pixelSize(pixelSize_), m_sampleCount(sampleCount_), m_flags(flags_)
+ m_format(format_), m_pixelSize(pixelSize_), m_depth(depth_), m_sampleCount(sampleCount_), m_flags(flags_)
{
}
@@ -4625,7 +4651,7 @@ void QRhiImplementation::textureFormatInfo(QRhiTexture::Format format, const QSi
}
// Approximate because it excludes subresource alignment or multisampling.
-quint32 QRhiImplementation::approxByteSizeForTexture(QRhiTexture::Format format, const QSize &baseSize,
+quint32 QRhiImplementation::approxByteSizeForTexture(QRhiTexture::Format format, const QSize &baseSize, int depth,
int mipCount, int layerCount)
{
quint32 approxSize = 0;
@@ -4636,7 +4662,8 @@ quint32 QRhiImplementation::approxByteSizeForTexture(QRhiTexture::Format format,
textureFormatInfo(format, size, nullptr, &byteSize, nullptr);
approxSize += byteSize;
}
- approxSize *= uint(layerCount);
+ approxSize *= depth; // 3D texture depth or 1 otherwise
+ approxSize *= uint(layerCount); // 6 for cubemaps or 1 otherwise
return approxSize;
}
@@ -5295,6 +5322,8 @@ void QRhiResourceUpdateBatch::copyTexture(QRhiTexture *dst, QRhiTexture *src, co
\note Multisample textures cannot be read back.
+ \note 3D textures cannot be read back.
+
\note The readback returns raw byte data, in order to allow the applications
to interpret it in any way they see fit. Be aware of the blending settings
of rendering code: if the blending is set up to rely on premultiplied alpha,
@@ -6377,7 +6406,30 @@ QRhiTexture *QRhi::newTexture(QRhiTexture::Format format,
int sampleCount,
QRhiTexture::Flags flags)
{
- return d->createTexture(format, pixelSize, sampleCount, flags);
+ return d->createTexture(format, pixelSize, 1, sampleCount, flags);
+}
+
+/*!
+ \return a new 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
+ flags, but using this overload that can be omitted because the flag is set
+ implicitly whenever \a depth is greater than 0. For 2D and cube textures \a
+ depth should be set to 0.
+
+ \overload
+ */
+QRhiTexture *QRhi::newTexture(QRhiTexture::Format format,
+ int width, int height, int depth,
+ int sampleCount,
+ QRhiTexture::Flags flags)
+{
+ if (depth > 0)
+ flags |= QRhiTexture::ThreeDimensional;
+
+ return d->createTexture(format, QSize(width, height), depth, sampleCount, flags);
}
/*!
diff --git a/src/gui/rhi/qrhi_p.h b/src/gui/rhi/qrhi_p.h
index c915f8df0d..c2a09b7edf 100644
--- a/src/gui/rhi/qrhi_p.h
+++ b/src/gui/rhi/qrhi_p.h
@@ -753,7 +753,8 @@ public:
UsedWithGenerateMips = 1 << 6,
UsedWithLoadStore = 1 << 7,
UsedAsCompressedAtlas = 1 << 8,
- ExternalOES = 1 << 9
+ ExternalOES = 1 << 9,
+ ThreeDimensional = 1 << 10
};
Q_DECLARE_FLAGS(Flags, Flag)
@@ -819,6 +820,9 @@ public:
QSize pixelSize() const { return m_pixelSize; }
void setPixelSize(const QSize &sz) { m_pixelSize = sz; }
+ int depth() const { return m_depth; }
+ void setDepth(int depth) { m_depth = depth; }
+
Flags flags() const { return m_flags; }
void setFlags(Flags f) { m_flags = f; }
@@ -831,10 +835,11 @@ public:
virtual void setNativeLayout(int layout);
protected:
- QRhiTexture(QRhiImplementation *rhi, Format format_, const QSize &pixelSize_,
+ QRhiTexture(QRhiImplementation *rhi, Format format_, const QSize &pixelSize_, int depth_,
int sampleCount_, Flags flags_);
Format m_format;
QSize m_pixelSize;
+ int m_depth;
int m_sampleCount;
Flags m_flags;
};
@@ -1540,7 +1545,9 @@ public:
ReadBackAnyTextureFormat,
PipelineCacheDataLoadSave,
ImageDataStride,
- RenderBufferImport
+ RenderBufferImport,
+ ThreeDimensionalTextures,
+ RenderTo3DTextureSlice
};
enum BeginFrameFlag {
@@ -1600,6 +1607,11 @@ public:
int sampleCount = 1,
QRhiTexture::Flags flags = {});
+ QRhiTexture *newTexture(QRhiTexture::Format format,
+ int width, int height, int depth,
+ int sampleCount = 1,
+ QRhiTexture::Flags flags = {});
+
QRhiSampler *newSampler(QRhiSampler::Filter magFilter,
QRhiSampler::Filter minFilter,
QRhiSampler::Filter mipmapMode,
@@ -1646,8 +1658,7 @@ public:
QRhiProfiler *profiler();
- static const int MAX_LAYERS = 6; // cubemaps only
- static const int MAX_LEVELS = 16; // a width and/or height of 65536 should be enough for everyone
+ static const int MAX_MIP_LEVELS = 16; // a width and/or height of 65536 should be enough for everyone
void releaseCachedResources();
diff --git a/src/gui/rhi/qrhi_p_p.h b/src/gui/rhi/qrhi_p_p.h
index f33626ae1d..89dd7b8e44 100644
--- a/src/gui/rhi/qrhi_p_p.h
+++ b/src/gui/rhi/qrhi_p_p.h
@@ -87,6 +87,7 @@ public:
QRhiTexture::Format backingFormatHint) = 0;
virtual QRhiTexture *createTexture(QRhiTexture::Format format,
const QSize &pixelSize,
+ int depth,
int sampleCount,
QRhiTexture::Flags flags) = 0;
virtual QRhiSampler *createSampler(QRhiSampler::Filter magFilter,
@@ -180,7 +181,7 @@ public:
QSize *blockDim) const;
void textureFormatInfo(QRhiTexture::Format format, const QSize &size,
quint32 *bpl, quint32 *byteSize, quint32 *bytesPerPixel) const;
- quint32 approxByteSizeForTexture(QRhiTexture::Format format, const QSize &baseSize,
+ quint32 approxByteSizeForTexture(QRhiTexture::Format format, const QSize &baseSize, int depth,
int mipCount, int layerCount);
QRhiProfilerPrivate *profilerPrivateOrNull()
@@ -447,7 +448,8 @@ public:
// In the backend this can then end up, where applicable, as a
// single, batched copy operation with only one set of barriers.
// This helps when doing for example glyph cache fills.
- QList<QRhiTextureSubresourceUploadDescription> subresDesc[QRhi::MAX_LAYERS][QRhi::MAX_LEVELS];
+ using MipLevelUploadList = std::array<QVector<QRhiTextureSubresourceUploadDescription>, QRhi::MAX_MIP_LEVELS>;
+ QVarLengthArray<MipLevelUploadList, 6> subresDesc;
QRhiTexture *src;
QRhiTextureCopyDescription desc;
QRhiReadbackDescription rb;
@@ -458,6 +460,12 @@ public:
TextureOp op = {};
op.type = Upload;
op.dst = tex;
+ int maxLayer = -1;
+ for (auto it = desc.cbeginEntries(), itEnd = desc.cendEntries(); it != itEnd; ++it) {
+ if (it->layer() > maxLayer)
+ maxLayer = it->layer();
+ }
+ op.subresDesc.resize(maxLayer + 1);
for (auto it = desc.cbeginEntries(), itEnd = desc.cendEntries(); it != itEnd; ++it)
op.subresDesc[it->layer()][it->level()].append(it->description());
return op;
diff --git a/src/gui/rhi/qrhid3d11.cpp b/src/gui/rhi/qrhid3d11.cpp
index 8d15f2622f..d16332e5b9 100644
--- a/src/gui/rhi/qrhid3d11.cpp
+++ b/src/gui/rhi/qrhid3d11.cpp
@@ -546,6 +546,10 @@ bool QRhiD3D11::isFeatureSupported(QRhi::Feature feature) const
return true;
case QRhi::RenderBufferImport:
return false;
+ case QRhi::ThreeDimensionalTextures:
+ return true;
+ case QRhi::RenderTo3DTextureSlice:
+ return true;
default:
Q_UNREACHABLE();
return false;
@@ -633,10 +637,11 @@ QRhiRenderBuffer *QRhiD3D11::createRenderBuffer(QRhiRenderBuffer::Type type, con
return new QD3D11RenderBuffer(this, type, pixelSize, sampleCount, flags, backingFormatHint);
}
-QRhiTexture *QRhiD3D11::createTexture(QRhiTexture::Format format, const QSize &pixelSize,
+QRhiTexture *QRhiD3D11::createTexture(QRhiTexture::Format format,
+ const QSize &pixelSize, int depth,
int sampleCount, QRhiTexture::Flags flags)
{
- return new QD3D11Texture(this, format, pixelSize, sampleCount, flags);
+ return new QD3D11Texture(this, format, pixelSize, depth, sampleCount, flags);
}
QRhiSampler *QRhiD3D11::createSampler(QRhiSampler::Filter magFilter, QRhiSampler::Filter minFilter,
@@ -1351,17 +1356,18 @@ QRhi::FrameOpResult QRhiD3D11::finish()
void QRhiD3D11::enqueueSubresUpload(QD3D11Texture *texD, QD3D11CommandBuffer *cbD,
int layer, int level, const QRhiTextureSubresourceUploadDescription &subresDesc)
{
- UINT subres = D3D11CalcSubresource(UINT(level), UINT(layer), texD->mipLevelCount);
- const QPoint dp = subresDesc.destinationTopLeft();
+ const bool is3D = texD->m_flags.testFlag(QRhiTexture::ThreeDimensional);
+ UINT subres = D3D11CalcSubresource(UINT(level), is3D ? 0u : UINT(layer), texD->mipLevelCount);
D3D11_BOX box;
- box.front = 0;
+ box.front = is3D ? UINT(layer) : 0u;
// back, right, bottom are exclusive
- box.back = 1;
+ box.back = box.front + 1;
QD3D11CommandBuffer::Command &cmd(cbD->commands.get());
cmd.cmd = QD3D11CommandBuffer::Command::UpdateSubRes;
- cmd.args.updateSubRes.dst = texD->tex;
+ cmd.args.updateSubRes.dst = texD->textureResource();
cmd.args.updateSubRes.dstSubRes = subres;
+ const QPoint dp = subresDesc.destinationTopLeft();
if (!subresDesc.image().isNull()) {
QImage img = subresDesc.image();
QSize size = img.size();
@@ -1488,6 +1494,7 @@ void QRhiD3D11::enqueueResourceUpdates(QRhiCommandBuffer *cb, QRhiResourceUpdate
cmd.args.copySubRes.dstSubRes = 0;
cmd.args.copySubRes.dstX = 0;
cmd.args.copySubRes.dstY = 0;
+ cmd.args.copySubRes.dstZ = 0;
cmd.args.copySubRes.src = bufD->buffer;
cmd.args.copySubRes.srcSubRes = 0;
cmd.args.copySubRes.hasSrcBox = true;
@@ -1508,8 +1515,8 @@ void QRhiD3D11::enqueueResourceUpdates(QRhiCommandBuffer *cb, QRhiResourceUpdate
const QRhiResourceUpdateBatchPrivate::TextureOp &u(ud->textureOps[opIdx]);
if (u.type == QRhiResourceUpdateBatchPrivate::TextureOp::Upload) {
QD3D11Texture *texD = QRHI_RES(QD3D11Texture, u.dst);
- for (int layer = 0; layer < QRhi::MAX_LAYERS; ++layer) {
- for (int level = 0; level < QRhi::MAX_LEVELS; ++level) {
+ for (int layer = 0, maxLayer = u.subresDesc.count(); layer < maxLayer; ++layer) {
+ for (int level = 0; level < QRhi::MAX_MIP_LEVELS; ++level) {
for (const QRhiTextureSubresourceUploadDescription &subresDesc : qAsConst(u.subresDesc[layer][level]))
enqueueSubresUpload(texD, cbD, layer, level, subresDesc);
}
@@ -1518,8 +1525,10 @@ void QRhiD3D11::enqueueResourceUpdates(QRhiCommandBuffer *cb, QRhiResourceUpdate
Q_ASSERT(u.src && u.dst);
QD3D11Texture *srcD = QRHI_RES(QD3D11Texture, u.src);
QD3D11Texture *dstD = QRHI_RES(QD3D11Texture, u.dst);
- UINT srcSubRes = D3D11CalcSubresource(UINT(u.desc.sourceLevel()), UINT(u.desc.sourceLayer()), srcD->mipLevelCount);
- UINT dstSubRes = D3D11CalcSubresource(UINT(u.desc.destinationLevel()), UINT(u.desc.destinationLayer()), dstD->mipLevelCount);
+ const bool srcIs3D = srcD->m_flags.testFlag(QRhiTexture::ThreeDimensional);
+ const bool dstIs3D = dstD->m_flags.testFlag(QRhiTexture::ThreeDimensional);
+ UINT srcSubRes = D3D11CalcSubresource(UINT(u.desc.sourceLevel()), srcIs3D ? 0u : UINT(u.desc.sourceLayer()), srcD->mipLevelCount);
+ UINT dstSubRes = D3D11CalcSubresource(UINT(u.desc.destinationLevel()), dstIs3D ? 0u : UINT(u.desc.destinationLayer()), dstD->mipLevelCount);
const QPoint dp = u.desc.destinationTopLeft();
const QSize mipSize = q->sizeForMipLevel(u.desc.sourceLevel(), srcD->m_pixelSize);
const QSize copySize = u.desc.pixelSize().isEmpty() ? mipSize : u.desc.pixelSize();
@@ -1527,18 +1536,19 @@ void QRhiD3D11::enqueueResourceUpdates(QRhiCommandBuffer *cb, QRhiResourceUpdate
D3D11_BOX srcBox;
srcBox.left = UINT(sp.x());
srcBox.top = UINT(sp.y());
- srcBox.front = 0;
+ srcBox.front = srcIs3D ? UINT(u.desc.sourceLayer()) : 0u;
// back, right, bottom are exclusive
srcBox.right = srcBox.left + UINT(copySize.width());
srcBox.bottom = srcBox.top + UINT(copySize.height());
- srcBox.back = 1;
+ srcBox.back = srcBox.front + 1;
QD3D11CommandBuffer::Command &cmd(cbD->commands.get());
cmd.cmd = QD3D11CommandBuffer::Command::CopySubRes;
- cmd.args.copySubRes.dst = dstD->tex;
+ cmd.args.copySubRes.dst = dstD->textureResource();
cmd.args.copySubRes.dstSubRes = dstSubRes;
cmd.args.copySubRes.dstX = UINT(dp.x());
cmd.args.copySubRes.dstY = UINT(dp.y());
- cmd.args.copySubRes.src = srcD->tex;
+ cmd.args.copySubRes.dstZ = dstIs3D ? UINT(u.desc.destinationLayer()) : 0u;
+ cmd.args.copySubRes.src = srcD->textureResource();
cmd.args.copySubRes.srcSubRes = srcSubRes;
cmd.args.copySubRes.hasSrcBox = true;
cmd.args.copySubRes.srcBox = srcBox;
@@ -1560,7 +1570,13 @@ void QRhiD3D11::enqueueResourceUpdates(QRhiCommandBuffer *cb, QRhiResourceUpdate
qWarning("Multisample texture cannot be read back");
continue;
}
- src = texD->tex;
+ // No support for reading back 3D, not because it is not
+ // possible technically, but we need to draw the line somewhere.
+ if (texD->m_flags.testFlag(QRhiTexture::ThreeDimensional)) {
+ qWarning("3D texture cannot be read back");
+ continue;
+ }
+ src = texD->textureResource();
dxgiFormat = texD->dxgiFormat;
pixelSize = q->sizeForMipLevel(u.rb.level(), texD->m_pixelSize);
format = texD->m_format;
@@ -1616,6 +1632,7 @@ void QRhiD3D11::enqueueResourceUpdates(QRhiCommandBuffer *cb, QRhiResourceUpdate
cmd.args.copySubRes.dstSubRes = 0;
cmd.args.copySubRes.dstX = 0;
cmd.args.copySubRes.dstY = 0;
+ cmd.args.copySubRes.dstZ = 0;
cmd.args.copySubRes.src = src;
cmd.args.copySubRes.srcSubRes = subres;
cmd.args.copySubRes.hasSrcBox = false;
@@ -1791,12 +1808,12 @@ void QRhiD3D11::endPass(QRhiCommandBuffer *cb, QRhiResourceUpdateBatch *resource
Q_ASSERT(srcTexD || srcRbD);
QD3D11CommandBuffer::Command &cmd(cbD->commands.get());
cmd.cmd = QD3D11CommandBuffer::Command::ResolveSubRes;
- cmd.args.resolveSubRes.dst = dstTexD->tex;
+ cmd.args.resolveSubRes.dst = dstTexD->textureResource();
cmd.args.resolveSubRes.dstSubRes = D3D11CalcSubresource(UINT(colorAtt.resolveLevel()),
UINT(colorAtt.resolveLayer()),
dstTexD->mipLevelCount);
if (srcTexD) {
- cmd.args.resolveSubRes.src = srcTexD->tex;
+ cmd.args.resolveSubRes.src = srcTexD->textureResource();
if (srcTexD->dxgiFormat != dstTexD->dxgiFormat) {
qWarning("Resolve source (%d) and destination (%d) formats do not match",
int(srcTexD->dxgiFormat), int(dstTexD->dxgiFormat));
@@ -2582,7 +2599,7 @@ void QRhiD3D11::executeCommandBuffer(QD3D11CommandBuffer *cbD, QD3D11SwapChain *
break;
case QD3D11CommandBuffer::Command::CopySubRes:
context->CopySubresourceRegion(cmd.args.copySubRes.dst, cmd.args.copySubRes.dstSubRes,
- cmd.args.copySubRes.dstX, cmd.args.copySubRes.dstY, 0,
+ cmd.args.copySubRes.dstX, cmd.args.copySubRes.dstY, cmd.args.copySubRes.dstZ,
cmd.args.copySubRes.src, cmd.args.copySubRes.srcSubRes,
cmd.args.copySubRes.hasSrcBox ? &cmd.args.copySubRes.srcBox : nullptr);
break;
@@ -2885,11 +2902,11 @@ QRhiTexture::Format QD3D11RenderBuffer::backingFormat() const
return m_type == Color ? QRhiTexture::RGBA8 : QRhiTexture::UnknownFormat;
}
-QD3D11Texture::QD3D11Texture(QRhiImplementation *rhi, Format format, const QSize &pixelSize,
+QD3D11Texture::QD3D11Texture(QRhiImplementation *rhi, Format format, const QSize &pixelSize, int depth,
int sampleCount, Flags flags)
- : QRhiTexture(rhi, format, pixelSize, sampleCount, flags)
+ : QRhiTexture(rhi, format, pixelSize, depth, sampleCount, flags)
{
- for (int i = 0; i < QRhi::MAX_LEVELS; ++i)
+ for (int i = 0; i < QRhi::MAX_MIP_LEVELS; ++i)
perLevelViews[i] = nullptr;
}
@@ -2900,7 +2917,7 @@ QD3D11Texture::~QD3D11Texture()
void QD3D11Texture::destroy()
{
- if (!tex)
+ if (!tex && !tex3D)
return;
if (srv) {
@@ -2908,17 +2925,22 @@ void QD3D11Texture::destroy()
srv = nullptr;
}
- for (int i = 0; i < QRhi::MAX_LEVELS; ++i) {
+ for (int i = 0; i < QRhi::MAX_MIP_LEVELS; ++i) {
if (perLevelViews[i]) {
perLevelViews[i]->Release();
perLevelViews[i] = nullptr;
}
}
- if (owns)
- tex->Release();
+ if (owns) {
+ if (tex)
+ tex->Release();
+ if (tex3D)
+ tex3D->Release();
+ }
tex = nullptr;
+ tex3D = nullptr;
QRHI_RES_RHI(QRhiD3D11);
QRHI_PROF;
@@ -2962,12 +2984,13 @@ static inline DXGI_FORMAT toD3DDepthTextureDSVFormat(QRhiTexture::Format format)
bool QD3D11Texture::prepareCreate(QSize *adjustedSize)
{
- if (tex)
+ if (tex || tex3D)
destroy();
const QSize size = m_pixelSize.isEmpty() ? QSize(1, 1) : m_pixelSize;
const bool isDepth = isDepthTextureFormat(m_format);
const bool isCube = m_flags.testFlag(CubeMap);
+ const bool is3D = m_flags.testFlag(ThreeDimensional);
const bool hasMipMaps = m_flags.testFlag(MipMapped);
QRHI_RES_RHI(QRhiD3D11);
@@ -2979,6 +3002,10 @@ bool QD3D11Texture::prepareCreate(QSize *adjustedSize)
qWarning("Cubemap texture cannot be multisample");
return false;
}
+ if (is3D) {
+ qWarning("3D texture cannot be multisample");
+ return false;
+ }
if (hasMipMaps) {
qWarning("Multisample texture cannot have mipmaps");
return false;
@@ -2988,6 +3015,15 @@ bool QD3D11Texture::prepareCreate(QSize *adjustedSize)
qWarning("Depth texture cannot have mipmaps");
return false;
}
+ if (isCube && is3D) {
+ qWarning("Texture cannot be both cube 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;
+ }
if (adjustedSize)
*adjustedSize = size;
@@ -3000,6 +3036,7 @@ bool QD3D11Texture::finishCreate()
QRHI_RES_RHI(QRhiD3D11);
const bool isDepth = isDepthTextureFormat(m_format);
const bool isCube = m_flags.testFlag(CubeMap);
+ const bool is3D = m_flags.testFlag(ThreeDimensional);
D3D11_SHADER_RESOURCE_VIEW_DESC srvDesc;
memset(&srvDesc, 0, sizeof(srvDesc));
@@ -3010,13 +3047,16 @@ bool QD3D11Texture::finishCreate()
} else {
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;
}
}
- HRESULT hr = rhiD->dev->CreateShaderResourceView(tex, &srvDesc, &srv);
+ HRESULT hr = rhiD->dev->CreateShaderResourceView(textureResource(), &srvDesc, &srv);
if (FAILED(hr)) {
qWarning("Failed to create srv: %s", qPrintable(comErrorMessage(hr)));
return false;
@@ -3034,6 +3074,7 @@ bool QD3D11Texture::create()
const bool isDepth = isDepthTextureFormat(m_format);
const bool isCube = m_flags.testFlag(CubeMap);
+ const bool is3D = m_flags.testFlag(ThreeDimensional);
uint bindFlags = D3D11_BIND_SHADER_RESOURCE;
uint miscFlags = isCube ? D3D11_RESOURCE_MISC_TEXTURECUBE : 0;
@@ -3054,31 +3095,51 @@ bool QD3D11Texture::create()
if (m_flags.testFlag(UsedWithLoadStore))
bindFlags |= D3D11_BIND_UNORDERED_ACCESS;
- D3D11_TEXTURE2D_DESC desc;
- memset(&desc, 0, sizeof(desc));
- desc.Width = UINT(size.width());
- desc.Height = UINT(size.height());
- desc.MipLevels = mipLevelCount;
- desc.ArraySize = isCube ? 6 : 1;
- desc.Format = dxgiFormat;
- desc.SampleDesc = sampleDesc;
- desc.Usage = D3D11_USAGE_DEFAULT;
- desc.BindFlags = bindFlags;
- desc.MiscFlags = miscFlags;
-
QRHI_RES_RHI(QRhiD3D11);
- HRESULT hr = rhiD->dev->CreateTexture2D(&desc, nullptr, &tex);
- if (FAILED(hr)) {
- qWarning("Failed to create texture: %s", qPrintable(comErrorMessage(hr)));
- return false;
+ if (!is3D) {
+ D3D11_TEXTURE2D_DESC desc;
+ memset(&desc, 0, sizeof(desc));
+ desc.Width = UINT(size.width());
+ desc.Height = UINT(size.height());
+ desc.MipLevels = mipLevelCount;
+ desc.ArraySize = isCube ? 6 : 1;
+ desc.Format = dxgiFormat;
+ desc.SampleDesc = sampleDesc;
+ desc.Usage = D3D11_USAGE_DEFAULT;
+ desc.BindFlags = bindFlags;
+ desc.MiscFlags = miscFlags;
+
+ HRESULT hr = rhiD->dev->CreateTexture2D(&desc, nullptr, &tex);
+ if (FAILED(hr)) {
+ qWarning("Failed to create 2D texture: %s", qPrintable(comErrorMessage(hr)));
+ return false;
+ }
+ if (!m_objectName.isEmpty())
+ tex->SetPrivateData(WKPDID_D3DDebugObjectName, UINT(m_objectName.size()), m_objectName.constData());
+ } else {
+ D3D11_TEXTURE3D_DESC desc;
+ memset(&desc, 0, sizeof(desc));
+ desc.Width = UINT(size.width());
+ desc.Height = UINT(size.height());
+ desc.Depth = UINT(m_depth);
+ desc.MipLevels = mipLevelCount;
+ desc.Format = dxgiFormat;
+ desc.Usage = D3D11_USAGE_DEFAULT;
+ desc.BindFlags = bindFlags;
+ desc.MiscFlags = miscFlags;
+
+ HRESULT hr = rhiD->dev->CreateTexture3D(&desc, nullptr, &tex3D);
+ if (FAILED(hr)) {
+ qWarning("Failed to create 3D texture: %s", qPrintable(comErrorMessage(hr)));
+ return false;
+ }
+ if (!m_objectName.isEmpty())
+ tex3D->SetPrivateData(WKPDID_D3DDebugObjectName, UINT(m_objectName.size()), m_objectName.constData());
}
if (!finishCreate())
return false;
- if (!m_objectName.isEmpty())
- tex->SetPrivateData(WKPDID_D3DDebugObjectName, UINT(m_objectName.size()), m_objectName.constData());
-
QRHI_PROF;
QRHI_PROF_F(newTexture(this, true, int(mipLevelCount), isCube ? 6 : 1, int(sampleDesc.Count)));
@@ -3089,14 +3150,16 @@ bool QD3D11Texture::create()
bool QD3D11Texture::createFrom(QRhiTexture::NativeTexture src)
{
- ID3D11Texture2D *srcTex = reinterpret_cast<ID3D11Texture2D *>(src.object);
- if (srcTex == nullptr)
+ if (!src.object)
return false;
if (!prepareCreate())
return false;
- tex = srcTex;
+ if (m_flags.testFlag(ThreeDimensional))
+ tex3D = reinterpret_cast<ID3D11Texture3D *>(src.object);
+ else
+ tex = reinterpret_cast<ID3D11Texture2D *>(src.object);
if (!finishCreate())
return false;
@@ -3112,7 +3175,7 @@ bool QD3D11Texture::createFrom(QRhiTexture::NativeTexture src)
QRhiTexture::NativeTexture QD3D11Texture::nativeTexture()
{
- return {quint64(tex), 0};
+ return { quint64(textureResource()), 0 };
}
ID3D11UnorderedAccessView *QD3D11Texture::unorderedAccessViewForLevel(int level)
@@ -3121,6 +3184,7 @@ ID3D11UnorderedAccessView *QD3D11Texture::unorderedAccessViewForLevel(int level)
return perLevelViews[level];
const bool isCube = m_flags.testFlag(CubeMap);
+ const bool is3D = m_flags.testFlag(ThreeDimensional);
D3D11_UNORDERED_ACCESS_VIEW_DESC desc;
memset(&desc, 0, sizeof(desc));
desc.Format = dxgiFormat;
@@ -3129,6 +3193,9 @@ ID3D11UnorderedAccessView *QD3D11Texture::unorderedAccessViewForLevel(int level)
desc.Texture2DArray.MipSlice = UINT(level);
desc.Texture2DArray.FirstArraySlice = 0;
desc.Texture2DArray.ArraySize = 6;
+ } else if (is3D) {
+ desc.ViewDimension = D3D11_UAV_DIMENSION_TEXTURE3D;
+ desc.Texture3D.MipSlice = UINT(level);
} else {
desc.ViewDimension = D3D11_UAV_DIMENSION_TEXTURE2D;
desc.Texture2D.MipSlice = UINT(level);
@@ -3136,7 +3203,7 @@ ID3D11UnorderedAccessView *QD3D11Texture::unorderedAccessViewForLevel(int level)
QRHI_RES_RHI(QRhiD3D11);
ID3D11UnorderedAccessView *uav = nullptr;
- HRESULT hr = rhiD->dev->CreateUnorderedAccessView(tex, &desc, &uav);
+ HRESULT hr = rhiD->dev->CreateUnorderedAccessView(textureResource(), &desc, &uav);
if (FAILED(hr)) {
qWarning("Failed to create UAV: %s", qPrintable(comErrorMessage(hr)));
return nullptr;
@@ -3404,6 +3471,11 @@ 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::ThreeDimensional)) {
+ rtvDesc.ViewDimension = D3D11_RTV_DIMENSION_TEXTURE3D;
+ rtvDesc.Texture3D.MipSlice = UINT(colorAtt.level());
+ rtvDesc.Texture3D.FirstWSlice = UINT(colorAtt.layer());
+ rtvDesc.Texture3D.WSize = 1;
} else {
if (texD->sampleDesc.Count > 1) {
rtvDesc.ViewDimension = D3D11_RTV_DIMENSION_TEXTURE2DMS;
@@ -3412,7 +3484,7 @@ bool QD3D11TextureRenderTarget::create()
rtvDesc.Texture2D.MipSlice = UINT(colorAtt.level());
}
}
- HRESULT hr = rhiD->dev->CreateRenderTargetView(texD->tex, &rtvDesc, &rtv[attIndex]);
+ HRESULT hr = rhiD->dev->CreateRenderTargetView(texD->textureResource(), &rtvDesc, &rtv[attIndex]);
if (FAILED(hr)) {
qWarning("Failed to create rtv: %s", qPrintable(comErrorMessage(hr)));
return false;
diff --git a/src/gui/rhi/qrhid3d11_p_p.h b/src/gui/rhi/qrhid3d11_p_p.h
index 82cbe5114c..0695259612 100644
--- a/src/gui/rhi/qrhid3d11_p_p.h
+++ b/src/gui/rhi/qrhid3d11_p_p.h
@@ -101,7 +101,7 @@ struct QD3D11RenderBuffer : public QRhiRenderBuffer
struct QD3D11Texture : public QRhiTexture
{
- QD3D11Texture(QRhiImplementation *rhi, Format format, const QSize &pixelSize,
+ QD3D11Texture(QRhiImplementation *rhi, Format format, const QSize &pixelSize, int depth,
int sampleCount, Flags flags);
~QD3D11Texture();
void destroy() override;
@@ -112,14 +112,21 @@ struct QD3D11Texture : public QRhiTexture
bool prepareCreate(QSize *adjustedSize = nullptr);
bool finishCreate();
ID3D11UnorderedAccessView *unorderedAccessViewForLevel(int level);
+ ID3D11Resource *textureResource() const
+ {
+ if (tex)
+ return tex;
+ return tex3D;
+ }
ID3D11Texture2D *tex = nullptr;
+ ID3D11Texture3D *tex3D = nullptr;
bool owns = true;
ID3D11ShaderResourceView *srv = nullptr;
DXGI_FORMAT dxgiFormat;
uint mipLevelCount = 0;
DXGI_SAMPLE_DESC sampleDesc;
- ID3D11UnorderedAccessView *perLevelViews[QRhi::MAX_LEVELS];
+ ID3D11UnorderedAccessView *perLevelViews[QRhi::MAX_MIP_LEVELS];
uint generation = 0;
friend class QRhiD3D11;
};
@@ -439,6 +446,7 @@ struct QD3D11CommandBuffer : public QRhiCommandBuffer
UINT dstSubRes;
UINT dstX;
UINT dstY;
+ UINT dstZ;
ID3D11Resource *src;
UINT srcSubRes;
bool hasSrcBox;
@@ -596,6 +604,7 @@ public:
QRhiTexture::Format backingFormatHint) override;
QRhiTexture *createTexture(QRhiTexture::Format format,
const QSize &pixelSize,
+ int depth,
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 8a5361b29a..5f8ef08e31 100644
--- a/src/gui/rhi/qrhigles2.cpp
+++ b/src/gui/rhi/qrhigles2.cpp
@@ -348,6 +348,14 @@ QT_BEGIN_NAMESPACE
#define GL_UNPACK_ROW_LENGTH 0x0CF2
#endif
+#ifndef GL_TEXTURE_3D
+#define GL_TEXTURE_3D 0x806F
+#endif
+
+#ifndef GL_TEXTURE_WRAP_R
+#define GL_TEXTURE_WRAP_R 0x8072
+#endif
+
/*!
Constructs a new QRhiGles2InitParams.
@@ -621,6 +629,8 @@ bool QRhiGles2::create(QRhi::Flags flags)
caps.programBinary = false;
}
+ caps.texture3D = caps.ctxMajor >= 3; // 3.0
+
if (!caps.gles) {
f->glEnable(GL_VERTEX_PROGRAM_POINT_SIZE);
f->glEnable(GL_POINT_SPRITE);
@@ -1013,6 +1023,10 @@ bool QRhiGles2::isFeatureSupported(QRhi::Feature feature) const
return !caps.gles || caps.ctxMajor >= 3;
case QRhi::RenderBufferImport:
return true;
+ case QRhi::ThreeDimensionalTextures:
+ return caps.texture3D;
+ case QRhi::RenderTo3DTextureSlice:
+ return caps.texture3D;
default:
Q_UNREACHABLE();
return false;
@@ -1229,10 +1243,11 @@ QRhiRenderBuffer *QRhiGles2::createRenderBuffer(QRhiRenderBuffer::Type type, con
return new QGles2RenderBuffer(this, type, pixelSize, sampleCount, flags, backingFormatHint);
}
-QRhiTexture *QRhiGles2::createTexture(QRhiTexture::Format format, const QSize &pixelSize,
+QRhiTexture *QRhiGles2::createTexture(QRhiTexture::Format format,
+ const QSize &pixelSize, int depth,
int sampleCount, QRhiTexture::Flags flags)
{
- return new QGles2Texture(this, format, pixelSize, sampleCount, flags);
+ return new QGles2Texture(this, format, pixelSize, depth, sampleCount, flags);
}
QRhiSampler *QRhiGles2::createSampler(QRhiSampler::Filter magFilter, QRhiSampler::Filter minFilter,
@@ -1812,6 +1827,7 @@ void QRhiGles2::enqueueSubresUpload(QGles2Texture *texD, QGles2CommandBuffer *cb
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.w = size.width();
cmd.args.subImage.h = size.height();
cmd.args.subImage.glformat = texD->glformat;
@@ -1820,10 +1836,19 @@ void QRhiGles2::enqueueSubresUpload(QGles2Texture *texD, QGles2CommandBuffer *cb
cmd.args.subImage.rowLength = 0;
cmd.args.subImage.data = cbD->retainImage(img);
} else if (!rawData.isEmpty() && isCompressed) {
- if (!texD->compressedAtlasBuilt && (texD->flags() & QRhiTexture::UsedAsCompressedAtlas)) {
- // Create on first upload since glCompressedTexImage2D cannot take nullptr data
+ const bool is3D = texD->flags().testFlag(QRhiTexture::ThreeDimensional);
+ if ((texD->flags().testFlag(QRhiTexture::UsedAsCompressedAtlas) || is3D)
+ && !texD->zeroInitialized)
+ {
+ // Create on first upload since glCompressedTexImage2D cannot take
+ // nullptr data. We have a rule in the QRhi docs that the first
+ // upload for a compressed texture must cover the entire image, but
+ // that is clearly not ideal when building a texture atlas, or when
+ // having a 3D texture with per-slice data.
quint32 byteSize = 0;
compressedFormatInfo(texD->m_format, texD->m_pixelSize, nullptr, &byteSize, nullptr);
+ if (is3D)
+ byteSize *= texD->m_depth;
QByteArray zeroBuf(byteSize, 0);
QGles2CommandBuffer::Command &cmd(cbD->commands.get());
cmd.cmd = QGles2CommandBuffer::Command::CompressedImage;
@@ -1834,14 +1859,15 @@ void QRhiGles2::enqueueSubresUpload(QGles2Texture *texD, QGles2CommandBuffer *cb
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.size = byteSize;
cmd.args.compressedImage.data = cbD->retainData(zeroBuf);
- texD->compressedAtlasBuilt = true;
+ texD->zeroInitialized = true;
}
const QSize size = subresDesc.sourceSize().isEmpty() ? q->sizeForMipLevel(level, texD->m_pixelSize)
: subresDesc.sourceSize();
- if (texD->specified || texD->compressedAtlasBuilt) {
+ if (texD->specified || texD->zeroInitialized) {
QGles2CommandBuffer::Command &cmd(cbD->commands.get());
cmd.cmd = QGles2CommandBuffer::Command::CompressedSubImage;
cmd.args.compressedSubImage.target = texD->target;
@@ -1850,6 +1876,7 @@ void QRhiGles2::enqueueSubresUpload(QGles2Texture *texD, QGles2CommandBuffer *cb
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.w = size.width();
cmd.args.compressedSubImage.h = size.height();
cmd.args.compressedSubImage.glintformat = texD->glintformat;
@@ -1865,6 +1892,7 @@ void QRhiGles2::enqueueSubresUpload(QGles2Texture *texD, QGles2CommandBuffer *cb
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.size = rawData.size();
cmd.args.compressedImage.data = cbD->retainData(rawData);
}
@@ -1882,6 +1910,7 @@ void QRhiGles2::enqueueSubresUpload(QGles2Texture *texD, QGles2CommandBuffer *cb
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.w = size.width();
cmd.args.subImage.h = size.height();
cmd.args.subImage.glformat = texD->glformat;
@@ -1961,8 +1990,8 @@ void QRhiGles2::enqueueResourceUpdates(QRhiCommandBuffer *cb, QRhiResourceUpdate
const QRhiResourceUpdateBatchPrivate::TextureOp &u(ud->textureOps[opIdx]);
if (u.type == QRhiResourceUpdateBatchPrivate::TextureOp::Upload) {
QGles2Texture *texD = QRHI_RES(QGles2Texture, u.dst);
- for (int layer = 0; layer < QRhi::MAX_LAYERS; ++layer) {
- for (int level = 0; level < QRhi::MAX_LEVELS; ++level) {
+ for (int layer = 0, maxLayer = u.subresDesc.count(); layer < maxLayer; ++layer) {
+ for (int level = 0; level < QRhi::MAX_MIP_LEVELS; ++level) {
for (const QRhiTextureSubresourceUploadDescription &subresDesc : qAsConst(u.subresDesc[layer][level]))
enqueueSubresUpload(texD, cbD, layer, level, subresDesc);
}
@@ -1990,18 +2019,21 @@ void QRhiGles2::enqueueResourceUpdates(QRhiCommandBuffer *cb, QRhiResourceUpdate
QGles2CommandBuffer::Command &cmd(cbD->commands.get());
cmd.cmd = QGles2CommandBuffer::Command::CopyTex;
+ cmd.args.copyTex.srcTarget = srcD->target;
cmd.args.copyTex.srcFaceTarget = srcFaceTargetBase + 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.dstTarget = dstD->target;
- cmd.args.copyTex.dstTexture = dstD->texture;
cmd.args.copyTex.dstFaceTarget = dstFaceTargetBase + 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.w = copySize.width();
cmd.args.copyTex.h = copySize.height();
@@ -2734,13 +2766,25 @@ void QRhiGles2::executeCommandBuffer(QRhiCommandBuffer *cb)
GLuint fbo;
f->glGenFramebuffers(1, &fbo);
f->glBindFramebuffer(GL_FRAMEBUFFER, fbo);
- f->glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
- cmd.args.copyTex.srcFaceTarget, cmd.args.copyTex.srcTexture, cmd.args.copyTex.srcLevel);
+ if (cmd.args.copyTex.srcTarget == GL_TEXTURE_3D) {
+ f->glFramebufferTextureLayer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, cmd.args.copyTex.srcTexture,
+ cmd.args.copyTex.srcLevel, cmd.args.copyTex.srcZ);
+ } else {
+ f->glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
+ cmd.args.copyTex.srcFaceTarget, cmd.args.copyTex.srcTexture, cmd.args.copyTex.srcLevel);
+ }
f->glBindTexture(cmd.args.copyTex.dstTarget, cmd.args.copyTex.dstTexture);
- f->glCopyTexSubImage2D(cmd.args.copyTex.dstFaceTarget, cmd.args.copyTex.dstLevel,
- cmd.args.copyTex.dstX, cmd.args.copyTex.dstY,
- cmd.args.copyTex.srcX, cmd.args.copyTex.srcY,
- cmd.args.copyTex.w, cmd.args.copyTex.h);
+ if (cmd.args.copyTex.dstTarget == GL_TEXTURE_3D) {
+ 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,
+ cmd.args.copyTex.w, cmd.args.copyTex.h);
+ } else {
+ f->glCopyTexSubImage2D(cmd.args.copyTex.dstFaceTarget, cmd.args.copyTex.dstLevel,
+ cmd.args.copyTex.dstX, cmd.args.copyTex.dstY,
+ cmd.args.copyTex.srcX, cmd.args.copyTex.srcY,
+ cmd.args.copyTex.w, cmd.args.copyTex.h);
+ }
f->glBindFramebuffer(GL_FRAMEBUFFER, ctx->defaultFramebufferObject());
f->glDeleteFramebuffers(1, &fbo);
}
@@ -2825,11 +2869,19 @@ 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);
- f->glTexSubImage2D(cmd.args.subImage.faceTarget, cmd.args.subImage.level,
- cmd.args.subImage.dx, cmd.args.subImage.dy,
- cmd.args.subImage.w, cmd.args.subImage.h,
- cmd.args.subImage.glformat, cmd.args.subImage.gltype,
- cmd.args.subImage.data);
+ if (cmd.args.subImage.target == GL_TEXTURE_3D) {
+ 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,
+ cmd.args.subImage.glformat, cmd.args.subImage.gltype,
+ cmd.args.subImage.data);
+ } else {
+ f->glTexSubImage2D(cmd.args.subImage.faceTarget, cmd.args.subImage.level,
+ cmd.args.subImage.dx, cmd.args.subImage.dy,
+ cmd.args.subImage.w, cmd.args.subImage.h,
+ cmd.args.subImage.glformat, cmd.args.subImage.gltype,
+ cmd.args.subImage.data);
+ }
if (cmd.args.subImage.rowStartAlign != 4)
f->glPixelStorei(GL_UNPACK_ALIGNMENT, 4);
if (cmd.args.subImage.rowLength != 0)
@@ -2837,18 +2889,33 @@ void QRhiGles2::executeCommandBuffer(QRhiCommandBuffer *cb)
break;
case QGles2CommandBuffer::Command::CompressedImage:
f->glBindTexture(cmd.args.compressedImage.target, cmd.args.compressedImage.texture);
- f->glCompressedTexImage2D(cmd.args.compressedImage.faceTarget, cmd.args.compressedImage.level,
- cmd.args.compressedImage.glintformat,
- cmd.args.compressedImage.w, cmd.args.compressedImage.h, 0,
- cmd.args.compressedImage.size, cmd.args.compressedImage.data);
+ if (cmd.args.compressedImage.target == GL_TEXTURE_3D) {
+ 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,
+ 0, cmd.args.compressedImage.size, cmd.args.compressedImage.data);
+ } else {
+ f->glCompressedTexImage2D(cmd.args.compressedImage.faceTarget, cmd.args.compressedImage.level,
+ cmd.args.compressedImage.glintformat,
+ cmd.args.compressedImage.w, cmd.args.compressedImage.h,
+ 0, cmd.args.compressedImage.size, cmd.args.compressedImage.data);
+ }
break;
case QGles2CommandBuffer::Command::CompressedSubImage:
f->glBindTexture(cmd.args.compressedSubImage.target, cmd.args.compressedSubImage.texture);
- f->glCompressedTexSubImage2D(cmd.args.compressedSubImage.faceTarget, cmd.args.compressedSubImage.level,
- cmd.args.compressedSubImage.dx, cmd.args.compressedSubImage.dy,
- cmd.args.compressedSubImage.w, cmd.args.compressedSubImage.h,
- cmd.args.compressedSubImage.glintformat,
- cmd.args.compressedSubImage.size, cmd.args.compressedSubImage.data);
+ if (cmd.args.compressedSubImage.target == GL_TEXTURE_3D) {
+ 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,
+ cmd.args.compressedSubImage.glintformat,
+ cmd.args.compressedSubImage.size, cmd.args.compressedSubImage.data);
+ } else {
+ f->glCompressedTexSubImage2D(cmd.args.compressedSubImage.faceTarget, cmd.args.compressedSubImage.level,
+ cmd.args.compressedSubImage.dx, cmd.args.compressedSubImage.dy,
+ cmd.args.compressedSubImage.w, cmd.args.compressedSubImage.h,
+ cmd.args.compressedSubImage.glintformat,
+ cmd.args.compressedSubImage.size, cmd.args.compressedSubImage.data);
+ }
break;
case QGles2CommandBuffer::Command::BlitFromRenderbuffer:
{
@@ -3399,6 +3466,8 @@ void QRhiGles2::bindShaderResources(QGles2CommandBuffer *cbD,
f->glTexParameteri(texD->target, GL_TEXTURE_MAG_FILTER, GLint(samplerD->d.glmagfilter));
f->glTexParameteri(texD->target, GL_TEXTURE_WRAP_S, GLint(samplerD->d.glwraps));
f->glTexParameteri(texD->target, GL_TEXTURE_WRAP_T, GLint(samplerD->d.glwrapt));
+ if (caps.texture3D)
+ f->glTexParameteri(texD->target, GL_TEXTURE_WRAP_R, GLint(samplerD->d.glwrapr));
if (caps.textureCompareMode) {
if (samplerD->d.gltexcomparefunc != GL_NEVER) {
f->glTexParameteri(texD->target, GL_TEXTURE_COMPARE_MODE, GL_COMPARE_REF_TO_TEXTURE);
@@ -4418,9 +4487,9 @@ QRhiTexture::Format QGles2RenderBuffer::backingFormat() const
return m_type == Color ? QRhiTexture::RGBA8 : QRhiTexture::UnknownFormat;
}
-QGles2Texture::QGles2Texture(QRhiImplementation *rhi, Format format, const QSize &pixelSize,
+QGles2Texture::QGles2Texture(QRhiImplementation *rhi, Format format, const QSize &pixelSize, int depth,
int sampleCount, Flags flags)
- : QRhiTexture(rhi, format, pixelSize, sampleCount, flags)
+ : QRhiTexture(rhi, format, pixelSize, depth, sampleCount, flags)
{
}
@@ -4441,7 +4510,7 @@ void QGles2Texture::destroy()
texture = 0;
specified = false;
- compressedAtlasBuilt = false;
+ zeroInitialized = false;
QRHI_RES_RHI(QRhiGles2);
if (owns)
@@ -4463,11 +4532,26 @@ 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 is3D = m_flags.testFlag(ThreeDimensional);
const bool hasMipMaps = m_flags.testFlag(MipMapped);
const bool isCompressed = rhiD->isCompressedFormat(m_format);
+ if (is3D && !rhiD->caps.texture3D) {
+ qWarning("3D textures are not supported");
+ return false;
+ }
+ if (isCube && is3D) {
+ qWarning("Texture cannot be both cube 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;
+ }
+
target = isCube ? GL_TEXTURE_CUBE_MAP
- : m_sampleCount > 1 ? GL_TEXTURE_2D_MULTISAMPLE : GL_TEXTURE_2D;
+ : m_sampleCount > 1 ? GL_TEXTURE_2D_MULTISAMPLE : (is3D ? GL_TEXTURE_3D : GL_TEXTURE_2D);
if (m_flags.testFlag(ExternalOES))
target = GL_TEXTURE_EXTERNAL_OES;
@@ -4511,12 +4595,24 @@ bool QGles2Texture::create()
rhiD->f->glGenTextures(1, &texture);
const bool isCube = m_flags.testFlag(CubeMap);
+ 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 (hasMipMaps || isCube) {
+ if (is3D) {
+ 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,
+ 0, glformat, gltype, nullptr);
+ }
+ } else {
+ rhiD->f->glTexImage3D(target, 0, GLint(glintformat), size.width(), size.height(), m_depth,
+ 0, glformat, gltype, nullptr);
+ }
+ } else if (hasMipMaps || isCube) {
const GLenum faceTargetBase = isCube ? GL_TEXTURE_CUBE_MAP_POSITIVE_X : target;
for (int layer = 0, layerCount = isCube ? 6 : 1; layer != layerCount; ++layer) {
for (int level = 0; level != mipLevelCount; ++level) {
@@ -4534,7 +4630,10 @@ bool QGles2Texture::create()
// Must be specified with immutable storage functions otherwise
// bindImageTexture may fail. Also, the internal format must be a
// sized format here.
- rhiD->f->glTexStorage2D(target, mipLevelCount, glsizedintformat, size.width(), size.height());
+ if (is3D)
+ rhiD->f->glTexStorage3D(target, mipLevelCount, glsizedintformat, size.width(), size.height(), m_depth);
+ else
+ rhiD->f->glTexStorage2D(target, mipLevelCount, glsizedintformat, size.width(), size.height());
}
specified = true;
} else {
@@ -4565,7 +4664,7 @@ bool QGles2Texture::createFrom(QRhiTexture::NativeTexture src)
texture = textureId;
specified = true;
- compressedAtlasBuilt = true;
+ zeroInitialized = true;
QRHI_RES_RHI(QRhiGles2);
QRHI_PROF;
@@ -4605,6 +4704,7 @@ bool QGles2Sampler::create()
d.glmagfilter = toGlMagFilter(m_magFilter);
d.glwraps = toGlWrapMode(m_addressU);
d.glwrapt = toGlWrapMode(m_addressV);
+ d.glwrapr = toGlWrapMode(m_addressW);
d.gltexcomparefunc = toGlTextureCompareFunc(m_compareOp);
generation += 1;
@@ -4744,9 +4844,14 @@ bool QGles2TextureRenderTarget::create()
if (texture) {
QGles2Texture *texD = QRHI_RES(QGles2Texture, texture);
Q_ASSERT(texD->texture && texD->specified);
- const GLenum faceTargetBase = texD->flags().testFlag(QRhiTexture::CubeMap) ? GL_TEXTURE_CUBE_MAP_POSITIVE_X : texD->target;
- rhiD->f->glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0 + uint(attIndex), faceTargetBase + uint(colorAtt.layer()),
- texD->texture, colorAtt.level());
+ if (texD->flags().testFlag(QRhiTexture::ThreeDimensional)) {
+ rhiD->f->glFramebufferTextureLayer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0 + uint(attIndex), texD->texture,
+ colorAtt.level(), colorAtt.layer());
+ } else {
+ const GLenum faceTargetBase = texD->flags().testFlag(QRhiTexture::CubeMap) ? GL_TEXTURE_CUBE_MAP_POSITIVE_X : texD->target;
+ rhiD->f->glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0 + uint(attIndex), faceTargetBase + uint(colorAtt.layer()),
+ texD->texture, colorAtt.level());
+ }
if (attIndex == 0) {
d.pixelSize = rhiD->q->sizeForMipLevel(colorAtt.level(), texD->pixelSize());
d.sampleCount = 1;
diff --git a/src/gui/rhi/qrhigles2_p_p.h b/src/gui/rhi/qrhigles2_p_p.h
index cb6cd241ff..b4179c0ce8 100644
--- a/src/gui/rhi/qrhigles2_p_p.h
+++ b/src/gui/rhi/qrhigles2_p_p.h
@@ -116,6 +116,7 @@ struct QGles2SamplerData
GLenum glmagfilter = 0;
GLenum glwraps = 0;
GLenum glwrapt = 0;
+ GLenum glwrapr = 0;
GLenum gltexcomparefunc = 0;
};
@@ -125,6 +126,7 @@ inline bool operator==(const QGles2SamplerData &a, const QGles2SamplerData &b)
&& a.glmagfilter == b.glmagfilter
&& a.glwraps == b.glwraps
&& a.glwrapt == b.glwrapt
+ && a.glwrapr == b.glwrapr
&& a.gltexcomparefunc == b.gltexcomparefunc;
}
@@ -135,7 +137,7 @@ inline bool operator!=(const QGles2SamplerData &a, const QGles2SamplerData &b)
struct QGles2Texture : public QRhiTexture
{
- QGles2Texture(QRhiImplementation *rhi, Format format, const QSize &pixelSize,
+ QGles2Texture(QRhiImplementation *rhi, Format format, const QSize &pixelSize, int depth,
int sampleCount, Flags flags);
~QGles2Texture();
void destroy() override;
@@ -154,7 +156,7 @@ struct QGles2Texture : public QRhiTexture
GLenum gltype;
QGles2SamplerData samplerState;
bool specified = false;
- bool compressedAtlasBuilt = false;
+ bool zeroInitialized = false;
int mipLevelCount = 0;
enum Access {
@@ -440,17 +442,20 @@ struct QGles2CommandBuffer : public QRhiCommandBuffer
int size;
} getBufferSubData;
struct {
+ GLenum srcTarget;
GLenum srcFaceTarget;
GLuint srcTexture;
int srcLevel;
int srcX;
int srcY;
+ int srcZ;
GLenum dstTarget;
GLuint dstTexture;
GLenum dstFaceTarget;
int dstLevel;
int dstX;
int dstY;
+ int dstZ;
int w;
int h;
} copyTex;
@@ -470,6 +475,7 @@ struct QGles2CommandBuffer : public QRhiCommandBuffer
int level;
int dx;
int dy;
+ int dz;
int w;
int h;
GLenum glformat;
@@ -486,6 +492,7 @@ struct QGles2CommandBuffer : public QRhiCommandBuffer
GLenum glintformat;
int w;
int h;
+ int depth;
int size;
const void *data; // must come from retainData()
} compressedImage;
@@ -496,6 +503,7 @@ struct QGles2CommandBuffer : public QRhiCommandBuffer
int level;
int dx;
int dy;
+ int dz;
int w;
int h;
GLenum glintformat;
@@ -739,6 +747,7 @@ public:
QRhiTexture::Format backingFormatHint) override;
QRhiTexture *createTexture(QRhiTexture::Format format,
const QSize &pixelSize,
+ int depth,
int sampleCount,
QRhiTexture::Flags flags) override;
QRhiSampler *createSampler(QRhiSampler::Filter magFilter,
@@ -929,7 +938,8 @@ public:
texelFetch(false),
intAttributes(true),
screenSpaceDerivatives(false),
- programBinary(false)
+ programBinary(false),
+ texture3D(false)
{ }
int ctxMajor;
int ctxMinor;
@@ -972,6 +982,7 @@ public:
uint intAttributes : 1;
uint screenSpaceDerivatives : 1;
uint programBinary : 1;
+ uint texture3D : 1;
} caps;
QGles2SwapChain *currentSwapChain = nullptr;
QList<GLint> supportedCompressedFormats;
diff --git a/src/gui/rhi/qrhimetal.mm b/src/gui/rhi/qrhimetal.mm
index 384e23598c..b31d7cccaa 100644
--- a/src/gui/rhi/qrhimetal.mm
+++ b/src/gui/rhi/qrhimetal.mm
@@ -182,7 +182,7 @@ struct QRhiMetalData
struct {
id<MTLTexture> texture;
id<MTLBuffer> stagingBuffers[QMTL_FRAMES_IN_FLIGHT];
- id<MTLTexture> views[QRhi::MAX_LEVELS];
+ id<MTLTexture> views[QRhi::MAX_MIP_LEVELS];
} texture;
struct {
id<MTLSamplerState> samplerState;
@@ -249,7 +249,7 @@ struct QMetalTextureData
id<MTLTexture> tex = nil;
id<MTLBuffer> stagingBuf[QMTL_FRAMES_IN_FLIGHT];
bool owns = true;
- id<MTLTexture> perLevelViews[QRhi::MAX_LEVELS];
+ id<MTLTexture> perLevelViews[QRhi::MAX_MIP_LEVELS];
id<MTLTexture> viewForLevel(int level);
};
@@ -281,7 +281,8 @@ struct QMetalRenderTargetData
struct ColorAtt {
bool needsDrawableForTex = false;
id<MTLTexture> tex = nil;
- int layer = 0;
+ int arrayLayer = 0;
+ int slice = 0;
int level = 0;
bool needsDrawableForResolveTex = false;
id<MTLTexture> resolveTex = nil;
@@ -597,6 +598,10 @@ bool QRhiMetal::isFeatureSupported(QRhi::Feature feature) const
return true;
case QRhi::RenderBufferImport:
return false;
+ case QRhi::ThreeDimensionalTextures:
+ return true;
+ case QRhi::RenderTo3DTextureSlice:
+ return true;
default:
Q_UNREACHABLE();
return false;
@@ -687,10 +692,11 @@ QRhiRenderBuffer *QRhiMetal::createRenderBuffer(QRhiRenderBuffer::Type type, con
return new QMetalRenderBuffer(this, type, pixelSize, sampleCount, flags, backingFormatHint);
}
-QRhiTexture *QRhiMetal::createTexture(QRhiTexture::Format format, const QSize &pixelSize,
+QRhiTexture *QRhiMetal::createTexture(QRhiTexture::Format format,
+ const QSize &pixelSize, int depth,
int sampleCount, QRhiTexture::Flags flags)
{
- return new QMetalTexture(this, format, pixelSize, sampleCount, flags);
+ return new QMetalTexture(this, format, pixelSize, depth, sampleCount, flags);
}
QRhiSampler *QRhiMetal::createSampler(QRhiSampler::Filter magFilter, QRhiSampler::Filter minFilter,
@@ -1613,6 +1619,7 @@ void QRhiMetal::enqueueSubresUpload(QMetalTexture *texD, void *mp, void *blitEnc
const QPoint dp = subresDesc.destinationTopLeft();
const QByteArray rawData = subresDesc.data();
QImage img = subresDesc.image();
+ const bool is3D = texD->m_flags.testFlag(QRhiTexture::ThreeDimensional);
id<MTLBlitCommandEncoder> blitEnc = (id<MTLBlitCommandEncoder>) blitEncPtr;
if (!img.isNull()) {
@@ -1649,9 +1656,9 @@ void QRhiMetal::enqueueSubresUpload(QMetalTexture *texD, void *mp, void *blitEnc
sourceBytesPerImage: 0
sourceSize: MTLSizeMake(NSUInteger(w), NSUInteger(h), 1)
toTexture: texD->d->tex
- destinationSlice: NSUInteger(layer)
+ destinationSlice: NSUInteger(is3D ? 0 : layer)
destinationLevel: NSUInteger(level)
- destinationOrigin: MTLOriginMake(NSUInteger(dp.x()), NSUInteger(dp.y()), 0)
+ destinationOrigin: MTLOriginMake(NSUInteger(dp.x()), NSUInteger(dp.y()), NSUInteger(is3D ? layer : 0))
options: MTLBlitOptionNone];
*curOfs += aligned<qsizetype>(fullImageSizeBytes, QRhiMetalData::TEXBUF_ALIGN);
@@ -1687,9 +1694,9 @@ void QRhiMetal::enqueueSubresUpload(QMetalTexture *texD, void *mp, void *blitEnc
sourceBytesPerImage: 0
sourceSize: MTLSizeMake(NSUInteger(w), NSUInteger(h), 1)
toTexture: texD->d->tex
- destinationSlice: NSUInteger(layer)
+ destinationSlice: NSUInteger(is3D ? 0 : layer)
destinationLevel: NSUInteger(level)
- destinationOrigin: MTLOriginMake(NSUInteger(dx), NSUInteger(dy), 0)
+ destinationOrigin: MTLOriginMake(NSUInteger(dx), NSUInteger(dy), NSUInteger(is3D ? layer : 0))
options: MTLBlitOptionNone];
*curOfs += aligned<qsizetype>(rawData.size(), QRhiMetalData::TEXBUF_ALIGN);
@@ -1720,9 +1727,9 @@ void QRhiMetal::enqueueSubresUpload(QMetalTexture *texD, void *mp, void *blitEnc
sourceBytesPerImage: 0
sourceSize: MTLSizeMake(NSUInteger(w), NSUInteger(h), 1)
toTexture: texD->d->tex
- destinationSlice: NSUInteger(layer)
+ destinationSlice: NSUInteger(is3D ? 0 : layer)
destinationLevel: NSUInteger(level)
- destinationOrigin: MTLOriginMake(NSUInteger(dp.x()), NSUInteger(dp.y()), 0)
+ destinationOrigin: MTLOriginMake(NSUInteger(dp.x()), NSUInteger(dp.y()), NSUInteger(is3D ? layer : 0))
options: MTLBlitOptionNone];
*curOfs += aligned<qsizetype>(rawData.size(), QRhiMetalData::TEXBUF_ALIGN);
@@ -1783,8 +1790,8 @@ void QRhiMetal::enqueueResourceUpdates(QRhiCommandBuffer *cb, QRhiResourceUpdate
if (u.type == QRhiResourceUpdateBatchPrivate::TextureOp::Upload) {
QMetalTexture *utexD = QRHI_RES(QMetalTexture, u.dst);
qsizetype stagingSize = 0;
- for (int layer = 0; layer < QRhi::MAX_LAYERS; ++layer) {
- for (int level = 0; level < QRhi::MAX_LEVELS; ++level) {
+ for (int layer = 0, maxLayer = u.subresDesc.count(); layer < maxLayer; ++layer) {
+ for (int level = 0; level < QRhi::MAX_MIP_LEVELS; ++level) {
for (const QRhiTextureSubresourceUploadDescription &subresDesc : qAsConst(u.subresDesc[layer][level]))
stagingSize += subresUploadByteSize(subresDesc);
}
@@ -1798,8 +1805,8 @@ void QRhiMetal::enqueueResourceUpdates(QRhiCommandBuffer *cb, QRhiResourceUpdate
void *mp = [utexD->d->stagingBuf[currentFrameSlot] contents];
qsizetype curOfs = 0;
- for (int layer = 0; layer < QRhi::MAX_LAYERS; ++layer) {
- for (int level = 0; level < QRhi::MAX_LEVELS; ++level) {
+ for (int layer = 0, maxLayer = u.subresDesc.count(); layer < maxLayer; ++layer) {
+ for (int level = 0; level < QRhi::MAX_MIP_LEVELS; ++level) {
for (const QRhiTextureSubresourceUploadDescription &subresDesc : qAsConst(u.subresDesc[layer][level]))
enqueueSubresUpload(utexD, mp, blitEnc, layer, level, subresDesc, &curOfs);
}
@@ -1818,6 +1825,8 @@ void QRhiMetal::enqueueResourceUpdates(QRhiCommandBuffer *cb, QRhiResourceUpdate
Q_ASSERT(u.src && u.dst);
QMetalTexture *srcD = QRHI_RES(QMetalTexture, u.src);
QMetalTexture *dstD = QRHI_RES(QMetalTexture, u.dst);
+ const bool srcIs3D = srcD->m_flags.testFlag(QRhiTexture::ThreeDimensional);
+ const bool dstIs3D = dstD->m_flags.testFlag(QRhiTexture::ThreeDimensional);
const QPoint dp = u.desc.destinationTopLeft();
const QSize mipSize = q->sizeForMipLevel(u.desc.sourceLevel(), srcD->m_pixelSize);
const QSize copySize = u.desc.pixelSize().isEmpty() ? mipSize : u.desc.pixelSize();
@@ -1825,14 +1834,14 @@ void QRhiMetal::enqueueResourceUpdates(QRhiCommandBuffer *cb, QRhiResourceUpdate
ensureBlit();
[blitEnc copyFromTexture: srcD->d->tex
- sourceSlice: NSUInteger(u.desc.sourceLayer())
+ sourceSlice: NSUInteger(srcIs3D ? 0 : u.desc.sourceLayer())
sourceLevel: NSUInteger(u.desc.sourceLevel())
- sourceOrigin: MTLOriginMake(NSUInteger(sp.x()), NSUInteger(sp.y()), 0)
+ sourceOrigin: MTLOriginMake(NSUInteger(sp.x()), NSUInteger(sp.y()), NSUInteger(srcIs3D ? u.desc.sourceLayer() : 0))
sourceSize: MTLSizeMake(NSUInteger(copySize.width()), NSUInteger(copySize.height()), 1)
toTexture: dstD->d->tex
- destinationSlice: NSUInteger(u.desc.destinationLayer())
+ destinationSlice: NSUInteger(dstIs3D ? 0 : u.desc.destinationLayer())
destinationLevel: NSUInteger(u.desc.destinationLevel())
- destinationOrigin: MTLOriginMake(NSUInteger(dp.x()), NSUInteger(dp.y()), 0)];
+ destinationOrigin: MTLOriginMake(NSUInteger(dp.x()), NSUInteger(dp.y()), NSUInteger(dstIs3D ? u.desc.destinationLayer() : 0))];
srcD->lastActiveFrameSlot = dstD->lastActiveFrameSlot = currentFrameSlot;
} else if (u.type == QRhiResourceUpdateBatchPrivate::TextureOp::Read) {
@@ -1850,6 +1859,10 @@ void QRhiMetal::enqueueResourceUpdates(QRhiCommandBuffer *cb, QRhiResourceUpdate
qWarning("Multisample texture cannot be read back");
continue;
}
+ if (texD->m_flags.testFlag(QRhiTexture::ThreeDimensional)) {
+ qWarning("3D texture readback is not implemented");
+ continue;
+ }
readback.pixelSize = q->sizeForMipLevel(u.rb.level(), texD->m_pixelSize);
readback.format = texD->m_format;
src = texD->d->tex;
@@ -2019,7 +2032,8 @@ void QRhiMetal::beginPass(QRhiCommandBuffer *cb,
for (uint i = 0; i < uint(rtD->colorAttCount); ++i) {
cbD->d->currentPassRpDesc.colorAttachments[i].texture = rtD->fb.colorAtt[i].tex;
- cbD->d->currentPassRpDesc.colorAttachments[i].slice = NSUInteger(rtD->fb.colorAtt[i].layer);
+ cbD->d->currentPassRpDesc.colorAttachments[i].slice = NSUInteger(rtD->fb.colorAtt[i].arrayLayer);
+ cbD->d->currentPassRpDesc.colorAttachments[i].depthPlane = NSUInteger(rtD->fb.colorAtt[i].slice);
cbD->d->currentPassRpDesc.colorAttachments[i].level = NSUInteger(rtD->fb.colorAtt[i].level);
if (rtD->fb.colorAtt[i].resolveTex) {
cbD->d->currentPassRpDesc.colorAttachments[i].storeAction = MTLStoreActionMultisampleResolve;
@@ -2129,7 +2143,7 @@ static void qrhimtl_releaseTexture(const QRhiMetalData::DeferredReleaseEntry &e)
[e.texture.texture release];
for (int i = 0; i < QMTL_FRAMES_IN_FLIGHT; ++i)
[e.texture.stagingBuffers[i] release];
- for (int i = 0; i < QRhi::MAX_LEVELS; ++i)
+ for (int i = 0; i < QRhi::MAX_MIP_LEVELS; ++i)
[e.texture.views[i] release];
}
@@ -2580,15 +2594,15 @@ QRhiTexture::Format QMetalRenderBuffer::backingFormat() const
return m_type == Color ? QRhiTexture::RGBA8 : QRhiTexture::UnknownFormat;
}
-QMetalTexture::QMetalTexture(QRhiImplementation *rhi, Format format, const QSize &pixelSize,
+QMetalTexture::QMetalTexture(QRhiImplementation *rhi, Format format, const QSize &pixelSize, int depth,
int sampleCount, Flags flags)
- : QRhiTexture(rhi, format, pixelSize, sampleCount, flags),
+ : QRhiTexture(rhi, format, pixelSize, depth, sampleCount, flags),
d(new QMetalTextureData(this))
{
for (int i = 0; i < QMTL_FRAMES_IN_FLIGHT; ++i)
d->stagingBuf[i] = nil;
- for (int i = 0; i < QRhi::MAX_LEVELS; ++i)
+ for (int i = 0; i < QRhi::MAX_MIP_LEVELS; ++i)
d->perLevelViews[i] = nil;
}
@@ -2615,7 +2629,7 @@ void QMetalTexture::destroy()
d->stagingBuf[i] = nil;
}
- for (int i = 0; i < QRhi::MAX_LEVELS; ++i) {
+ for (int i = 0; i < QRhi::MAX_MIP_LEVELS; ++i) {
e.texture.views[i] = d->perLevelViews[i];
d->perLevelViews[i] = nil;
}
@@ -2634,6 +2648,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 hasMipMaps = m_flags.testFlag(MipMapped);
QRHI_RES_RHI(QRhiMetal);
@@ -2645,11 +2660,24 @@ bool QMetalTexture::prepareCreate(QSize *adjustedSize)
qWarning("Cubemap texture cannot be multisample");
return false;
}
+ if (is3D) {
+ qWarning("3D texture cannot be multisample");
+ return false;
+ }
if (hasMipMaps) {
qWarning("Multisample texture cannot have mipmaps");
return false;
}
}
+ if (isCube && is3D) {
+ qWarning("Texture cannot be both cube 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;
+ }
if (adjustedSize)
*adjustedSize = size;
@@ -2666,13 +2694,17 @@ bool QMetalTexture::create()
MTLTextureDescriptor *desc = [[MTLTextureDescriptor alloc] init];
const bool isCube = m_flags.testFlag(CubeMap);
+ const bool is3D = m_flags.testFlag(ThreeDimensional);
if (isCube)
desc.textureType = MTLTextureTypeCube;
+ else if (is3D)
+ desc.textureType = MTLTextureType3D;
else
desc.textureType = samples > 1 ? MTLTextureType2DMultisample : MTLTextureType2D;
desc.pixelFormat = d->format;
desc.width = NSUInteger(size.width());
desc.height = NSUInteger(size.height());
+ desc.depth = is3D ? m_depth : 1;
desc.mipmapLevelCount = NSUInteger(mipLevelCount);
if (samples > 1)
desc.sampleCount = NSUInteger(samples);
@@ -3009,12 +3041,14 @@ bool QMetalTextureRenderTarget::create()
QMetalRenderBuffer *rbD = QRHI_RES(QMetalRenderBuffer, it->renderBuffer());
Q_ASSERT(texD || rbD);
id<MTLTexture> dst = nil;
+ bool is3D = false;
if (texD) {
dst = texD->d->tex;
if (attIndex == 0) {
d->pixelSize = rhiD->q->sizeForMipLevel(it->level(), texD->pixelSize());
d->sampleCount = texD->samples;
}
+ is3D = texD->flags().testFlag(QRhiTexture::ThreeDimensional);
} else if (rbD) {
dst = rbD->d->tex;
if (attIndex == 0) {
@@ -3024,7 +3058,8 @@ bool QMetalTextureRenderTarget::create()
}
QMetalRenderTargetData::ColorAtt colorAtt;
colorAtt.tex = dst;
- colorAtt.layer = it->layer();
+ colorAtt.arrayLayer = is3D ? 0 : it->layer();
+ colorAtt.slice = is3D ? it->layer() : 0;
colorAtt.level = it->level();
QMetalTexture *resTexD = QRHI_RES(QMetalTexture, it->resolveTexture());
colorAtt.resolveTex = resTexD ? resTexD->d->tex : nil;
diff --git a/src/gui/rhi/qrhimetal_p_p.h b/src/gui/rhi/qrhimetal_p_p.h
index c8ba5940a8..3a22b76b22 100644
--- a/src/gui/rhi/qrhimetal_p_p.h
+++ b/src/gui/rhi/qrhimetal_p_p.h
@@ -102,7 +102,7 @@ struct QMetalTextureData;
struct QMetalTexture : public QRhiTexture
{
- QMetalTexture(QRhiImplementation *rhi, Format format, const QSize &pixelSize,
+ QMetalTexture(QRhiImplementation *rhi, Format format, const QSize &pixelSize, int depth,
int sampleCount, Flags flags);
~QMetalTexture();
void destroy() override;
@@ -360,6 +360,7 @@ public:
QRhiTexture::Format backingFormatHint) override;
QRhiTexture *createTexture(QRhiTexture::Format format,
const QSize &pixelSize,
+ int depth,
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 0a25b58b91..d8dd77a790 100644
--- a/src/gui/rhi/qrhinull.cpp
+++ b/src/gui/rhi/qrhinull.cpp
@@ -218,10 +218,11 @@ QRhiRenderBuffer *QRhiNull::createRenderBuffer(QRhiRenderBuffer::Type type, cons
return new QNullRenderBuffer(this, type, pixelSize, sampleCount, flags, backingFormatHint);
}
-QRhiTexture *QRhiNull::createTexture(QRhiTexture::Format format, const QSize &pixelSize,
+QRhiTexture *QRhiNull::createTexture(QRhiTexture::Format format,
+ const QSize &pixelSize, int depth,
int sampleCount, QRhiTexture::Flags flags)
{
- return new QNullTexture(this, format, pixelSize, sampleCount, flags);
+ return new QNullTexture(this, format, pixelSize, depth, sampleCount, flags);
}
QRhiSampler *QRhiNull::createSampler(QRhiSampler::Filter magFilter, QRhiSampler::Filter minFilter,
@@ -415,8 +416,8 @@ QRhi::FrameOpResult QRhiNull::finish()
void QRhiNull::simulateTextureUpload(const QRhiResourceUpdateBatchPrivate::TextureOp &u)
{
QNullTexture *texD = QRHI_RES(QNullTexture, u.dst);
- for (int layer = 0; layer < QRhi::MAX_LAYERS; ++layer) {
- for (int level = 0; level < QRhi::MAX_LEVELS; ++level) {
+ for (int layer = 0, maxLayer = u.subresDesc.count(); layer < maxLayer; ++layer) {
+ for (int level = 0; level < QRhi::MAX_MIP_LEVELS; ++level) {
for (const QRhiTextureSubresourceUploadDescription &subresDesc : qAsConst(u.subresDesc[layer][level])) {
if (!subresDesc.image().isNull()) {
const QImage src = subresDesc.image();
@@ -642,9 +643,9 @@ QRhiTexture::Format QNullRenderBuffer::backingFormat() const
return m_type == Color ? QRhiTexture::RGBA8 : QRhiTexture::UnknownFormat;
}
-QNullTexture::QNullTexture(QRhiImplementation *rhi, Format format, const QSize &pixelSize,
+QNullTexture::QNullTexture(QRhiImplementation *rhi, Format format, const QSize &pixelSize, int depth,
int sampleCount, Flags flags)
- : QRhiTexture(rhi, format, pixelSize, sampleCount, flags)
+ : QRhiTexture(rhi, format, pixelSize, depth, sampleCount, flags)
{
}
@@ -663,12 +664,15 @@ bool QNullTexture::create()
{
QRHI_RES_RHI(QRhiNull);
const bool isCube = m_flags.testFlag(CubeMap);
+ const bool is3D = m_flags.testFlag(ThreeDimensional);
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 = isCube ? 6 : 1;
+ const int layerCount = is3D ? m_depth : (isCube ? 6 : 1);
if (m_format == RGBA8) {
+ image.resize(layerCount);
for (int layer = 0; layer < layerCount; ++layer) {
for (int level = 0; level < mipLevelCount; ++level) {
image[layer][level] = QImage(rhiD->q->sizeForMipLevel(level, size),
diff --git a/src/gui/rhi/qrhinull_p_p.h b/src/gui/rhi/qrhinull_p_p.h
index d296c0bbf0..b064e7ad37 100644
--- a/src/gui/rhi/qrhinull_p_p.h
+++ b/src/gui/rhi/qrhinull_p_p.h
@@ -80,14 +80,14 @@ struct QNullRenderBuffer : public QRhiRenderBuffer
struct QNullTexture : public QRhiTexture
{
- QNullTexture(QRhiImplementation *rhi, Format format, const QSize &pixelSize,
+ QNullTexture(QRhiImplementation *rhi, Format format, const QSize &pixelSize, int depth,
int sampleCount, Flags flags);
~QNullTexture();
void destroy() override;
bool create() override;
bool createFrom(NativeTexture src) override;
- QImage image[QRhi::MAX_LAYERS][QRhi::MAX_LEVELS];
+ QVarLengthArray<std::array<QImage, QRhi::MAX_MIP_LEVELS>, 6> image;
};
struct QNullSampler : public QRhiSampler
@@ -217,6 +217,7 @@ public:
QRhiTexture::Format backingFormatHint) override;
QRhiTexture *createTexture(QRhiTexture::Format format,
const QSize &pixelSize,
+ int depth,
int sampleCount,
QRhiTexture::Flags flags) override;
QRhiSampler *createSampler(QRhiSampler::Filter magFilter,
diff --git a/src/gui/rhi/qrhiprofiler.cpp b/src/gui/rhi/qrhiprofiler.cpp
index 364a247ea1..76798dc396 100644
--- a/src/gui/rhi/qrhiprofiler.cpp
+++ b/src/gui/rhi/qrhiprofiler.cpp
@@ -387,7 +387,7 @@ void QRhiProfilerPrivate::newRenderBuffer(QRhiRenderBuffer *rb, bool transientBa
const QSize sz = rb->pixelSize();
// just make up something, ds is likely D24S8 while color is RGBA8 or similar
const QRhiTexture::Format assumedFormat = type == QRhiRenderBuffer::DepthStencil ? QRhiTexture::D32F : QRhiTexture::RGBA8;
- quint32 byteSize = rhiDWhenEnabled->approxByteSizeForTexture(assumedFormat, sz, 1, 1);
+ quint32 byteSize = rhiDWhenEnabled->approxByteSizeForTexture(assumedFormat, sz, 1, 1, 1);
if (sampleCount > 1)
byteSize *= uint(sampleCount);
@@ -418,7 +418,8 @@ void QRhiProfilerPrivate::newTexture(QRhiTexture *tex, bool owns, int mipCount,
const QRhiTexture::Format format = tex->format();
const QSize sz = tex->pixelSize();
- quint32 byteSize = rhiDWhenEnabled->approxByteSizeForTexture(format, sz, mipCount, layerCount);
+ const int depth = tex->depth();
+ quint32 byteSize = rhiDWhenEnabled->approxByteSizeForTexture(format, sz, depth, mipCount, layerCount);
if (sampleCount > 1)
byteSize *= uint(sampleCount);
@@ -470,7 +471,7 @@ void QRhiProfilerPrivate::resizeSwapChain(QRhiSwapChain *sc, int bufferCount, in
return;
const QSize sz = sc->currentPixelSize();
- quint32 byteSize = rhiDWhenEnabled->approxByteSizeForTexture(QRhiTexture::BGRA8, sz, 1, 1);
+ quint32 byteSize = rhiDWhenEnabled->approxByteSizeForTexture(QRhiTexture::BGRA8, sz, 1, 1, 1);
byteSize = byteSize * uint(bufferCount) + byteSize * uint(msaaBufferCount) * uint(sampleCount);
startEntry(QRhiProfiler::ResizeSwapChain, ts.elapsed(), sc);
diff --git a/src/gui/rhi/qrhivulkan.cpp b/src/gui/rhi/qrhivulkan.cpp
index fa1bf9160c..d5b6f5760d 100644
--- a/src/gui/rhi/qrhivulkan.cpp
+++ b/src/gui/rhi/qrhivulkan.cpp
@@ -149,11 +149,21 @@ QT_BEGIN_NAMESPACE
in deviceExtensions. This can be relevant when integrating with native Vulkan
rendering code.
- It is expected that the desired list of instance extensions will be queried
- by calling the static function preferredInstanceExtensions() before
- initializing a QVulkanInstance. The returned list can be passed to
- QVulkanInstance::setExtensions() as-is, because unsupported extensions are
- filtered out automatically.
+ It is expected that the backend's desired list of instance extensions will
+ be queried by calling the static function preferredInstanceExtensions()
+ before initializing a QVulkanInstance. The returned list can be safely
+ passed to QVulkanInstance::setExtensions() as-is, because unsupported
+ extensions are filtered out automatically. If this is not done, certain
+ features, such as QRhi::CustomInstanceStepRate may be reported as
+ unsupported even when the Vulkan implementation on the system has support
+ for the relevant functionality.
+
+ For full functionality the QVulkanInstance needs to have API 1.1 enabled,
+ when available. This means calling QVulkanInstance::setApiVersion() with
+ 1.1 or higher whenever QVulkanInstance::supportedApiVersion() reports that
+ at least Vulkan 1.1 is supported. If this is not done, certain features,
+ such as QRhi::RenderTo3DTextureSlice may be reported as unsupported even
+ when the Vulkan implementation on the system supports Vulkan 1.1 or newer.
\section2 Working with existing Vulkan devices
@@ -555,17 +565,17 @@ bool QRhiVulkan::create(QRhi::Flags flags)
QList<const char *> requestedDevExts;
requestedDevExts.append("VK_KHR_swapchain");
- debugMarkersAvailable = false;
+ caps.debugMarkers = false;
if (devExts.contains(VK_EXT_DEBUG_MARKER_EXTENSION_NAME)) {
requestedDevExts.append(VK_EXT_DEBUG_MARKER_EXTENSION_NAME);
- debugMarkersAvailable = true;
+ caps.debugMarkers = true;
}
- vertexAttribDivisorAvailable = false;
+ caps.vertexAttribDivisor = false;
if (devExts.contains(VK_EXT_VERTEX_ATTRIBUTE_DIVISOR_EXTENSION_NAME)) {
if (inst->extensions().contains(QByteArrayLiteral("VK_KHR_get_physical_device_properties2"))) {
requestedDevExts.append(VK_EXT_VERTEX_ATTRIBUTE_DIVISOR_EXTENSION_NAME);
- vertexAttribDivisorAvailable = true;
+ caps.vertexAttribDivisor = true;
}
}
@@ -652,7 +662,7 @@ bool QRhiVulkan::create(QRhi::Flags flags)
if (queueFamilyProps.isEmpty())
queryQueueFamilyProps();
- hasCompute = (queueFamilyProps[gfxQueueFamilyIdx].queueFlags & VK_QUEUE_COMPUTE_BIT) != 0;
+ caps.compute = (queueFamilyProps[gfxQueueFamilyIdx].queueFlags & VK_QUEUE_COMPUTE_BIT) != 0;
timestampValidBits = queueFamilyProps[gfxQueueFamilyIdx].timestampValidBits;
ubufAlign = physDevProperties.limits.minUniformBufferOffsetAlignment;
@@ -660,7 +670,9 @@ bool QRhiVulkan::create(QRhi::Flags flags)
// elsewhere states that the minimum bufferOffset is 4...
texbufAlign = qMax<VkDeviceSize>(4, physDevProperties.limits.optimalBufferCopyOffsetAlignment);
- hasWideLines = physDevFeatures.wideLines;
+ caps.wideLines = physDevFeatures.wideLines;
+
+ caps.texture3DSliceAs2D = inst->apiVersion() >= QVersionNumber(1, 1);
if (!importedAllocator) {
VmaVulkanFunctions afuncs;
@@ -720,7 +732,7 @@ bool QRhiVulkan::create(QRhi::Flags flags)
timestampQueryPoolMap.resize(QVK_MAX_ACTIVE_TIMESTAMP_PAIRS); // 1 bit per pair
timestampQueryPoolMap.fill(false);
- if (debugMarkersAvailable) {
+ if (caps.debugMarkers) {
vkCmdDebugMarkerBegin = reinterpret_cast<PFN_vkCmdDebugMarkerBeginEXT>(f->vkGetDeviceProcAddr(dev, "vkCmdDebugMarkerBeginEXT"));
vkCmdDebugMarkerEnd = reinterpret_cast<PFN_vkCmdDebugMarkerEndEXT>(f->vkGetDeviceProcAddr(dev, "vkCmdDebugMarkerEndEXT"));
vkCmdDebugMarkerInsert = reinterpret_cast<PFN_vkCmdDebugMarkerInsertEXT>(f->vkGetDeviceProcAddr(dev, "vkCmdDebugMarkerInsertEXT"));
@@ -2933,15 +2945,18 @@ void QRhiVulkan::prepareUploadSubres(QVkTexture *texD, int layer, int level,
qsizetype copySizeBytes = 0;
qsizetype imageSizeBytes = 0;
const void *src = nullptr;
+ const bool is3D = texD->m_flags.testFlag(QRhiTexture::ThreeDimensional);
VkBufferImageCopy copyInfo;
memset(&copyInfo, 0, sizeof(copyInfo));
copyInfo.bufferOffset = *curOfs;
copyInfo.imageSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
copyInfo.imageSubresource.mipLevel = uint32_t(level);
- copyInfo.imageSubresource.baseArrayLayer = uint32_t(layer);
+ copyInfo.imageSubresource.baseArrayLayer = is3D ? 0 : uint32_t(layer);
copyInfo.imageSubresource.layerCount = 1;
copyInfo.imageExtent.depth = 1;
+ if (is3D)
+ copyInfo.imageOffset.z = uint32_t(layer);
const QByteArray rawData = subresDesc.data();
const QPoint dp = subresDesc.destinationTopLeft();
@@ -3191,8 +3206,8 @@ void QRhiVulkan::enqueueResourceUpdates(QVkCommandBuffer *cbD, QRhiResourceUpdat
QVkTexture *utexD = QRHI_RES(QVkTexture, u.dst);
// batch into a single staging buffer and a single CopyBufferToImage with multiple copyInfos
VkDeviceSize stagingSize = 0;
- for (int layer = 0; layer < QRhi::MAX_LAYERS; ++layer) {
- for (int level = 0; level < QRhi::MAX_LEVELS; ++level) {
+ for (int layer = 0, maxLayer = u.subresDesc.count(); layer < maxLayer; ++layer) {
+ for (int level = 0; level < QRhi::MAX_MIP_LEVELS; ++level) {
for (const QRhiTextureSubresourceUploadDescription &subresDesc : qAsConst(u.subresDesc[layer][level]))
stagingSize += subresUploadByteSize(subresDesc);
}
@@ -3229,8 +3244,8 @@ void QRhiVulkan::enqueueResourceUpdates(QVkCommandBuffer *cbD, QRhiResourceUpdat
continue;
}
- for (int layer = 0; layer < QRhi::MAX_LAYERS; ++layer) {
- for (int level = 0; level < QRhi::MAX_LEVELS; ++level) {
+ for (int layer = 0, maxLayer = u.subresDesc.count(); layer < maxLayer; ++layer) {
+ for (int level = 0; level < QRhi::MAX_MIP_LEVELS; ++level) {
const QList<QRhiTextureSubresourceUploadDescription> &srd(u.subresDesc[layer][level]);
if (srd.isEmpty())
continue;
@@ -3278,25 +3293,31 @@ void QRhiVulkan::enqueueResourceUpdates(QVkCommandBuffer *cbD, QRhiResourceUpdat
}
QVkTexture *srcD = QRHI_RES(QVkTexture, u.src);
QVkTexture *dstD = QRHI_RES(QVkTexture, u.dst);
+ const bool srcIs3D = srcD->m_flags.testFlag(QRhiTexture::ThreeDimensional);
+ const bool dstIs3D = dstD->m_flags.testFlag(QRhiTexture::ThreeDimensional);
VkImageCopy region;
memset(&region, 0, sizeof(region));
region.srcSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
region.srcSubresource.mipLevel = uint32_t(u.desc.sourceLevel());
- region.srcSubresource.baseArrayLayer = uint32_t(u.desc.sourceLayer());
+ region.srcSubresource.baseArrayLayer = srcIs3D ? 0 : uint32_t(u.desc.sourceLayer());
region.srcSubresource.layerCount = 1;
region.srcOffset.x = u.desc.sourceTopLeft().x();
region.srcOffset.y = u.desc.sourceTopLeft().y();
+ if (srcIs3D)
+ region.srcOffset.z = uint32_t(u.desc.sourceLayer());
region.dstSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
region.dstSubresource.mipLevel = uint32_t(u.desc.destinationLevel());
- region.dstSubresource.baseArrayLayer = uint32_t(u.desc.destinationLayer());
+ region.dstSubresource.baseArrayLayer = dstIs3D ? 0 : uint32_t(u.desc.destinationLayer());
region.dstSubresource.layerCount = 1;
region.dstOffset.x = u.desc.destinationTopLeft().x();
region.dstOffset.y = u.desc.destinationTopLeft().y();
+ if (dstIs3D)
+ region.dstOffset.z = uint32_t(u.desc.destinationLayer());
const QSize mipSize = q->sizeForMipLevel(u.desc.sourceLevel(), srcD->m_pixelSize);
const QSize copySize = u.desc.pixelSize().isEmpty() ? mipSize : u.desc.pixelSize();
@@ -3331,6 +3352,10 @@ void QRhiVulkan::enqueueResourceUpdates(QVkCommandBuffer *cbD, QRhiResourceUpdat
qWarning("Multisample texture cannot be read back");
continue;
}
+ if (texD->m_flags.testFlag(QRhiTexture::ThreeDimensional)) {
+ qWarning("3D texture cannot be read back");
+ continue;
+ }
readback.pixelSize = q->sizeForMipLevel(u.rb.level(), texD->m_pixelSize);
readback.format = texD->m_format;
texD->lastActiveFrameSlot = currentFrameSlot;
@@ -3426,6 +3451,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 is3D = utexD->m_flags.testFlag(QRhiTexture::ThreeDimensional);
VkImageLayout origLayout = utexD->usageState.layout;
VkAccessFlags origAccess = utexD->usageState.access;
@@ -3436,6 +3462,7 @@ void QRhiVulkan::enqueueResourceUpdates(QVkCommandBuffer *cbD, QRhiResourceUpdat
for (int layer = 0; layer < (isCube ? 6 : 1); ++layer) {
int w = utexD->m_pixelSize.width();
int h = utexD->m_pixelSize.height();
+ int depth = is3D ? utexD->m_depth : 1;
for (int level = 1; level < int(utexD->mipLevelCount); ++level) {
if (level == 1) {
subresourceBarrier(cbD, utexD->image,
@@ -3470,7 +3497,7 @@ void QRhiVulkan::enqueueResourceUpdates(QVkCommandBuffer *cbD, QRhiResourceUpdat
region.srcOffsets[1].x = qMax(1, w);
region.srcOffsets[1].y = qMax(1, h);
- region.srcOffsets[1].z = 1;
+ region.srcOffsets[1].z = qMax(1, depth);
region.dstSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
region.dstSubresource.mipLevel = uint32_t(level);
@@ -3479,7 +3506,7 @@ void QRhiVulkan::enqueueResourceUpdates(QVkCommandBuffer *cbD, QRhiResourceUpdat
region.dstOffsets[1].x = qMax(1, w >> 1);
region.dstOffsets[1].y = qMax(1, h >> 1);
- region.dstOffsets[1].z = 1;
+ region.dstOffsets[1].z = qMax(1, depth >> 1);
QVkCommandBuffer::Command &cmd(cbD->commands.get());
cmd.cmd = QVkCommandBuffer::Command::BlitImage;
@@ -3492,6 +3519,7 @@ void QRhiVulkan::enqueueResourceUpdates(QVkCommandBuffer *cbD, QRhiResourceUpdat
w >>= 1;
h >>= 1;
+ depth >>= 1;
}
if (utexD->mipLevelCount > 1) {
@@ -3569,7 +3597,7 @@ static void qrhivk_releaseTexture(const QRhiVulkan::DeferredReleaseEntry &e, VkD
vmaDestroyImage(toVmaAllocator(allocator), e.texture.image, toVmaAllocation(e.texture.allocation));
for (int i = 0; i < QVK_FRAMES_IN_FLIGHT; ++i)
vmaDestroyBuffer(toVmaAllocator(allocator), e.texture.stagingBuffers[i], toVmaAllocation(e.texture.stagingAllocations[i]));
- for (int i = 0; i < QRhi::MAX_LEVELS; ++i) {
+ for (int i = 0; i < QRhi::MAX_MIP_LEVELS; ++i) {
if (e.texture.extraImageViews[i])
df->vkDestroyImageView(dev, e.texture.extraImageViews[i], nullptr);
}
@@ -4183,13 +4211,13 @@ bool QRhiVulkan::isFeatureSupported(QRhi::Feature feature) const
case QRhi::MultisampleRenderBuffer:
return true;
case QRhi::DebugMarkers:
- return debugMarkersAvailable;
+ return caps.debugMarkers;
case QRhi::Timestamps:
return timestampValidBits != 0;
case QRhi::Instancing:
return true;
case QRhi::CustomInstanceStepRate:
- return vertexAttribDivisorAvailable;
+ return caps.vertexAttribDivisor;
case QRhi::PrimitiveRestart:
return true;
case QRhi::NonDynamicUniformBuffers:
@@ -4203,9 +4231,9 @@ bool QRhiVulkan::isFeatureSupported(QRhi::Feature feature) const
case QRhi::ElementIndexUint:
return true;
case QRhi::Compute:
- return hasCompute;
+ return caps.compute;
case QRhi::WideLines:
- return hasWideLines;
+ return caps.wideLines;
case QRhi::VertexShaderPointSize:
return true;
case QRhi::BaseVertex:
@@ -4234,6 +4262,10 @@ bool QRhiVulkan::isFeatureSupported(QRhi::Feature feature) const
return true;
case QRhi::RenderBufferImport:
return false;
+ case QRhi::ThreeDimensionalTextures:
+ return true;
+ case QRhi::RenderTo3DTextureSlice:
+ return caps.texture3DSliceAs2D;
default:
Q_UNREACHABLE();
return false;
@@ -4439,10 +4471,11 @@ QRhiRenderBuffer *QRhiVulkan::createRenderBuffer(QRhiRenderBuffer::Type type, co
return new QVkRenderBuffer(this, type, pixelSize, sampleCount, flags, backingFormatHint);
}
-QRhiTexture *QRhiVulkan::createTexture(QRhiTexture::Format format, const QSize &pixelSize,
+QRhiTexture *QRhiVulkan::createTexture(QRhiTexture::Format format,
+ const QSize &pixelSize, int depth,
int sampleCount, QRhiTexture::Flags flags)
{
- return new QVkTexture(this, format, pixelSize, sampleCount, flags);
+ return new QVkTexture(this, format, pixelSize, depth, sampleCount, flags);
}
QRhiSampler *QRhiVulkan::createSampler(QRhiSampler::Filter magFilter, QRhiSampler::Filter minFilter,
@@ -4939,7 +4972,7 @@ void QRhiVulkan::drawIndexed(QRhiCommandBuffer *cb, quint32 indexCount,
void QRhiVulkan::debugMarkBegin(QRhiCommandBuffer *cb, const QByteArray &name)
{
- if (!debugMarkers || !debugMarkersAvailable)
+ if (!debugMarkers || !caps.debugMarkers)
return;
VkDebugMarkerMarkerInfoEXT marker;
@@ -4961,7 +4994,7 @@ void QRhiVulkan::debugMarkBegin(QRhiCommandBuffer *cb, const QByteArray &name)
void QRhiVulkan::debugMarkEnd(QRhiCommandBuffer *cb)
{
- if (!debugMarkers || !debugMarkersAvailable)
+ if (!debugMarkers || !caps.debugMarkers)
return;
QVkCommandBuffer *cbD = QRHI_RES(QVkCommandBuffer, cb);
@@ -4975,7 +5008,7 @@ void QRhiVulkan::debugMarkEnd(QRhiCommandBuffer *cb)
void QRhiVulkan::debugMarkMsg(QRhiCommandBuffer *cb, const QByteArray &msg)
{
- if (!debugMarkers || !debugMarkersAvailable)
+ if (!debugMarkers || !caps.debugMarkers)
return;
VkDebugMarkerMarkerInfoEXT marker;
@@ -5075,7 +5108,7 @@ void QRhiVulkan::endExternal(QRhiCommandBuffer *cb)
void QRhiVulkan::setObjectName(uint64_t object, VkDebugReportObjectTypeEXT type, const QByteArray &name, int slot)
{
- if (!debugMarkers || !debugMarkersAvailable || name.isEmpty())
+ if (!debugMarkers || !caps.debugMarkers || name.isEmpty())
return;
VkDebugMarkerObjectNameInfoEXT nameInfo;
@@ -5673,6 +5706,7 @@ bool QVkRenderBuffer::create()
if (!backingTexture) {
backingTexture = QRHI_RES(QVkTexture, rhiD->createTexture(backingFormat(),
m_pixelSize,
+ 1,
m_sampleCount,
QRhiTexture::RenderTarget | QRhiTexture::UsedAsTransferSource));
} else {
@@ -5721,15 +5755,15 @@ QRhiTexture::Format QVkRenderBuffer::backingFormat() const
return m_type == Color ? QRhiTexture::RGBA8 : QRhiTexture::UnknownFormat;
}
-QVkTexture::QVkTexture(QRhiImplementation *rhi, Format format, const QSize &pixelSize,
+QVkTexture::QVkTexture(QRhiImplementation *rhi, Format format, const QSize &pixelSize, int depth,
int sampleCount, Flags flags)
- : QRhiTexture(rhi, format, pixelSize, sampleCount, flags)
+ : QRhiTexture(rhi, format, pixelSize, depth, sampleCount, flags)
{
for (int i = 0; i < QVK_FRAMES_IN_FLIGHT; ++i) {
stagingBuffers[i] = VK_NULL_HANDLE;
stagingAllocations[i] = nullptr;
}
- for (int i = 0; i < QRhi::MAX_LEVELS; ++i)
+ for (int i = 0; i < QRhi::MAX_MIP_LEVELS; ++i)
perLevelImageViews[i] = VK_NULL_HANDLE;
}
@@ -5759,7 +5793,7 @@ void QVkTexture::destroy()
stagingAllocations[i] = nullptr;
}
- for (int i = 0; i < QRhi::MAX_LEVELS; ++i) {
+ for (int i = 0; i < QRhi::MAX_MIP_LEVELS; ++i) {
e.texture.extraImageViews[i] = perLevelImageViews[i];
perLevelImageViews[i] = VK_NULL_HANDLE;
}
@@ -5794,10 +5828,11 @@ 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 is3D = m_flags.testFlag(ThreeDimensional);
const bool hasMipMaps = m_flags.testFlag(MipMapped);
mipLevelCount = uint(hasMipMaps ? rhiD->q->mipLevelsForSize(size) : 1);
- const int maxLevels = QRhi::MAX_LEVELS;
+ const int maxLevels = QRhi::MAX_MIP_LEVELS;
if (mipLevelCount > maxLevels) {
qWarning("Too many mip levels (%d, max is %d), truncating mip chain", mipLevelCount, maxLevels);
mipLevelCount = maxLevels;
@@ -5808,11 +5843,24 @@ bool QVkTexture::prepareCreate(QSize *adjustedSize)
qWarning("Cubemap texture cannot be multisample");
return false;
}
+ if (is3D) {
+ qWarning("3D texture cannot be multisample");
+ return false;
+ }
if (hasMipMaps) {
qWarning("Multisample texture cannot have mipmaps");
return false;
}
}
+ if (isCube && is3D) {
+ qWarning("Texture cannot be both cube 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;
+ }
usageState.layout = VK_IMAGE_LAYOUT_PREINITIALIZED;
usageState.access = 0;
@@ -5830,12 +5878,13 @@ bool QVkTexture::finishCreate()
const auto aspectMask = aspectMaskForTextureFormat(m_format);
const bool isCube = m_flags.testFlag(CubeMap);
+ 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 : VK_IMAGE_VIEW_TYPE_2D;
+ viewInfo.viewType = isCube ? VK_IMAGE_VIEW_TYPE_CUBE : (is3D ? VK_IMAGE_VIEW_TYPE_3D : VK_IMAGE_VIEW_TYPE_2D);
viewInfo.format = vkformat;
viewInfo.components.r = VK_COMPONENT_SWIZZLE_R;
viewInfo.components.g = VK_COMPONENT_SWIZZLE_G;
@@ -5863,19 +5912,39 @@ bool QVkTexture::create()
if (!prepareCreate(&size))
return false;
+ QRHI_RES_RHI(QRhiVulkan);
const bool isRenderTarget = m_flags.testFlag(QRhiTexture::RenderTarget);
const bool isDepth = isDepthTextureFormat(m_format);
const bool isCube = m_flags.testFlag(CubeMap);
+ const bool is3D = m_flags.testFlag(ThreeDimensional);
VkImageCreateInfo imageInfo;
memset(&imageInfo, 0, sizeof(imageInfo));
imageInfo.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO;
- imageInfo.flags = isCube ? VK_IMAGE_CREATE_CUBE_COMPATIBLE_BIT : 0;
- imageInfo.imageType = VK_IMAGE_TYPE_2D;
+ imageInfo.flags = 0;
+ if (isCube)
+ imageInfo.flags |= VK_IMAGE_CREATE_CUBE_COMPATIBLE_BIT;
+
+ if (is3D && isRenderTarget) {
+ // This relies on a Vulkan 1.1 constant. For guaranteed proper behavior
+ // this also requires that at run time the VkInstance has at least API 1.1
+ // enabled. (though it works as expected with some Vulkan (1.2)
+ // implementations regardless of the requested API version, but f.ex. the
+ // validation layer complains when using this without enabling >=1.1)
+ if (!rhiD->caps.texture3DSliceAs2D)
+ qWarning("QRhiVulkan: Rendering to 3D texture slice may not be functional without API 1.1 on the VkInstance");
+#ifdef VK_VERSION_1_1
+ imageInfo.flags |= VK_IMAGE_CREATE_2D_ARRAY_COMPATIBLE_BIT;
+#else
+ imageInfo.flags |= 0x00000020;
+#endif
+ }
+
+ imageInfo.imageType = is3D ? VK_IMAGE_TYPE_3D : VK_IMAGE_TYPE_2D;
imageInfo.format = vkformat;
imageInfo.extent.width = uint32_t(size.width());
imageInfo.extent.height = uint32_t(size.height());
- imageInfo.extent.depth = 1;
+ imageInfo.extent.depth = is3D ? m_depth : 1;
imageInfo.mipLevels = mipLevelCount;
imageInfo.arrayLayers = isCube ? 6 : 1;
imageInfo.samples = samples;
@@ -5900,7 +5969,6 @@ bool QVkTexture::create()
memset(&allocInfo, 0, sizeof(allocInfo));
allocInfo.usage = VMA_MEMORY_USAGE_GPU_ONLY;
- QRHI_RES_RHI(QRhiVulkan);
VmaAllocation allocation;
VkResult err = vmaCreateImage(toVmaAllocator(rhiD->allocator), &imageInfo, &allocInfo, &image, &allocation, nullptr);
if (err != VK_SUCCESS) {
@@ -5965,12 +6033,13 @@ VkImageView QVkTexture::imageViewForLevel(int level)
const VkImageAspectFlags aspectMask = aspectMaskForTextureFormat(m_format);
const bool isCube = m_flags.testFlag(CubeMap);
+ 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 : VK_IMAGE_VIEW_TYPE_2D;
+ viewInfo.viewType = isCube ? VK_IMAGE_VIEW_TYPE_CUBE : (is3D ? VK_IMAGE_VIEW_TYPE_3D : VK_IMAGE_VIEW_TYPE_2D);
viewInfo.format = vkformat;
viewInfo.components.r = VK_COMPONENT_SWIZZLE_R;
viewInfo.components.g = VK_COMPONENT_SWIZZLE_G;
@@ -6645,7 +6714,7 @@ bool QVkGraphicsPipeline::create()
? VK_VERTEX_INPUT_RATE_VERTEX : VK_VERTEX_INPUT_RATE_INSTANCE
};
if (it->classification() == QRhiVertexInputBinding::PerInstance && it->instanceStepRate() != 1) {
- if (rhiD->vertexAttribDivisorAvailable) {
+ if (rhiD->caps.vertexAttribDivisor) {
nonOneStepRates.append({ uint32_t(bindingIndex), uint32_t(it->instanceStepRate()) });
} else {
qWarning("QRhiVulkan: Instance step rates other than 1 not supported without "
@@ -6722,7 +6791,7 @@ bool QVkGraphicsPipeline::create()
rastInfo.depthBiasConstantFactor = float(m_depthBias);
rastInfo.depthBiasSlopeFactor = m_slopeScaledDepthBias;
}
- rastInfo.lineWidth = rhiD->hasWideLines ? m_lineWidth : 1.0f;
+ rastInfo.lineWidth = rhiD->caps.wideLines ? m_lineWidth : 1.0f;
pipelineInfo.pRasterizationState = &rastInfo;
VkPipelineMultisampleStateCreateInfo msInfo;
diff --git a/src/gui/rhi/qrhivulkan_p_p.h b/src/gui/rhi/qrhivulkan_p_p.h
index bb60fca42a..af647bb508 100644
--- a/src/gui/rhi/qrhivulkan_p_p.h
+++ b/src/gui/rhi/qrhivulkan_p_p.h
@@ -128,7 +128,7 @@ struct QVkRenderBuffer : public QRhiRenderBuffer
struct QVkTexture : public QRhiTexture
{
- QVkTexture(QRhiImplementation *rhi, Format format, const QSize &pixelSize,
+ QVkTexture(QRhiImplementation *rhi, Format format, const QSize &pixelSize, int depth,
int sampleCount, Flags flags);
~QVkTexture();
void destroy() override;
@@ -146,7 +146,7 @@ struct QVkTexture : public QRhiTexture
QVkAlloc imageAlloc = nullptr;
VkBuffer stagingBuffers[QVK_FRAMES_IN_FLIGHT];
QVkAlloc stagingAllocations[QVK_FRAMES_IN_FLIGHT];
- VkImageView perLevelImageViews[QRhi::MAX_LEVELS];
+ VkImageView perLevelImageViews[QRhi::MAX_MIP_LEVELS];
bool owns = true;
struct UsageState {
// no tracking of subresource layouts (some operations can keep
@@ -676,6 +676,7 @@ public:
QRhiTexture::Format backingFormatHint) override;
QRhiTexture *createTexture(QRhiTexture::Format format,
const QSize &pixelSize,
+ int depth,
int sampleCount,
QRhiTexture::Flags flags) override;
QRhiSampler *createSampler(QRhiSampler::Filter magFilter,
@@ -846,7 +847,6 @@ public:
int gfxQueueFamilyIdx = -1;
int gfxQueueIdx = 0;
VkQueue gfxQueue = VK_NULL_HANDLE;
- bool hasCompute = false;
quint32 timestampValidBits = 0;
bool importedAllocator = false;
QVkAllocator allocator = nullptr;
@@ -857,12 +857,9 @@ public:
VkPhysicalDeviceProperties physDevProperties;
VkDeviceSize ubufAlign;
VkDeviceSize texbufAlign;
- bool hasWideLines = false;
bool deviceLost = false;
bool releaseCachedResourcesCalledBeforeFrameStart = false;
- bool debugMarkersAvailable = false;
- bool vertexAttribDivisorAvailable = false;
PFN_vkCmdDebugMarkerBeginEXT vkCmdDebugMarkerBegin = nullptr;
PFN_vkCmdDebugMarkerEndEXT vkCmdDebugMarkerEnd = nullptr;
PFN_vkCmdDebugMarkerInsertEXT vkCmdDebugMarkerInsert = nullptr;
@@ -877,6 +874,14 @@ public:
PFN_vkGetPhysicalDeviceSurfaceFormatsKHR vkGetPhysicalDeviceSurfaceFormatsKHR;
PFN_vkGetPhysicalDeviceSurfacePresentModesKHR vkGetPhysicalDeviceSurfacePresentModesKHR;
+ struct {
+ bool compute = false;
+ bool wideLines = false;
+ bool debugMarkers = false;
+ bool vertexAttribDivisor = false;
+ bool texture3DSliceAs2D = false;
+ } caps;
+
VkPipelineCache pipelineCache = VK_NULL_HANDLE;
struct DescriptorPoolData {
DescriptorPoolData() { }
@@ -978,7 +983,7 @@ public:
QVkAlloc allocation;
VkBuffer stagingBuffers[QVK_FRAMES_IN_FLIGHT];
QVkAlloc stagingAllocations[QVK_FRAMES_IN_FLIGHT];
- VkImageView extraImageViews[QRhi::MAX_LEVELS];
+ VkImageView extraImageViews[QRhi::MAX_MIP_LEVELS];
} texture;
struct {
VkSampler sampler;