VIGS: Add support of hardware YUV420 conversion 86/160886/2
authorVladislav Andresov <v.andresov@partner.samsung.com>
Mon, 23 Oct 2017 07:30:41 +0000 (10:30 +0300)
committerJinhyung Jo <jinhyung.jo@samsung.com>
Tue, 21 Nov 2017 03:43:07 +0000 (12:43 +0900)
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>
hw/vigs/vigs_gl_backend.c

index b3d384c..f4c6f4a 100644 (file)
 #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);