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
qbackingstoredefaultcompositor.cpp
Go to the documentation of this file.
1// Copyright (C) 2021 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
5#include <QtGui/private/qwindow_p.h>
6#include <qpa/qplatformgraphicsbuffer.h>
7#include <QtCore/qfile.h>
8
10
11using namespace Qt::StringLiterals;
12
17
19{
20 m_rhi = nullptr;
21 m_psNoBlend.reset();
22 m_psBlend.reset();
23 m_psPremulBlend.reset();
24 m_samplerNearest.reset();
25 m_samplerLinear.reset();
26 m_vbuf.reset();
27 m_texture.reset();
28 m_widgetQuadData.reset();
29 for (PerQuadData &d : m_textureQuadData)
30 d.reset();
31}
32
33QRhiTexture *QBackingStoreDefaultCompositor::toTexture(const QPlatformBackingStore *backingStore,
34 QRhi *rhi,
35 QRhiResourceUpdateBatch *resourceUpdates,
36 const QRegion &dirtyRegion,
37 QPlatformBackingStore::TextureFlags *flags) const
38{
39 return toTexture(backingStore->toImage(), rhi, resourceUpdates, dirtyRegion, flags);
40}
41
42QRhiTexture *QBackingStoreDefaultCompositor::toTexture(const QImage &sourceImage,
43 QRhi *rhi,
44 QRhiResourceUpdateBatch *resourceUpdates,
45 const QRegion &dirtyRegion,
46 QPlatformBackingStore::TextureFlags *flags) const
47{
48 Q_ASSERT(rhi);
49 Q_ASSERT(resourceUpdates);
50 Q_ASSERT(flags);
51
52 if (!m_rhi) {
53 m_rhi = rhi;
54 } else if (m_rhi != rhi) {
55 qWarning("QBackingStoreDefaultCompositor: the QRhi has changed unexpectedly, this should not happen");
56 return nullptr;
57 }
58
59 QImage image = sourceImage;
60
61 bool needsConversion = false;
62 *flags = {};
63
64 switch (image.format()) {
65 case QImage::Format_ARGB32_Premultiplied:
66 *flags |= QPlatformBackingStore::TexturePremultiplied;
67 Q_FALLTHROUGH();
68 case QImage::Format_RGB32:
69 case QImage::Format_ARGB32:
70 *flags |= QPlatformBackingStore::TextureSwizzle;
71 break;
72 case QImage::Format_RGBA8888_Premultiplied:
73 *flags |= QPlatformBackingStore::TexturePremultiplied;
74 Q_FALLTHROUGH();
75 case QImage::Format_RGBX8888:
76 case QImage::Format_RGBA8888:
77 break;
78 case QImage::Format_BGR30:
79 case QImage::Format_A2BGR30_Premultiplied:
80 // no fast path atm
81 needsConversion = true;
82 break;
83 case QImage::Format_RGB30:
84 case QImage::Format_A2RGB30_Premultiplied:
85 // no fast path atm
86 needsConversion = true;
87 break;
88 default:
89 needsConversion = true;
90 break;
91 }
92
93 if (image.size().isEmpty())
94 return nullptr;
95
96 const bool resized = !m_texture || m_texture->pixelSize() != image.size();
97 if (dirtyRegion.isEmpty() && !resized)
98 return m_texture.get();
99
100 if (needsConversion)
101 image = image.convertToFormat(QImage::Format_RGBA8888);
102 else
103 image.detach(); // if it was just wrapping data, that's no good, we need ownership, so detach
104
105 if (resized) {
106 if (!m_texture)
107 m_texture.reset(rhi->newTexture(QRhiTexture::RGBA8, image.size()));
108 else
109 m_texture->setPixelSize(image.size());
110 m_texture->create();
111 resourceUpdates->uploadTexture(m_texture.get(), image);
112 } else {
113 QRect imageRect = image.rect();
114 QRect rect = dirtyRegion.boundingRect() & imageRect;
116 subresDesc.setSourceTopLeft(rect.topLeft());
117 subresDesc.setSourceSize(rect.size());
118 subresDesc.setDestinationTopLeft(rect.topLeft());
119 QRhiTextureUploadDescription uploadDesc(QRhiTextureUploadEntry(0, 0, subresDesc));
120 resourceUpdates->uploadTexture(m_texture.get(), uploadDesc);
121 }
122
123 return m_texture.get();
124}
125
126static inline QRect scaledRect(const QRect &rect, qreal factor)
127{
128 return QRect(rect.topLeft() * factor, rect.size() * factor);
129}
130
131static inline QPoint scaledOffset(const QPoint &pt, qreal factor)
132{
133 return pt * factor;
134}
135
136static QRegion scaledRegion(const QRegion &region, qreal factor, const QPoint &offset)
137{
138 if (offset.isNull() && factor <= 1)
139 return region;
140
141 QVarLengthArray<QRect, 4> rects;
142 rects.reserve(region.rectCount());
143 for (const QRect &rect : region)
144 rects.append(scaledRect(rect.translated(offset), factor));
145
146 QRegion deviceRegion;
147 deviceRegion.setRects(rects.constData(), rects.size());
148 return deviceRegion;
149}
150
151static QMatrix4x4 targetTransform(const QRectF &target, const QRect &viewport, bool invertY)
152{
153 qreal x_scale = target.width() / viewport.width();
154 qreal y_scale = target.height() / viewport.height();
155
156 const QPointF relative_to_viewport = target.topLeft() - viewport.topLeft();
157 qreal x_translate = x_scale - 1 + ((relative_to_viewport.x() / viewport.width()) * 2);
158 qreal y_translate;
159 if (invertY)
160 y_translate = y_scale - 1 + ((relative_to_viewport.y() / viewport.height()) * 2);
161 else
162 y_translate = -y_scale + 1 - ((relative_to_viewport.y() / viewport.height()) * 2);
163
164 QMatrix4x4 matrix;
165 matrix(0,3) = x_translate;
166 matrix(1,3) = y_translate;
167
168 matrix(0,0) = x_scale;
169 matrix(1,1) = (invertY ? -1.0 : 1.0) * y_scale;
170
171 return matrix;
172}
173
178
179static QMatrix3x3 sourceTransform(const QRectF &subTexture,
180 const QSize &textureSize,
182{
183 qreal x_scale = subTexture.width() / textureSize.width();
184 qreal y_scale = subTexture.height() / textureSize.height();
185
186 const QPointF topLeft = subTexture.topLeft();
187 qreal x_translate = topLeft.x() / textureSize.width();
188 qreal y_translate = topLeft.y() / textureSize.height();
189
190 if (origin == SourceTransformOrigin::TopLeft) {
191 y_scale = -y_scale;
192 y_translate = 1 - y_translate;
193 }
194
195 QMatrix3x3 matrix;
196 matrix(0,2) = x_translate;
197 matrix(1,2) = y_translate;
198
199 matrix(0,0) = x_scale;
200 matrix(1,1) = y_scale;
201
202 return matrix;
203}
204
205static inline QRect toBottomLeftRect(const QRect &topLeftRect, int windowHeight)
206{
207 return QRect(topLeftRect.x(), windowHeight - topLeftRect.bottomRight().y() - 1,
208 topLeftRect.width(), topLeftRect.height());
209}
210
211static bool prepareDrawForRenderToTextureWidget(const QPlatformTextureList *textures,
212 int idx,
213 QWindow *window,
214 const QRect &deviceWindowRect,
215 const QPoint &offset,
216 bool invertTargetY,
217 bool invertSource,
218 QMatrix4x4 *target,
219 QMatrix3x3 *source)
220{
221 const QRect clipRect = textures->clipRect(idx);
222 if (clipRect.isEmpty())
223 return false;
224
225 QRect rectInWindow = textures->geometry(idx);
226 // relative to the TLW, not necessarily our window (if the flush is for a native child widget), have to adjust
227 rectInWindow.translate(-offset);
228
229 const QRect clippedRectInWindow = rectInWindow & clipRect.translated(rectInWindow.topLeft());
230 const QRect srcRect = toBottomLeftRect(clipRect, rectInWindow.height());
231
232 *target = targetTransform(scaledRect(clippedRectInWindow, window->devicePixelRatio()),
233 deviceWindowRect,
234 invertTargetY);
235
236 *source = sourceTransform(scaledRect(srcRect, window->devicePixelRatio()),
237 scaledRect(rectInWindow, window->devicePixelRatio()).size(),
238 invertSource ? SourceTransformOrigin::TopLeft : SourceTransformOrigin::BottomLeft);
239
240 return true;
241}
242
243static QShader getShader(const QString &name)
244{
245 QFile f(name);
246 if (f.open(QIODevice::ReadOnly))
247 return QShader::fromSerialized(f.readAll());
248
249 qWarning("QBackingStoreDefaultCompositor: Could not find built-in shader %s "
250 "(is something wrong with QtGui library resources?)",
251 qPrintable(name));
252 return QShader();
253}
254
255static void updateMatrix3x3(QRhiResourceUpdateBatch *resourceUpdates, QRhiBuffer *ubuf, const QMatrix3x3 &m)
256{
257 // mat3 is still 4 floats per column in the uniform buffer (but there is no
258 // 4th column), so 48 bytes altogether, not 36 or 64.
259
260 float f[12];
261 const float *src = static_cast<const float *>(m.constData());
262 float *dst = f;
263 memcpy(dst, src, 3 * sizeof(float));
264 memcpy(dst + 4, src + 3, 3 * sizeof(float));
265 memcpy(dst + 8, src + 6, 3 * sizeof(float));
266
267 resourceUpdates->updateDynamicBuffer(ubuf, 64, 48, f);
268}
269
275
277 QRhiShaderResourceBindings *srb,
278 QRhiRenderPassDescriptor *rpDesc,
279 PipelineBlend blend)
280{
281 QRhiGraphicsPipeline *ps = rhi->newGraphicsPipeline();
282
283 switch (blend) {
285 {
286 QRhiGraphicsPipeline::TargetBlend blend;
287 blend.enable = true;
288 blend.srcColor = QRhiGraphicsPipeline::SrcAlpha;
289 blend.dstColor = QRhiGraphicsPipeline::OneMinusSrcAlpha;
290 blend.srcAlpha = QRhiGraphicsPipeline::One;
291 blend.dstAlpha = QRhiGraphicsPipeline::One;
292 ps->setTargetBlends({ blend });
293 }
294 break;
296 {
297 QRhiGraphicsPipeline::TargetBlend blend;
298 blend.enable = true;
299 blend.srcColor = QRhiGraphicsPipeline::One;
300 blend.dstColor = QRhiGraphicsPipeline::OneMinusSrcAlpha;
301 blend.srcAlpha = QRhiGraphicsPipeline::One;
302 blend.dstAlpha = QRhiGraphicsPipeline::One;
303 ps->setTargetBlends({ blend });
304 }
305 break;
306 default:
307 break;
308 }
309
310 ps->setShaderStages({
311 { QRhiShaderStage::Vertex, getShader(":/qt-project.org/gui/painting/shaders/backingstorecompose.vert.qsb"_L1) },
312 { QRhiShaderStage::Fragment, getShader(":/qt-project.org/gui/painting/shaders/backingstorecompose.frag.qsb"_L1) }
313 });
314 QRhiVertexInputLayout inputLayout;
315 inputLayout.setBindings({ { 5 * sizeof(float) } });
316 inputLayout.setAttributes({
317 { 0, 0, QRhiVertexInputAttribute::Float3, 0 },
318 { 0, 1, QRhiVertexInputAttribute::Float2, quint32(3 * sizeof(float)) }
319 });
320 ps->setVertexInputLayout(inputLayout);
321 ps->setShaderResourceBindings(srb);
322 ps->setRenderPassDescriptor(rpDesc);
323
324 if (!ps->create()) {
325 qWarning("QBackingStoreDefaultCompositor: Failed to build graphics pipeline");
326 delete ps;
327 return nullptr;
328 }
329 return ps;
330}
331
332static const int UBUF_SIZE = 120;
333
334QBackingStoreDefaultCompositor::PerQuadData QBackingStoreDefaultCompositor::createPerQuadData(QRhiTexture *texture, QRhiTexture *textureExtra)
335{
336 PerQuadData d;
337
338 d.ubuf = m_rhi->newBuffer(QRhiBuffer::Dynamic, QRhiBuffer::UniformBuffer, UBUF_SIZE);
339 if (!d.ubuf->create())
340 qWarning("QBackingStoreDefaultCompositor: Failed to create uniform buffer");
341
342 d.srb = m_rhi->newShaderResourceBindings();
343 d.srb->setBindings({
344 QRhiShaderResourceBinding::uniformBuffer(0, QRhiShaderResourceBinding::VertexStage | QRhiShaderResourceBinding::FragmentStage, d.ubuf, 0, UBUF_SIZE),
345 QRhiShaderResourceBinding::sampledTexture(1, QRhiShaderResourceBinding::FragmentStage, texture, m_samplerNearest.get())
346 });
347 if (!d.srb->create())
348 qWarning("QBackingStoreDefaultCompositor: Failed to create srb");
349 d.lastUsedTexture = texture;
350
351 if (textureExtra) {
352 d.srbExtra = m_rhi->newShaderResourceBindings();
353 d.srbExtra->setBindings({
354 QRhiShaderResourceBinding::uniformBuffer(0, QRhiShaderResourceBinding::VertexStage | QRhiShaderResourceBinding::FragmentStage, d.ubuf, 0, UBUF_SIZE),
355 QRhiShaderResourceBinding::sampledTexture(1, QRhiShaderResourceBinding::FragmentStage, textureExtra, m_samplerNearest.get())
356 });
357 if (!d.srbExtra->create())
358 qWarning("QBackingStoreDefaultCompositor: Failed to create srb");
359 }
360
361 d.lastUsedTextureExtra = textureExtra;
362
363 return d;
364}
365
366void QBackingStoreDefaultCompositor::updatePerQuadData(PerQuadData *d, QRhiTexture *texture, QRhiTexture *textureExtra,
367 UpdateQuadDataOptions options)
368{
369 // This whole check-if-texture-ptr-is-different is needed because there is
370 // nothing saying a QPlatformTextureList cannot return a different
371 // QRhiTexture* from the same index in a subsequent flush.
372
373 const QRhiSampler::Filter filter = options.testFlag(NeedsLinearFiltering) ? QRhiSampler::Linear : QRhiSampler::Nearest;
374 if ((d->lastUsedTexture == texture && d->lastUsedFilter == filter) || !d->srb)
375 return;
376
377 QRhiSampler *sampler = filter == QRhiSampler::Linear ? m_samplerLinear.get() : m_samplerNearest.get();
378 d->srb->setBindings({
379 QRhiShaderResourceBinding::uniformBuffer(0, QRhiShaderResourceBinding::VertexStage | QRhiShaderResourceBinding::FragmentStage, d->ubuf, 0, UBUF_SIZE),
380 QRhiShaderResourceBinding::sampledTexture(1, QRhiShaderResourceBinding::FragmentStage, texture, sampler)
381 });
382
383 d->srb->updateResources(QRhiShaderResourceBindings::BindingsAreSorted);
384 d->lastUsedTexture = texture;
385 d->lastUsedFilter = filter;
386
387 if (textureExtra) {
388 d->srbExtra->setBindings({
389 QRhiShaderResourceBinding::uniformBuffer(0, QRhiShaderResourceBinding::VertexStage | QRhiShaderResourceBinding::FragmentStage, d->ubuf, 0, UBUF_SIZE),
390 QRhiShaderResourceBinding::sampledTexture(1, QRhiShaderResourceBinding::FragmentStage, textureExtra, sampler)
391 });
392
393 d->srbExtra->updateResources(QRhiShaderResourceBindings::BindingsAreSorted);
394 d->lastUsedTextureExtra = textureExtra;
395 }
396}
397
398void QBackingStoreDefaultCompositor::updateUniforms(PerQuadData *d, QRhiResourceUpdateBatch *resourceUpdates,
399 const QMatrix4x4 &target, const QMatrix3x3 &source,
400 UpdateUniformOptions options)
401{
402 resourceUpdates->updateDynamicBuffer(d->ubuf, 0, 64, target.constData());
403 updateMatrix3x3(resourceUpdates, d->ubuf, source);
404 float opacity = 1.0f;
405 resourceUpdates->updateDynamicBuffer(d->ubuf, 112, 4, &opacity);
406 qint32 textureSwizzle = options;
407 resourceUpdates->updateDynamicBuffer(d->ubuf, 116, 4, &textureSwizzle);
408}
409
410void QBackingStoreDefaultCompositor::ensureResources(QRhiResourceUpdateBatch *resourceUpdates, QRhiRenderPassDescriptor *rpDesc)
411{
412 static const float vertexData[] = {
413 -1, -1, 0, 0, 0,
414 -1, 1, 0, 0, 1,
415 1, -1, 0, 1, 0,
416 -1, 1, 0, 0, 1,
417 1, -1, 0, 1, 0,
418 1, 1, 0, 1, 1
419 };
420
421 if (!m_vbuf) {
422 m_vbuf.reset(m_rhi->newBuffer(QRhiBuffer::Immutable, QRhiBuffer::VertexBuffer, sizeof(vertexData)));
423 if (m_vbuf->create())
424 resourceUpdates->uploadStaticBuffer(m_vbuf.get(), vertexData);
425 else
426 qWarning("QBackingStoreDefaultCompositor: Failed to create vertex buffer");
427 }
428
429 if (!m_samplerNearest) {
430 m_samplerNearest.reset(m_rhi->newSampler(QRhiSampler::Nearest, QRhiSampler::Nearest, QRhiSampler::None,
431 QRhiSampler::ClampToEdge, QRhiSampler::ClampToEdge));
432 if (!m_samplerNearest->create())
433 qWarning("QBackingStoreDefaultCompositor: Failed to create sampler (Nearest filtering)");
434 }
435
436 if (!m_samplerLinear) {
437 m_samplerLinear.reset(m_rhi->newSampler(QRhiSampler::Linear, QRhiSampler::Linear, QRhiSampler::None,
438 QRhiSampler::ClampToEdge, QRhiSampler::ClampToEdge));
439 if (!m_samplerLinear->create())
440 qWarning("QBackingStoreDefaultCompositor: Failed to create sampler (Linear filtering)");
441 }
442
443 if (!m_widgetQuadData.isValid())
444 m_widgetQuadData = createPerQuadData(m_texture.get());
445
446 QRhiShaderResourceBindings *srb = m_widgetQuadData.srb; // just for the layout
447 if (!m_psNoBlend)
448 m_psNoBlend.reset(createGraphicsPipeline(m_rhi, srb, rpDesc, PipelineBlend::None));
449 if (!m_psBlend)
450 m_psBlend.reset(createGraphicsPipeline(m_rhi, srb, rpDesc, PipelineBlend::Alpha));
451 if (!m_psPremulBlend)
452 m_psPremulBlend.reset(createGraphicsPipeline(m_rhi, srb, rpDesc, PipelineBlend::PremulAlpha));
453}
454
456 QRhi *rhi,
457 QRhiSwapChain *swapchain,
458 QWindow *window,
459 qreal sourceDevicePixelRatio,
460 const QRegion &region,
461 const QPoint &offset,
462 QPlatformTextureList *textures,
463 bool translucentBackground,
464 qreal sourceTransformFactor)
465{
466 if (!rhi || !swapchain)
467 return QPlatformBackingStore::FlushFailed;
468
469 // Note, the sourceTransformFactor is different from the sourceDevicePixelRatio,
470 // as the former may reflect the fact that the region and offset is pre-transformed,
471 // in which case we don't need to do a full transform here based on the source DPR.
472 // In the default case where no explicit source transform has been passed, we fall
473 // back to the source device pixel ratio.
474 if (!sourceTransformFactor)
475 sourceTransformFactor = sourceDevicePixelRatio;
476
477 Q_ASSERT(textures); // may be empty if there are no render-to-texture widgets at all, but null it cannot be
478
479 if (!m_rhi) {
480 m_rhi = rhi;
481 } else if (m_rhi != rhi) {
482 qWarning("QBackingStoreDefaultCompositor: the QRhi has changed unexpectedly, this should not happen");
483 return QPlatformBackingStore::FlushFailed;
484 }
485
486 if (!qt_window_private(window)->receivedExpose)
487 return QPlatformBackingStore::FlushSuccess;
488
489 qCDebug(lcQpaBackingStore) << "Composing and flushing" << region << "of" << window
490 << "at offset" << offset << "with" << textures->count() << "texture(s) in" << textures
491 << "via swapchain" << swapchain;
492
493 QWindowPrivate::get(window)->lastComposeTime.start();
494
495 if (swapchain->currentPixelSize() != swapchain->surfacePixelSize())
496 swapchain->createOrResize();
497
498 // Start recording a new frame.
499 QRhi::FrameOpResult frameResult = rhi->beginFrame(swapchain);
500 if (frameResult == QRhi::FrameOpSwapChainOutOfDate) {
501 if (!swapchain->createOrResize())
502 return QPlatformBackingStore::FlushFailed;
503 frameResult = rhi->beginFrame(swapchain);
504 }
505 if (frameResult == QRhi::FrameOpDeviceLost)
506 return QPlatformBackingStore::FlushFailedDueToLostDevice;
507 if (frameResult != QRhi::FrameOpSuccess)
508 return QPlatformBackingStore::FlushFailed;
509
510 // Prepare resource updates.
511 QRhiResourceUpdateBatch *resourceUpdates = rhi->nextResourceUpdateBatch();
512 QPlatformBackingStore::TextureFlags flags;
513
514 bool gotTextureFromGraphicsBuffer = false;
515 if (QPlatformGraphicsBuffer *graphicsBuffer = backingStore->graphicsBuffer()) {
516 if (graphicsBuffer->lock(QPlatformGraphicsBuffer::SWReadAccess)) {
517 const QImage::Format format = QImage::toImageFormat(graphicsBuffer->format());
518 const QSize size = graphicsBuffer->size();
519 QImage wrapperImage(graphicsBuffer->data(), size.width(), size.height(), graphicsBuffer->bytesPerLine(), format);
520 toTexture(wrapperImage, rhi, resourceUpdates, scaledRegion(region, sourceTransformFactor, offset), &flags);
521 gotTextureFromGraphicsBuffer = true;
522 graphicsBuffer->unlock();
523 if (graphicsBuffer->origin() == QPlatformGraphicsBuffer::OriginBottomLeft)
524 flags |= QPlatformBackingStore::TextureFlip;
525 }
526 }
527 if (!gotTextureFromGraphicsBuffer)
528 toTexture(backingStore, rhi, resourceUpdates, scaledRegion(region, sourceTransformFactor, offset), &flags);
529
530 ensureResources(resourceUpdates, swapchain->renderPassDescriptor());
531
532 UpdateUniformOptions uniformOptions;
533#if Q_BYTE_ORDER == Q_LITTLE_ENDIAN
534 if (flags & QPlatformBackingStore::TextureSwizzle)
535 uniformOptions |= NeedsRedBlueSwap;
536#else
537 if (flags & QPlatformBackingStore::TextureSwizzle)
538 uniformOptions |= NeedsAlphaRotate;
539#endif
540 const bool premultiplied = (flags & QPlatformBackingStore::TexturePremultiplied) != 0;
542 if (flags & QPlatformBackingStore::TextureFlip)
544
545 const qreal dpr = window->devicePixelRatio();
546 const QRect deviceWindowRect = scaledRect(QRect(QPoint(), window->size()), dpr);
547 const QRect sourceWindowRect = scaledRect(QRect(QPoint(), window->size()), sourceDevicePixelRatio);
548 // If sourceWindowRect is larger than deviceWindowRect, we are doing high
549 // DPI downscaling. In that case Linear filtering is a must, whereas for the
550 // 1:1 case Nearest must be used for Qt 5 visual compatibility.
551 const bool needsLinearSampler = sourceWindowRect.width() > deviceWindowRect.width()
552 && sourceWindowRect.height() > deviceWindowRect.height();
553
554 const bool invertTargetY = !rhi->isYUpInNDC();
555 const bool invertSource = !rhi->isYUpInFramebuffer();
556
557 if (m_texture) {
558 // The backingstore is for the entire tlw. In case of native children, offset tells the position
559 // relative to the tlw. The window rect is scaled by the source device pixel ratio to get
560 // the source rect.
561 const QPoint sourceWindowOffset = scaledOffset(offset, sourceTransformFactor);
562 const QRect srcRect = toBottomLeftRect(sourceWindowRect.translated(sourceWindowOffset), m_texture->pixelSize().height());
563 const QMatrix3x3 source = sourceTransform(srcRect, m_texture->pixelSize(), origin);
564 QMatrix4x4 target; // identity
565 if (invertTargetY)
566 target.data()[5] = -1.0f;
567 updateUniforms(&m_widgetQuadData, resourceUpdates, target, source, uniformOptions);
568 if (needsLinearSampler)
569 updatePerQuadData(&m_widgetQuadData, m_texture.get(), nullptr, NeedsLinearFiltering);
570 }
571
572 const int textureWidgetCount = textures->count();
573 const int oldTextureQuadDataCount = m_textureQuadData.size();
574 if (oldTextureQuadDataCount != textureWidgetCount) {
575 for (int i = textureWidgetCount; i < oldTextureQuadDataCount; ++i)
576 m_textureQuadData[i].reset();
577 m_textureQuadData.resize(textureWidgetCount);
578 }
579
580 for (int i = 0; i < textureWidgetCount; ++i) {
581 const bool invertSourceForTextureWidget = textures->flags(i).testFlag(QPlatformTextureList::MirrorVertically)
582 ? !invertSource : invertSource;
583 QMatrix4x4 target;
584 QMatrix3x3 source;
585 if (!prepareDrawForRenderToTextureWidget(textures, i, window, deviceWindowRect,
586 offset, invertTargetY, invertSourceForTextureWidget,
587 &target, &source))
588 {
589 m_textureQuadData[i].reset();
590 continue;
591 }
592 QRhiTexture *t = textures->texture(i);
593 QRhiTexture *tExtra = textures->textureExtra(i);
594 if (t) {
595 if (!m_textureQuadData[i].isValid())
596 m_textureQuadData[i] = createPerQuadData(t, tExtra);
597 else
598 updatePerQuadData(&m_textureQuadData[i], t, tExtra);
599 updateUniforms(&m_textureQuadData[i], resourceUpdates, target, source);
600 if (needsLinearSampler)
601 updatePerQuadData(&m_textureQuadData[i], t, tExtra, NeedsLinearFiltering);
602 } else {
603 m_textureQuadData[i].reset();
604 }
605 }
606
607 // Record the render pass (with committing the resource updates).
608 QRhiCommandBuffer *cb = swapchain->currentFrameCommandBuffer();
609 const QSize outputSizeInPixels = swapchain->currentPixelSize();
610 QColor clearColor = translucentBackground ? Qt::transparent : Qt::black;
611
612 cb->resourceUpdate(resourceUpdates);
613
614 auto render = [&](std::optional<QRhiSwapChain::StereoTargetBuffer> buffer = std::nullopt) {
615 QRhiRenderTarget* target = nullptr;
616 if (buffer.has_value())
617 target = swapchain->currentFrameRenderTarget(buffer.value());
618 else
619 target = swapchain->currentFrameRenderTarget();
620
621 cb->beginPass(target, clearColor, { 1.0f, 0 });
622
623 cb->setGraphicsPipeline(m_psNoBlend.get());
624 cb->setViewport({ 0, 0, float(outputSizeInPixels.width()), float(outputSizeInPixels.height()) });
625 QRhiCommandBuffer::VertexInput vbufBinding(m_vbuf.get(), 0);
626 cb->setVertexInput(0, 1, &vbufBinding);
627
628 // Textures for renderToTexture widgets.
629 for (int i = 0; i < textureWidgetCount; ++i) {
630 if (!textures->flags(i).testFlag(QPlatformTextureList::StacksOnTop)) {
631 if (m_textureQuadData[i].isValid()) {
632
633 QRhiShaderResourceBindings* srb = m_textureQuadData[i].srb;
634 if (buffer == QRhiSwapChain::RightBuffer && m_textureQuadData[i].srbExtra)
635 srb = m_textureQuadData[i].srbExtra;
636
637 cb->setShaderResources(srb);
638 cb->draw(6);
639 }
640 }
641 }
642
643 cb->setGraphicsPipeline(premultiplied ? m_psPremulBlend.get() : m_psBlend.get());
644
645 // Backingstore texture with the normal widgets.
646 if (m_texture) {
647 cb->setShaderResources(m_widgetQuadData.srb);
648 cb->draw(6);
649 }
650
651 // Textures for renderToTexture widgets that have WA_AlwaysStackOnTop set.
652 for (int i = 0; i < textureWidgetCount; ++i) {
653 const QPlatformTextureList::Flags flags = textures->flags(i);
654 if (flags.testFlag(QPlatformTextureList::StacksOnTop)) {
655 if (m_textureQuadData[i].isValid()) {
656 if (flags.testFlag(QPlatformTextureList::NeedsPremultipliedAlphaBlending))
657 cb->setGraphicsPipeline(m_psPremulBlend.get());
658 else
659 cb->setGraphicsPipeline(m_psBlend.get());
660
661 QRhiShaderResourceBindings* srb = m_textureQuadData[i].srb;
662 if (buffer == QRhiSwapChain::RightBuffer && m_textureQuadData[i].srbExtra)
663 srb = m_textureQuadData[i].srbExtra;
664
665 cb->setShaderResources(srb);
666 cb->draw(6);
667 }
668 }
669 }
670
671 cb->endPass();
672 };
673
674 if (swapchain->window()->format().stereo()) {
675 render(QRhiSwapChain::LeftBuffer);
676 render(QRhiSwapChain::RightBuffer);
677 } else
678 render();
679
680 rhi->endFrame(swapchain);
681
682 return QPlatformBackingStore::FlushSuccess;
683}
684
685QT_END_NAMESPACE
QRhiTexture * toTexture(const QPlatformBackingStore *backingStore, QRhi *rhi, QRhiResourceUpdateBatch *resourceUpdates, const QRegion &dirtyRegion, QPlatformBackingStore::TextureFlags *flags) const
QPlatformBackingStore::FlushResult flush(QPlatformBackingStore *backingStore, QRhi *rhi, QRhiSwapChain *swapchain, QWindow *window, qreal sourceDevicePixelRatio, const QRegion &region, const QPoint &offset, QPlatformTextureList *textures, bool translucentBackground, qreal sourceTransformFactor)
\inmodule QtGuiPrivate \inheaderfile rhi/qrhi.h
Definition qrhi.h:661
\inmodule QtGuiPrivate \inheaderfile rhi/qrhi.h
Definition qrhi.h:721
\inmodule QtGuiPrivate \inheaderfile rhi/qrhi.h
Definition qrhi.h:322
static QRegion scaledRegion(const QRegion &region, qreal factor, const QPoint &offset)
static QMatrix4x4 targetTransform(const QRectF &target, const QRect &viewport, bool invertY)
static QRect scaledRect(const QRect &rect, qreal factor)
static QMatrix3x3 sourceTransform(const QRectF &subTexture, const QSize &textureSize, SourceTransformOrigin origin)
static void updateMatrix3x3(QRhiResourceUpdateBatch *resourceUpdates, QRhiBuffer *ubuf, const QMatrix3x3 &m)
static QRect toBottomLeftRect(const QRect &topLeftRect, int windowHeight)
static QShader getShader(const QString &name)
static QPoint scaledOffset(const QPoint &pt, qreal factor)
static bool prepareDrawForRenderToTextureWidget(const QPlatformTextureList *textures, int idx, QWindow *window, const QRect &deviceWindowRect, const QPoint &offset, bool invertTargetY, bool invertSource, QMatrix4x4 *target, QMatrix3x3 *source)
static const int UBUF_SIZE
static QRhiGraphicsPipeline * createGraphicsPipeline(QRhi *rhi, QRhiShaderResourceBindings *srb, QRhiRenderPassDescriptor *rpDesc, PipelineBlend blend)