diff options
Diffstat (limited to 'src/render/materialsystem')
-rw-r--r-- | src/render/materialsystem/qeffect.cpp | 60 | ||||
-rw-r--r-- | src/render/materialsystem/qmaterial.cpp | 102 | ||||
-rw-r--r-- | src/render/materialsystem/qparameter.cpp | 127 | ||||
-rw-r--r-- | src/render/materialsystem/qrenderpass.cpp | 91 | ||||
-rw-r--r-- | src/render/materialsystem/qshaderdata_p.h | 7 | ||||
-rw-r--r-- | src/render/materialsystem/qshaderprogram.cpp | 79 | ||||
-rw-r--r-- | src/render/materialsystem/qshaderprogram.h | 15 | ||||
-rw-r--r-- | src/render/materialsystem/qshaderprogram_p.h | 8 | ||||
-rw-r--r-- | src/render/materialsystem/qtechnique.cpp | 128 | ||||
-rw-r--r-- | src/render/materialsystem/shader.cpp | 63 | ||||
-rw-r--r-- | src/render/materialsystem/shader_p.h | 18 | ||||
-rw-r--r-- | src/render/materialsystem/shadercache.cpp | 5 | ||||
-rw-r--r-- | src/render/materialsystem/shadercache_p.h | 1 |
13 files changed, 647 insertions, 57 deletions
diff --git a/src/render/materialsystem/qeffect.cpp b/src/render/materialsystem/qeffect.cpp index 7778b5621..b611657c4 100644 --- a/src/render/materialsystem/qeffect.cpp +++ b/src/render/materialsystem/qeffect.cpp @@ -67,6 +67,35 @@ QEffectPrivate::QEffectPrivate() The QEffect class combines a set of techniques and parameters used by those techniques to produce a rendering effect for a material. + An QEffect instance should be shared among several QMaterial instances when possible. + + \code + QEffect *effect = new QEffect(); + + // Create technique, render pass and shader + QTechnique *gl3Technique = new QTechnique(); + QRenderPass *gl3Pass = new QRenderPass(); + QShaderProgram *glShader = new QShaderProgram(); + + // Set the shader on the render pass + gl3Pass->setShaderProgram(glShader); + + // Add the pass to the technique + gl3Technique->addRenderPass(gl3Pass); + + // Set the targeted GL version for the technique + gl3Technique->graphicsApiFilter()->setApi(QGraphicsApiFilter::OpenGL); + gl3Technique->graphicsApiFilter()->setMajorVersion(3); + gl3Technique->graphicsApiFilter()->setMinorVersion(1); + gl3Technique->graphicsApiFilter()->setProfile(QGraphicsApiFilter::CoreProfile); + + // Add the technique to the effect + effect->addTechnique(gl3Technique); + \endcode + + A QParameter defined on an Effect is overridden by a QParameter (of the same + name) defined in a QMaterial, QTechniqueFilter, QRenderPassFilter. + \sa QMaterial, QTechnique, QParameter */ @@ -81,6 +110,37 @@ QEffectPrivate::QEffectPrivate() The Effect type combines a set of techniques and parameters used by those techniques to produce a rendering effect for a material. + An Effect instance should be shared among several Material instances when possible. + + A Parameter defined on an Effect is overridden by a QParameter (of the same + name) defined in a Material, TechniqueFilter, RenderPassFilter. + + \code + Effect { + id: effect + + technique: [ + Technique { + id: gl3Technique + graphicsApiFilter { + api: GraphicsApiFilter.OpenGL + profile: GraphicsApiFilter.CoreProfile + majorVersion: 3 + minorVersion: 1 + } + renderPasses: [ + RenderPass { + id: gl3Pass + shaderProgram: ShaderProgram { + ... + } + } + ] + } + ] + } + \endcode + \sa Material, Technique, Parameter */ diff --git a/src/render/materialsystem/qmaterial.cpp b/src/render/materialsystem/qmaterial.cpp index ca0f86463..c6913441e 100644 --- a/src/render/materialsystem/qmaterial.cpp +++ b/src/render/materialsystem/qmaterial.cpp @@ -60,7 +60,58 @@ sound should reflect off an element, the temperature of a surface, and so on. - \sa Effect + In itself, a Material doesn't do anything. It's only when it references an + Effect node that a Material becomes useful. + + In practice, it often happens that a single Effect is being referenced by + several Material components. This allows to only create the effect, + techniques, passes and shaders once while allowing to specify the material + by adding Parameter instances. + + A Parameter defined on a Material is overridden by a Parameter (of the same + name) defined in a TechniqueFilter or a RenderPassFilter. + + \code + Effect { + id: effect + + technique: [ + Technique { + id: gl3Technique + graphicsApiFilter { + api: GraphicsApiFilter.OpenGL + profile: GraphicsApiFilter.CoreProfile + majorVersion: 3 + minorVersion: 1 + } + renderPasses: [ + RenderPass { + id: gl3Pass + shaderProgram: ShaderProgram { + ... + } + } + ] + } + ] + } + + Material { + id: material1 + parameters: [ + Parameter { name: "color"; value: "green" } + ] + } + + Material { + id: material2 + parameters: [ + Parameter { name: "color"; value: "white" } + ] + } + \endcode + + \sa Effect, Technique, Parameter */ /*! @@ -77,7 +128,54 @@ sound should reflect off an element, the temperature of a surface, and so on. - \sa QEffect + In itself, a QMaterial doesn't do anything. It's only when it references a + QEffect node that a QMaterial becomes useful. + + In practice, it often happens that a single QEffect is being referenced by + several QMaterial components. This allows to only create the effect, + techniques, passes and shaders once while allowing to specify the material + by adding QParameter instances. + + A QParameter defined on a QMaterial is overridden by a QParameter (of the same + name) defined in a QTechniqueFilter or a QRenderPassFilter. + + \code + QMaterial *material1 = new QMaterial(); + QMaterial *material2 = new QMaterial(); + + // Create effect, technique, render pass and shader + QEffect *effect = new QEffect(); + QTechnique *gl3Technique = new QTechnique(); + QRenderPass *gl3Pass = new QRenderPass(); + QShaderProgram *glShader = new QShaderProgram(); + + // Set the shader on the render pass + gl3Pass->setShaderProgram(glShader); + + // Add the pass to the technique + gl3Technique->addRenderPass(gl3Pass); + + // Set the targeted GL version for the technique + gl3Technique->graphicsApiFilter()->setApi(QGraphicsApiFilter::OpenGL); + gl3Technique->graphicsApiFilter()->setMajorVersion(3); + gl3Technique->graphicsApiFilter()->setMinorVersion(1); + gl3Technique->graphicsApiFilter()->setProfile(QGraphicsApiFilter::CoreProfile); + + // Add the technique to the effect + effect->addTechnique(gl3Technique); + + // Set the effect on the materials + material1->setEffect(effect); + material2->setEffect(effect); + + // Set different parameters on the materials + const QString parameterName = QStringLiteral("color"); + material1->addParameter(new QParameter(parameterName, QColor::fromRgbF(0.0f, 1.0f, 0.0f, 1.0f); + material2->addParameter(new QParameter(parameterName, QColor::fromRgbF(1.0f, 1.0f, 1.0f, 1.0f); + + \endcode + + \sa QEffect, QTechnique, QParameter */ QT_BEGIN_NAMESPACE diff --git a/src/render/materialsystem/qparameter.cpp b/src/render/materialsystem/qparameter.cpp index 8f83fd02e..2ca7d176b 100644 --- a/src/render/materialsystem/qparameter.cpp +++ b/src/render/materialsystem/qparameter.cpp @@ -45,18 +45,127 @@ /*! - * \qmltype Parameter - * \instantiates Qt3DRender::QParameter - * \inqmlmodule Qt3D.Render - * \brief Provides storage for a name and value pair. + \qmltype Parameter + \instantiates Qt3DRender::QParameter + \inqmlmodule Qt3D.Render + \brief Provides storage for a name and value pair. This maps to a shader uniform. + + A Parameter can be referenced by a RenderPass, Technique, Effect, Material, + TechniqueFilter, RenderPassFilter. At runtime, depending on which shader is + selected for a given step of the rendering, the value contained in a + Parameter will be converted and uploaded if the shader contains a uniform + with a name matching that of the Parameter. + + \code + Parameter { + name: "diffuseColor" + value: "blue" + } + + // Works with the following GLSL uniform shader declarations + // uniform vec4 diffuseColor; + // uniform vec3 diffuseColor; + // uniform vec2 diffuseColor; + // uniform float diffuseColor; + \endcode + + \note some care must be taken to ensure the value wrapped by a Parameter + can actually be converted to what the real uniform expect. Giving a value + stored as an int where the actual shader uniform is of type float could + result in undefined behaviors. + + \note when the targeted uniform is an array, the name should be the name + of the uniform with [0] appended to it. + + \code + Parameter { + name: "diffuseValues[0]" + value: [0.0, 1.0. 2.0, 3.0, 4.0, 883.0, 1340.0, 1584.0] + } + + // Matching GLSL shader uniform declaration + // uniform float diffuseValues[8]; + \endcode + + When it comes to texture support, the Parameter value should be set to the + appropriate Texture subclass that matches the sampler type of the shader + uniform. + + \code + Parameter { + name: "diffuseTexture" + value: Texture2D { ... } + } + + // Works with the following GLSL uniform shader declaration + // uniform sampler2D diffuseTexture + \endcode + + \sa Texture */ /*! - * \class Qt3DRender::QParameter - * \inheaderfile Qt3DRender/QParameter - * \inmodule Qt3DRender - * - * \brief Provides storage for a name and value pair. + \class Qt3DRender::QParameter + \inheaderfile Qt3DRender/QParameter + \inmodule Qt3DRender + \brief Provides storage for a name and value pair. This maps to a shader uniform. + + A QParameter can be referenced by a QRenderPass, QTechnique, QEffect, QMaterial, + QTechniqueFilter, QRenderPassFilter. At runtime, depending on which shader is + selected for a given step of the rendering, the value contained in a + QParameter will be converted and uploaded if the shader contains a uniform + with a name matching that of the QParameter. + + \code + QParameter *param = new QParameter(); + param->setName(QStringLiteral("diffuseColor")); + param->setValue(QColor::fromRgbF(0.0f, 0.0f, 1.0f, 1.0f)); + + // Alternatively you can create and set a QParameter this way + QParameter *param2 = new QParameter(QStringLiteral("diffuseColor"), QColor::fromRgbF(0.0f, 0.0f, 1.0f, 1.0f)); + + // Such QParameters will work with the following GLSL uniform shader declarations + // uniform vec4 diffuseColor; + // uniform vec3 diffuseColor; + // uniform vec2 diffuseColor; + // uniform float diffuseColor; + \endcode + + \note some care must be taken to ensure the value wrapped by a QParameter + can actually be converted to what the real uniform expect. Giving a value + stored as an int where the actual shader uniform is of type float could + result in undefined behaviors. + + \note when the targeted uniform is an array, the name should be the name + of the uniform with [0] appended to it. + + \code + QParameter *param = new QParameter(); + QVariantList values = QVariantList() << 0.0f << 1.0f << 2.0f << 3.0f << 4.0f << 883.0f << 1340.0f << 1584.0f; + + param->setName(QStringLiteral("diffuseValues[0]")); + param->setValue(values); + + // Matching GLSL shader uniform declaration + // uniform float diffuseValues[8]; + \endcode + + When it comes to texture support, the QParameter value should be set to the + appropriate QAbstractTexture subclass that matches the sampler type of the shader + uniform. + + \code + QTexture2D *texture = new QTexture2D(); + ... + QParameter *param = new QParameter(); + param->setName(QStringLiteral("diffuseTexture")); + param->setValue(QVariant::fromValue(texture)); + + // Works with the following GLSL uniform shader declaration + // uniform sampler2D diffuseTexture + \endcode + + \sa QAbstractTexture */ QT_BEGIN_NAMESPACE diff --git a/src/render/materialsystem/qrenderpass.cpp b/src/render/materialsystem/qrenderpass.cpp index dd6363816..61f844be3 100644 --- a/src/render/materialsystem/qrenderpass.cpp +++ b/src/render/materialsystem/qrenderpass.cpp @@ -72,9 +72,40 @@ QRenderPassPrivate::QRenderPassPrivate() a list of FilterKey objects, a list of RenderState objects and a list of \l Parameter objects. - RenderPass executes the ShaderProgram using the given render states and parameters - when its filter keys match the filter keys in RenderPassFilter or when no filter - keys are specified and no RenderPassFilter is present in the FrameGraph. + RenderPass executes the ShaderProgram using the given RenderState and + Parameter nodes when at least one of FilterKey nodes being referenced + matches any of the FilterKey nodes in RenderPassFilter or when no FilterKey + nodes are specified and no RenderPassFilter is present in the FrameGraph. + + If the RenderPass defines a Parameter, it will be overridden by a Parameter + with the same name if it exists in any of the Technique, Effect, Material, + TechniqueFilter, RenderPassFilter associated with the pass at runtime. This + still can be useful to define sane default values. + + At render time, for each leaf node of the FrameGraph a base render state is + recorded by accumulating states defined by all RenderStateSet nodes in the + FrameGraph branch. Each RenderPass can overload this base render state by + specifying its own RenderState nodes. + + \code + RenderPass { + id: pass + shaderProgram: ShaderProgram { + ... + } + parameters: [ + Parameters { name: "color"; value: "red" } + ] + filterKeys: [ + FilterKey { name: "name"; value: "zFillPass" } + ] + renderStates: [ + DepthTest { } + ] + } + \endcode + + \sa RenderPassFilter, FilterKey, Parameter, RenderState, Effect, Technique */ /*! @@ -88,10 +119,54 @@ QRenderPassPrivate::QRenderPassPrivate() of a Qt3DRender::QShaderProgram and a list of Qt3DRender::QFilterKey objects, a list of Qt3DRender::QRenderState objects and a list of Qt3DRender::QParameter objects. - QRenderPass executes the QShaderProgram using the given render states and parameters - when its filter keys match the filter keys in Qt3DRender::QRenderPassFilter or - when no filter keys are specified and no QRenderPassFilter is present - in the FrameGraph. + QRenderPass executes the QShaderProgram using the given QRenderState and + QParameter nodes when at least one of QFilterKey nodes being referenced + matches any of the QFilterKey nodes in QRenderPassFilter or when no + QFilterKey nodes are specified and no QRenderPassFilter is present in the + FrameGraph. + + If the QRenderPass defines a QParameter, it will be overridden by a + QParameter with the same name if it exists in any of the QTechnique, + QEffect, QMaterial, QTechniqueFilter, QRenderPassFilter associated with the + pass at runtime. This still can be useful to define sane default values. + + At render time, for each leaf node of the FrameGraph a base render state is + recorded by accumulating states defined by all QRenderStateSet nodes in the + FrameGraph branch. Each QRenderPass can overload this base render state by + specifying its own QRenderState nodes. + + \code + // Create the render passes + QRenderPass *pass = new QRenderPass(); + + // Create shader program + QShaderProgram *glShader = new QShaderProgram(); + + // Set the shader on the render pass + pass->setShaderProgram(glShader); + + // Create a FilterKey + QFilterKey *filterKey = new QFilterKey(); + filterKey->setName(QStringLiteral("name")); + fitlerKey->setValue(QStringLiteral("zFillPass")); + + // Add the FilterKey to the pass + pass->addFilterKey(filterKey); + + // Create a QParameter + QParameter *colorParameter = new QParameter(QStringLiteral("color"), QColor::fromRgbF(0.0f, 0.0f, 1.0f, 1.0f)); + + // Add parameter to pass + pass->addParameter(colorParameter); + + // Create a QRenderState + QDepthTest *depthTest = new QDepthTest(); + + // Add the render state to the pass + pass->addRenderState(depthTest); + \endcode + + \sa QRenderPassFilter, QFilterKey, QParameter, QRenderState, QEffect, QTechnique */ /*! \typedef ParameterList @@ -236,7 +311,7 @@ QVector<QFilterKey *> QRenderPass::filterKeys() const /*! Adds a render \a state to the rendering pass. That implies that when the pass is executed at render time, the globally set render state will - be modifed by the states defined locally by the Qt3DRender::QRenderPass. + be modified by the states defined locally by the Qt3DRender::QRenderPass. \note not defining any Qt3DRender::QRenderState in a pass will result in the pass using the globally set render state for a given FrameGraph branch execution path. diff --git a/src/render/materialsystem/qshaderdata_p.h b/src/render/materialsystem/qshaderdata_p.h index bfa139890..17faee9ed 100644 --- a/src/render/materialsystem/qshaderdata_p.h +++ b/src/render/materialsystem/qshaderdata_p.h @@ -62,7 +62,6 @@ namespace Qt3DRender { namespace { const int qVectorShaderDataTypeId = qMetaTypeId<QVector<QShaderData*> >(); -const int qShaderDataTypeId = qMetaTypeId<QShaderData*>(); } @@ -70,10 +69,10 @@ class QShaderDataPropertyReader: public PropertyReaderInterface { QVariant readProperty(const QVariant &v) Q_DECL_OVERRIDE { - QShaderData *shaderData = nullptr; + const auto node = v.value<Qt3DCore::QNode *>(); - if (v.userType() == qShaderDataTypeId && (shaderData = v.value<QShaderData *>()) != nullptr) { - return QVariant::fromValue(shaderData->id()); + if (node) { + return QVariant::fromValue(node->id()); } else if (v.userType() == qVectorShaderDataTypeId) { QVariantList vlist; const auto data_ = v.value<QVector<QShaderData *> >(); diff --git a/src/render/materialsystem/qshaderprogram.cpp b/src/render/materialsystem/qshaderprogram.cpp index 522f021aa..2a65d257c 100644 --- a/src/render/materialsystem/qshaderprogram.cpp +++ b/src/render/materialsystem/qshaderprogram.cpp @@ -80,15 +80,48 @@ \value Compute Compute shader */ +/*! + \enum QShaderProgram::ShaderStatus + + This enum identifies the status of shader used. + + \value NotReady The shader hasn't been compiled and linked yet + \value Ready The shader was successfully compiled + \value Error An error occurred while compiling the shader +*/ + QT_BEGIN_NAMESPACE namespace Qt3DRender { QShaderProgramPrivate::QShaderProgramPrivate() : QNodePrivate() + , m_status(QShaderProgram::NotReady) { } +void QShaderProgramPrivate::setLog(const QString &log) +{ + Q_Q(QShaderProgram); + if (log != m_log) { + m_log = log; + const bool blocked = q->blockNotifications(true); + emit q->logChanged(m_log); + q->blockNotifications(blocked); + } +} + +void QShaderProgramPrivate::setStatus(QShaderProgram::Status status) +{ + Q_Q(QShaderProgram); + if (status != m_status) { + m_status = status; + const bool blocked = q->blockNotifications(true); + emit q->statusChanged(m_status); + q->blockNotifications(blocked); + } +} + QShaderProgram::QShaderProgram(QNode *parent) : QNode(*new QShaderProgramPrivate, parent) { @@ -104,6 +137,18 @@ QShaderProgram::QShaderProgram(QShaderProgramPrivate &dd, QNode *parent) { } +void QShaderProgram::sceneChangeEvent(const Qt3DCore::QSceneChangePtr &change) +{ + Q_D(QShaderProgram); + if (change->type() == Qt3DCore::PropertyUpdated) { + const Qt3DCore::QPropertyUpdatedChangePtr e = qSharedPointerCast<Qt3DCore::QPropertyUpdatedChange>(change); + if (e->propertyName() == QByteArrayLiteral("log")) + d->setLog(e->value().toString()); + else if (e->propertyName() == QByteArrayLiteral("status")) + d->setStatus(static_cast<QShaderProgram::Status>(e->value().toInt())); + } +} + /*! \qmlproperty string ShaderProgram::vertexShaderCode @@ -308,6 +353,40 @@ QByteArray QShaderProgram::shaderCode(ShaderType type) const } } +/*! + \qmlproperty string ShaderProgram::log + + Holds the log of the current shader program. This is useful to diagnose a + compilation failure of the shader program. +*/ +/*! + \property QShaderProgram::log + + Holds the log of the current shader program. This is useful to diagnose a + compilation failure of the shader program. +*/ +QString QShaderProgram::log() const +{ + Q_D(const QShaderProgram); + return d->m_log; +} + +/*! + \qmlproperty string ShaderProgram::status + + Holds the status of the current shader program. +*/ +/*! + \property QShaderProgram::status + + Holds the status of the current shader program. +*/ +QShaderProgram::Status QShaderProgram::status() const +{ + Q_D(const QShaderProgram); + return d->m_status; +} + static QByteArray deincludify(const QString &filePath) { QFile f(filePath); diff --git a/src/render/materialsystem/qshaderprogram.h b/src/render/materialsystem/qshaderprogram.h index 442a25b2e..8c3da1a4a 100644 --- a/src/render/materialsystem/qshaderprogram.h +++ b/src/render/materialsystem/qshaderprogram.h @@ -58,6 +58,8 @@ class QT3DRENDERSHARED_EXPORT QShaderProgram : public Qt3DCore::QNode Q_PROPERTY(QByteArray geometryShaderCode READ geometryShaderCode WRITE setGeometryShaderCode NOTIFY geometryShaderCodeChanged) Q_PROPERTY(QByteArray fragmentShaderCode READ fragmentShaderCode WRITE setFragmentShaderCode NOTIFY fragmentShaderCodeChanged) Q_PROPERTY(QByteArray computeShaderCode READ computeShaderCode WRITE setComputeShaderCode NOTIFY computeShaderCodeChanged) + Q_PROPERTY(QString log READ log NOTIFY logChanged REVISION 9) + Q_PROPERTY(Status status READ status NOTIFY statusChanged REVISION 9) public: explicit QShaderProgram(Qt3DCore::QNode *parent = nullptr); @@ -73,6 +75,13 @@ public: }; Q_ENUM(ShaderType) // LCOV_EXCL_LINE + enum Status { + NotReady = 0, + Ready, + Error + }; + Q_ENUM(Status) // LCOV_EXCL_LINE + // Source code in-line QByteArray vertexShaderCode() const; QByteArray tessellationControlShaderCode() const; @@ -84,6 +93,9 @@ public: void setShaderCode(ShaderType type, const QByteArray &shaderCode); QByteArray shaderCode(ShaderType type) const; + QString log() const; + Status status() const; + Q_INVOKABLE static QByteArray loadSource(const QUrl &sourceUrl); public Q_SLOTS: @@ -101,9 +113,12 @@ Q_SIGNALS: void geometryShaderCodeChanged(const QByteArray &geometryShaderCode); void fragmentShaderCodeChanged(const QByteArray &fragmentShaderCode); void computeShaderCodeChanged(const QByteArray &computeShaderCode); + void logChanged(const QString &log); + void statusChanged(Status status); protected: explicit QShaderProgram(QShaderProgramPrivate &dd, Qt3DCore::QNode *parent = nullptr); + void sceneChangeEvent(const Qt3DCore::QSceneChangePtr &change) Q_DECL_OVERRIDE; private: Q_DECLARE_PRIVATE(QShaderProgram) diff --git a/src/render/materialsystem/qshaderprogram_p.h b/src/render/materialsystem/qshaderprogram_p.h index 5f695a279..6bdde68f1 100644 --- a/src/render/materialsystem/qshaderprogram_p.h +++ b/src/render/materialsystem/qshaderprogram_p.h @@ -52,13 +52,12 @@ // #include <private/qnode_p.h> +#include <Qt3DRender/qshaderprogram.h> QT_BEGIN_NAMESPACE namespace Qt3DRender { -class QShaderProgram; - class QShaderProgramPrivate : public Qt3DCore::QNodePrivate { public: @@ -77,6 +76,11 @@ public: QByteArray m_geometryShaderCode; QByteArray m_fragmentShaderCode; QByteArray m_computeShaderCode; + QString m_log; + QShaderProgram::Status m_status; + + void setLog(const QString &log); + void setStatus(QShaderProgram::Status status); }; struct QShaderProgramData diff --git a/src/render/materialsystem/qtechnique.cpp b/src/render/materialsystem/qtechnique.cpp index 74505bfbd..df142cdee 100644 --- a/src/render/materialsystem/qtechnique.cpp +++ b/src/render/materialsystem/qtechnique.cpp @@ -68,14 +68,61 @@ QTechniquePrivate::~QTechniquePrivate() \since 5.7 \brief Encapsulates a Technique. - A Technique specifies a set of RenderPass objects, FilterKey objects, Parameter objects - and a GraphicsApiFilter, which together define a rendering technique the given - graphics API can render. The filter keys are used by TechniqueFilter - to select specific techinques at specific parts of the FrameGraph. - If the same parameter is specified both in Technique and RenderPass, the one - in Technique overrides the one used in the RenderPass. - - \sa Qt3D.Render::Effect + A Technique specifies a set of RenderPass objects, FilterKey objects, + Parameter objects and a GraphicsApiFilter, which together define a + rendering technique the given graphics API can render. The filter keys are + used by TechniqueFilter to select specific techniques at specific parts of + the FrameGraph. If two Parameter instances with the same name are specified + in a Technique and a RenderPass, the one in Technique overrides the one + used in the RenderPass. + + When creating an Effect that targets several versions of a graphics API, it + is useful to create several Technique nodes each with a graphicsApiFilter + set to match one of the targeted versions. At runtime, the Qt3D renderer + will select the most appropriate Technique based on which graphics API + versions are supported and (if specified) the FilterKey nodes that satisfy + a given TechniqueFilter in the FrameGraph. + + \note When using OpenGL as the graphics API for rendering, Qt3D relies on + the QSurfaceFormat returned by QSurfaceFormat::defaultFormat() at runtime + to decide what is the most appropriate GL version available. If you need to + customize the QSurfaceFormat, do not forget to apply it with + QSurfaceFormat::setDefaultFormat(). Setting the QSurfaceFormat on the view + will likely have no effect on Qt3D related rendering. + + \code + Technique { + id: gl3Technique + parameters: [ + Parameter { name: "color"; value: "orange" } + ] + filterKeys: [ + FilterKey { name: "name"; value: "zFillTechnique" } + ] + graphicsApiFilter { + api: GraphicsApiFilter.OpenGL + profile: GraphicsApiFilter.CoreProfile + majorVersion: 3 + minorVersion: 1 + } + renderPasses: [ + RenderPass { + id: firstPass + shaderProgram: ShaderProgram { + ... + } + }, + RenderPass { + id: secondPass + shaderProgram: ShaderProgram { + ... + } + } + ] + } + \endcode + + \sa Effect, RenderPass, TechniqueFilter */ /*! @@ -85,15 +132,62 @@ QTechniquePrivate::~QTechniquePrivate() \since 5.7 \brief Encapsulates a Technique. - A Qt3DRender::QTechnique specifies a set of Qt3DRender::QRenderPass objects, - Qt3DRender::QFilterKey objects, Qt3DRender::QParameter objects and - a Qt3DRender::QGraphicsApiFilter, which together define a rendering technique the given - graphics API can render. The filter keys are used by Qt3DRender::QTechniqueFilter - to select specific techinques at specific parts of the FrameGraph. - If the same parameter is specified both in QTechnique and QRenderPass, the one - in QTechnique overrides the one used in the QRenderPass. - - \sa Qt3DRender::QEffect + A Qt3DRender::QTechnique specifies a set of Qt3DRender::QRenderPass + objects, Qt3DRender::QFilterKey objects, Qt3DRender::QParameter objects and + a Qt3DRender::QGraphicsApiFilter, which together define a rendering + technique the given graphics API can render. The filter keys are used by + Qt3DRender::QTechniqueFilter to select specific techniques at specific + parts of the FrameGraph. If two QParameter instances with the same name are + specified in a QTechnique and a QRenderPass, the one in Technique overrides + the one used in the QRenderPass. + + When creating an QEffect that targets several versions of a graphics API, + it is useful to create several QTechnique nodes each with a + graphicsApiFilter set to match one of the targeted GL versions. At runtime, + the Qt3D renderer will select the most appropriate QTechnique based on + which graphics API versions are supported and (if specified) the QFilterKey + nodes that satisfy a given QTechniqueFilter in the FrameGraph. + + \note When using OpenGL as the graphics API for rendering, Qt3D relies on + the QSurfaceFormat returned by QSurfaceFormat::defaultFormat() at runtime + to decide what is the most appropriate GL version available. If you need to + customize the QSurfaceFormat, do not forget to apply it with + QSurfaceFormat::setDefaultFormat(). Setting the QSurfaceFormat on the view + will likely have no effect on Qt3D related rendering. + + \code + QTechnique *gl3Technique = new QTechnique(); + + // Create the render passes + QRenderPass *firstPass = new QRenderPass(); + QRenderPass *secondPass = new QRenderPass(); + + // Add the passes to the technique + gl3Technique->addRenderPass(firstPass); + gl3Technique->addRenderPass(secondPass); + + // Set the targeted GL version for the technique + gl3Technique->graphicsApiFilter()->setApi(QGraphicsApiFilter::OpenGL); + gl3Technique->graphicsApiFilter()->setMajorVersion(3); + gl3Technique->graphicsApiFilter()->setMinorVersion(1); + gl3Technique->graphicsApiFilter()->setProfile(QGraphicsApiFilter::CoreProfile); + + // Create a FilterKey + QFilterKey *filterKey = new QFilterKey(); + filterKey->setName(QStringLiteral("name")); + fitlerKey->setValue(QStringLiteral("zFillPass")); + + // Add the FilterKey to the Technique + gl3Technique->addFilterKey(filterKey); + + // Create a QParameter + QParameter *colorParameter = new QParameter(QStringLiteral("color"), QColor::fromRgbF(0.0f, 0.0f, 1.0f, 1.0f)); + + // Add parameter to technique + gl3Technique->addParameter(colorParameter); + \endcode + + \sa QEffect, QRenderPass, QTechniqueFilter */ /*! diff --git a/src/render/materialsystem/shader.cpp b/src/render/materialsystem/shader.cpp index bd0af0bed..915ca1d54 100644 --- a/src/render/materialsystem/shader.cpp +++ b/src/render/materialsystem/shader.cpp @@ -59,10 +59,11 @@ namespace Qt3DRender { namespace Render { Shader::Shader() - : BackendNode() + : BackendNode(ReadWrite) , m_isLoaded(false) , m_dna(0) , m_graphicsContext(nullptr) + , m_status(QShaderProgram::NotReady) { m_shaderCode.resize(static_cast<int>(QShaderProgram::Compute) + 1); } @@ -71,8 +72,8 @@ Shader::~Shader() { // TO DO: ShaderProgram is leaked as of now // Fix that taking care that they may be shared given a same dna - - QObject::disconnect(m_contextConnection); + if (m_graphicsContext) + QObject::disconnect(m_contextConnection); } void Shader::cleanup() @@ -84,6 +85,7 @@ void Shader::cleanup() if (m_graphicsContext) m_graphicsContext->removeShaderProgramReference(this); m_graphicsContext = nullptr; + QObject::disconnect(m_contextConnection); } QBackendNode::setEnabled(false); @@ -96,6 +98,7 @@ void Shader::cleanup() m_uniforms.clear(); m_attributes.clear(); m_uniformBlocks.clear(); + m_status = QShaderProgram::NotReady; } void Shader::initializeFromPeer(const Qt3DCore::QNodeCreatedChangeBasePtr &change) @@ -183,8 +186,10 @@ void Shader::sceneChangeEvent(const Qt3DCore::QSceneChangePtr &e) m_shaderCode[QShaderProgram::Compute] = propertyValue.toByteArray(); m_isLoaded = false; } - if (!m_isLoaded) + if (!m_isLoaded) { + setStatus(QShaderProgram::NotReady); updateDNA(); + } markDirty(AbstractRenderer::AllDirty); } @@ -253,6 +258,14 @@ ShaderStorageBlock Shader::storageBlockForBlockName(const QString &blockName) return ShaderStorageBlock(); } +// To be called from a worker thread +void Shader::submitPendingNotifications() +{ + const QVector<Qt3DCore::QPropertyUpdatedChangePtr> notifications = std::move(m_pendingNotifications); + for (const Qt3DCore::QPropertyUpdatedChangePtr ¬ification : notifications) + notifyObservers(notification); +} + void Shader::prepareUniforms(ShaderParameterPack &pack) { const PackUniformHash &values = pack.uniforms(); @@ -261,7 +274,7 @@ void Shader::prepareUniforms(ShaderParameterPack &pack) const auto end = values.cend(); while (it != end) { // Find if there's a uniform with the same name id - for (const ShaderUniform &uniform : m_uniforms) { + for (const ShaderUniform &uniform : qAsConst(m_uniforms)) { if (uniform.m_nameId == it.key()) { pack.setSubmissionUniform(uniform); break; @@ -298,8 +311,8 @@ void Shader::updateDNA() QMutexLocker locker(&m_mutex); uint attachmentHash = 0; - QHash<QString, int>::const_iterator it = m_fragOutputs.begin(); - QHash<QString, int>::const_iterator end = m_fragOutputs.end(); + QHash<QString, int>::const_iterator it = m_fragOutputs.cbegin(); + QHash<QString, int>::const_iterator end = m_fragOutputs.cend(); while (it != end) { attachmentHash += ::qHash(it.value()) + ::qHash(it.key()); ++it; @@ -360,11 +373,11 @@ void Shader::initializeUniformBlocks(const QVector<ShaderUniformBlock> &uniformB qCDebug(Shaders) << "Initializing Uniform Block {" << m_uniformBlockNames[i] << "}"; // Find all active uniforms for the shader block - QVector<ShaderUniform>::const_iterator uniformsIt = m_uniforms.begin(); - const QVector<ShaderUniform>::const_iterator uniformsEnd = m_uniforms.end(); + QVector<ShaderUniform>::const_iterator uniformsIt = m_uniforms.cbegin(); + const QVector<ShaderUniform>::const_iterator uniformsEnd = m_uniforms.cend(); - QVector<QString>::const_iterator uniformNamesIt = m_uniformsNames.begin(); - const QVector<QString>::const_iterator uniformNamesEnd = m_attributesNames.end(); + QVector<QString>::const_iterator uniformNamesIt = m_uniformsNames.cbegin(); + const QVector<QString>::const_iterator uniformNamesEnd = m_attributesNames.cend(); QHash<QString, ShaderUniform> activeUniformsInBlock; @@ -402,7 +415,7 @@ void Shader::initializeShaderStorageBlocks(const QVector<ShaderStorageBlock> &sh Initializes this Shader's state relating to attributes, global block uniforms and and named uniform blocks by copying these details from \a other. */ -void Shader::initialize(const Shader &other) +void Shader::initializeFromReference(const Shader &other) { Q_ASSERT(m_dna == other.m_dna); m_uniformsNamesIds = other.m_uniformsNamesIds; @@ -420,6 +433,32 @@ void Shader::initialize(const Shader &other) m_shaderStorageBlockNames = other.m_shaderStorageBlockNames; m_shaderStorageBlocks = other.m_shaderStorageBlocks; m_isLoaded = other.m_isLoaded; + setStatus(other.status()); + setLog(other.log()); +} + +void Shader::setLog(const QString &log) +{ + if (log != m_log) { + m_log = log; + Qt3DCore::QPropertyUpdatedChangePtr e = Qt3DCore::QPropertyUpdatedChangePtr::create(peerId()); + e->setDeliveryFlags(Qt3DCore::QSceneChange::DeliverToAll); + e->setPropertyName("log"); + e->setValue(QVariant::fromValue(m_log)); + m_pendingNotifications.push_back(e); + } +} + +void Shader::setStatus(QShaderProgram::Status status) +{ + if (status != m_status) { + m_status = status; + Qt3DCore::QPropertyUpdatedChangePtr e = Qt3DCore::QPropertyUpdatedChangePtr::create(peerId()); + e->setDeliveryFlags(Qt3DCore::QSceneChange::DeliverToAll); + e->setPropertyName("status"); + e->setValue(QVariant::fromValue(m_status)); + m_pendingNotifications.push_back(e); + } } } // namespace Render diff --git a/src/render/materialsystem/shader_p.h b/src/render/materialsystem/shader_p.h index ad68a3bd6..b5127f5ec 100644 --- a/src/render/materialsystem/shader_p.h +++ b/src/render/materialsystem/shader_p.h @@ -54,6 +54,8 @@ #include <Qt3DRender/private/backendnode_p.h> #include <Qt3DRender/private/shaderparameterpack_p.h> #include <Qt3DRender/private/shadervariables_p.h> +#include <Qt3DRender/qshaderprogram.h> +#include <Qt3DCore/qpropertyupdatedchange.h> #include <QMutex> #include <QVector> @@ -63,8 +65,6 @@ class QOpenGLShaderProgram; namespace Qt3DRender { -class QShaderProgram; - namespace Render { class ShaderManager; @@ -118,6 +118,12 @@ public: ShaderStorageBlock storageBlockForBlockNameId(int blockNameId); ShaderStorageBlock storageBlockForBlockName(const QString &blockName); + inline QString log() const { return m_log; } + inline QShaderProgram::Status status() const { return m_status; } + + void submitPendingNotifications(); + inline bool hasPendingNotifications() const { return !m_pendingNotifications.empty(); } + private: void initializeFromPeer(const Qt3DCore::QNodeCreatedChangeBasePtr &change) Q_DECL_FINAL; @@ -148,6 +154,10 @@ private: mutable QMutex m_mutex; GraphicsContext *m_graphicsContext; QMetaObject::Connection m_contextConnection; + QString m_log; + QShaderProgram::Status m_status; + + QVector<Qt3DCore::QPropertyUpdatedChangePtr> m_pendingNotifications; void updateDNA(); @@ -157,7 +167,9 @@ private: void initializeUniformBlocks(const QVector<ShaderUniformBlock> &uniformBlockDescription); void initializeShaderStorageBlocks(const QVector<ShaderStorageBlock> &shaderStorageBlockDescription); - void initialize(const Shader &other); + void initializeFromReference(const Shader &other); + void setLog(const QString &log); + void setStatus(QShaderProgram::Status status); friend class GraphicsContext; }; diff --git a/src/render/materialsystem/shadercache.cpp b/src/render/materialsystem/shadercache.cpp index de3842386..4ddf26799 100644 --- a/src/render/materialsystem/shadercache.cpp +++ b/src/render/materialsystem/shadercache.cpp @@ -151,6 +151,11 @@ QOpenGLShaderProgram *ShaderCache::getShaderProgramForDNA(ProgramDNA dna) const return m_programHash.value(dna, nullptr); } +QVector<Qt3DCore::QNodeId> ShaderCache::shaderIdsForProgram(ProgramDNA dna) const +{ + return m_programRefs.value(dna); +} + } // namespace Render } // namespace Qt3DRender diff --git a/src/render/materialsystem/shadercache_p.h b/src/render/materialsystem/shadercache_p.h index 3cbc6bd0c..24a55876e 100644 --- a/src/render/materialsystem/shadercache_p.h +++ b/src/render/materialsystem/shadercache_p.h @@ -79,6 +79,7 @@ public: // Only ever used from the OpenGL submission thread QOpenGLShaderProgram *getShaderProgramForDNA(ProgramDNA dna) const; + QVector<Qt3DCore::QNodeId> shaderIdsForProgram(ProgramDNA dna) const; private: // Only ever used from the OpenGL submission thread |