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
qopenglbuffer.cpp
Go to the documentation of this file.
1// Copyright (C) 2016 The Qt Company Ltd.
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
5#include <QtGui/qopengl.h>
6#include <QtGui/private/qopenglcontext_p.h>
7#include <QtCore/qatomic.h>
9#include <private/qopenglextensions_p.h>
10
11#ifndef GL_CONTEXT_LOST
12#define GL_CONTEXT_LOST 0x0507
13#endif
14
15QT_BEGIN_NAMESPACE
16
17/*!
18 \class QOpenGLBuffer
19 \brief The QOpenGLBuffer class provides functions for creating and managing OpenGL buffer objects.
20 \since 5.0
21 \ingroup painting-3D
22 \inmodule QtOpenGL
23
24 Buffer objects are created in the OpenGL server so that the
25 client application can avoid uploading vertices, indices,
26 texture image data, etc every time they are needed.
27
28 QOpenGLBuffer objects can be copied around as a reference to the
29 underlying OpenGL buffer object:
30
31 \snippet code/src_gui_opengl_qopenglbuffer.cpp 0
32
33 QOpenGLBuffer performs a shallow copy when objects are copied in this
34 manner, but does not implement copy-on-write semantics. The original
35 object will be affected whenever the copy is modified.
36*/
37
38/*!
39 \enum QOpenGLBuffer::Type
40 This enum defines the type of OpenGL buffer object to create with QOpenGLBuffer.
41
42 \value VertexBuffer Vertex buffer object for use when specifying
43 vertex arrays.
44 \value IndexBuffer Index buffer object for use with \c{glDrawElements()}.
45 \value PixelPackBuffer Pixel pack buffer object for reading pixel
46 data from the OpenGL server (for example, with \c{glReadPixels()}).
47 Not supported under OpenGL/ES.
48 \value PixelUnpackBuffer Pixel unpack buffer object for writing pixel
49 data to the OpenGL server (for example, with \c{glTexImage2D()}).
50 Not supported under OpenGL/ES.
51*/
52
53/*!
54 \enum QOpenGLBuffer::UsagePattern
55 This enum defines the usage pattern of a QOpenGLBuffer object.
56
57 \value StreamDraw The data will be set once and used a few times
58 for drawing operations. Under OpenGL/ES 1.1 this is identical
59 to StaticDraw.
60 \value StreamRead The data will be set once and used a few times
61 for reading data back from the OpenGL server. Not supported
62 under OpenGL/ES.
63 \value StreamCopy The data will be set once and used a few times
64 for reading data back from the OpenGL server for use in further
65 drawing operations. Not supported under OpenGL/ES.
66 \value StaticDraw The data will be set once and used many times
67 for drawing operations.
68 \value StaticRead The data will be set once and used many times
69 for reading data back from the OpenGL server. Not supported
70 under OpenGL/ES.
71 \value StaticCopy The data will be set once and used many times
72 for reading data back from the OpenGL server for use in further
73 drawing operations. Not supported under OpenGL/ES.
74 \value DynamicDraw The data will be modified repeatedly and used
75 many times for drawing operations.
76 \value DynamicRead The data will be modified repeatedly and used
77 many times for reading data back from the OpenGL server.
78 Not supported under OpenGL/ES.
79 \value DynamicCopy The data will be modified repeatedly and used
80 many times for reading data back from the OpenGL server for
81 use in further drawing operations. Not supported under OpenGL/ES.
82*/
83
84/*!
85 \enum QOpenGLBuffer::Access
86 This enum defines the access mode for QOpenGLBuffer::map().
87
88 \value ReadOnly The buffer will be mapped for reading only.
89 \value WriteOnly The buffer will be mapped for writing only.
90 \value ReadWrite The buffer will be mapped for reading and writing.
91*/
92
93/*!
94 \enum QOpenGLBuffer::RangeAccessFlag
95 This enum defines the access mode bits for QOpenGLBuffer::mapRange().
96
97 \value RangeRead The buffer will be mapped for reading.
98 \value RangeWrite The buffer will be mapped for writing.
99 \value RangeInvalidate Discard the previous contents of the specified range.
100 \value RangeInvalidateBuffer Discard the previous contents of the entire buffer.
101 \value RangeFlushExplicit Indicates that modifications are to be flushed explicitly via \c glFlushMappedBufferRange.
102 \value RangeUnsynchronized Indicates that pending operations should not be synchronized before returning from mapRange().
103*/
104
105class QOpenGLBufferPrivate
106{
107public:
108 QOpenGLBufferPrivate(QOpenGLBuffer::Type t)
109 : ref(1),
110 type(t),
111 guard(nullptr),
112 usagePattern(QOpenGLBuffer::StaticDraw),
113 actualUsagePattern(QOpenGLBuffer::StaticDraw),
114 funcs(nullptr)
115 {
116 }
117
118 QAtomicInt ref;
119 QOpenGLBuffer::Type type;
120 QOpenGLSharedResourceGuard *guard;
121 QOpenGLBuffer::UsagePattern usagePattern;
122 QOpenGLBuffer::UsagePattern actualUsagePattern;
123 QOpenGLExtensions *funcs;
124};
125
126/*!
127 Constructs a new buffer object of type QOpenGLBuffer::VertexBuffer.
128
129 Note: this constructor just creates the QOpenGLBuffer instance. The actual
130 buffer object in the OpenGL server is not created until create() is called.
131
132 \sa create()
133*/
134QOpenGLBuffer::QOpenGLBuffer()
135 : d_ptr(new QOpenGLBufferPrivate(QOpenGLBuffer::VertexBuffer))
136{
137}
138
139/*!
140 Constructs a new buffer object of \a type.
141
142 Note: this constructor just creates the QOpenGLBuffer instance. The actual
143 buffer object in the OpenGL server is not created until create() is called.
144
145 \sa create()
146*/
147QOpenGLBuffer::QOpenGLBuffer(QOpenGLBuffer::Type type)
148 : d_ptr(new QOpenGLBufferPrivate(type))
149{
150}
151
152/*!
153 Constructs a shallow copy of \a other.
154
155 Note: QOpenGLBuffer does not implement copy-on-write semantics,
156 so \a other will be affected whenever the copy is modified.
157*/
158QOpenGLBuffer::QOpenGLBuffer(const QOpenGLBuffer &other)
159 : d_ptr(other.d_ptr)
160{
161 d_ptr->ref.ref();
162}
163
164/*!
165 Destroys this buffer object, including the storage being
166 used in the OpenGL server.
167*/
168QOpenGLBuffer::~QOpenGLBuffer()
169{
170 if (d_ptr && !d_ptr->ref.deref()) {
171 destroy();
172 delete d_ptr;
173 }
174}
175
176/*!
177 Assigns a shallow copy of \a other to this object.
178
179 Note: QOpenGLBuffer does not implement copy-on-write semantics,
180 so \a other will be affected whenever the copy is modified.
181*/
182QOpenGLBuffer &QOpenGLBuffer::operator=(const QOpenGLBuffer &other)
183{
184 if (d_ptr != other.d_ptr) {
185 other.d_ptr->ref.ref();
186 if (d_ptr && !d_ptr->ref.deref()) {
187 destroy();
188 delete d_ptr;
189 }
190 d_ptr = other.d_ptr;
191 }
192 return *this;
193}
194
195/*!
196 \fn QOpenGLBuffer::QOpenGLBuffer(QOpenGLBuffer &&other)
197 \since 6.5
198
199 Move-constructs a new QOpenGLBuffer from \a other.
200
201 \note The moved-from object \a other is placed in a partially-formed state,
202 in which the only valid operations are destruction and assignment of a new
203 value.
204*/
205
206/*!
207 \fn QOpenGLBuffer &QOpenGLBuffer::operator=(QOpenGLBuffer &&other)
208 \since 6.5
209
210 Move-assigns \a other to this QOpenGLBuffer instance.
211
212 \note The moved-from object \a other is placed in a partially-formed state,
213 in which the only valid operations are destruction and assignment of a new
214 value.
215*/
216
217/*!
218 \fn QOpenGLBuffer::swap(QOpenGLBuffer &other)
219 \since 6.5
220 \memberswap{buffer}
221*/
222
223/*!
224 Returns the type of buffer represented by this object.
225*/
226QOpenGLBuffer::Type QOpenGLBuffer::type() const
227{
228 Q_D(const QOpenGLBuffer);
229 return d->type;
230}
231
232/*!
233 Returns the usage pattern for this buffer object.
234 The default value is StaticDraw.
235
236 \sa setUsagePattern()
237*/
238QOpenGLBuffer::UsagePattern QOpenGLBuffer::usagePattern() const
239{
240 Q_D(const QOpenGLBuffer);
241 return d->usagePattern;
242}
243
244/*!
245 Sets the usage pattern for this buffer object to \a value.
246 This function must be called before allocate() or write().
247
248 \sa usagePattern(), allocate(), write()
249*/
250void QOpenGLBuffer::setUsagePattern(QOpenGLBuffer::UsagePattern value)
251{
252 Q_D(QOpenGLBuffer);
253 d->usagePattern = d->actualUsagePattern = value;
254}
255
256namespace {
257 void freeBufferFunc(QOpenGLFunctions *funcs, GLuint id)
258 {
259 funcs->glDeleteBuffers(1, &id);
260 }
261}
262
263/*!
264 Creates the buffer object in the OpenGL server. Returns \c true if
265 the object was created; false otherwise.
266
267 This function must be called with a current QOpenGLContext.
268 The buffer will be bound to and can only be used in
269 that context (or any other context that is shared with it).
270
271 This function will return false if the OpenGL implementation
272 does not support buffers, or there is no current QOpenGLContext.
273
274 \sa isCreated(), allocate(), write(), destroy()
275*/
276bool QOpenGLBuffer::create()
277{
278 Q_D(QOpenGLBuffer);
279 if (d->guard && d->guard->id())
280 return true;
281 QOpenGLContext *ctx = QOpenGLContext::currentContext();
282 if (ctx) {
283 delete d->funcs;
284 d->funcs = new QOpenGLExtensions(ctx);
285 GLuint bufferId = 0;
286 d->funcs->glGenBuffers(1, &bufferId);
287 if (bufferId) {
288 if (d->guard)
289 d->guard->free();
290
291 d->guard = new QOpenGLSharedResourceGuard(ctx, bufferId, freeBufferFunc);
292 return true;
293 }
294 }
295 return false;
296}
297
298/*!
299 Returns \c true if this buffer has been created; false otherwise.
300
301 \sa create(), destroy()
302*/
303bool QOpenGLBuffer::isCreated() const
304{
305 Q_D(const QOpenGLBuffer);
306 return d->guard && d->guard->id();
307}
308
309/*!
310 Destroys this buffer object, including the storage being
311 used in the OpenGL server. All references to the buffer will
312 become invalid.
313*/
314void QOpenGLBuffer::destroy()
315{
316 Q_D(QOpenGLBuffer);
317 if (d->guard) {
318 d->guard->free();
319 d->guard = nullptr;
320 }
321 delete d->funcs;
322 d->funcs = nullptr;
323}
324
325/*!
326 Reads the \a count bytes in this buffer starting at \a offset
327 into \a data. Returns \c true on success; false if reading from
328 the buffer is not supported. Buffer reading is not supported
329 under OpenGL/ES.
330
331 It is assumed that this buffer has been bound to the current context.
332
333 \sa write(), bind()
334*/
335bool QOpenGLBuffer::read(int offset, void *data, int count)
336{
337#if !QT_CONFIG(opengles2)
338 Q_D(QOpenGLBuffer);
339 if (!d->funcs->hasOpenGLFeature(QOpenGLFunctions::Buffers) || !d->guard->id())
340 return false;
341
342 while (true) { // Clear error state.
343 GLenum error = d->funcs->glGetError();
344 if (error == GL_NO_ERROR)
345 break;
346 if (error == GL_CONTEXT_LOST)
347 return false;
348 };
349 d->funcs->glGetBufferSubData(d->type, offset, count, data);
350 return d->funcs->glGetError() == GL_NO_ERROR;
351#else
352 Q_UNUSED(offset);
353 Q_UNUSED(data);
354 Q_UNUSED(count);
355 return false;
356#endif
357}
358
359/*!
360 Replaces the \a count bytes of this buffer starting at \a offset
361 with the contents of \a data. Any other bytes in the buffer
362 will be left unmodified.
363
364 It is assumed that create() has been called on this buffer and that
365 it has been bound to the current context.
366
367 \sa create(), read(), allocate()
368*/
369void QOpenGLBuffer::write(int offset, const void *data, int count)
370{
371#ifndef QT_NO_DEBUG
372 if (!isCreated())
373 qWarning("QOpenGLBuffer::write(): buffer not created");
374#endif
375 Q_D(QOpenGLBuffer);
376 if (d->guard && d->guard->id())
377 d->funcs->glBufferSubData(d->type, offset, count, data);
378}
379
380/*!
381 Allocates \a count bytes of space to the buffer, initialized to
382 the contents of \a data. Any previous contents will be removed.
383
384 It is assumed that create() has been called on this buffer and that
385 it has been bound to the current context.
386
387 \sa create(), read(), write()
388*/
389void QOpenGLBuffer::allocate(const void *data, int count)
390{
391#ifndef QT_NO_DEBUG
392 if (!isCreated())
393 qWarning("QOpenGLBuffer::allocate(): buffer not created");
394#endif
395 Q_D(QOpenGLBuffer);
396 if (d->guard && d->guard->id())
397 d->funcs->glBufferData(d->type, count, data, d->actualUsagePattern);
398}
399
400/*!
401 \fn void QOpenGLBuffer::allocate(int count)
402 \overload
403
404 Allocates \a count bytes of space to the buffer. Any previous
405 contents will be removed.
406
407 It is assumed that create() has been called on this buffer and that
408 it has been bound to the current context.
409
410 \sa create(), write()
411*/
412
413/*!
414 Binds the buffer associated with this object to the current
415 OpenGL context. Returns \c false if binding was not possible, usually because
416 type() is not supported on this OpenGL implementation.
417
418 The buffer must be bound to the same QOpenGLContext current when create()
419 was called, or to another QOpenGLContext that is sharing with it.
420 Otherwise, false will be returned from this function.
421
422 \sa release(), create()
423*/
424bool QOpenGLBuffer::bind()
425{
426#ifndef QT_NO_DEBUG
427 if (!isCreated())
428 qWarning("QOpenGLBuffer::bind(): buffer not created");
429#endif
430 Q_D(const QOpenGLBuffer);
431 GLuint bufferId = d->guard ? d->guard->id() : 0;
432 if (bufferId) {
433 if (d->guard->group() != QOpenGLContextGroup::currentContextGroup()) {
434#ifndef QT_NO_DEBUG
435 qWarning("QOpenGLBuffer::bind: buffer is not valid in the current context");
436#endif
437 return false;
438 }
439 d->funcs->glBindBuffer(d->type, bufferId);
440 return true;
441 } else {
442 return false;
443 }
444}
445
446/*!
447 Releases the buffer associated with this object from the
448 current OpenGL context.
449
450 This function must be called with the same QOpenGLContext current
451 as when bind() was called on the buffer.
452
453 \sa bind()
454*/
455void QOpenGLBuffer::release()
456{
457#ifndef QT_NO_DEBUG
458 if (!isCreated())
459 qWarning("QOpenGLBuffer::release(): buffer not created");
460#endif
461 Q_D(const QOpenGLBuffer);
462 if (d->guard && d->guard->id())
463 d->funcs->glBindBuffer(d->type, 0);
464}
465
466/*!
467 Releases the buffer associated with \a type in the current
468 QOpenGLContext.
469
470 This function is a direct call to \c{glBindBuffer(type, 0)}
471 for use when the caller does not know which QOpenGLBuffer has
472 been bound to the context but wants to make sure that it
473 is released.
474
475 \snippet code/src_gui_opengl_qopenglbuffer.cpp 1
476*/
477void QOpenGLBuffer::release(QOpenGLBuffer::Type type)
478{
479 QOpenGLContext *ctx = QOpenGLContext::currentContext();
480 if (ctx)
481 ctx->functions()->glBindBuffer(GLenum(type), 0);
482}
483
484/*!
485 Returns the OpenGL identifier associated with this buffer; zero if
486 the buffer has not been created.
487
488 \sa isCreated()
489*/
490GLuint QOpenGLBuffer::bufferId() const
491{
492 Q_D(const QOpenGLBuffer);
493 return d->guard ? d->guard->id() : 0;
494}
495
496/*!
497 Returns the size of the data in this buffer, for reading operations.
498 Returns -1 if fetching the buffer size is not supported, or the
499 buffer has not been created.
500
501 It is assumed that this buffer has been bound to the current context.
502
503 \sa isCreated(), bind()
504*/
505int QOpenGLBuffer::size() const
506{
507 Q_D(const QOpenGLBuffer);
508 if (!d->guard || !d->guard->id())
509 return -1;
510 GLint value = -1;
511 d->funcs->glGetBufferParameteriv(d->type, GL_BUFFER_SIZE, &value);
512 return value;
513}
514
515/*!
516 Maps the contents of this buffer into the application's memory
517 space and returns a pointer to it. Returns null if memory
518 mapping is not possible. The \a access parameter indicates the
519 type of access to be performed.
520
521 It is assumed that create() has been called on this buffer and that
522 it has been bound to the current context.
523
524 \note This function is only supported under OpenGL ES 2.0 or
525 earlier if the \c GL_OES_mapbuffer extension is present.
526
527 \note On OpenGL ES 3.0 and newer, or, in case if desktop OpenGL,
528 if \c GL_ARB_map_buffer_range is supported, this function uses
529 \c glMapBufferRange instead of \c glMapBuffer.
530
531 \sa unmap(), create(), bind(), mapRange()
532*/
533void *QOpenGLBuffer::map(QOpenGLBuffer::Access access)
534{
535 Q_D(QOpenGLBuffer);
536#ifndef QT_NO_DEBUG
537 if (!isCreated())
538 qWarning("QOpenGLBuffer::map(): buffer not created");
539#endif
540 if (!d->guard || !d->guard->id())
541 return nullptr;
542 if (d->funcs->hasOpenGLExtension(QOpenGLExtensions::MapBufferRange)) {
543 QOpenGLBuffer::RangeAccessFlags rangeAccess;
544 switch (access) {
545 case QOpenGLBuffer::ReadOnly:
546 rangeAccess = QOpenGLBuffer::RangeRead;
547 break;
548 case QOpenGLBuffer::WriteOnly:
549 rangeAccess = QOpenGLBuffer::RangeWrite;
550 break;
551 case QOpenGLBuffer::ReadWrite:
552 rangeAccess = QOpenGLBuffer::RangeRead | QOpenGLBuffer::RangeWrite;
553 break;
554 }
555 return d->funcs->glMapBufferRange(d->type, 0, size(), rangeAccess);
556 } else {
557 return d->funcs->glMapBuffer(d->type, access);
558 }
559}
560
561/*!
562 Maps the range specified by \a offset and \a count of the contents
563 of this buffer into the application's memory space and returns a
564 pointer to it. Returns null if memory mapping is not possible.
565 The \a access parameter specifies a combination of access flags.
566
567 It is assumed that create() has been called on this buffer and that
568 it has been bound to the current context.
569
570 \note This function is not available on OpenGL ES 2.0 and earlier.
571
572 \sa unmap(), create(), bind()
573 */
574void *QOpenGLBuffer::mapRange(int offset, int count, QOpenGLBuffer::RangeAccessFlags access)
575{
576 Q_D(QOpenGLBuffer);
577#ifndef QT_NO_DEBUG
578 if (!isCreated())
579 qWarning("QOpenGLBuffer::mapRange(): buffer not created");
580#endif
581 if (!d->guard || !d->guard->id())
582 return nullptr;
583 return d->funcs->glMapBufferRange(d->type, offset, count, access);
584}
585
586/*!
587 Unmaps the buffer after it was mapped into the application's
588 memory space with a previous call to map(). Returns \c true if
589 the unmap succeeded; false otherwise.
590
591 It is assumed that this buffer has been bound to the current context,
592 and that it was previously mapped with map().
593
594 \note This function is only supported under OpenGL ES 2.0 and
595 earlier if the \c{GL_OES_mapbuffer} extension is present.
596
597 \sa map()
598*/
599bool QOpenGLBuffer::unmap()
600{
601 Q_D(QOpenGLBuffer);
602#ifndef QT_NO_DEBUG
603 if (!isCreated())
604 qWarning("QOpenGLBuffer::unmap(): buffer not created");
605#endif
606 if (!d->guard || !d->guard->id())
607 return false;
608 return d->funcs->glUnmapBuffer(d->type) == GL_TRUE;
609}
610
611QT_END_NAMESPACE