summaryrefslogtreecommitdiffstats
path: root/src/gui
diff options
context:
space:
mode:
authorLaszlo Agocs <laszlo.agocs@qt.io>2021-12-18 22:21:31 +0100
committerLaszlo Agocs <laszlo.agocs@qt.io>2022-01-03 14:57:01 +0100
commit04cdde30d64ed55adc8174dc379f131fb2aeba98 (patch)
tree4ce5ff5647ab97f46072951e6dacde3a7d0102fe /src/gui
parent66a79287f159d236090fd0092a81be133862bbdc (diff)
rhi: Enable exposing separate image and sampler objects from the shader
Adds the following in a QShader/QShaderDescription: - a list of separate images - a list of separate samplers - a list of "combined_sampler_uniform_name" -> [ separate_texture_binding, separate_sampler_binding ] mappings (relevant for GLSL only) On the QShader (and qsb/QShaderBaker) level not having separate image (texture) and sampler objects exposed in the reflection info is not entirely future proof. Right now we benefit strongly from the fact that Vulkan/SPIR-V supports both combined and separate images/samplers, while for HLSL and MSL SPIRV-Cross translates combined image samplers to separate texture and sampler objects, but it is not given that relying on combined image samplers will always be possible in the long run; it is mostly a legacy OpenGL thing that just happens to be supported in Vulkan/SPIR-V due to some benefits with certain implementations/hw, but is not something present in any newer APIs. In addition, before this patch, attempting to run a shader with separate textures and samplers through qsb will just fail for GLSL, even though SPIRV-Cross does have the ability to generate a "fake" combined sampler for each separate texture+sampler combination. Take this into use. This also involves generating and exposing a combined_name->[separate_texture_binding,separate_sampler_binding] mapping table for GLSL, not unlike we have the native binding map for HLSL and MSL. A user (such as, the GL backend of QRhi) would then use this table to recognize what user-provided texture+sampler binding point numbers correspond to which auto-generated sampler2Ds in the GL program. Take the following example: layout(binding = 1) uniform texture2D sepTex; layout(binding = 2) uniform sampler sepSampler; layout(binding = 3) uniform sampler sepSampler2; Inn the reflection info (QShaderDescription) this (assuming a corresponding qtshadertools patch in place) now gives one entry in separateImages() and two in separateSamplers(). Assuming sepTex is used both with sepSampler and sepSampler2, the GLSL output and mapping table from QShaderBaker will have two auto-generated sampler2Ds (and no 'texture2D' or 'sampler'). One immediate benefit is that it is now possible to create a shader that relies only on separate images and samplers, feed it into qsb, generate all the possible targets, and then also feed the SPIR-V binary into a tool or library such as Tint (e.g. to generate WGSL) that canot deal with combined image samplers. Change-Id: I9b19847ea5854837b45d3a23edc788c48502aa15 Reviewed-by: Andy Nichols <andy.nichols@qt.io>
Diffstat (limited to 'src/gui')
-rw-r--r--src/gui/rhi/qshader.cpp113
-rw-r--r--src/gui/rhi/qshader_p.h11
-rw-r--r--src/gui/rhi/qshader_p_p.h7
-rw-r--r--src/gui/rhi/qshaderdescription.cpp85
-rw-r--r--src/gui/rhi/qshaderdescription_p.h3
-rw-r--r--src/gui/rhi/qshaderdescription_p_p.h4
6 files changed, 210 insertions, 13 deletions
diff --git a/src/gui/rhi/qshader.cpp b/src/gui/rhi/qshader.cpp
index 74255dcf5c..b29abe2778 100644
--- a/src/gui/rhi/qshader.cpp
+++ b/src/gui/rhi/qshader.cpp
@@ -391,6 +391,18 @@ QByteArray QShader::serialized() const
ds << mapIt.value().second;
}
}
+ ds << int(d->combinedImageMap.count());
+ for (auto it = d->combinedImageMap.cbegin(), itEnd = d->combinedImageMap.cend(); it != itEnd; ++it) {
+ const QShaderKey &k(it.key());
+ writeShaderKey(&ds, k);
+ const SeparateToCombinedImageSamplerMappingList &list(it.value());
+ ds << int(list.count());
+ for (auto listIt = list.cbegin(), listItEnd = list.cend(); listIt != listItEnd; ++listIt) {
+ ds << listIt->combinedSamplerName;
+ ds << listIt->textureBinding;
+ ds << listIt->samplerBinding;
+ }
+ }
return qCompress(buf.buffer());
}
@@ -431,6 +443,7 @@ QShader QShader::fromSerialized(const QByteArray &data)
ds >> intVal;
d->qsbVersion = intVal;
if (d->qsbVersion != QShaderPrivate::QSB_VERSION
+ && d->qsbVersion != QShaderPrivate::QSB_VERSION_WITHOUT_SEPARATE_IMAGES_AND_SAMPLERS
&& d->qsbVersion != QShaderPrivate::QSB_VERSION_WITHOUT_VAR_ARRAYDIMS
&& d->qsbVersion != QShaderPrivate::QSB_VERSION_WITH_CBOR
&& d->qsbVersion != QShaderPrivate::QSB_VERSION_WITH_BINARY_JSON
@@ -486,6 +499,27 @@ QShader QShader::fromSerialized(const QByteArray &data)
}
}
+ if (d->qsbVersion > QShaderPrivate::QSB_VERSION_WITHOUT_SEPARATE_IMAGES_AND_SAMPLERS) {
+ ds >> count;
+ for (int i = 0; i < count; ++i) {
+ QShaderKey k;
+ readShaderKey(&ds, &k);
+ SeparateToCombinedImageSamplerMappingList list;
+ int listSize;
+ ds >> listSize;
+ for (int b = 0; b < listSize; ++b) {
+ QByteArray combinedSamplerName;
+ ds >> combinedSamplerName;
+ int textureBinding;
+ ds >> textureBinding;
+ int samplerBinding;
+ ds >> samplerBinding;
+ list.append({ combinedSamplerName, textureBinding, samplerBinding });
+ }
+ d->combinedImageMap.insert(k, list);
+ }
+ }
+
return bs;
}
@@ -684,17 +718,20 @@ QDebug operator<<(QDebug dbg, const QShaderVersion &v)
\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).
+ not fully compatible with this. 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. In addition, even when an automatic, implicit
+ translation is mostly possible (e.g. by using SPIR-V binding points as HLSL
+ resource register indices), assigning resource bindings without being
+ constrained by the SPIR-V binding points can lead to better results.
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.
+ native binding point for a given SPIR-V binding is. The QRhi backends, for
+ which this is relevant, 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
@@ -741,4 +778,62 @@ void QShader::removeResourceBindingMap(const QShaderKey &key)
d->bindings.erase(it);
}
+/*!
+ \typedef QShader::SeparateToCombinedImageSamplerMappingList
+
+ Synonym for QList<QShader::SeparateToCombinedImageSamplerMapping>.
+ */
+
+/*!
+ \struct QShader::SeparateToCombinedImageSamplerMapping
+
+ Describes a mapping from a traditional combined image sampler uniform to
+ binding points for a separate texture and sampler.
+
+ For example, if \c combinedImageSampler is \c{"_54"}, \c textureBinding is
+ \c 1, and \c samplerBinding is \c 2, this means that the GLSL shader code
+ contains a \c sampler2D (or sampler3D, etc.) uniform with the name of
+ \c{_54} which corresponds to two separate resource bindings (\c 1 and \c 2)
+ in the original shader.
+ */
+
+/*!
+ \return the combined image sampler mapping list for \a key or null if there
+ is no data available for \a key, for example because such a mapping is not
+ applicable for the shading language.
+ */
+const QShader::SeparateToCombinedImageSamplerMappingList *QShader::separateToCombinedImageSamplerMappingList(const QShaderKey &key) const
+{
+ auto it = d->combinedImageMap.constFind(key);
+ if (it == d->combinedImageMap.cend())
+ return nullptr;
+
+ return &it.value();
+}
+
+/*!
+ Stores the given combined image sampler mapping \a list associated with \a key.
+
+ \sa separateToCombinedImageSamplerMappingList()
+ */
+void QShader::setSeparateToCombinedImageSamplerMappingList(const QShaderKey &key,
+ const SeparateToCombinedImageSamplerMappingList &list)
+{
+ detach();
+ d->combinedImageMap[key] = list;
+}
+
+/*!
+ Removes the combined image sampler mapping list for \a key.
+ */
+void QShader::removeSeparateToCombinedImageSamplerMappingList(const QShaderKey &key)
+{
+ auto it = d->combinedImageMap.find(key);
+ if (it == d->combinedImageMap.end())
+ return;
+
+ detach();
+ d->combinedImageMap.erase(it);
+}
+
QT_END_NAMESPACE
diff --git a/src/gui/rhi/qshader_p.h b/src/gui/rhi/qshader_p.h
index b320340229..6dca86b1c4 100644
--- a/src/gui/rhi/qshader_p.h
+++ b/src/gui/rhi/qshader_p.h
@@ -167,6 +167,17 @@ public:
void setResourceBindingMap(const QShaderKey &key, const NativeResourceBindingMap &map);
void removeResourceBindingMap(const QShaderKey &key);
+ struct SeparateToCombinedImageSamplerMapping {
+ QByteArray combinedSamplerName;
+ int textureBinding;
+ int samplerBinding;
+ };
+ using SeparateToCombinedImageSamplerMappingList = QList<SeparateToCombinedImageSamplerMapping>;
+ const SeparateToCombinedImageSamplerMappingList *separateToCombinedImageSamplerMappingList(const QShaderKey &key) const;
+ void setSeparateToCombinedImageSamplerMappingList(const QShaderKey &key,
+ const SeparateToCombinedImageSamplerMappingList &list);
+ void removeSeparateToCombinedImageSamplerMappingList(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 2f6afb4a6d..51c3e29d45 100644
--- a/src/gui/rhi/qshader_p_p.h
+++ b/src/gui/rhi/qshader_p_p.h
@@ -60,7 +60,8 @@ QT_BEGIN_NAMESPACE
struct Q_GUI_EXPORT QShaderPrivate
{
- static const int QSB_VERSION = 5;
+ static const int QSB_VERSION = 6;
+ static const int QSB_VERSION_WITHOUT_SEPARATE_IMAGES_AND_SAMPLERS = 5;
static const int QSB_VERSION_WITHOUT_VAR_ARRAYDIMS = 4;
static const int QSB_VERSION_WITH_CBOR = 3;
static const int QSB_VERSION_WITH_BINARY_JSON = 2;
@@ -77,7 +78,8 @@ struct Q_GUI_EXPORT QShaderPrivate
stage(other->stage),
desc(other->desc),
shaders(other->shaders),
- bindings(other->bindings)
+ bindings(other->bindings),
+ combinedImageMap(other->combinedImageMap)
{
}
@@ -90,6 +92,7 @@ struct Q_GUI_EXPORT QShaderPrivate
QShaderDescription desc;
QHash<QShaderKey, QShaderCode> shaders;
QHash<QShaderKey, QShader::NativeResourceBindingMap> bindings;
+ QHash<QShaderKey, QShader::SeparateToCombinedImageSamplerMappingList> combinedImageMap;
};
QT_END_NAMESPACE
diff --git a/src/gui/rhi/qshaderdescription.cpp b/src/gui/rhi/qshaderdescription.cpp
index 2c79acb1c7..67d70281e6 100644
--- a/src/gui/rhi/qshaderdescription.cpp
+++ b/src/gui/rhi/qshaderdescription.cpp
@@ -222,6 +222,7 @@ QT_BEGIN_NAMESPACE
\value SamplerRect
\value SamplerBuffer
\value SamplerExternalOES
+ \value Sampler For separate samplers.
\value Image1D
\value Image2D
\value Image2DMS
@@ -336,7 +337,8 @@ bool QShaderDescription::isValid() const
{
return !d->inVars.isEmpty() || !d->outVars.isEmpty()
|| !d->uniformBlocks.isEmpty() || !d->pushConstantBlocks.isEmpty() || !d->storageBlocks.isEmpty()
- || !d->combinedImageSamplers.isEmpty() || !d->storageImages.isEmpty();
+ || !d->combinedImageSamplers.isEmpty() || !d->storageImages.isEmpty()
+ || !d->separateImages.isEmpty() || !d->separateSamplers.isEmpty();
}
/*!
@@ -510,6 +512,16 @@ QList<QShaderDescription::InOutVariable> QShaderDescription::combinedImageSample
return d->combinedImageSamplers;
}
+QList<QShaderDescription::InOutVariable> QShaderDescription::separateImages() const
+{
+ return d->separateImages;
+}
+
+QList<QShaderDescription::InOutVariable> QShaderDescription::separateSamplers() const
+{
+ return d->separateSamplers;
+}
+
/*!
\return the list of image variables.
@@ -579,6 +591,7 @@ static struct TypeTab {
{ QLatin1String("samplerRect"), QShaderDescription::SamplerRect },
{ QLatin1String("samplerBuffer"), QShaderDescription::SamplerBuffer },
{ QLatin1String("samplerExternalOES"), QShaderDescription::SamplerExternalOES },
+ { QLatin1String("sampler"), QShaderDescription::Sampler },
{ QLatin1String("mat2x3"), QShaderDescription::Mat2x3 },
{ QLatin1String("mat2x4"), QShaderDescription::Mat2x4 },
@@ -708,7 +721,9 @@ QDebug operator<<(QDebug dbg, const QShaderDescription &sd)
<< " pcBlocks " << d->pushConstantBlocks
<< " storageBlocks " << d->storageBlocks
<< " combinedSamplers " << d->combinedImageSamplers
- << " images " << d->storageImages
+ << " storageImages " << d->storageImages
+ << " separateImages " << d->separateImages
+ << " separateSamplers " << d->separateSamplers
<< ')';
} else {
dbg.nospace() << "QShaderDescription(null)";
@@ -818,6 +833,8 @@ static const QString storageBlocksKey = QLatin1String("storageBlocks");
static const QString combinedImageSamplersKey = QLatin1String("combinedImageSamplers");
static const QString storageImagesKey = QLatin1String("storageImages");
static const QString localSizeKey = QLatin1String("localSize");
+static const QString separateImagesKey = QLatin1String("separateImages");
+static const QString separateSamplersKey = QLatin1String("separateSamplers");
static void addDeco(QJsonObject *obj, const QShaderDescription::InOutVariable &v)
{
@@ -1007,6 +1024,28 @@ QJsonDocument QShaderDescriptionPrivate::makeDoc()
jlocalSize.append(QJsonValue(int(localSize[i])));
root[localSizeKey] = jlocalSize;
+ QJsonArray jseparateImages;
+ for (const QShaderDescription::InOutVariable &v : qAsConst(separateImages)) {
+ QJsonObject image;
+ image[nameKey] = QString::fromUtf8(v.name);
+ image[typeKey] = typeStr(v.type);
+ addDeco(&image, v);
+ jseparateImages.append(image);
+ }
+ if (!jseparateImages.isEmpty())
+ root[separateImagesKey] = jseparateImages;
+
+ QJsonArray jseparateSamplers;
+ for (const QShaderDescription::InOutVariable &v : qAsConst(separateSamplers)) {
+ QJsonObject sampler;
+ sampler[nameKey] = QString::fromUtf8(v.name);
+ sampler[typeKey] = typeStr(v.type);
+ addDeco(&sampler, v);
+ jseparateSamplers.append(sampler);
+ }
+ if (!jseparateSamplers.isEmpty())
+ root[separateSamplersKey] = jseparateSamplers;
+
return QJsonDocument(root);
}
@@ -1069,6 +1108,20 @@ void QShaderDescriptionPrivate::writeToStream(QDataStream *stream)
for (size_t i = 0; i < 3; ++i)
(*stream) << localSize[i];
+
+ (*stream) << int(separateImages.count());
+ for (const QShaderDescription::InOutVariable &v : qAsConst(separateImages)) {
+ (*stream) << QString::fromUtf8(v.name);
+ (*stream) << int(v.type);
+ serializeDecorations(stream, v);
+ }
+
+ (*stream) << int(separateSamplers.count());
+ for (const QShaderDescription::InOutVariable &v : qAsConst(separateSamplers)) {
+ (*stream) << QString::fromUtf8(v.name);
+ (*stream) << int(v.type);
+ serializeDecorations(stream, v);
+ }
}
static void deserializeDecorations(QDataStream *stream, int version, QShaderDescription::InOutVariable *v)
@@ -1220,6 +1273,32 @@ void QShaderDescriptionPrivate::loadFromStream(QDataStream *stream, int version)
for (size_t i = 0; i < 3; ++i)
(*stream) >> localSize[i];
+
+ if (version > QShaderPrivate::QSB_VERSION_WITHOUT_SEPARATE_IMAGES_AND_SAMPLERS) {
+ (*stream) >> count;
+ separateImages.resize(count);
+ for (int i = 0; i < count; ++i) {
+ QString tmp;
+ (*stream) >> tmp;
+ separateImages[i].name = tmp.toUtf8();
+ int t;
+ (*stream) >> t;
+ separateImages[i].type = QShaderDescription::VariableType(t);
+ deserializeDecorations(stream, version, &separateImages[i]);
+ }
+
+ (*stream) >> count;
+ separateSamplers.resize(count);
+ for (int i = 0; i < count; ++i) {
+ QString tmp;
+ (*stream) >> tmp;
+ separateSamplers[i].name = tmp.toUtf8();
+ int t;
+ (*stream) >> t;
+ separateSamplers[i].type = QShaderDescription::VariableType(t);
+ deserializeDecorations(stream, version, &separateSamplers[i]);
+ }
+ }
}
/*!
@@ -1239,6 +1318,8 @@ bool operator==(const QShaderDescription &lhs, const QShaderDescription &rhs) no
&& lhs.d->pushConstantBlocks == rhs.d->pushConstantBlocks
&& lhs.d->storageBlocks == rhs.d->storageBlocks
&& lhs.d->combinedImageSamplers == rhs.d->combinedImageSamplers
+ && lhs.d->separateImages == rhs.d->separateImages
+ && lhs.d->separateSamplers == rhs.d->separateSamplers
&& lhs.d->storageImages == rhs.d->storageImages
&& lhs.d->localSize == rhs.d->localSize;
}
diff --git a/src/gui/rhi/qshaderdescription_p.h b/src/gui/rhi/qshaderdescription_p.h
index edaa964527..347fbfd36d 100644
--- a/src/gui/rhi/qshaderdescription_p.h
+++ b/src/gui/rhi/qshaderdescription_p.h
@@ -137,6 +137,7 @@ public:
SamplerRect,
SamplerBuffer,
SamplerExternalOES,
+ Sampler,
Image1D,
Image2D,
@@ -259,6 +260,8 @@ public:
QList<PushConstantBlock> pushConstantBlocks() const;
QList<StorageBlock> storageBlocks() const;
QList<InOutVariable> combinedImageSamplers() const;
+ QList<InOutVariable> separateImages() const;
+ QList<InOutVariable> separateSamplers() const;
QList<InOutVariable> storageImages() const;
std::array<uint, 3> computeShaderLocalSize() const;
diff --git a/src/gui/rhi/qshaderdescription_p_p.h b/src/gui/rhi/qshaderdescription_p_p.h
index e0bed856b7..0ef7869d7b 100644
--- a/src/gui/rhi/qshaderdescription_p_p.h
+++ b/src/gui/rhi/qshaderdescription_p_p.h
@@ -74,6 +74,8 @@ struct Q_GUI_EXPORT QShaderDescriptionPrivate
pushConstantBlocks(other->pushConstantBlocks),
storageBlocks(other->storageBlocks),
combinedImageSamplers(other->combinedImageSamplers),
+ separateImages(other->separateImages),
+ separateSamplers(other->separateSamplers),
storageImages(other->storageImages),
localSize(other->localSize)
{
@@ -93,6 +95,8 @@ struct Q_GUI_EXPORT QShaderDescriptionPrivate
QList<QShaderDescription::PushConstantBlock> pushConstantBlocks;
QList<QShaderDescription::StorageBlock> storageBlocks;
QList<QShaderDescription::InOutVariable> combinedImageSamplers;
+ QList<QShaderDescription::InOutVariable> separateImages;
+ QList<QShaderDescription::InOutVariable> separateSamplers;
QList<QShaderDescription::InOutVariable> storageImages;
std::array<uint, 3> localSize;
};