diff options
author | Alexandru Croitor <alexandru.croitor@qt.io> | 2019-07-11 14:51:40 +0200 |
---|---|---|
committer | Alexandru Croitor <alexandru.croitor@qt.io> | 2019-07-11 17:24:39 +0200 |
commit | 13374ceb165c44658aa97890c37b206859c9a31c (patch) | |
tree | 562362b196a459ee3449a5a1e60e5216a9dd6984 /examples | |
parent | ae47deba4c943c496412530a8d2a5a688ae12038 (diff) | |
parent | b5d18be5a03406d0aac83856dd41e1525fd14a28 (diff) |
Diffstat (limited to 'examples')
40 files changed, 1807 insertions, 98 deletions
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); } 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 <QImage> #include <QThreadPool> -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; } 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 == "") { 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 <QtCore/QRunnable> +#include <QtQuick/QQuickWindow> + +#include <d3d11.h> +#include <d3dcompiler.h> + +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<ID3D11Device *>(rif->getResource(m_window, QSGRendererInterface::DeviceResource)); + Q_ASSERT(m_device); + m_context = reinterpret_cast<ID3D11DeviceContext *>(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<const char *>(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<location> 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 <QtQuick/QQuickItem> + +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 @@ +<RCC> + <qresource prefix="/scenegraph/d3d11underqml"> + <file>main.qml</file> + <file>squircle.vert</file> + <file>squircle.frag</file> + </qresource> +</RCC> 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 <QGuiApplication> +#include <QtQuick/QQuickView> +#include "d3d11squircle.h" + +int main(int argc, char **argv) +{ + QGuiApplication app(argc, argv); + + qmlRegisterType<D3D11Squircle>("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 <QGuiApplication> +#include <QtQuick/QQuickView> +#include "metalsquircle.h" + +int main(int argc, char **argv) +{ + QGuiApplication app(argc, argv); + + qmlRegisterType<MetalSquircle>("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 <QtQuick/QQuickItem> + +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 <QtCore/QRunnable> +#include <QtQuick/QQuickWindow> + +#include <Metal/Metal.h> + +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<MTLFunction>, id<MTLLibrary> >; + 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<MTLDevice> m_device; + id<MTLBuffer> m_vbuf; + id<MTLBuffer> m_ubuf[3]; + FuncAndLib m_vs; + FuncAndLib m_fs; + id<MTLRenderPipelineState> 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<MTLDevice>) 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<MTLRenderCommandEncoder> encoder = (id<MTLRenderCommandEncoder>) 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 @@ +<RCC> + <qresource prefix="/scenegraph/metalunderqml"> + <file>main.qml</file> + <file>squircle.vert</file> + <file>squircle.frag</file> + </qresource> +</RCC> 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 <metal_stdlib> +#include <simd/simd.h> + +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 <metal_stdlib> +#include <simd/simd.h> + +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..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 @@ -112,8 +113,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 @@ -124,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 d6f6b327f2..828857fe24 100644 --- a/examples/quick/scenegraph/openglunderqml/squircle.cpp +++ b/examples/quick/scenegraph/openglunderqml/squircle.cpp @@ -53,6 +53,7 @@ #include <QtQuick/qquickwindow.h> #include <QtGui/QOpenGLShaderProgram> #include <QtGui/QOpenGLContext> +#include <QtCore/QRunnable> //! [7] Squircle::Squircle() @@ -82,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] @@ -93,10 +93,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() @@ -110,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); @@ -119,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(); @@ -146,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); @@ -157,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); @@ -164,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); @@ -178,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 652e679f81..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: @@ -103,6 +104,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 Binary files differnew file mode 100644 index 0000000000..1c79359297 --- /dev/null +++ b/examples/quick/shadereffects/content/shaders/+qsb/blur.frag diff --git a/examples/quick/shadereffects/content/shaders/+qsb/colorize.frag b/examples/quick/shadereffects/content/shaders/+qsb/colorize.frag Binary files differnew file mode 100644 index 0000000000..45c5301f31 --- /dev/null +++ b/examples/quick/shadereffects/content/shaders/+qsb/colorize.frag diff --git a/examples/quick/shadereffects/content/shaders/+qsb/genie.vert b/examples/quick/shadereffects/content/shaders/+qsb/genie.vert Binary files differnew file mode 100644 index 0000000000..dd94129cf7 --- /dev/null +++ b/examples/quick/shadereffects/content/shaders/+qsb/genie.vert diff --git a/examples/quick/shadereffects/content/shaders/+qsb/outline.frag b/examples/quick/shadereffects/content/shaders/+qsb/outline.frag Binary files differnew file mode 100644 index 0000000000..470e2bd6e6 --- /dev/null +++ b/examples/quick/shadereffects/content/shaders/+qsb/outline.frag diff --git a/examples/quick/shadereffects/content/shaders/+qsb/shadow.frag b/examples/quick/shadereffects/content/shaders/+qsb/shadow.frag Binary files differnew file mode 100644 index 0000000000..128af21daa --- /dev/null +++ b/examples/quick/shadereffects/content/shaders/+qsb/shadow.frag diff --git a/examples/quick/shadereffects/content/shaders/+qsb/wobble.frag b/examples/quick/shadereffects/content/shaders/+qsb/wobble.frag Binary files differnew file mode 100644 index 0000000000..9b27ae87cb --- /dev/null +++ b/examples/quick/shadereffects/content/shaders/+qsb/wobble.frag 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 @@ <file>content/Slider.qml</file> <file>content/shaders/wobble.frag</file> <file>content/shaders/+hlsl/wobble.frag</file> + <file>content/shaders/+qsb/wobble.frag</file> <file>content/shaders/blur.frag</file> <file>content/shaders/+hlsl/blur.frag</file> + <file>content/shaders/+qsb/blur.frag</file> <file>content/shaders/shadow.frag</file> <file>content/shaders/+hlsl/shadow.frag</file> + <file>content/shaders/+qsb/shadow.frag</file> <file>content/shaders/outline.frag</file> <file>content/shaders/+hlsl/outline.frag</file> + <file>content/shaders/+qsb/outline.frag</file> <file>content/shaders/colorize.frag</file> <file>content/shaders/+hlsl/colorize.frag</file> + <file>content/shaders/+qsb/colorize.frag</file> <file>content/shaders/genie.vert</file> <file>content/shaders/+hlsl/genie.vert</file> + <file>content/shaders/+qsb/genie.vert</file> </qresource> </RCC> |