diff --git a/src/angle/src/d3dcompiler/main.cpp b/src/angle/src/d3dcompiler/main.cpp index 70a8f30fb0..3f00df63eb 100644 --- a/src/angle/src/d3dcompiler/main.cpp +++ b/src/angle/src/d3dcompiler/main.cpp @@ -161,31 +161,11 @@ static bool loadCompiler() return bool(compile); } -static bool serviceAvailable(const QString &path) -{ - if (path.isEmpty()) - return false; - - // Look for a file, "control", and check if it has been touched in the last 60 seconds - QFileInfo control(path + QStringLiteral("control")); - return control.exists() && control.lastModified().secsTo(QDateTime::currentDateTime()) < 60; -} - static QString cacheKeyFor(const void *data) { return QString::fromUtf8(QCryptographicHash::hash(reinterpret_cast(data), QCryptographicHash::Sha1).toHex()); } -static QString makePath(const QDir &parent, const QString &child) -{ - const QString path = parent.absoluteFilePath(child); - if (!parent.mkpath(child)) { - qCWarning(QT_D3DCOMPILER) << "Path is inaccessible: " << path; - return QString(); - } - return path; -} - } // namespace D3DCompiler #ifdef __MINGW32__ @@ -200,37 +180,56 @@ HRESULT WINAPI D3DCompile( const D3D_SHADER_MACRO *defines, ID3DInclude *include, const char *entrypoint, const char *target, UINT sflags, UINT eflags, ID3DBlob **shader, ID3DBlob **errorMsgs) { - static QString basePath; + static bool initialized = false; + static bool serviceAvailable = false; static QString binaryPath; static QString sourcePath; - if (basePath.isEmpty()) { - QDir base; - if (qEnvironmentVariableIsSet("QT_D3DCOMPILER_DIR")) - base.setPath(QString::fromUtf8(qgetenv("QT_D3DCOMPILER_DIR"))); - else - base.setPath(QStandardPaths::writableLocation(QStandardPaths::DataLocation)); - - if (!base.exists() && !base.mkdir(QStringLiteral("."))) { - qCWarning(QT_D3DCOMPILER) << "D3D compiler base directory does not exist: " << QDir::toNativeSeparators(base.path()); + if (!initialized) { + QString base; + if (qEnvironmentVariableIsSet("QT_D3DCOMPILER_DIR")) { + base = QString::fromLocal8Bit(qgetenv("QT_D3DCOMPILER_DIR")); } else { - const QString path = base.absoluteFilePath(QStringLiteral("d3dcompiler/")); - if (!QFile::exists(path) && !base.mkdir(QStringLiteral("d3dcompiler"))) - qCWarning(QT_D3DCOMPILER) << "D3D compiler path could not be created: " << QDir::toNativeSeparators(path); - else - basePath = path; + const QString location = QStandardPaths::writableLocation(QStandardPaths::DataLocation); + if (!location.isEmpty()) + base = location + QStringLiteral("/d3dcompiler"); } + + QDir baseDir(base); + if (!base.isEmpty() && baseDir.exists()) { + // Check if we have can read/write blobs + if (baseDir.exists(QStringLiteral("binary"))) { + binaryPath = baseDir.absoluteFilePath(QStringLiteral("binary/")); + } else { + qCWarning(QT_D3DCOMPILER) << "D3D compiler base directory exists, but the binary directory does not.\n" + "Check the compiler service."; + } + + // Check if we can write shader source + if (baseDir.exists(QStringLiteral("source"))) { + sourcePath = baseDir.absoluteFilePath(QStringLiteral("source/")); + } else { + qCWarning(QT_D3DCOMPILER) << "D3D compiler base directory exists, but the source directory does not.\n" + "Check the compiler service."; + } + + // Look for a file, "control", and check if it has been touched in the last 60 seconds + QFileInfo control(baseDir.absoluteFilePath(QStringLiteral("control"))); + serviceAvailable = control.exists() && control.lastModified().secsTo(QDateTime::currentDateTime()) < 60; + } else { + qCWarning(QT_D3DCOMPILER) << "D3D compiler base directory does not exist:" + << QDir::toNativeSeparators(base) + << "\nThe compiler service won't be used."; + } + + initialized = true; } - if (!basePath.isEmpty()) { - binaryPath = D3DCompiler::makePath(basePath, QStringLiteral("binary/")); - sourcePath = D3DCompiler::makePath(basePath, QStringLiteral("source/")); - } - - // Check if pre-compiled shader blob is available const QByteArray sourceData = QByteArray::fromRawData(reinterpret_cast(data), data_size); const QString cacheKey = D3DCompiler::cacheKeyFor(sourceData); - QFile blob(binaryPath + cacheKey); - if (!binaryPath.isEmpty() && blob.exists()) { + + // Check if pre-compiled shader blob is available + if (!binaryPath.isEmpty()) { + QFile blob(binaryPath + cacheKey); if (blob.open(QFile::ReadOnly)) { qCDebug(QT_D3DCOMPILER) << "Opening precompiled shader blob at" << blob.fileName(); *shader = new D3DCompiler::Blob(blob.readAll()); @@ -240,13 +239,7 @@ HRESULT WINAPI D3DCompile( } // Shader blob is not available, compile with compilation service if possible - if (D3DCompiler::serviceAvailable(basePath)) { - - if (sourcePath.isEmpty()) { - qCWarning(QT_D3DCOMPILER) << "Compiler service is available, but source directory is not writable."; - return E_ACCESSDENIED; - } - + if (!sourcePath.isEmpty() && serviceAvailable) { // Dump source to source path; wait for blob to appear QFile source(sourcePath + cacheKey); if (!source.open(QFile::WriteOnly)) { @@ -264,6 +257,7 @@ HRESULT WINAPI D3DCompile( QElapsedTimer timer; timer.start(); + QFile blob(binaryPath + cacheKey); while (!(blob.exists() && blob.open(QFile::ReadOnly)) && timer.elapsed() < timeout) QThread::msleep(100); @@ -285,6 +279,8 @@ HRESULT WINAPI D3DCompile( if (SUCCEEDED(hr) && !binaryPath.isEmpty()) { const QByteArray blobContents = QByteArray::fromRawData( reinterpret_cast((*shader)->GetBufferPointer()), (*shader)->GetBufferSize()); + + QFile blob(binaryPath + cacheKey); if (blob.open(QFile::WriteOnly) && blob.write(blobContents)) qCDebug(QT_D3DCOMPILER) << "Cached shader blob at" << blob.fileName(); else diff --git a/tests/auto/other/d3dcompiler/tst_d3dcompiler.cpp b/tests/auto/other/d3dcompiler/tst_d3dcompiler.cpp index 1a3f4f4592..f86c965d84 100644 --- a/tests/auto/other/d3dcompiler/tst_d3dcompiler.cpp +++ b/tests/auto/other/d3dcompiler/tst_d3dcompiler.cpp @@ -153,11 +153,12 @@ QString tst_d3dcompiler::blobPath() if (qEnvironmentVariableIsSet("QT_D3DCOMPILER_DIR")) path.setPath(qgetenv("QT_D3DCOMPILER_DIR")); else - path.setPath(QStandardPaths::writableLocation(QStandardPaths::DataLocation)); + path.setPath(QStandardPaths::writableLocation(QStandardPaths::DataLocation) + QStringLiteral("/d3dcompiler")); - path.mkdir(QStringLiteral("d3dcompiler")); + path.mkdir(QStringLiteral("binary")); + path.mkdir(QStringLiteral("source")); - return path.absoluteFilePath(QStringLiteral("d3dcompiler/")); + return path.absolutePath(); } void tst_d3dcompiler::initTestCase() @@ -177,7 +178,12 @@ void tst_d3dcompiler::cleanup() FreeLibrary(d3dcompiler_win); QDir path(blobPath()); - path.removeRecursively(); + foreach (const QString &entry, path.entryList(QStringList(), QDir::Files|QDir::NoDotAndDotDot)) + path.remove(entry); + foreach (const QString &entry, path.entryList(QStringList(), QDir::Dirs|QDir::NoDotAndDotDot)) { + QDir dir(path.absoluteFilePath(entry + QStringLiteral("/"))); + dir.removeRecursively(); + } } void tst_d3dcompiler::service_data() @@ -188,8 +194,9 @@ void tst_d3dcompiler::service_data() // Don't test the default case, as it would clutter the AppData directory //QTest::newRow("default") << QByteArrayLiteral("") << true << E_ABORT; - QTest::newRow("temporary") << tempDir.path().toUtf8() << true << E_ABORT; + QTest::newRow("temporary") << QFile::encodeName(tempDir.path()) << true << E_ABORT; QTest::newRow("invalid") << QByteArrayLiteral("ZZ:\\") << false << S_OK; + QTest::newRow("empty") << QByteArrayLiteral("") << false << S_OK; } void tst_d3dcompiler::service() @@ -254,6 +261,8 @@ void tst_d3dcompiler::service() void tst_d3dcompiler::offlineCompile() { + qputenv("QT_D3DCOMPILER_DIR", QFile::encodeName(tempDir.path())); + for (int i = 0; compilerDlls[i]; ++i) { d3dcompiler_win = loadLibrary(compilerDlls[i]); if (d3dcompiler_win) @@ -271,7 +280,8 @@ void tst_d3dcompiler::offlineCompile() QVERIFY(shader); QDir outputPath(blobPath()); - QVERIFY(outputPath.mkpath(QStringLiteral("binary"))); + QVERIFY(outputPath.exists()); + QVERIFY(outputPath.exists(QStringLiteral("binary"))); outputPath.cd(QStringLiteral("binary")); QFile output(outputPath.absoluteFilePath(QCryptographicHash::hash(data, QCryptographicHash::Sha1).toHex())); QVERIFY(output.open(QFile::WriteOnly)); @@ -291,6 +301,8 @@ void tst_d3dcompiler::offlineCompile() void tst_d3dcompiler::onlineCompile() { + qputenv("QT_D3DCOMPILER_DIR", QFile::encodeName(tempDir.path())); + QByteArray data(hlsl); const QDir path = blobPath(); @@ -313,8 +325,9 @@ void tst_d3dcompiler::onlineCompile() runner.start(); // Wait for source to appear - QVERIFY(path.mkpath(QStringLiteral("source"))); - QVERIFY(path.mkpath(QStringLiteral("binary"))); + QVERIFY(path.exists()); + QVERIFY(path.exists(QStringLiteral("source"))); + QVERIFY(path.exists(QStringLiteral("binary"))); const QByteArray hash = QCryptographicHash::hash(data, QCryptographicHash::Sha1).toHex(); QFile input(path.absoluteFilePath(QStringLiteral("source/") + hash));