From 046279a2c99f7bb7076d0b93fae5a41aca960ec7 Mon Sep 17 00:00:00 2001 From: Ulf Hermann Date: Wed, 22 May 2019 18:18:05 +0200 Subject: Register QRegularExpressionValidator in QtQuick This should replace QRegExpValidator. RegExpValidator is deprecated now. Task-nunber: QTBUG-72588 Change-Id: I072deb382914fd322ce192509782b241824ccd7b Reviewed-by: Shawn Rutledge --- examples/quick/localstorage/localstorage/Header.qml | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) (limited to 'examples') diff --git a/examples/quick/localstorage/localstorage/Header.qml b/examples/quick/localstorage/localstorage/Header.qml index 18f51c1b6e..b3807f6e40 100644 --- a/examples/quick/localstorage/localstorage/Header.qml +++ b/examples/quick/localstorage/localstorage/Header.qml @@ -48,7 +48,7 @@ ** $QT_END_LICENSE$ ** ****************************************************************************/ -import QtQuick 2.7 +import QtQuick 2.14 import QtQuick.Window 2.0 import QtQuick.LocalStorage 2.0 import "Database.js" as JS @@ -143,8 +143,8 @@ Item { font.pixelSize: 22 activeFocusOnPress: true activeFocusOnTab: true - validator: RegExpValidator { - regExp: /[0-9/,:.]+/ + validator: RegularExpressionValidator { + regularExpression: /[0-9/,:.]+/ } onEditingFinished: { if (dateInput.text == "") { @@ -174,8 +174,8 @@ Item { font.pixelSize: 22 activeFocusOnPress: true activeFocusOnTab: true - validator: RegExpValidator { - regExp: /\d{1,3}/ + validator: RegularExpressionValidator { + regularExpression: /\d{1,3}/ } onEditingFinished: { if (distInput.text == "") { -- cgit v1.2.3 From b1f238568214e6587b829d6695677e55a99b1d40 Mon Sep 17 00:00:00 2001 From: Fabian Kosmale Date: Tue, 25 Jun 2019 08:07:37 +0200 Subject: Discourage having AsyncImageResponse inherit from QRunnable As the AsyncImageResponse is owned by the qml engine, it should ideally live in the same thread. Else, when we call destroyLater() on it in the qml engine, we can run into various race coditions. see also commit c8827b444c23656f67feee2e3ebd4f2868ab3db7 Change-Id: I6e21ca4527281445e5f6df082f60b60838bb2eac Reviewed-by: Ulf Hermann --- .../imageresponseprovider.cpp | 71 ++++++++++++++-------- 1 file changed, 45 insertions(+), 26 deletions(-) (limited to 'examples') diff --git a/examples/quick/imageresponseprovider/imageresponseprovider.cpp b/examples/quick/imageresponseprovider/imageresponseprovider.cpp index 4f7c12b1d8..32510dbec8 100644 --- a/examples/quick/imageresponseprovider/imageresponseprovider.cpp +++ b/examples/quick/imageresponseprovider/imageresponseprovider.cpp @@ -57,40 +57,60 @@ #include #include -class AsyncImageResponse : public QQuickImageResponse, public QRunnable +class AsyncImageResponseRunnable : public QObject, public QRunnable +{ + Q_OBJECT + +signals: + void done(QImage image); + +public: + AsyncImageResponseRunnable(const QString &id, const QSize &requestedSize) + : m_id(id), m_requestedSize(requestedSize) {} + + void run() override + { + auto image = QImage(50, 50, QImage::Format_RGB32); + if (m_id == QLatin1String("slow")) { + qDebug() << "Slow, red, sleeping for 5 seconds"; + QThread::sleep(5); + image.fill(Qt::red); + } else { + qDebug() << "Fast, blue, sleeping for 1 second"; + QThread::sleep(1); + image.fill(Qt::blue); + } + if (m_requestedSize.isValid()) + image = image.scaled(m_requestedSize); + + emit done(image); + } + +private: + QString m_id; + QSize m_requestedSize; +}; + +class AsyncImageResponse : public QQuickImageResponse { public: - AsyncImageResponse(const QString &id, const QSize &requestedSize) - : m_id(id), m_requestedSize(requestedSize) + AsyncImageResponse(const QString &id, const QSize &requestedSize, QThreadPool *pool) { - setAutoDelete(false); + auto runnable = new AsyncImageResponseRunnable(id, requestedSize); + connect(runnable, &AsyncImageResponseRunnable::done, this, &AsyncImageResponse::handleDone); + pool->start(runnable); } - QQuickTextureFactory *textureFactory() const override - { - return QQuickTextureFactory::textureFactoryForImage(m_image); + void handleDone(QImage image) { + m_image = image; + emit finished(); } - void run() override + QQuickTextureFactory *textureFactory() const override { - m_image = QImage(50, 50, QImage::Format_RGB32); - if (m_id == "slow") { - qDebug() << "Slow, red, sleeping for 5 seconds"; - QThread::sleep(5); - m_image.fill(Qt::red); - } else { - qDebug() << "Fast, blue, sleeping for 1 second"; - QThread::sleep(1); - m_image.fill(Qt::blue); - } - if (m_requestedSize.isValid()) - m_image = m_image.scaled(m_requestedSize); - - emit finished(); + return QQuickTextureFactory::textureFactoryForImage(m_image); } - QString m_id; - QSize m_requestedSize; QImage m_image; }; @@ -99,8 +119,7 @@ class AsyncImageProvider : public QQuickAsyncImageProvider public: QQuickImageResponse *requestImageResponse(const QString &id, const QSize &requestedSize) override { - AsyncImageResponse *response = new AsyncImageResponse(id, requestedSize); - pool.start(response); + AsyncImageResponse *response = new AsyncImageResponse(id, requestedSize, &pool); return response; } -- cgit v1.2.3 From 11344a9070974db6ce21af3a377434f621513053 Mon Sep 17 00:00:00 2001 From: Marc Mutz Date: Tue, 2 Jul 2019 07:52:31 +0200 Subject: Port from QLineEdit::getTextMargins() to textMargins() getTextMargins() will be deprecated soon. Change-Id: I328f87553585fd1450c85191422013ebc0c8f7f8 Reviewed-by: Lars Knoll --- .../qml/referenceexamples/extended/lineedit.cpp | 48 +++++++++------------- 1 file changed, 20 insertions(+), 28 deletions(-) (limited to 'examples') diff --git a/examples/qml/referenceexamples/extended/lineedit.cpp b/examples/qml/referenceexamples/extended/lineedit.cpp index f2f5ec0efc..777e15db07 100644 --- a/examples/qml/referenceexamples/extended/lineedit.cpp +++ b/examples/qml/referenceexamples/extended/lineedit.cpp @@ -57,58 +57,50 @@ LineEditExtension::LineEditExtension(QObject *object) int LineEditExtension::leftMargin() const { - int l, r, t, b; - m_lineedit->getTextMargins(&l, &t, &r, &b); - return l; + return m_lineedit->textMargins().left(); } -void LineEditExtension::setLeftMargin(int m) +void LineEditExtension::setLeftMargin(int l) { - int l, r, t, b; - m_lineedit->getTextMargins(&l, &t, &r, &b); - m_lineedit->setTextMargins(m, t, r, b); + QMargins m = m_lineedit->textMargins(); + m.setLeft(l); + m_lineedit->setTextMargins(m); } int LineEditExtension::rightMargin() const { - int l, r, t, b; - m_lineedit->getTextMargins(&l, &t, &r, &b); - return r; + return m_lineedit->textMargins().right(); } -void LineEditExtension::setRightMargin(int m) +void LineEditExtension::setRightMargin(int r) { - int l, r, t, b; - m_lineedit->getTextMargins(&l, &t, &r, &b); - m_lineedit->setTextMargins(l, t, m, b); + QMargins m = m_lineedit->textMargins(); + m.setRight(r); + m_lineedit->setTextMargins(m); } int LineEditExtension::topMargin() const { - int l, r, t, b; - m_lineedit->getTextMargins(&l, &t, &r, &b); - return t; + return m_lineedit->textMargins().top(); } -void LineEditExtension::setTopMargin(int m) +void LineEditExtension::setTopMargin(int t) { - int l, r, t, b; - m_lineedit->getTextMargins(&l, &t, &r, &b); - m_lineedit->setTextMargins(l, m, r, b); + QMargins m = m_lineedit->textMargins(); + m.setTop(t); + m_lineedit->setTextMargins(m); } int LineEditExtension::bottomMargin() const { - int l, r, t, b; - m_lineedit->getTextMargins(&l, &t, &r, &b); - return b; + return m_lineedit->textMargins().bottom(); } -void LineEditExtension::setBottomMargin(int m) +void LineEditExtension::setBottomMargin(int b) { - int l, r, t, b; - m_lineedit->getTextMargins(&l, &t, &r, &b); - m_lineedit->setTextMargins(l, t, r, m); + QMargins m = m_lineedit->textMargins(); + m.setBottom(b); + m_lineedit->setTextMargins(m); } -- cgit v1.2.3 From 341ab7708049b1a3f559b76f16393e688951a938 Mon Sep 17 00:00:00 2001 From: Laszlo Agocs Date: Tue, 23 Apr 2019 09:40:59 +0200 Subject: Add the graphics api independent scenegraph port Opt in via environment variables: QSG_RHI=1 -> enable using QRhi instead of GL QSG_RHI_BACKEND -> set to vulkan, metal, d3d11, gl to override the default (the default is d3d11 on Windows, metal on Mac, gl elsewhere) Or force a given rhi backend via the existing QQuickWindow::setSceneGraphBackend(). Otherwise the default behavior is the same as before, the rhi code path is never active by default. -no-opengl builds are supported in the sense that they work and default to the software backend. However, the rhi code path cannot currently be used in such builds, even though QRhi from qtbase is fully functional with Vulkan, D3D, or Metal even when qtbase was configured with -no-opengl. This cannot be utilized by Quick atm due to OpenGL usage being all over the place in the sources corresponding to the default backend, and those host the rhi code path as well. This will be cleaned up hopefully in Qt 6, with the removal all direct OpenGL usage. Other env.vars.: QSG_RHI_DEBUG_LAYER=1 -> enable D3D debug or Vulkan validation layer (assuming the system is set up for this) QSG_RHI_SHADEREFFECT_DEBUG=1 -> print stuff from ShaderEffect QSG_SAMPLES=1,2,4,... -> MSAA sample count (but QSurfaceFormat works too) QT_D3D_ADAPTER_INDEX=0,1,... -> D3D adapter index QT_VK_PHYSICAL_DEVICE_INDEX=0,1,... -> Vulkan physical device index QSG_RHI_UINT32_INDEX=1 -> always use uint index data (both merged/unmerged, convert when needed - with some rhi backends this is implicit) QSG_RENDER_LOOP -> to override the render loop as usual. The default with RHI is threaded for Metal, threaded for Vulkan on Windows, basic for Vulkan on Linux and Android (to be checked later), while the existing rules apply for OpenGL. Not supported when running with QRhi: - particles - compressed atlases (though this is transparent to the apps) - QSGRenderNode - QQuickRenderControl - QQuickFramebufferObject - certain QQuickWindow functionality that depends directly on OpenGL - anisotropic filtering for textures - native text may lack some gamma correction - QSGEngine applicability unclear - some QML profiler logs may be incorrect or irrelevant Change-Id: I7822e99ad79e342e4166275da6e9e66498d76521 Reviewed-by: Lars Knoll --- .../scenegraph/d3d11underqml/d3d11squircle.cpp | 426 +++++++++++++++++++++ .../quick/scenegraph/d3d11underqml/d3d11squircle.h | 86 +++++ .../scenegraph/d3d11underqml/d3d11underqml.pro | 10 + .../scenegraph/d3d11underqml/d3d11underqml.qrc | 7 + examples/quick/scenegraph/d3d11underqml/main.cpp | 69 ++++ examples/quick/scenegraph/d3d11underqml/main.qml | 89 +++++ .../quick/scenegraph/d3d11underqml/squircle.frag | 35 ++ .../quick/scenegraph/d3d11underqml/squircle.vert | 30 ++ examples/quick/scenegraph/metalunderqml/main.cpp | 69 ++++ examples/quick/scenegraph/metalunderqml/main.qml | 89 +++++ .../quick/scenegraph/metalunderqml/metalsquircle.h | 86 +++++ .../scenegraph/metalunderqml/metalsquircle.mm | 364 ++++++++++++++++++ .../scenegraph/metalunderqml/metalunderqml.pro | 10 + .../scenegraph/metalunderqml/metalunderqml.qrc | 7 + .../quick/scenegraph/metalunderqml/squircle.frag | 29 ++ .../quick/scenegraph/metalunderqml/squircle.vert | 23 ++ .../openglunderqml/doc/src/openglunderqml.qdoc | 7 +- .../quick/scenegraph/openglunderqml/squircle.cpp | 22 +- .../quick/scenegraph/openglunderqml/squircle.h | 2 + examples/quick/scenegraph/scenegraph.pro | 8 + examples/quick/scenegraph/shared/squircle_rhi.frag | 16 + examples/quick/scenegraph/shared/squircle_rhi.vert | 13 + .../shadereffects/content/shaders/+qsb/blur.frag | Bin 0 -> 2247 bytes .../content/shaders/+qsb/colorize.frag | Bin 0 -> 2106 bytes .../shadereffects/content/shaders/+qsb/genie.vert | Bin 0 -> 2629 bytes .../content/shaders/+qsb/outline.frag | Bin 0 -> 2538 bytes .../shadereffects/content/shaders/+qsb/shadow.frag | Bin 0 -> 2151 bytes .../shadereffects/content/shaders/+qsb/wobble.frag | Bin 0 -> 2049 bytes .../shadereffects/content/shaders/rhi/blur.frag | 21 + .../content/shaders/rhi/colorize.frag | 20 + .../shadereffects/content/shaders/rhi/compile.bat | 56 +++ .../shadereffects/content/shaders/rhi/genie.vert | 29 ++ .../shadereffects/content/shaders/rhi/outline.frag | 24 ++ .../shadereffects/content/shaders/rhi/shadow.frag | 21 + .../shadereffects/content/shaders/rhi/wobble.frag | 20 + .../quick/shadereffects/doc/src/shadereffects.qdoc | 16 +- examples/quick/shadereffects/shadereffects.qrc | 6 + 37 files changed, 1700 insertions(+), 10 deletions(-) create mode 100644 examples/quick/scenegraph/d3d11underqml/d3d11squircle.cpp create mode 100644 examples/quick/scenegraph/d3d11underqml/d3d11squircle.h create mode 100644 examples/quick/scenegraph/d3d11underqml/d3d11underqml.pro create mode 100644 examples/quick/scenegraph/d3d11underqml/d3d11underqml.qrc create mode 100644 examples/quick/scenegraph/d3d11underqml/main.cpp create mode 100644 examples/quick/scenegraph/d3d11underqml/main.qml create mode 100644 examples/quick/scenegraph/d3d11underqml/squircle.frag create mode 100644 examples/quick/scenegraph/d3d11underqml/squircle.vert create mode 100644 examples/quick/scenegraph/metalunderqml/main.cpp create mode 100644 examples/quick/scenegraph/metalunderqml/main.qml create mode 100644 examples/quick/scenegraph/metalunderqml/metalsquircle.h create mode 100644 examples/quick/scenegraph/metalunderqml/metalsquircle.mm create mode 100644 examples/quick/scenegraph/metalunderqml/metalunderqml.pro create mode 100644 examples/quick/scenegraph/metalunderqml/metalunderqml.qrc create mode 100644 examples/quick/scenegraph/metalunderqml/squircle.frag create mode 100644 examples/quick/scenegraph/metalunderqml/squircle.vert create mode 100644 examples/quick/scenegraph/shared/squircle_rhi.frag create mode 100644 examples/quick/scenegraph/shared/squircle_rhi.vert create mode 100644 examples/quick/shadereffects/content/shaders/+qsb/blur.frag create mode 100644 examples/quick/shadereffects/content/shaders/+qsb/colorize.frag create mode 100644 examples/quick/shadereffects/content/shaders/+qsb/genie.vert create mode 100644 examples/quick/shadereffects/content/shaders/+qsb/outline.frag create mode 100644 examples/quick/shadereffects/content/shaders/+qsb/shadow.frag create mode 100644 examples/quick/shadereffects/content/shaders/+qsb/wobble.frag create mode 100644 examples/quick/shadereffects/content/shaders/rhi/blur.frag create mode 100644 examples/quick/shadereffects/content/shaders/rhi/colorize.frag create mode 100755 examples/quick/shadereffects/content/shaders/rhi/compile.bat create mode 100644 examples/quick/shadereffects/content/shaders/rhi/genie.vert create mode 100644 examples/quick/shadereffects/content/shaders/rhi/outline.frag create mode 100644 examples/quick/shadereffects/content/shaders/rhi/shadow.frag create mode 100644 examples/quick/shadereffects/content/shaders/rhi/wobble.frag (limited to 'examples') diff --git a/examples/quick/scenegraph/d3d11underqml/d3d11squircle.cpp b/examples/quick/scenegraph/d3d11underqml/d3d11squircle.cpp new file mode 100644 index 0000000000..f05bf2f843 --- /dev/null +++ b/examples/quick/scenegraph/d3d11underqml/d3d11squircle.cpp @@ -0,0 +1,426 @@ +/**************************************************************************** +** +** Copyright (C) 2019 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the demonstration applications of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:BSD$ +** 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. +** +** BSD License Usage +** Alternatively, 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 "d3d11squircle.h" +#include +#include + +#include +#include + +class SquircleRenderer : public QObject +{ + Q_OBJECT +public: + SquircleRenderer(); + ~SquircleRenderer(); + + void setT(qreal t) { m_t = t; } + void setViewportSize(const QSize &size) { m_viewportSize = size; } + void setWindow(QQuickWindow *window) { m_window = window; } + +public slots: + void frameStart(); + void mainPassRecordingStart(); + +private: + enum Stage { + VertexStage, + FragmentStage + }; + void prepareShader(Stage stage); + QByteArray compileShader(Stage stage, + const QByteArray &source, + const QByteArray &entryPoint); + void init(); + + QSize m_viewportSize; + qreal m_t; + QQuickWindow *m_window; + + ID3D11Device *m_device = nullptr; + ID3D11DeviceContext *m_context = nullptr; + QByteArray m_vert; + QByteArray m_vertEntryPoint; + QByteArray m_frag; + QByteArray m_fragEntryPoint; + + bool m_initialized = false; + ID3D11Buffer *m_vbuf = nullptr; + ID3D11Buffer *m_cbuf = nullptr; + ID3D11VertexShader *m_vs = nullptr; + ID3D11PixelShader *m_ps = nullptr; + ID3D11InputLayout *m_inputLayout = nullptr; + ID3D11RasterizerState *m_rastState = nullptr; + ID3D11DepthStencilState *m_dsState = nullptr; + ID3D11BlendState *m_blendState = nullptr; +}; + +D3D11Squircle::D3D11Squircle() + : m_t(0) + , m_renderer(nullptr) +{ + connect(this, &QQuickItem::windowChanged, this, &D3D11Squircle::handleWindowChanged); +} + +void D3D11Squircle::setT(qreal t) +{ + if (t == m_t) + return; + m_t = t; + emit tChanged(); + if (window()) + window()->update(); +} + +void D3D11Squircle::handleWindowChanged(QQuickWindow *win) +{ + if (win) { + connect(win, &QQuickWindow::beforeSynchronizing, this, &D3D11Squircle::sync, Qt::DirectConnection); + connect(win, &QQuickWindow::sceneGraphInvalidated, this, &D3D11Squircle::cleanup, Qt::DirectConnection); + + // Ensure we start with cleared to black. The squircle's blend mode relies on this. + win->setColor(Qt::black); + } +} + +SquircleRenderer::SquircleRenderer() + : m_t(0) +{ +} + +// The safe way to release custom graphics resources it to both connect to +// sceneGraphInvalidated() and implement releaseResources(). To support +// threaded render loops the latter performs the SquircleRenderer destruction +// via scheduleRenderJob(). Note that the D3D11Squircle may be gone by the time +// the QRunnable is invoked. + +void D3D11Squircle::cleanup() +{ + delete m_renderer; + m_renderer = nullptr; +} + +class CleanupJob : public QRunnable +{ +public: + CleanupJob(SquircleRenderer *renderer) : m_renderer(renderer) { } + void run() override { delete m_renderer; } +private: + SquircleRenderer *m_renderer; +}; + +void D3D11Squircle::releaseResources() +{ + window()->scheduleRenderJob(new CleanupJob(m_renderer), QQuickWindow::BeforeSynchronizingStage); + m_renderer = nullptr; +} + +SquircleRenderer::~SquircleRenderer() +{ + qDebug("cleanup"); + + if (m_vs) + m_vs->Release(); + + if (m_ps) + m_ps->Release(); + + if (m_vbuf) + m_vbuf->Release(); + + if (m_cbuf) + m_cbuf->Release(); + + if (m_inputLayout) + m_inputLayout->Release(); + + if (m_rastState) + m_rastState->Release(); + + if (m_dsState) + m_dsState->Release(); + + if (m_blendState) + m_blendState->Release(); +} + +void D3D11Squircle::sync() +{ + if (!m_renderer) { + m_renderer = new SquircleRenderer; + connect(window(), &QQuickWindow::beforeRendering, m_renderer, &SquircleRenderer::frameStart, Qt::DirectConnection); + connect(window(), &QQuickWindow::beforeRenderPassRecording, m_renderer, &SquircleRenderer::mainPassRecordingStart, Qt::DirectConnection); + } + m_renderer->setViewportSize(window()->size() * window()->devicePixelRatio()); + m_renderer->setT(m_t); + m_renderer->setWindow(window()); +} + +void SquircleRenderer::frameStart() +{ + QSGRendererInterface *rif = m_window->rendererInterface(); + + // We are not prepared for anything other than running with the RHI and its D3D11 backend. + Q_ASSERT(rif->graphicsApi() == QSGRendererInterface::Direct3D11Rhi); + + m_device = reinterpret_cast(rif->getResource(m_window, QSGRendererInterface::DeviceResource)); + Q_ASSERT(m_device); + m_context = reinterpret_cast(rif->getResource(m_window, QSGRendererInterface::DeviceContextResource)); + Q_ASSERT(m_context); + + if (m_vert.isEmpty()) + prepareShader(VertexStage); + if (m_frag.isEmpty()) + prepareShader(FragmentStage); + + if (!m_initialized) + init(); +} + +static const float vertices[] = { + -1, -1, + 1, -1, + -1, 1, + 1, 1 +}; + +void SquircleRenderer::mainPassRecordingStart() +{ + m_window->beginExternalCommands(); + + D3D11_MAPPED_SUBRESOURCE mp; + // will copy the entire constant buffer every time -> pass WRITE_DISCARD -> prevent pipeline stalls + HRESULT hr = m_context->Map(m_cbuf, 0, D3D11_MAP_WRITE_DISCARD, 0, &mp); + if (SUCCEEDED(hr)) { + float t = m_t; + memcpy(mp.pData, &t, 4); + m_context->Unmap(m_cbuf, 0); + } else { + qFatal("Failed to map constant buffer: 0x%x", hr); + } + + D3D11_VIEWPORT v; + v.TopLeftX = 0; + v.TopLeftY = 0; + v.Width = m_viewportSize.width(); + v.Height = m_viewportSize.height(); + v.MinDepth = 0; + v.MaxDepth = 1; + m_context->RSSetViewports(1, &v); + + m_context->VSSetShader(m_vs, nullptr, 0); + m_context->PSSetShader(m_ps, nullptr, 0); + m_context->IASetPrimitiveTopology(D3D11_PRIMITIVE_TOPOLOGY_TRIANGLESTRIP); + m_context->IASetInputLayout(m_inputLayout); + m_context->OMSetDepthStencilState(m_dsState, 0); + float blendConstants[] = { 1, 1, 1, 1 }; + m_context->OMSetBlendState(m_blendState, blendConstants, 0xFFFFFFFF); + m_context->RSSetState(m_rastState); + + const UINT stride = 2 * sizeof(float); // vec2 + const UINT offset = 0; + m_context->IASetVertexBuffers(0, 1, &m_vbuf, &stride, &offset); + m_context->PSSetConstantBuffers(0, 1, &m_cbuf); + + m_context->Draw(4, 0); + + m_window->endExternalCommands(); +} + +void SquircleRenderer::prepareShader(Stage stage) +{ + QString filename; + if (stage == VertexStage) { + filename = QLatin1String(":/scenegraph/d3d11underqml/squircle.vert"); + } else { + Q_ASSERT(stage == FragmentStage); + filename = QLatin1String(":/scenegraph/d3d11underqml/squircle.frag"); + } + QFile f(filename); + if (!f.open(QIODevice::ReadOnly)) + qFatal("Failed to read shader %s", qPrintable(filename)); + + const QByteArray contents = f.readAll(); + + if (stage == VertexStage) { + m_vert = contents; + Q_ASSERT(!m_vert.isEmpty()); + m_vertEntryPoint = QByteArrayLiteral("main"); + } else { + m_frag = contents; + Q_ASSERT(!m_frag.isEmpty()); + m_fragEntryPoint = QByteArrayLiteral("main"); + } +} + +QByteArray SquircleRenderer::compileShader(Stage stage, + const QByteArray &source, + const QByteArray &entryPoint) +{ + const char *target; + switch (stage) { + case VertexStage: + target = "vs_5_0"; + break; + case FragmentStage: + target = "ps_5_0"; + break; + default: + qFatal("Unknown shader stage %d", stage); + return QByteArray(); + } + + ID3DBlob *bytecode = nullptr; + ID3DBlob *errors = nullptr; + HRESULT hr = D3DCompile(source.constData(), source.size(), + nullptr, nullptr, nullptr, + entryPoint.constData(), target, 0, 0, &bytecode, &errors); + if (FAILED(hr) || !bytecode) { + qWarning("HLSL shader compilation failed: 0x%x", uint(hr)); + if (errors) { + const QByteArray msg(static_cast(errors->GetBufferPointer()), + errors->GetBufferSize()); + errors->Release(); + qWarning("%s", msg.constData()); + } + return QByteArray(); + } + + QByteArray result; + result.resize(bytecode->GetBufferSize()); + memcpy(result.data(), bytecode->GetBufferPointer(), result.size()); + bytecode->Release(); + + return result; +} + +void SquircleRenderer::init() +{ + qDebug("init"); + m_initialized = true; + + const QByteArray vs = compileShader(VertexStage, m_vert, m_vertEntryPoint); + const QByteArray fs = compileShader(FragmentStage, m_frag, m_fragEntryPoint); + + HRESULT hr = m_device->CreateVertexShader(vs.constData(), vs.size(), nullptr, &m_vs); + if (FAILED(hr)) + qFatal("Failed to create vertex shader: 0x%x", hr); + + hr = m_device->CreatePixelShader(fs.constData(), fs.size(), nullptr, &m_ps); + if (FAILED(hr)) + qFatal("Failed to create pixel shader: 0x%x", hr); + + D3D11_BUFFER_DESC bufDesc; + memset(&bufDesc, 0, sizeof(bufDesc)); + bufDesc.ByteWidth = sizeof(vertices); + bufDesc.Usage = D3D11_USAGE_DEFAULT; + bufDesc.BindFlags = D3D11_BIND_VERTEX_BUFFER; + hr = m_device->CreateBuffer(&bufDesc, nullptr, &m_vbuf); + if (FAILED(hr)) + qFatal("Failed to create buffer: 0x%x", hr); + + m_context->UpdateSubresource(m_vbuf, 0, nullptr, vertices, 0, 0); + + bufDesc.ByteWidth = 256; + bufDesc.Usage = D3D11_USAGE_DYNAMIC; + bufDesc.BindFlags = D3D11_BIND_CONSTANT_BUFFER; + bufDesc.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE; + hr = m_device->CreateBuffer(&bufDesc, nullptr, &m_cbuf); + if (FAILED(hr)) + qFatal("Failed to create buffer: 0x%x", hr); + + D3D11_INPUT_ELEMENT_DESC inputDesc; + memset(&inputDesc, 0, sizeof(inputDesc)); + // the output from SPIRV-Cross uses TEXCOORD as the semantic + inputDesc.SemanticName = "TEXCOORD"; + inputDesc.SemanticIndex = 0; + inputDesc.Format = DXGI_FORMAT_R32G32_FLOAT; // vec2 + inputDesc.InputSlot = 0; + inputDesc.AlignedByteOffset = 0; + inputDesc.InputSlotClass = D3D11_INPUT_PER_VERTEX_DATA; + hr = m_device->CreateInputLayout(&inputDesc, 1, vs.constData(), vs.size(), &m_inputLayout); + if (FAILED(hr)) + qFatal("Failed to create input layout: 0x%x", hr); + + D3D11_RASTERIZER_DESC rastDesc; + memset(&rastDesc, 0, sizeof(rastDesc)); + rastDesc.FillMode = D3D11_FILL_SOLID; + rastDesc.CullMode = D3D11_CULL_NONE; + hr = m_device->CreateRasterizerState(&rastDesc, &m_rastState); + if (FAILED(hr)) + qFatal("Failed to create rasterizer state: 0x%x", hr); + + D3D11_DEPTH_STENCIL_DESC dsDesc; + memset(&dsDesc, 0, sizeof(dsDesc)); + hr = m_device->CreateDepthStencilState(&dsDesc, &m_dsState); + if (FAILED(hr)) + qFatal("Failed to create depth/stencil state: 0x%x", hr); + + D3D11_BLEND_DESC blendDesc; + memset(&blendDesc, 0, sizeof(blendDesc)); + blendDesc.IndependentBlendEnable = true; + D3D11_RENDER_TARGET_BLEND_DESC blend; + memset(&blend, 0, sizeof(blend)); + blend.BlendEnable = true; + blend.SrcBlend = D3D11_BLEND_SRC_ALPHA; + blend.DestBlend = D3D11_BLEND_ONE; + blend.BlendOp = D3D11_BLEND_OP_ADD; + blend.SrcBlendAlpha = D3D11_BLEND_SRC_ALPHA; + blend.DestBlendAlpha = D3D11_BLEND_ONE; + blend.BlendOpAlpha = D3D11_BLEND_OP_ADD; + blend.RenderTargetWriteMask = D3D11_COLOR_WRITE_ENABLE_ALL; + blendDesc.RenderTarget[0] = blend; + hr = m_device->CreateBlendState(&blendDesc, &m_blendState); + if (FAILED(hr)) + qFatal("Failed to create blend state: 0x%x", hr); +} + +#include "d3d11squircle.moc" diff --git a/examples/quick/scenegraph/d3d11underqml/d3d11squircle.h b/examples/quick/scenegraph/d3d11underqml/d3d11squircle.h new file mode 100644 index 0000000000..be9aadc43b --- /dev/null +++ b/examples/quick/scenegraph/d3d11underqml/d3d11squircle.h @@ -0,0 +1,86 @@ +/**************************************************************************** +** +** Copyright (C) 2019 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the demonstration applications of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:BSD$ +** 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. +** +** BSD License Usage +** Alternatively, 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$ +** +****************************************************************************/ + +#ifndef D3D11SQUIRCLE_H +#define D3D11SQUIRCLE_H + +#include + +class SquircleRenderer; + +class D3D11Squircle : public QQuickItem +{ + Q_OBJECT + Q_PROPERTY(qreal t READ t WRITE setT NOTIFY tChanged) + +public: + D3D11Squircle(); + + qreal t() const { return m_t; } + void setT(qreal t); + +signals: + void tChanged(); + +public slots: + void sync(); + void cleanup(); + +private slots: + void handleWindowChanged(QQuickWindow *win); + +private: + void releaseResources() override; + + qreal m_t; + SquircleRenderer *m_renderer = nullptr; +}; + +#endif diff --git a/examples/quick/scenegraph/d3d11underqml/d3d11underqml.pro b/examples/quick/scenegraph/d3d11underqml/d3d11underqml.pro new file mode 100644 index 0000000000..3c94d48ac4 --- /dev/null +++ b/examples/quick/scenegraph/d3d11underqml/d3d11underqml.pro @@ -0,0 +1,10 @@ +QT += qml quick + +HEADERS += d3d11squircle.h +SOURCES += d3d11squircle.cpp main.cpp +RESOURCES += d3d11underqml.qrc + +LIBS += -ld3d11 -ld3dcompiler + +target.path = $$[QT_INSTALL_EXAMPLES]/quick/scenegraph/d3d11underqml +INSTALLS += target diff --git a/examples/quick/scenegraph/d3d11underqml/d3d11underqml.qrc b/examples/quick/scenegraph/d3d11underqml/d3d11underqml.qrc new file mode 100644 index 0000000000..fa16b88279 --- /dev/null +++ b/examples/quick/scenegraph/d3d11underqml/d3d11underqml.qrc @@ -0,0 +1,7 @@ + + + main.qml + squircle.vert + squircle.frag + + diff --git a/examples/quick/scenegraph/d3d11underqml/main.cpp b/examples/quick/scenegraph/d3d11underqml/main.cpp new file mode 100644 index 0000000000..d26de1144a --- /dev/null +++ b/examples/quick/scenegraph/d3d11underqml/main.cpp @@ -0,0 +1,69 @@ +/**************************************************************************** +** +** Copyright (C) 2019 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the demonstration applications of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:BSD$ +** 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. +** +** BSD License Usage +** Alternatively, 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 +#include +#include "d3d11squircle.h" + +int main(int argc, char **argv) +{ + QGuiApplication app(argc, argv); + + qmlRegisterType("D3D11UnderQML", 1, 0, "D3D11Squircle"); + + QQuickWindow::setSceneGraphBackend(QSGRendererInterface::Direct3D11Rhi); + + QQuickView view; + view.setResizeMode(QQuickView::SizeRootObjectToView); + view.setSource(QUrl("qrc:///scenegraph/d3d11underqml/main.qml")); + view.show(); + + return app.exec(); +} diff --git a/examples/quick/scenegraph/d3d11underqml/main.qml b/examples/quick/scenegraph/d3d11underqml/main.qml new file mode 100644 index 0000000000..da128bead6 --- /dev/null +++ b/examples/quick/scenegraph/d3d11underqml/main.qml @@ -0,0 +1,89 @@ +/**************************************************************************** +** +** Copyright (C) 2019 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the demonstration applications of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:BSD$ +** 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. +** +** BSD License Usage +** Alternatively, 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$ +** +****************************************************************************/ + +//! [1] +import QtQuick 2.0 +import D3D11UnderQML 1.0 + +Item { + + width: 320 + height: 480 + + D3D11Squircle { + SequentialAnimation on t { + NumberAnimation { to: 1; duration: 2500; easing.type: Easing.InQuad } + NumberAnimation { to: 0; duration: 2500; easing.type: Easing.OutQuad } + loops: Animation.Infinite + running: true + } + } +//! [1] //! [2] + Rectangle { + color: Qt.rgba(1, 1, 1, 0.7) + radius: 10 + border.width: 1 + border.color: "white" + anchors.fill: label + anchors.margins: -10 + } + + Text { + id: label + color: "black" + wrapMode: Text.WordWrap + text: "The background here is a squircle rendered with raw Direct3D 11 using the beforeRendering() and beforeRenderPassRecording() signals in QQuickWindow. This text label and its border is rendered using QML" + anchors.right: parent.right + anchors.left: parent.left + anchors.bottom: parent.bottom + anchors.margins: 20 + } +} +//! [2] diff --git a/examples/quick/scenegraph/d3d11underqml/squircle.frag b/examples/quick/scenegraph/d3d11underqml/squircle.frag new file mode 100644 index 0000000000..a907c84e58 --- /dev/null +++ b/examples/quick/scenegraph/d3d11underqml/squircle.frag @@ -0,0 +1,35 @@ +cbuffer buf : register(b0) +{ + float ubuf_t : packoffset(c0); +}; + + +static float2 coords; +static float4 fragColor; + +struct SPIRV_Cross_Input +{ + float2 coords : TEXCOORD0; +}; + +struct SPIRV_Cross_Output +{ + float4 fragColor : SV_Target0; +}; + +void frag_main() +{ + float i = 1.0f - (pow(abs(coords.x), 4.0f) + pow(abs(coords.y), 4.0f)); + i = smoothstep(ubuf_t - 0.800000011920928955078125f, ubuf_t + 0.800000011920928955078125f, i); + i = floor(i * 20.0f) / 20.0f; + fragColor = float4((coords * 0.5f) + 0.5f.xx, i, i); +} + +SPIRV_Cross_Output main(SPIRV_Cross_Input stage_input) +{ + coords = stage_input.coords; + frag_main(); + SPIRV_Cross_Output stage_output; + stage_output.fragColor = fragColor; + return stage_output; +} diff --git a/examples/quick/scenegraph/d3d11underqml/squircle.vert b/examples/quick/scenegraph/d3d11underqml/squircle.vert new file mode 100644 index 0000000000..077a1ad096 --- /dev/null +++ b/examples/quick/scenegraph/d3d11underqml/squircle.vert @@ -0,0 +1,30 @@ +static float4 gl_Position; +static float4 vertices; +static float2 coords; + +struct SPIRV_Cross_Input +{ + float4 vertices : TEXCOORD0; +}; + +struct SPIRV_Cross_Output +{ + float2 coords : TEXCOORD0; + float4 gl_Position : SV_Position; +}; + +void vert_main() +{ + gl_Position = vertices; + coords = vertices.xy; +} + +SPIRV_Cross_Output main(SPIRV_Cross_Input stage_input) +{ + vertices = stage_input.vertices; + vert_main(); + SPIRV_Cross_Output stage_output; + stage_output.gl_Position = gl_Position; + stage_output.coords = coords; + return stage_output; +} diff --git a/examples/quick/scenegraph/metalunderqml/main.cpp b/examples/quick/scenegraph/metalunderqml/main.cpp new file mode 100644 index 0000000000..5ad337abb1 --- /dev/null +++ b/examples/quick/scenegraph/metalunderqml/main.cpp @@ -0,0 +1,69 @@ +/**************************************************************************** +** +** Copyright (C) 2019 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the demonstration applications of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:BSD$ +** 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. +** +** BSD License Usage +** Alternatively, 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 +#include +#include "metalsquircle.h" + +int main(int argc, char **argv) +{ + QGuiApplication app(argc, argv); + + qmlRegisterType("MetalUnderQML", 1, 0, "MetalSquircle"); + + QQuickWindow::setSceneGraphBackend(QSGRendererInterface::MetalRhi); + + QQuickView view; + view.setResizeMode(QQuickView::SizeRootObjectToView); + view.setSource(QUrl("qrc:///scenegraph/metalunderqml/main.qml")); + view.show(); + + return app.exec(); +} diff --git a/examples/quick/scenegraph/metalunderqml/main.qml b/examples/quick/scenegraph/metalunderqml/main.qml new file mode 100644 index 0000000000..d41a0b8e81 --- /dev/null +++ b/examples/quick/scenegraph/metalunderqml/main.qml @@ -0,0 +1,89 @@ +/**************************************************************************** +** +** Copyright (C) 2019 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the demonstration applications of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:BSD$ +** 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. +** +** BSD License Usage +** Alternatively, 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$ +** +****************************************************************************/ + +//! [1] +import QtQuick 2.0 +import MetalUnderQML 1.0 + +Item { + + width: 320 + height: 480 + + MetalSquircle { + SequentialAnimation on t { + NumberAnimation { to: 1; duration: 2500; easing.type: Easing.InQuad } + NumberAnimation { to: 0; duration: 2500; easing.type: Easing.OutQuad } + loops: Animation.Infinite + running: true + } + } +//! [1] //! [2] + Rectangle { + color: Qt.rgba(1, 1, 1, 0.7) + radius: 10 + border.width: 1 + border.color: "white" + anchors.fill: label + anchors.margins: -10 + } + + Text { + id: label + color: "black" + wrapMode: Text.WordWrap + text: "The background here is a squircle rendered with raw Metal using the beforeRendering() and beforeRenderPassRecording() signals in QQuickWindow. This text label and its border is rendered using QML" + anchors.right: parent.right + anchors.left: parent.left + anchors.bottom: parent.bottom + anchors.margins: 20 + } +} +//! [2] diff --git a/examples/quick/scenegraph/metalunderqml/metalsquircle.h b/examples/quick/scenegraph/metalunderqml/metalsquircle.h new file mode 100644 index 0000000000..43c4afad21 --- /dev/null +++ b/examples/quick/scenegraph/metalunderqml/metalsquircle.h @@ -0,0 +1,86 @@ +/**************************************************************************** +** +** Copyright (C) 2019 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the demonstration applications of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:BSD$ +** 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. +** +** BSD License Usage +** Alternatively, 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$ +** +****************************************************************************/ + +#ifndef METALSQUIRCLE_H +#define METALSQUIRCLE_H + +#include + +class SquircleRenderer; + +class MetalSquircle : public QQuickItem +{ + Q_OBJECT + Q_PROPERTY(qreal t READ t WRITE setT NOTIFY tChanged) + +public: + MetalSquircle(); + + qreal t() const { return m_t; } + void setT(qreal t); + +signals: + void tChanged(); + +public slots: + void sync(); + void cleanup(); + +private slots: + void handleWindowChanged(QQuickWindow *win); + +private: + void releaseResources() override; + + qreal m_t; + SquircleRenderer *m_renderer = nullptr; +}; + +#endif diff --git a/examples/quick/scenegraph/metalunderqml/metalsquircle.mm b/examples/quick/scenegraph/metalunderqml/metalsquircle.mm new file mode 100644 index 0000000000..92aceeb433 --- /dev/null +++ b/examples/quick/scenegraph/metalunderqml/metalsquircle.mm @@ -0,0 +1,364 @@ +/**************************************************************************** +** +** Copyright (C) 2019 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the demonstration applications of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:BSD$ +** 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. +** +** BSD License Usage +** Alternatively, 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 "metalsquircle.h" +#include +#include + +#include + +class SquircleRenderer : public QObject +{ + Q_OBJECT +public: + SquircleRenderer(); + ~SquircleRenderer(); + + void setT(qreal t) { m_t = t; } + void setViewportSize(const QSize &size) { m_viewportSize = size; } + void setWindow(QQuickWindow *window) { m_window = window; } + +public slots: + void frameStart(); + void mainPassRecordingStart(); + +private: + enum Stage { + VertexStage, + FragmentStage + }; + void prepareShader(Stage stage); + using FuncAndLib = QPair, id >; + FuncAndLib compileShaderFromSource(const QByteArray &src, const QByteArray &entryPoint); + void init(int framesInFlight); + + QSize m_viewportSize; + qreal m_t; + QQuickWindow *m_window; + + QByteArray m_vert; + QByteArray m_vertEntryPoint; + QByteArray m_frag; + QByteArray m_fragEntryPoint; + + bool m_initialized = false; + id m_device; + id m_vbuf; + id m_ubuf[3]; + FuncAndLib m_vs; + FuncAndLib m_fs; + id m_pipeline; +}; + +MetalSquircle::MetalSquircle() + : m_t(0) + , m_renderer(nullptr) +{ + connect(this, &QQuickItem::windowChanged, this, &MetalSquircle::handleWindowChanged); +} + +void MetalSquircle::setT(qreal t) +{ + if (t == m_t) + return; + m_t = t; + emit tChanged(); + if (window()) + window()->update(); +} + +void MetalSquircle::handleWindowChanged(QQuickWindow *win) +{ + if (win) { + connect(win, &QQuickWindow::beforeSynchronizing, this, &MetalSquircle::sync, Qt::DirectConnection); + connect(win, &QQuickWindow::sceneGraphInvalidated, this, &MetalSquircle::cleanup, Qt::DirectConnection); + + // Ensure we start with cleared to black. The squircle's blend mode relies on this. + win->setColor(Qt::black); + } +} + +SquircleRenderer::SquircleRenderer() + : m_t(0) +{ + m_vbuf = nil; + + for (int i = 0; i < 3; ++i) + m_ubuf[i] = nil; + + m_vs.first = nil; + m_vs.second = nil; + + m_fs.first = nil; + m_fs.second = nil; +} + +// The safe way to release custom graphics resources is to both connect to +// sceneGraphInvalidated() and implement releaseResources(). To support +// threaded render loops the latter performs the SquircleRenderer destruction +// via scheduleRenderJob(). Note that the MetalSquircle may be gone by the time +// the QRunnable is invoked. + +void MetalSquircle::cleanup() +{ + delete m_renderer; + m_renderer = nullptr; +} + +class CleanupJob : public QRunnable +{ +public: + CleanupJob(SquircleRenderer *renderer) : m_renderer(renderer) { } + void run() override { delete m_renderer; } +private: + SquircleRenderer *m_renderer; +}; + +void MetalSquircle::releaseResources() +{ + window()->scheduleRenderJob(new CleanupJob(m_renderer), QQuickWindow::BeforeSynchronizingStage); + m_renderer = nullptr; +} + +SquircleRenderer::~SquircleRenderer() +{ + qDebug("cleanup"); + + [m_vbuf release]; + for (int i = 0; i < 3; ++i) + [m_ubuf[i] release]; + + [m_vs.first release]; + [m_vs.second release]; + + [m_fs.first release]; + [m_fs.second release]; +} + +void MetalSquircle::sync() +{ + if (!m_renderer) { + m_renderer = new SquircleRenderer; + connect(window(), &QQuickWindow::beforeRendering, m_renderer, &SquircleRenderer::frameStart, Qt::DirectConnection); + connect(window(), &QQuickWindow::beforeRenderPassRecording, m_renderer, &SquircleRenderer::mainPassRecordingStart, Qt::DirectConnection); + } + m_renderer->setViewportSize(window()->size() * window()->devicePixelRatio()); + m_renderer->setT(m_t); + m_renderer->setWindow(window()); +} + +void SquircleRenderer::frameStart() +{ + QSGRendererInterface *rif = m_window->rendererInterface(); + + // We are not prepared for anything other than running with the RHI and its Metal backend. + Q_ASSERT(rif->graphicsApi() == QSGRendererInterface::MetalRhi); + + m_device = (id) rif->getResource(m_window, QSGRendererInterface::DeviceResource); + Q_ASSERT(m_device); + + if (m_vert.isEmpty()) + prepareShader(VertexStage); + if (m_frag.isEmpty()) + prepareShader(FragmentStage); + + if (!m_initialized) + init(m_window->graphicsStateInfo()->framesInFlight); +} + +static const float vertices[] = { + -1, -1, + 1, -1, + -1, 1, + 1, 1 +}; + +const int UBUF_SIZE = 4; + +void SquircleRenderer::mainPassRecordingStart() +{ + // This example demonstrates the simple case: prepending some commands to + // the scenegraph's main renderpass. It does not create its own passes, + // rendertargets, etc. so no synchronization is needed. + + const QQuickWindow::GraphicsStateInfo *stateInfo = m_window->graphicsStateInfo(); + + QSGRendererInterface *rif = m_window->rendererInterface(); + id encoder = (id) rif->getResource( + m_window, QSGRendererInterface::CommandEncoderResource); + Q_ASSERT(encoder); + + m_window->beginExternalCommands(); + + void *p = [m_ubuf[stateInfo->currentFrameSlot] contents]; + float t = m_t; + memcpy(p, &t, 4); + + MTLViewport vp; + vp.originX = 0; + vp.originY = 0; + vp.width = m_viewportSize.width(); + vp.height = m_viewportSize.height(); + vp.znear = 0; + vp.zfar = 1; + [encoder setViewport: vp]; + + [encoder setFragmentBuffer: m_ubuf[stateInfo->currentFrameSlot] offset: 0 atIndex: 0]; + [encoder setVertexBuffer: m_vbuf offset: 0 atIndex: 1]; + [encoder setRenderPipelineState: m_pipeline]; + [encoder drawPrimitives: MTLPrimitiveTypeTriangleStrip vertexStart: 0 vertexCount: 4 instanceCount: 1 baseInstance: 0]; + + m_window->endExternalCommands(); +} + +void SquircleRenderer::prepareShader(Stage stage) +{ + QString filename; + if (stage == VertexStage) { + filename = QLatin1String(":/scenegraph/metalunderqml/squircle.vert"); + } else { + Q_ASSERT(stage == FragmentStage); + filename = QLatin1String(":/scenegraph/metalunderqml/squircle.frag"); + } + QFile f(filename); + if (!f.open(QIODevice::ReadOnly)) + qFatal("Failed to read shader %s", qPrintable(filename)); + + const QByteArray contents = f.readAll(); + + if (stage == VertexStage) { + m_vert = contents; + Q_ASSERT(!m_vert.isEmpty()); + m_vertEntryPoint = QByteArrayLiteral("main0"); + } else { + m_frag = contents; + Q_ASSERT(!m_frag.isEmpty()); + m_fragEntryPoint = QByteArrayLiteral("main0"); + } +} + +SquircleRenderer::FuncAndLib SquircleRenderer::compileShaderFromSource(const QByteArray &src, const QByteArray &entryPoint) +{ + FuncAndLib fl; + + NSString *srcstr = [NSString stringWithUTF8String: src.constData()]; + MTLCompileOptions *opts = [[MTLCompileOptions alloc] init]; + opts.languageVersion = MTLLanguageVersion1_2; + NSError *err = nil; + fl.second = [m_device newLibraryWithSource: srcstr options: opts error: &err]; + [opts release]; + // srcstr is autoreleased + + if (err) { + const QString msg = QString::fromNSString(err.localizedDescription); + qFatal("%s", qPrintable(msg)); + return fl; + } + + NSString *name = [NSString stringWithUTF8String: entryPoint.constData()]; + fl.first = [fl.second newFunctionWithName: name]; + [name release]; + + return fl; +} + +void SquircleRenderer::init(int framesInFlight) +{ + qDebug("init"); + + Q_ASSERT(framesInFlight <= 3); + m_initialized = true; + + m_vbuf = [m_device newBufferWithLength: sizeof(vertices) options: MTLResourceStorageModeShared]; + void *p = [m_vbuf contents]; + memcpy(p, vertices, sizeof(vertices)); + + for (int i = 0; i < framesInFlight; ++i) + m_ubuf[i] = [m_device newBufferWithLength: UBUF_SIZE options: MTLResourceStorageModeShared]; + + MTLVertexDescriptor *inputLayout = [MTLVertexDescriptor vertexDescriptor]; + inputLayout.attributes[0].format = MTLVertexFormatFloat2; + inputLayout.attributes[0].offset = 0; + inputLayout.attributes[0].bufferIndex = 1; // ubuf is 0, vbuf is 1 + inputLayout.layouts[1].stride = 2 * sizeof(float); + + MTLRenderPipelineDescriptor *rpDesc = [[MTLRenderPipelineDescriptor alloc] init]; + rpDesc.vertexDescriptor = inputLayout; + + m_vs = compileShaderFromSource(m_vert, m_vertEntryPoint); + rpDesc.vertexFunction = m_vs.first; + m_fs = compileShaderFromSource(m_frag, m_fragEntryPoint); + rpDesc.fragmentFunction = m_fs.first; + + rpDesc.colorAttachments[0].pixelFormat = MTLPixelFormatBGRA8Unorm; + rpDesc.colorAttachments[0].blendingEnabled = true; + rpDesc.colorAttachments[0].sourceRGBBlendFactor = MTLBlendFactorSourceAlpha; + rpDesc.colorAttachments[0].sourceAlphaBlendFactor = MTLBlendFactorSourceAlpha; + rpDesc.colorAttachments[0].destinationRGBBlendFactor = MTLBlendFactorOne; + rpDesc.colorAttachments[0].destinationAlphaBlendFactor = MTLBlendFactorOne; + + if (m_device.depth24Stencil8PixelFormatSupported) { + rpDesc.depthAttachmentPixelFormat = MTLPixelFormatDepth24Unorm_Stencil8; + rpDesc.stencilAttachmentPixelFormat = MTLPixelFormatDepth24Unorm_Stencil8; + } else { + rpDesc.depthAttachmentPixelFormat = MTLPixelFormatDepth32Float_Stencil8; + rpDesc.stencilAttachmentPixelFormat = MTLPixelFormatDepth32Float_Stencil8; + } + + NSError *err = nil; + m_pipeline = [m_device newRenderPipelineStateWithDescriptor: rpDesc error: &err]; + if (!m_pipeline) { + const QString msg = QString::fromNSString(err.localizedDescription); + qFatal("Failed to create render pipeline state: %s", qPrintable(msg)); + } + [rpDesc release]; +} + +#include "metalsquircle.moc" diff --git a/examples/quick/scenegraph/metalunderqml/metalunderqml.pro b/examples/quick/scenegraph/metalunderqml/metalunderqml.pro new file mode 100644 index 0000000000..9b27638a6d --- /dev/null +++ b/examples/quick/scenegraph/metalunderqml/metalunderqml.pro @@ -0,0 +1,10 @@ +QT += qml quick + +HEADERS += metalsquircle.h +SOURCES += metalsquircle.mm main.cpp +RESOURCES += metalunderqml.qrc + +LIBS += -framework Metal -framework AppKit + +target.path = $$[QT_INSTALL_EXAMPLES]/quick/scenegraph/metalunderqml +INSTALLS += target diff --git a/examples/quick/scenegraph/metalunderqml/metalunderqml.qrc b/examples/quick/scenegraph/metalunderqml/metalunderqml.qrc new file mode 100644 index 0000000000..7172f1fcb7 --- /dev/null +++ b/examples/quick/scenegraph/metalunderqml/metalunderqml.qrc @@ -0,0 +1,7 @@ + + + main.qml + squircle.vert + squircle.frag + + diff --git a/examples/quick/scenegraph/metalunderqml/squircle.frag b/examples/quick/scenegraph/metalunderqml/squircle.frag new file mode 100644 index 0000000000..15f34624fe --- /dev/null +++ b/examples/quick/scenegraph/metalunderqml/squircle.frag @@ -0,0 +1,29 @@ +#include +#include + +using namespace metal; + +struct buf +{ + float t; +}; + +struct main0_out +{ + float4 fragColor [[color(0)]]; +}; + +struct main0_in +{ + float2 coords [[user(locn0)]]; +}; + +fragment main0_out main0(main0_in in [[stage_in]], constant buf& ubuf [[buffer(0)]]) +{ + main0_out out = {}; + float i = 1.0 - (pow(abs(in.coords.x), 4.0) + pow(abs(in.coords.y), 4.0)); + i = smoothstep(ubuf.t - 0.800000011920928955078125, ubuf.t + 0.800000011920928955078125, i); + i = floor(i * 20.0) / 20.0; + out.fragColor = float4((in.coords * 0.5) + float2(0.5), i, i); + return out; +} diff --git a/examples/quick/scenegraph/metalunderqml/squircle.vert b/examples/quick/scenegraph/metalunderqml/squircle.vert new file mode 100644 index 0000000000..a88c59f541 --- /dev/null +++ b/examples/quick/scenegraph/metalunderqml/squircle.vert @@ -0,0 +1,23 @@ +#include +#include + +using namespace metal; + +struct main0_out +{ + float2 coords [[user(locn0)]]; + float4 gl_Position [[position]]; +}; + +struct main0_in +{ + float4 vertices [[attribute(0)]]; +}; + +vertex main0_out main0(main0_in in [[stage_in]]) +{ + main0_out out = {}; + out.gl_Position = in.vertices; + out.coords = in.vertices.xy; + return out; +} diff --git a/examples/quick/scenegraph/openglunderqml/doc/src/openglunderqml.qdoc b/examples/quick/scenegraph/openglunderqml/doc/src/openglunderqml.qdoc index 3d4f4443e9..778b754869 100644 --- a/examples/quick/scenegraph/openglunderqml/doc/src/openglunderqml.qdoc +++ b/examples/quick/scenegraph/openglunderqml/doc/src/openglunderqml.qdoc @@ -112,8 +112,11 @@ \snippet scenegraph/openglunderqml/squircle.cpp 6 - In the \c cleanup() function we delete the renderer which in turn - cleans up its own resources. + In the \c cleanup() function we delete the renderer which in turn cleans up + its own resources. This is complemented by reimplementing \l + QQuickWindow::releaseResources() since just connecting to the + sceneGraphInvalidated() signal is not sufficient on its own to handle all + cases. \snippet scenegraph/openglunderqml/squircle.cpp 8 diff --git a/examples/quick/scenegraph/openglunderqml/squircle.cpp b/examples/quick/scenegraph/openglunderqml/squircle.cpp index d6f6b327f2..4e5848be2a 100644 --- a/examples/quick/scenegraph/openglunderqml/squircle.cpp +++ b/examples/quick/scenegraph/openglunderqml/squircle.cpp @@ -53,6 +53,7 @@ #include #include #include +#include //! [7] Squircle::Squircle() @@ -93,10 +94,23 @@ void Squircle::handleWindowChanged(QQuickWindow *win) //! [6] void Squircle::cleanup() { - if (m_renderer) { - delete m_renderer; - m_renderer = nullptr; - } + delete m_renderer; + m_renderer = nullptr; +} + +class CleanupJob : public QRunnable +{ +public: + CleanupJob(SquircleRenderer *renderer) : m_renderer(renderer) { } + void run() override { delete m_renderer; } +private: + SquircleRenderer *m_renderer; +}; + +void Squircle::releaseResources() +{ + window()->scheduleRenderJob(new CleanupJob(m_renderer), QQuickWindow::BeforeSynchronizingStage); + m_renderer = nullptr; } SquircleRenderer::~SquircleRenderer() diff --git a/examples/quick/scenegraph/openglunderqml/squircle.h b/examples/quick/scenegraph/openglunderqml/squircle.h index 652e679f81..962ba21101 100644 --- a/examples/quick/scenegraph/openglunderqml/squircle.h +++ b/examples/quick/scenegraph/openglunderqml/squircle.h @@ -103,6 +103,8 @@ private slots: void handleWindowChanged(QQuickWindow *win); private: + void releaseResources() override; + qreal m_t; SquircleRenderer *m_renderer; }; diff --git a/examples/quick/scenegraph/scenegraph.pro b/examples/quick/scenegraph/scenegraph.pro index 2efeb5ed83..ac368c79a5 100644 --- a/examples/quick/scenegraph/scenegraph.pro +++ b/examples/quick/scenegraph/scenegraph.pro @@ -16,5 +16,13 @@ SUBDIRS += \ rendernode \ threadedanimation +macos { + SUBDIRS += metalunderqml +} + +win32 { + SUBDIRS += d3d11underqml +} + EXAMPLE_FILES += \ shared diff --git a/examples/quick/scenegraph/shared/squircle_rhi.frag b/examples/quick/scenegraph/shared/squircle_rhi.frag new file mode 100644 index 0000000000..8da62b93e6 --- /dev/null +++ b/examples/quick/scenegraph/shared/squircle_rhi.frag @@ -0,0 +1,16 @@ +#version 440 + +layout(location = 0) in vec2 coords; +layout(location = 0) out vec4 fragColor; + +layout(std140, binding = 0) uniform buf { + float t; +} ubuf; + +void main() +{ + float i = 1. - (pow(abs(coords.x), 4.) + pow(abs(coords.y), 4.)); + i = smoothstep(ubuf.t - 0.8, ubuf.t + 0.8, i); + i = floor(i * 20.) / 20.; + fragColor = vec4(coords * .5 + .5, i, i); +} diff --git a/examples/quick/scenegraph/shared/squircle_rhi.vert b/examples/quick/scenegraph/shared/squircle_rhi.vert new file mode 100644 index 0000000000..b57dfdfe10 --- /dev/null +++ b/examples/quick/scenegraph/shared/squircle_rhi.vert @@ -0,0 +1,13 @@ +#version 440 + +layout(location = 0) in vec4 vertices; + +layout(location = 0) out vec2 coords; + +out gl_PerVertex { vec4 gl_Position; }; + +void main() +{ + gl_Position = vertices; + coords = vertices.xy; +} diff --git a/examples/quick/shadereffects/content/shaders/+qsb/blur.frag b/examples/quick/shadereffects/content/shaders/+qsb/blur.frag new file mode 100644 index 0000000000..1c79359297 Binary files /dev/null and b/examples/quick/shadereffects/content/shaders/+qsb/blur.frag differ diff --git a/examples/quick/shadereffects/content/shaders/+qsb/colorize.frag b/examples/quick/shadereffects/content/shaders/+qsb/colorize.frag new file mode 100644 index 0000000000..45c5301f31 Binary files /dev/null and b/examples/quick/shadereffects/content/shaders/+qsb/colorize.frag differ diff --git a/examples/quick/shadereffects/content/shaders/+qsb/genie.vert b/examples/quick/shadereffects/content/shaders/+qsb/genie.vert new file mode 100644 index 0000000000..dd94129cf7 Binary files /dev/null and b/examples/quick/shadereffects/content/shaders/+qsb/genie.vert differ diff --git a/examples/quick/shadereffects/content/shaders/+qsb/outline.frag b/examples/quick/shadereffects/content/shaders/+qsb/outline.frag new file mode 100644 index 0000000000..470e2bd6e6 Binary files /dev/null and b/examples/quick/shadereffects/content/shaders/+qsb/outline.frag differ diff --git a/examples/quick/shadereffects/content/shaders/+qsb/shadow.frag b/examples/quick/shadereffects/content/shaders/+qsb/shadow.frag new file mode 100644 index 0000000000..128af21daa Binary files /dev/null and b/examples/quick/shadereffects/content/shaders/+qsb/shadow.frag differ diff --git a/examples/quick/shadereffects/content/shaders/+qsb/wobble.frag b/examples/quick/shadereffects/content/shaders/+qsb/wobble.frag new file mode 100644 index 0000000000..9b27ae87cb Binary files /dev/null and b/examples/quick/shadereffects/content/shaders/+qsb/wobble.frag differ diff --git a/examples/quick/shadereffects/content/shaders/rhi/blur.frag b/examples/quick/shadereffects/content/shaders/rhi/blur.frag new file mode 100644 index 0000000000..0c914d4244 --- /dev/null +++ b/examples/quick/shadereffects/content/shaders/rhi/blur.frag @@ -0,0 +1,21 @@ +#version 440 + +layout(location = 0) in vec2 qt_TexCoord0; +layout(location = 0) out vec4 fragColor; + +layout(binding = 1) uniform sampler2D source; + +layout(std140, binding = 0) uniform buf { + mat4 qt_Matrix; + float qt_Opacity; + vec2 delta; +} ubuf; + +void main() +{ + fragColor =(0.0538 * texture(source, qt_TexCoord0 - 3.182 * ubuf.delta) + + 0.3229 * texture(source, qt_TexCoord0 - 1.364 * ubuf.delta) + + 0.2466 * texture(source, qt_TexCoord0) + + 0.3229 * texture(source, qt_TexCoord0 + 1.364 * ubuf.delta) + + 0.0538 * texture(source, qt_TexCoord0 + 3.182 * ubuf.delta)) * ubuf.qt_Opacity; +} diff --git a/examples/quick/shadereffects/content/shaders/rhi/colorize.frag b/examples/quick/shadereffects/content/shaders/rhi/colorize.frag new file mode 100644 index 0000000000..bc8067cc8c --- /dev/null +++ b/examples/quick/shadereffects/content/shaders/rhi/colorize.frag @@ -0,0 +1,20 @@ +#version 440 + +layout(location = 0) in vec2 qt_TexCoord0; +layout(location = 0) out vec4 fragColor; + +layout(binding = 1) uniform sampler2D source; + +layout(std140, binding = 0) uniform buf { + mat4 qt_Matrix; + float qt_Opacity; + vec4 tint; +} ubuf; + +void main() +{ + vec4 c = texture(source, qt_TexCoord0); + float lo = min(min(c.x, c.y), c.z); + float hi = max(max(c.x, c.y), c.z); + fragColor = ubuf.qt_Opacity * vec4(mix(vec3(lo), vec3(hi), ubuf.tint.xyz), c.w); +} diff --git a/examples/quick/shadereffects/content/shaders/rhi/compile.bat b/examples/quick/shadereffects/content/shaders/rhi/compile.bat new file mode 100755 index 0000000000..93dfb7b2a9 --- /dev/null +++ b/examples/quick/shadereffects/content/shaders/rhi/compile.bat @@ -0,0 +1,56 @@ +::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: +:: +:: Copyright (C) 2019 The Qt Company Ltd. +:: Contact: https://www.qt.io/licensing/ +:: +:: This file is part of the examples of the Qt Toolkit. +:: +:: $QT_BEGIN_LICENSE:BSD$ +:: 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. +:: +:: BSD License Usage +:: Alternatively, 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$ +:: +::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: + +qsb --glsl "150,120,100 es" --hlsl 50 --msl 12 -o ../+qsb/blur.frag blur.frag +qsb --glsl "150,120,100 es" --hlsl 50 --msl 12 -o ../+qsb/colorize.frag colorize.frag +qsb --glsl "150,120,100 es" --hlsl 50 --msl 12 -o ../+qsb/outline.frag outline.frag +qsb --glsl "150,120,100 es" --hlsl 50 --msl 12 -o ../+qsb/shadow.frag shadow.frag +qsb --glsl "150,120,100 es" --hlsl 50 --msl 12 -o ../+qsb/wobble.frag wobble.frag +qsb -b --glsl "150,120,100 es" --hlsl 50 --msl 12 -o ../+qsb/genie.vert genie.vert diff --git a/examples/quick/shadereffects/content/shaders/rhi/genie.vert b/examples/quick/shadereffects/content/shaders/rhi/genie.vert new file mode 100644 index 0000000000..2cb1e71046 --- /dev/null +++ b/examples/quick/shadereffects/content/shaders/rhi/genie.vert @@ -0,0 +1,29 @@ +#version 440 + +layout(location = 0) in vec4 qt_Vertex; +layout(location = 1) in vec2 qt_MultiTexCoord0; + +layout(location = 0) out vec2 qt_TexCoord0; + +layout(std140, binding = 0) uniform buf { + mat4 qt_Matrix; + float qt_Opacity; + float bend; + float minimize; + float side; + float width; + float height; +} ubuf; + +out gl_PerVertex { vec4 gl_Position; }; + +void main() +{ + qt_TexCoord0 = qt_MultiTexCoord0; + vec4 pos = qt_Vertex; + pos.y = mix(qt_Vertex.y, ubuf.height, ubuf.minimize); + float t = pos.y / ubuf.height; + t = (3. - 2. * t) * t * t; + pos.x = mix(qt_Vertex.x, ubuf.side * ubuf.width, t * ubuf.bend); + gl_Position = ubuf.qt_Matrix * pos; +} diff --git a/examples/quick/shadereffects/content/shaders/rhi/outline.frag b/examples/quick/shadereffects/content/shaders/rhi/outline.frag new file mode 100644 index 0000000000..26df69c5fe --- /dev/null +++ b/examples/quick/shadereffects/content/shaders/rhi/outline.frag @@ -0,0 +1,24 @@ +#version 440 + +layout(location = 0) in vec2 qt_TexCoord0; +layout(location = 0) out vec4 fragColor; + +layout(binding = 1) uniform sampler2D source; + +layout(std140, binding = 0) uniform buf { + mat4 qt_Matrix; + float qt_Opacity; + vec2 delta; +} ubuf; + +void main() +{ + vec4 tl = texture(source, qt_TexCoord0 - ubuf.delta); + vec4 tr = texture(source, qt_TexCoord0 + vec2(ubuf.delta.x, -ubuf.delta.y)); + vec4 bl = texture(source, qt_TexCoord0 - vec2(ubuf.delta.x, -ubuf.delta.y)); + vec4 br = texture(source, qt_TexCoord0 + ubuf.delta); + vec4 gx = (tl + bl) - (tr + br); + vec4 gy = (tl + tr) - (bl + br); + fragColor.xyz = vec3(0.); + fragColor.w = clamp(dot(sqrt(gx * gx + gy * gy), vec4(1.)), 0., 1.) * ubuf.qt_Opacity; +} diff --git a/examples/quick/shadereffects/content/shaders/rhi/shadow.frag b/examples/quick/shadereffects/content/shaders/rhi/shadow.frag new file mode 100644 index 0000000000..8247517b6d --- /dev/null +++ b/examples/quick/shadereffects/content/shaders/rhi/shadow.frag @@ -0,0 +1,21 @@ +#version 440 + +layout(location = 0) in vec2 qt_TexCoord0; +layout(location = 0) out vec4 fragColor; + +layout(binding = 1) uniform sampler2D source; +layout(binding = 2) uniform sampler2D shadow; + +layout(std140, binding = 0) uniform buf { + mat4 qt_Matrix; + float qt_Opacity; + float darkness; + vec2 delta; +} ubuf; + +void main() +{ + vec4 fg = texture(source, qt_TexCoord0); + vec4 bg = texture(shadow, qt_TexCoord0 + ubuf.delta); + fragColor = (fg + vec4(0., 0., 0., ubuf.darkness * bg.a) * (1. - fg.a)) * ubuf.qt_Opacity; +} diff --git a/examples/quick/shadereffects/content/shaders/rhi/wobble.frag b/examples/quick/shadereffects/content/shaders/rhi/wobble.frag new file mode 100644 index 0000000000..a34481c2f2 --- /dev/null +++ b/examples/quick/shadereffects/content/shaders/rhi/wobble.frag @@ -0,0 +1,20 @@ +#version 440 + +layout(location = 0) in vec2 qt_TexCoord0; +layout(location = 0) out vec4 fragColor; + +layout(binding = 1) uniform sampler2D source; + +layout(std140, binding = 0) uniform buf { + mat4 qt_Matrix; + float qt_Opacity; + float amplitude; + float frequency; + float time; +} ubuf; + +void main() +{ + vec2 p = sin(ubuf.time + ubuf.frequency * qt_TexCoord0); + fragColor = texture(source, qt_TexCoord0 + ubuf.amplitude * vec2(p.y, -p.x)) * ubuf.qt_Opacity; +} diff --git a/examples/quick/shadereffects/doc/src/shadereffects.qdoc b/examples/quick/shadereffects/doc/src/shadereffects.qdoc index 8cb4024da2..d35989c262 100644 --- a/examples/quick/shadereffects/doc/src/shadereffects.qdoc +++ b/examples/quick/shadereffects/doc/src/shadereffects.qdoc @@ -52,10 +52,18 @@ \snippet shadereffects/shadereffects.qml fragment In order to support multiple graphics APIs, not just OpenGL, the shader - source is not embedded into QML. Instead, file selectors are used to select - the correct variant at runtime. Based on the Qt Quick backend in use, Qt - will automatically select either \c{shaders/wobble.frag} with the GLSL - source code or \c{shaders/+hlsl/wobble.frag} with the HLSL source code. + source is not embedded into QML. When running with the graphics API + independent scene graph, the actual file in use is a pre-generated shader + pack containing multiple variants of the shader code. The appropriate + shader is then chosen by Qt Quick, regardless of running on Vulkan, Metal, + Direct 3D, or OpenGL. Qt automatically selects the file under the \c qsb + selector, for example \c{shaders/+qsb/wobble.frag}, when present. + + On the traditional code path, which can mean using OpenGL or Direct3D 12, + file selectors are used to select the correct variant at runtime. Based on + the Qt Quick backend in use, Qt will automatically select either + \c{shaders/wobble.frag} with the GLSL source code or + \c{shaders/+hlsl/wobble.frag} with the HLSL source code. \note For simplicity shader source code is used in all variants of the files. However, with the Direct3D backend of Qt Quick pre-compiled shaders diff --git a/examples/quick/shadereffects/shadereffects.qrc b/examples/quick/shadereffects/shadereffects.qrc index e66b98a6df..762ad0d037 100644 --- a/examples/quick/shadereffects/shadereffects.qrc +++ b/examples/quick/shadereffects/shadereffects.qrc @@ -6,15 +6,21 @@ content/Slider.qml content/shaders/wobble.frag content/shaders/+hlsl/wobble.frag + content/shaders/+qsb/wobble.frag content/shaders/blur.frag content/shaders/+hlsl/blur.frag + content/shaders/+qsb/blur.frag content/shaders/shadow.frag content/shaders/+hlsl/shadow.frag + content/shaders/+qsb/shadow.frag content/shaders/outline.frag content/shaders/+hlsl/outline.frag + content/shaders/+qsb/outline.frag content/shaders/colorize.frag content/shaders/+hlsl/colorize.frag + content/shaders/+qsb/colorize.frag content/shaders/genie.vert content/shaders/+hlsl/genie.vert + content/shaders/+qsb/genie.vert -- cgit v1.2.3 From 192d4fa0af5f40f76979f195c4356b26eaed1696 Mon Sep 17 00:00:00 2001 From: Laszlo Agocs Date: Tue, 25 Jun 2019 16:37:58 +0200 Subject: Make openglunderqml functional with and without the rhi It has to be written following the new split approach (beforeRendering for resource setup, beforeRenderPassRecording to issue the actual underlay draw calls), but it will then work both with and without QSG_RHI=1. Change-Id: I9b7b35434aa0caec543cae268064b2684256382d Reviewed-by: Lars Knoll --- .../openglunderqml/doc/src/openglunderqml.qdoc | 34 +++++++++------------- .../quick/scenegraph/openglunderqml/squircle.cpp | 31 +++++++++++++++----- .../quick/scenegraph/openglunderqml/squircle.h | 1 + 3 files changed, 37 insertions(+), 29 deletions(-) (limited to 'examples') diff --git a/examples/quick/scenegraph/openglunderqml/doc/src/openglunderqml.qdoc b/examples/quick/scenegraph/openglunderqml/doc/src/openglunderqml.qdoc index 778b754869..69a9d2ce4b 100644 --- a/examples/quick/scenegraph/openglunderqml/doc/src/openglunderqml.qdoc +++ b/examples/quick/scenegraph/openglunderqml/doc/src/openglunderqml.qdoc @@ -94,17 +94,18 @@ \snippet scenegraph/openglunderqml/squircle.cpp 3 - The default behavior of the scene graph is to clear the - framebuffer before rendering. Since we render before the scene - graph, we need to turn this clearing off. This means that we need - to clear ourselves in the \c paint() function. + The default behavior of the scene graph is to clear the framebuffer before + rendering. This is fine since we will insert our own rendering code after + this clear is enqueued. Make sure however that we clear to the desired + color (black). \snippet scenegraph/openglunderqml/squircle.cpp 9 - We use the \c sync() function to initialize the renderer and to - copy the state in our item into the renderer. When the renderer is - created, we also connect the \l QQuickWindow::beforeRendering() to - the renderer's \c paint() slot. + We use the \c sync() function to initialize the renderer and to copy the + state in our item into the renderer. When the renderer is created, we also + connect the \l QQuickWindow::beforeRendering() and \l + QQuickWindow::beforeRenderPassRecording() to the renderer's \c init() and + \c paint() slots. \note The \l QQuickWindow::beforeSynchronizing() signal is emitted on the rendering thread while the GUI thread is blocked, so it is @@ -127,22 +128,13 @@ \snippet scenegraph/openglunderqml/squircle.cpp 4 - In the SquircleRenderer's \c paint() function we start by - initializing the shader program. By initializing the shader - program here, we make sure that the OpenGL context is bound and - that we are on the correct thread. + In the SquircleRenderer's \c init() function we start by initializing the + shader program if not yet done. The OpenGL context is current on the thread + when the slot is invoked. \snippet scenegraph/openglunderqml/squircle.cpp 5 - We use the shader program to draw the squircle. At the end of the - \c paint function we release the program and disable the - attributes we used so that the OpenGL context is in a "clean" - state for the scene graph to pick it up. - - \note If tracking the changes in the OpenGL context's state is not - feasible, one can use the function \l - QQuickWindow::resetOpenGLState() which will reset all state that - the scene graph relies on. + We use the shader program to draw the squircle in \c paint(). \snippet scenegraph/openglunderqml/main.cpp 1 diff --git a/examples/quick/scenegraph/openglunderqml/squircle.cpp b/examples/quick/scenegraph/openglunderqml/squircle.cpp index 4e5848be2a..828857fe24 100644 --- a/examples/quick/scenegraph/openglunderqml/squircle.cpp +++ b/examples/quick/scenegraph/openglunderqml/squircle.cpp @@ -83,10 +83,9 @@ void Squircle::handleWindowChanged(QQuickWindow *win) connect(win, &QQuickWindow::beforeSynchronizing, this, &Squircle::sync, Qt::DirectConnection); connect(win, &QQuickWindow::sceneGraphInvalidated, this, &Squircle::cleanup, Qt::DirectConnection); //! [1] - // If we allow QML to do the clearing, they would clear what we paint - // and nothing would show. //! [3] - win->setClearBeforeRendering(false); + // Ensure we start with cleared to black. The squircle's blend mode relies on this. + win->setColor(Qt::black); } } //! [3] @@ -124,7 +123,8 @@ void Squircle::sync() { if (!m_renderer) { m_renderer = new SquircleRenderer(); - connect(window(), &QQuickWindow::beforeRendering, m_renderer, &SquircleRenderer::paint, Qt::DirectConnection); + connect(window(), &QQuickWindow::beforeRendering, m_renderer, &SquircleRenderer::init, Qt::DirectConnection); + connect(window(), &QQuickWindow::beforeRenderPassRecording, m_renderer, &SquircleRenderer::paint, Qt::DirectConnection); } m_renderer->setViewportSize(window()->size() * window()->devicePixelRatio()); m_renderer->setT(m_t); @@ -133,9 +133,12 @@ void Squircle::sync() //! [9] //! [4] -void SquircleRenderer::paint() +void SquircleRenderer::init() { if (!m_program) { + QSGRendererInterface *rif = m_window->rendererInterface(); + Q_ASSERT(rif->graphicsApi() == QSGRendererInterface::OpenGL || rif->graphicsApi() == QSGRendererInterface::OpenGLRhi); + initializeOpenGLFunctions(); m_program = new QOpenGLShaderProgram(); @@ -160,7 +163,15 @@ void SquircleRenderer::paint() m_program->link(); } +} + //! [4] //! [5] +void SquircleRenderer::paint() +{ + // Play nice with the RHI. Not strictly needed when the scenegraph uses + // OpenGL directly. + m_window->beginExternalCommands(); + m_program->bind(); m_program->enableAttributeArray(0); @@ -171,6 +182,11 @@ void SquircleRenderer::paint() -1, 1, 1, 1 }; + + // This example relies on (deprecated) client-side pointers for the vertex + // input. Therefore, we have to make sure no vertex buffer is bound. + glBindBuffer(GL_ARRAY_BUFFER, 0); + m_program->setAttributeArray(0, GL_FLOAT, values, 2); m_program->setUniformValue("t", (float) m_t); @@ -178,9 +194,6 @@ void SquircleRenderer::paint() glDisable(GL_DEPTH_TEST); - glClearColor(0, 0, 0, 1); - glClear(GL_COLOR_BUFFER_BIT); - glEnable(GL_BLEND); glBlendFunc(GL_SRC_ALPHA, GL_ONE); @@ -192,5 +205,7 @@ void SquircleRenderer::paint() // Not strictly needed for this example, but generally useful for when // mixing with raw OpenGL. m_window->resetOpenGLState(); + + m_window->endExternalCommands(); } //! [5] diff --git a/examples/quick/scenegraph/openglunderqml/squircle.h b/examples/quick/scenegraph/openglunderqml/squircle.h index 962ba21101..1b9995bc1e 100644 --- a/examples/quick/scenegraph/openglunderqml/squircle.h +++ b/examples/quick/scenegraph/openglunderqml/squircle.h @@ -70,6 +70,7 @@ public: void setWindow(QQuickWindow *window) { m_window = window; } public slots: + void init(); void paint(); private: -- cgit v1.2.3