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