summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorNiklas Haas <git@haasn.xyz>2017-09-14 08:04:55 +0200
committerNiklas Haas <git@haasn.xyz>2017-09-21 15:00:55 +0200
commit65979986a923a8f08019b257c3fe72cd5e8ecf68 (patch)
treeb8f4b8c17d583594aef0ca509064f8b2ff7128d4
parent20f958c9775652c3213588c2a0824f5353276adc (diff)
downloadmpv-65979986a923a8f08019b257c3fe72cd5e8ecf68.tar.bz2
mpv-65979986a923a8f08019b257c3fe72cd5e8ecf68.tar.xz
vo_opengl: refactor into vo_gpu
This is done in several steps: 1. refactor MPGLContext -> struct ra_ctx 2. move GL-specific stuff in vo_opengl into opengl/context.c 3. generalize context creation to support other APIs, and add --gpu-api 4. rename all of the --opengl- options that are no longer opengl-specific 5. move all of the stuff from opengl/* that isn't GL-specific into gpu/ (note: opengl/gl_utils.h became opengl/utils.h) 6. rename vo_opengl to vo_gpu 7. to handle window screenshots, the short-term approach was to just add it to ra_swchain_fns. Long term (and for vulkan) this has to be moved to ra itself (and vo_gpu altered to compensate), but this was a stop-gap measure to prevent this commit from getting too big 8. move ra->fns->flush to ra_gl_ctx instead 9. some other minor changes that I've probably already forgotten Note: This is one half of a major refactor, the other half of which is provided by rossy's following commit. This commit enables support for all linux platforms, while his version enables support for all non-linux platforms. Note 2: vo_opengl_cb.c also re-uses ra_gl_ctx so it benefits from the --opengl- options like --opengl-early-flush, --opengl-finish etc. Should be a strict superset of the old functionality. Disclaimer: Since I have no way of compiling mpv on all platforms, some of these ports were done blindly. Specifically, the blind ports included context_mali_fbdev.c and context_rpi.c. Since they're both based on egl_helpers, the port should have gone smoothly without any major changes required. But if somebody complains about a compile error on those platforms (assuming anybody actually uses them), you know where to complain.
-rw-r--r--DOCS/interface-changes.rst16
-rw-r--r--DOCS/man/mpv.rst6
-rw-r--r--DOCS/man/options.rst170
-rw-r--r--DOCS/man/vo.rst33
-rw-r--r--DOCS/mplayer-changes.rst6
-rw-r--r--etc/builtin.conf6
-rw-r--r--etc/mpv.conf4
-rw-r--r--options/options.c11
-rw-r--r--options/options.h1
-rw-r--r--player/main.c17
-rw-r--r--video/out/gpu/context.c186
-rw-r--r--video/out/gpu/context.h95
-rw-r--r--video/out/gpu/hwdec.c (renamed from video/out/opengl/hwdec.c)0
-rw-r--r--video/out/gpu/hwdec.h (renamed from video/out/opengl/hwdec.h)0
-rw-r--r--video/out/gpu/lcms.c (renamed from video/out/opengl/lcms.c)0
-rw-r--r--video/out/gpu/lcms.h (renamed from video/out/opengl/lcms.h)0
-rw-r--r--video/out/gpu/osd.c (renamed from video/out/opengl/osd.c)0
-rw-r--r--video/out/gpu/osd.h (renamed from video/out/opengl/osd.h)0
-rw-r--r--video/out/gpu/ra.c (renamed from video/out/opengl/ra.c)0
-rw-r--r--video/out/gpu/ra.h (renamed from video/out/opengl/ra.h)3
-rw-r--r--video/out/gpu/shader_cache.c (renamed from video/out/opengl/shader_cache.c)7
-rw-r--r--video/out/gpu/shader_cache.h (renamed from video/out/opengl/shader_cache.h)0
-rw-r--r--video/out/gpu/user_shaders.c (renamed from video/out/opengl/user_shaders.c)2
-rw-r--r--video/out/gpu/user_shaders.h (renamed from video/out/opengl/user_shaders.h)0
-rw-r--r--video/out/gpu/utils.c372
-rw-r--r--video/out/gpu/utils.h120
-rw-r--r--video/out/gpu/video.c (renamed from video/out/opengl/video.c)36
-rw-r--r--video/out/gpu/video.h (renamed from video/out/opengl/video.h)1
-rw-r--r--video/out/gpu/video_shaders.c (renamed from video/out/opengl/video_shaders.c)0
-rw-r--r--video/out/gpu/video_shaders.h (renamed from video/out/opengl/video_shaders.h)0
-rw-r--r--video/out/opengl/common.h4
-rw-r--r--video/out/opengl/context.c446
-rw-r--r--video/out/opengl/context.h152
-rw-r--r--video/out/opengl/context_cocoa.c2
-rw-r--r--video/out/opengl/context_drm_egl.c194
-rw-r--r--video/out/opengl/context_glx.c (renamed from video/out/opengl/context_x11.c)196
-rw-r--r--video/out/opengl/context_mali_fbdev.c58
-rw-r--r--video/out/opengl/context_rpi.c84
-rw-r--r--video/out/opengl/context_vdpau.c202
-rw-r--r--video/out/opengl/context_wayland.c74
-rw-r--r--video/out/opengl/context_x11egl.c84
-rw-r--r--video/out/opengl/egl_helpers.c114
-rw-r--r--video/out/opengl/egl_helpers.h19
-rw-r--r--video/out/opengl/formats.h1
-rw-r--r--video/out/opengl/gl_utils.c291
-rw-r--r--video/out/opengl/gl_utils.h56
-rw-r--r--video/out/opengl/hwdec_cuda.c3
-rw-r--r--video/out/opengl/hwdec_ios.m2
-rw-r--r--video/out/opengl/hwdec_osx.c2
-rw-r--r--video/out/opengl/hwdec_rpi.c2
-rw-r--r--video/out/opengl/hwdec_vaegl.c4
-rw-r--r--video/out/opengl/hwdec_vaglx.c5
-rw-r--r--video/out/opengl/hwdec_vdpau.c2
-rw-r--r--video/out/opengl/ra_gl.c7
-rw-r--r--video/out/opengl/ra_gl.h3
-rw-r--r--video/out/opengl/utils.c524
-rw-r--r--video/out/opengl/utils.h151
-rw-r--r--video/out/vo.c6
-rw-r--r--video/out/vo_gpu.c (renamed from video/out/vo_opengl.c)301
-rw-r--r--video/out/vo_opengl_cb.c53
-rw-r--r--video/out/vo_rpi.c2
-rw-r--r--wscript13
-rw-r--r--wscript_build.py47
63 files changed, 2247 insertions, 1949 deletions
diff --git a/DOCS/interface-changes.rst b/DOCS/interface-changes.rst
index 656a5d204f..8d8870d81d 100644
--- a/DOCS/interface-changes.rst
+++ b/DOCS/interface-changes.rst
@@ -22,6 +22,22 @@ Interface changes
--- mpv 0.28.0 ---
- drop previously deprecated --heartbeat-cmd and --heartbeat--interval
options
+ - rename --vo=opengl to --vo=gpu
+ - rename --opengl-backend to --gpu-context
+ - rename --opengl-shaders to --glsl-shaders
+ - rename --opengl-shader-cache-dir to --gpu-shader-cache-dir
+ - rename --opengl-tex-pad-x/y to --gpu-tex-pad-x/y
+ - rename --opengl-fbo-format to --fbo-format
+ - rename --opengl-gamma to --gamma-factor
+ - rename --opengl-debug to --gpu-debug
+ - rename --opengl-sw to --gpu-sw
+ - rename --opengl-vsync-fences to --swapchain-depth, and the interpretation
+ slightly changed. Now defaults to 3.
+ - rename the built-in profile `opengl-hq` to `gpu-hq`
+ - the semantics of --opengl-es=yes are slightly changed -> now requires GLES
+ - remove the (deprecated) alias --gpu-context=drm-egl
+ - remove the (deprecated) --vo=opengl-hq
+ - remove --opengl-es=force2 (use --opengl-es=yes --opengl-restrict=300)
--- mpv 0.27.0 ---
- drop previously deprecated --field-dominance option
- drop previously deprecated "osd" command
diff --git a/DOCS/man/mpv.rst b/DOCS/man/mpv.rst
index 7202e326e7..a307cc2ff2 100644
--- a/DOCS/man/mpv.rst
+++ b/DOCS/man/mpv.rst
@@ -510,8 +510,8 @@ setting them to *no*. Even suboptions can be specified in this way.
::
- # Use opengl video output by default.
- vo=opengl
+ # Use GPU-accelerated video output by default.
+ vo=gpu
# Use quotes for text that can contain spaces:
status-msg="Time: ${time-pos}"
@@ -582,7 +582,7 @@ profile name ``default`` to continue with normal options.
[slow]
profile-desc="some profile name"
# reference a builtin profile
- profile=opengl-hq
+ profile=gpu-hq
[fast]
vo=vdpau
diff --git a/DOCS/man/options.rst b/DOCS/man/options.rst
index fa2f676190..4a880fb72c 100644
--- a/DOCS/man/options.rst
+++ b/DOCS/man/options.rst
@@ -667,29 +667,29 @@ Video
:auto: enable best hw decoder (see below)
:yes: exactly the same as ``auto``
:auto-copy: enable best hw decoder with copy-back (see below)
- :vdpau: requires ``--vo=vdpau`` or ``--vo=opengl`` (Linux only)
+ :vdpau: requires ``--vo=gpu`` or ``--vo=vdpau`` (Linux only)
:vdpau-copy: copies video back into system RAM (Linux with some GPUs only)
- :vaapi: requires ``--vo=opengl`` or ``--vo=vaapi`` (Linux only)
+ :vaapi: requires ``--vo=gpu`` or ``--vo=vaapi`` (Linux only)
:vaapi-copy: copies video back into system RAM (Linux with Intel GPUs only)
- :videotoolbox: requires ``--vo=opengl`` (OS X 10.8 and up),
+ :videotoolbox: requires ``--vo=gpu`` (OS X 10.8 and up),
or ``--vo=opengl-cb`` (iOS 9.0 and up)
:videotoolbox-copy: copies video back into system RAM (OS X 10.8 or iOS 9.0 and up)
- :dxva2: requires ``--vo=opengl`` with ``--opengl-backend=angle`` or
- ``--opengl-backend=dxinterop`` (Windows only)
+ :dxva2: requires ``--vo=gpu`` with ``--gpu-context=angle`` or
+ ``--gpu-context=dxinterop`` (Windows only)
:dxva2-copy: copies video back to system RAM (Windows only)
- :d3d11va: requires ``--vo=opengl`` with ``--opengl-backend=angle``
+ :d3d11va: requires ``--vo=gpu`` with ``--gpu-context=angle``
(Windows 8+ only)
:d3d11va-copy: copies video back to system RAM (Windows 8+ only)
:mediacodec: copies video back to system RAM (Android only)
- :rpi: requires ``--vo=opengl`` (Raspberry Pi only - default if available)
+ :rpi: requires ``--vo=gpu`` (Raspberry Pi only - default if available)
:rpi-copy: copies video back to system RAM (Raspberry Pi only)
- :cuda: requires ``--vo=opengl`` (Any platform CUDA is available)
+ :cuda: requires ``--vo=gpu`` (Any platform CUDA is available)
:cuda-copy: copies video back to system RAM (Any platform CUDA is available)
:crystalhd: copies video back to system RAM (Any platform supported by hardware)
``auto`` tries to automatically enable hardware decoding using the first
available method. This still depends what VO you are using. For example,
- if you are not using ``--vo=vdpau`` or ``--vo=opengl``, vdpau decoding will
+ if you are not using ``--vo=gpu`` or ``--vo=vdpau``, vdpau decoding will
never be enabled. Also note that if the first found method doesn't actually
work, it will always fall back to software decoding, instead of trying the
next method (might matter on some Linux systems).
@@ -701,10 +701,10 @@ Video
guaranteed to incur no additional loss compared to software decoding, and
will allow CPU processing with video filters.
- The ``vaapi`` mode, if used with ``--vo=opengl``, requires Mesa 11 and most
+ The ``vaapi`` mode, if used with ``--vo=gpu``, requires Mesa 11 and most
likely works with Intel GPUs only. It also requires the opengl EGL backend
(automatically used if available). You can also try the old GLX backend by
- forcing it with ``--opengl-backend=x11``, but the vaapi/GLX interop is
+ forcing it with ``--gpu-context=x11``, but the vaapi/GLX interop is
said to be slower than ``vaapi-copy``.
The ``cuda`` and ``cuda-copy`` modes provides deinterlacing in the decoder
@@ -712,7 +712,7 @@ Video
output path. To use this deinterlacing you must pass the option:
``vd-lavc-o=deint=[weave|bob|adaptive]``.
Pass ``weave`` (or leave the option unset) to not attempt any
- deinterlacing. ``cuda`` should always be preferred unless the ``opengl``
+ deinterlacing. ``cuda`` should always be preferred unless the ``gpu``
vo is not being used or filters are required.
Most video filters will not work with hardware decoding as they are
@@ -739,8 +739,8 @@ Video
be some loss, or even blatantly incorrect results.
In some cases, RGB conversion is forced, which means the RGB conversion
- is performed by the hardware decoding API, instead of the OpenGL code
- used by ``--vo=opengl``. This means certain colorspaces may not display
+ is performed by the hardware decoding API, instead of the shaders
+ used by ``--vo=gpu``. This means certain colorspaces may not display
correctly, and certain filtering (such as debanding) cannot be applied
in an ideal way. This will also usually force the use of low quality
chroma scalers instead of the one specified by ``--cscale``. In other
@@ -772,7 +772,7 @@ Video
completely ordinary video sources.
``rpi`` always uses the hardware overlay renderer, even with
- ``--vo=opengl``.
+ ``--vo=gpu``.
``cuda`` should be safe, but it has been reported to corrupt the
timestamps causing glitched, flashing frames on some files. It can also
@@ -800,13 +800,13 @@ Video
the first thing you should try is disabling it.
``--opengl-hwdec-interop=<name>``
- This is useful for the ``opengl`` and ``opengl-cb`` VOs for creating the
+ This is useful for the ``gpu`` and ``opengl-cb`` VOs for creating the
hardware decoding OpenGL interop context, but without actually enabling
hardware decoding itself (like ``--hwdec`` does).
If set to an empty string (default), the ``--hwdec`` option is used.
- For ``opengl``, if set, do not create the interop context on demand, but
+ For ``gpu``, if set, do not create the interop context on demand, but
when the VO is created.
For ``opengl-cb``, if set, load the interop context as soon as the OpenGL
@@ -1049,7 +1049,7 @@ Video
This can speed up video upload, and may help with large resolutions or
slow hardware. This works only with the following VOs:
- - ``opengl``: requires at least OpenGL 4.4.
+ - ``gpu``: requires at least OpenGL 4.4.
(In particular, this can't be made work with ``opengl-cb``.)
@@ -2402,8 +2402,8 @@ Window
``--force-rgba-osd-rendering``
Change how some video outputs render the OSD and text subtitles. This
does not change appearance of the subtitles and only has performance
- implications. For VOs which support native ASS rendering (like ``vdpau``,
- ``opengl``, ``direct3d``), this can be slightly faster or slower,
+ implications. For VOs which support native ASS rendering (like ``gpu``,
+ ``vdpau``, ``direct3d``), this can be slightly faster or slower,
depending on GPU drivers and hardware. For other VOs, this just makes
rendering slower.
@@ -3903,10 +3903,10 @@ ALSA audio output options
ALSA device).
-OpenGL renderer options
+GPU renderer options
-----------------------
-The following video options are currently all specific to ``--vo=opengl`` and
+The following video options are currently all specific to ``--vo=gpu`` and
``--vo=opengl-cb`` only, which are the only VOs that implement them.
``--scale=<filter>``
@@ -3917,7 +3917,7 @@ The following video options are currently all specific to ``--vo=opengl`` and
is the default for compatibility reasons.
``spline36``
- Mid quality and speed. This is the default when using ``opengl-hq``.
+ Mid quality and speed. This is the default when using ``gpu-hq``.
``lanczos``
Lanczos scaling. Provides mid quality and speed. Generally worse than
@@ -4080,7 +4080,7 @@ The following video options are currently all specific to ``--vo=opengl`` and
``--linear-scaling``
Scale in linear light. It should only be used with a
- ``--opengl-fbo-format`` that has at least 16 bit precision. This option
+ ``--fbo-format`` that has at least 16 bit precision. This option
has no effect on HDR content.
``--correct-downscaling``
@@ -4104,7 +4104,7 @@ The following video options are currently all specific to ``--vo=opengl`` and
the ``--tscale`` setting.
Note that this relies on vsync to work, see ``--opengl-swapinterval`` for
- more information. It should also only be used with an ``--opengl-fbo-format``
+ more information. It should also only be used with an ``--fbo-format``
that has at least 16 bit precision.
``--interpolation-threshold=<0..1,-1>``
@@ -4168,10 +4168,10 @@ The following video options are currently all specific to ``--vo=opengl`` and
``--temporal-dither`` is in use. 1 (the default) will update on every video
frame, 2 on every other frame, etc.
-``--opengl-debug``
- Check for OpenGL errors, i.e. call ``glGetError()``. Also, request a
- debug OpenGL context (which does nothing with current graphics drivers
- as of this writing).
+``--gpu-debug``
+ Enables GPU debugging. What this means depends on the API type. For OpenGL,
+ it calls ``glGetError()``, and requests a debug context. For Vulkan, it
+ enables validation layers.
``--opengl-swapinterval=<n>``
Interval in displayed frames between two buffer swaps. 1 is equivalent to
@@ -4184,7 +4184,7 @@ The following video options are currently all specific to ``--vo=opengl`` and
results, as can missing or incorrect display FPS information (see
``--display-fps``).
-``--opengl-shaders=<file-list>``
+``--glsl-shaders=<file-list>``
Custom GLSL hooks. These are a flexible way to add custom fragment shaders,
which can be injected at almost arbitrary points in the rendering pipeline,
and access all previous intermediate textures. Each use of the option will
@@ -4226,7 +4226,7 @@ The following video options are currently all specific to ``--vo=opengl`` and
FORMAT <name> (required)
The texture format for the samples. Supported texture formats are listed
- in debug logging when the ``opengl`` VO is initialized (look for
+ in debug logging when the ``gpu`` VO is initialized (look for
``Texture formats:``). Usually, this follows OpenGL naming conventions.
For example, ``rgb16`` provides 3 channels with normalized 16 bit
components. One oddity are float formats: for example, ``rgba16f`` has
@@ -4369,8 +4369,8 @@ The following video options are currently all specific to ``--vo=opengl`` and
vec2 tex_offset
Texture offset introduced by user shaders or options like panscan, video-align-x/y, video-pan-x/y.
- Internally, vo_opengl may generate any number of the following textures.
- Whenever a texture is rendered and saved by vo_opengl, all of the passes
+ Internally, vo_gpu may generate any number of the following textures.
+ Whenever a texture is rendered and saved by vo_gpu, all of the passes
that have hooked into it will run, in the order they were added by the
user. This is a list of the legal hook points:
@@ -4416,8 +4416,8 @@ The following video options are currently all specific to ``--vo=opengl`` and
pass. When overwriting a texture marked ``fixed``, the WIDTH, HEIGHT and
OFFSET must be left at their default values.
-``--opengl-shader=<file>``
- CLI/config file only alias for ``--opengl-shaders-append``.
+``--glsl-shader=<file>``
+ CLI/config file only alias for ``--glsl-shaders-append``.
``--deband``
Enable the debanding algorithm. This greatly reduces the amount of visible
@@ -4470,9 +4470,9 @@ The following video options are currently all specific to ``--vo=opengl`` and
``--scale-blur`` option.
``--opengl-glfinish``
- Call ``glFinish()`` before and after swapping buffers (default: disabled).
- Slower, but might improve results when doing framedropping. Can completely
- ruin performance. The details depend entirely on the OpenGL driver.
+ Call ``glFinish()`` before swapping buffers (default: disabled). Slower,
+ but might improve results when doing framedropping. Can completely ruin
+ performance. The details depend entirely on the OpenGL driver.
``--opengl-waitvsync``
Call ``glXWaitVideoSyncSGI`` after each buffer swap (default: disabled).
@@ -4481,15 +4481,6 @@ The following video options are currently all specific to ``--vo=opengl`` and
X11/GLX only.
-``--opengl-vsync-fences=<N>``
- Synchronize the CPU to the Nth past frame using the ``GL_ARB_sync``
- extension. A value of 0 disables this behavior (default). A value of 1
- means it will synchronize to the current frame after rendering it. Like
- ``--glfinish`` and ``--waitvsync``, this can lower or ruin performance. Its
- advantage is that it can span multiple frames, and effectively limit the
- number of frames the GPU queues ahead (which also has an influence on
- vsync).
-
``--opengl-dwmflush=<no|windowed|yes|auto>``
Calls ``DwmFlush`` after swapping buffers on Windows (default: auto). It
also sets ``SwapInterval(0)`` to ignore the OpenGL timing. Values are: no
@@ -4510,7 +4501,7 @@ The following video options are currently all specific to ``--vo=opengl`` and
used to select a lower feature level, which is mainly useful for debugging.
Note that OpenGL ES 3.0 is only supported at feature level 10_1 or higher.
Most extended OpenGL features will not work at lower feature levels
- (similar to ``--opengl-dumb-mode``).
+ (similar to ``--gpu-dumb-mode``).
Windows with ANGLE only.
@@ -4566,7 +4557,7 @@ The following video options are currently all specific to ``--vo=opengl`` and
renderer, though ``--angle-renderer=d3d9`` may give slightly better
performance on old hardware. Note that the D3D9 renderer only supports
OpenGL ES 2.0, so most extended OpenGL features will not work if this
- renderer is selected (similar to ``--opengl-dumb-mode``).
+ renderer is selected (similar to ``--gpu-dumb-mode``).
Windows with ANGLE only.
@@ -4587,13 +4578,21 @@ The following video options are currently all specific to ``--vo=opengl`` and
OS X only.
-``--opengl-sw``
+``--swapchain-depth=<N>``
+ Allow up to N in-flight frames. This essentially controls the frame
+ latency. Increasing the swapchain depth can improve pipelining and prevent
+ missed vsyncs, but increases visible latency. This option only mandates an
+ upper limit, the implementation can use a lower latency than requested
+ internally. A setting of 1 means that the VO will wait for every frame to
+ become visible before starting to render the next frame. (Default: 3)
+
+``--gpu-sw``
Continue even if a software renderer is detected.
-``--opengl-backend=<sys>``
- The value ``auto`` (the default) selects the windowing backend. You can
- also pass ``help`` to get a complete list of compiled in backends (sorted
- by autoprobe order).
+``--gpu-context=<sys>``
+ The value ``auto`` (the default) selects the GPU context. You can also pass
+ ``help`` to get a complete list of compiled in backends (sorted by
+ autoprobe order).
auto
auto-select (default)
@@ -4617,7 +4616,7 @@ The following video options are currently all specific to ``--vo=opengl`` and
wayland
Wayland/EGL
drm
- DRM/EGL (``drm-egl`` is a deprecated alias)
+ DRM/EGL
x11egl
X11/EGL
mali-fbdev
@@ -4628,19 +4627,32 @@ The following video options are currently all specific to ``--vo=opengl`` and
performance problems), and is for doing experiments only. Will not
be used automatically.
+``--gpu-api=<type>``
+ Controls which type of graphics APIs will be accepted:
+
+ auto
+ Use any available API (default)
+ opengl
+ Allow only OpenGL (requires OpenGL 2.1+ or GLES 2.0+)
+
``--opengl-es=<mode>``
- Select whether to use GLES:
+ Controls which type of OpenGL context will be accepted:
+ auto
+ Allow all types of OpenGL (default)
yes
- Try to prefer ES over Desktop GL
- force2
- Try to request a ES 2.0 context (the driver might ignore this)
+ Only allow GLES
no
- Try to prefer desktop GL over ES
- auto
- Use the default for each backend (default)
+ Only allow desktop/core GL
-``--opengl-fbo-format=<fmt>``
+``--opengl-restrict=<version>``
+ Restricts all OpenGL versions above a certain version. Versions are encoded
+ in hundreds, i.e. OpenGL 4.5 -> 450. As an example, --opengl-restrict=300
+ would restrict OpenGL 3.0 and higher, effectively only allowing 2.x
+ contexts. Note that this only imposes a limit on context creation APIs, the
+ actual OpenGL context may still have a higher OpenGL version. (Default: 0)
+
+``--fbo-format=<fmt>``
Selects the internal format of textures used for FBOs. The format can
influence performance and quality of the video output. ``fmt`` can be one
of: rgb8, rgb10, rgb10_a2, rgb16, rgb16f, rgb32f, rgba12, rgba16, rgba16f,
@@ -4648,10 +4660,10 @@ The following video options are currently all specific to ``--vo=opengl`` and
or rgb10_a2 on GLES (e.g. ANGLE), unless GL_EXT_texture_norm16 is
available.
-``--opengl-gamma=<0.1..2.0>``
- Set a gamma value (default: 1.0). If gamma is adjusted in other ways (like
- with the ``--gamma`` option or key bindings and the ``gamma`` property),
- the value is multiplied with the other gamma value.
+``--gamma-factor=<0.1..2.0>``
+ Set an additional raw gamma factor (default: 1.0). If gamma is adjusted in
+ other ways (like with the ``--gamma`` option or key bindings and the
+ ``gamma`` property), the value is multiplied with the other gamma value.
Recommended values based on the environmental brightness:
@@ -4888,7 +4900,7 @@ The following video options are currently all specific to ``--vo=opengl`` and
Blend subtitles directly onto upscaled video frames, before interpolation
and/or color management (default: no). Enabling this causes subtitles to be
affected by ``--icc-profile``, ``--target-prim``, ``--target-trc``,
- ``--interpolation``, ``--opengl-gamma`` and ``--post-shader``. It also
+ ``--interpolation``, ``--gpu-gamma`` and ``--post-shader``. It also
increases subtitle performance when using ``--interpolation``.
The downside of enabling this is that it restricts subtitles to the visible
@@ -4918,7 +4930,7 @@ The following video options are currently all specific to ``--vo=opengl`` and
if the video contains alpha information (which is extremely rare). May
not be supported on all platforms. If alpha framebuffers are
unavailable, it silently falls back on a normal framebuffer. Note that
- if you set the ``--opengl-fbo-format`` option to a non-default value, a
+ if you set the ``--fbo-format`` option to a non-default value, a
format with alpha must be specified, or this won't work.
This does not work on X11 with EGL and Mesa (freedesktop bug 67676).
no
@@ -4933,7 +4945,7 @@ The following video options are currently all specific to ``--vo=opengl`` and
Color used to draw parts of the mpv window not covered by video. See
``--osd-color`` option how colors are defined.
-``--opengl-tex-pad-x``, ``--opengl-tex-pad-y``
+``--gpu-tex-pad-x``, ``--gpu-tex-pad-y``
Enlarge the video source textures by this many pixels. For debugging only
(normally textures are sized exactly, but due to hardware decoding interop
we may have to deal with additional padding, which can be tested with these
@@ -4947,8 +4959,8 @@ The following video options are currently all specific to ``--vo=opengl`` and
flipping GL front and backbuffers immediately (i.e. it doesn't call it
in display-sync mode).
-``--opengl-dumb-mode=<yes|no|auto>``
- This mode is extremely restricted, and will disable most extended OpenGL
+``--gpu-dumb-mode=<yes|no|auto>``
+ This mode is extremely restricted, and will disable most extended
features. That includes high quality scalers and custom shaders!
It is intended for hardware that does not support FBOs (including GLES,
@@ -4961,18 +4973,16 @@ The following video options are currently all specific to ``--vo=opengl`` and
This option might be silently removed in the future.
-``--opengl-shader-cache-dir=<dirname>``
- Store and load compiled GL shaders in this directory. Normally, shader
- compilation is very fast, so this is usually not needed. But some GL
- implementations (notably ANGLE, the default on Windows) have relatively
- slow shader compilation, and can cause startup delays.
+``--gpu-shader-cache-dir=<dirname>``
+ Store and load compiled GLSL shaders in this directory. Normally, shader
+ compilation is very fast, so this is usually not needed. It mostly matters
+ for GPU APIs that require internally recompiling shaders to other languages,
+ for example anything based on ANGLE or Vulkan. Enabling this can improve
+ startup performance on these platforms.
NOTE: This is not cleaned automatically, so old, unused cache files may
stick around indefinitely.
- This option might be silently removed in the future, if ANGLE fixes shader
- compilation speed.
-
``--cuda-decode-device=<auto|0..>``
Choose the GPU device used for decoding when using the ``cuda`` hwdec.
diff --git a/DOCS/man/vo.rst b/DOCS/man/vo.rst
index 1552b217cb..84b3a6a9d9 100644
--- a/DOCS/man/vo.rst
+++ b/DOCS/man/vo.rst
@@ -14,7 +14,7 @@ in the list.
See ``--vo=help`` for a list of compiled-in video output drivers.
- The recommended output driver is ``--vo=opengl``, which is the default. All
+ The recommended output driver is ``--vo=gpu``, which is the default. All
other drivers are for compatibility or special purposes. If the default
does not work, it will fallback to other drivers (in the same order as
listed by ``--vo=help``).
@@ -273,37 +273,34 @@ Available video output drivers are:
``--vo-direct3d-exact-backbuffer``
Always resize the backbuffer to window size.
-``opengl``
- OpenGL video output driver. It supports extended scaling methods, dithering
- and color management.
+``gpu``
+ General purpose, customizable, GPU-accelerated video output driver. It
+ supports extended scaling methods, dithering, color management, custom
+ shaders, HDR, and more.
- See `OpenGL renderer options`_ for options specific to this VO.
+ See `GPU renderer options`_ for options specific to this VO.
By default, it tries to use fast and fail-safe settings. Use the
- ``opengl-hq`` profile to use this driver with defaults set to high
- quality rendering. (This profile is also the replacement for
- ``--vo=opengl-hq``.) The profile can be applied with ``--profile=opengl-hq``
- and its contents can be viewed with ``--show-profile=opengl-hq``.
+ ``gpu-hq`` profile to use this driver with defaults set to high quality
+ rendering. The profile can be applied with ``--profile=gpu-hq`` and its
+ contents can be viewed with ``--show-profile=gpu-hq``.
- Requires at least OpenGL 2.1.
-
- Some features are available with OpenGL 3 capable graphics drivers only
- (or if the necessary extensions are available).
-
- OpenGL ES 2.0 and 3.0 are supported as well.
+ This VO abstracts over several possible graphics APIs and windowing
+ contexts, which can be influenced using the ``--gpu-api`` and
+ ``--gpu-context`` options.
Hardware decoding over OpenGL-interop is supported to some degree. Note
that in this mode, some corner case might not be gracefully handled, and
color space conversion and chroma upsampling is generally in the hand of
the hardware decoder APIs.
- ``opengl`` makes use of FBOs by default. Sometimes you can achieve better
- quality or performance by changing the ``--opengl-fbo-format`` option to
+ ``gpu`` makes use of FBOs by default. Sometimes you can achieve better
+ quality or performance by changing the ``--gpu-fbo-format`` option to
``rgb16f``, ``rgb32f`` or ``rgb``. Known problems include Mesa/Intel not
accepting ``rgb16``, Mesa sometimes not being compiled with float texture
support, and some OS X setups being very slow with ``rgb16`` but fast
with ``rgb32f``. If you have problems, you can also try enabling the
- ``--opengl-dumb-mode=yes`` option.
+ ``--gpu-dumb-mode=yes`` option.
``sdl``
SDL 2.0+ Render video output driver, depending on system with or without
diff --git a/DOCS/mplayer-changes.rst b/DOCS/mplayer-changes.rst
index 7c8ec50a90..66cacb3205 100644
--- a/DOCS/mplayer-changes.rst
+++ b/DOCS/mplayer-changes.rst
@@ -76,8 +76,8 @@ Video
* Wayland support.
* Native support for VAAPI and VDA. Improved VDPAU video output.
-* Improved OpenGL output (see the ``opengl-hq`` video output).
-* Make hardware decoding work with the ``opengl`` video output.
+* Improved GPU-accelerated video output (see the ``gpu-hq`` preset).
+* Make hardware decoding work with the ``gpu`` video output.
* Support for libavfilter (for video->video and audio->audio). This allows
using most of FFmpeg's filters, which improve greatly on the old MPlayer
filters in features, performance, and correctness.
@@ -85,7 +85,7 @@ Video
for BT.2020 (Ultra HD). linear XYZ (Digital Cinema) and SMPTE ST2084 (HDR)
inputs.
* Support for color managed displays, via ICC profiles.
-* High-quality image resamplers (see the ``opengl`` ``scale`` suboption).
+* High-quality image resamplers (see the ``--scale`` suboption).
* Support for scaling in (sigmoidized) linear light.
* Better subtitle rendering using libass by default.
* Improvements when playing multiple files (``-fixed-vo`` is default, do not
diff --git a/etc/builtin.conf b/etc/builtin.conf
index 1d93df9606..ee46f8cb00 100644
--- a/etc/builtin.conf
+++ b/etc/builtin.conf
@@ -36,7 +36,7 @@ load-scripts=no
osc=no
framedrop=no
-[opengl-hq]
+[gpu-hq]
scale=spline36
cscale=spline36
dscale=mitchell
@@ -44,3 +44,7 @@ dither-depth=auto
correct-downscaling=yes
sigmoid-upscaling=yes
deband=yes
+
+# Compatibility alias (deprecated)
+[opengl-hq]
+profile=gpu-hq
diff --git a/etc/mpv.conf b/etc/mpv.conf
index d72c9ee6d7..2a8f8b9f8b 100644
--- a/etc/mpv.conf
+++ b/etc/mpv.conf
@@ -52,9 +52,9 @@
# Keep the player window on top of all other windows.
#ontop=yes
-# Specify high quality video rendering preset (for OpenGL VO only)
+# Specify high quality video rendering preset (for --vo=gpu only)
# Can cause performance problems with some drivers and GPUs.
-#profile=opengl-hq
+#profile=gpu-hq
# Force video to lock on the display's refresh rate, and change video and audio
# speed to some degree to ensure synchronous playback - can cause problems
diff --git a/options/options.c b/options/options.c
index 7dc3b0b160..18b6bb8dd8 100644
--- a/options/options.c
+++ b/options/options.c
@@ -57,8 +57,8 @@
#include "video/out/drm_common.h"
#endif
-#if HAVE_GL
-#include "video/out/opengl/hwdec.h"
+#if HAVE_GPU
+#include "video/out/gpu/hwdec.h"
#endif
static void print_version(struct mp_log *log)
@@ -90,6 +90,7 @@ extern const struct m_obj_list af_obj_list;
extern const struct m_obj_list vo_obj_list;
extern const struct m_obj_list ao_obj_list;
+extern const struct m_sub_options opengl_conf;
extern const struct m_sub_options angle_conf;
extern const struct m_sub_options cocoa_conf;
@@ -687,10 +688,14 @@ const m_option_t mp_opts[] = {
OPT_SUBSTRUCT("", vo, vo_sub_opts, 0),
OPT_SUBSTRUCT("", demux_opts, demux_conf, 0),
-#if HAVE_GL
+#if HAVE_GPU
OPT_SUBSTRUCT("", gl_video_opts, gl_video_conf, 0),
#endif
+#if HAVE_GL
+ OPT_SUBSTRUCT("", opengl_opts, opengl_conf, 0),
+#endif
+
#if HAVE_EGL_ANGLE_WIN32
OPT_SUBSTRUCT("", angle_opts, angle_conf, 0),
#endif
diff --git a/options/options.h b/options/options.h
index 895c12182b..c02b7a34ca 100644
--- a/options/options.h
+++ b/options/options.h
@@ -328,6 +328,7 @@ typedef struct MPOpts {
struct gl_video_opts *gl_video_opts;
struct angle_opts *angle_opts;
+ struct opengl_opts *opengl_opts;
struct cocoa_opts *cocoa_opts;
struct dvd_opts *dvd_opts;
diff --git a/player/main.c b/player/main.c
index 56a4f1d4cf..cf267fb170 100644
--- a/player/main.c
+++ b/player/main.c
@@ -297,21 +297,6 @@ static bool handle_help_options(struct MPContext *mpctx)
return false;
}
-static void handle_deprecated_options(struct MPContext *mpctx)
-{
- struct MPOpts *opts = mpctx->opts;
- struct m_obj_settings *vo = opts->vo->video_driver_list;
- if (vo && vo->name && strcmp(vo->name, "opengl-hq") == 0) {
- MP_WARN(mpctx,
- "--vo=opengl-hq is deprecated! Use --profile=opengl-hq instead.\n");
- // Fudge it. This will replace the --vo option too, which is why we
- // unset/safe it, and later restore it.
- talloc_free(vo->name);
- vo->name = talloc_strdup(NULL, "opengl");
- m_config_set_profile(mpctx->mconfig, "opengl-hq", 0);
- }
-}
-
static int cfg_include(void *ctx, char *filename, int flags)
{
struct MPContext *mpctx = ctx;
@@ -445,8 +430,6 @@ int mp_initialize(struct MPContext *mpctx, char **options)
if (handle_help_options(mpctx))
return -2;
- handle_deprecated_options(mpctx);
-
if (!print_libav_versions(mp_null_log, 0)) {
// Using mismatched libraries can be legitimate, but even then it's
// a bad idea. We don't acknowledge its usefulness and stability.
diff --git a/video/out/gpu/context.c b/video/out/gpu/context.c
new file mode 100644
index 0000000000..dbabba8b3b
--- /dev/null
+++ b/video/out/gpu/context.c
@@ -0,0 +1,186 @@
+/*
+ * This file is part of mpv.
+ *
+ * mpv is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * mpv is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with mpv. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <stddef.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <stdbool.h>
+#include <math.h>
+#include <assert.h>
+
+#include "config.h"
+#include "common/common.h"
+#include "common/msg.h"
+#include "options/options.h"
+#include "options/m_option.h"
+#include "video/out/vo.h"
+
+#include "context.h"
+
+extern const struct ra_ctx_fns ra_ctx_glx;
+extern const struct ra_ctx_fns ra_ctx_glx_probe;
+extern const struct ra_ctx_fns ra_ctx_x11_egl;
+extern const struct ra_ctx_fns ra_ctx_drm_egl;
+extern const struct ra_ctx_fns ra_ctx_cocoa;
+extern const struct ra_ctx_fns ra_ctx_wayland_egl;
+extern const struct ra_ctx_fns ra_ctx_wgl;
+extern const struct ra_ctx_fns ra_ctx_angle;
+extern const struct ra_ctx_fns ra_ctx_dxinterop;
+extern const struct ra_ctx_fns ra_ctx_rpi;
+extern const struct ra_ctx_fns ra_ctx_mali;
+extern const struct ra_ctx_fns ra_ctx_vdpauglx;
+
+static const struct ra_ctx_fns *contexts[] = {
+// OpenGL contexts:
+#if HAVE_RPI
+ &ra_ctx_rpi,
+#endif
+/*
+#if HAVE_GL_COCOA
+ &ra_ctx_cocoa,
+#endif
+#if HAVE_EGL_ANGLE_WIN32
+ &ra_ctx_angle,
+#endif
+#if HAVE_GL_WIN32
+ &ra_ctx_wgl,
+#endif
+#if HAVE_GL_DXINTEROP
+ &ra_ctx_dxinterop,
+#endif
+*/
+#if HAVE_GL_X11
+ &ra_ctx_glx_probe,
+#endif
+#if HAVE_EGL_X11
+ &ra_ctx_x11_egl,
+#endif
+#if HAVE_GL_X11
+ &ra_ctx_glx,
+#endif
+#if HAVE_GL_WAYLAND
+ &ra_ctx_wayland_egl,
+#endif
+#if HAVE_EGL_DRM
+ &ra_ctx_drm_egl,
+#endif
+#if HAVE_MALI_FBDEV
+ &ra_ctx_mali,
+#endif
+#if HAVE_VDPAU_GL_X11
+ &ra_ctx_vdpauglx,
+#endif
+};
+
+static bool get_help(struct mp_log *log, struct bstr param)
+{
+ if (bstr_equals0(param, "help")) {
+ mp_info(log, "GPU contexts / APIs:\n");
+ mp_info(log, " auto (autodetect)\n");
+ for (int n = 0; n < MP_ARRAY_SIZE(contexts); n++)
+ mp_info(log, " %s (%s)\n", contexts[n]->name, contexts[n]->type);
+ return true;
+ }
+
+ return false;
+}
+
+int ra_ctx_validate_api(struct mp_log *log, const struct m_option *opt,
+ struct bstr name, struct bstr param)
+{
+ if (get_help(log, param))
+ return M_OPT_EXIT;
+ if (bstr_equals0(param, "auto"))
+ return 1;
+ for (int i = 0; i < MP_ARRAY_SIZE(contexts); i++) {
+ if (bstr_equals0(param, contexts[i]->type))
+ return 1;
+ }
+ return M_OPT_INVALID;
+}
+
+int ra_ctx_validate_context(struct mp_log *log, const struct m_option *opt,
+ struct bstr name, struct bstr param)
+{
+ if (get_help(log, param))
+ return M_OPT_EXIT;
+ if (bstr_equals0(param, "auto"))
+ return 1;
+ for (int i = 0; i < MP_ARRAY_SIZE(contexts); i++) {
+ if (bstr_equals0(param, contexts[i]->name))
+ return 1;
+ }
+ return M_OPT_INVALID;
+}
+
+// Create a VO window and create a RA context on it.
+// vo_flags: passed to the backend's create window function
+struct ra_ctx *ra_ctx_create(struct vo *vo, const char *context_type,
+ const char *context_name, struct ra_ctx_opts opts)
+{
+ bool api_auto = !context_type || strcmp(context_type, "auto") == 0;
+ bool ctx_auto = !context_name || strcmp(context_name, "auto") == 0;
+
+ if (ctx_auto) {
+ MP_VERBOSE(vo, "Probing for best GPU context.\n");
+ opts.probing = true;
+ }
+
+ // Hack to silence backend (X11/Wayland/etc.) errors. Kill it once backends
+ // are separate from `struct vo`
+ bool old_probing = vo->probing;
+ vo->probing = opts.probing;
+
+ for (int i = 0; i < MP_ARRAY_SIZE(contexts); i++) {
+ if (!opts.probing && strcmp(contexts[i]->name, context_name) != 0)
+ continue;
+ if (!api_auto && strcmp(contexts[i]->type, context_type) != 0)
+ continue;
+
+ struct ra_ctx *ctx = talloc_ptrtype(NULL, ctx);
+ *ctx = (struct ra_ctx) {
+ .vo = vo,
+ .global = vo->global,
+ .log = mp_log_new(ctx, vo->log, contexts[i]->type),
+ .opts = opts,
+ .fns = contexts[i],
+ };
+
+ MP_VERBOSE(ctx, "Initializing GPU context '%s'\n", ctx->fns->name);
+ if (contexts[i]->init(ctx)) {
+ vo->probing = old_probing;
+ return ctx;
+ }
+
+ talloc_free(ctx);
+ }
+
+ // If we've reached this point, then none of the contexts matched the name
+ // requested, or the backend creation failed for all of them.
+ MP_ERR(vo, "Failed initializing any suitable GPU context!\n");
+ vo->probing = old_probing;
+ return NULL;
+}
+
+void ra_ctx_destroy(struct ra_ctx **ctx)
+{
+ if (*ctx)
+ (*ctx)->fns->uninit(*ctx);
+ talloc_free(*ctx);
+ *ctx = NULL;
+}
diff --git a/video/out/gpu/context.h b/video/out/gpu/context.h
new file mode 100644
index 0000000000..42de59b75f
--- /dev/null
+++ b/video/out/gpu/context.h
@@ -0,0 +1,95 @@
+#pragma once
+
+#include "video/out/vo.h"
+
+#include "config.h"
+#include "ra.h"
+
+struct ra_ctx_opts {
+ int allow_sw; // allow software renderers
+ int want_alpha; // create an alpha framebuffer if possible
+ int debug; // enable debugging layers/callbacks etc.
+ bool probing; // the backend was auto-probed
+ int swapchain_depth; // max number of images to render ahead
+};
+
+struct ra_ctx {
+ struct vo *vo;
+ struct ra *ra;
+ struct mpv_global *global;
+ struct mp_log *log;
+
+ struct ra_ctx_opts opts;
+ const struct ra_ctx_fns *fns;
+ struct ra_swapchain *swapchain;
+
+ void *priv;
+};
+
+// The functions that make up a ra_ctx.
+struct ra_ctx_fns {
+ const char *type; // API type (for --gpu-api)
+ const char *name; // name (for --gpu-context)
+
+ // Resize the window, or create a new window if there isn't one yet.
+ // Currently, there is an unfortunate interaction with ctx->vo, and
+ // display size etc. are determined by it.
+ bool (*reconfig)(struct ra_ctx *ctx);
+
+ // This behaves exactly like vo_driver.control().
+ int (*control)(struct ra_ctx *ctx, int *events, int request, void *arg);
+
+ // These behave exactly like vo_driver.wakeup/wait_events. They are
+ // optional.
+ void (*wakeup)(struct ra_ctx *ctx);
+ void (*wait_events)(struct ra_ctx *ctx, int64_t until_time_us);
+
+ // Initialize/destroy the 'struct ra' and possibly the underlying VO backend.
+ // Not normally called by the user of the ra_ctx.
+ bool (*init)(struct ra_ctx *ctx);
+ void (*uninit)(struct ra_ctx *ctx);
+};
+
+// Extra struct for the swapchain-related functions so they can be easily
+// inherited from helpers.
+struct ra_swapchain {
+ struct ra_ctx *ctx;
+ struct priv *priv;
+ const struct ra_swapchain_fns *fns;
+
+ bool flip_v; // flip the rendered image vertically (set by the swapchain)
+};
+
+struct ra_swapchain_fns {
+ // Gets the current framebuffer depth in bits (0 if unknown). Optional.
+ int (*color_depth)(struct ra_swapchain *sw);
+
+ // Retrieves a screenshot of the framebuffer. These are always the right
+ // side up, regardless of ra_swapchain->flip_v. Optional.
+ struct mp_image *(*screenshot)(struct ra_swapchain *sw);
+
+ // Called when rendering starts. Returns NULL on failure. This must be
+ // followed by submit_frame, to submit the rendered frame.
+ struct ra_tex *(*start_frame)(struct ra_swapchain *sw);
+
+ // Present the frame. Issued in lockstep with start_frame, with rendering
+ // commands in between. The `frame` is just there for timing data, for
+ // swapchains smart enough to do something with it.
+ bool (*submit_frame)(struct ra_swapchain *sw, const struct vo_frame *frame);
+
+ // Performs a buffer swap. This blocks for as long as necessary to meet
+ // params.swapchain_depth, or until the next vblank (for vsynced contexts)
+ void (*swap_buffers)(struct ra_swapchain *sw);
+};
+
+// Create and destroy a ra_ctx. This also takes care of creating and destroying
+// the underlying `struct ra`, and perhaps the underlying VO backend.
+struct ra_ctx *ra_ctx_create(struct vo *vo, const char *context_type,
+ const char *context_name, struct ra_ctx_opts opts);
+void ra_ctx_destroy(struct ra_ctx **ctx);
+
+struct m_option;
+int ra_ctx_validate_api(struct mp_log *log, const struct m_option *opt,
+ struct bstr name, struct bstr param);
+int ra_ctx_validate_context(struct mp_log *log, const struct m_option *opt,
+ struct bstr name, struct bstr param);
diff --git a/video/out/opengl/hwdec.c b/video/out/gpu/hwdec.c
index 5fbc1aa4a9..5fbc1aa4a9 100644
--- a/video/out/opengl/hwdec.c
+++ b/video/out/gpu/hwdec.c
diff --git a/video/out/opengl/hwdec.h b/video/out/gpu/hwdec.h
index 20bbaae9eb..20bbaae9eb 100644
--- a/video/out/opengl/hwdec.h
+++ b/video/out/gpu/hwdec.h
diff --git a/video/out/opengl/lcms.c b/video/out/gpu/lcms.c
index 8747ae6aa6..8747ae6aa6 100644
--- a/video/out/opengl/lcms.c
+++ b/video/out/gpu/lcms.c
diff --git a/video/out/opengl/lcms.h b/video/out/gpu/lcms.h
index 35bbd61fe0..35bbd61fe0 100644
--- a/video/out/opengl/lcms.h
+++ b/video/out/gpu/lcms.h
diff --git a/video/out/opengl/osd.c b/video/out/gpu/osd.c
index f7c325d1db..f7c325d1db 100644
--- a/video/out/opengl/osd.c
+++ b/video/out/gpu/osd.c
diff --git a/video/out/opengl/osd.h b/video/out/gpu/osd.h
index 6c2b886de3..6c2b886de3 100644
--- a/video/out/opengl/osd.h
+++ b/video/out/gpu/osd.h
diff --git a/video/out/opengl/ra.c b/video/out/gpu/ra.c
index ef1de54d1a..ef1de54d1a 100644
--- a/video/out/opengl/ra.c
+++ b/video/out/gpu/ra.c
diff --git a/video/out/opengl/ra.h b/video/out/gpu/ra.h
index ae7fb9aea7..76f98397f8 100644
--- a/video/out/opengl/ra.h
+++ b/video/out/gpu/ra.h
@@ -436,9 +436,6 @@ struct ra_fns {
// delayed by a few frames. When no value is available, this returns 0.
uint64_t (*timer_stop)(struct ra *ra, ra_timer *timer);
- // Hint that possibly queued up commands should be sent to the GPU. Optional.
- void (*flush)(struct ra *ra);
-
// Associates a marker with any past error messages, for debugging
// purposes. Optional.
void (*debug_marker)(struct ra *ra, const char *msg);
diff --git a/video/out/opengl/shader_cache.c b/video/out/gpu/shader_cache.c
index 90a757617b..afda9cc036 100644
--- a/video/out/opengl/shader_cache.c
+++ b/video/out/gpu/shader_cache.c
@@ -14,7 +14,6 @@
#include "options/path.h"
#include "stream/stream.h"
#include "shader_cache.h"
-#include "formats.h"
#include "utils.h"
// Force cache flush if more than this number of shaders is created.
@@ -361,7 +360,7 @@ void gl_sc_uniform_vec2(struct gl_shader_cache *sc, char *name, float f[2])
u->v.f[1] = f[1];
}
-void gl_sc_uniform_vec3(struct gl_shader_cache *sc, char *name, GLfloat f[3])
+void gl_sc_uniform_vec3(struct gl_shader_cache *sc, char *name, float f[3])
{
struct sc_uniform *u = find_uniform(sc, name);
u->input.type = RA_VARTYPE_FLOAT;
@@ -379,7 +378,7 @@ static void transpose2x2(float r[2 * 2])
}
void gl_sc_uniform_mat2(struct gl_shader_cache *sc, char *name,
- bool transpose, GLfloat *v)
+ bool transpose, float *v)
{
struct sc_uniform *u = find_uniform(sc, name);
u->input.type = RA_VARTYPE_FLOAT;
@@ -401,7 +400,7 @@ static void transpose3x3(float r[3 * 3])
}
void gl_sc_uniform_mat3(struct gl_shader_cache *sc, char *name,
- bool transpose, GLfloat *v)
+ bool transpose, float *v)
{
struct sc_uniform *u = find_uniform(sc, name);
u->input.type = RA_VARTYPE_FLOAT;
diff --git a/video/out/opengl/shader_cache.h b/video/out/gpu/shader_cache.h
index 82a078079b..82a078079b 100644
--- a/video/out/opengl/shader_cache.h
+++ b/video/out/gpu/shader_cache.h
diff --git a/video/out/opengl/user_shaders.c b/video/out/gpu/user_shaders.c
index 58a1ac9e64..446941b03f 100644
--- a/video/out/opengl/user_shaders.c
+++ b/video/out/gpu/user_shaders.c
@@ -17,9 +17,9 @@
#include <assert.h>
+#include "common/msg.h"
#include "misc/ctype.h"
#include "user_shaders.h"
-#include "formats.h"
static bool parse_rpn_szexpr(struct bstr line, struct szexp out[MAX_SZEXP_SIZE])
{
diff --git a/video/out/opengl/user_shaders.h b/video/out/gpu/user_shaders.h
index 94a070c8e2..94a070c8e2 100644
--- a/video/out/opengl/user_shaders.h
+++ b/video/out/gpu/user_shaders.h
diff --git a/video/out/gpu/utils.c b/video/out/gpu/utils.c
new file mode 100644
index 0000000000..f8dcbaac60
--- /dev/null
+++ b/video/out/gpu/utils.c
@@ -0,0 +1,372 @@
+#include "common/msg.h"
+#include "video/out/vo.h"
+#include "utils.h"
+
+// Standard parallel 2D projection, except y1 < y0 means that the coordinate
+// system is flipped, not the projection.
+void gl_transform_ortho(struct gl_transform *t, float x0, float x1,
+ float y0, float y1)
+{
+ if (y1 < y0) {
+ float tmp = y0;
+ y0 = tmp - y1;
+ y1 = tmp;
+ }
+
+ t->m[0][0] = 2.0f / (x1 - x0);
+ t->m[0][1] = 0.0f;
+ t->m[1][0] = 0.0f;
+ t->m[1][1] = 2.0f / (y1 - y0);
+ t->t[0] = -(x1 + x0) / (x1 - x0);
+ t->t[1] = -(y1 + y0) / (y1 - y0);
+}
+
+// Apply the effects of one transformation to another, transforming it in the
+// process. In other words: post-composes t onto x
+void gl_transform_trans(struct gl_transform t, struct gl_transform *x)
+{
+ struct gl_transform xt = *x;
+ x->m[0][0] = t.m[0][0] * xt.m[0][0] + t.m[0][1] * xt.m[1][0];
+ x->m[1][0] = t.m[1][0] * xt.m[0][0] + t.m[1][1] * xt.m[1][0];
+ x->m[0][1] = t.m[0][0] * xt.m[0][1] + t.m[0][1] * xt.m[1][1];
+ x->m[1][1] = t.m[1][0] * xt.m[0][1] + t.m[1][1] * xt.m[1][1];
+ gl_transform_vec(t, &x->t[0], &x->t[1]);
+}
+
+void gl_transform_ortho_fbodst(struct gl_transform *t, struct fbodst fbo)
+{
+ int y_dir = fbo.flip ? -1 : 1;
+ gl_transform_ortho(t, 0, fbo.tex->params.w, 0, fbo.tex->params.h * y_dir);
+}
+
+void ra_buf_pool_uninit(struct ra *ra, struct ra_buf_pool *pool)
+{
+ for (int i = 0; i < pool->num_buffers; i++)
+ ra_buf_free(ra, &pool->buffers[i]);
+
+ talloc_free(pool->buffers);
+ *pool = (struct ra_buf_pool){0};
+}
+
+static bool ra_buf_params_compatible(const struct ra_buf_params *new,
+ const struct ra_buf_params *old)
+{
+ return new->type == old->type &&
+ new->size <= old->size &&
+ new->host_mapped == old->host_mapped &&
+ new->host_mutable == old->host_mutable;
+}
+
+static bool ra_buf_pool_grow(struct ra *ra, struct ra_buf_pool *pool)
+{
+ struct ra_buf *buf = ra_buf_create(ra, &pool->current_params);
+ if (!buf)
+ return false;
+
+ MP_TARRAY_INSERT_AT(NULL, pool->buffers, pool->num_buffers, pool->index, buf);
+ MP_VERBOSE(ra, "Resized buffer pool of type %u to size %d\n",
+ pool->current_params.type, pool->num_buffers);
+ return true;
+}
+
+struct ra_buf *ra_buf_pool_get(struct ra *ra, struct ra_buf_pool *pool,
+ const struct ra_buf_params *params)
+{
+ assert(!params->initial_data);
+
+ if (!ra_buf_params_compatible(params, &pool->current_params)) {
+ ra_buf_pool_uninit(ra, pool);
+ pool->current_params = *params;
+ }
+
+ // Make sure we have at least one buffer available
+ if (!pool->buffers && !ra_buf_pool_grow(ra, pool))
+ return NULL;
+
+ // Make sure the next buffer is available for use
+ if (!ra->fns->buf_poll(ra, pool->buffers[pool->index]) &&
+ !ra_buf_pool_grow(ra, pool))
+ {
+ return NULL;
+ }
+
+ struct ra_buf *buf = pool->buffers[pool->index++];
+ pool->index %= pool->num_buffers;
+
+ return buf;
+}
+
+bool ra_tex_upload_pbo(struct ra *ra, struct ra_buf_pool *pbo,
+ const struct ra_tex_upload_params *params)
+{
+ if (params->buf)
+ return ra->fns->tex_upload(ra, params);
+
+ struct ra_tex *tex = params->tex;
+ size_t row_size = tex->params.dimensions == 2 ? params->stride :
+ tex->params.w * tex->params.format->pixel_size;
+
+ struct ra_buf_params bufparams = {
+ .type = RA_BUF_TYPE_TEX_UPLOAD,
+ .size = row_size * tex->params.h * tex->params.d,
+ .host_mutable = true,
+ };
+
+ struct ra_buf *buf = ra_buf_pool_get(ra, pbo, &bufparams);
+ if (!buf)
+ return false;
+
+ ra->fns->buf_update(ra, buf, 0, params->src, bufparams.size);
+
+ struct ra_tex_upload_params newparams = *params;
+ newparams.buf = buf;
+ newparams.src = NULL;
+
+ return ra->fns->tex_upload(ra, &newparams);
+}
+
+struct ra_layout std140_layout(struct ra_renderpass_input *inp)
+{
+ size_t el_size = ra_vartype_size(inp->type);
+
+ // std140 packing rules:
+ // 1. The alignment of generic values is their size in bytes
+ // 2. The alignment of vectors is the vector length * the base count, with
+ // the exception of vec3 which is always aligned like vec4
+ // 3. The alignment of arrays is that of the element size rounded up to
+ // the nearest multiple of vec4
+ // 4. Matrices are treated like arrays of vectors
+ // 5. Arrays/matrices are laid out with a stride equal to the alignment
+ size_t size = el_size * inp->dim_v;
+ if (inp->dim_v == 3)
+ size += el_size;
+ if (inp->dim_m > 1)
+ size = MP_ALIGN_UP(size, sizeof(float[4]));
+
+ return (struct ra_layout) {
+ .align = size,
+ .stride = size,
+ .size = size * inp->dim_m,
+ };
+}
+
+struct ra_layout std430_layout(struct ra_renderpass_input *inp)
+{
+ size_t el_size = ra_vartype_size(inp->type);
+
+ // std430 packing rules: like std140, except arrays/matrices are always
+ // "tightly" packed, even arrays/matrices of vec3s
+ size_t size = el_size * inp->dim_v;
+ if (inp->dim_v == 3 && inp->dim_m == 1)
+ size += el_size;
+
+ return (struct ra_layout) {
+ .align = size,
+ .stride = size,
+ .size = size * inp->dim_m,
+ };
+}
+
+// Create a texture and a FBO using the texture as color attachments.
+// fmt: texture internal format
+// If the parameters are the same as the previous call, do not touch it.
+// flags can be 0, or a combination of FBOTEX_FUZZY_W and FBOTEX_FUZZY_H.
+// Enabling FUZZY for W or H means the w or h does not need to be exact.
+bool fbotex_change(struct fbotex *fbo, struct ra *ra, struct mp_log *log,
+ int w, int h, const struct ra_format *fmt, int flags)
+{
+ int lw = w, lh = h;
+
+ if (fbo->tex) {
+ int cw = w, ch = h;
+ int rw = fbo->tex->params.w, rh = fbo->tex->params.h;
+
+ if ((flags & FBOTEX_FUZZY_W) && cw < rw)
+ cw = rw;
+ if ((flags & FBOTEX_FUZZY_H) && ch < rh)
+ ch = rh;
+
+ if (rw == cw && rh == ch && fbo->tex->params.format == fmt)
+ goto done;
+ }
+
+ if (flags & FBOTEX_FUZZY_W)
+ w = MP_ALIGN_UP(w, 256);
+ if (flags & FBOTEX_FUZZY_H)
+ h = MP_ALIGN_UP(h, 256);
+
+ mp_verbose(log, "Create FBO: %dx%d (%dx%d)\n", lw, lh, w, h);
+
+ if (!fmt || !fmt->renderable || !fmt->linear_filter) {
+ mp_err(log, "Format %s not supported.\n", fmt ? fmt->name : "(unset)");
+ return false;
+ }
+
+ fbotex_uninit(fbo);
+
+ *fbo = (struct fbotex) {
+ .ra = ra,
+ };
+
+ struct ra_tex_params params = {
+ .dimensions = 2,
+ .w = w,
+ .h = h,
+ .d = 1,
+ .format = fmt,
+ .src_linear = true,
+ .render_src = true,
+ .render_dst = true,
+ .storage_dst = true,
+ .blit_src = true,
+ };
+
+ fbo->tex = ra_tex_create(fbo->ra, &params);
+
+ if (!fbo->tex) {
+ mp_err(log, "Error: framebuffer could not be created.\n");
+ fbotex_uninit(fbo);
+ return false;
+ }
+
+done:
+
+ fbo->lw = lw;
+ fbo->lh = lh;
+
+ fbo->fbo = (struct fbodst){
+ .tex = fbo->tex,
+ };
+
+ return true;
+}
+
+void fbotex_uninit(struct fbotex *fbo)
+{
+ if (fbo->ra) {
+ ra_tex_free(fbo->ra, &fbo->tex);
+ *fbo = (struct fbotex) {0};
+ }
+}
+
+struct timer_pool {
+ struct ra *ra;
+ ra_timer *timer;
+ bool running; // detect invalid usage
+
+ uint64_t samples[VO_PERF_SAMPLE_COUNT];
+ int sample_idx;
+ int sample_count;
+
+ uint64_t sum;
+ uint64_t peak;
+};
+
+struct timer_pool *timer_pool_create(struct ra *ra)
+{
+ if (!ra->fns->timer_create)
+ return NULL;
+
+ ra_timer *timer = ra->fns->timer_create(ra);
+ if (!timer)
+ return NULL;
+
+ struct timer_pool *pool = talloc(NULL, struct timer_pool);
+ if (!pool) {
+ ra->fns->timer_destroy(ra, timer);
+ return NULL;
+ }
+
+ *pool = (struct timer_pool){ .ra = ra, .timer = timer };
+ return pool;
+}
+
+void timer_pool_destroy(struct timer_pool *pool)
+{
+ if (!pool)
+ return;
+
+ pool->ra->fns->timer_destroy(pool->ra, pool->timer);
+ talloc_free(pool);
+}
+
+void timer_pool_start(struct timer_pool *pool)
+{
+ if (!pool)
+ return;
+
+ assert(!pool->running);
+ pool->ra->fns->timer_start(pool->ra, pool->timer);
+ pool->running = true;
+}
+
+void timer_pool_stop(struct timer_pool *pool)
+{
+ if (!pool)
+ return;
+
+ assert(pool->running);
+ uint64_t res = pool->ra->fns->timer_stop(pool->ra, pool->timer);
+ pool->running = false;
+
+ if (res) {
+ // Input res into the buffer and grab the previous value
+ uint64_t old = pool->samples[pool->sample_idx];
+ pool->sample_count = MPMIN(pool->sample_count + 1, VO_PERF_SAMPLE_COUNT);
+ pool->samples[pool->sample_idx++] = res;
+ pool->sample_idx %= VO_PERF_SAMPLE_COUNT;
+ pool->sum = pool->sum + res - old;
+
+ // Update peak if necessary
+ if (res >= pool->peak) {
+ pool->peak = res;
+ } else if (pool->peak == old) {
+ // It's possible that the last peak was the value we just removed,
+ // if so we need to scan for the new peak
+ uint64_t peak = res;
+ for (int i = 0; i < VO_PERF_SAMPLE_COUNT; i++)
+ peak = MPMAX(peak, pool->samples[i]);
+ pool->peak = peak;
+ }
+ }
+}
+
+struct mp_pass_perf timer_pool_measure(struct timer_pool *pool)
+{
+ if (!pool)
+ return (struct mp_pass_perf){0};
+
+ struct mp_pass_perf res = {
+ .peak = pool->peak,
+ .count = pool->sample_count,
+ };
+
+ int idx = pool->sample_idx - pool->sample_count + VO_PERF_SAMPLE_COUNT;
+ for (int i = 0; i < res.count; i++) {
+ idx %= VO_PERF_SAMPLE_COUNT;
+ res.samples[i] = pool->samples[idx++];
+ }
+
+ if (res.count > 0) {
+ res.last = res.samples[res.count - 1];
+ res.avg = pool->sum / res.count;
+ }
+
+ return res;
+}
+
+void mp_log_source(struct mp_log *log, int lev, const char *src)
+{
+ int line = 1;
+ if (!src)
+ return;
+ while (*src) {
+ const char *end = strchr(src, '\n');
+ const char *next = end + 1;
+ if (!end)
+ next = end = src + strlen(src);
+ mp_msg(log, lev, "[%3d] %.*s\n", line, (int)(end - src), src);
+ line++;
+ src = next;
+ }
+}
diff --git a/video/out/gpu/utils.h b/video/out/gpu/utils.h
new file mode 100644
index 0000000000..04695f8085
--- /dev/null
+++ b/video/out/gpu/utils.h
@@ -0,0 +1,120 @@
+#pragma once
+
+#include <stdbool.h>
+#include <math.h>
+
+#include "ra.h"
+
+// A 3x2 matrix, with the translation part separate.
+struct gl_transform {
+ // row-major, e.g. in mathematical notation:
+ // | m[0][0] m[0][1] |
+ // | m[1][0] m[1][1] |
+ float m[2][2];
+ float t[2];
+};
+
+static const struct gl_transform identity_trans = {
+ .m = {{1.0, 0.0}, {0.0, 1.0}},
+ .t = {0.0, 0.0},
+};
+
+void gl_transform_ortho(struct gl_transform *t, float x0, float x1,
+ float y0, float y1);
+
+// This treats m as an affine transformation, in other words m[2][n] gets
+// added to the output.
+static inline void gl_transform_vec(struct gl_transform t, float *x, float *y)
+{
+ float vx = *x, vy = *y;
+ *x = vx * t.m[0][0] + vy * t.m[0][1] + t.t[0];
+ *y = vx * t.m[1][0] + vy * t.m[1][1] + t.t[1];
+}
+
+struct mp_rect_f {
+ float x0, y0, x1, y1;
+};
+
+// Semantic equality (fuzzy comparison)
+static inline bool mp_rect_f_seq(struct mp_rect_f a, struct mp_rect_f b)
+{
+ return fabs(a.x0 - b.x0) < 1e-6 && fabs(a.x1 - b.x1) < 1e-6 &&
+ fabs(a.y0 - b.y0) < 1e-6 && fabs(a.y1 - b.y1) < 1e-6;
+}
+
+static inline void gl_transform_rect(struct gl_transform t, struct mp_rect_f *r)
+{
+ gl_transform_vec(t, &r->x0, &r->y0);
+ gl_transform_vec(t, &r->x1, &r->y1);
+}
+
+static inline bool gl_transform_eq(struct gl_transform a, struct gl_transform b)
+{
+ for (int x = 0; x < 2; x++) {
+ for (int y = 0; y < 2; y++) {
+ if (a.m[x][y] != b.m[x][y])
+ return false;
+ }
+ }
+
+ return a.t[0] == b.t[0] && a.t[1] == b.t[1];
+}
+
+void gl_transform_trans(struct gl_transform t, struct gl_transform *x);
+
+struct fbodst {
+ struct ra_tex *tex;
+ bool flip; // mirror vertically
+};
+
+void gl_transform_ortho_fbodst(struct gl_transform *t, struct fbodst fbo);
+
+// A pool of buffers, which can grow as needed
+struct ra_buf_pool {
+ struct ra_buf_params current_params;
+ struct ra_buf **buffers;
+ int num_buffers;
+ int index;
+};
+
+void ra_buf_pool_uninit(struct ra *ra, struct ra_buf_pool *pool);
+
+// Note: params->initial_data is *not* supported
+struct ra_buf *ra_buf_pool_get(struct ra *ra, struct ra_buf_pool *pool,
+ const struct ra_buf_params *params);
+
+// Helper that wraps ra_tex_upload using texture upload buffers to ensure that
+// params->buf is always set. This is intended for RA-internal usage.
+bool ra_tex_upload_pbo(struct ra *ra, struct ra_buf_pool *pbo,
+ const struct ra_tex_upload_params *params);
+
+// Layout rules for GLSL's packing modes
+struct ra_layout std140_layout(struct ra_renderpass_input *inp);
+struct ra_layout std430_layout(struct ra_renderpass_input *inp);
+
+struct fbotex {
+ struct ra *ra;
+ struct ra_tex *tex;
+ int lw, lh; // logical (configured) size, <= than texture size
+ struct fbodst fbo;
+};
+
+void fbotex_uninit(struct fbotex *fbo);
+bool fbotex_change(struct fbotex *fbo, struct ra *ra, struct mp_log *log,
+ int w, int h, const struct ra_format *fmt, int flags);
+#define FBOTEX_FUZZY_W 1
+#define FBOTEX_FUZZY_H 2
+#define FBOTEX_FUZZY (FBOTEX_FUZZY_W | FBOTEX_FUZZY_H)
+
+// A wrapper around ra_timer that does result pooling, averaging etc.
+struct timer_pool;
+
+struct timer_pool *timer_pool_create(struct ra *ra);
+void timer_pool_destroy(struct timer_pool *pool);
+void timer_pool_start(struct timer_pool *pool);
+void timer_pool_stop(struct timer_pool *pool);
+struct mp_pass_perf timer_pool_measure(struct timer_pool *pool);
+
+// print a multi line string with line numbers (e.g. for shader sources)
+// log, lev: module and log level, as in mp_msg()
+void mp_log_source(struct mp_log *log, int lev, const char *src);
diff --git a/video/out/opengl/video.c b/video/out/gpu/video.c
index 3362381eff..e36fde60e8 100644
--- a/video/out/opengl/video.c
+++ b/video/out/gpu/video.c
@@ -347,9 +347,9 @@ static int validate_window_opt(struct mp_log *log, const m_option_t *opt,
const struct m_sub_options gl_video_conf = {
.opts = (const m_option_t[]) {
- OPT_CHOICE("opengl-dumb-mode", dumb_mode, 0,
+ OPT_CHOICE("gpu-dumb-mode", dumb_mode, 0,
({"auto", 0}, {"yes", 1}, {"no", -1})),
- OPT_FLOATRANGE("opengl-gamma", gamma, 0, 0.1, 2.0),
+ OPT_FLOATRANGE("gamma-factor", gamma, 0, 0.1, 2.0),
OPT_FLAG("gamma-auto", gamma_auto, 0),
OPT_CHOICE_C("target-prim", target_prim, 0, mp_csp_prim_names),
OPT_CHOICE_C("target-trc", target_trc, 0, mp_csp_trc_names),
@@ -376,7 +376,7 @@ const struct m_sub_options gl_video_conf = {
OPT_FLAG("sigmoid-upscaling", sigmoid_upscaling, 0),
OPT_FLOATRANGE("sigmoid-center", sigmoid_center, 0, 0.0, 1.0),
OPT_FLOATRANGE("sigmoid-slope", sigmoid_slope, 0, 1.0, 20.0),
- OPT_STRING("opengl-fbo-format", fbo_format, 0),
+ OPT_STRING("fbo-format", fbo_format, 0),
OPT_CHOICE_OR_INT("dither-depth", dither_depth, 0, -1, 16,
({"no", -1}, {"auto", 0})),
OPT_CHOICE("dither", dither_algo, 0,
@@ -399,18 +399,24 @@ const struct m_sub_options gl_video_conf = {
({"no", BLEND_SUBS_NO},
{"yes", BLEND_SUBS_YES},
{"video", BLEND_SUBS_VIDEO})),
- OPT_PATHLIST("opengl-shaders", user_shaders, 0),
- OPT_CLI_ALIAS("opengl-shader", "opengl-shaders-append"),
+ OPT_PATHLIST("glsl-shaders", user_shaders, 0),
+ OPT_CLI_ALIAS("glsl-shader", "glsl-shaders-append"),
OPT_FLAG("deband", deband, 0),
OPT_SUBSTRUCT("deband", deband_opts, deband_conf, 0),
OPT_FLOAT("sharpen", unsharp, 0),
- OPT_INTRANGE("opengl-tex-pad-x", tex_pad_x, 0, 0, 4096),
- OPT_INTRANGE("opengl-tex-pad-y", tex_pad_y, 0, 0, 4096),
+ OPT_INTRANGE("gpu-tex-pad-x", tex_pad_x, 0, 0, 4096),
+ OPT_INTRANGE("gpu-tex-pad-y", tex_pad_y, 0, 0, 4096),
OPT_SUBSTRUCT("", icc_opts, mp_icc_conf, 0),
- OPT_CHOICE("opengl-early-flush", early_flush, 0,
- ({"no", 0}, {"yes", 1}, {"auto", -1})),
- OPT_STRING("opengl-shader-cache-dir", shader_cache_dir, 0),
+ OPT_STRING("gpu-shader-cache-dir", shader_cache_dir, 0),
OPT_REPLACED("hdr-tone-mapping", "tone-mapping"),
+ OPT_REPLACED("opengl-shaders", "glsl-shaders"),
+ OPT_CLI_ALIAS("opengl-shader", "glsl-shaders-append"),
+ OPT_REPLACED("opengl-shader-cache-dir", "gpu-shader-cache-dir"),
+ OPT_REPLACED("opengl-tex-pad-x", "gpu-tex-pad-x"),
+ OPT_REPLACED("opengl-tex-pad-y", "gpu-tex-pad-y"),
+ OPT_REPLACED("opengl-fbo-format", "fbo-format"),
+ OPT_REPLACED("opengl-dumb-mode", "gpu-dumb-mode"),
+ OPT_REPLACED("opengl-gamma", "gpu-gamma"),
{0}
},
.size = sizeof(struct gl_video_opts),
@@ -3095,16 +3101,6 @@ done:
p->ra->fns->clear(p->ra, target.tex, color, &target_rc);
}
- // The playloop calls this last before waiting some time until it decides
- // to call flip_page(). Tell OpenGL to start execution of the GPU commands
- // while we sleep (this happens asynchronously).
- if ((p->opts.early_flush == -1 && !frame->display_synced) ||
- p->opts.early_flush == 1)
- {
- if (p->ra->fns->flush)
- p->ra->fns->flush(p->ra);
- }
-
p->frames_rendered++;
pass_report_performance(p);
}
diff --git a/video/out/opengl/video.h b/video/out/gpu/video.h
index d163bc8405..884f5914fd 100644
--- a/video/out/opengl/video.h
+++ b/video/out/gpu/video.h
@@ -27,7 +27,6 @@
#include "shader_cache.h"
#include "video/csputils.h"
#include "video/out/filter_kernels.h"
-#include "video/out/vo.h"
// Assume we have this many texture units for sourcing additional passes.
// The actual texture unit assignment is dynamic.
diff --git a/video/out/opengl/video_shaders.c b/video/out/gpu/video_shaders.c
index 60c5ce82ac..60c5ce82ac 100644
--- a/video/out/opengl/video_shaders.c
+++ b/video/out/gpu/video_shaders.c
diff --git a/video/out/opengl/video_shaders.h b/video/out/gpu/video_shaders.h
index 8345e4c598..8345e4c598 100644
--- a/video/out/opengl/video_shaders.h
+++ b/video/out/gpu/video_shaders.h
diff --git a/video/out/opengl/common.h b/video/out/opengl/common.h
index 7b2e3ed497..b9f582b79f 100644
--- a/video/out/opengl/common.h
+++ b/video/out/opengl/common.h
@@ -26,10 +26,10 @@
#include "common/msg.h"
#include "misc/bstr.h"
-#include "video/out/vo.h"
#include "video/csputils.h"
-
#include "video/mp_image.h"
+#include "video/out/vo.h"
+#include "video/out/gpu/ra.h"
#include "gl_headers.h"
diff --git a/video/out/opengl/context.c b/video/out/opengl/context.c
index fe454e9741..d3cdcac3b7 100644
--- a/video/out/opengl/context.c
+++ b/video/out/opengl/context.c
@@ -1,10 +1,4 @@
/*
- * common OpenGL routines
- *
- * copyleft (C) 2005-2010 Reimar Döffinger <Reimar.Doeffinger@gmx.de>
- * Special thanks go to the xine team and Matthias Hopf, whose video_out_opengl.c
- * gave me lots of good ideas.
- *
* This file is part of mpv.
*
* mpv is free software; you can redistribute it and/or
@@ -21,73 +15,10 @@
* License along with mpv. If not, see <http://www.gnu.org/licenses/>.
*/
-#include <stddef.h>
-#include <stdlib.h>
-#include <stdio.h>
-#include <string.h>
-#include <stdbool.h>
-#include <math.h>
-#include <assert.h>
-
+#include "options/m_config.h"
#include "context.h"
-#include "common/common.h"
-#include "options/options.h"
-#include "options/m_option.h"
-
-extern const struct mpgl_driver mpgl_driver_x11;
-extern const struct mpgl_driver mpgl_driver_x11egl;
-extern const struct mpgl_driver mpgl_driver_x11_probe;
-extern const struct mpgl_driver mpgl_driver_drm_egl;
-extern const struct mpgl_driver mpgl_driver_drm;
-extern const struct mpgl_driver mpgl_driver_cocoa;
-extern const struct mpgl_driver mpgl_driver_wayland;
-extern const struct mpgl_driver mpgl_driver_w32;
-extern const struct mpgl_driver mpgl_driver_angle;
-extern const struct mpgl_driver mpgl_driver_angle_es2;
-extern const struct mpgl_driver mpgl_driver_dxinterop;
-extern const struct mpgl_driver mpgl_driver_rpi;
-extern const struct mpgl_driver mpgl_driver_mali;
-extern const struct mpgl_driver mpgl_driver_vdpauglx;
-
-static const struct mpgl_driver *const backends[] = {
-#if HAVE_RPI
- &mpgl_driver_rpi,
-#endif
-#if HAVE_GL_COCOA
- &mpgl_driver_cocoa,
-#endif
-#if HAVE_EGL_ANGLE_WIN32
- &mpgl_driver_angle,
-#endif
-#if HAVE_GL_WIN32
- &mpgl_driver_w32,
-#endif
-#if HAVE_GL_DXINTEROP
- &mpgl_driver_dxinterop,
-#endif
-#if HAVE_GL_X11
- &mpgl_driver_x11_probe,
-#endif
-#if HAVE_EGL_X11
- &mpgl_driver_x11egl,
-#endif
-#if HAVE_GL_X11
- &mpgl_driver_x11,
-#endif
-#if HAVE_GL_WAYLAND
- &mpgl_driver_wayland,
-#endif
-#if HAVE_EGL_DRM
- &mpgl_driver_drm,
- &mpgl_driver_drm_egl,
-#endif
-#if HAVE_MALI_FBDEV
- &mpgl_driver_mali,
-#endif
-#if HAVE_VDPAU_GL_X11
- &mpgl_driver_vdpauglx,
-#endif
-};
+#include "ra_gl.h"
+#include "utils.h"
// 0-terminated list of desktop GL versions a backend should try to
// initialize. The first entry is the most preferred version.
@@ -103,140 +34,319 @@ const int mpgl_preferred_gl_versions[] = {
0
};
-int mpgl_find_backend(const char *name)
+enum {
+ FLUSH_NO = 0,
+ FLUSH_YES,
+ FLUSH_AUTO,
+};
+
+enum {
+ GLES_AUTO = 0,
+ GLES_YES,
+ GLES_NO,
+};
+
+struct opengl_opts {
+ int use_glfinish;
+ int waitvsync;
+ int vsync_pattern[2];
+ int swapinterval;
+ int early_flush;
+ int restrict_version;
+ int gles_mode;
+};
+
+#define OPT_BASE_STRUCT struct opengl_opts
+const struct m_sub_options opengl_conf = {
+ .opts = (const struct m_option[]) {
+ OPT_FLAG("opengl-glfinish", use_glfinish, 0),
+ OPT_FLAG("opengl-waitvsync", waitvsync, 0),
+ OPT_INT("opengl-swapinterval", swapinterval, 0),
+ OPT_INTPAIR("opengl-check-pattern", vsync_pattern, 0),
+ OPT_INT("opengl-restrict", restrict_version, 0),
+ OPT_CHOICE("opengl-es", gles_mode, 0,
+ ({"auto", GLES_AUTO}, {"yes", GLES_YES}, {"no", GLES_NO})),
+ OPT_CHOICE("opengl-early-flush", early_flush, 0,
+ ({"no", FLUSH_NO}, {"yes", FLUSH_YES}, {"auto", FLUSH_AUTO})),
+
+ OPT_REPLACED("opengl-debug", "gpu-debug"),
+ OPT_REPLACED("opengl-sw", "gpu-sw"),
+ OPT_REPLACED("opengl-vsync-fences", "swapchain-depth"),
+ OPT_REPLACED("opengl-backend", "gpu-context"),
+ {0},
+ },
+ .defaults = &(const struct opengl_opts) {
+ .swapinterval = 1,
+ },
+ .size = sizeof(struct opengl_opts),
+};
+
+struct priv {
+ GL *gl;
+ struct mp_log *log;
+ struct ra_gl_ctx_params params;
+ struct opengl_opts *opts;
+ struct ra_swapchain_fns fns;
+ GLuint main_fb;
+ struct ra_tex *wrapped_fb; // corresponds to main_fb
+ // for debugging:
+ int frames_rendered;
+ unsigned int prev_sgi_sync_count;
+ // for gl_vsync_pattern
+ int last_pattern;
+ int matches, mismatches;
+ // for swapchain_depth simulation
+ GLsync *vsync_fences;
+ int num_vsync_fences;
+};
+
+bool ra_gl_ctx_test_version(struct ra_ctx *ctx, int version, bool es)
{
- if (name == NULL || strcmp(name, "auto") == 0)
- return -1;
- for (int n = 0; n < MP_ARRAY_SIZE(backends); n++) {
- if (strcmp(backends[n]->name, name) == 0)
- return n;
+ bool ret;
+ struct opengl_opts *opts;
+ void *tmp = talloc_new(NULL);
+ opts = mp_get_config_group(tmp, ctx->global, &opengl_conf);
+
+ // Version too high
+ if (opts->restrict_version && version >= opts->restrict_version) {
+ ret = false;
+ goto done;
}
- return -2;
-}
-int mpgl_validate_backend_opt(struct mp_log *log, const struct m_option *opt,
- struct bstr name, struct bstr param)
-{
- if (bstr_equals0(param, "help")) {
- mp_info(log, "OpenGL windowing backends:\n");
- mp_info(log, " auto (autodetect)\n");
- for (int n = 0; n < MP_ARRAY_SIZE(backends); n++)
- mp_info(log, " %s\n", backends[n]->name);
- return M_OPT_EXIT;
+ switch (opts->gles_mode) {
+ case GLES_YES: ret = es; goto done;
+ case GLES_NO: ret = !es; goto done;
+ case GLES_AUTO: ret = true; goto done;
+ default: abort();
}
- char s[20];
- snprintf(s, sizeof(s), "%.*s", BSTR_P(param));
- return mpgl_find_backend(s) >= -1 ? 1 : M_OPT_INVALID;
+
+done:
+ talloc_free(tmp);
+ return ret;
}
-static void *get_native_display(void *pctx, const char *name)
+static void *get_native_display(void *priv, const char *name)
{
- MPGLContext *ctx = pctx;
- if (!ctx->native_display_type || !name)
+ struct priv *p = priv;
+ if (!p->params.native_display_type || !name)
+ return NULL;
+ if (strcmp(p->params.native_display_type, name) != 0)
return NULL;
- return strcmp(ctx->native_display_type, name) == 0 ? ctx->native_display : NULL;
+
+ return p->params.native_display;
}
-static MPGLContext *init_backend(struct vo *vo, const struct mpgl_driver *driver,
- bool probing, int vo_flags)
+void ra_gl_ctx_uninit(struct ra_ctx *ctx)
{
- MPGLContext *ctx = talloc_ptrtype(NULL, ctx);
- *ctx = (MPGLContext) {
- .gl = talloc_zero(ctx, GL),
- .vo = vo,
- .global = vo->global,
- .driver = driver,
- .log = vo->log,
+ if (ctx->ra)
+ ctx->ra->fns->destroy(ctx->ra);
+ if (ctx->swapchain) {
+ talloc_free(ctx->swapchain);
+ ctx->swapchain = NULL;
+ }
+}
+
+static const struct ra_swapchain_fns ra_gl_swapchain_fns;
+
+bool ra_gl_ctx_init(struct ra_ctx *ctx, GL *gl, struct ra_gl_ctx_params params)
+{
+ struct ra_swapchain *sw = ctx->swapchain = talloc_ptrtype(NULL, sw);
+ *sw = (struct ra_swapchain) {
+ .ctx = ctx,
+ .flip_v = !params.flipped, // OpenGL framebuffers are normally inverted
};
- if (probing)
- vo_flags |= VOFLAG_PROBING;
- bool old_probing = vo->probing;
- vo->probing = probing; // hack; kill it once backends are separate
- MP_VERBOSE(vo, "Initializing OpenGL backend '%s'\n", ctx->driver->name);
- ctx->priv = talloc_zero_size(ctx, ctx->driver->priv_size);
- if (ctx->driver->init(ctx, vo_flags) < 0) {
- vo->probing = old_probing;
- talloc_free(ctx);
- return NULL;
+
+ struct priv *p = sw->priv = talloc_ptrtype(sw, p);
+ *p = (struct priv) {
+ .gl = gl,
+ .log = ctx->log,
+ .params = params,
+ .opts = mp_get_config_group(p, ctx->global, &opengl_conf),
+ .fns = ra_gl_swapchain_fns,
+ };
+
+ sw->fns = &p->fns;
+
+ const struct ra_swapchain_fns *ext = p->params.external_swapchain;
+ if (ext) {
+ if (ext->color_depth)
+ p->fns.color_depth = ext->color_depth;
+ if (ext->screenshot)
+ p->fns.screenshot = ext->screenshot;
+ if (ext->start_frame)
+ p->fns.start_frame = ext->start_frame;
+ if (ext->submit_frame)
+ p->fns.submit_frame = ext->submit_frame;
+ if (ext->swap_buffers)
+ p->fns.swap_buffers = ext->swap_buffers;
}
- vo->probing = old_probing;
- if (!ctx->gl->version && !ctx->gl->es)
- goto cleanup;
+ if (!gl->version && !gl->es)
+ return false;
- if (probing && ctx->gl->es && (vo_flags & VOFLAG_NO_GLES)) {
- MP_VERBOSE(ctx->vo, "Skipping GLES backend.\n");
- goto cleanup;
+ if (gl->mpgl_caps & MPGL_CAP_SW) {
+ MP_WARN(p, "Suspected software renderer or indirect context.\n");
+ if (ctx->opts.probing && !ctx->opts.allow_sw)
+ return false;
}
- if (ctx->gl->mpgl_caps & MPGL_CAP_SW) {
- MP_WARN(ctx->vo, "Suspected software renderer or indirect context.\n");
- if (vo->probing && !(vo_flags & VOFLAG_SW))
- goto cleanup;
+ gl->debug_context = ctx->opts.debug;
+ gl->get_native_display_ctx = p;
+ gl->get_native_display = get_native_display;
+
+ if (gl->SwapInterval) {
+ gl->SwapInterval(p->opts->swapinterval);
+ } else {
+ MP_VERBOSE(p, "GL_*_swap_control extension missing.\n");
}
- ctx->gl->debug_context = !!(vo_flags & VOFLAG_GL_DEBUG);
+ ctx->ra = ra_create_gl(p->gl, ctx->log);
+ return !!ctx->ra;
+}
- ctx->gl->get_native_display_ctx = ctx;
- ctx->gl->get_native_display = get_native_display;
+void ra_gl_ctx_resize(struct ra_swapchain *sw, int w, int h, int fbo)
+{
+ struct priv *p = sw->priv;
+ if (p->main_fb == fbo && p->wrapped_fb && p->wrapped_fb->params.w == w
+ && p->wrapped_fb->params.h == h)
+ return;
- return ctx;
+ if (p->wrapped_fb)
+ ra_tex_free(sw->ctx->ra, &p->wrapped_fb);
-cleanup:
- mpgl_uninit(ctx);
- return NULL;
+ p->main_fb = fbo;
+ p->wrapped_fb = ra_create_wrapped_fb(sw->ctx->ra, fbo, w, h);
}
-// Create a VO window and create a GL context on it.
-// vo_flags: passed to the backend's create window function
-MPGLContext *mpgl_init(struct vo *vo, const char *backend_name, int vo_flags)
+int ra_gl_ctx_color_depth(struct ra_swapchain *sw)
{
- MPGLContext *ctx = NULL;
- int index = mpgl_find_backend(backend_name);
- if (index == -1) {
- for (int n = 0; n < MP_ARRAY_SIZE(backends); n++) {
- ctx = init_backend(vo, backends[n], true, vo_flags);
- if (ctx)
- break;
- }
- // VO forced, but no backend is ok => force the first that works at all
- if (!ctx && !vo->probing) {
- for (int n = 0; n < MP_ARRAY_SIZE(backends); n++) {
- ctx = init_backend(vo, backends[n], false, vo_flags);
- if (ctx)
- break;
- }
- }
- } else if (index >= 0) {
- ctx = init_backend(vo, backends[index], false, vo_flags);
- }
- return ctx;
+ struct priv *p = sw->priv;
+ GL *gl = p->gl;
+
+ if (!p->wrapped_fb)
+ return 0;
+
+ if ((gl->es < 300 && !gl->version) || !(gl->mpgl_caps & MPGL_CAP_FB))
+ return 0;
+
+ gl->BindFramebuffer(GL_FRAMEBUFFER, p->main_fb);
+
+ GLenum obj = gl->version ? GL_BACK_LEFT : GL_BACK;
+ if (p->main_fb)
+ obj = GL_COLOR_ATTACHMENT0;
+
+ GLint depth_g = 0;
+
+ gl->GetFramebufferAttachmentParameteriv(GL_FRAMEBUFFER, obj,
+ GL_FRAMEBUFFER_ATTACHMENT_GREEN_SIZE, &depth_g);
+
+ gl->BindFramebuffer(GL_FRAMEBUFFER, 0);
+
+ return depth_g;
}
-int mpgl_reconfig_window(struct MPGLContext *ctx)
+struct mp_image *ra_gl_ctx_screenshot(struct ra_swapchain *sw)
{
- return ctx->driver->reconfig(ctx);
+ struct priv *p = sw->priv;
+
+ assert(p->wrapped_fb);
+ struct mp_image *screen = gl_read_fbo_contents(p->gl, p->main_fb,
+ p->wrapped_fb->params.w,
+ p->wrapped_fb->params.h);
+
+ // OpenGL FB is also read in flipped order, so we need to flip when the
+ // rendering is *not* flipped, which in our case is whenever
+ // p->params.flipped is true. I hope that made sense
+ if (p->params.flipped)
+ mp_image_vflip(screen);
+
+ return screen;
}
-int mpgl_control(struct MPGLContext *ctx, int *events, int request, void *arg)
+struct ra_tex *ra_gl_ctx_start_frame(struct ra_swapchain *sw)
{
- return ctx->driver->control(ctx, events, request, arg);
+ struct priv *p = sw->priv;
+
+ return p->wrapped_fb;
}
-void mpgl_start_frame(struct MPGLContext *ctx)
+bool ra_gl_ctx_submit_frame(struct ra_swapchain *sw, const struct vo_frame *frame)
{
- if (ctx->driver->start_frame)
- ctx->driver->start_frame(ctx);
+ struct priv *p = sw->priv;
+ GL *gl = p->gl;
+
+ if (p->opts->use_glfinish)
+ gl->Finish();
+
+ if (gl->FenceSync && !p->params.external_swapchain) {
+ GLsync fence = gl->FenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0);
+ if (fence)
+ MP_TARRAY_APPEND(p, p->vsync_fences, p->num_vsync_fences, fence);
+ }
+
+ switch (p->opts->early_flush) {
+ case FLUSH_AUTO:
+ if (frame->display_synced)
+ break;
+ // fall through
+ case FLUSH_YES:
+ gl->Flush();
+ }
+
+ return true;
}
-void mpgl_swap_buffers(struct MPGLContext *ctx)
+static void check_pattern(struct priv *p, int item)
{
- ctx->driver->swap_buffers(ctx);
+ int expected = p->opts->vsync_pattern[p->last_pattern];
+ if (item == expected) {
+ p->last_pattern++;
+ if (p->last_pattern >= 2)
+ p->last_pattern = 0;
+ p->matches++;
+ } else {
+ p->mismatches++;
+ MP_WARN(p, "wrong pattern, expected %d got %d (hit: %d, mis: %d)\n",
+ expected, item, p->matches, p->mismatches);
+ }
}
-void mpgl_uninit(MPGLContext *ctx)
+void ra_gl_ctx_swap_buffers(struct ra_swapchain *sw)
{
- if (ctx)
- ctx->driver->uninit(ctx);
- talloc_free(ctx);
+ struct priv *p = sw->priv;
+ GL *gl = p->gl;
+
+ p->params.swap_buffers(sw->ctx);
+ p->frames_rendered++;
+
+ if (p->frames_rendered > 5 && !sw->ctx->opts.debug)
+ ra_gl_set_debug(sw->ctx->ra, false);
+
+ if ((p->opts->waitvsync || p->opts->vsync_pattern[0])
+ && gl->GetVideoSync)
+ {
+ unsigned int n1 = 0, n2 = 0;
+ gl->GetVideoSync(&n1);
+ if (p->opts->waitvsync)
+ gl->WaitVideoSync(2, (n1 + 1) % 2, &n2);
+ int step = n1 - p->prev_sgi_sync_count;
+ p->prev_sgi_sync_count = n1;
+ MP_DBG(p, "Flip counts: %u->%u, step=%d\n", n1, n2, step);
+ if (p->opts->vsync_pattern[0])
+ check_pattern(p, step);
+ }
+
+ while (p->num_vsync_fences >= sw->ctx->opts.swapchain_depth) {
+ gl->ClientWaitSync(p->vsync_fences[0], GL_SYNC_FLUSH_COMMANDS_BIT, 1e9);
+ gl->DeleteSync(p->vsync_fences[0]);
+ MP_TARRAY_REMOVE_AT(p->vsync_fences, p->num_vsync_fences, 0);
+ }
}
+
+static const struct ra_swapchain_fns ra_gl_swapchain_fns = {
+ .color_depth = ra_gl_ctx_color_depth,
+ .screenshot = ra_gl_ctx_screenshot,
+ .start_frame = ra_gl_ctx_start_frame,
+ .submit_frame = ra_gl_ctx_submit_frame,
+ .swap_buffers = ra_gl_ctx_swap_buffers,
+};
diff --git a/video/out/opengl/context.h b/video/out/opengl/context.h
index 229c5ef54f..bdf426b9b4 100644
--- a/video/out/opengl/context.h
+++ b/video/out/opengl/context.h
@@ -1,116 +1,56 @@
-/*
- * common OpenGL routines
- *
- * copyleft (C) 2005-2010 Reimar Döffinger <Reimar.Doeffinger@gmx.de>
- * Special thanks go to the xine team and Matthias Hopf, whose video_out_opengl.c
- * gave me lots of good ideas.
- *
- * This file is part of mpv.
- *
- * mpv is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2.1 of the License, or (at your option) any later version.
- *
- * mpv is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with mpv. If not, see <http://www.gnu.org/licenses/>.
- */
-
-#ifndef MP_GL_CONTEXT_H_
-#define MP_GL_CONTEXT_H_
+#pragma once
+#include "common/global.h"
+#include "video/out/gpu/context.h"
#include "common.h"
-enum {
- VOFLAG_GLES = 1 << 0, // Hint to create a GLES context
- VOFLAG_NO_GLES = 1 << 1, // Hint to create a desktop GL context
- VOFLAG_GL_DEBUG = 1 << 2, // Hint to request debug OpenGL context
- VOFLAG_ALPHA = 1 << 3, // Hint to request alpha framebuffer
- VOFLAG_SW = 1 << 4, // Hint to accept a software GL renderer
- VOFLAG_PROBING = 1 << 6, // The backend is being auto-probed.
- VOFLAG_GLES2 = 1 << 7, // Hint for GLESv2 (needs VOFLAG_GLES)
-};
-
extern const int mpgl_preferred_gl_versions[];
-struct MPGLContext;
-
-// A windowing backend (like X11, win32, ...), which provides OpenGL rendering.
-struct mpgl_driver {
- const char *name;
-
- // Size of the struct allocated for MPGLContext.priv
- int priv_size;
-
- // Init the GL context and possibly the underlying VO backend.
- // The created context should be compatible to GL 3.2 core profile, but
- // some other GL versions are supported as well (e.g. GL 2.1 or GLES 2).
- // Return 0 on success, negative value (-1) on error.
- int (*init)(struct MPGLContext *ctx, int vo_flags);
-
- // Resize the window, or create a new window if there isn't one yet.
- // Currently, there is an unfortunate interaction with ctx->vo, and
- // display size etc. are determined by it.
- // Return 0 on success, negative value (-1) on error.
- int (*reconfig)(struct MPGLContext *ctx);
-
- // Called when rendering starts. The backend can map or resize the
- // framebuffer, or update GL.main_fb. swap_buffers() ends the frame.
- // Optional.
- void (*start_frame)(struct MPGLContext *ctx);
-
- // Present the frame.
- void (*swap_buffers)(struct MPGLContext *ctx);
-
- // This behaves exactly like vo_driver.control().
- int (*control)(struct MPGLContext *ctx, int *events, int request, void *arg);
-
- // These behave exactly like vo_driver.wakeup/wait_events. They are
- // optional.
- void (*wakeup)(struct MPGLContext *ctx);
- void (*wait_events)(struct MPGLContext *ctx, int64_t until_time_us);
-
- // Destroy the GL context and possibly the underlying VO backend.
- void (*uninit)(struct MPGLContext *ctx);
-};
-
-typedef struct MPGLContext {
- GL *gl;
- struct vo *vo;
- const struct mpgl_driver *driver;
- struct mpv_global *global;
- struct mp_log *log;
-
- // For hwdec_vaegl.c.
+// Returns whether or not a candidate GL version should be accepted or not
+// (based on the --opengl opts). Implementations may call this before
+// ra_gl_ctx_init if they wish to probe for multiple possible GL versions.
+bool ra_gl_ctx_test_version(struct ra_ctx *ctx, int version, bool es);
+
+// These are a set of helpers for ra_ctx providers based on ra_gl.
+// The init function also initializes ctx->ra and ctx->swapchain, so the user
+// doesn't have to do this manually. (Similarly, the uninit function will
+// clean them up)
+
+struct ra_gl_ctx_params {
+ // Set to the platform-specific function to swap buffers, like
+ // glXSwapBuffers, eglSwapBuffers etc. This will be called by
+ // ra_gl_ctx_swap_buffers. Required unless you either never call that
+ // function or if you override it yourself.
+ void (*swap_buffers)(struct ra_ctx *ctx);
+
+ // Set to false if the implementation follows normal GL semantics, which is
+ // upside down. Set to true if it does *not*, i.e. if rendering is right
+ // side up
+ bool flipped;
+
+ // If this is set to non-NULL, then the ra_gl_ctx will consider the GL
+ // implementation to be using an external swapchain, which disables the
+ // software simulation of --swapchain-depth. Any functions defined by this
+ // ra_swapchain_fns structs will entirely replace the equivalent ra_gl_ctx
+ // functions in the resulting ra_swapchain.
+ const struct ra_swapchain_fns *external_swapchain;
+
+ // For hwdec_vaegl.c:
const char *native_display_type;
void *native_display;
+};
- // Flip the rendered image vertically. This is useful for dxinterop.
- bool flip_v;
-
- // framebuffer to render to (normally 0)
- GLuint main_fb;
-
- // For free use by the mpgl_driver.
- void *priv;
-} MPGLContext;
-
-MPGLContext *mpgl_init(struct vo *vo, const char *backend_name, int vo_flags);
-void mpgl_uninit(MPGLContext *ctx);
-int mpgl_reconfig_window(struct MPGLContext *ctx);
-int mpgl_control(struct MPGLContext *ctx, int *events, int request, void *arg);
-void mpgl_start_frame(struct MPGLContext *ctx);
-void mpgl_swap_buffers(struct MPGLContext *ctx);
-
-int mpgl_find_backend(const char *name);
+void ra_gl_ctx_uninit(struct ra_ctx *ctx);
+bool ra_gl_ctx_init(struct ra_ctx *ctx, GL *gl, struct ra_gl_ctx_params params);
-struct m_option;
-int mpgl_validate_backend_opt(struct mp_log *log, const struct m_option *opt,
- struct bstr name, struct bstr param);
+// Call this any time the window size or main framebuffer changes
+void ra_gl_ctx_resize(struct ra_swapchain *sw, int w, int h, int fbo);
-#endif
+// These functions are normally set in the ra_swapchain->fns, but if an
+// implementation has a need to override this fns struct with custom functions
+// for whatever reason, these can be used to inherit the original behavior.
+int ra_gl_ctx_color_depth(struct ra_swapchain *sw);
+struct mp_image *ra_gl_ctx_screenshot(struct ra_swapchain *sw);
+struct ra_tex *ra_gl_ctx_start_frame(struct ra_swapchain *sw);
+bool ra_gl_ctx_submit_frame(struct ra_swapchain *sw, const struct vo_frame *frame);
+void ra_gl_ctx_swap_buffers(struct ra_swapchain *sw);
diff --git a/video/out/opengl/context_cocoa.c b/video/out/opengl/context_cocoa.c
index 1d9a10cf38..cdf6faffcd 100644
--- a/video/out/opengl/context_cocoa.c
+++ b/video/out/opengl/context_cocoa.c
@@ -188,4 +188,4 @@ const struct mpgl_driver mpgl_driver_cocoa = {
.swap_buffers = cocoa_swap_buffers,
.control = cocoa_control,
.uninit = cocoa_uninit,
-}; \ No newline at end of file
+};
diff --git a/video/out/opengl/context_drm_egl.c b/video/out/opengl/context_drm_egl.c
index e52fec451b..21b16a52d5 100644
--- a/video/out/opengl/context_drm_egl.c
+++ b/video/out/opengl/context_drm_egl.c
@@ -28,10 +28,12 @@
#include <EGL/egl.h>
#include <EGL/eglext.h>
-#include "context.h"
-#include "egl_helpers.h"
-#include "common/common.h"
#include "video/out/drm_common.h"
+#include "common/common.h"
+
+#include "egl_helpers.h"
+#include "common.h"
+#include "context.h"
#define USE_MASTER 0
@@ -59,6 +61,7 @@ struct egl
};
struct priv {
+ GL gl;
struct kms *kms;
drmEventContext ev;
@@ -75,34 +78,33 @@ struct priv {
struct vt_switcher vt_switcher;
};
-static bool init_egl(struct MPGLContext *ctx, int flags)
+static bool init_egl(struct ra_ctx *ctx)
{
struct priv *p = ctx->priv;
- MP_VERBOSE(ctx->vo, "Initializing EGL\n");
+ MP_VERBOSE(ctx, "Initializing EGL\n");
p->egl.display = eglGetDisplay(p->gbm.device);
if (p->egl.display == EGL_NO_DISPLAY) {
- MP_ERR(ctx->vo, "Failed to get EGL display.\n");
+ MP_ERR(ctx, "Failed to get EGL display.\n");
return false;
}
if (!eglInitialize(p->egl.display, NULL, NULL)) {
- MP_ERR(ctx->vo, "Failed to initialize EGL.\n");
+ MP_ERR(ctx, "Failed to initialize EGL.\n");
return false;
}
EGLConfig config;
- if (!mpegl_create_context(p->egl.display, ctx->vo->log, flags,
- &p->egl.context, &config))
- return -1;
- MP_VERBOSE(ctx->vo, "Initializing EGL surface\n");
+ if (!mpegl_create_context(ctx, p->egl.display, &p->egl.context, &config))
+ return false;
+ MP_VERBOSE(ctx, "Initializing EGL surface\n");
p->egl.surface
= eglCreateWindowSurface(p->egl.display, config, p->gbm.surface, NULL);
if (p->egl.surface == EGL_NO_SURFACE) {
- MP_ERR(ctx->vo, "Failed to create EGL surface.\n");
+ MP_ERR(ctx, "Failed to create EGL surface.\n");
return false;
}
return true;
}
-static bool init_gbm(struct MPGLContext *ctx)
+static bool init_gbm(struct ra_ctx *ctx)
{
struct priv *p = ctx->priv;
MP_VERBOSE(ctx->vo, "Creating GBM device\n");
@@ -136,7 +138,7 @@ static void framebuffer_destroy_callback(struct gbm_bo *bo, void *data)
}
static void update_framebuffer_from_bo(
- const struct MPGLContext *ctx, struct gbm_bo *bo)
+ const struct ra_ctx *ctx, struct gbm_bo *bo)
{
struct priv *p = ctx->priv;
p->fb.bo = bo;
@@ -161,7 +163,7 @@ static void page_flipped(int fd, unsigned int frame, unsigned int sec,
p->waiting_for_flip = false;
}
-static bool crtc_setup(struct MPGLContext *ctx)
+static bool crtc_setup(struct ra_ctx *ctx)
{
struct priv *p = ctx->priv;
if (p->active)
@@ -174,7 +176,7 @@ static bool crtc_setup(struct MPGLContext *ctx)
return ret == 0;
}
-static void crtc_release(struct MPGLContext *ctx)
+static void crtc_release(struct ra_ctx *ctx)
{
struct priv *p = ctx->priv;
@@ -204,7 +206,7 @@ static void crtc_release(struct MPGLContext *ctx)
static void release_vt(void *data)
{
- struct MPGLContext *ctx = data;
+ struct ra_ctx *ctx = data;
MP_VERBOSE(ctx->vo, "Releasing VT");
crtc_release(ctx);
if (USE_MASTER) {
@@ -221,7 +223,7 @@ static void release_vt(void *data)
static void acquire_vt(void *data)
{
- struct MPGLContext *ctx = data;
+ struct ra_ctx *ctx = data;
MP_VERBOSE(ctx->vo, "Acquiring VT");
if (USE_MASTER) {
struct priv *p = ctx->priv;
@@ -234,11 +236,41 @@ static void acquire_vt(void *data)
crtc_setup(ctx);
}
-static void drm_egl_uninit(MPGLContext *ctx)
+static void drm_egl_swap_buffers(struct ra_ctx *ctx)
{
struct priv *p = ctx->priv;
- crtc_release(ctx);
+ eglSwapBuffers(p->egl.display, p->egl.surface);
+ p->gbm.next_bo = gbm_surface_lock_front_buffer(p->gbm.surface);
+ p->waiting_for_flip = true;
+ update_framebuffer_from_bo(ctx, p->gbm.next_bo);
+ int ret = drmModePageFlip(p->kms->fd, p->kms->crtc_id, p->fb.id,
+ DRM_MODE_PAGE_FLIP_EVENT, p);
+ if (ret) {
+ MP_WARN(ctx->vo, "Failed to queue page flip: %s\n", mp_strerror(errno));
+ }
+
+ // poll page flip finish event
+ const int timeout_ms = 3000;
+ struct pollfd fds[1] = { { .events = POLLIN, .fd = p->kms->fd } };
+ poll(fds, 1, timeout_ms);
+ if (fds[0].revents & POLLIN) {
+ ret = drmHandleEvent(p->kms->fd, &p->ev);
+ if (ret != 0) {
+ MP_ERR(ctx->vo, "drmHandleEvent failed: %i\n", ret);
+ return;
+ }
+ }
+
+ gbm_surface_release_buffer(p->gbm.surface, p->gbm.bo);
+ p->gbm.bo = p->gbm.next_bo;
+}
+static void drm_egl_uninit(struct ra_ctx *ctx)
+{
+ struct priv *p = ctx->priv;
+ ra_gl_ctx_uninit(ctx);
+
+ crtc_release(ctx);
if (p->vt_switcher_active)
vt_switcher_destroy(&p->vt_switcher);
@@ -258,19 +290,14 @@ static void drm_egl_uninit(MPGLContext *ctx)
}
}
-static int drm_egl_init(struct MPGLContext *ctx, int flags)
+static bool drm_egl_init(struct ra_ctx *ctx)
{
- if (ctx->vo->probing) {
- MP_VERBOSE(ctx->vo, "DRM EGL backend can be activated only manually.\n");
- return -1;
+ if (ctx->opts.probing) {
+ MP_VERBOSE(ctx, "DRM EGL backend can be activated only manually.\n");
+ return false;
}
- struct priv *p = ctx->priv;
- p->kms = NULL;
- p->old_crtc = NULL;
- p->gbm.surface = NULL;
- p->gbm.device = NULL;
- p->active = false;
- p->waiting_for_flip = false;
+
+ struct priv *p = ctx->priv = talloc_zero(ctx, struct priv);
p->ev.version = DRM_EVENT_CONTEXT_VERSION;
p->ev.page_flip_handler = page_flipped;
@@ -279,79 +306,76 @@ static int drm_egl_init(struct MPGLContext *ctx, int flags)
vt_switcher_acquire(&p->vt_switcher, acquire_vt, ctx);
vt_switcher_release(&p->vt_switcher, release_vt, ctx);
} else {
- MP_WARN(ctx->vo, "Failed to set up VT switcher. Terminal switching will be unavailable.\n");
+ MP_WARN(ctx, "Failed to set up VT switcher. Terminal switching will be unavailable.\n");
}
- MP_VERBOSE(ctx->vo, "Initializing KMS\n");
- p->kms = kms_create(ctx->vo->log, ctx->vo->opts->drm_connector_spec,
+ MP_VERBOSE(ctx, "Initializing KMS\n");
+ p->kms = kms_create(ctx->log, ctx->vo->opts->drm_connector_spec,
ctx->vo->opts->drm_mode_id);
if (!p->kms) {
MP_ERR(ctx->vo, "Failed to create KMS.\n");
- return -1;
+ return false;
}
if (!init_gbm(ctx)) {
MP_ERR(ctx->vo, "Failed to setup GBM.\n");
- return -1;
+ return false;
}
- if (!init_egl(ctx, flags)) {
+ if (!init_egl(ctx)) {
MP_ERR(ctx->vo, "Failed to setup EGL.\n");
- return -1;
+ return false;
}
if (!eglMakeCurrent(p->egl.display, p->egl.surface, p->egl.surface,
p->egl.context)) {
MP_ERR(ctx->vo, "Failed to make context current.\n");
- return -1;
+ return false;
}
- mpegl_load_functions(ctx->gl, ctx->vo->log);
-
- ctx->native_display_type = "drm";
- ctx->native_display = (void *)(intptr_t)p->kms->fd;
-
+ mpegl_load_functions(&p->gl, ctx->vo->log);
// required by gbm_surface_lock_front_buffer
eglSwapBuffers(p->egl.display, p->egl.surface);
- MP_VERBOSE(ctx->vo, "Preparing framebuffer\n");
+ MP_VERBOSE(ctx, "Preparing framebuffer\n");
p->gbm.bo = gbm_surface_lock_front_buffer(p->gbm.surface);
if (!p->gbm.bo) {
- MP_ERR(ctx->vo, "Failed to lock GBM surface.\n");
- return -1;
+ MP_ERR(ctx, "Failed to lock GBM surface.\n");
+ return false;
}
update_framebuffer_from_bo(ctx, p->gbm.bo);
if (!p->fb.id) {
- MP_ERR(ctx->vo, "Failed to create framebuffer.\n");
- return -1;
+ MP_ERR(ctx, "Failed to create framebuffer.\n");
+ return false;
}
if (!crtc_setup(ctx)) {
- MP_ERR(ctx->vo, "Failed to set CRTC for connector %u: %s\n",
+ MP_ERR(ctx, "Failed to set CRTC for connector %u: %s\n",
p->kms->connector->connector_id, mp_strerror(errno));
- return -1;
+ return false;
}
- return 0;
-}
+ struct ra_gl_ctx_params params = {
+ .swap_buffers = drm_egl_swap_buffers,
+ .native_display_type = "drm",
+ .native_display = (void *)(intptr_t)p->kms->fd,
+ };
+ if (!ra_gl_ctx_init(ctx, &p->gl, params))
+ return false;
-static int drm_egl_init_deprecated(struct MPGLContext *ctx, int flags)
-{
- if (ctx->vo->probing)
- return -1;
- MP_WARN(ctx->vo, "'drm-egl' is deprecated, use 'drm' instead.\n");
- return drm_egl_init(ctx, flags);
+ return true;
}
-static int drm_egl_reconfig(struct MPGLContext *ctx)
+static bool drm_egl_reconfig(struct ra_ctx *ctx)
{
struct priv *p = ctx->priv;
ctx->vo->dwidth = p->fb.width;
ctx->vo->dheight = p->fb.height;
- return 0;
+ ra_gl_ctx_resize(ctx->swapchain, p->fb.width, p->fb.height, 0);
+ return true;
}
-static int drm_egl_control(struct MPGLContext *ctx, int *events, int request,
+static int drm_egl_control(struct ra_ctx *ctx, int *events, int request,
void *arg)
{
struct priv *p = ctx->priv;
@@ -367,51 +391,11 @@ static int drm_egl_control(struct MPGLContext *ctx, int *events, int request,
return VO_NOTIMPL;
}
-static void drm_egl_swap_buffers(MPGLContext *ctx)
-{
- struct priv *p = ctx->priv;
- eglSwapBuffers(p->egl.display, p->egl.surface);
- p->gbm.next_bo = gbm_surface_lock_front_buffer(p->gbm.surface);
- p->waiting_for_flip = true;
- update_framebuffer_from_bo(ctx, p->gbm.next_bo);
- int ret = drmModePageFlip(p->kms->fd, p->kms->crtc_id, p->fb.id,
- DRM_MODE_PAGE_FLIP_EVENT, p);
- if (ret) {
- MP_WARN(ctx->vo, "Failed to queue page flip: %s\n", mp_strerror(errno));
- }
-
- // poll page flip finish event
- const int timeout_ms = 3000;
- struct pollfd fds[1] = { { .events = POLLIN, .fd = p->kms->fd } };
- poll(fds, 1, timeout_ms);
- if (fds[0].revents & POLLIN) {
- ret = drmHandleEvent(p->kms->fd, &p->ev);
- if (ret != 0) {
- MP_ERR(ctx->vo, "drmHandleEvent failed: %i\n", ret);
- return;
- }
- }
-
- gbm_surface_release_buffer(p->gbm.surface, p->gbm.bo);
- p->gbm.bo = p->gbm.next_bo;
-}
-
-const struct mpgl_driver mpgl_driver_drm = {
+const struct ra_ctx_fns ra_ctx_drm_egl = {
+ .type = "opengl",
.name = "drm",
- .priv_size = sizeof(struct priv),
- .init = drm_egl_init,
.reconfig = drm_egl_reconfig,
- .swap_buffers = drm_egl_swap_buffers,
- .control = drm_egl_control,
- .uninit = drm_egl_uninit,
-};
-
-const struct mpgl_driver mpgl_driver_drm_egl = {
- .name = "drm-egl",
- .priv_size = sizeof(struct priv),
- .init = drm_egl_init_deprecated,
- .reconfig = drm_egl_reconfig,
- .swap_buffers = drm_egl_swap_buffers,
.control = drm_egl_control,
+ .init = drm_egl_init,
.uninit = drm_egl_uninit,
};
diff --git a/video/out/opengl/context_x11.c b/video/out/opengl/context_glx.c
index 4d8dac1ea5..462f2cf592 100644
--- a/video/out/opengl/context_x11.c
+++ b/video/out/opengl/context_glx.c
@@ -39,43 +39,46 @@
#include "video/out/x11_common.h"
#include "context.h"
+#include "utils.h"
-struct glx_context {
+struct priv {
+ GL gl;
XVisualInfo *vinfo;
GLXContext context;
GLXFBConfig fbc;
};
-static void glx_uninit(MPGLContext *ctx)
+static void glx_uninit(struct ra_ctx *ctx)
{
- struct glx_context *glx_ctx = ctx->priv;
- if (glx_ctx->vinfo)
- XFree(glx_ctx->vinfo);
- if (glx_ctx->context) {
+ struct priv *p = ctx->priv;
+ ra_gl_ctx_uninit(ctx);
+
+ if (p->vinfo)
+ XFree(p->vinfo);
+ if (p->context) {
Display *display = ctx->vo->x11->display;
glXMakeCurrent(display, None, NULL);
- glXDestroyContext(display, glx_ctx->context);
+ glXDestroyContext(display, p->context);
}
+
vo_x11_uninit(ctx->vo);
}
-static bool create_context_x11_old(struct MPGLContext *ctx)
+static bool create_context_x11_old(struct ra_ctx *ctx, GL *gl)
{
- struct glx_context *glx_ctx = ctx->priv;
+ struct priv *p = ctx->priv;
Display *display = ctx->vo->x11->display;
struct vo *vo = ctx->vo;
- GL *gl = ctx->gl;
- if (glx_ctx->context)
+ if (p->context)
return true;
- if (!glx_ctx->vinfo) {
+ if (!p->vinfo) {
MP_FATAL(vo, "Can't create a legacy GLX context without X visual\n");
return false;
}
- GLXContext new_context = glXCreateContext(display, glx_ctx->vinfo, NULL,
- True);
+ GLXContext new_context = glXCreateContext(display, p->vinfo, NULL, True);
if (!new_context) {
MP_FATAL(vo, "Could not create GLX context!\n");
return false;
@@ -91,7 +94,7 @@ static bool create_context_x11_old(struct MPGLContext *ctx)
mpgl_load_functions(gl, (void *)glXGetProcAddressARB, glxstr, vo->log);
- glx_ctx->context = new_context;
+ p->context = new_context;
return true;
}
@@ -99,15 +102,18 @@ static bool create_context_x11_old(struct MPGLContext *ctx)
typedef GLXContext (*glXCreateContextAttribsARBProc)
(Display*, GLXFBConfig, GLXContext, Bool, const int*);
-static bool create_context_x11_gl3(struct MPGLContext *ctx, int vo_flags,
- int gl_version, bool es)
+static bool create_context_x11_gl3(struct ra_ctx *ctx, GL *gl, int gl_version,
+ bool es)
{
- struct glx_context *glx_ctx = ctx->priv;
+ struct priv *p = ctx->priv;
struct vo *vo = ctx->vo;
- if (glx_ctx->context)
+ if (p->context)
return true;
+ if (!ra_gl_ctx_test_version(ctx, gl_version, es))
+ return false;
+
glXCreateContextAttribsARBProc glXCreateContextAttribsARB =
(glXCreateContextAttribsARBProc)
glXGetProcAddressARB((const GLubyte *)"glXCreateContextAttribsARB");
@@ -120,7 +126,7 @@ static bool create_context_x11_gl3(struct MPGLContext *ctx, int vo_flags,
return false;
}
- int ctx_flags = vo_flags & VOFLAG_GL_DEBUG ? GLX_CONTEXT_DEBUG_BIT_ARB : 0;
+ int ctx_flags = ctx->opts.debug ? GLX_CONTEXT_DEBUG_BIT_ARB : 0;
int profile_mask = GLX_CONTEXT_CORE_PROFILE_BIT_ARB;
if (es) {
@@ -138,7 +144,7 @@ static bool create_context_x11_gl3(struct MPGLContext *ctx, int vo_flags,
};
vo_x11_silence_xlib(1);
GLXContext context = glXCreateContextAttribsARB(vo->x11->display,
- glx_ctx->fbc, 0, True,
+ p->fbc, 0, True,
context_attribs);
vo_x11_silence_xlib(-1);
if (!context)
@@ -151,9 +157,9 @@ static bool create_context_x11_gl3(struct MPGLContext *ctx, int vo_flags,
return false;
}
- glx_ctx->context = context;
+ p->context = context;
- mpgl_load_functions(ctx->gl, (void *)glXGetProcAddressARB, glxstr, vo->log);
+ mpgl_load_functions(gl, (void *)glXGetProcAddressARB, glxstr, vo->log);
return true;
}
@@ -162,7 +168,7 @@ static bool create_context_x11_gl3(struct MPGLContext *ctx, int vo_flags,
// http://www.opengl.org/wiki/Tutorial:_OpenGL_3.0_Context_Creation_(GLX)
// but also uses some of the old code.
-static GLXFBConfig select_fb_config(struct vo *vo, const int *attribs, int flags)
+static GLXFBConfig select_fb_config(struct vo *vo, const int *attribs, bool alpha)
{
int fbcount;
GLXFBConfig *fbc = glXChooseFBConfig(vo->x11->display, vo->x11->screen,
@@ -173,7 +179,7 @@ static GLXFBConfig select_fb_config(struct vo *vo, const int *attribs, int flags
// The list in fbc is sorted (so that the first element is the best).
GLXFBConfig fbconfig = fbcount > 0 ? fbc[0] : NULL;
- if (flags & VOFLAG_ALPHA) {
+ if (alpha) {
for (int n = 0; n < fbcount; n++) {
XVisualInfo *v = glXGetVisualFromFBConfig(vo->x11->display, fbc[n]);
if (v) {
@@ -202,10 +208,16 @@ static void set_glx_attrib(int *attribs, int name, int value)
}
}
-static int glx_init(struct MPGLContext *ctx, int flags)
+static void glx_swap_buffers(struct ra_ctx *ctx)
+{
+ glXSwapBuffers(ctx->vo->x11->display, ctx->vo->x11->window);
+}
+
+static bool glx_init(struct ra_ctx *ctx)
{
+ struct priv *p = ctx->priv = talloc_zero(ctx, struct priv);
struct vo *vo = ctx->vo;
- struct glx_context *glx_ctx = ctx->priv;
+ GL *gl = &p->gl;
if (!vo_x11_init(ctx->vo))
goto uninit;
@@ -213,12 +225,12 @@ static int glx_init(struct MPGLContext *ctx, int flags)
int glx_major, glx_minor;
if (!glXQueryVersion(vo->x11->display, &glx_major, &glx_minor)) {
- MP_ERR(vo, "GLX not found.\n");
+ MP_ERR(ctx, "GLX not found.\n");
goto uninit;
}
// FBConfigs were added in GLX version 1.3.
if (MPGL_VER(glx_major, glx_minor) < MPGL_VER(1, 3)) {
- MP_ERR(vo, "GLX version older than 1.3.\n");
+ MP_ERR(ctx, "GLX version older than 1.3.\n");
goto uninit;
}
@@ -233,126 +245,132 @@ static int glx_init(struct MPGLContext *ctx, int flags)
None
};
GLXFBConfig fbc = NULL;
- if (flags & VOFLAG_ALPHA) {
+ if (ctx->opts.want_alpha) {
set_glx_attrib(glx_attribs, GLX_ALPHA_SIZE, 1);
- fbc = select_fb_config(vo, glx_attribs, flags);
- if (!fbc) {
+ fbc = select_fb_config(vo, glx_attribs, true);
+ if (!fbc)
set_glx_attrib(glx_attribs, GLX_ALPHA_SIZE, 0);
- flags &= ~VOFLAG_ALPHA;
- }
}
if (!fbc)
- fbc = select_fb_config(vo, glx_attribs, flags);
+ fbc = select_fb_config(vo, glx_attribs, false);
if (!fbc) {
- MP_ERR(vo, "no GLX support present\n");
+ MP_ERR(ctx, "no GLX support present\n");
goto uninit;
}
int fbid = -1;
if (!glXGetFBConfigAttrib(vo->x11->display, fbc, GLX_FBCONFIG_ID, &fbid))
- MP_VERBOSE(vo, "GLX chose FB config with ID 0x%x\n", fbid);
+ MP_VERBOSE(ctx, "GLX chose FB config with ID 0x%x\n", fbid);
- glx_ctx->fbc = fbc;
- glx_ctx->vinfo = glXGetVisualFromFBConfig(vo->x11->display, fbc);
- if (glx_ctx->vinfo) {
- MP_VERBOSE(vo, "GLX chose visual with ID 0x%x\n",
- (int)glx_ctx->vinfo->visualid);
+ p->fbc = fbc;
+ p->vinfo = glXGetVisualFromFBConfig(vo->x11->display, fbc);
+ if (p->vinfo) {
+ MP_VERBOSE(ctx, "GLX chose visual with ID 0x%x\n",
+ (int)p->vinfo->visualid);
} else {
- MP_WARN(vo, "Selected GLX FB config has no associated X visual\n");
+ MP_WARN(ctx, "Selected GLX FB config has no associated X visual\n");
}
- if (!vo_x11_create_vo_window(vo, glx_ctx->vinfo, "gl"))
+ if (!vo_x11_create_vo_window(vo, p->vinfo, "gl"))
goto uninit;
bool success = false;
- if (!(flags & VOFLAG_GLES)) {
- for (int n = 0; mpgl_preferred_gl_versions[n]; n++) {
- int version = mpgl_preferred_gl_versions[n];
- MP_VERBOSE(vo, "Creating OpenGL %d.%d context...\n",
- MPGL_VER_P(version));
- if (version >= 300) {
- success = create_context_x11_gl3(ctx, flags, version, false);
- } else {
- success = create_context_x11_old(ctx);
- }
- if (success)
- break;
+ for (int n = 0; mpgl_preferred_gl_versions[n]; n++) {
+ int version = mpgl_preferred_gl_versions[n];
+ MP_VERBOSE(ctx, "Creating OpenGL %d.%d context...\n",
+ MPGL_VER_P(version));
+ if (version >= 300) {
+ success = create_context_x11_gl3(ctx, gl, version, false);
+ } else {
+ success = create_context_x11_old(ctx, gl);
}
+ if (success)
+ break;
}
- if (!success) // try ES
- success = create_context_x11_gl3(ctx, flags, 200, true);
- if (success && !glXIsDirect(vo->x11->display, glx_ctx->context))
- ctx->gl->mpgl_caps |= MPGL_CAP_SW;
+ if (!success) // try again for GLES
+ success = create_context_x11_gl3(ctx, gl, 200, true);
+ if (success && !glXIsDirect(vo->x11->display, p->context))
+ gl->mpgl_caps |= MPGL_CAP_SW;
if (!success)
goto uninit;
- return 0;
+ struct ra_gl_ctx_params params = {
+ .swap_buffers = glx_swap_buffers,
+ };
+
+ if (!ra_gl_ctx_init(ctx, gl, params))
+ goto uninit;
+
+ return true;
uninit:
glx_uninit(ctx);
- return -1;
+ return false;
}
-static int glx_init_probe(struct MPGLContext *ctx, int flags)
+static bool glx_init_probe(struct ra_ctx *ctx)
{
- int r = glx_init(ctx, flags);
- if (r >= 0) {
- if (!(ctx->gl->mpgl_caps & MPGL_CAP_VDPAU)) {
- MP_VERBOSE(ctx->vo, "No vdpau support found - probing more things.\n");
- glx_uninit(ctx);
- r = -1;
- }
+ if (!glx_init(ctx))
+ return false;
+
+ struct priv *p = ctx->priv;
+ if (!(p->gl.mpgl_caps & MPGL_CAP_VDPAU)) {
+ MP_VERBOSE(ctx, "No vdpau support found - probing more things.\n");
+ glx_uninit(ctx);
+ return false;
}
- return r;
+
+ return true;
}
-static int glx_reconfig(struct MPGLContext *ctx)
+static void resize(struct ra_ctx *ctx)
{
- vo_x11_config_vo_window(ctx->vo);
- return 0;
+ ra_gl_ctx_resize(ctx->swapchain, ctx->vo->dwidth, ctx->vo->dheight, 0);
}
-static int glx_control(struct MPGLContext *ctx, int *events, int request,
- void *arg)
+static bool glx_reconfig(struct ra_ctx *ctx)
{
- return vo_x11_control(ctx->vo, events, request, arg);
+ vo_x11_config_vo_window(ctx->vo);
+ resize(ctx);
+ return true;
}
-static void glx_swap_buffers(struct MPGLContext *ctx)
+static int glx_control(struct ra_ctx *ctx, int *events, int request, void *arg)
{
- glXSwapBuffers(ctx->vo->x11->display, ctx->vo->x11->window);
+ int ret = vo_x11_control(ctx->vo, events, request, arg);
+ if (*events & VO_EVENT_RESIZE)
+ resize(ctx);
+ return ret;
}
-static void glx_wakeup(struct MPGLContext *ctx)
+static void glx_wakeup(struct ra_ctx *ctx)
{
vo_x11_wakeup(ctx->vo);
}
-static void glx_wait_events(struct MPGLContext *ctx, int64_t until_time_us)
+static void glx_wait_events(struct ra_ctx *ctx, int64_t until_time_us)
{
vo_x11_wait_events(ctx->vo, until_time_us);
}
-const struct mpgl_driver mpgl_driver_x11 = {
+const struct ra_ctx_fns ra_ctx_glx = {
+ .type = "opengl",
.name = "x11",
- .priv_size = sizeof(struct glx_context),
- .init = glx_init,
.reconfig = glx_reconfig,
- .swap_buffers = glx_swap_buffers,
.control = glx_control,
.wakeup = glx_wakeup,
.wait_events = glx_wait_events,
+ .init = glx_init,
.uninit = glx_uninit,
};
-const struct mpgl_driver mpgl_driver_x11_probe = {
+const struct ra_ctx_fns ra_ctx_glx_probe = {
+ .type = "opengl",
.name = "x11probe",
- .priv_size = sizeof(struct glx_context),
- .init = glx_init_probe,
.reconfig = glx_reconfig,
- .swap_buffers = glx_swap_buffers,
.control = glx_control,
.wakeup = glx_wakeup,
.wait_events = glx_wait_events,
+ .init = glx_init_probe,
.uninit = glx_uninit,
};
diff --git a/video/out/opengl/context_mali_fbdev.c b/video/out/opengl/context_mali_fbdev.c
index 66daa7f9ee..8576e536d3 100644
--- a/video/out/opengl/context_mali_fbdev.c
+++ b/video/out/opengl/context_mali_fbdev.c
@@ -50,8 +50,7 @@ static bool get_fbdev_size(int *w, int *h)
}
struct priv {
- struct mp_log *log;
- struct GL *gl;
+ struct GL gl;
EGLDisplay egl_display;
EGLConfig egl_config;
EGLContext egl_context;
@@ -60,9 +59,10 @@ struct priv {
int w, h;
};
-static void mali_uninit(struct MPGLContext *ctx)
+static void mali_uninit(struct ra_ctx *ctx)
{
struct priv *p = ctx->priv;
+ ra_gl_ctx_uninit(ctx);
if (p->egl_surface) {
eglMakeCurrent(p->egl_display, EGL_NO_SURFACE, EGL_NO_SURFACE,
@@ -74,25 +74,29 @@ static void mali_uninit(struct MPGLContext *ctx)
eglReleaseThread();
}
-static int mali_init(struct MPGLContext *ctx, int flags)
+static void mali_swap_buffers(struct ra_ctx *ctx)
{
struct priv *p = ctx->priv;
- p->log = ctx->vo->log;
+ eglSwapBuffers(p->egl_display, p->egl_surface);
+}
+
+static bool mali_init(struct ra_ctx *ctx)
+{
+ struct priv *p = ctx->priv = talloc_zero(ctx, struct priv);
if (!get_fbdev_size(&p->w, &p->h)) {
- MP_FATAL(p, "Could not get fbdev size.\n");
+ MP_FATAL(ctx, "Could not get fbdev size.\n");
goto fail;
}
p->egl_display = eglGetDisplay(EGL_DEFAULT_DISPLAY);
if (!eglInitialize(p->egl_display, NULL, NULL)) {
- MP_FATAL(p, "EGL failed to initialize.\n");
+ MP_FATAL(ctx, "EGL failed to initialize.\n");
goto fail;
}
EGLConfig config;
- if (!mpegl_create_context(p->egl_display, p->log, flags, &p->egl_context,
- &config))
+ if (!mpegl_create_context(ctx, p->egl_display, &p->egl_context, &config))
goto fail;
p->egl_window = (struct fbdev_window){
@@ -104,53 +108,51 @@ static int mali_init(struct MPGLContext *ctx, int flags)
(EGLNativeWindowType)&p->egl_window, NULL);
if (p->egl_surface == EGL_NO_SURFACE) {
- MP_FATAL(p, "Could not create EGL surface!\n");
+ MP_FATAL(ctx, "Could not create EGL surface!\n");
goto fail;
}
if (!eglMakeCurrent(p->egl_display, p->egl_surface, p->egl_surface,
p->egl_context))
{
- MP_FATAL(p, "Failed to set context!\n");
+ MP_FATAL(ctx, "Failed to set context!\n");
goto fail;
}
- ctx->gl = talloc_zero(ctx, GL);
+ mpegl_load_functions(&p->gl, ctx->log);
- mpegl_load_functions(ctx->gl, p->log);
+ struct ra_gl_ctx_params params = {
+ .swap_buffers = mali_swap_buffers,
+ };
+
+ if (!ra_gl_ctx_init(ctx, &p->gl, params))
+ goto fail;
- return 0;
+ return true;
fail:
mali_uninit(ctx);
- return -1;
+ return false;
}
-static int mali_reconfig(struct MPGLContext *ctx)
+static bool mali_reconfig(struct ra_ctx *ctx)
{
struct priv *p = ctx->priv;
ctx->vo->dwidth = p->w;
ctx->vo->dheight = p->h;
- return 0;
+ ra_gl_ctx_resize(ctx->swapchain, p->w, p->h, 0);
}
-static void mali_swap_buffers(MPGLContext *ctx)
-{
- struct priv *p = ctx->priv;
- eglSwapBuffers(p->egl_display, p->egl_surface);
-}
-
-static int mali_control(MPGLContext *ctx, int *events, int request, void *arg)
+static int mali_control(struct ra_ctx *ctx, int *events, int request, void *arg)
{
return VO_NOTIMPL;
}
-const struct mpgl_driver mpgl_driver_mali = {
+const struct ra_ctx_fns ra_ctx_mali_fbdev = {
+ .type = "opengl",
.name = "mali-fbdev",
- .priv_size = sizeof(struct priv),
- .init = mali_init,
.reconfig = mali_reconfig,
- .swap_buffers = mali_swap_buffers,
.control = mali_control,
+ .init = mali_init,
.uninit = mali_uninit,
};
diff --git a/video/out/opengl/context_rpi.c b/video/out/opengl/context_rpi.c
index e79622be5d..8b447d0bfc 100644
--- a/video/out/opengl/context_rpi.c
+++ b/video/out/opengl/context_rpi.c
@@ -30,7 +30,7 @@
#include "egl_helpers.h"
struct priv {
- struct mp_log *log;
+ struct GL gl;
DISPMANX_DISPLAY_HANDLE_T display;
DISPMANX_ELEMENT_HANDLE_T window;
DISPMANX_UPDATE_HANDLE_T update;
@@ -49,13 +49,13 @@ struct priv {
static void tv_callback(void *callback_data, uint32_t reason, uint32_t param1,
uint32_t param2)
{
- struct MPGLContext *ctx = callback_data;
+ struct ra_ctx *ctx = callback_data;
struct priv *p = ctx->priv;
atomic_store(&p->reload_display, true);
vo_wakeup(ctx->vo);
}
-static void destroy_dispmanx(struct MPGLContext *ctx)
+static void destroy_dispmanx(struct ra_ctx *ctx)
{
struct priv *p = ctx->priv;
@@ -77,9 +77,10 @@ static void destroy_dispmanx(struct MPGLContext *ctx)
p->update = 0;
}
-static void rpi_uninit(MPGLContext *ctx)
+static void rpi_uninit(struct ra_ctx *ctx)
{
struct priv *p = ctx->priv;
+ ra_gl_ctx_uninit(ctx);
vc_tv_unregister_callback_full(tv_callback, ctx);
@@ -92,26 +93,26 @@ static void rpi_uninit(MPGLContext *ctx)
p->egl_display = EGL_NO_DISPLAY;
}
-static int recreate_dispmanx(struct MPGLContext *ctx)
+static bool recreate_dispmanx(struct ra_ctx *ctx)
{
struct priv *p = ctx->priv;
int display_nr = 0;
int layer = 0;
- MP_VERBOSE(ctx->vo, "Recreating DISPMANX state...\n");
+ MP_VERBOSE(ctx, "Recreating DISPMANX state...\n");
destroy_dispmanx(ctx);
p->display = vc_dispmanx_display_open(display_nr);
p->update = vc_dispmanx_update_start(0);
if (!p->display || !p->update) {
- MP_FATAL(ctx->vo, "Could not get DISPMANX objects.\n");
+ MP_FATAL(ctx, "Could not get DISPMANX objects.\n");
goto fail;
}
uint32_t dispw, disph;
if (graphics_get_display_size(0, &dispw, &disph) < 0) {
- MP_FATAL(ctx->vo, "Could not get display size.\n");
+ MP_FATAL(ctx, "Could not get display size.\n");
goto fail;
}
p->w = dispw;
@@ -145,7 +146,7 @@ static int recreate_dispmanx(struct MPGLContext *ctx)
&src, DISPMANX_PROTECTION_NONE, &alpha,
0, 0);
if (!p->window) {
- MP_FATAL(ctx->vo, "Could not add DISPMANX element.\n");
+ MP_FATAL(ctx, "Could not add DISPMANX element.\n");
goto fail;
}
@@ -161,14 +162,14 @@ static int recreate_dispmanx(struct MPGLContext *ctx)
&p->egl_window, NULL);
if (p->egl_surface == EGL_NO_SURFACE) {
- MP_FATAL(p, "Could not create EGL surface!\n");
+ MP_FATAL(ctx, "Could not create EGL surface!\n");
goto fail;
}
if (!eglMakeCurrent(p->egl_display, p->egl_surface, p->egl_surface,
p->egl_context))
{
- MP_FATAL(p, "Failed to set context!\n");
+ MP_FATAL(ctx, "Failed to set context!\n");
goto fail;
}
@@ -197,21 +198,27 @@ static int recreate_dispmanx(struct MPGLContext *ctx)
ctx->vo->dwidth = p->w;
ctx->vo->dheight = p->h;
+ ra_gl_ctx_resize(ctx->swapchain, p->w, p->h, 0);
ctx->vo->want_redraw = true;
vo_event(ctx->vo, VO_EVENT_WIN_STATE);
- return 0;
+ return true;
fail:
destroy_dispmanx(ctx);
- return -1;
+ return false;
}
-static int rpi_init(struct MPGLContext *ctx, int flags)
+static void rpi_swap_buffers(struct ra_ctx *ctx)
{
struct priv *p = ctx->priv;
- p->log = ctx->vo->log;
+ eglSwapBuffers(p->egl_display, p->egl_surface);
+}
+
+static bool rpi_init(struct ra_ctx *ctx)
+{
+ struct priv *p = ctx->priv = talloc_zero(ctx, struct priv);
bcm_host_init();
@@ -219,43 +226,40 @@ static int rpi_init(struct MPGLContext *ctx, int flags)
p->egl_display = eglGetDisplay(EGL_DEFAULT_DISPLAY);
if (!eglInitialize(p->egl_display, NULL, NULL)) {
- MP_FATAL(p, "EGL failed to initialize.\n");
+ MP_FATAL(ctx, "EGL failed to initialize.\n");
goto fail;
}
- if (!mpegl_create_context(p->egl_display, p->log, 0, &p->egl_context,
- &p->egl_config))
+ if (!mpegl_create_context(ctx, p->egl_display, &p->egl_context, &p->egl_config))
goto fail;
if (recreate_dispmanx(ctx) < 0)
goto fail;
- ctx->gl = talloc_zero(ctx, GL);
+ mpegl_load_functions(&p->gl, ctx->log);
- mpegl_load_functions(ctx->gl, p->log);
+ struct ra_gl_ctx_params params = {
+ .swap_buffers = rpi_swap_buffers,
+ .native_display_type = "MPV_RPI_WINDOW",
+ .native_display = p->win_params,
+ };
- ctx->native_display_type = "MPV_RPI_WINDOW";
- ctx->native_display = p->win_params;
+ if (!ra_gl_ctx_init(ctx, &p->gl, params))
+ goto fail;
- return 0;
+ return true;
fail:
rpi_uninit(ctx);
- return -1;
+ return false;
}
-static int rpi_reconfig(struct MPGLContext *ctx)
+static bool rpi_reconfig(struct ra_ctx *ctx)
{
return recreate_dispmanx(ctx);
}
-static void rpi_swap_buffers(MPGLContext *ctx)
-{
- struct priv *p = ctx->priv;
- eglSwapBuffers(p->egl_display, p->egl_surface);
-}
-
-static struct mp_image *take_screenshot(struct MPGLContext *ctx)
+static struct mp_image *take_screenshot(struct ra_ctx *ctx)
{
struct priv *p = ctx->priv;
@@ -289,21 +293,20 @@ fail:
return NULL;
}
-
-static int rpi_control(MPGLContext *ctx, int *events, int request, void *arg)
+static int rpi_control(struct ra_ctx *ctx, int *events, int request, void *arg)
{
struct priv *p = ctx->priv;
switch (request) {
case VOCTRL_SCREENSHOT_WIN:
*(struct mp_image **)arg = take_screenshot(ctx);
- return true;
+ return VO_TRUE;
case VOCTRL_FULLSCREEN:
recreate_dispmanx(ctx);
return VO_TRUE;
case VOCTRL_CHECK_EVENTS:
if (atomic_fetch_and(&p->reload_display, 0)) {
- MP_WARN(ctx->vo, "Recovering from display mode switch...\n");
+ MP_WARN(ctx, "Recovering from display mode switch...\n");
recreate_dispmanx(ctx);
}
return VO_TRUE;
@@ -315,12 +318,11 @@ static int rpi_control(MPGLContext *ctx, int *events, int request, void *arg)
return VO_NOTIMPL;
}
-const struct mpgl_driver mpgl_driver_rpi = {
+const struct ra_ctx_fns ra_ctx_rpi = {
+ .type = "opengl",
.name = "rpi",
- .priv_size = sizeof(struct priv),
- .init = rpi_init,
.reconfig = rpi_reconfig,
- .swap_buffers = rpi_swap_buffers,
.control = rpi_control,
+ .init = rpi_init,
.uninit = rpi_uninit,
-}; \ No newline at end of file
+};
diff --git a/video/out/opengl/context_vdpau.c b/video/out/opengl/context_vdpau.c
index 40d21ab65c..a2321f78dd 100644
--- a/video/out/opengl/context_vdpau.c
+++ b/video/out/opengl/context_vdpau.c
@@ -26,8 +26,6 @@
// follow it. I'm not sure about the original nvidia headers.
#define BRAINDEATH(x) ((void *)(uintptr_t)(x))
-#define NUM_SURFACES 4
-
struct surface {
int w, h;
VdpOutputSurface surface;
@@ -39,21 +37,22 @@ struct surface {
};
struct priv {
+ GL gl;
GLXContext context;
struct mp_vdpau_ctx *vdp;
VdpPresentationQueueTarget vdp_target;
VdpPresentationQueue vdp_queue;
+ struct surface *surfaces;
int num_surfaces;
- struct surface surfaces[NUM_SURFACES];
- int current_surface;
+ int idx_surfaces;
};
typedef GLXContext (*glXCreateContextAttribsARBProc)
(Display*, GLXFBConfig, GLXContext, Bool, const int*);
-static bool create_context_x11(struct MPGLContext *ctx, int vo_flags)
+static bool create_context_x11(struct ra_ctx *ctx)
{
- struct priv *glx_ctx = ctx->priv;
+ struct priv *p = ctx->priv;
struct vo *vo = ctx->vo;
int glx_major, glx_minor;
@@ -62,6 +61,9 @@ static bool create_context_x11(struct MPGLContext *ctx, int vo_flags)
return false;
}
+ if (!ra_gl_ctx_test_version(ctx, MPGL_VER(glx_major, glx_minor), false))
+ return false;
+
int glx_attribs[] = {
GLX_X_RENDERABLE, True,
GLX_X_VISUAL_TYPE, GLX_TRUE_COLOR,
@@ -96,7 +98,7 @@ static bool create_context_x11(struct MPGLContext *ctx, int vo_flags)
return false;
}
- int ctx_flags = vo_flags & VOFLAG_GL_DEBUG ? GLX_CONTEXT_DEBUG_BIT_ARB : 0;
+ int ctx_flags = ctx->opts.debug ? GLX_CONTEXT_DEBUG_BIT_ARB : 0;
int context_attribs[] = {
GLX_CONTEXT_MAJOR_VERSION_ARB, 4,
GLX_CONTEXT_MINOR_VERSION_ARB, 0,
@@ -117,19 +119,20 @@ static bool create_context_x11(struct MPGLContext *ctx, int vo_flags)
return false;
}
- glx_ctx->context = context;
- mpgl_load_functions(ctx->gl, (void *)glXGetProcAddressARB, glxstr, vo->log);
+ p->context = context;
+ mpgl_load_functions(&p->gl, (void *)glXGetProcAddressARB, glxstr, vo->log);
return true;
}
-static int create_vdpau_objects(struct MPGLContext *ctx)
+static int create_vdpau_objects(struct ra_ctx *ctx)
{
struct priv *p = ctx->priv;
+ struct GL *gl = &p->gl;
VdpDevice dev = p->vdp->vdp_device;
struct vdp_functions *vdp = &p->vdp->vdp;
VdpStatus vdp_st;
- ctx->gl->VDPAUInitNV(BRAINDEATH(dev), p->vdp->get_proc_address);
+ gl->VDPAUInitNV(BRAINDEATH(dev), p->vdp->get_proc_address);
vdp_st = vdp->presentation_queue_target_create_x11(dev, ctx->vo->x11->window,
&p->vdp_target);
@@ -141,13 +144,13 @@ static int create_vdpau_objects(struct MPGLContext *ctx)
return 0;
}
-static void destroy_vdpau_surface(struct MPGLContext *ctx,
+static void destroy_vdpau_surface(struct ra_ctx *ctx,
struct surface *surface)
{
struct priv *p = ctx->priv;
struct vdp_functions *vdp = &p->vdp->vdp;
VdpStatus vdp_st;
- GL *gl = ctx->gl;
+ GL *gl = &p->gl;
if (surface->mapped)
gl->VDPAUUnmapSurfacesNV(1, &surface->registered);
@@ -168,14 +171,14 @@ static void destroy_vdpau_surface(struct MPGLContext *ctx,
};
}
-static int recreate_vdpau_surface(struct MPGLContext *ctx,
- struct surface *surface)
+static bool recreate_vdpau_surface(struct ra_ctx *ctx,
+ struct surface *surface)
{
struct priv *p = ctx->priv;
VdpDevice dev = p->vdp->vdp_device;
struct vdp_functions *vdp = &p->vdp->vdp;
VdpStatus vdp_st;
- GL *gl = ctx->gl;
+ GL *gl = &p->gl;
destroy_vdpau_surface(ctx, surface);
@@ -219,16 +222,37 @@ static int recreate_vdpau_surface(struct MPGLContext *ctx,
gl->VDPAUUnmapSurfacesNV(1, &surface->registered);
surface->mapped = false;
- return 0;
+ return true;
error:
destroy_vdpau_surface(ctx, surface);
- return -1;
+ return false;
+}
+
+static void vdpau_swap_buffers(struct ra_ctx *ctx)
+{
+ struct priv *p = ctx->priv;
+ struct vdp_functions *vdp = &p->vdp->vdp;
+ VdpStatus vdp_st;
+
+ // This is the *next* surface we will be rendering to. By delaying the
+ // block_until_idle, we're essentially allowing p->num_surfaces - 1
+ // in-flight surfaces, plus the one currently visible surface.
+ struct surface *surf = &p->surfaces[p->idx_surfaces];
+ if (surf->surface == VDP_INVALID_HANDLE)
+ return;
+
+ VdpTime prev_vsync_time;
+ vdp_st = vdp->presentation_queue_block_until_surface_idle(p->vdp_queue,
+ surf->surface,
+ &prev_vsync_time);
+ CHECK_VDP_WARNING(ctx, "waiting for surface failed");
}
-static void glx_uninit(MPGLContext *ctx)
+static void vdpau_uninit(struct ra_ctx *ctx)
{
struct priv *p = ctx->priv;
+ ra_gl_ctx_uninit(ctx);
if (p->vdp) {
struct vdp_functions *vdp = &p->vdp->vdp;
@@ -259,10 +283,12 @@ static void glx_uninit(MPGLContext *ctx)
vo_x11_uninit(ctx->vo);
}
-static int glx_init(struct MPGLContext *ctx, int flags)
+static const struct ra_swapchain_fns vdpau_swapchain;
+
+static bool vdpau_init(struct ra_ctx *ctx)
{
struct vo *vo = ctx->vo;
- struct priv *p = ctx->priv;
+ struct priv *p = ctx->priv = talloc_zero(ctx, struct priv);
p->vdp_queue = VDP_INVALID_HANDLE;
p->vdp_target = VDP_INVALID_HANDLE;
@@ -280,110 +306,112 @@ static int glx_init(struct MPGLContext *ctx, int flags)
if (!vo_x11_create_vo_window(vo, NULL, "vdpauglx"))
goto uninit;
- if (!create_context_x11(ctx, flags))
+ if (!create_context_x11(ctx))
goto uninit;
- if (!(ctx->gl->mpgl_caps & MPGL_CAP_VDPAU))
+ if (!(p->gl.mpgl_caps & MPGL_CAP_VDPAU))
goto uninit;
if (create_vdpau_objects(ctx) < 0)
goto uninit;
- p->num_surfaces = NUM_SURFACES;
+ p->num_surfaces = ctx->opts.swapchain_depth + 1; // +1 for the visible image
+ p->surfaces = talloc_zero_array(p, struct surface, p->num_surfaces);
for (int n = 0; n < p->num_surfaces; n++)
p->surfaces[n].surface = VDP_INVALID_HANDLE;
- ctx->flip_v = true;
+ struct ra_gl_ctx_params params = {
+ .swap_buffers = vdpau_swap_buffers,
+ .external_swapchain = &vdpau_swapchain,
+ .flipped = true,
+ };
- return 0;
+ if (!ra_gl_ctx_init(ctx, &p->gl, params))
+ goto uninit;
+
+ return true;
uninit:
- glx_uninit(ctx);
- return -1;
+ vdpau_uninit(ctx);
+ return false;
}
-static int glx_reconfig(struct MPGLContext *ctx)
+static struct ra_tex *vdpau_start_frame(struct ra_swapchain *sw)
{
- vo_x11_config_vo_window(ctx->vo);
- return 0;
-}
+ struct priv *p = sw->ctx->priv;
+ struct vo *vo = sw->ctx->vo;
+ GL *gl = &p->gl;
+
+ struct surface *surf = &p->surfaces[p->idx_surfaces];
+ if (surf->w != vo->dwidth || surf->h != vo->dheight ||
+ surf->surface == VDP_INVALID_HANDLE)
+ {
+ if (!recreate_vdpau_surface(sw->ctx, surf))
+ return NULL;
+ }
-static int glx_control(struct MPGLContext *ctx, int *events, int request,
- void *arg)
-{
- return vo_x11_control(ctx->vo, events, request, arg);
+ assert(!surf->mapped);
+ gl->VDPAUMapSurfacesNV(1, &surf->registered);
+ surf->mapped = true;
+
+ ra_gl_ctx_resize(sw, surf->w, surf->h, surf->fbo);
+ return ra_gl_ctx_start_frame(sw);
}
-static void glx_start_frame(struct MPGLContext *ctx)
+static bool vdpau_submit_frame(struct ra_swapchain *sw,
+ const struct vo_frame *frame)
{
- struct priv *p = ctx->priv;
+ struct priv *p = sw->ctx->priv;
+ GL *gl = &p->gl;
struct vdp_functions *vdp = &p->vdp->vdp;
VdpStatus vdp_st;
- GL *gl = ctx->gl;
-
- struct surface *surface = &p->surfaces[p->current_surface];
-
- if (surface->surface != VDP_INVALID_HANDLE) {
- VdpTime prev_vsync_time;
- vdp_st = vdp->presentation_queue_block_until_surface_idle(p->vdp_queue,
- surface->surface,
- &prev_vsync_time);
- CHECK_VDP_WARNING(ctx, "waiting for surface failed");
- }
- if (surface->w != ctx->vo->dwidth || surface->h != ctx->vo->dheight)
- recreate_vdpau_surface(ctx, surface);
+ struct surface *surf = &p->surfaces[p->idx_surfaces];
+ assert(surf->surface != VDP_INVALID_HANDLE);
+ assert(surf->mapped);
+ gl->VDPAUUnmapSurfacesNV(1, &surf->registered);
+ surf->mapped = false;
+ vdp_st = vdp->presentation_queue_display(p->vdp_queue, surf->surface, 0, 0, 0);
+ CHECK_VDP_WARNING(sw->ctx, "trying to present vdp surface");
- ctx->main_fb = surface->fbo; // 0 if creating the surface failed
-
- if (surface->surface != VDP_INVALID_HANDLE) {
- gl->VDPAUMapSurfacesNV(1, &surface->registered);
- surface->mapped = true;
- }
+ p->idx_surfaces = (p->idx_surfaces + 1) % p->num_surfaces;
+ return ra_gl_ctx_submit_frame(sw, frame) && vdp_st == VDP_STATUS_OK;
}
-static void glx_swap_buffers(struct MPGLContext *ctx)
+static bool vdpau_reconfig(struct ra_ctx *ctx)
{
- struct priv *p = ctx->priv;
- struct vdp_functions *vdp = &p->vdp->vdp;
- VdpStatus vdp_st;
- GL *gl = ctx->gl;
-
- struct surface *surface = &p->surfaces[p->current_surface];
- if (surface->surface == VDP_INVALID_HANDLE)
- return; // surface alloc probably failed before
-
- if (surface->mapped)
- gl->VDPAUUnmapSurfacesNV(1, &surface->registered);
- surface->mapped = false;
-
- vdp_st = vdp->presentation_queue_display(p->vdp_queue, surface->surface,
- 0, 0, 0);
- CHECK_VDP_WARNING(ctx, "trying to present vdp surface");
+ vo_x11_config_vo_window(ctx->vo);
+ return true;
+}
- p->current_surface = (p->current_surface + 1) % p->num_surfaces;
+static int vdpau_control(struct ra_ctx *ctx, int *events, int request, void *arg)
+{
+ return vo_x11_control(ctx->vo, events, request, arg);
}
-static void glx_wakeup(struct MPGLContext *ctx)
+static void vdpau_wakeup(struct ra_ctx *ctx)
{
vo_x11_wakeup(ctx->vo);
}
-static void glx_wait_events(struct MPGLContext *ctx, int64_t until_time_us)
+static void vdpau_wait_events(struct ra_ctx *ctx, int64_t until_time_us)
{
vo_x11_wait_events(ctx->vo, until_time_us);
}
-const struct mpgl_driver mpgl_driver_vdpauglx = {
+static const struct ra_swapchain_fns vdpau_swapchain = {
+ .start_frame = vdpau_start_frame,
+ .submit_frame = vdpau_submit_frame,
+};
+
+const struct ra_ctx_fns ra_ctx_vdpauglx = {
+ .type = "opengl",
.name = "vdpauglx",
- .priv_size = sizeof(struct priv),
- .init = glx_init,
- .reconfig = glx_reconfig,
- .start_frame = glx_start_frame,
- .swap_buffers = glx_swap_buffers,
- .control = glx_control,
- .wakeup = glx_wakeup,
- .wait_events = glx_wait_events,
- .uninit = glx_uninit,
+ .reconfig = vdpau_reconfig,
+ .control = vdpau_control,
+ .wakeup = vdpau_wakeup,
+ .wait_events = vdpau_wait_events,
+ .init = vdpau_init,
+ .uninit = vdpau_uninit,
};
diff --git a/video/out/opengl/context_wayland.c b/video/out/opengl/context_wayland.c
index 87e98cd64f..6ddc550306 100644
--- a/video/out/opengl/context_wayland.c
+++ b/video/out/opengl/context_wayland.c
@@ -19,6 +19,7 @@
#include "video/out/wayland_common.h"
#include "context.h"
#include "egl_helpers.h"
+#include "utils.h"
static void egl_resize(struct vo_wayland_state *wl)
{
@@ -63,30 +64,42 @@ static void egl_resize(struct vo_wayland_state *wl)
wl->vo->want_redraw = true;
}
-static int egl_create_context(struct vo_wayland_state *wl, MPGLContext *ctx,
- int flags)
+static void waylandgl_swap_buffers(struct ra_ctx *ctx)
{
- GL *gl = ctx->gl;
+ struct vo_wayland_state *wl = ctx->vo->wayland;
+ vo_wayland_wait_events(ctx->vo, 0);
+ eglSwapBuffers(wl->egl_context.egl.dpy, wl->egl_context.egl_surface);
+}
+
+static bool egl_create_context(struct ra_ctx *ctx, struct vo_wayland_state *wl)
+{
+ GL *gl = ctx->priv = talloc_zero(ctx, GL);
if (!(wl->egl_context.egl.dpy = eglGetDisplay(wl->display.display)))
- return -1;
+ return false;
if (eglInitialize(wl->egl_context.egl.dpy, NULL, NULL) != EGL_TRUE)
- return -1;
+ return false;
- if (!mpegl_create_context(wl->egl_context.egl.dpy, wl->log, flags,
+ if (!mpegl_create_context(ctx, wl->egl_context.egl.dpy,
&wl->egl_context.egl.ctx,
&wl->egl_context.egl.conf))
- return -1;
+ return false;
eglMakeCurrent(wl->egl_context.egl.dpy, NULL, NULL, wl->egl_context.egl.ctx);
mpegl_load_functions(gl, wl->log);
- ctx->native_display_type = "wl";
- ctx->native_display = wl->display.display;
+ struct ra_gl_ctx_params params = {
+ .swap_buffers = waylandgl_swap_buffers,
+ .native_display_type = "wl",
+ .native_display = wl->display.display,
+ };
+
+ if (!ra_gl_ctx_init(ctx, gl, params))
+ return false;
- return 0;
+ return true;
}
static void egl_create_window(struct vo_wayland_state *wl)
@@ -122,23 +135,25 @@ static void egl_create_window(struct vo_wayland_state *wl)
eglSwapInterval(wl->egl_context.egl.dpy, 0);
}
-static int waylandgl_reconfig(struct MPGLContext *ctx)
+static bool waylandgl_reconfig(struct ra_ctx *ctx)
{
struct vo_wayland_state * wl = ctx->vo->wayland;
if (!vo_wayland_config(ctx->vo))
- return -1;
+ return false;
if (!wl->egl_context.egl_window)
egl_create_window(wl);
- return 0;
+ return true;
}
-static void waylandgl_uninit(MPGLContext *ctx)
+static void waylandgl_uninit(struct ra_ctx *ctx)
{
struct vo_wayland_state *wl = ctx->vo->wayland;
+ ra_gl_ctx_uninit(ctx);
+
if (wl->egl_context.egl.ctx) {
eglReleaseThread();
if (wl->egl_context.egl_window)
@@ -153,52 +168,45 @@ static void waylandgl_uninit(MPGLContext *ctx)
vo_wayland_uninit(ctx->vo);
}
-static void waylandgl_swap_buffers(MPGLContext *ctx)
-{
- struct vo_wayland_state *wl = ctx->vo->wayland;
-
- vo_wayland_wait_events(ctx->vo, 0);
-
- eglSwapBuffers(wl->egl_context.egl.dpy, wl->egl_context.egl_surface);
-}
-
-static int waylandgl_control(MPGLContext *ctx, int *events, int request,
+static int waylandgl_control(struct ra_ctx *ctx, int *events, int request,
void *data)
{
struct vo_wayland_state *wl = ctx->vo->wayland;
int r = vo_wayland_control(ctx->vo, events, request, data);
- if (*events & VO_EVENT_RESIZE)
+ if (*events & VO_EVENT_RESIZE) {
egl_resize(wl);
+ ra_gl_ctx_resize(ctx->swapchain, wl->vo->dwidth, wl->vo->dheight, 0);
+ }
return r;
}
-static void wayland_wakeup(struct MPGLContext *ctx)
+static void wayland_wakeup(struct ra_ctx *ctx)
{
vo_wayland_wakeup(ctx->vo);
}
-static void wayland_wait_events(struct MPGLContext *ctx, int64_t until_time_us)
+static void wayland_wait_events(struct ra_ctx *ctx, int64_t until_time_us)
{
vo_wayland_wait_events(ctx->vo, until_time_us);
}
-static int waylandgl_init(struct MPGLContext *ctx, int flags)
+static bool waylandgl_init(struct ra_ctx *ctx)
{
if (!vo_wayland_init(ctx->vo))
- return -1;
+ return false;
- return egl_create_context(ctx->vo->wayland, ctx, flags);
+ return egl_create_context(ctx, ctx->vo->wayland);
}
-const struct mpgl_driver mpgl_driver_wayland = {
+const struct ra_ctx_fns ra_ctx_wayland_egl = {
+ .type = "opengl",
.name = "wayland",
- .init = waylandgl_init,
.reconfig = waylandgl_reconfig,
- .swap_buffers = waylandgl_swap_buffers,
.control = waylandgl_control,
.wakeup = wayland_wakeup,
.wait_events = wayland_wait_events,
+ .init = waylandgl_init,
.uninit = waylandgl_uninit,
};
diff --git a/video/out/opengl/context_x11egl.c b/video/out/opengl/context_x11egl.c
index 2b68007a33..7ab4fe0579 100644
--- a/video/out/opengl/context_x11egl.c
+++ b/video/out/opengl/context_x11egl.c
@@ -32,14 +32,17 @@
#include "egl_helpers.h"
struct priv {
+ GL gl;
EGLDisplay egl_display;
EGLContext egl_context;
EGLSurface egl_surface;
};
-static void mpegl_uninit(MPGLContext *ctx)
+static void mpegl_uninit(struct ra_ctx *ctx)
{
struct priv *p = ctx->priv;
+ ra_gl_ctx_uninit(ctx);
+
if (p->egl_context) {
eglMakeCurrent(p->egl_display, EGL_NO_SURFACE, EGL_NO_SURFACE,
EGL_NO_CONTEXT);
@@ -51,7 +54,7 @@ static void mpegl_uninit(MPGLContext *ctx)
static int pick_xrgba_config(void *user_data, EGLConfig *configs, int num_configs)
{
- struct MPGLContext *ctx = user_data;
+ struct ra_ctx *ctx = user_data;
struct priv *p = ctx->priv;
struct vo *vo = ctx->vo;
@@ -72,40 +75,44 @@ static int pick_xrgba_config(void *user_data, EGLConfig *configs, int num_config
return 0;
}
-static int mpegl_init(struct MPGLContext *ctx, int flags)
+static void mpegl_swap_buffers(struct ra_ctx *ctx)
{
struct priv *p = ctx->priv;
+ eglSwapBuffers(p->egl_display, p->egl_surface);
+}
+
+static bool mpegl_init(struct ra_ctx *ctx)
+{
+ struct priv *p = ctx->priv = talloc_zero(ctx, struct priv);
struct vo *vo = ctx->vo;
- int msgl = vo->probing ? MSGL_V : MSGL_FATAL;
+ int msgl = ctx->opts.probing ? MSGL_V : MSGL_FATAL;
if (!vo_x11_init(vo))
goto uninit;
p->egl_display = eglGetDisplay(vo->x11->display);
if (!eglInitialize(p->egl_display, NULL, NULL)) {
- mp_msg(vo->log, msgl, "Could not initialize EGL.\n");
+ MP_MSG(ctx, msgl, "Could not initialize EGL.\n");
goto uninit;
}
- struct mpegl_opts opts = {
- .vo_flags = flags,
+ struct mpegl_cb cb = {
.user_data = ctx,
- .refine_config = (flags & VOFLAG_ALPHA) ? pick_xrgba_config : NULL,
+ .refine_config = ctx->opts.want_alpha ? pick_xrgba_config : NULL,
};
EGLConfig config;
- if (!mpegl_create_context_opts(p->egl_display, vo->log, &opts,
- &p->egl_context, &config))
+ if (!mpegl_create_context_cb(ctx, p->egl_display, cb, &p->egl_context, &config))
goto uninit;
int vID, n;
eglGetConfigAttrib(p->egl_display, config, EGL_NATIVE_VISUAL_ID, &vID);
- MP_VERBOSE(vo, "chose visual 0x%x\n", vID);
+ MP_VERBOSE(ctx, "chose visual 0x%x\n", vID);
XVisualInfo template = {.visualid = vID};
XVisualInfo *vi = XGetVisualInfo(vo->x11->display, VisualIDMask, &template, &n);
if (!vi) {
- MP_FATAL(vo, "Getting X visual failed!\n");
+ MP_FATAL(ctx, "Getting X visual failed!\n");
goto uninit;
}
@@ -120,64 +127,73 @@ static int mpegl_init(struct MPGLContext *ctx, int flags)
(EGLNativeWindowType)vo->x11->window, NULL);
if (p->egl_surface == EGL_NO_SURFACE) {
- MP_FATAL(ctx->vo, "Could not create EGL surface!\n");
+ MP_FATAL(ctx, "Could not create EGL surface!\n");
goto uninit;
}
if (!eglMakeCurrent(p->egl_display, p->egl_surface, p->egl_surface,
p->egl_context))
{
- MP_FATAL(ctx->vo, "Could not make context current!\n");
+ MP_FATAL(ctx, "Could not make context current!\n");
goto uninit;
}
- mpegl_load_functions(ctx->gl, vo->log);
+ mpegl_load_functions(&p->gl, ctx->log);
- ctx->native_display_type = "x11";
- ctx->native_display = vo->x11->display;
- return 0;
+ struct ra_gl_ctx_params params = {
+ .swap_buffers = mpegl_swap_buffers,
+ .native_display_type = "x11",
+ .native_display = vo->x11->display,
+ };
+
+ if (!ra_gl_ctx_init(ctx, &p->gl, params))
+ goto uninit;
+
+ return true;
uninit:
mpegl_uninit(ctx);
- return -1;
+ return false;
}
-static int mpegl_reconfig(struct MPGLContext *ctx)
+static void resize(struct ra_ctx *ctx)
{
- vo_x11_config_vo_window(ctx->vo);
- return 0;
+ ra_gl_ctx_resize(ctx->swapchain, ctx->vo->dwidth, ctx->vo->dheight, 0);
}
-static int mpegl_control(struct MPGLContext *ctx, int *events, int request,
- void *arg)
+static bool mpegl_reconfig(struct ra_ctx *ctx)
{
- return vo_x11_control(ctx->vo, events, request, arg);
+ vo_x11_config_vo_window(ctx->vo);
+ resize(ctx);
+ return true;
}
-static void mpegl_swap_buffers(MPGLContext *ctx)
+static int mpegl_control(struct ra_ctx *ctx, int *events, int request,
+ void *arg)
{
- struct priv *p = ctx->priv;
- eglSwapBuffers(p->egl_display, p->egl_surface);
+ int ret = vo_x11_control(ctx->vo, events, request, arg);
+ if (*events & VO_EVENT_RESIZE)
+ resize(ctx);
+ return ret;
}
-static void mpegl_wakeup(struct MPGLContext *ctx)
+static void mpegl_wakeup(struct ra_ctx *ctx)
{
vo_x11_wakeup(ctx->vo);
}
-static void mpegl_wait_events(struct MPGLContext *ctx, int64_t until_time_us)
+static void mpegl_wait_events(struct ra_ctx *ctx, int64_t until_time_us)
{
vo_x11_wait_events(ctx->vo, until_time_us);
}
-const struct mpgl_driver mpgl_driver_x11egl = {
+const struct ra_ctx_fns ra_ctx_x11_egl = {
+ .type = "opengl",
.name = "x11egl",
- .priv_size = sizeof(struct priv),
- .init = mpegl_init,
.reconfig = mpegl_reconfig,
- .swap_buffers = mpegl_swap_buffers,
.control = mpegl_control,
.wakeup = mpegl_wakeup,
.wait_events = mpegl_wait_events,
+ .init = mpegl_init,
.uninit = mpegl_uninit,
};
diff --git a/video/out/opengl/egl_helpers.c b/video/out/opengl/egl_helpers.c
index ac152df06a..0033bf1e33 100644
--- a/video/out/opengl/egl_helpers.c
+++ b/video/out/opengl/egl_helpers.c
@@ -25,6 +25,7 @@
#include "egl_helpers.h"
#include "common.h"
+#include "utils.h"
#include "context.h"
#if HAVE_EGL_ANGLE
@@ -43,41 +44,49 @@
#define EGL_OPENGL_ES3_BIT 0x00000040
#endif
-// es_version = 0 (desktop), 2/3 (ES major version)
-static bool create_context(EGLDisplay display, struct mp_log *log, bool probing,
- int es_version, struct mpegl_opts *opts,
+// es_version: 0 (core), 2 or 3
+static bool create_context(struct ra_ctx *ctx, EGLDisplay display,
+ int es_version, struct mpegl_cb cb,
EGLContext *out_context, EGLConfig *out_config)
{
- int msgl = probing ? MSGL_V : MSGL_FATAL;
-
- EGLenum api = EGL_OPENGL_API;
- EGLint rend = EGL_OPENGL_BIT;
- const char *name = "Desktop OpenGL";
- if (es_version == 2) {
+ int msgl = ctx->opts.probing ? MSGL_V : MSGL_FATAL;
+
+ EGLenum api;
+ EGLint rend;
+ const char *name;
+
+ switch (es_version) {
+ case 0:
+ api = EGL_OPENGL_API;
+ rend = EGL_OPENGL_BIT;
+ name = "Desktop OpenGL";
+ break;
+ case 2:
api = EGL_OPENGL_ES_API;
rend = EGL_OPENGL_ES2_BIT;
- name = "GLES 2.0";
- }
- if (es_version == 3) {
+ name = "GLES 2.x";
+ break;
+ case 3:
api = EGL_OPENGL_ES_API;
rend = EGL_OPENGL_ES3_BIT;
name = "GLES 3.x";
+ break;
+ default: abort();
}
- mp_msg(log, MSGL_V, "Trying to create %s context.\n", name);
+ MP_VERBOSE(ctx, "Trying to create %s context.\n", name);
if (!eglBindAPI(api)) {
- mp_msg(log, MSGL_V, "Could not bind API!\n");
+ MP_VERBOSE(ctx, "Could not bind API!\n");
return false;
}
-
EGLint attributes[] = {
EGL_SURFACE_TYPE, EGL_WINDOW_BIT,
EGL_RED_SIZE, 1,
EGL_GREEN_SIZE, 1,
EGL_BLUE_SIZE, 1,
- EGL_ALPHA_SIZE, (opts->vo_flags & VOFLAG_ALPHA ) ? 1 : 0,
+ EGL_ALPHA_SIZE, ctx->opts.want_alpha ? 1 : 0,
EGL_RENDERABLE_TYPE, rend,
EGL_NONE
};
@@ -92,29 +101,34 @@ static bool create_context(EGLDisplay display, struct mp_log *log, bool probing,
if (!num_configs) {
talloc_free(configs);
- mp_msg(log, msgl, "Could not choose EGLConfig!\n");
+ MP_MSG(ctx, msgl, "Could not choose EGLConfig!\n");
return false;
}
int chosen = 0;
- if (opts->refine_config)
- chosen = opts->refine_config(opts->user_data, configs, num_configs);
+ if (cb.refine_config)
+ chosen = cb.refine_config(cb.user_data, configs, num_configs);
EGLConfig config = configs[chosen];
talloc_free(configs);
- EGLContext *ctx = NULL;
+ EGLContext *egl_ctx = NULL;
if (es_version) {
+ if (!ra_gl_ctx_test_version(ctx, MPGL_VER(es_version, 0), true))
+ return false;
+
EGLint attrs[] = {
EGL_CONTEXT_CLIENT_VERSION, es_version,
EGL_NONE
};
- ctx = eglCreateContext(display, config, EGL_NO_CONTEXT, attrs);
+ egl_ctx = eglCreateContext(display, config, EGL_NO_CONTEXT, attrs);
} else {
for (int n = 0; mpgl_preferred_gl_versions[n]; n++) {
int ver = mpgl_preferred_gl_versions[n];
+ if (!ra_gl_ctx_test_version(ctx, ver, false))
+ continue;
EGLint attrs[] = {
EGL_CONTEXT_MAJOR_VERSION, MPGL_VER_GET_MAJOR(ver),
@@ -124,25 +138,25 @@ static bool create_context(EGLDisplay display, struct mp_log *log, bool probing,
EGL_NONE
};
- ctx = eglCreateContext(display, config, EGL_NO_CONTEXT, attrs);
- if (ctx)
+ egl_ctx = eglCreateContext(display, config, EGL_NO_CONTEXT, attrs);
+ if (egl_ctx)
break;
}
- if (!ctx) {
+ if (!egl_ctx && ra_gl_ctx_test_version(ctx, 140, false)) {
// Fallback for EGL 1.4 without EGL_KHR_create_context.
EGLint attrs[] = { EGL_NONE };
- ctx = eglCreateContext(display, config, EGL_NO_CONTEXT, attrs);
+ egl_ctx = eglCreateContext(display, config, EGL_NO_CONTEXT, attrs);
}
}
- if (!ctx) {
- mp_msg(log, msgl, "Could not create EGL context!\n");
+ if (!egl_ctx) {
+ MP_MSG(ctx, msgl, "Could not create EGL context!\n");
return false;
}
- *out_context = ctx;
+ *out_context = egl_ctx;
*out_config = config;
return true;
}
@@ -152,56 +166,36 @@ static bool create_context(EGLDisplay display, struct mp_log *log, bool probing,
// Create a context and return it and the config it was created with. If it
// returns false, the out_* pointers are set to NULL.
// vo_flags is a combination of VOFLAG_* values.
-bool mpegl_create_context(EGLDisplay display, struct mp_log *log, int vo_flags,
+bool mpegl_create_context(struct ra_ctx *ctx, EGLDisplay display,
EGLContext *out_context, EGLConfig *out_config)
{
- return mpegl_create_context_opts(display, log,
- &(struct mpegl_opts){.vo_flags = vo_flags}, out_context, out_config);
+ return mpegl_create_context_cb(ctx, display, (struct mpegl_cb){0},
+ out_context, out_config);
}
// Create a context and return it and the config it was created with. If it
// returns false, the out_* pointers are set to NULL.
-bool mpegl_create_context_opts(EGLDisplay display, struct mp_log *log,
- struct mpegl_opts *opts,
- EGLContext *out_context, EGLConfig *out_config)
+bool mpegl_create_context_cb(struct ra_ctx *ctx, EGLDisplay display,
+ struct mpegl_cb cb, EGLContext *out_context,
+ EGLConfig *out_config)
{
- assert(opts);
-
*out_context = NULL;
*out_config = NULL;
const char *version = eglQueryString(display, EGL_VERSION);
const char *vendor = eglQueryString(display, EGL_VENDOR);
const char *apis = eglQueryString(display, EGL_CLIENT_APIS);
- mp_verbose(log, "EGL_VERSION=%s\nEGL_VENDOR=%s\nEGL_CLIENT_APIS=%s\n",
+ MP_VERBOSE(ctx, "EGL_VERSION=%s\nEGL_VENDOR=%s\nEGL_CLIENT_APIS=%s\n",
STR_OR_ERR(version), STR_OR_ERR(vendor), STR_OR_ERR(apis));
- bool probing = opts->vo_flags & VOFLAG_PROBING;
- int msgl = probing ? MSGL_V : MSGL_FATAL;
- bool try_gles = !(opts->vo_flags & VOFLAG_NO_GLES);
-
- if (!(opts->vo_flags & VOFLAG_GLES)) {
- // Desktop OpenGL
- if (create_context(display, log, try_gles | probing, 0, opts,
- out_context, out_config))
- return true;
- }
-
- if (try_gles && !(opts->vo_flags & VOFLAG_GLES2)) {
- // ES 3.x
- if (create_context(display, log, true, 3, opts,
- out_context, out_config))
- return true;
- }
-
- if (try_gles) {
- // ES 2.0
- if (create_context(display, log, probing, 2, opts,
- out_context, out_config))
+ int es[] = {0, 3, 2}; // preference order
+ for (int i = 0; i < MP_ARRAY_SIZE(es); i++) {
+ if (create_context(ctx, display, es[i], cb, out_context, out_config))
return true;
}
- mp_msg(log, msgl, "Could not create a GL context.\n");
+ int msgl = ctx->opts.probing ? MSGL_V : MSGL_ERR;
+ MP_MSG(ctx, msgl, "Could not create a GL context.\n");
return false;
}
diff --git a/video/out/opengl/egl_helpers.h b/video/out/opengl/egl_helpers.h
index 05f9dccb70..eaaf9d7a48 100644
--- a/video/out/opengl/egl_helpers.h
+++ b/video/out/opengl/egl_helpers.h
@@ -6,26 +6,23 @@
#include <EGL/egl.h>
#include <EGL/eglext.h>
+#include "video/out/gpu/context.h"
+
struct mp_log;
-bool mpegl_create_context(EGLDisplay display, struct mp_log *log, int vo_flags,
+bool mpegl_create_context(struct ra_ctx *ctx, EGLDisplay display,
EGLContext *out_context, EGLConfig *out_config);
-struct mpegl_opts {
- // combination of VOFLAG_* values.
- int vo_flags;
-
- // for callbacks
- void *user_data;
-
+struct mpegl_cb {
// if set, pick the desired config from the given list and return its index
// defaults to 0 (they are sorted by eglChooseConfig)
int (*refine_config)(void *user_data, EGLConfig *configs, int num_configs);
+ void *user_data;
};
-bool mpegl_create_context_opts(EGLDisplay display, struct mp_log *log,
- struct mpegl_opts *opts,
- EGLContext *out_context, EGLConfig *out_config);
+bool mpegl_create_context_cb(struct ra_ctx *ctx, EGLDisplay display,
+ struct mpegl_cb cb, EGLContext *out_context,
+ EGLConfig *out_config);
struct GL;
void mpegl_load_functions(struct GL *gl, struct mp_log *log);
diff --git a/video/out/opengl/formats.h b/video/out/opengl/formats.h
index 3da6ede82a..f727a3b6ef 100644
--- a/video/out/opengl/formats.h
+++ b/video/out/opengl/formats.h
@@ -2,7 +2,6 @@
#define MPGL_FORMATS_H_
#include "common.h"
-#include "ra.h"
struct gl_format {
const char *name; // symbolic name for user interaction/debugging
diff --git a/video/out/opengl/gl_utils.c b/video/out/opengl/gl_utils.c
deleted file mode 100644
index bce2dabe5d..0000000000
--- a/video/out/opengl/gl_utils.c
+++ /dev/null
@@ -1,291 +0,0 @@
-/*
- * This file is part of mpv.
- * Parts based on MPlayer code by Reimar Döffinger.
- *
- * mpv is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2.1 of the License, or (at your option) any later version.
- *
- * mpv is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with mpv. If not, see <http://www.gnu.org/licenses/>.
- */
-
-#include <stddef.h>
-#include <stdint.h>
-#include <stdlib.h>
-#include <string.h>
-#include <stdarg.h>
-#include <assert.h>
-
-#include <libavutil/sha.h>
-#include <libavutil/intreadwrite.h>
-#include <libavutil/mem.h>
-
-#include "osdep/io.h"
-
-#include "common/common.h"
-#include "options/path.h"
-#include "stream/stream.h"
-#include "formats.h"
-#include "ra_gl.h"
-#include "gl_utils.h"
-
-// GLU has this as gluErrorString (we don't use GLU, as it is legacy-OpenGL)
-static const char *gl_error_to_string(GLenum error)
-{
- switch (error) {
- case GL_INVALID_ENUM: return "INVALID_ENUM";
- case GL_INVALID_VALUE: return "INVALID_VALUE";
- case GL_INVALID_OPERATION: return "INVALID_OPERATION";
- case GL_INVALID_FRAMEBUFFER_OPERATION: return "INVALID_FRAMEBUFFER_OPERATION";
- case GL_OUT_OF_MEMORY: return "OUT_OF_MEMORY";
- default: return "unknown";
- }
-}
-
-void gl_check_error(GL *gl, struct mp_log *log, const char *info)
-{
- for (;;) {
- GLenum error = gl->GetError();
- if (error == GL_NO_ERROR)
- break;
- mp_msg(log, MSGL_ERR, "%s: OpenGL error %s.\n", info,
- gl_error_to_string(error));
- }
-}
-
-static int get_alignment(int stride)
-{
- if (stride % 8 == 0)
- return 8;
- if (stride % 4 == 0)
- return 4;
- if (stride % 2 == 0)
- return 2;
- return 1;
-}
-
-// upload a texture, handling things like stride and slices
-// target: texture target, usually GL_TEXTURE_2D
-// format, type: texture parameters
-// dataptr, stride: image data
-// x, y, width, height: part of the image to upload
-void gl_upload_tex(GL *gl, GLenum target, GLenum format, GLenum type,
- const void *dataptr, int stride,
- int x, int y, int w, int h)
-{
- int bpp = gl_bytes_per_pixel(format, type);
- const uint8_t *data = dataptr;
- int y_max = y + h;
- if (w <= 0 || h <= 0 || !bpp)
- return;
- if (stride < 0) {
- data += (h - 1) * stride;
- stride = -stride;
- }
- gl->PixelStorei(GL_UNPACK_ALIGNMENT, get_alignment(stride));
- int slice = h;
- if (gl->mpgl_caps & MPGL_CAP_ROW_LENGTH) {
- // this is not always correct, but should work for MPlayer
- gl->PixelStorei(GL_UNPACK_ROW_LENGTH, stride / bpp);
- } else {
- if (stride != bpp * w)
- slice = 1; // very inefficient, but at least it works
- }
- for (; y + slice <= y_max; y += slice) {
- gl->TexSubImage2D(target, 0, x, y, w, slice, format, type, data);
- data += stride * slice;
- }
- if (y < y_max)
- gl->TexSubImage2D(target, 0, x, y, w, y_max - y, format, type, data);
- if (gl->mpgl_caps & MPGL_CAP_ROW_LENGTH)
- gl->PixelStorei(GL_UNPACK_ROW_LENGTH, 0);
- gl->PixelStorei(GL_UNPACK_ALIGNMENT, 4);
-}
-
-mp_image_t *gl_read_fbo_contents(GL *gl, int fbo, int w, int h)
-{
- if (gl->es)
- return NULL; // ES can't read from front buffer
- mp_image_t *image = mp_image_alloc(IMGFMT_RGB24, w, h);
- if (!image)
- return NULL;
- gl->BindFramebuffer(GL_FRAMEBUFFER, fbo);
- GLenum obj = fbo ? GL_COLOR_ATTACHMENT0 : GL_FRONT;
- gl->PixelStorei(GL_PACK_ALIGNMENT, 1);
- gl->ReadBuffer(obj);
- //flip image while reading (and also avoid stride-related trouble)
- for (int y = 0; y < h; y++) {
- gl->ReadPixels(0, h - y - 1, w, 1, GL_RGB, GL_UNSIGNED_BYTE,
- image->planes[0] + y * image->stride[0]);
- }
- gl->PixelStorei(GL_PACK_ALIGNMENT, 4);
- gl->BindFramebuffer(GL_FRAMEBUFFER, 0);
- return image;
-}
-
-static void gl_vao_enable_attribs(struct gl_vao *vao)
-{
- GL *gl = vao->gl;
-
- for (int n = 0; n < vao->num_entries; n++) {
- const struct ra_renderpass_input *e = &vao->entries[n];
- GLenum type = 0;
- bool normalized = false;
- switch (e->type) {
- case RA_VARTYPE_INT:
- type = GL_INT;
- break;
- case RA_VARTYPE_FLOAT:
- type = GL_FLOAT;
- break;
- case RA_VARTYPE_BYTE_UNORM:
- type = GL_UNSIGNED_BYTE;
- normalized = true;
- break;
- default:
- abort();
- }
- assert(e->dim_m == 1);
-
- gl->EnableVertexAttribArray(n);
- gl->VertexAttribPointer(n, e->dim_v, type, normalized,
- vao->stride, (void *)(intptr_t)e->offset);
- }
-}
-
-void gl_vao_init(struct gl_vao *vao, GL *gl, int stride,
- const struct ra_renderpass_input *entries,
- int num_entries)
-{
- assert(!vao->vao);
- assert(!vao->buffer);
-
- *vao = (struct gl_vao){
- .gl = gl,
- .stride = stride,
- .entries = entries,
- .num_entries = num_entries,
- };
-
- gl->GenBuffers(1, &vao->buffer);
-
- if (gl->BindVertexArray) {
- gl->BindBuffer(GL_ARRAY_BUFFER, vao->buffer);
-
- gl->GenVertexArrays(1, &vao->vao);
- gl->BindVertexArray(vao->vao);
- gl_vao_enable_attribs(vao);
- gl->BindVertexArray(0);
-
- gl->BindBuffer(GL_ARRAY_BUFFER, 0);
- }
-}
-
-void gl_vao_uninit(struct gl_vao *vao)
-{
- GL *gl = vao->gl;
- if (!gl)
- return;
-
- if (gl->DeleteVertexArrays)
- gl->DeleteVertexArrays(1, &vao->vao);
- gl->DeleteBuffers(1, &vao->buffer);
-
- *vao = (struct gl_vao){0};
-}
-
-static void gl_vao_bind(struct gl_vao *vao)
-{
- GL *gl = vao->gl;
-
- if (gl->BindVertexArray) {
- gl->BindVertexArray(vao->vao);
- } else {
- gl->BindBuffer(GL_ARRAY_BUFFER, vao->buffer);
- gl_vao_enable_attribs(vao);
- gl->BindBuffer(GL_ARRAY_BUFFER, 0);
- }
-}
-
-static void gl_vao_unbind(struct gl_vao *vao)
-{
- GL *gl = vao->gl;
-
- if (gl->BindVertexArray) {
- gl->BindVertexArray(0);
- } else {
- for (int n = 0; n < vao->num_entries; n++)
- gl->DisableVertexAttribArray(n);
- }
-}
-
-// Draw the vertex data (as described by the gl_vao_entry entries) in ptr
-// to the screen. num is the number of vertexes. prim is usually GL_TRIANGLES.
-// If ptr is NULL, then skip the upload, and use the data uploaded with the
-// previous call.
-void gl_vao_draw_data(struct gl_vao *vao, GLenum prim, void *ptr, size_t num)
-{
- GL *gl = vao->gl;
-
- if (ptr) {
- gl->BindBuffer(GL_ARRAY_BUFFER, vao->buffer);
- gl->BufferData(GL_ARRAY_BUFFER, num * vao->stride, ptr, GL_STREAM_DRAW);
- gl->BindBuffer(GL_ARRAY_BUFFER, 0);
- }
-
- gl_vao_bind(vao);
-
- gl->DrawArrays(prim, 0, num);
-
- gl_vao_unbind(vao);
-}
-
-static void GLAPIENTRY gl_debug_cb(GLenum source, GLenum type, GLuint id,
- GLenum severity, GLsizei length,
- const GLchar *message, const void *userParam)
-{
- // keep in mind that the debug callback can be asynchronous
- struct mp_log *log = (void *)userParam;
- int level = MSGL_ERR;
- switch (severity) {
- case GL_DEBUG_SEVERITY_NOTIFICATION:level = MSGL_V; break;
- case GL_DEBUG_SEVERITY_LOW: level = MSGL_INFO; break;
- case GL_DEBUG_SEVERITY_MEDIUM: level = MSGL_WARN; break;
- case GL_DEBUG_SEVERITY_HIGH: level = MSGL_ERR; break;
- }
- mp_msg(log, level, "GL: %s\n", message);
-}
-
-void gl_set_debug_logger(GL *gl, struct mp_log *log)
-{
- if (gl->DebugMessageCallback)
- gl->DebugMessageCallback(log ? gl_debug_cb : NULL, log);
-}
-
-int gl_get_fb_depth(GL *gl, int fbo)
-{
- if ((gl->es < 300 && !gl->version) || !(gl->mpgl_caps & MPGL_CAP_FB))
- return -1;
-
- gl->BindFramebuffer(GL_FRAMEBUFFER, fbo);
-
- GLenum obj = gl->version ? GL_BACK_LEFT : GL_BACK;
- if (fbo)
- obj = GL_COLOR_ATTACHMENT0;
-
- GLint depth_g = -1;
-
- gl->GetFramebufferAttachmentParameteriv(GL_FRAMEBUFFER, obj,
- GL_FRAMEBUFFER_ATTACHMENT_GREEN_SIZE, &depth_g);
-
- gl->BindFramebuffer(GL_FRAMEBUFFER, 0);
-
- return depth_g > 0 ? depth_g : -1;
-}
diff --git a/video/out/opengl/gl_utils.h b/video/out/opengl/gl_utils.h
deleted file mode 100644
index 306ee23f65..0000000000
--- a/video/out/opengl/gl_utils.h
+++ /dev/null
@@ -1,56 +0,0 @@
-/*
- * This file is part of mpv.
- * Parts based on MPlayer code by Reimar Döffinger.
- *
- * mpv is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2.1 of the License, or (at your option) any later version.
- *
- * mpv is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with mpv. If not, see <http://www.gnu.org/licenses/>.
- */
-
-#ifndef MP_GL_UTILS_
-#define MP_GL_UTILS_
-
-#include <math.h>
-
-#include "common.h"
-#include "ra.h"
-
-struct mp_log;
-
-void gl_check_error(GL *gl, struct mp_log *log, const char *info);
-
-void gl_upload_tex(GL *gl, GLenum target, GLenum format, GLenum type,
- const void *dataptr, int stride,
- int x, int y, int w, int h);
-
-mp_image_t *gl_read_fbo_contents(GL *gl, int fbo, int w, int h);
-
-struct gl_vao {
- GL *gl;
- GLuint vao; // the VAO object, or 0 if unsupported by driver
- GLuint buffer; // GL_ARRAY_BUFFER used for the data
- int stride; // size of each element (interleaved elements are assumed)
- const struct ra_renderpass_input *entries;
- int num_entries;
-};
-
-void gl_vao_init(struct gl_vao *vao, GL *gl, int stride,
- const struct ra_renderpass_input *entries,
- int num_entries);
-void gl_vao_uninit(struct gl_vao *vao);
-void gl_vao_draw_data(struct gl_vao *vao, GLenum prim, void *ptr, size_t num);
-
-void gl_set_debug_logger(GL *gl, struct mp_log *log);
-
-int gl_get_fb_depth(GL *gl, int fbo);
-
-#endif
diff --git a/video/out/opengl/hwdec_cuda.c b/video/out/opengl/hwdec_cuda.c
index d40bafee24..d9c4c199f1 100644
--- a/video/out/opengl/hwdec_cuda.c
+++ b/video/out/opengl/hwdec_cuda.c
@@ -32,11 +32,10 @@
#include <libavutil/hwcontext.h>
#include <libavutil/hwcontext_cuda.h>
+#include "video/out/gpu/hwdec.h"
#include "formats.h"
-#include "hwdec.h"
#include "options/m_config.h"
#include "ra_gl.h"
-#include "video.h"
struct priv_owner {
struct mp_hwdec_ctx hwctx;
diff --git a/video/out/opengl/hwdec_ios.m b/video/out/opengl/hwdec_ios.m
index 8e020ded63..71b205b583 100644
--- a/video/out/opengl/hwdec_ios.m
+++ b/video/out/opengl/hwdec_ios.m
@@ -27,10 +27,10 @@
#include "config.h"
+#include "video/out/gpu/hwdec.h"
#include "video/mp_image_pool.h"
#include "video/vt.h"
#include "ra_gl.h"
-#include "hwdec.h"
struct priv_owner {
struct mp_hwdec_ctx hwctx;
diff --git a/video/out/opengl/hwdec_osx.c b/video/out/opengl/hwdec_osx.c
index 348a5e19c5..cfd5f52e7b 100644
--- a/video/out/opengl/hwdec_osx.c
+++ b/video/out/opengl/hwdec_osx.c
@@ -29,9 +29,9 @@
#include "config.h"
#include "video/mp_image_pool.h"
+#include "video/out/gpu/hwdec.h"
#include "video/vt.h"
#include "ra_gl.h"
-#include "hwdec.h"
struct priv_owner {
struct mp_hwdec_ctx hwctx;
diff --git a/video/out/opengl/hwdec_rpi.c b/video/out/opengl/hwdec_rpi.c
index 6f39c3e330..ea8312a179 100644
--- a/video/out/opengl/hwdec_rpi.c
+++ b/video/out/opengl/hwdec_rpi.c
@@ -33,8 +33,8 @@
#include "common/common.h"
#include "common/msg.h"
#include "video/mp_image.h"
+#include "video/out/gpu/hwdec.h"
-#include "hwdec.h"
#include "common.h"
#include "ra_gl.h"
diff --git a/video/out/opengl/hwdec_vaegl.c b/video/out/opengl/hwdec_vaegl.c
index a0e3222cfc..6078222bd5 100644
--- a/video/out/opengl/hwdec_vaegl.c
+++ b/video/out/opengl/hwdec_vaegl.c
@@ -30,9 +30,9 @@
#include "config.h"
-#include "hwdec.h"
-#include "video/vaapi.h"
+#include "video/out/gpu/hwdec.h"
#include "video/mp_image_pool.h"
+#include "video/vaapi.h"
#include "common.h"
#include "ra_gl.h"
diff --git a/video/out/opengl/hwdec_vaglx.c b/video/out/opengl/hwdec_vaglx.c
index 8db15c4468..d5bc0b6ee7 100644
--- a/video/out/opengl/hwdec_vaglx.c
+++ b/video/out/opengl/hwdec_vaglx.c
@@ -25,10 +25,11 @@
#include <va/va_x11.h>
#include "video/out/x11_common.h"
-#include "ra_gl.h"
-#include "hwdec.h"
+#include "video/out/gpu/hwdec.h"
#include "video/vaapi.h"
+#include "ra_gl.h"
+
struct priv_owner {
struct mp_vaapi_ctx *ctx;
VADisplay *display;
diff --git a/video/out/opengl/hwdec_vdpau.c b/video/out/opengl/hwdec_vdpau.c
index d733650328..e0618e425e 100644
--- a/video/out/opengl/hwdec_vdpau.c
+++ b/video/out/opengl/hwdec_vdpau.c
@@ -20,7 +20,7 @@
#include <GL/glx.h>
-#include "hwdec.h"
+#include "video/out/gpu/hwdec.h"
#include "ra_gl.h"
#include "video/vdpau.h"
#include "video/vdpau_mixer.h"
diff --git a/video/out/opengl/ra_gl.c b/video/out/opengl/ra_gl.c
index 0d99877a9e..ccb8755ba6 100644
--- a/video/out/opengl/ra_gl.c
+++ b/video/out/opengl/ra_gl.c
@@ -1097,12 +1097,6 @@ static uint64_t gl_timer_stop(struct ra *ra, ra_timer *ratimer)
return timer->result;
}
-static void gl_flush(struct ra *ra)
-{
- GL *gl = ra_gl_get(ra);
- gl->Flush();
-}
-
static void gl_debug_marker(struct ra *ra, const char *msg)
{
struct ra_gl *p = ra->priv;
@@ -1130,6 +1124,5 @@ static struct ra_fns ra_fns_gl = {
.timer_destroy = gl_timer_destroy,
.timer_start = gl_timer_start,
.timer_stop = gl_timer_stop,
- .flush = gl_flush,
.debug_marker = gl_debug_marker,
};
diff --git a/video/out/opengl/ra_gl.h b/video/out/opengl/ra_gl.h
index e5e09a0197..9844977801 100644
--- a/video/out/opengl/ra_gl.h
+++ b/video/out/opengl/ra_gl.h
@@ -1,8 +1,7 @@
#pragma once
#include "common.h"
-#include "ra.h"
-#include "gl_utils.h"
+#include "utils.h"
struct ra *ra_create_gl(GL *gl, struct mp_log *log);
struct ra_tex *ra_create_wrapped_tex(struct ra *ra,
diff --git a/video/out/opengl/utils.c b/video/out/opengl/utils.c
index b8fc24a52e..3b296d52de 100644
--- a/video/out/opengl/utils.c
+++ b/video/out/opengl/utils.c
@@ -1,371 +1,269 @@
-#include "common/msg.h"
-#include "video/out/vo.h"
+/*
+ * This file is part of mpv.
+ * Parts based on MPlayer code by Reimar Döffinger.
+ *
+ * mpv is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * mpv is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with mpv. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <stddef.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdarg.h>
+#include <assert.h>
+
+#include <libavutil/sha.h>
+#include <libavutil/intreadwrite.h>
+#include <libavutil/mem.h>
+
+#include "osdep/io.h"
+
+#include "common/common.h"
+#include "options/path.h"
+#include "stream/stream.h"
+#include "formats.h"
#include "utils.h"
-// Standard parallel 2D projection, except y1 < y0 means that the coordinate
-// system is flipped, not the projection.
-void gl_transform_ortho(struct gl_transform *t, float x0, float x1,
- float y0, float y1)
+// GLU has this as gluErrorString (we don't use GLU, as it is legacy-OpenGL)
+static const char *gl_error_to_string(GLenum error)
{
- if (y1 < y0) {
- float tmp = y0;
- y0 = tmp - y1;
- y1 = tmp;
+ switch (error) {
+ case GL_INVALID_ENUM: return "INVALID_ENUM";
+ case GL_INVALID_VALUE: return "INVALID_VALUE";
+ case GL_INVALID_OPERATION: return "INVALID_OPERATION";
+ case GL_INVALID_FRAMEBUFFER_OPERATION: return "INVALID_FRAMEBUFFER_OPERATION";
+ case GL_OUT_OF_MEMORY: return "OUT_OF_MEMORY";
+ default: return "unknown";
}
-
- t->m[0][0] = 2.0f / (x1 - x0);
- t->m[0][1] = 0.0f;
- t->m[1][0] = 0.0f;
- t->m[1][1] = 2.0f / (y1 - y0);
- t->t[0] = -(x1 + x0) / (x1 - x0);
- t->t[1] = -(y1 + y0) / (y1 - y0);
-}
-
-// Apply the effects of one transformation to another, transforming it in the
-// process. In other words: post-composes t onto x
-void gl_transform_trans(struct gl_transform t, struct gl_transform *x)
-{
- struct gl_transform xt = *x;
- x->m[0][0] = t.m[0][0] * xt.m[0][0] + t.m[0][1] * xt.m[1][0];
- x->m[1][0] = t.m[1][0] * xt.m[0][0] + t.m[1][1] * xt.m[1][0];
- x->m[0][1] = t.m[0][0] * xt.m[0][1] + t.m[0][1] * xt.m[1][1];
- x->m[1][1] = t.m[1][0] * xt.m[0][1] + t.m[1][1] * xt.m[1][1];
- gl_transform_vec(t, &x->t[0], &x->t[1]);
-}
-
-void gl_transform_ortho_fbodst(struct gl_transform *t, struct fbodst fbo)
-{
- int y_dir = fbo.flip ? -1 : 1;
- gl_transform_ortho(t, 0, fbo.tex->params.w, 0, fbo.tex->params.h * y_dir);
}
-void ra_buf_pool_uninit(struct ra *ra, struct ra_buf_pool *pool)
+void gl_check_error(GL *gl, struct mp_log *log, const char *info)
{
- for (int i = 0; i < pool->num_buffers; i++)
- ra_buf_free(ra, &pool->buffers[i]);
-
- talloc_free(pool->buffers);
- *pool = (struct ra_buf_pool){0};
+ for (;;) {
+ GLenum error = gl->GetError();
+ if (error == GL_NO_ERROR)
+ break;
+ mp_msg(log, MSGL_ERR, "%s: OpenGL error %s.\n", info,
+ gl_error_to_string(error));
+ }
}
-static bool ra_buf_params_compatible(const struct ra_buf_params *new,
- const struct ra_buf_params *old)
+static int get_alignment(int stride)
{
- return new->type == old->type &&
- new->size <= old->size &&
- new->host_mapped == old->host_mapped &&
- new->host_mutable == old->host_mutable;
+ if (stride % 8 == 0)
+ return 8;
+ if (stride % 4 == 0)
+ return 4;
+ if (stride % 2 == 0)
+ return 2;
+ return 1;
}
-static bool ra_buf_pool_grow(struct ra *ra, struct ra_buf_pool *pool)
+// upload a texture, handling things like stride and slices
+// target: texture target, usually GL_TEXTURE_2D
+// format, type: texture parameters
+// dataptr, stride: image data
+// x, y, width, height: part of the image to upload
+void gl_upload_tex(GL *gl, GLenum target, GLenum format, GLenum type,
+ const void *dataptr, int stride,
+ int x, int y, int w, int h)
{
- struct ra_buf *buf = ra_buf_create(ra, &pool->current_params);
- if (!buf)
- return false;
-
- MP_TARRAY_INSERT_AT(NULL, pool->buffers, pool->num_buffers, pool->index, buf);
- MP_VERBOSE(ra, "Resized buffer pool to size %d\n", pool->num_buffers);
- return true;
+ int bpp = gl_bytes_per_pixel(format, type);
+ const uint8_t *data = dataptr;
+ int y_max = y + h;
+ if (w <= 0 || h <= 0 || !bpp)
+ return;
+ if (stride < 0) {
+ data += (h - 1) * stride;
+ stride = -stride;
+ }
+ gl->PixelStorei(GL_UNPACK_ALIGNMENT, get_alignment(stride));
+ int slice = h;
+ if (gl->mpgl_caps & MPGL_CAP_ROW_LENGTH) {
+ // this is not always correct, but should work for MPlayer
+ gl->PixelStorei(GL_UNPACK_ROW_LENGTH, stride / bpp);
+ } else {
+ if (stride != bpp * w)
+ slice = 1; // very inefficient, but at least it works
+ }
+ for (; y + slice <= y_max; y += slice) {
+ gl->TexSubImage2D(target, 0, x, y, w, slice, format, type, data);
+ data += stride * slice;
+ }
+ if (y < y_max)
+ gl->TexSubImage2D(target, 0, x, y, w, y_max - y, format, type, data);
+ if (gl->mpgl_caps & MPGL_CAP_ROW_LENGTH)
+ gl->PixelStorei(GL_UNPACK_ROW_LENGTH, 0);
+ gl->PixelStorei(GL_UNPACK_ALIGNMENT, 4);
}
-struct ra_buf *ra_buf_pool_get(struct ra *ra, struct ra_buf_pool *pool,
- const struct ra_buf_params *params)
+mp_image_t *gl_read_fbo_contents(GL *gl, int fbo, int w, int h)
{
- assert(!params->initial_data);
-
- if (!ra_buf_params_compatible(params, &pool->current_params)) {
- ra_buf_pool_uninit(ra, pool);
- pool->current_params = *params;
- }
-
- // Make sure we have at least one buffer available
- if (!pool->buffers && !ra_buf_pool_grow(ra, pool))
- return NULL;
-
- // Make sure the next buffer is available for use
- if (!ra->fns->buf_poll(ra, pool->buffers[pool->index]) &&
- !ra_buf_pool_grow(ra, pool))
- {
+ if (gl->es)
+ return NULL; // ES can't read from front buffer
+ mp_image_t *image = mp_image_alloc(IMGFMT_RGB24, w, h);
+ if (!image)
return NULL;
+ gl->BindFramebuffer(GL_FRAMEBUFFER, fbo);
+ GLenum obj = fbo ? GL_COLOR_ATTACHMENT0 : GL_FRONT;
+ gl->PixelStorei(GL_PACK_ALIGNMENT, 1);
+ gl->ReadBuffer(obj);
+ //flip image while reading (and also avoid stride-related trouble)
+ for (int y = 0; y < h; y++) {
+ gl->ReadPixels(0, h - y - 1, w, 1, GL_RGB, GL_UNSIGNED_BYTE,
+ image->planes[0] + y * image->stride[0]);
}
-
- struct ra_buf *buf = pool->buffers[pool->index++];
- pool->index %= pool->num_buffers;
-
- return buf;
+ gl->PixelStorei(GL_PACK_ALIGNMENT, 4);
+ gl->BindFramebuffer(GL_FRAMEBUFFER, 0);
+ return image;
}
-bool ra_tex_upload_pbo(struct ra *ra, struct ra_buf_pool *pbo,
- const struct ra_tex_upload_params *params)
+static void gl_vao_enable_attribs(struct gl_vao *vao)
{
- if (params->buf)
- return ra->fns->tex_upload(ra, params);
-
- struct ra_tex *tex = params->tex;
- size_t row_size = tex->params.dimensions == 2 ? params->stride :
- tex->params.w * tex->params.format->pixel_size;
-
- struct ra_buf_params bufparams = {
- .type = RA_BUF_TYPE_TEX_UPLOAD,
- .size = row_size * tex->params.h * tex->params.d,
- .host_mutable = true,
- };
-
- struct ra_buf *buf = ra_buf_pool_get(ra, pbo, &bufparams);
- if (!buf)
- return false;
-
- ra->fns->buf_update(ra, buf, 0, params->src, bufparams.size);
-
- struct ra_tex_upload_params newparams = *params;
- newparams.buf = buf;
- newparams.src = NULL;
-
- return ra->fns->tex_upload(ra, &newparams);
-}
+ GL *gl = vao->gl;
+
+ for (int n = 0; n < vao->num_entries; n++) {
+ const struct ra_renderpass_input *e = &vao->entries[n];
+ GLenum type = 0;
+ bool normalized = false;
+ switch (e->type) {
+ case RA_VARTYPE_INT:
+ type = GL_INT;
+ break;
+ case RA_VARTYPE_FLOAT:
+ type = GL_FLOAT;
+ break;
+ case RA_VARTYPE_BYTE_UNORM:
+ type = GL_UNSIGNED_BYTE;
+ normalized = true;
+ break;
+ default:
+ abort();
+ }
+ assert(e->dim_m == 1);
-struct ra_layout std140_layout(struct ra_renderpass_input *inp)
-{
- size_t el_size = ra_vartype_size(inp->type);
-
- // std140 packing rules:
- // 1. The alignment of generic values is their size in bytes
- // 2. The alignment of vectors is the vector length * the base count, with
- // the exception of vec3 which is always aligned like vec4
- // 3. The alignment of arrays is that of the element size rounded up to
- // the nearest multiple of vec4
- // 4. Matrices are treated like arrays of vectors
- // 5. Arrays/matrices are laid out with a stride equal to the alignment
- size_t size = el_size * inp->dim_v;
- if (inp->dim_v == 3)
- size += el_size;
- if (inp->dim_m > 1)
- size = MP_ALIGN_UP(size, sizeof(float[4]));
-
- return (struct ra_layout) {
- .align = size,
- .stride = size,
- .size = size * inp->dim_m,
- };
+ gl->EnableVertexAttribArray(n);
+ gl->VertexAttribPointer(n, e->dim_v, type, normalized,
+ vao->stride, (void *)(intptr_t)e->offset);
+ }
}
-struct ra_layout std430_layout(struct ra_renderpass_input *inp)
+void gl_vao_init(struct gl_vao *vao, GL *gl, int stride,
+ const struct ra_renderpass_input *entries,
+ int num_entries)
{
- size_t el_size = ra_vartype_size(inp->type);
-
- // std430 packing rules: like std140, except arrays/matrices are always
- // "tightly" packed, even arrays/matrices of vec3s
- size_t align = el_size * inp->dim_v;
- if (inp->dim_v == 3 && inp->dim_m == 1)
- align += el_size;
-
- return (struct ra_layout) {
- .align = align,
- .stride = align,
- .size = align * inp->dim_m,
+ assert(!vao->vao);
+ assert(!vao->buffer);
+
+ *vao = (struct gl_vao){
+ .gl = gl,
+ .stride = stride,
+ .entries = entries,
+ .num_entries = num_entries,
};
-}
-
-// Create a texture and a FBO using the texture as color attachments.
-// fmt: texture internal format
-// If the parameters are the same as the previous call, do not touch it.
-// flags can be 0, or a combination of FBOTEX_FUZZY_W and FBOTEX_FUZZY_H.
-// Enabling FUZZY for W or H means the w or h does not need to be exact.
-bool fbotex_change(struct fbotex *fbo, struct ra *ra, struct mp_log *log,
- int w, int h, const struct ra_format *fmt, int flags)
-{
- int lw = w, lh = h;
-
- if (fbo->tex) {
- int cw = w, ch = h;
- int rw = fbo->tex->params.w, rh = fbo->tex->params.h;
- if ((flags & FBOTEX_FUZZY_W) && cw < rw)
- cw = rw;
- if ((flags & FBOTEX_FUZZY_H) && ch < rh)
- ch = rh;
+ gl->GenBuffers(1, &vao->buffer);
- if (rw == cw && rh == ch && fbo->tex->params.format == fmt)
- goto done;
- }
+ if (gl->BindVertexArray) {
+ gl->BindBuffer(GL_ARRAY_BUFFER, vao->buffer);
- if (flags & FBOTEX_FUZZY_W)
- w = MP_ALIGN_UP(w, 256);
- if (flags & FBOTEX_FUZZY_H)
- h = MP_ALIGN_UP(h, 256);
+ gl->GenVertexArrays(1, &vao->vao);
+ gl->BindVertexArray(vao->vao);
+ gl_vao_enable_attribs(vao);
+ gl->BindVertexArray(0);
- mp_verbose(log, "Create FBO: %dx%d (%dx%d)\n", lw, lh, w, h);
-
- if (!fmt || !fmt->renderable || !fmt->linear_filter) {
- mp_err(log, "Format %s not supported.\n", fmt ? fmt->name : "(unset)");
- return false;
+ gl->BindBuffer(GL_ARRAY_BUFFER, 0);
}
+}
- fbotex_uninit(fbo);
-
- *fbo = (struct fbotex) {
- .ra = ra,
- };
-
- struct ra_tex_params params = {
- .dimensions = 2,
- .w = w,
- .h = h,
- .d = 1,
- .format = fmt,
- .src_linear = true,
- .render_src = true,
- .render_dst = true,
- .storage_dst = true,
- .blit_src = true,
- };
-
- fbo->tex = ra_tex_create(fbo->ra, &params);
-
- if (!fbo->tex) {
- mp_err(log, "Error: framebuffer could not be created.\n");
- fbotex_uninit(fbo);
- return false;
- }
-
-done:
-
- fbo->lw = lw;
- fbo->lh = lh;
+void gl_vao_uninit(struct gl_vao *vao)
+{
+ GL *gl = vao->gl;
+ if (!gl)
+ return;
- fbo->fbo = (struct fbodst){
- .tex = fbo->tex,
- };
+ if (gl->DeleteVertexArrays)
+ gl->DeleteVertexArrays(1, &vao->vao);
+ gl->DeleteBuffers(1, &vao->buffer);
- return true;
+ *vao = (struct gl_vao){0};
}
-void fbotex_uninit(struct fbotex *fbo)
+static void gl_vao_bind(struct gl_vao *vao)
{
- if (fbo->ra) {
- ra_tex_free(fbo->ra, &fbo->tex);
- *fbo = (struct fbotex) {0};
+ GL *gl = vao->gl;
+
+ if (gl->BindVertexArray) {
+ gl->BindVertexArray(vao->vao);
+ } else {
+ gl->BindBuffer(GL_ARRAY_BUFFER, vao->buffer);
+ gl_vao_enable_attribs(vao);
+ gl->BindBuffer(GL_ARRAY_BUFFER, 0);
}
}
-struct timer_pool {
- struct ra *ra;
- ra_timer *timer;
- bool running; // detect invalid usage
-
- uint64_t samples[VO_PERF_SAMPLE_COUNT];
- int sample_idx;
- int sample_count;
-
- uint64_t sum;
- uint64_t peak;
-};
-
-struct timer_pool *timer_pool_create(struct ra *ra)
+static void gl_vao_unbind(struct gl_vao *vao)
{
- if (!ra->fns->timer_create)
- return NULL;
-
- ra_timer *timer = ra->fns->timer_create(ra);
- if (!timer)
- return NULL;
+ GL *gl = vao->gl;
- struct timer_pool *pool = talloc(NULL, struct timer_pool);
- if (!pool) {
- ra->fns->timer_destroy(ra, timer);
- return NULL;
+ if (gl->BindVertexArray) {
+ gl->BindVertexArray(0);
+ } else {
+ for (int n = 0; n < vao->num_entries; n++)
+ gl->DisableVertexAttribArray(n);
}
-
- *pool = (struct timer_pool){ .ra = ra, .timer = timer };
- return pool;
}
-void timer_pool_destroy(struct timer_pool *pool)
+// Draw the vertex data (as described by the gl_vao_entry entries) in ptr
+// to the screen. num is the number of vertexes. prim is usually GL_TRIANGLES.
+// If ptr is NULL, then skip the upload, and use the data uploaded with the
+// previous call.
+void gl_vao_draw_data(struct gl_vao *vao, GLenum prim, void *ptr, size_t num)
{
- if (!pool)
- return;
-
- pool->ra->fns->timer_destroy(pool->ra, pool->timer);
- talloc_free(pool);
-}
+ GL *gl = vao->gl;
-void timer_pool_start(struct timer_pool *pool)
-{
- if (!pool)
- return;
+ if (ptr) {
+ gl->BindBuffer(GL_ARRAY_BUFFER, vao->buffer);
+ gl->BufferData(GL_ARRAY_BUFFER, num * vao->stride, ptr, GL_STREAM_DRAW);
+ gl->BindBuffer(GL_ARRAY_BUFFER, 0);
+ }
- assert(!pool->running);
- pool->ra->fns->timer_start(pool->ra, pool->timer);
- pool->running = true;
-}
+ gl_vao_bind(vao);
-void timer_pool_stop(struct timer_pool *pool)
-{
- if (!pool)
- return;
+ gl->DrawArrays(prim, 0, num);
- assert(pool->running);
- uint64_t res = pool->ra->fns->timer_stop(pool->ra, pool->timer);
- pool->running = false;
-
- if (res) {
- // Input res into the buffer and grab the previous value
- uint64_t old = pool->samples[pool->sample_idx];
- pool->sample_count = MPMIN(pool->sample_count + 1, VO_PERF_SAMPLE_COUNT);
- pool->samples[pool->sample_idx++] = res;
- pool->sample_idx %= VO_PERF_SAMPLE_COUNT;
- pool->sum = pool->sum + res - old;
-
- // Update peak if necessary
- if (res >= pool->peak) {
- pool->peak = res;
- } else if (pool->peak == old) {
- // It's possible that the last peak was the value we just removed,
- // if so we need to scan for the new peak
- uint64_t peak = res;
- for (int i = 0; i < VO_PERF_SAMPLE_COUNT; i++)
- peak = MPMAX(peak, pool->samples[i]);
- pool->peak = peak;
- }
- }
+ gl_vao_unbind(vao);
}
-struct mp_pass_perf timer_pool_measure(struct timer_pool *pool)
+static void GLAPIENTRY gl_debug_cb(GLenum source, GLenum type, GLuint id,
+ GLenum severity, GLsizei length,
+ const GLchar *message, const void *userParam)
{
- if (!pool)
- return (struct mp_pass_perf){0};
-
- struct mp_pass_perf res = {
- .peak = pool->peak,
- .count = pool->sample_count,
- };
-
- int idx = pool->sample_idx - pool->sample_count + VO_PERF_SAMPLE_COUNT;
- for (int i = 0; i < res.count; i++) {
- idx %= VO_PERF_SAMPLE_COUNT;
- res.samples[i] = pool->samples[idx++];
+ // keep in mind that the debug callback can be asynchronous
+ struct mp_log *log = (void *)userParam;
+ int level = MSGL_ERR;
+ switch (severity) {
+ case GL_DEBUG_SEVERITY_NOTIFICATION:level = MSGL_V; break;
+ case GL_DEBUG_SEVERITY_LOW: level = MSGL_INFO; break;
+ case GL_DEBUG_SEVERITY_MEDIUM: level = MSGL_WARN; break;
+ case GL_DEBUG_SEVERITY_HIGH: level = MSGL_ERR; break;
}
-
- if (res.count > 0) {
- res.last = res.samples[res.count - 1];
- res.avg = pool->sum / res.count;
- }
-
- return res;
+ mp_msg(log, level, "GL: %s\n", message);
}
-void mp_log_source(struct mp_log *log, int lev, const char *src)
+void gl_set_debug_logger(GL *gl, struct mp_log *log)
{
- int line = 1;
- if (!src)
- return;
- while (*src) {
- const char *end = strchr(src, '\n');
- const char *next = end + 1;
- if (!end)
- next = end = src + strlen(src);
- mp_msg(log, lev, "[%3d] %.*s\n", line, (int)(end - src), src);
- line++;
- src = next;
- }
+ if (gl->DebugMessageCallback)
+ gl->DebugMessageCallback(log ? gl_debug_cb : NULL, log);
}
diff --git a/video/out/opengl/utils.h b/video/out/opengl/utils.h
index 7d00d26cf5..18cab476ed 100644
--- a/video/out/opengl/utils.h
+++ b/video/out/opengl/utils.h
@@ -1,121 +1,54 @@
-#pragma once
+/*
+ * This file is part of mpv.
+ * Parts based on MPlayer code by Reimar Döffinger.
+ *
+ * mpv is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * mpv is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with mpv. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef MP_GL_UTILS_
+#define MP_GL_UTILS_
-#include <stdbool.h>
#include <math.h>
-#include "video/out/vo.h"
-#include "ra.h"
+#include "video/out/gpu/utils.h"
+#include "common.h"
-// A 3x2 matrix, with the translation part separate.
-struct gl_transform {
- // row-major, e.g. in mathematical notation:
- // | m[0][0] m[0][1] |
- // | m[1][0] m[1][1] |
- float m[2][2];
- float t[2];
-};
-
-static const struct gl_transform identity_trans = {
- .m = {{1.0, 0.0}, {0.0, 1.0}},
- .t = {0.0, 0.0},
-};
-
-void gl_transform_ortho(struct gl_transform *t, float x0, float x1,
- float y0, float y1);
-
-// This treats m as an affine transformation, in other words m[2][n] gets
-// added to the output.
-static inline void gl_transform_vec(struct gl_transform t, float *x, float *y)
-{
- float vx = *x, vy = *y;
- *x = vx * t.m[0][0] + vy * t.m[0][1] + t.t[0];
- *y = vx * t.m[1][0] + vy * t.m[1][1] + t.t[1];
-}
+struct mp_log;
-struct mp_rect_f {
- float x0, y0, x1, y1;
-};
-
-// Semantic equality (fuzzy comparison)
-static inline bool mp_rect_f_seq(struct mp_rect_f a, struct mp_rect_f b)
-{
- return fabs(a.x0 - b.x0) < 1e-6 && fabs(a.x1 - b.x1) < 1e-6 &&
- fabs(a.y0 - b.y0) < 1e-6 && fabs(a.y1 - b.y1) < 1e-6;
-}
-
-static inline void gl_transform_rect(struct gl_transform t, struct mp_rect_f *r)
-{
- gl_transform_vec(t, &r->x0, &r->y0);
- gl_transform_vec(t, &r->x1, &r->y1);
-}
+void gl_check_error(GL *gl, struct mp_log *log, const char *info);
-static inline bool gl_transform_eq(struct gl_transform a, struct gl_transform b)
-{
- for (int x = 0; x < 2; x++) {
- for (int y = 0; y < 2; y++) {
- if (a.m[x][y] != b.m[x][y])
- return false;
- }
- }
+void gl_upload_tex(GL *gl, GLenum target, GLenum format, GLenum type,
+ const void *dataptr, int stride,
+ int x, int y, int w, int h);
- return a.t[0] == b.t[0] && a.t[1] == b.t[1];
-}
+mp_image_t *gl_read_fbo_contents(GL *gl, int fbo, int w, int h);
-void gl_transform_trans(struct gl_transform t, struct gl_transform *x);
-
-struct fbodst {
- struct ra_tex *tex;
- bool flip; // mirror vertically
+struct gl_vao {
+ GL *gl;
+ GLuint vao; // the VAO object, or 0 if unsupported by driver
+ GLuint buffer; // GL_ARRAY_BUFFER used for the data
+ int stride; // size of each element (interleaved elements are assumed)
+ const struct ra_renderpass_input *entries;
+ int num_entries;
};
-void gl_transform_ortho_fbodst(struct gl_transform *t, struct fbodst fbo);
-
-// A pool of buffers, which can grow as needed
-struct ra_buf_pool {
- struct ra_buf_params current_params;
- struct ra_buf **buffers;
- int num_buffers;
- int index;
-};
-
-void ra_buf_pool_uninit(struct ra *ra, struct ra_buf_pool *pool);
-
-// Note: params->initial_data is *not* supported
-struct ra_buf *ra_buf_pool_get(struct ra *ra, struct ra_buf_pool *pool,
- const struct ra_buf_params *params);
-
-// Helper that wraps ra_tex_upload using texture upload buffers to ensure that
-// params->buf is always set. This is intended for RA-internal usage.
-bool ra_tex_upload_pbo(struct ra *ra, struct ra_buf_pool *pbo,
- const struct ra_tex_upload_params *params);
-
-// Layout rules for GLSL's packing modes
-struct ra_layout std140_layout(struct ra_renderpass_input *inp);
-struct ra_layout std430_layout(struct ra_renderpass_input *inp);
-
-struct fbotex {
- struct ra *ra;
- struct ra_tex *tex;
- int lw, lh; // logical (configured) size, <= than texture size
- struct fbodst fbo;
-};
-
-void fbotex_uninit(struct fbotex *fbo);
-bool fbotex_change(struct fbotex *fbo, struct ra *ra, struct mp_log *log,
- int w, int h, const struct ra_format *fmt, int flags);
-#define FBOTEX_FUZZY_W 1
-#define FBOTEX_FUZZY_H 2
-#define FBOTEX_FUZZY (FBOTEX_FUZZY_W | FBOTEX_FUZZY_H)
-
-// A wrapper around ra_timer that does result pooling, averaging etc.
-struct timer_pool;
+void gl_vao_init(struct gl_vao *vao, GL *gl, int stride,
+ const struct ra_renderpass_input *entries,
+ int num_entries);
+void gl_vao_uninit(struct gl_vao *vao);
+void gl_vao_draw_data(struct gl_vao *vao, GLenum prim, void *ptr, size_t num);
-struct timer_pool *timer_pool_create(struct ra *ra);
-void timer_pool_destroy(struct timer_pool *pool);
-void timer_pool_start(struct timer_pool *pool);
-void timer_pool_stop(struct timer_pool *pool);
-struct mp_pass_perf timer_pool_measure(struct timer_pool *pool);
+void gl_set_debug_logger(GL *gl, struct mp_log *log);
-// print a multi line string with line numbers (e.g. for shader sources)
-// log, lev: module and log level, as in mp_msg()
-void mp_log_source(struct mp_log *log, int lev, const char *src);
+#endif
diff --git a/video/out/vo.c b/video/out/vo.c
index f9c5d04e24..a40360b188 100644
--- a/video/out/vo.c
+++ b/video/out/vo.c
@@ -50,6 +50,7 @@
extern const struct vo_driver video_out_x11;
extern const struct vo_driver video_out_vdpau;
extern const struct vo_driver video_out_xv;
+extern const struct vo_driver video_out_gpu;
extern const struct vo_driver video_out_opengl;
extern const struct vo_driver video_out_opengl_cb;
extern const struct vo_driver video_out_null;
@@ -69,8 +70,8 @@ const struct vo_driver *const video_out_drivers[] =
#if HAVE_RPI
&video_out_rpi,
#endif
-#if HAVE_GL
- &video_out_opengl,
+#if HAVE_GPU
+ &video_out_gpu,
#endif
#if HAVE_VDPAU
&video_out_vdpau,
@@ -107,6 +108,7 @@ const struct vo_driver *const video_out_drivers[] =
&video_out_lavc,
#endif
#if HAVE_GL
+ &video_out_opengl,
&video_out_opengl_cb,
#endif
NULL
diff --git a/video/out/vo_opengl.c b/video/out/vo_gpu.c
index 72691e56c2..5df9e06f47 100644
--- a/video/out/vo_opengl.c
+++ b/video/out/vo_gpu.c
@@ -38,56 +38,25 @@
#include "video/mp_image.h"
#include "sub/osd.h"
-#include "opengl/context.h"
-#include "opengl/utils.h"
-#include "opengl/hwdec.h"
-#include "opengl/osd.h"
-#include "filter_kernels.h"
-#include "video/hwdec.h"
-#include "opengl/video.h"
-#include "opengl/ra_gl.h"
-
-#define NUM_VSYNC_FENCES 10
-
-struct vo_opengl_opts {
- int use_glFinish;
- int waitvsync;
- int use_gl_debug;
- int allow_sw;
- int swap_interval;
- int vsync_fences;
- char *backend;
- int es;
- int pattern[2];
-};
+#include "gpu/context.h"
+#include "gpu/hwdec.h"
+#include "gpu/video.h"
-struct gl_priv {
+struct gpu_priv {
struct vo *vo;
struct mp_log *log;
- MPGLContext *glctx;
- GL *gl;
- struct ra *ra;
-
- struct vo_opengl_opts opts;
+ struct ra_ctx *ctx;
+ char *context_name;
+ char *context_type;
+ struct ra_ctx_opts opts;
struct gl_video *renderer;
-
struct ra_hwdec *hwdec;
int events;
-
- int frames_rendered;
- unsigned int prev_sgi_sync_count;
-
- // check-pattern sub-option; for testing/debugging
- int last_pattern;
- int matches, mismatches;
-
- GLsync vsync_fences[NUM_VSYNC_FENCES];
- int num_vsync_fences;
};
-static void resize(struct gl_priv *p)
+static void resize(struct gpu_priv *p)
{
struct vo *vo = p->vo;
@@ -102,88 +71,39 @@ static void resize(struct gl_priv *p)
vo->want_redraw = true;
}
-static void check_pattern(struct vo *vo, int item)
-{
- struct gl_priv *p = vo->priv;
- int expected = p->opts.pattern[p->last_pattern];
- if (item == expected) {
- p->last_pattern++;
- if (p->last_pattern >= 2)
- p->last_pattern = 0;
- p->matches++;
- } else {
- p->mismatches++;
- MP_WARN(vo, "wrong pattern, expected %d got %d (hit: %d, mis: %d)\n",
- expected, item, p->matches, p->mismatches);
- }
-}
-
static void draw_frame(struct vo *vo, struct vo_frame *frame)
{
- struct gl_priv *p = vo->priv;
- GL *gl = p->gl;
-
- mpgl_start_frame(p->glctx);
+ struct gpu_priv *p = vo->priv;
+ struct ra_swapchain *sw = p->ctx->swapchain;
- if (gl->FenceSync && p->num_vsync_fences < p->opts.vsync_fences) {
- GLsync fence = gl->FenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0);;
- if (fence)
- p->vsync_fences[p->num_vsync_fences++] = fence;
+ struct ra_tex *tex = sw->fns->start_frame(sw);
+ if (!tex) {
+ MP_ERR(vo, "Failed starting frame!\n");
+ return;
}
- struct fbodst target = {
- .tex = ra_create_wrapped_fb(p->ra, p->glctx->main_fb,
- vo->dwidth, vo->dheight),
- .flip = !p->glctx->flip_v,
+ struct fbodst dst = {
+ .tex = tex,
+ .flip = sw->flip_v,
};
- gl_video_render_frame(p->renderer, frame, target);
- ra_tex_free(p->ra, &target.tex);
- if (p->opts.use_glFinish)
- gl->Finish();
+ gl_video_render_frame(p->renderer, frame, dst);
+ if (!sw->fns->submit_frame(sw, frame)) {
+ MP_ERR(vo, "Failed presenting frame!\n");
+ return;
+ }
}
static void flip_page(struct vo *vo)
{
- struct gl_priv *p = vo->priv;
- GL *gl = p->gl;
-
- mpgl_swap_buffers(p->glctx);
-
- p->frames_rendered++;
- if (p->frames_rendered > 5 && !p->opts.use_gl_debug)
- ra_gl_set_debug(p->ra, false);
-
- if (p->opts.use_glFinish)
- gl->Finish();
-
- if (p->opts.waitvsync || p->opts.pattern[0]) {
- if (gl->GetVideoSync) {
- unsigned int n1 = 0, n2 = 0;
- gl->GetVideoSync(&n1);
- if (p->opts.waitvsync)
- gl->WaitVideoSync(2, (n1 + 1) % 2, &n2);
- int step = n1 - p->prev_sgi_sync_count;
- p->prev_sgi_sync_count = n1;
- MP_DBG(vo, "Flip counts: %u->%u, step=%d\n", n1, n2, step);
- if (p->opts.pattern[0])
- check_pattern(vo, step);
- } else {
- MP_WARN(vo, "GLX_SGI_video_sync not available, disabling.\n");
- p->opts.waitvsync = 0;
- p->opts.pattern[0] = 0;
- }
- }
- while (p->opts.vsync_fences > 0 && p->num_vsync_fences >= p->opts.vsync_fences) {
- gl->ClientWaitSync(p->vsync_fences[0], GL_SYNC_FLUSH_COMMANDS_BIT, 1e9);
- gl->DeleteSync(p->vsync_fences[0]);
- MP_TARRAY_REMOVE_AT(p->vsync_fences, p->num_vsync_fences, 0);
- }
+ struct gpu_priv *p = vo->priv;
+ struct ra_swapchain *sw = p->ctx->swapchain;
+ sw->fns->swap_buffers(sw);
}
static int query_format(struct vo *vo, int format)
{
- struct gl_priv *p = vo->priv;
+ struct gpu_priv *p = vo->priv;
if (!gl_video_check_format(p->renderer, format))
return 0;
return 1;
@@ -191,13 +111,12 @@ static int query_format(struct vo *vo, int format)
static int reconfig(struct vo *vo, struct mp_image_params *params)
{
- struct gl_priv *p = vo->priv;
+ struct gpu_priv *p = vo->priv;
- if (mpgl_reconfig_window(p->glctx) < 0)
+ if (!p->ctx->fns->reconfig(p->ctx))
return -1;
resize(p);
-
gl_video_config(p->renderer, params);
return 0;
@@ -205,12 +124,12 @@ static int reconfig(struct vo *vo, struct mp_image_params *params)
static void request_hwdec_api(struct vo *vo, void *api)
{
- struct gl_priv *p = vo->priv;
+ struct gpu_priv *p = vo->priv;
if (p->hwdec)
return;
- p->hwdec = ra_hwdec_load_api(p->vo->log, p->ra, p->vo->global,
+ p->hwdec = ra_hwdec_load_api(p->vo->log, p->ctx->ra, p->vo->global,
vo->hwdec_devs, (intptr_t)api);
gl_video_set_hwdec(p->renderer, p->hwdec);
}
@@ -222,12 +141,12 @@ static void call_request_hwdec_api(void *ctx, enum hwdec_type type)
vo_control(ctx, VOCTRL_LOAD_HWDEC_API, (void *)(intptr_t)type);
}
-static void get_and_update_icc_profile(struct gl_priv *p)
+static void get_and_update_icc_profile(struct gpu_priv *p)
{
if (gl_video_icc_auto_enabled(p->renderer)) {
MP_VERBOSE(p, "Querying ICC profile...\n");
bstr icc = bstr0(NULL);
- int r = mpgl_control(p->glctx, &p->events, VOCTRL_GET_ICC_PROFILE, &icc);
+ int r = p->ctx->fns->control(p->ctx, &p->events, VOCTRL_GET_ICC_PROFILE, &icc);
if (r != VO_NOTAVAIL) {
if (r == VO_FALSE) {
@@ -241,10 +160,10 @@ static void get_and_update_icc_profile(struct gl_priv *p)
}
}
-static void get_and_update_ambient_lighting(struct gl_priv *p)
+static void get_and_update_ambient_lighting(struct gpu_priv *p)
{
int lux;
- int r = mpgl_control(p->glctx, &p->events, VOCTRL_GET_AMBIENT_LUX, &lux);
+ int r = p->ctx->fns->control(p->ctx, &p->events, VOCTRL_GET_AMBIENT_LUX, &lux);
if (r == VO_TRUE) {
gl_video_set_ambient_lux(p->renderer, lux);
}
@@ -256,7 +175,8 @@ static void get_and_update_ambient_lighting(struct gl_priv *p)
static int control(struct vo *vo, uint32_t request, void *data)
{
- struct gl_priv *p = vo->priv;
+ struct gpu_priv *p = vo->priv;
+ struct ra_swapchain *sw = p->ctx->swapchain;
switch (request) {
case VOCTRL_SET_PANSCAN:
@@ -266,14 +186,13 @@ static int control(struct vo *vo, uint32_t request, void *data)
vo->want_redraw = true;
return VO_TRUE;
case VOCTRL_SCREENSHOT_WIN: {
- struct mp_image *screen = gl_read_fbo_contents(p->gl, p->glctx->main_fb,
- vo->dwidth, vo->dheight);
+ struct mp_image *screen = NULL;
+ if (sw->fns->screenshot)
+ screen = sw->fns->screenshot(sw);
if (!screen)
break; // redirect to backend
// set image parameters according to the display, if possible
screen->params.color = gl_video_get_output_colorspace(p->renderer);
- if (p->glctx->flip_v)
- mp_image_vflip(screen);
*(struct mp_image **)data = screen;
return true;
}
@@ -300,7 +219,7 @@ static int control(struct vo *vo, uint32_t request, void *data)
}
int events = 0;
- int r = mpgl_control(p->glctx, &events, request, data);
+ int r = p->ctx->fns->control(p->ctx, &events, request, data);
if (events & VO_EVENT_ICC_PROFILE_CHANGED) {
get_and_update_icc_profile(p);
vo->want_redraw = true;
@@ -322,16 +241,16 @@ static int control(struct vo *vo, uint32_t request, void *data)
static void wakeup(struct vo *vo)
{
- struct gl_priv *p = vo->priv;
- if (p->glctx && p->glctx->driver->wakeup)
- p->glctx->driver->wakeup(p->glctx);
+ struct gpu_priv *p = vo->priv;
+ if (p->ctx && p->ctx->fns->wakeup)
+ p->ctx->fns->wakeup(p->ctx);
}
static void wait_events(struct vo *vo, int64_t until_time_us)
{
- struct gl_priv *p = vo->priv;
- if (p->glctx->driver->wait_events) {
- p->glctx->driver->wait_events(p->glctx, until_time_us);
+ struct gpu_priv *p = vo->priv;
+ if (p->ctx && p->ctx->fns->wait_events) {
+ p->ctx->fns->wait_events(p->ctx, until_time_us);
} else {
vo_wait_default(vo, until_time_us);
}
@@ -340,14 +259,14 @@ static void wait_events(struct vo *vo, int64_t until_time_us)
static struct mp_image *get_image(struct vo *vo, int imgfmt, int w, int h,
int stride_align)
{
- struct gl_priv *p = vo->priv;
+ struct gpu_priv *p = vo->priv;
return gl_video_get_image(p->renderer, imgfmt, w, h, stride_align);
}
static void uninit(struct vo *vo)
{
- struct gl_priv *p = vo->priv;
+ struct gpu_priv *p = vo->priv;
gl_video_uninit(p->renderer);
ra_hwdec_uninit(p->hwdec);
@@ -355,53 +274,29 @@ static void uninit(struct vo *vo)
hwdec_devices_set_loader(vo->hwdec_devs, NULL, NULL);
hwdec_devices_destroy(vo->hwdec_devs);
}
- ra_free(&p->ra);
- mpgl_uninit(p->glctx);
+ ra_ctx_destroy(&p->ctx);
}
static int preinit(struct vo *vo)
{
- struct gl_priv *p = vo->priv;
+ struct gpu_priv *p = vo->priv;
p->vo = vo;
p->log = vo->log;
- int vo_flags = 0;
-
int alpha_mode;
mp_read_option_raw(vo->global, "alpha", &m_option_type_choice, &alpha_mode);
- if (alpha_mode == 1)
- vo_flags |= VOFLAG_ALPHA;
-
- if (p->opts.use_gl_debug)
- vo_flags |= VOFLAG_GL_DEBUG;
-
- if (p->opts.es == 1)
- vo_flags |= VOFLAG_GLES;
- if (p->opts.es == 2)
- vo_flags |= VOFLAG_GLES | VOFLAG_GLES2;
- if (p->opts.es == -1)
- vo_flags |= VOFLAG_NO_GLES;
-
- if (p->opts.allow_sw)
- vo_flags |= VOFLAG_SW;
-
- p->glctx = mpgl_init(vo, p->opts.backend, vo_flags);
- if (!p->glctx)
- goto err_out;
- p->gl = p->glctx->gl;
-
- if (p->gl->SwapInterval) {
- p->gl->SwapInterval(p->opts.swap_interval);
- } else {
- MP_VERBOSE(vo, "swap_control extension missing.\n");
- }
+ struct ra_ctx_opts opts = p->opts;
+ opts.want_alpha = alpha_mode == 1;
- p->ra = ra_create_gl(p->gl, vo->log);
- if (!p->ra)
+ p->ctx = ra_ctx_create(vo, p->context_type, p->context_name, opts);
+ if (!p->ctx)
goto err_out;
+ assert(p->ctx->ra);
+ assert(p->ctx->swapchain);
+ struct ra_swapchain *sw = p->ctx->swapchain;
- p->renderer = gl_video_init(p->ra, vo->log, vo->global);
+ p->renderer = gl_video_init(p->ctx->ra, vo->log, vo->global);
gl_video_set_osd_source(p->renderer, vo->osd);
gl_video_configure_queue(p->renderer, vo);
@@ -411,13 +306,11 @@ static int preinit(struct vo *vo)
hwdec_devices_set_loader(vo->hwdec_devs, call_request_hwdec_api, vo);
- p->hwdec = ra_hwdec_load(p->vo->log, p->ra, vo->global,
+ p->hwdec = ra_hwdec_load(p->vo->log, p->ctx->ra, vo->global,
vo->hwdec_devs, vo->opts->gl_hwdec_interop);
gl_video_set_hwdec(p->renderer, p->hwdec);
- gl_check_error(p->gl, p->log, "before retrieving framebuffer depth");
- int fb_depth = gl_get_fb_depth(p->gl, p->glctx->main_fb);
- gl_check_error(p->gl, p->log, "retrieving framebuffer depth");
+ int fb_depth = sw->fns->color_depth ? sw->fns->color_depth(sw) : 0;
if (fb_depth)
MP_VERBOSE(p, "Reported display depth: %d\n", fb_depth);
gl_video_set_fb_depth(p->renderer, fb_depth);
@@ -429,13 +322,54 @@ err_out:
return -1;
}
-#define OPT_BASE_STRUCT struct gl_priv
+#define OPT_BASE_STRUCT struct gpu_priv
+static const m_option_t options[] = {
+ OPT_STRING_VALIDATE("gpu-context", context_name, 0, ra_ctx_validate_context),
+ OPT_STRING_VALIDATE("gpu-api", context_type, 0, ra_ctx_validate_api),
+ OPT_FLAG("gpu-debug", opts.debug, 0),
+ OPT_FLAG("gpu-sw", opts.allow_sw, 0),
+ OPT_INTRANGE("swapchain-depth", opts.swapchain_depth, 0, 1, 8),
+ {0}
+};
+
+static const struct gpu_priv defaults = { .opts = {
+ .swapchain_depth = 3,
+}};
+
+const struct vo_driver video_out_gpu = {
+ .description = "Shader-based GPU Renderer",
+ .name = "gpu",
+ .caps = VO_CAP_ROTATE90,
+ .preinit = preinit,
+ .query_format = query_format,
+ .reconfig = reconfig,
+ .control = control,
+ .get_image = get_image,
+ .draw_frame = draw_frame,
+ .flip_page = flip_page,
+ .wait_events = wait_events,
+ .wakeup = wakeup,
+ .uninit = uninit,
+ .priv_size = sizeof(struct gpu_priv),
+ .priv_defaults = &defaults,
+ .options = options,
+};
+
+static int preinit_opengl(struct vo *vo)
+{
+ MP_WARN(vo, "--vo=opengl was replaced by --vo=gpu --gpu-api=opengl, and will"
+ " be removed in the future!\n");
+
+ struct gpu_priv *p = vo->priv;
+ p->context_type = "opengl";
+ return preinit(vo);
+}
const struct vo_driver video_out_opengl = {
- .description = "Extended OpenGL Renderer",
+ .description = "Shader-based GPU Renderer",
.name = "opengl",
.caps = VO_CAP_ROTATE90,
- .preinit = preinit,
+ .preinit = preinit_opengl,
.query_format = query_format,
.reconfig = reconfig,
.control = control,
@@ -445,26 +379,7 @@ const struct vo_driver video_out_opengl = {
.wait_events = wait_events,
.wakeup = wakeup,
.uninit = uninit,
- .priv_size = sizeof(struct gl_priv),
- .options = (const m_option_t[]) {
- OPT_FLAG("opengl-glfinish", opts.use_glFinish, 0),
- OPT_FLAG("opengl-waitvsync", opts.waitvsync, 0),
- OPT_INT("opengl-swapinterval", opts.swap_interval, 0),
- OPT_FLAG("opengl-debug", opts.use_gl_debug, 0),
- OPT_STRING_VALIDATE("opengl-backend", opts.backend, 0,
- mpgl_validate_backend_opt),
- OPT_FLAG("opengl-sw", opts.allow_sw, 0),
- OPT_CHOICE("opengl-es", opts.es, 0, ({"no", -1}, {"auto", 0},
- {"yes", 1}, {"force2", 2})),
- OPT_INTPAIR("opengl-check-pattern", opts.pattern, 0),
- OPT_INTRANGE("opengl-vsync-fences", opts.vsync_fences, 0,
- 0, NUM_VSYNC_FENCES),
-
- {0}
- },
- .priv_defaults = &(const struct gl_priv){
- .opts = {
- .swap_interval = 1,
- },
- },
+ .priv_size = sizeof(struct gpu_priv),
+ .priv_defaults = &defaults,
+ .options = options,
};
diff --git a/video/out/vo_opengl_cb.c b/video/out/vo_opengl_cb.c
index ea6aaa9048..7e95e8bd31 100644
--- a/video/out/vo_opengl_cb.c
+++ b/video/out/vo_opengl_cb.c
@@ -24,9 +24,10 @@
#include "common/global.h"
#include "player/client.h"
+#include "gpu/video.h"
+#include "gpu/hwdec.h"
#include "opengl/common.h"
-#include "opengl/video.h"
-#include "opengl/hwdec.h"
+#include "opengl/context.h"
#include "opengl/ra_gl.h"
#include "libmpv/opengl_cb.h"
@@ -86,7 +87,7 @@ struct mpv_opengl_cb_context {
// application's OpenGL context is current - i.e. only while the
// host application is calling certain mpv_opengl_cb_* APIs.
GL *gl;
- struct ra *ra;
+ struct ra_ctx *ra_ctx;
struct gl_video *renderer;
struct ra_hwdec *hwdec;
struct m_config_cache *vo_opts_cache;
@@ -171,16 +172,36 @@ int mpv_opengl_cb_init_gl(struct mpv_opengl_cb_context *ctx, const char *exts,
return MPV_ERROR_UNSUPPORTED;
}
- ctx->ra = ra_create_gl(ctx->gl, ctx->log);
- if (!ctx->ra)
+ // initialize a blank ra_ctx to reuse ra_gl_ctx
+ ctx->ra_ctx = talloc_zero(ctx, struct ra_ctx);
+ ctx->ra_ctx->log = ctx->log;
+ ctx->ra_ctx->global = ctx->global;
+ ctx->ra_ctx->opts = (struct ra_ctx_opts) {
+ .probing = false,
+ .allow_sw = true,
+ };
+
+ static const struct ra_swapchain_fns empty_swapchain_fns = {0};
+ struct ra_gl_ctx_params gl_params = {
+ // vo_opengl_cb is essentially like a gigantic external swapchain where
+ // the user is in charge of presentation / swapping etc. But we don't
+ // actually need to provide any of these functions, since we can just
+ // not call them to begin with - so just set it to an empty object to
+ // signal to ra_gl_ctx that we don't care about its latency emulation
+ // functionality
+ .external_swapchain = &empty_swapchain_fns
+ };
+
+ ctx->gl->SwapInterval = NULL; // we shouldn't randomly change this, so lock it
+ if (!ra_gl_ctx_init(ctx->ra_ctx, ctx->gl, gl_params))
return MPV_ERROR_UNSUPPORTED;
- ctx->renderer = gl_video_init(ctx->ra, ctx->log, ctx->global);
+ ctx->renderer = gl_video_init(ctx->ra_ctx->ra, ctx->log, ctx->global);
m_config_cache_update(ctx->vo_opts_cache);
ctx->hwdec_devs = hwdec_devices_create();
- ctx->hwdec = ra_hwdec_load(ctx->log, ctx->ra, ctx->global,
+ ctx->hwdec = ra_hwdec_load(ctx->log, ctx->ra_ctx->ra, ctx->global,
ctx->hwdec_devs, ctx->vo_opts->gl_hwdec_interop);
gl_video_set_hwdec(ctx->renderer, ctx->hwdec);
@@ -221,7 +242,7 @@ int mpv_opengl_cb_uninit_gl(struct mpv_opengl_cb_context *ctx)
ctx->hwdec = NULL;
hwdec_devices_destroy(ctx->hwdec_devs);
ctx->hwdec_devs = NULL;
- ra_free(&ctx->ra);
+ ra_ctx_destroy(&ctx->ra_ctx);
talloc_free(ctx->gl);
ctx->gl = NULL;
return 0;
@@ -236,11 +257,6 @@ int mpv_opengl_cb_draw(mpv_opengl_cb_context *ctx, int fbo, int vp_w, int vp_h)
return MPV_ERROR_UNSUPPORTED;
}
- struct fbodst target = {
- .tex = ra_create_wrapped_fb(ctx->ra, fbo, vp_w, abs(vp_h)),
- .flip = vp_h < 0,
- };
-
reset_gl_state(ctx->gl);
pthread_mutex_lock(&ctx->lock);
@@ -280,7 +296,7 @@ int mpv_opengl_cb_draw(mpv_opengl_cb_context *ctx, int fbo, int vp_w, int vp_h)
mp_read_option_raw(ctx->global, "opengl-debug", &m_option_type_flag,
&debug);
ctx->gl->debug_context = debug;
- ra_gl_set_debug(ctx->ra, debug);
+ ra_gl_set_debug(ctx->ra_ctx->ra, debug);
if (gl_video_icc_auto_enabled(ctx->renderer))
MP_ERR(ctx, "icc-profile-auto is not available with opengl-cb\n");
}
@@ -316,7 +332,14 @@ int mpv_opengl_cb_draw(mpv_opengl_cb_context *ctx, int fbo, int vp_w, int vp_h)
pthread_mutex_unlock(&ctx->lock);
MP_STATS(ctx, "glcb-render");
+ struct ra_swapchain *sw = ctx->ra_ctx->swapchain;
+ ra_gl_ctx_resize(sw, vp_w, abs(vp_h), fbo);
+ struct fbodst target = {
+ .tex = ra_gl_ctx_start_frame(sw),
+ .flip = vp_h < 0,
+ };
gl_video_render_frame(ctx->renderer, frame, target);
+ ra_gl_ctx_submit_frame(sw, frame);
reset_gl_state(ctx->gl);
@@ -328,8 +351,6 @@ int mpv_opengl_cb_draw(mpv_opengl_cb_context *ctx, int fbo, int vp_w, int vp_h)
pthread_cond_wait(&ctx->wakeup, &ctx->lock);
pthread_mutex_unlock(&ctx->lock);
- ra_tex_free(ctx->ra, &target.tex);
-
return 0;
}
diff --git a/video/out/vo_rpi.c b/video/out/vo_rpi.c
index 5b5d62c78f..8b819af163 100644
--- a/video/out/vo_rpi.c
+++ b/video/out/vo_rpi.c
@@ -44,7 +44,7 @@
#include "sub/osd.h"
#include "opengl/ra_gl.h"
-#include "opengl/video.h"
+#include "gpu/video.h"
struct mp_egl_rpi {
struct mp_log *log;
diff --git a/wscript b/wscript
index 9c4c823a08..a50dcfed69 100644
--- a/wscript
+++ b/wscript
@@ -783,15 +783,20 @@ video_output_features = [
),
}, {
'name': '--gl',
- 'desc': 'OpenGL video outputs',
+ 'desc': 'OpenGL context support',
'deps': 'gl-cocoa || gl-x11 || egl-x11 || egl-drm || '
+ 'gl-win32 || gl-wayland || rpi || mali-fbdev || '
+ 'plain-gl',
'func': check_true,
+ }, {
+ 'name': '--gpu',
+ 'desc': 'GPU-accelerated video output support',
+ 'deps': 'gl',
+ 'func': check_true,
'req': True,
- 'fmsg': "No OpenGL video output found or enabled. " +
- "Aborting. If you really mean to compile without OpenGL " +
- "video outputs use --disable-gl."
+ 'fmsg': "No GPU context found or enabled. Aborting. " +
+ "If you really mean to compile without support for " +
+ "`--vo=gpu`, then use --disable-gpu."
}, {
'name': 'egl-helpers',
'desc': 'EGL helper functions',
diff --git a/wscript_build.py b/wscript_build.py
index 2f6c08bc0a..8dab6012c0 100644
--- a/wscript_build.py
+++ b/wscript_build.py
@@ -385,45 +385,46 @@ def build(ctx):
( "video/out/dither.c" ),
( "video/out/filter_kernels.c" ),
( "video/out/opengl/angle_dynamic.c", "egl-angle" ),
+ ( "video/out/gpu/context.c", "gpu" ),
+ ( "video/out/gpu/hwdec.c", "gpu" ),
+ ( "video/out/gpu/lcms.c", "gpu" ),
+ ( "video/out/gpu/osd.c", "gpu" ),
+ ( "video/out/gpu/ra.c", "gpu" ),
+ ( "video/out/gpu/shader_cache.c", "gpu" ),
+ ( "video/out/gpu/user_shaders.c", "gpu" ),
+ ( "video/out/gpu/utils.c", "gpu" ),
+ ( "video/out/gpu/video.c", "gpu" ),
+ ( "video/out/gpu/video_shaders.c", "gpu" ),
( "video/out/opengl/common.c", "gl" ),
+ ( "video/out/opengl/formats.c", "gl" ),
+ ( "video/out/opengl/utils.c", "gl" ),
+ ( "video/out/opengl/ra_gl.c", "gl" ),
( "video/out/opengl/context.c", "gl" ),
- ( "video/out/opengl/context_angle.c", "egl-angle-win32" ),
- ( "video/out/opengl/context_cocoa.c", "gl-cocoa" ),
+# ( "video/out/opengl/context_angle.c", "egl-angle-win32" ),
+# ( "video/out/opengl/context_cocoa.c", "gl-cocoa" ),
( "video/out/opengl/context_drm_egl.c", "egl-drm" ),
- ( "video/out/opengl/context_dxinterop.c","gl-dxinterop" ),
+# ( "video/out/opengl/context_dxinterop.c","gl-dxinterop" ),
( "video/out/opengl/context_mali_fbdev.c","mali-fbdev" ),
( "video/out/opengl/context_rpi.c", "rpi" ),
( "video/out/opengl/context_vdpau.c", "vdpau-gl-x11" ),
( "video/out/opengl/context_wayland.c", "gl-wayland" ),
- ( "video/out/opengl/context_w32.c", "gl-win32" ),
- ( "video/out/opengl/context_x11.c", "gl-x11" ),
+# ( "video/out/opengl/context_w32.c", "gl-win32" ),
+ ( "video/out/opengl/context_glx.c", "gl-x11" ),
( "video/out/opengl/context_x11egl.c", "egl-x11" ),
( "video/out/opengl/cuda_dynamic.c", "cuda-hwaccel" ),
- ( "video/out/opengl/d3d11_helpers.c", "egl-angle-win32" ),
+# ( "video/out/opengl/d3d11_helpers.c", "egl-angle-win32" ),
( "video/out/opengl/egl_helpers.c", "egl-helpers" ),
- ( "video/out/opengl/formats.c", "gl" ),
- ( "video/out/opengl/gl_utils.c", "gl" ),
- ( "video/out/opengl/hwdec.c", "gl" ),
( "video/out/opengl/hwdec_cuda.c", "cuda-hwaccel" ),
- ( "video/out/opengl/hwdec_d3d11egl.c", "d3d-hwaccel" ),
- ( "video/out/opengl/hwdec_d3d11eglrgb.c","d3d-hwaccel" ),
- ( "video/out/opengl/hwdec_dxva2gldx.c", "gl-dxinterop-d3d9" ),
- ( "video/out/opengl/hwdec_dxva2egl.c", "d3d9-hwaccel" ),
+# ( "video/out/opengl/hwdec_d3d11egl.c", "d3d-hwaccel" ),
+# ( "video/out/opengl/hwdec_d3d11eglrgb.c","d3d-hwaccel" ),
+# ( "video/out/opengl/hwdec_dxva2gldx.c", "gl-dxinterop-d3d9" ),
+# ( "video/out/opengl/hwdec_dxva2egl.c", "d3d9-hwaccel" ),
( "video/out/opengl/hwdec_osx.c", "videotoolbox-gl" ),
( "video/out/opengl/hwdec_ios.m", "ios-gl" ),
( "video/out/opengl/hwdec_rpi.c", "rpi" ),
( "video/out/opengl/hwdec_vaegl.c", "vaapi-egl" ),
( "video/out/opengl/hwdec_vaglx.c", "vaapi-glx" ),
( "video/out/opengl/hwdec_vdpau.c", "vdpau-gl-x11" ),
- ( "video/out/opengl/lcms.c", "gl" ),
- ( "video/out/opengl/osd.c", "gl" ),
- ( "video/out/opengl/ra.c", "gl" ),
- ( "video/out/opengl/ra_gl.c", "gl" ),
- ( "video/out/opengl/shader_cache.c", "gl" ),
- ( "video/out/opengl/user_shaders.c", "gl" ),
- ( "video/out/opengl/utils.c", "gl" ),
- ( "video/out/opengl/video.c", "gl" ),
- ( "video/out/opengl/video_shaders.c", "gl" ),
( "video/out/vo.c" ),
( "video/out/vo_caca.c", "caca" ),
( "video/out/vo_drm.c", "drm" ),
@@ -432,7 +433,7 @@ def build(ctx):
( "video/out/vo_lavc.c", "encoding" ),
( "video/out/vo_rpi.c", "rpi" ),
( "video/out/vo_null.c" ),
- ( "video/out/vo_opengl.c", "gl" ),
+ ( "video/out/vo_gpu.c", "gpu" ),
( "video/out/vo_opengl_cb.c", "gl" ),
( "video/out/vo_sdl.c", "sdl2" ),
( "video/out/vo_tct.c" ),