aboutsummaryrefslogtreecommitdiffstats
path: root/src/quick/scenegraph/coreapi/qsgbatchrenderer.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/quick/scenegraph/coreapi/qsgbatchrenderer.cpp')
-rw-r--r--src/quick/scenegraph/coreapi/qsgbatchrenderer.cpp175
1 files changed, 140 insertions, 35 deletions
diff --git a/src/quick/scenegraph/coreapi/qsgbatchrenderer.cpp b/src/quick/scenegraph/coreapi/qsgbatchrenderer.cpp
index f25e144674..79b5de72c0 100644
--- a/src/quick/scenegraph/coreapi/qsgbatchrenderer.cpp
+++ b/src/quick/scenegraph/coreapi/qsgbatchrenderer.cpp
@@ -40,11 +40,13 @@
****************************************************************************/
#include "qsgbatchrenderer_p.h"
+#include <private/qsgshadersourcebuilder_p.h>
#include <QtCore/QElapsedTimer>
#include <QtGui/QGuiApplication>
#include <QtGui/QOpenGLFramebufferObject>
+#include <QtGui/QOpenGLVertexArrayObject>
#include <private/qqmlprofilerservice_p.h>
@@ -56,7 +58,7 @@
QT_BEGIN_NAMESPACE
-extern QByteArray qsgShaderRewriter_insertZAttributes(const char *input);
+extern QByteArray qsgShaderRewriter_insertZAttributes(const char *input, QSurfaceFormat::OpenGLContextProfile profile);
namespace QSGBatchRenderer
{
@@ -112,6 +114,7 @@ struct QMatrix4x4_Accessor
int flagBits;
static bool isTranslate(const QMatrix4x4 &m) { return ((const QMatrix4x4_Accessor &) m).flagBits <= 0x1; }
+ static bool isScale(const QMatrix4x4 &m) { return ((const QMatrix4x4_Accessor &) m).flagBits <= 0x2; }
static bool is2DSafe(const QMatrix4x4 &m) { return ((const QMatrix4x4_Accessor &) m).flagBits < 0x8; }
};
@@ -130,10 +133,12 @@ ShaderManager::Shader *ShaderManager::prepareMaterial(QSGMaterial *material)
#endif
QSGMaterialShader *s = material->createShader();
+ QOpenGLContext *ctx = QOpenGLContext::currentContext();
+ QSurfaceFormat::OpenGLContextProfile profile = ctx->format().profile();
QOpenGLShaderProgram *p = s->program();
p->addShaderFromSourceCode(QOpenGLShader::Vertex,
- qsgShaderRewriter_insertZAttributes(s->vertexShader()));
+ qsgShaderRewriter_insertZAttributes(s->vertexShader(), profile));
p->addShaderFromSourceCode(QOpenGLShader::Fragment,
s->fragmentShader());
@@ -283,7 +288,6 @@ Updater::Updater(Renderer *r)
void Updater::updateStates(QSGNode *n)
{
- m_toplevel_alpha = 1;
m_current_clip = 0;
m_added = 0;
@@ -389,6 +393,9 @@ void Updater::visitOpacityNode(Node *n)
if (was != is) {
renderer->m_rebuild = Renderer::FullRebuild;
n->isOpaque = is;
+ } else if (!is) {
+ renderer->invalidateAlphaBatchesForRoot(m_roots.last());
+ renderer->m_rebuild |= Renderer::BuildBatches;
}
++m_force_update;
SHADOWNODE_TRAVERSE(n) visitNode(*child);
@@ -530,6 +537,38 @@ int qsg_positionAttribute(QSGGeometry *g) {
return -1;
}
+
+void Rect::map(const QMatrix4x4 &matrix)
+{
+ const float *m = matrix.constData();
+ if (QMatrix4x4_Accessor::isScale(matrix)) {
+ tl.x = tl.x * m[0] + m[12];
+ tl.y = tl.y * m[5] + m[13];
+ br.x = br.x * m[0] + m[12];
+ br.y = br.y * m[5] + m[13];
+ if (tl.x > br.x)
+ qSwap(tl.x, br.x);
+ if (tl.y > br.y)
+ qSwap(tl.y, br.y);
+ } else {
+ Pt mtl = tl;
+ Pt mtr = { br.x, tl.y };
+ Pt mbl = { tl.x, br.y };
+ Pt mbr = br;
+
+ mtl.map(matrix);
+ mtr.map(matrix);
+ mbl.map(matrix);
+ mbr.map(matrix);
+
+ set(FLT_MAX, FLT_MAX, -FLT_MAX, -FLT_MAX);
+ (*this) |= mtl;
+ (*this) |= mtr;
+ (*this) |= mbl;
+ (*this) |= mbr;
+ }
+}
+
void Element::computeBounds()
{
Q_ASSERT(!boundsComputed);
@@ -550,6 +589,17 @@ void Element::computeBounds()
vd += g->sizeOfVertex();
}
bounds.map(*node->matrix());
+
+ if (!qIsFinite(bounds.tl.x))
+ bounds.tl.x = -FLT_MAX;
+ if (!qIsFinite(bounds.tl.y))
+ bounds.tl.y = -FLT_MAX;
+ if (!qIsFinite(bounds.br.x))
+ bounds.br.x = FLT_MAX;
+ if (!qIsFinite(bounds.br.y))
+ bounds.br.y = FLT_MAX;
+
+ boundsOutsideFloatRange = bounds.isOutsideFloatRange();
}
RenderNodeElement::~RenderNodeElement()
@@ -650,12 +700,26 @@ bool Batch::isTranslateOnlyToRoot() const {
/*
* Iterates through all the nodes in the batch and returns true if the
- * nodes are all "2D safe" meaning that they can be merged and that
- * the value in the z coordinate is of no consequence.
+ * nodes are all safe to batch. There are two separate criteria:
+ *
+ * - The matrix is such that the z component of the result is of no
+ * consequence.
+ *
+ * - The bounds are inside the stable floating point range. This applies
+ * to desktop only where we in this case can trigger a fallback to
+ * unmerged in which case we pass the geometry straight through and
+ * just apply the matrix.
+ *
+ * NOTE: This also means a slight performance impact for geometries which
+ * are defined to be outside the stable floating point range and still
+ * use single precision float, but given that this implicitly fixes
+ * huge lists and tables, it is worth it.
*/
-bool Batch::allMatricesAre2DSafe() const {
+bool Batch::isSafeToBatch() const {
Element *e = first;
while (e) {
+ if (e->boundsOutsideFloatRange)
+ return false;
if (!QMatrix4x4_Accessor::is2DSafe(*e->node->matrix()))
return false;
e = e->nextInBatch;
@@ -683,7 +747,7 @@ static int qsg_countNodesInBatches(const QDataBuffer<Batch *> &batches)
return sum;
}
-Renderer::Renderer(QSGContext *ctx)
+Renderer::Renderer(QSGRenderContext *ctx)
: QSGRenderer(ctx)
, m_opaqueRenderList(64)
, m_alphaRenderList(64)
@@ -700,6 +764,7 @@ Renderer::Renderer(QSGContext *ctx)
, m_zRange(0)
, m_currentMaterial(0)
, m_currentShader(0)
+ , m_vao(0)
{
setNodeUpdater(new Updater(this));
@@ -740,6 +805,13 @@ Renderer::Renderer(QSGContext *ctx)
qDebug() << "Batch thresholds: nodes:" << m_batchNodeThreshold << " vertices:" << m_batchVertexThreshold;
qDebug() << "Using buffer strategy:" << (m_bufferStrategy == GL_STATIC_DRAW ? "static" : (m_bufferStrategy == GL_DYNAMIC_DRAW ? "dynamic" : "stream"));
}
+
+ // If rendering with an OpenGL Core profile context, we need to create a VAO
+ // to hold our vertex specification state.
+ if (context()->openglContext()->format().profile() == QSurfaceFormat::CoreProfile) {
+ m_vao = new QOpenGLVertexArrayObject(this);
+ m_vao->create();
+ }
}
static void qsg_wipeBuffer(Buffer *buffer, QOpenGLFunctions *funcs)
@@ -1344,6 +1416,15 @@ void Renderer::buildRenderListsFromScratch()
buildRenderLists(rootNode());
}
+void Renderer::invalidateAlphaBatchesForRoot(Node *root)
+{
+ for (int i=0; i<m_alphaBatches.size(); ++i) {
+ Batch *b = m_alphaBatches.at(i);
+ if (b->root == root || root == 0)
+ b->invalidate();
+ }
+}
+
/* Clean up batches by making it a consecutive list of "valid"
* batches and moving all invalidated batches to the batches pool.
*/
@@ -1491,6 +1572,13 @@ void Renderer::prepareAlphaBatches()
ej->batch = batch;
next->nextInBatch = ej;
next = ej;
+ } else {
+ /* When we come across a compatible element which hits an overlap, we
+ * need to stop the batch right away. We cannot add more elements
+ * to the current batch as they will be rendered before the batch that the
+ * current 'ej' will be added to.
+ */
+ break;
}
} else {
overlapBounds |= ej->bounds;
@@ -1616,7 +1704,7 @@ void Renderer::uploadBatch(Batch *b)
&& (((gn->activeMaterial()->flags() & QSGMaterial::RequiresDeterminant) == 0)
|| (((gn->activeMaterial()->flags() & QSGMaterial_RequiresFullMatrixBit) == 0) && b->isTranslateOnlyToRoot())
)
- && b->allMatricesAre2DSafe();
+ && b->isSafeToBatch();
b->merged = canMerge;
@@ -1890,7 +1978,14 @@ void Renderer::renderMergedBatch(const Batch *batch)
updateClip(gn->clipList(), batch);
glBindBuffer(GL_ARRAY_BUFFER, batch->vbo.id);
- glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, batch->vbo.id);
+
+ char *indexBase = 0;
+ if (m_context->hasBrokenIndexBufferObjects()) {
+ indexBase = batch->vbo.data;
+ glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
+ } else {
+ glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, batch->vbo.id);
+ }
QSGMaterial *material = gn->activeMaterial();
@@ -1925,7 +2020,7 @@ void Renderer::renderMergedBatch(const Batch *batch)
}
glVertexAttribPointer(sms->pos_order, 1, GL_FLOAT, false, 0, (void *) (qintptr) (draw.zorders));
- glDrawElements(g->drawingMode(), draw.indexCount, GL_UNSIGNED_SHORT, (void *) (qintptr) (draw.indices));
+ glDrawElements(g->drawingMode(), draw.indexCount, GL_UNSIGNED_SHORT, (void *) (qintptr) (indexBase + draw.indices));
}
}
@@ -1958,8 +2053,15 @@ void Renderer::renderUnmergedBatch(const Batch *batch)
updateClip(gn->clipList(), batch);
glBindBuffer(GL_ARRAY_BUFFER, batch->vbo.id);
- if (batch->indexCount)
- glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, batch->vbo.id);
+ char *indexBase = 0;
+ if (batch->indexCount) {
+ if (m_context->hasBrokenIndexBufferObjects()) {
+ indexBase = batch->vbo.data;
+ glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
+ } else {
+ glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, batch->vbo.id);
+ }
+ }
// We always have dirty matrix as all batches are at a unique z range.
QSGMaterialShader::RenderState::DirtyStates dirty = QSGMaterialShader::RenderState::DirtyMatrix;
@@ -1978,7 +2080,7 @@ void Renderer::renderUnmergedBatch(const Batch *batch)
}
int vOffset = 0;
- int iOffset = batch->vertexCount * gn->geometry()->sizeOfVertex();
+ char *iOffset = indexBase + batch->vertexCount * gn->geometry()->sizeOfVertex();
QMatrix4x4 rootMatrix = batch->root ? matrixForRoot(batch->root) : QMatrix4x4();
@@ -1994,7 +2096,9 @@ void Renderer::renderUnmergedBatch(const Batch *batch)
program->updateState(state(dirty), material, m_currentMaterial);
- m_currentMaterial = gn->activeMaterial();
+ // We don't need to bother with asking each node for its material as they
+ // are all identical (compare==0) since they are in the same batch.
+ m_currentMaterial = material;
QSGGeometry* g = gn->geometry();
char const *const *attrNames = program->attributeNames();
@@ -2009,7 +2113,7 @@ void Renderer::renderUnmergedBatch(const Batch *batch)
}
if (g->indexCount())
- glDrawElements(g->drawingMode(), g->indexCount(), g->indexType(), (void *) (qintptr) iOffset);
+ glDrawElements(g->drawingMode(), g->indexCount(), g->indexType(), iOffset);
else
glDrawArrays(g->drawingMode(), 0, g->vertexCount());
@@ -2031,14 +2135,13 @@ void Renderer::renderBatches()
<< " -> Alpha: " << qsg_countNodesInBatches(m_alphaBatches) << " nodes in " << m_alphaBatches.size() << " batches...";
}
- QRect r = viewportRect();
- glViewport(r.x(), deviceRect().bottom() - r.bottom(), r.width(), r.height());
-
for (QHash<QSGRenderNode *, RenderNodeElement *>::const_iterator it = m_renderNodeElements.constBegin();
it != m_renderNodeElements.constEnd(); ++it) {
prepareRenderNode(it.value());
}
+ QRect r = viewportRect();
+ glViewport(r.x(), deviceRect().bottom() - r.bottom(), r.width(), r.height());
glClearColor(clearColor().redF(), clearColor().greenF(), clearColor().blueF(), clearColor().alphaF());
#if defined(QT_OPENGL_ES)
glClearDepthf(1);
@@ -2126,6 +2229,17 @@ void Renderer::deleteRemovedElements()
m_elementsToDelete.reset();
}
+void Renderer::preprocess()
+{
+ // Bind our VAO. It's important that we do this here as the
+ // QSGRenderer::preprocess() call may well do work that requires
+ // a bound VAO.
+ if (m_vao)
+ m_vao->bind();
+
+ QSGRenderer::preprocess();
+}
+
void Renderer::render()
{
if (Q_UNLIKELY(debug_dump)) {
@@ -2233,6 +2347,9 @@ void Renderer::render()
renderBatches();
m_rebuild = 0;
+
+ if (m_vao)
+ m_vao->release();
}
void Renderer::prepareRenderNode(RenderNodeElement *e)
@@ -2310,23 +2427,11 @@ void Renderer::renderRenderNode(Batch *batch)
if (!m_shaderManager->blitProgram) {
m_shaderManager->blitProgram = new QOpenGLShaderProgram();
- const char *vs =
- "attribute highp vec4 av; "
- "attribute highp vec2 at; "
- "varying highp vec2 t; "
- "void main() { "
- " gl_Position = av; "
- " t = at; "
- "} ";
- const char *fs =
- "uniform lowp sampler2D tex; "
- "varying highp vec2 t; "
- "void main() { "
- " gl_FragColor = texture2D(tex, t); "
- "} ";
-
- m_shaderManager->blitProgram->addShaderFromSourceCode(QOpenGLShader::Vertex, vs);
- m_shaderManager->blitProgram->addShaderFromSourceCode(QOpenGLShader::Fragment, fs);
+
+ QSGShaderSourceBuilder::initializeProgramFromFiles(
+ m_shaderManager->blitProgram,
+ QStringLiteral(":/scenegraph/shaders/rendernode.vert"),
+ QStringLiteral(":/scenegraph/shaders/rendernode.frag"));
m_shaderManager->blitProgram->bindAttributeLocation("av", 0);
m_shaderManager->blitProgram->bindAttributeLocation("at", 1);
m_shaderManager->blitProgram->link();