Source code

Revision control

Copy as Markdown

// Copyright (C) 2025 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
#include "painterwindow.h"
#include "qcanvasrhipaintdriver.h"
#include <QPlatformSurfaceEvent>
#include <QCanvasPainter>
#include <QCanvasPainterFactory>
PainterWindow::PainterWindow(QRhi::Implementation api)
{
m_graphicsApi = api;
updateSurfaceType();
}
PainterWindow::~PainterWindow()
{
delete m_factory;
}
QColor PainterWindow::fillColor() const
{
return m_fillColor;
}
void PainterWindow::setFillColor(const QColor &color)
{
if (m_fillColor == color)
return;
m_fillColor = color;
requestUpdate();
}
void PainterWindow::cleanup() { }
void PainterWindow::updateSurfaceType()
{
switch (m_graphicsApi) {
case QRhi::OpenGLES2:
setSurfaceType(QSurface::OpenGLSurface);
break;
case QRhi::Vulkan:
setSurfaceType(QSurface::VulkanSurface);
break;
case QRhi::D3D11:
case QRhi::D3D12:
setSurfaceType(QSurface::Direct3DSurface);
break;
case QRhi::Metal:
setSurfaceType(QSurface::MetalSurface);
break;
default:
setSurfaceType(QSurface::RasterSurface);
break;
}
}
void PainterWindow::exposeEvent(QExposeEvent *)
{
if (isExposed() && !m_running) {
m_running = true;
init();
resizeSwapChain();
}
const QSize surfaceSize = m_hasSwapChain ? m_sc->surfacePixelSize() : QSize();
if ((!isExposed() || (m_hasSwapChain && surfaceSize.isEmpty())) && m_running && !m_notExposed)
m_notExposed = true;
if (isExposed() && m_running && m_notExposed && !surfaceSize.isEmpty()) {
m_notExposed = false;
m_newlyExposed = true;
}
if (isExposed() && !surfaceSize.isEmpty())
render();
}
bool PainterWindow::event(QEvent *e)
{
switch (e->type()) {
case QEvent::UpdateRequest:
render();
break;
case QEvent::PlatformSurface:
if (static_cast<QPlatformSurfaceEvent *>(e)->surfaceEventType()
== QPlatformSurfaceEvent::SurfaceAboutToBeDestroyed) {
cleanup();
releaseSwapChain();
}
break;
default:
break;
}
return QWindow::event(e);
}
void PainterWindow::init()
{
QRhi::Flags rhiFlags = QRhi::EnableDebugMarkers | QRhi::EnableTimestamps;
if (m_graphicsApi == QRhi::Null) {
QRhiNullInitParams params;
m_rhi.reset(QRhi::create(QRhi::Null, &params, rhiFlags));
}
#if QT_CONFIG(opengl)
if (m_graphicsApi == QRhi::OpenGLES2) {
m_fallbackSurface.reset(QRhiGles2InitParams::newFallbackSurface());
QRhiGles2InitParams params;
params.fallbackSurface = m_fallbackSurface.get();
params.window = this;
m_rhi.reset(QRhi::create(QRhi::OpenGLES2, &params, rhiFlags));
}
#endif
#if QT_CONFIG(vulkan)
if (m_graphicsApi == QRhi::Vulkan) {
QRhiVulkanInitParams params;
params.inst = vulkanInstance();
params.window = this;
m_rhi.reset(QRhi::create(QRhi::Vulkan, &params, rhiFlags));
}
#endif
#ifdef Q_OS_WIN
if (m_graphicsApi == QRhi::D3D11) {
QRhiD3D11InitParams params;
m_rhi.reset(QRhi::create(QRhi::D3D11, &params, rhiFlags));
} else if (m_graphicsApi == QRhi::D3D12) {
QRhiD3D12InitParams params;
m_rhi.reset(QRhi::create(QRhi::D3D12, &params, rhiFlags));
}
#endif
#if defined(Q_OS_MACOS) || defined(Q_OS_IOS)
if (m_graphicsApi == QRhi::Metal) {
QRhiMetalInitParams params;
m_rhi.reset(QRhi::create(QRhi::Metal, &params, rhiFlags));
}
#endif
if (!m_rhi)
qFatal("Failed to create RHI backend for graphics API %d", int(m_graphicsApi));
//![painterfactory]
if (!m_factory) {
m_factory = new QCanvasPainterFactory;
m_factory->create(m_rhi.get());
}
//![painterfactory]
m_sc.reset(m_rhi->newSwapChain());
m_ds.reset(m_rhi->newRenderBuffer(QRhiRenderBuffer::DepthStencil, QSize(), 1,
QRhiRenderBuffer::UsedWithSwapChainOnly));
m_sc->setWindow(this);
m_sc->setDepthStencil(m_ds.get());
m_rp.reset(m_sc->newCompatibleRenderPassDescriptor());
m_sc->setRenderPassDescriptor(m_rp.get());
}
void PainterWindow::resizeSwapChain()
{
m_hasSwapChain = m_sc->createOrResize();
}
void PainterWindow::releaseSwapChain()
{
if (m_hasSwapChain) {
m_hasSwapChain = false;
m_sc->destroy();
}
}
//![renderloop]
void PainterWindow::render()
{
if (!m_factory || !m_factory->isValid() || !m_rhi || !m_sc)
return;
if (!m_hasSwapChain || m_notExposed)
return;
if (m_sc->currentPixelSize() != m_sc->surfacePixelSize() || m_newlyExposed) {
resizeSwapChain();
if (!m_hasSwapChain)
return;
m_newlyExposed = false;
}
QRhi::FrameOpResult r = m_rhi->beginFrame(m_sc.get());
if (r == QRhi::FrameOpSwapChainOutOfDate) {
resizeSwapChain();
if (!m_hasSwapChain)
return;
r = m_rhi->beginFrame(m_sc.get());
}
if (r != QRhi::FrameOpSuccess) {
qDebug("beginFrame failed with %d, retry", r);
requestUpdate();
return;
}
QRhiCommandBuffer *cb = m_sc->currentFrameCommandBuffer();
QRhiRenderTarget *rt = m_sc->currentFrameRenderTarget();
QCanvasRhiPaintDriver *pd = m_factory->paintDriver();
QCanvasPainter *painter = m_factory->painter();
pd->resetForNewFrame();
pd->beginPaint(cb, rt, m_fillColor, size(), float(devicePixelRatio()));
paint(painter);
pd->endPaint();
m_rhi->endFrame(m_sc.get());
}
//![renderloop]
void PainterWindow::paint(QCanvasPainter *painter)
{
Q_UNUSED(painter);
}