3#include <QtCore/QCoreApplication>
4#include <QtCore/QEventLoop>
5#include <QtQml/QQmlComponent>
6#include <QtQml/QQmlEngine>
7#include <QtQml/QQmlError>
8#include <QtQuick/QQuickItem>
9#include <QtQuick/QQuickRenderControl>
10#include <QtQuick/QQuickRenderTarget>
11#include <QtQuick/QQuickWindow>
23 _renderControl->invalidate();
25 releaseRhiResources();
28void QGCOffscreenRenderer::releaseRhiResources()
31 _renderTarget.reset();
32 _depthStencil.reset();
36bool QGCOffscreenRenderer::ensureRhiTarget()
38 _rhi = _renderControl->rhi();
40 qCWarning(QGCOffscreenRendererLog) <<
"QQuickRenderControl produced no QRhi";
44 _texture.reset(_rhi->newTexture(QRhiTexture::RGBA8, _pixelSize, 1,
45 QRhiTexture::RenderTarget | QRhiTexture::UsedAsTransferSource));
46 if (!_texture->create()) {
47 qCWarning(QGCOffscreenRendererLog) <<
"Failed to create offscreen texture";
51 _depthStencil.reset(_rhi->newRenderBuffer(QRhiRenderBuffer::DepthStencil, _pixelSize));
52 if (!_depthStencil->create()) {
53 qCWarning(QGCOffscreenRendererLog) <<
"Failed to create depth-stencil buffer";
57 QRhiTextureRenderTargetDescription rtDesc(QRhiColorAttachment(_texture.get()), _depthStencil.get());
58 _renderTarget.reset(_rhi->newTextureRenderTarget(rtDesc));
59 _rpDesc.reset(_renderTarget->newCompatibleRenderPassDescriptor());
60 _renderTarget->setRenderPassDescriptor(_rpDesc.get());
61 if (!_renderTarget->create()) {
62 qCWarning(QGCOffscreenRendererLog) <<
"Failed to create texture render target";
66 _quickWindow->setRenderTarget(QQuickRenderTarget::fromRhiRenderTarget(_renderTarget.get()));
72 if (pixelSize.isEmpty()) {
73 qCWarning(QGCOffscreenRendererLog) <<
"Invalid pixel size" << pixelSize;
76 _pixelSize = pixelSize;
78 _renderControl = std::make_unique<QQuickRenderControl>(
this);
79 _quickWindow = std::make_unique<QQuickWindow>(_renderControl.get());
81 _qmlEngine = std::make_unique<QQmlEngine>();
82 if (!_qmlEngine->incubationController()) {
83 _qmlEngine->setIncubationController(_quickWindow->incubationController());
86 _qmlComponent = std::make_unique<QQmlComponent>(_qmlEngine.get(), qmlSource);
87 if (_qmlComponent->isError()) {
88 for (
const QQmlError&
error : _qmlComponent->errors()) {
89 qCWarning(QGCOffscreenRendererLog) <<
error.toString();
94 std::unique_ptr<QObject> rootObject(_qmlComponent->create());
95 if (_qmlComponent->isError()) {
96 for (
const QQmlError&
error : _qmlComponent->errors()) {
97 qCWarning(QGCOffscreenRendererLog) <<
error.toString();
102 _rootItem = qobject_cast<QQuickItem*>(rootObject.get());
104 qCWarning(QGCOffscreenRendererLog) <<
"QML root is not a QQuickItem";
107 rootObject.release()->setParent(_quickWindow.get());
109 _rootItem->setParentItem(_quickWindow->contentItem());
110 _rootItem->setSize(_pixelSize);
111 _quickWindow->setGeometry(0, 0, _pixelSize.width(), _pixelSize.height());
113 if (!_renderControl->initialize()) {
114 qCWarning(QGCOffscreenRendererLog) <<
"QQuickRenderControl::initialize() failed";
118 if (!ensureRhiTarget()) {
129 qCWarning(QGCOffscreenRendererLog) <<
"renderToImage() before successful load()";
133 _renderControl->polishItems();
134 _renderControl->beginFrame();
135 _renderControl->sync();
136 _renderControl->render();
139 QRhiReadbackResult readback;
141 readback.completed = [&]() {
142 const QImage img(
reinterpret_cast<const uchar*
>(readback.data.constData()), readback.pixelSize.width(),
143 readback.pixelSize.height(), QImage::Format_RGBA8888_Premultiplied);
148 QRhiResourceUpdateBatch* batch = _rhi->nextResourceUpdateBatch();
149 QRhiReadbackDescription rb(_texture.get());
150 batch->readBackTexture(rb, &readback);
152 QRhiCommandBuffer* cb = _renderControl->commandBuffer();
154 cb->resourceUpdate(batch);
157 qCWarning(QGCOffscreenRendererLog) <<
"No command buffer for readback";
160 _renderControl->endFrame();
163 qCWarning(QGCOffscreenRendererLog) <<
"Texture readback did not complete";
167 if (_rhi->isYUpInFramebuffer()) {
168 result.flip(Qt::Vertical);
#define QGC_LOGGING_CATEGORY(name, categoryStr)
Renders a QML scene into an offscreen RHI texture and reads it back to a QImage.
bool load(const QUrl &qmlSource, const QSize &pixelSize)
~QGCOffscreenRenderer() override