From 8364468a9d05b2ca96937dfe2962e67e32aee22b Mon Sep 17 00:00:00 2001 From: Martin Date: Tue, 12 Dec 2023 17:08:37 +0100 Subject: [PATCH] [GL] Fix resource leaks in ContextHandleOpenTK.create Destroys the GraphicsContext and NativeWindow when the ContextHandle is disposed. Also closes the X11 display as a workaround for a leak in OpenTK. See: https://github.com/opentk/opentk/pull/773 --- .../Core/ContextHandles.fs | 66 +++++++++++++++---- 1 file changed, 52 insertions(+), 14 deletions(-) diff --git a/src/Aardvark.Rendering.GL/Core/ContextHandles.fs b/src/Aardvark.Rendering.GL/Core/ContextHandles.fs index 5cfe273a..9437f7d0 100644 --- a/src/Aardvark.Rendering.GL/Core/ContextHandles.fs +++ b/src/Aardvark.Rendering.GL/Core/ContextHandles.fs @@ -61,6 +61,7 @@ type ContextHandle(handle : IGraphicsContext, window : IWindowInfo) = static let contextError = new Event() let l = obj() + let onDisposed = Event() let mutable debugOutput = None let mutable onMakeCurrent : ConcurrentHashSet unit> = null let mutable driverInfo = None @@ -77,6 +78,9 @@ type ContextHandle(handle : IGraphicsContext, window : IWindowInfo) = [] static member ContextError = contextError.Publish + [] + member x.OnDisposed = onDisposed.Publish + member x.GetProcAddress(name : string) = (handle |> unbox).GetAddress(name) @@ -198,6 +202,7 @@ type ContextHandle(handle : IGraphicsContext, window : IWindowInfo) = member x.Dispose() = debugOutput |> Option.iter (fun dbg -> dbg.Dispose()) + onDisposed.Trigger() interface IDisposable with member x.Dispose() = x.Dispose() @@ -215,9 +220,6 @@ module ContextHandle = if ctx.IsCurrent then ctx.ReleaseCurrent() ctx.Dispose() - //match windows.TryRemove ctx with - // | (true, w) -> w.Dispose() - // | _ -> () /// /// checks whether the given context is current on the calling thread @@ -236,8 +238,45 @@ module ContextHandle = let releaseCurrent (ctx : ContextHandle) = ctx.ReleaseCurrent() module ContextHandleOpenTK = - - let private windows = System.Collections.Concurrent.ConcurrentDictionary() + + // This is a workaround for closing the X11 display, since OpenTK leaks the display even when the + // window is disposed. This leads to problems when running multiple unit tests one after another. + // We currently use a custom version of OpenTK in which this issue isn't fixed yet. + // https://github.com/krauthaufen/OpenTKHack + // See: https://github.com/opentk/opentk/pull/773 + module private X11 = + open OpenTK.Platform.X11 + open System.Reflection + + [] + module private Internals = + let asm = typeof.Assembly + + let fiImplementation = + typeof.GetField("implementation", BindingFlags.NonPublic ||| BindingFlags.Instance) + + let fiWindow = + let t = asm.GetType("OpenTK.Platform.X11.X11GLNative") + if isNull t then null + else t.GetField("window", BindingFlags.NonPublic ||| BindingFlags.Instance) + + let fCloseDisplay = + let t = asm.GetType("OpenTK.Platform.X11.Functions") + if isNull t then null + else t.GetMethod("XCloseDisplay") + + let closeDisplay (window : NativeWindow) = + if RuntimeInformation.IsOSPlatform OSPlatform.Linux then + try + let x11GLNative = fiImplementation.GetValue(window) + let window = unbox <| fiWindow.GetValue(x11GLNative) + + if window.Display <> 0n then + fCloseDisplay.Invoke(null, [| window.Display |]) |> ignore + with exn -> + Log.warn "[GL] Failed to close X11 display: %A" exn.Message + else + () /// /// creates a new context using the default configuration @@ -261,15 +300,14 @@ module ContextHandleOpenTK = | ValueNone -> context.MakeCurrent(null) window, context - - - let handle = new ContextHandle(context, window.WindowInfo) - handle.Initialize(debug, setDefaultStates = false) - // add the window to the windows-table to save it from being - // garbage collected. - if not <| windows.TryAdd(handle, window) then failwith "failed to add new context to live-set" - - handle + let dispose() = + context.Dispose() + window.Dispose() + X11.closeDisplay window + let handle = new ContextHandle(context, window.WindowInfo) + handle.OnDisposed.Add dispose + handle.Initialize(debug, setDefaultStates = false) + handle \ No newline at end of file