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