Add support for RG textures

This adds COGL_PIXEL_FORMAT_RG_88 and COGL_TEXTURE_COMPONENTS_RG in
order to support two-component textures. The RG components for a
texture is only supported if COGL_FEATURE_ID_TEXTURE_RG is advertised.
This is only available on GL 3, GL 2 with the GL_ARB_texture_rg
extension or GLES with the GL_EXT_texture_rg extension. The RG pixel
format is always supported for images because Cogl can easily do the
conversion if an application uses this format to upload to a texture
with a different format.

If an application tries to create an RG texture when the feature isn't
supported then it will raise an error when the texture is allocated.

https://bugzilla.gnome.org/show_bug.cgi?id=712830

Reviewed-by: Robert Bragg <robert@linux.intel.com>

(cherry picked from commit 568677ab3bcb62ababad1623be0d6b9b117d0a26)

Conflicts:
	cogl/cogl-bitmap-packing.h
	cogl/cogl-types.h
	cogl/driver/gl/gl/cogl-driver-gl.c
	tests/conform/test-read-texture-formats.c
	tests/conform/test-write-texture-formats.c
This commit is contained in:
Neil Roberts 2014-01-14 15:52:45 +00:00
parent 06c75ea2e7
commit eb7ef457cb
15 changed files with 283 additions and 16 deletions

View File

