1// Copyright (C) 2022 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
3// Qt-Security score:critical reason:data-parser
4
5#include "qsbinspectorhelper.h"
6#include <QFile>
7#include <QCollator>
8#include <QUrl>
9
10// Note: These methods should be kept up-to-date with the qsb tool.
11
12static QString stageStr(QShader::Stage stage)
13{
14 switch (stage) {
15 case QShader::VertexStage:
16 return QStringLiteral("Vertex");
17 case QShader::TessellationControlStage:
18 return QStringLiteral("TessellationControl");
19 case QShader::TessellationEvaluationStage:
20 return QStringLiteral("TessellationEvaluation");
21 case QShader::GeometryStage:
22 return QStringLiteral("Geometry");
23 case QShader::FragmentStage:
24 return QStringLiteral("Fragment");
25 case QShader::ComputeStage:
26 return QStringLiteral("Compute");
27 default:
28 Q_UNREACHABLE();
29 }
30}
31static QString sourceStr(QShader::Source source)
32{
33 switch (source) {
34 case QShader::SpirvShader:
35 return QStringLiteral("SPIR-V");
36 case QShader::GlslShader:
37 return QStringLiteral("GLSL");
38 case QShader::HlslShader:
39 return QStringLiteral("HLSL");
40 case QShader::DxbcShader:
41 return QStringLiteral("DXBC");
42 case QShader::MslShader:
43 return QStringLiteral("MSL");
44 case QShader::DxilShader:
45 return QStringLiteral("DXIL");
46 case QShader::MetalLibShader:
47 return QStringLiteral("metallib");
48 default:
49 Q_UNREACHABLE();
50 }
51}
52
53static QString sourceVersionStr(const QShaderVersion &v)
54{
55 QString s = v.version() ? QString::number(v.version()) : QString();
56 if (v.flags().testFlag(flag: QShaderVersion::GlslEs))
57 s += QLatin1String(" es");
58
59 return s;
60}
61
62QsbInspectorHelper::QsbInspectorHelper(QObject *parent)
63 : QObject{parent}
64{
65
66}
67
68QsbShaderData QsbInspectorHelper::shaderData() const
69{
70 return m_shaderData;
71}
72
73QVariantList QsbInspectorHelper::sourceSelectorModel() const
74{
75 return m_sourceSelectorModel;
76}
77
78int QsbInspectorHelper::currentSourceIndex() const
79{
80 return m_currentSourceIndex;
81}
82
83void QsbInspectorHelper::setCurrentSourceIndex(int index)
84{
85 if (m_currentSourceIndex == index)
86 return;
87 m_currentSourceIndex = index;
88 Q_EMIT currentSourceIndexChanged();
89 // When index changes, update source also
90 Q_EMIT currentSourceCodeChanged();
91}
92
93QString QsbInspectorHelper::currentSourceCode() const
94{
95 if (m_currentSourceIndex == -1 || m_currentSourceIndex >= m_sourceCodes.size())
96 return QString();
97 return m_sourceCodes.at(i: m_currentSourceIndex);
98}
99
100// Loads QSB from filename and updates all data
101bool QsbInspectorHelper::loadQsb(const QString &filename)
102{
103 QString qsbFilename = filename;
104 QUrl url(filename);
105 if (url.scheme() == QStringLiteral("file"))
106 qsbFilename = url.toLocalFile();
107
108 QFile qsbFile(qsbFilename);
109 if (!qsbFile.open(flags: QIODevice::ReadOnly)) {
110 qWarning(msg: "Failed to open %s", qPrintable(qsbFilename));
111 return false;
112 }
113
114 QByteArray buf = qsbFile.readAll();
115 if (buf.isEmpty()) {
116 qWarning(msg: "Empty QSB file %s", qPrintable(qsbFilename));
117 return false;
118 }
119
120 QShader bs = QShader::fromSerialized(data: buf);
121 if (!bs.isValid()) {
122 qWarning(msg: "Invalid QSB file %s", qPrintable(qsbFilename));
123 return false;
124 }
125
126 const auto keys = bs.availableShaders();
127
128 m_shaderData.m_currentFile = qsbFile.fileName();
129 m_shaderData.m_stage = stageStr(stage: bs.stage());
130 m_shaderData.m_size = qsbFile.size();
131 m_shaderData.m_shaderCount = keys.count();
132 m_shaderData.m_qsbVersion = QShaderPrivate::get(s: &bs)->qsbVersion;
133 m_shaderData.m_reflectionInfo = bs.description().toJson();
134
135 m_sourceCodes.clear();
136 QVariantList sourceSelectorModel;
137 for (int i = 0; i < m_shaderData.m_shaderCount; ++i) {
138 const auto shaderKey = keys[i];
139 ShaderSelectorData selectorData;
140 selectorData.m_sourceIndex = i;
141 QString name = QString("%1 %2").arg(a: sourceStr(source: shaderKey.source()))
142 .arg(a: sourceVersionStr(v: shaderKey.sourceVersion()));
143 selectorData.m_name = name;
144 sourceSelectorModel << QVariant::fromValue(value: selectorData);
145
146 QShaderCode shaderCode = bs.shader(key: keys[i]);
147 switch (shaderKey.source()) {
148 case QShader::SpirvShader:
149 Q_FALLTHROUGH();
150 case QShader::DxbcShader:
151 Q_FALLTHROUGH();
152 case QShader::DxilShader:
153 Q_FALLTHROUGH();
154 case QShader::MetalLibShader:
155 // For binary shaders show just information
156 m_sourceCodes << QString("%1 binary of %2 bytes").arg(a: selectorData.m_name).arg(a: shaderCode.shader().size());
157 break;
158 default:
159 m_sourceCodes << shaderCode.shader();
160 break;
161 }
162 }
163
164 // QSB can contain shaders in any order, for our use
165 // sort them to alphaberical order in UI.
166 struct {
167 bool operator()(QVariant a, QVariant b) const {
168 ShaderSelectorData t1 = a.value<ShaderSelectorData>();
169 ShaderSelectorData t2 = b.value<ShaderSelectorData>();
170 return t1.m_name < t2.m_name;
171 }
172 } selectorDataSort;
173 std::sort(first: sourceSelectorModel.begin(), last: sourceSelectorModel.end(), comp: selectorDataSort);
174
175 // Add info as the first element, with index -1
176 ShaderSelectorData infoSelectorData;
177 infoSelectorData.m_sourceIndex = -1;
178 infoSelectorData.m_name = "DETAILS";
179 sourceSelectorModel.prepend(t: QVariant::fromValue(value: infoSelectorData));
180
181 if (sourceSelectorModel.size() != m_sourceSelectorModel.size()) {
182 // When the QSB shaders model has changed, select the deltails
183 m_sourceSelectorModel = sourceSelectorModel;
184 setCurrentSourceIndex(-1);
185 Q_EMIT sourceSelectorModelChanged();
186 }
187
188 // Source codes and shader data are always updated
189 Q_EMIT currentSourceCodeChanged();
190 Q_EMIT shaderDataChanged();
191
192 return true;
193}
194

source code of qtquickeffectmaker/tools/qqem/qsbinspectorhelper.cpp