diff options
-rw-r--r-- | hw/vigs/vigs_gl_backend.c | 209 |
1 files changed, 208 insertions, 1 deletions
diff --git a/hw/vigs/vigs_gl_backend.c b/hw/vigs/vigs_gl_backend.c index b3d384c9b2..f4c6f4a136 100644 --- a/hw/vigs/vigs_gl_backend.c +++ b/hw/vigs/vigs_gl_backend.c @@ -38,6 +38,17 @@ #include "winsys_gl.h" #include <math.h> +#ifdef __linux__ +#include "drm/drm_fourcc.h" +#else +#define MAKE_FOURCC(a, b, c, d) (((unsigned int)(a)) | \ + (((unsigned int)(b)) << 8) | \ + (((unsigned int)(c)) << 16) | \ + (((unsigned int)(d)) << 24)) +#define DRM_FORMAT_ARGB8888 MAKE_FOURCC('A', 'R', '2', '4') +#define DRM_FORMAT_YUV420 MAKE_FOURCC('Y', 'U', '1', '2') +#endif + struct vigs_gl_surface; struct vigs_winsys_gl_surface @@ -98,6 +109,12 @@ struct vigs_gl_surface * Ortho matrix for this surface. */ GLfloat ortho[16]; + + /* + * Input textures for + * YUV conversion shader + */ + GLuint yuv_textures[3]; }; static __inline struct vigs_winsys_gl_surface @@ -1605,6 +1622,181 @@ out: gl_backend->BindFramebuffer(GL_FRAMEBUFFER, 0); } +static GLuint vigs_gl_surface_create_pixel_buf(struct vigs_surface *sfc) +{ + struct vigs_gl_backend *gl_backend = (struct vigs_gl_backend*)sfc->backend; + struct vigs_gl_surface *gl_sfc = (struct vigs_gl_surface*)sfc; + struct vigs_winsys_gl_surface *ws_sfc = get_ws_sfc(gl_sfc); + GLsizei sfc_stride = sfc->stride; + GLsizei sfc_h = ws_sfc->base.base.height; + GLuint pbuf; + + if (!ws_sfc->tex) { + VIGS_LOG_ERROR("source surface must be not empty"); + return 0; + } + + gl_backend->GenBuffers(1, &pbuf); + + gl_backend->BindBuffer(GL_PIXEL_PACK_BUFFER, pbuf); + + gl_backend->BufferData(GL_PIXEL_PACK_BUFFER, + sfc_stride * sfc_h, + NULL, + GL_STREAM_COPY); + + vigs_gl_surface_read_pixels(sfc, 0); + + gl_backend->BindBuffer(GL_PIXEL_PACK_BUFFER, 0); + + return pbuf; +} + +static bool vigs_gl_surface_setup_yuv_textures(struct vigs_surface *dst, + struct vigs_surface *src) +{ + struct vigs_gl_backend *gl_backend = (struct vigs_gl_backend*)dst->backend; + struct vigs_gl_surface *gl_dst = (struct vigs_gl_surface*)dst; + struct vigs_winsys_gl_surface *ws_dst = get_ws_sfc(gl_dst); + GLsizei dst_w = ws_dst->base.base.width; + GLsizei dst_h = ws_dst->base.base.height; + GLsizei tex_widths[3] = {dst_w / 4, dst_w / 8, dst_w / 8}; + GLsizei tex_heights[3] = {dst_h, dst_h / 2, dst_h / 2}; + GLsizei pbuf_offsets[3] = {0, dst_w * dst_h, 5 * dst_w * dst_h / 4}; + int i; + bool need_alloc = false; + GLuint pbuf; + + /* + * We have to create and destroy pixel buffer at every yuv conversion, + * because we can't check restoring of source texture. + */ + pbuf = vigs_gl_surface_create_pixel_buf(src); + + if (pbuf == 0) { + return false; + } + + if (gl_dst->yuv_textures[0] == 0) { + gl_backend->GenTextures(3, &gl_dst->yuv_textures[0]); + need_alloc = true; + } + + gl_backend->BindBuffer(GL_PIXEL_UNPACK_BUFFER, pbuf); + + for (i = 0; i < 3; i++) { + gl_backend->ActiveTexture(GL_TEXTURE0 + i); + gl_backend->BindTexture(GL_TEXTURE_2D, gl_dst->yuv_textures[i]); + + gl_backend->TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + gl_backend->TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + + gl_backend->PixelStorei(GL_UNPACK_ALIGNMENT, 1); + gl_backend->PixelStorei(GL_UNPACK_ROW_LENGTH, tex_widths[i]); + + if (need_alloc) { + gl_backend->TexImage2D(GL_TEXTURE_2D, + 0, + ws_dst->tex_internalformat, + tex_widths[i], tex_heights[i], + 0, + ws_dst->tex_format, + ws_dst->tex_type, + (GLvoid *)(intptr_t)pbuf_offsets[i]); + } else { + gl_backend->TexSubImage2D(GL_TEXTURE_2D, + 0, + 0, 0, + tex_widths[i], tex_heights[i], + ws_dst->tex_format, + ws_dst->tex_type, + (GLvoid *)(intptr_t)pbuf_offsets[i]); + } + } + + gl_backend->ActiveTexture(GL_TEXTURE0); + + gl_backend->BindBuffer(GL_PIXEL_UNPACK_BUFFER, 0); + + gl_backend->DeleteBuffers(1, &pbuf); + + return true; +} + +static void vigs_gl_surface_convert_yuv2argb(struct vigs_surface *dst, + struct vigs_surface *src) +{ + struct vigs_gl_backend *gl_backend = (struct vigs_gl_backend*)dst->backend; + struct vigs_gl_surface *gl_dst = (struct vigs_gl_surface*)dst; + struct vigs_winsys_gl_surface *ws_dst = get_ws_sfc(gl_dst); + GLsizei dst_w = ws_dst->base.base.width; + GLsizei dst_h = ws_dst->base.base.height; + GLfloat *vert_coords; + GLfloat *tex_coords; + + if (!vigs_winsys_gl_surface_create_texture(ws_dst)) { + goto out; + } + + if (!vigs_gl_surface_setup_yuv_textures(dst, src)) { + goto out; + } + + if (!vigs_gl_surface_setup_framebuffer(gl_dst, + gl_backend->yuv420_prog_id, + gl_backend->yuv420_prog_proj_loc)) { + goto out; + } + + vigs_vector_resize(&gl_backend->v1, 12 * sizeof(GLfloat)); + vigs_vector_resize(&gl_backend->v2, 12 * sizeof(GLfloat)); + + vert_coords = vigs_vector_data(&gl_backend->v1); + tex_coords = vigs_vector_data(&gl_backend->v2); + + vert_coords[6] = vert_coords[0] = 0; + vert_coords[7] = vert_coords[1] = dst_h; + vert_coords[2] = dst_w; + vert_coords[3] = dst_h; + vert_coords[8] = vert_coords[4] = dst_w; + vert_coords[9] = vert_coords[5] = 0.0; + vert_coords[10] = 0.0; + vert_coords[11] = 0.0; + + tex_coords[6] = tex_coords[0] = 0.0; + tex_coords[7] = tex_coords[1] = 1.0; + tex_coords[2] = 1.0; + tex_coords[3] = 1.0; + tex_coords[8] = tex_coords[4] = 1.0; + tex_coords[9] = tex_coords[5] = 0.0; + tex_coords[10] = 0.0; + tex_coords[11] = 0.0; + + gl_backend->FramebufferTexture2D(GL_FRAMEBUFFER, + GL_COLOR_ATTACHMENT0, + GL_TEXTURE_2D, + ws_dst->tex, + 0); + + gl_backend->UseProgram(gl_backend->yuv420_prog_id); + + gl_backend->Uniform2f(gl_backend->yuv420_prog_ytexSize_loc, + dst_w / 4, dst_h); + gl_backend->Uniform2f(gl_backend->yuv420_prog_utexSize_loc, + dst_w / 8, dst_h / 2); + gl_backend->Uniform2f(gl_backend->yuv420_prog_vtexSize_loc, + dst_w / 8, dst_h / 2); + gl_backend->Uniform2f(gl_backend->yuv420_prog_size_loc, + dst_w, dst_h); + + vigs_gl_draw_yuv420_prog(gl_backend, 6); + + gl_backend->UseProgram(gl_backend->tex_prog_id); + +out: + gl_backend->BindFramebuffer(GL_FRAMEBUFFER, 0); +} + static void vigs_gl_surface_convert(struct vigs_surface *dst, uint32_t dst_format, struct vigs_surface *src, @@ -1627,7 +1819,20 @@ static void vigs_gl_surface_convert(struct vigs_surface *dst, -1.0f, 1.0f, gl_dst->ortho); } - vigs_gl_surface_copy(dst, src, &entry, 1); + /* + * Currently only ARGB8888 destination format is needed for support + */ + switch (src_format) { + case DRM_FORMAT_YUV420: + vigs_gl_surface_convert_yuv2argb(dst, src); + break; + case DRM_FORMAT_ARGB8888: + vigs_gl_surface_copy(dst, src, &entry, 1); + break; + default: + VIGS_LOG_ERROR("source format 0x%x is not supported", src_format); + break; + } if (y_invert) { vigs_gl_create_ortho(0.0f, width, 0.0f, height, @@ -1656,6 +1861,8 @@ static void vigs_gl_surface_destroy(struct vigs_surface *sfc) sfc->format); } + gl_backend->DeleteTextures(3, &gl_sfc->yuv_textures[0]); + vigs_surface_cleanup(&gl_sfc->base); g_free(gl_sfc); |