diff options
-rw-r--r-- | cogl/cogl-gles2-context.c | 224 | ||||
-rw-r--r-- | tests/conform/test-conform-main.c | 1 | ||||
-rw-r--r-- | tests/conform/test-gles2-context.c | 231 |
3 files changed, 442 insertions, 14 deletions
diff --git a/cogl/cogl-gles2-context.c b/cogl/cogl-gles2-context.c index a7507c62..35857056 100644 --- a/cogl/cogl-gles2-context.c +++ b/cogl/cogl-gles2-context.c @@ -43,6 +43,7 @@ #include "cogl-renderer-private.h" #include "cogl-swap-chain-private.h" #include "cogl-texture-2d-private.h" +#include "cogl-pipeline-opengl-private.h" static void _cogl_gles2_context_free (CoglGLES2Context *gles2_context); @@ -241,6 +242,145 @@ set_texture_object_data (CoglGLES2Context *gles2_ctx, } } +static void +copy_flipped_texture (CoglGLES2Context *gles2_ctx, + int level, + int src_x, + int src_y, + int dst_x, + int dst_y, + int width, + int height) +{ + GLuint tex_id = get_current_texture_2d_object (gles2_ctx); + CoglGLES2TextureObjectData *tex_object_data; + CoglContext *ctx; + const CoglWinsysVtable *winsys; + CoglTexture2D *dst_texture; + CoglPixelFormat internal_format; + + tex_object_data = g_hash_table_lookup (gles2_ctx->texture_object_map, + GUINT_TO_POINTER (tex_id)); + + /* We can't do anything if the application hasn't set a level 0 + * image on this texture object */ + if (tex_object_data == NULL || + tex_object_data->target != GL_TEXTURE_2D || + tex_object_data->width <= 0 || + tex_object_data->height <= 0) + return; + + switch (tex_object_data->format) + { + case GL_RGB: + internal_format = COGL_PIXEL_FORMAT_RGB_888; + break; + + case GL_RGBA: + internal_format = COGL_PIXEL_FORMAT_RGBA_8888_PRE; + break; + + case GL_ALPHA: + internal_format = COGL_PIXEL_FORMAT_A_8; + break; + + case GL_LUMINANCE: + internal_format = COGL_PIXEL_FORMAT_G_8; + break; + + default: + /* We can't handle this format so just give up */ + return; + } + + ctx = gles2_ctx->context; + winsys = ctx->display->renderer->winsys_vtable; + + /* We need to make sure the rendering on the GLES2 context is + * complete before the blit will be ready in the GLES2 context */ + ctx->glFinish (); + /* We need to force Cogl to rebind the texture because according to + * the GL spec a shared texture isn't guaranteed to be updated until + * is rebound */ + _cogl_get_texture_unit (0)->dirty_gl_texture = TRUE; + + /* Temporarily switch back to the Cogl context */ + winsys->restore_context (ctx); + + dst_texture = + cogl_gles2_texture_2d_new_from_handle (gles2_ctx->context, + gles2_ctx, + tex_id, + tex_object_data->width, + tex_object_data->height, + internal_format, + NULL /* error */); + + if (dst_texture) + { + CoglTexture *src_texture = + COGL_OFFSCREEN (gles2_ctx->read_buffer)->texture; + CoglPipeline *pipeline = cogl_pipeline_new (ctx); + const CoglOffscreenFlags flags = COGL_OFFSCREEN_DISABLE_DEPTH_AND_STENCIL; + CoglOffscreen *offscreen = + _cogl_offscreen_new_to_texture_full (COGL_TEXTURE (dst_texture), + flags, level); + int src_width = cogl_texture_get_width (src_texture); + int src_height = cogl_texture_get_height (src_texture); + /* The framebuffer size might be different from the texture size + * if a level > 0 is used */ + int dst_width = + cogl_framebuffer_get_width (COGL_FRAMEBUFFER (offscreen)); + int dst_height = + cogl_framebuffer_get_height (COGL_FRAMEBUFFER (offscreen)); + float x_1, y_1, x_2, y_2, s_1, t_1, s_2, t_2; + + cogl_pipeline_set_layer_texture (pipeline, 0, src_texture); + cogl_pipeline_set_blend (pipeline, + "RGBA = ADD(SRC_COLOR, 0)", + NULL); + cogl_pipeline_set_layer_filters (pipeline, + 0, /* layer_num */ + COGL_PIPELINE_FILTER_NEAREST, + COGL_PIPELINE_FILTER_NEAREST); + + x_1 = dst_x * 2.0f / dst_width - 1.0f; + y_1 = dst_y * 2.0f / dst_height - 1.0f; + x_2 = x_1 + width * 2.0f / dst_width; + y_2 = y_1 + height * 2.0f / dst_height; + + s_1 = src_x / (float) src_width; + t_1 = 1.0f - src_y / (float) src_height; + s_2 = (src_x + width) / (float) src_width; + t_2 = 1.0f - (src_y + height) / (float) src_height; + + cogl_framebuffer_draw_textured_rectangle (COGL_FRAMEBUFFER (offscreen), + pipeline, + x_1, y_1, + x_2, y_2, + s_1, t_1, + s_2, t_2); + + _cogl_framebuffer_flush_journal (COGL_FRAMEBUFFER (offscreen)); + + /* We need to make sure the rendering is complete before the + * blit will be ready in the GLES2 context */ + ctx->glFinish (); + + cogl_object_unref (pipeline); + cogl_object_unref (dst_texture); + cogl_object_unref (offscreen); + } + + winsys->set_gles2_context (gles2_ctx, NULL); + + /* From what I understand of the GL spec, changes to a shared object + * are not guaranteed to be propagated to another context until that + * object is rebound in that context so we can just rebind it + * here */ + gles2_ctx->vtable->glBindTexture (GL_TEXTURE_2D, tex_id); +} + /* We wrap glBindFramebuffer so that when framebuffer 0 is bound * we can instead bind the write_framebuffer passed to * cogl_push_gles2_context(). @@ -437,18 +577,51 @@ gl_copy_tex_image_2d_wrapper (GLenum target, GLint border) { CoglGLES2Context *gles2_ctx = current_gles2_context; - int restore_mode = transient_bind_read_buffer (gles2_ctx); - gles2_ctx->context->glCopyTexImage2D (target, level, internal_format, - x, y, width, height, border); + /* If we are reading from a CoglOffscreen buffer then the image will + * be upside down with respect to what GL expects so we can't use + * glCopyTexImage2D. Instead we we'll try to use the Cogl API to + * flip it */ + if (gles2_ctx->current_fbo_handle == 0 && + cogl_is_offscreen (gles2_ctx->read_buffer)) + { + /* This will only work with the GL_TEXTURE_2D target. FIXME: + * GLES2 also supports setting cube map textures with + * glTexImage2D so we need to handle that too */ + if (target != GL_TEXTURE_2D) + return; + + /* Create an empty texture to hold the data */ + gles2_ctx->vtable->glTexImage2D (target, + level, + internal_format, + width, height, + border, + internal_format, /* format */ + GL_UNSIGNED_BYTE, /* type */ + NULL /* data */); + + copy_flipped_texture (gles2_ctx, + level, + x, y, /* src_x/src_y */ + 0, 0, /* dst_x/dst_y */ + width, height); + } + else + { + int restore_mode = transient_bind_read_buffer (gles2_ctx); - restore_write_buffer (gles2_ctx, restore_mode); + gles2_ctx->context->glCopyTexImage2D (target, level, internal_format, + x, y, width, height, border); - set_texture_object_data (gles2_ctx, - target, - level, - internal_format, - width, height); + restore_write_buffer (gles2_ctx, restore_mode); + + set_texture_object_data (gles2_ctx, + target, + level, + internal_format, + width, height); + } } static void @@ -462,13 +635,36 @@ gl_copy_tex_sub_image_2d_wrapper (GLenum target, GLsizei height) { CoglGLES2Context *gles2_ctx = current_gles2_context; - int restore_mode = transient_bind_read_buffer (gles2_ctx); - gles2_ctx->context->glCopyTexSubImage2D (target, level, - xoffset, yoffset, - x, y, width, height); + /* If we are reading from a CoglOffscreen buffer then the image will + * be upside down with respect to what GL expects so we can't use + * glCopyTexSubImage2D. Instead we we'll try to use the Cogl API to + * flip it */ + if (gles2_ctx->current_fbo_handle == 0 && + cogl_is_offscreen (gles2_ctx->read_buffer)) + { + /* This will only work with the GL_TEXTURE_2D target. FIXME: + * GLES2 also supports setting cube map textures with + * glTexImage2D so we need to handle that too */ + if (target != GL_TEXTURE_2D) + return; + + copy_flipped_texture (gles2_ctx, + level, + x, y, /* src_x/src_y */ + xoffset, yoffset, /* dst_x/dst_y */ + width, height); + } + else + { + int restore_mode = transient_bind_read_buffer (gles2_ctx); + + gles2_ctx->context->glCopyTexSubImage2D (target, level, + xoffset, yoffset, + x, y, width, height); - restore_write_buffer (gles2_ctx, restore_mode); + restore_write_buffer (gles2_ctx, restore_mode); + } } static GLuint diff --git a/tests/conform/test-conform-main.c b/tests/conform/test-conform-main.c index a9d7d6a2..74756850 100644 --- a/tests/conform/test-conform-main.c +++ b/tests/conform/test-conform-main.c @@ -104,6 +104,7 @@ main (int argc, char **argv) ADD_TEST (test_gles2_context, TEST_REQUIREMENT_GLES2_CONTEXT); ADD_TEST (test_gles2_context_fbo, TEST_REQUIREMENT_GLES2_CONTEXT); + ADD_TEST (test_gles2_context_copy_tex_image, TEST_REQUIREMENT_GLES2_CONTEXT); ADD_TEST (test_euler_quaternion, 0); diff --git a/tests/conform/test-gles2-context.c b/tests/conform/test-gles2-context.c index f9d01752..a55920b5 100644 --- a/tests/conform/test-gles2-context.c +++ b/tests/conform/test-gles2-context.c @@ -743,3 +743,234 @@ test_gles2_context_fbo (void) 0x0000ffff); } } + +/* Position to draw a rectangle in. The top half of this rectangle + * will be red, and the bottom will be blue */ +#define RECTANGLE_DRAW_X 10 +#define RECTANGLE_DRAW_Y 15 + +/* Position to copy the rectangle to in the destination texture */ +#define RECTANGLE_COPY_X 110 +#define RECTANGLE_COPY_Y 115 + +#define RECTANGLE_WIDTH 30 +#define RECTANGLE_HEIGHT 40 + +static void +verify_region (const CoglGLES2Vtable *gles2, + int x, + int y, + int width, + int height, + uint32_t expected_pixel) +{ + uint8_t *buf, *p; + + buf = g_malloc (width * height * 4); + + gles2->glReadPixels (x, y, width, height, GL_RGBA, GL_UNSIGNED_BYTE, buf); + + for (p = buf + width * height * 4; p > buf; p -= 4) + test_utils_compare_pixel (p - 4, expected_pixel); + + g_free (buf); +} + +void +test_gles2_context_copy_tex_image (void) +{ + static const char vertex_shader_source[] = + "attribute vec2 pos;\n" + "attribute vec2 tex_coord_attrib;\n" + "varying vec2 tex_coord_varying;\n" + "\n" + "void\n" + "main ()\n" + "{\n" + " gl_Position = vec4 (pos, 0.0, 1.0);\n" + " tex_coord_varying = tex_coord_attrib;\n" + "}\n"; + static const char fragment_shader_source[] = + "precision mediump float;\n" + "varying vec2 tex_coord_varying;\n" + "uniform sampler2D tex;\n" + "\n" + "void\n" + "main ()\n" + "{\n" + " gl_FragColor = texture2D (tex, tex_coord_varying);\n" + "}\n"; + static const float verts[] = + { + -1.0f, -1.0f, 0.0f, 0.0f, + 1.0f, -1.0f, 1.0f, 0.0f, + -1.0f, 1.0f, 0.0f, 1.0f, + 1.0f, 1.0f, 1.0f, 1.0f + }; + int fb_width = cogl_framebuffer_get_width (test_fb); + int fb_height = cogl_framebuffer_get_height (test_fb); + CoglTexture *offscreen_texture; + CoglOffscreen *offscreen; + CoglPipeline *pipeline; + CoglGLES2Context *gles2_ctx; + const CoglGLES2Vtable *gles2; + GError *error = NULL; + GLuint tex; + GLint tex_uniform_location; + GLint pos_location; + GLint tex_coord_location; + GLuint program; + + create_gles2_context (&offscreen_texture, + &offscreen, + &pipeline, + &gles2_ctx, + &gles2); + + if (!cogl_push_gles2_context (test_ctx, + gles2_ctx, + COGL_FRAMEBUFFER (offscreen), + COGL_FRAMEBUFFER (offscreen), + &error)) + g_error ("Failed to push gles2 context: %s\n", error->message); + + gles2->glClearColor (1.0, 1.0, 0.0, 1.0); + gles2->glClear (GL_COLOR_BUFFER_BIT); + + /* Draw a rectangle using clear and the scissor so that we don't + * have to create a shader */ + gles2->glEnable (GL_SCISSOR_TEST); + + /* Top half red */ + gles2->glScissor (RECTANGLE_DRAW_X, + RECTANGLE_DRAW_Y + RECTANGLE_HEIGHT / 2, + RECTANGLE_WIDTH, + RECTANGLE_HEIGHT / 2); + gles2->glClearColor (1.0, 0.0, 0.0, 1.0); + gles2->glClear (GL_COLOR_BUFFER_BIT); + /* Bottom half blue */ + gles2->glScissor (RECTANGLE_DRAW_X, + RECTANGLE_DRAW_Y, + RECTANGLE_WIDTH, + RECTANGLE_HEIGHT / 2); + gles2->glClearColor (0.0, 0.0, 1.0, 1.0); + gles2->glClear (GL_COLOR_BUFFER_BIT); + + /* Draw where the rectangle would be if the coordinates were flipped + * in white to make it obvious that that is the problem if the + * assertion fails */ + gles2->glScissor (RECTANGLE_DRAW_X, + fb_width - (RECTANGLE_DRAW_Y + RECTANGLE_HEIGHT), + RECTANGLE_WIDTH, + RECTANGLE_HEIGHT); + gles2->glClearColor (1.0, 1.0, 1.0, 1.0); + gles2->glClear (GL_COLOR_BUFFER_BIT); + + gles2->glDisable (GL_SCISSOR_TEST); + + /* Create a texture */ + gles2->glGenTextures (1, &tex); + gles2->glBindTexture (GL_TEXTURE_2D, tex); + gles2->glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + gles2->glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + + /* Copy the entire framebuffer into the texture */ + gles2->glCopyTexImage2D (GL_TEXTURE_2D, + 0, /* level */ + GL_RGBA, + 0, 0, /* x/y */ + fb_width, fb_height, + 0 /* border */); + + /* Copy the rectangle into another part of the texture */ + gles2->glCopyTexSubImage2D (GL_TEXTURE_2D, + 0, /* level */ + RECTANGLE_COPY_X, + RECTANGLE_COPY_Y, + RECTANGLE_DRAW_X, + RECTANGLE_DRAW_Y, + RECTANGLE_WIDTH, + RECTANGLE_HEIGHT); + + /* Clear the framebuffer to make the test more thorough */ + gles2->glClearColor (1.0, 1.0, 0.0, 1.0); + gles2->glClear (GL_COLOR_BUFFER_BIT); + + /* Create a program to render the texture */ + program = create_program (gles2, + vertex_shader_source, + fragment_shader_source); + + pos_location = + gles2->glGetAttribLocation (program, "pos"); + if (pos_location == -1) + g_error ("Couldn't find ‘pos’ attribute"); + + tex_coord_location = + gles2->glGetAttribLocation (program, "tex_coord_attrib"); + if (tex_coord_location == -1) + g_error ("Couldn't find ‘tex_coord_attrib’ attribute"); + + tex_uniform_location = + gles2->glGetUniformLocation (program, "tex"); + if (tex_uniform_location == -1) + g_error ("Couldn't find ‘tex’ uniform"); + + gles2->glUseProgram (program); + + gles2->glUniform1i (tex_uniform_location, 0); + + /* Render the texture to fill the framebuffer */ + gles2->glEnableVertexAttribArray (pos_location); + gles2->glVertexAttribPointer (pos_location, + 2, /* n_components */ + GL_FLOAT, + FALSE, /* normalized */ + sizeof (float) * 4, + verts); + gles2->glEnableVertexAttribArray (tex_coord_location); + gles2->glVertexAttribPointer (tex_coord_location, + 2, /* n_components */ + GL_FLOAT, + FALSE, /* normalized */ + sizeof (float) * 4, + verts + 2); + + gles2->glDrawArrays (GL_TRIANGLE_STRIP, 0, 4); + + /* Verify top of drawn rectangle is red */ + verify_region (gles2, + RECTANGLE_DRAW_X, + RECTANGLE_DRAW_Y + RECTANGLE_HEIGHT / 2, + RECTANGLE_WIDTH, + RECTANGLE_HEIGHT / 2, + 0xff0000ff); + /* Verify bottom of drawn rectangle is blue */ + verify_region (gles2, + RECTANGLE_DRAW_X, + RECTANGLE_DRAW_Y, + RECTANGLE_WIDTH, + RECTANGLE_HEIGHT / 2, + 0x0000ffff); + /* Verify top of copied rectangle is red */ + verify_region (gles2, + RECTANGLE_COPY_X, + RECTANGLE_COPY_Y + RECTANGLE_HEIGHT / 2, + RECTANGLE_WIDTH, + RECTANGLE_HEIGHT / 2, + 0xff0000ff); + /* Verify bottom of copied rectangle is blue */ + verify_region (gles2, + RECTANGLE_COPY_X, + RECTANGLE_COPY_Y, + RECTANGLE_WIDTH, + RECTANGLE_HEIGHT / 2, + 0x0000ffff); + + cogl_pop_gles2_context (test_ctx); + + cogl_object_unref (offscreen); + cogl_object_unref (gles2_ctx); + cogl_object_unref (pipeline); + cogl_object_unref (offscreen_texture); +} |