mutter/cogl/tests/conform/test-premult.c

287 lines
9.6 KiB
C
Raw Permalink Normal View History

#include <cogl/cogl.h>
#include <string.h>
#include "test-declarations.h"
#include "test-utils.h"
#define QUAD_WIDTH 32
#define RED 0
#define GREEN 1
#define BLUE 2
#define ALPHA 3
#define MASK_RED(COLOR) ((COLOR & 0xff000000) >> 24)
#define MASK_GREEN(COLOR) ((COLOR & 0xff0000) >> 16)
#define MASK_BLUE(COLOR) ((COLOR & 0xff00) >> 8)
#define MASK_ALPHA(COLOR) (COLOR & 0xff)
typedef enum _MakeTextureFlags
{
TEXTURE_FLAG_SET_PREMULTIPLIED = 1,
TEXTURE_FLAG_SET_UNPREMULTIPLIED = 1<<1,
} MakeTextureFlags;
static guchar *
gen_tex_data (uint32_t color)
{
guchar *tex_data, *p;
uint8_t r = MASK_RED (color);
uint8_t g = MASK_GREEN (color);
uint8_t b = MASK_BLUE (color);
uint8_t a = MASK_ALPHA (color);
tex_data = g_malloc (QUAD_WIDTH * QUAD_WIDTH * 4);
for (p = tex_data + QUAD_WIDTH * QUAD_WIDTH * 4; p > tex_data;)
{
*(--p) = a;
*(--p) = b;
*(--p) = g;
*(--p) = r;
}
return tex_data;
}
static CoglTexture *
make_texture (uint32_t color,
CoglPixelFormat src_format,
MakeTextureFlags flags)
{
cogl test-premult: Don't free texture data until CoglBitmap is freed According to the cogl_bitmap_new_for_data documentation, the data is not copied, so the application must keep the buffer alive for the lifetime of the CoglBitmap. Freeing it too early led to a use-after-free in the cogl unit tests. With that fixed, the test passes, so remove the known failure annotation. This AddressSanitizer trace is from the original cogl, but the bug and fix apply equally to mutter's fork of cogl: ==6223==ERROR: AddressSanitizer: heap-use-after-free on address 0x62100001a500 at pc 0x7f3e2d4e7f4e bp 0x7ffcd9c41f30 sp 0x7ffcd9c416e0 READ of size 4096 at 0x62100001a500 thread T0 #0 0x7f3e2d4e7f4d (/usr/lib/x86_64-linux-gnu/libasan.so.5+0x96f4d) #1 0x7f3e260c7f6b in util_copy_box ../src/gallium/auxiliary/util/u_surface.c:131 #2 0x7f3e268c6c10 in u_default_texture_subdata ../src/gallium/auxiliary/util/u_transfer.c:67 #3 0x7f3e26486459 in st_TexSubImage ../src/mesa/state_tracker/st_cb_texture.c:1480 #4 0x7f3e26487029 in st_TexImage ../src/mesa/state_tracker/st_cb_texture.c:1709 #5 0x7f3e26487029 in st_TexImage ../src/mesa/state_tracker/st_cb_texture.c:1691 #6 0x7f3e2644bdba in teximage ../src/mesa/main/teximage.c:3105 #7 0x7f3e2644bdba in teximage_err ../src/mesa/main/teximage.c:3132 #8 0x7f3e2644d84f in _mesa_TexImage2D ../src/mesa/main/teximage.c:3170 #9 0x7f3e2cd1f7df in _cogl_texture_driver_upload_to_gl driver/gl/gl/cogl-texture-driver-gl.c:347 #10 0x7f3e2ccd441b in allocate_from_bitmap driver/gl/cogl-texture-2d-gl.c:255 #11 0x7f3e2ccd441b in _cogl_texture_2d_gl_allocate driver/gl/cogl-texture-2d-gl.c:462 #12 0x7f3e2ce3a6c0 in cogl_texture_allocate cogl/cogl-texture.c:1398 #13 0x7f3e2ce3e116 in _cogl_texture_pre_paint cogl/cogl-texture.c:359 #14 0x7f3e2cdee177 in _cogl_pipeline_layer_pre_paint cogl/cogl-pipeline-layer.c:864 #15 0x7f3e2cd574af in _cogl_rectangles_validate_layer_cb cogl/cogl-primitives.c:542 #16 0x7f3e2cdd742f in cogl_pipeline_foreach_layer cogl/cogl-pipeline.c:735 #17 0x7f3e2cd5c8b0 in _cogl_framebuffer_draw_multitextured_rectangles cogl/cogl-primitives.c:658 #18 0x7f3e2cd60152 in cogl_rectangle cogl/cogl-primitives.c:858 #19 0x5570a71ed6a0 in check_texture tests/conform/test-premult.c:103 #20 0x5570a71ed946 in test_premult tests/conform/test-premult.c:159 #21 0x5570a71df0d6 in main tests/conform/test-conform-main.c:58 #22 0x7f3e2bcd809a in __libc_start_main ../csu/libc-start.c:308 #23 0x5570a71e0869 in _start (/home/smcv/src/debian/cogl/tests/conform/.libs/test-conformance+0x33869) 0x62100001a500 is located 0 bytes inside of 4096-byte region [0x62100001a500,0x62100001b500) freed by thread T0 here: #0 0x7f3e2d5581d7 in __interceptor_free (/usr/lib/x86_64-linux-gnu/libasan.so.5+0x1071d7) #1 0x5570a71ed58b in make_texture tests/conform/test-premult.c:69 previously allocated by thread T0 here: #0 0x7f3e2d558588 in malloc (/usr/lib/x86_64-linux-gnu/libasan.so.5+0x107588) #1 0x7f3e2d384500 in g_malloc ../../../glib/gmem.c:99 This was originally cogl!12. https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/1274 Signed-off-by: Simon McVittie <smcv@debian.org>
2019-08-25 11:44:07 -04:00
static CoglUserDataKey bitmap_free_key;
CoglTexture2D *tex_2d;
guchar *tex_data = gen_tex_data (color);
CoglBitmap *bmp = cogl_bitmap_new_for_data (test_ctx,
QUAD_WIDTH,
QUAD_WIDTH,
src_format,
QUAD_WIDTH * 4,
tex_data);
cogl test-premult: Don't free texture data until CoglBitmap is freed According to the cogl_bitmap_new_for_data documentation, the data is not copied, so the application must keep the buffer alive for the lifetime of the CoglBitmap. Freeing it too early led to a use-after-free in the cogl unit tests. With that fixed, the test passes, so remove the known failure annotation. This AddressSanitizer trace is from the original cogl, but the bug and fix apply equally to mutter's fork of cogl: ==6223==ERROR: AddressSanitizer: heap-use-after-free on address 0x62100001a500 at pc 0x7f3e2d4e7f4e bp 0x7ffcd9c41f30 sp 0x7ffcd9c416e0 READ of size 4096 at 0x62100001a500 thread T0 #0 0x7f3e2d4e7f4d (/usr/lib/x86_64-linux-gnu/libasan.so.5+0x96f4d) #1 0x7f3e260c7f6b in util_copy_box ../src/gallium/auxiliary/util/u_surface.c:131 #2 0x7f3e268c6c10 in u_default_texture_subdata ../src/gallium/auxiliary/util/u_transfer.c:67 #3 0x7f3e26486459 in st_TexSubImage ../src/mesa/state_tracker/st_cb_texture.c:1480 #4 0x7f3e26487029 in st_TexImage ../src/mesa/state_tracker/st_cb_texture.c:1709 #5 0x7f3e26487029 in st_TexImage ../src/mesa/state_tracker/st_cb_texture.c:1691 #6 0x7f3e2644bdba in teximage ../src/mesa/main/teximage.c:3105 #7 0x7f3e2644bdba in teximage_err ../src/mesa/main/teximage.c:3132 #8 0x7f3e2644d84f in _mesa_TexImage2D ../src/mesa/main/teximage.c:3170 #9 0x7f3e2cd1f7df in _cogl_texture_driver_upload_to_gl driver/gl/gl/cogl-texture-driver-gl.c:347 #10 0x7f3e2ccd441b in allocate_from_bitmap driver/gl/cogl-texture-2d-gl.c:255 #11 0x7f3e2ccd441b in _cogl_texture_2d_gl_allocate driver/gl/cogl-texture-2d-gl.c:462 #12 0x7f3e2ce3a6c0 in cogl_texture_allocate cogl/cogl-texture.c:1398 #13 0x7f3e2ce3e116 in _cogl_texture_pre_paint cogl/cogl-texture.c:359 #14 0x7f3e2cdee177 in _cogl_pipeline_layer_pre_paint cogl/cogl-pipeline-layer.c:864 #15 0x7f3e2cd574af in _cogl_rectangles_validate_layer_cb cogl/cogl-primitives.c:542 #16 0x7f3e2cdd742f in cogl_pipeline_foreach_layer cogl/cogl-pipeline.c:735 #17 0x7f3e2cd5c8b0 in _cogl_framebuffer_draw_multitextured_rectangles cogl/cogl-primitives.c:658 #18 0x7f3e2cd60152 in cogl_rectangle cogl/cogl-primitives.c:858 #19 0x5570a71ed6a0 in check_texture tests/conform/test-premult.c:103 #20 0x5570a71ed946 in test_premult tests/conform/test-premult.c:159 #21 0x5570a71df0d6 in main tests/conform/test-conform-main.c:58 #22 0x7f3e2bcd809a in __libc_start_main ../csu/libc-start.c:308 #23 0x5570a71e0869 in _start (/home/smcv/src/debian/cogl/tests/conform/.libs/test-conformance+0x33869) 0x62100001a500 is located 0 bytes inside of 4096-byte region [0x62100001a500,0x62100001b500) freed by thread T0 here: #0 0x7f3e2d5581d7 in __interceptor_free (/usr/lib/x86_64-linux-gnu/libasan.so.5+0x1071d7) #1 0x5570a71ed58b in make_texture tests/conform/test-premult.c:69 previously allocated by thread T0 here: #0 0x7f3e2d558588 in malloc (/usr/lib/x86_64-linux-gnu/libasan.so.5+0x107588) #1 0x7f3e2d384500 in g_malloc ../../../glib/gmem.c:99 This was originally cogl!12. https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/1274 Signed-off-by: Simon McVittie <smcv@debian.org>
2019-08-25 11:44:07 -04:00
cogl_object_set_user_data (COGL_OBJECT (bmp),
&bitmap_free_key,
tex_data,
g_free);
tex_2d = cogl_texture_2d_new_from_bitmap (bmp);
if (flags & TEXTURE_FLAG_SET_PREMULTIPLIED)
cogl_texture_set_premultiplied (tex_2d, TRUE);
else if (flags & TEXTURE_FLAG_SET_UNPREMULTIPLIED)
cogl_texture_set_premultiplied (tex_2d, FALSE);
cogl_object_unref (bmp);
return tex_2d;
}
static void
set_region (CoglTexture *tex,
uint32_t color,
CoglPixelFormat format)
{
guchar *tex_data = gen_tex_data (color);
cogl_texture_set_region (tex,
0, 0, /* src x, y */
0, 0, /* dst x, y */
QUAD_WIDTH, QUAD_WIDTH, /* dst width, height */
QUAD_WIDTH, QUAD_WIDTH, /* src width, height */
format,
0, /* auto compute row stride */
tex_data);
}
static void
check_texture (CoglPipeline *pipeline,
int x,
int y,
CoglTexture *tex,
uint32_t expected_result)
{
/* New API */
cogl_pipeline_set_layer_texture (pipeline, 0, tex);
cogl_framebuffer_draw_rectangle (test_fb, pipeline,
x * QUAD_WIDTH,
y * QUAD_WIDTH,
x * QUAD_WIDTH + QUAD_WIDTH,
y * QUAD_WIDTH + QUAD_WIDTH);
test_utils_check_pixel (test_fb, x * QUAD_WIDTH + QUAD_WIDTH / 2, y * QUAD_WIDTH + QUAD_WIDTH / 2, expected_result);
}
void
test_premult (void)
{
CoglPipeline *pipeline;
CoglTexture *tex;
cogl_framebuffer_orthographic (test_fb, 0, 0,
cogl_framebuffer_get_width (test_fb),
cogl_framebuffer_get_height (test_fb),
-1,
100);
cogl_framebuffer_clear4f (test_fb,
COGL_BUFFER_BIT_COLOR,
1.0f, 1.0f, 1.0f, 1.0f);
/* New API */
pipeline = cogl_pipeline_new (test_ctx);
cogl_pipeline_set_blend (pipeline,
"RGBA = ADD (SRC_COLOR, 0)", NULL);
cogl_pipeline_set_layer_combine (pipeline, 0,
"RGBA = REPLACE (TEXTURE)", NULL);
/* If the user explicitly specifies an unmultiplied internal format then
* Cogl shouldn't automatically premultiply the given texture data... */
if (cogl_test_verbose ())
g_print ("make_texture (0xff00ff80, "
"src = RGBA_8888, internal = RGBA_8888)\n");
tex = make_texture (0xff00ff80,
COGL_PIXEL_FORMAT_RGBA_8888, /* src format */
TEXTURE_FLAG_SET_UNPREMULTIPLIED);
check_texture (pipeline, 0, 0, /* position */
tex,
0xff00ff80); /* expected */
/* If the user explicitly requests a premultiplied internal format and
* gives unmultiplied src data then Cogl should always premultiply that
* for us */
if (cogl_test_verbose ())
g_print ("make_texture (0xff00ff80, "
"src = RGBA_8888, internal = RGBA_8888_PRE)\n");
tex = make_texture (0xff00ff80,
COGL_PIXEL_FORMAT_RGBA_8888, /* src format */
TEXTURE_FLAG_SET_PREMULTIPLIED);
check_texture (pipeline, 1, 0, /* position */
tex,
0x80008080); /* expected */
/* If the user doesn't explicitly declare that the texture is premultiplied
* then Cogl should assume it is by default should premultiply
* unpremultiplied texture data...
*/
if (cogl_test_verbose ())
g_print ("make_texture (0xff00ff80, "
"src = RGBA_8888, internal = ANY)\n");
tex = make_texture (0xff00ff80,
COGL_PIXEL_FORMAT_RGBA_8888, /* src format */
0); /* default premultiplied status */
check_texture (pipeline, 2, 0, /* position */
tex,
0x80008080); /* expected */
/* If the user requests a premultiplied internal texture format and supplies
* premultiplied source data, Cogl should never modify that source data...
*/
if (cogl_test_verbose ())
g_print ("make_texture (0x80008080, "
"src = RGBA_8888_PRE, "
"internal = RGBA_8888_PRE)\n");
tex = make_texture (0x80008080,
COGL_PIXEL_FORMAT_RGBA_8888_PRE, /* src format */
TEXTURE_FLAG_SET_PREMULTIPLIED);
check_texture (pipeline, 3, 0, /* position */
tex,
0x80008080); /* expected */
/* If the user requests an unmultiplied internal texture format, but
* supplies premultiplied source data, then Cogl should always
* un-premultiply the source data... */
if (cogl_test_verbose ())
g_print ("make_texture (0x80008080, "
"src = RGBA_8888_PRE, internal = RGBA_8888)\n");
tex = make_texture (0x80008080,
COGL_PIXEL_FORMAT_RGBA_8888_PRE, /* src format */
TEXTURE_FLAG_SET_UNPREMULTIPLIED);
check_texture (pipeline, 4, 0, /* position */
tex,
0xff00ff80); /* expected */
/* If the user allows any internal texture format and provides premultipled
* source data then by default Cogl shouldn't modify the source data...
* (In the future there will be additional Cogl API to control this
* behaviour) */
if (cogl_test_verbose ())
g_print ("make_texture (0x80008080, "
"src = RGBA_8888_PRE, internal = ANY)\n");
tex = make_texture (0x80008080,
COGL_PIXEL_FORMAT_RGBA_8888_PRE, /* src format */
0); /* default premultiplied status */
check_texture (pipeline, 5, 0, /* position */
tex,
0x80008080); /* expected */
/*
* Test cogl_texture_set_region() ....
*/
if (cogl_test_verbose ())
g_print ("make_texture (0xDEADBEEF, "
"src = RGBA_8888, internal = RGBA_8888)\n");
tex = make_texture (0xDEADBEEF,
COGL_PIXEL_FORMAT_RGBA_8888, /* src format */
TEXTURE_FLAG_SET_UNPREMULTIPLIED);
if (cogl_test_verbose ())
g_print ("set_region (0xff00ff80, RGBA_8888)\n");
set_region (tex, 0xff00ff80, COGL_PIXEL_FORMAT_RGBA_8888);
check_texture (pipeline, 6, 0, /* position */
tex,
0xff00ff80); /* expected */
/* Updating a texture region for an unmultiplied texture using premultiplied
* region data should result in Cogl unmultiplying the given region data...
*/
if (cogl_test_verbose ())
g_print ("make_texture (0xDEADBEEF, "
"src = RGBA_8888, internal = RGBA_8888)\n");
tex = make_texture (0xDEADBEEF,
COGL_PIXEL_FORMAT_RGBA_8888, /* src format */
TEXTURE_FLAG_SET_UNPREMULTIPLIED);
if (cogl_test_verbose ())
g_print ("set_region (0x80008080, RGBA_8888_PRE)\n");
set_region (tex, 0x80008080, COGL_PIXEL_FORMAT_RGBA_8888_PRE);
check_texture (pipeline, 7, 0, /* position */
tex,
0xff00ff80); /* expected */
if (cogl_test_verbose ())
g_print ("make_texture (0xDEADBEEF, "
"src = RGBA_8888_PRE, "
"internal = RGBA_8888_PRE)\n");
tex = make_texture (0xDEADBEEF,
COGL_PIXEL_FORMAT_RGBA_8888_PRE, /* src format */
TEXTURE_FLAG_SET_PREMULTIPLIED);
if (cogl_test_verbose ())
g_print ("set_region (0x80008080, RGBA_8888_PRE)\n");
set_region (tex, 0x80008080, COGL_PIXEL_FORMAT_RGBA_8888_PRE);
check_texture (pipeline, 8, 0, /* position */
tex,
0x80008080); /* expected */
/* Updating a texture region for a premultiplied texture using unmultiplied
* region data should result in Cogl premultiplying the given region data...
*/
if (cogl_test_verbose ())
g_print ("make_texture (0xDEADBEEF, "
"src = RGBA_8888_PRE, "
"internal = RGBA_8888_PRE)\n");
tex = make_texture (0xDEADBEEF,
COGL_PIXEL_FORMAT_RGBA_8888_PRE, /* src format */
TEXTURE_FLAG_SET_PREMULTIPLIED);
if (cogl_test_verbose ())
g_print ("set_region (0xff00ff80, RGBA_8888)\n");
set_region (tex, 0xff00ff80, COGL_PIXEL_FORMAT_RGBA_8888);
check_texture (pipeline, 9, 0, /* position */
tex,
0x80008080); /* expected */
if (cogl_test_verbose ())
g_print ("OK\n");
}