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.
This commit is contained in:
Niklas Haas 2017-09-14 08:04:55 +02:00
parent 20f958c977
commit 65979986a9
63 changed files with 2351 additions and 2053 deletions

View File

@ -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

View File

@ -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

View File

@ -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.
``--opengl-es=<mode>``
Select whether to use GLES:
``--gpu-api=<type>``
Controls which type of graphics APIs will be accepted:
yes
Try to prefer ES over Desktop GL
force2
Try to request a ES 2.0 context (the driver might ignore this)
no
Try to prefer desktop GL over ES
auto
Use the default for each backend (default)
Use any available API (default)
opengl
Allow only OpenGL (requires OpenGL 2.1+ or GLES 2.0+)
``--opengl-fbo-format=<fmt>``
``--opengl-es=<mode>``
Controls which type of OpenGL context will be accepted:
auto
Allow all types of OpenGL (default)
yes
Only allow GLES
no
Only allow desktop/core GL
``--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.

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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;

View File

@ -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.

186
video/out/gpu/context.c Normal file
View File

@ -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;
}

95
video/out/gpu/context.h Normal file
View File

@ -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);

View File

@ -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);

View File

@ -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;

View File

@ -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])
{

372
video/out/gpu/utils.c Normal file
View File

@ -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;
}
}

120
video/out/gpu/utils.h Normal file
View File

@ -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);

View File

@ -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);
}

View File

@ -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.

View File

@ -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"

View File

