EGL-Registry icon indicating copy to clipboard operation
EGL-Registry copied to clipboard

Add EGL_EXT_display_alloc extension

Open kbrenneman opened this issue 2 years ago • 55 comments

This is an EGL extension that allows you to allocate private EGLDisplays and destroy them when they're no longer needed.

The default semantics of eglGetPlatformDisplay have a few inherent problems:

First, if two modules (applications, libraries, etc) both try to use EGL internally, then they have to sharing the same EGLDisplay, and they risk clobbering each other. EGL_KHR_display_reference can at least make the problem less catastrophic when something calls eglTerminate, but it's not a complete solution -- an extra call to eglTerminate would still break everything, and you can't rely on using eglTerminate to clean up any remaining display-owned objects.

Likewise, the fact that EGLDisplay handles stick around forever means that it's literally impossible to write an EGL application (or implementation) that doesn't leak memory.

Perhaps most importantly, the default semantics make no sense at all in X11 or Wayland applications, because the EGLDisplay will necessarily last longer than the display connection under it, leading to a dangling pointer.

So, this extension adds a new EGL_PRIVATE_DISPLAY_EXT attribute for eglGetPlatformDIsplay, which tells it to allocate a brand new EGLDisplay handle, even if another EGLDisplay would otherwise match the native display. Different modules can thus use their own private EGLDisplays without risk of clobbering each other.

It also adds a new eglDestroyDisplayEXT function, which will fully destroy an EGLDisplay handle that was created with EGL_PRIVATE_DISPLAY_EXT. That way, you can actually clean up properly, or deal with cases where a native display is no longer valid.

This would also make it possible to implement new client extensions such as EGL_KHR_display_reference entirely within libglvnd.

kbrenneman avatar Mar 25 '22 20:03 kbrenneman

https://www.khronos.org/registry/EGL/extensions/KHR/EGL_KHR_display_reference.txt - is not sufficient then? We had a lot of discussion at one time for that. Just curious, who is part of this EXT?

stonesthrow avatar Mar 28 '22 14:03 stonesthrow

EGL_KHR_display_reference is not sufficient, no. In terms of utility, it's effectively a strict subset of EGL_EXT_display_alloc.

With EGL_KHR_display_reference, you can have multiple libraries that call eglInitialize and eglTerminate independently without clobbering each other, which is arguably the worst problem with EGL's normal semantics. That also requires that you don't have any bugs that would lead to an extra eglTerminate call, and that each library cleans up every display-owned resource manually.

With EGL_EXT_display_alloc, multiple libraries can still call eglInitialize and eglTerminate independently because they would operate on separate EGLDisplay handles. If a library has an extra eglTerminate call, it doesn't matter. And a library could use eglTerminate to handle cleanup.

EGL_KHR_display_reference also doesn't fix the fact that everything that ever uses EGL has to leak memory, and it doesn't fix the dangling pointers when you call XCloseDisplay or wl_display_disconnect.

Perhaps most interestingly, EGL_EXT_display_alloc would make it possible to implement extensions like EGL_KHR_display_reference entirely within libglvnd. Libglvnd could implement reference tracking for an EGLDisplay internally pretty easily, but without EGL_EXT_display_alloc, it couldn't get separate EGLDisplay handles for different values of EGL_TRACK_REFERENCES_KHR.

As for the use of EXT, there's nothing hardware- or vendor-specific about this. It's just a change in a driver's bookkeeping, so any driver could implement it. I did float the general idea in a discussion a while ago with favorable response from someone on the Mesa side, though I don't remember who -- @nwnk maybe?

kbrenneman avatar Mar 28 '22 15:03 kbrenneman

Sorry my comment was not push back. I got a better read and I get it.

stonesthrow avatar Mar 28 '22 16:03 stonesthrow

Should I rebase this on top of #157, or just wait until #157 is merged and then rebase onto ToT?

kbrenneman avatar May 16 '22 15:05 kbrenneman

Not sure if rebasing on other work in progress has turned out without issue. Not sure.

stonesthrow avatar May 16 '22 17:05 stonesthrow

Okay. Easy enough to just wait for #156 to merge and then rebase on ToT, then.

kbrenneman avatar May 16 '22 18:05 kbrenneman

@kbrenneman you don't need to rebase, just please take out the generated files (eglext.h / index.php) to avoid conflicts. I will fix any XML conflicts (unlikely) and extension number issues prior to merging.

oddhack avatar May 19 '22 03:05 oddhack

Done.

kbrenneman avatar May 19 '22 15:05 kbrenneman

Rebased and renumbered. I also updated the issues section to say "Resolved" instead of "Proposed resolution."

kbrenneman avatar May 25 '22 17:05 kbrenneman

Please add other reviewers. Jon and I can says its well formed, but +1 from experts is reassuring.

stonesthrow avatar May 25 '22 17:05 stonesthrow

