QT6 Render QML to images using QQuickRenderControl
-
wrote on 3 Mar 2024, 17:18 last edited by
I am looking for some sample code snnipets to render a qml (animated) to set of png images offscreen using framebuffer as similar to the implementation mentioned in the below forum
https://www.qt.io/blog/2017/02/21/making-movies-qml
I have tried using the QQuickWindow and QquickRenderControl libraries of QT6 but its not working.
Not sure how to initialise the QquickRenderControl with the framebuffer object as the initialize object wont take OpenGLContext in QT6 . Any help is much appreciated . Thanks
-
I am looking for some sample code snnipets to render a qml (animated) to set of png images offscreen using framebuffer as similar to the implementation mentioned in the below forum
https://www.qt.io/blog/2017/02/21/making-movies-qml
I have tried using the QQuickWindow and QquickRenderControl libraries of QT6 but its not working.
Not sure how to initialise the QquickRenderControl with the framebuffer object as the initialize object wont take OpenGLContext in QT6 . Any help is much appreciated . Thanks
-
@Aujo In Qt 6, you need to set
QQuickWindow::setGraphicsApi(QSGRendererInterface::OpenGL);
beforeQGuiApplication app(argc, argv);
to use the old way of rendering.wrote on 16 Mar 2024, 10:57 last edited by@ZeroZero Thanks for your response below is my code refactored for QT6 . I have set q->setGraphicsApi(QSGRendererInterface::OpenGL); here previously .
Was getting the below error.
error:
QQuickWindow: No render target (neither swapchain nor custom target was provided)Moving that to main as you mentioned still get the same error with some additional .
int main(int argc, char *argv[])
{
{
QQuickWindow::setGraphicsApi(QSGRendererInterface::OpenGL);
QCoreApplication app(argc, argv);
....
........................................................................................
Error:
Framebuffer incomplete: 0x8cd6
Failed to build texture render target for QQuickRenderTarget
QQuickWindow: No render target (neither swapchain nor custom target was provided)Refactored Code for QT6 that I am struggling to make it work
#include "fboquickwindow.h" #include <QOffscreenSurface> #include <QOpenGLContext> #include <QOpenGLFramebufferObject> #include <QOpenGLFunctions> #include <QQuickRenderControl> #include <QTimer> #include <QQuickRenderTarget> class FboQuickWindowPrivate : public QObject { Q_OBJECT Q_DECLARE_PUBLIC(FboQuickWindow) Q_DISABLE_COPY(FboQuickWindowPrivate) public: FboQuickWindowPrivate(FboQuickWindow *q_ptr); ~FboQuickWindowPrivate(); static FboQuickWindowPrivate *create(FboQuickWindowPrivate *&d_ptr, FboQuickWindow *q_ptr); public slots: void sceneGraphInitialized(); void sceneGraphInvalidated(); void renderRequested(); void sceneChanged(); public: void render(); public: QOpenGLContext *context; QOffscreenSurface *offscreenSurface; QQuickRenderControl *renderControl; QOpenGLFramebufferObject *fbo; bool needsPolishAndSync; QTimer updateTimer; FboQuickWindow * const q_ptr; }; FboQuickWindowPrivate::FboQuickWindowPrivate(FboQuickWindow *q_ptr) : fbo(0), needsPolishAndSync(false), q_ptr(q_ptr) { updateTimer.setSingleShot(true); updateTimer.setInterval(5); connect(&updateTimer, &QTimer::timeout, this, &FboQuickWindowPrivate::render); renderControl = new QQuickRenderControl; connect(renderControl, &QQuickRenderControl::renderRequested, this, &FboQuickWindowPrivate::renderRequested); connect(renderControl, &QQuickRenderControl::sceneChanged, this, &FboQuickWindowPrivate::sceneChanged); QSurfaceFormat format; format.setDepthBufferSize(16); format.setStencilBufferSize(8); context = new QOpenGLContext; context->setFormat(format); context->create(); offscreenSurface = new QOffscreenSurface; offscreenSurface->setFormat(context->format()); offscreenSurface->create(); } FboQuickWindowPrivate::~FboQuickWindowPrivate() { context->makeCurrent(offscreenSurface); delete renderControl; context->doneCurrent(); delete offscreenSurface; delete context; } FboQuickWindowPrivate *FboQuickWindowPrivate::create(FboQuickWindowPrivate *&d_ptr, FboQuickWindow *q_ptr) { return (d_ptr = new FboQuickWindowPrivate(q_ptr)); } void FboQuickWindowPrivate::sceneGraphInitialized() { Q_Q(FboQuickWindow); Q_ASSERT(context); Q_ASSERT(!fbo); fbo = new QOpenGLFramebufferObject(QSize(1, 1).expandedTo(q->size()), QOpenGLFramebufferObject::CombinedDepthStencil); QQuickRenderTarget rt = QQuickRenderTarget::fromOpenGLRenderBuffer(fbo->handle(), fbo->size() ,1); q->setGraphicsApi(QSGRendererInterface::OpenGL); q->setRenderTarget(rt); } void FboQuickWindowPrivate::sceneGraphInvalidated() { delete fbo; fbo = 0; } void FboQuickWindowPrivate::renderRequested() { if (!updateTimer.isActive()) updateTimer.start(); } void FboQuickWindowPrivate::sceneChanged() { needsPolishAndSync = true; if (!updateTimer.isActive()) updateTimer.start(); } void FboQuickWindowPrivate::render() { Q_Q(FboQuickWindow); if (!context->makeCurrent(offscreenSurface)) return; // if (needsPolishAndSync) { // renderControl->polishItems(); // renderControl->beginFrame(); // renderControl->sync(); // renderControl->endFrame(); // needsPolishAndSync = false; // } q->beginExternalCommands(); // Begin external OpenGL commands renderControl->beginFrame(); renderControl->sync(); renderControl->render(); renderControl->endFrame(); q->endExternalCommands(); // End external OpenGL commands // q->resetOpenGLState(); //QOpenGLFramebufferObject::bindDefault(); //context->functions()->glFlush(); emit q->sceneRendered(); } FboQuickWindow::FboQuickWindow() : QQuickWindow(FboQuickWindowPrivate::create(d_ptr, this)->renderControl) { Q_D(FboQuickWindow); connect(this, &QQuickWindow::sceneGraphInitialized, d, &FboQuickWindowPrivate::sceneGraphInitialized); connect(this, &QQuickWindow::sceneGraphInvalidated, d, &FboQuickWindowPrivate::sceneGraphInvalidated); d->context->makeCurrent(d->offscreenSurface); d->renderControl->initialize(); // if (!d->renderControl->initialize()) // qWarning("Failed to initialize redirected Qt Quick rendering"); d->context->doneCurrent(); } FboQuickWindow::~FboQuickWindow() { delete d_ptr; } QImage FboQuickWindow::grab() const { Q_D(const FboQuickWindow); return d->renderControl->window()->grabWindow(); } void FboQuickWindow::resize(const QSize &newSize) { QResizeEvent event(size(), newSize); setGeometry(0, 0, newSize.width(), newSize.height()); resizeEvent(&event); } void FboQuickWindow::resize(int w, int h) { resize(QSize(w, h)); } void FboQuickWindow::resizeEvent(QResizeEvent *event) { Q_D(FboQuickWindow); QQuickWindow::resizeEvent(event); Q_ASSERT(d->context); if (d->context->makeCurrent(d->offscreenSurface)) { d->sceneGraphInvalidated(); d->sceneGraphInitialized(); d->context->doneCurrent(); } } #include "fboquickwindow.moc"
And here is the original code that works with QT5
#include "fboquickwindow.h" #include <QOffscreenSurface> #include <QOpenGLContext> #include <QOpenGLFramebufferObject> #include <QOpenGLFunctions> #include <QQuickRenderControl> #include <QTimer> class FboQuickWindowPrivate : public QObject { Q_OBJECT Q_DECLARE_PUBLIC(FboQuickWindow) Q_DISABLE_COPY(FboQuickWindowPrivate) public: FboQuickWindowPrivate(FboQuickWindow *q_ptr); ~FboQuickWindowPrivate(); static FboQuickWindowPrivate *create(FboQuickWindowPrivate *&d_ptr, FboQuickWindow *q_ptr); public slots: void sceneGraphInitialized(); void sceneGraphInvalidated(); void renderRequested(); void sceneChanged(); public: void render(); public: QOpenGLContext *context; QOffscreenSurface *offscreenSurface; QQuickRenderControl *renderControl; QOpenGLFramebufferObject *fbo; bool needsPolishAndSync; QTimer updateTimer; FboQuickWindow * const q_ptr; }; FboQuickWindowPrivate::FboQuickWindowPrivate(FboQuickWindow *q_ptr) : fbo(0), needsPolishAndSync(false), q_ptr(q_ptr) { updateTimer.setSingleShot(true); updateTimer.setInterval(5); connect(&updateTimer, &QTimer::timeout, this, &FboQuickWindowPrivate::render); renderControl = new QQuickRenderControl; connect(renderControl, &QQuickRenderControl::renderRequested, this, &FboQuickWindowPrivate::renderRequested); connect(renderControl, &QQuickRenderControl::sceneChanged, this, &FboQuickWindowPrivate::sceneChanged); QSurfaceFormat format; format.setDepthBufferSize(16); format.setStencilBufferSize(8); context = new QOpenGLContext; context->setFormat(format); context->create(); offscreenSurface = new QOffscreenSurface; offscreenSurface->setFormat(context->format()); offscreenSurface->create(); } FboQuickWindowPrivate::~FboQuickWindowPrivate() { context->makeCurrent(offscreenSurface); delete renderControl; context->doneCurrent(); delete offscreenSurface; delete context; } FboQuickWindowPrivate *FboQuickWindowPrivate::create(FboQuickWindowPrivate *&d_ptr, FboQuickWindow *q_ptr) { return (d_ptr = new FboQuickWindowPrivate(q_ptr)); } void FboQuickWindowPrivate::sceneGraphInitialized() { Q_Q(FboQuickWindow); Q_ASSERT(context); Q_ASSERT(!fbo); fbo = new QOpenGLFramebufferObject(QSize(1, 1).expandedTo(q->size()), QOpenGLFramebufferObject::CombinedDepthStencil); q->setRenderTarget(fbo); } void FboQuickWindowPrivate::sceneGraphInvalidated() { delete fbo; fbo = 0; } void FboQuickWindowPrivate::renderRequested() { if (!updateTimer.isActive()) updateTimer.start(); } void FboQuickWindowPrivate::sceneChanged() { needsPolishAndSync = true; if (!updateTimer.isActive()) updateTimer.start(); } void FboQuickWindowPrivate::render() { Q_Q(FboQuickWindow); if (!context->makeCurrent(offscreenSurface)) return; if (needsPolishAndSync) { renderControl->polishItems(); renderControl->sync(); needsPolishAndSync = false; } renderControl->render(); q->resetOpenGLState(); QOpenGLFramebufferObject::bindDefault(); context->functions()->glFlush(); emit q->sceneRendered(); } FboQuickWindow::FboQuickWindow() : QQuickWindow(FboQuickWindowPrivate::create(d_ptr, this)->renderControl) { Q_D(FboQuickWindow); connect(this, &QQuickWindow::sceneGraphInitialized, d, &FboQuickWindowPrivate::sceneGraphInitialized); connect(this, &QQuickWindow::sceneGraphInvalidated, d, &FboQuickWindowPrivate::sceneGraphInvalidated); d->context->makeCurrent(d->offscreenSurface); d->renderControl->initialize(d->context); d->context->doneCurrent(); } FboQuickWindow::~FboQuickWindow() { delete d_ptr; } QImage FboQuickWindow::grab() const { Q_D(const FboQuickWindow); return d->renderControl->grab(); } void FboQuickWindow::resize(const QSize &newSize) { QResizeEvent event(size(), newSize); setGeometry(0, 0, newSize.width(), newSize.height()); resizeEvent(&event); } void FboQuickWindow::resize(int w, int h) { resize(QSize(w, h)); } void FboQuickWindow::resizeEvent(QResizeEvent *event) { Q_D(FboQuickWindow); QQuickWindow::resizeEvent(event); Q_ASSERT(d->context); if (d->context->makeCurrent(d->offscreenSurface)) { d->sceneGraphInvalidated(); d->sceneGraphInitialized(); d->context->doneCurrent(); } } #include "fboquickwindow.moc"
Any thoughts or inputs is very helpful, many thanks.