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