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

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