aboutsummaryrefslogtreecommitdiff
path: root/src/egl.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/egl.c')
-rw-r--r--src/egl.c683
1 files changed, 314 insertions, 369 deletions
diff --git a/src/egl.c b/src/egl.c
index faae6e7..76ab9ec 100644
--- a/src/egl.c
+++ b/src/egl.c
@@ -1,211 +1,38 @@
#include "../include/egl.h"
+#include "../include/window/window.h"
#include "../include/library_loader.h"
+#include "../include/utils.h"
+
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <dlfcn.h>
#include <assert.h>
-
-#include <wayland-client.h>
-#include <wayland-egl.h>
-#include "../external/wlr-export-dmabuf-unstable-v1-client-protocol.h"
#include <unistd.h>
#include <sys/capability.h>
-// Move this shit to a separate wayland file, and have a separate file for x11.
-
-static void output_handle_geometry(void *data, struct wl_output *wl_output,
- int32_t x, int32_t y, int32_t phys_width, int32_t phys_height,
- int32_t subpixel, const char *make, const char *model,
- int32_t transform) {
- (void)wl_output;
- (void)phys_width;
- (void)phys_height;
- (void)subpixel;
- (void)make;
- (void)model;
- (void)transform;
- gsr_wayland_output *gsr_output = data;
- gsr_output->pos.x = x;
- gsr_output->pos.y = y;
-}
-
-static void output_handle_mode(void *data, struct wl_output *wl_output, uint32_t flags, int32_t width, int32_t height, int32_t refresh) {
- (void)wl_output;
- (void)flags;
- (void)refresh;
- gsr_wayland_output *gsr_output = data;
- gsr_output->size.x = width;
- gsr_output->size.y = height;
-}
-
-static void output_handle_done(void *data, struct wl_output *wl_output) {
- (void)data;
- (void)wl_output;
-}
-
-static void output_handle_scale(void* data, struct wl_output *wl_output, int32_t factor) {
- (void)data;
- (void)wl_output;
- (void)factor;
-}
-
-static void output_handle_name(void *data, struct wl_output *wl_output, const char *name) {
- (void)wl_output;
- gsr_wayland_output *gsr_output = data;
- if(gsr_output->name) {
- free(gsr_output->name);
- gsr_output->name = NULL;
- }
- gsr_output->name = strdup(name);
-}
-
-static void output_handle_description(void *data, struct wl_output *wl_output, const char *description) {
- (void)data;
- (void)wl_output;
- (void)description;
-}
-
-static const struct wl_output_listener output_listener = {
- .geometry = output_handle_geometry,
- .mode = output_handle_mode,
- .done = output_handle_done,
- .scale = output_handle_scale,
- .name = output_handle_name,
- .description = output_handle_description,
-};
-
-static void registry_add_object(void *data, struct wl_registry *registry, uint32_t name, const char *interface, uint32_t version) {
- (void)version;
- gsr_egl *egl = data;
- if (strcmp(interface, "wl_compositor") == 0) {
- if(egl->wayland.compositor) {
- wl_compositor_destroy(egl->wayland.compositor);
- egl->wayland.compositor = NULL;
- }
- egl->wayland.compositor = wl_registry_bind(registry, name, &wl_compositor_interface, 1);
- } else if(strcmp(interface, wl_output_interface.name) == 0) {
- if(version < 4) {
- fprintf(stderr, "gsr warning: wl output interface version is < 4, expected >= 4 to capture a monitor. Using KMS capture instead\n");
- return;
- }
-
- if(egl->wayland.num_outputs == GSR_MAX_OUTPUTS) {
- fprintf(stderr, "gsr warning: reached maximum outputs (32), ignoring output %u\n", name);
- return;
- }
-
- gsr_wayland_output *gsr_output = &egl->wayland.outputs[egl->wayland.num_outputs];
- egl->wayland.num_outputs++;
- *gsr_output = (gsr_wayland_output) {
- .wl_name = name,
- .output = wl_registry_bind(registry, name, &wl_output_interface, 4),
- .pos = { .x = 0, .y = 0 },
- .size = { .x = 0, .y = 0 },
- .name = NULL,
- };
- wl_output_add_listener(gsr_output->output, &output_listener, gsr_output);
- } else if(strcmp(interface, zwlr_export_dmabuf_manager_v1_interface.name) == 0) {
- if(egl->wayland.export_manager) {
- zwlr_export_dmabuf_manager_v1_destroy(egl->wayland.export_manager);
- egl->wayland.export_manager = NULL;
- }
- egl->wayland.export_manager = wl_registry_bind(registry, name, &zwlr_export_dmabuf_manager_v1_interface, 1);
- }
-}
-
-static void registry_remove_object(void *data, struct wl_registry *registry, uint32_t name) {
- (void)data;
- (void)registry;
- (void)name;
-}
-
-static struct wl_registry_listener registry_listener = {
- .global = registry_add_object,
- .global_remove = registry_remove_object,
-};
-
-static void frame_capture_output(gsr_egl *egl);
-
-static void frame_start(void *data, struct zwlr_export_dmabuf_frame_v1 *frame,
- uint32_t width, uint32_t height, uint32_t offset_x, uint32_t offset_y,
- uint32_t buffer_flags, uint32_t flags, uint32_t format,
- uint32_t mod_high, uint32_t mod_low, uint32_t num_objects) {
- (void)buffer_flags;
- (void)flags;
- (void)num_objects;
- gsr_egl *egl = data;
- //fprintf(stderr, "frame start %p, width: %u, height: %u, offset x: %u, offset y: %u, format: %u, num objects: %u\n", (void*)frame, width, height, offset_x, offset_y, format, num_objects);
- egl->x = offset_x;
- egl->y = offset_y;
- egl->width = width;
- egl->height = height;
- egl->pixel_format = format;
- egl->modifier = ((uint64_t)mod_high << 32) | (uint64_t)mod_low;
- egl->wayland.current_frame = frame;
-}
-
-static void frame_object(void *data, struct zwlr_export_dmabuf_frame_v1 *frame,
- uint32_t index, int32_t fd, uint32_t size, uint32_t offset,
- uint32_t stride, uint32_t plane_index) {
- // TODO: What if we get multiple objects? then we get multiple fd per frame
- (void)frame;
- (void)index;
- (void)size;
- (void)plane_index;
- gsr_egl *egl = data;
- if(egl->fd > 0) {
- close(egl->fd);
- egl->fd = 0;
- }
- egl->fd = fd;
- egl->pitch = stride;
- egl->offset = offset;
- //fprintf(stderr, "new frame %p, fd: %d, index: %u, size: %u, offset: %u, stride: %u, plane_index: %u\n", (void*)frame, fd, index, size, offset, stride, plane_index);
-}
-
-static void frame_ready(void *data, struct zwlr_export_dmabuf_frame_v1 *frame, uint32_t tv_sec_hi, uint32_t tv_sec_lo, uint32_t tv_nsec) {
- (void)frame;
- (void)tv_sec_hi;
- (void)tv_sec_lo;
- (void)tv_nsec;
- frame_capture_output(data);
-}
-
-static void frame_cancel(void *data, struct zwlr_export_dmabuf_frame_v1 *frame, uint32_t reason) {
- (void)frame;
- (void)reason;
- frame_capture_output(data);
-}
-
-static const struct zwlr_export_dmabuf_frame_v1_listener frame_listener = {
- .frame = frame_start,
- .object = frame_object,
- .ready = frame_ready,
- .cancel = frame_cancel,
-};
-
-static void frame_capture_output(gsr_egl *egl) {
- assert(egl->wayland.output_to_capture);
- bool with_cursor = true;
-
- if(egl->wayland.frame_callback) {
- zwlr_export_dmabuf_frame_v1_destroy(egl->wayland.frame_callback);
- egl->wayland.frame_callback = NULL;
- }
-
- egl->wayland.frame_callback = zwlr_export_dmabuf_manager_v1_capture_output(egl->wayland.export_manager, with_cursor, egl->wayland.output_to_capture->output);
- zwlr_export_dmabuf_frame_v1_add_listener(egl->wayland.frame_callback, &frame_listener, egl);
-}
-
-static gsr_wayland_output* get_wayland_output_by_name(gsr_egl *egl, const char *name) {
- assert(name);
- for(int i = 0; i < egl->wayland.num_outputs; ++i) {
- if(egl->wayland.outputs[i].name && strcmp(egl->wayland.outputs[i].name, name) == 0)
- return &egl->wayland.outputs[i];
- }
- return NULL;
-}
+// TODO: rename gsr_egl to something else since this includes both egl and glx and in the future maybe vulkan too
+
+#define GLX_DRAWABLE_TYPE 0x8010
+#define GLX_RENDER_TYPE 0x8011
+#define GLX_RGBA_BIT 0x00000001
+#define GLX_WINDOW_BIT 0x00000001
+#define GLX_PIXMAP_BIT 0x00000002
+#define GLX_BIND_TO_TEXTURE_RGBA_EXT 0x20D1
+#define GLX_BIND_TO_TEXTURE_TARGETS_EXT 0x20D3
+#define GLX_TEXTURE_2D_BIT_EXT 0x00000002
+#define GLX_DOUBLEBUFFER 5
+#define GLX_RED_SIZE 8
+#define GLX_GREEN_SIZE 9
+#define GLX_BLUE_SIZE 10
+#define GLX_ALPHA_SIZE 11
+#define GLX_DEPTH_SIZE 12
+#define GLX_RGBA_TYPE 0x8014
+
+#define GLX_CONTEXT_PRIORITY_LEVEL_EXT 0x3100
+#define GLX_CONTEXT_PRIORITY_HIGH_EXT 0x3101
+#define GLX_CONTEXT_PRIORITY_MEDIUM_EXT 0x3102
+#define GLX_CONTEXT_PRIORITY_LOW_EXT 0x3103
static void reset_cap_nice(void) {
cap_t caps = cap_get_proc();
@@ -220,54 +47,27 @@ static void reset_cap_nice(void) {
}
// TODO: Create egl context without surface (in other words, x11/wayland agnostic, doesn't require x11/wayland dependency)
-static bool gsr_egl_create_window(gsr_egl *self, bool wayland) {
+static bool gsr_egl_create_window(gsr_egl *self) {
EGLConfig ecfg;
int32_t num_config = 0;
+ // TODO: Use EGL_OPENGL_ES_BIT as amd requires that for external texture, but that breaks software encoding
const int32_t attr[] = {
EGL_BUFFER_SIZE, 24,
EGL_RENDERABLE_TYPE, EGL_OPENGL_BIT,
- EGL_NONE
+ EGL_NONE, EGL_NONE
};
const int32_t ctxattr[] = {
EGL_CONTEXT_CLIENT_VERSION, 2,
- EGL_CONTEXT_PRIORITY_LEVEL_IMG, EGL_CONTEXT_PRIORITY_HIGH_IMG, /* requires cap_sys_nice, ignored otherwise */
- EGL_NONE
+ //EGL_CONTEXT_PRIORITY_LEVEL_IMG, EGL_CONTEXT_PRIORITY_HIGH_IMG, /* requires cap_sys_nice, ignored otherwise */
+ EGL_NONE, EGL_NONE
};
- if(wayland) {
- self->wayland.dpy = wl_display_connect(NULL);
- if(!self->wayland.dpy) {
- fprintf(stderr, "gsr error: gsr_egl_create_window failed: wl_display_connect failed\n");
- goto fail;
- }
-
- self->wayland.registry = wl_display_get_registry(self->wayland.dpy); // TODO: Error checking
- wl_registry_add_listener(self->wayland.registry, &registry_listener, self); // TODO: Error checking
-
- // Fetch globals
- wl_display_roundtrip(self->wayland.dpy);
-
- // Fetch wl_output
- wl_display_roundtrip(self->wayland.dpy);
-
- if(!self->wayland.compositor) {
- fprintf(stderr, "gsr error: gsr_gl_create_window failed: failed to find compositor\n");
- goto fail;
- }
- } else {
- self->x11.window = XCreateWindow(self->x11.dpy, DefaultRootWindow(self->x11.dpy), 0, 0, 16, 16, 0, CopyFromParent, InputOutput, CopyFromParent, 0, NULL);
-
- if(!self->x11.window) {
- fprintf(stderr, "gsr error: gsr_gl_create_window failed: failed to create gl window\n");
- goto fail;
- }
- }
-
+ // TODO: Use EGL_OPENGL_ES_API as amd requires that for external texture, but that breaks software encoding
self->eglBindAPI(EGL_OPENGL_API);
- self->egl_display = self->eglGetDisplay(self->wayland.dpy ? (EGLNativeDisplayType)self->wayland.dpy : (EGLNativeDisplayType)self->x11.dpy);
+ self->egl_display = self->eglGetDisplay((EGLNativeDisplayType)gsr_window_get_display(self->window));
if(!self->egl_display) {
fprintf(stderr, "gsr error: gsr_egl_create_window failed: eglGetDisplay failed\n");
goto fail;
@@ -277,33 +77,26 @@ static bool gsr_egl_create_window(gsr_egl *self, bool wayland) {
fprintf(stderr, "gsr error: gsr_egl_create_window failed: eglInitialize failed\n");
goto fail;
}
-
+
if(!self->eglChooseConfig(self->egl_display, attr, &ecfg, 1, &num_config) || num_config != 1) {
fprintf(stderr, "gsr error: gsr_egl_create_window failed: failed to find a matching config\n");
goto fail;
}
-
+
self->egl_context = self->eglCreateContext(self->egl_display, ecfg, NULL, ctxattr);
if(!self->egl_context) {
fprintf(stderr, "gsr error: gsr_egl_create_window failed: failed to create egl context\n");
goto fail;
}
- if(wayland) {
- self->wayland.surface = wl_compositor_create_surface(self->wayland.compositor);
- self->wayland.window = wl_egl_window_create(self->wayland.surface, 16, 16);
- self->egl_surface = self->eglCreateWindowSurface(self->egl_display, ecfg, (EGLNativeWindowType)self->wayland.window, NULL);
- } else {
- self->egl_surface = self->eglCreateWindowSurface(self->egl_display, ecfg, (EGLNativeWindowType)self->x11.window, NULL);
- }
-
+ self->egl_surface = self->eglCreateWindowSurface(self->egl_display, ecfg, (EGLNativeWindowType)gsr_window_get_window(self->window), NULL);
if(!self->egl_surface) {
fprintf(stderr, "gsr error: gsr_egl_create_window failed: failed to create window surface\n");
goto fail;
}
if(!self->eglMakeCurrent(self->egl_display, self->egl_surface, self->egl_surface, self->egl_context)) {
- fprintf(stderr, "gsr error: gsr_egl_create_window failed: failed to make context current\n");
+ fprintf(stderr, "gsr error: gsr_egl_create_window failed: failed to make egl context current\n");
goto fail;
}
@@ -316,8 +109,86 @@ static bool gsr_egl_create_window(gsr_egl *self, bool wayland) {
return false;
}
+static GLXFBConfig glx_fb_config_choose(gsr_egl *self, Display *display) {
+ const int glx_visual_attribs[] = {
+ GLX_RENDER_TYPE, GLX_RGBA_BIT,
+ GLX_DRAWABLE_TYPE, GLX_WINDOW_BIT,
+ // TODO:
+ //GLX_BIND_TO_TEXTURE_RGBA_EXT, 1,
+ //GLX_BIND_TO_TEXTURE_TARGETS_EXT, GLX_TEXTURE_2D_BIT_EXT,
+ GLX_DOUBLEBUFFER, True,
+ GLX_RED_SIZE, 8,
+ GLX_GREEN_SIZE, 8,
+ GLX_BLUE_SIZE, 8,
+ GLX_ALPHA_SIZE, 0,
+ GLX_DEPTH_SIZE, 0,
+ None, None
+ };
+
+ // TODO: Cleanup
+ int c = 0;
+ GLXFBConfig *fb_configs = self->glXChooseFBConfig(display, DefaultScreen(display), glx_visual_attribs, &c);
+ if(c == 0 || !fb_configs)
+ return NULL;
+
+ return fb_configs[0];
+}
+
+static bool gsr_egl_switch_to_glx_context(gsr_egl *self) {
+ // TODO: Cleanup
+ assert(gsr_window_get_display_server(self->window) == GSR_DISPLAY_SERVER_X11);
+ Display *display = gsr_window_get_display(self->window);
+ const Window window = (Window)gsr_window_get_window(self->window);
+
+ if(self->egl_context) {
+ self->eglMakeCurrent(self->egl_display, NULL, NULL, NULL);
+ self->eglDestroyContext(self->egl_display, self->egl_context);
+ self->egl_context = NULL;
+ }
+
+ if(self->egl_surface) {
+ self->eglDestroySurface(self->egl_display, self->egl_surface);
+ self->egl_surface = NULL;
+ }
+
+ if(self->egl_display) {
+ self->eglTerminate(self->egl_display);
+ self->egl_display = NULL;
+ }
+
+ self->glx_fb_config = glx_fb_config_choose(self, display);
+ if(!self->glx_fb_config) {
+ fprintf(stderr, "gsr error: gsr_egl_create_window failed: failed to find a suitable fb config\n");
+ goto fail;
+ }
+
+ // TODO:
+ //self->glx_context = self->glXCreateContextAttribsARB(display, self->glx_fb_config, NULL, True, context_attrib_list);
+ self->glx_context = self->glXCreateNewContext(display, self->glx_fb_config, GLX_RGBA_TYPE, NULL, True);
+ if(!self->glx_context) {
+ fprintf(stderr, "gsr error: gsr_egl_create_window failed: failed to create glx context\n");
+ goto fail;
+ }
+
+ if(!self->glXMakeContextCurrent(display, window, window, self->glx_context)) {
+ fprintf(stderr, "gsr error: gsr_egl_create_window failed: failed to make glx context current\n");
+ goto fail;
+ }
+
+ return true;
+
+ fail:
+ if(self->glx_context) {
+ self->glXMakeContextCurrent(display, None, None, NULL);
+ self->glXDestroyContext(display, self->glx_context);
+ self->glx_context = NULL;
+ self->glx_fb_config = NULL;
+ }
+ return false;
+}
+
static bool gsr_egl_load_egl(gsr_egl *self, void *library) {
- dlsym_assign required_dlsym[] = {
+ const dlsym_assign required_dlsym[] = {
{ (void**)&self->eglGetError, "eglGetError" },
{ (void**)&self->eglGetDisplay, "eglGetDisplay" },
{ (void**)&self->eglInitialize, "eglInitialize" },
@@ -350,6 +221,27 @@ static bool gsr_egl_proc_load_egl(gsr_egl *self) {
self->eglExportDMABUFImageQueryMESA = (FUNC_eglExportDMABUFImageQueryMESA)self->eglGetProcAddress("eglExportDMABUFImageQueryMESA");
self->eglExportDMABUFImageMESA = (FUNC_eglExportDMABUFImageMESA)self->eglGetProcAddress("eglExportDMABUFImageMESA");
self->glEGLImageTargetTexture2DOES = (FUNC_glEGLImageTargetTexture2DOES)self->eglGetProcAddress("glEGLImageTargetTexture2DOES");
+ self->eglQueryDisplayAttribEXT = (FUNC_eglQueryDisplayAttribEXT)self->eglGetProcAddress("eglQueryDisplayAttribEXT");
+ self->eglQueryDeviceStringEXT = (FUNC_eglQueryDeviceStringEXT)self->eglGetProcAddress("eglQueryDeviceStringEXT");
+ self->eglQueryDmaBufModifiersEXT = (FUNC_eglQueryDmaBufModifiersEXT)self->eglGetProcAddress("eglQueryDmaBufModifiersEXT");
+
+ self->glCreateMemoryObjectsEXT = (FUNC_glCreateMemoryObjectsEXT)self->eglGetProcAddress("glCreateMemoryObjectsEXT");
+ self->glImportMemoryFdEXT = (FUNC_glImportMemoryFdEXT)self->eglGetProcAddress("glImportMemoryFdEXT");
+ self->glIsMemoryObjectEXT = (FUNC_glIsMemoryObjectEXT)self->eglGetProcAddress("glIsMemoryObjectEXT");
+ self->glTexStorageMem2DEXT = (FUNC_glTexStorageMem2DEXT)self->eglGetProcAddress("glTexStorageMem2DEXT");
+ self->glBufferStorageMemEXT = (FUNC_glBufferStorageMemEXT)self->eglGetProcAddress("glBufferStorageMemEXT");
+ self->glNamedBufferStorageMemEXT = (FUNC_glNamedBufferStorageMemEXT)self->eglGetProcAddress("glNamedBufferStorageMemEXT");
+ self->glMemoryObjectParameterivEXT = (FUNC_glMemoryObjectParameterivEXT)self->eglGetProcAddress("glMemoryObjectParameterivEXT");
+
+ if(!self->eglExportDMABUFImageQueryMESA) {
+ fprintf(stderr, "gsr error: gsr_egl_load failed: could not find eglExportDMABUFImageQueryMESA\n");
+ return false;
+ }
+
+ if(!self->eglExportDMABUFImageMESA) {
+ fprintf(stderr, "gsr error: gsr_egl_load failed: could not find eglExportDMABUFImageMESA\n");
+ return false;
+ }
if(!self->glEGLImageTargetTexture2DOES) {
fprintf(stderr, "gsr error: gsr_egl_load failed: could not find glEGLImageTargetTexture2DOES\n");
@@ -359,23 +251,61 @@ static bool gsr_egl_proc_load_egl(gsr_egl *self) {
return true;
}
+static bool gsr_egl_load_glx(gsr_egl *self, void *library) {
+ const dlsym_assign required_dlsym[] = {
+ { (void**)&self->glXGetProcAddress, "glXGetProcAddress" },
+ { (void**)&self->glXChooseFBConfig, "glXChooseFBConfig" },
+ { (void**)&self->glXMakeContextCurrent, "glXMakeContextCurrent" },
+ { (void**)&self->glXCreateNewContext, "glXCreateNewContext" },
+ { (void**)&self->glXDestroyContext, "glXDestroyContext" },
+ { (void**)&self->glXSwapBuffers, "glXSwapBuffers" },
+
+ { NULL, NULL }
+ };
+
+ if(!dlsym_load_list(library, required_dlsym)) {
+ fprintf(stderr, "gsr error: gsr_egl_load failed: missing required symbols in libGLX.so.0\n");
+ return false;
+ }
+
+ self->glXCreateContextAttribsARB = (FUNC_glXCreateContextAttribsARB)self->glXGetProcAddress((const unsigned char*)"glXCreateContextAttribsARB");
+ if(!self->glXCreateContextAttribsARB) {
+ fprintf(stderr, "gsr error: gsr_egl_load_glx failed: could not find glXCreateContextAttribsARB\n");
+ return false;
+ }
+
+ self->glXSwapIntervalEXT = (FUNC_glXSwapIntervalEXT)self->glXGetProcAddress((const unsigned char*)"glXSwapIntervalEXT");
+ self->glXSwapIntervalMESA = (FUNC_glXSwapIntervalMESA)self->glXGetProcAddress((const unsigned char*)"glXSwapIntervalMESA");
+ self->glXSwapIntervalSGI = (FUNC_glXSwapIntervalSGI)self->glXGetProcAddress((const unsigned char*)"glXSwapIntervalSGI");
+
+ return true;
+}
+
static bool gsr_egl_load_gl(gsr_egl *self, void *library) {
- dlsym_assign required_dlsym[] = {
+ const dlsym_assign required_dlsym[] = {
{ (void**)&self->glGetError, "glGetError" },
{ (void**)&self->glGetString, "glGetString" },
+ { (void**)&self->glFlush, "glFlush" },
+ { (void**)&self->glFinish, "glFinish" },
{ (void**)&self->glClear, "glClear" },
{ (void**)&self->glClearColor, "glClearColor" },
{ (void**)&self->glGenTextures, "glGenTextures" },
{ (void**)&self->glDeleteTextures, "glDeleteTextures" },
+ { (void**)&self->glActiveTexture, "glActiveTexture" },
{ (void**)&self->glBindTexture, "glBindTexture" },
+ { (void**)&self->glBindImageTexture, "glBindImageTexture" },
{ (void**)&self->glTexParameteri, "glTexParameteri" },
+ { (void**)&self->glTexParameteriv, "glTexParameteriv" },
+ { (void**)&self->glTexParameterfv, "glTexParameterfv" },
{ (void**)&self->glGetTexLevelParameteriv, "glGetTexLevelParameteriv" },
{ (void**)&self->glTexImage2D, "glTexImage2D" },
- { (void**)&self->glCopyImageSubData, "glCopyImageSubData" },
- { (void**)&self->glClearTexImage, "glClearTexImage" },
+ { (void**)&self->glTexSubImage2D, "glTexSubImage2D" },
+ { (void**)&self->glGetTexImage, "glGetTexImage" },
{ (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" },
{ (void**)&self->glDrawBuffers, "glDrawBuffers" },
@@ -406,9 +336,21 @@ static bool gsr_egl_load_gl(gsr_egl *self, void *library) {
{ (void**)&self->glEnableVertexAttribArray, "glEnableVertexAttribArray" },
{ (void**)&self->glDrawArrays, "glDrawArrays" },
{ (void**)&self->glEnable, "glEnable" },
+ { (void**)&self->glDisable, "glDisable" },
{ (void**)&self->glBlendFunc, "glBlendFunc" },
+ { (void**)&self->glPixelStorei, "glPixelStorei" },
{ (void**)&self->glGetUniformLocation, "glGetUniformLocation" },
{ (void**)&self->glUniform1f, "glUniform1f" },
+ { (void**)&self->glUniform2f, "glUniform2f" },
+ { (void**)&self->glUniform1i, "glUniform1i" },
+ { (void**)&self->glUniform2i, "glUniform2i" },
+ { (void**)&self->glUniformMatrix2fv, "glUniformMatrix2fv" },
+ { (void**)&self->glDebugMessageCallback, "glDebugMessageCallback" },
+ { (void**)&self->glScissor, "glScissor" },
+ { (void**)&self->glReadPixels, "glReadPixels" },
+ { (void**)&self->glMapBuffer, "glMapBuffer" },
+ { (void**)&self->glUnmapBuffer, "glUnmapBuffer" },
+ { (void**)&self->glGetIntegerv, "glGetIntegerv" },
{ NULL, NULL }
};
@@ -421,56 +363,139 @@ static bool gsr_egl_load_gl(gsr_egl *self, void *library) {
return true;
}
-bool gsr_egl_load(gsr_egl *self, Display *dpy, bool wayland) {
- memset(self, 0, sizeof(gsr_egl));
- self->x11.dpy = dpy;
+#define GL_DEBUG_TYPE_ERROR 0x824C
+#define GL_DEBUG_SEVERITY_NOTIFICATION 0x826B
+static void debug_callback(unsigned int source, unsigned int type, unsigned int id, unsigned int severity, int length, const char* message, const void* userParam) {
+ (void)source;
+ (void)id;
+ (void)length;
+ (void)userParam;
+ if(severity != GL_DEBUG_SEVERITY_NOTIFICATION)
+ fprintf(stderr, "gsr info: gl callback: %s type = 0x%x, severity = 0x%x, message = %s\n", type == GL_DEBUG_TYPE_ERROR ? "** GL ERROR **" : "", type, severity, message);
+}
- void *egl_lib = NULL;
- void *gl_lib = NULL;
+/* TODO: check for glx swap control extension string (GLX_EXT_swap_control, etc) */
+static void set_vertical_sync_enabled(gsr_egl *egl, int enabled) {
+ int result = 0;
+
+ if(egl->glXSwapIntervalEXT) {
+ assert(gsr_window_get_display_server(egl->window) == GSR_DISPLAY_SERVER_X11);
+ Display *display = gsr_window_get_display(egl->window);
+ const Window window = (Window)gsr_window_get_window(egl->window);
+ egl->glXSwapIntervalEXT(display, window, enabled ? 1 : 0);
+ } else if(egl->glXSwapIntervalMESA) {
+ result = egl->glXSwapIntervalMESA(enabled ? 1 : 0);
+ } else if(egl->glXSwapIntervalSGI) {
+ result = egl->glXSwapIntervalSGI(enabled ? 1 : 0);
+ } else {
+ static int warned = 0;
+ if (!warned) {
+ warned = 1;
+ fprintf(stderr, "gsr warning: setting vertical sync not supported\n");
+ }
+ }
+
+ if(result != 0)
+ fprintf(stderr, "gsr warning: setting vertical sync failed\n");
+}
+
+static void gsr_egl_disable_vsync(gsr_egl *self) {
+ switch(self->context_type) {
+ case GSR_GL_CONTEXT_TYPE_EGL: {
+ self->eglSwapInterval(self->egl_display, 0);
+ break;
+ }
+ case GSR_GL_CONTEXT_TYPE_GLX: {
+ set_vertical_sync_enabled(self, 0);
+ break;
+ }
+ }
+}
+
+bool gsr_egl_load(gsr_egl *self, gsr_window *window, bool is_monitor_capture, bool enable_debug) {
+ memset(self, 0, sizeof(gsr_egl));
+ self->context_type = GSR_GL_CONTEXT_TYPE_EGL;
+ self->window = window;
dlerror(); /* clear */
- egl_lib = dlopen("libEGL.so.1", RTLD_LAZY);
- if(!egl_lib) {
+ self->egl_library = dlopen("libEGL.so.1", RTLD_LAZY);
+ if(!self->egl_library) {
fprintf(stderr, "gsr error: gsr_egl_load: failed to load libEGL.so.1, error: %s\n", dlerror());
goto fail;
}
- gl_lib = dlopen("libGL.so.1", RTLD_LAZY);
- if(!egl_lib) {
+ self->glx_library = dlopen("libGLX.so.0", RTLD_LAZY);
+
+ self->gl_library = dlopen("libGL.so.1", RTLD_LAZY);
+ if(!self->gl_library) {
fprintf(stderr, "gsr error: gsr_egl_load: failed to load libGL.so.1, error: %s\n", dlerror());
goto fail;
}
- if(!gsr_egl_load_egl(self, egl_lib))
+ if(!gsr_egl_load_egl(self, self->egl_library))
+ goto fail;
+
+ /* In some distros (alpine for example libGLX doesn't exist, but libGL can be used instead) */
+ if(!gsr_egl_load_glx(self, self->glx_library ? self->glx_library : self->gl_library))
goto fail;
- if(!gsr_egl_load_gl(self, gl_lib))
+ if(!gsr_egl_load_gl(self, self->gl_library))
goto fail;
if(!gsr_egl_proc_load_egl(self))
goto fail;
- if(!gsr_egl_create_window(self, wayland))
+ if(!gsr_egl_create_window(self))
goto fail;
+ if(!gl_get_gpu_info(self, &self->gpu_info))
+ goto fail;
+
+ if(self->eglQueryDisplayAttribEXT && self->eglQueryDeviceStringEXT) {
+ intptr_t device = 0;
+ if(self->eglQueryDisplayAttribEXT(self->egl_display, EGL_DEVICE_EXT, &device) && device)
+ self->dri_card_path = self->eglQueryDeviceStringEXT((void*)device, EGL_DRM_DEVICE_FILE_EXT);
+ }
+
+ /* Nvfbc requires glx */
+ if(gsr_window_get_display_server(self->window) == GSR_DISPLAY_SERVER_X11 && is_monitor_capture && self->gpu_info.vendor == GSR_GPU_VENDOR_NVIDIA) {
+ self->context_type = GSR_GL_CONTEXT_TYPE_GLX;
+ self->dri_card_path = NULL;
+ if(!gsr_egl_switch_to_glx_context(self))
+ goto fail;
+ }
+
self->glEnable(GL_BLEND);
self->glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
+ self->glPixelStorei(GL_PACK_ALIGNMENT, 1);
+ self->glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
+
+ if(enable_debug) {
+ self->glEnable(GL_DEBUG_OUTPUT);
+ self->glDebugMessageCallback(debug_callback, NULL);
+ }
+
+ gsr_egl_disable_vsync(self);
+
+ if(self->gpu_info.vendor == GSR_GPU_VENDOR_NVIDIA) {
+ /* This fixes nvenc codecs unable to load on openSUSE tumbleweed because of a cuda error. Don't ask me why */
+ const bool inside_flatpak = getenv("FLATPAK_ID") != NULL;
+ if(inside_flatpak)
+ system("flatpak-spawn --host -- nvidia-smi -f /dev/null");
+ else
+ system("nvidia-smi -f /dev/null");
+ }
- self->egl_library = egl_lib;
- self->gl_library = gl_lib;
return true;
fail:
- if(egl_lib)
- dlclose(egl_lib);
- if(gl_lib)
- dlclose(gl_lib);
- memset(self, 0, sizeof(gsr_egl));
+ gsr_egl_unload(self);
return false;
}
void gsr_egl_unload(gsr_egl *self) {
if(self->egl_context) {
+ self->eglMakeCurrent(self->egl_display, NULL, NULL, NULL);
self->eglDestroyContext(self->egl_display, self->egl_context);
self->egl_context = NULL;
}
@@ -485,59 +510,13 @@ void gsr_egl_unload(gsr_egl *self) {
self->egl_display = NULL;
}
- if(self->x11.window) {
- XDestroyWindow(self->x11.dpy, self->x11.window);
- self->x11.window = None;
- }
-
- gsr_egl_cleanup_frame(self);
-
- if(self->wayland.frame_callback) {
- zwlr_export_dmabuf_frame_v1_destroy(self->wayland.frame_callback);
- self->wayland.frame_callback = NULL;
- }
-
- if(self->wayland.export_manager) {
- zwlr_export_dmabuf_manager_v1_destroy(self->wayland.export_manager);
- self->wayland.export_manager = NULL;
- }
-
- if(self->wayland.window) {
- wl_egl_window_destroy(self->wayland.window);
- self->wayland.window = NULL;
- }
-
- if(self->wayland.surface) {
- wl_surface_destroy(self->wayland.surface);
- self->wayland.surface = NULL;
- }
-
- for(int i = 0; i < self->wayland.num_outputs; ++i) {
- if(self->wayland.outputs[i].output) {
- wl_output_destroy(self->wayland.outputs[i].output);
- self->wayland.outputs[i].output = NULL;
- }
-
- if(self->wayland.outputs[i].name) {
- free(self->wayland.outputs[i].name);
- self->wayland.outputs[i].name = NULL;
- }
- }
- self->wayland.num_outputs = 0;
-
- if(self->wayland.compositor) {
- wl_compositor_destroy(self->wayland.compositor);
- self->wayland.compositor = NULL;
- }
-
- if(self->wayland.registry) {
- wl_registry_destroy(self->wayland.registry);
- self->wayland.registry = NULL;
- }
-
- if(self->wayland.dpy) {
- wl_display_disconnect(self->wayland.dpy);
- self->wayland.dpy = NULL;
+ if(self->glx_context) {
+ assert(gsr_window_get_display_server(self->window) == GSR_DISPLAY_SERVER_X11);
+ Display *display = gsr_window_get_display(self->window);
+ self->glXMakeContextCurrent(display, None, None, NULL);
+ self->glXDestroyContext(display, self->glx_context);
+ self->glx_context = NULL;
+ self->glx_fb_config = NULL;
}
if(self->egl_library) {
@@ -545,6 +524,11 @@ void gsr_egl_unload(gsr_egl *self) {
self->egl_library = NULL;
}
+ if(self->glx_library) {
+ dlclose(self->glx_library);
+ self->glx_library = NULL;
+ }
+
if(self->gl_library) {
dlclose(self->gl_library);
self->gl_library = NULL;
@@ -553,55 +537,16 @@ void gsr_egl_unload(gsr_egl *self) {
memset(self, 0, sizeof(gsr_egl));
}
-bool gsr_egl_supports_wayland_capture(gsr_egl *self) {
- // TODO: wlroots capture is broken right now (black screen) on amd and multiple monitors
- // so it has to be disabled right now. Find out why it happens and fix it.
- (void)self;
- return false;
- //return !!self->wayland.export_manager && self->wayland.num_outputs > 0;
-}
-
-bool gsr_egl_start_capture(gsr_egl *self, const char *monitor_to_capture) {
- assert(monitor_to_capture);
- if(!monitor_to_capture)
- return false;
-
- if(!self->wayland.dpy)
- return false;
-
- if(!gsr_egl_supports_wayland_capture(self))
- return false;
-
- if(self->wayland.frame_callback)
- return false;
-
- self->wayland.output_to_capture = get_wayland_output_by_name(self, monitor_to_capture);
- if(!self->wayland.output_to_capture)
- return false;
-
- frame_capture_output(self);
- return true;
-}
-
-void gsr_egl_update(gsr_egl *self) {
- if(!self->wayland.dpy)
- return;
-
- // TODO: pselect on wl_display_get_fd before doing dispatch
- wl_display_dispatch(self->wayland.dpy);
-}
-
-void gsr_egl_cleanup_frame(gsr_egl *self) {
- if(!self->wayland.dpy)
- return;
-
- if(self->fd > 0) {
- close(self->fd);
- self->fd = 0;
- }
-
- if(self->wayland.current_frame) {
- //zwlr_export_dmabuf_frame_v1_destroy(self->wayland.current_frame);
- self->wayland.current_frame = NULL;
+void gsr_egl_swap_buffers(gsr_egl *self) {
+ /* This uses less cpu than swap buffer on nvidia */
+ // TODO: Do these and remove swap
+ //self->glFlush();
+ //self->glFinish();
+ if(self->egl_display) {
+ self->eglSwapBuffers(self->egl_display, self->egl_surface);
+ } else if(gsr_window_get_display_server(self->window) == GSR_DISPLAY_SERVER_X11) {
+ Display *display = gsr_window_get_display(self->window);
+ const Window window = (Window)gsr_window_get_window(self->window);
+ self->glXSwapBuffers(display, window);
}
}