diff options
Diffstat (limited to 'src/quick/scenegraph/coreapi/qsgbatchrenderer.cpp')
-rw-r--r-- | src/quick/scenegraph/coreapi/qsgbatchrenderer.cpp | 924 |
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 = ± + // 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) |