aboutsummaryrefslogtreecommitdiffstats
path: root/src/quick/scenegraph/coreapi/qsgbatchrenderer.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/quick/scenegraph/coreapi/qsgbatchrenderer.cpp')
-rw-r--r--src/quick/scenegraph/coreapi/qsgbatchrenderer.cpp924
1 files changed, 582 insertions, 342 deletions
diff --git a/src/quick/scenegraph/coreapi/qsgbatchrenderer.cpp b/src/quick/scenegraph/coreapi/qsgbatchrenderer.cpp
index cca46986e3..5746d984bf 100644
--- a/src/quick/scenegraph/coreapi/qsgbatchrenderer.cpp
+++ b/src/quick/scenegraph/coreapi/qsgbatchrenderer.cpp
@@ -1,43 +1,7 @@
-/****************************************************************************
-**
-** Copyright (C) 2019 The Qt Company Ltd.
-** Copyright (C) 2016 Jolla Ltd, author: <gunnar.sletta@jollamobile.com>
-** Copyright (C) 2016 Robin Burchell <robin.burchell@viroteck.net>
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtQuick module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2019 The Qt Company Ltd.
+// Copyright (C) 2016 Jolla Ltd, author: <gunnar.sletta@jollamobile.com>
+// Copyright (C) 2016 Robin Burchell <robin.burchell@viroteck.net>
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#include "qsgbatchrenderer_p.h"
@@ -58,7 +22,7 @@
QT_BEGIN_NAMESPACE
#ifndef QT_NO_DEBUG
-Q_QUICK_PRIVATE_EXPORT bool qsg_test_and_clear_material_failure();
+Q_QUICK_EXPORT bool qsg_test_and_clear_material_failure();
#endif
int qt_sg_envInt(const char *name, int defaultValue);
@@ -120,7 +84,11 @@ const uint DYNAMIC_VERTEX_INDEX_BUFFER_THRESHOLD = 4;
const int VERTEX_BUFFER_BINDING = 0;
const int ZORDER_BUFFER_BINDING = VERTEX_BUFFER_BINDING + 1;
-static inline uint aligned(uint v, uint byteAlign)
+const float VIEWPORT_MIN_DEPTH = 0.0f;
+const float VIEWPORT_MAX_DEPTH = 1.0f;
+
+template <class Int>
+inline Int aligned(Int v, Int byteAlign)
{
return (v + byteAlign - 1) & ~(byteAlign - 1);
}
@@ -150,8 +118,7 @@ QRhiVertexInputAttribute::Format qsg_vertexInputFormat(const QSGGeometry::Attrib
break;
}
qWarning("Unsupported attribute type 0x%x with %d components", a.type, a.tupleSize);
- Q_UNREACHABLE();
- return QRhiVertexInputAttribute::Float;
+ Q_UNREACHABLE_RETURN(QRhiVertexInputAttribute::Float);
}
static QRhiVertexInputLayout calculateVertexInputLayout(const QSGMaterialShader *s, const QSGGeometry *geometry, bool batchable)
@@ -166,7 +133,7 @@ static QRhiVertexInputLayout calculateVertexInputLayout(const QSGMaterialShader
const int attrCount = geometry->attributeCount();
QVarLengthArray<QRhiVertexInputAttribute, 8> inputAttributes;
inputAttributes.reserve(attrCount + 1);
- int offset = 0;
+ quint32 offset = 0;
for (int i = 0; i < attrCount; ++i) {
const QSGGeometry::Attribute &a = geometry->attributes()[i];
if (!sd->vertexShader->vertexInputLocations.contains(a.position)) {
@@ -204,8 +171,7 @@ QRhiCommandBuffer::IndexFormat qsg_indexFormat(const QSGGeometry *geometry)
return QRhiCommandBuffer::IndexUInt32;
break;
default:
- Q_UNREACHABLE();
- return QRhiCommandBuffer::IndexUInt16;
+ Q_UNREACHABLE_RETURN(QRhiCommandBuffer::IndexUInt16);
}
}
@@ -235,13 +201,22 @@ QRhiGraphicsPipeline::Topology qsg_topology(int geomDrawMode)
return topology;
}
+void qsg_setMultiViewFlagsOnMaterial(QSGMaterial *material, int multiViewCount)
+{
+ material->setFlag(QSGMaterial::MultiView2, multiViewCount == 2);
+ material->setFlag(QSGMaterial::MultiView3, multiViewCount == 3);
+ material->setFlag(QSGMaterial::MultiView4, multiViewCount == 4);
+}
+
ShaderManager::Shader *ShaderManager::prepareMaterial(QSGMaterial *material,
const QSGGeometry *geometry,
- QSGRendererInterface::RenderMode renderMode)
+ QSGRendererInterface::RenderMode renderMode,
+ int multiViewCount)
{
- QSGMaterialType *type = material->type();
+ qsg_setMultiViewFlagsOnMaterial(material, multiViewCount);
- ShaderKey key = qMakePair(type, renderMode);
+ QSGMaterialType *type = material->type();
+ ShaderKey key = { type, renderMode, multiViewCount };
Shader *shader = rewrittenShaders.value(key, nullptr);
if (shader)
return shader;
@@ -249,12 +224,12 @@ ShaderManager::Shader *ShaderManager::prepareMaterial(QSGMaterial *material,
shader = new Shader;
QSGMaterialShader *s = static_cast<QSGMaterialShader *>(material->createShader(renderMode));
context->initializeRhiShader(s, QShader::BatchableVertexShader);
- shader->programRhi.program = s;
- shader->programRhi.inputLayout = calculateVertexInputLayout(s, geometry, true);
+ shader->materialShader = s;
+ shader->inputLayout = calculateVertexInputLayout(s, geometry, true);
QSGMaterialShaderPrivate *sD = QSGMaterialShaderPrivate::get(s);
- shader->programRhi.shaderStages = {
- { QRhiGraphicsShaderStage::Vertex, sD->shader(QShader::VertexStage), QShader::BatchableVertexShader },
- { QRhiGraphicsShaderStage::Fragment, sD->shader(QShader::FragmentStage) }
+ shader->stages = {
+ { QRhiShaderStage::Vertex, sD->shader(QShader::VertexStage), QShader::BatchableVertexShader },
+ { QRhiShaderStage::Fragment, sD->shader(QShader::FragmentStage) }
};
shader->lastOpacity = 0;
@@ -265,11 +240,13 @@ ShaderManager::Shader *ShaderManager::prepareMaterial(QSGMaterial *material,
ShaderManager::Shader *ShaderManager::prepareMaterialNoRewrite(QSGMaterial *material,
const QSGGeometry *geometry,
- QSGRendererInterface::RenderMode renderMode)
+ QSGRendererInterface::RenderMode renderMode,
+ int multiViewCount)
{
- QSGMaterialType *type = material->type();
+ qsg_setMultiViewFlagsOnMaterial(material, multiViewCount);
- ShaderKey key = qMakePair(type, renderMode);
+ QSGMaterialType *type = material->type();
+ ShaderKey key = { type, renderMode, multiViewCount };
Shader *shader = stockShaders.value(key, nullptr);
if (shader)
return shader;
@@ -277,12 +254,12 @@ ShaderManager::Shader *ShaderManager::prepareMaterialNoRewrite(QSGMaterial *mate
shader = new Shader;
QSGMaterialShader *s = static_cast<QSGMaterialShader *>(material->createShader(renderMode));
context->initializeRhiShader(s, QShader::StandardShader);
- shader->programRhi.program = s;
- shader->programRhi.inputLayout = calculateVertexInputLayout(s, geometry, false);
+ shader->materialShader = s;
+ shader->inputLayout = calculateVertexInputLayout(s, geometry, false);
QSGMaterialShaderPrivate *sD = QSGMaterialShaderPrivate::get(s);
- shader->programRhi.shaderStages = {
- { QRhiGraphicsShaderStage::Vertex, sD->shader(QShader::VertexStage) },
- { QRhiGraphicsShaderStage::Fragment, sD->shader(QShader::FragmentStage) }
+ shader->stages = {
+ { QRhiShaderStage::Vertex, sD->shader(QShader::VertexStage) },
+ { QRhiShaderStage::Fragment, sD->shader(QShader::FragmentStage) }
};
shader->lastOpacity = 0;
@@ -299,24 +276,24 @@ void ShaderManager::invalidated()
qDeleteAll(rewrittenShaders);
rewrittenShaders.clear();
- qDeleteAll(srbCache);
- srbCache.clear();
-
qDeleteAll(pipelineCache);
pipelineCache.clear();
+
+ qDeleteAll(srbPool);
+ srbPool.clear();
}
void ShaderManager::clearCachedRendererData()
{
- for (ShaderManager::Shader *sms : stockShaders) {
- QSGMaterialShader *s = sms->programRhi.program;
+ for (ShaderManager::Shader *sms : std::as_const(stockShaders)) {
+ QSGMaterialShader *s = sms->materialShader;
if (s) {
QSGMaterialShaderPrivate *sd = QSGMaterialShaderPrivate::get(s);
sd->clearCachedRendererData();
}
}
- for (ShaderManager::Shader *sms : rewrittenShaders) {
- QSGMaterialShader *s = sms->programRhi.program;
+ for (ShaderManager::Shader *sms : std::as_const(rewrittenShaders)) {
+ QSGMaterialShader *s = sms->materialShader;
if (s) {
QSGMaterialShaderPrivate *sd = QSGMaterialShaderPrivate::get(s);
sd->clearCachedRendererData();
@@ -324,24 +301,6 @@ void ShaderManager::clearCachedRendererData()
}
}
-QRhiShaderResourceBindings *ShaderManager::srb(const ShaderResourceBindingList &bindings)
-{
- auto it = srbCache.constFind(bindings);
- if (it != srbCache.constEnd())
- return *it;
-
- QRhiShaderResourceBindings *srb = context->rhi()->newShaderResourceBindings();
- srb->setBindings(bindings.cbegin(), bindings.cend());
- if (srb->create()) {
- srbCache.insert(bindings, srb);
- } else {
- qWarning("Failed to build srb");
- delete srb;
- srb = nullptr;
- }
- return srb;
-}
-
void qsg_dumpShadowRoots(BatchRootInfo *i, int indent)
{
static int extraIndent = 0;
@@ -748,7 +707,7 @@ BatchCompatibility Batch::isMaterialCompatible(Element *e) const
QSGMaterial *m = e->node->activeMaterial();
QSGMaterial *nm = n->node->activeMaterial();
- return (nm->type() == m->type() && nm->compare(m) == 0)
+ return (nm->type() == m->type() && nm->viewCount() == m->viewCount() && nm->compare(m) == 0)
? BatchIsCompatible
: BatchBreaksOnCompare;
}
@@ -891,6 +850,8 @@ Renderer::Renderer(QSGDefaultRenderContext *ctx, QSGRendererInterface::RenderMod
, m_elementsToDelete(64)
, m_tmpAlphaElements(16)
, m_tmpOpaqueElements(16)
+ , m_vboPool(16)
+ , m_iboPool(16)
, m_rebuild(FullRebuild)
, m_zRange(0)
#if defined(QSGBATCHRENDERER_INVALIDATE_WEDGED_NODES)
@@ -918,7 +879,7 @@ Renderer::Renderer(QSGDefaultRenderContext *ctx, QSGRendererInterface::RenderMod
// The shader manager is shared between renderers (think for example Item
// layers that create a new Renderer each) with the same rendercontext (and
// so same QRhi).
- m_shaderManager = ctx->findChild<ShaderManager *>(QStringLiteral("__qt_ShaderManager"), Qt::FindDirectChildrenOnly);
+ m_shaderManager = ctx->findChild<ShaderManager *>(QString(), Qt::FindDirectChildrenOnly);
if (!m_shaderManager) {
m_shaderManager = new ShaderManager(ctx);
m_shaderManager->setObjectName(QStringLiteral("__qt_ShaderManager"));
@@ -928,17 +889,17 @@ Renderer::Renderer(QSGDefaultRenderContext *ctx, QSGRendererInterface::RenderMod
m_batchNodeThreshold = qt_sg_envInt("QSG_RENDERER_BATCH_NODE_THRESHOLD", 64);
m_batchVertexThreshold = qt_sg_envInt("QSG_RENDERER_BATCH_VERTEX_THRESHOLD", 1024);
+ m_srbPoolThreshold = qt_sg_envInt("QSG_RENDERER_SRB_POOL_THRESHOLD", 1024);
if (Q_UNLIKELY(debug_build() || debug_render())) {
- qDebug("Batch thresholds: nodes: %d vertices: %d",
- m_batchNodeThreshold, m_batchVertexThreshold);
+ qDebug("Batch thresholds: nodes: %d vertices: %d Srb pool threshold: %d",
+ m_batchNodeThreshold, m_batchVertexThreshold, m_srbPoolThreshold);
}
}
static void qsg_wipeBuffer(Buffer *buffer)
{
- if (buffer->buf)
- delete buffer->buf;
+ delete buffer->buf;
// The free here is ok because we're in one of two situations.
// 1. We're using the upload pool in which case unmap will have set the
@@ -948,11 +909,10 @@ static void qsg_wipeBuffer(Buffer *buffer)
free(buffer->data);
}
-static void qsg_wipeBatch(Batch *batch, bool separateIndexBuffer)
+static void qsg_wipeBatch(Batch *batch)
{
qsg_wipeBuffer(&batch->vbo);
- if (separateIndexBuffer)
- qsg_wipeBuffer(&batch->ibo);
+ qsg_wipeBuffer(&batch->ibo);
delete batch->ubuf;
batch->stencilClipState.reset();
delete batch;
@@ -961,32 +921,31 @@ static void qsg_wipeBatch(Batch *batch, bool separateIndexBuffer)
Renderer::~Renderer()
{
if (m_rhi) {
- // If setExternalRenderPassDescriptor() was called, we have to
- // aggressively invalidate to prevent an object, the lifetime of which
- // we have no control over, staying in the (per-window) caches.
- invalidatePipelineCacheDependency(m_external_rp_desc);
-
// Clean up batches and buffers
- const bool separateIndexBuffer = m_context->separateIndexBuffer();
for (int i = 0; i < m_opaqueBatches.size(); ++i)
- qsg_wipeBatch(m_opaqueBatches.at(i), separateIndexBuffer);
+ qsg_wipeBatch(m_opaqueBatches.at(i));
for (int i = 0; i < m_alphaBatches.size(); ++i)
- qsg_wipeBatch(m_alphaBatches.at(i), separateIndexBuffer);
+ qsg_wipeBatch(m_alphaBatches.at(i));
for (int i = 0; i < m_batchPool.size(); ++i)
- qsg_wipeBatch(m_batchPool.at(i), separateIndexBuffer);
+ qsg_wipeBatch(m_batchPool.at(i));
+ for (int i = 0; i < m_vboPool.size(); ++i)
+ delete m_vboPool.at(i);
+ for (int i = 0; i < m_iboPool.size(); ++i)
+ delete m_iboPool.at(i);
}
- for (Node *n : qAsConst(m_nodes))
+ for (Node *n : std::as_const(m_nodes)) {
+ if (n->type() == QSGNode::GeometryNodeType) {
+ Element *e = n->element();
+ if (!e->removed)
+ m_elementsToDelete.add(e);
+ }
m_nodeAllocator.release(n);
+ }
// Remaining elements...
- for (int i=0; i<m_elementsToDelete.size(); ++i) {
- Element *e = m_elementsToDelete.at(i);
- if (e->isRenderNode)
- delete static_cast<RenderNodeElement *>(e);
- else
- m_elementAllocator.release(e);
- }
+ for (int i=0; i<m_elementsToDelete.size(); ++i)
+ releaseElement(m_elementsToDelete.at(i), true);
destroyGraphicsResources();
@@ -1017,10 +976,29 @@ void Renderer::releaseCachedResources()
m_dummyTexture = nullptr;
m_rhi->releaseCachedResources();
+
+ m_vertexUploadPool.shrink(0);
+ m_vertexUploadPool.reset();
+ m_indexUploadPool.shrink(0);
+ m_indexUploadPool.reset();
+
+ for (int i = 0; i < m_vboPool.size(); ++i)
+ delete m_vboPool.at(i);
+ m_vboPool.reset();
+
+ for (int i = 0; i < m_iboPool.size(); ++i)
+ delete m_iboPool.at(i);
+ m_iboPool.reset();
}
void Renderer::invalidateAndRecycleBatch(Batch *b)
{
+ if (b->vbo.buf != nullptr)
+ m_vboPool.add(b->vbo.buf);
+ if (b->ibo.buf != nullptr)
+ m_iboPool.add(b->ibo.buf);
+ b->vbo.buf = nullptr;
+ b->ibo.buf = nullptr;
b->invalidate();
for (int i=0; i<m_batchPool.size(); ++i)
if (b == m_batchPool.at(i))
@@ -1028,14 +1006,13 @@ void Renderer::invalidateAndRecycleBatch(Batch *b)
m_batchPool.add(b);
}
-void Renderer::map(Buffer *buffer, int byteSize, bool isIndexBuf)
+void Renderer::map(Buffer *buffer, quint32 byteSize, bool isIndexBuf)
{
if (m_visualizer->mode() == Visualizer::VisualizeNothing) {
// Common case, use a shared memory pool for uploading vertex data to avoid
// excessive reevaluation
- QDataBuffer<char> &pool = m_context->separateIndexBuffer() && isIndexBuf
- ? m_indexUploadPool : m_vertexUploadPool;
- if (byteSize > pool.size())
+ QDataBuffer<char> &pool = isIndexBuf ? m_indexUploadPool : m_vertexUploadPool;
+ if (byteSize > quint32(pool.size()))
pool.resize(byteSize);
buffer->data = pool.data();
} else if (buffer->size != byteSize) {
@@ -1050,14 +1027,40 @@ void Renderer::unmap(Buffer *buffer, bool isIndexBuf)
{
// Batches are pooled and reused which means the QRhiBuffer will be
// still valid in a recycled Batch. We only hit the newBuffer() path
- // for brand new Batches.
- if (!buffer->buf) {
+ // when there are no buffers to recycle.
+ QDataBuffer<QRhiBuffer *> *bufferPool = isIndexBuf ? &m_iboPool : &m_vboPool;
+ if (!buffer->buf && bufferPool->isEmpty()) {
buffer->buf = m_rhi->newBuffer(QRhiBuffer::Immutable,
isIndexBuf ? QRhiBuffer::IndexBuffer : QRhiBuffer::VertexBuffer,
buffer->size);
- if (!buffer->buf->create())
- qWarning("Failed to build vertex/index buffer of size %d", buffer->size);
+ if (!buffer->buf->create()) {
+ qWarning("Failed to build vertex/index buffer of size %u", buffer->size);
+ delete buffer->buf;
+ buffer->buf = nullptr;
+ }
} else {
+ if (!buffer->buf) {
+ const quint32 expectedSize = buffer->size;
+ qsizetype foundBufferIndex = 0;
+ for (qsizetype i = 0; i < bufferPool->size(); ++i) {
+ QRhiBuffer *testBuffer = bufferPool->at(i);
+ if (!buffer->buf
+ || (testBuffer->size() >= expectedSize && testBuffer->size() < buffer->buf->size())
+ || (testBuffer->size() < expectedSize && testBuffer->size() > buffer->buf->size())) {
+ foundBufferIndex = i;
+ buffer->buf = testBuffer;
+ if (buffer->buf->size() == expectedSize)
+ break;
+ }
+ }
+
+ if (foundBufferIndex < bufferPool->size() - 1) {
+ qSwap(bufferPool->data()[foundBufferIndex],
+ bufferPool->data()[bufferPool->size() - 1]);
+ }
+ bufferPool->pop_back();
+ }
+
bool needsRebuild = false;
if (buffer->buf->size() < buffer->size) {
buffer->buf->setSize(buffer->size);
@@ -1070,16 +1073,23 @@ void Renderer::unmap(Buffer *buffer, bool isIndexBuf)
buffer->nonDynamicChangeCount = 0;
needsRebuild = true;
}
- if (needsRebuild)
- buffer->buf->create();
+ if (needsRebuild) {
+ if (!buffer->buf->create()) {
+ qWarning("Failed to (re)build vertex/index buffer of size %u", buffer->size);
+ delete buffer->buf;
+ buffer->buf = nullptr;
+ }
+ }
}
- if (buffer->buf->type() != QRhiBuffer::Dynamic) {
- m_resourceUpdates->uploadStaticBuffer(buffer->buf,
- 0, buffer->size, buffer->data);
- buffer->nonDynamicChangeCount += 1;
- } else {
- m_resourceUpdates->updateDynamicBuffer(buffer->buf, 0, buffer->size,
- buffer->data);
+ if (buffer->buf) {
+ if (buffer->buf->type() != QRhiBuffer::Dynamic) {
+ m_resourceUpdates->uploadStaticBuffer(buffer->buf,
+ 0, buffer->size, buffer->data);
+ buffer->nonDynamicChangeCount += 1;
+ } else {
+ m_resourceUpdates->updateDynamicBuffer(buffer->buf, 0, buffer->size,
+ buffer->data);
+ }
}
if (m_visualizer->mode() == Visualizer::VisualizeNothing)
buffer->data = nullptr;
@@ -1266,8 +1276,13 @@ void Renderer::nodeWasRemoved(Node *node)
if (e) {
e->removed = true;
m_elementsToDelete.add(e);
- if (m_renderNodeElements.isEmpty())
+ if (m_renderNodeElements.isEmpty()) {
m_forceNoDepthBuffer = false;
+ // Must have a full rebuild given useDepthBuffer() now returns
+ // a different value than before, meaning there can once again
+ // be an opaque pass.
+ m_rebuild |= FullRebuild;
+ }
if (e->batch != nullptr)
e->batch->needsPurge = true;
@@ -1726,6 +1741,7 @@ void Renderer::prepareOpaqueBatches()
&& gni->geometry()->attributes() == gnj->geometry()->attributes()
&& gni->inheritedOpacity() == gnj->inheritedOpacity()
&& gni->activeMaterial()->type() == gnj->activeMaterial()->type()
+ && gni->activeMaterial()->viewCount() == gnj->activeMaterial()->viewCount()
&& gni->activeMaterial()->compare(gnj->activeMaterial()) == 0) {
ej->batch = batch;
next->nextInBatch = ej;
@@ -1829,10 +1845,15 @@ void Renderer::prepareAlphaBatches()
if (gni->clipList() == gnj->clipList()
&& gni->geometry()->drawingMode() == gnj->geometry()->drawingMode()
- && (gni->geometry()->drawingMode() != QSGGeometry::DrawLines || gni->geometry()->lineWidth() == gnj->geometry()->lineWidth())
+ && (gni->geometry()->drawingMode() != QSGGeometry::DrawLines
+ || (gni->geometry()->lineWidth() == gnj->geometry()->lineWidth()
+ // Must not do overlap checks when the line width is not 1,
+ // we have no knowledge how such lines are rasterized.
+ && gni->geometry()->lineWidth() == 1.0f))
&& gni->geometry()->attributes() == gnj->geometry()->attributes()
&& gni->inheritedOpacity() == gnj->inheritedOpacity()
&& gni->activeMaterial()->type() == gnj->activeMaterial()->type()
+ && gni->activeMaterial()->viewCount() == gnj->activeMaterial()->viewCount()
&& gni->activeMaterial()->compare(gnj->activeMaterial()) == 0) {
if (!overlapBounds.intersects(ej->bounds) || !checkOverlap(i+1, j - 1, ej->bounds)) {
ej->batch = batch;
@@ -1878,6 +1899,12 @@ static inline int qsg_fixIndexCount(int iCount, int drawMode)
}
}
+static inline float calculateElementZOrder(const Element *e, qreal zRange)
+{
+ // Clamp the zOrder to within the min and max depth of the viewport.
+ return std::clamp(1.0f - float(e->order * zRange), VIEWPORT_MIN_DEPTH, VIEWPORT_MAX_DEPTH);
+}
+
/* These parameters warrant some explanation...
*
* vaOffset: The byte offset into the vertex data to the location of the
@@ -1922,7 +1949,7 @@ void Renderer::uploadMergedElement(Element *e, int vaOffset, char **vertexData,
if (useDepthBuffer()) {
float *vzorder = (float *) *zData;
- float zorder = 1.0f - e->order * m_zRange;
+ float zorder = calculateElementZOrder(e, m_zRange);
for (int i=0; i<vCount; ++i)
vzorder[i] = zorder;
*zData += vCount * sizeof(float);
@@ -2030,8 +2057,8 @@ void Renderer::uploadBatch(Batch *b)
bool canMerge = (g->drawingMode() == QSGGeometry::DrawTriangles || g->drawingMode() == QSGGeometry::DrawTriangleStrip ||
g->drawingMode() == QSGGeometry::DrawLines || g->drawingMode() == QSGGeometry::DrawPoints)
&& b->positionAttribute >= 0
- && g->indexType() == QSGGeometry::UnsignedShortType
- && (flags & (QSGMaterial::CustomCompileStep | QSGMaterial_FullMatrix)) == 0
+ && (g->indexType() == QSGGeometry::UnsignedShortType && g->indexCount() > 0)
+ && (flags & (QSGMaterial::NoBatching | QSGMaterial_FullMatrix)) == 0
&& ((flags & QSGMaterial::RequiresFullMatrixExceptTranslate) == 0 || b->isTranslateOnlyToRoot())
&& b->isSafeToBatch();
@@ -2086,11 +2113,7 @@ void Renderer::uploadBatch(Batch *b)
ibufferSize = unmergedIndexSize;
}
- const bool separateIndexBuffer = m_context->separateIndexBuffer();
- if (separateIndexBuffer)
- map(&b->ibo, ibufferSize, true);
- else
- bufferSize += ibufferSize;
+ map(&b->ibo, ibufferSize, true);
map(&b->vbo, bufferSize);
if (Q_UNLIKELY(debug_upload())) qDebug() << " - batch" << b << " first:" << b->first << " root:"
@@ -2100,9 +2123,7 @@ void Renderer::uploadBatch(Batch *b)
if (b->merged) {
char *vertexData = b->vbo.data;
char *zData = vertexData + b->vertexCount * g->sizeOfVertex();
- char *indexData = separateIndexBuffer
- ? b->ibo.data
- : zData + (int(useDepthBuffer()) * b->vertexCount * sizeof(float));
+ char *indexData = b->ibo.data;
quint16 iOffset16 = 0;
quint32 iOffset32 = 0;
@@ -2114,8 +2135,8 @@ void Renderer::uploadBatch(Batch *b)
const uint verticesInSetLimit = m_uint32IndexForRhi ? 0xfffffffe : 0xfffe;
int indicesInSet = 0;
b->drawSets.reset();
- int drawSetIndices = separateIndexBuffer ? 0 : indexData - vertexData;
- const char *indexBase = separateIndexBuffer ? b->ibo.data : b->vbo.data;
+ int drawSetIndices = 0;
+ const char *indexBase = b->ibo.data;
b->drawSets << DrawSet(0, zData - vertexData, drawSetIndices);
while (e) {
verticesInSet += e->node->geometry()->vertexCount();
@@ -2149,8 +2170,7 @@ void Renderer::uploadBatch(Batch *b)
}
} else {
char *vboData = b->vbo.data;
- char *iboData = separateIndexBuffer ? b->ibo.data
- : vboData + b->vertexCount * g->sizeOfVertex();
+ char *iboData = b->ibo.data;
Element *e = b->first;
while (e) {
QSGGeometry *g = e->node->geometry();
@@ -2212,9 +2232,7 @@ void Renderer::uploadBatch(Batch *b)
if (!b->drawSets.isEmpty()) {
if (m_uint32IndexForRhi) {
- const quint32 *id = (const quint32 *)(separateIndexBuffer
- ? b->ibo.data
- : b->vbo.data + b->drawSets.at(0).indices);
+ const quint32 *id = (const quint32 *) b->ibo.data;
{
QDebug iDump = qDebug();
iDump << " -- Index Data, count:" << b->indexCount;
@@ -2225,9 +2243,7 @@ void Renderer::uploadBatch(Batch *b)
}
}
} else {
- const quint16 *id = (const quint16 *)(separateIndexBuffer
- ? b->ibo.data
- : b->vbo.data + b->drawSets.at(0).indices);
+ const quint16 *id = (const quint16 *) b->ibo.data;
{
QDebug iDump = qDebug();
iDump << " -- Index Data, count:" << b->indexCount;
@@ -2248,8 +2264,7 @@ void Renderer::uploadBatch(Batch *b)
#endif // QT_NO_DEBUG_OUTPUT
unmap(&b->vbo);
- if (separateIndexBuffer)
- unmap(&b->ibo, true);
+ unmap(&b->ibo, true);
if (Q_UNLIKELY(debug_upload())) qDebug() << " --- vertex/index buffers unmapped, batch upload completed...";
@@ -2272,7 +2287,7 @@ QRhiGraphicsPipeline *Renderer::buildStencilPipeline(const Batch *batch, bool fi
QRhiGraphicsPipeline::TargetBlend blend;
blend.colorWrite = {};
ps->setTargetBlends({ blend });
- ps->setSampleCount(renderTarget()->sampleCount());
+ ps->setSampleCount(renderTarget().rt->sampleCount());
ps->setStencilTest(true);
QRhiGraphicsPipeline::StencilOpState stencilOp;
if (firstStencilClipInBatch) {
@@ -2291,11 +2306,13 @@ QRhiGraphicsPipeline *Renderer::buildStencilPipeline(const Batch *batch, bool fi
ps->setTopology(m_stencilClipCommon.topology);
- ps->setShaderStages({ QRhiGraphicsShaderStage(QRhiGraphicsShaderStage::Vertex, m_stencilClipCommon.vs),
- QRhiGraphicsShaderStage(QRhiGraphicsShaderStage::Fragment, m_stencilClipCommon.fs) });
+ ps->setMultiViewCount(renderTarget().multiViewCount);
+
+ ps->setShaderStages({ QRhiShaderStage(QRhiShaderStage::Vertex, m_stencilClipCommon.vs),
+ QRhiShaderStage(QRhiShaderStage::Fragment, m_stencilClipCommon.fs) });
ps->setVertexInputLayout(m_stencilClipCommon.inputLayout);
ps->setShaderResourceBindings(batch->stencilClipState.srb); // use something, it just needs to be layout-compatible
- ps->setRenderPassDescriptor(renderPassDescriptor());
+ ps->setRenderPassDescriptor(renderTarget().rpDesc);
if (!ps->create()) {
qWarning("Failed to build stencil clip pipeline");
@@ -2333,13 +2350,13 @@ void Renderer::updateClipState(const QSGClipNode *clipList, Batch *batch)
const QSGClipNode *clip = clipList;
batch->stencilClipState.drawCalls.reset();
- int totalVSize = 0;
- int totalISize = 0;
- int totalUSize = 0;
- const int StencilClipUbufSize = 64;
+ quint32 totalVSize = 0;
+ quint32 totalISize = 0;
+ quint32 totalUSize = 0;
+ const quint32 StencilClipUbufSize = 64;
while (clip) {
- QMatrix4x4 m = m_current_projection_matrix_native_ndc;
+ QMatrix4x4 m = m_current_projection_matrix_native_ndc[0]; // never hit for 3D and so multiview
if (clip->matrix())
m *= *clip->matrix();
@@ -2391,11 +2408,11 @@ void Renderer::updateClipState(const QSGClipNode *clipList, Batch *batch)
const int vertexByteSize = g->sizeOfVertex() * g->vertexCount();
// the 4 byte alignment may not actually be needed here
- totalVSize = aligned(totalVSize, 4) + vertexByteSize;
+ totalVSize = aligned(totalVSize, 4u) + vertexByteSize;
if (g->indexCount()) {
const int indexByteSize = g->sizeOfIndex() * g->indexCount();
// so no need to worry about NonFourAlignedEffectiveIndexBufferOffset
- totalISize = aligned(totalISize, 4) + indexByteSize;
+ totalISize = aligned(totalISize, 4u) + indexByteSize;
}
// ubuf start offsets must be aligned (typically to 256 bytes)
totalUSize = aligned(totalUSize, m_ubufAlignment) + StencilClipUbufSize;
@@ -2473,9 +2490,9 @@ void Renderer::updateClipState(const QSGClipNode *clipList, Batch *batch)
}
}
- int vOffset = 0;
- int iOffset = 0;
- int uOffset = 0;
+ quint32 vOffset = 0;
+ quint32 iOffset = 0;
+ quint32 uOffset = 0;
for (const QSGClipNode *clip : stencilClipNodes) {
const QSGGeometry *g = clip->geometry();
const QSGGeometry::Attribute *a = g->attributes();
@@ -2496,13 +2513,13 @@ void Renderer::updateClipState(const QSGClipNode *clipList, Batch *batch)
}
#endif
- drawCall.vbufOffset = aligned(vOffset, 4);
+ drawCall.vbufOffset = aligned(vOffset, 4u);
const int vertexByteSize = g->sizeOfVertex() * g->vertexCount();
vOffset = drawCall.vbufOffset + vertexByteSize;
int indexByteSize = 0;
if (g->indexCount()) {
- drawCall.ibufOffset = aligned(iOffset, 4);
+ drawCall.ibufOffset = aligned(iOffset, 4u);
indexByteSize = g->sizeOfIndex() * g->indexCount();
iOffset = drawCall.ibufOffset + indexByteSize;
}
@@ -2510,7 +2527,7 @@ void Renderer::updateClipState(const QSGClipNode *clipList, Batch *batch)
drawCall.ubufOffset = aligned(uOffset, m_ubufAlignment);
uOffset = drawCall.ubufOffset + StencilClipUbufSize;
- QMatrix4x4 matrixYUpNDC = m_current_projection_matrix;
+ QMatrix4x4 matrixYUpNDC = m_current_projection_matrix[0];
if (clip->matrix())
matrixYUpNDC *= *clip->matrix();
@@ -2565,7 +2582,7 @@ void Renderer::enqueueStencilDraw(const Batch *batch)
if (!batch->stencilClipState.updateStencilBuffer)
return;
- QRhiCommandBuffer *cb = commandBuffer();
+ QRhiCommandBuffer *cb = renderTarget().cb;
const int count = batch->stencilClipState.drawCalls.size();
for (int i = 0; i < count; ++i) {
const StencilClipState::StencilDrawCall &drawCall(batch->stencilClipState.drawCalls.at(i));
@@ -2625,10 +2642,29 @@ static inline bool needsBlendConstant(QRhiGraphicsPipeline::BlendFactor f)
bool Renderer::ensurePipelineState(Element *e, const ShaderManager::Shader *sms, bool depthPostPass)
{
- // In unmerged batches the srbs in the elements are all compatible
- // layout-wise. Note the key's == and qHash implementations: the rp desc and
- // srb are tested for (layout) compatibility, not pointer equality.
- const GraphicsPipelineStateKey k { m_gstate, sms, renderPassDescriptor(), e->srb };
+ // Note the key's == and qHash implementations: the renderpass descriptor
+ // and srb are tested for compatibility, not pointer equality.
+ //
+ // We do not store the srb pointer itself because the ownership stays with
+ // the Element and that can go away more often that we would like it
+ // to. (think scrolling a list view, constantly dropping and creating new
+ // nodes) Rather, use an opaque blob of a few uints and store and compare
+ // that. This works because once the pipeline is built, we will always call
+ // setShaderResources with an explicitly specified srb which is fine even if
+ // e->srb we used here to bake the pipeline is already gone by that point.
+ //
+ // A typical QSGMaterial's serialized srb layout is 8 uints. (uniform buffer
+ // + texture, 4 fields each) Regardless, using an implicitly shared
+ // container is essential here. (won't detach so no more allocs and copies
+ // are done, unless the Element decides to rebake the srb with a different
+ // layout - but then the detach is exactly what we need)
+ //
+ // Same story for the renderpass descriptor: the object can go away but
+ // that's fine because that has no effect on an already built pipeline, and
+ // for comparison we only rely on the serialized blob in order decide if the
+ // render target is compatible with the pipeline.
+
+ const GraphicsPipelineStateKey k = GraphicsPipelineStateKey::create(m_gstate, sms, renderTarget().rpDesc, e->srb);
// Note: dynamic state (viewport rect, scissor rect, stencil ref, blend
// constant) is never a part of GraphicsState/QRhiGraphicsPipeline.
@@ -2645,14 +2681,17 @@ bool Renderer::ensurePipelineState(Element *e, const ShaderManager::Shader *sms,
// Build a new one. This is potentially expensive.
QRhiGraphicsPipeline *ps = m_rhi->newGraphicsPipeline();
- ps->setShaderStages(sms->programRhi.shaderStages.cbegin(), sms->programRhi.shaderStages.cend());
- ps->setVertexInputLayout(sms->programRhi.inputLayout);
+ ps->setShaderStages(sms->stages.cbegin(), sms->stages.cend());
+ ps->setVertexInputLayout(sms->inputLayout);
ps->setShaderResourceBindings(e->srb);
- ps->setRenderPassDescriptor(renderPassDescriptor());
+ ps->setRenderPassDescriptor(renderTarget().rpDesc);
QRhiGraphicsPipeline::Flags flags;
- if (needsBlendConstant(m_gstate.srcColor) || needsBlendConstant(m_gstate.dstColor))
+ if (needsBlendConstant(m_gstate.srcColor) || needsBlendConstant(m_gstate.dstColor)
+ || needsBlendConstant(m_gstate.srcAlpha) || needsBlendConstant(m_gstate.dstAlpha))
+ {
flags |= QRhiGraphicsPipeline::UsesBlendConstants;
+ }
if (m_gstate.usesScissor)
flags |= QRhiGraphicsPipeline::UsesScissor;
if (m_gstate.stencilTest)
@@ -2661,12 +2700,16 @@ bool Renderer::ensurePipelineState(Element *e, const ShaderManager::Shader *sms,
ps->setFlags(flags);
ps->setTopology(qsg_topology(m_gstate.drawMode));
ps->setCullMode(m_gstate.cullMode);
+ ps->setPolygonMode(m_gstate.polygonMode);
+ ps->setMultiViewCount(m_gstate.multiViewCount);
QRhiGraphicsPipeline::TargetBlend blend;
blend.colorWrite = m_gstate.colorWrite;
blend.enable = m_gstate.blending;
- blend.srcColor = blend.srcAlpha = m_gstate.srcColor;
- blend.dstColor = blend.dstAlpha = m_gstate.dstColor;
+ blend.srcColor = m_gstate.srcColor;
+ blend.dstColor = m_gstate.dstColor;
+ blend.srcAlpha = m_gstate.srcAlpha;
+ blend.dstAlpha = m_gstate.dstAlpha;
ps->setTargetBlends({ blend });
ps->setDepthTest(m_gstate.depthTest);
@@ -2800,13 +2843,25 @@ static void rendererToMaterialGraphicsState(QSGMaterialShader::GraphicsPipelineS
Q_ASSERT(int(QSGMaterialShader::GraphicsPipelineState::OneMinusSrc1Alpha) == int(QRhiGraphicsPipeline::OneMinusSrc1Alpha));
Q_ASSERT(int(QSGMaterialShader::GraphicsPipelineState::A) == int(QRhiGraphicsPipeline::A));
Q_ASSERT(int(QSGMaterialShader::GraphicsPipelineState::CullBack) == int(QRhiGraphicsPipeline::Back));
-
+ Q_ASSERT(int(QSGMaterialShader::GraphicsPipelineState::Line) == int(QRhiGraphicsPipeline::Line));
dst->srcColor = QSGMaterialShader::GraphicsPipelineState::BlendFactor(src->srcColor);
dst->dstColor = QSGMaterialShader::GraphicsPipelineState::BlendFactor(src->dstColor);
+ // For compatibility with any existing code, separateBlendFactors defaults
+ // to _false_ which means that materials that do not touch srcAlpha and
+ // dstAlpha will continue to use srcColor and dstColor as the alpha
+ // blending factors. New code that needs different values for color/alpha,
+ // can explicitly set separateBlendFactors to true and then set srcAlpha
+ // and dstAlpha as well.
+ dst->separateBlendFactors = false;
+
+ dst->srcAlpha = QSGMaterialShader::GraphicsPipelineState::BlendFactor(src->srcAlpha);
+ dst->dstAlpha = QSGMaterialShader::GraphicsPipelineState::BlendFactor(src->dstAlpha);
+
dst->colorWrite = QSGMaterialShader::GraphicsPipelineState::ColorMask(int(src->colorWrite));
dst->cullMode = QSGMaterialShader::GraphicsPipelineState::CullMode(src->cullMode);
+ dst->polygonMode = QSGMaterialShader::GraphicsPipelineState::PolygonMode(src->polygonMode);
}
static void materialToRendererGraphicsState(GraphicsState *dst,
@@ -2815,22 +2870,32 @@ static void materialToRendererGraphicsState(GraphicsState *dst,
dst->blending = src->blendEnable;
dst->srcColor = QRhiGraphicsPipeline::BlendFactor(src->srcColor);
dst->dstColor = QRhiGraphicsPipeline::BlendFactor(src->dstColor);
+ if (src->separateBlendFactors) {
+ dst->srcAlpha = QRhiGraphicsPipeline::BlendFactor(src->srcAlpha);
+ dst->dstAlpha = QRhiGraphicsPipeline::BlendFactor(src->dstAlpha);
+ } else {
+ dst->srcAlpha = dst->srcColor;
+ dst->dstAlpha = dst->dstColor;
+ }
dst->colorWrite = QRhiGraphicsPipeline::ColorMask(int(src->colorWrite));
dst->cullMode = QRhiGraphicsPipeline::CullMode(src->cullMode);
+ dst->polygonMode = QRhiGraphicsPipeline::PolygonMode(src->polygonMode);
}
void Renderer::updateMaterialDynamicData(ShaderManager::Shader *sms,
QSGMaterialShader::RenderState &renderState,
QSGMaterial *material,
- ShaderManager::ShaderResourceBindingList *bindings,
const Batch *batch,
+ Element *e,
int ubufOffset,
int ubufRegionSize)
{
m_current_resource_update_batch = m_resourceUpdates;
- QSGMaterialShader *shader = sms->programRhi.program;
+ QSGMaterialShader *shader = sms->materialShader;
QSGMaterialShaderPrivate *pd = QSGMaterialShaderPrivate::get(shader);
+ QVarLengthArray<QRhiShaderResourceBinding, 8> bindings;
+
if (pd->ubufBinding >= 0) {
m_current_uniform_data = &pd->masterUniformData;
const bool changed = shader->updateUniformData(renderState, material, m_currentMaterial);
@@ -2839,11 +2904,11 @@ void Renderer::updateMaterialDynamicData(ShaderManager::Shader *sms,
if (changed || !batch->ubufDataValid)
m_resourceUpdates->updateDynamicBuffer(batch->ubuf, ubufOffset, ubufRegionSize, pd->masterUniformData.constData());
- bindings->append(QRhiShaderResourceBinding::uniformBuffer(pd->ubufBinding,
- pd->ubufStages,
- batch->ubuf,
- ubufOffset,
- ubufRegionSize));
+ bindings.append(QRhiShaderResourceBinding::uniformBuffer(pd->ubufBinding,
+ pd->ubufStages,
+ batch->ubuf,
+ ubufOffset,
+ ubufRegionSize));
}
for (int binding = 0; binding < QSGMaterialShaderPrivate::MAX_SHADER_RESOURCE_BINDINGS; ++binding) {
@@ -2851,63 +2916,167 @@ void Renderer::updateMaterialDynamicData(ShaderManager::Shader *sms,
if (!stages)
continue;
- QSGTexture *prevTex = pd->textureBindingTable[binding];
- QSGTexture *t = prevTex;
+ QVarLengthArray<QSGTexture *, 4> prevTex = pd->textureBindingTable[binding];
+ QVarLengthArray<QSGTexture *, 4> nextTex = prevTex;
+
+ const int count = pd->combinedImageSamplerCount[binding];
+ nextTex.resize(count);
+
+ shader->updateSampledImage(renderState, binding, nextTex.data(), material,
+ m_currentMaterial);
- shader->updateSampledImage(renderState, binding, &t, material, m_currentMaterial);
- if (!t) {
+ if (nextTex.contains(nullptr)) {
qWarning("No QSGTexture provided from updateSampledImage(). This is wrong.");
continue;
}
- QSGTexturePrivate *td = QSGTexturePrivate::get(t);
+ bool hasDirtySamplerOptions = false;
+ bool isAnisotropic = false;
+ for (QSGTexture *t : nextTex) {
+ QSGTexturePrivate *td = QSGTexturePrivate::get(t);
+ hasDirtySamplerOptions |= td->hasDirtySamplerOptions();
+ isAnisotropic |= t->anisotropyLevel() != QSGTexture::AnisotropyNone;
+ td->resetDirtySamplerOptions();
+ }
+
// prevTex may be invalid at this point, avoid dereferencing it
- if (t != prevTex || td->hasDirtySamplerOptions()) {
+ if (nextTex != prevTex || hasDirtySamplerOptions) {
+
// The QSGTexture, and so the sampler parameters, may have changed.
// The rhiTexture is not relevant here.
- td->resetDirtySamplerOptions();
- pd->textureBindingTable[binding] = t; // does not own
- pd->samplerBindingTable[binding] = nullptr;
- if (t->anisotropyLevel() != QSGTexture::AnisotropyNone) // ###
+ pd->textureBindingTable[binding] = nextTex; // does not own
+ pd->samplerBindingTable[binding].clear();
+
+ if (isAnisotropic) // ###
qWarning("QSGTexture anisotropy levels are not currently supported");
- const QSGSamplerDescription samplerDesc = QSGSamplerDescription::fromTexture(t);
- QRhiSampler *sampler = nullptr;
- auto it = m_samplers.constFind(samplerDesc);
- if (it != m_samplers.constEnd()) {
- sampler = *it;
- Q_ASSERT(sampler);
- } else {
- sampler = newSampler(m_rhi, samplerDesc);
- if (!sampler->create()) {
- qWarning("Failed to build sampler");
- delete sampler;
- continue;
+ QVarLengthArray<QRhiSampler *, 4> samplers;
+
+ for (QSGTexture *t : nextTex) {
+ const QSGSamplerDescription samplerDesc = QSGSamplerDescription::fromTexture(t);
+
+ QRhiSampler *sampler = m_samplers[samplerDesc];
+
+ if (!sampler) {
+ sampler = newSampler(m_rhi, samplerDesc);
+ if (!sampler->create()) {
+ qWarning("Failed to build sampler");
+ delete sampler;
+ continue;
+ }
+ m_samplers[samplerDesc] = sampler;
}
- m_samplers.insert(samplerDesc, sampler);
+ samplers.append(sampler);
}
- pd->samplerBindingTable[binding] = sampler; // does not own
+
+ pd->samplerBindingTable[binding] = samplers; // does not own
}
- if (pd->textureBindingTable[binding] && pd->samplerBindingTable[binding]) {
- QRhiTexture *texture = pd->textureBindingTable[binding]->rhiTexture();
- // texture may be null if the update above failed for any reason,
- // or if the QSGTexture chose to return null intentionally. This is
- // valid and we still need to provide something to the shader.
- if (!texture)
- texture = dummyTexture();
- QRhiSampler *sampler = pd->samplerBindingTable[binding];
- bindings->append(QRhiShaderResourceBinding::sampledTexture(binding,
- stages,
- texture,
- sampler));
+ if (pd->textureBindingTable[binding].size() == pd->samplerBindingTable[binding].size()) {
+
+ QVarLengthArray<QRhiShaderResourceBinding::TextureAndSampler, 4> textureSamplers;
+
+ for (int i = 0; i < pd->textureBindingTable[binding].size(); ++i) {
+
+ QRhiTexture *texture = pd->textureBindingTable[binding].at(i)->rhiTexture();
+
+ // texture may be null if the update above failed for any reason,
+ // or if the QSGTexture chose to return null intentionally. This is
+ // valid and we still need to provide something to the shader.
+ if (!texture)
+ texture = dummyTexture();
+
+ QRhiSampler *sampler = pd->samplerBindingTable[binding].at(i);
+
+ textureSamplers.append(
+ QRhiShaderResourceBinding::TextureAndSampler { texture, sampler });
+ }
+
+ if (!textureSamplers.isEmpty())
+ bindings.append(QRhiShaderResourceBinding::sampledTextures(
+ binding, stages, count, textureSamplers.constData()));
}
}
#ifndef QT_NO_DEBUG
- if (bindings->isEmpty())
+ if (bindings.isEmpty())
qWarning("No shader resources for material %p, this is odd.", material);
#endif
+
+ enum class SrbAction {
+ Unknown,
+ DoNothing,
+ UpdateResources,
+ Rebake
+ } srbAction = SrbAction::Unknown;
+
+ // First, if the Element has no srb created at all, then try to find an existing,
+ // currently unused srb that is layout-compatible with our binding list.
+ if (!e->srb) {
+ // reuse a QVector as our work area, thus possibly reusing the underlying allocation too
+ QVector<quint32> &layoutDesc(m_shaderManager->srbLayoutDescSerializeWorkspace);
+ layoutDesc.clear();
+ QRhiShaderResourceBinding::serializeLayoutDescription(bindings.cbegin(), bindings.cend(), std::back_inserter(layoutDesc));
+ e->srb = m_shaderManager->srbPool.take(layoutDesc);
+ if (e->srb) {
+ // Here we know layout compatibility is satisfied, but do not spend time on full
+ // comparison. The chance of getting an srb that refers to the same resources
+ // (buffer, textures) is low in practice. So reuse, but write new resources.
+ srbAction = SrbAction::UpdateResources;
+ }
+ }
+
+ // If the Element had an existing srb, investigate:
+ // - It may be used as-is (when nothing changed in the scene regarding this node compared to the previous frame).
+ // - Otherwise it may be able to go with a lightweight update (replace resources, binding list layout is the same).
+ // - If all else fails rebake the full thing, meaning we reuse the memory allocation but will recreate everything underneath.
+ if (srbAction == SrbAction::Unknown && e->srb) {
+ if (std::equal(e->srb->cbeginBindings(), e->srb->cendBindings(), bindings.cbegin(), bindings.cend())) {
+ srbAction = SrbAction::DoNothing;
+ } else if (std::equal(e->srb->cbeginBindings(), e->srb->cendBindings(), bindings.cbegin(), bindings.cend(),
+ [](const auto &a, const auto &b) { return a.isLayoutCompatible(b); }))
+ {
+ srbAction = SrbAction::UpdateResources;
+ } else {
+ srbAction = SrbAction::Rebake;
+ }
+ }
+
+ // If the Element had no srb associated at all and could not find a layout-compatible
+ // one from the pool, then create a whole new object.
+ if (!e->srb) {
+ e->srb = m_rhi->newShaderResourceBindings();
+ srbAction = SrbAction::Rebake;
+ }
+
+ Q_ASSERT(srbAction != SrbAction::Unknown && e->srb);
+
+ switch (srbAction) {
+ case SrbAction::DoNothing:
+ break;
+ case SrbAction::UpdateResources:
+ {
+ e->srb->setBindings(bindings.cbegin(), bindings.cend());
+ QRhiShaderResourceBindings::UpdateFlags flags;
+ // Due to the way the binding list is built up above, if we have a uniform buffer
+ // at binding point 0 (or none at all) then the sampledTexture bindings are added
+ // with increasing binding points afterwards, so the list is already sorted based
+ // on the binding points, thus we can save some time by telling the QRhi backend
+ // not to sort again.
+ if (pd->ubufBinding <= 0 || bindings.size() <= 1)
+ flags |= QRhiShaderResourceBindings::BindingsAreSorted;
+
+ e->srb->updateResources(flags);
+ }
+ break;
+ case SrbAction::Rebake:
+ e->srb->setBindings(bindings.cbegin(), bindings.cend());
+ if (!e->srb->create())
+ qWarning("Failed to build srb");
+ break;
+ default:
+ Q_ASSERT_X(false, "updateMaterialDynamicData", "No srb action set, this cannot happen");
+ }
}
void Renderer::updateMaterialStaticData(ShaderManager::Shader *sms,
@@ -2916,7 +3085,7 @@ void Renderer::updateMaterialStaticData(ShaderManager::Shader *sms,
Batch *batch,
bool *gstateChanged)
{
- QSGMaterialShader *shader = sms->programRhi.program;
+ QSGMaterialShader *shader = sms->materialShader;
*gstateChanged = false;
if (shader->flags().testFlag(QSGMaterialShader::UpdatesGraphicsPipelineState)) {
// generate the public mini-state from m_gstate, invoke the material,
@@ -2928,8 +3097,11 @@ void Renderer::updateMaterialStaticData(ShaderManager::Shader *sms,
if (changed) {
m_gstateStack.push(m_gstate);
materialToRendererGraphicsState(&m_gstate, &shaderPs);
- if (needsBlendConstant(m_gstate.srcColor) || needsBlendConstant(m_gstate.dstColor))
+ if (needsBlendConstant(m_gstate.srcColor) || needsBlendConstant(m_gstate.dstColor)
+ || needsBlendConstant(m_gstate.srcAlpha) || needsBlendConstant(m_gstate.dstAlpha))
+ {
batch->blendConstant = shaderPs.blendConstant;
+ }
*gstateChanged = true;
}
}
@@ -2973,22 +3145,30 @@ bool Renderer::prepareRenderMergedBatch(Batch *batch, PreparedRenderBatch *rende
else
m_current_model_view_matrix.setToIdentity();
m_current_determinant = m_current_model_view_matrix.determinant();
- m_current_projection_matrix = projectionMatrix();
- m_current_projection_matrix_native_ndc = projectionMatrixWithNativeNDC();
+
+ const int viewCount = projectionMatrixCount();
+ m_current_projection_matrix.resize(viewCount);
+ for (int viewIndex = 0; viewIndex < viewCount; ++viewIndex)
+ m_current_projection_matrix[viewIndex] = projectionMatrix(viewIndex);
+
+ m_current_projection_matrix_native_ndc.resize(projectionMatrixWithNativeNDCCount());
+ for (int viewIndex = 0; viewIndex < projectionMatrixWithNativeNDCCount(); ++viewIndex)
+ m_current_projection_matrix_native_ndc[viewIndex] = projectionMatrixWithNativeNDC(viewIndex);
QSGMaterial *material = gn->activeMaterial();
if (m_renderMode != QSGRendererInterface::RenderMode3D)
updateClipState(gn->clipList(), batch);
const QSGGeometry *g = gn->geometry();
- ShaderManager::Shader *sms = useDepthBuffer() ? m_shaderManager->prepareMaterial(material, g, m_renderMode)
- : m_shaderManager->prepareMaterialNoRewrite(material, g, m_renderMode);
+ const int multiViewCount = renderTarget().multiViewCount;
+ ShaderManager::Shader *sms = useDepthBuffer() ? m_shaderManager->prepareMaterial(material, g, m_renderMode, multiViewCount)
+ : m_shaderManager->prepareMaterialNoRewrite(material, g, m_renderMode, multiViewCount);
if (!sms)
return false;
- Q_ASSERT(sms->programRhi.program);
+ Q_ASSERT(sms->materialShader);
if (m_currentShader != sms)
- setActiveRhiShader(sms->programRhi.program, sms);
+ setActiveRhiShader(sms->materialShader, sms);
m_current_opacity = gn->inheritedOpacity();
if (!qFuzzyCompare(sms->lastOpacity, float(m_current_opacity))) {
@@ -2996,8 +3176,8 @@ bool Renderer::prepareRenderMergedBatch(Batch *batch, PreparedRenderBatch *rende
sms->lastOpacity = m_current_opacity;
}
- QSGMaterialShaderPrivate *pd = QSGMaterialShaderPrivate::get(sms->programRhi.program);
- const int ubufSize = pd->masterUniformData.size();
+ QSGMaterialShaderPrivate *pd = QSGMaterialShaderPrivate::get(sms->materialShader);
+ const quint32 ubufSize = quint32(pd->masterUniformData.size());
if (pd->ubufBinding >= 0) {
bool ubufRebuild = false;
if (!batch->ubuf) {
@@ -3012,7 +3192,7 @@ bool Renderer::prepareRenderMergedBatch(Batch *batch, PreparedRenderBatch *rende
if (ubufRebuild) {
batch->ubufDataValid = false;
if (!batch->ubuf->create()) {
- qWarning("Failed to build uniform buffer of size %d bytes", ubufSize);
+ qWarning("Failed to build uniform buffer of size %u bytes", ubufSize);
delete batch->ubuf;
batch->ubuf = nullptr;
return false;
@@ -3025,8 +3205,7 @@ bool Renderer::prepareRenderMergedBatch(Batch *batch, PreparedRenderBatch *rende
bool pendingGStatePop = false;
updateMaterialStaticData(sms, renderState, material, batch, &pendingGStatePop);
- ShaderManager::ShaderResourceBindingList bindings;
- updateMaterialDynamicData(sms, renderState, material, &bindings, batch, 0, ubufSize);
+ updateMaterialDynamicData(sms, renderState, material, batch, e, 0, ubufSize);
#ifndef QT_NO_DEBUG
if (qsg_test_and_clear_material_failure()) {
@@ -3041,8 +3220,6 @@ bool Renderer::prepareRenderMergedBatch(Batch *batch, PreparedRenderBatch *rende
}
#endif
- e->srb = m_shaderManager->srb(bindings);
-
m_gstate.drawMode = QSGGeometry::DrawingMode(g->drawingMode());
m_gstate.lineWidth = g->lineWidth();
@@ -3099,6 +3276,9 @@ void Renderer::checkLineWidth(QSGGeometry *g)
void Renderer::renderMergedBatch(PreparedRenderBatch *renderBatch, bool depthPostPass)
{
const Batch *batch = renderBatch->batch;
+ if (!batch->vbo.buf || !batch->ibo.buf)
+ return;
+
Element *e = batch->first;
QSGGeometryNode *gn = e->node;
QSGGeometry *g = gn->geometry();
@@ -3107,7 +3287,7 @@ void Renderer::renderMergedBatch(PreparedRenderBatch *renderBatch, bool depthPos
if (batch->clipState.type & ClipState::StencilClip)
enqueueStencilDraw(batch);
- QRhiCommandBuffer *cb = commandBuffer();
+ QRhiCommandBuffer *cb = renderTarget().cb;
setGraphicsPipeline(cb, batch, e, depthPostPass);
for (int i = 0, ie = batch->drawSets.size(); i != ie; ++i) {
@@ -3146,8 +3326,14 @@ bool Renderer::prepareRenderUnmergedBatch(Batch *batch, PreparedRenderBatch *ren
batch->uploadedThisFrame = false;
}
- m_current_projection_matrix = projectionMatrix();
- m_current_projection_matrix_native_ndc = projectionMatrixWithNativeNDC();
+ const int viewCount = projectionMatrixCount();
+ m_current_projection_matrix.resize(viewCount);
+ for (int viewIndex = 0; viewIndex < viewCount; ++viewIndex)
+ m_current_projection_matrix[viewIndex] = projectionMatrix(viewIndex);
+
+ m_current_projection_matrix_native_ndc.resize(projectionMatrixWithNativeNDCCount());
+ for (int viewIndex = 0; viewIndex < projectionMatrixWithNativeNDCCount(); ++viewIndex)
+ m_current_projection_matrix_native_ndc[viewIndex] = projectionMatrixWithNativeNDC(viewIndex);
QSGGeometryNode *gn = e->node;
if (m_renderMode != QSGRendererInterface::RenderMode3D)
@@ -3160,13 +3346,13 @@ bool Renderer::prepareRenderUnmergedBatch(Batch *batch, PreparedRenderBatch *ren
// unmerged batch since the material (and so the shaders) is the same.
QSGGeometry *g = gn->geometry();
QSGMaterial *material = gn->activeMaterial();
- ShaderManager::Shader *sms = m_shaderManager->prepareMaterialNoRewrite(material, g);
+ ShaderManager::Shader *sms = m_shaderManager->prepareMaterialNoRewrite(material, g, m_renderMode, renderTarget().multiViewCount);
if (!sms)
return false;
- Q_ASSERT(sms->programRhi.program);
+ Q_ASSERT(sms->materialShader);
if (m_currentShader != sms)
- setActiveRhiShader(sms->programRhi.program, sms);
+ setActiveRhiShader(sms->materialShader, sms);
m_current_opacity = gn->inheritedOpacity();
if (sms->lastOpacity != m_current_opacity) {
@@ -3176,10 +3362,10 @@ bool Renderer::prepareRenderUnmergedBatch(Batch *batch, PreparedRenderBatch *ren
QMatrix4x4 rootMatrix = batch->root ? qsg_matrixForRoot(batch->root) : QMatrix4x4();
- QSGMaterialShaderPrivate *pd = QSGMaterialShaderPrivate::get(sms->programRhi.program);
- const int ubufSize = pd->masterUniformData.size();
+ QSGMaterialShaderPrivate *pd = QSGMaterialShaderPrivate::get(sms->materialShader);
+ const quint32 ubufSize = quint32(pd->masterUniformData.size());
if (pd->ubufBinding >= 0) {
- int totalUBufSize = 0;
+ quint32 totalUBufSize = 0;
while (e) {
totalUBufSize += aligned(ubufSize, m_ubufAlignment);
e = e->nextInBatch;
@@ -3197,7 +3383,7 @@ bool Renderer::prepareRenderUnmergedBatch(Batch *batch, PreparedRenderBatch *ren
if (ubufRebuild) {
batch->ubufDataValid = false;
if (!batch->ubuf->create()) {
- qWarning("Failed to build uniform buffer of size %d bytes", totalUBufSize);
+ qWarning("Failed to build uniform buffer of size %u bytes", totalUBufSize);
delete batch->ubuf;
batch->ubuf = nullptr;
return false;
@@ -3220,17 +3406,23 @@ bool Renderer::prepareRenderUnmergedBatch(Batch *batch, PreparedRenderBatch *ren
m_current_model_view_matrix = rootMatrix * *gn->matrix();
m_current_determinant = m_current_model_view_matrix.determinant();
- m_current_projection_matrix = projectionMatrix();
- m_current_projection_matrix_native_ndc = projectionMatrixWithNativeNDC();
+ const int viewCount = projectionMatrixCount();
+ m_current_projection_matrix.resize(viewCount);
+ for (int viewIndex = 0; viewIndex < viewCount; ++viewIndex)
+ m_current_projection_matrix[viewIndex] = projectionMatrix(viewIndex);
+
+ m_current_projection_matrix_native_ndc.resize(projectionMatrixWithNativeNDCCount());
+ for (int viewIndex = 0; viewIndex < projectionMatrixWithNativeNDCCount(); ++viewIndex)
+ m_current_projection_matrix_native_ndc[viewIndex] = projectionMatrixWithNativeNDC(viewIndex);
+
if (useDepthBuffer()) {
- m_current_projection_matrix(2, 2) = m_zRange;
- m_current_projection_matrix(2, 3) = 1.0f - e->order * m_zRange;
+ // this cannot be multiview
+ m_current_projection_matrix[0](2, 2) = m_zRange;
+ m_current_projection_matrix[0](2, 3) = calculateElementZOrder(e, m_zRange);
}
QSGMaterialShader::RenderState renderState = state(QSGMaterialShader::RenderState::DirtyStates(int(dirty)));
- ShaderManager::ShaderResourceBindingList bindings;
- updateMaterialDynamicData(sms, renderState,
- material, &bindings, batch, ubufOffset, ubufSize);
+ updateMaterialDynamicData(sms, renderState, material, batch, e, ubufOffset, ubufSize);
#ifndef QT_NO_DEBUG
if (qsg_test_and_clear_material_failure()) {
@@ -3242,8 +3434,6 @@ bool Renderer::prepareRenderUnmergedBatch(Batch *batch, PreparedRenderBatch *ren
}
#endif
- e->srb = m_shaderManager->srb(bindings);
-
ubufOffset += aligned(ubufSize, m_ubufAlignment);
const QSGGeometry::DrawingMode prevDrawMode = m_gstate.drawMode;
@@ -3298,19 +3488,20 @@ bool Renderer::prepareRenderUnmergedBatch(Batch *batch, PreparedRenderBatch *ren
void Renderer::renderUnmergedBatch(PreparedRenderBatch *renderBatch, bool depthPostPass)
{
const Batch *batch = renderBatch->batch;
+ if (!batch->vbo.buf)
+ return;
+
Element *e = batch->first;
- QSGGeometryNode *gn = e->node;
if (batch->clipState.type & ClipState::StencilClip)
enqueueStencilDraw(batch);
- int vOffset = 0;
- int iOffset = 0;
- QRhiCommandBuffer *cb = commandBuffer();
+ quint32 vOffset = 0;
+ quint32 iOffset = 0;
+ QRhiCommandBuffer *cb = renderTarget().cb;
while (e) {
- gn = e->node;
- QSGGeometry *g = gn->geometry();
+ QSGGeometry *g = e->node->geometry();
checkLineWidth(g);
const int effectiveIndexSize = m_uint32IndexForRhi ? sizeof(quint32) : g->sizeOfIndex();
@@ -3318,11 +3509,13 @@ void Renderer::renderUnmergedBatch(PreparedRenderBatch *renderBatch, bool depthP
const QRhiCommandBuffer::VertexInput vbufBinding(batch->vbo.buf, vOffset);
if (g->indexCount()) {
- cb->setVertexInput(VERTEX_BUFFER_BINDING, 1, &vbufBinding,
- batch->ibo.buf, iOffset,
- effectiveIndexSize == sizeof(quint32) ? QRhiCommandBuffer::IndexUInt32
- : QRhiCommandBuffer::IndexUInt16);
- cb->drawIndexed(g->indexCount());
+ if (batch->ibo.buf) {
+ cb->setVertexInput(VERTEX_BUFFER_BINDING, 1, &vbufBinding,
+ batch->ibo.buf, iOffset,
+ effectiveIndexSize == sizeof(quint32) ? QRhiCommandBuffer::IndexUInt32
+ : QRhiCommandBuffer::IndexUInt16);
+ cb->drawIndexed(g->indexCount());
+ }
} else {
cb->setVertexInput(VERTEX_BUFFER_BINDING, 1, &vbufBinding);
cb->draw(g->vertexCount());
@@ -3368,6 +3561,26 @@ void Renderer::setGraphicsPipeline(QRhiCommandBuffer *cb, const Batch *batch, El
cb->setShaderResources(e->srb);
}
+void Renderer::releaseElement(Element *e, bool inDestructor)
+{
+ if (e->isRenderNode) {
+ delete static_cast<RenderNodeElement *>(e);
+ } else {
+ if (e->srb) {
+ if (!inDestructor) {
+ if (m_shaderManager->srbPool.size() < m_srbPoolThreshold)
+ m_shaderManager->srbPool.insert(e->srb->serializedLayoutDescription(), e->srb);
+ else
+ delete e->srb;
+ } else {
+ delete e->srb;
+ }
+ e->srb = nullptr;
+ }
+ m_elementAllocator.release(e);
+ }
+}
+
void Renderer::deleteRemovedElements()
{
if (!m_elementsToDelete.size())
@@ -3384,13 +3597,9 @@ void Renderer::deleteRemovedElements()
*e = nullptr;
}
- for (int i=0; i<m_elementsToDelete.size(); ++i) {
- Element *e = m_elementsToDelete.at(i);
- if (e->isRenderNode)
- delete static_cast<RenderNodeElement *>(e);
- else
- m_elementAllocator.release(e);
- }
+ for (int i=0; i<m_elementsToDelete.size(); ++i)
+ releaseElement(m_elementsToDelete.at(i));
+
m_elementsToDelete.reset();
}
@@ -3398,7 +3607,7 @@ void Renderer::render()
{
// Gracefully handle the lack of a render target - some autotests may rely
// on this in odd cases.
- if (!renderTarget())
+ if (!renderTarget().rt)
return;
prepareRenderPass(&m_mainRenderPassContext);
@@ -3543,14 +3752,14 @@ void Renderer::prepareRenderPass(RenderPassContext *ctx)
if (Q_UNLIKELY(debug_render())) ctx->timeSorting = ctx->timer.restart();
- int largestVBO = 0;
- int largestIBO = 0;
+ // Set size to 0, nothing is deallocated, they will "grow" again
+ // as part of uploadBatch.
+ m_vertexUploadPool.reset();
+ m_indexUploadPool.reset();
if (Q_UNLIKELY(debug_upload())) qDebug("Uploading Opaque Batches:");
for (int i=0; i<m_opaqueBatches.size(); ++i) {
Batch *b = m_opaqueBatches.at(i);
- largestVBO = qMax(b->vbo.size, largestVBO);
- largestIBO = qMax(b->ibo.size, largestIBO);
uploadBatch(b);
}
if (Q_UNLIKELY(debug_render())) ctx->timeUploadOpaque = ctx->timer.restart();
@@ -3559,16 +3768,9 @@ void Renderer::prepareRenderPass(RenderPassContext *ctx)
for (int i=0; i<m_alphaBatches.size(); ++i) {
Batch *b = m_alphaBatches.at(i);
uploadBatch(b);
- largestVBO = qMax(b->vbo.size, largestVBO);
- largestIBO = qMax(b->ibo.size, largestIBO);
}
if (Q_UNLIKELY(debug_render())) ctx->timeUploadAlpha = ctx->timer.restart();
- if (largestVBO * 2 < m_vertexUploadPool.size())
- m_vertexUploadPool.resize(largestVBO * 2);
- if (m_context->separateIndexBuffer() && largestIBO * 2 < m_indexUploadPool.size())
- m_indexUploadPool.resize(largestIBO * 2);
-
if (Q_UNLIKELY(debug_render())) {
qDebug().nospace() << "Rendering:" << Qt::endl
<< " -> Opaque: " << qsg_countNodesInBatches(m_opaqueBatches) << " nodes in " << m_opaqueBatches.size() << " batches..." << Qt::endl
@@ -3586,7 +3788,9 @@ void Renderer::prepareRenderPass(RenderPassContext *ctx)
bool renderOpaque = !debug_noopaque();
bool renderAlpha = !debug_noalpha();
- m_pstate.viewport = QRhiViewport(viewport.x(), deviceRect().bottom() - viewport.bottom(), viewport.width(), viewport.height());
+ m_pstate.viewport =
+ QRhiViewport(viewport.x(), deviceRect().bottom() - viewport.bottom(), viewport.width(),
+ viewport.height(), VIEWPORT_MIN_DEPTH, VIEWPORT_MAX_DEPTH);
m_pstate.clearColor = clearColor();
m_pstate.dsClear = QRhiDepthStencilClearValue(1.0f, 0);
m_pstate.viewportSet = false;
@@ -3598,6 +3802,7 @@ void Renderer::prepareRenderPass(RenderPassContext *ctx)
m_gstate.blending = false;
m_gstate.cullMode = QRhiGraphicsPipeline::None;
+ m_gstate.polygonMode = QRhiGraphicsPipeline::Fill;
m_gstate.colorWrite = QRhiGraphicsPipeline::R
| QRhiGraphicsPipeline::G
| QRhiGraphicsPipeline::B
@@ -3605,7 +3810,8 @@ void Renderer::prepareRenderPass(RenderPassContext *ctx)
m_gstate.usesScissor = false;
m_gstate.stencilTest = false;
- m_gstate.sampleCount = renderTarget()->sampleCount();
+ m_gstate.sampleCount = renderTarget().rt->sampleCount();
+ m_gstate.multiViewCount = renderTarget().multiViewCount;
ctx->opaqueRenderBatches.clear();
if (Q_LIKELY(renderOpaque)) {
@@ -3664,20 +3870,21 @@ void Renderer::prepareRenderPass(RenderPassContext *ctx)
if (m_visualizer->mode() != Visualizer::VisualizeNothing)
m_visualizer->prepareVisualize();
- commandBuffer()->resourceUpdate(m_resourceUpdates);
+ renderTarget().cb->resourceUpdate(m_resourceUpdates);
m_resourceUpdates = nullptr;
}
void Renderer::beginRenderPass(RenderPassContext *)
{
- commandBuffer()->beginPass(renderTarget(), m_pstate.clearColor, m_pstate.dsClear, nullptr,
- // we cannot tell if the application will have
- // native rendering thrown in to this pass
- // (QQuickWindow::beginExternalCommands()), so
- // we have no choice but to set the flag always
- // (thus triggering using secondary command
- // buffers with Vulkan)
- QRhiCommandBuffer::ExternalContent);
+ const QSGRenderTarget &rt(renderTarget());
+ rt.cb->beginPass(rt.rt, m_pstate.clearColor, m_pstate.dsClear, nullptr,
+ // we cannot tell if the application will have
+ // native rendering thrown in to this pass
+ // (QQuickWindow::beginExternalCommands()), so
+ // we have no choice but to set the flag always
+ // (thus triggering using secondary command
+ // buffers with Vulkan)
+ QRhiCommandBuffer::ExternalContent);
if (m_renderPassRecordingCallbacks.start)
m_renderPassRecordingCallbacks.start(m_renderPassRecordingCallbacks.userData);
@@ -3698,10 +3905,12 @@ void Renderer::recordRenderPass(RenderPassContext *ctx)
ctx->valid = false;
- QRhiCommandBuffer *cb = commandBuffer();
+ QRhiCommandBuffer *cb = renderTarget().cb;
cb->debugMarkBegin(QByteArrayLiteral("Qt Quick scene render"));
- for (int i = 0, ie = ctx->opaqueRenderBatches.count(); i != ie; ++i) {
+ for (int i = 0, ie = ctx->opaqueRenderBatches.size(); i != ie; ++i) {
+ if (i == 0)
+ cb->debugMarkMsg(QByteArrayLiteral("Qt Quick opaque batches"));
PreparedRenderBatch *renderBatch = &ctx->opaqueRenderBatches[i];
if (renderBatch->batch->merged)
renderMergedBatch(renderBatch);
@@ -3709,7 +3918,13 @@ void Renderer::recordRenderPass(RenderPassContext *ctx)
renderUnmergedBatch(renderBatch);
}
- for (int i = 0, ie = ctx->alphaRenderBatches.count(); i != ie; ++i) {
+ for (int i = 0, ie = ctx->alphaRenderBatches.size(); i != ie; ++i) {
+ if (i == 0) {
+ if (m_renderMode == QSGRendererInterface::RenderMode3D)
+ cb->debugMarkMsg(QByteArrayLiteral("Qt Quick 2D-in-3D batches"));
+ else
+ cb->debugMarkMsg(QByteArrayLiteral("Qt Quick alpha batches"));
+ }
PreparedRenderBatch *renderBatch = &ctx->alphaRenderBatches[i];
if (renderBatch->batch->merged)
renderMergedBatch(renderBatch);
@@ -3720,8 +3935,15 @@ void Renderer::recordRenderPass(RenderPassContext *ctx)
}
if (m_renderMode == QSGRendererInterface::RenderMode3D) {
- // depth post-pass
- for (int i = 0, ie = ctx->alphaRenderBatches.count(); i != ie; ++i) {
+ // Depth post-pass to fill up the depth buffer in a way that it
+ // corresponds to what got rendered to the color buffer in the previous
+ // (alpha) pass. The previous pass cannot enable depth write due to Z
+ // fighting. Rather, do it separately in a dedicated color-write-off,
+ // depth-write-on pass. This enables the 3D content drawn afterwards to
+ // depth test against the 2D items' rendering.
+ for (int i = 0, ie = ctx->alphaRenderBatches.size(); i != ie; ++i) {
+ if (i == 0)
+ cb->debugMarkMsg(QByteArrayLiteral("Qt Quick 2D-in-3D depth post-pass"));
PreparedRenderBatch *renderBatch = &ctx->alphaRenderBatches[i];
if (renderBatch->batch->merged)
renderMergedBatch(renderBatch, true);
@@ -3753,7 +3975,7 @@ void Renderer::endRenderPass(RenderPassContext *)
if (m_visualizer->mode() != Visualizer::VisualizeNothing)
m_visualizer->visualize();
- commandBuffer()->endPass();
+ renderTarget().cb->endPass();
}
struct RenderNodeState : public QSGRenderNode::RenderState
@@ -3810,7 +4032,8 @@ bool Renderer::prepareRhiRenderNode(Batch *batch, PreparedRenderBatch *renderBat
}
xform = xform->parent();
}
- rd->m_matrix = &matrix;
+ rd->m_localMatrix = matrix;
+ rd->m_matrix = &rd->m_localMatrix;
QSGNode *opacity = e->renderNode->parent();
rd->m_opacity = 1.0;
@@ -3822,6 +4045,19 @@ bool Renderer::prepareRhiRenderNode(Batch *batch, PreparedRenderBatch *renderBat
opacity = opacity->parent();
}
+ rd->m_rt = renderTarget();
+
+ const int viewCount = projectionMatrixCount();
+ rd->m_projectionMatrix.resize(viewCount);
+ for (int viewIndex = 0; viewIndex < viewCount; ++viewIndex)
+ rd->m_projectionMatrix[viewIndex] = projectionMatrix(viewIndex);
+
+ if (useDepthBuffer()) {
+ // this cannot be multiview
+ rd->m_projectionMatrix[0](2, 2) = m_zRange;
+ rd->m_projectionMatrix[0](2, 3) = calculateElementZOrder(e, m_zRange);
+ }
+
e->renderNode->prepare();
renderBatch->batch = batch;
@@ -3838,14 +4074,10 @@ void Renderer::renderRhiRenderNode(const Batch *batch)
RenderNodeElement *e = static_cast<RenderNodeElement *>(batch->first);
QSGRenderNodePrivate *rd = QSGRenderNodePrivate::get(e->renderNode);
- QMatrix4x4 pm = projectionMatrix();
- if (useDepthBuffer()) {
- pm(2, 2) = m_zRange;
- pm(2, 3) = 1.0f - e->order * m_zRange;
- }
-
RenderNodeState state;
- state.m_projectionMatrix = &pm;
+ // Expose only the first matrix through the state object, the rest are
+ // queriable through the QSGRenderNode getters anyway.
+ state.m_projectionMatrix = &rd->m_projectionMatrix[0];
const std::array<int, 4> scissor = batch->clipState.scissor.scissor();
state.m_scissorRect = QRect(scissor[0], scissor[1], scissor[2], scissor[3]);
state.m_stencilValue = batch->clipState.stencilRef;
@@ -3854,7 +4086,7 @@ void Renderer::renderRhiRenderNode(const Batch *batch)
const QSGRenderNode::StateFlags changes = e->renderNode->changedStates();
- QRhiCommandBuffer *cb = commandBuffer();
+ QRhiCommandBuffer *cb = renderTarget().cb;
const bool needsExternal = !e->renderNode->flags().testFlag(QSGRenderNode::NoExternalRendering);
if (needsExternal)
cb->beginExternal();
@@ -3899,22 +4131,6 @@ bool Renderer::hasVisualizationModeWithContinuousUpdate() const
return m_visualizer->mode() == Visualizer::VisualizeOverdraw;
}
-void Renderer::invalidatePipelineCacheDependency(QRhiRenderPassDescriptor *rpDesc)
-{
- if (!rpDesc)
- return;
-
- for (auto it = m_shaderManager->pipelineCache.begin(); it != m_shaderManager->pipelineCache.end(); ) {
- if (it.key().compatibleRenderPassDescriptor == rpDesc) {
- QRhiGraphicsPipeline *ps = it.value();
- it = m_shaderManager->pipelineCache.erase(it);
- ps->deleteLater(); // QRhi takes care of it in endFrame()
- } else {
- ++it;
- }
- }
-}
-
bool operator==(const GraphicsState &a, const GraphicsState &b) noexcept
{
return a.depthTest == b.depthTest
@@ -3923,13 +4139,17 @@ bool operator==(const GraphicsState &a, const GraphicsState &b) noexcept
&& a.blending == b.blending
&& a.srcColor == b.srcColor
&& a.dstColor == b.dstColor
+ && a.srcAlpha == b.srcAlpha
+ && a.dstAlpha == b.dstAlpha
&& a.colorWrite == b.colorWrite
&& a.cullMode == b.cullMode
&& a.usesScissor == b.usesScissor
&& a.stencilTest == b.stencilTest
&& a.sampleCount == b.sampleCount
&& a.drawMode == b.drawMode
- && a.lineWidth == b.lineWidth;
+ && a.lineWidth == b.lineWidth
+ && a.polygonMode == b.polygonMode
+ && a.multiViewCount == b.multiViewCount;
}
bool operator!=(const GraphicsState &a, const GraphicsState &b) noexcept
@@ -3949,15 +4169,16 @@ size_t qHash(const GraphicsState &s, size_t seed) noexcept
+ s.cullMode
+ s.usesScissor
+ s.stencilTest
- + s.sampleCount;
+ + s.sampleCount
+ + s.multiViewCount;
}
bool operator==(const GraphicsPipelineStateKey &a, const GraphicsPipelineStateKey &b) noexcept
{
return a.state == b.state
- && a.sms->programRhi.program == b.sms->programRhi.program
- && a.compatibleRenderPassDescriptor->isCompatible(b.compatibleRenderPassDescriptor)
- && a.layoutCompatibleSrb->isLayoutCompatible(b.layoutCompatibleSrb);
+ && a.sms->materialShader == b.sms->materialShader
+ && a.renderTargetDescription == b.renderTargetDescription
+ && a.srbLayoutDescription == b.srbLayoutDescription;
}
bool operator!=(const GraphicsPipelineStateKey &a, const GraphicsPipelineStateKey &b) noexcept
@@ -3967,8 +4188,27 @@ bool operator!=(const GraphicsPipelineStateKey &a, const GraphicsPipelineStateKe
size_t qHash(const GraphicsPipelineStateKey &k, size_t seed) noexcept
{
- // no srb and rp included due to their special comparison semantics and lack of hash keys
- return qHash(k.state, seed) + qHash(k.sms->programRhi.program, seed);
+ return qHash(k.state, seed)
+ ^ qHash(k.sms->materialShader)
+ ^ k.extra.renderTargetDescriptionHash
+ ^ k.extra.srbLayoutDescriptionHash;
+}
+
+bool operator==(const ShaderKey &a, const ShaderKey &b) noexcept
+{
+ return a.type == b.type
+ && a.renderMode == b.renderMode
+ && a.multiViewCount == b.multiViewCount;
+}
+
+bool operator!=(const ShaderKey &a, const ShaderKey &b) noexcept
+{
+ return !(a == b);
+}
+
+size_t qHash(const ShaderKey &k, size_t seed) noexcept
+{
+ return qHash(k.type, seed) ^ int(k.renderMode) ^ k.multiViewCount;
}
Visualizer::Visualizer(Renderer *renderer)