diff --git a/DarkUI/DarkUI/Collections/ObservableList.cs b/DarkUI/DarkUI/Collections/ObservableList.cs index fce7f9a9e9..16764d0d49 100644 --- a/DarkUI/DarkUI/Collections/ObservableList.cs +++ b/DarkUI/DarkUI/Collections/ObservableList.cs @@ -19,28 +19,13 @@ public class ObservableList : List, IDisposable #endregion - #region Destructor Region - - ~ObservableList() - { - Dispose(false); - } - - #endregion - #region Dispose Region - public void Dispose() - { - Dispose(true); - GC.SuppressFinalize(this); - } - - protected virtual void Dispose(bool disposing) + public virtual void Dispose() { if (_disposed) return; - + ItemsAdded = null; ItemsRemoved = null; diff --git a/TombEditor/Controls/ImportedGeometryManager.cs b/TombEditor/Controls/ImportedGeometryManager.cs index b7bf2afb75..077ff93b43 100644 --- a/TombEditor/Controls/ImportedGeometryManager.cs +++ b/TombEditor/Controls/ImportedGeometryManager.cs @@ -139,6 +139,7 @@ public void Set(SetValue setValue) private readonly Color _correctColor; private readonly Color _wrongColor; + private ListChangedEventHandler _listChangedHandler; public ImportedGeometryManager() { @@ -193,7 +194,7 @@ public ImportedGeometryManager() }; dataGridView.DataSource = _dataGridViewDataSource; dataGridViewControls.DeleteRowCheckIfCancel = MessageUserAboutHimDeletingRows; - _dataGridViewDataSource.ListChanged += delegate (object sender, ListChangedEventArgs e) + _listChangedHandler = delegate (object sender, ListChangedEventArgs e) { switch (e.ListChangedType) { @@ -203,6 +204,7 @@ public ImportedGeometryManager() break; } }; + _dataGridViewDataSource.ListChanged += _listChangedHandler; Enabled = true; @@ -214,6 +216,7 @@ protected override void Dispose(bool disposing) { if (disposing) { + _dataGridViewDataSource.ListChanged -= _listChangedHandler; components?.Dispose(); Editor.Instance.EditorEventRaised -= EditorEventRaised; } diff --git a/TombEditor/Controls/Panel3D/Panel3D.cs b/TombEditor/Controls/Panel3D/Panel3D.cs index 667653af35..63407eabc5 100644 --- a/TombEditor/Controls/Panel3D/Panel3D.cs +++ b/TombEditor/Controls/Panel3D/Panel3D.cs @@ -135,6 +135,8 @@ public bool DisablePickingForHiddenRooms private Buffer _objectHeightLineVertexBuffer; private Buffer _flybyPathVertexBuffer; private Buffer _ghostBlockVertexBuffer; + private SolidVertex[] _ghostBlockVertices = new SolidVertex[84]; + private float[] _roomsDistanceCache; private Buffer _boxVertexBuffer; // Flyby stuff @@ -228,6 +230,7 @@ protected override void Dispose(bool disposing) _rasterizerStateDepthBias?.Dispose(); _currentContextMenu?.Dispose(); _wadRenderer?.Dispose(); + _fontDefault?.Dispose(); } base.Dispose(disposing); } diff --git a/TombEditor/Controls/Panel3D/Panel3DDraw.cs b/TombEditor/Controls/Panel3D/Panel3DDraw.cs index 8e148d1701..ec6698e8d5 100644 --- a/TombEditor/Controls/Panel3D/Panel3DDraw.cs +++ b/TombEditor/Controls/Panel3D/Panel3DDraw.cs @@ -668,8 +668,9 @@ private void DrawGhostBlockBodies(Effect effect, List ghostB if (!instance.Valid) continue; - // Create a vertex array - SolidVertex[] vtxs = new SolidVertex[84]; // 78 with diagonal steps + // Reuse cached vertex array + SolidVertex[] vtxs = _ghostBlockVertices; + Array.Clear(vtxs, 0, vtxs.Length); // Derive base sector colours var p1c = new Vector4(baseColor.To3() * (selected ? 0.8f : 0.4f), selected ? 0.7f : 0.5f); diff --git a/TombEditor/Controls/Panel3D/Panel3DDrawCollector.cs b/TombEditor/Controls/Panel3D/Panel3DDrawCollector.cs index 94e79dc803..1e6320aab9 100644 --- a/TombEditor/Controls/Panel3D/Panel3DDrawCollector.cs +++ b/TombEditor/Controls/Panel3D/Panel3DDrawCollector.cs @@ -144,12 +144,14 @@ Room[] CollectRoomsToDraw() // Collect rooms to draw var camPos = Camera.GetPosition(); var roomsToDraw = CollectRoomsToDraw(_editor.SelectedRoom).ToArray(); - var roomsToDrawDistanceSquared = new float[roomsToDraw.Length]; + + if (_roomsDistanceCache == null || _roomsDistanceCache.Length < roomsToDraw.Length) + _roomsDistanceCache = new float[roomsToDraw.Length]; for (int i = 0; i < roomsToDraw.Length; ++i) - roomsToDrawDistanceSquared[i] = Vector3.DistanceSquared(camPos, roomsToDraw[i].WorldPos + roomsToDraw[i].GetLocalCenter()); + _roomsDistanceCache[i] = Vector3.DistanceSquared(camPos, roomsToDraw[i].WorldPos + roomsToDraw[i].GetLocalCenter()); - Array.Sort(roomsToDrawDistanceSquared, roomsToDraw); + Array.Sort(_roomsDistanceCache, roomsToDraw, 0, roomsToDraw.Length); Array.Reverse(roomsToDraw); return roomsToDraw; diff --git a/TombLib/TombLib.Rendering/Rendering/RenderingFont.cs b/TombLib/TombLib.Rendering/Rendering/RenderingFont.cs index e85dc1d8c8..9ef006f2e2 100644 --- a/TombLib/TombLib.Rendering/Rendering/RenderingFont.cs +++ b/TombLib/TombLib.Rendering/Rendering/RenderingFont.cs @@ -315,11 +315,23 @@ public class GlyphRenderInfo public void Dispose() { - if (!_disposed) + if (_disposed) return; _disposed = true; TextureAllocator?.Dispose(); + ReleaseUnmanagedResources(); + + GC.SuppressFinalize(this); + } + + ~RenderingFont() + { + ReleaseUnmanagedResources(); + } + + private void ReleaseUnmanagedResources() + { GDI.DeleteObject(_gdiFont); GDI.DeleteDC(_gdiHdc); Marshal.FreeHGlobal(_gdiGetCharacterPlacementOrder); @@ -327,11 +339,6 @@ public void Dispose() Marshal.FreeHGlobal(_gdiGetCharacterPlacementGlpyhs); } - ~RenderingFont() - { - Dispose(); - } - private static class GDI { public class GDIException : Exception diff --git a/TombLib/TombLib/IO/BinaryWriterFast.cs b/TombLib/TombLib/IO/BinaryWriterFast.cs index 850221b13e..ef0c55bab4 100644 --- a/TombLib/TombLib/IO/BinaryWriterFast.cs +++ b/TombLib/TombLib/IO/BinaryWriterFast.cs @@ -52,6 +52,8 @@ public void Dispose() _ptr = null; _startPtr = null; _baseStream = null; + + GC.SuppressFinalize(this); } public Stream BaseStream diff --git a/TombLib/TombLib/LevelData/ImportedGeometry.cs b/TombLib/TombLib/LevelData/ImportedGeometry.cs index 2aef2029a5..e5c606c86a 100644 --- a/TombLib/TombLib/LevelData/ImportedGeometry.cs +++ b/TombLib/TombLib/LevelData/ImportedGeometry.cs @@ -16,10 +16,12 @@ namespace TombLib.LevelData { - public class ImportedGeometryTexture : Texture + public class ImportedGeometryTexture : Texture, IDisposable { public Texture2D DirectXTexture { get; private set; } + private bool _disposed; + public ImportedGeometryTexture(string absolutePath) { AbsolutePath = absolutePath; @@ -31,22 +33,39 @@ public ImportedGeometryTexture(string absolutePath) if (SynchronizationContext.Current == null) DirectXTexture = TextureLoad.Load(ImportedGeometry.Device, Image); else - SynchronizationContext.Current.Post(unused => // Synchronize DirectX, we can't 'send' because that may deadlock with the level settings reloader - DirectXTexture = TextureLoad.Load(ImportedGeometry.Device, Image), null); + SynchronizationContext.Current.Post(unused => { + if (_disposed) + return; + + DirectXTexture = TextureLoad.Load(ImportedGeometry.Device, Image); + }, null); } private ImportedGeometryTexture(ImportedGeometryTexture other) { - DirectXTexture = other.DirectXTexture; AbsolutePath = other.AbsolutePath; Image = other.Image; } public void Assign(ImportedGeometryTexture other) { + // Dispose old GPU texture if it differs from the new one. + if (DirectXTexture != null && DirectXTexture != other.DirectXTexture) + DirectXTexture.Dispose(); + AbsolutePath = other.AbsolutePath; Image = other.Image; DirectXTexture = other.DirectXTexture; + + _disposed = false; + } + + public void Dispose() + { + _disposed = true; + + DirectXTexture?.Dispose(); + DirectXTexture = null; } public override Texture Clone() => new ImportedGeometryTexture(this); diff --git a/TombLib/TombLib/Wad/WadTexture.cs b/TombLib/TombLib/Wad/WadTexture.cs index 678a4e5eac..e5b5739c6f 100644 --- a/TombLib/TombLib/Wad/WadTexture.cs +++ b/TombLib/TombLib/Wad/WadTexture.cs @@ -1,7 +1,9 @@ using System; +using System.Buffers.Binary; using System.IO; using TombLib.Graphics; using TombLib.Utils; +using Blake3Hasher = Blake3.Hasher; namespace TombLib.Wad { @@ -20,14 +22,20 @@ public WadTexture(ImageC image) { Image = image; - using (var ms = new MemoryStream()) - { - var writer = new BinaryWriter(ms); - writer.Write(Image.Size.X); - writer.Write(Image.Size.Y); - Image.WriteToStreamRaw(ms); - Hash = Hash.FromByteArray(ms.ToArray()); - } + // Hash image dimensions and pixel data directly without intermediate copies. + using var hasher = Blake3Hasher.New(); + Span header = stackalloc byte[8]; + BinaryPrimitives.WriteInt32LittleEndian(header, Image.Size.X); + BinaryPrimitives.WriteInt32LittleEndian(header.Slice(4), Image.Size.Y); + hasher.Update(header); + hasher.Update(Image.ToByteArray()); + + Span digest = stackalloc byte[32]; + hasher.Finalize(digest); + + ulong low = BinaryPrimitives.ReadUInt64LittleEndian(digest.Slice(0, 8)); + ulong high = BinaryPrimitives.ReadUInt64LittleEndian(digest.Slice(8, 8)); + Hash = new Hash { HashLow = low, HashHigh = high }; } public override Texture Clone() => this; diff --git a/WadTool/Controls/PanelRenderingAnimationEditor.cs b/WadTool/Controls/PanelRenderingAnimationEditor.cs index c4a935a4c6..8a9ecc5654 100644 --- a/WadTool/Controls/PanelRenderingAnimationEditor.cs +++ b/WadTool/Controls/PanelRenderingAnimationEditor.cs @@ -141,7 +141,6 @@ protected override void Dispose(bool disposing) { if (disposing) { - _fontTexture?.Dispose(); _fontDefault?.Dispose(); _gizmo?.Dispose(); _plane?.Dispose(); diff --git a/WadTool/Controls/PanelRenderingMesh.cs b/WadTool/Controls/PanelRenderingMesh.cs index 5ff525876f..b457fde211 100644 --- a/WadTool/Controls/PanelRenderingMesh.cs +++ b/WadTool/Controls/PanelRenderingMesh.cs @@ -353,6 +353,7 @@ protected override void Dispose(bool disposing) _bigSphere?.Dispose(); _plane?.Dispose(); _wadRenderer?.Dispose(); + _fontDefault?.Dispose(); } base.Dispose(disposing); } diff --git a/WadTool/Controls/PanelRenderingSkeleton.cs b/WadTool/Controls/PanelRenderingSkeleton.cs index 1fb687f80a..81221f69b4 100644 --- a/WadTool/Controls/PanelRenderingSkeleton.cs +++ b/WadTool/Controls/PanelRenderingSkeleton.cs @@ -114,7 +114,6 @@ protected override void Dispose(bool disposing) { if (disposing) { - _fontTexture?.Dispose(); _fontDefault?.Dispose(); _rasterizerWireframe?.Dispose(); _vertexBufferVisibility?.Dispose(); diff --git a/WadTool/Controls/PanelRenderingStaticEditor.cs b/WadTool/Controls/PanelRenderingStaticEditor.cs index d6d34f94c5..9f8acc8489 100644 --- a/WadTool/Controls/PanelRenderingStaticEditor.cs +++ b/WadTool/Controls/PanelRenderingStaticEditor.cs @@ -139,7 +139,6 @@ protected override void Dispose(bool disposing) { if (disposing) { - _fontTexture?.Dispose(); _fontDefault?.Dispose(); _gizmo?.Dispose(); _gizmoLight?.Dispose();