diff --git a/OpenRA.Game/Game.cs b/OpenRA.Game/Game.cs index bc82294d46..b3ed7c2915 100644 --- a/OpenRA.Game/Game.cs +++ b/OpenRA.Game/Game.cs @@ -271,6 +271,8 @@ namespace OpenRA orderManager.LastTickTime = Environment.TickCount; orderManager.StartGame(); worldRenderer.RefreshPalette(); + + GC.Collect(); } public static bool IsHost diff --git a/OpenRA.Game/Graphics/CursorProvider.cs b/OpenRA.Game/Graphics/CursorProvider.cs index 05a3f082ec..d84fc07ee7 100644 --- a/OpenRA.Game/Graphics/CursorProvider.cs +++ b/OpenRA.Game/Graphics/CursorProvider.cs @@ -46,6 +46,7 @@ namespace OpenRA.Graphics var spriteLoader = new SpriteLoader(new string[0], new SheetBuilder(SheetType.Indexed)); foreach (var s in nodesDict["Cursors"].Nodes) LoadSequencesForCursor(spriteLoader, s.Key, s.Value); + spriteLoader.SheetBuilder.Current.ReleaseBuffer(); palette.Initialize(); } diff --git a/OpenRA.Game/Graphics/Sheet.cs b/OpenRA.Game/Graphics/Sheet.cs index 893d585300..316c5d9790 100644 --- a/OpenRA.Game/Graphics/Sheet.cs +++ b/OpenRA.Game/Graphics/Sheet.cs @@ -18,18 +18,21 @@ namespace OpenRA.Graphics { public class Sheet { - ITexture texture; + readonly object textureLock = new object(); bool dirty; - readonly byte[] data; - readonly object dirtyLock = new object(); + bool releaseBufferOnCommit; + ITexture texture; + byte[] data; public readonly Size Size; public byte[] Data { get { return data ?? texture.GetData(); } } + public bool Buffered { get { return data != null; } } - public Sheet(Size size) + public Sheet(Size size, bool buffered) { Size = size; - data = new byte[4 * Size.Width * Size.Height]; + if (buffered) + data = new byte[4 * Size.Width * Size.Height]; } public Sheet(ITexture texture) @@ -54,6 +57,8 @@ namespace OpenRA.Graphics Marshal.Copy(IntPtr.Add(bd.Scan0, y * bd.Stride), data, y * dataStride, dataStride); bitmap.UnlockBits(bd); } + + ReleaseBuffer(); } public ITexture Texture @@ -62,22 +67,31 @@ namespace OpenRA.Graphics // is set from other threads too via CommitData(). get { - if (texture == null) - { - texture = Game.Renderer.Device.CreateTexture(); - dirty = true; - } + GenerateTexture(); + return texture; + } + } - lock (dirtyLock) + void GenerateTexture() + { + if (texture == null) + { + texture = Game.Renderer.Device.CreateTexture(); + dirty = true; + } + + if (Buffered) + { + lock (textureLock) { if (dirty) { texture.SetData(data, Size.Width, Size.Height); dirty = false; + if (releaseBufferOnCommit) + data = null; } } - - return texture; } } @@ -119,6 +133,7 @@ namespace OpenRA.Graphics } } } + bitmap.UnlockBits(bd); return bitmap; @@ -126,11 +141,20 @@ namespace OpenRA.Graphics public void CommitData() { - if (data == null) - throw new InvalidOperationException("Texture-wrappers are read-only"); + if (!Buffered) + throw new InvalidOperationException( + "This sheet is unbuffered. You cannot call CommitData on an unbuffered sheet. " + + "If you need to completely replace the texture data you should set data into the texture directly. " + + "If you need to make only small changes to the texture data consider creating a buffered sheet instead."); - lock (dirtyLock) + lock (textureLock) dirty = true; } + + public void ReleaseBuffer() + { + lock (textureLock) + releaseBufferOnCommit = true; + } } } diff --git a/OpenRA.Game/Graphics/SheetBuilder.cs b/OpenRA.Game/Graphics/SheetBuilder.cs index 46e57b56f9..45d9148acd 100644 --- a/OpenRA.Game/Graphics/SheetBuilder.cs +++ b/OpenRA.Game/Graphics/SheetBuilder.cs @@ -1,6 +1,6 @@ #region Copyright & License Information /* - * Copyright 2007-2011 The OpenRA Developers (see AUTHORS) + * Copyright 2007-2014 The OpenRA Developers (see AUTHORS) * This file is part of OpenRA, which is free software. It is made * available to you under the terms of the GNU General Public License * as published by the Free Software Foundation. For more information, @@ -17,7 +17,7 @@ namespace OpenRA.Graphics public class SheetOverflowException : Exception { public SheetOverflowException(string message) - : base(message) {} + : base(message) { } } public enum SheetType @@ -38,11 +38,11 @@ namespace OpenRA.Graphics public static Sheet AllocateSheet() { - return new Sheet(new Size(Renderer.SheetSize, Renderer.SheetSize)); + return new Sheet(new Size(Renderer.SheetSize, Renderer.SheetSize), true); } public SheetBuilder(SheetType t) - : this(t, AllocateSheet) {} + : this(t, AllocateSheet) { } public SheetBuilder(SheetType t, Func allocateSheet) { @@ -109,6 +109,7 @@ namespace OpenRA.Graphics var next = NextChannel(channel); if (next == null) { + current.ReleaseBuffer(); current = allocateSheet(); channel = TextureChannel.Red; } @@ -116,7 +117,7 @@ namespace OpenRA.Graphics channel = next.Value; rowHeight = imageSize.Height; - p = new Point(0,0); + p = new Point(0, 0); } var rect = new Sprite(current, new Rectangle(p, imageSize), spriteOffset, channel, BlendMode.Alpha); diff --git a/OpenRA.Game/Graphics/Theater.cs b/OpenRA.Game/Graphics/Theater.cs index 141e3ddd02..e3b962160e 100644 --- a/OpenRA.Game/Graphics/Theater.cs +++ b/OpenRA.Game/Graphics/Theater.cs @@ -1,6 +1,6 @@ #region Copyright & License Information /* - * Copyright 2007-2013 The OpenRA Developers (see AUTHORS) + * Copyright 2007-2014 The OpenRA Developers (see AUTHORS) * This file is part of OpenRA, which is free software. It is made * available to you under the terms of the GNU General Public License * as published by the Free Software Foundation. For more information, @@ -58,7 +58,7 @@ namespace OpenRA.Graphics throw new SheetOverflowException("Terrain sheet overflow. Try increasing the tileset SheetSize parameter."); allocated = true; - return new Sheet(new Size(tileset.SheetSize, tileset.SheetSize)); + return new Sheet(new Size(tileset.SheetSize, tileset.SheetSize), true); }; var sourceCache = new Dictionary(); @@ -69,6 +69,8 @@ namespace OpenRA.Graphics // 1x1px transparent tile missingTile = sheetBuilder.Add(new byte[1], new Size(1, 1)); + + Sheet.ReleaseBuffer(); } public Sprite TileSprite(TerrainTile r) diff --git a/OpenRA.Game/Graphics/VoxelLoader.cs b/OpenRA.Game/Graphics/VoxelLoader.cs index b4f7986a65..7ca9760151 100644 --- a/OpenRA.Game/Graphics/VoxelLoader.cs +++ b/OpenRA.Game/Graphics/VoxelLoader.cs @@ -179,6 +179,7 @@ namespace OpenRA.Graphics { // Sheet overflow - allocate a new sheet and try once more Log.Write("debug", "Voxel sheet overflow! Generating new sheet"); + sheetBuilder.Current.ReleaseBuffer(); sheetBuilder = CreateSheetBuilder(); v = GenerateSlicePlanes(l).SelectMany(x => x).ToArray(); } @@ -223,5 +224,10 @@ namespace OpenRA.Graphics { return voxels[Pair.New(vxl, hva)]; } + + public void Finish() + { + sheetBuilder.Current.ReleaseBuffer(); + } } } diff --git a/OpenRA.Game/ModData.cs b/OpenRA.Game/ModData.cs index 4f8754ab7f..c5b5fc3a0c 100644 --- a/OpenRA.Game/ModData.cs +++ b/OpenRA.Game/ModData.cs @@ -87,7 +87,7 @@ namespace OpenRA FieldLoader.Translations = new Dictionary(); return; } - + var yaml = Manifest.Translations.Select(MiniYaml.FromFile).Aggregate(MiniYaml.MergeLiberal); Languages = yaml.Select(t => t.Key).ToArray(); @@ -100,7 +100,7 @@ namespace OpenRA else if (y.Key == Game.Settings.Graphics.DefaultLanguage) defaultTranslations = y.Value.ToDictionary(my => my.Value ?? ""); } - + var translations = new Dictionary(); foreach (var tkv in defaultTranslations.Concat(selectedTranslations)) { @@ -140,6 +140,7 @@ namespace OpenRA map.SequenceProvider.Preload(); VoxelProvider.Initialize(Manifest.VoxelSequences, map.VoxelSequenceDefinitions); + VoxelLoader.Finish(); return map; } diff --git a/OpenRA.Game/Widgets/VqaPlayerWidget.cs b/OpenRA.Game/Widgets/VqaPlayerWidget.cs index cdc8a37d0b..a3a3c40b04 100644 --- a/OpenRA.Game/Widgets/VqaPlayerWidget.cs +++ b/OpenRA.Game/Widgets/VqaPlayerWidget.cs @@ -1,6 +1,6 @@ #region Copyright & License Information /* - * Copyright 2007-2011 The OpenRA Developers (see AUTHORS) + * Copyright 2007-2014 The OpenRA Developers (see AUTHORS) * This file is part of OpenRA, which is free software. It is made * available to you under the terms of the GNU General Public License * as published by the Free Software Foundation. For more information, @@ -27,14 +27,13 @@ namespace OpenRA.Widgets bool stopped; bool paused; - Action OnComplete; + Action onComplete; public bool Paused { get { return paused; } } - readonly World world; [ObjectCreator.UseCtor] - public VqaPlayerWidget( World world ) + public VqaPlayerWidget(World world) { this.world = world; } @@ -48,33 +47,35 @@ namespace OpenRA.Widgets stopped = true; paused = true; Sound.StopVideo(); - OnComplete = () => {}; + onComplete = () => { }; cachedVideo = filename; video = new VqaReader(GlobalFileSystem.Open(filename)); - invLength = video.Framerate*1f/video.Frames; + invLength = video.Framerate * 1f / video.Frames; var size = Math.Max(video.Width, video.Height); var textureSize = Exts.NextPowerOf2(size); - videoSprite = new Sprite(new Sheet(new Size(textureSize,textureSize)), new Rectangle( 0, 0, video.Width, video.Height ), TextureChannel.Alpha); - videoSprite.sheet.Texture.SetData(video.FrameData); + var videoSheet = new Sheet(new Size(textureSize, textureSize), false); + videoSheet.Texture.SetData(video.FrameData); + videoSprite = new Sprite(videoSheet, new Rectangle(0, 0, video.Width, video.Height), TextureChannel.Alpha); var scale = Math.Min(RenderBounds.Width / video.Width, RenderBounds.Height / video.Height); - videoOrigin = new float2(RenderBounds.X + (RenderBounds.Width - scale*video.Width)/2, RenderBounds.Y + (RenderBounds.Height - scale*video.Height)/2); + videoOrigin = new float2(RenderBounds.X + (RenderBounds.Width - scale * video.Width) / 2, RenderBounds.Y + (RenderBounds.Height - scale * video.Height) / 2); videoSize = new float2(video.Width * scale, video.Height * scale); if (!DrawOverlay) return; - overlay = new uint[2*textureSize, 2*textureSize]; + overlay = new uint[2 * textureSize, 2 * textureSize]; var black = (uint)255 << 24; for (var y = 0; y < video.Height; y++) for (var x = 0; x < video.Width; x++) - overlay[2*y,x] = black; + overlay[2 * y, x] = black; - overlaySprite = new Sprite(new Sheet(new Size(2*textureSize,2*textureSize)), new Rectangle( 0, 0, video.Width, 2*video.Height ), TextureChannel.Alpha); - overlaySprite.sheet.Texture.SetData(overlay); + var overlaySheet = new Sheet(new Size(2 * textureSize, 2 * textureSize), false); + overlaySheet.Texture.SetData(overlay); + overlaySprite = new Sprite(overlaySheet, new Rectangle(0, 0, video.Width, 2 * video.Height), TextureChannel.Alpha); } public override void Draw() @@ -84,7 +85,7 @@ namespace OpenRA.Widgets if (!(stopped || paused)) { - var nextFrame = (int)float2.Lerp(0, video.Frames, Sound.VideoSeekPosition*invLength); + var nextFrame = (int)float2.Lerp(0, video.Frames, Sound.VideoSeekPosition * invLength); if (nextFrame > video.Frames) { Stop(); @@ -115,12 +116,13 @@ namespace OpenRA.Widgets return true; } } + return false; } public void Play() { - PlayThen(() => {}); + PlayThen(() => { }); } public void PlayThen(Action after) @@ -128,7 +130,7 @@ namespace OpenRA.Widgets if (video == null) return; - OnComplete = after; + onComplete = after; if (stopped) Sound.PlayVideo(video.AudioData); else @@ -156,7 +158,7 @@ namespace OpenRA.Widgets Sound.StopVideo(); video.Reset(); videoSprite.sheet.Texture.SetData(video.FrameData); - world.AddFrameEndTask(_ => OnComplete()); + world.AddFrameEndTask(_ => onComplete()); } } } diff --git a/OpenRA.Mods.RA/Widgets/ColorMixerWidget.cs b/OpenRA.Mods.RA/Widgets/ColorMixerWidget.cs index a3807a6ac1..8548301935 100755 --- a/OpenRA.Mods.RA/Widgets/ColorMixerWidget.cs +++ b/OpenRA.Mods.RA/Widgets/ColorMixerWidget.cs @@ -51,8 +51,9 @@ namespace OpenRA.Mods.RA.Widgets back = new byte[4*256*256]; var rect = new Rectangle((int)(255*SRange[0]), (int)(255*(1 - VRange[1])), (int)(255*(SRange[1] - SRange[0]))+1, (int)(255*(VRange[1] - VRange[0])) + 1); - mixerSprite = new Sprite(new Sheet(new Size(256, 256)), rect, TextureChannel.Alpha); - mixerSprite.sheet.Texture.SetData(front, 256, 256); + var mixerSheet = new Sheet(new Size(256, 256), false); + mixerSheet.Texture.SetData(front, 256, 256); + mixerSprite = new Sprite(mixerSheet, rect, TextureChannel.Alpha); } void GenerateBitmap() diff --git a/OpenRA.Mods.RA/Widgets/HueSliderWidget.cs b/OpenRA.Mods.RA/Widgets/HueSliderWidget.cs index bd50749b66..c5e0cf7514 100755 --- a/OpenRA.Mods.RA/Widgets/HueSliderWidget.cs +++ b/OpenRA.Mods.RA/Widgets/HueSliderWidget.cs @@ -1,6 +1,6 @@ #region Copyright & License Information /* - * Copyright 2007-2011 The OpenRA Developers (see AUTHORS) + * Copyright 2007-2014 The OpenRA Developers (see AUTHORS) * This file is part of OpenRA, which is free software. It is made * available to you under the terms of the GNU General Public License * as published by the Free Software Foundation. For more information, @@ -19,8 +19,8 @@ namespace OpenRA.Mods.RA.Widgets { Sprite hueSprite; - public HueSliderWidget() {} - public HueSliderWidget(HueSliderWidget other) : base(other) {} + public HueSliderWidget() { } + public HueSliderWidget(HueSliderWidget other) : base(other) { } public override void Initialize(WidgetArgs args) { @@ -28,7 +28,8 @@ namespace OpenRA.Mods.RA.Widgets using (var hueBitmap = new Bitmap(256, 256)) { - hueSprite = new Sprite(new Sheet(new Size(256, 256)), new Rectangle(0, 0, 256, 1), TextureChannel.Alpha); + var hueSheet = new Sheet(new Size(256, 256), false); + hueSprite = new Sprite(hueSheet, new Rectangle(0, 0, 256, 1), TextureChannel.Alpha); var bitmapData = hueBitmap.LockBits(hueBitmap.Bounds(), ImageLockMode.ReadWrite, PixelFormat.Format32bppArgb); @@ -39,8 +40,7 @@ namespace OpenRA.Mods.RA.Widgets *(c + h) = HSLColor.FromHSV(h / 255f, 1, 1).RGB.ToArgb(); } hueBitmap.UnlockBits(bitmapData); - - hueSprite.sheet.Texture.SetData(hueBitmap); + hueSheet.Texture.SetData(hueBitmap); } } @@ -54,9 +54,8 @@ namespace OpenRA.Mods.RA.Widgets Game.Renderer.RgbaSpriteRenderer.DrawSprite(hueSprite, ro, new float2(rb.Size)); var sprite = ChromeProvider.GetImage("lobby-bits", "huepicker"); - var pos = RenderOrigin + new int2(PxFromValue(Value).Clamp(0, rb.Width-1) - sprite.bounds.Width/2, (rb.Height-sprite.bounds.Height)/2); + var pos = RenderOrigin + new int2(PxFromValue(Value).Clamp(0, rb.Width - 1) - sprite.bounds.Width / 2, (rb.Height - sprite.bounds.Height) / 2); Game.Renderer.RgbaSpriteRenderer.DrawSprite(sprite, pos); } } } - diff --git a/OpenRA.Mods.RA/Widgets/RadarWidget.cs b/OpenRA.Mods.RA/Widgets/RadarWidget.cs index e7b4fdaa96..708c1d1556 100755 --- a/OpenRA.Mods.RA/Widgets/RadarWidget.cs +++ b/OpenRA.Mods.RA/Widgets/RadarWidget.cs @@ -72,13 +72,14 @@ namespace OpenRA.Mods.RA.Widgets { var r = new Rectangle(0, 0, width, height); var s = new Size(terrainBitmap.Width, terrainBitmap.Height); - terrainSprite = new Sprite(new Sheet(s), r, TextureChannel.Alpha); - terrainSprite.sheet.Texture.SetData(terrainBitmap); + var terrainSheet = new Sheet(s, false); + terrainSheet.Texture.SetData(terrainBitmap); + terrainSprite = new Sprite(terrainSheet, r, TextureChannel.Alpha); // Data is set in Tick() - customTerrainSprite = new Sprite(new Sheet(s), r, TextureChannel.Alpha); - actorSprite = new Sprite(new Sheet(s), r, TextureChannel.Alpha); - shroudSprite = new Sprite(new Sheet(s), r, TextureChannel.Alpha); + customTerrainSprite = new Sprite(new Sheet(s, false), r, TextureChannel.Alpha); + actorSprite = new Sprite(new Sheet(s, false), r, TextureChannel.Alpha); + shroudSprite = new Sprite(new Sheet(s, false), r, TextureChannel.Alpha); } }