summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--hw/vigs/vigs_gl_backend.c209
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);