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
|
may want to associated a renderbuffer object with an EGLImage object) it is
|
||||||
important to allow wrapping an existing OpenGL renderbuffer object with a
|
important to allow wrapping an existing OpenGL renderbuffer object with a
|
||||||
QRhiRenderBuffer.
|
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
|
support for multisample textures, but does support multisample
|
||||||
renderbuffers).
|
renderbuffers).
|
||||||
|
|
||||||
When targeting a non-multisample texture, the layer() and level()
|
When targeting a non-multisample texture, the layer() and level() indicate
|
||||||
indicate the targeted layer (face index \c{0-5} for cubemaps) and mip
|
the targeted layer (face index \c{0-5} for cubemaps) and mip level. For 3D
|
||||||
level.
|
textures layer() specifies the slice (one 2D image within the 3D texture)
|
||||||
|
to render to.
|
||||||
|
|
||||||
When texture() or renderBuffer() is multisample, resolveTexture() can be
|
When texture() or renderBuffer() is multisample, resolveTexture() can be
|
||||||
set optionally. When set, samples are resolved automatically into that
|
set optionally. When set, samples are resolved automatically into that
|
||||||
@ -1815,6 +1825,13 @@ QRhiTextureUploadDescription::QRhiTextureUploadDescription(std::initializer_list
|
|||||||
\note The source and destination rectangles defined by pixelSize(),
|
\note The source and destination rectangles defined by pixelSize(),
|
||||||
sourceTopLeft(), and destinationTopLeft() must fit the source and
|
sourceTopLeft(), and destinationTopLeft() must fit the source and
|
||||||
destination textures, respectively. The behavior is undefined otherwise.
|
destination textures, respectively. The behavior is undefined otherwise.
|
||||||
|
|
||||||
|
With cubemap and 3D textures one face or slice can be copied at a time. The
|
||||||
|
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
|
\value ExternalOES The texture should use the GL_TEXTURE_EXTERNAL_OES
|
||||||
target with OpenGL. This flag is ignored with other graphics APIs.
|
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
|
\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_)
|
int sampleCount_, Flags flags_)
|
||||||
: QRhiResource(rhi),
|
: 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.
|
// 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)
|
int mipCount, int layerCount)
|
||||||
{
|
{
|
||||||
quint32 approxSize = 0;
|
quint32 approxSize = 0;
|
||||||
@ -4636,7 +4662,8 @@ quint32 QRhiImplementation::approxByteSizeForTexture(QRhiTexture::Format format,
|
|||||||
textureFormatInfo(format, size, nullptr, &byteSize, nullptr);
|
textureFormatInfo(format, size, nullptr, &byteSize, nullptr);
|
||||||
approxSize += byteSize;
|
approxSize += byteSize;
|
||||||
}
|
}
|
||||||
approxSize *= uint(layerCount);
|
approxSize *= depth; // 3D texture depth or 1 otherwise
|
||||||
|
approxSize *= uint(layerCount); // 6 for cubemaps or 1 otherwise
|
||||||
return approxSize;
|
return approxSize;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -5295,6 +5322,8 @@ void QRhiResourceUpdateBatch::copyTexture(QRhiTexture *dst, QRhiTexture *src, co
|
|||||||
|
|
||||||
\note Multisample textures cannot be read back.
|
\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
|
\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
|
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,
|
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,
|
int sampleCount,
|
||||||
QRhiTexture::Flags flags)
|
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,
|
UsedWithGenerateMips = 1 << 6,
|
||||||
UsedWithLoadStore = 1 << 7,
|
UsedWithLoadStore = 1 << 7,
|
||||||
UsedAsCompressedAtlas = 1 << 8,
|
UsedAsCompressedAtlas = 1 << 8,
|
||||||
ExternalOES = 1 << 9
|
ExternalOES = 1 << 9,
|
||||||
|
ThreeDimensional = 1 << 10
|
||||||
};
|
};
|
||||||
Q_DECLARE_FLAGS(Flags, Flag)
|
Q_DECLARE_FLAGS(Flags, Flag)
|
||||||
|
|
||||||
@ -819,6 +820,9 @@ public:
|
|||||||
QSize pixelSize() const { return m_pixelSize; }
|
QSize pixelSize() const { return m_pixelSize; }
|
||||||
void setPixelSize(const QSize &sz) { m_pixelSize = sz; }
|
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; }
|
Flags flags() const { return m_flags; }
|
||||||
void setFlags(Flags f) { m_flags = f; }
|
void setFlags(Flags f) { m_flags = f; }
|
||||||
|
|
||||||
@ -831,10 +835,11 @@ public:
|
|||||||
virtual void setNativeLayout(int layout);
|
virtual void setNativeLayout(int layout);
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
QRhiTexture(QRhiImplementation *rhi, Format format_, const QSize &pixelSize_,
|
QRhiTexture(QRhiImplementation *rhi, Format format_, const QSize &pixelSize_, int depth_,
|
||||||
int sampleCount_, Flags flags_);
|
int sampleCount_, Flags flags_);
|
||||||
Format m_format;
|
Format m_format;
|
||||||
QSize m_pixelSize;
|
QSize m_pixelSize;
|
||||||
|
int m_depth;
|
||||||
int m_sampleCount;
|
int m_sampleCount;
|
||||||
Flags m_flags;
|
Flags m_flags;
|
||||||
};
|
};
|
||||||
@ -1540,7 +1545,9 @@ public:
|
|||||||
ReadBackAnyTextureFormat,
|
ReadBackAnyTextureFormat,
|
||||||
PipelineCacheDataLoadSave,
|
PipelineCacheDataLoadSave,
|
||||||
ImageDataStride,
|
ImageDataStride,
|
||||||
RenderBufferImport
|
RenderBufferImport,
|
||||||
|
ThreeDimensionalTextures,
|
||||||
|
RenderTo3DTextureSlice
|
||||||
};
|
};
|
||||||
|
|
||||||
enum BeginFrameFlag {
|
enum BeginFrameFlag {
|
||||||
@ -1600,6 +1607,11 @@ public:
|
|||||||
int sampleCount = 1,
|
int sampleCount = 1,
|
||||||
QRhiTexture::Flags flags = {});
|
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 *newSampler(QRhiSampler::Filter magFilter,
|
||||||
QRhiSampler::Filter minFilter,
|
QRhiSampler::Filter minFilter,
|
||||||
QRhiSampler::Filter mipmapMode,
|
QRhiSampler::Filter mipmapMode,
|
||||||
@ -1646,8 +1658,7 @@ public:
|
|||||||
|
|
||||||
QRhiProfiler *profiler();
|
QRhiProfiler *profiler();
|
||||||
|
|
||||||
static const int MAX_LAYERS = 6; // cubemaps only
|
static const int MAX_MIP_LEVELS = 16; // a width and/or height of 65536 should be enough for everyone
|
||||||
static const int MAX_LEVELS = 16; // a width and/or height of 65536 should be enough for everyone
|
|
||||||
|
|
||||||
void releaseCachedResources();
|
void releaseCachedResources();
|
||||||
|
|
||||||
|
@ -87,6 +87,7 @@ public:
|
|||||||
QRhiTexture::Format backingFormatHint) = 0;
|
QRhiTexture::Format backingFormatHint) = 0;
|
||||||
virtual QRhiTexture *createTexture(QRhiTexture::Format format,
|
virtual QRhiTexture *createTexture(QRhiTexture::Format format,
|
||||||
const QSize &pixelSize,
|
const QSize &pixelSize,
|
||||||
|
int depth,
|
||||||
int sampleCount,
|
int sampleCount,
|
||||||
QRhiTexture::Flags flags) = 0;
|
QRhiTexture::Flags flags) = 0;
|
||||||
virtual QRhiSampler *createSampler(QRhiSampler::Filter magFilter,
|
virtual QRhiSampler *createSampler(QRhiSampler::Filter magFilter,
|
||||||
@ -180,7 +181,7 @@ public:
|
|||||||
QSize *blockDim) const;
|
QSize *blockDim) const;
|
||||||
void textureFormatInfo(QRhiTexture::Format format, const QSize &size,
|
void textureFormatInfo(QRhiTexture::Format format, const QSize &size,
|
||||||
quint32 *bpl, quint32 *byteSize, quint32 *bytesPerPixel) const;
|
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);
|
int mipCount, int layerCount);
|
||||||
|
|
||||||
QRhiProfilerPrivate *profilerPrivateOrNull()
|
QRhiProfilerPrivate *profilerPrivateOrNull()
|
||||||
@ -447,7 +448,8 @@ public:
|
|||||||
// In the backend this can then end up, where applicable, as a
|
// In the backend this can then end up, where applicable, as a
|
||||||
// single, batched copy operation with only one set of barriers.
|
// single, batched copy operation with only one set of barriers.
|
||||||
// This helps when doing for example glyph cache fills.
|
// 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;
|
QRhiTexture *src;
|
||||||
QRhiTextureCopyDescription desc;
|
QRhiTextureCopyDescription desc;
|
||||||
QRhiReadbackDescription rb;
|
QRhiReadbackDescription rb;
|
||||||
@ -458,6 +460,12 @@ public:
|
|||||||
TextureOp op = {};
|
TextureOp op = {};
|
||||||
op.type = Upload;
|
op.type = Upload;
|
||||||
op.dst = tex;
|
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)
|
for (auto it = desc.cbeginEntries(), itEnd = desc.cendEntries(); it != itEnd; ++it)
|
||||||
op.subresDesc[it->layer()][it->level()].append(it->description());
|
op.subresDesc[it->layer()][it->level()].append(it->description());
|
||||||
return op;
|
return op;
|
||||||
|
@ -546,6 +546,10 @@ bool QRhiD3D11::isFeatureSupported(QRhi::Feature feature) const
|
|||||||
return true;
|
return true;
|
||||||
case QRhi::RenderBufferImport:
|
case QRhi::RenderBufferImport:
|
||||||
return false;
|
return false;
|
||||||
|
case QRhi::ThreeDimensionalTextures:
|
||||||
|
return true;
|
||||||
|
case QRhi::RenderTo3DTextureSlice:
|
||||||
|
return true;
|
||||||
default:
|
default:
|
||||||
Q_UNREACHABLE();
|
Q_UNREACHABLE();
|
||||||
return false;
|
return false;
|
||||||
@ -633,10 +637,11 @@ QRhiRenderBuffer *QRhiD3D11::createRenderBuffer(QRhiRenderBuffer::Type type, con
|
|||||||
return new QD3D11RenderBuffer(this, type, pixelSize, sampleCount, flags, backingFormatHint);
|
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)
|
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,
|
QRhiSampler *QRhiD3D11::createSampler(QRhiSampler::Filter magFilter, QRhiSampler::Filter minFilter,
|
||||||
@ -1351,17 +1356,18 @@ QRhi::FrameOpResult QRhiD3D11::finish()
|
|||||||
void QRhiD3D11::enqueueSubresUpload(QD3D11Texture *texD, QD3D11CommandBuffer *cbD,
|
void QRhiD3D11::enqueueSubresUpload(QD3D11Texture *texD, QD3D11CommandBuffer *cbD,
|
||||||
int layer, int level, const QRhiTextureSubresourceUploadDescription &subresDesc)
|
int layer, int level, const QRhiTextureSubresourceUploadDescription &subresDesc)
|
||||||
{
|
{
|
||||||
UINT subres = D3D11CalcSubresource(UINT(level), UINT(layer), texD->mipLevelCount);
|
const bool is3D = texD->m_flags.testFlag(QRhiTexture::ThreeDimensional);
|
||||||
const QPoint dp = subresDesc.destinationTopLeft();
|
UINT subres = D3D11CalcSubresource(UINT(level), is3D ? 0u : UINT(layer), texD->mipLevelCount);
|
||||||
D3D11_BOX box;
|
D3D11_BOX box;
|
||||||
box.front = 0;
|
box.front = is3D ? UINT(layer) : 0u;
|
||||||
// back, right, bottom are exclusive
|
// back, right, bottom are exclusive
|
||||||
box.back = 1;
|
box.back = box.front + 1;
|
||||||
QD3D11CommandBuffer::Command &cmd(cbD->commands.get());
|
QD3D11CommandBuffer::Command &cmd(cbD->commands.get());
|
||||||
cmd.cmd = QD3D11CommandBuffer::Command::UpdateSubRes;
|
cmd.cmd = QD3D11CommandBuffer::Command::UpdateSubRes;
|
||||||
cmd.args.updateSubRes.dst = texD->tex;
|
cmd.args.updateSubRes.dst = texD->textureResource();
|
||||||
cmd.args.updateSubRes.dstSubRes = subres;
|
cmd.args.updateSubRes.dstSubRes = subres;
|
||||||
|
|
||||||
|
const QPoint dp = subresDesc.destinationTopLeft();
|
||||||
if (!subresDesc.image().isNull()) {
|
if (!subresDesc.image().isNull()) {
|
||||||
QImage img = subresDesc.image();
|
QImage img = subresDesc.image();
|
||||||
QSize size = img.size();
|
QSize size = img.size();
|
||||||
@ -1488,6 +1494,7 @@ void QRhiD3D11::enqueueResourceUpdates(QRhiCommandBuffer *cb, QRhiResourceUpdate
|
|||||||
cmd.args.copySubRes.dstSubRes = 0;
|
cmd.args.copySubRes.dstSubRes = 0;
|
||||||
cmd.args.copySubRes.dstX = 0;
|
cmd.args.copySubRes.dstX = 0;
|
||||||
cmd.args.copySubRes.dstY = 0;
|
cmd.args.copySubRes.dstY = 0;
|
||||||
|
cmd.args.copySubRes.dstZ = 0;
|
||||||
cmd.args.copySubRes.src = bufD->buffer;
|
cmd.args.copySubRes.src = bufD->buffer;
|
||||||
cmd.args.copySubRes.srcSubRes = 0;
|
cmd.args.copySubRes.srcSubRes = 0;
|
||||||
cmd.args.copySubRes.hasSrcBox = true;
|
cmd.args.copySubRes.hasSrcBox = true;
|
||||||
@ -1508,8 +1515,8 @@ void QRhiD3D11::enqueueResourceUpdates(QRhiCommandBuffer *cb, QRhiResourceUpdate
|
|||||||
const QRhiResourceUpdateBatchPrivate::TextureOp &u(ud->textureOps[opIdx]);
|
const QRhiResourceUpdateBatchPrivate::TextureOp &u(ud->textureOps[opIdx]);
|
||||||
if (u.type == QRhiResourceUpdateBatchPrivate::TextureOp::Upload) {
|
if (u.type == QRhiResourceUpdateBatchPrivate::TextureOp::Upload) {
|
||||||
QD3D11Texture *texD = QRHI_RES(QD3D11Texture, u.dst);
|
QD3D11Texture *texD = QRHI_RES(QD3D11Texture, u.dst);
|
||||||
for (int layer = 0; layer < QRhi::MAX_LAYERS; ++layer) {
|
for (int layer = 0, maxLayer = u.subresDesc.count(); layer < maxLayer; ++layer) {
|
||||||
for (int level = 0; level < QRhi::MAX_LEVELS; ++level) {
|
for (int level = 0; level < QRhi::MAX_MIP_LEVELS; ++level) {
|
||||||
for (const QRhiTextureSubresourceUploadDescription &subresDesc : qAsConst(u.subresDesc[layer][level]))
|
for (const QRhiTextureSubresourceUploadDescription &subresDesc : qAsConst(u.subresDesc[layer][level]))
|
||||||
enqueueSubresUpload(texD, cbD, layer, level, subresDesc);
|
enqueueSubresUpload(texD, cbD, layer, level, subresDesc);
|
||||||
}
|
}
|
||||||
@ -1518,8 +1525,10 @@ void QRhiD3D11::enqueueResourceUpdates(QRhiCommandBuffer *cb, QRhiResourceUpdate
|
|||||||
Q_ASSERT(u.src && u.dst);
|
Q_ASSERT(u.src && u.dst);
|
||||||
QD3D11Texture *srcD = QRHI_RES(QD3D11Texture, u.src);
|
QD3D11Texture *srcD = QRHI_RES(QD3D11Texture, u.src);
|
||||||
QD3D11Texture *dstD = QRHI_RES(QD3D11Texture, u.dst);
|
QD3D11Texture *dstD = QRHI_RES(QD3D11Texture, u.dst);
|
||||||
UINT srcSubRes = D3D11CalcSubresource(UINT(u.desc.sourceLevel()), UINT(u.desc.sourceLayer()), srcD->mipLevelCount);
|
const bool srcIs3D = srcD->m_flags.testFlag(QRhiTexture::ThreeDimensional);
|
||||||
UINT dstSubRes = D3D11CalcSubresource(UINT(u.desc.destinationLevel()), UINT(u.desc.destinationLayer()), dstD->mipLevelCount);
|
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 QPoint dp = u.desc.destinationTopLeft();
|
||||||
const QSize mipSize = q->sizeForMipLevel(u.desc.sourceLevel(), srcD->m_pixelSize);
|
const QSize mipSize = q->sizeForMipLevel(u.desc.sourceLevel(), srcD->m_pixelSize);
|
||||||
const QSize copySize = u.desc.pixelSize().isEmpty() ? mipSize : u.desc.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;
|
D3D11_BOX srcBox;
|
||||||
srcBox.left = UINT(sp.x());
|
srcBox.left = UINT(sp.x());
|
||||||
srcBox.top = UINT(sp.y());
|
srcBox.top = UINT(sp.y());
|
||||||
srcBox.front = 0;
|
srcBox.front = srcIs3D ? UINT(u.desc.sourceLayer()) : 0u;
|
||||||
// back, right, bottom are exclusive
|
// back, right, bottom are exclusive
|
||||||
srcBox.right = srcBox.left + UINT(copySize.width());
|
srcBox.right = srcBox.left + UINT(copySize.width());
|
||||||
srcBox.bottom = srcBox.top + UINT(copySize.height());
|
srcBox.bottom = srcBox.top + UINT(copySize.height());
|
||||||
srcBox.back = 1;
|
srcBox.back = srcBox.front + 1;
|
||||||
QD3D11CommandBuffer::Command &cmd(cbD->commands.get());
|
QD3D11CommandBuffer::Command &cmd(cbD->commands.get());
|
||||||
cmd.cmd = QD3D11CommandBuffer::Command::CopySubRes;
|
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.dstSubRes = dstSubRes;
|
||||||
cmd.args.copySubRes.dstX = UINT(dp.x());
|
cmd.args.copySubRes.dstX = UINT(dp.x());
|
||||||
cmd.args.copySubRes.dstY = UINT(dp.y());
|
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.srcSubRes = srcSubRes;
|
||||||
cmd.args.copySubRes.hasSrcBox = true;
|
cmd.args.copySubRes.hasSrcBox = true;
|
||||||
cmd.args.copySubRes.srcBox = srcBox;
|
cmd.args.copySubRes.srcBox = srcBox;
|
||||||
@ -1560,7 +1570,13 @@ void QRhiD3D11::enqueueResourceUpdates(QRhiCommandBuffer *cb, QRhiResourceUpdate
|
|||||||
qWarning("Multisample texture cannot be read back");
|
qWarning("Multisample texture cannot be read back");
|
||||||
continue;
|
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;
|
dxgiFormat = texD->dxgiFormat;
|
||||||
pixelSize = q->sizeForMipLevel(u.rb.level(), texD->m_pixelSize);
|
pixelSize = q->sizeForMipLevel(u.rb.level(), texD->m_pixelSize);
|
||||||
format = texD->m_format;
|
format = texD->m_format;
|
||||||
@ -1616,6 +1632,7 @@ void QRhiD3D11::enqueueResourceUpdates(QRhiCommandBuffer *cb, QRhiResourceUpdate
|
|||||||
cmd.args.copySubRes.dstSubRes = 0;
|
cmd.args.copySubRes.dstSubRes = 0;
|
||||||
cmd.args.copySubRes.dstX = 0;
|
cmd.args.copySubRes.dstX = 0;
|
||||||
cmd.args.copySubRes.dstY = 0;
|
cmd.args.copySubRes.dstY = 0;
|
||||||
|
cmd.args.copySubRes.dstZ = 0;
|
||||||
cmd.args.copySubRes.src = src;
|
cmd.args.copySubRes.src = src;
|
||||||
cmd.args.copySubRes.srcSubRes = subres;
|
cmd.args.copySubRes.srcSubRes = subres;
|
||||||
cmd.args.copySubRes.hasSrcBox = false;
|
cmd.args.copySubRes.hasSrcBox = false;
|
||||||
@ -1791,12 +1808,12 @@ void QRhiD3D11::endPass(QRhiCommandBuffer *cb, QRhiResourceUpdateBatch *resource
|
|||||||
Q_ASSERT(srcTexD || srcRbD);
|
Q_ASSERT(srcTexD || srcRbD);
|
||||||
QD3D11CommandBuffer::Command &cmd(cbD->commands.get());
|
QD3D11CommandBuffer::Command &cmd(cbD->commands.get());
|
||||||
cmd.cmd = QD3D11CommandBuffer::Command::ResolveSubRes;
|
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()),
|
cmd.args.resolveSubRes.dstSubRes = D3D11CalcSubresource(UINT(colorAtt.resolveLevel()),
|
||||||
UINT(colorAtt.resolveLayer()),
|
UINT(colorAtt.resolveLayer()),
|
||||||
dstTexD->mipLevelCount);
|
dstTexD->mipLevelCount);
|
||||||
if (srcTexD) {
|
if (srcTexD) {
|
||||||
cmd.args.resolveSubRes.src = srcTexD->tex;
|
cmd.args.resolveSubRes.src = srcTexD->textureResource();
|
||||||
if (srcTexD->dxgiFormat != dstTexD->dxgiFormat) {
|
if (srcTexD->dxgiFormat != dstTexD->dxgiFormat) {
|
||||||
qWarning("Resolve source (%d) and destination (%d) formats do not match",
|
qWarning("Resolve source (%d) and destination (%d) formats do not match",
|
||||||
int(srcTexD->dxgiFormat), int(dstTexD->dxgiFormat));
|
int(srcTexD->dxgiFormat), int(dstTexD->dxgiFormat));
|
||||||
@ -2582,7 +2599,7 @@ void QRhiD3D11::executeCommandBuffer(QD3D11CommandBuffer *cbD, QD3D11SwapChain *
|
|||||||
break;
|
break;
|
||||||
case QD3D11CommandBuffer::Command::CopySubRes:
|
case QD3D11CommandBuffer::Command::CopySubRes:
|
||||||
context->CopySubresourceRegion(cmd.args.copySubRes.dst, cmd.args.copySubRes.dstSubRes,
|
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.src, cmd.args.copySubRes.srcSubRes,
|
||||||
cmd.args.copySubRes.hasSrcBox ? &cmd.args.copySubRes.srcBox : nullptr);
|
cmd.args.copySubRes.hasSrcBox ? &cmd.args.copySubRes.srcBox : nullptr);
|
||||||
break;
|
break;
|
||||||
@ -2885,11 +2902,11 @@ QRhiTexture::Format QD3D11RenderBuffer::backingFormat() const
|
|||||||
return m_type == Color ? QRhiTexture::RGBA8 : QRhiTexture::UnknownFormat;
|
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)
|
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;
|
perLevelViews[i] = nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2900,7 +2917,7 @@ QD3D11Texture::~QD3D11Texture()
|
|||||||
|
|
||||||
void QD3D11Texture::destroy()
|
void QD3D11Texture::destroy()
|
||||||
{
|
{
|
||||||
if (!tex)
|
if (!tex && !tex3D)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (srv) {
|
if (srv) {
|
||||||
@ -2908,17 +2925,22 @@ void QD3D11Texture::destroy()
|
|||||||
srv = nullptr;
|
srv = nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (int i = 0; i < QRhi::MAX_LEVELS; ++i) {
|
for (int i = 0; i < QRhi::MAX_MIP_LEVELS; ++i) {
|
||||||
if (perLevelViews[i]) {
|
if (perLevelViews[i]) {
|
||||||
perLevelViews[i]->Release();
|
perLevelViews[i]->Release();
|
||||||
perLevelViews[i] = nullptr;
|
perLevelViews[i] = nullptr;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (owns)
|
if (owns) {
|
||||||
tex->Release();
|
if (tex)
|
||||||
|
tex->Release();
|
||||||
|
if (tex3D)
|
||||||
|
tex3D->Release();
|
||||||
|
}
|
||||||
|
|
||||||
tex = nullptr;
|
tex = nullptr;
|
||||||
|
tex3D = nullptr;
|
||||||
|
|
||||||
QRHI_RES_RHI(QRhiD3D11);
|
QRHI_RES_RHI(QRhiD3D11);
|
||||||
QRHI_PROF;
|
QRHI_PROF;
|
||||||
@ -2962,12 +2984,13 @@ static inline DXGI_FORMAT toD3DDepthTextureDSVFormat(QRhiTexture::Format format)
|
|||||||
|
|
||||||
bool QD3D11Texture::prepareCreate(QSize *adjustedSize)
|
bool QD3D11Texture::prepareCreate(QSize *adjustedSize)
|
||||||
{
|
{
|
||||||
if (tex)
|
if (tex || tex3D)
|
||||||
destroy();
|
destroy();
|
||||||
|
|
||||||
const QSize size = m_pixelSize.isEmpty() ? QSize(1, 1) : m_pixelSize;
|
const QSize size = m_pixelSize.isEmpty() ? QSize(1, 1) : m_pixelSize;
|
||||||
const bool isDepth = isDepthTextureFormat(m_format);
|
const bool isDepth = isDepthTextureFormat(m_format);
|
||||||
const bool isCube = m_flags.testFlag(CubeMap);
|
const bool isCube = m_flags.testFlag(CubeMap);
|
||||||
|
const bool is3D = m_flags.testFlag(ThreeDimensional);
|
||||||
const bool hasMipMaps = m_flags.testFlag(MipMapped);
|
const bool hasMipMaps = m_flags.testFlag(MipMapped);
|
||||||
|
|
||||||
QRHI_RES_RHI(QRhiD3D11);
|
QRHI_RES_RHI(QRhiD3D11);
|
||||||
@ -2979,6 +3002,10 @@ bool QD3D11Texture::prepareCreate(QSize *adjustedSize)
|
|||||||
qWarning("Cubemap texture cannot be multisample");
|
qWarning("Cubemap texture cannot be multisample");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
if (is3D) {
|
||||||
|
qWarning("3D texture cannot be multisample");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
if (hasMipMaps) {
|
if (hasMipMaps) {
|
||||||
qWarning("Multisample texture cannot have mipmaps");
|
qWarning("Multisample texture cannot have mipmaps");
|
||||||
return false;
|
return false;
|
||||||
@ -2988,6 +3015,15 @@ bool QD3D11Texture::prepareCreate(QSize *adjustedSize)
|
|||||||
qWarning("Depth texture cannot have mipmaps");
|
qWarning("Depth texture cannot have mipmaps");
|
||||||
return false;
|
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)
|
if (adjustedSize)
|
||||||
*adjustedSize = size;
|
*adjustedSize = size;
|
||||||
@ -3000,6 +3036,7 @@ bool QD3D11Texture::finishCreate()
|
|||||||
QRHI_RES_RHI(QRhiD3D11);
|
QRHI_RES_RHI(QRhiD3D11);
|
||||||
const bool isDepth = isDepthTextureFormat(m_format);
|
const bool isDepth = isDepthTextureFormat(m_format);
|
||||||
const bool isCube = m_flags.testFlag(CubeMap);
|
const bool isCube = m_flags.testFlag(CubeMap);
|
||||||
|
const bool is3D = m_flags.testFlag(ThreeDimensional);
|
||||||
|
|
||||||
D3D11_SHADER_RESOURCE_VIEW_DESC srvDesc;
|
D3D11_SHADER_RESOURCE_VIEW_DESC srvDesc;
|
||||||
memset(&srvDesc, 0, sizeof(srvDesc));
|
memset(&srvDesc, 0, sizeof(srvDesc));
|
||||||
@ -3010,13 +3047,16 @@ bool QD3D11Texture::finishCreate()
|
|||||||
} else {
|
} else {
|
||||||
if (sampleDesc.Count > 1) {
|
if (sampleDesc.Count > 1) {
|
||||||
srvDesc.ViewDimension = D3D11_SRV_DIMENSION_TEXTURE2DMS;
|
srvDesc.ViewDimension = D3D11_SRV_DIMENSION_TEXTURE2DMS;
|
||||||
|
} else if (is3D) {
|
||||||
|
srvDesc.ViewDimension = D3D11_SRV_DIMENSION_TEXTURE3D;
|
||||||
|
srvDesc.Texture3D.MipLevels = mipLevelCount;
|
||||||
} else {
|
} else {
|
||||||
srvDesc.ViewDimension = D3D11_SRV_DIMENSION_TEXTURE2D;
|
srvDesc.ViewDimension = D3D11_SRV_DIMENSION_TEXTURE2D;
|
||||||
srvDesc.Texture2D.MipLevels = mipLevelCount;
|
srvDesc.Texture2D.MipLevels = mipLevelCount;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
HRESULT hr = rhiD->dev->CreateShaderResourceView(tex, &srvDesc, &srv);
|
HRESULT hr = rhiD->dev->CreateShaderResourceView(textureResource(), &srvDesc, &srv);
|
||||||
if (FAILED(hr)) {
|
if (FAILED(hr)) {
|
||||||
qWarning("Failed to create srv: %s", qPrintable(comErrorMessage(hr)));
|
qWarning("Failed to create srv: %s", qPrintable(comErrorMessage(hr)));
|
||||||
return false;
|
return false;
|
||||||
@ -3034,6 +3074,7 @@ bool QD3D11Texture::create()
|
|||||||
|
|
||||||
const bool isDepth = isDepthTextureFormat(m_format);
|
const bool isDepth = isDepthTextureFormat(m_format);
|
||||||
const bool isCube = m_flags.testFlag(CubeMap);
|
const bool isCube = m_flags.testFlag(CubeMap);
|
||||||
|
const bool is3D = m_flags.testFlag(ThreeDimensional);
|
||||||
|
|
||||||
uint bindFlags = D3D11_BIND_SHADER_RESOURCE;
|
uint bindFlags = D3D11_BIND_SHADER_RESOURCE;
|
||||||
uint miscFlags = isCube ? D3D11_RESOURCE_MISC_TEXTURECUBE : 0;
|
uint miscFlags = isCube ? D3D11_RESOURCE_MISC_TEXTURECUBE : 0;
|
||||||
@ -3054,31 +3095,51 @@ bool QD3D11Texture::create()
|
|||||||
if (m_flags.testFlag(UsedWithLoadStore))
|
if (m_flags.testFlag(UsedWithLoadStore))
|
||||||
bindFlags |= D3D11_BIND_UNORDERED_ACCESS;
|
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);
|
QRHI_RES_RHI(QRhiD3D11);
|
||||||
HRESULT hr = rhiD->dev->CreateTexture2D(&desc, nullptr, &tex);
|
if (!is3D) {
|
||||||
if (FAILED(hr)) {
|
D3D11_TEXTURE2D_DESC desc;
|
||||||
qWarning("Failed to create texture: %s", qPrintable(comErrorMessage(hr)));
|
memset(&desc, 0, sizeof(desc));
|
||||||
return false;
|
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())
|
if (!finishCreate())
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
if (!m_objectName.isEmpty())
|
|
||||||
tex->SetPrivateData(WKPDID_D3DDebugObjectName, UINT(m_objectName.size()), m_objectName.constData());
|
|
||||||
|
|
||||||
QRHI_PROF;
|
QRHI_PROF;
|
||||||
QRHI_PROF_F(newTexture(this, true, int(mipLevelCount), isCube ? 6 : 1, int(sampleDesc.Count)));
|
QRHI_PROF_F(newTexture(this, true, int(mipLevelCount), isCube ? 6 : 1, int(sampleDesc.Count)));
|
||||||
|
|
||||||
@ -3089,14 +3150,16 @@ bool QD3D11Texture::create()
|
|||||||
|
|
||||||
bool QD3D11Texture::createFrom(QRhiTexture::NativeTexture src)
|
bool QD3D11Texture::createFrom(QRhiTexture::NativeTexture src)
|
||||||
{
|
{
|
||||||
ID3D11Texture2D *srcTex = reinterpret_cast<ID3D11Texture2D *>(src.object);
|
if (!src.object)
|
||||||
if (srcTex == nullptr)
|
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
if (!prepareCreate())
|
if (!prepareCreate())
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
tex = srcTex;
|
if (m_flags.testFlag(ThreeDimensional))
|
||||||
|
tex3D = reinterpret_cast<ID3D11Texture3D *>(src.object);
|
||||||
|
else
|
||||||
|
tex = reinterpret_cast<ID3D11Texture2D *>(src.object);
|
||||||
|
|
||||||
if (!finishCreate())
|
if (!finishCreate())
|
||||||
return false;
|
return false;
|
||||||
@ -3112,7 +3175,7 @@ bool QD3D11Texture::createFrom(QRhiTexture::NativeTexture src)
|
|||||||
|
|
||||||
QRhiTexture::NativeTexture QD3D11Texture::nativeTexture()
|
QRhiTexture::NativeTexture QD3D11Texture::nativeTexture()
|
||||||
{
|
{
|
||||||
return {quint64(tex), 0};
|
return { quint64(textureResource()), 0 };
|
||||||
}
|
}
|
||||||
|
|
||||||
ID3D11UnorderedAccessView *QD3D11Texture::unorderedAccessViewForLevel(int level)
|
ID3D11UnorderedAccessView *QD3D11Texture::unorderedAccessViewForLevel(int level)
|
||||||
@ -3121,6 +3184,7 @@ ID3D11UnorderedAccessView *QD3D11Texture::unorderedAccessViewForLevel(int level)
|
|||||||
return perLevelViews[level];
|
return perLevelViews[level];
|
||||||
|
|
||||||
const bool isCube = m_flags.testFlag(CubeMap);
|
const bool isCube = m_flags.testFlag(CubeMap);
|
||||||
|
const bool is3D = m_flags.testFlag(ThreeDimensional);
|
||||||
D3D11_UNORDERED_ACCESS_VIEW_DESC desc;
|
D3D11_UNORDERED_ACCESS_VIEW_DESC desc;
|
||||||
memset(&desc, 0, sizeof(desc));
|
memset(&desc, 0, sizeof(desc));
|
||||||
desc.Format = dxgiFormat;
|
desc.Format = dxgiFormat;
|
||||||
@ -3129,6 +3193,9 @@ ID3D11UnorderedAccessView *QD3D11Texture::unorderedAccessViewForLevel(int level)
|
|||||||
desc.Texture2DArray.MipSlice = UINT(level);
|
desc.Texture2DArray.MipSlice = UINT(level);
|
||||||
desc.Texture2DArray.FirstArraySlice = 0;
|
desc.Texture2DArray.FirstArraySlice = 0;
|
||||||
desc.Texture2DArray.ArraySize = 6;
|
desc.Texture2DArray.ArraySize = 6;
|
||||||
|
} else if (is3D) {
|
||||||
|
desc.ViewDimension = D3D11_UAV_DIMENSION_TEXTURE3D;
|
||||||
|
desc.Texture3D.MipSlice = UINT(level);
|
||||||
} else {
|
} else {
|
||||||
desc.ViewDimension = D3D11_UAV_DIMENSION_TEXTURE2D;
|
desc.ViewDimension = D3D11_UAV_DIMENSION_TEXTURE2D;
|
||||||
desc.Texture2D.MipSlice = UINT(level);
|
desc.Texture2D.MipSlice = UINT(level);
|
||||||
@ -3136,7 +3203,7 @@ ID3D11UnorderedAccessView *QD3D11Texture::unorderedAccessViewForLevel(int level)
|
|||||||
|
|
||||||
QRHI_RES_RHI(QRhiD3D11);
|
QRHI_RES_RHI(QRhiD3D11);
|
||||||
ID3D11UnorderedAccessView *uav = nullptr;
|
ID3D11UnorderedAccessView *uav = nullptr;
|
||||||
HRESULT hr = rhiD->dev->CreateUnorderedAccessView(tex, &desc, &uav);
|
HRESULT hr = rhiD->dev->CreateUnorderedAccessView(textureResource(), &desc, &uav);
|
||||||
if (FAILED(hr)) {
|
if (FAILED(hr)) {
|
||||||
qWarning("Failed to create UAV: %s", qPrintable(comErrorMessage(hr)));
|
qWarning("Failed to create UAV: %s", qPrintable(comErrorMessage(hr)));
|
||||||
return nullptr;
|
return nullptr;
|
||||||
@ -3404,6 +3471,11 @@ bool QD3D11TextureRenderTarget::create()
|
|||||||
rtvDesc.Texture2DArray.MipSlice = UINT(colorAtt.level());
|
rtvDesc.Texture2DArray.MipSlice = UINT(colorAtt.level());
|
||||||
rtvDesc.Texture2DArray.FirstArraySlice = UINT(colorAtt.layer());
|
rtvDesc.Texture2DArray.FirstArraySlice = UINT(colorAtt.layer());
|
||||||
rtvDesc.Texture2DArray.ArraySize = 1;
|
rtvDesc.Texture2DArray.ArraySize = 1;
|
||||||
|
} else if (texD->flags().testFlag(QRhiTexture::ThreeDimensional)) {
|
||||||
|
rtvDesc.ViewDimension = D3D11_RTV_DIMENSION_TEXTURE3D;
|
||||||
|
rtvDesc.Texture3D.MipSlice = UINT(colorAtt.level());
|
||||||
|
rtvDesc.Texture3D.FirstWSlice = UINT(colorAtt.layer());
|
||||||
|
rtvDesc.Texture3D.WSize = 1;
|
||||||
} else {
|
} else {
|
||||||
if (texD->sampleDesc.Count > 1) {
|
if (texD->sampleDesc.Count > 1) {
|
||||||
rtvDesc.ViewDimension = D3D11_RTV_DIMENSION_TEXTURE2DMS;
|
rtvDesc.ViewDimension = D3D11_RTV_DIMENSION_TEXTURE2DMS;
|
||||||
@ -3412,7 +3484,7 @@ bool QD3D11TextureRenderTarget::create()
|
|||||||
rtvDesc.Texture2D.MipSlice = UINT(colorAtt.level());
|
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)) {
|
if (FAILED(hr)) {
|
||||||
qWarning("Failed to create rtv: %s", qPrintable(comErrorMessage(hr)));
|
qWarning("Failed to create rtv: %s", qPrintable(comErrorMessage(hr)));
|
||||||
return false;
|
return false;
|
||||||
|
@ -101,7 +101,7 @@ struct QD3D11RenderBuffer : public QRhiRenderBuffer
|
|||||||
|
|
||||||
struct QD3D11Texture : public QRhiTexture
|
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);
|
int sampleCount, Flags flags);
|
||||||
~QD3D11Texture();
|
~QD3D11Texture();
|
||||||
void destroy() override;
|
void destroy() override;
|
||||||
@ -112,14 +112,21 @@ struct QD3D11Texture : public QRhiTexture
|
|||||||
bool prepareCreate(QSize *adjustedSize = nullptr);
|
bool prepareCreate(QSize *adjustedSize = nullptr);
|
||||||
bool finishCreate();
|
bool finishCreate();
|
||||||
ID3D11UnorderedAccessView *unorderedAccessViewForLevel(int level);
|
ID3D11UnorderedAccessView *unorderedAccessViewForLevel(int level);
|
||||||
|
ID3D11Resource *textureResource() const
|
||||||
|
{
|
||||||
|
if (tex)
|
||||||
|
return tex;
|
||||||
|
return tex3D;
|
||||||
|
}
|
||||||
|
|
||||||
ID3D11Texture2D *tex = nullptr;
|
ID3D11Texture2D *tex = nullptr;
|
||||||
|
ID3D11Texture3D *tex3D = nullptr;
|
||||||
bool owns = true;
|
bool owns = true;
|
||||||
ID3D11ShaderResourceView *srv = nullptr;
|
ID3D11ShaderResourceView *srv = nullptr;
|
||||||
DXGI_FORMAT dxgiFormat;
|
DXGI_FORMAT dxgiFormat;
|
||||||
uint mipLevelCount = 0;
|
uint mipLevelCount = 0;
|
||||||
DXGI_SAMPLE_DESC sampleDesc;
|
DXGI_SAMPLE_DESC sampleDesc;
|
||||||
ID3D11UnorderedAccessView *perLevelViews[QRhi::MAX_LEVELS];
|
ID3D11UnorderedAccessView *perLevelViews[QRhi::MAX_MIP_LEVELS];
|
||||||
uint generation = 0;
|
uint generation = 0;
|
||||||
friend class QRhiD3D11;
|
friend class QRhiD3D11;
|
||||||
};
|
};
|
||||||
@ -439,6 +446,7 @@ struct QD3D11CommandBuffer : public QRhiCommandBuffer
|
|||||||
UINT dstSubRes;
|
UINT dstSubRes;
|
||||||
UINT dstX;
|
UINT dstX;
|
||||||
UINT dstY;
|
UINT dstY;
|
||||||
|
UINT dstZ;
|
||||||
ID3D11Resource *src;
|
ID3D11Resource *src;
|
||||||
UINT srcSubRes;
|
UINT srcSubRes;
|
||||||
bool hasSrcBox;
|
bool hasSrcBox;
|
||||||
@ -596,6 +604,7 @@ public:
|
|||||||
QRhiTexture::Format backingFormatHint) override;
|
QRhiTexture::Format backingFormatHint) override;
|
||||||
QRhiTexture *createTexture(QRhiTexture::Format format,
|
QRhiTexture *createTexture(QRhiTexture::Format format,
|
||||||
const QSize &pixelSize,
|
const QSize &pixelSize,
|
||||||
|
int depth,
|
||||||
int sampleCount,
|
int sampleCount,
|
||||||
QRhiTexture::Flags flags) override;
|
QRhiTexture::Flags flags) override;
|
||||||
QRhiSampler *createSampler(QRhiSampler::Filter magFilter,
|
QRhiSampler *createSampler(QRhiSampler::Filter magFilter,
|
||||||
|
@ -348,6 +348,14 @@ QT_BEGIN_NAMESPACE
|
|||||||
#define GL_UNPACK_ROW_LENGTH 0x0CF2
|
#define GL_UNPACK_ROW_LENGTH 0x0CF2
|
||||||
#endif
|
#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.
|
Constructs a new QRhiGles2InitParams.
|
||||||
|
|
||||||
@ -621,6 +629,8 @@ bool QRhiGles2::create(QRhi::Flags flags)
|
|||||||
caps.programBinary = false;
|
caps.programBinary = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
caps.texture3D = caps.ctxMajor >= 3; // 3.0
|
||||||
|
|
||||||
if (!caps.gles) {
|
if (!caps.gles) {
|
||||||
f->glEnable(GL_VERTEX_PROGRAM_POINT_SIZE);
|
f->glEnable(GL_VERTEX_PROGRAM_POINT_SIZE);
|
||||||
f->glEnable(GL_POINT_SPRITE);
|
f->glEnable(GL_POINT_SPRITE);
|
||||||
@ -1013,6 +1023,10 @@ bool QRhiGles2::isFeatureSupported(QRhi::Feature feature) const
|
|||||||
return !caps.gles || caps.ctxMajor >= 3;
|
return !caps.gles || caps.ctxMajor >= 3;
|
||||||
case QRhi::RenderBufferImport:
|
case QRhi::RenderBufferImport:
|
||||||
return true;
|
return true;
|
||||||
|
case QRhi::ThreeDimensionalTextures:
|
||||||
|
return caps.texture3D;
|
||||||
|
case QRhi::RenderTo3DTextureSlice:
|
||||||
|
return caps.texture3D;
|
||||||
default:
|
default:
|
||||||
Q_UNREACHABLE();
|
Q_UNREACHABLE();
|
||||||
return false;
|
return false;
|
||||||
@ -1229,10 +1243,11 @@ QRhiRenderBuffer *QRhiGles2::createRenderBuffer(QRhiRenderBuffer::Type type, con
|
|||||||
return new QGles2RenderBuffer(this, type, pixelSize, sampleCount, flags, backingFormatHint);
|
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)
|
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,
|
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.level = level;
|
||||||
cmd.args.subImage.dx = dp.x();
|
cmd.args.subImage.dx = dp.x();
|
||||||
cmd.args.subImage.dy = dp.y();
|
cmd.args.subImage.dy = dp.y();
|
||||||
|
cmd.args.subImage.dz = texD->m_flags.testFlag(QRhiTexture::ThreeDimensional) ? layer : 0;
|
||||||
cmd.args.subImage.w = size.width();
|
cmd.args.subImage.w = size.width();
|
||||||
cmd.args.subImage.h = size.height();
|
cmd.args.subImage.h = size.height();
|
||||||
cmd.args.subImage.glformat = texD->glformat;
|
cmd.args.subImage.glformat = texD->glformat;
|
||||||
@ -1820,10 +1836,19 @@ void QRhiGles2::enqueueSubresUpload(QGles2Texture *texD, QGles2CommandBuffer *cb
|
|||||||
cmd.args.subImage.rowLength = 0;
|
cmd.args.subImage.rowLength = 0;
|
||||||
cmd.args.subImage.data = cbD->retainImage(img);
|
cmd.args.subImage.data = cbD->retainImage(img);
|
||||||
} else if (!rawData.isEmpty() && isCompressed) {
|
} else if (!rawData.isEmpty() && isCompressed) {
|
||||||
if (!texD->compressedAtlasBuilt && (texD->flags() & QRhiTexture::UsedAsCompressedAtlas)) {
|
const bool is3D = texD->flags().testFlag(QRhiTexture::ThreeDimensional);
|
||||||
// Create on first upload since glCompressedTexImage2D cannot take nullptr data
|
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;
|
quint32 byteSize = 0;
|
||||||
compressedFormatInfo(texD->m_format, texD->m_pixelSize, nullptr, &byteSize, nullptr);
|
compressedFormatInfo(texD->m_format, texD->m_pixelSize, nullptr, &byteSize, nullptr);
|
||||||
|
if (is3D)
|
||||||
|
byteSize *= texD->m_depth;
|
||||||
QByteArray zeroBuf(byteSize, 0);
|
QByteArray zeroBuf(byteSize, 0);
|
||||||
QGles2CommandBuffer::Command &cmd(cbD->commands.get());
|
QGles2CommandBuffer::Command &cmd(cbD->commands.get());
|
||||||
cmd.cmd = QGles2CommandBuffer::Command::CompressedImage;
|
cmd.cmd = QGles2CommandBuffer::Command::CompressedImage;
|
||||||
@ -1834,14 +1859,15 @@ void QRhiGles2::enqueueSubresUpload(QGles2Texture *texD, QGles2CommandBuffer *cb
|
|||||||
cmd.args.compressedImage.glintformat = texD->glintformat;
|
cmd.args.compressedImage.glintformat = texD->glintformat;
|
||||||
cmd.args.compressedImage.w = texD->m_pixelSize.width();
|
cmd.args.compressedImage.w = texD->m_pixelSize.width();
|
||||||
cmd.args.compressedImage.h = texD->m_pixelSize.height();
|
cmd.args.compressedImage.h = texD->m_pixelSize.height();
|
||||||
|
cmd.args.compressedImage.depth = is3D ? texD->m_depth : 0;
|
||||||
cmd.args.compressedImage.size = byteSize;
|
cmd.args.compressedImage.size = byteSize;
|
||||||
cmd.args.compressedImage.data = cbD->retainData(zeroBuf);
|
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)
|
const QSize size = subresDesc.sourceSize().isEmpty() ? q->sizeForMipLevel(level, texD->m_pixelSize)
|
||||||
: subresDesc.sourceSize();
|
: subresDesc.sourceSize();
|
||||||
if (texD->specified || texD->compressedAtlasBuilt) {
|
if (texD->specified || texD->zeroInitialized) {
|
||||||
QGles2CommandBuffer::Command &cmd(cbD->commands.get());
|
QGles2CommandBuffer::Command &cmd(cbD->commands.get());
|
||||||
cmd.cmd = QGles2CommandBuffer::Command::CompressedSubImage;
|
cmd.cmd = QGles2CommandBuffer::Command::CompressedSubImage;
|
||||||
cmd.args.compressedSubImage.target = texD->target;
|
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.level = level;
|
||||||
cmd.args.compressedSubImage.dx = dp.x();
|
cmd.args.compressedSubImage.dx = dp.x();
|
||||||
cmd.args.compressedSubImage.dy = dp.y();
|
cmd.args.compressedSubImage.dy = dp.y();
|
||||||
|
cmd.args.compressedSubImage.dz = texD->m_flags.testFlag(QRhiTexture::ThreeDimensional) ? layer : 0;
|
||||||
cmd.args.compressedSubImage.w = size.width();
|
cmd.args.compressedSubImage.w = size.width();
|
||||||
cmd.args.compressedSubImage.h = size.height();
|
cmd.args.compressedSubImage.h = size.height();
|
||||||
cmd.args.compressedSubImage.glintformat = texD->glintformat;
|
cmd.args.compressedSubImage.glintformat = texD->glintformat;
|
||||||
@ -1865,6 +1892,7 @@ void QRhiGles2::enqueueSubresUpload(QGles2Texture *texD, QGles2CommandBuffer *cb
|
|||||||
cmd.args.compressedImage.glintformat = texD->glintformat;
|
cmd.args.compressedImage.glintformat = texD->glintformat;
|
||||||
cmd.args.compressedImage.w = size.width();
|
cmd.args.compressedImage.w = size.width();
|
||||||
cmd.args.compressedImage.h = size.height();
|
cmd.args.compressedImage.h = size.height();
|
||||||
|
cmd.args.compressedImage.depth = is3D ? texD->m_depth : 0;
|
||||||
cmd.args.compressedImage.size = rawData.size();
|
cmd.args.compressedImage.size = rawData.size();
|
||||||
cmd.args.compressedImage.data = cbD->retainData(rawData);
|
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.level = level;
|
||||||
cmd.args.subImage.dx = dp.x();
|
cmd.args.subImage.dx = dp.x();
|
||||||
cmd.args.subImage.dy = dp.y();
|
cmd.args.subImage.dy = dp.y();
|
||||||
|
cmd.args.subImage.dz = texD->m_flags.testFlag(QRhiTexture::ThreeDimensional) ? layer : 0;
|
||||||
cmd.args.subImage.w = size.width();
|
cmd.args.subImage.w = size.width();
|
||||||
cmd.args.subImage.h = size.height();
|
cmd.args.subImage.h = size.height();
|
||||||
cmd.args.subImage.glformat = texD->glformat;
|
cmd.args.subImage.glformat = texD->glformat;
|
||||||
@ -1961,8 +1990,8 @@ void QRhiGles2::enqueueResourceUpdates(QRhiCommandBuffer *cb, QRhiResourceUpdate
|
|||||||
const QRhiResourceUpdateBatchPrivate::TextureOp &u(ud->textureOps[opIdx]);
|
const QRhiResourceUpdateBatchPrivate::TextureOp &u(ud->textureOps[opIdx]);
|
||||||
if (u.type == QRhiResourceUpdateBatchPrivate::TextureOp::Upload) {
|
if (u.type == QRhiResourceUpdateBatchPrivate::TextureOp::Upload) {
|
||||||
QGles2Texture *texD = QRHI_RES(QGles2Texture, u.dst);
|
QGles2Texture *texD = QRHI_RES(QGles2Texture, u.dst);
|
||||||
for (int layer = 0; layer < QRhi::MAX_LAYERS; ++layer) {
|
for (int layer = 0, maxLayer = u.subresDesc.count(); layer < maxLayer; ++layer) {
|
||||||
for (int level = 0; level < QRhi::MAX_LEVELS; ++level) {
|
for (int level = 0; level < QRhi::MAX_MIP_LEVELS; ++level) {
|
||||||
for (const QRhiTextureSubresourceUploadDescription &subresDesc : qAsConst(u.subresDesc[layer][level]))
|
for (const QRhiTextureSubresourceUploadDescription &subresDesc : qAsConst(u.subresDesc[layer][level]))
|
||||||
enqueueSubresUpload(texD, cbD, layer, level, subresDesc);
|
enqueueSubresUpload(texD, cbD, layer, level, subresDesc);
|
||||||
}
|
}
|
||||||
@ -1990,18 +2019,21 @@ void QRhiGles2::enqueueResourceUpdates(QRhiCommandBuffer *cb, QRhiResourceUpdate
|
|||||||
QGles2CommandBuffer::Command &cmd(cbD->commands.get());
|
QGles2CommandBuffer::Command &cmd(cbD->commands.get());
|
||||||
cmd.cmd = QGles2CommandBuffer::Command::CopyTex;
|
cmd.cmd = QGles2CommandBuffer::Command::CopyTex;
|
||||||
|
|
||||||
|
cmd.args.copyTex.srcTarget = srcD->target;
|
||||||
cmd.args.copyTex.srcFaceTarget = srcFaceTargetBase + uint(u.desc.sourceLayer());
|
cmd.args.copyTex.srcFaceTarget = srcFaceTargetBase + uint(u.desc.sourceLayer());
|
||||||
cmd.args.copyTex.srcTexture = srcD->texture;
|
cmd.args.copyTex.srcTexture = srcD->texture;
|
||||||
cmd.args.copyTex.srcLevel = u.desc.sourceLevel();
|
cmd.args.copyTex.srcLevel = u.desc.sourceLevel();
|
||||||
cmd.args.copyTex.srcX = sp.x();
|
cmd.args.copyTex.srcX = sp.x();
|
||||||
cmd.args.copyTex.srcY = sp.y();
|
cmd.args.copyTex.srcY = sp.y();
|
||||||
|
cmd.args.copyTex.srcZ = srcD->m_flags.testFlag(QRhiTexture::ThreeDimensional) ? u.desc.sourceLayer() : 0;
|
||||||
|
|
||||||
cmd.args.copyTex.dstTarget = dstD->target;
|
cmd.args.copyTex.dstTarget = dstD->target;
|
||||||
cmd.args.copyTex.dstTexture = dstD->texture;
|
|
||||||
cmd.args.copyTex.dstFaceTarget = dstFaceTargetBase + uint(u.desc.destinationLayer());
|
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.dstLevel = u.desc.destinationLevel();
|
||||||
cmd.args.copyTex.dstX = dp.x();
|
cmd.args.copyTex.dstX = dp.x();
|
||||||
cmd.args.copyTex.dstY = dp.y();
|
cmd.args.copyTex.dstY = dp.y();
|
||||||
|
cmd.args.copyTex.dstZ = dstD->m_flags.testFlag(QRhiTexture::ThreeDimensional) ? u.desc.destinationLayer() : 0;
|
||||||
|
|
||||||
cmd.args.copyTex.w = copySize.width();
|
cmd.args.copyTex.w = copySize.width();
|
||||||
cmd.args.copyTex.h = copySize.height();
|
cmd.args.copyTex.h = copySize.height();
|
||||||
@ -2734,13 +2766,25 @@ void QRhiGles2::executeCommandBuffer(QRhiCommandBuffer *cb)
|
|||||||
GLuint fbo;
|
GLuint fbo;
|
||||||
f->glGenFramebuffers(1, &fbo);
|
f->glGenFramebuffers(1, &fbo);
|
||||||
f->glBindFramebuffer(GL_FRAMEBUFFER, fbo);
|
f->glBindFramebuffer(GL_FRAMEBUFFER, fbo);
|
||||||
f->glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
|
if (cmd.args.copyTex.srcTarget == GL_TEXTURE_3D) {
|
||||||
cmd.args.copyTex.srcFaceTarget, cmd.args.copyTex.srcTexture, cmd.args.copyTex.srcLevel);
|
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->glBindTexture(cmd.args.copyTex.dstTarget, cmd.args.copyTex.dstTexture);
|
||||||
f->glCopyTexSubImage2D(cmd.args.copyTex.dstFaceTarget, cmd.args.copyTex.dstLevel,
|
if (cmd.args.copyTex.dstTarget == GL_TEXTURE_3D) {
|
||||||
cmd.args.copyTex.dstX, cmd.args.copyTex.dstY,
|
f->glCopyTexSubImage3D(cmd.args.copyTex.dstTarget, cmd.args.copyTex.dstLevel,
|
||||||
cmd.args.copyTex.srcX, cmd.args.copyTex.srcY,
|
cmd.args.copyTex.dstX, cmd.args.copyTex.dstY, cmd.args.copyTex.dstZ,
|
||||||
cmd.args.copyTex.w, cmd.args.copyTex.h);
|
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->glBindFramebuffer(GL_FRAMEBUFFER, ctx->defaultFramebufferObject());
|
||||||
f->glDeleteFramebuffers(1, &fbo);
|
f->glDeleteFramebuffers(1, &fbo);
|
||||||
}
|
}
|
||||||
@ -2825,11 +2869,19 @@ void QRhiGles2::executeCommandBuffer(QRhiCommandBuffer *cb)
|
|||||||
f->glPixelStorei(GL_UNPACK_ALIGNMENT, cmd.args.subImage.rowStartAlign);
|
f->glPixelStorei(GL_UNPACK_ALIGNMENT, cmd.args.subImage.rowStartAlign);
|
||||||
if (cmd.args.subImage.rowLength != 0)
|
if (cmd.args.subImage.rowLength != 0)
|
||||||
f->glPixelStorei(GL_UNPACK_ROW_LENGTH, cmd.args.subImage.rowLength);
|
f->glPixelStorei(GL_UNPACK_ROW_LENGTH, cmd.args.subImage.rowLength);
|
||||||
f->glTexSubImage2D(cmd.args.subImage.faceTarget, cmd.args.subImage.level,
|
if (cmd.args.subImage.target == GL_TEXTURE_3D) {
|
||||||
cmd.args.subImage.dx, cmd.args.subImage.dy,
|
f->glTexSubImage3D(cmd.args.subImage.target, cmd.args.subImage.level,
|
||||||
cmd.args.subImage.w, cmd.args.subImage.h,
|
cmd.args.subImage.dx, cmd.args.subImage.dy, cmd.args.subImage.dz,
|
||||||
cmd.args.subImage.glformat, cmd.args.subImage.gltype,
|
cmd.args.subImage.w, cmd.args.subImage.h, 1,
|
||||||
cmd.args.subImage.data);
|
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)
|
if (cmd.args.subImage.rowStartAlign != 4)
|
||||||
f->glPixelStorei(GL_UNPACK_ALIGNMENT, 4);
|
f->glPixelStorei(GL_UNPACK_ALIGNMENT, 4);
|
||||||
if (cmd.args.subImage.rowLength != 0)
|
if (cmd.args.subImage.rowLength != 0)
|
||||||
@ -2837,18 +2889,33 @@ void QRhiGles2::executeCommandBuffer(QRhiCommandBuffer *cb)
|
|||||||
break;
|
break;
|
||||||
case QGles2CommandBuffer::Command::CompressedImage:
|
case QGles2CommandBuffer::Command::CompressedImage:
|
||||||
f->glBindTexture(cmd.args.compressedImage.target, cmd.args.compressedImage.texture);
|
f->glBindTexture(cmd.args.compressedImage.target, cmd.args.compressedImage.texture);
|
||||||
f->glCompressedTexImage2D(cmd.args.compressedImage.faceTarget, cmd.args.compressedImage.level,
|
if (cmd.args.compressedImage.target == GL_TEXTURE_3D) {
|
||||||
cmd.args.compressedImage.glintformat,
|
f->glCompressedTexImage3D(cmd.args.compressedImage.target, cmd.args.compressedImage.level,
|
||||||
cmd.args.compressedImage.w, cmd.args.compressedImage.h, 0,
|
cmd.args.compressedImage.glintformat,
|
||||||
cmd.args.compressedImage.size, cmd.args.compressedImage.data);
|
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;
|
break;
|
||||||
case QGles2CommandBuffer::Command::CompressedSubImage:
|
case QGles2CommandBuffer::Command::CompressedSubImage:
|
||||||
f->glBindTexture(cmd.args.compressedSubImage.target, cmd.args.compressedSubImage.texture);
|
f->glBindTexture(cmd.args.compressedSubImage.target, cmd.args.compressedSubImage.texture);
|
||||||
f->glCompressedTexSubImage2D(cmd.args.compressedSubImage.faceTarget, cmd.args.compressedSubImage.level,
|
if (cmd.args.compressedSubImage.target == GL_TEXTURE_3D) {
|
||||||
cmd.args.compressedSubImage.dx, cmd.args.compressedSubImage.dy,
|
f->glCompressedTexSubImage3D(cmd.args.compressedSubImage.target, cmd.args.compressedSubImage.level,
|
||||||
cmd.args.compressedSubImage.w, cmd.args.compressedSubImage.h,
|
cmd.args.compressedSubImage.dx, cmd.args.compressedSubImage.dy, cmd.args.compressedSubImage.dz,
|
||||||
cmd.args.compressedSubImage.glintformat,
|
cmd.args.compressedSubImage.w, cmd.args.compressedSubImage.h, 1,
|
||||||
cmd.args.compressedSubImage.size, cmd.args.compressedSubImage.data);
|
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;
|
break;
|
||||||
case QGles2CommandBuffer::Command::BlitFromRenderbuffer:
|
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_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_S, GLint(samplerD->d.glwraps));
|
||||||
f->glTexParameteri(texD->target, GL_TEXTURE_WRAP_T, GLint(samplerD->d.glwrapt));
|
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 (caps.textureCompareMode) {
|
||||||
if (samplerD->d.gltexcomparefunc != GL_NEVER) {
|
if (samplerD->d.gltexcomparefunc != GL_NEVER) {
|
||||||
f->glTexParameteri(texD->target, GL_TEXTURE_COMPARE_MODE, GL_COMPARE_REF_TO_TEXTURE);
|
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;
|
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)
|
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;
|
texture = 0;
|
||||||
specified = false;
|
specified = false;
|
||||||
compressedAtlasBuilt = false;
|
zeroInitialized = false;
|
||||||
|
|
||||||
QRHI_RES_RHI(QRhiGles2);
|
QRHI_RES_RHI(QRhiGles2);
|
||||||
if (owns)
|
if (owns)
|
||||||
@ -4463,11 +4532,26 @@ bool QGles2Texture::prepareCreate(QSize *adjustedSize)
|
|||||||
const QSize size = m_pixelSize.isEmpty() ? QSize(1, 1) : m_pixelSize;
|
const QSize size = m_pixelSize.isEmpty() ? QSize(1, 1) : m_pixelSize;
|
||||||
|
|
||||||
const bool isCube = m_flags.testFlag(CubeMap);
|
const bool isCube = m_flags.testFlag(CubeMap);
|
||||||
|
const bool is3D = m_flags.testFlag(ThreeDimensional);
|
||||||
const bool hasMipMaps = m_flags.testFlag(MipMapped);
|
const bool hasMipMaps = m_flags.testFlag(MipMapped);
|
||||||
const bool isCompressed = rhiD->isCompressedFormat(m_format);
|
const bool isCompressed = rhiD->isCompressedFormat(m_format);
|
||||||
|
|
||||||
|
if (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
|
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))
|
if (m_flags.testFlag(ExternalOES))
|
||||||
target = GL_TEXTURE_EXTERNAL_OES;
|
target = GL_TEXTURE_EXTERNAL_OES;
|
||||||
|
|
||||||
@ -4511,12 +4595,24 @@ bool QGles2Texture::create()
|
|||||||
rhiD->f->glGenTextures(1, &texture);
|
rhiD->f->glGenTextures(1, &texture);
|
||||||
|
|
||||||
const bool isCube = m_flags.testFlag(CubeMap);
|
const bool isCube = m_flags.testFlag(CubeMap);
|
||||||
|
const bool is3D = m_flags.testFlag(ThreeDimensional);
|
||||||
const bool hasMipMaps = m_flags.testFlag(MipMapped);
|
const bool hasMipMaps = m_flags.testFlag(MipMapped);
|
||||||
const bool isCompressed = rhiD->isCompressedFormat(m_format);
|
const bool isCompressed = rhiD->isCompressedFormat(m_format);
|
||||||
if (!isCompressed) {
|
if (!isCompressed) {
|
||||||
rhiD->f->glBindTexture(target, texture);
|
rhiD->f->glBindTexture(target, texture);
|
||||||
if (!m_flags.testFlag(UsedWithLoadStore)) {
|
if (!m_flags.testFlag(UsedWithLoadStore)) {
|
||||||
if (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;
|
const GLenum faceTargetBase = isCube ? GL_TEXTURE_CUBE_MAP_POSITIVE_X : target;
|
||||||
for (int layer = 0, layerCount = isCube ? 6 : 1; layer != layerCount; ++layer) {
|
for (int layer = 0, layerCount = isCube ? 6 : 1; layer != layerCount; ++layer) {
|
||||||
for (int level = 0; level != mipLevelCount; ++level) {
|
for (int level = 0; level != mipLevelCount; ++level) {
|
||||||
@ -4534,7 +4630,10 @@ bool QGles2Texture::create()
|
|||||||
// Must be specified with immutable storage functions otherwise
|
// Must be specified with immutable storage functions otherwise
|
||||||
// bindImageTexture may fail. Also, the internal format must be a
|
// bindImageTexture may fail. Also, the internal format must be a
|
||||||
// sized format here.
|
// sized format here.
|
||||||
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;
|
specified = true;
|
||||||
} else {
|
} else {
|
||||||
@ -4565,7 +4664,7 @@ bool QGles2Texture::createFrom(QRhiTexture::NativeTexture src)
|
|||||||
|
|
||||||
texture = textureId;
|
texture = textureId;
|
||||||
specified = true;
|
specified = true;
|
||||||
compressedAtlasBuilt = true;
|
zeroInitialized = true;
|
||||||
|
|
||||||
QRHI_RES_RHI(QRhiGles2);
|
QRHI_RES_RHI(QRhiGles2);
|
||||||
QRHI_PROF;
|
QRHI_PROF;
|
||||||
@ -4605,6 +4704,7 @@ bool QGles2Sampler::create()
|
|||||||
d.glmagfilter = toGlMagFilter(m_magFilter);
|
d.glmagfilter = toGlMagFilter(m_magFilter);
|
||||||
d.glwraps = toGlWrapMode(m_addressU);
|
d.glwraps = toGlWrapMode(m_addressU);
|
||||||
d.glwrapt = toGlWrapMode(m_addressV);
|
d.glwrapt = toGlWrapMode(m_addressV);
|
||||||
|
d.glwrapr = toGlWrapMode(m_addressW);
|
||||||
d.gltexcomparefunc = toGlTextureCompareFunc(m_compareOp);
|
d.gltexcomparefunc = toGlTextureCompareFunc(m_compareOp);
|
||||||
|
|
||||||
generation += 1;
|
generation += 1;
|
||||||
@ -4744,9 +4844,14 @@ bool QGles2TextureRenderTarget::create()
|
|||||||
if (texture) {
|
if (texture) {
|
||||||
QGles2Texture *texD = QRHI_RES(QGles2Texture, texture);
|
QGles2Texture *texD = QRHI_RES(QGles2Texture, texture);
|
||||||
Q_ASSERT(texD->texture && texD->specified);
|
Q_ASSERT(texD->texture && texD->specified);
|
||||||
const GLenum faceTargetBase = texD->flags().testFlag(QRhiTexture::CubeMap) ? GL_TEXTURE_CUBE_MAP_POSITIVE_X : texD->target;
|
if (texD->flags().testFlag(QRhiTexture::ThreeDimensional)) {
|
||||||
rhiD->f->glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0 + uint(attIndex), faceTargetBase + uint(colorAtt.layer()),
|
rhiD->f->glFramebufferTextureLayer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0 + uint(attIndex), texD->texture,
|
||||||
texD->texture, colorAtt.level());
|
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) {
|
if (attIndex == 0) {
|
||||||
d.pixelSize = rhiD->q->sizeForMipLevel(colorAtt.level(), texD->pixelSize());
|
d.pixelSize = rhiD->q->sizeForMipLevel(colorAtt.level(), texD->pixelSize());
|
||||||
d.sampleCount = 1;
|
d.sampleCount = 1;
|
||||||
|
@ -116,6 +116,7 @@ struct QGles2SamplerData
|
|||||||
GLenum glmagfilter = 0;
|
GLenum glmagfilter = 0;
|
||||||
GLenum glwraps = 0;
|
GLenum glwraps = 0;
|
||||||
GLenum glwrapt = 0;
|
GLenum glwrapt = 0;
|
||||||
|
GLenum glwrapr = 0;
|
||||||
GLenum gltexcomparefunc = 0;
|
GLenum gltexcomparefunc = 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -125,6 +126,7 @@ inline bool operator==(const QGles2SamplerData &a, const QGles2SamplerData &b)
|
|||||||
&& a.glmagfilter == b.glmagfilter
|
&& a.glmagfilter == b.glmagfilter
|
||||||
&& a.glwraps == b.glwraps
|
&& a.glwraps == b.glwraps
|
||||||
&& a.glwrapt == b.glwrapt
|
&& a.glwrapt == b.glwrapt
|
||||||
|
&& a.glwrapr == b.glwrapr
|
||||||
&& a.gltexcomparefunc == b.gltexcomparefunc;
|
&& a.gltexcomparefunc == b.gltexcomparefunc;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -135,7 +137,7 @@ inline bool operator!=(const QGles2SamplerData &a, const QGles2SamplerData &b)
|
|||||||
|
|
||||||
struct QGles2Texture : public QRhiTexture
|
struct QGles2Texture : public QRhiTexture
|
||||||
{
|
{
|
||||||
QGles2Texture(QRhiImplementation *rhi, Format format, const QSize &pixelSize,
|
QGles2Texture(QRhiImplementation *rhi, Format format, const QSize &pixelSize, int depth,
|
||||||
int sampleCount, Flags flags);
|
int sampleCount, Flags flags);
|
||||||
~QGles2Texture();
|
~QGles2Texture();
|
||||||
void destroy() override;
|
void destroy() override;
|
||||||
@ -154,7 +156,7 @@ struct QGles2Texture : public QRhiTexture
|
|||||||
GLenum gltype;
|
GLenum gltype;
|
||||||
QGles2SamplerData samplerState;
|
QGles2SamplerData samplerState;
|
||||||
bool specified = false;
|
bool specified = false;
|
||||||
bool compressedAtlasBuilt = false;
|
bool zeroInitialized = false;
|
||||||
int mipLevelCount = 0;
|
int mipLevelCount = 0;
|
||||||
|
|
||||||
enum Access {
|
enum Access {
|
||||||
@ -440,17 +442,20 @@ struct QGles2CommandBuffer : public QRhiCommandBuffer
|
|||||||
int size;
|
int size;
|
||||||
} getBufferSubData;
|
} getBufferSubData;
|
||||||
struct {
|
struct {
|
||||||
|
GLenum srcTarget;
|
||||||
GLenum srcFaceTarget;
|
GLenum srcFaceTarget;
|
||||||
GLuint srcTexture;
|
GLuint srcTexture;
|
||||||
int srcLevel;
|
int srcLevel;
|
||||||
int srcX;
|
int srcX;
|
||||||
int srcY;
|
int srcY;
|
||||||
|
int srcZ;
|
||||||
GLenum dstTarget;
|
GLenum dstTarget;
|
||||||
GLuint dstTexture;
|
GLuint dstTexture;
|
||||||
GLenum dstFaceTarget;
|
GLenum dstFaceTarget;
|
||||||
int dstLevel;
|
int dstLevel;
|
||||||
int dstX;
|
int dstX;
|
||||||
int dstY;
|
int dstY;
|
||||||
|
int dstZ;
|
||||||
int w;
|
int w;
|
||||||
int h;
|
int h;
|
||||||
} copyTex;
|
} copyTex;
|
||||||
@ -470,6 +475,7 @@ struct QGles2CommandBuffer : public QRhiCommandBuffer
|
|||||||
int level;
|
int level;
|
||||||
int dx;
|
int dx;
|
||||||
int dy;
|
int dy;
|
||||||
|
int dz;
|
||||||
int w;
|
int w;
|
||||||
int h;
|
int h;
|
||||||
GLenum glformat;
|
GLenum glformat;
|
||||||
@ -486,6 +492,7 @@ struct QGles2CommandBuffer : public QRhiCommandBuffer
|
|||||||
GLenum glintformat;
|
GLenum glintformat;
|
||||||
int w;
|
int w;
|
||||||
int h;
|
int h;
|
||||||
|
int depth;
|
||||||
int size;
|
int size;
|
||||||
const void *data; // must come from retainData()
|
const void *data; // must come from retainData()
|
||||||
} compressedImage;
|
} compressedImage;
|
||||||
@ -496,6 +503,7 @@ struct QGles2CommandBuffer : public QRhiCommandBuffer
|
|||||||
int level;
|
int level;
|
||||||
int dx;
|
int dx;
|
||||||
int dy;
|
int dy;
|
||||||
|
int dz;
|
||||||
int w;
|
int w;
|
||||||
int h;
|
int h;
|
||||||
GLenum glintformat;
|
GLenum glintformat;
|
||||||
@ -739,6 +747,7 @@ public:
|
|||||||
QRhiTexture::Format backingFormatHint) override;
|
QRhiTexture::Format backingFormatHint) override;
|
||||||
QRhiTexture *createTexture(QRhiTexture::Format format,
|
QRhiTexture *createTexture(QRhiTexture::Format format,
|
||||||
const QSize &pixelSize,
|
const QSize &pixelSize,
|
||||||
|
int depth,
|
||||||
int sampleCount,
|
int sampleCount,
|
||||||
QRhiTexture::Flags flags) override;
|
QRhiTexture::Flags flags) override;
|
||||||
QRhiSampler *createSampler(QRhiSampler::Filter magFilter,
|
QRhiSampler *createSampler(QRhiSampler::Filter magFilter,
|
||||||
@ -929,7 +938,8 @@ public:
|
|||||||
texelFetch(false),
|
texelFetch(false),
|
||||||
intAttributes(true),
|
intAttributes(true),
|
||||||
screenSpaceDerivatives(false),
|
screenSpaceDerivatives(false),
|
||||||
programBinary(false)
|
programBinary(false),
|
||||||
|
texture3D(false)
|
||||||
{ }
|
{ }
|
||||||
int ctxMajor;
|
int ctxMajor;
|
||||||
int ctxMinor;
|
int ctxMinor;
|
||||||
@ -972,6 +982,7 @@ public:
|
|||||||
uint intAttributes : 1;
|
uint intAttributes : 1;
|
||||||
uint screenSpaceDerivatives : 1;
|
uint screenSpaceDerivatives : 1;
|
||||||
uint programBinary : 1;
|
uint programBinary : 1;
|
||||||
|
uint texture3D : 1;
|
||||||
} caps;
|
} caps;
|
||||||
QGles2SwapChain *currentSwapChain = nullptr;
|
QGles2SwapChain *currentSwapChain = nullptr;
|
||||||
QList<GLint> supportedCompressedFormats;
|
QList<GLint> supportedCompressedFormats;
|
||||||
|
@ -182,7 +182,7 @@ struct QRhiMetalData
|
|||||||
struct {
|
struct {
|
||||||
id<MTLTexture> texture;
|
id<MTLTexture> texture;
|
||||||
id<MTLBuffer> stagingBuffers[QMTL_FRAMES_IN_FLIGHT];
|
id<MTLBuffer> stagingBuffers[QMTL_FRAMES_IN_FLIGHT];
|
||||||
id<MTLTexture> views[QRhi::MAX_LEVELS];
|
id<MTLTexture> views[QRhi::MAX_MIP_LEVELS];
|
||||||
} texture;
|
} texture;
|
||||||
struct {
|
struct {
|
||||||
id<MTLSamplerState> samplerState;
|
id<MTLSamplerState> samplerState;
|
||||||
@ -249,7 +249,7 @@ struct QMetalTextureData
|
|||||||
id<MTLTexture> tex = nil;
|
id<MTLTexture> tex = nil;
|
||||||
id<MTLBuffer> stagingBuf[QMTL_FRAMES_IN_FLIGHT];
|
id<MTLBuffer> stagingBuf[QMTL_FRAMES_IN_FLIGHT];
|
||||||
bool owns = true;
|
bool owns = true;
|
||||||
id<MTLTexture> perLevelViews[QRhi::MAX_LEVELS];
|
id<MTLTexture> perLevelViews[QRhi::MAX_MIP_LEVELS];
|
||||||
|
|
||||||
id<MTLTexture> viewForLevel(int level);
|
id<MTLTexture> viewForLevel(int level);
|
||||||
};
|
};
|
||||||
@ -281,7 +281,8 @@ struct QMetalRenderTargetData
|
|||||||
struct ColorAtt {
|
struct ColorAtt {
|
||||||
bool needsDrawableForTex = false;
|
bool needsDrawableForTex = false;
|
||||||
id<MTLTexture> tex = nil;
|
id<MTLTexture> tex = nil;
|
||||||
int layer = 0;
|
int arrayLayer = 0;
|
||||||
|
int slice = 0;
|
||||||
int level = 0;
|
int level = 0;
|
||||||
bool needsDrawableForResolveTex = false;
|
bool needsDrawableForResolveTex = false;
|
||||||
id<MTLTexture> resolveTex = nil;
|
id<MTLTexture> resolveTex = nil;
|
||||||
@ -597,6 +598,10 @@ bool QRhiMetal::isFeatureSupported(QRhi::Feature feature) const
|
|||||||
return true;
|
return true;
|
||||||
case QRhi::RenderBufferImport:
|
case QRhi::RenderBufferImport:
|
||||||
return false;
|
return false;
|
||||||
|
case QRhi::ThreeDimensionalTextures:
|
||||||
|
return true;
|
||||||
|
case QRhi::RenderTo3DTextureSlice:
|
||||||
|
return true;
|
||||||
default:
|
default:
|
||||||
Q_UNREACHABLE();
|
Q_UNREACHABLE();
|
||||||
return false;
|
return false;
|
||||||
@ -687,10 +692,11 @@ QRhiRenderBuffer *QRhiMetal::createRenderBuffer(QRhiRenderBuffer::Type type, con
|
|||||||
return new QMetalRenderBuffer(this, type, pixelSize, sampleCount, flags, backingFormatHint);
|
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)
|
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,
|
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 QPoint dp = subresDesc.destinationTopLeft();
|
||||||
const QByteArray rawData = subresDesc.data();
|
const QByteArray rawData = subresDesc.data();
|
||||||
QImage img = subresDesc.image();
|
QImage img = subresDesc.image();
|
||||||
|
const bool is3D = texD->m_flags.testFlag(QRhiTexture::ThreeDimensional);
|
||||||
id<MTLBlitCommandEncoder> blitEnc = (id<MTLBlitCommandEncoder>) blitEncPtr;
|
id<MTLBlitCommandEncoder> blitEnc = (id<MTLBlitCommandEncoder>) blitEncPtr;
|
||||||
|
|
||||||
if (!img.isNull()) {
|
if (!img.isNull()) {
|
||||||
@ -1649,9 +1656,9 @@ void QRhiMetal::enqueueSubresUpload(QMetalTexture *texD, void *mp, void *blitEnc
|
|||||||
sourceBytesPerImage: 0
|
sourceBytesPerImage: 0
|
||||||
sourceSize: MTLSizeMake(NSUInteger(w), NSUInteger(h), 1)
|
sourceSize: MTLSizeMake(NSUInteger(w), NSUInteger(h), 1)
|
||||||
toTexture: texD->d->tex
|
toTexture: texD->d->tex
|
||||||
destinationSlice: NSUInteger(layer)
|
destinationSlice: NSUInteger(is3D ? 0 : layer)
|
||||||
destinationLevel: NSUInteger(level)
|
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];
|
options: MTLBlitOptionNone];
|
||||||
|
|
||||||
*curOfs += aligned<qsizetype>(fullImageSizeBytes, QRhiMetalData::TEXBUF_ALIGN);
|
*curOfs += aligned<qsizetype>(fullImageSizeBytes, QRhiMetalData::TEXBUF_ALIGN);
|
||||||
@ -1687,9 +1694,9 @@ void QRhiMetal::enqueueSubresUpload(QMetalTexture *texD, void *mp, void *blitEnc
|
|||||||
sourceBytesPerImage: 0
|
sourceBytesPerImage: 0
|
||||||
sourceSize: MTLSizeMake(NSUInteger(w), NSUInteger(h), 1)
|
sourceSize: MTLSizeMake(NSUInteger(w), NSUInteger(h), 1)
|
||||||
toTexture: texD->d->tex
|
toTexture: texD->d->tex
|
||||||
destinationSlice: NSUInteger(layer)
|
destinationSlice: NSUInteger(is3D ? 0 : layer)
|
||||||
destinationLevel: NSUInteger(level)
|
destinationLevel: NSUInteger(level)
|
||||||
destinationOrigin: MTLOriginMake(NSUInteger(dx), NSUInteger(dy), 0)
|
destinationOrigin: MTLOriginMake(NSUInteger(dx), NSUInteger(dy), NSUInteger(is3D ? layer : 0))
|
||||||
options: MTLBlitOptionNone];
|
options: MTLBlitOptionNone];
|
||||||
|
|
||||||
*curOfs += aligned<qsizetype>(rawData.size(), QRhiMetalData::TEXBUF_ALIGN);
|
*curOfs += aligned<qsizetype>(rawData.size(), QRhiMetalData::TEXBUF_ALIGN);
|
||||||
@ -1720,9 +1727,9 @@ void QRhiMetal::enqueueSubresUpload(QMetalTexture *texD, void *mp, void *blitEnc
|
|||||||
sourceBytesPerImage: 0
|
sourceBytesPerImage: 0
|
||||||
sourceSize: MTLSizeMake(NSUInteger(w), NSUInteger(h), 1)
|
sourceSize: MTLSizeMake(NSUInteger(w), NSUInteger(h), 1)
|
||||||
toTexture: texD->d->tex
|
toTexture: texD->d->tex
|
||||||
destinationSlice: NSUInteger(layer)
|
destinationSlice: NSUInteger(is3D ? 0 : layer)
|
||||||
destinationLevel: NSUInteger(level)
|
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];
|
options: MTLBlitOptionNone];
|
||||||
|
|
||||||
*curOfs += aligned<qsizetype>(rawData.size(), QRhiMetalData::TEXBUF_ALIGN);
|
*curOfs += aligned<qsizetype>(rawData.size(), QRhiMetalData::TEXBUF_ALIGN);
|
||||||
@ -1783,8 +1790,8 @@ void QRhiMetal::enqueueResourceUpdates(QRhiCommandBuffer *cb, QRhiResourceUpdate
|
|||||||
if (u.type == QRhiResourceUpdateBatchPrivate::TextureOp::Upload) {
|
if (u.type == QRhiResourceUpdateBatchPrivate::TextureOp::Upload) {
|
||||||
QMetalTexture *utexD = QRHI_RES(QMetalTexture, u.dst);
|
QMetalTexture *utexD = QRHI_RES(QMetalTexture, u.dst);
|
||||||
qsizetype stagingSize = 0;
|
qsizetype stagingSize = 0;
|
||||||
for (int layer = 0; layer < QRhi::MAX_LAYERS; ++layer) {
|
for (int layer = 0, maxLayer = u.subresDesc.count(); layer < maxLayer; ++layer) {
|
||||||
for (int level = 0; level < QRhi::MAX_LEVELS; ++level) {
|
for (int level = 0; level < QRhi::MAX_MIP_LEVELS; ++level) {
|
||||||
for (const QRhiTextureSubresourceUploadDescription &subresDesc : qAsConst(u.subresDesc[layer][level]))
|
for (const QRhiTextureSubresourceUploadDescription &subresDesc : qAsConst(u.subresDesc[layer][level]))
|
||||||
stagingSize += subresUploadByteSize(subresDesc);
|
stagingSize += subresUploadByteSize(subresDesc);
|
||||||
}
|
}
|
||||||
@ -1798,8 +1805,8 @@ void QRhiMetal::enqueueResourceUpdates(QRhiCommandBuffer *cb, QRhiResourceUpdate
|
|||||||
|
|
||||||
void *mp = [utexD->d->stagingBuf[currentFrameSlot] contents];
|
void *mp = [utexD->d->stagingBuf[currentFrameSlot] contents];
|
||||||
qsizetype curOfs = 0;
|
qsizetype curOfs = 0;
|
||||||
for (int layer = 0; layer < QRhi::MAX_LAYERS; ++layer) {
|
for (int layer = 0, maxLayer = u.subresDesc.count(); layer < maxLayer; ++layer) {
|
||||||
for (int level = 0; level < QRhi::MAX_LEVELS; ++level) {
|
for (int level = 0; level < QRhi::MAX_MIP_LEVELS; ++level) {
|
||||||
for (const QRhiTextureSubresourceUploadDescription &subresDesc : qAsConst(u.subresDesc[layer][level]))
|
for (const QRhiTextureSubresourceUploadDescription &subresDesc : qAsConst(u.subresDesc[layer][level]))
|
||||||
enqueueSubresUpload(utexD, mp, blitEnc, layer, level, subresDesc, &curOfs);
|
enqueueSubresUpload(utexD, mp, blitEnc, layer, level, subresDesc, &curOfs);
|
||||||
}
|
}
|
||||||
@ -1818,6 +1825,8 @@ void QRhiMetal::enqueueResourceUpdates(QRhiCommandBuffer *cb, QRhiResourceUpdate
|
|||||||
Q_ASSERT(u.src && u.dst);
|
Q_ASSERT(u.src && u.dst);
|
||||||
QMetalTexture *srcD = QRHI_RES(QMetalTexture, u.src);
|
QMetalTexture *srcD = QRHI_RES(QMetalTexture, u.src);
|
||||||
QMetalTexture *dstD = QRHI_RES(QMetalTexture, u.dst);
|
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 QPoint dp = u.desc.destinationTopLeft();
|
||||||
const QSize mipSize = q->sizeForMipLevel(u.desc.sourceLevel(), srcD->m_pixelSize);
|
const QSize mipSize = q->sizeForMipLevel(u.desc.sourceLevel(), srcD->m_pixelSize);
|
||||||
const QSize copySize = u.desc.pixelSize().isEmpty() ? mipSize : u.desc.pixelSize();
|
const QSize copySize = u.desc.pixelSize().isEmpty() ? mipSize : u.desc.pixelSize();
|
||||||
@ -1825,14 +1834,14 @@ void QRhiMetal::enqueueResourceUpdates(QRhiCommandBuffer *cb, QRhiResourceUpdate
|
|||||||
|
|
||||||
ensureBlit();
|
ensureBlit();
|
||||||
[blitEnc copyFromTexture: srcD->d->tex
|
[blitEnc copyFromTexture: srcD->d->tex
|
||||||
sourceSlice: NSUInteger(u.desc.sourceLayer())
|
sourceSlice: NSUInteger(srcIs3D ? 0 : u.desc.sourceLayer())
|
||||||
sourceLevel: NSUInteger(u.desc.sourceLevel())
|
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)
|
sourceSize: MTLSizeMake(NSUInteger(copySize.width()), NSUInteger(copySize.height()), 1)
|
||||||
toTexture: dstD->d->tex
|
toTexture: dstD->d->tex
|
||||||
destinationSlice: NSUInteger(u.desc.destinationLayer())
|
destinationSlice: NSUInteger(dstIs3D ? 0 : u.desc.destinationLayer())
|
||||||
destinationLevel: NSUInteger(u.desc.destinationLevel())
|
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;
|
srcD->lastActiveFrameSlot = dstD->lastActiveFrameSlot = currentFrameSlot;
|
||||||
} else if (u.type == QRhiResourceUpdateBatchPrivate::TextureOp::Read) {
|
} else if (u.type == QRhiResourceUpdateBatchPrivate::TextureOp::Read) {
|
||||||
@ -1850,6 +1859,10 @@ void QRhiMetal::enqueueResourceUpdates(QRhiCommandBuffer *cb, QRhiResourceUpdate
|
|||||||
qWarning("Multisample texture cannot be read back");
|
qWarning("Multisample texture cannot be read back");
|
||||||
continue;
|
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.pixelSize = q->sizeForMipLevel(u.rb.level(), texD->m_pixelSize);
|
||||||
readback.format = texD->m_format;
|
readback.format = texD->m_format;
|
||||||
src = texD->d->tex;
|
src = texD->d->tex;
|
||||||
@ -2019,7 +2032,8 @@ void QRhiMetal::beginPass(QRhiCommandBuffer *cb,
|
|||||||
|
|
||||||
for (uint i = 0; i < uint(rtD->colorAttCount); ++i) {
|
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].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);
|
cbD->d->currentPassRpDesc.colorAttachments[i].level = NSUInteger(rtD->fb.colorAtt[i].level);
|
||||||
if (rtD->fb.colorAtt[i].resolveTex) {
|
if (rtD->fb.colorAtt[i].resolveTex) {
|
||||||
cbD->d->currentPassRpDesc.colorAttachments[i].storeAction = MTLStoreActionMultisampleResolve;
|
cbD->d->currentPassRpDesc.colorAttachments[i].storeAction = MTLStoreActionMultisampleResolve;
|
||||||
@ -2129,7 +2143,7 @@ static void qrhimtl_releaseTexture(const QRhiMetalData::DeferredReleaseEntry &e)
|
|||||||
[e.texture.texture release];
|
[e.texture.texture release];
|
||||||
for (int i = 0; i < QMTL_FRAMES_IN_FLIGHT; ++i)
|
for (int i = 0; i < QMTL_FRAMES_IN_FLIGHT; ++i)
|
||||||
[e.texture.stagingBuffers[i] release];
|
[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];
|
[e.texture.views[i] release];
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2580,15 +2594,15 @@ QRhiTexture::Format QMetalRenderBuffer::backingFormat() const
|
|||||||
return m_type == Color ? QRhiTexture::RGBA8 : QRhiTexture::UnknownFormat;
|
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)
|
int sampleCount, Flags flags)
|
||||||
: QRhiTexture(rhi, format, pixelSize, sampleCount, flags),
|
: QRhiTexture(rhi, format, pixelSize, depth, sampleCount, flags),
|
||||||
d(new QMetalTextureData(this))
|
d(new QMetalTextureData(this))
|
||||||
{
|
{
|
||||||
for (int i = 0; i < QMTL_FRAMES_IN_FLIGHT; ++i)
|
for (int i = 0; i < QMTL_FRAMES_IN_FLIGHT; ++i)
|
||||||
d->stagingBuf[i] = nil;
|
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;
|
d->perLevelViews[i] = nil;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2615,7 +2629,7 @@ void QMetalTexture::destroy()
|
|||||||
d->stagingBuf[i] = nil;
|
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];
|
e.texture.views[i] = d->perLevelViews[i];
|
||||||
d->perLevelViews[i] = nil;
|
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 QSize size = m_pixelSize.isEmpty() ? QSize(1, 1) : m_pixelSize;
|
||||||
const bool isCube = m_flags.testFlag(CubeMap);
|
const bool isCube = m_flags.testFlag(CubeMap);
|
||||||
|
const bool is3D = m_flags.testFlag(ThreeDimensional);
|
||||||
const bool hasMipMaps = m_flags.testFlag(MipMapped);
|
const bool hasMipMaps = m_flags.testFlag(MipMapped);
|
||||||
|
|
||||||
QRHI_RES_RHI(QRhiMetal);
|
QRHI_RES_RHI(QRhiMetal);
|
||||||
@ -2645,11 +2660,24 @@ bool QMetalTexture::prepareCreate(QSize *adjustedSize)
|
|||||||
qWarning("Cubemap texture cannot be multisample");
|
qWarning("Cubemap texture cannot be multisample");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
if (is3D) {
|
||||||
|
qWarning("3D texture cannot be multisample");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
if (hasMipMaps) {
|
if (hasMipMaps) {
|
||||||
qWarning("Multisample texture cannot have mipmaps");
|
qWarning("Multisample texture cannot have mipmaps");
|
||||||
return false;
|
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)
|
if (adjustedSize)
|
||||||
*adjustedSize = size;
|
*adjustedSize = size;
|
||||||
@ -2666,13 +2694,17 @@ bool QMetalTexture::create()
|
|||||||
MTLTextureDescriptor *desc = [[MTLTextureDescriptor alloc] init];
|
MTLTextureDescriptor *desc = [[MTLTextureDescriptor alloc] init];
|
||||||
|
|
||||||
const bool isCube = m_flags.testFlag(CubeMap);
|
const bool isCube = m_flags.testFlag(CubeMap);
|
||||||
|
const bool is3D = m_flags.testFlag(ThreeDimensional);
|
||||||
if (isCube)
|
if (isCube)
|
||||||
desc.textureType = MTLTextureTypeCube;
|
desc.textureType = MTLTextureTypeCube;
|
||||||
|
else if (is3D)
|
||||||
|
desc.textureType = MTLTextureType3D;
|
||||||
else
|
else
|
||||||
desc.textureType = samples > 1 ? MTLTextureType2DMultisample : MTLTextureType2D;
|
desc.textureType = samples > 1 ? MTLTextureType2DMultisample : MTLTextureType2D;
|
||||||
desc.pixelFormat = d->format;
|
desc.pixelFormat = d->format;
|
||||||
desc.width = NSUInteger(size.width());
|
desc.width = NSUInteger(size.width());
|
||||||
desc.height = NSUInteger(size.height());
|
desc.height = NSUInteger(size.height());
|
||||||
|
desc.depth = is3D ? m_depth : 1;
|
||||||
desc.mipmapLevelCount = NSUInteger(mipLevelCount);
|
desc.mipmapLevelCount = NSUInteger(mipLevelCount);
|
||||||
if (samples > 1)
|
if (samples > 1)
|
||||||
desc.sampleCount = NSUInteger(samples);
|
desc.sampleCount = NSUInteger(samples);
|
||||||
@ -3009,12 +3041,14 @@ bool QMetalTextureRenderTarget::create()
|
|||||||
QMetalRenderBuffer *rbD = QRHI_RES(QMetalRenderBuffer, it->renderBuffer());
|
QMetalRenderBuffer *rbD = QRHI_RES(QMetalRenderBuffer, it->renderBuffer());
|
||||||
Q_ASSERT(texD || rbD);
|
Q_ASSERT(texD || rbD);
|
||||||
id<MTLTexture> dst = nil;
|
id<MTLTexture> dst = nil;
|
||||||
|
bool is3D = false;
|
||||||
if (texD) {
|
if (texD) {
|
||||||
dst = texD->d->tex;
|
dst = texD->d->tex;
|
||||||
if (attIndex == 0) {
|
if (attIndex == 0) {
|
||||||
d->pixelSize = rhiD->q->sizeForMipLevel(it->level(), texD->pixelSize());
|
d->pixelSize = rhiD->q->sizeForMipLevel(it->level(), texD->pixelSize());
|
||||||
d->sampleCount = texD->samples;
|
d->sampleCount = texD->samples;
|
||||||
}
|
}
|
||||||
|
is3D = texD->flags().testFlag(QRhiTexture::ThreeDimensional);
|
||||||
} else if (rbD) {
|
} else if (rbD) {
|
||||||
dst = rbD->d->tex;
|
dst = rbD->d->tex;
|
||||||
if (attIndex == 0) {
|
if (attIndex == 0) {
|
||||||
@ -3024,7 +3058,8 @@ bool QMetalTextureRenderTarget::create()
|
|||||||
}
|
}
|
||||||
QMetalRenderTargetData::ColorAtt colorAtt;
|
QMetalRenderTargetData::ColorAtt colorAtt;
|
||||||
colorAtt.tex = dst;
|
colorAtt.tex = dst;
|
||||||
colorAtt.layer = it->layer();
|
colorAtt.arrayLayer = is3D ? 0 : it->layer();
|
||||||
|
colorAtt.slice = is3D ? it->layer() : 0;
|
||||||
colorAtt.level = it->level();
|
colorAtt.level = it->level();
|
||||||
QMetalTexture *resTexD = QRHI_RES(QMetalTexture, it->resolveTexture());
|
QMetalTexture *resTexD = QRHI_RES(QMetalTexture, it->resolveTexture());
|
||||||
colorAtt.resolveTex = resTexD ? resTexD->d->tex : nil;
|
colorAtt.resolveTex = resTexD ? resTexD->d->tex : nil;
|
||||||
|
@ -102,7 +102,7 @@ struct QMetalTextureData;
|
|||||||
|
|
||||||
struct QMetalTexture : public QRhiTexture
|
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);
|
int sampleCount, Flags flags);
|
||||||
~QMetalTexture();
|
~QMetalTexture();
|
||||||
void destroy() override;
|
void destroy() override;
|
||||||
@ -360,6 +360,7 @@ public:
|
|||||||
QRhiTexture::Format backingFormatHint) override;
|
QRhiTexture::Format backingFormatHint) override;
|
||||||
QRhiTexture *createTexture(QRhiTexture::Format format,
|
QRhiTexture *createTexture(QRhiTexture::Format format,
|
||||||
const QSize &pixelSize,
|
const QSize &pixelSize,
|
||||||
|
int depth,
|
||||||
int sampleCount,
|
int sampleCount,
|
||||||
QRhiTexture::Flags flags) override;
|
QRhiTexture::Flags flags) override;
|
||||||
QRhiSampler *createSampler(QRhiSampler::Filter magFilter,
|
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);
|
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)
|
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,
|
QRhiSampler *QRhiNull::createSampler(QRhiSampler::Filter magFilter, QRhiSampler::Filter minFilter,
|
||||||
@ -415,8 +416,8 @@ QRhi::FrameOpResult QRhiNull::finish()
|
|||||||
void QRhiNull::simulateTextureUpload(const QRhiResourceUpdateBatchPrivate::TextureOp &u)
|
void QRhiNull::simulateTextureUpload(const QRhiResourceUpdateBatchPrivate::TextureOp &u)
|
||||||
{
|
{
|
||||||
QNullTexture *texD = QRHI_RES(QNullTexture, u.dst);
|
QNullTexture *texD = QRHI_RES(QNullTexture, u.dst);
|
||||||
for (int layer = 0; layer < QRhi::MAX_LAYERS; ++layer) {
|
for (int layer = 0, maxLayer = u.subresDesc.count(); layer < maxLayer; ++layer) {
|
||||||
for (int level = 0; level < QRhi::MAX_LEVELS; ++level) {
|
for (int level = 0; level < QRhi::MAX_MIP_LEVELS; ++level) {
|
||||||
for (const QRhiTextureSubresourceUploadDescription &subresDesc : qAsConst(u.subresDesc[layer][level])) {
|
for (const QRhiTextureSubresourceUploadDescription &subresDesc : qAsConst(u.subresDesc[layer][level])) {
|
||||||
if (!subresDesc.image().isNull()) {
|
if (!subresDesc.image().isNull()) {
|
||||||
const QImage src = subresDesc.image();
|
const QImage src = subresDesc.image();
|
||||||
@ -642,9 +643,9 @@ QRhiTexture::Format QNullRenderBuffer::backingFormat() const
|
|||||||
return m_type == Color ? QRhiTexture::RGBA8 : QRhiTexture::UnknownFormat;
|
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)
|
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);
|
QRHI_RES_RHI(QRhiNull);
|
||||||
const bool isCube = m_flags.testFlag(CubeMap);
|
const bool isCube = m_flags.testFlag(CubeMap);
|
||||||
|
const bool is3D = m_flags.testFlag(ThreeDimensional);
|
||||||
const bool hasMipMaps = m_flags.testFlag(MipMapped);
|
const bool hasMipMaps = m_flags.testFlag(MipMapped);
|
||||||
QSize size = m_pixelSize.isEmpty() ? QSize(1, 1) : m_pixelSize;
|
QSize size = m_pixelSize.isEmpty() ? QSize(1, 1) : m_pixelSize;
|
||||||
|
m_depth = qMax(1, m_depth);
|
||||||
const int mipLevelCount = hasMipMaps ? rhiD->q->mipLevelsForSize(size) : 1;
|
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) {
|
if (m_format == RGBA8) {
|
||||||
|
image.resize(layerCount);
|
||||||
for (int layer = 0; layer < layerCount; ++layer) {
|
for (int layer = 0; layer < layerCount; ++layer) {
|
||||||
for (int level = 0; level < mipLevelCount; ++level) {
|
for (int level = 0; level < mipLevelCount; ++level) {
|
||||||
image[layer][level] = QImage(rhiD->q->sizeForMipLevel(level, size),
|
image[layer][level] = QImage(rhiD->q->sizeForMipLevel(level, size),
|
||||||
|
@ -80,14 +80,14 @@ struct QNullRenderBuffer : public QRhiRenderBuffer
|
|||||||
|
|
||||||
struct QNullTexture : public QRhiTexture
|
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);
|
int sampleCount, Flags flags);
|
||||||
~QNullTexture();
|
~QNullTexture();
|
||||||
void destroy() override;
|
void destroy() override;
|
||||||
bool create() override;
|
bool create() override;
|
||||||
bool createFrom(NativeTexture src) 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
|
struct QNullSampler : public QRhiSampler
|
||||||
@ -217,6 +217,7 @@ public:
|
|||||||
QRhiTexture::Format backingFormatHint) override;
|
QRhiTexture::Format backingFormatHint) override;
|
||||||
QRhiTexture *createTexture(QRhiTexture::Format format,
|
QRhiTexture *createTexture(QRhiTexture::Format format,
|
||||||
const QSize &pixelSize,
|
const QSize &pixelSize,
|
||||||
|
int depth,
|
||||||
int sampleCount,
|
int sampleCount,
|
||||||
QRhiTexture::Flags flags) override;
|
QRhiTexture::Flags flags) override;
|
||||||
QRhiSampler *createSampler(QRhiSampler::Filter magFilter,
|
QRhiSampler *createSampler(QRhiSampler::Filter magFilter,
|
||||||
|
@ -387,7 +387,7 @@ void QRhiProfilerPrivate::newRenderBuffer(QRhiRenderBuffer *rb, bool transientBa
|
|||||||
const QSize sz = rb->pixelSize();
|
const QSize sz = rb->pixelSize();
|
||||||
// just make up something, ds is likely D24S8 while color is RGBA8 or similar
|
// 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;
|
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)
|
if (sampleCount > 1)
|
||||||
byteSize *= uint(sampleCount);
|
byteSize *= uint(sampleCount);
|
||||||
|
|
||||||
@ -418,7 +418,8 @@ void QRhiProfilerPrivate::newTexture(QRhiTexture *tex, bool owns, int mipCount,
|
|||||||
|
|
||||||
const QRhiTexture::Format format = tex->format();
|
const QRhiTexture::Format format = tex->format();
|
||||||
const QSize sz = tex->pixelSize();
|
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)
|
if (sampleCount > 1)
|
||||||
byteSize *= uint(sampleCount);
|
byteSize *= uint(sampleCount);
|
||||||
|
|
||||||
@ -470,7 +471,7 @@ void QRhiProfilerPrivate::resizeSwapChain(QRhiSwapChain *sc, int bufferCount, in
|
|||||||
return;
|
return;
|
||||||
|
|
||||||
const QSize sz = sc->currentPixelSize();
|
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);
|
byteSize = byteSize * uint(bufferCount) + byteSize * uint(msaaBufferCount) * uint(sampleCount);
|
||||||
|
|
||||||
startEntry(QRhiProfiler::ResizeSwapChain, ts.elapsed(), sc);
|
startEntry(QRhiProfiler::ResizeSwapChain, ts.elapsed(), sc);
|
||||||
|
@ -149,11 +149,21 @@ QT_BEGIN_NAMESPACE
|
|||||||
in deviceExtensions. This can be relevant when integrating with native Vulkan
|
in deviceExtensions. This can be relevant when integrating with native Vulkan
|
||||||
rendering code.
|
rendering code.
|
||||||
|
|
||||||
It is expected that the desired list of instance extensions will be queried
|
It is expected that the backend's desired list of instance extensions will
|
||||||
by calling the static function preferredInstanceExtensions() before
|
be queried by calling the static function preferredInstanceExtensions()
|
||||||
initializing a QVulkanInstance. The returned list can be passed to
|
before initializing a QVulkanInstance. The returned list can be safely
|
||||||
QVulkanInstance::setExtensions() as-is, because unsupported extensions are
|
passed to QVulkanInstance::setExtensions() as-is, because unsupported
|
||||||
filtered out automatically.
|
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
|
\section2 Working with existing Vulkan devices
|
||||||
|
|
||||||
@ -555,17 +565,17 @@ bool QRhiVulkan::create(QRhi::Flags flags)
|
|||||||
QList<const char *> requestedDevExts;
|
QList<const char *> requestedDevExts;
|
||||||
requestedDevExts.append("VK_KHR_swapchain");
|
requestedDevExts.append("VK_KHR_swapchain");
|
||||||
|
|
||||||
debugMarkersAvailable = false;
|
caps.debugMarkers = false;
|
||||||
if (devExts.contains(VK_EXT_DEBUG_MARKER_EXTENSION_NAME)) {
|
if (devExts.contains(VK_EXT_DEBUG_MARKER_EXTENSION_NAME)) {
|
||||||
requestedDevExts.append(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 (devExts.contains(VK_EXT_VERTEX_ATTRIBUTE_DIVISOR_EXTENSION_NAME)) {
|
||||||
if (inst->extensions().contains(QByteArrayLiteral("VK_KHR_get_physical_device_properties2"))) {
|
if (inst->extensions().contains(QByteArrayLiteral("VK_KHR_get_physical_device_properties2"))) {
|
||||||
requestedDevExts.append(VK_EXT_VERTEX_ATTRIBUTE_DIVISOR_EXTENSION_NAME);
|
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())
|
if (queueFamilyProps.isEmpty())
|
||||||
queryQueueFamilyProps();
|
queryQueueFamilyProps();
|
||||||
|
|
||||||
hasCompute = (queueFamilyProps[gfxQueueFamilyIdx].queueFlags & VK_QUEUE_COMPUTE_BIT) != 0;
|
caps.compute = (queueFamilyProps[gfxQueueFamilyIdx].queueFlags & VK_QUEUE_COMPUTE_BIT) != 0;
|
||||||
timestampValidBits = queueFamilyProps[gfxQueueFamilyIdx].timestampValidBits;
|
timestampValidBits = queueFamilyProps[gfxQueueFamilyIdx].timestampValidBits;
|
||||||
|
|
||||||
ubufAlign = physDevProperties.limits.minUniformBufferOffsetAlignment;
|
ubufAlign = physDevProperties.limits.minUniformBufferOffsetAlignment;
|
||||||
@ -660,7 +670,9 @@ bool QRhiVulkan::create(QRhi::Flags flags)
|
|||||||
// elsewhere states that the minimum bufferOffset is 4...
|
// elsewhere states that the minimum bufferOffset is 4...
|
||||||
texbufAlign = qMax<VkDeviceSize>(4, physDevProperties.limits.optimalBufferCopyOffsetAlignment);
|
texbufAlign = qMax<VkDeviceSize>(4, physDevProperties.limits.optimalBufferCopyOffsetAlignment);
|
||||||
|
|
||||||
hasWideLines = physDevFeatures.wideLines;
|
caps.wideLines = physDevFeatures.wideLines;
|
||||||
|
|
||||||
|
caps.texture3DSliceAs2D = inst->apiVersion() >= QVersionNumber(1, 1);
|
||||||
|
|
||||||
if (!importedAllocator) {
|
if (!importedAllocator) {
|
||||||
VmaVulkanFunctions afuncs;
|
VmaVulkanFunctions afuncs;
|
||||||
@ -720,7 +732,7 @@ bool QRhiVulkan::create(QRhi::Flags flags)
|
|||||||
timestampQueryPoolMap.resize(QVK_MAX_ACTIVE_TIMESTAMP_PAIRS); // 1 bit per pair
|
timestampQueryPoolMap.resize(QVK_MAX_ACTIVE_TIMESTAMP_PAIRS); // 1 bit per pair
|
||||||
timestampQueryPoolMap.fill(false);
|
timestampQueryPoolMap.fill(false);
|
||||||
|
|
||||||
if (debugMarkersAvailable) {
|
if (caps.debugMarkers) {
|
||||||
vkCmdDebugMarkerBegin = reinterpret_cast<PFN_vkCmdDebugMarkerBeginEXT>(f->vkGetDeviceProcAddr(dev, "vkCmdDebugMarkerBeginEXT"));
|
vkCmdDebugMarkerBegin = reinterpret_cast<PFN_vkCmdDebugMarkerBeginEXT>(f->vkGetDeviceProcAddr(dev, "vkCmdDebugMarkerBeginEXT"));
|
||||||
vkCmdDebugMarkerEnd = reinterpret_cast<PFN_vkCmdDebugMarkerEndEXT>(f->vkGetDeviceProcAddr(dev, "vkCmdDebugMarkerEndEXT"));
|
vkCmdDebugMarkerEnd = reinterpret_cast<PFN_vkCmdDebugMarkerEndEXT>(f->vkGetDeviceProcAddr(dev, "vkCmdDebugMarkerEndEXT"));
|
||||||
vkCmdDebugMarkerInsert = reinterpret_cast<PFN_vkCmdDebugMarkerInsertEXT>(f->vkGetDeviceProcAddr(dev, "vkCmdDebugMarkerInsertEXT"));
|
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 copySizeBytes = 0;
|
||||||
qsizetype imageSizeBytes = 0;
|
qsizetype imageSizeBytes = 0;
|
||||||
const void *src = nullptr;
|
const void *src = nullptr;
|
||||||
|
const bool is3D = texD->m_flags.testFlag(QRhiTexture::ThreeDimensional);
|
||||||
|
|
||||||
VkBufferImageCopy copyInfo;
|
VkBufferImageCopy copyInfo;
|
||||||
memset(©Info, 0, sizeof(copyInfo));
|
memset(©Info, 0, sizeof(copyInfo));
|
||||||
copyInfo.bufferOffset = *curOfs;
|
copyInfo.bufferOffset = *curOfs;
|
||||||
copyInfo.imageSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
|
copyInfo.imageSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
|
||||||
copyInfo.imageSubresource.mipLevel = uint32_t(level);
|
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.imageSubresource.layerCount = 1;
|
||||||
copyInfo.imageExtent.depth = 1;
|
copyInfo.imageExtent.depth = 1;
|
||||||
|
if (is3D)
|
||||||
|
copyInfo.imageOffset.z = uint32_t(layer);
|
||||||
|
|
||||||
const QByteArray rawData = subresDesc.data();
|
const QByteArray rawData = subresDesc.data();
|
||||||
const QPoint dp = subresDesc.destinationTopLeft();
|
const QPoint dp = subresDesc.destinationTopLeft();
|
||||||
@ -3191,8 +3206,8 @@ void QRhiVulkan::enqueueResourceUpdates(QVkCommandBuffer *cbD, QRhiResourceUpdat
|
|||||||
QVkTexture *utexD = QRHI_RES(QVkTexture, u.dst);
|
QVkTexture *utexD = QRHI_RES(QVkTexture, u.dst);
|
||||||
// batch into a single staging buffer and a single CopyBufferToImage with multiple copyInfos
|
// batch into a single staging buffer and a single CopyBufferToImage with multiple copyInfos
|
||||||
VkDeviceSize stagingSize = 0;
|
VkDeviceSize stagingSize = 0;
|
||||||
for (int layer = 0; layer < QRhi::MAX_LAYERS; ++layer) {
|
for (int layer = 0, maxLayer = u.subresDesc.count(); layer < maxLayer; ++layer) {
|
||||||
for (int level = 0; level < QRhi::MAX_LEVELS; ++level) {
|
for (int level = 0; level < QRhi::MAX_MIP_LEVELS; ++level) {
|
||||||
for (const QRhiTextureSubresourceUploadDescription &subresDesc : qAsConst(u.subresDesc[layer][level]))
|
for (const QRhiTextureSubresourceUploadDescription &subresDesc : qAsConst(u.subresDesc[layer][level]))
|
||||||
stagingSize += subresUploadByteSize(subresDesc);
|
stagingSize += subresUploadByteSize(subresDesc);
|
||||||
}
|
}
|
||||||
@ -3229,8 +3244,8 @@ void QRhiVulkan::enqueueResourceUpdates(QVkCommandBuffer *cbD, QRhiResourceUpdat
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (int layer = 0; layer < QRhi::MAX_LAYERS; ++layer) {
|
for (int layer = 0, maxLayer = u.subresDesc.count(); layer < maxLayer; ++layer) {
|
||||||
for (int level = 0; level < QRhi::MAX_LEVELS; ++level) {
|
for (int level = 0; level < QRhi::MAX_MIP_LEVELS; ++level) {
|
||||||
const QList<QRhiTextureSubresourceUploadDescription> &srd(u.subresDesc[layer][level]);
|
const QList<QRhiTextureSubresourceUploadDescription> &srd(u.subresDesc[layer][level]);
|
||||||
if (srd.isEmpty())
|
if (srd.isEmpty())
|
||||||
continue;
|
continue;
|
||||||
@ -3278,25 +3293,31 @@ void QRhiVulkan::enqueueResourceUpdates(QVkCommandBuffer *cbD, QRhiResourceUpdat
|
|||||||
}
|
}
|
||||||
QVkTexture *srcD = QRHI_RES(QVkTexture, u.src);
|
QVkTexture *srcD = QRHI_RES(QVkTexture, u.src);
|
||||||
QVkTexture *dstD = QRHI_RES(QVkTexture, u.dst);
|
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;
|
VkImageCopy region;
|
||||||
memset(®ion, 0, sizeof(region));
|
memset(®ion, 0, sizeof(region));
|
||||||
|
|
||||||
region.srcSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
|
region.srcSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
|
||||||
region.srcSubresource.mipLevel = uint32_t(u.desc.sourceLevel());
|
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.srcSubresource.layerCount = 1;
|
||||||
|
|
||||||
region.srcOffset.x = u.desc.sourceTopLeft().x();
|
region.srcOffset.x = u.desc.sourceTopLeft().x();
|
||||||
region.srcOffset.y = u.desc.sourceTopLeft().y();
|
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.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
|
||||||
region.dstSubresource.mipLevel = uint32_t(u.desc.destinationLevel());
|
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.dstSubresource.layerCount = 1;
|
||||||
|
|
||||||
region.dstOffset.x = u.desc.destinationTopLeft().x();
|
region.dstOffset.x = u.desc.destinationTopLeft().x();
|
||||||
region.dstOffset.y = u.desc.destinationTopLeft().y();
|
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 mipSize = q->sizeForMipLevel(u.desc.sourceLevel(), srcD->m_pixelSize);
|
||||||
const QSize copySize = u.desc.pixelSize().isEmpty() ? mipSize : u.desc.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");
|
qWarning("Multisample texture cannot be read back");
|
||||||
continue;
|
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.pixelSize = q->sizeForMipLevel(u.rb.level(), texD->m_pixelSize);
|
||||||
readback.format = texD->m_format;
|
readback.format = texD->m_format;
|
||||||
texD->lastActiveFrameSlot = currentFrameSlot;
|
texD->lastActiveFrameSlot = currentFrameSlot;
|
||||||
@ -3426,6 +3451,7 @@ void QRhiVulkan::enqueueResourceUpdates(QVkCommandBuffer *cbD, QRhiResourceUpdat
|
|||||||
QVkTexture *utexD = QRHI_RES(QVkTexture, u.dst);
|
QVkTexture *utexD = QRHI_RES(QVkTexture, u.dst);
|
||||||
Q_ASSERT(utexD->m_flags.testFlag(QRhiTexture::UsedWithGenerateMips));
|
Q_ASSERT(utexD->m_flags.testFlag(QRhiTexture::UsedWithGenerateMips));
|
||||||
const bool isCube = utexD->m_flags.testFlag(QRhiTexture::CubeMap);
|
const bool isCube = utexD->m_flags.testFlag(QRhiTexture::CubeMap);
|
||||||
|
const bool is3D = utexD->m_flags.testFlag(QRhiTexture::ThreeDimensional);
|
||||||
|
|
||||||
VkImageLayout origLayout = utexD->usageState.layout;
|
VkImageLayout origLayout = utexD->usageState.layout;
|
||||||
VkAccessFlags origAccess = utexD->usageState.access;
|
VkAccessFlags origAccess = utexD->usageState.access;
|
||||||
@ -3436,6 +3462,7 @@ void QRhiVulkan::enqueueResourceUpdates(QVkCommandBuffer *cbD, QRhiResourceUpdat
|
|||||||
for (int layer = 0; layer < (isCube ? 6 : 1); ++layer) {
|
for (int layer = 0; layer < (isCube ? 6 : 1); ++layer) {
|
||||||
int w = utexD->m_pixelSize.width();
|
int w = utexD->m_pixelSize.width();
|
||||||
int h = utexD->m_pixelSize.height();
|
int h = utexD->m_pixelSize.height();
|
||||||
|
int depth = is3D ? utexD->m_depth : 1;
|
||||||
for (int level = 1; level < int(utexD->mipLevelCount); ++level) {
|
for (int level = 1; level < int(utexD->mipLevelCount); ++level) {
|
||||||
if (level == 1) {
|
if (level == 1) {
|
||||||
subresourceBarrier(cbD, utexD->image,
|
subresourceBarrier(cbD, utexD->image,
|
||||||
@ -3470,7 +3497,7 @@ void QRhiVulkan::enqueueResourceUpdates(QVkCommandBuffer *cbD, QRhiResourceUpdat
|
|||||||
|
|
||||||
region.srcOffsets[1].x = qMax(1, w);
|
region.srcOffsets[1].x = qMax(1, w);
|
||||||
region.srcOffsets[1].y = qMax(1, h);
|
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.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
|
||||||
region.dstSubresource.mipLevel = uint32_t(level);
|
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].x = qMax(1, w >> 1);
|
||||||
region.dstOffsets[1].y = qMax(1, h >> 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());
|
QVkCommandBuffer::Command &cmd(cbD->commands.get());
|
||||||
cmd.cmd = QVkCommandBuffer::Command::BlitImage;
|
cmd.cmd = QVkCommandBuffer::Command::BlitImage;
|
||||||
@ -3492,6 +3519,7 @@ void QRhiVulkan::enqueueResourceUpdates(QVkCommandBuffer *cbD, QRhiResourceUpdat
|
|||||||
|
|
||||||
w >>= 1;
|
w >>= 1;
|
||||||
h >>= 1;
|
h >>= 1;
|
||||||
|
depth >>= 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (utexD->mipLevelCount > 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));
|
vmaDestroyImage(toVmaAllocator(allocator), e.texture.image, toVmaAllocation(e.texture.allocation));
|
||||||
for (int i = 0; i < QVK_FRAMES_IN_FLIGHT; ++i)
|
for (int i = 0; i < QVK_FRAMES_IN_FLIGHT; ++i)
|
||||||
vmaDestroyBuffer(toVmaAllocator(allocator), e.texture.stagingBuffers[i], toVmaAllocation(e.texture.stagingAllocations[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])
|
if (e.texture.extraImageViews[i])
|
||||||
df->vkDestroyImageView(dev, e.texture.extraImageViews[i], nullptr);
|
df->vkDestroyImageView(dev, e.texture.extraImageViews[i], nullptr);
|
||||||
}
|
}
|
||||||
@ -4183,13 +4211,13 @@ bool QRhiVulkan::isFeatureSupported(QRhi::Feature feature) const
|
|||||||
case QRhi::MultisampleRenderBuffer:
|
case QRhi::MultisampleRenderBuffer:
|
||||||
return true;
|
return true;
|
||||||
case QRhi::DebugMarkers:
|
case QRhi::DebugMarkers:
|
||||||
return debugMarkersAvailable;
|
return caps.debugMarkers;
|
||||||
case QRhi::Timestamps:
|
case QRhi::Timestamps:
|
||||||
return timestampValidBits != 0;
|
return timestampValidBits != 0;
|
||||||
case QRhi::Instancing:
|
case QRhi::Instancing:
|
||||||
return true;
|
return true;
|
||||||
case QRhi::CustomInstanceStepRate:
|
case QRhi::CustomInstanceStepRate:
|
||||||
return vertexAttribDivisorAvailable;
|
return caps.vertexAttribDivisor;
|
||||||
case QRhi::PrimitiveRestart:
|
case QRhi::PrimitiveRestart:
|
||||||
return true;
|
return true;
|
||||||
case QRhi::NonDynamicUniformBuffers:
|
case QRhi::NonDynamicUniformBuffers:
|
||||||
@ -4203,9 +4231,9 @@ bool QRhiVulkan::isFeatureSupported(QRhi::Feature feature) const
|
|||||||
case QRhi::ElementIndexUint:
|
case QRhi::ElementIndexUint:
|
||||||
return true;
|
return true;
|
||||||
case QRhi::Compute:
|
case QRhi::Compute:
|
||||||
return hasCompute;
|
return caps.compute;
|
||||||
case QRhi::WideLines:
|
case QRhi::WideLines:
|
||||||
return hasWideLines;
|
return caps.wideLines;
|
||||||
case QRhi::VertexShaderPointSize:
|
case QRhi::VertexShaderPointSize:
|
||||||
return true;
|
return true;
|
||||||
case QRhi::BaseVertex:
|
case QRhi::BaseVertex:
|
||||||
@ -4234,6 +4262,10 @@ bool QRhiVulkan::isFeatureSupported(QRhi::Feature feature) const
|
|||||||
return true;
|
return true;
|
||||||
case QRhi::RenderBufferImport:
|
case QRhi::RenderBufferImport:
|
||||||
return false;
|
return false;
|
||||||
|
case QRhi::ThreeDimensionalTextures:
|
||||||
|
return true;
|
||||||
|
case QRhi::RenderTo3DTextureSlice:
|
||||||
|
return caps.texture3DSliceAs2D;
|
||||||
default:
|
default:
|
||||||
Q_UNREACHABLE();
|
Q_UNREACHABLE();
|
||||||
return false;
|
return false;
|
||||||
@ -4439,10 +4471,11 @@ QRhiRenderBuffer *QRhiVulkan::createRenderBuffer(QRhiRenderBuffer::Type type, co
|
|||||||
return new QVkRenderBuffer(this, type, pixelSize, sampleCount, flags, backingFormatHint);
|
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)
|
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,
|
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)
|
void QRhiVulkan::debugMarkBegin(QRhiCommandBuffer *cb, const QByteArray &name)
|
||||||
{
|
{
|
||||||
if (!debugMarkers || !debugMarkersAvailable)
|
if (!debugMarkers || !caps.debugMarkers)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
VkDebugMarkerMarkerInfoEXT marker;
|
VkDebugMarkerMarkerInfoEXT marker;
|
||||||
@ -4961,7 +4994,7 @@ void QRhiVulkan::debugMarkBegin(QRhiCommandBuffer *cb, const QByteArray &name)
|
|||||||
|
|
||||||
void QRhiVulkan::debugMarkEnd(QRhiCommandBuffer *cb)
|
void QRhiVulkan::debugMarkEnd(QRhiCommandBuffer *cb)
|
||||||
{
|
{
|
||||||
if (!debugMarkers || !debugMarkersAvailable)
|
if (!debugMarkers || !caps.debugMarkers)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
QVkCommandBuffer *cbD = QRHI_RES(QVkCommandBuffer, cb);
|
QVkCommandBuffer *cbD = QRHI_RES(QVkCommandBuffer, cb);
|
||||||
@ -4975,7 +5008,7 @@ void QRhiVulkan::debugMarkEnd(QRhiCommandBuffer *cb)
|
|||||||
|
|
||||||
void QRhiVulkan::debugMarkMsg(QRhiCommandBuffer *cb, const QByteArray &msg)
|
void QRhiVulkan::debugMarkMsg(QRhiCommandBuffer *cb, const QByteArray &msg)
|
||||||
{
|
{
|
||||||
if (!debugMarkers || !debugMarkersAvailable)
|
if (!debugMarkers || !caps.debugMarkers)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
VkDebugMarkerMarkerInfoEXT marker;
|
VkDebugMarkerMarkerInfoEXT marker;
|
||||||
@ -5075,7 +5108,7 @@ void QRhiVulkan::endExternal(QRhiCommandBuffer *cb)
|
|||||||
|
|
||||||
void QRhiVulkan::setObjectName(uint64_t object, VkDebugReportObjectTypeEXT type, const QByteArray &name, int slot)
|
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;
|
return;
|
||||||
|
|
||||||
VkDebugMarkerObjectNameInfoEXT nameInfo;
|
VkDebugMarkerObjectNameInfoEXT nameInfo;
|
||||||
@ -5673,6 +5706,7 @@ bool QVkRenderBuffer::create()
|
|||||||
if (!backingTexture) {
|
if (!backingTexture) {
|
||||||
backingTexture = QRHI_RES(QVkTexture, rhiD->createTexture(backingFormat(),
|
backingTexture = QRHI_RES(QVkTexture, rhiD->createTexture(backingFormat(),
|
||||||
m_pixelSize,
|
m_pixelSize,
|
||||||
|
1,
|
||||||
m_sampleCount,
|
m_sampleCount,
|
||||||
QRhiTexture::RenderTarget | QRhiTexture::UsedAsTransferSource));
|
QRhiTexture::RenderTarget | QRhiTexture::UsedAsTransferSource));
|
||||||
} else {
|
} else {
|
||||||
@ -5721,15 +5755,15 @@ QRhiTexture::Format QVkRenderBuffer::backingFormat() const
|
|||||||
return m_type == Color ? QRhiTexture::RGBA8 : QRhiTexture::UnknownFormat;
|
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)
|
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) {
|
for (int i = 0; i < QVK_FRAMES_IN_FLIGHT; ++i) {
|
||||||
stagingBuffers[i] = VK_NULL_HANDLE;
|
stagingBuffers[i] = VK_NULL_HANDLE;
|
||||||
stagingAllocations[i] = nullptr;
|
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;
|
perLevelImageViews[i] = VK_NULL_HANDLE;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -5759,7 +5793,7 @@ void QVkTexture::destroy()
|
|||||||
stagingAllocations[i] = nullptr;
|
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];
|
e.texture.extraImageViews[i] = perLevelImageViews[i];
|
||||||
perLevelImageViews[i] = VK_NULL_HANDLE;
|
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 QSize size = m_pixelSize.isEmpty() ? QSize(1, 1) : m_pixelSize;
|
||||||
const bool isCube = m_flags.testFlag(CubeMap);
|
const bool isCube = m_flags.testFlag(CubeMap);
|
||||||
|
const bool is3D = m_flags.testFlag(ThreeDimensional);
|
||||||
const bool hasMipMaps = m_flags.testFlag(MipMapped);
|
const bool hasMipMaps = m_flags.testFlag(MipMapped);
|
||||||
|
|
||||||
mipLevelCount = uint(hasMipMaps ? rhiD->q->mipLevelsForSize(size) : 1);
|
mipLevelCount = uint(hasMipMaps ? rhiD->q->mipLevelsForSize(size) : 1);
|
||||||
const int maxLevels = QRhi::MAX_LEVELS;
|
const int maxLevels = QRhi::MAX_MIP_LEVELS;
|
||||||
if (mipLevelCount > maxLevels) {
|
if (mipLevelCount > maxLevels) {
|
||||||
qWarning("Too many mip levels (%d, max is %d), truncating mip chain", mipLevelCount, maxLevels);
|
qWarning("Too many mip levels (%d, max is %d), truncating mip chain", mipLevelCount, maxLevels);
|
||||||
mipLevelCount = maxLevels;
|
mipLevelCount = maxLevels;
|
||||||
@ -5808,11 +5843,24 @@ bool QVkTexture::prepareCreate(QSize *adjustedSize)
|
|||||||
qWarning("Cubemap texture cannot be multisample");
|
qWarning("Cubemap texture cannot be multisample");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
if (is3D) {
|
||||||
|
qWarning("3D texture cannot be multisample");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
if (hasMipMaps) {
|
if (hasMipMaps) {
|
||||||
qWarning("Multisample texture cannot have mipmaps");
|
qWarning("Multisample texture cannot have mipmaps");
|
||||||
return false;
|
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.layout = VK_IMAGE_LAYOUT_PREINITIALIZED;
|
||||||
usageState.access = 0;
|
usageState.access = 0;
|
||||||
@ -5830,12 +5878,13 @@ bool QVkTexture::finishCreate()
|
|||||||
|
|
||||||
const auto aspectMask = aspectMaskForTextureFormat(m_format);
|
const auto aspectMask = aspectMaskForTextureFormat(m_format);
|
||||||
const bool isCube = m_flags.testFlag(CubeMap);
|
const bool isCube = m_flags.testFlag(CubeMap);
|
||||||
|
const bool is3D = m_flags.testFlag(ThreeDimensional);
|
||||||
|
|
||||||
VkImageViewCreateInfo viewInfo;
|
VkImageViewCreateInfo viewInfo;
|
||||||
memset(&viewInfo, 0, sizeof(viewInfo));
|
memset(&viewInfo, 0, sizeof(viewInfo));
|
||||||
viewInfo.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO;
|
viewInfo.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO;
|
||||||
viewInfo.image = image;
|
viewInfo.image = image;
|
||||||
viewInfo.viewType = isCube ? VK_IMAGE_VIEW_TYPE_CUBE : 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.format = vkformat;
|
||||||
viewInfo.components.r = VK_COMPONENT_SWIZZLE_R;
|
viewInfo.components.r = VK_COMPONENT_SWIZZLE_R;
|
||||||
viewInfo.components.g = VK_COMPONENT_SWIZZLE_G;
|
viewInfo.components.g = VK_COMPONENT_SWIZZLE_G;
|
||||||
@ -5863,19 +5912,39 @@ bool QVkTexture::create()
|
|||||||
if (!prepareCreate(&size))
|
if (!prepareCreate(&size))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
|
QRHI_RES_RHI(QRhiVulkan);
|
||||||
const bool isRenderTarget = m_flags.testFlag(QRhiTexture::RenderTarget);
|
const bool isRenderTarget = m_flags.testFlag(QRhiTexture::RenderTarget);
|
||||||
const bool isDepth = isDepthTextureFormat(m_format);
|
const bool isDepth = isDepthTextureFormat(m_format);
|
||||||
const bool isCube = m_flags.testFlag(CubeMap);
|
const bool isCube = m_flags.testFlag(CubeMap);
|
||||||
|
const bool is3D = m_flags.testFlag(ThreeDimensional);
|
||||||
|
|
||||||
VkImageCreateInfo imageInfo;
|
VkImageCreateInfo imageInfo;
|
||||||
memset(&imageInfo, 0, sizeof(imageInfo));
|
memset(&imageInfo, 0, sizeof(imageInfo));
|
||||||
imageInfo.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO;
|
imageInfo.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO;
|
||||||
imageInfo.flags = isCube ? VK_IMAGE_CREATE_CUBE_COMPATIBLE_BIT : 0;
|
imageInfo.flags = 0;
|
||||||
imageInfo.imageType = VK_IMAGE_TYPE_2D;
|
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.format = vkformat;
|
||||||
imageInfo.extent.width = uint32_t(size.width());
|
imageInfo.extent.width = uint32_t(size.width());
|
||||||
imageInfo.extent.height = uint32_t(size.height());
|
imageInfo.extent.height = uint32_t(size.height());
|
||||||
imageInfo.extent.depth = 1;
|
imageInfo.extent.depth = is3D ? m_depth : 1;
|
||||||
imageInfo.mipLevels = mipLevelCount;
|
imageInfo.mipLevels = mipLevelCount;
|
||||||
imageInfo.arrayLayers = isCube ? 6 : 1;
|
imageInfo.arrayLayers = isCube ? 6 : 1;
|
||||||
imageInfo.samples = samples;
|
imageInfo.samples = samples;
|
||||||
@ -5900,7 +5969,6 @@ bool QVkTexture::create()
|
|||||||
memset(&allocInfo, 0, sizeof(allocInfo));
|
memset(&allocInfo, 0, sizeof(allocInfo));
|
||||||
allocInfo.usage = VMA_MEMORY_USAGE_GPU_ONLY;
|
allocInfo.usage = VMA_MEMORY_USAGE_GPU_ONLY;
|
||||||
|
|
||||||
QRHI_RES_RHI(QRhiVulkan);
|
|
||||||
VmaAllocation allocation;
|
VmaAllocation allocation;
|
||||||
VkResult err = vmaCreateImage(toVmaAllocator(rhiD->allocator), &imageInfo, &allocInfo, &image, &allocation, nullptr);
|
VkResult err = vmaCreateImage(toVmaAllocator(rhiD->allocator), &imageInfo, &allocInfo, &image, &allocation, nullptr);
|
||||||
if (err != VK_SUCCESS) {
|
if (err != VK_SUCCESS) {
|
||||||
@ -5965,12 +6033,13 @@ VkImageView QVkTexture::imageViewForLevel(int level)
|
|||||||
|
|
||||||
const VkImageAspectFlags aspectMask = aspectMaskForTextureFormat(m_format);
|
const VkImageAspectFlags aspectMask = aspectMaskForTextureFormat(m_format);
|
||||||
const bool isCube = m_flags.testFlag(CubeMap);
|
const bool isCube = m_flags.testFlag(CubeMap);
|
||||||
|
const bool is3D = m_flags.testFlag(ThreeDimensional);
|
||||||
|
|
||||||
VkImageViewCreateInfo viewInfo;
|
VkImageViewCreateInfo viewInfo;
|
||||||
memset(&viewInfo, 0, sizeof(viewInfo));
|
memset(&viewInfo, 0, sizeof(viewInfo));
|
||||||
viewInfo.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO;
|
viewInfo.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO;
|
||||||
viewInfo.image = image;
|
viewInfo.image = image;
|
||||||
viewInfo.viewType = isCube ? VK_IMAGE_VIEW_TYPE_CUBE : 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.format = vkformat;
|
||||||
viewInfo.components.r = VK_COMPONENT_SWIZZLE_R;
|
viewInfo.components.r = VK_COMPONENT_SWIZZLE_R;
|
||||||
viewInfo.components.g = VK_COMPONENT_SWIZZLE_G;
|
viewInfo.components.g = VK_COMPONENT_SWIZZLE_G;
|
||||||
@ -6645,7 +6714,7 @@ bool QVkGraphicsPipeline::create()
|
|||||||
? VK_VERTEX_INPUT_RATE_VERTEX : VK_VERTEX_INPUT_RATE_INSTANCE
|
? VK_VERTEX_INPUT_RATE_VERTEX : VK_VERTEX_INPUT_RATE_INSTANCE
|
||||||
};
|
};
|
||||||
if (it->classification() == QRhiVertexInputBinding::PerInstance && it->instanceStepRate() != 1) {
|
if (it->classification() == QRhiVertexInputBinding::PerInstance && it->instanceStepRate() != 1) {
|
||||||
if (rhiD->vertexAttribDivisorAvailable) {
|
if (rhiD->caps.vertexAttribDivisor) {
|
||||||
nonOneStepRates.append({ uint32_t(bindingIndex), uint32_t(it->instanceStepRate()) });
|
nonOneStepRates.append({ uint32_t(bindingIndex), uint32_t(it->instanceStepRate()) });
|
||||||
} else {
|
} else {
|
||||||
qWarning("QRhiVulkan: Instance step rates other than 1 not supported without "
|
qWarning("QRhiVulkan: Instance step rates other than 1 not supported without "
|
||||||
@ -6722,7 +6791,7 @@ bool QVkGraphicsPipeline::create()
|
|||||||
rastInfo.depthBiasConstantFactor = float(m_depthBias);
|
rastInfo.depthBiasConstantFactor = float(m_depthBias);
|
||||||
rastInfo.depthBiasSlopeFactor = m_slopeScaledDepthBias;
|
rastInfo.depthBiasSlopeFactor = m_slopeScaledDepthBias;
|
||||||
}
|
}
|
||||||
rastInfo.lineWidth = rhiD->hasWideLines ? m_lineWidth : 1.0f;
|
rastInfo.lineWidth = rhiD->caps.wideLines ? m_lineWidth : 1.0f;
|
||||||
pipelineInfo.pRasterizationState = &rastInfo;
|
pipelineInfo.pRasterizationState = &rastInfo;
|
||||||
|
|
||||||
VkPipelineMultisampleStateCreateInfo msInfo;
|
VkPipelineMultisampleStateCreateInfo msInfo;
|
||||||
|
@ -128,7 +128,7 @@ struct QVkRenderBuffer : public QRhiRenderBuffer
|
|||||||
|
|
||||||
struct QVkTexture : public QRhiTexture
|
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);
|
int sampleCount, Flags flags);
|
||||||
~QVkTexture();
|
~QVkTexture();
|
||||||
void destroy() override;
|
void destroy() override;
|
||||||
@ -146,7 +146,7 @@ struct QVkTexture : public QRhiTexture
|
|||||||
QVkAlloc imageAlloc = nullptr;
|
QVkAlloc imageAlloc = nullptr;
|
||||||
VkBuffer stagingBuffers[QVK_FRAMES_IN_FLIGHT];
|
VkBuffer stagingBuffers[QVK_FRAMES_IN_FLIGHT];
|
||||||
QVkAlloc stagingAllocations[QVK_FRAMES_IN_FLIGHT];
|
QVkAlloc stagingAllocations[QVK_FRAMES_IN_FLIGHT];
|
||||||
VkImageView perLevelImageViews[QRhi::MAX_LEVELS];
|
VkImageView perLevelImageViews[QRhi::MAX_MIP_LEVELS];
|
||||||
bool owns = true;
|
bool owns = true;
|
||||||
struct UsageState {
|
struct UsageState {
|
||||||
// no tracking of subresource layouts (some operations can keep
|
// no tracking of subresource layouts (some operations can keep
|
||||||
@ -676,6 +676,7 @@ public:
|
|||||||
QRhiTexture::Format backingFormatHint) override;
|
QRhiTexture::Format backingFormatHint) override;
|
||||||
QRhiTexture *createTexture(QRhiTexture::Format format,
|
QRhiTexture *createTexture(QRhiTexture::Format format,
|
||||||
const QSize &pixelSize,
|
const QSize &pixelSize,
|
||||||
|
int depth,
|
||||||
int sampleCount,
|
int sampleCount,
|
||||||
QRhiTexture::Flags flags) override;
|
QRhiTexture::Flags flags) override;
|
||||||
QRhiSampler *createSampler(QRhiSampler::Filter magFilter,
|
QRhiSampler *createSampler(QRhiSampler::Filter magFilter,
|
||||||
@ -846,7 +847,6 @@ public:
|
|||||||
int gfxQueueFamilyIdx = -1;
|
int gfxQueueFamilyIdx = -1;
|
||||||
int gfxQueueIdx = 0;
|
int gfxQueueIdx = 0;
|
||||||
VkQueue gfxQueue = VK_NULL_HANDLE;
|
VkQueue gfxQueue = VK_NULL_HANDLE;
|
||||||
bool hasCompute = false;
|
|
||||||
quint32 timestampValidBits = 0;
|
quint32 timestampValidBits = 0;
|
||||||
bool importedAllocator = false;
|
bool importedAllocator = false;
|
||||||
QVkAllocator allocator = nullptr;
|
QVkAllocator allocator = nullptr;
|
||||||
@ -857,12 +857,9 @@ public:
|
|||||||
VkPhysicalDeviceProperties physDevProperties;
|
VkPhysicalDeviceProperties physDevProperties;
|
||||||
VkDeviceSize ubufAlign;
|
VkDeviceSize ubufAlign;
|
||||||
VkDeviceSize texbufAlign;
|
VkDeviceSize texbufAlign;
|
||||||
bool hasWideLines = false;
|
|
||||||
bool deviceLost = false;
|
bool deviceLost = false;
|
||||||
bool releaseCachedResourcesCalledBeforeFrameStart = false;
|
bool releaseCachedResourcesCalledBeforeFrameStart = false;
|
||||||
|
|
||||||
bool debugMarkersAvailable = false;
|
|
||||||
bool vertexAttribDivisorAvailable = false;
|
|
||||||
PFN_vkCmdDebugMarkerBeginEXT vkCmdDebugMarkerBegin = nullptr;
|
PFN_vkCmdDebugMarkerBeginEXT vkCmdDebugMarkerBegin = nullptr;
|
||||||
PFN_vkCmdDebugMarkerEndEXT vkCmdDebugMarkerEnd = nullptr;
|
PFN_vkCmdDebugMarkerEndEXT vkCmdDebugMarkerEnd = nullptr;
|
||||||
PFN_vkCmdDebugMarkerInsertEXT vkCmdDebugMarkerInsert = nullptr;
|
PFN_vkCmdDebugMarkerInsertEXT vkCmdDebugMarkerInsert = nullptr;
|
||||||
@ -877,6 +874,14 @@ public:
|
|||||||
PFN_vkGetPhysicalDeviceSurfaceFormatsKHR vkGetPhysicalDeviceSurfaceFormatsKHR;
|
PFN_vkGetPhysicalDeviceSurfaceFormatsKHR vkGetPhysicalDeviceSurfaceFormatsKHR;
|
||||||
PFN_vkGetPhysicalDeviceSurfacePresentModesKHR vkGetPhysicalDeviceSurfacePresentModesKHR;
|
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;
|
VkPipelineCache pipelineCache = VK_NULL_HANDLE;
|
||||||
struct DescriptorPoolData {
|
struct DescriptorPoolData {
|
||||||
DescriptorPoolData() { }
|
DescriptorPoolData() { }
|
||||||
@ -978,7 +983,7 @@ public:
|
|||||||
QVkAlloc allocation;
|
QVkAlloc allocation;
|
||||||
VkBuffer stagingBuffers[QVK_FRAMES_IN_FLIGHT];
|
VkBuffer stagingBuffers[QVK_FRAMES_IN_FLIGHT];
|
||||||
QVkAlloc stagingAllocations[QVK_FRAMES_IN_FLIGHT];
|
QVkAlloc stagingAllocations[QVK_FRAMES_IN_FLIGHT];
|
||||||
VkImageView extraImageViews[QRhi::MAX_LEVELS];
|
VkImageView extraImageViews[QRhi::MAX_MIP_LEVELS];
|
||||||
} texture;
|
} texture;
|
||||||
struct {
|
struct {
|
||||||
VkSampler sampler;
|
VkSampler sampler;
|
||||||
|
@ -135,6 +135,8 @@ private slots:
|
|||||||
void textureImportOpenGL();
|
void textureImportOpenGL();
|
||||||
void renderbufferImportOpenGL_data();
|
void renderbufferImportOpenGL_data();
|
||||||
void renderbufferImportOpenGL();
|
void renderbufferImportOpenGL();
|
||||||
|
void threeDimTexture_data();
|
||||||
|
void threeDimTexture();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void setWindowType(QWindow *window, QRhi::Implementation impl);
|
void setWindowType(QWindow *window, QRhi::Implementation impl);
|
||||||
@ -169,6 +171,11 @@ void tst_QRhi::initTestCase()
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef TST_VK
|
#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.setLayers({ "VK_LAYER_KHRONOS_validation" });
|
||||||
vulkanInstance.setExtensions(QRhiVulkanInitParams::preferredInstanceExtensions());
|
vulkanInstance.setExtensions(QRhiVulkanInitParams::preferredInstanceExtensions());
|
||||||
vulkanInstance.create();
|
vulkanInstance.create();
|
||||||
@ -358,7 +365,8 @@ void tst_QRhi::create()
|
|||||||
QRhi::ReadBackAnyTextureFormat,
|
QRhi::ReadBackAnyTextureFormat,
|
||||||
QRhi::PipelineCacheDataLoadSave,
|
QRhi::PipelineCacheDataLoadSave,
|
||||||
QRhi::ImageDataStride,
|
QRhi::ImageDataStride,
|
||||||
QRhi::RenderBufferImport
|
QRhi::RenderBufferImport,
|
||||||
|
QRhi::ThreeDimensionalTextures
|
||||||
};
|
};
|
||||||
for (size_t i = 0; i <sizeof(features) / sizeof(QRhi::Feature); ++i)
|
for (size_t i = 0; i <sizeof(features) / sizeof(QRhi::Feature); ++i)
|
||||||
rhi->isFeatureSupported(features[i]);
|
rhi->isFeatureSupported(features[i]);
|
||||||
@ -3817,5 +3825,100 @@ void tst_QRhi::renderbufferImportOpenGL()
|
|||||||
#endif
|
#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>
|
#include <tst_qrhi.moc>
|
||||||
QTEST_MAIN(tst_QRhi)
|
QTEST_MAIN(tst_QRhi)
|
||||||
|
@ -21,6 +21,7 @@ add_subdirectory(computebuffer)
|
|||||||
add_subdirectory(computeimage)
|
add_subdirectory(computeimage)
|
||||||
add_subdirectory(instancing)
|
add_subdirectory(instancing)
|
||||||
add_subdirectory(noninstanced)
|
add_subdirectory(noninstanced)
|
||||||
|
add_subdirectory(tex3d)
|
||||||
if(QT_FEATURE_widgets)
|
if(QT_FEATURE_widgets)
|
||||||
add_subdirectory(qrhiprof)
|
add_subdirectory(qrhiprof)
|
||||||
endif()
|
endif()
|
||||||
|
@ -21,7 +21,8 @@ SUBDIRS += \
|
|||||||
computebuffer \
|
computebuffer \
|
||||||
computeimage \
|
computeimage \
|
||||||
instancing \
|
instancing \
|
||||||
noninstanced
|
noninstanced \
|
||||||
|
tex3d
|
||||||
|
|
||||||
qtConfig(widgets) {
|
qtConfig(widgets) {
|
||||||
SUBDIRS += \
|
SUBDIRS += \
|
||||||
|
@ -538,6 +538,12 @@ int main(int argc, char **argv)
|
|||||||
qDebug("Enabling Vulkan validation layer (if available)");
|
qDebug("Enabling Vulkan validation layer (if available)");
|
||||||
inst.setLayers({ "VK_LAYER_KHRONOS_validation" });
|
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());
|
inst.setExtensions(QRhiVulkanInitParams::preferredInstanceExtensions());
|
||||||
if (!inst.create()) {
|
if (!inst.create()) {
|
||||||
qWarning("Failed to create Vulkan instance, switching to OpenGL");
|
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