summaryrefslogtreecommitdiffstats
path: root/src/gui/rhi/qrhi.cpp
diff options
context:
space:
mode:
authorLaszlo Agocs <laszlo.agocs@qt.io>2021-12-21 13:26:45 +0100
committerLaszlo Agocs <laszlo.agocs@qt.io>2022-01-06 14:56:25 +0100
commit0a59101495634a02e7b893682904f4cfc5898624 (patch)
treeaadaf9ee66173de05d85579dc85338daa48a4a84 /src/gui/rhi/qrhi.cpp
parentc20b213eabd8138f8566c7a1fd0633625c47b520 (diff)
rhi: Add support for separate image and sampler objects
For Direct 3D, Metal, and Vulkan this is natively supported. (and makes no difference in particular for D3D and Metal because they do not have the legacy combined image sampler concept anyways) With OpenGL it will work too, but this relies on SPIR-Cross magic and is still using a combined sampler (e.g. a sampler2D) in the GLSL shader. The GL backend walks back and forth in the mapping tables from the shader baker in order to make this work, which is presumably slightly more expensive than combined image samplers. Do note that combined image samplers (i.e. sampler2D in the shader and QRhiShaderResourceBinding::sampledTexture() in code) continue to be the primary, recommended way for any user of the rhi for the time being. Change-Id: I194721bc657b1ffbcc1bb79e6eadebe569a25087 Reviewed-by: Qt CI Bot <qt_ci_bot@qt-project.org> Reviewed-by: Laszlo Agocs <laszlo.agocs@qt.io>
Diffstat (limited to 'src/gui/rhi/qrhi.cpp')
-rw-r--r--src/gui/rhi/qrhi.cpp166
1 files changed, 158 insertions, 8 deletions
diff --git a/src/gui/rhi/qrhi.cpp b/src/gui/rhi/qrhi.cpp
index 3da1edfdac..a2704b8491 100644
--- a/src/gui/rhi/qrhi.cpp
+++ b/src/gui/rhi/qrhi.cpp
@@ -3157,7 +3157,7 @@ void QRhiImplementation::updateLayoutDesc(QRhiShaderResourceBindings *srb)
for (const QRhiShaderResourceBinding &b : qAsConst(srb->m_bindings)) {
const QRhiShaderResourceBinding::Data *d = b.data();
srb->m_layoutDescHash ^= uint(d->binding) ^ uint(d->stage) ^ uint(d->type)
- ^ uint(d->type == QRhiShaderResourceBinding::SampledTexture ? d->u.stex.count : 1);
+ ^ uint(d->arraySize());
layoutDescAppender = d->serialize(layoutDescAppender);
}
}
@@ -3223,10 +3223,11 @@ void QRhiImplementation::updateLayoutDesc(QRhiShaderResourceBindings *srb)
*/
bool QRhiShaderResourceBinding::isLayoutCompatible(const QRhiShaderResourceBinding &other) const
{
- // i.e. everything that goes into a VkDescriptorSetLayoutBinding must match
- const int thisCount = d.type == QRhiShaderResourceBinding::SampledTexture ? d.u.stex.count : 1;
- const int otherCount = other.d.type == QRhiShaderResourceBinding::SampledTexture ? other.d.u.stex.count : 1;
- return d.binding == other.d.binding && d.stage == other.d.stage && d.type == other.d.type && thisCount == otherCount;
+ // everything that goes into a VkDescriptorSetLayoutBinding must match
+ return d.binding == other.d.binding
+ && d.stage == other.d.stage
+ && d.type == other.d.type
+ && d.arraySize() == other.d.arraySize();
}
/*!
@@ -3360,8 +3361,7 @@ QRhiShaderResourceBinding QRhiShaderResourceBinding::sampledTexture(
b.d.stage = stage;
b.d.type = SampledTexture;
b.d.u.stex.count = 1;
- b.d.u.stex.texSamplers[0].tex = tex;
- b.d.u.stex.texSamplers[0].sampler = sampler;
+ b.d.u.stex.texSamplers[0] = { tex, sampler };
return b;
}
@@ -3412,12 +3412,116 @@ QRhiShaderResourceBinding QRhiShaderResourceBinding::sampledTextures(
if (texSamplers)
b.d.u.stex.texSamplers[i] = texSamplers[i];
else
- b.d.u.stex.texSamplers[i] = {};
+ b.d.u.stex.texSamplers[i] = { nullptr, nullptr };
}
return b;
}
/*!
+ \return a shader resource binding for the given binding number, pipeline
+ stages, and texture specified by \a binding, \a stage, \a tex.
+
+ \note This function is equivalent to calling textures() with a
+ \c count of 1.
+
+ \note \a tex can be null. It is valid to create a
+ QRhiShaderResourceBindings with unspecified resources, but such an object
+ cannot be used with QRhiCommandBuffer::setShaderResources(). It is however
+ suitable for creating pipelines. Such a pipeline must then always be used
+ together with another, layout compatible QRhiShaderResourceBindings with
+ resources present passed to QRhiCommandBuffer::setShaderResources().
+
+ This creates a binding for a separate texture (image) object, whereas
+ sampledTexture() is suitable for combined image samplers. In
+ Vulkan-compatible GLSL code separate textures are declared as \c texture2D
+ as opposed to \c sampler2D: \c{layout(binding = 1) uniform texture2D tex;}
+
+ \sa textures(), sampler()
+ */
+QRhiShaderResourceBinding QRhiShaderResourceBinding::texture(int binding, StageFlags stage, QRhiTexture *tex)
+{
+ QRhiShaderResourceBinding b;
+ b.d.binding = binding;
+ b.d.stage = stage;
+ b.d.type = Texture;
+ b.d.u.stex.count = 1;
+ b.d.u.stex.texSamplers[0] = { tex, nullptr };
+ return b;
+}
+
+/*!
+ \return a shader resource binding for the given binding number, pipeline
+ stages, and the array of (separate) textures specified by \a binding, \a
+ stage, \a count, and \a tex.
+
+ \note \a count must be at least 1, and not larger than 16.
+
+ \note When \a count is 1, this function is equivalent to texture().
+
+ \warning All elements of the array must be specified.
+
+ \note \a tex can be null. It is valid to create a
+ QRhiShaderResourceBindings with unspecified resources, but such an object
+ cannot be used with QRhiCommandBuffer::setShaderResources(). It is however
+ suitable for creating pipelines. Such a pipeline must then always be used
+ together with another, layout compatible QRhiShaderResourceBindings with
+ resources present passed to QRhiCommandBuffer::setShaderResources().
+
+ \sa texture(), sampler()
+ */
+QRhiShaderResourceBinding QRhiShaderResourceBinding::textures(int binding, StageFlags stage, int count, QRhiTexture **tex)
+{
+ Q_ASSERT(count >= 1 && count <= Data::MAX_TEX_SAMPLER_ARRAY_SIZE);
+ QRhiShaderResourceBinding b;
+ b.d.binding = binding;
+ b.d.stage = stage;
+ b.d.type = Texture;
+ b.d.u.stex.count = count;
+ for (int i = 0; i < count; ++i) {
+ if (tex)
+ b.d.u.stex.texSamplers[i] = { tex[i], nullptr };
+ else
+ b.d.u.stex.texSamplers[i] = { nullptr, nullptr };
+ }
+ return b;
+}
+
+/*!
+ \return a shader resource binding for the given binding number, pipeline
+ stages, and sampler specified by \a binding, \a stage, \a sampler.
+
+ \note \a sampler can be null. It is valid to create a
+ QRhiShaderResourceBindings with unspecified resources, but such an object
+ cannot be used with QRhiCommandBuffer::setShaderResources(). It is however
+ suitable for creating pipelines. Such a pipeline must then always be used
+ together with another, layout compatible QRhiShaderResourceBindings with
+ resources present passed to QRhiCommandBuffer::setShaderResources().
+
+ Arrays of separate samplers are not supported.
+
+ This creates a binding for a separate sampler object, whereas
+ sampledTexture() is suitable for combined image samplers. In
+ Vulkan-compatible GLSL code separate samplers are declared as \c sampler
+ as opposed to \c sampler2D: \c{layout(binding = 2) uniform sampler samp;}
+
+ With both a \c texture2D and \c sampler present, they can be used together
+ to sample the texture: \c{fragColor = texture(sampler2D(tex, samp),
+ texcoord);}.
+
+ \sa texture()
+ */
+QRhiShaderResourceBinding QRhiShaderResourceBinding::sampler(int binding, StageFlags stage, QRhiSampler *sampler)
+{
+ QRhiShaderResourceBinding b;
+ b.d.binding = binding;
+ b.d.stage = stage;
+ b.d.type = Sampler;
+ b.d.u.stex.count = 1;
+ b.d.u.stex.texSamplers[0] = { nullptr, sampler };
+ return b;
+}
+
+/*!
\return a shader resource binding for a read-only storage image with the
given \a binding number and pipeline \a stage. The image load operations
will have access to all layers of the specified \a level. (so if the texture
@@ -3715,6 +3819,18 @@ bool operator==(const QRhiShaderResourceBinding &a, const QRhiShaderResourceBind
}
}
break;
+ case QRhiShaderResourceBinding::Texture:
+ if (da->u.stex.count != db->u.stex.count)
+ return false;
+ for (int i = 0; i < da->u.stex.count; ++i) {
+ if (da->u.stex.texSamplers[i].tex != db->u.stex.texSamplers[i].tex)
+ return false;
+ }
+ break;
+ case QRhiShaderResourceBinding::Sampler:
+ if (da->u.stex.texSamplers[0].sampler != db->u.stex.texSamplers[0].sampler)
+ return false;
+ break;
case QRhiShaderResourceBinding::ImageLoad:
Q_FALLTHROUGH();
case QRhiShaderResourceBinding::ImageStore:
@@ -3774,6 +3890,12 @@ size_t qHash(const QRhiShaderResourceBinding &b, size_t seed) noexcept
h ^= qHash(reinterpret_cast<quintptr>(d->u.stex.texSamplers[0].tex));
h ^= qHash(reinterpret_cast<quintptr>(d->u.stex.texSamplers[0].sampler));
break;
+ case QRhiShaderResourceBinding::Texture:
+ h ^= qHash(reinterpret_cast<quintptr>(d->u.stex.texSamplers[0].tex));
+ break;
+ case QRhiShaderResourceBinding::Sampler:
+ h ^= qHash(reinterpret_cast<quintptr>(d->u.stex.texSamplers[0].sampler));
+ break;
case QRhiShaderResourceBinding::ImageLoad:
Q_FALLTHROUGH();
case QRhiShaderResourceBinding::ImageStore:
@@ -3820,6 +3942,18 @@ QDebug operator<<(QDebug dbg, const QRhiShaderResourceBinding &b)
}
dbg.nospace() << ')';
break;
+ case QRhiShaderResourceBinding::Texture:
+ dbg.nospace() << " Textures("
+ << "count=" << d->u.stex.count;
+ for (int i = 0; i < d->u.stex.count; ++i)
+ dbg.nospace() << " texture=" << d->u.stex.texSamplers[i].tex;
+ dbg.nospace() << ')';
+ break;
+ case QRhiShaderResourceBinding::Sampler:
+ dbg.nospace() << " Sampler("
+ << " sampler=" << d->u.stex.texSamplers[0].sampler
+ << ')';
+ break;
case QRhiShaderResourceBinding::ImageLoad:
dbg.nospace() << " ImageLoad("
<< "texture=" << d->u.simage.tex
@@ -4931,6 +5065,22 @@ bool QRhiImplementation::sanityCheckShaderResourceBindings(QRhiShaderResourceBin
bindingsOk = false;
}
break;
+ case QRhiShaderResourceBinding::Texture:
+ if (!bindingSeen[binding]) {
+ bindingSeen[binding] = true;
+ } else {
+ qWarning("Texture duplicates an existing binding number %d", binding);
+ bindingsOk = false;
+ }
+ break;
+ case QRhiShaderResourceBinding::Sampler:
+ if (!bindingSeen[binding]) {
+ bindingSeen[binding] = true;
+ } else {
+ qWarning("Sampler duplicates an existing binding number %d", binding);
+ bindingsOk = false;
+ }
+ break;
case QRhiShaderResourceBinding::ImageLoad:
Q_FALLTHROUGH();
case QRhiShaderResourceBinding::ImageStore: