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
51QT_BEGIN_NAMESPACE
52
53extern 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*/
146void 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
168QGLFramebufferObjectFormat::QGLFramebufferObjectFormat()
169{
170 d = new QGLFramebufferObjectFormatPrivate;
171}
172
173/*!
174 Constructs a copy of \a other.
175*/
176
177QGLFramebufferObjectFormat::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
187QGLFramebufferObjectFormat &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*/
201QGLFramebufferObjectFormat::~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*/
220void 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*/
233int 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*/
252void 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*/
265bool 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*/
275void 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*/
287QGLFramebufferObject::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*/
298void 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*/
311GLenum 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*/
323void 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*/
337GLenum 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*/
346bool 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*/
358bool QGLFramebufferObjectFormat::operator!=(const QGLFramebufferObjectFormat& other) const
359{
360 return !(*this == other);
361}
362
363void 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
394QGLContext *QGLFBOGLPaintDevice::context() const
395{
396 return const_cast<QGLContext *>(QGLContext::currentContext());
397}
398
399bool 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
455namespace
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
476void 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
869QGLFramebufferObject::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*/
889QGLFramebufferObject::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
908QGLFramebufferObject::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
922QGLFramebufferObject::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*/
943QGLFramebufferObject::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*/
970QGLFramebufferObject::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*/
989QGLFramebufferObject::~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*/
1025bool 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*/
1040bool 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*/
1072bool 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*/
1109GLuint 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*/
1121QSize 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*/
1130QGLFramebufferObjectFormat 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*/
1158QImage 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
1184Q_GLOBAL_STATIC(QGLEngineThreadStorage<QGL2PaintEngineEx>, qt_buffer_2_engine)
1185
1186/*! \reimp */
1187QPaintEngine *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*/
1210bool 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*/
1236bool 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*/
1252void 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*/
1267void QGLFramebufferObject::drawTexture(const QPointF &point, GLuint textureId, GLenum textureTarget)
1268{
1269 const_cast<QGLContext *>(QGLContext::currentContext())->drawTexture(point, textureId, textureTarget);
1270}
1271
1272/*! \reimp */
1273int 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*/
1334GLuint 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
1350QGLFramebufferObject::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
1365bool 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*/
1387bool 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*/
1424void 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
1466QT_END_NAMESPACE
1467

source code of qtbase/src/opengl/qglframebufferobject.cpp