mirror of
https://github.com/mpv-player/mpv
synced 2025-04-20 22:26:30 +00:00
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:
parent
20f958c977
commit
65979986a9
@ -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
|
||||
|
@ -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
|
||||
|
@ -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.
|
||||
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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
186
video/out/gpu/context.c
Normal 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
95
video/out/gpu/context.h
Normal 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);
|
@ -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);
|
@ -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;
|
@ -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
372
video/out/gpu/utils.c
Normal 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, ¶ms);
|
||||
|
||||
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
120
video/out/gpu/utils.h
Normal 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);
|
@ -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);
|
||||
}
|
@ -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.
|
@ -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"
|
||||
|
||||
|
@ -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,
|
||||
};
|
||||
|
@ -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);
|
||||
|
@ -188,4 +188,4 @@ const struct mpgl_driver mpgl_driver_cocoa = {
|
||||
.swap_buffers = cocoa_swap_buffers,
|
||||
.control = cocoa_control,
|
||||
.uninit = cocoa_uninit,
|
||||
};
|
||||
};
|
||||
|
@ -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,
|
||||
};
|
||||
|
@ -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,
|
||||
};
|
@ -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,
|
||||
};
|
||||
|
@ -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,
|
||||
};
|
||||
};
|
||||
|
@ -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,
|
||||
};
|
||||
|
@ -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,
|
||||
};
|
||||
|
@ -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,
|
||||
};
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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);
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
}
|
@ -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
|
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -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"
|
||||
|
||||
|
@ -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"
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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"
|
||||
|
@ -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,
|
||||
};
|
||||
|
@ -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,
|
||||
|
@ -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, ¶ms);
|
||||
|
||||
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);
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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,
|
||||
};
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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
13
wscript
@ -783,15 +783,20 @@ video_output_features = [
|
||||
),
|
||||
}, {
|
||||
'name': '--gl',
|
||||
'desc': 'OpenGL video outputs',
|
||||
'desc': 'OpenGL context support',
|
||||
'deps': 'gl-cocoa || gl-x11 || egl-x11 || egl-drm || '
|
||||
+ 'gl-win32 || gl-wayland || rpi || mali-fbdev || '
|
||||
+ 'plain-gl',
|
||||
'func': check_true,
|
||||
}, {
|
||||
'name': '--gpu',
|
||||
'desc': 'GPU-accelerated video output support',
|
||||
'deps': 'gl',
|
||||
'func': check_true,
|
||||
'req': True,
|
||||
'fmsg': "No OpenGL video output found or enabled. " +
|
||||
"Aborting. If you really mean to compile without OpenGL " +
|
||||
"video outputs use --disable-gl."
|
||||
'fmsg': "No GPU context found or enabled. Aborting. " +
|
||||
"If you really mean to compile without support for " +
|
||||
"`--vo=gpu`, then use --disable-gpu."
|
||||
}, {
|
||||
'name': 'egl-helpers',
|
||||
'desc': 'EGL helper functions',
|
||||
|
@ -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" ),
|
||||
|
Loading…
Reference in New Issue
Block a user