Move away from CBOR in QShaderDescription serialization

...but keep support for deserializing for all older versions in order to play
nice with existing .qsb files.

The usage of binary JSON and then CBOR is a historical artifact: relying
on the QJsonDocument (which we generate for purposes unrelated to binary
serialization) was a convenient shortcut. However, writing to and
reading from a QDataStream instead (which QShader already does) is trivial.
In order not to be limited by potential CBOR requirements in the future,
take it all into our own hands.

Extend the qshader autotest accordingly.

Task-number: QTBUG-81298
Change-Id: If0047b659bd6601ca47b5bbbce1b719630cde01e
Reviewed-by: Paul Olav Tvete <paul.tvete@qt.io>
This commit is contained in:
Laszlo Agocs 2020-01-12 17:33:31 +01:00
parent 9a0ab41f28
commit 7e2cef0f15
7 changed files with 374 additions and 14 deletions

View File

@ -367,7 +367,7 @@ QByteArray QShader::serialized() const
ds << QShaderPrivate::QSB_VERSION;
ds << int(d->stage);
ds << d->desc.toCbor();
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());
@ -428,6 +428,7 @@ QShader QShader::fromSerialized(const QByteArray &data)
ds >> intVal;
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)
{
@ -437,14 +438,18 @@ QShader QShader::fromSerialized(const QByteArray &data)
ds >> intVal;
d->stage = Stage(intVal);
QByteArray descBin;
ds >> descBin;
if (d->qsbVersion > QShaderPrivate::QSB_VERSION_WITH_BINARY_JSON) {
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

View File

@ -57,7 +57,8 @@ QT_BEGIN_NAMESPACE
struct Q_GUI_EXPORT QShaderPrivate
{
static const int QSB_VERSION = 3;
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;

View File

@ -36,6 +36,7 @@
#include "qshaderdescription_p_p.h"
#include <QDebug>
#include <QDataStream>
#include <QJsonObject>
#include <QJsonArray>
#include <QCborValue>
@ -358,6 +359,11 @@ 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
@ -396,6 +402,13 @@ QShaderDescription QShaderDescription::fromCbor(const QByteArray &data)
return desc;
}
QShaderDescription QShaderDescription::deserialize(QDataStream *stream)
{
QShaderDescription desc;
QShaderDescriptionPrivate::get(&desc)->loadFromStream(stream);
return desc;
}
/*!
\return the list of input variables. This includes vertex inputs (sometimes
called attributes) for the vertex stage, and inputs for other stages
@ -867,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;
@ -876,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;
@ -904,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;
@ -1002,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;
@ -1020,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;
@ -1046,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()) {
@ -1150,6 +1304,87 @@ 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.

View File

@ -56,6 +56,7 @@
QT_BEGIN_NAMESPACE
struct QShaderDescriptionPrivate;
class QDataStream;
class Q_GUI_EXPORT QShaderDescription
{
@ -69,6 +70,7 @@ public:
bool isValid() const;
QByteArray toCbor() const;
void serialize(QDataStream *stream) const;
QByteArray toJson() const;
#if QT_CONFIG(binaryjson) && QT_DEPRECATED_SINCE(5, 15)
@ -76,6 +78,7 @@ public:
static QShaderDescription fromBinaryJson(const QByteArray &data);
#endif
static QShaderDescription fromCbor(const QByteArray &data);
static QShaderDescription deserialize(QDataStream *stream);
enum VariableType {
Unknown = 0,

View File

@ -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;

View File

@ -43,6 +43,8 @@ private slots:
void mslResourceMapping();
void loadV3();
void serializeShaderDesc();
void comparison();
void loadV4();
};
static QShader getShader(const QString &name)
@ -353,21 +355,38 @@ void tst_QShader::serializeShaderDesc()
QShaderDescription desc;
QVERIFY(!desc.isValid());
const QByteArray data = desc.toCbor();
QByteArray data;
{
QBuffer buf(&data);
QDataStream ds(&buf);
QVERIFY(buf.open(QIODevice::WriteOnly));
desc.serialize(&ds);
}
QVERIFY(!data.isEmpty());
QShaderDescription desc2 = QShaderDescription::fromCbor(data);
QVERIFY(!desc2.isValid());
{
QBuffer buf(&data);
QDataStream ds(&buf);
QVERIFY(buf.open(QIODevice::ReadOnly));
QShaderDescription desc2 = QShaderDescription::deserialize(&ds);
QVERIFY(!desc2.isValid());
}
}
// a QShaderDescription with inputs, outputs, uniform block and combined image sampler
{
QShader s = getShader(QLatin1String(":/data/texture_all_v3.frag.qsb"));
QShader s = getShader(QLatin1String(":/data/texture_all_v4.frag.qsb"));
QVERIFY(s.isValid());
const QShaderDescription desc = s.description();
QVERIFY(desc.isValid());
const QByteArray data = desc.toCbor();
QByteArray data;
{
QBuffer buf(&data);
QDataStream ds(&buf);
QVERIFY(buf.open(QIODevice::WriteOnly));
desc.serialize(&ds);
}
QVERIFY(!data.isEmpty());
QShaderDescription desc2;
@ -375,14 +394,22 @@ void tst_QShader::serializeShaderDesc()
QVERIFY(!(desc == desc2));
QVERIFY(desc != desc2);
desc2 = QShaderDescription::fromCbor(data);
QVERIFY(desc2.isValid());
QCOMPARE(desc, desc2);
{
QBuffer buf(&data);
QDataStream ds(&buf);
QVERIFY(buf.open(QIODevice::ReadOnly));
QShaderDescription desc2 = QShaderDescription::deserialize(&ds);
QVERIFY(desc2.isValid());
QCOMPARE(desc, desc2);
}
}
}
void tst_QShader::comparison()
{
// exercise QShader and QShaderDescription comparisons
{
QShader s1 = getShader(QLatin1String(":/data/texture_all_v3.frag.qsb"));
QShader s1 = getShader(QLatin1String(":/data/texture_all_v4.frag.qsb"));
QVERIFY(s1.isValid());
QShader s2 = getShader(QLatin1String(":/data/color_all_v1.vert.qsb"));
QVERIFY(s2.isValid());
@ -393,6 +420,93 @@ void tst_QShader::serializeShaderDesc()
QVERIFY(s1 != s2);
QVERIFY(s1.description() != s2.description());
}
{
QShader s1 = getShader(QLatin1String(":/data/texture_all_v4.frag.qsb"));
QVERIFY(s1.isValid());
QShader s2 = getShader(QLatin1String(":/data/texture_all_v4.frag.qsb"));
QVERIFY(s2.isValid());
QVERIFY(s1.description().isValid());
QVERIFY(s2.description().isValid());
QVERIFY(s1 == s2);
QVERIFY(s1.description() == s2.description());
}
}
void tst_QShader::loadV4()
{
// qsb version 4: QShaderDescription is serialized via QDataStream. Ensure the deserialized data is as expected.
QShader s = getShader(QLatin1String(":/data/texture_all_v4.frag.qsb"));
QVERIFY(s.isValid());
QCOMPARE(QShaderPrivate::get(&s)->qsbVersion, 4);
const QVector<QShaderKey> availableShaders = s.availableShaders();
QCOMPARE(availableShaders.count(), 7);
QVERIFY(availableShaders.contains(QShaderKey(QShader::SpirvShader, QShaderVersion(100))));
QVERIFY(availableShaders.contains(QShaderKey(QShader::MslShader, QShaderVersion(12))));
QVERIFY(availableShaders.contains(QShaderKey(QShader::HlslShader, QShaderVersion(50))));
QVERIFY(availableShaders.contains(QShaderKey(QShader::GlslShader, QShaderVersion(100, QShaderVersion::GlslEs))));
QVERIFY(availableShaders.contains(QShaderKey(QShader::GlslShader, QShaderVersion(120))));
QVERIFY(availableShaders.contains(QShaderKey(QShader::GlslShader, QShaderVersion(150))));
QVERIFY(availableShaders.contains(QShaderKey(QShader::GlslShader, QShaderVersion(330))));
const QShaderDescription desc = s.description();
QVERIFY(desc.isValid());
QCOMPARE(desc.inputVariables().count(), 1);
for (const QShaderDescription::InOutVariable &v : desc.inputVariables()) {
switch (v.location) {
case 0:
QCOMPARE(v.name, QLatin1String("qt_TexCoord"));
QCOMPARE(v.type, QShaderDescription::Vec2);
break;
default:
QVERIFY(false);
break;
}
}
QCOMPARE(desc.outputVariables().count(), 1);
for (const QShaderDescription::InOutVariable &v : desc.outputVariables()) {
switch (v.location) {
case 0:
QCOMPARE(v.name, QLatin1String("fragColor"));
QCOMPARE(v.type, QShaderDescription::Vec4);
break;
default:
QVERIFY(false);
break;
}
}
QCOMPARE(desc.uniformBlocks().count(), 1);
const QShaderDescription::UniformBlock blk = desc.uniformBlocks().first();
QCOMPARE(blk.blockName, QLatin1String("buf"));
QCOMPARE(blk.structName, QLatin1String("ubuf"));
QCOMPARE(blk.size, 68);
QCOMPARE(blk.binding, 0);
QCOMPARE(blk.descriptorSet, 0);
QCOMPARE(blk.members.count(), 2);
for (int i = 0; i < blk.members.count(); ++i) {
const QShaderDescription::BlockVariable v = blk.members[i];
switch (i) {
case 0:
QCOMPARE(v.offset, 0);
QCOMPARE(v.size, 64);
QCOMPARE(v.name, QLatin1String("qt_Matrix"));
QCOMPARE(v.type, QShaderDescription::Mat4);
QCOMPARE(v.matrixStride, 16);
break;
case 1:
QCOMPARE(v.offset, 64);
QCOMPARE(v.size, 4);
QCOMPARE(v.name, QLatin1String("opacity"));
QCOMPARE(v.type, QShaderDescription::Float);
break;
default:
QVERIFY(false);
break;
}
}
}
#include <tst_qshader.moc>