QSkinny 0.8.0
C++/Qt UI toolkit
Loading...
Searching...
No Matches
QskTextureRenderer.cpp
1/******************************************************************************
2 * QSkinny - Copyright (C) The authors
3 * SPDX-License-Identifier: BSD-3-Clause
4 *****************************************************************************/
5
6#include "QskTextureRenderer.h"
7#include "QskQuick.h"
8
9#include <qopenglcontext.h>
10#include <qopenglframebufferobject.h>
11#include <qopenglpaintdevice.h>
12
13#include <qimage.h>
14#include <qpainter.h>
15
16#include <qquickwindow.h>
17
18QSK_QT_PRIVATE_BEGIN
19#include <private/qsgplaintexture_p.h>
20#include <private/qopenglframebufferobject_p.h>
21QSK_QT_PRIVATE_END
22
23#if QT_VERSION >= QT_VERSION_CHECK( 6, 0, 0 )
24 #include <qquickopenglutils.h>
25#endif
26
27static GLuint qskTakeTexture( QOpenGLFramebufferObject& fbo )
28{
29 /*
30 See https://bugreports.qt.io/browse/QTBUG-103929
31
32 As we create a FBO for each update of a node we can't live
33 without having this ( ugly ) workaround.
34 */
35 class MyFBO
36 {
37 public:
38 virtual ~MyFBO() = default;
39 QScopedPointer< QOpenGLFramebufferObjectPrivate > d_ptr;
40 };
41
42 static_assert( sizeof( MyFBO ) == sizeof( QOpenGLFramebufferObject ),
43 "Bad cast: QOpenGLFramebufferObject does not match" );
44
45 auto& attachment = reinterpret_cast< MyFBO* >( &fbo )->d_ptr->colorAttachments[0];
46 auto guard = attachment.guard;
47
48 const auto textureId = fbo.takeTexture();
49
50 if ( guard )
51 {
52 class MyGuard : public QOpenGLSharedResourceGuard
53 {
54 public:
55 void invalidateTexture() { invalidateResource(); }
56 };
57
58 reinterpret_cast< MyGuard* >( guard )->invalidateTexture();
59 }
60
61 attachment.guard = guard;
62
63 return textureId;
64}
65
66bool QskTextureRenderer::isOpenGLWindow( const QQuickWindow* window )
67{
68 if ( window == nullptr )
69 return false;
70
71 const auto renderer = window->rendererInterface();
72 switch( renderer->graphicsApi() )
73 {
74 case QSGRendererInterface::OpenGL:
75#if QT_VERSION < QT_VERSION_CHECK( 6, 0, 0 )
76 case QSGRendererInterface::OpenGLRhi:
77#endif
78 return true;
79
80 default:
81 return false;
82 }
83}
84
85void QskTextureRenderer::setTextureId( QQuickWindow* window,
86 quint32 textureId, const QSize& size, QSGTexture* texture )
87{
88 auto plainTexture = qobject_cast< QSGPlainTexture* >( texture );
89 if ( plainTexture == nullptr )
90 return;
91
92 auto rhi = qskRenderingHardwareInterface( window );
93
94#if QT_VERSION >= QT_VERSION_CHECK( 6, 4, 0 )
95
96 const uint nativeFormat = 0;
97 plainTexture->setTextureFromNativeTexture(
98 rhi, quint64( textureId ), 0, nativeFormat, size, {}, {} );
99
100#elif QT_VERSION >= QT_VERSION_CHECK( 6, 0, 0 )
101 plainTexture->setTextureFromNativeTexture(
102 rhi, quint64( textureId ), 0, size, {}, {} );
103#else
104 if ( rhi )
105 {
106 // enabled with: "export QSG_RHI=1"
107 plainTexture->setTextureFromNativeObject( rhi,
108 QQuickWindow::NativeObjectTexture, &textureId, 0, size, false );
109 }
110 else
111 {
112 plainTexture->setTextureId( textureId );
113 plainTexture->setTextureSize( size );
114 }
115#endif
116}
117
118quint32 QskTextureRenderer::createPaintedTextureGL(
119 QQuickWindow* window, const QSize& size, QskTextureRenderer::PaintHelper* helper )
120{
121 /*
122 Binding GL_ARRAY_BUFFER/GL_ELEMENT_ARRAY_BUFFER to 0 seems to be enough.
123
124 However - as we do not know what is finally painted and what the
125 OpenGL paint engine is doing we better reinitialize everything.
126
127 Hope this has no side effects as the context will leave the function
128 in a modified state. Otherwise we could try to change the buffers
129 only and reset them, before leaving.
130
131 QQuickFramebufferObject does the FBO rendering early
132 ( QQuickWindow::beforeRendering ). But so far doing it below updatePaintNode
133 seems to work as well. Let's see if we run into issues ...
134 */
135
136 window->beginExternalCommands();
137
138#if QT_VERSION >= QT_VERSION_CHECK( 6, 0, 0 )
139 QQuickOpenGLUtils::resetOpenGLState();
140#else
141 window->resetOpenGLState();
142#endif
143
144 auto context = QOpenGLContext::currentContext();
145
146 QOpenGLFramebufferObjectFormat format1;
147 format1.setAttachment( QOpenGLFramebufferObject::CombinedDepthStencil );
148
149 format1.setSamples( context->format().samples() );
150
151 QOpenGLFramebufferObject multisampledFbo( size, format1 );
152
153 QOpenGLPaintDevice pd( size );
154 pd.setPaintFlipped( true );
155
156 {
157 QPainter painter( &pd );
158
159 painter.setCompositionMode( QPainter::CompositionMode_Source );
160 painter.fillRect( 0, 0, size.width(), size.height(), Qt::transparent );
161 painter.setCompositionMode( QPainter::CompositionMode_SourceOver );
162
163 const auto ratio = window->effectiveDevicePixelRatio();
164
165 painter.scale( ratio, ratio );
166 helper->paint( &painter, size / ratio );
167
168#if 1
169 if ( format1.samples() > 0 )
170 {
171 /*
172 Multisampling in the window surface might get lost
173 as a side effect of rendering to the FBO.
174 weired, needs to be investigated more
175 */
176 painter.setRenderHint( QPainter::Antialiasing, true );
177 }
178#endif
179 }
180
181 QOpenGLFramebufferObjectFormat format2;
182 format2.setAttachment( QOpenGLFramebufferObject::NoAttachment );
183
184 QOpenGLFramebufferObject fbo( size, format2 );
185
186 const QRect fboRect( 0, 0, size.width(), size.height() );
187
188 QOpenGLFramebufferObject::blitFramebuffer(
189 &fbo, fboRect, &multisampledFbo, fboRect );
190
191 window->endExternalCommands();
192
193 return qskTakeTexture( fbo );
194}
195
196static QSGTexture* qskCreateTextureRaster( QQuickWindow* window,
197 const QSize& size, QskTextureRenderer::PaintHelper* helper )
198{
199 const auto ratio = window ? window->effectiveDevicePixelRatio() : 1.0;
200
201 QImage image( size * ratio, QImage::Format_RGBA8888_Premultiplied );
202 image.fill( Qt::transparent );
203
204 {
205 QPainter painter( &image );
206
207 /*
208 setting a devicePixelRatio for the image only works for
209 value >= 1.0. So we have to scale manually.
210 */
211 painter.scale( ratio, ratio );
212
213 helper->paint( &painter, size );
214 }
215
216 return window->createTextureFromImage( image, QQuickWindow::TextureHasAlphaChannel );
217}
218
219QSGTexture* QskTextureRenderer::createPaintedTexture(
220 QQuickWindow* window, const QSize& size, PaintHelper* helper )
221{
222 if ( isOpenGLWindow( window ) )
223 {
224 const auto textureId = createPaintedTextureGL( window, size, helper );
225
226 auto texture = new QSGPlainTexture;
227 texture->setHasAlphaChannel( true );
228 texture->setOwnsTexture( true );
229
230 setTextureId( window, textureId, size, texture );
231
232 return texture;
233 }
234 else
235 {
236 return qskCreateTextureRaster( window, size, helper );
237 }
238}