[vlc-commits] [Git][videolan/vlc][master] opengl: add offscreen opengl for Windows

Steve Lhomme (@robUx4) gitlab at videolan.org
Wed Jun 11 06:56:03 UTC 2025



Steve Lhomme pushed to branch master at VideoLAN / VLC


Commits:
05fdfc59 by Steve Lhomme at 2025-06-11T06:38:16+00:00
opengl: add offscreen opengl for Windows

Based on egl_pbuffer.c.

It passes the test_modules_video_output_opengl_filters tests.

- - - - -


3 changed files:

- modules/video_filter/meson.build
- + modules/video_filter/opengl_win_offscreen.c
- modules/video_output/opengl/Makefile.am


Changes:

=====================================
modules/video_filter/meson.build
=====================================
@@ -55,6 +55,13 @@ vlc_modules += {
     'enabled' : opengl_filter_dep.found()
 }
 
+vlc_modules += {
+    'name' : 'opengl_win_offscreen',
+    'sources' : files('opengl_win_offscreen.c'),
+    'dependencies' : [ gl_common_dep, opengl_dep ],
+    'enabled' : opengl_dep.found() and host_system == 'windows'
+}
+
 if host_system == 'windows'
     vlc_modules += {
         'name' : 'amf_frc',


=====================================
modules/video_filter/opengl_win_offscreen.c
=====================================
@@ -0,0 +1,487 @@
+// SPDX-License-Identifier: LGPL-2.1-or-later
+/*****************************************************************************
+ * opengl_win_offscreen: Windows offscreen OpenGL module
+ *****************************************************************************
+ * Copyright © 2025 Videolabs, VLC authors and VideoLAN
+ *
+ * Authors: Alexandre Janniaux <ajanni at videolabs.io>
+ *          Romain Vimont <rom1v at videolabs.io>
+ *          Steve Lhomme <robux4 at videolabs.io>
+ *****************************************************************************/
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#include <vlc_common.h>
+#include <vlc_plugin.h>
+#include <vlc_modules.h>
+#include <vlc_picture.h>
+#include <vlc_filter.h>
+#include <vlc_opengl.h>
+#include <vlc_vout_display.h>
+#include <vlc_atomic.h>
+#include "../video_output/opengl/vout_helper.h"
+#include "../video_output/opengl/filters.h"
+#include "../video_output/opengl/gl_api.h"
+#include "../video_output/opengl/gl_common.h"
+#include "../video_output/opengl/interop.h"
+#include <GL/glew.h>
+#include <GL/wglew.h>
+
+#define BUFFER_COUNT 4
+
+struct wgl_vt {
+    PFNWGLGETEXTENSIONSSTRINGARBPROC     GetExtensionsStringARB;
+    PFNWGLCHOOSEPIXELFORMATARBPROC       ChoosePixelFormatARB;
+    PFNWGLCREATEPBUFFERARBPROC           CreatePbufferARB;
+    PFNWGLDESTROYPBUFFERARBPROC          DestroyPbufferARB;
+    PFNWGLGETPBUFFERDCARBPROC            GetPbufferDCARB;
+    PFNWGLRELEASEPBUFFERDCARBPROC        ReleasePbufferDCARB;
+};
+struct pbo_picture_context
+{
+    struct picture_context_t context;
+    void *buffer_mapping;
+    int rc;
+    vlc_mutex_t *lock;
+    vlc_cond_t *cond;
+};
+
+struct vlc_gl_pbuffer
+{
+    vlc_mutex_t             lock;
+    vlc_cond_t              cond;
+
+    video_format_t          fmt_out;
+
+    HMODULE                 hOpengl;
+    HPBUFFERARB             hbuffer;
+    HDC                     pixelBufferDC;
+    HGLRC                   hGLRC;
+
+    struct wgl_vt           wgl_vt;
+    struct vlc_gl_api       api;
+
+    size_t                  current_flip;
+    GLuint                  pixelbuffers[BUFFER_COUNT];
+    GLuint                  framebuffers[BUFFER_COUNT];
+    GLuint                  textures[BUFFER_COUNT];
+    struct pbo_picture_context     picture_contexts[BUFFER_COUNT];
+
+    bool current;
+};
+
+static int MakeCurrentBare (vlc_gl_t *gl)
+{
+    struct vlc_gl_pbuffer *sys = gl->sys;
+
+    BOOL success = wglMakeCurrent(sys->pixelBufferDC, sys->hGLRC);
+    if (unlikely(!success))
+        return VLC_EGENERIC;
+
+    return VLC_SUCCESS;
+}
+
+static int MakeBoundCurrent (vlc_gl_t *gl)
+{
+    struct vlc_gl_pbuffer *sys = gl->sys;
+
+    assert(!sys->current);
+    int err = MakeCurrentBare(gl);
+    if (err != VLC_SUCCESS)
+        return err;
+
+    sys->current = true;
+
+    const opengl_vtable_t *vt = &sys->api.vt;
+    vt->BindBuffer(GL_PIXEL_PACK_BUFFER, sys->pixelbuffers[sys->current_flip]);
+    vt->BindFramebuffer(GL_FRAMEBUFFER, sys->framebuffers[sys->current_flip]);
+
+    return VLC_SUCCESS;
+}
+
+static void ReleaseCurrent (vlc_gl_t *gl)
+{
+    struct vlc_gl_pbuffer *sys = gl->sys;
+
+    assert(sys->current);
+    wglMakeCurrent(NULL, NULL);
+
+    sys->current = false;
+}
+
+static void *GetSymbol(vlc_gl_t *gl, const char *procname)
+{
+    struct vlc_gl_pbuffer *sys = gl->sys;
+    /* See https://www.khronos.org/opengl/wiki/Load_OpenGL_Functions */
+    PROC f= wglGetProcAddress(procname);
+    if(f == 0 || (f == (void*)0x1) || (f == (void*)0x2) ||
+      (f == (void*)0x3) || (f == (void*)-1) )
+    {
+        f = GetProcAddress(sys->hOpengl, procname);
+    }
+    return f;
+}
+
+static picture_context_t *picture_context_copy(picture_context_t *input)
+{
+    struct pbo_picture_context *context =
+        (struct pbo_picture_context *)input;
+
+    vlc_mutex_lock(context->lock);
+    context->rc++;
+    vlc_mutex_unlock(context->lock);
+    return input;
+}
+
+static void picture_context_destroy(picture_context_t *input)
+{
+    struct pbo_picture_context *context = (struct pbo_picture_context *) input;
+
+    vlc_mutex_lock(context->lock);
+    context->rc--;
+    vlc_cond_signal(context->cond);
+    vlc_mutex_unlock(context->lock);
+}
+
+static inline void BindDrawFramebuffer(struct vlc_gl_pbuffer *sys)
+{
+    const opengl_vtable_t *vt = &sys->api.vt;
+    vt->BindFramebuffer(GL_DRAW_FRAMEBUFFER,
+                        sys->framebuffers[sys->current_flip]);
+}
+
+static void UpdateBuffer(vlc_gl_t *gl)
+{
+    struct vlc_gl_pbuffer *sys = gl->sys;
+
+    vlc_mutex_lock(&sys->lock);
+    size_t index;
+
+    do {
+        for (index = 0; index < BUFFER_COUNT; ++index)
+        {
+            assert(sys->picture_contexts[index].rc >= 0);
+            if (sys->picture_contexts[index].rc == 0)
+                goto out_loop;
+        }
+        vlc_cond_wait(&sys->cond, &sys->lock);
+    } while (index == BUFFER_COUNT);
+out_loop:
+    vlc_mutex_unlock(&sys->lock);
+
+    sys->current_flip = index;
+    BindDrawFramebuffer(sys);
+}
+
+static picture_t *Swap(vlc_gl_t *gl)
+{
+    struct vlc_gl_pbuffer *sys = gl->sys;
+    const opengl_vtable_t *vt = &sys->api.vt;
+
+    if (!sys->current)
+        MakeCurrentBare(gl);
+
+    /* Read current framebuffer */
+    struct pbo_picture_context *context =
+        &sys->picture_contexts[sys->current_flip];
+
+    vt->BindBuffer(GL_PIXEL_PACK_BUFFER, sys->pixelbuffers[sys->current_flip]);
+    vt->BindFramebuffer(GL_FRAMEBUFFER, sys->framebuffers[sys->current_flip]);
+    if (context->buffer_mapping != NULL)
+        vt->UnmapBuffer(GL_PIXEL_PACK_BUFFER);
+
+    GLsizei width = sys->fmt_out.i_visible_width;
+    GLsizei height = sys->fmt_out.i_visible_height;
+    GLenum format = GL_RGBA;
+
+    vt->ReadPixels(0, 0, width, height, format, GL_UNSIGNED_BYTE, 0);
+
+    void *pixels = vt->MapBufferRange(
+            GL_PIXEL_PACK_BUFFER, 0, width*height*4, GL_MAP_READ_BIT);
+
+    GLsizei stride;
+    vt->GetIntegerv(GL_PACK_ROW_LENGTH, &stride);
+    stride = width;
+
+    context->buffer_mapping = pixels;
+    context->rc ++;
+
+    /* Swap framebuffer */
+    UpdateBuffer(gl);
+
+    if (!sys->current)
+        wglMakeCurrent(NULL, NULL);
+
+    /* Output as picture */
+    picture_resource_t pict_resource = {
+        .pf_destroy = NULL,
+    };
+
+    pict_resource.p[0].p_pixels = pixels;
+    pict_resource.p[0].i_lines = height;
+    pict_resource.p[0].i_pitch = stride * 4;
+
+
+    picture_t *output = picture_NewFromResource(&sys->fmt_out, &pict_resource);
+    if (output == NULL)
+        goto error;
+
+    output->context = (picture_context_t *)context;
+    output->context->vctx = NULL;
+
+    return output;
+
+error:
+    context->rc--;
+    return NULL;
+}
+
+static void Close( vlc_gl_t *gl )
+{
+    struct vlc_gl_pbuffer *sys = gl->sys;
+    const opengl_vtable_t *vt = &sys->api.vt;
+
+    wglMakeCurrent(sys->pixelBufferDC, sys->hGLRC);
+    vt->DeleteBuffers(BUFFER_COUNT, sys->pixelbuffers);
+    vt->DeleteFramebuffers(BUFFER_COUNT, sys->framebuffers);
+    vt->DeleteTextures(BUFFER_COUNT, sys->textures);
+    wglMakeCurrent(NULL, NULL);
+
+    wglDeleteContext(sys->hGLRC);
+    sys->wgl_vt.ReleasePbufferDCARB(sys->hbuffer, sys->pixelBufferDC);
+    sys->wgl_vt.DestroyPbufferARB(sys->hbuffer);
+}
+
+static int LoadWGLExt(vlc_gl_t *gl, struct wgl_vt *wgl_vt)
+{
+#define LOAD_EXT(name, type) do { \
+    wgl_vt->name = (type)(void*) wglGetProcAddress("wgl" #name); \
+    if (!wgl_vt->name) { \
+        msg_Warn(gl, "'wgl " #name "' could not be loaded"); \
+        return VLC_EGENERIC; \
+    } \
+} while(0)
+
+    LOAD_EXT(GetExtensionsStringARB, PFNWGLGETEXTENSIONSSTRINGARBPROC);
+    LOAD_EXT(ChoosePixelFormatARB,   PFNWGLCHOOSEPIXELFORMATARBPROC);
+    LOAD_EXT(CreatePbufferARB,       PFNWGLCREATEPBUFFERARBPROC);
+    LOAD_EXT(DestroyPbufferARB,      PFNWGLDESTROYPBUFFERARBPROC);
+    LOAD_EXT(GetPbufferDCARB,        PFNWGLGETPBUFFERDCARBPROC);
+    LOAD_EXT(ReleasePbufferDCARB,    PFNWGLRELEASEPBUFFERDCARBPROC);
+    return VLC_SUCCESS;
+}
+
+static int Open(vlc_gl_t *gl, unsigned width, unsigned height,
+                const struct vlc_gl_cfg *gl_cfg)
+{
+    if (gl_cfg->need_alpha)
+    {
+        msg_Err(gl, "Cannot support alpha yet");
+        return VLC_ENOTSUP;
+    }
+
+    struct vlc_gl_pbuffer *sys = vlc_obj_malloc(&gl->obj, sizeof *sys);
+    if (sys == NULL)
+        return VLC_ENOMEM;
+    sys->hbuffer = NULL;
+    sys->pixelBufferDC = NULL;
+
+    HDC tmpScreenDC = NULL;
+    HGLRC tmpGLRC = NULL;
+
+    sys->current = false;
+
+    video_format_Init(&sys->fmt_out, VLC_CODEC_RGBA);
+    sys->fmt_out.i_visible_width
+        = sys->fmt_out.i_width
+        = width;
+    sys->fmt_out.i_visible_height
+        = sys->fmt_out.i_height
+        = height;
+
+    gl->offscreen_chroma_out = VLC_CODEC_RGBA;
+    gl->offscreen_vctx_out = NULL;
+
+    vlc_mutex_init(&sys->lock);
+    vlc_cond_init(&sys->cond);
+
+    gl->sys = sys;
+
+    sys->hOpengl = GetModuleHandleA("opengl32.dll");
+    if (unlikely(sys->hOpengl == NULL))
+    {
+        msg_Err(gl, "Could not get the opengl32 DLL");
+        goto error1;
+    }
+
+    static const struct vlc_gl_operations gl_ops =
+    {
+        .make_current = MakeBoundCurrent,
+        .release_current = ReleaseCurrent,
+        .swap_offscreen = Swap,
+        .get_proc_address = GetSymbol,
+        .close = Close,
+    };
+    gl->ops = &gl_ops;
+    gl->orientation = ORIENT_VFLIPPED;
+
+    tmpScreenDC = GetDC(NULL);
+    if(unlikely(tmpScreenDC == NULL))
+    {
+        msg_Err(gl, "Failed to get a temporary DeviceContext");
+        goto error1;
+    }
+
+    PIXELFORMATDESCRIPTOR pfd = {
+        .nSize = sizeof(PIXELFORMATDESCRIPTOR),
+        .nVersion = 1,
+        .dwFlags = PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL | PFD_DOUBLEBUFFER,
+        .iPixelType = PFD_TYPE_RGBA,
+        .cColorBits = 24,
+        .cDepthBits = 16,
+        .iLayerType = PFD_MAIN_PLANE,
+    };
+    SetPixelFormat(tmpScreenDC, ChoosePixelFormat(tmpScreenDC, &pfd), &pfd);
+    tmpGLRC = wglCreateContext(tmpScreenDC);
+    if (tmpGLRC == NULL)
+    {
+        msg_Err(gl, "Failed to get a wgl context");
+        goto error1;
+    }
+    BOOL current = wglMakeCurrent(tmpScreenDC, tmpGLRC);
+    if (unlikely(!current))
+    {
+        msg_Err(gl, "Failed to set current context");
+        goto error1;
+    }
+
+    int err = LoadWGLExt(gl, &sys->wgl_vt);
+    if (err != VLC_SUCCESS)
+    {
+        msg_Err(gl, "Failed to get wgl extensions");
+        wglMakeCurrent(NULL, NULL);
+        goto error1;
+    }
+
+    const char *extensions = sys->wgl_vt.GetExtensionsStringARB(tmpScreenDC);
+    if (!vlc_gl_StrHasToken(extensions, "WGL_ARB_pbuffer") ||
+        !vlc_gl_StrHasToken(extensions, "WGL_ARB_pixel_format"))
+    {
+        msg_Err(gl, "wgl pixel buffer extensions missing");
+        wglMakeCurrent(NULL, NULL);
+        goto error1;
+    }
+
+    int formatAttributes[] = {
+        WGL_DRAW_TO_PBUFFER_ARB, GL_TRUE,
+        WGL_SUPPORT_OPENGL_ARB,  GL_TRUE,
+        WGL_PIXEL_TYPE_ARB, WGL_TYPE_RGBA_ARB,
+        WGL_DEPTH_BITS_ARB, 24,
+        WGL_RED_BITS_ARB,   8,
+        WGL_GREEN_BITS_ARB, 8,
+        WGL_BLUE_BITS_ARB,  8,
+        WGL_ALPHA_BITS_ARB, 8,
+        0,0
+    };
+    float fAttributes[] = {0,0};
+    int pixelFormats[50] = {0};
+    UINT formats_count = 0;
+    BOOL gotFormat = sys->wgl_vt.ChoosePixelFormatARB(tmpScreenDC, formatAttributes, fAttributes, ARRAY_SIZE(pixelFormats), pixelFormats, &formats_count);
+    if (!gotFormat)
+    {
+        msg_Err(gl, "could not find appropriate pixel buffer PixelFormat");
+        wglMakeCurrent(NULL, NULL);
+        goto error2;
+    }
+
+    sys->hbuffer = sys->wgl_vt.CreatePbufferARB(tmpScreenDC, pixelFormats[0], width, height, NULL);
+    if (sys->hbuffer == NULL)
+    {
+        msg_Err(gl, "failed to create the pixel buffer");
+        wglMakeCurrent(NULL, NULL);
+        goto error2;
+    }
+    sys->pixelBufferDC = sys->wgl_vt.GetPbufferDCARB(sys->hbuffer);
+    if (sys->pixelBufferDC == NULL)
+    {
+        msg_Err(gl, "failed to create the pixel buffer DeviceContext");
+        wglMakeCurrent(NULL, NULL);
+        goto error2;
+    }
+
+    sys->hGLRC = wglCreateContext(sys->pixelBufferDC);
+    if (sys->hGLRC == NULL)
+    {
+        msg_Err(gl, "wgl pixel buffer extensions missing");
+        wglMakeCurrent(NULL, NULL);
+        goto error2;
+    }
+    wglMakeCurrent(NULL, NULL);
+    wglMakeCurrent(sys->pixelBufferDC, sys->hGLRC);
+
+    int ret = vlc_gl_api_Init(&sys->api, gl);
+    if (ret != VLC_SUCCESS)
+    {
+        msg_Err(gl, "Failed to initialize gl_api");
+        wglMakeCurrent(NULL, NULL);
+        goto error2;
+    }
+
+    const opengl_vtable_t *vt = &sys->api.vt;
+    vt->GenBuffers(BUFFER_COUNT, sys->pixelbuffers);
+    vt->GenFramebuffers(BUFFER_COUNT, sys->framebuffers);
+    vt->GenTextures(BUFFER_COUNT, sys->textures);
+
+    for (size_t i=0; i<BUFFER_COUNT; ++i)
+    {
+        vt->BindBuffer(GL_PIXEL_PACK_BUFFER, sys->pixelbuffers[i]);
+        vt->BufferData(GL_PIXEL_PACK_BUFFER, width*height*4, NULL,
+                       GL_STREAM_READ);
+        vt->BindFramebuffer(GL_FRAMEBUFFER, sys->framebuffers[i]);
+        vt->BindTexture(GL_TEXTURE_2D, sys->textures[i]);
+        vt->TexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0,
+                       GL_RGBA, GL_UNSIGNED_BYTE, NULL);
+        vt->FramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
+                                 GL_TEXTURE_2D, sys->textures[i], 0);
+
+        struct pbo_picture_context *context = &sys->picture_contexts[i];
+        context->buffer_mapping = NULL;
+        context->lock = &sys->lock;
+        context->cond = &sys->cond;
+        context->context.destroy = picture_context_destroy;
+        context->context.copy = picture_context_copy;
+        context->rc = 0;
+    }
+
+    sys->current_flip = BUFFER_COUNT - 1;
+    BindDrawFramebuffer(sys);
+    wglMakeCurrent(NULL, NULL);
+
+    wglDeleteContext(tmpGLRC);
+    ReleaseDC(NULL, tmpScreenDC);
+
+    return VLC_SUCCESS;
+
+error2:
+    if (sys->pixelBufferDC != NULL)
+        sys->wgl_vt.ReleasePbufferDCARB(sys->hbuffer, sys->pixelBufferDC);
+    if (sys->hbuffer != NULL)
+        sys->wgl_vt.DestroyPbufferARB(sys->hbuffer);
+    wglDeleteContext(sys->hGLRC);
+error1:
+    if (tmpGLRC)
+        wglDeleteContext(tmpGLRC);
+    if (tmpScreenDC)
+        ReleaseDC(NULL, tmpScreenDC);
+    vlc_obj_free(&gl->obj, sys);
+
+    return VLC_EGENERIC;
+}
+
+vlc_module_begin()
+    add_shortcut( "opengl_win_offscreen" )
+    set_shortname( N_("opengl_offscreen") )
+    set_description( N_("offscreen opengl provider for Windows") )
+    set_callback_opengl_offscreen( Open, 1 )
+vlc_module_end()


=====================================
modules/video_output/opengl/Makefile.am
=====================================
@@ -244,3 +244,11 @@ if HAVE_EGL
 vout_LTLIBRARIES += libegl_surfacetexture_plugin.la
 endif
 endif
+
+libopengl_win_offscreen_plugin_la_SOURCES = video_filter/opengl_win_offscreen.c
+if HAVE_WIN32
+if HAVE_GL
+libopengl_win_offscreen_plugin_la_LIBADD = libvlc_opengl.la $(GL_LIBS) -lgdi32
+vout_LTLIBRARIES += libopengl_win_offscreen_plugin.la
+endif
+endif



View it on GitLab: https://code.videolan.org/videolan/vlc/-/commit/05fdfc59c98f8689b2d1b671479f013360d2b9f8

-- 
View it on GitLab: https://code.videolan.org/videolan/vlc/-/commit/05fdfc59c98f8689b2d1b671479f013360d2b9f8
You're receiving this email because of your account on code.videolan.org.


VideoLAN code repository instance


More information about the vlc-commits mailing list