summaryrefslogtreecommitdiffstats
path: root/src/gui/rhi
diff options
context:
space:
mode:
Diffstat (limited to 'src/gui/rhi')
-rw-r--r--src/gui/rhi/qrhi.cpp111
-rw-r--r--src/gui/rhi/qrhi_p.h34
-rw-r--r--src/gui/rhi/qrhid3d11.cpp67
-rw-r--r--src/gui/rhi/qrhid3d11_p.h5
-rw-r--r--src/gui/rhi/qrhid3d11_p_p.h6
-rw-r--r--src/gui/rhi/qrhigles2.cpp354
-rw-r--r--src/gui/rhi/qrhigles2_p.h5
-rw-r--r--src/gui/rhi/qrhigles2_p_p.h28
-rw-r--r--src/gui/rhi/qrhimetal.mm348
-rw-r--r--src/gui/rhi/qrhimetal_p.h5
-rw-r--r--src/gui/rhi/qrhimetal_p_p.h13
-rw-r--r--src/gui/rhi/qrhinull.cpp22
-rw-r--r--src/gui/rhi/qrhinull_p.h4
-rw-r--r--src/gui/rhi/qrhinull_p_p.h5
-rw-r--r--src/gui/rhi/qrhivulkan.cpp442
-rw-r--r--src/gui/rhi/qrhivulkan_p.h6
-rw-r--r--src/gui/rhi/qrhivulkan_p_p.h32
-rw-r--r--src/gui/rhi/qshader.cpp182
-rw-r--r--src/gui/rhi/qshader_p.h5
-rw-r--r--src/gui/rhi/qshader_p_p.h11
-rw-r--r--src/gui/rhi/qshaderdescription.cpp382
-rw-r--r--src/gui/rhi/qshaderdescription_p.h47
-rw-r--r--src/gui/rhi/qshaderdescription_p_p.h2
-rw-r--r--src/gui/rhi/rhi.pri6
24 files changed, 1625 insertions, 497 deletions
diff --git a/src/gui/rhi/qrhi.cpp b/src/gui/rhi/qrhi.cpp
index dabad35688..a9c6835687 100644
--- a/src/gui/rhi/qrhi.cpp
+++ b/src/gui/rhi/qrhi.cpp
@@ -48,8 +48,7 @@
#ifdef Q_OS_WIN
#include "qrhid3d11_p_p.h"
#endif
-//#ifdef Q_OS_DARWIN
-#ifdef Q_OS_MACOS
+#if defined(Q_OS_MACOS) || defined(Q_OS_IOS)
#include "qrhimetal_p_p.h"
#endif
@@ -2161,6 +2160,32 @@ QRhiResource::Type QRhiRenderBuffer::resourceType() const
*/
/*!
+ \class QRhiTexture::NativeTexture
+ \brief Contains information about the underlying native resources of a texture.
+ */
+
+/*!
+ \variable QRhiTexture::NativeTexture::object
+ \brief a pointer to the native object handle.
+
+ With OpenGL, the native handle is a GLuint value, so \c object is then a
+ pointer to a GLuint. With Vulkan, the native handle is a VkImage, so \c
+ object is a pointer to a VkImage. With Direct3D 11 and Metal \c
+ object is a pointer to a ID3D11Texture2D or MTLTexture pointer, respectively.
+
+ \note Pay attention to the fact that \a object is always a pointer
+ to the native texture handle type, even if the native type itself is a
+ pointer.
+ */
+
+/*!
+ \variable QRhiTexture::NativeTexture::layout
+ \brief Specifies the current image layout for APIs like Vulkan.
+
+ For Vulkan, \c layout contains a \c VkImageLayout value.
+ */
+
+/*!
\internal
*/
QRhiTexture::QRhiTexture(QRhiImplementation *rhi, Format format_, const QSize &pixelSize_,
@@ -2190,21 +2215,20 @@ QRhiResource::Type QRhiTexture::resourceType() const
*/
/*!
- \return a pointer to a backend-specific QRhiNativeHandles subclass, such as
- QRhiVulkanTextureNativeHandles. The returned value is null when exposing
- the underlying native resources is not supported by the backend.
+ \return the underlying native resources for this texture. The returned value
+ will be empty if exposing the underlying native resources is not supported by
+ the backend.
- \sa QRhiVulkanTextureNativeHandles, QRhiD3D11TextureNativeHandles,
- QRhiMetalTextureNativeHandles, QRhiGles2TextureNativeHandles
+ \sa buildFrom()
*/
-const QRhiNativeHandles *QRhiTexture::nativeHandles()
+QRhiTexture::NativeTexture QRhiTexture::nativeTexture()
{
- return nullptr;
+ return {};
}
/*!
Similar to build() except that no new native textures are created. Instead,
- the texture from \a src is used.
+ the native texture resources specified by \a src is used.
This allows importing an existing native texture object (which must belong
to the same device or sharing context, depending on the graphics API) from
@@ -2220,12 +2244,10 @@ const QRhiNativeHandles *QRhiTexture::nativeHandles()
does not free the object or any associated memory.
The opposite of this operation, exposing a QRhiTexture-created native
- texture object to a foreign engine, is possible via nativeHandles().
+ texture object to a foreign engine, is possible via nativeTexture().
- \sa QRhiVulkanTextureNativeHandles, QRhiD3D11TextureNativeHandles,
- QRhiMetalTextureNativeHandles, QRhiGles2TextureNativeHandles
- */
-bool QRhiTexture::buildFrom(const QRhiNativeHandles *src)
+*/
+bool QRhiTexture::buildFrom(QRhiTexture::NativeTexture src)
{
Q_UNUSED(src);
return false;
@@ -2253,9 +2275,7 @@ bool QRhiTexture::buildFrom(const QRhiNativeHandles *src)
\value Repeat
\value ClampToEdge
- \value Border
\value Mirror
- \value MirrorOnce
*/
/*!
@@ -2322,8 +2342,26 @@ QRhiResource::Type QRhiRenderPassDescriptor::resourceType() const
}
/*!
+ \fn bool QRhiRenderPassDescriptor::isCompatible(const QRhiRenderPassDescriptor *other) const;
+
+ \return true if the \a other QRhiRenderPassDescriptor is compatible with
+ this one, meaning \c this and \a other can be used interchangebly in
+ QRhiGraphicsPipeline::setRenderPassDescriptor().
+
+ The concept of the compatibility of renderpass descriptors is similar to
+ the \l{QRhiShaderResourceBindings::isLayoutCompatible}{layout
+ compatibility} of QRhiShaderResourceBindings instances. They allow better
+ reuse of QRhiGraphicsPipeline instances: for example, a
+ QRhiGraphicsPipeline instance cache is expected to use these functions to
+ look for a matching pipeline, instead of just comparing pointers, thus
+ allowing a different QRhiRenderPassDescriptor and
+ QRhiShaderResourceBindings to be used in combination with the pipeline, as
+ long as they are compatible.
+ */
+
+/*!
\return a pointer to a backend-specific QRhiNativeHandles subclass, such as
- QRhiVulkanRenderPassNativeHandles. The returned value is null when exposing
+ QRhiVulkanRenderPassNativeHandles. The returned value is \nullptr when exposing
the underlying native resources is not supported by the backend.
\sa QRhiVulkanRenderPassNativeHandles
@@ -3518,14 +3556,36 @@ QRhiResource::Type QRhiSwapChain::resourceType() const
\c{currentPixelSize() != surfacePixelSize()} then the swapchain needs to be
resized.
+ \note Typical rendering logic will call this function to get the output
+ size when starting to prepare a new frame, and base dependent calculations
+ (such as, the viewport) on the size returned from this function.
+
+ While in many cases the value is the same as \c{QWindow::size() *
+ QWindow::devicePixelRatio()}, relying on the QWindow-reported size is not
+ guaranteed to be correct on all platforms and graphics API implementations.
+ Using this function is therefore strongly recommended whenever there is a
+ need to identify the dimensions, in pixels, of the output layer or surface.
+
+ This also has the added benefit of avoiding potential data races when QRhi
+ is used on a dedicated rendering thread, because the need to call QWindow
+ functions, that may then access data updated on the main thread, is
+ avoided.
+
\sa surfacePixelSize()
*/
/*!
\fn QSize QRhiSwapChain::surfacePixelSize()
- \return The size of the window's associated surface or layer. Do not assume
- this is the same as QWindow::size() * QWindow::devicePixelRatio().
+ \return The size of the window's associated surface or layer.
+
+ \warning Do not assume this is the same as \c{QWindow::size() *
+ QWindow::devicePixelRatio()}. With some graphics APIs and windowing system
+ interfaces (for example, Vulkan) there is a theoretical possibility for a
+ surface to assume a size different from the associated window. To support
+ these cases, rendering logic must always base size-derived calculations
+ (such as, viewports) on the size reported from QRhiSwapChain, and never on
+ the size queried from QWindow.
\note Can also be called before buildOrResize(), if at least window() is
already set) This in combination with currentPixelSize() allows to detect
@@ -3879,6 +3939,12 @@ void QRhiImplementation::textureFormatInfo(QRhiTexture::Format format, const QSi
case QRhiTexture::RGBA32F:
bpc = 16;
break;
+ case QRhiTexture::R16F:
+ bpc = 2;
+ break;
+ case QRhiTexture::R32F:
+ bpc = 4;
+ break;
case QRhiTexture::D16:
bpc = 2;
@@ -4027,8 +4093,7 @@ QRhi *QRhi::create(Implementation impl, QRhiInitParams *params, Flags flags, QRh
break;
#endif
case Metal:
-//#ifdef Q_OS_DARWIN
-#ifdef Q_OS_MACOS
+#if defined(Q_OS_MACOS) || defined(Q_OS_IOS)
r->d = new QRhiMetal(static_cast<QRhiMetalInitParams *>(params),
static_cast<QRhiMetalNativeHandles *>(importDevice));
break;
@@ -4868,7 +4933,7 @@ void QRhiCommandBuffer::dispatch(int x, int y, int z)
/*!
\return a pointer to a backend-specific QRhiNativeHandles subclass, such as
- QRhiVulkanCommandBufferNativeHandles. The returned value is null when
+ QRhiVulkanCommandBufferNativeHandles. The returned value is \nullptr when
exposing the underlying native resources is not supported by, or not
applicable to, the backend.
diff --git a/src/gui/rhi/qrhi_p.h b/src/gui/rhi/qrhi_p.h
index 907924c788..3a64835c22 100644
--- a/src/gui/rhi/qrhi_p.h
+++ b/src/gui/rhi/qrhi_p.h
@@ -246,10 +246,6 @@ public:
m_bindings.clear();
std::copy(first, last, std::back_inserter(m_bindings));
}
- void setBindings(const QVector<QRhiVertexInputBinding> &bindings) // compat., to be removed
- {
- setBindings(bindings.cbegin(), bindings.cend());
- }
const QRhiVertexInputBinding *cbeginBindings() const { return m_bindings.cbegin(); }
const QRhiVertexInputBinding *cendBindings() const { return m_bindings.cend(); }
const QRhiVertexInputBinding *bindingAt(int index) const { return &m_bindings.at(index); }
@@ -261,10 +257,6 @@ public:
m_attributes.clear();
std::copy(first, last, std::back_inserter(m_attributes));
}
- void setAttributes(const QVector<QRhiVertexInputAttribute> &attributes) // compat., to be removed
- {
- setAttributes(attributes.cbegin(), attributes.cend());
- }
const QRhiVertexInputAttribute *cbeginAttributes() const { return m_attributes.cbegin(); }
const QRhiVertexInputAttribute *cendAttributes() const { return m_attributes.cend(); }
@@ -551,9 +543,6 @@ public:
QRhiTextureUploadDescription() = default;
QRhiTextureUploadDescription(const QRhiTextureUploadEntry &entry);
QRhiTextureUploadDescription(std::initializer_list<QRhiTextureUploadEntry> list);
- QRhiTextureUploadDescription(const QVector<QRhiTextureUploadEntry> &entries) // compat., to be removed
- : m_entries(entries.cbegin(), entries.cend())
- { }
void setEntries(std::initializer_list<QRhiTextureUploadEntry> list) { m_entries = list; }
template<typename InputIterator>
@@ -739,6 +728,8 @@ public:
RGBA16F,
RGBA32F,
+ R16F,
+ R32F,
D16,
D32F,
@@ -771,6 +762,11 @@ public:
ASTC_12x12
};
+ struct NativeTexture {
+ const void *object;
+ int layout;
+ };
+
QRhiResource::Type resourceType() const override;
Format format() const { return m_format; }
@@ -786,8 +782,8 @@ public:
void setSampleCount(int s) { m_sampleCount = s; }
virtual bool build() = 0;
- virtual const QRhiNativeHandles *nativeHandles();
- virtual bool buildFrom(const QRhiNativeHandles *src);
+ virtual NativeTexture nativeTexture();
+ virtual bool buildFrom(NativeTexture src);
protected:
QRhiTexture(QRhiImplementation *rhi, Format format_, const QSize &pixelSize_,
@@ -812,9 +808,7 @@ public:
enum AddressMode {
Repeat,
ClampToEdge,
- Border,
Mirror,
- MirrorOnce
};
enum CompareOp {
@@ -913,6 +907,7 @@ class Q_GUI_EXPORT QRhiRenderPassDescriptor : public QRhiResource
public:
QRhiResource::Type resourceType() const override;
+ virtual bool isCompatible(const QRhiRenderPassDescriptor *other) const = 0;
virtual const QRhiNativeHandles *nativeHandles();
protected:
@@ -979,11 +974,6 @@ public:
std::copy(first, last, std::back_inserter(m_bindings));
}
- void setBindings(const QVector<QRhiShaderResourceBinding> &bindings) // compat., to be removed
- {
- setBindings(bindings.cbegin(), bindings.cend());
- }
-
const QRhiShaderResourceBinding *cbeginBindings() const { return m_bindings.cbegin(); }
const QRhiShaderResourceBinding *cendBindings() const { return m_bindings.cend(); }
@@ -1172,10 +1162,6 @@ public:
m_shaderStages.clear();
std::copy(first, last, std::back_inserter(m_shaderStages));
}
- void setShaderStages(const QVector<QRhiShaderStage> &stages) // compat., to be removed
- {
- setShaderStages(stages.cbegin(), stages.cend());
- }
const QRhiShaderStage *cbeginShaderStages() const { return m_shaderStages.cbegin(); }
const QRhiShaderStage *cendShaderStages() const { return m_shaderStages.cend(); }
diff --git a/src/gui/rhi/qrhid3d11.cpp b/src/gui/rhi/qrhid3d11.cpp
index 717f3e6d6c..445d162595 100644
--- a/src/gui/rhi/qrhid3d11.cpp
+++ b/src/gui/rhi/qrhid3d11.cpp
@@ -108,17 +108,6 @@ QT_BEGIN_NAMESPACE
\c{ID3D11Device *} and \c{ID3D11DeviceContext *}.
*/
-/*!
- \class QRhiD3D11TextureNativeHandles
- \internal
- \inmodule QtGui
- \brief Holds the D3D texture object that is backing a QRhiTexture instance.
-
- \note The class uses \c{void *} as the type since including the COM-based
- \c{d3d11.h} headers is not acceptable here. The actual type is
- \c{ID3D11Texture2D *}.
- */
-
// help mingw with its ancient sdk headers
#ifndef DXGI_ADAPTER_FLAG_SOFTWARE
#define DXGI_ADAPTER_FLAG_SOFTWARE 2
@@ -641,9 +630,7 @@ void QRhiD3D11::setShaderResources(QRhiCommandBuffer *cb, QRhiShaderResourceBind
}
break;
case QRhiShaderResourceBinding::ImageLoad:
- Q_FALLTHROUGH();
case QRhiShaderResourceBinding::ImageStore:
- Q_FALLTHROUGH();
case QRhiShaderResourceBinding::ImageLoadStore:
{
QD3D11Texture *texD = QRHI_RES(QD3D11Texture, b->u.simage.tex);
@@ -655,9 +642,7 @@ void QRhiD3D11::setShaderResources(QRhiCommandBuffer *cb, QRhiShaderResourceBind
}
break;
case QRhiShaderResourceBinding::BufferLoad:
- Q_FALLTHROUGH();
case QRhiShaderResourceBinding::BufferStore:
- Q_FALLTHROUGH();
case QRhiShaderResourceBinding::BufferLoadStore:
{
QD3D11Buffer *bufD = QRHI_RES(QD3D11Buffer, b->u.sbuf.buf);
@@ -1117,6 +1102,10 @@ static inline DXGI_FORMAT toD3DTextureFormat(QRhiTexture::Format format, QRhiTex
return DXGI_FORMAT_R16G16B16A16_FLOAT;
case QRhiTexture::RGBA32F:
return DXGI_FORMAT_R32G32B32A32_FLOAT;
+ case QRhiTexture::R16F:
+ return DXGI_FORMAT_R16_FLOAT;
+ case QRhiTexture::R32F:
+ return DXGI_FORMAT_R32_FLOAT;
case QRhiTexture::D16:
return DXGI_FORMAT_R16_TYPELESS;
@@ -1139,39 +1128,24 @@ static inline DXGI_FORMAT toD3DTextureFormat(QRhiTexture::Format format, QRhiTex
return srgb ? DXGI_FORMAT_BC7_UNORM_SRGB : DXGI_FORMAT_BC7_UNORM;
case QRhiTexture::ETC2_RGB8:
- Q_FALLTHROUGH();
case QRhiTexture::ETC2_RGB8A1:
- Q_FALLTHROUGH();
case QRhiTexture::ETC2_RGBA8:
qWarning("QRhiD3D11 does not support ETC2 textures");
return DXGI_FORMAT_R8G8B8A8_UNORM;
case QRhiTexture::ASTC_4x4:
- Q_FALLTHROUGH();
case QRhiTexture::ASTC_5x4:
- Q_FALLTHROUGH();
case QRhiTexture::ASTC_5x5:
- Q_FALLTHROUGH();
case QRhiTexture::ASTC_6x5:
- Q_FALLTHROUGH();
case QRhiTexture::ASTC_6x6:
- Q_FALLTHROUGH();
case QRhiTexture::ASTC_8x5:
- Q_FALLTHROUGH();
case QRhiTexture::ASTC_8x6:
- Q_FALLTHROUGH();
case QRhiTexture::ASTC_8x8:
- Q_FALLTHROUGH();
case QRhiTexture::ASTC_10x5:
- Q_FALLTHROUGH();
case QRhiTexture::ASTC_10x6:
- Q_FALLTHROUGH();
case QRhiTexture::ASTC_10x8:
- Q_FALLTHROUGH();
case QRhiTexture::ASTC_10x10:
- Q_FALLTHROUGH();
case QRhiTexture::ASTC_12x10:
- Q_FALLTHROUGH();
case QRhiTexture::ASTC_12x12:
qWarning("QRhiD3D11 does not support ASTC textures");
return DXGI_FORMAT_R8G8B8A8_UNORM;
@@ -1212,7 +1186,6 @@ static inline bool isDepthTextureFormat(QRhiTexture::Format format)
{
switch (format) {
case QRhiTexture::Format::D16:
- Q_FALLTHROUGH();
case QRhiTexture::Format::D32F:
return true;
@@ -1878,9 +1851,7 @@ void QRhiD3D11::updateShaderResourceBindings(QD3D11ShaderResourceBindings *srbD)
}
break;
case QRhiShaderResourceBinding::ImageLoad:
- Q_FALLTHROUGH();
case QRhiShaderResourceBinding::ImageStore:
- Q_FALLTHROUGH();
case QRhiShaderResourceBinding::ImageLoadStore:
{
QD3D11Texture *texD = QRHI_RES(QD3D11Texture, b->u.simage.tex);
@@ -1896,9 +1867,7 @@ void QRhiD3D11::updateShaderResourceBindings(QD3D11ShaderResourceBindings *srbD)
}
break;
case QRhiShaderResourceBinding::BufferLoad:
- Q_FALLTHROUGH();
case QRhiShaderResourceBinding::BufferStore:
- Q_FALLTHROUGH();
case QRhiShaderResourceBinding::BufferLoadStore:
{
QD3D11Buffer *bufD = QRHI_RES(QD3D11Buffer, b->u.sbuf.buf);
@@ -2674,8 +2643,6 @@ bool QD3D11Texture::finishBuild()
return false;
}
- nativeHandlesStruct.texture = tex;
-
generation += 1;
return true;
}
@@ -2741,16 +2708,16 @@ bool QD3D11Texture::build()
return true;
}
-bool QD3D11Texture::buildFrom(const QRhiNativeHandles *src)
+bool QD3D11Texture::buildFrom(QRhiTexture::NativeTexture src)
{
- const QRhiD3D11TextureNativeHandles *h = static_cast<const QRhiD3D11TextureNativeHandles *>(src);
- if (!h || !h->texture)
+ auto *srcTex = static_cast<ID3D11Texture2D * const *>(src.object);
+ if (!srcTex || !*srcTex)
return false;
if (!prepareBuild())
return false;
- tex = static_cast<ID3D11Texture2D *>(h->texture);
+ tex = *srcTex;
if (!finishBuild())
return false;
@@ -2764,9 +2731,9 @@ bool QD3D11Texture::buildFrom(const QRhiNativeHandles *src)
return true;
}
-const QRhiNativeHandles *QD3D11Texture::nativeHandles()
+QRhiTexture::NativeTexture QD3D11Texture::nativeTexture()
{
- return &nativeHandlesStruct;
+ return {&tex, 0};
}
ID3D11UnorderedAccessView *QD3D11Texture::unorderedAccessViewForLevel(int level)
@@ -2862,12 +2829,8 @@ static inline D3D11_TEXTURE_ADDRESS_MODE toD3DAddressMode(QRhiSampler::AddressMo
return D3D11_TEXTURE_ADDRESS_WRAP;
case QRhiSampler::ClampToEdge:
return D3D11_TEXTURE_ADDRESS_CLAMP;
- case QRhiSampler::Border:
- return D3D11_TEXTURE_ADDRESS_BORDER;
case QRhiSampler::Mirror:
return D3D11_TEXTURE_ADDRESS_MIRROR;
- case QRhiSampler::MirrorOnce:
- return D3D11_TEXTURE_ADDRESS_MIRROR_ONCE;
default:
Q_UNREACHABLE();
return D3D11_TEXTURE_ADDRESS_CLAMP;
@@ -2944,6 +2907,12 @@ void QD3D11RenderPassDescriptor::release()
// nothing to do here
}
+bool QD3D11RenderPassDescriptor::isCompatible(const QRhiRenderPassDescriptor *other) const
+{
+ Q_UNUSED(other);
+ return true;
+}
+
QD3D11ReferenceRenderTarget::QD3D11ReferenceRenderTarget(QRhiImplementation *rhi)
: QRhiRenderTarget(rhi),
d(rhi)
@@ -3367,11 +3336,9 @@ static inline D3D11_BLEND toD3DBlendFactor(QRhiGraphicsPipeline::BlendFactor f)
case QRhiGraphicsPipeline::OneMinusDstAlpha:
return D3D11_BLEND_INV_DEST_ALPHA;
case QRhiGraphicsPipeline::ConstantColor:
- Q_FALLTHROUGH();
case QRhiGraphicsPipeline::ConstantAlpha:
return D3D11_BLEND_BLEND_FACTOR;
case QRhiGraphicsPipeline::OneMinusConstantColor:
- Q_FALLTHROUGH();
case QRhiGraphicsPipeline::OneMinusConstantAlpha:
return D3D11_BLEND_INV_BLEND_FACTOR;
case QRhiGraphicsPipeline::SrcAlphaSaturate:
@@ -3922,7 +3889,7 @@ bool QD3D11SwapChain::buildOrResize()
desc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;
desc.BufferCount = BUFFER_COUNT;
desc.Scaling = DXGI_SCALING_STRETCH;
- desc.SwapEffect = DXGI_SWAP_EFFECT_FLIP_DISCARD;
+ desc.SwapEffect = DXGI_SWAP_EFFECT(4); // DXGI_SWAP_EFFECT_FLIP_DISCARD
// Do not bother with AlphaMode, if won't work unless we go through
// DirectComposition. Instead, we just take the other (DISCARD)
// path for now when alpha is requested.
diff --git a/src/gui/rhi/qrhid3d11_p.h b/src/gui/rhi/qrhid3d11_p.h
index 5df1843b1e..aba0f37ee7 100644
--- a/src/gui/rhi/qrhid3d11_p.h
+++ b/src/gui/rhi/qrhid3d11_p.h
@@ -69,11 +69,6 @@ struct Q_GUI_EXPORT QRhiD3D11NativeHandles : public QRhiNativeHandles
void *context = nullptr;
};
-struct Q_GUI_EXPORT QRhiD3D11TextureNativeHandles : public QRhiNativeHandles
-{
- void *texture = nullptr; // ID3D11Texture2D*
-};
-
QT_END_NAMESPACE
#endif
diff --git a/src/gui/rhi/qrhid3d11_p_p.h b/src/gui/rhi/qrhid3d11_p_p.h
index 26de34ae0a..9ddd2aa797 100644
--- a/src/gui/rhi/qrhid3d11_p_p.h
+++ b/src/gui/rhi/qrhid3d11_p_p.h
@@ -99,8 +99,8 @@ struct QD3D11Texture : public QRhiTexture
~QD3D11Texture();
void release() override;
bool build() override;
- bool buildFrom(const QRhiNativeHandles *src) override;
- const QRhiNativeHandles *nativeHandles() override;
+ bool buildFrom(NativeTexture src) override;
+ NativeTexture nativeTexture() override;
bool prepareBuild(QSize *adjustedSize = nullptr);
bool finishBuild();
@@ -112,7 +112,6 @@ struct QD3D11Texture : public QRhiTexture
DXGI_FORMAT dxgiFormat;
uint mipLevelCount = 0;
DXGI_SAMPLE_DESC sampleDesc;
- QRhiD3D11TextureNativeHandles nativeHandlesStruct;
ID3D11UnorderedAccessView *perLevelViews[QRhi::MAX_LEVELS];
uint generation = 0;
friend class QRhiD3D11;
@@ -136,6 +135,7 @@ struct QD3D11RenderPassDescriptor : public QRhiRenderPassDescriptor
QD3D11RenderPassDescriptor(QRhiImplementation *rhi);
~QD3D11RenderPassDescriptor();
void release() override;
+ bool isCompatible(const QRhiRenderPassDescriptor *other) const override;
};
struct QD3D11RenderTargetData
diff --git a/src/gui/rhi/qrhigles2.cpp b/src/gui/rhi/qrhigles2.cpp
index dec28cac9b..ec5e531e14 100644
--- a/src/gui/rhi/qrhigles2.cpp
+++ b/src/gui/rhi/qrhigles2.cpp
@@ -137,13 +137,6 @@ QT_BEGIN_NAMESPACE
\brief Holds the OpenGL context used by the QRhi.
*/
-/*!
- \class QRhiGles2TextureNativeHandles
- \internal
- \inmodule QtGui
- \brief Holds the OpenGL texture object that is backing a QRhiTexture instance.
- */
-
#ifndef GL_BGRA
#define GL_BGRA 0x80E1
#endif
@@ -172,6 +165,14 @@ QT_BEGIN_NAMESPACE
#define GL_RGBA16F 0x881A
#endif
+#ifndef GL_R16F
+#define GL_R16F 0x822D
+#endif
+
+#ifndef GL_R32F
+#define GL_R32F 0x822E
+#endif
+
#ifndef GL_HALF_FLOAT
#define GL_HALF_FLOAT 0x140B
#endif
@@ -268,6 +269,14 @@ QT_BEGIN_NAMESPACE
#define GL_ALL_BARRIER_BITS 0xFFFFFFFF
#endif
+#ifndef GL_SHADER_IMAGE_ACCESS_BARRIER_BIT
+#define GL_SHADER_IMAGE_ACCESS_BARRIER_BIT 0x00000020
+#endif
+
+#ifndef GL_SHADER_STORAGE_BARRIER_BIT
+#define GL_SHADER_STORAGE_BARRIER_BIT 0x00002000
+#endif
+
#ifndef GL_VERTEX_PROGRAM_POINT_SIZE
#define GL_VERTEX_PROGRAM_POINT_SIZE 0x8642
#endif
@@ -688,7 +697,6 @@ bool QRhiGles2::isTextureFormatSupported(QRhiTexture::Format format, QRhiTexture
switch (format) {
case QRhiTexture::D16:
- Q_FALLTHROUGH();
case QRhiTexture::D32F:
return caps.depthTexture;
@@ -702,10 +710,13 @@ bool QRhiGles2::isTextureFormatSupported(QRhiTexture::Format format, QRhiTexture
return caps.r16Format;
case QRhiTexture::RGBA16F:
- Q_FALLTHROUGH();
case QRhiTexture::RGBA32F:
return caps.floatFormats;
+ case QRhiTexture::R16F:
+ case QRhiTexture::R32F:
+ return caps.floatFormats;
+
default:
break;
}
@@ -906,9 +917,7 @@ void QRhiGles2::setShaderResources(QRhiCommandBuffer *cb, QRhiShaderResourceBind
QRhiPassResourceTracker::toPassTrackerTextureStage(b->stage));
break;
case QRhiShaderResourceBinding::ImageLoad:
- Q_FALLTHROUGH();
case QRhiShaderResourceBinding::ImageStore:
- Q_FALLTHROUGH();
case QRhiShaderResourceBinding::ImageLoadStore:
{
QGles2Texture *texD = QRHI_RES(QGles2Texture, b->u.simage.tex);
@@ -924,9 +933,7 @@ void QRhiGles2::setShaderResources(QRhiCommandBuffer *cb, QRhiShaderResourceBind
}
break;
case QRhiShaderResourceBinding::BufferLoad:
- Q_FALLTHROUGH();
case QRhiShaderResourceBinding::BufferStore:
- Q_FALLTHROUGH();
case QRhiShaderResourceBinding::BufferLoadStore:
{
QGles2Buffer *bufD = QRHI_RES(QGles2Buffer, b->u.sbuf.buf);
@@ -1308,6 +1315,21 @@ QRhi::FrameOpResult QRhiGles2::finish()
return QRhi::FrameOpSuccess;
}
+static bool bufferAccessIsWrite(QGles2Buffer::Access access)
+{
+ return access == QGles2Buffer::AccessStorageWrite
+ || access == QGles2Buffer::AccessStorageReadWrite
+ || access == QGles2Buffer::AccessUpdate;
+}
+
+static bool textureAccessIsWrite(QGles2Texture::Access access)
+{
+ return access == QGles2Texture::AccessStorageWrite
+ || access == QGles2Texture::AccessStorageReadWrite
+ || access == QGles2Texture::AccessUpdate
+ || access == QGles2Texture::AccessFramebuffer;
+}
+
void QRhiGles2::trackedBufferBarrier(QGles2CommandBuffer *cbD, QGles2Buffer *bufD, QGles2Buffer::Access access)
{
Q_ASSERT(cbD->recordingPass == QGles2CommandBuffer::NoPass); // this is for resource updates only
@@ -1315,7 +1337,7 @@ void QRhiGles2::trackedBufferBarrier(QGles2CommandBuffer *cbD, QGles2Buffer *buf
if (access == prevAccess)
return;
- if (prevAccess == QGles2Buffer::AccessStorageWrite || prevAccess == QGles2Buffer::AccessStorageReadWrite) {
+ if (bufferAccessIsWrite(prevAccess)) {
// Generating the minimal barrier set is way too complicated to do
// correctly (prevAccess is overwritten so we won't have proper
// tracking across multiple passes) so setting all barrier bits will do
@@ -1336,7 +1358,7 @@ void QRhiGles2::trackedImageBarrier(QGles2CommandBuffer *cbD, QGles2Texture *tex
if (access == prevAccess)
return;
- if (prevAccess == QGles2Texture::AccessStorageWrite || prevAccess == QGles2Texture::AccessStorageReadWrite) {
+ if (textureAccessIsWrite(prevAccess)) {
QGles2CommandBuffer::Command cmd;
cmd.cmd = QGles2CommandBuffer::Command::Barrier;
cmd.args.barrier.barriers = GL_ALL_BARRIER_BITS;
@@ -1661,11 +1683,8 @@ static inline GLenum toGlBlendFactor(QRhiGraphicsPipeline::BlendFactor f)
case QRhiGraphicsPipeline::SrcAlphaSaturate:
return GL_SRC_ALPHA_SATURATE;
case QRhiGraphicsPipeline::Src1Color:
- Q_FALLTHROUGH();
case QRhiGraphicsPipeline::OneMinusSrc1Color:
- Q_FALLTHROUGH();
case QRhiGraphicsPipeline::Src1Alpha:
- Q_FALLTHROUGH();
case QRhiGraphicsPipeline::OneMinusSrc1Alpha:
qWarning("Unsupported blend factor %d", f);
return GL_ZERO;
@@ -1785,11 +1804,6 @@ static inline GLenum toGlWrapMode(QRhiSampler::AddressMode m)
return GL_CLAMP_TO_EDGE;
case QRhiSampler::Mirror:
return GL_MIRRORED_REPEAT;
- case QRhiSampler::MirrorOnce:
- Q_FALLTHROUGH();
- case QRhiSampler::Border:
- qWarning("Unsupported wrap mode %d", m);
- return GL_CLAMP_TO_EDGE;
default:
Q_UNREACHABLE();
return GL_CLAMP_TO_EDGE;
@@ -2282,26 +2296,21 @@ void QRhiGles2::executeCommandBuffer(QRhiCommandBuffer *cb)
// subsequent pass.
for (auto it = tracker.cbeginBuffers(), itEnd = tracker.cendBuffers(); it != itEnd; ++it) {
QGles2Buffer::Access accessBeforePass = QGles2Buffer::Access(it->stateAtPassBegin.access);
- if (accessBeforePass == QGles2Buffer::AccessStorageWrite
- || accessBeforePass == QGles2Buffer::AccessStorageReadWrite)
- {
+ if (bufferAccessIsWrite(accessBeforePass))
barriers |= GL_ALL_BARRIER_BITS;
- }
}
for (auto it = tracker.cbeginTextures(), itEnd = tracker.cendTextures(); it != itEnd; ++it) {
QGles2Texture::Access accessBeforePass = QGles2Texture::Access(it->stateAtPassBegin.access);
- if (accessBeforePass == QGles2Texture::AccessStorageWrite
- || accessBeforePass == QGles2Texture::AccessStorageReadWrite)
- {
+ if (textureAccessIsWrite(accessBeforePass))
barriers |= GL_ALL_BARRIER_BITS;
- }
}
- if (barriers)
+ if (barriers && caps.compute)
f->glMemoryBarrier(barriers);
}
break;
case QGles2CommandBuffer::Command::Barrier:
- f->glMemoryBarrier(cmd.args.barrier.barriers);
+ if (caps.compute)
+ f->glMemoryBarrier(cmd.args.barrier.barriers);
break;
default:
break;
@@ -2383,12 +2392,23 @@ void QRhiGles2::executeBindGraphicsPipeline(QRhiGraphicsPipeline *ps)
f->glUseProgram(psD->program);
}
+static inline void qrhi_std140_to_packed(float *dst, int vecSize, int elemCount, const void *src)
+{
+ const float *p = reinterpret_cast<const float *>(src);
+ for (int i = 0; i < elemCount; ++i) {
+ for (int j = 0; j < vecSize; ++j)
+ dst[vecSize * i + j] = *p++;
+ p += 4 - vecSize;
+ }
+}
+
void QRhiGles2::bindShaderResources(QRhiGraphicsPipeline *maybeGraphicsPs, QRhiComputePipeline *maybeComputePs,
QRhiShaderResourceBindings *srb,
const uint *dynOfsPairs, int dynOfsCount)
{
QGles2ShaderResourceBindings *srbD = QRHI_RES(QGles2ShaderResourceBindings, srb);
int texUnit = 0;
+ QVarLengthArray<float, 256> packedFloatArray;
for (int i = 0, ie = srbD->m_bindings.count(); i != ie; ++i) {
const QRhiShaderResourceBinding::Data *b = srbD->m_bindings.at(i).data();
@@ -2416,24 +2436,78 @@ void QRhiGles2::bindShaderResources(QRhiGraphicsPipeline *maybeGraphicsPs, QRhiC
// so this should not cause unaligned reads
const void *src = bufView.constData() + uniform.offset;
+ if (uniform.arrayDim > 0
+ && uniform.type != QShaderDescription::Float
+ && uniform.type != QShaderDescription::Vec2
+ && uniform.type != QShaderDescription::Vec3
+ && uniform.type != QShaderDescription::Vec4)
+ {
+ qWarning("Uniform with buffer binding %d, buffer offset %d, type %d is an array, "
+ "but arrays are only supported for float, vec2, vec3, and vec4. "
+ "Only the first element will be set.",
+ uniform.binding, uniform.offset, uniform.type);
+ }
+
+ // Our input is an std140 layout uniform block. See
+ // "Standard Uniform Block Layout" in section 7.6.2.2 of
+ // the OpenGL spec. This has some peculiar alignment
+ // requirements, which is not what glUniform* wants. Hence
+ // the unpacking/repacking for arrays and certain types.
+
switch (uniform.type) {
case QShaderDescription::Float:
- f->glUniform1f(uniform.glslLocation, *reinterpret_cast<const float *>(src));
+ {
+ const int elemCount = uniform.arrayDim;
+ if (elemCount < 1) {
+ f->glUniform1f(uniform.glslLocation, *reinterpret_cast<const float *>(src));
+ } else {
+ // input is 16 bytes per element as per std140, have to convert to packed
+ packedFloatArray.resize(elemCount);
+ qrhi_std140_to_packed(packedFloatArray.data(), 1, elemCount, src);
+ f->glUniform1fv(uniform.glslLocation, elemCount, packedFloatArray.constData());
+ }
+ }
break;
case QShaderDescription::Vec2:
- f->glUniform2fv(uniform.glslLocation, 1, reinterpret_cast<const float *>(src));
+ {
+ const int elemCount = uniform.arrayDim;
+ if (elemCount < 1) {
+ f->glUniform2fv(uniform.glslLocation, 1, reinterpret_cast<const float *>(src));
+ } else {
+ packedFloatArray.resize(elemCount * 2);
+ qrhi_std140_to_packed(packedFloatArray.data(), 2, elemCount, src);
+ f->glUniform2fv(uniform.glslLocation, elemCount, packedFloatArray.constData());
+ }
+ }
break;
case QShaderDescription::Vec3:
- f->glUniform3fv(uniform.glslLocation, 1, reinterpret_cast<const float *>(src));
+ {
+ const int elemCount = uniform.arrayDim;
+ if (elemCount < 1) {
+ f->glUniform3fv(uniform.glslLocation, 1, reinterpret_cast<const float *>(src));
+ } else {
+ packedFloatArray.resize(elemCount * 3);
+ qrhi_std140_to_packed(packedFloatArray.data(), 3, elemCount, src);
+ f->glUniform3fv(uniform.glslLocation, elemCount, packedFloatArray.constData());
+ }
+ }
break;
case QShaderDescription::Vec4:
- f->glUniform4fv(uniform.glslLocation, 1, reinterpret_cast<const float *>(src));
+ f->glUniform4fv(uniform.glslLocation, qMax(1, uniform.arrayDim), reinterpret_cast<const float *>(src));
break;
case QShaderDescription::Mat2:
f->glUniformMatrix2fv(uniform.glslLocation, 1, GL_FALSE, reinterpret_cast<const float *>(src));
break;
case QShaderDescription::Mat3:
- f->glUniformMatrix3fv(uniform.glslLocation, 1, GL_FALSE, reinterpret_cast<const float *>(src));
+ {
+ // 4 floats per column (or row, if row-major)
+ float mat[9];
+ const float *srcMat = reinterpret_cast<const float *>(src);
+ memcpy(mat, srcMat, 3 * sizeof(float));
+ memcpy(mat + 3, srcMat + 4, 3 * sizeof(float));
+ memcpy(mat + 6, srcMat + 8, 3 * sizeof(float));
+ f->glUniformMatrix3fv(uniform.glslLocation, 1, GL_FALSE, mat);
+ }
break;
case QShaderDescription::Mat4:
f->glUniformMatrix4fv(uniform.glslLocation, 1, GL_FALSE, reinterpret_cast<const float *>(src));
@@ -2474,8 +2548,9 @@ void QRhiGles2::bindShaderResources(QRhiGraphicsPipeline *maybeGraphicsPs, QRhiC
case QShaderDescription::Bool4:
f->glUniform4iv(uniform.glslLocation, 1, reinterpret_cast<const qint32 *>(src));
break;
- // ### more types
default:
+ qWarning("Uniform with buffer binding %d, buffer offset %d has unsupported type %d",
+ uniform.binding, uniform.offset, uniform.type);
break;
}
}
@@ -2519,9 +2594,7 @@ void QRhiGles2::bindShaderResources(QRhiGraphicsPipeline *maybeGraphicsPs, QRhiC
}
break;
case QRhiShaderResourceBinding::ImageLoad:
- Q_FALLTHROUGH();
case QRhiShaderResourceBinding::ImageStore:
- Q_FALLTHROUGH();
case QRhiShaderResourceBinding::ImageLoadStore:
{
QGles2Texture *texD = QRHI_RES(QGles2Texture, b->u.simage.tex);
@@ -2537,9 +2610,7 @@ void QRhiGles2::bindShaderResources(QRhiGraphicsPipeline *maybeGraphicsPs, QRhiC
}
break;
case QRhiShaderResourceBinding::BufferLoad:
- Q_FALLTHROUGH();
case QRhiShaderResourceBinding::BufferStore:
- Q_FALLTHROUGH();
case QRhiShaderResourceBinding::BufferLoadStore:
{
QGles2Buffer *bufD = QRHI_RES(QGles2Buffer, b->u.sbuf.buf);
@@ -2738,6 +2809,8 @@ void QRhiGles2::beginComputePass(QRhiCommandBuffer *cb, QRhiResourceUpdateBatch
cbD->recordingPass = QGles2CommandBuffer::ComputePass;
cbD->resetCachedState();
+
+ cbD->computePassState.reset();
}
void QRhiGles2::endComputePass(QRhiCommandBuffer *cb, QRhiResourceUpdateBatch *resourceUpdates)
@@ -2770,11 +2843,96 @@ void QRhiGles2::setComputePipeline(QRhiCommandBuffer *cb, QRhiComputePipeline *p
}
}
+template<typename T>
+inline void qrhigl_accumulateComputeResource(T *writtenResources, QRhiResource *resource,
+ QRhiShaderResourceBinding::Type bindingType,
+ int loadTypeVal, int storeTypeVal, int loadStoreTypeVal)
+{
+ int access = 0;
+ if (bindingType == loadTypeVal) {
+ access = QGles2CommandBuffer::ComputePassState::Read;
+ } else {
+ access = QGles2CommandBuffer::ComputePassState::Write;
+ if (bindingType == loadStoreTypeVal)
+ access |= QGles2CommandBuffer::ComputePassState::Read;
+ }
+ auto it = writtenResources->find(resource);
+ if (it != writtenResources->end())
+ it->first |= access;
+ else if (bindingType == storeTypeVal || bindingType == loadStoreTypeVal)
+ writtenResources->insert(resource, { access, true });
+}
+
void QRhiGles2::dispatch(QRhiCommandBuffer *cb, int x, int y, int z)
{
QGles2CommandBuffer *cbD = QRHI_RES(QGles2CommandBuffer, cb);
Q_ASSERT(cbD->recordingPass == QGles2CommandBuffer::ComputePass);
+ if (cbD->currentComputeSrb) {
+ GLbitfield barriers = 0;
+
+ // The key in the writtenResources map indicates that the resource was
+ // written in a previous dispatch, whereas the value accumulates the
+ // access mask in the current one.
+ for (auto &accessAndIsNewFlag : cbD->computePassState.writtenResources)
+ accessAndIsNewFlag = { 0, false };
+
+ QGles2ShaderResourceBindings *srbD = QRHI_RES(QGles2ShaderResourceBindings, cbD->currentComputeSrb);
+ const int bindingCount = srbD->m_bindings.count();
+ for (int i = 0; i < bindingCount; ++i) {
+ const QRhiShaderResourceBinding::Data *b = srbD->m_bindings.at(i).data();
+ switch (b->type) {
+ case QRhiShaderResourceBinding::ImageLoad:
+ case QRhiShaderResourceBinding::ImageStore:
+ case QRhiShaderResourceBinding::ImageLoadStore:
+ qrhigl_accumulateComputeResource(&cbD->computePassState.writtenResources,
+ b->u.simage.tex,
+ b->type,
+ QRhiShaderResourceBinding::ImageLoad,
+ QRhiShaderResourceBinding::ImageStore,
+ QRhiShaderResourceBinding::ImageLoadStore);
+ break;
+ case QRhiShaderResourceBinding::BufferLoad:
+ case QRhiShaderResourceBinding::BufferStore:
+ case QRhiShaderResourceBinding::BufferLoadStore:
+ qrhigl_accumulateComputeResource(&cbD->computePassState.writtenResources,
+ b->u.sbuf.buf,
+ b->type,
+ QRhiShaderResourceBinding::BufferLoad,
+ QRhiShaderResourceBinding::BufferStore,
+ QRhiShaderResourceBinding::BufferLoadStore);
+ break;
+ default:
+ break;
+ }
+ }
+
+ for (auto it = cbD->computePassState.writtenResources.begin(); it != cbD->computePassState.writtenResources.end(); ) {
+ const int accessInThisDispatch = it->first;
+ const bool isNewInThisDispatch = it->second;
+ if (accessInThisDispatch && !isNewInThisDispatch) {
+ if (it.key()->resourceType() == QRhiResource::Texture)
+ barriers |= GL_SHADER_IMAGE_ACCESS_BARRIER_BIT;
+ else
+ barriers |= GL_SHADER_STORAGE_BARRIER_BIT;
+ }
+ // Anything that was previously written, but is only read now, can be
+ // removed from the written list (because that previous write got a
+ // corresponding barrier now).
+ if (accessInThisDispatch == QGles2CommandBuffer::ComputePassState::Read)
+ it = cbD->computePassState.writtenResources.erase(it);
+ else
+ ++it;
+ }
+
+ if (barriers) {
+ QGles2CommandBuffer::Command cmd;
+ cmd.cmd = QGles2CommandBuffer::Command::Barrier;
+ cmd.args.barrier.barriers = barriers;
+ cbD->commands.append(cmd);
+ }
+ }
+
QGles2CommandBuffer::Command cmd;
cmd.cmd = QGles2CommandBuffer::Command::Dispatch;
cmd.args.dispatch.x = GLuint(x);
@@ -2924,21 +3082,65 @@ bool QRhiGles2::linkProgram(GLuint program)
return true;
}
-void QRhiGles2::gatherUniforms(GLuint program, const QShaderDescription::UniformBlock &ub,
+void QRhiGles2::registerUniformIfActive(const QShaderDescription::BlockVariable &var,
+ const QByteArray &namePrefix,
+ int binding,
+ int baseOffset,
+ GLuint program,
+ QVector<QGles2UniformDescription> *dst)
+{
+ if (var.type == QShaderDescription::Struct) {
+ qWarning("Nested structs are not supported at the moment. '%s' ignored.",
+ qPrintable(var.name));
+ return;
+ }
+ QGles2UniformDescription uniform;
+ uniform.type = var.type;
+ const QByteArray name = namePrefix + var.name.toUtf8();
+ uniform.glslLocation = f->glGetUniformLocation(program, name.constData());
+ if (uniform.glslLocation >= 0) {
+ if (var.arrayDims.count() > 1) {
+ qWarning("Array '%s' has more than one dimension. This is not supported.",
+ qPrintable(var.name));
+ return;
+ }
+ uniform.binding = binding;
+ uniform.offset = uint(baseOffset + var.offset);
+ uniform.size = var.size;
+ uniform.arrayDim = var.arrayDims.isEmpty() ? 0 : var.arrayDims.first();
+ dst->append(uniform);
+ }
+}
+
+void QRhiGles2::gatherUniforms(GLuint program,
+ const QShaderDescription::UniformBlock &ub,
QVector<QGles2UniformDescription> *dst)
{
- const QByteArray prefix = ub.structName.toUtf8() + '.';
+ QByteArray prefix = ub.structName.toUtf8() + '.';
for (const QShaderDescription::BlockVariable &blockMember : ub.members) {
- // ### no array support for now
- QGles2UniformDescription uniform;
- uniform.type = blockMember.type;
- const QByteArray name = prefix + blockMember.name.toUtf8();
- uniform.glslLocation = f->glGetUniformLocation(program, name.constData());
- if (uniform.glslLocation >= 0) {
- uniform.binding = ub.binding;
- uniform.offset = uint(blockMember.offset);
- uniform.size = blockMember.size;
- dst->append(uniform);
+ if (blockMember.type == QShaderDescription::Struct) {
+ prefix += blockMember.name.toUtf8();
+ const int baseOffset = blockMember.offset;
+ if (blockMember.arrayDims.isEmpty()) {
+ for (const QShaderDescription::BlockVariable &structMember : blockMember.structMembers)
+ registerUniformIfActive(structMember, prefix, ub.binding, baseOffset, program, dst);
+ } else {
+ if (blockMember.arrayDims.count() > 1) {
+ qWarning("Array of struct '%s' has more than one dimension. Only the first dimension is used.",
+ qPrintable(blockMember.name));
+ }
+ const int dim = blockMember.arrayDims.first();
+ const int elemSize = blockMember.size / dim;
+ int elemOffset = baseOffset;
+ for (int di = 0; di < dim; ++di) {
+ const QByteArray arrayPrefix = prefix + '[' + QByteArray::number(di) + ']' + '.';
+ for (const QShaderDescription::BlockVariable &structMember : blockMember.structMembers)
+ registerUniformIfActive(structMember, arrayPrefix, ub.binding, elemOffset, program, dst);
+ elemOffset += elemSize;
+ }
+ }
+ } else {
+ registerUniformIfActive(blockMember, prefix, ub.binding, 0, program, dst);
}
}
}
@@ -2961,7 +3163,7 @@ bool QRhiGles2::isProgramBinaryDiskCacheEnabled() const
return checker.get(ctx)->isSupported();
}
-static QOpenGLProgramBinaryCache qrhi_programBinaryCache;
+Q_GLOBAL_STATIC(QOpenGLProgramBinaryCache, qrhi_programBinaryCache);
static inline QShader::Stage toShaderStage(QRhiShaderStage::Type type)
{
@@ -2995,7 +3197,7 @@ QRhiGles2::DiskCacheResult QRhiGles2::tryLoadFromDiskCache(const QRhiShaderStage
}
diskCacheKey = binaryProgram.cacheKey();
- if (qrhi_programBinaryCache.load(diskCacheKey, program)) {
+ if (qrhi_programBinaryCache()->load(diskCacheKey, program)) {
qCDebug(lcOpenGLProgramDiskCache, "Program binary received from cache, program %u, key %s",
program, diskCacheKey.constData());
result = QRhiGles2::DiskCacheHit;
@@ -3013,7 +3215,7 @@ void QRhiGles2::trySaveToDiskCache(GLuint program, const QByteArray &cacheKey)
if (isProgramBinaryDiskCacheEnabled()) {
qCDebug(lcOpenGLProgramDiskCache, "Saving program binary, program %u, key %s",
program, cacheKey.constData());
- qrhi_programBinaryCache.save(cacheKey, program);
+ qrhi_programBinaryCache()->save(cacheKey, program);
}
}
@@ -3147,8 +3349,7 @@ bool QGles2RenderBuffer::build()
switch (m_type) {
case QRhiRenderBuffer::DepthStencil:
if (rhiD->caps.msaaRenderBuffer && samples > 1) {
- const GLenum storage = rhiD->caps.needsDepthStencilCombinedAttach ? GL_DEPTH_STENCIL : GL_DEPTH24_STENCIL8;
- rhiD->f->glRenderbufferStorageMultisample(GL_RENDERBUFFER, samples, storage,
+ rhiD->f->glRenderbufferStorageMultisample(GL_RENDERBUFFER, samples, GL_DEPTH24_STENCIL8,
size.width(), size.height());
stencilRenderbuffer = 0;
} else if (rhiD->caps.packedDepthStencil || rhiD->caps.needsDepthStencilCombinedAttach) {
@@ -3220,7 +3421,6 @@ void QGles2Texture::release()
texture = 0;
specified = false;
- nativeHandlesStruct.texture = 0;
QRHI_RES_RHI(QRhiGles2);
if (owns)
@@ -3301,6 +3501,18 @@ bool QGles2Texture::prepareBuild(QSize *adjustedSize)
glformat = GL_RGBA;
gltype = GL_FLOAT;
break;
+ case QRhiTexture::R16F:
+ glintformat = GL_R16F;
+ glsizedintformat = glintformat;
+ glformat = GL_RED;
+ gltype = GL_HALF_FLOAT;
+ break;
+ case QRhiTexture::R32F:
+ glintformat = GL_R32F;
+ glsizedintformat = glintformat;
+ glformat = GL_RED;
+ gltype = GL_FLOAT;
+ break;
case QRhiTexture::D16:
glintformat = GL_DEPTH_COMPONENT16;
glsizedintformat = glintformat;
@@ -3379,23 +3591,22 @@ bool QGles2Texture::build()
QRHI_PROF_F(newTexture(this, true, mipLevelCount, isCube ? 6 : 1, 1));
owns = true;
- nativeHandlesStruct.texture = texture;
generation += 1;
rhiD->registerResource(this);
return true;
}
-bool QGles2Texture::buildFrom(const QRhiNativeHandles *src)
+bool QGles2Texture::buildFrom(QRhiTexture::NativeTexture src)
{
- const QRhiGles2TextureNativeHandles *h = static_cast<const QRhiGles2TextureNativeHandles *>(src);
- if (!h || !h->texture)
+ const uint *textureId = static_cast<const uint *>(src.object);
+ if (!textureId || !*textureId)
return false;
if (!prepareBuild())
return false;
- texture = h->texture;
+ texture = *textureId;
specified = true;
QRHI_RES_RHI(QRhiGles2);
@@ -3403,16 +3614,15 @@ bool QGles2Texture::buildFrom(const QRhiNativeHandles *src)
QRHI_PROF_F(newTexture(this, false, mipLevelCount, m_flags.testFlag(CubeMap) ? 6 : 1, 1));
owns = false;
- nativeHandlesStruct.texture = texture;
generation += 1;
rhiD->registerResource(this);
return true;
}
-const QRhiNativeHandles *QGles2Texture::nativeHandles()
+QRhiTexture::NativeTexture QGles2Texture::nativeTexture()
{
- return &nativeHandlesStruct;
+ return {&texture, 0};
}
QGles2Sampler::QGles2Sampler(QRhiImplementation *rhi, Filter magFilter, Filter minFilter, Filter mipmapMode,
@@ -3460,6 +3670,12 @@ void QGles2RenderPassDescriptor::release()
// nothing to do here
}
+bool QGles2RenderPassDescriptor::isCompatible(const QRhiRenderPassDescriptor *other) const
+{
+ Q_UNUSED(other);
+ return true;
+}
+
QGles2ReferenceRenderTarget::QGles2ReferenceRenderTarget(QRhiImplementation *rhi)
: QRhiRenderTarget(rhi),
d(rhi)
diff --git a/src/gui/rhi/qrhigles2_p.h b/src/gui/rhi/qrhigles2_p.h
index 7f7c8b4c40..8d8f0c7396 100644
--- a/src/gui/rhi/qrhigles2_p.h
+++ b/src/gui/rhi/qrhigles2_p.h
@@ -74,11 +74,6 @@ struct Q_GUI_EXPORT QRhiGles2NativeHandles : public QRhiNativeHandles
QOpenGLContext *context = nullptr;
};
-struct Q_GUI_EXPORT QRhiGles2TextureNativeHandles : public QRhiNativeHandles
-{
- uint texture = 0;
-};
-
QT_END_NAMESPACE
#endif
diff --git a/src/gui/rhi/qrhigles2_p_p.h b/src/gui/rhi/qrhigles2_p_p.h
index cc945876e6..679f806004 100644
--- a/src/gui/rhi/qrhigles2_p_p.h
+++ b/src/gui/rhi/qrhigles2_p_p.h
@@ -132,8 +132,8 @@ struct QGles2Texture : public QRhiTexture
~QGles2Texture();
void release() override;
bool build() override;
- bool buildFrom(const QRhiNativeHandles *src) override;
- const QRhiNativeHandles *nativeHandles() override;
+ bool buildFrom(NativeTexture src) override;
+ NativeTexture nativeTexture() override;
bool prepareBuild(QSize *adjustedSize = nullptr);
@@ -147,7 +147,7 @@ struct QGles2Texture : public QRhiTexture
QGles2SamplerData samplerState;
bool specified = false;
int mipLevelCount = 0;
- QRhiGles2TextureNativeHandles nativeHandlesStruct;
+
enum Access {
AccessNone,
AccessSample,
@@ -185,6 +185,7 @@ struct QGles2RenderPassDescriptor : public QRhiRenderPassDescriptor
QGles2RenderPassDescriptor(QRhiImplementation *rhi);
~QGles2RenderPassDescriptor();
void release() override;
+ bool isCompatible(const QRhiRenderPassDescriptor *other) const override;
};
struct QGles2RenderTargetData
@@ -249,6 +250,7 @@ struct QGles2UniformDescription
int binding;
uint offset;
int size;
+ int arrayDim;
};
Q_DECLARE_TYPEINFO(QGles2UniformDescription, Q_MOVABLE_TYPE);
@@ -519,6 +521,17 @@ struct QGles2CommandBuffer : public QRhiCommandBuffer
QRhiShaderResourceBindings *currentComputeSrb;
uint currentSrbGeneration;
+ struct ComputePassState {
+ enum Access {
+ Read = 0x01,
+ Write = 0x02
+ };
+ QHash<QRhiResource *, QPair<int, bool> > writtenResources;
+ void reset() {
+ writtenResources.clear();
+ }
+ } computePassState;
+
QVector<QByteArray> dataRetainPool;
QVector<QImage> imageRetainPool;
@@ -703,7 +716,14 @@ public:
QByteArray shaderSource(const QRhiShaderStage &shaderStage, int *glslVersion);
bool compileShader(GLuint program, const QRhiShaderStage &shaderStage, int *glslVersion);
bool linkProgram(GLuint program);
- void gatherUniforms(GLuint program, const QShaderDescription::UniformBlock &ub,
+ void registerUniformIfActive(const QShaderDescription::BlockVariable &var,
+ const QByteArray &namePrefix,
+ int binding,
+ int baseOffset,
+ GLuint program,
+ QVector<QGles2UniformDescription> *dst);
+ void gatherUniforms(GLuint program,
+ const QShaderDescription::UniformBlock &ub,
QVector<QGles2UniformDescription> *dst);
void gatherSamplers(GLuint program, const QShaderDescription::InOutVariable &v,
QVector<QGles2SamplerDescription> *dst);
diff --git a/src/gui/rhi/qrhimetal.mm b/src/gui/rhi/qrhimetal.mm
index 5f14d917b8..98cbd416b5 100644
--- a/src/gui/rhi/qrhimetal.mm
+++ b/src/gui/rhi/qrhimetal.mm
@@ -35,14 +35,14 @@
****************************************************************************/
#include "qrhimetal_p_p.h"
-#include "qshader_p.h"
-#include "qshaderdescription_p.h"
#include <QGuiApplication>
#include <QWindow>
#include <qmath.h>
#ifdef Q_OS_MACOS
#include <AppKit/AppKit.h>
+#else
+#include <UIKit/UIKit.h>
#endif
#include <Metal/Metal.h>
@@ -114,15 +114,6 @@ QT_BEGIN_NAMESPACE
*/
/*!
- \class QRhiMetalTextureNativeHandles
- \inmodule QtRhi
- \brief Holds the Metal texture object that is backing a QRhiTexture instance.
-
- \note The class uses \c{void *} as the type since including the Objective C
- headers is not acceptable here. The actual type is \c{id<MTLTexture>}.
- */
-
-/*!
\class QRhiMetalCommandBufferNativeHandles
\inmodule QtRhi
\brief Holds the MTLCommandBuffer and MTLRenderCommandEncoder objects that are backing a QRhiCommandBuffer.
@@ -131,7 +122,7 @@ QT_BEGIN_NAMESPACE
recording a frame, that is, between a \l{QRhi::beginFrame()}{beginFrame()}
- \l{QRhi::endFrame()}{endFrame()} or
\l{QRhi::beginOffscreenFrame()}{beginOffscreenFrame()} -
- \l{QRhi::endOffsrceenFrame()}{endOffscreenFrame()} pair.
+ \l{QRhi::endOffscreenFrame()}{endOffsrceenFrame()} pair.
\note The command encoder is only valid while recording a pass, that is,
between \l{QRhiCommandBuffer::beginPass()} -
@@ -143,8 +134,10 @@ struct QMetalShader
id<MTLLibrary> lib = nil;
id<MTLFunction> func = nil;
std::array<uint, 3> localSize;
+ QShader::NativeResourceBindingMap nativeResourceBindingMap;
void release() {
+ nativeResourceBindingMap.clear();
[lib release];
lib = nil;
[func release];
@@ -164,7 +157,7 @@ struct QRhiMetalData
const QRhiDepthStencilClearValue &depthStencilClearValue,
int colorAttCount);
id<MTLLibrary> createMetalLib(const QShader &shader, QShader::Variant shaderVariant,
- QString *error, QByteArray *entryPoint);
+ QString *error, QByteArray *entryPoint, QShaderKey *activeKey);
id<MTLFunction> createMSLShaderFunction(id<MTLLibrary> lib, const QByteArray &entryPoint);
struct DeferredReleaseEntry {
@@ -318,7 +311,13 @@ struct QMetalComputePipelineData
struct QMetalSwapChainData
{
+ // The iOS simulator's headers mark CAMetalLayer as iOS 13.0+ only.
+ // (for real device SDKs it is 8.0+)
+#ifdef TARGET_IPHONE_SIMULATOR
+ API_AVAILABLE(ios(13.0)) CAMetalLayer *layer = nullptr;
+#else
CAMetalLayer *layer = nullptr;
+#endif
id<CAMetalDrawable> curDrawable;
dispatch_semaphore_t sem[QMTL_FRAMES_IN_FLIGHT];
MTLRenderPassDescriptor *rp = nullptr;
@@ -653,18 +652,45 @@ QRhiShaderResourceBindings *QRhiMetal::createShaderResourceBindings()
return new QMetalShaderResourceBindings(this);
}
-void QRhiMetal::enqueueShaderResourceBindings(QMetalShaderResourceBindings *srbD, QMetalCommandBuffer *cbD,
+enum class BindingType {
+ Buffer,
+ Texture,
+ Sampler
+};
+
+static inline int mapBinding(int binding,
+ int stageIndex,
+ const QShader::NativeResourceBindingMap *nativeResourceBindingMaps[],
+ BindingType type)
+{
+ const QShader::NativeResourceBindingMap *map = nativeResourceBindingMaps[stageIndex];
+ if (!map || map->isEmpty())
+ return binding; // old QShader versions do not have this map, assume 1:1 mapping then
+
+ auto it = map->constFind(binding);
+ if (it != map->cend())
+ return type == BindingType::Sampler ? it->second : it->first; // may be -1, if the resource is inactive
+
+ // Hitting this path is normal too. It is not given that the resource (for
+ // example, a uniform block) is present in the shaders for all the stages
+ // specified by the visibility mask in the QRhiShaderResourceBinding.
+ return -1;
+}
+
+void QRhiMetal::enqueueShaderResourceBindings(QMetalShaderResourceBindings *srbD,
+ QMetalCommandBuffer *cbD,
int dynamicOffsetCount,
const QRhiCommandBuffer::DynamicOffset *dynamicOffsets,
- bool offsetOnlyChange)
+ bool offsetOnlyChange,
+ const QShader::NativeResourceBindingMap *nativeResourceBindingMaps[SUPPORTED_STAGES])
{
- static const int KNOWN_STAGES = 3;
struct {
QRhiBatchedBindings<id<MTLBuffer> > buffers;
QRhiBatchedBindings<NSUInteger> bufferOffsets;
QRhiBatchedBindings<id<MTLTexture> > textures;
QRhiBatchedBindings<id<MTLSamplerState> > samplers;
- } res[KNOWN_STAGES];
+ } res[SUPPORTED_STAGES];
+ enum { VERTEX = 0, FRAGMENT = 1, COMPUTE = 2 };
for (const QRhiShaderResourceBinding &binding : qAsConst(srbD->sortedBindings)) {
const QRhiShaderResourceBinding::Data *b = binding.data();
@@ -682,16 +708,25 @@ void QRhiMetal::enqueueShaderResourceBindings(QMetalShaderResourceBindings *srbD
}
}
if (b->stage.testFlag(QRhiShaderResourceBinding::VertexStage)) {
- res[0].buffers.feed(b->binding, mtlbuf);
- res[0].bufferOffsets.feed(b->binding, offset);
+ const int nativeBinding = mapBinding(b->binding, VERTEX, nativeResourceBindingMaps, BindingType::Buffer);
+ if (nativeBinding >= 0) {
+ res[VERTEX].buffers.feed(nativeBinding, mtlbuf);
+ res[VERTEX].bufferOffsets.feed(b->binding, offset);
+ }
}
if (b->stage.testFlag(QRhiShaderResourceBinding::FragmentStage)) {
- res[1].buffers.feed(b->binding, mtlbuf);
- res[1].bufferOffsets.feed(b->binding, offset);
+ const int nativeBinding = mapBinding(b->binding, FRAGMENT, nativeResourceBindingMaps, BindingType::Buffer);
+ if (nativeBinding >= 0) {
+ res[FRAGMENT].buffers.feed(nativeBinding, mtlbuf);
+ res[FRAGMENT].bufferOffsets.feed(b->binding, offset);
+ }
}
if (b->stage.testFlag(QRhiShaderResourceBinding::ComputeStage)) {
- res[2].buffers.feed(b->binding, mtlbuf);
- res[2].bufferOffsets.feed(b->binding, offset);
+ const int nativeBinding = mapBinding(b->binding, COMPUTE, nativeResourceBindingMaps, BindingType::Buffer);
+ if (nativeBinding >= 0) {
+ res[COMPUTE].buffers.feed(nativeBinding, mtlbuf);
+ res[COMPUTE].bufferOffsets.feed(b->binding, offset);
+ }
}
}
break;
@@ -700,55 +735,81 @@ void QRhiMetal::enqueueShaderResourceBindings(QMetalShaderResourceBindings *srbD
QMetalTexture *texD = QRHI_RES(QMetalTexture, b->u.stex.tex);
QMetalSampler *samplerD = QRHI_RES(QMetalSampler, b->u.stex.sampler);
if (b->stage.testFlag(QRhiShaderResourceBinding::VertexStage)) {
- res[0].textures.feed(b->binding, texD->d->tex);
- res[0].samplers.feed(b->binding, samplerD->d->samplerState);
+ const int nativeBindingTexture = mapBinding(b->binding, VERTEX, nativeResourceBindingMaps, BindingType::Texture);
+ const int nativeBindingSampler = mapBinding(b->binding, VERTEX, nativeResourceBindingMaps, BindingType::Sampler);
+ if (nativeBindingTexture >= 0 && nativeBindingSampler >= 0) {
+ res[VERTEX].textures.feed(nativeBindingTexture, texD->d->tex);
+ res[VERTEX].samplers.feed(nativeBindingSampler, samplerD->d->samplerState);
+ }
}
if (b->stage.testFlag(QRhiShaderResourceBinding::FragmentStage)) {
- res[1].textures.feed(b->binding, texD->d->tex);
- res[1].samplers.feed(b->binding, samplerD->d->samplerState);
+ const int nativeBindingTexture = mapBinding(b->binding, FRAGMENT, nativeResourceBindingMaps, BindingType::Texture);
+ const int nativeBindingSampler = mapBinding(b->binding, FRAGMENT, nativeResourceBindingMaps, BindingType::Sampler);
+ if (nativeBindingTexture >= 0 && nativeBindingSampler >= 0) {
+ res[FRAGMENT].textures.feed(nativeBindingTexture, texD->d->tex);
+ res[FRAGMENT].samplers.feed(nativeBindingSampler, samplerD->d->samplerState);
+ }
}
if (b->stage.testFlag(QRhiShaderResourceBinding::ComputeStage)) {
- res[2].textures.feed(b->binding, texD->d->tex);
- res[2].samplers.feed(b->binding, samplerD->d->samplerState);
+ const int nativeBindingTexture = mapBinding(b->binding, COMPUTE, nativeResourceBindingMaps, BindingType::Texture);
+ const int nativeBindingSampler = mapBinding(b->binding, COMPUTE, nativeResourceBindingMaps, BindingType::Sampler);
+ if (nativeBindingTexture >= 0 && nativeBindingSampler >= 0) {
+ res[COMPUTE].textures.feed(nativeBindingTexture, texD->d->tex);
+ res[COMPUTE].samplers.feed(nativeBindingSampler, samplerD->d->samplerState);
+ }
}
}
break;
case QRhiShaderResourceBinding::ImageLoad:
- Q_FALLTHROUGH();
case QRhiShaderResourceBinding::ImageStore:
- Q_FALLTHROUGH();
case QRhiShaderResourceBinding::ImageLoadStore:
{
QMetalTexture *texD = QRHI_RES(QMetalTexture, b->u.simage.tex);
id<MTLTexture> t = texD->d->viewForLevel(b->u.simage.level);
- if (b->stage.testFlag(QRhiShaderResourceBinding::VertexStage))
- res[0].textures.feed(b->binding, t);
- if (b->stage.testFlag(QRhiShaderResourceBinding::FragmentStage))
- res[1].textures.feed(b->binding, t);
- if (b->stage.testFlag(QRhiShaderResourceBinding::ComputeStage))
- res[2].textures.feed(b->binding, t);
+ if (b->stage.testFlag(QRhiShaderResourceBinding::VertexStage)) {
+ const int nativeBinding = mapBinding(b->binding, VERTEX, nativeResourceBindingMaps, BindingType::Texture);
+ if (nativeBinding >= 0)
+ res[VERTEX].textures.feed(nativeBinding, t);
+ }
+ if (b->stage.testFlag(QRhiShaderResourceBinding::FragmentStage)) {
+ const int nativeBinding = mapBinding(b->binding, FRAGMENT, nativeResourceBindingMaps, BindingType::Texture);
+ if (nativeBinding >= 0)
+ res[FRAGMENT].textures.feed(nativeBinding, t);
+ }
+ if (b->stage.testFlag(QRhiShaderResourceBinding::ComputeStage)) {
+ const int nativeBinding = mapBinding(b->binding, COMPUTE, nativeResourceBindingMaps, BindingType::Texture);
+ if (nativeBinding >= 0)
+ res[COMPUTE].textures.feed(nativeBinding, t);
+ }
}
break;
case QRhiShaderResourceBinding::BufferLoad:
- Q_FALLTHROUGH();
case QRhiShaderResourceBinding::BufferStore:
- Q_FALLTHROUGH();
case QRhiShaderResourceBinding::BufferLoadStore:
{
QMetalBuffer *bufD = QRHI_RES(QMetalBuffer, b->u.sbuf.buf);
id<MTLBuffer> mtlbuf = bufD->d->buf[0];
uint offset = uint(b->u.sbuf.offset);
if (b->stage.testFlag(QRhiShaderResourceBinding::VertexStage)) {
- res[0].buffers.feed(b->binding, mtlbuf);
- res[0].bufferOffsets.feed(b->binding, offset);
+ const int nativeBinding = mapBinding(b->binding, VERTEX, nativeResourceBindingMaps, BindingType::Buffer);
+ if (nativeBinding >= 0) {
+ res[VERTEX].buffers.feed(nativeBinding, mtlbuf);
+ res[VERTEX].bufferOffsets.feed(b->binding, offset);
+ }
}
if (b->stage.testFlag(QRhiShaderResourceBinding::FragmentStage)) {
- res[1].buffers.feed(b->binding, mtlbuf);
- res[1].bufferOffsets.feed(b->binding, offset);
+ const int nativeBinding = mapBinding(b->binding, FRAGMENT, nativeResourceBindingMaps, BindingType::Buffer);
+ if (nativeBinding >= 0) {
+ res[FRAGMENT].buffers.feed(nativeBinding, mtlbuf);
+ res[FRAGMENT].bufferOffsets.feed(b->binding, offset);
+ }
}
if (b->stage.testFlag(QRhiShaderResourceBinding::ComputeStage)) {
- res[2].buffers.feed(b->binding, mtlbuf);
- res[2].bufferOffsets.feed(b->binding, offset);
+ const int nativeBinding = mapBinding(b->binding, COMPUTE, nativeResourceBindingMaps, BindingType::Buffer);
+ if (nativeBinding >= 0) {
+ res[COMPUTE].buffers.feed(nativeBinding, mtlbuf);
+ res[COMPUTE].bufferOffsets.feed(b->binding, offset);
+ }
}
}
break;
@@ -758,25 +819,30 @@ void QRhiMetal::enqueueShaderResourceBindings(QMetalShaderResourceBindings *srbD
}
}
- for (int idx = 0; idx < KNOWN_STAGES; ++idx) {
- res[idx].buffers.finish();
- res[idx].bufferOffsets.finish();
+ for (int stage = 0; stage < SUPPORTED_STAGES; ++stage) {
+ if (cbD->recordingPass != QMetalCommandBuffer::RenderPass && (stage == VERTEX || stage == FRAGMENT))
+ continue;
+ if (cbD->recordingPass != QMetalCommandBuffer::ComputePass && stage == COMPUTE)
+ continue;
- for (int i = 0, ie = res[idx].buffers.batches.count(); i != ie; ++i) {
- const auto &bufferBatch(res[idx].buffers.batches[i]);
- const auto &offsetBatch(res[idx].bufferOffsets.batches[i]);
- switch (idx) {
- case 0:
+ res[stage].buffers.finish();
+ res[stage].bufferOffsets.finish();
+
+ for (int i = 0, ie = res[stage].buffers.batches.count(); i != ie; ++i) {
+ const auto &bufferBatch(res[stage].buffers.batches[i]);
+ const auto &offsetBatch(res[stage].bufferOffsets.batches[i]);
+ switch (stage) {
+ case VERTEX:
[cbD->d->currentRenderPassEncoder setVertexBuffers: bufferBatch.resources.constData()
offsets: offsetBatch.resources.constData()
withRange: NSMakeRange(bufferBatch.startBinding, NSUInteger(bufferBatch.resources.count()))];
break;
- case 1:
+ case FRAGMENT:
[cbD->d->currentRenderPassEncoder setFragmentBuffers: bufferBatch.resources.constData()
offsets: offsetBatch.resources.constData()
withRange: NSMakeRange(bufferBatch.startBinding, NSUInteger(bufferBatch.resources.count()))];
break;
- case 2:
+ case COMPUTE:
[cbD->d->currentComputePassEncoder setBuffers: bufferBatch.resources.constData()
offsets: offsetBatch.resources.constData()
withRange: NSMakeRange(bufferBatch.startBinding, NSUInteger(bufferBatch.resources.count()))];
@@ -790,21 +856,21 @@ void QRhiMetal::enqueueShaderResourceBindings(QMetalShaderResourceBindings *srbD
if (offsetOnlyChange)
continue;
- res[idx].textures.finish();
- res[idx].samplers.finish();
+ res[stage].textures.finish();
+ res[stage].samplers.finish();
- for (int i = 0, ie = res[idx].textures.batches.count(); i != ie; ++i) {
- const auto &batch(res[idx].textures.batches[i]);
- switch (idx) {
- case 0:
+ for (int i = 0, ie = res[stage].textures.batches.count(); i != ie; ++i) {
+ const auto &batch(res[stage].textures.batches[i]);
+ switch (stage) {
+ case VERTEX:
[cbD->d->currentRenderPassEncoder setVertexTextures: batch.resources.constData()
withRange: NSMakeRange(batch.startBinding, NSUInteger(batch.resources.count()))];
break;
- case 1:
+ case FRAGMENT:
[cbD->d->currentRenderPassEncoder setFragmentTextures: batch.resources.constData()
withRange: NSMakeRange(batch.startBinding, NSUInteger(batch.resources.count()))];
break;
- case 2:
+ case COMPUTE:
[cbD->d->currentComputePassEncoder setTextures: batch.resources.constData()
withRange: NSMakeRange(batch.startBinding, NSUInteger(batch.resources.count()))];
break;
@@ -813,18 +879,18 @@ void QRhiMetal::enqueueShaderResourceBindings(QMetalShaderResourceBindings *srbD
break;
}
}
- for (int i = 0, ie = res[idx].samplers.batches.count(); i != ie; ++i) {
- const auto &batch(res[idx].samplers.batches[i]);
- switch (idx) {
- case 0:
+ for (int i = 0, ie = res[stage].samplers.batches.count(); i != ie; ++i) {
+ const auto &batch(res[stage].samplers.batches[i]);
+ switch (stage) {
+ case VERTEX:
[cbD->d->currentRenderPassEncoder setVertexSamplerStates: batch.resources.constData()
withRange: NSMakeRange(batch.startBinding, NSUInteger(batch.resources.count()))];
break;
- case 1:
+ case FRAGMENT:
[cbD->d->currentRenderPassEncoder setFragmentSamplerStates: batch.resources.constData()
withRange: NSMakeRange(batch.startBinding, NSUInteger(batch.resources.count()))];
break;
- case 2:
+ case COMPUTE:
[cbD->d->currentComputePassEncoder setSamplerStates: batch.resources.constData()
withRange: NSMakeRange(batch.startBinding, NSUInteger(batch.resources.count()))];
break;
@@ -926,9 +992,7 @@ void QRhiMetal::setShaderResources(QRhiCommandBuffer *cb, QRhiShaderResourceBind
}
break;
case QRhiShaderResourceBinding::ImageLoad:
- Q_FALLTHROUGH();
case QRhiShaderResourceBinding::ImageStore:
- Q_FALLTHROUGH();
case QRhiShaderResourceBinding::ImageLoadStore:
{
QMetalTexture *texD = QRHI_RES(QMetalTexture, b->u.simage.tex);
@@ -941,9 +1005,7 @@ void QRhiMetal::setShaderResources(QRhiCommandBuffer *cb, QRhiShaderResourceBind
}
break;
case QRhiShaderResourceBinding::BufferLoad:
- Q_FALLTHROUGH();
case QRhiShaderResourceBinding::BufferStore:
- Q_FALLTHROUGH();
case QRhiShaderResourceBinding::BufferLoadStore:
{
QMetalBuffer *bufD = QRHI_RES(QMetalBuffer, b->u.sbuf.buf);
@@ -973,18 +1035,22 @@ void QRhiMetal::setShaderResources(QRhiCommandBuffer *cb, QRhiShaderResourceBind
// dynamic uniform buffer offsets always trigger a rebind
if (hasDynamicOffsetInSrb || resNeedsRebind || srbChanged || srbRebuilt) {
+ const QShader::NativeResourceBindingMap *resBindMaps[SUPPORTED_STAGES] = { nullptr, nullptr, nullptr };
if (gfxPsD) {
cbD->currentGraphicsSrb = srb;
cbD->currentComputeSrb = nullptr;
+ resBindMaps[0] = &gfxPsD->d->vs.nativeResourceBindingMap;
+ resBindMaps[1] = &gfxPsD->d->fs.nativeResourceBindingMap;
} else {
cbD->currentGraphicsSrb = nullptr;
cbD->currentComputeSrb = srb;
+ resBindMaps[2] = &compPsD->d->cs.nativeResourceBindingMap;
}
cbD->currentSrbGeneration = srbD->generation;
cbD->currentResSlot = resSlot;
const bool offsetOnlyChange = hasDynamicOffsetInSrb && !resNeedsRebind && !srbChanged && !srbRebuilt;
- enqueueShaderResourceBindings(srbD, cbD, dynamicOffsetCount, dynamicOffsets, offsetOnlyChange);
+ enqueueShaderResourceBindings(srbD, cbD, dynamicOffsetCount, dynamicOffsets, offsetOnlyChange, resBindMaps);
}
}
@@ -1732,8 +1798,10 @@ void QRhiMetal::executeBufferHostWritesForCurrentFrame(QMetalBuffer *bufD)
if (changeEnd == -1 || u.offset + u.data.size() > changeEnd)
changeEnd = u.offset + u.data.size();
}
+#ifdef Q_OS_MACOS
if (changeBegin >= 0 && bufD->d->managed)
[bufD->d->buf[idx] didModifyRange: NSMakeRange(NSUInteger(changeBegin), NSUInteger(changeEnd - changeBegin))];
+#endif
bufD->d->pendingUpdates[idx].clear();
}
@@ -1767,8 +1835,12 @@ void QRhiMetal::beginPass(QRhiCommandBuffer *cb,
if (color0.needsDrawableForTex || color0.needsDrawableForResolveTex) {
Q_ASSERT(currentSwapChain);
QMetalSwapChain *swapChainD = QRHI_RES(QMetalSwapChain, currentSwapChain);
- if (!swapChainD->d->curDrawable)
- swapChainD->d->curDrawable = [swapChainD->d->layer nextDrawable];
+ if (!swapChainD->d->curDrawable) {
+#ifdef TARGET_IPHONE_SIMULATOR
+ if (@available(ios 13.0, *))
+#endif
+ swapChainD->d->curDrawable = [swapChainD->d->layer nextDrawable];
+ }
if (!swapChainD->d->curDrawable) {
qWarning("No drawable");
return;
@@ -2139,12 +2211,13 @@ bool QMetalRenderBuffer::build()
case DepthStencil:
#ifdef Q_OS_MACOS
desc.storageMode = MTLStorageModePrivate;
+ d->format = rhiD->d->dev.depth24Stencil8PixelFormatSupported
+ ? MTLPixelFormatDepth24Unorm_Stencil8 : MTLPixelFormatDepth32Float_Stencil8;
#else
- desc.storageMode = MTLResourceStorageModeMemoryless;
+ desc.storageMode = MTLStorageModeMemoryless;
transientBacking = true;
+ d->format = MTLPixelFormatDepth32Float_Stencil8;
#endif
- d->format = rhiD->d->dev.depth24Stencil8PixelFormatSupported
- ? MTLPixelFormatDepth24Unorm_Stencil8 : MTLPixelFormatDepth32Float_Stencil8;
desc.pixelFormat = d->format;
break;
case Color:
@@ -2206,7 +2279,6 @@ void QMetalTexture::release()
e.texture.texture = d->owns ? d->tex : nil;
d->tex = nil;
- nativeHandlesStruct.texture = nullptr;
for (int i = 0; i < QMTL_FRAMES_IN_FLIGHT; ++i) {
e.texture.stagingBuffers[i] = d->stagingBuf[i];
@@ -2248,6 +2320,10 @@ static inline MTLPixelFormat toMetalTextureFormat(QRhiTexture::Format format, QR
return MTLPixelFormatRGBA16Float;
case QRhiTexture::RGBA32F:
return MTLPixelFormatRGBA32Float;
+ case QRhiTexture::R16F:
+ return MTLPixelFormatR16Float;
+ case QRhiTexture::R32F:
+ return MTLPixelFormatR32Float;
case QRhiTexture::D16:
#ifdef Q_OS_MACOS
@@ -2418,7 +2494,6 @@ bool QMetalTexture::build()
d->tex.label = [NSString stringWithUTF8String: m_objectName.constData()];
d->owns = true;
- nativeHandlesStruct.texture = d->tex;
QRHI_PROF;
QRHI_PROF_F(newTexture(this, true, mipLevelCount, isCube ? 6 : 1, samples));
@@ -2429,19 +2504,18 @@ bool QMetalTexture::build()
return true;
}
-bool QMetalTexture::buildFrom(const QRhiNativeHandles *src)
+bool QMetalTexture::buildFrom(QRhiTexture::NativeTexture src)
{
- const QRhiMetalTextureNativeHandles *h = static_cast<const QRhiMetalTextureNativeHandles *>(src);
- if (!h || !h->texture)
+ void * const * tex = (void * const *) src.object;
+ if (!tex || !*tex)
return false;
if (!prepareBuild())
return false;
- d->tex = (id<MTLTexture>) h->texture;
+ d->tex = (id<MTLTexture>) *tex;
d->owns = false;
- nativeHandlesStruct.texture = d->tex;
QRHI_PROF;
QRHI_PROF_F(newTexture(this, false, mipLevelCount, m_flags.testFlag(CubeMap) ? 6 : 1, samples));
@@ -2453,9 +2527,9 @@ bool QMetalTexture::buildFrom(const QRhiNativeHandles *src)
return true;
}
-const QRhiNativeHandles *QMetalTexture::nativeHandles()
+QRhiTexture::NativeTexture QMetalTexture::nativeTexture()
{
- return &nativeHandlesStruct;
+ return {&d->tex, 0};
}
id<MTLTexture> QMetalTextureData::viewForLevel(int level)
@@ -2538,12 +2612,8 @@ static inline MTLSamplerAddressMode toMetalAddressMode(QRhiSampler::AddressMode
return MTLSamplerAddressModeRepeat;
case QRhiSampler::ClampToEdge:
return MTLSamplerAddressModeClampToEdge;
- case QRhiSampler::Border:
- return MTLSamplerAddressModeClampToBorderColor;
case QRhiSampler::Mirror:
return MTLSamplerAddressModeMirrorRepeat;
- case QRhiSampler::MirrorOnce:
- return MTLSamplerAddressModeMirrorClampToEdge;
default:
Q_UNREACHABLE();
return MTLSamplerAddressModeClampToEdge;
@@ -2616,6 +2686,32 @@ void QMetalRenderPassDescriptor::release()
// nothing to do here
}
+bool QMetalRenderPassDescriptor::isCompatible(const QRhiRenderPassDescriptor *other) const
+{
+ if (!other)
+ return false;
+
+ const QMetalRenderPassDescriptor *o = QRHI_RES(const QMetalRenderPassDescriptor, other);
+
+ if (colorAttachmentCount != o->colorAttachmentCount)
+ return false;
+
+ if (hasDepthStencil != o->hasDepthStencil)
+ return false;
+
+ for (int i = 0; i < colorAttachmentCount; ++i) {
+ if (colorFormat[i] != o->colorFormat[i])
+ return false;
+ }
+
+ if (hasDepthStencil) {
+ if (dsFormat != o->dsFormat)
+ return false;
+ }
+
+ return true;
+}
+
QMetalReferenceRenderTarget::QMetalReferenceRenderTarget(QRhiImplementation *rhi)
: QRhiRenderTarget(rhi),
d(new QMetalRenderTargetData)
@@ -2828,9 +2924,7 @@ bool QMetalShaderResourceBindings::build()
}
break;
case QRhiShaderResourceBinding::ImageLoad:
- Q_FALLTHROUGH();
case QRhiShaderResourceBinding::ImageStore:
- Q_FALLTHROUGH();
case QRhiShaderResourceBinding::ImageLoadStore:
{
QMetalTexture *texD = QRHI_RES(QMetalTexture, b->u.simage.tex);
@@ -2839,9 +2933,7 @@ bool QMetalShaderResourceBindings::build()
}
break;
case QRhiShaderResourceBinding::BufferLoad:
- Q_FALLTHROUGH();
case QRhiShaderResourceBinding::BufferStore:
- Q_FALLTHROUGH();
case QRhiShaderResourceBinding::BufferLoadStore:
{
QMetalBuffer *bufD = QRHI_RES(QMetalBuffer, b->u.sbuf.buf);
@@ -3081,9 +3173,10 @@ static inline MTLCullMode toMetalCullMode(QRhiGraphicsPipeline::CullMode c)
}
id<MTLLibrary> QRhiMetalData::createMetalLib(const QShader &shader, QShader::Variant shaderVariant,
- QString *error, QByteArray *entryPoint)
+ QString *error, QByteArray *entryPoint, QShaderKey *activeKey)
{
- QShaderCode mtllib = shader.shader({ QShader::MetalLibShader, 12, shaderVariant });
+ QShaderKey key = { QShader::MetalLibShader, 12, shaderVariant };
+ QShaderCode mtllib = shader.shader(key);
if (!mtllib.shader().isEmpty()) {
dispatch_data_t data = dispatch_data_create(mtllib.shader().constData(),
size_t(mtllib.shader().size()),
@@ -3094,6 +3187,7 @@ id<MTLLibrary> QRhiMetalData::createMetalLib(const QShader &shader, QShader::Var
dispatch_release(data);
if (!err) {
*entryPoint = mtllib.entryPoint();
+ *activeKey = key;
return lib;
} else {
const QString msg = QString::fromNSString(err.localizedDescription);
@@ -3101,7 +3195,8 @@ id<MTLLibrary> QRhiMetalData::createMetalLib(const QShader &shader, QShader::Var
}
}
- QShaderCode mslSource = shader.shader({ QShader::MslShader, 12, shaderVariant });
+ key = { QShader::MslShader, 12, shaderVariant };
+ QShaderCode mslSource = shader.shader(key);
if (mslSource.shader().isEmpty()) {
qWarning() << "No MSL 1.2 code found in baked shader" << shader;
return nil;
@@ -3115,13 +3210,17 @@ id<MTLLibrary> QRhiMetalData::createMetalLib(const QShader &shader, QShader::Var
[opts release];
// src is autoreleased
- if (err) {
+ // if lib is null and err is non-null, we had errors (fail)
+ // if lib is non-null and err is non-null, we had warnings (success)
+ // if lib is non-null and err is null, there were no errors or warnings (success)
+ if (!lib) {
const QString msg = QString::fromNSString(err.localizedDescription);
*error = msg;
return nil;
}
*entryPoint = mslSource.entryPoint();
+ *activeKey = key;
return lib;
}
@@ -3195,9 +3294,12 @@ bool QMetalGraphicsPipeline::build()
break;
}
} else {
+ const QShader shader = shaderStage.shader();
QString error;
QByteArray entryPoint;
- id<MTLLibrary> lib = rhiD->d->createMetalLib(shaderStage.shader(), shaderStage.shaderVariant(), &error, &entryPoint);
+ QShaderKey activeKey;
+ id<MTLLibrary> lib = rhiD->d->createMetalLib(shader, shaderStage.shaderVariant(),
+ &error, &entryPoint, &activeKey);
if (!lib) {
qWarning("MSL shader compilation failed: %s", qPrintable(error));
return false;
@@ -3218,6 +3320,8 @@ bool QMetalGraphicsPipeline::build()
case QRhiShaderStage::Vertex:
d->vs.lib = lib;
d->vs.func = func;
+ if (const QShader::NativeResourceBindingMap *map = shader.nativeResourceBindingMap(activeKey))
+ d->vs.nativeResourceBindingMap = *map;
rhiD->d->shaderCache.insert(shaderStage, d->vs);
[d->vs.lib retain];
[d->vs.func retain];
@@ -3226,6 +3330,8 @@ bool QMetalGraphicsPipeline::build()
case QRhiShaderStage::Fragment:
d->fs.lib = lib;
d->fs.func = func;
+ if (const QShader::NativeResourceBindingMap *map = shader.nativeResourceBindingMap(activeKey))
+ d->fs.nativeResourceBindingMap = *map;
rhiD->d->shaderCache.insert(shaderStage, d->fs);
[d->fs.lib retain];
[d->fs.func retain];
@@ -3269,7 +3375,11 @@ bool QMetalGraphicsPipeline::build()
// validation blows up otherwise.
MTLPixelFormat fmt = MTLPixelFormat(rpD->dsFormat);
rpDesc.depthAttachmentPixelFormat = fmt;
+#ifdef Q_OS_MACOS
if (fmt != MTLPixelFormatDepth16Unorm && fmt != MTLPixelFormatDepth32Float)
+#else
+ if (fmt != MTLPixelFormatDepth32Float)
+#endif
rpDesc.stencilAttachmentPixelFormat = fmt;
}
@@ -3360,8 +3470,9 @@ bool QMetalComputePipeline::build()
const QShader shader = m_shaderStage.shader();
QString error;
QByteArray entryPoint;
+ QShaderKey activeKey;
id<MTLLibrary> lib = rhiD->d->createMetalLib(shader, m_shaderStage.shaderVariant(),
- &error, &entryPoint);
+ &error, &entryPoint, &activeKey);
if (!lib) {
qWarning("MSL shader compilation failed: %s", qPrintable(error));
return false;
@@ -3375,6 +3486,8 @@ bool QMetalComputePipeline::build()
d->cs.lib = lib;
d->cs.func = func;
d->cs.localSize = shader.description().computeShaderLocalSize();
+ if (const QShader::NativeResourceBindingMap *map = shader.nativeResourceBindingMap(activeKey))
+ d->cs.nativeResourceBindingMap = *map;
if (rhiD->d->shaderCache.count() >= QRhiMetal::MAX_SHADER_CACHE_ENTRIES) {
for (QMetalShader &s : rhiD->d->shaderCache)
@@ -3483,6 +3596,10 @@ QMetalSwapChain::~QMetalSwapChain()
void QMetalSwapChain::release()
{
+#ifdef TARGET_IPHONE_SIMULATOR
+ if (@available(ios 13.0, *)) {
+#endif
+
if (!d->layer)
return;
@@ -3511,6 +3628,10 @@ void QMetalSwapChain::release()
QRHI_PROF_F(releaseSwapChain(this));
rhiD->unregisterResource(this);
+
+#ifdef TARGET_IPHONE_SIMULATOR
+ }
+#endif
}
QRhiCommandBuffer *QMetalSwapChain::currentFrameCommandBuffer()
@@ -3533,16 +3654,20 @@ QRhiRenderPassDescriptor *QMetalSwapChain::newCompatibleRenderPassDescriptor()
{
chooseFormats(); // ensure colorFormat and similar are filled out
- QRHI_RES_RHI(QRhiMetal);
QMetalRenderPassDescriptor *rpD = new QMetalRenderPassDescriptor(m_rhi);
rpD->colorAttachmentCount = 1;
rpD->hasDepthStencil = m_depthStencil != nullptr;
rpD->colorFormat[0] = int(d->colorFormat);
+#ifdef Q_OS_MACOS
// m_depthStencil may not be built yet so cannot rely on computed fields in it
+ QRHI_RES_RHI(QRhiMetal);
rpD->dsFormat = rhiD->d->dev.depth24Stencil8PixelFormatSupported
? MTLPixelFormatDepth24Unorm_Stencil8 : MTLPixelFormatDepth32Float_Stencil8;
+#else
+ rpD->dsFormat = MTLPixelFormatDepth32Float_Stencil8;
+#endif
return rpD;
}
@@ -3558,6 +3683,10 @@ void QMetalSwapChain::chooseFormats()
bool QMetalSwapChain::buildOrResize()
{
+#ifdef TARGET_IPHONE_SIMULATOR
+ if (@available(ios 13.0, *)) {
+#endif
+
Q_ASSERT(m_window);
const bool needsRegistration = !window || window != m_window;
@@ -3577,7 +3706,11 @@ bool QMetalSwapChain::buildOrResize()
return false;
}
+#ifdef Q_OS_MACOS
NSView *view = reinterpret_cast<NSView *>(window->winId());
+#else
+ UIView *view = reinterpret_cast<UIView *>(window->winId());
+#endif
Q_ASSERT(view);
d->layer = static_cast<CAMetalLayer *>(view.layer);
Q_ASSERT(d->layer);
@@ -3681,6 +3814,15 @@ bool QMetalSwapChain::buildOrResize()
rhiD->registerResource(this);
return true;
+
+#ifdef TARGET_IPHONE_SIMULATOR
+ } else {
+ // Won't ever get here in a normal app because MTLDevice creation would
+ // fail too. Print a warning, just in case.
+ qWarning("No CAMetalLayer support in this version of the iOS Simulator");
+ return false;
+ }
+#endif
}
QT_END_NAMESPACE
diff --git a/src/gui/rhi/qrhimetal_p.h b/src/gui/rhi/qrhimetal_p.h
index 094801c58c..17e28b2c0f 100644
--- a/src/gui/rhi/qrhimetal_p.h
+++ b/src/gui/rhi/qrhimetal_p.h
@@ -64,11 +64,6 @@ struct Q_GUI_EXPORT QRhiMetalNativeHandles : public QRhiNativeHandles
void *cmdQueue = nullptr; // id<MTLCommandQueue>
};
-struct Q_GUI_EXPORT QRhiMetalTextureNativeHandles : public QRhiNativeHandles
-{
- void *texture = nullptr; // id<MTLTexture>
-};
-
struct Q_GUI_EXPORT QRhiMetalCommandBufferNativeHandles : public QRhiNativeHandles
{
void *commandBuffer = nullptr; // id<MTLCommandBuffer>
diff --git a/src/gui/rhi/qrhimetal_p_p.h b/src/gui/rhi/qrhimetal_p_p.h
index 688fec8147..71d4325b1a 100644
--- a/src/gui/rhi/qrhimetal_p_p.h
+++ b/src/gui/rhi/qrhimetal_p_p.h
@@ -100,13 +100,12 @@ struct QMetalTexture : public QRhiTexture
~QMetalTexture();
void release() override;
bool build() override;
- bool buildFrom(const QRhiNativeHandles *src) override;
- const QRhiNativeHandles *nativeHandles() override;
+ bool buildFrom(NativeTexture src) override;
+ NativeTexture nativeTexture() override;
bool prepareBuild(QSize *adjustedSize = nullptr);
QMetalTextureData *d;
- QRhiMetalTextureNativeHandles nativeHandlesStruct;
int mipLevelCount = 0;
int samples = 1;
uint generation = 0;
@@ -138,6 +137,7 @@ struct QMetalRenderPassDescriptor : public QRhiRenderPassDescriptor
QMetalRenderPassDescriptor(QRhiImplementation *rhi);
~QMetalRenderPassDescriptor();
void release() override;
+ bool isCompatible(const QRhiRenderPassDescriptor *other) const override;
// there is no MTLRenderPassDescriptor here as one will be created for each pass in beginPass()
@@ -433,10 +433,13 @@ public:
qsizetype *curOfs);
void enqueueResourceUpdates(QRhiCommandBuffer *cb, QRhiResourceUpdateBatch *resourceUpdates);
void executeBufferHostWritesForCurrentFrame(QMetalBuffer *bufD);
- void enqueueShaderResourceBindings(QMetalShaderResourceBindings *srbD, QMetalCommandBuffer *cbD,
+ static const int SUPPORTED_STAGES = 3;
+ void enqueueShaderResourceBindings(QMetalShaderResourceBindings *srbD,
+ QMetalCommandBuffer *cbD,
int dynamicOffsetCount,
const QRhiCommandBuffer::DynamicOffset *dynamicOffsets,
- bool offsetOnlyChange);
+ bool offsetOnlyChange,
+ const QShader::NativeResourceBindingMap *nativeResourceBindingMaps[SUPPORTED_STAGES]);
int effectiveSampleCount(int sampleCount) const;
bool importedDevice = false;
diff --git a/src/gui/rhi/qrhinull.cpp b/src/gui/rhi/qrhinull.cpp
index fe606f971f..ea67f80138 100644
--- a/src/gui/rhi/qrhinull.cpp
+++ b/src/gui/rhi/qrhinull.cpp
@@ -67,13 +67,6 @@ QT_BEGIN_NAMESPACE
\brief Empty.
*/
-/*!
- \class QRhiNullTextureNativeHandles
- \internal
- \inmodule QtGui
- \brief Empty.
- */
-
QRhiNull::QRhiNull(QRhiNullInitParams *params)
: offscreenCommandBuffer(this)
{
@@ -638,9 +631,9 @@ bool QNullTexture::build()
return true;
}
-bool QNullTexture::buildFrom(const QRhiNativeHandles *src)
+bool QNullTexture::buildFrom(QRhiTexture::NativeTexture src)
{
- Q_UNUSED(src);
+ Q_UNUSED(src)
QRHI_RES_RHI(QRhiNull);
const bool isCube = m_flags.testFlag(CubeMap);
const bool hasMipMaps = m_flags.testFlag(MipMapped);
@@ -651,11 +644,6 @@ bool QNullTexture::buildFrom(const QRhiNativeHandles *src)
return true;
}
-const QRhiNativeHandles *QNullTexture::nativeHandles()
-{
- return &nativeHandlesStruct;
-}
-
QNullSampler::QNullSampler(QRhiImplementation *rhi, Filter magFilter, Filter minFilter, Filter mipmapMode,
AddressMode u, AddressMode v)
: QRhiSampler(rhi, magFilter, minFilter, mipmapMode, u, v)
@@ -690,6 +678,12 @@ void QNullRenderPassDescriptor::release()
{
}
+bool QNullRenderPassDescriptor::isCompatible(const QRhiRenderPassDescriptor *other) const
+{
+ Q_UNUSED(other);
+ return true;
+}
+
QNullReferenceRenderTarget::QNullReferenceRenderTarget(QRhiImplementation *rhi)
: QRhiRenderTarget(rhi),
d(rhi)
diff --git a/src/gui/rhi/qrhinull_p.h b/src/gui/rhi/qrhinull_p.h
index 7d3ce5dbf1..dbf385555d 100644
--- a/src/gui/rhi/qrhinull_p.h
+++ b/src/gui/rhi/qrhinull_p.h
@@ -60,10 +60,6 @@ struct Q_GUI_EXPORT QRhiNullNativeHandles : public QRhiNativeHandles
{
};
-struct Q_GUI_EXPORT QRhiNullTextureNativeHandles : public QRhiNativeHandles
-{
-};
-
QT_END_NAMESPACE
#endif
diff --git a/src/gui/rhi/qrhinull_p_p.h b/src/gui/rhi/qrhinull_p_p.h
index ce517bfa63..f541fd90b8 100644
--- a/src/gui/rhi/qrhinull_p_p.h
+++ b/src/gui/rhi/qrhinull_p_p.h
@@ -80,10 +80,8 @@ struct QNullTexture : public QRhiTexture
~QNullTexture();
void release() override;
bool build() override;
- bool buildFrom(const QRhiNativeHandles *src) override;
- const QRhiNativeHandles *nativeHandles() override;
+ bool buildFrom(NativeTexture src) override;
- QRhiNullTextureNativeHandles nativeHandlesStruct;
QImage image[QRhi::MAX_LAYERS][QRhi::MAX_LEVELS];
};
@@ -101,6 +99,7 @@ struct QNullRenderPassDescriptor : public QRhiRenderPassDescriptor
QNullRenderPassDescriptor(QRhiImplementation *rhi);
~QNullRenderPassDescriptor();
void release() override;
+ bool isCompatible(const QRhiRenderPassDescriptor *other) const override;
};
struct QNullRenderTargetData
diff --git a/src/gui/rhi/qrhivulkan.cpp b/src/gui/rhi/qrhivulkan.cpp
index 2d69abb36b..e7faa0cadd 100644
--- a/src/gui/rhi/qrhivulkan.cpp
+++ b/src/gui/rhi/qrhivulkan.cpp
@@ -48,7 +48,7 @@
#include <qmath.h>
#include <QVulkanFunctions>
-#include <QVulkanWindow>
+#include <QtGui/qwindow.h>
QT_BEGIN_NAMESPACE
@@ -177,21 +177,6 @@ QT_BEGIN_NAMESPACE
*/
/*!
- \class QRhiVulkanTextureNativeHandles
- \internal
- \inmodule QtGui
- \brief Holds the Vulkan image object that is backing a QRhiTexture.
-
- Importing and exporting Vulkan image objects that back a QRhiTexture when
- running with the Vulkan backend is supported via this class. Ownership of
- the Vulkan object is never transferred.
-
- \note Memory allocation details are not exposed. This is intentional since
- memory is typically suballocated from a bigger chunk of VkDeviceMemory, and
- exposing the allocator details is not desirable for now.
- */
-
-/*!
\class QRhiVulkanCommandBufferNativeHandles
\internal
\inmodule QtGui
@@ -501,6 +486,17 @@ bool QRhiVulkan::create(QRhi::Flags flags)
}
}
+ QByteArrayList envExtList;
+ if (qEnvironmentVariableIsSet("QT_VULKAN_DEVICE_EXTENSIONS")) {
+ envExtList = qgetenv("QT_VULKAN_DEVICE_EXTENSIONS").split(';');
+ for (auto ext : requestedDevExts)
+ envExtList.removeAll(ext);
+ for (const QByteArray &ext : envExtList) {
+ if (!ext.isEmpty())
+ requestedDevExts.append(ext.constData());
+ }
+ }
+
VkDeviceCreateInfo devInfo;
memset(&devInfo, 0, sizeof(devInfo));
devInfo.sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO;
@@ -771,6 +767,10 @@ static inline VkFormat toVkTextureFormat(QRhiTexture::Format format, QRhiTexture
return VK_FORMAT_R16G16B16A16_SFLOAT;
case QRhiTexture::RGBA32F:
return VK_FORMAT_R32G32B32A32_SFLOAT;
+ case QRhiTexture::R16F:
+ return VK_FORMAT_R16_SFLOAT;
+ case QRhiTexture::R32F:
+ return VK_FORMAT_R32_SFLOAT;
case QRhiTexture::D16:
return VK_FORMAT_D16_UNORM;
@@ -868,7 +868,6 @@ static inline bool isDepthTextureFormat(QRhiTexture::Format format)
{
switch (format) {
case QRhiTexture::Format::D16:
- Q_FALLTHROUGH();
case QRhiTexture::Format::D32F:
return true;
@@ -1032,54 +1031,62 @@ VkFormat QRhiVulkan::optimalDepthStencilFormat()
return optimalDsFormat;
}
-bool QRhiVulkan::createDefaultRenderPass(VkRenderPass *rp, bool hasDepthStencil, VkSampleCountFlagBits samples, VkFormat colorFormat)
+bool QRhiVulkan::createDefaultRenderPass(QVkRenderPassDescriptor *rpD, bool hasDepthStencil, VkSampleCountFlagBits samples, VkFormat colorFormat)
{
- VkAttachmentDescription attDesc[3];
- memset(attDesc, 0, sizeof(attDesc));
-
// attachment list layout is color (1), ds (0-1), resolve (0-1)
- attDesc[0].format = colorFormat;
- attDesc[0].samples = samples;
- attDesc[0].loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR;
- attDesc[0].storeOp = samples > VK_SAMPLE_COUNT_1_BIT ? VK_ATTACHMENT_STORE_OP_DONT_CARE : VK_ATTACHMENT_STORE_OP_STORE;
- attDesc[0].stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE;
- attDesc[0].stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE;
- attDesc[0].initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
- attDesc[0].finalLayout = samples > VK_SAMPLE_COUNT_1_BIT ? VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL : VK_IMAGE_LAYOUT_PRESENT_SRC_KHR;
-
- // clear on load + no store + lazy alloc + transient image should play
- // nicely with tiled GPUs (no physical backing necessary for ds buffer)
- attDesc[1].format = optimalDepthStencilFormat();
- attDesc[1].samples = samples;
- attDesc[1].loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR;
- attDesc[1].storeOp = VK_ATTACHMENT_STORE_OP_DONT_CARE;
- attDesc[1].stencilLoadOp = VK_ATTACHMENT_LOAD_OP_CLEAR;
- attDesc[1].stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE;
- attDesc[1].initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
- attDesc[1].finalLayout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL;
+ VkAttachmentDescription attDesc;
+ memset(&attDesc, 0, sizeof(attDesc));
+ attDesc.format = colorFormat;
+ attDesc.samples = samples;
+ attDesc.loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR;
+ attDesc.storeOp = samples > VK_SAMPLE_COUNT_1_BIT ? VK_ATTACHMENT_STORE_OP_DONT_CARE : VK_ATTACHMENT_STORE_OP_STORE;
+ attDesc.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE;
+ attDesc.stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE;
+ attDesc.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
+ attDesc.finalLayout = samples > VK_SAMPLE_COUNT_1_BIT ? VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL : VK_IMAGE_LAYOUT_PRESENT_SRC_KHR;
+ rpD->attDescs.append(attDesc);
- if (samples > VK_SAMPLE_COUNT_1_BIT) {
- attDesc[2].format = colorFormat;
- attDesc[2].samples = VK_SAMPLE_COUNT_1_BIT;
- attDesc[2].loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR;
- attDesc[2].storeOp = VK_ATTACHMENT_STORE_OP_STORE;
- attDesc[2].stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE;
- attDesc[2].stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE;
- attDesc[2].initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
- attDesc[2].finalLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR;
+ rpD->colorRefs.append({ 0, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL });
+
+ if (hasDepthStencil) {
+ // clear on load + no store + lazy alloc + transient image should play
+ // nicely with tiled GPUs (no physical backing necessary for ds buffer)
+ memset(&attDesc, 0, sizeof(attDesc));
+ attDesc.format = optimalDepthStencilFormat();
+ attDesc.samples = samples;
+ attDesc.loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR;
+ attDesc.storeOp = VK_ATTACHMENT_STORE_OP_DONT_CARE;
+ attDesc.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_CLEAR;
+ attDesc.stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE;
+ attDesc.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
+ attDesc.finalLayout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL;
+ rpD->attDescs.append(attDesc);
+
+ rpD->dsRef = { 1, VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL };
}
- VkAttachmentReference colorRef = { 0, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL };
- VkAttachmentReference dsRef = { 1, VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL };
- VkAttachmentReference resolveRef = { 2, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL };
+ if (samples > VK_SAMPLE_COUNT_1_BIT) {
+ memset(&attDesc, 0, sizeof(attDesc));
+ attDesc.format = colorFormat;
+ attDesc.samples = VK_SAMPLE_COUNT_1_BIT;
+ attDesc.loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR;
+ attDesc.storeOp = VK_ATTACHMENT_STORE_OP_STORE;
+ attDesc.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE;
+ attDesc.stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE;
+ attDesc.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
+ attDesc.finalLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR;
+ rpD->attDescs.append(attDesc);
+
+ rpD->resolveRefs.append({ 2, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL });
+ }
VkSubpassDescription subpassDesc;
memset(&subpassDesc, 0, sizeof(subpassDesc));
subpassDesc.pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS;
subpassDesc.colorAttachmentCount = 1;
- subpassDesc.pColorAttachments = &colorRef;
- subpassDesc.pDepthStencilAttachment = hasDepthStencil ? &dsRef : nullptr;
+ subpassDesc.pColorAttachments = rpD->colorRefs.constData();
+ subpassDesc.pDepthStencilAttachment = hasDepthStencil ? &rpD->dsRef : nullptr;
// Replace the first implicit dep (TOP_OF_PIPE / ALL_COMMANDS) with our own.
VkSubpassDependency subpassDep;
@@ -1095,7 +1102,7 @@ bool QRhiVulkan::createDefaultRenderPass(VkRenderPass *rp, bool hasDepthStencil,
memset(&rpInfo, 0, sizeof(rpInfo));
rpInfo.sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO;
rpInfo.attachmentCount = 1;
- rpInfo.pAttachments = attDesc;
+ rpInfo.pAttachments = rpD->attDescs.constData();
rpInfo.subpassCount = 1;
rpInfo.pSubpasses = &subpassDesc;
rpInfo.dependencyCount = 1;
@@ -1106,19 +1113,21 @@ bool QRhiVulkan::createDefaultRenderPass(VkRenderPass *rp, bool hasDepthStencil,
if (samples > VK_SAMPLE_COUNT_1_BIT) {
rpInfo.attachmentCount += 1;
- subpassDesc.pResolveAttachments = &resolveRef;
+ subpassDesc.pResolveAttachments = rpD->resolveRefs.constData();
}
- VkResult err = df->vkCreateRenderPass(dev, &rpInfo, nullptr, rp);
+ VkResult err = df->vkCreateRenderPass(dev, &rpInfo, nullptr, &rpD->rp);
if (err != VK_SUCCESS) {
qWarning("Failed to create renderpass: %d", err);
return false;
}
+ rpD->hasDepthStencil = hasDepthStencil;
+
return true;
}
-bool QRhiVulkan::createOffscreenRenderPass(VkRenderPass *rp,
+bool QRhiVulkan::createOffscreenRenderPass(QVkRenderPassDescriptor *rpD,
const QRhiColorAttachment *firstColorAttachment,
const QRhiColorAttachment *lastColorAttachment,
bool preserveColor,
@@ -1126,10 +1135,6 @@ bool QRhiVulkan::createOffscreenRenderPass(VkRenderPass *rp,
QRhiRenderBuffer *depthStencilBuffer,
QRhiTexture *depthTexture)
{
- QVarLengthArray<VkAttachmentDescription, 8> attDescs;
- QVarLengthArray<VkAttachmentReference, 8> colorRefs;
- QVarLengthArray<VkAttachmentReference, 8> resolveRefs;
-
// attachment list layout is color (0-8), ds (0-1), resolve (0-8)
for (auto it = firstColorAttachment; it != lastColorAttachment; ++it) {
@@ -1150,14 +1155,14 @@ bool QRhiVulkan::createOffscreenRenderPass(VkRenderPass *rp,
// this has to interact correctly with activateTextureRenderTarget(), hence leaving in COLOR_ATT
attDesc.initialLayout = preserveColor ? VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL : VK_IMAGE_LAYOUT_UNDEFINED;
attDesc.finalLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
- attDescs.append(attDesc);
+ rpD->attDescs.append(attDesc);
- const VkAttachmentReference ref = { uint32_t(attDescs.count() - 1), VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL };
- colorRefs.append(ref);
+ const VkAttachmentReference ref = { uint32_t(rpD->attDescs.count() - 1), VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL };
+ rpD->colorRefs.append(ref);
}
- const bool hasDepthStencil = depthStencilBuffer || depthTexture;
- if (hasDepthStencil) {
+ rpD->hasDepthStencil = depthStencilBuffer || depthTexture;
+ if (rpD->hasDepthStencil) {
const VkFormat dsFormat = depthTexture ? QRHI_RES(QVkTexture, depthTexture)->vkformat
: QRHI_RES(QVkRenderBuffer, depthStencilBuffer)->vkformat;
const VkSampleCountFlagBits samples = depthTexture ? QRHI_RES(QVkTexture, depthTexture)->samples
@@ -1174,9 +1179,9 @@ bool QRhiVulkan::createOffscreenRenderPass(VkRenderPass *rp,
attDesc.stencilStoreOp = storeOp;
attDesc.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
attDesc.finalLayout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL;
- attDescs.append(attDesc);
+ rpD->attDescs.append(attDesc);
}
- VkAttachmentReference dsRef = { uint32_t(attDescs.count() - 1), VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL };
+ rpD->dsRef = { uint32_t(rpD->attDescs.count() - 1), VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL };
for (auto it = firstColorAttachment; it != lastColorAttachment; ++it) {
if (it->resolveTexture()) {
@@ -1194,37 +1199,37 @@ bool QRhiVulkan::createOffscreenRenderPass(VkRenderPass *rp,
attDesc.stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE;
attDesc.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
attDesc.finalLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
- attDescs.append(attDesc);
+ rpD->attDescs.append(attDesc);
- const VkAttachmentReference ref = { uint32_t(attDescs.count() - 1), VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL };
- resolveRefs.append(ref);
+ const VkAttachmentReference ref = { uint32_t(rpD->attDescs.count() - 1), VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL };
+ rpD->resolveRefs.append(ref);
} else {
const VkAttachmentReference ref = { VK_ATTACHMENT_UNUSED, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL };
- resolveRefs.append(ref);
+ rpD->resolveRefs.append(ref);
}
}
VkSubpassDescription subpassDesc;
memset(&subpassDesc, 0, sizeof(subpassDesc));
subpassDesc.pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS;
- subpassDesc.colorAttachmentCount = uint32_t(colorRefs.count());
- Q_ASSERT(colorRefs.count() == resolveRefs.count());
- subpassDesc.pColorAttachments = !colorRefs.isEmpty() ? colorRefs.constData() : nullptr;
- subpassDesc.pDepthStencilAttachment = hasDepthStencil ? &dsRef : nullptr;
- subpassDesc.pResolveAttachments = !resolveRefs.isEmpty() ? resolveRefs.constData() : nullptr;
+ subpassDesc.colorAttachmentCount = uint32_t(rpD->colorRefs.count());
+ Q_ASSERT(rpD->colorRefs.count() == rpD->resolveRefs.count());
+ subpassDesc.pColorAttachments = !rpD->colorRefs.isEmpty() ? rpD->colorRefs.constData() : nullptr;
+ subpassDesc.pDepthStencilAttachment = rpD->hasDepthStencil ? &rpD->dsRef : nullptr;
+ subpassDesc.pResolveAttachments = !rpD->resolveRefs.isEmpty() ? rpD->resolveRefs.constData() : nullptr;
VkRenderPassCreateInfo rpInfo;
memset(&rpInfo, 0, sizeof(rpInfo));
rpInfo.sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO;
- rpInfo.attachmentCount = uint32_t(attDescs.count());
- rpInfo.pAttachments = attDescs.constData();
+ rpInfo.attachmentCount = uint32_t(rpD->attDescs.count());
+ rpInfo.pAttachments = rpD->attDescs.constData();
rpInfo.subpassCount = 1;
rpInfo.pSubpasses = &subpassDesc;
// don't yet know the correct initial/final access and stage stuff for the
// implicit deps at this point, so leave it to the resource tracking to
// generate barriers
- VkResult err = df->vkCreateRenderPass(dev, &rpInfo, nullptr, rp);
+ VkResult err = df->vkCreateRenderPass(dev, &rpInfo, nullptr, &rpD->rp);
if (err != VK_SUCCESS) {
qWarning("Failed to create renderpass: %d", err);
return false;
@@ -1346,8 +1351,8 @@ bool QRhiVulkan::recreateSwapChain(QRhiSwapChain *swapChain)
quint32 actualSwapChainBufferCount = 0;
err = vkGetSwapchainImagesKHR(dev, swapChainD->sc, &actualSwapChainBufferCount, nullptr);
- if (err != VK_SUCCESS || actualSwapChainBufferCount < 2) {
- qWarning("Failed to get swapchain images: %d (count=%u)", err, actualSwapChainBufferCount);
+ if (err != VK_SUCCESS || actualSwapChainBufferCount == 0) {
+ qWarning("Failed to get swapchain images: %d", err);
return false;
}
@@ -1523,7 +1528,8 @@ void QRhiVulkan::releaseSwapChainResources(QRhiSwapChain *swapChain)
QRhi::FrameOpResult QRhiVulkan::beginFrame(QRhiSwapChain *swapChain, QRhi::BeginFrameFlags flags)
{
QVkSwapChain *swapChainD = QRHI_RES(QVkSwapChain, swapChain);
- QVkSwapChain::FrameResources &frame(swapChainD->frameRes[swapChainD->currentFrameSlot]);
+ const int frameResIndex = swapChainD->bufferCount > 1 ? swapChainD->currentFrameSlot : 0;
+ QVkSwapChain::FrameResources &frame(swapChainD->frameRes[frameResIndex]);
QRhiProfilerPrivate *rhiP = profilerPrivateOrNull();
if (!frame.imageAcquired) {
@@ -1566,7 +1572,7 @@ QRhi::FrameOpResult QRhiVulkan::beginFrame(QRhiSwapChain *swapChain, QRhi::Begin
// will make B wait for A's frame 0 commands, so if a resource is written
// in B's frame or when B checks for pending resource releases, that won't
// mess up A's in-flight commands (as they are not in flight anymore).
- waitCommandCompletion(int(swapChainD->currentFrameSlot));
+ waitCommandCompletion(frameResIndex);
// Now is the time to read the timestamps for the previous frame for this slot.
if (frame.timestampQueryIndex >= 0) {
@@ -1601,7 +1607,7 @@ QRhi::FrameOpResult QRhiVulkan::beginFrame(QRhiSwapChain *swapChain, QRhi::Begin
// when profiling is enabled, pick a free query (pair) from the pool
int timestampQueryIdx = -1;
- if (profilerPrivateOrNull()) {
+ if (profilerPrivateOrNull() && swapChainD->bufferCount > 1) { // no timestamps if not having at least 2 frames in flight
for (int i = 0; i < timestampQueryPoolMap.count(); ++i) {
if (!timestampQueryPoolMap.testBit(i)) {
timestampQueryPoolMap.setBit(i);
@@ -1643,7 +1649,8 @@ QRhi::FrameOpResult QRhiVulkan::endFrame(QRhiSwapChain *swapChain, QRhi::EndFram
recordPrimaryCommandBuffer(&swapChainD->cbWrapper);
- QVkSwapChain::FrameResources &frame(swapChainD->frameRes[swapChainD->currentFrameSlot]);
+ int frameResIndex = swapChainD->bufferCount > 1 ? swapChainD->currentFrameSlot : 0;
+ QVkSwapChain::FrameResources &frame(swapChainD->frameRes[frameResIndex]);
QVkSwapChain::ImageResources &image(swapChainD->imageRes[swapChainD->currentImageIndex]);
if (image.lastUse != QVkSwapChain::ImageResources::ScImageUseRender) {
@@ -1855,7 +1862,8 @@ QRhi::FrameOpResult QRhiVulkan::endAndSubmitPrimaryCommandBuffer(VkCommandBuffer
void QRhiVulkan::waitCommandCompletion(int frameSlot)
{
for (QVkSwapChain *sc : qAsConst(swapchains)) {
- QVkSwapChain::FrameResources &frame(sc->frameRes[frameSlot]);
+ const int frameResIndex = sc->bufferCount > 1 ? frameSlot : 0;
+ QVkSwapChain::FrameResources &frame(sc->frameRes[frameResIndex]);
if (frame.cmdFenceWaitable) {
df->vkWaitForFences(dev, 1, &frame.cmdFence, VK_TRUE, UINT64_MAX);
df->vkResetFences(dev, 1, &frame.cmdFence);
@@ -2214,6 +2222,8 @@ void QRhiVulkan::beginComputePass(QRhiCommandBuffer *cb, QRhiResourceUpdateBatch
cbD->recordingPass = QVkCommandBuffer::ComputePass;
+ cbD->computePassState.reset();
+
if (cbD->useSecondaryCb)
cbD->secondaryCbs.append(startSecondaryCommandBuffer());
}
@@ -2262,15 +2272,152 @@ void QRhiVulkan::setComputePipeline(QRhiCommandBuffer *cb, QRhiComputePipeline *
psD->lastActiveFrameSlot = currentFrameSlot;
}
+template<typename T>
+inline void qrhivk_accumulateComputeResource(T *writtenResources, QRhiResource *resource,
+ QRhiShaderResourceBinding::Type bindingType,
+ int loadTypeVal, int storeTypeVal, int loadStoreTypeVal)
+{
+ VkAccessFlags access = 0;
+ if (bindingType == loadTypeVal) {
+ access = VK_ACCESS_SHADER_READ_BIT;
+ } else {
+ access = VK_ACCESS_SHADER_WRITE_BIT;
+ if (bindingType == loadStoreTypeVal)
+ access |= VK_ACCESS_SHADER_READ_BIT;
+ }
+ auto it = writtenResources->find(resource);
+ if (it != writtenResources->end())
+ it->first |= access;
+ else if (bindingType == storeTypeVal || bindingType == loadStoreTypeVal)
+ writtenResources->insert(resource, { access, true });
+}
+
void QRhiVulkan::dispatch(QRhiCommandBuffer *cb, int x, int y, int z)
{
QVkCommandBuffer *cbD = QRHI_RES(QVkCommandBuffer, cb);
Q_ASSERT(cbD->recordingPass == QVkCommandBuffer::ComputePass);
+ // When there are multiple dispatches, read-after-write and
+ // write-after-write need a barrier.
+ QVarLengthArray<VkImageMemoryBarrier, 8> imageBarriers;
+ QVarLengthArray<VkBufferMemoryBarrier, 8> bufferBarriers;
+ if (cbD->currentComputeSrb) {
+ // The key in the writtenResources map indicates that the resource was
+ // written in a previous dispatch, whereas the value accumulates the
+ // access mask in the current one.
+ for (auto &accessAndIsNewFlag : cbD->computePassState.writtenResources)
+ accessAndIsNewFlag = { 0, false };
+
+ QVkShaderResourceBindings *srbD = QRHI_RES(QVkShaderResourceBindings, cbD->currentComputeSrb);
+ const int bindingCount = srbD->m_bindings.count();
+ for (int i = 0; i < bindingCount; ++i) {
+ const QRhiShaderResourceBinding::Data *b = srbD->m_bindings.at(i).data();
+ switch (b->type) {
+ case QRhiShaderResourceBinding::ImageLoad:
+ case QRhiShaderResourceBinding::ImageStore:
+ case QRhiShaderResourceBinding::ImageLoadStore:
+ qrhivk_accumulateComputeResource(&cbD->computePassState.writtenResources,
+ b->u.simage.tex,
+ b->type,
+ QRhiShaderResourceBinding::ImageLoad,
+ QRhiShaderResourceBinding::ImageStore,
+ QRhiShaderResourceBinding::ImageLoadStore);
+ break;
+ case QRhiShaderResourceBinding::BufferLoad:
+ case QRhiShaderResourceBinding::BufferStore:
+ case QRhiShaderResourceBinding::BufferLoadStore:
+ qrhivk_accumulateComputeResource(&cbD->computePassState.writtenResources,
+ b->u.sbuf.buf,
+ b->type,
+ QRhiShaderResourceBinding::BufferLoad,
+ QRhiShaderResourceBinding::BufferStore,
+ QRhiShaderResourceBinding::BufferLoadStore);
+ break;
+ default:
+ break;
+ }
+ }
+
+ for (auto it = cbD->computePassState.writtenResources.begin(); it != cbD->computePassState.writtenResources.end(); ) {
+ const int accessInThisDispatch = it->first;
+ const bool isNewInThisDispatch = it->second;
+ if (accessInThisDispatch && !isNewInThisDispatch) {
+ if (it.key()->resourceType() == QRhiResource::Texture) {
+ QVkTexture *texD = QRHI_RES(QVkTexture, it.key());
+ VkImageMemoryBarrier barrier;
+ memset(&barrier, 0, sizeof(barrier));
+ barrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER;
+ barrier.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
+ // won't care about subresources, pretend the whole resource was written
+ barrier.subresourceRange.baseMipLevel = 0;
+ barrier.subresourceRange.levelCount = VK_REMAINING_MIP_LEVELS;
+ barrier.subresourceRange.baseArrayLayer = 0;
+ barrier.subresourceRange.layerCount = VK_REMAINING_ARRAY_LAYERS;
+ barrier.oldLayout = texD->usageState.layout;
+ barrier.newLayout = texD->usageState.layout;
+ barrier.srcAccessMask = VK_ACCESS_SHADER_WRITE_BIT;
+ barrier.dstAccessMask = accessInThisDispatch;
+ barrier.image = texD->image;
+ imageBarriers.append(barrier);
+ } else {
+ QVkBuffer *bufD = QRHI_RES(QVkBuffer, it.key());
+ VkBufferMemoryBarrier barrier;
+ memset(&barrier, 0, sizeof(barrier));
+ barrier.sType = VK_STRUCTURE_TYPE_BUFFER_MEMORY_BARRIER;
+ barrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
+ barrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
+ barrier.srcAccessMask = VK_ACCESS_SHADER_WRITE_BIT;
+ barrier.dstAccessMask = accessInThisDispatch;
+ barrier.buffer = bufD->buffers[bufD->m_type == QRhiBuffer::Dynamic ? currentFrameSlot : 0];
+ barrier.size = VK_WHOLE_SIZE;
+ bufferBarriers.append(barrier);
+ }
+ }
+ // Anything that was previously written, but is only read now, can be
+ // removed from the written list (because that previous write got a
+ // corresponding barrier now).
+ if (accessInThisDispatch == VK_ACCESS_SHADER_READ_BIT)
+ it = cbD->computePassState.writtenResources.erase(it);
+ else
+ ++it;
+ }
+ }
+
if (cbD->useSecondaryCb) {
- df->vkCmdDispatch(cbD->secondaryCbs.last(), uint32_t(x), uint32_t(y), uint32_t(z));
+ VkCommandBuffer secondaryCb = cbD->secondaryCbs.last();
+ if (!imageBarriers.isEmpty()) {
+ df->vkCmdPipelineBarrier(secondaryCb, VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT,
+ 0, 0, nullptr,
+ 0, nullptr,
+ imageBarriers.count(), imageBarriers.constData());
+ }
+ if (!bufferBarriers.isEmpty()) {
+ df->vkCmdPipelineBarrier(secondaryCb, VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT,
+ 0, 0, nullptr,
+ bufferBarriers.count(), bufferBarriers.constData(),
+ 0, nullptr);
+ }
+ df->vkCmdDispatch(secondaryCb, uint32_t(x), uint32_t(y), uint32_t(z));
} else {
QVkCommandBuffer::Command cmd;
+ if (!imageBarriers.isEmpty()) {
+ cmd.cmd = QVkCommandBuffer::Command::ImageBarrier;
+ cmd.args.imageBarrier.srcStageMask = VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT;
+ cmd.args.imageBarrier.dstStageMask = VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT;
+ cmd.args.imageBarrier.count = imageBarriers.count();
+ cmd.args.imageBarrier.index = cbD->pools.imageBarrier.count();
+ cbD->pools.imageBarrier.append(imageBarriers.constData(), imageBarriers.count());
+ cbD->commands.append(cmd);
+ }
+ if (!bufferBarriers.isEmpty()) {
+ cmd.cmd = QVkCommandBuffer::Command::BufferBarrier;
+ cmd.args.bufferBarrier.srcStageMask = VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT;
+ cmd.args.bufferBarrier.dstStageMask = VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT;
+ cmd.args.bufferBarrier.count = bufferBarriers.count();
+ cmd.args.bufferBarrier.index = cbD->pools.bufferBarrier.count();
+ cbD->pools.bufferBarrier.append(bufferBarriers.constData(), bufferBarriers.count());
+ cbD->commands.append(cmd);
+ }
cmd.cmd = QVkCommandBuffer::Command::Dispatch;
cmd.args.dispatch.x = x;
cmd.args.dispatch.y = y;
@@ -2371,9 +2518,7 @@ void QRhiVulkan::updateShaderResourceBindings(QRhiShaderResourceBindings *srb, i
}
break;
case QRhiShaderResourceBinding::ImageLoad:
- Q_FALLTHROUGH();
case QRhiShaderResourceBinding::ImageStore:
- Q_FALLTHROUGH();
case QRhiShaderResourceBinding::ImageLoadStore:
{
QVkTexture *texD = QRHI_RES(QVkTexture, b->u.simage.tex);
@@ -2392,9 +2537,7 @@ void QRhiVulkan::updateShaderResourceBindings(QRhiShaderResourceBindings *srb, i
}
break;
case QRhiShaderResourceBinding::BufferLoad:
- Q_FALLTHROUGH();
case QRhiShaderResourceBinding::BufferStore:
- Q_FALLTHROUGH();
case QRhiShaderResourceBinding::BufferLoadStore:
{
QVkBuffer *bufD = QRHI_RES(QVkBuffer, b->u.sbuf.buf);
@@ -2464,7 +2607,9 @@ void QRhiVulkan::trackedBufferBarrier(QVkCommandBuffer *cbD, QVkBuffer *bufD, in
cmd.cmd = QVkCommandBuffer::Command::BufferBarrier;
cmd.args.bufferBarrier.srcStageMask = s.stage;
cmd.args.bufferBarrier.dstStageMask = stage;
- cmd.args.bufferBarrier.desc = bufMemBarrier;
+ cmd.args.bufferBarrier.count = 1;
+ cmd.args.bufferBarrier.index = cbD->pools.bufferBarrier.count();
+ cbD->pools.bufferBarrier.append(bufMemBarrier);
cbD->commands.append(cmd);
s.access = access;
@@ -2506,7 +2651,9 @@ void QRhiVulkan::trackedImageBarrier(QVkCommandBuffer *cbD, QVkTexture *texD,
cmd.cmd = QVkCommandBuffer::Command::ImageBarrier;
cmd.args.imageBarrier.srcStageMask = srcStage;
cmd.args.imageBarrier.dstStageMask = stage;
- cmd.args.imageBarrier.desc = barrier;
+ cmd.args.imageBarrier.count = 1;
+ cmd.args.imageBarrier.index = cbD->pools.imageBarrier.count();
+ cbD->pools.imageBarrier.append(barrier);
cbD->commands.append(cmd);
s.layout = layout;
@@ -2540,7 +2687,9 @@ void QRhiVulkan::subresourceBarrier(QVkCommandBuffer *cbD, VkImage image,
cmd.cmd = QVkCommandBuffer::Command::ImageBarrier;
cmd.args.imageBarrier.srcStageMask = srcStage;
cmd.args.imageBarrier.dstStageMask = dstStage;
- cmd.args.imageBarrier.desc = barrier;
+ cmd.args.imageBarrier.count = 1;
+ cmd.args.imageBarrier.index = cbD->pools.imageBarrier.count();
+ cbD->pools.imageBarrier.append(barrier);
cbD->commands.append(cmd);
}
@@ -3408,12 +3557,12 @@ void QRhiVulkan::recordPrimaryCommandBuffer(QVkCommandBuffer *cbD)
case QVkCommandBuffer::Command::ImageBarrier:
df->vkCmdPipelineBarrier(cbD->cb, cmd.args.imageBarrier.srcStageMask, cmd.args.imageBarrier.dstStageMask,
0, 0, nullptr, 0, nullptr,
- 1, &cmd.args.imageBarrier.desc);
+ cmd.args.imageBarrier.count, cbD->pools.imageBarrier.constData() + cmd.args.imageBarrier.index);
break;
case QVkCommandBuffer::Command::BufferBarrier:
df->vkCmdPipelineBarrier(cbD->cb, cmd.args.bufferBarrier.srcStageMask, cmd.args.bufferBarrier.dstStageMask,
0, 0, nullptr,
- 1, &cmd.args.bufferBarrier.desc,
+ cmd.args.bufferBarrier.count, cbD->pools.bufferBarrier.constData() + cmd.args.bufferBarrier.index,
0, nullptr);
break;
case QVkCommandBuffer::Command::BlitImage:
@@ -3562,9 +3711,7 @@ static inline VkImageLayout toVkLayout(QRhiPassResourceTracker::TextureAccess ac
case QRhiPassResourceTracker::TexDepthOutput:
return VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL;
case QRhiPassResourceTracker::TexStorageLoad:
- Q_FALLTHROUGH();
case QRhiPassResourceTracker::TexStorageStore:
- Q_FALLTHROUGH();
case QRhiPassResourceTracker::TexStorageLoadStore:
return VK_IMAGE_LAYOUT_GENERAL;
default:
@@ -4043,9 +4190,7 @@ void QRhiVulkan::setShaderResources(QRhiCommandBuffer *cb, QRhiShaderResourceBin
}
break;
case QRhiShaderResourceBinding::ImageLoad:
- Q_FALLTHROUGH();
case QRhiShaderResourceBinding::ImageStore:
- Q_FALLTHROUGH();
case QRhiShaderResourceBinding::ImageLoadStore:
{
QVkTexture *texD = QRHI_RES(QVkTexture, b->u.simage.tex);
@@ -4070,9 +4215,7 @@ void QRhiVulkan::setShaderResources(QRhiCommandBuffer *cb, QRhiShaderResourceBin
}
break;
case QRhiShaderResourceBinding::BufferLoad:
- Q_FALLTHROUGH();
case QRhiShaderResourceBinding::BufferStore:
- Q_FALLTHROUGH();
case QRhiShaderResourceBinding::BufferLoadStore:
{
QVkBuffer *bufD = QRHI_RES(QVkBuffer, b->u.sbuf.buf);
@@ -4617,12 +4760,8 @@ static inline VkSamplerAddressMode toVkAddressMode(QRhiSampler::AddressMode m)
return VK_SAMPLER_ADDRESS_MODE_REPEAT;
case QRhiSampler::ClampToEdge:
return VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE;
- case QRhiSampler::Border:
- return VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_BORDER;
case QRhiSampler::Mirror:
return VK_SAMPLER_ADDRESS_MODE_MIRRORED_REPEAT;
- case QRhiSampler::MirrorOnce:
- return VK_SAMPLER_ADDRESS_MODE_MIRROR_CLAMP_TO_EDGE;
default:
Q_UNREACHABLE();
return VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE;
@@ -4865,16 +5004,12 @@ static inline VkDescriptorType toVkDescriptorType(const QRhiShaderResourceBindin
return VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
case QRhiShaderResourceBinding::ImageLoad:
- Q_FALLTHROUGH();
case QRhiShaderResourceBinding::ImageStore:
- Q_FALLTHROUGH();
case QRhiShaderResourceBinding::ImageLoadStore:
return VK_DESCRIPTOR_TYPE_STORAGE_IMAGE;
case QRhiShaderResourceBinding::BufferLoad:
- Q_FALLTHROUGH();
case QRhiShaderResourceBinding::BufferStore:
- Q_FALLTHROUGH();
case QRhiShaderResourceBinding::BufferLoadStore:
return VK_DESCRIPTOR_TYPE_STORAGE_BUFFER;
@@ -5185,7 +5320,6 @@ void QVkTexture::release()
image = VK_NULL_HANDLE;
imageView = VK_NULL_HANDLE;
imageAlloc = nullptr;
- nativeHandlesStruct.image = VK_NULL_HANDLE;
QRHI_RES_RHI(QRhiVulkan);
rhiD->releaseQueue.append(e);
@@ -5270,8 +5404,6 @@ bool QVkTexture::finishBuild()
return false;
}
- nativeHandlesStruct.image = image;
-
lastActiveFrameSlot = -1;
generation += 1;
@@ -5343,16 +5475,16 @@ bool QVkTexture::build()
return true;
}
-bool QVkTexture::buildFrom(const QRhiNativeHandles *src)
+bool QVkTexture::buildFrom(QRhiTexture::NativeTexture src)
{
- const QRhiVulkanTextureNativeHandles *h = static_cast<const QRhiVulkanTextureNativeHandles *>(src);
- if (!h || !h->image)
+ auto *img = static_cast<const VkImage*>(src.object);
+ if (!img || !*img)
return false;
if (!prepareBuild())
return false;
- image = h->image;
+ image = *img;
if (!finishBuild())
return false;
@@ -5360,7 +5492,7 @@ bool QVkTexture::buildFrom(const QRhiNativeHandles *src)
QRHI_PROF;
QRHI_PROF_F(newTexture(this, false, int(mipLevelCount), m_flags.testFlag(CubeMap) ? 6 : 1, samples));
- usageState.layout = h->layout;
+ usageState.layout = VkImageLayout(src.layout);
owns = false;
QRHI_RES_RHI(QRhiVulkan);
@@ -5368,10 +5500,9 @@ bool QVkTexture::buildFrom(const QRhiNativeHandles *src)
return true;
}
-const QRhiNativeHandles *QVkTexture::nativeHandles()
+QRhiTexture::NativeTexture QVkTexture::nativeTexture()
{
- nativeHandlesStruct.layout = usageState.layout;
- return &nativeHandlesStruct;
+ return {&image, usageState.layout};
}
VkImageView QVkTexture::imageViewForLevel(int level)
@@ -5505,6 +5636,61 @@ void QVkRenderPassDescriptor::release()
rhiD->unregisterResource(this);
}
+static inline bool attachmentDescriptionEquals(const VkAttachmentDescription &a, const VkAttachmentDescription &b)
+{
+ return a.format == b.format
+ && a.samples == b.samples
+ && a.loadOp == b.loadOp
+ && a.storeOp == b.storeOp
+ && a.stencilLoadOp == b.stencilLoadOp
+ && a.stencilStoreOp == b.stencilStoreOp
+ && a.initialLayout == b.initialLayout
+ && a.finalLayout == b.finalLayout;
+}
+
+bool QVkRenderPassDescriptor::isCompatible(const QRhiRenderPassDescriptor *other) const
+{
+ if (!other)
+ return false;
+
+ const QVkRenderPassDescriptor *o = QRHI_RES(const QVkRenderPassDescriptor, other);
+
+ if (attDescs.count() != o->attDescs.count())
+ return false;
+ if (colorRefs.count() != o->colorRefs.count())
+ return false;
+ if (resolveRefs.count() != o->resolveRefs.count())
+ return false;
+ if (hasDepthStencil != o->hasDepthStencil)
+ return false;
+
+ for (int i = 0, ie = colorRefs.count(); i != ie; ++i) {
+ const uint32_t attIdx = colorRefs[i].attachment;
+ if (attIdx != o->colorRefs[i].attachment)
+ return false;
+ if (attIdx != VK_ATTACHMENT_UNUSED && !attachmentDescriptionEquals(attDescs[attIdx], o->attDescs[attIdx]))
+ return false;
+ }
+
+ if (hasDepthStencil) {
+ const uint32_t attIdx = dsRef.attachment;
+ if (attIdx != o->dsRef.attachment)
+ return false;
+ if (attIdx != VK_ATTACHMENT_UNUSED && !attachmentDescriptionEquals(attDescs[attIdx], o->attDescs[attIdx]))
+ return false;
+ }
+
+ for (int i = 0, ie = resolveRefs.count(); i != ie; ++i) {
+ const uint32_t attIdx = resolveRefs[i].attachment;
+ if (attIdx != o->resolveRefs[i].attachment)
+ return false;
+ if (attIdx != VK_ATTACHMENT_UNUSED && !attachmentDescriptionEquals(attDescs[attIdx], o->attDescs[attIdx]))
+ return false;
+ }
+
+ return true;
+}
+
const QRhiNativeHandles *QVkRenderPassDescriptor::nativeHandles()
{
nativeHandlesStruct.renderPass = rp;
@@ -5588,7 +5774,7 @@ QRhiRenderPassDescriptor *QVkTextureRenderTarget::newCompatibleRenderPassDescrip
QRHI_RES_RHI(QRhiVulkan);
QVkRenderPassDescriptor *rp = new QVkRenderPassDescriptor(m_rhi);
- if (!rhiD->createOffscreenRenderPass(&rp->rp,
+ if (!rhiD->createOffscreenRenderPass(rp,
m_desc.cbeginColorAttachments(),
m_desc.cendColorAttachments(),
m_flags.testFlag(QRhiTextureRenderTarget::PreserveColorContents),
@@ -6289,7 +6475,7 @@ QRhiRenderPassDescriptor *QVkSwapChain::newCompatibleRenderPassDescriptor()
QRHI_RES_RHI(QRhiVulkan);
QVkRenderPassDescriptor *rp = new QVkRenderPassDescriptor(m_rhi);
- if (!rhiD->createDefaultRenderPass(&rp->rp,
+ if (!rhiD->createDefaultRenderPass(rp,
m_depthStencil != nullptr,
samples,
colorFormat))
@@ -6307,17 +6493,11 @@ static inline bool isSrgbFormat(VkFormat format)
{
switch (format) {
case VK_FORMAT_R8_SRGB:
- Q_FALLTHROUGH();
case VK_FORMAT_R8G8_SRGB:
- Q_FALLTHROUGH();
case VK_FORMAT_R8G8B8_SRGB:
- Q_FALLTHROUGH();
case VK_FORMAT_B8G8R8_SRGB:
- Q_FALLTHROUGH();
case VK_FORMAT_R8G8B8A8_SRGB:
- Q_FALLTHROUGH();
case VK_FORMAT_B8G8R8A8_SRGB:
- Q_FALLTHROUGH();
case VK_FORMAT_A8B8G8R8_SRGB_PACK32:
return true;
default:
diff --git a/src/gui/rhi/qrhivulkan_p.h b/src/gui/rhi/qrhivulkan_p.h
index ff19c7a54e..d495919671 100644
--- a/src/gui/rhi/qrhivulkan_p.h
+++ b/src/gui/rhi/qrhivulkan_p.h
@@ -69,12 +69,6 @@ struct Q_GUI_EXPORT QRhiVulkanNativeHandles : public QRhiNativeHandles
void *vmemAllocator = nullptr;
};
-struct Q_GUI_EXPORT QRhiVulkanTextureNativeHandles : public QRhiNativeHandles
-{
- VkImage image = VK_NULL_HANDLE;
- VkImageLayout layout = VK_IMAGE_LAYOUT_GENERAL;
-};
-
struct Q_GUI_EXPORT QRhiVulkanCommandBufferNativeHandles : public QRhiNativeHandles
{
VkCommandBuffer commandBuffer = VK_NULL_HANDLE;
diff --git a/src/gui/rhi/qrhivulkan_p_p.h b/src/gui/rhi/qrhivulkan_p_p.h
index d0e1e6758b..b0e90dae56 100644
--- a/src/gui/rhi/qrhivulkan_p_p.h
+++ b/src/gui/rhi/qrhivulkan_p_p.h
@@ -120,8 +120,8 @@ struct QVkTexture : public QRhiTexture
~QVkTexture();
void release() override;
bool build() override;
- bool buildFrom(const QRhiNativeHandles *src) override;
- const QRhiNativeHandles *nativeHandles() override;
+ bool buildFrom(NativeTexture src) override;
+ NativeTexture nativeTexture() override;
bool prepareBuild(QSize *adjustedSize = nullptr);
bool finishBuild();
@@ -134,7 +134,6 @@ struct QVkTexture : public QRhiTexture
QVkAlloc stagingAllocations[QVK_FRAMES_IN_FLIGHT];
VkImageView perLevelImageViews[QRhi::MAX_LEVELS];
bool owns = true;
- QRhiVulkanTextureNativeHandles nativeHandlesStruct;
struct UsageState {
// no tracking of subresource layouts (some operations can keep
// subresources in different layouts for some time, but that does not
@@ -171,10 +170,16 @@ struct QVkRenderPassDescriptor : public QRhiRenderPassDescriptor
QVkRenderPassDescriptor(QRhiImplementation *rhi);
~QVkRenderPassDescriptor();
void release() override;
+ bool isCompatible(const QRhiRenderPassDescriptor *other) const override;
const QRhiNativeHandles *nativeHandles() override;
VkRenderPass rp = VK_NULL_HANDLE;
bool ownsRp = false;
+ QVarLengthArray<VkAttachmentDescription, 8> attDescs;
+ QVarLengthArray<VkAttachmentReference, 8> colorRefs;
+ QVarLengthArray<VkAttachmentReference, 8> resolveRefs;
+ bool hasDepthStencil = false;
+ VkAttachmentReference dsRef;
QRhiVulkanRenderPassNativeHandles nativeHandlesStruct;
int lastActiveFrameSlot = -1;
};
@@ -365,6 +370,13 @@ struct QVkCommandBuffer : public QRhiCommandBuffer
QVarLengthArray<VkCommandBuffer, 4> secondaryCbs;
bool inExternal;
+ struct {
+ QHash<QRhiResource *, QPair<VkAccessFlags, bool> > writtenResources;
+ void reset() {
+ writtenResources.clear();
+ }
+ } computePassState;
+
struct Command {
enum Cmd {
CopyBuffer,
@@ -424,12 +436,14 @@ struct QVkCommandBuffer : public QRhiCommandBuffer
struct {
VkPipelineStageFlags srcStageMask;
VkPipelineStageFlags dstStageMask;
- VkImageMemoryBarrier desc;
+ int count;
+ int index;
} imageBarrier;
struct {
VkPipelineStageFlags srcStageMask;
VkPipelineStageFlags dstStageMask;
- VkBufferMemoryBarrier desc;
+ int count;
+ int index;
} bufferBarrier;
struct {
VkImage src;
@@ -532,6 +546,8 @@ struct QVkCommandBuffer : public QRhiCommandBuffer
pools.vertexBuffer.clear();
pools.vertexBufferOffset.clear();
pools.debugMarkerData.clear();
+ pools.imageBarrier.clear();
+ pools.bufferBarrier.clear();
}
struct {
@@ -541,6 +557,8 @@ struct QVkCommandBuffer : public QRhiCommandBuffer
QVarLengthArray<VkBuffer, 4> vertexBuffer;
QVarLengthArray<VkDeviceSize, 4> vertexBufferOffset;
QVarLengthArray<QByteArray, 4> debugMarkerData;
+ QVarLengthArray<VkImageMemoryBarrier, 8> imageBarrier;
+ QVarLengthArray<VkBufferMemoryBarrier, 8> bufferBarrier;
} pools;
friend class QRhiVulkan;
@@ -727,11 +745,11 @@ public:
VkFormat optimalDepthStencilFormat();
VkSampleCountFlagBits effectiveSampleCount(int sampleCount);
- bool createDefaultRenderPass(VkRenderPass *rp,
+ bool createDefaultRenderPass(QVkRenderPassDescriptor *rpD,
bool hasDepthStencil,
VkSampleCountFlagBits samples,
VkFormat colorFormat);
- bool createOffscreenRenderPass(VkRenderPass *rp,
+ bool createOffscreenRenderPass(QVkRenderPassDescriptor *rpD,
const QRhiColorAttachment *firstColorAttachment,
const QRhiColorAttachment *lastColorAttachment,
bool preserveColor,
diff --git a/src/gui/rhi/qshader.cpp b/src/gui/rhi/qshader.cpp
index 6a2c596557..69f4a68215 100644
--- a/src/gui/rhi/qshader.cpp
+++ b/src/gui/rhi/qshader.cpp
@@ -214,8 +214,6 @@ QT_BEGIN_NAMESPACE
QShader, it indicates no shader code was found for the requested key.
*/
-static const int QSB_VERSION = 1;
-
/*!
Constructs a new, empty (and thus invalid) QShader instance.
*/
@@ -345,6 +343,14 @@ void QShader::removeShader(const QShaderKey &key)
d->shaders.erase(it);
}
+static void writeShaderKey(QDataStream *ds, const QShaderKey &k)
+{
+ *ds << int(k.source());
+ *ds << k.sourceVersion().version();
+ *ds << k.sourceVersion().flags();
+ *ds << int(k.sourceVariant());
+}
+
/*!
\return a serialized binary version of all the data held by the
QShader, suitable for writing to files or other I/O devices.
@@ -359,24 +365,48 @@ QByteArray QShader::serialized() const
if (!buf.open(QIODevice::WriteOnly))
return QByteArray();
- ds << QSB_VERSION;
- ds << d->stage;
- ds << d->desc.toBinaryJson();
+ ds << QShaderPrivate::QSB_VERSION;
+ ds << int(d->stage);
+ d->desc.serialize(&ds);
ds << d->shaders.count();
for (auto it = d->shaders.cbegin(), itEnd = d->shaders.cend(); it != itEnd; ++it) {
const QShaderKey &k(it.key());
- ds << k.source();
- ds << k.sourceVersion().version();
- ds << k.sourceVersion().flags();
- ds << k.sourceVariant();
+ writeShaderKey(&ds, k);
const QShaderCode &shader(d->shaders.value(k));
ds << shader.shader();
ds << shader.entryPoint();
}
+ ds << d->bindings.count();
+ for (auto it = d->bindings.cbegin(), itEnd = d->bindings.cend(); it != itEnd; ++it) {
+ const QShaderKey &k(it.key());
+ writeShaderKey(&ds, k);
+ const NativeResourceBindingMap &map(it.value());
+ ds << map.count();
+ for (auto mapIt = map.cbegin(), mapItEnd = map.cend(); mapIt != mapItEnd; ++mapIt) {
+ ds << mapIt.key();
+ ds << mapIt.value().first;
+ ds << mapIt.value().second;
+ }
+ }
return qCompress(buf.buffer());
}
+static void readShaderKey(QDataStream *ds, QShaderKey *k)
+{
+ int intVal;
+ *ds >> intVal;
+ k->setSource(QShader::Source(intVal));
+ QShaderVersion ver;
+ *ds >> intVal;
+ ver.setVersion(intVal);
+ *ds >> intVal;
+ ver.setFlags(QShaderVersion::Flags(intVal));
+ k->setSourceVersion(ver);
+ *ds >> intVal;
+ k->setSourceVariant(QShader::Variant(intVal));
+}
+
/*!
Creates a new QShader instance from the given \a data.
@@ -396,28 +426,42 @@ QShader QShader::fromSerialized(const QByteArray &data)
Q_ASSERT(d->ref.loadRelaxed() == 1); // must be detached
int intVal;
ds >> intVal;
- if (intVal != QSB_VERSION)
+ d->qsbVersion = intVal;
+ if (d->qsbVersion != QShaderPrivate::QSB_VERSION
+ && d->qsbVersion != QShaderPrivate::QSB_VERSION_WITH_CBOR
+ && d->qsbVersion != QShaderPrivate::QSB_VERSION_WITH_BINARY_JSON
+ && d->qsbVersion != QShaderPrivate::QSB_VERSION_WITHOUT_BINDINGS)
+ {
+ qWarning("Attempted to deserialize QShader with unknown version %d.", d->qsbVersion);
return QShader();
+ }
ds >> intVal;
d->stage = Stage(intVal);
- QByteArray descBin;
- ds >> descBin;
- d->desc = QShaderDescription::fromBinaryJson(descBin);
+ if (d->qsbVersion > QShaderPrivate::QSB_VERSION_WITH_CBOR) {
+ d->desc = QShaderDescription::deserialize(&ds);
+ } else if (d->qsbVersion > QShaderPrivate::QSB_VERSION_WITH_BINARY_JSON) {
+ QByteArray descBin;
+ ds >> descBin;
+ d->desc = QShaderDescription::fromCbor(descBin);
+ } else {
+#if QT_CONFIG(binaryjson) && QT_DEPRECATED_SINCE(5, 15)
+ QT_WARNING_PUSH
+ QT_WARNING_DISABLE_DEPRECATED
+ QByteArray descBin;
+ ds >> descBin;
+ d->desc = QShaderDescription::fromBinaryJson(descBin);
+ QT_WARNING_POP
+#else
+ qWarning("Cannot load QShaderDescription from binary JSON due to disabled binaryjson feature");
+ d->desc = QShaderDescription();
+#endif
+ }
int count;
ds >> count;
for (int i = 0; i < count; ++i) {
QShaderKey k;
- ds >> intVal;
- k.setSource(Source(intVal));
- QShaderVersion ver;
- ds >> intVal;
- ver.setVersion(intVal);
- ds >> intVal;
- ver.setFlags(QShaderVersion::Flags(intVal));
- k.setSourceVersion(ver);
- ds >> intVal;
- k.setSourceVariant(Variant(intVal));
+ readShaderKey(&ds, &k);
QShaderCode shader;
QByteArray s;
ds >> s;
@@ -427,6 +471,27 @@ QShader QShader::fromSerialized(const QByteArray &data)
d->shaders[k] = shader;
}
+ if (d->qsbVersion > QShaderPrivate::QSB_VERSION_WITHOUT_BINDINGS) {
+ ds >> count;
+ for (int i = 0; i < count; ++i) {
+ QShaderKey k;
+ readShaderKey(&ds, &k);
+ NativeResourceBindingMap map;
+ int mapSize;
+ ds >> mapSize;
+ for (int b = 0; b < mapSize; ++b) {
+ int binding;
+ ds >> binding;
+ int firstNativeBinding;
+ ds >> firstNativeBinding;
+ int secondNativeBinding;
+ ds >> secondNativeBinding;
+ map.insert(binding, { firstNativeBinding, secondNativeBinding });
+ }
+ d->bindings.insert(k, map);
+ }
+ }
+
return bs;
}
@@ -460,7 +525,7 @@ bool operator==(const QShader &lhs, const QShader &rhs) Q_DECL_NOTHROW
{
return lhs.d->stage == rhs.d->stage
&& lhs.d->shaders == rhs.d->shaders;
- // do not bother with desc, if the shader code is the same, the description must match too
+ // do not bother with desc and bindings, if the shader code is the same, the description must match too
}
/*!
@@ -586,4 +651,73 @@ QDebug operator<<(QDebug dbg, const QShaderVersion &v)
}
#endif // QT_NO_DEBUG_STREAM
+/*!
+ \typedef QShader::NativeResourceBindingMap
+
+ Synonym for QHash<int, QPair<int, int>>.
+
+ The resource binding model QRhi assumes is based on SPIR-V. This means that
+ uniform buffers, storage buffers, combined image samplers, and storage
+ images share a common binding point space. The binding numbers in
+ QShaderDescription and QRhiShaderResourceBinding are expected to match the
+ \c binding layout qualifier in the Vulkan-compatible GLSL shader.
+
+ Graphics APIs other than Vulkan may use a resource binding model that is
+ not fully compatible with this. In addition, the generator of the shader
+ code translated from SPIR-V may choose not to take the SPIR-V binding
+ qualifiers into account, for various reasons. (this is the case with the
+ Metal backend of SPIRV-Cross, for example).
+
+ Therefore, a QShader may expose an additional map that describes what the
+ native binding point for a given SPIR-V binding is. The QRhi backends are
+ expected to use this map automatically, as appropriate. The value is a
+ pair, because combined image samplers may map to two native resources (a
+ texture and a sampler) in some shading languages. In that case the second
+ value refers to the sampler.
+
+ \note The native binding may be -1, in case there is no active binding for
+ the resource in the shader. (for example, there is a uniform block
+ declared, but it is not used in the shader code) The map is always
+ complete, meaning there is an entry for all declared uniform blocks,
+ storage blocks, image objects, and combined samplers, but the value will be
+ -1 for those that are not actually referenced in the shader functions.
+*/
+
+/*!
+ \return the native binding map for \a key or null if no extra mapping is
+ available, or is not applicable.
+ */
+const QShader::NativeResourceBindingMap *QShader::nativeResourceBindingMap(const QShaderKey &key) const
+{
+ auto it = d->bindings.constFind(key);
+ if (it == d->bindings.cend())
+ return nullptr;
+
+ return &it.value();
+}
+
+/*!
+ Stores the given native resource binding \a map associated with \a key.
+
+ \sa nativeResourceBindingMap()
+ */
+void QShader::setResourceBindingMap(const QShaderKey &key, const NativeResourceBindingMap &map)
+{
+ detach();
+ d->bindings[key] = map;
+}
+
+/*!
+ Removes the native resource binding map for \a key.
+ */
+void QShader::removeResourceBindingMap(const QShaderKey &key)
+{
+ auto it = d->bindings.find(key);
+ if (it == d->bindings.end())
+ return;
+
+ detach();
+ d->bindings.erase(it);
+}
+
QT_END_NAMESPACE
diff --git a/src/gui/rhi/qshader_p.h b/src/gui/rhi/qshader_p.h
index 243842a95a..4b561b6fa9 100644
--- a/src/gui/rhi/qshader_p.h
+++ b/src/gui/rhi/qshader_p.h
@@ -149,6 +149,11 @@ public:
QByteArray serialized() const;
static QShader fromSerialized(const QByteArray &data);
+ using NativeResourceBindingMap = QHash<int, QPair<int, int> >; // binding -> native_binding[, native_binding]
+ const NativeResourceBindingMap *nativeResourceBindingMap(const QShaderKey &key) const;
+ void setResourceBindingMap(const QShaderKey &key, const NativeResourceBindingMap &map);
+ void removeResourceBindingMap(const QShaderKey &key);
+
private:
QShaderPrivate *d;
friend struct QShaderPrivate;
diff --git a/src/gui/rhi/qshader_p_p.h b/src/gui/rhi/qshader_p_p.h
index 6473590e95..66ef18f391 100644
--- a/src/gui/rhi/qshader_p_p.h
+++ b/src/gui/rhi/qshader_p_p.h
@@ -57,6 +57,11 @@ QT_BEGIN_NAMESPACE
struct Q_GUI_EXPORT QShaderPrivate
{
+ static const int QSB_VERSION = 4;
+ static const int QSB_VERSION_WITH_CBOR = 3;
+ static const int QSB_VERSION_WITH_BINARY_JSON = 2;
+ static const int QSB_VERSION_WITHOUT_BINDINGS = 1;
+
QShaderPrivate()
: ref(1)
{
@@ -64,9 +69,11 @@ struct Q_GUI_EXPORT QShaderPrivate
QShaderPrivate(const QShaderPrivate *other)
: ref(1),
+ qsbVersion(other->qsbVersion),
stage(other->stage),
desc(other->desc),
- shaders(other->shaders)
+ shaders(other->shaders),
+ bindings(other->bindings)
{
}
@@ -74,9 +81,11 @@ struct Q_GUI_EXPORT QShaderPrivate
static const QShaderPrivate *get(const QShader *s) { return s->d; }
QAtomicInt ref;
+ int qsbVersion = QSB_VERSION;
QShader::Stage stage = QShader::VertexStage;
QShaderDescription desc;
QHash<QShaderKey, QShaderCode> shaders;
+ QHash<QShaderKey, QShader::NativeResourceBindingMap> bindings;
};
QT_END_NAMESPACE
diff --git a/src/gui/rhi/qshaderdescription.cpp b/src/gui/rhi/qshaderdescription.cpp
index 179d5f3a07..96c8d082fc 100644
--- a/src/gui/rhi/qshaderdescription.cpp
+++ b/src/gui/rhi/qshaderdescription.cpp
@@ -36,8 +36,12 @@
#include "qshaderdescription_p_p.h"
#include <QDebug>
+#include <QDataStream>
#include <QJsonObject>
#include <QJsonArray>
+#include <QCborValue>
+#include <QCborMap>
+#include <QCborArray>
QT_BEGIN_NAMESPACE
@@ -99,8 +103,8 @@ QT_BEGIN_NAMESPACE
float \c opacity at offset 64.
All this is described by a QShaderDescription object. QShaderDescription
- can also be serialized to JSON and binary JSON, and can be deserialized
- from binary JSON. In practice this is rarely needed since QShader
+ can also be serialized to JSON and CBOR, and can be deserialized
+ from CBOR. In practice this is rarely needed since QShader
takes care of the associated QShaderDescription automatically, but if the
QShaderDescription of the above shader would be written out as JSON, it
would look like the following:
@@ -333,13 +337,14 @@ bool QShaderDescription::isValid() const
}
/*!
- \return a serialized binary version of the data.
+ \return a serialized binary version of the data in CBOR (Concise Binary
+ Object Representation) format.
- \sa toJson()
+ \sa QCborValue, toJson()
*/
-QByteArray QShaderDescription::toBinaryJson() const
+QByteArray QShaderDescription::toCbor() const
{
- return d->makeDoc().toBinaryData();
+ return QCborValue::fromJsonValue(d->makeDoc().object()).toCbor();
}
/*!
@@ -347,21 +352,60 @@ QByteArray QShaderDescription::toBinaryJson() const
\note There is no deserialization method provided for JSON text.
- \sa toBinaryJson()
+ \sa toCbor()
*/
QByteArray QShaderDescription::toJson() const
{
return d->makeDoc().toJson();
}
+void QShaderDescription::serialize(QDataStream *stream) const
+{
+ d->writeToStream(stream);
+}
+
+#if QT_CONFIG(binaryjson) && QT_DEPRECATED_SINCE(5, 15)
/*!
+ \deprecated
+
Deserializes the given binary JSON \a data and returns a new
QShaderDescription.
+
+ \sa fromCbor()
*/
QShaderDescription QShaderDescription::fromBinaryJson(const QByteArray &data)
{
QShaderDescription desc;
+QT_WARNING_PUSH
+QT_WARNING_DISABLE_DEPRECATED
QShaderDescriptionPrivate::get(&desc)->loadDoc(QJsonDocument::fromBinaryData(data));
+QT_WARNING_POP
+ return desc;
+}
+#endif
+
+/*!
+ Deserializes the given CBOR \a data and returns a new QShaderDescription.
+ */
+QShaderDescription QShaderDescription::fromCbor(const QByteArray &data)
+{
+ QShaderDescription desc;
+ const QCborValue cbor = QCborValue::fromCbor(data);
+ if (cbor.isMap()) {
+ const QJsonDocument doc(cbor.toMap().toJsonObject());
+ QShaderDescriptionPrivate::get(&desc)->loadDoc(doc);
+ }
+ if (cbor.isArray()) {
+ const QJsonDocument doc(cbor.toArray().toJsonArray());
+ QShaderDescriptionPrivate::get(&desc)->loadDoc(doc);
+ }
+ return desc;
+}
+
+QShaderDescription QShaderDescription::deserialize(QDataStream *stream)
+{
+ QShaderDescription desc;
+ QShaderDescriptionPrivate::get(&desc)->loadFromStream(stream);
return desc;
}
@@ -836,6 +880,15 @@ static void addDeco(QJsonObject *obj, const QShaderDescription::InOutVariable &v
(*obj)[imageFlagsKey] = int(v.imageFlags);
}
+static void serializeDecorations(QDataStream *stream, const QShaderDescription::InOutVariable &v)
+{
+ (*stream) << v.location;
+ (*stream) << v.binding;
+ (*stream) << v.descriptorSet;
+ (*stream) << int(v.imageFormat);
+ (*stream) << int(v.imageFlags);
+}
+
static QJsonObject inOutObject(const QShaderDescription::InOutVariable &v)
{
QJsonObject obj;
@@ -845,6 +898,13 @@ static QJsonObject inOutObject(const QShaderDescription::InOutVariable &v)
return obj;
}
+static void serializeInOutVar(QDataStream *stream, const QShaderDescription::InOutVariable &v)
+{
+ (*stream) << v.name;
+ (*stream) << int(v.type);
+ serializeDecorations(stream, v);
+}
+
static QJsonObject blockMemberObject(const QShaderDescription::BlockVariable &v)
{
QJsonObject obj;
@@ -873,6 +933,23 @@ static QJsonObject blockMemberObject(const QShaderDescription::BlockVariable &v)
return obj;
}
+static void serializeBlockMemberVar(QDataStream *stream, const QShaderDescription::BlockVariable &v)
+{
+ (*stream) << v.name;
+ (*stream) << int(v.type);
+ (*stream) << v.offset;
+ (*stream) << v.size;
+ (*stream) << v.arrayDims.count();
+ for (int dim : v.arrayDims)
+ (*stream) << dim;
+ (*stream) << v.arrayStride;
+ (*stream) << v.matrixStride;
+ (*stream) << v.matrixIsRowMajor;
+ (*stream) << v.structMembers.count();
+ for (const QShaderDescription::BlockVariable &sv : v.structMembers)
+ serializeBlockMemberVar(stream, sv);
+}
+
QJsonDocument QShaderDescriptionPrivate::makeDoc()
{
QJsonObject root;
@@ -971,6 +1048,67 @@ QJsonDocument QShaderDescriptionPrivate::makeDoc()
return QJsonDocument(root);
}
+void QShaderDescriptionPrivate::writeToStream(QDataStream *stream)
+{
+ (*stream) << inVars.count();
+ for (const QShaderDescription::InOutVariable &v : qAsConst(inVars))
+ serializeInOutVar(stream, v);
+
+ (*stream) << outVars.count();
+ for (const QShaderDescription::InOutVariable &v : qAsConst(outVars))
+ serializeInOutVar(stream, v);
+
+ (*stream) << uniformBlocks.count();
+ for (const QShaderDescription::UniformBlock &b : uniformBlocks) {
+ (*stream) << b.blockName;
+ (*stream) << b.structName;
+ (*stream) << b.size;
+ (*stream) << b.binding;
+ (*stream) << b.descriptorSet;
+ (*stream) << b.members.count();
+ for (const QShaderDescription::BlockVariable &v : b.members)
+ serializeBlockMemberVar(stream, v);
+ }
+
+ (*stream) << pushConstantBlocks.count();
+ for (const QShaderDescription::PushConstantBlock &b : pushConstantBlocks) {
+ (*stream) << b.name;
+ (*stream) << b.size;
+ (*stream) << b.members.count();
+ for (const QShaderDescription::BlockVariable &v : b.members)
+ serializeBlockMemberVar(stream, v);
+ }
+
+ (*stream) << storageBlocks.count();
+ for (const QShaderDescription::StorageBlock &b : storageBlocks) {
+ (*stream) << b.blockName;
+ (*stream) << b.instanceName;
+ (*stream) << b.knownSize;
+ (*stream) << b.binding;
+ (*stream) << b.descriptorSet;
+ (*stream) << b.members.count();
+ for (const QShaderDescription::BlockVariable &v : b.members)
+ serializeBlockMemberVar(stream, v);
+ }
+
+ (*stream) << combinedImageSamplers.count();
+ for (const QShaderDescription::InOutVariable &v : qAsConst(combinedImageSamplers)) {
+ (*stream) << v.name;
+ (*stream) << int(v.type);
+ serializeDecorations(stream, v);
+ }
+
+ (*stream) << storageImages.count();
+ for (const QShaderDescription::InOutVariable &v : qAsConst(storageImages)) {
+ (*stream) << v.name;
+ (*stream) << int(v.type);
+ serializeDecorations(stream, v);
+ }
+
+ for (size_t i = 0; i < 3; ++i)
+ (*stream) << localSize[i];
+}
+
static QShaderDescription::InOutVariable inOutVar(const QJsonObject &obj)
{
QShaderDescription::InOutVariable var;
@@ -989,6 +1127,29 @@ static QShaderDescription::InOutVariable inOutVar(const QJsonObject &obj)
return var;
}
+static void deserializeDecorations(QDataStream *stream, QShaderDescription::InOutVariable *v)
+{
+ (*stream) >> v->location;
+ (*stream) >> v->binding;
+ (*stream) >> v->descriptorSet;
+ int f;
+ (*stream) >> f;
+ v->imageFormat = QShaderDescription::ImageFormat(f);
+ (*stream) >> f;
+ v->imageFlags = QShaderDescription::ImageFlags(f);
+}
+
+static QShaderDescription::InOutVariable deserializeInOutVar(QDataStream *stream)
+{
+ QShaderDescription::InOutVariable var;
+ (*stream) >> var.name;
+ int t;
+ (*stream) >> t;
+ var.type = QShaderDescription::VariableType(t);
+ deserializeDecorations(stream, &var);
+ return var;
+}
+
static QShaderDescription::BlockVariable blockVar(const QJsonObject &obj)
{
QShaderDescription::BlockVariable var;
@@ -1015,6 +1176,30 @@ static QShaderDescription::BlockVariable blockVar(const QJsonObject &obj)
return var;
}
+static QShaderDescription::BlockVariable deserializeBlockMemberVar(QDataStream *stream)
+{
+ QShaderDescription::BlockVariable var;
+ (*stream) >> var.name;
+ int t;
+ (*stream) >> t;
+ var.type = QShaderDescription::VariableType(t);
+ (*stream) >> var.offset;
+ (*stream) >> var.size;
+ int count;
+ (*stream) >> count;
+ var.arrayDims.resize(count);
+ for (int i = 0; i < count; ++i)
+ (*stream) >> var.arrayDims[i];
+ (*stream) >> var.arrayStride;
+ (*stream) >> var.matrixStride;
+ (*stream) >> var.matrixIsRowMajor;
+ (*stream) >> count;
+ var.structMembers.resize(count);
+ for (int i = 0; i < count; ++i)
+ var.structMembers[i] = deserializeBlockMemberVar(stream);
+ return var;
+}
+
void QShaderDescriptionPrivate::loadDoc(const QJsonDocument &doc)
{
if (doc.isNull()) {
@@ -1119,4 +1304,187 @@ void QShaderDescriptionPrivate::loadDoc(const QJsonDocument &doc)
}
}
+void QShaderDescriptionPrivate::loadFromStream(QDataStream *stream)
+{
+ Q_ASSERT(ref.loadRelaxed() == 1); // must be detached
+
+ int count;
+ (*stream) >> count;
+ inVars.resize(count);
+ for (int i = 0; i < count; ++i)
+ inVars[i] = deserializeInOutVar(stream);
+
+ (*stream) >> count;
+ outVars.resize(count);
+ for (int i = 0; i < count; ++i)
+ outVars[i] = deserializeInOutVar(stream);
+
+ (*stream) >> count;
+ uniformBlocks.resize(count);
+ for (int i = 0; i < count; ++i) {
+ (*stream) >> uniformBlocks[i].blockName;
+ (*stream) >> uniformBlocks[i].structName;
+ (*stream) >> uniformBlocks[i].size;
+ (*stream) >> uniformBlocks[i].binding;
+ (*stream) >> uniformBlocks[i].descriptorSet;
+ int memberCount;
+ (*stream) >> memberCount;
+ uniformBlocks[i].members.resize(memberCount);
+ for (int memberIdx = 0; memberIdx < memberCount; ++memberIdx)
+ uniformBlocks[i].members[memberIdx] = deserializeBlockMemberVar(stream);
+ }
+
+ (*stream) >> count;
+ pushConstantBlocks.resize(count);
+ for (int i = 0; i < count; ++i) {
+ (*stream) >> pushConstantBlocks[i].name;
+ (*stream) >> pushConstantBlocks[i].size;
+ int memberCount;
+ (*stream) >> memberCount;
+ pushConstantBlocks[i].members.resize(memberCount);
+ for (int memberIdx = 0; memberIdx < memberCount; ++memberIdx)
+ pushConstantBlocks[i].members[memberIdx] = deserializeBlockMemberVar(stream);
+ }
+
+ (*stream) >> count;
+ storageBlocks.resize(count);
+ for (int i = 0; i < count; ++i) {
+ (*stream) >> storageBlocks[i].blockName;
+ (*stream) >> storageBlocks[i].instanceName;
+ (*stream) >> storageBlocks[i].knownSize;
+ (*stream) >> storageBlocks[i].binding;
+ (*stream) >> storageBlocks[i].descriptorSet;
+ int memberCount;
+ (*stream) >> memberCount;
+ storageBlocks[i].members.resize(memberCount);
+ for (int memberIdx = 0; memberIdx < memberCount; ++memberIdx)
+ storageBlocks[i].members[memberIdx] = deserializeBlockMemberVar(stream);
+ }
+
+ (*stream) >> count;
+ combinedImageSamplers.resize(count);
+ for (int i = 0; i < count; ++i) {
+ (*stream) >> combinedImageSamplers[i].name;
+ int t;
+ (*stream) >> t;
+ combinedImageSamplers[i].type = QShaderDescription::VariableType(t);
+ deserializeDecorations(stream, &combinedImageSamplers[i]);
+ }
+
+ (*stream) >> count;
+ storageImages.resize(count);
+ for (int i = 0; i < count; ++i) {
+ (*stream) >> storageImages[i].name;
+ int t;
+ (*stream) >> t;
+ storageImages[i].type = QShaderDescription::VariableType(t);
+ deserializeDecorations(stream, &storageImages[i]);
+ }
+
+ for (size_t i = 0; i < 3; ++i)
+ (*stream) >> localSize[i];
+}
+
+/*!
+ Returns \c true if the two QShaderDescription objects \a lhs and \a rhs are
+ equal.
+
+ \relates QShaderDescription
+ */
+bool operator==(const QShaderDescription &lhs, const QShaderDescription &rhs) Q_DECL_NOTHROW
+{
+ if (lhs.d == rhs.d)
+ return true;
+
+ return lhs.d->inVars == rhs.d->inVars
+ && lhs.d->outVars == rhs.d->outVars
+ && lhs.d->uniformBlocks == rhs.d->uniformBlocks
+ && lhs.d->pushConstantBlocks == rhs.d->pushConstantBlocks
+ && lhs.d->storageBlocks == rhs.d->storageBlocks
+ && lhs.d->combinedImageSamplers == rhs.d->combinedImageSamplers
+ && lhs.d->storageImages == rhs.d->storageImages
+ && lhs.d->localSize == rhs.d->localSize;
+}
+
+/*!
+ Returns \c true if the two InOutVariable objects \a lhs and \a rhs are
+ equal.
+
+ \relates QShaderDescription::InOutVariable
+ */
+bool operator==(const QShaderDescription::InOutVariable &lhs, const QShaderDescription::InOutVariable &rhs) Q_DECL_NOTHROW
+{
+ return lhs.name == rhs.name
+ && lhs.type == rhs.type
+ && lhs.location == rhs.location
+ && lhs.binding == rhs.binding
+ && lhs.descriptorSet == rhs.descriptorSet
+ && lhs.imageFormat == rhs.imageFormat
+ && lhs.imageFlags == rhs.imageFlags;
+}
+
+/*!
+ Returns \c true if the two BlockVariable objects \a lhs and \a rhs are
+ equal.
+
+ \relates QShaderDescription::BlockVariable
+ */
+bool operator==(const QShaderDescription::BlockVariable &lhs, const QShaderDescription::BlockVariable &rhs) Q_DECL_NOTHROW
+{
+ return lhs.name == rhs.name
+ && lhs.type == rhs.type
+ && lhs.offset == rhs.offset
+ && lhs.size == rhs.size
+ && lhs.arrayDims == rhs.arrayDims
+ && lhs.arrayStride == rhs.arrayStride
+ && lhs.matrixStride == rhs.matrixStride
+ && lhs.matrixIsRowMajor == rhs.matrixIsRowMajor
+ && lhs.structMembers == rhs.structMembers;
+}
+
+/*!
+ Returns \c true if the two UniformBlock objects \a lhs and \a rhs are
+ equal.
+
+ \relates QShaderDescription::UniformBlock
+ */
+bool operator==(const QShaderDescription::UniformBlock &lhs, const QShaderDescription::UniformBlock &rhs) Q_DECL_NOTHROW
+{
+ return lhs.blockName == rhs.blockName
+ && lhs.structName == rhs.structName
+ && lhs.size == rhs.size
+ && lhs.binding == rhs.binding
+ && lhs.descriptorSet == rhs.descriptorSet
+ && lhs.members == rhs.members;
+}
+
+/*!
+ Returns \c true if the two PushConstantBlock objects \a lhs and \a rhs are
+ equal.
+
+ \relates QShaderDescription::PushConstantBlock
+ */
+bool operator==(const QShaderDescription::PushConstantBlock &lhs, const QShaderDescription::PushConstantBlock &rhs) Q_DECL_NOTHROW
+{
+ return lhs.name == rhs.name
+ && lhs.size == rhs.size
+ && lhs.members == rhs.members;
+}
+
+/*!
+ Returns \c true if the two StorageBlock objects \a lhs and \a rhs are
+ equal.
+
+ \relates QShaderDescription::StorageBlock
+ */
+bool operator==(const QShaderDescription::StorageBlock &lhs, const QShaderDescription::StorageBlock &rhs) Q_DECL_NOTHROW
+{
+ return lhs.blockName == rhs.blockName
+ && lhs.instanceName == rhs.instanceName
+ && lhs.knownSize == rhs.knownSize
+ && lhs.binding == rhs.binding
+ && lhs.descriptorSet == rhs.descriptorSet
+ && lhs.members == rhs.members;
+}
+
QT_END_NAMESPACE
diff --git a/src/gui/rhi/qshaderdescription_p.h b/src/gui/rhi/qshaderdescription_p.h
index 5a63b998cd..108fc32a56 100644
--- a/src/gui/rhi/qshaderdescription_p.h
+++ b/src/gui/rhi/qshaderdescription_p.h
@@ -56,6 +56,7 @@
QT_BEGIN_NAMESPACE
struct QShaderDescriptionPrivate;
+class QDataStream;
class Q_GUI_EXPORT QShaderDescription
{
@@ -68,10 +69,16 @@ public:
bool isValid() const;
- QByteArray toBinaryJson() const;
+ QByteArray toCbor() const;
+ void serialize(QDataStream *stream) const;
QByteArray toJson() const;
+#if QT_CONFIG(binaryjson) && QT_DEPRECATED_SINCE(5, 15)
+ QT_DEPRECATED_X("Use CBOR format instead")
static QShaderDescription fromBinaryJson(const QByteArray &data);
+#endif
+ static QShaderDescription fromCbor(const QByteArray &data);
+ static QShaderDescription deserialize(QDataStream *stream);
enum VariableType {
Unknown = 0,
@@ -263,6 +270,7 @@ private:
#ifndef QT_NO_DEBUG_STREAM
friend Q_GUI_EXPORT QDebug operator<<(QDebug, const QShaderDescription &);
#endif
+ friend Q_GUI_EXPORT bool operator==(const QShaderDescription &lhs, const QShaderDescription &rhs) Q_DECL_NOTHROW;
};
Q_DECLARE_OPERATORS_FOR_FLAGS(QShaderDescription::ImageFlags)
@@ -276,6 +284,43 @@ Q_GUI_EXPORT QDebug operator<<(QDebug, const QShaderDescription::PushConstantBlo
Q_GUI_EXPORT QDebug operator<<(QDebug, const QShaderDescription::StorageBlock &);
#endif
+Q_GUI_EXPORT bool operator==(const QShaderDescription &lhs, const QShaderDescription &rhs) Q_DECL_NOTHROW;
+Q_GUI_EXPORT bool operator==(const QShaderDescription::InOutVariable &lhs, const QShaderDescription::InOutVariable &rhs) Q_DECL_NOTHROW;
+Q_GUI_EXPORT bool operator==(const QShaderDescription::BlockVariable &lhs, const QShaderDescription::BlockVariable &rhs) Q_DECL_NOTHROW;
+Q_GUI_EXPORT bool operator==(const QShaderDescription::UniformBlock &lhs, const QShaderDescription::UniformBlock &rhs) Q_DECL_NOTHROW;
+Q_GUI_EXPORT bool operator==(const QShaderDescription::PushConstantBlock &lhs, const QShaderDescription::PushConstantBlock &rhs) Q_DECL_NOTHROW;
+Q_GUI_EXPORT bool operator==(const QShaderDescription::StorageBlock &lhs, const QShaderDescription::StorageBlock &rhs) Q_DECL_NOTHROW;
+
+inline bool operator!=(const QShaderDescription &lhs, const QShaderDescription &rhs) Q_DECL_NOTHROW
+{
+ return !(lhs == rhs);
+}
+
+inline bool operator!=(const QShaderDescription::InOutVariable &lhs, const QShaderDescription::InOutVariable &rhs) Q_DECL_NOTHROW
+{
+ return !(lhs == rhs);
+}
+
+inline bool operator!=(const QShaderDescription::BlockVariable &lhs, const QShaderDescription::BlockVariable &rhs) Q_DECL_NOTHROW
+{
+ return !(lhs == rhs);
+}
+
+inline bool operator!=(const QShaderDescription::UniformBlock &lhs, const QShaderDescription::UniformBlock &rhs) Q_DECL_NOTHROW
+{
+ return !(lhs == rhs);
+}
+
+inline bool operator!=(const QShaderDescription::PushConstantBlock &lhs, const QShaderDescription::PushConstantBlock &rhs) Q_DECL_NOTHROW
+{
+ return !(lhs == rhs);
+}
+
+inline bool operator!=(const QShaderDescription::StorageBlock &lhs, const QShaderDescription::StorageBlock &rhs) Q_DECL_NOTHROW
+{
+ return !(lhs == rhs);
+}
+
QT_END_NAMESPACE
#endif
diff --git a/src/gui/rhi/qshaderdescription_p_p.h b/src/gui/rhi/qshaderdescription_p_p.h
index 1caee24984..69b6e811a1 100644
--- a/src/gui/rhi/qshaderdescription_p_p.h
+++ b/src/gui/rhi/qshaderdescription_p_p.h
@@ -80,7 +80,9 @@ struct Q_GUI_EXPORT QShaderDescriptionPrivate
static const QShaderDescriptionPrivate *get(const QShaderDescription *desc) { return desc->d; }
QJsonDocument makeDoc();
+ void writeToStream(QDataStream *stream);
void loadDoc(const QJsonDocument &doc);
+ void loadFromStream(QDataStream *stream);
QAtomicInt ref;
QVector<QShaderDescription::InOutVariable> inVars;
diff --git a/src/gui/rhi/rhi.pri b/src/gui/rhi/rhi.pri
index 4297a5602b..ccd9592634 100644
--- a/src/gui/rhi/rhi.pri
+++ b/src/gui/rhi/rhi.pri
@@ -43,15 +43,15 @@ win32 {
LIBS += -ld3d11 -ldxgi -ldxguid
}
-# darwin {
-macos {
+macos|ios {
HEADERS += \
rhi/qrhimetal_p.h \
rhi/qrhimetal_p_p.h
SOURCES += \
rhi/qrhimetal.mm
- LIBS += -framework AppKit -framework Metal
+ macos: LIBS += -framework AppKit
+ LIBS += -framework Metal
}
include($$PWD/../../3rdparty/VulkanMemoryAllocator.pri)