aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authordec05eba <dec05eba@protonmail.com>2024-02-10 02:47:01 +0100
committerdec05eba <dec05eba@protonmail.com>2024-02-10 03:48:43 +0100
commitb8843395acd03520b7630b6a1dc19f151f42382d (patch)
treef8da6cf22229ba44916de608c9bb46bfddcd8e79
parentac73d9cb13ea7e4694b6b013de426cefddae4ca7 (diff)
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.
-rw-r--r--TODO8
-rw-r--r--include/color_conversion.h7
-rw-r--r--include/egl.h2
-rw-r--r--include/utils.h12
-rw-r--r--src/capture/kms_cuda.c64
-rw-r--r--src/capture/kms_vaapi.c67
-rw-r--r--src/color_conversion.c75
-rw-r--r--src/egl.c4
-rw-r--r--src/main.cpp1
-rw-r--r--src/utils.c146
10 files changed, 330 insertions, 56 deletions
diff --git a/TODO b/TODO
index b9bcf13..e15f703 100644
--- a/TODO
+++ b/TODO
@@ -116,4 +116,10 @@ Support low power option (does it even work with vaapi in ffmpeg??). Would be ve
Instead of sending a big list of drm data back to kms client, send the monitor we want to record to kms server and the server should respond with only the matching monitor, and cursor.
Tonemap hdr to sdr when hdr is enabled and when hevc_hdr/av1_hdr is not used.
-Support hdr capture with kms cuda. \ No newline at end of file
+Support hdr capture with kms cuda.
+
+Mention known issue, capture on intel wayland compressed data glitch.
+
+Add 10 bit record option, h264_10bit, hevc_10bit and av1_10bit.
+
+Rotate cursor texture properly (around top left origin) \ No newline at end of file
diff --git a/include/color_conversion.h b/include/color_conversion.h
index 07b29f6..8bfe599 100644
--- a/include/color_conversion.h
+++ b/include/color_conversion.h
@@ -20,6 +20,11 @@ typedef enum {
} gsr_destination_color;
typedef struct {
+ int offset;
+ int rotation;
+} gsr_color_uniforms;
+
+typedef struct {
gsr_egl *egl;
gsr_source_color source_color;
@@ -33,7 +38,7 @@ typedef struct {
typedef struct {
gsr_color_conversion_params params;
- int rotation_uniforms[2];
+ gsr_color_uniforms uniforms[2];
gsr_shader shaders[2];
unsigned int framebuffers[2];
diff --git a/include/egl.h b/include/egl.h
index b8513fd..4705742 100644
--- a/include/egl.h
+++ b/include/egl.h
@@ -119,6 +119,7 @@ typedef struct {
void *output;
vec2i pos;
vec2i size;
+ int32_t transform;
char *name;
} gsr_wayland_output;
@@ -215,6 +216,7 @@ typedef struct {
void (*glBlendFunc)(unsigned int sfactor, unsigned int dfactor);
int (*glGetUniformLocation)(unsigned int program, const char *name);
void (*glUniform1f)(int location, float v0);
+ void (*glUniform2f)(int location, float v0, float v1);
} gsr_egl;
bool gsr_egl_load(gsr_egl *self, Display *dpy, bool wayland);
diff --git a/include/utils.h b/include/utils.h
index 26a0aed..898f375 100644
--- a/include/utils.h
+++ b/include/utils.h
@@ -18,13 +18,22 @@ typedef struct {
int gpu_version; /* 0 if unknown */
} gsr_gpu_info;
+typedef enum {
+ GSR_MONITOR_ROT_0,
+ GSR_MONITOR_ROT_90,
+ GSR_MONITOR_ROT_180,
+ GSR_MONITOR_ROT_270
+} gsr_monitor_rotation;
+
typedef struct {
const char *name;
int name_len;
vec2i pos;
vec2i size;
XRRCrtcInfo *crt_info; /* Only on x11 */
- uint32_t connector_id; /* Only on drm */
+ uint32_t connector_id; /* Only on x11 and drm */
+ gsr_monitor_rotation rotation; /* Only on x11 and wayland */
+ uint32_t monitor_identifier; /* Only on drm and wayland */
} gsr_monitor;
typedef enum {
@@ -46,6 +55,7 @@ typedef void (*active_monitor_callback)(const gsr_monitor *monitor, void *userda
void for_each_active_monitor_output_x11(Display *display, active_monitor_callback callback, void *userdata);
void for_each_active_monitor_output(const gsr_egl *egl, gsr_connection_type connection_type, active_monitor_callback callback, void *userdata);
bool get_monitor_by_name(const gsr_egl *egl, gsr_connection_type connection_type, const char *name, gsr_monitor *monitor);
+gsr_monitor_rotation drm_monitor_get_display_server_rotation(const gsr_egl *egl, const gsr_monitor *monitor);
bool gl_get_gpu_info(gsr_egl *egl, gsr_gpu_info *info);
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 <xf86drmMode.h>
#include <xf86drm.h>
#include <stdlib.h>
+#include <X11/Xatom.h>
+
+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;