summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--cogl/cogl-gles2-context.c224
-rw-r--r--tests/conform/test-conform-main.c1
-rw-r--r--tests/conform/test-gles2-context.c231
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);
+}