From b8843395acd03520b7630b6a1dc19f151f42382d Mon Sep 17 00:00:00 2001 From: dec05eba Date: Sat, 10 Feb 2024 02:47:01 +0100 Subject: Add screen rotation support (tested on amd/intel) screen rotation in wayland is best effort, wayland doesn't give the necessary information to make it robust. --- src/capture/kms_cuda.c | 64 +++++++++++++++++++-- src/capture/kms_vaapi.c | 67 ++++++++++++++++++++-- src/color_conversion.c | 75 +++++++++++++++---------- src/egl.c | 4 +- src/main.cpp | 1 + src/utils.c | 146 ++++++++++++++++++++++++++++++++++++++++++++---- 6 files changed, 304 insertions(+), 53 deletions(-) (limited to 'src') diff --git a/src/capture/kms_cuda.c b/src/capture/kms_cuda.c index f5b96a8..f9a6a11 100644 --- a/src/capture/kms_cuda.c +++ b/src/capture/kms_cuda.c @@ -50,6 +50,8 @@ typedef struct { unsigned int target_texture; gsr_color_conversion color_conversion; + + gsr_monitor_rotation monitor_rotation; } gsr_capture_kms_cuda; static int max_int(int a, int b) { @@ -162,8 +164,16 @@ static int gsr_capture_kms_cuda_start(gsr_capture *cap, AVCodecContext *video_co return -1; } + monitor.name = cap_kms->params.display_to_capture; + cap_kms->monitor_rotation = drm_monitor_get_display_server_rotation(cap_kms->params.egl, &monitor); + cap_kms->capture_pos = monitor.pos; - cap_kms->capture_size = monitor.size; + if(cap_kms->monitor_rotation == GSR_MONITOR_ROT_90 || cap_kms->monitor_rotation == GSR_MONITOR_ROT_270) { + cap_kms->capture_size.x = monitor.size.y; + cap_kms->capture_size.y = monitor.size.x; + } else { + cap_kms->capture_size = monitor.size; + } video_codec_context->width = max_int(2, cap_kms->capture_size.x & ~1); video_codec_context->height = max_int(2, cap_kms->capture_size.y & ~1); @@ -316,6 +326,16 @@ static bool gsr_capture_kms_cuda_should_stop(gsr_capture *cap, bool *err) { return false; } +static float monitor_rotation_to_radians(gsr_monitor_rotation rot) { + switch(rot) { + case GSR_MONITOR_ROT_0: return 0.0f; + case GSR_MONITOR_ROT_90: return M_PI_2; + case GSR_MONITOR_ROT_180: return M_PI; + case GSR_MONITOR_ROT_270: return M_PI + M_PI_2; + } + return 0.0f; +} + /* Prefer non combined planes */ static gsr_kms_response_fd* find_drm_by_connector_id(gsr_kms_response *kms_response, uint32_t connector_id) { int index_combined = -1; @@ -381,6 +401,13 @@ static gsr_kms_response_fd* find_cursor_drm(gsr_kms_response *kms_response) { return NULL; } +static vec2i swap_vec2i(vec2i value) { + int tmp = value.x; + value.x = value.y; + value.y = tmp; + return value; +} + static int gsr_capture_kms_cuda_capture(gsr_capture *cap, AVFrame *frame) { (void)frame; gsr_capture_kms_cuda *cap_kms = cap->priv; @@ -457,12 +484,40 @@ static int gsr_capture_kms_cuda_capture(gsr_capture *cap, AVFrame *frame) { if(!capture_is_combined_plane) capture_pos = (vec2i){drm_fd->x, drm_fd->y}; + const float texture_rotation = monitor_rotation_to_radians(cap_kms->monitor_rotation); + gsr_color_conversion_draw(&cap_kms->color_conversion, cap_kms->input_texture, (vec2i){0, 0}, cap_kms->capture_size, capture_pos, cap_kms->capture_size, - 0.0f, false); + texture_rotation, false); if(cursor_drm_fd) { + const vec2i cursor_size = {cursor_drm_fd->width, cursor_drm_fd->height}; + vec2i cursor_pos = {cursor_drm_fd->x, cursor_drm_fd->y}; + switch(cap_kms->monitor_rotation) { + case GSR_MONITOR_ROT_0: + break; + case GSR_MONITOR_ROT_90: + cursor_pos = swap_vec2i(cursor_pos); + cursor_pos.x = cap_kms->capture_size.x - cursor_pos.x; + // TODO: Remove this horrible hack + cursor_pos.x -= cursor_size.x; + break; + case GSR_MONITOR_ROT_180: + cursor_pos.x = cap_kms->capture_size.x - cursor_pos.x; + cursor_pos.y = cap_kms->capture_size.y - cursor_pos.y; + // TODO: Remove this horrible hack + cursor_pos.x -= cursor_size.x; + cursor_pos.y -= cursor_size.y; + break; + case GSR_MONITOR_ROT_270: + cursor_pos = swap_vec2i(cursor_pos); + cursor_pos.y = cap_kms->capture_size.y - cursor_pos.y; + // TODO: Remove this horrible hack + cursor_pos.y -= cursor_size.y; + break; + } + const intptr_t img_attr_cursor[] = { EGL_LINUX_DRM_FOURCC_EXT, cursor_drm_fd->pixel_format, EGL_WIDTH, cursor_drm_fd->width, @@ -481,11 +536,10 @@ static int gsr_capture_kms_cuda_capture(gsr_capture *cap, AVFrame *frame) { cap_kms->params.egl->eglDestroyImage(cap_kms->params.egl->egl_display, cursor_image); cap_kms->params.egl->glBindTexture(GL_TEXTURE_EXTERNAL_OES, 0); - vec2i cursor_size = {cursor_drm_fd->width, cursor_drm_fd->height}; gsr_color_conversion_draw(&cap_kms->color_conversion, cap_kms->cursor_texture, - (vec2i){cursor_drm_fd->x, cursor_drm_fd->y}, cursor_size, + cursor_pos, cursor_size, (vec2i){0, 0}, cursor_size, - 0.0f, true); + texture_rotation, true); } cap_kms->params.egl->eglSwapBuffers(cap_kms->params.egl->egl_display, cap_kms->params.egl->egl_surface); diff --git a/src/capture/kms_vaapi.c b/src/capture/kms_vaapi.c index 28527cf..d86389c 100644 --- a/src/capture/kms_vaapi.c +++ b/src/capture/kms_vaapi.c @@ -46,6 +46,8 @@ typedef struct { AVCodecContext *video_codec_context; AVMasteringDisplayMetadata *mastering_display_metadata; AVContentLightMetadata *light_metadata; + + gsr_monitor_rotation monitor_rotation; } gsr_capture_kms_vaapi; static int max_int(int a, int b) { @@ -156,8 +158,16 @@ static int gsr_capture_kms_vaapi_start(gsr_capture *cap, AVCodecContext *video_c return -1; } + monitor.name = cap_kms->params.display_to_capture; + cap_kms->monitor_rotation = drm_monitor_get_display_server_rotation(cap_kms->params.egl, &monitor); + cap_kms->capture_pos = monitor.pos; - cap_kms->capture_size = monitor.size; + if(cap_kms->monitor_rotation == GSR_MONITOR_ROT_90 || cap_kms->monitor_rotation == GSR_MONITOR_ROT_270) { + cap_kms->capture_size.x = monitor.size.y; + cap_kms->capture_size.y = monitor.size.x; + } else { + cap_kms->capture_size = monitor.size; + } /* Disable vsync */ cap_kms->params.egl->eglSwapInterval(cap_kms->params.egl->egl_display, 0); @@ -338,6 +348,16 @@ static bool gsr_capture_kms_vaapi_should_stop(gsr_capture *cap, bool *err) { return false; } +static float monitor_rotation_to_radians(gsr_monitor_rotation rot) { + switch(rot) { + case GSR_MONITOR_ROT_0: return 0.0f; + case GSR_MONITOR_ROT_90: return M_PI_2; + case GSR_MONITOR_ROT_180: return M_PI; + case GSR_MONITOR_ROT_270: return M_PI + M_PI_2; + } + return 0.0f; +} + /* Prefer non combined planes */ static gsr_kms_response_fd* find_drm_by_connector_id(gsr_kms_response *kms_response, uint32_t connector_id) { int index_combined = -1; @@ -426,6 +446,13 @@ static void gsr_capture_kms_vaapi_set_hdr_metadata(gsr_capture_kms_vaapi *cap_km } } +static vec2i swap_vec2i(vec2i value) { + int tmp = value.x; + value.x = value.y; + value.y = tmp; + return value; +} + static int gsr_capture_kms_vaapi_capture(gsr_capture *cap, AVFrame *frame) { gsr_capture_kms_vaapi *cap_kms = cap->priv; @@ -518,12 +545,15 @@ static int gsr_capture_kms_vaapi_capture(gsr_capture *cap, AVFrame *frame) { cap_kms->params.egl->eglDestroyImage(cap_kms->params.egl->egl_display, image); cap_kms->params.egl->glBindTexture(GL_TEXTURE_2D, 0); - vec2i capture_pos = cap_kms->capture_pos; + // TODO: Test rotation with multiple monitors, different rotation setups + // TODO: Make rotation work on wayland + // TODO: Apply these changes to kms cuda too, and test that + vec2i capture_pos = cap_kms->capture_pos; if(!capture_is_combined_plane) capture_pos = (vec2i){drm_fd->x, drm_fd->y}; - float texture_rotation = 0.0f; + const float texture_rotation = monitor_rotation_to_radians(cap_kms->monitor_rotation); gsr_color_conversion_draw(&cap_kms->color_conversion, cap_kms->input_texture, (vec2i){0, 0}, cap_kms->capture_size, @@ -531,6 +561,32 @@ static int gsr_capture_kms_vaapi_capture(gsr_capture *cap, AVFrame *frame) { texture_rotation, false); if(cursor_drm_fd) { + const vec2i cursor_size = {cursor_drm_fd->width, cursor_drm_fd->height}; + vec2i cursor_pos = {cursor_drm_fd->x, cursor_drm_fd->y}; + switch(cap_kms->monitor_rotation) { + case GSR_MONITOR_ROT_0: + break; + case GSR_MONITOR_ROT_90: + cursor_pos = swap_vec2i(cursor_pos); + cursor_pos.x = cap_kms->capture_size.x - cursor_pos.x; + // TODO: Remove this horrible hack + cursor_pos.x -= cursor_size.x; + break; + case GSR_MONITOR_ROT_180: + cursor_pos.x = cap_kms->capture_size.x - cursor_pos.x; + cursor_pos.y = cap_kms->capture_size.y - cursor_pos.y; + // TODO: Remove this horrible hack + cursor_pos.x -= cursor_size.x; + cursor_pos.y -= cursor_size.y; + break; + case GSR_MONITOR_ROT_270: + cursor_pos = swap_vec2i(cursor_pos); + cursor_pos.y = cap_kms->capture_size.y - cursor_pos.y; + // TODO: Remove this horrible hack + cursor_pos.y -= cursor_size.y; + break; + } + const intptr_t img_attr_cursor[] = { EGL_LINUX_DRM_FOURCC_EXT, cursor_drm_fd->pixel_format, EGL_WIDTH, cursor_drm_fd->width, @@ -549,11 +605,10 @@ static int gsr_capture_kms_vaapi_capture(gsr_capture *cap, AVFrame *frame) { cap_kms->params.egl->eglDestroyImage(cap_kms->params.egl->egl_display, cursor_image); cap_kms->params.egl->glBindTexture(GL_TEXTURE_2D, 0); - vec2i cursor_size = {cursor_drm_fd->width, cursor_drm_fd->height}; gsr_color_conversion_draw(&cap_kms->color_conversion, cap_kms->cursor_texture, - (vec2i){cursor_drm_fd->x, cursor_drm_fd->y}, cursor_size, + cursor_pos, cursor_size, (vec2i){0, 0}, cursor_size, - 0.0f, false); + texture_rotation, false); } cap_kms->params.egl->eglSwapBuffers(cap_kms->params.egl->egl_display, cap_kms->params.egl->egl_surface); diff --git a/src/color_conversion.c b/src/color_conversion.c index bc83ddd..0446c85 100644 --- a/src/color_conversion.c +++ b/src/color_conversion.c @@ -33,7 +33,7 @@ static float abs_f(float v) { #define RGB_TO_P010_LIMITED "const mat4 RGBtoYUV = mat4(0.225613, -0.119918, 0.429412, 0.000000,\n" \ " 0.582282, -0.309494, -0.394875, 0.000000,\n" \ " 0.050928, 0.429412, -0.034537, 0.000000,\n" \ - " 0.062745, 0.500000, 0.500000, 1.000000)"; + " 0.062745, 0.500000, 0.500000, 1.000000);" /* ITU-R BT709, full */ /* https://www.itu.int/dms_pubrec/itu-r/rec/bt/R-REC-BT.709-6-201506-I!!PDF-E.pdf */ @@ -74,19 +74,20 @@ static const char* color_format_range_get_transform_matrix(gsr_destination_color return NULL; } -static int load_shader_bgr(gsr_shader *shader, gsr_egl *egl, int *rotation_uniform) { +static int load_shader_bgr(gsr_shader *shader, gsr_egl *egl, gsr_color_uniforms *uniforms) { 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" ROTATE_Z "void main() \n" "{ \n" - " texcoords_out = texcoords; \n" - " gl_Position = vec4(pos.x, pos.y, 0.0, 1.0) * rotate_z(rotation); \n" + " texcoords_out = (vec4(texcoords.x - 0.5, texcoords.y - 0.5, 0.0, 0.0) * rotate_z(rotation)).xy + 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"); char fragment_shader[] = @@ -105,23 +106,25 @@ static int load_shader_bgr(gsr_shader *shader, gsr_egl *egl, int *rotation_unifo gsr_shader_bind_attribute_location(shader, "pos", 0); gsr_shader_bind_attribute_location(shader, "texcoords", 1); - *rotation_uniform = egl->glGetUniformLocation(shader->program_id, "rotation"); + uniforms->offset = egl->glGetUniformLocation(shader->program_id, "offset"); + uniforms->rotation = egl->glGetUniformLocation(shader->program_id, "rotation"); return 0; } -static int load_shader_bgr_external_texture(gsr_shader *shader, gsr_egl *egl, int *rotation_uniform) { +static int load_shader_bgr_external_texture(gsr_shader *shader, gsr_egl *egl, gsr_color_uniforms *uniforms) { 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" ROTATE_Z "void main() \n" "{ \n" - " texcoords_out = texcoords; \n" - " gl_Position = vec4(pos.x, pos.y, 0.0, 1.0) * rotate_z(rotation); \n" + " texcoords_out = (vec4(texcoords.x - 0.5, texcoords.y - 0.5, 0.0, 0.0) * rotate_z(rotation)).xy + 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"); char fragment_shader[] = @@ -142,11 +145,12 @@ static int load_shader_bgr_external_texture(gsr_shader *shader, gsr_egl *egl, in gsr_shader_bind_attribute_location(shader, "pos", 0); gsr_shader_bind_attribute_location(shader, "texcoords", 1); - *rotation_uniform = egl->glGetUniformLocation(shader->program_id, "rotation"); + uniforms->offset = egl->glGetUniformLocation(shader->program_id, "offset"); + uniforms->rotation = egl->glGetUniformLocation(shader->program_id, "rotation"); return 0; } -static int load_shader_y(gsr_shader *shader, gsr_egl *egl, int *rotation_uniform, gsr_destination_color color_format, gsr_color_range color_range) { +static int load_shader_y(gsr_shader *shader, gsr_egl *egl, gsr_color_uniforms *uniforms, gsr_destination_color color_format, gsr_color_range color_range) { const char *color_transform_matrix = color_format_range_get_transform_matrix(color_format, color_range); char vertex_shader[2048]; @@ -155,12 +159,13 @@ static int load_shader_y(gsr_shader *shader, gsr_egl *egl, int *rotation_uniform "in vec2 pos; \n" "in vec2 texcoords; \n" "out vec2 texcoords_out; \n" + "uniform vec2 offset; \n" "uniform float rotation; \n" ROTATE_Z "void main() \n" "{ \n" - " texcoords_out = texcoords; \n" - " gl_Position = vec4(pos.x, pos.y, 0.0, 1.0) * rotate_z(rotation); \n" + " texcoords_out = (vec4(texcoords.x - 0.5, texcoords.y - 0.5, 0.0, 0.0) * rotate_z(rotation)).xy + 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"); char fragment_shader[2048]; @@ -183,11 +188,12 @@ static int load_shader_y(gsr_shader *shader, gsr_egl *egl, int *rotation_uniform gsr_shader_bind_attribute_location(shader, "pos", 0); gsr_shader_bind_attribute_location(shader, "texcoords", 1); - *rotation_uniform = egl->glGetUniformLocation(shader->program_id, "rotation"); + uniforms->offset = egl->glGetUniformLocation(shader->program_id, "offset"); + uniforms->rotation = egl->glGetUniformLocation(shader->program_id, "rotation"); return 0; } -static unsigned int load_shader_uv(gsr_shader *shader, gsr_egl *egl, int *rotation_uniform, gsr_destination_color color_format, gsr_color_range color_range) { +static unsigned int load_shader_uv(gsr_shader *shader, gsr_egl *egl, gsr_color_uniforms *uniforms, gsr_destination_color color_format, gsr_color_range color_range) { const char *color_transform_matrix = color_format_range_get_transform_matrix(color_format, color_range); char vertex_shader[2048]; @@ -196,12 +202,13 @@ static unsigned int load_shader_uv(gsr_shader *shader, gsr_egl *egl, int *rotati "in vec2 pos; \n" "in vec2 texcoords; \n" "out vec2 texcoords_out; \n" + "uniform vec2 offset; \n" "uniform float rotation; \n" ROTATE_Z "void main() \n" "{ \n" - " texcoords_out = texcoords; \n" - " gl_Position = vec4(pos.x, pos.y, 0.0, 1.0) * rotate_z(rotation) * vec4(0.5, 0.5, 1.0, 1.0) - vec4(0.5, 0.5, 0.0, 0.0); \n" + " texcoords_out = (vec4(texcoords.x - 0.5, texcoords.y - 0.5, 0.0, 0.0) * rotate_z(rotation)).xy + 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"); char fragment_shader[2048]; @@ -224,7 +231,8 @@ static unsigned int load_shader_uv(gsr_shader *shader, gsr_egl *egl, int *rotati gsr_shader_bind_attribute_location(shader, "pos", 0); gsr_shader_bind_attribute_location(shader, "texcoords", 1); - *rotation_uniform = egl->glGetUniformLocation(shader->program_id, "rotation"); + uniforms->offset = egl->glGetUniformLocation(shader->program_id, "offset"); + uniforms->rotation = egl->glGetUniformLocation(shader->program_id, "rotation"); return 0; } @@ -291,12 +299,12 @@ int gsr_color_conversion_init(gsr_color_conversion *self, const gsr_color_conver return -1; } - if(load_shader_bgr(&self->shaders[0], self->params.egl, &self->rotation_uniforms[0]) != 0) { + if(load_shader_bgr(&self->shaders[0], self->params.egl, &self->uniforms[0]) != 0) { fprintf(stderr, "gsr error: gsr_color_conversion_init: failed to load bgr shader\n"); goto err; } - if(load_shader_bgr_external_texture(&self->shaders[1], self->params.egl, &self->rotation_uniforms[1]) != 0) { + if(load_shader_bgr_external_texture(&self->shaders[1], self->params.egl, &self->uniforms[1]) != 0) { fprintf(stderr, "gsr error: gsr_color_conversion_init: failed to load bgr shader (external texture)\n"); goto err; } @@ -309,12 +317,12 @@ int gsr_color_conversion_init(gsr_color_conversion *self, const gsr_color_conver return -1; } - if(load_shader_y(&self->shaders[0], self->params.egl, &self->rotation_uniforms[0], params->destination_color, params->color_range) != 0) { + if(load_shader_y(&self->shaders[0], self->params.egl, &self->uniforms[0], params->destination_color, params->color_range) != 0) { fprintf(stderr, "gsr error: gsr_color_conversion_init: failed to load Y shader\n"); goto err; } - if(load_shader_uv(&self->shaders[1], self->params.egl, &self->rotation_uniforms[1], params->destination_color, params->color_range) != 0) { + if(load_shader_uv(&self->shaders[1], self->params.egl, &self->uniforms[1], params->destination_color, params->color_range) != 0) { fprintf(stderr, "gsr error: gsr_color_conversion_init: failed to load UV shader\n"); goto err; } @@ -363,6 +371,9 @@ void gsr_color_conversion_deinit(gsr_color_conversion *self) { /* |source_pos| is in pixel coordinates and |source_size| */ void gsr_color_conversion_draw(gsr_color_conversion *self, unsigned int texture_id, vec2i source_pos, vec2i source_size, vec2i texture_pos, vec2i texture_size, float rotation, bool external_texture) { + // TODO: Remove this crap + rotation = M_PI*2.0f - rotation; + /* 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]); @@ -383,6 +394,7 @@ void gsr_color_conversion_draw(gsr_color_conversion *self, unsigned int texture_ self->params.egl->glGetTexLevelParameteriv(texture_target, 0, GL_TEXTURE_HEIGHT, &source_texture_size.y); } + // TODO: Remove this crap if(abs_f(M_PI * 0.5f - rotation) <= 0.001f || abs_f(M_PI * 1.5f - rotation) <= 0.001f) { float tmp = source_texture_size.x; source_texture_size.x = source_texture_size.y; @@ -410,13 +422,13 @@ void gsr_color_conversion_draw(gsr_color_conversion *self, unsigned int texture_ }; const float vertices[] = { - -1.0f + pos_norm.x, -1.0f + pos_norm.y + size_norm.y, texture_pos_norm.x, texture_pos_norm.y + texture_size_norm.y, - -1.0f + pos_norm.x, -1.0f + pos_norm.y, texture_pos_norm.x, texture_pos_norm.y, - -1.0f + pos_norm.x + size_norm.x, -1.0f + pos_norm.y, 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, -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 + pos_norm.x, -1.0f + pos_norm.y + size_norm.y, texture_pos_norm.x, texture_pos_norm.y + texture_size_norm.y, - -1.0f + pos_norm.x + size_norm.x, -1.0f + pos_norm.y, texture_pos_norm.x + texture_size_norm.x, texture_pos_norm.y, - -1.0f + pos_norm.x + size_norm.x, -1.0f + pos_norm.y + size_norm.y, texture_pos_norm.x + texture_size_norm.x, texture_pos_norm.y + texture_size_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); @@ -432,10 +444,12 @@ void gsr_color_conversion_draw(gsr_color_conversion *self, unsigned int texture_ if(external_texture) { gsr_shader_use(&self->shaders[1]); - self->params.egl->glUniform1f(self->rotation_uniforms[1], rotation); + self->params.egl->glUniform1f(self->uniforms[1].rotation, rotation); + self->params.egl->glUniform2f(self->uniforms[1].offset, pos_norm.x, pos_norm.y); } else { gsr_shader_use(&self->shaders[0]); - self->params.egl->glUniform1f(self->rotation_uniforms[0], rotation); + self->params.egl->glUniform1f(self->uniforms[0].rotation, rotation); + self->params.egl->glUniform2f(self->uniforms[0].offset, pos_norm.x, pos_norm.y); } self->params.egl->glDrawArrays(GL_TRIANGLES, 0, 6); } @@ -445,7 +459,8 @@ void gsr_color_conversion_draw(gsr_color_conversion *self, unsigned int texture_ //cap_xcomp->params.egl->glClear(GL_COLOR_BUFFER_BIT); gsr_shader_use(&self->shaders[1]); - self->params.egl->glUniform1f(self->rotation_uniforms[1], rotation); + self->params.egl->glUniform1f(self->uniforms[1].rotation, rotation); + self->params.egl->glUniform2f(self->uniforms[1].offset, pos_norm.x, pos_norm.y); self->params.egl->glDrawArrays(GL_TRIANGLES, 0, 6); } diff --git a/src/egl.c b/src/egl.c index f1e3885..7971dbd 100644 --- a/src/egl.c +++ b/src/egl.c @@ -23,10 +23,10 @@ static void output_handle_geometry(void *data, struct wl_output *wl_output, (void)subpixel; (void)make; (void)model; - (void)transform; gsr_wayland_output *gsr_output = data; gsr_output->pos.x = x; gsr_output->pos.y = y; + gsr_output->transform = transform; } static void output_handle_mode(void *data, struct wl_output *wl_output, uint32_t flags, int32_t width, int32_t height, int32_t refresh) { @@ -101,6 +101,7 @@ static void registry_add_object(void *data, struct wl_registry *registry, uint32 .output = wl_registry_bind(registry, name, &wl_output_interface, 4), .pos = { .x = 0, .y = 0 }, .size = { .x = 0, .y = 0 }, + .transform = 0, .name = NULL, }; wl_output_add_listener(gsr_output->output, &output_listener, gsr_output); @@ -322,6 +323,7 @@ static bool gsr_egl_load_gl(gsr_egl *self, void *library) { { (void**)&self->glBlendFunc, "glBlendFunc" }, { (void**)&self->glGetUniformLocation, "glGetUniformLocation" }, { (void**)&self->glUniform1f, "glUniform1f" }, + { (void**)&self->glUniform2f, "glUniform2f" }, { NULL, NULL } }; diff --git a/src/main.cpp b/src/main.cpp index 4a04206..31b58d7 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -851,6 +851,7 @@ static void usage_full() { fprintf(stderr, "\n"); fprintf(stderr, " -cr Color range. Should be either 'limited' (aka mpeg) or 'full' (aka jpeg). Defaults to 'limited'.\n"); fprintf(stderr, " Limited color range means that colors are in range 16-235 while full color range means that colors are in range 0-255 (when not recording with hdr).\n"); + fprintf(stderr, " Note that some buggy video players (such as vlc) are unable to correctly display videos in full color range.\n"); fprintf(stderr, "\n"); fprintf(stderr, " -v Prints per second, fps updates. Optional, set to 'yes' by default.\n"); fprintf(stderr, "\n"); diff --git a/src/utils.c b/src/utils.c index 8316fa4..f98527f 100644 --- a/src/utils.c +++ b/src/utils.c @@ -7,6 +7,14 @@ #include #include #include +#include + +typedef enum { + X11_ROT_0 = 1 << 0, + X11_ROT_90 = 1 << 1, + X11_ROT_180 = 1 << 2, + X11_ROT_270 = 1 << 3 +} X11Rotation; double clock_get_monotonic_seconds(void) { struct timespec ts; @@ -24,11 +32,49 @@ static const XRRModeInfo* get_mode_info(const XRRScreenResources *sr, RRMode id) return NULL; } +static gsr_monitor_rotation x11_rotation_to_gsr_rotation(X11Rotation rot) { + switch(rot) { + case X11_ROT_0: return GSR_MONITOR_ROT_0; + case X11_ROT_90: return GSR_MONITOR_ROT_90; + case X11_ROT_180: return GSR_MONITOR_ROT_180; + case X11_ROT_270: return GSR_MONITOR_ROT_270; + } + return GSR_MONITOR_ROT_0; +} + +static gsr_monitor_rotation wayland_transform_to_gsr_rotation(int32_t rot) { + switch(rot) { + case 0: return GSR_MONITOR_ROT_0; + case 1: return GSR_MONITOR_ROT_90; + case 2: return GSR_MONITOR_ROT_180; + case 3: return GSR_MONITOR_ROT_270; + } + return GSR_MONITOR_ROT_0; +} + +static uint32_t x11_output_get_connector_id(Display *dpy, RROutput output, Atom randr_connector_id_atom) { + Atom type = 0; + int format = 0; + unsigned long bytes_after = 0; + unsigned long nitems = 0; + unsigned char *prop = NULL; + XRRGetOutputProperty(dpy, output, randr_connector_id_atom, 0, 128, false, false, AnyPropertyType, &type, &format, &nitems, &bytes_after, &prop); + + long result = 0; + if(type == XA_INTEGER && format == 32) + result = *(long*)prop; + + free(prop); + return result; +} + void for_each_active_monitor_output_x11(Display *display, active_monitor_callback callback, void *userdata) { XRRScreenResources *screen_res = XRRGetScreenResources(display, DefaultRootWindow(display)); if(!screen_res) return; + const Atom randr_connector_id_atom = XInternAtom(display, "CONNECTOR_ID", False); + char display_name[256]; for(int i = 0; i < screen_res->noutput; ++i) { XRROutputInfo *out_info = XRRGetOutputInfo(display, screen_res, screen_res->outputs[i]); @@ -40,13 +86,15 @@ void for_each_active_monitor_output_x11(Display *display, active_monitor_callbac memcpy(display_name, out_info->name, out_info->nameLen); display_name[out_info->nameLen] = '\0'; - gsr_monitor monitor = { + const gsr_monitor monitor = { .name = display_name, .name_len = out_info->nameLen, .pos = { .x = crt_info->x, .y = crt_info->y }, .size = { .x = (int)crt_info->width, .y = (int)crt_info->height }, .crt_info = crt_info, - .connector_id = 0 // TODO: Get connector id + .connector_id = x11_output_get_connector_id(display, screen_res->outputs[i], randr_connector_id_atom), + .rotation = x11_rotation_to_gsr_rotation(crt_info->rotation), + .monitor_identifier = 0 }; callback(&monitor, userdata); } @@ -64,6 +112,7 @@ void for_each_active_monitor_output_x11(Display *display, active_monitor_callbac typedef struct { int type; int count; + int count_active; } drm_connector_type_count; #define CONNECTOR_TYPE_COUNTS 32 @@ -80,6 +129,7 @@ static drm_connector_type_count* drm_connector_types_get_index(drm_connector_typ const int index = *num_type_counts; type_counts[index].type = connector_type; type_counts[index].count = 0; + type_counts[index].count_active = 0; ++*num_type_counts; return &type_counts[index]; } @@ -99,18 +149,51 @@ static bool connector_get_property_by_name(int drmfd, drmModeConnectorPtr props, return false; } +/* TODO: Support more connector types*/ +static int get_connector_type_by_name(const char *name) { + int len = strlen(name); + if(len >= 5 && strncmp(name, "HDMI-", 5) == 0) + return 1; + else if(len >= 3 && strncmp(name, "DP-", 3) == 0) + return 2; + else if(len >= 12 && strncmp(name, "DisplayPort-", 12) == 0) + return 3; + else + return -1; +} + +static uint32_t monitor_identifier_from_type_and_count(int monitor_type_index, int monitor_type_count) { + return ((uint32_t)monitor_type_index << 16) | ((uint32_t)monitor_type_count); +} + static void for_each_active_monitor_output_wayland(const gsr_egl *egl, active_monitor_callback callback, void *userdata) { + drm_connector_type_count type_counts[CONNECTOR_TYPE_COUNTS]; + int num_type_counts = 0; + for(int i = 0; i < egl->wayland.num_outputs; ++i) { - if(!egl->wayland.outputs[i].name) + const gsr_wayland_output *output = &egl->wayland.outputs[i]; + if(!output->name) continue; - gsr_monitor monitor = { - .name = egl->wayland.outputs[i].name, - .name_len = strlen(egl->wayland.outputs[i].name), - .pos = { .x = egl->wayland.outputs[i].pos.x, .y = egl->wayland.outputs[i].pos.y }, - .size = { .x = egl->wayland.outputs[i].size.x, .y = egl->wayland.outputs[i].size.y }, + const int connector_type_index = get_connector_type_by_name(output->name); + drm_connector_type_count *connector_type = NULL; + if(connector_type_index != -1) + connector_type = drm_connector_types_get_index(type_counts, &num_type_counts, connector_type_index); + + if(connector_type) { + ++connector_type->count; + ++connector_type->count_active; + } + + const gsr_monitor monitor = { + .name = output->name, + .name_len = strlen(output->name), + .pos = { .x = output->pos.x, .y = output->pos.y }, + .size = { .x = output->size.x, .y = output->size.y }, .crt_info = NULL, - .connector_id = 0 + .connector_id = 0, + .rotation = wayland_transform_to_gsr_rotation(output->transform), + .monitor_identifier = connector_type ? monitor_identifier_from_type_and_count(connector_type_index, connector_type->count_active) : 0 }; callback(&monitor, userdata); } @@ -145,19 +228,25 @@ static void for_each_active_monitor_output_drm(const gsr_egl *egl, active_monito continue; } + if(connector_type) + ++connector_type->count_active; + uint64_t crtc_id = 0; connector_get_property_by_name(fd, connector, "CRTC_ID", &crtc_id); drmModeCrtcPtr crtc = drmModeGetCrtc(fd, crtc_id); if(connector_type && crtc_id > 0 && crtc && connection_name_len + 5 < (int)sizeof(display_name)) { const int display_name_len = snprintf(display_name, sizeof(display_name), "%s-%d", connection_name, connector_type->count); - gsr_monitor monitor = { + const int connector_type_index_name = get_connector_type_by_name(display_name); + const gsr_monitor monitor = { .name = display_name, .name_len = display_name_len, .pos = { .x = crtc->x, .y = crtc->y }, .size = { .x = (int)crtc->width, .y = (int)crtc->height }, .crt_info = NULL, - .connector_id = connector->connector_id + .connector_id = connector->connector_id, + .rotation = GSR_MONITOR_ROT_0, + .monitor_identifier = connector_type_index_name != -1 ? monitor_identifier_from_type_and_count(connector_type_index_name, connector_type->count_active) : 0 }; callback(&monitor, userdata); } @@ -192,6 +281,9 @@ static void get_monitor_by_name_callback(const gsr_monitor *monitor, void *userd if(!data->found_monitor && strcmp(data->name, monitor->name) == 0) { data->monitor->pos = monitor->pos; data->monitor->size = monitor->size; + data->monitor->connector_id = monitor->connector_id; + data->monitor->rotation = monitor->rotation; + data->monitor->monitor_identifier = monitor->monitor_identifier; data->found_monitor = true; } } @@ -206,6 +298,38 @@ bool get_monitor_by_name(const gsr_egl *egl, gsr_connection_type connection_type return userdata.found_monitor; } +typedef struct { + const gsr_monitor *monitor; + gsr_monitor_rotation rotation; +} get_monitor_by_connector_id_userdata; + +static void get_monitor_by_connector_id_callback(const gsr_monitor *monitor, void *userdata) { + get_monitor_by_connector_id_userdata *data = (get_monitor_by_connector_id_userdata*)userdata; + if(monitor->connector_id == data->monitor->connector_id || + (!monitor->connector_id && monitor->monitor_identifier == data->monitor->monitor_identifier)) + { + data->rotation = monitor->rotation; + } +} + +gsr_monitor_rotation drm_monitor_get_display_server_rotation(const gsr_egl *egl, const gsr_monitor *monitor) { + if(egl->wayland.dpy) { + get_monitor_by_connector_id_userdata userdata; + userdata.monitor = monitor; + userdata.rotation = GSR_MONITOR_ROT_0; + for_each_active_monitor_output_wayland(egl, get_monitor_by_connector_id_callback, &userdata); + return userdata.rotation; + } else { + get_monitor_by_connector_id_userdata userdata; + userdata.monitor = monitor; + userdata.rotation = GSR_MONITOR_ROT_0; + for_each_active_monitor_output_x11(egl->x11.dpy, get_monitor_by_connector_id_callback, &userdata); + return userdata.rotation; + } + + return GSR_MONITOR_ROT_0; +} + bool gl_get_gpu_info(gsr_egl *egl, gsr_gpu_info *info) { const char *software_renderers[] = { "llvmpipe", "SWR", "softpipe", NULL }; bool supported = true; -- cgit v1.2.3