diff --git a/src/quick/scenegraph/coreapi/qsgbatchrenderer.cpp b/src/quick/scenegraph/coreapi/qsgbatchrenderer.cpp index e68b001ad19e4ccbd18ad05cd210a8c13a5ca591..4119dfb5307bfcec050e8b8b91ef25a2e5d84b49 100644 --- a/src/quick/scenegraph/coreapi/qsgbatchrenderer.cpp +++ b/src/quick/scenegraph/coreapi/qsgbatchrenderer.cpp @@ -750,11 +750,14 @@ Renderer::Renderer(QSGRenderContext *ctx) , m_renderOrderRebuildUpper(-1) , m_currentMaterial(0) , m_currentShader(0) + , m_currentStencilValue(0) + , m_clipMatrixId(0) , m_currentClip(0) , m_currentClipType(NoClip) , m_vao(0) , m_visualizeMode(VisualizeNothing) { + initializeOpenGLFunctions(); setNodeUpdater(new Updater(this)); m_shaderManager = ctx->findChild<ShaderManager *>(QStringLiteral("__qt_ShaderManager"), Qt::FindDirectChildrenOnly); @@ -1912,6 +1915,132 @@ void Renderer::uploadBatch(Batch *b) b->uploadedThisFrame = true; } +/*! + * Convenience function to set up the stencil buffer for clipping based on \a clip. + * + * If the clip is a pixel aligned rectangle, this function will use glScissor instead + * of stencil. + */ +Renderer::ClipType Renderer::updateStencilClip(const QSGClipNode *clip) +{ + if (!clip) { + glDisable(GL_STENCIL_TEST); + glDisable(GL_SCISSOR_TEST); + return NoClip; + } + + ClipType clipType = NoClip; + + glDisable(GL_SCISSOR_TEST); + + m_currentStencilValue = 0; + m_currentScissorRect = QRect(); + while (clip) { + QMatrix4x4 m = m_current_projection_matrix; + if (clip->matrix()) + m *= *clip->matrix(); + + // TODO: Check for multisampling and pixel grid alignment. + bool isRectangleWithNoPerspective = clip->isRectangular() + && qFuzzyIsNull(m(3, 0)) && qFuzzyIsNull(m(3, 1)); + bool noRotate = qFuzzyIsNull(m(0, 1)) && qFuzzyIsNull(m(1, 0)); + bool isRotate90 = qFuzzyIsNull(m(0, 0)) && qFuzzyIsNull(m(1, 1)); + + if (isRectangleWithNoPerspective && (noRotate || isRotate90)) { + QRectF bbox = clip->clipRect(); + qreal invW = 1 / m(3, 3); + qreal fx1, fy1, fx2, fy2; + if (noRotate) { + fx1 = (bbox.left() * m(0, 0) + m(0, 3)) * invW; + fy1 = (bbox.bottom() * m(1, 1) + m(1, 3)) * invW; + fx2 = (bbox.right() * m(0, 0) + m(0, 3)) * invW; + fy2 = (bbox.top() * m(1, 1) + m(1, 3)) * invW; + } else { + Q_ASSERT(isRotate90); + fx1 = (bbox.bottom() * m(0, 1) + m(0, 3)) * invW; + fy1 = (bbox.left() * m(1, 0) + m(1, 3)) * invW; + fx2 = (bbox.top() * m(0, 1) + m(0, 3)) * invW; + fy2 = (bbox.right() * m(1, 0) + m(1, 3)) * invW; + } + + if (fx1 > fx2) + qSwap(fx1, fx2); + if (fy1 > fy2) + qSwap(fy1, fy2); + + QRect deviceRect = this->deviceRect(); + + GLint ix1 = qRound((fx1 + 1) * deviceRect.width() * qreal(0.5)); + GLint iy1 = qRound((fy1 + 1) * deviceRect.height() * qreal(0.5)); + GLint ix2 = qRound((fx2 + 1) * deviceRect.width() * qreal(0.5)); + GLint iy2 = qRound((fy2 + 1) * deviceRect.height() * qreal(0.5)); + + if (!(clipType & ScissorClip)) { + m_currentScissorRect = QRect(ix1, iy1, ix2 - ix1, iy2 - iy1); + glEnable(GL_SCISSOR_TEST); + clipType |= ScissorClip; + } else { + m_currentScissorRect &= QRect(ix1, iy1, ix2 - ix1, iy2 - iy1); + } + glScissor(m_currentScissorRect.x(), m_currentScissorRect.y(), + m_currentScissorRect.width(), m_currentScissorRect.height()); + } else { + if (!(clipType & StencilClip)) { + if (!m_clipProgram.isLinked()) { + QSGShaderSourceBuilder::initializeProgramFromFiles( + &m_clipProgram, + QStringLiteral(":/scenegraph/shaders/stencilclip.vert"), + QStringLiteral(":/scenegraph/shaders/stencilclip.frag")); + m_clipProgram.bindAttributeLocation("vCoord", 0); + m_clipProgram.link(); + m_clipMatrixId = m_clipProgram.uniformLocation("matrix"); + } + + glClearStencil(0); + glClear(GL_STENCIL_BUFFER_BIT); + glEnable(GL_STENCIL_TEST); + glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE); + glDepthMask(GL_FALSE); + + m_clipProgram.bind(); + m_clipProgram.enableAttributeArray(0); + + clipType |= StencilClip; + } + + glStencilFunc(GL_EQUAL, m_currentStencilValue, 0xff); // stencil test, ref, test mask + glStencilOp(GL_KEEP, GL_KEEP, GL_INCR); // stencil fail, z fail, z pass + + const QSGGeometry *g = clip->geometry(); + Q_ASSERT(g->attributeCount() > 0); + const QSGGeometry::Attribute *a = g->attributes(); + glVertexAttribPointer(0, a->tupleSize, a->type, GL_FALSE, g->sizeOfVertex(), g->vertexData()); + + m_clipProgram.setUniformValue(m_clipMatrixId, m); + if (g->indexCount()) { + glDrawElements(g->drawingMode(), g->indexCount(), g->indexType(), g->indexData()); + } else { + glDrawArrays(g->drawingMode(), 0, g->vertexCount()); + } + + ++m_currentStencilValue; + } + + clip = clip->clipList(); + } + + if (clipType & StencilClip) { + m_clipProgram.disableAttributeArray(0); + glStencilFunc(GL_EQUAL, m_currentStencilValue, 0xff); // stencil test, ref, test mask + glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP); // stencil fail, z fail, z pass + bindable()->reactivate(); + } else { + glDisable(GL_STENCIL_TEST); + } + + return clipType; +} + void Renderer::updateClip(const QSGClipNode *clipList, const Batch *batch) { if (clipList != m_currentClip && Q_LIKELY(!debug_noclip)) { @@ -2485,8 +2614,8 @@ void Renderer::renderRenderNode(Batch *batch) state.projectionMatrix = ± state.scissorEnabled = m_currentClipType & ScissorClip; state.stencilEnabled = m_currentClipType & StencilClip; - state.scissorRect = m_current_scissor_rect; - state.stencilValue = m_current_stencil_value; + state.scissorRect = m_currentScissorRect; + state.stencilValue = m_currentStencilValue; QSGNode *xform = e->renderNode->parent(); QMatrix4x4 matrix; diff --git a/src/quick/scenegraph/coreapi/qsgbatchrenderer_p.h b/src/quick/scenegraph/coreapi/qsgbatchrenderer_p.h index 89a33cb8c474b741aac6be2ef7aac22b28a3de06..574c6df810400e1db305e3e318eddb418e5be6e9 100644 --- a/src/quick/scenegraph/coreapi/qsgbatchrenderer_p.h +++ b/src/quick/scenegraph/coreapi/qsgbatchrenderer_p.h @@ -406,7 +406,7 @@ public: QSGRenderContext *context; }; -class Q_QUICK_PRIVATE_EXPORT Renderer : public QSGRenderer +class Q_QUICK_PRIVATE_EXPORT Renderer : public QSGRenderer, public QOpenGLFunctions { public: Renderer(QSGRenderContext *); @@ -426,6 +426,14 @@ protected: void render(); private: + enum ClipTypeBit + { + NoClip = 0x00, + ScissorClip = 0x01, + StencilClip = 0x02 + }; + Q_DECLARE_FLAGS(ClipType, ClipTypeBit) + enum RebuildFlag { BuildRenderListsForTaggedRoots = 0x0001, BuildRenderLists = 0x0002, @@ -457,6 +465,7 @@ private: void renderBatches(); void renderMergedBatch(const Batch *batch); void renderUnmergedBatch(const Batch *batch); + ClipType updateStencilClip(const QSGClipNode *clip); void updateClip(const QSGClipNode *clipList, const Batch *batch); const QMatrix4x4 &matrixForRoot(Node *node); void renderRenderNode(Batch *batch); @@ -518,6 +527,11 @@ private: QSGMaterial *m_currentMaterial; QSGMaterialShader *m_currentProgram; ShaderManager::Shader *m_currentShader; + + QRect m_currentScissorRect; + int m_currentStencilValue; + QOpenGLShaderProgram m_clipProgram; + int m_clipMatrixId; const QSGClipNode *m_currentClip; ClipType m_currentClipType; diff --git a/src/quick/scenegraph/coreapi/qsgrenderer.cpp b/src/quick/scenegraph/coreapi/qsgrenderer.cpp index 23afba368fbf771bb13b7d165a75a3fc53e80450..c05650772972972895665915e64977a07e97c49f 100644 --- a/src/quick/scenegraph/coreapi/qsgrenderer.cpp +++ b/src/quick/scenegraph/coreapi/qsgrenderer.cpp @@ -48,7 +48,6 @@ #include <private/qsgadaptationlayer_p.h> #include <private/qsgshadersourcebuilder_p.h> -#include <QOpenGLShaderProgram> #include <qopenglframebufferobject.h> #include <QtGui/qguiapplication.h> @@ -125,7 +124,6 @@ QSGRenderer::QSGRenderer(QSGRenderContext *context) , m_current_opacity(1) , m_current_determinant(1) , m_device_pixel_ratio(1) - , m_current_stencil_value(0) , m_context(context) , m_root_node(0) , m_node_updater(0) @@ -134,7 +132,6 @@ QSGRenderer::QSGRenderer(QSGRenderContext *context) , m_mirrored(false) , m_is_rendering(false) { - initializeOpenGLFunctions(); } @@ -224,10 +221,10 @@ void QSGRenderer::renderScene(const QSGBindable &bindable) // Sanity check that attribute registers are disabled if (qsg_sanity_check) { GLint count = 0; - glGetIntegerv(GL_MAX_VERTEX_ATTRIBS, &count); + QOpenGLContext::currentContext()->functions()->glGetIntegerv(GL_MAX_VERTEX_ATTRIBS, &count); GLint enabled; for (int i=0; i<count; ++i) { - glGetVertexAttribiv(i, GL_VERTEX_ATTRIB_ARRAY_ENABLED, &enabled); + QOpenGLContext::currentContext()->functions()->glGetVertexAttribiv(i, GL_VERTEX_ATTRIB_ARRAY_ENABLED, &enabled); if (enabled) { qWarning("QSGRenderer: attribute %d is enabled, this can lead to memory corruption and crashes.", i); } @@ -238,7 +235,6 @@ void QSGRenderer::renderScene(const QSGBindable &bindable) if (profileFrames) renderTime = frameTimer.nsecsElapsed(); - glDisable(GL_SCISSOR_TEST); m_is_rendering = false; m_changed_emitted = false; m_bindable = 0; @@ -355,133 +351,6 @@ void QSGRenderer::removeNodesToPreprocess(QSGNode *node) } -/*! - Convenience function to set up the stencil buffer for clipping based on \a clip. - - If the clip is a pixel aligned rectangle, this function will use glScissor instead - of stencil. - */ - -QSGRenderer::ClipType QSGRenderer::updateStencilClip(const QSGClipNode *clip) -{ - if (!clip) { - glDisable(GL_STENCIL_TEST); - glDisable(GL_SCISSOR_TEST); - return NoClip; - } - - ClipType clipType = NoClip; - - glDisable(GL_SCISSOR_TEST); - - m_current_stencil_value = 0; - m_current_scissor_rect = QRect(); - while (clip) { - QMatrix4x4 m = m_current_projection_matrix; - if (clip->matrix()) - m *= *clip->matrix(); - - // TODO: Check for multisampling and pixel grid alignment. - bool isRectangleWithNoPerspective = clip->isRectangular() - && qFuzzyIsNull(m(3, 0)) && qFuzzyIsNull(m(3, 1)); - bool noRotate = qFuzzyIsNull(m(0, 1)) && qFuzzyIsNull(m(1, 0)); - bool isRotate90 = qFuzzyIsNull(m(0, 0)) && qFuzzyIsNull(m(1, 1)); - - if (isRectangleWithNoPerspective && (noRotate || isRotate90)) { - QRectF bbox = clip->clipRect(); - qreal invW = 1 / m(3, 3); - qreal fx1, fy1, fx2, fy2; - if (noRotate) { - fx1 = (bbox.left() * m(0, 0) + m(0, 3)) * invW; - fy1 = (bbox.bottom() * m(1, 1) + m(1, 3)) * invW; - fx2 = (bbox.right() * m(0, 0) + m(0, 3)) * invW; - fy2 = (bbox.top() * m(1, 1) + m(1, 3)) * invW; - } else { - Q_ASSERT(isRotate90); - fx1 = (bbox.bottom() * m(0, 1) + m(0, 3)) * invW; - fy1 = (bbox.left() * m(1, 0) + m(1, 3)) * invW; - fx2 = (bbox.top() * m(0, 1) + m(0, 3)) * invW; - fy2 = (bbox.right() * m(1, 0) + m(1, 3)) * invW; - } - - if (fx1 > fx2) - qSwap(fx1, fx2); - if (fy1 > fy2) - qSwap(fy1, fy2); - - GLint ix1 = qRound((fx1 + 1) * m_device_rect.width() * qreal(0.5)); - GLint iy1 = qRound((fy1 + 1) * m_device_rect.height() * qreal(0.5)); - GLint ix2 = qRound((fx2 + 1) * m_device_rect.width() * qreal(0.5)); - GLint iy2 = qRound((fy2 + 1) * m_device_rect.height() * qreal(0.5)); - - if (!(clipType & ScissorClip)) { - m_current_scissor_rect = QRect(ix1, iy1, ix2 - ix1, iy2 - iy1); - glEnable(GL_SCISSOR_TEST); - clipType |= ScissorClip; - } else { - m_current_scissor_rect &= QRect(ix1, iy1, ix2 - ix1, iy2 - iy1); - } - glScissor(m_current_scissor_rect.x(), m_current_scissor_rect.y(), - m_current_scissor_rect.width(), m_current_scissor_rect.height()); - } else { - if (!(clipType & StencilClip)) { - if (!m_clip_program.isLinked()) { - QSGShaderSourceBuilder::initializeProgramFromFiles( - &m_clip_program, - QStringLiteral(":/scenegraph/shaders/stencilclip.vert"), - QStringLiteral(":/scenegraph/shaders/stencilclip.frag")); - m_clip_program.bindAttributeLocation("vCoord", 0); - m_clip_program.link(); - m_clip_matrix_id = m_clip_program.uniformLocation("matrix"); - } - - glClearStencil(0); - glClear(GL_STENCIL_BUFFER_BIT); - glEnable(GL_STENCIL_TEST); - glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE); - glDepthMask(GL_FALSE); - - m_clip_program.bind(); - m_clip_program.enableAttributeArray(0); - - clipType |= StencilClip; - } - - glStencilFunc(GL_EQUAL, m_current_stencil_value, 0xff); // stencil test, ref, test mask - glStencilOp(GL_KEEP, GL_KEEP, GL_INCR); // stencil fail, z fail, z pass - - const QSGGeometry *g = clip->geometry(); - Q_ASSERT(g->attributeCount() > 0); - const QSGGeometry::Attribute *a = g->attributes(); - glVertexAttribPointer(0, a->tupleSize, a->type, GL_FALSE, g->sizeOfVertex(), g->vertexData()); - - m_clip_program.setUniformValue(m_clip_matrix_id, m); - if (g->indexCount()) { - glDrawElements(g->drawingMode(), g->indexCount(), g->indexType(), g->indexData()); - } else { - glDrawArrays(g->drawingMode(), 0, g->vertexCount()); - } - - ++m_current_stencil_value; - } - - clip = clip->clipList(); - } - - if (clipType & StencilClip) { - m_clip_program.disableAttributeArray(0); - glStencilFunc(GL_EQUAL, m_current_stencil_value, 0xff); // stencil test, ref, test mask - glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP); // stencil fail, z fail, z pass - bindable()->reactivate(); - } else { - glDisable(GL_STENCIL_TEST); - } - - return clipType; -} - - - /*! \class QSGNodeDumper \brief The QSGNodeDumper class provides a way of dumping a scene grahp to the console. diff --git a/src/quick/scenegraph/coreapi/qsgrenderer_p.h b/src/quick/scenegraph/coreapi/qsgrenderer_p.h index f0cd06abe908e04746c3dfbc390986d78fcb85a8..e24a6b652f96cc600dafccbed69ea77e682579da 100644 --- a/src/quick/scenegraph/coreapi/qsgrenderer_p.h +++ b/src/quick/scenegraph/coreapi/qsgrenderer_p.h @@ -42,13 +42,10 @@ #ifndef QSGRENDERER_P_H #define QSGRENDERER_P_H +#include <qcolor.h> #include <qset.h> #include <qhash.h> -#include <qcolor.h> -#include <qopenglfunctions.h> -#include <qopenglshaderprogram.h> - #include "qsgnode.h" #include "qsgmaterial.h" #include <QtQuick/qsgtexture.h> @@ -67,18 +64,10 @@ class QSGNodeUpdater; Q_QUICK_PRIVATE_EXPORT bool qsg_test_and_clear_fatal_render_error(); Q_QUICK_PRIVATE_EXPORT void qsg_set_fatal_renderer_error(); -class Q_QUICK_PRIVATE_EXPORT QSGRenderer : public QObject, public QOpenGLFunctions +class Q_QUICK_PRIVATE_EXPORT QSGRenderer : public QObject { Q_OBJECT public: - enum ClipTypeBit - { - NoClip = 0x00, - ScissorClip = 0x01, - StencilClip = 0x02 - }; - Q_DECLARE_FLAGS(ClipType, ClipTypeBit) - enum ClearModeBit { ClearColorBuffer = 0x0001, @@ -142,7 +131,6 @@ Q_SIGNALS: protected: virtual void render() = 0; - QSGRenderer::ClipType updateStencilClip(const QSGClipNode *clip); const QSGBindable *bindable() const { return m_bindable; } @@ -160,8 +148,6 @@ protected: qreal m_current_opacity; qreal m_current_determinant; qreal m_device_pixel_ratio; - QRect m_current_scissor_rect; - int m_current_stencil_value; QSGRenderContext *m_context; @@ -175,8 +161,6 @@ private: QSet<QSGNode *> m_nodes_to_preprocess; QMatrix4x4 m_projection_matrix; - QOpenGLShaderProgram m_clip_program; - int m_clip_matrix_id; const QSGBindable *m_bindable;