I was looking to implement similar functionality to this - I want to be able to force a new allocation of a display. I would want my one to be destroyed the normal way using eglTerminate, and interact with EGL_KHR_display_reference to have additional logic for deleting. I also do not fully understand the purpose of eglDestroyDisplayEXT - is this just to provide different behaviour depending on EGL_KHR_display_reference - if so, would it be more logical to only require it be included it when both extensions are present? Would this extension do what I want? Edit: ignore that bit about private, I can't read lol

hamarb123 avatar Jun 05 '22 15:06 hamarb123

In practice, I expect that most applications and libraries would use EGL_EXT_display_alloc instead of EGL_KHR_display_reference, not use the two of them together. The main purpose of EGL_KHR_display_reference is so that two libraries which end up getting the same EGLDisplay when they call eglGetPlatformDisplay won't clobber each other when one of them calls eglTerminate (assuming no bugs in either, of course). With EGL_EXT_display_alloc, you can simply not share an EGLDisplay in the first place.

The reason I added a separate eglDestroyDisplayEXT function is so that eglInitialize and eglTerminate would not have different behavior. Once you've got an EGLDisplay handle, calling eglInitialize and eglTerminate would do exactly the same thing regardless of whether you set EGL_PRIVATE_DISPLAY_EXT or not, including being able to reinitialize an EGLDisplay.

That also means that this extension is logically orthogonal to EGL_KHR_display_reference. This extension only cares about whether an EGLDisplay is initialized, not how many times you'd have to call eglTerminate to uninitialize it.

In addition, you can easily get the behavior that you want, too: Just call eglDestroyDisplayEXT immediately after eglInitialize. It'll mark the EGLDisplay for deletion, but since it treats an initialized EGLDisplay is "in use", it'll wait until eglTerminate to actually destroy it.

kbrenneman avatar Jun 05 '22 16:06 kbrenneman

Cool, sounds good. Just checking, the difference between eglDestroyDisplayEXT and eglTerminate is that it doesn't allow reviving the display? If so, that's exactly what I wanted I think. Thanks! Edit: out of curiosity, what codebase are you implementing this on? I was hoping to implement it on my fork of ANGLE.

hamarb123 avatar Jun 06 '22 01:06 hamarb123

Cool, sounds good. Just checking, the difference between eglDestroyDisplayEXT and eglTerminate is that it doesn't allow reviving the display? If so, that's exactly what I wanted I think. Thanks!

In effect, yeah. Every EGL function would do exactly the same thing that it does now. eglTerminate basically reverts the EGLDisplay to the same uninitialized state that it was in before you called eglInitialize. Trying to pass it to any other function would produce an EGL_NOT_INITIALIZED error, but the EGLDisplay handle still exists, so you can pass it to eglInitialize. If you call eglGetPlatformDisplay with EGL_PRIVATE_DISPLAY_EXT set to true, then it will not return that EGLDisplay handle.

When you call eglDestroyDisplayEXT, it destroys the EGLDisplay handle entirely, with the same semantics you'd expect out of eglDestroyContext. You can't reinitialize it because it no longer exists, and a call to eglGetPlatformDisplay is free to re-use that handle.

Edit: out of curiosity, what codebase are you implementing this on? I was hoping to implement it on my fork of ANGLE.

Libglvnd first, since that'll be needed for anything else to work. After that, the NVIDIA driver.

kbrenneman avatar Jun 06 '22 12:06 kbrenneman

I've started sketching this up for Mesa and it's looking pretty sane. I might have some questions about exactly what gets destroyed when but I think the discussion above has most of the answers. I'll link to a merge request once I've got it working but I think the spec gets a +1 from me.

nwnk avatar Jul 15 '22 21:07 nwnk

Mesa merge review is now up. @kbrenneman if you have any tests for this you could share that'd be cool. (I do not, yet, they'll be in piglit once I do though.)

nwnk avatar Jul 18 '22 18:07 nwnk

Any news about this ext?

emersion avatar Apr 07 '23 12:04 emersion

What should happen if I create 2 displays (A and B) using the same platform display (e.g. wl_display'). And then use EGLSurfacefrom displayAin aEGLContextfrom displayB`.

Do I understand correctly, that unshared means that I can't use anything from A with the B entirely even though, they have exactly the same platform display under the hood?

kchibisov avatar Apr 07 '23 13:04 kchibisov

Each EGLDisplay would have its own wl_display, and Wayland objects cannot be shared across two wl_displays.

emersion avatar Apr 07 '23 13:04 emersion

Each EGLDisplay would have its own wl_display, and Wayland objects cannot be shared across two wl_displays.

I don't think I understand with wl_display(you don't wl_display_connect you just get the handle from the user), but I think you've meant that they will have different queues under the hood and you can't mix the objects between the queues.

However on X11 where everything is a XID, I think you could be able to do so, given that.

If the EGL_PRIVATE_DISPLAY_EXT attribute is set to EGL_TRUE, then eglGetPlatformDisplay returns a new EGLDisplay handle, even if the platform, native_display, and attributes would match an existing display.

It would be nice to be more explicit here, so the libraries could do runtime asserts just in case.

kchibisov avatar Apr 07 '23 13:04 kchibisov

Oh, hm, right. You should be able to use wl_display objects from multiple queues… Assuming the per-queue wrappers are properly created.