@ -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;
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();
}
done:
talloc_free(tmp);
return ret;
}
int mpgl_validate_backend_opt(struct mp_log *log, const struct m_option *opt,
struct bstr name, struct bstr param)
static void *get_native_display(void *priv, const char *name)
{
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;
}
char s[20];
snprintf(s, sizeof(s), "%.*s", BSTR_P(param));
return mpgl_find_backend(s) >= -1 ? 1 : M_OPT_INVALID;
}
static void *get_native_display(void *pctx, 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;
return strcmp(ctx->native_display_type, name) == 0 ? ctx->native_display : NULL;
if (strcmp(p->params.native_display_type, name) != 0)
return 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;
}
vo->probing = old_probing;
if (!ctx->gl->version && !ctx->gl->es)
goto cleanup;
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,
};
if (probing && ctx->gl->es && (vo_flags & VOFLAG_NO_GLES)) {
MP_VERBOSE(ctx->vo, "Skipping GLES backend.\n");
goto cleanup;
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;
}
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;
if (!gl->version && !gl->es)
return false;
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;
}
ctx->gl->debug_context = !!(vo_flags & VOFLAG_GL_DEBUG);
gl->debug_context = ctx->opts.debug;
gl->get_native_display_ctx = p;
gl->get_native_display = get_native_display;
ctx->gl->get_native_display_ctx = ctx;
ctx->gl->get_native_display = get_native_display;
return ctx;
cleanup:
mpgl_uninit(ctx);
return NULL;
}
// 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)
{
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);
if (gl->SwapInterval) {
gl->SwapInterval(p->opts->swapinterval);
} else {
MP_VERBOSE(p, "GL_*_swap_control extension missing.\n");
}
return ctx;
ctx->ra = ra_create_gl(p->gl, ctx->log);
return !!ctx->ra;
}
int mpgl_reconfig_window(struct MPGLContext *ctx)
void ra_gl_ctx_resize(struct ra_swapchain *sw, int w, int h, int fbo)
{
return ctx->driver->reconfig(ctx);
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;
if (p->wrapped_fb)
ra_tex_free(sw->ctx->ra, &p->wrapped_fb);
p->main_fb = fbo;
p->wrapped_fb = ra_create_wrapped_fb(sw->ctx->ra, fbo, w, h);
}
int mpgl_control(struct MPGLContext *ctx, int *events, int request, void *arg)
int ra_gl_ctx_color_depth(struct ra_swapchain *sw)
{
return ctx->driver->control(ctx, events, request, arg);
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;
}
void mpgl_start_frame(struct MPGLContext *ctx)
struct mp_image *ra_gl_ctx_screenshot(struct ra_swapchain *sw)
{
if (ctx->driver->start_frame)
ctx->driver->start_frame(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;
}
void mpgl_swap_buffers(struct MPGLContext *ctx)
struct ra_tex *ra_gl_ctx_start_frame(struct ra_swapchain *sw)
{
ctx->driver->swap_buffers(ctx);
struct priv *p = sw->priv;
return p->wrapped_fb;
}
void mpgl_uninit(MPGLContext *ctx)
bool ra_gl_ctx_submit_frame(struct ra_swapchain *sw, const struct vo_frame *frame)
{
if (ctx)
ctx->driver->uninit(ctx);
talloc_free(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;
}
static void check_pattern(struct priv *p, int item)
{
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 ra_gl_ctx_swap_buffers(struct ra_swapchain *sw)
{
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,
};

View File

@ -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;
// 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);
// A windowing backend (like X11, win32, ...), which provides OpenGL rendering.
struct mpgl_driver {
const char *name;
// 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)
// Size of the struct allocated for MPGLContext.priv
int priv_size;
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);
// 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);
// 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;
// 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);
// 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;
// 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.
// 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;
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);
// framebuffer to render to (normally 0)
GLuint main_fb;
// 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);
// 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);
struct m_option;
int mpgl_validate_backend_opt(struct mp_log *log, const struct m_option *opt,
struct bstr name, struct bstr param);
#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);

View File

@ -188,4 +188,4 @@ const struct mpgl_driver mpgl_driver_cocoa = {
.swap_buffers = cocoa_swap_buffers,
.control = cocoa_control,
.uninit = cocoa_uninit,
};
};

View File

@ -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,140 +236,7 @@ static void acquire_vt(void *data)
crtc_setup(ctx);
}
static void drm_egl_uninit(MPGLContext *ctx)
{
struct priv *p = ctx->priv;
crtc_release(ctx);
if (p->vt_switcher_active)
vt_switcher_destroy(&p->vt_switcher);
eglMakeCurrent(p->egl.display, EGL_NO_SURFACE, EGL_NO_SURFACE,
EGL_NO_CONTEXT);
eglDestroyContext(p->egl.display, p->egl.context);
eglDestroySurface(p->egl.display, p->egl.surface);
gbm_surface_destroy(p->gbm.surface);
eglTerminate(p->egl.display);
gbm_device_destroy(p->gbm.device);
p->egl.context = EGL_NO_CONTEXT;
eglDestroyContext(p->egl.display, p->egl.context);
if (p->kms) {
kms_destroy(p->kms);
p->kms = 0;
}
}
static int drm_egl_init(struct MPGLContext *ctx, int flags)
{
if (ctx->vo->probing) {
MP_VERBOSE(ctx->vo, "DRM EGL backend can be activated only manually.\n");
return -1;
}
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;
p->ev.version = DRM_EVENT_CONTEXT_VERSION;
p->ev.page_flip_handler = page_flipped;
p->vt_switcher_active = vt_switcher_init(&p->vt_switcher, ctx->vo->log);
if (p->vt_switcher_active) {
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_VERBOSE(ctx->vo, "Initializing KMS\n");
p->kms = kms_create(ctx->vo->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;
}
if (!init_gbm(ctx)) {
MP_ERR(ctx->vo, "Failed to setup GBM.\n");
return -1;
}
if (!init_egl(ctx, flags)) {
MP_ERR(ctx->vo, "Failed to setup EGL.\n");
return -1;
}
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;
}
mpegl_load_functions(ctx->gl, ctx->vo->log);
ctx->native_display_type = "drm";
ctx->native_display = (void *)(intptr_t)p->kms->fd;
// required by gbm_surface_lock_front_buffer
eglSwapBuffers(p->egl.display, p->egl.surface);
MP_VERBOSE(ctx->vo, "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;
}
update_framebuffer_from_bo(ctx, p->gbm.bo);
if (!p->fb.id) {
MP_ERR(ctx->vo, "Failed to create framebuffer.\n");
return -1;
}
if (!crtc_setup(ctx)) {
MP_ERR(ctx->vo, "Failed to set CRTC for connector %u: %s\n",
p->kms->connector->connector_id, mp_strerror(errno));
return -1;
}
return 0;
}
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);
}
static int drm_egl_reconfig(struct MPGLContext *ctx)
{
struct priv *p = ctx->priv;
ctx->vo->dwidth = p->fb.width;
ctx->vo->dheight = p->fb.height;
return 0;
}
static int drm_egl_control(struct MPGLContext *ctx, int *events, int request,
void *arg)
{
struct priv *p = ctx->priv;
switch (request) {
case VOCTRL_GET_DISPLAY_FPS: {
double fps = kms_get_display_fps(p->kms);
if (fps <= 0)
break;
*(double*)arg = fps;
return VO_TRUE;
}
}
return VO_NOTIMPL;
}
static void drm_egl_swap_buffers(MPGLContext *ctx)
static void drm_egl_swap_buffers(struct ra_ctx *ctx)
{
struct priv *p = ctx->priv;
eglSwapBuffers(p->egl.display, p->egl.surface);
@ -396,22 +265,137 @@ static void drm_egl_swap_buffers(MPGLContext *ctx)
p->gbm.bo = p->gbm.next_bo;
}
const struct mpgl_driver mpgl_driver_drm = {
.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,
};
static void drm_egl_uninit(struct ra_ctx *ctx)
{
struct priv *p = ctx->priv;
ra_gl_ctx_uninit(ctx);
const struct mpgl_driver mpgl_driver_drm_egl = {
.name = "drm-egl",
.priv_size = sizeof(struct priv),
.init = drm_egl_init_deprecated,
crtc_release(ctx);
if (p->vt_switcher_active)
vt_switcher_destroy(&p->vt_switcher);
eglMakeCurrent(p->egl.display, EGL_NO_SURFACE, EGL_NO_SURFACE,
EGL_NO_CONTEXT);
eglDestroyContext(p->egl.display, p->egl.context);
eglDestroySurface(p->egl.display, p->egl.surface);
gbm_surface_destroy(p->gbm.surface);
eglTerminate(p->egl.display);
gbm_device_destroy(p->gbm.device);
p->egl.context = EGL_NO_CONTEXT;
eglDestroyContext(p->egl.display, p->egl.context);
if (p->kms) {
kms_destroy(p->kms);
p->kms = 0;
}
}
static bool drm_egl_init(struct ra_ctx *ctx)
{
if (ctx->opts.probing) {
MP_VERBOSE(ctx, "DRM EGL backend can be activated only manually.\n");
return 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;
p->vt_switcher_active = vt_switcher_init(&p->vt_switcher, ctx->vo->log);
if (p->vt_switcher_active) {
vt_switcher_acquire(&p->vt_switcher, acquire_vt, ctx);
vt_switcher_release(&p->vt_switcher, release_vt, ctx);
} else {
MP_WARN(ctx, "Failed to set up VT switcher. Terminal switching will be unavailable.\n");
}
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 false;
}
if (!init_gbm(ctx)) {
MP_ERR(ctx->vo, "Failed to setup GBM.\n");
return false;
}
if (!init_egl(ctx)) {
MP_ERR(ctx->vo, "Failed to setup EGL.\n");
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 false;
}
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, "Preparing framebuffer\n");
p->gbm.bo = gbm_surface_lock_front_buffer(p->gbm.surface);
if (!p->gbm.bo) {
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, "Failed to create framebuffer.\n");
return false;
}
if (!crtc_setup(ctx)) {
MP_ERR(ctx, "Failed to set CRTC for connector %u: %s\n",
p->kms->connector->connector_id, mp_strerror(errno));
return false;
}
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;
return true;
}
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;
ra_gl_ctx_resize(ctx->swapchain, p->fb.width, p->fb.height, 0);
return true;
}
static int drm_egl_control(struct ra_ctx *ctx, int *events, int request,
void *arg)
{
struct priv *p = ctx->priv;
switch (request) {
case VOCTRL_GET_DISPLAY_FPS: {
double fps = kms_get_display_fps(p->kms);
if (fps <= 0)
break;
*(double*)arg = fps;
return VO_TRUE;
}
}
return VO_NOTIMPL;
}
const struct ra_ctx_fns ra_ctx_drm_egl = {
.type = "opengl",
.name = "drm",
.reconfig = drm_egl_reconfig,
.swap_buffers = drm_egl_swap_buffers,
.control = drm_egl_control,
.init = drm_egl_init,
.uninit = drm_egl_uninit,
};

