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:
parent
ee702d9436
commit
dd523fcb00
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Reference in New Issue