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