33#define DECLARE_DEBUG_VAR(variable) \
34 static bool debug_ ## variable() \
35 { static bool value = qgetenv("QSG_RENDERER_DEBUG").contains(QT_STRINGIFY(variable)); return value; }
45#undef DECLARE_DEBUG_VAR
47#define QSGNODE_TRAVERSE(NODE) for (QSGNode *child = NODE->firstChild(); child; child = child->nextSibling())
48#define SHADOWNODE_TRAVERSE(NODE) for (Node *child = NODE->firstChild(); child; child = child->sibling())
52 static int sizes[] = {
54 sizeof(
unsigned char),
56 sizeof(
unsigned short),
93 return (
v + byteAlign - 1) & ~(byteAlign - 1);
100 if (
a.tupleSize == 4)
102 if (
a.tupleSize == 3)
104 if (
a.tupleSize == 2)
106 if (
a.tupleSize == 1)
110 if (
a.tupleSize == 4)
112 if (
a.tupleSize == 2)
114 if (
a.tupleSize == 1)
120 qWarning(
"Unsupported attribute type 0x%x with %d components",
a.type,
a.tupleSize);
129 qWarning(
"No vertex shader in QSGMaterialShader %p",
s);
134 QVarLengthArray<QRhiVertexInputAttribute, 8> inputAttributes;
135 inputAttributes.reserve(attrCount + 1);
137 for (
int i = 0;
i < attrCount; ++
i) {
140 qWarning(
"Vertex input %d is present in material but not in shader. This is wrong.",
152 QVarLengthArray<QRhiVertexInputBinding, 2> inputBindings;
158 inputLayout.
setBindings(inputBindings.cbegin(), inputBindings.cend());
159 inputLayout.
setAttributes(inputAttributes.cbegin(), inputAttributes.cend());
181 switch (geomDrawMode) {
198 qWarning(
"Primitive topology 0x%x not supported", geomDrawMode);
275 stockShaders.clear();
277 rewrittenShaders.clear();
306 static int extraIndent = 0;
309 QByteArray ind(indent + extraIndent + 10,
' ');
314 qDebug() << ind.
constData() <<
"- parent:" <<
i->parentRoot <<
"orders" <<
i->firstOrder <<
"->" <<
i->lastOrder <<
", avail:" <<
i->availableOrders;
327#ifndef QT_NO_DEBUG_OUTPUT
328 static int indent = 0;
340 d <<
"order" <<
Qt::dec <<
n->element()->order;
359 m_rootMatrices.
add(m_identityMatrix);
367 m_transformChange = 0;
370 Node *sn = renderer->m_nodes.value(
n, 0);
377 qDebug(
"Updater::updateStates()");
379 qDebug(
" - nodes have been added");
381 qDebug(
" - transforms have changed");
383 qDebug(
" - opacity has changed");
396 if (m_added == 0 &&
n->dirtyState == 0 &&
m_force_update == 0 && m_transformChange == 0 && m_opacityChange == 0)
422 n->renderNodeElement()->root = m_roots.last();
440 if (m_roots.last() && m_added > 0)
441 renderer->registerBatchRoot(
n, m_roots.last());
448 cn->setRendererMatrix(&extra->
matrix);
468 bool was =
n->isOpaque;
471 renderer->m_rebuild = Renderer::FullRebuild;
488 bool popMatrixStack =
false;
489 bool popRootStack =
false;
494 if (
n->isBatchRoot) {
495 if (m_added > 0 && m_roots.last())
496 renderer->registerBatchRoot(
n, m_roots.last());
505 it !=
info->subRoots.constEnd(); ++
it) {
511 n->becameBatchRoot =
false;
515 m_rootMatrices.
add(tn->combinedMatrix());
517 popMatrixStack =
true;
519 }
else if (!tn->matrix().isIdentity()) {
522 popMatrixStack =
true;
552 e->
root = m_roots.last();
557 while (
info !=
nullptr) {
558 info->availableOrders--;
559 if (
info->availableOrders < 0) {
560 renderer->m_rebuild |= Renderer::BuildRenderLists;
562 renderer->m_rebuild |= Renderer::BuildRenderListsForTaggedRoots;
563 renderer->m_taggedRoots << e->
root;
565 if (
info->parentRoot !=
nullptr)
566 info = renderer->batchRootInfo(
info->parentRoot);
571 renderer->m_rebuild |= Renderer::FullRebuild;
574 if (m_transformChange) {
578 if (m_opacityChange) {
581 renderer->invalidateBatchAndOverlappingRenderOrders(e->
batch);
610 it !=
info->subRoots.constEnd(); ++
it) {
618 for (
int a=0;
a<
g->attributeCount(); ++
a) {
631 const float *
m =
matrix.constData();
652 set(FLT_MAX, FLT_MAX, -FLT_MAX, -FLT_MAX);
660void Element::computeBounds()
669 bounds.
set(-FLT_MAX, -FLT_MAX, FLT_MAX, FLT_MAX);
673 bounds.
set(FLT_MAX, FLT_MAX, -FLT_MAX, -FLT_MAX);
674 char *vd = (
char *)
g->vertexData() +
offset;
675 for (
int i=0;
i<
g->vertexCount(); ++
i) {
677 vd +=
g->sizeOfVertex();
723 Q_ASSERT_X(e,
"Batch::geometryWasChanged",
"Batch is expected to 'valid' at this time");
831 for (
int i=0;
i<batches.size(); ++
i) {
840 , m_renderMode(renderMode)
841 , m_opaqueRenderList(64)
842 , m_alphaRenderList(64)
843 , m_nextRenderOrder(0)
844 , m_partialRebuild(
false)
845 , m_partialRebuildRoot(
nullptr)
846 , m_forceNoDepthBuffer(
false)
847 , m_opaqueBatches(16)
850 , m_elementsToDelete(64)
851 , m_tmpAlphaElements(16)
852 , m_tmpOpaqueElements(16)
855 , m_rebuild(FullRebuild)
857#
if defined(QSGBATCHRENDERER_INVALIDATE_WEDGED_NODES)
858 , m_renderOrderRebuildLower(-1)
859 , m_renderOrderRebuildUpper(-1)
863 , m_vertexUploadPool(256)
864 , m_indexUploadPool(64)
873 m_uint32IndexForRhi =
true;
883 if (!m_shaderManager) {
890 m_batchNodeThreshold =
qt_sg_envInt(
"QSG_RENDERER_BATCH_NODE_THRESHOLD", 64);
891 m_batchVertexThreshold =
qt_sg_envInt(
"QSG_RENDERER_BATCH_VERTEX_THRESHOLD", 1024);
892 m_srbPoolThreshold =
qt_sg_envInt(
"QSG_RENDERER_SRB_POOL_THRESHOLD", 1024);
894 if (
Q_UNLIKELY(debug_build() || debug_render())) {
895 qDebug(
"Batch thresholds: nodes: %d vertices: %d Srb pool threshold: %d",
896 m_batchNodeThreshold, m_batchVertexThreshold, m_srbPoolThreshold);
925 for (
int i = 0;
i < m_opaqueBatches.size(); ++
i)
927 for (
int i = 0;
i < m_alphaBatches.size(); ++
i)
929 for (
int i = 0;
i < m_batchPool.size(); ++
i)
931 for (
int i = 0;
i < m_vboPool.
size(); ++
i)
932 delete m_vboPool.
at(
i);
933 for (
int i = 0;
i < m_iboPool.
size(); ++
i)
934 delete m_iboPool.
at(
i);
937 for (
Node *
n : std::as_const(m_nodes)) {
941 m_elementsToDelete.add(e);
943 m_nodeAllocator.release(
n);
947 for (
int i=0;
i<m_elementsToDelete.size(); ++
i)
948 releaseElement(m_elementsToDelete.at(
i),
true);
950 destroyGraphicsResources();
955void Renderer::destroyGraphicsResources()
964 m_stencilClipCommon.reset();
965 delete m_dummyTexture;
973 destroyGraphicsResources();
976 m_dummyTexture =
nullptr;
980 m_vertexUploadPool.
shrink(0);
981 m_vertexUploadPool.
reset();
982 m_indexUploadPool.
shrink(0);
983 m_indexUploadPool.
reset();
985 for (
int i = 0;
i < m_vboPool.
size(); ++
i)
986 delete m_vboPool.
at(
i);
989 for (
int i = 0;
i < m_iboPool.
size(); ++
i)
990 delete m_iboPool.
at(
i);
994void Renderer::invalidateAndRecycleBatch(
Batch *
b)
996 if (
b->vbo.buf !=
nullptr)
997 m_vboPool.
add(
b->vbo.buf);
998 if (
b->ibo.buf !=
nullptr)
999 m_iboPool.
add(
b->ibo.buf);
1000 b->vbo.buf =
nullptr;
1001 b->ibo.buf =
nullptr;
1003 for (
int i=0;
i<m_batchPool.size(); ++
i)
1004 if (
b == m_batchPool.at(
i))
1014 QDataBuffer<char> &
pool = isIndexBuf ? m_indexUploadPool : m_vertexUploadPool;
1016 pool.resize(byteSize);
1018 }
else if (
buffer->size != byteSize) {
1020 buffer->data = (
char *) malloc(byteSize);
1031 QDataBuffer<QRhiBuffer *> *bufferPool = isIndexBuf ? &m_iboPool : &m_vboPool;
1032 if (!
buffer->buf && bufferPool->isEmpty()) {
1036 if (!
buffer->buf->create()) {
1037 qWarning(
"Failed to build vertex/index buffer of size %u",
buffer->size);
1048 || (testBuffer->size() >= expectedSize && testBuffer->size() <
buffer->buf->size())
1049 || (testBuffer->size() < expectedSize && testBuffer->size() >
buffer->buf->size())) {
1050 foundBufferIndex =
i;
1051 buffer->buf = testBuffer;
1052 if (
buffer->buf->size() == expectedSize)
1057 if (foundBufferIndex < bufferPool->
size() - 1) {
1058 qSwap(bufferPool->data()[foundBufferIndex],
1059 bufferPool->data()[bufferPool->size() - 1]);
1061 bufferPool->pop_back();
1064 bool needsRebuild =
false;
1067 needsRebuild =
true;
1073 buffer->nonDynamicChangeCount = 0;
1074 needsRebuild =
true;
1077 if (!
buffer->buf->create()) {
1078 qWarning(
"Failed to (re)build vertex/index buffer of size %u",
buffer->size);
1088 buffer->nonDynamicChangeCount += 1;
1098BatchRootInfo *Renderer::batchRootInfo(
Node *node)
1100 BatchRootInfo *
info = node->rootInfo();
1103 info =
new ClipBatchRootInfo;
1106 info =
new BatchRootInfo;
1113void Renderer::removeBatchRootFromParent(
Node *childRoot)
1115 BatchRootInfo *childInfo = batchRootInfo(childRoot);
1116 if (!childInfo->parentRoot)
1118 BatchRootInfo *parentInfo = batchRootInfo(childInfo->parentRoot);
1120 Q_ASSERT(parentInfo->subRoots.contains(childRoot));
1121 parentInfo->subRoots.remove(childRoot);
1122 childInfo->parentRoot =
nullptr;
1125void Renderer::registerBatchRoot(
Node *subRoot,
Node *parentRoot)
1127 BatchRootInfo *subInfo = batchRootInfo(subRoot);
1128 BatchRootInfo *parentInfo = batchRootInfo(parentRoot);
1129 subInfo->parentRoot = parentRoot;
1130 parentInfo->subRoots << subRoot;
1133bool Renderer::changeBatchRoot(
Node *node,
Node *root)
1135 BatchRootInfo *subInfo = batchRootInfo(node);
1136 if (subInfo->parentRoot == root)
1138 if (subInfo->parentRoot) {
1139 BatchRootInfo *oldRootInfo = batchRootInfo(subInfo->parentRoot);
1140 oldRootInfo->subRoots.remove(node);
1142 BatchRootInfo *newRootInfo = batchRootInfo(root);
1143 newRootInfo->subRoots << node;
1144 subInfo->parentRoot = root;
1148void Renderer::nodeChangedBatchRoot(
Node *node,
Node *root)
1153 changeBatchRoot(node, root);
1160 e->boundsComputed =
false;
1163 RenderNodeElement *e = node->renderNodeElement();
1169 nodeChangedBatchRoot(
child, root);
1172void Renderer::nodeWasTransformed(
Node *node,
int *vertexCount)
1179 e->boundsComputed =
false;
1181 if (!e->batch->isOpaque) {
1182 invalidateBatchAndOverlappingRenderOrders(e->batch);
1183 }
else if (e->batch->merged) {
1184 e->batch->needsUpload =
true;
1191 nodeWasTransformed(
child, vertexCount);
1194void Renderer::nodeWasAdded(
QSGNode *node,
Node *shadowParent)
1200 Node *snode = m_nodeAllocator.allocate();
1201 snode->sgNode = node;
1202 m_nodes.insert(node, snode);
1204 shadowParent->append(snode);
1207 snode->data = m_elementAllocator.allocate();
1211 snode->data =
new ClipBatchRootInfo;
1212 m_rebuild |= FullRebuild;
1216 RenderNodeElement *e =
new RenderNodeElement(rn);
1218 Q_ASSERT(!m_renderNodeElements.contains(rn));
1219 m_renderNodeElements.insert(e->renderNode, e);
1221 m_forceNoDepthBuffer =
true;
1222 m_rebuild |= FullRebuild;
1226 nodeWasAdded(
child, snode);
1229void Renderer::nodeWasRemoved(
Node *node)
1239 node->remove(
child);
1240 nodeWasRemoved(
child);
1241 child = node->firstChild();
1249 m_elementsToDelete.add(e);
1252 BatchRootInfo *
info = batchRootInfo(e->root);
1253 info->availableOrders++;
1256 e->batch->needsUpload =
true;
1257 e->batch->needsPurge =
true;
1263 removeBatchRootFromParent(node);
1264 delete node->clipInfo();
1265 m_rebuild |= FullRebuild;
1266 m_taggedRoots.remove(node);
1268 }
else if (node->isBatchRoot) {
1269 removeBatchRootFromParent(node);
1270 delete node->rootInfo();
1271 m_rebuild |= FullRebuild;
1272 m_taggedRoots.remove(node);
1275 RenderNodeElement *e = m_renderNodeElements.take(
static_cast<QSGRenderNode *
>(node->sgNode));
1278 m_elementsToDelete.add(e);
1279 if (m_renderNodeElements.isEmpty()) {
1280 m_forceNoDepthBuffer =
false;
1284 m_rebuild |= FullRebuild;
1287 if (e->batch !=
nullptr)
1288 e->batch->needsPurge =
true;
1292 Q_ASSERT(m_nodes.contains(node->sgNode));
1294 m_nodeAllocator.release(m_nodes.take(node->sgNode));
1297void Renderer::turnNodeIntoBatchRoot(
Node *node)
1300 m_rebuild |= FullRebuild;
1301 node->isBatchRoot =
true;
1302 node->becameBatchRoot =
true;
1304 Node *
p = node->parent();
1307 registerBatchRoot(node,
p);
1314 nodeChangedBatchRoot(
child, node);
1320#ifndef QT_NO_DEBUG_OUTPUT
1325 debug <<
"Geometry";
1327 debug <<
"Material";
1337 debug <<
"SubtreeBlocked";
1339 debug <<
"ForceUpdate";
1352 Node *sn = m_nodes.value(node);
1356 m_rebuild |= FullRebuild;
1359 if (blocked && sn) {
1361 Q_ASSERT(m_nodes.value(node) == 0);
1362 }
else if (!blocked && !sn) {
1374 nodeWasAdded(node,
nullptr);
1376 nodeWasAdded(node, m_nodes.value(node->
parent()));
1380 Node *shadowNode = m_nodes.value(node);
1389 shadowNode->dirtyState |=
state;
1393 if (node->m_subtreeRenderableCount > m_batchNodeThreshold) {
1394 turnNodeIntoBatchRoot(shadowNode);
1397 nodeWasTransformed(shadowNode, &vertices);
1398 if (vertices > m_batchVertexThreshold) {
1399 turnNodeIntoBatchRoot(shadowNode);
1406 Element *e = shadowNode->element();
1412 invalidateBatchAndOverlappingRenderOrders(e->
batch);
1414 b->needsUpload =
true;
1421 Element *e = shadowNode->element();
1425 m_rebuild |= Renderer::FullRebuild;
1427 }
else if (e->
batch) {
1429 invalidateBatchAndOverlappingRenderOrders(e->
batch);
1431 m_rebuild |= Renderer::BuildBatches;
1442 if (dirtyChain != 0) {
1443 dirtyChain = QSGNode::DirtyState(dirtyChain << 16);
1455 parent->remove(shadowNode);
1456 nodeWasRemoved(shadowNode);
1457 Q_ASSERT(m_nodes.value(node) == 0);
1477void Renderer::buildRenderLists(
QSGNode *node)
1482 Node *shadowNode = m_nodes.value(node);
1488 Element *e = shadowNode->element();
1492 if (opaque && useDepthBuffer())
1493 m_opaqueRenderList << e;
1495 m_alphaRenderList << e;
1497 e->
order = ++m_nextRenderOrder;
1499 if (m_partialRebuild)
1500 e->orphaned =
false;
1504 BatchRootInfo *
info = batchRootInfo(shadowNode);
1505 if (node == m_partialRebuildRoot) {
1506 m_nextRenderOrder =
info->firstOrder;
1508 buildRenderLists(
child);
1509 m_nextRenderOrder =
info->lastOrder + 1;
1511 int currentOrder = m_nextRenderOrder;
1513 buildRenderLists(
child);
1514 int padding = (m_nextRenderOrder - currentOrder) >> 2;
1515 info->firstOrder = currentOrder;
1516 info->availableOrders = padding;
1517 info->lastOrder = m_nextRenderOrder + padding;
1518 m_nextRenderOrder =
info->lastOrder;
1522 RenderNodeElement *e = shadowNode->renderNodeElement();
1523 m_alphaRenderList << e;
1524 e->order = ++m_nextRenderOrder;
1529 buildRenderLists(
child);
1532void Renderer::tagSubRoots(
Node *node)
1534 BatchRootInfo *
i = batchRootInfo(node);
1535 m_taggedRoots << node;
1545 for (
int i=0;
i<renderList.size(); ++
i) {
1556 for (
int i=0;
i<orphans.size(); ++
i) {
1578void Renderer::buildRenderListsForTaggedRoots()
1592 QSet<Node *> roots = m_taggedRoots;
1594 it != roots.constEnd(); ++
it) {
1598 for (
int i=0;
i<m_opaqueBatches.size(); ++
i) {
1599 Batch *
b = m_opaqueBatches.at(
i);
1600 if (m_taggedRoots.contains(
b->root))
1601 invalidateAndRecycleBatch(
b);
1604 for (
int i=0;
i<m_alphaBatches.size(); ++
i) {
1605 Batch *
b = m_alphaBatches.at(
i);
1606 if (m_taggedRoots.contains(
b->root))
1607 invalidateAndRecycleBatch(
b);
1610 m_opaqueRenderList.reset();
1611 m_alphaRenderList.reset();
1612 int maxRenderOrder = m_nextRenderOrder;
1613 m_partialRebuild =
true;
1616 it != m_taggedRoots.constEnd(); ++
it) {
1618 BatchRootInfo *
i = batchRootInfo(root);
1619 if ((!
i->parentRoot || !m_taggedRoots.contains(
i->parentRoot))
1621 m_nextRenderOrder =
i->firstOrder;
1622 m_partialRebuildRoot = root->sgNode;
1623 buildRenderLists(root->sgNode);
1626 m_partialRebuild =
false;
1627 m_partialRebuildRoot =
nullptr;
1628 m_taggedRoots.clear();
1629 m_nextRenderOrder =
qMax(m_nextRenderOrder, maxRenderOrder);
1635 if (m_opaqueRenderList.size())
1637 if (m_alphaRenderList.size())
1642void Renderer::buildRenderListsFromScratch()
1644 m_opaqueRenderList.reset();
1645 m_alphaRenderList.reset();
1647 for (
int i=0;
i<m_opaqueBatches.size(); ++
i)
1648 invalidateAndRecycleBatch(m_opaqueBatches.at(
i));
1649 for (
int i=0;
i<m_alphaBatches.size(); ++
i)
1650 invalidateAndRecycleBatch(m_alphaBatches.at(
i));
1651 m_opaqueBatches.reset();
1652 m_alphaBatches.reset();
1654 m_nextRenderOrder = 0;
1659void Renderer::invalidateBatchAndOverlappingRenderOrders(Batch *batch)
1664#if defined(QSGBATCHRENDERER_INVALIDATE_WEDGED_NODES)
1665 if (m_renderOrderRebuildLower < 0 || batch->
first->order < m_renderOrderRebuildLower)
1666 m_renderOrderRebuildLower = batch->first->order;
1667 if (m_renderOrderRebuildUpper < 0 || batch->lastOrderInBatch > m_renderOrderRebuildUpper)
1668 m_renderOrderRebuildUpper = batch->lastOrderInBatch;
1670 int first = m_renderOrderRebuildLower;
1671 int last = m_renderOrderRebuildUpper;
1673 int first = batch->first->order;
1674 int last = batch->lastOrderInBatch;
1677 batch->invalidate();
1679 for (
int i=0;
i<m_alphaBatches.size(); ++
i) {
1680 Batch *
b = m_alphaBatches.at(
i);
1682 int bf =
b->first->order;
1683 int bl =
b->lastOrderInBatch;
1684 if (bl >
first && bf < last)
1689 m_rebuild |= BuildBatches;
1695void Renderer::cleanupBatches(QDataBuffer<Batch *> *batches) {
1696 if (batches->size()) {
1699 while (count < batches->
size() && batches->at(
count)->first)
1701 for (
int i=
count;
i<batches->size(); ++
i)
1702 invalidateAndRecycleBatch(batches->at(
i));
1703 batches->resize(
count);
1707void Renderer::prepareOpaqueBatches()
1709 for (
int i=m_opaqueRenderList.size() - 1;
i >= 0; --
i) {
1710 Element *ei = m_opaqueRenderList.at(
i);
1711 if (!ei || ei->batch || ei->node->geometry()->vertexCount() == 0)
1713 Batch *batch = newBatch();
1715 batch->root = ei->root;
1716 batch->isOpaque =
true;
1717 batch->needsUpload =
true;
1720 m_opaqueBatches.add(batch);
1727 for (
int j =
i - 1;
j >= 0; --
j) {
1728 Element *ej = m_opaqueRenderList.at(
j);
1731 if (ej->root != ei->root)
1733 if (ej->batch || ej->node->geometry()->vertexCount() == 0)
1738 if (gni->clipList() == gnj->clipList()
1739 && gni->geometry()->drawingMode() == gnj->geometry()->drawingMode()
1740 && (gni->geometry()->drawingMode() !=
QSGGeometry::DrawLines || gni->geometry()->lineWidth() == gnj->geometry()->lineWidth())
1741 && gni->geometry()->attributes() == gnj->geometry()->attributes()
1742 && gni->inheritedOpacity() == gnj->inheritedOpacity()
1743 && gni->activeMaterial()->type() == gnj->activeMaterial()->type()
1744 && gni->activeMaterial()->viewCount() == gnj->activeMaterial()->viewCount()
1745 && gni->activeMaterial()->compare(gnj->activeMaterial()) == 0) {
1747 next->nextInBatch = ej;
1752 batch->lastOrderInBatch =
next->order;
1756bool Renderer::checkOverlap(
int first,
int last,
const Rect &bounds)
1759 Element *e = m_alphaRenderList.at(
i);
1760#if defined(QSGBATCHRENDERER_INVALIDATE_WEDGED_NODES)
1767 if (e->bounds.intersects(bounds))
1784void Renderer::prepareAlphaBatches()
1786 for (
int i=0;
i<m_alphaRenderList.size(); ++
i) {
1787 Element *e = m_alphaRenderList.at(
i);
1788 if (!e || e->isRenderNode)
1791 e->ensureBoundsValid();
1794 for (
int i=0;
i<m_alphaRenderList.size(); ++
i) {
1795 Element *ei = m_alphaRenderList.at(
i);
1796 if (!ei || ei->batch)
1799 if (ei->isRenderNode) {
1800 Batch *rnb = newBatch();
1802 rnb->root = ei->root;
1803 rnb->isOpaque =
false;
1804 rnb->isRenderNode =
true;
1806 m_alphaBatches.add(rnb);
1810 if (ei->node->geometry()->vertexCount() == 0)
1813 Batch *batch = newBatch();
1815 batch->root = ei->root;
1816 batch->isOpaque =
false;
1817 batch->needsUpload =
true;
1818 m_alphaBatches.add(batch);
1825 overlapBounds.set(FLT_MAX, FLT_MAX, -FLT_MAX, -FLT_MAX);
1829 for (
int j =
i + 1;
j < m_alphaRenderList.size(); ++
j) {
1830 Element *ej = m_alphaRenderList.at(
j);
1833 if (ej->root != ei->root || ej->isRenderNode)
1836#if !defined(QSGBATCHRENDERER_INVALIDATE_WEDGED_NODES)
1837 overlapBounds |= ej->bounds;
1843 if (gnj->geometry()->vertexCount() == 0)
1846 if (gni->clipList() == gnj->clipList()
1847 && gni->geometry()->drawingMode() == gnj->geometry()->drawingMode()
1849 || (gni->geometry()->lineWidth() == gnj->geometry()->lineWidth()
1852 && gni->geometry()->lineWidth() == 1.0f))
1853 && gni->geometry()->attributes() == gnj->geometry()->attributes()
1854 && gni->inheritedOpacity() == gnj->inheritedOpacity()
1855 && gni->activeMaterial()->type() == gnj->activeMaterial()->type()
1856 && gni->activeMaterial()->viewCount() == gnj->activeMaterial()->viewCount()
1857 && gni->activeMaterial()->compare(gnj->activeMaterial()) == 0) {
1858 if (!overlapBounds.intersects(ej->bounds) || !checkOverlap(
i+1,
j - 1, ej->bounds)) {
1860 next->nextInBatch = ej;
1871 overlapBounds |= ej->bounds;
1875 batch->lastOrderInBatch =
next->order;
1893 return iCount - (iCount % 2);
1896 return iCount - (iCount % 3);
1922void Renderer::uploadMergedElement(
Element *e,
int vaOffset,
char **
vertexData,
char **zData,
char **indexData,
void *iBasePtr,
int *indexCount)
1927 const QMatrix4x4 &localx = *e->node->matrix();
1928 const float *localxdata = localx.constData();
1930 const int vCount =
g->vertexCount();
1931 const int vSize =
g->sizeOfVertex();
1932 memcpy(*
vertexData,
g->vertexData(), vSize * vCount);
1937 for (
int i=0;
i<vCount; ++
i) {
1938 Pt *
p = (Pt *) vdata;
1939 p->x += localxdata[12];
1940 p->y += localxdata[13];
1944 for (
int i=0;
i<vCount; ++
i) {
1945 ((Pt *) vdata)->map(localx);
1950 if (useDepthBuffer()) {
1951 float *vzorder = (
float *) *zData;
1953 for (
int i=0;
i<vCount; ++
i)
1954 vzorder[
i] = zorder;
1955 *zData += vCount *
sizeof(float);
1958 int iCount =
g->indexCount();
1959 if (m_uint32IndexForRhi) {
1970 for (
int i=0;
i<iCount; ++
i)
1974 const quint16 *srcIndices =
g->indexDataAsUShort();
1976 *
indices++ = *iBase + srcIndices[0];
1980 for (
int i=0;
i<iCount; ++
i)
1999 for (
int i=0;
i<iCount; ++
i)
2002 const quint16 *srcIndices =
g->indexDataAsUShort();
2004 *
indices++ = *iBase + srcIndices[0];
2008 for (
int i=0;
i<iCount; ++
i)
2019 *indexData += iCount * mergedIndexElemSize();
2020 *indexCount += iCount;
2032void Renderer::uploadBatch(Batch *
b)
2035 if (!
b->needsUpload) {
2036 if (
Q_UNLIKELY(debug_upload()))
qDebug() <<
" Batch:" <<
b <<
"already uploaded...";
2045 if (
b->isRenderNode) {
2046 if (
Q_UNLIKELY(debug_upload()))
qDebug() <<
" Batch: " <<
b <<
"is a render node...";
2056 QSGMaterial::Flags
flags = gn->activeMaterial()->flags();
2059 &&
b->positionAttribute >= 0
2063 &&
b->isSafeToBatch();
2065 b->merged = canMerge;
2070 int unmergedIndexSize = 0;
2075 b->vertexCount += eg->vertexCount();
2076 int iCount = eg->indexCount();
2079 iCount = eg->vertexCount();
2082 const int effectiveIndexSize = m_uint32IndexForRhi ?
sizeof(
quint32) : eg->sizeOfIndex();
2083 unmergedIndexSize += iCount * effectiveIndexSize;
2085 b->indexCount += iCount;
2091 if (
b->vertexCount == 0 || (
b->merged &&
b->indexCount == 0))
2106 int bufferSize =
b->vertexCount *
g->sizeOfVertex();
2107 int ibufferSize = 0;
2109 ibufferSize =
b->indexCount * mergedIndexElemSize();
2110 if (useDepthBuffer())
2111 bufferSize +=
b->vertexCount *
sizeof(float);
2113 ibufferSize = unmergedIndexSize;
2116 map(&
b->ibo, ibufferSize,
true);
2117 map(&
b->vbo, bufferSize);
2119 if (
Q_UNLIKELY(debug_upload()))
qDebug() <<
" - batch" <<
b <<
" first:" <<
b->first <<
" root:"
2120 <<
b->root <<
" merged:" <<
b->merged <<
" positionAttribute" <<
b->positionAttribute
2121 <<
" vbo:" <<
b->vbo.buf <<
":" <<
b->vbo.size;
2125 char *zData =
vertexData +
b->vertexCount *
g->sizeOfVertex();
2126 char *indexData =
b->ibo.data;
2131 uint verticesInSet = 0;
2135 const uint verticesInSetLimit = m_uint32IndexForRhi ? 0xfffffffe : 0xfffe;
2136 int indicesInSet = 0;
2137 b->drawSets.reset();
2138 int drawSetIndices = 0;
2139 const char *indexBase =
b->ibo.data;
2140 b->drawSets << DrawSet(0, zData -
vertexData, drawSetIndices);
2142 verticesInSet += e->node->
geometry()->vertexCount();
2143 if (verticesInSet > verticesInSetLimit) {
2144 b->drawSets.last().indexCount = indicesInSet;
2146 b->drawSets.last().indices += 1 * mergedIndexElemSize();
2147 b->drawSets.last().indexCount -= 2;
2149 drawSetIndices = indexData - indexBase;
2151 zData -
b->vbo.data,
2155 verticesInSet = e->node->
geometry()->vertexCount();
2158 void *iBasePtr = &iOffset16;
2159 if (m_uint32IndexForRhi)
2160 iBasePtr = &iOffset32;
2161 uploadMergedElement(e,
b->positionAttribute, &
vertexData, &zData, &indexData, iBasePtr, &indicesInSet);
2164 b->drawSets.last().indexCount = indicesInSet;
2168 b->drawSets.last().indices += 1 * mergedIndexElemSize();
2169 b->drawSets.last().indexCount -= 2;
2172 char *vboData =
b->vbo.data;
2173 char *iboData =
b->ibo.data;
2177 int vbs =
g->vertexCount() *
g->sizeOfVertex();
2178 memcpy(vboData,
g->vertexData(), vbs);
2179 vboData = vboData + vbs;
2180 const int indexCount =
g->indexCount();
2182 const int effectiveIndexSize = m_uint32IndexForRhi ?
sizeof(
quint32) :
g->sizeOfIndex();
2183 const int ibs = indexCount * effectiveIndexSize;
2184 if (
g->sizeOfIndex() == effectiveIndexSize) {
2185 memcpy(iboData,
g->indexData(), ibs);
2187 if (
g->sizeOfIndex() ==
sizeof(
quint16) && effectiveIndexSize ==
sizeof(
quint32)) {
2190 for (
int i = 0;
i < indexCount; ++
i)
2193 Q_ASSERT_X(
false,
"uploadBatch (unmerged)",
"uint index with ushort effective index - cannot happen");
2201#ifndef QT_NO_DEBUG_OUTPUT
2203 const char *vd =
b->vbo.data;
2204 qDebug() <<
" -- Vertex Data, count:" <<
b->vertexCount <<
" - " <<
g->sizeOfVertex() <<
"bytes/vertex";
2205 for (
int i=0;
i<
b->vertexCount; ++
i) {
2207 dump <<
" --- " <<
i <<
": ";
2209 for (
int a=0;
a<
g->attributeCount(); ++
a) {
2217 dump << *(
const float *)(vd +
offset +
t *
sizeof(float)) <<
" ";
2221 dump << *(
const unsigned char *)(vd +
offset +
t *
sizeof(
unsigned char)) <<
" ";
2226 if (
b->merged && useDepthBuffer()) {
2227 float zorder = ((
float*)(
b->vbo.data +
b->vertexCount *
g->sizeOfVertex()))[
i];
2228 dump <<
" Z:(" << zorder <<
")";
2230 vd +=
g->sizeOfVertex();
2233 if (!
b->drawSets.isEmpty()) {
2234 if (m_uint32IndexForRhi) {
2238 iDump <<
" -- Index Data, count:" <<
b->indexCount;
2239 for (
int i=0;
i<
b->indexCount; ++
i) {
2249 iDump <<
" -- Index Data, count:" <<
b->indexCount;
2250 for (
int i=0;
i<
b->indexCount; ++
i) {
2258 for (
int i=0;
i<
b->drawSets.size(); ++
i) {
2259 const DrawSet &
s =
b->drawSets.at(
i);
2260 qDebug() <<
" -- DrawSet: indexCount:" <<
s.indexCount <<
" vertices:" <<
s.vertices <<
" z:" <<
s.zorders <<
" indices:" <<
s.indices;
2267 unmap(&
b->ibo,
true);
2269 if (
Q_UNLIKELY(debug_upload()))
qDebug() <<
" --- vertex/index buffers unmapped, batch upload completed...";
2271 b->needsUpload =
false;
2274 b->uploadedThisFrame =
true;
2277void Renderer::applyClipStateToGraphicsState()
2283QRhiGraphicsPipeline *Renderer::buildStencilPipeline(
const Batch *batch,
bool firstStencilClipInBatch)
2293 if (firstStencilClipInBatch) {
2318 qWarning(
"Failed to build stencil clip pipeline");
2326void Renderer::updateClipState(
const QSGClipNode *clipList, Batch *batch)
2340 batch->stencilClipState.updateStencilBuffer =
false;
2342 applyClipStateToGraphicsState();
2343 batch->clipState = m_currentClipState;
2349 QVarLengthArray<const QSGClipNode *, 4> stencilClipNodes;
2352 batch->stencilClipState.drawCalls.reset();
2356 const quint32 StencilClipUbufSize = 64;
2368 if (isRectangleWithNoPerspective && (noRotate || isRotate90)) {
2370 qreal invW = 1 /
m(3, 3);
2371 qreal fx1, fy1, fx2, fy2;
2373 fx1 = (bbox.left() *
m(0, 0) +
m(0, 3)) * invW;
2374 fy1 = (bbox.bottom() *
m(1, 1) +
m(1, 3)) * invW;
2375 fx2 = (bbox.right() *
m(0, 0) +
m(0, 3)) * invW;
2376 fy2 = (bbox.top() *
m(1, 1) +
m(1, 3)) * invW;
2379 fx1 = (bbox.bottom() *
m(0, 1) +
m(0, 3)) * invW;
2380 fy1 = (bbox.left() *
m(1, 0) +
m(1, 3)) * invW;
2381 fx2 = (bbox.top() *
m(0, 1) +
m(0, 3)) * invW;
2382 fy2 = (bbox.right() *
m(1, 0) +
m(1, 3)) * invW;
2399 scissorRect =
QRect(ix1, iy1, ix2 - ix1, iy2 - iy1);
2401 scissorRect &=
QRect(ix1, iy1, ix2 - ix1, iy2 - iy1);
2409 const int vertexByteSize =
g->sizeOfVertex() *
g->vertexCount();
2411 totalVSize =
aligned(totalVSize, 4u) + vertexByteSize;
2412 if (
g->indexCount()) {
2413 const int indexByteSize =
g->sizeOfIndex() *
g->indexCount();
2415 totalISize =
aligned(totalISize, 4u) + indexByteSize;
2418 totalUSize =
aligned(totalUSize, m_ubufAlignment) + StencilClipUbufSize;
2420 stencilClipNodes.append(clip);
2427 bool rebuildVBuf =
false;
2428 if (!batch->stencilClipState.vbuf) {
2431 }
else if (batch->stencilClipState.vbuf->size() < totalVSize) {
2432 batch->stencilClipState.vbuf->
setSize(totalVSize);
2436 if (!batch->stencilClipState.vbuf->create()) {
2437 qWarning(
"Failed to build stencil clip vertex buffer");
2438 delete batch->stencilClipState.vbuf;
2439 batch->stencilClipState.vbuf =
nullptr;
2445 bool rebuildIBuf =
false;
2446 if (!batch->stencilClipState.ibuf) {
2449 }
else if (batch->stencilClipState.ibuf->size() < totalISize) {
2450 batch->stencilClipState.ibuf->
setSize(totalISize);
2454 if (!batch->stencilClipState.ibuf->create()) {
2455 qWarning(
"Failed to build stencil clip index buffer");
2456 delete batch->stencilClipState.ibuf;
2457 batch->stencilClipState.ibuf =
nullptr;
2463 bool rebuildUBuf =
false;
2464 if (!batch->stencilClipState.ubuf) {
2467 }
else if (batch->stencilClipState.ubuf->size() < totalUSize) {
2468 batch->stencilClipState.ubuf->
setSize(totalUSize);
2472 if (!batch->stencilClipState.ubuf->create()) {
2473 qWarning(
"Failed to build stencil clip uniform buffer");
2474 delete batch->stencilClipState.ubuf;
2475 batch->stencilClipState.ubuf =
nullptr;
2480 if (!batch->stencilClipState.srb) {
2484 batch->stencilClipState.srb->setBindings({ ubufBinding });
2485 if (!batch->stencilClipState.srb->create()) {
2486 qWarning(
"Failed to build stencil clip srb");
2487 delete batch->stencilClipState.srb;
2488 batch->stencilClipState.srb =
nullptr;
2496 for (
const QSGClipNode *clip : stencilClipNodes) {
2499 StencilClipState::StencilDrawCall drawCall;
2500 const bool firstStencilClipInBatch = batch->stencilClipState.drawCalls.isEmpty();
2502 if (firstStencilClipInBatch) {
2505 m_stencilClipCommon.topology =
qsg_topology(
g->drawingMode());
2509 if (
qsg_topology(
g->drawingMode()) != m_stencilClipCommon.topology)
2510 qWarning(
"updateClipState: Clip list entries have different primitive topologies, this is not currently supported.");
2512 qWarning(
"updateClipState: Clip list entries have different vertex input layouts, this is must not happen.");
2516 drawCall.vbufOffset =
aligned(vOffset, 4u);
2517 const int vertexByteSize =
g->sizeOfVertex() *
g->vertexCount();
2518 vOffset = drawCall.vbufOffset + vertexByteSize;
2520 int indexByteSize = 0;
2521 if (
g->indexCount()) {
2522 drawCall.ibufOffset =
aligned(iOffset, 4u);
2523 indexByteSize =
g->sizeOfIndex() *
g->indexCount();
2524 iOffset = drawCall.ibufOffset + indexByteSize;
2527 drawCall.ubufOffset =
aligned(uOffset, m_ubufAlignment);
2528 uOffset = drawCall.ubufOffset + StencilClipUbufSize;
2532 matrixYUpNDC *= *clip->
matrix();
2534 m_resourceUpdates->
updateDynamicBuffer(batch->stencilClipState.ubuf, drawCall.ubufOffset, 64, matrixYUpNDC.constData());
2535 m_resourceUpdates->
updateDynamicBuffer(batch->stencilClipState.vbuf, drawCall.vbufOffset, vertexByteSize,
g->vertexData());
2537 m_resourceUpdates->
updateDynamicBuffer(batch->stencilClipState.ibuf, drawCall.ibufOffset, indexByteSize,
g->indexData());
2543 drawCall.stencilRef = firstStencilClipInBatch ? m_currentClipState.
stencilRef + 1 : m_currentClipState.
stencilRef;
2546 drawCall.vertexCount =
g->vertexCount();
2547 drawCall.indexCount =
g->indexCount();
2549 batch->stencilClipState.drawCalls.add(drawCall);
2552 if (!m_stencilClipCommon.vs.isValid())
2555 if (!m_stencilClipCommon.fs.isValid())
2558 if (!m_stencilClipCommon.replacePs)
2559 m_stencilClipCommon.replacePs = buildStencilPipeline(batch,
true);
2561 if (!m_stencilClipCommon.incrPs)
2562 m_stencilClipCommon.incrPs = buildStencilPipeline(batch,
false);
2564 batch->stencilClipState.updateStencilBuffer =
true;
2567 m_currentClipState.
clipList = clipList;
2568 m_currentClipState.
type = clipType;
2572 applyClipStateToGraphicsState();
2573 batch->clipState = m_currentClipState;
2576void Renderer::enqueueStencilDraw(
const Batch *batch)
2582 if (!batch->stencilClipState.updateStencilBuffer)
2586 const int count = batch->stencilClipState.drawCalls.size();
2588 const StencilClipState::StencilDrawCall &drawCall(batch->stencilClipState.drawCalls.at(
i));
2592 cb->setGraphicsPipeline(m_stencilClipCommon.replacePs);
2594 }
else if (
i == 1) {
2595 cb->setGraphicsPipeline(m_stencilClipCommon.incrPs);
2599 cb->setShaderResources(srb, 1, &ubufOffset);
2600 cb->setStencilRef(drawCall.stencilRef);
2602 if (drawCall.indexCount) {
2603 cb->setVertexInput(0, 1, &vbufBinding,
2604 batch->stencilClipState.ibuf, drawCall.ibufOffset, drawCall.indexFormat);
2605 cb->drawIndexed(drawCall.indexCount);
2607 cb->setVertexInput(0, 1, &vbufBinding);
2608 cb->draw(drawCall.vertexCount);
2617 m_currentShader =
shader;
2618 m_currentMaterial =
nullptr;
2676 e->depthPostPassPs = *
it;
2689 QRhiGraphicsPipeline::Flags
flags;
2737 qWarning(
"Failed to build graphics pipeline state");
2744 e->depthPostPassPs = ps;
2758 switch (desc.filtering) {
2773 switch (desc.mipmapFiltering) {
2789 switch (desc.horizontalWrap) {
2805 switch (desc.verticalWrap) {
2821 return rhi->
newSampler(magFilter, minFilter, mipmapMode, u,
v);
2826 if (!m_dummyTexture) {
2828 if (m_dummyTexture->
create()) {
2829 if (m_resourceUpdates) {
2836 return m_dummyTexture;
2842 dst->blendEnable =
src->blending;
2859 dst->separateBlendFactors =
false;
2867 dst->colorWrite = QSGMaterialShader::GraphicsPipelineState::ColorMask(
int(
src->colorWrite));
2876 dst->blending =
src->blendEnable;
2879 if (
src->separateBlendFactors) {
2883 dst->srcAlpha =
dst->srcColor;
2884 dst->dstAlpha =
dst->dstColor;
2888 dst->colorWrite = QRhiGraphicsPipeline::ColorMask(
int(
src->colorWrite));
2905 QVarLengthArray<QRhiShaderResourceBinding, 8> bindings;
2909 const bool changed =
shader->updateUniformData(renderState, material, m_currentMaterial);
2912 if (changed || !batch->ubufDataValid)
2928 QVarLengthArray<QSGTexture *, 4> nextTex = prevTex;
2931 nextTex.resize(
count);
2933 shader->updateSampledImage(renderState, binding, nextTex.data(), material,
2936 if (nextTex.contains(
nullptr)) {
2937 qWarning(
"No QSGTexture provided from updateSampledImage(). This is wrong.");
2941 bool hasDirtySamplerOptions =
false;
2942 bool isAnisotropic =
false;
2951 if (nextTex != prevTex || hasDirtySamplerOptions) {
2959 qWarning(
"QSGTexture anisotropy levels are not currently supported");
2961 QVarLengthArray<QRhiSampler *, 4>
samplers;
2971 qWarning(
"Failed to build sampler");
2975 m_samplers[samplerDesc] =
sampler;
2985 QVarLengthArray<QRhiShaderResourceBinding::TextureAndSampler, 4> textureSamplers;
2999 textureSamplers.append(
3003 if (!textureSamplers.isEmpty())
3005 binding,
stages,
count, textureSamplers.constData()));
3010 if (bindings.isEmpty())
3011 qWarning(
"No shader resources for material %p, this is odd.", material);
3014 enum class SrbAction {
3019 } srbAction = SrbAction::Unknown;
3028 e->srb = m_shaderManager->
srbPool.take(layoutDesc);
3033 srbAction = SrbAction::UpdateResources;
3041 if (srbAction == SrbAction::Unknown && e->srb) {
3042 if (std::equal(e->srb->cbeginBindings(), e->srb->cendBindings(), bindings.cbegin(), bindings.cend())) {
3043 srbAction = SrbAction::DoNothing;
3044 }
else if (std::equal(e->srb->cbeginBindings(), e->srb->cendBindings(), bindings.cbegin(), bindings.cend(),
3045 [](
const auto &
a,
const auto &
b) { return a.isLayoutCompatible(b); }))
3047 srbAction = SrbAction::UpdateResources;
3049 srbAction = SrbAction::Rebake;
3057 srbAction = SrbAction::Rebake;
3060 Q_ASSERT(srbAction != SrbAction::Unknown && e->srb);
3062 switch (srbAction) {
3063 case SrbAction::DoNothing:
3065 case SrbAction::UpdateResources:
3067 e->srb->setBindings(bindings.cbegin(), bindings.cend());
3068 QRhiShaderResourceBindings::UpdateFlags
flags;
3077 e->srb->updateResources(
flags);
3080 case SrbAction::Rebake:
3081 e->srb->setBindings(bindings.cbegin(), bindings.cend());
3086 Q_ASSERT_X(
false,
"updateMaterialDynamicData",
"No srb action set, this cannot happen");
3094 bool *gstateChanged)
3097 *gstateChanged =
false;
3104 const bool changed =
shader->updateGraphicsPipelineState(renderState, &shaderPs, material, m_currentMaterial);
3106 m_gstateStack.push(m_gstate);
3111 batch->blendConstant = shaderPs.blendConstant;
3113 *gstateChanged =
true;
3118bool Renderer::prepareRenderMergedBatch(Batch *batch, PreparedRenderBatch *renderBatch)
3120 if (batch->vertexCount == 0 || batch->indexCount == 0)
3126#ifndef QT_NO_DEBUG_OUTPUT
3131 << (batch->uploadedThisFrame ?
"[ upload]" :
"[retained]")
3132 << (e->node->clipList() ?
"[ clip]" :
"[noclip]")
3133 << (batch->isOpaque ?
"[opaque]" :
"[ alpha]")
3136 <<
" Vertices:" <<
QString::fromLatin1(
"%1").
arg(batch->vertexCount, 5).toLatin1().constData()
3137 <<
" Indices:" <<
QString::fromLatin1(
"%1").
arg(batch->indexCount, 5).toLatin1().constData()
3138 <<
" root:" << batch->root;
3139 if (batch->drawSets.size() > 1)
3140 debug <<
"sets:" << batch->drawSets.size();
3141 if (!batch->isOpaque)
3142 debug <<
"opacity:" << e->node->inheritedOpacity();
3143 batch->uploadedThisFrame =
false;
3159 for (
int viewIndex = 0; viewIndex < viewCount; ++viewIndex)
3168 updateClipState(gn->clipList(), batch);
3178 if (m_currentShader != sms)
3179 setActiveRhiShader(sms->materialShader, sms);
3190 bool ubufRebuild =
false;
3195 if (batch->ubuf->size() < ubufSize) {
3196 batch->ubuf->
setSize(ubufSize);
3201 batch->ubufDataValid =
false;
3202 if (!batch->ubuf->create()) {
3203 qWarning(
"Failed to build uniform buffer of size %u bytes", ubufSize);
3205 batch->ubuf =
nullptr;
3213 bool pendingGStatePop =
false;
3214 updateMaterialStaticData(sms, renderState, material, batch, &pendingGStatePop);
3216 updateMaterialDynamicData(sms, renderState, material, batch, e, 0, ubufSize);
3220 qDebug(
"QSGMaterial::updateState triggered an error (merged), batch will be skipped:");
3223 qDebug() <<
" -" << ee->node;
3224 ee = ee->nextInBatch;
3227 qFatal(
"Aborting: scene graph is invalid...");
3234 const bool hasPipeline = ensurePipelineState(e, sms);
3236 if (pendingGStatePop)
3237 m_gstate = m_gstateStack.pop();
3243 m_gstateStack.push(m_gstate);
3244 setStateForDepthPostPass();
3245 ensurePipelineState(e, sms,
true);
3246 m_gstate = m_gstateStack.pop();
3249 batch->ubufDataValid =
true;
3251 m_currentMaterial = material;
3253 renderBatch->batch = batch;
3254 renderBatch->sms = sms;
3264 if (
g->lineWidth() != 1.0f) {
3265 static bool checkedWideLineSupport =
false;
3266 if (!checkedWideLineSupport) {
3267 checkedWideLineSupport =
true;
3269 qWarning(
"Line widths other than 1 are not supported by the graphics API");
3273 if (
g->lineWidth() != 1.0f) {
3274 static bool warnedPointSize =
false;
3275 if (!warnedPointSize) {
3276 warnedPointSize =
true;
3277 qWarning(
"Point size is not controllable by QSGGeometry. "
3278 "Set gl_PointSize from the vertex shader instead.");
3284void Renderer::renderMergedBatch(PreparedRenderBatch *renderBatch,
bool depthPostPass)
3286 const Batch *batch = renderBatch->batch;
3287 if (!batch->vbo.buf || !batch->ibo.buf)
3296 enqueueStencilDraw(batch);
3299 setGraphicsPipeline(
cb, batch, e, depthPostPass);
3301 for (
int i = 0, ie = batch->drawSets.size();
i != ie; ++
i) {
3302 const DrawSet &
draw = batch->drawSets.at(
i);
3310 cb->drawIndexed(
draw.indexCount);
3314bool Renderer::prepareRenderUnmergedBatch(Batch *batch, PreparedRenderBatch *renderBatch)
3316 if (batch->vertexCount == 0)
3325 << (batch->uploadedThisFrame ?
"[ upload]" :
"[retained]")
3326 << (e->node->clipList() ?
"[ clip]" :
"[noclip]")
3327 << (batch->isOpaque ?
"[opaque]" :
"[ alpha]")
3330 <<
" Vertices:" <<
QString::fromLatin1(
"%1").
arg(batch->vertexCount, 5).toLatin1().constData()
3331 <<
" Indices:" <<
QString::fromLatin1(
"%1").
arg(batch->indexCount, 5).toLatin1().constData()
3332 <<
" root:" << batch->root;
3334 batch->uploadedThisFrame =
false;
3339 for (
int viewIndex = 0; viewIndex < viewCount; ++viewIndex)
3348 updateClipState(gn->clipList(), batch);
3362 if (m_currentShader != sms)
3363 setActiveRhiShader(sms->materialShader, sms);
3378 totalUBufSize +=
aligned(ubufSize, m_ubufAlignment);
3381 bool ubufRebuild =
false;
3386 if (batch->ubuf->size() < totalUBufSize) {
3387 batch->ubuf->
setSize(totalUBufSize);
3392 batch->ubufDataValid =
false;
3393 if (!batch->ubuf->create()) {
3394 qWarning(
"Failed to build uniform buffer of size %u bytes", totalUBufSize);
3396 batch->ubuf =
nullptr;
3403 bool pendingGStatePop =
false;
3404 updateMaterialStaticData(sms, renderState,
3405 material, batch, &pendingGStatePop);
3419 for (
int viewIndex = 0; viewIndex < viewCount; ++viewIndex)
3426 if (useDepthBuffer()) {
3433 updateMaterialDynamicData(sms, renderState, material, batch, e, ubufOffset, ubufSize);
3437 qDebug(
"QSGMaterial::updateState() triggered an error (unmerged), batch will be skipped:");
3438 qDebug() <<
" - offending node is" << e->node;
3440 qFatal(
"Aborting: scene graph is invalid...");
3445 ubufOffset +=
aligned(ubufSize, m_ubufAlignment);
3448 const float prevLineWidth = m_gstate.
lineWidth;
3455 if (!ps || m_gstate.
drawMode != prevDrawMode || m_gstate.
lineWidth != prevLineWidth || pendingGStatePop) {
3456 if (!ensurePipelineState(e, sms)) {
3457 if (pendingGStatePop)
3458 m_gstate = m_gstateStack.pop();
3463 m_gstateStack.push(m_gstate);
3464 setStateForDepthPostPass();
3465 ensurePipelineState(e, sms,
true);
3466 m_gstate = m_gstateStack.pop();
3467 depthPostPassPs = e->depthPostPassPs;
3472 e->depthPostPassPs = depthPostPassPs;
3477 m_currentMaterial = material;
3485 if (pendingGStatePop)
3486 m_gstate = m_gstateStack.pop();
3488 batch->ubufDataValid =
true;
3490 renderBatch->batch = batch;
3491 renderBatch->sms = sms;
3496void Renderer::renderUnmergedBatch(PreparedRenderBatch *renderBatch,
bool depthPostPass)
3498 const Batch *batch = renderBatch->batch;
3499 if (!batch->vbo.buf)
3505 enqueueStencilDraw(batch);
3514 const int effectiveIndexSize = m_uint32IndexForRhi ?
sizeof(
quint32) :
g->sizeOfIndex();
3516 setGraphicsPipeline(
cb, batch, e, depthPostPass);
3519 if (
g->indexCount()) {
3520 if (batch->ibo.buf) {
3522 batch->ibo.buf, iOffset,
3525 cb->drawIndexed(
g->indexCount());
3529 cb->draw(
g->vertexCount());
3532 vOffset +=
g->sizeOfVertex() *
g->vertexCount();
3533 iOffset +=
g->indexCount() * effectiveIndexSize;
3541 cb->setGraphicsPipeline(depthPostPass ? e->depthPostPassPs : e->ps);
3550 cb->setScissor(batch->clipState.scissor);
3564 cb->setStencilRef(batch->clipState.stencilRef);
3567 cb->setBlendConstants(batch->blendConstant);
3569 cb->setShaderResources(e->srb);
3572void Renderer::releaseElement(
Element *e,
bool inDestructor)
3574 if (e->isRenderNode) {
3575 delete static_cast<RenderNodeElement *
>(e);
3578 if (!inDestructor) {
3579 if (m_shaderManager->
srbPool.size() < m_srbPoolThreshold)
3580 m_shaderManager->
srbPool.insert(e->srb->serializedLayoutDescription(), e->srb);
3588 m_elementAllocator.release(e);
3592void Renderer::deleteRemovedElements()
3594 if (!m_elementsToDelete.size())
3597 for (
int i=0;
i<m_opaqueRenderList.size(); ++
i) {
3598 Element **e = m_opaqueRenderList.data() +
i;
3599 if (*e && (*e)->removed)
3602 for (
int i=0;
i<m_alphaRenderList.size(); ++
i) {
3603 Element **e = m_alphaRenderList.data() +
i;
3604 if (*e && (*e)->removed)
3608 for (
int i=0;
i<m_elementsToDelete.size(); ++
i)
3609 releaseElement(m_elementsToDelete.at(
i));
3611 m_elementsToDelete.reset();
3646 qWarning(
"prepareRenderPass() called with an already prepared render pass context");
3655 ctx->timeRenderLists = 0;
3656 ctx->timePrepareOpaque = 0;
3657 ctx->timePrepareAlpha = 0;
3658 ctx->timeSorting = 0;
3659 ctx->timeUploadOpaque = 0;
3660 ctx->timeUploadAlpha = 0;
3662 if (
Q_UNLIKELY(debug_render() || debug_build())) {
3666 if (m_rebuild == FullRebuild)
3669 if (m_rebuild & BuildRenderLists)
3670 type +=
" renderlists";
3671 else if (m_rebuild & BuildRenderListsForTaggedRoots)
3673 else if (m_rebuild & BuildBatches)
3677 qDebug() <<
"Renderer::render()" <<
this <<
type;
3683 if (m_rebuild & (BuildRenderLists | BuildRenderListsForTaggedRoots)) {
3684 bool complete = (m_rebuild & BuildRenderLists) != 0;
3686 buildRenderListsFromScratch();
3688 buildRenderListsForTaggedRoots();
3689 m_rebuild |= BuildBatches;
3692 qDebug(
"Opaque render lists %s:", (complete ?
"(complete)" :
"(partial)"));
3693 for (
int i=0;
i<m_opaqueRenderList.size(); ++
i) {
3694 Element *e = m_opaqueRenderList.at(
i);
3695 qDebug() <<
" - element:" << e <<
" batch:" << e->
batch <<
" node:" << e->
node <<
" order:" << e->
order;
3697 qDebug(
"Alpha render list %s:", complete ?
"(complete)" :
"(partial)");
3698 for (
int i=0;
i<m_alphaRenderList.size(); ++
i) {
3699 Element *e = m_alphaRenderList.at(
i);
3700 qDebug() <<
" - element:" << e <<
" batch:" << e->
batch <<
" node:" << e->
node <<
" order:" << e->
order;
3704 if (
Q_UNLIKELY(debug_render()))
ctx->timeRenderLists =
ctx->timer.restart();
3706 for (
int i=0;
i<m_opaqueBatches.size(); ++
i)
3707 m_opaqueBatches.at(
i)->cleanupRemovedElements();
3708 for (
int i=0;
i<m_alphaBatches.size(); ++
i)
3709 m_alphaBatches.at(
i)->cleanupRemovedElements();
3710 deleteRemovedElements();
3712 cleanupBatches(&m_opaqueBatches);
3713 cleanupBatches(&m_alphaBatches);
3715 if (m_rebuild & BuildBatches) {
3716 prepareOpaqueBatches();
3717 if (
Q_UNLIKELY(debug_render()))
ctx->timePrepareOpaque =
ctx->timer.restart();
3718 prepareAlphaBatches();
3719 if (
Q_UNLIKELY(debug_render()))
ctx->timePrepareAlpha =
ctx->timer.restart();
3722 qDebug(
"Opaque Batches:");
3723 for (
int i=0;
i<m_opaqueBatches.size(); ++
i) {
3724 Batch *
b = m_opaqueBatches.at(
i);
3725 qDebug() <<
" - Batch " <<
i <<
b << (
b->needsUpload ?
"upload" :
"") <<
" root:" <<
b->root;
3726 for (
Element *e =
b->first; e; e = e->nextInBatch) {
3727 qDebug() <<
" - element:" << e <<
" node:" << e->node << e->order;
3730 qDebug(
"Alpha Batches:");
3731 for (
int i=0;
i<m_alphaBatches.size(); ++
i) {
3732 Batch *
b = m_alphaBatches.at(
i);
3733 qDebug() <<
" - Batch " <<
i <<
b << (
b->needsUpload ?
"upload" :
"") <<
" root:" <<
b->root;
3734 for (
Element *e =
b->first; e; e = e->nextInBatch) {
3735 qDebug() <<
" - element:" << e << e->bounds <<
" node:" << e->node <<
" order:" << e->order;
3740 if (
Q_UNLIKELY(debug_render()))
ctx->timePrepareOpaque =
ctx->timePrepareAlpha =
ctx->timer.restart();
3744 deleteRemovedElements();
3746 if (m_rebuild != 0) {
3749 if (m_opaqueBatches.size())
3753 if (m_alphaBatches.size())
3756 m_zRange = m_nextRenderOrder != 0
3757 ? 1.0 / (m_nextRenderOrder)
3765 m_vertexUploadPool.
reset();
3766 m_indexUploadPool.
reset();
3769 for (
int i=0;
i<m_opaqueBatches.size(); ++
i) {
3770 Batch *
b = m_opaqueBatches.at(
i);
3773 if (
Q_UNLIKELY(debug_render()))
ctx->timeUploadOpaque =
ctx->timer.restart();
3776 for (
int i=0;
i<m_alphaBatches.size(); ++
i) {
3777 Batch *
b = m_alphaBatches.at(
i);
3780 if (
Q_UNLIKELY(debug_render()))
ctx->timeUploadAlpha =
ctx->timer.restart();
3785 <<
" -> Alpha: " <<
qsg_countNodesInBatches(m_alphaBatches) <<
" nodes in " << m_alphaBatches.size() <<
" batches...";
3789 m_currentMaterial =
nullptr;
3790 m_currentShader =
nullptr;
3791 m_currentProgram =
nullptr;
3792 m_currentClipState.
reset();
3796 bool renderOpaque = !debug_noopaque();
3797 bool renderAlpha = !debug_noalpha();
3824 ctx->opaqueRenderBatches.clear();
3826 for (
int i = 0, ie = m_opaqueBatches.size();
i != ie; ++
i) {
3827 Batch *
b = m_opaqueBatches.at(
i);
3831 ok = prepareRenderMergedBatch(
b, &renderBatch);
3833 ok = prepareRenderUnmergedBatch(
b, &renderBatch);
3835 ctx->opaqueRenderBatches.append(renderBatch);
3850 Q_ASSERT(m_opaqueBatches.isEmpty());
3854 ctx->alphaRenderBatches.clear();
3856 for (
int i = 0, ie = m_alphaBatches.size();
i != ie; ++
i) {
3857 Batch *
b = m_alphaBatches.at(
i);
3861 ok = prepareRenderMergedBatch(
b, &renderBatch);
3862 else if (
b->isRenderNode)
3863 ok = prepareRhiRenderNode(
b, &renderBatch);
3865 ok = prepareRenderUnmergedBatch(
b, &renderBatch);
3867 ctx->alphaRenderBatches.append(renderBatch);
3873#if defined(QSGBATCHRENDERER_INVALIDATE_WEDGED_NODES)
3874 m_renderOrderRebuildLower = -1;
3875 m_renderOrderRebuildUpper = -1;
3882 m_resourceUpdates =
nullptr;
3912 qWarning(
"recordRenderPass() called without a prepared render pass context");
3919 for (
int i = 0, ie =
ctx->opaqueRenderBatches.size();
i != ie; ++
i) {
3923 if (renderBatch->batch->merged)
3924 renderMergedBatch(renderBatch);
3926 renderUnmergedBatch(renderBatch);
3929 for (
int i = 0, ie =
ctx->alphaRenderBatches.size();
i != ie; ++
i) {
3937 if (renderBatch->batch->merged)
3938 renderMergedBatch(renderBatch);
3939 else if (renderBatch->batch->isRenderNode)
3940 renderRhiRenderNode(renderBatch->batch);
3942 renderUnmergedBatch(renderBatch);
3952 for (
int i = 0, ie =
ctx->alphaRenderBatches.size();
i != ie; ++
i) {
3956 if (renderBatch->batch->merged)
3957 renderMergedBatch(renderBatch,
true);
3958 else if (!renderBatch->batch->isRenderNode)
3959 renderUnmergedBatch(renderBatch,
true);
3963 if (m_currentShader)
3964 setActiveRhiShader(
nullptr,
nullptr);
3969 qDebug(
" -> times: build: %d, prepare(opaque/alpha): %d/%d, sorting: %d, upload(opaque/alpha): %d/%d, record rendering: %d",
3970 (
int)
ctx->timeRenderLists,
3971 (
int)
ctx->timePrepareOpaque, (
int)
ctx->timePrepareAlpha,
3972 (
int)
ctx->timeSorting,
3973 (
int)
ctx->timeUploadOpaque, (
int)
ctx->timeUploadAlpha,
3974 (
int)
ctx->timer.elapsed());
4005bool Renderer::prepareRhiRenderNode(
Batch *batch, PreparedRenderBatch *renderBatch)
4008 qDebug() <<
" -" << batch <<
"rendernode";
4013 setActiveRhiShader(
nullptr,
nullptr);
4016 rd->m_clip_list =
nullptr;
4026 updateClipState(
rd->m_clip_list, batch);
4036 while (
xform != root) {
4044 rd->m_matrix = &
rd->m_localMatrix;
4047 rd->m_opacity = 1.0;
4053 opacity = opacity->
parent();
4059 rd->m_projectionMatrix.resize(viewCount);
4060 for (
int viewIndex = 0; viewIndex < viewCount; ++viewIndex)
4063 if (useDepthBuffer()) {
4065 rd->m_projectionMatrix[0](2, 2) = m_zRange;
4071 renderBatch->batch = batch;
4072 renderBatch->sms =
nullptr;
4077void Renderer::renderRhiRenderNode(
const Batch *batch)
4080 enqueueStencilDraw(batch);
4082 RenderNodeElement *e =
static_cast<RenderNodeElement *
>(batch->first);
4088 state.m_projectionMatrix = &
rd->m_projectionMatrix[0];
4089 const std::array<int, 4> scissor = batch->clipState.scissor.scissor();
4090 state.m_scissorRect =
QRect(scissor[0], scissor[1], scissor[2], scissor[3]);
4091 state.m_stencilValue = batch->clipState.stencilRef;
4095 const QSGRenderNode::StateFlags changes = e->renderNode->changedStates();
4100 cb->beginExternal();
4101 e->renderNode->render(&
state);
4105 rd->m_matrix =
nullptr;
4106 rd->m_clip_list =
nullptr;
4127 else if (
mode ==
"clip")
4129 else if (
mode ==
"overdraw")
4131 else if (
mode ==
"batches")
4133 else if (
mode ==
"changes")
4144 return a.depthTest ==
b.depthTest
4145 &&
a.depthWrite ==
b.depthWrite
4146 &&
a.depthFunc ==
b.depthFunc
4147 &&
a.blending ==
b.blending
4148 &&
a.srcColor ==
b.srcColor
4149 &&
a.dstColor ==
b.dstColor
4150 &&
a.srcAlpha ==
b.srcAlpha
4151 &&
a.dstAlpha ==
b.dstAlpha
4152 &&
a.opColor ==
b.opColor
4153 &&
a.opAlpha ==
b.opAlpha
4154 &&
a.colorWrite ==
b.colorWrite
4155 &&
a.cullMode ==
b.cullMode
4156 &&
a.usesScissor ==
b.usesScissor
4157 &&
a.stencilTest ==
b.stencilTest
4158 &&
a.sampleCount ==
b.sampleCount
4159 &&
a.drawMode ==
b.drawMode
4160 &&
a.lineWidth ==
b.lineWidth
4161 &&
a.polygonMode ==
b.polygonMode
4162 &&
a.multiViewCount ==
b.multiViewCount;
4174 +
s.depthTest * 1000
4175 +
s.depthWrite * 100
4188 return a.state ==
b.state
4189 &&
a.sms->materialShader ==
b.sms->materialShader
4190 &&
a.renderTargetDescription ==
b.renderTargetDescription
4191 &&
a.srbLayoutDescription ==
b.srbLayoutDescription;
4202 ^
qHash(k.sms->materialShader)
4203 ^ k.extra.renderTargetDescriptionHash
4204 ^ k.extra.srbLayoutDescriptionHash;
4209 return a.type ==
b.type
4210 &&
a.renderMode ==
b.renderMode
4211 &&
a.multiViewCount ==
b.multiViewCount;
4221 return qHash(k.type,
seed) ^ int(k.renderMode) ^ k.multiViewCount;
4226 m_visualizeMode(VisualizeNothing)
4234#define QSGNODE_DIRTY_PARENT (QSGNode::DirtyNodeAdded \
4235 | QSGNode::DirtyOpacity \
4236 | QSGNode::DirtyMatrix \
4237 | QSGNode::DirtyNodeRemoved)
4242 uint selfDirty =
n->dirtyState | parentChanges;