ff4ff35918
Red Bear OS is a full fork. All sources must be available from git clone with zero network access. Removed gitignore rules that excluded fetched source trees under recipes/*/source/, local/recipes/kde/*/source/, local/recipes/qt/*/source/, and vendor source trees. Build artifacts (target/, build/, source.tar, *.o, *.so) remain excluded. 127291 files added — kernel, relibc, base, bootloader, pkgar, all KDE/Qt frameworks, mesa, wayland, DRM drivers, and every other recipe source.
274 lines
9.9 KiB
C++
274 lines
9.9 KiB
C++
// Copyright (C) 2020 The Qt Company Ltd.
|
|
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
|
|
|
|
#include <QCoreApplication>
|
|
|
|
#include <QXmlStreamWriter>
|
|
#include <qlist.h>
|
|
|
|
#include <QCommandLineOption>
|
|
#include <QCommandLineParser>
|
|
|
|
#include <QtCore/qfile.h>
|
|
#include <QtCore/qdir.h>
|
|
|
|
#include <QtQuick3DUtils/private/qqsbcollection_p.h>
|
|
|
|
#include "genshaders.h"
|
|
|
|
#include "parser.h"
|
|
|
|
constexpr int DEAFULT_SEARCH_DEPTH = 0x10;
|
|
|
|
static int generateShaders(QVector<QString> &qsbcFiles,
|
|
const QVector<QString> &filePaths,
|
|
const QDir &sourceDir,
|
|
const QDir &outDir,
|
|
bool multilight,
|
|
bool verboseOutput,
|
|
bool dryRun)
|
|
{
|
|
MaterialParser::SceneData sceneData;
|
|
if (MaterialParser::parseQmlFiles(filePaths, sourceDir, sceneData, verboseOutput) == 0) {
|
|
if (sceneData.hasData()) {
|
|
GenShaders genShaders;
|
|
if (!genShaders.process(sceneData, qsbcFiles, outDir, multilight, dryRun))
|
|
return -1;
|
|
} else if (verboseOutput) {
|
|
if (!sceneData.viewport)
|
|
qWarning() << "No View3D item found";
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static int writeResourceFile(const QString &resourceFile,
|
|
const QVector<QString> &qsbcFiles,
|
|
const QDir &outDir)
|
|
{
|
|
if (qsbcFiles.isEmpty())
|
|
return -1;
|
|
|
|
const QString outFilename = outDir.canonicalPath() + QDir::separator() + resourceFile;
|
|
QFile outFile(outFilename);
|
|
if (!outFile.open(QFile::WriteOnly | QFile::Text | QFile::Truncate)) {
|
|
qWarning() << "Unable to create output file " << outFilename;
|
|
return -1;
|
|
}
|
|
|
|
QXmlStreamWriter writer(&outFile);
|
|
writer.setAutoFormatting(true);
|
|
writer.writeStartElement("RCC");
|
|
writer.writeStartElement("qresource");
|
|
writer.writeAttribute("prefix", "/");
|
|
for (const auto &f : qsbcFiles)
|
|
writer.writeTextElement("file", f);
|
|
writer.writeEndElement();
|
|
writer.writeEndElement();
|
|
outFile.close();
|
|
|
|
return 0;
|
|
}
|
|
|
|
struct SearchDepthGuard
|
|
{
|
|
explicit SearchDepthGuard(int m) : max(m) {}
|
|
int value = 0;
|
|
const int max = DEAFULT_SEARCH_DEPTH;
|
|
};
|
|
|
|
static void collectQmlFiles(const QList<QString> &pathArgs, QSet<QString> &filePaths, SearchDepthGuard &depth)
|
|
{
|
|
QFileInfo fi;
|
|
QDir dir;
|
|
for (const auto &arg : pathArgs) {
|
|
fi.setFile(arg);
|
|
if (fi.isFile()) {
|
|
if (fi.suffix() == QLatin1String("qml"))
|
|
filePaths.insert(fi.canonicalFilePath());
|
|
} else if (fi.isDir() && depth.value <= depth.max) {
|
|
dir.setPath(fi.filePath());
|
|
const auto entries = dir.entryList(QDir::Filter::Dirs | QDir::Filter::Files | QDir::Filter::NoDotAndDotDot);
|
|
const QString currentPath = QDir::currentPath();
|
|
QDir::setCurrent(dir.path());
|
|
++depth.value;
|
|
collectQmlFiles(entries, filePaths, depth);
|
|
--depth.value;
|
|
QDir::setCurrent(currentPath);
|
|
}
|
|
}
|
|
}
|
|
|
|
int main(int argc, char *argv[])
|
|
{
|
|
QCoreApplication a(argc, argv);
|
|
|
|
QCommandLineParser cmdLineparser;
|
|
cmdLineparser.setApplicationDescription("Pre-generates material shaders for Qt Quick 3D");
|
|
cmdLineparser.addHelpOption();
|
|
// File options
|
|
QCommandLineOption changeDirOption({QChar(u'C'), QLatin1String("directory")},
|
|
QLatin1String("Change the working directory"),
|
|
QLatin1String("dir"));
|
|
cmdLineparser.addOption(changeDirOption);
|
|
|
|
// Debug options
|
|
QCommandLineOption verboseOutputOption({QChar(u'v'), QLatin1String("verbose")}, QLatin1String("Turn on verbose output."));
|
|
cmdLineparser.addOption(verboseOutputOption);
|
|
|
|
// Generator options
|
|
QCommandLineOption dryRunOption({QChar(u'n'), QLatin1String("dry-run")}, QLatin1String("Runs as normal, but no files are created."));
|
|
cmdLineparser.addOption(dryRunOption);
|
|
|
|
QCommandLineOption outputDirOption({QChar(u'o'), QLatin1String("output-dir")}, QLatin1String("Output directory for generated files."), QLatin1String("file"));
|
|
cmdLineparser.addOption(outputDirOption);
|
|
|
|
QCommandLineOption resourceFileOption({QChar(u'r'), QLatin1String("resource-file")}, QLatin1String("Name of generated resource file."), QLatin1String("file"));
|
|
cmdLineparser.addOption(resourceFileOption);
|
|
|
|
QCommandLineOption dumpQsbcFileOption({QChar(u'l'), QLatin1String("list-qsbc")}, QLatin1String("Lists qsbc file content."));
|
|
cmdLineparser.addOption(dumpQsbcFileOption);
|
|
|
|
QCommandLineOption extractQsbFileOption({QChar(u'e'), QLatin1String("extract-qsb")}, QLatin1String("Extract qsb from collection."), QLatin1String("key:[desc|vert|frag]"));
|
|
cmdLineparser.addOption(extractQsbFileOption);
|
|
|
|
QCommandLineOption dirDepthOption(QLatin1String("depth"), QLatin1String("Override default max depth (16) value when traversing the filesystem."), QLatin1String("number"));
|
|
cmdLineparser.addOption(dirDepthOption);
|
|
|
|
cmdLineparser.process(a);
|
|
|
|
if (cmdLineparser.isSet(changeDirOption)) {
|
|
const auto value = cmdLineparser.value(changeDirOption);
|
|
QFileInfo fi(value);
|
|
if (!fi.isDir()) {
|
|
qWarning("%s : %s - Not a directory", qPrintable(a.applicationName()), qPrintable(value));
|
|
return -1;
|
|
}
|
|
QDir::setCurrent(value);
|
|
}
|
|
|
|
QSet<QString> filePaths;
|
|
auto args = cmdLineparser.positionalArguments();
|
|
|
|
const bool collectQmlFilesMode = !(cmdLineparser.isSet(dumpQsbcFileOption) || cmdLineparser.isSet(extractQsbFileOption));
|
|
if (collectQmlFilesMode) {
|
|
if (args.isEmpty())
|
|
args.push_back(QDir::currentPath());
|
|
|
|
int searchDepth = DEAFULT_SEARCH_DEPTH;
|
|
if (cmdLineparser.isSet(dirDepthOption)) {
|
|
bool ok = false;
|
|
const int v = cmdLineparser.value(dirDepthOption).toInt(&ok);
|
|
if (ok)
|
|
searchDepth = v;
|
|
}
|
|
|
|
SearchDepthGuard depth(searchDepth);
|
|
collectQmlFiles(args, filePaths, depth);
|
|
} else if (!args.isEmpty()) {
|
|
filePaths.insert(args.first());
|
|
}
|
|
|
|
if (filePaths.isEmpty()) {
|
|
qWarning("No input file(s) found!");
|
|
a.exit(-1);
|
|
return -1;
|
|
}
|
|
|
|
if (cmdLineparser.isSet(dumpQsbcFileOption)) {
|
|
const auto &f = *filePaths.cbegin();
|
|
if (!f.isEmpty()) {
|
|
QQsbIODeviceCollection::dumpInfo(f);
|
|
a.exit(0);
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
static const auto printBytes = [](const QByteArray &ba) {
|
|
for (const auto &b : ba)
|
|
printf("%c", b);
|
|
};
|
|
|
|
if (cmdLineparser.isSet(extractQsbFileOption)) {
|
|
const auto &f = *filePaths.cbegin();
|
|
const auto k = cmdLineparser.value(extractQsbFileOption);
|
|
const auto kl = QStringView(k).split(u':');
|
|
|
|
const auto &keyView = kl.at(0);
|
|
const QByteArray key = keyView.toLatin1();
|
|
enum ExtractWhat : quint8 { Desc = 0x1, Vert = 0x2, Frag = 0x4 };
|
|
quint8 what = 0;
|
|
if (kl.size() > 1) {
|
|
const auto &rest = kl.at(1);
|
|
const auto &options = rest.split(u'|');
|
|
for (const auto &o : options) {
|
|
if (o == QLatin1String("desc"))
|
|
what |= ExtractWhat::Desc;
|
|
if (o == QLatin1String("vert"))
|
|
what |= ExtractWhat::Vert;
|
|
if (o == QLatin1String("frag"))
|
|
what |= ExtractWhat::Frag;
|
|
}
|
|
}
|
|
QQsbIODeviceCollection qsbc(f);
|
|
if (qsbc.map(QQsbIODeviceCollection::Read)) {
|
|
const auto entries = qsbc.availableEntries();
|
|
const auto foundIt = entries.constFind(QQsbCollection::Entry(key));
|
|
if (foundIt != entries.cend()) {
|
|
QQsbCollection::EntryDesc ed;
|
|
qsbc.extractEntry(*foundIt, ed);
|
|
if (what == 0)
|
|
qDebug("Entry with key %s found.", key.constData());
|
|
if (what & ExtractWhat::Desc)
|
|
printBytes(ed.materialKey);
|
|
if (what & ExtractWhat::Vert)
|
|
printBytes(qUncompress(ed.vertShader.serialized()));
|
|
if (what & ExtractWhat::Frag)
|
|
printBytes(qUncompress(ed.fragShader.serialized()));
|
|
} else {
|
|
qWarning("Entry with key %s could not be found.", key.constData());
|
|
}
|
|
qsbc.unmap();
|
|
}
|
|
a.exit(0);
|
|
return 0;
|
|
|
|
qWarning("Command %s failed with input: %s and %s.", qPrintable(extractQsbFileOption.valueName()), qPrintable(f), qPrintable(k));
|
|
a.exit(-1);
|
|
return -1;
|
|
}
|
|
|
|
QString resourceFile = cmdLineparser.value(resourceFileOption);
|
|
if (resourceFile.isEmpty())
|
|
resourceFile = QStringLiteral("genshaders.qrc");
|
|
|
|
const bool dryRun = cmdLineparser.isSet(dryRunOption);
|
|
const QString &outputPath = cmdLineparser.isSet(outputDirOption) ? cmdLineparser.value(outputDirOption) : QDir::currentPath();
|
|
QDir outDir;
|
|
if (!outputPath.isEmpty() && !dryRun) {
|
|
outDir.setPath(outputPath);
|
|
if (outDir.exists(outputPath) || (!outDir.exists(outputPath) && outDir.mkpath(outputPath))) {
|
|
outDir.setPath(outputPath);
|
|
qDebug("Writing files to %s", qPrintable(outDir.canonicalPath()));
|
|
} else {
|
|
qDebug("Unable to change or create output folder %s", qPrintable(outputPath));
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
const bool verboseOutput = cmdLineparser.isSet(verboseOutputOption);
|
|
const bool multilight = false;
|
|
|
|
QVector<QString> qsbcFiles;
|
|
|
|
int ret = 0;
|
|
if (filePaths.size())
|
|
ret = generateShaders(qsbcFiles, filePaths.values(), QDir::currentPath(), outDir, multilight, verboseOutput, dryRun);
|
|
|
|
if (ret == 0 && !dryRun)
|
|
writeResourceFile(resourceFile, qsbcFiles, outDir);
|
|
|
|
a.exit(ret);
|
|
return ret;
|
|
}
|