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>
This commit is contained in:
parent
5eab5d6253
commit
51c22a1f51
@ -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);
|
||||
}
|
||||
|
||||
/*!
|
||||
|
@ -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();
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -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,
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -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,
|
||||
|
@ -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),
|
||||
|
@ -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,
|
||||
|
@ -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);
|
||||
|
@ -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(©Info, 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(®ion, 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;
|
||||
|
@ -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;
|
||||
|
@ -135,6 +135,8 @@ private slots:
|
||||
void textureImportOpenGL();
|
||||
void renderbufferImportOpenGL_data();
|
||||
void renderbufferImportOpenGL();
|
||||
void threeDimTexture_data();
|
||||
void threeDimTexture();
|
||||
|
||||
private:
|
||||
void setWindowType(QWindow *window, QRhi::Implementation impl);
|
||||
@ -169,6 +171,11 @@ void tst_QRhi::initTestCase()
|
||||
#endif
|
||||
|
||||
#ifdef TST_VK
|
||||
const QVersionNumber supportedVersion = vulkanInstance.supportedApiVersion();
|
||||
if (supportedVersion >= QVersionNumber(1, 2))
|
||||
vulkanInstance.setApiVersion(QVersionNumber(1, 2));
|
||||
else if (supportedVersion >= QVersionNumber(1, 1))
|
||||
vulkanInstance.setApiVersion(QVersionNumber(1, 2));
|
||||
vulkanInstance.setLayers({ "VK_LAYER_KHRONOS_validation" });
|
||||
vulkanInstance.setExtensions(QRhiVulkanInitParams::preferredInstanceExtensions());
|
||||
vulkanInstance.create();
|
||||
@ -358,7 +365,8 @@ void tst_QRhi::create()
|
||||
QRhi::ReadBackAnyTextureFormat,
|
||||
QRhi::PipelineCacheDataLoadSave,
|
||||
QRhi::ImageDataStride,
|
||||
QRhi::RenderBufferImport
|
||||
QRhi::RenderBufferImport,
|
||||
QRhi::ThreeDimensionalTextures
|
||||
};
|
||||
for (size_t i = 0; i <sizeof(features) / sizeof(QRhi::Feature); ++i)
|
||||
rhi->isFeatureSupported(features[i]);
|
||||
@ -3817,5 +3825,100 @@ void tst_QRhi::renderbufferImportOpenGL()
|
||||
#endif
|
||||
}
|
||||
|
||||
void tst_QRhi::threeDimTexture_data()
|
||||
{
|
||||
rhiTestData();
|
||||
}
|
||||
|
||||
void tst_QRhi::threeDimTexture()
|
||||
{
|
||||
QFETCH(QRhi::Implementation, impl);
|
||||
QFETCH(QRhiInitParams *, initParams);
|
||||
|
||||
QScopedPointer<QRhi> rhi(QRhi::create(impl, initParams));
|
||||
if (!rhi)
|
||||
QSKIP("QRhi could not be created, skipping testing 3D textures");
|
||||
|
||||
if (!rhi->isFeatureSupported(QRhi::ThreeDimensionalTextures))
|
||||
QSKIP("Skipping testing 3D textures because they are reported as unsupported");
|
||||
|
||||
const int WIDTH = 512;
|
||||
const int HEIGHT = 256;
|
||||
const int DEPTH = 128;
|
||||
|
||||
{
|
||||
QScopedPointer<QRhiTexture> texture(rhi->newTexture(QRhiTexture::RGBA8, WIDTH, HEIGHT, DEPTH));
|
||||
QVERIFY(texture->create());
|
||||
|
||||
QRhiResourceUpdateBatch *batch = rhi->nextResourceUpdateBatch();
|
||||
QVERIFY(batch);
|
||||
|
||||
for (int i = 0; i < DEPTH; ++i) {
|
||||
QImage img(WIDTH, HEIGHT, QImage::Format_RGBA8888);
|
||||
img.fill(QColor::fromRgb(i * 2, 0, 0));
|
||||
QRhiTextureUploadEntry sliceUpload(i, 0, QRhiTextureSubresourceUploadDescription(img));
|
||||
batch->uploadTexture(texture.data(), sliceUpload);
|
||||
}
|
||||
|
||||
QVERIFY(submitResourceUpdates(rhi.data(), batch));
|
||||
}
|
||||
|
||||
// mipmaps
|
||||
{
|
||||
QScopedPointer<QRhiTexture> texture(rhi->newTexture(QRhiTexture::RGBA8, WIDTH, HEIGHT, DEPTH,
|
||||
1, QRhiTexture::MipMapped | QRhiTexture::UsedWithGenerateMips));
|
||||
QVERIFY(texture->create());
|
||||
|
||||
QRhiResourceUpdateBatch *batch = rhi->nextResourceUpdateBatch();
|
||||
QVERIFY(batch);
|
||||
|
||||
for (int i = 0; i < DEPTH; ++i) {
|
||||
QImage img(WIDTH, HEIGHT, QImage::Format_RGBA8888);
|
||||
img.fill(QColor::fromRgb(i * 2, 0, 0));
|
||||
QRhiTextureUploadEntry sliceUpload(i, 0, QRhiTextureSubresourceUploadDescription(img));
|
||||
batch->uploadTexture(texture.data(), sliceUpload);
|
||||
}
|
||||
|
||||
batch->generateMips(texture.data());
|
||||
|
||||
QVERIFY(submitResourceUpdates(rhi.data(), batch));
|
||||
}
|
||||
|
||||
// render target (one slice)
|
||||
// NB with Vulkan we require Vulkan 1.1 for this to work.
|
||||
{
|
||||
const int SLICE = 23;
|
||||
QScopedPointer<QRhiTexture> texture(rhi->newTexture(QRhiTexture::RGBA8, WIDTH, HEIGHT, DEPTH,
|
||||
1, QRhiTexture::RenderTarget));
|
||||
QVERIFY(texture->create());
|
||||
|
||||
QRhiColorAttachment att(texture.data());
|
||||
att.setLayer(SLICE);
|
||||
QRhiTextureRenderTargetDescription rtDesc(att);
|
||||
QScopedPointer<QRhiTextureRenderTarget> rt(rhi->newTextureRenderTarget(rtDesc));
|
||||
QScopedPointer<QRhiRenderPassDescriptor> rp(rt->newCompatibleRenderPassDescriptor());
|
||||
rt->setRenderPassDescriptor(rp.data());
|
||||
QVERIFY(rt->create());
|
||||
|
||||
QRhiResourceUpdateBatch *batch = rhi->nextResourceUpdateBatch();
|
||||
QVERIFY(batch);
|
||||
|
||||
for (int i = 0; i < DEPTH; ++i) {
|
||||
QImage img(WIDTH, HEIGHT, QImage::Format_RGBA8888);
|
||||
img.fill(QColor::fromRgb(i * 2, 0, 0));
|
||||
QRhiTextureUploadEntry sliceUpload(i, 0, QRhiTextureSubresourceUploadDescription(img));
|
||||
batch->uploadTexture(texture.data(), sliceUpload);
|
||||
}
|
||||
|
||||
QRhiCommandBuffer *cb = nullptr;
|
||||
QVERIFY(rhi->beginOffscreenFrame(&cb) == QRhi::FrameOpSuccess);
|
||||
QVERIFY(cb);
|
||||
cb->beginPass(rt.data(), Qt::blue, { 1.0f, 0 }, batch);
|
||||
// slice 23 is now blue
|
||||
cb->endPass();
|
||||
rhi->endOffscreenFrame();
|
||||
}
|
||||
}
|
||||
|
||||
#include <tst_qrhi.moc>
|
||||
QTEST_MAIN(tst_QRhi)
|
||||
|
@ -21,6 +21,7 @@ add_subdirectory(computebuffer)
|
||||
add_subdirectory(computeimage)
|
||||
add_subdirectory(instancing)
|
||||
add_subdirectory(noninstanced)
|
||||
add_subdirectory(tex3d)
|
||||
if(QT_FEATURE_widgets)
|
||||
add_subdirectory(qrhiprof)
|
||||
endif()
|
||||
|
@ -21,7 +21,8 @@ SUBDIRS += \
|
||||
computebuffer \
|
||||
computeimage \
|
||||
instancing \
|
||||
noninstanced
|
||||
noninstanced \
|
||||
tex3d
|
||||
|
||||
qtConfig(widgets) {
|
||||
SUBDIRS += \
|
||||
|
@ -538,6 +538,12 @@ int main(int argc, char **argv)
|
||||
qDebug("Enabling Vulkan validation layer (if available)");
|
||||
inst.setLayers({ "VK_LAYER_KHRONOS_validation" });
|
||||
}
|
||||
const QVersionNumber supportedVersion = inst.supportedApiVersion();
|
||||
qDebug() << "Supported Vulkan API version:" << supportedVersion;
|
||||
if (supportedVersion >= QVersionNumber(1, 1)) {
|
||||
qDebug("Requesting Vulkan API 1.1 on the VkInstance");
|
||||
inst.setApiVersion(QVersionNumber(1, 1));
|
||||
}
|
||||
inst.setExtensions(QRhiVulkanInitParams::preferredInstanceExtensions());
|
||||
if (!inst.create()) {
|
||||
qWarning("Failed to create Vulkan instance, switching to OpenGL");
|
||||
|
20
tests/manual/rhi/tex3d/CMakeLists.txt
Normal file
20
tests/manual/rhi/tex3d/CMakeLists.txt
Normal file
@ -0,0 +1,20 @@
|
||||
qt_internal_add_manual_test(tex3d
|
||||
GUI
|
||||
SOURCES
|
||||
tex3d.cpp
|
||||
PUBLIC_LIBRARIES
|
||||
Qt::Gui
|
||||
Qt::GuiPrivate
|
||||
)
|
||||
|
||||
set(tex3d_resource_files
|
||||
"texture3d.vert.qsb"
|
||||
"texture3d.frag.qsb"
|
||||
)
|
||||
|
||||
qt_internal_add_resource(tex3d "tex3d"
|
||||
PREFIX
|
||||
"/"
|
||||
FILES
|
||||
${tex3d_resource_files}
|
||||
)
|
2
tests/manual/rhi/tex3d/buildshaders.bat
Normal file
2
tests/manual/rhi/tex3d/buildshaders.bat
Normal file
@ -0,0 +1,2 @@
|
||||
qsb --glsl "300 es,150" --hlsl 50 --msl 12 -c texture3d.vert -o texture3d.vert.qsb
|
||||
qsb --glsl "300 es,150" --hlsl 50 --msl 12 -c texture3d.frag -o texture3d.frag.qsb
|
274
tests/manual/rhi/tex3d/tex3d.cpp
Normal file
274
tests/manual/rhi/tex3d/tex3d.cpp
Normal file
@ -0,0 +1,274 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2021 The Qt Company Ltd.
|
||||
** Contact: https://www.qt.io/licensing/
|
||||
**
|
||||
** This file is part of the examples of the Qt Toolkit.
|
||||
**
|
||||
** $QT_BEGIN_LICENSE:BSD$
|
||||
** Commercial License Usage
|
||||
** Licensees holding valid commercial Qt licenses may use this file in
|
||||
** accordance with the commercial license agreement provided with the
|
||||
** Software or, alternatively, in accordance with the terms contained in
|
||||
** a written agreement between you and The Qt Company. For licensing terms
|
||||
** and conditions see https://www.qt.io/terms-conditions. For further
|
||||
** information use the contact form at https://www.qt.io/contact-us.
|
||||
**
|
||||
** BSD License Usage
|
||||
** Alternatively, you may use this file under the terms of the BSD license
|
||||
** as follows:
|
||||
**
|
||||
** "Redistribution and use in source and binary forms, with or without
|
||||
** modification, are permitted provided that the following conditions are
|
||||
** met:
|
||||
** * Redistributions of source code must retain the above copyright
|
||||
** notice, this list of conditions and the following disclaimer.
|
||||
** * Redistributions in binary form must reproduce the above copyright
|
||||
** notice, this list of conditions and the following disclaimer in
|
||||
** the documentation and/or other materials provided with the
|
||||
** distribution.
|
||||
** * Neither the name of The Qt Company Ltd nor the names of its
|
||||
** contributors may be used to endorse or promote products derived
|
||||
** from this software without specific prior written permission.
|
||||
**
|
||||
**
|
||||
** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
|
||||
**
|
||||
** $QT_END_LICENSE$
|
||||
**
|
||||
****************************************************************************/
|
||||
|
||||
#include "../shared/examplefw.h"
|
||||
#include <QImage>
|
||||
#include <QPainter>
|
||||
|
||||
// Create a 3D texture of 256x256x3, where we will render to one of the slices
|
||||
// in a render pass, while providing data to two other slices in texture
|
||||
// uploads. Finally we use the texture to in the on-screen rendering: texture 3
|
||||
// quads with the same volume texture, in each draw the shader will sample with
|
||||
// a w that selects slice 0, 1, or 2, respectively.
|
||||
|
||||
const int TEXTURE_DEPTH = 3;
|
||||
|
||||
// Tests mipmap generation and linear-mipmap filtering when true. Note: Be
|
||||
// aware what a mipmap chain for a 3D texture is and isn't, do not confuse with
|
||||
// 2D (array) textures. The expected result on-screen is the black-red (slice
|
||||
// 1) content mixed in to slice 0 and 2 on lower mip levels (resize the window
|
||||
// to a smaller size -> the effect becomes more apparent, with totally taking
|
||||
// over at small sizes)
|
||||
// See https://docs.microsoft.com/en-us/windows/win32/direct3d11/overviews-direct3d-11-resources-textures-intro
|
||||
//
|
||||
const bool MIPMAP = true;
|
||||
|
||||
static float quadVertexData[] =
|
||||
{ // Y up, CCW
|
||||
-0.5f, 0.5f, 0.0f, 0.0f,
|
||||
-0.5f, -0.5f, 0.0f, 1.0f,
|
||||
0.5f, -0.5f, 1.0f, 1.0f,
|
||||
0.5f, 0.5f, 1.0f, 0.0f
|
||||
};
|
||||
|
||||
static quint16 quadIndexData[] =
|
||||
{
|
||||
0, 1, 2, 0, 2, 3
|
||||
};
|
||||
|
||||
struct {
|
||||
QList<QRhiResource *> releasePool;
|
||||
QRhiResourceUpdateBatch *initialUpdates = nullptr;
|
||||
QRhiTexture *tex = nullptr;
|
||||
QRhiTextureRenderTarget *rt = nullptr;
|
||||
QRhiRenderPassDescriptor *rtRp = nullptr;
|
||||
QRhiBuffer *vbuf = nullptr;
|
||||
QRhiBuffer *ibuf = nullptr;
|
||||
QRhiBuffer *ubuf = nullptr;
|
||||
QRhiSampler *sampler = nullptr;
|
||||
QRhiGraphicsPipeline *ps = nullptr;
|
||||
QRhiShaderResourceBindings *srb = nullptr;
|
||||
QMatrix4x4 winProj;
|
||||
QVector3D slice1ClearColor;
|
||||
} d;
|
||||
|
||||
void Window::customInit()
|
||||
{
|
||||
if (!m_r->isFeatureSupported(QRhi::ThreeDimensionalTextures))
|
||||
qWarning("3D textures are not supported");
|
||||
|
||||
QRhiTexture::Flags texFlags = QRhiTexture::RenderTarget;
|
||||
if (MIPMAP)
|
||||
texFlags |= QRhiTexture::MipMapped | QRhiTexture::UsedWithGenerateMips;
|
||||
d.tex = m_r->newTexture(QRhiTexture::RGBA8,
|
||||
256, 256, TEXTURE_DEPTH,
|
||||
1, texFlags);
|
||||
d.releasePool << d.tex;
|
||||
d.tex->create();
|
||||
|
||||
d.initialUpdates = m_r->nextResourceUpdateBatch();
|
||||
|
||||
QImage imgWhiteText(256, 256, QImage::Format_RGBA8888);
|
||||
imgWhiteText.fill(Qt::blue);
|
||||
QPainter p(&imgWhiteText);
|
||||
p.setPen(Qt::white);
|
||||
p.drawText(10, 10, "Slice 2:\nWhite text on blue background");
|
||||
p.end();
|
||||
|
||||
// imgWhiteText -> slice 2 of the 3D texture
|
||||
QRhiTextureUploadEntry uploadSlice2(2, 0, QRhiTextureSubresourceUploadDescription(imgWhiteText));
|
||||
d.initialUpdates->uploadTexture(d.tex, uploadSlice2);
|
||||
|
||||
QImage imgYellowText(256, 256, QImage::Format_RGBA8888);
|
||||
imgYellowText.fill(Qt::green);
|
||||
p.begin(&imgYellowText);
|
||||
p.setPen(Qt::yellow);
|
||||
p.drawText(10, 10, "Slice 0: Yellow text on green background");
|
||||
p.end();
|
||||
|
||||
// imgYellowText -> slice 0 of the 3D texture
|
||||
QRhiTextureUploadEntry uploadSlice0(0, 0, QRhiTextureSubresourceUploadDescription(imgYellowText));
|
||||
d.initialUpdates->uploadTexture(d.tex, uploadSlice0);
|
||||
|
||||
// slice 1 -> clear to some color in a render pass
|
||||
QRhiColorAttachment att(d.tex);
|
||||
att.setLayer(1);
|
||||
QRhiTextureRenderTargetDescription rtDesc(att);
|
||||
d.rt = m_r->newTextureRenderTarget(rtDesc);
|
||||
d.releasePool << d.rt;
|
||||
d.rtRp = d.rt->newCompatibleRenderPassDescriptor();
|
||||
d.releasePool << d.rtRp;
|
||||
d.rt->setRenderPassDescriptor(d.rtRp);
|
||||
d.rt->create();
|
||||
|
||||
// set up resources needed to render a quad
|
||||
d.vbuf = m_r->newBuffer(QRhiBuffer::Immutable, QRhiBuffer::VertexBuffer, sizeof(quadVertexData));
|
||||
d.vbuf->create();
|
||||
d.releasePool << d.vbuf;
|
||||
|
||||
d.ibuf = m_r->newBuffer(QRhiBuffer::Immutable, QRhiBuffer::IndexBuffer, sizeof(quadIndexData));
|
||||
d.ibuf->create();
|
||||
d.releasePool << d.ibuf;
|
||||
|
||||
d.initialUpdates->uploadStaticBuffer(d.vbuf, 0, sizeof(quadVertexData), quadVertexData);
|
||||
d.initialUpdates->uploadStaticBuffer(d.ibuf, quadIndexData);
|
||||
|
||||
d.ubuf = m_r->newBuffer(QRhiBuffer::Dynamic, QRhiBuffer::UniformBuffer, m_r->ubufAligned(72) * TEXTURE_DEPTH);
|
||||
d.ubuf->create();
|
||||
d.releasePool << d.ubuf;
|
||||
|
||||
for (int i = 0; i < TEXTURE_DEPTH; ++i) {
|
||||
qint32 flip = 0;
|
||||
if (i == 1) // this slice will be rendered to in a render pass so needs flipping with OpenGL when on screen
|
||||
flip = m_r->isYUpInFramebuffer() ? 1 : 0;
|
||||
d.initialUpdates->updateDynamicBuffer(d.ubuf, m_r->ubufAligned(72) * i + 64, 4, &flip);
|
||||
qint32 idx = i;
|
||||
d.initialUpdates->updateDynamicBuffer(d.ubuf, m_r->ubufAligned(72) * i + 68, 4, &idx);
|
||||
}
|
||||
|
||||
d.sampler = m_r->newSampler(QRhiSampler::Linear, QRhiSampler::Linear, MIPMAP ? QRhiSampler::Linear : QRhiSampler::None,
|
||||
QRhiSampler::ClampToEdge, QRhiSampler::ClampToEdge, QRhiSampler::ClampToEdge);
|
||||
d.releasePool << d.sampler;
|
||||
d.sampler->create();
|
||||
|
||||
d.srb = m_r->newShaderResourceBindings();
|
||||
d.releasePool << d.srb;
|
||||
d.srb->setBindings({
|
||||
QRhiShaderResourceBinding::uniformBufferWithDynamicOffset(0, QRhiShaderResourceBinding::VertexStage | QRhiShaderResourceBinding::FragmentStage, d.ubuf, 72),
|
||||
QRhiShaderResourceBinding::sampledTexture(1, QRhiShaderResourceBinding::FragmentStage, d.tex, d.sampler)
|
||||
});
|
||||
d.srb->create();
|
||||
|
||||
d.ps = m_r->newGraphicsPipeline();
|
||||
d.releasePool << d.ps;
|
||||
d.ps->setShaderStages({
|
||||
{ QRhiShaderStage::Vertex, getShader(QLatin1String(":/texture3d.vert.qsb")) },
|
||||
{ QRhiShaderStage::Fragment, getShader(QLatin1String(":/texture3d.frag.qsb")) }
|
||||
});
|
||||
QRhiVertexInputLayout inputLayout;
|
||||
inputLayout.setBindings({
|
||||
{ 4 * sizeof(float) }
|
||||
});
|
||||
inputLayout.setAttributes({
|
||||
{ 0, 0, QRhiVertexInputAttribute::Float2, 0 },
|
||||
{ 0, 1, QRhiVertexInputAttribute::Float2, quint32(2 * sizeof(float)) }
|
||||
});
|
||||
d.ps->setVertexInputLayout(inputLayout);
|
||||
d.ps->setShaderResourceBindings(d.srb);
|
||||
d.ps->setRenderPassDescriptor(m_rp);
|
||||
d.ps->create();
|
||||
}
|
||||
|
||||
void Window::customRelease()
|
||||
{
|
||||
qDeleteAll(d.releasePool);
|
||||
d.releasePool.clear();
|
||||
}
|
||||
|
||||
void Window::customRender()
|
||||
{
|
||||
QRhiCommandBuffer *cb = m_sc->currentFrameCommandBuffer();
|
||||
QRhiResourceUpdateBatch *u = m_r->nextResourceUpdateBatch();
|
||||
if (d.initialUpdates) {
|
||||
u->merge(d.initialUpdates);
|
||||
d.initialUpdates->release();
|
||||
d.initialUpdates = nullptr;
|
||||
}
|
||||
|
||||
if (d.winProj != m_proj) {
|
||||
d.winProj = m_proj;
|
||||
for (int i = 0; i < TEXTURE_DEPTH; ++i) {
|
||||
QMatrix4x4 mvp = m_proj;
|
||||
switch (i) {
|
||||
case 0:
|
||||
mvp.translate(-2.0f, 0.0f, 0.0f);
|
||||
break;
|
||||
case 2:
|
||||
mvp.translate(2.0f, 0.0f, 0.0f);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
u->updateDynamicBuffer(d.ubuf, m_r->ubufAligned(72) * i, 64, mvp.constData());
|
||||
}
|
||||
}
|
||||
|
||||
const QColor slice1ClearColor = QColor::fromRgbF(d.slice1ClearColor.x(), d.slice1ClearColor.y(), d.slice1ClearColor.z());
|
||||
d.slice1ClearColor.setX(d.slice1ClearColor.x() + 0.01f);
|
||||
if (d.slice1ClearColor.x() > 1.0f)
|
||||
d.slice1ClearColor.setX(0.0f);
|
||||
cb->beginPass(d.rt, slice1ClearColor, { 1.0f, 0 }, u);
|
||||
cb->endPass();
|
||||
|
||||
// now all 3 slices have something, regenerate the mipmaps
|
||||
u = m_r->nextResourceUpdateBatch();
|
||||
if (MIPMAP)
|
||||
u->generateMips(d.tex);
|
||||
|
||||
const QSize outputSizeInPixels = m_sc->currentPixelSize();
|
||||
cb->beginPass(m_sc->currentFrameRenderTarget(), m_clearColor, { 1.0f, 0 }, u);
|
||||
cb->setGraphicsPipeline(d.ps);
|
||||
QRhiCommandBuffer::DynamicOffset dynOfs = { 0, 0 };
|
||||
cb->setShaderResources(d.srb, 1, &dynOfs);
|
||||
cb->setViewport({ 0, 0, float(outputSizeInPixels.width()), float(outputSizeInPixels.height()) });
|
||||
QRhiCommandBuffer::VertexInput vbufBinding(d.vbuf, 0);
|
||||
cb->setVertexInput(0, 1, &vbufBinding, d.ibuf, 0, QRhiCommandBuffer::IndexUInt16);
|
||||
cb->drawIndexed(6);
|
||||
|
||||
dynOfs.second = m_r->ubufAligned(72);
|
||||
cb->setShaderResources(d.srb, 1, &dynOfs);
|
||||
cb->drawIndexed(6);
|
||||
|
||||
dynOfs.second = m_r->ubufAligned(72) * 2;
|
||||
cb->setShaderResources(d.srb, 1, &dynOfs);
|
||||
cb->drawIndexed(6);
|
||||
|
||||
cb->endPass();
|
||||
}
|
8
tests/manual/rhi/tex3d/tex3d.pro
Normal file
8
tests/manual/rhi/tex3d/tex3d.pro
Normal file
@ -0,0 +1,8 @@
|
||||
TEMPLATE = app
|
||||
|
||||
QT += gui-private
|
||||
|
||||
SOURCES = \
|
||||
tex3d.cpp
|
||||
|
||||
RESOURCES = tex3d.qrc
|
6
tests/manual/rhi/tex3d/tex3d.qrc
Normal file
6
tests/manual/rhi/tex3d/tex3d.qrc
Normal file
@ -0,0 +1,6 @@
|
||||
<!DOCTYPE RCC><RCC version="1.0">
|
||||
<qresource>
|
||||
<file>texture3d.vert.qsb</file>
|
||||
<file>texture3d.frag.qsb</file>
|
||||
</qresource>
|
||||
</RCC>
|
24
tests/manual/rhi/tex3d/texture3d.frag
Normal file
24
tests/manual/rhi/tex3d/texture3d.frag
Normal file
@ -0,0 +1,24 @@
|
||||
#version 440
|
||||
|
||||
layout(location = 0) in vec2 v_texcoord;
|
||||
|
||||
layout(location = 0) out vec4 fragColor;
|
||||
|
||||
layout(std140, binding = 0) uniform buf {
|
||||
mat4 mvp;
|
||||
int flip;
|
||||
int idx;
|
||||
} ubuf;
|
||||
|
||||
layout(binding = 1) uniform sampler3D tex;
|
||||
|
||||
void main()
|
||||
{
|
||||
float w = 0.0;
|
||||
if (ubuf.idx == 1)
|
||||
w = 0.5;
|
||||
else if (ubuf.idx == 2)
|
||||
w = 1.0;
|
||||
vec4 c = texture(tex, vec3(v_texcoord, w));
|
||||
fragColor = vec4(c.rgb * c.a, c.a);
|
||||
}
|
BIN
tests/manual/rhi/tex3d/texture3d.frag.qsb
Normal file
BIN
tests/manual/rhi/tex3d/texture3d.frag.qsb
Normal file
Binary file not shown.
22
tests/manual/rhi/tex3d/texture3d.vert
Normal file
22
tests/manual/rhi/tex3d/texture3d.vert
Normal file
@ -0,0 +1,22 @@
|
||||
#version 440
|
||||
|
||||
layout(location = 0) in vec4 position;
|
||||
layout(location = 1) in vec2 texcoord;
|
||||
|
||||
layout(location = 0) out vec2 v_texcoord;
|
||||
|
||||
layout(std140, binding = 0) uniform buf {
|
||||
mat4 mvp;
|
||||
int flip;
|
||||
int idx;
|
||||
} ubuf;
|
||||
|
||||
out gl_PerVertex { vec4 gl_Position; };
|
||||
|
||||
void main()
|
||||
{
|
||||
v_texcoord = vec2(texcoord.x, texcoord.y);
|
||||
if (ubuf.flip != 0)
|
||||
v_texcoord.y = 1.0 - v_texcoord.y;
|
||||
gl_Position = ubuf.mvp * position;
|
||||
}
|
BIN
tests/manual/rhi/tex3d/texture3d.vert.qsb
Normal file
BIN
tests/manual/rhi/tex3d/texture3d.vert.qsb
Normal file
Binary file not shown.
Loading…
x
Reference in New Issue
Block a user