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
142 \note Providing a vertex shader is not supported, only a fragment shader.
143
144 \note If \l Texture properties are provided it will not render until the dependent textures are available.
145
146 \note There is currently no support for adding / removing properties at runtime, just modifying the original ones.
147
148 \sa ShaderEffect
149 */
150
151/*!
152 \qmlproperty url QuadTextureProvider::fragmentShader
153 \since 6.12
154
155 Specifies the file with the snippet of custom fragment shader code.
156
157 The value is a URL and must either be a local file or use the qrc scheme to
158 access files embedded via the Qt resource system. Relative file paths
159 (without a scheme) are also accepted, in which case the file is treated as
160 relative to the component (the \c{.qml} file).
161
162 \warning Shader snippets are assumed to be trusted content. Application
163 developers are advised to carefully consider the potential implications
164 before allowing the loading of user-provided content that is not part of the
165 application.
166
167 \note If set, fragmentShaderCode will take precedence over fragmentShader.
168
169 \sa fragmentShaderCode
170*/
171
172/*!
173 \qmlproperty string QuadTextureProvider::fragmentShaderCode
174 \since 6.12
175
176 Specifies a snippet of custom fragment shader code.
177
178 Used as a way to inline shader code as a string instead of providing a file.
179
180 \note If set, this property will take precedence over \l fragmentShader.
181
182 \sa fragmentShader
183*/
184
185/*!
186 \qmlproperty int QuadTextureProvider::width
187 \since 6.12
188 \default 128
189
190 Specifies the width in pixels of the output texture.
191
192 \sa height
193*/
194
195/*!
196 \qmlproperty int QuadTextureProvider::height
197 \since 6.12
198 \default 128
199
200 Specifies the height in pixels of the output texture.
201
202 \sa width
203*/
204
205/*!
206 \qmlproperty enumeration QuadTextureProvider::format
207 \since 6.12
208 \default TexureData.RGBA16F
209
210 This property holds the format of the output texture.
211
212 \value TexureData.RGBA8 The color format is considered as 8-bit integer in R, G, B and alpha channels.
213 \value TexureData.RGBA16F The color format is considered as 16-bit float in R,G,B and alpha channels.
214 \value TexureData.RGBA32F The color format is considered as 32-bit float in R, G, B and alpha channels.
215 \value TexureData.RGBE8 The color format is considered as 8-bit mantissa in the R, G, and B channels and 8-bit shared exponent.
216 \value TexureData.R8 The color format is considered as 8-bit integer in R channel.
217 \value TexureData.R16 The color format is considered as 16-bit integer in R channel.
218 \value TexureData.R16F The color format is considered as 16-bit float in R channel.
219 \value TexureData.R32F The color format is considered as 32-bit float R channel.
220
221 \note With the exception of \c TexureData.RGBA8, not every format is supported at runtime as this
222 depends on which backend is being used as well which hardware is being used.
223*/
224
226
227static constexpr float g_vertexData[] = { -1.0f, -1.0f, 0.0f, 0.0f, 0.0f, 1.0f, -1.0f, 0.0f, 1.0f, 0.0f,
228 1.0f, 1.0f, 0.0f, 1.0f, 1.0f, -1.0f, 1.0f, 0.0f, 0.0f, 1.0f };
229
230static constexpr uint16_t g_indexData[] = { 0, 1, 2, 0, 2, 3 };
231
233void main()
234{
235 qt_inputUV = attr_uv;
236 gl_Position = vec4(attr_pos, 0.0, 1.0);
237}
238)";
239
241void main()
242{
243 qt_inputUV = attr_uv;
244 qt_inputUV.y = 1.0 - qt_inputUV.y;
245 gl_Position = vec4(attr_pos, 0.0, 1.0);
246}
247)";
248
250void main()
251{
252 qt_customMain();
253}
254)";
255
257void MAIN()
258{
259 FRAGCOLOR = vec4(1.0, 0.0, 1.0, 1.0);
260}
261)";
262
263static inline void insertVertexMainArgs(QByteArray &snippet)
264{
265 static const char *argKey = "/*%QT_ARGS_MAIN%*/";
266 const int argKeyLen = int(strlen(argKey));
267 const int argKeyPos = snippet.indexOf(argKey);
268 if (argKeyPos >= 0)
269 snippet = snippet.left(argKeyPos) + QByteArrayLiteral("inout vec3 VERTEX") + snippet.mid(argKeyPos + argKeyLen);
270}
271
272namespace {
273
274QRhiTexture::Format rhiTextureFormatFromTextureDataFormat(QQuick3DTextureData::Format format)
275{
276 switch (format) {
277 case QQuick3DTextureData::RGBA8:
278 return QRhiTexture::Format::RGBA8;
279 case QQuick3DTextureData::RGBA16F:
280 return QRhiTexture::Format::RGBA16F;
281 case QQuick3DTextureData::RGBA32F:
282 return QRhiTexture::Format::RGBA32F;
283 case QQuick3DTextureData::RGBE8:
284 return QRhiTexture::Format::RGBA8;
285 case QQuick3DTextureData::R8:
286 return QRhiTexture::Format::R8;
287 case QQuick3DTextureData::R16:
288 return QRhiTexture::Format::R16;
289 case QQuick3DTextureData::R16F:
290 return QRhiTexture::Format::R16F;
291 case QQuick3DTextureData::R32F:
292 return QRhiTexture::Format::R32F;
293 default:
294 return QRhiTexture::Format::RGBA8;
295 }
296 Q_UNREACHABLE_RETURN(QRhiTexture::Format::RGBA8);
297}
298
299QByteArray generateFinalVertexShaderCode(QRhi *rhi,
300 QSSGShaderCustomMaterialAdapter::StringPairList uniforms,
301 QSSGShaderCustomMaterialAdapter::StringPairList varyings)
302{
303 // Handle d3d flipped textures
304 const bool doFlip = rhi->isYUpInNDC() && !rhi->isYUpInFramebuffer();
305 QByteArray vertexShader = doFlip ? mainVertexSnippetFlipped : mainVertexSnippet;
306
307 QSSGShaderCustomMaterialAdapter::ShaderCodeAndMetaData result;
308 QByteArray buf;
309 QSSGShaderCustomMaterialAdapter::CustomShaderPrepWorkData scratch;
310 QSSGShaderCustomMaterialAdapter::beginPrepareCustomShader(&scratch, &result, vertexShader, QSSGShaderCache::ShaderType::Vertex, false);
311 QSSGShaderCustomMaterialAdapter::finishPrepareCustomShader(&buf,
312 scratch,
313 result,
314 QSSGShaderCache::ShaderType::Vertex,
315 false,
316 uniforms,
317 { },
318 varyings,
319 { },
320 { });
321 insertVertexMainArgs(result.first);
322 return result.first + buf;
323}
324
325QByteArray generateFinalFragmentShaderCode(const QByteArray &baseFragmentShaderCode,
326 QSSGShaderCustomMaterialAdapter::StringPairList uniforms,
327 QSSGShaderCustomMaterialAdapter::StringPairList varyings)
328{
329 QSSGShaderCustomMaterialAdapter::ShaderCodeAndMetaData result;
330 QByteArray buf;
331 QSSGShaderCustomMaterialAdapter::CustomShaderPrepWorkData scratch;
332 QSSGShaderCustomMaterialAdapter::beginPrepareCustomShader(&scratch, &result, baseFragmentShaderCode, QSSGShaderCache::ShaderType::Fragment, false);
333 QSSGShaderCustomMaterialAdapter::finishPrepareCustomShader(&buf,
334 scratch,
335 result,
336 QSSGShaderCache::ShaderType::Fragment,
337 false,
338 uniforms,
339 varyings,
340 { },
341 { },
342 { });
343 return result.first + buf;
344}
345
346QSSGRhiShaderPipelinePtr compileShader(QSSGRenderContextInterface *sgContext,
347 const QByteArray &shaderPathKey,
348 const QByteArray &vertexShader,
349 const QByteArray &fragmentShader)
350{
351 QSSGProgramGenerator *generator = sgContext->shaderProgramGenerator().get();
352 QSSGShaderLibraryManager *shaderLib = sgContext->shaderLibraryManager().get();
353 QSSGShaderCache *shaderCache = sgContext->shaderCache().get();
354
355 generator->beginProgram();
356 auto vertex = generator->getStage(QSSGShaderGeneratorStage::Vertex);
357 vertex->addIncoming("attr_pos", "vec2");
358 vertex->addIncoming("attr_uv", "vec2");
359 vertex->append(vertexShader);
360
361 generator->getStage(QSSGShaderGeneratorStage::Fragment)->append(fragmentShader);
362
363 QSSGShaderFeatures features;
364 const QByteArray key = shaderPathKey + ':'
365 + QCryptographicHash::hash(QByteArray(vertexShader + fragmentShader), QCryptographicHash::Algorithm::Sha1)
366 .toHex();
367 return generator->compileGeneratedRhiShader(key, features, *shaderLib, *shaderCache, QSSGRhiShaderPipeline::UsedWithoutIa, { }, 1, false);
368}
369
370} // namespace
371
373{
374public:
377 bool prepareData(QSSGFrameData &data) override;
378 void prepareRender(QSSGFrameData &data) override;
379 void render(QSSGFrameData &data) override;
381
383
387 int width = 128;
388 int height = 128;
389
390 QByteArray shaderPathKey = "quad texture provider --";
391
392private:
393 QPointer<QQuick3DQuadTextureProvider> m_ext;
394
395 QSSGRhiShaderResourceBindingList m_srbBindings;
396 QSSGRhiShaderPipelinePtr m_shaderPipeline;
397
398 std::unique_ptr<QRhiBuffer> m_vertexBuffer;
399 std::unique_ptr<QRhiBuffer> m_indexBuffer;
400
401 std::unique_ptr<QRhiTexture> m_outputTexture;
402 std::unique_ptr<QRhiTexture> m_outputTextureOld;
403 std::unique_ptr<QRhiTextureRenderTarget> m_renderTarget;
404 std::unique_ptr<QRhiRenderPassDescriptor> m_renderPassDesc;
405
406 std::unique_ptr<QRhiGraphicsPipeline> m_graphicsPipeline;
407 QRhiShaderResourceBindings* m_srb = nullptr; // not owned
408};
409
411
413
414bool QSSGQuadTextureProvider::prepareData(QSSGFrameData &data)
415{
416 QSSGRenderContextInterface *ctxIfx = data.contextInterface();
417 auto bufferManager = ctxIfx->bufferManager().get();
418 QSSGRhiContext *rhiCtx = ctxIfx->rhiContext().get();
419 if (!rhiCtx)
420 return false;
421
422 QSSGExtensionId extensionId = m_ext ? QQuick3DExtensionHelpers::getExtensionId(*m_ext) : QSSGExtensionId { };
423 if (QQuick3DExtensionHelpers::isNull(extensionId))
424 return false;
425
426 // TODO: should know if new properties are added or removed
427 bool needsRebuild = false;
428 if (dirtyFlag & QQuick3DQuadTextureProvider::Dirty::Dimensions)
429 needsRebuild = true;
430 if (dirtyFlag & QQuick3DQuadTextureProvider::Dirty::Format)
431 needsRebuild = true;
432 if (dirtyFlag & QQuick3DQuadTextureProvider::Dirty::FragmentShader)
433 needsRebuild = true;
434 if (m_shaderPipeline && !needsRebuild)
435 return dirtyFlag & QQuick3DQuadTextureProvider::Dirty::TrackedProperty;
436
437 if (!m_shaderPipeline || dirtyFlag & QQuick3DQuadTextureProvider::Dirty::FragmentShader) {
438 // Run through all inputs and if any texture is not created yet wait until next frame.
439 // The problem with textures not created yet is that they are assumed to be sampler2DArray in glsl but
440 // that could cause shader compilation failures so we need to know the actual type before trying to compile.
441 for (const auto &u : std::as_const(propertyUniforms)) {
442 if (u.shaderDataType == QSSGRenderShaderValue::Texture) {
443 QSSGRenderImage *image = u.value.value<QSSGRenderImage *>();
444 const QSSGRenderImageTexture texture = image ? bufferManager->loadRenderImage(image) : QSSGRenderImageTexture { };
445 if (!image || !texture.m_texture) {
446 return false;
447 }
448 }
449 }
450
451 QByteArray fragmentShaderCode = (!fragmentShaderSource.isEmpty() ? fragmentShaderSource : fallbackFragmentShaderStr)
452 + mainFragmentSnippet;
453
454 QSSGShaderCustomMaterialAdapter::StringPairList baseUniforms;
455 for (const auto &u : std::as_const(propertyUniforms))
456 baseUniforms.append({ u.typeName, u.name });
457
458 baseUniforms.append({ "vec2", "qt_outputSize" });
459
460 QSSGShaderCustomMaterialAdapter::StringPairList baseInputOutputs;
461 baseInputOutputs.append({ "vec2", "qt_inputUV" });
462
463 QByteArray vertexShader = generateFinalVertexShaderCode(rhiCtx->rhi(), baseUniforms, baseInputOutputs);
464 QByteArray fragmentShader = generateFinalFragmentShaderCode(fragmentShaderCode, baseUniforms, baseInputOutputs);
465
466 QSSGRenderContextInterface *sgContext = data.contextInterface();
467
468 m_shaderPipeline = compileShader(sgContext, shaderPathKey, vertexShader, fragmentShader);
469 // If there's an issue compiling try using the fallback fragment shader. Might be something wrong with the
470 // provided fragment shader, e.g. syntax error. This avoids asserting at runtime.
471 if (!m_shaderPipeline) {
472 fragmentShaderCode = fallbackFragmentShaderStr + mainFragmentSnippet;
473 fragmentShader = generateFinalFragmentShaderCode(fragmentShaderCode, baseUniforms, baseInputOutputs);
474 m_shaderPipeline = compileShader(sgContext, shaderPathKey, vertexShader, fragmentShader);
475 }
476 }
477
478 QRhi *rhi = rhiCtx->rhi();
479
480 if (!m_outputTexture || dirtyFlag & QQuick3DQuadTextureProvider::Dirty::Dimensions
481 || dirtyFlag & QQuick3DQuadTextureProvider::Dirty::Format) {
482 // Workaround: Preserve the previous texture until the end of the frame.
483 //
484 // QuadTextureProviders are rendered sequentially. If recreating the output
485 // texture destroys the old QRhiTexture immediately, consumers that have not
486 // rendered yet may still hold a pointer to the previous texture, resulting in
487 // a dangling reference during the current frame.
488 //
489 // By the next frame, surfaceChanged() will have propagated and consumers are
490 // expected to have updated to the new texture. The old texture is then released
491 // in resetForFrame().
492 if (m_outputTexture)
493 m_outputTextureOld = std::move(m_outputTexture);
494 m_outputTexture.reset(rhi->newTexture(rhiTextureFormatFromTextureDataFormat(format), QSize(width, height), 1, QRhiTexture::RenderTarget));
495 m_outputTexture->create();
496
497 m_renderTarget.reset(rhi->newTextureRenderTarget({ m_outputTexture.get() }));
498 m_renderPassDesc.reset(m_renderTarget->newCompatibleRenderPassDescriptor());
499 m_renderTarget->setRenderPassDescriptor(m_renderPassDesc.get());
500 m_renderTarget->create();
501
502 QSSGRenderExtensionHelpers::registerRenderResult(data, extensionId, m_outputTexture.get());
503
504 m_graphicsPipeline.reset();
505 }
506
507 return true;
508}
509
510void QSSGQuadTextureProvider::prepareRender(QSSGFrameData &data)
511{
512 QSSGRenderContextInterface *ctxIfx = data.contextInterface();
513 QSSGRhiContext *rhiCtx = ctxIfx->rhiContext().get();
514 if (!rhiCtx || !m_shaderPipeline)
515 return;
516
517 QSSGRhiContextPrivate *rhiCtxD = QSSGRhiContextPrivate::get(rhiCtx);
518 QRhi *rhi = rhiCtx->rhi();
519
520 QSSGRhiDrawCallData *dcd = &rhiCtxD->drawCallData({ (void *)this, nullptr, nullptr, 0 });
521 if (!dcd->ubuf) {
522 const int uniformStride = rhiCtx->rhi()->ubufAligned(m_shaderPipeline->ub0Size());
523 dcd->ubuf = rhiCtx->rhi()->newBuffer(QRhiBuffer::Dynamic, QRhiBuffer::UniformBuffer, uniformStride);
524 dcd->ubuf->create();
525 }
526
527 const bool trackedPropertyDirty = dirtyFlag & QQuick3DQuadTextureProvider::Dirty::TrackedProperty;
528 const bool dimensionsDirty = dirtyFlag & QQuick3DQuadTextureProvider::Dirty::Dimensions;
529 const bool pipelineDirty = !m_graphicsPipeline || (dirtyFlag & QQuick3DQuadTextureProvider::Dirty::Format)
530 || (dirtyFlag & QQuick3DQuadTextureProvider::Dirty::FragmentShader);
531 // SRB only needs rebuild when texture bindings may have changed
532 const bool srbDirty = pipelineDirty || trackedPropertyDirty || !m_srb;
533
534 // Update uniforms / textures
535 if (trackedPropertyDirty || dimensionsDirty) {
536
537 char *ubufData = dcd->ubuf->beginFullDynamicBufferUpdateForCurrentFrame();
538
539 if (trackedPropertyDirty) {
540 // rebuild transient texture list
541 m_shaderPipeline->resetExtraTextures();
542
543 QSSGBufferManager *bufferManager = ctxIfx->bufferManager().get();
544
545 for (const auto &u : std::as_const(propertyUniforms)) {
546 m_shaderPipeline->setShaderResources(ubufData, *bufferManager, u.name, u.value, u.shaderDataType);
547 }
548 }
549
550 if (dimensionsDirty) {
551 m_shaderPipeline->setUniformValue(ubufData, "qt_outputSize", QVector2D(width, height), QSSGRenderShaderValue::Vec2);
552 }
553 dcd->ubuf->endFullDynamicBufferUpdateForCurrentFrame();
554 }
555
556 // Static geometry (create once)
557 if (!m_vertexBuffer && !m_indexBuffer) {
558 // 1 quad (2 trianges), pos + uv. 4 vertices, 5 values each (x, y, z, u, v)
559 m_vertexBuffer.reset(rhi->newBuffer(QRhiBuffer::Immutable, QRhiBuffer::VertexBuffer, 5 * 4 * sizeof(float)));
560 m_vertexBuffer->create();
561
562 // 6 indexes (2 triangles)
563 m_indexBuffer.reset(rhi->newBuffer(QRhiBuffer::Immutable, QRhiBuffer::IndexBuffer, 6 * sizeof(uint16_t)));
564 m_indexBuffer->create();
565 QRhiResourceUpdateBatch *updates = rhi->nextResourceUpdateBatch();
566 updates->uploadStaticBuffer(m_vertexBuffer.get(), g_vertexData);
567 updates->uploadStaticBuffer(m_indexBuffer.get(), g_indexData);
568 rhiCtx->commandBuffer()->resourceUpdate(updates);
569 }
570
571 // SRB rebuild
572 if (srbDirty) {
573 rhiCtxD->releaseCachedSrb(m_srbBindings);
574 m_srbBindings = QSSGRhiShaderResourceBindingList();
575 m_srbBindings.addUniformBuffer(0, QRhiShaderResourceBinding::FragmentStage, dcd->ubuf);
576
577 // Add textures (mostly copy pasta from addOpaqueDepthPrePassBindings())
578 int maxSamplerBinding = -1;
579 QVector<QShaderDescription::InOutVariable> samplerVars = m_shaderPipeline->fragmentStage()->shader().description().combinedImageSamplers();
580 for (const QShaderDescription::InOutVariable &var :
581 m_shaderPipeline->vertexStage()->shader().description().combinedImageSamplers()) {
582 auto it = std::find_if(samplerVars.cbegin(), samplerVars.cend(), [&var](const QShaderDescription::InOutVariable &v) {
583 return var.binding == v.binding;
584 });
585 if (it == samplerVars.cend())
586 samplerVars.append(var);
587 }
588 for (const QShaderDescription::InOutVariable &var : std::as_const(samplerVars))
589 maxSamplerBinding = qMax(maxSamplerBinding, var.binding);
590
591 if (maxSamplerBinding >= 0) {
592 // custom property textures
593 int customTexCount = m_shaderPipeline->extraTextureCount();
594 for (int i = 0; i < customTexCount; ++i) {
595 const QSSGRhiTexture &t(m_shaderPipeline->extraTextureAt(i));
596 const int samplerBinding = m_shaderPipeline->bindingForTexture(t.name);
597 if (samplerBinding >= 0) {
598 QRhiSampler *sampler = rhiCtx->sampler(t.samplerDesc);
599 m_srbBindings.addTexture(samplerBinding, QRhiShaderResourceBinding::FragmentStage, t.texture, sampler);
600 }
601 }
602 }
603
604 m_srb = rhiCtxD->srb(m_srbBindings);
605 }
606
607 // Graphics pipeline rebuild
608 if (pipelineDirty) {
609
610 m_graphicsPipeline.reset(rhi->newGraphicsPipeline());
611 m_graphicsPipeline->setShaderStages({ *m_shaderPipeline->vertexStage(), *m_shaderPipeline->fragmentStage() });
612
613 // 2 Attributes, Position (vec3) + UV (vec2)
614 QRhiVertexInputLayout inputLayout;
615 inputLayout.setBindings({ { 5 * sizeof(float) } });
616 inputLayout.setAttributes({ { 0, 0, QRhiVertexInputAttribute::Float3, 0 },
617 { 0, 1, QRhiVertexInputAttribute::Float2, 3 * sizeof(float) } });
618 m_graphicsPipeline->setVertexInputLayout(inputLayout);
619 m_graphicsPipeline->setShaderResourceBindings(m_srb);
620 m_graphicsPipeline->setRenderPassDescriptor(m_renderPassDesc.get());
621
622 m_graphicsPipeline->create();
623 }
624
625 dirtyFlag = 0;
626}
627
628void QSSGQuadTextureProvider::render(QSSGFrameData &data)
629{
630 const auto &ctxIfx = data.contextInterface();
631 const auto &rhiCtx = ctxIfx->rhiContext();
632 if (!rhiCtx)
633 return;
634
635 QRhiCommandBuffer *cb = rhiCtx->commandBuffer();
636 cb->debugMarkBegin(QByteArrayLiteral("Quick3D Quad Texture Provider"));
637
638 cb->beginPass(m_renderTarget.get(), Qt::transparent, { 1.0f, 0 }, nullptr, rhiCtx->commonPassFlags());
639 cb->setViewport(QRhiViewport(0, 0, width, height));
640 cb->setScissor(QRhiScissor(0, 0, width, height));
641
642 cb->setGraphicsPipeline(m_graphicsPipeline.get());
643
644 QSSGRhiContextPrivate *rhiCtxD = QSSGRhiContextPrivate::get(rhiCtx.get());
645 auto srb = rhiCtxD->srb(m_srbBindings);
646 cb->setShaderResources(srb);
647
648 QRhiCommandBuffer::VertexInput vb(m_vertexBuffer.get(), 0);
649 cb->setVertexInput(0, 1, &vb, m_indexBuffer.get(), QRhiCommandBuffer::IndexFormat::IndexUInt16);
650 cb->drawIndexed(6);
651 cb->endPass();
652
653 cb->debugMarkEnd();
654
655}
656
658 m_outputTextureOld.reset();
659}
660
661QQuick3DQuadTextureProvider::QQuick3DQuadTextureProvider(QQuick3DObject *parent)
662 : QQuick3DTextureProviderExtension(parent)
663 , QQuick3DPropertyChangedTracker(this, QQuick3DSuperClassInfo<QQuick3DQuadTextureProvider>())
664{
665}
666
667QQuick3DQuadTextureProvider::~QQuick3DQuadTextureProvider() { }
668
669QSSGRenderGraphObject *QQuick3DQuadTextureProvider::updateSpatialNode(QSSGRenderGraphObject *node)
670{
671 auto n = static_cast<QSSGQuadTextureProvider *>(node);
672
673 if (!n) {
674 n = new QSSGQuadTextureProvider(this);
675 }
676
677 if (m_dirtyFlag & Dirty::Dimensions) {
678 n->width = m_width;
679 n->height = m_height;
680 }
681
682 if (m_dirtyFlag & Dirty::Format)
683 n->format = m_format;
684
685 if (m_dirtyFlag & Dirty::FragmentShader) {
686 const QQmlContext *context = qmlContext(this);
687 n->fragmentShaderSource = !m_fragmentShaderCode.isEmpty() ? m_fragmentShaderCode.toUtf8()
688 : !m_fragmentShader.isEmpty() ? QSSGShaderUtils::resolveShader(m_fragmentShader, context, n->shaderPathKey)
689 : QByteArray();
690 }
691
692 if (m_dirtyFlag & Dirty::TrackedProperty) {
693 n->propertyUniforms = extractProperties();
694 }
695
696 if (m_dirtyFlag != 0)
697 emit surfaceChanged();
698
699 n->dirtyFlag = m_dirtyFlag;
700 m_dirtyFlag = 0;
701
702 return n;
703}
704
705void QQuick3DQuadTextureProvider::markTrackedPropertyDirty(QMetaProperty property, DirtyPropertyHint hint)
706{
707 Q_UNUSED(property);
708 Q_UNUSED(hint);
709 markDirty(Dirty::TrackedProperty);
710}
711
712void QQuick3DQuadTextureProvider::markDirty(Dirty v)
713{
714 m_dirtyFlag |= v;
715 update();
716}
717
718QUrl QQuick3DQuadTextureProvider::fragmentShader() const
719{
720 return m_fragmentShader;
721}
722
723void QQuick3DQuadTextureProvider::setFragmentShader(const QUrl &newFragmentShader)
724{
725 if (m_fragmentShader == newFragmentShader)
726 return;
727 m_fragmentShader = newFragmentShader;
728 emit fragmentShaderChanged();
729 markDirty(Dirty::FragmentShader);
730}
731
732QString QQuick3DQuadTextureProvider::fragmentShaderCode() const
733{
734 return m_fragmentShaderCode;
735}
736
737void QQuick3DQuadTextureProvider::setFragmentShaderCode(const QString &newFragmentShaderCode)
738{
739 if (m_fragmentShaderCode == newFragmentShaderCode)
740 return;
741 m_fragmentShaderCode = newFragmentShaderCode;
742 emit fragmentShaderCodeChanged();
743 markDirty(Dirty::FragmentShader);
744}
745
746
747int QQuick3DQuadTextureProvider::width() const
748{
749 return m_width;
750}
751
752void QQuick3DQuadTextureProvider::setWidth(int newWidth)
753{
754 newWidth = qMax(1, newWidth);
755 if (m_width == newWidth)
756 return;
757 m_width = newWidth;
758 emit widthChanged();
759 markDirty(Dirty::Dimensions);
760}
761
762int QQuick3DQuadTextureProvider::height() const
763{
764 return m_height;
765}
766
767void QQuick3DQuadTextureProvider::setHeight(int newHeight)
768{
769 newHeight = qMax(1, newHeight);
770 if (m_height == newHeight)
771 return;
772 m_height = newHeight;
773 emit heightChanged();
774 markDirty(Dirty::Dimensions);
775}
776
777QQuick3DTextureData::Format QQuick3DQuadTextureProvider::format() const
778{
779 return m_format;
780}
781
782void QQuick3DQuadTextureProvider::setFormat(QQuick3DTextureData::Format newFormat)
783{
784 if (m_format == newFormat)
785 return;
786 m_format = newFormat;
787 emit formatChanged();
788 markDirty(Dirty::Format);
789}
790
791QT_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