5#include <QtCore/QLockFile>
6#include <QtCore/QSaveFile>
7#include <QtCore/QCryptographicHash>
12QQsbCollection::~QQsbCollection()
16QDataStream &operator<<(QDataStream &stream,
const QQsbCollection::Entry &entry)
18 return (stream << entry.key << entry.value);
21QDataStream &
operator>>(QDataStream &stream, QQsbCollection::Entry &entry)
25 stream >> key >> value;
26 entry = QQsbCollection::Entry(key, value);
35bool operator==(
const QQsbCollection::Entry &l,
const QQsbCollection::Entry &r)
37 return (l.key == r.key);
40QDataStream &operator<<(QDataStream &stream,
const QQsbCollection::EntryDesc &entryDesc)
42 return (stream << entryDesc.materialKey
43 << entryDesc.featureSet
44 << entryDesc.vertShader.serialized()
45 << entryDesc.fragShader.serialized());
48QDataStream &
operator>>(QDataStream &stream, QQsbCollection::EntryDesc &entryDesc)
51 QQsbCollection::FeatureSet fs;
54 stream >> desc >> fs >> vertData >> fragData;
55 entryDesc.materialKey = desc;
56 entryDesc.featureSet = fs;
57 entryDesc.vertShader = QShader::fromSerialized(vertData);
58 entryDesc.fragShader = QShader::fromSerialized(fragData);
63static constexpr qint64 HeaderSize =
sizeof(qint64 ) +
sizeof(quint8 ) +
sizeof(quint32 ) +
sizeof(MagicaDS);
64static constexpr quint32 QtVersion = (QT_VERSION_MAJOR << 16) | (QT_VERSION_MINOR << 8) | (QT_VERSION_PATCH);
66bool QQsbCollection::readEndHeader(QDataStream &ds, qint64 *startPos, quint8 *version)
70 ds >> *startPos >> *version >> qtver >> fileId;
71 if (fileId != MagicaDS) {
72 qWarning(
"Corrupt qsbc file");
75 if (*version != Version::Two) {
76 qWarning(
"qsbc file has an unsupported version");
79 if (qtver != QtVersion) {
80 qWarning(
"qsbc file is for a different Qt version");
86bool QQsbCollection::readEndHeader(QIODevice *device, EntryMap *entries, quint8 *version)
89 const qint64 size = device->size();
90 if (device->seek(size - HeaderSize)) {
91 QDataStream ds(device);
92 ds.setVersion(QDataStream::Qt_6_0);
94 if (readEndHeader(ds, &startPos, version)) {
95 if (startPos >= 0 && startPos < size && device->seek(startPos)) {
104void QQsbCollection::writeEndHeader(QDataStream &ds, qint64 startPos, quint8 version, quint64 magic)
106 ds << startPos << version << QtVersion << magic;
109void QQsbCollection::writeEndHeader(QIODevice *device,
const EntryMap &entries)
111 if (!device->atEnd()) {
112 device->seek(device->size() - 1);
113 Q_ASSERT(device->atEnd());
115 QDataStream ds(device);
116 ds.setVersion(QDataStream::Qt_6_0);
117 const qint64 startPos = device->pos();
119 writeEndHeader(ds, startPos, quint8(Version::Two), MagicaDS);
122QByteArray QQsbCollection::EntryDesc::generateSha(
const QByteArray &materialKey,
const FeatureSet &featureSet)
124 QCryptographicHash h(QCryptographicHash::Algorithm::Sha1);
125 h.addData(materialKey);
126 for (
auto it = featureSet.cbegin(), end = featureSet.cend(); it != end; ++it) {
130 return h.result().toHex();
133QByteArray QQsbCollection::EntryDesc::generateSha()
const
135 return generateSha(materialKey, featureSet);
138QQsbCollection::EntryMap QQsbInMemoryCollection::availableEntries()
const
140 return EntryMap(entries.keyBegin(), entries.keyEnd());
143QQsbCollection::Entry QQsbInMemoryCollection::addEntry(
const QByteArray &key,
const EntryDesc &entryDesc)
146 if (!entries.contains(e)) {
147 entries.insert(e, entryDesc);
153bool QQsbInMemoryCollection::extractEntry(Entry entry, EntryDesc &entryDesc)
155 auto it = entries.constFind(entry);
156 if (it != entries.constEnd()) {
163void QQsbInMemoryCollection::clear()
170 return name + QLatin1String(
".lck");
173bool QQsbInMemoryCollection::load(
const QString &filename)
175 QLockFile lock(lockFileName(filename));
177 qWarning(
"Could not create shader cache lock file '%s'",
178 qPrintable(lock.fileName()));
183 if (!f.open(QIODevice::ReadOnly)) {
184 qWarning(
"Failed to open qsbc file %s", qPrintable(filename));
190 if (!readEndHeader(&f, &entryMap, &version)) {
191 qWarning(
"Ignoring qsbc file %s", qPrintable(filename));
196 const qint64 size = f.size();
200 for (
const Entry &e : entryMap) {
201 const qint64 offset = e.value;
202 if (e.isValid() && offset >= 0 && size > offset && f.seek(offset)) {
204 ds.setVersion(QDataStream::Qt_6_0);
207 entries.insert(Entry(e.key), entryDesc);
214bool QQsbInMemoryCollection::save(
const QString &filename)
216 QLockFile lock(lockFileName(filename));
218 qWarning(
"Could not create shader cache lock file '%s'",
219 qPrintable(lock.fileName()));
223#if QT_CONFIG(temporaryfile)
224 QSaveFile f(filename);
228 if (!f.open(QIODevice::WriteOnly | QIODevice::Truncate)) {
229 qWarning(
"Failed to write qsbc file %s", qPrintable(filename));
234 ds.setVersion(QDataStream::Qt_6_0);
237 for (
auto it = entries.cbegin(), end = entries.cend(); it != end; ++it) {
238 const qint64 offset = f.pos();
240 entryMap.insert(Entry(it.key().key, offset));
243 writeEndHeader(&f, entryMap);
245#if QT_CONFIG(temporaryfile)
252QQsbIODeviceCollection::QQsbIODeviceCollection(
const QString &filePath)
258QQsbIODeviceCollection::QQsbIODeviceCollection(QIODevice &dev)
260 , devOwner(DeviceOwner::Extern)
265QQsbIODeviceCollection::~QQsbIODeviceCollection()
267 if (!entries.isEmpty() || device.isOpen())
271bool QQsbIODeviceCollection::map(MapMode mode)
273 if (device.isOpen()) {
275 if ((device.openMode() & QIODevice::WriteOnly) != 0) {
276 if ((device.openMode() & QIODevice::Truncate) == 0) {
277 qWarning(
"Open mode needs to have Truncate set for writing!");
280 if ((device.openMode() & QIODevice::Text) != 0) {
281 qWarning(
"Open mode can't have Text mode set!");
285 }
else if (!device.open(QIODevice::OpenMode(mode))) {
286 qWarning(
"Unable to open device!");
293 Q_ASSERT(mode == Read);
295 const bool ret = readEndHeader(&device, &entries, &version);
303void QQsbIODeviceCollection::unmap()
305 if (device.isOpen() && ((device.openMode() & Write) == Write)) {
306 if (!entries.isEmpty()) {
307 writeEndHeader(&device, entries);
309 if (devOwner == DeviceOwner::Self)
317QQsbCollection::EntryMap QQsbIODeviceCollection::availableEntries()
const
322QQsbCollection::Entry QQsbIODeviceCollection::addEntry(
const QByteArray &key,
const EntryDesc &entryDesc)
324 if (entries.contains(Entry(key)) || !map(MapMode::Write))
327 QDataStream ds(&device);
328 ds.setVersion(QDataStream::Qt_6_0);
329 const auto offset = device.pos();
331 Entry e(key, offset);
336bool QQsbIODeviceCollection::extractEntry(Entry entry, EntryDesc &entryDesc)
338 if (device.isOpen() && device.isReadable()) {
339 const qint64 offset = entry.value;
340 if (entry.isValid() && offset >= 0) {
341 const qint64 size = device.size();
342 if (size > offset && device.seek(offset)) {
343 QDataStream ds(&device);
344 ds.setVersion(QDataStream::Qt_6_0);
349 qWarning(
"Entry not found id(%s), offset(%lld)", entry.key.constData(), entry.value);
352 qWarning(
"Unable to open file for reading");
358static const char *
borderText() {
return "--------------------------------------------------------------------------------"; }
360void QQsbIODeviceCollection::dumpInfo()
362 if (map(QQsbIODeviceCollection::Read)) {
363 qDebug(
"Number of entries in collection: %zu\n", size_t(entries.size()));
365 qDebug(
"Qsbc version: %u", version);
366 for (
const auto &e : std::as_const(entries)) {
370 "Offset: %llu", borderText(), i++, borderText(), e.key.constData(), e.value);
372 QQsbCollection::EntryDesc ed;
373 if (extractEntry(e, ed)) {
374 qDebug() << ed.materialKey << Qt::endl
375 << ed.featureSet << Qt::endl
376 << ed.vertShader << Qt::endl
379 qWarning(
"Extracting Qsb entry failed!");
386void QQsbIODeviceCollection::dumpInfo(
const QString &file)
388 QQsbIODeviceCollection qsbc(file);
392void QQsbIODeviceCollection::dumpInfo(QIODevice &device)
394 QQsbIODeviceCollection qsbc(device);
friend bool operator==(const QByteArray::FromBase64Result &lhs, const QByteArray::FromBase64Result &rhs) noexcept
Returns true if lhs and rhs are equal, otherwise returns false.
QDataStream & operator>>(QDataStream &s, QKeyCombination &combination)
static const char * borderText()
static constexpr qint64 HeaderSize
static QString lockFileName(const QString &name)
static constexpr quint64 MagicaDS
static constexpr quint32 QtVersion
constexpr size_t qHash(const QSize &s, size_t seed=0) noexcept