emersion avatar Apr 07 '23 13:04 emersion

You can create two EGLDisplays from the same wl_display, but they're still separate EGLDisplays.

Remember, an EGLDisplay is the namespace that EGLContexts, EGLSurfaces, and other EGL objects belong to. Every other EGL object is only valid with the EGLDisplay that created it. So, if you try to call eglMakeCurrent with EGLDisplay A, but you pass it an EGLContext from EGLDisplay B, then that's an error.

That much is not new behavior. You can already create multiple EGLDisplays from the same native display just by passing different attributes to eglGetPlatformDisplay. I think Mesa will hand back different EGLDisplays even if the attribute lists are logically equivalent:

wl_display *wdpy = wl_display_connect(NULL);
EGLAttrib attribs1[] = [ EGL_TRACK_REFERENCES_KHR, EGL_TRUE, EGL_NONE ]
EGLDisplay dpy1 = eglGetPlatformDisplay(EGL_PLATFORM_WAYLAND_KHR, wdpy, attribs1);
EGLAttrib attribs2[] = [ EGL_TRACK_REFERENCES_KHR, EGL_FALSE, EGL_NONE ]
EGLDisplay dpy2 = eglGetPlatformDisplay(EGL_PLATFORM_WAYLAND_KHR, wdpy, attribs2);
EGLAttrib attribs3[] = [ EGL_TRACK_REFERENCES_KHR, EGL_FALSE, EGL_NONE ]
EGLDisplay dpy3 = eglGetPlatformDisplay(EGL_PLATFORM_WAYLAND_KHR, wdpy, attribs3);
EGLAttrib attribs4[] = [ EGL_NONE ]
EGLDisplay dpy4 = eglGetPlatformDisplay(EGL_PLATFORM_WAYLAND_KHR, wdpy, attribs4);
if (dpy1 != dpy2) {
    // This will be true on all drivers, because we used different attribute lists.
}
if (dpy2 == dpy3) {
    // This will be true on all drivers, because we used the same attribute list.
}
if (dpy3 != dpy4) {
    // This could be true or false depending on the driver.
    // EGL_TRACK_REFERENCES_KHR defaults to false, so the attribute lists are
    // different, but logically equivalent. The EGL spec allows either behavior.
}
EGLContext ctx = eglCreateContext(dpy1, ...);
eglMakeCurrent(dpy2, NULL, NULL, ctx); // ERROR: ctx came from dpy1, not dpy2

What this extension adds is a way to guarantee that you get a different EGLDisplay when you call eglGetPlatformDisplay, even if the attribute list is otherwise identical. It also gives you a way to destroy an EGLDisplay afterward instead of leaking it.

kbrenneman avatar Apr 07 '23 14:04 kbrenneman

When you say "private" EGL_PRIVATE_DISPLAY_EXT, private to what? Is this private to a single thread (TLS space for implementation)? or private by some other context? That would need to be clear in the spec.

I guess the point is that "eglGetPlatformDisplay" will not return the same handle twice.

stonesthrow avatar Apr 07 '23 19:04 stonesthrow

"Private" in this case means private to whatever called eglGetPlatformDisplay, rather than the default behavior where everything that calls eglGetPlatformDisplay would end up with the same shared EGLDisplay object.

The use I was thinking about initially was for libraries that use EGL as an internal implementation detail, where they'd want their own private EGLDisplay so that they don't interfere with the application or with other libraries.

The name "EGL_PRIVATE_DISPLAY" is left over from that, but it's definitely not the best choice of enum name.

Maybe something like "EGL_ALLOC_NEW_DISPLAY_EXT" instead? That would more clearly describe what it does, rather than what it might be used for.

I'm open to other suggestions, if anyone's got any.

kbrenneman avatar Apr 07 '23 19:04 kbrenneman

Is "UNIQUE" any clearer?

cubanismo avatar Apr 07 '23 19:04 cubanismo

I just wanted to be sure that it wasn't private to a thread. eglGetPlatformDisplay" with EGL_PRIVATE_DISPLAY_EXT set EGL_TRUE - will not return the same EGLDisplay handle twice, so private by unknown/obscure by any other EGL user.

stonesthrow avatar Apr 07 '23 19:04 stonesthrow

Yeah, that's correct.

kbrenneman avatar Apr 07 '23 19:04 kbrenneman

Looking at your current EGL_EXT_display_alloc.txt lines 62-64 Good Line 88, may I suggest add "unique" handle. That might cover James' suggestion and would cover my desire for clarity for users and implementers.

stonesthrow avatar Apr 07 '23 19:04 stonesthrow

Can do.

For the enum name, would something like "EGL_UNIQUE_DISPLAY_EXT" or "EGL_ALLOC_DISPLAY_EXT" read better than "EGL_PRIVATE_DISPLAY_EXT"?

kbrenneman avatar Apr 07 '23 20:04 kbrenneman

PRIVATE is OK with me. In all cases what that means needs to be clear.

stonesthrow avatar Apr 07 '23 20:04 stonesthrow