Skip to content

vo_opengl/angle_dynamic: accept EGL_-prefixed exports - #18325

Open
unl0cks wants to merge 1 commit into
mpv-player:masterfrom
unl0cks:angle-prefixed-exports
Open

vo_opengl/angle_dynamic: accept EGL_-prefixed exports#18325
unl0cks wants to merge 1 commit into
mpv-player:masterfrom
unl0cks:angle-prefixed-exports

Conversation

@unl0cks

@unl0cks unl0cks commented Jul 30, 2026

Copy link
Copy Markdown

angle_load() resolves the EGL entry points with GetProcAddress() using their unprefixed names, and gives up on the first one that is missing:

#define ANGLE_LOAD_ENTRY(NAME, VAR) \
    NAME = (void *)GetProcAddress(angle_dll, #NAME); \
    if (!NAME) return;

Some ANGLE builds export those entry points under the EGL_-prefixed spelling instead — EGL_QueryString rather than eglQueryString — which is what Chromium uses, and which applications embedding such a build inherit.

On those builds angle_load() returns false, so every ANGLE-based hwdec is refused at its second gate, before any device, capability or extension is examined. hwdec_d3d11egl.c and hwdec_dxva2egl.c both start with:

if (!ra_is_gl(hw->ra_ctx->ra))
    return -1;
if (!angle_load())          // <- here
    return -1;

Because every gate in those functions is a bare return -1, mpv logs only:

Loading hwdec driver 'd3d11-egl'
Loading failed.

which is hard to tell apart from a machine that genuinely lacks the interop. That is what made this expensive to diagnose — it took a local build with a log line at each gate to find out which one it was.

How it was found

Embedding libmpv in an Avalonia application on Windows. Avalonia ships ANGLE as a single combined binary that exports only the prefixed names, so eglGetCurrentDisplay, eglGetCurrentContext and eglQueryString are all absent under the names angle_load() looks up, while EGL_GetCurrentDisplay, EGL_GetCurrentContext and EGL_QueryString are present.

Measured on ANGLE 2.1.1 (git 1c89805903c1), Windows 11, RTX 3080, with libmpv's OpenGL render API on an ES 3.0 context.

Result

With this change, plus the EGL_EXT_device_query fix from #10212 (that extension is a client extension queried against EGL_NO_DISPLAY, and hwdec_d3d11egl.c tests the display string), d3d11-egl initialises and hwdec goes from d3d11va-copy to d3d11va on that setup. Frame render time in the host dropped from ~14–19 ms to ~0.7 ms at 4K HEVC, since the per-frame GPU→RAM→GPU round trip is gone.

The two are independent and both are needed — either alone still ends in d3d11va-copy. This PR is only the angle_load() half, since #10212 already has #11142 open against it.

Cost

One extra GetProcAddress() per entry point on builds that export the plain names, once per process, and nothing at all on the builds that need it. Behaviour is unchanged wherever the plain names resolve.

<string.h> is added explicitly for strlen/memcpy — it currently arrives only transitively via windows.h on MinGW.

Compile-tested on MSYS2 MINGW64 (gcc 16.1.0, meson 1.11.2) and runtime-tested as described above.

angle_load() resolves the EGL entry points with GetProcAddress() using
their unprefixed names, and gives up on the first one that is missing.
Some ANGLE builds export those entry points under the EGL_-prefixed
spelling instead - EGL_QueryString rather than eglQueryString - which is
what Chromium uses, and which applications embedding such a build
inherit.

On those builds angle_load() returns false, so every ANGLE-based hwdec
(d3d11-egl, dxva2-egl) is refused at its second gate, before any device,
capability or extension is examined. mpv logs only "Loading failed.",
which is hard to distinguish from a machine that genuinely lacks the
interop.

Try the prefixed spelling as a fallback. This costs one extra
GetProcAddress() per entry point on builds that export the plain names,
and nothing on the builds that need it.

Found while embedding libmpv in an Avalonia application on Windows.
Avalonia ships ANGLE as a single combined binary that exports only the
prefixed names, so eglGetCurrentDisplay, eglGetCurrentContext and
eglQueryString were all absent under the names angle_load() looks up.
With this change, and with the EGL_EXT_device_query fix from issue
 mpv-player#10212, hwdec on that setup goes from d3d11va-copy to d3d11va.
@sfan5

sfan5 commented Jul 30, 2026

Copy link
Copy Markdown
Member

The actual interesting question here is why do "some"(?) ANGLE DLLs export their symbols under different names?

@CounterPillow

Copy link
Copy Markdown
Contributor

these names are defined by the standard: https://registry.khronos.org/EGL/sdk/docs/man/

I'm suspecting GetProcAddress onto the DLL simply isn't the correct way to load them, see e.g. eglGetProcAddress.

@kasper93

Copy link
Copy Markdown
Member

these names are defined by the standard: https://registry.khronos.org/EGL/sdk/docs/man/

I'm suspecting GetProcAddress onto the DLL simply isn't the correct way to load them, see e.g. eglGetProcAddress.

Correct. We should be going through eglGetProcAddress entry point, not loading those functions directly.

@unl0cks

unl0cks commented Jul 31, 2026

Copy link
Copy Markdown
Author

The actual interesting question here is why do "some"(?) ANGLE DLLs export their symbols under different names?

I was wrong about that, sorry. Should've phrased it better.

The DLL here is ANGLE's libGLESv2, not libEGL. I dumped the exports from the one Avalonia ships (av_libglesv2.dll, ANGLE 2.1.1, git 1c89805903c1):

EGL_*  : 115
egl*   : 0
gl*    : 829

The normal egl* names live in ANGLE's separate libEGL.dll, which forwards to these. Avalonia only ships the combined libGLESv2 and no forwarder, and my app is what puts it in front of mpv under the filename libEGL.dll. So angle_load() opens a DLL that never had those exports to begin with. Nothing weird about the ANGLE build, the deployment is just incomplete. So this is probably my bug and not mpv's.

I'm suspecting GetProcAddress onto the DLL simply isn't the correct way to load them
Correct. We should be going through eglGetProcAddress entry point, not loading those functions directly.

Yeah, my bad. I tested it. Same DLL, all 22 entry points from ANGLE_FNS, resolved both ways:

GetProcAddress("eglGetProcAddress") = absent
GetProcAddress("EGL_GetProcAddress") = FOUND

of 22 core entry points:
  resolvable via eglGetProcAddress : 22
  resolvable via GetProcAddress    : 0

eglGetProcAddress gets all of them, core EGL 1.x included. The display advertises EGL_KHR_get_all_proc_addresses and EGL_KHR_client_get_all_proc_addresses, which is what makes that allowed.

However if you do that, you can't get eglGetProcAddress from eglGetProcAddress, and that's the one symbol that actually is missing under its normal name here. So you'd still need a GetProcAddress fallback just for that bootstrap. The other 21 come free after it.

I can redo it so it bootstraps eglGetProcAddress first, then uses that to resolve everything else. That’s probably the cleaner and more correct approach anyway, and it removes the dependency on exported function names entirely.

Or close it if you want, that's fine too. If the take is that a host should ship a proper ANGLE deployment instead of one library renamed to another's filename, that's fair and I'll fix it on my end. Either way the export dump seemed worth leaving here. I didn't work out the why myself until I looked at the table.

@kasper93

kasper93 commented Jul 31, 2026

Copy link
Copy Markdown
Member

these names are defined by the standard: https://registry.khronos.org/EGL/sdk/docs/man/

I'm suspecting GetProcAddress onto the DLL simply isn't the correct way to load them, see e.g. eglGetProcAddress.

GetProcAddress on the DLL is the correct way for these. The list contains only EGL <= 1.4 core functions, which libEGL.dll exports as its normal linkable ABI. And we already depend on this, because by default we link this library during build. Extension entry points are already resolved through eglGetProcAddress where needed.

The DLL here is ANGLE's libGLESv2, not libEGL.

No it's not. mpv loads libEGL explicitly.

HANDLE angle_dll = LoadLibraryW(L"LIBEGL.DLL");

Avalonia only ships the combined libGLESv2 and no forwarder, and my app is what puts it in front of mpv under the filename libEGL.dll.

So, why do you ever though it's an mpv issue to be fixed here? mpv links to libEGL.dll and uses entrypoints defined by the https://registry.khronos.org/EGL/sdk/docs/man/. Everything is correct. If you want to use GLES library, it's another story.

Yeah, that's right. I tested it instead of guessing.

You are welcome to provide any patches you want, but you have to ensure they meet some quality standard. I will not spend time prompting your LLM. It's not my job as a reviewer/maintainer to do the work for you. If you are unable to provide or even recognize that your patch is a hack and nothing more, it's not our job to educate you.

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants