Qt
Internal/Contributor docs for the Qt SDK. Note: These are NOT official API docs; those are found at https://doc.qt.io/
Loading...
Searching...
No Matches
qopenglvertexarrayobject.cpp
Go to the documentation of this file.
1// Copyright (C) 2014 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com, author Sean Harmer <sean.harmer@kdab.com>
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
3// Qt-Security score:significant reason:default
4
6
7#include <QtCore/private/qobject_p.h>
8#include <QtCore/qthread.h>
9#include <QtGui/qopenglcontext.h>
10#include <QtGui/qoffscreensurface.h>
11#include <QtGui/qguiapplication.h>
12
13#include <QtOpenGL/QOpenGLVersionFunctionsFactory>
14
15#if !QT_CONFIG(opengles2)
16# include <QtOpenGL/qopenglfunctions_3_0.h>
17# include <QtOpenGL/qopenglfunctions_3_2_core.h>
18#endif
19
20#include <private/qopenglcontext_p.h>
21#include <private/qopenglextensions_p.h>
22#include <private/qopenglvertexarrayobject_p.h>
23
25
26class QOpenGLFunctions_3_0;
27class QOpenGLFunctions_3_2_Core;
28
29static void vertexArrayObjectHelperDestroyCallback(QOpenGLVertexArrayObjectHelper *vaoHelper)
30{
31 delete vaoHelper;
32}
33
34QOpenGLVertexArrayObjectHelper *QOpenGLVertexArrayObjectHelper::vertexArrayObjectHelperForContext(QOpenGLContext *context)
35{
36 Q_ASSERT(context);
37
38 auto contextPrivate = QOpenGLContextPrivate::get(context);
39 auto &vaoHelper = contextPrivate->vaoHelper;
40
41 if (!vaoHelper) {
42 vaoHelper = new QOpenGLVertexArrayObjectHelper(context);
43 contextPrivate->vaoHelperDestroyCallback = &vertexArrayObjectHelperDestroyCallback;
44 }
45
46 return vaoHelper;
47}
48
49void QOpenGLVertexArrayObjectHelper::initializeFromContext(QOpenGLContext *context)
50{
51 Q_ASSERT(context);
52
53 bool tryARB = true;
54
55 if (context->isOpenGLES()) {
56 if (context->format().majorVersion() >= 3) {
57 QOpenGLExtraFunctionsPrivate *extra = static_cast<QOpenGLExtensions *>(context->extraFunctions())->d();
58 GenVertexArrays = extra->f.GenVertexArrays;
59 DeleteVertexArrays = extra->f.DeleteVertexArrays;
60 BindVertexArray = extra->f.BindVertexArray;
61 IsVertexArray = extra->f.IsVertexArray;
62 tryARB = false;
63 } else if (context->hasExtension(QByteArrayLiteral("GL_OES_vertex_array_object"))) {
64 GenVertexArrays = reinterpret_cast<QOpenGLVertexArrayObjectHelper::qt_GenVertexArrays_t>(context->getProcAddress("glGenVertexArraysOES"));
65 DeleteVertexArrays = reinterpret_cast<QOpenGLVertexArrayObjectHelper::qt_DeleteVertexArrays_t>(context->getProcAddress("glDeleteVertexArraysOES"));
66 BindVertexArray = reinterpret_cast<QOpenGLVertexArrayObjectHelper::qt_BindVertexArray_t>(context->getProcAddress("glBindVertexArrayOES"));
67 IsVertexArray = reinterpret_cast<QOpenGLVertexArrayObjectHelper::qt_IsVertexArray_t>(context->getProcAddress("glIsVertexArrayOES"));
68 tryARB = false;
69 }
70 } else if (context->hasExtension(QByteArrayLiteral("GL_APPLE_vertex_array_object")) &&
71 !context->hasExtension(QByteArrayLiteral("GL_ARB_vertex_array_object"))) {
72 GenVertexArrays = reinterpret_cast<QOpenGLVertexArrayObjectHelper::qt_GenVertexArrays_t>(context->getProcAddress("glGenVertexArraysAPPLE"));
73 DeleteVertexArrays = reinterpret_cast<QOpenGLVertexArrayObjectHelper::qt_DeleteVertexArrays_t>(context->getProcAddress("glDeleteVertexArraysAPPLE"));
74 BindVertexArray = reinterpret_cast<QOpenGLVertexArrayObjectHelper::qt_BindVertexArray_t>(context->getProcAddress("glBindVertexArrayAPPLE"));
75 IsVertexArray = reinterpret_cast<QOpenGLVertexArrayObjectHelper::qt_IsVertexArray_t>(context->getProcAddress("glIsVertexArrayAPPLE"));
76 tryARB = false;
77 }
78
79 if (tryARB && context->hasExtension(QByteArrayLiteral("GL_ARB_vertex_array_object"))) {
80 GenVertexArrays = reinterpret_cast<QOpenGLVertexArrayObjectHelper::qt_GenVertexArrays_t>(context->getProcAddress("glGenVertexArrays"));
81 DeleteVertexArrays = reinterpret_cast<QOpenGLVertexArrayObjectHelper::qt_DeleteVertexArrays_t>(context->getProcAddress("glDeleteVertexArrays"));
82 BindVertexArray = reinterpret_cast<QOpenGLVertexArrayObjectHelper::qt_BindVertexArray_t>(context->getProcAddress("glBindVertexArray"));
83 IsVertexArray = reinterpret_cast<QOpenGLVertexArrayObjectHelper::qt_IsVertexArray_t>(context->getProcAddress("glIsVertexArray"));
84 }
85}
86
88{
89public:
91 : vao(0)
92 , vaoFuncsType(NotSupported)
93 , context(nullptr)
94 , guiThread(nullptr)
95 {
96 }
97
98 bool create();
99 void destroy();
100 void bind();
101 void release();
103
104 Q_DECLARE_PUBLIC(QOpenGLVertexArrayObject)
105
107
108 union {
109 QOpenGLFunctions_3_0 *core_3_0;
110 QOpenGLFunctions_3_2_Core *core_3_2;
112 } vaoFuncs;
113 enum {
120 } vaoFuncsType;
121
124};
125
127{
128 if (vao) {
129 qWarning("QOpenGLVertexArrayObject::create() VAO is already created");
130 return false;
131 }
132
133 Q_Q(QOpenGLVertexArrayObject);
134
135 QOpenGLContext *ctx = QOpenGLContext::currentContext();
136 if (!ctx) {
137 qWarning("QOpenGLVertexArrayObject::create() requires a valid current OpenGL context");
138 return false;
139 }
140
141 //Fail early, if context is the same as ctx, it means we have tried to initialize for this context and failed
142 if (ctx == context)
143 return false;
144
145 context = ctx;
146 QObject::connect(context, SIGNAL(aboutToBeDestroyed()), q, SLOT(_q_contextAboutToBeDestroyed()));
147
148 guiThread = qGuiApp->thread();
149
150 if (ctx->isOpenGLES()) {
151 if (ctx->format().majorVersion() >= 3 || ctx->hasExtension(QByteArrayLiteral("GL_OES_vertex_array_object"))) {
152 vaoFuncs.helper = QOpenGLVertexArrayObjectHelper::vertexArrayObjectHelperForContext(ctx);
153 vaoFuncsType = OES;
154 vaoFuncs.helper->glGenVertexArrays(1, &vao);
155 }
156 } else {
157 vaoFuncs.core_3_0 = nullptr;
158 vaoFuncsType = NotSupported;
159 QSurfaceFormat format = ctx->format();
160#if !QT_CONFIG(opengles2)
161 if (format.version() >= std::pair(3,2)) {
162 vaoFuncs.core_3_2 = QOpenGLVersionFunctionsFactory::get<QOpenGLFunctions_3_2_Core>(ctx);
163 vaoFuncsType = Core_3_2;
164 vaoFuncs.core_3_2->glGenVertexArrays(1, &vao);
165 } else if (format.majorVersion() >= 3) {
166 vaoFuncs.core_3_0 = QOpenGLVersionFunctionsFactory::get<QOpenGLFunctions_3_0>(ctx);
167 vaoFuncsType = Core_3_0;
168 vaoFuncs.core_3_0->glGenVertexArrays(1, &vao);
169 } else
170#endif
171 if (ctx->hasExtension(QByteArrayLiteral("GL_ARB_vertex_array_object"))) {
172 vaoFuncs.helper = QOpenGLVertexArrayObjectHelper::vertexArrayObjectHelperForContext(ctx);
173 vaoFuncsType = ARB;
174 vaoFuncs.helper->glGenVertexArrays(1, &vao);
175 } else if (ctx->hasExtension(QByteArrayLiteral("GL_APPLE_vertex_array_object"))) {
176 vaoFuncs.helper = QOpenGLVertexArrayObjectHelper::vertexArrayObjectHelperForContext(ctx);
177 vaoFuncsType = APPLE;
178 vaoFuncs.helper->glGenVertexArrays(1, &vao);
179 }
180 }
181
182 return (vao != 0);
183}
184
186{
187 Q_Q(QOpenGLVertexArrayObject);
188
189 QOpenGLContext *ctx = QOpenGLContext::currentContext();
190 QOpenGLContext *oldContext = nullptr;
191 QSurface *oldContextSurface = nullptr;
192 QScopedPointer<QOffscreenSurface> offscreenSurface;
193 if (context && context != ctx) {
194 oldContext = ctx;
195 oldContextSurface = ctx ? ctx->surface() : nullptr;
196 // Before going through the effort of creating an offscreen surface
197 // check that we are on the GUI thread because otherwise many platforms
198 // will not able to create that offscreen surface.
199 if (QThread::currentThread() != guiThread) {
200 ctx = nullptr;
201 } else {
202 // Cannot just make the current surface current again with another context.
203 // The format may be incompatible and some platforms (iOS) may impose
204 // restrictions on using a window with different contexts. Create an
205 // offscreen surface (a pbuffer or a hidden window) instead to be safe.
206 offscreenSurface.reset(new QOffscreenSurface);
207 offscreenSurface->setFormat(context->format());
208 offscreenSurface->create();
209 if (context->makeCurrent(offscreenSurface.data())) {
210 ctx = context;
211 } else {
212 qWarning("QOpenGLVertexArrayObject::destroy() failed to make VAO's context current");
213 ctx = nullptr;
214 }
215 }
216 }
217
218 if (context) {
219 QObject::disconnect(context, SIGNAL(aboutToBeDestroyed()), q, SLOT(_q_contextAboutToBeDestroyed()));
220 context = nullptr;
221 }
222
223 if (vao && ctx) {
224 switch (vaoFuncsType) {
225#if !QT_CONFIG(opengles2)
226 case Core_3_2:
227 vaoFuncs.core_3_2->glDeleteVertexArrays(1, &vao);
228 break;
229 case Core_3_0:
230 vaoFuncs.core_3_0->glDeleteVertexArrays(1, &vao);
231 break;
232#endif
233 case ARB:
234 case APPLE:
235 case OES:
236 vaoFuncs.helper->glDeleteVertexArrays(1, &vao);
237 break;
238 default:
239 break;
240 }
241
242 vao = 0;
243 }
244
245 if (oldContext && oldContextSurface && oldContextSurface->surfaceHandle()) {
246 if (!oldContext->makeCurrent(oldContextSurface))
247 qWarning("QOpenGLVertexArrayObject::destroy() failed to restore current context");
248 }
249}
250
251/*!
252 \internal
253*/
258
260{
261 switch (vaoFuncsType) {
262#if !QT_CONFIG(opengles2)
263 case Core_3_2:
264 vaoFuncs.core_3_2->glBindVertexArray(vao);
265 break;
266 case Core_3_0:
267 vaoFuncs.core_3_0->glBindVertexArray(vao);
268 break;
269#endif
270 case ARB:
271 case APPLE:
272 case OES:
273 vaoFuncs.helper->glBindVertexArray(vao);
274 break;
275 default:
276 break;
277 }
278}
279
281{
282 switch (vaoFuncsType) {
283#if !QT_CONFIG(opengles2)
284 case Core_3_2:
285 vaoFuncs.core_3_2->glBindVertexArray(0);
286 break;
287 case Core_3_0:
288 vaoFuncs.core_3_0->glBindVertexArray(0);
289 break;
290#endif
291 case ARB:
292 case APPLE:
293 case OES:
294 vaoFuncs.helper->glBindVertexArray(0);
295 break;
296 default:
297 break;
298 }
299}
300
301
302/*!
303 \class QOpenGLVertexArrayObject
304 \brief The QOpenGLVertexArrayObject class wraps an OpenGL Vertex Array Object.
305 \inmodule QtOpenGL
306 \since 5.1
307 \ingroup painting-3D
308
309 A Vertex Array Object (VAO) is an OpenGL container object that encapsulates
310 the state needed to specify per-vertex attribute data to the OpenGL pipeline.
311 To put it another way, a VAO remembers the states of buffer objects (see
312 QOpenGLBuffer) and their associated state (e.g. vertex attribute divisors).
313 This allows a very easy and efficient method of switching between OpenGL buffer
314 states for rendering different "objects" in a scene. The QOpenGLVertexArrayObject
315 class is a thin wrapper around an OpenGL VAO.
316
317 For the desktop, VAOs are supported as a core feature in OpenGL 3.0 or newer and by the
318 GL_ARB_vertex_array_object for older versions. On OpenGL ES 2, VAOs are provided by
319 the optional GL_OES_vertex_array_object extension. You can check the version of
320 OpenGL with QOpenGLContext::surfaceFormat() and check for the presence of extensions
321 with QOpenGLContext::hasExtension().
322
323 As with the other Qt OpenGL classes, QOpenGLVertexArrayObject has a create()
324 function to create the underlying OpenGL object. This is to allow the developer to
325 ensure that there is a valid current OpenGL context at the time.
326
327 Once you have successfully created a VAO the typical usage pattern is:
328
329 \list
330 \li In scene initialization function, for each visual object:
331 \list
332 \li Bind the VAO
333 \li Set vertex data state for this visual object (vertices, normals, texture coordinates etc.)
334 \li Unbind (release()) the VAO
335 \endlist
336 \li In render function, for each visual object:
337 \list
338 \li Bind the VAO (and shader program if needed)
339 \li Call a glDraw*() function
340 \li Unbind (release()) the VAO
341 \endlist
342 \endlist
343
344 The act of binding the VAO in the render function has the effect of restoring
345 all of the vertex data state setup in the initialization phase. In this way we can
346 set a great deal of state when setting up a VAO and efficiently switch between
347 state sets of objects to be rendered. Using VAOs also allows the OpenGL driver
348 to amortise the validation checks of the vertex data.
349
350 \note Vertex Array Objects, like all other OpenGL container objects, are specific
351 to the context for which they were created and cannot be shared amongst a
352 context group.
353
354 \sa QOpenGLVertexArrayObject::Binder, QOpenGLBuffer
355*/
356
357/*!
358 Creates a QOpenGLVertexArrayObject with the given \a parent. You must call create()
359 with a valid OpenGL context before using.
360*/
361QOpenGLVertexArrayObject::QOpenGLVertexArrayObject(QObject* parent)
362 : QObject(*new QOpenGLVertexArrayObjectPrivate, parent)
363{
364}
365
366/*!
367 \internal
368*/
369QOpenGLVertexArrayObject::QOpenGLVertexArrayObject(QOpenGLVertexArrayObjectPrivate &dd)
370 : QObject(dd)
371{
372}
373
374/*!
375 Destroys the QOpenGLVertexArrayObject and the underlying OpenGL resource.
376*/
377QOpenGLVertexArrayObject::~QOpenGLVertexArrayObject()
378{
379 destroy();
380}
381
382/*!
383 Creates the underlying OpenGL vertex array object. There must be a valid OpenGL context
384 that supports vertex array objects current for this function to succeed.
385
386 Returns \c true if the OpenGL vertex array object was successfully created.
387
388 When the return value is \c false, vertex array object support is not available. This
389 is not an error: on systems with OpenGL 2.x or OpenGL ES 2.0 vertex array objects may
390 not be supported. The application is free to continue execution in this case, but it
391 then has to be prepared to operate in a VAO-less manner too. This means that instead
392 of merely calling bind(), the value of isCreated() must be checked and the vertex
393 arrays has to be initialized in the traditional way when there is no vertex array
394 object present.
395
396 \sa isCreated()
397*/
398bool QOpenGLVertexArrayObject::create()
399{
400 Q_D(QOpenGLVertexArrayObject);
401 return d->create();
402}
403
404/*!
405 Destroys the underlying OpenGL vertex array object. There must be a valid OpenGL context
406 that supports vertex array objects current for this function to succeed.
407*/
408void QOpenGLVertexArrayObject::destroy()
409{
410 Q_D(QOpenGLVertexArrayObject);
411 d->destroy();
412}
413
414/*!
415 Returns \c true is the underlying OpenGL vertex array object has been created. If this
416 returns \c true and the associated OpenGL context is current, then you are able to bind()
417 this object.
418*/
419bool QOpenGLVertexArrayObject::isCreated() const
420{
421 Q_D(const QOpenGLVertexArrayObject);
422 return (d->vao != 0);
423}
424
425/*!
426 Returns the id of the underlying OpenGL vertex array object.
427*/
428GLuint QOpenGLVertexArrayObject::objectId() const
429{
430 Q_D(const QOpenGLVertexArrayObject);
431 return d->vao;
432}
433
434/*!
435 Binds this vertex array object to the OpenGL binding point. From this point on
436 and until release() is called or another vertex array object is bound, any
437 modifications made to vertex data state are stored inside this vertex array object.
438
439 If another vertex array object is then bound you can later restore the set of
440 state associated with this object by calling bind() on this object once again.
441 This allows efficient changes between vertex data states in rendering functions.
442*/
443void QOpenGLVertexArrayObject::bind()
444{
445 Q_D(QOpenGLVertexArrayObject);
446 d->bind();
447}
448
449/*!
450 Unbinds this vertex array object by binding the default vertex array object (id = 0).
451*/
452void QOpenGLVertexArrayObject::release()
453{
454 Q_D(QOpenGLVertexArrayObject);
455 d->release();
456}
457
458
459/*!
460 \class QOpenGLVertexArrayObject::Binder
461 \brief The QOpenGLVertexArrayObject::Binder class is a convenience class to help
462 with the binding and releasing of OpenGL Vertex Array Objects.
463 \inmodule QtOpenGL
464 \reentrant
465 \since 5.1
466 \ingroup painting-3D
467
468 QOpenGLVertexArrayObject::Binder is a simple convenience class that can be used
469 to assist with the binding and releasing of QOpenGLVertexArrayObject instances.
470 This class is to QOpenGLVertexArrayObject as QMutexLocker is to QMutex.
471
472 This class implements the RAII principle which helps to ensure behavior in
473 complex code or in the presence of exceptions.
474
475 The constructor of this class accepts a QOpenGLVertexArrayObject (VAO) as an
476 argument and attempts to bind the VAO, calling QOpenGLVertexArrayObject::create()
477 if necessary. The destructor of this class calls QOpenGLVertexArrayObject::release()
478 which unbinds the VAO.
479
480 If needed the VAO can be temporarily unbound with the release() function and bound
481 once more with rebind().
482
483 \sa QOpenGLVertexArrayObject
484*/
485
486/*!
487 \fn QOpenGLVertexArrayObject::Binder::Binder(QOpenGLVertexArrayObject *v)
488
489 Creates a QOpenGLVertexArrayObject::Binder object and binds \a v by calling
490 QOpenGLVertexArrayObject::bind(). If necessary it first calls
491 QOpenGLVertexArrayObject::create().
492*/
493
494/*!
495 \fn QOpenGLVertexArrayObject::Binder::~Binder()
496
497 Destroys the QOpenGLVertexArrayObject::Binder and releases the associated vertex array object.
498*/
499
500/*!
501 \fn QOpenGLVertexArrayObject::Binder::release()
502
503 Can be used to temporarily release the associated vertex array object.
504
505 \sa rebind()
506*/
507
508/*!
509 \fn QOpenGLVertexArrayObject::Binder::rebind()
510
511 Can be used to rebind the associated vertex array object.
512
513 \sa release()
514*/
515
516QT_END_NAMESPACE
517
518#include "moc_qopenglvertexarrayobject.cpp"
QOpenGLVertexArrayObjectHelper * helper
Combined button and popup list for selecting options.
static void vertexArrayObjectHelperDestroyCallback(QOpenGLVertexArrayObjectHelper *vaoHelper)