View File

@ -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)
{
ra_gl_ctx_resize(ctx->swapchain, ctx->vo->dwidth, ctx->vo->dheight, 0);
}
static bool glx_reconfig(struct ra_ctx *ctx)
{
vo_x11_config_vo_window(ctx->vo);
return 0;
resize(ctx);
return true;
}
static int glx_control(struct MPGLContext *ctx, int *events, int request,
void *arg)
static int glx_control(struct ra_ctx *ctx, int *events, int request, void *arg)
{
return vo_x11_control(ctx->vo, events, request, arg);
int ret = vo_x11_control(ctx->vo, events, request, arg);
if (*events & VO_EVENT_RESIZE)
resize(ctx);
return ret;
}
static void glx_swap_buffers(struct MPGLContext *ctx)
{
glXSwapBuffers(ctx->vo->x11->display, ctx->vo->x11->window);
}
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,
};

View File

@ -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,
};
return 0;
if (!ra_gl_ctx_init(ctx, &p->gl, params))
goto fail;
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,
};

View File

@ -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,
};
};

View File

@ -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 glx_uninit(MPGLContext *ctx)
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 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)
{
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;
}
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 bool vdpau_submit_frame(struct ra_swapchain *sw,
const struct vo_frame *frame)
{
struct priv *p = sw->ctx->priv;
GL *gl = &p->gl;
struct vdp_functions *vdp = &p->vdp->vdp;
VdpStatus vdp_st;
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");
p->idx_surfaces = (p->idx_surfaces + 1) % p->num_surfaces;
return ra_gl_ctx_submit_frame(sw, frame) && vdp_st == VDP_STATUS_OK;
}
static bool vdpau_reconfig(struct ra_ctx *ctx)
{
vo_x11_config_vo_window(ctx->vo);
return 0;
return true;
}
static int glx_control(struct MPGLContext *ctx, int *events, int request,
void *arg)
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_start_frame(struct MPGLContext *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) {
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);
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;
}
}
static void glx_swap_buffers(struct MPGLContext *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");
p->current_surface = (p->current_surface + 1) % p->num_surfaces;
}
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 = {
.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,
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",
.reconfig = vdpau_reconfig,
.control = vdpau_control,
.wakeup = vdpau_wakeup,
.wait_events = vdpau_wait_events,
.init = vdpau_init,
.uninit = vdpau_uninit,
};

