1/*
2 * SPDX-FileCopyrightText: 2025 Arjen Hiemstra <ahiemstra@heimr.nl>
3 *
4 * SPDX-License-Identifier: LGPL-2.0-or-later
5 */
6
7#pragma once
8
9#include <QSGGeometryNode>
10#include <QSGMaterial>
11#include <QSGMaterialShader>
12#include <QSGTextureProvider>
13#include <QVariant>
14
15#include "uniformdatastream.h"
16
17class ShaderMaterial;
18
19/*!
20 * A base class for scene graph nodes that want to use a shader to render something.
21 */
22class ShaderNode : public QSGGeometryNode
23{
24public:
25 using TextureChannel = unsigned char;
26
27 struct TextureInfo {
28 TextureChannel channel = 0;
29 QQuickWindow::CreateTextureOptions options;
30 QSGTexture::Filtering filtering = QSGTexture::Linear;
31 std::shared_ptr<QSGTexture> texture = nullptr;
32 QPointer<QSGTextureProvider> provider = nullptr;
33 QMetaObject::Connection providerConnection;
34 };
35
36 ShaderNode();
37 ~ShaderNode() override;
38
39 void preprocess() override;
40
41 /*!
42 * The rectangle describing the geometry of this node.
43 */
44 QRectF rect() const;
45 void setRect(const QRectF &newRect);
46
47 /*!
48 * The UV coordinates of the geometry of this node.
49 */
50 QRectF uvs(TextureChannel channel) const;
51 void setUVs(TextureChannel channel, const QRectF &newUvs);
52
53 /*!
54 * The variant of the material used for rendering.
55 *
56 * This will be passed to createMaterialVariant() to perform the actual
57 * creation of the material.
58 */
59 QSGMaterialType *materialVariant() const;
60 void setMaterialVariant(QSGMaterialType *variant);
61
62 /*!
63 * Set the name of the shader to use for rendering.
64 *
65 * By default this will create and use an instance of ShaderMaterial that
66 * corresponds to the given shader.
67 */
68 void setShader(const QString &shader);
69
70 /*!
71 * Set the size of buffer used by the material for storing uniform values.
72 *
73 * The given size is in bytes. Note that you should account for all uniforms
74 * in your shader's uniform buffer.
75 */
76 void setUniformBufferSize(qsizetype size);
77
78 /*!
79 * A writeable view of the material's uniform data buffer.
80 *
81 * This can be used in combination with UniformDataStream to write the
82 * values of your uniforms.
83 */
84 std::span<char> uniformData();
85
86 /*!
87 * Set the number of texture channels.
88 *
89 * Each texture channel gets its own set of UV coordinates and texture. By
90 * default, the UVs will be set to (0, 0, 1, 1) unless the texture is an
91 * atlas texture, in which case coordinates matching the atlas will be used.
92 * Use setTexture() to set the texture to use for a channel.
93 */
94 void setTextureChannels(unsigned char count);
95
96 /*!
97 * Set the texture for a channel to an image.
98 *
99 * This will create a texture from \a image using \a window and the options
100 * specified by \a options, then assign it to texture channel \a channel.
101 * Textures created from images are cached, if an image has the same cache
102 * ID as a previous call to setTexture(), no new texture will be created.
103 */
104 void setTexture(TextureChannel channel, const QImage &image, QQuickWindow *window, QQuickWindow::CreateTextureOptions options = {});
105
106 /*!
107 * Set the texture for a channel to a texture provider.
108 *
109 * This will use \a provider to provide the texture for channel \a channel.
110 * \a options will be used whenever a new texture is created from
111 * \a provider.
112 */
113 void setTexture(TextureChannel channel, QSGTextureProvider *provider, QQuickWindow::CreateTextureOptions options = {});
114
115 /*!
116 * Set the texture filtering mode for texture \a channel to \a filtering.
117 */
118 void setTextureFiltering(TextureChannel channel, QSGTexture::Filtering filtering);
119
120 /*!
121 * Update internal state based on newly-set parameters.
122 *
123 * This is done as an explicit step to ensure we don't modify expensive GPU
124 * resources like geometry multiple times during a single update.
125 */
126 virtual void update();
127
128 /*!
129 * Helper function that returns a pre-multiplied version of a color.
130 */
131 static inline QColor toPremultiplied(const QColor &value)
132 {
133 return QColor::fromRgbF(r: value.redF() * value.alphaF(), //
134 g: value.greenF() * value.alphaF(),
135 b: value.blueF() * value.alphaF(),
136 a: value.alphaF());
137 }
138
139protected:
140 /*!
141 * Create a new instance of a certain material variant.
142 *
143 * This should return a new instance of the material that matches \a variant,
144 * or nullptr if the specified variant cannot be handled by the current node.
145 */
146 virtual QSGMaterial *createMaterialVariant(QSGMaterialType *variant);
147
148private:
149 void preprocessTexture(const TextureInfo &texture);
150
151 QRectF m_rect;
152 QVarLengthArray<QRectF, 16> m_uvs;
153 bool m_geometryUpdateNeeded = true;
154 unsigned char m_textureChannels = 1;
155
156 QSGMaterialType *m_materialVariant = nullptr;
157 ShaderMaterial *m_shaderMaterial = nullptr;
158
159 QList<TextureInfo> m_textures;
160
161 QSGGeometry::AttributeSet *m_attributeSet = nullptr;
162};
163

source code of kirigami/src/primitives/scenegraph/shadernode.h