From e83dcc4cab2a2e6413be3384eae98cb06af5a57e Mon Sep 17 00:00:00 2001 From: Matthias Clasen Date: Tue, 3 Oct 2023 17:28:17 -0400 Subject: [PATCH] gsk: Support dmabuf textures Support dmabuf textures in the GL renderer. This copies the approach taken by mutter for yuv dmabuf conversion. --- gsk/gl/gskgldriver.c | 417 ++++++++++++++++++++++++++++++++--- gsk/gl/gskgldriverprivate.h | 5 + gsk/gl/gskglprograms.defs | 19 ++ gsk/gl/gskglrenderjob.c | 26 ++- gsk/gl/resources/nv12.glsl | 49 ++++ gsk/gl/resources/yuv420.glsl | 51 +++++ gsk/gl/resources/yuyv.glsl | 49 ++++ gsk/meson.build | 3 + 8 files changed, 572 insertions(+), 47 deletions(-) create mode 100644 gsk/gl/resources/nv12.glsl create mode 100644 gsk/gl/resources/yuv420.glsl create mode 100644 gsk/gl/resources/yuyv.glsl diff --git a/gsk/gl/gskgldriver.c b/gsk/gl/gskgldriver.c index 225cae6920..198dc4b2dd 100644 --- a/gsk/gl/gskgldriver.c +++ b/gsk/gl/gskgldriver.c @@ -42,9 +42,16 @@ #include #include #include - +#include #include +#ifdef HAVE_EGL +#include +#endif +#ifdef HAVE_LINUX_DMA_BUF_H +#include +#endif + G_DEFINE_TYPE (GskGLDriver, gsk_gl_driver, G_TYPE_OBJECT) static guint @@ -224,6 +231,8 @@ gsk_gl_driver_dispose (GObject *object) GSK_GL_DELETE_PROGRAM(name); \ GSK_GL_DELETE_PROGRAM(name ## _no_clip); \ GSK_GL_DELETE_PROGRAM(name ## _rect_clip); +#define GSK_GL_DEFINE_PROGRAM_NO_CLIP(name, resource, uniforms) \ + GSK_GL_DELETE_PROGRAM(name); #define GSK_GL_DELETE_PROGRAM(name) \ G_STMT_START { \ if (self->name) \ @@ -238,6 +247,7 @@ gsk_gl_driver_dispose (GObject *object) #undef GSK_GL_SHADER_JOINED #undef GSK_GL_ADD_UNIFORM #undef GSK_GL_DEFINE_PROGRAM +#undef GSK_GL_DEFINE_PROGRAM_NO_CLIP if (self->shader_cache != NULL) { @@ -373,6 +383,11 @@ gsk_gl_driver_load_programs (GskGLDriver *self, GSK_GL_COMPILE_PROGRAM(name ## _no_clip, uniforms, "#define NO_CLIP 1\n"); \ GSK_GL_COMPILE_PROGRAM(name ## _rect_clip, uniforms, "#define RECT_CLIP 1\n"); \ GSK_GL_COMPILE_PROGRAM(name, uniforms, ""); +#define GSK_GL_DEFINE_PROGRAM_NO_CLIP(name, sources, uniforms) \ + gsk_gl_compiler_set_source (compiler, GSK_GL_COMPILER_VERTEX, NULL); \ + gsk_gl_compiler_set_source (compiler, GSK_GL_COMPILER_FRAGMENT, NULL); \ + sources \ + GSK_GL_COMPILE_PROGRAM(name, uniforms, "#define NO_CLIP 1\n"); #define GSK_GL_COMPILE_PROGRAM(name, uniforms, clip) \ G_STMT_START { \ GskGLProgram *program; \ @@ -399,8 +414,8 @@ gsk_gl_driver_load_programs (GskGLDriver *self, g_steal_pointer (&program); \ } G_STMT_END; # include "gskglprograms.defs" -#undef GSK_GL_DEFINE_PROGRAM_CLIP #undef GSK_GL_DEFINE_PROGRAM +#undef GSK_GL_DEFINE_PROGRAM_NO_CLIP #undef GSK_GL_ADD_UNIFORM #undef GSK_GL_SHADER_SINGLE #undef GSK_GL_SHADER_JOINED @@ -703,6 +718,321 @@ gsk_gl_driver_cache_texture (GskGLDriver *self, } } +#if defined(HAVE_EGL) && defined(HAVE_LINUX_DMA_BUF_H) +static int +import_dmabuf_planes (EGLDisplay egl_display, + int width, + int height, + unsigned int fourcc, + guint64 modifier, + unsigned int n_planes, + int *fds, + unsigned int *strides, + unsigned int *offsets) +{ + EGLint attribs[64]; + EGLImage image; + guint texture_id; + int i; + + g_assert (1 <= n_planes && n_planes <= 4); + + GSK_DEBUG (OPENGL, + "Importing dma-buf into GL via EGLImage. Format %c%c%c%c:%#lx", + fourcc & 0xff, (fourcc >> 8) & 0xff, (fourcc >> 16) & 0xff, (fourcc >> 24) & 0xff, modifier); + + i = 0; + attribs[i++] = EGL_WIDTH; + attribs[i++] = width; + attribs[i++] = EGL_HEIGHT; + attribs[i++] = height; + attribs[i++] = EGL_LINUX_DRM_FOURCC_EXT; + attribs[i++] = fourcc; + +#define ADD_PLANE(plane) \ + { \ + if (modifier != DRM_FORMAT_MOD_INVALID) \ + { \ + attribs[i++] = EGL_DMA_BUF_PLANE## plane ##_MODIFIER_LO_EXT; \ + attribs[i++] = modifier & 0xFFFFFFFF; \ + attribs[i++] = EGL_DMA_BUF_PLANE## plane ## _MODIFIER_HI_EXT; \ + attribs[i++] = modifier >> 32; \ + } \ + attribs[i++] = EGL_DMA_BUF_PLANE## plane ##_FD_EXT; \ + attribs[i++] = fds[plane]; \ + attribs[i++] = EGL_DMA_BUF_PLANE## plane ##_PITCH_EXT; \ + attribs[i++] = strides[plane]; \ + attribs[i++] = EGL_DMA_BUF_PLANE## plane ##_OFFSET_EXT; \ + attribs[i++] = offsets[plane]; \ + } + + ADD_PLANE (0); + + if (n_planes > 1) ADD_PLANE (1); + if (n_planes > 2) ADD_PLANE (2); + if (n_planes > 3) ADD_PLANE (3); + + attribs[i++] = EGL_NONE; + + image = eglCreateImageKHR (egl_display, + EGL_NO_CONTEXT, + EGL_LINUX_DMA_BUF_EXT, + (EGLClientBuffer)NULL, + attribs); + + if (image == EGL_NO_IMAGE) + { + g_warning ("Failed to create EGL image: %#x\n", eglGetError ()); + return 0; + } + + glGenTextures (1, &texture_id); + glBindTexture (GL_TEXTURE_2D, texture_id); + glEGLImageTargetTexture2DOES (GL_TEXTURE_2D, image); + glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + + eglDestroyImageKHR (egl_display, image); + + return texture_id; +} + +static void +set_viewport_for_size (GskGLDriver *self, + GskGLProgram *program, + float width, + float height) +{ + float viewport[4] = { 0, 0, width, height }; + + gsk_gl_uniform_state_set4fv (program->uniforms, + program->program_info, + UNIFORM_SHARED_VIEWPORT, 0, + 1, + (const float *)&viewport); + self->stamps[UNIFORM_SHARED_VIEWPORT]++; +} + +#define ORTHO_NEAR_PLANE -10000 +#define ORTHO_FAR_PLANE 10000 + +static void +set_projection_for_size (GskGLDriver *self, + GskGLProgram *program, + float width, + float height) +{ + graphene_matrix_t projection; + + graphene_matrix_init_ortho (&projection, 0, width, 0, height, ORTHO_NEAR_PLANE, ORTHO_FAR_PLANE); + graphene_matrix_scale (&projection, 1, -1, 1); + + gsk_gl_uniform_state_set_matrix (program->uniforms, + program->program_info, + UNIFORM_SHARED_PROJECTION, 0, + &projection); + self->stamps[UNIFORM_SHARED_PROJECTION]++; +} + +static void +reset_modelview (GskGLDriver *self, + GskGLProgram *program) +{ + graphene_matrix_t modelview; + + graphene_matrix_init_identity (&modelview); + + gsk_gl_uniform_state_set_matrix (program->uniforms, + program->program_info, + UNIFORM_SHARED_MODELVIEW, 0, + &modelview); + self->stamps[UNIFORM_SHARED_MODELVIEW]++; +} + +static void +draw_rect (GskGLCommandQueue *command_queue, + float min_x, + float min_y, + float max_x, + float max_y) +{ + GskGLDrawVertex *vertices = gsk_gl_command_queue_add_vertices (command_queue); + float min_u = 0; + float max_u = 1; + float min_v = 1; + float max_v = 0; + guint16 c = FP16_ZERO; + + vertices[0] = (GskGLDrawVertex) { .position = { min_x, min_y }, .uv = { min_u, min_v }, .color = { c, c, c, c } }; + vertices[1] = (GskGLDrawVertex) { .position = { min_x, max_y }, .uv = { min_u, max_v }, .color = { c, c, c, c } }; + vertices[2] = (GskGLDrawVertex) { .position = { max_x, min_y }, .uv = { max_u, min_v }, .color = { c, c, c, c } }; + vertices[3] = (GskGLDrawVertex) { .position = { max_x, max_y }, .uv = { max_u, max_v }, .color = { c, c, c, c } }; + vertices[4] = (GskGLDrawVertex) { .position = { min_x, max_y }, .uv = { min_u, max_v }, .color = { c, c, c, c } }; + vertices[5] = (GskGLDrawVertex) { .position = { max_x, min_y }, .uv = { max_u, min_v }, .color = { c, c, c, c } }; +} + +typedef struct _TextureFormatInfo TextureFormatInfo; +struct _TextureFormatInfo +{ + int n_planes; + unsigned int subformats[3]; + int plane_indices[3]; + int hsub[3]; + int vsub[3]; + int uniforms[3]; +}; + +static TextureFormatInfo texture_format_info[] = { + { .n_planes = 2, + .subformats = { DRM_FORMAT_GR88, DRM_FORMAT_ARGB8888 }, + .plane_indices = { 0, 0 }, + .hsub = { 1, 2 }, + .vsub = { 1, 1 }, + .uniforms = { UNIFORM_SHARED_SOURCE, UNIFORM_YUYV_SOURCE2 }, + }, + { .n_planes = 2, + .subformats = { DRM_FORMAT_R8, DRM_FORMAT_GR88 }, + .plane_indices = { 0, 1 }, + .hsub = { 1, 2 }, + .vsub = { 1, 2 }, + .uniforms = { UNIFORM_SHARED_SOURCE, UNIFORM_NV12_SOURCE2 }, + }, + { .n_planes = 2, + .subformats = { DRM_FORMAT_R16, DRM_FORMAT_GR1616 }, + .plane_indices = { 0, 1 }, + .hsub = { 1, 2 }, + .vsub = { 1, 2 }, + .uniforms = { UNIFORM_SHARED_SOURCE, UNIFORM_NV12_SOURCE2 }, + }, + { .n_planes = 3, + .subformats = { DRM_FORMAT_R8, DRM_FORMAT_R8, DRM_FORMAT_R8 }, + .plane_indices = { 0, 1, 2 }, + .hsub = { 1, 2, 2 }, + .vsub = { 1, 2, 2 }, + .uniforms = { UNIFORM_SHARED_SOURCE, UNIFORM_YUV420_SOURCE2, UNIFORM_YUV420_SOURCE3 }, + }, + +}; + +static unsigned int release_render_target (GskGLDriver *self, + GskGLRenderTarget *render_target, + gboolean release_texture, + gboolean cache_texture); + +static unsigned int +gsk_gl_driver_import_dmabuf_texture (GskGLDriver *self, + GdkDmabufTexture *texture) +{ + GdkGLContext *context = self->command_queue->context; + GdkDisplay *display = gdk_gl_context_get_display (context); + EGLDisplay egl_display; + int width, height; + guint64 modifier; + TextureFormatInfo *info; + GskGLProgram *program; + GskGLRenderTarget *render_target; + guint prev_fbo; + + egl_display = gdk_display_get_egl_display (display); + if (egl_display == NULL) + { + g_warning ("Can't import dmabufs when not using EGL"); + return 0; + } + + if (!display->have_egl_dma_buf_import) + { + g_warning ("EGL does ont support importing dmabufs"); + return 0; + } + + gdk_gl_context_make_current (context); + + width = gdk_texture_get_width (GDK_TEXTURE (texture)); + height = gdk_texture_get_height (GDK_TEXTURE (texture)); + modifier = gdk_dmabuf_texture_get_modifier (texture); + + if (gdk_dmabuf_texture_get_fourcc (texture) == DRM_FORMAT_YUYV) + { + info = &texture_format_info[0]; + program = self->yuyv; + } + else if (gdk_dmabuf_texture_get_fourcc (texture) == DRM_FORMAT_NV12) + { + info = &texture_format_info[1]; + program = self->nv12; + } + else if (gdk_dmabuf_texture_get_fourcc (texture) == DRM_FORMAT_P010) + { + info = &texture_format_info[2]; + program = self->nv12; + } + else if (gdk_dmabuf_texture_get_fourcc (texture) == DRM_FORMAT_YUV420) + { + info = &texture_format_info[3]; + program = self->yuv420; + } + else + { + return import_dmabuf_planes (egl_display, + gdk_texture_get_width (GDK_TEXTURE (texture)), + gdk_texture_get_height (GDK_TEXTURE (texture)), + gdk_dmabuf_texture_get_fourcc (texture), + gdk_dmabuf_texture_get_modifier (texture), + gdk_dmabuf_texture_get_n_planes (texture), + gdk_dmabuf_texture_get_fds (texture), + gdk_dmabuf_texture_get_strides (texture), + gdk_dmabuf_texture_get_offsets (texture)); + } + + gsk_gl_driver_create_render_target (self, width, height, GL_RGBA8, &render_target); + + prev_fbo = gsk_gl_command_queue_bind_framebuffer (self->command_queue, render_target->framebuffer_id); + gsk_gl_command_queue_clear (self->command_queue, 0, &GRAPHENE_RECT_INIT (0, 0, width, height)); + + gsk_gl_command_queue_begin_draw (self->command_queue, program->program_info, width, height); + + set_projection_for_size (self, program, width, height); + set_viewport_for_size (self, program, width, height); + reset_modelview (self, program); + + for (int i = 0; i < info->n_planes; i++) + { + int plane = info->plane_indices[i]; + int texture_id = import_dmabuf_planes (egl_display, + width / info->hsub[i], + height / info->vsub[i], + info->subformats[i], + modifier, + 1, + gdk_dmabuf_texture_get_fds (texture) + plane, + gdk_dmabuf_texture_get_strides (texture) + plane, + gdk_dmabuf_texture_get_offsets (texture) + plane); + + gsk_gl_program_set_uniform_texture (program, + info->uniforms[i], 0, + GL_TEXTURE_2D, GL_TEXTURE0 + i, texture_id); + + gsk_gl_driver_autorelease_texture (self, texture_id); + } + + draw_rect (self->command_queue, 0, 0, width, height); + + gsk_gl_command_queue_end_draw (self->command_queue); + + gsk_gl_command_queue_bind_framebuffer (self->command_queue, prev_fbo); + + return release_render_target (self, render_target, FALSE, FALSE); +} +#else +static unsigned int +gsk_gl_driver_import_dmabuf_texture (GskGLDriver *self, + GdkDmabufTexture *texture) +{ + return 0; +} +#endif /* HAVE_EGL && HAVE_LINUX_DMA_BUF_H */ + /** * gsk_gl_driver_load_texture: * @self: a `GdkTexture` @@ -759,7 +1089,11 @@ gsk_gl_driver_load_texture (GskGLDriver *self, return t->texture_id; } - if (GDK_IS_GL_TEXTURE (texture)) + if (GDK_IS_DMABUF_TEXTURE (texture)) + { + texture_id = gsk_gl_driver_import_dmabuf_texture (self, GDK_DMABUF_TEXTURE (texture)); + } + else if (GDK_IS_GL_TEXTURE (texture)) { GdkGLTexture *gl_texture = (GdkGLTexture *) texture; GdkGLContext *texture_context = gdk_gl_texture_get_context (gl_texture); @@ -788,9 +1122,8 @@ gsk_gl_driver_load_texture (GskGLDriver *self, width = gdk_texture_get_width (texture); height = gdk_texture_get_height (texture); - t = gsk_gl_texture_new (texture_id, - width, height, - self->current_frame_id); + t = gsk_gl_texture_new (texture_id, width, height, self->current_frame_id); + if (ensure_mipmap) { glBindTexture (GL_TEXTURE_2D, t->texture_id); @@ -962,6 +1295,47 @@ gsk_gl_driver_create_render_target (GskGLDriver *self, return FALSE; } +static unsigned int +release_render_target (GskGLDriver *self, + GskGLRenderTarget *render_target, + gboolean release_texture, + gboolean cache_texture) +{ + guint texture_id; + + g_return_val_if_fail (GSK_IS_GL_DRIVER (self), 0); + g_return_val_if_fail (render_target != NULL, 0); + + if (release_texture) + { + texture_id = 0; + g_ptr_array_add (self->render_targets, render_target); + } + else + { + texture_id = render_target->texture_id; + + if (cache_texture) + { + GskGLTexture *texture; + + texture = gsk_gl_texture_new (render_target->texture_id, + render_target->width, + render_target->height, + self->current_frame_id); + g_hash_table_insert (self->textures, + GUINT_TO_POINTER (texture_id), + g_steal_pointer (&texture)); + } + + gsk_gl_driver_autorelease_framebuffer (self, render_target->framebuffer_id); + g_free (render_target); + + } + + return texture_id; +} + /** * gsk_gl_driver_release_render_target: * @self: a `GskGLDriver` @@ -987,36 +1361,7 @@ gsk_gl_driver_release_render_target (GskGLDriver *self, GskGLRenderTarget *render_target, gboolean release_texture) { - guint texture_id; - - g_return_val_if_fail (GSK_IS_GL_DRIVER (self), 0); - g_return_val_if_fail (render_target != NULL, 0); - - if (release_texture) - { - texture_id = 0; - g_ptr_array_add (self->render_targets, render_target); - } - else - { - GskGLTexture *texture; - - texture_id = render_target->texture_id; - - texture = gsk_gl_texture_new (render_target->texture_id, - render_target->width, - render_target->height, - self->current_frame_id); - g_hash_table_insert (self->textures, - GUINT_TO_POINTER (texture_id), - g_steal_pointer (&texture)); - - gsk_gl_driver_autorelease_framebuffer (self, render_target->framebuffer_id); - g_free (render_target); - - } - - return texture_id; + return release_render_target (self, render_target, release_texture, TRUE); } /** diff --git a/gsk/gl/gskgldriverprivate.h b/gsk/gl/gskgldriverprivate.h index 021792bd8c..c421bed98a 100644 --- a/gsk/gl/gskgldriverprivate.h +++ b/gsk/gl/gskgldriverprivate.h @@ -69,7 +69,9 @@ typedef struct { #define CONCAT_EXPANDED2(a,b) a##b #define GSK_GL_ADD_UNIFORM(pos, KEY, name) UNIFORM_##KEY = UNIFORM_SHARED_LAST + pos, #define GSK_GL_DEFINE_PROGRAM(name, resource, uniforms) enum { uniforms }; +#define GSK_GL_DEFINE_PROGRAM_NO_CLIP(name, resource, uniforms) enum { uniforms }; # include "gskglprograms.defs" +#undef GSK_GL_DEFINE_PROGRAM_NO_CLIP #undef GSK_GL_DEFINE_PROGRAM #undef GSK_GL_ADD_UNIFORM #undef GSK_GL_NO_UNIFORMS @@ -116,10 +118,13 @@ struct _GskGLDriver GskGLProgram *name ## _no_clip; \ GskGLProgram *name ## _rect_clip; \ GskGLProgram *name; +#define GSK_GL_DEFINE_PROGRAM_NO_CLIP(name, resource, uniforms) \ + GskGLProgram *name; # include "gskglprograms.defs" #undef GSK_GL_NO_UNIFORMS #undef GSK_GL_ADD_UNIFORM #undef GSK_GL_DEFINE_PROGRAM +#undef GSK_GL_DEFINE_PROGRAM_NO_CLIP gint64 current_frame_id; diff --git a/gsk/gl/gskglprograms.defs b/gsk/gl/gskglprograms.defs index 9afb826bf6..9d5e9c9d27 100644 --- a/gsk/gl/gskglprograms.defs +++ b/gsk/gl/gskglprograms.defs @@ -87,3 +87,22 @@ GSK_GL_DEFINE_PROGRAM (unblurred_outset_shadow, GSK_GL_ADD_UNIFORM (1, UNBLURRED_OUTSET_SHADOW_SPREAD, u_spread) GSK_GL_ADD_UNIFORM (2, UNBLURRED_OUTSET_SHADOW_OFFSET, u_offset) GSK_GL_ADD_UNIFORM (3, UNBLURRED_OUTSET_SHADOW_OUTLINE_RECT, u_outline_rect)) + +/* Texture conversion shaders. + * + * Note: If you add new formats here, they need to be added + * to the list of supported formats in gdk/gdkdmabuftexture.c. + */ + +GSK_GL_DEFINE_PROGRAM_NO_CLIP (yuyv, + GSK_GL_SHADER_SINGLE (GSK_GL_SHADER_RESOURCE ("yuyv.glsl")), + GSK_GL_ADD_UNIFORM (1, YUYV_SOURCE2, u_source2)) + +GSK_GL_DEFINE_PROGRAM_NO_CLIP (nv12, + GSK_GL_SHADER_SINGLE (GSK_GL_SHADER_RESOURCE ("nv12.glsl")), + GSK_GL_ADD_UNIFORM (1, NV12_SOURCE2, u_source2)) + +GSK_GL_DEFINE_PROGRAM_NO_CLIP (yuv420, + GSK_GL_SHADER_SINGLE (GSK_GL_SHADER_RESOURCE ("yuv420.glsl")), + GSK_GL_ADD_UNIFORM (1, YUV420_SOURCE2, u_source2) + GSK_GL_ADD_UNIFORM (2, YUV420_SOURCE3, u_source2)) diff --git a/gsk/gl/gskglrenderjob.c b/gsk/gl/gskglrenderjob.c index 8dbe6df97f..f28d752fa1 100644 --- a/gsk/gl/gskglrenderjob.c +++ b/gsk/gl/gskglrenderjob.c @@ -30,6 +30,8 @@ #include #include #include +#include +#include #include #include #include @@ -43,10 +45,15 @@ #include "gskglprogramprivate.h" #include "gskglrenderjobprivate.h" #include "gskglshadowlibraryprivate.h" +#include "gskdebugprivate.h" #include "ninesliceprivate.h" #include "fp16private.h" +#include + +#include + #define ORTHO_NEAR_PLANE -10000 #define ORTHO_FAR_PLANE 10000 #define MAX_GRADIENT_STOPS 6 @@ -3630,16 +3637,11 @@ gsk_gl_render_job_upload_texture (GskGLRenderJob *job, gboolean ensure_mipmap, GskGLRenderOffscreen *offscreen) { - GdkGLTexture *gl_texture = NULL; - - if (GDK_IS_GL_TEXTURE (texture)) - gl_texture = GDK_GL_TEXTURE (texture); - if (!ensure_mipmap && gsk_gl_texture_library_can_cache ((GskGLTextureLibrary *)job->driver->icons_library, texture->width, texture->height) && - !gl_texture) + !(GDK_IS_GL_TEXTURE (texture) || GDK_IS_DMABUF_TEXTURE (texture))) { const GskGLIconData *icon_data; @@ -3653,16 +3655,18 @@ gsk_gl_render_job_upload_texture (GskGLRenderJob *job, /* Only generate a mipmap if it does not make use reupload * a GL texture which we could otherwise use directly. */ - if (gl_texture && - gdk_gl_context_is_shared (gdk_gl_texture_get_context (gl_texture), job->command_queue->context)) - ensure_mipmap = gdk_gl_texture_has_mipmap (gl_texture); + if (GDK_IS_GL_TEXTURE (texture) && + gdk_gl_context_is_shared (gdk_gl_texture_get_context (GDK_GL_TEXTURE (texture)), + job->command_queue->context)) + ensure_mipmap = gdk_gl_texture_has_mipmap (GDK_GL_TEXTURE (texture)); offscreen->texture_id = gsk_gl_driver_load_texture (job->driver, texture, ensure_mipmap); init_full_texture_region (offscreen); offscreen->has_mipmap = ensure_mipmap; - if (gl_texture && offscreen->texture_id == gdk_gl_texture_get_id (gl_texture)) - offscreen->sync = gdk_gl_texture_get_sync (gl_texture); + if (GDK_IS_GL_TEXTURE (texture) && + offscreen->texture_id == gdk_gl_texture_get_id (GDK_GL_TEXTURE (texture))) + offscreen->sync = gdk_gl_texture_get_sync (GDK_GL_TEXTURE (texture)); } } diff --git a/gsk/gl/resources/nv12.glsl b/gsk/gl/resources/nv12.glsl new file mode 100644 index 0000000000..7d4cce22c9 --- /dev/null +++ b/gsk/gl/resources/nv12.glsl @@ -0,0 +1,49 @@ +// VERTEX_SHADER: +// nv12.glsl + +void main() { + gl_Position = u_projection * u_modelview * vec4(aPosition, 0.0, 1.0); + + vUv = vec2(aUv.x, aUv.y); +} + +// FRAGMENT_SHADER: +// nv12.glsl + +uniform sampler2D u_source2; + +vec4 yuv_to_rgb(vec4 yuv) +{ + vec4 res; + + float Y = 1.16438356 * (yuv.x - 0.0625); + float su = yuv.y - 0.5; + float sv = yuv.z - 0.5; + + res.r = Y + 1.59602678 * sv; + res.g = Y - 0.39176229 * su - 0.81296764 * sv; + res.b = Y + 2.01723214 * su; + + res.rgb *= yuv.w; + res.a = yuv.w; + + return res; +} + +/* This shader converts 2-plane yuv (DRM_FORMAT_NV12 or DRM_FORMAT_P010) + * into rgb. It assumes that the two planes have been imported separately, + * the first one as R texture, the second one as RG. + */ + +void main() { + vec4 y = GskTexture(u_source, vUv); + vec4 uv = GskTexture(u_source2, vUv); + + vec4 yuv; + + yuv.x = y.x; + yuv.yz = uv.xy; + yuv.w = 1.0; + + gskSetOutputColor(yuv_to_rgb(yuv)); +} diff --git a/gsk/gl/resources/yuv420.glsl b/gsk/gl/resources/yuv420.glsl new file mode 100644 index 0000000000..377cd2727b --- /dev/null +++ b/gsk/gl/resources/yuv420.glsl @@ -0,0 +1,51 @@ +// VERTEX_SHADER: +// y_uv_to_rgba.glsl + +void main() { + gl_Position = u_projection * u_modelview * vec4(aPosition, 0.0, 1.0); + + vUv = vec2(aUv.x, aUv.y); +} + +// FRAGMENT_SHADER: +// y_uv_to_rgba.glsl + +uniform sampler2D u_source2; +uniform sampler2D u_source3; + +vec4 yuv_to_rgb(vec4 yuv) +{ + vec4 res; + + float Y = 1.16438356 * (yuv.x - 0.0625); + float su = yuv.y - 0.5; + float sv = yuv.z - 0.5; + + res.r = Y + 1.59602678 * sv; + res.g = Y - 0.39176229 * su - 0.81296764 * sv; + res.b = Y + 2.01723214 * su; + + res.rgb *= yuv.w; + res.a = yuv.w; + + return res; +} + +/* This shader converts 3-plane yuv (DRM_FORMAT_YUY420) into rgb. + */ + + +void main() { + vec4 y = GskTexture(u_source, vUv); + vec4 u = GskTexture(u_source2, vUv); + vec4 v = GskTexture(u_source3, vUv); + + vec4 yuv; + + yuv.x = y.x; + yuv.y = u.x; + yuv.z = v.x; + yuv.w = 1.0; + + gskSetOutputColor(yuv_to_rgb(yuv)); +} diff --git a/gsk/gl/resources/yuyv.glsl b/gsk/gl/resources/yuyv.glsl new file mode 100644 index 0000000000..e527a5eda5 --- /dev/null +++ b/gsk/gl/resources/yuyv.glsl @@ -0,0 +1,49 @@ +// VERTEX_SHADER: +// y_xuxv_to_rgba.glsl + +void main() { + gl_Position = u_projection * u_modelview * vec4(aPosition, 0.0, 1.0); + + vUv = vec2(aUv.x, aUv.y); +} + +// FRAGMENT_SHADER: +// y_xuxv_to_rgba.glsl + +uniform sampler2D u_source2; + +vec4 yuv_to_rgb(vec4 yuv) +{ + vec4 res; + + float Y = 1.16438356 * (yuv.x - 0.0625); + float su = yuv.y - 0.5; + float sv = yuv.z - 0.5; + + res.r = Y + 1.59602678 * sv; + res.g = Y - 0.39176229 * su - 0.81296764 * sv; + res.b = Y + 2.01723214 * su; + + res.rgb *= yuv.w; + res.a = yuv.w; + + return res; +} + +/* This shader converts single-plane yuv (DRM_FORMAT_YUYV) into rgb. + * It assumes that the buffer has been imported twice - once as RG + * texture with full size, and once as RGBA textures with half the + * width (to account for the subsampling). + */ +void main() { + vec4 y = GskTexture(u_source, vUv); + vec4 uv = GskTexture(u_source2, vUv); + + vec4 yuv; + + yuv.x = y.x; + yuv.yz = uv.yw; + yuv.w = 1.0; + + gskSetOutputColor(yuv_to_rgb(yuv)); +} diff --git a/gsk/meson.build b/gsk/meson.build index d47621c7c5..1cef4f55cb 100644 --- a/gsk/meson.build +++ b/gsk/meson.build @@ -20,6 +20,9 @@ gsk_private_gl_shaders = [ 'gl/resources/custom.glsl', 'gl/resources/filled_border.glsl', 'gl/resources/mask.glsl', + 'gl/resources/yuyv.glsl', + 'gl/resources/nv12.glsl', + 'gl/resources/yuv420.glsl', ] gsk_public_sources = files([