diff options
author | Vladislav Andresov <v.andresov@partner.samsung.com> | 2017-10-23 10:30:41 +0300 |
---|---|---|
committer | Jinhyung Jo <jinhyung.jo@samsung.com> | 2017-11-21 12:43:07 +0900 |
commit | 152c8a156567674a42768c1a97179e52ded5d85e (patch) | |
tree | f76d53efd8869848652d89c7f6e0576035e3d6b9 | |
parent | cde1408d73c4e178e2816a731dab50dac4d23295 (diff) | |
download | qemu-152c8a156567674a42768c1a97179e52ded5d85e.tar.gz qemu-152c8a156567674a42768c1a97179e52ded5d85e.tar.bz2 qemu-152c8a156567674a42768c1a97179e52ded5d85e.zip |
VIGS: Add support of hardware YUV420 conversion
For perfomance reasons this patch implements shader conversion
on GPU for YUV420 surfaces into ARGB8888 format. This way takes
much less time than software conversion.
Change-Id: I03e110ed20651f5df233361d0456e433a1b61ef3
Signed-off-by: Vladislav Andresov <v.andresov@partner.samsung.com>
Signed-off-by: Jinhyung Jo <jinhyung.jo@samsung.com>
-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); |