rendernode example: Add support for Metal
Plus clarify QQuickWindow::begin/endExternalCommands() in combination with QSGRenderNode in the docs. As the example demonstrates, calling these functions is not necessary within render() of a render node. Also fix an issue with resetting the scissor in the renderer after calling render() of a QSGRenderNode. Change-Id: If8c2dab38d62aa444266d37901f062a51e767f68 Reviewed-by: Christian Strømme <christian.stromme@qt.io>
This commit is contained in:
parent
ef2715251e
commit
7dcdf3b7ae
|
@ -53,6 +53,7 @@
|
|||
#include <QSGRendererInterface>
|
||||
|
||||
#include "openglrenderer.h"
|
||||
#include "metalrenderer.h"
|
||||
#include "d3d12renderer.h"
|
||||
#include "softwarerenderer.h"
|
||||
|
||||
|
@ -82,6 +83,18 @@ QSGNode *CustomRenderItem::updatePaintNode(QSGNode *node, UpdatePaintNodeData *)
|
|||
#endif
|
||||
break;
|
||||
|
||||
case QSGRendererInterface::MetalRhi:
|
||||
#ifdef Q_OS_DARWIN
|
||||
{
|
||||
MetalRenderNode *metalNode = new MetalRenderNode(this);
|
||||
n = metalNode;
|
||||
metalNode->resourceBuilder()->setWindow(window());
|
||||
QObject::connect(window(), &QQuickWindow::beforeRendering,
|
||||
metalNode->resourceBuilder(), &MetalRenderNodeResourceBuilder::build);
|
||||
}
|
||||
#endif
|
||||
break;
|
||||
|
||||
case QSGRendererInterface::Direct3D12: // ### Qt 6: remove
|
||||
#if QT_CONFIG(d3d12)
|
||||
n = new D3D12RenderNode(this);
|
||||
|
@ -102,3 +115,6 @@ QSGNode *CustomRenderItem::updatePaintNode(QSGNode *node, UpdatePaintNodeData *)
|
|||
return n;
|
||||
}
|
||||
//! [2]
|
||||
|
||||
// This item does not support being moved between windows. If that is desired,
|
||||
// itemChange() should be reimplemented as well.
|
||||
|
|
|
@ -38,7 +38,9 @@
|
|||
Metal, Direct 3D, or OpenGL). This example demonstrates implementing a
|
||||
custom QQuickItem backed by a QSGRenderNode implementation, where the node
|
||||
renders a triangle directly via the graphics API. The rest of the scene
|
||||
(background, text, rectangles) are standard Qt Quick items.
|
||||
(background, text, rectangles) are standard Qt Quick items. The example has
|
||||
full support for OpenGL and Metal, as well as the software backend of Qt
|
||||
Quick.
|
||||
|
||||
The custom item behaves like any other Qt Quick item, meaning it
|
||||
participates and stacking and clipping as usual, which is a big difference
|
||||
|
|
|
@ -146,6 +146,7 @@ Item {
|
|||
case GraphicsInfo.Direct3D12: apiStr = "Direct3D 12 (direct)"; break;
|
||||
case GraphicsInfo.Software: apiStr = "Software (QPainter)"; break;
|
||||
case GraphicsInfo.OpenGLRhi: apiStr = "OpenGL (RHI)"; break;
|
||||
case GraphicsInfo.MetalRhi: apiStr = "Metal (RHI)"; break;
|
||||
// the example has no other QSGRenderNode subclasses
|
||||
default: apiStr = "<UNSUPPORTED>"; break;
|
||||
}
|
||||
|
|
|
@ -0,0 +1,100 @@
|
|||
/****************************************************************************
|
||||
**
|
||||
** 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$
|
||||
**
|
||||
****************************************************************************/
|
||||
|
||||
#ifndef METALRENDERER_H
|
||||
#define METALRENDERER_H
|
||||
|
||||
#include <qsgrendernode.h>
|
||||
|
||||
#ifdef Q_OS_DARWIN
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
|
||||
class QQuickItem;
|
||||
class QQuickWindow;
|
||||
|
||||
QT_END_NAMESPACE
|
||||
|
||||
class MetalRenderNodeResourceBuilder : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
void setWindow(QQuickWindow *w) { m_window = w; }
|
||||
|
||||
public slots:
|
||||
void build();
|
||||
|
||||
private:
|
||||
QQuickWindow *m_window = nullptr;
|
||||
};
|
||||
|
||||
class MetalRenderNode : public QSGRenderNode
|
||||
{
|
||||
public:
|
||||
MetalRenderNode(QQuickItem *item);
|
||||
~MetalRenderNode();
|
||||
|
||||
void render(const RenderState *state) override;
|
||||
void releaseResources() override;
|
||||
StateFlags changedStates() const override;
|
||||
RenderingFlags flags() const override;
|
||||
QRectF rect() const override;
|
||||
|
||||
MetalRenderNodeResourceBuilder *resourceBuilder() { return &m_resourceBuilder; }
|
||||
|
||||
private:
|
||||
QQuickItem *m_item;
|
||||
MetalRenderNodeResourceBuilder m_resourceBuilder;
|
||||
};
|
||||
|
||||
#endif // Q_OS_DARWIN
|
||||
|
||||
#endif
|
|
@ -0,0 +1,326 @@
|
|||
/****************************************************************************
|
||||
**
|
||||
** 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$
|
||||
**
|
||||
****************************************************************************/
|
||||
|
||||
#include "metalrenderer.h"
|
||||
#include <QQuickItem>
|
||||
#include <QQuickWindow>
|
||||
|
||||
#include <Metal/Metal.h>
|
||||
|
||||
using FuncAndLib = QPair<id<MTLFunction>, id<MTLLibrary> >;
|
||||
|
||||
const int MAX_FRAMES_IN_FLIGHT = 3;
|
||||
|
||||
struct {
|
||||
id<MTLDevice> dev = nil;
|
||||
QByteArray vsSource;
|
||||
FuncAndLib vs;
|
||||
QByteArray fsSource;
|
||||
FuncAndLib fs;
|
||||
id<MTLBuffer> vbuf[MAX_FRAMES_IN_FLIGHT];
|
||||
id<MTLBuffer> ubuf[MAX_FRAMES_IN_FLIGHT];
|
||||
id<MTLDepthStencilState> stencilEnabledDsState = nil;
|
||||
id<MTLRenderPipelineState> pipeline = nil;
|
||||
} g;
|
||||
|
||||
static FuncAndLib 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 = [g.dev 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;
|
||||
}
|
||||
|
||||
const int VERTEX_SIZE = 6 * sizeof(float);
|
||||
|
||||
static float colors[] = {
|
||||
1.0f, 0.0f, 0.0f,
|
||||
0.0f, 1.0f, 0.0f,
|
||||
0.0f, 0.0f, 1.0f
|
||||
};
|
||||
|
||||
void MetalRenderNodeResourceBuilder::build()
|
||||
{
|
||||
if (!g.dev) {
|
||||
QSGRendererInterface *rif = m_window->rendererInterface();
|
||||
Q_ASSERT(rif->graphicsApi() == QSGRendererInterface::MetalRhi);
|
||||
|
||||
g.dev = (id<MTLDevice>) rif->getResource(m_window, QSGRendererInterface::DeviceResource);
|
||||
Q_ASSERT(g.dev);
|
||||
}
|
||||
|
||||
if (g.vsSource.isEmpty()) {
|
||||
const QString filename = QLatin1String(":/scenegraph/rendernode/metalshader.vert");
|
||||
QFile f(filename);
|
||||
if (!f.open(QIODevice::ReadOnly | QIODevice::Text))
|
||||
qFatal("Failed to read shader %s", qPrintable(filename));
|
||||
g.vsSource = f.readAll();
|
||||
g.vs = compileShaderFromSource(g.vsSource, QByteArrayLiteral("main0"));
|
||||
}
|
||||
|
||||
if (g.fsSource.isEmpty()) {
|
||||
const QString filename = QLatin1String(":/scenegraph/rendernode/metalshader.frag");
|
||||
QFile f(filename);
|
||||
if (!f.open(QIODevice::ReadOnly | QIODevice::Text))
|
||||
qFatal("Failed to read shader %s", qPrintable(filename));
|
||||
g.fsSource = f.readAll();
|
||||
g.fs = compileShaderFromSource(g.fsSource, QByteArrayLiteral("main0"));
|
||||
}
|
||||
|
||||
const int framesInFlight = m_window->graphicsStateInfo()->framesInFlight;
|
||||
|
||||
// For simplicity's sake we use shared mode (something like host visible +
|
||||
// host coherent) for everything.
|
||||
|
||||
for (int i = 0; i < framesInFlight; ++i) {
|
||||
// Have multiple versions for vertex too since we'll just memcpy new
|
||||
// vertices based on item width and height on every render(). This could
|
||||
// be optimized further however.
|
||||
if (!g.vbuf[i]) {
|
||||
g.vbuf[i] = [g.dev newBufferWithLength: VERTEX_SIZE + sizeof(colors) options: MTLResourceStorageModeShared];
|
||||
char *p = (char *) [g.vbuf[i] contents];
|
||||
memcpy(p + VERTEX_SIZE, colors, sizeof(colors));
|
||||
}
|
||||
|
||||
if (!g.ubuf[i])
|
||||
g.ubuf[i] = [g.dev newBufferWithLength: 256 options: MTLResourceStorageModeShared];
|
||||
}
|
||||
|
||||
if (!g.stencilEnabledDsState) {
|
||||
MTLDepthStencilDescriptor *dsDesc = [[MTLDepthStencilDescriptor alloc] init];
|
||||
dsDesc.frontFaceStencil = [[MTLStencilDescriptor alloc] init];
|
||||
dsDesc.frontFaceStencil.stencilFailureOperation = MTLStencilOperationKeep;
|
||||
dsDesc.frontFaceStencil.depthFailureOperation = MTLStencilOperationKeep;
|
||||
dsDesc.frontFaceStencil.depthStencilPassOperation = MTLStencilOperationKeep;
|
||||
dsDesc.frontFaceStencil.stencilCompareFunction = MTLCompareFunctionEqual;
|
||||
dsDesc.frontFaceStencil.readMask = 0xFF;
|
||||
dsDesc.frontFaceStencil.writeMask = 0xFF;
|
||||
|
||||
dsDesc.backFaceStencil = [[MTLStencilDescriptor alloc] init];
|
||||
dsDesc.backFaceStencil.stencilFailureOperation = MTLStencilOperationKeep;
|
||||
dsDesc.backFaceStencil.depthFailureOperation = MTLStencilOperationKeep;
|
||||
dsDesc.backFaceStencil.depthStencilPassOperation = MTLStencilOperationKeep;
|
||||
dsDesc.backFaceStencil.stencilCompareFunction = MTLCompareFunctionEqual;
|
||||
dsDesc.backFaceStencil.readMask = 0xFF;
|
||||
dsDesc.backFaceStencil.writeMask = 0xFF;
|
||||
|
||||
g.stencilEnabledDsState = [g.dev newDepthStencilStateWithDescriptor: dsDesc];
|
||||
[dsDesc release];
|
||||
}
|
||||
|
||||
if (!g.pipeline) {
|
||||
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 and 2
|
||||
inputLayout.attributes[1].format = MTLVertexFormatFloat3;
|
||||
inputLayout.attributes[1].offset = 0;
|
||||
inputLayout.attributes[1].bufferIndex = 2;
|
||||
inputLayout.layouts[1].stride = 2 * sizeof(float);
|
||||
inputLayout.layouts[2].stride = 3 * sizeof(float);
|
||||
|
||||
MTLRenderPipelineDescriptor *rpDesc = [[MTLRenderPipelineDescriptor alloc] init];
|
||||
rpDesc.vertexDescriptor = inputLayout;
|
||||
|
||||
rpDesc.vertexFunction = g.vs.first;
|
||||
rpDesc.fragmentFunction = g.fs.first;
|
||||
|
||||
rpDesc.colorAttachments[0].pixelFormat = MTLPixelFormatBGRA8Unorm;
|
||||
rpDesc.colorAttachments[0].blendingEnabled = true;
|
||||
rpDesc.colorAttachments[0].sourceRGBBlendFactor = MTLBlendFactorOne;
|
||||
rpDesc.colorAttachments[0].sourceAlphaBlendFactor = MTLBlendFactorOne;
|
||||
rpDesc.colorAttachments[0].destinationRGBBlendFactor = MTLBlendFactorOneMinusSourceAlpha;
|
||||
rpDesc.colorAttachments[0].destinationAlphaBlendFactor = MTLBlendFactorOneMinusSourceAlpha;
|
||||
|
||||
if (g.dev.depth24Stencil8PixelFormatSupported) {
|
||||
rpDesc.depthAttachmentPixelFormat = MTLPixelFormatDepth24Unorm_Stencil8;
|
||||
rpDesc.stencilAttachmentPixelFormat = MTLPixelFormatDepth24Unorm_Stencil8;
|
||||
} else {
|
||||
rpDesc.depthAttachmentPixelFormat = MTLPixelFormatDepth32Float_Stencil8;
|
||||
rpDesc.stencilAttachmentPixelFormat = MTLPixelFormatDepth32Float_Stencil8;
|
||||
}
|
||||
|
||||
NSError *err = nil;
|
||||
g.pipeline = [g.dev newRenderPipelineStateWithDescriptor: rpDesc error: &err];
|
||||
if (!g.pipeline) {
|
||||
const QString msg = QString::fromNSString(err.localizedDescription);
|
||||
qFatal("Failed to create render pipeline state: %s", qPrintable(msg));
|
||||
}
|
||||
[rpDesc release];
|
||||
}
|
||||
}
|
||||
|
||||
MetalRenderNode::MetalRenderNode(QQuickItem *item)
|
||||
: m_item(item)
|
||||
{
|
||||
g.vs.first = g.fs.first = nil;
|
||||
g.vs.second = g.fs.second = nil;
|
||||
|
||||
for (int i = 0; i < MAX_FRAMES_IN_FLIGHT; ++i) {
|
||||
g.vbuf[i] = nil;
|
||||
g.ubuf[i] = nil;
|
||||
}
|
||||
}
|
||||
|
||||
MetalRenderNode::~MetalRenderNode()
|
||||
{
|
||||
releaseResources();
|
||||
}
|
||||
|
||||
void MetalRenderNode::releaseResources()
|
||||
{
|
||||
[g.stencilEnabledDsState release];
|
||||
g.stencilEnabledDsState = nil;
|
||||
|
||||
[g.pipeline release];
|
||||
g.pipeline = nil;
|
||||
|
||||
[g.vs.first release];
|
||||
[g.vs.second release];
|
||||
|
||||
[g.fs.first release];
|
||||
[g.fs.second release];
|
||||
|
||||
g.vs.first = g.fs.first = nil;
|
||||
g.vs.second = g.fs.second = nil;
|
||||
|
||||
for (int i = 0; i < MAX_FRAMES_IN_FLIGHT; ++i) {
|
||||
[g.vbuf[i] release];
|
||||
g.vbuf[i] = nil;
|
||||
[g.ubuf[i] release];
|
||||
g.ubuf[i] = nil;
|
||||
}
|
||||
}
|
||||
|
||||
void MetalRenderNode::render(const RenderState *state)
|
||||
{
|
||||
QQuickWindow *window = m_item->window();
|
||||
const QQuickWindow::GraphicsStateInfo *stateInfo = window->graphicsStateInfo();
|
||||
id<MTLBuffer> vbuf = g.vbuf[stateInfo->currentFrameSlot];
|
||||
id<MTLBuffer> ubuf = g.ubuf[stateInfo->currentFrameSlot];
|
||||
|
||||
QPointF p0(m_item->width() - 1, m_item->height() - 1);
|
||||
QPointF p1(0, 0);
|
||||
QPointF p2(0, m_item->height() - 1);
|
||||
|
||||
float vertices[6] = { float(p0.x()), float(p0.y()),
|
||||
float(p1.x()), float(p1.y()),
|
||||
float(p2.x()), float(p2.y()) };
|
||||
char *p = (char *) [vbuf contents];
|
||||
memcpy(p, vertices, VERTEX_SIZE);
|
||||
|
||||
const QMatrix4x4 mvp = *state->projectionMatrix() * *matrix();
|
||||
const float opacity = inheritedOpacity();
|
||||
|
||||
p = (char *) [ubuf contents];
|
||||
memcpy(p, mvp.constData(), 64);
|
||||
memcpy(p + 64, &opacity, 4);
|
||||
|
||||
QSGRendererInterface *rif = window->rendererInterface();
|
||||
id<MTLRenderCommandEncoder> encoder = (id<MTLRenderCommandEncoder>) rif->getResource(
|
||||
window, QSGRendererInterface::CommandEncoderResource);
|
||||
Q_ASSERT(encoder);
|
||||
|
||||
[encoder setVertexBuffer: vbuf offset: 0 atIndex: 1];
|
||||
[encoder setVertexBuffer: vbuf offset: VERTEX_SIZE atIndex: 2];
|
||||
|
||||
[encoder setVertexBuffer: ubuf offset: 0 atIndex: 0];
|
||||
[encoder setFragmentBuffer: ubuf offset: 0 atIndex: 0];
|
||||
|
||||
// Clip support.
|
||||
if (state->scissorEnabled()) {
|
||||
const QRect r = state->scissorRect(); // bottom-up
|
||||
MTLScissorRect s;
|
||||
s.x = r.x();
|
||||
s.y = (window->height() * window->effectiveDevicePixelRatio()) - (r.y() + r.height());
|
||||
s.width = r.width();
|
||||
s.height = r.height();
|
||||
[encoder setScissorRect: s];
|
||||
}
|
||||
if (state->stencilEnabled()) {
|
||||
[encoder setDepthStencilState: g.stencilEnabledDsState];
|
||||
[encoder setStencilReferenceValue: state->stencilValue()];
|
||||
}
|
||||
|
||||
[encoder setRenderPipelineState: g.pipeline];
|
||||
[encoder drawPrimitives: MTLPrimitiveTypeTriangle vertexStart: 0 vertexCount: 3 instanceCount: 1 baseInstance: 0];
|
||||
}
|
||||
|
||||
QSGRenderNode::StateFlags MetalRenderNode::changedStates() const
|
||||
{
|
||||
return BlendState | ScissorState | StencilState;
|
||||
}
|
||||
|
||||
QSGRenderNode::RenderingFlags MetalRenderNode::flags() const
|
||||
{
|
||||
return BoundedRectRendering | DepthAwareRendering;
|
||||
}
|
||||
|
||||
QRectF MetalRenderNode::rect() const
|
||||
{
|
||||
return QRect(0, 0, m_item->width(), m_item->height());
|
||||
}
|
|
@ -0,0 +1,28 @@
|
|||
#include <metal_stdlib>
|
||||
#include <simd/simd.h>
|
||||
|
||||
using namespace metal;
|
||||
|
||||
struct buf
|
||||
{
|
||||
float4x4 matrix;
|
||||
float opacity;
|
||||
};
|
||||
|
||||
struct main0_out
|
||||
{
|
||||
float4 fragColor [[color(0)]];
|
||||
};
|
||||
|
||||
struct main0_in
|
||||
{
|
||||
float4 v_color [[user(locn0)]];
|
||||
};
|
||||
|
||||
fragment main0_out main0(main0_in in [[stage_in]], constant buf& ubuf [[buffer(0)]])
|
||||
{
|
||||
main0_out out = {};
|
||||
out.fragColor = in.v_color * ubuf.opacity;
|
||||
return out;
|
||||
}
|
||||
|
|
@ -0,0 +1,31 @@
|
|||
#include <metal_stdlib>
|
||||
#include <simd/simd.h>
|
||||
|
||||
using namespace metal;
|
||||
|
||||
struct buf
|
||||
{
|
||||
float4x4 matrix;
|
||||
float opacity;
|
||||
};
|
||||
|
||||
struct main0_out
|
||||
{
|
||||
float4 v_color [[user(locn0)]];
|
||||
float4 gl_Position [[position]];
|
||||
};
|
||||
|
||||
struct main0_in
|
||||
{
|
||||
float4 pos [[attribute(0)]];
|
||||
float4 color [[attribute(1)]];
|
||||
};
|
||||
|
||||
vertex main0_out main0(main0_in in [[stage_in]], constant buf& ubuf [[buffer(0)]])
|
||||
{
|
||||
main0_out out = {};
|
||||
out.v_color = in.color;
|
||||
out.gl_Position = ubuf.matrix * in.pos;
|
||||
return out;
|
||||
}
|
||||
|
|
@ -22,3 +22,9 @@ qtConfig(d3d12) {
|
|||
SOURCES += d3d12renderer.cpp
|
||||
LIBS += -ld3d12
|
||||
}
|
||||
|
||||
macos {
|
||||
HEADERS += metalrenderer.h
|
||||
SOURCES += metalrenderer.mm
|
||||
LIBS += -framework Metal -framework AppKit
|
||||
}
|
||||
|
|
|
@ -3,5 +3,7 @@
|
|||
<file>main.qml</file>
|
||||
<file>shader_vert.cso</file>
|
||||
<file>shader_frag.cso</file>
|
||||
<file>metalshader.vert</file>
|
||||
<file>metalshader.frag</file>
|
||||
</qresource>
|
||||
</RCC>
|
||||
|
|
|
@ -4666,6 +4666,11 @@ const QQuickWindow::GraphicsStateInfo *QQuickWindow::graphicsStateInfo()
|
|||
beginExternalCommands() and endExternalCommands() together provide a
|
||||
replacement for resetOpenGLState().
|
||||
|
||||
Calling this function and endExternalCommands() is not necessary within the
|
||||
\l{QSGRenderNode::render()}{render()} implementation of a QSGRenderNode
|
||||
because the scene graph performs the necessary steps implicitly for render
|
||||
nodes.
|
||||
|
||||
\note This function has no effect when the scene graph is using OpenGL
|
||||
directly and the RHI graphics abstraction layer is not in use. Refer to
|
||||
resetOpenGLState() in that case.
|
||||
|
@ -4704,6 +4709,11 @@ void QQuickWindow::beginExternalCommands()
|
|||
beginExternalCommands() and endExternalCommands() together provide a
|
||||
replacement for resetOpenGLState().
|
||||
|
||||
Calling this function and beginExternalCommands() is not necessary within the
|
||||
\l{QSGRenderNode::render()}{render()} implementation of a QSGRenderNode
|
||||
because the scene graph performs the necessary steps implicitly for render
|
||||
nodes.
|
||||
|
||||
\note This function has no effect when the scene graph is using OpenGL
|
||||
directly and the RHI graphics abstraction layer is not in use. Refer to
|
||||
resetOpenGLState() in that case.
|
||||
|
|
|
@ -4563,11 +4563,14 @@ void Renderer::renderRhiRenderNode(const Batch *batch) // split prepare-render (
|
|||
rd->m_matrix = nullptr;
|
||||
rd->m_clip_list = nullptr;
|
||||
|
||||
if (changes & QSGRenderNode::ViewportState)
|
||||
if ((changes & QSGRenderNode::ViewportState)
|
||||
|| (changes & QSGRenderNode::ScissorState))
|
||||
{
|
||||
// Reset both flags if either is reported as changed, since with the rhi
|
||||
// it could be setViewport() that will record the resetting of the scissor.
|
||||
m_pstate.viewportSet = false;
|
||||
|
||||
if (changes & QSGRenderNode::ScissorState)
|
||||
m_pstate.scissorSet = false;
|
||||
}
|
||||
|
||||
// Do not bother with RenderTargetState. Where applicable, endExternal()
|
||||
// ensures the correct target is rebound. For others (like Vulkan) it makes
|
||||
|
|
Loading…
Reference in New Issue