d3dcompiler_qt: Remove directory creation inside the proxy library

Use of mkpath/mkdir is removed in order to make the proxy less
invasive when there is no compiler service running. Creation of the
directory structure is fully the service's responsibility.
Service availability is now only checked once, at the first invocation of
D3DCompile, as it is expected to be started before the application.

Change-Id: Ib8c4f062c418497c2253daf524654e1db30dae47
Reviewed-by: Friedemann Kleint <Friedemann.Kleint@digia.com>
This commit is contained in:
Andrew Knight 2013-12-13 13:45:30 +02:00 committed by The Qt Project
parent 1487ec4da0
commit 863810eb28
2 changed files with 67 additions and 58 deletions

View File

@ -161,31 +161,11 @@ static bool loadCompiler()
return bool(compile); 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) static QString cacheKeyFor(const void *data)
{ {
return QString::fromUtf8(QCryptographicHash::hash(reinterpret_cast<const char *>(data), QCryptographicHash::Sha1).toHex()); return QString::fromUtf8(QCryptographicHash::hash(reinterpret_cast<const char *>(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 } // namespace D3DCompiler
#ifdef __MINGW32__ #ifdef __MINGW32__
@ -200,37 +180,56 @@ HRESULT WINAPI D3DCompile(
const D3D_SHADER_MACRO *defines, ID3DInclude *include, const char *entrypoint, const D3D_SHADER_MACRO *defines, ID3DInclude *include, const char *entrypoint,
const char *target, UINT sflags, UINT eflags, ID3DBlob **shader, ID3DBlob **errorMsgs) 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 binaryPath;
static QString sourcePath; static QString sourcePath;
if (basePath.isEmpty()) { if (!initialized) {
QDir base; QString base;
if (qEnvironmentVariableIsSet("QT_D3DCOMPILER_DIR")) if (qEnvironmentVariableIsSet("QT_D3DCOMPILER_DIR")) {
base.setPath(QString::fromUtf8(qgetenv("QT_D3DCOMPILER_DIR"))); base = QString::fromLocal8Bit(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());
} else { } else {
const QString path = base.absoluteFilePath(QStringLiteral("d3dcompiler/")); const QString location = QStandardPaths::writableLocation(QStandardPaths::DataLocation);
if (!QFile::exists(path) && !base.mkdir(QStringLiteral("d3dcompiler"))) if (!location.isEmpty())
qCWarning(QT_D3DCOMPILER) << "D3D compiler path could not be created: " << QDir::toNativeSeparators(path); base = location + QStringLiteral("/d3dcompiler");
else
basePath = path;
} }
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<const char *>(data), data_size); const QByteArray sourceData = QByteArray::fromRawData(reinterpret_cast<const char *>(data), data_size);
const QString cacheKey = D3DCompiler::cacheKeyFor(sourceData); 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)) { if (blob.open(QFile::ReadOnly)) {
qCDebug(QT_D3DCOMPILER) << "Opening precompiled shader blob at" << blob.fileName(); qCDebug(QT_D3DCOMPILER) << "Opening precompiled shader blob at" << blob.fileName();
*shader = new D3DCompiler::Blob(blob.readAll()); *shader = new D3DCompiler::Blob(blob.readAll());
@ -240,13 +239,7 @@ HRESULT WINAPI D3DCompile(
} }
// Shader blob is not available, compile with compilation service if possible // Shader blob is not available, compile with compilation service if possible
if (D3DCompiler::serviceAvailable(basePath)) { if (!sourcePath.isEmpty() && serviceAvailable) {
if (sourcePath.isEmpty()) {
qCWarning(QT_D3DCOMPILER) << "Compiler service is available, but source directory is not writable.";
return E_ACCESSDENIED;
}
// Dump source to source path; wait for blob to appear // Dump source to source path; wait for blob to appear
QFile source(sourcePath + cacheKey); QFile source(sourcePath + cacheKey);
if (!source.open(QFile::WriteOnly)) { if (!source.open(QFile::WriteOnly)) {
@ -264,6 +257,7 @@ HRESULT WINAPI D3DCompile(
QElapsedTimer timer; QElapsedTimer timer;
timer.start(); timer.start();
QFile blob(binaryPath + cacheKey);
while (!(blob.exists() && blob.open(QFile::ReadOnly)) && timer.elapsed() < timeout) while (!(blob.exists() && blob.open(QFile::ReadOnly)) && timer.elapsed() < timeout)
QThread::msleep(100); QThread::msleep(100);
@ -285,6 +279,8 @@ HRESULT WINAPI D3DCompile(
if (SUCCEEDED(hr) && !binaryPath.isEmpty()) { if (SUCCEEDED(hr) && !binaryPath.isEmpty()) {
const QByteArray blobContents = QByteArray::fromRawData( const QByteArray blobContents = QByteArray::fromRawData(
reinterpret_cast<const char *>((*shader)->GetBufferPointer()), (*shader)->GetBufferSize()); reinterpret_cast<const char *>((*shader)->GetBufferPointer()), (*shader)->GetBufferSize());
QFile blob(binaryPath + cacheKey);
if (blob.open(QFile::WriteOnly) && blob.write(blobContents)) if (blob.open(QFile::WriteOnly) && blob.write(blobContents))
qCDebug(QT_D3DCOMPILER) << "Cached shader blob at" << blob.fileName(); qCDebug(QT_D3DCOMPILER) << "Cached shader blob at" << blob.fileName();
else else

View File

@ -153,11 +153,12 @@ QString tst_d3dcompiler::blobPath()
if (qEnvironmentVariableIsSet("QT_D3DCOMPILER_DIR")) if (qEnvironmentVariableIsSet("QT_D3DCOMPILER_DIR"))
path.setPath(qgetenv("QT_D3DCOMPILER_DIR")); path.setPath(qgetenv("QT_D3DCOMPILER_DIR"));
else 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() void tst_d3dcompiler::initTestCase()
@ -177,7 +178,12 @@ void tst_d3dcompiler::cleanup()
FreeLibrary(d3dcompiler_win); FreeLibrary(d3dcompiler_win);
QDir path(blobPath()); 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() 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 // Don't test the default case, as it would clutter the AppData directory
//QTest::newRow("default") << QByteArrayLiteral("") << true << E_ABORT; //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("invalid") << QByteArrayLiteral("ZZ:\\") << false << S_OK;
QTest::newRow("empty") << QByteArrayLiteral("") << false << S_OK;
} }
void tst_d3dcompiler::service() void tst_d3dcompiler::service()
@ -254,6 +261,8 @@ void tst_d3dcompiler::service()
void tst_d3dcompiler::offlineCompile() void tst_d3dcompiler::offlineCompile()
{ {
qputenv("QT_D3DCOMPILER_DIR", QFile::encodeName(tempDir.path()));
for (int i = 0; compilerDlls[i]; ++i) { for (int i = 0; compilerDlls[i]; ++i) {
d3dcompiler_win = loadLibrary(compilerDlls[i]); d3dcompiler_win = loadLibrary(compilerDlls[i]);
if (d3dcompiler_win) if (d3dcompiler_win)
@ -271,7 +280,8 @@ void tst_d3dcompiler::offlineCompile()
QVERIFY(shader); QVERIFY(shader);
QDir outputPath(blobPath()); QDir outputPath(blobPath());
QVERIFY(outputPath.mkpath(QStringLiteral("binary"))); QVERIFY(outputPath.exists());
QVERIFY(outputPath.exists(QStringLiteral("binary")));
outputPath.cd(QStringLiteral("binary")); outputPath.cd(QStringLiteral("binary"));
QFile output(outputPath.absoluteFilePath(QCryptographicHash::hash(data, QCryptographicHash::Sha1).toHex())); QFile output(outputPath.absoluteFilePath(QCryptographicHash::hash(data, QCryptographicHash::Sha1).toHex()));
QVERIFY(output.open(QFile::WriteOnly)); QVERIFY(output.open(QFile::WriteOnly));
@ -291,6 +301,8 @@ void tst_d3dcompiler::offlineCompile()
void tst_d3dcompiler::onlineCompile() void tst_d3dcompiler::onlineCompile()
{ {
qputenv("QT_D3DCOMPILER_DIR", QFile::encodeName(tempDir.path()));
QByteArray data(hlsl); QByteArray data(hlsl);
const QDir path = blobPath(); const QDir path = blobPath();
@ -313,8 +325,9 @@ void tst_d3dcompiler::onlineCompile()
runner.start(); runner.start();
// Wait for source to appear // Wait for source to appear
QVERIFY(path.mkpath(QStringLiteral("source"))); QVERIFY(path.exists());
QVERIFY(path.mkpath(QStringLiteral("binary"))); QVERIFY(path.exists(QStringLiteral("source")));
QVERIFY(path.exists(QStringLiteral("binary")));
const QByteArray hash = QCryptographicHash::hash(data, QCryptographicHash::Sha1).toHex(); const QByteArray hash = QCryptographicHash::hash(data, QCryptographicHash::Sha1).toHex();
QFile input(path.absoluteFilePath(QStringLiteral("source/") + hash)); QFile input(path.absoluteFilePath(QStringLiteral("source/") + hash));