Qt
Internal/Contributor docs for the Qt SDK. Note: These are NOT official API docs; those are found at https://doc.qt.io/
Loading...
Searching...
No Matches
qquick3dquadtextureprovider.cpp
Go to the documentation of this file.
1// Copyright (C) 2026 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
3// Qt-Security score:significant reason:default
4
6
7#include <QtQuick3DRuntimeRender/private/qssgshadermaterialadapter_p.h>
8
9#include <ssg/qssgrenderextensions.h>
10#include <ssg/qssgrenderhelpers.h>
11#include <ssg/qssgrendercontextcore.h>
12#include <ssg/qquick3dextensionhelpers.h>
13
14
15/*!
16 \qmltype QuadTextureProvider
17 \nativetype QQuick3DQuadTextureProvider
18 \inqmlmodule QtQuick3D.Helpers
19 \inherits TextureProviderExtension
20 \since 6.12
21 \brief Used to render a quad texture using a custom fragment shader.
22
23 This type is used to render quad textures using custom shader code, enabling a convenient way
24 to get programmable textures. By providing a fragment shader and wanted properties, a pass is created
25 and a shader pipeline is built based on the provided data, passing the properties in as uniform values
26 to the fragment shader.
27
28 Built-ins provided:
29
30 \table
31 \header
32 \li Keyword
33 \li Type
34 \li Description
35 \row
36 \li MAIN
37 \li
38 \li void MAIN() is the entry point. This function must always be present in the fragment shader provided.
39 \row
40 \li INPUT_UV
41 \li vec2
42 \li UV coordinates for current fragment. Top-right: [1, 1] and bottom-left: [0, 0].
43 \row
44 \li OUTPUT_SIZE
45 \li vec2
46 \li Size of the output texture.
47 \row
48 \endtable
49
50 Custom properties gets mapped to uniforms. Any time the values change, the updated
51 value will become visible in the shader. This concept may already be familiar from \l ShaderEffect.
52
53 The name of the QML property and the GLSL variable must match. There is no separate
54 declaration in the shader code for the individual uniforms. Rather, the QML property name
55 can be used as-is.
56
57 The following table lists how the types are mapped:
58
59 \table
60 \header
61 \li QML Type
62 \li Shader Type
63 \li Notes
64 \row
65 \li real, int, bool
66 \li float, int, bool
67 \li
68 \row
69 \li color
70 \li vec4
71 \li sRGB to linear conversion is performed implicitly
72 \row
73 \li vector2d
74 \li vec2
75 \li
76 \row
77 \li vector3d
78 \li vec3
79 \li
80 \row
81 \li vector4d
82 \li vec4
83 \li
84 \row
85 \li matrix4x4
86 \li mat4
87 \li
88 \row
89 \li quaternion
90 \li vec4
91 \li scalar value is \c w
92 \row
93 \li rect
94 \li vec4
95 \li
96 \row
97 \li point, size
98 \li vec2
99 \li
100 \row
101 \li TextureInput
102 \li sampler2D
103 \li
104 \endtable
105
106 An example of outputting a simple red texture could be done the following way:
107 \badcode
108 Texture {
109 textureProvider: QuadTextureProvider {
110 width: 128
111 height: 128
112 fragmentShaderCode: `
113 void MAIN() {
114 FRAGCOLOR = vec4(1.0, 0.0, 0.0, 1.0);
115 }
116 `
117 }
118 }
119 \endcode
120
121 Another example sampling from a \l Texture property and mixing with UV colors:
122 \badcode
123 Texture {
124 textureProvider: QuadTextureProvider {
125 fragmentShaderCode: `
126 void MAIN() {
127 vec2 uv = INPUT_UV;
128 vec4 c = texture(checkers, uv);
129 FRAGCOLOR = mix(c, vec4(uv, 1, 1), 0.5);
130 }`
131
132 property Texture checkers : Texture {
133 source: "../shared/maps/checkers2.png"
134 }
135 }
136 }
137 \endcode
138
139 The result is the following:
140 \image quadtextureprovider_checkers.webp
141 {2x2 grid of gradient-filled cells in teal, white, blue, and
142 magenta}
143
144 \note Providing a vertex shader is not supported, only a fragment shader.
145
146 \note If \l Texture properties are provided it will not render until the dependent textures are available.
147
148 \note There is currently no support for adding / removing properties at runtime, just modifying the original ones.
149
150 \sa ShaderEffect
151 */
152
153/*!
154 \qmlproperty url QuadTextureProvider::fragmentShader
155 \since 6.12
156
157 Specifies the file with the snippet of custom fragment shader code.
158
159 The value is a URL and must either be a local file or use the qrc scheme to
160 access files embedded via the Qt resource system. Relative file paths
161 (without a scheme) are also accepted, in which case the file is treated as
162 relative to the component (the \c{.qml} file).
163
164 \warning Shader snippets are assumed to be trusted content. Application
165 developers are advised to carefully consider the potential implications
166 before allowing the loading of user-provided content that is not part of the
167 application.
168
169 \note If set, fragmentShaderCode will take precedence over fragmentShader.
170
171 \sa fragmentShaderCode
172*/
173
174/*!
175 \qmlproperty string QuadTextureProvider::fragmentShaderCode
176 \since 6.12
177
178 Specifies a snippet of custom fragment shader code.
179
180 Used as a way to inline shader code as a string instead of providing a file.
181
182 \note If set, this property will take precedence over \l fragmentShader.
183
184 \sa fragmentShader
185*/
186
187/*!
188 \qmlproperty int QuadTextureProvider::width
189 \since 6.12
190 \default 128
191
192 Specifies the width in pixels of the output texture.
193
194 \sa height
195*/
196
197/*!
198 \qmlproperty int QuadTextureProvider::height
199 \since 6.12
200 \default 128
201
202 Specifies the height in pixels of the output texture.
203
204 \sa width
205*/
206
207/*!
208 \qmlproperty enumeration QuadTextureProvider::format
209 \since 6.12
210 \default TexureData.RGBA16F
211
212 This property holds the format of the output texture.
213
214 \value TexureData.RGBA8 The color format is considered as 8-bit integer in R, G, B and alpha channels.
215 \value TexureData.RGBA16F The color format is considered as 16-bit float in R,G,B and alpha channels.
216 \value TexureData.RGBA32F The color format is considered as 32-bit float in R, G, B and alpha channels.
217 \value TexureData.RGBE8 The color format is considered as 8-bit mantissa in the R, G, and B channels and 8-bit shared exponent.
218 \value TexureData.R8 The color format is considered as 8-bit integer in R channel.
219 \value TexureData.R16 The color format is considered as 16-bit integer in R channel.
220 \value TexureData.R16F The color format is considered as 16-bit float in R channel.
221 \value TexureData.R32F The color format is considered as 32-bit float R channel.
222
223 \note With the exception of \c TexureData.RGBA8, not every format is supported at runtime as this
224 depends on which backend is being used as well which hardware is being used.
225*/
226
228
229static constexpr float g_vertexData[] = { -1.0f, -1.0f, 0.0f, 0.0f, 0.0f, 1.0f, -1.0f, 0.0f, 1.0f, 0.0f,
230 1.0f, 1.0f, 0.0f, 1.0f, 1.0f, -1.0f, 1.0f, 0.0f, 0.0f, 1.0f };
231
232static constexpr uint16_t g_indexData[] = { 0, 1, 2, 0, 2, 3 };
233
235void main()
236{
237 qt_inputUV = attr_uv;
238 gl_Position = vec4(attr_pos, 0.0, 1.0);
239}
240)";
241
243void main()
244{
245 qt_inputUV = attr_uv;
246 qt_inputUV.y = 1.0 - qt_inputUV.y;
247 gl_Position = vec4(attr_pos, 0.0, 1.0);
248}
249)";
250
252void main()
253{
254 qt_customMain();
255}
256)";
257
259void MAIN()
260{
261 FRAGCOLOR = vec4(1.0, 0.0, 1.0, 1.0);
262}
263)";
264
265static inline void insertVertexMainArgs(QByteArray &snippet)
266{
267 static const char *argKey = "/*%QT_ARGS_MAIN%*/";
268 const int argKeyLen = int(strlen(argKey));
269 const int argKeyPos = snippet.indexOf(argKey);
270 if (argKeyPos >= 0)
271 snippet = snippet.left(argKeyPos) + QByteArrayLiteral("inout vec3 VERTEX") + snippet.mid(argKeyPos + argKeyLen);
272}
273
274namespace {
275
276QRhiTexture::Format rhiTextureFormatFromTextureDataFormat(QQuick3DTextureData::Format format)
277{
278 switch (format) {
279 case QQuick3DTextureData::RGBA8:
280 return QRhiTexture::Format::RGBA8;
281 case QQuick3DTextureData::RGBA16F:
282 return QRhiTexture::Format::RGBA16F;
283 case QQuick3DTextureData::RGBA32F:
284 return QRhiTexture::Format::RGBA32F;
285 case QQuick3DTextureData::RGBE8:
286 return QRhiTexture::Format::RGBA8;
287 case QQuick3DTextureData::R8:
288 return QRhiTexture::Format::R8;
289 case QQuick3DTextureData::R16:
290 return QRhiTexture::Format::R16;
291 case QQuick3DTextureData::R16F:
292 return QRhiTexture::Format::R16F;
293 case QQuick3DTextureData::R32F:
294 return QRhiTexture::Format::R32F;
295 default:
296 return QRhiTexture::Format::RGBA8;
297 }
298 Q_UNREACHABLE_RETURN(QRhiTexture::Format::RGBA8);
299}
300
301QByteArray generateFinalVertexShaderCode(QRhi *rhi,
302 QSSGShaderCustomMaterialAdapter::StringPairList uniforms,
303 QSSGShaderCustomMaterialAdapter::StringPairList varyings)
304{
305 // Handle d3d flipped textures
306 const bool doFlip = rhi->isYUpInNDC() && !rhi->isYUpInFramebuffer();
307 QByteArray vertexShader = doFlip ? mainVertexSnippetFlipped : mainVertexSnippet;
308
309 QSSGShaderCustomMaterialAdapter::ShaderCodeAndMetaData result;
310 QByteArray buf;
311 QSSGShaderCustomMaterialAdapter::CustomShaderPrepWorkData scratch;
312 QSSGShaderCustomMaterialAdapter::beginPrepareCustomShader(&scratch, &result, vertexShader, QSSGShaderCache::ShaderType::Vertex, false);
313 QSSGShaderCustomMaterialAdapter::finishPrepareCustomShader(&buf,
314 scratch,
315 result,
316 QSSGShaderCache::ShaderType::Vertex,
317 false,
318 uniforms,
319 { },
320 varyings,
321 { },
322 { });
323 insertVertexMainArgs(result.first);
324 return result.first + buf;
325}
326
327QByteArray generateFinalFragmentShaderCode(const QByteArray &baseFragmentShaderCode,
328 QSSGShaderCustomMaterialAdapter::StringPairList uniforms,
329 QSSGShaderCustomMaterialAdapter::StringPairList varyings)
330{
331 QSSGShaderCustomMaterialAdapter::ShaderCodeAndMetaData result;
332 QByteArray buf;
333 QSSGShaderCustomMaterialAdapter::CustomShaderPrepWorkData scratch;
334 QSSGShaderCustomMaterialAdapter::beginPrepareCustomShader(&scratch, &result, baseFragmentShaderCode, QSSGShaderCache::ShaderType::Fragment, false);
335 QSSGShaderCustomMaterialAdapter::finishPrepareCustomShader(&buf,
336 scratch,
337 result,
338 QSSGShaderCache::ShaderType::Fragment,
339 false,
340 uniforms,
341 varyings,
342 { },
343 { },
344 { });
345 return result.first + buf;
346}
347
348QSSGRhiShaderPipelinePtr compileShader(QSSGRenderContextInterface *sgContext,
349 const QByteArray &shaderPathKey,
350 const QByteArray &vertexShader,
351 const QByteArray &fragmentShader)
352{
353 QSSGProgramGenerator *generator = sgContext->shaderProgramGenerator().get();
354 QSSGShaderLibraryManager *shaderLib = sgContext->shaderLibraryManager().get();
355 QSSGShaderCache *shaderCache = sgContext->shaderCache().get();
356
357 generator->beginProgram();
358 auto vertex = generator->getStage(QSSGShaderGeneratorStage::Vertex);
359 vertex->addIncoming("attr_pos", "vec2");
360 vertex->addIncoming("attr_uv", "vec2");
361 vertex->append(vertexShader);
362
363 generator->getStage(QSSGShaderGeneratorStage::Fragment)->append(fragmentShader);
364
365 QSSGShaderFeatures features;
366 const QByteArray key = shaderPathKey + ':'
367 + QCryptographicHash::hash(QByteArray(vertexShader + fragmentShader), QCryptographicHash::Algorithm::Sha1)
368 .toHex();
369 return generator->compileGeneratedRhiShader(key, features, *shaderLib, *shaderCache, QSSGRhiShaderPipeline::UsedWithoutIa, { }, 1, false);
370}
371
372} // namespace
373
375{
376public:
379 bool prepareData(QSSGFrameData &data) override;
380 void prepareRender(QSSGFrameData &data) override;
381 void render(QSSGFrameData &data) override;
383
385
389 int width = 128;
390 int height = 128;
391
392 QByteArray shaderPathKey = "quad texture provider --";
393
394private:
395 QPointer<QQuick3DQuadTextureProvider> m_ext;
396
397 QSSGRhiShaderResourceBindingList m_srbBindings;
398 QSSGRhiShaderPipelinePtr m_shaderPipeline;
399
400 std::unique_ptr<QRhiBuffer> m_vertexBuffer;
401 std::unique_ptr<QRhiBuffer> m_indexBuffer;
402
403 std::unique_ptr<QRhiTexture> m_outputTexture;
404 std::unique_ptr<QRhiTexture> m_outputTextureOld;
405 std::unique_ptr<QRhiTextureRenderTarget> m_renderTarget;
406 std::unique_ptr<QRhiRenderPassDescriptor> m_renderPassDesc;
407
408 std::unique_ptr<QRhiGraphicsPipeline> m_graphicsPipeline;
409 QRhiShaderResourceBindings* m_srb = nullptr; // not owned
410};
411
413
415
416bool QSSGQuadTextureProvider::prepareData(QSSGFrameData &data)
417{
418 QSSGRenderContextInterface *ctxIfx = data.contextInterface();
419 auto bufferManager = ctxIfx->bufferManager().get();
420 QSSGRhiContext *rhiCtx = ctxIfx->rhiContext().get();
421 if (!rhiCtx)
422 return false;
423
424 QSSGExtensionId extensionId = m_ext ? QQuick3DExtensionHelpers::getExtensionId(*m_ext) : QSSGExtensionId { };
425 if (QQuick3DExtensionHelpers::isNull(extensionId))
426 return false;
427
428 // TODO: should know if new properties are added or removed
429 bool needsRebuild = false;
430 if (dirtyFlag & QQuick3DQuadTextureProvider::Dirty::Dimensions)
431 needsRebuild = true;
432 if (dirtyFlag & QQuick3DQuadTextureProvider::Dirty::Format)
433 needsRebuild = true;
434 if (dirtyFlag & QQuick3DQuadTextureProvider::Dirty::FragmentShader)
435 needsRebuild = true;
436 if (m_shaderPipeline && !needsRebuild)
437 return dirtyFlag & QQuick3DQuadTextureProvider::Dirty::TrackedProperty;
438
439 if (!m_shaderPipeline || dirtyFlag & QQuick3DQuadTextureProvider::Dirty::FragmentShader) {
440 // Run through all inputs and if any texture is not created yet wait until next frame.
441 // The problem with textures not created yet is that they are assumed to be sampler2DArray in glsl but
442 // that could cause shader compilation failures so we need to know the actual type before trying to compile.
443 for (const auto &u : std::as_const(propertyUniforms)) {
444 if (u.shaderDataType == QSSGRenderShaderValue::Texture) {
445 QSSGRenderImage *image = u.value.value<QSSGRenderImage *>();
446 const QSSGRenderImageTexture texture = image ? bufferManager->loadRenderImage(image) : QSSGRenderImageTexture { };
447 if (!image || !texture.m_texture) {
448 return false;
449 }
450 }
451 }
452
453 QByteArray fragmentShaderCode = (!fragmentShaderSource.isEmpty() ? fragmentShaderSource : fallbackFragmentShaderStr)
454 + mainFragmentSnippet;
455
456 QSSGShaderCustomMaterialAdapter::StringPairList baseUniforms;
457 for (const auto &u : std::as_const(propertyUniforms))
458 baseUniforms.append({ u.typeName, u.name });
459
460 baseUniforms.append({ "vec2", "qt_outputSize" });
461
462 QSSGShaderCustomMaterialAdapter::StringPairList baseInputOutputs;
463 baseInputOutputs.append({ "vec2", "qt_inputUV" });
464
465 QByteArray vertexShader = generateFinalVertexShaderCode(rhiCtx->rhi(), baseUniforms, baseInputOutputs);
466 QByteArray fragmentShader = generateFinalFragmentShaderCode(fragmentShaderCode, baseUniforms, baseInputOutputs);
467
468 QSSGRenderContextInterface *sgContext = data.contextInterface();
469
470 m_shaderPipeline = compileShader(sgContext, shaderPathKey, vertexShader, fragmentShader);
471 // If there's an issue compiling try using the fallback fragment shader. Might be something wrong with the
472 // provided fragment shader, e.g. syntax error. This avoids asserting at runtime.
473 if (!m_shaderPipeline) {
474 fragmentShaderCode = fallbackFragmentShaderStr + mainFragmentSnippet;
475 fragmentShader = generateFinalFragmentShaderCode(fragmentShaderCode, baseUniforms, baseInputOutputs);
476 m_shaderPipeline = compileShader(sgContext, shaderPathKey, vertexShader, fragmentShader);
477 }
478 }
479
480 QRhi *rhi = rhiCtx->rhi();
481
482 if (!m_outputTexture || dirtyFlag & QQuick3DQuadTextureProvider::Dirty::Dimensions
483 || dirtyFlag & QQuick3DQuadTextureProvider::Dirty::Format) {
484 // Workaround: Preserve the previous texture until the end of the frame.
485 //
486 // QuadTextureProviders are rendered sequentially. If recreating the output
487 // texture destroys the old QRhiTexture immediately, consumers that have not
488 // rendered yet may still hold a pointer to the previous texture, resulting in
489 // a dangling reference during the current frame.
490 //
491 // By the next frame, surfaceChanged() will have propagated and consumers are
492 // expected to have updated to the new texture. The old texture is then released
493 // in resetForFrame().
494 if (m_outputTexture)
495 m_outputTextureOld = std::move(m_outputTexture);
496 m_outputTexture.reset(rhi->newTexture(rhiTextureFormatFromTextureDataFormat(format), QSize(width, height), 1, QRhiTexture::RenderTarget));
497 m_outputTexture->create();
498
499 m_renderTarget.reset(rhi->newTextureRenderTarget({ m_outputTexture.get() }));
500 m_renderPassDesc.reset(m_renderTarget->newCompatibleRenderPassDescriptor());
501 m_renderTarget->setRenderPassDescriptor(m_renderPassDesc.get());
502 m_renderTarget->create();
503
504 QSSGRenderExtensionHelpers::registerRenderResult(data, extensionId, m_outputTexture.get());
505
506 m_graphicsPipeline.reset();
507 }
508
509 return true;
510}
511
512void QSSGQuadTextureProvider::prepareRender(QSSGFrameData &data)
513{
514 QSSGRenderContextInterface *ctxIfx = data.contextInterface();
515 QSSGRhiContext *rhiCtx = ctxIfx->rhiContext().get();
516 if (!rhiCtx || !m_shaderPipeline)
517 return;
518
519 QSSGRhiContextPrivate *rhiCtxD = QSSGRhiContextPrivate::get(rhiCtx);
520 QRhi *rhi = rhiCtx->rhi();
521
522 QSSGRhiDrawCallData *dcd = &rhiCtxD->drawCallData({ (void *)this, nullptr, nullptr, 0 });
523 if (!dcd->ubuf) {
524 const int uniformStride = rhiCtx->rhi()->ubufAligned(m_shaderPipeline->ub0Size());
525 dcd->ubuf = rhiCtx->rhi()->newBuffer(QRhiBuffer::Dynamic, QRhiBuffer::UniformBuffer, uniformStride);
526 dcd->ubuf->create();
527 }
528
529 const bool trackedPropertyDirty = dirtyFlag & QQuick3DQuadTextureProvider::Dirty::TrackedProperty;
530 const bool dimensionsDirty = dirtyFlag & QQuick3DQuadTextureProvider::Dirty::Dimensions;
531 const bool pipelineDirty = !m_graphicsPipeline || (dirtyFlag & QQuick3DQuadTextureProvider::Dirty::Format)
532 || (dirtyFlag & QQuick3DQuadTextureProvider::Dirty::FragmentShader);
533 // SRB only needs rebuild when texture bindings may have changed
534 const bool srbDirty = pipelineDirty || trackedPropertyDirty || !m_srb;
535
536 // Update uniforms / textures
537 if (trackedPropertyDirty || dimensionsDirty) {
538
539 char *ubufData = dcd->ubuf->beginFullDynamicBufferUpdateForCurrentFrame();
540
541 if (trackedPropertyDirty) {
542 // rebuild transient texture list
543 m_shaderPipeline->resetExtraTextures();
544
545 QSSGBufferManager *bufferManager = ctxIfx->bufferManager().get();
546
547 for (const auto &u : std::as_const(propertyUniforms)) {
548 m_shaderPipeline->setShaderResources(ubufData, *bufferManager, u.name, u.value, u.shaderDataType);
549 }
550 }
551
552 if (dimensionsDirty) {
553 m_shaderPipeline->setUniformValue(ubufData, "qt_outputSize", QVector2D(width, height), QSSGRenderShaderValue::Vec2);
554 }
555 dcd->ubuf->endFullDynamicBufferUpdateForCurrentFrame();
556 }
557
558 // Static geometry (create once)
559 if (!m_vertexBuffer && !m_indexBuffer) {
560 // 1 quad (2 trianges), pos + uv. 4 vertices, 5 values each (x, y, z, u, v)
561 m_vertexBuffer.reset(rhi->newBuffer(QRhiBuffer::Immutable, QRhiBuffer::VertexBuffer, 5 * 4 * sizeof(float)));
562 m_vertexBuffer->create();
563
564 // 6 indexes (2 triangles)
565 m_indexBuffer.reset(rhi->newBuffer(QRhiBuffer::Immutable, QRhiBuffer::IndexBuffer, 6 * sizeof(uint16_t)));
566 m_indexBuffer->create();
567 QRhiResourceUpdateBatch *updates = rhi->nextResourceUpdateBatch();
568 updates->uploadStaticBuffer(m_vertexBuffer.get(), g_vertexData);
569 updates->uploadStaticBuffer(m_indexBuffer.get(), g_indexData);
570 rhiCtx->commandBuffer()->resourceUpdate(updates);
571 }
572
573 // SRB rebuild
574 if (srbDirty) {
575 rhiCtxD->releaseCachedSrb(m_srbBindings);
576 m_srbBindings = QSSGRhiShaderResourceBindingList();
577 m_srbBindings.addUniformBuffer(0, QRhiShaderResourceBinding::FragmentStage, dcd->ubuf);
578
579 // Add textures (mostly copy pasta from addOpaqueDepthPrePassBindings())
580 int maxSamplerBinding = -1;
581 QVector<QShaderDescription::InOutVariable> samplerVars = m_shaderPipeline->fragmentStage()->shader().description().combinedImageSamplers();
582 for (const QShaderDescription::InOutVariable &var :
583 m_shaderPipeline->vertexStage()->shader().description().combinedImageSamplers()) {
584 auto it = std::find_if(samplerVars.cbegin(), samplerVars.cend(), [&var](const QShaderDescription::InOutVariable &v) {
585 return var.binding == v.binding;
586 });
587 if (it == samplerVars.cend())
588 samplerVars.append(var);
589 }
590 for (const QShaderDescription::InOutVariable &var : std::as_const(samplerVars))
591 maxSamplerBinding = qMax(maxSamplerBinding, var.binding);
592
593 if (maxSamplerBinding >= 0) {
594 // custom property textures
595 int customTexCount = m_shaderPipeline->extraTextureCount();
596 for (int i = 0; i < customTexCount; ++i) {
597 const QSSGRhiTexture &t(m_shaderPipeline->extraTextureAt(i));
598 const int samplerBinding = m_shaderPipeline->bindingForTexture(t.name);
599 if (samplerBinding >= 0) {
600 QRhiSampler *sampler = rhiCtx->sampler(t.samplerDesc);
601 m_srbBindings.addTexture(samplerBinding, QRhiShaderResourceBinding::FragmentStage, t.texture, sampler);
602 }
603 }
604 }
605
606 m_srb = rhiCtxD->srb(m_srbBindings);
607 }
608
609 // Graphics pipeline rebuild
610 if (pipelineDirty) {
611
612 m_graphicsPipeline.reset(rhi->newGraphicsPipeline());
613 m_graphicsPipeline->setShaderStages({ *m_shaderPipeline->vertexStage(), *m_shaderPipeline->fragmentStage() });
614
615 // 2 Attributes, Position (vec3) + UV (vec2)
616 QRhiVertexInputLayout inputLayout;
617 inputLayout.setBindings({ { 5 * sizeof(float) } });
618 inputLayout.setAttributes({ { 0, 0, QRhiVertexInputAttribute::Float3, 0 },
619 { 0, 1, QRhiVertexInputAttribute::Float2, 3 * sizeof(float) } });
620 m_graphicsPipeline->setVertexInputLayout(inputLayout);
621 m_graphicsPipeline->setShaderResourceBindings(m_srb);
622 m_graphicsPipeline->setRenderPassDescriptor(m_renderPassDesc.get());
623
624 m_graphicsPipeline->create();
625 }
626
627 dirtyFlag = 0;
628}
629
630void QSSGQuadTextureProvider::render(QSSGFrameData &data)
631{
632 const auto &ctxIfx = data.contextInterface();
633 const auto &rhiCtx = ctxIfx->rhiContext();
634 if (!rhiCtx)
635 return;
636
637 QRhiCommandBuffer *cb = rhiCtx->commandBuffer();
638 cb->debugMarkBegin(QByteArrayLiteral("Quick3D Quad Texture Provider"));
639
640 cb->beginPass(m_renderTarget.get(), Qt::transparent, { 1.0f, 0 }, nullptr, rhiCtx->commonPassFlags());
641 cb->setViewport(QRhiViewport(0, 0, width, height));
642 cb->setScissor(QRhiScissor(0, 0, width, height));
643
644 cb->setGraphicsPipeline(m_graphicsPipeline.get());
645
646 QSSGRhiContextPrivate *rhiCtxD = QSSGRhiContextPrivate::get(rhiCtx.get());
647 auto srb = rhiCtxD->srb(m_srbBindings);
648 cb->setShaderResources(srb);
649
650 QRhiCommandBuffer::VertexInput vb(m_vertexBuffer.get(), 0);
651 cb->setVertexInput(0, 1, &vb, m_indexBuffer.get(), QRhiCommandBuffer::IndexFormat::IndexUInt16);
652 cb->drawIndexed(6);
653 cb->endPass();
654
655 cb->debugMarkEnd();
656
657}
658
660 m_outputTextureOld.reset();
661}
662
663QQuick3DQuadTextureProvider::QQuick3DQuadTextureProvider(QQuick3DObject *parent)
664 : QQuick3DTextureProviderExtension(parent)
665 , QQuick3DPropertyChangedTracker(this, QQuick3DSuperClassInfo<QQuick3DQuadTextureProvider>())
666{
667}
668
669QQuick3DQuadTextureProvider::~QQuick3DQuadTextureProvider() { }
670
671QSSGRenderGraphObject *QQuick3DQuadTextureProvider::updateSpatialNode(QSSGRenderGraphObject *node)
672{
673 auto n = static_cast<QSSGQuadTextureProvider *>(node);
674
675 if (!n) {
676 n = new QSSGQuadTextureProvider(this);
677 }
678
679 if (m_dirtyFlag & Dirty::Dimensions) {
680 n->width = m_width;
681 n->height = m_height;
682 }
683
684 if (m_dirtyFlag & Dirty::Format)
685 n->format = m_format;
686
687 if (m_dirtyFlag & Dirty::FragmentShader) {
688 const QQmlContext *context = qmlContext(this);
689 n->fragmentShaderSource = !m_fragmentShaderCode.isEmpty() ? m_fragmentShaderCode.toUtf8()
690 : !m_fragmentShader.isEmpty() ? QSSGShaderUtils::resolveShader(m_fragmentShader, context, n->shaderPathKey)
691 : QByteArray();
692 }
693
694 if (m_dirtyFlag & Dirty::TrackedProperty) {
695 n->propertyUniforms = extractProperties();
696 }
697
698 if (m_dirtyFlag != 0)
699 emit surfaceChanged();
700
701 n->dirtyFlag = m_dirtyFlag;
702 m_dirtyFlag = 0;
703
704 return n;
705}
706
707void QQuick3DQuadTextureProvider::markTrackedPropertyDirty(QMetaProperty property, DirtyPropertyHint hint)
708{
709 Q_UNUSED(property);
710 Q_UNUSED(hint);
711 markDirty(Dirty::TrackedProperty);
712}
713
714void QQuick3DQuadTextureProvider::markDirty(Dirty v)
715{
716 m_dirtyFlag |= v;
717 update();
718}
719
720QUrl QQuick3DQuadTextureProvider::fragmentShader() const
721{
722 return m_fragmentShader;
723}
724
725void QQuick3DQuadTextureProvider::setFragmentShader(const QUrl &newFragmentShader)
726{
727 if (m_fragmentShader == newFragmentShader)
728 return;
729 m_fragmentShader = newFragmentShader;
730 emit fragmentShaderChanged();
731 markDirty(Dirty::FragmentShader);
732}
733
734QString QQuick3DQuadTextureProvider::fragmentShaderCode() const
735{
736 return m_fragmentShaderCode;
737}
738
739void QQuick3DQuadTextureProvider::setFragmentShaderCode(const QString &newFragmentShaderCode)
740{
741 if (m_fragmentShaderCode == newFragmentShaderCode)
742 return;
743 m_fragmentShaderCode = newFragmentShaderCode;
744 emit fragmentShaderCodeChanged();
745 markDirty(Dirty::FragmentShader);
746}
747
748
749int QQuick3DQuadTextureProvider::width() const
750{
751 return m_width;
752}
753
754void QQuick3DQuadTextureProvider::setWidth(int newWidth)
755{
756 newWidth = qMax(1, newWidth);
757 if (m_width == newWidth)
758 return;
759 m_width = newWidth;
760 emit widthChanged();
761 markDirty(Dirty::Dimensions);
762}
763
764int QQuick3DQuadTextureProvider::height() const
765{
766 return m_height;
767}
768
769void QQuick3DQuadTextureProvider::setHeight(int newHeight)
770{
771 newHeight = qMax(1, newHeight);
772 if (m_height == newHeight)
773 return;
774 m_height = newHeight;
775 emit heightChanged();
776 markDirty(Dirty::Dimensions);
777}
778
779QQuick3DTextureData::Format QQuick3DQuadTextureProvider::format() const
780{
781 return m_format;
782}
783
784void QQuick3DQuadTextureProvider::setFormat(QQuick3DTextureData::Format newFormat)
785{
786 if (m_format == newFormat)
787 return;
788 m_format = newFormat;
789 emit formatChanged();
790 markDirty(Dirty::Format);
791}
792
793QT_END_NAMESPACE
QSSGQuadTextureProvider(QQuick3DQuadTextureProvider *ext)
void render(QSSGFrameData &data) override
Record the render pass.
QList< QSSGBaseTypeProperty > propertyUniforms
QQuick3DQuadTextureProvider::DirtyT dirtyFlag
void prepareRender(QSSGFrameData &data) override
Prepare data for rendering.
void resetForFrame() override
Called each time a new frame starts.
bool prepareData(QSSGFrameData &data) override
Called after scene data is collected, but before any render data or rendering in the current frame ha...
QQuick3DTextureData::Format format
Combined button and popup list for selecting options.
static const QByteArray mainVertexSnippet
static const QByteArray mainFragmentSnippet
static const QByteArray fallbackFragmentShaderStr
static constexpr uint16_t g_indexData[]
static QT_BEGIN_NAMESPACE constexpr float g_vertexData[]
\qmltype QuadTextureProvider \nativetype QQuick3DQuadTextureProvider \inqmlmodule QtQuick3D....
static void insertVertexMainArgs(QByteArray &snippet)
static const QByteArray mainVertexSnippetFlipped