From 5e53809f9619672410b9f9710f0c04fed69ae8e3 Mon Sep 17 00:00:00 2001 From: codeimp Date: Fri, 26 Oct 2007 15:28:32 +0000 Subject: [PATCH] background resource loading implemented --- Source/Config/GameConfiguration.cs | 1 + Source/Data/DataManager.cs | 263 ++++++++++++++++++++----- Source/Data/FileImage.cs | 11 +- Source/Data/FlatImage.cs | 55 +++--- Source/Data/ImageData.cs | 78 ++++---- Source/Data/ResourceImage.cs | 17 +- Source/Data/SpriteImage.cs | 55 +++--- Source/Data/TextureImage.cs | 91 ++++----- Source/Interface/OpenMapOptionsForm.cs | 1 + 9 files changed, 386 insertions(+), 186 deletions(-) diff --git a/Source/Config/GameConfiguration.cs b/Source/Config/GameConfiguration.cs index 8947c64b..610e6d79 100644 --- a/Source/Config/GameConfiguration.cs +++ b/Source/Config/GameConfiguration.cs @@ -78,6 +78,7 @@ namespace CodeImp.DoomBuilder.Config // Things public List ThingCategories { get { return thingcategories; } } + public ICollection Things { get { return things.Values; } } #endregion diff --git a/Source/Data/DataManager.cs b/Source/Data/DataManager.cs index 6c93d31c..38f7a330 100644 --- a/Source/Data/DataManager.cs +++ b/Source/Data/DataManager.cs @@ -27,6 +27,8 @@ using System.IO; using CodeImp.DoomBuilder.IO; using System.Windows.Forms; using SlimDX.Direct3D9; +using CodeImp.DoomBuilder.Config; +using System.Threading; #endregion @@ -55,6 +57,9 @@ namespace CodeImp.DoomBuilder.Data // Sprites private Dictionary sprites; + // Background loading + private Thread backgroundloader; + // Disposing private bool isdisposed = false; @@ -169,11 +174,19 @@ namespace CodeImp.DoomBuilder.Data LoadTextures(); General.WriteLogLine("Loading flats..."); LoadFlats(); + General.WriteLogLine("Loading sprites..."); + LoadSprites(); + + // Start background loading + StartBackgroundLoader(); } // This unloads all data public void Unload() { + // Stop background loader + StopBackgroundLoader(); + // Dispose resources foreach(KeyValuePair i in textures) i.Value.Dispose(); foreach(KeyValuePair i in flats) i.Value.Dispose(); @@ -192,6 +205,9 @@ namespace CodeImp.DoomBuilder.Data // This suspends data resources public void Suspend() { + // Stop background loader + StopBackgroundLoader(); + // Go for all containers foreach(DataReader d in containers) { @@ -219,10 +235,123 @@ namespace CodeImp.DoomBuilder.Data General.ShowErrorMessage("Unable to load resources from location \"" + d.Location.location + "\". Please make sure the location is accessible and not in use by another program.", MessageBoxButtons.OK); } } + + // Start background loading + StartBackgroundLoader(); } #endregion + #region ================== Background Loading + + // This starts background loading + private void StartBackgroundLoader() + { + // If a loader is already running, stop it first + if(backgroundloader != null) StopBackgroundLoader(); + + // Start a low priority thread to load images in background + General.WriteLogLine("Starting background resource loading..."); + backgroundloader = new Thread(new ThreadStart(BackgroundLoad)); + backgroundloader.Name = "BackgroundLoader"; + backgroundloader.Priority = ThreadPriority.Lowest; + backgroundloader.Start(); + } + + // This stops background loading + private void StopBackgroundLoader() + { + // Stop the thread and wait for it to end + General.WriteLogLine("Stopping background resource loading..."); + backgroundloader.Interrupt(); + backgroundloader.Join(); + + // Done + backgroundloader = null; + } + + // The background loader + private void BackgroundLoad() + { + int loadedtextures; + int loadedflats; + int loadedsprites; + + try + { + // Load all lists + loadedtextures = LoadImagesList(textures); + loadedflats = LoadImagesList(flats); + loadedsprites = LoadImagesList(sprites); + } + catch(ThreadInterruptedException) + { + return; + } + + // Done + General.WriteLogLine("Background resource loading completed"); + General.WriteLogLine("Loaded textures: " + loadedtextures + " / " + textures.Count); + General.WriteLogLine("Loaded flats: " + loadedflats + " / " + flats.Count); + General.WriteLogLine("Loaded sprites: " + loadedsprites + " / " + sprites.Count); + } + + // This loads a list of ImageData + private int LoadImagesList(Dictionary list) + { + Dictionary.Enumerator walker; + bool moveresult = false; + bool interrupted = false; + int numloaded; + + do + { + // Get enumerator + lock(list) + { + walker = list.GetEnumerator(); + moveresult = walker.MoveNext(); + numloaded = 0; + } + + // Continue until at end of list + while(moveresult) + { + lock(list) + { + // Load image + walker.Current.Value.LoadImage(); + //walker.Current.Value.CreateTexture(); + if(walker.Current.Value.IsLoaded) numloaded++; + } + + // Wait a bit + Thread.Sleep(10); + + lock(list) + { + try + { + // Move to next item + moveresult = walker.MoveNext(); + } + catch(InvalidOperationException) + { + // List was modified, restart! + interrupted = true; + break; + } + } + } + } + while(interrupted); + + // Return result + return numloaded; + } + + #endregion + #region ================== Palette // This loads the PLAYPAL palette @@ -301,19 +430,22 @@ namespace CodeImp.DoomBuilder.Data // This returns an image by long public ImageData GetTextureImage(long longname) { - // Does this texture exist? - if(textures.ContainsKey(longname)) + lock(textures) { - // Return texture - return textures[longname]; - } - else - { - // Return null image - return new NullImage(); + // Does this texture exist? + if(textures.ContainsKey(longname)) + { + // Return texture + return textures[longname]; + } + else + { + // Return null image + return new NullImage(); + } } } - + // This returns a bitmap by string public Bitmap GetTextureBitmap(string name) { @@ -403,16 +535,19 @@ namespace CodeImp.DoomBuilder.Data // This returns an image by long public ImageData GetFlatImage(long longname) { - // Does this flat exist? - if(flats.ContainsKey(longname)) + lock(flats) { - // Return flat - return flats[longname]; - } - else - { - // Return null image - return new NullImage(); + // Does this flat exist? + if(flats.ContainsKey(longname)) + { + // Return flat + return flats[longname]; + } + else + { + // Return null image + return new NullImage(); + } } } @@ -454,6 +589,39 @@ namespace CodeImp.DoomBuilder.Data #region ================== Sprites + // This loads the sprites + private void LoadSprites() + { + Stream spritedata = null; + SpriteImage image; + + // Go for all things + foreach(ThingTypeInfo ti in General.Map.Config.Things) + { + // Sprite not added to collection yet? + if(!sprites.ContainsKey(ti.SpriteLongName)) + { + // Go for all opened containers + for(int i = containers.Count - 1; i >= 0; i--) + { + // This contain provides this sprite? + spritedata = containers[i].GetSpriteData(ti.Sprite); + if(spritedata != null) break; + } + + // Found anything? + if(spritedata != null) + { + // Make new sprite image + image = new SpriteImage(ti.Sprite); + + // Add to collection + sprites.Add(ti.SpriteLongName, image); + } + } + } + } + // This returns an image by long public ImageData GetSpriteImage(string name) { @@ -461,38 +629,41 @@ namespace CodeImp.DoomBuilder.Data long longname = Lump.MakeLongName(name); SpriteImage image; - // Sprite already loaded? - if(sprites.ContainsKey(longname)) + lock(sprites) { - // Return exiting sprite - return sprites[longname]; - } - else - { - // Go for all opened containers - for(int i = containers.Count - 1; i >= 0; i--) + // Sprite already loaded? + if(sprites.ContainsKey(longname)) { - // This contain provides this sprite? - spritedata = containers[i].GetSpriteData(name); - if(spritedata != null) break; - } - - // Found anything? - if(spritedata != null) - { - // Make new sprite image - image = new SpriteImage(name); - - // Add to collection - sprites.Add(longname, image); - - // Return result - return image; + // Return exiting sprite + return sprites[longname]; } else { - // Return null image - return new NullImage(); + // Go for all opened containers + for(int i = containers.Count - 1; i >= 0; i--) + { + // This contain provides this sprite? + spritedata = containers[i].GetSpriteData(name); + if(spritedata != null) break; + } + + // Found anything? + if(spritedata != null) + { + // Make new sprite image + image = new SpriteImage(name); + + // Add to collection + sprites.Add(longname, image); + + // Return result + return image; + } + else + { + // Return null image + return new NullImage(); + } } } } diff --git a/Source/Data/FileImage.cs b/Source/Data/FileImage.cs index ffd0a15c..6e779b56 100644 --- a/Source/Data/FileImage.cs +++ b/Source/Data/FileImage.cs @@ -60,11 +60,14 @@ namespace CodeImp.DoomBuilder.Data // Leave when already loaded if(this.IsLoaded) return; - // Load file - bitmap = (Bitmap)Bitmap.FromFile(filepathname); + lock(this) + { + // Load file + bitmap = (Bitmap)Bitmap.FromFile(filepathname); - // Pass on to base - base.LoadImage(); + // Pass on to base + base.LoadImage(); + } } #endregion diff --git a/Source/Data/FlatImage.cs b/Source/Data/FlatImage.cs index 36d550b3..da49db4d 100644 --- a/Source/Data/FlatImage.cs +++ b/Source/Data/FlatImage.cs @@ -62,37 +62,40 @@ namespace CodeImp.DoomBuilder.Data // Leave when already loaded if(this.IsLoaded) return; - // Get the lump data stream - lumpdata = General.Map.Data.GetPatchData(Name); - if(lumpdata != null) + lock(this) { - // Copy lump data to memory - lumpdata.Seek(0, SeekOrigin.Begin); - membytes = new byte[(int)lumpdata.Length]; - lumpdata.Read(membytes, 0, (int)lumpdata.Length); - mem = new MemoryStream(membytes); - mem.Seek(0, SeekOrigin.Begin); - - // Get a reader for the data - reader = ImageDataFormat.GetImageReader(mem, ImageDataFormat.DOOMFLAT, General.Map.Data.Palette); - if(reader is UnknownImageReader) + // Get the lump data stream + lumpdata = General.Map.Data.GetPatchData(Name); + if(lumpdata != null) { - // Data is in an unknown format! - General.WriteLogLine("WARNING: Flat lump '" + Name + "' data format could not be read!"); + // Copy lump data to memory + lumpdata.Seek(0, SeekOrigin.Begin); + membytes = new byte[(int)lumpdata.Length]; + lumpdata.Read(membytes, 0, (int)lumpdata.Length); + mem = new MemoryStream(membytes); + mem.Seek(0, SeekOrigin.Begin); + + // Get a reader for the data + reader = ImageDataFormat.GetImageReader(mem, ImageDataFormat.DOOMFLAT, General.Map.Data.Palette); + if(reader is UnknownImageReader) + { + // Data is in an unknown format! + General.WriteLogLine("WARNING: Flat lump '" + Name + "' data format could not be read!"); + } + + // Read data as bitmap + mem.Seek(0, SeekOrigin.Begin); + bitmap = reader.ReadAsBitmap(mem); + } + else + { + // Missing a patch lump! + General.WriteLogLine("WARNING: Missing flat lump '" + Name + "'!"); } - // Read data as bitmap - mem.Seek(0, SeekOrigin.Begin); - bitmap = reader.ReadAsBitmap(mem); + // Pass on to base + base.LoadImage(); } - else - { - // Missing a patch lump! - General.WriteLogLine("WARNING: Missing flat lump '" + Name + "'!"); - } - - // Pass on to base - base.LoadImage(); } #endregion diff --git a/Source/Data/ImageData.cs b/Source/Data/ImageData.cs index 22913930..5e952eb1 100644 --- a/Source/Data/ImageData.cs +++ b/Source/Data/ImageData.cs @@ -94,18 +94,21 @@ namespace CodeImp.DoomBuilder.Data // Not already disposed? if(!isdisposed) { - // Clean up - if(bitmap != null) bitmap.Dispose(); - if(texture != null) texture.Dispose(); - if(pixeldata != null) + lock(this) { - General.VirtualFree((void*)pixeldata, new UIntPtr(pixeldatasize), General.MEM_RELEASE); - pixeldata = null; - GC.RemoveMemoryPressure(pixeldatasize); - } + // Clean up + if(bitmap != null) bitmap.Dispose(); + if(texture != null) texture.Dispose(); + if(pixeldata != null) + { + General.VirtualFree((void*)pixeldata, new UIntPtr(pixeldatasize), General.MEM_RELEASE); + pixeldata = null; + GC.RemoveMemoryPressure(pixeldatasize); + } - // Done - isdisposed = true; + // Done + isdisposed = true; + } } } @@ -133,21 +136,24 @@ namespace CodeImp.DoomBuilder.Data // Only do this when data is not created yet if((pixeldata == null) && IsLoaded) { - // Clean up old memory if reserved - if(pixeldata != null) + lock(this) { - General.VirtualFree((void*)pixeldata, new UIntPtr(pixeldatasize), General.MEM_RELEASE); - pixeldata = null; - GC.RemoveMemoryPressure(pixeldatasize); - } + // Clean up old memory if reserved + if(pixeldata != null) + { + General.VirtualFree((void*)pixeldata, new UIntPtr(pixeldatasize), General.MEM_RELEASE); + pixeldata = null; + GC.RemoveMemoryPressure(pixeldatasize); + } - // Make a data copy of the bits for the 2D renderer - bmpdata = bitmap.LockBits(new Rectangle(0, 0, bitmap.Size.Width, bitmap.Size.Height), ImageLockMode.ReadOnly, PixelFormat.Format32bppArgb); - pixeldatasize = (uint)(bmpdata.Width * bmpdata.Height * sizeof(PixelColor)); - pixeldata = (PixelColor*)General.VirtualAlloc(IntPtr.Zero, new UIntPtr(pixeldatasize), General.MEM_COMMIT, General.PAGE_READWRITE); - General.CopyMemory((void*)pixeldata, bmpdata.Scan0.ToPointer(), new UIntPtr(pixeldatasize)); - bitmap.UnlockBits(bmpdata); - GC.AddMemoryPressure(pixeldatasize); + // Make a data copy of the bits for the 2D renderer + bmpdata = bitmap.LockBits(new Rectangle(0, 0, bitmap.Size.Width, bitmap.Size.Height), ImageLockMode.ReadOnly, PixelFormat.Format32bppArgb); + pixeldatasize = (uint)(bmpdata.Width * bmpdata.Height * sizeof(PixelColor)); + pixeldata = (PixelColor*)General.VirtualAlloc(IntPtr.Zero, new UIntPtr(pixeldatasize), General.MEM_COMMIT, General.PAGE_READWRITE); + General.CopyMemory((void*)pixeldata, bmpdata.Scan0.ToPointer(), new UIntPtr(pixeldatasize)); + bitmap.UnlockBits(bmpdata); + GC.AddMemoryPressure(pixeldatasize); + } } } @@ -159,22 +165,28 @@ namespace CodeImp.DoomBuilder.Data // Only do this when texture is not created yet if((texture == null) && IsLoaded) { - // Write to memory stream and read from memory - memstream = new MemoryStream(); - bitmap.Save(memstream, ImageFormat.Bmp); - memstream.Seek(0, SeekOrigin.Begin); - texture = Texture.FromStream(General.Map.Graphics.Device, memstream, (int)memstream.Length, bitmap.Size.Width, bitmap.Size.Height, 0, - Usage.None, Format.Unknown, Pool.Managed, Filter.Box, Filter.Box, 0); - memstream.Dispose(); + lock(this) + { + // Write to memory stream and read from memory + memstream = new MemoryStream(); + bitmap.Save(memstream, ImageFormat.Bmp); + memstream.Seek(0, SeekOrigin.Begin); + texture = Texture.FromStream(General.Map.Graphics.Device, memstream, (int)memstream.Length, bitmap.Size.Width, bitmap.Size.Height, 0, + Usage.None, Format.Unknown, Pool.Managed, Filter.Box, Filter.Box, 0); + memstream.Dispose(); + } } } // This destroys the Direct3D texture public void ReleaseTexture() { - // Trash it - if(texture != null) texture.Dispose(); - texture = null; + lock(this) + { + // Trash it + if(texture != null) texture.Dispose(); + texture = null; + } } #endregion diff --git a/Source/Data/ResourceImage.cs b/Source/Data/ResourceImage.cs index 44c85c68..c5f32801 100644 --- a/Source/Data/ResourceImage.cs +++ b/Source/Data/ResourceImage.cs @@ -78,13 +78,16 @@ namespace CodeImp.DoomBuilder.Data public override void LoadImage() { Stream bitmapdata; - - // Get resource from memory - bitmapdata = General.ThisAssembly.GetManifestResourceStream("CodeImp.DoomBuilder.Resources." + Name); - bitmap = (Bitmap)Image.FromStream(bitmapdata); - - // Pass on to base - base.LoadImage(); + + lock(this) + { + // Get resource from memory + bitmapdata = General.ThisAssembly.GetManifestResourceStream("CodeImp.DoomBuilder.Resources." + Name); + bitmap = (Bitmap)Image.FromStream(bitmapdata); + + // Pass on to base + base.LoadImage(); + } } #endregion diff --git a/Source/Data/SpriteImage.cs b/Source/Data/SpriteImage.cs index de6187f6..2e2d5a8c 100644 --- a/Source/Data/SpriteImage.cs +++ b/Source/Data/SpriteImage.cs @@ -62,37 +62,40 @@ namespace CodeImp.DoomBuilder.Data // Leave when already loaded if(this.IsLoaded) return; - // Get the lump data stream - lumpdata = General.Map.Data.GetPatchData(Name); - if(lumpdata != null) + lock(this) { - // Copy lump data to memory - lumpdata.Seek(0, SeekOrigin.Begin); - membytes = new byte[(int)lumpdata.Length]; - lumpdata.Read(membytes, 0, (int)lumpdata.Length); - mem = new MemoryStream(membytes); - mem.Seek(0, SeekOrigin.Begin); - - // Get a reader for the data - reader = ImageDataFormat.GetImageReader(mem, ImageDataFormat.DOOMPICTURE, General.Map.Data.Palette); - if(reader is UnknownImageReader) + // Get the lump data stream + lumpdata = General.Map.Data.GetPatchData(Name); + if(lumpdata != null) { - // Data is in an unknown format! - General.WriteLogLine("WARNING: Sprite lump '" + Name + "' data format could not be read!"); + // Copy lump data to memory + lumpdata.Seek(0, SeekOrigin.Begin); + membytes = new byte[(int)lumpdata.Length]; + lumpdata.Read(membytes, 0, (int)lumpdata.Length); + mem = new MemoryStream(membytes); + mem.Seek(0, SeekOrigin.Begin); + + // Get a reader for the data + reader = ImageDataFormat.GetImageReader(mem, ImageDataFormat.DOOMPICTURE, General.Map.Data.Palette); + if(reader is UnknownImageReader) + { + // Data is in an unknown format! + General.WriteLogLine("WARNING: Sprite lump '" + Name + "' data format could not be read!"); + } + + // Read data as bitmap + mem.Seek(0, SeekOrigin.Begin); + bitmap = reader.ReadAsBitmap(mem); + } + else + { + // Missing a patch lump! + General.WriteLogLine("WARNING: Missing sprite lump '" + Name + "'!"); } - // Read data as bitmap - mem.Seek(0, SeekOrigin.Begin); - bitmap = reader.ReadAsBitmap(mem); + // Pass on to base + base.LoadImage(); } - else - { - // Missing a patch lump! - General.WriteLogLine("WARNING: Missing sprite lump '" + Name + "'!"); - } - - // Pass on to base - base.LoadImage(); } #endregion diff --git a/Source/Data/TextureImage.cs b/Source/Data/TextureImage.cs index 799c14bc..f12dcd5b 100644 --- a/Source/Data/TextureImage.cs +++ b/Source/Data/TextureImage.cs @@ -106,56 +106,59 @@ namespace CodeImp.DoomBuilder.Data // Leave when already loaded if(this.IsLoaded) return; - - // Create texture bitmap - bitmap = new Bitmap(width, height, PixelFormat.Format32bppArgb); - bitmapdata = bitmap.LockBits(new Rectangle(0, 0, width, height), ImageLockMode.WriteOnly, PixelFormat.Format32bppArgb); - pixels = (PixelColor*)bitmapdata.Scan0.ToPointer(); - General.ZeroMemory(new IntPtr(pixels), width * height * sizeof(PixelColor)); - // Go for all patches - foreach(TexturePatch p in patches) + lock(this) { - // Get the patch data stream - patchdata = General.Map.Data.GetPatchData(p.lumpname); - if(patchdata != null) - { - // Copy patch data to memory - patchdata.Seek(0, SeekOrigin.Begin); - membytes = new byte[(int)patchdata.Length]; - patchdata.Read(membytes, 0, (int)patchdata.Length); - mem = new MemoryStream(membytes); - mem.Seek(0, SeekOrigin.Begin); + // Create texture bitmap + bitmap = new Bitmap(width, height, PixelFormat.Format32bppArgb); + bitmapdata = bitmap.LockBits(new Rectangle(0, 0, width, height), ImageLockMode.WriteOnly, PixelFormat.Format32bppArgb); + pixels = (PixelColor*)bitmapdata.Scan0.ToPointer(); + General.ZeroMemory(new IntPtr(pixels), width * height * sizeof(PixelColor)); - // Get a reader for the data - reader = ImageDataFormat.GetImageReader(mem, ImageDataFormat.DOOMPICTURE, General.Map.Data.Palette); - if(reader is UnknownImageReader) - { - // Data is in an unknown format! - General.WriteLogLine("WARNING: Patch lump '" + p.lumpname + "' data format could not be read, while loading texture '" + this.Name + "'!"); - failed = true; - break; - } - - // Draw the patch - mem.Seek(0, SeekOrigin.Begin); - reader.DrawToPixelData(mem, pixels, width, height, p.x, p.y); - } - else + // Go for all patches + foreach(TexturePatch p in patches) { - // Missing a patch lump! - General.WriteLogLine("WARNING: Missing patch lump '" + p.lumpname + "' while loading texture '" + this.Name + "'!"); + // Get the patch data stream + patchdata = General.Map.Data.GetPatchData(p.lumpname); + if(patchdata != null) + { + // Copy patch data to memory + patchdata.Seek(0, SeekOrigin.Begin); + membytes = new byte[(int)patchdata.Length]; + patchdata.Read(membytes, 0, (int)patchdata.Length); + mem = new MemoryStream(membytes); + mem.Seek(0, SeekOrigin.Begin); + + // Get a reader for the data + reader = ImageDataFormat.GetImageReader(mem, ImageDataFormat.DOOMPICTURE, General.Map.Data.Palette); + if(reader is UnknownImageReader) + { + // Data is in an unknown format! + General.WriteLogLine("WARNING: Patch lump '" + p.lumpname + "' data format could not be read, while loading texture '" + this.Name + "'!"); + failed = true; + break; + } + + // Draw the patch + mem.Seek(0, SeekOrigin.Begin); + reader.DrawToPixelData(mem, pixels, width, height, p.x, p.y); + } + else + { + // Missing a patch lump! + General.WriteLogLine("WARNING: Missing patch lump '" + p.lumpname + "' while loading texture '" + this.Name + "'!"); + } } + + // Done + bitmap.UnlockBits(bitmapdata); + + // When failed, use the error picture + if(failed) bitmap = UnknownImageReader.ReadAsBitmap(); + + // Pass on to base + base.LoadImage(); } - - // Done - bitmap.UnlockBits(bitmapdata); - - // When failed, use the error picture - if(failed) bitmap = UnknownImageReader.ReadAsBitmap(); - - // Pass on to base - base.LoadImage(); } #endregion diff --git a/Source/Interface/OpenMapOptionsForm.cs b/Source/Interface/OpenMapOptionsForm.cs index 944a8f80..aaa71022 100644 --- a/Source/Interface/OpenMapOptionsForm.cs +++ b/Source/Interface/OpenMapOptionsForm.cs @@ -52,6 +52,7 @@ namespace CodeImp.DoomBuilder.Interface { // Initialize InitializeComponent(); + this.Text = "Open Map from " + Path.GetFileName(filepathname); this.filepathname = filepathname; this.options = new MapOptions(); }