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

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