Qt
Internal/Contributor docs for the Qt SDK. Note: These are NOT official API docs; those are found at https://doc.qt.io/
Loading...
Searching...
No Matches
qquickshapegenericrenderer.cpp
Go to the documentation of this file.
1// Copyright (C) 2024 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
3
5#include <QtGui/private/qtriangulator_p.h>
6#include <QtGui/private/qtriangulatingstroker_p.h>
7#include <rhi/qrhi.h>
8#include <QSGVertexColorMaterial>
9#include <QSGTextureProvider>
10#include <private/qsgplaintexture_p.h>
11
12#include <QtQuick/private/qsggradientcache_p.h>
13
14#if QT_CONFIG(thread)
15#include <QThreadPool>
16#endif
17
19
20struct ColoredVertex // must match QSGGeometry::ColoredPoint2D
21{
22 float x, y;
24 void set(float nx, float ny, QQuickShapeGenericRenderer::Color4ub ncolor)
25 {
26 x = nx; y = ny; color = ncolor;
27 }
28};
29
31{
32 float r, g, b, a;
33 c.getRgbF(&r, &g, &b, &a);
35 uchar(qRound(r * a * 255)),
36 uchar(qRound(g * a * 255)),
37 uchar(qRound(b * a * 255)),
38 uchar(qRound(a * 255))
39 };
40 return color;
41}
42
43QQuickShapeGenericStrokeFillNode::QQuickShapeGenericStrokeFillNode(QQuickWindow *window)
44 : m_material(nullptr)
45{
46 setFlag(QSGNode::OwnsGeometry, true);
47 setFlag(QSGNode::UsePreprocess, true);
48 setGeometry(new QSGGeometry(QSGGeometry::defaultAttributes_ColoredPoint2D(), 0, 0));
49 activateMaterial(window, MatSolidColor);
51 qsgnode_set_description(this, QLatin1String("stroke-fill"));
52#endif
53}
54
56{
57 switch (m) {
58 case MatSolidColor:
59 // Use vertexcolor material. Items with different colors remain batchable
60 // this way, at the expense of having to provide per-vertex color values.
61 m_material.reset(QQuickShapeGenericMaterialFactory::createVertexColor(window));
62 break;
63 case MatLinearGradient:
64 m_material.reset(QQuickShapeGenericMaterialFactory::createLinearGradient(window, this));
65 break;
66 case MatRadialGradient:
67 m_material.reset(QQuickShapeGenericMaterialFactory::createRadialGradient(window, this));
68 break;
69 case MatConicalGradient:
70 m_material.reset(QQuickShapeGenericMaterialFactory::createConicalGradient(window, this));
71 break;
72 case MatTextureFill:
73 m_material.reset(QQuickShapeGenericMaterialFactory::createTextureFill(window, this));
74 break;
75 default:
76 qWarning("Unknown material %d", m);
77 return;
78 }
79
80 if (material() != m_material.data())
81 setMaterial(m_material.data());
82}
83
85{
86 if (m_fillTextureProvider != nullptr) {
87 if (QSGDynamicTexture *texture = qobject_cast<QSGDynamicTexture *>(m_fillTextureProvider->texture()))
88 texture->updateTexture();
89 }
90}
91
92void QQuickShapeGenericStrokeFillNode::handleTextureChanged()
93{
94 markDirty(QSGNode::DirtyMaterial);
95}
96
97void QQuickShapeGenericStrokeFillNode::handleTextureProviderDestroyed()
98{
99 m_fillTextureProvider = nullptr;
100 markDirty(QSGNode::DirtyMaterial);
101}
102
104{
105 for (ShapePathData &d : m_sp) {
106 if (d.pendingFill)
107 d.pendingFill->orphaned = true;
108 if (d.pendingStroke)
109 d.pendingStroke->orphaned = true;
110 }
111}
112
113// sync, and so triangulation too, happens on the gui thread
114// - except when async is set, in which case triangulation is moved to worker threads
115
116void QQuickShapeGenericRenderer::beginSync(int totalCount, bool *countChanged)
117{
118 for (int i = totalCount; i < m_sp.size(); i++) // Handle removal of paths
119 setFillTextureProvider(i, nullptr); // deref window
120
121 if (m_sp.size() != totalCount) {
122 m_sp.resize(totalCount);
123 m_accDirty |= DirtyList;
124 *countChanged = true;
125 } else {
126 *countChanged = false;
127 }
128 for (ShapePathData &d : m_sp)
129 d.syncDirty = 0;
130}
131
132void QQuickShapeGenericRenderer::setPath(int index, const QPainterPath &path, QQuickShapePath::PathHints)
133{
134 ShapePathData &d(m_sp[index]);
135 d.path = path;
136 d.syncDirty |= DirtyFillGeom | DirtyStrokeGeom;
137}
138
139void QQuickShapeGenericRenderer::setStrokeColor(int index, const QColor &color)
140{
141 ShapePathData &d(m_sp[index]);
142 const bool wasTransparent = d.strokeColor.a == 0;
143 d.strokeColor = colorToColor4ub(color);
144 const bool isTransparent = d.strokeColor.a == 0;
145 d.syncDirty |= DirtyColor;
146 if (wasTransparent && !isTransparent)
147 d.syncDirty |= DirtyStrokeGeom;
148}
149
151{
152 ShapePathData &d(m_sp[index]);
153 d.strokeWidth = w;
154 if (w > 0.0f)
155 d.pen.setWidthF(w);
156 d.syncDirty |= DirtyStrokeGeom;
157}
158
160{
161 ShapePathData &d(m_sp[index]);
162 d.pen.setCosmetic(c);
163 d.syncDirty |= DirtyStrokeGeom;
164 // as long as the stroke is cosmetic,
165 // QQuickShape::itemChange triggers re-triangulation whenever scale changes
166}
167
168void QQuickShapeGenericRenderer::setFillColor(int index, const QColor &color)
169{
170 ShapePathData &d(m_sp[index]);
171 const bool wasTransparent = d.fillColor.a == 0;
172 d.fillColor = colorToColor4ub(color);
173 const bool isTransparent = d.fillColor.a == 0;
174 d.syncDirty |= DirtyColor;
175 if (wasTransparent && !isTransparent)
176 d.syncDirty |= DirtyFillGeom;
177}
178
179void QQuickShapeGenericRenderer::setFillRule(int index, QQuickShapePath::FillRule fillRule)
180{
181 ShapePathData &d(m_sp[index]);
182 d.fillRule = Qt::FillRule(fillRule);
183 d.syncDirty |= DirtyFillGeom;
184}
185
186void QQuickShapeGenericRenderer::setJoinStyle(int index, QQuickShapePath::JoinStyle joinStyle, int miterLimit)
187{
188 ShapePathData &d(m_sp[index]);
189 d.pen.setJoinStyle(Qt::PenJoinStyle(joinStyle));
190 d.pen.setMiterLimit(miterLimit);
191 d.syncDirty |= DirtyStrokeGeom;
192}
193
194void QQuickShapeGenericRenderer::setCapStyle(int index, QQuickShapePath::CapStyle capStyle)
195{
196 ShapePathData &d(m_sp[index]);
197 d.pen.setCapStyle(Qt::PenCapStyle(capStyle));
198 d.syncDirty |= DirtyStrokeGeom;
199}
200
201void QQuickShapeGenericRenderer::setStrokeStyle(int index, QQuickShapePath::StrokeStyle strokeStyle,
202 qreal dashOffset, const QVector<qreal> &dashPattern)
203{
204 ShapePathData &d(m_sp[index]);
205 d.pen.setStyle(Qt::PenStyle(strokeStyle));
206 if (strokeStyle == QQuickShapePath::DashLine) {
207 d.pen.setDashPattern(dashPattern);
208 d.pen.setDashOffset(dashOffset);
209 }
210 d.syncDirty |= DirtyStrokeGeom;
211}
212
213void QQuickShapeGenericRenderer::setFillGradient(int index, QQuickShapeGradient *gradient)
214{
215 ShapePathData &d(m_sp[index]);
216 if (gradient) {
217 d.fillGradient.stops = gradient->gradientStops(); // sorted
218 d.fillGradient.spread = QGradient::Spread(gradient->spread());
219 if (QQuickShapeLinearGradient *g = qobject_cast<QQuickShapeLinearGradient *>(gradient)) {
220 d.fillGradientActive = LinearGradient;
221 d.fillGradient.a = QPointF(g->x1(), g->y1());
222 d.fillGradient.b = QPointF(g->x2(), g->y2());
223 } else if (QQuickShapeRadialGradient *g = qobject_cast<QQuickShapeRadialGradient *>(gradient)) {
224 d.fillGradientActive = RadialGradient;
225 d.fillGradient.a = QPointF(g->centerX(), g->centerY());
226 d.fillGradient.b = QPointF(g->focalX(), g->focalY());
227 d.fillGradient.v0 = g->centerRadius();
228 d.fillGradient.v1 = g->focalRadius();
229 } else if (QQuickShapeConicalGradient *g = qobject_cast<QQuickShapeConicalGradient *>(gradient)) {
230 d.fillGradientActive = ConicalGradient;
231 d.fillGradient.a = QPointF(g->centerX(), g->centerY());
232 d.fillGradient.v0 = g->angle();
233 } else {
234 Q_UNREACHABLE();
235 }
236 } else {
237 d.fillGradientActive = NoGradient;
238 }
239 d.syncDirty |= DirtyFillGradient;
240}
241
242void QQuickShapeGenericRenderer::setFillTextureProvider(int index, QQuickItem *textureProviderItem)
243{
244 ShapePathData &d(m_sp[index]);
245 if ((d.fillTextureProviderItem == nullptr) != (textureProviderItem == nullptr))
246 d.syncDirty |= DirtyFillGeom;
247 if (d.fillTextureProviderItem != nullptr)
248 QQuickItemPrivate::get(d.fillTextureProviderItem)->derefWindow();
249 d.fillTextureProviderItem = textureProviderItem;
250 if (d.fillTextureProviderItem != nullptr)
251 QQuickItemPrivate::get(d.fillTextureProviderItem)->refWindow(m_item->window());
252 d.syncDirty |= DirtyFillTexture;
253}
254
256{
257 for (auto &pathData : m_sp) {
258 if (pathData.fillTextureProviderItem != nullptr) {
259 if (window == nullptr)
260 QQuickItemPrivate::get(pathData.fillTextureProviderItem)->derefWindow();
261 else
262 QQuickItemPrivate::get(pathData.fillTextureProviderItem)->refWindow(window);
263 }
264 }
265}
266
267
268void QQuickShapeGenericRenderer::setFillTransform(int index, const QSGTransform &transform)
269{
270 ShapePathData &d(m_sp[index]);
271 d.fillTransform = transform;
272 d.syncDirty |= DirtyFillTransform;
273}
274
276{
277 // No dirty, this is called at the start of every sync. Just store the value.
278 m_triangulationScale = scale;
279}
280
282{
283 QQuickShapeGenericRenderer::triangulateFill(path, fillColor, &fillVertices, &fillIndices, &indexType,
284 supportsElementIndexUint, triangulationScale);
285 emit done(this);
286}
287
289{
290 QQuickShapeGenericRenderer::triangulateStroke(path, pen, strokeColor, &strokeVertices, clipSize, triangulationScale);
291 emit done(this);
292}
293
294void QQuickShapeGenericRenderer::setAsyncCallback(void (*callback)(void *), void *data)
295{
296 m_asyncCallback = callback;
297 m_asyncCallbackData = data;
298}
299
300#if QT_CONFIG(thread)
301static QThreadPool *pathWorkThreadPool = nullptr;
302
303static void deletePathWorkThreadPool()
304{
305 delete pathWorkThreadPool;
306 pathWorkThreadPool = nullptr;
307}
308#endif
309
311{
312#if !QT_CONFIG(thread)
313 // Force synchronous mode for the no-thread configuration due
314 // to lack of QThreadPool.
315 async = false;
316#endif
317
318 bool didKickOffAsync = false;
319
320 for (int i = 0; i < m_sp.size(); ++i) {
321 ShapePathData &d(m_sp[i]);
322 if (!d.syncDirty)
323 continue;
324
325 m_accDirty |= d.syncDirty;
326
327 // Use a shadow dirty flag in order to avoid losing state in case there are
328 // multiple syncs with different dirty flags before we get to updateNode()
329 // on the render thread (with the gui thread blocked). For our purposes
330 // here syncDirty is still required since geometry regeneration must only
331 // happen when there was an actual change in this particular sync round.
332 d.effectiveDirty |= d.syncDirty;
333
334 if (d.path.isEmpty()) {
335 d.fillVertices.clear();
336 d.fillIndices.clear();
337 d.strokeVertices.clear();
338 continue;
339 }
340
341#if QT_CONFIG(thread)
342 if (async && !pathWorkThreadPool) {
343 qAddPostRoutine(deletePathWorkThreadPool);
344 pathWorkThreadPool = new QThreadPool;
345 const int idealCount = QThread::idealThreadCount();
346 pathWorkThreadPool->setMaxThreadCount(idealCount > 0 ? idealCount * 2 : 4);
347 }
348#endif
349 auto testFeatureIndexUint = [](QQuickItem *item) -> bool {
350 if (auto *w = item->window()) {
351 if (auto *rhi = QQuickWindowPrivate::get(w)->rhi)
352 return rhi->isFeatureSupported(QRhi::ElementIndexUint);
353 }
354 return true;
355 };
356 static bool supportsElementIndexUint = testFeatureIndexUint(m_item);
357 if ((d.syncDirty & DirtyFillGeom) && d.fillColor.a) {
358 d.path.setFillRule(d.fillRule);
359 if (m_api == QSGRendererInterface::Unknown)
360 m_api = m_item->window()->rendererInterface()->graphicsApi();
361 if (async) {
363 r->setAutoDelete(false);
364 if (d.pendingFill)
365 d.pendingFill->orphaned = true;
366 d.pendingFill = r;
367 r->path = d.path;
368 r->fillColor = d.fillColor;
369 r->supportsElementIndexUint = supportsElementIndexUint;
370 r->triangulationScale = m_triangulationScale;
371 // Unlikely in practice but in theory m_sp could be
372 // resized. Therefore, capture 'i' instead of 'd'.
373 QObject::connect(r, &QQuickShapeFillRunnable::done, qApp, [this, i](QQuickShapeFillRunnable *r) {
374 // Bail out when orphaned (meaning either another run was
375 // started after this one, or the renderer got destroyed).
376 if (!r->orphaned && i < m_sp.size()) {
377 ShapePathData &d(m_sp[i]);
378 d.fillVertices = r->fillVertices;
379 d.fillIndices = r->fillIndices;
380 d.indexType = r->indexType;
381 d.pendingFill = nullptr;
382 d.effectiveDirty |= DirtyFillGeom;
383 maybeUpdateAsyncItem();
384 }
385 r->deleteLater();
386 });
387 didKickOffAsync = true;
388#if QT_CONFIG(thread)
389 // qtVectorPathForPath() initializes a unique_ptr without locking.
390 // Do that before starting the threads as otherwise we get a race condition.
391 qtVectorPathForPath(r->path);
392 pathWorkThreadPool->start(r);
393#endif
394 } else {
395 triangulateFill(d.path, d.fillColor, &d.fillVertices, &d.fillIndices, &d.indexType,
396 supportsElementIndexUint,
397 m_triangulationScale);
398 }
399 }
400
401 if ((d.syncDirty & DirtyStrokeGeom) && d.strokeWidth > 0.0f && d.strokeColor.a) {
402 if (async) {
404 r->setAutoDelete(false);
405 if (d.pendingStroke)
406 d.pendingStroke->orphaned = true;
407 d.pendingStroke = r;
408 r->path = d.path;
409 r->pen = d.pen;
410 r->strokeColor = d.strokeColor;
411 r->clipSize = QSize(m_item->width(), m_item->height());
412 r->triangulationScale = m_triangulationScale;
413 QObject::connect(r, &QQuickShapeStrokeRunnable::done, qApp, [this, i](QQuickShapeStrokeRunnable *r) {
414 if (!r->orphaned && i < m_sp.size()) {
415 ShapePathData &d(m_sp[i]);
416 d.strokeVertices = r->strokeVertices;
417 d.pendingStroke = nullptr;
418 d.effectiveDirty |= DirtyStrokeGeom;
419 maybeUpdateAsyncItem();
420 }
421 r->deleteLater();
422 });
423 didKickOffAsync = true;
424#if QT_CONFIG(thread)
425 // qtVectorPathForPath() initializes a unique_ptr without locking.
426 // Do that before starting the threads as otherwise we get a race condition.
427 qtVectorPathForPath(r->path);
428 pathWorkThreadPool->start(r);
429#endif
430 } else {
431 triangulateStroke(d.path, d.pen, d.strokeColor, &d.strokeVertices,
432 QSize(m_item->width(), m_item->height()), m_triangulationScale);
433 }
434 }
435 }
436
437 if (!didKickOffAsync && async && m_asyncCallback)
438 m_asyncCallback(m_asyncCallbackData);
439}
440
441void QQuickShapeGenericRenderer::maybeUpdateAsyncItem()
442{
443 for (const ShapePathData &d : std::as_const(m_sp)) {
444 if (d.pendingFill || d.pendingStroke)
445 return;
446 }
447 m_accDirty |= DirtyFillGeom | DirtyStrokeGeom;
448 m_item->update();
449 if (m_asyncCallback)
450 m_asyncCallback(m_asyncCallbackData);
451}
452
453// the stroke/fill triangulation functions may be invoked either on the gui
454// thread or some worker thread and must thus be self-contained.
455void QQuickShapeGenericRenderer::triangulateFill(const QPainterPath &path,
456 const Color4ub &fillColor,
457 VertexContainerType *fillVertices,
458 IndexContainerType *fillIndices,
459 QSGGeometry::Type *indexType,
460 bool supportsElementIndexUint,
461 qreal triangulationScale)
462{
463 const QVectorPath &vp = qtVectorPathForPath(path);
464
465 QTriangleSet ts = qTriangulate(vp, QTransform::fromScale(triangulationScale, triangulationScale), 1, supportsElementIndexUint);
466 const int vertexCount = ts.vertices.size() / 2; // just a qreal vector with x,y hence the / 2
467 fillVertices->resize(vertexCount);
468 ColoredVertex *vdst = reinterpret_cast<ColoredVertex *>(fillVertices->data());
469 const qreal *vsrc = ts.vertices.constData();
470 for (int i = 0; i < vertexCount; ++i)
471 vdst[i].set(vsrc[i * 2] / triangulationScale, vsrc[i * 2 + 1] / triangulationScale, fillColor);
472
473 size_t indexByteSize;
474 if (ts.indices.type() == QVertexIndexVector::UnsignedShort) {
475 *indexType = QSGGeometry::UnsignedShortType;
476 // fillIndices is still QVector<quint32>. Just resize to N/2 and pack
477 // the N quint16s into it.
478 fillIndices->resize(ts.indices.size() / 2);
479 indexByteSize = ts.indices.size() * sizeof(quint16);
480 } else {
481 *indexType = QSGGeometry::UnsignedIntType;
482 fillIndices->resize(ts.indices.size());
483 indexByteSize = ts.indices.size() * sizeof(quint32);
484 }
485 memcpy(fillIndices->data(), ts.indices.data(), indexByteSize);
486}
487
488void QQuickShapeGenericRenderer::triangulateStroke(const QPainterPath &path,
489 const QPen &pen,
490 const Color4ub &strokeColor,
491 VertexContainerType *strokeVertices,
492 const QSize &clipSize,
493 qreal triangulationScale)
494{
495 const QVectorPath &vp = qtVectorPathForPath(path);
496 const QRectF clip(QPointF(0, 0), clipSize);
497 const qreal inverseScale = 1.0 / triangulationScale;
498
499 QTriangulatingStroker stroker;
500 stroker.setInvScale(inverseScale);
501
502 if (pen.style() == Qt::SolidLine) {
503 stroker.process(vp, pen, clip, {});
504 } else {
505 QDashedStrokeProcessor dashStroker;
506 dashStroker.setInvScale(inverseScale);
507 dashStroker.process(vp, pen, clip, {});
508 QVectorPath dashStroke(dashStroker.points(), dashStroker.elementCount(),
509 dashStroker.elementTypes(), 0);
510 stroker.process(dashStroke, pen, clip, {});
511 }
512
513 if (!stroker.vertexCount()) {
514 strokeVertices->clear();
515 return;
516 }
517
518 const int vertexCount = stroker.vertexCount() / 2; // just a float vector with x,y hence the / 2
519 strokeVertices->resize(vertexCount);
520 ColoredVertex *vdst = reinterpret_cast<ColoredVertex *>(strokeVertices->data());
521 const float *vsrc = stroker.vertices();
522 for (int i = 0; i < vertexCount; ++i)
523 vdst[i].set(vsrc[i * 2], vsrc[i * 2 + 1], strokeColor);
524}
525
527{
528 if (m_rootNode != node) {
529 m_rootNode = node;
530 m_accDirty |= DirtyList;
531 }
532}
533
534// on the render thread with gui blocked
536{
537 if (!m_rootNode || !m_accDirty)
538 return;
539
540// [ m_rootNode ]
541// / / /
542// #0 [ fill ] [ stroke ] [ next ]
543// / / |
544// #1 [ fill ] [ stroke ] [ next ]
545// / / |
546// #2 [ fill ] [ stroke ] [ next ]
547// ...
548// ...
549
550 QQuickShapeGenericNode **nodePtr = &m_rootNode;
551 QQuickShapeGenericNode *prevNode = nullptr;
552
553 for (ShapePathData &d : m_sp) {
554 if (!*nodePtr) {
555 Q_ASSERT(prevNode);
556 *nodePtr = new QQuickShapeGenericNode;
557 prevNode->m_next = *nodePtr;
558 prevNode->appendChildNode(*nodePtr);
559 }
560
561 QQuickShapeGenericNode *node = *nodePtr;
562
563 if (m_accDirty & DirtyList)
564 d.effectiveDirty |= DirtyFillGeom | DirtyStrokeGeom | DirtyColor | DirtyFillGradient | DirtyFillTransform | DirtyFillTexture;
565
566 if (!d.effectiveDirty) {
567 prevNode = node;
568 nodePtr = &node->m_next;
569 continue;
570 }
571
572 if (d.fillColor.a == 0) {
573 delete node->m_fillNode;
574 node->m_fillNode = nullptr;
575 } else if (!node->m_fillNode) {
576 node->m_fillNode = new QQuickShapeGenericStrokeFillNode(m_item->window());
577 if (node->m_strokeNode)
578 node->removeChildNode(node->m_strokeNode);
579 node->appendChildNode(node->m_fillNode);
580 if (node->m_strokeNode)
581 node->appendChildNode(node->m_strokeNode);
582 d.effectiveDirty |= DirtyFillGeom;
583 }
584
585 if (d.strokeWidth <= 0.0f || d.strokeColor.a == 0) {
586 delete node->m_strokeNode;
587 node->m_strokeNode = nullptr;
588 } else if (!node->m_strokeNode) {
589 node->m_strokeNode = new QQuickShapeGenericStrokeFillNode(m_item->window());
590 node->appendChildNode(node->m_strokeNode);
591 d.effectiveDirty |= DirtyStrokeGeom;
592 }
593
594 updateFillNode(&d, node);
595 updateStrokeNode(&d, node);
596
597 d.effectiveDirty = 0;
598
599 prevNode = node;
600 nodePtr = &node->m_next;
601 }
602
603 if (*nodePtr && prevNode) {
604 prevNode->removeChildNode(*nodePtr);
605 delete *nodePtr;
606 *nodePtr = nullptr;
607 }
608
609 if (m_sp.isEmpty()) {
610 delete m_rootNode->m_fillNode;
611 m_rootNode->m_fillNode = nullptr;
612 delete m_rootNode->m_strokeNode;
613 m_rootNode->m_strokeNode = nullptr;
614 delete m_rootNode->m_next;
615 m_rootNode->m_next = nullptr;
616 }
617
618 m_accDirty = 0;
619}
620
621void QQuickShapeGenericRenderer::updateShadowDataInNode(ShapePathData *d, QQuickShapeGenericStrokeFillNode *n)
622{
623 if (d->fillGradientActive) {
624 if (d->effectiveDirty & DirtyFillGradient)
625 n->m_fillGradient = d->fillGradient;
626 }
627 if (d->effectiveDirty & DirtyFillTexture) {
628 bool needsUpdate = d->fillTextureProviderItem == nullptr && n->m_fillTextureProvider != nullptr;
629 if (!needsUpdate
630 && d->fillTextureProviderItem != nullptr
631 && n->m_fillTextureProvider != d->fillTextureProviderItem->textureProvider()) {
632 needsUpdate = true;
633 }
634
635 if (needsUpdate) {
636 if (n->m_fillTextureProvider != nullptr) {
637 QObject::disconnect(n->m_fillTextureProvider, &QSGTextureProvider::textureChanged,
638 n, &QQuickShapeGenericStrokeFillNode::handleTextureChanged);
639 QObject::disconnect(n->m_fillTextureProvider, &QSGTextureProvider::destroyed,
640 n, &QQuickShapeGenericStrokeFillNode::handleTextureProviderDestroyed);
641 }
642
643 n->m_fillTextureProvider = d->fillTextureProviderItem == nullptr
644 ? nullptr
645 : d->fillTextureProviderItem->textureProvider();
646
647 if (n->m_fillTextureProvider != nullptr) {
648 QObject::connect(n->m_fillTextureProvider, &QSGTextureProvider::textureChanged,
649 n, &QQuickShapeGenericStrokeFillNode::handleTextureChanged);
650 QObject::connect(n->m_fillTextureProvider, &QSGTextureProvider::destroyed,
651 n, &QQuickShapeGenericStrokeFillNode::handleTextureProviderDestroyed);
652 }
653 }
654 }
655 if (d->effectiveDirty & DirtyFillTransform)
656 n->m_fillTransform = d->fillTransform;
657}
658
659void QQuickShapeGenericRenderer::updateFillNode(ShapePathData *d, QQuickShapeGenericNode *node)
660{
661 if (!node->m_fillNode)
662 return;
664 return;
665
666 // Make a copy of the data that will be accessed by the material on
667 // the render thread. This must be done even when we bail out below.
669 updateShadowDataInNode(d, n);
670
671 QSGGeometry *g = n->geometry();
672 if (d->fillVertices.isEmpty()) {
673 if (g->vertexCount() || g->indexCount()) {
674 g->allocate(0, 0);
675 n->markDirty(QSGNode::DirtyGeometry);
676 }
677 return;
678 }
679
680 if (d->fillGradientActive) {
682 switch (d->fillGradientActive) {
683 case LinearGradient:
685 break;
686 case RadialGradient:
688 break;
689 case ConicalGradient:
691 break;
692 default:
693 Q_UNREACHABLE_RETURN();
694 }
695 n->activateMaterial(m_item->window(), gradMat);
696 if (d->effectiveDirty & (DirtyFillGradient | DirtyFillTransform)) {
697 // Gradients are implemented via a texture-based material.
698 n->markDirty(QSGNode::DirtyMaterial);
699 // stop here if only the gradient or filltransform changed; no need to touch the geometry
700 if (!(d->effectiveDirty & DirtyFillGeom))
701 return;
702 }
703 } else if (d->fillTextureProviderItem != nullptr) {
704 n->activateMaterial(m_item->window(), QQuickShapeGenericStrokeFillNode::MatTextureFill);
705 if (d->effectiveDirty & DirtyFillTexture)
706 n->markDirty(QSGNode::DirtyMaterial);
707 } else {
708 n->activateMaterial(m_item->window(), QQuickShapeGenericStrokeFillNode::MatSolidColor);
709 // fast path for updating only color values when no change in vertex positions
710 if ((d->effectiveDirty & DirtyColor) && !(d->effectiveDirty & DirtyFillGeom) && d->fillTextureProviderItem == nullptr) {
711 ColoredVertex *vdst = reinterpret_cast<ColoredVertex *>(g->vertexData());
712 for (int i = 0; i < g->vertexCount(); ++i)
713 vdst[i].set(vdst[i].x, vdst[i].y, d->fillColor);
714 n->markDirty(QSGNode::DirtyGeometry);
715 return;
716 }
717 }
718
719 const int indexCount = d->indexType == QSGGeometry::UnsignedShortType
720 ? d->fillIndices.size() * 2 : d->fillIndices.size();
721 if (g->indexType() != d->indexType) {
722 g = new QSGGeometry(QSGGeometry::defaultAttributes_ColoredPoint2D(),
723 d->fillVertices.size(), indexCount, d->indexType);
724 n->setGeometry(g);
725 } else {
726 g->allocate(d->fillVertices.size(), indexCount);
727 }
728 g->setDrawingMode(QSGGeometry::DrawTriangles);
729
730 memcpy(g->vertexData(), d->fillVertices.constData(), g->vertexCount() * g->sizeOfVertex());
731 memcpy(g->indexData(), d->fillIndices.constData(), g->indexCount() * g->sizeOfIndex());
732
733 n->markDirty(QSGNode::DirtyGeometry);
734}
735
736void QQuickShapeGenericRenderer::updateStrokeNode(ShapePathData *d, QQuickShapeGenericNode *node)
737{
738 if (!node->m_strokeNode)
739 return;
740 if (!(d->effectiveDirty & (DirtyStrokeGeom | DirtyColor)))
741 return;
742
744 QSGGeometry *g = n->geometry();
745 if (d->strokeVertices.isEmpty()) {
746 if (g->vertexCount() || g->indexCount()) {
747 g->allocate(0, 0);
748 n->markDirty(QSGNode::DirtyGeometry);
749 }
750 return;
751 }
752
753 n->markDirty(QSGNode::DirtyGeometry);
754
755 // Async loading runs update once, bails out above, then updates again once
756 // ready. Set the material dirty then. This is in-line with fill where the
757 // first activateMaterial() achieves the same.
758 if (!g->vertexCount())
759 n->markDirty(QSGNode::DirtyMaterial);
760
761 if ((d->effectiveDirty & DirtyColor) && !(d->effectiveDirty & DirtyStrokeGeom)) {
762 ColoredVertex *vdst = reinterpret_cast<ColoredVertex *>(g->vertexData());
763 for (int i = 0; i < g->vertexCount(); ++i)
764 vdst[i].set(vdst[i].x, vdst[i].y, d->strokeColor);
765 return;
766 }
767
768 g->allocate(d->strokeVertices.size(), 0);
769 g->setDrawingMode(QSGGeometry::DrawTriangleStrip);
770 memcpy(g->vertexData(), d->strokeVertices.constData(), g->vertexCount() * g->sizeOfVertex());
771}
772
773QSGMaterial *QQuickShapeGenericMaterialFactory::createVertexColor(QQuickWindow *window)
774{
775 QSGRendererInterface::GraphicsApi api = window->rendererInterface()->graphicsApi();
776
777 if (api == QSGRendererInterface::OpenGL || QSGRendererInterface::isApiRhiBased(api))
778 return new QSGVertexColorMaterial;
779
780 qWarning("Vertex-color material: Unsupported graphics API %d", api);
781 return nullptr;
782}
783
784QSGMaterial *QQuickShapeGenericMaterialFactory::createLinearGradient(QQuickWindow *window,
786{
787 QSGRendererInterface::GraphicsApi api = window->rendererInterface()->graphicsApi();
788
789 if (api == QSGRendererInterface::OpenGL || QSGRendererInterface::isApiRhiBased(api))
791
792 qWarning("Linear gradient material: Unsupported graphics API %d", api);
793 return nullptr;
794}
795
796QSGMaterial *QQuickShapeGenericMaterialFactory::createRadialGradient(QQuickWindow *window,
798{
799 QSGRendererInterface::GraphicsApi api = window->rendererInterface()->graphicsApi();
800
801 if (api == QSGRendererInterface::OpenGL || QSGRendererInterface::isApiRhiBased(api))
803
804 qWarning("Radial gradient material: Unsupported graphics API %d", api);
805 return nullptr;
806}
807
808QSGMaterial *QQuickShapeGenericMaterialFactory::createConicalGradient(QQuickWindow *window,
810{
811 QSGRendererInterface::GraphicsApi api = window->rendererInterface()->graphicsApi();
812
813 if (api == QSGRendererInterface::OpenGL || QSGRendererInterface::isApiRhiBased(api))
815
816 qWarning("Conical gradient material: Unsupported graphics API %d", api);
817 return nullptr;
818}
819
820QSGMaterial *QQuickShapeGenericMaterialFactory::createTextureFill(QQuickWindow *window,
822{
823 QSGRendererInterface::GraphicsApi api = window->rendererInterface()->graphicsApi();
824
825 if (api == QSGRendererInterface::OpenGL || QSGRendererInterface::isApiRhiBased(api))
827
828 qWarning("Texture fill material: Unsupported graphics API %d", api);
829 return nullptr;
830}
831
833{
834 setShaderFileName(VertexStage, QStringLiteral(":/qt-project.org/shapes/shaders_ng/lineargradient.vert.qsb"), viewCount);
835 setShaderFileName(FragmentStage, QStringLiteral(":/qt-project.org/shapes/shaders_ng/lineargradient.frag.qsb"), viewCount);
836}
837
839 QSGMaterial *newMaterial, QSGMaterial *oldMaterial)
840{
841 Q_ASSERT(oldMaterial == nullptr || newMaterial->type() == oldMaterial->type());
842 QQuickShapeLinearGradientMaterial *m = static_cast<QQuickShapeLinearGradientMaterial *>(newMaterial);
843 bool changed = false;
844 QByteArray *buf = state.uniformData();
845 Q_ASSERT(buf->size() >= 84 + 64);
846 const int shaderMatrixCount = newMaterial->viewCount();
847 const int matrixCount = qMin(state.projectionMatrixCount(), shaderMatrixCount);
848
849 if (state.isMatrixDirty()) {
850 for (int viewIndex = 0; viewIndex < matrixCount; ++viewIndex) {
851 const QMatrix4x4 m = state.combinedMatrix();
852 memcpy(buf->data() + 64 * viewIndex, m.constData(), 64);
853 changed = true;
854 }
855 }
856
858
859 if (!oldMaterial || m_fillTransform != node->m_fillTransform) {
860 memcpy(buf->data() + 64 * shaderMatrixCount, node->m_fillTransform.invertedData(), 64);
861 m_fillTransform = node->m_fillTransform;
862 changed = true;
863 }
864
865 if (!oldMaterial || m_gradA.x() != node->m_fillGradient.a.x() || m_gradA.y() != node->m_fillGradient.a.y()) {
866 m_gradA = QVector2D(node->m_fillGradient.a.x(), node->m_fillGradient.a.y());
867 Q_ASSERT(sizeof(m_gradA) == 8);
868 memcpy(buf->data() + 64 * shaderMatrixCount + 64, &m_gradA, 8);
869 changed = true;
870 }
871
872 if (!oldMaterial || m_gradB.x() != node->m_fillGradient.b.x() || m_gradB.y() != node->m_fillGradient.b.y()) {
873 m_gradB = QVector2D(node->m_fillGradient.b.x(), node->m_fillGradient.b.y());
874 memcpy(buf->data() + 64 * shaderMatrixCount + 64 + 8, &m_gradB, 8);
875 changed = true;
876 }
877
878 if (state.isOpacityDirty()) {
879 const float opacity = state.opacity();
880 memcpy(buf->data() + 64 * shaderMatrixCount + 64 + 8 + 8, &opacity, 4);
881 changed = true;
882 }
883
884 return changed;
885}
886
887void QQuickShapeLinearGradientRhiShader::updateSampledImage(RenderState &state, int binding, QSGTexture **texture,
888 QSGMaterial *newMaterial, QSGMaterial *)
889{
890 if (binding != 1)
891 return;
892
893 QQuickShapeLinearGradientMaterial *m = static_cast<QQuickShapeLinearGradientMaterial *>(newMaterial);
895 const QSGGradientCacheKey cacheKey(node->m_fillGradient.stops, QGradient::Spread(node->m_fillGradient.spread));
896 QSGTexture *t = QSGGradientCache::cacheForRhi(state.rhi())->get(cacheKey);
897 t->commitTextureOperations(state.rhi(), state.resourceUpdateBatch());
898 *texture = t;
899}
900
902{
903 static QSGMaterialType type;
904 return &type;
905}
906
907int QQuickShapeLinearGradientMaterial::compare(const QSGMaterial *other) const
908{
909 Q_ASSERT(other && type() == other->type());
910 const QQuickShapeLinearGradientMaterial *m = static_cast<const QQuickShapeLinearGradientMaterial *>(other);
911
914 Q_ASSERT(a && b);
915 if (a == b)
916 return 0;
917
918 const QSGGradientCache::GradientDesc *ga = &a->m_fillGradient;
919 const QSGGradientCache::GradientDesc *gb = &b->m_fillGradient;
920
921 if (int d = ga->spread - gb->spread)
922 return d;
923
924 if (int d = ga->a.x() - gb->a.x())
925 return d;
926 if (int d = ga->a.y() - gb->a.y())
927 return d;
928 if (int d = ga->b.x() - gb->b.x())
929 return d;
930 if (int d = ga->b.y() - gb->b.y())
931 return d;
932
933 if (int d = ga->stops.size() - gb->stops.size())
934 return d;
935
936 for (int i = 0; i < ga->stops.size(); ++i) {
937 if (int d = ga->stops[i].first - gb->stops[i].first)
938 return d;
939 if (int d = ga->stops[i].second.rgba() - gb->stops[i].second.rgba())
940 return d;
941 }
942
943 if (int d = a->m_fillTransform.compareTo(b->m_fillTransform))
944 return d;
945
946 return 0;
947}
948
949QSGMaterialShader *QQuickShapeLinearGradientMaterial::createShader(QSGRendererInterface::RenderMode renderMode) const
950{
951 Q_UNUSED(renderMode);
952 return new QQuickShapeLinearGradientRhiShader(viewCount());
953}
954
956{
957 setShaderFileName(VertexStage, QStringLiteral(":/qt-project.org/shapes/shaders_ng/radialgradient.vert.qsb"), viewCount);
958 setShaderFileName(FragmentStage, QStringLiteral(":/qt-project.org/shapes/shaders_ng/radialgradient.frag.qsb"), viewCount);
959}
960
962 QSGMaterial *newMaterial, QSGMaterial *oldMaterial)
963{
964 Q_ASSERT(oldMaterial == nullptr || newMaterial->type() == oldMaterial->type());
965 QQuickShapeRadialGradientMaterial *m = static_cast<QQuickShapeRadialGradientMaterial *>(newMaterial);
966 bool changed = false;
967 QByteArray *buf = state.uniformData();
968 Q_ASSERT(buf->size() >= 92 + 64);
969 const int shaderMatrixCount = newMaterial->viewCount();
970 const int matrixCount = qMin(state.projectionMatrixCount(), shaderMatrixCount);
971
972 if (state.isMatrixDirty()) {
973 for (int viewIndex = 0; viewIndex < matrixCount; ++viewIndex) {
974 const QMatrix4x4 m = state.combinedMatrix();
975 memcpy(buf->data() + 64 * viewIndex, m.constData(), 64);
976 changed = true;
977 }
978 }
979
981
982 if (!oldMaterial || m_fillTransform != node->m_fillTransform) {
983 memcpy(buf->data() + 64 * shaderMatrixCount, node->m_fillTransform.invertedData(), 64);
984 m_fillTransform = node->m_fillTransform;
985 changed = true;
986 }
987
988 const QPointF centerPoint = node->m_fillGradient.a;
989 const QPointF focalPoint = node->m_fillGradient.b;
990 const QPointF focalToCenter = centerPoint - focalPoint;
991 const float centerRadius = node->m_fillGradient.v0;
992 const float focalRadius = node->m_fillGradient.v1;
993
994 if (!oldMaterial || m_focalPoint.x() != focalPoint.x() || m_focalPoint.y() != focalPoint.y()) {
995 m_focalPoint = QVector2D(focalPoint.x(), focalPoint.y());
996 Q_ASSERT(sizeof(m_focalPoint) == 8);
997 memcpy(buf->data() + 64 * shaderMatrixCount + 64, &m_focalPoint, 8);
998 changed = true;
999 }
1000
1001 if (!oldMaterial || m_focalToCenter.x() != focalToCenter.x() || m_focalToCenter.y() != focalToCenter.y()) {
1002 m_focalToCenter = QVector2D(focalToCenter.x(), focalToCenter.y());
1003 Q_ASSERT(sizeof(m_focalToCenter) == 8);
1004 memcpy(buf->data() + 64 * shaderMatrixCount + 64 + 8, &m_focalToCenter, 8);
1005 changed = true;
1006 }
1007
1008 if (!oldMaterial || m_centerRadius != centerRadius) {
1009 m_centerRadius = centerRadius;
1010 memcpy(buf->data() + 64 * shaderMatrixCount + 64 + 8 + 8, &m_centerRadius, 4);
1011 changed = true;
1012 }
1013
1014 if (!oldMaterial || m_focalRadius != focalRadius) {
1015 m_focalRadius = focalRadius;
1016 memcpy(buf->data() + 64 * shaderMatrixCount + 64 + 8 + 8 + 4, &m_focalRadius, 4);
1017 changed = true;
1018 }
1019
1020 if (state.isOpacityDirty()) {
1021 const float opacity = state.opacity();
1022 memcpy(buf->data() + 64 * shaderMatrixCount + 64 + 8 + 8 + 4 + 4, &opacity, 4);
1023 changed = true;
1024 }
1025
1026 return changed;
1027}
1028
1029void QQuickShapeRadialGradientRhiShader::updateSampledImage(RenderState &state, int binding, QSGTexture **texture,
1030 QSGMaterial *newMaterial, QSGMaterial *)
1031{
1032 if (binding != 1)
1033 return;
1034
1035 QQuickShapeRadialGradientMaterial *m = static_cast<QQuickShapeRadialGradientMaterial *>(newMaterial);
1037 const QSGGradientCacheKey cacheKey(node->m_fillGradient.stops, QGradient::Spread(node->m_fillGradient.spread));
1038 QSGTexture *t = QSGGradientCache::cacheForRhi(state.rhi())->get(cacheKey);
1039 t->commitTextureOperations(state.rhi(), state.resourceUpdateBatch());
1040 *texture = t;
1041}
1042
1044{
1045 static QSGMaterialType type;
1046 return &type;
1047}
1048
1049int QQuickShapeRadialGradientMaterial::compare(const QSGMaterial *other) const
1050{
1051 Q_ASSERT(other && type() == other->type());
1052 const QQuickShapeRadialGradientMaterial *m = static_cast<const QQuickShapeRadialGradientMaterial *>(other);
1053
1056 Q_ASSERT(a && b);
1057 if (a == b)
1058 return 0;
1059
1060 const QSGGradientCache::GradientDesc *ga = &a->m_fillGradient;
1061 const QSGGradientCache::GradientDesc *gb = &b->m_fillGradient;
1062
1063 if (int d = ga->spread - gb->spread)
1064 return d;
1065
1066 if (int d = ga->a.x() - gb->a.x())
1067 return d;
1068 if (int d = ga->a.y() - gb->a.y())
1069 return d;
1070 if (int d = ga->b.x() - gb->b.x())
1071 return d;
1072 if (int d = ga->b.y() - gb->b.y())
1073 return d;
1074
1075 if (int d = ga->v0 - gb->v0)
1076 return d;
1077 if (int d = ga->v1 - gb->v1)
1078 return d;
1079
1080 if (int d = ga->stops.size() - gb->stops.size())
1081 return d;
1082
1083 for (int i = 0; i < ga->stops.size(); ++i) {
1084 if (int d = ga->stops[i].first - gb->stops[i].first)
1085 return d;
1086 if (int d = ga->stops[i].second.rgba() - gb->stops[i].second.rgba())
1087 return d;
1088 }
1089
1090 if (int d = a->m_fillTransform.compareTo(b->m_fillTransform))
1091 return d;
1092
1093 return 0;
1094}
1095
1096QSGMaterialShader *QQuickShapeRadialGradientMaterial::createShader(QSGRendererInterface::RenderMode renderMode) const
1097{
1098 Q_UNUSED(renderMode);
1099 return new QQuickShapeRadialGradientRhiShader(viewCount());
1100}
1101
1103{
1104 setShaderFileName(VertexStage, QStringLiteral(":/qt-project.org/shapes/shaders_ng/conicalgradient.vert.qsb"), viewCount);
1105 setShaderFileName(FragmentStage, QStringLiteral(":/qt-project.org/shapes/shaders_ng/conicalgradient.frag.qsb"), viewCount);
1106}
1107
1109 QSGMaterial *newMaterial, QSGMaterial *oldMaterial)
1110{
1111 Q_ASSERT(oldMaterial == nullptr || newMaterial->type() == oldMaterial->type());
1112 QQuickShapeConicalGradientMaterial *m = static_cast<QQuickShapeConicalGradientMaterial *>(newMaterial);
1113 bool changed = false;
1114 QByteArray *buf = state.uniformData();
1115 Q_ASSERT(buf->size() >= 80 + 64);
1116 const int shaderMatrixCount = newMaterial->viewCount();
1117 const int matrixCount = qMin(state.projectionMatrixCount(), shaderMatrixCount);
1118
1119 if (state.isMatrixDirty()) {
1120 for (int viewIndex = 0; viewIndex < matrixCount; ++viewIndex) {
1121 const QMatrix4x4 m = state.combinedMatrix();
1122 memcpy(buf->data() + 64 * viewIndex, m.constData(), 64);
1123 changed = true;
1124 }
1125 }
1126
1128
1129 if (!oldMaterial || m_fillTransform != node->m_fillTransform) {
1130 memcpy(buf->data() + 64 * shaderMatrixCount, node->m_fillTransform.invertedData(), 64);
1131 m_fillTransform = node->m_fillTransform;
1132 changed = true;
1133 }
1134
1135 const QPointF centerPoint = node->m_fillGradient.a;
1136 const float angle = -qDegreesToRadians(node->m_fillGradient.v0);
1137
1138 if (!oldMaterial || m_centerPoint.x() != centerPoint.x() || m_centerPoint.y() != centerPoint.y()) {
1139 m_centerPoint = QVector2D(centerPoint.x(), centerPoint.y());
1140 Q_ASSERT(sizeof(m_centerPoint) == 8);
1141 memcpy(buf->data() + 64 * shaderMatrixCount + 64, &m_centerPoint, 8);
1142 changed = true;
1143 }
1144
1145 if (!oldMaterial || m_angle != angle) {
1146 m_angle = angle;
1147 memcpy(buf->data() + 64 * shaderMatrixCount + 64 + 8, &m_angle, 4);
1148 changed = true;
1149 }
1150
1151 if (state.isOpacityDirty()) {
1152 const float opacity = state.opacity();
1153 memcpy(buf->data() + 64 * shaderMatrixCount + 64 + 8 + 4, &opacity, 4);
1154 changed = true;
1155 }
1156
1157 return changed;
1158}
1159
1160void QQuickShapeConicalGradientRhiShader::updateSampledImage(RenderState &state, int binding, QSGTexture **texture,
1161 QSGMaterial *newMaterial, QSGMaterial *)
1162{
1163 if (binding != 1)
1164 return;
1165
1166 QQuickShapeConicalGradientMaterial *m = static_cast<QQuickShapeConicalGradientMaterial *>(newMaterial);
1168 const QSGGradientCacheKey cacheKey(node->m_fillGradient.stops, QGradient::Spread(node->m_fillGradient.spread));
1169 QSGTexture *t = QSGGradientCache::cacheForRhi(state.rhi())->get(cacheKey);
1170 t->commitTextureOperations(state.rhi(), state.resourceUpdateBatch());
1171 *texture = t;
1172}
1173
1175{
1176 static QSGMaterialType type;
1177 return &type;
1178}
1179
1180int QQuickShapeConicalGradientMaterial::compare(const QSGMaterial *other) const
1181{
1182 Q_ASSERT(other && type() == other->type());
1183 const QQuickShapeConicalGradientMaterial *m = static_cast<const QQuickShapeConicalGradientMaterial *>(other);
1184
1187 Q_ASSERT(a && b);
1188 if (a == b)
1189 return 0;
1190
1191 const QSGGradientCache::GradientDesc *ga = &a->m_fillGradient;
1192 const QSGGradientCache::GradientDesc *gb = &b->m_fillGradient;
1193
1194 if (int d = ga->a.x() - gb->a.x())
1195 return d;
1196 if (int d = ga->a.y() - gb->a.y())
1197 return d;
1198
1199 if (int d = ga->v0 - gb->v0)
1200 return d;
1201
1202 if (int d = ga->stops.size() - gb->stops.size())
1203 return d;
1204
1205 for (int i = 0; i < ga->stops.size(); ++i) {
1206 if (int d = ga->stops[i].first - gb->stops[i].first)
1207 return d;
1208 if (int d = ga->stops[i].second.rgba() - gb->stops[i].second.rgba())
1209 return d;
1210 }
1211
1212 if (int d = a->m_fillTransform.compareTo(b->m_fillTransform))
1213 return d;
1214
1215 return 0;
1216}
1217
1218QSGMaterialShader *QQuickShapeConicalGradientMaterial::createShader(QSGRendererInterface::RenderMode renderMode) const
1219{
1220 Q_UNUSED(renderMode);
1221 return new QQuickShapeConicalGradientRhiShader(viewCount());
1222}
1223
1225{
1226 setShaderFileName(VertexStage, QStringLiteral(":/qt-project.org/shapes/shaders_ng/texturefill.vert.qsb"), viewCount);
1227 setShaderFileName(FragmentStage, QStringLiteral(":/qt-project.org/shapes/shaders_ng/texturefill.frag.qsb"), viewCount);
1228}
1229
1231 QSGMaterial *newMaterial, QSGMaterial *oldMaterial)
1232{
1233 Q_ASSERT(oldMaterial == nullptr || newMaterial->type() == oldMaterial->type());
1234 QQuickShapeTextureFillMaterial *m = static_cast<QQuickShapeTextureFillMaterial *>(newMaterial);
1235 bool changed = false;
1236 QByteArray *buf = state.uniformData();
1237 const int shaderMatrixCount = newMaterial->viewCount();
1238 const int matrixCount = qMin(state.projectionMatrixCount(), shaderMatrixCount);
1239 Q_ASSERT(buf->size() >= 64 * shaderMatrixCount + 64 + 8 + 4);
1240
1241 if (state.isMatrixDirty()) {
1242 for (int viewIndex = 0; viewIndex < matrixCount; ++viewIndex) {
1243 const QMatrix4x4 m = state.combinedMatrix();
1244 memcpy(buf->data() + 64 * viewIndex, m.constData(), 64);
1245 changed = true;
1246 }
1247 }
1248
1250
1251 if (!oldMaterial || m_fillTransform != node->m_fillTransform) {
1252 memcpy(buf->data() + 64 * shaderMatrixCount, node->m_fillTransform.invertedData(), 64);
1253 m_fillTransform = node->m_fillTransform;
1254 changed = true;
1255 }
1256
1257 const QSizeF boundsSize = node->m_fillTextureProvider != nullptr && node->m_fillTextureProvider->texture() != nullptr
1258 ? node->m_fillTextureProvider->texture()->textureSize()
1259 : QSizeF(0, 0);
1260
1261
1262 const QVector2D boundsVector(boundsSize.width() / state.devicePixelRatio(),
1263 boundsSize.height() / state.devicePixelRatio());
1264 if (!oldMaterial || m_boundsSize != boundsVector) {
1265 m_boundsSize = boundsVector;
1266 Q_ASSERT(sizeof(m_boundsSize) == 8);
1267 memcpy(buf->data() + 64 * shaderMatrixCount + 64, &m_boundsSize, 8);
1268 changed = true;
1269 }
1270
1271 if (state.isOpacityDirty()) {
1272 const float opacity = state.opacity();
1273 memcpy(buf->data() + 64 * shaderMatrixCount + 64 + 8, &opacity, 4);
1274 changed = true;
1275 }
1276
1277 return changed;
1278}
1279
1280void QQuickShapeTextureFillRhiShader::updateSampledImage(RenderState &state, int binding, QSGTexture **texture,
1281 QSGMaterial *newMaterial, QSGMaterial *)
1282{
1283 if (binding != 1)
1284 return;
1285
1286 QQuickShapeTextureFillMaterial *m = static_cast<QQuickShapeTextureFillMaterial *>(newMaterial);
1288 if (node->m_fillTextureProvider != nullptr) {
1289 QSGTexture *providedTexture = node->m_fillTextureProvider->texture();
1290 if (providedTexture != nullptr) {
1291 if (providedTexture->isAtlasTexture()) {
1292 // Create a non-atlas copy to make texture coordinate wrapping work. This
1293 // texture copy is owned by the QSGTexture so memory is managed with the original
1294 // texture provider.
1295 QSGTexture *newTexture = providedTexture->removedFromAtlas(state.resourceUpdateBatch());
1296 if (newTexture != nullptr)
1297 providedTexture = newTexture;
1298 }
1299
1300 providedTexture->commitTextureOperations(state.rhi(), state.resourceUpdateBatch());
1301 *texture = providedTexture;
1302 return;
1303 }
1304 }
1305
1306 if (m->dummyTexture() == nullptr) {
1307 QSGPlainTexture *dummyTexture = new QSGPlainTexture;
1308 dummyTexture->setFiltering(QSGTexture::Nearest);
1309 dummyTexture->setHorizontalWrapMode(QSGTexture::Repeat);
1310 dummyTexture->setVerticalWrapMode(QSGTexture::Repeat);
1311 QImage img(128, 128, QImage::Format_ARGB32_Premultiplied);
1312 img.fill(0);
1313 dummyTexture->setImage(img);
1314 dummyTexture->commitTextureOperations(state.rhi(), state.resourceUpdateBatch());
1315
1316 m->setDummyTexture(dummyTexture);
1317 }
1318
1319 *texture = m->dummyTexture();
1320}
1321
1323{
1324 delete m_dummyTexture;
1325}
1326
1328{
1329 static QSGMaterialType type;
1330 return &type;
1331}
1332
1333int QQuickShapeTextureFillMaterial::compare(const QSGMaterial *other) const
1334{
1335 Q_ASSERT(other && type() == other->type());
1336 const QQuickShapeTextureFillMaterial *m = static_cast<const QQuickShapeTextureFillMaterial *>(other);
1337
1340 Q_ASSERT(a && b);
1341 if (a == b)
1342 return 0;
1343
1344 if (int d = a->m_fillTransform.compareTo(b->m_fillTransform))
1345 return d;
1346
1347 const qintptr diff = qintptr(a->m_fillTextureProvider) - qintptr(b->m_fillTextureProvider);
1348 return diff < 0 ? -1 : (diff > 0 ? 1 : 0);
1349}
1350
1351QSGMaterialShader *QQuickShapeTextureFillMaterial::createShader(QSGRendererInterface::RenderMode renderMode) const
1352{
1353 Q_UNUSED(renderMode);
1354 return new QQuickShapeTextureFillRhiShader(viewCount());
1355}
1356
1357QT_END_NAMESPACE
1358
1359#include "moc_qquickshapegenericrenderer_p.cpp"
QSGMaterialType * type() const override
This function is called by the scene graph to query an identifier that is unique to the QSGMaterialSh...
QQuickShapeConicalGradientMaterial(QQuickShapeGenericStrokeFillNode *node)
int compare(const QSGMaterial *other) const override
Compares this material to other and returns 0 if they are equal; -1 if this material should sort befo...
QSGMaterialShader * createShader(QSGRendererInterface::RenderMode renderMode) const override
This function returns a new instance of a the QSGMaterialShader implementation used to render geometr...
QQuickShapeGenericStrokeFillNode * node() const
void updateSampledImage(RenderState &state, int binding, QSGTexture **texture, QSGMaterial *newMaterial, QSGMaterial *oldMaterial) override
This function is called by the scene graph to prepare use of sampled images in the shader,...
bool updateUniformData(RenderState &state, QSGMaterial *newMaterial, QSGMaterial *oldMaterial) override
This function is called by the scene graph to get the contents of the shader program's uniform buffer...
QQuickShapeGenericRenderer::Color4ub fillColor
QQuickShapeGenericStrokeFillNode * m_fillNode
QQuickShapeGenericStrokeFillNode * m_strokeNode
void setAsyncCallback(void(*)(void *), void *) override
void setTriangulationScale(qreal scale) override
void setFillGradient(int index, QQuickShapeGradient *gradient) override
void setFillTransform(int index, const QSGTransform &transform) override
void setCosmeticStroke(int index, bool c) override
void setPath(int index, const QPainterPath &path, QQuickShapePath::PathHints pathHints={}) override
void setStrokeColor(int index, const QColor &color) override
void setFillRule(int index, QQuickShapePath::FillRule fillRule) override
void setFillTextureProvider(int index, QQuickItem *textureProviderItem) override
void setFillColor(int index, const QColor &color) override
QVector< QSGGeometry::ColoredPoint2D > VertexContainerType
void setJoinStyle(int index, QQuickShapePath::JoinStyle joinStyle, int miterLimit) override
void setStrokeWidth(int index, qreal w) override
void setRootNode(QQuickShapeGenericNode *node)
void handleSceneChange(QQuickWindow *window) override
void beginSync(int totalCount, bool *countChanged) override
void setCapStyle(int index, QQuickShapePath::CapStyle capStyle) override
void setStrokeStyle(int index, QQuickShapePath::StrokeStyle strokeStyle, qreal dashOffset, const QVector< qreal > &dashPattern) override
void preprocess() override
Override this function to do processing on the node before it is rendered.
void activateMaterial(QQuickWindow *window, Material m)
QQuickShapeLinearGradientMaterial(QQuickShapeGenericStrokeFillNode *node)
QSGMaterialShader * createShader(QSGRendererInterface::RenderMode renderMode) const override
This function returns a new instance of a the QSGMaterialShader implementation used to render geometr...
int compare(const QSGMaterial *other) const override
Compares this material to other and returns 0 if they are equal; -1 if this material should sort befo...
QSGMaterialType * type() const override
This function is called by the scene graph to query an identifier that is unique to the QSGMaterialSh...
QQuickShapeGenericStrokeFillNode * node() const
void updateSampledImage(RenderState &state, int binding, QSGTexture **texture, QSGMaterial *newMaterial, QSGMaterial *oldMaterial) override
This function is called by the scene graph to prepare use of sampled images in the shader,...
bool updateUniformData(RenderState &state, QSGMaterial *newMaterial, QSGMaterial *oldMaterial) override
This function is called by the scene graph to get the contents of the shader program's uniform buffer...
QQuickShapeGenericStrokeFillNode * node() const
QQuickShapeRadialGradientMaterial(QQuickShapeGenericStrokeFillNode *node)
QSGMaterialType * type() const override
This function is called by the scene graph to query an identifier that is unique to the QSGMaterialSh...
QSGMaterialShader * createShader(QSGRendererInterface::RenderMode renderMode) const override
This function returns a new instance of a the QSGMaterialShader implementation used to render geometr...
int compare(const QSGMaterial *other) const override
Compares this material to other and returns 0 if they are equal; -1 if this material should sort befo...
bool updateUniformData(RenderState &state, QSGMaterial *newMaterial, QSGMaterial *oldMaterial) override
This function is called by the scene graph to get the contents of the shader program's uniform buffer...
void updateSampledImage(RenderState &state, int binding, QSGTexture **texture, QSGMaterial *newMaterial, QSGMaterial *oldMaterial) override
This function is called by the scene graph to prepare use of sampled images in the shader,...
QQuickShapeGenericRenderer::Color4ub strokeColor
QQuickShapeTextureFillMaterial(QQuickShapeGenericStrokeFillNode *node)
QSGMaterialShader * createShader(QSGRendererInterface::RenderMode renderMode) const override
This function returns a new instance of a the QSGMaterialShader implementation used to render geometr...
int compare(const QSGMaterial *other) const override
Compares this material to other and returns 0 if they are equal; -1 if this material should sort befo...
QQuickShapeGenericStrokeFillNode * node() const
QSGMaterialType * type() const override
This function is called by the scene graph to query an identifier that is unique to the QSGMaterialSh...
bool updateUniformData(RenderState &state, QSGMaterial *newMaterial, QSGMaterial *oldMaterial) override
This function is called by the scene graph to get the contents of the shader program's uniform buffer...
void updateSampledImage(RenderState &state, int binding, QSGTexture **texture, QSGMaterial *newMaterial, QSGMaterial *oldMaterial) override
This function is called by the scene graph to prepare use of sampled images in the shader,...
static QQuickShapeGenericRenderer::Color4ub colorToColor4ub(const QColor &c)
#define QSG_RUNTIME_DESCRIPTION
Definition qsgnode.h:16
void set(float nx, float ny, QQuickShapeGenericRenderer::Color4ub ncolor)