Skip to content

Conversation

@nowrep
Copy link
Owner

@nowrep nowrep commented Sep 20, 2025

@konovalov-nk You can try if this helps with the nvidia issue.

@konovalov-nk
Copy link

konovalov-nk commented Sep 21, 2025

Hey David, you've managed to get ahead of me!

I wanted to dig into this sooner but I've been very busy for the last 6 weeks on my job and personal matters.
I'm very thankful for this and will try to compile/install it today this week and test on my machine 🙇

@konovalov-nk
Copy link

konovalov-nk commented Sep 28, 2025

So I did a lot of testing today but too tired to wrap it all up properly. I don't want to lose context, so I'm just dumping here my findings 🙇

In short: I have dual-gpu system. Intel Arc 310 as the output, and RTX 3080 as offload for games / CUDA. Wayland/hyprland.

I did not know I could run OBS on NVIDIA like this __NV_PRIME_RENDER_OFFLOAD=1 __GLX_VENDOR_LIBRARY_NAME=nvidia QT_QPA_PLATFORM=xcb obs. This solves the tearing/blocking issue (even without the NV_timeline_semaphore patch) but:

  1. introduces a workaround which is making obs take up VRAM (not much but 120MB is still 120MB) on offload GPU (nvidia).
  2. For AV1 encoding on Arc 310 (the reason why I bought it), an extra frame buffer copy is required. It's not as bad but it is sub-optimal.

Your patch is also working but something happened to the preview colors (where source screenshot looks okay, but preview is darker), which I was able to fix with the following patch:

@@ static void vkcapture_source_render(void *data, gs_effect_t *effect)
-    const enum gs_color_space color_space = gs_get_color_space();
-    const bool linear_srgb = gs_get_linear_srgb();
-    const char *tech_name = linear_srgb ? "DrawSrgbDecompress" : "Draw";
+    const enum gs_color_space color_space = gs_get_color_space();
+
+    /* Allow overriding sRGB path from environment.
+       OBS_VKCAPTURE_SRGB=0 -> always "Draw" (NO sRGB decompression)
+       OBS_VKCAPTURE_SRGB=1 -> always "DrawSrgbDecompress"
+       unset -> OBS default (gs_get_linear_srgb()) */
+    bool linear_srgb = gs_get_linear_srgb();
+    {
+        const char *env = getenv("OBS_VKCAPTURE_SRGB");
+        if (env && *env) {
+            if (env[0] == '0') linear_srgb = false;
+            else if (env[0] == '1') linear_srgb = true;
+        }
+    }
+    const char *tech_name = linear_srgb ? "DrawSrgbDecompress" : "Draw";
@@
-    bool previous = gs_framebuffer_srgb_enabled();
-    gs_enable_framebuffer_srgb(linear_srgb);
+    bool previous = gs_framebuffer_srgb_enabled();
+    gs_enable_framebuffer_srgb(linear_srgb);
@@
-    gs_enable_framebuffer_srgb(previous);
+    gs_enable_framebuffer_srgb(previous);

and then setting OBS_VKCAPTURE_SRGB to 0

Here's how the colors look like:

Preview (different than gamescope window)

Screenshot 2025-09-28 01-38-37

Source (expected)

Screenshot 2025-09-28 01-37-25

I also recorded a few clips during testing but haven't tagged them:

This is my current workaround to record without tearing/blocking:

#!/usr/bin/env bash
export QT_QPA_PLATFORM=xcb
export __NV_PRIME_RENDER_OFFLOAD=1
export __GLX_VENDOR_LIBRARY_NAME=nvidia
exec obs "$@"

