aboutsummaryrefslogtreecommitdiffstats
path: root/src/plugins/scenegraph
diff options
context:
space:
mode:
authorLaszlo Agocs <laszlo.agocs@theqtcompany.com>2016-06-15 15:17:32 +0200
committerLaszlo Agocs <laszlo.agocs@qt.io>2016-06-22 08:54:40 +0000
commit7de18e6f52d6247bddd7bfabe0b2601d7db239b5 (patch)
tree4fb4df87bf129fe399734a4eccf97cfe61de5671 /src/plugins/scenegraph
parenta03eb67a786788511302a8ac3ecc809002408fed (diff)
D3D12: Support runtime threaded shader compilation
Let's revise our policy of offline/bytecode only shaders. ShaderEffect benefits greatly from having runtime compilation support for HLSL source strings, especially when dynamically constructing shader strings. There is no reason not to support both approaches since we rely on d3dcompiler for reflection anyhow. What's more, we can call D3DCompile on a dedicated thread, keeping the gui responsive while compilation is on-going. Change-Id: Ie6c02c2aa0ebd0c8371bbf30b3ce6582128c457b Reviewed-by: Andy Nichols <andy.nichols@qt.io>
Diffstat (limited to 'src/plugins/scenegraph')
-rw-r--r--src/plugins/scenegraph/d3d12/qsgd3d12engine.cpp4
-rw-r--r--src/plugins/scenegraph/d3d12/qsgd3d12shadereffectnode.cpp89
-rw-r--r--src/plugins/scenegraph/d3d12/qsgd3d12shadereffectnode_p.h9
3 files changed, 89 insertions, 13 deletions
diff --git a/src/plugins/scenegraph/d3d12/qsgd3d12engine.cpp b/src/plugins/scenegraph/d3d12/qsgd3d12engine.cpp
index 45b9de9ece..b54d10aa85 100644
--- a/src/plugins/scenegraph/d3d12/qsgd3d12engine.cpp
+++ b/src/plugins/scenegraph/d3d12/qsgd3d12engine.cpp
@@ -552,12 +552,12 @@ QSGRendererInterface::ShaderType QSGD3D12Engine::shaderType() const
QSGRendererInterface::ShaderCompilationTypes QSGD3D12Engine::shaderCompilationType() const
{
- return OfflineCompilation;
+ return RuntimeCompilation | OfflineCompilation;
}
QSGRendererInterface::ShaderSourceTypes QSGD3D12Engine::shaderSourceType() const
{
- return ShaderByteCode;
+ return ShaderSourceString | ShaderByteCode;
}
static inline quint32 alignedSize(quint32 size, quint32 byteAlign)
diff --git a/src/plugins/scenegraph/d3d12/qsgd3d12shadereffectnode.cpp b/src/plugins/scenegraph/d3d12/qsgd3d12shadereffectnode.cpp
index f77719f876..8c46e781e9 100644
--- a/src/plugins/scenegraph/d3d12/qsgd3d12shadereffectnode.cpp
+++ b/src/plugins/scenegraph/d3d12/qsgd3d12shadereffectnode.cpp
@@ -41,6 +41,7 @@
#include "qsgd3d12rendercontext_p.h"
#include "qsgd3d12texture_p.h"
#include "qsgd3d12engine_p.h"
+#include <QtCore/qthreadpool.h>
#include <QtCore/qfile.h>
#include <QtQml/qqmlfile.h>
#include <qsgtextureprovider.h>
@@ -777,12 +778,12 @@ bool QSGD3D12GuiThreadShaderEffectManager::hasSeparateSamplerAndTextureObjects()
QString QSGD3D12GuiThreadShaderEffectManager::log() const
{
- return QString();
+ return m_log;
}
QSGGuiThreadShaderEffectManager::Status QSGD3D12GuiThreadShaderEffectManager::status() const
{
- return Compiled;
+ return m_status;
}
struct RefGuard {
@@ -791,17 +792,85 @@ struct RefGuard {
IUnknown *p;
};
-bool QSGD3D12GuiThreadShaderEffectManager::reflect(const QByteArray &src, ShaderInfo *result)
+class QSGD3D12ShaderCompileTask : public QRunnable
{
- const QString fn = QQmlFile::urlToLocalFileOrQrc(src);
- QFile f(fn);
- if (!f.open(QIODevice::ReadOnly)) {
- qWarning("ShaderEffect: Failed to read %s", qPrintable(fn));
- return false;
+public:
+ QSGD3D12ShaderCompileTask(QSGD3D12GuiThreadShaderEffectManager *mgr,
+ QSGD3D12GuiThreadShaderEffectManager::ShaderInfo::Type typeHint,
+ const QByteArray &src,
+ QSGD3D12GuiThreadShaderEffectManager::ShaderInfo *result)
+ : mgr(mgr), typeHint(typeHint), src(src), result(result) { }
+
+ void run() override;
+
+private:
+ QSGD3D12GuiThreadShaderEffectManager *mgr;
+ QSGD3D12GuiThreadShaderEffectManager::ShaderInfo::Type typeHint;
+ QByteArray src;
+ QSGD3D12GuiThreadShaderEffectManager::ShaderInfo *result;
+};
+
+void QSGD3D12ShaderCompileTask::run()
+{
+ const char *target = typeHint == QSGD3D12GuiThreadShaderEffectManager::ShaderInfo::TypeVertex ? "vs_5_0" : "ps_5_0";
+ ID3DBlob *bytecode = nullptr;
+ ID3DBlob *errors = nullptr;
+ HRESULT hr = D3DCompile(src.constData(), src.size(), nullptr, nullptr, nullptr,
+ "main", target, 0, 0, &bytecode, &errors);
+ if (FAILED(hr) || !bytecode) {
+ qWarning("HLSL shader compilation failed: 0x%x", hr);
+ if (errors) {
+ mgr->m_log += QString::fromUtf8(static_cast<const char *>(errors->GetBufferPointer()), errors->GetBufferSize());
+ errors->Release();
+ }
+ mgr->m_status = QSGGuiThreadShaderEffectManager::Error;
+ emit mgr->shaderCodePrepared(false, typeHint, src, result);
+ emit mgr->logAndStatusChanged();
+ return;
}
- result->blob = f.readAll();
- f.close();
+ result->blob.resize(bytecode->GetBufferSize());
+ memcpy(result->blob.data(), bytecode->GetBufferPointer(), result->blob.size());
+ bytecode->Release();
+
+ const bool ok = mgr->reflect(result);
+ mgr->m_status = ok ? QSGGuiThreadShaderEffectManager::Compiled : QSGGuiThreadShaderEffectManager::Error;
+ emit mgr->shaderCodePrepared(ok, typeHint, src, result);
+ emit mgr->logAndStatusChanged();
+}
+
+void QSGD3D12GuiThreadShaderEffectManager::prepareShaderCode(ShaderInfo::Type typeHint, const QByteArray &src, ShaderInfo *result)
+{
+ // The D3D12 backend's ShaderEffect implementation supports both HLSL
+ // source strings and bytecode in files as input. The latter is strongly
+ // recommended, but in order to make ShaderEffect users' (and
+ // qtgraphicaleffects') life easier, and since we link to d3dcompiler
+ // anyways, compiling from source is also supported.
+
+ // For simplicity, assume that file = bytecode, string = HLSL.
+ QUrl srcUrl(src);
+ if (!srcUrl.scheme().compare(QLatin1String("qrc"), Qt::CaseInsensitive) || srcUrl.isLocalFile()) {
+ const QString fn = QQmlFile::urlToLocalFileOrQrc(src);
+ QFile f(fn);
+ if (!f.open(QIODevice::ReadOnly)) {
+ qWarning("ShaderEffect: Failed to read %s", qPrintable(fn));
+ emit shaderCodePrepared(false, typeHint, src, result);
+ return;
+ }
+ result->blob = f.readAll();
+ f.close();
+ const bool ok = reflect(result);
+ m_status = ok ? Compiled : Error;
+ emit shaderCodePrepared(ok, typeHint, src, result);
+ emit logAndStatusChanged();
+ return;
+ }
+
+ QThreadPool::globalInstance()->start(new QSGD3D12ShaderCompileTask(this, typeHint, src, result));
+}
+
+bool QSGD3D12GuiThreadShaderEffectManager::reflect(ShaderInfo *result)
+{
ID3D12ShaderReflection *reflector;
HRESULT hr = D3DReflect(result->blob.constData(), result->blob.size(), IID_PPV_ARGS(&reflector));
if (FAILED(hr)) {
diff --git a/src/plugins/scenegraph/d3d12/qsgd3d12shadereffectnode_p.h b/src/plugins/scenegraph/d3d12/qsgd3d12shadereffectnode_p.h
index edeaba899b..c36ee1a6e6 100644
--- a/src/plugins/scenegraph/d3d12/qsgd3d12shadereffectnode_p.h
+++ b/src/plugins/scenegraph/d3d12/qsgd3d12shadereffectnode_p.h
@@ -160,7 +160,14 @@ public:
QString log() const override;
Status status() const override;
- bool reflect(const QByteArray &src, ShaderInfo *result) override;
+ void prepareShaderCode(ShaderInfo::Type typeHint, const QByteArray &src, ShaderInfo *result) override;
+
+private:
+ bool reflect(ShaderInfo *result);
+ QString m_log;
+ Status m_status = Uncompiled;
+
+ friend class QSGD3D12ShaderCompileTask;
};
QT_END_NAMESPACE