aboutsummaryrefslogtreecommitdiffstats
path: root/src/quick/scenegraph/coreapi
diff options
context:
space:
mode:
Diffstat (limited to 'src/quick/scenegraph/coreapi')
-rw-r--r--src/quick/scenegraph/coreapi/qsgabstractrenderer.cpp39
-rw-r--r--src/quick/scenegraph/coreapi/qsgabstractrenderer.h4
-rw-r--r--src/quick/scenegraph/coreapi/qsgabstractrenderer_p.h1
-rw-r--r--src/quick/scenegraph/coreapi/qsgbatchrenderer.cpp2356
-rw-r--r--src/quick/scenegraph/coreapi/qsgbatchrenderer_p.h253
-rw-r--r--src/quick/scenegraph/coreapi/qsggeometry.cpp1
-rw-r--r--src/quick/scenegraph/coreapi/qsggeometry.h6
-rw-r--r--src/quick/scenegraph/coreapi/qsgmaterial.cpp572
-rw-r--r--src/quick/scenegraph/coreapi/qsgmaterial.h98
-rw-r--r--src/quick/scenegraph/coreapi/qsgmaterialrhishader.cpp565
-rw-r--r--src/quick/scenegraph/coreapi/qsgmaterialrhishader.h182
-rw-r--r--src/quick/scenegraph/coreapi/qsgmaterialrhishader_p.h114
-rw-r--r--src/quick/scenegraph/coreapi/qsgmaterialshader.cpp556
-rw-r--r--src/quick/scenegraph/coreapi/qsgmaterialshader.h136
-rw-r--r--src/quick/scenegraph/coreapi/qsgmaterialshader_p.h8
-rw-r--r--src/quick/scenegraph/coreapi/qsgmaterialtype.h51
-rw-r--r--src/quick/scenegraph/coreapi/qsgnode.cpp18
-rw-r--r--src/quick/scenegraph/coreapi/qsgrenderer.cpp31
-rw-r--r--src/quick/scenegraph/coreapi/qsgrenderer_p.h54
-rw-r--r--src/quick/scenegraph/coreapi/qsgrendererinterface.cpp96
-rw-r--r--src/quick/scenegraph/coreapi/qsgrendererinterface.h20
-rw-r--r--src/quick/scenegraph/coreapi/qsgtexture.cpp810
-rw-r--r--src/quick/scenegraph/coreapi/qsgtexture.h153
-rw-r--r--src/quick/scenegraph/coreapi/qsgtexture_p.h108
24 files changed, 5186 insertions, 1046 deletions
diff --git a/src/quick/scenegraph/coreapi/qsgabstractrenderer.cpp b/src/quick/scenegraph/coreapi/qsgabstractrenderer.cpp
index fddac7ed71..679ad1d445 100644
--- a/src/quick/scenegraph/coreapi/qsgabstractrenderer.cpp
+++ b/src/quick/scenegraph/coreapi/qsgabstractrenderer.cpp
@@ -224,18 +224,33 @@ QRect QSGAbstractRenderer::viewportRect() const
Convenience method that calls setProjectionMatrix() with an
orthographic matrix generated from \a rect.
+ \a flipY must be \c true when the graphics API uses Y down in its
+ normalized device coordinate system (for example, Vulkan), \c false
+ otherwise.
+
\sa setProjectionMatrix(), projectionMatrix()
*/
-void QSGAbstractRenderer::setProjectionMatrixToRect(const QRectF &rect)
+void QSGAbstractRenderer::setProjectionMatrixToRect(const QRectF &rect, bool flipY)
{
QMatrix4x4 matrix;
matrix.ortho(rect.x(),
rect.x() + rect.width(),
- rect.y() + rect.height(),
- rect.y(),
+ flipY ? rect.y() : rect.y() + rect.height(),
+ flipY ? rect.y() + rect.height() : rect.y(),
1,
-1);
setProjectionMatrix(matrix);
+
+ if (flipY) {
+ matrix.setToIdentity();
+ matrix.ortho(rect.x(),
+ rect.x() + rect.width(),
+ rect.y() + rect.height(),
+ rect.y(),
+ 1,
+ -1);
+ }
+ setProjectionMatrixWithNativeNDC(matrix);
}
/*!
@@ -250,6 +265,15 @@ void QSGAbstractRenderer::setProjectionMatrix(const QMatrix4x4 &matrix)
}
/*!
+ \internal
+ */
+void QSGAbstractRenderer::setProjectionMatrixWithNativeNDC(const QMatrix4x4 &matrix)
+{
+ Q_D(QSGAbstractRenderer);
+ d->m_projection_matrix_native_ndc = matrix;
+}
+
+/*!
Returns the projection matrix
\sa setProjectionMatrix(), setProjectionMatrixToRect()
@@ -261,6 +285,15 @@ QMatrix4x4 QSGAbstractRenderer::projectionMatrix() const
}
/*!
+ \internal
+ */
+QMatrix4x4 QSGAbstractRenderer::projectionMatrixWithNativeNDC() const
+{
+ Q_D(const QSGAbstractRenderer);
+ return d->m_projection_matrix_native_ndc;
+}
+
+/*!
Use \a color to clear the framebuffer when clearMode() is
set to QSGAbstractRenderer::ClearColorBuffer.
diff --git a/src/quick/scenegraph/coreapi/qsgabstractrenderer.h b/src/quick/scenegraph/coreapi/qsgabstractrenderer.h
index b9805f9db6..08e600e0b8 100644
--- a/src/quick/scenegraph/coreapi/qsgabstractrenderer.h
+++ b/src/quick/scenegraph/coreapi/qsgabstractrenderer.h
@@ -75,9 +75,11 @@ public:
inline void setViewportRect(const QSize &size) { setViewportRect(QRect(QPoint(), size)); }
QRect viewportRect() const;
- void setProjectionMatrixToRect(const QRectF &rect);
+ void setProjectionMatrixToRect(const QRectF &rect, bool flipY = false);
void setProjectionMatrix(const QMatrix4x4 &matrix);
+ void setProjectionMatrixWithNativeNDC(const QMatrix4x4 &matrix);
QMatrix4x4 projectionMatrix() const;
+ QMatrix4x4 projectionMatrixWithNativeNDC() const;
void setClearColor(const QColor &color);
QColor clearColor() const;
diff --git a/src/quick/scenegraph/coreapi/qsgabstractrenderer_p.h b/src/quick/scenegraph/coreapi/qsgabstractrenderer_p.h
index e6b17f4448..bbc4289b2c 100644
--- a/src/quick/scenegraph/coreapi/qsgabstractrenderer_p.h
+++ b/src/quick/scenegraph/coreapi/qsgabstractrenderer_p.h
@@ -78,6 +78,7 @@ public:
QRect m_viewport_rect;
QMatrix4x4 m_projection_matrix;
+ QMatrix4x4 m_projection_matrix_native_ndc;
uint m_mirrored : 1;
};
diff --git a/src/quick/scenegraph/coreapi/qsgbatchrenderer.cpp b/src/quick/scenegraph/coreapi/qsgbatchrenderer.cpp
index afe3380494..b5437ffb7e 100644
--- a/src/quick/scenegraph/coreapi/qsgbatchrenderer.cpp
+++ b/src/quick/scenegraph/coreapi/qsgbatchrenderer.cpp
@@ -1,6 +1,6 @@
/****************************************************************************
**
-** Copyright (C) 2016 The Qt Company Ltd.
+** 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/
@@ -57,7 +57,7 @@
#include <private/qnumeric_p.h>
#include <private/qquickprofiler_p.h>
-#include "qsgmaterialshader_p.h"
+#include "qsgmaterialrhishader_p.h"
#include <algorithm>
@@ -67,6 +67,10 @@
QT_BEGIN_NAMESPACE
+#ifndef QT_NO_DEBUG
+Q_QUICK_PRIVATE_EXPORT bool qsg_test_and_clear_material_failure();
+#endif
+
extern QByteArray qsgShaderRewriter_insertZAttributes(const char *input, QSurfaceFormat::OpenGLContextProfile profile);
int qt_sg_envInt(const char *name, int defaultValue);
@@ -108,8 +112,8 @@ static inline int size_of_type(GLenum type)
4,
sizeof(double)
};
- Q_ASSERT(type >= GL_BYTE && type <= 0x140A); // the value of GL_DOUBLE
- return sizes[type - GL_BYTE];
+ Q_ASSERT(type >= QSGGeometry::ByteType && type <= QSGGeometry::DoubleType);
+ return sizes[type - QSGGeometry::ByteType];
}
bool qsg_sort_element_increasing_order(Element *a, Element *b) { return a->order < b->order; }
@@ -132,44 +136,180 @@ struct QMatrix4x4_Accessor
const float OPAQUE_LIMIT = 0.999f;
-ShaderManager::Shader *ShaderManager::prepareMaterial(QSGMaterial *material)
+const uint DYNAMIC_VERTEX_INDEX_BUFFER_THRESHOLD = 4;
+const int VERTEX_BUFFER_BINDING = 0;
+const int ZORDER_BUFFER_BINDING = VERTEX_BUFFER_BINDING + 1;
+
+static inline uint aligned(uint v, uint byteAlign)
+{
+ return (v + byteAlign - 1) & ~(byteAlign - 1);
+}
+
+static inline QRhiVertexInputAttribute::Format vertexInputFormat(const QSGGeometry::Attribute &a)
+{
+ switch (a.type) {
+ case QSGGeometry::FloatType:
+ if (a.tupleSize == 4)
+ return QRhiVertexInputAttribute::Float4;
+ if (a.tupleSize == 3)
+ return QRhiVertexInputAttribute::Float3;
+ if (a.tupleSize == 2)
+ return QRhiVertexInputAttribute::Float2;
+ if (a.tupleSize == 1)
+ return QRhiVertexInputAttribute::Float;
+ break;
+ case QSGGeometry::UnsignedByteType:
+ if (a.tupleSize == 4)
+ return QRhiVertexInputAttribute::UNormByte4;
+ if (a.tupleSize == 2)
+ return QRhiVertexInputAttribute::UNormByte2;
+ if (a.tupleSize == 1)
+ return QRhiVertexInputAttribute::UNormByte;
+ break;
+ default:
+ break;
+ }
+ qWarning("Unsupported attribute type 0x%x with %d components", a.type, a.tupleSize);
+ Q_UNREACHABLE();
+ return QRhiVertexInputAttribute::Float;
+}
+
+static QRhiVertexInputLayout calculateVertexInputLayout(const QSGMaterialRhiShader *s, const QSGGeometry *geometry, bool batchable)
+{
+ Q_ASSERT(geometry);
+ const QSGMaterialRhiShaderPrivate *sd = QSGMaterialRhiShaderPrivate::get(s);
+ if (!sd->vertexShader) {
+ qWarning("No vertex shader in QSGMaterialRhiShader %p", s);
+ return QRhiVertexInputLayout();
+ }
+
+ const int attrCount = geometry->attributeCount();
+ QVector<QRhiVertexInputAttribute> inputAttributes;
+ inputAttributes.reserve(attrCount + 1);
+ int offset = 0;
+ for (int i = 0; i < attrCount; ++i) {
+ const QSGGeometry::Attribute &a = geometry->attributes()[i];
+ if (!sd->vertexShader->vertexInputLocations.contains(a.position)) {
+ 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));
+ offset += a.tupleSize * size_of_type(a.type);
+ }
+ if (batchable) {
+ inputAttributes.append(QRhiVertexInputAttribute(ZORDER_BUFFER_BINDING, sd->vertexShader->qt_order_attrib_location,
+ QRhiVertexInputAttribute::Float, 0));
+ }
+
+ Q_ASSERT(VERTEX_BUFFER_BINDING == 0 && ZORDER_BUFFER_BINDING == 1); // not very flexible
+ QVector<QRhiVertexInputBinding> inputBindings;
+ inputBindings.reserve(2);
+ inputBindings.append(QRhiVertexInputBinding(geometry->sizeOfVertex()));
+ if (batchable)
+ inputBindings.append(QRhiVertexInputBinding(sizeof(float)));
+
+ QRhiVertexInputLayout inputLayout;
+ inputLayout.setBindings(inputBindings);
+ inputLayout.setAttributes(inputAttributes);
+
+ return inputLayout;
+}
+
+static inline QRhiCommandBuffer::IndexFormat indexFormat(const QSGGeometry *geometry)
+{
+ switch (geometry->indexType()) {
+ case QSGGeometry::UnsignedShortType:
+ return QRhiCommandBuffer::IndexUInt16;
+ break;
+ case QSGGeometry::UnsignedIntType:
+ return QRhiCommandBuffer::IndexUInt32;
+ break;
+ default:
+ Q_UNREACHABLE();
+ return QRhiCommandBuffer::IndexUInt16;
+ }
+}
+
+static inline QRhiGraphicsPipeline::Topology gpTopology(int geomDrawMode)
+{
+ QRhiGraphicsPipeline::Topology topology = QRhiGraphicsPipeline::Triangles;
+ switch (geomDrawMode) {
+ case QSGGeometry::DrawPoints:
+ topology = QRhiGraphicsPipeline::Points;
+ break;
+ case QSGGeometry::DrawLines:
+ topology = QRhiGraphicsPipeline::Lines;
+ break;
+ case QSGGeometry::DrawLineStrip:
+ topology = QRhiGraphicsPipeline::LineStrip;
+ break;
+ case QSGGeometry::DrawTriangles:
+ topology = QRhiGraphicsPipeline::Triangles;
+ break;
+ case QSGGeometry::DrawTriangleStrip:
+ topology = QRhiGraphicsPipeline::TriangleStrip;
+ break;
+ default:
+ qWarning("Primitive topology 0x%x not supported", geomDrawMode);
+ break;
+ }
+ return topology;
+}
+
+ShaderManager::Shader *ShaderManager::prepareMaterial(QSGMaterial *material, bool enableRhiShaders, const QSGGeometry *geometry)
{
QSGMaterialType *type = material->type();
Shader *shader = rewrittenShaders.value(type, 0);
if (shader)
return shader;
+ if (enableRhiShaders && !material->flags().testFlag(QSGMaterial::SupportsRhiShader)) {
+ qWarning("The material failed to provide a working QShader pack");
+ return nullptr;
+ }
+
if (QSG_LOG_TIME_COMPILATION().isDebugEnabled())
qsg_renderer_timer.start();
Q_QUICK_SG_PROFILE_START(QQuickProfiler::SceneGraphContextFrame);
- QSGMaterialShader *s = material->createShader();
- QOpenGLContext *ctx = context->openglContext();
- QSurfaceFormat::OpenGLContextProfile profile = ctx->format().profile();
-
- QOpenGLShaderProgram *p = s->program();
- char const *const *attr = s->attributeNames();
- int i;
- for (i = 0; attr[i]; ++i) {
- if (*attr[i])
- p->bindAttributeLocation(attr[i], i);
- }
- p->bindAttributeLocation("_qt_order", i);
- context->compileShader(s, material, qsgShaderRewriter_insertZAttributes(s->vertexShader(), profile), nullptr);
- context->initializeShader(s);
- if (!p->isLinked())
- return nullptr;
-
shader = new Shader;
- shader->program = s;
- shader->pos_order = i;
- shader->id_zRange = p->uniformLocation("_qt_zRange");
- shader->lastOpacity = 0;
+ if (enableRhiShaders) {
+ material->setFlag(QSGMaterial::RhiShaderWanted, true);
+ QSGMaterialRhiShader *s = static_cast<QSGMaterialRhiShader *>(material->createShader());
+ material->setFlag(QSGMaterial::RhiShaderWanted, false);
+ context->initializeRhiShader(s, QShader::BatchableVertexShader);
+ shader->programRhi.program = s;
+ shader->programRhi.inputLayout = calculateVertexInputLayout(s, geometry, true);
+ QSGMaterialRhiShaderPrivate *sD = QSGMaterialRhiShaderPrivate::get(s);
+ shader->programRhi.shaderStages = {
+ { QRhiGraphicsShaderStage::Vertex, sD->shader(QShader::VertexStage), QShader::BatchableVertexShader },
+ { QRhiGraphicsShaderStage::Fragment, sD->shader(QShader::FragmentStage) }
+ };
+ } else {
+ QSGMaterialShader *s = material->createShader();
+ QOpenGLContext *ctx = context->openglContext();
+ QSurfaceFormat::OpenGLContextProfile profile = ctx->format().profile();
+ QOpenGLShaderProgram *p = s->program();
+ char const *const *attr = s->attributeNames();
+ int i;
+ for (i = 0; attr[i]; ++i) {
+ if (*attr[i])
+ p->bindAttributeLocation(attr[i], i);
+ }
+ p->bindAttributeLocation("_qt_order", i);
+ context->compileShader(s, material, qsgShaderRewriter_insertZAttributes(s->vertexShader(), profile), nullptr);
+ context->initializeShader(s);
+ if (!p->isLinked()) {
+ delete shader;
+ return nullptr;
+ }
+ shader->programGL.program = s;
+ shader->programGL.pos_order = i;
+ }
- Q_ASSERT(shader->pos_order >= 0);
- Q_ASSERT(shader->id_zRange >= 0);
+ shader->lastOpacity = 0;
- qCDebug(QSG_LOG_TIME_COMPILATION, "shader compiled in %dms", (int) qsg_renderer_timer.elapsed());
+ qCDebug(QSG_LOG_TIME_COMPILATION, "material shaders prepared in %dms", (int) qsg_renderer_timer.elapsed());
Q_QUICK_SG_PROFILE_END(QQuickProfiler::SceneGraphContextFrame,
QQuickProfiler::SceneGraphContextMaterialCompile);
@@ -178,25 +318,43 @@ ShaderManager::Shader *ShaderManager::prepareMaterial(QSGMaterial *material)
return shader;
}
-ShaderManager::Shader *ShaderManager::prepareMaterialNoRewrite(QSGMaterial *material)
+ShaderManager::Shader *ShaderManager::prepareMaterialNoRewrite(QSGMaterial *material, bool enableRhiShaders, const QSGGeometry *geometry)
{
QSGMaterialType *type = material->type();
Shader *shader = stockShaders.value(type, 0);
if (shader)
return shader;
+ if (enableRhiShaders && !material->flags().testFlag(QSGMaterial::SupportsRhiShader)) {
+ qWarning("The material failed to provide a working QShader pack");
+ return nullptr;
+ }
+
if (QSG_LOG_TIME_COMPILATION().isDebugEnabled())
qsg_renderer_timer.start();
Q_QUICK_SG_PROFILE_START(QQuickProfiler::SceneGraphContextFrame);
- QSGMaterialShader *s = static_cast<QSGMaterialShader *>(material->createShader());
- context->compileShader(s, material);
- context->initializeShader(s);
+ shader = new Shader;
+ if (enableRhiShaders) {
+ material->setFlag(QSGMaterial::RhiShaderWanted, true);
+ QSGMaterialRhiShader *s = static_cast<QSGMaterialRhiShader *>(material->createShader());
+ material->setFlag(QSGMaterial::RhiShaderWanted, false);
+ context->initializeRhiShader(s, QShader::StandardShader);
+ shader->programRhi.program = s;
+ shader->programRhi.inputLayout = calculateVertexInputLayout(s, geometry, false);
+ QSGMaterialRhiShaderPrivate *sD = QSGMaterialRhiShaderPrivate::get(s);
+ shader->programRhi.shaderStages = {
+ { QRhiGraphicsShaderStage::Vertex, sD->shader(QShader::VertexStage) },
+ { QRhiGraphicsShaderStage::Fragment, sD->shader(QShader::FragmentStage) }
+ };
+ } else {
+ QSGMaterialShader *s = material->createShader();
+ context->compileShader(s, material);
+ context->initializeShader(s);
+ shader->programGL.program = s;
+ shader->programGL.pos_order = -1;
+ }
- shader = new Shader();
- shader->program = s;
- shader->id_zRange = -1;
- shader->pos_order = -1;
shader->lastOpacity = 0;
stockShaders[type] = shader;
@@ -216,6 +374,45 @@ void ShaderManager::invalidated()
rewrittenShaders.clear();
delete blitProgram;
blitProgram = nullptr;
+
+ qDeleteAll(srbCache);
+ srbCache.clear();
+}
+
+void ShaderManager::clearCachedRendererData()
+{
+ for (ShaderManager::Shader *sms : stockShaders) {
+ QSGMaterialRhiShader *s = sms->programRhi.program;
+ if (s) {
+ QSGMaterialRhiShaderPrivate *sd = QSGMaterialRhiShaderPrivate::get(s);
+ sd->clearCachedRendererData();
+ }
+ }
+ for (ShaderManager::Shader *sms : rewrittenShaders) {
+ QSGMaterialRhiShader *s = sms->programRhi.program;
+ if (s) {
+ QSGMaterialRhiShaderPrivate *sd = QSGMaterialRhiShaderPrivate::get(s);
+ sd->clearCachedRendererData();
+ }
+ }
+}
+
+QRhiShaderResourceBindings *ShaderManager::srb(const QVector<QRhiShaderResourceBinding> &bindings)
+{
+ auto it = srbCache.constFind(bindings);
+ if (it != srbCache.constEnd())
+ return *it;
+
+ QRhiShaderResourceBindings *srb = context->rhi()->newShaderResourceBindings();
+ srb->setBindings(bindings);
+ if (srb->build()) {
+ srbCache.insert(bindings, srb);
+ } else {
+ qWarning("Failed to build srb");
+ delete srb;
+ srb = nullptr;
+ }
+ return srb;
}
void qsg_dumpShadowRoots(BatchRootInfo *i, int indent)
@@ -248,13 +445,13 @@ void qsg_dumpShadowRoots(Node *n)
QByteArray ind(indent, ' ');
if (n->type() == QSGNode::ClipNodeType || n->isBatchRoot) {
- qDebug() << ind.constData() << "[X]" << n->sgNode << hex << uint(n->sgNode->flags());
+ qDebug() << ind.constData() << "[X]" << n->sgNode << Qt::hex << uint(n->sgNode->flags());
qsg_dumpShadowRoots(n->rootInfo(), indent);
} else {
QDebug d = qDebug();
- d << ind.constData() << "[ ]" << n->sgNode << hex << uint(n->sgNode->flags());
+ d << ind.constData() << "[ ]" << n->sgNode << Qt::hex << uint(n->sgNode->flags());
if (n->type() == QSGNode::GeometryNodeType)
- d << "order" << dec << n->element()->order;
+ d << "order" << Qt::dec << n->element()->order;
}
SHADOWNODE_TRAVERSE(n)
@@ -531,11 +728,12 @@ void Updater::updateRootTransforms(Node *node, Node *root, const QMatrix4x4 &com
}
}
-int qsg_positionAttribute(QSGGeometry *g) {
+int qsg_positionAttribute(QSGGeometry *g)
+{
int vaOffset = 0;
for (int a=0; a<g->attributeCount(); ++a) {
const QSGGeometry::Attribute &attr = g->attributes()[a];
- if (attr.isVertexCoordinate && attr.tupleSize == 2 && attr.type == GL_FLOAT) {
+ if (attr.isVertexCoordinate && attr.tupleSize == 2 && attr.type == QSGGeometry::FloatType) {
return vaOffset;
}
vaOffset += attr.tupleSize * size_of_type(attr.type);
@@ -773,15 +971,28 @@ Renderer::Renderer(QSGDefaultRenderContext *ctx)
, m_currentStencilValue(0)
, m_clipMatrixId(0)
, m_currentClip(nullptr)
- , m_currentClipType(NoClip)
+ , m_currentClipType(ClipState::NoClip)
, m_vertexUploadPool(256)
, m_indexUploadPool(64)
, m_vao(nullptr)
, m_visualizeMode(VisualizeNothing)
{
- initializeOpenGLFunctions();
+ m_rhi = m_context->rhi();
+ if (m_rhi) {
+ m_ubufAlignment = m_rhi->ubufAlignment();
+ m_uint32IndexForRhi = !m_rhi->isFeatureSupported(QRhi::NonFourAlignedEffectiveIndexBufferOffset);
+ if (qEnvironmentVariableIntValue("QSG_RHI_UINT32_INDEX"))
+ m_uint32IndexForRhi = true;
+ } else {
+ initializeOpenGLFunctions();
+ m_uint32IndexForRhi = false;
+ }
+
setNodeUpdater(new Updater(this));
+ // The shader manager is shared between renderers (think for example Item
+ // layers that create a new Renderer each) with the same rendercontext
+ // (i.e. QRhi or QOpenGLContext).
m_shaderManager = ctx->findChild<ShaderManager *>(QStringLiteral("__qt_ShaderManager"), Qt::FindDirectChildrenOnly);
if (!m_shaderManager) {
m_shaderManager = new ShaderManager(ctx);
@@ -810,20 +1021,29 @@ Renderer::Renderer(QSGDefaultRenderContext *ctx)
? "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 (m_context->openglContext()->format().profile() == QSurfaceFormat::CoreProfile) {
- m_vao = new QOpenGLVertexArrayObject(this);
- m_vao->create();
- }
+ if (!m_rhi) {
+ // If rendering with an OpenGL Core profile context, we need to create a VAO
+ // to hold our vertex specification state.
+ if (m_context->openglContext()->format().profile() == QSurfaceFormat::CoreProfile) {
+ m_vao = new QOpenGLVertexArrayObject(this);
+ m_vao->create();
+ }
- bool useDepth = qEnvironmentVariableIsEmpty("QSG_NO_DEPTH_BUFFER");
- m_useDepthBuffer = useDepth && ctx->openglContext()->format().depthBufferSize() > 0;
+ bool useDepth = qEnvironmentVariableIsEmpty("QSG_NO_DEPTH_BUFFER");
+ m_useDepthBuffer = useDepth && ctx->openglContext()->format().depthBufferSize() > 0;
+ }
}
static void qsg_wipeBuffer(Buffer *buffer, QOpenGLFunctions *funcs)
{
- funcs->glDeleteBuffers(1, &buffer->id);
+ if (buffer->buf) {
+ //qDebug("releasing rhibuf %p", buffer->buf);
+ delete buffer->buf;
+ }
+
+ if (buffer->id)
+ funcs->glDeleteBuffers(1, &buffer->id);
+
// The free here is ok because we're in one of two situations.
// 1. We're using the upload pool in which case unmap will have set the
// data pointer to 0 and calling free on 0 is ok.
@@ -837,12 +1057,14 @@ static void qsg_wipeBatch(Batch *batch, QOpenGLFunctions *funcs, bool separateIn
qsg_wipeBuffer(&batch->vbo, funcs);
if (separateIndexBuffer)
qsg_wipeBuffer(&batch->ibo, funcs);
+ delete batch->ubuf;
+ batch->stencilClipState.reset();
delete batch;
}
Renderer::~Renderer()
{
- if (QOpenGLContext::currentContext()) {
+ if (m_rhi || QOpenGLContext::currentContext()) {
// Clean up batches and buffers
const bool separateIndexBuffer = m_context->separateIndexBuffer();
for (int i = 0; i < m_opaqueBatches.size(); ++i)
@@ -864,6 +1086,35 @@ Renderer::~Renderer()
else
m_elementAllocator.release(e);
}
+
+ destroyGraphicsResources();
+}
+
+void Renderer::destroyGraphicsResources()
+{
+ // If this is from the dtor, then the shader manager and its already
+ // prepared shaders will stay around for other renderers -> the cached data
+ // in the rhi shaders have to be purged as it may refer to samplers we
+ // are going to destroy.
+ m_shaderManager->clearCachedRendererData();
+
+ qDeleteAll(m_pipelines);
+ qDeleteAll(m_samplers);
+
+ m_stencilClipCommon.reset();
+
+ delete m_dummyTexture;
+}
+
+void Renderer::releaseCachedResources()
+{
+ m_shaderManager->invalidated();
+
+ destroyGraphicsResources();
+
+ m_pipelines.clear();
+ m_samplers.clear();
+ m_dummyTexture = nullptr;
}
void Renderer::invalidateAndRecycleBatch(Batch *b)
@@ -905,14 +1156,54 @@ void Renderer::map(Buffer *buffer, int byteSize, bool isIndexBuf)
void Renderer::unmap(Buffer *buffer, bool isIndexBuf)
{
- if (buffer->id == 0)
- glGenBuffers(1, &buffer->id);
- 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) {
- buffer->data = nullptr;
+ if (m_rhi) {
+ // Batches are pooled and reused which means the QRhiBuffer will be
+ // still valid in a recycled Batch. We only hit the newBuffer() path
+ // for brand new Batches.
+ if (!buffer->buf) {
+ buffer->buf = m_rhi->newBuffer(QRhiBuffer::Immutable,
+ isIndexBuf ? QRhiBuffer::IndexBuffer : QRhiBuffer::VertexBuffer,
+ buffer->size);
+ if (!buffer->buf->build())
+ qWarning("Failed to build vertex/index buffer of size %d", buffer->size);
+// else
+// qDebug("created rhibuf %p size %d", buffer->buf, buffer->size);
+ } else {
+ bool needsRebuild = false;
+ if (buffer->buf->size() < buffer->size) {
+ buffer->buf->setSize(buffer->size);
+ needsRebuild = true;
+ }
+ if (buffer->buf->type() != QRhiBuffer::Dynamic
+ && buffer->nonDynamicChangeCount > DYNAMIC_VERTEX_INDEX_BUFFER_THRESHOLD)
+ {
+ buffer->buf->setType(QRhiBuffer::Dynamic);
+ buffer->nonDynamicChangeCount = 0;
+ needsRebuild = true;
+ }
+ if (needsRebuild) {
+ //qDebug("rebuilding rhibuf %p size %d type Dynamic", buffer->buf, buffer->size);
+ buffer->buf->build();
+ }
+ }
+ if (buffer->buf->type() != QRhiBuffer::Dynamic) {
+ m_resourceUpdates->uploadStaticBuffer(buffer->buf,
+ QByteArray::fromRawData(buffer->data, buffer->size));
+ buffer->nonDynamicChangeCount += 1;
+ } else {
+ m_resourceUpdates->updateDynamicBuffer(buffer->buf, 0, buffer->size,
+ QByteArray::fromRawData(buffer->data, buffer->size));
+ }
+ if (m_visualizeMode == VisualizeNothing)
+ buffer->data = nullptr;
+ } else {
+ if (buffer->id == 0)
+ glGenBuffers(1, &buffer->id);
+ 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)
+ buffer->data = nullptr;
}
}
@@ -1098,8 +1389,12 @@ void Renderer::nodeWasRemoved(Node *node)
m_elementsToDelete.add(e);
if (m_renderNodeElements.isEmpty()) {
- static bool useDepth = qEnvironmentVariableIsEmpty("QSG_NO_DEPTH_BUFFER");
- m_useDepthBuffer = useDepth && m_context->openglContext()->format().depthBufferSize() > 0;
+ if (m_rhi) {
+ m_useDepthBuffer = true;
+ } else {
+ static bool useDepth = qEnvironmentVariableIsEmpty("QSG_NO_DEPTH_BUFFER");
+ m_useDepthBuffer = useDepth && m_context->openglContext()->format().depthBufferSize() > 0;
+ }
}
}
}
@@ -1539,7 +1834,7 @@ void Renderer::prepareOpaqueBatches()
if (gni->clipList() == gnj->clipList()
&& gni->geometry()->drawingMode() == gnj->geometry()->drawingMode()
- && (gni->geometry()->drawingMode() != GL_LINES || gni->geometry()->lineWidth() == gnj->geometry()->lineWidth())
+ && (gni->geometry()->drawingMode() != QSGGeometry::DrawLines || gni->geometry()->lineWidth() == gnj->geometry()->lineWidth())
&& gni->geometry()->attributes() == gnj->geometry()->attributes()
&& gni->inheritedOpacity() == gnj->inheritedOpacity()
&& gni->activeMaterial()->type() == gnj->activeMaterial()->type()
@@ -1638,7 +1933,7 @@ void Renderer::prepareAlphaBatches()
if (gni->clipList() == gnj->clipList()
&& gni->geometry()->drawingMode() == gnj->geometry()->drawingMode()
- && (gni->geometry()->drawingMode() != GL_LINES || gni->geometry()->lineWidth() == gnj->geometry()->lineWidth())
+ && (gni->geometry()->drawingMode() != QSGGeometry::DrawLines || gni->geometry()->lineWidth() == gnj->geometry()->lineWidth())
&& gni->geometry()->attributes() == gnj->geometry()->attributes()
&& gni->inheritedOpacity() == gnj->inheritedOpacity()
&& gni->activeMaterial()->type() == gnj->activeMaterial()->type()
@@ -1666,19 +1961,20 @@ void Renderer::prepareAlphaBatches()
}
-static inline int qsg_fixIndexCount(int iCount, GLenum drawMode) {
+static inline int qsg_fixIndexCount(int iCount, int drawMode)
+{
switch (drawMode) {
- case GL_TRIANGLE_STRIP:
+ case QSGGeometry::DrawTriangleStrip:
// Merged triangle strips need to contain degenerate triangles at the beginning and end.
// One could save 2 uploaded ushorts here by ditching the padding for the front of the
// first and the end of the last, but for simplicity, we simply don't care.
// Those extra triangles will be skipped while drawing to preserve the strip's parity
// anyhow.
return iCount + 2;
- case GL_LINES:
+ case QSGGeometry::DrawLines:
// For lines we drop the last vertex if the number of vertices is uneven.
return iCount - (iCount % 2);
- case GL_TRIANGLES:
+ case QSGGeometry::DrawTriangles:
// For triangles we drop trailing vertices until the result is divisible by 3.
return iCount - (iCount % 3);
default:
@@ -1700,7 +1996,7 @@ static inline int qsg_fixIndexCount(int iCount, GLenum drawMode) {
* iBase: The starting index for this element in the batch
*/
-void Renderer::uploadMergedElement(Element *e, int vaOffset, char **vertexData, char **zData, char **indexData, quint16 *iBase, int *indexCount)
+void Renderer::uploadMergedElement(Element *e, int vaOffset, char **vertexData, char **zData, char **indexData, void *iBasePtr, int *indexCount)
{
if (Q_UNLIKELY(debug_upload())) qDebug() << " - uploading element:" << e << e->node << (void *) *vertexData << (qintptr) (*zData - *vertexData) << (qintptr) (*indexData - *vertexData);
QSGGeometry *g = e->node->geometry();
@@ -1736,35 +2032,67 @@ void Renderer::uploadMergedElement(Element *e, int vaOffset, char **vertexData,
}
int iCount = g->indexCount();
- quint16 *indices = (quint16 *) *indexData;
+ if (m_uint32IndexForRhi) {
+ // can only happen when using the rhi
+ quint32 *iBase = (quint32 *) iBasePtr;
+ quint32 *indices = (quint32 *) *indexData;
+ if (iCount == 0) {
+ iCount = vCount;
+ if (g->drawingMode() == QSGGeometry::DrawTriangleStrip)
+ *indices++ = *iBase;
+ else
+ iCount = qsg_fixIndexCount(iCount, g->drawingMode());
- if (iCount == 0) {
- iCount = vCount;
- if (g->drawingMode() == GL_TRIANGLE_STRIP)
- *indices++ = *iBase;
- else
- iCount = qsg_fixIndexCount(iCount, g->drawingMode());
+ for (int i=0; i<iCount; ++i)
+ indices[i] = *iBase + i;
+ } else {
+ // source index data in QSGGeometry is always ushort (we would not merge otherwise)
+ const quint16 *srcIndices = g->indexDataAsUShort();
+ if (g->drawingMode() == QSGGeometry::DrawTriangleStrip)
+ *indices++ = *iBase + srcIndices[0];
+ else
+ iCount = qsg_fixIndexCount(iCount, g->drawingMode());
- for (int i=0; i<iCount; ++i)
- indices[i] = *iBase + i;
+ for (int i=0; i<iCount; ++i)
+ indices[i] = *iBase + srcIndices[i];
+ }
+ if (g->drawingMode() == QSGGeometry::DrawTriangleStrip) {
+ indices[iCount] = indices[iCount - 1];
+ iCount += 2;
+ }
+ *iBase += vCount;
} else {
- const quint16 *srcIndices = g->indexDataAsUShort();
- if (g->drawingMode() == GL_TRIANGLE_STRIP)
- *indices++ = *iBase + srcIndices[0];
- else
- iCount = qsg_fixIndexCount(iCount, g->drawingMode());
+ // normally batching is only done for ushort index data
+ quint16 *iBase = (quint16 *) iBasePtr;
+ quint16 *indices = (quint16 *) *indexData;
+ if (iCount == 0) {
+ iCount = vCount;
+ if (g->drawingMode() == QSGGeometry::DrawTriangleStrip)
+ *indices++ = *iBase;
+ else
+ iCount = qsg_fixIndexCount(iCount, g->drawingMode());
- for (int i=0; i<iCount; ++i)
- indices[i] = *iBase + srcIndices[i];
- }
- if (g->drawingMode() == GL_TRIANGLE_STRIP) {
- indices[iCount] = indices[iCount - 1];
- iCount += 2;
+ for (int i=0; i<iCount; ++i)
+ indices[i] = *iBase + i;
+ } else {
+ const quint16 *srcIndices = g->indexDataAsUShort();
+ if (g->drawingMode() == QSGGeometry::DrawTriangleStrip)
+ *indices++ = *iBase + srcIndices[0];
+ else
+ iCount = qsg_fixIndexCount(iCount, g->drawingMode());
+
+ for (int i=0; i<iCount; ++i)
+ indices[i] = *iBase + srcIndices[i];
+ }
+ if (g->drawingMode() == QSGGeometry::DrawTriangleStrip) {
+ indices[iCount] = indices[iCount - 1];
+ iCount += 2;
+ }
+ *iBase += vCount;
}
*vertexData += vCount * vSize;
- *indexData += iCount * sizeof(quint16);
- *iBase += vCount;
+ *indexData += iCount * mergedIndexElemSize();
*indexCount += iCount;
}
@@ -1779,66 +2107,67 @@ static QMatrix4x4 qsg_matrixForRoot(Node *node)
void Renderer::uploadBatch(Batch *b)
{
- // Early out if nothing has changed in this batch..
- if (!b->needsUpload) {
- if (Q_UNLIKELY(debug_upload())) qDebug() << " Batch:" << b << "already uploaded...";
- return;
- }
-
- if (!b->first) {
- if (Q_UNLIKELY(debug_upload())) qDebug() << " Batch:" << b << "is invalid...";
- return;
- }
-
- if (b->isRenderNode) {
- if (Q_UNLIKELY(debug_upload())) qDebug() << " Batch: " << b << "is a render node...";
- return;
- }
+ // Early out if nothing has changed in this batch..
+ if (!b->needsUpload) {
+ if (Q_UNLIKELY(debug_upload())) qDebug() << " Batch:" << b << "already uploaded...";
+ return;
+ }
- // Figure out if we can merge or not, if not, then just render the batch as is..
- Q_ASSERT(b->first);
- Q_ASSERT(b->first->node);
+ if (!b->first) {
+ if (Q_UNLIKELY(debug_upload())) qDebug() << " Batch:" << b << "is invalid...";
+ return;
+ }
- QSGGeometryNode *gn = b->first->node;
- QSGGeometry *g = gn->geometry();
- QSGMaterial::Flags flags = gn->activeMaterial()->flags();
- bool canMerge = (g->drawingMode() == GL_TRIANGLES || g->drawingMode() == GL_TRIANGLE_STRIP ||
- g->drawingMode() == GL_LINES || g->drawingMode() == GL_POINTS)
- && b->positionAttribute >= 0
- && g->indexType() == GL_UNSIGNED_SHORT
- && (flags & (QSGMaterial::CustomCompileStep | QSGMaterial_FullMatrix)) == 0
- && ((flags & QSGMaterial::RequiresFullMatrixExceptTranslate) == 0 || b->isTranslateOnlyToRoot())
- && b->isSafeToBatch();
+ if (b->isRenderNode) {
+ if (Q_UNLIKELY(debug_upload())) qDebug() << " Batch: " << b << "is a render node...";
+ return;
+ }
- b->merged = canMerge;
+ // Figure out if we can merge or not, if not, then just render the batch as is..
+ Q_ASSERT(b->first);
+ Q_ASSERT(b->first->node);
- // Figure out how much memory we need...
- b->vertexCount = 0;
- b->indexCount = 0;
- int unmergedIndexSize = 0;
- Element *e = b->first;
+ QSGGeometryNode *gn = b->first->node;
+ QSGGeometry *g = gn->geometry();
+ QSGMaterial::Flags flags = gn->activeMaterial()->flags();
+ bool canMerge = (g->drawingMode() == QSGGeometry::DrawTriangles || g->drawingMode() == QSGGeometry::DrawTriangleStrip ||
+ g->drawingMode() == QSGGeometry::DrawLines || g->drawingMode() == QSGGeometry::DrawPoints)
+ && b->positionAttribute >= 0
+ && g->indexType() == QSGGeometry::UnsignedShortType
+ && (flags & (QSGMaterial::CustomCompileStep | QSGMaterial_FullMatrix)) == 0
+ && ((flags & QSGMaterial::RequiresFullMatrixExceptTranslate) == 0 || b->isTranslateOnlyToRoot())
+ && b->isSafeToBatch();
+
+ b->merged = canMerge;
+
+ // Figure out how much memory we need...
+ b->vertexCount = 0;
+ b->indexCount = 0;
+ int unmergedIndexSize = 0;
+ Element *e = b->first;
- while (e) {
- QSGGeometry *eg = e->node->geometry();
- b->vertexCount += eg->vertexCount();
- int iCount = eg->indexCount();
- if (b->merged) {
- if (iCount == 0)
- iCount = eg->vertexCount();
- iCount = qsg_fixIndexCount(iCount, g->drawingMode());
- } else {
- unmergedIndexSize += iCount * eg->sizeOfIndex();
- }
- b->indexCount += iCount;
- e = e->nextInBatch;
+ while (e) {
+ QSGGeometry *eg = e->node->geometry();
+ b->vertexCount += eg->vertexCount();
+ int iCount = eg->indexCount();
+ if (b->merged) {
+ if (iCount == 0)
+ iCount = eg->vertexCount();
+ iCount = qsg_fixIndexCount(iCount, g->drawingMode());
+ } else {
+ const int effectiveIndexSize = m_uint32IndexForRhi ? sizeof(quint32) : eg->sizeOfIndex();
+ unmergedIndexSize += iCount * effectiveIndexSize;
}
+ b->indexCount += iCount;
+ e = e->nextInBatch;
+ }
- // Abort if there are no vertices in this batch.. We abort this late as
- // this is a broken usecase which we do not care to optimize for...
- if (b->vertexCount == 0 || (b->merged && b->indexCount == 0))
- return;
+ // Abort if there are no vertices in this batch.. We abort this late as
+ // this is a broken usecase which we do not care to optimize for...
+ if (b->vertexCount == 0 || (b->merged && b->indexCount == 0))
+ return;
- /* Allocate memory for this batch. Merged batches are divided into three separate blocks
+ /* Allocate memory for this batch. Merged batches are divided into three separate blocks
1. Vertex data for all elements, as they were in the QSGGeometry object, but
with the tranform relative to this batch's root applied. The vertex data
is otherwise unmodified.
@@ -1850,119 +2179,161 @@ void Renderer::uploadBatch(Batch *b)
primitive. These are unsigned shorts for merged and arbitrary for
non-merged.
*/
- int bufferSize = b->vertexCount * g->sizeOfVertex();
- int ibufferSize = 0;
- if (b->merged) {
- ibufferSize = b->indexCount * sizeof(quint16);
- if (m_useDepthBuffer)
- bufferSize += b->vertexCount * sizeof(float);
- } else {
- ibufferSize = unmergedIndexSize;
- }
+ int bufferSize = b->vertexCount * g->sizeOfVertex();
+ int ibufferSize = 0;
+ if (b->merged) {
+ ibufferSize = b->indexCount * mergedIndexElemSize();
+ if (m_useDepthBuffer)
+ bufferSize += b->vertexCount * sizeof(float);
+ } else {
+ ibufferSize = unmergedIndexSize;
+ }
- const bool separateIndexBuffer = m_context->separateIndexBuffer();
- if (separateIndexBuffer)
- map(&b->ibo, ibufferSize, true);
- else
- bufferSize += ibufferSize;
- map(&b->vbo, bufferSize);
+ const bool separateIndexBuffer = m_context->separateIndexBuffer();
+ if (separateIndexBuffer)
+ map(&b->ibo, ibufferSize, true);
+ else
+ bufferSize += ibufferSize;
+ map(&b->vbo, bufferSize);
- if (Q_UNLIKELY(debug_upload())) qDebug() << " - batch" << b << " first:" << b->first << " root:"
- << b->root << " merged:" << b->merged << " positionAttribute" << b->positionAttribute
- << " vbo:" << b->vbo.id << ":" << b->vbo.size;
+ if (Q_UNLIKELY(debug_upload())) qDebug() << " - batch" << b << " first:" << b->first << " root:"
+ << b->root << " merged:" << b->merged << " positionAttribute" << b->positionAttribute
+ << " vbo:" << b->vbo.id << ":" << b->vbo.size;
- if (b->merged) {
- char *vertexData = b->vbo.data;
- char *zData = vertexData + b->vertexCount * g->sizeOfVertex();
- char *indexData = separateIndexBuffer
- ? b->ibo.data
- : zData + (int(m_useDepthBuffer) * b->vertexCount * sizeof(float));
-
- quint16 iOffset = 0;
- e = b->first;
- int verticesInSet = 0;
- int indicesInSet = 0;
- b->drawSets.reset();
- int drawSetIndices = separateIndexBuffer ? 0 : indexData - vertexData;
- const auto indexBase = separateIndexBuffer ? b->ibo.data : b->vbo.data;
- b->drawSets << DrawSet(0, zData - vertexData, drawSetIndices);
- while (e) {
- verticesInSet += e->node->geometry()->vertexCount();
- if (verticesInSet > 0xffff) {
- b->drawSets.last().indexCount = indicesInSet;
- if (g->drawingMode() == GL_TRIANGLE_STRIP) {
- b->drawSets.last().indices += 1 * sizeof(quint16);
- b->drawSets.last().indexCount -= 2;
- }
- drawSetIndices = indexData - indexBase;
- b->drawSets << DrawSet(vertexData - b->vbo.data,
- zData - b->vbo.data,
- drawSetIndices);
- iOffset = 0;
- verticesInSet = e->node->geometry()->vertexCount();
- indicesInSet = 0;
+ if (b->merged) {
+ char *vertexData = b->vbo.data;
+ char *zData = vertexData + b->vertexCount * g->sizeOfVertex();
+ char *indexData = separateIndexBuffer
+ ? b->ibo.data
+ : zData + (int(m_useDepthBuffer) * b->vertexCount * sizeof(float));
+
+ quint16 iOffset16 = 0;
+ quint32 iOffset32 = 0;
+ e = b->first;
+ uint verticesInSet = 0;
+ // Start a new set already after 65534 vertices because 0xFFFF may be
+ // used for an always-on primitive restart with some apis (adapt for
+ // uint32 indices as appropriate).
+ const uint verticesInSetLimit = m_uint32IndexForRhi ? 0xfffffffe : 0xfffe;
+ int indicesInSet = 0;
+ b->drawSets.reset();
+ int drawSetIndices = separateIndexBuffer ? 0 : indexData - vertexData;
+ const char *indexBase = separateIndexBuffer ? b->ibo.data : b->vbo.data;
+ b->drawSets << DrawSet(0, zData - vertexData, drawSetIndices);
+ while (e) {
+ verticesInSet += e->node->geometry()->vertexCount();
+ if (verticesInSet > verticesInSetLimit) {
+ b->drawSets.last().indexCount = indicesInSet;
+ if (g->drawingMode() == QSGGeometry::DrawTriangleStrip) {
+ b->drawSets.last().indices += 1 * mergedIndexElemSize();
+ b->drawSets.last().indexCount -= 2;
}
- uploadMergedElement(e, b->positionAttribute, &vertexData, &zData, &indexData, &iOffset, &indicesInSet);
- e = e->nextInBatch;
+ drawSetIndices = indexData - indexBase;
+ b->drawSets << DrawSet(vertexData - b->vbo.data,
+ zData - b->vbo.data,
+ drawSetIndices);
+ iOffset16 = 0;
+ iOffset32 = 0;
+ verticesInSet = e->node->geometry()->vertexCount();
+ indicesInSet = 0;
}
- b->drawSets.last().indexCount = indicesInSet;
- // We skip the very first and very last degenerate triangles since they aren't needed
- // and the first one would reverse the vertex ordering of the merged strips.
- if (g->drawingMode() == GL_TRIANGLE_STRIP) {
- b->drawSets.last().indices += 1 * sizeof(quint16);
- b->drawSets.last().indexCount -= 2;
- }
- } else {
- char *vboData = b->vbo.data;
- char *iboData = separateIndexBuffer ? b->ibo.data
- : vboData + b->vertexCount * g->sizeOfVertex();
- Element *e = b->first;
- while (e) {
- QSGGeometry *g = e->node->geometry();
- int vbs = g->vertexCount() * g->sizeOfVertex();
- memcpy(vboData, g->vertexData(), vbs);
- vboData = vboData + vbs;
- if (g->indexCount()) {
+ void *iBasePtr = &iOffset16;
+ if (m_uint32IndexForRhi)
+ iBasePtr = &iOffset32;
+ uploadMergedElement(e, b->positionAttribute, &vertexData, &zData, &indexData, iBasePtr, &indicesInSet);
+ e = e->nextInBatch;
+ }
+ b->drawSets.last().indexCount = indicesInSet;
+ // We skip the very first and very last degenerate triangles since they aren't needed
+ // and the first one would reverse the vertex ordering of the merged strips.
+ if (g->drawingMode() == QSGGeometry::DrawTriangleStrip) {
+ b->drawSets.last().indices += 1 * mergedIndexElemSize();
+ b->drawSets.last().indexCount -= 2;
+ }
+ } else {
+ char *vboData = b->vbo.data;
+ char *iboData = separateIndexBuffer ? b->ibo.data
+ : vboData + b->vertexCount * g->sizeOfVertex();
+ Element *e = b->first;
+ while (e) {
+ QSGGeometry *g = e->node->geometry();
+ int vbs = g->vertexCount() * g->sizeOfVertex();
+ memcpy(vboData, g->vertexData(), vbs);
+ vboData = vboData + vbs;
+ const int indexCount = g->indexCount();
+ if (indexCount) {
+ if (!m_rhi) {
int ibs = g->indexCount() * g->sizeOfIndex();
memcpy(iboData, g->indexData(), ibs);
iboData += ibs;
+ } else {
+ const int effectiveIndexSize = m_uint32IndexForRhi ? sizeof(quint32) : g->sizeOfIndex();
+ const int ibs = indexCount * effectiveIndexSize;
+ if (g->sizeOfIndex() == effectiveIndexSize) {
+ memcpy(iboData, g->indexData(), ibs);
+ } else {
+ if (g->sizeOfIndex() == sizeof(quint16) && effectiveIndexSize == sizeof(quint32)) {
+ quint16 *src = g->indexDataAsUShort();
+ quint32 *dst = (quint32 *) iboData;
+ for (int i = 0; i < indexCount; ++i)
+ dst[i] = src[i];
+ } else {
+ Q_ASSERT_X(false, "uploadBatch (unmerged)", "uint index with ushort effective index - cannot happen");
+ }
+ }
+ iboData += ibs;
}
- e = e->nextInBatch;
}
+ e = e->nextInBatch;
}
+ }
#ifndef QT_NO_DEBUG_OUTPUT
- if (Q_UNLIKELY(debug_upload())) {
- const char *vd = b->vbo.data;
- qDebug() << " -- Vertex Data, count:" << b->vertexCount << " - " << g->sizeOfVertex() << "bytes/vertex";
- for (int i=0; i<b->vertexCount; ++i) {
- QDebug dump = qDebug().nospace();
- dump << " --- " << i << ": ";
- int offset = 0;
- for (int a=0; a<g->attributeCount(); ++a) {
- const QSGGeometry::Attribute &attr = g->attributes()[a];
- dump << attr.position << ":(" << attr.tupleSize << ",";
- if (attr.type == GL_FLOAT) {
- dump << "float ";
- if (attr.isVertexCoordinate)
- dump << "* ";
- for (int t=0; t<attr.tupleSize; ++t)
- dump << *(const float *)(vd + offset + t * sizeof(float)) << " ";
- } else if (attr.type == GL_UNSIGNED_BYTE) {
- dump << "ubyte ";
- for (int t=0; t<attr.tupleSize; ++t)
- dump << *(const unsigned char *)(vd + offset + t * sizeof(unsigned char)) << " ";
- }
- dump << ") ";
- offset += attr.tupleSize * size_of_type(attr.type);
+ if (Q_UNLIKELY(debug_upload())) {
+ const char *vd = b->vbo.data;
+ qDebug() << " -- Vertex Data, count:" << b->vertexCount << " - " << g->sizeOfVertex() << "bytes/vertex";
+ for (int i=0; i<b->vertexCount; ++i) {
+ QDebug dump = qDebug().nospace();
+ dump << " --- " << i << ": ";
+ int offset = 0;
+ for (int a=0; a<g->attributeCount(); ++a) {
+ const QSGGeometry::Attribute &attr = g->attributes()[a];
+ dump << attr.position << ":(" << attr.tupleSize << ",";
+ if (attr.type == QSGGeometry::FloatType) {
+ dump << "float ";
+ if (attr.isVertexCoordinate)
+ dump << "* ";
+ for (int t=0; t<attr.tupleSize; ++t)
+ dump << *(const float *)(vd + offset + t * sizeof(float)) << " ";
+ } else if (attr.type == QSGGeometry::UnsignedByteType) {
+ dump << "ubyte ";
+ for (int t=0; t<attr.tupleSize; ++t)
+ dump << *(const unsigned char *)(vd + offset + t * sizeof(unsigned char)) << " ";
}
- if (b->merged && m_useDepthBuffer) {
- float zorder = ((float*)(b->vbo.data + b->vertexCount * g->sizeOfVertex()))[i];
- dump << " Z:(" << zorder << ")";
- }
- vd += g->sizeOfVertex();
+ dump << ") ";
+ offset += attr.tupleSize * size_of_type(attr.type);
+ }
+ if (b->merged && m_useDepthBuffer) {
+ float zorder = ((float*)(b->vbo.data + b->vertexCount * g->sizeOfVertex()))[i];
+ dump << " Z:(" << zorder << ")";
}
+ vd += g->sizeOfVertex();
+ }
- if (!b->drawSets.isEmpty()) {
+ if (!b->drawSets.isEmpty()) {
+ if (m_uint32IndexForRhi) {
+ const quint32 *id = (const quint32 *)(separateIndexBuffer
+ ? b->ibo.data
+ : b->vbo.data + b->drawSets.at(0).indices);
+ {
+ QDebug iDump = qDebug();
+ iDump << " -- Index Data, count:" << b->indexCount;
+ for (int i=0; i<b->indexCount; ++i) {
+ if ((i % 24) == 0)
+ iDump << Qt::endl << " --- ";
+ iDump << id[i];
+ }
+ }
+ } else {
const quint16 *id = (const quint16 *)(separateIndexBuffer
? b->ibo.data
: b->vbo.data + b->drawSets.at(0).indices);
@@ -1971,29 +2342,30 @@ void Renderer::uploadBatch(Batch *b)
iDump << " -- Index Data, count:" << b->indexCount;
for (int i=0; i<b->indexCount; ++i) {
if ((i % 24) == 0)
- iDump << endl << " --- ";
+ iDump << Qt::endl << " --- ";
iDump << id[i];
}
}
+ }
- for (int i=0; i<b->drawSets.size(); ++i) {
- const DrawSet &s = b->drawSets.at(i);
- qDebug() << " -- DrawSet: indexCount:" << s.indexCount << " vertices:" << s.vertices << " z:" << s.zorders << " indices:" << s.indices;
- }
+ for (int i=0; i<b->drawSets.size(); ++i) {
+ const DrawSet &s = b->drawSets.at(i);
+ qDebug() << " -- DrawSet: indexCount:" << s.indexCount << " vertices:" << s.vertices << " z:" << s.zorders << " indices:" << s.indices;
}
}
+ }
#endif // QT_NO_DEBUG_OUTPUT
- unmap(&b->vbo);
- if (separateIndexBuffer)
- unmap(&b->ibo, true);
+ unmap(&b->vbo);
+ if (separateIndexBuffer)
+ unmap(&b->ibo, true);
- if (Q_UNLIKELY(debug_upload())) qDebug() << " --- vertex/index buffers unmapped, batch upload completed...";
+ if (Q_UNLIKELY(debug_upload())) qDebug() << " --- vertex/index buffers unmapped, batch upload completed...";
- b->needsUpload = false;
+ b->needsUpload = false;
- if (Q_UNLIKELY(debug_render()))
- b->uploadedThisFrame = true;
+ if (Q_UNLIKELY(debug_render()))
+ b->uploadedThisFrame = true;
}
/*!
@@ -2002,15 +2374,15 @@ void Renderer::uploadBatch(Batch *b)
* If the clip is a pixel aligned rectangle, this function will use glScissor instead
* of stencil.
*/
-Renderer::ClipType Renderer::updateStencilClip(const QSGClipNode *clip)
+ClipState::ClipType Renderer::updateStencilClip(const QSGClipNode *clip)
{
if (!clip) {
glDisable(GL_STENCIL_TEST);
glDisable(GL_SCISSOR_TEST);
- return NoClip;
+ return ClipState::NoClip;
}
- ClipType clipType = NoClip;
+ ClipState::ClipType clipType = ClipState::NoClip;
GLuint vbo = 0;
int vboSize = 0;
@@ -2067,17 +2439,17 @@ Renderer::ClipType Renderer::updateStencilClip(const QSGClipNode *clip)
GLint ix2 = qRound((fx2 + 1) * deviceRect.width() * qreal(0.5));
GLint iy2 = qRound((fy2 + 1) * deviceRect.height() * qreal(0.5));
- if (!(clipType & ScissorClip)) {
+ if (!(clipType & ClipState::ScissorClip)) {
m_currentScissorRect = QRect(ix1, iy1, ix2 - ix1, iy2 - iy1);
glEnable(GL_SCISSOR_TEST);
- clipType |= ScissorClip;
+ clipType |= ClipState::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 (!(clipType & ClipState::StencilClip)) {
if (!m_clipProgram.isLinked()) {
QSGShaderSourceBuilder::initializeProgramFromFiles(
&m_clipProgram,
@@ -2097,7 +2469,7 @@ Renderer::ClipType Renderer::updateStencilClip(const QSGClipNode *clip)
m_clipProgram.bind();
m_clipProgram.enableAttributeArray(0);
- clipType |= StencilClip;
+ clipType |= ClipState::StencilClip;
}
glStencilFunc(GL_EQUAL, m_currentStencilValue, 0xff); // stencil test, ref, test mask
@@ -2148,7 +2520,7 @@ Renderer::ClipType Renderer::updateStencilClip(const QSGClipNode *clip)
if (vbo)
glDeleteBuffers(1, &vbo);
- if (clipType & StencilClip) {
+ if (clipType & ClipState::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
@@ -2160,7 +2532,7 @@ Renderer::ClipType Renderer::updateStencilClip(const QSGClipNode *clip)
return clipType;
}
-void Renderer::updateClip(const QSGClipNode *clipList, const Batch *batch)
+void Renderer::updateClip(const QSGClipNode *clipList, const Batch *batch) // legacy (GL-only)
{
if (clipList != m_currentClip && Q_LIKELY(!debug_noclip())) {
m_currentClip = clipList;
@@ -2174,7 +2546,7 @@ void Renderer::updateClip(const QSGClipNode *clipList, const Batch *batch)
m_currentClipType = updateStencilClip(m_currentClip);
if (batch->isOpaque) {
glEnable(GL_DEPTH_TEST);
- if (m_currentClipType & StencilClip)
+ if (m_currentClipType & ClipState::StencilClip)
glDepthMask(true);
}
}
@@ -2185,13 +2557,14 @@ void Renderer::updateClip(const QSGClipNode *clipList, const Batch *batch)
* which vertex attribute arrays need to be enabled and not. Then update the current
* Shader and current QSGMaterialShader.
*/
-void Renderer::setActiveShader(QSGMaterialShader *program, ShaderManager::Shader *shader)
+void Renderer::setActiveShader(QSGMaterialShader *program, ShaderManager::Shader *shader) // legacy (GL-only)
{
+ Q_ASSERT(!m_rhi);
const char * const *c = m_currentProgram ? m_currentProgram->attributeNames() : nullptr;
const char * const *n = program ? program->attributeNames() : nullptr;
- int cza = m_currentShader ? m_currentShader->pos_order : -1;
- int nza = shader ? shader->pos_order : -1;
+ int cza = m_currentShader ? m_currentShader->programGL.pos_order : -1;
+ int nza = shader ? shader->programGL.pos_order : -1;
int i = 0;
while (c || n) {
@@ -2233,7 +2606,372 @@ void Renderer::setActiveShader(QSGMaterialShader *program, ShaderManager::Shader
}
}
-void Renderer::renderMergedBatch(const Batch *batch)
+void Renderer::applyClipStateToGraphicsState() // RHI only
+{
+ m_gstate.usesScissor = (m_currentClipState.type & ClipState::ScissorClip);
+ m_gstate.stencilTest = (m_currentClipState.type & ClipState::StencilClip);
+}
+
+QRhiGraphicsPipeline *Renderer::buildStencilPipeline(const Batch *batch, bool firstStencilClipInBatch)
+{
+ QRhiGraphicsPipeline *ps = m_rhi->newGraphicsPipeline();
+ ps->setFlags(QRhiGraphicsPipeline::UsesStencilRef);
+ QRhiGraphicsPipeline::TargetBlend blend;
+ blend.colorWrite = 0;
+ ps->setTargetBlends({ blend });
+ ps->setSampleCount(renderTarget()->sampleCount());
+ ps->setStencilTest(true);
+ QRhiGraphicsPipeline::StencilOpState stencilOp;
+ if (firstStencilClipInBatch) {
+ stencilOp.compareOp = QRhiGraphicsPipeline::Always;
+ stencilOp.failOp = QRhiGraphicsPipeline::Keep;
+ stencilOp.depthFailOp = QRhiGraphicsPipeline::Keep;
+ stencilOp.passOp = QRhiGraphicsPipeline::Replace;
+ } else {
+ stencilOp.compareOp = QRhiGraphicsPipeline::Equal;
+ stencilOp.failOp = QRhiGraphicsPipeline::Keep;
+ stencilOp.depthFailOp = QRhiGraphicsPipeline::Keep;
+ stencilOp.passOp = QRhiGraphicsPipeline::IncrementAndClamp;
+ }
+ ps->setStencilFront(stencilOp);
+ ps->setStencilBack(stencilOp);
+
+ ps->setTopology(m_stencilClipCommon.topology);
+
+ ps->setShaderStages({ QRhiGraphicsShaderStage(QRhiGraphicsShaderStage::Vertex, m_stencilClipCommon.vs),
+ QRhiGraphicsShaderStage(QRhiGraphicsShaderStage::Fragment, m_stencilClipCommon.fs) });
+ ps->setVertexInputLayout(m_stencilClipCommon.inputLayout);
+ ps->setShaderResourceBindings(batch->stencilClipState.srb); // use something, it just needs to be layout-compatible
+ ps->setRenderPassDescriptor(renderPassDescriptor());
+
+ if (!ps->build()) {
+ qWarning("Failed to build stencil clip pipeline");
+ delete ps;
+ return nullptr;
+ }
+
+ return ps;
+}
+
+void Renderer::updateClipState(const QSGClipNode *clipList, Batch *batch) // RHI only
+{
+ // Note: No use of the clip-related speparate m_current* vars is allowed
+ // here. All stored in batch->clipState instead. To collect state during
+ // renderBatches(), m_currentClipState is used.
+
+ // The stenciling logic is slightly different from the legacy GL path as we
+ // cannot just randomly clear the stencil buffer. We now put all clip
+ // shapes into the stencil buffer for all batches in the frame. This means
+ // that the number of total clips in a scene is reduced (since the stencil
+ // value cannot exceed 255) but we do not need any clears inbetween.
+
+ Q_ASSERT(m_rhi);
+ batch->stencilClipState.updateStencilBuffer = false;
+ if (clipList == m_currentClipState.clipList || Q_UNLIKELY(debug_noclip())) {
+ applyClipStateToGraphicsState();
+ batch->clipState = m_currentClipState;
+ return;
+ }
+
+ ClipState::ClipType clipType = ClipState::NoClip;
+ QRect scissorRect;
+ QVarLengthArray<const QSGClipNode *, 4> stencilClipNodes;
+ const QSGClipNode *clip = clipList;
+
+ batch->stencilClipState.drawCalls.reset();
+ int totalVSize = 0;
+ int totalISize = 0;
+ int totalUSize = 0;
+ const int StencilClipUbufSize = 64;
+
+ while (clip) {
+ QMatrix4x4 m = m_current_projection_matrix_native_ndc;
+ if (clip->matrix())
+ m *= *clip->matrix();
+
+ 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 & ClipState::ScissorClip)) {
+ clipType |= ClipState::ScissorClip;
+ scissorRect = QRect(ix1, iy1, ix2 - ix1, iy2 - iy1);
+ } else {
+ scissorRect &= QRect(ix1, iy1, ix2 - ix1, iy2 - iy1);
+ }
+ } else {
+ clipType |= ClipState::StencilClip;
+
+ const QSGGeometry *g = clip->geometry();
+ Q_ASSERT(g->attributeCount() > 0);
+
+ const int vertexByteSize = g->sizeOfVertex() * g->vertexCount();
+ // the 4 byte alignment may not actually be needed here
+ totalVSize = aligned(totalVSize, 4) + vertexByteSize;
+ if (g->indexCount()) {
+ const int indexByteSize = g->sizeOfIndex() * g->indexCount();
+ // so no need to worry about NonFourAlignedEffectiveIndexBufferOffset
+ totalISize = aligned(totalISize, 4) + indexByteSize;
+ }
+ // ubuf start offsets must be aligned (typically to 256 bytes)
+ totalUSize = aligned(totalUSize, m_ubufAlignment) + StencilClipUbufSize;
+
+ stencilClipNodes.append(clip);
+ }
+
+ clip = clip->clipList();
+ }
+
+ if (clipType & ClipState::StencilClip) {
+ bool rebuildVBuf = false;
+ if (!batch->stencilClipState.vbuf) {
+ batch->stencilClipState.vbuf = m_rhi->newBuffer(QRhiBuffer::Dynamic, QRhiBuffer::VertexBuffer, totalVSize);
+ rebuildVBuf = true;
+ } else if (batch->stencilClipState.vbuf->size() < totalVSize) {
+ batch->stencilClipState.vbuf->setSize(totalVSize);
+ rebuildVBuf = true;
+ }
+ if (rebuildVBuf) {
+ if (!batch->stencilClipState.vbuf->build()) {
+ qWarning("Failed to build stencil clip vertex buffer");
+ delete batch->stencilClipState.vbuf;
+ batch->stencilClipState.vbuf = nullptr;
+ return;
+ }
+ }
+
+ if (totalISize) {
+ bool rebuildIBuf = false;
+ if (!batch->stencilClipState.ibuf) {
+ batch->stencilClipState.ibuf = m_rhi->newBuffer(QRhiBuffer::Dynamic, QRhiBuffer::IndexBuffer, totalISize);
+ rebuildIBuf = true;
+ } else if (batch->stencilClipState.ibuf->size() < totalISize) {
+ batch->stencilClipState.ibuf->setSize(totalISize);
+ rebuildIBuf = true;
+ }
+ if (rebuildIBuf) {
+ if (!batch->stencilClipState.ibuf->build()) {
+ qWarning("Failed to build stencil clip index buffer");
+ delete batch->stencilClipState.ibuf;
+ batch->stencilClipState.ibuf = nullptr;
+ return;
+ }
+ }
+ }
+
+ bool rebuildUBuf = false;
+ if (!batch->stencilClipState.ubuf) {
+ batch->stencilClipState.ubuf = m_rhi->newBuffer(QRhiBuffer::Dynamic, QRhiBuffer::UniformBuffer, totalUSize);
+ rebuildUBuf = true;
+ } else if (batch->stencilClipState.ubuf->size() < totalUSize) {
+ batch->stencilClipState.ubuf->setSize(totalUSize);
+ rebuildUBuf = true;
+ }
+ if (rebuildUBuf) {
+ if (!batch->stencilClipState.ubuf->build()) {
+ qWarning("Failed to build stencil clip uniform buffer");
+ delete batch->stencilClipState.ubuf;
+ batch->stencilClipState.ubuf = nullptr;
+ return;
+ }
+ }
+
+ if (!batch->stencilClipState.srb) {
+ batch->stencilClipState.srb = m_rhi->newShaderResourceBindings();
+ const QRhiShaderResourceBinding ubufBinding = QRhiShaderResourceBinding::uniformBufferWithDynamicOffset(
+ 0, QRhiShaderResourceBinding::VertexStage, batch->stencilClipState.ubuf, StencilClipUbufSize);
+ batch->stencilClipState.srb->setBindings({ ubufBinding });
+ if (!batch->stencilClipState.srb->build()) {
+ qWarning("Failed to build stencil clip srb");
+ delete batch->stencilClipState.srb;
+ batch->stencilClipState.srb = nullptr;
+ return;
+ }
+ }
+
+ int vOffset = 0;
+ int iOffset = 0;
+ int uOffset = 0;
+ for (const QSGClipNode *clip : stencilClipNodes) {
+ const QSGGeometry *g = clip->geometry();
+ const QSGGeometry::Attribute *a = g->attributes();
+ StencilClipState::StencilDrawCall drawCall;
+ const bool firstStencilClipInBatch = batch->stencilClipState.drawCalls.isEmpty();
+
+ 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());
+ }
+#ifndef QT_NO_DEBUG
+ else {
+ if (gpTopology(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())
+ 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;
+
+ int indexByteSize = 0;
+ if (g->indexCount()) {
+ drawCall.ibufOffset = aligned(iOffset, 4);
+ indexByteSize = g->sizeOfIndex() * g->indexCount();
+ iOffset += indexByteSize;
+ }
+
+ drawCall.ubufOffset = aligned(uOffset, m_ubufAlignment);
+ uOffset += StencilClipUbufSize;
+
+ QMatrix4x4 matrixYUpNDC = m_current_projection_matrix;
+ if (clip->matrix())
+ matrixYUpNDC *= *clip->matrix();
+
+ m_resourceUpdates->updateDynamicBuffer(batch->stencilClipState.ubuf, drawCall.ubufOffset, 64, matrixYUpNDC.constData());
+ m_resourceUpdates->updateDynamicBuffer(batch->stencilClipState.vbuf, drawCall.vbufOffset, vertexByteSize, g->vertexData());
+ if (indexByteSize)
+ m_resourceUpdates->updateDynamicBuffer(batch->stencilClipState.ibuf, drawCall.ibufOffset, indexByteSize, g->indexData());
+
+ // stencil ref goes 1, 1, 2, 3, 4, ..., N for the clips in the first batch,
+ // then N+1, N+1, N+2, N+3, ... for the next batch,
+ // and so on.
+ // Note the different stencilOp for the first and the subsequent clips.
+ drawCall.stencilRef = firstStencilClipInBatch ? m_currentClipState.stencilRef + 1 : m_currentClipState.stencilRef;
+ m_currentClipState.stencilRef += 1;
+
+ drawCall.vertexCount = g->vertexCount();
+ drawCall.indexCount = g->indexCount();
+ drawCall.indexFormat = indexFormat(g);
+ batch->stencilClipState.drawCalls.add(drawCall);
+ }
+
+ if (!m_stencilClipCommon.vs.isValid())
+ m_stencilClipCommon.vs = QSGMaterialRhiShaderPrivate::loadShader(QLatin1String(":/qt-project.org/scenegraph/shaders_ng/stencilclip.vert.qsb"));
+
+ if (!m_stencilClipCommon.fs.isValid())
+ m_stencilClipCommon.fs = QSGMaterialRhiShaderPrivate::loadShader(QLatin1String(":/qt-project.org/scenegraph/shaders_ng/stencilclip.frag.qsb"));
+
+ if (!m_stencilClipCommon.replacePs)
+ m_stencilClipCommon.replacePs = buildStencilPipeline(batch, true);
+
+ if (!m_stencilClipCommon.incrPs)
+ m_stencilClipCommon.incrPs = buildStencilPipeline(batch, false);
+
+ batch->stencilClipState.updateStencilBuffer = true;
+ }
+
+ m_currentClipState.clipList = clipList;
+ m_currentClipState.type = clipType;
+ m_currentClipState.scissor = QRhiScissor(scissorRect.x(), scissorRect.y(),
+ scissorRect.width(), scissorRect.height());
+
+ applyClipStateToGraphicsState();
+ batch->clipState = m_currentClipState;
+}
+
+void Renderer::enqueueStencilDraw(const Batch *batch) // RHI only
+{
+ // cliptype stencil + updateStencilBuffer==false means the batch uses
+ // stenciling but relies on the stencil data generated by a previous batch
+ // (due to the having the same clip node). Do not enqueue draw calls for
+ // stencil in this case as the stencil buffer is already up-to-date.
+ if (!batch->stencilClipState.updateStencilBuffer)
+ return;
+
+ QRhiCommandBuffer *cb = commandBuffer();
+ const int count = batch->stencilClipState.drawCalls.size();
+ for (int i = 0; i < count; ++i) {
+ const StencilClipState::StencilDrawCall &drawCall(batch->stencilClipState.drawCalls.at(i));
+ QRhiShaderResourceBindings *srb = batch->stencilClipState.srb;
+ 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);
+ }
+ cb->setStencilRef(drawCall.stencilRef);
+ const QRhiCommandBuffer::VertexInput vbufBinding(batch->stencilClipState.vbuf, drawCall.vbufOffset);
+ if (drawCall.indexCount) {
+ cb->setVertexInput(0, 1, &vbufBinding,
+ batch->stencilClipState.ibuf, drawCall.ibufOffset, drawCall.indexFormat);
+ cb->drawIndexed(drawCall.indexCount);
+ } else {
+ cb->setVertexInput(0, 1, &vbufBinding);
+ cb->draw(drawCall.vertexCount);
+ }
+ }
+}
+
+void Renderer::setActiveRhiShader(QSGMaterialRhiShader *program, ShaderManager::Shader *shader) // RHI only
+{
+ Q_ASSERT(m_rhi);
+ m_currentRhiProgram = program;
+ m_currentShader = shader;
+ m_currentMaterial = nullptr;
+}
+
+void Renderer::updateLineWidth(QSGGeometry *g) // legacy (GL-only)
+{
+ if (g->drawingMode() == GL_LINE_STRIP || g->drawingMode() == GL_LINE_LOOP || g->drawingMode() == GL_LINES)
+ glLineWidth(g->lineWidth());
+#if !defined(QT_OPENGL_ES_2)
+ else {
+ QOpenGLContext *ctx = m_context->openglContext();
+ if (!ctx->isOpenGLES() && g->drawingMode() == GL_POINTS) {
+ QOpenGLFunctions_1_0 *gl1funcs = nullptr;
+ QOpenGLFunctions_3_2_Core *gl3funcs = nullptr;
+ if (ctx->format().profile() == QSurfaceFormat::CoreProfile)
+ gl3funcs = ctx->versionFunctions<QOpenGLFunctions_3_2_Core>();
+ else
+ gl1funcs = ctx->versionFunctions<QOpenGLFunctions_1_0>();
+ Q_ASSERT(gl1funcs || gl3funcs);
+ if (gl1funcs)
+ gl1funcs->glPointSize(g->lineWidth());
+ else
+ gl3funcs->glPointSize(g->lineWidth());
+ }
+ }
+#endif
+}
+
+void Renderer::renderMergedBatch(const Batch *batch) // legacy (GL-only)
{
if (batch->vertexCount == 0 || batch->indexCount == 0)
return;
@@ -2289,13 +3027,14 @@ void Renderer::renderMergedBatch(const Batch *batch)
QSGMaterial *material = gn->activeMaterial();
- ShaderManager::Shader *sms = m_useDepthBuffer ? m_shaderManager->prepareMaterial(material) : m_shaderManager->prepareMaterialNoRewrite(material);
+ ShaderManager::Shader *sms = m_useDepthBuffer ? m_shaderManager->prepareMaterial(material)
+ : m_shaderManager->prepareMaterialNoRewrite(material);
if (!sms)
return;
- QSGMaterialShader *program = sms->program;
+ Q_ASSERT(sms->programGL.program);
if (m_currentShader != sms)
- setActiveShader(program, sms);
+ setActiveShader(sms->programGL.program, sms);
m_current_opacity = gn->inheritedOpacity();
if (!qFuzzyCompare(sms->lastOpacity, float(m_current_opacity))) {
@@ -2303,7 +3042,7 @@ void Renderer::renderMergedBatch(const Batch *batch)
sms->lastOpacity = m_current_opacity;
}
- program->updateState(state(dirty), material, m_currentMaterial);
+ sms->programGL.program->updateState(state(dirty), material, m_currentMaterial);
#ifndef QT_NO_DEBUG
if (qsg_test_and_clear_material_failure()) {
@@ -2320,9 +3059,9 @@ void Renderer::renderMergedBatch(const Batch *batch)
m_currentMaterial = material;
- QSGGeometry* g = gn->geometry();
+ QSGGeometry *g = gn->geometry();
updateLineWidth(g);
- char const *const *attrNames = program->attributeNames();
+ char const *const *attrNames = sms->programGL.program->attributeNames();
for (int i=0; i<batch->drawSets.size(); ++i) {
const DrawSet &draw = batch->drawSets.at(i);
int offset = 0;
@@ -2335,13 +3074,13 @@ void Renderer::renderMergedBatch(const Batch *batch)
offset += a.tupleSize * size_of_type(a.type);
}
if (m_useDepthBuffer)
- glVertexAttribPointer(sms->pos_order, 1, GL_FLOAT, false, 0, (void *) (qintptr) (draw.zorders));
+ glVertexAttribPointer(sms->programGL.pos_order, 1, GL_FLOAT, false, 0, (void *) (qintptr) (draw.zorders));
glDrawElements(g->drawingMode(), draw.indexCount, GL_UNSIGNED_SHORT, (void *) (qintptr) (indexBase + draw.indices));
}
}
-void Renderer::renderUnmergedBatch(const Batch *batch)
+void Renderer::renderUnmergedBatch(const Batch *batch) // legacy (GL-only)
{
if (batch->vertexCount == 0)
return;
@@ -2371,7 +3110,7 @@ void Renderer::renderUnmergedBatch(const Batch *batch)
glBindBuffer(GL_ARRAY_BUFFER, batch->vbo.id);
char *indexBase = nullptr;
- const auto separateIndexBuffer = m_context->separateIndexBuffer();
+ const bool separateIndexBuffer = m_context->separateIndexBuffer();
const Buffer *indexBuf = separateIndexBuffer ? &batch->ibo : &batch->vbo;
if (batch->indexCount) {
if (m_context->hasBrokenIndexBufferObjects()) {
@@ -2389,10 +3128,10 @@ void Renderer::renderUnmergedBatch(const Batch *batch)
ShaderManager::Shader *sms = m_shaderManager->prepareMaterialNoRewrite(material);
if (!sms)
return;
- QSGMaterialShader *program = sms->program;
- if (sms != m_currentShader)
- setActiveShader(program, sms);
+ Q_ASSERT(sms->programGL.program);
+ if (m_currentShader != sms)
+ setActiveShader(sms->programGL.program, sms);
m_current_opacity = gn->inheritedOpacity();
if (sms->lastOpacity != m_current_opacity) {
@@ -2419,7 +3158,7 @@ void Renderer::renderUnmergedBatch(const Batch *batch)
m_current_projection_matrix(2, 3) = 1.0f - e->order * m_zRange;
}
- program->updateState(state(dirty), material, m_currentMaterial);
+ sms->programGL.program->updateState(state(dirty), material, m_currentMaterial);
#ifndef QT_NO_DEBUG
if (qsg_test_and_clear_material_failure()) {
@@ -2435,8 +3174,8 @@ void Renderer::renderUnmergedBatch(const Batch *batch)
// 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();
+ QSGGeometry *g = gn->geometry();
+ char const *const *attrNames = sms->programGL.program->attributeNames();
int offset = 0;
for (int j = 0; attrNames[j]; ++j) {
if (!*attrNames[j])
@@ -2463,101 +3202,926 @@ void Renderer::renderUnmergedBatch(const Batch *batch)
}
}
-void Renderer::updateLineWidth(QSGGeometry *g)
+static inline bool needsBlendConstant(QRhiGraphicsPipeline::BlendFactor f)
{
- if (g->drawingMode() == GL_LINE_STRIP || g->drawingMode() == GL_LINE_LOOP || g->drawingMode() == GL_LINES)
- glLineWidth(g->lineWidth());
-#if !defined(QT_OPENGL_ES_2)
- else {
- QOpenGLContext *ctx = m_context->openglContext();
- if (!ctx->isOpenGLES() && g->drawingMode() == GL_POINTS) {
- QOpenGLFunctions_1_0 *gl1funcs = nullptr;
- QOpenGLFunctions_3_2_Core *gl3funcs = nullptr;
- if (ctx->format().profile() == QSurfaceFormat::CoreProfile)
- gl3funcs = ctx->versionFunctions<QOpenGLFunctions_3_2_Core>();
- else
- gl1funcs = ctx->versionFunctions<QOpenGLFunctions_1_0>();
- Q_ASSERT(gl1funcs || gl3funcs);
- if (gl1funcs)
- gl1funcs->glPointSize(g->lineWidth());
- else
- gl3funcs->glPointSize(g->lineWidth());
+ return f == QRhiGraphicsPipeline::ConstantColor
+ || f == QRhiGraphicsPipeline::OneMinusConstantColor
+ || f == QRhiGraphicsPipeline::ConstantAlpha
+ || f == QRhiGraphicsPipeline::OneMinusConstantAlpha;
+}
+
+// With QRhi renderBatches() is split to two steps: prepare and render.
+//
+// Prepare goes through the batches and elements, and set up a graphics
+// pipeline, srb, uniform buffer, calculates clipping, based on m_gstate, the
+// material (shaders), and the batches. This step does not touch the command
+// buffer or renderpass-related state (m_pstate).
+//
+// The render step then starts a renderpass, and goes through all
+// batches/elements again and records setGraphicsPipeline, drawIndexed, etc. on
+// the command buffer. The prepare step's accumulated global state like
+// m_gstate must not be used here. Rather, all data needed for rendering is
+// available from Batch/Element at this stage. Bookkeeping of state in the
+// renderpass is done via m_pstate.
+
+bool Renderer::ensurePipelineState(Element *e, const ShaderManager::Shader *sms) // RHI only, [prepare step]
+{
+ // In unmerged batches the srbs in the elements are all compatible layout-wise.
+ const GraphicsPipelineStateKey k { m_gstate, sms, renderPassDescriptor(), e->srb };
+
+ // Note: dynamic state (viewport rect, scissor rect, stencil ref, blend
+ // constant) is never a part of GraphicsState/QRhiGraphicsPipeline.
+
+ // See if there is an existing, matching pipeline state object.
+ auto it = m_pipelines.constFind(k);
+ if (it != m_pipelines.constEnd()) {
+ e->ps = *it;
+ return true;
+ }
+
+ // Build a new one. This is potentially expensive.
+ QRhiGraphicsPipeline *ps = m_rhi->newGraphicsPipeline();
+ ps->setShaderStages(sms->programRhi.shaderStages);
+ ps->setVertexInputLayout(sms->programRhi.inputLayout);
+ ps->setShaderResourceBindings(e->srb);
+ ps->setRenderPassDescriptor(renderPassDescriptor());
+
+ QRhiGraphicsPipeline::Flags flags = 0;
+ if (needsBlendConstant(m_gstate.srcColor) || needsBlendConstant(m_gstate.dstColor))
+ flags |= QRhiGraphicsPipeline::UsesBlendConstants;
+ if (m_gstate.usesScissor)
+ flags |= QRhiGraphicsPipeline::UsesScissor;
+ if (m_gstate.stencilTest)
+ flags |= QRhiGraphicsPipeline::UsesStencilRef;
+
+ ps->setFlags(flags);
+ ps->setTopology(gpTopology(m_gstate.drawMode));
+ ps->setCullMode(m_gstate.cullMode);
+
+ QRhiGraphicsPipeline::TargetBlend blend;
+ blend.colorWrite = m_gstate.colorWrite;
+ blend.enable = m_gstate.blending;
+ blend.srcColor = m_gstate.srcColor;
+ blend.dstColor = m_gstate.dstColor;
+ ps->setTargetBlends({ blend });
+
+ ps->setDepthTest(m_gstate.depthTest);
+ ps->setDepthWrite(m_gstate.depthWrite);
+ ps->setDepthOp(m_gstate.depthFunc);
+
+ if (m_gstate.stencilTest) {
+ ps->setStencilTest(true);
+ QRhiGraphicsPipeline::StencilOpState stencilOp;
+ stencilOp.compareOp = QRhiGraphicsPipeline::Equal;
+ stencilOp.failOp = QRhiGraphicsPipeline::Keep;
+ stencilOp.depthFailOp = QRhiGraphicsPipeline::Keep;
+ stencilOp.passOp = QRhiGraphicsPipeline::Keep;
+ ps->setStencilFront(stencilOp);
+ ps->setStencilBack(stencilOp);
+ }
+
+ ps->setSampleCount(m_gstate.sampleCount);
+
+ ps->setLineWidth(m_gstate.lineWidth);
+
+ //qDebug("building new ps %p", ps);
+ if (!ps->build()) {
+ qWarning("Failed to build graphics pipeline state");
+ delete ps;
+ return false;
+ }
+
+ m_pipelines.insert(k, ps);
+ e->ps = ps;
+ return true;
+}
+
+static QRhiSampler *newSampler(QRhi *rhi, const QSGSamplerDescription &desc)
+{
+ QRhiSampler::Filter magFilter;
+ QRhiSampler::Filter minFilter;
+ QRhiSampler::Filter mipmapMode;
+ QRhiSampler::AddressMode u;
+ QRhiSampler::AddressMode v;
+
+ switch (desc.filtering) {
+ case QSGTexture::None:
+ Q_FALLTHROUGH();
+ case QSGTexture::Nearest:
+ magFilter = minFilter = QRhiSampler::Nearest;
+ break;
+ case QSGTexture::Linear:
+ magFilter = minFilter = QRhiSampler::Linear;
+ break;
+ default:
+ Q_UNREACHABLE();
+ magFilter = minFilter = QRhiSampler::Nearest;
+ break;
+ }
+
+ switch (desc.mipmapFiltering) {
+ case QSGTexture::None:
+ mipmapMode = QRhiSampler::None;
+ break;
+ case QSGTexture::Nearest:
+ mipmapMode = QRhiSampler::Nearest;
+ break;
+ case QSGTexture::Linear:
+ mipmapMode = QRhiSampler::Linear;
+ break;
+ default:
+ Q_UNREACHABLE();
+ mipmapMode = QRhiSampler::None;
+ break;
+ }
+
+ switch (desc.horizontalWrap) {
+ case QSGTexture::Repeat:
+ u = QRhiSampler::Repeat;
+ break;
+ case QSGTexture::ClampToEdge:
+ u = QRhiSampler::ClampToEdge;
+ break;
+ case QSGTexture::MirroredRepeat:
+ u = QRhiSampler::Mirror;
+ break;
+ default:
+ Q_UNREACHABLE();
+ u = QRhiSampler::ClampToEdge;
+ break;
+ }
+
+ switch (desc.verticalWrap) {
+ case QSGTexture::Repeat:
+ v = QRhiSampler::Repeat;
+ break;
+ case QSGTexture::ClampToEdge:
+ v = QRhiSampler::ClampToEdge;
+ break;
+ case QSGTexture::MirroredRepeat:
+ v = QRhiSampler::Mirror;
+ break;
+ default:
+ Q_UNREACHABLE();
+ v = QRhiSampler::ClampToEdge;
+ break;
+ }
+
+ return rhi->newSampler(magFilter, minFilter, mipmapMode, u, v);
+}
+
+QRhiTexture *Renderer::dummyTexture()
+{
+ if (!m_dummyTexture) {
+ m_dummyTexture = m_rhi->newTexture(QRhiTexture::RGBA8, QSize(64, 64));
+ if (m_dummyTexture->build()) {
+ if (m_resourceUpdates) {
+ QImage img(m_dummyTexture->pixelSize(), QImage::Format_RGBA8888_Premultiplied);
+ img.fill(0);
+ m_resourceUpdates->uploadTexture(m_dummyTexture, img);
+ }
+ }
+ }
+ return m_dummyTexture;
+}
+
+static void rendererToMaterialGraphicsState(QSGMaterialRhiShader::GraphicsPipelineState *dst,
+ GraphicsState *src)
+{
+ dst->blendEnable = src->blending;
+
+ // the enum values should match, sanity check it
+ Q_ASSERT(int(QSGMaterialRhiShader::GraphicsPipelineState::OneMinusSrc1Alpha) == int(QRhiGraphicsPipeline::OneMinusSrc1Alpha));
+ Q_ASSERT(int(QSGMaterialRhiShader::GraphicsPipelineState::A) == int(QRhiGraphicsPipeline::A));
+ Q_ASSERT(int(QSGMaterialRhiShader::GraphicsPipelineState::CullBack) == int(QRhiGraphicsPipeline::Back));
+
+ dst->srcColor = QSGMaterialRhiShader::GraphicsPipelineState::BlendFactor(src->srcColor);
+ dst->dstColor = QSGMaterialRhiShader::GraphicsPipelineState::BlendFactor(src->dstColor);
+
+ dst->colorWrite = QSGMaterialRhiShader::GraphicsPipelineState::ColorMask(int(src->colorWrite));
+
+ dst->cullMode = QSGMaterialRhiShader::GraphicsPipelineState::CullMode(src->cullMode);
+}
+
+static void materialToRendererGraphicsState(GraphicsState *dst,
+ QSGMaterialRhiShader::GraphicsPipelineState *src)
+{
+ dst->blending = src->blendEnable;
+ dst->srcColor = QRhiGraphicsPipeline::BlendFactor(src->srcColor);
+ dst->dstColor = QRhiGraphicsPipeline::BlendFactor(src->dstColor);
+ dst->colorWrite = QRhiGraphicsPipeline::ColorMask(int(src->colorWrite));
+ dst->cullMode = QRhiGraphicsPipeline::CullMode(src->cullMode);
+}
+
+void Renderer::updateMaterialDynamicData(ShaderManager::Shader *sms,
+ const QSGMaterialRhiShader::RenderState &renderState,
+ QSGMaterial *material,
+ QVector<QRhiShaderResourceBinding> *bindings,
+ const Batch *batch,
+ int ubufOffset,
+ int ubufRegionSize) // RHI only, [prepare step]
+{
+ m_current_resource_update_batch = m_resourceUpdates;
+
+ QSGMaterialRhiShader *shader = sms->programRhi.program;
+ QSGMaterialRhiShaderPrivate *pd = QSGMaterialRhiShaderPrivate::get(shader);
+ if (pd->ubufBinding >= 0) {
+ m_current_uniform_data = &pd->masterUniformData;
+ const bool changed = shader->updateUniformData(renderState, material, m_currentMaterial);
+
+ if (changed || !batch->ubufDataValid)
+ m_resourceUpdates->updateDynamicBuffer(batch->ubuf, ubufOffset, ubufRegionSize, pd->masterUniformData.constData());
+
+ bindings->append(QRhiShaderResourceBinding::uniformBuffer(pd->ubufBinding,
+ pd->ubufStages,
+ batch->ubuf,
+ ubufOffset,
+ ubufRegionSize));
+ }
+
+ for (int binding = 0; binding < QSGMaterialRhiShaderPrivate::MAX_SHADER_RESOURCE_BINDINGS; ++binding) {
+ const QRhiShaderResourceBinding::StageFlags stages = pd->combinedImageSamplerBindings[binding];
+ if (!stages)
+ continue;
+
+ QSGTexture *prevTex = pd->textureBindingTable[binding];
+ QSGTexture *t = prevTex;
+
+ shader->updateSampledImage(renderState, binding, &t, material, m_currentMaterial);
+ if (!t) {
+ qWarning("No QSGTexture provided from updateSampledImage(). This is wrong.");
+ continue;
+ }
+
+ QSGTexturePrivate *td = QSGTexturePrivate::get(t);
+ // prevTex may be invalid at this point, avoid dereferencing it
+ if (t != prevTex || td->hasDirtySamplerOptions()) {
+ // The QSGTexture, and so the sampler parameters, may have changed.
+ // The rhiTexture is not relevant here.
+ td->resetDirtySamplerOptions();
+ pd->textureBindingTable[binding] = t; // does not own
+ pd->samplerBindingTable[binding] = nullptr;
+ if (t->anisotropyLevel() != QSGTexture::AnisotropyNone) // ###
+ qWarning("QSGTexture anisotropy levels are not currently supported");
+
+ const QSGSamplerDescription samplerDesc = QSGSamplerDescription::fromTexture(t);
+ QRhiSampler *sampler = nullptr;
+ auto it = m_samplers.constFind(samplerDesc);
+ if (it != m_samplers.constEnd()) {
+ sampler = *it;
+ Q_ASSERT(sampler);
+ } else {
+ sampler = newSampler(m_rhi, samplerDesc);
+ if (!sampler->build()) {
+ qWarning("Failed to build sampler");
+ delete sampler;
+ continue;
+ }
+ m_samplers.insert(samplerDesc, sampler);
+ }
+ pd->samplerBindingTable[binding] = sampler; // does not own
+ }
+
+ if (pd->textureBindingTable[binding] && pd->samplerBindingTable[binding]) {
+ QRhiTexture *texture = pd->textureBindingTable[binding]->rhiTexture();
+ // texture may be null if the update above failed for any reason,
+ // or if the QSGTexture chose to return null intentionally. This is
+ // valid and we still need to provide something to the shader.
+ if (!texture)
+ texture = dummyTexture();
+ QRhiSampler *sampler = pd->samplerBindingTable[binding];
+ bindings->append(QRhiShaderResourceBinding::sampledTexture(binding,
+ stages,
+ texture,
+ sampler));
}
}
+
+#ifndef QT_NO_DEBUG
+ if (bindings->isEmpty())
+ qWarning("No shader resources for material %p, this is odd.", material);
#endif
}
-void Renderer::renderBatches()
+void Renderer::updateMaterialStaticData(ShaderManager::Shader *sms,
+ const QSGMaterialRhiShader::RenderState &renderState,
+ QSGMaterial *material,
+ Batch *batch,
+ bool *gstateChanged) // RHI only, [prepare step]
{
+ QSGMaterialRhiShader *shader = sms->programRhi.program;
+ *gstateChanged = false;
+ if (shader->flags().testFlag(QSGMaterialRhiShader::UpdatesGraphicsPipelineState)) {
+ // generate the public mini-state from m_gstate, invoke the material,
+ // write the changes, if any, back to m_gstate, together with a way to
+ // roll those back.
+ QSGMaterialRhiShader::GraphicsPipelineState shaderPs;
+ rendererToMaterialGraphicsState(&shaderPs, &m_gstate);
+ const bool changed = shader->updateGraphicsPipelineState(renderState, &shaderPs, material, m_currentMaterial);
+ if (changed) {
+ m_gstateStack.push(m_gstate);
+ materialToRendererGraphicsState(&m_gstate, &shaderPs);
+ if (needsBlendConstant(m_gstate.srcColor) || needsBlendConstant(m_gstate.dstColor))
+ batch->blendConstant = shaderPs.blendConstant;
+ *gstateChanged = true;
+ }
+ }
+}
+
+bool Renderer::prepareRenderMergedBatch(Batch *batch, PreparedRenderBatch *renderBatch) // split prepare-render (RHI only)
+{
+ if (batch->vertexCount == 0 || batch->indexCount == 0)
+ return false;
+
+ Element *e = batch->first;
+ Q_ASSERT(e);
+
+#ifndef QT_NO_DEBUG_OUTPUT
if (Q_UNLIKELY(debug_render())) {
- qDebug().nospace() << "Rendering:" << endl
- << " -> Opaque: " << qsg_countNodesInBatches(m_opaqueBatches) << " nodes in " << m_opaqueBatches.size() << " batches..." << endl
- << " -> Alpha: " << qsg_countNodesInBatches(m_alphaBatches) << " nodes in " << m_alphaBatches.size() << " batches...";
+ QDebug debug = qDebug();
+ debug << " -"
+ << batch
+ << (batch->uploadedThisFrame ? "[ upload]" : "[retained]")
+ << (e->node->clipList() ? "[ clip]" : "[noclip]")
+ << (batch->isOpaque ? "[opaque]" : "[ alpha]")
+ << "[ merged]"
+ << " Nodes:" << QString::fromLatin1("%1").arg(qsg_countNodesInBatch(batch), 4).toLatin1().constData()
+ << " Vertices:" << QString::fromLatin1("%1").arg(batch->vertexCount, 5).toLatin1().constData()
+ << " Indices:" << QString::fromLatin1("%1").arg(batch->indexCount, 5).toLatin1().constData()
+ << " root:" << batch->root;
+ if (batch->drawSets.size() > 1)
+ debug << "sets:" << batch->drawSets.size();
+ if (!batch->isOpaque)
+ debug << "opacity:" << e->node->inheritedOpacity();
+ batch->uploadedThisFrame = false;
}
+#endif
- QRect r = viewportRect();
- glViewport(r.x(), deviceRect().bottom() - r.bottom(), r.width(), r.height());
- glClearColor(clearColor().redF(), clearColor().greenF(), clearColor().blueF(), clearColor().alphaF());
+ QSGGeometryNode *gn = e->node;
- if (m_useDepthBuffer) {
- glClearDepthf(1); // calls glClearDepth() under the hood for desktop OpenGL
- glEnable(GL_DEPTH_TEST);
- glDepthFunc(GL_LESS);
- glDepthMask(true);
- glDisable(GL_BLEND);
+ // We always have dirty matrix as all batches are at a unique z range.
+ QSGMaterialShader::RenderState::DirtyStates dirty = QSGMaterialShader::RenderState::DirtyMatrix;
+ if (batch->root)
+ m_current_model_view_matrix = qsg_matrixForRoot(batch->root);
+ else
+ m_current_model_view_matrix.setToIdentity();
+ m_current_determinant = m_current_model_view_matrix.determinant();
+ m_current_projection_matrix = projectionMatrix();
+ m_current_projection_matrix_native_ndc = projectionMatrixWithNativeNDC();
+
+ QSGMaterial *material = gn->activeMaterial();
+ updateClipState(gn->clipList(), batch);
+
+ const QSGGeometry *g = gn->geometry();
+ ShaderManager::Shader *sms = m_useDepthBuffer ? m_shaderManager->prepareMaterial(material, true, g)
+ : m_shaderManager->prepareMaterialNoRewrite(material, true, g);
+ if (!sms)
+ return false;
+
+ Q_ASSERT(sms->programRhi.program);
+ if (m_currentShader != sms)
+ setActiveRhiShader(sms->programRhi.program, sms);
+
+ m_current_opacity = gn->inheritedOpacity();
+ if (!qFuzzyCompare(sms->lastOpacity, float(m_current_opacity))) {
+ dirty |= QSGMaterialShader::RenderState::DirtyOpacity;
+ sms->lastOpacity = m_current_opacity;
+ }
+
+ QSGMaterialRhiShaderPrivate *pd = QSGMaterialRhiShaderPrivate::get(sms->programRhi.program);
+ const int ubufSize = pd->masterUniformData.size();
+ if (pd->ubufBinding >= 0) {
+ bool ubufRebuild = false;
+ if (!batch->ubuf) {
+ batch->ubuf = m_rhi->newBuffer(QRhiBuffer::Dynamic, QRhiBuffer::UniformBuffer, ubufSize);
+ ubufRebuild = true;
+ } else {
+ if (batch->ubuf->size() < ubufSize) {
+ batch->ubuf->setSize(ubufSize);
+ ubufRebuild = true;
+ }
+ }
+ if (ubufRebuild) {
+ batch->ubufDataValid = false;
+ if (!batch->ubuf->build()) {
+ qWarning("Failed to build uniform buffer of size %d bytes", ubufSize);
+ delete batch->ubuf;
+ batch->ubuf = nullptr;
+ return false;
+ }
+ }
+ }
+
+ const QSGMaterialRhiShader::RenderState renderState = rhiState(QSGMaterialRhiShader::RenderState::DirtyStates(int(dirty)));
+
+ bool pendingGStatePop = false;
+ updateMaterialStaticData(sms, renderState, material, batch, &pendingGStatePop);
+
+ QVector<QRhiShaderResourceBinding> bindings;
+ updateMaterialDynamicData(sms, renderState, material, &bindings, batch, 0, ubufSize);
+
+#ifndef QT_NO_DEBUG
+ if (qsg_test_and_clear_material_failure()) {
+ qDebug("QSGMaterial::updateState triggered an error (merged), batch will be skipped:");
+ Element *ee = e;
+ while (ee) {
+ qDebug() << " -" << ee->node;
+ ee = ee->nextInBatch;
+ }
+ QSGNodeDumper::dump(rootNode());
+ qFatal("Aborting: scene graph is invalid...");
+ }
+#endif
+
+ e->srb = m_shaderManager->srb(bindings);
+
+ m_gstate.drawMode = QSGGeometry::DrawingMode(g->drawingMode());
+ m_gstate.lineWidth = g->lineWidth();
+
+ const bool hasPipeline = ensurePipelineState(e, sms);
+
+ if (pendingGStatePop)
+ m_gstate = m_gstateStack.pop();
+
+ if (!hasPipeline)
+ return false;
+
+ batch->ubufDataValid = true;
+
+ m_currentMaterial = material;
+
+ renderBatch->batch = batch;
+ renderBatch->sms = sms;
+
+ return true;
+}
+
+void Renderer::checkLineWidth(QSGGeometry *g)
+{
+ if (g->drawingMode() == QSGGeometry::DrawLines || g->drawingMode() == QSGGeometry::DrawLineLoop
+ || g->drawingMode() == QSGGeometry::DrawLineStrip)
+ {
+ if (g->lineWidth() != 1.0f) {
+ static bool checkedWideLineSupport = false;
+ if (!checkedWideLineSupport) {
+ checkedWideLineSupport = true;
+ if (!m_rhi->isFeatureSupported(QRhi::WideLines))
+ qWarning("Line widths other than 1 are not supported by the graphics API");
+ }
+ }
+ } else if (g->drawingMode() == QSGGeometry::DrawPoints) {
+ if (g->lineWidth() != 1.0f) {
+ static bool warnedPointSize = false;
+ if (!warnedPointSize) {
+ warnedPointSize = true;
+ qWarning("Point size is not controllable by QSGGeometry. "
+ "Set gl_PointSize from the vertex shader instead.");
+ }
+ }
+ }
+}
+
+void Renderer::renderMergedBatch(PreparedRenderBatch *renderBatch) // split prepare-render (RHI only)
+{
+ const Batch *batch = renderBatch->batch;
+ Element *e = batch->first;
+ QSGGeometryNode *gn = e->node;
+ QSGGeometry *g = gn->geometry();
+ checkLineWidth(g);
+
+ if (batch->clipState.type & ClipState::StencilClip)
+ enqueueStencilDraw(batch);
+
+ QRhiCommandBuffer *cb = commandBuffer();
+ setGraphicsPipeline(cb, batch, e);
+
+ for (int i = 0, ie = batch->drawSets.size(); i != ie; ++i) {
+ const DrawSet &draw = batch->drawSets.at(i);
+ const QRhiCommandBuffer::VertexInput vbufBindings[] = {
+ { batch->vbo.buf, quint32(draw.vertices) },
+ { batch->vbo.buf, quint32(draw.zorders) }
+ };
+ cb->setVertexInput(VERTEX_BUFFER_BINDING, 2, vbufBindings,
+ batch->ibo.buf, draw.indices,
+ m_uint32IndexForRhi ? QRhiCommandBuffer::IndexUInt32 : QRhiCommandBuffer::IndexUInt16);
+ cb->drawIndexed(draw.indexCount);
+ }
+}
+
+bool Renderer::prepareRenderUnmergedBatch(Batch *batch, PreparedRenderBatch *renderBatch) // split prepare-render (RHI only)
+{
+ if (batch->vertexCount == 0)
+ return false;
+
+ Element *e = batch->first;
+ Q_ASSERT(e);
+
+ if (Q_UNLIKELY(debug_render())) {
+ qDebug() << " -"
+ << batch
+ << (batch->uploadedThisFrame ? "[ upload]" : "[retained]")
+ << (e->node->clipList() ? "[ clip]" : "[noclip]")
+ << (batch->isOpaque ? "[opaque]" : "[ alpha]")
+ << "[unmerged]"
+ << " Nodes:" << QString::fromLatin1("%1").arg(qsg_countNodesInBatch(batch), 4).toLatin1().constData()
+ << " Vertices:" << QString::fromLatin1("%1").arg(batch->vertexCount, 5).toLatin1().constData()
+ << " Indices:" << QString::fromLatin1("%1").arg(batch->indexCount, 5).toLatin1().constData()
+ << " root:" << batch->root;
+
+ batch->uploadedThisFrame = false;
+ }
+
+ m_current_projection_matrix = projectionMatrix();
+ m_current_projection_matrix_native_ndc = projectionMatrixWithNativeNDC();
+
+ QSGGeometryNode *gn = e->node;
+ updateClipState(gn->clipList(), batch);
+
+ // We always have dirty matrix as all batches are at a unique z range.
+ QSGMaterialShader::RenderState::DirtyStates dirty = QSGMaterialShader::RenderState::DirtyMatrix;
+
+ // The vertex attributes are assumed to be the same for all elements in the
+ // unmerged batch since the material (and so the shaders) is the same.
+ QSGGeometry *g = gn->geometry();
+ QSGMaterial *material = gn->activeMaterial();
+ ShaderManager::Shader *sms = m_shaderManager->prepareMaterialNoRewrite(material, m_rhi, g);
+ if (!sms)
+ return false;
+
+ Q_ASSERT(sms->programRhi.program);
+ if (m_currentShader != sms)
+ setActiveRhiShader(sms->programRhi.program, sms);
+
+ m_current_opacity = gn->inheritedOpacity();
+ if (sms->lastOpacity != m_current_opacity) {
+ dirty |= QSGMaterialShader::RenderState::DirtyOpacity;
+ sms->lastOpacity = m_current_opacity;
+ }
+
+ QMatrix4x4 rootMatrix = batch->root ? qsg_matrixForRoot(batch->root) : QMatrix4x4();
+
+ QSGMaterialRhiShaderPrivate *pd = QSGMaterialRhiShaderPrivate::get(sms->programRhi.program);
+ const int ubufSize = pd->masterUniformData.size();
+ if (pd->ubufBinding >= 0) {
+ int totalUBufSize = 0;
+ while (e) {
+ totalUBufSize += aligned(ubufSize, m_ubufAlignment);
+ e = e->nextInBatch;
+ }
+ bool ubufRebuild = false;
+ if (!batch->ubuf) {
+ batch->ubuf = m_rhi->newBuffer(QRhiBuffer::Dynamic, QRhiBuffer::UniformBuffer, totalUBufSize);
+ ubufRebuild = true;
+ } else {
+ if (batch->ubuf->size() < totalUBufSize) {
+ batch->ubuf->setSize(totalUBufSize);
+ ubufRebuild = true;
+ }
+ }
+ if (ubufRebuild) {
+ batch->ubufDataValid = false;
+ if (!batch->ubuf->build()) {
+ qWarning("Failed to build uniform buffer of size %d bytes", totalUBufSize);
+ delete batch->ubuf;
+ batch->ubuf = nullptr;
+ return false;
+ }
+ }
+ }
+
+ bool pendingGStatePop = false;
+ updateMaterialStaticData(sms, rhiState(QSGMaterialRhiShader::RenderState::DirtyStates(int(dirty))),
+ material, batch, &pendingGStatePop);
+
+ int ubufOffset = 0;
+ QRhiGraphicsPipeline *ps = nullptr;
+ e = batch->first;
+ while (e) {
+ gn = e->node;
+
+ m_current_model_view_matrix = rootMatrix * *gn->matrix();
+ m_current_determinant = m_current_model_view_matrix.determinant();
+
+ m_current_projection_matrix = projectionMatrix();
+ m_current_projection_matrix_native_ndc = projectionMatrixWithNativeNDC();
+ if (m_useDepthBuffer) {
+ m_current_projection_matrix(2, 2) = m_zRange;
+ m_current_projection_matrix(2, 3) = 1.0f - e->order * m_zRange;
+ }
+
+ QVector<QRhiShaderResourceBinding> bindings;
+ updateMaterialDynamicData(sms, rhiState(QSGMaterialRhiShader::RenderState::DirtyStates(int(dirty))),
+ material, &bindings, batch, ubufOffset, ubufSize);
+
+#ifndef QT_NO_DEBUG
+ if (qsg_test_and_clear_material_failure()) {
+ qDebug("QSGMaterial::updateState() triggered an error (unmerged), batch will be skipped:");
+ qDebug() << " - offending node is" << e->node;
+ QSGNodeDumper::dump(rootNode());
+ qFatal("Aborting: scene graph is invalid...");
+ return false;
+ }
+#endif
+
+ e->srb = m_shaderManager->srb(bindings);
+
+ ubufOffset += aligned(ubufSize, m_ubufAlignment);
+
+ const QSGGeometry::DrawingMode prevDrawMode = m_gstate.drawMode;
+ const float prevLineWidth = m_gstate.lineWidth;
+ m_gstate.drawMode = QSGGeometry::DrawingMode(g->drawingMode());
+ m_gstate.lineWidth = g->lineWidth();
+
+ // Do not bother even looking up the ps if the topology has not changed
+ // since everything else is the same for all elements in the batch.
+ // (except if the material modified blend state)
+ if (!ps || m_gstate.drawMode != prevDrawMode || m_gstate.lineWidth != prevLineWidth || pendingGStatePop) {
+ if (!ensurePipelineState(e, sms)) {
+ if (pendingGStatePop)
+ m_gstate = m_gstateStack.pop();
+ return false;
+ }
+ ps = e->ps;
+ } else {
+ e->ps = ps;
+ }
+
+ // 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;
+
+ // We only need to push this on the very first iteration...
+ dirty &= ~QSGMaterialShader::RenderState::DirtyOpacity;
+
+ e = e->nextInBatch;
+ }
+
+ if (pendingGStatePop)
+ m_gstate = m_gstateStack.pop();
+
+ batch->ubufDataValid = true;
+
+ renderBatch->batch = batch;
+ renderBatch->sms = sms;
+
+ return true;
+}
+
+void Renderer::renderUnmergedBatch(PreparedRenderBatch *renderBatch) // split prepare-render (RHI only)
+{
+ const Batch *batch = renderBatch->batch;
+ Element *e = batch->first;
+ QSGGeometryNode *gn = e->node;
+
+ if (batch->clipState.type & ClipState::StencilClip)
+ enqueueStencilDraw(batch);
+
+ int vOffset = 0;
+ int iOffset = 0;
+ QRhiCommandBuffer *cb = commandBuffer();
+
+ while (e) {
+ gn = e->node;
+ QSGGeometry *g = gn->geometry();
+ checkLineWidth(g);
+ const int effectiveIndexSize = m_uint32IndexForRhi ? sizeof(quint32) : g->sizeOfIndex();
+
+ setGraphicsPipeline(cb, batch, e);
+
+ const QRhiCommandBuffer::VertexInput vbufBinding(batch->vbo.buf, vOffset);
+ if (g->indexCount()) {
+ cb->setVertexInput(VERTEX_BUFFER_BINDING, 1, &vbufBinding,
+ batch->ibo.buf, iOffset,
+ effectiveIndexSize == sizeof(quint32) ? QRhiCommandBuffer::IndexUInt32
+ : QRhiCommandBuffer::IndexUInt16);
+ cb->drawIndexed(g->indexCount());
+ } else {
+ cb->setVertexInput(VERTEX_BUFFER_BINDING, 1, &vbufBinding);
+ cb->draw(g->vertexCount());
+ }
+
+ vOffset += g->sizeOfVertex() * g->vertexCount();
+ iOffset += g->indexCount() * effectiveIndexSize;
+
+ e = e->nextInBatch;
+ }
+}
+
+void Renderer::setGraphicsPipeline(QRhiCommandBuffer *cb, const Batch *batch, Element *e) // RHI only, [render step]
+{
+ cb->setGraphicsPipeline(e->ps);
+
+ if (!m_pstate.viewportSet) {
+ m_pstate.viewportSet = true;
+ cb->setViewport(m_pstate.viewport);
+ }
+ if (batch->clipState.type & ClipState::ScissorClip) {
+ Q_ASSERT(e->ps->flags().testFlag(QRhiGraphicsPipeline::UsesScissor));
+ m_pstate.scissorSet = true;
+ cb->setScissor(batch->clipState.scissor);
} else {
- glDisable(GL_DEPTH_TEST);
- glDepthMask(false);
+ Q_ASSERT(!e->ps->flags().testFlag(QRhiGraphicsPipeline::UsesScissor));
+ // Regardless of the ps not using scissor, the scissor may need to be
+ // reset, depending on the backend. So set the viewport again, which in
+ // turn also sets the scissor on backends where a scissor rect is
+ // always-on (Vulkan).
+ if (m_pstate.scissorSet) {
+ m_pstate.scissorSet = false;
+ cb->setViewport(m_pstate.viewport);
+ }
}
- glDisable(GL_CULL_FACE);
- glColorMask(true, true, true, true);
- glDisable(GL_SCISSOR_TEST);
- glDisable(GL_STENCIL_TEST);
+ if (batch->clipState.type & ClipState::StencilClip) {
+ Q_ASSERT(e->ps->flags().testFlag(QRhiGraphicsPipeline::UsesStencilRef));
+ cb->setStencilRef(batch->clipState.stencilRef);
+ }
+ if (e->ps->flags().testFlag(QRhiGraphicsPipeline::UsesBlendConstants))
+ cb->setBlendConstants(batch->blendConstant);
+
+ cb->setShaderResources(e->srb);
+}
- bindable()->clear(clearMode());
+void Renderer::renderBatches()
+{
+ if (Q_UNLIKELY(debug_render())) {
+ qDebug().nospace() << "Rendering:" << Qt::endl
+ << " -> Opaque: " << qsg_countNodesInBatches(m_opaqueBatches) << " nodes in " << m_opaqueBatches.size() << " batches..." << Qt::endl
+ << " -> Alpha: " << qsg_countNodesInBatches(m_alphaBatches) << " nodes in " << m_alphaBatches.size() << " batches...";
+ }
m_current_opacity = 1;
m_currentMaterial = nullptr;
m_currentShader = nullptr;
m_currentProgram = nullptr;
+ m_currentRhiProgram = nullptr;
m_currentClip = nullptr;
+ m_currentClipState.reset();
+
+ const QRect viewport = viewportRect();
bool renderOpaque = !debug_noopaque();
bool renderAlpha = !debug_noalpha();
- if (Q_LIKELY(renderOpaque)) {
- for (int i=0; i<m_opaqueBatches.size(); ++i) {
- Batch *b = m_opaqueBatches.at(i);
- if (b->merged)
- renderMergedBatch(b);
- else
- renderUnmergedBatch(b);
+ if (!m_rhi) {
+ // legacy, GL-only path
+
+ glViewport(viewport.x(), deviceRect().bottom() - viewport.bottom(), viewport.width(), viewport.height());
+ glClearColor(clearColor().redF(), clearColor().greenF(), clearColor().blueF(), clearColor().alphaF());
+
+ if (m_useDepthBuffer) {
+ glClearDepthf(1); // calls glClearDepth() under the hood for desktop OpenGL
+ glEnable(GL_DEPTH_TEST);
+ glDepthFunc(GL_LESS);
+ glDepthMask(true);
+ glDisable(GL_BLEND);
+ } else {
+ glDisable(GL_DEPTH_TEST);
+ glDepthMask(false);
}
- }
+ glDisable(GL_CULL_FACE);
+ glColorMask(true, true, true, true);
+ glDisable(GL_SCISSOR_TEST);
+ glDisable(GL_STENCIL_TEST);
- glEnable(GL_BLEND);
- if (m_useDepthBuffer)
- glDepthMask(false);
- glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
+ bindable()->clear(clearMode());
+
+ if (m_renderPassRecordingCallbacks.start)
+ m_renderPassRecordingCallbacks.start(m_renderPassRecordingCallbacks.userData);
+
+ if (Q_LIKELY(renderOpaque)) {
+ for (int i=0; i<m_opaqueBatches.size(); ++i) {
+ Batch *b = m_opaqueBatches.at(i);
+ if (b->merged)
+ renderMergedBatch(b);
+ else
+ renderUnmergedBatch(b);
+ }
+ }
- if (Q_LIKELY(renderAlpha)) {
- for (int i=0; i<m_alphaBatches.size(); ++i) {
- Batch *b = m_alphaBatches.at(i);
- if (b->merged)
- renderMergedBatch(b);
- else if (b->isRenderNode)
- renderRenderNode(b);
+ glEnable(GL_BLEND);
+ if (m_useDepthBuffer)
+ glDepthMask(false);
+ glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
+
+ if (Q_LIKELY(renderAlpha)) {
+ for (int i=0; i<m_alphaBatches.size(); ++i) {
+ Batch *b = m_alphaBatches.at(i);
+ if (b->merged)
+ renderMergedBatch(b);
+ else if (b->isRenderNode)
+ renderRenderNode(b);
+ else
+ renderUnmergedBatch(b);
+ }
+ }
+
+ if (m_currentShader)
+ setActiveShader(nullptr, nullptr);
+
+ updateStencilClip(nullptr);
+
+ glBindBuffer(GL_ARRAY_BUFFER, 0);
+ glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
+ glDepthMask(true);
+
+ if (m_renderPassRecordingCallbacks.end)
+ m_renderPassRecordingCallbacks.end(m_renderPassRecordingCallbacks.userData);
+
+ } else {
+ // RHI path
+
+ m_pstate.viewport = QRhiViewport(viewport.x(), deviceRect().bottom() - viewport.bottom(), viewport.width(), viewport.height());
+ m_pstate.clearColor = clearColor();
+ m_pstate.dsClear = QRhiDepthStencilClearValue(1.0f, 0);
+ m_pstate.viewportSet = false;
+ m_pstate.scissorSet = false;
+
+ m_gstate.depthTest = true;
+ m_gstate.depthWrite = true;
+ m_gstate.depthFunc = QRhiGraphicsPipeline::Less;
+ m_gstate.blending = false;
+
+ m_gstate.cullMode = QRhiGraphicsPipeline::None;
+ m_gstate.colorWrite = QRhiGraphicsPipeline::R
+ | QRhiGraphicsPipeline::G
+ | QRhiGraphicsPipeline::B
+ | QRhiGraphicsPipeline::A;
+ m_gstate.usesScissor = false;
+ m_gstate.stencilTest = false;
+
+ m_gstate.sampleCount = renderTarget()->sampleCount();
+
+ QVarLengthArray<PreparedRenderBatch, 64> opaqueRenderBatches;
+ if (Q_LIKELY(renderOpaque)) {
+ for (int i = 0, ie = m_opaqueBatches.size(); i != ie; ++i) {
+ Batch *b = m_opaqueBatches.at(i);
+ PreparedRenderBatch renderBatch;
+ bool ok;
+ if (b->merged)
+ ok = prepareRenderMergedBatch(b, &renderBatch);
+ else
+ ok = prepareRenderUnmergedBatch(b, &renderBatch);
+ if (ok)
+ opaqueRenderBatches.append(renderBatch);
+ }
+ }
+
+ m_gstate.blending = true;
+ // factors never change, always set for premultiplied alpha based blending
+
+ // depth test stays enabled but no need to write out depth from the
+ // transparent (back-to-front) pass
+ m_gstate.depthWrite = false;
+
+ QVarLengthArray<PreparedRenderBatch, 64> alphaRenderBatches;
+ if (Q_LIKELY(renderAlpha)) {
+ for (int i = 0, ie = m_alphaBatches.size(); i != ie; ++i) {
+ Batch *b = m_alphaBatches.at(i);
+ PreparedRenderBatch renderBatch;
+ bool ok;
+ if (b->merged)
+ ok = prepareRenderMergedBatch(b, &renderBatch);
+ else if (b->isRenderNode)
+ ok = false; // ###
+ else
+ ok = prepareRenderUnmergedBatch(b, &renderBatch);
+ if (ok)
+ alphaRenderBatches.append(renderBatch);
+ }
+ }
+
+ QRhiCommandBuffer *cb = commandBuffer();
+ cb->beginPass(renderTarget(), m_pstate.clearColor, m_pstate.dsClear, m_resourceUpdates);
+ m_resourceUpdates = nullptr;
+
+ if (m_renderPassRecordingCallbacks.start)
+ m_renderPassRecordingCallbacks.start(m_renderPassRecordingCallbacks.userData);
+
+ for (int i = 0, ie = opaqueRenderBatches.count(); i != ie; ++i) {
+ PreparedRenderBatch *renderBatch = &opaqueRenderBatches[i];
+ if (renderBatch->batch->merged)
+ renderMergedBatch(renderBatch);
else
- renderUnmergedBatch(b);
+ renderUnmergedBatch(renderBatch);
}
- }
- if (m_currentShader)
- setActiveShader(nullptr, nullptr);
- updateStencilClip(nullptr);
- glBindBuffer(GL_ARRAY_BUFFER, 0);
- glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
- glDepthMask(true);
+ for (int i = 0, ie = alphaRenderBatches.count(); i != ie; ++i) {
+ PreparedRenderBatch *renderBatch = &alphaRenderBatches[i];
+ if (renderBatch->batch->merged)
+ renderMergedBatch(renderBatch);
+ else if (renderBatch->batch->isRenderNode)
+ Q_UNREACHABLE(); // ###
+ else
+ renderUnmergedBatch(renderBatch);
+ }
+
+ if (m_currentShader)
+ setActiveRhiShader(nullptr, nullptr);
+
+ if (m_renderPassRecordingCallbacks.end)
+ m_renderPassRecordingCallbacks.end(m_renderPassRecordingCallbacks.userData);
+
+ cb->endPass();
+ }
}
void Renderer::deleteRemovedElements()
@@ -2588,8 +4152,6 @@ void Renderer::deleteRemovedElements()
void Renderer::render()
{
- Q_ASSERT(m_context->openglContext() == QOpenGLContext::currentContext());
-
if (Q_UNLIKELY(debug_dump())) {
qDebug("\n");
QSGNodeDumper::dump(rootNode());
@@ -2622,8 +4184,13 @@ void Renderer::render()
timer.start();
}
- if (m_vao)
- m_vao->bind();
+ if (!m_rhi) {
+ Q_ASSERT(m_context->openglContext() == QOpenGLContext::currentContext());
+ if (m_vao)
+ m_vao->bind();
+ } else {
+ m_resourceUpdates = m_rhi->nextResourceUpdateBatch();
+ }
if (m_rebuild & (BuildRenderLists | BuildRenderListsForTaggedRoots)) {
bool complete = (m_rebuild & BuildRenderLists) != 0;
@@ -2747,11 +4314,18 @@ void Renderer::render()
m_renderOrderRebuildLower = -1;
m_renderOrderRebuildUpper = -1;
- if (m_visualizeMode != VisualizeNothing)
- visualize();
+ if (!m_rhi) {
+ if (m_visualizeMode != VisualizeNothing)
+ visualize();
- if (m_vao)
- m_vao->release();
+ if (m_vao)
+ m_vao->release();
+ } else {
+ if (m_resourceUpdates) {
+ m_resourceUpdates->release();
+ m_resourceUpdates = nullptr;
+ }
+ }
}
struct RenderNodeState : public QSGRenderNode::RenderState
@@ -2801,8 +4375,8 @@ void Renderer::renderRenderNode(Batch *batch)
RenderNodeState state;
state.m_projectionMatrix = &pm;
- state.m_scissorEnabled = m_currentClipType & ScissorClip;
- state.m_stencilEnabled = m_currentClipType & StencilClip;
+ state.m_scissorEnabled = m_currentClipType & ClipState::ScissorClip;
+ state.m_stencilEnabled = m_currentClipType & ClipState::StencilClip;
state.m_scissorRect = m_currentScissorRect;
state.m_stencilValue = m_currentStencilValue;
@@ -2863,7 +4437,7 @@ void Renderer::renderRenderNode(Batch *batch)
if (changes & (QSGRenderNode::StencilState | QSGRenderNode::ScissorState)) {
glDisable(GL_SCISSOR_TEST);
m_currentClip = nullptr;
- m_currentClipType = NoClip;
+ m_currentClipType = ClipState::NoClip;
}
if (changes & QSGRenderNode::DepthState)
@@ -2886,11 +4460,6 @@ void Renderer::renderRenderNode(Batch *batch)
glBindFramebuffer(GL_FRAMEBUFFER, prevFbo);
}
-void Renderer::releaseCachedResources()
-{
- m_shaderManager->invalidated();
-}
-
class VisualizeShader : public QOpenGLShaderProgram
{
public:
@@ -2941,7 +4510,7 @@ void Renderer::visualizeBatch(Batch *b)
if (b->merged) {
shader->setUniformValue(shader->matrix, matrix);
- const auto &dataStart = m_context->separateIndexBuffer() ? b->ibo.data : b->vbo.data;
+ 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));
@@ -3198,6 +4767,61 @@ void Renderer::visualize()
shader->release();
}
+bool operator==(const GraphicsState &a, const GraphicsState &b) Q_DECL_NOTHROW
+{
+ return a.depthTest == b.depthTest
+ && a.depthWrite == b.depthWrite
+ && a.depthFunc == b.depthFunc
+ && a.blending == b.blending
+ && a.srcColor == b.srcColor
+ && a.dstColor == b.dstColor
+ && a.colorWrite == b.colorWrite
+ && a.cullMode == b.cullMode
+ && a.usesScissor == b.usesScissor
+ && a.stencilTest == b.stencilTest
+ && a.sampleCount == b.sampleCount
+ && a.drawMode == b.drawMode
+ && a.lineWidth == b.lineWidth;
+}
+
+bool operator!=(const GraphicsState &a, const GraphicsState &b) Q_DECL_NOTHROW
+{
+ return !(a == b);
+}
+
+uint qHash(const GraphicsState &s, uint seed) Q_DECL_NOTHROW
+{
+ // do not bother with all fields
+ return seed
+ + s.depthTest * 1000
+ + s.depthWrite * 100
+ + s.depthFunc
+ + s.blending * 10
+ + s.srcColor
+ + s.cullMode
+ + s.usesScissor
+ + s.stencilTest
+ + s.sampleCount;
+}
+
+bool operator==(const GraphicsPipelineStateKey &a, const GraphicsPipelineStateKey &b) Q_DECL_NOTHROW
+{
+ return a.state == b.state
+ && a.sms->programRhi.program == b.sms->programRhi.program
+ && a.rpDesc == b.rpDesc
+ && a.layoutCompatibleSrb->isLayoutCompatible(b.layoutCompatibleSrb);
+}
+
+bool operator!=(const GraphicsPipelineStateKey &a, const GraphicsPipelineStateKey &b) Q_DECL_NOTHROW
+{
+ return !(a == b);
+}
+
+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
}
diff --git a/src/quick/scenegraph/coreapi/qsgbatchrenderer_p.h b/src/quick/scenegraph/coreapi/qsgbatchrenderer_p.h
index 12b48c1451..9dec203e73 100644
--- a/src/quick/scenegraph/coreapi/qsgbatchrenderer_p.h
+++ b/src/quick/scenegraph/coreapi/qsgbatchrenderer_p.h
@@ -58,11 +58,14 @@
#include <private/qsgnodeupdater_p.h>
#include <private/qsgrendernode_p.h>
#include <private/qdatabuffer_p.h>
+#include <private/qsgtexture_p.h>
#include <QtCore/QBitArray>
-
+#include <QtCore/QStack>
#include <QtGui/QOpenGLFunctions>
+#include <QtGui/private/qrhi_p.h>
+
QT_BEGIN_NAMESPACE
class QOpenGLVertexArrayObject;
@@ -300,10 +303,11 @@ struct Buffer {
// Data is only valid while preparing the upload. Exception is if we are using the
// broken IBO workaround or we are using a visualization mode.
char *data;
+ QRhiBuffer *buf;
+ uint nonDynamicChangeCount;
};
struct Element {
-
Element()
: boundsComputed(false)
, boundsOutsideFloatRange(false)
@@ -334,6 +338,8 @@ struct Element {
Rect bounds; // in device coordinates
int order = 0;
+ QRhiShaderResourceBindings *srb = nullptr;
+ QRhiGraphicsPipeline *ps = nullptr;
uint boundsComputed : 1;
uint boundsOutsideFloatRange : 1;
@@ -390,6 +396,48 @@ enum BatchCompatibility
BatchIsCompatible
};
+struct ClipState
+{
+ enum ClipTypeBit
+ {
+ NoClip = 0x00,
+ ScissorClip = 0x01,
+ StencilClip = 0x02
+ };
+ Q_DECLARE_FLAGS(ClipType, ClipTypeBit)
+
+ const QSGClipNode *clipList;
+ ClipType type;
+ QRhiScissor scissor;
+ int stencilRef;
+
+ inline void reset();
+};
+
+struct StencilClipState
+{
+ StencilClipState() : drawCalls(1) { }
+
+ bool updateStencilBuffer = false;
+ QRhiShaderResourceBindings *srb = nullptr;
+ QRhiBuffer *vbuf = nullptr;
+ QRhiBuffer *ibuf = nullptr;
+ QRhiBuffer *ubuf = nullptr;
+
+ struct StencilDrawCall {
+ int stencilRef;
+ int vertexCount;
+ int indexCount;
+ QRhiCommandBuffer::IndexFormat indexFormat;
+ quint32 vbufOffset;
+ quint32 ibufOffset;
+ quint32 ubufOffset;
+ };
+ QDataBuffer<StencilDrawCall> drawCalls;
+
+ inline void reset();
+};
+
struct Batch
{
Batch() : drawSets(1) {}
@@ -403,6 +451,7 @@ struct Batch
// pseudo-constructor...
void init() {
+ // Only non-reusable members are reset here. See Renderer::newBatch().
first = nullptr;
root = nullptr;
vertexCount = 0;
@@ -413,6 +462,9 @@ struct Batch
positionAttribute = -1;
uploadedThisFrame = false;
isRenderNode = false;
+ ubufDataValid = false;
+ clipState.reset();
+ blendConstant = QColor();
}
Element *first;
@@ -429,16 +481,21 @@ struct Batch
uint needsUpload : 1;
uint merged : 1;
uint isRenderNode : 1;
+ uint ubufDataValid : 1;
mutable uint uploadedThisFrame : 1; // solely for debugging purposes
Buffer vbo;
Buffer ibo;
+ QRhiBuffer *ubuf;
+ ClipState clipState;
+ StencilClipState stencilClipState;
+ QColor blendConstant;
QDataBuffer<DrawSet> drawSets;
};
-// NOTE: Node is zero-allocated by the Allocator.
+// NOTE: Node is zero-initialized by the Allocator.
struct Node
{
QSGNode *sgNode;
@@ -574,10 +631,19 @@ class ShaderManager : public QObject
Q_OBJECT
public:
struct Shader {
- ~Shader() { delete program; }
- int id_zRange;
- int pos_order;
- QSGMaterialShader *program;
+ ~Shader() {
+ delete programRhi.program;
+ delete programGL.program;
+ }
+ struct {
+ QSGMaterialShader *program = nullptr;
+ int pos_order;
+ } programGL;
+ struct {
+ QSGMaterialRhiShader *program = nullptr;
+ QRhiVertexInputLayout inputLayout;
+ QVector<QRhiGraphicsShaderStage> shaderStages;
+ } programRhi;
float lastOpacity;
};
@@ -588,12 +654,16 @@ public:
qDeleteAll(stockShaders);
}
+ void clearCachedRendererData();
+
+ QRhiShaderResourceBindings *srb(const QVector<QRhiShaderResourceBinding> &bindings);
+
public Q_SLOTS:
void invalidated();
public:
- Shader *prepareMaterial(QSGMaterial *material);
- Shader *prepareMaterialNoRewrite(QSGMaterial *material);
+ Shader *prepareMaterial(QSGMaterial *material, bool enableRhiShaders = false, const QSGGeometry *geometry = nullptr);
+ Shader *prepareMaterialNoRewrite(QSGMaterial *material, bool enableRhiShaders = false, const QSGGeometry *geometry = nullptr);
QOpenGLShaderProgram *visualizeProgram;
@@ -603,6 +673,50 @@ private:
QOpenGLShaderProgram *blitProgram;
QSGDefaultRenderContext *context;
+
+ QHash<QVector<QRhiShaderResourceBinding>, QRhiShaderResourceBindings *> srbCache;
+};
+
+struct GraphicsState
+{
+ bool depthTest = false;
+ bool depthWrite = false;
+ QRhiGraphicsPipeline::CompareOp depthFunc = QRhiGraphicsPipeline::Less;
+ bool blending = false;
+ QRhiGraphicsPipeline::BlendFactor srcColor = QRhiGraphicsPipeline::One;
+ QRhiGraphicsPipeline::BlendFactor dstColor = QRhiGraphicsPipeline::OneMinusSrcAlpha;
+ QRhiGraphicsPipeline::ColorMask colorWrite = QRhiGraphicsPipeline::ColorMask(0xF);
+ QRhiGraphicsPipeline::CullMode cullMode = QRhiGraphicsPipeline::None;
+ bool usesScissor = false;
+ bool stencilTest = false;
+ int sampleCount = 1;
+ QSGGeometry::DrawingMode drawMode = QSGGeometry::DrawTriangles;
+ float lineWidth = 1.0f;
+};
+
+bool operator==(const GraphicsState &a, const GraphicsState &b) Q_DECL_NOTHROW;
+bool operator!=(const GraphicsState &a, const GraphicsState &b) Q_DECL_NOTHROW;
+uint qHash(const GraphicsState &s, uint seed = 0) Q_DECL_NOTHROW;
+
+struct GraphicsPipelineStateKey
+{
+ GraphicsState state;
+ const ShaderManager::Shader *sms;
+ const QRhiRenderPassDescriptor *rpDesc;
+ const QRhiShaderResourceBindings *layoutCompatibleSrb;
+};
+
+bool operator==(const GraphicsPipelineStateKey &a, const GraphicsPipelineStateKey &b) Q_DECL_NOTHROW;
+bool operator!=(const GraphicsPipelineStateKey &a, const GraphicsPipelineStateKey &b) Q_DECL_NOTHROW;
+uint qHash(const GraphicsPipelineStateKey &k, uint seed = 0) Q_DECL_NOTHROW;
+
+struct RenderPassState
+{
+ QRhiViewport viewport;
+ QColor clearColor;
+ QRhiDepthStencilClearValue dsClear;
+ bool viewportSet;
+ bool scissorSet;
};
class Q_QUICK_PRIVATE_EXPORT Renderer : public QSGRenderer, public QOpenGLFunctions
@@ -625,14 +739,6 @@ protected:
void releaseCachedResources() override;
private:
- enum ClipTypeBit
- {
- NoClip = 0x00,
- ScissorClip = 0x01,
- StencilClip = 0x02
- };
- Q_DECLARE_FLAGS(ClipType, ClipTypeBit)
-
enum RebuildFlag {
BuildRenderListsForTaggedRoots = 0x0001,
BuildRenderLists = 0x0002,
@@ -642,6 +748,7 @@ private:
friend class Updater;
+ void destroyGraphicsResources();
void map(Buffer *buffer, int size, bool isIndexBuf = false);
void unmap(Buffer *buffer, bool isIndexBuf = false);
@@ -658,16 +765,39 @@ private:
void invalidateBatchAndOverlappingRenderOrders(Batch *batch);
void uploadBatch(Batch *b);
- void uploadMergedElement(Element *e, int vaOffset, char **vertexData, char **zData, char **indexData, quint16 *iBase, int *indexCount);
+ void uploadMergedElement(Element *e, int vaOffset, char **vertexData, char **zData, char **indexData, void *iBasePtr, int *indexCount);
+
+ struct PreparedRenderBatch {
+ const Batch *batch;
+ ShaderManager::Shader *sms;
+ };
void renderBatches();
- void renderMergedBatch(const Batch *batch);
- void renderUnmergedBatch(const Batch *batch);
- ClipType updateStencilClip(const QSGClipNode *clip);
+ bool ensurePipelineState(Element *e, const ShaderManager::Shader *sms);
+ QRhiTexture *dummyTexture();
+ void updateMaterialDynamicData(ShaderManager::Shader *sms, const QSGMaterialRhiShader::RenderState &renderState,
+ QSGMaterial *material, QVector<QRhiShaderResourceBinding> *bindings,
+ const Batch *batch, int ubufOffset, int ubufRegionSize);
+ void updateMaterialStaticData(ShaderManager::Shader *sms, const QSGMaterialRhiShader::RenderState &renderState,
+ QSGMaterial *material, Batch *batch, bool *gstateChanged);
+ void checkLineWidth(QSGGeometry *g);
+ bool prepareRenderMergedBatch(Batch *batch, PreparedRenderBatch *renderBatch);
+ void renderMergedBatch(PreparedRenderBatch *renderBatch);
+ bool prepareRenderUnmergedBatch(Batch *batch, PreparedRenderBatch *renderBatch);
+ void renderUnmergedBatch(PreparedRenderBatch *renderBatch);
+ void setGraphicsPipeline(QRhiCommandBuffer *cb, const Batch *batch, Element *e);
+ void renderMergedBatch(const Batch *batch); // GL
+ void renderUnmergedBatch(const Batch *batch); // GL
+ ClipState::ClipType updateStencilClip(const QSGClipNode *clip);
void updateClip(const QSGClipNode *clipList, const Batch *batch);
+ void applyClipStateToGraphicsState();
+ QRhiGraphicsPipeline *buildStencilPipeline(const Batch *batch, bool firstStencilClipInBatch);
+ void updateClipState(const QSGClipNode *clipList, Batch *batch);
+ void enqueueStencilDraw(const Batch *batch);
const QMatrix4x4 &matrixForRoot(Node *node);
void renderRenderNode(Batch *batch);
void setActiveShader(QSGMaterialShader *program, ShaderManager::Shader *shader);
+ void setActiveRhiShader(QSGMaterialRhiShader *program, ShaderManager::Shader *shader);
bool changeBatchRoot(Node *node, Node *newRoot);
void registerBatchRoot(Node *childRoot, Node *parentRoot);
@@ -723,17 +853,21 @@ private:
int m_batchVertexThreshold;
// Stuff used during rendering only...
- ShaderManager *m_shaderManager;
+ ShaderManager *m_shaderManager; // per rendercontext, shared
QSGMaterial *m_currentMaterial;
QSGMaterialShader *m_currentProgram;
+ QSGMaterialRhiShader *m_currentRhiProgram;
ShaderManager::Shader *m_currentShader;
+ ClipState m_currentClipState;
+ // *** legacy (GL) only
QRect m_currentScissorRect;
int m_currentStencilValue;
QOpenGLShaderProgram m_clipProgram;
int m_clipMatrixId;
const QSGClipNode *m_currentClip;
- ClipType m_currentClipType;
+ ClipState::ClipType m_currentClipType;
+ // ***
QDataBuffer<char> m_vertexUploadPool;
QDataBuffer<char> m_indexUploadPool;
@@ -745,6 +879,28 @@ private:
Allocator<Node, 256> m_nodeAllocator;
Allocator<Element, 64> m_elementAllocator;
+
+ QRhiResourceUpdateBatch *m_resourceUpdates = nullptr;
+ uint m_ubufAlignment;
+ bool m_uint32IndexForRhi;
+ GraphicsState m_gstate;
+ RenderPassState m_pstate;
+ QStack<GraphicsState> m_gstateStack;
+ QHash<GraphicsPipelineStateKey, QRhiGraphicsPipeline *> m_pipelines;
+ QHash<QSGSamplerDescription, QRhiSampler *> m_samplers;
+ QRhiTexture *m_dummyTexture = nullptr;
+
+ struct StencilClipCommonData {
+ QRhiGraphicsPipeline *replacePs = nullptr;
+ QRhiGraphicsPipeline *incrPs = nullptr;
+ QShader vs;
+ QShader fs;
+ QRhiVertexInputLayout inputLayout;
+ QRhiGraphicsPipeline::Topology topology;
+ inline void reset();
+ } m_stencilClipCommon;
+
+ inline int mergedIndexElemSize() const;
};
Batch *Renderer::newBatch()
@@ -753,18 +909,69 @@ Batch *Renderer::newBatch()
int size = m_batchPool.size();
if (size) {
b = m_batchPool.at(size - 1);
+ // vbo, ibo, ubuf, stencil-related buffers are reused
m_batchPool.resize(size - 1);
} else {
b = new Batch();
Q_ASSERT(offsetof(Batch, ibo) == sizeof(Buffer) + offsetof(Batch, vbo));
memset(&b->vbo, 0, sizeof(Buffer) * 2); // Clear VBO & IBO
+ b->ubuf = nullptr;
+ b->stencilClipState.reset();
}
+ // initialize (when new batch) or reset (when reusing a batch) the non-reusable fields
b->init();
return b;
}
+int Renderer::mergedIndexElemSize() const
+{
+ return m_uint32IndexForRhi ? sizeof(quint32) : sizeof(quint16);
}
+void Renderer::StencilClipCommonData::reset()
+{
+ delete replacePs;
+ replacePs = nullptr;
+
+ delete incrPs;
+ incrPs = nullptr;
+
+ vs = QShader();
+ fs = QShader();
+}
+
+void ClipState::reset()
+{
+ clipList = nullptr;
+ type = NoClip;
+ stencilRef = 0;
+}
+
+void StencilClipState::reset()
+{
+ updateStencilBuffer = false;
+
+ delete srb;
+ srb = nullptr;
+
+ delete vbuf;
+ vbuf = nullptr;
+
+ delete ibuf;
+ ibuf = nullptr;
+
+ delete ubuf;
+ ubuf = nullptr;
+
+ drawCalls.reset();
+}
+
+}
+
+Q_DECLARE_TYPEINFO(QSGBatchRenderer::GraphicsState, Q_MOVABLE_TYPE);
+Q_DECLARE_TYPEINFO(QSGBatchRenderer::GraphicsPipelineStateKey, Q_MOVABLE_TYPE);
+Q_DECLARE_TYPEINFO(QSGBatchRenderer::RenderPassState, Q_MOVABLE_TYPE);
+
QT_END_NAMESPACE
#endif // QSGBATCHRENDERER_P_H
diff --git a/src/quick/scenegraph/coreapi/qsggeometry.cpp b/src/quick/scenegraph/coreapi/qsggeometry.cpp
index dd701fba5f..5218b6b1d5 100644
--- a/src/quick/scenegraph/coreapi/qsggeometry.cpp
+++ b/src/quick/scenegraph/coreapi/qsggeometry.cpp
@@ -443,6 +443,7 @@ QSGGeometry::QSGGeometry(const QSGGeometry::AttributeSet &attributes,
Q_ASSERT(m_attributes.stride > 0);
#if QT_CONFIG(opengl)
Q_ASSERT_X(indexType != GL_UNSIGNED_INT
+ || !QOpenGLContext::currentContext() // rhi, support for uint cannot be checked here
|| static_cast<QOpenGLExtensions *>(QOpenGLContext::currentContext()->functions())
->hasOpenGLExtension(QOpenGLExtensions::ElementIndexUint),
"QSGGeometry::QSGGeometry",
diff --git a/src/quick/scenegraph/coreapi/qsggeometry.h b/src/quick/scenegraph/coreapi/qsggeometry.h
index 7a916610e3..d17915a842 100644
--- a/src/quick/scenegraph/coreapi/qsggeometry.h
+++ b/src/quick/scenegraph/coreapi/qsggeometry.h
@@ -88,7 +88,11 @@ public:
UnsignedShortType = 0x1403,
IntType = 0x1404,
UnsignedIntType = 0x1405,
- FloatType = 0x1406
+ FloatType = 0x1406,
+ Bytes2Type = 0x1407,
+ Bytes3Type = 0x1408,
+ Bytes4Type = 0x1409,
+ DoubleType = 0x140A
};
struct Q_QUICK_EXPORT Attribute
diff --git a/src/quick/scenegraph/coreapi/qsgmaterial.cpp b/src/quick/scenegraph/coreapi/qsgmaterial.cpp
index 8557de1b1f..c8ae26311b 100644
--- a/src/quick/scenegraph/coreapi/qsgmaterial.cpp
+++ b/src/quick/scenegraph/coreapi/qsgmaterial.cpp
@@ -39,14 +39,6 @@
#include "qsgmaterial.h"
#include "qsgrenderer_p.h"
-#include "qsgmaterialshader_p.h"
-#if QT_CONFIG(opengl)
-# include <private/qsgshadersourcebuilder_p.h>
-# include <private/qsgdefaultcontext_p.h>
-# include <private/qsgdefaultrendercontext_p.h>
-# include <QtGui/QOpenGLFunctions>
-# include <QtGui/QOpenGLContext>
-#endif
QT_BEGIN_NAMESPACE
@@ -64,17 +56,6 @@ void qsg_set_material_failure()
qsg_material_failure = true;
}
#endif
-#if QT_CONFIG(opengl)
-const char *QSGMaterialShaderPrivate::loadShaderSource(QOpenGLShader::ShaderType type) const
-{
- const QStringList files = m_sourceFiles[type];
- QSGShaderSourceBuilder builder;
- for (const QString &file : files)
- builder.appendSourceFile(file);
- m_sources[type] = builder.source();
- return m_sources[type].constData();
-}
-#endif
#ifndef QT_NO_DEBUG
static const bool qsg_leak_check = !qEnvironmentVariableIsEmpty("QML_LEAK_CHECK");
@@ -89,494 +70,6 @@ static const bool qsg_leak_check = !qEnvironmentVariableIsEmpty("QML_LEAK_CHECK"
\l {scene graph}{Qt Quick Scene Graph}.
*/
-/*!
- \class QSGMaterialShader
- \brief The QSGMaterialShader class represents an OpenGL shader program
- in the renderer.
- \inmodule QtQuick
- \ingroup qtquick-scenegraph-materials
-
- The QSGMaterialShader API is very low-level. A more convenient API, which
- provides almost all the same features, is available through
- QSGSimpleMaterialShader.
-
- The QSGMaterial and QSGMaterialShader form a tight relationship. For one
- scene graph (including nested graphs), there is one unique QSGMaterialShader
- instance which encapsulates the QOpenGLShaderProgram the scene graph uses
- to render that material, such as a shader to flat coloring of geometry.
- Each QSGGeometryNode can have a unique QSGMaterial containing the
- how the shader should be configured when drawing that node, such as
- the actual color used to render the geometry.
-
- An instance of QSGMaterialShader is never created explicitly by the user,
- it will be created on demand by the scene graph through
- QSGMaterial::createShader(). The scene graph will make sure that there
- is only one instance of each shader implementation through a scene graph.
-
- The source code returned from vertexShader() is used to control what the
- material does with the vertiex data that comes in from the geometry.
- The source code returned from the fragmentShader() is used to control
- what how the material should fill each individual pixel in the geometry.
- The vertex and fragment source code is queried once during initialization,
- changing what is returned from these functions later will not have
- any effect.
-
- The activate() function is called by the scene graph when a shader is
- is starting to be used. The deactivate function is called by the scene
- graph when the shader is no longer going to be used. While active,
- the scene graph may make one or more calls to updateState() which
- will update the state of the shader for each individual geometry to
- render.
-
- The attributeNames() returns the name of the attributes used in the
- vertexShader(). These are used in the default implementation of
- activate() and deactivate() to decide whice vertex registers are enabled.
-
- The initialize() function is called during program creation to allow
- subclasses to prepare for use, such as resolve uniform names in the
- vertexShader() and fragmentShader().
-
- A minimal example:
- \code
- class Shader : public QSGMaterialShader
- {
- public:
- const char *vertexShader() const {
- return
- "attribute highp vec4 vertex; \n"
- "uniform highp mat4 matrix; \n"
- "void main() { \n"
- " gl_Position = matrix * vertex; \n"
- "}";
- }
-
- const char *fragmentShader() const {
- return
- "uniform lowp float opacity; \n"
- "void main() { \n"
- " gl_FragColor = vec4(1, 0, 0, 1) * opacity; \n"
- "}";
- }
-
- char const *const *attributeNames() const
- {
- static char const *const names[] = { "vertex", 0 };
- return names;
- }
-
- void initialize()
- {
- QSGMaterialShader::initialize();
- m_id_matrix = program()->uniformLocation("matrix");
- m_id_opacity = program()->uniformLocation("opacity");
- }
-
- void updateState(const RenderState &state, QSGMaterial *newMaterial, QSGMaterial *oldMaterial)
- {
- Q_ASSERT(program()->isLinked());
- if (state.isMatrixDirty())
- program()->setUniformValue(m_id_matrix, state.combinedMatrix());
- if (state.isOpacityDirty())
- program()->setUniformValue(m_id_opacity, state.opacity());
- }
-
- private:
- int m_id_matrix;
- int m_id_opacity;
- };
- \endcode
-
- \note All classes with QSG prefix should be used solely on the scene graph's
- rendering thread. See \l {Scene Graph and Rendering} for more information.
-
- */
-
-
-
-/*!
- Creates a new QSGMaterialShader.
- */
-QSGMaterialShader::QSGMaterialShader()
- : d_ptr(new QSGMaterialShaderPrivate)
-{
-}
-
-/*!
- \internal
- */
-QSGMaterialShader::QSGMaterialShader(QSGMaterialShaderPrivate &dd)
- : d_ptr(&dd)
-{
-}
-
-/*!
- \internal
- */
-QSGMaterialShader::~QSGMaterialShader()
-{
-}
-
-/*!
- \fn char const *const *QSGMaterialShader::attributeNames() const
-
- Returns a zero-terminated array describing the names of the
- attributes used in the vertex shader.
-
- This function is called when the shader is compiled to specify
- which attributes exist. The order of the attribute names
- defines the attribute register position in the vertex shader.
- */
-
-#if QT_CONFIG(opengl)
-/*!
- \fn const char *QSGMaterialShader::vertexShader() const
-
- Called when the shader is being initialized to get the vertex
- shader source code.
-
- The contents returned from this function should never change.
-*/
-const char *QSGMaterialShader::vertexShader() const
-{
- Q_D(const QSGMaterialShader);
- return d->loadShaderSource(QOpenGLShader::Vertex);
-}
-
-
-/*!
- \fn const char *QSGMaterialShader::fragmentShader() const
-
- Called when the shader is being initialized to get the fragment
- shader source code.
-
- The contents returned from this function should never change.
-*/
-const char *QSGMaterialShader::fragmentShader() const
-{
- Q_D(const QSGMaterialShader);
- return d->loadShaderSource(QOpenGLShader::Fragment);
-}
-
-
-/*!
- \fn QOpenGLShaderProgram *QSGMaterialShader::program()
-
- Returns the shader program used by this QSGMaterialShader.
- */
-#endif
-
-/*!
- \fn void QSGMaterialShader::initialize()
-
- Reimplement this function to do one-time initialization when the
- shader program is compiled. The OpenGL shader program is compiled
- and linked, but not bound, when this function is called.
- */
-
-
-/*!
- This function is called by the scene graph to indicate that geometry is
- about to be rendered using this shader.
-
- State that is global for all uses of the shader, independent of the geometry
- that is being drawn, can be setup in this function.
- */
-
-void QSGMaterialShader::activate()
-{
-}
-
-
-
-/*!
- This function is called by the scene graph to indicate that geometry will
- no longer to be rendered using this shader.
- */
-
-void QSGMaterialShader::deactivate()
-{
-}
-
-
-
-/*!
- This function is called by the scene graph before geometry is rendered
- to make sure the shader is in the right state.
-
- The current rendering \a state is passed from the scene graph. If the state
- indicates that any state is dirty, the updateState implementation must
- update accordingly for the geometry to render correctly.
-
- The subclass specific state, such as the color of a flat color material, should
- be extracted from \a newMaterial to update the color uniforms accordingly.
-
- The \a oldMaterial can be used to minimze state changes when updating
- material states. The \a oldMaterial is 0 if this shader was just activated.
-
- \sa activate(), deactivate()
- */
-
-void QSGMaterialShader::updateState(const RenderState & /* state */, QSGMaterial * /* newMaterial */, QSGMaterial * /* oldMaterial */)
-{
-}
-
-#if QT_CONFIG(opengl)
-/*!
- Sets the GLSL source file for the shader stage \a type to \a sourceFile. The
- default implementation of the vertexShader() and fragmentShader() functions
- will load the source files set by this function.
-
- This function is useful when you have a single source file for a given shader
- stage. If your shader consists of multiple source files then use
- setShaderSourceFiles()
-
- \sa setShaderSourceFiles(), vertexShader(), fragmentShader()
- */
-void QSGMaterialShader::setShaderSourceFile(QOpenGLShader::ShaderType type, const QString &sourceFile)
-{
- Q_D(QSGMaterialShader);
- d->m_sourceFiles[type] = (QStringList() << sourceFile);
-}
-
-/*!
- Sets the GLSL source files for the shader stage \a type to \a sourceFiles. The
- default implementation of the vertexShader() and fragmentShader() functions
- will load the source files set by this function in the order given.
-
- \sa setShaderSourceFile(), vertexShader(), fragmentShader()
- */
-void QSGMaterialShader::setShaderSourceFiles(QOpenGLShader::ShaderType type, const QStringList &sourceFiles)
-{
- Q_D(QSGMaterialShader);
- d->m_sourceFiles[type] = sourceFiles;
-}
-
-/*!
- This function is called when the shader is initialized to compile the
- actual QOpenGLShaderProgram. Do not call it explicitly.
-
- The default implementation will extract the vertexShader() and
- fragmentShader() and bind the names returned from attributeNames()
- to consecutive vertex attribute registers starting at 0.
- */
-
-void QSGMaterialShader::compile()
-{
- Q_ASSERT_X(!m_program.isLinked(), "QSGSMaterialShader::compile()", "Compile called multiple times!");
-
- program()->addCacheableShaderFromSourceCode(QOpenGLShader::Vertex, vertexShader());
- program()->addCacheableShaderFromSourceCode(QOpenGLShader::Fragment, fragmentShader());
-
- char const *const *attr = attributeNames();
-#ifndef QT_NO_DEBUG
- int maxVertexAttribs = 0;
- QOpenGLFunctions *funcs = QOpenGLContext::currentContext()->functions();
- funcs->glGetIntegerv(GL_MAX_VERTEX_ATTRIBS, &maxVertexAttribs);
- for (int i = 0; attr[i]; ++i) {
- if (i >= maxVertexAttribs) {
- qFatal("List of attribute names is either too long or not null-terminated.\n"
- "Maximum number of attributes on this hardware is %i.\n"
- "Vertex shader:\n%s\n"
- "Fragment shader:\n%s\n",
- maxVertexAttribs, vertexShader(), fragmentShader());
- }
- if (*attr[i])
- program()->bindAttributeLocation(attr[i], i);
- }
-#else
- for (int i = 0; attr[i]; ++i) {
- if (*attr[i])
- program()->bindAttributeLocation(attr[i], i);
- }
-#endif
-
- if (!program()->link()) {
- qWarning("QSGMaterialShader: Shader compilation failed:");
- qWarning() << program()->log();
- }
-}
-
-#endif
-
-/*!
- \class QSGMaterialShader::RenderState
- \brief The QSGMaterialShader::RenderState encapsulates the current rendering state
- during a call to QSGMaterialShader::updateState().
- \inmodule QtQuick
-
- The render state contains a number of accessors that the shader needs to respect
- in order to conform to the current state of the scene graph.
-
- The instance is only valid inside a call to QSGMaterialShader::updateState() and
- should not be used outisde this function.
- */
-
-
-
-/*!
- \enum QSGMaterialShader::RenderState::DirtyState
-
- \value DirtyMatrix Used to indicate that the matrix has changed and must be updated.
-
- \value DirtyOpacity Used to indicate that the opacity has changed and must be updated.
-
- \value DirtyCachedMaterialData Used to indicate that the cached material data have changed and must be updated.
-
- \value DirtyAll Used to indicate that everything needs to be updated.
- */
-
-
-
-/*!
- \fn bool QSGMaterialShader::RenderState::isMatrixDirty() const
-
- Returns \c true if the dirtyStates() contain the dirty matrix state,
- otherwise returns \c false.
- */
-
-
-
-/*!
- \fn bool QSGMaterialShader::RenderState::isOpacityDirty() const
-
- Returns \c true if the dirtyStates() contains the dirty opacity state,
- otherwise returns \c false.
- */
-
-/*!
- \fn bool QSGMaterialShader::RenderState::isCachedMaterialDataDirty() const
-
- Returns \c true if the dirtyStates() contains the dirty cached material state,
- otherwise returns \c false.
- */
-
-/*!
- \fn QSGMaterialShader::RenderState::DirtyStates QSGMaterialShader::RenderState::dirtyStates() const
-
- Returns which rendering states that have changed and needs to be updated
- for geometry rendered with this material to conform to the current
- rendering state.
- */
-
-
-
-/*!
- Returns the accumulated opacity to be used for rendering.
- */
-
-float QSGMaterialShader::RenderState::opacity() const
-{
- Q_ASSERT(m_data);
- return static_cast<const QSGRenderer *>(m_data)->currentOpacity();
-}
-
-/*!
- Returns the modelview determinant to be used for rendering.
- */
-
-float QSGMaterialShader::RenderState::determinant() const
-{
- Q_ASSERT(m_data);
- return static_cast<const QSGRenderer *>(m_data)->determinant();
-}
-
-/*!
- Returns the matrix combined of modelview matrix and project matrix.
- */
-
-QMatrix4x4 QSGMaterialShader::RenderState::combinedMatrix() const
-{
- Q_ASSERT(m_data);
- return static_cast<const QSGRenderer *>(m_data)->currentCombinedMatrix();
-}
-/*!
- Returns the ratio between physical pixels and device-independent pixels
- to be used for rendering.
-*/
-float QSGMaterialShader::RenderState::devicePixelRatio() const
-{
- Q_ASSERT(m_data);
- return static_cast<const QSGRenderer *>(m_data)->devicePixelRatio();
-}
-
-
-
-/*!
- Returns the model view matrix.
-
- If the material has the RequiresFullMatrix flag
- set, this is guaranteed to be the complete transform
- matrix calculated from the scenegraph.
-
- However, if this flag is not set, the renderer may
- choose to alter this matrix. For example, it may
- pre-transform vertices on the CPU and set this matrix
- to identity.
-
- In a situation such as the above, it is still possible
- to retrieve the actual matrix determinant by setting
- the RequiresDeterminant flag in the material and
- calling the determinant() accessor.
- */
-
-QMatrix4x4 QSGMaterialShader::RenderState::modelViewMatrix() const
-{
- Q_ASSERT(m_data);
- return static_cast<const QSGRenderer *>(m_data)->currentModelViewMatrix();
-}
-
-/*!
- Returns the projection matrix.
- */
-
-QMatrix4x4 QSGMaterialShader::RenderState::projectionMatrix() const
-{
- Q_ASSERT(m_data);
- return static_cast<const QSGRenderer *>(m_data)->currentProjectionMatrix();
-}
-
-
-
-/*!
- Returns the viewport rect of the surface being rendered to.
- */
-
-QRect QSGMaterialShader::RenderState::viewportRect() const
-{
- Q_ASSERT(m_data);
- return static_cast<const QSGRenderer *>(m_data)->viewportRect();
-}
-
-
-
-/*!
- Returns the device rect of the surface being rendered to
- */
-
-QRect QSGMaterialShader::RenderState::deviceRect() const
-{
- Q_ASSERT(m_data);
- return static_cast<const QSGRenderer *>(m_data)->deviceRect();
-}
-
-#if QT_CONFIG(opengl)
-
-/*!
- Returns the QOpenGLContext that is being used for rendering
- */
-
-QOpenGLContext *QSGMaterialShader::RenderState::context() const
-{
- // Only the QSGDefaultRenderContext will have an OpenGL Context to query
- auto openGLRenderContext = static_cast<const QSGDefaultRenderContext *>(static_cast<const QSGRenderer *>(m_data)->context());
- if (openGLRenderContext != nullptr)
- return openGLRenderContext->openglContext();
- else
- return nullptr;
-}
-
-#endif
-
#ifndef QT_NO_DEBUG
static int qt_material_count = 0;
@@ -605,22 +98,20 @@ static void qt_print_material_count()
\inmodule QtQuick
\ingroup qtquick-scenegraph-materials
- The QSGMaterial API is very low-level. A more convenient API, which
- provides almost all the same features, is available through
- QSGSimpleMaterialShader.
-
- The QSGMaterial and QSGMaterialShader subclasses form a tight relationship. For
- one scene graph (including nested graphs), there is one unique QSGMaterialShader
- instance which encapsulates the QOpenGLShaderProgram the scene graph uses
- to render that material, such as a shader to flat coloring of geometry.
- Each QSGGeometryNode can have a unique QSGMaterial containing the
- how the shader should be configured when drawing that node, such as
- the actual color to used to render the geometry.
-
- The QSGMaterial has two virtual functions that both need to be implemented.
- The function type() should return a unique instance for all instances of a
+ The QSGMaterial, QSGMaterialShader and QSGMaterialRhiShader subclasses
+ form a tight relationship. For one scene graph (including nested graphs),
+ there is one unique QSGMaterialShader or QSGMaterialRhiShader instance
+ which encapsulates the shaders the scene graph uses to render that
+ material, such as a shader to flat coloring of geometry. Each
+ QSGGeometryNode can have a unique QSGMaterial containing the how the shader
+ should be configured when drawing that node, such as the actual color to
+ used to render the geometry.
+
+ QSGMaterial has two virtual functions that both need to be implemented. The
+ function type() should return a unique instance for all instances of a
specific subclass. The createShader() function should return a new instance
- of QSGMaterialShader, specific to the subclass of QSGMaterial.
+ of QSGMaterialShader or QSGMaterialRhiShader, specific to that subclass of
+ QSGMaterial.
A minimal QSGMaterial implementation could look like this:
\code
@@ -632,6 +123,27 @@ static void qt_print_material_count()
};
\endcode
+ This is suitable only for the OpenGL-based, traditional renderer of the
+ scene graph. When using the new, graphics API abstracted renderer,
+ materials must create QSGMaterialRhiShader instances instead, or in
+ addition:
+ \code
+ class Material : public QSGMaterial
+ {
+ public:
+ Material() { setFlag(SupportsRhiShader, true); }
+ QSGMaterialType *type() const { static QSGMaterialType type; return &type; }
+ QSGMaterialShader *createShader() {
+ if (flags().testFlag(RhiShaderWanted)) {
+ return new RhiShader;
+ } else {
+ // this is optional, relevant for materials that intend to be usable with the legacy OpenGL renderer as well
+ return new Shader;
+ }
+ }
+ };
+ \endcode
+
\note All classes with QSG prefix should be used solely on the scene graph's
rendering thread. See \l {Scene Graph and Rendering} for more information.
*/
@@ -693,6 +205,16 @@ QSGMaterial::~QSGMaterial()
QSGMaterialShader::compile() when its shader program is compiled and linked.
Set this flag to enforce that the function is called.
+ \value SupportsRhiShader Starting with Qt 5.14, the scene graph supports
+ QSGMaterialRhiShader as an alternative to the OpenGL-specific
+ QSGMaterialShader. Set this flag to indicate createShader() is capable of
+ returning QSGMaterialRhiShader instances when the RhiShaderWanted flag is
+ set.
+
+ \value RhiShaderWanted This flag is set by the scene graph, not by the
+ QSGMaterial. When set, and that can only happen when SupportsRhiShader was
+ set by the material, it indicates that createShader() must return a
+ QSGMaterialRhiShader instance instead of QSGMaterialShader.
*/
/*!
@@ -757,7 +279,11 @@ int QSGMaterial::compare(const QSGMaterial *other) const
The function will be called only once for each material type that
exists in the scene graph and will be cached internally.
-*/
+ When the QSGMaterial reports SupportsRhiShader in flags(), the scene graph
+ may request a QSGMaterialRhiShader instead of QSGMaterialShader. This is
+ indicated by having the RhiShaderWanted flag set. In this case the return
+ value must be a QSGRhiMaterialShader subclass.
+*/
QT_END_NAMESPACE
diff --git a/src/quick/scenegraph/coreapi/qsgmaterial.h b/src/quick/scenegraph/coreapi/qsgmaterial.h
index c002cd5d5e..cb6e9a456a 100644
--- a/src/quick/scenegraph/coreapi/qsgmaterial.h
+++ b/src/quick/scenegraph/coreapi/qsgmaterial.h
@@ -1,6 +1,6 @@
/****************************************************************************
**
-** Copyright (C) 2016 The Qt Company Ltd.
+** Copyright (C) 2019 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the QtQuick module of the Qt Toolkit.
@@ -41,95 +41,12 @@
#define QSGMATERIAL_H
#include <QtQuick/qtquickglobal.h>
-#if QT_CONFIG(opengl)
-# include <QtGui/qopenglshaderprogram.h>
-#endif
-#include <QtGui/QMatrix4x4>
-#include <QtCore/QRect>
+#include <QtQuick/qsgmaterialshader.h>
+#include <QtQuick/qsgmaterialrhishader.h>
+#include <QtQuick/qsgmaterialtype.h>
QT_BEGIN_NAMESPACE
-class QSGMaterial;
-class QSGMaterialShaderPrivate;
-
-namespace QSGBatchRenderer {
- class ShaderManager;
-}
-
-class Q_QUICK_EXPORT QSGMaterialShader
-{
-public:
- class Q_QUICK_EXPORT RenderState {
- public:
- enum DirtyState
- {
- DirtyMatrix = 0x0001,
- DirtyOpacity = 0x0002,
- DirtyCachedMaterialData = 0x0004,
- DirtyAll = 0xFFFF
- };
- Q_DECLARE_FLAGS(DirtyStates, DirtyState)
-
- inline DirtyStates dirtyStates() const { return m_dirty; }
-
- inline bool isMatrixDirty() const { return m_dirty & DirtyMatrix; }
- inline bool isOpacityDirty() const { return m_dirty & DirtyOpacity; }
- bool isCachedMaterialDataDirty() const { return m_dirty & DirtyCachedMaterialData; }
-
- float opacity() const;
- QMatrix4x4 combinedMatrix() const;
- QMatrix4x4 modelViewMatrix() const;
- QMatrix4x4 projectionMatrix() const;
- QRect viewportRect() const;
- QRect deviceRect() const;
- float determinant() const;
- float devicePixelRatio() const;
-#if QT_CONFIG(opengl)
- QOpenGLContext *context() const;
-#endif
- private:
- friend class QSGRenderer;
- DirtyStates m_dirty;
- const void *m_data;
- };
-
- QSGMaterialShader();
- virtual ~QSGMaterialShader();
-
- virtual void activate();
- virtual void deactivate();
- // First time a material is used, oldMaterial is null.
- virtual void updateState(const RenderState &state, QSGMaterial *newMaterial, QSGMaterial *oldMaterial);
- virtual char const *const *attributeNames() const = 0; // Array must end with null.
-#if QT_CONFIG(opengl)
- inline QOpenGLShaderProgram *program() { return &m_program; }
-#endif
-protected:
- Q_DECLARE_PRIVATE(QSGMaterialShader)
- QSGMaterialShader(QSGMaterialShaderPrivate &dd);
-
- friend class QSGDefaultRenderContext;
- friend class QSGBatchRenderer::ShaderManager;
-#if QT_CONFIG(opengl)
- void setShaderSourceFile(QOpenGLShader::ShaderType type, const QString &sourceFile);
- void setShaderSourceFiles(QOpenGLShader::ShaderType type, const QStringList &sourceFiles);
-
- virtual void compile();
-#endif
- virtual void initialize() { }
-#if QT_CONFIG(opengl)
- virtual const char *vertexShader() const;
- virtual const char *fragmentShader() const;
-#endif
-private:
-#if QT_CONFIG(opengl)
- QOpenGLShaderProgram m_program;
-#endif
- QScopedPointer<QSGMaterialShaderPrivate> d_ptr;
-};
-
-struct QSGMaterialType { };
-
class Q_QUICK_EXPORT QSGMaterial
{
public:
@@ -139,7 +56,11 @@ public:
RequiresFullMatrixExceptTranslate = 0x0004 | RequiresDeterminant, // Allow precalculated translation
RequiresFullMatrix = 0x0008 | RequiresFullMatrixExceptTranslate,
- CustomCompileStep = 0x0010
+ CustomCompileStep = 0x0010,
+
+ SupportsRhiShader = 0x0020,
+
+ RhiShaderWanted = 0x1000 // // ### Qt 6: remove
};
Q_DECLARE_FLAGS(Flags, Flag)
@@ -160,7 +81,6 @@ private:
};
Q_DECLARE_OPERATORS_FOR_FLAGS(QSGMaterial::Flags)
-Q_DECLARE_OPERATORS_FOR_FLAGS(QSGMaterialShader::RenderState::DirtyStates)
QT_END_NAMESPACE
diff --git a/src/quick/scenegraph/coreapi/qsgmaterialrhishader.cpp b/src/quick/scenegraph/coreapi/qsgmaterialrhishader.cpp
new file mode 100644
index 0000000000..03f74df6b7
--- /dev/null
+++ b/src/quick/scenegraph/coreapi/qsgmaterialrhishader.cpp
@@ -0,0 +1,565 @@
+/****************************************************************************
+**
+** Copyright (C) 2019 The Qt Company Ltd.
+** 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 "qsgmaterial.h"
+#include "qsgrenderer_p.h"
+#include "qsgmaterialrhishader_p.h"
+#include <QtCore/QFile>
+
+QT_BEGIN_NAMESPACE
+
+/*!
+ \class QSGMaterialRhiShader
+ \brief The QSGMaterialRhiShader class represents a graphics API independent shader program.
+ \inmodule QtQuick
+ \ingroup qtquick-scenegraph-materials
+ \since 5.14
+
+ QSGMaterialRhiShader is a modern, cross-platform alternative to
+ QSGMaterialShader. The latter is tied to OpenGL and GLSL by design, whereas
+ QSGMaterialRhiShader is based on QShader, a container for multiple
+ versions of a graphics shader together with reflection information.
+
+ \note All classes with QSG prefix should be used solely on the scene graph's
+ rendering thread. See \l {Scene Graph and Rendering} for more information.
+ */
+
+/*!
+ \enum QSGMaterialRhiShader::Flag
+ Flag values to indicate special material properties.
+
+ \value UpdatesGraphicsPipelineState Setting this flag enables calling
+ updateGraphicsPipelineState().
+ */
+
+QShader QSGMaterialRhiShaderPrivate::loadShader(const QString &filename)
+{
+ QFile f(filename);
+ if (!f.open(QIODevice::ReadOnly)) {
+ qWarning() << "Failed to find shader" << filename;
+ return QShader();
+ }
+ return QShader::fromSerialized(f.readAll());
+}
+
+void QSGMaterialRhiShaderPrivate::clearCachedRendererData()
+{
+ for (int i = 0; i < MAX_SHADER_RESOURCE_BINDINGS; ++i)
+ textureBindingTable[i] = nullptr;
+ for (int i = 0; i < MAX_SHADER_RESOURCE_BINDINGS; ++i)
+ samplerBindingTable[i] = nullptr;
+}
+
+static inline QRhiShaderResourceBinding::StageFlags toSrbStage(QShader::Stage stage)
+{
+ switch (stage) {
+ case QShader::VertexStage:
+ return QRhiShaderResourceBinding::VertexStage;
+ case QShader::FragmentStage:
+ return QRhiShaderResourceBinding::FragmentStage;
+ default:
+ Q_UNREACHABLE();
+ break;
+ }
+ return 0;
+}
+
+void QSGMaterialRhiShaderPrivate::prepare(QShader::Variant vertexShaderVariant)
+{
+ ubufBinding = -1;
+ ubufSize = 0;
+ ubufStages = 0;
+ memset(combinedImageSamplerBindings, 0, sizeof(combinedImageSamplerBindings));
+ vertexShader = fragmentShader = nullptr;
+ masterUniformData.clear();
+
+ clearCachedRendererData();
+
+ for (QShader::Stage stage : { QShader::VertexStage, QShader::FragmentStage }) {
+ auto it = shaderFileNames.find(stage);
+ if (it != shaderFileNames.end()) {
+ QString fn = *it;
+ const QShader s = loadShader(*it);
+ if (!s.isValid())
+ continue;
+ shaders[stage] = ShaderStageData(s);
+ // load only once, subsequent prepare() calls will have it all in shaders already
+ shaderFileNames.erase(it);
+ }
+ }
+
+ auto vsIt = shaders.find(QShader::VertexStage);
+ if (vsIt != shaders.end()) {
+ vsIt->shaderVariant = vertexShaderVariant;
+ vsIt->vertexInputLocations.clear();
+ vsIt->qt_order_attrib_location = -1;
+
+ const QShaderDescription desc = vsIt->shader.description();
+ const QVector<QShaderDescription::InOutVariable> vertexInputs = desc.inputVariables();
+ for (const QShaderDescription::InOutVariable &v : vertexInputs) {
+ const QByteArray name = v.name.toUtf8();
+ if (vertexShaderVariant == QShader::BatchableVertexShader
+ && name == QByteArrayLiteral("_qt_order"))
+ {
+ vsIt->qt_order_attrib_location = v.location;
+ } else {
+ vsIt->vertexInputLocations.append(v.location);
+ }
+ }
+
+ if (vsIt->vertexInputLocations.contains(vsIt->qt_order_attrib_location)) {
+ qWarning("Vertex input clash in rewritten (batchable) vertex shader at input location %d. "
+ "Vertex shaders must avoid using this location.", vsIt->qt_order_attrib_location);
+ }
+ }
+
+ for (auto it = shaders.begin(); it != shaders.end(); ++it) {
+ const QShaderDescription desc = it->shader.description();
+
+ const QVector<QShaderDescription::UniformBlock> ubufs = desc.uniformBlocks();
+ const int ubufCount = ubufs.count();
+ if (ubufCount > 1) {
+ qWarning("Multiple uniform blocks found in shader. "
+ "This should be avoided as Qt Quick supports only one.");
+ }
+ for (int i = 0; i < ubufCount; ++i) {
+ const QShaderDescription::UniformBlock &ubuf(ubufs[i]);
+ if (ubufBinding == -1 && ubuf.binding >= 0) {
+ ubufBinding = ubuf.binding;
+ ubufSize = ubuf.size;
+ ubufStages |= toSrbStage(it->shader.stage());
+ masterUniformData.fill('\0', ubufSize);
+ } else if (ubufBinding == ubuf.binding && ubuf.binding >= 0) {
+ if (ubuf.size > ubufSize) {
+ ubufSize = ubuf.size;
+ masterUniformData.fill('\0', ubufSize);
+ }
+ ubufStages |= toSrbStage(it->shader.stage());
+ } else {
+ qWarning("Uniform block %s (binding %d) ignored", qPrintable(ubuf.blockName), ubuf.binding);
+ }
+ }
+
+ const QVector<QShaderDescription::InOutVariable> imageSamplers = desc.combinedImageSamplers();
+ const int imageSamplersCount = imageSamplers.count();
+ for (int i = 0; i < imageSamplersCount; ++i) {
+ const QShaderDescription::InOutVariable &var(imageSamplers[i]);
+ if (var.binding >= 0 && var.binding < MAX_SHADER_RESOURCE_BINDINGS)
+ combinedImageSamplerBindings[var.binding] |= toSrbStage(it->shader.stage());
+ else
+ qWarning("Encountered invalid combined image sampler (%s) binding %d",
+ qPrintable(var.name), var.binding);
+ }
+
+ if (it.key() == QShader::VertexStage)
+ vertexShader = &it.value();
+ else if (it.key() == QShader::FragmentStage)
+ fragmentShader = &it.value();
+ }
+
+ if (vertexShader && vertexShaderVariant == QShader::BatchableVertexShader && vertexShader->qt_order_attrib_location == -1)
+ qWarning("No rewriter-inserted attribute found, this should not happen.");
+}
+
+/*!
+ Constructs a new QSGMaterialRhiShader.
+ */
+QSGMaterialRhiShader::QSGMaterialRhiShader()
+ : d_ptr(new QSGMaterialRhiShaderPrivate(this))
+{
+}
+
+/*!
+ \internal
+ */
+QSGMaterialRhiShader::QSGMaterialRhiShader(QSGMaterialRhiShaderPrivate &dd)
+ : d_ptr(&dd)
+{
+}
+
+/*!
+ \internal
+ */
+QSGMaterialRhiShader::~QSGMaterialRhiShader()
+{
+}
+
+// We have our own enum as QShader is not initially public. Internally
+// everything works with QShader::Stage however. So convert.
+static inline QShader::Stage toShaderStage(QSGMaterialRhiShader::Stage stage)
+{
+ switch (stage) {
+ case QSGMaterialRhiShader::VertexStage:
+ return QShader::VertexStage;
+ case QSGMaterialRhiShader::FragmentStage:
+ return QShader::FragmentStage;
+ default:
+ Q_UNREACHABLE();
+ return QShader::VertexStage;
+ }
+}
+
+/*!
+ Sets the \a shader for the specified \a stage.
+ */
+void QSGMaterialRhiShader::setShader(Stage stage, const QShader &shader)
+{
+ Q_D(QSGMaterialRhiShader);
+ d->shaders[toShaderStage(stage)] = QSGMaterialRhiShaderPrivate::ShaderStageData(shader);
+}
+
+/*!
+ Sets the \a filename for the shader for the specified \a stage.
+
+ The file is expected to contain a serialized QRhiShader.
+ */
+void QSGMaterialRhiShader::setShaderFileName(Stage stage, const QString &filename)
+{
+ Q_D(QSGMaterialRhiShader);
+ d->shaderFileNames[toShaderStage(stage)] = filename;
+}
+
+/*!
+ \return the currently set flags for this material shader.
+ */
+QSGMaterialRhiShader::Flags QSGMaterialRhiShader::flags() const
+{
+ Q_D(const QSGMaterialRhiShader);
+ return d->flags;
+}
+
+/*!
+ Sets the \a flags on this material shader if \a on is true;
+ otherwise clears the specified flags.
+*/
+void QSGMaterialRhiShader::setFlag(Flags flags, bool on)
+{
+ Q_D(QSGMaterialRhiShader);
+ if (on)
+ d->flags |= flags;
+ else
+ d->flags &= ~flags;
+}
+
+/*!
+ This function is called by the scene graph to get the contents of the
+ shader program's uniform buffer updated. The implementation is not expected
+ to perform any real graphics operations, it is merely responsible for
+ copying data to the QByteArray returned from RenderState::uniformData().
+ The scene graph takes care of making that buffer visible in the shaders.
+
+ The current rendering \a state is passed from the scene graph. If the state
+ indicates that any relevant state is dirty, the implementation must update
+ the appropriate region in the buffer data that is accessible via
+ RenderState::uniformData(). When a state, such as, matrix or opacity, is
+ not dirty, there is no need to touch the corresponding region since the
+ data is persistent.
+
+ The return value must be \c true whenever any change was made to the uniform data.
+
+ The subclass specific state, such as the color of a flat color material,
+ should be extracted from \a newMaterial to update the relevant regions in
+ the buffer accordingly.
+
+ \a oldMaterial can be used to minimize buffer changes (which are typically
+ memcpy calls) when updating material states. When \a oldMaterial is null,
+ this shader was just activated.
+ */
+bool QSGMaterialRhiShader::updateUniformData(const RenderState &state,
+ QSGMaterial *newMaterial,
+ QSGMaterial *oldMaterial)
+{
+ Q_UNUSED(state);
+ Q_UNUSED(newMaterial);
+ Q_UNUSED(oldMaterial);
+ return false;
+}
+
+/*!
+ This function is called by the scene graph to prepare using a sampled image
+ in the shader, typically in form of a combined image sampler.
+
+ \a binding is the binding number of the sampler. The function is called for
+ each variable in the material's shaders'
+ \l{QShaderDescription::combinedImageSamplers()}.
+
+ When \c{*texture} is null, it must be set to a QSGTexture pointer before
+ returning. When non-null, it is up to the material to decide if a new
+ \c{QSGTexture *} is stored to it, or if it updates some parameters on the
+ already known QSGTexture. The ownership of the QSGTexture is not
+ transferred.
+
+ The current rendering \a state is passed from the scene graph. It is up to
+ the material to enqueue the texture data uploads to the
+ QRhiResourceUpdateBatch retriveable via RenderState::resourceUpdateBatch().
+
+ The subclass specific state can be extracted from \a newMaterial.
+
+ \a oldMaterial can be used to minimize changes. When \a oldMaterial is null,
+ this shader was just activated.
+ */
+void QSGMaterialRhiShader::updateSampledImage(const RenderState &state,
+ int binding,
+ QSGTexture **texture,
+ QSGMaterial *newMaterial,
+ QSGMaterial *oldMaterial)
+{
+ Q_UNUSED(state);
+ Q_UNUSED(binding);
+ Q_UNUSED(texture);
+ Q_UNUSED(newMaterial);
+ Q_UNUSED(oldMaterial);
+}
+
+/*!
+ This function is called by the scene graph to enable the material to
+ provide a custom set of graphics state. The set of states that are
+ customizable by material is limited to blending and related settings.
+
+ \note This function is only called when the UpdatesGraphicsPipelineState
+ flag was enabled via setFlags(). By default it is not set, and so this
+ function is never called.
+
+ The return value must be \c true whenever a change was made to any of the
+ members in \a ps.
+
+ \note The contents of \a ps is not persistent between invocations of this
+ function.
+
+ The current rendering \a state is passed from the scene graph.
+
+ The subclass specific state can be extracted from \a newMaterial. When \a
+ oldMaterial is null, this shader was just activated.
+ */
+bool QSGMaterialRhiShader::updateGraphicsPipelineState(const RenderState &state, GraphicsPipelineState *ps,
+ QSGMaterial *newMaterial, QSGMaterial *oldMaterial)
+{
+ Q_UNUSED(state);
+ Q_UNUSED(ps);
+ Q_UNUSED(newMaterial);
+ Q_UNUSED(oldMaterial);
+ return false;
+}
+
+/*!
+ \class QSGMaterialRhiShader::RenderState
+
+ \brief Encapsulates the current rendering state during a call to
+ QSGMaterialRhiShader::updateUniformData() and the other \c update type of
+ functions.
+
+ \inmodule QtQuick
+ \since 5.14
+
+ The render state contains a number of accessors that the shader needs to
+ respect in order to conform to the current state of the scene graph.
+ */
+
+/*!
+ \enum QSGMaterialRhiShader::RenderState::DirtyState
+
+ \value DirtyMatrix Used to indicate that the matrix has changed and must be
+ updated.
+
+ \value DirtyOpacity Used to indicate that the opacity has changed and must
+ be updated.
+
+ \value DirtyAll Used to indicate that everything needs to be updated.
+ */
+
+/*!
+ \fn bool QSGMaterialRhiShader::RenderState::isMatrixDirty() const
+
+ Returns \c true if the dirtyStates() contain the dirty matrix state,
+ otherwise returns \c false.
+ */
+
+/*!
+ \fn bool QSGMaterialRhiShader::RenderState::isOpacityDirty() const
+
+ Returns \c true if the dirtyStates() contains the dirty opacity state,
+ otherwise returns \c false.
+ */
+
+/*!
+ \fn QSGMaterialRhiShader::RenderState::DirtyStates QSGMaterialRhiShader::RenderState::dirtyStates() const
+
+ Returns which rendering states that have changed and needs to be updated
+ for geometry rendered with this material to conform to the current
+ rendering state.
+ */
+
+/*!
+ Returns the accumulated opacity to be used for rendering.
+ */
+float QSGMaterialRhiShader::RenderState::opacity() const
+{
+ Q_ASSERT(m_data);
+ return static_cast<const QSGRenderer *>(m_data)->currentOpacity();
+}
+
+/*!
+ Returns the modelview determinant to be used for rendering.
+ */
+float QSGMaterialRhiShader::RenderState::determinant() const
+{
+ Q_ASSERT(m_data);
+ return static_cast<const QSGRenderer *>(m_data)->determinant();
+}
+
+/*!
+ Returns the matrix combined of modelview matrix and project matrix.
+ */
+QMatrix4x4 QSGMaterialRhiShader::RenderState::combinedMatrix() const
+{
+ Q_ASSERT(m_data);
+ return static_cast<const QSGRenderer *>(m_data)->currentCombinedMatrix();
+}
+
+/*!
+ Returns the ratio between physical pixels and device-independent pixels
+ to be used for rendering.
+*/
+float QSGMaterialRhiShader::RenderState::devicePixelRatio() const
+{
+ Q_ASSERT(m_data);
+ return static_cast<const QSGRenderer *>(m_data)->devicePixelRatio();
+}
+
+/*!
+ Returns the model view matrix.
+
+ If the material has the RequiresFullMatrix flag set, this is guaranteed to
+ be the complete transform matrix calculated from the scenegraph.
+
+ However, if this flag is not set, the renderer may choose to alter this
+ matrix. For example, it may pre-transform vertices on the CPU and set this
+ matrix to identity.
+
+ In a situation such as the above, it is still possible to retrieve the
+ actual matrix determinant by setting the RequiresDeterminant flag in the
+ material and calling the determinant() accessor.
+ */
+QMatrix4x4 QSGMaterialRhiShader::RenderState::modelViewMatrix() const
+{
+ Q_ASSERT(m_data);
+ return static_cast<const QSGRenderer *>(m_data)->currentModelViewMatrix();
+}
+
+/*!
+ Returns the projection matrix.
+ */
+QMatrix4x4 QSGMaterialRhiShader::RenderState::projectionMatrix() const
+{
+ Q_ASSERT(m_data);
+ return static_cast<const QSGRenderer *>(m_data)->currentProjectionMatrix();
+}
+
+/*!
+ Returns the viewport rect of the surface being rendered to.
+ */
+QRect QSGMaterialRhiShader::RenderState::viewportRect() const
+{
+ Q_ASSERT(m_data);
+ return static_cast<const QSGRenderer *>(m_data)->viewportRect();
+}
+
+/*!
+ Returns the device rect of the surface being rendered to
+ */
+QRect QSGMaterialRhiShader::RenderState::deviceRect() const
+{
+ Q_ASSERT(m_data);
+ return static_cast<const QSGRenderer *>(m_data)->deviceRect();
+}
+
+/*!
+ Returns a pointer to the data for the uniform (constant) buffer in the
+ shader.
+
+ \note It is strongly recommended to declare the uniform block with \c
+ std140 in the shader, and to carefully study the standard uniform block
+ layout as described in section 7.6.2.2 of the OpenGL specification. It is
+ up to the QSGMaterialRhiShader implementation to ensure data gets placed
+ at the right location in this QByteArray, taking alignment requirements
+ into account. Shader code translated to other shading languages is expected
+ to use the same offsets for block members, even when the target language
+ uses different packing rules by default.
+
+ \note Avoid copying from C++ POD types, such as, structs, in order to
+ update multiple members at once, unless it has been verified that the
+ layouts of the C++ struct and the GLSL uniform block match.
+
+ \note Uniform data must only be updated from
+ QSGMaterialRhiShader::updateUniformData().
+ */
+QByteArray *QSGMaterialRhiShader::RenderState::uniformData() const
+{
+ Q_ASSERT(m_data);
+ return static_cast<const QSGRenderer *>(m_data)->currentUniformData();
+}
+
+/*!
+ Returns a resource update batch to which upload and copy operatoins can be
+ queued. This is typically used by
+ QSGMaterialRhiShader::updateSampledImage() to enqueue texture image
+ content updates.
+ */
+QRhiResourceUpdateBatch *QSGMaterialRhiShader::RenderState::resourceUpdateBatch() const
+{
+ Q_ASSERT(m_data);
+ return static_cast<const QSGRenderer *>(m_data)->currentResourceUpdateBatch();
+}
+
+/*!
+ Returns the current QRhi.
+ */
+QRhi *QSGMaterialRhiShader::RenderState::rhi() const
+{
+ Q_ASSERT(m_data);
+ return static_cast<const QSGRenderer *>(m_data)->currentRhi();
+}
+
+char const *const *QSGMaterialRhiShader::attributeNames() const
+{
+ Q_ASSERT_X(false, "QSGMaterialRhiShader::attributeNames()", "Not implemented for RHI");
+ return nullptr;
+}
+
+QT_END_NAMESPACE
diff --git a/src/quick/scenegraph/coreapi/qsgmaterialrhishader.h b/src/quick/scenegraph/coreapi/qsgmaterialrhishader.h
new file mode 100644
index 0000000000..01991135ec
--- /dev/null
+++ b/src/quick/scenegraph/coreapi/qsgmaterialrhishader.h
@@ -0,0 +1,182 @@
+/****************************************************************************
+**
+** Copyright (C) 2019 The Qt Company Ltd.
+** 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 QSGMATERIALRHISHADER_H
+#define QSGMATERIALRHISHADER_H
+
+#include <QtQuick/qtquickglobal.h>
+#include <QtCore/QRect>
+#include <QtGui/QMatrix4x4>
+#include <QtGui/QColor>
+#include <QtQuick/qsgmaterialshader.h>
+
+QT_BEGIN_NAMESPACE
+
+class QSGMaterial;
+class QSGMaterialRhiShaderPrivate;
+class QSGTexture;
+class QRhiResourceUpdateBatch;
+class QRhi;
+class QShader;
+
+class Q_QUICK_EXPORT QSGMaterialRhiShader : public QSGMaterialShader // ### Qt 6: remove inheritance
+{
+public:
+ class Q_QUICK_EXPORT RenderState {
+ public:
+ using DirtyStates = QSGMaterialShader::RenderState::DirtyStates;
+
+ inline DirtyStates dirtyStates() const { return m_dirty; }
+
+ inline bool isMatrixDirty() const { return m_dirty & QSGMaterialShader::RenderState::DirtyMatrix; }
+ inline bool isOpacityDirty() const { return m_dirty & QSGMaterialShader::RenderState::DirtyOpacity; }
+
+ float opacity() const;
+ QMatrix4x4 combinedMatrix() const;
+ QMatrix4x4 modelViewMatrix() const;
+ QMatrix4x4 projectionMatrix() const;
+ QRect viewportRect() const;
+ QRect deviceRect() const;
+ float determinant() const;
+ float devicePixelRatio() const;
+
+ QByteArray *uniformData() const;
+ QRhiResourceUpdateBatch *resourceUpdateBatch() const;
+ QRhi *rhi() const;
+
+ private:
+ friend class QSGRenderer;
+ DirtyStates m_dirty;
+ const void *m_data;
+ };
+
+ struct Q_QUICK_EXPORT GraphicsPipelineState {
+ enum BlendFactor {
+ Zero,
+ One,
+ SrcColor,
+ OneMinusSrcColor,
+ DstColor,
+ OneMinusDstColor,
+ SrcAlpha,
+ OneMinusSrcAlpha,
+ DstAlpha,
+ OneMinusDstAlpha,
+ ConstantColor,
+ OneMinusConstantColor,
+ ConstantAlpha,
+ OneMinusConstantAlpha,
+ SrcAlphaSaturate,
+ Src1Color,
+ OneMinusSrc1Color,
+ Src1Alpha,
+ OneMinusSrc1Alpha
+ };
+
+ enum ColorMaskComponent {
+ R = 1 << 0,
+ G = 1 << 1,
+ B = 1 << 2,
+ A = 1 << 3
+ };
+ Q_DECLARE_FLAGS(ColorMask, ColorMaskComponent)
+
+ enum CullMode {
+ CullNone,
+ CullFront,
+ CullBack
+ };
+
+ bool blendEnable;
+ BlendFactor srcColor;
+ BlendFactor dstColor;
+ ColorMask colorWrite;
+ QColor blendConstant;
+ CullMode cullMode;
+ // This struct is extensible while keeping BC since apps only ever get
+ // a ptr to the struct, it is not created by them.
+ };
+
+ enum Flag {
+ UpdatesGraphicsPipelineState = 0x0001
+ };
+ Q_DECLARE_FLAGS(Flags, Flag)
+
+ enum Stage {
+ VertexStage,
+ FragmentStage,
+ };
+
+ QSGMaterialRhiShader();
+ virtual ~QSGMaterialRhiShader();
+
+ virtual bool updateUniformData(const RenderState &state,
+ QSGMaterial *newMaterial, QSGMaterial *oldMaterial);
+
+ virtual void updateSampledImage(const RenderState &state, int binding, QSGTexture **texture,
+ QSGMaterial *newMaterial, QSGMaterial *oldMaterial);
+
+ virtual bool updateGraphicsPipelineState(const RenderState &state, GraphicsPipelineState *ps,
+ QSGMaterial *newMaterial, QSGMaterial *oldMaterial);
+
+ Flags flags() const;
+ void setFlag(Flags flags, bool on = true);
+
+ // dummy impl for base class pure virtual, never called
+ char const *const *attributeNames() const override;
+
+protected:
+ Q_DECLARE_PRIVATE(QSGMaterialRhiShader)
+ QSGMaterialRhiShader(QSGMaterialRhiShaderPrivate &dd);
+
+ // filename is for a file containing a serialized QShader.
+ void setShaderFileName(Stage stage, const QString &filename);
+
+ void setShader(Stage stage, const QShader &shader);
+
+private:
+ QScopedPointer<QSGMaterialRhiShaderPrivate> d_ptr;
+};
+
+Q_DECLARE_OPERATORS_FOR_FLAGS(QSGMaterialRhiShader::GraphicsPipelineState::ColorMask)
+Q_DECLARE_OPERATORS_FOR_FLAGS(QSGMaterialRhiShader::Flags)
+
+QT_END_NAMESPACE
+
+#endif
diff --git a/src/quick/scenegraph/coreapi/qsgmaterialrhishader_p.h b/src/quick/scenegraph/coreapi/qsgmaterialrhishader_p.h
new file mode 100644
index 0000000000..153b4b120a
--- /dev/null
+++ b/src/quick/scenegraph/coreapi/qsgmaterialrhishader_p.h
@@ -0,0 +1,114 @@
+/****************************************************************************
+**
+** Copyright (C) 2019 The Qt Company Ltd.
+** 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 QSGMATERIALRHISHADER_P_H
+#define QSGMATERIALRHISHADER_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 <private/qtquickglobal_p.h>
+#include "qsgmaterialrhishader.h"
+#include "qsgmaterial.h"
+#include <QtGui/private/qrhi_p.h>
+#include <QtGui/private/qshader_p.h>
+
+QT_BEGIN_NAMESPACE
+
+class QRhiSampler;
+
+class Q_QUICK_PRIVATE_EXPORT QSGMaterialRhiShaderPrivate
+{
+public:
+ Q_DECLARE_PUBLIC(QSGMaterialRhiShader)
+
+ QSGMaterialRhiShaderPrivate(QSGMaterialRhiShader *q) : q_ptr(q) { }
+ static QSGMaterialRhiShaderPrivate *get(QSGMaterialRhiShader *s) { return s->d_func(); }
+ static const QSGMaterialRhiShaderPrivate *get(const QSGMaterialRhiShader *s) { return s->d_func(); }
+
+ void clearCachedRendererData();
+ void prepare(QShader::Variant vertexShaderVariant);
+
+ QShader shader(QShader::Stage stage) const { return shaders[stage].shader; }
+
+ static QShader loadShader(const QString &filename);
+
+ QSGMaterialRhiShader *q_ptr;
+ QHash<QShader::Stage, QString> shaderFileNames;
+ QSGMaterialRhiShader::Flags flags;
+
+ struct ShaderStageData {
+ ShaderStageData() { } // so shader.isValid() == false
+ ShaderStageData(const QShader &shader) : shader(shader) { }
+ QShader shader;
+ QShader::Variant shaderVariant = QShader::StandardShader;
+ QVector<int> vertexInputLocations; // excluding rewriter-inserted ones
+ int qt_order_attrib_location = -1; // rewriter-inserted
+ };
+ QHash<QShader::Stage, ShaderStageData> shaders;
+
+ static const int MAX_SHADER_RESOURCE_BINDINGS = 32;
+
+ int ubufBinding = -1;
+ int ubufSize = 0;
+ QRhiShaderResourceBinding::StageFlags ubufStages;
+ QRhiShaderResourceBinding::StageFlags combinedImageSamplerBindings[MAX_SHADER_RESOURCE_BINDINGS];
+
+ ShaderStageData *vertexShader = nullptr;
+ ShaderStageData *fragmentShader = nullptr;
+
+ QByteArray masterUniformData;
+
+ QSGTexture *textureBindingTable[MAX_SHADER_RESOURCE_BINDINGS];
+ QRhiSampler *samplerBindingTable[MAX_SHADER_RESOURCE_BINDINGS];
+};
+
+Q_DECLARE_TYPEINFO(QSGMaterialRhiShaderPrivate::ShaderStageData, Q_MOVABLE_TYPE);
+
+QT_END_NAMESPACE
+
+#endif // QSGMATERIALRHISHADER_P_H
diff --git a/src/quick/scenegraph/coreapi/qsgmaterialshader.cpp b/src/quick/scenegraph/coreapi/qsgmaterialshader.cpp
new file mode 100644
index 0000000000..d614f9be4c
--- /dev/null
+++ b/src/quick/scenegraph/coreapi/qsgmaterialshader.cpp
@@ -0,0 +1,556 @@
+/****************************************************************************
+**
+** Copyright (C) 2019 The Qt Company Ltd.
+** 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 "qsgmaterial.h"
+#include "qsgrenderer_p.h"
+#include "qsgmaterialshader_p.h"
+#if QT_CONFIG(opengl)
+# include <private/qsgshadersourcebuilder_p.h>
+# include <private/qsgdefaultcontext_p.h>
+# include <private/qsgdefaultrendercontext_p.h>
+# include <QtGui/QOpenGLFunctions>
+# include <QtGui/QOpenGLContext>
+#endif
+
+QT_BEGIN_NAMESPACE
+
+#if QT_CONFIG(opengl)
+const char *QSGMaterialShaderPrivate::loadShaderSource(QOpenGLShader::ShaderType type) const
+{
+ const QStringList files = m_sourceFiles[type];
+ QSGShaderSourceBuilder builder;
+ for (const QString &file : files)
+ builder.appendSourceFile(file);
+ m_sources[type] = builder.source();
+ return m_sources[type].constData();
+}
+#endif
+
+/*!
+ \class QSGMaterialShader
+ \brief The QSGMaterialShader class represents an OpenGL shader program
+ in the renderer.
+ \inmodule QtQuick
+ \ingroup qtquick-scenegraph-materials
+
+ The QSGMaterialShader API is relatively low-level. A more convenient API,
+ which provides almost all the same features, is available through
+ QSGSimpleMaterialShader.
+
+ \warning This class is only functional when running with the legacy OpenGL
+ renderer of the Qt Quick scenegraph.
+
+ The QSGMaterial and QSGMaterialShader form a tight relationship. For one
+ scene graph (including nested graphs), there is one unique QSGMaterialShader
+ instance which encapsulates the QOpenGLShaderProgram the scene graph uses
+ to render that material, such as a shader to flat coloring of geometry.
+ Each QSGGeometryNode can have a unique QSGMaterial containing the
+ how the shader should be configured when drawing that node, such as
+ the actual color used to render the geometry.
+
+ An instance of QSGMaterialShader is never created explicitly by the user,
+ it will be created on demand by the scene graph through
+ QSGMaterial::createShader(). The scene graph will make sure that there
+ is only one instance of each shader implementation through a scene graph.
+
+ The source code returned from vertexShader() is used to control what the
+ material does with the vertiex data that comes in from the geometry.
+ The source code returned from the fragmentShader() is used to control
+ what how the material should fill each individual pixel in the geometry.
+ The vertex and fragment source code is queried once during initialization,
+ changing what is returned from these functions later will not have
+ any effect.
+
+ The activate() function is called by the scene graph when a shader is
+ is starting to be used. The deactivate function is called by the scene
+ graph when the shader is no longer going to be used. While active,
+ the scene graph may make one or more calls to updateState() which
+ will update the state of the shader for each individual geometry to
+ render.
+
+ The attributeNames() returns the name of the attributes used in the
+ vertexShader(). These are used in the default implementation of
+ activate() and deactivate() to decide whice vertex registers are enabled.
+
+ The initialize() function is called during program creation to allow
+ subclasses to prepare for use, such as resolve uniform names in the
+ vertexShader() and fragmentShader().
+
+ A minimal example:
+ \code
+ class Shader : public QSGMaterialShader
+ {
+ public:
+ const char *vertexShader() const {
+ return
+ "attribute highp vec4 vertex; \n"
+ "uniform highp mat4 matrix; \n"
+ "void main() { \n"
+ " gl_Position = matrix * vertex; \n"
+ "}";
+ }
+
+ const char *fragmentShader() const {
+ return
+ "uniform lowp float opacity; \n"
+ "void main() { \n"
+ " gl_FragColor = vec4(1, 0, 0, 1) * opacity; \n"
+ "}";
+ }
+
+ char const *const *attributeNames() const
+ {
+ static char const *const names[] = { "vertex", 0 };
+ return names;
+ }
+
+ void initialize()
+ {
+ QSGMaterialShader::initialize();
+ m_id_matrix = program()->uniformLocation("matrix");
+ m_id_opacity = program()->uniformLocation("opacity");
+ }
+
+ void updateState(const RenderState &state, QSGMaterial *newMaterial, QSGMaterial *oldMaterial)
+ {
+ Q_ASSERT(program()->isLinked());
+ if (state.isMatrixDirty())
+ program()->setUniformValue(m_id_matrix, state.combinedMatrix());
+ if (state.isOpacityDirty())
+ program()->setUniformValue(m_id_opacity, state.opacity());
+ }
+
+ private:
+ int m_id_matrix;
+ int m_id_opacity;
+ };
+ \endcode
+
+ \note All classes with QSG prefix should be used solely on the scene graph's
+ rendering thread. See \l {Scene Graph and Rendering} for more information.
+
+ */
+
+
+
+/*!
+ Creates a new QSGMaterialShader.
+ */
+QSGMaterialShader::QSGMaterialShader()
+ : d_ptr(new QSGMaterialShaderPrivate)
+{
+}
+
+/*!
+ \internal
+ */
+QSGMaterialShader::QSGMaterialShader(QSGMaterialShaderPrivate &dd)
+ : d_ptr(&dd)
+{
+}
+
+/*!
+ \internal
+ */
+QSGMaterialShader::~QSGMaterialShader()
+{
+}
+
+/*!
+ \fn char const *const *QSGMaterialShader::attributeNames() const
+
+ Returns a zero-terminated array describing the names of the
+ attributes used in the vertex shader.
+
+ This function is called when the shader is compiled to specify
+ which attributes exist. The order of the attribute names
+ defines the attribute register position in the vertex shader.
+ */
+
+#if QT_CONFIG(opengl)
+/*!
+ \fn const char *QSGMaterialShader::vertexShader() const
+
+ Called when the shader is being initialized to get the vertex
+ shader source code.
+
+ The contents returned from this function should never change.
+*/
+const char *QSGMaterialShader::vertexShader() const
+{
+ Q_D(const QSGMaterialShader);
+ return d->loadShaderSource(QOpenGLShader::Vertex);
+}
+
+
+/*!
+ \fn const char *QSGMaterialShader::fragmentShader() const
+
+ Called when the shader is being initialized to get the fragment
+ shader source code.
+
+ The contents returned from this function should never change.
+*/
+const char *QSGMaterialShader::fragmentShader() const
+{
+ Q_D(const QSGMaterialShader);
+ return d->loadShaderSource(QOpenGLShader::Fragment);
+}
+
+
+/*!
+ \fn QOpenGLShaderProgram *QSGMaterialShader::program()
+
+ Returns the shader program used by this QSGMaterialShader.
+ */
+#endif
+
+/*!
+ \fn void QSGMaterialShader::initialize()
+
+ Reimplement this function to do one-time initialization when the
+ shader program is compiled. The OpenGL shader program is compiled
+ and linked, but not bound, when this function is called.
+ */
+
+
+/*!
+ This function is called by the scene graph to indicate that geometry is
+ about to be rendered using this shader.
+
+ State that is global for all uses of the shader, independent of the geometry
+ that is being drawn, can be setup in this function.
+ */
+
+void QSGMaterialShader::activate()
+{
+}
+
+
+
+/*!
+ This function is called by the scene graph to indicate that geometry will
+ no longer to be rendered using this shader.
+ */
+
+void QSGMaterialShader::deactivate()
+{
+}
+
+
+
+/*!
+ This function is called by the scene graph before geometry is rendered
+ to make sure the shader is in the right state.
+
+ The current rendering \a state is passed from the scene graph. If the state
+ indicates that any state is dirty, the updateState implementation must
+ update accordingly for the geometry to render correctly.
+
+ The subclass specific state, such as the color of a flat color material, should
+ be extracted from \a newMaterial to update the color uniforms accordingly.
+
+ The \a oldMaterial can be used to minimze state changes when updating
+ material states. The \a oldMaterial is 0 if this shader was just activated.
+
+ \sa activate(), deactivate()
+ */
+
+void QSGMaterialShader::updateState(const RenderState & /* state */, QSGMaterial * /* newMaterial */, QSGMaterial * /* oldMaterial */)
+{
+}
+
+#if QT_CONFIG(opengl)
+/*!
+ Sets the GLSL source file for the shader stage \a type to \a sourceFile. The
+ default implementation of the vertexShader() and fragmentShader() functions
+ will load the source files set by this function.
+
+ This function is useful when you have a single source file for a given shader
+ stage. If your shader consists of multiple source files then use
+ setShaderSourceFiles()
+
+ \sa setShaderSourceFiles(), vertexShader(), fragmentShader()
+ */
+void QSGMaterialShader::setShaderSourceFile(QOpenGLShader::ShaderType type, const QString &sourceFile)
+{
+ Q_D(QSGMaterialShader);
+ d->m_sourceFiles[type] = (QStringList() << sourceFile);
+}
+
+/*!
+ Sets the GLSL source files for the shader stage \a type to \a sourceFiles. The
+ default implementation of the vertexShader() and fragmentShader() functions
+ will load the source files set by this function in the order given.
+
+ \sa setShaderSourceFile(), vertexShader(), fragmentShader()
+ */
+void QSGMaterialShader::setShaderSourceFiles(QOpenGLShader::ShaderType type, const QStringList &sourceFiles)
+{
+ Q_D(QSGMaterialShader);
+ d->m_sourceFiles[type] = sourceFiles;
+}
+
+/*!
+ This function is called when the shader is initialized to compile the
+ actual QOpenGLShaderProgram. Do not call it explicitly.
+
+ The default implementation will extract the vertexShader() and
+ fragmentShader() and bind the names returned from attributeNames()
+ to consecutive vertex attribute registers starting at 0.
+ */
+
+void QSGMaterialShader::compile()
+{
+ Q_ASSERT_X(!m_program.isLinked(), "QSGSMaterialShader::compile()", "Compile called multiple times!");
+
+ program()->addCacheableShaderFromSourceCode(QOpenGLShader::Vertex, vertexShader());
+ program()->addCacheableShaderFromSourceCode(QOpenGLShader::Fragment, fragmentShader());
+
+ char const *const *attr = attributeNames();
+#ifndef QT_NO_DEBUG
+ int maxVertexAttribs = 0;
+ QOpenGLFunctions *funcs = QOpenGLContext::currentContext()->functions();
+ funcs->glGetIntegerv(GL_MAX_VERTEX_ATTRIBS, &maxVertexAttribs);
+ for (int i = 0; attr[i]; ++i) {
+ if (i >= maxVertexAttribs) {
+ qFatal("List of attribute names is either too long or not null-terminated.\n"
+ "Maximum number of attributes on this hardware is %i.\n"
+ "Vertex shader:\n%s\n"
+ "Fragment shader:\n%s\n",
+ maxVertexAttribs, vertexShader(), fragmentShader());
+ }
+ if (*attr[i])
+ program()->bindAttributeLocation(attr[i], i);
+ }
+#else
+ for (int i = 0; attr[i]; ++i) {
+ if (*attr[i])
+ program()->bindAttributeLocation(attr[i], i);
+ }
+#endif
+
+ if (!program()->link()) {
+ qWarning("QSGMaterialShader: Shader compilation failed:");
+ qWarning() << program()->log();
+ }
+}
+
+#endif
+
+/*!
+ \class QSGMaterialShader::RenderState
+ \brief The QSGMaterialShader::RenderState encapsulates the current rendering state
+ during a call to QSGMaterialShader::updateState().
+ \inmodule QtQuick
+
+ The render state contains a number of accessors that the shader needs to respect
+ in order to conform to the current state of the scene graph.
+
+ The instance is only valid inside a call to QSGMaterialShader::updateState() and
+ should not be used outisde this function.
+ */
+
+
+
+/*!
+ \enum QSGMaterialShader::RenderState::DirtyState
+
+ \value DirtyMatrix Used to indicate that the matrix has changed and must be updated.
+
+ \value DirtyOpacity Used to indicate that the opacity has changed and must be updated.
+
+ \value DirtyCachedMaterialData Used to indicate that the cached material data have changed and must be updated.
+
+ \value DirtyAll Used to indicate that everything needs to be updated.
+ */
+
+
+
+/*!
+ \fn bool QSGMaterialShader::RenderState::isMatrixDirty() const
+
+ Returns \c true if the dirtyStates() contain the dirty matrix state,
+ otherwise returns \c false.
+ */
+
+
+
+/*!
+ \fn bool QSGMaterialShader::RenderState::isOpacityDirty() const
+
+ Returns \c true if the dirtyStates() contains the dirty opacity state,
+ otherwise returns \c false.
+ */
+
+/*!
+ \fn bool QSGMaterialShader::RenderState::isCachedMaterialDataDirty() const
+
+ Returns \c true if the dirtyStates() contains the dirty cached material state,
+ otherwise returns \c false.
+ */
+
+/*!
+ \fn QSGMaterialShader::RenderState::DirtyStates QSGMaterialShader::RenderState::dirtyStates() const
+
+ Returns which rendering states that have changed and needs to be updated
+ for geometry rendered with this material to conform to the current
+ rendering state.
+ */
+
+
+
+/*!
+ Returns the accumulated opacity to be used for rendering.
+ */
+
+float QSGMaterialShader::RenderState::opacity() const
+{
+ Q_ASSERT(m_data);
+ return static_cast<const QSGRenderer *>(m_data)->currentOpacity();
+}
+
+/*!
+ Returns the modelview determinant to be used for rendering.
+ */
+
+float QSGMaterialShader::RenderState::determinant() const
+{
+ Q_ASSERT(m_data);
+ return static_cast<const QSGRenderer *>(m_data)->determinant();
+}
+
+/*!
+ Returns the matrix combined of modelview matrix and project matrix.
+ */
+
+QMatrix4x4 QSGMaterialShader::RenderState::combinedMatrix() const
+{
+ Q_ASSERT(m_data);
+ return static_cast<const QSGRenderer *>(m_data)->currentCombinedMatrix();
+}
+/*!
+ Returns the ratio between physical pixels and device-independent pixels
+ to be used for rendering.
+*/
+float QSGMaterialShader::RenderState::devicePixelRatio() const
+{
+ Q_ASSERT(m_data);
+ return static_cast<const QSGRenderer *>(m_data)->devicePixelRatio();
+}
+
+
+
+/*!
+ Returns the model view matrix.
+
+ If the material has the RequiresFullMatrix flag
+ set, this is guaranteed to be the complete transform
+ matrix calculated from the scenegraph.
+
+ However, if this flag is not set, the renderer may
+ choose to alter this matrix. For example, it may
+ pre-transform vertices on the CPU and set this matrix
+ to identity.
+
+ In a situation such as the above, it is still possible
+ to retrieve the actual matrix determinant by setting
+ the RequiresDeterminant flag in the material and
+ calling the determinant() accessor.
+ */
+
+QMatrix4x4 QSGMaterialShader::RenderState::modelViewMatrix() const
+{
+ Q_ASSERT(m_data);
+ return static_cast<const QSGRenderer *>(m_data)->currentModelViewMatrix();
+}
+
+/*!
+ Returns the projection matrix.
+ */
+
+QMatrix4x4 QSGMaterialShader::RenderState::projectionMatrix() const
+{
+ Q_ASSERT(m_data);
+ return static_cast<const QSGRenderer *>(m_data)->currentProjectionMatrix();
+}
+
+
+
+/*!
+ Returns the viewport rect of the surface being rendered to.
+ */
+
+QRect QSGMaterialShader::RenderState::viewportRect() const
+{
+ Q_ASSERT(m_data);
+ return static_cast<const QSGRenderer *>(m_data)->viewportRect();
+}
+
+
+
+/*!
+ Returns the device rect of the surface being rendered to
+ */
+
+QRect QSGMaterialShader::RenderState::deviceRect() const
+{
+ Q_ASSERT(m_data);
+ return static_cast<const QSGRenderer *>(m_data)->deviceRect();
+}
+
+#if QT_CONFIG(opengl)
+
+/*!
+ Returns the QOpenGLContext that is being used for rendering
+ */
+
+QOpenGLContext *QSGMaterialShader::RenderState::context() const
+{
+ // Only the QSGDefaultRenderContext will have an OpenGL Context to query
+ auto openGLRenderContext = static_cast<const QSGDefaultRenderContext *>(static_cast<const QSGRenderer *>(m_data)->context());
+ if (openGLRenderContext != nullptr)
+ return openGLRenderContext->openglContext();
+ else
+ return nullptr;
+}
+
+#endif
+
+QT_END_NAMESPACE
diff --git a/src/quick/scenegraph/coreapi/qsgmaterialshader.h b/src/quick/scenegraph/coreapi/qsgmaterialshader.h
new file mode 100644
index 0000000000..d7ee23384f
--- /dev/null
+++ b/src/quick/scenegraph/coreapi/qsgmaterialshader.h
@@ -0,0 +1,136 @@
+/****************************************************************************
+**
+** Copyright (C) 2019 The Qt Company Ltd.
+** 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 QSGMATERIALSHADER_H
+#define QSGMATERIALSHADER_H
+
+#include <QtQuick/qtquickglobal.h>
+#if QT_CONFIG(opengl)
+# include <QtGui/qopenglshaderprogram.h>
+#endif
+#include <QtGui/QMatrix4x4>
+#include <QtCore/QRect>
+#include <QtQuick/qsgmaterialtype.h> // for source compat
+
+QT_BEGIN_NAMESPACE
+
+class QSGMaterial;
+class QSGMaterialShaderPrivate;
+
+namespace QSGBatchRenderer {
+ class ShaderManager;
+}
+
+class Q_QUICK_EXPORT QSGMaterialShader
+{
+public:
+ class Q_QUICK_EXPORT RenderState {
+ public:
+ enum DirtyState
+ {
+ DirtyMatrix = 0x0001,
+ DirtyOpacity = 0x0002,
+ DirtyCachedMaterialData = 0x0004,
+ DirtyAll = 0xFFFF
+ };
+ Q_DECLARE_FLAGS(DirtyStates, DirtyState)
+
+ inline DirtyStates dirtyStates() const { return m_dirty; }
+
+ inline bool isMatrixDirty() const { return m_dirty & DirtyMatrix; }
+ inline bool isOpacityDirty() const { return m_dirty & DirtyOpacity; }
+ bool isCachedMaterialDataDirty() const { return m_dirty & DirtyCachedMaterialData; }
+
+ float opacity() const;
+ QMatrix4x4 combinedMatrix() const;
+ QMatrix4x4 modelViewMatrix() const;
+ QMatrix4x4 projectionMatrix() const;
+ QRect viewportRect() const;
+ QRect deviceRect() const;
+ float determinant() const;
+ float devicePixelRatio() const;
+#if QT_CONFIG(opengl)
+ QOpenGLContext *context() const;
+#endif
+ private:
+ friend class QSGRenderer;
+ DirtyStates m_dirty;
+ const void *m_data;
+ };
+
+ QSGMaterialShader();
+ virtual ~QSGMaterialShader();
+
+ virtual void activate();
+ virtual void deactivate();
+ // First time a material is used, oldMaterial is null.
+ virtual void updateState(const RenderState &state, QSGMaterial *newMaterial, QSGMaterial *oldMaterial);
+ virtual char const *const *attributeNames() const = 0; // Array must end with null.
+#if QT_CONFIG(opengl)
+ inline QOpenGLShaderProgram *program() { return &m_program; }
+#endif
+protected:
+ Q_DECLARE_PRIVATE(QSGMaterialShader)
+ QSGMaterialShader(QSGMaterialShaderPrivate &dd);
+
+ friend class QSGDefaultRenderContext;
+ friend class QSGBatchRenderer::ShaderManager;
+#if QT_CONFIG(opengl)
+ void setShaderSourceFile(QOpenGLShader::ShaderType type, const QString &sourceFile);
+ void setShaderSourceFiles(QOpenGLShader::ShaderType type, const QStringList &sourceFiles);
+
+ virtual void compile();
+#endif
+ virtual void initialize() { }
+#if QT_CONFIG(opengl)
+ virtual const char *vertexShader() const;
+ virtual const char *fragmentShader() const;
+#endif
+private:
+#if QT_CONFIG(opengl)
+ QOpenGLShaderProgram m_program;
+#endif
+ QScopedPointer<QSGMaterialShaderPrivate> d_ptr;
+};
+
+Q_DECLARE_OPERATORS_FOR_FLAGS(QSGMaterialShader::RenderState::DirtyStates)
+
+QT_END_NAMESPACE
+
+#endif
diff --git a/src/quick/scenegraph/coreapi/qsgmaterialshader_p.h b/src/quick/scenegraph/coreapi/qsgmaterialshader_p.h
index 47f5e5de09..ae23b4a8ce 100644
--- a/src/quick/scenegraph/coreapi/qsgmaterialshader_p.h
+++ b/src/quick/scenegraph/coreapi/qsgmaterialshader_p.h
@@ -1,5 +1,6 @@
/****************************************************************************
**
+** Copyright (C) 2019 The Qt Company Ltd.
** Copyright (C) 2016 Klaralvdalens Datakonsult AB (KDAB).
** Contact: https://www.qt.io/licensing/
**
@@ -52,7 +53,7 @@
//
#include <private/qtquickglobal_p.h>
-#include <QOpenGLShader>
+#include "qsgmaterial.h"
QT_BEGIN_NAMESPACE
@@ -67,11 +68,6 @@ public:
#endif
};
-#ifndef QT_NO_DEBUG
-Q_QUICK_PRIVATE_EXPORT bool qsg_test_and_clear_material_failure();
-Q_QUICK_PRIVATE_EXPORT void qsg_set_material_failure();
-#endif
-
QT_END_NAMESPACE
#endif // QSGMATERIALSHADER_P_H
diff --git a/src/quick/scenegraph/coreapi/qsgmaterialtype.h b/src/quick/scenegraph/coreapi/qsgmaterialtype.h
new file mode 100644
index 0000000000..15141c2d9e
--- /dev/null
+++ b/src/quick/scenegraph/coreapi/qsgmaterialtype.h
@@ -0,0 +1,51 @@
+/****************************************************************************
+**
+** Copyright (C) 2019 The Qt Company Ltd.
+** 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 QSGMATERIALTYPE_H
+#define QSGMATERIALTYPE_H
+
+#include <QtQuick/qtquickglobal.h>
+
+QT_BEGIN_NAMESPACE
+
+struct QSGMaterialType { };
+
+QT_END_NAMESPACE
+
+#endif
diff --git a/src/quick/scenegraph/coreapi/qsgnode.cpp b/src/quick/scenegraph/coreapi/qsgnode.cpp
index 1976538aec..7d9b74bc2b 100644
--- a/src/quick/scenegraph/coreapi/qsgnode.cpp
+++ b/src/quick/scenegraph/coreapi/qsgnode.cpp
@@ -1466,7 +1466,7 @@ QDebug operator<<(QDebug d, const QSGGeometryNode *n)
d << "Geometry(null)";
return d;
}
- d << "GeometryNode(" << hex << (const void *) n << dec;
+ d << "GeometryNode(" << Qt::hex << (const void *) n << Qt::dec;
const QSGGeometry *g = n->geometry();
@@ -1517,7 +1517,7 @@ QDebug operator<<(QDebug d, const QSGClipNode *n)
d << "ClipNode(null)";
return d;
}
- d << "ClipNode(" << hex << (const void *) n << dec;
+ d << "ClipNode(" << Qt::hex << (const void *) n << Qt::dec;
if (n->childCount())
d << "children=" << n->childCount();
@@ -1540,7 +1540,7 @@ QDebug operator<<(QDebug d, const QSGTransformNode *n)
}
const QMatrix4x4 m = n->matrix();
d << "TransformNode(";
- d << hex << (const void *) n << dec;
+ d << Qt::hex << (const void *) n << Qt::dec;
if (m.isIdentity())
d << "identity";
else if (m.determinant() == 1 && m(0, 0) == 1 && m(1, 1) == 1 && m(2, 2) == 1)
@@ -1562,7 +1562,7 @@ QDebug operator<<(QDebug d, const QSGOpacityNode *n)
return d;
}
d << "OpacityNode(";
- d << hex << (const void *) n << dec;
+ d << Qt::hex << (const void *) n << Qt::dec;
d << "opacity=" << n->opacity()
<< "combined=" << n->combinedOpacity()
<< (n->isSubtreeBlocked() ? "*BLOCKED*" : "");
@@ -1581,7 +1581,7 @@ QDebug operator<<(QDebug d, const QSGRootNode *n)
return d;
}
QDebugStateSaver saver(d);
- d << "RootNode" << hex << (const void *) n << (n->isSubtreeBlocked() ? "*BLOCKED*" : "");
+ d << "RootNode" << Qt::hex << (const void *) n << (n->isSubtreeBlocked() ? "*BLOCKED*" : "");
#ifdef QSG_RUNTIME_DESCRIPTION
d << QSGNodePrivate::description(n);
#endif
@@ -1614,8 +1614,8 @@ QDebug operator<<(QDebug d, const QSGNode *n)
d << static_cast<const QSGOpacityNode *>(n);
break;
case QSGNode::RenderNodeType:
- d << "RenderNode(" << hex << (const void *) n << dec
- << "flags=" << (int) n->flags() << dec
+ d << "RenderNode(" << Qt::hex << (const void *) n << Qt::dec
+ << "flags=" << (int) n->flags() << Qt::dec
<< (n->isSubtreeBlocked() ? "*BLOCKED*" : "");
#ifdef QSG_RUNTIME_DESCRIPTION
d << QSGNodePrivate::description(n);
@@ -1623,8 +1623,8 @@ QDebug operator<<(QDebug d, const QSGNode *n)
d << ')';
break;
default:
- d << "Node(" << hex << (const void *) n << dec
- << "flags=" << (int) n->flags() << dec
+ d << "Node(" << Qt::hex << (const void *) n << Qt::dec
+ << "flags=" << (int) n->flags() << Qt::dec
<< (n->isSubtreeBlocked() ? "*BLOCKED*" : "");
#ifdef QSG_RUNTIME_DESCRIPTION
d << QSGNodePrivate::description(n);
diff --git a/src/quick/scenegraph/coreapi/qsgrenderer.cpp b/src/quick/scenegraph/coreapi/qsgrenderer.cpp
index e1ba001d2d..7af932eeb5 100644
--- a/src/quick/scenegraph/coreapi/qsgrenderer.cpp
+++ b/src/quick/scenegraph/coreapi/qsgrenderer.cpp
@@ -132,6 +132,12 @@ QSGRenderer::QSGRenderer(QSGRenderContext *context)
, m_current_determinant(1)
, m_device_pixel_ratio(1)
, m_context(context)
+ , m_current_uniform_data(nullptr)
+ , m_current_resource_update_batch(nullptr)
+ , m_rhi(nullptr)
+ , m_rt(nullptr)
+ , m_cb(nullptr)
+ , m_rp_desc(nullptr)
, m_node_updater(nullptr)
, m_bindable(nullptr)
, m_changed_emitted(false)
@@ -184,21 +190,30 @@ bool QSGRenderer::isMirrored() const
void QSGRenderer::renderScene(uint fboId)
{
-#if QT_CONFIG(opengl)
- if (fboId) {
- QSGBindableFboId bindable(fboId);
- renderScene(bindable);
- } else {
+ if (m_rt) {
class B : public QSGBindable
{
public:
- void bind() const override { QOpenGLFramebufferObject::bindDefault(); }
+ void bind() const override { }
} bindable;
renderScene(bindable);
- }
+ } else {
+#if QT_CONFIG(opengl)
+ if (fboId) {
+ QSGBindableFboId bindable(fboId);
+ renderScene(bindable);
+ } else {
+ class B : public QSGBindable
+ {
+ public:
+ void bind() const override { QOpenGLFramebufferObject::bindDefault(); }
+ } bindable;
+ renderScene(bindable);
+ }
#else
- Q_UNUSED(fboId)
+ Q_UNUSED(fboId)
#endif
+ }
}
void QSGRenderer::renderScene(const QSGBindable &bindable)
diff --git a/src/quick/scenegraph/coreapi/qsgrenderer_p.h b/src/quick/scenegraph/coreapi/qsgrenderer_p.h
index d4ff6ea9fe..9c83ddf111 100644
--- a/src/quick/scenegraph/coreapi/qsgrenderer_p.h
+++ b/src/quick/scenegraph/coreapi/qsgrenderer_p.h
@@ -62,6 +62,10 @@ QT_BEGIN_NAMESPACE
class QSGBindable;
class QSGNodeUpdater;
+class QRhiRenderTarget;
+class QRhiCommandBuffer;
+class QRhiRenderPassDescriptor;
+class QRhiResourceUpdateBatch;
Q_QUICK_PRIVATE_EXPORT bool qsg_test_and_clear_fatal_render_error();
Q_QUICK_PRIVATE_EXPORT void qsg_set_fatal_renderer_error();
@@ -69,11 +73,10 @@ Q_QUICK_PRIVATE_EXPORT void qsg_set_fatal_renderer_error();
class Q_QUICK_PRIVATE_EXPORT QSGRenderer : public QSGAbstractRenderer
{
public:
-
QSGRenderer(QSGRenderContext *context);
virtual ~QSGRenderer();
- // Accessed by QSGMaterialShader::RenderState.
+ // Accessed by QSGMaterial[Rhi]Shader::RenderState.
QMatrix4x4 currentProjectionMatrix() const { return m_current_projection_matrix; }
QMatrix4x4 currentModelViewMatrix() const { return m_current_model_view_matrix; }
QMatrix4x4 currentCombinedMatrix() const { return m_current_projection_matrix * m_current_model_view_matrix; }
@@ -92,11 +95,35 @@ public:
QSGNodeUpdater *nodeUpdater() const;
void setNodeUpdater(QSGNodeUpdater *updater);
inline QSGMaterialShader::RenderState state(QSGMaterialShader::RenderState::DirtyStates dirty) const;
+ inline QSGMaterialRhiShader::RenderState rhiState(QSGMaterialRhiShader::RenderState::DirtyStates dirty) const;
virtual void setCustomRenderMode(const QByteArray &) { }
virtual void releaseCachedResources() { }
void clearChangedFlag() { m_changed_emitted = false; }
+ // Accessed by QSGMaterialRhiShader::RenderState.
+ QByteArray *currentUniformData() const { return m_current_uniform_data; }
+ QRhiResourceUpdateBatch *currentResourceUpdateBatch() const { return m_current_resource_update_batch; }
+ QRhi *currentRhi() const { return m_rhi; }
+
+ void setRenderTarget(QRhiRenderTarget *rt) { m_rt = rt; }
+ QRhiRenderTarget *renderTarget() const { return m_rt; }
+
+ void setCommandBuffer(QRhiCommandBuffer *cb) { m_cb = cb; }
+ QRhiCommandBuffer *commandBuffer() const { return m_cb; }
+
+ void setRenderPassDescriptor(QRhiRenderPassDescriptor *rpDesc) { m_rp_desc = rpDesc; }
+ QRhiRenderPassDescriptor *renderPassDescriptor() const { return m_rp_desc; }
+
+ void setRenderPassRecordingCallbacks(QSGRenderContext::RenderPassCallback start,
+ QSGRenderContext::RenderPassCallback end,
+ void *userData)
+ {
+ m_renderPassRecordingCallbacks.start = start;
+ m_renderPassRecordingCallbacks.end = end;
+ m_renderPassRecordingCallbacks.userData = userData;
+ }
+
protected:
virtual void render() = 0;
@@ -107,7 +134,8 @@ protected:
void addNodesToPreprocess(QSGNode *node);
void removeNodesToPreprocess(QSGNode *node);
- QMatrix4x4 m_current_projection_matrix;
+ QMatrix4x4 m_current_projection_matrix; // includes adjustment, where applicable, so can be treated as Y up in NDC always
+ QMatrix4x4 m_current_projection_matrix_native_ndc; // Vulkan has Y down in normalized device coordinates, others Y up...
QMatrix4x4 m_current_model_view_matrix;
qreal m_current_opacity;
qreal m_current_determinant;
@@ -115,6 +143,18 @@ protected:
QSGRenderContext *m_context;
+ QByteArray *m_current_uniform_data;
+ QRhiResourceUpdateBatch *m_current_resource_update_batch;
+ QRhi *m_rhi;
+ QRhiRenderTarget *m_rt;
+ QRhiCommandBuffer *m_cb;
+ QRhiRenderPassDescriptor *m_rp_desc;
+ struct {
+ QSGRenderContext::RenderPassCallback start = nullptr;
+ QSGRenderContext::RenderPassCallback end = nullptr;
+ void *userData = nullptr;
+ } m_renderPassRecordingCallbacks;
+
private:
QSGNodeUpdater *m_node_updater;
@@ -156,6 +196,14 @@ QSGMaterialShader::RenderState QSGRenderer::state(QSGMaterialShader::RenderState
return s;
}
+QSGMaterialRhiShader::RenderState QSGRenderer::rhiState(QSGMaterialRhiShader::RenderState::DirtyStates dirty) const
+{
+ QSGMaterialRhiShader::RenderState s;
+ s.m_dirty = dirty;
+ s.m_data = this;
+ return s;
+}
+
class Q_QUICK_PRIVATE_EXPORT QSGNodeDumper : public QSGNodeVisitor {
diff --git a/src/quick/scenegraph/coreapi/qsgrendererinterface.cpp b/src/quick/scenegraph/coreapi/qsgrendererinterface.cpp
index 0f49e615e4..e504fe1c62 100644
--- a/src/quick/scenegraph/coreapi/qsgrendererinterface.cpp
+++ b/src/quick/scenegraph/coreapi/qsgrendererinterface.cpp
@@ -55,13 +55,13 @@ QT_BEGIN_NAMESPACE
the Direct3D or Vulkan device) that is used by the scenegraph.
QSGRendererInterface's functions have varying availability. API and
- language queries, like graphicsApi() or shaderType() are always available,
- meaning it is sufficient to construct a QQuickWindow or QQuickView, and the
- graphics API or shading language in use can be queried right after via
- QQuickWindow::rendererInterface(). This guarantees that utilities like the
- GraphicsInfo QML type are able to report the correct values as early as
- possible, without having conditional property values - depending on for
- instance shaderType() - evaluate to unexpected values.
+ language queries, such as, graphicsApi() or shaderType() are always
+ available, meaning it is sufficient to construct a QQuickWindow or
+ QQuickView, and the graphics API or shading language in use can be queried
+ right after via QQuickWindow::rendererInterface(). This guarantees that
+ utilities like the GraphicsInfo QML type are able to report the correct
+ values as early as possible, without having conditional property values -
+ depending on for instance shaderType() - evaluate to unexpected values.
Engine-specific accessors, like getResource(), are however available only
after the scenegraph is initialized. Additionally, there may be
@@ -78,14 +78,62 @@ QT_BEGIN_NAMESPACE
\value OpenGL OpenGL ES 2.0 or higher
\value Direct3D12 Direct3D 12
\value OpenVG OpenVG via EGL
+ \value OpenGLRhi OpenGL ES 2.0 or higher via a graphics abstraction layer
+ \value Direct3D11Rhi Direct3D 11 via a graphics abstraction layer
+ \value VulkanRhi Vulkan 1.0 via a graphics abstraction layer
+ \value MetalRhi Metal via a graphics abstraction layer
+ \value NullRhi Null (no output) via a graphics abstraction layer
*/
/*!
\enum QSGRendererInterface::Resource
- \value DeviceResource The graphics device, when applicable.
- \value CommandQueueResource The graphics command queue used by the scenegraph, when applicable.
- \value CommandListResource The command list or buffer used by the scenegraph, when applicable.
- \value PainterResource The active QPainter used by the scenegraph, when running with the software backend.
+
+ \value DeviceResource The resource is a pointer to the graphics device,
+ when applicable. For example, a \c{VkDevice *}, \c{MTLDevice *} or
+ \c{ID3D11Device *}. Note that with Vulkan the returned value is a pointer
+ to the VkDevice, not the handle itself. This is because Vulkan handles may
+ not be pointers, and may use a different size from the architecture's
+ pointer size so merely casting to/from \c{void *} is wrong.
+
+ \value CommandQueueResource The resource is a pointer to the graphics
+ command queue used by the scenegraph, when applicable. For example, a
+ \c{VkQueue *} or \c{MTLCommandQueue *}. Note that with Vulkan the returned
+ value is a pointer to the VkQueue, not the handle itself.
+
+ \value CommandListResource The resource is a pointer to the command list or
+ buffer used by the scenegraph, when applicable. For example, a
+ \c{VkCommandBuffer *} or \c{MTLCommandBuffer *}. This object has limited
+ validity, and is only valid while the scene graph is preparing the next
+ frame. Note that with Vulkan the returned value is a pointer to the
+ VkCommandBuffer, not the handle itself.
+
+ \value PainterResource The resource is a pointer to the active QPainter
+ used by the scenegraph, when running with the software backend.
+
+ \value RhiResource The resource is a pointer to the QRhi instance used by
+ the scenegraph, when applicable.
+
+ \value PhysicalDeviceResource The resource is a pointer to the pysical
+ device object used by the scenegraph, when applicable. For example, a
+ \c{VkPhysicalDevice *}. Note that with Vulkan the returned value is a
+ pointer to the VkPhysicalDevice, not the handle itself.
+
+ \value OpenGLContextResource The resource is a pointer to the
+ QOpenGLContext used by the scenegraph (on the render thread), when
+ applicable.
+
+ \value DeviceContextResource The resource is a pointer to the device
+ context used by the scenegraph, when applicable. For example, a
+ \c{ID3D11DeviceContext *}.
+
+ \value CommandEncoderResource The resource is a pointer to the currently
+ active render command encoder object used by the scenegraph, when
+ applicable. For example, a \c{MTLRenderCommandEncoder *}. This object has
+ limited validity, and is only valid while the scene graph is recording a
+ render pass for the next frame.
+
+ \value VulkanInstanceResource The resource is a pointer to the
+ QVulkanInstance used by the scenegraph, when applicable.
*/
/*!
@@ -93,6 +141,8 @@ QT_BEGIN_NAMESPACE
\value UnknownShadingLanguage Not yet known due to no window and scenegraph associated
\value GLSL GLSL or GLSL ES
\value HLSL HLSL
+ \value RhiShader Consumes QShader instances containing shader
+ variants for multiple target languages and bytecode formats
*/
/*!
@@ -164,6 +214,30 @@ void *QSGRendererInterface::getResource(QQuickWindow *window, const char *resour
}
/*!
+ \return true if \a api is based on a graphics abstraction layer (QRhi)
+ instead of directly calling the native graphics API.
+
+ \note This function can be called on any thread.
+ */
+bool QSGRendererInterface::isApiRhiBased(GraphicsApi api)
+{
+ switch (api) {
+ case OpenGLRhi:
+ Q_FALLTHROUGH();
+ case Direct3D11Rhi:
+ Q_FALLTHROUGH();
+ case VulkanRhi:
+ Q_FALLTHROUGH();
+ case MetalRhi:
+ Q_FALLTHROUGH();
+ case NullRhi:
+ return true;
+ default:
+ return false;
+ }
+}
+
+/*!
\fn QSGRendererInterface::ShaderType QSGRendererInterface::shaderType() const
\return the shading language supported by the Qt Quick backend the
diff --git a/src/quick/scenegraph/coreapi/qsgrendererinterface.h b/src/quick/scenegraph/coreapi/qsgrendererinterface.h
index 722488201b..3052c81f6c 100644
--- a/src/quick/scenegraph/coreapi/qsgrendererinterface.h
+++ b/src/quick/scenegraph/coreapi/qsgrendererinterface.h
@@ -54,20 +54,32 @@ public:
Software,
OpenGL,
Direct3D12,
- OpenVG
+ OpenVG,
+ OpenGLRhi,
+ Direct3D11Rhi,
+ VulkanRhi,
+ MetalRhi,
+ NullRhi,
};
enum Resource {
DeviceResource,
CommandQueueResource,
CommandListResource,
- PainterResource
+ PainterResource,
+ RhiResource,
+ PhysicalDeviceResource,
+ OpenGLContextResource,
+ DeviceContextResource,
+ CommandEncoderResource,
+ VulkanInstanceResource
};
enum ShaderType {
UnknownShadingLanguage,
GLSL,
- HLSL
+ HLSL,
+ RhiShader
};
enum ShaderCompilationType {
@@ -93,6 +105,8 @@ public:
virtual ShaderType shaderType() const = 0;
virtual ShaderCompilationTypes shaderCompilationType() const = 0;
virtual ShaderSourceTypes shaderSourceType() const = 0;
+
+ static bool isApiRhiBased(GraphicsApi api);
};
Q_DECLARE_OPERATORS_FOR_FLAGS(QSGRendererInterface::ShaderCompilationTypes)
diff --git a/src/quick/scenegraph/coreapi/qsgtexture.cpp b/src/quick/scenegraph/coreapi/qsgtexture.cpp
new file mode 100644
index 0000000000..cfd0cb9f06
--- /dev/null
+++ b/src/quick/scenegraph/coreapi/qsgtexture.cpp
@@ -0,0 +1,810 @@
+/****************************************************************************
+**
+** Copyright (C) 2019 The Qt Company Ltd.
+** 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 "qsgtexture_p.h"
+#if QT_CONFIG(opengl)
+# include <QtGui/qopenglcontext.h>
+# include <QtGui/qopenglfunctions.h>
+#endif
+#include <private/qqmlglobal_p.h>
+#include <private/qsgmaterialshader_p.h>
+#include <QtGui/private/qrhi_p.h>
+
+#if defined(Q_OS_LINUX) && !defined(Q_OS_ANDROID) && defined(__GLIBC__)
+#define CAN_BACKTRACE_EXECINFO
+#endif
+
+#if defined(Q_OS_MAC)
+#define CAN_BACKTRACE_EXECINFO
+#endif
+
+#if defined(QT_NO_DEBUG)
+#undef CAN_BACKTRACE_EXECINFO
+#endif
+
+#if defined(CAN_BACKTRACE_EXECINFO)
+#include <execinfo.h>
+#include <QHash>
+#endif
+
+#ifndef QT_NO_DEBUG
+static const bool qsg_leak_check = !qEnvironmentVariableIsEmpty("QML_LEAK_CHECK");
+#endif
+
+QT_BEGIN_NAMESPACE
+
+bool operator==(const QSGSamplerDescription &a, const QSGSamplerDescription &b) Q_DECL_NOTHROW
+{
+ return a.filtering == b.filtering
+ && a.mipmapFiltering == b.mipmapFiltering
+ && a.horizontalWrap == b.horizontalWrap
+ && a.verticalWrap == b.verticalWrap
+ && a.anisotropylevel == b.anisotropylevel;
+}
+
+bool operator!=(const QSGSamplerDescription &a, const QSGSamplerDescription &b) Q_DECL_NOTHROW
+{
+ return !(a == b);
+}
+
+uint qHash(const QSGSamplerDescription &s, uint seed) Q_DECL_NOTHROW
+{
+ const int f = s.filtering;
+ const int m = s.mipmapFiltering;
+ const int w = s.horizontalWrap;
+ const int a = s.anisotropylevel;
+ return (((f & 7) << 24) | ((m & 7) << 16) | ((w & 7) << 8) | (a & 7)) ^ seed;
+}
+
+QSGSamplerDescription QSGSamplerDescription::fromTexture(QSGTexture *t)
+{
+ QSGSamplerDescription s;
+ s.filtering = t->filtering();
+ s.mipmapFiltering = t->mipmapFiltering();
+ s.horizontalWrap = t->horizontalWrapMode();
+ s.verticalWrap = t->verticalWrapMode();
+ s.anisotropylevel = t->anisotropyLevel();
+ return s;
+}
+
+#if QT_CONFIG(opengl)
+#ifndef QT_NO_DEBUG
+inline static bool isPowerOfTwo(int x)
+{
+ // Assumption: x >= 1
+ return x == (x & -x);
+}
+#endif
+#endif
+
+QSGTexturePrivate::QSGTexturePrivate()
+ : wrapChanged(false)
+ , filteringChanged(false)
+ , anisotropyChanged(false)
+ , horizontalWrap(QSGTexture::ClampToEdge)
+ , verticalWrap(QSGTexture::ClampToEdge)
+ , mipmapMode(QSGTexture::None)
+ , filterMode(QSGTexture::Nearest)
+ , anisotropyLevel(QSGTexture::AnisotropyNone)
+{
+}
+
+#ifndef QT_NO_DEBUG
+
+static int qt_debug_texture_count = 0;
+
+#if (defined(Q_OS_LINUX) || defined (Q_OS_MAC)) && !defined(Q_OS_ANDROID)
+DEFINE_BOOL_CONFIG_OPTION(qmlDebugLeakBacktrace, QML_DEBUG_LEAK_BACKTRACE)
+
+#define BACKTRACE_SIZE 20
+class SGTextureTraceItem
+{
+public:
+ void *backTrace[BACKTRACE_SIZE];
+ size_t backTraceSize;
+};
+
+static QHash<QSGTexture*, SGTextureTraceItem*> qt_debug_allocated_textures;
+#endif
+
+inline static void qt_debug_print_texture_count()
+{
+ qDebug("Number of leaked textures: %i", qt_debug_texture_count);
+ qt_debug_texture_count = -1;
+
+#if defined(CAN_BACKTRACE_EXECINFO)
+ if (qmlDebugLeakBacktrace()) {
+ while (!qt_debug_allocated_textures.isEmpty()) {
+ QHash<QSGTexture*, SGTextureTraceItem*>::Iterator it = qt_debug_allocated_textures.begin();
+ QSGTexture* texture = it.key();
+ SGTextureTraceItem* item = it.value();
+
+ qt_debug_allocated_textures.erase(it);
+
+ qDebug() << "------";
+ qDebug() << "Leaked" << texture << "backtrace:";
+
+ char** symbols = backtrace_symbols(item->backTrace, item->backTraceSize);
+
+ if (symbols) {
+ for (int i=0; i<(int) item->backTraceSize; i++)
+ qDebug("Backtrace <%02d>: %s", i, symbols[i]);
+ free(symbols);
+ }
+
+ qDebug() << "------";
+
+ delete item;
+ }
+ }
+#endif
+}
+
+inline static void qt_debug_add_texture(QSGTexture* texture)
+{
+#if defined(CAN_BACKTRACE_EXECINFO)
+ if (qmlDebugLeakBacktrace()) {
+ SGTextureTraceItem* item = new SGTextureTraceItem;
+ item->backTraceSize = backtrace(item->backTrace, BACKTRACE_SIZE);
+ qt_debug_allocated_textures.insert(texture, item);
+ }
+#else
+ Q_UNUSED(texture);
+#endif // Q_OS_LINUX
+
+ ++qt_debug_texture_count;
+
+ static bool atexit_registered = false;
+ if (!atexit_registered) {
+ atexit(qt_debug_print_texture_count);
+ atexit_registered = true;
+ }
+}
+
+static void qt_debug_remove_texture(QSGTexture* texture)
+{
+#if defined(CAN_BACKTRACE_EXECINFO)
+ if (qmlDebugLeakBacktrace()) {
+ SGTextureTraceItem* item = qt_debug_allocated_textures.value(texture, 0);
+ if (item) {
+ qt_debug_allocated_textures.remove(texture);
+ delete item;
+ }
+ }
+#else
+ Q_UNUSED(texture)
+#endif
+
+ --qt_debug_texture_count;
+
+ if (qt_debug_texture_count < 0)
+ qDebug("Texture destroyed after qt_debug_print_texture_count() was called.");
+}
+
+#endif // QT_NO_DEBUG
+
+/*!
+ \class QSGTexture
+
+ \inmodule QtQuick
+
+ \brief The QSGTexture class is a baseclass for textures used in
+ the scene graph.
+
+
+ Users can freely implement their own texture classes to support
+ arbitrary input textures, such as YUV video frames or 8 bit alpha
+ masks. The scene graph backend provides a default implementation
+ of normal color textures. As the implementation of these may be
+ hardware specific, they are constructed via the factory
+ function QQuickWindow::createTextureFromImage().
+
+ The texture is a wrapper around an OpenGL texture, which texture
+ id is given by textureId() and which size in pixels is given by
+ textureSize(). hasAlphaChannel() reports if the texture contains
+ opacity values and hasMipmaps() reports if the texture contains
+ mipmap levels.
+
+ To use a texture, call the bind() function. The texture parameters
+ specifying how the texture is bound, can be specified with
+ setMipmapFiltering(), setFiltering(), setHorizontalWrapMode() and
+ setVerticalWrapMode(). The texture will internally try to store
+ these values to minimize the OpenGL state changes when the texture
+ is bound.
+
+ \section1 Texture Atlasses
+
+ Some scene graph backends use texture atlasses, grouping multiple
+ small textures into one large texture. If this is the case, the
+ function isAtlasTexture() will return true. Atlasses are used to
+ aid the rendering algorithm to do better sorting which increases
+ performance. The location of the texture inside the atlas is
+ given with the normalizedTextureSubRect() function.
+
+ If the texture is used in such a way that atlas is not preferable,
+ the function removedFromAtlas() can be used to extract a
+ non-atlassed copy.
+
+ \note All classes with QSG prefix should be used solely on the scene graph's
+ rendering thread. See \l {Scene Graph and Rendering} for more information.
+
+ \sa {Scene Graph - Rendering FBOs}, {Scene Graph - Rendering FBOs in a thread}
+ */
+
+/*!
+ \enum QSGTexture::WrapMode
+
+ Specifies how the texture should treat texture coordinates.
+
+ \value Repeat Only the fractional part of the texture coordinate is
+ used, causing values above 1 and below 0 to repeat.
+
+ \value ClampToEdge Values above 1 are clamped to 1 and values
+ below 0 are clamped to 0.
+
+ \value MirroredRepeat When the texture coordinate is even, only the
+ fractional part is used. When odd, the texture coordinate is set to
+ \c{1 - fractional part}. This value has been introduced in Qt 5.10.
+ */
+
+/*!
+ \enum QSGTexture::Filtering
+
+ Specifies how sampling of texels should filter when texture
+ coordinates are not pixel aligned.
+
+ \value None No filtering should occur. This value is only used
+ together with setMipmapFiltering().
+
+ \value Nearest Sampling returns the nearest texel.
+
+ \value Linear Sampling returns a linear interpolation of the
+ neighboring texels.
+*/
+
+/*!
+ \enum QSGTexture::AnisotropyLevel
+
+ Specifies the anisotropic filtering level to be used when
+ the texture is not screen aligned.
+
+ \value AnisotropyNone No anisotropic filtering.
+
+ \value Anisotropy2x 2x anisotropic filtering.
+
+ \value Anisotropy4x 4x anisotropic filtering.
+
+ \value Anisotropy8x 8x anisotropic filtering.
+
+ \value Anisotropy16x 16x anisotropic filtering.
+
+ \since 5.9
+*/
+
+#ifndef QT_NO_DEBUG
+Q_QUICK_PRIVATE_EXPORT void qsg_set_material_failure();
+#endif
+
+#ifndef QT_NO_DEBUG
+Q_GLOBAL_STATIC(QSet<QSGTexture *>, qsg_valid_texture_set)
+Q_GLOBAL_STATIC(QMutex, qsg_valid_texture_mutex)
+
+bool qsg_safeguard_texture(QSGTexture *texture)
+{
+#if QT_CONFIG(opengl)
+ QMutexLocker locker(qsg_valid_texture_mutex());
+ if (!qsg_valid_texture_set()->contains(texture)) {
+ qWarning() << "Invalid texture accessed:" << (void *) texture;
+ qsg_set_material_failure();
+ QOpenGLContext::currentContext()->functions()->glBindTexture(GL_TEXTURE_2D, 0);
+ return false;
+ }
+#else
+ Q_UNUSED(texture)
+#endif
+ return true;
+}
+#endif
+
+/*!
+ Constructs the QSGTexture base class.
+ */
+QSGTexture::QSGTexture()
+ : QObject(*(new QSGTexturePrivate))
+{
+#ifndef QT_NO_DEBUG
+ if (qsg_leak_check)
+ qt_debug_add_texture(this);
+
+ QMutexLocker locker(qsg_valid_texture_mutex());
+ qsg_valid_texture_set()->insert(this);
+#endif
+}
+
+/*!
+ \internal
+ */
+QSGTexture::QSGTexture(QSGTexturePrivate &dd)
+ : QObject(dd)
+{
+#ifndef QT_NO_DEBUG
+ if (qsg_leak_check)
+ qt_debug_add_texture(this);
+
+ QMutexLocker locker(qsg_valid_texture_mutex());
+ qsg_valid_texture_set()->insert(this);
+#endif
+}
+
+/*!
+ Destroys the QSGTexture.
+ */
+QSGTexture::~QSGTexture()
+{
+#ifndef QT_NO_DEBUG
+ if (qsg_leak_check)
+ qt_debug_remove_texture(this);
+
+ QMutexLocker locker(qsg_valid_texture_mutex());
+ qsg_valid_texture_set()->remove(this);
+#endif
+}
+
+/*!
+ \fn void QSGTexture::bind()
+
+ Call this function to bind this texture to the current texture
+ target.
+
+ Binding a texture may also include uploading the texture data from
+ a previously set QImage.
+
+ \warning This function can only be called from the rendering thread.
+ */
+
+/*!
+ \fn QRectF QSGTexture::convertToNormalizedSourceRect(const QRectF &rect) const
+
+ Returns \a rect converted to normalized coordinates.
+
+ \sa normalizedTextureSubRect()
+ */
+
+/*!
+ This function returns a copy of the current texture which is removed
+ from its atlas.
+
+ The current texture remains unchanged, so texture coordinates do not
+ need to be updated.
+
+ Removing a texture from an atlas is primarily useful when passing
+ it to a shader that operates on the texture coordinates 0-1 instead
+ of the texture subrect inside the atlas.
+
+ If the texture is not part of a texture atlas, this function returns 0.
+
+ Implementations of this function are recommended to return the same instance
+ for multiple calls to limit memory usage.
+
+ \warning This function can only be called from the rendering thread.
+ */
+
+QSGTexture *QSGTexture::removedFromAtlas() const
+{
+ Q_ASSERT_X(!isAtlasTexture(), "QSGTexture::removedFromAtlas()", "Called on a non-atlas texture");
+ return nullptr;
+}
+
+/*!
+ Returns weither this texture is part of an atlas or not.
+
+ The default implementation returns false.
+ */
+bool QSGTexture::isAtlasTexture() const
+{
+ return false;
+}
+
+/*!
+ \fn int QSGTexture::textureId() const
+
+ Returns the OpenGL texture id for this texture.
+
+ The default value is 0, indicating that it is an invalid texture id.
+
+ The function should at all times return the correct texture id.
+
+ \warning This function can only be called from the rendering thread.
+ */
+
+/*!
+ Returns a key suitable for comparing textures. Typically used in
+ QSGMaterial::compare() implementations.
+
+ Just comparing QSGTexture pointers is not always sufficient because two
+ QSGTexture instances that refer to the same native texture object
+ underneath should also be considered equal. Hence this function.
+
+ \note Unlike textureId(), implementations of this function are not expected
+ to and should not create any graphics resources (so texture objects) in
+ case there is none yet.
+
+ A QSGTexture that does not have a native texture object underneath is
+ typically not equal to any other QSGTexture. There are exceptions to this,
+ in particular when atlasing is used (where multiple textures share the same
+ atlas texture under the hood), that is then up to the subclass
+ implementations to deal with as appropriate.
+
+ \warning This function can only be called from the rendering thread.
+
+ \since 5.14
+ */
+int QSGTexture::comparisonKey() const
+{
+ Q_D(const QSGTexture);
+ return d->comparisonKey();
+}
+
+/*!
+ \fn QSize QSGTexture::textureSize() const
+
+ Returns the size of the texture.
+ */
+
+/*!
+ Returns the rectangle inside textureSize() that this texture
+ represents in normalized coordinates.
+
+ The default implementation returns a rect at position (0, 0) with
+ width and height of 1.
+ */
+QRectF QSGTexture::normalizedTextureSubRect() const
+{
+ return QRectF(0, 0, 1, 1);
+}
+
+/*!
+ \fn bool QSGTexture::hasAlphaChannel() const
+
+ Returns true if the texture data contains an alpha channel.
+ */
+
+/*!
+ \fn bool QSGTexture::hasMipmaps() const
+
+ Returns true if the texture data contains mipmap levels.
+ */
+
+
+/*!
+ Sets the mipmap sampling mode to be used for the upcoming bind() call to \a filter.
+
+ Setting the mipmap filtering has no effect it the texture does not have mipmaps.
+
+ \sa hasMipmaps()
+ */
+void QSGTexture::setMipmapFiltering(Filtering filter)
+{
+ Q_D(QSGTexture);
+ if (d->mipmapMode != (uint) filter) {
+ d->mipmapMode = filter;
+ d->filteringChanged = true;
+ }
+}
+
+/*!
+ Returns whether mipmapping should be used when sampling from this texture.
+ */
+QSGTexture::Filtering QSGTexture::mipmapFiltering() const
+{
+ return (QSGTexture::Filtering) d_func()->mipmapMode;
+}
+
+
+/*!
+ Sets the sampling mode to be used for the upcoming bind() call to \a filter.
+ */
+void QSGTexture::setFiltering(QSGTexture::Filtering filter)
+{
+ Q_D(QSGTexture);
+ if (d->filterMode != (uint) filter) {
+ d->filterMode = filter;
+ d->filteringChanged = true;
+ }
+}
+
+/*!
+ Returns the sampling mode to be used for this texture.
+ */
+QSGTexture::Filtering QSGTexture::filtering() const
+{
+ return (QSGTexture::Filtering) d_func()->filterMode;
+}
+
+/*!
+ Sets the level of anisotropic filtering to be used for the upcoming bind() call to \a level.
+ The default value is QSGTexture::AnisotropyNone, which means no anisotropic filtering is enabled.
+
+ \since 5.9
+ */
+void QSGTexture::setAnisotropyLevel(AnisotropyLevel level)
+{
+ Q_D(QSGTexture);
+ if (d->anisotropyLevel != (uint) level) {
+ d->anisotropyLevel = level;
+ d->anisotropyChanged = true;
+ }
+}
+
+/*!
+ Returns the anisotropy level in use for filtering this texture.
+
+ \since 5.9
+ */
+QSGTexture::AnisotropyLevel QSGTexture::anisotropyLevel() const
+{
+ return (QSGTexture::AnisotropyLevel) d_func()->anisotropyLevel;
+}
+
+
+
+/*!
+ Sets the horizontal wrap mode to be used for the upcoming bind() call to \a hwrap
+ */
+
+void QSGTexture::setHorizontalWrapMode(WrapMode hwrap)
+{
+ Q_D(QSGTexture);
+ if ((uint) hwrap != d->horizontalWrap) {
+ d->horizontalWrap = hwrap;
+ d->wrapChanged = true;
+ }
+}
+
+/*!
+ Returns the horizontal wrap mode to be used for this texture.
+ */
+QSGTexture::WrapMode QSGTexture::horizontalWrapMode() const
+{
+ return (QSGTexture::WrapMode) d_func()->horizontalWrap;
+}
+
+
+
+/*!
+ Sets the vertical wrap mode to be used for the upcoming bind() call to \a vwrap
+ */
+void QSGTexture::setVerticalWrapMode(WrapMode vwrap)
+{
+ Q_D(QSGTexture);
+ if ((uint) vwrap != d->verticalWrap) {
+ d->verticalWrap = vwrap;
+ d->wrapChanged = true;
+ }
+}
+
+/*!
+ Returns the vertical wrap mode to be used for this texture.
+ */
+QSGTexture::WrapMode QSGTexture::verticalWrapMode() const
+{
+ return (QSGTexture::WrapMode) d_func()->verticalWrap;
+}
+
+
+/*!
+ Update the texture state to match the filtering, mipmap and wrap options
+ currently set.
+
+ If \a force is true, all properties will be updated regardless of weither
+ they have changed or not.
+ */
+void QSGTexture::updateBindOptions(bool force) // legacy (GL-only)
+{
+#if QT_CONFIG(opengl)
+ Q_D(QSGTexture);
+ QOpenGLFunctions *funcs = QOpenGLContext::currentContext()->functions();
+ force |= isAtlasTexture();
+
+ if (force || d->filteringChanged) {
+ bool linear = d->filterMode == Linear;
+ GLint minFilter = linear ? GL_LINEAR : GL_NEAREST;
+ GLint magFilter = linear ? GL_LINEAR : GL_NEAREST;
+
+ if (hasMipmaps()) {
+ if (d->mipmapMode == Nearest)
+ minFilter = linear ? GL_LINEAR_MIPMAP_NEAREST : GL_NEAREST_MIPMAP_NEAREST;
+ else if (d->mipmapMode == Linear)
+ minFilter = linear ? GL_LINEAR_MIPMAP_LINEAR : GL_NEAREST_MIPMAP_LINEAR;
+ }
+ funcs->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, minFilter);
+ funcs->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, magFilter);
+ d->filteringChanged = false;
+ }
+
+ if (force || d->anisotropyChanged) {
+ d->anisotropyChanged = false;
+ if (QOpenGLContext::currentContext()->hasExtension(QByteArrayLiteral("GL_EXT_texture_filter_anisotropic")))
+ funcs->glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT, float(1 << (d->anisotropyLevel)));
+ }
+
+ if (force || d->wrapChanged) {
+#ifndef QT_NO_DEBUG
+ if (d->horizontalWrap == Repeat || d->verticalWrap == Repeat
+ || d->horizontalWrap == MirroredRepeat || d->verticalWrap == MirroredRepeat)
+ {
+ bool npotSupported = QOpenGLFunctions(QOpenGLContext::currentContext()).hasOpenGLFeature(QOpenGLFunctions::NPOTTextures);
+ QSize size = textureSize();
+ bool isNpot = !isPowerOfTwo(size.width()) || !isPowerOfTwo(size.height());
+ if (!npotSupported && isNpot)
+ qWarning("Scene Graph: This system does not support the REPEAT wrap mode for non-power-of-two textures.");
+ }
+#endif
+ GLenum wrapS = GL_CLAMP_TO_EDGE;
+ if (d->horizontalWrap == Repeat)
+ wrapS = GL_REPEAT;
+ else if (d->horizontalWrap == MirroredRepeat)
+ wrapS = GL_MIRRORED_REPEAT;
+ GLenum wrapT = GL_CLAMP_TO_EDGE;
+ if (d->verticalWrap == Repeat)
+ wrapT = GL_REPEAT;
+ else if (d->verticalWrap == MirroredRepeat)
+ wrapT = GL_MIRRORED_REPEAT;
+ funcs->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, wrapS);
+ funcs->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, wrapT);
+ d->wrapChanged = false;
+ }
+#else
+ Q_UNUSED(force)
+#endif
+}
+
+/*!
+ \return the QRhiTexture for this QSGTexture or null if there is none.
+
+ Unlike textureId(), this function is not expected to create a new
+ QRhiTexture in case there is none. Just return null in that case. The
+ expectation towards the renderer is that a null texture leads to using a
+ transparent, dummy texture instead.
+
+ \note This function is only used when running the graphics API independent
+ rendering path of the scene graph.
+
+ \warning This function can only be called from the rendering thread.
+
+ \since 5.14
+ */
+QRhiTexture *QSGTexture::rhiTexture() const
+{
+ Q_D(const QSGTexture);
+ return d->rhiTexture();
+}
+
+/*!
+ Call this function to enqueue image upload operations to \a
+ resourceUpdates, in case there are any pending ones. When there is no new
+ data (for example, because there was no setImage() since the last call to
+ this function), the function does nothing.
+
+ Materials involving textures are expected to call this function from their
+ updateSampledImage() implementation, typically without any conditions.
+
+ \note This function is only used when running the graphics API independent
+ rendering path of the scene graph.
+
+ \warning This function can only be called from the rendering thread.
+
+ \since 5.14
+ */
+void QSGTexture::updateRhiTexture(QRhi *rhi, QRhiResourceUpdateBatch *resourceUpdates)
+{
+ Q_D(QSGTexture);
+ d->updateRhiTexture(rhi, resourceUpdates);
+}
+
+void QSGTexture::setWorkResourceUpdateBatch(QRhiResourceUpdateBatch *resourceUpdates)
+{
+ Q_D(QSGTexture);
+ d->workResourceUpdateBatch = resourceUpdates;
+}
+
+bool QSGTexturePrivate::hasDirtySamplerOptions() const
+{
+ return wrapChanged || filteringChanged || anisotropyChanged;
+}
+
+void QSGTexturePrivate::resetDirtySamplerOptions()
+{
+ wrapChanged = filteringChanged = anisotropyChanged = false;
+}
+
+int QSGTexturePrivate::comparisonKey() const
+{
+ // Must be overridden in subclasses but we cannot make this pure virtual
+ // before Qt 6 because the simple QSGTexture ctor must be kept working.
+ Q_Q(const QSGTexture);
+ return q->textureId(); // this is semantically wrong but at least compatible with existing, non-RHI-aware subclasses
+}
+
+QRhiTexture *QSGTexturePrivate::rhiTexture() const
+{
+ return nullptr;
+}
+
+void QSGTexturePrivate::updateRhiTexture(QRhi *rhi, QRhiResourceUpdateBatch *resourceUpdates)
+{
+ Q_UNUSED(rhi);
+ Q_UNUSED(resourceUpdates);
+}
+
+/*!
+ \class QSGDynamicTexture
+ \brief The QSGDynamicTexture class serves as a baseclass for dynamically changing textures,
+ such as content that is rendered to FBO's.
+ \inmodule QtQuick
+
+ To update the content of the texture, call updateTexture() explicitly. Simply calling bind()
+ will not update the texture.
+
+ \note All classes with QSG prefix should be used solely on the scene graph's
+ rendering thread. See \l {Scene Graph and Rendering} for more information.
+ */
+
+
+/*!
+ \fn bool QSGDynamicTexture::updateTexture()
+
+ Call this function to explicitly update the dynamic texture. Calling bind() will bind
+ the content that was previously updated.
+
+ The function returns true if the texture was changed as a resul of the update; otherwise
+ returns false.
+ */
+
+/*!
+ \internal
+ */
+QSGDynamicTexture::QSGDynamicTexture(QSGTexturePrivate &dd)
+ : QSGTexture(dd)
+{
+}
+
+QT_END_NAMESPACE
+
+#include "moc_qsgtexture.cpp"
diff --git a/src/quick/scenegraph/coreapi/qsgtexture.h b/src/quick/scenegraph/coreapi/qsgtexture.h
new file mode 100644
index 0000000000..f2b0e902f3
--- /dev/null
+++ b/src/quick/scenegraph/coreapi/qsgtexture.h
@@ -0,0 +1,153 @@
+/****************************************************************************
+**
+** Copyright (C) 2019 The Qt Company Ltd.
+** 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 QSGTEXTURE_H
+#define QSGTEXTURE_H
+
+#include <QtQuick/qtquickglobal.h>
+#include <QtCore/QObject>
+#include <QtGui/QImage>
+
+QT_BEGIN_NAMESPACE
+
+class QSGTexturePrivate;
+class QRhi;
+class QRhiTexture;
+class QRhiResourceUpdateBatch;
+
+class Q_QUICK_EXPORT QSGTexture : public QObject
+{
+ Q_OBJECT
+ Q_DECLARE_PRIVATE(QSGTexture)
+
+public:
+ QSGTexture();
+ ~QSGTexture() override;
+
+ enum WrapMode {
+ Repeat,
+ ClampToEdge,
+ MirroredRepeat
+ };
+
+ enum Filtering {
+ None,
+ Nearest,
+ Linear
+ };
+
+ enum AnisotropyLevel {
+ AnisotropyNone,
+ Anisotropy2x,
+ Anisotropy4x,
+ Anisotropy8x,
+ Anisotropy16x
+ };
+
+ virtual int textureId() const = 0; // ### Qt 6: remove
+ virtual QSize textureSize() const = 0;
+ virtual bool hasAlphaChannel() const = 0;
+ virtual bool hasMipmaps() const = 0;
+
+ virtual QRectF normalizedTextureSubRect() const;
+
+ virtual bool isAtlasTexture() const;
+
+ virtual QSGTexture *removedFromAtlas() const;
+
+ virtual void bind() = 0;
+ void updateBindOptions(bool force = false);
+
+ void setMipmapFiltering(Filtering filter);
+ QSGTexture::Filtering mipmapFiltering() const;
+
+ void setFiltering(Filtering filter);
+ QSGTexture::Filtering filtering() const;
+
+ void setAnisotropyLevel(AnisotropyLevel level);
+ QSGTexture::AnisotropyLevel anisotropyLevel() const;
+
+ void setHorizontalWrapMode(WrapMode hwrap);
+ QSGTexture::WrapMode horizontalWrapMode() const;
+
+ void setVerticalWrapMode(WrapMode vwrap);
+ QSGTexture::WrapMode verticalWrapMode() const;
+
+ inline QRectF convertToNormalizedSourceRect(const QRectF &rect) const;
+
+ // ### Qt 6: make these virtual
+ int comparisonKey() const;
+ QRhiTexture *rhiTexture() const;
+ void updateRhiTexture(QRhi *rhi, QRhiResourceUpdateBatch *resourceUpdates);
+
+ // ### Qt 6: make this an argument for removedFromAtlas()
+ void setWorkResourceUpdateBatch(QRhiResourceUpdateBatch *resourceUpdates);
+
+protected:
+ QSGTexture(QSGTexturePrivate &dd);
+};
+
+QRectF QSGTexture::convertToNormalizedSourceRect(const QRectF &rect) const
+{
+ QSize s = textureSize();
+ QRectF r = normalizedTextureSubRect();
+
+ qreal sx = r.width() / s.width();
+ qreal sy = r.height() / s.height();
+
+ return QRectF(r.x() + rect.x() * sx,
+ r.y() + rect.y() * sy,
+ rect.width() * sx,
+ rect.height() * sy);
+}
+
+class Q_QUICK_EXPORT QSGDynamicTexture : public QSGTexture
+{
+ Q_OBJECT
+
+public:
+ virtual bool updateTexture() = 0;
+
+protected:
+ QSGDynamicTexture(QSGTexturePrivate &dd);
+};
+
+QT_END_NAMESPACE
+
+#endif
diff --git a/src/quick/scenegraph/coreapi/qsgtexture_p.h b/src/quick/scenegraph/coreapi/qsgtexture_p.h
new file mode 100644
index 0000000000..1d248b0305
--- /dev/null
+++ b/src/quick/scenegraph/coreapi/qsgtexture_p.h
@@ -0,0 +1,108 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** 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 QSGTEXTURE_P_H
+#define QSGTEXTURE_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 <QtQuick/private/qtquickglobal_p.h>
+#include <private/qobject_p.h>
+#include "qsgtexture.h"
+
+QT_BEGIN_NAMESPACE
+
+struct QSGSamplerDescription
+{
+ QSGTexture::Filtering filtering = QSGTexture::Nearest;
+ QSGTexture::Filtering mipmapFiltering = QSGTexture::None;
+ QSGTexture::WrapMode horizontalWrap = QSGTexture::ClampToEdge;
+ QSGTexture::WrapMode verticalWrap = QSGTexture::ClampToEdge;
+ QSGTexture::AnisotropyLevel anisotropylevel = QSGTexture::AnisotropyNone;
+
+ static QSGSamplerDescription fromTexture(QSGTexture *t);
+};
+
+Q_DECLARE_TYPEINFO(QSGSamplerDescription, Q_MOVABLE_TYPE);
+
+bool operator==(const QSGSamplerDescription &a, const QSGSamplerDescription &b) Q_DECL_NOTHROW;
+bool operator!=(const QSGSamplerDescription &a, const QSGSamplerDescription &b) Q_DECL_NOTHROW;
+uint qHash(const QSGSamplerDescription &s, uint seed = 0) Q_DECL_NOTHROW;
+
+class Q_QUICK_PRIVATE_EXPORT QSGTexturePrivate : public QObjectPrivate
+{
+ Q_DECLARE_PUBLIC(QSGTexture)
+public:
+ QSGTexturePrivate();
+ static QSGTexturePrivate *get(QSGTexture *t) { return t->d_func(); }
+ void resetDirtySamplerOptions();
+ bool hasDirtySamplerOptions() const;
+
+ // ### Qt 6: these should be virtuals in the public class instead
+ virtual int comparisonKey() const; // ### Qt 6: pure virtual
+ virtual QRhiTexture *rhiTexture() const;
+ virtual void updateRhiTexture(QRhi *rhi, QRhiResourceUpdateBatch *resourceUpdates);
+
+ QRhiResourceUpdateBatch *workResourceUpdateBatch = nullptr; // ### Qt 6: remove
+
+ uint wrapChanged : 1;
+ uint filteringChanged : 1;
+ uint anisotropyChanged : 1;
+
+ uint horizontalWrap : 2;
+ uint verticalWrap : 2;
+ uint mipmapMode : 2;
+ uint filterMode : 2;
+ uint anisotropyLevel: 3;
+};
+
+Q_QUICK_PRIVATE_EXPORT bool qsg_safeguard_texture(QSGTexture *);
+
+QT_END_NAMESPACE
+
+#endif // QSGTEXTURE_P_H