@ -319,6 +319,7 @@ _cogl_bitmap_needs_short_temp_buffer (CoglPixelFormat format)
g_assert_not_reached ();
case COGL_PIXEL_FORMAT_A_8:
case COGL_PIXEL_FORMAT_RG_88:
case COGL_PIXEL_FORMAT_RGB_565:
case COGL_PIXEL_FORMAT_RGBA_4444:
case COGL_PIXEL_FORMAT_RGBA_5551:
@ -507,6 +508,35 @@ _cogl_bitmap_convert (CoglBitmap *src_bmp,
return dst_bmp;
}
static CoglBool
driver_can_convert (CoglContext *ctx,
CoglPixelFormat src_format,
CoglPixelFormat internal_format)
{
if (!_cogl_has_private_feature (ctx, COGL_PRIVATE_FEATURE_FORMAT_CONVERSION))
return FALSE;
if (src_format == internal_format)
return TRUE;
/* If the driver doesn't natively support alpha textures then it
* won't work correctly to convert to/from component-alpha
* textures */
if (!_cogl_has_private_feature (ctx, COGL_PRIVATE_FEATURE_ALPHA_TEXTURES) &&
(src_format == COGL_PIXEL_FORMAT_A_8 ||
internal_format == COGL_PIXEL_FORMAT_A_8))
return FALSE;
/* Same for red-green textures. If red-green textures aren't
* supported then the internal format should never be RG_88 but we
* should still be able to convert from an RG source image */
if (!cogl_has_feature (ctx, COGL_FEATURE_ID_TEXTURE_RG) &&
src_format == COGL_PIXEL_FORMAT_RG_88)
return FALSE;
return TRUE;
}
CoglBitmap *
_cogl_bitmap_convert_for_upload (CoglBitmap *src_bmp,
CoglPixelFormat internal_format,
@ -527,15 +557,7 @@ _cogl_bitmap_convert_for_upload (CoglBitmap *src_bmp,
limited number of formats so we must convert using the Cogl
bitmap code instead */
/* If the driver doesn't natively support alpha textures then it
* won't work correctly to convert to/from component-alpha
* textures */
if (_cogl_has_private_feature (ctx, COGL_PRIVATE_FEATURE_FORMAT_CONVERSION) &&
(_cogl_has_private_feature (ctx, COGL_PRIVATE_FEATURE_ALPHA_TEXTURES) ||
(src_format != COGL_PIXEL_FORMAT_A_8 &&
internal_format != COGL_PIXEL_FORMAT_A_8) ||
src_format == internal_format))
if (driver_can_convert (ctx, src_format, internal_format))
{
/* If the source format does not have the same premult flag as the
internal_format then we need to copy and convert it */

View File

@ -78,6 +78,22 @@ G_PASTE (_cogl_unpack_g_8_, component_size) (const uint8_t *src,
}
}
inline static void
G_PASTE (_cogl_unpack_rg_88_, component_size) (const uint8_t *src,
component_type *dst,
int width)
{
while (width-- > 0)
{
dst[0] = UNPACK_BYTE (src[0]);
dst[1] = UNPACK_BYTE (src[1]);
dst[2] = 0;
dst[3] = UNPACK_BYTE (255);
dst += 4;
src += 2;
}
}
inline static void
G_PASTE (_cogl_unpack_rgb_888_, component_size) (const uint8_t *src,
component_type *dst,
@ -321,6 +337,9 @@ G_PASTE (_cogl_unpack_, component_size) (CoglPixelFormat format,
case COGL_PIXEL_FORMAT_G_8:
G_PASTE (_cogl_unpack_g_8_, component_size) (src, dst, width);
break;
case COGL_PIXEL_FORMAT_RG_88:
G_PASTE (_cogl_unpack_rg_88_, component_size) (src, dst, width);
break;
case COGL_PIXEL_FORMAT_RGB_888:
G_PASTE (_cogl_unpack_rgb_888_, component_size) (src, dst, width);
break;
@ -424,6 +443,20 @@ G_PASTE (_cogl_pack_g_8_, component_size) (const component_type *src,
}
}
inline static void
G_PASTE (_cogl_pack_rg_88_, component_size) (const component_type *src,
uint8_t *dst,
int width)
{
while (width-- > 0)
{
dst[0] = PACK_BYTE (src[0]);
dst[1] = PACK_BYTE (src[1]);
src += 4;
dst += 2;
}
}
inline static void
G_PASTE (_cogl_pack_rgb_888_, component_size) (const component_type *src,
uint8_t *dst,
@ -665,6 +698,9 @@ G_PASTE (_cogl_pack_, component_size) (CoglPixelFormat format,
case COGL_PIXEL_FORMAT_G_8:
G_PASTE (_cogl_pack_g_8_, component_size) (src, dst, width);
break;
case COGL_PIXEL_FORMAT_RG_88:
G_PASTE (_cogl_pack_rg_88_, component_size) (src, dst, width);
break;
case COGL_PIXEL_FORMAT_RGB_888:
G_PASTE (_cogl_pack_rgb_888_, component_size) (src, dst, width);
break;

View File

@ -200,6 +200,9 @@ cogl_is_context (void *object);
* and %COGL_FEATURE_ID_TEXTURE_NPOT_REPEAT features combined.
* @COGL_FEATURE_ID_TEXTURE_RECTANGLE: Support for rectangular
* textures with non-normalized texture coordinates.
* @COGL_FEATURE_ID_TEXTURE_RG: Support for
* %COGL_TEXTURE_COMPONENTS_RG as the internal components of a
* texture.
* @COGL_FEATURE_ID_TEXTURE_3D: 3D texture support
* @COGL_FEATURE_ID_OFFSCREEN: Offscreen rendering support
* @COGL_FEATURE_ID_OFFSCREEN_MULTISAMPLE: Multisample support for
@ -263,6 +266,7 @@ typedef enum _CoglFeatureID
COGL_FEATURE_ID_PRESENTATION_TIME,
COGL_FEATURE_ID_FENCE,
COGL_FEATURE_ID_PER_VERTEX_POINT_SIZE,
COGL_FEATURE_ID_TEXTURE_RG,
/*< private >*/
_COGL_N_FEATURE_IDS /*< skip >*/

View File

@ -1377,6 +1377,14 @@ cogl_texture_allocate (CoglTexture *texture,
if (texture->allocated)
return TRUE;
if (texture->components == COGL_TEXTURE_COMPONENTS_RG &&
!cogl_has_feature (texture->context, COGL_FEATURE_ID_TEXTURE_RG))
_cogl_set_error (error,
COGL_TEXTURE_ERROR,
COGL_TEXTURE_ERROR_FORMAT,
"A red-green texture was requested but the driver "
"does not support them");
texture->allocated = texture->vtable->allocate (texture, error);
return texture->allocated;
@ -1396,6 +1404,11 @@ _cogl_texture_set_internal_format (CoglTexture *texture,
texture->components = COGL_TEXTURE_COMPONENTS_A;
return;
}
else if (internal_format == COGL_PIXEL_FORMAT_RG_88)
{
texture->components = COGL_TEXTURE_COMPONENTS_RG;
return;
}
else if (internal_format & COGL_DEPTH_BIT)
{
texture->components = COGL_TEXTURE_COMPONENTS_DEPTH;
@ -1437,6 +1450,8 @@ _cogl_texture_determine_internal_format (CoglTexture *texture,
}
case COGL_TEXTURE_COMPONENTS_A:
return COGL_PIXEL_FORMAT_A_8;
case COGL_TEXTURE_COMPONENTS_RG:
return COGL_PIXEL_FORMAT_RG_88;
case COGL_TEXTURE_COMPONENTS_RGB:
if (src_format != COGL_PIXEL_FORMAT_ANY &&
!(src_format & COGL_A_BIT) && !(src_format & COGL_DEPTH_BIT))

View File

@ -127,6 +127,9 @@ cogl_is_texture (void *object);
/**
* CoglTextureComponents:
* @COGL_TEXTURE_COMPONENTS_A: Only the alpha component
* @COGL_TEXTURE_COMPONENTS_RG: Red and green components. Note that
* this can only be used if the %COGL_FEATURE_ID_TEXTURE_RG feature
* is advertised.
* @COGL_TEXTURE_COMPONENTS_RGB: Red, green and blue components
* @COGL_TEXTURE_COMPONENTS_RGBA: Red, green, blue and alpha components
* @COGL_TEXTURE_COMPONENTS_DEPTH: Only a depth component
@ -138,6 +141,7 @@ cogl_is_texture (void *object);
typedef enum _CoglTextureComponents
{
COGL_TEXTURE_COMPONENTS_A = 1,
COGL_TEXTURE_COMPONENTS_RG,
COGL_TEXTURE_COMPONENTS_RGB,
COGL_TEXTURE_COMPONENTS_RGBA,
COGL_TEXTURE_COMPONENTS_DEPTH
@ -158,6 +162,15 @@ typedef enum _CoglTextureComponents
* a %CoglBitmap or a data pointer default to the same components as
* the pixel format of the data.
*
* Note that the %COGL_TEXTURE_COMPONENTS_RG format is not available
* on all drivers. The availability can be determined by checking for
* the %COGL_FEATURE_ID_TEXTURE_RG feature. If this format is used on
* a driver where it is not available then %COGL_TEXTURE_ERROR_FORMAT
* will be raised when the texture is allocated. Even if the feature
* is not available then %COGL_PIXEL_FORMAT_RG_88 can still be used as
* an image format as long as %COGL_TEXTURE_COMPONENTS_RG isn't used
* as the texture's components.
*
* Since: 1.18
*/
void

View File

@ -298,6 +298,9 @@ typedef struct _CoglTextureVertex CoglTextureVertex;
* CoglPixelFormat:
* @COGL_PIXEL_FORMAT_ANY: Any format
* @COGL_PIXEL_FORMAT_A_8: 8 bits alpha mask
* @COGL_PIXEL_FORMAT_RG_88: RG, 16 bits. Note that red-green textures
* are only available if %COGL_FEATURE_ID_TEXTURE_RG is advertised.
* See cogl_texture_set_components() for details.
* @COGL_PIXEL_FORMAT_RGB_565: RGB, 16 bits
* @COGL_PIXEL_FORMAT_RGBA_4444: RGBA, 16 bits
* @COGL_PIXEL_FORMAT_RGBA_5551: RGBA, 16 bits
@ -355,6 +358,8 @@ typedef enum { /*< prefix=COGL_PIXEL_FORMAT >*/
COGL_PIXEL_FORMAT_YUV = 7,
COGL_PIXEL_FORMAT_G_8 = 8,
COGL_PIXEL_FORMAT_RG_88 = 9,
COGL_PIXEL_FORMAT_RGB_888 = 2,
COGL_PIXEL_FORMAT_BGR_888 = (2 | COGL_BGR_BIT),

View File

@ -68,6 +68,10 @@ _cogl_driver_pixel_format_from_gl_internal (CoglContext *context,
*out_format = COGL_PIXEL_FORMAT_G_8;
return TRUE;
case GL_RG:
*out_format = COGL_PIXEL_FORMAT_RG_88;
return TRUE;
case GL_RGB: case GL_RGB4: case GL_RGB5: case GL_RGB8:
case GL_RGB10: case GL_RGB12: case GL_RGB16: case GL_R3_G3_B2:
@ -124,6 +128,26 @@ _cogl_driver_pixel_format_to_gl (CoglContext *context,
gltype = GL_UNSIGNED_BYTE;
break;
case COGL_PIXEL_FORMAT_RG_88:
if (cogl_has_feature (context, COGL_FEATURE_ID_TEXTURE_RG))
{
glintformat = GL_RG;
glformat = GL_RG;
}
else
{
/* If red-green textures aren't supported then we'll use RGB
* as an internal format. Note this should only end up
* mattering for downloading the data because Cogl will
* refuse to allocate a texture with RG components if RG
* textures aren't supported */
glintformat = GL_RGB;
glformat = GL_RGB;
required_format = COGL_PIXEL_FORMAT_RGB_888;
}
gltype = GL_UNSIGNED_BYTE;
break;
case COGL_PIXEL_FORMAT_RGB_888:
glintformat = GL_RGB;
glformat = GL_RGB;
@ -637,6 +661,12 @@ _cogl_driver_update_features (CoglContext *ctx,
if (ctx->glFenceSync)
COGL_FLAGS_SET (ctx->features, COGL_FEATURE_ID_FENCE, TRUE);
if (COGL_CHECK_GL_VERSION (gl_major, gl_minor, 3, 0) ||
_cogl_check_extension ("GL_ARB_texture_rg", gl_extensions))
COGL_FLAGS_SET (ctx->features,
COGL_FEATURE_ID_TEXTURE_RG,
TRUE);
/* Cache features */
for (i = 0; i < G_N_ELEMENTS (private_features); i++)
ctx->private_features[i] |= private_features[i];

View File

@ -44,6 +44,12 @@
#ifndef GL_DEPTH_STENCIL
#define GL_DEPTH_STENCIL 0x84F9
#endif
#ifndef GL_RG
#define GL_RG 0x8227
#endif
#ifndef GL_RG8
#define GL_RG8 0x822B
#endif
static CoglBool
_cogl_driver_pixel_format_from_gl_internal (CoglContext *context,
@ -81,6 +87,26 @@ _cogl_driver_pixel_format_to_gl (CoglContext *context,
gltype = GL_UNSIGNED_BYTE;
break;
case COGL_PIXEL_FORMAT_RG_88:
if (cogl_has_feature (context, COGL_FEATURE_ID_TEXTURE_RG))
{
glintformat = GL_RG8;
glformat = GL_RG;
}
else
{
/* If red-green textures aren't supported then we'll use RGB
* as an internal format. Note this should only end up
* mattering for downloading the data because Cogl will
* refuse to allocate a texture with RG components if RG
* textures aren't supported */
glintformat = GL_RGB;
glformat = GL_RGB;
required_format = COGL_PIXEL_FORMAT_RGB_888;
}
gltype = GL_UNSIGNED_BYTE;
break;
case COGL_PIXEL_FORMAT_BGRA_8888:
case COGL_PIXEL_FORMAT_BGRA_8888_PRE:
/* There is an extension to support this format */
@ -367,6 +393,11 @@ _cogl_driver_update_features (CoglContext *context,
_cogl_check_extension ("GL_OES_egl_sync", gl_extensions))
COGL_FLAGS_SET (private_features, COGL_PRIVATE_FEATURE_OES_EGL_SYNC, TRUE);
if (_cogl_check_extension ("GL_EXT_texture_rg", gl_extensions))
COGL_FLAGS_SET (context->features,
COGL_FEATURE_ID_TEXTURE_RG,
TRUE);
/* Cache features */
for (i = 0; i < G_N_ELEMENTS (private_features); i++)
context->private_features[i] |= private_features[i];

View File

@ -42,6 +42,12 @@ check_flags (TestFlags flags,
return FALSE;
}
if (flags & TEST_REQUIREMENT_TEXTURE_RG &&
!cogl_has_feature (test_ctx, COGL_FEATURE_ID_TEXTURE_RG))
{
return FALSE;
}
if (flags & TEST_REQUIREMENT_POINT_SPRITE &&
!cogl_has_feature (test_ctx, COGL_FEATURE_ID_POINT_SPRITE))
{

View File

@ -35,13 +35,14 @@ typedef enum _TestFlags
TEST_REQUIREMENT_NPOT = 1<<2,
TEST_REQUIREMENT_TEXTURE_3D = 1<<3,
TEST_REQUIREMENT_TEXTURE_RECTANGLE = 1<<4,
TEST_REQUIREMENT_POINT_SPRITE = 1<<5,
TEST_REQUIREMENT_GLES2_CONTEXT = 1<<6,
TEST_REQUIREMENT_MAP_WRITE = 1<<7,
TEST_REQUIREMENT_GLSL = 1<<8,
TEST_REQUIREMENT_OFFSCREEN = 1<<9,
TEST_REQUIREMENT_FENCE = 1<<10,
TEST_REQUIREMENT_PER_VERTEX_POINT_SIZE = 1<<11
TEST_REQUIREMENT_TEXTURE_RG = 1<<5,
TEST_REQUIREMENT_POINT_SPRITE = 1<<6,
TEST_REQUIREMENT_GLES2_CONTEXT = 1<<7,
TEST_REQUIREMENT_MAP_WRITE = 1<<8,
TEST_REQUIREMENT_GLSL = 1<<9,
TEST_REQUIREMENT_OFFSCREEN = 1<<10,
TEST_REQUIREMENT_FENCE = 1<<11,
TEST_REQUIREMENT_PER_VERTEX_POINT_SIZE = 1<<12
} TestFlags;
/**

View File

@ -67,6 +67,7 @@ test_sources = \
test-pipeline-cache-unrefs-texture.c \
test-texture-no-allocate.c \
test-pipeline-shader-state.c \
test-texture-rg.c \
$(NULL)
if !USING_EMSCRIPTEN

View File

@ -149,6 +149,8 @@ main (int argc, char **argv)
ADD_TEST (test_texture_no_allocate, 0, 0);
ADD_TEST (test_texture_rg, TEST_REQUIREMENT_TEXTURE_RG, 0);
g_printerr ("Unknown test name \"%s\"\n", argv[1]);
return 1;

View File

@ -79,6 +79,23 @@ test_read_888 (CoglTexture2D *tex_2d,
test_utils_compare_pixel (pixel, expected_pixel);
}
static void
test_read_88 (CoglTexture2D *tex_2d,
CoglPixelFormat format,
uint32_t expected_pixel)
{
uint8_t pixel[4];
pixel[2] = 0x00;
cogl_texture_get_data (tex_2d,
format,
2, /* rowstride */
pixel);
test_utils_compare_pixel (pixel, expected_pixel);
}
static void
test_read_8888 (CoglTexture2D *tex_2d,
CoglPixelFormat format,
@ -162,6 +179,11 @@ test_read_texture_formats (void)
test_read_byte (tex_2d, COGL_PIXEL_FORMAT_G_8, 0x9c);
#endif
/* We should always be able to read into an RG buffer regardless of
* whether RG textures are supported because Cogl will do the
* conversion for us */
test_read_88 (tex_2d, COGL_PIXEL_FORMAT_RG_88, 0x123400ff);
test_read_short (tex_2d, COGL_PIXEL_FORMAT_RGB_565,
5, 0x12, 6, 0x34, 5, 0x56,
-1);

View File

@ -0,0 +1,74 @@
#include <cogl/cogl.h>
#include <string.h>
#include "test-utils.h"
#define TEX_WIDTH 8
#define TEX_HEIGHT 8
static CoglTexture2D *
make_texture (void)
{
uint8_t tex_data[TEX_WIDTH * TEX_HEIGHT * 2], *p = tex_data;
int x, y;
for (y = 0; y < TEX_HEIGHT; y++)
for (x = 0; x < TEX_WIDTH; x++)
{
*(p++) = x * 256 / TEX_WIDTH;
*(p++) = y * 256 / TEX_HEIGHT;
}
return cogl_texture_2d_new_from_data (test_ctx,
TEX_WIDTH, TEX_HEIGHT,
COGL_PIXEL_FORMAT_RG_88,
TEX_WIDTH * 2,
tex_data,
NULL);
}
void
test_texture_rg (void)
{
CoglPipeline *pipeline;
CoglTexture2D *tex;
int fb_width, fb_height;
int x, y;
fb_width = cogl_framebuffer_get_width (test_fb);
fb_height = cogl_framebuffer_get_height (test_fb);
tex = make_texture ();
g_assert (cogl_texture_get_components (tex) == COGL_TEXTURE_COMPONENTS_RG);
pipeline = cogl_pipeline_new (test_ctx);
cogl_pipeline_set_layer_texture (pipeline, 0, tex);
cogl_pipeline_set_layer_filters (pipeline,
0,
COGL_PIPELINE_FILTER_NEAREST,
COGL_PIPELINE_FILTER_NEAREST);
cogl_framebuffer_draw_rectangle (test_fb,
pipeline,
-1.0f, 1.0f,
1.0f, -1.0f);
for (y = 0; y < TEX_HEIGHT; y++)
for (x = 0; x < TEX_WIDTH; x++)
{
test_utils_check_pixel_rgb (test_fb,
x * fb_width / TEX_WIDTH +
fb_width / (TEX_WIDTH * 2),
y * fb_height / TEX_HEIGHT +
fb_height / (TEX_HEIGHT * 2),
x * 256 / TEX_WIDTH,
y * 256 / TEX_HEIGHT,
0);
}
cogl_object_unref (pipeline);
cogl_object_unref (tex);
}

View File

@ -141,6 +141,11 @@ test_write_texture_formats (void)
test_write_byte (test_ctx, COGL_PIXEL_FORMAT_G_8, 0x34, 0x340000ff);
#endif
/* We should always be able to read from an RG buffer regardless of
* whether RG textures are supported because Cogl will do the
* conversion for us */
test_write_bytes (test_ctx, COGL_PIXEL_FORMAT_RG_88, 0x123456ff, 0x123400ff);
test_write_short (test_ctx, COGL_PIXEL_FORMAT_RGB_565, 0x0843, 0x080819ff);
test_write_short (test_ctx, COGL_PIXEL_FORMAT_RGBA_4444_PRE, 0x1234, 0x11223344);
test_write_short (test_ctx, COGL_PIXEL_FORMAT_RGBA_5551_PRE, 0x0887, 0x081019ff);