aboutsummaryrefslogtreecommitdiffstats
path: root/src/quick/scenegraph/coreapi/qsgmaterialrhishader.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/quick/scenegraph/coreapi/qsgmaterialrhishader.cpp')
-rw-r--r--src/quick/scenegraph/coreapi/qsgmaterialrhishader.cpp565
1 files changed, 565 insertions, 0 deletions
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