rendernode example: Prevent gui-render thread data race

By taking things like the item width and height in updatePaintNode()
(while gui is locked).

Change-Id: I840c6c858a0478eb6ceb09653fd1e033cb54372d
Reviewed-by: Andy Nichols <andy.nichols@qt.io>
This commit is contained in:
Laszlo Agocs 2019-08-29 10:16:07 +02:00
parent ee702d9436
commit dd523fcb00
9 changed files with 118 additions and 85 deletions

View File

@ -70,47 +70,55 @@ CustomRenderItem::CustomRenderItem(QQuickItem *parent)
QSGNode *CustomRenderItem::updatePaintNode(QSGNode *node, UpdatePaintNodeData *)
{
QSGRenderNode *n = static_cast<QSGRenderNode *>(node);
if (!n) {
QSGRendererInterface *ri = window()->rendererInterface();
if (!ri)
return nullptr;
switch (ri->graphicsApi()) {
case QSGRendererInterface::OpenGL:
Q_FALLTHROUGH();
case QSGRendererInterface::OpenGLRhi:
#if QT_CONFIG(opengl)
n = new OpenGLRenderNode(this);
if (!n)
n = new OpenGLRenderNode;
static_cast<OpenGLRenderNode *>(n)->sync(this);
#endif
break;
case QSGRendererInterface::MetalRhi:
#ifdef Q_OS_DARWIN
{
MetalRenderNode *metalNode = new MetalRenderNode(this);
if (!n) {
MetalRenderNode *metalNode = new MetalRenderNode;
n = metalNode;
metalNode->resourceBuilder()->setWindow(window());
QObject::connect(window(), &QQuickWindow::beforeRendering,
metalNode->resourceBuilder(), &MetalRenderNodeResourceBuilder::build);
}
static_cast<MetalRenderNode *>(n)->sync(this);
#endif
break;
case QSGRendererInterface::Direct3D12: // ### Qt 6: remove
#if QT_CONFIG(d3d12)
n = new D3D12RenderNode(this);
if (!n)
n = new D3D12RenderNode;
static_cast<D3D12RenderNode *>(n)->sync(this);
#endif
break;
case QSGRendererInterface::Software:
n = new SoftwareRenderNode(this);
if (!n)
n = new SoftwareRenderNode;
static_cast<SoftwareRenderNode *>(n)->sync(this);
break;
default:
break;
}
if (!n)
qWarning("QSGRendererInterface reports unknown graphics API %d", ri->graphicsApi());
}
return n;
}

View File

@ -58,11 +58,6 @@
#if QT_CONFIG(d3d12)
D3D12RenderNode::D3D12RenderNode(QQuickItem *item)
: m_item(item)
{
}
D3D12RenderNode::~D3D12RenderNode()
{
releaseResources();
@ -87,8 +82,8 @@ void D3D12RenderNode::releaseResources()
void D3D12RenderNode::init()
{
QSGRendererInterface *rif = m_item->window()->rendererInterface();
m_device = static_cast<ID3D12Device *>(rif->getResource(m_item->window(), QSGRendererInterface::DeviceResource));
QSGRendererInterface *rif = m_window->rendererInterface();
m_device = static_cast<ID3D12Device *>(rif->getResource(m_window, QSGRendererInterface::DeviceResource));
Q_ASSERT(m_device);
D3D12_ROOT_PARAMETER rootParameter;
@ -178,7 +173,7 @@ void D3D12RenderNode::init()
psoDesc.DSVFormat = DXGI_FORMAT_D24_UNORM_S8_UINT; // not in use due to !DepthEnable, but this would be the correct format otherwise
// We are rendering on the default render target so if the QuickWindow/View
// has requested samples > 0 then we have to follow suit.
const uint samples = qMax(1, m_item->window()->format().samples());
const uint samples = qMax(1, m_window->format().samples());
psoDesc.SampleDesc.Count = samples;
if (samples > 1) {
D3D12_FEATURE_DATA_MULTISAMPLE_QUALITY_LEVELS msaaInfo = {};
@ -257,9 +252,9 @@ void D3D12RenderNode::render(const RenderState *state)
if (!m_device)
init();
QSGRendererInterface *rif = m_item->window()->rendererInterface();
QSGRendererInterface *rif = m_window->rendererInterface();
ID3D12GraphicsCommandList *commandList = static_cast<ID3D12GraphicsCommandList *>(
rif->getResource(m_item->window(), QSGRendererInterface::CommandListResource));
rif->getResource(m_window, QSGRendererInterface::CommandListResource));
Q_ASSERT(commandList);
const int msize = 16 * sizeof(float);
@ -268,9 +263,9 @@ void D3D12RenderNode::render(const RenderState *state)
const float opacity = inheritedOpacity();
memcpy(cbPtr + 2 * msize, &opacity, sizeof(float));
const QPointF p0(m_item->width() - 1, m_item->height() - 1);
const QPointF p0(m_width - 1, m_height - 1);
const QPointF p1(0, 0);
const QPointF p2(0, m_item->height() - 1);
const QPointF p2(0, m_height - 1);
float *vp = reinterpret_cast<float *>(vbPtr);
*vp++ = p0.x();
@ -301,7 +296,14 @@ QSGRenderNode::RenderingFlags D3D12RenderNode::flags() const
QRectF D3D12RenderNode::rect() const
{
return QRect(0, 0, m_item->width(), m_item->height());
return QRect(0, 0, m_width, m_height);
}
void D3D12RenderNode::sync(QQuickItem *item)
{
m_window = item->window();
m_width = item->width();
m_height = item->height();
}
#endif // d3d12

View File

@ -52,11 +52,10 @@
#define D3D12RENDERER_H
#include <qsgrendernode.h>
#include <QQuickItem>
#if QT_CONFIG(d3d12)
QT_FORWARD_DECLARE_CLASS(QQuickItem)
#include <d3d12.h>
#include <wrl/client.h>
@ -65,7 +64,6 @@ using namespace Microsoft::WRL;
class D3D12RenderNode : public QSGRenderNode
{
public:
D3D12RenderNode(QQuickItem *item);
~D3D12RenderNode();
void render(const RenderState *state) override;
@ -73,10 +71,15 @@ public:
RenderingFlags flags() const override;
QRectF rect() const override;
void sync(QQuickItem *item);
private:
void init();
QQuickItem *m_item;
QQuickWindow *m_window = nullptr;
int m_width = 0;
int m_height = 0;
ID3D12Device *m_device = nullptr;
ComPtr<ID3D12PipelineState> pipelineState;
ComPtr<ID3D12RootSignature> rootSignature;

View File

@ -52,14 +52,12 @@
#define METALRENDERER_H
#include <qsgrendernode.h>
#include <QQuickItem>
#ifdef Q_OS_DARWIN
QT_BEGIN_NAMESPACE
class QQuickItem;
class QQuickWindow;
QT_END_NAMESPACE
class MetalRenderNodeResourceBuilder : public QObject
@ -79,7 +77,7 @@ private:
class MetalRenderNode : public QSGRenderNode
{
public:
MetalRenderNode(QQuickItem *item);
MetalRenderNode();
~MetalRenderNode();
void render(const RenderState *state) override;
@ -90,9 +88,14 @@ public:
MetalRenderNodeResourceBuilder *resourceBuilder() { return &m_resourceBuilder; }
void sync(QQuickItem *item);
private:
QQuickItem *m_item;
MetalRenderNodeResourceBuilder m_resourceBuilder;
QQuickWindow *m_window = nullptr;
int m_width = 0;
int m_height = 0;
int m_outputHeight = 0;
};
#endif // Q_OS_DARWIN

View File

@ -214,8 +214,7 @@ void MetalRenderNodeResourceBuilder::build()
}
}
MetalRenderNode::MetalRenderNode(QQuickItem *item)
: m_item(item)
MetalRenderNode::MetalRenderNode()
{
g.vs.first = g.fs.first = nil;
g.vs.second = g.fs.second = nil;
@ -258,14 +257,14 @@ void MetalRenderNode::releaseResources()
void MetalRenderNode::render(const RenderState *state)
{
QQuickWindow *window = m_item->window();
const QQuickWindow::GraphicsStateInfo *stateInfo = window->graphicsStateInfo();
Q_ASSERT(m_window);
const QQuickWindow::GraphicsStateInfo *stateInfo = m_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 p0(m_width - 1, m_height - 1);
QPointF p1(0, 0);
QPointF p2(0, m_item->height() - 1);
QPointF p2(0, m_height - 1);
float vertices[6] = { float(p0.x()), float(p0.y()),
float(p1.x()), float(p1.y()),
@ -280,9 +279,9 @@ void MetalRenderNode::render(const RenderState *state)
memcpy(p, mvp.constData(), 64);
memcpy(p + 64, &opacity, 4);
QSGRendererInterface *rif = window->rendererInterface();
QSGRendererInterface *rif = m_window->rendererInterface();
id<MTLRenderCommandEncoder> encoder = (id<MTLRenderCommandEncoder>) rif->getResource(
window, QSGRendererInterface::CommandEncoderResource);
m_window, QSGRendererInterface::CommandEncoderResource);
Q_ASSERT(encoder);
[encoder setVertexBuffer: vbuf offset: 0 atIndex: 1];
@ -296,7 +295,7 @@ void MetalRenderNode::render(const RenderState *state)
const QRect r = state->scissorRect(); // bottom-up
MTLScissorRect s;
s.x = r.x();
s.y = (window->height() * window->effectiveDevicePixelRatio()) - (r.y() + r.height());
s.y = m_outputHeight - (r.y() + r.height());
s.width = r.width();
s.height = r.height();
[encoder setScissorRect: s];
@ -322,5 +321,13 @@ QSGRenderNode::RenderingFlags MetalRenderNode::flags() const
QRectF MetalRenderNode::rect() const
{
return QRect(0, 0, m_item->width(), m_item->height());
return QRect(0, 0, m_width, m_height);
}
void MetalRenderNode::sync(QQuickItem *item)
{
m_window = item->window();
m_width = item->width();
m_height = item->height();
m_outputHeight = m_window->height() * m_window->effectiveDevicePixelRatio();
}

View File

@ -58,11 +58,6 @@
#include <QOpenGLFunctions>
//! [1]
OpenGLRenderNode::OpenGLRenderNode(QQuickItem *item)
: m_item(item)
{
}
OpenGLRenderNode::~OpenGLRenderNode()
{
releaseResources();
@ -138,9 +133,9 @@ void OpenGLRenderNode::render(const RenderState *state)
m_vbo->bind();
//! [5]
QPointF p0(m_item->width() - 1, m_item->height() - 1);
QPointF p0(m_width - 1, m_height - 1);
QPointF p1(0, 0);
QPointF p2(0, m_item->height() - 1);
QPointF p2(0, m_height - 1);
GLfloat vertices[6] = { GLfloat(p0.x()), GLfloat(p0.y()),
GLfloat(p1.x()), GLfloat(p1.y()),
@ -192,8 +187,14 @@ QSGRenderNode::RenderingFlags OpenGLRenderNode::flags() const
QRectF OpenGLRenderNode::rect() const
{
return QRect(0, 0, m_item->width(), m_item->height());
return QRect(0, 0, m_width, m_height);
}
//! [4]
void OpenGLRenderNode::sync(QQuickItem *item)
{
m_width = item->width();
m_height = item->height();
}
#endif // opengl

View File

@ -52,12 +52,12 @@
#define OPENGLRENDERER_H
#include <qsgrendernode.h>
#include <QQuickItem>
#if QT_CONFIG(opengl)
QT_BEGIN_NAMESPACE
class QQuickItem;
class QOpenGLShaderProgram;
class QOpenGLBuffer;
@ -67,7 +67,6 @@ QT_END_NAMESPACE
class OpenGLRenderNode : public QSGRenderNode
{
public:
OpenGLRenderNode(QQuickItem *item);
~OpenGLRenderNode();
void render(const RenderState *state) override;
@ -77,10 +76,13 @@ public:
QRectF rect() const override;
//! [1]
void sync(QQuickItem *item);
private:
void init();
QQuickItem *m_item;
int m_width = 0;
int m_height = 0;
QOpenGLShaderProgram *m_program = nullptr;
int m_matrixUniform;
int m_opacityUniform;

View File

@ -54,11 +54,6 @@
#include <QSGRendererInterface>
#include <QPainter>
SoftwareRenderNode::SoftwareRenderNode(QQuickItem *item)
: m_item(item)
{
}
SoftwareRenderNode::~SoftwareRenderNode()
{
releaseResources();
@ -70,8 +65,10 @@ void SoftwareRenderNode::releaseResources()
void SoftwareRenderNode::render(const RenderState *renderState)
{
QSGRendererInterface *rif = m_item->window()->rendererInterface();
QPainter *p = static_cast<QPainter *>(rif->getResource(m_item->window(), QSGRendererInterface::PainterResource));
Q_ASSERT(m_window);
QSGRendererInterface *rif = m_window->rendererInterface();
QPainter *p = static_cast<QPainter *>(rif->getResource(m_window, QSGRendererInterface::PainterResource));
Q_ASSERT(p);
const QRegion *clipRegion = renderState->clipRegion();
@ -81,15 +78,15 @@ void SoftwareRenderNode::render(const RenderState *renderState)
p->setTransform(matrix()->toTransform());
p->setOpacity(inheritedOpacity());
const QPointF p0(m_item->width() - 1, m_item->height() - 1);
const QPointF p0(m_width - 1, m_height - 1);
const QPointF p1(0, 0);
const QPointF p2(0, m_item->height() - 1);
const QPointF p2(0, m_height - 1);
QPainterPath path(p0);
path.lineTo(p1);
path.lineTo(p2);
path.closeSubpath();
QLinearGradient gradient(QPointF(0, 0), QPointF(m_item->width(), m_item->height()));
QLinearGradient gradient(QPointF(0, 0), QPointF(m_width, m_height));
gradient.setColorAt(0, Qt::green);
gradient.setColorAt(1, Qt::red);
@ -108,5 +105,12 @@ QSGRenderNode::RenderingFlags SoftwareRenderNode::flags() const
QRectF SoftwareRenderNode::rect() const
{
return QRect(0, 0, m_item->width(), m_item->height());
return QRect(0, 0, m_width, m_height);
}
void SoftwareRenderNode::sync(QQuickItem *item)
{
m_window = item->window();
m_width = item->width();
m_height = item->height();
}

View File

@ -57,7 +57,6 @@
class SoftwareRenderNode : public QSGRenderNode
{
public:
SoftwareRenderNode(QQuickItem *item);
~SoftwareRenderNode();
void render(const RenderState *state) override;
@ -66,8 +65,12 @@ public:
RenderingFlags flags() const override;
QRectF rect() const override;
void sync(QQuickItem *item);
private:
QQuickItem *m_item;
QQuickWindow *m_window = nullptr;
int m_width = 0;
int m_height = 0;
};
#endif