From 5242a167c395d28cde669b7b52ec30af53773c7d Mon Sep 17 00:00:00 2001 From: dec05eba Date: Wed, 24 Dec 2025 20:38:31 +0100 Subject: [PATCH] Support v4l2 yuyv on nvidia --- TODO | 8 +- include/color_conversion.h | 2 +- include/egl.h | 10 -- src/capture/kms.c | 12 -- src/capture/v4l2.c | 101 +++++++++------- src/color_conversion.c | 228 ++++++++++++++++++++++++++++++++----- src/egl.c | 4 - 7 files changed, 272 insertions(+), 93 deletions(-) diff --git a/TODO b/TODO index 8055608..f394166 100644 --- a/TODO +++ b/TODO @@ -378,4 +378,10 @@ Make capture from multiple sources work on nvidia x11 when capturing monitor + w Support v4l2 mplane on devices where it's supported (where it's more efficient). My camera doesn't support mplane. Implement v4l2 yuyv nvidia capture by capturing rg88 yuyv to rgb as is done now for screenshot, but also do that for video by creating an intermediate rgb texture for the camera. Then render that rgb texture to the video texture. - This is needed to properly scale the yuyv texture without messing it up (the texture indexing). \ No newline at end of file + This is needed to properly scale the yuyv texture without messing it up (the texture indexing). + +Set v4l2 camera fps to video output fps (if lower than the camera fps) and the same for resolution. + +Support camera controls, such as white balance. Otherwise tell user to use cameractrl software. + +Camera capture doesn't work perfectly. The image gets glitched, need to properly wait for image to be done. \ No newline at end of file diff --git a/include/color_conversion.h b/include/color_conversion.h index ec551da..9157712 100644 --- a/include/color_conversion.h +++ b/include/color_conversion.h @@ -6,7 +6,7 @@ #include "vec2.h" #include -#define GSR_COLOR_CONVERSION_MAX_GRAPHICS_SHADERS 8 +#define GSR_COLOR_CONVERSION_MAX_GRAPHICS_SHADERS 12 #define GSR_COLOR_CONVERSION_MAX_FRAMEBUFFERS 2 typedef enum { diff --git a/include/egl.h b/include/egl.h index 2927b53..d4fed98 100644 --- a/include/egl.h +++ b/include/egl.h @@ -40,7 +40,6 @@ typedef struct __GLXFBConfigRec *GLXFBConfig; typedef struct __GLXcontextRec *GLXContext; typedef XID GLXDrawable; typedef void(*__GLXextFuncPtr)(void); -typedef struct __GLsync *GLsync; #define EGL_SUCCESS 0x3000 #define EGL_BUFFER_SIZE 0x3020 @@ -152,11 +151,6 @@ typedef struct __GLsync *GLsync; #define GL_COMPILE_STATUS 0x8B81 #define GL_LINK_STATUS 0x8B82 -#define GL_SYNC_GPU_COMMANDS_COMPLETE 0x9117 -#define GL_SYNC_FLUSH_COMMANDS_BIT 0x00000001 -#define GL_CONDITION_SATISFIED 0x911C -#define GL_ALREADY_SIGNALED 0x911A - typedef unsigned int (*FUNC_eglExportDMABUFImageQueryMESA)(EGLDisplay dpy, EGLImageKHR image, int *fourcc, int *num_planes, uint64_t *modifiers); typedef unsigned int (*FUNC_eglExportDMABUFImageMESA)(EGLDisplay dpy, EGLImageKHR image, int *fds, int32_t *strides, int32_t *offsets); typedef void (*FUNC_glEGLImageTargetTexture2DOES)(unsigned int target, GLeglImageOES image); @@ -315,10 +309,6 @@ struct gsr_egl { void* (*glMapBufferRange)(unsigned int target, intptr_t offset, ssize_t length, unsigned int access); unsigned char (*glUnmapBuffer)(unsigned int target); void (*glGetIntegerv)(unsigned int pname, int *params); - - GLsync (*glFenceSync)(unsigned int condition, unsigned int flags); - void (*glDeleteSync)(GLsync sync); - unsigned int (*glClientWaitSync)(GLsync sync, unsigned int flags, uint64_t timeout); }; bool gsr_egl_load(gsr_egl *self, gsr_window *window, bool is_monitor_capture, bool enable_debug); diff --git a/src/capture/kms.c b/src/capture/kms.c index c0bb54b..4f2eb7d 100644 --- a/src/capture/kms.c +++ b/src/capture/kms.c @@ -628,18 +628,6 @@ static int gsr_capture_kms_capture(gsr_capture *cap, gsr_capture_metadata *captu } } - // static GLsync sync = NULL; - - // if(sync) { - // const unsigned int r = self->params.egl->glClientWaitSync(sync, GL_SYNC_FLUSH_COMMANDS_BIT, 1000 * 1000 * 1000); - // if(r != GL_CONDITION_SATISFIED && r != GL_ALREADY_SIGNALED) { - // fprintf(stderr, "failed sync: %u\n", r); - // } - // self->params.egl->glDeleteSync(sync); - // } - - // sync = self->params.egl->glFenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0); - //self->params.egl->glFlush(); //self->params.egl->glFinish(); diff --git a/src/capture/v4l2.c b/src/capture/v4l2.c index e466c40..e3dd97c 100644 --- a/src/capture/v4l2.c +++ b/src/capture/v4l2.c @@ -53,7 +53,8 @@ typedef struct { int fd; int dmabuf_fd[NUM_BUFFERS]; EGLImage dma_image[NUM_BUFFERS]; - unsigned int texture_id; + unsigned int texture_id[NUM_BUFFERS]; + unsigned int prev_texture_index; bool got_first_frame; void *dmabuf_map[NUM_BUFFERS]; @@ -72,6 +73,8 @@ typedef struct { tjhandle jpeg_decompressor; double capture_start_time; + + bool yuyv_conversion_fallback; } gsr_capture_v4l2; static int xioctl(int fd, unsigned long request, void *arg) { @@ -90,9 +93,9 @@ static void gsr_capture_v4l2_stop(gsr_capture_v4l2 *self) { self->pbos[i] = 0; } - if(self->texture_id) { - self->params.egl->glDeleteTextures(1, &self->texture_id); - self->texture_id = 0; + self->params.egl->glDeleteTextures(NUM_BUFFERS, self->texture_id); + for(int i = 0; i < NUM_BUFFERS; ++i) { + self->texture_id[i] = 0; } for(int i = 0; i < NUM_BUFFERS; ++i) { @@ -246,32 +249,48 @@ static bool gsr_capture_v4l2_map_buffer(gsr_capture_v4l2 *self, const struct v4l return false; } case GSR_CAPTURE_V4L2_PIXFMT_YUYV: { + self->params.egl->glGenTextures(NUM_BUFFERS, self->texture_id); + for(int i = 0; i < NUM_BUFFERS; ++i) { self->dma_image[i] = self->params.egl->eglCreateImage(self->params.egl->egl_display, 0, EGL_LINUX_DMA_BUF_EXT, NULL, (intptr_t[]) { EGL_WIDTH, fmt->fmt.pix.width, EGL_HEIGHT, fmt->fmt.pix.height, - EGL_LINUX_DRM_FOURCC_EXT, DRM_FORMAT_YUYV, // TODO: Use DRM_FORMAT_RG88 on nvidia and custom shader. Test on intel as well, or use fallback method on every system. + EGL_LINUX_DRM_FOURCC_EXT, DRM_FORMAT_YUYV, EGL_DMA_BUF_PLANE0_FD_EXT, self->dmabuf_fd[i], EGL_DMA_BUF_PLANE0_OFFSET_EXT, 0, EGL_DMA_BUF_PLANE0_PITCH_EXT, fmt->fmt.pix.bytesperline, EGL_NONE }); + if(!self->dma_image[i]) { - fprintf(stderr, "gsr error: gsr_capture_v4l2_map_buffer: eglCreateImage failed, error: %d\n", self->params.egl->eglGetError()); + self->yuyv_conversion_fallback = true; + self->dma_image[i] = self->params.egl->eglCreateImage(self->params.egl->egl_display, 0, EGL_LINUX_DMA_BUF_EXT, NULL, (intptr_t[]) { + EGL_WIDTH, fmt->fmt.pix.width, + EGL_HEIGHT, fmt->fmt.pix.height, + EGL_LINUX_DRM_FOURCC_EXT, DRM_FORMAT_RG88, + EGL_DMA_BUF_PLANE0_FD_EXT, self->dmabuf_fd[i], + EGL_DMA_BUF_PLANE0_OFFSET_EXT, 0, + EGL_DMA_BUF_PLANE0_PITCH_EXT, fmt->fmt.pix.bytesperline, + EGL_NONE + }); + + if(!self->dma_image[i]) { + fprintf(stderr, "gsr error: gsr_capture_v4l2_map_buffer: eglCreateImage failed, error: %d\n", self->params.egl->eglGetError()); + return false; + } + } + + self->params.egl->glBindTexture(GL_TEXTURE_EXTERNAL_OES, self->texture_id[i]); + self->params.egl->glEGLImageTargetTexture2DOES(GL_TEXTURE_EXTERNAL_OES, self->dma_image[i]); + self->params.egl->glTexParameteri(GL_TEXTURE_EXTERNAL_OES, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + self->params.egl->glTexParameteri(GL_TEXTURE_EXTERNAL_OES, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + self->params.egl->glBindTexture(GL_TEXTURE_EXTERNAL_OES, 0); + if(self->texture_id[i] == 0) { + fprintf(stderr, "gsr error: gsr_capture_v4l2_map_buffer: failed to create texture\n"); return false; } } - self->params.egl->glGenTextures(1, &self->texture_id); - self->params.egl->glBindTexture(GL_TEXTURE_EXTERNAL_OES, self->texture_id); - self->params.egl->glTexParameteri(GL_TEXTURE_EXTERNAL_OES, GL_TEXTURE_MIN_FILTER, GL_LINEAR); - self->params.egl->glTexParameteri(GL_TEXTURE_EXTERNAL_OES, GL_TEXTURE_MAG_FILTER, GL_LINEAR); - self->params.egl->glBindTexture(GL_TEXTURE_EXTERNAL_OES, 0); - if(self->texture_id == 0) { - fprintf(stderr, "gsr error: gsr_capture_v4l2_map_buffer: failed to create texture\n"); - return false; - } - self->buffer_type = V4L2_BUFFER_TYPE_DMABUF; break; } @@ -283,13 +302,13 @@ static bool gsr_capture_v4l2_map_buffer(gsr_capture_v4l2 *self, const struct v4l fprintf(stderr, "gsr error: gsr_capture_v4l2_map_buffer: mmap failed, error: %s\n", strerror(errno)); return false; } - } - // GL_RGBA is intentionally used here instead of GL_RGB, because the performance is much better when using glTexSubImage2D (22% cpu usage compared to 38% cpu usage) - self->texture_id = gl_create_texture(self->params.egl, fmt->fmt.pix.width, fmt->fmt.pix.height, GL_RGBA8, GL_RGBA, GL_LINEAR); - if(self->texture_id == 0) { - fprintf(stderr, "gsr error: gsr_capture_v4l2_map_buffer: failed to create texture\n"); - return false; + // GL_RGBA is intentionally used here instead of GL_RGB, because the performance is much better when using glTexSubImage2D (22% cpu usage compared to 38% cpu usage) + self->texture_id[i] = gl_create_texture(self->params.egl, fmt->fmt.pix.width, fmt->fmt.pix.height, GL_RGBA8, GL_RGBA, GL_LINEAR); + if(self->texture_id[i] == 0) { + fprintf(stderr, "gsr error: gsr_capture_v4l2_map_buffer: failed to create texture\n"); + return false; + } } if(!gsr_capture_v4l2_create_pbos(self, fmt->fmt.pix.width, fmt->fmt.pix.height)) @@ -377,15 +396,6 @@ static int gsr_capture_v4l2_setup(gsr_capture_v4l2 *self) { return -1; } - /* Buggy driver paranoia */ - const uint32_t min_stride = fmt.fmt.pix.width * 2; // * 2 because the stride is width (Y) + width/2 (U) + width/2 (V) - if(fmt.fmt.pix.bytesperline < min_stride) - fmt.fmt.pix.bytesperline = min_stride; - - const uint32_t min_size = fmt.fmt.pix.bytesperline * fmt.fmt.pix.height; - if(fmt.fmt.pix.sizeimage < min_size) - fmt.fmt.pix.sizeimage = min_size; - self->capture_size.x = fmt.fmt.pix.width; self->capture_size.y = fmt.fmt.pix.height; @@ -479,7 +489,7 @@ static void gsr_capture_v4l2_decode_jpeg_to_texture(gsr_capture_v4l2 *self, cons return; } - self->params.egl->glBindTexture(GL_TEXTURE_2D, self->texture_id); + self->params.egl->glBindTexture(GL_TEXTURE_2D, self->texture_id[buf->index]); self->pbo_index = (self->pbo_index + 1) % NUM_PBOS; const unsigned int next_pbo_index = (self->pbo_index + 1) % NUM_PBOS; @@ -511,6 +521,8 @@ static int gsr_capture_v4l2_capture(gsr_capture *cap, gsr_capture_metadata *capt }; xioctl(self->fd, VIDIOC_DQBUF, &buf); + unsigned int texture_index = buf.index; + if(buf.bytesused > 0 && !(buf.flags & V4L2_BUF_FLAG_ERROR)) { if(!self->got_first_frame) fprintf(stderr, "gsr info: gsr_capture_v4l2_capture: camera %s is now ready\n", self->params.device_path); @@ -518,9 +530,9 @@ static int gsr_capture_v4l2_capture(gsr_capture *cap, gsr_capture_metadata *capt switch(self->buffer_type) { case V4L2_BUFFER_TYPE_DMABUF: { - self->params.egl->glBindTexture(GL_TEXTURE_EXTERNAL_OES, self->texture_id); - self->params.egl->glEGLImageTargetTexture2DOES(GL_TEXTURE_EXTERNAL_OES, self->dma_image[buf.index]); - self->params.egl->glBindTexture(GL_TEXTURE_EXTERNAL_OES, 0); + //self->params.egl->glBindTexture(GL_TEXTURE_EXTERNAL_OES, self->texture_id); + //self->params.egl->glEGLImageTargetTexture2DOES(GL_TEXTURE_EXTERNAL_OES, self->dma_image[buf.index]); + //self->params.egl->glBindTexture(GL_TEXTURE_EXTERNAL_OES, 0); break; } case V4L2_BUFFER_TYPE_MMAP: { @@ -530,6 +542,10 @@ static int gsr_capture_v4l2_capture(gsr_capture *cap, gsr_capture_metadata *capt break; } } + + self->prev_texture_index = buf.index; + } else { + texture_index = self->prev_texture_index; } xioctl(self->fd, VIDIOC_QBUF, &buf); @@ -537,13 +553,20 @@ static int gsr_capture_v4l2_capture(gsr_capture *cap, gsr_capture_metadata *capt const vec2i target_pos = gsr_capture_get_target_position(output_size, capture_metadata); self->params.egl->glFlush(); + if(self->params.egl->gpu_info.vendor == GSR_GPU_VENDOR_NVIDIA) + self->params.egl->glFinish(); - //if(self->got_first_frame) { - gsr_color_conversion_draw(color_conversion, self->texture_id, + if(self->buffer_type == V4L2_BUFFER_TYPE_DMABUF) { + gsr_color_conversion_draw(color_conversion, self->texture_id[texture_index], target_pos, output_size, (vec2i){0, 0}, self->capture_size, self->capture_size, - GSR_ROT_0, capture_metadata->flip, GSR_SOURCE_COLOR_RGB, self->buffer_type == V4L2_BUFFER_TYPE_DMABUF); - //} + GSR_ROT_0, capture_metadata->flip, self->yuyv_conversion_fallback ? GSR_SOURCE_COLOR_YUYV : GSR_SOURCE_COLOR_RGB, true); + } else { + gsr_color_conversion_draw(color_conversion, self->texture_id[texture_index], + target_pos, output_size, + (vec2i){0, 0}, self->capture_size, self->capture_size, + GSR_ROT_0, capture_metadata->flip, GSR_SOURCE_COLOR_RGB, false); + } return self->got_first_frame ? 0 : -1; } diff --git a/src/color_conversion.c b/src/color_conversion.c index 2b12ff5..22a1378 100644 --- a/src/color_conversion.c +++ b/src/color_conversion.c @@ -10,8 +10,12 @@ #define GRAPHICS_SHADER_INDEX_UV_EXTERNAL 3 #define GRAPHICS_SHADER_INDEX_RGB 4 #define GRAPHICS_SHADER_INDEX_RGB_EXTERNAL 5 -#define GRAPHICS_SHADER_INDEX_YUYV_TO_RGB 6 -#define GRAPHICS_SHADER_INDEX_YUYV_TO_RGB_EXTERNAL 7 +#define GRAPHICS_SHADER_INDEX_YUYV_TO_Y 6 +#define GRAPHICS_SHADER_INDEX_YUYV_TO_UV 7 +#define GRAPHICS_SHADER_INDEX_YUYV_TO_Y_EXTERNAL 8 +#define GRAPHICS_SHADER_INDEX_YUYV_TO_UV_EXTERNAL 9 +#define GRAPHICS_SHADER_INDEX_YUYV_TO_RGB 10 +#define GRAPHICS_SHADER_INDEX_YUYV_TO_RGB_EXTERNAL 11 /* https://en.wikipedia.org/wiki/YCbCr, see study/color_space_transform_matrix.png */ @@ -250,6 +254,135 @@ static unsigned int load_graphics_shader_rgb(gsr_shader *shader, gsr_egl *egl, g return 0; } +static int load_graphics_shader_yuyv_to_y(gsr_shader *shader, gsr_egl *egl, gsr_color_graphics_uniforms *uniforms, bool external_texture) { + char vertex_shader[2048]; + snprintf(vertex_shader, sizeof(vertex_shader), + "#version 300 es \n" + "in vec2 pos; \n" + "in vec2 texcoords; \n" + "out vec2 texcoords_out; \n" + "uniform vec2 offset; \n" + "uniform float rotation; \n" + "uniform mat2 rotation_matrix; \n" + "void main() \n" + "{ \n" + " texcoords_out = vec2(texcoords.x - 0.5, texcoords.y - 0.5) * rotation_matrix + vec2(0.5, 0.5); \n" + " gl_Position = vec4(offset.x, offset.y, 0.0, 0.0) + vec4(pos.x, pos.y, 0.0, 1.0); \n" + "} \n"); + + const char *main_code = + " vec4 pixel = texture(tex1, texcoords_out); \n" + " FragColor.x = pixel.r; \n" + " FragColor.w = 1.0; \n"; + + char fragment_shader[2048]; + if(external_texture) { + snprintf(fragment_shader, sizeof(fragment_shader), + "#version 300 es \n" + "#extension GL_OES_EGL_image_external : enable \n" + "#extension GL_OES_EGL_image_external_essl3 : require \n" + "precision highp float; \n" + "in vec2 texcoords_out; \n" + "uniform samplerExternalOES tex1; \n" + "out vec4 FragColor; \n" + "void main() \n" + "{ \n" + "%s" + "} \n", main_code); + } else { + snprintf(fragment_shader, sizeof(fragment_shader), + "#version 300 es \n" + "precision highp float; \n" + "in vec2 texcoords_out; \n" + "uniform sampler2D tex1; \n" + "out vec4 FragColor; \n" + "void main() \n" + "{ \n" + "%s" + "} \n", main_code); + } + + if(gsr_shader_init(shader, egl, vertex_shader, fragment_shader) != 0) + return -1; + + gsr_shader_bind_attribute_location(shader, "pos", 0); + gsr_shader_bind_attribute_location(shader, "texcoords", 1); + uniforms->offset = egl->glGetUniformLocation(shader->program_id, "offset"); + uniforms->rotation_matrix = egl->glGetUniformLocation(shader->program_id, "rotation_matrix"); + return 0; +} + +static unsigned int load_graphics_shader_yuyv_to_uv(gsr_shader *shader, gsr_egl *egl, gsr_color_graphics_uniforms *uniforms, bool external_texture) { + char vertex_shader[2048]; + snprintf(vertex_shader, sizeof(vertex_shader), + "#version 300 es \n" + "in vec2 pos; \n" + "in vec2 texcoords; \n" + "out vec2 texcoords_out; \n" + "uniform vec2 offset; \n" + "uniform float rotation; \n" + "uniform mat2 rotation_matrix; \n" + "void main() \n" + "{ \n" + " texcoords_out = vec2(texcoords.x - 0.5, texcoords.y - 0.5) * rotation_matrix + vec2(0.5, 0.5); \n" + " gl_Position = (vec4(offset.x, offset.y, 0.0, 0.0) + vec4(pos.x, pos.y, 0.0, 1.0)) * vec4(0.5, 0.5, 1.0, 1.0) - vec4(0.5, 0.5, 0.0, 0.0); \n" + "} \n"); + + const char *main_code = + " vec2 resolution = vec2(textureSize(tex1, 0));\n" + " ivec2 uv = ivec2(texcoords_out * resolution);\n" + " float u = 0.0;\n" + " float v = 0.0;\n" + " vec4 this_color = texelFetch(tex1, uv, 0);\n" + " if((uv.x & 1) == 0) {\n" + " vec2 next_color = texelFetch(tex1, uv + ivec2(1, 0), 0).rg;\n" + " u = this_color.g;\n" + " v = next_color.g;\n" + " } else {\n" + " vec2 prev_color = texelFetch(tex1, uv - ivec2(1, 0), 0).rg;\n" + " u = prev_color.g;\n" + " v = this_color.g;\n" + " }\n" + " FragColor.rg = vec2(u, v);\n" + " FragColor.w = 1.0;\n"; + + char fragment_shader[2048]; + if(external_texture) { + snprintf(fragment_shader, sizeof(fragment_shader), + "#version 300 es \n" + "#extension GL_OES_EGL_image_external : enable \n" + "#extension GL_OES_EGL_image_external_essl3 : require \n" + "precision highp float; \n" + "in vec2 texcoords_out; \n" + "uniform samplerExternalOES tex1; \n" + "out vec4 FragColor; \n" + "void main() \n" + "{ \n" + "%s" + "} \n", main_code); + } else { + snprintf(fragment_shader, sizeof(fragment_shader), + "#version 300 es \n" + "precision highp float; \n" + "in vec2 texcoords_out; \n" + "uniform sampler2D tex1; \n" + "out vec4 FragColor; \n" + "void main() \n" + "{ \n" + "%s" + "} \n", main_code); + } + + if(gsr_shader_init(shader, egl, vertex_shader, fragment_shader) != 0) + return -1; + + gsr_shader_bind_attribute_location(shader, "pos", 0); + gsr_shader_bind_attribute_location(shader, "texcoords", 1); + uniforms->offset = egl->glGetUniformLocation(shader->program_id, "offset"); + uniforms->rotation_matrix = egl->glGetUniformLocation(shader->program_id, "rotation_matrix"); + return 0; +} + static unsigned int load_graphics_shader_yuyv_to_rgb(gsr_shader *shader, gsr_egl *egl, gsr_color_graphics_uniforms *uniforms, bool external_texture) { char vertex_shader[2048]; snprintf(vertex_shader, sizeof(vertex_shader), @@ -268,21 +401,18 @@ static unsigned int load_graphics_shader_yuyv_to_rgb(gsr_shader *shader, gsr_egl const char *main_code = " vec2 resolution = vec2(textureSize(tex1, 0));\n" - " vec2 uv = texcoords_out * resolution;\n" + " ivec2 uv = ivec2(texcoords_out * resolution);\n" " float y = 0.0;\n" " float u = 0.0;\n" " float v = 0.0;\n" - " if(mod(uv.x, 2.0) < 1.0) {\n" - " vec3 this_color = texture(tex1, texcoords_out).rgb;\n" - " vec3 next_color = texture(tex1, texcoords_out + vec2(1.0, 0.0)/resolution).rgb;\n" - "\n" + " vec4 this_color = texelFetch(tex1, uv, 0);\n" + " if((uv.x & 1) == 0) {\n" + " vec2 next_color = texelFetch(tex1, uv + ivec2(1, 0), 0).rg;\n" " y = this_color.r;\n" " u = this_color.g;\n" " v = next_color.g;\n" " } else {\n" - " vec3 this_color = texture(tex1, texcoords_out).rgb;\n" - " vec3 prev_color = texture(tex1, texcoords_out - vec2(1.0, 0.0)/resolution).rgb;\n" - "\n" + " vec2 prev_color = texelFetch(tex1, uv - ivec2(1, 0), 0).rg;\n" " y = this_color.r;\n" " u = prev_color.g;\n" " v = this_color.g;\n" @@ -392,6 +522,16 @@ static bool gsr_color_conversion_load_graphics_shaders(gsr_color_conversion *sel fprintf(stderr, "gsr error: gsr_color_conversion_init: failed to load UV graphics shader\n"); return false; } + + if(load_graphics_shader_yuyv_to_y(&self->graphics_shaders[GRAPHICS_SHADER_INDEX_YUYV_TO_Y], self->params.egl, &self->graphics_uniforms[GRAPHICS_SHADER_INDEX_YUYV_TO_Y], false) != 0) { + fprintf(stderr, "gsr error: gsr_color_conversion_init: failed to load YUYV to Y graphics shader\n"); + return false; + } + + if(load_graphics_shader_yuyv_to_uv(&self->graphics_shaders[GRAPHICS_SHADER_INDEX_YUYV_TO_UV], self->params.egl, &self->graphics_uniforms[GRAPHICS_SHADER_INDEX_YUYV_TO_UV], false) != 0) { + fprintf(stderr, "gsr error: gsr_color_conversion_init: failed to load YUYV to UV graphics shader\n"); + return false; + } break; } case GSR_DESTINATION_COLOR_RGB8: { @@ -423,6 +563,16 @@ static bool gsr_color_conversion_load_external_graphics_shaders(gsr_color_conver fprintf(stderr, "gsr error: gsr_color_conversion_init: failed to load UV graphics shader (external)\n"); return false; } + + if(load_graphics_shader_yuyv_to_y(&self->graphics_shaders[GRAPHICS_SHADER_INDEX_YUYV_TO_Y_EXTERNAL], self->params.egl, &self->graphics_uniforms[GRAPHICS_SHADER_INDEX_YUYV_TO_Y_EXTERNAL], true) != 0) { + fprintf(stderr, "gsr error: gsr_color_conversion_init: failed to load YUYV to Y graphics shader (external)\n"); + return false; + } + + if(load_graphics_shader_yuyv_to_uv(&self->graphics_shaders[GRAPHICS_SHADER_INDEX_YUYV_TO_UV_EXTERNAL], self->params.egl, &self->graphics_uniforms[GRAPHICS_SHADER_INDEX_YUYV_TO_UV_EXTERNAL], true) != 0) { + fprintf(stderr, "gsr error: gsr_color_conversion_init: failed to load YUYV to UV graphics shader (external)\n"); + return false; + } break; } case GSR_DESTINATION_COLOR_RGB8: { @@ -550,17 +700,17 @@ static void gsr_color_conversion_apply_rotation(gsr_rotation rotation, float rot } } -static void gsr_color_conversion_swizzle_texture_source(gsr_color_conversion *self, gsr_source_color source_color) { +static void gsr_color_conversion_swizzle_texture_source(gsr_color_conversion *self, unsigned int texture_target, gsr_source_color source_color) { if(source_color == GSR_SOURCE_COLOR_BGR) { const int swizzle_mask[] = { GL_BLUE, GL_GREEN, GL_RED, 1 }; - self->params.egl->glTexParameteriv(GL_TEXTURE_2D, GL_TEXTURE_SWIZZLE_RGBA, swizzle_mask); + self->params.egl->glTexParameteriv(texture_target, GL_TEXTURE_SWIZZLE_RGBA, swizzle_mask); } } -static void gsr_color_conversion_swizzle_reset(gsr_color_conversion *self, gsr_source_color source_color) { +static void gsr_color_conversion_swizzle_reset(gsr_color_conversion *self, unsigned int texture_target, gsr_source_color source_color) { if(source_color == GSR_SOURCE_COLOR_BGR) { const int swizzle_mask[] = { GL_RED, GL_GREEN, GL_BLUE, GL_ALPHA }; - self->params.egl->glTexParameteriv(GL_TEXTURE_2D, GL_TEXTURE_SWIZZLE_RGBA, swizzle_mask); + self->params.egl->glTexParameteriv(texture_target, GL_TEXTURE_SWIZZLE_RGBA, swizzle_mask); } } @@ -569,7 +719,7 @@ static void gsr_color_conversion_draw_graphics(gsr_color_conversion *self, unsig return; const vec2i dest_texture_size = self->params.destination_textures_size[0]; - const int texture_target = external_texture ? GL_TEXTURE_EXTERNAL_OES : GL_TEXTURE_2D; + const unsigned int texture_target = external_texture ? GL_TEXTURE_EXTERNAL_OES : GL_TEXTURE_2D; if(rotation == GSR_ROT_90 || rotation == GSR_ROT_270) { const float tmp = texture_size.x; @@ -578,7 +728,7 @@ static void gsr_color_conversion_draw_graphics(gsr_color_conversion *self, unsig } self->params.egl->glBindTexture(texture_target, texture_id); - gsr_color_conversion_swizzle_texture_source(self, source_color); + gsr_color_conversion_swizzle_texture_source(self, texture_target, source_color); const vec2f pos_norm = { ((float)destination_pos.x / (dest_texture_size.x == 0 ? 1.0f : (float)dest_texture_size.x)) * 2.0f, @@ -674,21 +824,49 @@ static void gsr_color_conversion_draw_graphics(gsr_color_conversion *self, unsig break; } case GSR_SOURCE_COLOR_YUYV: { - self->params.egl->glBindFramebuffer(GL_FRAMEBUFFER, self->framebuffers[0]); - //cap_xcomp->params.egl->glClear(GL_COLOR_BUFFER_BIT); // TODO: Do this in a separate clear_ function. We want to do that when using multiple drm to create the final image (multiple monitors for example) + switch(self->params.destination_color) { + case GSR_DESTINATION_COLOR_NV12: + case GSR_DESTINATION_COLOR_P010: { + self->params.egl->glBindFramebuffer(GL_FRAMEBUFFER, self->framebuffers[0]); + //cap_xcomp->params.egl->glClear(GL_COLOR_BUFFER_BIT); // TODO: Do this in a separate clear_ function. We want to do that when using multiple drm to create the final image (multiple monitors for example) - const int shader_index = external_texture ? GRAPHICS_SHADER_INDEX_YUYV_TO_RGB_EXTERNAL : GRAPHICS_SHADER_INDEX_YUYV_TO_RGB; - gsr_shader_use(&self->graphics_shaders[shader_index]); - self->params.egl->glUniformMatrix2fv(self->graphics_uniforms[shader_index].rotation_matrix, 1, GL_TRUE, (const float*)rotation_matrix); - self->params.egl->glUniform2f(self->graphics_uniforms[shader_index].offset, pos_norm.x, pos_norm.y); - self->params.egl->glDrawArrays(GL_TRIANGLES, 0, 6); + int shader_index = external_texture ? GRAPHICS_SHADER_INDEX_YUYV_TO_Y_EXTERNAL : GRAPHICS_SHADER_INDEX_YUYV_TO_Y; + gsr_shader_use(&self->graphics_shaders[shader_index]); + self->params.egl->glUniformMatrix2fv(self->graphics_uniforms[shader_index].rotation_matrix, 1, GL_TRUE, (const float*)rotation_matrix); + self->params.egl->glUniform2f(self->graphics_uniforms[shader_index].offset, pos_norm.x, pos_norm.y); + self->params.egl->glDrawArrays(GL_TRIANGLES, 0, 6); + + if(self->params.num_destination_textures > 1) { + self->params.egl->glBindFramebuffer(GL_FRAMEBUFFER, self->framebuffers[1]); + //cap_xcomp->params.egl->glClear(GL_COLOR_BUFFER_BIT); + + shader_index = external_texture ? GRAPHICS_SHADER_INDEX_YUYV_TO_UV_EXTERNAL : GRAPHICS_SHADER_INDEX_YUYV_TO_UV; + gsr_shader_use(&self->graphics_shaders[shader_index]); + self->params.egl->glUniformMatrix2fv(self->graphics_uniforms[shader_index].rotation_matrix, 1, GL_TRUE, (const float*)rotation_matrix); + self->params.egl->glUniform2f(self->graphics_uniforms[shader_index].offset, pos_norm.x, pos_norm.y); + self->params.egl->glDrawArrays(GL_TRIANGLES, 0, 6); + } + break; + } + case GSR_DESTINATION_COLOR_RGB8: { + self->params.egl->glBindFramebuffer(GL_FRAMEBUFFER, self->framebuffers[0]); + //cap_xcomp->params.egl->glClear(GL_COLOR_BUFFER_BIT); // TODO: Do this in a separate clear_ function. We want to do that when using multiple drm to create the final image (multiple monitors for example) + + const int shader_index = external_texture ? GRAPHICS_SHADER_INDEX_YUYV_TO_RGB_EXTERNAL : GRAPHICS_SHADER_INDEX_YUYV_TO_RGB; + gsr_shader_use(&self->graphics_shaders[shader_index]); + self->params.egl->glUniformMatrix2fv(self->graphics_uniforms[shader_index].rotation_matrix, 1, GL_TRUE, (const float*)rotation_matrix); + self->params.egl->glUniform2f(self->graphics_uniforms[shader_index].offset, pos_norm.x, pos_norm.y); + self->params.egl->glDrawArrays(GL_TRIANGLES, 0, 6); + break; + } + } break; } } self->params.egl->glBindVertexArray(0); self->params.egl->glUseProgram(0); - gsr_color_conversion_swizzle_reset(self, source_color); + gsr_color_conversion_swizzle_reset(self, texture_target, source_color); self->params.egl->glBindTexture(texture_target, 0); self->params.egl->glBindFramebuffer(GL_FRAMEBUFFER, 0); } @@ -710,7 +888,6 @@ void gsr_color_conversion_draw(gsr_color_conversion *self, unsigned int texture_ const int texture_target = external_texture ? GL_TEXTURE_EXTERNAL_OES : GL_TEXTURE_2D; self->params.egl->glBindTexture(texture_target, texture_id); - gsr_color_conversion_swizzle_texture_source(self, source_color); source_position.x += source_pos.x; source_position.y += source_pos.y; @@ -721,7 +898,6 @@ void gsr_color_conversion_draw(gsr_color_conversion *self, unsigned int texture_ self->params.egl->glMemoryBarrier(GL_ALL_BARRIER_BITS); // GL_SHADER_IMAGE_ACCESS_BARRIER_BIT self->params.egl->glUseProgram(0); - gsr_color_conversion_swizzle_reset(self, source_color); self->params.egl->glBindTexture(texture_target, 0); } diff --git a/src/egl.c b/src/egl.c index 83c4bd6..81d39f1 100644 --- a/src/egl.c +++ b/src/egl.c @@ -333,10 +333,6 @@ static bool gsr_egl_load_gl(gsr_egl *self, void *library) { { (void**)&self->glUnmapBuffer, "glUnmapBuffer" }, { (void**)&self->glGetIntegerv, "glGetIntegerv" }, - { (void**)&self->glFenceSync, "glFenceSync" }, - { (void**)&self->glDeleteSync, "glDeleteSync" }, - { (void**)&self->glClientWaitSync, "glClientWaitSync" }, - { NULL, NULL } };