Extra details (EGL/Mesa explicit-sync)

  • When OBS renders on Arc (Mesa, EGL/X11 or Wayland) the plugin logs

    info: [linux-vkcapture] Client 1 connected (pid=80030)
    info: [linux-vkcapture] Creating texture from dmabuf 3840x2160 modifier:216172782120099860
    info: [linux-vkcapture] [0] fd:63 stride:15360 offset:0
    error: Cannot create EGLImage: Arguments are inconsistent (for example, a valid context requires buffers not supplied by a valid surface).
    warning: [linux-vkcapture] Asking client to create texture no modifiers
    [linux-vkcapture] … Sync disabled
    

    and I can consistently see tearing.
    The same scene with OBS on NVIDIA (GLX) shows

    [linux-vkcapture] … Sync enabled
    

    and there is no tearing.

  • Arc/Mesa clearly supports the required sync extensions:

    • EGL: EGL_ANDROID_native_fence_sync, EGL_KHR_fence_sync, EGL_KHR_wait_sync
    • GL: GL_EXT_semaphore, GL_EXT_semaphore_fd, GL_EXT_memory_object(_fd)
      (snippets from glxinfo/eglinfo available on request)
  • What seems to be missing in the plugin is an EGL fallback path.
    Today explicit sync is enabled on the GLX/NVIDIA path via GL_EXT_semaphore_fd.
    On EGL/Mesa the plugin should instead wait the acquire fence via native-fence sync, roughly:

    // fence_fd comes from the layer (exported via vkGetSemaphoreFdKHR)
    EGLint attribs[] = {
        EGL_SYNC_NATIVE_FENCE_FD_ANDROID, fence_fd,
        EGL_NONE
    };
    EGLSyncKHR fence = eglCreateSyncKHR(eglDisplay, EGL_SYNC_NATIVE_FENCE_ANDROID, attribs);
    // Block until producer finished
    eglWaitSyncKHR(eglDisplay, fence, 0);
    eglDestroySyncKHR(eglDisplay, fence);
    // fence_fd consumed/closed by EGL

    If GL_EXT_semaphore_fd is available in this EGL context, that path could still be used, but the native-fence + eglWaitSyncKHR fallback would cover Mesa reliably.

  • I'll provide more logs tomorrow if it's helpful; one thing is that dmabuf import sometimes retries without modifiers (valid), but sync remains disabled on EGL, which is what causes tearing in multi-GPU setups where OBS is on Arc.

  • I’m happy to test a branch/PR that adds this EGL native-fence path. If you're busy I'll try to make it myself because I've finally figured out how to build&test changes locally 🙂

    • I know that you don't even have NVIDIA hardware / multi-gpu setup so I'm here to help out with the testing.

Repro steps are simple:

  • game on NVIDIA via gamescope
  • OBS on Arc (EGL)
  • with the patch it should flip to Sync enabled and remove tearing.

@nowrep
Copy link
Owner Author

nowrep commented Sep 28, 2025

GL_NV_timeline_semaphore is only guaranteed to work with the same device on Vulkan and GL side. It will not work with NVIDIA proprietary running game and OBS running on Intel card. It should work when both drivers are Mesa though.

The extension is an OpenGL extension, not EGL. It is not supported in any stable Mesa release.

So any tests you do, please test it with NVIDIA running both game and OBS and only with games using Vulkan.

@nowrep
Copy link
Owner Author

nowrep commented Sep 28, 2025

diff --git a/src/vkcapture.c b/src/vkcapture.c
index 58f2f83..f4f1615 100644
--- a/src/vkcapture.c
+++ b/src/vkcapture.c
@@ -647,9 +647,14 @@ static void vkcapture_source_render(void *data, gs_effect_t *effect)
         p_glSemaphoreParameterui64vEXT(ctx->semaphore, GL_TIMELINE_SEMAPHORE_VALUE_NV, &value);
         p_glWaitSemaphoreEXT(ctx->semaphore, 0, NULL, 1, gs_texture_get_obj(ctx->texture), &layout);
 
+        const bool previous = gs_framebuffer_srgb_enabled();
+        gs_enable_framebuffer_srgb(false);
+
         gs_copy_texture(ctx->texture_copy, ctx->texture);
         value++;
 
+        gs_enable_framebuffer_srgb(previous);
+
         p_glSemaphoreParameterui64vEXT(ctx->semaphore, GL_TIMELINE_SEMAPHORE_VALUE_NV, &value);
         p_glSignalSemaphoreEXT(ctx->semaphore, 0, NULL, 1, gs_texture_get_obj(ctx->texture), &layout);
 

Does this help with the colors?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants