Multithreading with Metal backend?

156 views
Skip to first unread message

David Holtkamp

unread,
Mar 19, 2025, 10:27:20 AMMar 19
to angleproject
I have an iOS/Mac game that I am trying to use Angle on. iOS currently simply uses OpenGL ES 3.0 and the multi-threading works great. The background thread just generates preview images, so no share context is used or needed.

On Mac, I am moving to Angle. It works great if I do the preview generation in the foreground thread, but as soon as I let it do multi-threaded, it becomes unstable. The crash sometimes occurs in the foreground thread, and sometimes the background. I see a couple errors before the crash too, but not consistently

-[IOGPUMetalCommandBuffer validate]:214: failed assertion `commit command buffer with uncommitted encoder'

UNSUPPORTED (log once): POSSIBLE ISSUE: unit 0 GLD_TEXTURE_INDEX_2D is unloadable and bound to sampler type (Float) - using zero texture because texture unloadable


Here is how I am creating the context in the background thread:

   EGLDisplay display = eglGetDisplay(EGL_DEFAULT_DISPLAY);

        if (display == EGL_NO_DISPLAY) {

            CMLogMessage("Failure to get EGL display");

            return;

        }


        EGLint major, minor;

        if (!eglInitialize(display, &major, &minor)) {

            CMLogMessage("Failure to initialize EGL");

            return;

        }


        // Step 2: Choose EGL configuration

        EGLint attribs[] = {

            EGL_RENDERABLE_TYPE, EGL_OPENGL_ES3_BIT,

            EGL_SURFACE_TYPE, EGL_PBUFFER_BIT,

            EGL_RED_SIZE, 8,

            EGL_GREEN_SIZE, 8,

            EGL_BLUE_SIZE, 8,

            EGL_ALPHA_SIZE, 8,

            EGL_DEPTH_SIZE, 24,

            EGL_STENCIL_SIZE, 8,

            EGL_NONE

        };


        EGLConfig config;

        EGLint numConfigs;

        if (!eglChooseConfig(display, attribs, &config, 1, &numConfigs) || numConfigs == 0) {

            CMLogMessage("Failure to choose EGL config");

            eglTerminate(display);

            return;

        }


        // Step 3: Create EGL context

        EGLint contextAttribs[] = {

            EGL_CONTEXT_CLIENT_VERSION, 3, // OpenGL ES 3.0

            EGL_NONE

        };


        EGLContext context = eglCreateContext(display, config, EGL_NO_CONTEXT, contextAttribs);

        if (context == EGL_NO_CONTEXT) {

            CMLogMessage("Failure to create EGL context");

            eglTerminate(display);

            return;

        }


        // Step 4: Create a pbuffer surface for offscreen rendering

        EGLint pbufferAttribs[] = {

            EGL_WIDTH, 1,

            EGL_HEIGHT, 1,

            EGL_NONE

        };


        EGLSurface surface = eglCreatePbufferSurface(display, config, pbufferAttribs);

        if (surface == EGL_NO_SURFACE) {

            CMLogMessage("Failure to create EGL pbuffer surface");

            eglDestroyContext(display, context);

            eglTerminate(display);

            return;

        }


        // Step 5: Make the context current

        if (!eglMakeCurrent(display, surface, surface, context)) {

            CMLogMessage("Failure to make EGL context current");

            eglDestroySurface(display, surface);

            eglDestroyContext(display, context);

            eglTerminate(display);

            return;

        }


Is there some extra step needed to start a second context? Do I have to use a share group even though I don't need it?

Thanks!
David


Shahbaz Youssefi

unread,
Mar 19, 2025, 11:11:22 AMMar 19
to angleproject
Hi,

The only backend of ANGLE that is thread safe is the Vulkan backend. I'm afraid there's no plan to support multithreading in the other backends.

Cheers,

David Holtkamp

unread,
Mar 19, 2025, 11:43:33 AMMar 19
to angleproject
Hi, thank you for the reply. Just for clarification, even without the use of share groups with no correlated assets, there is no way to do this? Is this simply because there are global states being stored within the metal implementation? It seems like if I could simply break it into different processes it would work fine, but because its got shared memory it cannot?

Shahbaz Youssefi

unread,
Mar 19, 2025, 1:18:44 PMMar 19
to angleproject
Yeap, that's it. FWIW, you probably don't need multiple processes, I think technically having multiple EGL _displays_ should work, the "global" state is really global under the Display. But I've never tried such a thing myself, you can certainly give it a try, create one display per thread and see what happens!

Yulong He

unread,
Jul 28, 2025, 11:10:10 PMJul 28
to angleproject

Hi, I encountered the same issue when using multi-threading with D3D11 backend. The cause was the same: multiple threads acquired the same EGLDisplay, and the underlying of EGLDisplay corresponded to the same StateManager (when switching threads, the current context did not correspond to the new thread, causing the issue). I tried commenting out the following code, which resolved the issue, but I'm unsure of the potential implications. Would this affect off-screen rendering and on-screen rendering differently?

image.png

Additionally, I'd like to ask: is the Vulkan backend thread-safe because it has been specially optimized at the lower level (or does not use a global StateManager/StateCache) to support multithreading?

Shahbaz Youssefi

unread,
Jul 28, 2025, 11:13:18 PMJul 28
to angleproject
That wouldn't be enough to fix the threading issues. I mean, if you end up creating multiple displays with the above change, your multiple threads may work, but that wouldn't be multithreaded OpenGL as the threads cannot share resources. It would be more like having multiple processes with their own OpenGL displays; certainly something you could do with multiple processes today.

As for the Vulkan backend, it's thread safe because it's shared objects (equivalent to "StateManager") are protected by locks.

Yulong He

unread,
Jul 29, 2025, 9:59:08 AMJul 29
to angleproject
BTW, are there alternative methods to get a different EGLDisplay?

Yulong He

unread,
Jul 29, 2025, 9:59:25 AMJul 29
to angleproject
Yeah, no shared resources right now.

However, putting aside the issue of shared resources, since EGLDisplay is associated with the graphics display device, I'm more concerned about whether continuously creating EGLDisplay instances (though at most five or ten) might cause any problems in off-screen or on-screen rendering scenarios.  

Thanks for your reply, and your expertise on this matter would be invaluable.
On Tuesday, July 29, 2025 at 11:13:18 AM UTC+8 syou...@chromium.org wrote:

Shahbaz Youssefi

unread,
Jul 29, 2025, 10:02:17 AMJul 29
to angleproject
In theory it shouldn't, you already have numerous windows in your OS each interacting with the graphics device. But not the way you've hacked in ANGLE, that is untested and anything could go wrong.

If you need multiple displays that do completely unrelated things, you might as well create multiple processes instead of multiple threads. Threads are really just processes that have their address spaces shared by default, you can always share memory, synchronization primitives etc between the processes using OS primitives to make them similar to threads for your purpose.

Reply all
Reply to author
Forward
0 new messages