7#include <QtCore/QLockFile>
8#include <QtCore/QSaveFile>
9#include <QtCore/QCryptographicHash>
10#include <QtCore/QLoggingCategory>
15Q_STATIC_LOGGING_CATEGORY(lcQuick3DCache,
"qt.quick3d.cache")
17QQsbCollection::~QQsbCollection()
21QDataStream &operator<<(QDataStream &stream,
const QQsbCollection::Entry &entry)
23 return (stream << entry.key << entry.value);
26QDataStream &
operator>>(QDataStream &stream, QQsbCollection::Entry &entry)
30 stream >> key >> value;
31 entry = QQsbCollection::Entry(key, value);
40bool operator==(
const QQsbCollection::Entry &l,
const QQsbCollection::Entry &r)
42 return (l.key == r.key);
45QDataStream &operator<<(QDataStream &stream,
const QQsbCollection::EntryDesc &entryDesc)
47 return (stream << entryDesc.materialKey
48 << entryDesc.featureSet
49 << entryDesc.vertShader.serialized()
50 << entryDesc.fragShader.serialized());
53QDataStream &
operator>>(QDataStream &stream, QQsbCollection::EntryDesc &entryDesc)
56 QQsbCollection::FeatureSet fs;
59 stream >> desc >> fs >> vertData >> fragData;
60 entryDesc.materialKey = desc;
61 entryDesc.featureSet = fs;
62 entryDesc.vertShader = QShader::fromSerialized(vertData);
63 entryDesc.fragShader = QShader::fromSerialized(fragData);
68static constexpr qint64 HeaderSize =
sizeof(qint64 ) +
sizeof(quint8 ) +
sizeof(quint32 ) +
sizeof(MagicaDS);
69static constexpr quint32 QtVersion = (QT_VERSION_MAJOR << 16) | (QT_VERSION_MINOR << 8) | (QT_VERSION_PATCH);
71bool QQsbCollection::readEndHeader(QDataStream &ds, qint64 *startPos, quint8 *version)
75 ds >> *startPos >> *version >> qtver >> fileId;
76 if (fileId != MagicaDS) {
77 qCDebug(lcQuick3DCache,
"Corrupt qsbc file");
80 if (*version != Version::Two) {
81 qCDebug(lcQuick3DCache,
"qsbc file has an unsupported version");
84 if (qtver != QtVersion) {
85 qCDebug(lcQuick3DCache,
"qsbc file is for a different Qt version");
91bool QQsbCollection::readEndHeader(QIODevice *device, EntryMap *entries, quint8 *version)
94 const qint64 size = device->size();
95 if (device->seek(size - HeaderSize)) {
96 QDataStream ds(device);
97 ds.setVersion(QDataStream::Qt_6_0);
99 if (readEndHeader(ds, &startPos, version)) {
100 if (startPos >= 0 && startPos < size && device->seek(startPos)) {
109void QQsbCollection::writeEndHeader(QDataStream &ds, qint64 startPos, quint8 version, quint64 magic)
111 ds << startPos << version << QtVersion << magic;
114void QQsbCollection::writeEndHeader(QIODevice *device,
const EntryMap &entries)
116 if (!device->atEnd()) {
117 device->seek(device->size() - 1);
118 Q_ASSERT(device->atEnd());
120 QDataStream ds(device);
121 ds.setVersion(QDataStream::Qt_6_0);
122 const qint64 startPos = device->pos();
124 writeEndHeader(ds, startPos, quint8(Version::Two), MagicaDS);
127QByteArray QQsbCollection::EntryDesc::generateSha(
const QByteArray &materialKey,
const FeatureSet &featureSet)
129 QCryptographicHash h(QCryptographicHash::Algorithm::Sha1);
130 h.addData(materialKey);
131 for (
auto it = featureSet.cbegin(), end = featureSet.cend(); it != end; ++it) {
135 return h.result().toHex();
138QByteArray QQsbCollection::EntryDesc::generateSha()
const
140 return generateSha(materialKey, featureSet);
143QQsbCollection::EntryMap QQsbInMemoryCollection::availableEntries()
const
145 return EntryMap(entries.keyBegin(), entries.keyEnd());
148QQsbCollection::Entry QQsbInMemoryCollection::addEntry(
const QByteArray &key,
const EntryDesc &entryDesc)
151 if (!entries.contains(e)) {
152 entries.insert(e, entryDesc);
158bool QQsbInMemoryCollection::extractEntry(Entry entry, EntryDesc &entryDesc)
160 auto it = entries.constFind(entry);
161 if (it != entries.constEnd()) {
168void QQsbInMemoryCollection::clear()
175 return name + QLatin1String(
".lck");
178bool QQsbInMemoryCollection::load(
const QString &filename)
180 QLockFile lock(lockFileName(filename));
182 qCDebug(lcQuick3DCache,
"Could not create shader cache lock file '%s'",
183 qPrintable(lock.fileName()));
188 if (!f.open(QIODevice::ReadOnly)) {
189 qCDebug(lcQuick3DCache,
"Failed to open qsbc file %s", qPrintable(filename));
195 if (!readEndHeader(&f, &entryMap, &version)) {
196 qCDebug(lcQuick3DCache,
"Ignoring qsbc file %s", qPrintable(filename));
201 const qint64 size = f.size();
205 const qsizetype entryCountBefore = entries.size();
206 for (
const Entry &e : entryMap) {
207 const qint64 offset = e.value;
208 if (e.isValid() && offset >= 0 && size > offset && f.seek(offset)) {
210 ds.setVersion(QDataStream::Qt_6_0);
213 entries.insert(Entry(e.key), entryDesc);
216 qCDebug(lcQuick3DCache,
"Loaded %d material cache entries from %s",
217 int(entries.size() - entryCountBefore),
218 qPrintable(filename));
223bool QQsbInMemoryCollection::save(
const QString &filename)
225 QLockFile lock(lockFileName(filename));
227 qCDebug(lcQuick3DCache,
"Could not create shader cache lock file '%s'",
228 qPrintable(lock.fileName()));
232#if QT_CONFIG(temporaryfile)
233 QSaveFile f(filename);
237 if (!f.open(QIODevice::WriteOnly | QIODevice::Truncate)) {
238 qCDebug(lcQuick3DCache,
"Failed to write qsbc file %s", qPrintable(filename));
243 ds.setVersion(QDataStream::Qt_6_0);
246 for (
auto it = entries.cbegin(), end = entries.cend(); it != end; ++it) {
247 const qint64 offset = f.pos();
249 entryMap.insert(Entry(it.key().key, offset));
252 writeEndHeader(&f, entryMap);
254 qCDebug(lcQuick3DCache,
"Writing %d material cache entries to %s",
255 int(entryMap.size()),
256 qPrintable(filename));
258#if QT_CONFIG(temporaryfile)
265QQsbIODeviceCollection::QQsbIODeviceCollection(
const QString &filePath)
271QQsbIODeviceCollection::QQsbIODeviceCollection(QIODevice &dev)
273 , devOwner(DeviceOwner::Extern)
278QQsbIODeviceCollection::~QQsbIODeviceCollection()
280 if (!entries.isEmpty() || device.isOpen())
284bool QQsbIODeviceCollection::map(MapMode mode)
286 if (device.isOpen()) {
288 if ((device.openMode() & QIODevice::WriteOnly) != 0) {
289 if ((device.openMode() & QIODevice::Truncate) == 0) {
290 qWarning(
"Open mode needs to have Truncate set for writing!");
293 if ((device.openMode() & QIODevice::Text) != 0) {
294 qWarning(
"Open mode can't have Text mode set!");
298 }
else if (!device.open(QIODevice::OpenMode(mode))) {
299 qWarning(
"Unable to open device!");
306 Q_ASSERT(mode == Read);
308 const bool ret = readEndHeader(&device, &entries, &version);
316void QQsbIODeviceCollection::unmap()
318 if (device.isOpen() && ((device.openMode() & Write) == Write)) {
319 if (!entries.isEmpty()) {
320 writeEndHeader(&device, entries);
322 if (devOwner == DeviceOwner::Self)
330QQsbCollection::EntryMap QQsbIODeviceCollection::availableEntries()
const
335QQsbCollection::Entry QQsbIODeviceCollection::addEntry(
const QByteArray &key,
const EntryDesc &entryDesc)
337 if (entries.contains(Entry(key)) || !map(MapMode::Write))
340 QDataStream ds(&device);
341 ds.setVersion(QDataStream::Qt_6_0);
342 const auto offset = device.pos();
344 Entry e(key, offset);
349bool QQsbIODeviceCollection::extractEntry(Entry entry, EntryDesc &entryDesc)
351 if (device.isOpen() && device.isReadable()) {
352 const qint64 offset = entry.value;
353 if (entry.isValid() && offset >= 0) {
354 const qint64 size = device.size();
355 if (size > offset && device.seek(offset)) {
356 QDataStream ds(&device);
357 ds.setVersion(QDataStream::Qt_6_0);
362 qWarning(
"Entry not found id(%s), offset(%lld)", entry.key.constData(), entry.value);
365 qWarning(
"Unable to open file for reading");
371static const char *
borderText() {
return "--------------------------------------------------------------------------------"; }
373void QQsbIODeviceCollection::dumpInfo()
375 if (map(QQsbIODeviceCollection::Read)) {
376 qDebug(
"Number of entries in collection: %zu\n", size_t(entries.size()));
378 qDebug(
"Qsbc version: %u", version);
379 for (
const auto &e : std::as_const(entries)) {
383 "Offset: %llu", borderText(), i++, borderText(), e.key.constData(), e.value);
385 QQsbCollection::EntryDesc ed;
386 if (extractEntry(e, ed)) {
387 qDebug() << ed.materialKey << Qt::endl
388 << ed.featureSet << Qt::endl
389 << ed.vertShader << Qt::endl
392 qWarning(
"Extracting Qsb entry failed!");
399void QQsbIODeviceCollection::dumpInfo(
const QString &file)
401 QQsbIODeviceCollection qsbc(file);
405void QQsbIODeviceCollection::dumpInfo(QIODevice &device)
407 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.
Combined button and popup list for selecting options.
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