1 | // Copyright (C) 2023 The Qt Company Ltd. |
2 | // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only |
3 | |
4 | #include "qquickshapecurvenode_p_p.h" |
5 | #include "qquickshapecurvenode_p.h" |
6 | |
7 | #include "qquickshapegenericrenderer_p.h" |
8 | |
9 | QT_BEGIN_NAMESPACE |
10 | |
11 | namespace { |
12 | |
13 | class QQuickShapeCurveMaterialShader : public QSGMaterialShader |
14 | { |
15 | public: |
16 | QQuickShapeCurveMaterialShader(QQuickAbstractPathRenderer::FillGradientType gradientType, |
17 | bool includeStroke, |
18 | bool useDerivatives); |
19 | |
20 | bool updateUniformData(RenderState &state, QSGMaterial *newEffect, QSGMaterial *oldEffect) override; |
21 | void updateSampledImage(RenderState &state, int binding, QSGTexture **texture, |
22 | QSGMaterial *newMaterial, QSGMaterial *oldMaterial) override; |
23 | }; |
24 | |
25 | QQuickShapeCurveMaterialShader::QQuickShapeCurveMaterialShader(QQuickAbstractPathRenderer::FillGradientType gradientType, |
26 | bool includeStroke, |
27 | bool useDerivatives) |
28 | { |
29 | QString baseName = QStringLiteral(":/qt-project.org/shapes/shaders_ng/shapecurve" ); |
30 | |
31 | if (gradientType == QQuickAbstractPathRenderer::LinearGradient) { |
32 | baseName += QStringLiteral("_lg" ); |
33 | } else if (gradientType == QQuickAbstractPathRenderer::RadialGradient) { |
34 | baseName += QStringLiteral("_rg" ); |
35 | } else if (gradientType == QQuickAbstractPathRenderer::ConicalGradient) { |
36 | baseName += QStringLiteral("_cg" ); |
37 | } |
38 | |
39 | if (includeStroke) |
40 | baseName += QStringLiteral("_stroke" ); |
41 | |
42 | if (useDerivatives) |
43 | baseName += QStringLiteral("_derivatives" ); |
44 | |
45 | setShaderFileName(stage: VertexStage, filename: baseName + QStringLiteral(".vert.qsb" )); |
46 | setShaderFileName(stage: FragmentStage, filename: baseName + QStringLiteral(".frag.qsb" )); |
47 | } |
48 | |
49 | void QQuickShapeCurveMaterialShader::updateSampledImage(RenderState &state, int binding, QSGTexture **texture, |
50 | QSGMaterial *newMaterial, QSGMaterial *oldMaterial) |
51 | { |
52 | Q_UNUSED(oldMaterial); |
53 | const QQuickShapeCurveMaterial *m = static_cast<QQuickShapeCurveMaterial *>(newMaterial); |
54 | const QQuickShapeCurveNode *node = m->node(); |
55 | if (binding != 1 || node->gradientType() == QQuickAbstractPathRenderer::NoGradient) |
56 | return; |
57 | |
58 | const QQuickShapeGradientCacheKey cacheKey(node->fillGradient().stops, |
59 | node->fillGradient().spread); |
60 | QSGTexture *t = QQuickShapeGradientCache::cacheForRhi(rhi: state.rhi())->get(grad: cacheKey); |
61 | t->commitTextureOperations(rhi: state.rhi(), resourceUpdates: state.resourceUpdateBatch()); |
62 | *texture = t; |
63 | } |
64 | |
65 | bool QQuickShapeCurveMaterialShader::updateUniformData(RenderState &state, QSGMaterial *newEffect, QSGMaterial *oldEffect) |
66 | { |
67 | bool changed = false; |
68 | QByteArray *buf = state.uniformData(); |
69 | Q_ASSERT(buf->size() >= 80); |
70 | |
71 | int offset = 0; |
72 | float matrixScale = 0.0f; |
73 | if (state.isMatrixDirty()) { |
74 | const QMatrix4x4 m = state.combinedMatrix(); |
75 | |
76 | memcpy(dest: buf->data() + offset, src: m.constData(), n: 64); |
77 | |
78 | matrixScale = qSqrt(v: qAbs(t: state.determinant())); |
79 | memcpy(dest: buf->data() + offset + 64, src: &matrixScale, n: 4); |
80 | |
81 | changed = true; |
82 | } |
83 | offset += 68; |
84 | |
85 | if (state.isOpacityDirty()) { |
86 | const float opacity = state.opacity(); |
87 | memcpy(dest: buf->data() + offset, src: &opacity, n: 4); |
88 | changed = true; |
89 | } |
90 | offset += 4; |
91 | |
92 | QQuickShapeCurveMaterial *newMaterial = static_cast<QQuickShapeCurveMaterial *>(newEffect); |
93 | QQuickShapeCurveMaterial *oldMaterial = static_cast<QQuickShapeCurveMaterial *>(oldEffect); |
94 | |
95 | QQuickShapeCurveNode *newNode = newMaterial != nullptr ? newMaterial->node() : nullptr; |
96 | QQuickShapeCurveNode *oldNode = oldMaterial != nullptr ? oldMaterial->node() : nullptr; |
97 | |
98 | if (newNode == nullptr) |
99 | return changed; |
100 | |
101 | if (oldNode == nullptr || oldNode->debug() != newNode->debug()) { |
102 | float debug = newNode->debug(); |
103 | memcpy(dest: buf->data() + offset, src: &debug, n: 4); |
104 | changed = true; |
105 | } |
106 | offset += 8; |
107 | |
108 | if (newNode->hasStroke()) { |
109 | Q_ASSERT(buf->size() >= offset + 32); |
110 | QVector4D newStrokeColor(newNode->strokeColor().redF(), |
111 | newNode->strokeColor().greenF(), |
112 | newNode->strokeColor().blueF(), |
113 | newNode->strokeColor().alphaF()); |
114 | QVector4D oldStrokeColor = oldNode != nullptr |
115 | ? QVector4D(oldNode->strokeColor().redF(), |
116 | oldNode->strokeColor().greenF(), |
117 | oldNode->strokeColor().blueF(), |
118 | oldNode->strokeColor().alphaF()) |
119 | : QVector4D{}; |
120 | |
121 | if (oldNode == nullptr || oldStrokeColor != newStrokeColor) { |
122 | memcpy(dest: buf->data() + offset, src: &newStrokeColor, n: 16); |
123 | changed = true; |
124 | } |
125 | offset += 16; |
126 | |
127 | if (oldNode == nullptr |
128 | || !qFuzzyCompare(p1: newNode->strokeWidth(), p2: oldNode->strokeWidth()) |
129 | || (state.isMatrixDirty() && newNode->strokeWidth() > 0.0f)) { |
130 | float w = newNode->strokeWidth() * matrixScale; // matrixScale calculated earlier |
131 | memcpy(dest: buf->data() + offset, src: &w, n: 4); |
132 | changed = true; |
133 | } |
134 | offset += 16; |
135 | } |
136 | |
137 | if (newNode->gradientType() == QQuickAbstractPathRenderer::NoGradient) { |
138 | Q_ASSERT(buf->size() >= offset + 16); |
139 | |
140 | QVector4D newColor = QVector4D(newNode->color().redF(), |
141 | newNode->color().greenF(), |
142 | newNode->color().blueF(), |
143 | newNode->color().alphaF()); |
144 | QVector4D oldColor = oldNode != nullptr |
145 | ? QVector4D(oldNode->color().redF(), |
146 | oldNode->color().greenF(), |
147 | oldNode->color().blueF(), |
148 | oldNode->color().alphaF()) |
149 | : QVector4D{}; |
150 | |
151 | if (oldNode == nullptr || oldColor != newColor) { |
152 | memcpy(dest: buf->data() + offset, src: &newColor, n: 16); |
153 | changed = true; |
154 | } |
155 | |
156 | offset += 16; |
157 | } else if (newNode->gradientType() == QQuickAbstractPathRenderer::LinearGradient) { |
158 | Q_ASSERT(buf->size() >= offset + 8 + 8); |
159 | |
160 | QVector2D newGradientStart = QVector2D(newNode->fillGradient().a); |
161 | QVector2D oldGradientStart = oldNode != nullptr |
162 | ? QVector2D(oldNode->fillGradient().a) |
163 | : QVector2D{}; |
164 | |
165 | if (newGradientStart != oldGradientStart || oldEffect == nullptr) { |
166 | memcpy(dest: buf->data() + offset, src: &newGradientStart, n: 8); |
167 | changed = true; |
168 | } |
169 | offset += 8; |
170 | |
171 | QVector2D newGradientEnd = QVector2D(newNode->fillGradient().b); |
172 | QVector2D oldGradientEnd = oldNode!= nullptr |
173 | ? QVector2D(oldNode->fillGradient().b) |
174 | : QVector2D{}; |
175 | |
176 | if (newGradientEnd != oldGradientEnd || oldEffect == nullptr) { |
177 | memcpy(dest: buf->data() + offset, src: &newGradientEnd, n: 8); |
178 | changed = true; |
179 | } |
180 | |
181 | offset += 8; |
182 | } else if (newNode->gradientType() == QQuickAbstractPathRenderer::RadialGradient) { |
183 | Q_ASSERT(buf->size() >= offset + 8 + 8 + 4 + 4); |
184 | |
185 | QVector2D newFocalPoint = QVector2D(newNode->fillGradient().b); |
186 | QVector2D oldFocalPoint = oldNode != nullptr |
187 | ? QVector2D(oldNode->fillGradient().b) |
188 | : QVector2D{}; |
189 | if (oldNode == nullptr || newFocalPoint != oldFocalPoint) { |
190 | memcpy(dest: buf->data() + offset, src: &newFocalPoint, n: 8); |
191 | changed = true; |
192 | } |
193 | offset += 8; |
194 | |
195 | QVector2D newCenterPoint = QVector2D(newNode->fillGradient().a); |
196 | QVector2D oldCenterPoint = oldNode != nullptr |
197 | ? QVector2D(oldNode->fillGradient().a) |
198 | : QVector2D{}; |
199 | |
200 | QVector2D newCenterToFocal = newCenterPoint - newFocalPoint; |
201 | QVector2D oldCenterToFocal = oldCenterPoint - oldFocalPoint; |
202 | if (oldNode == nullptr || newCenterToFocal != oldCenterToFocal) { |
203 | memcpy(dest: buf->data() + offset, src: &newCenterToFocal, n: 8); |
204 | changed = true; |
205 | } |
206 | offset += 8; |
207 | |
208 | float newCenterRadius = newNode->fillGradient().v0; |
209 | float oldCenterRadius = oldNode != nullptr |
210 | ? oldNode->fillGradient().v0 |
211 | : 0.0f; |
212 | if (oldNode == nullptr || !qFuzzyCompare(p1: newCenterRadius, p2: oldCenterRadius)) { |
213 | memcpy(dest: buf->data() + offset, src: &newCenterRadius, n: 4); |
214 | changed = true; |
215 | } |
216 | offset += 4; |
217 | |
218 | float newFocalRadius = newNode->fillGradient().v1; |
219 | float oldFocalRadius = oldNode != nullptr |
220 | ? oldNode->fillGradient().v1 |
221 | : 0.0f; |
222 | if (oldNode == nullptr || !qFuzzyCompare(p1: newFocalRadius, p2: oldFocalRadius)) { |
223 | memcpy(dest: buf->data() + offset, src: &newFocalRadius, n: 4); |
224 | changed = true; |
225 | } |
226 | offset += 4; |
227 | |
228 | } else if (newNode->gradientType() == QQuickAbstractPathRenderer::ConicalGradient) { |
229 | Q_ASSERT(buf->size() >= offset + 8 + 4); |
230 | |
231 | QVector2D newFocalPoint = QVector2D(newNode->fillGradient().a); |
232 | QVector2D oldFocalPoint = oldNode != nullptr |
233 | ? QVector2D(oldNode->fillGradient().a) |
234 | : QVector2D{}; |
235 | if (oldNode == nullptr || newFocalPoint != oldFocalPoint) { |
236 | memcpy(dest: buf->data() + offset, src: &newFocalPoint, n: 8); |
237 | changed = true; |
238 | } |
239 | offset += 8; |
240 | |
241 | float newAngle = newNode->fillGradient().v0; |
242 | float oldAngle = oldNode != nullptr |
243 | ? oldNode->fillGradient().v0 |
244 | : 0.0f; |
245 | if (oldNode == nullptr || !qFuzzyCompare(p1: newAngle, p2: oldAngle)) { |
246 | newAngle = -qDegreesToRadians(degrees: newAngle); |
247 | memcpy(dest: buf->data() + offset, src: &newAngle, n: 4); |
248 | changed = true; |
249 | } |
250 | offset += 4; |
251 | } |
252 | |
253 | return changed; |
254 | } |
255 | } |
256 | |
257 | QQuickShapeCurveMaterial::QQuickShapeCurveMaterial(QQuickShapeCurveNode *node) |
258 | : m_node(node) |
259 | { |
260 | setFlag(flags: Blending, on: true); |
261 | setFlag(flags: RequiresDeterminant, on: true); |
262 | } |
263 | |
264 | int QQuickShapeCurveMaterial::compare(const QSGMaterial *other) const |
265 | { |
266 | if (other->type() != type()) |
267 | return (type() - other->type()); |
268 | |
269 | const QQuickShapeCurveMaterial *otherMaterial = |
270 | static_cast<const QQuickShapeCurveMaterial *>(other); |
271 | |
272 | QQuickShapeCurveNode *a = node(); |
273 | QQuickShapeCurveNode *b = otherMaterial->node(); |
274 | if (a == b) |
275 | return 0; |
276 | |
277 | if (int d = a->strokeColor().rgba() - b->strokeColor().rgba()) |
278 | return d; |
279 | |
280 | if (a->gradientType() == QQuickAbstractPathRenderer::NoGradient) { |
281 | if (int d = a->color().red() - b->color().red()) |
282 | return d; |
283 | if (int d = a->color().green() - b->color().green()) |
284 | return d; |
285 | if (int d = a->color().blue() - b->color().blue()) |
286 | return d; |
287 | if (int d = a->color().alpha() - b->color().alpha()) |
288 | return d; |
289 | } else { |
290 | const QQuickAbstractPathRenderer::GradientDesc &ga = a->fillGradient(); |
291 | const QQuickAbstractPathRenderer::GradientDesc &gb = b->fillGradient(); |
292 | |
293 | if (int d = ga.a.x() - gb.a.x()) |
294 | return d; |
295 | if (int d = ga.a.y() - gb.a.y()) |
296 | return d; |
297 | if (int d = ga.b.x() - gb.b.x()) |
298 | return d; |
299 | if (int d = ga.b.y() - gb.b.y()) |
300 | return d; |
301 | |
302 | if (int d = ga.v0 - gb.v0) |
303 | return d; |
304 | if (int d = ga.v1 - gb.v1) |
305 | return d; |
306 | |
307 | if (int d = ga.spread - gb.spread) |
308 | return d; |
309 | |
310 | if (int d = ga.stops.size() - gb.stops.size()) |
311 | return d; |
312 | |
313 | for (int i = 0; i < ga.stops.size(); ++i) { |
314 | if (int d = ga.stops[i].first - gb.stops[i].first) |
315 | return d; |
316 | if (int d = ga.stops[i].second.rgba() - gb.stops[i].second.rgba()) |
317 | return d; |
318 | } |
319 | } |
320 | |
321 | return 0; |
322 | } |
323 | |
324 | QSGMaterialType *QQuickShapeCurveMaterial::type() const |
325 | { |
326 | static QSGMaterialType type[8]; |
327 | uint index = node()->gradientType(); |
328 | Q_ASSERT((index & ~3) == 0); // Only two first bits for gradient type |
329 | |
330 | if (node()->hasStroke()) |
331 | index |= 4; |
332 | |
333 | return &type[index]; |
334 | } |
335 | |
336 | QSGMaterialShader *QQuickShapeCurveMaterial::createShader(QSGRendererInterface::RenderMode renderMode) const |
337 | { |
338 | return new QQuickShapeCurveMaterialShader(node()->gradientType(), |
339 | node()->hasStroke(), |
340 | renderMode == QSGRendererInterface::RenderMode3D); |
341 | } |
342 | |
343 | |
344 | QT_END_NAMESPACE |
345 | |