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 | |
50 | QT_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 | |
140 | class QOpenGLBufferPrivate |
141 | { |
142 | public: |
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 | */ |
169 | QOpenGLBuffer::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 | */ |
182 | QOpenGLBuffer::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 | */ |
193 | QOpenGLBuffer::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 | */ |
203 | QOpenGLBuffer::~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 | */ |
217 | QOpenGLBuffer &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 | */ |
233 | QOpenGLBuffer::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 | */ |
245 | QOpenGLBuffer::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 | */ |
257 | void QOpenGLBuffer::setUsagePattern(QOpenGLBuffer::UsagePattern value) |
258 | { |
259 | Q_D(QOpenGLBuffer); |
260 | d->usagePattern = d->actualUsagePattern = value; |
261 | } |
262 | |
263 | namespace { |
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 | */ |
283 | bool 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 | */ |
310 | bool 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 | */ |
321 | void 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 | */ |
342 | bool 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 | */ |
376 | void 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 | */ |
396 | void 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 | */ |
431 | bool 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 | */ |
462 | void 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 | */ |
484 | void 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 | */ |
497 | GLuint 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 | */ |
512 | int 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 | */ |
540 | void *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 | */ |
581 | void *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 | */ |
606 | bool 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 | |
618 | QT_END_NAMESPACE |
619 | |