View File

@ -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,
};
return 0;
if (!ra_gl_ctx_init(ctx, gl, params))
return false;
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,
};

View File

@ -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)
{
ra_gl_ctx_resize(ctx->swapchain, ctx->vo->dwidth, ctx->vo->dheight, 0);
}
static bool mpegl_reconfig(struct ra_ctx *ctx)
{
vo_x11_config_vo_window(ctx->vo);
return 0;
resize(ctx);
return true;
}
static int mpegl_control(struct MPGLContext *ctx, int *events, int request,
static int mpegl_control(struct ra_ctx *ctx, int *events, int request,
void *arg)
{
return vo_x11_control(ctx->vo, events, request, arg);
int ret = vo_x11_control(ctx->vo, events, request, arg);
if (*events & VO_EVENT_RESIZE)
resize(ctx);
return ret;
}
static void mpegl_swap_buffers(MPGLContext *ctx)
{
struct priv *p = ctx->priv;
eglSwapBuffers(p->egl_display, p->egl_surface);
}
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,
};

View File

@ -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;
int msgl = ctx->opts.probing ? MSGL_V : MSGL_FATAL;
EGLenum api = EGL_OPENGL_API;
EGLint rend = EGL_OPENGL_BIT;
const char *name = "Desktop OpenGL";
if (es_version == 2) {
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))
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;
}
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))
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;
}

View File

@ -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);

View File

@ -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

View File

@ -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;
}

View File

@ -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

View File

@ -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;

View File

@ -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;

View File

@ -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;

View File

@ -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"

View File

@ -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"

View File

@ -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;

View File

@ -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"

View File

@ -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,
};

View File

@ -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,

View File

