1 | /**************************************************************************** |
2 | ** |
3 | ** Copyright (C) 2016 The Qt Company Ltd. |
4 | ** Contact: https://www.qt.io/licensing/ |
5 | ** |
6 | ** This file is part of the QtOpenGL module of the Qt Toolkit. |
7 | ** |
8 | ** $QT_BEGIN_LICENSE:LGPL$ |
9 | ** Commercial License Usage |
10 | ** Licensees holding valid commercial Qt licenses may use this file in |
11 | ** accordance with the commercial license agreement provided with the |
12 | ** Software or, alternatively, in accordance with the terms contained in |
13 | ** a written agreement between you and The Qt Company. For licensing terms |
14 | ** and conditions see https://www.qt.io/terms-conditions. For further |
15 | ** information use the contact form at https://www.qt.io/contact-us. |
16 | ** |
17 | ** GNU Lesser General Public License Usage |
18 | ** Alternatively, this file may be used under the terms of the GNU Lesser |
19 | ** General Public License version 3 as published by the Free Software |
20 | ** Foundation and appearing in the file LICENSE.LGPL3 included in the |
21 | ** packaging of this file. Please review the following information to |
22 | ** ensure the GNU Lesser General Public License version 3 requirements |
23 | ** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. |
24 | ** |
25 | ** GNU General Public License Usage |
26 | ** Alternatively, this file may be used under the terms of the GNU |
27 | ** General Public License version 2.0 or (at your option) the GNU General |
28 | ** Public license version 3 or any later version approved by the KDE Free |
29 | ** Qt Foundation. The licenses are as published by the Free Software |
30 | ** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 |
31 | ** included in the packaging of this file. Please review the following |
32 | ** information to ensure the GNU General Public License requirements will |
33 | ** be met: https://www.gnu.org/licenses/gpl-2.0.html and |
34 | ** https://www.gnu.org/licenses/gpl-3.0.html. |
35 | ** |
36 | ** $QT_END_LICENSE$ |
37 | ** |
38 | ****************************************************************************/ |
39 | |
40 | #include "qglframebufferobject.h" |
41 | #include "qglframebufferobject_p.h" |
42 | |
43 | #include <qdebug.h> |
44 | #include <private/qgl_p.h> |
45 | #include <private/qfont_p.h> |
46 | #include "gl2paintengineex/qpaintengineex_opengl2_p.h" |
47 | |
48 | #include <qimage.h> |
49 | #include <qwindow.h> |
50 | |
51 | QT_BEGIN_NAMESPACE |
52 | |
53 | extern QImage qt_gl_read_frame_buffer(const QSize&, bool, bool); |
54 | |
55 | #define QGL_FUNC_CONTEXT const QGLContext *ctx = QGLContext::currentContext(); |
56 | #define QGL_FUNCP_CONTEXT const QGLContext *ctx = QGLContext::currentContext(); |
57 | |
58 | #ifndef QT_NO_DEBUG |
59 | #define QT_RESET_GLERROR() \ |
60 | { \ |
61 | while (QOpenGLContext::currentContext()->functions()->glGetError() != GL_NO_ERROR) {} \ |
62 | } |
63 | #define QT_CHECK_GLERROR() \ |
64 | { \ |
65 | GLenum err = QOpenGLContext::currentContext()->functions()->glGetError(); \ |
66 | if (err != GL_NO_ERROR) { \ |
67 | qDebug("[%s line %d] GL Error: %d", \ |
68 | __FILE__, __LINE__, (int)err); \ |
69 | } \ |
70 | } |
71 | #else |
72 | #define QT_RESET_GLERROR() {} |
73 | #define QT_CHECK_GLERROR() {} |
74 | #endif |
75 | |
76 | // ####TODO Properly #ifdef this class to use #define symbols actually defined |
77 | // by OpenGL/ES includes |
78 | #ifndef GL_MAX_SAMPLES |
79 | #define GL_MAX_SAMPLES 0x8D57 |
80 | #endif |
81 | |
82 | #ifndef GL_RENDERBUFFER_SAMPLES |
83 | #define GL_RENDERBUFFER_SAMPLES 0x8CAB |
84 | #endif |
85 | |
86 | #ifndef GL_DEPTH24_STENCIL8 |
87 | #define GL_DEPTH24_STENCIL8 0x88F0 |
88 | #endif |
89 | |
90 | #ifndef GL_DEPTH_COMPONENT24 |
91 | #define GL_DEPTH_COMPONENT24 0x81A6 |
92 | #endif |
93 | |
94 | #ifndef GL_DEPTH_COMPONENT24_OES |
95 | #define GL_DEPTH_COMPONENT24_OES 0x81A6 |
96 | #endif |
97 | |
98 | #ifndef GL_READ_FRAMEBUFFER |
99 | #define GL_READ_FRAMEBUFFER 0x8CA8 |
100 | #endif |
101 | |
102 | #ifndef GL_DRAW_FRAMEBUFFER |
103 | #define GL_DRAW_FRAMEBUFFER 0x8CA9 |
104 | #endif |
105 | |
106 | #ifndef GL_DEPTH_STENCIL_ATTACHMENT |
107 | #define GL_DEPTH_STENCIL_ATTACHMENT 0x821A |
108 | #endif |
109 | |
110 | #ifndef GL_DEPTH_STENCIL |
111 | #define GL_DEPTH_STENCIL 0x84F9 |
112 | #endif |
113 | |
114 | /*! |
115 | \class QGLFramebufferObjectFormat |
116 | \inmodule QtOpenGL |
117 | \brief The QGLFramebufferObjectFormat class specifies the format of an OpenGL |
118 | framebuffer object. |
119 | |
120 | \since 4.6 |
121 | \obsolete |
122 | |
123 | \ingroup painting-3D |
124 | |
125 | A framebuffer object has several characteristics: |
126 | \list |
127 | \li \l{setSamples()}{Number of samples per pixels.} |
128 | \li \l{setAttachment()}{Depth and/or stencil attachments.} |
129 | \li \l{setTextureTarget()}{Texture target.} |
130 | \li \l{setInternalTextureFormat()}{Internal texture format.} |
131 | \endlist |
132 | |
133 | Note that the desired attachments or number of samples per pixels might not |
134 | be supported by the hardware driver. Call QGLFramebufferObject::format() |
135 | after creating a QGLFramebufferObject to find the exact format that was |
136 | used to create the frame buffer object. |
137 | |
138 | \note This class has been deprecated in favor of QOpenGLFramebufferObjectFormat. |
139 | |
140 | \sa QGLFramebufferObject |
141 | */ |
142 | |
143 | /*! |
144 | \internal |
145 | */ |
146 | void QGLFramebufferObjectFormat::detach() |
147 | { |
148 | if (d->ref.loadRelaxed() != 1) { |
149 | QGLFramebufferObjectFormatPrivate *newd |
150 | = new QGLFramebufferObjectFormatPrivate(d); |
151 | if (!d->ref.deref()) |
152 | delete d; |
153 | d = newd; |
154 | } |
155 | } |
156 | |
157 | /*! |
158 | Creates a QGLFramebufferObjectFormat object for specifying |
159 | the format of an OpenGL framebuffer object. |
160 | |
161 | By default the format specifies a non-multisample framebuffer object with no |
162 | attachments, texture target \c GL_TEXTURE_2D, and internal format \c GL_RGBA8. |
163 | On OpenGL/ES systems, the default internal format is \c GL_RGBA. |
164 | |
165 | \sa samples(), attachment(), internalTextureFormat() |
166 | */ |
167 | |
168 | QGLFramebufferObjectFormat::QGLFramebufferObjectFormat() |
169 | { |
170 | d = new QGLFramebufferObjectFormatPrivate; |
171 | } |
172 | |
173 | /*! |
174 | Constructs a copy of \a other. |
175 | */ |
176 | |
177 | QGLFramebufferObjectFormat::QGLFramebufferObjectFormat(const QGLFramebufferObjectFormat &other) |
178 | { |
179 | d = other.d; |
180 | d->ref.ref(); |
181 | } |
182 | |
183 | /*! |
184 | Assigns \a other to this object. |
185 | */ |
186 | |
187 | QGLFramebufferObjectFormat &QGLFramebufferObjectFormat::operator=(const QGLFramebufferObjectFormat &other) |
188 | { |
189 | if (d != other.d) { |
190 | other.d->ref.ref(); |
191 | if (!d->ref.deref()) |
192 | delete d; |
193 | d = other.d; |
194 | } |
195 | return *this; |
196 | } |
197 | |
198 | /*! |
199 | Destroys the QGLFramebufferObjectFormat. |
200 | */ |
201 | QGLFramebufferObjectFormat::~QGLFramebufferObjectFormat() |
202 | { |
203 | if (!d->ref.deref()) |
204 | delete d; |
205 | } |
206 | |
207 | /*! |
208 | Sets the number of samples per pixel for a multisample framebuffer object |
209 | to \a samples. The default sample count of 0 represents a regular |
210 | non-multisample framebuffer object. |
211 | |
212 | If the desired amount of samples per pixel is not supported by the hardware |
213 | then the maximum number of samples per pixel will be used. Note that |
214 | multisample framebuffer objects cannot be bound as textures. Also, the |
215 | \c{GL_EXT_framebuffer_multisample} extension is required to create a |
216 | framebuffer with more than one sample per pixel. |
217 | |
218 | \sa samples() |
219 | */ |
220 | void QGLFramebufferObjectFormat::setSamples(int samples) |
221 | { |
222 | detach(); |
223 | d->samples = samples; |
224 | } |
225 | |
226 | /*! |
227 | Returns the number of samples per pixel if a framebuffer object |
228 | is a multisample framebuffer object. Otherwise, returns 0. |
229 | The default value is 0. |
230 | |
231 | \sa setSamples() |
232 | */ |
233 | int QGLFramebufferObjectFormat::samples() const |
234 | { |
235 | return d->samples; |
236 | } |
237 | |
238 | /*! |
239 | \since 4.8 |
240 | |
241 | Enables mipmapping if \a enabled is true; otherwise disables it. |
242 | |
243 | Mipmapping is disabled by default. |
244 | |
245 | If mipmapping is enabled, additional memory will be allocated for |
246 | the mipmap levels. The mipmap levels can be updated by binding the |
247 | texture and calling glGenerateMipmap(). Mipmapping cannot be enabled |
248 | for multisampled framebuffer objects. |
249 | |
250 | \sa mipmap(), QGLFramebufferObject::texture() |
251 | */ |
252 | void QGLFramebufferObjectFormat::setMipmap(bool enabled) |
253 | { |
254 | detach(); |
255 | d->mipmap = enabled; |
256 | } |
257 | |
258 | /*! |
259 | \since 4.8 |
260 | |
261 | Returns \c true if mipmapping is enabled. |
262 | |
263 | \sa setMipmap() |
264 | */ |
265 | bool QGLFramebufferObjectFormat::mipmap() const |
266 | { |
267 | return d->mipmap; |
268 | } |
269 | |
270 | /*! |
271 | Sets the attachment configuration of a framebuffer object to \a attachment. |
272 | |
273 | \sa attachment() |
274 | */ |
275 | void QGLFramebufferObjectFormat::setAttachment(QGLFramebufferObject::Attachment attachment) |
276 | { |
277 | detach(); |
278 | d->attachment = attachment; |
279 | } |
280 | |
281 | /*! |
282 | Returns the configuration of the depth and stencil buffers attached to |
283 | a framebuffer object. The default is QGLFramebufferObject::NoAttachment. |
284 | |
285 | \sa setAttachment() |
286 | */ |
287 | QGLFramebufferObject::Attachment QGLFramebufferObjectFormat::attachment() const |
288 | { |
289 | return d->attachment; |
290 | } |
291 | |
292 | /*! |
293 | Sets the texture target of the texture attached to a framebuffer object to |
294 | \a target. Ignored for multisample framebuffer objects. |
295 | |
296 | \sa textureTarget(), samples() |
297 | */ |
298 | void QGLFramebufferObjectFormat::setTextureTarget(GLenum target) |
299 | { |
300 | detach(); |
301 | d->target = target; |
302 | } |
303 | |
304 | /*! |
305 | Returns the texture target of the texture attached to a framebuffer object. |
306 | Ignored for multisample framebuffer objects. The default is |
307 | \c GL_TEXTURE_2D. |
308 | |
309 | \sa setTextureTarget(), samples() |
310 | */ |
311 | GLenum QGLFramebufferObjectFormat::textureTarget() const |
312 | { |
313 | return d->target; |
314 | } |
315 | |
316 | /*! |
317 | Sets the internal format of a framebuffer object's texture or |
318 | multisample framebuffer object's color buffer to |
319 | \a internalTextureFormat. |
320 | |
321 | \sa internalTextureFormat() |
322 | */ |
323 | void QGLFramebufferObjectFormat::setInternalTextureFormat(GLenum internalTextureFormat) |
324 | { |
325 | detach(); |
326 | d->internal_format = internalTextureFormat; |
327 | } |
328 | |
329 | /*! |
330 | Returns the internal format of a framebuffer object's texture or |
331 | multisample framebuffer object's color buffer. The default is |
332 | \c GL_RGBA8 on desktop OpenGL systems, and \c GL_RGBA on |
333 | OpenGL/ES systems. |
334 | |
335 | \sa setInternalTextureFormat() |
336 | */ |
337 | GLenum QGLFramebufferObjectFormat::internalTextureFormat() const |
338 | { |
339 | return d->internal_format; |
340 | } |
341 | |
342 | /*! |
343 | Returns \c true if all the options of this framebuffer object format |
344 | are the same as \a other; otherwise returns \c false. |
345 | */ |
346 | bool QGLFramebufferObjectFormat::operator==(const QGLFramebufferObjectFormat& other) const |
347 | { |
348 | if (d == other.d) |
349 | return true; |
350 | else |
351 | return d->equals(other: other.d); |
352 | } |
353 | |
354 | /*! |
355 | Returns \c false if all the options of this framebuffer object format |
356 | are the same as \a other; otherwise returns \c true. |
357 | */ |
358 | bool QGLFramebufferObjectFormat::operator!=(const QGLFramebufferObjectFormat& other) const |
359 | { |
360 | return !(*this == other); |
361 | } |
362 | |
363 | void QGLFBOGLPaintDevice::setFBO(QGLFramebufferObject* f, |
364 | QGLFramebufferObject::Attachment attachment) |
365 | { |
366 | fbo = f; |
367 | m_thisFBO = fbo->d_func()->fbo(); // This shouldn't be needed |
368 | |
369 | // The context that the fbo was created in may not have depth |
370 | // and stencil buffers, but the fbo itself might. |
371 | fboFormat = QGLContext::currentContext()->format(); |
372 | if (attachment == QGLFramebufferObject::CombinedDepthStencil) { |
373 | fboFormat.setDepth(true); |
374 | fboFormat.setStencil(true); |
375 | } else if (attachment == QGLFramebufferObject::Depth) { |
376 | fboFormat.setDepth(true); |
377 | fboFormat.setStencil(false); |
378 | } else { |
379 | fboFormat.setDepth(false); |
380 | fboFormat.setStencil(false); |
381 | } |
382 | |
383 | GLenum format = f->format().internalTextureFormat(); |
384 | reqAlpha = (format != GL_RGB |
385 | #ifdef GL_RGB5 |
386 | && format != GL_RGB5 |
387 | #endif |
388 | #ifdef GL_RGB8 |
389 | && format != GL_RGB8 |
390 | #endif |
391 | ); |
392 | } |
393 | |
394 | QGLContext *QGLFBOGLPaintDevice::context() const |
395 | { |
396 | return const_cast<QGLContext *>(QGLContext::currentContext()); |
397 | } |
398 | |
399 | bool QGLFramebufferObjectPrivate::checkFramebufferStatus() const |
400 | { |
401 | QGL_FUNCP_CONTEXT; |
402 | if (!ctx) |
403 | return false; // Context no longer exists. |
404 | GLenum status = ctx->contextHandle()->functions()->glCheckFramebufferStatus(GL_FRAMEBUFFER); |
405 | switch(status) { |
406 | case GL_NO_ERROR: |
407 | case GL_FRAMEBUFFER_COMPLETE: |
408 | return true; |
409 | case GL_FRAMEBUFFER_UNSUPPORTED: |
410 | qDebug(msg: "QGLFramebufferObject: Unsupported framebuffer format." ); |
411 | break; |
412 | case GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT: |
413 | qDebug(msg: "QGLFramebufferObject: Framebuffer incomplete attachment." ); |
414 | break; |
415 | case GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT: |
416 | qDebug(msg: "QGLFramebufferObject: Framebuffer incomplete, missing attachment." ); |
417 | break; |
418 | #ifdef GL_FRAMEBUFFER_INCOMPLETE_DUPLICATE_ATTACHMENT |
419 | case GL_FRAMEBUFFER_INCOMPLETE_DUPLICATE_ATTACHMENT: |
420 | qDebug("QGLFramebufferObject: Framebuffer incomplete, duplicate attachment." ); |
421 | break; |
422 | #endif |
423 | #ifdef GL_FRAMEBUFFER_INCOMPLETE_DIMENSIONS |
424 | case GL_FRAMEBUFFER_INCOMPLETE_DIMENSIONS: |
425 | qDebug("QGLFramebufferObject: Framebuffer incomplete, attached images must have same dimensions." ); |
426 | break; |
427 | #endif |
428 | #ifdef GL_FRAMEBUFFER_INCOMPLETE_FORMATS |
429 | case GL_FRAMEBUFFER_INCOMPLETE_FORMATS: |
430 | qDebug("QGLFramebufferObject: Framebuffer incomplete, attached images must have same format." ); |
431 | break; |
432 | #endif |
433 | #ifdef GL_FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER |
434 | case GL_FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER: |
435 | qDebug(msg: "QGLFramebufferObject: Framebuffer incomplete, missing draw buffer." ); |
436 | break; |
437 | #endif |
438 | #ifdef GL_FRAMEBUFFER_INCOMPLETE_READ_BUFFER |
439 | case GL_FRAMEBUFFER_INCOMPLETE_READ_BUFFER: |
440 | qDebug(msg: "QGLFramebufferObject: Framebuffer incomplete, missing read buffer." ); |
441 | break; |
442 | #endif |
443 | #ifdef GL_FRAMEBUFFER_INCOMPLETE_MULTISAMPLE |
444 | case GL_FRAMEBUFFER_INCOMPLETE_MULTISAMPLE: |
445 | qDebug(msg: "QGLFramebufferObject: Framebuffer incomplete, attachments must have same number of samples per pixel." ); |
446 | break; |
447 | #endif |
448 | default: |
449 | qDebug() <<"QGLFramebufferObject: An undefined error has occurred: " << status; |
450 | break; |
451 | } |
452 | return false; |
453 | } |
454 | |
455 | namespace |
456 | { |
457 | void freeFramebufferFunc(QGLContext *ctx, GLuint id) |
458 | { |
459 | Q_ASSERT(ctx); |
460 | ctx->contextHandle()->functions()->glDeleteFramebuffers(n: 1, framebuffers: &id); |
461 | } |
462 | |
463 | void freeRenderbufferFunc(QGLContext *ctx, GLuint id) |
464 | { |
465 | Q_ASSERT(ctx); |
466 | ctx->contextHandle()->functions()->glDeleteRenderbuffers(n: 1, renderbuffers: &id); |
467 | } |
468 | |
469 | void freeTextureFunc(QGLContext *ctx, GLuint id) |
470 | { |
471 | Q_UNUSED(ctx); |
472 | ctx->contextHandle()->functions()->glDeleteTextures(n: 1, textures: &id); |
473 | } |
474 | } |
475 | |
476 | void QGLFramebufferObjectPrivate::init(QGLFramebufferObject *q, const QSize &sz, |
477 | QGLFramebufferObject::Attachment attachment, |
478 | GLenum texture_target, GLenum internal_format, |
479 | GLint samples, bool mipmap) |
480 | { |
481 | QGLContext *ctx = const_cast<QGLContext *>(QGLContext::currentContext()); |
482 | |
483 | funcs.initializeOpenGLFunctions(); |
484 | |
485 | if (!funcs.hasOpenGLFeature(feature: QOpenGLFunctions::Framebuffers)) |
486 | return; |
487 | |
488 | ctx->d_ptr->refreshCurrentFbo(); |
489 | |
490 | size = sz; |
491 | target = texture_target; |
492 | // texture dimensions |
493 | |
494 | QT_RESET_GLERROR(); // reset error state |
495 | GLuint fbo = 0; |
496 | funcs.glGenFramebuffers(n: 1, framebuffers: &fbo); |
497 | funcs.glBindFramebuffer(GL_FRAMEBUFFER, framebuffer: fbo); |
498 | |
499 | GLuint texture = 0; |
500 | GLuint color_buffer = 0; |
501 | GLuint depth_buffer = 0; |
502 | GLuint stencil_buffer = 0; |
503 | |
504 | QT_CHECK_GLERROR(); |
505 | // init texture |
506 | if (samples == 0) { |
507 | funcs.glGenTextures(n: 1, textures: &texture); |
508 | funcs.glBindTexture(target, texture); |
509 | funcs.glTexImage2D(target, level: 0, internalformat: internal_format, width: size.width(), height: size.height(), border: 0, |
510 | GL_RGBA, GL_UNSIGNED_BYTE, NULL); |
511 | if (mipmap) { |
512 | int width = size.width(); |
513 | int height = size.height(); |
514 | int level = 0; |
515 | while (width > 1 || height > 1) { |
516 | width = qMax(a: 1, b: width >> 1); |
517 | height = qMax(a: 1, b: height >> 1); |
518 | ++level; |
519 | funcs.glTexImage2D(target, level, internalformat: internal_format, width, height, border: 0, |
520 | GL_RGBA, GL_UNSIGNED_BYTE, NULL); |
521 | } |
522 | } |
523 | funcs.glTexParameteri(target, GL_TEXTURE_MIN_FILTER, GL_NEAREST); |
524 | funcs.glTexParameteri(target, GL_TEXTURE_MAG_FILTER, GL_NEAREST); |
525 | funcs.glTexParameteri(target, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); |
526 | funcs.glTexParameteri(target, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); |
527 | funcs.glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, |
528 | textarget: target, texture, level: 0); |
529 | |
530 | QT_CHECK_GLERROR(); |
531 | valid = checkFramebufferStatus(); |
532 | funcs.glBindTexture(target, texture: 0); |
533 | |
534 | color_buffer = 0; |
535 | } else { |
536 | mipmap = false; |
537 | GLint maxSamples; |
538 | funcs.glGetIntegerv(GL_MAX_SAMPLES, params: &maxSamples); |
539 | |
540 | samples = qBound(min: 0, val: int(samples), max: int(maxSamples)); |
541 | |
542 | funcs.glGenRenderbuffers(n: 1, renderbuffers: &color_buffer); |
543 | funcs.glBindRenderbuffer(GL_RENDERBUFFER, renderbuffer: color_buffer); |
544 | if (funcs.hasOpenGLExtension(extension: QOpenGLExtensions::FramebufferMultisample) && samples > 0) { |
545 | funcs.glRenderbufferStorageMultisample(GL_RENDERBUFFER, samples, |
546 | internalformat: internal_format, width: size.width(), height: size.height()); |
547 | } else { |
548 | samples = 0; |
549 | funcs.glRenderbufferStorage(GL_RENDERBUFFER, internalformat: internal_format, |
550 | width: size.width(), height: size.height()); |
551 | } |
552 | |
553 | funcs.glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, |
554 | GL_RENDERBUFFER, renderbuffer: color_buffer); |
555 | |
556 | QT_CHECK_GLERROR(); |
557 | valid = checkFramebufferStatus(); |
558 | |
559 | if (valid) |
560 | funcs.glGetRenderbufferParameteriv(GL_RENDERBUFFER, GL_RENDERBUFFER_SAMPLES, params: &samples); |
561 | } |
562 | |
563 | // In practice, a combined depth-stencil buffer is supported by all desktop platforms, while a |
564 | // separate stencil buffer is not. On embedded devices however, a combined depth-stencil buffer |
565 | // might not be supported while separate buffers are, according to QTBUG-12861. |
566 | |
567 | if (attachment == QGLFramebufferObject::CombinedDepthStencil |
568 | && funcs.hasOpenGLExtension(extension: QOpenGLExtensions::PackedDepthStencil)) { |
569 | // depth and stencil buffer needs another extension |
570 | funcs.glGenRenderbuffers(n: 1, renderbuffers: &depth_buffer); |
571 | funcs.glBindRenderbuffer(GL_RENDERBUFFER, renderbuffer: depth_buffer); |
572 | Q_ASSERT(funcs.glIsRenderbuffer(depth_buffer)); |
573 | #ifndef Q_OS_WASM |
574 | if (samples != 0 && funcs.hasOpenGLExtension(extension: QOpenGLExtensions::FramebufferMultisample)) |
575 | funcs.glRenderbufferStorageMultisample(GL_RENDERBUFFER, samples, |
576 | GL_DEPTH24_STENCIL8, width: size.width(), height: size.height()); |
577 | else |
578 | funcs.glRenderbufferStorage(GL_RENDERBUFFER, |
579 | GL_DEPTH24_STENCIL8, width: size.width(), height: size.height()); |
580 | |
581 | stencil_buffer = depth_buffer; |
582 | funcs.glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, |
583 | GL_RENDERBUFFER, renderbuffer: depth_buffer); |
584 | funcs.glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, |
585 | GL_RENDERBUFFER, renderbuffer: stencil_buffer); |
586 | #else |
587 | // webgl does not allow separate depth and stencil attachments |
588 | if (samples != 0) { |
589 | funcs.glRenderbufferStorageMultisample(GL_RENDERBUFFER, samples, |
590 | GL_DEPTH_STENCIL, size.width(), size.height()); |
591 | } else { |
592 | funcs.glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_STENCIL, |
593 | size.width(), size.height()); |
594 | } |
595 | stencil_buffer = depth_buffer; |
596 | funcs.glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, |
597 | GL_RENDERBUFFER, depth_buffer); |
598 | #endif |
599 | |
600 | valid = checkFramebufferStatus(); |
601 | if (!valid) { |
602 | funcs.glDeleteRenderbuffers(n: 1, renderbuffers: &depth_buffer); |
603 | stencil_buffer = depth_buffer = 0; |
604 | } |
605 | } |
606 | |
607 | if (depth_buffer == 0 && (attachment == QGLFramebufferObject::CombinedDepthStencil |
608 | || (attachment == QGLFramebufferObject::Depth))) |
609 | { |
610 | funcs.glGenRenderbuffers(n: 1, renderbuffers: &depth_buffer); |
611 | funcs.glBindRenderbuffer(GL_RENDERBUFFER, renderbuffer: depth_buffer); |
612 | Q_ASSERT(funcs.glIsRenderbuffer(depth_buffer)); |
613 | if (samples != 0 && funcs.hasOpenGLExtension(extension: QOpenGLExtensions::FramebufferMultisample)) { |
614 | #ifdef QT_OPENGL_ES |
615 | if (funcs.hasOpenGLExtension(QOpenGLExtensions::Depth24)) { |
616 | funcs.glRenderbufferStorageMultisample(GL_RENDERBUFFER, samples, |
617 | GL_DEPTH_COMPONENT24_OES, size.width(), size.height()); |
618 | } else { |
619 | funcs.glRenderbufferStorageMultisample(GL_RENDERBUFFER, samples, |
620 | GL_DEPTH_COMPONENT16, size.width(), size.height()); |
621 | } |
622 | #else |
623 | if (ctx->contextHandle()->isOpenGLES()) { |
624 | if (funcs.hasOpenGLExtension(extension: QOpenGLExtensions::Depth24)) |
625 | funcs.glRenderbufferStorageMultisample(GL_RENDERBUFFER, samples, |
626 | GL_DEPTH_COMPONENT24, width: size.width(), height: size.height()); |
627 | else |
628 | funcs.glRenderbufferStorageMultisample(GL_RENDERBUFFER, samples, |
629 | GL_DEPTH_COMPONENT16, width: size.width(), height: size.height()); |
630 | } else { |
631 | funcs.glRenderbufferStorageMultisample(GL_RENDERBUFFER, samples, |
632 | GL_DEPTH_COMPONENT, width: size.width(), height: size.height()); |
633 | } |
634 | #endif |
635 | } else { |
636 | #ifdef QT_OPENGL_ES |
637 | if (funcs.hasOpenGLExtension(QOpenGLExtensions::Depth24)) { |
638 | funcs.glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT24_OES, |
639 | size.width(), size.height()); |
640 | } else { |
641 | funcs.glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT16, |
642 | size.width(), size.height()); |
643 | } |
644 | #else |
645 | if (ctx->contextHandle()->isOpenGLES()) { |
646 | if (funcs.hasOpenGLExtension(extension: QOpenGLExtensions::Depth24)) { |
647 | funcs.glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT24, |
648 | width: size.width(), height: size.height()); |
649 | } else { |
650 | funcs.glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT16, |
651 | width: size.width(), height: size.height()); |
652 | } |
653 | } else { |
654 | funcs.glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT, width: size.width(), height: size.height()); |
655 | } |
656 | #endif |
657 | } |
658 | funcs.glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, |
659 | GL_RENDERBUFFER, renderbuffer: depth_buffer); |
660 | valid = checkFramebufferStatus(); |
661 | if (!valid) { |
662 | funcs.glDeleteRenderbuffers(n: 1, renderbuffers: &depth_buffer); |
663 | depth_buffer = 0; |
664 | } |
665 | } |
666 | |
667 | if (stencil_buffer == 0 && (attachment == QGLFramebufferObject::CombinedDepthStencil)) { |
668 | funcs.glGenRenderbuffers(n: 1, renderbuffers: &stencil_buffer); |
669 | funcs.glBindRenderbuffer(GL_RENDERBUFFER, renderbuffer: stencil_buffer); |
670 | Q_ASSERT(funcs.glIsRenderbuffer(stencil_buffer)); |
671 | |
672 | #ifdef QT_OPENGL_ES |
673 | GLenum storage = GL_STENCIL_INDEX8; |
674 | #else |
675 | GLenum storage = ctx->contextHandle()->isOpenGLES() ? GL_STENCIL_INDEX8 : GL_STENCIL_INDEX; |
676 | #endif |
677 | |
678 | if (samples != 0 && funcs.hasOpenGLExtension(extension: QOpenGLExtensions::FramebufferMultisample)) |
679 | funcs.glRenderbufferStorageMultisample(GL_RENDERBUFFER, samples, internalformat: storage, width: size.width(), height: size.height()); |
680 | else |
681 | funcs.glRenderbufferStorage(GL_RENDERBUFFER, internalformat: storage, width: size.width(), height: size.height()); |
682 | |
683 | funcs.glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, |
684 | GL_RENDERBUFFER, renderbuffer: stencil_buffer); |
685 | valid = checkFramebufferStatus(); |
686 | if (!valid) { |
687 | funcs.glDeleteRenderbuffers(n: 1, renderbuffers: &stencil_buffer); |
688 | stencil_buffer = 0; |
689 | } |
690 | } |
691 | |
692 | // The FBO might have become valid after removing the depth or stencil buffer. |
693 | valid = checkFramebufferStatus(); |
694 | |
695 | if (depth_buffer && stencil_buffer) { |
696 | fbo_attachment = QGLFramebufferObject::CombinedDepthStencil; |
697 | } else if (depth_buffer) { |
698 | fbo_attachment = QGLFramebufferObject::Depth; |
699 | } else { |
700 | fbo_attachment = QGLFramebufferObject::NoAttachment; |
701 | } |
702 | |
703 | funcs.glBindFramebuffer(GL_FRAMEBUFFER, framebuffer: ctx->d_ptr->current_fbo); |
704 | if (valid) { |
705 | fbo_guard = createSharedResourceGuard(context: ctx, id: fbo, cleanupFunc: freeFramebufferFunc); |
706 | if (color_buffer) |
707 | color_buffer_guard = createSharedResourceGuard(context: ctx, id: color_buffer, cleanupFunc: freeRenderbufferFunc); |
708 | else |
709 | texture_guard = createSharedResourceGuard(context: ctx, id: texture, cleanupFunc: freeTextureFunc); |
710 | if (depth_buffer) |
711 | depth_buffer_guard = createSharedResourceGuard(context: ctx, id: depth_buffer, cleanupFunc: freeRenderbufferFunc); |
712 | if (stencil_buffer) { |
713 | if (stencil_buffer == depth_buffer) |
714 | stencil_buffer_guard = depth_buffer_guard; |
715 | else |
716 | stencil_buffer_guard = createSharedResourceGuard(context: ctx, id: stencil_buffer, cleanupFunc: freeRenderbufferFunc); |
717 | } |
718 | } else { |
719 | if (color_buffer) |
720 | funcs.glDeleteRenderbuffers(n: 1, renderbuffers: &color_buffer); |
721 | else |
722 | funcs.glDeleteTextures(n: 1, textures: &texture); |
723 | if (depth_buffer) |
724 | funcs.glDeleteRenderbuffers(n: 1, renderbuffers: &depth_buffer); |
725 | if (stencil_buffer && depth_buffer != stencil_buffer) |
726 | funcs.glDeleteRenderbuffers(n: 1, renderbuffers: &stencil_buffer); |
727 | funcs.glDeleteFramebuffers(n: 1, framebuffers: &fbo); |
728 | } |
729 | QT_CHECK_GLERROR(); |
730 | |
731 | format.setTextureTarget(target); |
732 | format.setSamples(int(samples)); |
733 | format.setAttachment(fbo_attachment); |
734 | format.setInternalTextureFormat(internal_format); |
735 | format.setMipmap(mipmap); |
736 | |
737 | glDevice.setFBO(f: q, attachment); |
738 | } |
739 | |
740 | /*! |
741 | \class QGLFramebufferObject |
742 | \inmodule QtOpenGL |
743 | \brief The QGLFramebufferObject class encapsulates an OpenGL framebuffer object. |
744 | \since 4.2 |
745 | |
746 | \obsolete |
747 | |
748 | \ingroup painting-3D |
749 | |
750 | The QGLFramebufferObject class encapsulates an OpenGL framebuffer |
751 | object, defined by the \c{GL_EXT_framebuffer_object} extension. In |
752 | addition it provides a rendering surface that can be painted on |
753 | with a QPainter, rendered to using native GL calls, or both. This |
754 | surface can be bound and used as a regular texture in your own GL |
755 | drawing code. By default, the QGLFramebufferObject class |
756 | generates a 2D GL texture (using the \c{GL_TEXTURE_2D} target), |
757 | which is used as the internal rendering target. |
758 | |
759 | \b{It is important to have a current GL context when creating a |
760 | QGLFramebufferObject, otherwise initialization will fail.} |
761 | |
762 | OpenGL framebuffer objects and pbuffers (see |
763 | \l{QGLPixelBuffer}{QGLPixelBuffer}) can both be used to render to |
764 | offscreen surfaces, but there are a number of advantages with |
765 | using framebuffer objects instead of pbuffers: |
766 | |
767 | \list 1 |
768 | \li A framebuffer object does not require a separate rendering |
769 | context, so no context switching will occur when switching |
770 | rendering targets. There is an overhead involved in switching |
771 | targets, but in general it is cheaper than a context switch to a |
772 | pbuffer. |
773 | |
774 | \li Rendering to dynamic textures (i.e. render-to-texture |
775 | functionality) works on all platforms. No need to do explicit copy |
776 | calls from a render buffer into a texture, as was necessary on |
777 | systems that did not support the \c{render_texture} extension. |
778 | |
779 | \li It is possible to attach several rendering buffers (or texture |
780 | objects) to the same framebuffer object, and render to all of them |
781 | without doing a context switch. |
782 | |
783 | \li The OpenGL framebuffer extension is a pure GL extension with no |
784 | system dependant WGL, CGL, or GLX parts. This makes using |
785 | framebuffer objects more portable. |
786 | \endlist |
787 | |
788 | When using a QPainter to paint to a QGLFramebufferObject you should take |
789 | care that the QGLFramebufferObject is created with the CombinedDepthStencil |
790 | attachment for QPainter to be able to render correctly. |
791 | Note that you need to create a QGLFramebufferObject with more than one |
792 | sample per pixel for primitives to be antialiased when drawing using a |
793 | QPainter. To create a multisample framebuffer object you should use one of |
794 | the constructors that take a QGLFramebufferObjectFormat parameter, and set |
795 | the QGLFramebufferObjectFormat::samples() property to a non-zero value. |
796 | |
797 | When painting to a QGLFramebufferObject using QPainter, the state of |
798 | the current GL context will be altered by the paint engine to reflect |
799 | its needs. Applications should not rely upon the GL state being reset |
800 | to its original conditions, particularly the current shader program, |
801 | GL viewport, texture units, and drawing modes. |
802 | |
803 | For multisample framebuffer objects a color render buffer is created, |
804 | otherwise a texture with the specified texture target is created. |
805 | The color render buffer or texture will have the specified internal |
806 | format, and will be bound to the \c GL_COLOR_ATTACHMENT0 |
807 | attachment in the framebuffer object. |
808 | |
809 | If you want to use a framebuffer object with multisampling enabled |
810 | as a texture, you first need to copy from it to a regular framebuffer |
811 | object using QGLContext::blitFramebuffer(). |
812 | |
813 | \section1 Threading |
814 | |
815 | As of Qt 4.8, it's possible to draw into a QGLFramebufferObject |
816 | using a QPainter in a separate thread. Note that OpenGL 2.0 or |
817 | OpenGL ES 2.0 is required for this to work. |
818 | |
819 | \note This class has been deprecated in favor of QOpenGLFramebufferObject. |
820 | */ |
821 | |
822 | |
823 | /*! |
824 | \enum QGLFramebufferObject::Attachment |
825 | \since 4.3 |
826 | |
827 | This enum type is used to configure the depth and stencil buffers |
828 | attached to the framebuffer object when it is created. |
829 | |
830 | \value NoAttachment No attachment is added to the framebuffer object. Note that the |
831 | OpenGL depth and stencil tests won't work when rendering to a |
832 | framebuffer object without any depth or stencil buffers. |
833 | This is the default value. |
834 | |
835 | \value CombinedDepthStencil If the \c GL_EXT_packed_depth_stencil extension is present, |
836 | a combined depth and stencil buffer is attached. |
837 | If the extension is not present, only a depth buffer is attached. |
838 | |
839 | \value Depth A depth buffer is attached to the framebuffer object. |
840 | |
841 | \sa attachment() |
842 | */ |
843 | |
844 | |
845 | /*! \fn QGLFramebufferObject::QGLFramebufferObject(const QSize &size, GLenum target) |
846 | |
847 | Constructs an OpenGL framebuffer object and binds a 2D GL texture |
848 | to the buffer of the size \a size. The texture is bound to the |
849 | \c GL_COLOR_ATTACHMENT0 target in the framebuffer object. |
850 | |
851 | The \a target parameter is used to specify the GL texture |
852 | target. The default target is \c GL_TEXTURE_2D. Keep in mind that |
853 | \c GL_TEXTURE_2D textures must have a power of 2 width and height |
854 | (e.g. 256x512), unless you are using OpenGL 2.0 or higher. |
855 | |
856 | By default, no depth and stencil buffers are attached. This behavior |
857 | can be toggled using one of the overloaded constructors. |
858 | |
859 | The default internal texture format is \c GL_RGBA8 for desktop |
860 | OpenGL, and \c GL_RGBA for OpenGL/ES. |
861 | |
862 | It is important that you have a current GL context set when |
863 | creating the QGLFramebufferObject, otherwise the initialization |
864 | will fail. |
865 | |
866 | \sa size(), texture(), attachment() |
867 | */ |
868 | |
869 | QGLFramebufferObject::QGLFramebufferObject(const QSize &size, GLenum target) |
870 | : d_ptr(new QGLFramebufferObjectPrivate) |
871 | { |
872 | Q_D(QGLFramebufferObject); |
873 | d->init(q: this, sz: size, attachment: NoAttachment, texture_target: target, |
874 | #ifndef QT_OPENGL_ES_2 |
875 | internal_format: QOpenGLContext::currentContext()->isOpenGLES() ? GL_RGBA : GL_RGBA8 |
876 | #else |
877 | GL_RGBA |
878 | #endif |
879 | ); |
880 | } |
881 | |
882 | /*! \overload |
883 | |
884 | Constructs an OpenGL framebuffer object and binds a 2D GL texture |
885 | to the buffer of the given \a width and \a height. |
886 | |
887 | \sa size(), texture() |
888 | */ |
889 | QGLFramebufferObject::QGLFramebufferObject(int width, int height, GLenum target) |
890 | : d_ptr(new QGLFramebufferObjectPrivate) |
891 | { |
892 | Q_D(QGLFramebufferObject); |
893 | d->init(q: this, sz: QSize(width, height), attachment: NoAttachment, texture_target: target, |
894 | #ifndef QT_OPENGL_ES_2 |
895 | internal_format: QOpenGLContext::currentContext()->isOpenGLES() ? GL_RGBA : GL_RGBA8 |
896 | #else |
897 | GL_RGBA |
898 | #endif |
899 | ); |
900 | } |
901 | |
902 | /*! \overload |
903 | |
904 | Constructs an OpenGL framebuffer object of the given \a size based on the |
905 | supplied \a format. |
906 | */ |
907 | |
908 | QGLFramebufferObject::QGLFramebufferObject(const QSize &size, const QGLFramebufferObjectFormat &format) |
909 | : d_ptr(new QGLFramebufferObjectPrivate) |
910 | { |
911 | Q_D(QGLFramebufferObject); |
912 | d->init(q: this, sz: size, attachment: format.attachment(), texture_target: format.textureTarget(), internal_format: format.internalTextureFormat(), |
913 | samples: format.samples(), mipmap: format.mipmap()); |
914 | } |
915 | |
916 | /*! \overload |
917 | |
918 | Constructs an OpenGL framebuffer object of the given \a width and \a height |
919 | based on the supplied \a format. |
920 | */ |
921 | |
922 | QGLFramebufferObject::QGLFramebufferObject(int width, int height, const QGLFramebufferObjectFormat &format) |
923 | : d_ptr(new QGLFramebufferObjectPrivate) |
924 | { |
925 | Q_D(QGLFramebufferObject); |
926 | d->init(q: this, sz: QSize(width, height), attachment: format.attachment(), texture_target: format.textureTarget(), |
927 | internal_format: format.internalTextureFormat(), samples: format.samples(), mipmap: format.mipmap()); |
928 | } |
929 | |
930 | /*! \overload |
931 | |
932 | Constructs an OpenGL framebuffer object and binds a texture to the |
933 | buffer of the given \a width and \a height. |
934 | |
935 | The \a attachment parameter describes the depth/stencil buffer |
936 | configuration, \a target the texture target and \a internal_format |
937 | the internal texture format. The default texture target is \c |
938 | GL_TEXTURE_2D, while the default internal format is \c GL_RGBA8 |
939 | for desktop OpenGL and \c GL_RGBA for OpenGL/ES. |
940 | |
941 | \sa size(), texture(), attachment() |
942 | */ |
943 | QGLFramebufferObject::QGLFramebufferObject(int width, int height, Attachment attachment, |
944 | GLenum target, GLenum internal_format) |
945 | : d_ptr(new QGLFramebufferObjectPrivate) |
946 | { |
947 | Q_D(QGLFramebufferObject); |
948 | if (!internal_format) |
949 | #ifdef QT_OPENGL_ES_2 |
950 | internal_format = GL_RGBA; |
951 | #else |
952 | internal_format = QOpenGLContext::currentContext()->isOpenGLES() ? GL_RGBA : GL_RGBA8; |
953 | #endif |
954 | d->init(q: this, sz: QSize(width, height), attachment, texture_target: target, internal_format); |
955 | } |
956 | |
957 | /*! \overload |
958 | |
959 | Constructs an OpenGL framebuffer object and binds a texture to the |
960 | buffer of the given \a size. |
961 | |
962 | The \a attachment parameter describes the depth/stencil buffer |
963 | configuration, \a target the texture target and \a internal_format |
964 | the internal texture format. The default texture target is \c |
965 | GL_TEXTURE_2D, while the default internal format is \c GL_RGBA8 |
966 | for desktop OpenGL and \c GL_RGBA for OpenGL/ES. |
967 | |
968 | \sa size(), texture(), attachment() |
969 | */ |
970 | QGLFramebufferObject::QGLFramebufferObject(const QSize &size, Attachment attachment, |
971 | GLenum target, GLenum internal_format) |
972 | : d_ptr(new QGLFramebufferObjectPrivate) |
973 | { |
974 | Q_D(QGLFramebufferObject); |
975 | if (!internal_format) |
976 | #ifdef QT_OPENGL_ES_2 |
977 | internal_format = GL_RGBA; |
978 | #else |
979 | internal_format = QOpenGLContext::currentContext()->isOpenGLES() ? GL_RGBA : GL_RGBA8; |
980 | #endif |
981 | d->init(q: this, sz: size, attachment, texture_target: target, internal_format); |
982 | } |
983 | |
984 | /*! |
985 | \fn QGLFramebufferObject::~QGLFramebufferObject() |
986 | |
987 | Destroys the framebuffer object and frees any allocated resources. |
988 | */ |
989 | QGLFramebufferObject::~QGLFramebufferObject() |
990 | { |
991 | Q_D(QGLFramebufferObject); |
992 | |
993 | delete d->engine; |
994 | |
995 | if (d->texture_guard) |
996 | d->texture_guard->free(); |
997 | if (d->color_buffer_guard) |
998 | d->color_buffer_guard->free(); |
999 | if (d->depth_buffer_guard) |
1000 | d->depth_buffer_guard->free(); |
1001 | if (d->stencil_buffer_guard && d->stencil_buffer_guard != d->depth_buffer_guard) |
1002 | d->stencil_buffer_guard->free(); |
1003 | if (d->fbo_guard) |
1004 | d->fbo_guard->free(); |
1005 | } |
1006 | |
1007 | /*! |
1008 | \fn bool QGLFramebufferObject::isValid() const |
1009 | |
1010 | Returns \c true if the framebuffer object is valid. |
1011 | |
1012 | The framebuffer can become invalid if the initialization process |
1013 | fails, the user attaches an invalid buffer to the framebuffer |
1014 | object, or a non-power of two width/height is specified as the |
1015 | texture size if the texture target is \c{GL_TEXTURE_2D}. |
1016 | The non-power of two limitation does not apply if the OpenGL version |
1017 | is 2.0 or higher, or if the GL_ARB_texture_non_power_of_two extension |
1018 | is present. |
1019 | |
1020 | The framebuffer can also become invalid if the QGLContext that |
1021 | the framebuffer was created within is destroyed and there are |
1022 | no other shared contexts that can take over ownership of the |
1023 | framebuffer. |
1024 | */ |
1025 | bool QGLFramebufferObject::isValid() const |
1026 | { |
1027 | Q_D(const QGLFramebufferObject); |
1028 | return d->valid && d->fbo_guard && d->fbo_guard->id(); |
1029 | } |
1030 | |
1031 | /*! |
1032 | \fn bool QGLFramebufferObject::bind() |
1033 | |
1034 | Switches rendering from the default, windowing system provided |
1035 | framebuffer to this framebuffer object. |
1036 | Returns \c true upon success, false otherwise. |
1037 | |
1038 | \sa release() |
1039 | */ |
1040 | bool QGLFramebufferObject::bind() |
1041 | { |
1042 | if (!isValid()) |
1043 | return false; |
1044 | Q_D(QGLFramebufferObject); |
1045 | QGL_FUNC_CONTEXT; |
1046 | if (!ctx) |
1047 | return false; // Context no longer exists. |
1048 | const QGLContext *current = QGLContext::currentContext(); |
1049 | #ifdef QT_DEBUG |
1050 | if (!current || |
1051 | QGLContextPrivate::contextGroup(ctx: current) != QGLContextPrivate::contextGroup(ctx)) |
1052 | { |
1053 | qWarning(msg: "QGLFramebufferObject::bind() called from incompatible context" ); |
1054 | } |
1055 | #endif |
1056 | d->funcs.glBindFramebuffer(GL_FRAMEBUFFER, framebuffer: d->fbo()); |
1057 | d->valid = d->checkFramebufferStatus(); |
1058 | if (d->valid && current) |
1059 | current->d_ptr->setCurrentFbo(d->fbo()); |
1060 | return d->valid; |
1061 | } |
1062 | |
1063 | /*! |
1064 | \fn bool QGLFramebufferObject::release() |
1065 | |
1066 | Switches rendering back to the default, windowing system provided |
1067 | framebuffer. |
1068 | Returns \c true upon success, false otherwise. |
1069 | |
1070 | \sa bind() |
1071 | */ |
1072 | bool QGLFramebufferObject::release() |
1073 | { |
1074 | if (!isValid()) |
1075 | return false; |
1076 | Q_D(QGLFramebufferObject); |
1077 | QGL_FUNC_CONTEXT; |
1078 | if (!ctx) |
1079 | return false; // Context no longer exists. |
1080 | |
1081 | const QGLContext *current = QGLContext::currentContext(); |
1082 | |
1083 | #ifdef QT_DEBUG |
1084 | if (!current || |
1085 | QGLContextPrivate::contextGroup(ctx: current) != QGLContextPrivate::contextGroup(ctx)) |
1086 | { |
1087 | qWarning(msg: "QGLFramebufferObject::release() called from incompatible context" ); |
1088 | } |
1089 | #endif |
1090 | |
1091 | if (current) { |
1092 | current->d_ptr->setCurrentFbo(current->d_ptr->default_fbo); |
1093 | d->funcs.glBindFramebuffer(GL_FRAMEBUFFER, framebuffer: current->d_ptr->default_fbo); |
1094 | } |
1095 | |
1096 | return true; |
1097 | } |
1098 | |
1099 | /*! |
1100 | \fn GLuint QGLFramebufferObject::texture() const |
1101 | |
1102 | Returns the texture id for the texture attached as the default |
1103 | rendering target in this framebuffer object. This texture id can |
1104 | be bound as a normal texture in your own GL code. |
1105 | |
1106 | If a multisample framebuffer object is used then the value returned |
1107 | from this function will be invalid. |
1108 | */ |
1109 | GLuint QGLFramebufferObject::texture() const |
1110 | { |
1111 | Q_D(const QGLFramebufferObject); |
1112 | return d->texture_guard ? d->texture_guard->id() : 0; |
1113 | } |
1114 | |
1115 | /*! |
1116 | \fn QSize QGLFramebufferObject::size() const |
1117 | |
1118 | Returns the size of the texture attached to this framebuffer |
1119 | object. |
1120 | */ |
1121 | QSize QGLFramebufferObject::size() const |
1122 | { |
1123 | Q_D(const QGLFramebufferObject); |
1124 | return d->size; |
1125 | } |
1126 | |
1127 | /*! |
1128 | Returns the format of this framebuffer object. |
1129 | */ |
1130 | QGLFramebufferObjectFormat QGLFramebufferObject::format() const |
1131 | { |
1132 | Q_D(const QGLFramebufferObject); |
1133 | return d->format; |
1134 | } |
1135 | |
1136 | /*! |
1137 | \fn QImage QGLFramebufferObject::toImage() const |
1138 | |
1139 | Returns the contents of this framebuffer object as a QImage. |
1140 | |
1141 | The returned image has a format of premultiplied ARGB32 or RGB32. The latter is used |
1142 | only when internalTextureFormat() is set to \c GL_RGB. |
1143 | |
1144 | If the rendering in the framebuffer was not done with premultiplied alpha in mind, |
1145 | create a wrapper QImage with a non-premultiplied format. This is necessary before |
1146 | performing operations like QImage::save() because otherwise the image data would get |
1147 | unpremultiplied, even though it was not premultiplied in the first place. To create |
1148 | such a wrapper without performing a copy of the pixel data, do the following: |
1149 | |
1150 | \code |
1151 | QImage fboImage(fbo.toImage()); |
1152 | QImage image(fboImage.constBits(), fboImage.width(), fboImage.height(), QImage::Format_ARGB32); |
1153 | \endcode |
1154 | |
1155 | On QNX the back buffer is not preserved when a buffer swap occures. So this function |
1156 | might return old content. |
1157 | */ |
1158 | QImage QGLFramebufferObject::toImage() const |
1159 | { |
1160 | Q_D(const QGLFramebufferObject); |
1161 | if (!d->valid) |
1162 | return QImage(); |
1163 | |
1164 | // qt_gl_read_frame_buffer doesn't work on a multisample FBO |
1165 | if (format().samples() != 0) { |
1166 | QGLFramebufferObject temp(size(), QGLFramebufferObjectFormat()); |
1167 | |
1168 | QRect rect(QPoint(0, 0), size()); |
1169 | blitFramebuffer(target: &temp, targetRect: rect, source: const_cast<QGLFramebufferObject *>(this), sourceRect: rect); |
1170 | |
1171 | return temp.toImage(); |
1172 | } |
1173 | |
1174 | bool wasBound = isBound(); |
1175 | if (!wasBound) |
1176 | const_cast<QGLFramebufferObject *>(this)->bind(); |
1177 | QImage image = qt_gl_read_frame_buffer(d->size, format().internalTextureFormat() != GL_RGB, true); |
1178 | if (!wasBound) |
1179 | const_cast<QGLFramebufferObject *>(this)->release(); |
1180 | |
1181 | return image; |
1182 | } |
1183 | |
1184 | Q_GLOBAL_STATIC(QGLEngineThreadStorage<QGL2PaintEngineEx>, qt_buffer_2_engine) |
1185 | |
1186 | /*! \reimp */ |
1187 | QPaintEngine *QGLFramebufferObject::paintEngine() const |
1188 | { |
1189 | Q_D(const QGLFramebufferObject); |
1190 | if (d->engine) |
1191 | return d->engine; |
1192 | |
1193 | QPaintEngine *engine = qt_buffer_2_engine()->engine(); |
1194 | if (engine->isActive() && engine->paintDevice() != this) { |
1195 | d->engine = new QGL2PaintEngineEx; |
1196 | return d->engine; |
1197 | } |
1198 | return engine; |
1199 | } |
1200 | |
1201 | /*! |
1202 | \fn bool QGLFramebufferObject::bindDefault() |
1203 | |
1204 | Switches rendering back to the default, windowing system provided |
1205 | framebuffer. |
1206 | Returns \c true upon success, false otherwise. |
1207 | |
1208 | \sa bind(), release() |
1209 | */ |
1210 | bool QGLFramebufferObject::bindDefault() |
1211 | { |
1212 | QGLContext *ctx = const_cast<QGLContext *>(QGLContext::currentContext()); |
1213 | |
1214 | if (ctx) { |
1215 | QOpenGLFunctions functions(ctx->contextHandle()); |
1216 | if (!functions.hasOpenGLFeature(feature: QOpenGLFunctions::Framebuffers)) |
1217 | return false; |
1218 | |
1219 | ctx->d_ptr->setCurrentFbo(ctx->d_ptr->default_fbo); |
1220 | functions.glBindFramebuffer(GL_FRAMEBUFFER, framebuffer: ctx->d_ptr->default_fbo); |
1221 | #ifdef QT_DEBUG |
1222 | } else { |
1223 | qWarning(msg: "QGLFramebufferObject::bindDefault() called without current context." ); |
1224 | #endif |
1225 | } |
1226 | |
1227 | return ctx != 0; |
1228 | } |
1229 | |
1230 | /*! |
1231 | \fn bool QGLFramebufferObject::hasOpenGLFramebufferObjects() |
1232 | |
1233 | Returns \c true if the OpenGL \c{GL_EXT_framebuffer_object} extension |
1234 | is present on this system; otherwise returns \c false. |
1235 | */ |
1236 | bool QGLFramebufferObject::hasOpenGLFramebufferObjects() |
1237 | { |
1238 | return qgl_hasFeature(feature: QOpenGLFunctions::Framebuffers); |
1239 | } |
1240 | |
1241 | /*! |
1242 | \since 4.4 |
1243 | |
1244 | Draws the given texture, \a textureId, to the given target rectangle, |
1245 | \a target, in OpenGL model space. The \a textureTarget should be a 2D |
1246 | texture target. |
1247 | |
1248 | The framebuffer object should be bound when calling this function. |
1249 | |
1250 | Equivalent to the corresponding QGLContext::drawTexture(). |
1251 | */ |
1252 | void QGLFramebufferObject::drawTexture(const QRectF &target, GLuint textureId, GLenum textureTarget) |
1253 | { |
1254 | const_cast<QGLContext *>(QGLContext::currentContext())->drawTexture(target, textureId, textureTarget); |
1255 | } |
1256 | |
1257 | /*! |
1258 | \since 4.4 |
1259 | |
1260 | Draws the given texture, \a textureId, at the given \a point in OpenGL |
1261 | model space. The \a textureTarget should be a 2D texture target. |
1262 | |
1263 | The framebuffer object should be bound when calling this function. |
1264 | |
1265 | Equivalent to the corresponding QGLContext::drawTexture(). |
1266 | */ |
1267 | void QGLFramebufferObject::drawTexture(const QPointF &point, GLuint textureId, GLenum textureTarget) |
1268 | { |
1269 | const_cast<QGLContext *>(QGLContext::currentContext())->drawTexture(point, textureId, textureTarget); |
1270 | } |
1271 | |
1272 | /*! \reimp */ |
1273 | int QGLFramebufferObject::metric(PaintDeviceMetric metric) const |
1274 | { |
1275 | Q_D(const QGLFramebufferObject); |
1276 | |
1277 | float dpmx = qt_defaultDpiX()*100./2.54; |
1278 | float dpmy = qt_defaultDpiY()*100./2.54; |
1279 | int w = d->size.width(); |
1280 | int h = d->size.height(); |
1281 | switch (metric) { |
1282 | case PdmWidth: |
1283 | return w; |
1284 | |
1285 | case PdmHeight: |
1286 | return h; |
1287 | |
1288 | case PdmWidthMM: |
1289 | return qRound(d: w * 1000 / dpmx); |
1290 | |
1291 | case PdmHeightMM: |
1292 | return qRound(d: h * 1000 / dpmy); |
1293 | |
1294 | case PdmNumColors: |
1295 | return 0; |
1296 | |
1297 | case PdmDepth: |
1298 | return 32;//d->depth; |
1299 | |
1300 | case PdmDpiX: |
1301 | return qRound(d: dpmx * 0.0254); |
1302 | |
1303 | case PdmDpiY: |
1304 | return qRound(d: dpmy * 0.0254); |
1305 | |
1306 | case PdmPhysicalDpiX: |
1307 | return qRound(d: dpmx * 0.0254); |
1308 | |
1309 | case PdmPhysicalDpiY: |
1310 | return qRound(d: dpmy * 0.0254); |
1311 | |
1312 | case QPaintDevice::PdmDevicePixelRatio: |
1313 | return 1; |
1314 | |
1315 | case QPaintDevice::PdmDevicePixelRatioScaled: |
1316 | return 1 * QPaintDevice::devicePixelRatioFScale(); |
1317 | |
1318 | default: |
1319 | qWarning(msg: "QGLFramebufferObject::metric(), Unhandled metric type: %d.\n" , metric); |
1320 | break; |
1321 | } |
1322 | return 0; |
1323 | } |
1324 | |
1325 | /*! |
1326 | \fn GLuint QGLFramebufferObject::handle() const |
1327 | |
1328 | Returns the GL framebuffer object handle for this framebuffer |
1329 | object (returned by the \c{glGenFrameBuffersEXT()} function). This |
1330 | handle can be used to attach new images or buffers to the |
1331 | framebuffer. The user is responsible for cleaning up and |
1332 | destroying these objects. |
1333 | */ |
1334 | GLuint QGLFramebufferObject::handle() const |
1335 | { |
1336 | Q_D(const QGLFramebufferObject); |
1337 | return d->fbo(); |
1338 | } |
1339 | |
1340 | /*! \fn int QGLFramebufferObject::devType() const |
1341 | \internal |
1342 | */ |
1343 | |
1344 | |
1345 | /*! |
1346 | Returns the status of the depth and stencil buffers attached to |
1347 | this framebuffer object. |
1348 | */ |
1349 | |
1350 | QGLFramebufferObject::Attachment QGLFramebufferObject::attachment() const |
1351 | { |
1352 | Q_D(const QGLFramebufferObject); |
1353 | if (d->valid) |
1354 | return d->fbo_attachment; |
1355 | return NoAttachment; |
1356 | } |
1357 | |
1358 | /*! |
1359 | \since 4.5 |
1360 | |
1361 | Returns \c true if the framebuffer object is currently bound to a context, |
1362 | otherwise false is returned. |
1363 | */ |
1364 | |
1365 | bool QGLFramebufferObject::isBound() const |
1366 | { |
1367 | Q_D(const QGLFramebufferObject); |
1368 | const QGLContext *current = QGLContext::currentContext(); |
1369 | if (current) { |
1370 | current->d_ptr->refreshCurrentFbo(); |
1371 | return current->d_ptr->current_fbo == d->fbo(); |
1372 | } |
1373 | |
1374 | return false; |
1375 | } |
1376 | |
1377 | /*! |
1378 | \fn bool QGLFramebufferObject::hasOpenGLFramebufferBlit() |
1379 | |
1380 | \since 4.6 |
1381 | |
1382 | Returns \c true if the OpenGL \c{GL_EXT_framebuffer_blit} extension |
1383 | is present on this system; otherwise returns \c false. |
1384 | |
1385 | \sa blitFramebuffer() |
1386 | */ |
1387 | bool QGLFramebufferObject::hasOpenGLFramebufferBlit() |
1388 | { |
1389 | return QOpenGLExtensions(QOpenGLContext::currentContext()).hasOpenGLExtension(extension: QOpenGLExtensions::FramebufferBlit); |
1390 | } |
1391 | |
1392 | /*! |
1393 | \since 4.6 |
1394 | |
1395 | Blits from the \a sourceRect rectangle in the \a source framebuffer |
1396 | object to the \a targetRect rectangle in the \a target framebuffer object. |
1397 | |
1398 | If \a source or \a target is \nullptr, the default framebuffer will be used |
1399 | instead of a framebuffer object as source or target respectively. |
1400 | |
1401 | The \a buffers parameter should be a mask consisting of any combination of |
1402 | \c GL_COLOR_BUFFER_BIT, \c GL_DEPTH_BUFFER_BIT, and |
1403 | \c GL_STENCIL_BUFFER_BIT. Any buffer type that is not present both |
1404 | in the source and target buffers is ignored. |
1405 | |
1406 | The \a sourceRect and \a targetRect rectangles may have different sizes; |
1407 | in this case \a buffers should not contain \c GL_DEPTH_BUFFER_BIT or |
1408 | \c GL_STENCIL_BUFFER_BIT. The \a filter parameter should be set to |
1409 | \c GL_LINEAR or \c GL_NEAREST, and specifies whether linear or nearest |
1410 | interpolation should be used when scaling is performed. |
1411 | |
1412 | If \a source equals \a target a copy is performed within the same buffer. |
1413 | Results are undefined if the source and target rectangles overlap and |
1414 | have different sizes. The sizes must also be the same if any of the |
1415 | framebuffer objects are multisample framebuffers. |
1416 | |
1417 | Note that the scissor test will restrict the blit area if enabled. |
1418 | |
1419 | This function will have no effect unless hasOpenGLFramebufferBlit() returns |
1420 | true. |
1421 | |
1422 | \sa hasOpenGLFramebufferBlit() |
1423 | */ |
1424 | void QGLFramebufferObject::blitFramebuffer(QGLFramebufferObject *target, const QRect &targetRect, |
1425 | QGLFramebufferObject *source, const QRect &sourceRect, |
1426 | GLbitfield buffers, |
1427 | GLenum filter) |
1428 | { |
1429 | const QGLContext *ctx = QGLContext::currentContext(); |
1430 | if (!ctx || !ctx->contextHandle()) |
1431 | return; |
1432 | |
1433 | QOpenGLExtensions functions(ctx->contextHandle()); |
1434 | if (!functions.hasOpenGLExtension(extension: QOpenGLExtensions::FramebufferBlit)) |
1435 | return; |
1436 | |
1437 | QSurface *surface = ctx->contextHandle()->surface(); |
1438 | |
1439 | const int height = static_cast<QWindow *>(surface)->height(); |
1440 | |
1441 | const int sh = source ? source->height() : height; |
1442 | const int th = target ? target->height() : height; |
1443 | |
1444 | const int sx0 = sourceRect.left(); |
1445 | const int sx1 = sourceRect.left() + sourceRect.width(); |
1446 | const int sy0 = sh - (sourceRect.top() + sourceRect.height()); |
1447 | const int sy1 = sh - sourceRect.top(); |
1448 | |
1449 | const int tx0 = targetRect.left(); |
1450 | const int tx1 = targetRect.left() + targetRect.width(); |
1451 | const int ty0 = th - (targetRect.top() + targetRect.height()); |
1452 | const int ty1 = th - targetRect.top(); |
1453 | |
1454 | ctx->d_ptr->refreshCurrentFbo(); |
1455 | |
1456 | functions.glBindFramebuffer(GL_READ_FRAMEBUFFER, framebuffer: source ? source->handle() : 0); |
1457 | functions.glBindFramebuffer(GL_DRAW_FRAMEBUFFER, framebuffer: target ? target->handle() : 0); |
1458 | |
1459 | functions.glBlitFramebuffer(srcX0: sx0, srcY0: sy0, srcX1: sx1, srcY1: sy1, |
1460 | dstX0: tx0, dstY0: ty0, dstX1: tx1, dstY1: ty1, |
1461 | mask: buffers, filter); |
1462 | |
1463 | functions.glBindFramebuffer(GL_FRAMEBUFFER, framebuffer: ctx->d_ptr->current_fbo); |
1464 | } |
1465 | |
1466 | QT_END_NAMESPACE |
1467 | |