Move threading into the preview generation.
This commit is contained in:
@@ -12,6 +12,7 @@ using System;
|
|||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Drawing;
|
using System.Drawing;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
using System.Threading;
|
||||||
using OpenRA.FileFormats;
|
using OpenRA.FileFormats;
|
||||||
using OpenRA.Graphics;
|
using OpenRA.Graphics;
|
||||||
|
|
||||||
@@ -26,8 +27,6 @@ namespace OpenRA.Widgets
|
|||||||
public bool IgnoreMouseInput = false;
|
public bool IgnoreMouseInput = false;
|
||||||
public bool ShowSpawnPoints = true;
|
public bool ShowSpawnPoints = true;
|
||||||
|
|
||||||
static readonly Cache<Map,Bitmap> PreviewCache = new Cache<Map, Bitmap>(stub => Minimap.RenderMapPreview( new Map( stub.Path )));
|
|
||||||
|
|
||||||
public MapPreviewWidget() : base() { }
|
public MapPreviewWidget() : base() { }
|
||||||
|
|
||||||
protected MapPreviewWidget(MapPreviewWidget other)
|
protected MapPreviewWidget(MapPreviewWidget other)
|
||||||
@@ -68,19 +67,30 @@ namespace OpenRA.Widgets
|
|||||||
public override void Draw()
|
public override void Draw()
|
||||||
{
|
{
|
||||||
var map = Map();
|
var map = Map();
|
||||||
if( map == null ) return;
|
if (map == null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
// Preview unavailable
|
||||||
|
if (!Loaded)
|
||||||
|
{
|
||||||
|
GeneratePreview();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (lastMap != map)
|
if (lastMap != map)
|
||||||
{
|
{
|
||||||
lastMap = map;
|
lastMap = map;
|
||||||
|
|
||||||
// Update image data
|
// Update image data
|
||||||
var preview = PreviewCache[map];
|
Bitmap preview;
|
||||||
if( mapChooserSheet == null || mapChooserSheet.Size.Width != preview.Width || mapChooserSheet.Size.Height != preview.Height )
|
lock (syncRoot)
|
||||||
mapChooserSheet = new Sheet(new Size( preview.Width, preview.Height ) );
|
preview = Previews[map.Uid];
|
||||||
|
|
||||||
mapChooserSheet.Texture.SetData( preview );
|
if (mapChooserSheet == null || mapChooserSheet.Size.Width != preview.Width || mapChooserSheet.Size.Height != preview.Height)
|
||||||
mapChooserSprite = new Sprite( mapChooserSheet, new Rectangle( 0, 0, map.Bounds.Width, map.Bounds.Height ), TextureChannel.Alpha );
|
mapChooserSheet = new Sheet(new Size(preview.Width, preview.Height));
|
||||||
|
|
||||||
|
mapChooserSheet.Texture.SetData(preview);
|
||||||
|
mapChooserSprite = new Sprite(mapChooserSheet, new Rectangle(0, 0, map.Bounds.Width, map.Bounds.Height), TextureChannel.Alpha);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Update map rect
|
// Update map rect
|
||||||
@@ -90,9 +100,9 @@ namespace OpenRA.Widgets
|
|||||||
var dh = (int)(PreviewScale * (size - map.Bounds.Height)) / 2;
|
var dh = (int)(PreviewScale * (size - map.Bounds.Height)) / 2;
|
||||||
MapRect = new Rectangle(RenderBounds.X + dw, RenderBounds.Y + dh, (int)(map.Bounds.Width * PreviewScale), (int)(map.Bounds.Height * PreviewScale));
|
MapRect = new Rectangle(RenderBounds.X + dw, RenderBounds.Y + dh, (int)(map.Bounds.Width * PreviewScale), (int)(map.Bounds.Height * PreviewScale));
|
||||||
|
|
||||||
Game.Renderer.RgbaSpriteRenderer.DrawSprite( mapChooserSprite,
|
Game.Renderer.RgbaSpriteRenderer.DrawSprite(mapChooserSprite,
|
||||||
new float2(MapRect.Location),
|
new float2(MapRect.Location),
|
||||||
new float2( MapRect.Size ) );
|
new float2(MapRect.Size));
|
||||||
|
|
||||||
if (ShowSpawnPoints)
|
if (ShowSpawnPoints)
|
||||||
{
|
{
|
||||||
@@ -119,15 +129,70 @@ namespace OpenRA.Widgets
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
// Async map preview generation bits
|
||||||
/// Forces loading the preview into the map cache.
|
enum PreviewStatus { Invalid, Uncached, Generating, Cached }
|
||||||
/// </summary>
|
static Thread previewLoaderThread;
|
||||||
public Bitmap LoadMapPreview()
|
static object syncRoot = new object();
|
||||||
{
|
static Queue<string> cacheUids = new Queue<string>();
|
||||||
var map = Map();
|
static readonly Dictionary<string, Bitmap> Previews = new Dictionary<string, Bitmap>();
|
||||||
if( map == null ) return null;
|
|
||||||
|
|
||||||
return PreviewCache[map];
|
void LoadAsyncInternal()
|
||||||
|
{
|
||||||
|
for (;;)
|
||||||
|
{
|
||||||
|
string uid;
|
||||||
|
lock (syncRoot)
|
||||||
|
{
|
||||||
|
if (cacheUids.Count == 0)
|
||||||
|
break;
|
||||||
|
uid = cacheUids.Peek();
|
||||||
|
}
|
||||||
|
|
||||||
|
var bitmap = Minimap.RenderMapPreview(Game.modData.AvailableMaps[uid]);
|
||||||
|
lock (syncRoot)
|
||||||
|
{
|
||||||
|
// TODO: We should add previews to a sheet here (with multiple previews per sheet)
|
||||||
|
Previews.Add(uid, bitmap);
|
||||||
|
cacheUids.Dequeue();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void GeneratePreview()
|
||||||
|
{
|
||||||
|
var m = Map();
|
||||||
|
if (m == null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
var status = Status(m);
|
||||||
|
if (status == PreviewStatus.Uncached)
|
||||||
|
lock (syncRoot)
|
||||||
|
cacheUids.Enqueue(m.Uid);
|
||||||
|
|
||||||
|
if (previewLoaderThread == null || !previewLoaderThread.IsAlive)
|
||||||
|
{
|
||||||
|
previewLoaderThread = new Thread(LoadAsyncInternal);
|
||||||
|
previewLoaderThread.Priority = ThreadPriority.Lowest;
|
||||||
|
previewLoaderThread.Start();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static PreviewStatus Status(Map m)
|
||||||
|
{
|
||||||
|
if (m == null)
|
||||||
|
return PreviewStatus.Invalid;
|
||||||
|
|
||||||
|
lock (syncRoot)
|
||||||
|
{
|
||||||
|
if (Previews.ContainsKey(m.Uid))
|
||||||
|
return PreviewStatus.Cached;
|
||||||
|
|
||||||
|
if (cacheUids.Contains(m.Uid))
|
||||||
|
return PreviewStatus.Generating;
|
||||||
|
}
|
||||||
|
return PreviewStatus.Uncached;
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool Loaded { get { return Status(Map()) == PreviewStatus.Cached; } }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -27,7 +27,6 @@ namespace OpenRA.Mods.RA.Widgets.Logic
|
|||||||
ScrollPanelWidget scrollpanel;
|
ScrollPanelWidget scrollpanel;
|
||||||
ScrollItemWidget itemTemplate;
|
ScrollItemWidget itemTemplate;
|
||||||
string gameMode;
|
string gameMode;
|
||||||
Thread mapLoaderThread;
|
|
||||||
|
|
||||||
[ObjectCreator.UseCtor]
|
[ObjectCreator.UseCtor]
|
||||||
internal MapChooserLogic(Widget widget, string initialMap, Action onExit, Action<Map> onSelect)
|
internal MapChooserLogic(Widget widget, string initialMap, Action onExit, Action<Map> onSelect)
|
||||||
@@ -61,7 +60,7 @@ namespace OpenRA.Mods.RA.Widgets.Logic
|
|||||||
{
|
{
|
||||||
var item = ScrollItemWidget.Setup(template,
|
var item = ScrollItemWidget.Setup(template,
|
||||||
() => gameMode == ii.First,
|
() => gameMode == ii.First,
|
||||||
() => { gameMode = ii.First; EnumerateMapsAsync(); });
|
() => { gameMode = ii.First; EnumerateMaps(); });
|
||||||
item.Get<LabelWidget>("LABEL").GetText = () => showItem(ii);
|
item.Get<LabelWidget>("LABEL").GetText = () => showItem(ii);
|
||||||
return item;
|
return item;
|
||||||
};
|
};
|
||||||
@@ -84,16 +83,7 @@ namespace OpenRA.Mods.RA.Widgets.Logic
|
|||||||
randomMapButton.IsDisabled = () => visibleMaps == null || visibleMaps.Count == 0;
|
randomMapButton.IsDisabled = () => visibleMaps == null || visibleMaps.Count == 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
EnumerateMapsAsync();
|
EnumerateMaps();
|
||||||
}
|
|
||||||
|
|
||||||
void EnumerateMapsAsync()
|
|
||||||
{
|
|
||||||
if (mapLoaderThread != null && mapLoaderThread.IsAlive)
|
|
||||||
mapLoaderThread.Abort(); // violent, but should be fine since we are not doing anything sensitive in this thread
|
|
||||||
|
|
||||||
mapLoaderThread = new Thread(EnumerateMaps);
|
|
||||||
mapLoaderThread.Start();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void EnumerateMaps()
|
void EnumerateMaps()
|
||||||
@@ -104,7 +94,7 @@ namespace OpenRA.Mods.RA.Widgets.Logic
|
|||||||
.OrderBy(kv => kv.Value.PlayerCount)
|
.OrderBy(kv => kv.Value.PlayerCount)
|
||||||
.ThenBy(kv => kv.Value.Title);
|
.ThenBy(kv => kv.Value.Title);
|
||||||
|
|
||||||
var children = new List<ScrollItemWidget>();
|
scrollpanel.RemoveChildren();
|
||||||
foreach (var kv in maps)
|
foreach (var kv in maps)
|
||||||
{
|
{
|
||||||
var m = kv.Value;
|
var m = kv.Value;
|
||||||
@@ -117,17 +107,20 @@ namespace OpenRA.Mods.RA.Widgets.Logic
|
|||||||
previewWidget.IgnoreMouseOver = true;
|
previewWidget.IgnoreMouseOver = true;
|
||||||
previewWidget.IgnoreMouseInput = true;
|
previewWidget.IgnoreMouseInput = true;
|
||||||
previewWidget.Map = () => m;
|
previewWidget.Map = () => m;
|
||||||
previewWidget.LoadMapPreview();
|
|
||||||
|
|
||||||
var detailsWidget = item.Get<LabelWidget>("DETAILS");
|
var previewLoadingWidget = item.GetOrNull<BackgroundWidget>("PREVIEW_PLACEHOLDER");
|
||||||
|
if (previewLoadingWidget != null)
|
||||||
|
previewLoadingWidget.IsVisible = () => !previewWidget.Loaded;
|
||||||
|
|
||||||
|
var detailsWidget = item.GetOrNull<LabelWidget>("DETAILS");
|
||||||
if (detailsWidget != null)
|
if (detailsWidget != null)
|
||||||
detailsWidget.GetText = () => "{0} ({1})".F(m.Type, m.PlayerCount);
|
detailsWidget.GetText = () => "{0} ({1})".F(m.Type, m.PlayerCount);
|
||||||
|
|
||||||
var authorWidget = item.Get<LabelWidget>("AUTHOR");
|
var authorWidget = item.GetOrNull<LabelWidget>("AUTHOR");
|
||||||
if (authorWidget != null)
|
if (authorWidget != null)
|
||||||
authorWidget.GetText = () => m.Author;
|
authorWidget.GetText = () => m.Author;
|
||||||
|
|
||||||
var sizeWidget = item.Get<LabelWidget>("SIZE");
|
var sizeWidget = item.GetOrNull<LabelWidget>("SIZE");
|
||||||
if (sizeWidget != null)
|
if (sizeWidget != null)
|
||||||
{
|
{
|
||||||
var size = m.Bounds.Width + "x" + m.Bounds.Height;
|
var size = m.Bounds.Width + "x" + m.Bounds.Height;
|
||||||
@@ -139,20 +132,12 @@ namespace OpenRA.Mods.RA.Widgets.Logic
|
|||||||
sizeWidget.GetText = () => size;
|
sizeWidget.GetText = () => size;
|
||||||
}
|
}
|
||||||
|
|
||||||
children.Add(item);
|
scrollpanel.AddChild(item);
|
||||||
}
|
}
|
||||||
|
|
||||||
Game.RunAfterTick(() =>
|
visibleMaps = maps.ToDictionary(kv => kv.Key, kv => kv.Value);
|
||||||
{
|
if (visibleMaps.ContainsValue(map))
|
||||||
scrollpanel.RemoveChildren();
|
scrollpanel.ScrollToItem(visibleMaps.First(m => m.Value == map).Key);
|
||||||
|
|
||||||
foreach (var c in children)
|
|
||||||
scrollpanel.AddChild(c);
|
|
||||||
|
|
||||||
visibleMaps = maps.ToDictionary(kv => kv.Key, kv => kv.Value);
|
|
||||||
if (visibleMaps.ContainsValue(map))
|
|
||||||
scrollpanel.ScrollToItem(visibleMaps.First(m => m.Value == map).Key);
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -43,6 +43,13 @@ Container@MAPCHOOSER_PANEL:
|
|||||||
Y:0
|
Y:0
|
||||||
Visible:false
|
Visible:false
|
||||||
Children:
|
Children:
|
||||||
|
Background@PREVIEW_PLACEHOLDER:
|
||||||
|
X:(PARENT_RIGHT - WIDTH)/2
|
||||||
|
Y:4
|
||||||
|
Width:158
|
||||||
|
Height:158
|
||||||
|
Background:panel-black
|
||||||
|
ClickThrough: false
|
||||||
MapPreview@PREVIEW:
|
MapPreview@PREVIEW:
|
||||||
X:(PARENT_RIGHT - WIDTH)/2
|
X:(PARENT_RIGHT - WIDTH)/2
|
||||||
Y:4
|
Y:4
|
||||||
|
|||||||
Reference in New Issue
Block a user