diff options
Diffstat (limited to 'src/gui/painting/qbackingstoredefaultcompositor.cpp')
-rw-r--r-- | src/gui/painting/qbackingstoredefaultcompositor.cpp | 254 |
1 files changed, 165 insertions, 89 deletions
diff --git a/src/gui/painting/qbackingstoredefaultcompositor.cpp b/src/gui/painting/qbackingstoredefaultcompositor.cpp index 913afed846..c1452ca768 100644 --- a/src/gui/painting/qbackingstoredefaultcompositor.cpp +++ b/src/gui/painting/qbackingstoredefaultcompositor.cpp @@ -17,18 +17,14 @@ QBackingStoreDefaultCompositor::~QBackingStoreDefaultCompositor() void QBackingStoreDefaultCompositor::reset() { - delete m_psNoBlend; - m_psNoBlend = nullptr; - delete m_psBlend; - m_psBlend = nullptr; - delete m_psPremulBlend; - m_psPremulBlend = nullptr; - delete m_sampler; - m_sampler = nullptr; - delete m_vbuf; - m_vbuf = nullptr; - delete m_texture; - m_texture = nullptr; + m_rhi = nullptr; + m_psNoBlend.reset(); + m_psBlend.reset(); + m_psPremulBlend.reset(); + m_samplerNearest.reset(); + m_samplerLinear.reset(); + m_vbuf.reset(); + m_texture.reset(); m_widgetQuadData.reset(); for (PerQuadData &d : m_textureQuadData) d.reset(); @@ -99,7 +95,7 @@ QRhiTexture *QBackingStoreDefaultCompositor::toTexture(const QImage &sourceImage const bool resized = !m_texture || m_texture->pixelSize() != image.size(); if (dirtyRegion.isEmpty() && !resized) - return m_texture; + return m_texture.get(); if (needsConversion) image = image.convertToFormat(QImage::Format_RGBA8888); @@ -108,11 +104,11 @@ QRhiTexture *QBackingStoreDefaultCompositor::toTexture(const QImage &sourceImage if (resized) { if (!m_texture) - m_texture = rhi->newTexture(QRhiTexture::RGBA8, image.size()); + m_texture.reset(rhi->newTexture(QRhiTexture::RGBA8, image.size())); else m_texture->setPixelSize(image.size()); m_texture->create(); - resourceUpdates->uploadTexture(m_texture, image); + resourceUpdates->uploadTexture(m_texture.get(), image); } else { QRect imageRect = image.rect(); QRect rect = dirtyRegion.boundingRect() & imageRect; @@ -121,10 +117,10 @@ QRhiTexture *QBackingStoreDefaultCompositor::toTexture(const QImage &sourceImage subresDesc.setSourceSize(rect.size()); subresDesc.setDestinationTopLeft(rect.topLeft()); QRhiTextureUploadDescription uploadDesc(QRhiTextureUploadEntry(0, 0, subresDesc)); - resourceUpdates->uploadTexture(m_texture, uploadDesc); + resourceUpdates->uploadTexture(m_texture.get(), uploadDesc); } - return m_texture; + return m_texture.get(); } static inline QRect scaledRect(const QRect &rect, qreal factor) @@ -148,7 +144,7 @@ static QRegion scaledRegion(const QRegion ®ion, qreal factor, const QPoint &o rects.append(scaledRect(rect.translated(offset), factor)); QRegion deviceRegion; - deviceRegion.setRects(rects.constData(), rects.count()); + deviceRegion.setRects(rects.constData(), rects.size()); return deviceRegion; } @@ -170,7 +166,7 @@ static QMatrix4x4 targetTransform(const QRectF &target, const QRect &viewport, b matrix(1,3) = y_translate; matrix(0,0) = x_scale; - matrix(1,1) = y_scale; + matrix(1,1) = (invertY ? -1.0 : 1.0) * y_scale; return matrix; } @@ -279,7 +275,7 @@ enum class PipelineBlend { static QRhiGraphicsPipeline *createGraphicsPipeline(QRhi *rhi, QRhiShaderResourceBindings *srb, - QRhiSwapChain *swapchain, + QRhiRenderPassDescriptor *rpDesc, PipelineBlend blend) { QRhiGraphicsPipeline *ps = rhi->newGraphicsPipeline(); @@ -323,7 +319,7 @@ static QRhiGraphicsPipeline *createGraphicsPipeline(QRhi *rhi, }); ps->setVertexInputLayout(inputLayout); ps->setShaderResourceBindings(srb); - ps->setRenderPassDescriptor(swapchain->renderPassDescriptor()); + ps->setRenderPassDescriptor(rpDesc); if (!ps->create()) { qWarning("QBackingStoreDefaultCompositor: Failed to build graphics pipeline"); @@ -335,7 +331,7 @@ static QRhiGraphicsPipeline *createGraphicsPipeline(QRhi *rhi, static const int UBUF_SIZE = 120; -QBackingStoreDefaultCompositor::PerQuadData QBackingStoreDefaultCompositor::createPerQuadData(QRhiTexture *texture) +QBackingStoreDefaultCompositor::PerQuadData QBackingStoreDefaultCompositor::createPerQuadData(QRhiTexture *texture, QRhiTexture *textureExtra) { PerQuadData d; @@ -346,47 +342,72 @@ QBackingStoreDefaultCompositor::PerQuadData QBackingStoreDefaultCompositor::crea d.srb = m_rhi->newShaderResourceBindings(); d.srb->setBindings({ QRhiShaderResourceBinding::uniformBuffer(0, QRhiShaderResourceBinding::VertexStage | QRhiShaderResourceBinding::FragmentStage, d.ubuf, 0, UBUF_SIZE), - QRhiShaderResourceBinding::sampledTexture(1, QRhiShaderResourceBinding::FragmentStage, texture, m_sampler) + QRhiShaderResourceBinding::sampledTexture(1, QRhiShaderResourceBinding::FragmentStage, texture, m_samplerNearest.get()) }); if (!d.srb->create()) qWarning("QBackingStoreDefaultCompositor: Failed to create srb"); - d.lastUsedTexture = texture; + if (textureExtra) { + d.srbExtra = m_rhi->newShaderResourceBindings(); + d.srbExtra->setBindings({ + QRhiShaderResourceBinding::uniformBuffer(0, QRhiShaderResourceBinding::VertexStage | QRhiShaderResourceBinding::FragmentStage, d.ubuf, 0, UBUF_SIZE), + QRhiShaderResourceBinding::sampledTexture(1, QRhiShaderResourceBinding::FragmentStage, textureExtra, m_samplerNearest.get()) + }); + if (!d.srbExtra->create()) + qWarning("QBackingStoreDefaultCompositor: Failed to create srb"); + } + + d.lastUsedTextureExtra = textureExtra; + return d; } -void QBackingStoreDefaultCompositor::updatePerQuadData(PerQuadData *d, QRhiTexture *texture) +void QBackingStoreDefaultCompositor::updatePerQuadData(PerQuadData *d, QRhiTexture *texture, QRhiTexture *textureExtra, + UpdateQuadDataOptions options) { // This whole check-if-texture-ptr-is-different is needed because there is // nothing saying a QPlatformTextureList cannot return a different // QRhiTexture* from the same index in a subsequent flush. - if (d->lastUsedTexture == texture || !d->srb) + const QRhiSampler::Filter filter = options.testFlag(NeedsLinearFiltering) ? QRhiSampler::Linear : QRhiSampler::Nearest; + if ((d->lastUsedTexture == texture && d->lastUsedFilter == filter) || !d->srb) return; + QRhiSampler *sampler = filter == QRhiSampler::Linear ? m_samplerLinear.get() : m_samplerNearest.get(); d->srb->setBindings({ QRhiShaderResourceBinding::uniformBuffer(0, QRhiShaderResourceBinding::VertexStage | QRhiShaderResourceBinding::FragmentStage, d->ubuf, 0, UBUF_SIZE), - QRhiShaderResourceBinding::sampledTexture(1, QRhiShaderResourceBinding::FragmentStage, texture, m_sampler) + QRhiShaderResourceBinding::sampledTexture(1, QRhiShaderResourceBinding::FragmentStage, texture, sampler) }); d->srb->updateResources(QRhiShaderResourceBindings::BindingsAreSorted); - d->lastUsedTexture = texture; + d->lastUsedFilter = filter; + + if (textureExtra) { + d->srbExtra->setBindings({ + QRhiShaderResourceBinding::uniformBuffer(0, QRhiShaderResourceBinding::VertexStage | QRhiShaderResourceBinding::FragmentStage, d->ubuf, 0, UBUF_SIZE), + QRhiShaderResourceBinding::sampledTexture(1, QRhiShaderResourceBinding::FragmentStage, textureExtra, sampler) + }); + + d->srbExtra->updateResources(QRhiShaderResourceBindings::BindingsAreSorted); + d->lastUsedTextureExtra = textureExtra; + } } void QBackingStoreDefaultCompositor::updateUniforms(PerQuadData *d, QRhiResourceUpdateBatch *resourceUpdates, - const QMatrix4x4 &target, const QMatrix3x3 &source, UpdateUniformOption option) + const QMatrix4x4 &target, const QMatrix3x3 &source, + UpdateUniformOptions options) { resourceUpdates->updateDynamicBuffer(d->ubuf, 0, 64, target.constData()); updateMatrix3x3(resourceUpdates, d->ubuf, source); float opacity = 1.0f; resourceUpdates->updateDynamicBuffer(d->ubuf, 112, 4, &opacity); - qint32 textureSwizzle = static_cast<qint32>(option); + qint32 textureSwizzle = options; resourceUpdates->updateDynamicBuffer(d->ubuf, 116, 4, &textureSwizzle); } -void QBackingStoreDefaultCompositor::ensureResources(QRhiSwapChain *swapchain, QRhiResourceUpdateBatch *resourceUpdates) +void QBackingStoreDefaultCompositor::ensureResources(QRhiResourceUpdateBatch *resourceUpdates, QRhiRenderPassDescriptor *rpDesc) { static const float vertexData[] = { -1, -1, 0, 0, 0, @@ -398,30 +419,37 @@ void QBackingStoreDefaultCompositor::ensureResources(QRhiSwapChain *swapchain, Q }; if (!m_vbuf) { - m_vbuf = m_rhi->newBuffer(QRhiBuffer::Immutable, QRhiBuffer::VertexBuffer, sizeof(vertexData)); + m_vbuf.reset(m_rhi->newBuffer(QRhiBuffer::Immutable, QRhiBuffer::VertexBuffer, sizeof(vertexData))); if (m_vbuf->create()) - resourceUpdates->uploadStaticBuffer(m_vbuf, vertexData); + resourceUpdates->uploadStaticBuffer(m_vbuf.get(), vertexData); else qWarning("QBackingStoreDefaultCompositor: Failed to create vertex buffer"); } - if (!m_sampler) { - m_sampler = m_rhi->newSampler(QRhiSampler::Linear, QRhiSampler::Linear, QRhiSampler::None, - QRhiSampler::ClampToEdge, QRhiSampler::ClampToEdge); - if (!m_sampler->create()) - qWarning("QBackingStoreDefaultCompositor: Failed to create sampler"); + if (!m_samplerNearest) { + m_samplerNearest.reset(m_rhi->newSampler(QRhiSampler::Nearest, QRhiSampler::Nearest, QRhiSampler::None, + QRhiSampler::ClampToEdge, QRhiSampler::ClampToEdge)); + if (!m_samplerNearest->create()) + qWarning("QBackingStoreDefaultCompositor: Failed to create sampler (Nearest filtering)"); + } + + if (!m_samplerLinear) { + m_samplerLinear.reset(m_rhi->newSampler(QRhiSampler::Linear, QRhiSampler::Linear, QRhiSampler::None, + QRhiSampler::ClampToEdge, QRhiSampler::ClampToEdge)); + if (!m_samplerLinear->create()) + qWarning("QBackingStoreDefaultCompositor: Failed to create sampler (Linear filtering)"); } if (!m_widgetQuadData.isValid()) - m_widgetQuadData = createPerQuadData(m_texture); + m_widgetQuadData = createPerQuadData(m_texture.get()); QRhiShaderResourceBindings *srb = m_widgetQuadData.srb; // just for the layout if (!m_psNoBlend) - m_psNoBlend = createGraphicsPipeline(m_rhi, srb, swapchain, PipelineBlend::None); + m_psNoBlend.reset(createGraphicsPipeline(m_rhi, srb, rpDesc, PipelineBlend::None)); if (!m_psBlend) - m_psBlend = createGraphicsPipeline(m_rhi, srb, swapchain, PipelineBlend::Alpha); + m_psBlend.reset(createGraphicsPipeline(m_rhi, srb, rpDesc, PipelineBlend::Alpha)); if (!m_psPremulBlend) - m_psPremulBlend = createGraphicsPipeline(m_rhi, srb, swapchain, PipelineBlend::PremulAlpha); + m_psPremulBlend.reset(createGraphicsPipeline(m_rhi, srb, rpDesc, PipelineBlend::PremulAlpha)); } QPlatformBackingStore::FlushResult QBackingStoreDefaultCompositor::flush(QPlatformBackingStore *backingStore, @@ -434,6 +462,9 @@ QPlatformBackingStore::FlushResult QBackingStoreDefaultCompositor::flush(QPlatfo QPlatformTextureList *textures, bool translucentBackground) { + if (!rhi) + return QPlatformBackingStore::FlushFailed; + Q_ASSERT(textures); // may be empty if there are no render-to-texture widgets at all, but null it cannot be if (!m_rhi) { @@ -487,12 +518,15 @@ QPlatformBackingStore::FlushResult QBackingStoreDefaultCompositor::flush(QPlatfo if (!gotTextureFromGraphicsBuffer) toTexture(backingStore, rhi, resourceUpdates, scaledRegion(region, sourceDevicePixelRatio, offset), &flags); - ensureResources(swapchain, resourceUpdates); + ensureResources(resourceUpdates, swapchain->renderPassDescriptor()); + UpdateUniformOptions uniformOptions; #if Q_BYTE_ORDER == Q_LITTLE_ENDIAN - const UpdateUniformOption uniformOption = (flags & QPlatformBackingStore::TextureSwizzle) != 0? NeedsRedBlueSwap : NoOption; + if (flags & QPlatformBackingStore::TextureSwizzle) + uniformOptions |= NeedsRedBlueSwap; #else - const UpdateUniformOption uniformOption = (flags & QPlatformBackingStore::TextureSwizzle) != 0? NeedsAlphaRotate : NoOption; + if (flags & QPlatformBackingStore::TextureSwizzle) + uniformOptions |= NeedsAlphaRotate; #endif const bool premultiplied = (flags & QPlatformBackingStore::TexturePremultiplied) != 0; SourceTransformOrigin origin = SourceTransformOrigin::TopLeft; @@ -501,24 +535,33 @@ QPlatformBackingStore::FlushResult QBackingStoreDefaultCompositor::flush(QPlatfo const qreal dpr = window->devicePixelRatio(); const QRect deviceWindowRect = scaledRect(QRect(QPoint(), window->size()), dpr); - const QPoint deviceWindowOffset = scaledOffset(offset, dpr); + const QRect sourceWindowRect = scaledRect(QRect(QPoint(), window->size()), sourceDevicePixelRatio); + // If sourceWindowRect is larger than deviceWindowRect, we are doing high + // DPI downscaling. In that case Linear filtering is a must, whereas for the + // 1:1 case Nearest must be used for Qt 5 visual compatibility. + const bool needsLinearSampler = sourceWindowRect.width() > deviceWindowRect.width() + && sourceWindowRect.height() > deviceWindowRect.height(); + + const bool invertTargetY = !rhi->isYUpInNDC(); + const bool invertSource = !rhi->isYUpInFramebuffer(); - const bool invertTargetY = rhi->clipSpaceCorrMatrix().data()[5] < 0.0f; - const bool invertSource = rhi->isYUpInFramebuffer() != rhi->isYUpInNDC(); if (m_texture) { - // The backingstore is for the entire tlw. - // In case of native children offset tells the position relative to the tlw. - const QRect textureRect = QRect(QPoint(), m_texture->pixelSize()); - const QRect srcRect = toBottomLeftRect(textureRect.translated(deviceWindowOffset), m_texture->pixelSize().height()); + // The backingstore is for the entire tlw. In case of native children, offset tells the position + // relative to the tlw. The window rect is scaled by the source device pixel ratio to get + // the source rect. + const QPoint sourceWindowOffset = scaledOffset(offset, sourceDevicePixelRatio); + const QRect srcRect = toBottomLeftRect(sourceWindowRect.translated(sourceWindowOffset), m_texture->pixelSize().height()); const QMatrix3x3 source = sourceTransform(srcRect, m_texture->pixelSize(), origin); QMatrix4x4 target; // identity if (invertTargetY) target.data()[5] = -1.0f; - updateUniforms(&m_widgetQuadData, resourceUpdates, target, source, uniformOption); + updateUniforms(&m_widgetQuadData, resourceUpdates, target, source, uniformOptions); + if (needsLinearSampler) + updatePerQuadData(&m_widgetQuadData, m_texture.get(), nullptr, NeedsLinearFiltering); } const int textureWidgetCount = textures->count(); - const int oldTextureQuadDataCount = m_textureQuadData.count(); + const int oldTextureQuadDataCount = m_textureQuadData.size(); if (oldTextureQuadDataCount != textureWidgetCount) { for (int i = textureWidgetCount; i < oldTextureQuadDataCount; ++i) m_textureQuadData[i].reset(); @@ -526,21 +569,27 @@ QPlatformBackingStore::FlushResult QBackingStoreDefaultCompositor::flush(QPlatfo } for (int i = 0; i < textureWidgetCount; ++i) { + const bool invertSourceForTextureWidget = textures->flags(i).testFlag(QPlatformTextureList::MirrorVertically) + ? !invertSource : invertSource; QMatrix4x4 target; QMatrix3x3 source; if (!prepareDrawForRenderToTextureWidget(textures, i, window, deviceWindowRect, - offset, invertTargetY, invertSource, &target, &source)) + offset, invertTargetY, invertSourceForTextureWidget, + &target, &source)) { m_textureQuadData[i].reset(); continue; } QRhiTexture *t = textures->texture(i); + QRhiTexture *tExtra = textures->textureExtra(i); if (t) { if (!m_textureQuadData[i].isValid()) - m_textureQuadData[i] = createPerQuadData(t); + m_textureQuadData[i] = createPerQuadData(t, tExtra); else - updatePerQuadData(&m_textureQuadData[i], t); - updateUniforms(&m_textureQuadData[i], resourceUpdates, target, source, NoOption); + updatePerQuadData(&m_textureQuadData[i], t, tExtra); + updateUniforms(&m_textureQuadData[i], resourceUpdates, target, source); + if (needsLinearSampler) + updatePerQuadData(&m_textureQuadData[i], t, tExtra, NeedsLinearFiltering); } else { m_textureQuadData[i].reset(); } @@ -550,47 +599,74 @@ QPlatformBackingStore::FlushResult QBackingStoreDefaultCompositor::flush(QPlatfo QRhiCommandBuffer *cb = swapchain->currentFrameCommandBuffer(); const QSize outputSizeInPixels = swapchain->currentPixelSize(); QColor clearColor = translucentBackground ? Qt::transparent : Qt::black; - cb->beginPass(swapchain->currentFrameRenderTarget(), clearColor, { 1.0f, 0 }, resourceUpdates); - cb->setGraphicsPipeline(m_psNoBlend); - cb->setViewport({ 0, 0, float(outputSizeInPixels.width()), float(outputSizeInPixels.height()) }); - QRhiCommandBuffer::VertexInput vbufBinding(m_vbuf, 0); - cb->setVertexInput(0, 1, &vbufBinding); + cb->resourceUpdate(resourceUpdates); - // Textures for renderToTexture widgets. - for (int i = 0; i < textureWidgetCount; ++i) { - if (!textures->flags(i).testFlag(QPlatformTextureList::StacksOnTop)) { - if (m_textureQuadData[i].isValid()) { - cb->setShaderResources(m_textureQuadData[i].srb); - cb->draw(6); + auto render = [&](std::optional<QRhiSwapChain::StereoTargetBuffer> buffer = std::nullopt) { + QRhiRenderTarget* target = nullptr; + if (buffer.has_value()) + target = swapchain->currentFrameRenderTarget(buffer.value()); + else + target = swapchain->currentFrameRenderTarget(); + + cb->beginPass(target, clearColor, { 1.0f, 0 }); + + cb->setGraphicsPipeline(m_psNoBlend.get()); + cb->setViewport({ 0, 0, float(outputSizeInPixels.width()), float(outputSizeInPixels.height()) }); + QRhiCommandBuffer::VertexInput vbufBinding(m_vbuf.get(), 0); + cb->setVertexInput(0, 1, &vbufBinding); + + // Textures for renderToTexture widgets. + for (int i = 0; i < textureWidgetCount; ++i) { + if (!textures->flags(i).testFlag(QPlatformTextureList::StacksOnTop)) { + if (m_textureQuadData[i].isValid()) { + + QRhiShaderResourceBindings* srb = m_textureQuadData[i].srb; + if (buffer == QRhiSwapChain::RightBuffer && m_textureQuadData[i].srbExtra) + srb = m_textureQuadData[i].srbExtra; + + cb->setShaderResources(srb); + cb->draw(6); + } } } - } - cb->setGraphicsPipeline(premultiplied ? m_psPremulBlend : m_psBlend); + cb->setGraphicsPipeline(premultiplied ? m_psPremulBlend.get() : m_psBlend.get()); - // Backingstore texture with the normal widgets. - if (m_texture) { - cb->setShaderResources(m_widgetQuadData.srb); - cb->draw(6); - } + // Backingstore texture with the normal widgets. + if (m_texture) { + cb->setShaderResources(m_widgetQuadData.srb); + cb->draw(6); + } - // Textures for renderToTexture widgets that have WA_AlwaysStackOnTop set. - for (int i = 0; i < textureWidgetCount; ++i) { - const QPlatformTextureList::Flags flags = textures->flags(i); - if (flags.testFlag(QPlatformTextureList::StacksOnTop)) { - if (m_textureQuadData[i].isValid()) { - if (flags.testFlag(QPlatformTextureList::NeedsPremultipliedAlphaBlending)) - cb->setGraphicsPipeline(m_psPremulBlend); - else - cb->setGraphicsPipeline(m_psBlend); - cb->setShaderResources(m_textureQuadData[i].srb); - cb->draw(6); + // Textures for renderToTexture widgets that have WA_AlwaysStackOnTop set. + for (int i = 0; i < textureWidgetCount; ++i) { + const QPlatformTextureList::Flags flags = textures->flags(i); + if (flags.testFlag(QPlatformTextureList::StacksOnTop)) { + if (m_textureQuadData[i].isValid()) { + if (flags.testFlag(QPlatformTextureList::NeedsPremultipliedAlphaBlending)) + cb->setGraphicsPipeline(m_psPremulBlend.get()); + else + cb->setGraphicsPipeline(m_psBlend.get()); + + QRhiShaderResourceBindings* srb = m_textureQuadData[i].srb; + if (buffer == QRhiSwapChain::RightBuffer && m_textureQuadData[i].srbExtra) + srb = m_textureQuadData[i].srbExtra; + + cb->setShaderResources(srb); + cb->draw(6); + } } } - } - cb->endPass(); + cb->endPass(); + }; + + if (swapchain->window()->format().stereo()) { + render(QRhiSwapChain::LeftBuffer); + render(QRhiSwapChain::RightBuffer); + } else + render(); rhi->endFrame(swapchain); |