Skip to content

Commit ec66b45

Browse files
committed
linux-capture: Add EGL support for xcomposite
When running on EGL we can use the new create_texture_from_pixmap functions to implement xcomposite capture. This removes the texture indirection previously implemented for GLX due to not using the spriting functionality in the built in shaders. Now that we texture directly from the pixmap we can remove the red/blue swap workarounds.
1 parent 1f0ba80 commit ec66b45

File tree

2 files changed

+113
-109
lines changed

2 files changed

+113
-109
lines changed

plugins/linux-capture/linux-capture.c

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ bool obs_module_load(void)
3939

4040
case OBS_NIX_PLATFORM_X11_EGL:
4141
obs_register_source(&xshm_input);
42+
xcomposite_load();
4243
break;
4344

4445
#ifdef ENABLE_WAYLAND
@@ -52,6 +53,7 @@ bool obs_module_load(void)
5253

5354
void obs_module_unload(void)
5455
{
55-
if (obs_get_nix_platform() == OBS_NIX_PLATFORM_X11_GLX)
56+
if (obs_get_nix_platform() == OBS_NIX_PLATFORM_X11_GLX ||
57+
obs_get_nix_platform() == OBS_NIX_PLATFORM_X11_EGL)
5658
xcomposite_unload();
5759
}

plugins/linux-capture/xcomposite-input.c

Lines changed: 110 additions & 108 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
#include <obs-module.h>
2+
#include <obs-nix-platform.h>
23
#include <glad/glad.h>
34
#include <glad/glad_glx.h>
45
#include <X11/Xlib-xcb.h>
@@ -58,7 +59,6 @@ struct xcompcap {
5859

5960
Pixmap pixmap;
6061
GLXPixmap glxpixmap;
61-
gs_texture_t *tex;
6262
gs_texture_t *gltex;
6363

6464
pthread_mutex_t lock;
@@ -72,6 +72,7 @@ struct xcompcap {
7272
// performance when this is done, so setting this to false allows working
7373
// around it.
7474
bool strict_binding;
75+
bool egl;
7576
};
7677

7778
static void xcompcap_update(void *data, obs_data_t *settings);
@@ -375,11 +376,6 @@ void xcomp_cleanup_pixmap(Display *disp, struct xcompcap *s)
375376
XFreePixmap(disp, s->pixmap);
376377
s->pixmap = 0;
377378
}
378-
379-
if (s->tex) {
380-
gs_texture_destroy(s->tex);
381-
s->tex = 0;
382-
}
383379
}
384380

385381
static enum gs_color_format gs_format_from_tex()
@@ -479,96 +475,102 @@ void xcomp_create_pixmap(xcb_connection_t *conn, Display *disp,
479475
return;
480476
}
481477

