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