aboutsummaryrefslogtreecommitdiffstats
path: root/src/quick/scenegraph
diff options
context:
space:
mode:
Diffstat (limited to 'src/quick/scenegraph')
-rw-r--r--src/quick/scenegraph/coreapi/qsgbatchrenderer.cpp407
-rw-r--r--src/quick/scenegraph/coreapi/qsgbatchrenderer_p.h49
-rw-r--r--src/quick/scenegraph/coreapi/qsgopenglvisualizer.cpp365
-rw-r--r--src/quick/scenegraph/coreapi/qsgopenglvisualizer_p.h91
-rw-r--r--src/quick/scenegraph/coreapi/qsgrenderer_p.h1
-rw-r--r--src/quick/scenegraph/coreapi/qsgrhivisualizer.cpp929
-rw-r--r--src/quick/scenegraph/coreapi/qsgrhivisualizer_p.h233
-rw-r--r--src/quick/scenegraph/qsgdefaultglyphnode.cpp2
-rw-r--r--src/quick/scenegraph/qsgdefaultglyphnode_p.cpp31
-rw-r--r--src/quick/scenegraph/qsgdefaultglyphnode_p_p.h7
-rw-r--r--src/quick/scenegraph/qsgrenderloop.cpp14
-rw-r--r--src/quick/scenegraph/qsgrhisupport.cpp11
-rw-r--r--src/quick/scenegraph/qsgrhitextureglyphcache.cpp5
-rw-r--r--src/quick/scenegraph/qsgrhitextureglyphcache_p.h3
-rw-r--r--src/quick/scenegraph/qsgthreadedrenderloop.cpp32
-rw-r--r--src/quick/scenegraph/scenegraph.pri6
-rw-r--r--src/quick/scenegraph/scenegraph.qrc3
-rwxr-xr-xsrc/quick/scenegraph/shaders_ng/compile.bat2
-rw-r--r--src/quick/scenegraph/shaders_ng/visualization.frag19
-rw-r--r--src/quick/scenegraph/shaders_ng/visualization.frag.qsbbin0 -> 1877 bytes
-rw-r--r--src/quick/scenegraph/shaders_ng/visualization.vert28
-rw-r--r--src/quick/scenegraph/shaders_ng/visualization.vert.qsbbin0 -> 2030 bytes
-rw-r--r--src/quick/scenegraph/util/qsgdefaultpainternode_p.h3
23 files changed, 1856 insertions, 385 deletions
diff --git a/src/quick/scenegraph/coreapi/qsgbatchrenderer.cpp b/src/quick/scenegraph/coreapi/qsgbatchrenderer.cpp
index b5437ffb7e..2cd9ee689b 100644
--- a/src/quick/scenegraph/coreapi/qsgbatchrenderer.cpp
+++ b/src/quick/scenegraph/coreapi/qsgbatchrenderer.cpp
@@ -59,6 +59,9 @@
#include <private/qquickprofiler_p.h>
#include "qsgmaterialrhishader_p.h"
+#include "qsgopenglvisualizer_p.h"
+#include "qsgrhivisualizer_p.h"
+
#include <algorithm>
#ifndef GL_DOUBLE
@@ -145,7 +148,7 @@ static inline uint aligned(uint v, uint byteAlign)
return (v + byteAlign - 1) & ~(byteAlign - 1);
}
-static inline QRhiVertexInputAttribute::Format vertexInputFormat(const QSGGeometry::Attribute &a)
+QRhiVertexInputAttribute::Format qsg_vertexInputFormat(const QSGGeometry::Attribute &a)
{
switch (a.type) {
case QSGGeometry::FloatType:
@@ -193,7 +196,7 @@ static QRhiVertexInputLayout calculateVertexInputLayout(const QSGMaterialRhiShad
qWarning("Vertex input %d is present in material but not in shader. This is wrong.",
a.position);
}
- inputAttributes.append(QRhiVertexInputAttribute(VERTEX_BUFFER_BINDING, a.position, vertexInputFormat(a), offset));
+ inputAttributes.append(QRhiVertexInputAttribute(VERTEX_BUFFER_BINDING, a.position, qsg_vertexInputFormat(a), offset));
offset += a.tupleSize * size_of_type(a.type);
}
if (batchable) {
@@ -215,7 +218,7 @@ static QRhiVertexInputLayout calculateVertexInputLayout(const QSGMaterialRhiShad
return inputLayout;
}
-static inline QRhiCommandBuffer::IndexFormat indexFormat(const QSGGeometry *geometry)
+QRhiCommandBuffer::IndexFormat qsg_indexFormat(const QSGGeometry *geometry)
{
switch (geometry->indexType()) {
case QSGGeometry::UnsignedShortType:
@@ -230,7 +233,7 @@ static inline QRhiCommandBuffer::IndexFormat indexFormat(const QSGGeometry *geom
}
}
-static inline QRhiGraphicsPipeline::Topology gpTopology(int geomDrawMode)
+QRhiGraphicsPipeline::Topology qsg_topology(int geomDrawMode)
{
QRhiGraphicsPipeline::Topology topology = QRhiGraphicsPipeline::Triangles;
switch (geomDrawMode) {
@@ -501,8 +504,8 @@ void Updater::updateStates(QSGNode *n)
qDebug(" - forceupdate");
}
- if (Q_UNLIKELY(renderer->m_visualizeMode == Renderer::VisualizeChanges))
- renderer->visualizeChangesPrepare(sn);
+ if (Q_UNLIKELY(renderer->m_visualizer->mode() == Visualizer::VisualizeChanges))
+ renderer->m_visualizer->visualizeChangesPrepare(sn);
visitNode(sn);
}
@@ -975,7 +978,6 @@ Renderer::Renderer(QSGDefaultRenderContext *ctx)
, m_vertexUploadPool(256)
, m_indexUploadPool(64)
, m_vao(nullptr)
- , m_visualizeMode(VisualizeNothing)
{
m_rhi = m_context->rhi();
if (m_rhi) {
@@ -983,9 +985,11 @@ Renderer::Renderer(QSGDefaultRenderContext *ctx)
m_uint32IndexForRhi = !m_rhi->isFeatureSupported(QRhi::NonFourAlignedEffectiveIndexBufferOffset);
if (qEnvironmentVariableIntValue("QSG_RHI_UINT32_INDEX"))
m_uint32IndexForRhi = true;
+ m_visualizer = new RhiVisualizer(this);
} else {
initializeOpenGLFunctions();
m_uint32IndexForRhi = false;
+ m_visualizer = new OpenGLVisualizer(this);
}
setNodeUpdater(new Updater(this));
@@ -1088,6 +1092,8 @@ Renderer::~Renderer()
}
destroyGraphicsResources();
+
+ delete m_visualizer;
}
void Renderer::destroyGraphicsResources()
@@ -1104,6 +1110,8 @@ void Renderer::destroyGraphicsResources()
m_stencilClipCommon.reset();
delete m_dummyTexture;
+
+ m_visualizer->releaseResources();
}
void Renderer::releaseCachedResources()
@@ -1138,7 +1146,7 @@ void Renderer::invalidateAndRecycleBatch(Batch *b)
*/
void Renderer::map(Buffer *buffer, int byteSize, bool isIndexBuf)
{
- if (!m_context->hasBrokenIndexBufferObjects() && m_visualizeMode == VisualizeNothing) {
+ if (!m_context->hasBrokenIndexBufferObjects() && 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
@@ -1194,7 +1202,7 @@ void Renderer::unmap(Buffer *buffer, bool isIndexBuf)
m_resourceUpdates->updateDynamicBuffer(buffer->buf, 0, buffer->size,
QByteArray::fromRawData(buffer->data, buffer->size));
}
- if (m_visualizeMode == VisualizeNothing)
+ if (m_visualizer->mode() == Visualizer::VisualizeNothing)
buffer->data = nullptr;
} else {
if (buffer->id == 0)
@@ -1202,7 +1210,7 @@ void Renderer::unmap(Buffer *buffer, bool isIndexBuf)
GLenum target = isIndexBuf ? GL_ELEMENT_ARRAY_BUFFER : GL_ARRAY_BUFFER;
glBindBuffer(target, buffer->id);
glBufferData(target, buffer->size, buffer->data, m_bufferStrategy);
- if (!m_context->hasBrokenIndexBufferObjects() && m_visualizeMode == VisualizeNothing)
+ if (!m_context->hasBrokenIndexBufferObjects() && m_visualizer->mode() == Visualizer::VisualizeNothing)
buffer->data = nullptr;
}
}
@@ -2096,7 +2104,7 @@ void Renderer::uploadMergedElement(Element *e, int vaOffset, char **vertexData,
*indexCount += iCount;
}
-static QMatrix4x4 qsg_matrixForRoot(Node *node)
+QMatrix4x4 qsg_matrixForRoot(Node *node)
{
if (node->type() == QSGNode::TransformNodeType)
return static_cast<QSGTransformNode *>(node->sgNode)->combinedMatrix();
@@ -2830,31 +2838,31 @@ void Renderer::updateClipState(const QSGClipNode *clipList, Batch *batch) // RHI
if (firstStencilClipInBatch) {
m_stencilClipCommon.inputLayout.setBindings({ QRhiVertexInputBinding(g->sizeOfVertex()) });
- m_stencilClipCommon.inputLayout.setAttributes({ QRhiVertexInputAttribute(0, 0, vertexInputFormat(*a), 0) });
- m_stencilClipCommon.topology = gpTopology(g->drawingMode());
+ m_stencilClipCommon.inputLayout.setAttributes({ QRhiVertexInputAttribute(0, 0, qsg_vertexInputFormat(*a), 0) });
+ m_stencilClipCommon.topology = qsg_topology(g->drawingMode());
}
#ifndef QT_NO_DEBUG
else {
- if (gpTopology(g->drawingMode()) != m_stencilClipCommon.topology)
+ if (qsg_topology(g->drawingMode()) != m_stencilClipCommon.topology)
qWarning("updateClipState: Clip list entries have different primitive topologies, this is not currently supported.");
- if (vertexInputFormat(*a) != m_stencilClipCommon.inputLayout.attributes().first().format())
+ if (qsg_vertexInputFormat(*a) != m_stencilClipCommon.inputLayout.attributes().first().format())
qWarning("updateClipState: Clip list entries have different vertex input layouts, this is must not happen.");
}
#endif
drawCall.vbufOffset = aligned(vOffset, 4);
const int vertexByteSize = g->sizeOfVertex() * g->vertexCount();
- vOffset += vertexByteSize;
+ vOffset = drawCall.vbufOffset + vertexByteSize;
int indexByteSize = 0;
if (g->indexCount()) {
drawCall.ibufOffset = aligned(iOffset, 4);
indexByteSize = g->sizeOfIndex() * g->indexCount();
- iOffset += indexByteSize;
+ iOffset = drawCall.ibufOffset + indexByteSize;
}
drawCall.ubufOffset = aligned(uOffset, m_ubufAlignment);
- uOffset += StencilClipUbufSize;
+ uOffset = drawCall.ubufOffset + StencilClipUbufSize;
QMatrix4x4 matrixYUpNDC = m_current_projection_matrix;
if (clip->matrix())
@@ -2874,7 +2882,7 @@ void Renderer::updateClipState(const QSGClipNode *clipList, Batch *batch) // RHI
drawCall.vertexCount = g->vertexCount();
drawCall.indexCount = g->indexCount();
- drawCall.indexFormat = indexFormat(g);
+ drawCall.indexFormat = qsg_indexFormat(g);
batch->stencilClipState.drawCalls.add(drawCall);
}
@@ -2919,13 +2927,13 @@ void Renderer::enqueueStencilDraw(const Batch *batch) // RHI only
QRhiCommandBuffer::DynamicOffset ubufOffset(0, drawCall.ubufOffset);
if (i == 0) {
cb->setGraphicsPipeline(m_stencilClipCommon.replacePs);
- cb->setShaderResources(srb, 1, &ubufOffset);
cb->setViewport(m_pstate.viewport);
} else if (i == 1) {
cb->setGraphicsPipeline(m_stencilClipCommon.incrPs);
- cb->setShaderResources(srb, 1, &ubufOffset);
cb->setViewport(m_pstate.viewport);
}
+ // else incrPs is already bound
+ cb->setShaderResources(srb, 1, &ubufOffset);
cb->setStencilRef(drawCall.stencilRef);
const QRhiCommandBuffer::VertexInput vbufBinding(batch->stencilClipState.vbuf, drawCall.vbufOffset);
if (drawCall.indexCount) {
@@ -3255,7 +3263,7 @@ bool Renderer::ensurePipelineState(Element *e, const ShaderManager::Shader *sms)
flags |= QRhiGraphicsPipeline::UsesStencilRef;
ps->setFlags(flags);
- ps->setTopology(gpTopology(m_gstate.drawMode));
+ ps->setTopology(qsg_topology(m_gstate.drawMode));
ps->setCullMode(m_gstate.cullMode);
QRhiGraphicsPipeline::TargetBlend blend;
@@ -4089,6 +4097,9 @@ void Renderer::renderBatches()
}
}
+ if (m_visualizer->mode() != Visualizer::VisualizeNothing)
+ m_visualizer->prepareVisualize();
+
QRhiCommandBuffer *cb = commandBuffer();
cb->beginPass(renderTarget(), m_pstate.clearColor, m_pstate.dsClear, m_resourceUpdates);
m_resourceUpdates = nullptr;
@@ -4120,7 +4131,8 @@ void Renderer::renderBatches()
if (m_renderPassRecordingCallbacks.end)
m_renderPassRecordingCallbacks.end(m_renderPassRecordingCallbacks.userData);
- cb->endPass();
+ if (m_visualizer->mode() == Visualizer::VisualizeNothing)
+ cb->endPass();
}
}
@@ -4314,13 +4326,16 @@ void Renderer::render()
m_renderOrderRebuildLower = -1;
m_renderOrderRebuildUpper = -1;
- if (!m_rhi) {
- if (m_visualizeMode != VisualizeNothing)
- visualize();
+ if (m_visualizer->mode() != Visualizer::VisualizeNothing)
+ m_visualizer->visualize();
+ if (!m_rhi) {
if (m_vao)
m_vao->release();
} else {
+ if (m_visualizer->mode() != Visualizer::VisualizeNothing)
+ commandBuffer()->endPass();
+
if (m_resourceUpdates) {
m_resourceUpdates->release();
m_resourceUpdates = nullptr;
@@ -4460,311 +4475,23 @@ void Renderer::renderRenderNode(Batch *batch)
glBindFramebuffer(GL_FRAMEBUFFER, prevFbo);
}
-class VisualizeShader : public QOpenGLShaderProgram
-{
-public:
- int color;
- int matrix;
- int rotation;
- int pattern;
- int projection;
-};
-
-void Renderer::visualizeDrawGeometry(const QSGGeometry *g)
-{
- if (g->attributeCount() < 1)
- return;
- const QSGGeometry::Attribute *a = g->attributes();
- glVertexAttribPointer(0, a->tupleSize, a->type, false, g->sizeOfVertex(), g->vertexData());
- if (g->indexCount())
- glDrawElements(g->drawingMode(), g->indexCount(), g->indexType(), g->indexData());
- else
- glDrawArrays(g->drawingMode(), 0, g->vertexCount());
-
-}
-
-void Renderer::visualizeBatch(Batch *b)
-{
- VisualizeShader *shader = static_cast<VisualizeShader *>(m_shaderManager->visualizeProgram);
-
- if (b->positionAttribute != 0)
- return;
-
- QSGGeometryNode *gn = b->first->node;
- QSGGeometry *g = gn->geometry();
- const QSGGeometry::Attribute &a = g->attributes()[b->positionAttribute];
-
- glBindBuffer(GL_ARRAY_BUFFER, b->vbo.id);
-
- QMatrix4x4 matrix(m_current_projection_matrix);
- if (b->root)
- matrix = matrix * qsg_matrixForRoot(b->root);
-
- shader->setUniformValue(shader->pattern, float(b->merged ? 0 : 1));
-
- QColor color = QColor::fromHsvF((rand() & 1023) / 1023.0, 1.0, 1.0);
- float cr = color.redF();
- float cg = color.greenF();
- float cb = color.blueF();
- shader->setUniformValue(shader->color, cr, cg, cb, 1.0);
-
- if (b->merged) {
- shader->setUniformValue(shader->matrix, matrix);
- const char *dataStart = m_context->separateIndexBuffer() ? b->ibo.data : b->vbo.data;
- for (int ds=0; ds<b->drawSets.size(); ++ds) {
- const DrawSet &set = b->drawSets.at(ds);
- glVertexAttribPointer(a.position, 2, a.type, false, g->sizeOfVertex(), (void *) (qintptr) (set.vertices));
- glDrawElements(g->drawingMode(), set.indexCount, GL_UNSIGNED_SHORT,
- (void *)(qintptr)(dataStart + set.indices));
- }
- } else {
- Element *e = b->first;
- int offset = 0;
- while (e) {
- gn = e->node;
- g = gn->geometry();
- shader->setUniformValue(shader->matrix, matrix * *gn->matrix());
- glVertexAttribPointer(a.position, a.tupleSize, a.type, false, g->sizeOfVertex(), (void *) (qintptr) offset);
- if (g->indexCount())
- glDrawElements(g->drawingMode(), g->indexCount(), g->indexType(), g->indexData());
- else
- glDrawArrays(g->drawingMode(), 0, g->vertexCount());
- offset += g->sizeOfVertex() * g->vertexCount();
- e = e->nextInBatch;
- }
- }
-}
-
-
-
-
-void Renderer::visualizeClipping(QSGNode *node)
-{
- if (node->type() == QSGNode::ClipNodeType) {
- VisualizeShader *shader = static_cast<VisualizeShader *>(m_shaderManager->visualizeProgram);
- QSGClipNode *clipNode = static_cast<QSGClipNode *>(node);
- QMatrix4x4 matrix = m_current_projection_matrix;
- if (clipNode->matrix())
- matrix = matrix * *clipNode->matrix();
- shader->setUniformValue(shader->matrix, matrix);
- visualizeDrawGeometry(clipNode->geometry());
- }
-
- QSGNODE_TRAVERSE(node) {
- visualizeClipping(child);
- }
-}
-
-#define QSGNODE_DIRTY_PARENT (QSGNode::DirtyNodeAdded \
- | QSGNode::DirtyOpacity \
- | QSGNode::DirtyMatrix \
- | QSGNode::DirtyNodeRemoved)
-
-void Renderer::visualizeChangesPrepare(Node *n, uint parentChanges)
-{
- uint childDirty = (parentChanges | n->dirtyState) & QSGNODE_DIRTY_PARENT;
- uint selfDirty = n->dirtyState | parentChanges;
- if (n->type() == QSGNode::GeometryNodeType && selfDirty != 0)
- m_visualizeChanceSet.insert(n, selfDirty);
- SHADOWNODE_TRAVERSE(n) {
- visualizeChangesPrepare(child, childDirty);
- }
-}
-
-void Renderer::visualizeChanges(Node *n)
-{
-
- if (n->type() == QSGNode::GeometryNodeType && n->element()->batch && m_visualizeChanceSet.contains(n)) {
- uint dirty = m_visualizeChanceSet.value(n);
- bool tinted = (dirty & QSGNODE_DIRTY_PARENT) != 0;
-
- VisualizeShader *shader = static_cast<VisualizeShader *>(m_shaderManager->visualizeProgram);
- QColor color = QColor::fromHsvF((rand() & 1023) / 1023.0, 0.3, 1.0);
- float ca = 0.5;
- float cr = color.redF() * ca;
- float cg = color.greenF() * ca;
- float cb = color.blueF() * ca;
- shader->setUniformValue(shader->color, cr, cg, cb, ca);
- shader->setUniformValue(shader->pattern, float(tinted ? 0.5 : 0));
-
- QSGGeometryNode *gn = static_cast<QSGGeometryNode *>(n->sgNode);
-
- QMatrix4x4 matrix = m_current_projection_matrix;
- if (n->element()->batch->root)
- matrix = matrix * qsg_matrixForRoot(n->element()->batch->root);
- matrix = matrix * *gn->matrix();
- shader->setUniformValue(shader->matrix, matrix);
- visualizeDrawGeometry(gn->geometry());
-
- // This is because many changes don't propegate their dirty state to the
- // parent so the node updater will not unset these states. They are
- // not used for anything so, unsetting it should have no side effects.
- n->dirtyState = nullptr;
- }
-
- SHADOWNODE_TRAVERSE(n) {
- visualizeChanges(child);
- }
-}
-
-void Renderer::visualizeOverdraw_helper(Node *node)
-{
- if (node->type() == QSGNode::GeometryNodeType && node->element()->batch) {
- VisualizeShader *shader = static_cast<VisualizeShader *>(m_shaderManager->visualizeProgram);
- QSGGeometryNode *gn = static_cast<QSGGeometryNode *>(node->sgNode);
-
- QMatrix4x4 matrix = m_current_projection_matrix;
- matrix(2, 2) = m_zRange;
- matrix(2, 3) = 1.0f - node->element()->order * m_zRange;
-
- if (node->element()->batch->root)
- matrix = matrix * qsg_matrixForRoot(node->element()->batch->root);
- matrix = matrix * *gn->matrix();
- shader->setUniformValue(shader->matrix, matrix);
-
- QColor color = node->element()->batch->isOpaque ? QColor::fromRgbF(0.3, 1.0, 0.3) : QColor::fromRgbF(1.0, 0.3, 0.3);
- float ca = 0.33f;
- shader->setUniformValue(shader->color, color.redF() * ca, color.greenF() * ca, color.blueF() * ca, ca);
-
- visualizeDrawGeometry(gn->geometry());
- }
-
- SHADOWNODE_TRAVERSE(node) {
- visualizeOverdraw_helper(child);
- }
-}
-
-void Renderer::visualizeOverdraw()
-{
- VisualizeShader *shader = static_cast<VisualizeShader *>(m_shaderManager->visualizeProgram);
- shader->setUniformValue(shader->color, 0.5f, 0.5f, 1.0f, 1.0f);
- shader->setUniformValue(shader->projection, 1);
-
- glBlendFunc(GL_ONE, GL_ONE);
-
- static float step = 0;
- step += static_cast<float>(M_PI * 2 / 1000.);
- if (step > M_PI * 2)
- step = 0;
- float angle = 80.0 * std::sin(step);
-
- QMatrix4x4 xrot; xrot.rotate(20, 1, 0, 0);
- QMatrix4x4 zrot; zrot.rotate(angle, 0, 0, 1);
- QMatrix4x4 tx; tx.translate(0, 0, 1);
-
- QMatrix4x4 m;
-
-// m.rotate(180, 0, 1, 0);
-
- m.translate(0, 0.5, 4);
- m.scale(2, 2, 1);
-
- m.rotate(-30, 1, 0, 0);
- m.rotate(angle, 0, 1, 0);
- m.translate(0, 0, -1);
-
- shader->setUniformValue(shader->rotation, m);
-
- float box[] = {
- // lower
- -1, 1, 0, 1, 1, 0,
- -1, 1, 0, -1, -1, 0,
- 1, 1, 0, 1, -1, 0,
- -1, -1, 0, 1, -1, 0,
-
- // upper
- -1, 1, 1, 1, 1, 1,
- -1, 1, 1, -1, -1, 1,
- 1, 1, 1, 1, -1, 1,
- -1, -1, 1, 1, -1, 1,
-
- // sides
- -1, -1, 0, -1, -1, 1,
- 1, -1, 0, 1, -1, 1,
- -1, 1, 0, -1, 1, 1,
- 1, 1, 0, 1, 1, 1
- };
- glVertexAttribPointer(0, 3, GL_FLOAT, false, 0, box);
- glLineWidth(2);
- glDrawArrays(GL_LINES, 0, 24);
-
- visualizeOverdraw_helper(m_nodes.value(rootNode()));
-
- // Animate the view...
- QSurface *surface = m_context->openglContext()->surface();
- if (surface->surfaceClass() == QSurface::Window)
- if (QQuickWindow *window = qobject_cast<QQuickWindow *>(static_cast<QWindow *>(surface)))
- window->update();
-}
-
void Renderer::setCustomRenderMode(const QByteArray &mode)
{
- if (mode.isEmpty()) m_visualizeMode = VisualizeNothing;
- else if (mode == "clip") m_visualizeMode = VisualizeClipping;
- else if (mode == "overdraw") m_visualizeMode = VisualizeOverdraw;
- else if (mode == "batches") m_visualizeMode = VisualizeBatches;
- else if (mode == "changes") m_visualizeMode = VisualizeChanges;
+ if (mode.isEmpty())
+ m_visualizer->setMode(Visualizer::VisualizeNothing);
+ else if (mode == "clip")
+ m_visualizer->setMode(Visualizer::VisualizeClipping);
+ else if (mode == "overdraw")
+ m_visualizer->setMode(Visualizer::VisualizeOverdraw);
+ else if (mode == "batches")
+ m_visualizer->setMode(Visualizer::VisualizeBatches);
+ else if (mode == "changes")
+ m_visualizer->setMode(Visualizer::VisualizeChanges);
}
-void Renderer::visualize()
+bool Renderer::hasCustomRenderModeWithContinuousUpdate() const
{
- if (!m_shaderManager->visualizeProgram) {
- VisualizeShader *prog = new VisualizeShader();
- QSGShaderSourceBuilder::initializeProgramFromFiles(
- prog,
- QStringLiteral(":/qt-project.org/scenegraph/shaders/visualization.vert"),
- QStringLiteral(":/qt-project.org/scenegraph/shaders/visualization.frag"));
- prog->bindAttributeLocation("v", 0);
- prog->link();
- prog->bind();
- prog->color = prog->uniformLocation("color");
- prog->pattern = prog->uniformLocation("pattern");
- prog->projection = prog->uniformLocation("projection");
- prog->matrix = prog->uniformLocation("matrix");
- prog->rotation = prog->uniformLocation("rotation");
- m_shaderManager->visualizeProgram = prog;
- } else {
- m_shaderManager->visualizeProgram->bind();
- }
- VisualizeShader *shader = static_cast<VisualizeShader *>(m_shaderManager->visualizeProgram);
-
- glDisable(GL_DEPTH_TEST);
- glEnable(GL_BLEND);
- glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
- glEnableVertexAttribArray(0);
-
- // Blacken out the actual rendered content...
- float bgOpacity = 0.8f;
- if (m_visualizeMode == VisualizeBatches)
- bgOpacity = 1.0;
- float v[] = { -1, 1, 1, 1, -1, -1, 1, -1 };
- shader->setUniformValue(shader->color, 0.0f, 0.0f, 0.0f, bgOpacity);
- shader->setUniformValue(shader->matrix, QMatrix4x4());
- shader->setUniformValue(shader->rotation, QMatrix4x4());
- shader->setUniformValue(shader->pattern, 0.0f);
- shader->setUniformValue(shader->projection, false);
- glVertexAttribPointer(0, 2, GL_FLOAT, false, 0, v);
- glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
-
- if (m_visualizeMode == VisualizeBatches) {
- srand(0); // To force random colors to be roughly the same every time..
- for (int i=0; i<m_opaqueBatches.size(); ++i) visualizeBatch(m_opaqueBatches.at(i));
- for (int i=0; i<m_alphaBatches.size(); ++i) visualizeBatch(m_alphaBatches.at(i));
- } else if (m_visualizeMode == VisualizeClipping) {
- shader->setUniformValue(shader->pattern, 0.5f);
- shader->setUniformValue(shader->color, 0.2f, 0.0f, 0.0f, 0.2f);
- visualizeClipping(rootNode());
- } else if (m_visualizeMode == VisualizeChanges) {
- visualizeChanges(m_nodes.value(rootNode()));
- m_visualizeChanceSet.clear();
- } else if (m_visualizeMode == VisualizeOverdraw) {
- visualizeOverdraw();
- }
-
- // Reset state back to defaults..
- glDisable(GL_BLEND);
- glDisableVertexAttribArray(0);
- shader->release();
+ return m_visualizer->mode() == Visualizer::VisualizeOverdraw;
}
bool operator==(const GraphicsState &a, const GraphicsState &b) Q_DECL_NOTHROW
@@ -4822,8 +4549,34 @@ uint qHash(const GraphicsPipelineStateKey &k, uint seed) Q_DECL_NOTHROW
return qHash(k.state, seed) + qHash(k.sms->programRhi.program, seed) + qHash(k.rpDesc, seed);
}
-QT_END_NAMESPACE
+Visualizer::Visualizer(Renderer *renderer)
+ : m_renderer(renderer),
+ m_visualizeMode(VisualizeNothing)
+{
+}
+
+Visualizer::~Visualizer()
+{
+}
+
+#define QSGNODE_DIRTY_PARENT (QSGNode::DirtyNodeAdded \
+ | QSGNode::DirtyOpacity \
+ | QSGNode::DirtyMatrix \
+ | QSGNode::DirtyNodeRemoved)
+void Visualizer::visualizeChangesPrepare(Node *n, uint parentChanges)
+{
+ uint childDirty = (parentChanges | n->dirtyState) & QSGNODE_DIRTY_PARENT;
+ uint selfDirty = n->dirtyState | parentChanges;
+ if (n->type() == QSGNode::GeometryNodeType && selfDirty != 0)
+ m_visualizeChangeSet.insert(n, selfDirty);
+ SHADOWNODE_TRAVERSE(n) {
+ visualizeChangesPrepare(child, childDirty);
+ }
}
+} // namespace QSGBatchRenderer
+
+QT_END_NAMESPACE
+
#include "moc_qsgbatchrenderer_p.cpp"
diff --git a/src/quick/scenegraph/coreapi/qsgbatchrenderer_p.h b/src/quick/scenegraph/coreapi/qsgbatchrenderer_p.h
index 9dec203e73..ea9dab244f 100644
--- a/src/quick/scenegraph/coreapi/qsgbatchrenderer_p.h
+++ b/src/quick/scenegraph/coreapi/qsgbatchrenderer_p.h
@@ -648,7 +648,7 @@ public:
float lastOpacity;
};
- ShaderManager(QSGDefaultRenderContext *ctx) : visualizeProgram(nullptr), blitProgram(nullptr), context(ctx) { }
+ ShaderManager(QSGDefaultRenderContext *ctx) : blitProgram(nullptr), context(ctx) { }
~ShaderManager() {
qDeleteAll(rewrittenShaders);
qDeleteAll(stockShaders);
@@ -665,8 +665,6 @@ public:
Shader *prepareMaterial(QSGMaterial *material, bool enableRhiShaders = false, const QSGGeometry *geometry = nullptr);
Shader *prepareMaterialNoRewrite(QSGMaterial *material, bool enableRhiShaders = false, const QSGGeometry *geometry = nullptr);
- QOpenGLShaderProgram *visualizeProgram;
-
private:
QHash<QSGMaterialType *, Shader *> rewrittenShaders;
QHash<QSGMaterialType *, Shader *> stockShaders;
@@ -719,12 +717,9 @@ struct RenderPassState
bool scissorSet;
};
-class Q_QUICK_PRIVATE_EXPORT Renderer : public QSGRenderer, public QOpenGLFunctions
+class Visualizer
{
public:
- Renderer(QSGDefaultRenderContext *);
- ~Renderer();
-
enum VisualizeMode {
VisualizeNothing,
VisualizeBatches,
@@ -733,6 +728,30 @@ public:
VisualizeOverdraw
};
+ Visualizer(Renderer *renderer);
+ virtual ~Visualizer();
+
+ VisualizeMode mode() const { return m_visualizeMode; }
+ void setMode(VisualizeMode mode) { m_visualizeMode = mode; }
+
+ virtual void visualizeChangesPrepare(Node *n, uint parentChanges = 0);
+ virtual void prepareVisualize() = 0;
+ virtual void visualize() = 0;
+
+ virtual void releaseResources() = 0;
+
+protected:
+ Renderer *m_renderer;
+ VisualizeMode m_visualizeMode;
+ QHash<Node *, uint> m_visualizeChangeSet;
+};
+
+class Q_QUICK_PRIVATE_EXPORT Renderer : public QSGRenderer, public QOpenGLFunctions
+{
+public:
+ Renderer(QSGDefaultRenderContext *);
+ ~Renderer();
+
protected:
void nodeChanged(QSGNode *node, QSGNode::DirtyState state) override;
void render() override;
@@ -747,6 +766,8 @@ private:
};
friend class Updater;
+ friend class OpenGLVisualizer;
+ friend class RhiVisualizer;
void destroyGraphicsResources();
void map(Buffer *buffer, int size, bool isIndexBuf = false);
@@ -813,15 +834,8 @@ private:
inline Batch *newBatch();
void invalidateAndRecycleBatch(Batch *b);
- void visualize();
- void visualizeBatch(Batch *b);
- void visualizeClipping(QSGNode *node);
- void visualizeChangesPrepare(Node *n, uint parentChanges = 0);
- void visualizeChanges(Node *n);
- void visualizeOverdraw();
- void visualizeOverdraw_helper(Node *node);
- void visualizeDrawGeometry(const QSGGeometry *g);
void setCustomRenderMode(const QByteArray &mode) override;
+ bool hasCustomRenderModeWithContinuousUpdate() const override;
QSGDefaultRenderContext *m_context;
QSet<Node *> m_taggedRoots;
@@ -852,6 +866,8 @@ private:
int m_batchNodeThreshold;
int m_batchVertexThreshold;
+ Visualizer *m_visualizer;
+
// Stuff used during rendering only...
ShaderManager *m_shaderManager; // per rendercontext, shared
QSGMaterial *m_currentMaterial;
@@ -874,9 +890,6 @@ private:
// For minimal OpenGL core profile support
QOpenGLVertexArrayObject *m_vao;
- QHash<Node *, uint> m_visualizeChanceSet;
- VisualizeMode m_visualizeMode;
-
Allocator<Node, 256> m_nodeAllocator;
Allocator<Element, 64> m_elementAllocator;
diff --git a/src/quick/scenegraph/coreapi/qsgopenglvisualizer.cpp b/src/quick/scenegraph/coreapi/qsgopenglvisualizer.cpp
new file mode 100644
index 0000000000..6c2ff0b176
--- /dev/null
+++ b/src/quick/scenegraph/coreapi/qsgopenglvisualizer.cpp
@@ -0,0 +1,365 @@
+/****************************************************************************
+**
+** 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$
+**
+****************************************************************************/
+
+#include "qsgopenglvisualizer_p.h"
+#include <qmath.h>
+#include <private/qsgshadersourcebuilder_p.h>
+
+QT_BEGIN_NAMESPACE
+
+namespace QSGBatchRenderer
+{
+
+#define QSGNODE_TRAVERSE(NODE) for (QSGNode *child = NODE->firstChild(); child; child = child->nextSibling())
+#define SHADOWNODE_TRAVERSE(NODE) for (Node *child = NODE->firstChild(); child; child = child->sibling())
+#define QSGNODE_DIRTY_PARENT (QSGNode::DirtyNodeAdded \
+ | QSGNode::DirtyOpacity \
+ | QSGNode::DirtyMatrix \
+ | QSGNode::DirtyNodeRemoved)
+
+QMatrix4x4 qsg_matrixForRoot(Node *node);
+
+class VisualizeShader : public QOpenGLShaderProgram
+{
+public:
+ int color;
+ int matrix;
+ int rotation;
+ int pattern;
+ int projection;
+};
+
+OpenGLVisualizer::OpenGLVisualizer(Renderer *renderer)
+ : Visualizer(renderer),
+ m_funcs(QOpenGLContext::currentContext()->functions()),
+ m_visualizeProgram(nullptr)
+{
+}
+
+OpenGLVisualizer::~OpenGLVisualizer()
+{
+ releaseResources();
+}
+
+void OpenGLVisualizer::releaseResources()
+{
+ delete m_visualizeProgram;
+ m_visualizeProgram = nullptr;
+}
+
+void OpenGLVisualizer::prepareVisualize()
+{
+ // nothing to do here
+}
+
+void OpenGLVisualizer::visualizeDrawGeometry(const QSGGeometry *g)
+{
+ if (g->attributeCount() < 1)
+ return;
+ const QSGGeometry::Attribute *a = g->attributes();
+ m_funcs->glVertexAttribPointer(0, a->tupleSize, a->type, false, g->sizeOfVertex(), g->vertexData());
+ if (g->indexCount())
+ m_funcs->glDrawElements(g->drawingMode(), g->indexCount(), g->indexType(), g->indexData());
+ else
+ m_funcs->glDrawArrays(g->drawingMode(), 0, g->vertexCount());
+
+}
+
+void OpenGLVisualizer::visualizeBatch(Batch *b)
+{
+ VisualizeShader *shader = static_cast<VisualizeShader *>(m_visualizeProgram);
+
+ if (b->positionAttribute != 0)
+ return;
+
+ QSGGeometryNode *gn = b->first->node;
+ QSGGeometry *g = gn->geometry();
+ const QSGGeometry::Attribute &a = g->attributes()[b->positionAttribute];
+
+ m_funcs->glBindBuffer(GL_ARRAY_BUFFER, b->vbo.id);
+
+ QMatrix4x4 matrix(m_renderer->m_current_projection_matrix);
+ if (b->root)
+ matrix = matrix * qsg_matrixForRoot(b->root);
+
+ shader->setUniformValue(shader->pattern, float(b->merged ? 0 : 1));
+
+ QColor color = QColor::fromHsvF((rand() & 1023) / 1023.0, 1.0, 1.0);
+ float cr = color.redF();
+ float cg = color.greenF();
+ float cb = color.blueF();
+ shader->setUniformValue(shader->color, cr, cg, cb, 1.0);
+
+ if (b->merged) {
+ shader->setUniformValue(shader->matrix, matrix);
+ const char *dataStart = m_renderer->m_context->separateIndexBuffer() ? b->ibo.data : b->vbo.data;
+ for (int ds=0; ds<b->drawSets.size(); ++ds) {
+ const DrawSet &set = b->drawSets.at(ds);
+ m_funcs->glVertexAttribPointer(a.position, 2, a.type, false, g->sizeOfVertex(),
+ (void *) (qintptr) (set.vertices));
+ m_funcs->glDrawElements(g->drawingMode(), set.indexCount, GL_UNSIGNED_SHORT,
+ (void *)(qintptr)(dataStart + set.indices));
+ }
+ } else {
+ Element *e = b->first;
+ int offset = 0;
+ while (e) {
+ gn = e->node;
+ g = gn->geometry();
+ shader->setUniformValue(shader->matrix, matrix * *gn->matrix());
+ m_funcs->glVertexAttribPointer(a.position, a.tupleSize, a.type, false, g->sizeOfVertex(),
+ (void *) (qintptr) offset);
+ if (g->indexCount())
+ m_funcs->glDrawElements(g->drawingMode(), g->indexCount(), g->indexType(), g->indexData());
+ else
+ m_funcs->glDrawArrays(g->drawingMode(), 0, g->vertexCount());
+ offset += g->sizeOfVertex() * g->vertexCount();
+ e = e->nextInBatch;
+ }
+ }
+}
+
+void OpenGLVisualizer::visualizeClipping(QSGNode *node)
+{
+ if (node->type() == QSGNode::ClipNodeType) {
+ VisualizeShader *shader = static_cast<VisualizeShader *>(m_visualizeProgram);
+ QSGClipNode *clipNode = static_cast<QSGClipNode *>(node);
+ QMatrix4x4 matrix = m_renderer->m_current_projection_matrix;
+ if (clipNode->matrix())
+ matrix = matrix * *clipNode->matrix();
+ shader->setUniformValue(shader->matrix, matrix);
+ visualizeDrawGeometry(clipNode->geometry());
+ }
+
+ QSGNODE_TRAVERSE(node) {
+ visualizeClipping(child);
+ }
+}
+
+void OpenGLVisualizer::visualizeChanges(Node *n)
+{
+
+ if (n->type() == QSGNode::GeometryNodeType && n->element()->batch && m_visualizeChangeSet.contains(n)) {
+ uint dirty = m_visualizeChangeSet.value(n);
+ bool tinted = (dirty & QSGNODE_DIRTY_PARENT) != 0;
+
+ VisualizeShader *shader = static_cast<VisualizeShader *>(m_visualizeProgram);
+ QColor color = QColor::fromHsvF((rand() & 1023) / 1023.0, 0.3, 1.0);
+ float ca = 0.5;
+ float cr = color.redF() * ca;
+ float cg = color.greenF() * ca;
+ float cb = color.blueF() * ca;
+ shader->setUniformValue(shader->color, cr, cg, cb, ca);
+ shader->setUniformValue(shader->pattern, float(tinted ? 0.5 : 0));
+
+ QSGGeometryNode *gn = static_cast<QSGGeometryNode *>(n->sgNode);
+
+ QMatrix4x4 matrix = m_renderer->m_current_projection_matrix;
+ if (n->element()->batch->root)
+ matrix = matrix * qsg_matrixForRoot(n->element()->batch->root);
+ matrix = matrix * *gn->matrix();
+ shader->setUniformValue(shader->matrix, matrix);
+ visualizeDrawGeometry(gn->geometry());
+
+ // This is because many changes don't propegate their dirty state to the
+ // parent so the node updater will not unset these states. They are
+ // not used for anything so, unsetting it should have no side effects.
+ n->dirtyState = nullptr;
+ }
+
+ SHADOWNODE_TRAVERSE(n) {
+ visualizeChanges(child);
+ }
+}
+
+void OpenGLVisualizer::visualizeOverdraw_helper(Node *node)
+{
+ if (node->type() == QSGNode::GeometryNodeType && node->element()->batch) {
+ VisualizeShader *shader = static_cast<VisualizeShader *>(m_visualizeProgram);
+ QSGGeometryNode *gn = static_cast<QSGGeometryNode *>(node->sgNode);
+
+ QMatrix4x4 matrix = m_renderer->m_current_projection_matrix;
+ matrix(2, 2) = m_renderer->m_zRange;
+ matrix(2, 3) = 1.0f - node->element()->order * m_renderer->m_zRange;
+
+ if (node->element()->batch->root)
+ matrix = matrix * qsg_matrixForRoot(node->element()->batch->root);
+ matrix = matrix * *gn->matrix();
+ shader->setUniformValue(shader->matrix, matrix);
+
+ QColor color = node->element()->batch->isOpaque ? QColor::fromRgbF(0.3, 1.0, 0.3) : QColor::fromRgbF(1.0, 0.3, 0.3);
+ float ca = 0.33f;
+ shader->setUniformValue(shader->color, color.redF() * ca, color.greenF() * ca, color.blueF() * ca, ca);
+
+ visualizeDrawGeometry(gn->geometry());
+ }
+
+ SHADOWNODE_TRAVERSE(node) {
+ visualizeOverdraw_helper(child);
+ }
+}
+
+void OpenGLVisualizer::visualizeOverdraw()
+{
+ VisualizeShader *shader = static_cast<VisualizeShader *>(m_visualizeProgram);
+ shader->setUniformValue(shader->color, 0.5f, 0.5f, 1.0f, 1.0f);
+ shader->setUniformValue(shader->projection, 1);
+
+ m_funcs->glBlendFunc(GL_ONE, GL_ONE);
+
+ static float step = 0;
+ step += static_cast<float>(M_PI * 2 / 1000.);
+ if (step > M_PI * 2)
+ step = 0;
+ float angle = 80.0 * std::sin(step);
+
+ QMatrix4x4 xrot; xrot.rotate(20, 1, 0, 0);
+ QMatrix4x4 zrot; zrot.rotate(angle, 0, 0, 1);
+ QMatrix4x4 tx; tx.translate(0, 0, 1);
+
+ QMatrix4x4 m;
+
+// m.rotate(180, 0, 1, 0);
+
+ m.translate(0, 0.5, 4);
+ m.scale(2, 2, 1);
+
+ m.rotate(-30, 1, 0, 0);
+ m.rotate(angle, 0, 1, 0);
+ m.translate(0, 0, -1);
+
+ shader->setUniformValue(shader->rotation, m);
+
+ float box[] = {
+ // lower
+ -1, 1, 0, 1, 1, 0,
+ -1, 1, 0, -1, -1, 0,
+ 1, 1, 0, 1, -1, 0,
+ -1, -1, 0, 1, -1, 0,
+
+ // upper
+ -1, 1, 1, 1, 1, 1,
+ -1, 1, 1, -1, -1, 1,
+ 1, 1, 1, 1, -1, 1,
+ -1, -1, 1, 1, -1, 1,
+
+ // sides
+ -1, -1, 0, -1, -1, 1,
+ 1, -1, 0, 1, -1, 1,
+ -1, 1, 0, -1, 1, 1,
+ 1, 1, 0, 1, 1, 1
+ };
+ m_funcs->glVertexAttribPointer(0, 3, GL_FLOAT, false, 0, box);
+ m_funcs->glLineWidth(2);
+ m_funcs->glDrawArrays(GL_LINES, 0, 24);
+
+ visualizeOverdraw_helper(m_renderer->m_nodes.value(m_renderer->rootNode()));
+}
+
+void OpenGLVisualizer::visualize()
+{
+ if (m_visualizeMode == VisualizeNothing)
+ return;
+
+ if (!m_visualizeProgram) {
+ VisualizeShader *prog = new VisualizeShader();
+ QSGShaderSourceBuilder::initializeProgramFromFiles(
+ prog,
+ QStringLiteral(":/qt-project.org/scenegraph/shaders/visualization.vert"),
+ QStringLiteral(":/qt-project.org/scenegraph/shaders/visualization.frag"));
+ prog->bindAttributeLocation("v", 0);
+ prog->link();
+ prog->bind();
+ prog->color = prog->uniformLocation("color");
+ prog->pattern = prog->uniformLocation("pattern");
+ prog->projection = prog->uniformLocation("projection");
+ prog->matrix = prog->uniformLocation("matrix");
+ prog->rotation = prog->uniformLocation("rotation");
+ m_visualizeProgram = prog;
+ } else {
+ m_visualizeProgram->bind();
+ }
+ VisualizeShader *shader = static_cast<VisualizeShader *>(m_visualizeProgram);
+
+ m_funcs->glDisable(GL_DEPTH_TEST);
+ m_funcs->glEnable(GL_BLEND);
+ m_funcs->glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
+ m_funcs->glEnableVertexAttribArray(0);
+
+ // Blacken out the actual rendered content...
+ float bgOpacity = 0.8f;
+ if (m_visualizeMode == VisualizeBatches)
+ bgOpacity = 1.0;
+ float v[] = { -1, 1, 1, 1, -1, -1, 1, -1 };
+ shader->setUniformValue(shader->color, 0.0f, 0.0f, 0.0f, bgOpacity);
+ shader->setUniformValue(shader->matrix, QMatrix4x4());
+ shader->setUniformValue(shader->rotation, QMatrix4x4());
+ shader->setUniformValue(shader->pattern, 0.0f);
+ shader->setUniformValue(shader->projection, false);
+ m_funcs->glVertexAttribPointer(0, 2, GL_FLOAT, false, 0, v);
+ m_funcs->glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
+
+ if (m_visualizeMode == VisualizeBatches) {
+ srand(0); // To force random colors to be roughly the same every time..
+ for (int i = 0; i < m_renderer->m_opaqueBatches.size(); ++i)
+ visualizeBatch(m_renderer->m_opaqueBatches.at(i));
+ for (int i = 0; i < m_renderer->m_alphaBatches.size(); ++i)
+ visualizeBatch(m_renderer->m_alphaBatches.at(i));
+ } else if (m_visualizeMode == VisualizeClipping) {
+ shader->setUniformValue(shader->pattern, 0.5f);
+ shader->setUniformValue(shader->color, 0.2f, 0.0f, 0.0f, 0.2f);
+ visualizeClipping(m_renderer->rootNode());
+ } else if (m_visualizeMode == VisualizeChanges) {
+ visualizeChanges(m_renderer->m_nodes.value(m_renderer->rootNode()));
+ m_visualizeChangeSet.clear();
+ } else if (m_visualizeMode == VisualizeOverdraw) {
+ visualizeOverdraw();
+ }
+
+ // Reset state back to defaults..
+ m_funcs->glDisable(GL_BLEND);
+ m_funcs->glDisableVertexAttribArray(0);
+ shader->release();
+}
+
+}
+
+QT_END_NAMESPACE
diff --git a/src/quick/scenegraph/coreapi/qsgopenglvisualizer_p.h b/src/quick/scenegraph/coreapi/qsgopenglvisualizer_p.h
new file mode 100644
index 0000000000..0b21694351
--- /dev/null
+++ b/src/quick/scenegraph/coreapi/qsgopenglvisualizer_p.h
@@ -0,0 +1,91 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 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$
+**
+****************************************************************************/
+
+#ifndef QSGOPENGLVISUALIZER_P_H
+#define QSGOPENGLVISUALIZER_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include "qsgbatchrenderer_p.h"
+
+QT_BEGIN_NAMESPACE
+
+namespace QSGBatchRenderer
+{
+
+// ### Qt 6: remove
+class OpenGLVisualizer : public Visualizer
+{
+public:
+ OpenGLVisualizer(Renderer *renderer);
+ ~OpenGLVisualizer();
+
+ void prepareVisualize() override;
+ void visualize() override;
+
+ void releaseResources() override;
+
+private:
+ void visualizeBatch(Batch *b);
+ void visualizeClipping(QSGNode *node);
+ void visualizeChanges(Node *n);
+ void visualizeOverdraw();
+ void visualizeOverdraw_helper(Node *node);
+ void visualizeDrawGeometry(const QSGGeometry *g);
+
+ QOpenGLFunctions *m_funcs;
+ QOpenGLShaderProgram *m_visualizeProgram;
+};
+
+} // namespace QSGBatchRenderer
+
+QT_END_NAMESPACE
+
+#endif // QSGOPENGLVISUALIZER_P_H
diff --git a/src/quick/scenegraph/coreapi/qsgrenderer_p.h b/src/quick/scenegraph/coreapi/qsgrenderer_p.h
index 9c83ddf111..c4ed0072f6 100644
--- a/src/quick/scenegraph/coreapi/qsgrenderer_p.h
+++ b/src/quick/scenegraph/coreapi/qsgrenderer_p.h
@@ -97,6 +97,7 @@ public:
inline QSGMaterialShader::RenderState state(QSGMaterialShader::RenderState::DirtyStates dirty) const;
inline QSGMaterialRhiShader::RenderState rhiState(QSGMaterialRhiShader::RenderState::DirtyStates dirty) const;
virtual void setCustomRenderMode(const QByteArray &) { }
+ virtual bool hasCustomRenderModeWithContinuousUpdate() const { return false; }
virtual void releaseCachedResources() { }
void clearChangedFlag() { m_changed_emitted = false; }
diff --git a/src/quick/scenegraph/coreapi/qsgrhivisualizer.cpp b/src/quick/scenegraph/coreapi/qsgrhivisualizer.cpp
new file mode 100644
index 0000000000..38d4c4440f
--- /dev/null
+++ b/src/quick/scenegraph/coreapi/qsgrhivisualizer.cpp
@@ -0,0 +1,929 @@
+/****************************************************************************
+**
+** 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$
+**
+****************************************************************************/
+
+#include "qsgrhivisualizer_p.h"
+#include <qmath.h>
+#include <QQuickWindow>
+#include <private/qsgmaterialrhishader_p.h>
+#include <private/qsgshadersourcebuilder_p.h>
+
+QT_BEGIN_NAMESPACE
+
+namespace QSGBatchRenderer
+{
+
+#define QSGNODE_TRAVERSE(NODE) for (QSGNode *child = NODE->firstChild(); child; child = child->nextSibling())
+#define SHADOWNODE_TRAVERSE(NODE) for (Node *child = NODE->firstChild(); child; child = child->sibling())
+#define QSGNODE_DIRTY_PARENT (QSGNode::DirtyNodeAdded \
+ | QSGNode::DirtyOpacity \
+ | QSGNode::DirtyMatrix \
+ | QSGNode::DirtyNodeRemoved)
+
+QMatrix4x4 qsg_matrixForRoot(Node *node);
+QRhiVertexInputAttribute::Format qsg_vertexInputFormat(const QSGGeometry::Attribute &a);
+QRhiCommandBuffer::IndexFormat qsg_indexFormat(const QSGGeometry *geometry);
+QRhiGraphicsPipeline::Topology qsg_topology(int geomDrawMode);
+
+RhiVisualizer::RhiVisualizer(Renderer *renderer)
+ : Visualizer(renderer)
+{
+}
+
+RhiVisualizer::~RhiVisualizer()
+{
+ releaseResources();
+}
+
+void RhiVisualizer::releaseResources()
+{
+ m_pipelines.releaseResources();
+
+ m_fade.releaseResources();
+
+ m_changeVis.releaseResources();
+ m_batchVis.releaseResources();
+ m_clipVis.releaseResources();
+ m_overdrawVis.releaseResources();
+}
+
+void RhiVisualizer::prepareVisualize()
+{
+ // Called before the render pass has begun (but after preparing the
+ // batches). Now is the time to put resource updates to the renderer's
+ // current m_resourceUpdates instance.
+
+ if (m_visualizeMode == VisualizeNothing)
+ return;
+
+ if (!m_vs.isValid()) {
+ m_vs = QSGMaterialRhiShaderPrivate::loadShader(
+ QLatin1String(":/qt-project.org/scenegraph/shaders_ng/visualization.vert.qsb"));
+ m_fs = QSGMaterialRhiShaderPrivate::loadShader(
+ QLatin1String(":/qt-project.org/scenegraph/shaders_ng/visualization.frag.qsb"));
+ }
+
+ m_fade.prepare(this, m_renderer->m_rhi, m_renderer->m_resourceUpdates, m_renderer->renderPassDescriptor());
+
+ const bool forceUintIndex = m_renderer->m_uint32IndexForRhi;
+
+ switch (m_visualizeMode) {
+ case VisualizeBatches:
+ m_batchVis.prepare(m_renderer->m_opaqueBatches, m_renderer->m_alphaBatches,
+ this,
+ m_renderer->m_rhi, m_renderer->m_resourceUpdates,
+ forceUintIndex);
+ break;
+ case VisualizeClipping:
+ m_clipVis.prepare(m_renderer->rootNode(), this,
+ m_renderer->m_rhi, m_renderer->m_resourceUpdates);
+ break;
+ case VisualizeChanges:
+ m_changeVis.prepare(m_renderer->m_nodes.value(m_renderer->rootNode()),
+ this,
+ m_renderer->m_rhi, m_renderer->m_resourceUpdates);
+ m_visualizeChangeSet.clear();
+ break;
+ case VisualizeOverdraw:
+ m_overdrawVis.prepare(m_renderer->m_nodes.value(m_renderer->rootNode()),
+ this,
+ m_renderer->m_rhi, m_renderer->m_resourceUpdates);
+ break;
+ default:
+ Q_UNREACHABLE();
+ break;
+ }
+}
+
+void RhiVisualizer::visualize()
+{
+ if (m_visualizeMode == VisualizeNothing)
+ return;
+
+ QRhiCommandBuffer *cb = m_renderer->commandBuffer();
+ m_fade.render(cb);
+
+ switch (m_visualizeMode) {
+ case VisualizeBatches:
+ m_batchVis.render(cb);
+ break;
+ case VisualizeClipping:
+ m_clipVis.render(cb);
+ break;
+ case VisualizeChanges:
+ m_changeVis.render(cb);
+ break;
+ case VisualizeOverdraw:
+ m_overdrawVis.render(cb);
+ break;
+ default:
+ Q_UNREACHABLE();
+ break;
+ }
+}
+
+void RhiVisualizer::recordDrawCalls(const QVector<DrawCall> &drawCalls,
+ QRhiCommandBuffer *cb,
+ QRhiShaderResourceBindings *srb,
+ bool blendOneOne)
+{
+ for (const DrawCall &dc : drawCalls) {
+ QRhiGraphicsPipeline *ps = m_pipelines.pipeline(this, m_renderer->m_rhi, srb, m_renderer->renderPassDescriptor(),
+ dc.vertex.topology, dc.vertex.format, dc.vertex.stride,
+ blendOneOne);
+ if (!ps)
+ continue;
+ cb->setGraphicsPipeline(ps); // no-op if same as the last one
+ QRhiCommandBuffer::DynamicOffset dynofs(0, dc.buf.ubufOffset);
+ cb->setShaderResources(srb, 1, &dynofs);
+ QRhiCommandBuffer::VertexInput vb(dc.buf.vbuf, dc.buf.vbufOffset);
+ if (dc.index.count) {
+ cb->setVertexInput(0, 1, &vb, dc.buf.ibuf, dc.buf.ibufOffset, dc.index.format);
+ cb->drawIndexed(dc.index.count);
+ } else {
+ cb->setVertexInput(0, 1, &vb);
+ cb->draw(dc.vertex.count);
+ }
+ }
+}
+
+const QRhiShaderResourceBinding::StageFlags ubufVisibility =
+ QRhiShaderResourceBinding::VertexStage | QRhiShaderResourceBinding::FragmentStage;
+
+void RhiVisualizer::Fade::prepare(RhiVisualizer *visualizer,
+ QRhi *rhi, QRhiResourceUpdateBatch *u, QRhiRenderPassDescriptor *rpDesc)
+{
+ this->visualizer = visualizer;
+
+ if (!vbuf) {
+ float v[] = { -1, 1, 1, 1, -1, -1, 1, -1 };
+ vbuf = rhi->newBuffer(QRhiBuffer::Immutable, QRhiBuffer::VertexBuffer, sizeof(v));
+ if (!vbuf->build())
+ return;
+ u->uploadStaticBuffer(vbuf, v);
+ }
+
+ if (!ubuf) {
+ ubuf = rhi->newBuffer(QRhiBuffer::Dynamic, QRhiBuffer::UniformBuffer, DrawCall::UBUF_SIZE);
+ if (!ubuf->build())
+ return;
+ float bgOpacity = 0.8f;
+ if (visualizer->m_visualizeMode == Visualizer::VisualizeBatches)
+ bgOpacity = 1.0;
+ QMatrix4x4 ident;
+ u->updateDynamicBuffer(ubuf, 0, 64, ident.constData()); // matrix
+ u->updateDynamicBuffer(ubuf, 64, 64, ident.constData()); // rotation
+ float color[4] = { 0.0f, 0.0f, 0.0f, bgOpacity };
+ u->updateDynamicBuffer(ubuf, 128, 16, color);
+ float pattern = 0.0f;
+ u->updateDynamicBuffer(ubuf, 144, 4, &pattern);
+ qint32 projection = 0;
+ u->updateDynamicBuffer(ubuf, 148, 4, &projection);
+ }
+
+ if (!srb) {
+ srb = rhi->newShaderResourceBindings();
+ srb->setBindings({ QRhiShaderResourceBinding::uniformBuffer(0, ubufVisibility, ubuf) });
+ if (!srb->build())
+ return;
+ }
+
+ if (!ps) {
+ ps = rhi->newGraphicsPipeline();
+ ps->setTopology(QRhiGraphicsPipeline::TriangleStrip);
+ QRhiGraphicsPipeline::TargetBlend blend; // defaults to premul alpha, just what we need
+ blend.enable = true;
+ ps->setTargetBlends({ blend });
+ ps->setShaderStages({ { QRhiShaderStage::Vertex, visualizer->m_vs },
+ { QRhiShaderStage::Fragment, visualizer->m_fs } });
+ QRhiVertexInputLayout inputLayout;
+ inputLayout.setBindings({ { 2 * sizeof(float) } });
+ inputLayout.setAttributes({ { 0, 0, QRhiVertexInputAttribute::Float2, 0 } });
+ ps->setVertexInputLayout(inputLayout);
+ ps->setShaderResourceBindings(srb);
+ ps->setRenderPassDescriptor(rpDesc);
+ if (!ps->build())
+ return;
+ }
+}
+
+void RhiVisualizer::Fade::releaseResources()
+{
+ delete ps;
+ ps = nullptr;
+
+ delete srb;
+ srb = nullptr;
+
+ delete ubuf;
+ ubuf = nullptr;
+
+ delete vbuf;
+ vbuf = nullptr;
+}
+
+void RhiVisualizer::Fade::render(QRhiCommandBuffer *cb)
+{
+ cb->setGraphicsPipeline(ps);
+ cb->setViewport(visualizer->m_renderer->m_pstate.viewport);
+ cb->setShaderResources();
+ QRhiCommandBuffer::VertexInput vb(vbuf, 0);
+ cb->setVertexInput(0, 1, &vb);
+ cb->draw(4);
+}
+
+static void fillVertexIndex(RhiVisualizer::DrawCall *dc, QSGGeometry *g, bool withData, bool forceUintIndex)
+{
+ dc->vertex.topology = qsg_topology(g->drawingMode());
+ dc->vertex.format = qsg_vertexInputFormat(g->attributes()[0]);
+ dc->vertex.count = g->vertexCount();
+ dc->vertex.stride = g->sizeOfVertex();
+ if (withData)
+ dc->vertex.data = g->vertexData();
+
+ dc->index.format = forceUintIndex ? QRhiCommandBuffer::IndexUInt32 : qsg_indexFormat(g);
+ dc->index.count = g->indexCount();
+ dc->index.stride = forceUintIndex ? sizeof(quint32) : g->sizeOfIndex();
+ if (withData && g->indexCount())
+ dc->index.data = g->indexData();
+}
+
+static inline uint aligned(uint v, uint byteAlign)
+{
+ return (v + byteAlign - 1) & ~(byteAlign - 1);
+}
+
+static bool ensureBuffer(QRhi *rhi, QRhiBuffer **buf, QRhiBuffer::UsageFlags usage, int newSize)
+{
+ if (!*buf) {
+ *buf = rhi->newBuffer(QRhiBuffer::Dynamic, usage, newSize);
+ if (!(*buf)->build())
+ return false;
+ } else if ((*buf)->size() < newSize) {
+ (*buf)->setSize(newSize);
+ if (!(*buf)->build())
+ return false;
+ }
+ return true;
+}
+
+QRhiGraphicsPipeline *RhiVisualizer::PipelineCache::pipeline(RhiVisualizer *visualizer,
+ QRhi *rhi,
+ QRhiShaderResourceBindings *srb,
+ QRhiRenderPassDescriptor *rpDesc,
+ QRhiGraphicsPipeline::Topology topology,
+ QRhiVertexInputAttribute::Format vertexFormat,
+ quint32 vertexStride,
+ bool blendOneOne)
+{
+ for (int i = 0, ie = pipelines.count(); i != ie; ++i) {
+ const Pipeline &p(pipelines.at(i));
+ if (p.topology == topology && p.format == vertexFormat && p.stride == vertexStride)
+ return p.ps;
+ }
+
+ QRhiGraphicsPipeline *ps = rhi->newGraphicsPipeline();
+ ps->setTopology(topology);
+ QRhiGraphicsPipeline::TargetBlend blend; // premul alpha
+ blend.enable = true;
+ if (blendOneOne) {
+ // only for visualizing overdraw, other modes use premul alpha
+ blend.srcColor = QRhiGraphicsPipeline::One;
+ blend.dstColor = QRhiGraphicsPipeline::One;
+ blend.srcAlpha = QRhiGraphicsPipeline::One;
+ blend.dstAlpha = QRhiGraphicsPipeline::One;
+ }
+ ps->setTargetBlends({ blend });
+ ps->setShaderStages({ { QRhiShaderStage::Vertex, visualizer->m_vs },
+ { QRhiShaderStage::Fragment, visualizer->m_fs } });
+ QRhiVertexInputLayout inputLayout;
+ inputLayout.setBindings({ { vertexStride } });
+ inputLayout.setAttributes({ { 0, 0, vertexFormat, 0 } });
+ ps->setVertexInputLayout(inputLayout);
+ ps->setShaderResourceBindings(srb);
+ ps->setRenderPassDescriptor(rpDesc);
+ if (!ps->build())
+ return nullptr;
+
+ Pipeline p;
+ p.topology = topology;
+ p.format = vertexFormat;
+ p.stride = vertexStride;
+ p.ps = ps;
+ pipelines.append(p);
+
+ return ps;
+}
+
+void RhiVisualizer::PipelineCache::releaseResources()
+{
+ for (int i = 0, ie = pipelines.count(); i != ie; ++i)
+ delete pipelines.at(i).ps;
+
+ pipelines.clear();
+}
+
+void RhiVisualizer::ChangeVis::gather(Node *n)
+{
+ if (n->type() == QSGNode::GeometryNodeType && n->element()->batch && visualizer->m_visualizeChangeSet.contains(n)) {
+ const uint dirty = visualizer->m_visualizeChangeSet.value(n);
+ const bool tinted = (dirty & QSGNODE_DIRTY_PARENT) != 0;
+ const QColor color = QColor::fromHsvF((rand() & 1023) / 1023.0f, 0.3f, 1.0f);
+ const float alpha = 0.5f;
+
+ QMatrix4x4 matrix = visualizer->m_renderer->m_current_projection_matrix;
+ if (n->element()->batch->root)
+ matrix = matrix * qsg_matrixForRoot(n->element()->batch->root);
+
+ QSGGeometryNode *gn = static_cast<QSGGeometryNode *>(n->sgNode);
+ matrix = matrix * *gn->matrix();
+
+ QSGGeometry *g = gn->geometry();
+ if (g->attributeCount() >= 1) {
+ DrawCall dc;
+ memcpy(dc.uniforms.data, matrix.constData(), 64);
+ QMatrix4x4 rotation;
+ memcpy(dc.uniforms.data + 64, rotation.constData(), 64);
+ float c[4] = {
+ float(color.redF()) * alpha,
+ float(color.greenF()) * alpha,
+ float(color.blueF()) * alpha,
+ alpha
+ };
+ memcpy(dc.uniforms.data + 128, c, 16);
+ float pattern = tinted ? 0.5f : 0.0f;
+ memcpy(dc.uniforms.data + 144, &pattern, 4);
+ qint32 projection = 0;
+ memcpy(dc.uniforms.data + 148, &projection, 4);
+
+ fillVertexIndex(&dc, g, true, false);
+ drawCalls.append(dc);
+ }
+
+ // This is because many changes don't propegate their dirty state to the
+ // parent so the node updater will not unset these states. They are
+ // not used for anything so, unsetting it should have no side effects.
+ n->dirtyState = nullptr;
+ }
+
+ SHADOWNODE_TRAVERSE(n) {
+ gather(child);
+ }
+}
+
+void RhiVisualizer::ChangeVis::prepare(Node *n, RhiVisualizer *visualizer,
+ QRhi *rhi, QRhiResourceUpdateBatch *u)
+{
+ this->visualizer = visualizer;
+
+ drawCalls.clear();
+ gather(n);
+
+ if (drawCalls.isEmpty())
+ return;
+
+ const int ubufAlign = rhi->ubufAlignment();
+ int vbufOffset = 0;
+ int ibufOffset = 0;
+ int ubufOffset = 0;
+ for (RhiVisualizer::DrawCall &dc : drawCalls) {
+ dc.buf.vbufOffset = aligned(vbufOffset, 4);
+ vbufOffset = dc.buf.vbufOffset + dc.vertex.count * dc.vertex.stride;
+
+ dc.buf.ibufOffset = aligned(ibufOffset, 4);
+ ibufOffset = dc.buf.ibufOffset + dc.index.count * dc.index.stride;
+
+ dc.buf.ubufOffset = aligned(ubufOffset, ubufAlign);
+ ubufOffset = dc.buf.ubufOffset + DrawCall::UBUF_SIZE;
+ }
+
+ ensureBuffer(rhi, &vbuf, QRhiBuffer::VertexBuffer, vbufOffset);
+ if (ibufOffset)
+ ensureBuffer(rhi, &ibuf, QRhiBuffer::IndexBuffer, ibufOffset);
+ const int ubufSize = ubufOffset;
+ ensureBuffer(rhi, &ubuf, QRhiBuffer::UniformBuffer, ubufSize);
+
+ for (RhiVisualizer::DrawCall &dc : drawCalls) {
+ u->updateDynamicBuffer(vbuf, dc.buf.vbufOffset, dc.vertex.count * dc.vertex.stride, dc.vertex.data);
+ dc.buf.vbuf = vbuf;
+ if (dc.index.count) {
+ u->updateDynamicBuffer(ibuf, dc.buf.ibufOffset, dc.index.count * dc.index.stride, dc.index.data);
+ dc.buf.ibuf = ibuf;
+ }
+ u->updateDynamicBuffer(ubuf, dc.buf.ubufOffset, DrawCall::UBUF_SIZE, dc.uniforms.data);
+ }
+
+ if (!srb) {
+ srb = rhi->newShaderResourceBindings();
+ srb->setBindings({ QRhiShaderResourceBinding::uniformBufferWithDynamicOffset(0, ubufVisibility, ubuf, DrawCall::UBUF_SIZE) });
+ if (!srb->build())
+ return;
+ }
+}
+
+void RhiVisualizer::ChangeVis::releaseResources()
+{
+ delete srb;
+ srb = nullptr;
+
+ delete ubuf;
+ ubuf = nullptr;
+
+ delete ibuf;
+ ibuf = nullptr;
+
+ delete vbuf;
+ vbuf = nullptr;
+}
+
+void RhiVisualizer::ChangeVis::render(QRhiCommandBuffer *cb)
+{
+ visualizer->recordDrawCalls(drawCalls, cb, srb);
+}
+
+void RhiVisualizer::BatchVis::gather(Batch *b)
+{
+ if (b->positionAttribute != 0)
+ return;
+
+ QMatrix4x4 matrix(visualizer->m_renderer->m_current_projection_matrix);
+ if (b->root)
+ matrix = matrix * qsg_matrixForRoot(b->root);
+
+ DrawCall dc;
+
+ QMatrix4x4 rotation;
+ memcpy(dc.uniforms.data + 64, rotation.constData(), 64);
+
+ QColor color = QColor::fromHsvF((rand() & 1023) / 1023.0, 1.0, 1.0);
+
+ float c[4] = {
+ float(color.redF()),
+ float(color.greenF()),
+ float(color.blueF()),
+ 1.0f
+ };
+ memcpy(dc.uniforms.data + 128, c, 16);
+
+ float pattern = b->merged ? 0.0f : 1.0f;
+ memcpy(dc.uniforms.data + 144, &pattern, 4);
+
+ qint32 projection = 0;
+ memcpy(dc.uniforms.data + 148, &projection, 4);
+
+ if (b->merged) {
+ memcpy(dc.uniforms.data, matrix.constData(), 64);
+
+ QSGGeometryNode *gn = b->first->node;
+ QSGGeometry *g = gn->geometry();
+
+ fillVertexIndex(&dc, g, false, forceUintIndex);
+
+ for (int ds = 0; ds < b->drawSets.size(); ++ds) {
+ const DrawSet &set = b->drawSets.at(ds);
+ dc.buf.vbuf = b->vbo.buf;
+ dc.buf.vbufOffset = set.vertices;
+ dc.buf.ibuf = b->ibo.buf;
+ dc.buf.ibufOffset = set.indices;
+ dc.index.count = set.indexCount;
+ drawCalls.append(dc);
+ }
+ } else {
+ Element *e = b->first;
+ int vOffset = 0;
+ int iOffset = 0;
+
+ while (e) {
+ QSGGeometryNode *gn = e->node;
+ QSGGeometry *g = gn->geometry();
+
+ QMatrix4x4 m = matrix * *gn->matrix();
+ memcpy(dc.uniforms.data, m.constData(), 64);
+
+ fillVertexIndex(&dc, g, false, forceUintIndex);
+
+ dc.buf.vbuf = b->vbo.buf;
+ dc.buf.vbufOffset = vOffset;
+ if (g->indexCount()) {
+ dc.buf.ibuf = b->ibo.buf;
+ dc.buf.ibufOffset = iOffset;
+ }
+
+ drawCalls.append(dc);
+
+ vOffset += dc.vertex.count * dc.vertex.stride;
+ iOffset += dc.index.count * dc.index.stride;
+
+ e = e->nextInBatch;
+ }
+ }
+}
+
+void RhiVisualizer::BatchVis::prepare(const QDataBuffer<Batch *> &opaqueBatches, const QDataBuffer<Batch *> &alphaBatches,
+ RhiVisualizer *visualizer,
+ QRhi *rhi, QRhiResourceUpdateBatch *u,
+ bool forceUintIndex)
+{
+ this->visualizer = visualizer;
+ this->forceUintIndex = forceUintIndex;
+
+ drawCalls.clear();
+
+ srand(0); // To force random colors to be roughly the same every time..
+ for (int i = 0; i < opaqueBatches.size(); ++i)
+ gather(opaqueBatches.at(i));
+ for (int i = 0; i < alphaBatches.size(); ++i)
+ gather(alphaBatches.at(i));
+
+ if (drawCalls.isEmpty())
+ return;
+
+ const int ubufAlign = rhi->ubufAlignment();
+ int ubufOffset = 0;
+ for (RhiVisualizer::DrawCall &dc : drawCalls) {
+ dc.buf.ubufOffset = aligned(ubufOffset, ubufAlign);
+ ubufOffset = dc.buf.ubufOffset + DrawCall::UBUF_SIZE;
+ }
+
+ const int ubufSize = ubufOffset;
+ ensureBuffer(rhi, &ubuf, QRhiBuffer::UniformBuffer, ubufSize);
+
+ for (RhiVisualizer::DrawCall &dc : drawCalls)
+ u->updateDynamicBuffer(ubuf, dc.buf.ubufOffset, DrawCall::UBUF_SIZE, dc.uniforms.data);
+
+ if (!srb) {
+ srb = rhi->newShaderResourceBindings();
+ srb->setBindings({ QRhiShaderResourceBinding::uniformBufferWithDynamicOffset(0, ubufVisibility, ubuf, DrawCall::UBUF_SIZE) });
+ if (!srb->build())
+ return;
+ }
+}
+
+void RhiVisualizer::BatchVis::releaseResources()
+{
+ delete srb;
+ srb = nullptr;
+
+ delete ubuf;
+ ubuf = nullptr;
+}
+
+void RhiVisualizer::BatchVis::render(QRhiCommandBuffer *cb)
+{
+ visualizer->recordDrawCalls(drawCalls, cb, srb);
+}
+
+void RhiVisualizer::ClipVis::gather(QSGNode *node)
+{
+ if (node->type() == QSGNode::ClipNodeType) {
+ QSGClipNode *clipNode = static_cast<QSGClipNode *>(node);
+ QMatrix4x4 matrix = visualizer->m_renderer->m_current_projection_matrix;
+ if (clipNode->matrix())
+ matrix = matrix * *clipNode->matrix();
+
+ QSGGeometry *g = clipNode->geometry();
+ if (g->attributeCount() >= 1) {
+ DrawCall dc;
+ memcpy(dc.uniforms.data, matrix.constData(), 64);
+ QMatrix4x4 rotation;
+ memcpy(dc.uniforms.data + 64, rotation.constData(), 64);
+ float c[4] = { 0.2f, 0.0f, 0.0f, 0.2f };
+ memcpy(dc.uniforms.data + 128, c, 16);
+ float pattern = 0.5f;
+ memcpy(dc.uniforms.data + 144, &pattern, 4);
+ qint32 projection = 0;
+ memcpy(dc.uniforms.data + 148, &projection, 4);
+ fillVertexIndex(&dc, g, true, false);
+ drawCalls.append(dc);
+ }
+ }
+
+ QSGNODE_TRAVERSE(node) {
+ gather(child);
+ }
+}
+
+void RhiVisualizer::ClipVis::prepare(QSGNode *node, RhiVisualizer *visualizer,
+ QRhi *rhi, QRhiResourceUpdateBatch *u)
+{
+ this->visualizer = visualizer;
+
+ drawCalls.clear();
+ gather(node);
+
+ if (drawCalls.isEmpty())
+ return;
+
+ const int ubufAlign = rhi->ubufAlignment();
+ int vbufOffset = 0;
+ int ibufOffset = 0;
+ int ubufOffset = 0;
+ for (RhiVisualizer::DrawCall &dc : drawCalls) {
+ dc.buf.vbufOffset = aligned(vbufOffset, 4);
+ vbufOffset = dc.buf.vbufOffset + dc.vertex.count * dc.vertex.stride;
+
+ dc.buf.ibufOffset = aligned(ibufOffset, 4);
+ ibufOffset = dc.buf.ibufOffset + dc.index.count * dc.index.stride;
+
+ dc.buf.ubufOffset = aligned(ubufOffset, ubufAlign);
+ ubufOffset = dc.buf.ubufOffset + DrawCall::UBUF_SIZE;
+ }
+
+ ensureBuffer(rhi, &vbuf, QRhiBuffer::VertexBuffer, vbufOffset);
+ if (ibufOffset)
+ ensureBuffer(rhi, &ibuf, QRhiBuffer::IndexBuffer, ibufOffset);
+ const int ubufSize = ubufOffset;
+ ensureBuffer(rhi, &ubuf, QRhiBuffer::UniformBuffer, ubufSize);
+
+ for (RhiVisualizer::DrawCall &dc : drawCalls) {
+ u->updateDynamicBuffer(vbuf, dc.buf.vbufOffset, dc.vertex.count * dc.vertex.stride, dc.vertex.data);
+ dc.buf.vbuf = vbuf;
+ if (dc.index.count) {
+ u->updateDynamicBuffer(ibuf, dc.buf.ibufOffset, dc.index.count * dc.index.stride, dc.index.data);
+ dc.buf.ibuf = ibuf;
+ }
+ u->updateDynamicBuffer(ubuf, dc.buf.ubufOffset, DrawCall::UBUF_SIZE, dc.uniforms.data);
+ }
+
+ if (!srb) {
+ srb = rhi->newShaderResourceBindings();
+ srb->setBindings({ QRhiShaderResourceBinding::uniformBufferWithDynamicOffset(0, ubufVisibility, ubuf, DrawCall::UBUF_SIZE) });
+ if (!srb->build())
+ return;
+ }
+}
+
+void RhiVisualizer::ClipVis::releaseResources()
+{
+ delete srb;
+ srb = nullptr;
+
+ delete ubuf;
+ ubuf = nullptr;
+
+ delete ibuf;
+ ibuf = nullptr;
+
+ delete vbuf;
+ vbuf = nullptr;
+}
+
+void RhiVisualizer::ClipVis::render(QRhiCommandBuffer *cb)
+{
+ visualizer->recordDrawCalls(drawCalls, cb, srb);
+}
+
+void RhiVisualizer::OverdrawVis::gather(Node *n)
+{
+ if (n->type() == QSGNode::GeometryNodeType && n->element()->batch) {
+ QMatrix4x4 matrix = visualizer->m_renderer->m_current_projection_matrix;
+ matrix(2, 2) = visualizer->m_renderer->m_zRange;
+ matrix(2, 3) = 1.0f - n->element()->order * visualizer->m_renderer->m_zRange;
+
+ if (n->element()->batch->root)
+ matrix = matrix * qsg_matrixForRoot(n->element()->batch->root);
+
+ QSGGeometryNode *gn = static_cast<QSGGeometryNode *>(n->sgNode);
+ matrix = matrix * *gn->matrix();
+
+ QSGGeometry *g = gn->geometry();
+ if (g->attributeCount() >= 1) {
+ DrawCall dc;
+ memcpy(dc.uniforms.data, matrix.constData(), 64);
+ memcpy(dc.uniforms.data + 64, rotation.constData(), 64);
+
+ float c[4];
+ const float ca = 0.33f;
+ if (n->element()->batch->isOpaque) {
+ c[0] = ca * 0.3f; c[1] = ca * 1.0f; c[2] = ca * 0.3f; c[3] = ca;
+ } else {
+ c[0] = ca * 1.0f; c[1] = ca * 0.3f; c[2] = ca * 0.3f; c[3] = ca;
+ }
+ memcpy(dc.uniforms.data + 128, c, 16);
+ float pattern = 0.0f;
+ memcpy(dc.uniforms.data + 144, &pattern, 4);
+ qint32 projection = 1;
+ memcpy(dc.uniforms.data + 148, &projection, 4);
+
+ fillVertexIndex(&dc, g, true, false);
+ drawCalls.append(dc);
+ }
+ }
+
+ SHADOWNODE_TRAVERSE(n) {
+ gather(child);
+ }
+}
+
+void RhiVisualizer::OverdrawVis::prepare(Node *n, RhiVisualizer *visualizer,
+ QRhi *rhi, QRhiResourceUpdateBatch *u)
+{
+ this->visualizer = visualizer;
+
+ step += float(M_PI * 2 / 1000.0);
+ if (step > float(M_PI * 2))
+ step = 0.0f;
+
+ const float yfix = rhi->isYUpInNDC() ? 1.0f : -1.0f;
+ rotation.setToIdentity();
+ rotation.translate(0.0f, 0.5f * yfix, 4.0f);
+ rotation.scale(2.0f, 2.0f, 1.0f);
+ rotation.rotate(-30.0f * yfix, 1.0f, 0.0f, 0.0f);
+ rotation.rotate(80.0f * std::sin(step), 0.0f, 1.0f, 0.0f);
+ rotation.translate(0.0f, 0.0f, -1.0f);
+
+ drawCalls.clear();
+ gather(n);
+
+ if (!box.vbuf) {
+ const float v[] = {
+ // lower
+ -1, 1, 0, 1, 1, 0,
+ -1, 1, 0, -1, -1, 0,
+ 1, 1, 0, 1, -1, 0,
+ -1, -1, 0, 1, -1, 0,
+
+ // upper
+ -1, 1, 1, 1, 1, 1,
+ -1, 1, 1, -1, -1, 1,
+ 1, 1, 1, 1, -1, 1,
+ -1, -1, 1, 1, -1, 1,
+
+ // sides
+ -1, -1, 0, -1, -1, 1,
+ 1, -1, 0, 1, -1, 1,
+ -1, 1, 0, -1, 1, 1,
+ 1, 1, 0, 1, 1, 1
+ };
+ box.vbuf = rhi->newBuffer(QRhiBuffer::Immutable, QRhiBuffer::VertexBuffer, sizeof(v));
+ if (!box.vbuf->build())
+ return;
+ u->uploadStaticBuffer(box.vbuf, v);
+ }
+
+ if (!box.ubuf) {
+ box.ubuf = rhi->newBuffer(QRhiBuffer::Dynamic, QRhiBuffer::UniformBuffer, DrawCall::UBUF_SIZE);
+ if (!box.ubuf->build())
+ return;
+ QMatrix4x4 ident;
+ u->updateDynamicBuffer(box.ubuf, 0, 64, ident.constData());
+ float color[4] = { 0.5f, 0.5f, 1.0f, 1.0f };
+ u->updateDynamicBuffer(box.ubuf, 128, 16, color);
+ float pattern = 0.0f;
+ u->updateDynamicBuffer(box.ubuf, 144, 4, &pattern);
+ qint32 projection = 1;
+ u->updateDynamicBuffer(box.ubuf, 148, 4, &projection);
+ }
+
+ u->updateDynamicBuffer(box.ubuf, 64, 64, rotation.constData());
+
+ if (!box.srb) {
+ box.srb = rhi->newShaderResourceBindings();
+ box.srb->setBindings({ QRhiShaderResourceBinding::uniformBuffer(0, ubufVisibility, box.ubuf) });
+ if (!box.srb->build())
+ return;
+ }
+
+ if (!box.ps) {
+ box.ps = rhi->newGraphicsPipeline();
+ box.ps->setTopology(QRhiGraphicsPipeline::Lines);
+ box.ps->setLineWidth(2); // may be be ignored (D3D, Metal), but may be used on GL and Vulkan
+ QRhiGraphicsPipeline::TargetBlend blend;
+ blend.enable = true;
+ blend.srcColor = QRhiGraphicsPipeline::One;
+ blend.dstColor = QRhiGraphicsPipeline::One;
+ blend.srcAlpha = QRhiGraphicsPipeline::One;
+ blend.dstAlpha = QRhiGraphicsPipeline::One;
+ box.ps->setTargetBlends({ blend });
+ box.ps->setShaderStages({ { QRhiShaderStage::Vertex, visualizer->m_vs },
+ { QRhiShaderStage::Fragment, visualizer->m_fs } });
+ QRhiVertexInputLayout inputLayout;
+ inputLayout.setBindings({ { 3 * sizeof(float) } });
+ inputLayout.setAttributes({ { 0, 0, QRhiVertexInputAttribute::Float3, 0 } });
+ box.ps->setVertexInputLayout(inputLayout);
+ box.ps->setShaderResourceBindings(box.srb);
+ box.ps->setRenderPassDescriptor(visualizer->m_renderer->renderPassDescriptor());
+ if (!box.ps->build())
+ return;
+ }
+
+ if (drawCalls.isEmpty())
+ return;
+
+ const int ubufAlign = rhi->ubufAlignment();
+ int vbufOffset = 0;
+ int ibufOffset = 0;
+ int ubufOffset = 0;
+ for (RhiVisualizer::DrawCall &dc : drawCalls) {
+ dc.buf.vbufOffset = aligned(vbufOffset, 4);
+ vbufOffset = dc.buf.vbufOffset + dc.vertex.count * dc.vertex.stride;
+
+ dc.buf.ibufOffset = aligned(ibufOffset, 4);
+ ibufOffset = dc.buf.ibufOffset + dc.index.count * dc.index.stride;
+
+ dc.buf.ubufOffset = aligned(ubufOffset, ubufAlign);
+ ubufOffset = dc.buf.ubufOffset + DrawCall::UBUF_SIZE;
+ }
+
+ ensureBuffer(rhi, &vbuf, QRhiBuffer::VertexBuffer, vbufOffset);
+ if (ibufOffset)
+ ensureBuffer(rhi, &ibuf, QRhiBuffer::IndexBuffer, ibufOffset);
+ const int ubufSize = ubufOffset;
+ ensureBuffer(rhi, &ubuf, QRhiBuffer::UniformBuffer, ubufSize);
+
+ for (RhiVisualizer::DrawCall &dc : drawCalls) {
+ u->updateDynamicBuffer(vbuf, dc.buf.vbufOffset, dc.vertex.count * dc.vertex.stride, dc.vertex.data);
+ dc.buf.vbuf = vbuf;
+ if (dc.index.count) {
+ u->updateDynamicBuffer(ibuf, dc.buf.ibufOffset, dc.index.count * dc.index.stride, dc.index.data);
+ dc.buf.ibuf = ibuf;
+ }
+ u->updateDynamicBuffer(ubuf, dc.buf.ubufOffset, DrawCall::UBUF_SIZE, dc.uniforms.data);
+ }
+
+ if (!srb) {
+ srb = rhi->newShaderResourceBindings();
+ srb->setBindings({ QRhiShaderResourceBinding::uniformBufferWithDynamicOffset(0, ubufVisibility, ubuf, DrawCall::UBUF_SIZE) });
+ if (!srb->build())
+ return;
+ }
+}
+
+void RhiVisualizer::OverdrawVis::releaseResources()
+{
+ delete srb;
+ srb = nullptr;
+
+ delete ubuf;
+ ubuf = nullptr;
+
+ delete ibuf;
+ ibuf = nullptr;
+
+ delete vbuf;
+ vbuf = nullptr;
+
+ delete box.ps;
+ box.ps = nullptr;
+
+ delete box.srb;
+ box.srb = nullptr;
+
+ delete box.ubuf;
+ box.ubuf = nullptr;
+
+ delete box.vbuf;
+ box.vbuf = nullptr;
+}
+
+void RhiVisualizer::OverdrawVis::render(QRhiCommandBuffer *cb)
+{
+ cb->setGraphicsPipeline(box.ps);
+ cb->setShaderResources();
+ QRhiCommandBuffer::VertexInput vb(box.vbuf, 0);
+ cb->setVertexInput(0, 1, &vb);
+ cb->draw(24);
+
+ visualizer->recordDrawCalls(drawCalls, cb, srb, true);
+}
+
+}
+
+QT_END_NAMESPACE
diff --git a/src/quick/scenegraph/coreapi/qsgrhivisualizer_p.h b/src/quick/scenegraph/coreapi/qsgrhivisualizer_p.h
new file mode 100644
index 0000000000..1ce52a81f1
--- /dev/null
+++ b/src/quick/scenegraph/coreapi/qsgrhivisualizer_p.h
@@ -0,0 +1,233 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 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$
+**
+****************************************************************************/
+
+#ifndef QSGRHIVISUALIZER_P_H
+#define QSGRHIVISUALIZER_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include "qsgbatchrenderer_p.h"
+
+QT_BEGIN_NAMESPACE
+
+namespace QSGBatchRenderer
+{
+
+class RhiVisualizer : public Visualizer
+{
+public:
+ RhiVisualizer(Renderer *renderer);
+ ~RhiVisualizer();
+
+ void prepareVisualize() override;
+ void visualize() override;
+
+ void releaseResources() override;
+
+ struct DrawCall
+ {
+ static const int UBUF_SIZE = 152; // visualization.vert/frag
+ struct {
+ char data[UBUF_SIZE]; // matrix, rotation, color, pattern, projection
+ } uniforms;
+ struct {
+ QRhiGraphicsPipeline::Topology topology;
+ QRhiVertexInputAttribute::Format format;
+ int count;
+ int stride;
+ const void *data; // only when using own vbuf
+ } vertex;
+ struct {
+ QRhiCommandBuffer::IndexFormat format;
+ int count;
+ int stride;
+ const void *data; // only when using own ibuf
+ } index;
+ struct {
+ QRhiBuffer *vbuf; // either same for all draw calls and owned by the *Vis, or points to a Batch.Buffer.vbo.buf
+ int vbufOffset;
+ QRhiBuffer *ibuf; // same, but for index
+ int ibufOffset;
+ int ubufOffset;
+ } buf;
+ };
+
+private:
+ QShader m_vs;
+ QShader m_fs;
+
+ void recordDrawCalls(const QVector<DrawCall> &drawCalls,
+ QRhiCommandBuffer *cb,
+ QRhiShaderResourceBindings *srb,
+ bool blendOneOne = false);
+
+ class PipelineCache {
+ public:
+ QRhiGraphicsPipeline *pipeline(RhiVisualizer *visualizer,
+ QRhi *rhi,
+ QRhiShaderResourceBindings *srb,
+ QRhiRenderPassDescriptor *rpDesc,
+ QRhiGraphicsPipeline::Topology topology,
+ QRhiVertexInputAttribute::Format vertexFormat,
+ quint32 vertexStride,
+ bool blendOneOne);
+ void releaseResources();
+ private:
+ struct Pipeline {
+ QRhiGraphicsPipeline::Topology topology;
+ QRhiVertexInputAttribute::Format format;
+ quint32 stride;
+ QRhiGraphicsPipeline *ps;
+ };
+ QVarLengthArray<Pipeline, 16> pipelines;
+ };
+
+ PipelineCache m_pipelines;
+
+ class Fade {
+ public:
+ void prepare(RhiVisualizer *visualizer,
+ QRhi *rhi, QRhiResourceUpdateBatch *u, QRhiRenderPassDescriptor *rpDesc);
+ void releaseResources();
+ void render(QRhiCommandBuffer *cb);
+ private:
+ RhiVisualizer *visualizer;
+ QRhiBuffer *vbuf = nullptr;
+ QRhiBuffer *ubuf = nullptr;
+ QRhiGraphicsPipeline *ps = nullptr;
+ QRhiShaderResourceBindings *srb = nullptr;
+ } m_fade;
+
+ class ChangeVis {
+ public:
+ void prepare(Node *n, RhiVisualizer *visualizer,
+ QRhi *rhi, QRhiResourceUpdateBatch *u);
+ void releaseResources();
+ void render(QRhiCommandBuffer *cb);
+ private:
+ void gather(Node *n);
+ RhiVisualizer *visualizer;
+ QVector<DrawCall> drawCalls;
+ QRhiBuffer *vbuf = nullptr;
+ QRhiBuffer *ibuf = nullptr;
+ QRhiBuffer *ubuf = nullptr;
+ QRhiShaderResourceBindings *srb = nullptr;
+ } m_changeVis;
+
+ class BatchVis {
+ public:
+ void prepare(const QDataBuffer<Batch *> &opaqueBatches,
+ const QDataBuffer<Batch *> &alphaBatches,
+ RhiVisualizer *visualizer,
+ QRhi *rhi, QRhiResourceUpdateBatch *u,
+ bool forceUintIndex);
+ void releaseResources();
+ void render(QRhiCommandBuffer *cb);
+ private:
+ void gather(Batch *b);
+ RhiVisualizer *visualizer;
+ bool forceUintIndex;
+ QVector<DrawCall> drawCalls;
+ QRhiBuffer *ubuf = nullptr;
+ QRhiShaderResourceBindings *srb = nullptr;
+ } m_batchVis;
+
+ class ClipVis {
+ public:
+ void prepare(QSGNode *node, RhiVisualizer *visualizer,
+ QRhi *rhi, QRhiResourceUpdateBatch *u);
+ void releaseResources();
+ void render(QRhiCommandBuffer *cb);
+ private:
+ void gather(QSGNode *node);
+ RhiVisualizer *visualizer;
+ QVector<DrawCall> drawCalls;
+ QRhiBuffer *vbuf = nullptr;
+ QRhiBuffer *ibuf = nullptr;
+ QRhiBuffer *ubuf = nullptr;
+ QRhiShaderResourceBindings *srb = nullptr;
+ } m_clipVis;
+
+ class OverdrawVis {
+ public:
+ void prepare(Node *n, RhiVisualizer *visualizer,
+ QRhi *rhi, QRhiResourceUpdateBatch *u);
+ void releaseResources();
+ void render(QRhiCommandBuffer *cb);
+ private:
+ void gather(Node *n);
+ RhiVisualizer *visualizer;
+ QVector<DrawCall> drawCalls;
+ QRhiBuffer *vbuf = nullptr;
+ QRhiBuffer *ibuf = nullptr;
+ QRhiBuffer *ubuf = nullptr;
+ QRhiShaderResourceBindings *srb = nullptr;
+ float step = 0.0f;
+ QMatrix4x4 rotation;
+ struct {
+ QRhiBuffer *vbuf = nullptr;
+ QRhiBuffer *ubuf = nullptr;
+ QRhiShaderResourceBindings *srb = nullptr;
+ QRhiGraphicsPipeline *ps = nullptr;
+ } box;
+ } m_overdrawVis;
+
+ friend class Fade;
+ friend class PipelineCache;
+ friend class ChangeVis;
+ friend class ClipVis;
+ friend class OverdrawVis;
+};
+
+} // namespace QSGBatchRenderer
+
+QT_END_NAMESPACE
+
+#endif // QSGRHIVISUALIZER_P_H
diff --git a/src/quick/scenegraph/qsgdefaultglyphnode.cpp b/src/quick/scenegraph/qsgdefaultglyphnode.cpp
index ba286b8a36..5bd5cc4891 100644
--- a/src/quick/scenegraph/qsgdefaultglyphnode.cpp
+++ b/src/quick/scenegraph/qsgdefaultglyphnode.cpp
@@ -76,7 +76,7 @@ void QSGDefaultGlyphNode::update()
QMargins margins(0, 0, 0, 0);
if (m_style == QQuickText::Normal) {
- m_material = new QSGTextMaskMaterial(m_context, font);
+ m_material = new QSGTextMaskMaterial(m_context, QVector4D(m_color.redF(), m_color.greenF(), m_color.blueF(), m_color.alphaF()), font);
} else if (m_style == QQuickText::Outline) {
QSGOutlinedTextMaterial *material = new QSGOutlinedTextMaterial(m_context, font);
material->setStyleColor(m_styleColor);
diff --git a/src/quick/scenegraph/qsgdefaultglyphnode_p.cpp b/src/quick/scenegraph/qsgdefaultglyphnode_p.cpp
index 8ce469b39b..db889c3102 100644
--- a/src/quick/scenegraph/qsgdefaultglyphnode_p.cpp
+++ b/src/quick/scenegraph/qsgdefaultglyphnode_p.cpp
@@ -687,11 +687,12 @@ public:
// ***** common material stuff
-QSGTextMaskMaterial::QSGTextMaskMaterial(QSGRenderContext *rc, const QRawFont &font, QFontEngine::GlyphFormat glyphFormat)
+QSGTextMaskMaterial::QSGTextMaskMaterial(QSGRenderContext *rc, const QVector4D &color, const QRawFont &font, QFontEngine::GlyphFormat glyphFormat)
: m_rc(qobject_cast<QSGDefaultRenderContext *>(rc))
, m_texture(nullptr)
, m_glyphCache(nullptr)
, m_font(font)
+ , m_color(color)
{
init(glyphFormat);
}
@@ -701,6 +702,19 @@ QSGTextMaskMaterial::~QSGTextMaskMaterial()
delete m_texture;
}
+void QSGTextMaskMaterial::setColor(const QVector4D &color)
+{
+ if (m_color == color)
+ return;
+
+ m_color = color;
+
+ // If it is an RGB cache, then the pen color is actually part of the cache key
+ // so it has to be updated
+ if (m_glyphCache != nullptr && m_glyphCache->glyphFormat() == QFontEngine::Format_ARGB)
+ updateCache(QFontEngine::Format_ARGB);
+}
+
void QSGTextMaskMaterial::init(QFontEngine::GlyphFormat glyphFormat)
{
Q_ASSERT(m_font.isValid());
@@ -711,6 +725,11 @@ void QSGTextMaskMaterial::init(QFontEngine::GlyphFormat glyphFormat)
Q_ASSERT(m_rc);
m_rhi = m_rc->rhi();
+ updateCache(glyphFormat);
+}
+
+void QSGTextMaskMaterial::updateCache(QFontEngine::GlyphFormat glyphFormat)
+{
// The following piece of code will read/write to the font engine's caches,
// potentially from different threads. However, this is safe because this
// code is only called from QQuickItem::updatePaintNode() which is called
@@ -745,13 +764,13 @@ void QSGTextMaskMaterial::init(QFontEngine::GlyphFormat glyphFormat)
if (!fontEngine->supportsTransformation(glyphCacheTransform))
glyphCacheTransform = QTransform();
- m_glyphCache = fontEngine->glyphCache(cacheKey, glyphFormat, glyphCacheTransform);
-
+ QColor color = glyphFormat == QFontEngine::Format_ARGB ? QColor::fromRgbF(m_color.x(), m_color.y(), m_color.z(), m_color.w()) : QColor();
+ m_glyphCache = fontEngine->glyphCache(cacheKey, glyphFormat, glyphCacheTransform, color);
if (!m_glyphCache || int(m_glyphCache->glyphFormat()) != glyphFormat) {
if (m_rhi)
- m_glyphCache = new QSGRhiTextureGlyphCache(m_rhi, glyphFormat, glyphCacheTransform);
+ m_glyphCache = new QSGRhiTextureGlyphCache(m_rhi, glyphFormat, glyphCacheTransform, color);
else
- m_glyphCache = new QOpenGLTextureGlyphCache(glyphFormat, glyphCacheTransform);
+ m_glyphCache = new QOpenGLTextureGlyphCache(glyphFormat, glyphCacheTransform, color);
fontEngine->setGlyphCache(cacheKey, m_glyphCache.data());
m_rc->registerFontengineForCleanup(fontEngine);
@@ -963,7 +982,7 @@ bool QSGTextMaskMaterial::ensureUpToDate()
QSGStyledTextMaterial::QSGStyledTextMaterial(QSGRenderContext *rc, const QRawFont &font)
- : QSGTextMaskMaterial(rc, font, QFontEngine::Format_A8)
+ : QSGTextMaskMaterial(rc, QVector4D(), font, QFontEngine::Format_A8)
{
}
diff --git a/src/quick/scenegraph/qsgdefaultglyphnode_p_p.h b/src/quick/scenegraph/qsgdefaultglyphnode_p_p.h
index cd1b331278..7d2635794d 100644
--- a/src/quick/scenegraph/qsgdefaultglyphnode_p_p.h
+++ b/src/quick/scenegraph/qsgdefaultglyphnode_p_p.h
@@ -72,15 +72,15 @@ class QSGDefaultRenderContext;
class QSGTextMaskMaterial: public QSGMaterial
{
public:
- QSGTextMaskMaterial(QSGRenderContext *rc, const QRawFont &font, QFontEngine::GlyphFormat glyphFormat = QFontEngine::Format_None);
+ QSGTextMaskMaterial(QSGRenderContext *rc, const QVector4D &color, const QRawFont &font, QFontEngine::GlyphFormat glyphFormat = QFontEngine::Format_None);
virtual ~QSGTextMaskMaterial();
QSGMaterialType *type() const override;
QSGMaterialShader *createShader() const override;
int compare(const QSGMaterial *other) const override;
- void setColor(const QColor &c) { m_color = QVector4D(c.redF(), c.greenF(), c.blueF(), c.alphaF()); }
- void setColor(const QVector4D &color) { m_color = color; }
+ void setColor(const QColor &c) { setColor(QVector4D(c.redF(), c.greenF(), c.blueF(), c.alphaF())); }
+ void setColor(const QVector4D &color);
const QVector4D &color() const { return m_color; }
QSGTexture *texture() const { return m_texture; }
@@ -98,6 +98,7 @@ public:
private:
void init(QFontEngine::GlyphFormat glyphFormat);
+ void updateCache(QFontEngine::GlyphFormat glyphFormat);
QSGDefaultRenderContext *m_rc;
QSGPlainTexture *m_texture;
diff --git a/src/quick/scenegraph/qsgrenderloop.cpp b/src/quick/scenegraph/qsgrenderloop.cpp
index d38b5649c7..ec835fe3bd 100644
--- a/src/quick/scenegraph/qsgrenderloop.cpp
+++ b/src/quick/scenegraph/qsgrenderloop.cpp
@@ -249,14 +249,6 @@ QSGRenderLoop *QSGRenderLoop::instance()
loopType = BasicRenderLoop;
switch (rhiSupport->rhiBackend()) {
- case QRhi::Vulkan:
-#if defined(Q_OS_LINUX) && !defined(Q_OS_ANDROID)
- // ### to be investigated (Mesa/Gnome deadlocks on
- // resize with threaded+Vulkan (but not threaded+GL))
- loopType = BasicRenderLoop;
-#endif
- break;
-
case QRhi::D3D11:
// D3D11 is forced to 'basic' always for now. The threaded loop's model may
// not be suitable for DXGI due to the possibility of having the main
@@ -572,7 +564,7 @@ void QSGGuiThreadRenderLoop::renderWindow(QQuickWindow *window)
QRhiRenderBuffer::UsedWithSwapChainOnly);
cd->swapchain->setWindow(window);
cd->swapchain->setDepthStencil(cd->depthStencilForSwapchain);
- qDebug("MSAA sample count for the swapchain is %d", rhiSampleCount);
+ qCDebug(QSG_LOG_INFO, "MSAA sample count for the swapchain is %d", rhiSampleCount);
cd->swapchain->setSampleCount(rhiSampleCount);
cd->swapchain->setFlags(flags);
cd->rpDescForSwapchain = cd->swapchain->newCompatibleRenderPassDescriptor();
@@ -637,7 +629,7 @@ void QSGGuiThreadRenderLoop::renderWindow(QQuickWindow *window)
const QSize previousOutputSize = cd->swapchain->currentPixelSize();
if (previousOutputSize != effectiveOutputSize || cd->swapchainJustBecameRenderable) {
if (cd->swapchainJustBecameRenderable)
- qDebug("just became exposed");
+ qCDebug(QSG_LOG_RENDERLOOP, "just became exposed");
cd->swapchainJustBecameRenderable = false;
cd->depthStencilForSwapchain->setPixelSize(effectiveOutputSize);
@@ -648,7 +640,7 @@ void QSGGuiThreadRenderLoop::renderWindow(QQuickWindow *window)
if (!cd->hasActiveSwapchain)
qWarning("Failed to build or resize swapchain");
else
- qDebug() << "rhi swapchain size" << effectiveOutputSize;
+ qCDebug(QSG_LOG_RENDERLOOP) << "rhi swapchain size" << effectiveOutputSize;
}
Q_ASSERT(rhi == cd->rhi);
diff --git a/src/quick/scenegraph/qsgrhisupport.cpp b/src/quick/scenegraph/qsgrhisupport.cpp
index 2ca66f23d5..8bae24dc76 100644
--- a/src/quick/scenegraph/qsgrhisupport.cpp
+++ b/src/quick/scenegraph/qsgrhisupport.cpp
@@ -192,8 +192,9 @@ void QSGRhiSupport::applySettings()
default:
break;
}
- qDebug("Using QRhi with backend %s\n graphics API debug/validation layers: %d\n QRhi profiling and debug markers: %d",
- backendName, m_debugLayer, m_profile);
+ qCDebug(QSG_LOG_INFO,
+ "Using QRhi with backend %s\n graphics API debug/validation layers: %d\n QRhi profiling and debug markers: %d",
+ backendName, m_debugLayer, m_profile);
}
QSGRhiSupport *QSGRhiSupport::staticInst()
@@ -527,11 +528,11 @@ void QSGRhiProfileConnection::initialize(QRhi *rhi)
int profPort = qEnvironmentVariableIntValue("QSG_RHI_PROFILE_PORT");
if (!profPort)
profPort = 30667;
- qDebug("Sending RHI profiling output to %s:%d", qPrintable(profHost), profPort);
+ qCDebug(QSG_LOG_INFO, "Sending RHI profiling output to %s:%d", qPrintable(profHost), profPort);
m_profConn.reset(new QTcpSocket);
QObject::connect(m_profConn.data(), QOverload<QAbstractSocket::SocketError>::of(&QAbstractSocket::error), m_profConn.data(),
- [this](QAbstractSocket::SocketError socketError) { qDebug(" RHI profiler error: %d (%s)",
- socketError, qPrintable(m_profConn->errorString())); });
+ [this](QAbstractSocket::SocketError socketError) { qWarning(" RHI profiler error: %d (%s)",
+ socketError, qPrintable(m_profConn->errorString())); });
m_profConn->connectToHost(profHost, profPort);
m_profConn->waitForConnected(); // blocking wait because we want to send stuff already from the init below
rhi->profiler()->setDevice(m_profConn.data());
diff --git a/src/quick/scenegraph/qsgrhitextureglyphcache.cpp b/src/quick/scenegraph/qsgrhitextureglyphcache.cpp
index f181d101c6..99761302e2 100644
--- a/src/quick/scenegraph/qsgrhitextureglyphcache.cpp
+++ b/src/quick/scenegraph/qsgrhitextureglyphcache.cpp
@@ -43,8 +43,9 @@
QT_BEGIN_NAMESPACE
-QSGRhiTextureGlyphCache::QSGRhiTextureGlyphCache(QRhi *rhi, QFontEngine::GlyphFormat format, const QTransform &matrix)
- : QImageTextureGlyphCache(format, matrix),
+QSGRhiTextureGlyphCache::QSGRhiTextureGlyphCache(QRhi *rhi, QFontEngine::GlyphFormat format, const QTransform &matrix,
+ const QColor &color)
+ : QImageTextureGlyphCache(format, matrix, color),
m_rhi(rhi)
{
// Some OpenGL implementations, for instance macOS, have issues with
diff --git a/src/quick/scenegraph/qsgrhitextureglyphcache_p.h b/src/quick/scenegraph/qsgrhitextureglyphcache_p.h
index 1533beb162..75d82de90d 100644
--- a/src/quick/scenegraph/qsgrhitextureglyphcache_p.h
+++ b/src/quick/scenegraph/qsgrhitextureglyphcache_p.h
@@ -59,7 +59,8 @@ QT_BEGIN_NAMESPACE
class QSGRhiTextureGlyphCache : public QImageTextureGlyphCache
{
public:
- QSGRhiTextureGlyphCache(QRhi *rhi, QFontEngine::GlyphFormat format, const QTransform &matrix);
+ QSGRhiTextureGlyphCache(QRhi *rhi, QFontEngine::GlyphFormat format, const QTransform &matrix,
+ const QColor &color = QColor());
~QSGRhiTextureGlyphCache();
void createTextureData(int width, int height) override;
diff --git a/src/quick/scenegraph/qsgthreadedrenderloop.cpp b/src/quick/scenegraph/qsgthreadedrenderloop.cpp
index db8e17a8e6..d1258cf903 100644
--- a/src/quick/scenegraph/qsgthreadedrenderloop.cpp
+++ b/src/quick/scenegraph/qsgthreadedrenderloop.cpp
@@ -686,10 +686,11 @@ void QSGRenderThread::syncAndRender(QImage *grabImage)
syncResultedInChanges = false;
QQuickWindowPrivate *d = QQuickWindowPrivate::get(window);
- bool repaintRequested = (pendingUpdate & RepaintRequest) || d->customRenderStage || grabImage;
- bool syncRequested = (pendingUpdate & SyncRequest) || grabImage;
- bool exposeRequested = (pendingUpdate & ExposeRequest) == ExposeRequest;
+ const bool repaintRequested = (pendingUpdate & RepaintRequest) || d->customRenderStage || grabImage;
+ const bool syncRequested = (pendingUpdate & SyncRequest) || grabImage;
+ const bool exposeRequested = (pendingUpdate & ExposeRequest) == ExposeRequest;
pendingUpdate = 0;
+ const bool grabRequested = grabImage != nullptr;
QQuickWindowPrivate *cd = QQuickWindowPrivate::get(window);
// Begin the frame before syncing -> sync is where we may invoke
@@ -708,7 +709,7 @@ void QSGRenderThread::syncAndRender(QImage *grabImage)
const QSize previousOutputSize = cd->swapchain->currentPixelSize();
if (previousOutputSize != effectiveOutputSize || cd->swapchainJustBecameRenderable) {
if (cd->swapchainJustBecameRenderable)
- qDebug("just became exposed");
+ qCDebug(QSG_LOG_RENDERLOOP, QSG_RT_PAD, "just became exposed");
cd->swapchainJustBecameRenderable = false;
cd->depthStencilForSwapchain->setPixelSize(effectiveOutputSize);
@@ -719,7 +720,7 @@ void QSGRenderThread::syncAndRender(QImage *grabImage)
if (!cd->hasActiveSwapchain)
qWarning("Failed to build or resize swapchain");
else
- qDebug() << "rhi swapchain size" << effectiveOutputSize;
+ qCDebug(QSG_LOG_RENDERLOOP) << "rhi swapchain size" << effectiveOutputSize;
}
Q_ASSERT(rhi == cd->rhi);
@@ -732,13 +733,26 @@ void QSGRenderThread::syncAndRender(QImage *grabImage)
// try again later
if (frameResult == QRhi::FrameOpDeviceLost || frameResult == QRhi::FrameOpSwapChainOutOfDate)
QCoreApplication::postEvent(window, new QEvent(QEvent::Type(QQuickWindowPrivate::FullUpdateRequest)));
+
+ // Before returning we need to ensure the same wake up logic that
+ // would have happened if beginFrame() had suceeded.
+ if (exposeRequested) {
+ qCDebug(QSG_LOG_RENDERLOOP, QSG_RT_PAD, "- bailing out due to failed beginFrame, wake Gui");
+ waitCondition.wakeOne();
+ mutex.unlock();
+ } else if (syncRequested && !grabRequested) {
+ qCDebug(QSG_LOG_RENDERLOOP, QSG_RT_PAD, "- bailing out due to failed beginFrame, wake Gui like sync would do");
+ mutex.lock();
+ waitCondition.wakeOne();
+ mutex.unlock();
+ }
return;
}
}
if (syncRequested) {
qCDebug(QSG_LOG_RENDERLOOP, QSG_RT_PAD, "- updatePending, doing sync");
- sync(exposeRequested, grabImage != nullptr);
+ sync(exposeRequested, grabRequested);
}
#ifndef QSG_NO_RENDER_TIMING
if (profileFrames)
@@ -888,6 +902,10 @@ void QSGRenderThread::run()
QQuickProfiler::registerAnimationCallback();
while (active) {
+#ifdef Q_OS_DARWIN
+ QMacAutoReleasePool frameReleasePool;
+#endif
+
if (window) {
if (enableRhi) {
if (!rhi) {
@@ -922,7 +940,7 @@ void QSGRenderThread::run()
QRhiRenderBuffer::UsedWithSwapChainOnly);
cd->swapchain->setWindow(window);
cd->swapchain->setDepthStencil(cd->depthStencilForSwapchain);
- qDebug("MSAA sample count for the swapchain is %d", rhiSampleCount);
+ qCDebug(QSG_LOG_INFO, "MSAA sample count for the swapchain is %d", rhiSampleCount);
cd->swapchain->setSampleCount(rhiSampleCount);
cd->swapchain->setFlags(flags);
cd->rpDescForSwapchain = cd->swapchain->newCompatibleRenderPassDescriptor();
diff --git a/src/quick/scenegraph/scenegraph.pri b/src/quick/scenegraph/scenegraph.pri
index 3390d2b87a..ee9ea0f5ed 100644
--- a/src/quick/scenegraph/scenegraph.pri
+++ b/src/quick/scenegraph/scenegraph.pri
@@ -37,9 +37,13 @@ SOURCES += \
qtConfig(opengl(es1|es2)?) {
HEADERS += \
- $$PWD/coreapi/qsgbatchrenderer_p.h
+ $$PWD/coreapi/qsgbatchrenderer_p.h \
+ $$PWD/coreapi/qsgopenglvisualizer_p.h \
+ $$PWD/coreapi/qsgrhivisualizer_p.h
SOURCES += \
$$PWD/coreapi/qsgbatchrenderer.cpp \
+ $$PWD/coreapi/qsgopenglvisualizer.cpp \
+ $$PWD/coreapi/qsgrhivisualizer.cpp \
$$PWD/coreapi/qsgshaderrewriter.cpp
}
diff --git a/src/quick/scenegraph/scenegraph.qrc b/src/quick/scenegraph/scenegraph.qrc
index c7257c17a0..e409f07610 100644
--- a/src/quick/scenegraph/scenegraph.qrc
+++ b/src/quick/scenegraph/scenegraph.qrc
@@ -121,5 +121,8 @@
<file>shaders_ng/shadereffect.frag.qsb</file>
<file>shaders_ng/sprite.vert.qsb</file>
<file>shaders_ng/sprite.frag.qsb</file>
+
+ <file>shaders_ng/visualization.vert.qsb</file>
+ <file>shaders_ng/visualization.frag.qsb</file>
</qresource>
</RCC>
diff --git a/src/quick/scenegraph/shaders_ng/compile.bat b/src/quick/scenegraph/shaders_ng/compile.bat
index 8ce42f3483..a0c74c22c7 100755
--- a/src/quick/scenegraph/shaders_ng/compile.bat
+++ b/src/quick/scenegraph/shaders_ng/compile.bat
@@ -82,3 +82,5 @@ qsb -b --glsl "150,120,100 es" --hlsl 50 --msl 12 -o shadereffect.vert.qsb shade
qsb --glsl "150,120,100 es" --hlsl 50 --msl 12 -o shadereffect.frag.qsb shadereffect.frag
qsb -b --glsl "150,120,100 es" --hlsl 50 --msl 12 -o sprite.vert.qsb sprite.vert
qsb --glsl "150,120,100 es" --hlsl 50 --msl 12 -o sprite.frag.qsb sprite.frag
+qsb --glsl "150,120,100 es" --hlsl 50 --msl 12 -o visualization.vert.qsb visualization.vert
+qsb --glsl "150,120,100 es" --hlsl 50 --msl 12 -o visualization.frag.qsb visualization.frag
diff --git a/src/quick/scenegraph/shaders_ng/visualization.frag b/src/quick/scenegraph/shaders_ng/visualization.frag
new file mode 100644
index 0000000000..29f718fe5d
--- /dev/null
+++ b/src/quick/scenegraph/shaders_ng/visualization.frag
@@ -0,0 +1,19 @@
+#version 440
+
+layout(location = 0) in vec2 pos;
+layout(location = 0) out vec4 fragColor;
+
+layout(std140, binding = 0) uniform buf {
+ mat4 matrix;
+ mat4 rotation;
+ vec4 color;
+ float pattern;
+ int projection;
+} ubuf;
+
+void main(void)
+{
+ vec4 c = ubuf.color;
+ c.xyz += pow(max(sin(pos.x + pos.y), 0.0), 2.0) * ubuf.pattern * 0.25;
+ fragColor = c;
+}
diff --git a/src/quick/scenegraph/shaders_ng/visualization.frag.qsb b/src/quick/scenegraph/shaders_ng/visualization.frag.qsb
new file mode 100644
index 0000000000..eadad927dc
--- /dev/null
+++ b/src/quick/scenegraph/shaders_ng/visualization.frag.qsb
Binary files differ
diff --git a/src/quick/scenegraph/shaders_ng/visualization.vert b/src/quick/scenegraph/shaders_ng/visualization.vert
new file mode 100644
index 0000000000..c29492417a
--- /dev/null
+++ b/src/quick/scenegraph/shaders_ng/visualization.vert
@@ -0,0 +1,28 @@
+#version 440
+
+layout(location = 0) in vec4 v;
+layout(location = 0) out vec2 pos;
+
+layout(std140, binding = 0) uniform buf {
+ mat4 matrix;
+ mat4 rotation;
+ vec4 color;
+ float pattern;
+ int projection;
+} ubuf;
+
+out gl_PerVertex { vec4 gl_Position; };
+
+void main()
+{
+ vec4 p = ubuf.matrix * v;
+
+ if (ubuf.projection != 0) {
+ vec4 proj = ubuf.rotation * p;
+ gl_Position = vec4(proj.x, proj.y, 0, proj.z);
+ } else {
+ gl_Position = p;
+ }
+
+ pos = v.xy * 1.37;
+}
diff --git a/src/quick/scenegraph/shaders_ng/visualization.vert.qsb b/src/quick/scenegraph/shaders_ng/visualization.vert.qsb
new file mode 100644
index 0000000000..bd89847dd3
--- /dev/null
+++ b/src/quick/scenegraph/shaders_ng/visualization.vert.qsb
Binary files differ
diff --git a/src/quick/scenegraph/util/qsgdefaultpainternode_p.h b/src/quick/scenegraph/util/qsgdefaultpainternode_p.h
index a86f7397be..dc103648ff 100644
--- a/src/quick/scenegraph/util/qsgdefaultpainternode_p.h
+++ b/src/quick/scenegraph/util/qsgdefaultpainternode_p.h
@@ -159,9 +159,6 @@ private:
QSize m_textureSize;
QRect m_dirtyRect;
QColor m_fillColor;
-#if QT_VERSION >= 0x060000
-#warning "Remove m_contentsScale and assume 1 everywhere"
-#endif
qreal m_contentsScale;
bool m_dirtyContents : 1;