diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/capture/kms.c | 31 | ||||
-rw-r--r-- | src/capture/nvfbc.c | 5 | ||||
-rw-r--r-- | src/capture/portal.c | 25 | ||||
-rw-r--r-- | src/capture/xcomposite.c | 14 | ||||
-rw-r--r-- | src/capture/ximage.c | 8 | ||||
-rw-r--r-- | src/color_conversion.c | 784 | ||||
-rw-r--r-- | src/cursor.c | 6 | ||||
-rw-r--r-- | src/egl.c | 1 | ||||
-rw-r--r-- | src/encoder/video/vaapi.c | 9 | ||||
-rw-r--r-- | src/encoder/video/vulkan.c | 2 | ||||
-rw-r--r-- | src/main.cpp | 110 | ||||
-rw-r--r-- | src/pipewire_video.c | 6 | ||||
-rw-r--r-- | src/shader.c | 18 | ||||
-rw-r--r-- | src/utils.c | 21 | ||||
-rw-r--r-- | src/window/wayland.c | 93 | ||||
-rw-r--r-- | src/window_texture.c | 6 |
16 files changed, 885 insertions, 254 deletions
diff --git a/src/capture/kms.c b/src/capture/kms.c index e98fab7..18858f2 100644 --- a/src/capture/kms.c +++ b/src/capture/kms.c @@ -108,18 +108,22 @@ static int max_int(int a, int b) { } static void gsr_capture_kms_create_input_texture_ids(gsr_capture_kms *self) { + const float border_color[4] = { 0.0f, 0.0f, 0.0f, 0.0f }; + self->params.egl->glGenTextures(1, &self->input_texture_id); self->params.egl->glBindTexture(GL_TEXTURE_2D, self->input_texture_id); - self->params.egl->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); - self->params.egl->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + self->params.egl->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_BORDER); + self->params.egl->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_BORDER); + self->params.egl->glTexParameterfv(GL_TEXTURE_2D, GL_TEXTURE_BORDER_COLOR, border_color); self->params.egl->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); self->params.egl->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); self->params.egl->glBindTexture(GL_TEXTURE_2D, 0); self->params.egl->glGenTextures(1, &self->external_input_texture_id); self->params.egl->glBindTexture(GL_TEXTURE_EXTERNAL_OES, self->external_input_texture_id); - self->params.egl->glTexParameteri(GL_TEXTURE_EXTERNAL_OES, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); - self->params.egl->glTexParameteri(GL_TEXTURE_EXTERNAL_OES, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + self->params.egl->glTexParameteri(GL_TEXTURE_EXTERNAL_OES, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_BORDER); + self->params.egl->glTexParameteri(GL_TEXTURE_EXTERNAL_OES, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_BORDER); + self->params.egl->glTexParameterfv(GL_TEXTURE_EXTERNAL_OES, GL_TEXTURE_BORDER_COLOR, border_color); 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); @@ -129,8 +133,9 @@ static void gsr_capture_kms_create_input_texture_ids(gsr_capture_kms *self) { self->params.egl->glGenTextures(1, &self->cursor_texture_id); self->params.egl->glBindTexture(cursor_texture_id_target, self->cursor_texture_id); - self->params.egl->glTexParameteri(cursor_texture_id_target, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); - self->params.egl->glTexParameteri(cursor_texture_id_target, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + self->params.egl->glTexParameteri(cursor_texture_id_target, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_BORDER); + self->params.egl->glTexParameteri(cursor_texture_id_target, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_BORDER); + self->params.egl->glTexParameterfv(cursor_texture_id_target, GL_TEXTURE_BORDER_COLOR, border_color); self->params.egl->glTexParameteri(cursor_texture_id_target, GL_TEXTURE_MIN_FILTER, GL_LINEAR); self->params.egl->glTexParameteri(cursor_texture_id_target, GL_TEXTURE_MAG_FILTER, GL_LINEAR); self->params.egl->glBindTexture(cursor_texture_id_target, 0); @@ -497,8 +502,8 @@ static void render_drm_cursor(gsr_capture_kms *self, gsr_color_conversion *color gsr_color_conversion_draw(color_conversion, self->cursor_texture_id, cursor_pos, (vec2i){cursor_size.x * scale.x, cursor_size.y * scale.y}, - (vec2i){0, 0}, cursor_size, - gsr_monitor_rotation_to_rotation(self->monitor_rotation), cursor_texture_id_is_external, GSR_SOURCE_COLOR_RGB); + (vec2i){0, 0}, cursor_size, cursor_size, + gsr_monitor_rotation_to_rotation(self->monitor_rotation), GSR_SOURCE_COLOR_RGB, cursor_texture_id_is_external, true); self->params.egl->glDisable(GL_SCISSOR_TEST); } @@ -525,8 +530,8 @@ static void render_x11_cursor(gsr_capture_kms *self, gsr_color_conversion *color gsr_color_conversion_draw(color_conversion, self->x11_cursor.texture_id, cursor_pos, (vec2i){self->x11_cursor.size.x * scale.x, self->x11_cursor.size.y * scale.y}, - (vec2i){0, 0}, self->x11_cursor.size, - GSR_ROT_0, false, GSR_SOURCE_COLOR_RGB); + (vec2i){0, 0}, self->x11_cursor.size, self->x11_cursor.size, + GSR_ROT_0, GSR_SOURCE_COLOR_RGB, false, true); self->params.egl->glDisable(GL_SCISSOR_TEST); } @@ -569,6 +574,7 @@ static void gsr_capture_kms_update_connector_ids(gsr_capture_kms *self) { monitor.name = self->params.display_to_capture; vec2i monitor_position = {0, 0}; + // TODO: This is cached. We need it updated. drm_monitor_get_display_server_data(self->params.egl->window, &monitor, &self->monitor_rotation, &monitor_position); self->capture_pos = monitor.pos; @@ -611,6 +617,7 @@ static int gsr_capture_kms_capture(gsr_capture *cap, gsr_capture_metadata *captu gsr_kms_set_hdr_metadata(self, drm_fd); self->capture_size = rotate_capture_size_if_rotated(self, (vec2i){ drm_fd->src_w, drm_fd->src_h }); + const vec2i original_frame_size = self->capture_size; if(self->params.region_size.x > 0 && self->params.region_size.y > 0) self->capture_size = self->params.region_size; @@ -639,8 +646,8 @@ static int gsr_capture_kms_capture(gsr_capture *cap, gsr_capture_metadata *captu gsr_color_conversion_draw(color_conversion, self->external_texture_fallback ? self->external_input_texture_id : self->input_texture_id, target_pos, output_size, - capture_pos, self->capture_size, - gsr_monitor_rotation_to_rotation(self->monitor_rotation), self->external_texture_fallback, GSR_SOURCE_COLOR_RGB); + capture_pos, self->capture_size, original_frame_size, + gsr_monitor_rotation_to_rotation(self->monitor_rotation), GSR_SOURCE_COLOR_RGB, self->external_texture_fallback, false); if(self->params.record_cursor) { gsr_kms_response_item *cursor_drm_fd = find_cursor_drm_if_on_monitor(self, drm_fd->connector_id, capture_is_combined_plane); diff --git a/src/capture/nvfbc.c b/src/capture/nvfbc.c index 5f47b00..b92bd41 100644 --- a/src/capture/nvfbc.c +++ b/src/capture/nvfbc.c @@ -363,6 +363,7 @@ static int gsr_capture_nvfbc_capture(gsr_capture *cap, gsr_capture_metadata *cap } vec2i frame_size = (vec2i){self->width, self->height}; + const vec2i original_frame_size = frame_size; if(self->params.region_size.x > 0 && self->params.region_size.y > 0) frame_size = self->params.region_size; @@ -395,8 +396,8 @@ static int gsr_capture_nvfbc_capture(gsr_capture *cap, gsr_capture_metadata *cap gsr_color_conversion_draw(color_conversion, self->setup_params.dwTextures[grab_params.dwTextureIndex], target_pos, (vec2i){output_size.x, output_size.y}, - self->params.region_position, frame_size, - GSR_ROT_0, false, GSR_SOURCE_COLOR_BGR); + self->params.region_position, frame_size, original_frame_size, + GSR_ROT_0, GSR_SOURCE_COLOR_BGR, false, false); //self->params.egl->glFlush(); //self->params.egl->glFinish(); diff --git a/src/capture/portal.c b/src/capture/portal.c index 56072d8..27f514f 100644 --- a/src/capture/portal.c +++ b/src/capture/portal.c @@ -64,26 +64,31 @@ static void gsr_capture_portal_stop(gsr_capture_portal *self) { } static void gsr_capture_portal_create_input_textures(gsr_capture_portal *self) { + const float border_color[4] = { 0.0f, 0.0f, 0.0f, 0.0f }; + self->params.egl->glGenTextures(1, &self->texture_map.texture_id); self->params.egl->glBindTexture(GL_TEXTURE_2D, self->texture_map.texture_id); - self->params.egl->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); - self->params.egl->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + self->params.egl->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_BORDER); + self->params.egl->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_BORDER); + self->params.egl->glTexParameterfv(GL_TEXTURE_2D, GL_TEXTURE_BORDER_COLOR, border_color); self->params.egl->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); self->params.egl->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); self->params.egl->glBindTexture(GL_TEXTURE_2D, 0); self->params.egl->glGenTextures(1, &self->texture_map.external_texture_id); self->params.egl->glBindTexture(GL_TEXTURE_EXTERNAL_OES, self->texture_map.external_texture_id); - self->params.egl->glTexParameteri(GL_TEXTURE_EXTERNAL_OES, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); - self->params.egl->glTexParameteri(GL_TEXTURE_EXTERNAL_OES, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + self->params.egl->glTexParameteri(GL_TEXTURE_EXTERNAL_OES, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_BORDER); + self->params.egl->glTexParameteri(GL_TEXTURE_EXTERNAL_OES, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_BORDER); + self->params.egl->glTexParameterfv(GL_TEXTURE_EXTERNAL_OES, GL_TEXTURE_BORDER_COLOR, border_color); 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); self->params.egl->glGenTextures(1, &self->texture_map.cursor_texture_id); self->params.egl->glBindTexture(GL_TEXTURE_2D, self->texture_map.cursor_texture_id); - self->params.egl->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); - self->params.egl->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + self->params.egl->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_BORDER); + self->params.egl->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_BORDER); + self->params.egl->glTexParameterfv(GL_TEXTURE_2D, GL_TEXTURE_BORDER_COLOR, border_color); self->params.egl->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); self->params.egl->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); self->params.egl->glBindTexture(GL_TEXTURE_2D, 0); @@ -342,8 +347,8 @@ static int gsr_capture_portal_capture(gsr_capture *cap, gsr_capture_metadata *ca gsr_color_conversion_draw(color_conversion, using_external_image ? self->texture_map.external_texture_id : self->texture_map.texture_id, target_pos, output_size, - (vec2i){region.x, region.y}, self->capture_size, - GSR_ROT_0, using_external_image, GSR_SOURCE_COLOR_RGB); + (vec2i){region.x, region.y}, self->capture_size, self->capture_size, + GSR_ROT_0, GSR_SOURCE_COLOR_RGB, using_external_image, false); if(self->params.record_cursor && self->texture_map.cursor_texture_id > 0 && cursor_region.width > 0) { const vec2d scale = { @@ -360,8 +365,8 @@ static int gsr_capture_portal_capture(gsr_capture *cap, gsr_capture_metadata *ca self->params.egl->glScissor(target_pos.x, target_pos.y, output_size.x, output_size.y); gsr_color_conversion_draw(color_conversion, self->texture_map.cursor_texture_id, (vec2i){cursor_pos.x, cursor_pos.y}, (vec2i){cursor_region.width * scale.x, cursor_region.height * scale.y}, - (vec2i){0, 0}, (vec2i){cursor_region.width, cursor_region.height}, - GSR_ROT_0, false, GSR_SOURCE_COLOR_RGB); + (vec2i){0, 0}, (vec2i){cursor_region.width, cursor_region.height}, (vec2i){cursor_region.width, cursor_region.height}, + GSR_ROT_0, GSR_SOURCE_COLOR_RGB, false, true); self->params.egl->glDisable(GL_SCISSOR_TEST); } diff --git a/src/capture/xcomposite.c b/src/capture/xcomposite.c index 16bc988..db41f63 100644 --- a/src/capture/xcomposite.c +++ b/src/capture/xcomposite.c @@ -258,8 +258,8 @@ static int gsr_capture_xcomposite_capture(gsr_capture *cap, gsr_capture_metadata gsr_color_conversion_draw(color_conversion, window_texture_get_opengl_texture_id(&self->window_texture), target_pos, output_size, - (vec2i){0, 0}, self->texture_size, - GSR_ROT_0, false, GSR_SOURCE_COLOR_RGB); + (vec2i){0, 0}, self->texture_size, self->texture_size, + GSR_ROT_0, GSR_SOURCE_COLOR_RGB, false, false); if(self->params.record_cursor && self->cursor.visible) { const vec2d scale = { @@ -274,15 +274,13 @@ static int gsr_capture_xcomposite_capture(gsr_capture *cap, gsr_capture_metadata target_pos.y + (self->cursor.position.y - self->cursor.hotspot.y) * scale.y }; - self->params.egl->glEnable(GL_SCISSOR_TEST); - self->params.egl->glScissor(target_pos.x, target_pos.y, output_size.x, output_size.y); + if(cursor_pos.x < target_pos.x || cursor_pos.x + self->cursor.size.x > target_pos.x + output_size.x || cursor_pos.y < target_pos.y || cursor_pos.y + self->cursor.size.y > target_pos.y + output_size.y) + self->clear_background = true; gsr_color_conversion_draw(color_conversion, self->cursor.texture_id, cursor_pos, (vec2i){self->cursor.size.x * scale.x, self->cursor.size.y * scale.y}, - (vec2i){0, 0}, self->cursor.size, - GSR_ROT_0, false, GSR_SOURCE_COLOR_RGB); - - self->params.egl->glDisable(GL_SCISSOR_TEST); + (vec2i){0, 0}, self->cursor.size, self->cursor.size, + GSR_ROT_0, GSR_SOURCE_COLOR_RGB, false, true); } //self->params.egl->glFlush(); diff --git a/src/capture/ximage.c b/src/capture/ximage.c index ac00d72..9b02907 100644 --- a/src/capture/ximage.c +++ b/src/capture/ximage.c @@ -159,8 +159,8 @@ static int gsr_capture_ximage_capture(gsr_capture *cap, gsr_capture_metadata *ca gsr_color_conversion_draw(color_conversion, self->texture_id, target_pos, output_size, - (vec2i){0, 0}, self->capture_size, - GSR_ROT_0, false, GSR_SOURCE_COLOR_RGB); + (vec2i){0, 0}, self->capture_size, self->capture_size, + GSR_ROT_0, GSR_SOURCE_COLOR_RGB, false, false); if(self->params.record_cursor && self->cursor.visible) { const vec2d scale = { @@ -180,8 +180,8 @@ static int gsr_capture_ximage_capture(gsr_capture *cap, gsr_capture_metadata *ca gsr_color_conversion_draw(color_conversion, self->cursor.texture_id, cursor_pos, (vec2i){self->cursor.size.x * scale.x, self->cursor.size.y * scale.y}, - (vec2i){0, 0}, self->cursor.size, - GSR_ROT_0, false, GSR_SOURCE_COLOR_RGB); + (vec2i){0, 0}, self->cursor.size, self->cursor.size, + GSR_ROT_0, GSR_SOURCE_COLOR_RGB, false, true); self->params.egl->glDisable(GL_SCISSOR_TEST); } diff --git a/src/color_conversion.c b/src/color_conversion.c index 5d9da3c..88dc398 100644 --- a/src/color_conversion.c +++ b/src/color_conversion.c @@ -5,15 +5,25 @@ #include <string.h> #include <assert.h> -// TODO: Scissor doesn't work with compute shader. In the compute shader this can be implemented with two step calls, and using the result -// with a call to mix to choose source/output color. - -#define GL_SHADER_IMAGE_ACCESS_BARRIER_BIT 0x00000020 -// TODO: Use the minimal barrier required and move this to egl.h -#define GL_ALL_BARRIER_BITS 0xFFFFFFFF - -#define MAX_FRAMEBUFFERS 2 -#define EXTERNAL_TEXTURE_SHADER_OFFSET 2 +#define COMPUTE_SHADER_INDEX_Y 0 +#define COMPUTE_SHADER_INDEX_UV 1 +#define COMPUTE_SHADER_INDEX_Y_EXTERNAL 2 +#define COMPUTE_SHADER_INDEX_UV_EXTERNAL 3 +#define COMPUTE_SHADER_INDEX_RGB 4 +#define COMPUTE_SHADER_INDEX_RGB_EXTERNAL 5 +#define COMPUTE_SHADER_INDEX_Y_BLEND 6 +#define COMPUTE_SHADER_INDEX_UV_BLEND 7 +#define COMPUTE_SHADER_INDEX_Y_EXTERNAL_BLEND 8 +#define COMPUTE_SHADER_INDEX_UV_EXTERNAL_BLEND 9 +#define COMPUTE_SHADER_INDEX_RGB_BLEND 10 +#define COMPUTE_SHADER_INDEX_RGB_EXTERNAL_BLEND 11 + +#define GRAPHICS_SHADER_INDEX_Y 0 +#define GRAPHICS_SHADER_INDEX_UV 1 +#define GRAPHICS_SHADER_INDEX_Y_EXTERNAL 2 +#define GRAPHICS_SHADER_INDEX_UV_EXTERNAL 3 +#define GRAPHICS_SHADER_INDEX_RGB 4 +#define GRAPHICS_SHADER_INDEX_RGB_EXTERNAL 5 /* https://en.wikipedia.org/wiki/YCbCr, see study/color_space_transform_matrix.png */ @@ -75,20 +85,35 @@ static const char* color_format_range_get_transform_matrix(gsr_destination_color return NULL; } -// TODO: Make alpha blending optional -// TODO: Optimize these shaders. -static int load_compute_shader_y(gsr_shader *shader, gsr_egl *egl, gsr_color_uniforms *uniforms, int max_local_size_dim, gsr_destination_color color_format, gsr_color_range color_range, bool external_texture) { +static void get_compute_shader_header(char *header, size_t header_size, bool external_texture) { + if(external_texture) { + snprintf(header, header_size, + "#version 310 es\n" + "#extension GL_ARB_compute_shader: enable\n" + "#extension GL_OES_EGL_image_external : enable\n" + "#extension GL_OES_EGL_image_external_essl3 : require\n" + "layout(binding = 0) uniform highp samplerExternalOES img_input;\n" + "layout(binding = 1) uniform highp sampler2D img_background;\n"); + } else { + snprintf(header, header_size, + "#version 420\n" + "#extension GL_ARB_compute_shader: enable\n" + "layout(binding = 0) uniform highp sampler2D img_input;\n" + "layout(binding = 1) uniform highp sampler2D img_background;\n"); + } +} + +static int load_compute_shader_y(gsr_shader *shader, gsr_egl *egl, gsr_color_compute_uniforms *uniforms, int max_local_size_dim, gsr_destination_color color_format, gsr_color_range color_range, bool external_texture, bool alpha_blending) { const char *color_transform_matrix = color_format_range_get_transform_matrix(color_format, color_range); + char header[512]; + get_compute_shader_header(header, sizeof(header), external_texture); + char compute_shader[2048]; snprintf(compute_shader, sizeof(compute_shader), - "#version 310 es\n" - "#extension GL_OES_EGL_image_external : enable\n" - "#extension GL_OES_EGL_image_external_essl3 : require\n" - "precision highp float;\n" + "%s" "layout (local_size_x = %d, local_size_y = %d, local_size_z = 1) in;\n" - "layout(binding = 0) uniform highp %s img_input;\n" - "layout(binding = 1) uniform highp sampler2D img_background;\n" + "precision highp float;\n" "uniform ivec2 source_position;\n" "uniform ivec2 target_position;\n" "uniform vec2 scale;\n" @@ -98,15 +123,17 @@ static int load_compute_shader_y(gsr_shader *shader, gsr_egl *egl, gsr_color_uni "void main() {\n" " ivec2 texel_coord = ivec2(gl_GlobalInvocationID.xy);\n" " ivec2 size = ivec2(vec2(textureSize(img_input, 0)) * scale + 0.5);\n" + " ivec2 size_shift = size >> 1;\n" // size/2 " ivec2 output_size = textureSize(img_background, 0);\n" - " vec2 rotated_texel_coord = vec2(texel_coord - source_position - size/2) * rotation_matrix + vec2(size/2) + 0.5;\n" - " vec2 tex_coord = vec2(rotated_texel_coord)/vec2(size);\n" - " vec4 source_color = texture(img_input, tex_coord);\n" + " vec2 rotated_texel_coord = vec2(texel_coord - source_position - size_shift) * rotation_matrix + vec2(size_shift) + 0.5;\n" + " vec2 output_texel_coord = vec2(texel_coord - source_position + target_position) + 0.5;\n" + " vec4 source_color = texture(img_input, rotated_texel_coord/vec2(size));\n" " vec4 source_color_yuv = RGBtoYUV * vec4(source_color.rgb, 1.0);\n" - " vec4 output_color_yuv = texture(img_background, (rotated_texel_coord + vec2(target_position))/vec2(output_size));\n" + " vec4 output_color_yuv = %s;\n" " float y_color = mix(output_color_yuv.r, source_color_yuv.r, source_color.a);\n" " imageStore(img_output, texel_coord + target_position, vec4(y_color, 1.0, 1.0, 1.0));\n" - "}\n", max_local_size_dim, max_local_size_dim, external_texture ? "samplerExternalOES" : "sampler2D", color_transform_matrix); + "}\n", header, max_local_size_dim, max_local_size_dim, color_transform_matrix, + alpha_blending ? "texture(img_background, output_texel_coord/vec2(output_size))" : "source_color_yuv"); if(gsr_shader_init(shader, egl, NULL, NULL, compute_shader) != 0) return -1; @@ -118,18 +145,17 @@ static int load_compute_shader_y(gsr_shader *shader, gsr_egl *egl, gsr_color_uni return 0; } -static int load_compute_shader_uv(gsr_shader *shader, gsr_egl *egl, gsr_color_uniforms *uniforms, int max_local_size_dim, gsr_destination_color color_format, gsr_color_range color_range, bool external_texture) { +static int load_compute_shader_uv(gsr_shader *shader, gsr_egl *egl, gsr_color_compute_uniforms *uniforms, int max_local_size_dim, gsr_destination_color color_format, gsr_color_range color_range, bool external_texture, bool alpha_blending) { const char *color_transform_matrix = color_format_range_get_transform_matrix(color_format, color_range); + char header[512]; + get_compute_shader_header(header, sizeof(header), external_texture); + char compute_shader[2048]; snprintf(compute_shader, sizeof(compute_shader), - "#version 310 es\n" - "#extension GL_OES_EGL_image_external : enable\n" - "#extension GL_OES_EGL_image_external_essl3 : require\n" - "precision highp float;\n" + "%s" "layout (local_size_x = %d, local_size_y = %d, local_size_z = 1) in;\n" - "layout(binding = 0) uniform highp %s img_input;\n" - "layout(binding = 1) uniform highp sampler2D img_background;\n" + "precision highp float;\n" "uniform ivec2 source_position;\n" "uniform ivec2 target_position;\n" "uniform vec2 scale;\n" @@ -139,15 +165,17 @@ static int load_compute_shader_uv(gsr_shader *shader, gsr_egl *egl, gsr_color_un "void main() {\n" " ivec2 texel_coord = ivec2(gl_GlobalInvocationID.xy);\n" " ivec2 size = ivec2(vec2(textureSize(img_input, 0)) * scale + 0.5);\n" + " ivec2 size_shift = size >> 2;\n" // size/4 " ivec2 output_size = textureSize(img_background, 0);\n" - " vec2 rotated_texel_coord = vec2(texel_coord - source_position/2 - size/4) * rotation_matrix + vec2(size/4) + 0.5;\n" - " vec2 tex_coord = vec2(rotated_texel_coord)/vec2(size);\n" - " vec4 source_color = texture(img_input, tex_coord * 2.0);\n" + " vec2 rotated_texel_coord = vec2(texel_coord - source_position - size_shift) * rotation_matrix + vec2(size_shift) + 0.5;\n" + " vec2 output_texel_coord = vec2(texel_coord - source_position + target_position) + 0.5;\n" + " vec4 source_color = texture(img_input, rotated_texel_coord/vec2(size>>1));\n" // size/2 " vec4 source_color_yuv = RGBtoYUV * vec4(source_color.rgb, 1.0);\n" - " vec4 output_color_yuv = texture(img_background, (rotated_texel_coord + vec2(target_position/2))/vec2(output_size));\n" + " vec4 output_color_yuv = %s;\n" " vec2 uv_color = mix(output_color_yuv.rg, source_color_yuv.gb, source_color.a);\n" - " imageStore(img_output, texel_coord + target_position/2, vec4(uv_color, 1.0, 1.0));\n" - "}\n", max_local_size_dim, max_local_size_dim, external_texture ? "samplerExternalOES" : "sampler2D", color_transform_matrix); + " imageStore(img_output, texel_coord + target_position, vec4(uv_color, 1.0, 1.0));\n" + "}\n", header, max_local_size_dim, max_local_size_dim, color_transform_matrix, + alpha_blending ? "texture(img_background, output_texel_coord/vec2(output_size))" : "source_color_yuv"); if(gsr_shader_init(shader, egl, NULL, NULL, compute_shader) != 0) return -1; @@ -159,15 +187,14 @@ static int load_compute_shader_uv(gsr_shader *shader, gsr_egl *egl, gsr_color_un return 0; } -static int load_compute_shader_rgb(gsr_shader *shader, gsr_egl *egl, gsr_color_uniforms *uniforms, int max_local_size_dim, bool external_texture) { +static int load_compute_shader_rgb(gsr_shader *shader, gsr_egl *egl, gsr_color_compute_uniforms *uniforms, int max_local_size_dim, bool external_texture, bool alpha_blending) { + char header[512]; + get_compute_shader_header(header, sizeof(header), external_texture); + char compute_shader[2048]; snprintf(compute_shader, sizeof(compute_shader), - "#version 310 es\n" - "#extension GL_OES_EGL_image_external : enable\n" - "#extension GL_OES_EGL_image_external_essl3 : require\n" + "%s" "layout (local_size_x = %d, local_size_y = %d, local_size_z = 1) in;\n" - "layout(binding = 0) uniform highp %s img_input;\n" - "layout(binding = 1) uniform highp sampler2D img_background;\n" "uniform ivec2 source_position;\n" "uniform ivec2 target_position;\n" "uniform vec2 scale;\n" @@ -176,14 +203,16 @@ static int load_compute_shader_rgb(gsr_shader *shader, gsr_egl *egl, gsr_color_u "void main() {\n" " ivec2 texel_coord = ivec2(gl_GlobalInvocationID.xy);\n" " ivec2 size = ivec2(vec2(textureSize(img_input, 0)) * scale + 0.5);\n" + " ivec2 size_shift = size >> 1;\n" // size/2 " ivec2 output_size = textureSize(img_background, 0);\n" - " vec2 rotated_texel_coord = vec2(texel_coord - source_position - size/2) * rotation_matrix + vec2(size/2) + 0.5;\n" - " vec2 tex_coord = vec2(rotated_texel_coord)/vec2(size);\n" - " vec4 source_color = texture(img_input, tex_coord);\n" - " vec4 output_color = texture(img_background, (rotated_texel_coord + vec2(target_position))/vec2(output_size));\n" + " vec2 rotated_texel_coord = vec2(texel_coord - source_position - size_shift) * rotation_matrix + vec2(size_shift) + 0.5;\n" + " vec2 output_texel_coord = vec2(texel_coord - source_position + target_position) + 0.5;\n" + " vec4 source_color = texture(img_input, rotated_texel_coord/vec2(size));\n" + " vec4 output_color = %s;\n" " vec3 color = mix(output_color.rgb, source_color.rgb, source_color.a);\n" " imageStore(img_output, texel_coord + target_position, vec4(color, 1.0));\n" - "}\n", max_local_size_dim, max_local_size_dim, external_texture ? "samplerExternalOES" : "sampler2D"); + "}\n", header, max_local_size_dim, max_local_size_dim, + alpha_blending ? "texture(img_background, output_texel_coord/vec2(output_size))" : "source_color"); if(gsr_shader_init(shader, egl, NULL, NULL, compute_shader) != 0) return -1; @@ -195,10 +224,194 @@ static int load_compute_shader_rgb(gsr_shader *shader, gsr_egl *egl, gsr_color_u return 0; } +static int load_graphics_shader_y(gsr_shader *shader, gsr_egl *egl, gsr_color_graphics_uniforms *uniforms, gsr_destination_color color_format, gsr_color_range color_range, bool external_texture) { + const char *color_transform_matrix = color_format_range_get_transform_matrix(color_format, color_range); + + 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 = + main_code = + " vec4 pixel = texture(tex1, texcoords_out); \n" + " FragColor.x = (RGBtoYUV * vec4(pixel.rgb, 1.0)).x; \n" + " FragColor.w = pixel.a; \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" + "%s" + "void main() \n" + "{ \n" + "%s" + "} \n", color_transform_matrix, 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" + "%s" + "void main() \n" + "{ \n" + "%s" + "} \n", color_transform_matrix, main_code); + } + + if(gsr_shader_init(shader, egl, vertex_shader, fragment_shader, NULL) != 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_uv(gsr_shader *shader, gsr_egl *egl, gsr_color_graphics_uniforms *uniforms, gsr_destination_color color_format, gsr_color_range color_range, bool external_texture) { + const char *color_transform_matrix = color_format_range_get_transform_matrix(color_format, color_range); + + 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 = + main_code = + " vec4 pixel = texture(tex1, texcoords_out); \n" + " FragColor.xy = (RGBtoYUV * vec4(pixel.rgb, 1.0)).yz; \n" + " FragColor.w = pixel.a; \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" + "%s" + "void main() \n" + "{ \n" + "%s" + "} \n", color_transform_matrix, 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" + "%s" + "void main() \n" + "{ \n" + "%s" + "} \n", color_transform_matrix, main_code); + } + + if(gsr_shader_init(shader, egl, vertex_shader, fragment_shader, NULL) != 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_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), + "#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 = + main_code = + " vec4 pixel = texture(tex1, texcoords_out); \n" + " FragColor = pixel; \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, NULL) != 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 int load_framebuffers(gsr_color_conversion *self) { /* TODO: Only generate the necessary amount of framebuffers (self->params.num_destination_textures) */ const unsigned int draw_buffer = GL_COLOR_ATTACHMENT0; - self->params.egl->glGenFramebuffers(MAX_FRAMEBUFFERS, self->framebuffers); + self->params.egl->glGenFramebuffers(GSR_COLOR_CONVERSION_MAX_FRAMEBUFFERS, self->framebuffers); self->params.egl->glBindFramebuffer(GL_FRAMEBUFFER, self->framebuffers[0]); self->params.egl->glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, self->params.destination_textures[0], 0); @@ -244,6 +457,140 @@ static int create_vertices(gsr_color_conversion *self) { return 0; } +static bool gsr_color_conversion_load_compute_shaders(gsr_color_conversion *self) { + switch(self->params.destination_color) { + case GSR_DESTINATION_COLOR_NV12: + case GSR_DESTINATION_COLOR_P010: { + if(load_compute_shader_y(&self->compute_shaders[COMPUTE_SHADER_INDEX_Y], self->params.egl, &self->compute_uniforms[COMPUTE_SHADER_INDEX_Y], self->max_local_size_dim, self->params.destination_color, self->params.color_range, false, false) != 0) { + fprintf(stderr, "gsr error: gsr_color_conversion_init: failed to load Y compute shader\n"); + return false; + } + + if(load_compute_shader_uv(&self->compute_shaders[COMPUTE_SHADER_INDEX_UV], self->params.egl, &self->compute_uniforms[COMPUTE_SHADER_INDEX_UV], self->max_local_size_dim, self->params.destination_color, self->params.color_range, false, false) != 0) { + fprintf(stderr, "gsr error: gsr_color_conversion_init: failed to load UV compute shader\n"); + return false; + } + + if(load_compute_shader_y(&self->compute_shaders[COMPUTE_SHADER_INDEX_Y_BLEND], self->params.egl, &self->compute_uniforms[COMPUTE_SHADER_INDEX_Y_BLEND], self->max_local_size_dim, self->params.destination_color, self->params.color_range, false, true) != 0) { + fprintf(stderr, "gsr error: gsr_color_conversion_init: failed to load Y compute shader\n"); + return false; + } + + if(load_compute_shader_uv(&self->compute_shaders[COMPUTE_SHADER_INDEX_UV_BLEND], self->params.egl, &self->compute_uniforms[COMPUTE_SHADER_INDEX_UV_BLEND], self->max_local_size_dim, self->params.destination_color, self->params.color_range, false, true) != 0) { + fprintf(stderr, "gsr error: gsr_color_conversion_init: failed to load UV compute shader\n"); + return false; + } + break; + } + case GSR_DESTINATION_COLOR_RGB8: { + if(load_compute_shader_rgb(&self->compute_shaders[COMPUTE_SHADER_INDEX_RGB], self->params.egl, &self->compute_uniforms[COMPUTE_SHADER_INDEX_RGB], self->max_local_size_dim, false, false) != 0) { + fprintf(stderr, "gsr error: gsr_color_conversion_init: failed to load Y compute shader\n"); + return false; + } + + if(load_compute_shader_rgb(&self->compute_shaders[COMPUTE_SHADER_INDEX_RGB_BLEND], self->params.egl, &self->compute_uniforms[COMPUTE_SHADER_INDEX_RGB_BLEND], self->max_local_size_dim, false, true) != 0) { + fprintf(stderr, "gsr error: gsr_color_conversion_init: failed to load Y compute shader\n"); + return false; + } + break; + } + } + return true; +} + +static bool gsr_color_conversion_load_external_compute_shaders(gsr_color_conversion *self) { + switch(self->params.destination_color) { + case GSR_DESTINATION_COLOR_NV12: + case GSR_DESTINATION_COLOR_P010: { + if(load_compute_shader_y(&self->compute_shaders[COMPUTE_SHADER_INDEX_Y_EXTERNAL], self->params.egl, &self->compute_uniforms[COMPUTE_SHADER_INDEX_Y_EXTERNAL], self->max_local_size_dim, self->params.destination_color, self->params.color_range, true, false) != 0) { + fprintf(stderr, "gsr error: gsr_color_conversion_init: failed to load Y compute shader\n"); + return false; + } + + if(load_compute_shader_uv(&self->compute_shaders[COMPUTE_SHADER_INDEX_UV_EXTERNAL], self->params.egl, &self->compute_uniforms[COMPUTE_SHADER_INDEX_UV_EXTERNAL], self->max_local_size_dim, self->params.destination_color, self->params.color_range, true, false) != 0) { + fprintf(stderr, "gsr error: gsr_color_conversion_init: failed to load UV compute shader\n"); + return false; + } + + if(load_compute_shader_y(&self->compute_shaders[COMPUTE_SHADER_INDEX_Y_EXTERNAL_BLEND], self->params.egl, &self->compute_uniforms[COMPUTE_SHADER_INDEX_Y_EXTERNAL_BLEND], self->max_local_size_dim, self->params.destination_color, self->params.color_range, true, true) != 0) { + fprintf(stderr, "gsr error: gsr_color_conversion_init: failed to load Y compute shader\n"); + return false; + } + + if(load_compute_shader_uv(&self->compute_shaders[COMPUTE_SHADER_INDEX_UV_EXTERNAL_BLEND], self->params.egl, &self->compute_uniforms[COMPUTE_SHADER_INDEX_UV_EXTERNAL_BLEND], self->max_local_size_dim, self->params.destination_color, self->params.color_range, true, true) != 0) { + fprintf(stderr, "gsr error: gsr_color_conversion_init: failed to load UV compute shader\n"); + return false; + } + break; + } + case GSR_DESTINATION_COLOR_RGB8: { + if(load_compute_shader_rgb(&self->compute_shaders[COMPUTE_SHADER_INDEX_RGB_EXTERNAL], self->params.egl, &self->compute_uniforms[COMPUTE_SHADER_INDEX_RGB_EXTERNAL], self->max_local_size_dim, true, false) != 0) { + fprintf(stderr, "gsr error: gsr_color_conversion_init: failed to load Y compute shader\n"); + return false; + } + + if(load_compute_shader_rgb(&self->compute_shaders[COMPUTE_SHADER_INDEX_RGB_EXTERNAL_BLEND], self->params.egl, &self->compute_uniforms[COMPUTE_SHADER_INDEX_RGB_EXTERNAL_BLEND], self->max_local_size_dim, true, true) != 0) { + fprintf(stderr, "gsr error: gsr_color_conversion_init: failed to load Y compute shader\n"); + return false; + } + break; + } + } + return true; +} + +static bool gsr_color_conversion_load_graphics_shaders(gsr_color_conversion *self) { + switch(self->params.destination_color) { + case GSR_DESTINATION_COLOR_NV12: + case GSR_DESTINATION_COLOR_P010: { + if(load_graphics_shader_y(&self->graphics_shaders[GRAPHICS_SHADER_INDEX_Y], self->params.egl, &self->graphics_uniforms[GRAPHICS_SHADER_INDEX_Y], self->params.destination_color, self->params.color_range, false) != 0) { + fprintf(stderr, "gsr error: gsr_color_conversion_init: failed to load Y graphics shader\n"); + return false; + } + + if(load_graphics_shader_uv(&self->graphics_shaders[GRAPHICS_SHADER_INDEX_UV], self->params.egl, &self->graphics_uniforms[GRAPHICS_SHADER_INDEX_UV], self->params.destination_color, self->params.color_range, false) != 0) { + fprintf(stderr, "gsr error: gsr_color_conversion_init: failed to load UV graphics shader\n"); + return false; + } + break; + } + case GSR_DESTINATION_COLOR_RGB8: { + if(load_graphics_shader_rgb(&self->graphics_shaders[GRAPHICS_SHADER_INDEX_RGB], self->params.egl, &self->graphics_uniforms[GRAPHICS_SHADER_INDEX_RGB], false) != 0) { + fprintf(stderr, "gsr error: gsr_color_conversion_init: failed to load Y graphics shader\n"); + return false; + } + break; + } + } + return true; +} + +static bool gsr_color_conversion_load_external_graphics_shaders(gsr_color_conversion *self) { + switch(self->params.destination_color) { + case GSR_DESTINATION_COLOR_NV12: + case GSR_DESTINATION_COLOR_P010: { + if(load_graphics_shader_y(&self->graphics_shaders[GRAPHICS_SHADER_INDEX_Y_EXTERNAL], self->params.egl, &self->graphics_uniforms[GRAPHICS_SHADER_INDEX_Y_EXTERNAL], self->params.destination_color, self->params.color_range, true) != 0) { + fprintf(stderr, "gsr error: gsr_color_conversion_init: failed to load Y graphics shader\n"); + return false; + } + + if(load_graphics_shader_uv(&self->graphics_shaders[GRAPHICS_SHADER_INDEX_UV_EXTERNAL], self->params.egl, &self->graphics_uniforms[GRAPHICS_SHADER_INDEX_UV_EXTERNAL], self->params.destination_color, self->params.color_range, true) != 0) { + fprintf(stderr, "gsr error: gsr_color_conversion_init: failed to load UV graphics shader\n"); + return false; + } + break; + } + case GSR_DESTINATION_COLOR_RGB8: { + if(load_graphics_shader_rgb(&self->graphics_shaders[GRAPHICS_SHADER_INDEX_RGB_EXTERNAL], self->params.egl, &self->graphics_uniforms[GRAPHICS_SHADER_INDEX_RGB_EXTERNAL], true) != 0) { + fprintf(stderr, "gsr error: gsr_color_conversion_init: failed to load Y graphics shader\n"); + return false; + } + break; + } + } + return true; +} + int gsr_color_conversion_init(gsr_color_conversion *self, const gsr_color_conversion_params *params) { assert(params); assert(params->egl); @@ -255,58 +602,40 @@ int gsr_color_conversion_init(gsr_color_conversion *self, const gsr_color_conver self->params.egl->glGetIntegerv(GL_MAX_COMPUTE_FIXED_GROUP_INVOCATIONS, &max_compute_work_group_invocations); self->max_local_size_dim = sqrt(max_compute_work_group_invocations); - switch(params->destination_color) { + switch(self->params.destination_color) { case GSR_DESTINATION_COLOR_NV12: case GSR_DESTINATION_COLOR_P010: { if(self->params.num_destination_textures != 2) { fprintf(stderr, "gsr error: gsr_color_conversion_init: expected 2 destination textures for destination color NV12/P010, got %d destination texture(s)\n", self->params.num_destination_textures); - return -1; - } - - if(load_compute_shader_y(&self->shaders[0], self->params.egl, &self->uniforms[0], self->max_local_size_dim, params->destination_color, params->color_range, false) != 0) { - fprintf(stderr, "gsr error: gsr_color_conversion_init: failed to load Y compute shader\n"); goto err; } - - if(load_compute_shader_uv(&self->shaders[1], self->params.egl, &self->uniforms[1], self->max_local_size_dim, params->destination_color, params->color_range, false) != 0) { - fprintf(stderr, "gsr error: gsr_color_conversion_init: failed to load UV compute shader\n"); - goto err; - } - - if(self->params.load_external_image_shader) { - if(load_compute_shader_y(&self->shaders[2], self->params.egl, &self->uniforms[2], self->max_local_size_dim, params->destination_color, params->color_range, true) != 0) { - fprintf(stderr, "gsr error: gsr_color_conversion_init: failed to load Y compute shader\n"); - goto err; - } - - if(load_compute_shader_uv(&self->shaders[3], self->params.egl, &self->uniforms[3], self->max_local_size_dim, params->destination_color, params->color_range, true) != 0) { - fprintf(stderr, "gsr error: gsr_color_conversion_init: failed to load UV compute shader\n"); - goto err; - } - } break; } case GSR_DESTINATION_COLOR_RGB8: { if(self->params.num_destination_textures != 1) { fprintf(stderr, "gsr error: gsr_color_conversion_init: expected 1 destination textures for destination color RGB8, got %d destination texture(s)\n", self->params.num_destination_textures); - return -1; - } - - if(load_compute_shader_rgb(&self->shaders[4], self->params.egl, &self->uniforms[4], self->max_local_size_dim, false) != 0) { - fprintf(stderr, "gsr error: gsr_color_conversion_init: failed to load Y compute shader\n"); goto err; } - - if(self->params.load_external_image_shader) { - if(load_compute_shader_rgb(&self->shaders[5], self->params.egl, &self->uniforms[5], self->max_local_size_dim, true) != 0) { - fprintf(stderr, "gsr error: gsr_color_conversion_init: failed to load Y compute shader\n"); - goto err; - } - } break; } } + if(!gsr_color_conversion_load_compute_shaders(self)) { + self->compute_shaders_failed_to_load = true; + fprintf(stderr, "gsr info: failed to load one or more compute shaders, run gpu-screen-recorder with the '-gl-debug yes' option to see why. Falling back to slower graphics shader instead\n"); + if(!gsr_color_conversion_load_graphics_shaders(self)) + goto err; + } + + if(self->params.load_external_image_shader) { + if(!gsr_color_conversion_load_external_compute_shaders(self)) { + self->external_compute_shaders_failed_to_load = true; + fprintf(stderr, "gsr info: failed to load one or more external compute shaders, run gpu-screen-recorder with the '-gl-debug yes' option to see why. Falling back to slower graphics shader instead\n"); + if(!gsr_color_conversion_load_external_graphics_shaders(self)) + goto err; + } + } + if(load_framebuffers(self) != 0) goto err; @@ -334,19 +663,23 @@ void gsr_color_conversion_deinit(gsr_color_conversion *self) { self->vertex_array_object_id = 0; } - self->params.egl->glDeleteFramebuffers(MAX_FRAMEBUFFERS, self->framebuffers); - for(int i = 0; i < MAX_FRAMEBUFFERS; ++i) { + self->params.egl->glDeleteFramebuffers(GSR_COLOR_CONVERSION_MAX_FRAMEBUFFERS, self->framebuffers); + for(int i = 0; i < GSR_COLOR_CONVERSION_MAX_FRAMEBUFFERS; ++i) { self->framebuffers[i] = 0; } - for(int i = 0; i < GSR_COLOR_CONVERSION_MAX_SHADERS; ++i) { - gsr_shader_deinit(&self->shaders[i]); + for(int i = 0; i < GSR_COLOR_CONVERSION_MAX_COMPUTE_SHADERS; ++i) { + gsr_shader_deinit(&self->compute_shaders[i]); + } + + for(int i = 0; i < GSR_COLOR_CONVERSION_MAX_GRAPHICS_SHADERS; ++i) { + gsr_shader_deinit(&self->graphics_shaders[i]); } self->params.egl = NULL; } -static void gsr_color_conversion_apply_rotation(gsr_rotation rotation, float rotation_matrix[2][2], vec2i *source_position, vec2i texture_size, vec2f scale) { +static void gsr_color_conversion_apply_rotation(gsr_rotation rotation, float rotation_matrix[2][2]) { /* rotation_matrix[0][0] = cos(angle); rotation_matrix[0][1] = -sin(angle); @@ -367,8 +700,6 @@ static void gsr_color_conversion_apply_rotation(gsr_rotation rotation, float rot rotation_matrix[0][1] = -1.0f; rotation_matrix[1][0] = 1.0f; rotation_matrix[1][1] = 0.0f; - source_position->x += (((double)texture_size.x*0.5 - (double)texture_size.y*0.5) * scale.x + 0.5); - source_position->y += (((double)texture_size.y*0.5 - (double)texture_size.x*0.5) * scale.y + 0.5); break; case GSR_ROT_180: rotation_matrix[0][0] = -1.0f; @@ -381,8 +712,6 @@ static void gsr_color_conversion_apply_rotation(gsr_rotation rotation, float rot rotation_matrix[0][1] = 1.0f; rotation_matrix[1][0] = -1.0f; rotation_matrix[1][1] = 0.0f; - source_position->x += (((double)texture_size.x*0.5 - (double)texture_size.y*0.5) * scale.x + 0.5); - source_position->y += (((double)texture_size.y*0.5 - (double)texture_size.x*0.5) * scale.y + 0.5); break; } } @@ -401,87 +730,232 @@ static void gsr_color_conversion_swizzle_reset(gsr_color_conversion *self, gsr_s } } -// TODO: Handle source_color -void gsr_color_conversion_draw(gsr_color_conversion *self, unsigned int texture_id, vec2i destination_pos, vec2i destination_size, vec2i texture_pos, vec2i texture_size, gsr_rotation rotation, bool external_texture, gsr_source_color source_color) { - vec2f scale = {0.0f, 0.0f}; - if(texture_size.x > 0 && texture_size.y > 0) - scale = (vec2f){ (double)destination_size.x/(double)texture_size.x, (double)destination_size.y/(double)texture_size.y }; +typedef enum { + GSR_COLOR_COMP_Y, + GSR_COLOR_COMP_UV, + GSR_COLOR_COMP_RGB +} gsr_color_component; + +static int color_component_get_destination_texture_index(gsr_color_component color_component) { + switch(color_component) { + case GSR_COLOR_COMP_Y: return 0; + case GSR_COLOR_COMP_UV: return 1; + case GSR_COLOR_COMP_RGB: return 0; + } + assert(false); + return 0; +} - vec2i source_position = {0, 0}; - float rotation_matrix[2][2] = {{0, 0}, {0, 0}}; - gsr_color_conversion_apply_rotation(rotation, rotation_matrix, &source_position, texture_size, scale); +static unsigned int color_component_get_color_format(gsr_color_component color_component, bool use_16bit_colors) { + switch(color_component) { + case GSR_COLOR_COMP_Y: return use_16bit_colors ? GL_R16 : GL_R8; + case GSR_COLOR_COMP_UV: return use_16bit_colors ? GL_RG16 : GL_RG8; + case GSR_COLOR_COMP_RGB: return GL_RGBA8; // TODO: 16-bit color support + } + assert(false); + return GL_RGBA8; +} + +static int color_component_get_COMPUTE_SHADER_INDEX(gsr_color_component color_component, bool external_texture, bool alpha_blending) { + switch(color_component) { + case GSR_COLOR_COMP_Y: { + if(external_texture) + return alpha_blending ? COMPUTE_SHADER_INDEX_Y_EXTERNAL_BLEND : COMPUTE_SHADER_INDEX_Y_EXTERNAL; + else + return alpha_blending ? COMPUTE_SHADER_INDEX_Y_BLEND : COMPUTE_SHADER_INDEX_Y; + } + case GSR_COLOR_COMP_UV: { + if(external_texture) + return alpha_blending ? COMPUTE_SHADER_INDEX_UV_EXTERNAL_BLEND : COMPUTE_SHADER_INDEX_UV_EXTERNAL; + else + return alpha_blending ? COMPUTE_SHADER_INDEX_UV_BLEND : COMPUTE_SHADER_INDEX_UV; + } + case GSR_COLOR_COMP_RGB: { + if(external_texture) + return alpha_blending ? COMPUTE_SHADER_INDEX_RGB_EXTERNAL_BLEND : COMPUTE_SHADER_INDEX_RGB_EXTERNAL; + else + return alpha_blending ? COMPUTE_SHADER_INDEX_RGB_BLEND : COMPUTE_SHADER_INDEX_RGB; + } + } + assert(false); + return COMPUTE_SHADER_INDEX_RGB; +} + +static void gsr_color_conversion_dispatch_compute_shader(gsr_color_conversion *self, bool external_texture, bool alpha_blending, float rotation_matrix[2][2], vec2i source_position, vec2i destination_pos, vec2i destination_size, vec2f scale, bool use_16bit_colors, gsr_color_component color_component) { + const int compute_shader_index = color_component_get_COMPUTE_SHADER_INDEX(color_component, external_texture, alpha_blending); + const int destination_texture_index = color_component_get_destination_texture_index(color_component); + const unsigned int color_format = color_component_get_color_format(color_component, use_16bit_colors); + + self->params.egl->glActiveTexture(GL_TEXTURE1); + self->params.egl->glBindTexture(GL_TEXTURE_2D, self->params.destination_textures[destination_texture_index]); + self->params.egl->glActiveTexture(GL_TEXTURE0); + + gsr_color_compute_uniforms *uniform = &self->compute_uniforms[compute_shader_index]; + gsr_shader_use(&self->compute_shaders[compute_shader_index]); + self->params.egl->glUniformMatrix2fv(uniform->rotation_matrix, 1, GL_TRUE, (const float*)rotation_matrix); + self->params.egl->glUniform2i(uniform->source_position, source_position.x, source_position.y); + self->params.egl->glUniform2i(uniform->target_position, destination_pos.x, destination_pos.y); + self->params.egl->glUniform2f(uniform->scale, scale.x, scale.y); + self->params.egl->glBindImageTexture(0, self->params.destination_textures[destination_texture_index], 0, GL_FALSE, 0, GL_WRITE_ONLY, color_format); + const double num_groups_x = ceil((double)destination_size.x/(double)self->max_local_size_dim); + const double num_groups_y = ceil((double)destination_size.y/(double)self->max_local_size_dim); + self->params.egl->glDispatchCompute(max_int(1, num_groups_x), max_int(1, num_groups_y), 1); +} - source_position.x += texture_pos.x; - source_position.y += texture_pos.y; +static void gsr_color_conversion_draw_graphics(gsr_color_conversion *self, unsigned int texture_id, bool external_texture, float rotation_matrix[2][2], vec2i source_position, vec2i source_size, vec2i destination_pos, vec2i texture_size, vec2f scale, gsr_source_color source_color) { + /* TODO: Do not call this every frame? */ + vec2i dest_texture_size = {0, 0}; + self->params.egl->glBindTexture(GL_TEXTURE_2D, self->params.destination_textures[0]); + self->params.egl->glGetTexLevelParameteriv(GL_TEXTURE_2D, 0, GL_TEXTURE_WIDTH, &dest_texture_size.x); + self->params.egl->glGetTexLevelParameteriv(GL_TEXTURE_2D, 0, GL_TEXTURE_HEIGHT, &dest_texture_size.y); + self->params.egl->glBindTexture(GL_TEXTURE_2D, 0); 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); + const vec2f pos_norm = { + ((float)destination_pos.x / (dest_texture_size.x == 0 ? 1.0f : (float)dest_texture_size.x)) * 2.0f, + ((float)destination_pos.y / (dest_texture_size.y == 0 ? 1.0f : (float)dest_texture_size.y)) * 2.0f, + }; + + const vec2f size_norm = { + ((float)source_size.x / (dest_texture_size.x == 0 ? 1.0f : (float)dest_texture_size.x)) * 2.0f * scale.x, + ((float)source_size.y / (dest_texture_size.y == 0 ? 1.0f : (float)dest_texture_size.y)) * 2.0f * scale.y, + }; + + const vec2f texture_pos_norm = { + (float)source_position.x / (texture_size.x == 0 ? 1.0f : (float)texture_size.x), + (float)source_position.y / (texture_size.y == 0 ? 1.0f : (float)texture_size.y), + }; + + const vec2f texture_size_norm = { + (float)source_size.x / (texture_size.x == 0 ? 1.0f : (float)texture_size.x), + (float)source_size.y / (texture_size.y == 0 ? 1.0f : (float)texture_size.y), + }; + + const float vertices[] = { + -1.0f + 0.0f, -1.0f + 0.0f + size_norm.y, texture_pos_norm.x, texture_pos_norm.y + texture_size_norm.y, + -1.0f + 0.0f, -1.0f + 0.0f, texture_pos_norm.x, texture_pos_norm.y, + -1.0f + 0.0f + size_norm.x, -1.0f + 0.0f, texture_pos_norm.x + texture_size_norm.x, texture_pos_norm.y, + + -1.0f + 0.0f, -1.0f + 0.0f + size_norm.y, texture_pos_norm.x, texture_pos_norm.y + texture_size_norm.y, + -1.0f + 0.0f + size_norm.x, -1.0f + 0.0f, texture_pos_norm.x + texture_size_norm.x, texture_pos_norm.y, + -1.0f + 0.0f + size_norm.x, -1.0f + 0.0f + size_norm.y, texture_pos_norm.x + texture_size_norm.x, texture_pos_norm.y + texture_size_norm.y + }; + + self->params.egl->glBindVertexArray(self->vertex_array_object_id); + self->params.egl->glViewport(0, 0, dest_texture_size.x, dest_texture_size.y); + + /* TODO: this, also cleanup */ + //self->params.egl->glBindBuffer(GL_ARRAY_BUFFER, self->vertex_buffer_object_id); + self->params.egl->glBufferSubData(GL_ARRAY_BUFFER, 0, 24 * sizeof(float), vertices); + switch(self->params.destination_color) { case GSR_DESTINATION_COLOR_NV12: case GSR_DESTINATION_COLOR_P010: { - const bool use_16bit_colors = self->params.destination_color == GSR_DESTINATION_COLOR_P010; - const int shader_index_offset = external_texture ? 2 : 0; - - self->params.egl->glActiveTexture(GL_TEXTURE1); - self->params.egl->glBindTexture(GL_TEXTURE_2D, self->params.destination_textures[0]); - self->params.egl->glActiveTexture(GL_TEXTURE0); - - // Y - { - gsr_color_uniforms *uniform = &self->uniforms[shader_index_offset + 0]; - gsr_shader_use(&self->shaders[shader_index_offset + 0]); - self->params.egl->glUniformMatrix2fv(uniform->rotation_matrix, 1, GL_TRUE, (const float*)rotation_matrix); - self->params.egl->glUniform2i(uniform->source_position, source_position.x, source_position.y); - self->params.egl->glUniform2i(uniform->target_position, destination_pos.x, destination_pos.y); - self->params.egl->glUniform2f(uniform->scale, scale.x, scale.y); - self->params.egl->glBindImageTexture(0, self->params.destination_textures[0], 0, GL_FALSE, 0, GL_WRITE_ONLY, use_16bit_colors ? GL_R16 : GL_R8); - const double num_groups_x = ceil((double)texture_size.x/(double)self->max_local_size_dim); - const double num_groups_y = ceil((double)texture_size.y/(double)self->max_local_size_dim); - self->params.egl->glDispatchCompute(max_int(1, num_groups_x), max_int(1, num_groups_y), 1); - } - - self->params.egl->glActiveTexture(GL_TEXTURE1); - self->params.egl->glBindTexture(GL_TEXTURE_2D, self->params.destination_textures[1]); - self->params.egl->glActiveTexture(GL_TEXTURE0); - - // UV - { - gsr_color_uniforms *uniform = &self->uniforms[shader_index_offset + 1]; - gsr_shader_use(&self->shaders[shader_index_offset + 1]); - self->params.egl->glUniformMatrix2fv(uniform->rotation_matrix, 1, GL_TRUE, (const float*)rotation_matrix); - self->params.egl->glUniform2i(uniform->source_position, source_position.x, source_position.y); - self->params.egl->glUniform2i(uniform->target_position, destination_pos.x, destination_pos.y); - self->params.egl->glUniform2f(uniform->scale, scale.x, scale.y); - self->params.egl->glBindImageTexture(0, self->params.destination_textures[1], 0, GL_FALSE, 0, GL_WRITE_ONLY, use_16bit_colors ? GL_RG16 : GL_RG8); - const double num_groups_x = ceil((double)texture_size.x*0.5/(double)self->max_local_size_dim); - const double num_groups_y = ceil((double)texture_size.y*0.5/(double)self->max_local_size_dim); - self->params.egl->glDispatchCompute(max_int(1, num_groups_x), max_int(1, num_groups_y), 1); + 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) + + int shader_index = external_texture ? GRAPHICS_SHADER_INDEX_Y_EXTERNAL : GRAPHICS_SHADER_INDEX_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_UV_EXTERNAL : GRAPHICS_SHADER_INDEX_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: { - const int shader_index_offset = external_texture ? 1 : 0; - - self->params.egl->glActiveTexture(GL_TEXTURE1); - self->params.egl->glBindTexture(GL_TEXTURE_2D, self->params.destination_textures[0]); - self->params.egl->glActiveTexture(GL_TEXTURE0); - - gsr_color_uniforms *uniform = &self->uniforms[shader_index_offset + 4]; - gsr_shader_use(&self->shaders[shader_index_offset + 4]); - self->params.egl->glUniformMatrix2fv(uniform->rotation_matrix, 1, GL_TRUE, (const float*)rotation_matrix); - self->params.egl->glUniform2i(uniform->source_position, source_position.x, source_position.y); - self->params.egl->glUniform2i(uniform->target_position, destination_pos.x, destination_pos.y); - self->params.egl->glUniform2f(uniform->scale, scale.x, scale.y); - self->params.egl->glBindImageTexture(0, self->params.destination_textures[0], 0, GL_FALSE, 0, GL_WRITE_ONLY, GL_RGBA8); - const double num_groups_x = ceil((double)texture_size.x/(double)self->max_local_size_dim); - const double num_groups_y = ceil((double)texture_size.y/(double)self->max_local_size_dim); - self->params.egl->glDispatchCompute(max_int(1, num_groups_x), max_int(1, num_groups_y), 1); + 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_RGB_EXTERNAL : GRAPHICS_SHADER_INDEX_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; } } + self->params.egl->glBindVertexArray(0); + self->params.egl->glUseProgram(0); + gsr_color_conversion_swizzle_reset(self, source_color); + self->params.egl->glBindTexture(texture_target, 0); + self->params.egl->glBindFramebuffer(GL_FRAMEBUFFER, 0); +} + +void gsr_color_conversion_draw(gsr_color_conversion *self, unsigned int texture_id, vec2i destination_pos, vec2i destination_size, vec2i source_pos, vec2i source_size, vec2i texture_size, gsr_rotation rotation, gsr_source_color source_color, bool external_texture, bool alpha_blending) { + assert(!external_texture || self->params.load_external_image_shader); + if(external_texture && !self->params.load_external_image_shader) { + fprintf(stderr, "gsr error: gsr_color_conversion_draw: external texture not loaded\n"); + return; + } + + vec2f scale = {0.0f, 0.0f}; + if(source_size.x > 0 && source_size.y > 0) + scale = (vec2f){ (double)destination_size.x/(double)source_size.x, (double)destination_size.y/(double)source_size.y }; + + vec2i source_position = {0, 0}; + float rotation_matrix[2][2] = {{0, 0}, {0, 0}}; + gsr_color_conversion_apply_rotation(rotation, rotation_matrix); + + 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); + + const bool use_graphics_shader = external_texture ? self->external_compute_shaders_failed_to_load : self->compute_shaders_failed_to_load; + if(use_graphics_shader) { + source_position.x += source_pos.x; + source_position.y += source_pos.y; + gsr_color_conversion_draw_graphics(self, texture_id, external_texture, rotation_matrix, source_position, source_size, destination_pos, texture_size, scale, source_color); + // TODO: Is glFlush and glFinish needed here for graphics garbage? + } else { + switch(rotation) { + case GSR_ROT_0: + break; + case GSR_ROT_90: + source_position.x += (((double)texture_size.x*0.5 - (double)texture_size.y*0.5) * scale.x); + source_position.y += (((double)texture_size.y*0.5 - (double)texture_size.x*0.5) * scale.y); + break; + case GSR_ROT_180: + break; + case GSR_ROT_270: + source_position.x += (((double)texture_size.x*0.5 - (double)texture_size.y*0.5) * scale.x); + source_position.y += (((double)texture_size.y*0.5 - (double)texture_size.x*0.5) * scale.y); + break; + } + source_position.x -= (source_pos.x * scale.x + 0.5); + source_position.y -= (source_pos.y * scale.y + 0.5); + + switch(self->params.destination_color) { + case GSR_DESTINATION_COLOR_NV12: + case GSR_DESTINATION_COLOR_P010: { + const bool use_16bit_colors = self->params.destination_color == GSR_DESTINATION_COLOR_P010; + gsr_color_conversion_dispatch_compute_shader(self, external_texture, alpha_blending, rotation_matrix, source_position, destination_pos, destination_size, scale, use_16bit_colors, GSR_COLOR_COMP_Y); + gsr_color_conversion_dispatch_compute_shader(self, external_texture, alpha_blending, rotation_matrix, (vec2i){source_position.x/2, source_position.y/2}, + (vec2i){destination_pos.x/2, destination_pos.y/2}, (vec2i){destination_size.x/2, destination_size.y/2}, scale, use_16bit_colors, GSR_COLOR_COMP_UV); + break; + } + case GSR_DESTINATION_COLOR_RGB8: { + gsr_color_conversion_dispatch_compute_shader(self, external_texture, alpha_blending, rotation_matrix, source_position, destination_pos, destination_size, scale, false, GSR_COLOR_COMP_RGB); + break; + } + } + } + + // TODO: Use the minimal barrier required self->params.egl->glMemoryBarrier(GL_ALL_BARRIER_BITS); // GL_SHADER_IMAGE_ACCESS_BARRIER_BIT self->params.egl->glUseProgram(0); diff --git a/src/cursor.c b/src/cursor.c index 56b9694..40532f8 100644 --- a/src/cursor.c +++ b/src/cursor.c @@ -56,8 +56,10 @@ static bool gsr_cursor_set_from_x11_cursor_image(gsr_cursor *self, XFixesCursorI self->egl->glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, self->size.x, self->size.y, 0, GL_RGBA, GL_UNSIGNED_BYTE, cursor_data); free(cursor_data); - self->egl->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); - self->egl->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + const float border_color[4] = { 0.0f, 0.0f, 0.0f, 0.0f }; + self->egl->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_BORDER); + self->egl->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_BORDER); + self->egl->glTexParameterfv(GL_TEXTURE_2D, GL_TEXTURE_BORDER_COLOR, border_color); self->egl->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); self->egl->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); @@ -288,6 +288,7 @@ static bool gsr_egl_load_gl(gsr_egl *self, void *library) { { (void**)&self->glBindImageTexture, "glBindImageTexture" }, { (void**)&self->glTexParameteri, "glTexParameteri" }, { (void**)&self->glTexParameteriv, "glTexParameteriv" }, + { (void**)&self->glTexParameterfv, "glTexParameterfv" }, { (void**)&self->glGetTexLevelParameteriv, "glGetTexLevelParameteriv" }, { (void**)&self->glTexImage2D, "glTexImage2D" }, { (void**)&self->glTexSubImage2D, "glTexSubImage2D" }, diff --git a/src/encoder/video/vaapi.c b/src/encoder/video/vaapi.c index 1d5dae0..c7ccd26 100644 --- a/src/encoder/video/vaapi.c +++ b/src/encoder/video/vaapi.c @@ -92,6 +92,10 @@ static bool gsr_video_encoder_vaapi_setup_textures(gsr_video_encoder_vaapi *self if(self->prime.fourcc == VA_FOURCC_NV12 || self->prime.fourcc == VA_FOURCC_P010) { const uint32_t *formats = self->prime.fourcc == VA_FOURCC_NV12 ? formats_nv12 : formats_p010; const int div[2] = {1, 2}; // divide UV texture size by 2 because chroma is half size + const float border_colors[2][4] = { + {0.0f, 0.0f, 0.0f, 1.0f}, + {0.5f, 0.5f, 0.0f, 1.0f} + }; self->params.egl->glGenTextures(2, self->target_textures); for(int i = 0; i < 2; ++i) { @@ -121,8 +125,9 @@ static bool gsr_video_encoder_vaapi_setup_textures(gsr_video_encoder_vaapi *self } self->params.egl->glBindTexture(GL_TEXTURE_2D, self->target_textures[i]); - self->params.egl->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); - self->params.egl->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + self->params.egl->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_BORDER); + self->params.egl->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_BORDER); + self->params.egl->glTexParameterfv(GL_TEXTURE_2D, GL_TEXTURE_BORDER_COLOR, border_colors[i]); self->params.egl->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); self->params.egl->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); diff --git a/src/encoder/video/vulkan.c b/src/encoder/video/vulkan.c index 062967b..7210870 100644 --- a/src/encoder/video/vulkan.c +++ b/src/encoder/video/vulkan.c @@ -94,7 +94,7 @@ static bool gsr_video_encoder_vulkan_setup_textures(gsr_video_encoder_vulkan *se //AVVkFrame *target_surface_id = (AVVkFrame*)frame->data[0]; self->vv = video_codec_context_get_vulkan_data(video_codec_context); - const unsigned int internal_formats_nv12[2] = { GL_RGBA8, GL_RGBA8 }; + const unsigned int internal_formats_nv12[2] = { GL_RGBA8, GL_RGBA8 }; // TODO: GL_R8, GL_R16 const unsigned int internal_formats_p010[2] = { GL_R16, GL_RG16 }; const unsigned int formats[2] = { GL_RED, GL_RG }; const int div[2] = {1, 2}; // divide UV texture size by 2 because chroma is half size diff --git a/src/main.cpp b/src/main.cpp index 8b3bbd1..b5f95db 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -82,11 +82,14 @@ typedef struct { static void monitor_output_callback_print(const gsr_monitor *monitor, void *userdata) { const MonitorOutputCallbackUserdata *options = (MonitorOutputCallbackUserdata*)userdata; vec2i monitor_position = monitor->pos; + vec2i monitor_size = monitor->size; if(gsr_window_get_display_server(options->window) == GSR_DISPLAY_SERVER_WAYLAND) { gsr_monitor_rotation monitor_rotation = GSR_MONITOR_ROT_0; drm_monitor_get_display_server_data(options->window, monitor, &monitor_rotation, &monitor_position); + if(monitor_rotation == GSR_MONITOR_ROT_90 || monitor_rotation == GSR_MONITOR_ROT_270) + std::swap(monitor_size.x, monitor_size.y); } - fprintf(stderr, " \"%.*s\" (%dx%d+%d+%d)\n", monitor->name_len, monitor->name, monitor->size.x, monitor->size.y, monitor_position.x, monitor_position.y); + fprintf(stderr, " \"%.*s\" (%dx%d+%d+%d)\n", monitor->name_len, monitor->name, monitor_size.x, monitor_size.y, monitor_position.x, monitor_position.y); } typedef struct { @@ -110,9 +113,14 @@ typedef struct { static void get_monitor_by_position_callback(const gsr_monitor *monitor, void *userdata) { MonitorByPositionCallback *data = (MonitorByPositionCallback*)userdata; - gsr_monitor_rotation monitor_rotation = GSR_MONITOR_ROT_0; vec2i monitor_position = monitor->pos; - drm_monitor_get_display_server_data(data->window, monitor, &monitor_rotation, &monitor_position); + vec2i monitor_size = monitor->size; + if(gsr_window_get_display_server(data->window) == GSR_DISPLAY_SERVER_WAYLAND) { + gsr_monitor_rotation monitor_rotation = GSR_MONITOR_ROT_0; + drm_monitor_get_display_server_data(data->window, monitor, &monitor_rotation, &monitor_position); + if(monitor_rotation == GSR_MONITOR_ROT_90 || monitor_rotation == GSR_MONITOR_ROT_270) + std::swap(monitor_size.x, monitor_size.y); + } if(!data->output_name && data->position.x >= monitor_position.x && data->position.x <= monitor_position.x + monitor->size.x && data->position.y >= monitor_position.y && data->position.y <= monitor_position.y + monitor->size.y) @@ -173,6 +181,11 @@ enum class BitrateMode { CBR }; +enum class Tune { + PERFORMANCE, + QUALITY +}; + static int x11_error_handler(Display*, XErrorEvent*) { return 0; } @@ -915,7 +928,7 @@ static void video_hardware_set_qp(AVCodecContext *codec_context, VideoQuality vi av_dict_set_int(options, "qp", 30 * qp_multiply, 0); break; case VideoQuality::VERY_HIGH: - av_dict_set_int(options, "qp", 27 * qp_multiply, 0); + av_dict_set_int(options, "qp", 25 * qp_multiply, 0); break; case VideoQuality::ULTRA: av_dict_set_int(options, "qp", 22 * qp_multiply, 0); @@ -930,7 +943,7 @@ static void video_hardware_set_qp(AVCodecContext *codec_context, VideoQuality vi av_dict_set_int(options, "qp", 30 * qp_multiply, 0); break; case VideoQuality::VERY_HIGH: - av_dict_set_int(options, "qp", 27 * qp_multiply, 0); + av_dict_set_int(options, "qp", 25 * qp_multiply, 0); break; case VideoQuality::ULTRA: av_dict_set_int(options, "qp", 22 * qp_multiply, 0); @@ -945,7 +958,7 @@ static void video_hardware_set_qp(AVCodecContext *codec_context, VideoQuality vi av_dict_set_int(options, "qp", 30 * qp_multiply, 0); break; case VideoQuality::VERY_HIGH: - av_dict_set_int(options, "qp", 27 * qp_multiply, 0); + av_dict_set_int(options, "qp", 25 * qp_multiply, 0); break; case VideoQuality::ULTRA: av_dict_set_int(options, "qp", 22 * qp_multiply, 0); @@ -960,7 +973,7 @@ static void video_hardware_set_qp(AVCodecContext *codec_context, VideoQuality vi av_dict_set_int(options, "qp", 30 * qp_multiply, 0); break; case VideoQuality::VERY_HIGH: - av_dict_set_int(options, "qp", 27 * qp_multiply, 0); + av_dict_set_int(options, "qp", 25 * qp_multiply, 0); break; case VideoQuality::ULTRA: av_dict_set_int(options, "qp", 22 * qp_multiply, 0); @@ -979,7 +992,7 @@ static void video_hardware_set_qp(AVCodecContext *codec_context, VideoQuality vi av_dict_set_int(options, "qp", 30 * qp_multiply, 0); break; case VideoQuality::VERY_HIGH: - av_dict_set_int(options, "qp", 27 * qp_multiply, 0); + av_dict_set_int(options, "qp", 25 * qp_multiply, 0); break; case VideoQuality::ULTRA: av_dict_set_int(options, "qp", 22 * qp_multiply, 0); @@ -994,7 +1007,7 @@ static void video_hardware_set_qp(AVCodecContext *codec_context, VideoQuality vi av_dict_set_int(options, "qp", 30 * qp_multiply, 0); break; case VideoQuality::VERY_HIGH: - av_dict_set_int(options, "qp", 27 * qp_multiply, 0); + av_dict_set_int(options, "qp", 25 * qp_multiply, 0); break; case VideoQuality::ULTRA: av_dict_set_int(options, "qp", 22 * qp_multiply, 0); @@ -1009,7 +1022,7 @@ static void video_hardware_set_qp(AVCodecContext *codec_context, VideoQuality vi av_dict_set_int(options, "qp", 30 * qp_multiply, 0); break; case VideoQuality::VERY_HIGH: - av_dict_set_int(options, "qp", 27 * qp_multiply, 0); + av_dict_set_int(options, "qp", 25 * qp_multiply, 0); break; case VideoQuality::ULTRA: av_dict_set_int(options, "qp", 22 * qp_multiply, 0); @@ -1019,7 +1032,7 @@ static void video_hardware_set_qp(AVCodecContext *codec_context, VideoQuality vi } } -static void open_video_hardware(AVCodecContext *codec_context, VideoQuality video_quality, bool very_old_gpu, gsr_gpu_vendor vendor, PixelFormat pixel_format, bool hdr, gsr_color_depth color_depth, BitrateMode bitrate_mode, VideoCodec video_codec, bool low_power, bool high_performance_encoding) { +static void open_video_hardware(AVCodecContext *codec_context, VideoQuality video_quality, bool very_old_gpu, gsr_gpu_vendor vendor, PixelFormat pixel_format, bool hdr, gsr_color_depth color_depth, BitrateMode bitrate_mode, VideoCodec video_codec, bool low_power, Tune tune) { (void)very_old_gpu; AVDictionary *options = nullptr; @@ -1043,6 +1056,17 @@ static void open_video_hardware(AVCodecContext *codec_context, VideoQuality vide // } av_dict_set(&options, "tune", "hq", 0); + switch(tune) { + case Tune::PERFORMANCE: + //av_dict_set(&options, "multipass", "qres", 0); + break; + case Tune::QUALITY: + av_dict_set(&options, "multipass", "fullres", 0); + av_dict_set(&options, "preset", "p6", 0); + av_dict_set_int(&options, "rc-lookahead", 0, 0); + break; + } + dict_set_profile(codec_context, vendor, color_depth, &options); if(codec_context->codec_id == AV_CODEC_ID_H264) { @@ -1074,8 +1098,7 @@ static void open_video_hardware(AVCodecContext *codec_context, VideoQuality vide av_dict_set_int(&options, "low_power", 1, 0); // Improves performance but increases vram. // TODO: Might need a different async_depth for optimal performance on different amd/intel gpus - if(high_performance_encoding) - av_dict_set_int(&options, "async_depth", 3, 0); + av_dict_set_int(&options, "async_depth", 3, 0); if(codec_context->codec_id == AV_CODEC_ID_H264) { // Removed because it causes stutter in games for some people @@ -1106,7 +1129,7 @@ static void open_video_hardware(AVCodecContext *codec_context, VideoQuality vide static void usage_header() { const bool inside_flatpak = getenv("FLATPAK_ID") != NULL; const char *program_name = inside_flatpak ? "flatpak run --command=gpu-screen-recorder com.dec05eba.gpu_screen_recorder" : "gpu-screen-recorder"; - printf("usage: %s -w <window_id|monitor|focused|portal|region> [-c <container_format>] [-s WxH] [-region WxH+X+Y] [-f <fps>] [-a <audio_input>] [-q <quality>] [-r <replay_buffer_size_sec>] [-restart-replay-on-save yes|no] [-k h264|hevc|av1|vp8|vp9|hevc_hdr|av1_hdr|hevc_10bit|av1_10bit] [-ac aac|opus|flac] [-ab <bitrate>] [-oc yes|no] [-fm cfr|vfr|content] [-bm auto|qp|vbr|cbr] [-cr limited|full] [-df yes|no] [-sc <script_path>] [-cursor yes|no] [-keyint <value>] [-restore-portal-session yes|no] [-portal-session-token-filepath filepath] [-encoder gpu|cpu] [-high-performance-encoding yes|no] [-o <output_file>] [--list-capture-options [card_path] [vendor]] [--list-audio-devices] [--list-application-audio] [-v yes|no] [-gl-debug yes|no] [--version] [-h|--help]\n", program_name); + printf("usage: %s -w <window_id|monitor|focused|portal|region> [-c <container_format>] [-s WxH] [-region WxH+X+Y] [-f <fps>] [-a <audio_input>] [-q <quality>] [-r <replay_buffer_size_sec>] [-restart-replay-on-save yes|no] [-k h264|hevc|av1|vp8|vp9|hevc_hdr|av1_hdr|hevc_10bit|av1_10bit] [-ac aac|opus|flac] [-ab <bitrate>] [-oc yes|no] [-fm cfr|vfr|content] [-bm auto|qp|vbr|cbr] [-cr limited|full] [-tune performance|quality] [-df yes|no] [-sc <script_path>] [-cursor yes|no] [-keyint <value>] [-restore-portal-session yes|no] [-portal-session-token-filepath filepath] [-encoder gpu|cpu] [-o <output_file>] [--list-capture-options [card_path] [vendor]] [--list-audio-devices] [--list-application-audio] [-v yes|no] [-gl-debug yes|no] [--version] [-h|--help]\n", program_name); fflush(stdout); } @@ -1211,6 +1234,10 @@ static void usage_full() { printf(" Note that some buggy video players (such as vlc) are unable to correctly display videos in full color range and when upload the video to websites the website\n"); printf(" might re-encoder the video to make the video limited color range.\n"); printf("\n"); + printf(" -tune\n"); + printf(" Tune for performance or quality. Should be either 'performance' or 'quality'. At the moment this option only has an effect on Nvidia where setting this to quality\n"); + printf(" sets options such as preset, multipass and b frames. Optional, set to 'performance' by default.\n"); + printf("\n"); printf(" -df Organise replays in folders based on the current date.\n"); printf("\n"); printf(" -sc Run a script on the saved video file (asynchronously). The first argument to the script is the filepath to the saved video file and the second argument is the recording type (either \"regular\" or \"replay\").\n"); @@ -1240,10 +1267,6 @@ static void usage_full() { printf(" Which device should be used for video encoding. Should either be 'gpu' or 'cpu'. 'cpu' option currently only work with h264 codec option (-k).\n"); printf(" Optional, set to 'gpu' by default.\n"); printf("\n"); - printf(" -high-performance-encoding\n"); - printf(" Enable high performance video encoding mode. Only applicable to AMD and Intel. Optional, set to 'no' by default.\n"); - printf(" Note: this option is experimental. On some AMD GPUs this may cause the game you are recording to performance worse.\n"); - printf("\n"); printf(" --info\n"); printf(" List info about the system. Lists the following information (prints them to stdout and exits):\n"); printf(" Supported video codecs (h264, h264_software, hevc, hevc_hdr, hevc_10bit, av1, av1_hdr, av1_10bit, vp8, vp9) and image codecs (jpeg, png) (if supported).\n"); @@ -1258,7 +1281,7 @@ static void usage_full() { printf(" window\n"); printf(" DP-1|1920x1080\n"); printf(" The <option> and <monitor_name> is the name that can be passed to GPU Screen Recorder with the -w option.\n"); - printf(" --list-capture-options optionally accepts a card path (\"/dev/dri/cardN\") and vendor (\"amd\", \"intel\" or \"nvidia\") which can improve the performance of running this command.\n"); + printf(" --list-capture-options optionally accepts a card path (\"/dev/dri/cardN\") and vendor (\"amd\", \"intel\", \"nvidia\" or \"broadcom\") which can improve the performance of running this command.\n"); printf("\n"); printf(" --list-audio-devices\n"); printf(" List audio devices. Lists audio devices in the following format (prints them to stdout and exits):\n"); @@ -1293,7 +1316,7 @@ static void usage_full() { printf("NOTES:\n"); printf(" Send signal SIGINT to gpu-screen-recorder (Ctrl+C, or killall -SIGINT gpu-screen-recorder) to stop and save the recording. When in replay mode this stops recording without saving.\n"); printf(" Send signal SIGUSR1 to gpu-screen-recorder (killall -SIGUSR1 gpu-screen-recorder) to save a replay (when in replay mode).\n"); - printf(" Send signal SIGUSR2 to gpu-screen-recorder (killall -SIGUSR2 gpu-screen-recorder) to pause/unpause recording. Only applicable and useful when recording (not streaming nor replay).\n"); + printf(" Send signal SIGUSR2 to gpu-screen-recorder (killall -SIGUSR2 gpu-screen-recorder) to pause/unpause recording. Only applicable when recording (not streaming nor replay).\n"); printf("\n"); printf("EXAMPLES:\n"); printf(" %s -w screen -f 60 -a default_output -o video.mp4\n", program_name); @@ -1305,7 +1328,7 @@ static void usage_full() { printf(" %s -w screen -f 60 -a default_output -bm cbr -q 15000 -o video.mp4\n", program_name); printf(" %s -w screen -f 60 -a \"app:firefox|app:csgo\" -o video.mp4\n", program_name); printf(" %s -w screen -f 60 -a \"app-inverse:firefox|app-inverse:csgo\" -o video.mp4\n", program_name); - printf(" %s -w screen -f 60 -a \"default-input|app-inverse:Brave\" -o video.mp4\n", program_name); + printf(" %s -w screen -f 60 -a \"default_input|app-inverse:Brave\" -o video.mp4\n", program_name); printf(" %s -w screen -o image.jpg\n", program_name); printf(" %s -w screen -q medium -o image.jpg\n", program_name); printf(" %s -w region -region 640x480+100+100 -o video.mp4\n", program_name); @@ -3371,12 +3394,12 @@ int main(int argc, char **argv) { { "-df", Arg { {}, is_optional, !is_list, ArgType::BOOLEAN, {false} } }, { "-sc", Arg { {}, is_optional, !is_list, ArgType::STRING, {false} } }, { "-cr", Arg { {}, is_optional, !is_list, ArgType::STRING, {false} } }, + { "-tune", Arg { {}, is_optional, !is_list, ArgType::STRING, {false} } }, { "-cursor", Arg { {}, is_optional, !is_list, ArgType::BOOLEAN, {false} } }, { "-keyint", Arg { {}, is_optional, !is_list, ArgType::STRING, {false} } }, { "-restore-portal-session", Arg { {}, is_optional, !is_list, ArgType::BOOLEAN, {false} } }, { "-portal-session-token-filepath", Arg { {}, is_optional, !is_list, ArgType::STRING, {false} } }, { "-encoder", Arg { {}, is_optional, !is_list, ArgType::STRING, {false} } }, - { "-high-performance-encoding", Arg { {}, is_optional, !is_list, ArgType::BOOLEAN, {false} } }, }; for(int i = 1; i < argc; i += 2) { @@ -3447,7 +3470,7 @@ int main(int argc, char **argv) { //} else if(strcmp(video_codec_to_use, "hevc_vulkan") == 0) { // video_codec = VideoCodec::HEVC_VULKAN; } else if(strcmp(video_codec_to_use, "auto") != 0) { - fprintf(stderr, "Error: -k should either be either 'auto', 'h264', 'hevc', 'av1', 'vp8', 'vp9', 'hevc_hdr', 'av1_hdr', 'hevc_10bit' or 'av1_10bit', got: '%s'\n", video_codec_to_use); + fprintf(stderr, "Error: -k should either be 'auto', 'h264', 'hevc', 'av1', 'vp8', 'vp9', 'hevc_hdr', 'av1_hdr', 'hevc_10bit' or 'av1_10bit', got: '%s'\n", video_codec_to_use); usage(); } @@ -3463,7 +3486,7 @@ int main(int argc, char **argv) { } else if(strcmp(audio_codec_to_use, "flac") == 0) { audio_codec = AudioCodec::FLAC; } else { - fprintf(stderr, "Error: -ac should either be either 'aac', 'opus' or 'flac', got: '%s'\n", audio_codec_to_use); + fprintf(stderr, "Error: -ac should either be 'aac', 'opus' or 'flac', got: '%s'\n", audio_codec_to_use); usage(); } @@ -3528,7 +3551,6 @@ int main(int argc, char **argv) { const bool date_folders = arg_get_boolean_value(args, "-df", false); const bool restore_portal_session = arg_get_boolean_value(args, "-restore-portal-session", false); const bool restart_replay_on_save = arg_get_boolean_value(args, "-restart-replay-on-save", false); - const bool high_performance_encoding = arg_get_boolean_value(args, "-high-performance-encoding", false); const char *portal_session_token_filepath = args["-portal-session-token-filepath"].value(); if(portal_session_token_filepath) { @@ -3563,7 +3585,7 @@ int main(int argc, char **argv) { } else if(strcmp(pixfmt, "yuv444") == 0) { pixel_format = PixelFormat::YUV444; } else { - fprintf(stderr, "Error: -pixfmt should either be either 'yuv420', or 'yuv444', got: '%s'\n", pixfmt); + fprintf(stderr, "Error: -pixfmt should either be 'yuv420', or 'yuv444', got: '%s'\n", pixfmt); usage(); } @@ -3684,6 +3706,11 @@ int main(int argc, char **argv) { _exit(1); } + gsr_shader_enable_debug_output(gl_debug); +#ifndef NDEBUG + gsr_shader_enable_debug_output(true); +#endif + if(egl.gpu_info.is_steam_deck) { fprintf(stderr, "gsr warning: steam deck has multiple driver issues. One of them has been reported here: https://github.com/ValveSoftware/SteamOS/issues/1609\n" "If you have issues with GPU Screen Recorder on steam deck that you don't have on a desktop computer then report the issue to Valve and/or AMD.\n"); @@ -3735,7 +3762,7 @@ int main(int argc, char **argv) { } else if(strcmp(framerate_mode_str, "content") == 0) { framerate_mode = FramerateMode::CONTENT; } else { - fprintf(stderr, "Error: -fm should either be either 'cfr', 'vfr' or 'content', got: '%s'\n", framerate_mode_str); + fprintf(stderr, "Error: -fm should either be 'cfr', 'vfr' or 'content', got: '%s'\n", framerate_mode_str); usage(); } @@ -3756,7 +3783,7 @@ int main(int argc, char **argv) { } else if(strcmp(bitrate_mode_str, "cbr") == 0) { bitrate_mode = BitrateMode::CBR; } else if(strcmp(bitrate_mode_str, "auto") != 0) { - fprintf(stderr, "Error: -bm should either be either 'auto', 'qp', 'vbr' or 'cbr', got: '%s'\n", bitrate_mode_str); + fprintf(stderr, "Error: -bm should either be 'auto', 'qp', 'vbr' or 'cbr', got: '%s'\n", bitrate_mode_str); usage(); } @@ -3809,7 +3836,7 @@ int main(int argc, char **argv) { } else if(strcmp(quality_str, "ultra") == 0) { quality = VideoQuality::ULTRA; } else { - fprintf(stderr, "Error: -q should either be either 'medium', 'high', 'very_high' or 'ultra', got: '%s'\n", quality_str); + fprintf(stderr, "Error: -q should either be 'medium', 'high', 'very_high' or 'ultra', got: '%s'\n", quality_str); usage(); } } @@ -3824,7 +3851,21 @@ int main(int argc, char **argv) { } else if(strcmp(color_range_str, "full") == 0) { color_range = GSR_COLOR_RANGE_FULL; } else { - fprintf(stderr, "Error: -cr should either be either 'limited' or 'full', got: '%s'\n", color_range_str); + fprintf(stderr, "Error: -cr should either be 'limited' or 'full', got: '%s'\n", color_range_str); + usage(); + } + + Tune tune = Tune::PERFORMANCE; + const char *tune_str = args["-tune"].value(); + if(!tune_str) + tune_str = "performance"; + + if(strcmp(tune_str, "performance") == 0) { + tune = Tune::PERFORMANCE; + } else if(strcmp(tune_str, "quality") == 0) { + tune = Tune::QUALITY; + } else { + fprintf(stderr, "Error: -tune should either be 'performance' or 'quality', got: '%s'\n", tune_str); usage(); } @@ -3842,7 +3883,7 @@ int main(int argc, char **argv) { } if(output_resolution.x < 0 || output_resolution.y < 0) { - fprintf(stderr, "Error: invalud value for option -s '%s', expected width and height to be greater or equal to 0\n", output_resolution_str); + fprintf(stderr, "Error: invalid value for option -s '%s', expected width and height to be greater or equal to 0\n", output_resolution_str); usage(); } } @@ -3862,7 +3903,7 @@ int main(int argc, char **argv) { } if(region_size.x < 0 || region_size.y < 0 || region_position.x < 0 || region_position.y < 0) { - fprintf(stderr, "Error: invalud value for option -region '%s', expected width, height, x and y to be greater or equal to 0\n", region_str); + fprintf(stderr, "Error: invalid value for option -region '%s', expected width, height, x and y to be greater or equal to 0\n", region_str); usage(); } } else { @@ -3989,6 +4030,9 @@ int main(int argc, char **argv) { if(replay_buffer_size_secs == -1) video_stream = create_stream(av_format_context, video_codec_context); + if(tune == Tune::QUALITY) + video_codec_context->max_b_frames = 2; + AVFrame *video_frame = av_frame_alloc(); if(!video_frame) { fprintf(stderr, "Error: Failed to allocate video frame\n"); @@ -4053,7 +4097,7 @@ int main(int argc, char **argv) { if(use_software_video_encoder) { open_video_software(video_codec_context, quality, pixel_format, hdr, color_depth, bitrate_mode); } else { - open_video_hardware(video_codec_context, quality, very_old_gpu, egl.gpu_info.vendor, pixel_format, hdr, color_depth, bitrate_mode, video_codec, low_power, high_performance_encoding); + open_video_hardware(video_codec_context, quality, very_old_gpu, egl.gpu_info.vendor, pixel_format, hdr, color_depth, bitrate_mode, video_codec, low_power, tune); } if(video_stream) avcodec_parameters_from_context(video_stream->codecpar, video_codec_context); diff --git a/src/pipewire_video.c b/src/pipewire_video.c index 3f7b2df..bbc3f5d 100644 --- a/src/pipewire_video.c +++ b/src/pipewire_video.c @@ -736,11 +736,13 @@ static void gsr_pipewire_video_update_cursor_texture(gsr_pipewire_video *self, g if(!self->cursor.data) return; + const float border_color[4] = { 0.0f, 0.0f, 0.0f, 0.0f }; self->egl->glBindTexture(GL_TEXTURE_2D, texture_map.cursor_texture_id); // TODO: glTextureSubImage2D if same size self->egl->glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, self->cursor.width, self->cursor.height, 0, GL_RGBA, GL_UNSIGNED_BYTE, self->cursor.data); - self->egl->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); - self->egl->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + self->egl->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_BORDER); + self->egl->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_BORDER); + self->egl->glTexParameterfv(GL_TEXTURE_2D, GL_TEXTURE_BORDER_COLOR, border_color); self->egl->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); self->egl->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); self->egl->glBindTexture(GL_TEXTURE_2D, 0); diff --git a/src/shader.c b/src/shader.c index b9fbb62..9b6de6b 100644 --- a/src/shader.c +++ b/src/shader.c @@ -3,14 +3,16 @@ #include <stdio.h> #include <assert.h> +static bool print_compile_errors = false; + static int min_int(int a, int b) { return a < b ? a : b; } -static unsigned int loader_shader(gsr_egl *egl, unsigned int type, const char *source) { +static unsigned int load_shader(gsr_egl *egl, unsigned int type, const char *source) { unsigned int shader_id = egl->glCreateShader(type); if(shader_id == 0) { - fprintf(stderr, "gsr error: loader_shader: failed to create shader, error: %d\n", egl->glGetError()); + fprintf(stderr, "gsr error: load_shader: failed to create shader, error: %d\n", egl->glGetError()); return 0; } @@ -23,7 +25,7 @@ static unsigned int loader_shader(gsr_egl *egl, unsigned int type, const char *s int info_length = 0; egl->glGetShaderiv(shader_id, GL_INFO_LOG_LENGTH, &info_length); - if(info_length > 1) { + if(info_length > 1 && print_compile_errors) { char info_log[4096]; egl->glGetShaderInfoLog(shader_id, min_int(4096, info_length), NULL, info_log); fprintf(stderr, "gsr error: loader shader: failed to compile shader, error:\n%s\nshader source:\n%s\n", info_log, source); @@ -45,19 +47,19 @@ static unsigned int load_program(gsr_egl *egl, const char *vertex_shader, const bool success = false; if(vertex_shader) { - vertex_shader_id = loader_shader(egl, GL_VERTEX_SHADER, vertex_shader); + vertex_shader_id = load_shader(egl, GL_VERTEX_SHADER, vertex_shader); if(vertex_shader_id == 0) goto done; } if(fragment_shader) { - fragment_shader_id = loader_shader(egl, GL_FRAGMENT_SHADER, fragment_shader); + fragment_shader_id = load_shader(egl, GL_FRAGMENT_SHADER, fragment_shader); if(fragment_shader_id == 0) goto done; } if(compute_shader) { - compute_shader_id = loader_shader(egl, GL_COMPUTE_SHADER, compute_shader); + compute_shader_id = load_shader(egl, GL_COMPUTE_SHADER, compute_shader); if(compute_shader_id == 0) goto done; } @@ -151,3 +153,7 @@ void gsr_shader_use(gsr_shader *self) { void gsr_shader_use_none(gsr_shader *self) { self->egl->glUseProgram(0); } + +void gsr_shader_enable_debug_output(bool enable) { + print_compile_errors = enable; +} diff --git a/src/utils.c b/src/utils.c index 943fb2d..4aab138 100644 --- a/src/utils.c +++ b/src/utils.c @@ -427,14 +427,6 @@ bool gl_get_gpu_info(gsr_egl *egl, gsr_gpu_info *info) { return supported; } -bool version_greater_than(int major, int minor, int patch, int other_major, int other_minor, int other_patch) { - return (major > other_major) || (major == other_major && minor > other_minor) || (major == other_major && minor == other_minor && patch > other_patch); -} - -bool gl_driver_version_greater_than(const gsr_gpu_info *gpu_info, int major, int minor, int patch) { - return version_greater_than(gpu_info->driver_major, gpu_info->driver_minor, gpu_info->driver_patch, major, minor, patch); -} - bool try_card_has_valid_plane(const char *card_path) { drmVersion *ver = NULL; drmModePlaneResPtr planes = NULL; @@ -679,13 +671,22 @@ vec2i scale_keep_aspect_ratio(vec2i from, vec2i to) { } unsigned int gl_create_texture(gsr_egl *egl, int width, int height, int internal_format, unsigned int format, int filter) { + float border_color[4] = { 0.0f, 0.0f, 0.0f, 1.0f }; + if(format == GL_RG) { // UV + border_color[0] = 0.5f; + border_color[1] = 0.5f; + border_color[2] = 0.0f; + border_color[3] = 1.0f; + } + unsigned int texture_id = 0; egl->glGenTextures(1, &texture_id); egl->glBindTexture(GL_TEXTURE_2D, texture_id); egl->glTexImage2D(GL_TEXTURE_2D, 0, internal_format, width, height, 0, format, GL_UNSIGNED_BYTE, NULL); - egl->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); - egl->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + egl->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_BORDER); + egl->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_BORDER); + egl->glTexParameterfv(GL_TEXTURE_2D, GL_TEXTURE_BORDER_COLOR, border_color); egl->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, filter); egl->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, filter); diff --git a/src/window/wayland.c b/src/window/wayland.c index ba7b547..7e319fb 100644 --- a/src/window/wayland.c +++ b/src/window/wayland.c @@ -9,19 +9,23 @@ #include <stdint.h> #include <wayland-client.h> #include <wayland-egl.h> +#include "xdg-output-unstable-v1-client-protocol.h" #define GSR_MAX_OUTPUTS 32 +typedef struct gsr_window_wayland gsr_window_wayland; + typedef struct { uint32_t wl_name; - void *output; + struct wl_output *output; + struct zxdg_output_v1 *xdg_output; vec2i pos; vec2i size; int32_t transform; char *name; } gsr_wayland_output; -typedef struct { +struct gsr_window_wayland { void *display; void *window; void *registry; @@ -29,7 +33,8 @@ typedef struct { void *compositor; gsr_wayland_output outputs[GSR_MAX_OUTPUTS]; int num_outputs; -} gsr_window_wayland; + struct zxdg_output_manager_v1 *xdg_output_manager; +}; static void output_handle_geometry(void *data, struct wl_output *wl_output, int32_t x, int32_t y, int32_t phys_width, int32_t phys_height, @@ -95,7 +100,7 @@ static const struct wl_output_listener output_listener = { static void registry_add_object(void *data, struct wl_registry *registry, uint32_t name, const char *interface, uint32_t version) { (void)version; gsr_window_wayland *window_wayland = data; - if (strcmp(interface, "wl_compositor") == 0) { + if(strcmp(interface, "wl_compositor") == 0) { if(window_wayland->compositor) { wl_compositor_destroy(window_wayland->compositor); window_wayland->compositor = NULL; @@ -103,7 +108,7 @@ static void registry_add_object(void *data, struct wl_registry *registry, uint32 window_wayland->compositor = wl_registry_bind(registry, name, &wl_compositor_interface, 1); } else if(strcmp(interface, wl_output_interface.name) == 0) { if(version < 4) { - fprintf(stderr, "gsr warning: wl output interface version is < 4, expected >= 4 to capture a monitor. Using KMS capture instead\n"); + fprintf(stderr, "gsr warning: wl output interface version is < 4, expected >= 4 to capture a monitor\n"); return; } @@ -123,6 +128,17 @@ static void registry_add_object(void *data, struct wl_registry *registry, uint32 .name = NULL, }; wl_output_add_listener(gsr_output->output, &output_listener, gsr_output); + } else if(strcmp(interface, zxdg_output_manager_v1_interface.name) == 0) { + if(version < 1) { + fprintf(stderr, "gsr warning: xdg output interface version is < 1, expected >= 1 to capture a monitor\n"); + return; + } + + if(window_wayland->xdg_output_manager) { + zxdg_output_manager_v1_destroy(window_wayland->xdg_output_manager); + window_wayland->xdg_output_manager = NULL; + } + window_wayland->xdg_output_manager = wl_registry_bind(registry, name, &zxdg_output_manager_v1_interface, 1); } } @@ -130,6 +146,7 @@ static void registry_remove_object(void *data, struct wl_registry *registry, uin (void)data; (void)registry; (void)name; + // TODO: Remove output } static struct wl_registry_listener registry_listener = { @@ -137,6 +154,60 @@ static struct wl_registry_listener registry_listener = { .global_remove = registry_remove_object, }; +static void xdg_output_logical_position(void *data, struct zxdg_output_v1 *zxdg_output_v1, int32_t x, int32_t y) { + (void)zxdg_output_v1; + gsr_wayland_output *gsr_xdg_output = data; + gsr_xdg_output->pos.x = x; + gsr_xdg_output->pos.y = y; +} + +static void xdg_output_handle_logical_size(void *data, struct zxdg_output_v1 *xdg_output, int32_t width, int32_t height) { + (void)data; + (void)xdg_output; + (void)width; + (void)height; +} + +static void xdg_output_handle_done(void *data, struct zxdg_output_v1 *xdg_output) { + (void)data; + (void)xdg_output; +} + +static void xdg_output_handle_name(void *data, struct zxdg_output_v1 *xdg_output, const char *name) { + (void)data; + (void)xdg_output; + (void)name; +} + +static void xdg_output_handle_description(void *data, struct zxdg_output_v1 *xdg_output, const char *description) { + (void)data; + (void)xdg_output; + (void)description; +} + +static const struct zxdg_output_v1_listener xdg_output_listener = { + .logical_position = xdg_output_logical_position, + .logical_size = xdg_output_handle_logical_size, + .done = xdg_output_handle_done, + .name = xdg_output_handle_name, + .description = xdg_output_handle_description, +}; + +static void gsr_window_wayland_set_monitor_outputs_from_xdg_output(gsr_window_wayland *self) { + if(!self->xdg_output_manager) { + fprintf(stderr, "gsr warning: zxdg_output_manager not found. registered monitor positions might be incorrect\n"); + return; + } + + for(int i = 0; i < self->num_outputs; ++i) { + self->outputs[i].xdg_output = zxdg_output_manager_v1_get_xdg_output(self->xdg_output_manager, self->outputs[i].output); + zxdg_output_v1_add_listener(self->outputs[i].xdg_output, &xdg_output_listener, &self->outputs[i]); + } + + // Fetch xdg_output + wl_display_roundtrip(self->display); +} + static void gsr_window_wayland_deinit(gsr_window_wayland *self) { if(self->window) { wl_egl_window_destroy(self->window); @@ -158,9 +229,19 @@ static void gsr_window_wayland_deinit(gsr_window_wayland *self) { free(self->outputs[i].name); self->outputs[i].name = NULL; } + + if(self->outputs[i].xdg_output) { + zxdg_output_v1_destroy(self->outputs[i].xdg_output); + self->outputs[i].output = NULL; + } } self->num_outputs = 0; + if(self->xdg_output_manager) { + zxdg_output_manager_v1_destroy(self->xdg_output_manager); + self->xdg_output_manager = NULL; + } + if(self->compositor) { wl_compositor_destroy(self->compositor); self->compositor = NULL; @@ -193,6 +274,8 @@ static bool gsr_window_wayland_init(gsr_window_wayland *self) { // Fetch wl_output wl_display_roundtrip(self->display); + gsr_window_wayland_set_monitor_outputs_from_xdg_output(self); + if(!self->compositor) { fprintf(stderr, "gsr error: gsr_window_wayland_init failed: failed to find compositor\n"); goto fail; diff --git a/src/window_texture.c b/src/window_texture.c index 8eef4c9..4846bdc 100644 --- a/src/window_texture.c +++ b/src/window_texture.c @@ -85,8 +85,10 @@ int window_texture_on_resize(WindowTexture *self) { texture_id = self->texture_id; } - self->egl->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); - self->egl->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + const float border_color[4] = { 0.0f, 0.0f, 0.0f, 0.0f }; + self->egl->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_BORDER); + self->egl->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_BORDER); + self->egl->glTexParameterfv(GL_TEXTURE_2D, GL_TEXTURE_BORDER_COLOR, border_color); self->egl->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); self->egl->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); |