6#include <QtQuick/private/qquickpainteditem_p.h>
8#include <QtQuick/private/qsgdefaultrendercontext_p.h>
9#include <QtQuick/private/qsgcontext_p.h>
14#include <private/qopenglextensions_p.h>
20#define QT_MINIMUM_DYNAMIC_FBO_SIZE 64U
22QSGPainterTexture::QSGPainterTexture()
23 : QSGPlainTexture(*(
new QSGPlainTexturePrivate(
this)))
25 m_retain_image =
true;
28void QSGPainterTexture::commitTextureOperations(QRhi *rhi, QRhiResourceUpdateBatch *resourceUpdates)
30 if (!m_dirty_rect.isNull()) {
32 m_dirty_rect = QRect();
34 QSGPlainTexture::commitTextureOperations(rhi, resourceUpdates);
37QSGDefaultPainterNode::QSGDefaultPainterNode(QQuickPaintedItem *item)
39 , m_preferredRenderTarget(QQuickPaintedItem::Image)
40 , m_actualRenderTarget(QQuickPaintedItem::Image)
42 , m_geometry(QSGGeometry::defaultAttributes_TexturedPoint2D(), 4)
46 , m_multisampledFbo(
nullptr)
47 , m_gl_device(
nullptr)
48 , m_wrapperTexture(
nullptr)
50 , m_fillColor(Qt::transparent)
51 , m_contentsScale(1.0)
52 , m_dirtyContents(
false)
53 , m_opaquePainting(
false)
54 , m_linear_filtering(
false)
56 , m_smoothPainting(
false)
58 , m_extensionsChecked(
false)
59 , m_multisamplingSupported(
false)
61 , m_fastFBOResizing(
false)
62 , m_dirtyGeometry(
false)
63 , m_dirtyRenderTarget(
false)
64 , m_dirtyTexture(
false)
66 m_context =
static_cast<QSGDefaultRenderContext *>(
static_cast<QQuickPaintedItemPrivate *>(QObjectPrivate::get(item))->sceneGraphRenderContext());
68 setMaterial(&m_materialO);
69 setOpaqueMaterial(&m_material);
70 setGeometry(&m_geometry);
72#ifdef QSG_RUNTIME_DESCRIPTION
73 qsgnode_set_description(
this, QString::fromLatin1(
"QQuickPaintedItem(%1):%2").arg(QString::fromLatin1(item->metaObject()->className())).arg(item->objectName()));
77QSGDefaultPainterNode::~QSGDefaultPainterNode()
82 delete m_wrapperTexture;
84 delete m_multisampledFbo;
89void QSGDefaultPainterNode::paint()
91 QRect dirtyRect = m_dirtyRect.isNull() ? QRect(0, 0, m_size.width(), m_size.height()) : m_dirtyRect;
95 if (m_context->rhi()->backend() == QRhi::OpenGLES2 && m_actualRenderTarget != QQuickPaintedItem::Image) {
97 m_gl_device =
new QOpenGLPaintDevice(m_fboSize);
98 m_gl_device->setPaintFlipped(
true);
101 if (m_multisampledFbo)
102 m_multisampledFbo->bind();
106 painter.begin(m_gl_device);
110 Q_ASSERT(m_actualRenderTarget == QQuickPaintedItem::Image);
111 if (m_image.isNull())
114 m_texture->setImage(QImage());
115 painter.begin(&m_image);
118 if (m_smoothPainting) {
119 painter.setRenderHints(QPainter::Antialiasing | QPainter::TextAntialiasing | QPainter::SmoothPixmapTransform);
123 QRect dirtyTextureRect;
125 if (m_contentsScale == 1) {
126 qreal scaleX = m_textureSize.width() / (qreal) m_size.width();
127 qreal scaleY = m_textureSize.height() / (qreal) m_size.height();
128 painter.scale(scaleX, scaleY);
129 clipRect = dirtyRect;
130 dirtyTextureRect = QRectF(dirtyRect.x() * scaleX,
131 dirtyRect.y() * scaleY,
132 dirtyRect.width() * scaleX,
133 dirtyRect.height() * scaleY).toAlignedRect();
135 painter.scale(m_contentsScale, m_contentsScale);
136 QRect sclip(qFloor(dirtyRect.x()/m_contentsScale),
137 qFloor(dirtyRect.y()/m_contentsScale),
138 qCeil(dirtyRect.width()/m_contentsScale+dirtyRect.x()/m_contentsScale-qFloor(dirtyRect.x()/m_contentsScale)),
139 qCeil(dirtyRect.height()/m_contentsScale+dirtyRect.y()/m_contentsScale-qFloor(dirtyRect.y()/m_contentsScale)));
141 dirtyTextureRect = dirtyRect;
145 if (!m_dirtyRect.isNull()) {
146 painter.setClipRect(clipRect);
149 painter.setCompositionMode(QPainter::CompositionMode_Source);
150 if (m_fillColor.isValid())
151 painter.fillRect(clipRect, m_fillColor);
152 painter.setCompositionMode(QPainter::CompositionMode_SourceOver);
154 m_item->paint(&painter);
158 if (m_context->rhi()->backend() == QRhi::OpenGLES2 && m_actualRenderTarget != QQuickPaintedItem::Image) {
159 if (m_multisampledFbo) {
160 QOpenGLFramebufferObject::blitFramebuffer(m_fbo, dirtyTextureRect, m_multisampledFbo, dirtyTextureRect);
161 m_multisampledFbo->release();
168 m_texture->setImage(m_image);
169 m_texture->setDirtyRect(dirtyTextureRect);
172 m_dirtyRect = QRect();
175void QSGDefaultPainterNode::update()
177 if (m_dirtyRenderTarget)
178 updateRenderTarget();
187 m_dirtyGeometry =
false;
188 m_dirtyRenderTarget =
false;
189 m_dirtyTexture =
false;
190 m_dirtyContents =
false;
193void QSGDefaultPainterNode::updateTexture()
195 m_texture->setHasAlphaChannel(!m_opaquePainting);
196 m_material.setTexture(m_texture);
197 m_materialO.setTexture(m_texture);
199 markDirty(DirtyMaterial);
202void QSGDefaultPainterNode::updateGeometry()
206 if (m_context->rhi()->backend() == QRhi::OpenGLES2 && m_actualRenderTarget != QQuickPaintedItem::Image) {
207 source = QRectF(0, 0, qreal(m_textureSize.width()) / m_fboSize.width(), qreal(m_textureSize.height()) / m_fboSize.height());
211 source = QRectF(0, 0, 1, 1);
213 QRectF dest(0, 0, m_size.width(), m_size.height());
214 if (m_actualRenderTarget == QQuickPaintedItem::InvertedYFramebufferObject)
215 dest = QRectF(QPointF(0, m_size.height()), QPointF(m_size.width(), 0));
216 QSGGeometry::updateTexturedRectGeometry(&m_geometry,
219 markDirty(DirtyGeometry);
222void QSGDefaultPainterNode::updateRenderTarget()
224 m_dirtyContents =
true;
227 if (m_context->rhi()->backend() == QRhi::OpenGLES2) {
228 if (!m_extensionsChecked) {
229 QOpenGLExtensions *e =
static_cast<QOpenGLExtensions *>(QOpenGLContext::currentContext()->functions());
230 m_multisamplingSupported = e->hasOpenGLExtension(QOpenGLExtensions::FramebufferMultisample)
231 && e->hasOpenGLExtension(QOpenGLExtensions::FramebufferBlit);
232 m_extensionsChecked =
true;
234 QQuickPaintedItem::RenderTarget oldTarget = m_actualRenderTarget;
235 if (m_preferredRenderTarget == QQuickPaintedItem::Image) {
236 m_actualRenderTarget = QQuickPaintedItem::Image;
241 if (!m_multisamplingSupported && m_smoothPainting)
242 m_actualRenderTarget = QQuickPaintedItem::Image;
244 m_actualRenderTarget = m_preferredRenderTarget;
246 if (oldTarget != m_actualRenderTarget) {
249 delete m_multisampledFbo;
251 m_fbo = m_multisampledFbo =
nullptr;
252 m_gl_device =
nullptr;
255 if (m_actualRenderTarget == QQuickPaintedItem::FramebufferObject
256 || m_actualRenderTarget == QQuickPaintedItem::InvertedYFramebufferObject)
258 const QOpenGLContext *ctx =
static_cast<
const QRhiGles2NativeHandles *>(m_context->rhi()->nativeHandles())->context;
259 if (m_fbo && !m_dirtyGeometry && (!ctx->format().samples() || !m_multisamplingSupported))
262 if (m_fboSize.isEmpty())
266 delete m_multisampledFbo;
267 m_fbo = m_multisampledFbo =
nullptr;
269 m_gl_device->setSize(m_fboSize);
271 if (m_smoothPainting && ctx->format().samples() && m_multisamplingSupported) {
272 QOpenGLFramebufferObjectFormat msaaFormat;
273 msaaFormat.setAttachment(QOpenGLFramebufferObject::CombinedDepthStencil);
274 msaaFormat.setSamples(8);
275 m_multisampledFbo =
new QOpenGLFramebufferObject(m_fboSize, msaaFormat);
276 QOpenGLFramebufferObjectFormat format;
277 format.setAttachment(QOpenGLFramebufferObject::NoAttachment);
278 m_fbo =
new QOpenGLFramebufferObject(m_fboSize, format);
280 QOpenGLFramebufferObjectFormat format;
281 format.setAttachment(QOpenGLFramebufferObject::CombinedDepthStencil);
282 m_fbo =
new QOpenGLFramebufferObject(m_fboSize, format);
285 if (!m_image.isNull() && !m_dirtyGeometry)
288 m_image = QImage(m_textureSize, QImage::Format_RGBA8888_Premultiplied);
289 m_image.fill(Qt::transparent);
292 QSGPainterTexture *texture =
new QSGPainterTexture;
293 if (m_actualRenderTarget == QQuickPaintedItem::Image) {
294 texture->setOwnsTexture(
true);
295 texture->setTextureSize(m_textureSize);
297 if (!m_wrapperTexture)
298 m_wrapperTexture = m_context->rhi()->newTexture(QRhiTexture::RGBA8, m_fboSize);
299 m_wrapperTexture->createFrom({ m_fbo->texture(), 0 });
300 texture->setTexture(m_wrapperTexture);
301 texture->setOwnsTexture(
false);
302 texture->setTextureSize(m_fboSize);
312 m_actualRenderTarget = QQuickPaintedItem::Image;
313 if (!m_image.isNull() && !m_dirtyGeometry)
316 m_image = QImage(m_textureSize, QImage::Format_RGBA8888_Premultiplied);
317 m_image.fill(Qt::transparent);
320 m_texture =
new QSGPainterTexture;
321 m_texture->setOwnsTexture(
true);
323 m_texture->setTextureSize(m_textureSize);
328void QSGDefaultPainterNode::updateFBOSize()
332 if (m_fastFBOResizing) {
333 fboWidth = qMax(QT_MINIMUM_DYNAMIC_FBO_SIZE, qNextPowerOfTwo(m_textureSize.width() - 1));
334 fboHeight = qMax(QT_MINIMUM_DYNAMIC_FBO_SIZE, qNextPowerOfTwo(m_textureSize.height() - 1));
336 QSize minimumFBOSize = m_context->sceneGraphContext()->minimumFBOSize();
337 fboWidth = qMax(minimumFBOSize.width(), m_textureSize.width());
338 fboHeight = qMax(minimumFBOSize.height(), m_textureSize.height());
340 m_fboSize = QSize(fboWidth, fboHeight);
344void QSGDefaultPainterNode::setPreferredRenderTarget(QQuickPaintedItem::RenderTarget target)
346 if (m_preferredRenderTarget == target)
349 m_preferredRenderTarget = target;
351 m_dirtyRenderTarget =
true;
352 m_dirtyGeometry =
true;
353 m_dirtyTexture =
true;
356void QSGDefaultPainterNode::setSize(
const QSize &size)
362 m_dirtyGeometry =
true;
365void QSGDefaultPainterNode::setTextureSize(
const QSize &size)
367 if (size == m_textureSize)
370 m_textureSize = size;
373 if (m_context->rhi()->backend() == QRhi::OpenGLES2) {
376 m_dirtyRenderTarget = m_fbo->size() != m_fboSize || m_dirtyRenderTarget;
378 m_dirtyRenderTarget =
true;
382 m_dirtyRenderTarget =
true;
385 m_dirtyGeometry =
true;
386 m_dirtyTexture =
true;
389void QSGDefaultPainterNode::setDirty(
const QRect &dirtyRect)
391 m_dirtyContents =
true;
392 m_dirtyRect = dirtyRect;
395 m_dirtyTexture =
true;
397 markDirty(DirtyMaterial);
400void QSGDefaultPainterNode::setOpaquePainting(
bool opaque)
402 if (opaque == m_opaquePainting)
405 m_opaquePainting = opaque;
406 m_dirtyTexture =
true;
409void QSGDefaultPainterNode::setLinearFiltering(
bool linearFiltering)
411 if (linearFiltering == m_linear_filtering)
414 m_linear_filtering = linearFiltering;
416 m_material.setFiltering(linearFiltering ? QSGTexture::Linear : QSGTexture::Nearest);
417 m_materialO.setFiltering(linearFiltering ? QSGTexture::Linear : QSGTexture::Nearest);
418 markDirty(DirtyMaterial);
421void QSGDefaultPainterNode::setMipmapping(
bool mipmapping)
423 if (mipmapping == m_mipmapping)
426 m_mipmapping = mipmapping;
427 m_material.setMipmapFiltering(mipmapping ? QSGTexture::Linear : QSGTexture::None);
428 m_materialO.setMipmapFiltering(mipmapping ? QSGTexture::Linear : QSGTexture::None);
429 m_dirtyTexture =
true;
432void QSGDefaultPainterNode::setSmoothPainting(
bool s)
434 if (s == m_smoothPainting)
437 m_smoothPainting = s;
438 m_dirtyRenderTarget =
true;
441void QSGDefaultPainterNode::setFillColor(
const QColor &c)
443 if (c == m_fillColor)
447 markDirty(DirtyMaterial);
450void QSGDefaultPainterNode::setContentsScale(qreal s)
452 if (s == m_contentsScale)
456 markDirty(DirtyMaterial);
459void QSGDefaultPainterNode::setFastFBOResizing(
bool fastResizing)
461 if (m_fastFBOResizing == fastResizing)
464 m_fastFBOResizing = fastResizing;
467 if (m_context->rhi()->backend() == QRhi::OpenGLES2) {
469 if ((m_preferredRenderTarget == QQuickPaintedItem::FramebufferObject
470 || m_preferredRenderTarget == QQuickPaintedItem::InvertedYFramebufferObject)
471 && (!m_fbo || (m_fbo && m_fbo->size() != m_fboSize))) {
472 m_dirtyRenderTarget =
true;
473 m_dirtyGeometry =
true;
474 m_dirtyTexture =
true;
480QImage QSGDefaultPainterNode::toImage()
const
483 if (m_context->rhi()->backend() == QRhi::OpenGLES2 && m_actualRenderTarget != QQuickPaintedItem::Image) {
485 return m_fbo->toImage();
489 Q_ASSERT(m_actualRenderTarget == QQuickPaintedItem::Image);