From cbd6951134dbd7b5e576e8570e8dc37d32add47f Mon Sep 17 00:00:00 2001 From: Robert Bragg Date: Sun, 23 Jun 2013 16:18:18 +0100 Subject: [PATCH] introduce texture loaders to make allocations lazy This introduces the internal idea of texture loaders that track the state for loading and allocating a texture. This defers a lot more work until the texture is allocated. There are several intentions to this change: - provides a means for extending how textures are allocated without requiring all the parameters to be supplied in a single _texture_new() function call. - allow us to remove the internal_format argument from all _texture_new() apis since using CoglPixelFormat is bad way of expressing the internal format constraints because it is too specific. For now the internal_format arguments haven't actually been removed but this patch does introduce replacement apis for controlling the internal format: cogl_texture_set_components() lets you specify what components your texture needs when it is allocated. cogl_texture_set_premultiplied() lets you specify whether a texture data should be interpreted as premultiplied or not. - Enable us to support asynchronous texture loading + allocation in the future. Of note, the _new_from_data() texture constructors all continue to allocate textures immediately so that existing code doesn't need to be adapted to manage the lifetime of the data being uploaded. Reviewed-by: Neil Roberts (cherry picked from commit 6a83de9ef4210f380a31f410797447b365a8d02c) Note: Compared to the original patch, the ->premultipled state for textures isn't forced to be %TRUE in _cogl_texture_init since that effectively ignores the users explicitly given internal_format which was a mistake and on master that change should have been made in the patch that followed. The gtk-doc comments for cogl_texture_set_premultiplied() and cogl_texture_set_components() have also been updated in-line with this fix. --- cogl/cogl-atlas-texture-private.h | 2 +- cogl/cogl-atlas-texture.c | 214 +++++-- cogl/cogl-atlas-texture.h | 43 +- cogl/cogl-context.c | 54 +- cogl/cogl-driver.h | 25 - cogl/cogl-sub-texture.c | 9 +- cogl/cogl-texture-2d-gl.h | 4 + cogl/cogl-texture-2d-private.h | 3 +- cogl/cogl-texture-2d-sliced-private.h | 1 + cogl/cogl-texture-2d-sliced.c | 537 ++++++++++-------- cogl/cogl-texture-2d-sliced.h | 29 +- cogl/cogl-texture-2d.c | 105 ++-- cogl/cogl-texture-2d.h | 67 ++- cogl/cogl-texture-3d.c | 524 +++++++++-------- cogl/cogl-texture-3d.h | 53 +- cogl/cogl-texture-private.h | 96 +++- cogl/cogl-texture-rectangle.c | 244 +++++--- cogl/cogl-texture-rectangle.h | 20 +- cogl/cogl-texture.c | 199 ++++++- cogl/cogl-texture.h | 90 +++ cogl/driver/gl/cogl-texture-2d-gl.c | 414 ++++++++------ cogl/driver/gl/gl/cogl-driver-gl.c | 4 - cogl/driver/gl/gles/cogl-driver-gles.c | 4 - cogl/driver/nop/cogl-driver-nop.c | 4 - cogl/driver/nop/cogl-texture-2d-nop-private.h | 16 - cogl/driver/nop/cogl-texture-2d-nop.c | 30 - cogl/winsys/cogl-texture-pixmap-x11.c | 17 +- examples/cogl-basic-video-player.c | 1 + 28 files changed, 1791 insertions(+), 1018 deletions(-) diff --git a/cogl/cogl-atlas-texture-private.h b/cogl/cogl-atlas-texture-private.h index ae5799da9..9b226699b 100644 --- a/cogl/cogl-atlas-texture-private.h +++ b/cogl/cogl-atlas-texture-private.h @@ -37,7 +37,7 @@ struct _CoglAtlasTexture /* The format that the texture is in. This isn't necessarily the same format as the atlas texture because we can store pre-multiplied and non-pre-multiplied textures together */ - CoglPixelFormat format; + CoglPixelFormat internal_format; /* The rectangle that was used to add this texture to the atlas. This includes the 1-pixel border */ diff --git a/cogl/cogl-atlas-texture.c b/cogl/cogl-atlas-texture.c index da27f0ef3..b33c716fb 100644 --- a/cogl/cogl-atlas-texture.c +++ b/cogl/cogl-atlas-texture.c @@ -384,7 +384,7 @@ _cogl_atlas_texture_migrate_out_of_atlas (CoglAtlasTexture *atlas_tex) atlas_tex->rectangle.y + 1, atlas_tex->rectangle.width - 2, atlas_tex->rectangle.height - 2, - atlas_tex->format); + atlas_tex->internal_format); /* Note: we simply silently ignore failures to migrate a texture * out (most likely due to lack of memory) and hope for the * best. @@ -515,10 +515,10 @@ _cogl_atlas_texture_set_region_with_border (CoglAtlasTexture *atlas_tex, static CoglBitmap * _cogl_atlas_texture_convert_bitmap_for_upload (CoglAtlasTexture *atlas_tex, CoglBitmap *bmp, + CoglPixelFormat internal_format, CoglBool can_convert_in_place, CoglError **error) { - CoglPixelFormat internal_format; CoglBitmap *upload_bmp; CoglBitmap *override_bmp; @@ -531,7 +531,7 @@ _cogl_atlas_texture_convert_bitmap_for_upload (CoglAtlasTexture *atlas_tex, orignal format so we do need to trigger the conversion */ internal_format = (COGL_PIXEL_FORMAT_RGBA_8888 | - (atlas_tex->format & COGL_PREMULT_BIT)); + (internal_format & COGL_PREMULT_BIT)); upload_bmp = _cogl_bitmap_convert_for_upload (bmp, internal_format, @@ -582,6 +582,7 @@ _cogl_atlas_texture_set_region (CoglTexture *tex, CoglBitmap *upload_bmp = _cogl_atlas_texture_convert_bitmap_for_upload (atlas_tex, bmp, + atlas_tex->internal_format, FALSE, /* can't convert in place */ error); @@ -619,7 +620,7 @@ _cogl_atlas_texture_get_format (CoglTexture *tex) /* We don't want to forward this on the sub-texture because it isn't the necessarily the same format. This will happen if the texture isn't pre-multiplied */ - return atlas_tex->format; + return atlas_tex->internal_format; } static GLenum @@ -645,24 +646,20 @@ _cogl_atlas_texture_can_use_format (CoglPixelFormat format) format == COGL_PIXEL_FORMAT_RGBA_8888); } -CoglAtlasTexture * -cogl_atlas_texture_new_with_size (CoglContext *ctx, - int width, - int height, - CoglPixelFormat internal_format, - CoglError **error) +static CoglAtlasTexture * +_cogl_atlas_texture_create_base (CoglContext *ctx, + int width, + int height, + CoglPixelFormat internal_format, + CoglTextureLoader *loader) { CoglAtlasTexture *atlas_tex; - /* We can't atlas zero-sized textures because it breaks the atlas - * data structure */ - _COGL_RETURN_VAL_IF_FAIL (width > 0 && height > 0, NULL); - COGL_NOTE (ATLAS, "Adding texture of size %ix%i", width, height); /* We need to allocate the texture now because we need the pointer to set as the data for the rectangle in the atlas */ - atlas_tex = g_new (CoglAtlasTexture, 1); + atlas_tex = g_new0 (CoglAtlasTexture, 1); /* Mark it as having no atlas so we don't try to unref it in _cogl_atlas_texture_post_reorganize_cb */ atlas_tex->atlas = NULL; @@ -670,27 +667,53 @@ cogl_atlas_texture_new_with_size (CoglContext *ctx, _cogl_texture_init (COGL_TEXTURE (atlas_tex), ctx, width, height, + internal_format, + loader, &cogl_atlas_texture_vtable); atlas_tex->sub_texture = NULL; - atlas_tex->format = internal_format; atlas_tex->atlas = NULL; return _cogl_atlas_texture_object_new (atlas_tex); } -static CoglBool -_cogl_atlas_texture_allocate (CoglTexture *tex, - CoglError **error) +CoglAtlasTexture * +cogl_atlas_texture_new_with_size (CoglContext *ctx, + int width, + int height, + CoglPixelFormat internal_format, + CoglError **error) { + CoglTextureLoader *loader; + + /* We can't atlas zero-sized textures because it breaks the atlas + * data structure */ + _COGL_RETURN_VAL_IF_FAIL (width > 0 && height > 0, NULL); + + loader = _cogl_texture_create_loader (); + loader->src_type = COGL_TEXTURE_SOURCE_TYPE_SIZED; + loader->src.sized.width = width; + loader->src.sized.height = height; + + return _cogl_atlas_texture_create_base (ctx, width, height, + internal_format, loader); +} + +static CoglBool +allocate_space (CoglAtlasTexture *atlas_tex, + int width, + int height, + CoglPixelFormat internal_format, + CoglError **error) +{ + CoglTexture *tex = COGL_TEXTURE (atlas_tex); CoglContext *ctx = tex->context; - CoglAtlasTexture *atlas_tex = COGL_ATLAS_TEXTURE (tex); CoglAtlas *atlas; GSList *l; /* If the texture is in a strange format then we won't use it */ - if (!_cogl_atlas_texture_can_use_format (atlas_tex->format)) + if (!_cogl_atlas_texture_can_use_format (internal_format)) { COGL_NOTE (ATLAS, "Texture can not be added because the " "format is unsupported"); @@ -718,7 +741,7 @@ _cogl_atlas_texture_allocate (CoglTexture *tex, /* Try to make some space in the atlas for the texture */ if (_cogl_atlas_reserve_space (atlas = l->data, /* Add two pixels for the border */ - tex->width + 2, tex->height + 2, + width + 2, height + 2, atlas_tex)) { cogl_object_ref (atlas); @@ -732,7 +755,7 @@ _cogl_atlas_texture_allocate (CoglTexture *tex, COGL_NOTE (ATLAS, "Created new atlas for textures: %p", atlas); if (!_cogl_atlas_reserve_space (atlas, /* Add two pixels for the border */ - tex->width + 2, tex->height + 2, + width + 2, height + 2, atlas_tex)) { /* Ok, this means we really can't add it to the atlas */ @@ -746,55 +769,73 @@ _cogl_atlas_texture_allocate (CoglTexture *tex, } } + atlas_tex->internal_format = internal_format; + atlas_tex->atlas = atlas; return TRUE; } -CoglAtlasTexture * -_cogl_atlas_texture_new_from_bitmap (CoglBitmap *bmp, - CoglPixelFormat internal_format, - CoglBool can_convert_in_place, - CoglError **error) +static CoglBool +allocate_with_size (CoglAtlasTexture *atlas_tex, + CoglTextureLoader *loader, + CoglError **error) { - CoglContext *ctx = _cogl_bitmap_get_context (bmp); - CoglAtlasTexture *atlas_tex; - CoglBitmap *upload_bmp; - int bmp_width; - int bmp_height; - CoglPixelFormat bmp_format; + CoglTexture *tex = COGL_TEXTURE (atlas_tex); + CoglPixelFormat internal_format = + _cogl_texture_determine_internal_format (tex, COGL_PIXEL_FORMAT_ANY); - _COGL_RETURN_VAL_IF_FAIL (cogl_is_bitmap (bmp), NULL); - - bmp_width = cogl_bitmap_get_width (bmp); - bmp_height = cogl_bitmap_get_height (bmp); - bmp_format = cogl_bitmap_get_format (bmp); - - internal_format = _cogl_texture_determine_internal_format (bmp_format, - internal_format); - - atlas_tex = cogl_atlas_texture_new_with_size (ctx, - bmp_width, bmp_height, - internal_format, - error); - if (!atlas_tex) - return NULL; - - if (!cogl_texture_allocate (COGL_TEXTURE (atlas_tex), error)) + if (allocate_space (atlas_tex, + loader->src.sized.width, + loader->src.sized.height, + internal_format, + error)) { - cogl_object_unref (atlas_tex); - return NULL; + _cogl_texture_set_allocated (COGL_TEXTURE (atlas_tex), + internal_format, + loader->src.sized.width, + loader->src.sized.height); + return TRUE; } + else + return FALSE; +} + +static CoglBool +allocate_from_bitmap (CoglAtlasTexture *atlas_tex, + CoglTextureLoader *loader, + CoglError **error) +{ + CoglTexture *tex = COGL_TEXTURE (atlas_tex); + CoglBitmap *bmp = loader->src.bitmap.bitmap; + CoglPixelFormat bmp_format = cogl_bitmap_get_format (bmp); + int width = cogl_bitmap_get_width (bmp); + int height = cogl_bitmap_get_height (bmp); + CoglBool can_convert_in_place = loader->src.bitmap.can_convert_in_place; + CoglPixelFormat internal_format; + CoglBitmap *upload_bmp; + + _COGL_RETURN_VAL_IF_FAIL (atlas_tex->atlas == NULL, FALSE); + + internal_format = _cogl_texture_determine_internal_format (tex, bmp_format); upload_bmp = _cogl_atlas_texture_convert_bitmap_for_upload (atlas_tex, bmp, + internal_format, can_convert_in_place, error); if (upload_bmp == NULL) + return FALSE; + + if (!allocate_space (atlas_tex, + width, + height, + internal_format, + error)) { - cogl_object_unref (atlas_tex); - return NULL; + cogl_object_unref (upload_bmp); + return FALSE; } /* Defer to set_region so that we can share the code for copying the @@ -804,19 +845,65 @@ _cogl_atlas_texture_new_from_bitmap (CoglBitmap *bmp, 0, /* src_y */ 0, /* dst_x */ 0, /* dst_y */ - bmp_width, /* dst_width */ - bmp_height, /* dst_height */ + width, /* dst_width */ + height, /* dst_height */ upload_bmp, error)) { + _cogl_atlas_texture_remove_from_atlas (atlas_tex); cogl_object_unref (upload_bmp); - cogl_object_unref (atlas_tex); - return NULL; + return FALSE; } cogl_object_unref (upload_bmp); - return atlas_tex; + _cogl_texture_set_allocated (tex, internal_format, width, height); + + return TRUE; +} + +static CoglBool +_cogl_atlas_texture_allocate (CoglTexture *tex, + CoglError **error) +{ + CoglAtlasTexture *atlas_tex = COGL_ATLAS_TEXTURE (tex); + CoglTextureLoader *loader = tex->loader; + + _COGL_RETURN_VAL_IF_FAIL (loader, FALSE); + + switch (loader->src_type) + { + case COGL_TEXTURE_SOURCE_TYPE_SIZED: + return allocate_with_size (atlas_tex, loader, error); + case COGL_TEXTURE_SOURCE_TYPE_BITMAP: + return allocate_from_bitmap (atlas_tex, loader, error); + default: + break; + } + + g_return_val_if_reached (FALSE); +} + +CoglAtlasTexture * +_cogl_atlas_texture_new_from_bitmap (CoglBitmap *bmp, + CoglPixelFormat internal_format, + CoglBool can_convert_in_place, + CoglError **error) +{ + CoglTextureLoader *loader; + + _COGL_RETURN_VAL_IF_FAIL (cogl_is_bitmap (bmp), NULL); + + loader = _cogl_texture_create_loader (); + loader->src_type = COGL_TEXTURE_SOURCE_TYPE_BITMAP; + loader->src.bitmap.bitmap = cogl_object_ref (bmp); + loader->src.bitmap.can_convert_in_place = can_convert_in_place; + + return _cogl_atlas_texture_create_base (_cogl_bitmap_get_context (bmp), + cogl_bitmap_get_width (bmp), + cogl_bitmap_get_height (bmp), + internal_format, + loader); } CoglAtlasTexture * @@ -859,6 +946,13 @@ cogl_atlas_texture_new_from_data (CoglContext *ctx, cogl_object_unref (bmp); + if (atlas_tex && + !cogl_texture_allocate (COGL_TEXTURE (atlas_tex), error)) + { + cogl_object_unref (atlas_tex); + return NULL; + } + return atlas_tex; } diff --git a/cogl/cogl-atlas-texture.h b/cogl/cogl-atlas-texture.h index 4e8623dac..53803c634 100644 --- a/cogl/cogl-atlas-texture.h +++ b/cogl/cogl-atlas-texture.h @@ -78,6 +78,11 @@ typedef struct _CoglAtlasTexture CoglAtlasTexture; * allocate the underlying storage or let Cogl automatically allocate * storage lazily. * + * The texture is still configurable until it has been allocated so + * for example you can influence the internal format of the texture + * using cogl_texture_set_components() and + * cogl_texture_set_premultiplied(). + * * This call can fail if Cogl considers the given * @internal_format incompatible with the format of its internal * atlases. @@ -109,7 +114,17 @@ cogl_atlas_texture_new_with_size (CoglContext *ctx, * represents a sub-region within one of Cogl's shared texture * atlases. * - * This call can fail if Cogl considers the given + * The storage for the texture is not allocated before this function + * returns. You can call cogl_texture_allocate() to explicitly + * allocate the underlying storage or let Cogl automatically allocate + * storage lazily. + * + * The texture is still configurable until it has been allocated so + * for example you can influence the internal format of the texture + * using cogl_texture_set_components() and + * cogl_texture_set_premultiplied(). + * + * Allocation can fail later if Cogl considers the given * @internal_format incompatible with the format of its internal * atlases. * @@ -152,7 +167,18 @@ cogl_atlas_texture_new_from_file (CoglContext *ctx, * memory. A #CoglAtlasTexture represents a sub-region within one of * Cogl's shared texture atlases. * - * This call can fail if Cogl considers the given + * This api will always immediately allocate GPU memory for the + * texture and upload the given data so that the @data pointer does + * not need to remain valid once this function returns. This means it + * is not possible to configure the texture before it is allocated. If + * you do need to configure the texture before allocation (to specify + * constraints on the internal format for example) then you can + * instead create a #CoglBitmap for your data and use + * cogl_atlas_texture_new_from_bitmap() or use + * cogl_atlas_texture_new_with_size() and then upload data using + * cogl_texture_set_data() + * + * Allocation can fail if Cogl considers the given * @internal_format incompatible with the format of its internal * atlases. * @@ -192,7 +218,18 @@ cogl_atlas_texture_new_from_data (CoglContext *ctx, * @bitmap. A #CoglAtlasTexture represents a sub-region within one of * Cogl's shared texture atlases. * - * This call can fail if Cogl considers the given + * The storage for the texture is not allocated before this function + * returns. You can call cogl_texture_allocate() to explicitly + * allocate the underlying storage or preferably let Cogl + * automatically allocate storage lazily when it may know more about + * how the texture is being used and can optimize how it is allocated. + * + * The texture is still configurable until it has been allocated so + * for example you can influence the internal format of the texture + * using cogl_texture_set_components() and + * cogl_texture_set_premultiplied(). + * + * Allocation can fail if Cogl considers the given * @internal_format incompatible with the format of its internal * atlases. * diff --git a/cogl/cogl-context.c b/cogl/cogl-context.c index a240954fa..33c2685d0 100644 --- a/cogl/cogl-context.c +++ b/cogl/cogl-context.c @@ -135,8 +135,8 @@ cogl_context_new (CoglDisplay *display, CoglError **error) { CoglContext *context; - uint8_t default_texture_data[] = { 0xff, 0xff, 0xff, 0xff }; - CoglBitmap *default_texture_bitmap; + uint8_t white_pixel[] = { 0xff, 0xff, 0xff, 0xff }; + CoglBitmap *white_pixel_bitmap; const CoglWinsysVtable *winsys; int i; CoglError *internal_error = NULL; @@ -429,41 +429,53 @@ cogl_context_new (CoglDisplay *display, _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); + cogl_texture_2d_new_from_data (context, + 1, 1, + COGL_PIXEL_FORMAT_RGBA_8888_PRE, + COGL_PIXEL_FORMAT_RGBA_8888_PRE, + 0, /* rowstride */ + white_pixel, + NULL); /* abort on error */ /* If 3D or rectangle textures aren't supported then these will * return errors that we can simply ignore. */ internal_error = 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, - &internal_error); + cogl_texture_3d_new_from_data (context, + 1, 1, 1, /* width, height, depth */ + COGL_PIXEL_FORMAT_RGBA_8888_PRE, + COGL_PIXEL_FORMAT_RGBA_8888_PRE, + 0, /* rowstride */ + 0, /* image stride */ + white_pixel, + &internal_error); if (internal_error) cogl_error_free (internal_error); + /* TODO: add cogl_texture_rectangle_new_from_data() */ + white_pixel_bitmap = + cogl_bitmap_new_for_data (context, + 1, 1, /* width/height */ + COGL_PIXEL_FORMAT_RGBA_8888_PRE, + 4, /* rowstride */ + white_pixel); + internal_error = NULL; context->default_gl_texture_rect_tex = - cogl_texture_rectangle_new_from_bitmap (default_texture_bitmap, + cogl_texture_rectangle_new_from_bitmap (white_pixel_bitmap, COGL_PIXEL_FORMAT_RGBA_8888_PRE, - &internal_error); + NULL); /* legacy error argument */ + + /* XXX: we need to allocate the texture now because the white_pixel + * data is on the stack */ + cogl_texture_allocate (COGL_TEXTURE (context->default_gl_texture_rect_tex), + &internal_error); if (internal_error) cogl_error_free (internal_error); - cogl_object_unref (default_texture_bitmap); + cogl_object_unref (white_pixel_bitmap); cogl_push_source (context->opaque_color_pipeline); diff --git a/cogl/cogl-driver.h b/cogl/cogl-driver.h index ca62c44ab..1181fc1b4 100644 --- a/cogl/cogl-driver.h +++ b/cogl/cogl-driver.h @@ -141,31 +141,6 @@ struct _CoglDriverVtable (* texture_2d_allocate) (CoglTexture *tex, CoglError **error); - /* Instantiates a new CoglTexture2D object with storage initialized - * with the contents of the given bitmap, using the specified - * internal format. - */ - CoglTexture2D * - (* texture_2d_new_from_bitmap) (CoglBitmap *bmp, - CoglPixelFormat internal_format, - CoglBool can_convert_in_place, - CoglError **error); - -#if defined (COGL_HAS_EGL_SUPPORT) && defined (EGL_KHR_image_base) - /* Instantiates a new CoglTexture2D object with storage initialized - * with the contents of the given EGL image. - * - * This is optional for drivers to support - */ - CoglTexture2D * - (* egl_texture_2d_new_from_image) (CoglContext *ctx, - int width, - int height, - CoglPixelFormat format, - EGLImageKHR image, - CoglError **error); -#endif - /* Initialize the specified region of storage of the given texture * with the contents of the specified framebuffer region */ diff --git a/cogl/cogl-sub-texture.c b/cogl/cogl-sub-texture.c index f9a356293..8f18229b7 100644 --- a/cogl/cogl-sub-texture.c +++ b/cogl/cogl-sub-texture.c @@ -235,6 +235,8 @@ cogl_sub_texture_new (CoglContext *ctx, tex = COGL_TEXTURE (sub_tex); _cogl_texture_init (tex, ctx, sub_width, sub_height, + _cogl_texture_get_format (next_texture), + NULL, /* no loader */ &cogl_sub_texture_vtable); /* If the next texture is also a sub texture we can avoid one level @@ -264,8 +266,13 @@ _cogl_sub_texture_allocate (CoglTexture *tex, CoglError **error) { CoglSubTexture *sub_tex = COGL_SUB_TEXTURE (tex); + CoglBool status = cogl_texture_allocate (sub_tex->full_texture, error); - return cogl_texture_allocate (sub_tex->full_texture, error); + _cogl_texture_set_allocated (tex, + _cogl_texture_get_format (sub_tex->full_texture), + tex->width, tex->height); + + return status; } CoglTexture * diff --git a/cogl/cogl-texture-2d-gl.h b/cogl/cogl-texture-2d-gl.h index 6d6372593..55d61c436 100644 --- a/cogl/cogl-texture-2d-gl.h +++ b/cogl/cogl-texture-2d-gl.h @@ -49,6 +49,10 @@ COGL_BEGIN_DECLS * This can be used for integrating Cogl with software using OpenGL * directly. * + * The texture is still configurable until it has been allocated so + * for example you can declare whether the texture is premultiplied + * with cogl_texture_set_premultiplied(). + * * The results are undefined for passing an invalid @gl_handle * or if @width or @height don't have the correct texture * geometry. diff --git a/cogl/cogl-texture-2d-private.h b/cogl/cogl-texture-2d-private.h index 5d193bceb..3d6c3c5b7 100644 --- a/cogl/cogl-texture-2d-private.h +++ b/cogl/cogl-texture-2d-private.h @@ -82,7 +82,8 @@ CoglTexture2D * _cogl_texture_2d_create_base (CoglContext *ctx, int width, int height, - CoglPixelFormat internal_format); + CoglPixelFormat internal_format, + CoglTextureLoader *loader); void _cogl_texture_2d_set_auto_mipmap (CoglTexture *tex, diff --git a/cogl/cogl-texture-2d-sliced-private.h b/cogl/cogl-texture-2d-sliced-private.h index 4ba30c511..f4895f95c 100644 --- a/cogl/cogl-texture-2d-sliced-private.h +++ b/cogl/cogl-texture-2d-sliced-private.h @@ -34,6 +34,7 @@ struct _CoglTexture2DSliced { CoglTexture _parent; + GArray *slice_x_spans; GArray *slice_y_spans; GArray *slice_textures; diff --git a/cogl/cogl-texture-2d-sliced.c b/cogl/cogl-texture-2d-sliced.c index e2e9f519e..61d569e3a 100644 --- a/cogl/cogl-texture-2d-sliced.c +++ b/cogl/cogl-texture-2d-sliced.c @@ -659,14 +659,30 @@ _cogl_texture_2d_sliced_gl_flush_legacy_texobj_wrap_modes (CoglTexture *tex, } } +static void +free_spans (CoglTexture2DSliced *tex_2ds) +{ + if (tex_2ds->slice_x_spans != NULL) + { + g_array_free (tex_2ds->slice_x_spans, TRUE); + tex_2ds->slice_x_spans = NULL; + } + + if (tex_2ds->slice_y_spans != NULL) + { + g_array_free (tex_2ds->slice_y_spans, TRUE); + tex_2ds->slice_y_spans = NULL; + } +} + static CoglBool -_cogl_texture_2d_sliced_setup_spans (CoglContext *ctx, - CoglTexture2DSliced *tex_2ds, - int width, - int height, - int max_waste, - CoglPixelFormat internal_format, - CoglError **error) +setup_spans (CoglContext *ctx, + CoglTexture2DSliced *tex_2ds, + int width, + int height, + int max_waste, + CoglPixelFormat internal_format, + CoglError **error) { int max_width; int max_height; @@ -754,8 +770,8 @@ _cogl_texture_2d_sliced_setup_spans (CoglContext *ctx, COGL_TEXTURE_ERROR, COGL_TEXTURE_ERROR_SIZE, "No suitable slice geometry found"); + free_spans (tex_2ds); return FALSE; - } } @@ -791,7 +807,7 @@ _cogl_texture_2d_sliced_setup_spans (CoglContext *ctx, } static void -_cogl_texture_2d_sliced_slices_free (CoglTexture2DSliced *tex_2ds) +free_slices (CoglTexture2DSliced *tex_2ds) { if (tex_2ds->slice_textures != NULL) { @@ -806,111 +822,36 @@ _cogl_texture_2d_sliced_slices_free (CoglTexture2DSliced *tex_2ds) g_array_free (tex_2ds->slice_textures, TRUE); } -} -static void -_cogl_texture_2d_sliced_free (CoglTexture2DSliced *tex_2ds) -{ - _cogl_texture_2d_sliced_slices_free (tex_2ds); - - if (tex_2ds->slice_x_spans != NULL) - g_array_free (tex_2ds->slice_x_spans, TRUE); - - if (tex_2ds->slice_y_spans != NULL) - g_array_free (tex_2ds->slice_y_spans, TRUE); - - /* Chain up */ - _cogl_texture_free (COGL_TEXTURE (tex_2ds)); + free_spans (tex_2ds); } static CoglBool -_cogl_texture_2d_sliced_init_base (CoglContext *ctx, - CoglTexture2DSliced *tex_2ds, - int width, - int height, - int max_waste, - CoglPixelFormat internal_format, - CoglError **error) +allocate_slices (CoglTexture2DSliced *tex_2ds, + int width, + int height, + int max_waste, + CoglPixelFormat internal_format, + CoglError **error) { CoglTexture *tex = COGL_TEXTURE (tex_2ds); - - _cogl_texture_init (tex, ctx, width, height, &cogl_texture_2d_sliced_vtable); - - tex_2ds->max_waste = max_waste; - tex_2ds->internal_format = internal_format; - - return _cogl_texture_2d_sliced_setup_spans (ctx, tex_2ds, - width, height, - max_waste, - internal_format, - error); -} - -CoglTexture2DSliced * -cogl_texture_2d_sliced_new_with_size (CoglContext *ctx, - int width, - int height, - int max_waste, - CoglPixelFormat internal_format) -{ - CoglTexture2DSliced *tex_2ds; - CoglError *ignore_error = NULL; - - /* Since no data, we need some internal format */ - if (internal_format == COGL_PIXEL_FORMAT_ANY) - internal_format = COGL_PIXEL_FORMAT_RGBA_8888_PRE; - - /* Init texture with empty bitmap */ - tex_2ds = g_new0 (CoglTexture2DSliced, 1); - - if (!_cogl_texture_2d_sliced_init_base (ctx, - tex_2ds, - width, height, - max_waste, - internal_format, - &ignore_error)) - { - /* In this case we failed to find any suitable slicing geometry - * for the given texture size. - * - * We don't need to do anything with the error here since it - * will be picked up on later when trying to allocate the - * texture. - */ - cogl_error_free (ignore_error); - } - - /* NB: We need to be sure that cogl_texture_is_sliced() will work - * correctly before returning since - * cogl_framebuffer_allocate() uses this api to determine - * if a texture can be rendered to which may be before the - * slices have been allocated. - */ - - return _cogl_texture_2d_sliced_object_new (tex_2ds); -} - -static CoglBool -_cogl_texture_2d_sliced_allocate (CoglTexture *tex, - CoglError **error) -{ CoglContext *ctx = tex->context; - CoglTexture2DSliced *tex_2ds = COGL_TEXTURE_2D_SLICED (tex); int n_x_slices; int n_y_slices; int n_slices; int x, y; - CoglPixelFormat format = tex_2ds->internal_format; CoglSpan *x_span; CoglSpan *y_span; - if (!tex_2ds->slice_x_spans || !tex_2ds->slice_y_spans) + tex_2ds->internal_format = internal_format; + + if (!setup_spans (ctx, tex_2ds, + width, + height, + max_waste, + internal_format, + error)) { - _cogl_set_error (error, - COGL_TEXTURE_ERROR, - COGL_TEXTURE_ERROR_SIZE, - "Couldn't find suitable slicing geometry " - "for given size"); return FALSE; } @@ -941,11 +882,11 @@ _cogl_texture_2d_sliced_allocate (CoglTexture *tex, slice = COGL_TEXTURE ( cogl_texture_2d_new_with_size (ctx, x_span->size, y_span->size, - format)); + internal_format)); g_array_append_val (tex_2ds->slice_textures, slice); if (!cogl_texture_allocate (slice, error)) { - _cogl_texture_2d_sliced_slices_free (tex_2ds); + free_slices (tex_2ds); return FALSE; } } @@ -954,6 +895,60 @@ _cogl_texture_2d_sliced_allocate (CoglTexture *tex, return TRUE; } +static void +_cogl_texture_2d_sliced_free (CoglTexture2DSliced *tex_2ds) +{ + free_slices (tex_2ds); + + /* Chain up */ + _cogl_texture_free (COGL_TEXTURE (tex_2ds)); +} + +static CoglTexture2DSliced * +_cogl_texture_2d_sliced_create_base (CoglContext *ctx, + int width, + int height, + int max_waste, + CoglPixelFormat internal_format, + CoglTextureLoader *loader) +{ + CoglTexture2DSliced *tex_2ds = g_new0 (CoglTexture2DSliced, 1); + + _cogl_texture_init (COGL_TEXTURE (tex_2ds), ctx, width, height, + internal_format, loader, + &cogl_texture_2d_sliced_vtable); + + tex_2ds->max_waste = max_waste; + + return _cogl_texture_2d_sliced_object_new (tex_2ds); +} + +CoglTexture2DSliced * +cogl_texture_2d_sliced_new_with_size (CoglContext *ctx, + int width, + int height, + int max_waste, + CoglPixelFormat internal_format) +{ + CoglTextureLoader *loader; + + /* Since no data, we need some internal format */ + if (internal_format == COGL_PIXEL_FORMAT_ANY) + internal_format = COGL_PIXEL_FORMAT_RGBA_8888_PRE; + + loader = _cogl_texture_create_loader (); + loader->src_type = COGL_TEXTURE_SOURCE_TYPE_SIZED; + loader->src.sized.width = width; + loader->src.sized.height = height; + + return _cogl_texture_2d_sliced_create_base (ctx, + width, + height, + max_waste, + internal_format, + loader); +} + CoglTexture2DSliced * _cogl_texture_2d_sliced_new_from_bitmap (CoglBitmap *bmp, int max_waste, @@ -961,61 +956,21 @@ _cogl_texture_2d_sliced_new_from_bitmap (CoglBitmap *bmp, CoglBool can_convert_in_place, CoglError **error) { - CoglContext *ctx; - CoglTexture2DSliced *tex_2ds; - CoglBitmap *upload_bmp; - int width, height; + CoglTextureLoader *loader; _COGL_RETURN_VAL_IF_FAIL (cogl_is_bitmap (bmp), NULL); - ctx = _cogl_bitmap_get_context (bmp); + loader = _cogl_texture_create_loader (); + loader->src_type = COGL_TEXTURE_SOURCE_TYPE_BITMAP; + loader->src.bitmap.bitmap = cogl_object_ref (bmp); + loader->src.bitmap.can_convert_in_place = can_convert_in_place; - width = cogl_bitmap_get_width (bmp); - height = cogl_bitmap_get_height (bmp); - - /* Create new texture and fill with loaded data */ - tex_2ds = g_new0 (CoglTexture2DSliced, 1); - - internal_format = - _cogl_texture_determine_internal_format (cogl_bitmap_get_format (bmp), - internal_format); - - upload_bmp = _cogl_bitmap_convert_for_upload (bmp, - internal_format, - can_convert_in_place, - error); - if (upload_bmp == NULL) - { - _cogl_texture_2d_sliced_free (tex_2ds); - return NULL; - } - - /* NB: we may fail to find any suitable slicing geometry for the - * given texture size. */ - if (!_cogl_texture_2d_sliced_init_base (ctx, - tex_2ds, - width, height, - max_waste, - internal_format, - error)) - goto error; - - if (!cogl_texture_allocate (COGL_TEXTURE (tex_2ds), error)) - goto error; - - if (!_cogl_texture_2d_sliced_upload_bitmap (tex_2ds, - upload_bmp, - error)) - goto error; - - cogl_object_unref (upload_bmp); - - return _cogl_texture_2d_sliced_object_new (tex_2ds); - - error: - cogl_object_unref (upload_bmp); - _cogl_texture_2d_sliced_free (tex_2ds); - return NULL; + return _cogl_texture_2d_sliced_create_base (_cogl_bitmap_get_context (bmp), + cogl_bitmap_get_width (bmp), + cogl_bitmap_get_height (bmp), + max_waste, + internal_format, + loader); } CoglTexture2DSliced * @@ -1042,85 +997,40 @@ _cogl_texture_2d_sliced_new_from_foreign (CoglContext *ctx, CoglPixelFormat format, CoglError **error) { + CoglTextureLoader *loader; + /* NOTE: width, height and internal format are not queriable * in GLES, hence such a function prototype. */ - int gl_width = 0; - int gl_height = 0; - CoglTexture2DSliced *tex_2ds; - CoglTexture *tex; - CoglSpan x_span; - CoglSpan y_span; - CoglTexture2D *tex_2d; - /* This should only be called when the texture target is 2D. If a rectangle texture is used then _cogl_texture_new_from_foreign will create a cogl_texture_rectangle instead */ _COGL_RETURN_VAL_IF_FAIL (gl_target == GL_TEXTURE_2D, NULL); - gl_width = width + x_pot_waste; - gl_height = height + y_pot_waste; + /* Assert it is a valid GL texture object */ + _COGL_RETURN_VAL_IF_FAIL (ctx->glIsTexture (gl_handle), FALSE); + + /* Validate width and height */ + _COGL_RETURN_VAL_IF_FAIL (width > 0 && height > 0, NULL); /* Validate pot waste */ - if (x_pot_waste < 0 || x_pot_waste >= width || - y_pot_waste < 0 || y_pot_waste >= height) - return NULL; + _COGL_RETURN_VAL_IF_FAIL (x_pot_waste >= 0 && x_pot_waste < width && + y_pot_waste >= 0 && y_pot_waste < height, + NULL); - tex_2d = cogl_texture_2d_new_from_foreign (ctx, - gl_target, - gl_width, - gl_height, - format, - error); + loader = _cogl_texture_create_loader (); + loader->src_type = COGL_TEXTURE_SOURCE_TYPE_GL_FOREIGN; + loader->src.gl_foreign.gl_handle = gl_handle; + loader->src.gl_foreign.width = width + x_pot_waste; + loader->src.gl_foreign.height = height + y_pot_waste; + loader->src.gl_foreign.format = format; - if (!tex_2d) - return NULL; - - /* The texture 2d backend may use a different pixel format if it - queries the actual texture so we'll refetch the format it - actually used */ - format = _cogl_texture_get_format (COGL_TEXTURE (tex_2d)); - - /* Create new texture */ - tex_2ds = g_new0 (CoglTexture2DSliced, 1); - - tex = COGL_TEXTURE (tex_2ds); - _cogl_texture_init (tex, ctx, gl_width, gl_height, - &cogl_texture_2d_sliced_vtable); - - tex_2ds->max_waste = 0; - tex_2ds->internal_format = format; - - /* Create slice arrays */ - tex_2ds->slice_x_spans = - g_array_sized_new (FALSE, FALSE, - sizeof (CoglSpan), 1); - - tex_2ds->slice_y_spans = - g_array_sized_new (FALSE, FALSE, - sizeof (CoglSpan), 1); - - tex_2ds->slice_textures = - g_array_sized_new (FALSE, FALSE, - sizeof (CoglTexture2D *), 1); - - /* Store info for a single slice */ - x_span.start = 0; - x_span.size = gl_width; - x_span.waste = x_pot_waste; - g_array_append_val (tex_2ds->slice_x_spans, x_span); - - y_span.start = 0; - y_span.size = gl_height; - y_span.waste = y_pot_waste; - g_array_append_val (tex_2ds->slice_y_spans, y_span); - - g_array_append_val (tex_2ds->slice_textures, tex_2d); - - _cogl_texture_set_allocated (COGL_TEXTURE (tex_2ds), TRUE); - - return _cogl_texture_2d_sliced_object_new (tex_2ds); + return _cogl_texture_2d_sliced_create_base (ctx, + width, + height, + 0, /* max waste */ + format, loader); } CoglTexture2DSliced * @@ -1157,6 +1067,13 @@ cogl_texture_2d_sliced_new_from_data (CoglContext *ctx, cogl_object_unref (bmp); + if (tex_2ds && + !cogl_texture_allocate (COGL_TEXTURE (tex_2ds), error)) + { + cogl_object_unref (tex_2ds); + return NULL; + } + return tex_2ds; } @@ -1187,6 +1104,175 @@ cogl_texture_2d_sliced_new_from_file (CoglContext *ctx, return tex_2ds; } +static CoglBool +allocate_with_size (CoglTexture2DSliced *tex_2ds, + CoglTextureLoader *loader, + CoglError **error) +{ + CoglTexture *tex = COGL_TEXTURE (tex_2ds); + CoglPixelFormat internal_format = + _cogl_texture_determine_internal_format (tex, COGL_PIXEL_FORMAT_ANY); + + if (allocate_slices (tex_2ds, + loader->src.sized.width, + loader->src.sized.height, + tex_2ds->max_waste, + internal_format, + error)) + { + _cogl_texture_set_allocated (COGL_TEXTURE (tex_2ds), + internal_format, + loader->src.sized.width, + loader->src.sized.height); + return TRUE; + } + else + return FALSE; +} + +static CoglBool +allocate_from_bitmap (CoglTexture2DSliced *tex_2ds, + CoglTextureLoader *loader, + CoglError **error) +{ + CoglTexture *tex = COGL_TEXTURE (tex_2ds); + CoglBitmap *bmp = loader->src.bitmap.bitmap; + int width = cogl_bitmap_get_width (bmp); + int height = cogl_bitmap_get_height (bmp); + CoglBool can_convert_in_place = loader->src.bitmap.can_convert_in_place; + CoglPixelFormat internal_format; + CoglBitmap *upload_bmp; + + _COGL_RETURN_VAL_IF_FAIL (tex_2ds->slice_textures == NULL, FALSE); + + internal_format = + _cogl_texture_determine_internal_format (tex, + cogl_bitmap_get_format (bmp)); + + upload_bmp = _cogl_bitmap_convert_for_upload (bmp, + internal_format, + can_convert_in_place, + error); + if (upload_bmp == NULL) + return FALSE; + + if (!allocate_slices (tex_2ds, + width, + height, + tex_2ds->max_waste, + internal_format, + error)) + { + cogl_object_unref (upload_bmp); + return FALSE; + } + + if (!_cogl_texture_2d_sliced_upload_bitmap (tex_2ds, + upload_bmp, + error)) + { + free_slices (tex_2ds); + cogl_object_unref (upload_bmp); + return FALSE; + } + + cogl_object_unref (upload_bmp); + + _cogl_texture_set_allocated (tex, internal_format, width, height); + + return TRUE; +} + +static CoglBool +allocate_from_gl_foreign (CoglTexture2DSliced *tex_2ds, + CoglTextureLoader *loader, + CoglError **error) +{ + CoglTexture *tex = COGL_TEXTURE (tex_2ds); + CoglContext *ctx = tex->context; + CoglPixelFormat format = loader->src.gl_foreign.format; + int gl_width = loader->src.gl_foreign.width; + int gl_height = loader->src.gl_foreign.height; + int x_pot_waste = gl_width - tex->width; + int y_pot_waste = gl_height - tex->height; + CoglSpan x_span; + CoglSpan y_span; + CoglTexture2D *tex_2d = + cogl_texture_2d_new_from_foreign (ctx, + loader->src.gl_foreign.gl_handle, + gl_width, + gl_height, + format, + error); + + if (!tex_2d) + return FALSE; + + if (!cogl_texture_allocate (COGL_TEXTURE (tex_2d), error)) + return FALSE; + + /* The texture 2d backend may use a different pixel format if it + queries the actual texture so we'll refetch the format it + actually used */ + format = _cogl_texture_get_format (tex); + + tex_2ds->internal_format = format; + + /* Create slice arrays */ + tex_2ds->slice_x_spans = + g_array_sized_new (FALSE, FALSE, sizeof (CoglSpan), 1); + + tex_2ds->slice_y_spans = + g_array_sized_new (FALSE, FALSE, sizeof (CoglSpan), 1); + + tex_2ds->slice_textures = + g_array_sized_new (FALSE, FALSE, sizeof (CoglTexture2D *), 1); + + /* Store info for a single slice */ + x_span.start = 0; + x_span.size = gl_width; + x_span.waste = x_pot_waste; + g_array_append_val (tex_2ds->slice_x_spans, x_span); + + y_span.start = 0; + y_span.size = gl_height; + y_span.waste = y_pot_waste; + g_array_append_val (tex_2ds->slice_y_spans, y_span); + + g_array_append_val (tex_2ds->slice_textures, tex_2d); + + _cogl_texture_set_allocated (tex, + format, + tex->width, + tex->height); + + return TRUE; +} + +static CoglBool +_cogl_texture_2d_sliced_allocate (CoglTexture *tex, + CoglError **error) +{ + CoglTexture2DSliced *tex_2ds = COGL_TEXTURE_2D_SLICED (tex); + CoglTextureLoader *loader = tex->loader; + + _COGL_RETURN_VAL_IF_FAIL (loader, FALSE); + + switch (loader->src_type) + { + case COGL_TEXTURE_SOURCE_TYPE_SIZED: + return allocate_with_size (tex_2ds, loader, error); + case COGL_TEXTURE_SOURCE_TYPE_BITMAP: + return allocate_from_bitmap (tex_2ds, loader, error); + case COGL_TEXTURE_SOURCE_TYPE_GL_FOREIGN: + return allocate_from_gl_foreign (tex_2ds, loader, error); + default: + break; + } + + g_return_val_if_reached (FALSE); +} + static CoglBool _cogl_texture_2d_sliced_is_foreign (CoglTexture *tex) { @@ -1215,17 +1301,10 @@ _cogl_texture_2d_sliced_is_sliced (CoglTexture *tex) { CoglTexture2DSliced *tex_2ds = COGL_TEXTURE_2D_SLICED (tex); - /* It's possible that we failed to calculate valid slicing geometry - * when initializing the texture due to the max_waste size and in - * this case we report that the texture is not sliced. - * - * In this case though we know that we will be throwing an error - * when this texture is later allocated so it shouldn't really - * matter what we report here since the texture won't be used in the - * end. - */ - if (!tex_2ds->slice_x_spans || !tex_2ds->slice_y_spans) - return FALSE; + /* It's only after allocating a sliced texture that we will know + * whether it really needed to be sliced... */ + if (!tex->allocated) + cogl_texture_allocate (tex, NULL); if (tex_2ds->slice_x_spans->len != 1 || tex_2ds->slice_y_spans->len != 1) diff --git a/cogl/cogl-texture-2d-sliced.h b/cogl/cogl-texture-2d-sliced.h index f6fdc9750..c52d5d6da 100644 --- a/cogl/cogl-texture-2d-sliced.h +++ b/cogl/cogl-texture-2d-sliced.h @@ -147,6 +147,11 @@ cogl_texture_2d_sliced_new_with_size (CoglContext *ctx, * wasted padding at the bottom and right of the textures is less than * specified. A negative @max_waste will disable slicing. * + * The storage for the texture is not allocated before this function + * returns. You can call cogl_texture_allocate() to explicitly + * allocate the underlying storage or let Cogl automatically allocate + * storage lazily. + * * It's possible for the allocation of a sliced texture to fail * later due to impossible slicing constraints if a negative * @max_waste value is given. If the given virtual texture size is @@ -206,11 +211,22 @@ cogl_texture_2d_sliced_new_from_file (CoglContext *ctx, * wasted padding at the bottom and right of the textures is less than * specified. A negative @max_waste will disable slicing. * + * This api will always immediately allocate GPU memory for all + * the required texture slices and upload the given data so that the + * @data pointer does not need to remain valid once this function + * returns. This means it is not possible to configure the texture + * before it is allocated. If you do need to configure the texture + * before allocation (to specify constraints on the internal format + * for example) then you can instead create a #CoglBitmap for your + * data and use cogl_texture_2d_sliced_new_from_bitmap() or use + * cogl_texture_2d_sliced_new_with_size() and then upload data using + * cogl_texture_set_data() + * * It's possible for the allocation of a sliced texture to fail - * later due to impossible slicing constraints if a negative - * @max_waste value is given. If the given virtual texture size is - * larger than is supported by the hardware but slicing is disabled - * the texture size would be too large to handle. + * due to impossible slicing constraints if a negative @max_waste + * value is given. If the given virtual texture size is larger than is + * supported by the hardware but slicing is disabled the texture size + * would be too large to handle. * * Return value: (transfer full): A newly created #CoglTexture2DSliced * or %NULL on failure and @error will be updated. @@ -262,6 +278,11 @@ cogl_texture_2d_sliced_new_from_data (CoglContext *ctx, * wasted padding at the bottom and right of the textures is less than * specified. A negative @max_waste will disable slicing. * + * The storage for the texture is not allocated before this function + * returns. You can call cogl_texture_allocate() to explicitly + * allocate the underlying storage or let Cogl automatically allocate + * storage lazily. + * * It's possible for the allocation of a sliced texture to fail * later due to impossible slicing constraints if a negative * @max_waste value is given. If the given virtual texture size is diff --git a/cogl/cogl-texture-2d.c b/cogl/cogl-texture-2d.c index d422a7a6d..0a85d239b 100644 --- a/cogl/cogl-texture-2d.c +++ b/cogl/cogl-texture-2d.c @@ -75,25 +75,6 @@ _cogl_texture_2d_free (CoglTexture2D *tex_2d) _cogl_texture_free (COGL_TEXTURE (tex_2d)); } -static CoglBool -_cogl_texture_2d_can_create (CoglContext *ctx, - unsigned int width, - unsigned int height, - CoglPixelFormat internal_format) -{ - /* If NPOT textures aren't supported then the size must be a power - of two */ - if (!cogl_has_feature (ctx, COGL_FEATURE_ID_TEXTURE_NPOT_BASIC) && - (!_cogl_util_is_pot (width) || - !_cogl_util_is_pot (height))) - return FALSE; - - return ctx->driver_vtable->texture_2d_can_create (ctx, - width, - height, - internal_format); -} - void _cogl_texture_2d_set_auto_mipmap (CoglTexture *tex, CoglBool value) @@ -107,20 +88,20 @@ CoglTexture2D * _cogl_texture_2d_create_base (CoglContext *ctx, int width, int height, - CoglPixelFormat internal_format) + CoglPixelFormat internal_format, + CoglTextureLoader *loader) { CoglTexture2D *tex_2d = g_new (CoglTexture2D, 1); CoglTexture *tex = COGL_TEXTURE (tex_2d); - _cogl_texture_init (tex, ctx, width, height, &cogl_texture_2d_vtable); + _cogl_texture_init (tex, ctx, width, height, internal_format, loader, + &cogl_texture_2d_vtable); tex_2d->mipmaps_dirty = TRUE; tex_2d->auto_mipmap = TRUE; tex_2d->is_foreign = FALSE; - tex_2d->internal_format = internal_format; - ctx->driver_vtable->texture_2d_init (tex_2d); return _cogl_texture_2d_object_new (tex_2d); @@ -132,13 +113,19 @@ cogl_texture_2d_new_with_size (CoglContext *ctx, int height, CoglPixelFormat internal_format) { + CoglTextureLoader *loader; + /* Since no data, we need some internal format */ if (internal_format == COGL_PIXEL_FORMAT_ANY) internal_format = COGL_PIXEL_FORMAT_RGBA_8888_PRE; - return _cogl_texture_2d_create_base (ctx, - width, height, - internal_format); + loader = _cogl_texture_create_loader (); + loader->src_type = COGL_TEXTURE_SOURCE_TYPE_SIZED; + loader->src.sized.width = width; + loader->src.sized.height = height; + + return _cogl_texture_2d_create_base (ctx, width, height, + internal_format, loader); } static CoglBool @@ -146,6 +133,7 @@ _cogl_texture_2d_allocate (CoglTexture *tex, CoglError **error) { CoglContext *ctx = tex->context; + return ctx->driver_vtable->texture_2d_allocate (tex, error); } @@ -155,32 +143,20 @@ _cogl_texture_2d_new_from_bitmap (CoglBitmap *bmp, CoglBool can_convert_in_place, CoglError **error) { - CoglContext *ctx; + CoglTextureLoader *loader; _COGL_RETURN_VAL_IF_FAIL (bmp != NULL, NULL); - ctx = _cogl_bitmap_get_context (bmp); + loader = _cogl_texture_create_loader (); + loader->src_type = COGL_TEXTURE_SOURCE_TYPE_BITMAP; + loader->src.bitmap.bitmap = cogl_object_ref (bmp); + loader->src.bitmap.can_convert_in_place = can_convert_in_place; - internal_format = - _cogl_texture_determine_internal_format (cogl_bitmap_get_format (bmp), - internal_format); - - if (!_cogl_texture_2d_can_create (ctx, - cogl_bitmap_get_width (bmp), - cogl_bitmap_get_height (bmp), - internal_format)) - { - _cogl_set_error (error, COGL_TEXTURE_ERROR, - COGL_TEXTURE_ERROR_SIZE, - "Failed to create texture 2d due to size/format" - " constraints"); - return NULL; - } - - return ctx->driver_vtable->texture_2d_new_from_bitmap (bmp, - internal_format, - can_convert_in_place, - error); + return _cogl_texture_2d_create_base (_cogl_bitmap_get_context (bmp), + cogl_bitmap_get_width (bmp), + cogl_bitmap_get_height (bmp), + internal_format, + loader); } CoglTexture2D * @@ -249,6 +225,13 @@ cogl_texture_2d_new_from_data (CoglContext *ctx, cogl_object_unref (bmp); + if (tex_2d && + !cogl_texture_allocate (COGL_TEXTURE (tex_2d), error)) + { + cogl_object_unref (tex_2d); + return NULL; + } + return tex_2d; } @@ -264,6 +247,8 @@ _cogl_egl_texture_2d_new_from_image (CoglContext *ctx, EGLImageKHR image, CoglError **error) { + CoglTextureLoader *loader; + _COGL_RETURN_VAL_IF_FAIL (_cogl_context_get_winsys (ctx)->constraints & COGL_RENDERER_CONSTRAINT_USES_EGL, NULL); @@ -273,22 +258,14 @@ _cogl_egl_texture_2d_new_from_image (CoglContext *ctx, COGL_PRIVATE_FEATURE_TEXTURE_2D_FROM_EGL_IMAGE), NULL); - if (ctx->driver_vtable->egl_texture_2d_new_from_image) - return ctx->driver_vtable->egl_texture_2d_new_from_image (ctx, - width, - height, - format, - image, - error); - else - { - _cogl_set_error (error, - COGL_SYSTEM_ERROR, - COGL_SYSTEM_ERROR_UNSUPPORTED, - "Creating 2D textures from EGL images is not " - "supported by the current driver"); - return NULL; - } + loader = _cogl_texture_create_loader (); + loader->src_type = COGL_TEXTURE_SOURCE_TYPE_EGL_IMAGE; + loader->src.egl_image.image = image; + loader->src.egl_image.width = width; + loader->src.egl_image.height = height; + loader->src.egl_image.format = format; + + return _cogl_texture_2d_create_base (ctx, width, height, format, loader); } #endif /* defined (COGL_HAS_EGL_SUPPORT) && defined (EGL_KHR_image_base) */ diff --git a/cogl/cogl-texture-2d.h b/cogl/cogl-texture-2d.h index 752675513..9b985c773 100644 --- a/cogl/cogl-texture-2d.h +++ b/cogl/cogl-texture-2d.h @@ -3,7 +3,7 @@ * * An object oriented GL/GLES Abstraction/Utility Layer * - * Copyright (C) 2011 Intel Corporation. + * Copyright (C) 2011,2013 Intel Corporation. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -75,11 +75,8 @@ cogl_is_texture_2d (void *object); * @height: Height of the texture to allocate * @internal_format: The format of the texture * - * Allocates a low-level #CoglTexture2D texture that your GPU can - * texture from directly. This is unlike sliced textures for example - * which may be comprised of multiple internal textures, or atlas - * textures where Cogl has to modify texture coordinates before they - * may be used by the GPU. + * Creates a low-level #CoglTexture2D texture with a given @width and + * @height that your GPU can texture from directly. * * The storage for the texture is not allocated before this function * returns. You can call cogl_texture_allocate() to explicitly @@ -87,6 +84,11 @@ cogl_is_texture_2d (void *object); * automatically allocate storage lazily when it may know more about * how the texture is being used and can optimize how it is allocated. * + * The texture is still configurable until it has been allocated so + * for example you can influence the internal format of the texture + * using cogl_texture_set_components() and + * cogl_texture_set_premultiplied(). + * * Many GPUs only support power of two sizes for #CoglTexture2D * textures. You can check support for non power of two textures by * checking for the %COGL_FEATURE_ID_TEXTURE_NPOT feature via @@ -116,7 +118,23 @@ cogl_texture_2d_new_with_size (CoglContext *ctx, * other than straight blending. * @error: A #CoglError to catch exceptional errors or %NULL * - * Creates a #CoglTexture2D from an image file. + * Creates a low-level #CoglTexture2D texture from an image file. + * + * The storage for the texture is not allocated before this function + * returns. You can call cogl_texture_allocate() to explicitly + * allocate the underlying storage or preferably let Cogl + * automatically allocate storage lazily when it may know more about + * how the texture is being used and can optimize how it is allocated. + * + * The texture is still configurable until it has been allocated so + * for example you can influence the internal format of the texture + * using cogl_texture_set_components() and + * cogl_texture_set_premultiplied(). + * + * Many GPUs only support power of two sizes for #CoglTexture2D + * textures. You can check support for non power of two textures by + * checking for the %COGL_FEATURE_ID_TEXTURE_NPOT feature via + * cogl_has_feature(). * * Return value: (transfer full): A newly created #CoglTexture2D or %NULL on failure * and @error will be updated. @@ -149,10 +167,19 @@ cogl_texture_2d_new_from_file (CoglContext *ctx, * @data: pointer the memory region where the source buffer resides * @error: A #CoglError for exceptions * - * Creates a new #CoglTexture2D texture based on data residing in memory. - * These are unlike sliced textures for example which may be comprised - * of multiple internal textures, or atlas textures where Cogl has to - * modify texture coordinates before they may be used by the GPU. + * Creates a low-level #CoglTexture2D texture based on data residing + * in memory. + * + * This api will always immediately allocate GPU memory for the + * texture and upload the given data so that the @data pointer does + * not need to remain valid once this function returns. This means it + * is not possible to configure the texture before it is allocated. If + * you do need to configure the texture before allocation (to specify + * constraints on the internal format for example) then you can + * instead create a #CoglBitmap for your data and use + * cogl_texture_2d_new_from_bitmap() or use + * cogl_texture_2d_new_with_size() and then upload data using + * cogl_texture_set_data() * * Many GPUs only support power of two sizes for #CoglTexture2D * textures. You can check support for non power of two textures by @@ -189,11 +216,19 @@ cogl_texture_2d_new_from_data (CoglContext *ctx, * something other than straight blending. * @error: A #CoglError for exceptions * - * Creates a new #CoglTexture2D texture based on data residing in a - * bitmap. These are unlike sliced textures for example which may be - * comprised of multiple internal textures, or atlas textures where - * Cogl has to modify texture coordinates before they may be used by - * the GPU. + * Creates a low-level #CoglTexture2D texture based on data residing + * in a #CoglBitmap. + * + * The storage for the texture is not allocated before this function + * returns. You can call cogl_texture_allocate() to explicitly + * allocate the underlying storage or preferably let Cogl + * automatically allocate storage lazily when it may know more about + * how the texture is being used and can optimize how it is allocated. + * + * The texture is still configurable until it has been allocated so + * for example you can influence the internal format of the texture + * using cogl_texture_set_components() and + * cogl_texture_set_premultiplied(). * * Many GPUs only support power of two sizes for #CoglTexture2D * textures. You can check support for non power of two textures by diff --git a/cogl/cogl-texture-3d.c b/cogl/cogl-texture-3d.c index c4d4679e9..5f283b852 100644 --- a/cogl/cogl-texture-3d.c +++ b/cogl/cogl-texture-3d.c @@ -116,12 +116,14 @@ _cogl_texture_3d_create_base (CoglContext *ctx, int width, int height, int depth, - CoglPixelFormat internal_format) + CoglPixelFormat internal_format, + CoglTextureLoader *loader) { CoglTexture3D *tex_3d = g_new (CoglTexture3D, 1); CoglTexture *tex = COGL_TEXTURE (tex_3d); - _cogl_texture_init (tex, ctx, width, height, &cogl_texture_3d_vtable); + _cogl_texture_init (tex, ctx, width, height, + internal_format, loader, &cogl_texture_3d_vtable); tex_3d->gl_texture = 0; @@ -138,72 +140,9 @@ _cogl_texture_3d_create_base (CoglContext *ctx, tex_3d->gl_legacy_texobj_wrap_mode_t = GL_FALSE; tex_3d->gl_legacy_texobj_wrap_mode_p = GL_FALSE; - tex_3d->internal_format = internal_format; - return _cogl_texture_3d_object_new (tex_3d); } -static CoglBool -_cogl_texture_3d_can_create (CoglContext *ctx, - int width, - int height, - int depth, - CoglPixelFormat internal_format, - CoglError **error) -{ - GLenum gl_intformat; - GLenum gl_type; - - /* This should only happen on GLES */ - if (!cogl_has_feature (ctx, COGL_FEATURE_ID_TEXTURE_3D)) - { - _cogl_set_error (error, - COGL_SYSTEM_ERROR, - COGL_SYSTEM_ERROR_UNSUPPORTED, - "3D textures are not supported by the GPU"); - return FALSE; - } - - /* If NPOT textures aren't supported then the size must be a power - of two */ - if (!cogl_has_feature (ctx, COGL_FEATURE_ID_TEXTURE_NPOT) && - (!_cogl_util_is_pot (width) || - !_cogl_util_is_pot (height) || - !_cogl_util_is_pot (depth))) - { - _cogl_set_error (error, - COGL_SYSTEM_ERROR, - COGL_SYSTEM_ERROR_UNSUPPORTED, - "A non-power-of-two size was requested but this is not " - "supported by the GPU"); - return FALSE; - } - - ctx->driver_vtable->pixel_format_to_gl (ctx, - internal_format, - &gl_intformat, - NULL, - &gl_type); - - /* Check that the driver can create a texture with that size */ - if (!ctx->texture_driver->size_supported_3d (ctx, - GL_TEXTURE_3D, - gl_intformat, - gl_type, - width, - height, - depth)) - { - _cogl_set_error (error, - COGL_SYSTEM_ERROR, - COGL_SYSTEM_ERROR_UNSUPPORTED, - "The requested dimensions are not supported by the GPU"); - return FALSE; - } - - return TRUE; -} - CoglTexture3D * cogl_texture_3d_new_with_size (CoglContext *ctx, int width, @@ -211,177 +150,46 @@ cogl_texture_3d_new_with_size (CoglContext *ctx, int depth, CoglPixelFormat internal_format) { + CoglTextureLoader *loader; + /* Since no data, we need some internal format */ if (internal_format == COGL_PIXEL_FORMAT_ANY) internal_format = COGL_PIXEL_FORMAT_RGBA_8888_PRE; - return _cogl_texture_3d_create_base (ctx, - width, height, depth, - internal_format); -} + loader = _cogl_texture_create_loader (); + loader->src_type = COGL_TEXTURE_SOURCE_TYPE_SIZED; + loader->src.sized.width = width; + loader->src.sized.height = height; + loader->src.sized.depth = depth; -static CoglBool -_cogl_texture_3d_allocate (CoglTexture *tex, - CoglError **error) -{ - CoglContext *ctx = tex->context; - CoglTexture3D *tex_3d = COGL_TEXTURE_3D (tex); - GLenum gl_intformat; - GLenum gl_format; - GLenum gl_type; - GLenum gl_error; - GLenum gl_texture; - - if (!_cogl_texture_3d_can_create (ctx, - tex->width, - tex->height, - tex_3d->depth, - tex_3d->internal_format, - error)) - return FALSE; - - ctx->driver_vtable->pixel_format_to_gl (ctx, - tex_3d->internal_format, - &gl_intformat, - &gl_format, - &gl_type); - - gl_texture = - ctx->texture_driver->gen (ctx, GL_TEXTURE_3D, tex_3d->internal_format); - _cogl_bind_gl_texture_transient (GL_TEXTURE_3D, - gl_texture, - FALSE); - /* Clear any GL errors */ - while ((gl_error = ctx->glGetError ()) != GL_NO_ERROR) - ; - - ctx->glTexImage3D (GL_TEXTURE_3D, 0, gl_intformat, - tex->width, tex->height, tex_3d->depth, - 0, gl_format, gl_type, NULL); - - if (_cogl_gl_util_catch_out_of_memory (ctx, error)) - { - GE( ctx, glDeleteTextures (1, &gl_texture) ); - return FALSE; - } - - tex_3d->gl_texture = gl_texture; - tex_3d->gl_format = gl_intformat; - - return TRUE; + return _cogl_texture_3d_create_base (ctx, width, height, depth, + internal_format, loader); } CoglTexture3D * cogl_texture_3d_new_from_bitmap (CoglBitmap *bmp, - unsigned int height, - unsigned int depth, + int height, + int depth, CoglPixelFormat internal_format, CoglError **error) { - CoglTexture3D *tex_3d; - CoglBitmap *upload_bmp; - CoglPixelFormat bmp_format; - CoglPixelFormat upload_format; - unsigned int bmp_width; - GLenum gl_intformat; - GLenum gl_format; - GLenum gl_type; - CoglContext *ctx; + CoglTextureLoader *loader; - ctx = _cogl_bitmap_get_context (bmp); + _COGL_RETURN_VAL_IF_FAIL (bmp, NULL); - bmp_width = cogl_bitmap_get_width (bmp); - bmp_format = cogl_bitmap_get_format (bmp); + loader = _cogl_texture_create_loader (); + loader->src_type = COGL_TEXTURE_SOURCE_TYPE_BITMAP; + loader->src.bitmap.bitmap = cogl_object_ref (bmp); + loader->src.bitmap.height = height; + loader->src.bitmap.depth = depth; + loader->src.bitmap.can_convert_in_place = FALSE; /* TODO add api for this */ - internal_format = _cogl_texture_determine_internal_format (bmp_format, - internal_format); - - if (!_cogl_texture_3d_can_create (ctx, - bmp_width, height, depth, - internal_format, - error)) - return NULL; - - upload_bmp = - _cogl_bitmap_convert_for_upload (bmp, - internal_format, - FALSE, /* can't convert in place */ - error); - if (upload_bmp == NULL) - return NULL; - - upload_format = cogl_bitmap_get_format (upload_bmp); - - ctx->driver_vtable->pixel_format_to_gl (ctx, - upload_format, - NULL, /* internal format */ - &gl_format, - &gl_type); - ctx->driver_vtable->pixel_format_to_gl (ctx, - internal_format, - &gl_intformat, - NULL, - NULL); - - tex_3d = _cogl_texture_3d_create_base (ctx, - bmp_width, height, depth, - internal_format); - - /* Keep a copy of the first pixel so that if glGenerateMipmap isn't - supported we can fallback to using GL_GENERATE_MIPMAP */ - if (!cogl_has_feature (ctx, COGL_FEATURE_ID_OFFSCREEN)) - { - CoglError *ignore = NULL; - uint8_t *data = _cogl_bitmap_map (upload_bmp, - COGL_BUFFER_ACCESS_READ, 0, - &ignore); - - tex_3d->first_pixel.gl_format = gl_format; - tex_3d->first_pixel.gl_type = gl_type; - - if (data) - { - memcpy (tex_3d->first_pixel.data, data, - _cogl_pixel_format_get_bytes_per_pixel (upload_format)); - _cogl_bitmap_unmap (upload_bmp); - } - else - { - g_warning ("Failed to read first pixel of bitmap for " - "glGenerateMipmap fallback"); - cogl_error_free (ignore); - memset (tex_3d->first_pixel.data, 0, - _cogl_pixel_format_get_bytes_per_pixel (upload_format)); - } - } - - tex_3d->gl_texture = - ctx->texture_driver->gen (ctx, GL_TEXTURE_3D, internal_format); - - if (!ctx->texture_driver->upload_to_gl_3d (ctx, - GL_TEXTURE_3D, - tex_3d->gl_texture, - FALSE, /* is_foreign */ - height, - depth, - upload_bmp, - gl_intformat, - gl_format, - gl_type, - error)) - { - cogl_object_unref (upload_bmp); - cogl_object_unref (tex_3d); - return NULL; - } - - tex_3d->gl_format = gl_intformat; - - cogl_object_unref (upload_bmp); - - _cogl_texture_set_allocated (COGL_TEXTURE (tex_3d), TRUE); - - return tex_3d; + return _cogl_texture_3d_create_base (_cogl_bitmap_get_context (bmp), + cogl_bitmap_get_width (bmp), + height, + depth, + internal_format, + loader); } CoglTexture3D * @@ -399,14 +207,8 @@ cogl_texture_3d_new_from_data (CoglContext *context, CoglBitmap *bitmap; CoglTexture3D *ret; - /* These are considered a programmer errors so we won't set a - CoglError. It would be nice if this was a _COGL_RETURN_IF_FAIL but the - rest of Cogl isn't using that */ - if (format == COGL_PIXEL_FORMAT_ANY) - return NULL; - - if (data == NULL) - return NULL; + _COGL_RETURN_VAL_IF_FAIL (data, NULL); + _COGL_RETURN_VAL_IF_FAIL (format != COGL_PIXEL_FORMAT_ANY, NULL); /* Rowstride from width if not given */ if (rowstride == 0) @@ -475,9 +277,273 @@ cogl_texture_3d_new_from_data (CoglContext *context, cogl_object_unref (bitmap); + if (ret && + !cogl_texture_allocate (COGL_TEXTURE (ret), error)) + { + cogl_object_unref (ret); + return NULL; + } + return ret; } +static CoglBool +_cogl_texture_3d_can_create (CoglContext *ctx, + int width, + int height, + int depth, + CoglPixelFormat internal_format, + CoglError **error) +{ + GLenum gl_intformat; + GLenum gl_type; + + /* This should only happen on GLES */ + if (!cogl_has_feature (ctx, COGL_FEATURE_ID_TEXTURE_3D)) + { + _cogl_set_error (error, + COGL_SYSTEM_ERROR, + COGL_SYSTEM_ERROR_UNSUPPORTED, + "3D textures are not supported by the GPU"); + return FALSE; + } + + /* If NPOT textures aren't supported then the size must be a power + of two */ + if (!cogl_has_feature (ctx, COGL_FEATURE_ID_TEXTURE_NPOT) && + (!_cogl_util_is_pot (width) || + !_cogl_util_is_pot (height) || + !_cogl_util_is_pot (depth))) + { + _cogl_set_error (error, + COGL_SYSTEM_ERROR, + COGL_SYSTEM_ERROR_UNSUPPORTED, + "A non-power-of-two size was requested but this is not " + "supported by the GPU"); + return FALSE; + } + + ctx->driver_vtable->pixel_format_to_gl (ctx, + internal_format, + &gl_intformat, + NULL, + &gl_type); + + /* Check that the driver can create a texture with that size */ + if (!ctx->texture_driver->size_supported_3d (ctx, + GL_TEXTURE_3D, + gl_intformat, + gl_type, + width, + height, + depth)) + { + _cogl_set_error (error, + COGL_SYSTEM_ERROR, + COGL_SYSTEM_ERROR_UNSUPPORTED, + "The requested dimensions are not supported by the GPU"); + return FALSE; + } + + return TRUE; +} + +static CoglBool +allocate_with_size (CoglTexture3D *tex_3d, + CoglTextureLoader *loader, + CoglError **error) +{ + CoglTexture *tex = COGL_TEXTURE (tex_3d); + CoglContext *ctx = tex->context; + CoglPixelFormat internal_format; + int width = loader->src.sized.width; + int height = loader->src.sized.height; + int depth = loader->src.sized.depth; + GLenum gl_intformat; + GLenum gl_format; + GLenum gl_type; + GLenum gl_error; + GLenum gl_texture; + + internal_format = + _cogl_texture_determine_internal_format (tex, COGL_PIXEL_FORMAT_ANY); + + if (!_cogl_texture_3d_can_create (ctx, + width, + height, + depth, + internal_format, + error)) + return FALSE; + + ctx->driver_vtable->pixel_format_to_gl (ctx, + internal_format, + &gl_intformat, + &gl_format, + &gl_type); + + gl_texture = + ctx->texture_driver->gen (ctx, GL_TEXTURE_3D, internal_format); + _cogl_bind_gl_texture_transient (GL_TEXTURE_3D, + gl_texture, + FALSE); + /* Clear any GL errors */ + while ((gl_error = ctx->glGetError ()) != GL_NO_ERROR) + ; + + ctx->glTexImage3D (GL_TEXTURE_3D, 0, gl_intformat, + width, height, depth, + 0, gl_format, gl_type, NULL); + + if (_cogl_gl_util_catch_out_of_memory (ctx, error)) + { + GE( ctx, glDeleteTextures (1, &gl_texture) ); + return FALSE; + } + + tex_3d->gl_texture = gl_texture; + tex_3d->gl_format = gl_intformat; + + tex_3d->depth = depth; + + tex_3d->internal_format = internal_format; + + _cogl_texture_set_allocated (tex, internal_format, width, height); + + return TRUE; +} + +static CoglBool +allocate_from_bitmap (CoglTexture3D *tex_3d, + CoglTextureLoader *loader, + CoglError **error) +{ + CoglTexture *tex = COGL_TEXTURE (tex_3d); + CoglContext *ctx = tex->context; + CoglPixelFormat internal_format; + CoglBitmap *bmp = loader->src.bitmap.bitmap; + int bmp_width = cogl_bitmap_get_width (bmp); + int height = loader->src.bitmap.height; + int depth = loader->src.bitmap.depth; + CoglPixelFormat bmp_format = cogl_bitmap_get_format (bmp); + CoglBool can_convert_in_place = loader->src.bitmap.can_convert_in_place; + CoglBitmap *upload_bmp; + CoglPixelFormat upload_format; + GLenum gl_intformat; + GLenum gl_format; + GLenum gl_type; + + internal_format = _cogl_texture_determine_internal_format (tex, bmp_format); + + if (!_cogl_texture_3d_can_create (ctx, + bmp_width, height, depth, + internal_format, + error)) + return FALSE; + + upload_bmp = _cogl_bitmap_convert_for_upload (bmp, + internal_format, + can_convert_in_place, + error); + if (upload_bmp == NULL) + return FALSE; + + upload_format = cogl_bitmap_get_format (upload_bmp); + + ctx->driver_vtable->pixel_format_to_gl (ctx, + upload_format, + NULL, /* internal format */ + &gl_format, + &gl_type); + ctx->driver_vtable->pixel_format_to_gl (ctx, + internal_format, + &gl_intformat, + NULL, + NULL); + + /* Keep a copy of the first pixel so that if glGenerateMipmap isn't + supported we can fallback to using GL_GENERATE_MIPMAP */ + if (!cogl_has_feature (ctx, COGL_FEATURE_ID_OFFSCREEN)) + { + CoglError *ignore = NULL; + uint8_t *data = _cogl_bitmap_map (upload_bmp, + COGL_BUFFER_ACCESS_READ, 0, + &ignore); + + tex_3d->first_pixel.gl_format = gl_format; + tex_3d->first_pixel.gl_type = gl_type; + + if (data) + { + memcpy (tex_3d->first_pixel.data, data, + _cogl_pixel_format_get_bytes_per_pixel (upload_format)); + _cogl_bitmap_unmap (upload_bmp); + } + else + { + g_warning ("Failed to read first pixel of bitmap for " + "glGenerateMipmap fallback"); + cogl_error_free (ignore); + memset (tex_3d->first_pixel.data, 0, + _cogl_pixel_format_get_bytes_per_pixel (upload_format)); + } + } + + tex_3d->gl_texture = + ctx->texture_driver->gen (ctx, GL_TEXTURE_3D, internal_format); + + if (!ctx->texture_driver->upload_to_gl_3d (ctx, + GL_TEXTURE_3D, + tex_3d->gl_texture, + FALSE, /* is_foreign */ + height, + depth, + upload_bmp, + gl_intformat, + gl_format, + gl_type, + error)) + { + cogl_object_unref (upload_bmp); + return FALSE; + } + + tex_3d->gl_format = gl_intformat; + + cogl_object_unref (upload_bmp); + + tex_3d->depth = loader->src.bitmap.depth; + + tex_3d->internal_format = internal_format; + + _cogl_texture_set_allocated (tex, internal_format, + bmp_width, loader->src.bitmap.height); + + return TRUE; +} + +static CoglBool +_cogl_texture_3d_allocate (CoglTexture *tex, + CoglError **error) +{ + CoglTexture3D *tex_3d = COGL_TEXTURE_3D (tex); + CoglTextureLoader *loader = tex->loader; + + _COGL_RETURN_VAL_IF_FAIL (loader, FALSE); + + switch (loader->src_type) + { + case COGL_TEXTURE_SOURCE_TYPE_SIZED: + return allocate_with_size (tex_3d, loader, error); + case COGL_TEXTURE_SOURCE_TYPE_BITMAP: + return allocate_from_bitmap (tex_3d, loader, error); + default: + break; + } + + g_return_val_if_reached (FALSE); +} + static int _cogl_texture_3d_get_max_waste (CoglTexture *tex) { diff --git a/cogl/cogl-texture-3d.h b/cogl/cogl-texture-3d.h index 7aca7e2e8..6ab209335 100644 --- a/cogl/cogl-texture-3d.h +++ b/cogl/cogl-texture-3d.h @@ -56,8 +56,8 @@ typedef struct _CoglTexture3D CoglTexture3D; * @internal_format: the #CoglPixelFormat to use for the GPU * storage of the texture. * - * Creates a new #CoglTexture3D texture with the specified dimensions - * and pixel format. + * Creates a low-level #CoglTexture3D texture with the specified + * dimensions and pixel format. * * The storage for the texture is not allocated before this function * returns. You can call cogl_texture_allocate() to explicitly @@ -66,6 +66,11 @@ typedef struct _CoglTexture3D CoglTexture3D; * how the texture is going to be used and can optimize how it is * allocated. * + * The texture is still configurable until it has been allocated so + * for example you can influence the internal format of the texture + * using cogl_texture_set_components() and + * cogl_texture_set_premultiplied(). + * * This texture will fail to allocate later if * %COGL_FEATURE_ID_TEXTURE_3D is not advertised. Allocation can also * fail if the requested dimensions are not supported by the @@ -107,16 +112,21 @@ cogl_texture_3d_new_with_size (CoglContext *context, * @data: pointer the memory region where the source buffer resides * @error: A CoglError return location. * - * Creates a new 3D texture and initializes it with @data. The data is - * assumed to be packed array of @depth images. There can be padding - * between the images using @image_stride. + * Creates a low-level 3D texture and initializes it with @data. The + * data is assumed to be packed array of @depth images. There can be + * padding between the images using @image_stride. * - * Note that this function will throw a #CoglError if - * %COGL_FEATURE_ID_TEXTURE_3D is not advertised. It can also fail if the - * requested dimensions are not supported by the GPU. + * This api will always immediately allocate GPU memory for the + * texture and upload the given data so that the @data pointer does + * not need to remain valid once this function returns. This means it + * is not possible to configure the texture before it is allocated. If + * you do need to configure the texture before allocation (to specify + * constraints on the internal format for example) then you can + * instead create a #CoglBitmap for your data and use + * cogl_texture_3d_new_from_bitmap(). * * Return value: (transfer full): the newly created #CoglTexture3D or - * %NULL if there was an error an an exception will be + * %NULL if there was an error and an exception will be * returned through @error. * Since: 1.10 * Stability: Unstable @@ -148,13 +158,30 @@ cogl_texture_3d_new_from_data (CoglContext *context, * something other than straight blending. * @error: A CoglError return location. * - * Creates a new 3D texture and initializes it with the images in - * @bitmap. The images are assumed to be packed together after one + * Creates a low-level 3D texture and initializes it with the images + * in @bitmap. The images are assumed to be packed together after one * another in the increasing y axis. The height of individual image is * given as @height and the number of images is given in @depth. The * actual height of the bitmap can be larger than @height × @depth. In * this case it assumes there is padding between the images. * + * The storage for the texture is not allocated before this function + * returns. You can call cogl_texture_allocate() to explicitly + * allocate the underlying storage or preferably let Cogl + * automatically allocate storage lazily when it may know more about + * how the texture is going to be used and can optimize how it is + * allocated. + * + * The texture is still configurable until it has been allocated so + * for example you can influence the internal format of the texture + * using cogl_texture_set_components() and + * cogl_texture_set_premultiplied(). + * + * This texture will fail to allocate later if + * %COGL_FEATURE_ID_TEXTURE_3D is not advertised. Allocation can also + * fail if the requested dimensions are not supported by the + * GPU. + * * Return value: (transfer full): the newly created texture or %NULL * if there was an error. * Since: 2.0 @@ -162,8 +189,8 @@ cogl_texture_3d_new_from_data (CoglContext *context, */ CoglTexture3D * cogl_texture_3d_new_from_bitmap (CoglBitmap *bitmap, - unsigned int height, - unsigned int depth, + int height, + int depth, CoglPixelFormat internal_format, CoglError **error); diff --git a/cogl/cogl-texture-private.h b/cogl/cogl-texture-private.h index a18cd31a6..db8bc96ba 100644 --- a/cogl/cogl-texture-private.h +++ b/cogl/cogl-texture-private.h @@ -31,6 +31,10 @@ #include "cogl-meta-texture.h" #include "cogl-framebuffer.h" +#ifdef COGL_HAS_EGL_SUPPORT +#include "cogl-egl-defines.h" +#endif + typedef struct _CoglTextureVtable CoglTextureVtable; /* Encodes three possibiloities result of transforming a quad */ @@ -139,15 +143,62 @@ struct _CoglTextureVtable CoglBool value); }; +typedef enum _CoglTextureSoureType { + COGL_TEXTURE_SOURCE_TYPE_SIZED = 1, + COGL_TEXTURE_SOURCE_TYPE_BITMAP, + COGL_TEXTURE_SOURCE_TYPE_EGL_IMAGE, + COGL_TEXTURE_SOURCE_TYPE_GL_FOREIGN +} CoglTextureSourceType; + +typedef struct _CoglTextureLoader +{ + CoglTextureSourceType src_type; + union { + struct { + int width; + int height; + int depth; /* for 3d textures */ + } sized; + struct { + CoglBitmap *bitmap; + int height; /* for 3d textures */ + int depth; /* for 3d textures */ + CoglBool can_convert_in_place; + } bitmap; +#if defined (COGL_HAS_EGL_SUPPORT) && defined (EGL_KHR_image_base) + struct { + EGLImageKHR image; + int width; + int height; + CoglPixelFormat format; + } egl_image; +#endif + struct { + int width; + int height; + unsigned int gl_handle; + CoglPixelFormat format; + } gl_foreign; + } src; +} CoglTextureLoader; + struct _CoglTexture { CoglObject _parent; CoglContext *context; + CoglTextureLoader *loader; GList *framebuffers; int max_level; int width; int height; CoglBool allocated; + + /* + * Internal format + */ + CoglTextureComponents components; + unsigned int premultiplied:1; + const CoglTextureVtable *vtable; }; @@ -183,6 +234,8 @@ _cogl_texture_init (CoglTexture *texture, CoglContext *ctx, int width, int height, + CoglPixelFormat internal_format, + CoglTextureLoader *loader, const CoglTextureVtable *vtable); void @@ -220,12 +273,38 @@ _cogl_texture_pre_paint (CoglTexture *texture, CoglTexturePrePaintFlags flags); void _cogl_texture_ensure_non_quad_rendering (CoglTexture *texture); -/* Utility function to determine which pixel format to use when - dst_format is COGL_PIXEL_FORMAT_ANY. If dst_format is not ANY then - it will just be returned directly */ +/* + * This determines a CoglPixelFormat according to texture::components + * and texture::premultiplied (i.e. the user required components and + * whether the texture should be considered premultiplied) + * + * A reference/source format can be given (or COGL_PIXEL_FORMAT_ANY) + * and wherever possible this function tries to simply return the + * given source format if its compatible with the required components. + * + * Texture backends can call this when allocating a texture to know + * how to convert a source image in preparation for uploading. + */ CoglPixelFormat -_cogl_texture_determine_internal_format (CoglPixelFormat src_format, - CoglPixelFormat dst_format); +_cogl_texture_determine_internal_format (CoglTexture *texture, + CoglPixelFormat src_format); + +/* This is called by texture backends when they have successfully + * allocated a texture. + * + * Most texture backends currently track the internal layout of + * textures using a CoglPixelFormat which will be finalized when a + * texture is allocated. At this point we need to update + * texture::components and texture::premultiplied according to the + * determined layout. + * + * XXX: Going forward we should probably aim to stop using + * CoglPixelFormat at all for tracking the internal layout of + * textures. + */ +void +_cogl_texture_set_internal_format (CoglTexture *texture, + CoglPixelFormat internal_format); CoglBool _cogl_texture_is_foreign (CoglTexture *texture); @@ -306,9 +385,14 @@ _cogl_texture_get_level_size (CoglTexture *texture, void _cogl_texture_set_allocated (CoglTexture *texture, - CoglBool allocated); + CoglPixelFormat internal_format, + int width, + int height); CoglPixelFormat _cogl_texture_get_format (CoglTexture *texture); +CoglTextureLoader * +_cogl_texture_create_loader (void); + #endif /* __COGL_TEXTURE_PRIVATE_H */ diff --git a/cogl/cogl-texture-rectangle.c b/cogl/cogl-texture-rectangle.c index 597d2a4db..6e5488023 100644 --- a/cogl/cogl-texture-rectangle.c +++ b/cogl/cogl-texture-rectangle.c @@ -166,12 +166,15 @@ static CoglTextureRectangle * _cogl_texture_rectangle_create_base (CoglContext *ctx, int width, int height, - CoglPixelFormat internal_format) + CoglPixelFormat internal_format, + CoglTextureLoader *loader) { CoglTextureRectangle *tex_rect = g_new (CoglTextureRectangle, 1); CoglTexture *tex = COGL_TEXTURE (tex_rect); - _cogl_texture_init (tex, ctx, width, height, &cogl_texture_rectangle_vtable); + _cogl_texture_init (tex, ctx, width, height, + internal_format, loader, + &cogl_texture_rectangle_vtable); tex_rect->gl_texture = 0; tex_rect->is_foreign = FALSE; @@ -184,8 +187,6 @@ _cogl_texture_rectangle_create_base (CoglContext *ctx, tex_rect->gl_legacy_texobj_wrap_mode_s = GL_FALSE; tex_rect->gl_legacy_texobj_wrap_mode_t = GL_FALSE; - tex_rect->internal_format = internal_format; - return _cogl_texture_rectangle_object_new (tex_rect); } @@ -196,16 +197,20 @@ cogl_texture_rectangle_new_with_size (CoglContext *ctx, CoglPixelFormat internal_format, CoglError **error) { + CoglTextureLoader *loader; CoglTextureRectangle *tex_rect; /* Since no data, we need some internal format */ if (internal_format == COGL_PIXEL_FORMAT_ANY) internal_format = COGL_PIXEL_FORMAT_RGBA_8888_PRE; - tex_rect =_cogl_texture_rectangle_create_base (ctx, - width, height, - internal_format); + loader = _cogl_texture_create_loader (); + loader->src_type = COGL_TEXTURE_SOURCE_TYPE_SIZED; + loader->src.sized.width = width; + loader->src.sized.height = height; + tex_rect = _cogl_texture_rectangle_create_base (ctx, width, height, + internal_format, loader); /* XXX: This api has been changed for Cogl 2.0 on the master branch * to not take a CoglError to allow the storage to be allocated * lazily but since Mutter uses this api we are currently @@ -216,30 +221,38 @@ cogl_texture_rectangle_new_with_size (CoglContext *ctx, cogl_object_unref (tex_rect); return NULL; } + return tex_rect; } static CoglBool -_cogl_texture_rectangle_allocate (CoglTexture *tex, - CoglError **error) +allocate_with_size (CoglTextureRectangle *tex_rect, + CoglTextureLoader *loader, + CoglError **error) { + CoglTexture *tex = COGL_TEXTURE (tex_rect); CoglContext *ctx = tex->context; - CoglTextureRectangle *tex_rect = COGL_TEXTURE_RECTANGLE (tex); + CoglPixelFormat internal_format; + int width = loader->src.sized.width; + int height = loader->src.sized.height; GLenum gl_intformat; GLenum gl_format; GLenum gl_type; GLenum gl_error; GLenum gl_texture; + internal_format = + _cogl_texture_determine_internal_format (tex, COGL_PIXEL_FORMAT_ANY); + if (!_cogl_texture_rectangle_can_create (ctx, - tex->width, - tex->height, - tex_rect->internal_format, + width, + height, + internal_format, error)) return FALSE; ctx->driver_vtable->pixel_format_to_gl (ctx, - tex_rect->internal_format, + internal_format, &gl_intformat, &gl_format, &gl_type); @@ -247,7 +260,7 @@ _cogl_texture_rectangle_allocate (CoglTexture *tex, gl_texture = ctx->texture_driver->gen (ctx, GL_TEXTURE_RECTANGLE_ARB, - tex_rect->internal_format); + internal_format); _cogl_bind_gl_texture_transient (GL_TEXTURE_RECTANGLE_ARB, gl_texture, tex_rect->is_foreign); @@ -257,7 +270,7 @@ _cogl_texture_rectangle_allocate (CoglTexture *tex, ; ctx->glTexImage2D (GL_TEXTURE_RECTANGLE_ARB, 0, gl_intformat, - tex->width, tex->height, 0, gl_format, gl_type, NULL); + width, height, 0, gl_format, gl_type, NULL); if (_cogl_gl_util_catch_out_of_memory (ctx, error)) { @@ -265,46 +278,50 @@ _cogl_texture_rectangle_allocate (CoglTexture *tex, return FALSE; } + tex_rect->internal_format = internal_format; + tex_rect->gl_texture = gl_texture; tex_rect->gl_format = gl_intformat; + _cogl_texture_set_allocated (COGL_TEXTURE (tex_rect), + internal_format, width, height); + return TRUE; } -CoglTextureRectangle * -cogl_texture_rectangle_new_from_bitmap (CoglBitmap *bmp, - CoglPixelFormat internal_format, - CoglError **error) +static CoglBool +allocate_from_bitmap (CoglTextureRectangle *tex_rect, + CoglTextureLoader *loader, + CoglError **error) { - CoglTextureRectangle *tex_rect; + CoglTexture *tex = COGL_TEXTURE (tex_rect); + CoglContext *ctx = tex->context; + CoglPixelFormat internal_format; + CoglBitmap *bmp = loader->src.bitmap.bitmap; + int width = cogl_bitmap_get_width (bmp); + int height = cogl_bitmap_get_height (bmp); + CoglBool can_convert_in_place = loader->src.bitmap.can_convert_in_place; CoglBitmap *upload_bmp; GLenum gl_intformat; GLenum gl_format; GLenum gl_type; - CoglContext *ctx; - - _COGL_RETURN_VAL_IF_FAIL (cogl_is_bitmap (bmp), NULL); - - ctx = _cogl_bitmap_get_context (bmp); internal_format = - _cogl_texture_determine_internal_format (cogl_bitmap_get_format (bmp), - internal_format); + _cogl_texture_determine_internal_format (tex, cogl_bitmap_get_format (bmp)); if (!_cogl_texture_rectangle_can_create (ctx, - cogl_bitmap_get_width (bmp), - cogl_bitmap_get_height (bmp), + width, + height, internal_format, error)) - return NULL; + return FALSE; - upload_bmp = - _cogl_bitmap_convert_for_upload (bmp, - internal_format, - FALSE, /* can't convert in place */ - error); + upload_bmp = _cogl_bitmap_convert_for_upload (bmp, + internal_format, + can_convert_in_place, + error); if (upload_bmp == NULL) - return NULL; + return FALSE; ctx->driver_vtable->pixel_format_to_gl (ctx, cogl_bitmap_get_format (upload_bmp), @@ -317,11 +334,6 @@ cogl_texture_rectangle_new_from_bitmap (CoglBitmap *bmp, NULL, NULL); - tex_rect = _cogl_texture_rectangle_create_base (ctx, - cogl_bitmap_get_width (bmp), - cogl_bitmap_get_height (bmp), - internal_format); - tex_rect->gl_texture = ctx->texture_driver->gen (ctx, GL_TEXTURE_RECTANGLE_ARB, @@ -337,38 +349,31 @@ cogl_texture_rectangle_new_from_bitmap (CoglBitmap *bmp, error)) { cogl_object_unref (upload_bmp); - cogl_object_unref (tex_rect); - return NULL; + return FALSE; } tex_rect->gl_format = gl_intformat; + tex_rect->internal_format = internal_format; cogl_object_unref (upload_bmp); - _cogl_texture_set_allocated (COGL_TEXTURE (tex_rect), TRUE); + _cogl_texture_set_allocated (COGL_TEXTURE (tex_rect), + internal_format, width, height); - return tex_rect; + return TRUE; } -CoglTextureRectangle * -cogl_texture_rectangle_new_from_foreign (CoglContext *ctx, - unsigned int gl_handle, - int width, - int height, - CoglPixelFormat format, - CoglError **error) +static CoglBool +allocate_from_gl_foreign (CoglTextureRectangle *tex_rect, + CoglTextureLoader *loader, + CoglError **error) { - /* NOTE: width, height and internal format are not queriable - * in GLES, hence such a function prototype. - */ - + CoglTexture *tex = COGL_TEXTURE (tex_rect); + CoglContext *ctx = tex->context; + CoglPixelFormat format = loader->src.gl_foreign.format; GLenum gl_error = 0; GLint gl_compressed = GL_FALSE; GLenum gl_int_format = 0; - CoglTextureRectangle *tex_rect; - - /* Assert that it is a valid GL texture object */ - g_return_val_if_fail (ctx->glIsTexture (gl_handle), NULL); if (!ctx->texture_driver->allows_foreign_gl_target (ctx, GL_TEXTURE_RECTANGLE_ARB)) @@ -378,21 +383,22 @@ cogl_texture_rectangle_new_from_foreign (CoglContext *ctx, COGL_SYSTEM_ERROR_UNSUPPORTED, "Foreign GL_TEXTURE_RECTANGLE textures are not " "supported by your system"); - return NULL; + return FALSE; } /* Make sure binding succeeds */ while ((gl_error = ctx->glGetError ()) != GL_NO_ERROR) ; - _cogl_bind_gl_texture_transient (GL_TEXTURE_RECTANGLE_ARB, gl_handle, TRUE); + _cogl_bind_gl_texture_transient (GL_TEXTURE_RECTANGLE_ARB, + loader->src.gl_foreign.gl_handle, TRUE); if (ctx->glGetError () != GL_NO_ERROR) { _cogl_set_error (error, COGL_SYSTEM_ERROR, COGL_SYSTEM_ERROR_UNSUPPORTED, "Failed to bind foreign GL_TEXTURE_RECTANGLE texture"); - return NULL; + return FALSE; } /* Obtain texture parameters */ @@ -423,7 +429,7 @@ cogl_texture_rectangle_new_from_foreign (CoglContext *ctx, COGL_SYSTEM_ERROR, COGL_SYSTEM_ERROR_UNSUPPORTED, "Unsupported internal format for foreign texture"); - return NULL; + return FALSE; } } else @@ -438,16 +444,6 @@ cogl_texture_rectangle_new_from_foreign (CoglContext *ctx, NULL); } - /* Note: We always trust the given width and height without querying - * the texture object because the user may be creating a Cogl - * texture for a texture_from_pixmap object where glTexImage2D may - * not have been called and the texture_from_pixmap spec doesn't - * clarify that it is reliable to query back the size from OpenGL. - */ - - /* Validate width and height */ - g_return_val_if_fail (width > 0 && height > 0, NULL); - /* Compressed texture images not supported */ if (gl_compressed == GL_TRUE) { @@ -455,27 +451,107 @@ cogl_texture_rectangle_new_from_foreign (CoglContext *ctx, COGL_SYSTEM_ERROR, COGL_SYSTEM_ERROR_UNSUPPORTED, "Compressed foreign textures aren't currently supported"); - return NULL; + return FALSE; } - /* Create new texture */ - tex_rect = _cogl_texture_rectangle_create_base (ctx, width, height, format); - /* Setup bitmap info */ tex_rect->is_foreign = TRUE; - tex_rect->internal_format = format; - - tex_rect->gl_texture = gl_handle; + tex_rect->gl_texture = loader->src.gl_foreign.gl_handle; tex_rect->gl_format = gl_int_format; /* Unknown filter */ tex_rect->gl_legacy_texobj_min_filter = GL_FALSE; tex_rect->gl_legacy_texobj_mag_filter = GL_FALSE; - _cogl_texture_set_allocated (COGL_TEXTURE (tex_rect), TRUE); + tex_rect->internal_format = format; - return tex_rect; + _cogl_texture_set_allocated (COGL_TEXTURE (tex_rect), + format, + loader->src.gl_foreign.width, + loader->src.gl_foreign.height); + + return TRUE; +} + +static CoglBool +_cogl_texture_rectangle_allocate (CoglTexture *tex, + CoglError **error) +{ + CoglTextureRectangle *tex_rect = COGL_TEXTURE_RECTANGLE (tex); + CoglTextureLoader *loader = tex->loader; + + _COGL_RETURN_VAL_IF_FAIL (loader, FALSE); + + switch (loader->src_type) + { + case COGL_TEXTURE_SOURCE_TYPE_SIZED: + return allocate_with_size (tex_rect, loader, error); + case COGL_TEXTURE_SOURCE_TYPE_BITMAP: + return allocate_from_bitmap (tex_rect, loader, error); + case COGL_TEXTURE_SOURCE_TYPE_GL_FOREIGN: + return allocate_from_gl_foreign (tex_rect, loader, error); + default: + break; + } + + g_return_val_if_reached (FALSE); +} + +CoglTextureRectangle * +cogl_texture_rectangle_new_from_bitmap (CoglBitmap *bmp, + CoglPixelFormat internal_format, + CoglError **error) +{ + CoglTextureLoader *loader; + + _COGL_RETURN_VAL_IF_FAIL (cogl_is_bitmap (bmp), NULL); + + loader = _cogl_texture_create_loader (); + loader->src_type = COGL_TEXTURE_SOURCE_TYPE_BITMAP; + loader->src.bitmap.bitmap = cogl_object_ref (bmp); + loader->src.bitmap.can_convert_in_place = FALSE; /* TODO add api for this */ + + return _cogl_texture_rectangle_create_base (_cogl_bitmap_get_context (bmp), + cogl_bitmap_get_width (bmp), + cogl_bitmap_get_height (bmp), + internal_format, + loader); +} + +CoglTextureRectangle * +cogl_texture_rectangle_new_from_foreign (CoglContext *ctx, + unsigned int gl_handle, + int width, + int height, + CoglPixelFormat format, + CoglError **error) +{ + CoglTextureLoader *loader; + + /* NOTE: width, height and internal format are not queriable in + * GLES, hence such a function prototype. Also in the case of full + * opengl the user may be creating a Cogl texture for a + * texture_from_pixmap object where glTexImage2D may not have been + * called and the texture_from_pixmap spec doesn't clarify that it + * is reliable to query back the size from OpenGL. + */ + + /* Assert that it is a valid GL texture object */ + _COGL_RETURN_VAL_IF_FAIL (ctx->glIsTexture (gl_handle), NULL); + + /* Validate width and height */ + _COGL_RETURN_VAL_IF_FAIL (width > 0 && height > 0, NULL); + + loader = _cogl_texture_create_loader (); + loader->src_type = COGL_TEXTURE_SOURCE_TYPE_GL_FOREIGN; + loader->src.gl_foreign.gl_handle = gl_handle; + loader->src.gl_foreign.width = width; + loader->src.gl_foreign.height = height; + loader->src.gl_foreign.format = format; + + return _cogl_texture_rectangle_create_base (ctx, width, height, + format, loader); } static int diff --git a/cogl/cogl-texture-rectangle.h b/cogl/cogl-texture-rectangle.h index 96d0002a9..81920d7aa 100644 --- a/cogl/cogl-texture-rectangle.h +++ b/cogl/cogl-texture-rectangle.h @@ -102,6 +102,11 @@ cogl_is_texture_rectangle (void *object); * first check for the %COGL_FEATURE_ID_TEXTURE_RECTANGLE feature * using cogl_has_feature(). * + * For compatibility, unlike other texture constructors, this + * api allocates texture storage synchronously and returns %NULL on + * failure so it is not possible to configure rectangle textures + * created with this api before allocation. + * * Return value: (transfer full): A pointer to a newly allocated * #CoglTextureRectangle texture or if the size was too large * or there wasn't enough memory %NULL is returned and @error @@ -144,10 +149,18 @@ cogl_texture_rectangle_new_with_size (CoglContext *ctx, * first check for the %COGL_FEATURE_ID_TEXTURE_RECTANGLE feature * using cogl_has_feature(). * + * The storage for the texture is not allocated before this function + * returns. You can call cogl_texture_allocate() to explicitly + * allocate the underlying storage or preferably let Cogl + * automatically allocate storage lazily when it may know more about + * how the texture is going to be used and can optimize how it is + * allocated. + * * Return value: (transfer full): A pointer to a newly allocated * #CoglTextureRectangle texture or if the size was too large * or there wasn't enough memory %NULL is returned and @error * set. + * * Since: 2.0 * Stability: unstable */ @@ -185,12 +198,15 @@ cogl_texture_rectangle_new_from_bitmap (CoglBitmap *bitmap, * Applications wanting to use #CoglTextureRectangle should * first check for the %COGL_FEATURE_ID_TEXTURE_RECTANGLE feature * using cogl_has_feature(). - + * + * The texture is still configurable until it has been allocated so + * for example you can declare whether the texture is premultiplied + * with cogl_texture_set_premultiplied(). + * * Return value: (transfer full): A newly allocated * #CoglTextureRectangle, or if Cogl could not validate the * @gl_handle in some way (perhaps because of an unsupported * format) it will return %NULL and set @error. - * */ CoglTextureRectangle * cogl_texture_rectangle_new_from_foreign (CoglContext *ctx, diff --git a/cogl/cogl-texture.c b/cogl/cogl-texture.c index f1974759c..f6d6bd7b8 100644 --- a/cogl/cogl-texture.c +++ b/cogl/cogl-texture.c @@ -107,6 +107,8 @@ _cogl_texture_init (CoglTexture *texture, CoglContext *context, int width, int height, + CoglPixelFormat internal_format, + CoglTextureLoader *loader, const CoglTextureVtable *vtable) { texture->context = context; @@ -116,11 +118,44 @@ _cogl_texture_init (CoglTexture *texture, texture->allocated = FALSE; texture->vtable = vtable; texture->framebuffers = NULL; + + texture->loader = loader; + + _cogl_texture_set_internal_format (texture, internal_format); +} + +static void +_cogl_texture_free_loader (CoglTexture *texture) +{ + if (texture->loader) + { + CoglTextureLoader *loader = texture->loader; + switch (loader->src_type) + { + case COGL_TEXTURE_SOURCE_TYPE_SIZED: + case COGL_TEXTURE_SOURCE_TYPE_EGL_IMAGE: + case COGL_TEXTURE_SOURCE_TYPE_GL_FOREIGN: + break; + case COGL_TEXTURE_SOURCE_TYPE_BITMAP: + cogl_object_unref (loader->src.bitmap.bitmap); + break; + } + g_slice_free (CoglTextureLoader, loader); + texture->loader = NULL; + } +} + +CoglTextureLoader * +_cogl_texture_create_loader (void) +{ + return g_slice_new0 (CoglTextureLoader); } void _cogl_texture_free (CoglTexture *texture) { + _cogl_texture_free_loader (texture); + g_free (texture); } @@ -135,34 +170,6 @@ _cogl_texture_needs_premult_conversion (CoglPixelFormat src_format, (dst_format & COGL_PREMULT_BIT)); } -CoglPixelFormat -_cogl_texture_determine_internal_format (CoglPixelFormat src_format, - CoglPixelFormat dst_format) -{ - /* If the application hasn't specified a specific format then we'll - * pick the most appropriate. By default Cogl will use a - * premultiplied internal format. Later we will add control over - * this. */ - if (dst_format == COGL_PIXEL_FORMAT_ANY) - { - if (COGL_PIXEL_FORMAT_CAN_HAVE_PREMULT (src_format)) - return src_format | COGL_PREMULT_BIT; - else - return src_format; - } - else - /* XXX: It might be nice to make this match the component ordering - of the source format when the formats are otherwise the same - because on GL there is no way to specify the ordering of the - internal format. However when using GLES with the - GL_EXT_texture_format_BGRA8888 the order of the internal format - becomes important because it must exactly match the format of - the uploaded data. That means that if someone creates a texture - with some RGBA data and then later tries to upload BGRA data we - do actually have to swizzle the components */ - return dst_format; -} - CoglBool _cogl_texture_is_foreign (CoglTexture *texture) { @@ -1348,9 +1355,17 @@ _cogl_texture_spans_foreach_in_region (CoglSpan *x_spans, void _cogl_texture_set_allocated (CoglTexture *texture, - CoglBool allocated) + CoglPixelFormat internal_format, + int width, + int height) { - texture->allocated = allocated; + _cogl_texture_set_internal_format (texture, internal_format); + + texture->width = width; + texture->height = height; + texture->allocated = TRUE; + + _cogl_texture_free_loader (texture); } CoglBool @@ -1364,3 +1379,127 @@ cogl_texture_allocate (CoglTexture *texture, return texture->allocated; } + +void +_cogl_texture_set_internal_format (CoglTexture *texture, + CoglPixelFormat internal_format) +{ + texture->premultiplied = FALSE; + + if (internal_format == COGL_PIXEL_FORMAT_ANY) + internal_format = COGL_PIXEL_FORMAT_RGBA_8888_PRE; + + if (internal_format == COGL_PIXEL_FORMAT_A_8) + { + texture->components = COGL_TEXTURE_COMPONENTS_A; + return; + } + else if (internal_format & COGL_DEPTH_BIT) + { + texture->components = COGL_TEXTURE_COMPONENTS_DEPTH; + return; + } + else if (internal_format & COGL_A_BIT) + { + texture->components = COGL_TEXTURE_COMPONENTS_RGBA; + if (internal_format & COGL_PREMULT_BIT) + texture->premultiplied = TRUE; + return; + } + else + texture->components = COGL_TEXTURE_COMPONENTS_RGB; +} + +CoglPixelFormat +_cogl_texture_determine_internal_format (CoglTexture *texture, + CoglPixelFormat src_format) +{ + switch (texture->components) + { + case COGL_TEXTURE_COMPONENTS_DEPTH: + if (src_format & COGL_DEPTH_BIT) + return src_format; + else + { + CoglContext *ctx = texture->context; + + if (_cogl_has_private_feature (ctx, + COGL_PRIVATE_FEATURE_EXT_PACKED_DEPTH_STENCIL) || + _cogl_has_private_feature (ctx, + COGL_PRIVATE_FEATURE_OES_PACKED_DEPTH_STENCIL)) + { + return COGL_PIXEL_FORMAT_DEPTH_24_STENCIL_8; + } + else + return COGL_PIXEL_FORMAT_DEPTH_16; + } + case COGL_TEXTURE_COMPONENTS_A: + return COGL_PIXEL_FORMAT_A_8; + case COGL_TEXTURE_COMPONENTS_RGB: + if (src_format != COGL_PIXEL_FORMAT_ANY && + !(src_format & COGL_A_BIT) && !(src_format & COGL_DEPTH_BIT)) + return src_format; + else + return COGL_PIXEL_FORMAT_RGB_888; + case COGL_TEXTURE_COMPONENTS_RGBA: + { + CoglPixelFormat format; + + if (src_format != COGL_PIXEL_FORMAT_ANY && + (src_format & COGL_A_BIT) && src_format != COGL_PIXEL_FORMAT_A_8) + format = src_format; + else + format = COGL_PIXEL_FORMAT_RGBA_8888; + + if (texture->premultiplied) + { + if (COGL_PIXEL_FORMAT_CAN_HAVE_PREMULT (format)) + return format |= COGL_PREMULT_BIT; + else + return COGL_PIXEL_FORMAT_RGBA_8888_PRE; + } + else + return format & ~COGL_PREMULT_BIT; + } + } + + g_return_val_if_reached (COGL_PIXEL_FORMAT_RGBA_8888_PRE); +} + +void +cogl_texture_set_components (CoglTexture *texture, + CoglTextureComponents components) +{ + _COGL_RETURN_IF_FAIL (!texture->allocated); + + if (texture->components == components) + return; + + texture->components = components; +} + +CoglBool +cogl_texture_get_components (CoglTexture *texture) +{ + return texture->components; +} + +void +cogl_texture_set_premultiplied (CoglTexture *texture, + CoglBool premultiplied) +{ + _COGL_RETURN_IF_FAIL (!texture->allocated); + + premultiplied = !!premultiplied; + + if (texture->premultiplied == premultiplied) + return; + + texture->premultiplied = premultiplied; +} + +CoglBool +cogl_texture_get_premultiplied (CoglTexture *texture) +{ + return texture->premultiplied; +} diff --git a/cogl/cogl-texture.h b/cogl/cogl-texture.h index 2e3f25b7b..55b3be945 100644 --- a/cogl/cogl-texture.h +++ b/cogl/cogl-texture.h @@ -256,6 +256,96 @@ cogl_texture_new_from_bitmap (CoglBitmap *bitmap, CoglBool cogl_is_texture (void *object); +typedef enum _CoglTextureComponents +{ + COGL_TEXTURE_COMPONENTS_A = 1, + COGL_TEXTURE_COMPONENTS_RGB, + COGL_TEXTURE_COMPONENTS_RGBA, + COGL_TEXTURE_COMPONENTS_DEPTH +} CoglTextureComponents; + +/** + * cogl_texture_set_components: + * @texture: a #CoglTexture pointer. + * + * Affects the internal storage format for this texture by + * determinging what components will be required for sampling later. + * + * This api affects how data is uploaded to the GPU since unused + * components can potentially be discarded from source data. + * + * By default the required components are automatically determined + * using the format of the source data that is first uploaded to + * the given @texture. + */ +void +cogl_texture_set_components (CoglTexture *texture, + CoglTextureComponents components); + +/** + * cogl_texture_get_components: + * @texture: a #CoglTexture pointer. + * + * Queries what components the given @texture stores internally as set + * via cogl_texture_set_components(). + * + * By default the required components are automatically determined + * using the format of the source data that is first uploaded to + * the given @texture. + */ +CoglBool +cogl_texture_get_components (CoglTexture *texture); + +/** + * cogl_texture_set_premultiplied: + * @texture: a #CoglTexture pointer. + * @premultiplied: Whether any internally stored red, green or blue + * components are pre-multiplied by an alpha + * component. + * + * Affects the internal storage format for this texture by determining + * whether red, green and blue color components should be stored as + * pre-multiplied alpha values. + * + * This api affects how data is uploaded to the GPU since Cogl will + * convert source data to have premultiplied or unpremultiplied + * components according to this state. + * + * For example if you create a texture via + * cogl_texture_2d_new_with_size() and then upload data via + * cogl_texture_set_data() passing a source format of + * %COGL_PIXEL_FORMAT_RGBA_8888 then Cogl will internally multiply the + * red, green and blue components of the source data by the alpha + * component, for each pixel so that the internally stored data has + * pre-multiplied alpha components. If you instead upload data that + * already has pre-multiplied components by passing + * %COGL_PIXEL_FORMAT_RGBA_8888_PRE as the source format to + * cogl_texture_set_data() then the data can be uploaded without being + * converted. + * + * By default the @premultipled state is @TRUE. + */ +void +cogl_texture_set_premultiplied (CoglTexture *texture, + CoglBool premultiplied); + +/** + * cogl_texture_get_premultiplied: + * @texture: a #CoglTexture pointer. + * + * Queries the pre-multiplied alpha status for internally stored red, + * green and blue components for the given @texture as set by + * cogl_texture_set_premultiplied(). + * + * By default the pre-multipled state is @TRUE. + * + * Return value: %TRUE if red, green and blue components are + * internally stored pre-multiplied by the alpha + * value or %FALSE if not. + */ +CoglBool +cogl_texture_get_premultiplied (CoglTexture *texture); + /** * cogl_texture_get_width: * @texture: a #CoglTexture pointer. diff --git a/cogl/driver/gl/cogl-texture-2d-gl.c b/cogl/driver/gl/cogl-texture-2d-gl.c index eea1792b5..c5d2cdae2 100644 --- a/cogl/driver/gl/cogl-texture-2d-gl.c +++ b/cogl/driver/gl/cogl-texture-2d-gl.c @@ -25,13 +25,12 @@ * Robert Bragg */ -#ifdef HAVE_CONFIG_H -#include "config.h" -#endif +#include #include #include "cogl-private.h" +#include "cogl-texture-private.h" #include "cogl-texture-2d-gl.h" #include "cogl-texture-2d-gl-private.h" #include "cogl-texture-2d-private.h" @@ -57,6 +56,13 @@ _cogl_texture_2d_gl_can_create (CoglContext *ctx, GLenum gl_format; GLenum gl_type; + /* If NPOT textures aren't supported then the size must be a power + of two */ + if (!cogl_has_feature (ctx, COGL_FEATURE_ID_TEXTURE_NPOT_BASIC) && + (!_cogl_util_is_pot (width) || + !_cogl_util_is_pot (height))) + return FALSE; + ctx->driver_vtable->pixel_format_to_gl (ctx, internal_format, &gl_intformat, @@ -90,22 +96,29 @@ _cogl_texture_2d_gl_init (CoglTexture2D *tex_2d) tex_2d->gl_legacy_texobj_wrap_mode_t = GL_FALSE; } -CoglBool -_cogl_texture_2d_gl_allocate (CoglTexture *tex, - CoglError **error) +static CoglBool +allocate_with_size (CoglTexture2D *tex_2d, + CoglTextureLoader *loader, + CoglError **error) { + CoglTexture *tex = COGL_TEXTURE (tex_2d); + CoglPixelFormat internal_format; + int width = loader->src.sized.width; + int height = loader->src.sized.height; CoglContext *ctx = tex->context; - CoglTexture2D *tex_2d = COGL_TEXTURE_2D (tex); GLenum gl_intformat; GLenum gl_format; GLenum gl_type; GLenum gl_error; GLenum gl_texture; + internal_format = + _cogl_texture_determine_internal_format (tex, COGL_PIXEL_FORMAT_ANY); + if (!_cogl_texture_2d_gl_can_create (ctx, - tex->width, - tex->height, - tex_2d->internal_format)) + width, + height, + internal_format)) { _cogl_set_error (error, COGL_TEXTURE_ERROR, COGL_TEXTURE_ERROR_SIZE, @@ -115,13 +128,12 @@ _cogl_texture_2d_gl_allocate (CoglTexture *tex, } ctx->driver_vtable->pixel_format_to_gl (ctx, - tex_2d->internal_format, + internal_format, &gl_intformat, &gl_format, &gl_type); - gl_texture = - ctx->texture_driver->gen (ctx, GL_TEXTURE_2D, tex_2d->internal_format); + gl_texture = ctx->texture_driver->gen (ctx, GL_TEXTURE_2D, internal_format); tex_2d->gl_internal_format = gl_intformat; @@ -134,7 +146,7 @@ _cogl_texture_2d_gl_allocate (CoglTexture *tex, ; ctx->glTexImage2D (GL_TEXTURE_2D, 0, gl_intformat, - tex->width, tex->height, 0, gl_format, gl_type, NULL); + width, height, 0, gl_format, gl_type, NULL); if (_cogl_gl_util_catch_out_of_memory (ctx, error)) { @@ -145,28 +157,51 @@ _cogl_texture_2d_gl_allocate (CoglTexture *tex, tex_2d->gl_texture = gl_texture; tex_2d->gl_internal_format = gl_intformat; + tex_2d->internal_format = internal_format; + + _cogl_texture_set_allocated (tex, internal_format, width, height); + return TRUE; } -CoglTexture2D * -_cogl_texture_2d_gl_new_from_bitmap (CoglBitmap *bmp, - CoglPixelFormat internal_format, - CoglBool can_convert_in_place, - CoglError **error) +static CoglBool +allocate_from_bitmap (CoglTexture2D *tex_2d, + CoglTextureLoader *loader, + CoglError **error) { + CoglTexture *tex = COGL_TEXTURE (tex_2d); + CoglBitmap *bmp = loader->src.bitmap.bitmap; CoglContext *ctx = _cogl_bitmap_get_context (bmp); - CoglTexture2D *tex_2d; + CoglPixelFormat internal_format; + int width = cogl_bitmap_get_width (bmp); + int height = cogl_bitmap_get_height (bmp); + CoglBool can_convert_in_place = loader->src.bitmap.can_convert_in_place; CoglBitmap *upload_bmp; GLenum gl_intformat; GLenum gl_format; GLenum gl_type; + internal_format = + _cogl_texture_determine_internal_format (tex, cogl_bitmap_get_format (bmp)); + + if (!_cogl_texture_2d_gl_can_create (ctx, + width, + height, + internal_format)) + { + _cogl_set_error (error, COGL_TEXTURE_ERROR, + COGL_TEXTURE_ERROR_SIZE, + "Failed to create texture 2d due to size/format" + " constraints"); + return FALSE; + } + upload_bmp = _cogl_bitmap_convert_for_upload (bmp, internal_format, can_convert_in_place, error); if (upload_bmp == NULL) - return NULL; + return FALSE; ctx->driver_vtable->pixel_format_to_gl (ctx, cogl_bitmap_get_format (upload_bmp), @@ -179,11 +214,6 @@ _cogl_texture_2d_gl_new_from_bitmap (CoglBitmap *bmp, NULL, NULL); - tex_2d = _cogl_texture_2d_create_base (ctx, - cogl_bitmap_get_width (bmp), - cogl_bitmap_get_height (bmp), - internal_format); - /* Keep a copy of the first pixel so that if glGenerateMipmap isn't supported we can fallback to using GL_GENERATE_MIPMAP */ if (!cogl_has_feature (ctx, COGL_FEATURE_ID_OFFSCREEN)) @@ -226,44 +256,40 @@ _cogl_texture_2d_gl_new_from_bitmap (CoglBitmap *bmp, error)) { cogl_object_unref (upload_bmp); - cogl_object_unref (tex_2d); - return NULL; + return FALSE; } tex_2d->gl_internal_format = gl_intformat; cogl_object_unref (upload_bmp); - _cogl_texture_set_allocated (COGL_TEXTURE (tex_2d), TRUE); + tex_2d->internal_format = internal_format; - return tex_2d; + _cogl_texture_set_allocated (tex, internal_format, width, height); + + return TRUE; } #if defined (COGL_HAS_EGL_SUPPORT) && defined (EGL_KHR_image_base) -CoglTexture2D * -_cogl_egl_texture_2d_gl_new_from_image (CoglContext *ctx, - int width, - int height, - CoglPixelFormat format, - EGLImageKHR image, - CoglError **error) +static CoglBool +allocate_from_egl_image (CoglTexture2D *tex_2d, + CoglTextureLoader *loader, + CoglError **error) { - CoglTexture2D *tex_2d; + CoglTexture *tex = COGL_TEXTURE (tex_2d); + CoglContext *ctx = tex->context; + CoglPixelFormat internal_format = loader->src.egl_image.format; GLenum gl_error; - tex_2d = _cogl_texture_2d_create_base (ctx, - width, height, - format); - tex_2d->gl_texture = - ctx->texture_driver->gen (ctx, GL_TEXTURE_2D, format); + ctx->texture_driver->gen (ctx, GL_TEXTURE_2D, internal_format); _cogl_bind_gl_texture_transient (GL_TEXTURE_2D, tex_2d->gl_texture, FALSE); while ((gl_error = ctx->glGetError ()) != GL_NO_ERROR) ; - ctx->glEGLImageTargetTexture2D (GL_TEXTURE_2D, image); + ctx->glEGLImageTargetTexture2D (GL_TEXTURE_2D, loader->src.egl_image.image); if (ctx->glGetError () != GL_NO_ERROR) { _cogl_set_error (error, @@ -271,16 +297,175 @@ _cogl_egl_texture_2d_gl_new_from_image (CoglContext *ctx, COGL_TEXTURE_ERROR_BAD_PARAMETER, "Could not create a CoglTexture2D from a given " "EGLImage"); - cogl_object_unref (tex_2d); - return NULL; + GE( ctx, glDeleteTextures (1, &tex_2d->gl_texture) ); + return FALSE; } - _cogl_texture_set_allocated (COGL_TEXTURE (tex_2d), TRUE); + tex_2d->internal_format = internal_format; - return tex_2d; + _cogl_texture_set_allocated (tex, + internal_format, + loader->src.egl_image.width, + loader->src.egl_image.height); + + return TRUE; } #endif +static CoglBool +allocate_from_gl_foreign (CoglTexture2D *tex_2d, + CoglTextureLoader *loader, + CoglError **error) +{ + CoglTexture *tex = COGL_TEXTURE (tex_2d); + CoglContext *ctx = tex->context; + CoglPixelFormat format = loader->src.gl_foreign.format; + GLenum gl_error = 0; + GLint gl_compressed = GL_FALSE; + GLenum gl_int_format = 0; + + if (!ctx->texture_driver->allows_foreign_gl_target (ctx, GL_TEXTURE_2D)) + { + _cogl_set_error (error, + COGL_SYSTEM_ERROR, + COGL_SYSTEM_ERROR_UNSUPPORTED, + "Foreign GL_TEXTURE_2D textures are not " + "supported by your system"); + return FALSE; + } + + /* Make sure binding succeeds */ + while ((gl_error = ctx->glGetError ()) != GL_NO_ERROR) + ; + + _cogl_bind_gl_texture_transient (GL_TEXTURE_2D, + loader->src.gl_foreign.gl_handle, TRUE); + if (ctx->glGetError () != GL_NO_ERROR) + { + _cogl_set_error (error, + COGL_SYSTEM_ERROR, + COGL_SYSTEM_ERROR_UNSUPPORTED, + "Failed to bind foreign GL_TEXTURE_2D texture"); + return FALSE; + } + + /* Obtain texture parameters + (only level 0 we are interested in) */ + +#ifdef HAVE_COGL_GL + if (_cogl_has_private_feature + (ctx, COGL_PRIVATE_FEATURE_QUERY_TEXTURE_PARAMETERS)) + { + GE( ctx, glGetTexLevelParameteriv (GL_TEXTURE_2D, 0, + GL_TEXTURE_COMPRESSED, + &gl_compressed) ); + + { + GLint val; + + GE( ctx, glGetTexLevelParameteriv (GL_TEXTURE_2D, 0, + GL_TEXTURE_INTERNAL_FORMAT, + &val) ); + + gl_int_format = val; + } + + /* If we can query GL for the actual pixel format then we'll ignore + the passed in format and use that. */ + if (!ctx->driver_vtable->pixel_format_from_gl_internal (ctx, + gl_int_format, + &format)) + { + _cogl_set_error (error, + COGL_SYSTEM_ERROR, + COGL_SYSTEM_ERROR_UNSUPPORTED, + "Unsupported internal format for foreign texture"); + return FALSE; + } + } + else +#endif + { + /* Otherwise we'll assume we can derive the GL format from the + passed in format */ + ctx->driver_vtable->pixel_format_to_gl (ctx, + format, + &gl_int_format, + NULL, + NULL); + } + + /* Compressed texture images not supported */ + if (gl_compressed == GL_TRUE) + { + _cogl_set_error (error, + COGL_SYSTEM_ERROR, + COGL_SYSTEM_ERROR_UNSUPPORTED, + "Compressed foreign textures aren't currently supported"); + return FALSE; + } + + /* Note: previously this code would query the texture object for + whether it has GL_GENERATE_MIPMAP enabled to determine whether to + auto-generate the mipmap. This doesn't make much sense any more + since Cogl switch to using glGenerateMipmap. Ideally I think + cogl_texture_2d_new_from_foreign should take a flags parameter so + that the application can decide whether it wants + auto-mipmapping. To be compatible with existing code, Cogl now + disables its own auto-mipmapping but leaves the value of + GL_GENERATE_MIPMAP alone so that it would still work but without + the dirtiness tracking that Cogl would do. */ + + _cogl_texture_2d_set_auto_mipmap (COGL_TEXTURE (tex_2d), FALSE); + + /* Setup bitmap info */ + tex_2d->is_foreign = TRUE; + tex_2d->mipmaps_dirty = TRUE; + + tex_2d->gl_texture = loader->src.gl_foreign.gl_handle; + tex_2d->gl_internal_format = gl_int_format; + + /* Unknown filter */ + tex_2d->gl_legacy_texobj_min_filter = GL_FALSE; + tex_2d->gl_legacy_texobj_mag_filter = GL_FALSE; + + tex_2d->internal_format = format; + + _cogl_texture_set_allocated (tex, + format, + loader->src.gl_foreign.width, + loader->src.gl_foreign.height); + return TRUE; +} + +CoglBool +_cogl_texture_2d_gl_allocate (CoglTexture *tex, + CoglError **error) +{ + CoglTexture2D *tex_2d = COGL_TEXTURE_2D (tex); + CoglTextureLoader *loader = tex->loader; + + _COGL_RETURN_VAL_IF_FAIL (loader, FALSE); + + switch (loader->src_type) + { + case COGL_TEXTURE_SOURCE_TYPE_SIZED: + return allocate_with_size (tex_2d, loader, error); + case COGL_TEXTURE_SOURCE_TYPE_BITMAP: + return allocate_from_bitmap (tex_2d, loader, error); + case COGL_TEXTURE_SOURCE_TYPE_EGL_IMAGE: +#if defined (COGL_HAS_EGL_SUPPORT) && defined (EGL_KHR_image_base) + return allocate_from_egl_image (tex_2d, loader, error); +#else + g_return_val_if_reached (FALSE); +#endif + case COGL_TEXTURE_SOURCE_TYPE_GL_FOREIGN: + return allocate_from_gl_foreign (tex_2d, loader, error); + } + + g_return_val_if_reached (FALSE); +} + void _cogl_texture_2d_gl_flush_legacy_texobj_filters (CoglTexture *tex, GLenum min_filter, @@ -343,89 +528,12 @@ cogl_texture_2d_new_from_foreign (CoglContext *ctx, CoglPixelFormat format, CoglError **error) { + CoglTextureLoader *loader; + /* NOTE: width, height and internal format are not queriable * in GLES, hence such a function prototype. */ - GLenum gl_error = 0; - GLint gl_compressed = GL_FALSE; - GLenum gl_int_format = 0; - CoglTexture2D *tex_2d; - - /* Assert it is a valid GL texture object */ - g_return_val_if_fail (ctx->glIsTexture (gl_handle), NULL); - - if (!ctx->texture_driver->allows_foreign_gl_target (ctx, GL_TEXTURE_2D)) - { - _cogl_set_error (error, - COGL_SYSTEM_ERROR, - COGL_SYSTEM_ERROR_UNSUPPORTED, - "Foreign GL_TEXTURE_2D textures are not " - "supported by your system"); - return NULL; - } - - - /* Make sure binding succeeds */ - while ((gl_error = ctx->glGetError ()) != GL_NO_ERROR) - ; - - _cogl_bind_gl_texture_transient (GL_TEXTURE_2D, gl_handle, TRUE); - if (ctx->glGetError () != GL_NO_ERROR) - { - _cogl_set_error (error, - COGL_SYSTEM_ERROR, - COGL_SYSTEM_ERROR_UNSUPPORTED, - "Failed to bind foreign GL_TEXTURE_2D texture"); - return NULL; - } - - /* Obtain texture parameters - (only level 0 we are interested in) */ - -#ifdef HAVE_COGL_GL - if (_cogl_has_private_feature - (ctx, COGL_PRIVATE_FEATURE_QUERY_TEXTURE_PARAMETERS)) - { - GE( ctx, glGetTexLevelParameteriv (GL_TEXTURE_2D, 0, - GL_TEXTURE_COMPRESSED, - &gl_compressed) ); - - { - GLint val; - - GE( ctx, glGetTexLevelParameteriv (GL_TEXTURE_2D, 0, - GL_TEXTURE_INTERNAL_FORMAT, - &val) ); - - gl_int_format = val; - } - - /* If we can query GL for the actual pixel format then we'll ignore - the passed in format and use that. */ - if (!ctx->driver_vtable->pixel_format_from_gl_internal (ctx, - gl_int_format, - &format)) - { - _cogl_set_error (error, - COGL_SYSTEM_ERROR, - COGL_SYSTEM_ERROR_UNSUPPORTED, - "Unsupported internal format for foreign texture"); - return NULL; - } - } - else -#endif - { - /* Otherwise we'll assume we can derive the GL format from the - passed in format */ - ctx->driver_vtable->pixel_format_to_gl (ctx, - format, - &gl_int_format, - NULL, - NULL); - } - /* Note: We always trust the given width and height without querying * the texture object because the user may be creating a Cogl * texture for a texture_from_pixmap object where glTexImage2D may @@ -433,50 +541,20 @@ cogl_texture_2d_new_from_foreign (CoglContext *ctx, * clarify that it is reliable to query back the size from OpenGL. */ + /* Assert it is a valid GL texture object */ + _COGL_RETURN_VAL_IF_FAIL (ctx->glIsTexture (gl_handle), FALSE); + /* Validate width and height */ - g_return_val_if_fail (width > 0 && height > 0, NULL); + _COGL_RETURN_VAL_IF_FAIL (width > 0 && height > 0, NULL); - /* Compressed texture images not supported */ - if (gl_compressed == GL_TRUE) - { - _cogl_set_error (error, - COGL_SYSTEM_ERROR, - COGL_SYSTEM_ERROR_UNSUPPORTED, - "Compressed foreign textures aren't currently supported"); - return NULL; - } + loader = _cogl_texture_create_loader (); + loader->src_type = COGL_TEXTURE_SOURCE_TYPE_GL_FOREIGN; + loader->src.gl_foreign.gl_handle = gl_handle; + loader->src.gl_foreign.width = width; + loader->src.gl_foreign.height = height; + loader->src.gl_foreign.format = format; - /* Note: previously this code would query the texture object for - whether it has GL_GENERATE_MIPMAP enabled to determine whether to - auto-generate the mipmap. This doesn't make much sense any more - since Cogl switch to using glGenerateMipmap. Ideally I think - cogl_texture_2d_new_from_foreign should take a flags parameter so - that the application can decide whether it wants - auto-mipmapping. To be compatible with existing code, Cogl now - disables its own auto-mipmapping but leaves the value of - GL_GENERATE_MIPMAP alone so that it would still work but without - the dirtiness tracking that Cogl would do. */ - - /* Create new texture */ - tex_2d = _cogl_texture_2d_create_base (ctx, - width, height, - format); - _cogl_texture_2d_set_auto_mipmap (COGL_TEXTURE (tex_2d), FALSE); - - /* Setup bitmap info */ - tex_2d->is_foreign = TRUE; - tex_2d->mipmaps_dirty = TRUE; - - tex_2d->gl_texture = gl_handle; - tex_2d->gl_internal_format = gl_int_format; - - /* Unknown filter */ - tex_2d->gl_legacy_texobj_min_filter = GL_FALSE; - tex_2d->gl_legacy_texobj_mag_filter = GL_FALSE; - - _cogl_texture_set_allocated (COGL_TEXTURE (tex_2d), TRUE); - - return tex_2d; + return _cogl_texture_2d_create_base (ctx, width, height, format, loader); } void diff --git a/cogl/driver/gl/gl/cogl-driver-gl.c b/cogl/driver/gl/gl/cogl-driver-gl.c index 2ce78d811..905956ad3 100644 --- a/cogl/driver/gl/gl/cogl-driver-gl.c +++ b/cogl/driver/gl/gl/cogl-driver-gl.c @@ -678,10 +678,6 @@ _cogl_driver_gl = _cogl_texture_2d_gl_can_create, _cogl_texture_2d_gl_init, _cogl_texture_2d_gl_allocate, - _cogl_texture_2d_gl_new_from_bitmap, -#if defined (COGL_HAS_EGL_SUPPORT) && defined (EGL_KHR_image_base) - _cogl_egl_texture_2d_gl_new_from_image, -#endif _cogl_texture_2d_gl_copy_from_framebuffer, _cogl_texture_2d_gl_get_gl_handle, _cogl_texture_2d_gl_generate_mipmap, diff --git a/cogl/driver/gl/gles/cogl-driver-gles.c b/cogl/driver/gl/gles/cogl-driver-gles.c index 4783a7eef..82c33edbc 100644 --- a/cogl/driver/gl/gles/cogl-driver-gles.c +++ b/cogl/driver/gl/gles/cogl-driver-gles.c @@ -397,10 +397,6 @@ _cogl_driver_gles = _cogl_texture_2d_gl_can_create, _cogl_texture_2d_gl_init, _cogl_texture_2d_gl_allocate, - _cogl_texture_2d_gl_new_from_bitmap, -#if defined (COGL_HAS_EGL_SUPPORT) && defined (EGL_KHR_image_base) - _cogl_egl_texture_2d_gl_new_from_image, -#endif _cogl_texture_2d_gl_copy_from_framebuffer, _cogl_texture_2d_gl_get_gl_handle, _cogl_texture_2d_gl_generate_mipmap, diff --git a/cogl/driver/nop/cogl-driver-nop.c b/cogl/driver/nop/cogl-driver-nop.c index b5f71b0c2..efd914ca7 100644 --- a/cogl/driver/nop/cogl-driver-nop.c +++ b/cogl/driver/nop/cogl-driver-nop.c @@ -69,10 +69,6 @@ _cogl_driver_nop = _cogl_texture_2d_nop_can_create, _cogl_texture_2d_nop_init, _cogl_texture_2d_nop_allocate, - _cogl_texture_2d_nop_new_from_bitmap, -#if defined (COGL_HAS_EGL_SUPPORT) && defined (EGL_KHR_image_base) - _cogl_egl_texture_2d_nop_new_from_image, -#endif _cogl_texture_2d_nop_copy_from_framebuffer, _cogl_texture_2d_nop_get_gl_handle, _cogl_texture_2d_nop_generate_mipmap, diff --git a/cogl/driver/nop/cogl-texture-2d-nop-private.h b/cogl/driver/nop/cogl-texture-2d-nop-private.h index f822a7f15..a0722d7fe 100644 --- a/cogl/driver/nop/cogl-texture-2d-nop-private.h +++ b/cogl/driver/nop/cogl-texture-2d-nop-private.h @@ -48,22 +48,6 @@ CoglBool _cogl_texture_2d_nop_allocate (CoglTexture *tex, CoglError **error); -CoglTexture2D * -_cogl_texture_2d_nop_new_from_bitmap (CoglBitmap *bmp, - CoglPixelFormat internal_format, - CoglBool can_convert_in_place, - CoglError **error); - -#if defined (COGL_HAS_EGL_SUPPORT) && defined (EGL_KHR_image_base) -CoglTexture2D * -_cogl_egl_texture_2d_nop_new_from_image (CoglContext *ctx, - int width, - int height, - CoglPixelFormat format, - EGLImageKHR image, - CoglError **error); -#endif - void _cogl_texture_2d_nop_flush_legacy_texobj_filters (CoglTexture *tex, GLenum min_filter, diff --git a/cogl/driver/nop/cogl-texture-2d-nop.c b/cogl/driver/nop/cogl-texture-2d-nop.c index b63a7b7a4..30aadedec 100644 --- a/cogl/driver/nop/cogl-texture-2d-nop.c +++ b/cogl/driver/nop/cogl-texture-2d-nop.c @@ -62,36 +62,6 @@ _cogl_texture_2d_nop_allocate (CoglTexture *tex, return TRUE; } -CoglTexture2D * -_cogl_texture_2d_nop_new_from_bitmap (CoglBitmap *bmp, - CoglPixelFormat internal_format, - CoglBool can_convert_in_place, - CoglError **error) -{ - return _cogl_texture_2d_create_base (_cogl_bitmap_get_context (bmp), - cogl_bitmap_get_width (bmp), - cogl_bitmap_get_height (bmp), - internal_format); -} - -#if defined (COGL_HAS_EGL_SUPPORT) && defined (EGL_KHR_image_base) -CoglTexture2D * -_cogl_egl_texture_2d_nop_new_from_image (CoglContext *ctx, - int width, - int height, - CoglPixelFormat format, - EGLImageKHR image, - CoglError **error) -{ - _cogl_set_error (error, - COGL_SYSTEM_ERROR, - COGL_SYSTEM_ERROR_UNSUPPORTED, - "Creating 2D textures from an EGLImage isn't " - "supported by the NOP backend"); - return NULL; -} -#endif - void _cogl_texture_2d_nop_flush_legacy_texobj_filters (CoglTexture *tex, GLenum min_filter, diff --git a/cogl/winsys/cogl-texture-pixmap-x11.c b/cogl/winsys/cogl-texture-pixmap-x11.c index 1e01a938a..b2b739f62 100644 --- a/cogl/winsys/cogl-texture-pixmap-x11.c +++ b/cogl/winsys/cogl-texture-pixmap-x11.c @@ -287,6 +287,7 @@ cogl_texture_pixmap_x11_new (CoglContext *ctxt, int pixmap_x, pixmap_y; unsigned int pixmap_width, pixmap_height; unsigned int pixmap_border_width; + CoglPixelFormat internal_format; CoglTexture *tex = COGL_TEXTURE (tex_pixmap); XWindowAttributes window_attributes; int damage_base; @@ -305,7 +306,15 @@ cogl_texture_pixmap_x11_new (CoglContext *ctxt, return NULL; } + /* Note: the detailed pixel layout doesn't matter here, we are just + * interested in RGB vs RGBA... */ + internal_format = (tex_pixmap->depth >= 32 + ? COGL_PIXEL_FORMAT_RGBA_8888_PRE + : COGL_PIXEL_FORMAT_RGB_888); + _cogl_texture_init (tex, ctxt, pixmap_width, pixmap_height, + internal_format, + NULL, /* no loader */ &cogl_texture_pixmap_x11_vtable); tex_pixmap->pixmap = pixmap; @@ -326,6 +335,7 @@ cogl_texture_pixmap_x11_new (CoglContext *ctxt, "Unable to query root window attributes"); return NULL; } + tex_pixmap->visual = window_attributes.visual; /* If automatic updates are requested and the Xlib connection @@ -346,9 +356,9 @@ cogl_texture_pixmap_x11_new (CoglContext *ctxt, /* Assume the entire pixmap is damaged to begin with */ tex_pixmap->damage_rect.x1 = 0; - tex_pixmap->damage_rect.x2 = tex->width; + tex_pixmap->damage_rect.x2 = pixmap_width; tex_pixmap->damage_rect.y1 = 0; - tex_pixmap->damage_rect.y2 = tex->height; + tex_pixmap->damage_rect.y2 = pixmap_height; winsys = _cogl_texture_pixmap_x11_get_winsys (tex_pixmap); if (winsys->texture_pixmap_x11_create) @@ -362,7 +372,8 @@ cogl_texture_pixmap_x11_new (CoglContext *ctxt, if (!tex_pixmap->use_winsys_texture) tex_pixmap->winsys = NULL; - _cogl_texture_set_allocated (tex, TRUE); + _cogl_texture_set_allocated (tex, internal_format, + pixmap_width, pixmap_height); return _cogl_texture_pixmap_x11_object_new (tex_pixmap); } diff --git a/examples/cogl-basic-video-player.c b/examples/cogl-basic-video-player.c index cc1ace298..552298aee 100644 --- a/examples/cogl-basic-video-player.c +++ b/examples/cogl-basic-video-player.c @@ -1,4 +1,5 @@ #include +#include #include #include