Skip to content

Commit 7de875d

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 e6ff904 commit 7de875d

File tree

2 files changed

+112
-109
lines changed

2 files changed

+112
-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: 109 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,10 @@ 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;
715721

716722
obs_enter_graphics();
717723
if (strcmp(glGetString(GL_VENDOR), "NVIDIA Corporation") == 0) {
@@ -736,9 +742,6 @@ static void xcompcap_destroy(void *data)
736742
watcher_unregister(conn, s);
737743
xcomp_cleanup_pixmap(disp, s);
738744

739-
if (s->tex)
740-
gs_texture_destroy(s->tex);
741-
742745
if (s->cursor)
743746
xcb_xcursor_destroy(s->cursor);
744747

@@ -784,7 +787,7 @@ static void xcompcap_video_tick(void *data, float seconds)
784787
s->cursor->y_org + s->crop_top);
785788
}
786789

787-
if (!s->tex || !s->gltex)
790+
if (!s->gltex)
788791
goto done;
789792

790793
if (xcompcap_get_height(s) == 0 || xcompcap_get_width(s) == 0)
@@ -795,9 +798,6 @@ static void xcompcap_video_tick(void *data, float seconds)
795798
glXReleaseTexImageEXT(disp, s->glxpixmap, GLX_FRONT_EXT);
796799
glXBindTexImageEXT(disp, s->glxpixmap, GLX_FRONT_EXT, NULL);
797800
}
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));
801801
glBindTexture(GL_TEXTURE_2D, 0);
802802

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

821821
pthread_mutex_lock(&s->lock);
822822

823-
if (!s->tex || !s->gltex)
823+
if (!s->gltex)
824824
goto done;
825825

826+
effect = obs_get_base_effect(OBS_EFFECT_DEFAULT);
826827
if (s->exclude_alpha)
827828
effect = obs_get_base_effect(OBS_EFFECT_OPAQUE);
828-
else
829-
effect = obs_get_base_effect(OBS_EFFECT_DEFAULT);
830829

831830
image = gs_effect_get_param_by_name(effect, "image");
832-
gs_effect_set_texture(image, s->tex);
831+
gs_effect_set_texture(image, s->gltex);
833832

834833
while (gs_effect_loop(effect, "Draw")) {
835-
gs_draw_sprite(s->tex, 0, 0, 0);
834+
gs_draw_sprite_subregion(s->gltex, 0, s->crop_left, s->crop_top,
835+
xcompcap_get_width(s),
836+
xcompcap_get_height(s));
836837
}
837838

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

0 commit comments

Comments
 (0)