482-
const int config_attrs[] = {GLX_BIND_TO_TEXTURE_RGBA_EXT,
483-
GL_TRUE,
484-
GLX_DRAWABLE_TYPE,
485-
GLX_PIXMAP_BIT,
486-
GLX_BIND_TO_TEXTURE_TARGETS_EXT,
487-
GLX_TEXTURE_2D_BIT_EXT,
488-
GLX_DOUBLEBUFFER,
489-
GL_FALSE,
490-
None};
491-
int nelem = 0;
492-
GLXFBConfig *configs =
493-
glXChooseFBConfig(disp, xcb_get_screen_for_root(conn, root),
494-
config_attrs, &nelem);
495-
496-
bool found = false;
497-
GLXFBConfig config;
498-
for (int i = 0; i < nelem; i++) {
499-
config = configs[i];
500-
XVisualInfo *visual = glXGetVisualFromFBConfig(disp, config);
501-
if (!visual)
502-
continue;
503-
504-
found = depth == visual->depth;
505-
XFree(visual);
506-
if (found)
507-
break;
508-
}
509-
XFree(configs);
510-
if (!found) {
511-
blog(log_level, "no matching fb config found");
512-
s->pixmap = 0;
513-
return;
514-
}
515-
516-
// Should be consistent format with config we are using. Since we searched on RGBA let's use RGBA here.
517-
const int pixmap_attrs[] = {GLX_TEXTURE_TARGET_EXT, GLX_TEXTURE_2D_EXT,
518-
GLX_TEXTURE_FORMAT_EXT,
519-
GLX_TEXTURE_FORMAT_RGBA_EXT, None};
520-
521-
// Try very hard to capture errors in glXCreatePixmap for NVIDIA drivers
522-
// where only one pixmap can be bound in GLX at a time.
523-
pixmap_err = false;
524-
XErrorHandler prev = XSetErrorHandler(catch_pixmap_errors);
525-
s->glxpixmap = glXCreatePixmap(disp, config, s->pixmap, pixmap_attrs);
526-
XSync(disp, false);
527-
528-
s->gltex = gs_texture_create(s->width, s->height, GS_RGBA_UNORM, 1, 0,
529-
GS_GL_DUMMYTEX);
530-
GLuint gltex = *(GLuint *)gs_texture_get_obj(s->gltex);
531-
glBindTexture(GL_TEXTURE_2D, gltex);
532-
// Not respecting a captured glXCreatePixmap error will result in Xorg closing our connection.
533-
if (!pixmap_err)
534-
glXBindTexImageEXT(disp, s->glxpixmap, GLX_FRONT_EXT, NULL);
535-
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
536-
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
537-
// glxBindTexImageEXT might modify the textures format.
538-
enum gs_color_format format = gs_format_from_tex();
539-
// Check if texture is invalid... because X11 hates us.
540-
int w;
541-
int h;
542-
glGetTexLevelParameteriv(GL_TEXTURE_2D, 0, GL_TEXTURE_WIDTH, &w);
543-
glGetTexLevelParameteriv(GL_TEXTURE_2D, 0, GL_TEXTURE_HEIGHT, &h);
544-
glBindTexture(GL_TEXTURE_2D, 0);
545-
// We must sync OBS texture format based on any glxBindTexImageEXT changes.
546-
s->gltex->format = format;
478+
if (s->egl) {
479+
s->gltex = gs_texture_create_from_pixmap(s->width, s->height,
480+
GS_BGRA_UNORM,
481+
GL_TEXTURE_2D,
482+
(void *)s->pixmap);
483+
484+
} else {
485+
const int config_attrs[] = {GLX_BIND_TO_TEXTURE_RGBA_EXT,
486+
GL_TRUE,
487+
GLX_DRAWABLE_TYPE,
488+
GLX_PIXMAP_BIT,
489+
GLX_BIND_TO_TEXTURE_TARGETS_EXT,
490+
GLX_TEXTURE_2D_BIT_EXT,
491+
GLX_DOUBLEBUFFER,
492+
GL_FALSE,
493+
None};
494+
int nelem = 0;
495+
GLXFBConfig *configs = glXChooseFBConfig(
496+
disp, xcb_get_screen_for_root(conn, root), config_attrs,
497+
&nelem);
498+
499+
bool found = false;
500+
GLXFBConfig config;
501+
for (int i = 0; i < nelem; i++) {
502+
config = configs[i];
503+
XVisualInfo *visual =
504+
glXGetVisualFromFBConfig(disp, config);
505+
if (!visual)
506+
continue;
507+
508+
found = depth == visual->depth;
509+
XFree(visual);
510+
if (found)
511+
break;
512+
}
513+
XFree(configs);
514+
if (!found) {
515+
blog(log_level, "no matching fb config found");
516+
s->pixmap = 0;
517+
return;
518+
}
547519

548-
XSync(disp, false);
549-
if (pixmap_err || (uint32_t)w < s->width || (uint32_t)h < s->height) {
550-
blog(log_level, "glXCreatePixmap failed: %s", pixmap_err_text);
551-
glXDestroyPixmap(disp, s->glxpixmap);
552-
XFreePixmap(disp, s->pixmap);
553-
gs_texture_destroy(s->gltex);
554-
s->pixmap = 0;
555-
s->glxpixmap = 0;
556-
s->gltex = 0;
557-
XSetErrorHandler(prev);
558-
return;
559-
}
560-
XSetErrorHandler(prev);
561-
562-
s->tex = gs_texture_create(
563-
s->width - s->crop_left - s->crop_right - 2 * s->border,
564-
s->height - s->crop_top - s->crop_bot - 2 * s->border, format,
565-
1, 0, GS_GL_DUMMYTEX);
566-
if (s->swapRedBlue) {
567-
GLuint tex = *(GLuint *)gs_texture_get_obj(s->tex);
568-
glBindTexture(GL_TEXTURE_2D, tex);
569-
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_SWIZZLE_B, GL_RED);
570-
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_SWIZZLE_R, GL_BLUE);
520+
// Should be consistent format with config we are using. Since we searched on RGBA let's use RGBA here.
521+
const int pixmap_attrs[] = {GLX_TEXTURE_TARGET_EXT,
522+
GLX_TEXTURE_2D_EXT,
523+
GLX_TEXTURE_FORMAT_EXT,
524+
GLX_TEXTURE_FORMAT_RGBA_EXT, None};
525+
526+
// Try very hard to capture errors in glXCreatePixmap for NVIDIA drivers
527+
// where only one pixmap can be bound in GLX at a time.
528+
pixmap_err = false;
529+
XErrorHandler prev = XSetErrorHandler(catch_pixmap_errors);
530+
s->glxpixmap =
531+
glXCreatePixmap(disp, config, s->pixmap, pixmap_attrs);
532+
XSync(disp, false);
533+
534+
s->gltex = gs_texture_create(s->width, s->height, GS_RGBA_UNORM,
535+
1, 0, GS_GL_DUMMYTEX);
536+
GLuint gltex = *(GLuint *)gs_texture_get_obj(s->gltex);
537+
glBindTexture(GL_TEXTURE_2D, gltex);
538+
// Not respecting a captured glXCreatePixmap error will result in Xorg closing our connection.
539+
if (!pixmap_err)
540+
glXBindTexImageEXT(disp, s->glxpixmap, GLX_FRONT_EXT,
541+
NULL);
542+
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER,
543+
GL_LINEAR);
544+
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER,
545+
GL_LINEAR);
546+
// glxBindTexImageEXT might modify the textures format.
547+
enum gs_color_format format = gs_format_from_tex();
548+
// Check if texture is invalid... because X11 hates us.
549+
int w;
550+
int h;
551+
glGetTexLevelParameteriv(GL_TEXTURE_2D, 0, GL_TEXTURE_WIDTH,
552+
&w);
553+
glGetTexLevelParameteriv(GL_TEXTURE_2D, 0, GL_TEXTURE_HEIGHT,
554+
&h);
571555
glBindTexture(GL_TEXTURE_2D, 0);
556+
// We must sync OBS texture format based on any glxBindTexImageEXT changes.
557+
s->gltex->format = format;
558+
559+
XSync(disp, false);
560+
if (pixmap_err || (uint32_t)w < s->width ||
561+
(uint32_t)h < s->height) {
562+
blog(log_level, "glXCreatePixmap failed: %s",
563+
pixmap_err_text);
564+
glXDestroyPixmap(disp, s->glxpixmap);
565+
XFreePixmap(disp, s->pixmap);
566+
gs_texture_destroy(s->gltex);
567+
s->pixmap = 0;
568+
s->glxpixmap = 0;
569+
s->gltex = 0;
570+
XSetErrorHandler(prev);
571+
return;
572+
}
573+
XSetErrorHandler(prev);
572574
}
573575
}
574576

