mirror of
https://repo.dec05eba.com/gpu-screen-recorder
synced 2026-04-04 02:26:36 +09:00
Compare commits
12 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a4845db319 | ||
|
|
8065ede251 | ||
|
|
baf0434854 | ||
|
|
0deb41afe1 | ||
|
|
752e773fb7 | ||
|
|
12dd2cd3e1 | ||
|
|
33467cb7f4 | ||
|
|
1a61c64e3f | ||
|
|
7ed9977068 | ||
|
|
8feb94f518 | ||
|
|
6acd65a9c2 | ||
|
|
56e2a82474 |
@@ -42,7 +42,7 @@ When recording GTA V at 4k on highest settings, fps drops from 60 to 23 when usi
|
||||
GPU Screen Recorder also produces much smoother videos than OBS when GPU utilization is close to 100%, see comparison here: [https://www.youtube.com/watch?v=zfj4sNVLLLg](https://www.youtube.com/watch?v=zfj4sNVLLLg).\
|
||||
GPU Screen Recorder has much better performance than OBS Studio even with version 30.2 that does "zero-copy" recording and encoding, see: [https://www.youtube.com/watch?v=jdroRjibsDw](https://www.youtube.com/watch?v=jdroRjibsDw).\
|
||||
It is recommended to save the video to a SSD because of the large file size, which a slow HDD might not be fast enough to handle. Using variable framerate mode (-fm vfr) which is the default is also recommended as this reduces encoding load. Ultra quality is also overkill most of the time, very high (the default) or lower quality is usually enough.\
|
||||
Note that recording on AMD can have some performance issues on Wayland in the recording itself when recording without desktop portal unless your mesa version is 25.0.0 or greater.
|
||||
Note that for best performance you should close other screen recorders such as OBS Studio when using GPU Screen Recorder even if they are not recording, since they can affect performance even when idle. This is the case with OBS Studio.
|
||||
## Note about optimal performance on NVIDIA
|
||||
NVIDIA driver has a "feature" (read: bug) where it will downclock memory transfer rate when a program uses cuda (or nvenc, which uses cuda), such as GPU Screen Recorder. To work around this bug, GPU Screen Recorder can overclock your GPU memory transfer rate to it's normal optimal level.\
|
||||
To enable overclocking for optimal performance use the `-oc` option when running GPU Screen Recorder. You also need to have "Coolbits" NVIDIA X setting set to "12" to enable overclocking. You can automatically add this option if you run `sudo nvidia-xconfig --cool-bits=12` and then reboot your computer.\
|
||||
|
||||
11
TODO
11
TODO
@@ -4,7 +4,6 @@ See https://trac.ffmpeg.org/wiki/EncodingForStreamingSites for optimizing stream
|
||||
Look at VK_EXT_external_memory_dma_buf.
|
||||
Use mov+faststart.
|
||||
Allow recording all monitors/selected monitor without nvfbc by recording the compositor proxy window and only recording the part that matches the monitor(s).
|
||||
Allow recording a region by recording the compositor proxy window / nvfbc window and copying part of it.
|
||||
Support amf and qsv.
|
||||
Disable flipping on nvidia? this might fix some stuttering issues on some setups. See NvCtrlGetAttribute/NvCtrlSetAttributeAndGetStatus NV_CTRL_SYNC_TO_VBLANK https://github.com/NVIDIA/nvidia-settings/blob/d5f022976368cbceb2f20b838ddb0bf992f0cfb9/src/gtk%2B-2.x/ctkopengl.c.
|
||||
Replays seem to have some issues with audio/video. Why?
|
||||
@@ -253,4 +252,12 @@ When webcam support is added also support v4l2loopback? this is done by using av
|
||||
|
||||
Do proper exit, to call gsr_capture_destroy which will properly stop gsr-kms-server. Otherwise there can be zombie gsr-kms-server on error.
|
||||
|
||||
Replace all scissors with clearing textures if the cursor hits the outside of the frame image
|
||||
Replace all scissors with clearing textures if the cursor hits the outside of the frame image.
|
||||
|
||||
Cursor position might be slightly wrong on rotated monitor.
|
||||
|
||||
External texture doesn't work on nvidia x11, probably because of glx context (requires gles es). External texture is not used on nvidia x11 right now so it's not an issue.
|
||||
|
||||
Add option to save replay buffer on disk instead of ram.
|
||||
|
||||
nvfbc capture cursor with cursor.h instead and composite that on top. This allows us to also always get a cursor in direct capture mode. This could possible give better performance as well.
|
||||
@@ -6,7 +6,7 @@
|
||||
#include "vec2.h"
|
||||
#include <stdbool.h>
|
||||
|
||||
#define GSR_COLOR_CONVERSION_MAX_SHADERS 6
|
||||
#define GSR_COLOR_CONVERSION_MAX_SHADERS 12
|
||||
#define GSR_COLOR_CONVERSION_MAX_FRAMEBUFFERS 2
|
||||
|
||||
typedef enum {
|
||||
@@ -72,7 +72,7 @@ typedef struct {
|
||||
int gsr_color_conversion_init(gsr_color_conversion *self, const gsr_color_conversion_params *params);
|
||||
void gsr_color_conversion_deinit(gsr_color_conversion *self);
|
||||
|
||||
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);
|
||||
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);
|
||||
void gsr_color_conversion_clear(gsr_color_conversion *self);
|
||||
|
||||
gsr_rotation gsr_monitor_rotation_to_rotation(gsr_monitor_rotation monitor_rotation);
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
project('gpu-screen-recorder', ['c', 'cpp'], version : '5.3.4', default_options : ['warning_level=2'])
|
||||
project('gpu-screen-recorder', ['c', 'cpp'], version : '5.3.6', default_options : ['warning_level=2'])
|
||||
|
||||
add_project_arguments('-Wshadow', language : ['c', 'cpp'])
|
||||
if get_option('buildtype') == 'debug'
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
[package]
|
||||
name = "gpu-screen-recorder"
|
||||
type = "executable"
|
||||
version = "5.3.4"
|
||||
version = "5.3.6"
|
||||
platforms = ["posix"]
|
||||
|
||||
[config]
|
||||
|
||||
@@ -502,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);
|
||||
}
|
||||
@@ -530,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);
|
||||
}
|
||||
@@ -574,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;
|
||||
@@ -616,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;
|
||||
|
||||
@@ -644,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);
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -347,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 = {
|
||||
@@ -365,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);
|
||||
}
|
||||
|
||||
|
||||
@@ -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 = {
|
||||
@@ -279,8 +279,8 @@ static int gsr_capture_xcomposite_capture(gsr_capture *cap, gsr_capture_metadata
|
||||
|
||||
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->glFlush();
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -5,15 +5,18 @@
|
||||
#include <string.h>
|
||||
#include <assert.h>
|
||||
|
||||
#define SHADER_INDEX_Y 0
|
||||
#define SHADER_INDEX_UV 1
|
||||
#define SHADER_INDEX_Y_EXTERNAL 2
|
||||
#define SHADER_INDEX_UV_EXTERNAL 3
|
||||
#define SHADER_INDEX_RGB 4
|
||||
#define SHADER_INDEX_RGB_EXTERNAL 5
|
||||
|
||||
// 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 SHADER_INDEX_Y 0
|
||||
#define SHADER_INDEX_UV 1
|
||||
#define SHADER_INDEX_Y_EXTERNAL 2
|
||||
#define SHADER_INDEX_UV_EXTERNAL 3
|
||||
#define SHADER_INDEX_RGB 4
|
||||
#define SHADER_INDEX_RGB_EXTERNAL 5
|
||||
#define SHADER_INDEX_Y_BLEND 6
|
||||
#define SHADER_INDEX_UV_BLEND 7
|
||||
#define SHADER_INDEX_Y_EXTERNAL_BLEND 8
|
||||
#define SHADER_INDEX_UV_EXTERNAL_BLEND 9
|
||||
#define SHADER_INDEX_RGB_BLEND 10
|
||||
#define SHADER_INDEX_RGB_EXTERNAL_BLEND 11
|
||||
|
||||
/* https://en.wikipedia.org/wiki/YCbCr, see study/color_space_transform_matrix.png */
|
||||
|
||||
@@ -75,9 +78,7 @@ 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 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, bool alpha_blending) {
|
||||
const char *color_transform_matrix = color_format_range_get_transform_matrix(color_format, color_range);
|
||||
|
||||
char compute_shader[2048];
|
||||
@@ -98,15 +99,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 = texelFetch(img_input, ivec2(rotated_texel_coord), 0);\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", max_local_size_dim, max_local_size_dim, external_texture ? "samplerExternalOES" : "sampler2D", color_transform_matrix,
|
||||
alpha_blending ? "texelFetch(img_background, ivec2(output_texel_coord), 0)" : "source_color_yuv");
|
||||
|
||||
if(gsr_shader_init(shader, egl, NULL, NULL, compute_shader) != 0)
|
||||
return -1;
|
||||
@@ -118,7 +121,7 @@ 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_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 compute_shader[2048];
|
||||
@@ -139,15 +142,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 = texelFetch(img_input, ivec2(rotated_texel_coord) << 1, 0);\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/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", max_local_size_dim, max_local_size_dim, external_texture ? "samplerExternalOES" : "sampler2D", color_transform_matrix,
|
||||
alpha_blending ? "texelFetch(img_background, ivec2(output_texel_coord), 0)" : "source_color_yuv");
|
||||
|
||||
if(gsr_shader_init(shader, egl, NULL, NULL, compute_shader) != 0)
|
||||
return -1;
|
||||
@@ -159,7 +164,7 @@ 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_uniforms *uniforms, int max_local_size_dim, bool external_texture, bool alpha_blending) {
|
||||
char compute_shader[2048];
|
||||
snprintf(compute_shader, sizeof(compute_shader),
|
||||
"#version 310 es\n"
|
||||
@@ -177,14 +182,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 = texelFetch(img_input, ivec2(rotated_texel_coord), 0);\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", max_local_size_dim, max_local_size_dim, external_texture ? "samplerExternalOES" : "sampler2D",
|
||||
alpha_blending ? "texelFetch(img_background, ivec2(output_texel_coord), 0)" : "source_color");
|
||||
|
||||
if(gsr_shader_init(shader, egl, NULL, NULL, compute_shader) != 0)
|
||||
return -1;
|
||||
@@ -264,23 +271,43 @@ int gsr_color_conversion_init(gsr_color_conversion *self, const gsr_color_conver
|
||||
return -1;
|
||||
}
|
||||
|
||||
if(load_compute_shader_y(&self->shaders[SHADER_INDEX_Y], self->params.egl, &self->uniforms[SHADER_INDEX_Y], self->max_local_size_dim, params->destination_color, params->color_range, false) != 0) {
|
||||
if(load_compute_shader_y(&self->shaders[SHADER_INDEX_Y], self->params.egl, &self->uniforms[SHADER_INDEX_Y], self->max_local_size_dim, params->destination_color, params->color_range, false, 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[SHADER_INDEX_UV], self->params.egl, &self->uniforms[SHADER_INDEX_UV], self->max_local_size_dim, params->destination_color, params->color_range, false) != 0) {
|
||||
if(load_compute_shader_uv(&self->shaders[SHADER_INDEX_UV], self->params.egl, &self->uniforms[SHADER_INDEX_UV], self->max_local_size_dim, params->destination_color, params->color_range, false, false) != 0) {
|
||||
fprintf(stderr, "gsr error: gsr_color_conversion_init: failed to load UV compute shader\n");
|
||||
goto err;
|
||||
}
|
||||
|
||||
if(load_compute_shader_y(&self->shaders[SHADER_INDEX_Y_BLEND], self->params.egl, &self->uniforms[SHADER_INDEX_Y_BLEND], self->max_local_size_dim, params->destination_color, params->color_range, false, 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[SHADER_INDEX_UV_BLEND], self->params.egl, &self->uniforms[SHADER_INDEX_UV_BLEND], self->max_local_size_dim, params->destination_color, params->color_range, false, true) != 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[SHADER_INDEX_Y_EXTERNAL], self->params.egl, &self->uniforms[SHADER_INDEX_Y_EXTERNAL], self->max_local_size_dim, params->destination_color, params->color_range, true) != 0) {
|
||||
if(load_compute_shader_y(&self->shaders[SHADER_INDEX_Y_EXTERNAL], self->params.egl, &self->uniforms[SHADER_INDEX_Y_EXTERNAL], self->max_local_size_dim, params->destination_color, params->color_range, true, 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[SHADER_INDEX_UV_EXTERNAL], self->params.egl, &self->uniforms[SHADER_INDEX_UV_EXTERNAL], self->max_local_size_dim, params->destination_color, params->color_range, true) != 0) {
|
||||
if(load_compute_shader_uv(&self->shaders[SHADER_INDEX_UV_EXTERNAL], self->params.egl, &self->uniforms[SHADER_INDEX_UV_EXTERNAL], self->max_local_size_dim, params->destination_color, params->color_range, true, false) != 0) {
|
||||
fprintf(stderr, "gsr error: gsr_color_conversion_init: failed to load UV compute shader\n");
|
||||
goto err;
|
||||
}
|
||||
|
||||
if(load_compute_shader_y(&self->shaders[SHADER_INDEX_Y_EXTERNAL_BLEND], self->params.egl, &self->uniforms[SHADER_INDEX_Y_EXTERNAL_BLEND], self->max_local_size_dim, params->destination_color, params->color_range, true, 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[SHADER_INDEX_UV_EXTERNAL_BLEND], self->params.egl, &self->uniforms[SHADER_INDEX_UV_EXTERNAL_BLEND], self->max_local_size_dim, params->destination_color, params->color_range, true, true) != 0) {
|
||||
fprintf(stderr, "gsr error: gsr_color_conversion_init: failed to load UV compute shader\n");
|
||||
goto err;
|
||||
}
|
||||
@@ -293,13 +320,23 @@ int gsr_color_conversion_init(gsr_color_conversion *self, const gsr_color_conver
|
||||
return -1;
|
||||
}
|
||||
|
||||
if(load_compute_shader_rgb(&self->shaders[SHADER_INDEX_RGB], self->params.egl, &self->uniforms[SHADER_INDEX_RGB], self->max_local_size_dim, false) != 0) {
|
||||
if(load_compute_shader_rgb(&self->shaders[SHADER_INDEX_RGB], self->params.egl, &self->uniforms[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");
|
||||
goto err;
|
||||
}
|
||||
|
||||
if(load_compute_shader_rgb(&self->shaders[SHADER_INDEX_RGB_BLEND], self->params.egl, &self->uniforms[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");
|
||||
goto err;
|
||||
}
|
||||
|
||||
if(self->params.load_external_image_shader) {
|
||||
if(load_compute_shader_rgb(&self->shaders[SHADER_INDEX_RGB_EXTERNAL], self->params.egl, &self->uniforms[SHADER_INDEX_RGB_EXTERNAL], self->max_local_size_dim, true) != 0) {
|
||||
if(load_compute_shader_rgb(&self->shaders[SHADER_INDEX_RGB_EXTERNAL], self->params.egl, &self->uniforms[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");
|
||||
goto err;
|
||||
}
|
||||
|
||||
if(load_compute_shader_rgb(&self->shaders[SHADER_INDEX_RGB_EXTERNAL_BLEND], self->params.egl, &self->uniforms[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");
|
||||
goto err;
|
||||
}
|
||||
@@ -408,7 +445,7 @@ typedef enum {
|
||||
GSR_COLOR_COMP_RGB
|
||||
} gsr_color_component;
|
||||
|
||||
static int color_component_get_texture_index(gsr_color_component 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;
|
||||
@@ -428,23 +465,38 @@ static unsigned int color_component_get_color_format(gsr_color_component color_c
|
||||
return GL_RGBA8;
|
||||
}
|
||||
|
||||
static int color_component_get_shader_index(gsr_color_component color_component, bool external_texture) {
|
||||
static int color_component_get_shader_index(gsr_color_component color_component, bool external_texture, bool alpha_blending) {
|
||||
switch(color_component) {
|
||||
case GSR_COLOR_COMP_Y: return external_texture ? SHADER_INDEX_Y_EXTERNAL : SHADER_INDEX_Y;
|
||||
case GSR_COLOR_COMP_UV: return external_texture ? SHADER_INDEX_UV_EXTERNAL : SHADER_INDEX_UV;
|
||||
case GSR_COLOR_COMP_RGB: return external_texture ? SHADER_INDEX_RGB_EXTERNAL : SHADER_INDEX_RGB;
|
||||
case GSR_COLOR_COMP_Y: {
|
||||
if(external_texture)
|
||||
return alpha_blending ? SHADER_INDEX_Y_EXTERNAL_BLEND : SHADER_INDEX_Y_EXTERNAL;
|
||||
else
|
||||
return alpha_blending ? SHADER_INDEX_Y_BLEND : SHADER_INDEX_Y;
|
||||
}
|
||||
case GSR_COLOR_COMP_UV: {
|
||||
if(external_texture)
|
||||
return alpha_blending ? SHADER_INDEX_UV_EXTERNAL_BLEND : SHADER_INDEX_UV_EXTERNAL;
|
||||
else
|
||||
return alpha_blending ? SHADER_INDEX_UV_BLEND : SHADER_INDEX_UV;
|
||||
}
|
||||
case GSR_COLOR_COMP_RGB: {
|
||||
if(external_texture)
|
||||
return alpha_blending ? SHADER_INDEX_RGB_EXTERNAL_BLEND : SHADER_INDEX_RGB_EXTERNAL;
|
||||
else
|
||||
return alpha_blending ? SHADER_INDEX_RGB_BLEND : SHADER_INDEX_RGB;
|
||||
}
|
||||
}
|
||||
assert(false);
|
||||
return SHADER_INDEX_RGB;
|
||||
}
|
||||
|
||||
static void gsr_color_conversion_dispatch_compute_shader(gsr_color_conversion *self, bool external_texture, 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 shader_index = color_component_get_shader_index(color_component, external_texture);
|
||||
const int texture_index = color_component_get_texture_index(color_component);
|
||||
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 shader_index = color_component_get_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[texture_index]);
|
||||
self->params.egl->glBindTexture(GL_TEXTURE_2D, self->params.destination_textures[destination_texture_index]);
|
||||
self->params.egl->glActiveTexture(GL_TEXTURE0);
|
||||
|
||||
gsr_color_uniforms *uniform = &self->uniforms[shader_index];
|
||||
@@ -453,23 +505,23 @@ static void gsr_color_conversion_dispatch_compute_shader(gsr_color_conversion *s
|
||||
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[texture_index], 0, GL_FALSE, 0, GL_WRITE_ONLY, color_format);
|
||||
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);
|
||||
}
|
||||
|
||||
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) {
|
||||
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) {
|
||||
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 };
|
||||
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, &source_position, texture_size, scale);
|
||||
|
||||
source_position.x -= (texture_pos.x * scale.x + 0.5);
|
||||
source_position.y -= (texture_pos.y * scale.y + 0.5);
|
||||
source_position.x -= (source_pos.x * scale.x + 0.5);
|
||||
source_position.y -= (source_pos.y * scale.y + 0.5);
|
||||
|
||||
const int texture_target = external_texture ? GL_TEXTURE_EXTERNAL_OES : GL_TEXTURE_2D;
|
||||
self->params.egl->glBindTexture(texture_target, texture_id);
|
||||
@@ -479,12 +531,13 @@ void gsr_color_conversion_draw(gsr_color_conversion *self, unsigned int texture_
|
||||
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, 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, rotation_matrix, source_position, destination_pos, (vec2i){destination_size.x/2, destination_size.y/2}, scale, use_16bit_colors, GSR_COLOR_COMP_UV);
|
||||
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, rotation_matrix, source_position, destination_pos, destination_size, scale, false, GSR_COLOR_COMP_RGB);
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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) {
|
||||
@@ -120,11 +124,10 @@ static bool gsr_video_encoder_vaapi_setup_textures(gsr_video_encoder_vaapi *self
|
||||
return false;
|
||||
}
|
||||
|
||||
const float border_color[4] = { 0.0f, 0.0f, 0.0f, 0.0f };
|
||||
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_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->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);
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
89
src/main.cpp
89
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;
|
||||
}
|
||||
@@ -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");
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
@@ -3735,7 +3757,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 +3778,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 +3831,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 +3846,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 +3878,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 +3898,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 +4025,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 +4092,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);
|
||||
|
||||
@@ -671,12 +671,19 @@ 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);
|
||||
|
||||
const float border_color[4] = { 0.0f, 0.0f, 0.0f, 0.0f };
|
||||
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);
|
||||
|
||||
@@ -130,6 +130,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 = {
|
||||
|
||||
Reference in New Issue
Block a user