diff options
Diffstat (limited to 'examples/quick/scenegraph/metalunderqml')
-rw-r--r-- | examples/quick/scenegraph/metalunderqml/doc/images/metalunderqml-example.jpg | bin | 0 -> 55194 bytes | |||
-rw-r--r-- | examples/quick/scenegraph/metalunderqml/doc/src/metalunderqml.qdoc | 69 | ||||
-rw-r--r-- | examples/quick/scenegraph/metalunderqml/main.cpp | 69 | ||||
-rw-r--r-- | examples/quick/scenegraph/metalunderqml/main.qml | 89 | ||||
-rw-r--r-- | examples/quick/scenegraph/metalunderqml/metalsquircle.h | 86 | ||||
-rw-r--r-- | examples/quick/scenegraph/metalunderqml/metalsquircle.mm | 369 | ||||
-rw-r--r-- | examples/quick/scenegraph/metalunderqml/metalunderqml.pro | 12 | ||||
-rw-r--r-- | examples/quick/scenegraph/metalunderqml/metalunderqml.qrc | 7 | ||||
-rw-r--r-- | examples/quick/scenegraph/metalunderqml/squircle.frag | 29 | ||||
-rw-r--r-- | examples/quick/scenegraph/metalunderqml/squircle.vert | 23 |
10 files changed, 753 insertions, 0 deletions
diff --git a/examples/quick/scenegraph/metalunderqml/doc/images/metalunderqml-example.jpg b/examples/quick/scenegraph/metalunderqml/doc/images/metalunderqml-example.jpg Binary files differnew file mode 100644 index 0000000000..98085773de --- /dev/null +++ b/examples/quick/scenegraph/metalunderqml/doc/images/metalunderqml-example.jpg diff --git a/examples/quick/scenegraph/metalunderqml/doc/src/metalunderqml.qdoc b/examples/quick/scenegraph/metalunderqml/doc/src/metalunderqml.qdoc new file mode 100644 index 0000000000..fb28270315 --- /dev/null +++ b/examples/quick/scenegraph/metalunderqml/doc/src/metalunderqml.qdoc @@ -0,0 +1,69 @@ +/**************************************************************************** +** +** Copyright (C) 2019 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the documentation of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:FDL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Free Documentation License Usage +** Alternatively, this file may be used under the terms of the GNU Free +** Documentation License version 1.3 as published by the Free Software +** Foundation and appearing in the file included in the packaging of +** this file. Please review the following information to ensure +** the GNU Free Documentation License version 1.3 requirements +** will be met: https://www.gnu.org/licenses/fdl-1.3.html. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +/*! + \example scenegraph/metalunderqml + \title Scene Graph - Metal Under QML + \ingroup qtquickexamples + \brief Shows how to render directly with Metal under a Qt Quick scene. + + \image metalunderqml-example.jpg + + The Metal Under QML example shows how an application can make use + of the \l QQuickWindow::beforeRendering() and \l + QQuickWindow::beforeRenderPassRecording() signals to draw custom + Metal content under a Qt Quick scene. This signal is emitted at + the start of every frame, before the scene graph starts its + rendering, thus any Metal draw calls that are made as a response + to this signal, will stack under the Qt Quick items. There are two + signals, because the custom Metal commands are recorded onto the + same command buffer with the same render command encoder that the + scene graph uses. beforeRendering() on its own is not sufficient + for this because it gets emitted at the start of the frame, before + having an + \l{https://developer.apple.com/documentation/metal/mtlrendercommandencoder}{MTLRenderCommandEncoder} + available. By also connecting to beforeRenderPassRecording(), the + application can gain access to the necessary native objects. + + As an alternative, applications that wish to render Metal content + on top of the Qt Quick scene, can do so by connecting to the \l + QQuickWindow::afterRendering() and \l + QQuickWindow::afterRenderPassRecording() signals. + + In this example, we will also see how it is possible to have + values that are exposed to QML which affect the Metal + rendering. We animate the threshold value using a NumberAnimation + in the QML file and this value is used by the Metal shader + program that draws the squircles. + + The example is equivalent in most ways to the \l{Scene Graph - OpenGL Under + QML}{OpenGL Under QML}, \l{Scene Graph - Direct3D 11 Under QML}{Direct3D 11 + Under QML}, and \l{Scene Graph - Vulkan Under QML}{Vulkan Under QML} + examples, they all render the same custom content, just via different + native APIs. + + */ 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..5ca6daa01a --- /dev/null +++ b/examples/quick/scenegraph/metalunderqml/metalsquircle.mm @@ -0,0 +1,369 @@ +/**************************************************************************** +** +** 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; + // Initializing resources is done before starting to encode render + // commands, regardless of wanting an underlay or overlay. + connect(window(), &QQuickWindow::beforeRendering, m_renderer, &SquircleRenderer::frameStart, Qt::DirectConnection); + // Here we want an underlay and therefore connect to + // beforeRenderPassRecording. Changing to afterRenderPassRecording + // would render the squircle on top (overlay). + 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..9fd131fe1b --- /dev/null +++ b/examples/quick/scenegraph/metalunderqml/metalunderqml.pro @@ -0,0 +1,12 @@ +!macos: error("This example requires macOS") + +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; +} |