From c963806dae6ee5e713ec33cc8d3e32cf44677957 Mon Sep 17 00:00:00 2001 From: RoosterDragon Date: Tue, 21 Apr 2015 20:24:26 +0100 Subject: [PATCH 1/2] Prevent leaking of hardware cursors. Added the lacking finialization machinery, ensured disposal under exceptional circumstances and also ensure the game calls dispose on old cursors before creating new ones. --- OpenRA.Game/Game.cs | 3 + OpenRA.Renderer.Sdl2/Sdl2GraphicsDevice.cs | 64 +++++++++++++++++----- 2 files changed, 53 insertions(+), 14 deletions(-) diff --git a/OpenRA.Game/Game.cs b/OpenRA.Game/Game.cs index 1c6a68c82e..69e795569a 100644 --- a/OpenRA.Game/Game.cs +++ b/OpenRA.Game/Game.cs @@ -309,6 +309,9 @@ namespace OpenRA using (new PerfTimer("LoadMaps")) ModData.MapCache.LoadMaps(); + if (Cursor != null) + Cursor.Dispose(); + if (Settings.Graphics.HardwareCursors) { try diff --git a/OpenRA.Renderer.Sdl2/Sdl2GraphicsDevice.cs b/OpenRA.Renderer.Sdl2/Sdl2GraphicsDevice.cs index 2c866d194f..ab622f7e53 100755 --- a/OpenRA.Renderer.Sdl2/Sdl2GraphicsDevice.cs +++ b/OpenRA.Renderer.Sdl2/Sdl2GraphicsDevice.cs @@ -106,11 +106,14 @@ namespace OpenRA.Renderer.Sdl2 public IHardwareCursor CreateHardwareCursor(string name, Size size, byte[] data, int2 hotspot) { - var c = new SDL2HardwareCursor(size, data, hotspot); - if (c.Cursor == IntPtr.Zero) - throw new InvalidDataException("Failed to create hardware cursor `{0}`: {1}".F(name, SDL.SDL_GetError())); - - return c; + try + { + return new SDL2HardwareCursor(size, data, hotspot); + } + catch (Exception ex) + { + throw new InvalidDataException("Failed to create hardware cursor `{0}`".F(name), ex); + } } public void SetHardwareCursor(IHardwareCursor cursor) @@ -125,24 +128,57 @@ namespace OpenRA.Renderer.Sdl2 } } - class SDL2HardwareCursor : IHardwareCursor + sealed class SDL2HardwareCursor : IHardwareCursor { - public readonly IntPtr Cursor; - readonly IntPtr surface; + public IntPtr Cursor { get; private set; } + IntPtr surface; public SDL2HardwareCursor(Size size, byte[] data, int2 hotspot) { - surface = SDL.SDL_CreateRGBSurface(0, size.Width, size.Height, 32, 0x00FF0000, 0x0000FF00, 0x000000FF, 0xFF000000); + try + { + surface = SDL.SDL_CreateRGBSurface(0, size.Width, size.Height, 32, 0x00FF0000, 0x0000FF00, 0x000000FF, 0xFF000000); + if (surface == IntPtr.Zero) + throw new InvalidDataException("Failed to create surface: {0}".F(SDL.SDL_GetError())); - var sur = (SDL2.SDL.SDL_Surface)Marshal.PtrToStructure(surface, typeof(SDL2.SDL.SDL_Surface)); - Marshal.Copy(data, 0, sur.pixels, data.Length); - Cursor = SDL.SDL_CreateColorCursor(surface, hotspot.X, hotspot.Y); + var sur = (SDL2.SDL.SDL_Surface)Marshal.PtrToStructure(surface, typeof(SDL2.SDL.SDL_Surface)); + Marshal.Copy(data, 0, sur.pixels, data.Length); + + Cursor = SDL.SDL_CreateColorCursor(surface, hotspot.X, hotspot.Y); + if (Cursor == IntPtr.Zero) + throw new InvalidDataException("Failed to create cursor: {0}".F(SDL.SDL_GetError())); + } + catch + { + Dispose(); + throw; + } + } + + ~SDL2HardwareCursor() + { + Game.RunAfterTick(() => Dispose(false)); } public void Dispose() { - SDL.SDL_FreeCursor(Cursor); - SDL.SDL_FreeSurface(surface); + Game.RunAfterTick(() => Dispose(true)); + GC.SuppressFinalize(this); + } + + void Dispose(bool disposing) + { + if (Cursor != IntPtr.Zero) + { + SDL.SDL_FreeCursor(Cursor); + Cursor = IntPtr.Zero; + } + + if (surface != IntPtr.Zero) + { + SDL.SDL_FreeSurface(surface); + surface = IntPtr.Zero; + } } } From 13078c820be0d96b96c1fa0825e4ff4b1805ecde Mon Sep 17 00:00:00 2001 From: RoosterDragon Date: Tue, 21 Apr 2015 20:27:06 +0100 Subject: [PATCH 2/2] Retry hardware cursor creation. On Windows, a spurious error sometimes occur when creating cursors. We retry a few times before before having to give up and fall back to software cursors. --- OpenRA.Renderer.Sdl2/Sdl2GraphicsDevice.cs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/OpenRA.Renderer.Sdl2/Sdl2GraphicsDevice.cs b/OpenRA.Renderer.Sdl2/Sdl2GraphicsDevice.cs index ab622f7e53..59cf43505a 100755 --- a/OpenRA.Renderer.Sdl2/Sdl2GraphicsDevice.cs +++ b/OpenRA.Renderer.Sdl2/Sdl2GraphicsDevice.cs @@ -144,7 +144,9 @@ namespace OpenRA.Renderer.Sdl2 var sur = (SDL2.SDL.SDL_Surface)Marshal.PtrToStructure(surface, typeof(SDL2.SDL.SDL_Surface)); Marshal.Copy(data, 0, sur.pixels, data.Length); - Cursor = SDL.SDL_CreateColorCursor(surface, hotspot.X, hotspot.Y); + // This call very occasionally fails on Windows, but often works when retried. + for (var retries = 0; retries < 3 && Cursor == IntPtr.Zero; retries++) + Cursor = SDL.SDL_CreateColorCursor(surface, hotspot.X, hotspot.Y); if (Cursor == IntPtr.Zero) throw new InvalidDataException("Failed to create cursor: {0}".F(SDL.SDL_GetError())); }