gl-renderer: implement surface_copy_content

Taking the easy way, always do a rendering pass when copying any real
buffer or texture. Will handle YUV formats, and makes it easy to always
return data the right y-direction up.

All the FBO GL state is created and torn down on every invocation, so this
is a pretty naive implementation.

If there was a wl_shm buffer giving the content to the surface, and the
stride of the buffer was greater than width * bytes_per_pixel, then this
implementation will return stride long rows, not width.

Changes in v2:
- simplify pack_color()
- remove stride and format from the API

Signed-off-by: Pekka Paalanen <pekka.paalanen@collabora.co.uk>
v1 Tested-by: Nobuhiko Tanibata <NOBUHIKO_TANIBATA@xddp.denso.co.jp>
Reviewed-by: Daniel Stone <daniels@collabora.com>
This commit is contained in:
Pekka Paalanen 2015-02-09 13:37:27 +02:00
parent aeb917ee5d
commit eb35cbe2b1

View File

@ -1,5 +1,6 @@
/*
* Copyright © 2012 Intel Corporation
* Copyright © 2015 Collabora, Ltd.
*
* Permission to use, copy, modify, distribute, and sell this software and
* its documentation for any purpose is hereby granted without fee, provided
@ -1363,6 +1364,151 @@ gl_renderer_surface_set_color(struct weston_surface *surface,
gs->shader = &gr->solid_shader;
}
static void
gl_renderer_surface_get_content_size(struct weston_surface *surface,
int *width, int *height)
{
struct gl_surface_state *gs = get_surface_state(surface);
if (gs->buffer_type == BUFFER_TYPE_NULL) {
*width = 0;
*height = 0;
} else {
*width = gs->pitch;
*height = gs->height;
}
}
static uint32_t
pack_color(pixman_format_code_t format, float *c)
{
uint8_t r = round(c[0] * 255.0f);
uint8_t g = round(c[1] * 255.0f);
uint8_t b = round(c[2] * 255.0f);
uint8_t a = round(c[3] * 255.0f);
switch (format) {
case PIXMAN_a8b8g8r8:
return (a << 24) | (b << 16) | (g << 8) | r;
default:
assert(0);
return 0;
}
}
static int
gl_renderer_surface_copy_content(struct weston_surface *surface,
void *target, size_t size,
int src_x, int src_y,
int width, int height)
{
static const GLfloat verts[4 * 2] = {
0.0f, 0.0f,
1.0f, 0.0f,
1.0f, 1.0f,
0.0f, 1.0f
};
static const GLfloat projmat_normal[16] = { /* transpose */
2.0f, 0.0f, 0.0f, 0.0f,
0.0f, 2.0f, 0.0f, 0.0f,
0.0f, 0.0f, 1.0f, 0.0f,
-1.0f, -1.0f, 0.0f, 1.0f
};
static const GLfloat projmat_yinvert[16] = { /* transpose */
2.0f, 0.0f, 0.0f, 0.0f,
0.0f, -2.0f, 0.0f, 0.0f,
0.0f, 0.0f, 1.0f, 0.0f,
-1.0f, 1.0f, 0.0f, 1.0f
};
const pixman_format_code_t format = PIXMAN_a8b8g8r8;
const size_t bytespp = 4; /* PIXMAN_a8b8g8r8 */
const GLenum gl_format = GL_RGBA; /* PIXMAN_a8b8g8r8 little-endian */
struct gl_renderer *gr = get_renderer(surface->compositor);
struct gl_surface_state *gs = get_surface_state(surface);
int cw, ch;
GLuint fbo;
GLuint tex;
GLenum status;
const GLfloat *proj;
int i;
gl_renderer_surface_get_content_size(surface, &cw, &ch);
switch (gs->buffer_type) {
case BUFFER_TYPE_NULL:
return -1;
case BUFFER_TYPE_SOLID:
*(uint32_t *)target = pack_color(format, gs->color);
return 0;
case BUFFER_TYPE_SHM:
gl_renderer_flush_damage(surface);
/* fall through */
case BUFFER_TYPE_EGL:
break;
}
glGenTextures(1, &tex);
glBindTexture(GL_TEXTURE_2D, tex);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, cw, ch,
0, GL_RGBA, GL_UNSIGNED_BYTE, NULL);
glBindTexture(GL_TEXTURE_2D, 0);
glGenFramebuffers(1, &fbo);
glBindFramebuffer(GL_FRAMEBUFFER, fbo);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
GL_TEXTURE_2D, tex, 0);
status = glCheckFramebufferStatus(GL_FRAMEBUFFER);
if (status != GL_FRAMEBUFFER_COMPLETE) {
weston_log("%s: fbo error: %#x\n", __func__, status);
glDeleteFramebuffers(1, &fbo);
glDeleteTextures(1, &tex);
return -1;
}
glViewport(0, 0, cw, ch);
glDisable(GL_BLEND);
use_shader(gr, gs->shader);
if (gs->y_inverted)
proj = projmat_normal;
else
proj = projmat_yinvert;
glUniformMatrix4fv(gs->shader->proj_uniform, 1, GL_FALSE, proj);
glUniform1f(gs->shader->alpha_uniform, 1.0f);
for (i = 0; i < gs->num_textures; i++) {
glUniform1i(gs->shader->tex_uniforms[i], i);
glActiveTexture(GL_TEXTURE0 + i);
glBindTexture(gs->target, gs->textures[i]);
glTexParameteri(gs->target, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameteri(gs->target, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
}
/* position: */
glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 0, verts);
glEnableVertexAttribArray(0);
/* texcoord: */
glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 0, verts);
glEnableVertexAttribArray(1);
glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
glDisableVertexAttribArray(1);
glDisableVertexAttribArray(0);
glPixelStorei(GL_PACK_ALIGNMENT, bytespp);
glReadPixels(src_x, src_y, width, height, gl_format,
GL_UNSIGNED_BYTE, target);
glDeleteFramebuffers(1, &fbo);
glDeleteTextures(1, &tex);
return 0;
}
static void
surface_state_destroy(struct gl_surface_state *gs, struct gl_renderer *gr)
{
@ -1992,6 +2138,9 @@ gl_renderer_create(struct weston_compositor *ec, EGLNativeDisplayType display,
gr->base.attach = gl_renderer_attach;
gr->base.surface_set_color = gl_renderer_surface_set_color;
gr->base.destroy = gl_renderer_destroy;
gr->base.surface_get_content_size =
gl_renderer_surface_get_content_size;
gr->base.surface_copy_content = gl_renderer_surface_copy_content;
gr->egl_display = eglGetDisplay(display);
if (gr->egl_display == EGL_NO_DISPLAY) {