summaryrefslogtreecommitdiffstats
path: root/src/shadertools
diff options
context:
space:
mode:
Diffstat (limited to 'src/shadertools')
-rw-r--r--src/shadertools/doc/doc.pri3
-rw-r--r--src/shadertools/doc/qtshadertools.qdocconf49
-rw-r--r--src/shadertools/doc/snippets/color.frag16
-rw-r--r--src/shadertools/doc/snippets/color.vert20
-rw-r--r--src/shadertools/doc/src/qtshadertools-copyright.qdoc37
-rw-r--r--src/shadertools/doc/src/qtshadertools-cpp.qdoc45
-rw-r--r--src/shadertools/doc/src/qtshadertools-index.qdoc63
-rw-r--r--src/shadertools/qshaderbaker.cpp421
-rw-r--r--src/shadertools/qshaderbaker.h78
-rw-r--r--src/shadertools/qshaderbatchablerewriter.cpp223
-rw-r--r--src/shadertools/qshaderbatchablerewriter_p.h64
-rw-r--r--src/shadertools/qspirvcompiler.cpp390
-rw-r--r--src/shadertools/qspirvcompiler_p.h89
-rw-r--r--src/shadertools/qspirvshader.cpp468
-rw-r--r--src/shadertools/qspirvshader_p.h101
-rw-r--r--src/shadertools/qtshadertoolsglobal.h62
-rw-r--r--src/shadertools/qtshadertoolsglobal_p.h48
-rw-r--r--src/shadertools/shadertools.pro41
18 files changed, 2218 insertions, 0 deletions
diff --git a/src/shadertools/doc/doc.pri b/src/shadertools/doc/doc.pri
new file mode 100644
index 0000000..3fa7632
--- /dev/null
+++ b/src/shadertools/doc/doc.pri
@@ -0,0 +1,3 @@
+QMAKE_DOCS = $$PWD/qtshadertools.qdocconf
+
+OTHER_FILES += $$PWD/src/*.qdoc
diff --git a/src/shadertools/doc/qtshadertools.qdocconf b/src/shadertools/doc/qtshadertools.qdocconf
new file mode 100644
index 0000000..9afd3f7
--- /dev/null
+++ b/src/shadertools/doc/qtshadertools.qdocconf
@@ -0,0 +1,49 @@
+include($QT_INSTALL_DOCS/global/qt-module-defaults.qdocconf)
+
+project = QtShaderTools
+description = Qt Shader Tools Reference Documentation
+version = $QT_VERSION
+
+examplesinstallpath = shadertools
+
+qhp.projects = QtShaderTools
+
+qhp.QtShaderTools.file = qtshadertools.qhp
+qhp.QtShaderTools.namespace = org.qt-project.qtshadertools.$QT_VERSION_TAG
+qhp.QtShaderTools.virtualFolder = qtshadertools
+qhp.QtShaderTools.indexTitle = Qt Shader Tools
+qhp.QtShaderTools.indexRoot =
+
+qhp.QtShaderTools.filterAttributes = qtshadertools $QT_VERSION qtrefdoc
+qhp.QtShaderTools.customFilters.Qt.name = QtShaderTools $QT_VERSION
+qhp.QtShaderTools.customFilters.Qt.filterAttributes = qtshadertools $QT_VERSION
+
+qhp.QtShaderTools.subprojects = classes examples
+
+qhp.QtShaderTools.subprojects.classes.title = C++ Classes
+qhp.QtShaderTools.subprojects.classes.indexTitle = Qt Shader Tools C++ Classes
+qhp.QtShaderTools.subprojects.classes.selectors = class fake:headerfile
+qhp.QtShaderTools.subprojects.classes.sortPages = true
+
+qhp.QtShaderTools.subprojects.examples.title = Examples
+qhp.QtShaderTools.subprojects.examples.indexTitle = Qt Shader Tools Examples and Tutorials
+qhp.QtShaderTools.subprojects.examples.selectors = fake:example
+
+tagfile = ../../../doc/qtshadertools/qtshadertools.tags
+
+depends += qtcore qtgui
+
+headerdirs += ..
+
+sourcedirs += ..
+
+exampledirs += ../../../examples/shadertools \
+ snippets
+
+imagedirs += images
+
+navigation.landingpage = "Qt Shader Tools"
+navigation.landingtitle = "Shader Tools"
+navigation.cppclassespage = "Qt Shader Tools C++ Classes"
+
+Cpp.ignoretokens += Q_SHADERTOOLS_EXPORT
diff --git a/src/shadertools/doc/snippets/color.frag b/src/shadertools/doc/snippets/color.frag
new file mode 100644
index 0000000..859946a
--- /dev/null
+++ b/src/shadertools/doc/snippets/color.frag
@@ -0,0 +1,16 @@
+//! [0]
+#version 440
+
+layout(location = 0) in vec3 v_color;
+layout(location = 0) out vec4 fragColor;
+
+layout(std140, binding = 0) uniform buf {
+ mat4 mvp;
+ float opacity;
+} ubuf;
+
+void main()
+{
+ fragColor = vec4(v_color * ubuf.opacity, ubuf.opacity);
+}
+//! [0]
diff --git a/src/shadertools/doc/snippets/color.vert b/src/shadertools/doc/snippets/color.vert
new file mode 100644
index 0000000..9e21794
--- /dev/null
+++ b/src/shadertools/doc/snippets/color.vert
@@ -0,0 +1,20 @@
+//! [0]
+#version 440
+
+layout(location = 0) in vec4 position;
+layout(location = 1) in vec3 color;
+layout(location = 0) out vec3 v_color;
+
+layout(std140, binding = 0) uniform buf {
+ mat4 mvp;
+ float opacity;
+} ubuf;
+
+out gl_PerVertex { vec4 gl_Position; };
+
+void main()
+{
+ v_color = color;
+ gl_Position = ubuf.mvp * position;
+}
+//! [0]
diff --git a/src/shadertools/doc/src/qtshadertools-copyright.qdoc b/src/shadertools/doc/src/qtshadertools-copyright.qdoc
new file mode 100644
index 0000000..f7a5e7c
--- /dev/null
+++ b/src/shadertools/doc/src/qtshadertools-copyright.qdoc
@@ -0,0 +1,37 @@
+/****************************************************************************
+**
+** Copyright (C) 2018 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt Shader Tools module
+**
+** $QT_BEGIN_LICENSE:FDL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Free Documentation License Usage
+** Alternatively, this file may be used under the terms of the GNU Free
+** Documentation License version 1.3 as published by the Free Software
+** Foundation and appearing in the file included in the packaging of
+** this file. Please review the following information to ensure
+** the GNU Free Documentation License version 1.3 requirements
+** will be met: https://www.gnu.org/licenses/fdl-1.3.html.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+/*!
+\page copyright-notices.html
+\title Copyright Notices
+\section1 Third-party Licenses
+
+The following table lists parts (modules) of Qt Rendering Hardware Interface that
+incorporate code licensed under third-party open-source licenses:
+
+\annotatedlist attributions-qtshadertools
+*/
diff --git a/src/shadertools/doc/src/qtshadertools-cpp.qdoc b/src/shadertools/doc/src/qtshadertools-cpp.qdoc
new file mode 100644
index 0000000..2d1ea91
--- /dev/null
+++ b/src/shadertools/doc/src/qtshadertools-cpp.qdoc
@@ -0,0 +1,45 @@
+/****************************************************************************
+**
+** Copyright (C) 2018 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt Shader Tools module
+**
+** $QT_BEGIN_LICENSE:FDL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Free Documentation License Usage
+** Alternatively, this file may be used under the terms of the GNU Free
+** Documentation License version 1.3 as published by the Free Software
+** Foundation and appearing in the file included in the packaging of
+** this file. Please review the following information to ensure
+** the GNU Free Documentation License version 1.3 requirements
+** will be met: https://www.gnu.org/licenses/fdl-1.3.html.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+/*!
+ \module QtShaderTools
+ \title Qt Shader Tools C++ Classes
+ \ingroup modules
+ \qtvariable shadertools
+
+ To include the definitions of the module's classes, use the following directive:
+
+ \badcode
+ #include <QtShaderTools>
+ \endcode
+
+ To link against the module, add this line to your \l qmake \c .pro file:
+
+ \badcode
+ QT += shadertools
+ \endcode
+*/
diff --git a/src/shadertools/doc/src/qtshadertools-index.qdoc b/src/shadertools/doc/src/qtshadertools-index.qdoc
new file mode 100644
index 0000000..4e0d8aa
--- /dev/null
+++ b/src/shadertools/doc/src/qtshadertools-index.qdoc
@@ -0,0 +1,63 @@
+/****************************************************************************
+**
+** Copyright (C) 2018 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt Shader Tools module
+**
+** $QT_BEGIN_LICENSE:FDL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Free Documentation License Usage
+** Alternatively, this file may be used under the terms of the GNU Free
+** Documentation License version 1.3 as published by the Free Software
+** Foundation and appearing in the file included in the packaging of
+** this file. Please review the following information to ensure
+** the GNU Free Documentation License version 1.3 requirements
+** will be met: https://www.gnu.org/licenses/fdl-1.3.html.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+/*!
+\title Qt Shader Tools
+\page qtshadertools-index.html
+
+\section1 Introduction
+
+The Qt Shader Tools module builds on the SPIR-V Open Source Ecosystem as
+described at \l{https://www.khronos.org/spir/}{the Khronos SPIR-V web
+site}. For compiling into SPIR-V
+\l{https://github.com/KhronosGroup/glslang}{glslang} is used, while
+translating and reflecting is done via
+\l{https://github.com/KhronosGroup/SPIRV-Cross}{SPIRV-Cross}.
+
+In order to allow shader code to be written once in Qt applications and
+libraries, all shaders are expected to be written in a single language
+which is then compiled into SPIR-V. Versions for various shading language
+are then generated from that, together with reflection information (inputs,
+outputs, shader resources). This is then packed into easily and efficiently
+serializable QRhiShader instances. The Qt Rendering Hardware Interface
+consumes QRhiShader instances directly.
+
+The two main components are:
+
+\list
+\li QShaderBaker and the \c qsb command-line tool, and
+\li QRhiShader (part of the Qt Rhi module)
+\endlist
+
+\section1 Table of Contents
+
+\list
+ \li \l {Qt Shader Tools C++ Classes}
+ \li \l {Copyright Notices}
+\endlist
+
+*/
diff --git a/src/shadertools/qshaderbaker.cpp b/src/shadertools/qshaderbaker.cpp
new file mode 100644
index 0000000..fbb0a2b
--- /dev/null
+++ b/src/shadertools/qshaderbaker.cpp
@@ -0,0 +1,421 @@
+/****************************************************************************
+**
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the Qt Shader Tools module
+**
+** $QT_BEGIN_LICENSE:LGPL3$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see http://www.qt.io/terms-conditions. For further
+** information use the contact form at http://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPLv3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or later as published by the Free
+** Software Foundation and appearing in the file LICENSE.GPL included in
+** the packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 2.0 requirements will be
+** met: http://www.gnu.org/licenses/gpl-2.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qshaderbaker.h"
+#include "qspirvcompiler_p.h"
+#include "qspirvshader_p.h"
+#include <QFileInfo>
+#include <QFile>
+#include <QDebug>
+
+QT_BEGIN_NAMESPACE
+
+/*!
+ \class QShaderBaker
+ \inmodule QtShaderTools
+
+ \brief Compiles a GLSL/Vulkan shader into SPIR-V, translates into other
+ shading languages, and gathers reflection metadata.
+
+ QShaderBaker takes a graphics (vertex, fragment, etc.) or compute shader,
+ and produces multiple - either source or bytecode - variants of it,
+ together with reflection information. The results are represented by a
+ QRhiShader instance, which also provides simple and fast serialization
+ and deserialization.
+
+ \note Applications and libraries are recommended to avoid using this class
+ directly. Rather, all Qt users are encouraged to rely on offline
+ compilation by invoking the \c qsb command-line tool at build time. This
+ tool uses QShaderBaker itself and writes the serialized version of the
+ generated QRhiShader into a file. The usage of this class should be
+ restricted to cases where run time compilation cannot be avoided, such as
+ when working with user-provided shader source strings.
+
+ The input format is always assumed to be Vulkan-flavored GLSL at the
+ moment. See the
+ \l{https://github.com/KhronosGroup/GLSL/blob/master/extensions/khr/GL_KHR_vulkan_glsl.txt}{GL_KHR_vulkan_glsl
+ specification} for an overview, keeping in mind that the Qt Shader Tools
+ module is meant to be used in combination with the QRhi classes from Qt
+ Rendering Hardware Interface module, and therefore a number of concepts and
+ constructs (push constants, storage buffers, subpasses, etc.) are not
+ applicable at the moment. Additional options may be introduced in the
+ future, for example, by enabling
+ \l{https://docs.microsoft.com/en-us/windows/desktop/direct3dhlsl/dx-graphics-hlsl}{HLSL}
+ as a source format, once HLSL to SPIR-V compilation is deemed suitable.
+
+ The reflection metadata is retrievable from the resulting QRhiShader by
+ calling QRhiShader::description(). This is essential when having to
+ discover what set of vertex inputs and shader resources a shader expects,
+ and what the layouts of those are, as many modern graphics APIs offer no
+ built-in shader reflection capabilities.
+
+ \section2 Typical Workflow
+
+ Let's assume an application has a vertex and fragment shader like the following:
+
+ Vertex shader:
+ \snippet color.vert 0
+
+ Fragment shader:
+ \snippet color.frag 0
+
+ To get QRhiShader instances that can be passed as-is to a
+ QRhiGraphicsPipeline, there are two options: doing the shader pack
+ generation off line, or at run time.
+
+ The former involves running the \c qsb tool:
+
+ \badcode
+ qsb --glsl "100 es,120" --hlsl 50 --msl 12 color.vert -o color.vert.qsb
+ qsb --glsl "100 es,120" --hlsl 50 --msl 12 color.frag -o color.frag.qsb
+ \endcode
+
+ The example uses the translation targets as appropriate for QRhi. This
+ means GLSL/ES 100, GLSL 120, HLSL Shader Model 5.0, and Metal Shading
+ Language 1.2.
+
+ Note how the command line options correspond to what can be specified via
+ setGeneratedShaders(). Once the resulting files are available, they can be
+ shipped with the application (typically embedded into the executable the
+ the Qt Resource System), and can be loaded and passed to
+ QRhiShader::fromSerialized() at run time.
+
+ While not shown here, \c qsb can do more: it is also able to invoke \c fxc
+ on Windows or the appropriate XCode tools on macOS to compile the generated
+ HLSL or Metal shader code into bytecode and include the compiled versions
+ in the QRhiShader. After a baked shader pack is written into a file, its
+ contents can be examined by running \c{qsb -d} on it. Run \c qsb with
+ \c{--help} for more information.
+
+ The alternative approach is to perform the same at run time. This involves
+ creating a QShaderBaker instance, calling setSourceFileName(), and then
+ setting up the translation targets via setGeneratedShaders():
+
+ \badcode
+ baker.setGeneratedShaderVariants({ QRhiShaderKey::StandardShader });
+ QVector<QShaderBaker::GeneratedShader> targets;
+ targets.append({ QRhiShaderKey::SpirvShader, QRhiShaderVersion(100) });
+ targets.append({ QRhiShaderKey::GlslShader, QRhiShaderVersion(100, QRhiShaderVersion::GlslEs) });
+ targets.append({ QRhiShaderKey::SpirvShader, QRhiShaderVersion(120) });
+ targets.append({ QRhiShaderKey::HlslShader, QRhiShaderVersion(50) });
+ targets.append({ QRhiShaderKey::MslShader, QRhiShaderVersion(12) });
+ baker.setGeneratedShaders(targets);
+ QRhiShader shaders = baker.bake();
+ if (!shaders.isValid())
+ qWarning() << baker.errorMessage();
+ \endcode
+
+ \sa QRhiShader
+ */
+
+struct QShaderBakerPrivate
+{
+ bool readFile(const QString &fn);
+
+ QString sourceFileName;
+ QByteArray source;
+ QRhiShader::ShaderStage stage;
+ QVector<QShaderBaker::GeneratedShader> reqVersions;
+ QVector<QRhiShaderKey::ShaderVariant> variants;
+ QSpirvCompiler compiler;
+ QString errorMessage;
+};
+
+bool QShaderBakerPrivate::readFile(const QString &fn)
+{
+ QFile f(fn);
+ if (!f.open(QIODevice::ReadOnly | QIODevice::Text)) {
+ qWarning("QShaderBaker: Failed to open %s", qPrintable(fn));
+ return false;
+ }
+ source = f.readAll();
+ sourceFileName = fn;
+ return true;
+}
+
+/*!
+ Constructs a new QShaderBaker.
+ */
+QShaderBaker::QShaderBaker()
+ : d(new QShaderBakerPrivate)
+{
+}
+
+/*!
+ Destructor.
+ */
+QShaderBaker::~QShaderBaker()
+{
+ delete d;
+}
+
+/*!
+ Sets the name of the shader source file to \a fileName. This is the file
+ that will be read when calling bake(). The shader stage is deduced
+ automatically from the file extension. When this is not desired or not
+ possible, use the overload with the stage argument instead.
+
+ The supported file extensions are:
+ \list
+ \li \c{.vert} - vertex shader
+ \li \c{.frag} - fragment (pixel) shader
+ \li \c{.tesc} - tessellation control (hull) shader
+ \li \c{.tese} - tessellation evaluation (domain) shader
+ \li \c{.geom} - geometry shader
+ \li \c{.comp} - compute shader
+ \endlist
+ */
+void QShaderBaker::setSourceFileName(const QString &fileName)
+{
+ if (!d->readFile(fileName))
+ return;
+
+ const QString suffix = QFileInfo(fileName).suffix();
+ if (suffix == QStringLiteral("vert")) {
+ d->stage = QRhiShader::VertexStage;
+ } else if (suffix == QStringLiteral("frag")) {
+ d->stage = QRhiShader::FragmentStage;
+ } else if (suffix == QStringLiteral("tesc")) {
+ d->stage = QRhiShader::TessControlStage;
+ } else if (suffix == QStringLiteral("tese")) {
+ d->stage = QRhiShader::TessEvaluationStage;
+ } else if (suffix == QStringLiteral("geom")) {
+ d->stage = QRhiShader::GeometryStage;
+ } else if (suffix == QStringLiteral("comp")) {
+ d->stage = QRhiShader::ComputeStage;
+ } else {
+ qWarning("QShaderBaker: Unknown shader stage, defaulting to vertex");
+ d->stage = QRhiShader::VertexStage;
+ }
+}
+
+/*!
+ Sets the name of the shader source file to \a fileName. This is the file
+ that will be read when calling bake(). The shader stage is specified by \a
+ stage.
+ */
+void QShaderBaker::setSourceFileName(const QString &fileName, QRhiShader::ShaderStage stage)
+{
+ if (d->readFile(fileName))
+ d->stage = stage;
+}
+
+/*!
+ Sets the source \a device. This allows using any QIODevice instead of just
+ files. \a stage specifies the shader stage, while the optional \a fileName
+ contains a filename that is used in the error messages.
+ */
+void QShaderBaker::setSourceDevice(QIODevice *device, QRhiShader::ShaderStage stage, const QString &fileName)
+{
+ setSourceString(device->readAll(), stage, fileName);
+}
+
+/*!
+ Sets the input shader \a sourceString. \a stage specified the shader stage,
+ while the optional \a fileName contains a filename that is used in the
+ error messages.
+ */
+void QShaderBaker::setSourceString(const QByteArray &sourceString, QRhiShader::ShaderStage stage, const QString &fileName)
+{
+ d->sourceFileName = fileName; // for error messages, include handling, etc.
+ d->source = sourceString;
+ d->stage = stage;
+}
+
+/*!
+ \typedef QShaderBaker::GeneratedShader
+
+ Synonym for QPair<QRhiShaderKey::ShaderSource, QRhiShaderVersion>.
+*/
+
+/*!
+ Specifies what kind of shaders to compile or translate to. Nothing is
+ generated by default so calling this function before bake() is mandatory
+
+ \note when this function is not called or \a v is empty or contains only invalid
+ entries, the resulting QRhiShader will be empty and thus invalid.
+
+ For example, the minimal possible baking target is SPIR-V, without any
+ additional translations to other languages. To request this, do:
+
+ \badcode
+ baker.setGeneratedShaders({ QRhiShaderKey::SpirvShader, QRhiShaderVersion(100) });
+ \endcode
+ */
+void QShaderBaker::setGeneratedShaders(const QVector<GeneratedShader> &v)
+{
+ d->reqVersions = v;
+}
+
+/*!
+ Specifies which shader variants are genetated. Each shader version can have
+ multiple variants in the resulting QRhiShader.
+
+ In most cases \a v contains a single entry, QRhiShaderKey::StandardShader.
+
+ \note when no variants are set, the resulting QRhiShader will be empty and
+ thus invalid.
+ */
+void QShaderBaker::setGeneratedShaderVariants(const QVector<QRhiShaderKey::ShaderVariant> &v)
+{
+ d->variants = v;
+}
+
+/*!
+ Runs the compilation and translation process.
+
+ \return a QRhiShader instance. To check if the process was successful,
+ call QRhiShader::isValid(). When that indicates \c false, call
+ errorMessage() to retrieve the log.
+
+ This is an expensive operation. When calling this from applications, it can
+ be advisable to do it on a separate thread.
+
+ \note QShaderBaker instances are reusable: after calling bake(), the same
+ instance can be used with different inputs again. However, a QShaderBaker
+ instance should only be used on one single thread during its lifetime.
+ */
+QRhiShader QShaderBaker::bake()
+{
+ d->errorMessage.clear();
+
+ if (d->source.isEmpty()) {
+ d->errorMessage = QLatin1String("QShaderBaker: No source specified");
+ return QRhiShader();
+ }
+
+ d->compiler.setSourceString(d->source, d->stage, d->sourceFileName);
+ d->compiler.setFlags(0);
+ QByteArray spirv = d->compiler.compileToSpirv();
+ if (spirv.isEmpty()) {
+ d->errorMessage = d->compiler.errorMessage();
+ return QRhiShader();
+ }
+
+ QByteArray batchableSpirv;
+ if (d->stage == QRhiShader::VertexStage && d->variants.contains(QRhiShaderKey::BatchableVertexShader)) {
+ d->compiler.setFlags(QSpirvCompiler::RewriteToMakeBatchableForSG);
+ batchableSpirv = d->compiler.compileToSpirv();
+ if (batchableSpirv.isEmpty()) {
+ d->errorMessage = d->compiler.errorMessage();
+ return QRhiShader();
+ }
+ }
+
+ QRhiShader bs;
+ bs.setStage(d->stage);
+
+ QSpirvShader spirvShader;
+ spirvShader.setSpirvBinary(spirv);
+
+ QSpirvShader batchableSpirvShader;
+ if (!batchableSpirv.isEmpty()) {
+ batchableSpirvShader.setSpirvBinary(batchableSpirv);
+ bs.setDescription(batchableSpirvShader.shaderDescription());
+ } else {
+ bs.setDescription(spirvShader.shaderDescription());
+ }
+
+ for (const GeneratedShader &req: d->reqVersions) {
+ for (const QRhiShaderKey::ShaderVariant &v : d->variants) {
+ QByteArray *currentSpirv = &spirv;
+ QSpirvShader *currentSpirvShader = &spirvShader;
+ if (v == QRhiShaderKey::BatchableVertexShader) {
+ if (!batchableSpirv.isEmpty()) {
+ currentSpirv = &batchableSpirv;
+ currentSpirvShader = &batchableSpirvShader;
+ } else {
+ continue;
+ }
+ }
+ const QRhiShaderKey key(req.first, req.second, v);
+ QRhiShaderCode shader;
+ shader.setEntryPoint(QByteArrayLiteral("main"));
+ switch (req.first) {
+ case QRhiShaderKey::SpirvShader:
+ shader.setShader(*currentSpirv);
+ break;
+ case QRhiShaderKey::GlslShader:
+ {
+ QSpirvShader::GlslFlags flags = 0;
+ if (req.second.flags().testFlag(QRhiShaderVersion::GlslEs))
+ flags |= QSpirvShader::GlslEs;
+ shader.setShader(currentSpirvShader->translateToGLSL(req.second.version(), flags));
+ if (shader.shader().isEmpty()) {
+ d->errorMessage = currentSpirvShader->translationErrorMessage();
+ return QRhiShader();
+ }
+ }
+ break;
+ case QRhiShaderKey::HlslShader:
+ shader.setShader(currentSpirvShader->translateToHLSL(req.second.version()));
+ if (shader.shader().isEmpty()) {
+ d->errorMessage = currentSpirvShader->translationErrorMessage();
+ return QRhiShader();
+ }
+ break;
+ case QRhiShaderKey::MslShader:
+ shader.setShader(currentSpirvShader->translateToMSL(req.second.version()));
+ if (shader.shader().isEmpty()) {
+ d->errorMessage = currentSpirvShader->translationErrorMessage();
+ return QRhiShader();
+ }
+ shader.setEntryPoint(QByteArrayLiteral("main0"));
+ break;
+ default:
+ Q_UNREACHABLE();
+ }
+ bs.setShader(key, shader);
+ }
+ }
+
+ return bs;
+}
+
+/*!
+ \return the error message from the last bake() run, or an empty string if
+ there was no error.
+
+ \note Errors include file read errors, compilation, and translation
+ failures. Not requesting any targets or variants does not count as an error
+ even though the resulting QRhiShader is invalid.
+ */
+QString QShaderBaker::errorMessage() const
+{
+ return d->errorMessage;
+}
+
+QT_END_NAMESPACE
diff --git a/src/shadertools/qshaderbaker.h b/src/shadertools/qshaderbaker.h
new file mode 100644
index 0000000..de6159b
--- /dev/null
+++ b/src/shadertools/qshaderbaker.h
@@ -0,0 +1,78 @@
+/****************************************************************************
+**
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the Qt Shader Tools module
+**
+** $QT_BEGIN_LICENSE:LGPL3$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see http://www.qt.io/terms-conditions. For further
+** information use the contact form at http://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPLv3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or later as published by the Free
+** Software Foundation and appearing in the file LICENSE.GPL included in
+** the packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 2.0 requirements will be
+** met: http://www.gnu.org/licenses/gpl-2.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QSHADERBAKER_H
+#define QSHADERBAKER_H
+
+#include <QtShaderTools/qtshadertoolsglobal.h>
+#include <QtGui/qrhishader.h>
+
+QT_BEGIN_NAMESPACE
+
+struct QShaderBakerPrivate;
+class QIODevice;
+
+class Q_SHADERTOOLS_EXPORT QShaderBaker
+{
+public:
+ QShaderBaker();
+ ~QShaderBaker();
+
+ void setSourceFileName(const QString &fileName);
+ void setSourceFileName(const QString &fileName, QRhiShader::ShaderStage stage);
+
+ void setSourceDevice(QIODevice *device, QRhiShader::ShaderStage stage,
+ const QString &fileName = QString());
+
+ void setSourceString(const QByteArray &sourceString, QRhiShader::ShaderStage stage,
+ const QString &fileName = QString());
+
+ typedef QPair<QRhiShaderKey::ShaderSource, QRhiShaderVersion> GeneratedShader;
+ void setGeneratedShaders(const QVector<GeneratedShader> &v);
+ void setGeneratedShaderVariants(const QVector<QRhiShaderKey::ShaderVariant> &v);
+
+ QRhiShader bake();
+
+ QString errorMessage() const;
+
+private:
+ Q_DISABLE_COPY(QShaderBaker)
+ QShaderBakerPrivate *d = nullptr;
+};
+
+QT_END_NAMESPACE
+
+#endif
diff --git a/src/shadertools/qshaderbatchablerewriter.cpp b/src/shadertools/qshaderbatchablerewriter.cpp
new file mode 100644
index 0000000..72b369f
--- /dev/null
+++ b/src/shadertools/qshaderbatchablerewriter.cpp
@@ -0,0 +1,223 @@
+/****************************************************************************
+**
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the Qt Shader Tools module
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qshaderbatchablerewriter_p.h"
+
+// This is a slightly modified version of qsgshaderrewriter.cpp from
+// qtdeclarative/src/quick/scenegraph/coreapi. Here we insert an extra vertex
+// attribute (_qt_order) at location 7.
+
+QT_BEGIN_NAMESPACE
+
+namespace QShaderBatchableRewriter {
+
+struct Tokenizer {
+
+ enum Token {
+ Token_Void,
+ Token_OpenBrace,
+ Token_CloseBrace,
+ Token_SemiColon,
+ Token_Identifier,
+ Token_Macro,
+ Token_Unspecified,
+
+ Token_EOF
+ };
+
+ static const char *NAMES[];
+
+ void initialize(const QByteArray &input);
+ Token next();
+
+ const char *stream;
+ const char *pos;
+ const char *identifier;
+};
+
+const char *Tokenizer::NAMES[] = {
+ "Void",
+ "OpenBrace",
+ "CloseBrace",
+ "SemiColon",
+ "Identifier",
+ "Macro",
+ "Unspecified",
+ "EOF"
+};
+
+void Tokenizer::initialize(const QByteArray &input)
+{
+ stream = input.constData();
+ pos = input;
+ identifier = input;
+}
+
+Tokenizer::Token Tokenizer::next()
+{
+ while (*pos != 0) {
+ char c = *pos++;
+ switch (c) {
+ case '/':
+
+ if (*pos == '/') {
+ // '//' comment
+ ++pos;
+ while (*pos != 0 && *pos != '\n') ++pos;
+ if (*pos != 0) ++pos; // skip the newline
+
+ } else if (*pos == '*') {
+ // /* */ comment
+ ++pos;
+ while (*pos != 0 && *pos != '*' && pos[1] != '/') ++pos;
+ if (*pos != 0) pos += 2;
+ }
+ break;
+
+ case '#': {
+ while (*pos != 0) {
+ if (*pos == '\n') {
+ ++pos;
+ break;
+ } else if (*pos == '\\') {
+ ++pos;
+ while (*pos != 0 && (*pos == ' ' || *pos == '\t'))
+ ++pos;
+ if (*pos != 0 && (*pos == '\n' || (*pos == '\r' && pos[1] == '\n')))
+ pos+=2;
+ } else {
+ ++pos;
+ }
+ }
+ break;
+ }
+
+ case 'v': {
+ if (*pos == 'o' && pos[1] == 'i' && pos[2] == 'd') {
+ pos += 3;
+ return Token_Void;
+ }
+ Q_FALLTHROUGH();
+ }
+
+ case ';': return Token_SemiColon;
+ case 0: return Token_EOF;
+ case '{': return Token_OpenBrace;
+ case '}': return Token_CloseBrace;
+
+ case ' ':
+ case '\n':
+ case '\r': break;
+ default:
+ // Identifier...
+ if ((c >= 'a' && c <= 'z' ) || (c >= 'A' && c <= 'Z' ) || c == '_') {
+ identifier = pos - 1;
+ while (*pos != 0 && ((*pos >= 'a' && *pos <= 'z')
+ || (*pos >= 'A' && *pos <= 'Z')
+ || *pos == '_'
+ || (*pos >= '0' && *pos <= '9'))) {
+ ++pos;
+ }
+ return Token_Identifier;
+ } else {
+ return Token_Unspecified;
+ }
+ }
+ }
+
+ return Token_EOF;
+}
+
+QByteArray addZAdjustment(const QByteArray &input)
+{
+ Tokenizer tok;
+ tok.initialize(input);
+
+ Tokenizer::Token lt = tok.next();
+ Tokenizer::Token t = tok.next();
+
+ // First find "void main() { ... "
+ const char* voidPos = input.constData();
+ while (t != Tokenizer::Token_EOF) {
+ if (lt == Tokenizer::Token_Void && t == Tokenizer::Token_Identifier) {
+ if (qstrncmp("main", tok.identifier, 4) == 0)
+ break;
+ }
+ voidPos = tok.pos - 4;
+ lt = t;
+ t = tok.next();
+ }
+
+ QByteArray result;
+ result.reserve(1024);
+ result += QByteArray::fromRawData(input.constData(), voidPos - input.constData());
+
+ result += QByteArrayLiteral("layout(location = 7) in float _qt_order;\n");
+
+ // Find first brace '{'
+ while (t != Tokenizer::Token_EOF && t != Tokenizer::Token_OpenBrace) t = tok.next();
+ int braceDepth = 1;
+ t = tok.next();
+
+ // Find matching brace and insert our code there...
+ while (t != Tokenizer::Token_EOF) {
+ switch (t) {
+ case Tokenizer::Token_CloseBrace:
+ braceDepth--;
+ if (braceDepth == 0) {
+ result += QByteArray::fromRawData(voidPos, tok.pos - 1 - voidPos);
+ result += QByteArrayLiteral(" gl_Position.z = _qt_order * gl_Position.w;\n");
+ result += QByteArray(tok.pos - 1);
+ return result;
+ }
+ break;
+ case Tokenizer::Token_OpenBrace:
+ ++braceDepth;
+ break;
+ default:
+ break;
+ }
+ t = tok.next();
+ }
+ return QByteArray();
+}
+
+} // namespace
+
+QT_END_NAMESPACE
diff --git a/src/shadertools/qshaderbatchablerewriter_p.h b/src/shadertools/qshaderbatchablerewriter_p.h
new file mode 100644
index 0000000..3ee8eca
--- /dev/null
+++ b/src/shadertools/qshaderbatchablerewriter_p.h
@@ -0,0 +1,64 @@
+/****************************************************************************
+**
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the Qt Shader Tools module
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QSHADERBATCHABLEREWRITER_P_H
+#define QSHADERBATCHABLEREWRITER_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists for the convenience
+// of a number of Qt sources files. This header file may change from
+// version to version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <QtCore/QByteArray>
+
+QT_BEGIN_NAMESPACE
+
+namespace QShaderBatchableRewriter {
+QByteArray addZAdjustment(const QByteArray &input);
+}
+
+QT_END_NAMESPACE
+
+#endif
diff --git a/src/shadertools/qspirvcompiler.cpp b/src/shadertools/qspirvcompiler.cpp
new file mode 100644
index 0000000..22547e9
--- /dev/null
+++ b/src/shadertools/qspirvcompiler.cpp
@@ -0,0 +1,390 @@
+/****************************************************************************
+**
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the Qt Shader Tools module
+**
+** $QT_BEGIN_LICENSE:LGPL3$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see http://www.qt.io/terms-conditions. For further
+** information use the contact form at http://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPLv3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or later as published by the Free
+** Software Foundation and appearing in the file LICENSE.GPL included in
+** the packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 2.0 requirements will be
+** met: http://www.gnu.org/licenses/gpl-2.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qspirvcompiler_p.h"
+#include "qshaderbatchablerewriter_p.h"
+#include <QFile>
+#include <QFileInfo>
+
+#include <glslang/Public/ShaderLang.h>
+#include <SPIRV/GlslangToSpv.h>
+
+QT_BEGIN_NAMESPACE
+
+static const TBuiltInResource resourceLimits =
+{
+ /* .MaxLights = */ 32,
+ /* .MaxClipPlanes = */ 6,
+ /* .MaxTextureUnits = */ 32,
+ /* .MaxTextureCoords = */ 32,
+ /* .MaxVertexAttribs = */ 64,
+ /* .MaxVertexUniformComponents = */ 4096,
+ /* .MaxVaryingFloats = */ 64,
+ /* .MaxVertexTextureImageUnits = */ 32,
+ /* .MaxCombinedTextureImageUnits = */ 80,
+ /* .MaxTextureImageUnits = */ 32,
+ /* .MaxFragmentUniformComponents = */ 4096,
+ /* .MaxDrawBuffers = */ 32,
+ /* .MaxVertexUniformVectors = */ 128,
+ /* .MaxVaryingVectors = */ 8,
+ /* .MaxFragmentUniformVectors = */ 16,
+ /* .MaxVertexOutputVectors = */ 16,
+ /* .MaxFragmentInputVectors = */ 15,
+ /* .MinProgramTexelOffset = */ -8,
+ /* .MaxProgramTexelOffset = */ 7,
+ /* .MaxClipDistances = */ 8,
+ /* .MaxComputeWorkGroupCountX = */ 65535,
+ /* .MaxComputeWorkGroupCountY = */ 65535,
+ /* .MaxComputeWorkGroupCountZ = */ 65535,
+ /* .MaxComputeWorkGroupSizeX = */ 1024,
+ /* .MaxComputeWorkGroupSizeY = */ 1024,
+ /* .MaxComputeWorkGroupSizeZ = */ 64,
+ /* .MaxComputeUniformComponents = */ 1024,
+ /* .MaxComputeTextureImageUnits = */ 16,
+ /* .MaxComputeImageUniforms = */ 8,
+ /* .MaxComputeAtomicCounters = */ 8,
+ /* .MaxComputeAtomicCounterBuffers = */ 1,
+ /* .MaxVaryingComponents = */ 60,
+ /* .MaxVertexOutputComponents = */ 64,
+ /* .MaxGeometryInputComponents = */ 64,
+ /* .MaxGeometryOutputComponents = */ 128,
+ /* .MaxFragmentInputComponents = */ 128,
+ /* .MaxImageUnits = */ 8,
+ /* .MaxCombinedImageUnitsAndFragmentOutputs = */ 8,
+ /* .MaxCombinedShaderOutputResources = */ 8,
+ /* .MaxImageSamples = */ 0,
+ /* .MaxVertexImageUniforms = */ 0,
+ /* .MaxTessControlImageUniforms = */ 0,
+ /* .MaxTessEvaluationImageUniforms = */ 0,
+ /* .MaxGeometryImageUniforms = */ 0,
+ /* .MaxFragmentImageUniforms = */ 8,
+ /* .MaxCombinedImageUniforms = */ 8,
+ /* .MaxGeometryTextureImageUnits = */ 16,
+ /* .MaxGeometryOutputVertices = */ 256,
+ /* .MaxGeometryTotalOutputComponents = */ 1024,
+ /* .MaxGeometryUniformComponents = */ 1024,
+ /* .MaxGeometryVaryingComponents = */ 64,
+ /* .MaxTessControlInputComponents = */ 128,
+ /* .MaxTessControlOutputComponents = */ 128,
+ /* .MaxTessControlTextureImageUnits = */ 16,
+ /* .MaxTessControlUniformComponents = */ 1024,
+ /* .MaxTessControlTotalOutputComponents = */ 4096,
+ /* .MaxTessEvaluationInputComponents = */ 128,
+ /* .MaxTessEvaluationOutputComponents = */ 128,
+ /* .MaxTessEvaluationTextureImageUnits = */ 16,
+ /* .MaxTessEvaluationUniformComponents = */ 1024,
+ /* .MaxTessPatchComponents = */ 120,
+ /* .MaxPatchVertices = */ 32,
+ /* .MaxTessGenLevel = */ 64,
+ /* .MaxViewports = */ 16,
+ /* .MaxVertexAtomicCounters = */ 0,
+ /* .MaxTessControlAtomicCounters = */ 0,
+ /* .MaxTessEvaluationAtomicCounters = */ 0,
+ /* .MaxGeometryAtomicCounters = */ 0,
+ /* .MaxFragmentAtomicCounters = */ 8,
+ /* .MaxCombinedAtomicCounters = */ 8,
+ /* .MaxAtomicCounterBindings = */ 1,
+ /* .MaxVertexAtomicCounterBuffers = */ 0,
+ /* .MaxTessControlAtomicCounterBuffers = */ 0,
+ /* .MaxTessEvaluationAtomicCounterBuffers = */ 0,
+ /* .MaxGeometryAtomicCounterBuffers = */ 0,
+ /* .MaxFragmentAtomicCounterBuffers = */ 1,
+ /* .MaxCombinedAtomicCounterBuffers = */ 1,
+ /* .MaxAtomicCounterBufferSize = */ 16384,
+ /* .MaxTransformFeedbackBuffers = */ 4,
+ /* .MaxTransformFeedbackInterleavedComponents = */ 64,
+ /* .MaxCullDistances = */ 8,
+ /* .MaxCombinedClipAndCullDistances = */ 8,
+ /* .MaxSamples = */ 4,
+ /* .maxMeshOutputVerticesNV = */ 256,
+ /* .maxMeshOutputPrimitivesNV = */ 512,
+ /* .maxMeshWorkGroupSizeX_NV = */ 32,
+ /* .maxMeshWorkGroupSizeY_NV = */ 1,
+ /* .maxMeshWorkGroupSizeZ_NV = */ 1,
+ /* .maxTaskWorkGroupSizeX_NV = */ 32,
+ /* .maxTaskWorkGroupSizeY_NV = */ 1,
+ /* .maxTaskWorkGroupSizeZ_NV = */ 1,
+ /* .maxMeshViewCountNV = */ 4,
+
+ /* .limits = */ {
+ /* .nonInductiveForLoops = */ 1,
+ /* .whileLoops = */ 1,
+ /* .doWhileLoops = */ 1,
+ /* .generalUniformIndexing = */ 1,
+ /* .generalAttributeMatrixVectorIndexing = */ 1,
+ /* .generalVaryingIndexing = */ 1,
+ /* .generalSamplerIndexing = */ 1,
+ /* .generalVariableIndexing = */ 1,
+ /* .generalConstantMatrixVectorIndexing = */ 1,
+ }
+};
+
+struct QSpirvCompilerPrivate
+{
+ bool readFile(const QString &fn);
+ bool compile();
+
+ QString sourceFileName;
+ QByteArray source;
+ QByteArray batchableSource;
+ EShLanguage stage = EShLangVertex;
+ QSpirvCompiler::Flags flags = 0;
+ QByteArray spirv;
+ QString log;
+};
+
+bool QSpirvCompilerPrivate::readFile(const QString &fn)
+{
+ QFile f(fn);
+ if (!f.open(QIODevice::ReadOnly | QIODevice::Text)) {
+ qWarning("QSpirvCompiler: Failed to open %s", qPrintable(fn));
+ return false;
+ }
+ source = f.readAll();
+ batchableSource.clear();
+ sourceFileName = fn;
+ f.close();
+ return true;
+}
+
+class Includer : public glslang::TShader::Includer
+{
+public:
+ IncludeResult *includeLocal(const char *headerName,
+ const char *includerName,
+ size_t inclusionDepth) override
+ {
+ Q_UNUSED(inclusionDepth);
+ return readFile(headerName, includerName);
+ }
+
+ IncludeResult *includeSystem(const char *headerName,
+ const char *includerName,
+ size_t inclusionDepth) override
+ {
+ Q_UNUSED(inclusionDepth);
+ return readFile(headerName, includerName);
+ }
+
+ void releaseInclude(IncludeResult *result) override
+ {
+ if (result) {
+ delete static_cast<QByteArray *>(result->userData);
+ delete result;
+ }
+ }
+
+private:
+ IncludeResult *readFile(const char *headerName, const char *includerName);
+};
+
+glslang::TShader::Includer::IncludeResult *Includer::readFile(const char *headerName, const char *includerName)
+{
+ // Just treat the included name as relative to the includer:
+ // Take the path from the includer, append the included name, remove redundancies.
+ // This should work also for qrc (source filenames with qrc:/ or :/ prefix).
+
+ QString includer = QString::fromUtf8(includerName);
+ if (includer.isEmpty())
+ includer = QLatin1String(".");
+ QString included = QFileInfo(includer).canonicalPath() + QLatin1Char('/') + QString::fromUtf8(headerName);
+ included = QFileInfo(included).canonicalFilePath();
+ if (included.isEmpty()) {
+ qWarning("QSpirvCompiler: Failed to find include file %s", headerName);
+ return nullptr;
+ }
+ QFile f(included);
+ if (!f.open(QIODevice::ReadOnly | QIODevice::Text)) {
+ qWarning("QSpirvCompiler: Failed to read include file %s", qPrintable(included));
+ return nullptr;
+ }
+
+ QByteArray *data = new QByteArray;
+ *data = f.readAll();
+ return new IncludeResult(included.toStdString(), data->constData(), data->size(), data);
+}
+
+class GlobalInit
+{
+public:
+ GlobalInit() { glslang::InitializeProcess(); }
+ ~GlobalInit() { glslang::FinalizeProcess(); }
+};
+
+bool QSpirvCompilerPrivate::compile()
+{
+ log.clear();
+
+ const bool useBatchable = (stage == EShLangVertex && flags.testFlag(QSpirvCompiler::RewriteToMakeBatchableForSG));
+ const QByteArray *actualSource = useBatchable ? &batchableSource : &source;
+ if (actualSource->isEmpty())
+ return false;
+
+ static GlobalInit globalInit;
+
+ glslang::TShader shader(stage);
+ const QByteArray fn = sourceFileName.toUtf8();
+ const char *fnStr = fn.constData();
+ const char *srcStr = actualSource->constData();
+ const int size = actualSource->size();
+ shader.setStringsWithLengthsAndNames(&srcStr, &size, &fnStr, 1);
+
+ shader.setEnvInput(glslang::EShSourceGlsl, stage, glslang::EShClientVulkan, 100);
+ shader.setEnvClient(glslang::EShClientVulkan, glslang::EShTargetVulkan_1_0);
+ shader.setEnvTarget(glslang::EshTargetSpv, glslang::EShTargetSpv_1_0);
+
+ Includer includer;
+ if (!shader.parse(&resourceLimits, 100, false, EShMsgDefault, includer)) {
+ qWarning("QSpirvCompiler: Failed to parse shader");
+ log = QString::fromUtf8(shader.getInfoLog()).trimmed();
+ return false;
+ }
+
+ glslang::TProgram program;
+ program.addShader(&shader);
+ if (!program.link(EShMsgDefault)) {
+ qWarning("QSpirvCompiler: Link failed");
+ log = QString::fromUtf8(shader.getInfoLog()).trimmed();
+ return false;
+ }
+
+ std::vector<unsigned int> spv;
+ glslang::GlslangToSpv(*program.getIntermediate(stage), spv);
+ if (!spv.size()) {
+ qWarning("Failed to generate SPIR-V");
+ return false;
+ }
+
+ spirv.resize(int(spv.size() * 4));
+ memcpy(spirv.data(), spv.data(), spirv.size());
+
+ return true;
+}
+
+QSpirvCompiler::QSpirvCompiler()
+ : d(new QSpirvCompilerPrivate)
+{
+}
+
+QSpirvCompiler::~QSpirvCompiler()
+{
+ delete d;
+}
+
+void QSpirvCompiler::setSourceFileName(const QString &fileName)
+{
+ if (!d->readFile(fileName))
+ return;
+
+ const QString suffix = QFileInfo(fileName).suffix();
+ if (suffix == QStringLiteral("vert")) {
+ d->stage = EShLangVertex;
+ } else if (suffix == QStringLiteral("frag")) {
+ d->stage = EShLangFragment;
+ } else if (suffix == QStringLiteral("tesc")) {
+ d->stage = EShLangTessControl;
+ } else if (suffix == QStringLiteral("tese")) {
+ d->stage = EShLangTessEvaluation;
+ } else if (suffix == QStringLiteral("geom")) {
+ d->stage = EShLangGeometry;
+ } else if (suffix == QStringLiteral("comp")) {
+ d->stage = EShLangCompute;
+ } else {
+ qWarning("QSpirvCompiler: Unknown shader stage, defaulting to vertex");
+ d->stage = EShLangVertex;
+ }
+}
+
+static inline EShLanguage mapShaderStage(QRhiShader::ShaderStage stage)
+{
+ switch (stage) {
+ case QRhiShader::VertexStage:
+ return EShLangVertex;
+ case QRhiShader::TessControlStage:
+ return EShLangTessControl;
+ case QRhiShader::TessEvaluationStage:
+ return EShLangTessEvaluation;
+ case QRhiShader::GeometryStage:
+ return EShLangGeometry;
+ case QRhiShader::FragmentStage:
+ return EShLangFragment;
+ case QRhiShader::ComputeStage:
+ return EShLangCompute;
+ default:
+ return EShLangVertex;
+ }
+}
+
+void QSpirvCompiler::setSourceFileName(const QString &fileName, QRhiShader::ShaderStage stage)
+{
+ if (!d->readFile(fileName))
+ return;
+
+ d->stage = mapShaderStage(stage);
+}
+
+void QSpirvCompiler::setSourceDevice(QIODevice *device, QRhiShader::ShaderStage stage, const QString &fileName)
+{
+ setSourceString(device->readAll(), stage, fileName);
+}
+
+void QSpirvCompiler::setSourceString(const QByteArray &sourceString, QRhiShader::ShaderStage stage, const QString &fileName)
+{
+ d->sourceFileName = fileName; // for error messages, include handling, etc.
+ d->source = sourceString;
+ d->batchableSource.clear();
+ d->stage = mapShaderStage(stage);
+}
+
+void QSpirvCompiler::setFlags(Flags flags)
+{
+ d->flags = flags;
+}
+
+QByteArray QSpirvCompiler::compileToSpirv()
+{
+ if (d->stage == EShLangVertex && d->flags.testFlag(RewriteToMakeBatchableForSG) && d->batchableSource.isEmpty())
+ d->batchableSource = QShaderBatchableRewriter::addZAdjustment(d->source);
+
+ return d->compile() ? d->spirv : QByteArray();
+}
+
+QString QSpirvCompiler::errorMessage() const
+{
+ return d->log;
+}
+
+QT_END_NAMESPACE
diff --git a/src/shadertools/qspirvcompiler_p.h b/src/shadertools/qspirvcompiler_p.h
new file mode 100644
index 0000000..2af56b2
--- /dev/null
+++ b/src/shadertools/qspirvcompiler_p.h
@@ -0,0 +1,89 @@
+/****************************************************************************
+**
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the Qt Shader Tools module
+**
+** $QT_BEGIN_LICENSE:LGPL3$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see http://www.qt.io/terms-conditions. For further
+** information use the contact form at http://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPLv3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or later as published by the Free
+** Software Foundation and appearing in the file LICENSE.GPL included in
+** the packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 2.0 requirements will be
+** met: http://www.gnu.org/licenses/gpl-2.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QSPIRVCOMPILER_P_H
+#define QSPIRVCOMPILER_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists for the convenience
+// of a number of Qt sources files. This header file may change from
+// version to version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <QtShaderTools/private/qtshadertoolsglobal_p.h>
+#include <QtGui/qrhishader.h>
+#include <QtCore/QString>
+
+QT_BEGIN_NAMESPACE
+
+struct QSpirvCompilerPrivate;
+class QIODevice;
+
+class Q_SHADERTOOLS_PRIVATE_EXPORT QSpirvCompiler
+{
+public:
+ QSpirvCompiler();
+ ~QSpirvCompiler();
+
+ enum Flag {
+ RewriteToMakeBatchableForSG = 0x01
+ };
+ Q_DECLARE_FLAGS(Flags, Flag)
+
+ void setSourceFileName(const QString &fileName);
+ void setSourceFileName(const QString &fileName, QRhiShader::ShaderStage stage);
+ void setSourceDevice(QIODevice *device, QRhiShader::ShaderStage stage, const QString &fileName = QString());
+ void setSourceString(const QByteArray &sourceString, QRhiShader::ShaderStage stage, const QString &fileName = QString());
+ void setFlags(Flags flags);
+
+ QByteArray compileToSpirv();
+ QString errorMessage() const;
+
+private:
+ Q_DISABLE_COPY(QSpirvCompiler)
+ QSpirvCompilerPrivate *d = nullptr;
+};
+
+Q_DECLARE_OPERATORS_FOR_FLAGS(QSpirvCompiler::Flags)
+
+QT_END_NAMESPACE
+
+#endif
diff --git a/src/shadertools/qspirvshader.cpp b/src/shadertools/qspirvshader.cpp
new file mode 100644
index 0000000..7e0057e
--- /dev/null
+++ b/src/shadertools/qspirvshader.cpp
@@ -0,0 +1,468 @@
+/****************************************************************************
+**
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the Qt Shader Tools module
+**
+** $QT_BEGIN_LICENSE:LGPL3$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see http://www.qt.io/terms-conditions. For further
+** information use the contact form at http://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPLv3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or later as published by the Free
+** Software Foundation and appearing in the file LICENSE.GPL included in
+** the packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 2.0 requirements will be
+** met: http://www.gnu.org/licenses/gpl-2.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qspirvshader_p.h"
+#include <QtGui/private/qrhishaderdescription_p.h>
+#include <QFile>
+#include <QDebug>
+
+#include <SPIRV/SPVRemapper.h>
+
+#include <spirv_glsl.hpp>
+#include <spirv_hlsl.hpp>
+#include <spirv_msl.hpp>
+
+QT_BEGIN_NAMESPACE
+
+struct QSpirvShaderPrivate
+{
+ ~QSpirvShaderPrivate();
+
+ void createGLSLCompiler();
+ void processResources();
+
+ QRhiShaderDescription::InOutVariable inOutVar(const spirv_cross::Resource &r);
+ QRhiShaderDescription::BlockVariable blockVar(uint32_t typeId,
+ uint32_t memberIdx,
+ uint32_t memberTypeId);
+
+ void remapErrorHandler(const std::string &s);
+ void remapLogHandler(const std::string &s);
+
+ QByteArray ir;
+ QRhiShaderDescription shaderDescription;
+
+ spirv_cross::CompilerGLSL *glslGen = nullptr;
+ spirv_cross::CompilerHLSL *hlslGen = nullptr;
+ spirv_cross::CompilerMSL *mslGen = nullptr;
+
+ QString spirvCrossErrorMsg;
+ QString remapErrorMsg;
+};
+
+QSpirvShaderPrivate::~QSpirvShaderPrivate()
+{
+ delete mslGen;
+ delete hlslGen;
+ delete glslGen;
+}
+
+void QSpirvShaderPrivate::createGLSLCompiler()
+{
+ delete glslGen;
+ glslGen = new spirv_cross::CompilerGLSL(reinterpret_cast<const uint32_t *>(ir.constData()), ir.size() / 4);
+}
+
+static QRhiShaderDescription::VarType matVarType(const spirv_cross::SPIRType &t, QRhiShaderDescription::VarType compType)
+{
+ switch (t.columns) {
+ case 2:
+ return QRhiShaderDescription::VarType(compType + 4 + (t.vecsize == 3 ? 1 : t.vecsize == 4 ? 2 : 0));
+ case 3:
+ return QRhiShaderDescription::VarType(compType + 7 + (t.vecsize == 2 ? 1 : t.vecsize == 4 ? 2 : 0));
+ case 4:
+ return QRhiShaderDescription::VarType(compType + 10 + (t.vecsize == 2 ? 1 : t.vecsize == 3 ? 2 : 0));
+ default:
+ return QRhiShaderDescription::Unknown;
+ }
+}
+
+static QRhiShaderDescription::VarType vecVarType(const spirv_cross::SPIRType &t, QRhiShaderDescription::VarType compType)
+{
+ switch (t.vecsize) {
+ case 1:
+ return compType;
+ case 2:
+ return QRhiShaderDescription::VarType(compType + 1);
+ case 3:
+ return QRhiShaderDescription::VarType(compType + 2);
+ case 4:
+ return QRhiShaderDescription::VarType(compType + 3);
+ default:
+ return QRhiShaderDescription::Unknown;
+ }
+}
+
+static QRhiShaderDescription::VarType imageVarType(const spirv_cross::SPIRType &t)
+{
+ switch (t.image.dim) {
+ case spv::Dim1D:
+ return t.image.arrayed ? QRhiShaderDescription::Sampler1DArray : QRhiShaderDescription::Sampler1D;
+ case spv::Dim2D:
+ return t.image.arrayed
+ ? (t.image.ms ? QRhiShaderDescription::Sampler2DMSArray : QRhiShaderDescription::Sampler2DArray)
+ : (t.image.ms ? QRhiShaderDescription::Sampler2DMS : QRhiShaderDescription::Sampler2D);
+ case spv::Dim3D:
+ return t.image.arrayed ? QRhiShaderDescription::Sampler3DArray : QRhiShaderDescription::Sampler3D;
+ case spv::DimCube:
+ return t.image.arrayed ? QRhiShaderDescription::SamplerCubeArray : QRhiShaderDescription::SamplerCube;
+ default:
+ return QRhiShaderDescription::Unknown;
+ }
+}
+
+static QRhiShaderDescription::VarType varType(const spirv_cross::SPIRType &t)
+{
+ QRhiShaderDescription::VarType vt = QRhiShaderDescription::Unknown;
+ switch (t.basetype) {
+ case spirv_cross::SPIRType::Float:
+ vt = t.columns > 1 ? matVarType(t, QRhiShaderDescription::Float) : vecVarType(t, QRhiShaderDescription::Float);
+ break;
+ case spirv_cross::SPIRType::Double:
+ vt = t.columns > 1 ? matVarType(t, QRhiShaderDescription::Double) : vecVarType(t, QRhiShaderDescription::Double);
+ break;
+ case spirv_cross::SPIRType::UInt:
+ vt = vecVarType(t, QRhiShaderDescription::Uint);
+ break;
+ case spirv_cross::SPIRType::Int:
+ vt = vecVarType(t, QRhiShaderDescription::Int);
+ break;
+ case spirv_cross::SPIRType::Boolean:
+ vt = vecVarType(t, QRhiShaderDescription::Uint);
+ break;
+ case spirv_cross::SPIRType::SampledImage:
+ vt = imageVarType(t);
+ break;
+ case spirv_cross::SPIRType::Struct:
+ vt = QRhiShaderDescription::Struct;
+ break;
+ // ### separate image/sampler, atomic counter, ...
+ default:
+ break;
+ }
+ return vt;
+}
+
+QRhiShaderDescription::InOutVariable QSpirvShaderPrivate::inOutVar(const spirv_cross::Resource &r)
+{
+ QRhiShaderDescription::InOutVariable v;
+ v.name = QString::fromStdString(r.name);
+
+ const spirv_cross::SPIRType &t = glslGen->get_type(r.base_type_id);
+ v.type = varType(t);
+
+ if (glslGen->has_decoration(r.id, spv::DecorationLocation))
+ v.location = glslGen->get_decoration(r.id, spv::DecorationLocation);
+
+ if (glslGen->has_decoration(r.id, spv::DecorationBinding))
+ v.binding = glslGen->get_decoration(r.id, spv::DecorationBinding);
+
+ if (glslGen->has_decoration(r.id, spv::DecorationDescriptorSet))
+ v.descriptorSet = glslGen->get_decoration(r.id, spv::DecorationDescriptorSet);
+
+ return v;
+}
+
+QRhiShaderDescription::BlockVariable QSpirvShaderPrivate::blockVar(uint32_t typeId,
+ uint32_t memberIdx,
+ uint32_t memberTypeId)
+{
+ QRhiShaderDescription::BlockVariable v;
+ v.name = QString::fromStdString(glslGen->get_member_name(typeId, memberIdx));
+
+ const spirv_cross::SPIRType &memberType(glslGen->get_type(memberTypeId));
+ v.type = varType(memberType);
+
+ const spirv_cross::SPIRType &t = glslGen->get_type(typeId);
+ v.offset = glslGen->type_struct_member_offset(t, memberIdx);
+ v.size = int(glslGen->get_declared_struct_member_size(t, memberIdx));
+
+ for (uint32_t dimSize : memberType.array)
+ v.arrayDims.append(dimSize);
+
+ if (glslGen->has_member_decoration(typeId, memberIdx, spv::DecorationArrayStride))
+ v.arrayStride = glslGen->type_struct_member_array_stride(t, memberIdx);
+
+ if (glslGen->has_member_decoration(typeId, memberIdx, spv::DecorationMatrixStride))
+ v.matrixStride = glslGen->type_struct_member_matrix_stride(t, memberIdx);
+
+ if (glslGen->has_member_decoration(typeId, memberIdx, spv::DecorationRowMajor))
+ v.matrixIsRowMajor = true;
+
+ if (v.type == QRhiShaderDescription::Struct) {
+ uint32_t memberMemberIdx = 0;
+ for (uint32_t memberMemberType : memberType.member_types) {
+ v.structMembers.append(blockVar(memberType.self, memberMemberIdx, memberMemberType));
+ ++memberMemberIdx;
+ }
+ }
+
+ return v;
+}
+
+void QSpirvShaderPrivate::processResources()
+{
+ shaderDescription = QRhiShaderDescription();
+ QRhiShaderDescriptionPrivate *dd = QRhiShaderDescriptionPrivate::get(&shaderDescription);
+
+ spirv_cross::ShaderResources resources = glslGen->get_shader_resources();
+
+ /* ###
+ std::vector<Resource> uniform_buffers;
+ std::vector<Resource> storage_buffers;
+ std::vector<Resource> stage_inputs;
+ std::vector<Resource> stage_outputs;
+ std::vector<Resource> subpass_inputs;
+ std::vector<Resource> storage_images;
+ std::vector<Resource> sampled_images;
+ std::vector<Resource> atomic_counters;
+ std::vector<Resource> push_constant_buffers;
+ std::vector<Resource> separate_images;
+ std::vector<Resource> separate_samplers;
+ */
+
+ for (const spirv_cross::Resource &r : resources.stage_inputs) {
+ const QRhiShaderDescription::InOutVariable v = inOutVar(r);
+ if (v.type != QRhiShaderDescription::Unknown)
+ dd->inVars.append(v);
+ }
+
+ for (const spirv_cross::Resource &r : resources.stage_outputs) {
+ const QRhiShaderDescription::InOutVariable v = inOutVar(r);
+ if (v.type != QRhiShaderDescription::Unknown)
+ dd->outVars.append(v);
+ }
+
+ // uniform blocks map to either a uniform buffer or a plain struct
+ for (const spirv_cross::Resource &r : resources.uniform_buffers) {
+ const spirv_cross::SPIRType &t = glslGen->get_type(r.base_type_id);
+ QRhiShaderDescription::UniformBlock block;
+ block.blockName = QString::fromStdString(r.name);
+ block.structName = QString::fromStdString(glslGen->get_name(r.id));
+ block.size = int(glslGen->get_declared_struct_size(t));
+ if (glslGen->has_decoration(r.id, spv::DecorationBinding))
+ block.binding = glslGen->get_decoration(r.id, spv::DecorationBinding);
+ if (glslGen->has_decoration(r.id, spv::DecorationDescriptorSet))
+ block.descriptorSet = glslGen->get_decoration(r.id, spv::DecorationDescriptorSet);
+ uint32_t idx = 0;
+ for (uint32_t memberTypeId : t.member_types) {
+ const QRhiShaderDescription::BlockVariable v = blockVar(r.base_type_id, idx, memberTypeId);
+ ++idx;
+ if (v.type != QRhiShaderDescription::Unknown)
+ block.members.append(v);
+ }
+ dd->uniformBlocks.append(block);
+ }
+
+ // push constant blocks map to a plain GLSL struct regardless of version
+ for (const spirv_cross::Resource &r : resources.push_constant_buffers) {
+ const spirv_cross::SPIRType &t = glslGen->get_type(r.base_type_id);
+ QRhiShaderDescription::PushConstantBlock block;
+ block.name = QString::fromStdString(glslGen->get_name(r.id));
+ block.size = int(glslGen->get_declared_struct_size(t));
+ uint32_t idx = 0;
+ for (uint32_t memberTypeId : t.member_types) {
+ const QRhiShaderDescription::BlockVariable v = blockVar(r.base_type_id, idx, memberTypeId);
+ ++idx;
+ if (v.type != QRhiShaderDescription::Unknown)
+ block.members.append(v);
+ }
+ dd->pushConstantBlocks.append(block);
+ }
+
+ for (const spirv_cross::Resource &r : resources.sampled_images) {
+ const QRhiShaderDescription::InOutVariable v = inOutVar(r);
+ if (v.type != QRhiShaderDescription::Unknown)
+ dd->combinedImageSamplers.append(v);
+ }
+}
+
+QSpirvShader::QSpirvShader()
+ : d(new QSpirvShaderPrivate)
+{
+}
+
+QSpirvShader::~QSpirvShader()
+{
+ delete d;
+}
+
+void QSpirvShader::setFileName(const QString &fileName)
+{
+ QFile f(fileName);
+ if (!f.open(QIODevice::ReadOnly)) {
+ qWarning("QSpirvShader: Failed to open %s", qPrintable(fileName));
+ return;
+ }
+ setDevice(&f);
+}
+
+void QSpirvShader::setDevice(QIODevice *device)
+{
+ d->ir = device->readAll();
+ d->createGLSLCompiler();
+ d->processResources();
+}
+
+void QSpirvShader::setSpirvBinary(const QByteArray &spirv)
+{
+ d->ir = spirv;
+ d->createGLSLCompiler();
+ d->processResources();
+}
+
+QRhiShaderDescription QSpirvShader::shaderDescription() const
+{
+ return d->shaderDescription;
+}
+
+void QSpirvShaderPrivate::remapErrorHandler(const std::string &s)
+{
+ if (!remapErrorMsg.isEmpty())
+ remapErrorMsg.append(QLatin1Char('\n'));
+ remapErrorMsg.append(QString::fromStdString(s));
+}
+
+void QSpirvShaderPrivate::remapLogHandler(const std::string &)
+{
+}
+
+QByteArray QSpirvShader::strippedSpirvBinary(StripFlags flags, QString *errorMessage) const
+{
+ if (d->ir.isEmpty())
+ return QByteArray();
+
+ spv::spirvbin_t b;
+
+ d->remapErrorMsg.clear();
+ b.registerErrorHandler(std::bind(&QSpirvShaderPrivate::remapErrorHandler, d, std::placeholders::_1));
+ b.registerLogHandler(std::bind(&QSpirvShaderPrivate::remapLogHandler, d, std::placeholders::_1));
+
+ const uint32_t opts = flags.testFlag(Remap) ? spv::spirvbin_t::DO_EVERYTHING : spv::spirvbin_t::STRIP;
+
+ std::vector<uint32_t> v;
+ v.resize(d->ir.size() / 4);
+ memcpy(v.data(), d->ir.constData(), d->ir.size());
+
+ b.remap(v, opts);
+
+ if (!d->remapErrorMsg.isEmpty()) {
+ if (errorMessage)
+ *errorMessage = d->remapErrorMsg;
+ return QByteArray();
+ }
+
+ return QByteArray(reinterpret_cast<const char *>(v.data()), int(v.size()) * 4);
+}
+
+QByteArray QSpirvShader::translateToGLSL(int version, GlslFlags flags) const
+{
+ d->spirvCrossErrorMsg.clear();
+
+ try {
+ // create a new instance every time since option handling seem to be problematic
+ // (won't pick up new options on the second and subsequent compile())
+ d->createGLSLCompiler();
+
+ spirv_cross::CompilerGLSL::Options options;
+ options.version = version;
+ options.es = flags.testFlag(GlslEs);
+ options.vertex.fixup_clipspace = flags.testFlag(FixClipSpace);
+ options.fragment.default_float_precision = flags.testFlag(FragDefaultMediump)
+ ? spirv_cross::CompilerGLSL::Options::Mediump
+ : spirv_cross::CompilerGLSL::Options::Highp;
+ d->glslGen->set_common_options(options);
+
+ const std::string glsl = d->glslGen->compile();
+
+ QByteArray src = QByteArray::fromStdString(glsl);
+
+ // Fix it up by adding #extension GL_ARB_separate_shader_objects : require
+ // as well in order to make Mesa and perhaps others happy.
+ const QByteArray searchStr = QByteArrayLiteral("#extension GL_ARB_shading_language_420pack : require\n#endif\n");
+ int pos = src.indexOf(searchStr);
+ if (pos >= 0) {
+ src.insert(pos + searchStr.count(), QByteArrayLiteral("#ifdef GL_ARB_separate_shader_objects\n"
+ "#extension GL_ARB_separate_shader_objects : require\n"
+ "#endif\n"));
+ }
+
+ return src;
+ } catch (const std::runtime_error &e) {
+ d->spirvCrossErrorMsg = QString::fromUtf8(e.what());
+ return QByteArray();
+ }
+}
+
+QByteArray QSpirvShader::translateToHLSL(int version) const
+{
+ d->spirvCrossErrorMsg.clear();
+
+ try {
+ if (!d->hlslGen)
+ d->hlslGen = new spirv_cross::CompilerHLSL(reinterpret_cast<const uint32_t *>(d->ir.constData()), d->ir.size() / 4);
+
+ spirv_cross::CompilerHLSL::Options options;
+ options.shader_model = version;
+ d->hlslGen->set_hlsl_options(options);
+
+ const std::string hlsl = d->hlslGen->compile();
+
+ return QByteArray::fromStdString(hlsl);
+ } catch (const std::runtime_error &e) {
+ d->spirvCrossErrorMsg = QString::fromUtf8(e.what());
+ return QByteArray();
+ }
+}
+
+QByteArray QSpirvShader::translateToMSL(int version) const
+{
+ d->spirvCrossErrorMsg.clear();
+
+ try {
+ if (!d->mslGen)
+ d->mslGen = new spirv_cross::CompilerMSL(reinterpret_cast<const uint32_t *>(d->ir.constData()), d->ir.size() / 4);
+
+ spirv_cross::CompilerMSL::Options options;
+ options.msl_version = spirv_cross::CompilerMSL::Options::make_msl_version(version / 10, version % 10);
+ // leave platform set to macOS, it won't matter in practice (hopefully)
+ d->mslGen->set_msl_options(options);
+
+ const std::string msl = d->mslGen->compile();
+
+ return QByteArray::fromStdString(msl);
+ } catch (const std::runtime_error &e) {
+ d->spirvCrossErrorMsg = QString::fromUtf8(e.what());
+ return QByteArray();
+ }
+}
+
+QString QSpirvShader::translationErrorMessage() const
+{
+ return d->spirvCrossErrorMsg;
+}
+
+QT_END_NAMESPACE
diff --git a/src/shadertools/qspirvshader_p.h b/src/shadertools/qspirvshader_p.h
new file mode 100644
index 0000000..3191173
--- /dev/null
+++ b/src/shadertools/qspirvshader_p.h
@@ -0,0 +1,101 @@
+/****************************************************************************
+**
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the Qt Shader Tools module
+**
+** $QT_BEGIN_LICENSE:LGPL3$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see http://www.qt.io/terms-conditions. For further
+** information use the contact form at http://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPLv3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or later as published by the Free
+** Software Foundation and appearing in the file LICENSE.GPL included in
+** the packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 2.0 requirements will be
+** met: http://www.gnu.org/licenses/gpl-2.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QSPIRVSHADER_P_H
+#define QSPIRVSHADER_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists for the convenience
+// of a number of Qt sources files. This header file may change from
+// version to version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <QtShaderTools/private/qtshadertoolsglobal_p.h>
+#include <QtGui/qrhishaderdescription.h>
+
+QT_BEGIN_NAMESPACE
+
+class QIODevice;
+struct QSpirvShaderPrivate;
+
+class Q_SHADERTOOLS_PRIVATE_EXPORT QSpirvShader
+{
+public:
+ enum GlslFlag {
+ GlslEs = 0x01,
+ FixClipSpace = 0x02,
+ FragDefaultMediump = 0x04
+ };
+ Q_DECLARE_FLAGS(GlslFlags, GlslFlag)
+
+ enum StripFlag {
+ Remap = 0x01
+ };
+ Q_DECLARE_FLAGS(StripFlags, StripFlag)
+
+ QSpirvShader();
+ ~QSpirvShader();
+
+ void setFileName(const QString &fileName);
+ void setDevice(QIODevice *device);
+ void setSpirvBinary(const QByteArray &spirv);
+
+ QRhiShaderDescription shaderDescription() const;
+
+ QByteArray strippedSpirvBinary(StripFlags flags = StripFlags(), QString *errorMessage = nullptr) const;
+
+ QByteArray translateToGLSL(int version = 120, GlslFlags flags = GlslFlags()) const;
+ QByteArray translateToHLSL(int version = 50) const;
+ QByteArray translateToMSL(int version = 12) const;
+
+ QString translationErrorMessage() const;
+
+private:
+ Q_DISABLE_COPY(QSpirvShader)
+ QSpirvShaderPrivate *d = nullptr;
+};
+
+Q_DECLARE_OPERATORS_FOR_FLAGS(QSpirvShader::GlslFlags)
+Q_DECLARE_OPERATORS_FOR_FLAGS(QSpirvShader::StripFlags)
+
+QT_END_NAMESPACE
+
+#endif
diff --git a/src/shadertools/qtshadertoolsglobal.h b/src/shadertools/qtshadertoolsglobal.h
new file mode 100644
index 0000000..fda42ff
--- /dev/null
+++ b/src/shadertools/qtshadertoolsglobal.h
@@ -0,0 +1,62 @@
+/****************************************************************************
+**
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the Qt Shader Tools module
+**
+** $QT_BEGIN_LICENSE:LGPL3$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see http://www.qt.io/terms-conditions. For further
+** information use the contact form at http://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPLv3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or later as published by the Free
+** Software Foundation and appearing in the file LICENSE.GPL included in
+** the packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 2.0 requirements will be
+** met: http://www.gnu.org/licenses/gpl-2.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QTSHADERTOOLSGLOBAL_H
+#define QTSHADERTOOLSGLOBAL_H
+
+#include <QtCore/qglobal.h>
+
+QT_BEGIN_NAMESPACE
+
+#ifndef Q_SHADERTOOLS_EXPORT
+# if !defined(QT_STATIC)
+# if defined(QT_BUILD_SHADERTOOLS_LIB)
+# define Q_SHADERTOOLS_EXPORT Q_DECL_EXPORT
+# else
+# define Q_SHADERTOOLS_EXPORT Q_DECL_IMPORT
+# endif
+# else
+# define Q_SHADERTOOLS_EXPORT
+# endif
+#endif
+
+#ifdef Q_CLANG_QDOC
+#define Q_SHADERTOOLS_EXPORT
+#endif
+
+QT_END_NAMESPACE
+
+#endif
diff --git a/src/shadertools/qtshadertoolsglobal_p.h b/src/shadertools/qtshadertoolsglobal_p.h
new file mode 100644
index 0000000..3516624
--- /dev/null
+++ b/src/shadertools/qtshadertoolsglobal_p.h
@@ -0,0 +1,48 @@
+/****************************************************************************
+**
+** Copyright (C) 2018 The Qt Company Ltd.
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the Qt Shader Tools module
+**
+** $QT_BEGIN_LICENSE:GPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 or (at your option) any later version
+** approved by the KDE Free Qt Foundation. The licenses are as published by
+** the Free Software Foundation and appearing in the file LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QTSHADERTOOLSGLOBAL_P_H
+#define QTSHADERTOOLSGLOBAL_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include "qtshadertoolsglobal.h"
+
+#define Q_SHADERTOOLS_PRIVATE_EXPORT Q_SHADERTOOLS_EXPORT
+
+#endif
diff --git a/src/shadertools/shadertools.pro b/src/shadertools/shadertools.pro
new file mode 100644
index 0000000..e9123b3
--- /dev/null
+++ b/src/shadertools/shadertools.pro
@@ -0,0 +1,41 @@
+TARGET = QtShaderTools
+
+QT += gui-private
+
+DEFINES += QT_BUILD_SHADERTOOLS_LIB
+
+HEADERS += \
+ $$PWD/qtshadertoolsglobal.h \
+ $$PWD/qshaderbaker.h \
+ $$PWD/qspirvshader_p.h \
+ $$PWD/qspirvcompiler_p.h \
+ $$PWD/qshaderbatchablerewriter_p.h
+
+SOURCES += \
+ $$PWD/qshaderbaker.cpp \
+ $$PWD/qspirvshader.cpp \
+ $$PWD/qspirvcompiler.cpp \
+ $$PWD/qshaderbatchablerewriter.cpp
+
+INCLUDEPATH += $$PWD/../3rdparty/SPIRV-Cross $$PWD/../3rdparty/glslang
+
+# Exceptions must be enabled since that is the only sane way to get errors reported from SPIRV-Cross.
+# They will not propagate outside of this module though so should be safe enough.
+CONFIG += exceptions
+
+!exists($$[QT_HOST_DATA]/.qmake.cache) {
+ LIBLOC = $$shadowed($$dirname(_QMAKE_CONF_))/lib
+} else {
+ LIBLOC = $$[QT_HOST_LIBS]
+}
+
+STATICLIBS = qtspirv-cross qtglslang-glslang qtglslang-spirv qtglslang-osdependent qtglslang-oglcompiler # qtglslang-hlsl
+for(libname, STATICLIBS) {
+ staticlib = $$LIBLOC/$${QMAKE_PREFIX_STATICLIB}$$qtLibraryTarget($$libname).$${QMAKE_EXTENSION_STATICLIB}
+ LIBS_PRIVATE += $$staticlib
+ PRE_TARGETDEPS += $$staticlib
+}
+
+include($$PWD/doc/doc.pri)
+
+load(qt_module)