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 <span>
10
11#include <QSGMaterialShader>
12#include <QVariant>
13
14/*!
15 * A helper that simplifies writing uniform data for QSGMaterialShader.
16 */
17struct UniformDataStream {
18 inline UniformDataStream(std::span<char> data) noexcept
19 : bytes(data.data())
20 , remainingSize(data.size())
21 {
22 }
23
24 ~UniformDataStream()
25 {
26 }
27
28 template<typename Data>
29 inline void skip(const Data &data = {})
30 {
31 constexpr int dataSize = sizeof(Data);
32 align(size: dataSize);
33 Q_UNUSED(data);
34
35 Q_ASSERT(remainingSize - dataSize >= 0);
36
37 bytes += dataSize;
38 offset += dataSize;
39 remainingSize -= dataSize;
40 }
41
42 inline void skipComponents(int count)
43 {
44 const int skipCount = count * 4;
45 align(size: 4);
46
47 Q_ASSERT(remainingSize - skipCount >= 0);
48
49 bytes += skipCount;
50 offset += skipCount;
51 remainingSize -= skipCount;
52 }
53
54 inline void skipMatrixOpacity()
55 {
56 align(size: Matrix4x4Size);
57 Q_ASSERT(remainingSize - Matrix4x4Size >= 0);
58 bytes += Matrix4x4Size;
59 offset += Matrix4x4Size;
60 remainingSize -= Matrix4x4Size;
61
62 align(size: FloatSize);
63 Q_ASSERT(remainingSize - FloatSize >= 0);
64 bytes += FloatSize;
65 offset += FloatSize;
66 remainingSize -= FloatSize;
67 }
68
69 template<typename Data>
70 friend inline UniformDataStream &operator<<(UniformDataStream &stream, const Data &data)
71 {
72 constexpr uint dataSize = sizeof(Data);
73 stream.align(size: dataSize);
74
75 Q_ASSERT(stream.remainingSize - dataSize >= 0);
76
77 memcpy(stream.bytes, &data, dataSize);
78
79 stream.bytes += dataSize;
80 stream.offset += dataSize;
81 stream.remainingSize -= dataSize;
82
83 return stream;
84 }
85
86 friend inline UniformDataStream &operator<<(UniformDataStream &stream, const QMatrix4x4 &m)
87 {
88 stream.align(size: Matrix4x4Size);
89
90 Q_ASSERT(stream.remainingSize - Matrix4x4Size >= 0);
91
92 memcpy(dest: stream.bytes, src: m.constData(), n: Matrix4x4Size);
93 stream.bytes += Matrix4x4Size;
94 stream.offset += Matrix4x4Size;
95 stream.remainingSize -= Matrix4x4Size;
96
97 return stream;
98 }
99
100 friend inline UniformDataStream &operator<<(UniformDataStream &stream, const QColor &color)
101 {
102 stream.align(size: ColorSize);
103
104 Q_ASSERT(stream.remainingSize - ColorSize >= 0);
105
106 std::array<float, 4> colorArray;
107 color.getRgbF(r: &colorArray[0], g: &colorArray[1], b: &colorArray[2], a: &colorArray[3]);
108 memcpy(dest: stream.bytes, src: colorArray.data(), n: ColorSize);
109
110 stream.bytes += ColorSize;
111 stream.offset += ColorSize;
112 stream.remainingSize -= ColorSize;
113
114 return stream;
115 }
116
117 template<typename T>
118 friend inline UniformDataStream &operator<<(UniformDataStream &stream, const QList<T> &v)
119 {
120 for (const auto &item : v) {
121 stream << item;
122 // Using std140, array elements are padded to a size of 16 bytes per element.
123 stream.align(size: 16);
124 }
125 return stream;
126 }
127
128 char *bytes;
129 int remainingSize;
130 int padding = 16;
131 int offset = 0;
132
133private:
134 static constexpr int FloatSize = sizeof(float);
135 static constexpr int ColorSize = FloatSize * 4;
136 static constexpr int Matrix4x4Size = FloatSize * 4 * 4;
137
138 // Encode alignment rules for std140.
139 // Minimum alignment is 4 bytes.
140 // Vec2 alignment is 8 bytes.
141 // Vec3 and Vec4 alignment is 16 bytes.
142 inline void align(uint size)
143 {
144 if (size <= 4) {
145 const auto padding = offset % 4 > 0 ? 4 - offset % 4 : 0;
146
147 Q_ASSERT(remainingSize - padding >= 0);
148
149 offset += padding;
150 bytes += padding;
151 remainingSize -= padding;
152 } else if (size <= 8) {
153 auto padding = offset % 8 > 0 ? 8 - offset % 8 : 0;
154
155 Q_ASSERT(remainingSize - padding >= 0);
156
157 offset += padding;
158 bytes += padding;
159 remainingSize -= padding;
160 } else {
161 auto padding = offset % 16 > 0 ? 16 - offset % 16 : 0;
162
163 Q_ASSERT(remainingSize - padding >= 0);
164
165 offset += padding;
166 bytes += padding;
167 remainingSize -= padding;
168 }
169 }
170};
171

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