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>
7#include "qopenglbuffer.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
220 Swaps buffer \a other with this buffer. This operation is very fast and
221 never fails.
222*/
223
224/*!
225 Returns the type of buffer represented by this object.
226*/
227QOpenGLBuffer::Type QOpenGLBuffer::type() const
228{
229 Q_D(const QOpenGLBuffer);
230 return d->type;
231}
232
233/*!
234 Returns the usage pattern for this buffer object.
235 The default value is StaticDraw.
236
237 \sa setUsagePattern()
238*/
239QOpenGLBuffer::UsagePattern QOpenGLBuffer::usagePattern() const
240{
241 Q_D(const QOpenGLBuffer);
242 return d->usagePattern;
243}
244
245/*!
246 Sets the usage pattern for this buffer object to \a value.
247 This function must be called before allocate() or write().
248
249 \sa usagePattern(), allocate(), write()
250*/
251void QOpenGLBuffer::setUsagePattern(QOpenGLBuffer::UsagePattern value)
252{
253 Q_D(QOpenGLBuffer);
254 d->usagePattern = d->actualUsagePattern = value;
255}
256
257namespace {
258 void freeBufferFunc(QOpenGLFunctions *funcs, GLuint id)
259 {
260 funcs->glDeleteBuffers(n: 1, buffers: &id);
261 }
262}
263
264/*!
265 Creates the buffer object in the OpenGL server. Returns \c true if
266 the object was created; false otherwise.
267
268 This function must be called with a current QOpenGLContext.
269 The buffer will be bound to and can only be used in
270 that context (or any other context that is shared with it).
271
272 This function will return false if the OpenGL implementation
273 does not support buffers, or there is no current QOpenGLContext.
274
275 \sa isCreated(), allocate(), write(), destroy()
276*/
277bool QOpenGLBuffer::create()
278{
279 Q_D(QOpenGLBuffer);
280 if (d->guard && d->guard->id())
281 return true;
282 QOpenGLContext *ctx = QOpenGLContext::currentContext();
283 if (ctx) {
284 delete d->funcs;
285 d->funcs = new QOpenGLExtensions(ctx);
286 GLuint bufferId = 0;
287 d->funcs->glGenBuffers(n: 1, buffers: &bufferId);
288 if (bufferId) {
289 if (d->guard)
290 d->guard->free();
291
292 d->guard = new QOpenGLSharedResourceGuard(ctx, bufferId, freeBufferFunc);
293 return true;
294 }
295 }
296 return false;
297}
298
299/*!
300 Returns \c true if this buffer has been created; false otherwise.
301
302 \sa create(), destroy()
303*/
304bool QOpenGLBuffer::isCreated() const
305{
306 Q_D(const QOpenGLBuffer);
307 return d->guard && d->guard->id();
308}
309
310/*!
311 Destroys this buffer object, including the storage being
312 used in the OpenGL server. All references to the buffer will
313 become invalid.
314*/
315void QOpenGLBuffer::destroy()
316{
317 Q_D(QOpenGLBuffer);
318 if (d->guard) {
319 d->guard->free();
320 d->guard = nullptr;
321 }
322 delete d->funcs;
323 d->funcs = nullptr;
324}
325
326/*!
327 Reads the \a count bytes in this buffer starting at \a offset
328 into \a data. Returns \c true on success; false if reading from
329 the buffer is not supported. Buffer reading is not supported
330 under OpenGL/ES.
331
332 It is assumed that this buffer has been bound to the current context.
333
334 \sa write(), bind()
335*/
336bool QOpenGLBuffer::read(int offset, void *data, int count)
337{
338#if !QT_CONFIG(opengles2)
339 Q_D(QOpenGLBuffer);
340 if (!d->funcs->hasOpenGLFeature(feature: QOpenGLFunctions::Buffers) || !d->guard->id())
341 return false;
342
343 while (true) { // Clear error state.
344 GLenum error = d->funcs->glGetError();
345 if (error == GL_NO_ERROR)
346 break;
347 if (error == GL_CONTEXT_LOST)
348 return false;
349 };
350 d->funcs->glGetBufferSubData(target: d->type, offset, size: count, data);
351 return d->funcs->glGetError() == GL_NO_ERROR;
352#else
353 Q_UNUSED(offset);
354 Q_UNUSED(data);
355 Q_UNUSED(count);
356 return false;
357#endif
358}
359
360/*!
361 Replaces the \a count bytes of this buffer starting at \a offset
362 with the contents of \a data. Any other bytes in the buffer
363 will be left unmodified.
364
365 It is assumed that create() has been called on this buffer and that
366 it has been bound to the current context.
367
368 \sa create(), read(), allocate()
369*/
370void QOpenGLBuffer::write(int offset, const void *data, int count)
371{
372#ifndef QT_NO_DEBUG
373 if (!isCreated())
374 qWarning(msg: "QOpenGLBuffer::write(): buffer not created");
375#endif
376 Q_D(QOpenGLBuffer);
377 if (d->guard && d->guard->id())
378 d->funcs->glBufferSubData(target: d->type, offset, size: count, data);
379}
380
381/*!
382 Allocates \a count bytes of space to the buffer, initialized to
383 the contents of \a data. Any previous contents will be removed.
384
385 It is assumed that create() has been called on this buffer and that
386 it has been bound to the current context.
387
388 \sa create(), read(), write()
389*/
390void QOpenGLBuffer::allocate(const void *data, int count)
391{
392#ifndef QT_NO_DEBUG
393 if (!isCreated())
394 qWarning(msg: "QOpenGLBuffer::allocate(): buffer not created");
395#endif
396 Q_D(QOpenGLBuffer);
397 if (d->guard && d->guard->id())
398 d->funcs->glBufferData(target: d->type, size: count, data, usage: d->actualUsagePattern);
399}
400
401/*!
402 \fn void QOpenGLBuffer::allocate(int count)
403 \overload
404
405 Allocates \a count bytes of space to the buffer. Any previous
406 contents will be removed.
407
408 It is assumed that create() has been called on this buffer and that
409 it has been bound to the current context.
410
411 \sa create(), write()
412*/
413
414/*!
415 Binds the buffer associated with this object to the current
416 OpenGL context. Returns \c false if binding was not possible, usually because
417 type() is not supported on this OpenGL implementation.
418
419 The buffer must be bound to the same QOpenGLContext current when create()
420 was called, or to another QOpenGLContext that is sharing with it.
421 Otherwise, false will be returned from this function.
422
423 \sa release(), create()
424*/
425bool QOpenGLBuffer::bind()
426{
427#ifndef QT_NO_DEBUG
428 if (!isCreated())
429 qWarning(msg: "QOpenGLBuffer::bind(): buffer not created");
430#endif
431 Q_D(const QOpenGLBuffer);
432 GLuint bufferId = d->guard ? d->guard->id() : 0;
433 if (bufferId) {
434 if (d->guard->group() != QOpenGLContextGroup::currentContextGroup()) {
435#ifndef QT_NO_DEBUG
436 qWarning(msg: "QOpenGLBuffer::bind: buffer is not valid in the current context");
437#endif
438 return false;
439 }
440 d->funcs->glBindBuffer(target: d->type, buffer: bufferId);
441 return true;
442 } else {
443 return false;
444 }
445}
446
447/*!
448 Releases the buffer associated with this object from the
449 current OpenGL context.
450
451 This function must be called with the same QOpenGLContext current
452 as when bind() was called on the buffer.
453
454 \sa bind()
455*/
456void QOpenGLBuffer::release()
457{
458#ifndef QT_NO_DEBUG
459 if (!isCreated())
460 qWarning(msg: "QOpenGLBuffer::release(): buffer not created");
461#endif
462 Q_D(const QOpenGLBuffer);
463 if (d->guard && d->guard->id())
464 d->funcs->glBindBuffer(target: d->type, buffer: 0);
465}
466
467/*!
468 Releases the buffer associated with \a type in the current
469 QOpenGLContext.
470
471 This function is a direct call to \c{glBindBuffer(type, 0)}
472 for use when the caller does not know which QOpenGLBuffer has
473 been bound to the context but wants to make sure that it
474 is released.
475
476 \snippet code/src_gui_opengl_qopenglbuffer.cpp 1
477*/
478void QOpenGLBuffer::release(QOpenGLBuffer::Type type)
479{
480 QOpenGLContext *ctx = QOpenGLContext::currentContext();
481 if (ctx)
482 ctx->functions()->glBindBuffer(target: GLenum(type), buffer: 0);
483}
484
485/*!
486 Returns the OpenGL identifier associated with this buffer; zero if
487 the buffer has not been created.
488
489 \sa isCreated()
490*/
491GLuint QOpenGLBuffer::bufferId() const
492{
493 Q_D(const QOpenGLBuffer);
494 return d->guard ? d->guard->id() : 0;
495}
496
497/*!
498 Returns the size of the data in this buffer, for reading operations.
499 Returns -1 if fetching the buffer size is not supported, or the
500 buffer has not been created.
501
502 It is assumed that this buffer has been bound to the current context.
503
504 \sa isCreated(), bind()
505*/
506int QOpenGLBuffer::size() const
507{
508 Q_D(const QOpenGLBuffer);
509 if (!d->guard || !d->guard->id())
510 return -1;
511 GLint value = -1;
512 d->funcs->glGetBufferParameteriv(target: d->type, GL_BUFFER_SIZE, params: &value);
513 return value;
514}
515
516/*!
517 Maps the contents of this buffer into the application's memory
518 space and returns a pointer to it. Returns null if memory
519 mapping is not possible. The \a access parameter indicates the
520 type of access to be performed.
521
522 It is assumed that create() has been called on this buffer and that
523 it has been bound to the current context.
524
525 \note This function is only supported under OpenGL ES 2.0 or
526 earlier if the \c GL_OES_mapbuffer extension is present.
527
528 \note On OpenGL ES 3.0 and newer, or, in case if desktop OpenGL,
529 if \c GL_ARB_map_buffer_range is supported, this function uses
530 \c glMapBufferRange instead of \c glMapBuffer.
531
532 \sa unmap(), create(), bind(), mapRange()
533*/
534void *QOpenGLBuffer::map(QOpenGLBuffer::Access access)
535{
536 Q_D(QOpenGLBuffer);
537#ifndef QT_NO_DEBUG
538 if (!isCreated())
539 qWarning(msg: "QOpenGLBuffer::map(): buffer not created");
540#endif
541 if (!d->guard || !d->guard->id())
542 return nullptr;
543 if (d->funcs->hasOpenGLExtension(extension: QOpenGLExtensions::MapBufferRange)) {
544 QOpenGLBuffer::RangeAccessFlags rangeAccess;
545 switch (access) {
546 case QOpenGLBuffer::ReadOnly:
547 rangeAccess = QOpenGLBuffer::RangeRead;
548 break;
549 case QOpenGLBuffer::WriteOnly:
550 rangeAccess = QOpenGLBuffer::RangeWrite;
551 break;
552 case QOpenGLBuffer::ReadWrite:
553 rangeAccess = QOpenGLBuffer::RangeRead | QOpenGLBuffer::RangeWrite;
554 break;
555 }
556 return d->funcs->glMapBufferRange(target: d->type, offset: 0, length: size(), access: rangeAccess);
557 } else {
558 return d->funcs->glMapBuffer(target: d->type, access);
559 }
560}
561
562/*!
563 Maps the range specified by \a offset and \a count of the contents
564 of this buffer into the application's memory space and returns a
565 pointer to it. Returns null if memory mapping is not possible.
566 The \a access parameter specifies a combination of access flags.
567
568 It is assumed that create() has been called on this buffer and that
569 it has been bound to the current context.
570
571 \note This function is not available on OpenGL ES 2.0 and earlier.
572
573 \sa unmap(), create(), bind()
574 */
575void *QOpenGLBuffer::mapRange(int offset, int count, QOpenGLBuffer::RangeAccessFlags access)
576{
577 Q_D(QOpenGLBuffer);
578#ifndef QT_NO_DEBUG
579 if (!isCreated())
580 qWarning(msg: "QOpenGLBuffer::mapRange(): buffer not created");
581#endif
582 if (!d->guard || !d->guard->id())
583 return nullptr;
584 return d->funcs->glMapBufferRange(target: d->type, offset, length: count, access);
585}
586
587/*!
588 Unmaps the buffer after it was mapped into the application's
589 memory space with a previous call to map(). Returns \c true if
590 the unmap succeeded; false otherwise.
591
592 It is assumed that this buffer has been bound to the current context,
593 and that it was previously mapped with map().
594
595 \note This function is only supported under OpenGL ES 2.0 and
596 earlier if the \c{GL_OES_mapbuffer} extension is present.
597
598 \sa map()
599*/
600bool QOpenGLBuffer::unmap()
601{
602 Q_D(QOpenGLBuffer);
603#ifndef QT_NO_DEBUG
604 if (!isCreated())
605 qWarning(msg: "QOpenGLBuffer::unmap(): buffer not created");
606#endif
607 if (!d->guard || !d->guard->id())
608 return false;
609 return d->funcs->glUnmapBuffer(target: d->type) == GL_TRUE;
610}
611
612QT_END_NAMESPACE
613

source code of qtbase/src/opengl/qopenglbuffer.cpp