aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--.gitignore4
-rw-r--r--config.tests/d3d12/d3d12.cpp51
-rw-r--r--config.tests/d3d12/d3d12.pro4
-rw-r--r--features/hlsl_bytecode_header.prf10
-rw-r--r--qtdeclarative.pro3
-rw-r--r--src/quick/scenegraph/adaptations/adaptations.pri2
-rw-r--r--src/quick/scenegraph/adaptations/d3d12/d3d12.pri28
-rw-r--r--src/quick/scenegraph/adaptations/d3d12/qsgd3d12adaptation.cpp71
-rw-r--r--src/quick/scenegraph/adaptations/d3d12/qsgd3d12adaptation_p.h77
-rw-r--r--src/quick/scenegraph/adaptations/d3d12/qsgd3d12context.cpp95
-rw-r--r--src/quick/scenegraph/adaptations/d3d12/qsgd3d12context_p.h75
-rw-r--r--src/quick/scenegraph/adaptations/d3d12/qsgd3d12engine.cpp996
-rw-r--r--src/quick/scenegraph/adaptations/d3d12/qsgd3d12engine_p.h226
-rw-r--r--src/quick/scenegraph/adaptations/d3d12/qsgd3d12engine_p_p.h225
-rw-r--r--src/quick/scenegraph/adaptations/d3d12/qsgd3d12material.cpp163
-rw-r--r--src/quick/scenegraph/adaptations/d3d12/qsgd3d12material_p.h133
-rw-r--r--src/quick/scenegraph/adaptations/d3d12/qsgd3d12rectanglenode.cpp72
-rw-r--r--src/quick/scenegraph/adaptations/d3d12/qsgd3d12rectanglenode_p.h74
-rw-r--r--src/quick/scenegraph/adaptations/d3d12/qsgd3d12rendercontext.cpp79
-rw-r--r--src/quick/scenegraph/adaptations/d3d12/qsgd3d12rendercontext_p.h80
-rw-r--r--src/quick/scenegraph/adaptations/d3d12/qsgd3d12renderer.cpp477
-rw-r--r--src/quick/scenegraph/adaptations/d3d12/qsgd3d12renderer_p.h101
-rw-r--r--src/quick/scenegraph/adaptations/d3d12/qsgd3d12renderloop.cpp277
-rw-r--r--src/quick/scenegraph/adaptations/d3d12/qsgd3d12renderloop_p.h109
-rw-r--r--src/quick/scenegraph/adaptations/d3d12/shaders/shaders.pri12
-rw-r--r--src/quick/scenegraph/adaptations/d3d12/shaders/vertexcolor.hlsl32
-rw-r--r--src/quick/scenegraph/coreapi/qsggeometry.cpp17
-rw-r--r--src/quick/scenegraph/coreapi/qsggeometry.h71
-rw-r--r--src/quick/scenegraph/coreapi/qsgnode.h3
-rw-r--r--src/quick/scenegraph/qsgcontextplugin.cpp6
-rw-r--r--src/quick/scenegraph/qsgdefaultrectanglenode.cpp74
-rw-r--r--src/quick/scenegraph/qsgdefaultrectanglenode_p.h47
-rw-r--r--tests/manual/nodetypes/main.qml87
-rw-r--r--tests/manual/nodetypes/nodetypes.cpp57
-rw-r--r--tests/manual/nodetypes/nodetypes.pro7
-rw-r--r--tests/manual/nodetypes/nodetypes.qrc5
36 files changed, 3778 insertions, 72 deletions
diff --git a/.gitignore b/.gitignore
index e949cddb22..67c40bff90 100644
--- a/.gitignore
+++ b/.gitignore
@@ -282,3 +282,7 @@ src/qml/RegExpJitTables.h
src/qml/udis86_itab.c
src/qml/udis86_itab.h
+# Generated HLSL bytecode headers
+hlsl_vs_*.h
+hlsl_ps_*.h
+hlsl_cs_*.h
diff --git a/config.tests/d3d12/d3d12.cpp b/config.tests/d3d12/d3d12.cpp
new file mode 100644
index 0000000000..0bf90cc457
--- /dev/null
+++ b/config.tests/d3d12/d3d12.cpp
@@ -0,0 +1,51 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtQuick module of the Qt Toolkit.
+**
+** $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 <d3d12.h>
+#include <dxgi1_4.h>
+#include <wrl/client.h>
+
+using namespace Microsoft::WRL;
+
+int main(int, char **)
+{
+ ID3D12Device *dev = 0;
+
+ return 0;
+}
diff --git a/config.tests/d3d12/d3d12.pro b/config.tests/d3d12/d3d12.pro
new file mode 100644
index 0000000000..24c5991a4b
--- /dev/null
+++ b/config.tests/d3d12/d3d12.pro
@@ -0,0 +1,4 @@
+SOURCES = d3d12.cpp
+CONFIG -= qt dylib
+CONFIG += console
+LIBS += -ldxgi -ld3d12
diff --git a/features/hlsl_bytecode_header.prf b/features/hlsl_bytecode_header.prf
new file mode 100644
index 0000000000..0fa27a75d5
--- /dev/null
+++ b/features/hlsl_bytecode_header.prf
@@ -0,0 +1,10 @@
+for (SHADER, HLSL_SHADERS) {
+ INPUT = $$eval($${SHADER}.input)
+ fxc_$${SHADER}.input = $$INPUT
+ fxc_$${SHADER}.commands = fxc.exe /nologo /E $$eval($${SHADER}.entry) /T $$eval($${SHADER}.type) /Fh ${QMAKE_FILE_OUT} ${QMAKE_FILE_NAME}
+ fxc_$${SHADER}.output = $$eval($${SHADER}.header)
+ fxc_$${SHADER}.dependency_type = TYPE_C
+ fxc_$${SHADER}.variable_out = HEADERS
+ fxc_$${SHADER}.CONFIG += target_predeps
+ QMAKE_EXTRA_COMPILERS += fxc_$${SHADER}
+}
diff --git a/qtdeclarative.pro b/qtdeclarative.pro
index 0e746c3c65..b4991db4ee 100644
--- a/qtdeclarative.pro
+++ b/qtdeclarative.pro
@@ -1,3 +1,6 @@
+load(configure)
+qtCompileTest(d3d12)
+
CONFIG += tests_need_tools examples_need_tools
load(qt_parts)
diff --git a/src/quick/scenegraph/adaptations/adaptations.pri b/src/quick/scenegraph/adaptations/adaptations.pri
index 3511971cac..850d2a3bbe 100644
--- a/src/quick/scenegraph/adaptations/adaptations.pri
+++ b/src/quick/scenegraph/adaptations/adaptations.pri
@@ -1 +1,3 @@
include(dummy/dummy.pri)
+
+config_d3d12: include(d3d12/d3d12.pri)
diff --git a/src/quick/scenegraph/adaptations/d3d12/d3d12.pri b/src/quick/scenegraph/adaptations/d3d12/d3d12.pri
new file mode 100644
index 0000000000..8123b9d19b
--- /dev/null
+++ b/src/quick/scenegraph/adaptations/d3d12/d3d12.pri
@@ -0,0 +1,28 @@
+SOURCES += \
+ $$PWD/qsgd3d12adaptation.cpp \
+ $$PWD/qsgd3d12renderloop.cpp \
+ $$PWD/qsgd3d12renderer.cpp \
+ $$PWD/qsgd3d12context.cpp \
+ $$PWD/qsgd3d12rendercontext.cpp \
+ $$PWD/qsgd3d12rectanglenode.cpp \
+ $$PWD/qsgd3d12material.cpp
+
+NO_PCH_SOURCES += \
+ $$PWD/qsgd3d12engine.cpp
+
+HEADERS += \
+ $$PWD/qsgd3d12adaptation_p.h \
+ $$PWD/qsgd3d12renderloop_p.h \
+ $$PWD/qsgd3d12renderer_p.h \
+ $$PWD/qsgd3d12context_p.h \
+ $$PWD/qsgd3d12rendercontext_p.h \
+ $$PWD/qsgd3d12engine_p.h \
+ $$PWD/qsgd3d12engine_p_p.h \
+ $$PWD/qsgd3d12rectanglenode_p.h \
+ $$PWD/qsgd3d12material_p.h
+
+LIBS += -ldxgi -ld3d12
+
+DEFINES += QSG_D3D12
+
+include($$PWD/shaders/shaders.pri)
diff --git a/src/quick/scenegraph/adaptations/d3d12/qsgd3d12adaptation.cpp b/src/quick/scenegraph/adaptations/d3d12/qsgd3d12adaptation.cpp
new file mode 100644
index 0000000000..826a1cc5cb
--- /dev/null
+++ b/src/quick/scenegraph/adaptations/d3d12/qsgd3d12adaptation.cpp
@@ -0,0 +1,71 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtQuick module of the Qt Toolkit.
+**
+** $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 "qsgd3d12adaptation_p.h"
+#include "qsgd3d12renderloop_p.h"
+#include "qsgd3d12context_p.h"
+
+QT_BEGIN_NAMESPACE
+
+QSGD3D12Adaptation::QSGD3D12Adaptation(QObject *parent)
+ : QSGContextPlugin(parent)
+{
+}
+
+QStringList QSGD3D12Adaptation::keys() const
+{
+ return QStringList() << QLatin1String("d3d12");
+}
+
+QSGContext *QSGD3D12Adaptation::create(const QString &) const
+{
+ if (!contextInstance)
+ contextInstance = new QSGD3D12Context;
+
+ return contextInstance;
+}
+
+QSGRenderLoop *QSGD3D12Adaptation::createWindowManager()
+{
+ return new QSGD3D12RenderLoop;
+}
+
+QSGD3D12Context *QSGD3D12Adaptation::contextInstance = nullptr;
+
+QT_END_NAMESPACE
diff --git a/src/quick/scenegraph/adaptations/d3d12/qsgd3d12adaptation_p.h b/src/quick/scenegraph/adaptations/d3d12/qsgd3d12adaptation_p.h
new file mode 100644
index 0000000000..f3f5d5706e
--- /dev/null
+++ b/src/quick/scenegraph/adaptations/d3d12/qsgd3d12adaptation_p.h
@@ -0,0 +1,77 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtQuick module of the Qt Toolkit.
+**
+** $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 QSGD3D12ADAPTATION_P_H
+#define QSGD3D12ADAPTATION_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 <private/qsgcontextplugin_p.h>
+
+QT_BEGIN_NAMESPACE
+
+class QSGD3D12Context;
+class QSGContext;
+class QSGRenderLoop;
+
+class QSGD3D12Adaptation : public QSGContextPlugin
+{
+public:
+ QSGD3D12Adaptation(QObject *parent = 0);
+
+ QStringList keys() const override;
+ QSGContext *create(const QString &key) const override;
+ QSGRenderLoop *createWindowManager() override;
+
+private:
+ static QSGD3D12Context *contextInstance;
+};
+
+QT_END_NAMESPACE
+
+#endif // QSGD3D12ADAPTATION_P_H
diff --git a/src/quick/scenegraph/adaptations/d3d12/qsgd3d12context.cpp b/src/quick/scenegraph/adaptations/d3d12/qsgd3d12context.cpp
new file mode 100644
index 0000000000..b75d6ebb89
--- /dev/null
+++ b/src/quick/scenegraph/adaptations/d3d12/qsgd3d12context.cpp
@@ -0,0 +1,95 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtQuick module of the Qt Toolkit.
+**
+** $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 "qsgd3d12context_p.h"
+#include "qsgd3d12rendercontext_p.h"
+#include "qsgd3d12rectanglenode_p.h"
+
+QT_BEGIN_NAMESPACE
+
+QSGRenderContext *QSGD3D12Context::createRenderContext()
+{
+ return new QSGD3D12RenderContext(this);
+}
+
+QSGRectangleNode *QSGD3D12Context::createRectangleNode()
+{
+ return new QSGD3D12RectangleNode;
+}
+
+QSGImageNode *QSGD3D12Context::createImageNode()
+{
+ Q_UNREACHABLE();
+ return nullptr;
+}
+
+QSGPainterNode *QSGD3D12Context::createPainterNode(QQuickPaintedItem *item)
+{
+ Q_UNUSED(item);
+ Q_UNREACHABLE();
+ return nullptr;
+}
+
+QSGGlyphNode *QSGD3D12Context::createGlyphNode(QSGRenderContext *rc, bool preferNativeGlyphNode)
+{
+ Q_UNUSED(rc);
+ Q_UNUSED(preferNativeGlyphNode);
+ Q_UNREACHABLE();
+ return nullptr;
+}
+
+QSGNinePatchNode *QSGD3D12Context::createNinePatchNode()
+{
+ Q_UNREACHABLE();
+ return nullptr;
+}
+
+QSGLayer *QSGD3D12Context::createLayer(QSGRenderContext *rc)
+{
+ Q_UNUSED(rc);
+ Q_UNREACHABLE();
+ return nullptr;
+}
+
+QSurfaceFormat QSGD3D12Context::defaultSurfaceFormat() const
+{
+ return QSGContext::defaultSurfaceFormat();
+}
+
+QT_END_NAMESPACE
diff --git a/src/quick/scenegraph/adaptations/d3d12/qsgd3d12context_p.h b/src/quick/scenegraph/adaptations/d3d12/qsgd3d12context_p.h
new file mode 100644
index 0000000000..6a092e8606
--- /dev/null
+++ b/src/quick/scenegraph/adaptations/d3d12/qsgd3d12context_p.h
@@ -0,0 +1,75 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtQuick module of the Qt Toolkit.
+**
+** $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 QSGD3D12CONTEXT_P_H
+#define QSGD3D12CONTEXT_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 <private/qsgcontext_p.h>
+
+QT_BEGIN_NAMESPACE
+
+class QSGD3D12Context : public QSGContext
+{
+public:
+ QSGD3D12Context(QObject *parent = 0) : QSGContext(parent) { }
+
+ QSGRenderContext *createRenderContext() override;
+ QSGRectangleNode *createRectangleNode() override;
+ QSGImageNode *createImageNode() override;
+ QSGPainterNode *createPainterNode(QQuickPaintedItem *item) override;
+ QSGGlyphNode *createGlyphNode(QSGRenderContext *rc, bool preferNativeGlyphNode) override;
+ QSGNinePatchNode *createNinePatchNode() override;
+ QSGLayer *createLayer(QSGRenderContext *rc) override;
+ QSurfaceFormat defaultSurfaceFormat() const override;
+};
+
+QT_END_NAMESPACE
+
+#endif // QSGD3D12CONTEXT_P_H
diff --git a/src/quick/scenegraph/adaptations/d3d12/qsgd3d12engine.cpp b/src/quick/scenegraph/adaptations/d3d12/qsgd3d12engine.cpp
new file mode 100644
index 0000000000..a623956e72
--- /dev/null
+++ b/src/quick/scenegraph/adaptations/d3d12/qsgd3d12engine.cpp
@@ -0,0 +1,996 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtQuick module of the Qt Toolkit.
+**
+** $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 "qsgd3d12engine_p.h"
+#include "qsgd3d12engine_p_p.h"
+#include <QString>
+#include <QColor>
+
+QT_BEGIN_NAMESPACE
+
+// Recommended reading before moving further: https://github.com/Microsoft/DirectXTK/wiki/ComPtr
+// Note esp. operator= vs. Attach and operator& vs. GetAddressOf
+
+static const int MAX_DESCRIPTORS_PER_HEAP = 256;
+
+QSGD3D12DescriptorHandle QSGD3D12DescriptorHeapManager::allocate(D3D12_DESCRIPTOR_HEAP_TYPE type, Flags flags)
+{
+ QSGD3D12DescriptorHandle h;
+ for (Heap &heap : m_heaps) {
+ if (heap.type == type && heap.count < MAX_DESCRIPTORS_PER_HEAP) {
+ h = heap.start;
+ h.cpu.ptr += heap.count * heap.handleSize;
+ if (h.gpu.ptr)
+ h.gpu.ptr += heap.count * heap.handleSize;
+ ++heap.count;
+ return h;
+ }
+ }
+
+ Heap heap;
+ heap.type = type;
+ heap.handleSize = m_handleSizes[type];
+
+ D3D12_DESCRIPTOR_HEAP_DESC heapDesc = {};
+ heapDesc.NumDescriptors = MAX_DESCRIPTORS_PER_HEAP;
+ heapDesc.Type = type;
+ if (flags & ShaderVisible)
+ heapDesc.Flags |= D3D12_DESCRIPTOR_HEAP_FLAG_SHADER_VISIBLE;
+
+ HRESULT hr = m_device->CreateDescriptorHeap(&heapDesc, IID_PPV_ARGS(&heap.heap));
+ if (FAILED(hr)) {
+ qWarning("Failed to create heap with type 0x%x: %x", type, hr);
+ return h;
+ }
+
+ heap.start.cpu = heap.heap->GetCPUDescriptorHandleForHeapStart();
+ qDebug("type %x start is %llu", type, heap.start.cpu.ptr);
+ if (flags & ShaderVisible)
+ heap.start.gpu = heap.heap->GetGPUDescriptorHandleForHeapStart();
+
+ heap.count = 1;
+ h = heap.start;
+ m_heaps.append(heap);
+
+ return h;
+}
+
+void QSGD3D12DescriptorHeapManager::initialize(ID3D12Device *device)
+{
+ m_device = device;
+
+ for (int i = 0; i < D3D12_DESCRIPTOR_HEAP_TYPE_NUM_TYPES; ++i)
+ m_handleSizes[i] = m_device->GetDescriptorHandleIncrementSize(D3D12_DESCRIPTOR_HEAP_TYPE(i));
+}
+
+void QSGD3D12DescriptorHeapManager::releaseResources()
+{
+ for (Heap &heap : m_heaps)
+ heap.heap = nullptr;
+
+ m_heaps.clear();
+
+ m_device = nullptr;
+}
+
+// One device per process, one everything else (engine) per window.
+Q_GLOBAL_STATIC(QSGD3D12DeviceManager, deviceManager)
+
+static void getHardwareAdapter(IDXGIFactory1 *factory, IDXGIAdapter1 **outAdapter)
+{
+ const D3D_FEATURE_LEVEL fl = D3D_FEATURE_LEVEL_11_0;
+ ComPtr<IDXGIAdapter1> adapter;
+ DXGI_ADAPTER_DESC1 desc;
+
+ for (int adapterIndex = 0; factory->EnumAdapters1(adapterIndex, &adapter) != DXGI_ERROR_NOT_FOUND; ++adapterIndex) {
+ DXGI_ADAPTER_DESC1 desc;
+ adapter->GetDesc1(&desc);
+ const QString name = QString::fromUtf16((char16_t *) desc.Description);
+ qDebug("Adapter %d: '%s' (flags 0x%x)", adapterIndex, qPrintable(name), desc.Flags);
+ }
+
+ if (qEnvironmentVariableIsSet("QT_D3D_ADAPTER_INDEX")) {
+ const int adapterIndex = qEnvironmentVariableIntValue("QT_D3D_ADAPTER_INDEX");
+ if (SUCCEEDED(factory->EnumAdapters1(adapterIndex, &adapter))
+ && SUCCEEDED(D3D12CreateDevice(adapter.Get(), fl, _uuidof(ID3D12Device), nullptr))) {
+ adapter->GetDesc1(&desc);
+ const QString name = QString::fromUtf16((char16_t *) desc.Description);
+ qDebug("Using requested adapter '%s'", qPrintable(name));
+ *outAdapter = adapter.Detach();
+ return;
+ }
+ }
+
+ for (int adapterIndex = 0; factory->EnumAdapters1(adapterIndex, &adapter) != DXGI_ERROR_NOT_FOUND; ++adapterIndex) {
+ adapter->GetDesc1(&desc);
+ if (desc.Flags & DXGI_ADAPTER_FLAG_SOFTWARE)
+ continue;
+
+ if (SUCCEEDED(D3D12CreateDevice(adapter.Get(), fl, _uuidof(ID3D12Device), nullptr))) {
+ const QString name = QString::fromUtf16((char16_t *) desc.Description);
+ qDebug("Using adapter '%s'", qPrintable(name));
+ break;
+ }
+ }
+
+ *outAdapter = adapter.Detach();
+}
+
+ID3D12Device *QSGD3D12DeviceManager::ref()
+{
+ ensureCreated();
+ m_ref.ref();
+ return m_device.Get();
+}
+
+void QSGD3D12DeviceManager::unref()
+{
+ if (!m_ref.deref()) {
+ qDebug("destroying d3d device");
+ m_device = nullptr;
+ m_factory = nullptr;
+ }
+}
+
+void QSGD3D12DeviceManager::deviceLossDetected()
+{
+ for (DeviceLossObserver *observer : qAsConst(m_observers))
+ observer->deviceLost();
+
+ // Nothing else to do here. All windows are expected to release their
+ // resources and call unref() in response immediately.
+}
+
+IDXGIFactory4 *QSGD3D12DeviceManager::dxgi()
+{
+ ensureCreated();
+ return m_factory.Get();
+}
+
+void QSGD3D12DeviceManager::ensureCreated()
+{
+ if (m_device)
+ return;
+
+ HRESULT hr = CreateDXGIFactory2(0, IID_PPV_ARGS(&m_factory));
+ if (FAILED(hr)) {
+ qWarning("Failed to create DXGI: 0x%x", hr);
+ return;
+ }
+
+ ComPtr<IDXGIAdapter1> adapter;
+ getHardwareAdapter(m_factory.Get(), &adapter);
+
+ bool warp = true;
+ if (adapter) {
+ HRESULT hr = D3D12CreateDevice(adapter.Get(), D3D_FEATURE_LEVEL_11_0, IID_PPV_ARGS(&m_device));
+ if (SUCCEEDED(hr))
+ warp = false;
+ else
+ qWarning("Failed to create device: 0x%x", hr);
+ }
+
+ if (warp) {
+ qDebug("Using WARP");
+ ComPtr<IDXGIAdapter> warpAdapter;
+ m_factory->EnumWarpAdapter(IID_PPV_ARGS(&warpAdapter));
+ HRESULT hr = D3D12CreateDevice(warpAdapter.Get(), D3D_FEATURE_LEVEL_11_0, IID_PPV_ARGS(&m_device));
+ if (FAILED(hr)) {
+ qWarning("Failed to create WARP device: 0x%x", hr);
+ return;
+ }
+ }
+}
+
+void QSGD3D12DeviceManager::registerDeviceLossObserver(DeviceLossObserver *observer)
+{
+ if (!m_observers.contains(observer))
+ m_observers.append(observer);
+}
+
+QSGD3D12Engine::QSGD3D12Engine()
+{
+ d = new QSGD3D12EnginePrivate;
+}
+
+QSGD3D12Engine::~QSGD3D12Engine()
+{
+ d->releaseResources();
+ delete d;
+}
+
+bool QSGD3D12Engine::attachToWindow(QWindow *window)
+{
+ if (d->isInitialized()) {
+ qWarning("QSGD3D12Engine: Cannot attach active engine to window");
+ return false;
+ }
+
+ d->initialize(window);
+ return d->isInitialized();
+}
+
+void QSGD3D12Engine::releaseResources()
+{
+ d->releaseResources();
+}
+
+void QSGD3D12Engine::resize()
+{
+ d->resize();
+}
+
+void QSGD3D12Engine::beginFrame()
+{
+ d->beginFrame();
+}
+
+void QSGD3D12Engine::endFrame()
+{
+ d->endFrame();
+}
+
+void QSGD3D12Engine::setPipelineState(const QSGD3D12PipelineState &pipelineState)
+{
+ d->setPipelineState(pipelineState);
+}
+
+void QSGD3D12Engine::setVertexBuffer(const quint8 *data, int size)
+{
+ d->setVertexBuffer(data, size);
+}
+
+void QSGD3D12Engine::setIndexBuffer(const quint8 *data, int size)
+{
+ d->setIndexBuffer(data, size);
+}
+
+void QSGD3D12Engine::setConstantBuffer(const quint8 *data, int size)
+{
+ d->setConstantBuffer(data, size);
+}
+
+void QSGD3D12Engine::queueViewport(const QRect &rect)
+{
+ d->queueViewport(rect);
+}
+
+void QSGD3D12Engine::queueScissor(const QRect &rect)
+{
+ d->queueScissor(rect);
+}
+
+void QSGD3D12Engine::queueSetRenderTarget()
+{
+ d->queueSetRenderTarget();
+}
+
+void QSGD3D12Engine::queueClearRenderTarget(const QColor &color)
+{
+ d->queueClearRenderTarget(color);
+}
+
+void QSGD3D12Engine::queueClearDepthStencil(float depthValue, quint8 stencilValue)
+{
+ d->queueClearDepthStencil(depthValue, stencilValue);
+}
+
+void QSGD3D12Engine::queueDraw(QSGGeometry::DrawingMode mode, int count, int vboOffset, int vboStride,
+ int cboOffset,
+ int startIndexIndex, QSGD3D12Format indexFormat)
+{
+ d->queueDraw(mode, count, vboOffset, vboStride, cboOffset, startIndexIndex, indexFormat);
+}
+
+void QSGD3D12Engine::present()
+{
+ d->present();
+}
+
+void QSGD3D12Engine::waitGPU()
+{
+ d->waitGPU();
+}
+
+quint32 QSGD3D12Engine::alignedConstantBufferSize(quint32 size)
+{
+ return (size + D3D12_CONSTANT_BUFFER_DATA_PLACEMENT_ALIGNMENT - 1) & ~(D3D12_CONSTANT_BUFFER_DATA_PLACEMENT_ALIGNMENT - 1);
+}
+
+void QSGD3D12EnginePrivate::releaseResources()
+{
+ if (!initialized)
+ return;
+
+ bundleAllocator = nullptr;
+ commandAllocator = nullptr;
+
+ depthStencil = nullptr;
+ for (int i = 0; i < swapChainBufferCount; ++i)
+ renderTargets[i] = nullptr;
+
+ vertexBuffer = nullptr;
+ indexBuffer = nullptr;
+ constantBuffer = nullptr;
+
+ for (ComPtr<ID3D12PipelineState> &ps : m_psoCache)
+ ps = nullptr;
+ m_psoCache.clear();
+ m_rootSig = nullptr;
+
+ descHeapManager.releaseResources();
+
+ commandQueue = nullptr;
+ swapChain = nullptr;
+
+ delete presentFence;
+
+ deviceManager()->unref();
+
+ initialized = false;
+
+ // 'window' must be kept, may just be a device loss
+}
+
+void QSGD3D12EnginePrivate::initialize(QWindow *w)
+{
+ if (initialized)
+ return;
+
+ window = w;
+
+ HWND hwnd = reinterpret_cast<HWND>(window->winId());
+
+ if (qEnvironmentVariableIntValue("QT_D3D_DEBUG") != 0) {
+ qDebug("Enabling debug layer");
+ ComPtr<ID3D12Debug> debugController;
+ if (SUCCEEDED(D3D12GetDebugInterface(IID_PPV_ARGS(&debugController))))
+ debugController->EnableDebugLayer();
+ }
+
+ QSGD3D12DeviceManager *dev = deviceManager();
+ device = dev->ref();
+ dev->registerDeviceLossObserver(this);
+
+ D3D12_COMMAND_QUEUE_DESC queueDesc = {};
+ queueDesc.Type = D3D12_COMMAND_LIST_TYPE_DIRECT;
+
+ if (FAILED(device->CreateCommandQueue(&queueDesc, IID_PPV_ARGS(&commandQueue)))) {
+ qWarning("Failed to create command queue");
+ return;
+ }
+
+ DXGI_SWAP_CHAIN_DESC swapChainDesc = {};
+ swapChainDesc.BufferCount = swapChainBufferCount;
+ swapChainDesc.BufferDesc.Width = window->width() * window->devicePixelRatio();
+ swapChainDesc.BufferDesc.Height = window->height() * window->devicePixelRatio();
+ swapChainDesc.BufferDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
+ swapChainDesc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;
+ swapChainDesc.SwapEffect = DXGI_SWAP_EFFECT_FLIP_DISCARD; // D3D12 requires the flip model
+ swapChainDesc.OutputWindow = hwnd;
+ swapChainDesc.SampleDesc.Count = 1; // Flip does not support MSAA so no choice here
+ swapChainDesc.Windowed = TRUE;
+
+ ComPtr<IDXGISwapChain> baseSwapChain;
+ HRESULT hr = dev->dxgi()->CreateSwapChain(commandQueue.Get(), &swapChainDesc, &baseSwapChain);
+ if (FAILED(hr)) {
+ qWarning("Failed to create swap chain: 0x%x", hr);
+ return;
+ }
+ if (FAILED(baseSwapChain.As(&swapChain))) {
+ qWarning("Failed to cast swap chain");
+ return;
+ }
+
+ dev->dxgi()->MakeWindowAssociation(hwnd, DXGI_MWA_NO_ALT_ENTER);
+
+ if (FAILED(device->CreateCommandAllocator(D3D12_COMMAND_LIST_TYPE_DIRECT, IID_PPV_ARGS(&commandAllocator)))) {
+ qWarning("Failed to create command allocator");
+ return;
+ }
+
+ if (FAILED(device->CreateCommandAllocator(D3D12_COMMAND_LIST_TYPE_BUNDLE, IID_PPV_ARGS(&bundleAllocator)))) {
+ qWarning("Failed to create command bundle allocator");
+ return;
+ }
+
+ descHeapManager.initialize(device);
+
+ setupRenderTargets();
+
+ if (FAILED(device->CreateCommandList(0, D3D12_COMMAND_LIST_TYPE_DIRECT, commandAllocator.Get(),
+ nullptr, IID_PPV_ARGS(&commandList)))) {
+ qWarning("Failed to create command list");
+ return;
+ }
+ // created in recording state, close it for now
+ commandList->Close();
+
+ presentFence = createFence();
+
+ vertexData = StagingBufferRef();
+ indexData = StagingBufferRef();
+ constantData = StagingBufferRef();
+
+ initialized = true;
+}
+
+DXGI_SAMPLE_DESC QSGD3D12EnginePrivate::makeSampleDesc(DXGI_FORMAT format, int samples)
+{
+ DXGI_SAMPLE_DESC sampleDesc;
+ sampleDesc.Count = 1;
+ sampleDesc.Quality = 0;
+
+ if (samples > 1) {
+ D3D12_FEATURE_DATA_MULTISAMPLE_QUALITY_LEVELS msaaInfo = {};
+ msaaInfo.Format = format;
+ msaaInfo.SampleCount = samples;
+ if (SUCCEEDED(device->CheckFeatureSupport(D3D12_FEATURE_MULTISAMPLE_QUALITY_LEVELS, &msaaInfo, sizeof(msaaInfo)))) {
+ if (msaaInfo.NumQualityLevels > 0) {
+ sampleDesc.Count = samples;
+ sampleDesc.Quality = msaaInfo.NumQualityLevels - 1;
+ } else {
+ qWarning("No quality levels for multisampling?");
+ }
+ } else {
+ qWarning("Failed to query multisample quality levels");
+ }
+ }
+
+ return sampleDesc;
+}
+
+ID3D12Resource *QSGD3D12EnginePrivate::createDepthStencil(D3D12_CPU_DESCRIPTOR_HANDLE viewHandle, const QSize &size, int samples)
+{
+ D3D12_CLEAR_VALUE depthClearValue = {};
+ depthClearValue.Format = DXGI_FORMAT_D32_FLOAT;
+ depthClearValue.DepthStencil.Depth = 1.0f;
+ depthClearValue.DepthStencil.Stencil = 0;
+
+ D3D12_HEAP_PROPERTIES heapProp = {};
+ heapProp.Type = D3D12_HEAP_TYPE_DEFAULT;
+
+ D3D12_RESOURCE_DESC bufDesc = {};
+ bufDesc.Dimension = D3D12_RESOURCE_DIMENSION_TEXTURE2D;
+ bufDesc.Width = size.width();
+ bufDesc.Height = size.height();
+ bufDesc.DepthOrArraySize = 1;
+ bufDesc.MipLevels = 1;
+ bufDesc.Format = DXGI_FORMAT_D32_FLOAT;
+ bufDesc.SampleDesc = makeSampleDesc(bufDesc.Format, samples);
+ bufDesc.Layout = D3D12_TEXTURE_LAYOUT_UNKNOWN;
+ bufDesc.Flags = D3D12_RESOURCE_FLAG_ALLOW_DEPTH_STENCIL;
+
+ ID3D12Resource *resource = nullptr;
+ if (FAILED(device->CreateCommittedResource(&heapProp, D3D12_HEAP_FLAG_NONE, &bufDesc,
+ D3D12_RESOURCE_STATE_DEPTH_WRITE, &depthClearValue, IID_PPV_ARGS(&resource)))) {
+ qWarning("Failed to create depth-stencil buffer of size %dx%d", size.width(), size.height());
+ return nullptr;
+ }
+
+ D3D12_DEPTH_STENCIL_VIEW_DESC depthStencilDesc = {};
+ depthStencilDesc.Format = DXGI_FORMAT_D32_FLOAT;
+ depthStencilDesc.ViewDimension = bufDesc.SampleDesc.Count <= 1 ? D3D12_DSV_DIMENSION_TEXTURE2D : D3D12_DSV_DIMENSION_TEXTURE2DMS;
+
+ device->CreateDepthStencilView(resource, &depthStencilDesc, viewHandle);
+
+ return resource;
+}
+
+void QSGD3D12EnginePrivate::setupRenderTargets()
+{
+ for (int i = 0; i < swapChainBufferCount; ++i) {
+ if (FAILED(swapChain->GetBuffer(i, IID_PPV_ARGS(&renderTargets[i])))) {
+ qWarning("Failed to get buffer %d from swap chain", i);
+ return;
+ }
+ D3D12_CPU_DESCRIPTOR_HANDLE h = descHeapManager.allocate(D3D12_DESCRIPTOR_HEAP_TYPE_RTV).cpu;
+ if (i == 0)
+ rtv0 = h;
+ device->CreateRenderTargetView(renderTargets[i].Get(), nullptr, h);
+ }
+
+ dsv = descHeapManager.allocate(D3D12_DESCRIPTOR_HEAP_TYPE_DSV).cpu;
+ ID3D12Resource *ds = createDepthStencil(dsv, window->size(), 0);
+ if (ds)
+ depthStencil.Attach(ds);
+}
+
+void QSGD3D12EnginePrivate::resize()
+{
+ if (!initialized)
+ return;
+
+ qDebug() << window->size();
+
+ // Clear these, otherwise resizing will fail.
+ depthStencil = nullptr;
+ for (int i = 0; i < swapChainBufferCount; ++i)
+ renderTargets[i] = nullptr;
+
+ HRESULT hr = swapChain->ResizeBuffers(swapChainBufferCount, window->width(), window->height(), DXGI_FORMAT_R8G8B8A8_UNORM, 0);
+ if (hr == DXGI_ERROR_DEVICE_REMOVED || hr == DXGI_ERROR_DEVICE_RESET) {
+ deviceManager()->deviceLossDetected();
+ return;
+ } else if (FAILED(hr)) {
+ qWarning("Failed to resize buffers: 0x%x", hr);
+ return;
+ }
+
+ setupRenderTargets();
+}
+
+void QSGD3D12EnginePrivate::deviceLost()
+{
+ qWarning("D3D device lost, will attempt to reinitialize");
+
+ // Release all resources. This is important because otherwise reinitialization may fail.
+ releaseResources();
+
+ // Now in uninitialized state (but 'window' is still valid). Will recreate
+ // all the resources on the next beginFrame().
+}
+
+QSGD3D12Fence *QSGD3D12EnginePrivate::createFence() const
+{
+ QSGD3D12Fence *f = new QSGD3D12Fence;
+ HRESULT hr = device->CreateFence(f->value, D3D12_FENCE_FLAG_NONE, IID_PPV_ARGS(&f->fence));
+ if (FAILED(hr)) {
+ qWarning("Failed to create fence: 0x%x", hr);
+ return f;
+ }
+ f->event = CreateEvent(nullptr, FALSE, FALSE, nullptr);
+ return f;
+}
+
+void QSGD3D12EnginePrivate::waitForGPU(QSGD3D12Fence *f) const
+{
+ const UINT64 newValue = f->value.fetchAndAddAcquire(1) + 1;
+ commandQueue->Signal(f->fence.Get(), newValue);
+ if (f->fence->GetCompletedValue() < newValue) {
+ HRESULT hr = f->fence->SetEventOnCompletion(newValue, f->event);
+ if (FAILED(hr)) {
+ qWarning("SetEventOnCompletion failed: 0x%x", hr);
+ return;
+ }
+ WaitForSingleObject(f->event, INFINITE);
+ }
+}
+
+void QSGD3D12EnginePrivate::transitionResource(ID3D12Resource *resource, ID3D12GraphicsCommandList *commandList,
+ D3D12_RESOURCE_STATES before, D3D12_RESOURCE_STATES after) const
+{
+ D3D12_RESOURCE_BARRIER barrier;
+ barrier.Type = D3D12_RESOURCE_BARRIER_TYPE_TRANSITION;
+ barrier.Flags = D3D12_RESOURCE_BARRIER_FLAG_NONE;
+ barrier.Transition.pResource = resource;
+ barrier.Transition.StateBefore = before;
+ barrier.Transition.StateAfter = after;
+ barrier.Transition.Subresource = D3D12_RESOURCE_BARRIER_ALL_SUBRESOURCES;
+
+ commandList->ResourceBarrier(1, &barrier);
+}
+
+ID3D12Resource *QSGD3D12EnginePrivate::createBuffer(int size)
+{
+ ID3D12Resource *buf;
+
+ D3D12_HEAP_PROPERTIES uploadHeapProp = {};
+ uploadHeapProp.Type = D3D12_HEAP_TYPE_UPLOAD;
+
+ D3D12_RESOURCE_DESC bufDesc = {};
+ bufDesc.Dimension = D3D12_RESOURCE_DIMENSION_BUFFER;
+ bufDesc.Width = size;
+ bufDesc.Height = 1;
+ bufDesc.DepthOrArraySize = 1;
+ bufDesc.MipLevels = 1;
+ bufDesc.Format = DXGI_FORMAT_UNKNOWN;
+ bufDesc.SampleDesc.Count = 1;
+ bufDesc.Layout = D3D12_TEXTURE_LAYOUT_ROW_MAJOR;
+
+ HRESULT hr = device->CreateCommittedResource(&uploadHeapProp, D3D12_HEAP_FLAG_NONE, &bufDesc,
+ D3D12_RESOURCE_STATE_GENERIC_READ, Q_NULLPTR, IID_PPV_ARGS(&buf));
+ if (FAILED(hr))
+ qWarning("Failed to create buffer resource: 0x%x", hr);
+
+ return buf;
+}
+
+ID3D12Resource *QSGD3D12EnginePrivate::backBufferRT() const
+{
+ return renderTargets[swapChain->GetCurrentBackBufferIndex()].Get();
+}
+
+D3D12_CPU_DESCRIPTOR_HANDLE QSGD3D12EnginePrivate::backBufferRTV() const
+{
+ const int frameIndex = swapChain->GetCurrentBackBufferIndex();
+ D3D12_CPU_DESCRIPTOR_HANDLE rtvHandle = rtv0;
+ rtvHandle.ptr += frameIndex * descHeapManager.handleSize(D3D12_DESCRIPTOR_HEAP_TYPE_RTV);
+ return rtvHandle;
+}
+
+void QSGD3D12EnginePrivate::beginFrame()
+{
+ static int cnt = 0;
+ qDebug() << "***** begin frame" << cnt;
+ ++cnt;
+
+ // The device may have been lost. This is the point to attempt to start again from scratch.
+ if (!initialized && window)
+ initialize(window);
+
+ commandAllocator->Reset();
+ commandList->Reset(commandAllocator.Get(), nullptr);
+
+ transitionResource(backBufferRT(), commandList.Get(), D3D12_RESOURCE_STATE_PRESENT, D3D12_RESOURCE_STATE_RENDER_TARGET);
+}
+
+void QSGD3D12EnginePrivate::endFrame()
+{
+ qDebug() << "***** end frame";
+
+ transitionResource(backBufferRT(), commandList.Get(), D3D12_RESOURCE_STATE_RENDER_TARGET, D3D12_RESOURCE_STATE_PRESENT);
+
+ HRESULT hr = commandList->Close();
+ if (FAILED(hr)) {
+ qWarning("Failed to close command list: 0x%x", hr);
+ if (hr == E_INVALIDARG)
+ qWarning("Invalid arguments. Some of the commands in the list is invalid in some way.");
+ }
+
+ ID3D12CommandList *commandLists[] = { commandList.Get() };
+ commandQueue->ExecuteCommandLists(_countof(commandLists), commandLists);
+}
+
+void QSGD3D12EnginePrivate::setPipelineState(const QSGD3D12PipelineState &pipelineState)
+{
+ // One single root signature for now. This will obviously need some improvements later on...
+ if (!m_rootSig) {
+ D3D12_ROOT_PARAMETER rootParameter;
+ rootParameter.ParameterType = D3D12_ROOT_PARAMETER_TYPE_CBV;
+ rootParameter.ShaderVisibility = D3D12_SHADER_VISIBILITY_VERTEX;
+ rootParameter.Descriptor.ShaderRegister = 0; // b0
+ rootParameter.Descriptor.RegisterSpace = 0;
+
+ D3D12_ROOT_SIGNATURE_DESC desc;
+ desc.NumParameters = 1;
+ desc.pParameters = &rootParameter;
+ desc.NumStaticSamplers = 0;
+ desc.pStaticSamplers = nullptr;
+ desc.Flags = D3D12_ROOT_SIGNATURE_FLAG_ALLOW_INPUT_ASSEMBLER_INPUT_LAYOUT;
+
+ ComPtr<ID3DBlob> signature;
+ ComPtr<ID3DBlob> error;
+ if (FAILED(D3D12SerializeRootSignature(&desc, D3D_ROOT_SIGNATURE_VERSION_1, &signature, &error))) {
+ qWarning("Failed to serialize root signature");
+ return;
+ }
+ if (FAILED(device->CreateRootSignature(0, signature->GetBufferPointer(), signature->GetBufferSize(),
+ IID_PPV_ARGS(&m_rootSig)))) {
+ qWarning("Failed to create root signature");
+ return;
+ }
+ }
+
+ // Unlimited number of cached PSOs for now, may want to change this later.
+ ComPtr<ID3D12PipelineState> pso = m_psoCache[pipelineState];
+
+ if (!pso) {
+ qDebug("NEW PSO");
+ D3D12_GRAPHICS_PIPELINE_STATE_DESC psoDesc = {};
+
+ D3D12_INPUT_ELEMENT_DESC inputElements[8];
+ Q_ASSERT(pipelineState.inputElements.count() <= _countof(inputElements));
+ UINT ieIdx = 0;
+ for (const QSGD3D12InputElement &ie : pipelineState.inputElements) {
+ D3D12_INPUT_ELEMENT_DESC ieDesc = {};
+ ieDesc.SemanticName = ie.name;
+ ieDesc.Format = DXGI_FORMAT(ie.format);
+ ieDesc.InputSlot = ie.slot;
+ ieDesc.AlignedByteOffset = ie.offset;
+ ieDesc.InputSlotClass = D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA;
+ qDebug("input [%d]: %s %d 0x%x %d", ieIdx, ie.name, ie.offset, ie.format, ie.slot);
+ inputElements[ieIdx++] = ieDesc;
+ }
+
+ psoDesc.InputLayout = { inputElements, ieIdx };
+
+ psoDesc.pRootSignature = m_rootSig.Get();
+
+ D3D12_SHADER_BYTECODE vshader;
+ vshader.pShaderBytecode = pipelineState.shaders.vs;
+ vshader.BytecodeLength = pipelineState.shaders.vsSize;
+ D3D12_SHADER_BYTECODE pshader;
+ pshader.pShaderBytecode = pipelineState.shaders.ps;
+ pshader.BytecodeLength = pipelineState.shaders.psSize;
+
+ psoDesc.VS = vshader;
+ psoDesc.PS = pshader;
+
+ D3D12_RASTERIZER_DESC rastDesc = {};
+ rastDesc.FillMode = D3D12_FILL_MODE_SOLID;
+ rastDesc.CullMode = D3D12_CULL_MODE(pipelineState.cullMode);
+ rastDesc.FrontCounterClockwise = pipelineState.frontCCW;
+ rastDesc.DepthBias = D3D12_DEFAULT_DEPTH_BIAS;
+ rastDesc.DepthBiasClamp = D3D12_DEFAULT_DEPTH_BIAS_CLAMP;
+ rastDesc.SlopeScaledDepthBias = D3D12_DEFAULT_SLOPE_SCALED_DEPTH_BIAS;
+ rastDesc.DepthClipEnable = TRUE;
+
+ psoDesc.RasterizerState = rastDesc;
+
+ // ### this is wrong
+ const D3D12_RENDER_TARGET_BLEND_DESC defaultRenderTargetBlendDesc = {
+ TRUE, FALSE,
+ D3D12_BLEND_SRC_ALPHA, D3D12_BLEND_INV_SRC_ALPHA, D3D12_BLEND_OP_ADD,
+ D3D12_BLEND_ZERO, D3D12_BLEND_ZERO, D3D12_BLEND_OP_ADD,
+ D3D12_LOGIC_OP_NOOP,
+ D3D12_COLOR_WRITE_ENABLE_ALL
+ };
+ D3D12_BLEND_DESC blendDesc = {};
+ blendDesc.RenderTarget[0] = defaultRenderTargetBlendDesc;
+
+ psoDesc.BlendState = blendDesc;
+
+ psoDesc.DepthStencilState.DepthEnable = pipelineState.depthEnable;
+ psoDesc.DepthStencilState.DepthWriteMask = pipelineState.depthWrite ? D3D12_DEPTH_WRITE_MASK_ALL : D3D12_DEPTH_WRITE_MASK_ZERO;
+ psoDesc.DepthStencilState.DepthFunc = D3D12_COMPARISON_FUNC(pipelineState.depthFunc);
+ psoDesc.DepthStencilState.StencilEnable = pipelineState.stencilEnable;
+ // ### stencil stuff
+ psoDesc.SampleMask = UINT_MAX;
+ psoDesc.PrimitiveTopologyType = D3D12_PRIMITIVE_TOPOLOGY_TYPE(pipelineState.topologyType);
+ psoDesc.NumRenderTargets = 1;
+ psoDesc.RTVFormats[0] = DXGI_FORMAT_R8G8B8A8_UNORM;
+ psoDesc.DSVFormat = DXGI_FORMAT_D32_FLOAT;
+ psoDesc.SampleDesc.Count = 1;
+
+ HRESULT hr = device->CreateGraphicsPipelineState(&psoDesc, IID_PPV_ARGS(&pso));
+ if (FAILED(hr)) {
+ qWarning("Failed to create graphics pipeline state");
+ return;
+ }
+ m_psoCache[pipelineState] = pso;
+ }
+
+ commandList->SetPipelineState(pso.Get());
+
+ commandList->SetGraphicsRootSignature(m_rootSig.Get()); // invalidates bindings
+}
+
+void QSGD3D12EnginePrivate::setVertexBuffer(const quint8 *data, int size)
+{
+ vertexData.p = data;
+ vertexData.size = size;
+ vertexData.changed = true;
+}
+
+void QSGD3D12EnginePrivate::setIndexBuffer(const quint8 *data, int size)
+{
+ indexData.p = data;
+ indexData.size = size;
+ indexData.changed = true;
+}
+
+void QSGD3D12EnginePrivate::setConstantBuffer(const quint8 *data, int size)
+{
+ constantData.p = data;
+ constantData.size = size;
+ constantData.changed = true;
+}
+
+void QSGD3D12EnginePrivate::queueViewport(const QRect &rect)
+{
+ const D3D12_VIEWPORT viewport = { 0, 0, float(rect.width()), float(rect.height()), 0, 1 };
+ commandList->RSSetViewports(1, &viewport);
+}
+
+void QSGD3D12EnginePrivate::queueScissor(const QRect &rect)
+{
+ const D3D12_RECT scissorRect = { 0, 0, rect.width() - 1, rect.height() - 1 };
+ commandList->RSSetScissorRects(1, &scissorRect);
+}
+
+void QSGD3D12EnginePrivate::queueSetRenderTarget()
+{
+ D3D12_CPU_DESCRIPTOR_HANDLE rtvHandle = backBufferRTV();
+ D3D12_CPU_DESCRIPTOR_HANDLE dsvHandle = dsv;
+ commandList->OMSetRenderTargets(1, &rtvHandle, FALSE, &dsvHandle);
+}
+
+void QSGD3D12EnginePrivate::queueClearRenderTarget(const QColor &color)
+{
+ const float clearColor[] = { float(color.redF()), float(color.blueF()), float(color.greenF()), float(color.alphaF()) };
+ commandList->ClearRenderTargetView(backBufferRTV(), clearColor, 0, nullptr);
+}
+
+void QSGD3D12EnginePrivate::queueClearDepthStencil(float depthValue, quint8 stencilValue)
+{
+ commandList->ClearDepthStencilView(dsv, D3D12_CLEAR_FLAG_DEPTH, depthValue, stencilValue, 0, nullptr);
+}
+
+void QSGD3D12EnginePrivate::queueDraw(QSGGeometry::DrawingMode mode, int count, int vboOffset, int vboStride,
+ int cboOffset,
+ int startIndexIndex, QSGD3D12Format indexFormat)
+{
+ // Due to the simplistic way our current renderer works, we can just upload the
+ // entire vertex/index data in case it was rebuilt.
+
+ if (vertexData.changed) {
+ vertexData.changed = false;
+ qDebug("upload vertex");
+ // Only enlarge, never shrink
+ const bool newBufferNeeded = vertexBuffer ? (vertexData.size > vertexBuffer->GetDesc().Width) : true;
+ if (newBufferNeeded) {
+ qDebug("new vertex buffer of size %d", vertexData.size);
+ vertexBuffer.Attach(createBuffer(vertexData.size));
+ }
+ if (!vertexBuffer)
+ return;
+ quint8 *p = nullptr;
+ D3D12_RANGE readRange = { 0, 0 };
+ if (FAILED(vertexBuffer->Map(0, &readRange, reinterpret_cast<void **>(&p)))) {
+ qWarning("Map failed for buffer of size %d", vertexData.size);
+ return;
+ }
+ memcpy(p, vertexData.p, vertexData.size);
+ vertexBuffer->Unmap(0, nullptr);
+ }
+
+ if (indexData.changed) {
+ indexData.changed = false;
+ if (indexData.size > 0) {
+ qDebug("upload index");
+ const bool newBufferNeeded = indexBuffer ? (indexData.size > indexBuffer->GetDesc().Width) : true;
+ if (newBufferNeeded) {
+ qDebug("new index buffer of size %d", indexData.size);
+ indexBuffer.Attach(createBuffer(indexData.size));
+ }
+ if (!indexBuffer)
+ return;
+ quint8 *p = nullptr;
+ D3D12_RANGE readRange = { 0, 0 };
+ if (FAILED(indexBuffer->Map(0, &readRange, reinterpret_cast<void **>(&p)))) {
+ qWarning("Map failed for buffer of size %d", indexData.size);
+ return;
+ }
+ memcpy(p, indexData.p, indexData.size);
+ indexBuffer->Unmap(0, nullptr);
+ } else {
+ indexBuffer = nullptr;
+ }
+ }
+
+ if (constantData.changed) {
+ constantData.changed = false;
+ qDebug("upload constant");
+ const bool newBufferNeeded = constantBuffer ? (constantData.size > constantBuffer->GetDesc().Width) : true;
+ if (newBufferNeeded) {
+ qDebug("new constant buffer of size %d", constantData.size);
+ constantBuffer.Attach(createBuffer(constantData.size));
+ }
+ if (!constantBuffer)
+ return;
+ quint8 *p = nullptr;
+ D3D12_RANGE readRange = { 0, 0 };
+ if (FAILED(constantBuffer->Map(0, &readRange, reinterpret_cast<void **>(&p)))) {
+ qWarning("Map failed for buffer of size %d", constantData.size);
+ return;
+ }
+ memcpy(p, constantData.p, constantData.size);
+ constantBuffer->Unmap(0, nullptr);
+ }
+
+ if (cboOffset >= 0 && constantBuffer)
+ commandList->SetGraphicsRootConstantBufferView(0, constantBuffer->GetGPUVirtualAddress() + cboOffset);
+
+ Q_ASSERT(vertexBuffer);
+
+ D3D12_VERTEX_BUFFER_VIEW vbv;
+ vbv.BufferLocation = vertexBuffer->GetGPUVirtualAddress() + vboOffset;
+ vbv.SizeInBytes = vboStride * count;
+ vbv.StrideInBytes = vboStride;
+
+ Q_ASSERT(indexBuffer || startIndexIndex < 0);
+
+ D3D_PRIMITIVE_TOPOLOGY topology;
+ switch (mode) {
+ case QSGGeometry::DrawPoints:
+ topology = D3D_PRIMITIVE_TOPOLOGY_POINTLIST;
+ break;
+ case QSGGeometry::DrawLines:
+ topology = D3D_PRIMITIVE_TOPOLOGY_LINELIST;
+ break;
+ case QSGGeometry::DrawLineStrip:
+ topology = D3D_PRIMITIVE_TOPOLOGY_LINESTRIP;
+ break;
+ case QSGGeometry::DrawTriangles:
+ topology = D3D_PRIMITIVE_TOPOLOGY_TRIANGLELIST;
+ break;
+ case QSGGeometry::DrawTriangleStrip:
+ topology = D3D_PRIMITIVE_TOPOLOGY_TRIANGLESTRIP;
+ break;
+ default:
+ qFatal("Unsupported drawing mode 0x%x", mode);
+ break;
+ }
+
+ commandList->IASetPrimitiveTopology(topology);
+
+ commandList->IASetVertexBuffers(0, 1, &vbv);
+
+ if (startIndexIndex >= 0) {
+ D3D12_INDEX_BUFFER_VIEW ibv;
+ ibv.BufferLocation = indexBuffer->GetGPUVirtualAddress();
+ ibv.SizeInBytes = indexData.size;
+ ibv.Format = DXGI_FORMAT(indexFormat);
+ commandList->IASetIndexBuffer(&ibv);
+ }
+
+ if (startIndexIndex >= 0)
+ commandList->DrawIndexedInstanced(count, 1, startIndexIndex, 0, 0);
+ else
+ commandList->DrawInstanced(count, 1, 0, 0);
+}
+
+void QSGD3D12EnginePrivate::present()
+{
+ if (!initialized)
+ return;
+
+ qDebug("--- present with vsync ---");
+
+ HRESULT hr = swapChain->Present(1, 0);
+ if (hr == DXGI_ERROR_DEVICE_REMOVED || hr == DXGI_ERROR_DEVICE_RESET) {
+ deviceManager()->deviceLossDetected();
+ return;
+ } else if (FAILED(hr)) {
+ qWarning("Present failed: 0x%x", hr);
+ return;
+ }
+}
+
+void QSGD3D12EnginePrivate::waitGPU()
+{
+ if (!initialized)
+ return;
+
+ qDebug("--- blocking wait for GPU ---");
+ waitForGPU(presentFence);
+}
+
+QT_END_NAMESPACE
diff --git a/src/quick/scenegraph/adaptations/d3d12/qsgd3d12engine_p.h b/src/quick/scenegraph/adaptations/d3d12/qsgd3d12engine_p.h
new file mode 100644
index 0000000000..d5f038fd0c
--- /dev/null
+++ b/src/quick/scenegraph/adaptations/d3d12/qsgd3d12engine_p.h
@@ -0,0 +1,226 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtQuick module of the Qt Toolkit.
+**
+** $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 QSGD3D12ENGINE_P_H
+#define QSGD3D12ENGINE_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 <QWindow>
+#include <qsggeometry.h>
+
+QT_BEGIN_NAMESPACE
+
+// No D3D or COM headers must be pulled in here. All that has to be isolated
+// to engine_p_p.h and engine.cpp.
+
+class QSGD3D12EnginePrivate;
+
+// Shader bytecode and other strings are expected to be static so that a
+// different pointer == different shader.
+
+enum QSGD3D12Format {
+ FmtUnknown = 0,
+
+ FmtFloat4 = 2, // DXGI_FORMAT_R32G32B32A32_FLOAT
+ FmtFloat3 = 6, // DXGI_FORMAT_R32G32B32_FLOAT
+ FmtFloat2 = 16, // DXGI_FORMAT_R32G32_FLOAT
+ FmtFloat = 41, // DXGI_FORMAT_R32_FLOAT
+
+ // glVertexAttribPointer with GL_UNSIGNED_BYTE and normalized == true maps to the UNORM formats below
+ FmtUNormByte4 = 28, // DXGI_FORMAT_R8G8B8A8_UNORM
+ FmtUNormByte2 = 49, // DXGI_FORMAT_R8G8_UNORM
+ FmtUNormByte = 61, // DXGI_FORMAT_R8_UNORM
+
+ // Index data types
+ FmtUnsignedShort = 57, // DXGI_FORMAT_R16_UINT
+ FmtUnsignedInt = 42 // DXGI_FORMAT_R32_UINT
+};
+
+struct QSGD3D12InputElement
+{
+ QSGD3D12InputElement() { }
+ QSGD3D12InputElement(const char *name, QSGD3D12Format format, quint32 slot, quint32 offset)
+ : name(name), format(format), slot(slot), offset(offset) { }
+
+ const char *name = nullptr;
+ QSGD3D12Format format = FmtFloat4;
+ quint32 slot = 0;
+ quint32 offset = 0;
+
+ bool operator==(const QSGD3D12InputElement &other) const {
+ return name == other.name && format == other.format
+ && slot == other.slot && offset == other.offset;
+ }
+};
+
+inline uint qHash(const QSGD3D12InputElement &key, uint seed = 0)
+{
+ return qHash(key.name, seed) + key.format + key.offset;
+}
+
+struct QSGD3D12ShaderState
+{
+ const quint8 *vs = nullptr;
+ quint32 vsSize = 0;
+ const quint8 *ps = nullptr;
+ quint32 psSize = 0;
+
+ bool operator==(const QSGD3D12ShaderState &other) const {
+ return vs == other.vs && vsSize == other.vsSize
+ && ps == other.ps && psSize == other.psSize;
+ }
+};
+
+inline uint qHash(const QSGD3D12ShaderState &key, uint seed = 0)
+{
+ return qHash(key.vs, seed) + key.vsSize + qHash(key.ps, seed) + key.psSize;
+}
+
+struct QSGD3D12PipelineState
+{
+ enum CullMode {
+ CullNone = 1,
+ CullFront,
+ CullBack
+ };
+
+ enum DepthFunc {
+ DepthNever = 1,
+ DepthLess,
+ DepthEqual,
+ DepthLessEqual,
+ DepthGreater,
+ DepthNotEqual,
+ DepthGreaterEqual,
+ DepthAlways
+ };
+
+ enum TopologyType {
+ TopologyTypePoint = 1,
+ TopologyTypeLine,
+ TopologyTypeTriangle
+ };
+
+ QSGD3D12ShaderState shaders;
+
+ QVector<QSGD3D12InputElement> inputElements;
+
+ CullMode cullMode = CullNone;
+ bool frontCCW = true;
+ bool premulBlend = false; // == GL_ONE, GL_ONE_MINUS_SRC_ALPHA
+ bool depthEnable = true;
+ DepthFunc depthFunc = DepthLess;
+ bool depthWrite = true;
+ bool stencilEnable = false;
+ // ### stencil stuff
+ TopologyType topologyType = TopologyTypeTriangle;
+
+ bool operator==(const QSGD3D12PipelineState &other) const {
+ return shaders == other.shaders
+ && inputElements == other.inputElements
+ && cullMode == other.cullMode
+ && frontCCW == other.frontCCW
+ && premulBlend == other.premulBlend
+ && depthEnable == other.depthEnable
+ && depthFunc == other.depthFunc
+ && depthWrite == other.depthWrite
+ && stencilEnable == other.stencilEnable
+ && topologyType == other.topologyType;
+ }
+};
+
+inline uint qHash(const QSGD3D12PipelineState &key, uint seed = 0)
+{
+ return qHash(key.shaders, seed) + qHash(key.inputElements, seed)
+ + key.cullMode + key.frontCCW + key.premulBlend + key.depthEnable
+ + key.depthFunc + key.depthWrite + key.stencilEnable + key.topologyType;
+}
+
+class QSGD3D12Engine
+{
+public:
+ QSGD3D12Engine();
+ ~QSGD3D12Engine();
+
+ bool attachToWindow(QWindow *window);
+ void releaseResources();
+ void resize();
+
+ void beginFrame();
+ void endFrame();
+
+ void setPipelineState(const QSGD3D12PipelineState &pipelineState);
+
+ void setVertexBuffer(const quint8 *data, int size);
+ void setIndexBuffer(const quint8 *data, int size);
+ void setConstantBuffer(const quint8 *data, int size);
+
+ void queueViewport(const QRect &rect);
+ void queueScissor(const QRect &rect);
+ void queueSetRenderTarget();
+ void queueClearRenderTarget(const QColor &color);
+ void queueClearDepthStencil(float depthValue, quint8 stencilValue);
+
+ void queueDraw(QSGGeometry::DrawingMode mode, int count, int vboOffset, int vboStride,
+ int cboOffset,
+ int startIndexIndex = -1, QSGD3D12Format indexFormat = FmtUnsignedShort);
+
+ void present();
+ void waitGPU();
+
+ static quint32 alignedConstantBufferSize(quint32 size);
+
+private:
+ QSGD3D12EnginePrivate *d;
+ Q_DISABLE_COPY(QSGD3D12Engine)
+};
+
+QT_END_NAMESPACE
+
+#endif // QSGD3D12ENGINE_P_H
diff --git a/src/quick/scenegraph/adaptations/d3d12/qsgd3d12engine_p_p.h b/src/quick/scenegraph/adaptations/d3d12/qsgd3d12engine_p_p.h
new file mode 100644
index 0000000000..733444339c
--- /dev/null
+++ b/src/quick/scenegraph/adaptations/d3d12/qsgd3d12engine_p_p.h
@@ -0,0 +1,225 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtQuick module of the Qt Toolkit.
+**
+** $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 QSGD3D12ENGINE_P_P_H
+#define QSGD3D12ENGINE_P_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 "qsgd3d12engine_p.h"
+
+#include <d3d12.h>
+#include <dxgi1_4.h>
+#include <wrl/client.h>
+
+using namespace Microsoft::WRL;
+
+// No moc-related features (Q_OBJECT, signals, etc.) can be used here to due
+// moc-generated code failing to compile when combined with COM stuff.
+
+QT_BEGIN_NAMESPACE
+
+struct QSGD3D12DescriptorHandle
+{
+ QSGD3D12DescriptorHandle() { cpu.ptr = 0; gpu.ptr = 0; }
+ D3D12_CPU_DESCRIPTOR_HANDLE cpu;
+ D3D12_GPU_DESCRIPTOR_HANDLE gpu;
+};
+
+class QSGD3D12DescriptorHeapManager
+{
+public:
+ void initialize(ID3D12Device *device);
+
+ void releaseResources();
+
+ enum Flag {
+ ShaderVisible = 0x01
+ };
+ Q_DECLARE_FLAGS(Flags, Flag)
+
+ QSGD3D12DescriptorHandle allocate(D3D12_DESCRIPTOR_HEAP_TYPE type, Flags flags = 0);
+ quint32 handleSize(D3D12_DESCRIPTOR_HEAP_TYPE type) const { return m_handleSizes[type]; }
+
+private:
+ ID3D12Device *m_device = nullptr;
+ struct Heap {
+ D3D12_DESCRIPTOR_HEAP_TYPE type;
+ ComPtr<ID3D12DescriptorHeap> heap;
+ int count = 0;
+ QSGD3D12DescriptorHandle start;
+ quint32 handleSize;
+ };
+ QVector<Heap> m_heaps;
+ quint32 m_handleSizes[D3D12_DESCRIPTOR_HEAP_TYPE_NUM_TYPES];
+};
+
+class QSGD3D12DeviceManager
+{
+public:
+ ID3D12Device *ref();
+ void unref();
+ void deviceLossDetected();
+ IDXGIFactory4 *dxgi();
+
+ struct DeviceLossObserver {
+ virtual void deviceLost() = 0;
+ };
+ void registerDeviceLossObserver(DeviceLossObserver *observer);
+
+private:
+ void ensureCreated();
+
+ ComPtr<ID3D12Device> m_device;
+ ComPtr<IDXGIFactory4> m_factory;
+ QAtomicInt m_ref;
+ QVector<DeviceLossObserver *> m_observers;
+};
+
+Q_DECLARE_OPERATORS_FOR_FLAGS(QSGD3D12DescriptorHeapManager::Flags)
+
+struct QSGD3D12Fence
+{
+ ~QSGD3D12Fence() {
+ if (event)
+ CloseHandle(event);
+ }
+ ComPtr<ID3D12Fence> fence;
+ HANDLE event = nullptr;
+ QAtomicInt value;
+};
+
+class QSGD3D12EnginePrivate : public QSGD3D12DeviceManager::DeviceLossObserver
+{
+public:
+ void initialize(QWindow *w);
+ bool isInitialized() const { return initialized; }
+ void releaseResources();
+ void resize();
+
+ void beginFrame();
+ void endFrame();
+
+ void setPipelineState(const QSGD3D12PipelineState &pipelineState);
+
+ void setVertexBuffer(const quint8 *data, int size);
+ void setIndexBuffer(const quint8 *data, int size);
+ void setConstantBuffer(const quint8 *data, int size);
+
+ void queueViewport(const QRect &rect);
+ void queueScissor(const QRect &rect);
+ void queueSetRenderTarget();
+ void queueClearRenderTarget(const QColor &color);
+ void queueClearDepthStencil(float depthValue, quint8 stencilValue);
+
+ void queueDraw(QSGGeometry::DrawingMode mode, int count, int vboOffset, int vboStride,
+ int cboOffset,
+ int startIndexIndex, QSGD3D12Format indexFormat);
+
+ void present();
+ void waitGPU();
+
+ // the device is intentionally hidden here. all resources have to go
+ // through the engine and, unlike with GL, cannot just be created in random
+ // places due to the need for proper tracking, managing and releasing.
+private:
+ void setupRenderTargets();
+ void deviceLost() override;
+
+ DXGI_SAMPLE_DESC makeSampleDesc(DXGI_FORMAT format, int samples);
+ ID3D12Resource *createDepthStencil(D3D12_CPU_DESCRIPTOR_HANDLE viewHandle, const QSize &size, int samples);
+
+ QSGD3D12Fence *createFence() const;
+ void waitForGPU(QSGD3D12Fence *f) const;
+
+ void transitionResource(ID3D12Resource *resource, ID3D12GraphicsCommandList *commandList,
+ D3D12_RESOURCE_STATES before, D3D12_RESOURCE_STATES after) const;
+
+ ID3D12Resource *createBuffer(int size);
+
+ ID3D12Resource *backBufferRT() const;
+ D3D12_CPU_DESCRIPTOR_HANDLE backBufferRTV() const;
+
+ struct StagingBufferRef {
+ const quint8 *p = nullptr;
+ int size = 0;
+ bool changed = true;
+ };
+
+ bool initialized = false;
+ QWindow *window = nullptr;
+ int swapChainBufferCount = 2;
+ ID3D12Device *device;
+ ComPtr<ID3D12CommandQueue> commandQueue;
+ ComPtr<IDXGISwapChain3> swapChain;
+ ComPtr<ID3D12Resource> renderTargets[2];
+ D3D12_CPU_DESCRIPTOR_HANDLE rtv0;
+ D3D12_CPU_DESCRIPTOR_HANDLE dsv;
+ ComPtr<ID3D12Resource> depthStencil;
+ ComPtr<ID3D12CommandAllocator> commandAllocator;
+ ComPtr<ID3D12CommandAllocator> bundleAllocator;
+ ComPtr<ID3D12GraphicsCommandList> commandList;
+ QSGD3D12DescriptorHeapManager descHeapManager;
+ QSGD3D12Fence *presentFence = nullptr;
+
+ StagingBufferRef vertexData;
+ StagingBufferRef indexData;
+ StagingBufferRef constantData;
+
+ ComPtr<ID3D12Resource> vertexBuffer;
+ ComPtr<ID3D12Resource> indexBuffer;
+ ComPtr<ID3D12Resource> constantBuffer;
+
+ QHash<QSGD3D12PipelineState, ComPtr<ID3D12PipelineState> > m_psoCache;
+
+ ComPtr<ID3D12RootSignature> m_rootSig;
+};
+
+QT_END_NAMESPACE
+
+#endif
diff --git a/src/quick/scenegraph/adaptations/d3d12/qsgd3d12material.cpp b/src/quick/scenegraph/adaptations/d3d12/qsgd3d12material.cpp
new file mode 100644
index 0000000000..e7cee712d3
--- /dev/null
+++ b/src/quick/scenegraph/adaptations/d3d12/qsgd3d12material.cpp
@@ -0,0 +1,163 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtQuick module of the Qt Toolkit.
+**
+** $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 "qsgd3d12material_p.h"
+#include <private/qsgrenderer_p.h>
+
+#include "hlsl_vs_vertexcolor.h"
+#include "hlsl_ps_vertexcolor.h"
+
+QT_BEGIN_NAMESPACE
+
+QSGD3D12Material::RenderState QSGD3D12Material::makeRenderState(QSGRenderer *renderer, RenderState::DirtyStates dirty)
+{
+ RenderState rs;
+ rs.m_dirty = dirty;
+ rs.m_data = renderer;
+ return rs;
+}
+
+float QSGD3D12Material::RenderState::opacity() const
+{
+ Q_ASSERT(m_data);
+ return static_cast<const QSGRenderer *>(m_data)->currentOpacity();
+}
+
+float QSGD3D12Material::RenderState::determinant() const
+{
+ Q_ASSERT(m_data);
+ return static_cast<const QSGRenderer *>(m_data)->determinant();
+}
+
+QMatrix4x4 QSGD3D12Material::RenderState::combinedMatrix() const
+{
+ Q_ASSERT(m_data);
+ return static_cast<const QSGRenderer *>(m_data)->currentCombinedMatrix();
+}
+
+float QSGD3D12Material::RenderState::devicePixelRatio() const
+{
+ Q_ASSERT(m_data);
+ return static_cast<const QSGRenderer *>(m_data)->devicePixelRatio();
+}
+
+QMatrix4x4 QSGD3D12Material::RenderState::modelViewMatrix() const
+{
+ Q_ASSERT(m_data);
+ return static_cast<const QSGRenderer *>(m_data)->currentModelViewMatrix();
+}
+
+QMatrix4x4 QSGD3D12Material::RenderState::projectionMatrix() const
+{
+ Q_ASSERT(m_data);
+ return static_cast<const QSGRenderer *>(m_data)->currentProjectionMatrix();
+}
+
+QRect QSGD3D12Material::RenderState::viewportRect() const
+{
+ Q_ASSERT(m_data);
+ return static_cast<const QSGRenderer *>(m_data)->viewportRect();
+}
+
+QRect QSGD3D12Material::RenderState::deviceRect() const
+{
+ Q_ASSERT(m_data);
+ return static_cast<const QSGRenderer *>(m_data)->deviceRect();
+}
+
+QSGMaterialShader *QSGD3D12Material::createShader() const
+{
+ return nullptr;
+}
+
+QSGMaterialType QSGD3D12VertexColorMaterial::mtype;
+
+QSGMaterialType *QSGD3D12VertexColorMaterial::type() const
+{
+ return &QSGD3D12VertexColorMaterial::mtype;
+}
+
+int QSGD3D12VertexColorMaterial::compare(const QSGMaterial *other) const
+{
+ Q_ASSERT(other && type() == other->type());
+ // As the vertex color material has all its state in the vertex attributes
+ // defined by the geometry, all such materials will be equal.
+ return 0;
+}
+
+static const int VERTEX_COLOR_CB_SIZE_0 = 16 * sizeof(float); // float4x4
+static const int VERTEX_COLOR_CB_SIZE_1 = sizeof(float); // float
+static const int VERTEX_COLOR_CB_SIZE = VERTEX_COLOR_CB_SIZE_0 + VERTEX_COLOR_CB_SIZE_1;
+
+int QSGD3D12VertexColorMaterial::constantBufferSize() const
+{
+ return QSGD3D12Engine::alignedConstantBufferSize(VERTEX_COLOR_CB_SIZE);
+}
+
+void QSGD3D12VertexColorMaterial::preparePipeline(QSGD3D12ShaderState *shaders)
+{
+ shaders->vs = g_VS_VertexColor;
+ shaders->vsSize = sizeof(g_VS_VertexColor);
+ shaders->ps = g_PS_VertexColor;
+ shaders->psSize = sizeof(g_PS_VertexColor);
+}
+
+QSGD3D12Material::UpdateResults QSGD3D12VertexColorMaterial::updatePipeline(const RenderState &state,
+ QSGD3D12ShaderState *,
+ quint8 *constantBuffer)
+{
+ QSGD3D12Material::UpdateResults r = 0;
+ quint8 *p = constantBuffer;
+
+ if (state.isMatrixDirty()) {
+ memcpy(p, state.combinedMatrix().constData(), VERTEX_COLOR_CB_SIZE_0);
+ r |= UpdatedConstantBuffer;
+ }
+ p += VERTEX_COLOR_CB_SIZE_0;
+
+ if (state.isOpacityDirty()) {
+ const float opacity = state.opacity();
+ memcpy(p, &opacity, VERTEX_COLOR_CB_SIZE_1);
+ r |= UpdatedConstantBuffer;
+ }
+
+ return r;
+}
+
+QT_END_NAMESPACE
diff --git a/src/quick/scenegraph/adaptations/d3d12/qsgd3d12material_p.h b/src/quick/scenegraph/adaptations/d3d12/qsgd3d12material_p.h
new file mode 100644
index 0000000000..d61b1e94dc
--- /dev/null
+++ b/src/quick/scenegraph/adaptations/d3d12/qsgd3d12material_p.h
@@ -0,0 +1,133 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtQuick module of the Qt Toolkit.
+**
+** $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 QSGD3D12MATERIAL_P_H
+#define QSGD3D12MATERIAL_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 <QtQuick/qsgmaterial.h>
+#include "qsgd3d12engine_p.h"
+
+QT_BEGIN_NAMESPACE
+
+class QSGRenderer;
+
+// The D3D renderer works with QSGD3D12Material as the "base" class since
+// QSGMaterial and its GL program related bits are not suitable. Also, there is
+// no split like with QSGMaterialShader.
+
+class QSGD3D12Material : public QSGMaterial
+{
+public:
+ struct RenderState {
+ enum DirtyState {
+ DirtyMatrix = 0x0001,
+ DirtyOpacity = 0x0002,
+ DirtyAll = 0xFFFF
+ };
+ Q_DECLARE_FLAGS(DirtyStates, DirtyState)
+
+ DirtyStates dirtyStates() const { return m_dirty; }
+
+ bool isMatrixDirty() const { return m_dirty & DirtyMatrix; }
+ bool isOpacityDirty() const { return m_dirty & DirtyOpacity; }
+
+ float opacity() const;
+ QMatrix4x4 combinedMatrix() const;
+ QMatrix4x4 modelViewMatrix() const;
+ QMatrix4x4 projectionMatrix() const;
+ QRect viewportRect() const;
+ QRect deviceRect() const;
+ float determinant() const;
+ float devicePixelRatio() const;
+
+ DirtyStates m_dirty = 0;
+ void *m_data = nullptr;
+ };
+
+ enum UpdateResult {
+ UpdatedShaders = 0x0001,
+ UpdatedConstantBuffer = 0x0002
+ };
+ Q_DECLARE_FLAGS(UpdateResults, UpdateResult)
+
+ static RenderState makeRenderState(QSGRenderer *renderer, RenderState::DirtyStates dirty);
+
+ virtual int constantBufferSize() const = 0;
+ virtual void preparePipeline(QSGD3D12ShaderState *shaders) = 0;
+ virtual UpdateResults updatePipeline(const RenderState &state,
+ QSGD3D12ShaderState *shaders,
+ quint8 *constantBuffer) = 0;
+
+private:
+ QSGMaterialShader *createShader() const override; // dummy, QSGMaterialShader is too GL dependent
+};
+
+Q_DECLARE_OPERATORS_FOR_FLAGS(QSGD3D12Material::RenderState::DirtyStates)
+Q_DECLARE_OPERATORS_FOR_FLAGS(QSGD3D12Material::UpdateResults)
+
+class QSGD3D12VertexColorMaterial : public QSGD3D12Material
+{
+public:
+ QSGMaterialType *type() const override;
+ int compare(const QSGMaterial *other) const override;
+
+ virtual int constantBufferSize() const override;
+ void preparePipeline(QSGD3D12ShaderState *shaders) override;
+ UpdateResults updatePipeline(const RenderState &state,
+ QSGD3D12ShaderState *shaders,
+ quint8 *constantBuffer) override;
+
+private:
+ static QSGMaterialType mtype;
+};
+
+QT_END_NAMESPACE
+
+#endif // QSGD3D12MATERIAL_P_H
diff --git a/src/quick/scenegraph/adaptations/d3d12/qsgd3d12rectanglenode.cpp b/src/quick/scenegraph/adaptations/d3d12/qsgd3d12rectanglenode.cpp
new file mode 100644
index 0000000000..5956a06e2f
--- /dev/null
+++ b/src/quick/scenegraph/adaptations/d3d12/qsgd3d12rectanglenode.cpp
@@ -0,0 +1,72 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtQuick module of the Qt Toolkit.
+**
+** $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 "qsgd3d12rectanglenode_p.h"
+
+QT_BEGIN_NAMESPACE
+
+QSGD3D12RectangleNode::QSGD3D12RectangleNode()
+{
+ setMaterial(&m_material);
+}
+
+void QSGD3D12RectangleNode::updateMaterialAntialiasing()
+{
+ //if (m_antialiasing)
+ // setMaterial(&m_smoothMaterial);
+ //else
+ setMaterial(&m_material);
+}
+
+void QSGD3D12RectangleNode::updateMaterialBlending(QSGNode::DirtyState *state)
+{
+ // smoothed material is always blended, so no change in material state
+ if (material() == &m_material) {
+ bool wasBlending = (m_material.flags() & QSGMaterial::Blending);
+ bool isBlending = (m_gradient_stops.size() > 0 && !m_gradient_is_opaque)
+ || (m_color.alpha() < 255 && m_color.alpha() != 0)
+ || (m_pen_width > 0 && m_border_color.alpha() < 255);
+ if (wasBlending != isBlending) {
+ m_material.setFlag(QSGMaterial::Blending, isBlending);
+ *state |= QSGNode::DirtyMaterial;
+ }
+ }
+}
+
+QT_END_NAMESPACE
diff --git a/src/quick/scenegraph/adaptations/d3d12/qsgd3d12rectanglenode_p.h b/src/quick/scenegraph/adaptations/d3d12/qsgd3d12rectanglenode_p.h
new file mode 100644
index 0000000000..464dc763a6
--- /dev/null
+++ b/src/quick/scenegraph/adaptations/d3d12/qsgd3d12rectanglenode_p.h
@@ -0,0 +1,74 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtQuick module of the Qt Toolkit.
+**
+** $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 QSGD3D12RECTANGLENODE_P_H
+#define QSGD3D12RECTANGLENODE_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 <private/qsgdefaultrectanglenode_p.h>
+#include "qsgd3d12material_p.h"
+
+QT_BEGIN_NAMESPACE
+
+class QSGD3D12RectangleNode : public QSGDefaultNoMaterialRectangleNode
+{
+public:
+ QSGD3D12RectangleNode();
+
+private:
+ void updateMaterialAntialiasing() override;
+ void updateMaterialBlending(QSGNode::DirtyState *state) override;
+
+ QSGD3D12VertexColorMaterial m_material;
+ //QSGD3D12Material m_smoothMaterial;
+};
+
+QT_END_NAMESPACE
+
+#endif // QSGD3D12RECTANGLENODE_P_H
diff --git a/src/quick/scenegraph/adaptations/d3d12/qsgd3d12rendercontext.cpp b/src/quick/scenegraph/adaptations/d3d12/qsgd3d12rendercontext.cpp
new file mode 100644
index 0000000000..a9dea49200
--- /dev/null
+++ b/src/quick/scenegraph/adaptations/d3d12/qsgd3d12rendercontext.cpp
@@ -0,0 +1,79 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtQuick module of the Qt Toolkit.
+**
+** $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 "qsgd3d12rendercontext_p.h"
+#include "qsgd3d12renderer_p.h"
+
+QT_BEGIN_NAMESPACE
+
+QSGD3D12RenderContext::QSGD3D12RenderContext(QSGContext *ctx)
+ : QSGRenderContext(ctx)
+{
+ qDebug("new d3d12 render context");
+}
+
+void QSGD3D12RenderContext::initialize(QOpenGLContext *)
+{
+ Q_UNREACHABLE();
+}
+
+void QSGD3D12RenderContext::invalidate()
+{
+ QSGRenderContext::invalidate();
+}
+
+QSGTexture *QSGD3D12RenderContext::createTexture(const QImage &image, uint flags) const
+{
+ Q_UNUSED(image);
+ Q_UNUSED(flags);
+ Q_UNREACHABLE();
+ return nullptr;
+}
+
+QSGRenderer *QSGD3D12RenderContext::createRenderer()
+{
+ return new QSGD3D12Renderer(this);
+}
+
+void QSGD3D12RenderContext::renderNextFrame(QSGRenderer *renderer, GLuint fbo)
+{
+ QSGRenderContext::renderNextFrame(renderer, fbo);
+}
+
+QT_END_NAMESPACE
diff --git a/src/quick/scenegraph/adaptations/d3d12/qsgd3d12rendercontext_p.h b/src/quick/scenegraph/adaptations/d3d12/qsgd3d12rendercontext_p.h
new file mode 100644
index 0000000000..2e05632ad9
--- /dev/null
+++ b/src/quick/scenegraph/adaptations/d3d12/qsgd3d12rendercontext_p.h
@@ -0,0 +1,80 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtQuick module of the Qt Toolkit.
+**
+** $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 QSGD3D12RENDERCONTEXT_P_H
+#define QSGD3D12RENDERCONTEXT_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 <private/qsgcontext_p.h>
+
+QT_BEGIN_NAMESPACE
+
+class QSGD3D12Engine;
+
+class QSGD3D12RenderContext : public QSGRenderContext
+{
+public:
+ QSGD3D12RenderContext(QSGContext *ctx);
+
+ void initialize(QOpenGLContext *) override; // not in use
+ void invalidate() override;
+ void renderNextFrame(QSGRenderer *renderer, GLuint fbo) override;
+ QSGTexture *createTexture(const QImage &image, uint flags) const override;
+ QSGRenderer *createRenderer() override;
+
+ void setEngine(QSGD3D12Engine *engine) { m_engine = engine; }
+ QSGD3D12Engine *engine() { return m_engine; }
+
+private:
+ QSGD3D12Engine *m_engine = nullptr;
+};
+
+QT_END_NAMESPACE
+
+#endif // QSGD3D12RENDERCONTEXT_P_H
diff --git a/src/quick/scenegraph/adaptations/d3d12/qsgd3d12renderer.cpp b/src/quick/scenegraph/adaptations/d3d12/qsgd3d12renderer.cpp
new file mode 100644
index 0000000000..b063138d74
--- /dev/null
+++ b/src/quick/scenegraph/adaptations/d3d12/qsgd3d12renderer.cpp
@@ -0,0 +1,477 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtQuick module of the Qt Toolkit.
+**
+** $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 "qsgd3d12renderer_p.h"
+#include "qsgd3d12rendercontext_p.h"
+#include "qsgd3d12material_p.h"
+#include <private/qsgnodeupdater_p.h>
+
+QT_BEGIN_NAMESPACE
+
+#define QSGNODE_TRAVERSE(NODE) for (QSGNode *child = NODE->firstChild(); child; child = child->nextSibling())
+
+#define DECLARE_DEBUG_VAR(variable) \
+ static bool debug_ ## variable() \
+ { static bool value = qgetenv("QSG_RENDERER_DEBUG").contains(QT_STRINGIFY(variable)); return value; }
+
+DECLARE_DEBUG_VAR(build)
+DECLARE_DEBUG_VAR(change)
+DECLARE_DEBUG_VAR(render)
+
+class DummyUpdater : public QSGNodeUpdater
+{
+public:
+ void updateState(QSGNode *) { };
+};
+
+QSGD3D12Renderer::QSGD3D12Renderer(QSGRenderContext *context)
+ : QSGRenderer(context),
+ m_renderList(16),
+ m_vboData(1024),
+ m_iboData(256),
+ m_cboData(256)
+{
+ setNodeUpdater(new DummyUpdater);
+}
+
+void QSGD3D12Renderer::renderScene(GLuint fboId)
+{
+ Q_UNUSED(fboId);
+
+ struct B : public QSGBindable {
+ void bind() const { }
+ } bindable;
+
+ QSGRenderer::renderScene(bindable);
+}
+
+// Search through the node set and remove nodes that are leaves of other
+// nodes in the same set.
+static QSet<QSGNode *> qsg_filterSubTree(const QSet<QSGNode *> &nodes, QSGRootNode *root)
+{
+ QSet<QSGNode *> result = nodes;
+ for (QSGNode *node : nodes) {
+ QSGNode *n = node;
+ while (n != root) {
+ if (result.contains(n)) {
+ result.remove(node);
+ break;
+ }
+ n = n->parent();
+ }
+ }
+ return result;
+}
+
+void QSGD3D12Renderer::updateMatrices(QSGNode *node, QSGTransformNode *xform)
+{
+ if (node->isSubtreeBlocked())
+ return;
+
+ if (node->type() == QSGNode::TransformNodeType) {
+ QSGTransformNode *tn = static_cast<QSGTransformNode *>(node);
+ if (xform)
+ tn->setCombinedMatrix(xform->combinedMatrix() * tn->matrix());
+ else
+ tn->setCombinedMatrix(tn->matrix());
+ QSGNODE_TRAVERSE(node)
+ updateMatrices(child, tn);
+
+ } else {
+ if (node->type() == QSGNode::GeometryNodeType || node->type() == QSGNode::ClipNodeType) {
+ static_cast<QSGBasicGeometryNode *>(node)->setMatrix(xform ? &xform->combinedMatrix() : 0);
+ }
+ QSGNODE_TRAVERSE(node)
+ updateMatrices(child, xform);
+ }
+}
+
+void QSGD3D12Renderer::buildRenderList(QSGNode *node, QSGClipNode *clip)
+{
+ if (node->isSubtreeBlocked())
+ return;
+
+ if (node->type() == QSGNode::GeometryNodeType || node->type() == QSGNode::ClipNodeType) {
+ QSGBasicGeometryNode *gn = static_cast<QSGBasicGeometryNode *>(node);
+ QSGGeometry *g = gn->geometry();
+
+ Element e;
+ e.node = gn;
+
+ if (g->vertexCount() > 0) {
+ e.vboOffset = m_vboData.size();
+ const int vertexSize = g->sizeOfVertex() * g->vertexCount();
+ m_vboData.resize(m_vboData.size() + vertexSize);
+ memcpy(m_vboData.data() + e.vboOffset, g->vertexData(), vertexSize);
+ }
+
+ if (g->indexCount() > 0) {
+ e.iboOffset = m_iboData.size();
+ e.iboStride = g->sizeOfIndex();
+ const int indexSize = e.iboStride * g->indexCount();
+ m_iboData.resize(m_iboData.size() + indexSize);
+ memcpy(m_iboData.data() + e.iboOffset, g->indexData(), indexSize);
+ }
+
+ if (node->type() == QSGNode::GeometryNodeType) {
+ QSGD3D12Material *m = static_cast<QSGD3D12Material *>(static_cast<QSGGeometryNode *>(node)->activeMaterial());
+ e.cboOffset = m_cboData.size();
+ e.cboSize = m->constantBufferSize();
+ m_cboData.resize(m_cboData.size() + e.cboSize);
+ }
+
+ m_renderList.add(e);
+
+ gn->setClipList(clip);
+ if (node->type() == QSGNode::ClipNodeType)
+ clip = static_cast<QSGClipNode *>(node);
+ }
+
+ QSGNODE_TRAVERSE(node)
+ buildRenderList(child, clip);
+}
+
+void QSGD3D12Renderer::render()
+{
+ QSGD3D12RenderContext *rc = static_cast<QSGD3D12RenderContext *>(context());
+ m_engine = rc->engine();
+ m_engine->beginFrame();
+
+ if (m_rebuild) {
+ // This changes everything, so discard all cached states
+ m_rebuild = false;
+ m_dirtyTransformNodes.clear();
+ m_dirtyTransformNodes.insert(rootNode());
+
+ m_renderList.reset();
+ m_vboData.reset();
+ m_iboData.reset();
+ m_cboData.reset();
+
+ buildRenderList(rootNode(), 0);
+
+ m_engine->setVertexBuffer(m_vboData.data(), m_vboData.size());
+ m_engine->setIndexBuffer(m_iboData.data(), m_iboData.size());
+
+ if (Q_UNLIKELY(debug_build())) {
+ qDebug("renderList: %d elements in total", m_renderList.size());
+ for (int i = 0; i < m_renderList.size(); ++i) {
+ const Element &e = m_renderList.at(i);
+ qDebug() << " - " << e.vboOffset << e.iboOffset << e.cboOffset << e.cboSize << e.node;
+ }
+ }
+ }
+
+ if (m_dirtyTransformNodes.size()) {
+ const QSet<QSGNode *> subTreeRoots = qsg_filterSubTree(m_dirtyTransformNodes, rootNode());
+ for (QSGNode *node : subTreeRoots) {
+ // First find the parent transform so we have the accumulated
+ // matrix up until this point.
+ QSGTransformNode *xform = 0;
+ QSGNode *n = node;
+ if (n->type() == QSGNode::TransformNodeType)
+ n = node->parent();
+ while (n != rootNode() && n->type() != QSGNode::TransformNodeType)
+ n = n->parent();
+ if (n != rootNode())
+ xform = static_cast<QSGTransformNode *>(n);
+
+ // Then update in the subtree
+ updateMatrices(node, xform);
+ }
+ }
+
+ if (m_dirtyOpaqueElements) {
+ m_dirtyOpaqueElements = false;
+ m_opaqueElements.clear();
+ m_opaqueElements.resize(m_renderList.size());
+ for (int i = 0; i < m_renderList.size(); ++i) {
+ const Element &e = m_renderList.at(i);
+ if (e.node->type() == QSGNode::GeometryNodeType) {
+ const QSGGeometryNode *gn = static_cast<QSGGeometryNode *>(e.node);
+ if (gn->inheritedOpacity() > 0.999f && ((gn->activeMaterial()->flags() & QSGMaterial::Blending) == 0))
+ m_opaqueElements.setBit(i);
+ }
+ }
+ }
+
+ renderElements();
+
+ m_engine->endFrame();
+ m_engine = nullptr;
+}
+
+void QSGD3D12Renderer::nodeChanged(QSGNode *node, QSGNode::DirtyState state)
+{
+ // note that with DirtyNodeRemoved the window and all the graphics engine may already be gone
+
+ if (Q_UNLIKELY(debug_change())) {
+ QDebug debug = qDebug();
+ debug << "dirty:";
+ if (state & QSGNode::DirtyGeometry)
+ debug << "Geometry";
+ if (state & QSGNode::DirtyMaterial)
+ debug << "Material";
+ if (state & QSGNode::DirtyMatrix)
+ debug << "Matrix";
+ if (state & QSGNode::DirtyNodeAdded)
+ debug << "Added";
+ if (state & QSGNode::DirtyNodeRemoved)
+ debug << "Removed";
+ if (state & QSGNode::DirtyOpacity)
+ debug << "Opacity";
+ if (state & QSGNode::DirtySubtreeBlocked)
+ debug << "SubtreeBlocked";
+ if (state & QSGNode::DirtyForceUpdate)
+ debug << "ForceUpdate";
+
+ // when removed, some parts of the node could already have been destroyed
+ // so don't debug it out.
+ if (state & QSGNode::DirtyNodeRemoved)
+ debug << (void *) node << node->type();
+ else
+ debug << node;
+ }
+
+ if (state & (QSGNode::DirtyNodeAdded
+ | QSGNode::DirtyNodeRemoved
+ | QSGNode::DirtySubtreeBlocked
+ | QSGNode::DirtyGeometry
+ | QSGNode::DirtyForceUpdate))
+ m_rebuild = true;
+
+ if (state & QSGNode::DirtyMatrix)
+ m_dirtyTransformNodes << node;
+
+ if (state & QSGNode::DirtyMaterial)
+ m_dirtyOpaqueElements = true;
+
+ QSGRenderer::nodeChanged(node, state);
+}
+
+void QSGD3D12Renderer::renderElements()
+{
+ QRect r = viewportRect();
+ r.setY(deviceRect().bottom() - r.bottom());
+ m_engine->queueViewport(r);
+ m_engine->queueScissor(r);
+ m_engine->queueSetRenderTarget();
+ m_engine->queueClearRenderTarget(clearColor());
+ m_engine->queueClearDepthStencil(1, 0);
+
+ m_pipelineState.premulBlend = false;
+ m_pipelineState.depthEnable = true;
+ m_pipelineState.depthWrite = true;
+
+ m_current_projection_matrix = projectionMatrix();
+
+ // First do opaque...
+ // The algorithm is quite simple. We traverse the list back-to-from
+ // and for every item, we start a second traversal from this point
+ // and draw all elements which have identical material. Then we clear
+ // the bit for this in the rendered list so we don't draw it again
+ // when we come to that index.
+ QBitArray rendered = m_opaqueElements;
+ for (int i = m_renderList.size() - 1; i >= 0; --i) {
+ if (rendered.testBit(i)) {
+ renderElement(i);
+ for (int j = i - 1; j >= 0; --j) {
+ if (rendered.testBit(j)) {
+ const QSGGeometryNode *gni = static_cast<QSGGeometryNode *>(m_renderList.at(i).node);
+ const QSGGeometryNode *gnj = static_cast<QSGGeometryNode *>(m_renderList.at(j).node);
+ if (gni->clipList() == gnj->clipList()
+ && gni->inheritedOpacity() == gnj->inheritedOpacity()
+ && gni->geometry()->drawingMode() == gnj->geometry()->drawingMode()
+ && gni->geometry()->attributes() == gnj->geometry()->attributes()) {
+ const QSGMaterial *ami = gni->activeMaterial();
+ const QSGMaterial *amj = gnj->activeMaterial();
+ if (ami->type() == amj->type()
+ && ami->flags() == amj->flags()
+ && ami->compare(amj) == 0) {
+ renderElement(j);
+ rendered.clearBit(j);
+ }
+ }
+ }
+ }
+ }
+ }
+
+ m_pipelineState.premulBlend = true;
+ m_pipelineState.depthWrite = false;
+
+ // ...then the alpha ones
+ for (int i = 0; i < m_renderList.size(); ++i) {
+ if (m_renderList.at(i).node->type() == QSGNode::GeometryNodeType && !m_opaqueElements.testBit(i))
+ renderElement(i);
+ }
+}
+
+void QSGD3D12Renderer::renderElement(int elementIndex)
+{
+ Element &e = m_renderList.at(elementIndex);
+ Q_ASSERT(e.node->type() == QSGNode::GeometryNodeType);
+
+ if (e.vboOffset < 0)
+ return;
+
+ Q_ASSERT(e.cboOffset >= 0);
+
+ const QSGGeometryNode *gn = static_cast<QSGGeometryNode *>(e.node);
+ if (Q_UNLIKELY(debug_render()))
+ qDebug() << "renderElement:" << elementIndex << gn << e.vboOffset << e.iboOffset << gn->inheritedOpacity() << gn->clipList();
+
+ if (gn->inheritedOpacity() < 0.001f) // pretty much invisible, don't draw it
+ return;
+
+ QSGD3D12Material::RenderState::DirtyStates dirtyState = QSGD3D12Material::RenderState::DirtyMatrix;
+ m_current_projection_matrix = projectionMatrix();
+ qreal scale = 1.0 / m_renderList.size();
+ m_current_projection_matrix(2, 2) = scale;
+ m_current_projection_matrix(2, 3) = 1.0f - (elementIndex + 1) * scale;
+ m_current_model_view_matrix = gn->matrix() ? *gn->matrix() : QMatrix4x4();
+ m_current_determinant = m_current_model_view_matrix.determinant();
+ if (gn->inheritedOpacity() != m_current_opacity) {
+ m_current_opacity = gn->inheritedOpacity();
+ dirtyState |= QSGD3D12Material::RenderState::DirtyOpacity;
+ }
+
+ const QSGGeometry *g = gn->geometry();
+ QSGD3D12Material *m = static_cast<QSGD3D12Material *>(gn->activeMaterial());
+
+ if (m->type() != m_lastMaterialType)
+ m->preparePipeline(&m_pipelineState.shaders);
+
+ if (!e.cboPrepared) {
+ e.cboPrepared = true;
+ dirtyState = QSGD3D12Material::RenderState::DirtyAll;
+ }
+
+ quint8 *cboPtr = nullptr;
+ if (e.cboSize > 0)
+ cboPtr = m_cboData.data() + e.cboOffset;
+
+ qDebug() << "ds" << dirtyState;
+ QSGD3D12Material::UpdateResults updRes = m->updatePipeline(QSGD3D12Material::makeRenderState(this, dirtyState),
+ &m_pipelineState.shaders,
+ cboPtr);
+ // For now there is no way to have extra SRVs and such. Once texturing is
+ // introduced, the above update call will have to be able to affect the
+ // root signature and communicate the need for SRVs or UAVs to the engine.
+
+ if (updRes.testFlag(QSGD3D12Material::UpdatedConstantBuffer))
+ m_engine->setConstantBuffer(m_cboData.data(), m_cboData.size());
+
+ // Input element layout
+ m_pipelineState.inputElements.resize(g->attributeCount());
+ const QSGGeometry::Attribute *attrs = g->attributes();
+ quint32 offset = 0;
+ for (int i = 0; i < g->attributeCount(); ++i) {
+ QSGD3D12InputElement &ie(m_pipelineState.inputElements[i]);
+ static const char *semanticNames[] = { "UNKNOWN", "POSITION", "COLOR", "TEXCOORD" };
+ Q_ASSERT(attrs[i].semantic >= 1 && attrs[i].semantic < _countof(semanticNames));
+ const int tupleSize = attrs[i].tupleSize;
+ ie.name = semanticNames[attrs[i].semantic];
+ ie.offset = offset;
+ // ### move format mapping to engine
+ static const QSGD3D12Format formatMap_ub[] = { FmtUnknown,
+ FmtUNormByte,
+ FmtUNormByte2,
+ FmtUnknown,
+ FmtUNormByte4 };
+ static const QSGD3D12Format formatMap_f[] = { FmtUnknown,
+ FmtFloat,
+ FmtFloat2,
+ FmtFloat3,
+ FmtFloat4 };
+ switch (attrs[i].type) {
+ case QSGGeometry::TypeUnsignedByte:
+ ie.format = formatMap_ub[tupleSize];
+ offset += tupleSize;
+ break;
+ case QSGGeometry::TypeFloat:
+ ie.format = formatMap_f[tupleSize];
+ offset += sizeof(float) * tupleSize;
+ break;
+ case QSGGeometry::TypeByte:
+ case QSGGeometry::TypeInt:
+ case QSGGeometry::TypeUnsignedInt:
+ case QSGGeometry::TypeShort:
+ case QSGGeometry::TypeUnsignedShort:
+ qFatal("QSGD3D12Renderer: attribute type 0x%x is not currently supported", attrs[i].type);
+ break;
+ }
+ if (ie.format == FmtUnknown)
+ qFatal("QSGD3D12Renderer: unsupported tuple size for attribute type 0x%x", attrs[i].type);
+
+ // There is one buffer with interleaved data so the slot is always 0.
+ ie.slot = 0;
+ }
+
+ m_lastMaterialType = m->type();
+
+ // ### line width / point size ??
+
+ m_engine->setPipelineState(m_pipelineState);
+
+ if (e.iboOffset >= 0) {
+ // ### move format mapping to engine
+ QSGD3D12Format indexFormat;
+ const QSGGeometry::Type indexType = QSGGeometry::Type(g->indexType());
+ switch (indexType) {
+ case QSGGeometry::TypeUnsignedShort:
+ indexFormat = FmtUnsignedShort;
+ break;
+ case QSGGeometry::TypeUnsignedInt:
+ indexFormat = FmtUnsignedInt;
+ break;
+ default:
+ qFatal("QSGD3D12Renderer: unsupported index data type 0x%x", indexType);
+ break;
+ };
+ m_engine->queueDraw(QSGGeometry::DrawingMode(g->drawingMode()), g->indexCount(), e.vboOffset, g->sizeOfVertex(),
+ e.cboOffset,
+ e.iboOffset / e.iboStride, indexFormat);
+ } else {
+ m_engine->queueDraw(QSGGeometry::DrawingMode(g->drawingMode()), g->vertexCount(), e.vboOffset, g->sizeOfVertex(),
+ e.cboOffset);
+ }
+}
+
+QT_END_NAMESPACE
diff --git a/src/quick/scenegraph/adaptations/d3d12/qsgd3d12renderer_p.h b/src/quick/scenegraph/adaptations/d3d12/qsgd3d12renderer_p.h
new file mode 100644
index 0000000000..a0ae99a2e0
--- /dev/null
+++ b/src/quick/scenegraph/adaptations/d3d12/qsgd3d12renderer_p.h
@@ -0,0 +1,101 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtQuick module of the Qt Toolkit.
+**
+** $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 QSGD3D12RENDERER_P_H
+#define QSGD3D12RENDERER_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 <private/qsgrenderer_p.h>
+#include <QtGui/private/qdatabuffer_p.h>
+#include "qsgd3d12engine_p.h"
+
+QT_BEGIN_NAMESPACE
+
+class QSGD3D12Renderer : public QSGRenderer
+{
+public:
+ QSGD3D12Renderer(QSGRenderContext *context);
+
+ void renderScene(GLuint fboId) override;
+ void render() override;
+ void nodeChanged(QSGNode *node, QSGNode::DirtyState state) override;
+
+private:
+ void updateMatrices(QSGNode *node, QSGTransformNode *xform);
+ void buildRenderList(QSGNode *node, QSGClipNode *clip);
+ void renderElements();
+ void renderElement(int elementIndex);
+
+ struct Element {
+ QSGBasicGeometryNode *node = nullptr;
+ qint32 vboOffset = -1;
+ qint32 iboOffset = -1;
+ quint32 iboStride = 0;
+ qint32 cboOffset = -1;
+ quint32 cboSize = 0;
+ bool cboPrepared = false;
+ };
+
+ QSet<QSGNode *> m_dirtyTransformNodes;
+ QBitArray m_opaqueElements;
+ bool m_rebuild = true;
+ bool m_dirtyOpaqueElements = true;
+ QDataBuffer<quint8> m_vboData;
+ QDataBuffer<quint8> m_iboData;
+ QDataBuffer<quint8> m_cboData;
+ QDataBuffer<Element> m_renderList;
+ QSGD3D12Engine *m_engine;
+
+ QSGMaterialType *m_lastMaterialType = nullptr;
+ QSGD3D12PipelineState m_pipelineState;
+};
+
+QT_END_NAMESPACE
+
+#endif // QSGD3D12RENDERER_P_H
diff --git a/src/quick/scenegraph/adaptations/d3d12/qsgd3d12renderloop.cpp b/src/quick/scenegraph/adaptations/d3d12/qsgd3d12renderloop.cpp
new file mode 100644
index 0000000000..e0ee5e02dd
--- /dev/null
+++ b/src/quick/scenegraph/adaptations/d3d12/qsgd3d12renderloop.cpp
@@ -0,0 +1,277 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtQuick module of the Qt Toolkit.
+**
+** $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 "qsgd3d12renderloop_p.h"
+#include "qsgd3d12engine_p.h"
+#include "qsgd3d12context_p.h"
+#include "qsgd3d12rendercontext_p.h"
+#include <private/qquickwindow_p.h>
+#include <private/qquickprofiler_p.h>
+#include <QElapsedTimer>
+
+QT_BEGIN_NAMESPACE
+
+QSGD3D12RenderLoop::QSGD3D12RenderLoop()
+{
+ qDebug("new d3d12 render loop");
+ sg = new QSGD3D12Context;
+ rc = new QSGD3D12RenderContext(sg);
+}
+
+QSGD3D12RenderLoop::~QSGD3D12RenderLoop()
+{
+ delete rc;
+ delete sg;
+}
+
+void QSGD3D12RenderLoop::show(QQuickWindow *window)
+{
+ qDebug() << "show" << window;
+
+ WindowData data;
+ data.engine = new QSGD3D12Engine;
+ m_windows[window] = data;
+
+ data.engine->attachToWindow(window);
+
+ maybeUpdate(window);
+}
+
+void QSGD3D12RenderLoop::hide(QQuickWindow *window)
+{
+ qDebug() << "hide" << window;
+
+ QQuickWindowPrivate *wd = QQuickWindowPrivate::get(window);
+ wd->fireAboutToStop();
+}
+
+void QSGD3D12RenderLoop::resize(QQuickWindow *window)
+{
+ if (!window->isExposed() || window->size().isEmpty())
+ return;
+
+ qDebug() << "resize" << window;
+
+ WindowData &data(m_windows[window]);
+ if (data.engine)
+ data.engine->resize();
+}
+
+void QSGD3D12RenderLoop::windowDestroyed(QQuickWindow *window)
+{
+ qDebug() << "window destroyed" << window;
+
+ WindowData &data(m_windows[window]);
+ delete data.engine;
+ m_windows.remove(window);
+
+ hide(window);
+
+ QQuickWindowPrivate *wd = QQuickWindowPrivate::get(window);
+ wd->cleanupNodesOnShutdown();
+
+ if (m_windows.isEmpty()) {
+ rc->invalidate();
+ QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete);
+ }
+}
+
+void QSGD3D12RenderLoop::exposureChanged(QQuickWindow *window)
+{
+ qDebug() << "exposure changed" << window;
+
+ if (window->isExposed()) {
+ m_windows[window].updatePending = true;
+ renderWindow(window);
+ }
+}
+
+QImage QSGD3D12RenderLoop::grab(QQuickWindow *window)
+{
+ Q_UNUSED(window);
+ Q_UNREACHABLE();
+ return QImage();
+}
+
+void QSGD3D12RenderLoop::update(QQuickWindow *window)
+{
+ //qDebug() << "update" << window;
+
+ if (!m_windows.contains(window))
+ return;
+
+ m_windows[window].updatePending = true;
+ window->requestUpdate();
+}
+
+void QSGD3D12RenderLoop::maybeUpdate(QQuickWindow *window)
+{
+ //qDebug() << "maybeUpdate" << window;
+
+ update(window);
+}
+
+// called in response to window->requestUpdate()
+void QSGD3D12RenderLoop::handleUpdateRequest(QQuickWindow *window)
+{
+ qDebug() << "handleUpdateRequest" << window;
+
+ renderWindow(window);
+}
+
+QAnimationDriver *QSGD3D12RenderLoop::animationDriver() const
+{
+ return nullptr;
+}
+
+QSGContext *QSGD3D12RenderLoop::sceneGraphContext() const
+{
+ return sg;
+}
+
+QSGRenderContext *QSGD3D12RenderLoop::createRenderContext(QSGContext *) const
+{
+ return rc;
+}
+
+void QSGD3D12RenderLoop::releaseResources(QQuickWindow *window)
+{
+ qDebug() << "releaseResources" << window;
+}
+
+void QSGD3D12RenderLoop::postJob(QQuickWindow *window, QRunnable *job)
+{
+ Q_UNUSED(window);
+ Q_UNUSED(job);
+ Q_UNREACHABLE();
+}
+
+QSurface::SurfaceType QSGD3D12RenderLoop::windowSurfaceType() const
+{
+ return QSurface::OpenGLSurface;
+}
+
+void QSGD3D12RenderLoop::renderWindow(QQuickWindow *window)
+{
+ qDebug() << "renderWindow" << window;
+
+ QQuickWindowPrivate *wd = QQuickWindowPrivate::get(window);
+ if (!wd->isRenderable() || !m_windows.contains(window))
+ return;
+
+ WindowData &data(m_windows[window]);
+
+ const bool needsSwap = data.updatePending;
+ data.updatePending = false;
+
+ if (!data.grabOnly) {
+ wd->flushDelayedTouchEvent();
+ if (!m_windows.contains(window))
+ return;
+ }
+
+ rc->setEngine(data.engine);
+
+ QElapsedTimer renderTimer;
+ qint64 renderTime = 0, syncTime = 0, polishTime = 0;
+ const bool profileFrames = QSG_LOG_TIME_RENDERLOOP().isDebugEnabled();
+ if (profileFrames)
+ renderTimer.start();
+ Q_QUICK_SG_PROFILE_START(QQuickProfiler::SceneGraphPolishFrame);
+
+ wd->polishItems();
+
+ if (profileFrames)
+ polishTime = renderTimer.nsecsElapsed();
+ Q_QUICK_SG_PROFILE_SWITCH(QQuickProfiler::SceneGraphPolishFrame,
+ QQuickProfiler::SceneGraphRenderLoopFrame);
+
+ emit window->afterAnimating();
+
+ wd->syncSceneGraph();
+
+ if (profileFrames)
+ syncTime = renderTimer.nsecsElapsed();
+ Q_QUICK_SG_PROFILE_RECORD(QQuickProfiler::SceneGraphRenderLoopFrame);
+
+ wd->renderSceneGraph(window->size());
+
+ if (profileFrames)
+ renderTime = renderTimer.nsecsElapsed();
+ Q_QUICK_SG_PROFILE_RECORD(QQuickProfiler::SceneGraphRenderLoopFrame);
+
+ if (data.grabOnly) {
+ Q_UNREACHABLE();
+ data.grabOnly = false;
+ }
+
+ if (needsSwap && window->isVisible()) {
+ data.engine->present();
+ data.engine->waitGPU();
+ wd->fireFrameSwapped();
+ } else {
+ data.engine->waitGPU();
+ }
+
+ qint64 swapTime = 0;
+ if (profileFrames)
+ swapTime = renderTimer.nsecsElapsed();
+ Q_QUICK_SG_PROFILE_END(QQuickProfiler::SceneGraphRenderLoopFrame);
+
+ if (QSG_LOG_TIME_RENDERLOOP().isDebugEnabled()) {
+ static QTime lastFrameTime = QTime::currentTime();
+ qCDebug(QSG_LOG_TIME_RENDERLOOP,
+ "Frame rendered with 'd3d12' renderloop in %dms, polish=%d, sync=%d, render=%d, swap=%d, frameDelta=%d",
+ int(swapTime / 1000000),
+ int(polishTime / 1000000),
+ int((syncTime - polishTime) / 1000000),
+ int((renderTime - syncTime) / 1000000),
+ int((swapTime - renderTime) / 10000000),
+ int(lastFrameTime.msecsTo(QTime::currentTime())));
+ lastFrameTime = QTime::currentTime();
+ }
+
+ rc->setEngine(nullptr);
+
+ // Might have been set during syncSceneGraph()
+ if (data.updatePending)
+ maybeUpdate(window);
+}
+
+QT_END_NAMESPACE
diff --git a/src/quick/scenegraph/adaptations/d3d12/qsgd3d12renderloop_p.h b/src/quick/scenegraph/adaptations/d3d12/qsgd3d12renderloop_p.h
new file mode 100644
index 0000000000..63848ed723
--- /dev/null
+++ b/src/quick/scenegraph/adaptations/d3d12/qsgd3d12renderloop_p.h
@@ -0,0 +1,109 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtQuick module of the Qt Toolkit.
+**
+** $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 QSGD3D12RENDERLOOP_P_H
+#define QSGD3D12RENDERLOOP_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 <private/qsgrenderloop_p.h>
+
+QT_BEGIN_NAMESPACE
+
+class QSGD3D12Engine;
+class QSGD3D12Context;
+class QSGD3D12RenderContext;
+
+class QSGD3D12RenderLoop : public QSGRenderLoop
+{
+public:
+ QSGD3D12RenderLoop();
+ ~QSGD3D12RenderLoop();
+
+ void show(QQuickWindow *window) override;
+ void hide(QQuickWindow *window) override;
+ void resize(QQuickWindow *window) override;
+
+ void windowDestroyed(QQuickWindow *window) override;
+
+ void exposureChanged(QQuickWindow *window) override;
+
+ QImage grab(QQuickWindow *window) override;
+
+ void update(QQuickWindow *window) override;
+ void maybeUpdate(QQuickWindow *window) override;
+ void handleUpdateRequest(QQuickWindow *window) override;
+
+ QAnimationDriver *animationDriver() const override;
+
+ QSGContext *sceneGraphContext() const override;
+ QSGRenderContext *createRenderContext(QSGContext *) const override;
+
+ void releaseResources(QQuickWindow *window) override;
+ void postJob(QQuickWindow *window, QRunnable *job) override;
+
+ QSurface::SurfaceType windowSurfaceType() const override;
+
+private:
+ void renderWindow(QQuickWindow *window);
+
+ QSGD3D12Context *sg;
+ QSGD3D12RenderContext *rc;
+
+ struct WindowData {
+ QSGD3D12Engine *engine = nullptr;
+ bool updatePending = false;
+ bool grabOnly = false;
+ };
+
+ QHash<QQuickWindow *, WindowData> m_windows;
+};
+
+QT_END_NAMESPACE
+
+#endif // QSGD3D12RENDERLOOP_P_H
diff --git a/src/quick/scenegraph/adaptations/d3d12/shaders/shaders.pri b/src/quick/scenegraph/adaptations/d3d12/shaders/shaders.pri
new file mode 100644
index 0000000000..c3745cc682
--- /dev/null
+++ b/src/quick/scenegraph/adaptations/d3d12/shaders/shaders.pri
@@ -0,0 +1,12 @@
+vertexcolor_VSPS = $$PWD/vertexcolor.hlsl
+vertexcolor_vshader.input = vertexcolor_VSPS
+vertexcolor_vshader.header = hlsl_vs_vertexcolor.h
+vertexcolor_vshader.entry = VS_VertexColor
+vertexcolor_vshader.type = vs_5_0
+vertexcolor_pshader.input = vertexcolor_VSPS
+vertexcolor_pshader.header = hlsl_ps_vertexcolor.h
+vertexcolor_pshader.entry = PS_VertexColor
+vertexcolor_pshader.type = ps_5_0
+
+HLSL_SHADERS = vertexcolor_vshader vertexcolor_pshader
+load(hlsl_bytecode_header)
diff --git a/src/quick/scenegraph/adaptations/d3d12/shaders/vertexcolor.hlsl b/src/quick/scenegraph/adaptations/d3d12/shaders/vertexcolor.hlsl
new file mode 100644
index 0000000000..a0569bb5c1
--- /dev/null
+++ b/src/quick/scenegraph/adaptations/d3d12/shaders/vertexcolor.hlsl
@@ -0,0 +1,32 @@
+struct VSInput
+{
+ float4 position : POSITION;
+ float4 color : COLOR;
+};
+
+cbuffer ConstantBuffer : register(b0)
+{
+ float4x4 mvp;
+ float opacity;
+};
+
+struct PSInput
+{
+ float4 position : SV_POSITION;
+ float4 color : COLOR;
+};
+
+PSInput VS_VertexColor(VSInput input)
+{
+ PSInput result;
+
+ result.position = mul(mvp, input.position);
+ result.color = input.color * opacity;
+
+ return result;
+}
+
+float4 PS_VertexColor(PSInput input) : SV_TARGET
+{
+ return input.color;
+}
diff --git a/src/quick/scenegraph/coreapi/qsggeometry.cpp b/src/quick/scenegraph/coreapi/qsggeometry.cpp
index 5012f6a31b..152bb253d7 100644
--- a/src/quick/scenegraph/coreapi/qsggeometry.cpp
+++ b/src/quick/scenegraph/coreapi/qsggeometry.cpp
@@ -53,10 +53,15 @@ QT_BEGIN_NAMESPACE
QSGGeometry::Attribute QSGGeometry::Attribute::create(int attributeIndex, int tupleSize, int primitiveType, bool isPrimitive)
{
- Attribute a = { attributeIndex, tupleSize, primitiveType, isPrimitive, 0 };
+ Attribute a = { attributeIndex, tupleSize, primitiveType, isPrimitive, UNKNOWN, 0 };
return a;
}
+QSGGeometry::Attribute QSGGeometry::Attribute::createWithSemantic(int pos, int tupleSize, int type, Semantic semantic)
+{
+ Attribute a = { pos, tupleSize, type, semantic == POSITION, semantic, 0 };
+ return a;
+}
/*!
Convenience function which returns attributes to be used for 2D solid
@@ -66,7 +71,7 @@ QSGGeometry::Attribute QSGGeometry::Attribute::create(int attributeIndex, int tu
const QSGGeometry::AttributeSet &QSGGeometry::defaultAttributes_Point2D()
{
static Attribute data[] = {
- QSGGeometry::Attribute::create(0, 2, GL_FLOAT, true)
+ Attribute::createWithSemantic(0, 2, GL_FLOAT, Attribute::POSITION)
};
static AttributeSet attrs = { 1, sizeof(float) * 2, data };
return attrs;
@@ -79,8 +84,8 @@ const QSGGeometry::AttributeSet &QSGGeometry::defaultAttributes_Point2D()
const QSGGeometry::AttributeSet &QSGGeometry::defaultAttributes_TexturedPoint2D()
{
static Attribute data[] = {
- QSGGeometry::Attribute::create(0, 2, GL_FLOAT, true),
- QSGGeometry::Attribute::create(1, 2, GL_FLOAT)
+ Attribute::createWithSemantic(0, 2, GL_FLOAT, Attribute::POSITION),
+ Attribute::createWithSemantic(1, 2, GL_FLOAT, Attribute::TEXCOORD)
};
static AttributeSet attrs = { 2, sizeof(float) * 4, data };
return attrs;
@@ -93,8 +98,8 @@ const QSGGeometry::AttributeSet &QSGGeometry::defaultAttributes_TexturedPoint2D(
const QSGGeometry::AttributeSet &QSGGeometry::defaultAttributes_ColoredPoint2D()
{
static Attribute data[] = {
- QSGGeometry::Attribute::create(0, 2, GL_FLOAT, true),
- QSGGeometry::Attribute::create(1, 4, GL_UNSIGNED_BYTE)
+ Attribute::createWithSemantic(0, 2, GL_FLOAT, Attribute::POSITION),
+ Attribute::createWithSemantic(1, 4, GL_UNSIGNED_BYTE, Attribute::COLOR)
};
static AttributeSet attrs = { 2, 2 * sizeof(float) + 4 * sizeof(char), data };
return attrs;
diff --git a/src/quick/scenegraph/coreapi/qsggeometry.h b/src/quick/scenegraph/coreapi/qsggeometry.h
index 5773b6abd1..6b1e3e3f4e 100644
--- a/src/quick/scenegraph/coreapi/qsggeometry.h
+++ b/src/quick/scenegraph/coreapi/qsggeometry.h
@@ -54,15 +54,25 @@ public:
struct Q_QUICK_EXPORT Attribute
{
+ enum Semantic {
+ UNKNOWN,
+ POSITION,
+ COLOR,
+ TEXCOORD
+ };
+
int position;
int tupleSize;
int type;
uint isVertexCoordinate : 1;
- uint reserved : 31;
+ Semantic semantic : 4;
+
+ uint reserved : 27;
static Attribute create(int pos, int tupleSize, int primitiveType, bool isPosition = false);
+ static Attribute createWithSemantic(int pos, int tupleSize, int type, Semantic semantic);
};
struct AttributeSet {
@@ -104,12 +114,35 @@ public:
StaticPattern = 3
};
+ // Equivalents to GL_* drawing modes.
+ enum DrawingMode {
+ DrawPoints = 0x0000,
+ DrawLines = 0x0001,
+ DrawLineLoop = 0x0002,
+ DrawLineStrip = 0x0003,
+ DrawTriangles = 0x0004,
+ DrawTriangleStrip = 0x0005,
+ DrawTriangleFan = 0x0006
+ };
+
+ // Equivalents to GL_BYTE and similar type constants.
+ enum Type {
+ TypeByte = 0x1400,
+ TypeUnsignedByte = 0x1401,
+ TypeShort = 0x1402,
+ TypeUnsignedShort = 0x1403,
+ TypeInt = 0x1404,
+ TypeUnsignedInt = 0x1405,
+ TypeFloat = 0x1406
+ };
+
QSGGeometry(const QSGGeometry::AttributeSet &attribs,
int vertexCount,
int indexCount = 0,
- int indexType = GL_UNSIGNED_SHORT);
+ int indexType = TypeUnsignedShort);
virtual ~QSGGeometry();
+ // ### Qt 6: GL types to be removed from the public API
void setDrawingMode(GLenum mode);
inline GLenum drawingMode() const { return m_drawing_mode; }
@@ -187,25 +220,25 @@ private:
inline uint *QSGGeometry::indexDataAsUInt()
{
- Q_ASSERT(m_index_type == GL_UNSIGNED_INT);
+ Q_ASSERT(m_index_type == TypeUnsignedInt);
return static_cast<uint *>(indexData());
}
inline quint16 *QSGGeometry::indexDataAsUShort()
{
- Q_ASSERT(m_index_type == GL_UNSIGNED_SHORT);
+ Q_ASSERT(m_index_type == TypeUnsignedShort);
return static_cast<quint16 *>(indexData());
}
inline const uint *QSGGeometry::indexDataAsUInt() const
{
- Q_ASSERT(m_index_type == GL_UNSIGNED_INT);
+ Q_ASSERT(m_index_type == TypeUnsignedInt);
return static_cast<const uint *>(indexData());
}
inline const quint16 *QSGGeometry::indexDataAsUShort() const
{
- Q_ASSERT(m_index_type == GL_UNSIGNED_SHORT);
+ Q_ASSERT(m_index_type == TypeUnsignedShort);
return static_cast<const quint16 *>(indexData());
}
@@ -214,7 +247,7 @@ inline QSGGeometry::Point2D *QSGGeometry::vertexDataAsPoint2D()
Q_ASSERT(m_attributes.count == 1);
Q_ASSERT(m_attributes.stride == 2 * sizeof(float));
Q_ASSERT(m_attributes.attributes[0].tupleSize == 2);
- Q_ASSERT(m_attributes.attributes[0].type == GL_FLOAT);
+ Q_ASSERT(m_attributes.attributes[0].type == TypeFloat);
Q_ASSERT(m_attributes.attributes[0].position == 0);
return static_cast<Point2D *>(m_data);
}
@@ -225,10 +258,10 @@ inline QSGGeometry::TexturedPoint2D *QSGGeometry::vertexDataAsTexturedPoint2D()
Q_ASSERT(m_attributes.stride == 4 * sizeof(float));
Q_ASSERT(m_attributes.attributes[0].position == 0);
Q_ASSERT(m_attributes.attributes[0].tupleSize == 2);
- Q_ASSERT(m_attributes.attributes[0].type == GL_FLOAT);
+ Q_ASSERT(m_attributes.attributes[0].type == TypeFloat);
Q_ASSERT(m_attributes.attributes[1].position == 1);
Q_ASSERT(m_attributes.attributes[1].tupleSize == 2);
- Q_ASSERT(m_attributes.attributes[1].type == GL_FLOAT);
+ Q_ASSERT(m_attributes.attributes[1].type == TypeFloat);
return static_cast<TexturedPoint2D *>(m_data);
}
@@ -238,10 +271,10 @@ inline QSGGeometry::ColoredPoint2D *QSGGeometry::vertexDataAsColoredPoint2D()
Q_ASSERT(m_attributes.stride == 2 * sizeof(float) + 4 * sizeof(char));
Q_ASSERT(m_attributes.attributes[0].position == 0);
Q_ASSERT(m_attributes.attributes[0].tupleSize == 2);
- Q_ASSERT(m_attributes.attributes[0].type == GL_FLOAT);
+ Q_ASSERT(m_attributes.attributes[0].type == TypeFloat);
Q_ASSERT(m_attributes.attributes[1].position == 1);
Q_ASSERT(m_attributes.attributes[1].tupleSize == 4);
- Q_ASSERT(m_attributes.attributes[1].type == GL_UNSIGNED_BYTE);
+ Q_ASSERT(m_attributes.attributes[1].type == TypeUnsignedByte);
return static_cast<ColoredPoint2D *>(m_data);
}
@@ -250,7 +283,7 @@ inline const QSGGeometry::Point2D *QSGGeometry::vertexDataAsPoint2D() const
Q_ASSERT(m_attributes.count == 1);
Q_ASSERT(m_attributes.stride == 2 * sizeof(float));
Q_ASSERT(m_attributes.attributes[0].tupleSize == 2);
- Q_ASSERT(m_attributes.attributes[0].type == GL_FLOAT);
+ Q_ASSERT(m_attributes.attributes[0].type == TypeFloat);
Q_ASSERT(m_attributes.attributes[0].position == 0);
return static_cast<const Point2D *>(m_data);
}
@@ -261,10 +294,10 @@ inline const QSGGeometry::TexturedPoint2D *QSGGeometry::vertexDataAsTexturedPoin
Q_ASSERT(m_attributes.stride == 4 * sizeof(float));
Q_ASSERT(m_attributes.attributes[0].position == 0);
Q_ASSERT(m_attributes.attributes[0].tupleSize == 2);
- Q_ASSERT(m_attributes.attributes[0].type == GL_FLOAT);
+ Q_ASSERT(m_attributes.attributes[0].type == TypeFloat);
Q_ASSERT(m_attributes.attributes[1].position == 1);
Q_ASSERT(m_attributes.attributes[1].tupleSize == 2);
- Q_ASSERT(m_attributes.attributes[1].type == GL_FLOAT);
+ Q_ASSERT(m_attributes.attributes[1].type == TypeFloat);
return static_cast<const TexturedPoint2D *>(m_data);
}
@@ -274,18 +307,18 @@ inline const QSGGeometry::ColoredPoint2D *QSGGeometry::vertexDataAsColoredPoint2
Q_ASSERT(m_attributes.stride == 2 * sizeof(float) + 4 * sizeof(char));
Q_ASSERT(m_attributes.attributes[0].position == 0);
Q_ASSERT(m_attributes.attributes[0].tupleSize == 2);
- Q_ASSERT(m_attributes.attributes[0].type == GL_FLOAT);
+ Q_ASSERT(m_attributes.attributes[0].type == TypeFloat);
Q_ASSERT(m_attributes.attributes[1].position == 1);
Q_ASSERT(m_attributes.attributes[1].tupleSize == 4);
- Q_ASSERT(m_attributes.attributes[1].type == GL_UNSIGNED_BYTE);
+ Q_ASSERT(m_attributes.attributes[1].type == TypeUnsignedByte);
return static_cast<const ColoredPoint2D *>(m_data);
}
int QSGGeometry::sizeOfIndex() const
{
- if (m_index_type == GL_UNSIGNED_SHORT) return 2;
- else if (m_index_type == GL_UNSIGNED_BYTE) return 1;
- else if (m_index_type == GL_UNSIGNED_INT) return 4;
+ if (m_index_type == TypeUnsignedShort) return 2;
+ else if (m_index_type == TypeUnsignedByte) return 1;
+ else if (m_index_type == TypeUnsignedInt) return 4;
return 0;
}
diff --git a/src/quick/scenegraph/coreapi/qsgnode.h b/src/quick/scenegraph/coreapi/qsgnode.h
index d294e6f3ca..144f42da1c 100644
--- a/src/quick/scenegraph/coreapi/qsgnode.h
+++ b/src/quick/scenegraph/coreapi/qsgnode.h
@@ -204,6 +204,9 @@ public:
const QMatrix4x4 *matrix() const { return m_matrix; }
const QSGClipNode *clipList() const { return m_clip_list; }
+ void setMatrix(const QMatrix4x4 *matrix) { m_matrix = matrix; }
+ void setClipList(const QSGClipNode *clipList) { m_clip_list = clipList; }
+
protected:
QSGBasicGeometryNode(NodeType type);
QSGBasicGeometryNode(QSGBasicGeometryNodePrivate &dd, NodeType type);
diff --git a/src/quick/scenegraph/qsgcontextplugin.cpp b/src/quick/scenegraph/qsgcontextplugin.cpp
index bc5d2a0a0d..87b4ed5b8e 100644
--- a/src/quick/scenegraph/qsgcontextplugin.cpp
+++ b/src/quick/scenegraph/qsgcontextplugin.cpp
@@ -45,6 +45,9 @@
// Built-in adaptations
#include <QtQuick/private/qsgdummyadaptation_p.h>
+#ifdef QSG_D3D12
+#include <QtQuick/private/qsgd3d12adaptation_p.h>
+#endif
QT_BEGIN_NAMESPACE
@@ -81,6 +84,9 @@ QSGAdaptionBackendData::QSGAdaptionBackendData()
{
// Fill in the table with the built-in adaptations.
builtIns.append(new QSGDummyAdaptation);
+#ifdef QSG_D3D12
+ builtIns.append(new QSGD3D12Adaptation);
+#endif
}
Q_GLOBAL_STATIC(QSGAdaptionBackendData, qsg_adaptation_data)
diff --git a/src/quick/scenegraph/qsgdefaultrectanglenode.cpp b/src/quick/scenegraph/qsgdefaultrectanglenode.cpp
index 551575573e..9a219ece63 100644
--- a/src/quick/scenegraph/qsgdefaultrectanglenode.cpp
+++ b/src/quick/scenegraph/qsgdefaultrectanglenode.cpp
@@ -96,9 +96,9 @@ namespace
const QSGGeometry::AttributeSet &smoothAttributeSet()
{
static QSGGeometry::Attribute data[] = {
- QSGGeometry::Attribute::create(0, 2, GL_FLOAT, true),
- QSGGeometry::Attribute::create(1, 4, GL_UNSIGNED_BYTE, false),
- QSGGeometry::Attribute::create(2, 2, GL_FLOAT, false)
+ QSGGeometry::Attribute::create(0, 2, QSGGeometry::TypeFloat, true),
+ QSGGeometry::Attribute::create(1, 4, QSGGeometry::TypeUnsignedByte, false),
+ QSGGeometry::Attribute::create(2, 2, QSGGeometry::TypeFloat, false)
};
static QSGGeometry::AttributeSet attrs = { 3, sizeof(SmoothVertex), data };
return attrs;
@@ -184,7 +184,7 @@ QSGMaterialShader *QSGSmoothColorMaterial::createShader() const
}
-QSGDefaultRectangleNode::QSGDefaultRectangleNode()
+QSGDefaultNoMaterialRectangleNode::QSGDefaultNoMaterialRectangleNode()
: m_radius(0)
, m_pen_width(0)
, m_aligned(true)
@@ -194,14 +194,13 @@ QSGDefaultRectangleNode::QSGDefaultRectangleNode()
, m_geometry(QSGGeometry::defaultAttributes_ColoredPoint2D(), 0)
{
setGeometry(&m_geometry);
- setMaterial(&m_material);
#ifdef QSG_RUNTIME_DESCRIPTION
qsgnode_set_description(this, QLatin1String("rectangle"));
#endif
}
-void QSGDefaultRectangleNode::setRect(const QRectF &rect)
+void QSGDefaultNoMaterialRectangleNode::setRect(const QRectF &rect)
{
if (rect == m_rect)
return;
@@ -209,7 +208,7 @@ void QSGDefaultRectangleNode::setRect(const QRectF &rect)
m_dirty_geometry = true;
}
-void QSGDefaultRectangleNode::setColor(const QColor &color)
+void QSGDefaultNoMaterialRectangleNode::setColor(const QColor &color)
{
if (color == m_color)
return;
@@ -218,7 +217,7 @@ void QSGDefaultRectangleNode::setColor(const QColor &color)
m_dirty_geometry = true;
}
-void QSGDefaultRectangleNode::setPenColor(const QColor &color)
+void QSGDefaultNoMaterialRectangleNode::setPenColor(const QColor &color)
{
if (color == m_border_color)
return;
@@ -227,7 +226,7 @@ void QSGDefaultRectangleNode::setPenColor(const QColor &color)
m_dirty_geometry = true;
}
-void QSGDefaultRectangleNode::setPenWidth(qreal width)
+void QSGDefaultNoMaterialRectangleNode::setPenWidth(qreal width)
{
if (width == m_pen_width)
return;
@@ -236,7 +235,7 @@ void QSGDefaultRectangleNode::setPenWidth(qreal width)
}
-void QSGDefaultRectangleNode::setGradientStops(const QGradientStops &stops)
+void QSGDefaultNoMaterialRectangleNode::setGradientStops(const QGradientStops &stops)
{
if (stops.constData() == m_gradient_stops.constData())
return;
@@ -249,7 +248,7 @@ void QSGDefaultRectangleNode::setGradientStops(const QGradientStops &stops)
m_dirty_geometry = true;
}
-void QSGDefaultRectangleNode::setRadius(qreal radius)
+void QSGDefaultNoMaterialRectangleNode::setRadius(qreal radius)
{
if (radius == m_radius)
return;
@@ -257,24 +256,23 @@ void QSGDefaultRectangleNode::setRadius(qreal radius)
m_dirty_geometry = true;
}
-void QSGDefaultRectangleNode::setAntialiasing(bool antialiasing)
+void QSGDefaultNoMaterialRectangleNode::setAntialiasing(bool antialiasing)
{
if (antialiasing == m_antialiasing)
return;
m_antialiasing = antialiasing;
if (m_antialiasing) {
- setMaterial(&m_smoothMaterial);
setGeometry(new QSGGeometry(smoothAttributeSet(), 0));
setFlag(OwnsGeometry, true);
} else {
- setMaterial(&m_material);
setGeometry(&m_geometry);
setFlag(OwnsGeometry, false);
}
+ updateMaterialAntialiasing();
m_dirty_geometry = true;
}
-void QSGDefaultRectangleNode::setAligned(bool aligned)
+void QSGDefaultNoMaterialRectangleNode::setAligned(bool aligned)
{
if (aligned == m_aligned)
return;
@@ -282,30 +280,19 @@ void QSGDefaultRectangleNode::setAligned(bool aligned)
m_dirty_geometry = true;
}
-void QSGDefaultRectangleNode::update()
+void QSGDefaultNoMaterialRectangleNode::update()
{
if (m_dirty_geometry) {
updateGeometry();
m_dirty_geometry = false;
QSGNode::DirtyState state = QSGNode::DirtyGeometry;
- // smoothed material is always blended, so no change in material state
- if (material() == &m_material) {
- bool wasBlending = (m_material.flags() & QSGMaterial::Blending);
- bool isBlending = (m_gradient_stops.size() > 0 && !m_gradient_is_opaque)
- || (m_color.alpha() < 255 && m_color.alpha() != 0)
- || (m_pen_width > 0 && m_border_color.alpha() < 255);
- if (wasBlending != isBlending) {
- m_material.setFlag(QSGMaterial::Blending, isBlending);
- state |= QSGNode::DirtyMaterial;
- }
- }
-
+ updateMaterialBlending(&state);
markDirty(state);
}
}
-void QSGDefaultRectangleNode::updateGeometry()
+void QSGDefaultNoMaterialRectangleNode::updateGeometry()
{
float width = float(m_rect.width());
float height = float(m_rect.height());
@@ -315,7 +302,7 @@ void QSGDefaultRectangleNode::updateGeometry()
penWidth = qRound(penWidth);
QSGGeometry *g = geometry();
- g->setDrawingMode(GL_TRIANGLE_STRIP);
+ g->setDrawingMode(QSGGeometry::DrawTriangleStrip);
int vertexStride = g->sizeOfVertex();
union {
@@ -782,5 +769,32 @@ void QSGDefaultRectangleNode::updateGeometry()
}
}
+QSGDefaultRectangleNode::QSGDefaultRectangleNode()
+{
+ setMaterial(&m_material);
+}
+
+void QSGDefaultRectangleNode::updateMaterialAntialiasing()
+{
+ if (m_antialiasing)
+ setMaterial(&m_smoothMaterial);
+ else
+ setMaterial(&m_material);
+}
+
+void QSGDefaultRectangleNode::updateMaterialBlending(QSGNode::DirtyState *state)
+{
+ // smoothed material is always blended, so no change in material state
+ if (material() == &m_material) {
+ bool wasBlending = (m_material.flags() & QSGMaterial::Blending);
+ bool isBlending = (m_gradient_stops.size() > 0 && !m_gradient_is_opaque)
+ || (m_color.alpha() < 255 && m_color.alpha() != 0)
+ || (m_pen_width > 0 && m_border_color.alpha() < 255);
+ if (wasBlending != isBlending) {
+ m_material.setFlag(QSGMaterial::Blending, isBlending);
+ *state |= QSGNode::DirtyMaterial;
+ }
+ }
+}
QT_END_NAMESPACE
diff --git a/src/quick/scenegraph/qsgdefaultrectanglenode_p.h b/src/quick/scenegraph/qsgdefaultrectanglenode_p.h
index 4cfe921127..61bf4fb4ae 100644
--- a/src/quick/scenegraph/qsgdefaultrectanglenode_p.h
+++ b/src/quick/scenegraph/qsgdefaultrectanglenode_p.h
@@ -68,32 +68,32 @@ public:
int compare(const QSGMaterial *other) const;
protected:
- virtual QSGMaterialType *type() const;
- virtual QSGMaterialShader *createShader() const;
+ QSGMaterialType *type() const override;
+ QSGMaterialShader *createShader() const override;
};
-class Q_QUICK_PRIVATE_EXPORT QSGDefaultRectangleNode : public QSGRectangleNode
+class Q_QUICK_PRIVATE_EXPORT QSGDefaultNoMaterialRectangleNode : public QSGRectangleNode
{
public:
- QSGDefaultRectangleNode();
+ QSGDefaultNoMaterialRectangleNode();
+
+ void setRect(const QRectF &rect) override;
+ void setColor(const QColor &color) override;
+ void setPenColor(const QColor &color) override;
+ void setPenWidth(qreal width) override;
+ void setGradientStops(const QGradientStops &stops) override;
+ void setRadius(qreal radius) override;
+ void setAntialiasing(bool antialiasing) override;
+ void setAligned(bool aligned) override;
+ void update() override;
- virtual void setRect(const QRectF &rect);
- virtual void setColor(const QColor &color);
- virtual void setPenColor(const QColor &color);
- virtual void setPenWidth(qreal width);
- virtual void setGradientStops(const QGradientStops &stops);
- virtual void setRadius(qreal radius);
- virtual void setAntialiasing(bool antialiasing);
- virtual void setAligned(bool aligned);
- virtual void update();
+protected:
+ virtual void updateMaterialAntialiasing() = 0;
+ virtual void updateMaterialBlending(QSGNode::DirtyState *state) = 0;
-private:
void updateGeometry();
void updateGradientTexture();
- QSGVertexColorMaterial m_material;
- QSGSmoothColorMaterial m_smoothMaterial;
-
QRectF m_rect;
QGradientStops m_gradient_stops;
QColor m_color;
@@ -109,6 +109,19 @@ private:
QSGGeometry m_geometry;
};
+class Q_QUICK_PRIVATE_EXPORT QSGDefaultRectangleNode : public QSGDefaultNoMaterialRectangleNode
+{
+public:
+ QSGDefaultRectangleNode();
+
+private:
+ void updateMaterialAntialiasing() override;
+ void updateMaterialBlending(QSGNode::DirtyState *state) override;
+
+ QSGVertexColorMaterial m_material;
+ QSGSmoothColorMaterial m_smoothMaterial;
+};
+
QT_END_NAMESPACE
#endif
diff --git a/tests/manual/nodetypes/main.qml b/tests/manual/nodetypes/main.qml
new file mode 100644
index 0000000000..c4d0a5266d
--- /dev/null
+++ b/tests/manual/nodetypes/main.qml
@@ -0,0 +1,87 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the demonstration applications of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:BSD$
+** You may use this file under the terms of the BSD license as follows:
+**
+** "Redistribution and use in source and binary forms, with or without
+** modification, are permitted provided that the following conditions are
+** met:
+** * Redistributions of source code must retain the above copyright
+** notice, this list of conditions and the following disclaimer.
+** * Redistributions in binary form must reproduce the above copyright
+** notice, this list of conditions and the following disclaimer in
+** the documentation and/or other materials provided with the
+** distribution.
+** * Neither the name of The Qt Company Ltd nor the names of its
+** contributors may be used to endorse or promote products derived
+** from this software without specific prior written permission.
+**
+**
+** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+import QtQuick 2.0
+
+Item {
+ Rectangle {
+ width: 100
+ height: 100
+ anchors.centerIn: parent
+ color: "red"
+ NumberAnimation on rotation { from: 0; to: 360; duration: 2000; loops: Animation.Infinite; }
+ }
+
+ Rectangle {
+ color: "green"
+ width: 100
+ height: 200
+ x: 0
+ y: 0
+
+ NumberAnimation on x {
+ from: 0
+ to: 300
+ duration: 10000
+ }
+ }
+
+ Rectangle {
+ color: "blue"
+ width: 200
+ height: 100
+ x: 100
+ y: 300
+
+ SequentialAnimation on y {
+ loops: Animation.Infinite
+ NumberAnimation {
+ from: 300
+ to: 500
+ duration: 7000
+ }
+ NumberAnimation {
+ from: 500
+ to: 300
+ duration: 3000
+ }
+ }
+ }
+}
diff --git a/tests/manual/nodetypes/nodetypes.cpp b/tests/manual/nodetypes/nodetypes.cpp
new file mode 100644
index 0000000000..506a202ec7
--- /dev/null
+++ b/tests/manual/nodetypes/nodetypes.cpp
@@ -0,0 +1,57 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the demonstration applications of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:BSD$
+** You may use this file under the terms of the BSD license as follows:
+**
+** "Redistribution and use in source and binary forms, with or without
+** modification, are permitted provided that the following conditions are
+** met:
+** * Redistributions of source code must retain the above copyright
+** notice, this list of conditions and the following disclaimer.
+** * Redistributions in binary form must reproduce the above copyright
+** notice, this list of conditions and the following disclaimer in
+** the documentation and/or other materials provided with the
+** distribution.
+** * Neither the name of The Qt Company Ltd nor the names of its
+** contributors may be used to endorse or promote products derived
+** from this software without specific prior written permission.
+**
+**
+** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include <QGuiApplication>
+#include <QQuickView>
+
+int main(int argc, char **argv)
+{
+ qputenv("QT_QUICK_BACKEND", "d3d12");
+
+ QGuiApplication app(argc, argv);
+
+ QQuickView view;
+ view.setResizeMode(QQuickView::SizeRootObjectToView);
+ view.resize(1024, 768);
+ view.setSource(QUrl("qrc:/main.qml"));
+ view.show();
+
+ return app.exec();
+}
diff --git a/tests/manual/nodetypes/nodetypes.pro b/tests/manual/nodetypes/nodetypes.pro
new file mode 100644
index 0000000000..c0b258d9ae
--- /dev/null
+++ b/tests/manual/nodetypes/nodetypes.pro
@@ -0,0 +1,7 @@
+QT += qml quick
+
+SOURCES += nodetypes.cpp
+
+RESOURCES += nodetypes.qrc
+
+OTHER_FILES += main.qml
diff --git a/tests/manual/nodetypes/nodetypes.qrc b/tests/manual/nodetypes/nodetypes.qrc
new file mode 100644
index 0000000000..5f6483ac33
--- /dev/null
+++ b/tests/manual/nodetypes/nodetypes.qrc
@@ -0,0 +1,5 @@
+<RCC>
+ <qresource prefix="/">
+ <file>main.qml</file>
+ </qresource>
+</RCC>