@@ -685,7 +687,7 @@ void watcher_unload()
685687
static uint32_t xcompcap_get_width(void *data)
686688
{
687689
struct xcompcap *s = (struct xcompcap *)data;
688-
if (!s->tex || !s->gltex)
690+
if (!s->gltex)
689691
return 0;
690692

691693
int32_t border = s->crop_left + s->crop_right + 2 * s->border;
@@ -696,7 +698,7 @@ static uint32_t xcompcap_get_width(void *data)
696698
static uint32_t xcompcap_get_height(void *data)
697699
{
698700
struct xcompcap *s = (struct xcompcap *)data;
699-
if (!s->tex || !s->gltex)
701+
if (!s->gltex)
700702
return 0;
701703

702704
int32_t border = s->crop_bot + s->crop_top + 2 * s->border;
@@ -712,6 +714,11 @@ static void *xcompcap_create(obs_data_t *settings, obs_source_t *source)
712714
s->show_cursor = true;
713715
s->strict_binding = true;
714716
s->source = source;
717+
enum obs_nix_platform_type platform = obs_get_nix_platform();
718+
s->egl = platform == OBS_NIX_PLATFORM_X11_EGL;
719+
if (s->egl) {
720+
s->strict_binding = false;
721+
}
715722

716723
obs_enter_graphics();
717724
if (strcmp(glGetString(GL_VENDOR), "NVIDIA Corporation") == 0) {
@@ -736,9 +743,6 @@ static void xcompcap_destroy(void *data)
736743
watcher_unregister(conn, s);
737744
xcomp_cleanup_pixmap(disp, s);
738745

739-
if (s->tex)
740-
gs_texture_destroy(s->tex);
741-
742746
if (s->cursor)
743747
xcb_xcursor_destroy(s->cursor);
744748

@@ -784,7 +788,7 @@ static void xcompcap_video_tick(void *data, float seconds)
784788
s->cursor->y_org + s->crop_top);
785789
}
786790

787-
if (!s->tex || !s->gltex)
791+
if (!s->gltex)
788792
goto done;
789793

790794
if (xcompcap_get_height(s) == 0 || xcompcap_get_width(s) == 0)
@@ -795,9 +799,6 @@ static void xcompcap_video_tick(void *data, float seconds)
795799
glXReleaseTexImageEXT(disp, s->glxpixmap, GLX_FRONT_EXT);
796800
glXBindTexImageEXT(disp, s->glxpixmap, GLX_FRONT_EXT, NULL);
797801
}
798-
gs_copy_texture_region(s->tex, 0, 0, s->gltex, s->crop_left + s->border,
799-
s->crop_top + s->border, xcompcap_get_width(s),
800-
xcompcap_get_height(s));
801802
glBindTexture(GL_TEXTURE_2D, 0);
802803

803804
if (s->show_cursor) {
@@ -820,19 +821,20 @@ static void xcompcap_video_render(void *data, gs_effect_t *effect)
820821

821822
pthread_mutex_lock(&s->lock);
822823

823-
if (!s->tex || !s->gltex)
824+
if (!s->gltex)
824825
goto done;
825826

827+
effect = obs_get_base_effect(OBS_EFFECT_DEFAULT);
826828
if (s->exclude_alpha)
827829
effect = obs_get_base_effect(OBS_EFFECT_OPAQUE);
828-
else
829-
effect = obs_get_base_effect(OBS_EFFECT_DEFAULT);
830830

831831
image = gs_effect_get_param_by_name(effect, "image");
832-
gs_effect_set_texture(image, s->tex);
832+
gs_effect_set_texture(image, s->gltex);
833833

834834
while (gs_effect_loop(effect, "Draw")) {
835-
gs_draw_sprite(s->tex, 0, 0, 0);
835+
gs_draw_sprite_subregion(s->gltex, 0, s->crop_left, s->crop_top,
836+
xcompcap_get_width(s),
837+
xcompcap_get_height(s));
836838
}
837839

838840
if (s->gltex && s->show_cursor && !s->cursor_outside) {

0 commit comments

Comments
 (0)