From c562b8c51eb472634ea0f996a870b1bb356d9357 Mon Sep 17 00:00:00 2001 From: RoosterDragon Date: Sun, 1 Nov 2015 17:15:23 +0000 Subject: [PATCH 1/3] Detach event handlers on dispose in TerrainSpriteLayer. The WorldRenderer outlives the TerrainSpriteLayer and thus keeps it alive longer than expected via the event handler. We detach it to allow the GC to reclaim it. --- OpenRA.Game/Graphics/TerrainSpriteLayer.cs | 33 +++++++++++----------- 1 file changed, 17 insertions(+), 16 deletions(-) diff --git a/OpenRA.Game/Graphics/TerrainSpriteLayer.cs b/OpenRA.Game/Graphics/TerrainSpriteLayer.cs index 81c07151de..153f7eca24 100644 --- a/OpenRA.Game/Graphics/TerrainSpriteLayer.cs +++ b/OpenRA.Game/Graphics/TerrainSpriteLayer.cs @@ -33,7 +33,7 @@ namespace OpenRA.Graphics readonly WorldRenderer worldRenderer; readonly Map map; - float paletteIndex; + readonly PaletteReference palette; public TerrainSpriteLayer(World world, WorldRenderer wr, Sheet sheet, BlendMode blendMode, PaletteReference palette, bool restrictToBounds) { @@ -41,7 +41,7 @@ namespace OpenRA.Graphics this.restrictToBounds = restrictToBounds; Sheet = sheet; BlendMode = blendMode; - paletteIndex = palette.TextureIndex; + this.palette = palette; map = world.Map; rowStride = 4 * map.MapSize.X; @@ -50,21 +50,21 @@ namespace OpenRA.Graphics vertexBuffer = Game.Renderer.Device.CreateVertexBuffer(vertices.Length); emptySprite = new Sprite(sheet, Rectangle.Empty, TextureChannel.Alpha); - wr.PaletteInvalidated += () => + wr.PaletteInvalidated += UpdatePaletteIndices; + } + + void UpdatePaletteIndices() + { + // Everything in the layer uses the same palette, + // so we can fix the indices in one pass + for (var i = 0; i < vertices.Length; i++) { - paletteIndex = palette.TextureIndex; + var v = vertices[i]; + vertices[i] = new Vertex(v.X, v.Y, v.Z, v.U, v.V, palette.TextureIndex, v.C); + } - // Everything in the layer uses the same palette, - // so we can fix the indices in one pass - for (var i = 0; i < vertices.Length; i++) - { - var v = vertices[i]; - vertices[i] = new Vertex(v.X, v.Y, v.Z, v.U, v.V, paletteIndex, v.C); - } - - for (var row = 0; row < map.MapSize.Y; row++) - dirtyRows.Add(row); - }; + for (var row = 0; row < map.MapSize.Y; row++) + dirtyRows.Add(row); } public void Update(CPos cell, Sprite sprite) @@ -88,7 +88,7 @@ namespace OpenRA.Graphics sprite = emptySprite; var offset = rowStride * uv.V + 4 * uv.U; - Util.FastCreateQuad(vertices, pos, sprite, paletteIndex, offset, sprite.Size); + Util.FastCreateQuad(vertices, pos, sprite, palette.TextureIndex, offset, sprite.Size); dirtyRows.Add(uv.V); } @@ -130,6 +130,7 @@ namespace OpenRA.Graphics public void Dispose() { + worldRenderer.PaletteInvalidated -= UpdatePaletteIndices; vertexBuffer.Dispose(); } } From 9f728b287bec680a8cf38970fc31a2761d1fc4ca Mon Sep 17 00:00:00 2001 From: RoosterDragon Date: Sun, 1 Nov 2015 17:21:42 +0000 Subject: [PATCH 2/3] Remove event handlers to CellEntryChanged when done. Several classes would attach event handlers to the Map which would live longer then they did. Detaching them when no longer needed allows the GC to reclaim them. --- OpenRA.Game/Graphics/TerrainRenderer.cs | 8 ++++++-- OpenRA.Mods.Common/Traits/World/EditorResourceLayer.cs | 2 ++ OpenRA.Mods.Common/Traits/World/ResourceLayer.cs | 2 ++ OpenRA.Mods.Common/Widgets/RadarWidget.cs | 7 +++++++ 4 files changed, 17 insertions(+), 2 deletions(-) diff --git a/OpenRA.Game/Graphics/TerrainRenderer.cs b/OpenRA.Game/Graphics/TerrainRenderer.cs index 1f6a123159..a6011527b0 100644 --- a/OpenRA.Game/Graphics/TerrainRenderer.cs +++ b/OpenRA.Game/Graphics/TerrainRenderer.cs @@ -18,11 +18,13 @@ namespace OpenRA.Graphics readonly TerrainSpriteLayer terrain; readonly Theater theater; readonly CellLayer mapTiles; + readonly CellLayer mapHeight; public TerrainRenderer(World world, WorldRenderer wr) { theater = wr.Theater; mapTiles = world.Map.MapTiles.Value; + mapHeight = world.Map.MapHeight.Value; terrain = new TerrainSpriteLayer(world, wr, theater.Sheet, BlendMode.Alpha, wr.Palette("terrain"), wr.World.Type != WorldType.Editor); @@ -30,8 +32,8 @@ namespace OpenRA.Graphics foreach (var cell in world.Map.AllCells) UpdateCell(cell); - world.Map.MapTiles.Value.CellEntryChanged += UpdateCell; - world.Map.MapHeight.Value.CellEntryChanged += UpdateCell; + mapTiles.CellEntryChanged += UpdateCell; + mapHeight.CellEntryChanged += UpdateCell; } public void UpdateCell(CPos cell) @@ -48,6 +50,8 @@ namespace OpenRA.Graphics public void Dispose() { + mapTiles.CellEntryChanged -= UpdateCell; + mapHeight.CellEntryChanged -= UpdateCell; terrain.Dispose(); } } diff --git a/OpenRA.Mods.Common/Traits/World/EditorResourceLayer.cs b/OpenRA.Mods.Common/Traits/World/EditorResourceLayer.cs index 3224e6b4aa..56fc6dd836 100644 --- a/OpenRA.Mods.Common/Traits/World/EditorResourceLayer.cs +++ b/OpenRA.Mods.Common/Traits/World/EditorResourceLayer.cs @@ -207,6 +207,8 @@ namespace OpenRA.Mods.Common.Traits foreach (var kv in spriteLayers.Values) kv.Dispose(); + Map.MapResources.Value.CellEntryChanged -= UpdateCell; + disposed = true; } } diff --git a/OpenRA.Mods.Common/Traits/World/ResourceLayer.cs b/OpenRA.Mods.Common/Traits/World/ResourceLayer.cs index f11e9c9e95..570deb906d 100644 --- a/OpenRA.Mods.Common/Traits/World/ResourceLayer.cs +++ b/OpenRA.Mods.Common/Traits/World/ResourceLayer.cs @@ -294,6 +294,8 @@ namespace OpenRA.Mods.Common.Traits foreach (var kv in spriteLayers.Values) kv.Dispose(); + RenderContent.CellEntryChanged -= UpdateSpriteLayers; + disposed = true; } diff --git a/OpenRA.Mods.Common/Widgets/RadarWidget.cs b/OpenRA.Mods.Common/Widgets/RadarWidget.cs index 99c1164f7a..4c194f6321 100644 --- a/OpenRA.Mods.Common/Widgets/RadarWidget.cs +++ b/OpenRA.Mods.Common/Widgets/RadarWidget.cs @@ -442,5 +442,12 @@ namespace OpenRA.Mods.Common.Widgets var v = (int)((p.Y - mapRect.Y) / previewScale) + world.Map.Bounds.Top; return new MPos(u, v).ToCPos(world.Map); } + + public override void Removed() + { + base.Removed(); + world.Map.MapTiles.Value.CellEntryChanged -= UpdateTerrainCell; + world.Map.CustomTerrain.CellEntryChanged -= UpdateTerrainCell; + } } } From 6f09d1a2f43eb75ef6e6222bf010e2900c9b9df2 Mon Sep 17 00:00:00 2001 From: RoosterDragon Date: Sun, 1 Nov 2015 17:50:38 +0000 Subject: [PATCH 3/3] Remove event handler when disposed in MainMenuLogic. Removes a static event handler to Game.OnRemoteDirectConnect which allow the GC to reclaim the MainMenuLogic class after it has been disposed. --- .../Widgets/Logic/MainMenuLogic.cs | 29 ++++++++++++------- 1 file changed, 19 insertions(+), 10 deletions(-) diff --git a/OpenRA.Mods.Common/Widgets/Logic/MainMenuLogic.cs b/OpenRA.Mods.Common/Widgets/Logic/MainMenuLogic.cs index 698f679156..c64aa3a0e7 100644 --- a/OpenRA.Mods.Common/Widgets/Logic/MainMenuLogic.cs +++ b/OpenRA.Mods.Common/Widgets/Logic/MainMenuLogic.cs @@ -217,17 +217,19 @@ namespace OpenRA.Mods.Common.Widgets.Logic } } - Game.OnRemoteDirectConnect += (host, port) => + Game.OnRemoteDirectConnect += OnRemoteDirectConnect; + } + + void OnRemoteDirectConnect(string host, int port) + { + menuType = MenuType.None; + Ui.OpenWindow("MULTIPLAYER_PANEL", new WidgetArgs { - menuType = MenuType.None; - Ui.OpenWindow("MULTIPLAYER_PANEL", new WidgetArgs - { - { "onStart", RemoveShellmapUI }, - { "onExit", () => menuType = MenuType.Main }, - { "directConnectHost", host }, - { "directConnectPort", port }, - }); - }; + { "onStart", RemoveShellmapUI }, + { "onExit", () => menuType = MenuType.Main }, + { "directConnectHost", host }, + { "directConnectPort", port }, + }); } void LoadMapIntoEditor(Map map) @@ -362,5 +364,12 @@ namespace OpenRA.Mods.Common.Widgets.Logic OpenSkirmishLobbyPanel, () => { Game.CloseServer(); menuType = MenuType.Main; }); } + + protected override void Dispose(bool disposing) + { + if (disposing) + Game.OnRemoteDirectConnect -= OnRemoteDirectConnect; + base.Dispose(disposing); + } } }