diff options
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> |