543 lines
17 KiB
C
543 lines
17 KiB
C
|
/*
|
||
|
* Cogl
|
||
|
*
|
||
|
* An object oriented GL/GLES Abstraction/Utility Layer
|
||
|
*
|
||
|
* Copyright (C) 2011 Collabora Ltd.
|
||
|
* Copyright (C) 2012 Intel Corporation.
|
||
|
*
|
||
|
* This library is free software; you can redistribute it and/or
|
||
|
* modify it under the terms of the GNU Lesser General Public
|
||
|
* License as published by the Free Software Foundation; either
|
||
|
* version 2 of the License, or (at your option) any later version.
|
||
|
*
|
||
|
* This library is distributed in the hope that it will be useful,
|
||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||
|
* Lesser General Public License for more details.
|
||
|
*
|
||
|
* You should have received a copy of the GNU Lesser General Public
|
||
|
* License along with this library. If not, see
|
||
|
* <http://www.gnu.org/licenses/>.
|
||
|
*
|
||
|
* Authors:
|
||
|
* Tomeu Vizoso <tomeu.vizoso@collabora.com>
|
||
|
* Robert Bragg <robert@linux.intel.com>
|
||
|
*
|
||
|
*/
|
||
|
|
||
|
#ifdef HAVE_CONFIG_H
|
||
|
#include "config.h"
|
||
|
#endif
|
||
|
|
||
|
#include "cogl-gles2.h"
|
||
|
#include "cogl-gles2-context-private.h"
|
||
|
|
||
|
#include "cogl-context-private.h"
|
||
|
#include "cogl-display-private.h"
|
||
|
#include "cogl-framebuffer-private.h"
|
||
|
#include "cogl-onscreen-template-private.h"
|
||
|
#include "cogl-renderer-private.h"
|
||
|
#include "cogl-swap-chain-private.h"
|
||
|
#include "cogl-texture-2d-private.h"
|
||
|
|
||
|
static void _cogl_gles2_context_free (CoglGLES2Context *gles2_context);
|
||
|
|
||
|
COGL_OBJECT_DEFINE (GLES2Context, gles2_context);
|
||
|
|
||
|
static CoglGLES2Context *current_gles2_context;
|
||
|
|
||
|
static CoglUserDataKey offscreen_wrapper_key;
|
||
|
|
||
|
enum {
|
||
|
RESTORE_FB_NONE,
|
||
|
RESTORE_FB_FROM_OFFSCREEN,
|
||
|
RESTORE_FB_FROM_ONSCREEN,
|
||
|
};
|
||
|
|
||
|
GQuark
|
||
|
_cogl_gles2_context_error_quark (void)
|
||
|
{
|
||
|
return g_quark_from_static_string ("cogl-gles2-context-error-quark");
|
||
|
}
|
||
|
|
||
|
/* We wrap glBindFramebuffer so that when framebuffer 0 is bound
|
||
|
* we can instead bind the write_framebuffer passed to
|
||
|
* cogl_push_gles2_context().
|
||
|
*/
|
||
|
static void
|
||
|
gl_bind_framebuffer_wrapper (GLenum target, GLuint framebuffer)
|
||
|
{
|
||
|
CoglGLES2Context *gles2_ctx = current_gles2_context;
|
||
|
|
||
|
gles2_ctx->current_fbo_handle = framebuffer;
|
||
|
|
||
|
if (framebuffer == 0 && cogl_is_offscreen (gles2_ctx->write_buffer))
|
||
|
{
|
||
|
CoglGLES2Offscreen *write = gles2_ctx->gles2_write_buffer;
|
||
|
framebuffer = write->gl_framebuffer.fbo_handle;
|
||
|
}
|
||
|
|
||
|
gles2_ctx->context->glBindFramebuffer (target, framebuffer);
|
||
|
}
|
||
|
|
||
|
static int
|
||
|
transient_bind_read_buffer (CoglGLES2Context *gles2_ctx)
|
||
|
{
|
||
|
if (gles2_ctx->current_fbo_handle == 0)
|
||
|
{
|
||
|
if (cogl_is_offscreen (gles2_ctx->read_buffer))
|
||
|
{
|
||
|
CoglGLES2Offscreen *read = gles2_ctx->gles2_read_buffer;
|
||
|
GLuint read_fbo_handle = read->gl_framebuffer.fbo_handle;
|
||
|
|
||
|
gles2_ctx->context->glBindFramebuffer (GL_FRAMEBUFFER,
|
||
|
read_fbo_handle);
|
||
|
|
||
|
return RESTORE_FB_FROM_OFFSCREEN;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
_cogl_gl_framebuffer_bind (gles2_ctx->read_buffer,
|
||
|
0 /* target ignored */);
|
||
|
|
||
|
return RESTORE_FB_FROM_ONSCREEN;
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
return RESTORE_FB_NONE;
|
||
|
}
|
||
|
|
||
|
static void
|
||
|
restore_write_buffer (CoglGLES2Context *gles2_ctx,
|
||
|
int restore_mode)
|
||
|
{
|
||
|
switch (restore_mode)
|
||
|
{
|
||
|
case RESTORE_FB_FROM_OFFSCREEN:
|
||
|
|
||
|
gl_bind_framebuffer_wrapper (GL_FRAMEBUFFER, 0);
|
||
|
|
||
|
break;
|
||
|
case RESTORE_FB_FROM_ONSCREEN:
|
||
|
|
||
|
/* Note: we can't restore the original write buffer using
|
||
|
* _cogl_gl_framebuffer_bind() if it's an offscreen
|
||
|
* framebuffer because _cogl_gl_framebuffer_bind() doesn't
|
||
|
* know about the fbo handle owned by the gles2 context.
|
||
|
*/
|
||
|
if (cogl_is_offscreen (gles2_ctx->write_buffer))
|
||
|
gl_bind_framebuffer_wrapper (GL_FRAMEBUFFER, 0);
|
||
|
else
|
||
|
_cogl_gl_framebuffer_bind (gles2_ctx->write_buffer, GL_FRAMEBUFFER);
|
||
|
|
||
|
break;
|
||
|
case RESTORE_FB_NONE:
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/* We wrap glReadPixels so when framebuffer 0 is bound then we can
|
||
|
* read from the read_framebuffer passed to cogl_push_gles2_context().
|
||
|
*/
|
||
|
static void
|
||
|
gl_read_pixels_wrapper (GLint x,
|
||
|
GLint y,
|
||
|
GLsizei width,
|
||
|
GLsizei height,
|
||
|
GLenum format,
|
||
|
GLenum type,
|
||
|
GLvoid *pixels)
|
||
|
{
|
||
|
CoglGLES2Context *gles2_ctx = current_gles2_context;
|
||
|
int restore_mode = transient_bind_read_buffer (gles2_ctx);
|
||
|
|
||
|
gles2_ctx->context->glReadPixels (x, y, width, height, format, type, pixels);
|
||
|
|
||
|
restore_write_buffer (gles2_ctx, restore_mode);
|
||
|
}
|
||
|
|
||
|
static void
|
||
|
gl_copy_tex_image_2d_wrapper (GLenum target,
|
||
|
GLint level,
|
||
|
GLenum internalformat,
|
||
|
GLint x,
|
||
|
GLint y,
|
||
|
GLsizei width,
|
||
|
GLsizei height,
|
||
|
GLint border)
|
||
|
{
|
||
|
CoglGLES2Context *gles2_ctx = current_gles2_context;
|
||
|
int restore_mode = transient_bind_read_buffer (gles2_ctx);
|
||
|
|
||
|
gles2_ctx->context->glCopyTexImage2D (target, level, internalformat,
|
||
|
x, y, width, height, border);
|
||
|
|
||
|
restore_write_buffer (gles2_ctx, restore_mode);
|
||
|
}
|
||
|
|
||
|
static void
|
||
|
gl_copy_tex_sub_image_2d_wrapper (GLenum target,
|
||
|
GLint level,
|
||
|
GLint xoffset,
|
||
|
GLint yoffset,
|
||
|
GLint x,
|
||
|
GLint y,
|
||
|
GLsizei width,
|
||
|
GLsizei height)
|
||
|
{
|
||
|
CoglGLES2Context *gles2_ctx = current_gles2_context;
|
||
|
int restore_mode = transient_bind_read_buffer (gles2_ctx);
|
||
|
|
||
|
gles2_ctx->context->glCopyTexSubImage2D (target, level,
|
||
|
xoffset, yoffset,
|
||
|
x, y, width, height);
|
||
|
|
||
|
restore_write_buffer (gles2_ctx, restore_mode);
|
||
|
}
|
||
|
|
||
|
static void
|
||
|
_cogl_gles2_offscreen_free (CoglGLES2Offscreen *gles2_offscreen)
|
||
|
{
|
||
|
COGL_LIST_REMOVE (gles2_offscreen, list_node);
|
||
|
g_slice_free (CoglGLES2Offscreen, gles2_offscreen);
|
||
|
}
|
||
|
|
||
|
static void
|
||
|
_cogl_gles2_context_free (CoglGLES2Context *gles2_context)
|
||
|
{
|
||
|
CoglContext *ctx = gles2_context->context;
|
||
|
const CoglWinsysVtable *winsys;
|
||
|
|
||
|
winsys = ctx->display->renderer->winsys_vtable;
|
||
|
winsys->destroy_gles2_context (gles2_context);
|
||
|
|
||
|
while (gles2_context->foreign_offscreens.lh_first)
|
||
|
{
|
||
|
CoglGLES2Offscreen *gles2_offscreen =
|
||
|
gles2_context->foreign_offscreens.lh_first;
|
||
|
|
||
|
/* Note: this will also indirectly free the gles2_offscreen by
|
||
|
* calling the destroy notify for the _user_data */
|
||
|
cogl_object_set_user_data (COGL_OBJECT (gles2_offscreen->original_offscreen),
|
||
|
&offscreen_wrapper_key,
|
||
|
NULL,
|
||
|
NULL);
|
||
|
}
|
||
|
|
||
|
cogl_object_unref (gles2_context->context);
|
||
|
|
||
|
g_free (gles2_context->vtable);
|
||
|
|
||
|
g_free (gles2_context);
|
||
|
}
|
||
|
|
||
|
CoglGLES2Context *
|
||
|
cogl_gles2_context_new (CoglContext *ctx, GError **error)
|
||
|
{
|
||
|
CoglGLES2Context *gles2_ctx;
|
||
|
const CoglWinsysVtable *winsys;
|
||
|
|
||
|
if (!cogl_has_feature (ctx, COGL_FEATURE_ID_GLES2_CONTEXT))
|
||
|
{
|
||
|
g_set_error (error, COGL_GLES2_CONTEXT_ERROR,
|
||
|
COGL_GLES2_CONTEXT_ERROR_UNSUPPORTED,
|
||
|
"Backend doesn't support creating GLES2 contexts");
|
||
|
|
||
|
return NULL;
|
||
|
}
|
||
|
|
||
|
gles2_ctx = g_malloc0 (sizeof (CoglGLES2Context));
|
||
|
|
||
|
cogl_object_ref (ctx);
|
||
|
gles2_ctx->context = ctx;
|
||
|
|
||
|
COGL_LIST_INIT (&gles2_ctx->foreign_offscreens);
|
||
|
|
||
|
winsys = ctx->display->renderer->winsys_vtable;
|
||
|
gles2_ctx->winsys = winsys->context_create_gles2_context (ctx, error);
|
||
|
if (gles2_ctx->winsys == NULL)
|
||
|
{
|
||
|
cogl_object_unref (gles2_ctx->context);
|
||
|
g_free (gles2_ctx);
|
||
|
return NULL;
|
||
|
}
|
||
|
|
||
|
gles2_ctx->vtable = g_malloc0 (sizeof (CoglGLES2Vtable));
|
||
|
#define COGL_EXT_BEGIN(name, \
|
||
|
min_gl_major, min_gl_minor, \
|
||
|
gles_availability, \
|
||
|
extension_suffixes, extension_names)
|
||
|
|
||
|
#define COGL_EXT_FUNCTION(ret, name, args) \
|
||
|
gles2_ctx->vtable->name = ctx->name;
|
||
|
|
||
|
#define COGL_EXT_END()
|
||
|
|
||
|
#include "gl-prototypes/cogl-gles2-functions.h"
|
||
|
|
||
|
#undef COGL_EXT_BEGIN
|
||
|
#undef COGL_EXT_FUNCTION
|
||
|
#undef COGL_EXT_END
|
||
|
|
||
|
gles2_ctx->vtable->glBindFramebuffer = gl_bind_framebuffer_wrapper;
|
||
|
gles2_ctx->vtable->glReadPixels = gl_read_pixels_wrapper;
|
||
|
gles2_ctx->vtable->glCopyTexImage2D = gl_copy_tex_image_2d_wrapper;
|
||
|
gles2_ctx->vtable->glCopyTexSubImage2D = gl_copy_tex_sub_image_2d_wrapper;
|
||
|
|
||
|
return _cogl_gles2_context_object_new (gles2_ctx);
|
||
|
}
|
||
|
|
||
|
const CoglGLES2Vtable *
|
||
|
cogl_gles2_context_get_vtable (CoglGLES2Context *gles2_ctx)
|
||
|
{
|
||
|
return gles2_ctx->vtable;
|
||
|
}
|
||
|
|
||
|
/* When drawing to a CoglFramebuffer from a separate context we have
|
||
|
* to be able to allocate ancillary buffers for that context...
|
||
|
*/
|
||
|
static CoglGLES2Offscreen *
|
||
|
_cogl_gles2_offscreen_allocate (CoglOffscreen *offscreen,
|
||
|
CoglGLES2Context *gles2_context,
|
||
|
GError **error)
|
||
|
{
|
||
|
CoglFramebuffer *framebuffer = COGL_FRAMEBUFFER (offscreen);
|
||
|
const CoglWinsysVtable *winsys;
|
||
|
GError *internal_error = NULL;
|
||
|
CoglGLES2Offscreen *gles2_offscreen;
|
||
|
|
||
|
if (!framebuffer->allocated &&
|
||
|
!cogl_framebuffer_allocate (framebuffer, error))
|
||
|
{
|
||
|
return NULL;
|
||
|
}
|
||
|
|
||
|
for (gles2_offscreen = gles2_context->foreign_offscreens.lh_first;
|
||
|
gles2_offscreen;
|
||
|
gles2_offscreen = gles2_offscreen->list_node.le_next)
|
||
|
{
|
||
|
if (gles2_offscreen->original_offscreen == offscreen)
|
||
|
return gles2_offscreen;
|
||
|
}
|
||
|
|
||
|
winsys = _cogl_framebuffer_get_winsys (framebuffer);
|
||
|
winsys->save_context (framebuffer->context);
|
||
|
if (!winsys->set_gles2_context (gles2_context, &internal_error))
|
||
|
{
|
||
|
winsys->restore_context (framebuffer->context);
|
||
|
|
||
|
g_error_free (internal_error);
|
||
|
g_set_error (error, COGL_FRAMEBUFFER_ERROR,
|
||
|
COGL_FRAMEBUFFER_ERROR_ALLOCATE,
|
||
|
"Failed to bind gles2 context to create framebuffer");
|
||
|
return NULL;
|
||
|
}
|
||
|
|
||
|
gles2_offscreen = g_slice_new0 (CoglGLES2Offscreen);
|
||
|
if (!_cogl_framebuffer_try_creating_gl_fbo (gles2_context->context,
|
||
|
offscreen->texture,
|
||
|
offscreen->texture_level,
|
||
|
offscreen->texture_level_width,
|
||
|
offscreen->texture_level_height,
|
||
|
&COGL_FRAMEBUFFER (offscreen)->config,
|
||
|
offscreen->allocation_flags,
|
||
|
&gles2_offscreen->gl_framebuffer))
|
||
|
{
|
||
|
winsys->restore_context (framebuffer->context);
|
||
|
|
||
|
g_slice_free (CoglGLES2Offscreen, gles2_offscreen);
|
||
|
|
||
|
g_set_error (error, COGL_FRAMEBUFFER_ERROR,
|
||
|
COGL_FRAMEBUFFER_ERROR_ALLOCATE,
|
||
|
"Failed to create an OpenGL framebuffer object");
|
||
|
return NULL;
|
||
|
}
|
||
|
|
||
|
winsys->restore_context (framebuffer->context);
|
||
|
|
||
|
gles2_offscreen->original_offscreen = offscreen;
|
||
|
|
||
|
COGL_LIST_INSERT_HEAD (&gles2_context->foreign_offscreens,
|
||
|
gles2_offscreen,
|
||
|
list_node);
|
||
|
|
||
|
/* So we avoid building up an ever growing collection of ancillary
|
||
|
* buffers for wrapped framebuffers, we make sure that the wrappers
|
||
|
* get freed when the original offscreen framebuffer is freed. */
|
||
|
cogl_object_set_user_data (COGL_OBJECT (framebuffer),
|
||
|
&offscreen_wrapper_key,
|
||
|
gles2_offscreen,
|
||
|
(CoglUserDataDestroyCallback)
|
||
|
_cogl_gles2_offscreen_free);
|
||
|
|
||
|
return gles2_offscreen;
|
||
|
}
|
||
|
|
||
|
CoglBool
|
||
|
cogl_push_gles2_context (CoglContext *ctx,
|
||
|
CoglGLES2Context *gles2_ctx,
|
||
|
CoglFramebuffer *read_buffer,
|
||
|
CoglFramebuffer *write_buffer,
|
||
|
GError **error)
|
||
|
{
|
||
|
const CoglWinsysVtable *winsys = ctx->display->renderer->winsys_vtable;
|
||
|
GError *internal_error = NULL;
|
||
|
|
||
|
_COGL_RETURN_VAL_IF_FAIL (gles2_ctx != NULL, FALSE);
|
||
|
|
||
|
/* The read/write buffers are properties of the gles2 context and we
|
||
|
* don't currently track the read/write buffers as part of the stack
|
||
|
* entries so we explicitly don't allow the same context to be
|
||
|
* pushed multiple times. */
|
||
|
if (g_queue_find (&ctx->gles2_context_stack, gles2_ctx))
|
||
|
{
|
||
|
g_critical ("Pushing the same GLES2 context multiple times isn't "
|
||
|
"supported");
|
||
|
return FALSE;
|
||
|
}
|
||
|
|
||
|
if (ctx->gles2_context_stack.length == 0)
|
||
|
{
|
||
|
_cogl_journal_flush (read_buffer->journal);
|
||
|
if (write_buffer != read_buffer)
|
||
|
_cogl_journal_flush (write_buffer->journal);
|
||
|
winsys->save_context (ctx);
|
||
|
}
|
||
|
else
|
||
|
gles2_ctx->vtable->glFlush ();
|
||
|
|
||
|
if (gles2_ctx->read_buffer != read_buffer)
|
||
|
{
|
||
|
if (cogl_is_offscreen (read_buffer))
|
||
|
{
|
||
|
gles2_ctx->gles2_read_buffer =
|
||
|
_cogl_gles2_offscreen_allocate (COGL_OFFSCREEN (read_buffer),
|
||
|
gles2_ctx,
|
||
|
error);
|
||
|
/* XXX: what consistency guarantees should this api have?
|
||
|
*
|
||
|
* It should be safe to return at this point but we provide
|
||
|
* no guarantee to the caller whether their given buffers
|
||
|
* may be referenced and old buffers unreferenced even
|
||
|
* if the _push fails. */
|
||
|
if (!gles2_ctx->gles2_read_buffer)
|
||
|
return FALSE;
|
||
|
}
|
||
|
else
|
||
|
gles2_ctx->gles2_read_buffer = NULL;
|
||
|
if (gles2_ctx->read_buffer)
|
||
|
cogl_object_unref (gles2_ctx->read_buffer);
|
||
|
gles2_ctx->read_buffer = cogl_object_ref (read_buffer);
|
||
|
}
|
||
|
|
||
|
if (gles2_ctx->write_buffer != write_buffer)
|
||
|
{
|
||
|
if (cogl_is_offscreen (write_buffer))
|
||
|
{
|
||
|
gles2_ctx->gles2_write_buffer =
|
||
|
_cogl_gles2_offscreen_allocate (COGL_OFFSCREEN (write_buffer),
|
||
|
gles2_ctx,
|
||
|
error);
|
||
|
/* XXX: what consistency guarantees should this api have?
|
||
|
*
|
||
|
* It should be safe to return at this point but we provide
|
||
|
* no guarantee to the caller whether their given buffers
|
||
|
* may be referenced and old buffers unreferenced even
|
||
|
* if the _push fails. */
|
||
|
if (!gles2_ctx->gles2_write_buffer)
|
||
|
return FALSE;
|
||
|
}
|
||
|
else
|
||
|
gles2_ctx->gles2_write_buffer = NULL;
|
||
|
if (gles2_ctx->write_buffer)
|
||
|
cogl_object_unref (gles2_ctx->write_buffer);
|
||
|
gles2_ctx->write_buffer = cogl_object_ref (write_buffer);
|
||
|
}
|
||
|
|
||
|
if (!winsys->set_gles2_context (gles2_ctx, &internal_error))
|
||
|
{
|
||
|
winsys->restore_context (ctx);
|
||
|
|
||
|
g_error_free (internal_error);
|
||
|
g_set_error (error, COGL_GLES2_CONTEXT_ERROR,
|
||
|
COGL_GLES2_CONTEXT_ERROR_DRIVER,
|
||
|
"Driver failed to make GLES2 context current");
|
||
|
return FALSE;
|
||
|
}
|
||
|
|
||
|
g_queue_push_tail (&ctx->gles2_context_stack, gles2_ctx);
|
||
|
|
||
|
/* The last time this context was pushed may have been with a
|
||
|
* different offscreen draw framebuffer and so if GL framebuffer 0
|
||
|
* is bound for this GLES2 context we may need to bind a new,
|
||
|
* corresponding, window system framebuffer... */
|
||
|
if (gles2_ctx->current_fbo_handle == 0)
|
||
|
{
|
||
|
if (cogl_is_offscreen (gles2_ctx->write_buffer))
|
||
|
{
|
||
|
CoglGLES2Offscreen *write = gles2_ctx->gles2_write_buffer;
|
||
|
GLuint handle = write->gl_framebuffer.fbo_handle;
|
||
|
gles2_ctx->context->glBindFramebuffer (GL_FRAMEBUFFER, handle);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
current_gles2_context = gles2_ctx;
|
||
|
return TRUE;
|
||
|
}
|
||
|
|
||
|
CoglGLES2Vtable *
|
||
|
cogl_gles2_get_current_vtable (void)
|
||
|
{
|
||
|
return current_gles2_context ? current_gles2_context->vtable : NULL;
|
||
|
}
|
||
|
|
||
|
void
|
||
|
cogl_pop_gles2_context (CoglContext *ctx)
|
||
|
{
|
||
|
CoglGLES2Context *gles2_ctx;
|
||
|
const CoglWinsysVtable *winsys = ctx->display->renderer->winsys_vtable;
|
||
|
|
||
|
_COGL_RETURN_IF_FAIL (ctx->gles2_context_stack.length > 0);
|
||
|
|
||
|
g_queue_pop_tail (&ctx->gles2_context_stack);
|
||
|
|
||
|
gles2_ctx = g_queue_peek_tail (&ctx->gles2_context_stack);
|
||
|
|
||
|
if (gles2_ctx)
|
||
|
{
|
||
|
winsys->set_gles2_context (gles2_ctx, NULL);
|
||
|
current_gles2_context = gles2_ctx;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
winsys->restore_context (ctx);
|
||
|
current_gles2_context = NULL;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
CoglTexture2D *
|
||
|
cogl_gles2_texture_2d_new_from_handle (CoglContext *ctx,
|
||
|
CoglGLES2Context *gles2_ctx,
|
||
|
unsigned int handle,
|
||
|
int width,
|
||
|
int height,
|
||
|
CoglPixelFormat internal_format,
|
||
|
GError **error)
|
||
|
{
|
||
|
return cogl_texture_2d_new_from_foreign (ctx,
|
||
|
handle,
|
||
|
width,
|
||
|
height,
|
||
|
internal_format,
|
||
|
error);
|
||
|
}
|
||
|
|
||
|
CoglBool
|
||
|
cogl_gles2_texture_get_handle (CoglTexture *texture,
|
||
|
unsigned int *handle,
|
||
|
unsigned int *target)
|
||
|
{
|
||
|
return cogl_texture_get_gl_texture (texture, handle, target);
|
||
|
}
|