2012-09-19 21:32:25 +00:00
|
|
|
/*
|
|
|
|
* Cogl
|
|
|
|
*
|
2014-02-22 01:28:54 +00:00
|
|
|
* A Low Level GPU Graphics and Utilities API
|
2012-09-19 21:32:25 +00:00
|
|
|
*
|
2013-02-19 13:51:34 +00:00
|
|
|
* Copyright (C) 2010,2011,2012,2013 Intel Corporation.
|
2012-09-19 21:32:25 +00:00
|
|
|
*
|
2014-02-22 01:28:54 +00:00
|
|
|
* Permission is hereby granted, free of charge, to any person
|
|
|
|
* obtaining a copy of this software and associated documentation
|
|
|
|
* files (the "Software"), to deal in the Software without
|
|
|
|
* restriction, including without limitation the rights to use, copy,
|
|
|
|
* modify, merge, publish, distribute, sublicense, and/or sell copies
|
|
|
|
* of the Software, and to permit persons to whom the Software is
|
|
|
|
* furnished to do so, subject to the following conditions:
|
|
|
|
*
|
|
|
|
* The above copyright notice and this permission notice shall be
|
|
|
|
* included in all copies or substantial portions of the Software.
|
|
|
|
*
|
|
|
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
|
|
|
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
|
|
|
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
|
|
|
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
|
|
|
|
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
|
|
|
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
|
|
|
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
|
|
* SOFTWARE.
|
2012-09-19 21:32:25 +00:00
|
|
|
*
|
|
|
|
*
|
|
|
|
*
|
|
|
|
* Authors:
|
|
|
|
* Damien Lespiau <damien.lespiau@intel.com>
|
|
|
|
* Robert Bragg <robert@linux.intel.com>
|
|
|
|
*/
|
|
|
|
|
|
|
|
#ifdef HAVE_CONFIG_H
|
|
|
|
#include "config.h"
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#include "cogl-context-private.h"
|
|
|
|
#include "cogl-buffer-gl-private.h"
|
2012-11-08 17:54:10 +00:00
|
|
|
#include "cogl-error-private.h"
|
|
|
|
#include "cogl-util-gl-private.h"
|
2012-09-19 21:32:25 +00:00
|
|
|
|
|
|
|
/*
|
|
|
|
* GL/GLES compatibility defines for the buffer API:
|
|
|
|
*/
|
|
|
|
|
|
|
|
#ifndef GL_PIXEL_PACK_BUFFER
|
|
|
|
#define GL_PIXEL_PACK_BUFFER 0x88EB
|
|
|
|
#endif
|
|
|
|
#ifndef GL_PIXEL_UNPACK_BUFFER
|
|
|
|
#define GL_PIXEL_UNPACK_BUFFER 0x88EC
|
|
|
|
#endif
|
|
|
|
#ifndef GL_ARRAY_BUFFER
|
|
|
|
#define GL_ARRAY_BUFFER 0x8892
|
|
|
|
#endif
|
|
|
|
#ifndef GL_ELEMENT_ARRAY_BUFFER
|
|
|
|
#define GL_ARRAY_BUFFER 0x8893
|
|
|
|
#endif
|
|
|
|
#ifndef GL_READ_ONLY
|
|
|
|
#define GL_READ_ONLY 0x88B8
|
|
|
|
#endif
|
|
|
|
#ifndef GL_WRITE_ONLY
|
|
|
|
#define GL_WRITE_ONLY 0x88B9
|
|
|
|
#endif
|
|
|
|
#ifndef GL_READ_WRITE
|
|
|
|
#define GL_READ_WRITE 0x88BA
|
|
|
|
#endif
|
2012-10-17 20:28:45 +00:00
|
|
|
#ifndef GL_MAP_READ_BIT
|
|
|
|
#define GL_MAP_READ_BIT 0x0001
|
|
|
|
#endif
|
|
|
|
#ifndef GL_MAP_WRITE_BIT
|
|
|
|
#define GL_MAP_WRITE_BIT 0x0002
|
|
|
|
#endif
|
|
|
|
#ifndef GL_MAP_INVALIDATE_RANGE_BIT
|
|
|
|
#define GL_MAP_INVALIDATE_RANGE_BIT 0x0004
|
|
|
|
#endif
|
|
|
|
#ifndef GL_MAP_INVALIDATE_BUFFER_BIT
|
|
|
|
#define GL_MAP_INVALIDATE_BUFFER_BIT 0x0008
|
|
|
|
#endif
|
2012-09-19 21:32:25 +00:00
|
|
|
|
|
|
|
void
|
|
|
|
_cogl_buffer_gl_create (CoglBuffer *buffer)
|
|
|
|
{
|
|
|
|
CoglContext *ctx = buffer->context;
|
|
|
|
|
|
|
|
GE (ctx, glGenBuffers (1, &buffer->gl_handle));
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
_cogl_buffer_gl_destroy (CoglBuffer *buffer)
|
|
|
|
{
|
|
|
|
GE( buffer->context, glDeleteBuffers (1, &buffer->gl_handle) );
|
|
|
|
}
|
|
|
|
|
|
|
|
static GLenum
|
|
|
|
update_hints_to_gl_enum (CoglBuffer *buffer)
|
|
|
|
{
|
|
|
|
/* usage hint is always DRAW for now */
|
|
|
|
switch (buffer->update_hint)
|
|
|
|
{
|
|
|
|
case COGL_BUFFER_UPDATE_HINT_STATIC:
|
|
|
|
return GL_STATIC_DRAW;
|
|
|
|
case COGL_BUFFER_UPDATE_HINT_DYNAMIC:
|
|
|
|
return GL_DYNAMIC_DRAW;
|
|
|
|
|
|
|
|
case COGL_BUFFER_UPDATE_HINT_STREAM:
|
|
|
|
/* OpenGL ES 1.1 only knows about STATIC_DRAW and DYNAMIC_DRAW */
|
|
|
|
#if defined(HAVE_COGL_GL) || defined(HAVE_COGL_GLES2)
|
|
|
|
if (buffer->context->driver != COGL_DRIVER_GLES1)
|
|
|
|
return GL_STREAM_DRAW;
|
|
|
|
#else
|
|
|
|
return GL_DYNAMIC_DRAW;
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
|
|
|
g_assert_not_reached ();
|
|
|
|
}
|
|
|
|
|
|
|
|
static GLenum
|
|
|
|
convert_bind_target_to_gl_target (CoglBufferBindTarget target)
|
|
|
|
{
|
|
|
|
switch (target)
|
|
|
|
{
|
|
|
|
case COGL_BUFFER_BIND_TARGET_PIXEL_PACK:
|
|
|
|
return GL_PIXEL_PACK_BUFFER;
|
|
|
|
case COGL_BUFFER_BIND_TARGET_PIXEL_UNPACK:
|
|
|
|
return GL_PIXEL_UNPACK_BUFFER;
|
|
|
|
case COGL_BUFFER_BIND_TARGET_ATTRIBUTE_BUFFER:
|
|
|
|
return GL_ARRAY_BUFFER;
|
|
|
|
case COGL_BUFFER_BIND_TARGET_INDEX_BUFFER:
|
|
|
|
return GL_ELEMENT_ARRAY_BUFFER;
|
|
|
|
default:
|
|
|
|
g_return_val_if_reached (COGL_BUFFER_BIND_TARGET_PIXEL_UNPACK);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2012-11-08 17:54:10 +00:00
|
|
|
static CoglBool
|
|
|
|
recreate_store (CoglBuffer *buffer,
|
|
|
|
CoglError **error)
|
2012-09-19 21:32:25 +00:00
|
|
|
{
|
2012-11-08 17:54:10 +00:00
|
|
|
CoglContext *ctx = buffer->context;
|
2012-09-19 21:32:25 +00:00
|
|
|
GLenum gl_target;
|
|
|
|
GLenum gl_enum;
|
2012-11-08 17:54:10 +00:00
|
|
|
GLenum gl_error;
|
2012-09-19 21:32:25 +00:00
|
|
|
|
|
|
|
/* This assumes the buffer is already bound */
|
|
|
|
|
|
|
|
gl_target = convert_bind_target_to_gl_target (buffer->last_target);
|
|
|
|
gl_enum = update_hints_to_gl_enum (buffer);
|
|
|
|
|
2012-11-08 17:54:10 +00:00
|
|
|
/* Clear any GL errors */
|
|
|
|
while ((gl_error = ctx->glGetError ()) != GL_NO_ERROR)
|
|
|
|
;
|
|
|
|
|
|
|
|
ctx->glBufferData (gl_target,
|
|
|
|
buffer->size,
|
|
|
|
NULL,
|
|
|
|
gl_enum);
|
|
|
|
|
|
|
|
if (_cogl_gl_util_catch_out_of_memory (ctx, error))
|
|
|
|
return FALSE;
|
|
|
|
|
2012-09-19 21:32:25 +00:00
|
|
|
buffer->store_created = TRUE;
|
2012-11-08 17:54:10 +00:00
|
|
|
return TRUE;
|
2012-09-19 21:32:25 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
GLenum
|
|
|
|
_cogl_buffer_access_to_gl_enum (CoglBufferAccess access)
|
|
|
|
{
|
|
|
|
if ((access & COGL_BUFFER_ACCESS_READ_WRITE) == COGL_BUFFER_ACCESS_READ_WRITE)
|
|
|
|
return GL_READ_WRITE;
|
|
|
|
else if (access & COGL_BUFFER_ACCESS_WRITE)
|
|
|
|
return GL_WRITE_ONLY;
|
|
|
|
else
|
|
|
|
return GL_READ_ONLY;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void *
|
|
|
|
_cogl_buffer_bind_no_create (CoglBuffer *buffer,
|
|
|
|
CoglBufferBindTarget target)
|
|
|
|
{
|
|
|
|
CoglContext *ctx = buffer->context;
|
|
|
|
|
|
|
|
_COGL_RETURN_VAL_IF_FAIL (buffer != NULL, NULL);
|
|
|
|
|
|
|
|
/* Don't allow binding the buffer to multiple targets at the same time */
|
|
|
|
_COGL_RETURN_VAL_IF_FAIL (ctx->current_buffer[buffer->last_target] != buffer,
|
|
|
|
NULL);
|
|
|
|
|
|
|
|
/* Don't allow nesting binds to the same target */
|
|
|
|
_COGL_RETURN_VAL_IF_FAIL (ctx->current_buffer[target] == NULL, NULL);
|
|
|
|
|
|
|
|
buffer->last_target = target;
|
|
|
|
ctx->current_buffer[target] = buffer;
|
|
|
|
|
|
|
|
if (buffer->flags & COGL_BUFFER_FLAG_BUFFER_OBJECT)
|
|
|
|
{
|
|
|
|
GLenum gl_target = convert_bind_target_to_gl_target (buffer->last_target);
|
|
|
|
GE( ctx, glBindBuffer (gl_target, buffer->gl_handle) );
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
return buffer->data;
|
|
|
|
}
|
|
|
|
|
|
|
|
void *
|
2012-10-17 20:28:45 +00:00
|
|
|
_cogl_buffer_gl_map_range (CoglBuffer *buffer,
|
|
|
|
size_t offset,
|
|
|
|
size_t size,
|
|
|
|
CoglBufferAccess access,
|
2012-11-08 17:54:10 +00:00
|
|
|
CoglBufferMapHint hints,
|
|
|
|
CoglError **error)
|
2012-09-19 21:32:25 +00:00
|
|
|
{
|
|
|
|
uint8_t *data;
|
|
|
|
CoglBufferBindTarget target;
|
|
|
|
GLenum gl_target;
|
|
|
|
CoglContext *ctx = buffer->context;
|
2012-11-08 17:54:10 +00:00
|
|
|
GLenum gl_error;
|
2012-09-19 21:32:25 +00:00
|
|
|
|
2012-11-08 17:54:10 +00:00
|
|
|
if (((access & COGL_BUFFER_ACCESS_READ) &&
|
|
|
|
!cogl_has_feature (ctx, COGL_FEATURE_ID_MAP_BUFFER_FOR_READ)) ||
|
|
|
|
((access & COGL_BUFFER_ACCESS_WRITE) &&
|
|
|
|
!cogl_has_feature (ctx, COGL_FEATURE_ID_MAP_BUFFER_FOR_WRITE)))
|
|
|
|
{
|
|
|
|
_cogl_set_error (error,
|
|
|
|
COGL_SYSTEM_ERROR,
|
|
|
|
COGL_SYSTEM_ERROR_UNSUPPORTED,
|
|
|
|
"Tried to map a buffer with unsupported access mode");
|
|
|
|
return NULL;
|
|
|
|
}
|
2012-09-19 21:32:25 +00:00
|
|
|
|
|
|
|
target = buffer->last_target;
|
|
|
|
_cogl_buffer_bind_no_create (buffer, target);
|
|
|
|
|
|
|
|
gl_target = convert_bind_target_to_gl_target (target);
|
|
|
|
|
2013-02-19 13:51:34 +00:00
|
|
|
if ((hints & COGL_BUFFER_MAP_HINT_DISCARD_RANGE) &&
|
|
|
|
offset == 0 && size >= buffer->size)
|
|
|
|
hints |= COGL_BUFFER_MAP_HINT_DISCARD;
|
|
|
|
|
2012-10-17 20:28:45 +00:00
|
|
|
/* If the map buffer range extension is supported then we will
|
|
|
|
* always use it even if we are mapping the full range because the
|
|
|
|
* normal mapping function doesn't support passing the discard
|
|
|
|
* hints */
|
|
|
|
if (ctx->glMapBufferRange)
|
|
|
|
{
|
|
|
|
GLbitfield gl_access = 0;
|
2013-02-19 13:51:34 +00:00
|
|
|
CoglBool should_recreate_store = !buffer->store_created;
|
2012-10-17 20:28:45 +00:00
|
|
|
|
|
|
|
if ((access & COGL_BUFFER_ACCESS_READ))
|
|
|
|
gl_access |= GL_MAP_READ_BIT;
|
|
|
|
if ((access & COGL_BUFFER_ACCESS_WRITE))
|
|
|
|
gl_access |= GL_MAP_WRITE_BIT;
|
|
|
|
|
|
|
|
if ((hints & COGL_BUFFER_MAP_HINT_DISCARD))
|
2013-02-19 13:51:34 +00:00
|
|
|
{
|
|
|
|
/* glMapBufferRange generates an error if you pass the
|
|
|
|
* discard hint along with asking for read access. However
|
|
|
|
* it can make sense to ask for both if write access is also
|
|
|
|
* requested so that the application can immediately read
|
|
|
|
* back what it just wrote. To work around the restriction
|
|
|
|
* in GL we just recreate the buffer storage in that case
|
|
|
|
* which is an alternative way to indicate that the buffer
|
|
|
|
* contents can be discarded. */
|
|
|
|
if ((access & COGL_BUFFER_ACCESS_READ))
|
|
|
|
should_recreate_store = TRUE;
|
|
|
|
else
|
|
|
|
gl_access |= GL_MAP_INVALIDATE_BUFFER_BIT;
|
|
|
|
}
|
|
|
|
else if ((hints & COGL_BUFFER_MAP_HINT_DISCARD_RANGE) &&
|
|
|
|
!(access & COGL_BUFFER_ACCESS_READ))
|
2012-10-17 20:28:45 +00:00
|
|
|
gl_access |= GL_MAP_INVALIDATE_RANGE_BIT;
|
|
|
|
|
2013-02-19 13:51:34 +00:00
|
|
|
if (should_recreate_store)
|
2012-11-08 17:54:10 +00:00
|
|
|
{
|
|
|
|
if (!recreate_store (buffer, error))
|
|
|
|
{
|
|
|
|
_cogl_buffer_gl_unbind (buffer);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Clear any GL errors */
|
|
|
|
while ((gl_error = ctx->glGetError ()) != GL_NO_ERROR)
|
|
|
|
;
|
|
|
|
|
|
|
|
data = ctx->glMapBufferRange (gl_target,
|
|
|
|
offset,
|
|
|
|
size,
|
|
|
|
gl_access);
|
|
|
|
|
|
|
|
if (_cogl_gl_util_catch_out_of_memory (ctx, error))
|
|
|
|
{
|
|
|
|
_cogl_buffer_gl_unbind (buffer);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
_COGL_RETURN_VAL_IF_FAIL (data != NULL, NULL);
|
2012-10-17 20:28:45 +00:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
/* create an empty store if we don't have one yet. creating the store
|
|
|
|
* lazily allows the user of the CoglBuffer to set a hint before the
|
|
|
|
* store is created. */
|
|
|
|
if (!buffer->store_created ||
|
2013-02-19 13:51:34 +00:00
|
|
|
(hints & COGL_BUFFER_MAP_HINT_DISCARD))
|
2012-11-08 17:54:10 +00:00
|
|
|
{
|
|
|
|
if (!recreate_store (buffer, error))
|
|
|
|
{
|
|
|
|
_cogl_buffer_gl_unbind (buffer);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Clear any GL errors */
|
|
|
|
while ((gl_error = ctx->glGetError ()) != GL_NO_ERROR)
|
|
|
|
;
|
|
|
|
|
|
|
|
data = ctx->glMapBuffer (gl_target,
|
|
|
|
_cogl_buffer_access_to_gl_enum (access));
|
|
|
|
|
|
|
|
if (_cogl_gl_util_catch_out_of_memory (ctx, error))
|
|
|
|
{
|
|
|
|
_cogl_buffer_gl_unbind (buffer);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
_COGL_RETURN_VAL_IF_FAIL (data != NULL, NULL);
|
|
|
|
|
|
|
|
data += offset;
|
2012-10-17 20:28:45 +00:00
|
|
|
}
|
2012-09-19 21:32:25 +00:00
|
|
|
|
|
|
|
if (data)
|
|
|
|
buffer->flags |= COGL_BUFFER_FLAG_MAPPED;
|
|
|
|
|
|
|
|
_cogl_buffer_gl_unbind (buffer);
|
|
|
|
|
|
|
|
return data;
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
_cogl_buffer_gl_unmap (CoglBuffer *buffer)
|
|
|
|
{
|
|
|
|
CoglContext *ctx = buffer->context;
|
|
|
|
|
|
|
|
_cogl_buffer_bind_no_create (buffer, buffer->last_target);
|
|
|
|
|
|
|
|
GE( ctx, glUnmapBuffer (convert_bind_target_to_gl_target
|
|
|
|
(buffer->last_target)) );
|
|
|
|
buffer->flags &= ~COGL_BUFFER_FLAG_MAPPED;
|
|
|
|
|
|
|
|
_cogl_buffer_gl_unbind (buffer);
|
|
|
|
}
|
|
|
|
|
|
|
|
CoglBool
|
|
|
|
_cogl_buffer_gl_set_data (CoglBuffer *buffer,
|
|
|
|
unsigned int offset,
|
|
|
|
const void *data,
|
2012-11-08 17:54:10 +00:00
|
|
|
unsigned int size,
|
|
|
|
CoglError **error)
|
2012-09-19 21:32:25 +00:00
|
|
|
{
|
|
|
|
CoglBufferBindTarget target;
|
|
|
|
GLenum gl_target;
|
|
|
|
CoglContext *ctx = buffer->context;
|
2012-11-08 17:54:10 +00:00
|
|
|
GLenum gl_error;
|
|
|
|
CoglBool status = TRUE;
|
|
|
|
CoglError *internal_error = NULL;
|
2012-09-19 21:32:25 +00:00
|
|
|
|
|
|
|
target = buffer->last_target;
|
2012-11-08 17:54:10 +00:00
|
|
|
|
|
|
|
_cogl_buffer_gl_bind (buffer, target, &internal_error);
|
|
|
|
|
|
|
|
/* NB: _cogl_buffer_gl_bind() may return NULL in non-error
|
|
|
|
* conditions so we have to explicity check internal_error
|
|
|
|
* to see if an exception was thrown.
|
|
|
|
*/
|
|
|
|
if (internal_error)
|
|
|
|
{
|
2012-11-19 20:14:55 +00:00
|
|
|
_cogl_propagate_error (error, internal_error);
|
2012-11-08 17:54:10 +00:00
|
|
|
return FALSE;
|
|
|
|
}
|
2012-09-19 21:32:25 +00:00
|
|
|
|
|
|
|
gl_target = convert_bind_target_to_gl_target (target);
|
|
|
|
|
2012-11-08 17:54:10 +00:00
|
|
|
/* Clear any GL errors */
|
|
|
|
while ((gl_error = ctx->glGetError ()) != GL_NO_ERROR)
|
|
|
|
;
|
|
|
|
|
|
|
|
ctx->glBufferSubData (gl_target, offset, size, data);
|
|
|
|
|
|
|
|
if (_cogl_gl_util_catch_out_of_memory (ctx, error))
|
|
|
|
status = FALSE;
|
2012-09-19 21:32:25 +00:00
|
|
|
|
|
|
|
_cogl_buffer_gl_unbind (buffer);
|
|
|
|
|
2012-11-08 17:54:10 +00:00
|
|
|
return status;
|
2012-09-19 21:32:25 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void *
|
2012-11-08 17:54:10 +00:00
|
|
|
_cogl_buffer_gl_bind (CoglBuffer *buffer,
|
|
|
|
CoglBufferBindTarget target,
|
|
|
|
CoglError **error)
|
2012-09-19 21:32:25 +00:00
|
|
|
{
|
|
|
|
void *ret;
|
|
|
|
|
|
|
|
ret = _cogl_buffer_bind_no_create (buffer, target);
|
|
|
|
|
|
|
|
/* create an empty store if we don't have one yet. creating the store
|
|
|
|
* lazily allows the user of the CoglBuffer to set a hint before the
|
|
|
|
* store is created. */
|
|
|
|
if ((buffer->flags & COGL_BUFFER_FLAG_BUFFER_OBJECT) &&
|
|
|
|
!buffer->store_created)
|
2012-11-08 17:54:10 +00:00
|
|
|
{
|
|
|
|
if (!recreate_store (buffer, error))
|
|
|
|
{
|
|
|
|
_cogl_buffer_gl_unbind (buffer);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
}
|
2012-09-19 21:32:25 +00:00
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
_cogl_buffer_gl_unbind (CoglBuffer *buffer)
|
|
|
|
{
|
|
|
|
CoglContext *ctx = buffer->context;
|
|
|
|
|
|
|
|
_COGL_RETURN_IF_FAIL (buffer != NULL);
|
|
|
|
|
|
|
|
/* the unbind should pair up with a previous bind */
|
|
|
|
_COGL_RETURN_IF_FAIL (ctx->current_buffer[buffer->last_target] == buffer);
|
|
|
|
|
|
|
|
if (buffer->flags & COGL_BUFFER_FLAG_BUFFER_OBJECT)
|
|
|
|
{
|
|
|
|
GLenum gl_target = convert_bind_target_to_gl_target (buffer->last_target);
|
|
|
|
GE( ctx, glBindBuffer (gl_target, 0) );
|
|
|
|
}
|
|
|
|
|
|
|
|
ctx->current_buffer[buffer->last_target] = NULL;
|
|
|
|
}
|