// sanity check for Vulkan #ifdef ENABLE_VULKAN_RENDER if (rhiSupport->rhiBackend() == QRhi::Vulkan) { vkInstance.reset(newQVulkanInstance());
auto phdev = wlr_vk_renderer_get_physical_device(m_renderer->handle()); auto dev = wlr_vk_renderer_get_device(m_renderer->handle()); auto queue_family = wlr_vk_renderer_get_queue_family(m_renderer->handle());
QOffscreenSurface *offscreenSurface = new QW::OffscreenSurface(nullptr, q); offscreenSurface->create();
QSGRhiSupport::RhiCreateResult result = rhiSupport->createRhi(q, offscreenSurface); if (!result.rhi) { qWarning("WOutput::initRhi: Failed to initialize QRhi"); returnfalse; }
rcd->rhi = result.rhi; // Ensure the QQuickRenderControl don't reinit the RHI rcd->ownRhi = true; if (!rc()->initialize()) returnfalse; rcd->ownRhi = result.own; Q_ASSERT(rcd->rhi == result.rhi); Q_ASSERT(!swapchain);
if (QSGRendererInterface::isApiRhiBased(WRenderHelper::getGraphicsApi())) rc()->endFrame();
if (doCommit) { for (auto i : std::as_const(needsCommit)) { bool ok = i.first->commit(i.second);
if (i.second->currentBuffer()) { i.second->endRender(); }
i.first->resetState(ok); } }
resetGlState();
// On Intel&Nvidia multi-GPU environment, wlroots using Intel card do render for all // outputs, and blit nvidia's output buffer in drm_connector_state_update_primary_fb, // the 'blit' behavior will make EGL context to Nvidia renderer. So must done current // OpenGL context here in order to ensure QtQuick always make EGL context to Intel // renderer before next frame. if (glContext) glContext->doneCurrent();
// configure swapchain if (flags.testFlag(RenderFlag::DontConfigureSwapchain)) { auto renderFormat = pickFormat(m_output->renderer(), format); if (!renderFormat) { qWarning("wlr_renderer doesn't support format 0x%s", drmGetFormatName(format)); returnnullptr; }
if (!m_swapchain || QSize(m_swapchain->handle()->width, m_swapchain->handle()->height) != pixelSize || m_swapchain->handle()->format.format != renderFormat->format) { if (m_swapchain) delete m_swapchain; m_swapchain = qw_swapchain::create(m_output->allocator()->handle(), pixelSize.width(), pixelSize.height(), renderFormat); } } elseif (flags.testFlag(RenderFlag::UseCursorFormats)) { bool ok = m_output->configureCursorSwapchain(pixelSize, format, &m_swapchain); if (!ok) returnnullptr; } else { bool ok = m_output->configurePrimarySwapchain(pixelSize, format, &m_swapchain, !flags.testFlag(DontTestSwapchain)); if (!ok) returnnullptr; }
// TODO: Support scanout buffer of wlr_surface(from WSurfaceItem) int bufferAge; auto wbuffer = m_swapchain->acquire(&bufferAge); if (!wbuffer) returnnullptr; auto buffer = qw_buffer::from(wbuffer);
if (!m_renderHelper) m_renderHelper = newWRenderHelper(m_output->renderer()); m_renderHelper->setSize(pixelSize);
auto wd = QQuickWindowPrivate::get(window()); Q_ASSERT(wd->renderControl); auto lastRT = m_renderHelper->lastRenderTarget(); auto rt = m_renderHelper->acquireRenderTarget(wd->renderControl, buffer); if (rt.isNull()) { buffer->unlock(); returnnullptr; }
auto rtd = QQuickRenderTargetPrivate::get(&rt); QSGRenderTarget sgRT;
auto softwareRenderer = dynamic_cast<QSGSoftwareRenderer*>(renderer); { // before render if (softwareRenderer) { // because software renderer don't supports viewportRect, // so use transform to simulation. constauto mapTransform = inputMapToOutput(sourceRect, targetRect, state.pixelSize, state.devicePixelRatio); if (!mapTransform.isIdentity()) state.worldTransform = mapTransform * state.worldTransform; state.worldTransform.optimize(); auto image = getImageFrom(state.renderTarget); image->setDevicePixelRatio(devicePixelRatio);
// TODO: Should set to QSGSoftwareRenderer, but it's not support specify matrix. // If transform is changed, it will full repaint. if (isRootItem(source.source)) { auto rootTransform = QQuickItemPrivate::get(wd->contentItem)->itemNode(); if (rootTransform->matrix() != state.worldTransform) rootTransform->setMatrix(state.worldTransform); } else { auto t = state.worldTransform.toTransform(); if (t.type() > QTransform::TxTranslate) { (image->operator QImage &()).fill(renderer->clearColor()); softwareRenderer->markDirty(); }
auto textureRT = static_cast<QRhiTextureRenderTarget*>(state.sgRenderTarget.rt); if (preserveColorContents) { textureRT->setFlags(textureRT->flags() | QRhiTextureRenderTarget::PreserveColorContents); } else { textureRT->setFlags(textureRT->flags() & ~QRhiTextureRenderTarget::PreserveColorContents); } } }
state.context->renderNextFrame(renderer);
{ // after render if (!softwareRenderer) { // TODO: get damage area from QRhi renderer m_damageRing.add_whole(); // ###: maybe Qt bug? Before executing QRhi::endOffscreenFrame, we may // use the same QSGRenderer for multiple drawings. This can lead to // rendering the same content for different QSGRhiRenderTarget instances // when using the RhiGles backend. Additionally, considering that the // result of the current drawing may be needed when drawing the next // sourceIndex, we should let the RHI (Rendering Hardware Interface) // complete the results of this drawing here to ensure the current // drawing result is available for use. wd->rhi->finish(); } else { auto currentImage = getImageFrom(state.renderTarget); Q_ASSERT(currentImage && currentImage == softwareRenderer->m_rt.paintDevice); currentImage->setDevicePixelRatio(1.0); constauto scaleTF = QTransform::fromScale(devicePixelRatio, devicePixelRatio); constauto scaledFlushRegion = scaleTF.map(softwareRenderer->flushRegion()); PixmanRegion scaledFlushDamage; bool ok = WTools::toPixmanRegion(scaledFlushRegion, scaledFlushDamage); Q_ASSERT(ok);
if (viewportRect.isValid()) { QRect imageRect = (currentImage->operatorconst QImage &()).rect(); QRegion invalidRegion(imageRect); invalidRegion -= viewportRect; if (!scaledFlushRegion.isEmpty()) invalidRegion &= scaledFlushRegion;
if (!invalidRegion.isEmpty()) { QPainter pa(currentImage); for (constauto r : std::as_const(invalidRegion)) pa.fillRect(r, softwareRenderer->clearColor()); } }
if (!damage.isEmpty() && state.lastRT.first != state.buffer && !state.lastRT.second.isNull()) { auto image = getImageFrom(state.lastRT.second); Q_ASSERT(image); Q_ASSERT(image->size() == state.pixelSize);
// TODO: Don't use the previous render target, we can get the damage region of QtQuick // before QQuickRenderControl::render for qw_damage_ring, and add dirty region to // QSGAbstractSoftwareRenderer to force repaint the damage region of current render target. QPainter pa(currentImage);
PixmanRegion remainderDamage; ok = pixman_region32_subtract(remainderDamage, damage, scaledFlushDamage); Q_ASSERT(ok);
int count = 0; auto rects = pixman_region32_rectangles(remainderDamage, &count); for (int i = 0; i < count; ++i) { auto r = rects[i]; pa.drawImage(r.x1, r.y1, *image, r.x1, r.y1, r.x2 - r.x1, r.y2 - r.y1); } } }
if (!isRootItem(source.source)) applyTransform(softwareRenderer, state.worldTransform.inverted().toTransform()); m_damageRing.add(scaledFlushDamage); } }
if (auto dr = qobject_cast<QSGDefaultRenderContext*>(state.context)) { QRhiResourceUpdateBatch *resourceUpdates = wd->rhi->nextResourceUpdateBatch(); dr->currentFrameCommandBuffer()->resourceUpdate(resourceUpdates); }
if (shouldCacheBuffer()) wTextureProvider()->setBuffer(state.buffer); }
处理完画面以后,如果需要上屏画面,就调用 commit 把画面送到屏幕上。
boolOutputHelper::commit(WBufferRenderer *buffer) { if (output()->offscreen()) returntrue;
if (!buffer || !buffer->currentBuffer()) { Q_ASSERT(!this->buffer()); return WOutputHelper::commit(); }
setBuffer(buffer->currentBuffer());
if (m_lastCommitBuffer == buffer) { if (pixman_region32_not_empty(&buffer->damageRing()->handle()->current)) setDamage(&buffer->damageRing()->handle()->current); }
auto w = qobject_cast<WOutputRenderWindow*>(d->window); if (!w || !d->sceneGraphRenderContext() || QThread::currentThread() != d->sceneGraphRenderContext()->thread()) { qWarning("WQuickCursor::textureProvider: can only be queried on the rendering thread of an WOutputRenderWindow"); returnnullptr; }
if (!d->textureProvider) { d->textureProvider = newWSGTextureProvider(w); if (d->surface) { if (auto texture = d->surface->handle()->get_texture()) { d->textureProvider->setTexture(qw_texture::from(texture), d->buffer.get()); } else { d->textureProvider->setBuffer(d->buffer.get()); } } } return d->textureProvider; }