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

source code of qtbase/src/gui/painting/qbackingstoredefaultcompositor.cpp