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