/*
* Cogl
*
* An object oriented GL/GLES Abstraction/Utility Layer
*
* Copyright (C) 2007,2008,2009 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 .
*
*
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include "cogl-object.h"
#include "cogl-internal.h"
#include "cogl-private.h"
#include "cogl-winsys-private.h"
#include "winsys/cogl-winsys-stub-private.h"
#include "cogl-profile.h"
#include "cogl-util.h"
#include "cogl-context-private.h"
#include "cogl-display-private.h"
#include "cogl-renderer-private.h"
#include "cogl-journal-private.h"
#include "cogl-texture-private.h"
#include "cogl-texture-2d-private.h"
#include "cogl-texture-3d-private.h"
#include "cogl-texture-rectangle-private.h"
#include "cogl-pipeline-private.h"
#include "cogl-pipeline-opengl-private.h"
#include "cogl-framebuffer-private.h"
#include "cogl-onscreen-private.h"
#include "cogl2-path.h"
#include "cogl-attribute-private.h"
#include "cogl1-context.h"
#include "cogl-gpu-info-private.h"
#include "cogl-config-private.h"
#include
#ifdef HAVE_COGL_GL
#include "cogl-pipeline-fragend-arbfp-private.h"
#endif
/* This isn't defined in the GLES headers */
#ifndef GL_POINT_SPRITE
#define GL_POINT_SPRITE 0x8861
#endif
#ifdef HAVE_COGL_GL
extern const CoglTextureDriver _cogl_texture_driver_gl;
extern const CoglDriverVtable _cogl_driver_gl;
#endif
#if defined (HAVE_COGL_GLES) || defined (HAVE_COGL_GLES2)
extern const CoglTextureDriver _cogl_texture_driver_gles;
extern const CoglDriverVtable _cogl_driver_gles;
#endif
static void _cogl_context_free (CoglContext *context);
COGL_OBJECT_DEFINE (Context, context);
extern void
_cogl_create_context_driver (CoglContext *context);
static CoglContext *_context = NULL;
static void
_cogl_init_feature_overrides (CoglContext *ctx)
{
if (G_UNLIKELY (COGL_DEBUG_ENABLED (COGL_DEBUG_DISABLE_VBOS)))
ctx->private_feature_flags &= ~COGL_PRIVATE_FEATURE_VBOS;
if (G_UNLIKELY (COGL_DEBUG_ENABLED (COGL_DEBUG_DISABLE_PBOS)))
ctx->private_feature_flags &= ~COGL_PRIVATE_FEATURE_PBOS;
if (G_UNLIKELY (COGL_DEBUG_ENABLED (COGL_DEBUG_DISABLE_ARBFP)))
{
ctx->feature_flags &= ~COGL_FEATURE_SHADERS_ARBFP;
COGL_FLAGS_SET (ctx->features, COGL_FEATURE_ID_ARBFP, FALSE);
}
if (G_UNLIKELY (COGL_DEBUG_ENABLED (COGL_DEBUG_DISABLE_GLSL)))
{
ctx->feature_flags &= ~COGL_FEATURE_SHADERS_GLSL;
COGL_FLAGS_SET (ctx->features, COGL_FEATURE_ID_GLSL, FALSE);
}
if (G_UNLIKELY (COGL_DEBUG_ENABLED (COGL_DEBUG_DISABLE_NPOT_TEXTURES)))
{
ctx->feature_flags &= ~(COGL_FEATURE_TEXTURE_NPOT |
COGL_FEATURE_TEXTURE_NPOT_BASIC |
COGL_FEATURE_TEXTURE_NPOT_MIPMAP |
COGL_FEATURE_TEXTURE_NPOT_REPEAT);
COGL_FLAGS_SET (ctx->features, COGL_FEATURE_ID_TEXTURE_NPOT, FALSE);
COGL_FLAGS_SET (ctx->features,
COGL_FEATURE_ID_TEXTURE_NPOT_BASIC, FALSE);
COGL_FLAGS_SET (ctx->features,
COGL_FEATURE_ID_TEXTURE_NPOT_MIPMAP, FALSE);
COGL_FLAGS_SET (ctx->features,
COGL_FEATURE_ID_TEXTURE_NPOT_REPEAT, FALSE);
}
}
const CoglWinsysVtable *
_cogl_context_get_winsys (CoglContext *context)
{
return context->display->renderer->winsys_vtable;
}
/* For reference: There was some deliberation over whether to have a
* constructor that could throw an exception but looking at standard
* practices with several high level OO languages including python, C++,
* C# Java and Ruby they all support exceptions in constructors and the
* general consensus appears to be that throwing an exception is neater
* than successfully constructing with an internal error status that
* would then have to be explicitly checked via some form of ::is_ok()
* method.
*/
CoglContext *
cogl_context_new (CoglDisplay *display,
GError **error)
{
CoglContext *context;
GLubyte default_texture_data[] = { 0xff, 0xff, 0xff, 0x0 };
CoglBitmap *default_texture_bitmap;
const CoglWinsysVtable *winsys;
int i;
_cogl_init ();
#ifdef COGL_ENABLE_PROFILE
/* We need to be absolutely sure that uprof has been initialized
* before calling _cogl_uprof_init. uprof_init (NULL, NULL)
* will be a NOP if it has been initialized but it will also
* mean subsequent parsing of the UProf GOptionGroup will have no
* affect.
*
* Sadly GOptionGroup based library initialization is extremely
* fragile by design because GOptionGroups have no notion of
* dependencies and so the order things are initialized isn't
* currently under tight control.
*/
uprof_init (NULL, NULL);
_cogl_uprof_init ();
#endif
/* Allocate context memory */
context = g_malloc0 (sizeof (CoglContext));
/* Convert the context into an object immediately in case any of the
code below wants to verify that the context pointer is a valid
object */
_cogl_context_object_new (context);
/* XXX: Gross hack!
* Currently everything in Cogl just assumes there is a default
* context which it can access via _COGL_GET_CONTEXT() including
* code used to construct a CoglContext. Until all of that code
* has been updated to take an explicit context argument we have
* to immediately make our pointer the default context.
*/
_context = context;
/* Init default values */
memset (context->features, 0, sizeof (context->features));
context->feature_flags = 0;
context->private_feature_flags = 0;
context->texture_types = NULL;
context->buffer_types = NULL;
context->rectangle_state = COGL_WINSYS_RECTANGLE_STATE_UNKNOWN;
memset (context->winsys_features, 0, sizeof (context->winsys_features));
if (!display)
{
CoglRenderer *renderer = cogl_renderer_new ();
if (!cogl_renderer_connect (renderer, error))
{
g_free (context);
return NULL;
}
display = cogl_display_new (renderer, NULL);
}
else
cogl_object_ref (display);
if (!cogl_display_setup (display, error))
{
cogl_object_unref (display);
g_free (context);
return NULL;
}
context->display = display;
/* This is duplicated data, but it's much more convenient to have
the driver attached to the context and the value is accessed a
lot throughout Cogl */
context->driver = display->renderer->driver;
switch (context->driver)
{
#ifdef HAVE_COGL_GL
case COGL_DRIVER_GL:
context->driver_vtable = &_cogl_driver_gl;
context->texture_driver = &_cogl_texture_driver_gl;
break;
#endif
#if defined (HAVE_COGL_GLES) || defined (HAVE_COGL_GLES2)
case COGL_DRIVER_GLES1:
case COGL_DRIVER_GLES2:
context->driver_vtable = &_cogl_driver_gles;
context->texture_driver = &_cogl_texture_driver_gles;
break;
#endif
default:
g_assert_not_reached ();
}
winsys = _cogl_context_get_winsys (context);
if (!winsys->context_init (context, error))
{
cogl_object_unref (display);
g_free (context);
return NULL;
}
context->attribute_name_states_hash =
g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free);
context->attribute_name_index_map = NULL;
context->n_attribute_names = 0;
/* The "cogl_color_in" attribute needs a deterministic name_index
* so we make sure it's the first attribute name we register */
_cogl_attribute_register_attribute_name (context, "cogl_color_in");
context->uniform_names =
g_ptr_array_new_with_free_func ((GDestroyNotify) g_free);
context->uniform_name_hash = g_hash_table_new (g_str_hash, g_str_equal);
context->n_uniform_names = 0;
/* Initialise the driver specific state */
_cogl_init_feature_overrides (context);
_context->sampler_cache = _cogl_sampler_cache_new (_context);
_cogl_pipeline_init_default_pipeline ();
_cogl_pipeline_init_default_layers ();
_cogl_pipeline_init_state_hash_functions ();
_cogl_pipeline_init_layer_state_hash_functions ();
context->current_clip_stack_valid = FALSE;
context->current_clip_stack = NULL;
context->legacy_backface_culling_enabled = FALSE;
cogl_matrix_init_identity (&context->identity_matrix);
cogl_matrix_init_identity (&context->y_flip_matrix);
cogl_matrix_scale (&context->y_flip_matrix, 1, -1, 1);
context->flushed_matrix_mode = COGL_MATRIX_MODELVIEW;
context->texture_units =
g_array_new (FALSE, FALSE, sizeof (CoglTextureUnit));
/* See cogl-pipeline.c for more details about why we leave texture unit 1
* active by default... */
context->active_texture_unit = 1;
GE (context, glActiveTexture (GL_TEXTURE1));
context->legacy_fog_state.enabled = FALSE;
context->opaque_color_pipeline = cogl_pipeline_new (context);
context->blended_color_pipeline = cogl_pipeline_new (context);
context->texture_pipeline = cogl_pipeline_new (context);
context->codegen_header_buffer = g_string_new ("");
context->codegen_source_buffer = g_string_new ("");
context->source_stack = NULL;
context->legacy_state_set = 0;
context->default_gl_texture_2d_tex = NULL;
context->default_gl_texture_3d_tex = NULL;
context->default_gl_texture_rect_tex = NULL;
context->framebuffers = NULL;
context->current_draw_buffer = NULL;
context->current_read_buffer = NULL;
context->current_draw_buffer_state_flushed = 0;
context->current_draw_buffer_changes = COGL_FRAMEBUFFER_STATE_ALL;
g_queue_init (&context->gles2_context_stack);
context->journal_flush_attributes_array =
g_array_new (TRUE, FALSE, sizeof (CoglAttribute *));
context->journal_clip_bounds = NULL;
context->polygon_vertices = g_array_new (FALSE, FALSE, sizeof (float));
context->current_pipeline = NULL;
context->current_pipeline_changes_since_flush = 0;
context->current_pipeline_skip_gl_color = FALSE;
_cogl_bitmask_init (&context->enabled_builtin_attributes);
_cogl_bitmask_init (&context->enable_builtin_attributes_tmp);
_cogl_bitmask_init (&context->enabled_texcoord_attributes);
_cogl_bitmask_init (&context->enable_texcoord_attributes_tmp);
_cogl_bitmask_init (&context->enabled_custom_attributes);
_cogl_bitmask_init (&context->enable_custom_attributes_tmp);
_cogl_bitmask_init (&context->changed_bits_tmp);
context->max_texture_units = -1;
context->max_activateable_texture_units = -1;
context->current_fragment_program_type = COGL_PIPELINE_PROGRAM_TYPE_FIXED;
context->current_vertex_program_type = COGL_PIPELINE_PROGRAM_TYPE_FIXED;
context->current_gl_program = 0;
context->current_gl_dither_enabled = TRUE;
context->current_gl_color_mask = COGL_COLOR_MASK_ALL;
context->gl_blend_enable_cache = FALSE;
context->depth_test_enabled_cache = FALSE;
context->depth_test_function_cache = COGL_DEPTH_TEST_FUNCTION_LESS;
context->depth_writing_enabled_cache = TRUE;
context->depth_range_near_cache = 0;
context->depth_range_far_cache = 1;
context->legacy_depth_test_enabled = FALSE;
context->pipeline_cache = cogl_pipeline_cache_new ();
for (i = 0; i < COGL_BUFFER_BIND_TARGET_COUNT; i++)
context->current_buffer[i] = NULL;
context->window_buffer = NULL;
context->framebuffer_stack = _cogl_create_framebuffer_stack ();
/* XXX: In this case the Clutter backend is still responsible for
* the OpenGL binding API and for creating onscreen framebuffers and
* so we have to add a dummy framebuffer to represent the backend
* owned window... */
if (_cogl_context_get_winsys (context) == _cogl_winsys_stub_get_vtable ())
{
CoglOnscreen *window = _cogl_onscreen_new ();
cogl_set_framebuffer (COGL_FRAMEBUFFER (window));
cogl_object_unref (COGL_FRAMEBUFFER (window));
}
context->current_path = cogl2_path_new ();
context->stencil_pipeline = cogl_pipeline_new (context);
context->in_begin_gl_block = FALSE;
context->quad_buffer_indices_byte = NULL;
context->quad_buffer_indices = NULL;
context->quad_buffer_indices_len = 0;
context->rectangle_byte_indices = NULL;
context->rectangle_short_indices = NULL;
context->rectangle_short_indices_len = 0;
context->texture_download_pipeline = NULL;
context->blit_texture_pipeline = NULL;
#if defined (HAVE_COGL_GL) || defined (HAVE_COGL_GLES)
if (context->driver != COGL_DRIVER_GLES2)
/* The default for GL_ALPHA_TEST is to always pass which is equivalent to
* the test being disabled therefore we assume that for all drivers there
* will be no performance impact if we always leave the test enabled which
* makes things a bit simpler for us. Under GLES2 the alpha test is
* implemented in the fragment shader so there is no enable for it
*/
GE (context, glEnable (GL_ALPHA_TEST));
#endif
_context->current_modelview_entry = NULL;
_context->current_projection_entry = NULL;
_cogl_matrix_entry_identity_init (&_context->identity_entry);
_cogl_matrix_entry_cache_init (&_context->builtin_flushed_projection);
_cogl_matrix_entry_cache_init (&_context->builtin_flushed_modelview);
default_texture_bitmap =
cogl_bitmap_new_for_data (_context,
1, 1, /* width/height */
COGL_PIXEL_FORMAT_RGBA_8888_PRE,
4, /* rowstride */
default_texture_data);
/* Create default textures used for fall backs */
context->default_gl_texture_2d_tex =
cogl_texture_2d_new_from_bitmap (default_texture_bitmap,
/* internal format */
COGL_PIXEL_FORMAT_RGBA_8888_PRE,
NULL);
/* If 3D or rectangle textures aren't supported then these should
just silently return NULL */
context->default_gl_texture_3d_tex =
cogl_texture_3d_new_from_bitmap (default_texture_bitmap,
1, /* height */
1, /* depth */
COGL_PIXEL_FORMAT_RGBA_8888_PRE,
NULL);
context->default_gl_texture_rect_tex =
cogl_texture_rectangle_new_from_bitmap (default_texture_bitmap,
COGL_PIXEL_FORMAT_RGBA_8888_PRE,
NULL);
cogl_object_unref (default_texture_bitmap);
cogl_push_source (context->opaque_color_pipeline);
context->atlases = NULL;
g_hook_list_init (&context->atlas_reorganize_callbacks, sizeof (GHook));
_context->buffer_map_fallback_array = g_byte_array_new ();
_context->buffer_map_fallback_in_use = FALSE;
/* As far as I can tell, GL_POINT_SPRITE doesn't have any effect
unless GL_COORD_REPLACE is enabled for an individual
layer. Therefore it seems like it should be ok to just leave it
enabled all the time instead of having to have a set property on
each pipeline to track whether any layers have point sprite
coords enabled. We don't need to do this for GLES2 because point
sprites are handled using a builtin varying in the shader. */
if (_context->driver != COGL_DRIVER_GLES2 &&
cogl_has_feature (context, COGL_FEATURE_ID_POINT_SPRITE))
GE (context, glEnable (GL_POINT_SPRITE));
return context;
}
static void
_cogl_context_free (CoglContext *context)
{
const CoglWinsysVtable *winsys = _cogl_context_get_winsys (context);
winsys->context_deinit (context);
_cogl_free_framebuffer_stack (context->framebuffer_stack);
if (context->current_path)
cogl_handle_unref (context->current_path);
if (context->default_gl_texture_2d_tex)
cogl_object_unref (context->default_gl_texture_2d_tex);
if (context->default_gl_texture_3d_tex)
cogl_object_unref (context->default_gl_texture_3d_tex);
if (context->default_gl_texture_rect_tex)
cogl_object_unref (context->default_gl_texture_rect_tex);
if (context->opaque_color_pipeline)
cogl_object_unref (context->opaque_color_pipeline);
if (context->blended_color_pipeline)
cogl_object_unref (context->blended_color_pipeline);
if (context->texture_pipeline)
cogl_object_unref (context->texture_pipeline);
if (context->blit_texture_pipeline)
cogl_object_unref (context->blit_texture_pipeline);
g_warn_if_fail (context->gles2_context_stack.length == 0);
if (context->journal_flush_attributes_array)
g_array_free (context->journal_flush_attributes_array, TRUE);
if (context->journal_clip_bounds)
g_array_free (context->journal_clip_bounds, TRUE);
if (context->polygon_vertices)
g_array_free (context->polygon_vertices, TRUE);
if (context->quad_buffer_indices_byte)
cogl_object_unref (context->quad_buffer_indices_byte);
if (context->quad_buffer_indices)
cogl_object_unref (context->quad_buffer_indices);
if (context->rectangle_byte_indices)
cogl_object_unref (context->rectangle_byte_indices);
if (context->rectangle_short_indices)
cogl_object_unref (context->rectangle_short_indices);
if (context->default_pipeline)
cogl_object_unref (context->default_pipeline);
if (context->dummy_layer_dependant)
cogl_object_unref (context->dummy_layer_dependant);
if (context->default_layer_n)
cogl_object_unref (context->default_layer_n);
if (context->default_layer_0)
cogl_object_unref (context->default_layer_0);
if (context->current_clip_stack_valid)
_cogl_clip_stack_unref (context->current_clip_stack);
g_slist_free (context->atlases);
g_hook_list_clear (&context->atlas_reorganize_callbacks);
_cogl_bitmask_destroy (&context->enabled_builtin_attributes);
_cogl_bitmask_destroy (&context->enable_builtin_attributes_tmp);
_cogl_bitmask_destroy (&context->enabled_texcoord_attributes);
_cogl_bitmask_destroy (&context->enable_texcoord_attributes_tmp);
_cogl_bitmask_destroy (&context->enabled_custom_attributes);
_cogl_bitmask_destroy (&context->enable_custom_attributes_tmp);
_cogl_bitmask_destroy (&context->changed_bits_tmp);
g_slist_free (context->texture_types);
g_slist_free (context->buffer_types);
if (_context->current_modelview_entry)
_cogl_matrix_entry_unref (_context->current_modelview_entry);
if (_context->current_projection_entry)
_cogl_matrix_entry_unref (_context->current_projection_entry);
_cogl_matrix_entry_cache_destroy (&context->builtin_flushed_projection);
_cogl_matrix_entry_cache_destroy (&context->builtin_flushed_modelview);
cogl_pipeline_cache_free (context->pipeline_cache);
_cogl_sampler_cache_free (context->sampler_cache);
_cogl_destroy_texture_units ();
g_ptr_array_free (context->uniform_names, TRUE);
g_hash_table_destroy (context->uniform_name_hash);
g_hash_table_destroy (context->attribute_name_states_hash);
g_array_free (context->attribute_name_index_map, TRUE);
g_byte_array_free (context->buffer_map_fallback_array, TRUE);
cogl_object_unref (context->display);
g_free (context);
}
CoglContext *
_cogl_context_get_default (void)
{
GError *error = NULL;
/* Create if doesn't exist yet */
if (_context == NULL)
{
_context = cogl_context_new (NULL, &error);
if (!_context)
{
g_warning ("Failed to create default context: %s",
error->message);
g_error_free (error);
}
}
return _context;
}
CoglDisplay *
cogl_context_get_display (CoglContext *context)
{
return context->display;
}
#ifdef COGL_HAS_EGL_SUPPORT
EGLDisplay
cogl_egl_context_get_egl_display (CoglContext *context)
{
const CoglWinsysVtable *winsys = _cogl_context_get_winsys (context);
/* This should only be called for EGL contexts */
_COGL_RETURN_VAL_IF_FAIL (winsys->context_egl_get_egl_display != NULL, NULL);
return winsys->context_egl_get_egl_display (context);
}
#endif
CoglBool
_cogl_context_update_features (CoglContext *context,
GError **error)
{
return context->driver_vtable->update_features (context, error);
}
void
_cogl_context_set_current_projection_entry (CoglContext *context,
CoglMatrixEntry *entry)
{
_cogl_matrix_entry_ref (entry);
if (context->current_projection_entry)
_cogl_matrix_entry_unref (context->current_projection_entry);
context->current_projection_entry = entry;
}
void
_cogl_context_set_current_modelview_entry (CoglContext *context,
CoglMatrixEntry *entry)
{
_cogl_matrix_entry_ref (entry);
if (context->current_modelview_entry)
_cogl_matrix_entry_unref (context->current_modelview_entry);
context->current_modelview_entry = entry;
}
const char *
_cogl_context_get_gl_extensions (CoglContext *context)
{
const char *env_disabled_extensions;
if ((env_disabled_extensions = g_getenv ("COGL_DISABLE_GL_EXTENSIONS"))
|| _cogl_config_disable_gl_extensions)
{
static CoglUserDataKey extensions_key;
const char *enabled_extensions;
char **split_enabled_extensions;
char **split_env_disabled_extensions;
char **split_conf_disabled_extensions;
char **e;
GString *result;
/* We need to return a const string so we'll attach the results
* to the CoglContext to avoid leaking the generated string.
* This string is only used rarely so we are using
* cogl_object_set_user_data instead of adding an explicit
* member to CoglContext to avoid making the struct bigger */
enabled_extensions =
cogl_object_get_user_data (COGL_OBJECT (context), &extensions_key);
if (enabled_extensions)
return enabled_extensions;
enabled_extensions = (const char *) context->glGetString (GL_EXTENSIONS);
split_enabled_extensions = g_strsplit (enabled_extensions,
" ",
0 /* no max tokens */);
if (env_disabled_extensions)
split_env_disabled_extensions =
g_strsplit (env_disabled_extensions,
",",
0 /* no max tokens */);
else
split_env_disabled_extensions = NULL;
if (_cogl_config_disable_gl_extensions)
split_conf_disabled_extensions =
g_strsplit (_cogl_config_disable_gl_extensions,
",",
0 /* no max tokens */);
else
split_conf_disabled_extensions = NULL;
result = g_string_new (NULL);
for (e = split_enabled_extensions; *e; e++)
{
char **d;
if (split_env_disabled_extensions)
for (d = split_env_disabled_extensions; *d; d++)
if (!strcmp (*e, *d))
goto disabled;
if (split_conf_disabled_extensions)
for (d = split_conf_disabled_extensions; *d; d++)
if (!strcmp (*e, *d))
goto disabled;
if (result->len > 0)
g_string_append_c (result, ' ');
g_string_append (result, *e);
disabled:
continue;
}
enabled_extensions = g_string_free (result, FALSE);
g_strfreev (split_enabled_extensions);
if (split_env_disabled_extensions)
g_strfreev (split_env_disabled_extensions);
if (split_conf_disabled_extensions)
g_strfreev (split_conf_disabled_extensions);
cogl_object_set_user_data (COGL_OBJECT (context),
&extensions_key,
(void *) enabled_extensions,
(CoglUserDataDestroyCallback) g_free);
return enabled_extensions;
}
else
return (const char *) context->glGetString (GL_EXTENSIONS);
}
const char *
_cogl_context_get_gl_version (CoglContext *context)
{
const char *version_override;
if ((version_override = g_getenv ("COGL_OVERRIDE_GL_VERSION")))
return version_override;
else if (_cogl_config_override_gl_version)
return _cogl_config_override_gl_version;
else
return (const char *) context->glGetString (GL_VERSION);
}