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 "qopenglframebufferobject.h" |
41 | #include "qopenglframebufferobject_p.h" |
42 | |
43 | #include <qdebug.h> |
44 | #include <private/qopengl_p.h> |
45 | #include <private/qopenglcontext_p.h> |
46 | #include <private/qopenglextensions_p.h> |
47 | #include <private/qfont_p.h> |
48 | |
49 | #include <qwindow.h> |
50 | #include <qimage.h> |
51 | #include <QtCore/qbytearray.h> |
52 | |
53 | #include <qtopengl_tracepoints_p.h> |
54 | |
55 | QT_BEGIN_NAMESPACE |
56 | |
57 | #ifndef QT_NO_DEBUG |
58 | #define QT_RESET_GLERROR() \ |
59 | { \ |
60 | while (true) {\ |
61 | GLenum error = QOpenGLContext::currentContext()->functions()->glGetError(); \ |
62 | if (error == GL_NO_ERROR || error == GL_CONTEXT_LOST) \ |
63 | break; \ |
64 | } \ |
65 | } |
66 | #define QT_CHECK_GLERROR() \ |
67 | { \ |
68 | GLenum err = QOpenGLContext::currentContext()->functions()->glGetError(); \ |
69 | if (err != GL_NO_ERROR && err != GL_CONTEXT_LOST) { \ |
70 | qDebug("[%s line %d] OpenGL Error: %d", \ |
71 | __FILE__, __LINE__, (int)err); \ |
72 | } \ |
73 | } |
74 | #else |
75 | #define QT_RESET_GLERROR() {} |
76 | #define QT_CHECK_GLERROR() {} |
77 | #endif |
78 | |
79 | #ifndef GL_MAX_SAMPLES |
80 | #define GL_MAX_SAMPLES 0x8D57 |
81 | #endif |
82 | |
83 | #ifndef GL_RENDERBUFFER_SAMPLES |
84 | #define GL_RENDERBUFFER_SAMPLES 0x8CAB |
85 | #endif |
86 | |
87 | #ifndef GL_DEPTH24_STENCIL8 |
88 | #define GL_DEPTH24_STENCIL8 0x88F0 |
89 | #endif |
90 | |
91 | #ifndef GL_DEPTH_COMPONENT24 |
92 | #define GL_DEPTH_COMPONENT24 0x81A6 |
93 | #endif |
94 | |
95 | #ifndef GL_DEPTH_COMPONENT24_OES |
96 | #define GL_DEPTH_COMPONENT24_OES 0x81A6 |
97 | #endif |
98 | |
99 | #ifndef GL_READ_FRAMEBUFFER |
100 | #define GL_READ_FRAMEBUFFER 0x8CA8 |
101 | #endif |
102 | |
103 | #ifndef GL_DRAW_FRAMEBUFFER |
104 | #define GL_DRAW_FRAMEBUFFER 0x8CA9 |
105 | #endif |
106 | |
107 | #ifndef GL_RGB8 |
108 | #define GL_RGB8 0x8051 |
109 | #endif |
110 | |
111 | #ifndef GL_RGB10 |
112 | #define GL_RGB10 0x8052 |
113 | #endif |
114 | |
115 | #ifndef GL_RGB16 |
116 | #define GL_RGB16 0x8054 |
117 | #endif |
118 | |
119 | #ifndef GL_RGBA8 |
120 | #define GL_RGBA8 0x8058 |
121 | #endif |
122 | |
123 | #ifndef GL_RGB10_A2 |
124 | #define GL_RGB10_A2 0x8059 |
125 | #endif |
126 | |
127 | #ifndef GL_RGBA16 |
128 | #define GL_RGBA16 0x805B |
129 | #endif |
130 | |
131 | #ifndef GL_BGRA |
132 | #define GL_BGRA 0x80E1 |
133 | #endif |
134 | |
135 | #ifndef GL_UNSIGNED_INT_8_8_8_8_REV |
136 | #define GL_UNSIGNED_INT_8_8_8_8_REV 0x8367 |
137 | #endif |
138 | |
139 | #ifndef GL_UNSIGNED_INT_2_10_10_10_REV |
140 | #define GL_UNSIGNED_INT_2_10_10_10_REV 0x8368 |
141 | #endif |
142 | |
143 | #ifndef GL_CONTEXT_LOST |
144 | #define GL_CONTEXT_LOST 0x0507 |
145 | #endif |
146 | |
147 | #ifndef GL_DEPTH_STENCIL_ATTACHMENT |
148 | #define GL_DEPTH_STENCIL_ATTACHMENT 0x821A |
149 | #endif |
150 | |
151 | #ifndef GL_DEPTH_STENCIL |
152 | #define GL_DEPTH_STENCIL 0x84F9 |
153 | #endif |
154 | |
155 | |
156 | |
157 | /*! |
158 | \class QOpenGLFramebufferObjectFormat |
159 | \brief The QOpenGLFramebufferObjectFormat class specifies the format of an OpenGL |
160 | framebuffer object. |
161 | \inmodule QtOpenGL |
162 | |
163 | \since 5.0 |
164 | |
165 | \ingroup painting-3D |
166 | |
167 | A framebuffer object has several characteristics: |
168 | \list |
169 | \li \l{setSamples()}{Number of samples per pixels.} |
170 | \li \l{setAttachment()}{Depth and/or stencil attachments.} |
171 | \li \l{setTextureTarget()}{Texture target.} |
172 | \li \l{setInternalTextureFormat()}{Internal texture format.} |
173 | \endlist |
174 | |
175 | Note that the desired attachments or number of samples per pixels might not |
176 | be supported by the hardware driver. Call QOpenGLFramebufferObject::format() |
177 | after creating a QOpenGLFramebufferObject to find the exact format that was |
178 | used to create the frame buffer object. |
179 | |
180 | \sa QOpenGLFramebufferObject |
181 | */ |
182 | |
183 | /*! |
184 | \internal |
185 | */ |
186 | void QOpenGLFramebufferObjectFormat::detach() |
187 | { |
188 | if (d->ref.loadRelaxed() != 1) { |
189 | QOpenGLFramebufferObjectFormatPrivate *newd |
190 | = new QOpenGLFramebufferObjectFormatPrivate(d); |
191 | if (!d->ref.deref()) |
192 | delete d; |
193 | d = newd; |
194 | } |
195 | } |
196 | |
197 | /*! |
198 | Creates a QOpenGLFramebufferObjectFormat object for specifying |
199 | the format of an OpenGL framebuffer object. |
200 | |
201 | By default the format specifies a non-multisample framebuffer object with no |
202 | depth/stencil attachments, texture target \c GL_TEXTURE_2D, and internal format \c GL_RGBA8. |
203 | On OpenGL/ES systems, the default internal format is \c GL_RGBA. |
204 | |
205 | \sa samples(), attachment(), internalTextureFormat() |
206 | */ |
207 | |
208 | QOpenGLFramebufferObjectFormat::QOpenGLFramebufferObjectFormat() |
209 | { |
210 | d = new QOpenGLFramebufferObjectFormatPrivate; |
211 | } |
212 | |
213 | /*! |
214 | Constructs a copy of \a other. |
215 | */ |
216 | |
217 | QOpenGLFramebufferObjectFormat::QOpenGLFramebufferObjectFormat(const QOpenGLFramebufferObjectFormat &other) |
218 | { |
219 | d = other.d; |
220 | d->ref.ref(); |
221 | } |
222 | |
223 | /*! |
224 | Assigns \a other to this object. |
225 | */ |
226 | |
227 | QOpenGLFramebufferObjectFormat &QOpenGLFramebufferObjectFormat::operator=(const QOpenGLFramebufferObjectFormat &other) |
228 | { |
229 | if (d != other.d) { |
230 | other.d->ref.ref(); |
231 | if (!d->ref.deref()) |
232 | delete d; |
233 | d = other.d; |
234 | } |
235 | return *this; |
236 | } |
237 | |
238 | /*! |
239 | Destroys the QOpenGLFramebufferObjectFormat. |
240 | */ |
241 | QOpenGLFramebufferObjectFormat::~QOpenGLFramebufferObjectFormat() |
242 | { |
243 | if (!d->ref.deref()) |
244 | delete d; |
245 | } |
246 | |
247 | /*! |
248 | Sets the number of samples per pixel for a multisample framebuffer object |
249 | to \a samples. The default sample count of 0 represents a regular |
250 | non-multisample framebuffer object. |
251 | |
252 | If the desired amount of samples per pixel is not supported by the hardware |
253 | then the maximum number of samples per pixel will be used. Note that |
254 | multisample framebuffer objects cannot be bound as textures. Also, the |
255 | \c{GL_EXT_framebuffer_multisample} extension is required to create a |
256 | framebuffer with more than one sample per pixel. |
257 | |
258 | \sa samples() |
259 | */ |
260 | void QOpenGLFramebufferObjectFormat::setSamples(int samples) |
261 | { |
262 | detach(); |
263 | d->samples = samples; |
264 | } |
265 | |
266 | /*! |
267 | Returns the number of samples per pixel if a framebuffer object |
268 | is a multisample framebuffer object. Otherwise, returns 0. |
269 | The default value is 0. |
270 | |
271 | \sa setSamples() |
272 | */ |
273 | int QOpenGLFramebufferObjectFormat::samples() const |
274 | { |
275 | return d->samples; |
276 | } |
277 | |
278 | /*! |
279 | Enables mipmapping if \a enabled is true; otherwise disables it. |
280 | |
281 | Mipmapping is disabled by default. |
282 | |
283 | If mipmapping is enabled, additional memory will be allocated for |
284 | the mipmap levels. The mipmap levels can be updated by binding the |
285 | texture and calling glGenerateMipmap(). Mipmapping cannot be enabled |
286 | for multisampled framebuffer objects. |
287 | |
288 | \sa mipmap(), QOpenGLFramebufferObject::texture() |
289 | */ |
290 | void QOpenGLFramebufferObjectFormat::setMipmap(bool enabled) |
291 | { |
292 | detach(); |
293 | d->mipmap = enabled; |
294 | } |
295 | |
296 | /*! |
297 | Returns \c true if mipmapping is enabled. |
298 | |
299 | \sa setMipmap() |
300 | */ |
301 | bool QOpenGLFramebufferObjectFormat::mipmap() const |
302 | { |
303 | return d->mipmap; |
304 | } |
305 | |
306 | /*! |
307 | Sets the attachment configuration of a framebuffer object to \a attachment. |
308 | |
309 | \sa attachment() |
310 | */ |
311 | void QOpenGLFramebufferObjectFormat::setAttachment(QOpenGLFramebufferObject::Attachment attachment) |
312 | { |
313 | detach(); |
314 | d->attachment = attachment; |
315 | } |
316 | |
317 | /*! |
318 | Returns the configuration of the depth and stencil buffers attached to |
319 | a framebuffer object. The default is QOpenGLFramebufferObject::NoAttachment. |
320 | |
321 | \sa setAttachment() |
322 | */ |
323 | QOpenGLFramebufferObject::Attachment QOpenGLFramebufferObjectFormat::attachment() const |
324 | { |
325 | return d->attachment; |
326 | } |
327 | |
328 | /*! |
329 | Sets the texture target of the texture attached to a framebuffer object to |
330 | \a target. Ignored for multisample framebuffer objects. |
331 | |
332 | \sa textureTarget(), samples() |
333 | */ |
334 | void QOpenGLFramebufferObjectFormat::setTextureTarget(GLenum target) |
335 | { |
336 | detach(); |
337 | d->target = target; |
338 | } |
339 | |
340 | /*! |
341 | Returns the texture target of the texture attached to a framebuffer object. |
342 | Ignored for multisample framebuffer objects. The default is |
343 | \c GL_TEXTURE_2D. |
344 | |
345 | \sa setTextureTarget(), samples() |
346 | */ |
347 | GLenum QOpenGLFramebufferObjectFormat::textureTarget() const |
348 | { |
349 | return d->target; |
350 | } |
351 | |
352 | /*! |
353 | Sets the internal format of a framebuffer object's texture or |
354 | multisample framebuffer object's color buffer to |
355 | \a internalTextureFormat. |
356 | |
357 | \sa internalTextureFormat() |
358 | */ |
359 | void QOpenGLFramebufferObjectFormat::setInternalTextureFormat(GLenum internalTextureFormat) |
360 | { |
361 | detach(); |
362 | d->internal_format = internalTextureFormat; |
363 | } |
364 | |
365 | /*! |
366 | Returns the internal format of a framebuffer object's texture or |
367 | multisample framebuffer object's color buffer. The default is |
368 | \c GL_RGBA8 on desktop OpenGL systems, and \c GL_RGBA on |
369 | OpenGL/ES systems. |
370 | |
371 | \sa setInternalTextureFormat() |
372 | */ |
373 | GLenum QOpenGLFramebufferObjectFormat::internalTextureFormat() const |
374 | { |
375 | return d->internal_format; |
376 | } |
377 | |
378 | /*! |
379 | Returns \c true if all the options of this framebuffer object format |
380 | are the same as \a other; otherwise returns \c false. |
381 | */ |
382 | bool QOpenGLFramebufferObjectFormat::operator==(const QOpenGLFramebufferObjectFormat& other) const |
383 | { |
384 | if (d == other.d) |
385 | return true; |
386 | else |
387 | return d->equals(other.d); |
388 | } |
389 | |
390 | /*! |
391 | Returns \c false if all the options of this framebuffer object format |
392 | are the same as \a other; otherwise returns \c true. |
393 | */ |
394 | bool QOpenGLFramebufferObjectFormat::operator!=(const QOpenGLFramebufferObjectFormat& other) const |
395 | { |
396 | return !(*this == other); |
397 | } |
398 | |
399 | bool QOpenGLFramebufferObjectPrivate::checkFramebufferStatus(QOpenGLContext *ctx) const |
400 | { |
401 | if (!ctx) |
402 | return false; // Context no longer exists. |
403 | GLenum status = ctx->functions()->glCheckFramebufferStatus(GL_FRAMEBUFFER); |
404 | switch(status) { |
405 | case GL_NO_ERROR: |
406 | case GL_FRAMEBUFFER_COMPLETE: |
407 | return true; |
408 | case GL_FRAMEBUFFER_UNSUPPORTED: |
409 | qDebug("QOpenGLFramebufferObject: Unsupported framebuffer format." ); |
410 | break; |
411 | case GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT: |
412 | qDebug("QOpenGLFramebufferObject: Framebuffer incomplete attachment." ); |
413 | break; |
414 | case GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT: |
415 | qDebug("QOpenGLFramebufferObject: Framebuffer incomplete, missing attachment." ); |
416 | break; |
417 | #ifdef GL_FRAMEBUFFER_INCOMPLETE_DUPLICATE_ATTACHMENT |
418 | case GL_FRAMEBUFFER_INCOMPLETE_DUPLICATE_ATTACHMENT: |
419 | qDebug("QOpenGLFramebufferObject: Framebuffer incomplete, duplicate attachment." ); |
420 | break; |
421 | #endif |
422 | #ifdef GL_FRAMEBUFFER_INCOMPLETE_DIMENSIONS |
423 | case GL_FRAMEBUFFER_INCOMPLETE_DIMENSIONS: |
424 | qDebug("QOpenGLFramebufferObject: Framebuffer incomplete, attached images must have same dimensions." ); |
425 | break; |
426 | #endif |
427 | #ifdef GL_FRAMEBUFFER_INCOMPLETE_FORMATS |
428 | case GL_FRAMEBUFFER_INCOMPLETE_FORMATS: |
429 | qDebug("QOpenGLFramebufferObject: Framebuffer incomplete, attached images must have same format." ); |
430 | break; |
431 | #endif |
432 | #ifdef GL_FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER |
433 | case GL_FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER: |
434 | qDebug("QOpenGLFramebufferObject: Framebuffer incomplete, missing draw buffer." ); |
435 | break; |
436 | #endif |
437 | #ifdef GL_FRAMEBUFFER_INCOMPLETE_READ_BUFFER |
438 | case GL_FRAMEBUFFER_INCOMPLETE_READ_BUFFER: |
439 | qDebug("QOpenGLFramebufferObject: Framebuffer incomplete, missing read buffer." ); |
440 | break; |
441 | #endif |
442 | #ifdef GL_FRAMEBUFFER_INCOMPLETE_MULTISAMPLE |
443 | case GL_FRAMEBUFFER_INCOMPLETE_MULTISAMPLE: |
444 | qDebug("QOpenGLFramebufferObject: Framebuffer incomplete, attachments must have same number of samples per pixel." ); |
445 | break; |
446 | #endif |
447 | default: |
448 | qDebug() <<"QOpenGLFramebufferObject: An undefined error has occurred: " << status; |
449 | break; |
450 | } |
451 | return false; |
452 | } |
453 | |
454 | namespace |
455 | { |
456 | void freeFramebufferFunc(QOpenGLFunctions *funcs, GLuint id) |
457 | { |
458 | funcs->glDeleteFramebuffers(1, &id); |
459 | } |
460 | |
461 | void freeRenderbufferFunc(QOpenGLFunctions *funcs, GLuint id) |
462 | { |
463 | funcs->glDeleteRenderbuffers(1, &id); |
464 | } |
465 | |
466 | void freeTextureFunc(QOpenGLFunctions *funcs, GLuint id) |
467 | { |
468 | funcs->glDeleteTextures(1, &id); |
469 | } |
470 | } |
471 | |
472 | void QOpenGLFramebufferObjectPrivate::init(QOpenGLFramebufferObject *qfbo, const QSize &size, |
473 | QOpenGLFramebufferObject::Attachment attachment, |
474 | GLenum texture_target, GLenum internal_format, |
475 | GLint samples, bool mipmap) |
476 | { |
477 | Q_TRACE_SCOPE(QOpenGLFramebufferObjectPrivate_init, qfbo, size, attachment, texture_target, internal_format, samples, mipmap); |
478 | Q_UNUSED(qfbo); |
479 | |
480 | QOpenGLContext *ctx = QOpenGLContext::currentContext(); |
481 | |
482 | funcs.initializeOpenGLFunctions(); |
483 | |
484 | if (!funcs.hasOpenGLFeature(QOpenGLFunctions::Framebuffers)) |
485 | return; |
486 | |
487 | // Fall back to using a normal non-msaa FBO if we don't have support for MSAA |
488 | if (!funcs.hasOpenGLExtension(QOpenGLExtensions::FramebufferMultisample) |
489 | || !funcs.hasOpenGLExtension(QOpenGLExtensions::FramebufferBlit)) { |
490 | samples = 0; |
491 | } else if (!ctx->isOpenGLES() || ctx->format().majorVersion() >= 3) { |
492 | GLint maxSamples; |
493 | funcs.glGetIntegerv(GL_MAX_SAMPLES, &maxSamples); |
494 | samples = qBound(0, int(samples), int(maxSamples)); |
495 | } |
496 | |
497 | colorAttachments.append(ColorAttachment(size, internal_format)); |
498 | |
499 | dsSize = size; |
500 | |
501 | samples = qMax(0, samples); |
502 | requestedSamples = samples; |
503 | |
504 | target = texture_target; |
505 | |
506 | QT_RESET_GLERROR(); // reset error state |
507 | GLuint fbo = 0; |
508 | |
509 | funcs.glGenFramebuffers(1, &fbo); |
510 | funcs.glBindFramebuffer(GL_FRAMEBUFFER, fbo); |
511 | |
512 | QOpenGLContextPrivate::get(ctx)->qgl_current_fbo_invalid = true; |
513 | |
514 | QT_CHECK_GLERROR(); |
515 | |
516 | format.setTextureTarget(target); |
517 | format.setInternalTextureFormat(internal_format); |
518 | format.setMipmap(mipmap); |
519 | |
520 | if (samples == 0) |
521 | initTexture(0); |
522 | else |
523 | initColorBuffer(0, &samples); |
524 | |
525 | format.setSamples(int(samples)); |
526 | |
527 | initDepthStencilAttachments(ctx, attachment); |
528 | |
529 | if (valid) |
530 | fbo_guard = new QOpenGLSharedResourceGuard(ctx, fbo, freeFramebufferFunc); |
531 | else |
532 | funcs.glDeleteFramebuffers(1, &fbo); |
533 | |
534 | QT_CHECK_GLERROR(); |
535 | } |
536 | |
537 | void QOpenGLFramebufferObjectPrivate::initTexture(int idx) |
538 | { |
539 | QOpenGLContext *ctx = QOpenGLContext::currentContext(); |
540 | GLuint texture = 0; |
541 | |
542 | funcs.glGenTextures(1, &texture); |
543 | funcs.glBindTexture(target, texture); |
544 | |
545 | funcs.glTexParameteri(target, GL_TEXTURE_MIN_FILTER, GL_NEAREST); |
546 | funcs.glTexParameteri(target, GL_TEXTURE_MAG_FILTER, GL_NEAREST); |
547 | funcs.glTexParameteri(target, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); |
548 | funcs.glTexParameteri(target, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); |
549 | |
550 | ColorAttachment &color(colorAttachments[idx]); |
551 | |
552 | GLuint pixelType = GL_UNSIGNED_BYTE; |
553 | if (color.internalFormat == GL_RGB10_A2 || color.internalFormat == GL_RGB10) |
554 | pixelType = GL_UNSIGNED_INT_2_10_10_10_REV; |
555 | else if (color.internalFormat == GL_RGB16 || color.internalFormat == GL_RGBA16) |
556 | pixelType = GL_UNSIGNED_SHORT; |
557 | |
558 | funcs.glTexImage2D(target, 0, color.internalFormat, color.size.width(), color.size.height(), 0, |
559 | GL_RGBA, pixelType, nullptr); |
560 | if (format.mipmap()) { |
561 | int width = color.size.width(); |
562 | int height = color.size.height(); |
563 | int level = 0; |
564 | while (width > 1 || height > 1) { |
565 | width = qMax(1, width >> 1); |
566 | height = qMax(1, height >> 1); |
567 | ++level; |
568 | funcs.glTexImage2D(target, level, color.internalFormat, width, height, 0, |
569 | GL_RGBA, pixelType, nullptr); |
570 | } |
571 | } |
572 | funcs.glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0 + idx, |
573 | target, texture, 0); |
574 | |
575 | QT_CHECK_GLERROR(); |
576 | funcs.glBindTexture(target, 0); |
577 | valid = checkFramebufferStatus(ctx); |
578 | if (valid) { |
579 | color.guard = new QOpenGLSharedResourceGuard(ctx, texture, freeTextureFunc); |
580 | } else { |
581 | funcs.glDeleteTextures(1, &texture); |
582 | } |
583 | } |
584 | |
585 | void QOpenGLFramebufferObjectPrivate::initColorBuffer(int idx, GLint *samples) |
586 | { |
587 | QOpenGLContext *ctx = QOpenGLContext::currentContext(); |
588 | GLuint color_buffer = 0; |
589 | |
590 | ColorAttachment &color(colorAttachments[idx]); |
591 | |
592 | GLenum storageFormat = color.internalFormat; |
593 | // ES requires a sized format. The older desktop extension does not. Correct the format on ES. |
594 | if (ctx->isOpenGLES()) { |
595 | if (color.internalFormat == GL_RGBA) { |
596 | if (funcs.hasOpenGLExtension(QOpenGLExtensions::Sized8Formats)) |
597 | storageFormat = GL_RGBA8; |
598 | else |
599 | storageFormat = GL_RGBA4; |
600 | } else if (color.internalFormat == GL_RGB10) { |
601 | // GL_RGB10 is not allowed in ES for glRenderbufferStorage. |
602 | storageFormat = GL_RGB10_A2; |
603 | } |
604 | } |
605 | |
606 | funcs.glGenRenderbuffers(1, &color_buffer); |
607 | funcs.glBindRenderbuffer(GL_RENDERBUFFER, color_buffer); |
608 | funcs.glRenderbufferStorageMultisample(GL_RENDERBUFFER, *samples, storageFormat, color.size.width(), color.size.height()); |
609 | funcs.glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0 + idx, |
610 | GL_RENDERBUFFER, color_buffer); |
611 | |
612 | QT_CHECK_GLERROR(); |
613 | valid = checkFramebufferStatus(ctx); |
614 | if (valid) { |
615 | // Query the actual number of samples. This can be greater than the requested |
616 | // value since the typically supported values are 0, 4, 8, ..., and the |
617 | // requests are mapped to the next supported value. |
618 | funcs.glGetRenderbufferParameteriv(GL_RENDERBUFFER, GL_RENDERBUFFER_SAMPLES, samples); |
619 | color.guard = new QOpenGLSharedResourceGuard(ctx, color_buffer, freeRenderbufferFunc); |
620 | } else { |
621 | funcs.glDeleteRenderbuffers(1, &color_buffer); |
622 | } |
623 | } |
624 | |
625 | void QOpenGLFramebufferObjectPrivate::initDepthStencilAttachments(QOpenGLContext *ctx, |
626 | QOpenGLFramebufferObject::Attachment attachment) |
627 | { |
628 | // Use the same sample count for all attachments. format.samples() already contains |
629 | // the actual number of samples for the color attachment and is not suitable. Use |
630 | // requestedSamples instead. |
631 | const int samples = requestedSamples; |
632 | |
633 | // free existing attachments |
634 | if (depth_buffer_guard) { |
635 | #ifdef Q_OS_WASM |
636 | funcs.glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_RENDERBUFFER, 0); |
637 | #else |
638 | funcs.glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, 0); |
639 | #endif |
640 | depth_buffer_guard->free(); |
641 | } |
642 | if (stencil_buffer_guard) { |
643 | funcs.glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_RENDERBUFFER, 0); |
644 | if (stencil_buffer_guard != depth_buffer_guard) |
645 | stencil_buffer_guard->free(); |
646 | } |
647 | |
648 | depth_buffer_guard = nullptr; |
649 | stencil_buffer_guard = nullptr; |
650 | |
651 | GLuint depth_buffer = 0; |
652 | GLuint stencil_buffer = 0; |
653 | |
654 | // In practice, a combined depth-stencil buffer is supported by all desktop platforms, while a |
655 | // separate stencil buffer is not. On embedded devices however, a combined depth-stencil buffer |
656 | // might not be supported while separate buffers are, according to QTBUG-12861. |
657 | #ifdef Q_OS_WASM |
658 | // WebGL doesn't allow separately attach buffers to |
659 | // STENCIL_ATTACHMENT and DEPTH_ATTACHMENT |
660 | // QTBUG-69913 |
661 | if (attachment == QOpenGLFramebufferObject::CombinedDepthStencil) { |
662 | funcs.glGenRenderbuffers(1, &depth_buffer); |
663 | funcs.glBindRenderbuffer(GL_RENDERBUFFER, depth_buffer); |
664 | Q_ASSERT(funcs.glIsRenderbuffer(depth_buffer)); |
665 | |
666 | if (samples != 0 ) { |
667 | funcs.glRenderbufferStorageMultisample(GL_RENDERBUFFER, samples, |
668 | GL_DEPTH24_STENCIL8, dsSize.width(), dsSize.height()); |
669 | } else { |
670 | funcs.glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_STENCIL, |
671 | dsSize.width(), dsSize.height()); |
672 | } |
673 | |
674 | funcs.glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, |
675 | GL_RENDERBUFFER, depth_buffer); |
676 | |
677 | valid = checkFramebufferStatus(ctx); |
678 | if (!valid) { |
679 | funcs.glDeleteRenderbuffers(1, &depth_buffer); |
680 | depth_buffer = 0; |
681 | } |
682 | } |
683 | #else |
684 | if (attachment == QOpenGLFramebufferObject::CombinedDepthStencil |
685 | && funcs.hasOpenGLExtension(QOpenGLExtensions::PackedDepthStencil)) |
686 | { |
687 | // depth and stencil buffer needs another extension |
688 | funcs.glGenRenderbuffers(1, &depth_buffer); |
689 | funcs.glBindRenderbuffer(GL_RENDERBUFFER, depth_buffer); |
690 | Q_ASSERT(funcs.glIsRenderbuffer(depth_buffer)); |
691 | if (samples != 0 && funcs.hasOpenGLExtension(QOpenGLExtensions::FramebufferMultisample)) |
692 | funcs.glRenderbufferStorageMultisample(GL_RENDERBUFFER, samples, |
693 | GL_DEPTH24_STENCIL8, dsSize.width(), dsSize.height()); |
694 | else |
695 | funcs.glRenderbufferStorage(GL_RENDERBUFFER, |
696 | GL_DEPTH24_STENCIL8, dsSize.width(), dsSize.height()); |
697 | |
698 | stencil_buffer = depth_buffer; |
699 | funcs.glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, |
700 | GL_RENDERBUFFER, depth_buffer); |
701 | funcs.glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, |
702 | GL_RENDERBUFFER, stencil_buffer); |
703 | |
704 | valid = checkFramebufferStatus(ctx); |
705 | if (!valid) { |
706 | funcs.glDeleteRenderbuffers(1, &depth_buffer); |
707 | stencil_buffer = depth_buffer = 0; |
708 | } |
709 | } |
710 | |
711 | if (depth_buffer == 0 && (attachment == QOpenGLFramebufferObject::CombinedDepthStencil |
712 | || (attachment == QOpenGLFramebufferObject::Depth))) |
713 | { |
714 | funcs.glGenRenderbuffers(1, &depth_buffer); |
715 | funcs.glBindRenderbuffer(GL_RENDERBUFFER, depth_buffer); |
716 | Q_ASSERT(funcs.glIsRenderbuffer(depth_buffer)); |
717 | if (samples != 0 && funcs.hasOpenGLExtension(QOpenGLExtensions::FramebufferMultisample)) { |
718 | if (ctx->isOpenGLES()) { |
719 | if (funcs.hasOpenGLExtension(QOpenGLExtensions::Depth24)) |
720 | funcs.glRenderbufferStorageMultisample(GL_RENDERBUFFER, samples, |
721 | GL_DEPTH_COMPONENT24, dsSize.width(), dsSize.height()); |
722 | else |
723 | funcs.glRenderbufferStorageMultisample(GL_RENDERBUFFER, samples, |
724 | GL_DEPTH_COMPONENT16, dsSize.width(), dsSize.height()); |
725 | } else { |
726 | funcs.glRenderbufferStorageMultisample(GL_RENDERBUFFER, samples, |
727 | GL_DEPTH_COMPONENT, dsSize.width(), dsSize.height()); |
728 | } |
729 | } else { |
730 | if (ctx->isOpenGLES()) { |
731 | if (funcs.hasOpenGLExtension(QOpenGLExtensions::Depth24)) { |
732 | funcs.glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT24, |
733 | dsSize.width(), dsSize.height()); |
734 | } else { |
735 | funcs.glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT16, |
736 | dsSize.width(), dsSize.height()); |
737 | } |
738 | } else { |
739 | funcs.glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT, dsSize.width(), dsSize.height()); |
740 | } |
741 | } |
742 | funcs.glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, |
743 | GL_RENDERBUFFER, depth_buffer); |
744 | valid = checkFramebufferStatus(ctx); |
745 | if (!valid) { |
746 | funcs.glDeleteRenderbuffers(1, &depth_buffer); |
747 | depth_buffer = 0; |
748 | } |
749 | } |
750 | |
751 | if (stencil_buffer == 0 && (attachment == QOpenGLFramebufferObject::CombinedDepthStencil)) { |
752 | funcs.glGenRenderbuffers(1, &stencil_buffer); |
753 | funcs.glBindRenderbuffer(GL_RENDERBUFFER, stencil_buffer); |
754 | Q_ASSERT(funcs.glIsRenderbuffer(stencil_buffer)); |
755 | |
756 | #if QT_CONFIG(opengles2) |
757 | GLenum storage = GL_STENCIL_INDEX8; |
758 | #else |
759 | GLenum storage = ctx->isOpenGLES() ? GL_STENCIL_INDEX8 : GL_STENCIL_INDEX; |
760 | #endif |
761 | |
762 | if (samples != 0 && funcs.hasOpenGLExtension(QOpenGLExtensions::FramebufferMultisample)) |
763 | funcs.glRenderbufferStorageMultisample(GL_RENDERBUFFER, samples, storage, dsSize.width(), dsSize.height()); |
764 | else |
765 | funcs.glRenderbufferStorage(GL_RENDERBUFFER, storage, dsSize.width(), dsSize.height()); |
766 | |
767 | funcs.glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, |
768 | GL_RENDERBUFFER, stencil_buffer); |
769 | valid = checkFramebufferStatus(ctx); |
770 | if (!valid) { |
771 | funcs.glDeleteRenderbuffers(1, &stencil_buffer); |
772 | stencil_buffer = 0; |
773 | } |
774 | } |
775 | #endif //Q_OS_WASM |
776 | |
777 | // The FBO might have become valid after removing the depth or stencil buffer. |
778 | valid = checkFramebufferStatus(ctx); |
779 | |
780 | #ifdef Q_OS_WASM |
781 | if (depth_buffer) { |
782 | #else |
783 | if (depth_buffer && stencil_buffer) { |
784 | #endif |
785 | fbo_attachment = QOpenGLFramebufferObject::CombinedDepthStencil; |
786 | } else if (depth_buffer) { |
787 | fbo_attachment = QOpenGLFramebufferObject::Depth; |
788 | } else { |
789 | fbo_attachment = QOpenGLFramebufferObject::NoAttachment; |
790 | } |
791 | |
792 | if (valid) { |
793 | if (depth_buffer) |
794 | depth_buffer_guard = new QOpenGLSharedResourceGuard(ctx, depth_buffer, freeRenderbufferFunc); |
795 | if (stencil_buffer) { |
796 | if (stencil_buffer == depth_buffer) |
797 | stencil_buffer_guard = depth_buffer_guard; |
798 | else |
799 | stencil_buffer_guard = new QOpenGLSharedResourceGuard(ctx, stencil_buffer, freeRenderbufferFunc); |
800 | } |
801 | } else { |
802 | if (depth_buffer) |
803 | funcs.glDeleteRenderbuffers(1, &depth_buffer); |
804 | if (stencil_buffer && depth_buffer != stencil_buffer) |
805 | funcs.glDeleteRenderbuffers(1, &stencil_buffer); |
806 | } |
807 | QT_CHECK_GLERROR(); |
808 | |
809 | format.setAttachment(fbo_attachment); |
810 | } |
811 | |
812 | /*! |
813 | \class QOpenGLFramebufferObject |
814 | \brief The QOpenGLFramebufferObject class encapsulates an OpenGL framebuffer object. |
815 | \since 5.0 |
816 | \inmodule QtOpenGL |
817 | |
818 | \ingroup painting-3D |
819 | |
820 | The QOpenGLFramebufferObject class encapsulates an OpenGL framebuffer |
821 | object, defined by the \c{GL_EXT_framebuffer_object} extension. It provides |
822 | a rendering surface that can be painted on with a QPainter with the help of |
823 | QOpenGLPaintDevice, or rendered to using native OpenGL calls. This surface |
824 | can be bound and used as a regular texture in your own OpenGL drawing code. |
825 | By default, the QOpenGLFramebufferObject class generates a 2D OpenGL |
826 | texture (using the \c{GL_TEXTURE_2D} target), which is used as the internal |
827 | rendering target. |
828 | |
829 | \b{It is important to have a current OpenGL context when creating a |
830 | QOpenGLFramebufferObject, otherwise initialization will fail.} |
831 | |
832 | Create the QOpenGLFrameBufferObject instance with the CombinedDepthStencil |
833 | attachment if you want QPainter to render correctly. Note that you need to |
834 | create a QOpenGLFramebufferObject with more than one sample per pixel for |
835 | primitives to be antialiased when drawing using a QPainter. To create a |
836 | multisample framebuffer object you should use one of the constructors that |
837 | take a QOpenGLFramebufferObjectFormat parameter, and set the |
838 | QOpenGLFramebufferObjectFormat::samples() property to a non-zero value. |
839 | |
840 | For multisample framebuffer objects a color render buffer is created, |
841 | otherwise a texture with the specified texture target is created. |
842 | The color render buffer or texture will have the specified internal |
843 | format, and will be bound to the \c GL_COLOR_ATTACHMENT0 |
844 | attachment in the framebuffer object. |
845 | |
846 | Multiple render targets are also supported, in case the OpenGL |
847 | implementation supports this. Here there will be multiple textures (or, in |
848 | case of multisampling, renderbuffers) present and each of them will get |
849 | attached to \c GL_COLOR_ATTACHMENT0, \c 1, \c 2, ... |
850 | |
851 | If you want to use a framebuffer object with multisampling enabled |
852 | as a texture, you first need to copy from it to a regular framebuffer |
853 | object using QOpenGLContext::blitFramebuffer(). |
854 | |
855 | It is possible to draw into a QOpenGLFramebufferObject using QPainter and |
856 | QOpenGLPaintDevice in a separate thread. |
857 | */ |
858 | |
859 | |
860 | /*! |
861 | \enum QOpenGLFramebufferObject::Attachment |
862 | |
863 | This enum type is used to configure the depth and stencil buffers |
864 | attached to the framebuffer object when it is created. |
865 | |
866 | \value NoAttachment No attachment is added to the framebuffer object. Note that the |
867 | OpenGL depth and stencil tests won't work when rendering to a |
868 | framebuffer object without any depth or stencil buffers. |
869 | This is the default value. |
870 | |
871 | \value CombinedDepthStencil If the \c GL_EXT_packed_depth_stencil extension is present, |
872 | a combined depth and stencil buffer is attached. |
873 | If the extension is not present, only a depth buffer is attached. |
874 | |
875 | \value Depth A depth buffer is attached to the framebuffer object. |
876 | |
877 | \sa attachment() |
878 | */ |
879 | |
880 | static inline GLenum effectiveInternalFormat(GLenum internalFormat) |
881 | { |
882 | if (!internalFormat) |
883 | #if QT_CONFIG(opengles2) |
884 | internalFormat = GL_RGBA; |
885 | #else |
886 | internalFormat = QOpenGLContext::currentContext()->isOpenGLES() ? GL_RGBA : GL_RGBA8; |
887 | #endif |
888 | return internalFormat; |
889 | } |
890 | |
891 | /*! |
892 | |
893 | Constructs an OpenGL framebuffer object and binds a 2D OpenGL texture |
894 | to the buffer of the size \a size. The texture is bound to the |
895 | \c GL_COLOR_ATTACHMENT0 target in the framebuffer object. |
896 | |
897 | The \a target parameter is used to specify the OpenGL texture |
898 | target. The default target is \c GL_TEXTURE_2D. Keep in mind that |
899 | \c GL_TEXTURE_2D textures must have a power of 2 width and height |
900 | (e.g. 256x512), unless you are using OpenGL 2.0 or higher. |
901 | |
902 | By default, no depth and stencil buffers are attached. This behavior |
903 | can be toggled using one of the overloaded constructors. |
904 | |
905 | The default internal texture format is \c GL_RGBA8 for desktop |
906 | OpenGL, and \c GL_RGBA for OpenGL/ES. |
907 | |
908 | It is important that you have a current OpenGL context set when |
909 | creating the QOpenGLFramebufferObject, otherwise the initialization |
910 | will fail. |
911 | |
912 | \sa size(), texture(), attachment() |
913 | */ |
914 | |
915 | QOpenGLFramebufferObject::QOpenGLFramebufferObject(const QSize &size, GLenum target) |
916 | : d_ptr(new QOpenGLFramebufferObjectPrivate) |
917 | { |
918 | Q_D(QOpenGLFramebufferObject); |
919 | d->init(this, size, NoAttachment, target, effectiveInternalFormat(0)); |
920 | } |
921 | |
922 | /*! |
923 | |
924 | Constructs an OpenGL framebuffer object and binds a 2D OpenGL texture |
925 | to the buffer of the given \a width and \a height. |
926 | |
927 | \sa size(), texture() |
928 | */ |
929 | QOpenGLFramebufferObject::QOpenGLFramebufferObject(int width, int height, GLenum target) |
930 | : QOpenGLFramebufferObject(QSize(width, height), target) |
931 | { |
932 | } |
933 | |
934 | /*! |
935 | |
936 | Constructs an OpenGL framebuffer object of the given \a size based on the |
937 | supplied \a format. |
938 | */ |
939 | |
940 | QOpenGLFramebufferObject::QOpenGLFramebufferObject(const QSize &size, const QOpenGLFramebufferObjectFormat &format) |
941 | : d_ptr(new QOpenGLFramebufferObjectPrivate) |
942 | { |
943 | Q_D(QOpenGLFramebufferObject); |
944 | d->init(this, size, format.attachment(), format.textureTarget(), format.internalTextureFormat(), |
945 | format.samples(), format.mipmap()); |
946 | } |
947 | |
948 | /*! |
949 | |
950 | Constructs an OpenGL framebuffer object of the given \a width and \a height |
951 | based on the supplied \a format. |
952 | */ |
953 | |
954 | QOpenGLFramebufferObject::QOpenGLFramebufferObject(int width, int height, const QOpenGLFramebufferObjectFormat &format) |
955 | : QOpenGLFramebufferObject(QSize(width, height), format) |
956 | { |
957 | } |
958 | |
959 | /*! |
960 | |
961 | Constructs an OpenGL framebuffer object and binds a texture to the |
962 | buffer of the given \a width and \a height. |
963 | |
964 | The \a attachment parameter describes the depth/stencil buffer |
965 | configuration, \a target the texture target and \a internalFormat |
966 | the internal texture format. The default texture target is \c |
967 | GL_TEXTURE_2D, while the default internal format is \c GL_RGBA8 |
968 | for desktop OpenGL and \c GL_RGBA for OpenGL/ES. |
969 | |
970 | \sa size(), texture(), attachment() |
971 | */ |
972 | QOpenGLFramebufferObject::QOpenGLFramebufferObject(int width, int height, Attachment attachment, |
973 | GLenum target, GLenum internalFormat) |
974 | : d_ptr(new QOpenGLFramebufferObjectPrivate) |
975 | { |
976 | Q_D(QOpenGLFramebufferObject); |
977 | d->init(this, QSize(width, height), attachment, target, effectiveInternalFormat(internalFormat)); |
978 | } |
979 | |
980 | /*! |
981 | |
982 | Constructs an OpenGL framebuffer object and binds a texture to the |
983 | buffer of the given \a size. |
984 | |
985 | The \a attachment parameter describes the depth/stencil buffer |
986 | configuration, \a target the texture target and \a internalFormat |
987 | the internal texture format. The default texture target is \c |
988 | GL_TEXTURE_2D, while the default internal format is \c GL_RGBA8 |
989 | for desktop OpenGL and \c GL_RGBA for OpenGL/ES. |
990 | |
991 | \sa size(), texture(), attachment() |
992 | */ |
993 | QOpenGLFramebufferObject::QOpenGLFramebufferObject(const QSize &size, Attachment attachment, |
994 | GLenum target, GLenum internalFormat) |
995 | : d_ptr(new QOpenGLFramebufferObjectPrivate) |
996 | { |
997 | Q_D(QOpenGLFramebufferObject); |
998 | d->init(this, size, attachment, target, effectiveInternalFormat(internalFormat)); |
999 | } |
1000 | |
1001 | /*! |
1002 | |
1003 | Destroys the framebuffer object and frees any allocated resources. |
1004 | */ |
1005 | QOpenGLFramebufferObject::~QOpenGLFramebufferObject() |
1006 | { |
1007 | Q_D(QOpenGLFramebufferObject); |
1008 | if (isBound()) |
1009 | release(); |
1010 | |
1011 | for (const auto &color : qAsConst(d->colorAttachments)) { |
1012 | if (color.guard) |
1013 | color.guard->free(); |
1014 | } |
1015 | d->colorAttachments.clear(); |
1016 | |
1017 | if (d->depth_buffer_guard) |
1018 | d->depth_buffer_guard->free(); |
1019 | if (d->stencil_buffer_guard && d->stencil_buffer_guard != d->depth_buffer_guard) |
1020 | d->stencil_buffer_guard->free(); |
1021 | if (d->fbo_guard) |
1022 | d->fbo_guard->free(); |
1023 | |
1024 | QOpenGLContextPrivate *contextPrv = QOpenGLContextPrivate::get(QOpenGLContext::currentContext()); |
1025 | if (contextPrv && contextPrv->qgl_current_fbo == this) { |
1026 | contextPrv->qgl_current_fbo_invalid = true; |
1027 | contextPrv->qgl_current_fbo = nullptr; |
1028 | } |
1029 | } |
1030 | |
1031 | /*! |
1032 | Creates and attaches an additional texture or renderbuffer of \a size width |
1033 | and height. |
1034 | |
1035 | There is always an attachment at GL_COLOR_ATTACHMENT0. Call this function |
1036 | to set up additional attachments at GL_COLOR_ATTACHMENT1, |
1037 | GL_COLOR_ATTACHMENT2, ... |
1038 | |
1039 | When \a internalFormat is not \c 0, it specifies the internal format of the |
1040 | texture or renderbuffer. Otherwise a default of GL_RGBA or GL_RGBA8 is |
1041 | used. |
1042 | |
1043 | \note This is only functional when multiple render targets are supported by |
1044 | the OpenGL implementation. When that is not the case, the function will not |
1045 | add any additional color attachments. Call |
1046 | QOpenGLFunctions::hasOpenGLFeature() with |
1047 | QOpenGLFunctions::MultipleRenderTargets at runtime to check if MRT is |
1048 | supported. |
1049 | |
1050 | \note The internal format of the color attachments may differ but there may |
1051 | be limitations on the supported combinations, depending on the drivers. |
1052 | |
1053 | \note The size of the color attachments may differ but rendering is limited |
1054 | to the area that fits all the attachments, according to the OpenGL |
1055 | specification. Some drivers may not be fully conformant in this respect, |
1056 | however. |
1057 | |
1058 | \since 5.6 |
1059 | */ |
1060 | void QOpenGLFramebufferObject::addColorAttachment(const QSize &size, GLenum internalFormat) |
1061 | { |
1062 | Q_D(QOpenGLFramebufferObject); |
1063 | |
1064 | if (!QOpenGLContext::currentContext()->functions()->hasOpenGLFeature(QOpenGLFunctions::MultipleRenderTargets)) { |
1065 | qWarning("Multiple render targets not supported, ignoring extra color attachment request" ); |
1066 | return; |
1067 | } |
1068 | |
1069 | QOpenGLFramebufferObjectPrivate::ColorAttachment color(size, effectiveInternalFormat(internalFormat)); |
1070 | d->colorAttachments.append(color); |
1071 | const int idx = d->colorAttachments.count() - 1; |
1072 | |
1073 | if (d->requestedSamples == 0) { |
1074 | d->initTexture(idx); |
1075 | } else { |
1076 | GLint samples = d->requestedSamples; |
1077 | d->initColorBuffer(idx, &samples); |
1078 | } |
1079 | } |
1080 | |
1081 | /*! \overload |
1082 | |
1083 | Creates and attaches an additional texture or renderbuffer of size \a width and \a height. |
1084 | |
1085 | When \a internalFormat is not \c 0, it specifies the internal format of the texture or |
1086 | renderbuffer. Otherwise a default of GL_RGBA or GL_RGBA8 is used. |
1087 | |
1088 | \since 5.6 |
1089 | */ |
1090 | void QOpenGLFramebufferObject::addColorAttachment(int width, int height, GLenum internalFormat) |
1091 | { |
1092 | addColorAttachment(QSize(width, height), internalFormat); |
1093 | } |
1094 | |
1095 | /*! |
1096 | \fn bool QOpenGLFramebufferObject::isValid() const |
1097 | |
1098 | Returns \c true if the framebuffer object is valid. |
1099 | |
1100 | The framebuffer can become invalid if the initialization process |
1101 | fails, the user attaches an invalid buffer to the framebuffer |
1102 | object, or a non-power of two width/height is specified as the |
1103 | texture size if the texture target is \c{GL_TEXTURE_2D}. |
1104 | The non-power of two limitation does not apply if the OpenGL version |
1105 | is 2.0 or higher, or if the GL_ARB_texture_non_power_of_two extension |
1106 | is present. |
1107 | |
1108 | The framebuffer can also become invalid if the QOpenGLContext that |
1109 | the framebuffer was created within is destroyed and there are |
1110 | no other shared contexts that can take over ownership of the |
1111 | framebuffer. |
1112 | */ |
1113 | bool QOpenGLFramebufferObject::isValid() const |
1114 | { |
1115 | Q_D(const QOpenGLFramebufferObject); |
1116 | return d->valid && d->fbo_guard && d->fbo_guard->id(); |
1117 | } |
1118 | |
1119 | /*! |
1120 | \fn bool QOpenGLFramebufferObject::bind() |
1121 | |
1122 | Switches rendering from the default, windowing system provided |
1123 | framebuffer to this framebuffer object. |
1124 | Returns \c true upon success, false otherwise. |
1125 | |
1126 | \note If takeTexture() was called, a new texture is created and associated |
1127 | with the framebuffer object. This is potentially expensive and changes the |
1128 | context state (the currently bound texture). |
1129 | |
1130 | \sa release() |
1131 | */ |
1132 | bool QOpenGLFramebufferObject::bind() |
1133 | { |
1134 | if (!isValid()) |
1135 | return false; |
1136 | Q_D(QOpenGLFramebufferObject); |
1137 | QOpenGLContext *current = QOpenGLContext::currentContext(); |
1138 | if (!current) |
1139 | return false; |
1140 | #ifdef QT_DEBUG |
1141 | if (current->shareGroup() != d->fbo_guard->group()) |
1142 | qWarning("QOpenGLFramebufferObject::bind() called from incompatible context" ); |
1143 | #endif |
1144 | |
1145 | d->funcs.glBindFramebuffer(GL_FRAMEBUFFER, d->fbo()); |
1146 | |
1147 | QOpenGLContextPrivate::get(current)->qgl_current_fbo_invalid = true; |
1148 | QOpenGLContextPrivate::get(current)->qgl_current_fbo = this; |
1149 | |
1150 | if (d->format.samples() == 0) { |
1151 | // Create new textures to replace the ones stolen via takeTexture(). |
1152 | for (int i = 0; i < d->colorAttachments.count(); ++i) { |
1153 | if (!d->colorAttachments.at(i).guard) |
1154 | d->initTexture(i); |
1155 | } |
1156 | } |
1157 | |
1158 | return d->valid; |
1159 | } |
1160 | |
1161 | /*! |
1162 | \fn bool QOpenGLFramebufferObject::release() |
1163 | |
1164 | Switches rendering back to the default, windowing system provided |
1165 | framebuffer. |
1166 | Returns \c true upon success, false otherwise. |
1167 | |
1168 | \sa bind() |
1169 | */ |
1170 | bool QOpenGLFramebufferObject::release() |
1171 | { |
1172 | if (!isValid()) |
1173 | return false; |
1174 | |
1175 | QOpenGLContext *current = QOpenGLContext::currentContext(); |
1176 | if (!current) |
1177 | return false; |
1178 | |
1179 | Q_D(QOpenGLFramebufferObject); |
1180 | #ifdef QT_DEBUG |
1181 | if (current->shareGroup() != d->fbo_guard->group()) |
1182 | qWarning("QOpenGLFramebufferObject::release() called from incompatible context" ); |
1183 | #endif |
1184 | |
1185 | if (current) { |
1186 | d->funcs.glBindFramebuffer(GL_FRAMEBUFFER, current->defaultFramebufferObject()); |
1187 | |
1188 | QOpenGLContextPrivate *contextPrv = QOpenGLContextPrivate::get(current); |
1189 | contextPrv->qgl_current_fbo_invalid = true; |
1190 | contextPrv->qgl_current_fbo = nullptr; |
1191 | } |
1192 | |
1193 | return true; |
1194 | } |
1195 | |
1196 | /*! |
1197 | \fn GLuint QOpenGLFramebufferObject::texture() const |
1198 | |
1199 | Returns the texture id for the texture attached as the default |
1200 | rendering target in this framebuffer object. This texture id can |
1201 | be bound as a normal texture in your own OpenGL code. |
1202 | |
1203 | If a multisample framebuffer object is used then the value returned |
1204 | from this function will be invalid. |
1205 | |
1206 | When multiple textures are attached, the return value is the ID of |
1207 | the first one. |
1208 | |
1209 | \sa takeTexture(), textures() |
1210 | */ |
1211 | GLuint QOpenGLFramebufferObject::texture() const |
1212 | { |
1213 | Q_D(const QOpenGLFramebufferObject); |
1214 | return d->colorAttachments[0].guard ? d->colorAttachments[0].guard->id() : 0; |
1215 | } |
1216 | |
1217 | /*! |
1218 | Returns the texture id for all attached textures. |
1219 | |
1220 | If a multisample framebuffer object is used, then an empty vector is returned. |
1221 | |
1222 | \since 5.6 |
1223 | |
1224 | \sa takeTexture(), texture() |
1225 | */ |
1226 | QList<GLuint> QOpenGLFramebufferObject::textures() const |
1227 | { |
1228 | Q_D(const QOpenGLFramebufferObject); |
1229 | QList<GLuint> ids; |
1230 | if (d->format.samples() != 0) |
1231 | return ids; |
1232 | ids.reserve(d->colorAttachments.count()); |
1233 | for (const auto &color : d->colorAttachments) |
1234 | ids.append(color.guard ? color.guard->id() : 0); |
1235 | return ids; |
1236 | } |
1237 | |
1238 | /*! |
1239 | \fn GLuint QOpenGLFramebufferObject::takeTexture() |
1240 | |
1241 | Returns the texture id for the texture attached to this framebuffer |
1242 | object. The ownership of the texture is transferred to the caller. |
1243 | |
1244 | If the framebuffer object is currently bound, an implicit release() |
1245 | will be done. During the next call to bind() a new texture will be |
1246 | created. |
1247 | |
1248 | If a multisample framebuffer object is used, then there is no |
1249 | texture and the return value from this function will be invalid. |
1250 | Similarly, incomplete framebuffer objects will also return 0. |
1251 | |
1252 | \since 5.3 |
1253 | |
1254 | \sa texture(), bind(), release() |
1255 | */ |
1256 | GLuint QOpenGLFramebufferObject::takeTexture() |
1257 | { |
1258 | return takeTexture(0); |
1259 | } |
1260 | |
1261 | /*! \overload |
1262 | |
1263 | Returns the texture id for the texture attached to the color attachment of |
1264 | index \a colorAttachmentIndex of this framebuffer object. The ownership of |
1265 | the texture is transferred to the caller. |
1266 | |
1267 | When \a colorAttachmentIndex is \c 0, the behavior is identical to the |
1268 | parameter-less variant of this function. |
1269 | |
1270 | If the framebuffer object is currently bound, an implicit release() |
1271 | will be done. During the next call to bind() a new texture will be |
1272 | created. |
1273 | |
1274 | If a multisample framebuffer object is used, then there is no |
1275 | texture and the return value from this function will be invalid. |
1276 | Similarly, incomplete framebuffer objects will also return 0. |
1277 | |
1278 | \since 5.6 |
1279 | */ |
1280 | GLuint QOpenGLFramebufferObject::takeTexture(int colorAttachmentIndex) |
1281 | { |
1282 | Q_D(QOpenGLFramebufferObject); |
1283 | GLuint id = 0; |
1284 | if (isValid() && d->format.samples() == 0 && d->colorAttachments.count() > colorAttachmentIndex) { |
1285 | QOpenGLContext *current = QOpenGLContext::currentContext(); |
1286 | if (current && current->shareGroup() == d->fbo_guard->group() && isBound()) |
1287 | release(); |
1288 | auto &guard = d->colorAttachments[colorAttachmentIndex].guard; |
1289 | id = guard ? guard->id() : 0; |
1290 | // Do not call free() on texture_guard, just null it out. |
1291 | // This way the texture will not be deleted when the guard is destroyed. |
1292 | guard = nullptr; |
1293 | } |
1294 | return id; |
1295 | } |
1296 | |
1297 | /*! |
1298 | \return the size of the color and depth/stencil attachments attached to |
1299 | this framebuffer object. |
1300 | */ |
1301 | QSize QOpenGLFramebufferObject::size() const |
1302 | { |
1303 | Q_D(const QOpenGLFramebufferObject); |
1304 | return d->dsSize; |
1305 | } |
1306 | |
1307 | /*! |
1308 | \return the sizes of all color attachments attached to this framebuffer |
1309 | object. |
1310 | |
1311 | \since 5.6 |
1312 | */ |
1313 | QList<QSize> QOpenGLFramebufferObject::sizes() const |
1314 | { |
1315 | Q_D(const QOpenGLFramebufferObject); |
1316 | QList<QSize> sz; |
1317 | sz.reserve(d->colorAttachments.size()); |
1318 | for (const auto &color : d->colorAttachments) |
1319 | sz.append(color.size); |
1320 | return sz; |
1321 | } |
1322 | |
1323 | /*! |
1324 | \fn int QOpenGLFramebufferObject::width() const |
1325 | |
1326 | Returns the width of the framebuffer object attachments. |
1327 | */ |
1328 | |
1329 | /*! |
1330 | \fn int QOpenGLFramebufferObject::height() const |
1331 | |
1332 | Returns the height of the framebuffer object attachments. |
1333 | */ |
1334 | |
1335 | /*! |
1336 | Returns the format of this framebuffer object. |
1337 | */ |
1338 | QOpenGLFramebufferObjectFormat QOpenGLFramebufferObject::format() const |
1339 | { |
1340 | Q_D(const QOpenGLFramebufferObject); |
1341 | return d->format; |
1342 | } |
1343 | |
1344 | static inline QImage qt_gl_read_framebuffer_rgba8(const QSize &size, bool include_alpha, QOpenGLContext *context) |
1345 | { |
1346 | QOpenGLFunctions *funcs = context->functions(); |
1347 | const int w = size.width(); |
1348 | const int h = size.height(); |
1349 | bool isOpenGL12orBetter = !context->isOpenGLES() && (context->format().majorVersion() >= 2 || context->format().minorVersion() >= 2); |
1350 | if (isOpenGL12orBetter) { |
1351 | QImage img(size, include_alpha ? QImage::Format_ARGB32_Premultiplied : QImage::Format_RGB32); |
1352 | funcs->glReadPixels(0, 0, w, h, GL_BGRA, GL_UNSIGNED_INT_8_8_8_8_REV, img.bits()); |
1353 | return img; |
1354 | } |
1355 | |
1356 | // For OpenGL ES stick with the byte ordered format / RGBA readback format |
1357 | // since that is the only spec mandated way. (also, skip the |
1358 | // GL_IMPLEMENTATION_COLOR_READ_FORMAT mess since there is nothing saying a |
1359 | // BGRA capable impl would return BGRA from there) |
1360 | |
1361 | QImage rgbaImage(size, include_alpha ? QImage::Format_RGBA8888_Premultiplied : QImage::Format_RGBX8888); |
1362 | funcs->glReadPixels(0, 0, w, h, GL_RGBA, GL_UNSIGNED_BYTE, rgbaImage.bits()); |
1363 | return rgbaImage; |
1364 | } |
1365 | |
1366 | static inline QImage qt_gl_read_framebuffer_rgb10a2(const QSize &size, bool include_alpha, QOpenGLContext *context) |
1367 | { |
1368 | // We assume OpenGL 1.2+ or ES 3.0+ here. |
1369 | QImage img(size, include_alpha ? QImage::Format_A2BGR30_Premultiplied : QImage::Format_BGR30); |
1370 | context->functions()->glReadPixels(0, 0, size.width(), size.height(), GL_RGBA, GL_UNSIGNED_INT_2_10_10_10_REV, img.bits()); |
1371 | return img; |
1372 | } |
1373 | |
1374 | static inline QImage qt_gl_read_framebuffer_rgba16(const QSize &size, bool include_alpha, QOpenGLContext *context) |
1375 | { |
1376 | // We assume OpenGL 1.2+ or ES 3.0+ here. |
1377 | QImage img(size, include_alpha ? QImage::Format_RGBA64_Premultiplied : QImage::Format_RGBX64); |
1378 | context->functions()->glReadPixels(0, 0, size.width(), size.height(), GL_RGBA, GL_UNSIGNED_SHORT, img.bits()); |
1379 | return img; |
1380 | } |
1381 | |
1382 | static QImage qt_gl_read_framebuffer(const QSize &size, GLenum internal_format, bool include_alpha, bool flip) |
1383 | { |
1384 | QOpenGLContext *ctx = QOpenGLContext::currentContext(); |
1385 | QOpenGLFunctions *funcs = ctx->functions(); |
1386 | while (true) { |
1387 | GLenum error = funcs->glGetError(); |
1388 | if (error == GL_NO_ERROR || error == GL_CONTEXT_LOST) |
1389 | break; |
1390 | } |
1391 | switch (internal_format) { |
1392 | case GL_RGB: |
1393 | case GL_RGB8: |
1394 | return qt_gl_read_framebuffer_rgba8(size, false, ctx).mirrored(false, flip); |
1395 | case GL_RGB10: |
1396 | return qt_gl_read_framebuffer_rgb10a2(size, false, ctx).mirrored(false, flip); |
1397 | case GL_RGB10_A2: |
1398 | return qt_gl_read_framebuffer_rgb10a2(size, include_alpha, ctx).mirrored(false, flip); |
1399 | case GL_RGB16: |
1400 | return qt_gl_read_framebuffer_rgba16(size, false, ctx).mirrored(false, flip); |
1401 | case GL_RGBA16: |
1402 | return qt_gl_read_framebuffer_rgba16(size, include_alpha, ctx).mirrored(false, flip); |
1403 | case GL_RGBA: |
1404 | case GL_RGBA8: |
1405 | default: |
1406 | return qt_gl_read_framebuffer_rgba8(size, include_alpha, ctx).mirrored(false, flip); |
1407 | } |
1408 | |
1409 | Q_UNREACHABLE(); |
1410 | return QImage(); |
1411 | } |
1412 | |
1413 | Q_OPENGL_EXPORT QImage qt_gl_read_framebuffer(const QSize &size, bool alpha_format, bool include_alpha) |
1414 | { |
1415 | return qt_gl_read_framebuffer(size, alpha_format ? GL_RGBA : GL_RGB, include_alpha, true); |
1416 | } |
1417 | |
1418 | /*! |
1419 | \fn QImage QOpenGLFramebufferObject::toImage(bool flipped) const |
1420 | |
1421 | Returns the contents of this framebuffer object as a QImage. |
1422 | |
1423 | If \a flipped is true the image is flipped from OpenGL coordinates to raster coordinates. |
1424 | If used together with QOpenGLPaintDevice, \a flipped should be the opposite of the value |
1425 | of QOpenGLPaintDevice::paintFlipped(). |
1426 | |
1427 | The returned image has a format of premultiplied ARGB32 or RGB32. The latter |
1428 | is used only when internalTextureFormat() is set to \c GL_RGB. Since Qt 5.2 |
1429 | the function will fall back to premultiplied RGBA8888 or RGBx8888 when |
1430 | reading to (A)RGB32 is not supported, and this includes OpenGL ES. Since Qt |
1431 | 5.4 an A2BGR30 image is returned if the internal format is RGB10_A2, and since |
1432 | Qt 5.12 a RGBA64 image is return if the internal format is RGBA16. |
1433 | |
1434 | If the rendering in the framebuffer was not done with premultiplied alpha in mind, |
1435 | create a wrapper QImage with a non-premultiplied format. This is necessary before |
1436 | performing operations like QImage::save() because otherwise the image data would get |
1437 | unpremultiplied, even though it was not premultiplied in the first place. To create |
1438 | such a wrapper without performing a copy of the pixel data, do the following: |
1439 | |
1440 | \code |
1441 | QImage fboImage(fbo.toImage()); |
1442 | QImage image(fboImage.constBits(), fboImage.width(), fboImage.height(), QImage::Format_ARGB32); |
1443 | \endcode |
1444 | |
1445 | For multisampled framebuffer objects the samples are resolved using the |
1446 | \c{GL_EXT_framebuffer_blit} extension. If the extension is not available, the contents |
1447 | of the returned image is undefined. |
1448 | |
1449 | For singlesampled framebuffers the contents is retrieved via \c glReadPixels. This is |
1450 | a potentially expensive and inefficient operation. Therefore it is recommended that |
1451 | this function is used as seldom as possible. |
1452 | |
1453 | \sa QOpenGLPaintDevice::paintFlipped() |
1454 | */ |
1455 | |
1456 | QImage QOpenGLFramebufferObject::toImage(bool flipped) const |
1457 | { |
1458 | return toImage(flipped, 0); |
1459 | } |
1460 | |
1461 | /*! \overload |
1462 | |
1463 | Returns the contents of the color attachment of index \a |
1464 | colorAttachmentIndex of this framebuffer object as a QImage. This method |
1465 | flips the image from OpenGL coordinates to raster coordinates when \a |
1466 | flipped is set to \c true. |
1467 | |
1468 | \note This overload is only fully functional when multiple render targets are |
1469 | supported by the OpenGL implementation. When that is not the case, only one |
1470 | color attachment will be set up. |
1471 | |
1472 | \since 5.6 |
1473 | */ |
1474 | QImage QOpenGLFramebufferObject::toImage(bool flipped, int colorAttachmentIndex) const |
1475 | { |
1476 | Q_D(const QOpenGLFramebufferObject); |
1477 | if (!d->valid) |
1478 | return QImage(); |
1479 | |
1480 | QOpenGLContext *ctx = QOpenGLContext::currentContext(); |
1481 | if (!ctx) { |
1482 | qWarning("QOpenGLFramebufferObject::toImage() called without a current context" ); |
1483 | return QImage(); |
1484 | } |
1485 | |
1486 | if (d->colorAttachments.count() <= colorAttachmentIndex) { |
1487 | qWarning("QOpenGLFramebufferObject::toImage() called for missing color attachment" ); |
1488 | return QImage(); |
1489 | } |
1490 | |
1491 | GLuint prevFbo = 0; |
1492 | ctx->functions()->glGetIntegerv(GL_FRAMEBUFFER_BINDING, (GLint *) &prevFbo); |
1493 | |
1494 | if (prevFbo != d->fbo()) |
1495 | const_cast<QOpenGLFramebufferObject *>(this)->bind(); |
1496 | |
1497 | QImage image; |
1498 | QOpenGLExtraFunctions * = ctx->extraFunctions(); |
1499 | // qt_gl_read_framebuffer doesn't work on a multisample FBO |
1500 | if (format().samples() != 0) { |
1501 | QRect rect(QPoint(0, 0), size()); |
1502 | QOpenGLFramebufferObjectFormat fmt; |
1503 | if (extraFuncs->hasOpenGLFeature(QOpenGLFunctions::MultipleRenderTargets)) { |
1504 | fmt.setInternalTextureFormat(d->colorAttachments[colorAttachmentIndex].internalFormat); |
1505 | QOpenGLFramebufferObject temp(d->colorAttachments[colorAttachmentIndex].size, fmt); |
1506 | blitFramebuffer(&temp, rect, const_cast<QOpenGLFramebufferObject *>(this), rect, |
1507 | GL_COLOR_BUFFER_BIT, GL_NEAREST, |
1508 | colorAttachmentIndex, 0); |
1509 | image = temp.toImage(flipped); |
1510 | } else { |
1511 | fmt.setInternalTextureFormat(d->colorAttachments[0].internalFormat); |
1512 | QOpenGLFramebufferObject temp(size(), fmt); |
1513 | blitFramebuffer(&temp, rect, const_cast<QOpenGLFramebufferObject *>(this), rect); |
1514 | image = temp.toImage(flipped); |
1515 | } |
1516 | } else { |
1517 | if (extraFuncs->hasOpenGLFeature(QOpenGLFunctions::MultipleRenderTargets)) { |
1518 | extraFuncs->glReadBuffer(GL_COLOR_ATTACHMENT0 + colorAttachmentIndex); |
1519 | image = qt_gl_read_framebuffer(d->colorAttachments[colorAttachmentIndex].size, |
1520 | d->colorAttachments[colorAttachmentIndex].internalFormat, |
1521 | true, flipped); |
1522 | extraFuncs->glReadBuffer(GL_COLOR_ATTACHMENT0); |
1523 | } else { |
1524 | image = qt_gl_read_framebuffer(d->colorAttachments[0].size, |
1525 | d->colorAttachments[0].internalFormat, |
1526 | true, flipped); |
1527 | } |
1528 | } |
1529 | |
1530 | if (prevFbo != d->fbo()) |
1531 | ctx->functions()->glBindFramebuffer(GL_FRAMEBUFFER, prevFbo); |
1532 | |
1533 | return image; |
1534 | } |
1535 | |
1536 | /*! |
1537 | \fn bool QOpenGLFramebufferObject::bindDefault() |
1538 | |
1539 | Switches rendering back to the default, windowing system provided |
1540 | framebuffer. |
1541 | Returns \c true upon success, false otherwise. |
1542 | |
1543 | \sa bind(), release() |
1544 | */ |
1545 | bool QOpenGLFramebufferObject::bindDefault() |
1546 | { |
1547 | QOpenGLContext *ctx = const_cast<QOpenGLContext *>(QOpenGLContext::currentContext()); |
1548 | |
1549 | if (ctx) { |
1550 | ctx->functions()->glBindFramebuffer(GL_FRAMEBUFFER, ctx->defaultFramebufferObject()); |
1551 | QOpenGLContextPrivate::get(ctx)->qgl_current_fbo_invalid = true; |
1552 | QOpenGLContextPrivate::get(ctx)->qgl_current_fbo = nullptr; |
1553 | } |
1554 | #ifdef QT_DEBUG |
1555 | else |
1556 | qWarning("QOpenGLFramebufferObject::bindDefault() called without current context." ); |
1557 | #endif |
1558 | |
1559 | return ctx != nullptr; |
1560 | } |
1561 | |
1562 | /*! |
1563 | \fn bool QOpenGLFramebufferObject::hasOpenGLFramebufferObjects() |
1564 | |
1565 | Returns \c true if the OpenGL \c{GL_EXT_framebuffer_object} extension |
1566 | is present on this system; otherwise returns \c false. |
1567 | */ |
1568 | bool QOpenGLFramebufferObject::hasOpenGLFramebufferObjects() |
1569 | { |
1570 | return QOpenGLContext::currentContext()->functions()->hasOpenGLFeature(QOpenGLFunctions::Framebuffers); |
1571 | } |
1572 | |
1573 | /*! |
1574 | \fn GLuint QOpenGLFramebufferObject::handle() const |
1575 | |
1576 | Returns the OpenGL framebuffer object handle for this framebuffer |
1577 | object (returned by the \c{glGenFrameBuffersEXT()} function). This |
1578 | handle can be used to attach new images or buffers to the |
1579 | framebuffer. The user is responsible for cleaning up and |
1580 | destroying these objects. |
1581 | */ |
1582 | GLuint QOpenGLFramebufferObject::handle() const |
1583 | { |
1584 | Q_D(const QOpenGLFramebufferObject); |
1585 | return d->fbo(); |
1586 | } |
1587 | |
1588 | /*! |
1589 | Returns the status of the depth and stencil buffers attached to |
1590 | this framebuffer object. |
1591 | */ |
1592 | |
1593 | QOpenGLFramebufferObject::Attachment QOpenGLFramebufferObject::attachment() const |
1594 | { |
1595 | Q_D(const QOpenGLFramebufferObject); |
1596 | if (d->valid) |
1597 | return d->fbo_attachment; |
1598 | return NoAttachment; |
1599 | } |
1600 | |
1601 | /*! |
1602 | Sets the attachments of the framebuffer object to \a attachment. |
1603 | |
1604 | This can be used to free or reattach the depth and stencil buffer |
1605 | attachments as needed. |
1606 | |
1607 | \note This function alters the current framebuffer binding. |
1608 | */ |
1609 | void QOpenGLFramebufferObject::setAttachment(QOpenGLFramebufferObject::Attachment attachment) |
1610 | { |
1611 | Q_D(QOpenGLFramebufferObject); |
1612 | if (attachment == d->fbo_attachment || !isValid()) |
1613 | return; |
1614 | QOpenGLContext *current = QOpenGLContext::currentContext(); |
1615 | if (!current) |
1616 | return; |
1617 | #ifdef QT_DEBUG |
1618 | if (current->shareGroup() != d->fbo_guard->group()) |
1619 | qWarning("QOpenGLFramebufferObject::setAttachment() called from incompatible context" ); |
1620 | #endif |
1621 | d->funcs.glBindFramebuffer(GL_FRAMEBUFFER, d->fbo()); |
1622 | QOpenGLContextPrivate::get(current)->qgl_current_fbo_invalid = true; |
1623 | d->initDepthStencilAttachments(current, attachment); |
1624 | } |
1625 | |
1626 | /*! |
1627 | Returns \c true if the framebuffer object is currently bound to the current context, |
1628 | otherwise false is returned. |
1629 | */ |
1630 | bool QOpenGLFramebufferObject::isBound() const |
1631 | { |
1632 | Q_D(const QOpenGLFramebufferObject); |
1633 | QOpenGLContext *ctx = QOpenGLContext::currentContext(); |
1634 | if (!ctx) |
1635 | return false; |
1636 | GLint fbo = 0; |
1637 | ctx->functions()->glGetIntegerv(GL_FRAMEBUFFER_BINDING, &fbo); |
1638 | return GLuint(fbo) == d->fbo(); |
1639 | } |
1640 | |
1641 | /*! |
1642 | \fn bool QOpenGLFramebufferObject::hasOpenGLFramebufferBlit() |
1643 | |
1644 | Returns \c true if the OpenGL \c{GL_EXT_framebuffer_blit} extension |
1645 | is present on this system; otherwise returns \c false. |
1646 | |
1647 | \sa blitFramebuffer() |
1648 | */ |
1649 | bool QOpenGLFramebufferObject::hasOpenGLFramebufferBlit() |
1650 | { |
1651 | return QOpenGLExtensions(QOpenGLContext::currentContext()).hasOpenGLExtension(QOpenGLExtensions::FramebufferBlit); |
1652 | } |
1653 | |
1654 | |
1655 | /*! |
1656 | \overload |
1657 | |
1658 | Convenience overload to blit between two framebuffer objects. |
1659 | */ |
1660 | void QOpenGLFramebufferObject::blitFramebuffer(QOpenGLFramebufferObject *target, |
1661 | QOpenGLFramebufferObject *source, |
1662 | GLbitfield buffers, GLenum filter) |
1663 | { |
1664 | if (!target && !source) |
1665 | return; |
1666 | |
1667 | QSize targetSize; |
1668 | QSize sourceSize; |
1669 | |
1670 | if (target) |
1671 | targetSize = target->size(); |
1672 | if (source) |
1673 | sourceSize = source->size(); |
1674 | |
1675 | if (targetSize.isEmpty()) |
1676 | targetSize = sourceSize; |
1677 | else if (sourceSize.isEmpty()) |
1678 | sourceSize = targetSize; |
1679 | |
1680 | blitFramebuffer(target, QRect(QPoint(0, 0), targetSize), |
1681 | source, QRect(QPoint(0, 0), sourceSize), |
1682 | buffers, filter); |
1683 | } |
1684 | |
1685 | /*! \overload |
1686 | * |
1687 | Convenience overload to blit between two framebuffer objects. |
1688 | */ |
1689 | void QOpenGLFramebufferObject::blitFramebuffer(QOpenGLFramebufferObject *target, const QRect &targetRect, |
1690 | QOpenGLFramebufferObject *source, const QRect &sourceRect, |
1691 | GLbitfield buffers, |
1692 | GLenum filter) |
1693 | { |
1694 | blitFramebuffer(target, targetRect, source, sourceRect, buffers, filter, 0, 0); |
1695 | } |
1696 | |
1697 | /*! |
1698 | \enum QOpenGLFramebufferObject::FramebufferRestorePolicy |
1699 | \since 5.7 |
1700 | |
1701 | This enum type is used to configure the behavior related to restoring |
1702 | framebuffer bindings when calling blitFramebuffer(). |
1703 | |
1704 | \value DontRestoreFramebufferBinding Do not restore the previous framebuffer binding. |
1705 | The caller is responsible for tracking and setting |
1706 | the framebuffer binding as needed. |
1707 | |
1708 | \value RestoreFramebufferBindingToDefault After the blit operation, bind the default |
1709 | framebuffer. |
1710 | |
1711 | \value RestoreFrameBufferBinding Restore the previously bound framebuffer. This is |
1712 | potentially expensive because of the need to |
1713 | query the currently bound framebuffer. |
1714 | |
1715 | \sa blitFramebuffer() |
1716 | */ |
1717 | |
1718 | /*! |
1719 | \since 5.7 |
1720 | |
1721 | Blits from the \a sourceRect rectangle in the \a source framebuffer |
1722 | object to the \a targetRect rectangle in the \a target framebuffer object. |
1723 | |
1724 | If \a source or \a target is 0, the default framebuffer will be used |
1725 | instead of a framebuffer object as source or target respectively. |
1726 | |
1727 | This function will have no effect unless hasOpenGLFramebufferBlit() returns |
1728 | true. |
1729 | |
1730 | The \a buffers parameter should be a mask consisting of any combination of |
1731 | \c GL_COLOR_BUFFER_BIT, \c GL_DEPTH_BUFFER_BIT, and |
1732 | \c GL_STENCIL_BUFFER_BIT. Any buffer type that is not present both |
1733 | in the source and target buffers is ignored. |
1734 | |
1735 | The \a sourceRect and \a targetRect rectangles may have different sizes; |
1736 | in this case \a buffers should not contain \c GL_DEPTH_BUFFER_BIT or |
1737 | \c GL_STENCIL_BUFFER_BIT. The \a filter parameter should be set to |
1738 | \c GL_LINEAR or \c GL_NEAREST, and specifies whether linear or nearest |
1739 | interpolation should be used when scaling is performed. |
1740 | |
1741 | If \a source equals \a target a copy is performed within the same buffer. |
1742 | Results are undefined if the source and target rectangles overlap and |
1743 | have different sizes. The sizes must also be the same if any of the |
1744 | framebuffer objects are multisample framebuffers. |
1745 | |
1746 | \note The scissor test will restrict the blit area if enabled. |
1747 | |
1748 | When multiple render targets are in use, \a readColorAttachmentIndex and \a |
1749 | drawColorAttachmentIndex specify the index of the color attachments in the |
1750 | source and destination framebuffers. |
1751 | |
1752 | The \a restorePolicy determines if the framebuffer that was bound prior to |
1753 | calling this function should be restored, or if the default framebuffer |
1754 | should be bound before returning, of if the caller is responsible for |
1755 | tracking and setting the bound framebuffer. Restoring the previous |
1756 | framebuffer can be relatively expensive due to the call to \c{glGetIntegerv} |
1757 | which on some OpenGL drivers may imply a pipeline stall. |
1758 | |
1759 | \sa hasOpenGLFramebufferBlit() |
1760 | */ |
1761 | void QOpenGLFramebufferObject::blitFramebuffer(QOpenGLFramebufferObject *target, const QRect &targetRect, |
1762 | QOpenGLFramebufferObject *source, const QRect &sourceRect, |
1763 | GLbitfield buffers, |
1764 | GLenum filter, |
1765 | int readColorAttachmentIndex, |
1766 | int drawColorAttachmentIndex, |
1767 | QOpenGLFramebufferObject::FramebufferRestorePolicy restorePolicy) |
1768 | { |
1769 | QOpenGLContext *ctx = QOpenGLContext::currentContext(); |
1770 | if (!ctx) |
1771 | return; |
1772 | |
1773 | QOpenGLExtensions extensions(ctx); |
1774 | if (!extensions.hasOpenGLExtension(QOpenGLExtensions::FramebufferBlit)) |
1775 | return; |
1776 | |
1777 | GLuint prevFbo = 0; |
1778 | if (restorePolicy == RestoreFrameBufferBinding) |
1779 | ctx->functions()->glGetIntegerv(GL_FRAMEBUFFER_BINDING, (GLint *) &prevFbo); |
1780 | |
1781 | const int sx0 = sourceRect.left(); |
1782 | const int sx1 = sourceRect.left() + sourceRect.width(); |
1783 | const int sy0 = sourceRect.top(); |
1784 | const int sy1 = sourceRect.top() + sourceRect.height(); |
1785 | |
1786 | const int tx0 = targetRect.left(); |
1787 | const int tx1 = targetRect.left() + targetRect.width(); |
1788 | const int ty0 = targetRect.top(); |
1789 | const int ty1 = targetRect.top() + targetRect.height(); |
1790 | |
1791 | const GLuint defaultFboId = ctx->defaultFramebufferObject(); |
1792 | |
1793 | extensions.glBindFramebuffer(GL_READ_FRAMEBUFFER, source ? source->handle() : defaultFboId); |
1794 | extensions.glBindFramebuffer(GL_DRAW_FRAMEBUFFER, target ? target->handle() : defaultFboId); |
1795 | |
1796 | const bool supportsMRT = extensions.hasOpenGLFeature(QOpenGLFunctions::MultipleRenderTargets); |
1797 | if (supportsMRT) { |
1798 | extensions.glReadBuffer(GL_COLOR_ATTACHMENT0 + readColorAttachmentIndex); |
1799 | if (target) { |
1800 | GLenum drawBuf = GL_COLOR_ATTACHMENT0 + drawColorAttachmentIndex; |
1801 | extensions.glDrawBuffers(1, &drawBuf); |
1802 | } |
1803 | } |
1804 | |
1805 | extensions.glBlitFramebuffer(sx0, sy0, sx1, sy1, |
1806 | tx0, ty0, tx1, ty1, |
1807 | buffers, filter); |
1808 | |
1809 | if (supportsMRT) |
1810 | extensions.glReadBuffer(GL_COLOR_ATTACHMENT0); |
1811 | |
1812 | switch (restorePolicy) { |
1813 | case RestoreFrameBufferBinding: |
1814 | ctx->functions()->glBindFramebuffer(GL_FRAMEBUFFER, prevFbo); // sets both READ and DRAW |
1815 | break; |
1816 | |
1817 | case RestoreFramebufferBindingToDefault: |
1818 | ctx->functions()->glBindFramebuffer(GL_FRAMEBUFFER, ctx->defaultFramebufferObject()); // sets both READ and DRAW |
1819 | break; |
1820 | |
1821 | case DontRestoreFramebufferBinding: |
1822 | break; |
1823 | } |
1824 | } |
1825 | |
1826 | /*! |
1827 | \overload |
1828 | |
1829 | Convenience overload to blit between two framebuffer objects and |
1830 | to restore the previous framebuffer binding. Equivalent to calling |
1831 | blitFramebuffer(target, targetRect, source, sourceRect, buffers, filter, |
1832 | readColorAttachmentIndex, drawColorAttachmentIndex, |
1833 | RestoreFrameBufferBinding). |
1834 | */ |
1835 | void QOpenGLFramebufferObject::blitFramebuffer(QOpenGLFramebufferObject *target, const QRect &targetRect, |
1836 | QOpenGLFramebufferObject *source, const QRect &sourceRect, |
1837 | GLbitfield buffers, |
1838 | GLenum filter, |
1839 | int readColorAttachmentIndex, |
1840 | int drawColorAttachmentIndex) |
1841 | { |
1842 | blitFramebuffer(target, targetRect, source, sourceRect, |
1843 | buffers, filter, |
1844 | readColorAttachmentIndex, |
1845 | drawColorAttachmentIndex, |
1846 | RestoreFrameBufferBinding); |
1847 | } |
1848 | |
1849 | QT_END_NAMESPACE |
1850 | |