@ -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;
}
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 to size %d\n", 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 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,
};
}
// 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};
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";
}
}
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)
void gl_check_error(GL *gl, struct mp_log *log, const char *info)
{
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;
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));
}
*pool = (struct timer_pool){ .ra = ra, .timer = timer };
return pool;
}
void timer_pool_destroy(struct timer_pool *pool)
static int get_alignment(int stride)
{
if (!pool)
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;
pool->ra->fns->timer_destroy(pool->ra, pool->timer);
talloc_free(pool);
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);
}
void timer_pool_start(struct timer_pool *pool)
mp_image_t *gl_read_fbo_contents(GL *gl, int fbo, int w, int h)
{
if (!pool)
return;
assert(!pool->running);
pool->ra->fns->timer_start(pool->ra, pool->timer);
pool->running = true;
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;
}
void timer_pool_stop(struct timer_pool *pool)
static void gl_vao_enable_attribs(struct gl_vao *vao)
{
if (!pool)
return;
GL *gl = vao->gl;
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;
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);
}
}
struct mp_pass_perf timer_pool_measure(struct timer_pool *pool)
void gl_vao_init(struct gl_vao *vao, GL *gl, int stride,
const struct ra_renderpass_input *entries,
int num_entries)
{
if (!pool)
return (struct mp_pass_perf){0};
assert(!vao->vao);
assert(!vao->buffer);
struct mp_pass_perf res = {
.peak = pool->peak,
.count = pool->sample_count,
*vao = (struct gl_vao){
.gl = gl,
.stride = stride,
.entries = entries,
.num_entries = num_entries,
};
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++];
}
gl->GenBuffers(1, &vao->buffer);
if (res.count > 0) {
res.last = res.samples[res.count - 1];
res.avg = pool->sum / res.count;
}
if (gl->BindVertexArray) {
gl->BindBuffer(GL_ARRAY_BUFFER, vao->buffer);
return res;
gl->GenVertexArrays(1, &vao->vao);
gl->BindVertexArray(vao->vao);
gl_vao_enable_attribs(vao);
gl->BindVertexArray(0);
gl->BindBuffer(GL_ARRAY_BUFFER, 0);
}
}
void mp_log_source(struct mp_log *log, int lev, const char *src)
void gl_vao_uninit(struct gl_vao *vao)
{
int line = 1;
if (!src)
GL *gl = vao->gl;
if (!gl)
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->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);
}

View File

@ -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];
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;
};
static const struct gl_transform identity_trans = {
.m = {{1.0, 0.0}, {0.0, 1.0}},
.t = {0.0, 0.0},
};
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_transform_ortho(struct gl_transform *t, float x0, float x1,
float y0, float y1);
void gl_set_debug_logger(GL *gl, struct mp_log *log);
// 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);
#endif

View File

@ -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

View File

@ -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"
#include "gpu/context.h"
#include "gpu/hwdec.h"
#include "gpu/video.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];
};
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;
struct gpu_priv *p = vo->priv;
struct ra_swapchain *sw = p->ctx->swapchain;
mpgl_start_frame(p->glctx);
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;
struct ra_ctx_opts opts = p->opts;
opts.want_alpha = alpha_mode == 1;
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)
p->ctx = ra_ctx_create(vo, p->context_type, p->context_name, opts);
if (!p->ctx)
goto err_out;
p->gl = p->glctx->gl;
assert(p->ctx->ra);
assert(p->ctx->swapchain);
struct ra_swapchain *sw = p->ctx->swapchain;
if (p->gl->SwapInterval) {
p->gl->SwapInterval(p->opts.swap_interval);
} else {
MP_VERBOSE(vo, "swap_control extension missing.\n");
}
p->ra = ra_create_gl(p->gl, vo->log);
if (!p->ra)
goto err_out;
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,11 +322,23 @@ 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}
};
const struct vo_driver video_out_opengl = {
.description = "Extended OpenGL Renderer",
.name = "opengl",
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,
@ -445,26 +350,36 @@ 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,
};
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 = "Shader-based GPU Renderer",
.name = "opengl",
.caps = VO_CAP_ROTATE90,
.preinit = preinit_opengl,
.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,
};

View File

@ -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;
}

View File

@ -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;

13
wscript
View File

@ -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',

View File

@ -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" ),