Compare commits

...

11 Commits
5.6.7 ... 5.7.0

23 changed files with 140 additions and 548 deletions

View File

@@ -27,30 +27,6 @@ Supported image formats:
* PNG
This software works on X11 and Wayland on AMD, Intel and NVIDIA.
### TEMPORARY ISSUES
1) Videos are in variable framerate format. Use MPV to play such videos, otherwise you might experience stuttering in the video if you are using a buggy video player. You can try saving the video into a .mkv file instead as some software may have better support for .mkv files (such as kdenlive). You can use the "-fm cfr" option to to use constant framerate mode.
2) FLAC audio codec is disabled at the moment because of temporary issues.
### AMD/Intel/Wayland root permission
When recording a window or when using the `-w portal` option no special user permission is required,
however when recording a monitor the program needs root permission (to access KMS).\
This is safe in GPU Screen Recorder as the part that needs root access has been moved to its own small program that only does one thing.\
For you as a user this only means that if you installed GPU Screen Recorder as a flatpak then a prompt asking for root password will show up once when you start recording.
# Performance
On a system with a i5 4690k CPU and a GTX 1080 GPU:\
When recording Legend of Zelda Breath of the Wild at 4k, fps drops from 30 to 7 when using OBS Studio + nvenc, however when using this screen recorder the fps remains at 30.\
When recording GTA V at 4k on highest settings, fps drops from 60 to 23 when using obs-nvfbc + nvenc, however when using this screen recorder the fps only drops to 58.\
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) and [https://www.youtube.com/watch?v=aK67RSZw2ZQ](https://www.youtube.com/watch?v=aK67RSZw2ZQ).\
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 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.\
Note that this only works when Xorg server is running as root, and using this option will only give you a performance boost if the game you are recording is bottlenecked by your GPU.\
Note! use at your own risk!
# VRR/G-SYNC
This should work fine on AMD/Intel X11 or Wayland. On Nvidia X11 G-SYNC only works with the -w screen-direct option, but because of bugs in the Nvidia driver this option is not always recommended.
For example it can cause your computer to freeze when recording certain games.
# Installation
If you are running an Arch Linux based distro then you can find gpu screen recorder on aur under the name gpu-screen-recorder (`yay -S gpu-screen-recorder`).\
@@ -88,6 +64,11 @@ These are the dependencies needed to build GPU Screen Recorder:
* libcap
* wayland (wayland-client, wayland-egl, wayland-scanner)
## Optional dependencies
When building GPU Screen Recorder with portal support (`-Dportal=true` meson option, which is enabled by default) these dependencies are also needed:
* libdbus
* libpipewire (and libspa which is usually part of libpipewire)
## Runtime dependencies
* libglvnd (which provides libgl, libglx and libegl) is needed. Your system needs to support at least OpenGL ES 3.0 (released in 2012)
@@ -107,11 +88,6 @@ There are also additional dependencies needed at runtime depending on your GPU v
* nvfbc (libnvidia-fbc1, when recording the screen on x11)
* xnvctrl (libxnvctrl0, when using the `-oc` option)
## Optional dependencies
When building GPU Screen Recorder with portal support (`-Dportal=true` meson option, which is enabled by default) these dependencies are also needed:
* libdbus
* libpipewire (and libspa which is usually part of libpipewire)
# How to use
Run `gpu-screen-recorder --help` to see all options and also examples.\
There is also a gui for the gpu screen recorder called [GPU Screen Recorder GTK](https://git.dec05eba.com/gpu-screen-recorder-gtk/).\
@@ -181,12 +157,38 @@ You have to reboot your computer after installing GPU Screen Recorder for the fi
# Examples
Look at the [scripts](https://git.dec05eba.com/gpu-screen-recorder/tree/scripts) directory for script examples. For example if you want to automatically save a recording/replay into a folder with the same name as the game you are recording.
# AMD/Intel/Wayland root permission
When recording a window or when using the `-w portal` option no special user permission is required,
however when recording a monitor the program needs root permission (to access KMS).\
This is safe in GPU Screen Recorder as the part that needs root access has been moved to its own small program that only does one thing.\
For you as a user this only means that if you installed GPU Screen Recorder as a flatpak then a prompt asking for root password will show up once when you start recording.
# Performance
On a system with a i5 4690k CPU and a GTX 1080 GPU:\
When recording Legend of Zelda Breath of the Wild at 4k, fps drops from 30 to 7 when using OBS Studio + nvenc, however when using this screen recorder the fps remains at 30.\
When recording GTA V at 4k on highest settings, fps drops from 60 to 23 when using obs-nvfbc + nvenc, however when using this screen recorder the fps only drops to 58.\
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) and [https://www.youtube.com/watch?v=aK67RSZw2ZQ](https://www.youtube.com/watch?v=aK67RSZw2ZQ).\
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 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.\
Note that this only works when Xorg server is running as root, and using this option will only give you a performance boost if the game you are recording is bottlenecked by your GPU.\
Note! use at your own risk!
# VRR/G-SYNC
This should work fine on AMD/Intel X11 or Wayland. On Nvidia X11 G-SYNC only works with the -w screen-direct option, but because of bugs in the Nvidia driver this option is not always recommended.
For example it can cause your computer to freeze when recording certain games.
# Reporting bugs, contributing patches, questions or donation
See [https://git.dec05eba.com/?p=about](https://git.dec05eba.com/?p=about).
# Demo
[![Click here to watch a demo video on youtube](https://img.youtube.com/vi/n5tm0g01n6A/0.jpg)](https://www.youtube.com/watch?v=n5tm0g01n6A)
# TEMPORARY ISSUES
1) Videos are in variable framerate format. Use MPV to play such videos, otherwise you might experience stuttering in the video if you are using a buggy video player. You can try saving the video into a .mkv file instead as some software may have better support for .mkv files (such as kdenlive). You can use the "-fm cfr" option to to use constant framerate mode.
2) FLAC audio codec is disabled at the moment because of temporary issues.
# FAQ
## It tells me that my AMD/Intel GPU is not supported or that my GPU doesn't support h264/hevc, but that's not true!
Some linux distros (such as manjaro and fedora) disable hardware accelerated h264/hevc on AMD/Intel because of "patent license issues". If you are using an arch-based distro then you can install mesa-git instead of mesa and if you are using another distro then you may have to switch to a better distro. On fedora based distros you can follow this: [Hardware Accelerated Codec](https://rpmfusion.org/Howto/Multimedia).\

18
TODO
View File

@@ -244,8 +244,6 @@ 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.
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.
@@ -322,4 +320,18 @@ KDE Plasma Wayland seems to use overlay planes now in non-fullscreen mode(limite
If it is, then support it in kms capture.
Check if pipewire audio link-factory is available before attempting to use app audio or merging audio with pipewire.
Also do the same in supports_app_audio check in gpu-screen-recorder --info output.
Also do the same in supports_app_audio check in gpu-screen-recorder --info output.
Move region capture to an option in the color conversion instead of having the region code in kms capture code. This would make it cleaner and make it work with all capture methods.
-w region would just be an option for selecting the monitor and -region would work with all capture methods (-w).
Set top level window argument for portal capture. Same for gpu-screen-recorder-gtk global shortcuts.
Remove unix domain socket code from kms-client/server and use socketpair directly. To make this possible always execute the kms server permission setup in flatpak, before starting recording (in gpu-screen-recorder-gtk).
Application audio capture isn't good enough. It creates a sink that for some automatically gets selected as the default output device and it's visible as an output device.
When shutting down gpu screen recorder it will also cause audio applications to pause.
Fix some of these issues by setting gsr-app-sink media class to "Stream/Input/Audio" and node.virtual=true.
However that causes pulseaudio to be unable to record from gsr-app-sink, and it ends up being stuck in pa_sound_device_handle_reconnect in the loop with pa_mainloop_iterate.
Add -k best/best_hdr/best_10bit option, to automatically choose the best codec (prefer av1, then hevc and then h264. For webm files choose vp9 and then vp8).

View File

@@ -6,7 +6,6 @@
#include "vec2.h"
#include <stdbool.h>
#define GSR_COLOR_CONVERSION_MAX_COMPUTE_SHADERS 12
#define GSR_COLOR_CONVERSION_MAX_GRAPHICS_SHADERS 6
#define GSR_COLOR_CONVERSION_MAX_FRAMEBUFFERS 2
@@ -33,44 +32,29 @@ typedef struct {
int offset;
} gsr_color_graphics_uniforms;
typedef struct {
int rotation_matrix;
int source_position;
int target_position;
int scale;
} gsr_color_compute_uniforms;
typedef struct {
gsr_egl *egl;
gsr_destination_color destination_color;
unsigned int destination_textures[2];
vec2i destination_textures_size[2];
int num_destination_textures;
gsr_color_range color_range;
bool load_external_image_shader;
bool force_graphics_shader;
} gsr_color_conversion_params;
typedef struct {
gsr_color_conversion_params params;
gsr_color_compute_uniforms compute_uniforms[GSR_COLOR_CONVERSION_MAX_COMPUTE_SHADERS];
gsr_shader compute_shaders[GSR_COLOR_CONVERSION_MAX_COMPUTE_SHADERS];
/* These are only loader if compute shaders (of the same type) fail to load */
gsr_color_graphics_uniforms graphics_uniforms[GSR_COLOR_CONVERSION_MAX_GRAPHICS_SHADERS];
gsr_shader graphics_shaders[GSR_COLOR_CONVERSION_MAX_GRAPHICS_SHADERS];
bool compute_shaders_failed_to_load;
bool external_compute_shaders_failed_to_load;
unsigned int framebuffers[GSR_COLOR_CONVERSION_MAX_FRAMEBUFFERS];
unsigned int vertex_array_object_id;
unsigned int vertex_buffer_object_id;
int max_local_size_dim;
} gsr_color_conversion;
int gsr_color_conversion_init(gsr_color_conversion *self, const gsr_color_conversion_params *params);

View File

@@ -118,8 +118,6 @@ typedef void(*__GLXextFuncPtr)(void);
#define GL_TEXTURE_WRAP_T 0x2803
#define GL_TEXTURE_MAG_FILTER 0x2800
#define GL_TEXTURE_MIN_FILTER 0x2801
#define GL_TEXTURE_WIDTH 0x1000
#define GL_TEXTURE_HEIGHT 0x1001
#define GL_NEAREST 0x2600
#define GL_CLAMP_TO_EDGE 0x812F
#define GL_LINEAR 0x2601
@@ -138,7 +136,6 @@ typedef void(*__GLXextFuncPtr)(void);
#define GL_READ_ONLY 0x88B8
#define GL_WRITE_ONLY 0x88B9
#define GL_READ_WRITE 0x88BA
#define GL_MAX_COMPUTE_FIXED_GROUP_INVOCATIONS 0x90EB
#define GL_TEXTURE0 0x84C0
#define GL_TEXTURE1 0x84C1
#define GL_SHADER_IMAGE_ACCESS_BARRIER_BIT 0x00000020
@@ -152,7 +149,6 @@ typedef void(*__GLXextFuncPtr)(void);
#define GL_INFO_LOG_LENGTH 0x8B84
#define GL_FRAGMENT_SHADER 0x8B30
#define GL_VERTEX_SHADER 0x8B31
#define GL_COMPUTE_SHADER 0x91B9
#define GL_COMPILE_STATUS 0x8B81
#define GL_LINK_STATUS 0x8B82
@@ -260,7 +256,6 @@ struct gsr_egl {
void (*glTexParameteri)(unsigned int target, unsigned int pname, int param);
void (*glTexParameteriv)(unsigned int target, unsigned int pname, const int *params);
void (*glTexParameterfv)(unsigned int target, unsigned int pname, const float *params);
void (*glGetTexLevelParameteriv)(unsigned int target, int level, unsigned int pname, int *params);
void (*glTexImage2D)(unsigned int target, int level, int internalFormat, int width, int height, int border, unsigned int format, unsigned int type, const void *pixels);
void (*glTexSubImage2D)(unsigned int target, int level, int xoffset, int yoffset, int width, int height, unsigned format, unsigned type, const void *pixels);
void (*glTexStorage2D)(unsigned int target, int levels, unsigned int internalformat, int width, int height);
@@ -268,7 +263,7 @@ struct gsr_egl {
void (*glGenFramebuffers)(int n, unsigned int *framebuffers);
void (*glBindFramebuffer)(unsigned int target, unsigned int framebuffer);
void (*glDeleteFramebuffers)(int n, const unsigned int *framebuffers);
void (*glDispatchCompute)(unsigned int num_groups_x, unsigned int num_groups_y, unsigned int num_groups_z);
/* TODO: Note: this is only available since OpenGL ES 3.1 */
void (*glMemoryBarrier)(unsigned int barriers);
void (*glViewport)(int x, int y, int width, int height);
void (*glFramebufferTexture2D)(unsigned int target, unsigned int attachment, unsigned int textarget, unsigned int texture, int level);

View File

@@ -15,7 +15,7 @@ struct gsr_video_encoder {
void (*destroy)(gsr_video_encoder *encoder, AVCodecContext *video_codec_context);
void (*copy_textures_to_frame)(gsr_video_encoder *encoder, AVFrame *frame, gsr_color_conversion *color_conversion); /* Can be NULL */
/* |textures| should be able to fit 2 elements */
void (*get_textures)(gsr_video_encoder *encoder, unsigned int *textures, int *num_textures, gsr_destination_color *destination_color);
void (*get_textures)(gsr_video_encoder *encoder, unsigned int *textures, vec2i *texture_sizes, int *num_textures, gsr_destination_color *destination_color);
void *priv;
bool started;
@@ -25,6 +25,6 @@ struct gsr_video_encoder {
bool gsr_video_encoder_start(gsr_video_encoder *encoder, AVCodecContext *video_codec_context, AVFrame *frame);
void gsr_video_encoder_destroy(gsr_video_encoder *encoder, AVCodecContext *video_codec_context);
void gsr_video_encoder_copy_textures_to_frame(gsr_video_encoder *encoder, AVFrame *frame, gsr_color_conversion *color_conversion);
void gsr_video_encoder_get_textures(gsr_video_encoder *encoder, unsigned int *textures, int *num_textures, gsr_destination_color *destination_color);
void gsr_video_encoder_get_textures(gsr_video_encoder *encoder, unsigned int *textures, vec2i *texture_sizes, int *num_textures, gsr_destination_color *destination_color);
#endif /* GSR_ENCODER_VIDEO_H */

View File

@@ -11,7 +11,7 @@ typedef struct {
} gsr_shader;
/* |vertex_shader| or |fragment_shader| may be NULL */
int gsr_shader_init(gsr_shader *self, gsr_egl *egl, const char *vertex_shader, const char *fragment_shader, const char *compute_shader);
int gsr_shader_init(gsr_shader *self, gsr_egl *egl, const char *vertex_shader, const char *fragment_shader);
void gsr_shader_deinit(gsr_shader *self);
int gsr_shader_bind_attribute_location(gsr_shader *self, const char *attribute, int location);

View File

@@ -11,6 +11,8 @@ typedef struct {
unsigned int texture_id;
int redirected;
gsr_egl *egl;
unsigned int window_width;
unsigned int window_height;
} WindowTexture;
/* Returns 0 on success */

View File

@@ -1,4 +1,4 @@
project('gpu-screen-recorder', ['c', 'cpp'], version : '5.6.7', default_options : ['warning_level=2'])
project('gpu-screen-recorder', ['c', 'cpp'], version : '5.7.0', default_options : ['warning_level=2'])
add_project_arguments('-Wshadow', language : ['c', 'cpp'])
if get_option('buildtype') == 'debug'

View File

@@ -7,7 +7,9 @@ extern "C" {
#define GSR_PLUGIN_INTERFACE_MAJOR_VERSION 0
#define GSR_PLUGIN_INTERFACE_MINOR_VERSION 1
#define GSR_PLUGIN_INTERFACE_VERSION ((GSR_PLUGIN_INTERFACE_MAJOR_VERSION << 16) | GSR_PLUGIN_INTERFACE_MINOR_VERSION)
#define GSR_PLUGIN_INTERFACE_MAKE_VERSION(major, minor) (((major) << 16) | (minor))
#define GSR_PLUGIN_INTERFACE_VERSION GSR_PLUGIN_INTERFACE_MAKE_VERSION(GSR_PLUGIN_INTERFACE_MAJOR_VERSION, GSR_PLUGIN_INTERFACE_MINOR_VERSION)
#include <stdbool.h>

View File

@@ -1,7 +1,7 @@
[package]
name = "gpu-screen-recorder"
type = "executable"
version = "5.6.7"
version = "5.7.0"
platforms = ["posix"]
[config]

View File

@@ -100,13 +100,8 @@ static int gsr_capture_xcomposite_start(gsr_capture *cap, gsr_capture_metadata *
return -1;
}
self->texture_size.x = 0;
self->texture_size.y = 0;
self->params.egl->glBindTexture(GL_TEXTURE_2D, window_texture_get_opengl_texture_id(&self->window_texture));
self->params.egl->glGetTexLevelParameteriv(GL_TEXTURE_2D, 0, GL_TEXTURE_WIDTH, &self->texture_size.x);
self->params.egl->glGetTexLevelParameteriv(GL_TEXTURE_2D, 0, GL_TEXTURE_HEIGHT, &self->texture_size.y);
self->params.egl->glBindTexture(GL_TEXTURE_2D, 0);
self->texture_size.x = self->window_texture.window_width;
self->texture_size.y = self->window_texture.window_height;
if(self->params.output_resolution.x == 0 && self->params.output_resolution.y == 0) {
capture_metadata->width = self->texture_size.x;
@@ -148,13 +143,8 @@ static void gsr_capture_xcomposite_tick(gsr_capture *cap) {
window_texture_deinit(&self->window_texture);
window_texture_init(&self->window_texture, self->display, self->window, self->params.egl); // TODO: Do not do the below window_texture_on_resize after this
self->texture_size.x = 0;
self->texture_size.y = 0;
self->params.egl->glBindTexture(GL_TEXTURE_2D, window_texture_get_opengl_texture_id(&self->window_texture));
self->params.egl->glGetTexLevelParameteriv(GL_TEXTURE_2D, 0, GL_TEXTURE_WIDTH, &self->texture_size.x);
self->params.egl->glGetTexLevelParameteriv(GL_TEXTURE_2D, 0, GL_TEXTURE_HEIGHT, &self->texture_size.y);
self->params.egl->glBindTexture(GL_TEXTURE_2D, 0);
self->texture_size.x = self->window_texture.window_width;
self->texture_size.y = self->window_texture.window_height;
self->window_resized = false;
self->clear_background = true;
@@ -172,13 +162,8 @@ static void gsr_capture_xcomposite_tick(gsr_capture *cap) {
return;
}
self->texture_size.x = 0;
self->texture_size.y = 0;
self->params.egl->glBindTexture(GL_TEXTURE_2D, window_texture_get_opengl_texture_id(&self->window_texture));
self->params.egl->glGetTexLevelParameteriv(GL_TEXTURE_2D, 0, GL_TEXTURE_WIDTH, &self->texture_size.x);
self->params.egl->glGetTexLevelParameteriv(GL_TEXTURE_2D, 0, GL_TEXTURE_HEIGHT, &self->texture_size.y);
self->params.egl->glBindTexture(GL_TEXTURE_2D, 0);
self->texture_size.x = self->window_texture.window_width;
self->texture_size.y = self->window_texture.window_height;
self->clear_background = true;
}

View File

@@ -1,23 +1,9 @@
#include "../include/color_conversion.h"
#include "../include/egl.h"
#include <stdio.h>
#include <math.h>
#include <string.h>
#include <assert.h>
#define COMPUTE_SHADER_INDEX_Y 0
#define COMPUTE_SHADER_INDEX_UV 1
#define COMPUTE_SHADER_INDEX_Y_EXTERNAL 2
#define COMPUTE_SHADER_INDEX_UV_EXTERNAL 3
#define COMPUTE_SHADER_INDEX_RGB 4
#define COMPUTE_SHADER_INDEX_RGB_EXTERNAL 5
#define COMPUTE_SHADER_INDEX_Y_BLEND 6
#define COMPUTE_SHADER_INDEX_UV_BLEND 7
#define COMPUTE_SHADER_INDEX_Y_EXTERNAL_BLEND 8
#define COMPUTE_SHADER_INDEX_UV_EXTERNAL_BLEND 9
#define COMPUTE_SHADER_INDEX_RGB_BLEND 10
#define COMPUTE_SHADER_INDEX_RGB_EXTERNAL_BLEND 11
#define GRAPHICS_SHADER_INDEX_Y 0
#define GRAPHICS_SHADER_INDEX_UV 1
#define GRAPHICS_SHADER_INDEX_Y_EXTERNAL 2
@@ -85,156 +71,6 @@ static const char* color_format_range_get_transform_matrix(gsr_destination_color
return NULL;
}
static void get_compute_shader_header(char *header, size_t header_size, bool external_texture) {
if(external_texture) {
snprintf(header, header_size,
"#version 310 es\n"
"#extension GL_OES_EGL_image_external : enable\n"
"#extension GL_OES_EGL_image_external_essl3 : require\n"
"layout(binding = 0) uniform highp samplerExternalOES img_input;\n"
"layout(binding = 1) uniform highp sampler2D img_background;\n");
} else {
snprintf(header, header_size,
"#version 310 es\n"
"layout(binding = 0) uniform highp sampler2D img_input;\n"
"layout(binding = 1) uniform highp sampler2D img_background;\n");
}
}
static int load_compute_shader_y(gsr_shader *shader, gsr_egl *egl, gsr_color_compute_uniforms *uniforms, int max_local_size_dim, gsr_destination_color color_format, gsr_color_range color_range, bool external_texture, bool alpha_blending) {
const char *color_transform_matrix = color_format_range_get_transform_matrix(color_format, color_range);
char header[512];
get_compute_shader_header(header, sizeof(header), external_texture);
char compute_shader[4096];
snprintf(compute_shader, sizeof(compute_shader),
"%s"
"layout (local_size_x = %d, local_size_y = %d, local_size_z = 1) in;\n"
"precision highp float;\n"
"uniform ivec2 source_position;\n"
"uniform ivec2 target_position;\n"
"uniform vec2 scale;\n"
"uniform mat2 rotation_matrix;\n"
"layout(rgba8, binding = 0) writeonly uniform highp image2D img_output;\n"
"%s"
"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_shift) * rotation_matrix + vec2(size_shift) + 0.5;\n"
" vec2 output_texel_coord = vec2(texel_coord - source_position + target_position) + 0.5;\n"
" vec2 source_color_coords = rotated_texel_coord/vec2(size);\n"
" vec4 source_color = texture(img_input, source_color_coords);\n"
" if(source_color_coords.x > 1.0 || source_color_coords.y > 1.0)\n"
" source_color.rgba = vec4(0.0, 0.0, 0.0, %s);\n"
" vec4 source_color_yuv = RGBtoYUV * vec4(source_color.rgb, 1.0);\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", header, max_local_size_dim, max_local_size_dim, color_transform_matrix,
alpha_blending ? "0.0" : "1.0",
alpha_blending ? "texture(img_background, output_texel_coord/vec2(output_size))" : "source_color_yuv");
if(gsr_shader_init(shader, egl, NULL, NULL, compute_shader) != 0)
return -1;
uniforms->source_position = egl->glGetUniformLocation(shader->program_id, "source_position");
uniforms->target_position = egl->glGetUniformLocation(shader->program_id, "target_position");
uniforms->rotation_matrix = egl->glGetUniformLocation(shader->program_id, "rotation_matrix");
uniforms->scale = egl->glGetUniformLocation(shader->program_id, "scale");
return 0;
}
static int load_compute_shader_uv(gsr_shader *shader, gsr_egl *egl, gsr_color_compute_uniforms *uniforms, int max_local_size_dim, gsr_destination_color color_format, gsr_color_range color_range, bool external_texture, bool alpha_blending) {
const char *color_transform_matrix = color_format_range_get_transform_matrix(color_format, color_range);
char header[512];
get_compute_shader_header(header, sizeof(header), external_texture);
char compute_shader[4096];
snprintf(compute_shader, sizeof(compute_shader),
"%s"
"layout (local_size_x = %d, local_size_y = %d, local_size_z = 1) in;\n"
"precision highp float;\n"
"uniform ivec2 source_position;\n"
"uniform ivec2 target_position;\n"
"uniform vec2 scale;\n"
"uniform mat2 rotation_matrix;\n"
"layout(rgba8, binding = 0) writeonly uniform highp image2D img_output;\n"
"%s"
"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 - size_shift) * rotation_matrix + vec2(size_shift) + 0.5;\n"
" vec2 output_texel_coord = vec2(texel_coord - source_position + target_position) + 0.5;\n"
" vec2 source_color_coords = rotated_texel_coord/vec2(size>>1);\n"
" vec4 source_color = texture(img_input, source_color_coords);\n" // size/2
" if(source_color_coords.x > 1.0 || source_color_coords.y > 1.0)\n"
" source_color.rgba = vec4(0.0, 0.0, 0.0, %s);\n"
" vec4 source_color_yuv = RGBtoYUV * vec4(source_color.rgb, 1.0);\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, vec4(uv_color, 1.0, 1.0));\n"
"}\n", header, max_local_size_dim, max_local_size_dim, color_transform_matrix,
alpha_blending ? "0.0" : "1.0",
alpha_blending ? "texture(img_background, output_texel_coord/vec2(output_size))" : "source_color_yuv");
if(gsr_shader_init(shader, egl, NULL, NULL, compute_shader) != 0)
return -1;
uniforms->source_position = egl->glGetUniformLocation(shader->program_id, "source_position");
uniforms->target_position = egl->glGetUniformLocation(shader->program_id, "target_position");
uniforms->rotation_matrix = egl->glGetUniformLocation(shader->program_id, "rotation_matrix");
uniforms->scale = egl->glGetUniformLocation(shader->program_id, "scale");
return 0;
}
static int load_compute_shader_rgb(gsr_shader *shader, gsr_egl *egl, gsr_color_compute_uniforms *uniforms, int max_local_size_dim, bool external_texture, bool alpha_blending) {
char header[512];
get_compute_shader_header(header, sizeof(header), external_texture);
char compute_shader[4096];
snprintf(compute_shader, sizeof(compute_shader),
"%s"
"layout (local_size_x = %d, local_size_y = %d, local_size_z = 1) in;\n"
"precision highp float;\n"
"uniform ivec2 source_position;\n"
"uniform ivec2 target_position;\n"
"uniform vec2 scale;\n"
"uniform mat2 rotation_matrix;\n"
"layout(rgba8, binding = 0) writeonly uniform highp image2D img_output;\n"
"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_shift) * rotation_matrix + vec2(size_shift) + 0.5;\n"
" vec2 output_texel_coord = vec2(texel_coord - source_position + target_position) + 0.5;\n"
" vec2 source_color_coords = rotated_texel_coord/vec2(size);\n"
" vec4 source_color = texture(img_input, source_color_coords);\n"
" if(source_color_coords.x > 1.0 || source_color_coords.y > 1.0)\n"
" source_color.rgba = vec4(0.0, 0.0, 0.0, %s);\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", header, max_local_size_dim, max_local_size_dim,
alpha_blending ? "0.0" : "1.0",
alpha_blending ? "texture(img_background, output_texel_coord/vec2(output_size))" : "source_color");
if(gsr_shader_init(shader, egl, NULL, NULL, compute_shader) != 0)
return -1;
uniforms->source_position = egl->glGetUniformLocation(shader->program_id, "source_position");
uniforms->target_position = egl->glGetUniformLocation(shader->program_id, "target_position");
uniforms->rotation_matrix = egl->glGetUniformLocation(shader->program_id, "rotation_matrix");
uniforms->scale = egl->glGetUniformLocation(shader->program_id, "scale");
return 0;
}
static int load_graphics_shader_y(gsr_shader *shader, gsr_egl *egl, gsr_color_graphics_uniforms *uniforms, gsr_destination_color color_format, gsr_color_range color_range, bool external_texture) {
const char *color_transform_matrix = color_format_range_get_transform_matrix(color_format, color_range);
@@ -287,7 +123,7 @@ static int load_graphics_shader_y(gsr_shader *shader, gsr_egl *egl, gsr_color_gr
"} \n", color_transform_matrix, main_code);
}
if(gsr_shader_init(shader, egl, vertex_shader, fragment_shader, NULL) != 0)
if(gsr_shader_init(shader, egl, vertex_shader, fragment_shader) != 0)
return -1;
gsr_shader_bind_attribute_location(shader, "pos", 0);
@@ -349,7 +185,7 @@ static unsigned int load_graphics_shader_uv(gsr_shader *shader, gsr_egl *egl, gs
"} \n", color_transform_matrix, main_code);
}
if(gsr_shader_init(shader, egl, vertex_shader, fragment_shader, NULL) != 0)
if(gsr_shader_init(shader, egl, vertex_shader, fragment_shader) != 0)
return -1;
gsr_shader_bind_attribute_location(shader, "pos", 0);
@@ -406,7 +242,7 @@ static unsigned int load_graphics_shader_rgb(gsr_shader *shader, gsr_egl *egl, g
"} \n", main_code);
}
if(gsr_shader_init(shader, egl, vertex_shader, fragment_shader, NULL) != 0)
if(gsr_shader_init(shader, egl, vertex_shader, fragment_shader) != 0)
return -1;
gsr_shader_bind_attribute_location(shader, "pos", 0);
@@ -465,88 +301,6 @@ static int create_vertices(gsr_color_conversion *self) {
return 0;
}
static bool gsr_color_conversion_load_compute_shaders(gsr_color_conversion *self) {
switch(self->params.destination_color) {
case GSR_DESTINATION_COLOR_NV12:
case GSR_DESTINATION_COLOR_P010: {
if(load_compute_shader_y(&self->compute_shaders[COMPUTE_SHADER_INDEX_Y], self->params.egl, &self->compute_uniforms[COMPUTE_SHADER_INDEX_Y], self->max_local_size_dim, self->params.destination_color, self->params.color_range, false, false) != 0) {
fprintf(stderr, "gsr error: gsr_color_conversion_init: failed to load Y compute shader\n");
return false;
}
if(load_compute_shader_uv(&self->compute_shaders[COMPUTE_SHADER_INDEX_UV], self->params.egl, &self->compute_uniforms[COMPUTE_SHADER_INDEX_UV], self->max_local_size_dim, self->params.destination_color, self->params.color_range, false, false) != 0) {
fprintf(stderr, "gsr error: gsr_color_conversion_init: failed to load UV compute shader\n");
return false;
}
if(load_compute_shader_y(&self->compute_shaders[COMPUTE_SHADER_INDEX_Y_BLEND], self->params.egl, &self->compute_uniforms[COMPUTE_SHADER_INDEX_Y_BLEND], self->max_local_size_dim, self->params.destination_color, self->params.color_range, false, true) != 0) {
fprintf(stderr, "gsr error: gsr_color_conversion_init: failed to load Y compute shader\n");
return false;
}
if(load_compute_shader_uv(&self->compute_shaders[COMPUTE_SHADER_INDEX_UV_BLEND], self->params.egl, &self->compute_uniforms[COMPUTE_SHADER_INDEX_UV_BLEND], self->max_local_size_dim, self->params.destination_color, self->params.color_range, false, true) != 0) {
fprintf(stderr, "gsr error: gsr_color_conversion_init: failed to load UV compute shader\n");
return false;
}
break;
}
case GSR_DESTINATION_COLOR_RGB8: {
if(load_compute_shader_rgb(&self->compute_shaders[COMPUTE_SHADER_INDEX_RGB], self->params.egl, &self->compute_uniforms[COMPUTE_SHADER_INDEX_RGB], self->max_local_size_dim, false, false) != 0) {
fprintf(stderr, "gsr error: gsr_color_conversion_init: failed to load Y compute shader\n");
return false;
}
if(load_compute_shader_rgb(&self->compute_shaders[COMPUTE_SHADER_INDEX_RGB_BLEND], self->params.egl, &self->compute_uniforms[COMPUTE_SHADER_INDEX_RGB_BLEND], self->max_local_size_dim, false, true) != 0) {
fprintf(stderr, "gsr error: gsr_color_conversion_init: failed to load Y compute shader\n");
return false;
}
break;
}
}
return true;
}
static bool gsr_color_conversion_load_external_compute_shaders(gsr_color_conversion *self) {
switch(self->params.destination_color) {
case GSR_DESTINATION_COLOR_NV12:
case GSR_DESTINATION_COLOR_P010: {
if(load_compute_shader_y(&self->compute_shaders[COMPUTE_SHADER_INDEX_Y_EXTERNAL], self->params.egl, &self->compute_uniforms[COMPUTE_SHADER_INDEX_Y_EXTERNAL], self->max_local_size_dim, self->params.destination_color, self->params.color_range, true, false) != 0) {
fprintf(stderr, "gsr error: gsr_color_conversion_init: failed to load Y compute shader\n");
return false;
}
if(load_compute_shader_uv(&self->compute_shaders[COMPUTE_SHADER_INDEX_UV_EXTERNAL], self->params.egl, &self->compute_uniforms[COMPUTE_SHADER_INDEX_UV_EXTERNAL], self->max_local_size_dim, self->params.destination_color, self->params.color_range, true, false) != 0) {
fprintf(stderr, "gsr error: gsr_color_conversion_init: failed to load UV compute shader\n");
return false;
}
if(load_compute_shader_y(&self->compute_shaders[COMPUTE_SHADER_INDEX_Y_EXTERNAL_BLEND], self->params.egl, &self->compute_uniforms[COMPUTE_SHADER_INDEX_Y_EXTERNAL_BLEND], self->max_local_size_dim, self->params.destination_color, self->params.color_range, true, true) != 0) {
fprintf(stderr, "gsr error: gsr_color_conversion_init: failed to load Y compute shader\n");
return false;
}
if(load_compute_shader_uv(&self->compute_shaders[COMPUTE_SHADER_INDEX_UV_EXTERNAL_BLEND], self->params.egl, &self->compute_uniforms[COMPUTE_SHADER_INDEX_UV_EXTERNAL_BLEND], self->max_local_size_dim, self->params.destination_color, self->params.color_range, true, true) != 0) {
fprintf(stderr, "gsr error: gsr_color_conversion_init: failed to load UV compute shader\n");
return false;
}
break;
}
case GSR_DESTINATION_COLOR_RGB8: {
if(load_compute_shader_rgb(&self->compute_shaders[COMPUTE_SHADER_INDEX_RGB_EXTERNAL], self->params.egl, &self->compute_uniforms[COMPUTE_SHADER_INDEX_RGB_EXTERNAL], self->max_local_size_dim, true, false) != 0) {
fprintf(stderr, "gsr error: gsr_color_conversion_init: failed to load Y compute shader\n");
return false;
}
if(load_compute_shader_rgb(&self->compute_shaders[COMPUTE_SHADER_INDEX_RGB_EXTERNAL_BLEND], self->params.egl, &self->compute_uniforms[COMPUTE_SHADER_INDEX_RGB_EXTERNAL_BLEND], self->max_local_size_dim, true, true) != 0) {
fprintf(stderr, "gsr error: gsr_color_conversion_init: failed to load Y compute shader\n");
return false;
}
break;
}
}
return true;
}
static bool gsr_color_conversion_load_graphics_shaders(gsr_color_conversion *self) {
switch(self->params.destination_color) {
case GSR_DESTINATION_COLOR_NV12:
@@ -605,10 +359,6 @@ int gsr_color_conversion_init(gsr_color_conversion *self, const gsr_color_conver
memset(self, 0, sizeof(*self));
self->params.egl = params->egl;
self->params = *params;
int max_compute_work_group_invocations = 256;
self->params.egl->glGetIntegerv(GL_MAX_COMPUTE_FIXED_GROUP_INVOCATIONS, &max_compute_work_group_invocations);
self->max_local_size_dim = sqrt(max_compute_work_group_invocations);
switch(self->params.destination_color) {
case GSR_DESTINATION_COLOR_NV12:
@@ -628,33 +378,12 @@ int gsr_color_conversion_init(gsr_color_conversion *self, const gsr_color_conver
}
}
if(self->params.force_graphics_shader) {
self->compute_shaders_failed_to_load = true;
self->external_compute_shaders_failed_to_load = true;
if(!gsr_color_conversion_load_graphics_shaders(self))
if(!gsr_color_conversion_load_graphics_shaders(self))
goto err;
if(self->params.load_external_image_shader) {
if(!gsr_color_conversion_load_external_graphics_shaders(self))
goto err;
if(self->params.load_external_image_shader) {
if(!gsr_color_conversion_load_external_graphics_shaders(self))
goto err;
}
} else {
if(!gsr_color_conversion_load_compute_shaders(self)) {
self->compute_shaders_failed_to_load = true;
fprintf(stderr, "gsr info: failed to load one or more compute shaders, run gpu-screen-recorder with the '-gl-debug yes' option to see why. Falling back to slower graphics shader instead\n");
if(!gsr_color_conversion_load_graphics_shaders(self))
goto err;
}
if(self->params.load_external_image_shader) {
if(!gsr_color_conversion_load_external_compute_shaders(self)) {
self->external_compute_shaders_failed_to_load = true;
fprintf(stderr, "gsr info: failed to load one or more external compute shaders, run gpu-screen-recorder with the '-gl-debug yes' option to see why. Falling back to slower graphics shader instead\n");
if(!gsr_color_conversion_load_external_graphics_shaders(self))
goto err;
}
}
}
if(load_framebuffers(self) != 0)
@@ -689,10 +418,6 @@ void gsr_color_conversion_deinit(gsr_color_conversion *self) {
self->framebuffers[i] = 0;
}
for(int i = 0; i < GSR_COLOR_CONVERSION_MAX_COMPUTE_SHADERS; ++i) {
gsr_shader_deinit(&self->compute_shaders[i]);
}
for(int i = 0; i < GSR_COLOR_CONVERSION_MAX_GRAPHICS_SHADERS; ++i) {
gsr_shader_deinit(&self->graphics_shaders[i]);
}
@@ -751,86 +476,8 @@ static void gsr_color_conversion_swizzle_reset(gsr_color_conversion *self, gsr_s
}
}
typedef enum {
GSR_COLOR_COMP_Y,
GSR_COLOR_COMP_UV,
GSR_COLOR_COMP_RGB
} gsr_color_component;
static int color_component_get_destination_texture_index(gsr_color_component color_component) {
switch(color_component) {
case GSR_COLOR_COMP_Y: return 0;
case GSR_COLOR_COMP_UV: return 1;
case GSR_COLOR_COMP_RGB: return 0;
}
assert(false);
return 0;
}
static unsigned int color_component_get_color_format(gsr_color_component color_component, bool use_16bit_colors) {
switch(color_component) {
case GSR_COLOR_COMP_Y: return use_16bit_colors ? GL_R16 : GL_R8;
case GSR_COLOR_COMP_UV: return use_16bit_colors ? GL_RG16 : GL_RG8;
case GSR_COLOR_COMP_RGB: return GL_RGBA8; // TODO: 16-bit color support
}
assert(false);
return GL_RGBA8;
}
static int color_component_get_COMPUTE_SHADER_INDEX(gsr_color_component color_component, bool external_texture, bool alpha_blending) {
switch(color_component) {
case GSR_COLOR_COMP_Y: {
if(external_texture)
return alpha_blending ? COMPUTE_SHADER_INDEX_Y_EXTERNAL_BLEND : COMPUTE_SHADER_INDEX_Y_EXTERNAL;
else
return alpha_blending ? COMPUTE_SHADER_INDEX_Y_BLEND : COMPUTE_SHADER_INDEX_Y;
}
case GSR_COLOR_COMP_UV: {
if(external_texture)
return alpha_blending ? COMPUTE_SHADER_INDEX_UV_EXTERNAL_BLEND : COMPUTE_SHADER_INDEX_UV_EXTERNAL;
else
return alpha_blending ? COMPUTE_SHADER_INDEX_UV_BLEND : COMPUTE_SHADER_INDEX_UV;
}
case GSR_COLOR_COMP_RGB: {
if(external_texture)
return alpha_blending ? COMPUTE_SHADER_INDEX_RGB_EXTERNAL_BLEND : COMPUTE_SHADER_INDEX_RGB_EXTERNAL;
else
return alpha_blending ? COMPUTE_SHADER_INDEX_RGB_BLEND : COMPUTE_SHADER_INDEX_RGB;
}
}
assert(false);
return COMPUTE_SHADER_INDEX_RGB;
}
static void gsr_color_conversion_dispatch_compute_shader(gsr_color_conversion *self, bool external_texture, bool alpha_blending, float rotation_matrix[2][2], vec2i source_position, vec2i destination_pos, vec2i destination_size, vec2f scale, bool use_16bit_colors, gsr_color_component color_component) {
const int compute_shader_index = color_component_get_COMPUTE_SHADER_INDEX(color_component, external_texture, alpha_blending);
const int destination_texture_index = color_component_get_destination_texture_index(color_component);
const unsigned int color_format = color_component_get_color_format(color_component, use_16bit_colors);
self->params.egl->glActiveTexture(GL_TEXTURE1);
self->params.egl->glBindTexture(GL_TEXTURE_2D, self->params.destination_textures[destination_texture_index]);
self->params.egl->glActiveTexture(GL_TEXTURE0);
gsr_color_compute_uniforms *uniform = &self->compute_uniforms[compute_shader_index];
gsr_shader_use(&self->compute_shaders[compute_shader_index]);
self->params.egl->glUniformMatrix2fv(uniform->rotation_matrix, 1, GL_TRUE, (const float*)rotation_matrix);
self->params.egl->glUniform2i(uniform->source_position, source_position.x, source_position.y);
self->params.egl->glUniform2i(uniform->target_position, destination_pos.x, destination_pos.y);
self->params.egl->glUniform2f(uniform->scale, scale.x, scale.y);
self->params.egl->glBindImageTexture(0, self->params.destination_textures[destination_texture_index], 0, GL_FALSE, 0, GL_WRITE_ONLY, color_format);
const double num_groups_x = ceil((double)destination_size.x/(double)self->max_local_size_dim);
const double num_groups_y = ceil((double)destination_size.y/(double)self->max_local_size_dim);
self->params.egl->glDispatchCompute(max_int(1, num_groups_x), max_int(1, num_groups_y), 1);
}
static void gsr_color_conversion_draw_graphics(gsr_color_conversion *self, unsigned int texture_id, bool external_texture, float rotation_matrix[2][2], vec2i source_position, vec2i source_size, vec2i destination_pos, vec2i texture_size, vec2f scale, gsr_source_color source_color) {
/* TODO: Do not call this every frame? */
vec2i dest_texture_size = {0, 0};
self->params.egl->glBindTexture(GL_TEXTURE_2D, self->params.destination_textures[0]);
self->params.egl->glGetTexLevelParameteriv(GL_TEXTURE_2D, 0, GL_TEXTURE_WIDTH, &dest_texture_size.x);
self->params.egl->glGetTexLevelParameteriv(GL_TEXTURE_2D, 0, GL_TEXTURE_HEIGHT, &dest_texture_size.y);
self->params.egl->glBindTexture(GL_TEXTURE_2D, 0);
const vec2i dest_texture_size = self->params.destination_textures_size[0];
const int texture_target = external_texture ? GL_TEXTURE_EXTERNAL_OES : GL_TEXTURE_2D;
self->params.egl->glBindTexture(texture_target, texture_id);
@@ -936,44 +583,9 @@ void gsr_color_conversion_draw(gsr_color_conversion *self, unsigned int texture_
self->params.egl->glBindTexture(texture_target, texture_id);
gsr_color_conversion_swizzle_texture_source(self, source_color);
const bool use_graphics_shader = external_texture ? self->external_compute_shaders_failed_to_load : self->compute_shaders_failed_to_load;
if(use_graphics_shader) {
source_position.x += source_pos.x;
source_position.y += source_pos.y;
gsr_color_conversion_draw_graphics(self, texture_id, external_texture, rotation_matrix, source_position, source_size, destination_pos, texture_size, scale, source_color);
} else {
switch(rotation) {
case GSR_ROT_0:
break;
case GSR_ROT_90:
source_position.x += (((double)texture_size.x*0.5 - (double)texture_size.y*0.5) * scale.x);
source_position.y += (((double)texture_size.y*0.5 - (double)texture_size.x*0.5) * scale.y);
break;
case GSR_ROT_180:
break;
case GSR_ROT_270:
source_position.x += (((double)texture_size.x*0.5 - (double)texture_size.y*0.5) * scale.x);
source_position.y += (((double)texture_size.y*0.5 - (double)texture_size.x*0.5) * scale.y);
break;
}
source_position.x -= (source_pos.x * scale.x + 0.5);
source_position.y -= (source_pos.y * scale.y + 0.5);
switch(self->params.destination_color) {
case GSR_DESTINATION_COLOR_NV12:
case GSR_DESTINATION_COLOR_P010: {
const bool use_16bit_colors = self->params.destination_color == GSR_DESTINATION_COLOR_P010;
gsr_color_conversion_dispatch_compute_shader(self, external_texture, alpha_blending, rotation_matrix, source_position, destination_pos, destination_size, scale, use_16bit_colors, GSR_COLOR_COMP_Y);
gsr_color_conversion_dispatch_compute_shader(self, external_texture, alpha_blending, rotation_matrix, (vec2i){source_position.x/2, source_position.y/2},
(vec2i){destination_pos.x/2, destination_pos.y/2}, (vec2i){destination_size.x/2, destination_size.y/2}, scale, use_16bit_colors, GSR_COLOR_COMP_UV);
break;
}
case GSR_DESTINATION_COLOR_RGB8: {
gsr_color_conversion_dispatch_compute_shader(self, external_texture, alpha_blending, rotation_matrix, source_position, destination_pos, destination_size, scale, false, GSR_COLOR_COMP_RGB);
break;
}
}
}
source_position.x += source_pos.x;
source_position.y += source_pos.y;
gsr_color_conversion_draw_graphics(self, texture_id, external_texture, rotation_matrix, source_position, source_size, destination_pos, texture_size, scale, source_color);
self->params.egl->glFlush();
// TODO: Use the minimal barrier required

View File

@@ -280,7 +280,6 @@ static bool gsr_egl_load_gl(gsr_egl *self, void *library) {
{ (void**)&self->glTexParameteri, "glTexParameteri" },
{ (void**)&self->glTexParameteriv, "glTexParameteriv" },
{ (void**)&self->glTexParameterfv, "glTexParameterfv" },
{ (void**)&self->glGetTexLevelParameteriv, "glGetTexLevelParameteriv" },
{ (void**)&self->glTexImage2D, "glTexImage2D" },
{ (void**)&self->glTexSubImage2D, "glTexSubImage2D" },
{ (void**)&self->glTexStorage2D, "glTexStorage2D" },
@@ -288,7 +287,6 @@ static bool gsr_egl_load_gl(gsr_egl *self, void *library) {
{ (void**)&self->glGenFramebuffers, "glGenFramebuffers" },
{ (void**)&self->glBindFramebuffer, "glBindFramebuffer" },
{ (void**)&self->glDeleteFramebuffers, "glDeleteFramebuffers" },
{ (void**)&self->glDispatchCompute, "glDispatchCompute" },
{ (void**)&self->glMemoryBarrier, "glMemoryBarrier" },
{ (void**)&self->glViewport, "glViewport" },
{ (void**)&self->glFramebufferTexture2D, "glFramebufferTexture2D" },

View File

@@ -12,6 +12,7 @@ typedef struct {
gsr_video_encoder_nvenc_params params;
unsigned int target_textures[2];
vec2i target_texture_size[2];
AVBufferRef *device_ctx;
@@ -95,7 +96,8 @@ static bool gsr_video_encoder_nvenc_setup_textures(gsr_video_encoder_nvenc *self
const int div[2] = {1, 2}; // divide UV texture size by 2 because chroma is half size
for(int i = 0; i < 2; ++i) {
self->target_textures[i] = gl_create_texture(self->params.egl, video_codec_context->width / div[i], video_codec_context->height / div[i], self->params.color_depth == GSR_COLOR_DEPTH_8_BITS ? internal_formats_nv12[i] : internal_formats_p010[i], formats[i], GL_NEAREST);
self->target_texture_size[i] = (vec2i){ video_codec_context->width / div[i], video_codec_context->height / div[i] };
self->target_textures[i] = gl_create_texture(self->params.egl, self->target_texture_size[i].x, self->target_texture_size[i].y, self->params.color_depth == GSR_COLOR_DEPTH_8_BITS ? internal_formats_nv12[i] : internal_formats_p010[i], formats[i], GL_NEAREST);
if(self->target_textures[i] == 0) {
fprintf(stderr, "gsr error: gsr_video_encoder_nvenc_setup_textures: failed to create opengl texture\n");
return false;
@@ -198,10 +200,12 @@ static void gsr_video_encoder_nvenc_copy_textures_to_frame(gsr_video_encoder *en
self->cuda.cuStreamSynchronize(self->cuda_stream);
}
static void gsr_video_encoder_nvenc_get_textures(gsr_video_encoder *encoder, unsigned int *textures, int *num_textures, gsr_destination_color *destination_color) {
static void gsr_video_encoder_nvenc_get_textures(gsr_video_encoder *encoder, unsigned int *textures, vec2i *texture_sizes, int *num_textures, gsr_destination_color *destination_color) {
gsr_video_encoder_nvenc *self = encoder->priv;
textures[0] = self->target_textures[0];
textures[1] = self->target_textures[1];
texture_sizes[0] = self->target_texture_size[0];
texture_sizes[1] = self->target_texture_size[1];
*num_textures = 2;
*destination_color = self->params.color_depth == GSR_COLOR_DEPTH_10_BITS ? GSR_DESTINATION_COLOR_P010 : GSR_DESTINATION_COLOR_NV12;
}

View File

@@ -13,6 +13,7 @@ typedef struct {
gsr_video_encoder_software_params params;
unsigned int target_textures[2];
vec2i texture_sizes[2];
} gsr_video_encoder_software;
static bool gsr_video_encoder_software_setup_textures(gsr_video_encoder_software *self, AVCodecContext *video_codec_context, AVFrame *frame) {
@@ -34,7 +35,8 @@ static bool gsr_video_encoder_software_setup_textures(gsr_video_encoder_software
const int div[2] = {1, 2}; // divide UV texture size by 2 because chroma is half size
for(int i = 0; i < 2; ++i) {
self->target_textures[i] = gl_create_texture(self->params.egl, video_codec_context->width / div[i], video_codec_context->height / div[i], self->params.color_depth == GSR_COLOR_DEPTH_8_BITS ? internal_formats_nv12[i] : internal_formats_p010[i], formats[i], GL_NEAREST);
self->texture_sizes[i] = (vec2i){ video_codec_context->width / div[i], video_codec_context->height / div[i] };
self->target_textures[i] = gl_create_texture(self->params.egl, self->texture_sizes[i].x, self->texture_sizes[i].y, self->params.color_depth == GSR_COLOR_DEPTH_8_BITS ? internal_formats_nv12[i] : internal_formats_p010[i], formats[i], GL_NEAREST);
if(self->target_textures[i] == 0) {
fprintf(stderr, "gsr error: gsr_capture_kms_setup_cuda_textures: failed to create opengl texture\n");
return false;
@@ -86,10 +88,12 @@ static void gsr_video_encoder_software_copy_textures_to_frame(gsr_video_encoder
//self->params.egl->glFinish();
}
static void gsr_video_encoder_software_get_textures(gsr_video_encoder *encoder, unsigned int *textures, int *num_textures, gsr_destination_color *destination_color) {
static void gsr_video_encoder_software_get_textures(gsr_video_encoder *encoder, unsigned int *textures, vec2i *texture_sizes, int *num_textures, gsr_destination_color *destination_color) {
gsr_video_encoder_software *self = encoder->priv;
textures[0] = self->target_textures[0];
textures[1] = self->target_textures[1];
texture_sizes[0] = self->texture_sizes[0];
texture_sizes[1] = self->texture_sizes[1];
*num_textures = 2;
*destination_color = self->params.color_depth == GSR_COLOR_DEPTH_10_BITS ? GSR_DESTINATION_COLOR_P010 : GSR_DESTINATION_COLOR_NV12;
}

View File

@@ -16,6 +16,7 @@ typedef struct {
gsr_video_encoder_vaapi_params params;
unsigned int target_textures[2];
vec2i texture_sizes[2];
AVBufferRef *device_ctx;
VADisplay va_dpy;
@@ -112,6 +113,7 @@ static bool gsr_video_encoder_vaapi_setup_textures(gsr_video_encoder_vaapi *self
intptr_t img_attr[44];
setup_dma_buf_attrs(img_attr, formats[i], self->prime.width / div[i], self->prime.height / div[i],
fds, offsets, pitches, modifiers, self->prime.layers[layer].num_planes, true);
self->texture_sizes[i] = (vec2i){ self->prime.width / div[i], self->prime.height / div[i] };
while(self->params.egl->eglGetError() != EGL_SUCCESS){}
EGLImage image = self->params.egl->eglCreateImage(self->params.egl->egl_display, 0, EGL_LINUX_DMA_BUF_EXT, NULL, img_attr);
@@ -214,10 +216,12 @@ void gsr_video_encoder_vaapi_stop(gsr_video_encoder_vaapi *self, AVCodecContext
}
}
static void gsr_video_encoder_vaapi_get_textures(gsr_video_encoder *encoder, unsigned int *textures, int *num_textures, gsr_destination_color *destination_color) {
static void gsr_video_encoder_vaapi_get_textures(gsr_video_encoder *encoder, unsigned int *textures, vec2i *texture_sizes, int *num_textures, gsr_destination_color *destination_color) {
gsr_video_encoder_vaapi *self = encoder->priv;
textures[0] = self->target_textures[0];
textures[1] = self->target_textures[1];
texture_sizes[0] = self->texture_sizes[0];
texture_sizes[1] = self->texture_sizes[1];
*num_textures = 2;
*destination_color = self->params.color_depth == GSR_COLOR_DEPTH_10_BITS ? GSR_DESTINATION_COLOR_P010 : GSR_DESTINATION_COLOR_NV12;
}

View File

@@ -22,7 +22,7 @@ void gsr_video_encoder_copy_textures_to_frame(gsr_video_encoder *encoder, AVFram
encoder->copy_textures_to_frame(encoder, frame, color_conversion);
}
void gsr_video_encoder_get_textures(gsr_video_encoder *encoder, unsigned int *textures, int *num_textures, gsr_destination_color *destination_color) {
void gsr_video_encoder_get_textures(gsr_video_encoder *encoder, unsigned int *textures, vec2i *texture_sizes, int *num_textures, gsr_destination_color *destination_color) {
assert(encoder->started);
encoder->get_textures(encoder, textures, num_textures, destination_color);
encoder->get_textures(encoder, textures, texture_sizes, num_textures, destination_color);
}

View File

@@ -16,6 +16,7 @@
typedef struct {
gsr_video_encoder_vulkan_params params;
unsigned int target_textures[2];
vec2i texture_sizes[2];
AVBufferRef *device_ctx;
} gsr_video_encoder_vulkan;
@@ -224,6 +225,9 @@ static bool gsr_video_encoder_vulkan_setup_textures(gsr_video_encoder_vulkan *se
fprintf(stderr, "3 gl error: %d\n", self->params.egl->glGetError());
self->params.egl->glBindTexture(GL_TEXTURE_2D, 0);
self->texture_sizes[0] = (vec2i){ frame->width, frame->height };
self->texture_sizes[1] = (vec2i){ frame->width/2, frame->height/2 };
}
#endif
return true;
@@ -270,10 +274,12 @@ void gsr_video_encoder_vulkan_stop(gsr_video_encoder_vulkan *self, AVCodecContex
av_buffer_unref(&self->device_ctx);
}
static void gsr_video_encoder_vulkan_get_textures(gsr_video_encoder *encoder, unsigned int *textures, int *num_textures, gsr_destination_color *destination_color) {
static void gsr_video_encoder_vulkan_get_textures(gsr_video_encoder *encoder, unsigned int *textures, vec2i *texture_sizes, int *num_textures, gsr_destination_color *destination_color) {
gsr_video_encoder_vulkan *self = encoder->priv;
textures[0] = self->target_textures[0];
textures[1] = self->target_textures[1];
texture_sizes[0] = self->texture_sizes[0];
texture_sizes[1] = self->texture_sizes[1];
*num_textures = 2;
*destination_color = self->params.color_depth == GSR_COLOR_DEPTH_10_BITS ? GSR_DESTINATION_COLOR_P010 : GSR_DESTINATION_COLOR_NV12;
}

View File

@@ -76,12 +76,6 @@ static const int VIDEO_STREAM_INDEX = 0;
static thread_local char av_error_buffer[AV_ERROR_MAX_STRING_SIZE];
enum class AudioMergeType {
NONE,
AMIX,
PIPEWIRE
};
typedef struct {
const gsr_window *window;
} MonitorOutputCallbackUserdata;
@@ -2321,6 +2315,7 @@ static void capture_image_to_file(args_parser &arg_parser, gsr_egl *egl, gsr_ima
color_conversion_params.load_external_image_shader = gsr_capture_uses_external_image(capture);
color_conversion_params.destination_textures[0] = image_writer.texture;
color_conversion_params.destination_textures_size[0] = { capture_metadata.width, capture_metadata.height };
color_conversion_params.num_destination_textures = 1;
color_conversion_params.destination_color = GSR_DESTINATION_COLOR_RGB8;
@@ -3125,24 +3120,12 @@ int main(int argc, char **argv) {
std::vector<MergedAudioInputs> requested_audio_inputs = parse_audio_inputs(audio_devices, audio_input_arg);
const bool uses_app_audio = merged_audio_inputs_has_app_audio(requested_audio_inputs);
AudioMergeType audio_merge_type = AudioMergeType::NONE;
std::vector<std::string> app_audio_names;
#ifdef GSR_APP_AUDIO
const bool audio_server_is_pipewire = audio_input_arg->num_values > 0 && pulseaudio_server_is_pipewire();
if(merged_audio_inputs_should_use_amix(requested_audio_inputs)) {
if(audio_server_is_pipewire || uses_app_audio)
audio_merge_type = AudioMergeType::PIPEWIRE;
else
audio_merge_type = AudioMergeType::AMIX;
}
gsr_pipewire_audio pipewire_audio;
memset(&pipewire_audio, 0, sizeof(pipewire_audio));
// TODO: When recording multiple audio devices and merging them (for example desktop audio and microphone) then one (or more) of the audio sources
// can get desynced. I'm unable to reproduce this but some others are. Instead of merging audio with ffmpeg amix, merge audio with pipewire (if available).
// This fixes the issue for people that had the issue.
if(audio_merge_type == AudioMergeType::PIPEWIRE || uses_app_audio) {
if(!audio_server_is_pipewire) {
if(uses_app_audio) {
if(!pulseaudio_server_is_pipewire()) {
fprintf(stderr, "gsr error: your sound server is not PipeWire. Application audio is only available when running PipeWire audio server\n");
_exit(2);
}
@@ -3159,9 +3142,6 @@ int main(int argc, char **argv) {
}, &app_audio_names);
}
#else
if(merged_audio_inputs_should_use_amix(requested_audio_inputs))
audio_merge_type = AudioMergeType::AMIX;
if(uses_app_audio) {
fprintf(stderr, "gsr error: application audio can't be recorded because GPU Screen Recorder is built without application audio support (-Dapp_audio option)\n");
_exit(2);
@@ -3271,7 +3251,8 @@ int main(int argc, char **argv) {
const bool force_no_audio_offset = arg_parser.is_livestream || arg_parser.is_output_piped || (file_extension != "mp4" && file_extension != "mkv" && file_extension != "webm");
const double target_fps = 1.0 / (double)arg_parser.fps;
arg_parser.audio_codec = select_audio_codec_with_fallback(arg_parser.audio_codec, file_extension, audio_merge_type == AudioMergeType::AMIX);
const bool uses_amix = merged_audio_inputs_should_use_amix(requested_audio_inputs);
arg_parser.audio_codec = select_audio_codec_with_fallback(arg_parser.audio_codec, file_extension, uses_amix);
gsr_capture *capture = create_capture_impl(arg_parser, &egl, false);
@@ -3374,7 +3355,7 @@ int main(int argc, char **argv) {
color_conversion_params.color_range = arg_parser.color_range;
color_conversion_params.egl = &egl;
color_conversion_params.load_external_image_shader = gsr_capture_uses_external_image(capture);
gsr_video_encoder_get_textures(video_encoder, color_conversion_params.destination_textures, &color_conversion_params.num_destination_textures, &color_conversion_params.destination_color);
gsr_video_encoder_get_textures(video_encoder, color_conversion_params.destination_textures, color_conversion_params.destination_textures_size, &color_conversion_params.num_destination_textures, &color_conversion_params.destination_color);
gsr_color_conversion color_conversion;
if(gsr_color_conversion_init(&color_conversion, &color_conversion_params) != 0) {
@@ -3428,7 +3409,7 @@ int main(int argc, char **argv) {
std::vector<AVFilterContext*> src_filter_ctx;
AVFilterGraph *graph = nullptr;
AVFilterContext *sink = nullptr;
if(use_amix && audio_merge_type == AudioMergeType::AMIX) {
if(use_amix) {
int err = init_filter_graph(audio_codec_context, &graph, &sink, src_filter_ctx, merged_audio_inputs.audio_inputs.size());
if(err < 0) {
fprintf(stderr, "gsr error: failed to create audio filter\n");
@@ -3445,7 +3426,8 @@ int main(int argc, char **argv) {
const double num_audio_frames_shift = audio_startup_time_seconds / timeout_sec;
std::vector<AudioDeviceData> audio_track_audio_devices;
if((use_amix && audio_merge_type == AudioMergeType::PIPEWIRE) || audio_inputs_has_app_audio(merged_audio_inputs.audio_inputs)) {
if(audio_inputs_has_app_audio(merged_audio_inputs.audio_inputs)) {
assert(!use_amix);
#ifdef GSR_APP_AUDIO
audio_track_audio_devices.push_back(create_application_audio_audio_input(merged_audio_inputs, audio_codec_context, num_channels, num_audio_frames_shift, &pipewire_audio));
#endif
@@ -3660,7 +3642,7 @@ int main(int argc, char **argv) {
}
std::thread amix_thread;
if(audio_merge_type == AudioMergeType::AMIX) {
if(uses_amix) {
amix_thread = std::thread([&]() {
AVFrame *aframe = av_frame_alloc();
while(running) {
@@ -3704,15 +3686,17 @@ int main(int argc, char **argv) {
bool use_damage_tracking = false;
gsr_damage damage;
memset(&damage, 0, sizeof(damage));
if(gsr_window_get_display_server(window) == GSR_DISPLAY_SERVER_X11) {
gsr_damage_init(&damage, &egl, arg_parser.record_cursor);
use_damage_tracking = true;
} else if(!capture->is_damaged) {
fprintf(stderr, "gsr warning: \"-fm content\" has no effect on Wayland when recording a monitor. Either record a monitor on X11 or capture with desktop portal instead (-w portal)\n");
}
if(arg_parser.framerate_mode == GSR_FRAMERATE_MODE_CONTENT) {
if(gsr_window_get_display_server(window) == GSR_DISPLAY_SERVER_X11) {
gsr_damage_init(&damage, &egl, arg_parser.record_cursor);
use_damage_tracking = true;
} else if(!capture->is_damaged) {
fprintf(stderr, "gsr warning: \"-fm content\" has no effect on Wayland when recording a monitor. Either record a monitor on X11 or capture with desktop portal instead (-w portal)\n");
}
if(is_monitor_capture)
gsr_damage_set_target_monitor(&damage, arg_parser.window);
if(is_monitor_capture)
gsr_damage_set_target_monitor(&damage, arg_parser.window);
}
while(running) {
while(gsr_window_process_event(window)) {

View File

@@ -404,8 +404,8 @@ static void registry_event_global(void *data, uint32_t id, uint32_t permissions,
//fprintf(stderr, " node id: %u, node name: %s, media class: %s\n", id, node_name, media_class);
const bool is_stream_output = media_class && strcmp(media_class, "Stream/Output/Audio") == 0;
const bool is_stream_input = media_class && strcmp(media_class, "Stream/Input/Audio") == 0;
const bool is_sink = media_class && strcmp(media_class, "Audio/Sink") == 0;
const bool is_source = media_class && strcmp(media_class, "Audio/Source") == 0;
const bool is_sink = media_class && string_starts_with(media_class, "Audio/Sink"); // Matches Audio/Sink/Virtual as well
const bool is_source = media_class && string_starts_with(media_class, "Audio/Source"); // Matches Audio/Source/Virtual as well
if(node_name && (is_stream_output || is_stream_input || is_sink || is_source)) {
//const char *application_binary = spa_dict_lookup(props, PW_KEY_APP_PROCESS_BINARY);
//const char *application_name = spa_dict_lookup(props, PW_KEY_APP_NAME);
@@ -693,7 +693,9 @@ void gsr_pipewire_audio_deinit(gsr_pipewire_audio *self) {
static struct pw_properties* gsr_pipewire_create_null_audio_sink(const char *name) {
char props_str[512];
snprintf(props_str, sizeof(props_str), "{ factory.name=support.null-audio-sink node.name=\"%s\" media.class=Audio/Sink object.linger=false audio.position=[FL FR] monitor.channel-volumes=true monitor.passthrough=true adjust_time=0 node.description=gsr-app-sink slaves=\"\" }", name);
snprintf(props_str, sizeof(props_str),
"{ factory.name=support.null-audio-sink node.name=\"%s\" media.class=Audio/Sink object.linger=false audio.position=[FL FR]"
" monitor.channel-volumes=true monitor.passthrough=true adjust_time=0 node.description=gsr-app-sink slaves=\"\" priority.driver=1 priority.session=1 }", name);
struct pw_properties *props = pw_properties_new_string(props_str);
if(!props) {
fprintf(stderr, "gsr error: gsr_pipewire_create_null_audio_sink: failed to create virtual sink properties\n");

View File

@@ -33,6 +33,7 @@ bool gsr_plugins_init(gsr_plugins *self, gsr_plugin_init_params init_params, gsr
.egl = egl,
.destination_color = GSR_DESTINATION_COLOR_RGB8, /* TODO: Support 10-bits, use init_params.color_depth */
.destination_textures[0] = self->texture,
.destination_textures_size[0] = (vec2i){ init_params.width, init_params.height },
.num_destination_textures = 1,
.color_range = GSR_COLOR_RANGE_FULL,
.load_external_image_shader = false,

View File

@@ -38,10 +38,9 @@ static unsigned int load_shader(gsr_egl *egl, unsigned int type, const char *sou
return shader_id;
}
static unsigned int load_program(gsr_egl *egl, const char *vertex_shader, const char *fragment_shader, const char *compute_shader) {
static unsigned int load_program(gsr_egl *egl, const char *vertex_shader, const char *fragment_shader) {
unsigned int vertex_shader_id = 0;
unsigned int fragment_shader_id = 0;
unsigned int compute_shader_id = 0;
unsigned int program_id = 0;
int linked = 0;
bool success = false;
@@ -58,12 +57,6 @@ static unsigned int load_program(gsr_egl *egl, const char *vertex_shader, const
goto done;
}
if(compute_shader) {
compute_shader_id = load_shader(egl, GL_COMPUTE_SHADER, compute_shader);
if(compute_shader_id == 0)
goto done;
}
program_id = egl->glCreateProgram();
if(program_id == 0) {
fprintf(stderr, "gsr error: load_program: failed to create shader program, error: %d\n", egl->glGetError());
@@ -76,9 +69,6 @@ static unsigned int load_program(gsr_egl *egl, const char *vertex_shader, const
if(fragment_shader_id)
egl->glAttachShader(program_id, fragment_shader_id);
if(compute_shader_id)
egl->glAttachShader(program_id, compute_shader_id);
egl->glLinkProgram(program_id);
egl->glGetProgramiv(program_id, GL_LINK_STATUS, &linked);
@@ -102,8 +92,6 @@ static unsigned int load_program(gsr_egl *egl, const char *vertex_shader, const
if(program_id)
egl->glDeleteProgram(program_id);
}
if(compute_shader_id)
egl->glDeleteShader(compute_shader_id);
if(fragment_shader_id)
egl->glDeleteShader(fragment_shader_id);
if(vertex_shader_id)
@@ -111,17 +99,17 @@ static unsigned int load_program(gsr_egl *egl, const char *vertex_shader, const
return program_id;
}
int gsr_shader_init(gsr_shader *self, gsr_egl *egl, const char *vertex_shader, const char *fragment_shader, const char *compute_shader) {
int gsr_shader_init(gsr_shader *self, gsr_egl *egl, const char *vertex_shader, const char *fragment_shader) {
assert(egl);
self->egl = egl;
self->program_id = 0;
if(!vertex_shader && !fragment_shader && !compute_shader) {
fprintf(stderr, "gsr error: gsr_shader_init: vertex, fragment shader and compute shaders can't be NULL at the same time\n");
if(!vertex_shader && !fragment_shader) {
fprintf(stderr, "gsr error: gsr_shader_init: vertex and fragment shader can't be NULL at the same time\n");
return -1;
}
self->program_id = load_program(self->egl, vertex_shader, fragment_shader, compute_shader);
self->program_id = load_program(self->egl, vertex_shader, fragment_shader);
if(self->program_id == 0)
return -1;

View File

@@ -20,6 +20,8 @@ int window_texture_init(WindowTexture *window_texture, Display *display, Window
window_texture->texture_id = 0;
window_texture->redirected = 0;
window_texture->egl = egl;
window_texture->window_width = 0;
window_texture->window_height = 0;
if(!x11_supports_composite_named_window_pixmap(display))
return 1;
@@ -67,6 +69,11 @@ int window_texture_on_resize(WindowTexture *self) {
EGL_NONE,
};
Window root_window;
int window_x, window_y;
unsigned int window_border, window_depth;
XGetGeometry(self->display, self->window, &root_window, &window_x, &window_y, &self->window_width, &self->window_height, &window_border, &window_depth);
pixmap = XCompositeNameWindowPixmap(self->display, self->window);
if(!pixmap) {
result = 2;