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