Revision control

Copy as Markdown

// Copyright (C) 2022 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
// Qt-Security score:critical reason:data-parser
#include "qsbinspectorhelper.h"
#include <QFile>
#include <QCollator>
#include <QUrl>
// Note: These methods should be kept up-to-date with the qsb tool.
static QString stageStr(QShader::Stage stage)
{
switch (stage) {
case QShader::VertexStage:
return QStringLiteral("Vertex");
case QShader::TessellationControlStage:
return QStringLiteral("TessellationControl");
case QShader::TessellationEvaluationStage:
return QStringLiteral("TessellationEvaluation");
case QShader::GeometryStage:
return QStringLiteral("Geometry");
case QShader::FragmentStage:
return QStringLiteral("Fragment");
case QShader::ComputeStage:
return QStringLiteral("Compute");
default:
Q_UNREACHABLE();
}
}
static QString sourceStr(QShader::Source source)
{
switch (source) {
case QShader::SpirvShader:
return QStringLiteral("SPIR-V");
case QShader::GlslShader:
return QStringLiteral("GLSL");
case QShader::HlslShader:
return QStringLiteral("HLSL");
case QShader::DxbcShader:
return QStringLiteral("DXBC");
case QShader::MslShader:
return QStringLiteral("MSL");
case QShader::DxilShader:
return QStringLiteral("DXIL");
case QShader::MetalLibShader:
return QStringLiteral("metallib");
default:
Q_UNREACHABLE();
}
}
static QString sourceVersionStr(const QShaderVersion &v)
{
QString s = v.version() ? QString::number(v.version()) : QString();
if (v.flags().testFlag(QShaderVersion::GlslEs))
s += QLatin1String(" es");
return s;
}
QsbInspectorHelper::QsbInspectorHelper(QObject *parent)
: QObject{parent}
{
}
QsbShaderData QsbInspectorHelper::shaderData() const
{
return m_shaderData;
}
QVariantList QsbInspectorHelper::sourceSelectorModel() const
{
return m_sourceSelectorModel;
}
int QsbInspectorHelper::currentSourceIndex() const
{
return m_currentSourceIndex;
}
void QsbInspectorHelper::setCurrentSourceIndex(int index)
{
if (m_currentSourceIndex == index)
return;
m_currentSourceIndex = index;
Q_EMIT currentSourceIndexChanged();
// When index changes, update source also
Q_EMIT currentSourceCodeChanged();
}
QString QsbInspectorHelper::currentSourceCode() const
{
if (m_currentSourceIndex == -1 || m_currentSourceIndex >= m_sourceCodes.size())
return QString();
return m_sourceCodes.at(m_currentSourceIndex);
}
// Loads QSB from filename and updates all data
bool QsbInspectorHelper::loadQsb(const QString &filename)
{
QString qsbFilename = filename;
QUrl url(filename);
if (url.scheme() == QStringLiteral("file"))
qsbFilename = url.toLocalFile();
QFile qsbFile(qsbFilename);
if (!qsbFile.open(QIODevice::ReadOnly)) {
qWarning("Failed to open %s", qPrintable(qsbFilename));
return false;
}
QByteArray buf = qsbFile.readAll();
if (buf.isEmpty()) {
qWarning("Empty QSB file %s", qPrintable(qsbFilename));
return false;
}
QShader bs = QShader::fromSerialized(buf);
if (!bs.isValid()) {
qWarning("Invalid QSB file %s", qPrintable(qsbFilename));
return false;
}
const auto keys = bs.availableShaders();
m_shaderData.m_currentFile = qsbFile.fileName();
m_shaderData.m_stage = stageStr(bs.stage());
m_shaderData.m_size = qsbFile.size();
m_shaderData.m_shaderCount = keys.count();
m_shaderData.m_qsbVersion = QShaderPrivate::get(&bs)->qsbVersion;
m_shaderData.m_reflectionInfo = bs.description().toJson();
m_sourceCodes.clear();
QVariantList sourceSelectorModel;
for (int i = 0; i < m_shaderData.m_shaderCount; ++i) {
const auto shaderKey = keys[i];
ShaderSelectorData selectorData;
selectorData.m_sourceIndex = i;
QString name = QString("%1 %2").arg(sourceStr(shaderKey.source()))
.arg(sourceVersionStr(shaderKey.sourceVersion()));
selectorData.m_name = name;
sourceSelectorModel << QVariant::fromValue(selectorData);
QShaderCode shaderCode = bs.shader(keys[i]);
switch (shaderKey.source()) {
case QShader::SpirvShader:
Q_FALLTHROUGH();
case QShader::DxbcShader:
Q_FALLTHROUGH();
case QShader::DxilShader:
Q_FALLTHROUGH();
case QShader::MetalLibShader:
// For binary shaders show just information
m_sourceCodes << QString("%1 binary of %2 bytes").arg(selectorData.m_name).arg(shaderCode.shader().size());
break;
default:
m_sourceCodes << shaderCode.shader();
break;
}
}
// QSB can contain shaders in any order, for our use
// sort them to alphaberical order in UI.
struct {
bool operator()(QVariant a, QVariant b) const {
ShaderSelectorData t1 = a.value<ShaderSelectorData>();
ShaderSelectorData t2 = b.value<ShaderSelectorData>();
return t1.m_name < t2.m_name;
}
} selectorDataSort;
std::sort(sourceSelectorModel.begin(), sourceSelectorModel.end(), selectorDataSort);
// Add info as the first element, with index -1
ShaderSelectorData infoSelectorData;
infoSelectorData.m_sourceIndex = -1;
infoSelectorData.m_name = "DETAILS";
sourceSelectorModel.prepend(QVariant::fromValue(infoSelectorData));
if (sourceSelectorModel.size() != m_sourceSelectorModel.size()) {
// When the QSB shaders model has changed, select the deltails
m_sourceSelectorModel = sourceSelectorModel;
setCurrentSourceIndex(-1);
Q_EMIT sourceSelectorModelChanged();
}
// Source codes and shader data are always updated
Q_EMIT currentSourceCodeChanged();
Q_EMIT shaderDataChanged();
return true;
}