Make map preview generation fast.

- Change Map.LoadMapTiles and Map.LoadResourceTiles to read the whole stream into memory before processing individual bytes. This removes the cost of significant overhead from repeated calls to ReadUInt8/16.
- Remove significant UI jank caused by the map chooser by not including the placeholder widget. The maps render fast enough that it is no longer worthwhile and it was causing a lot of flushes which were the source of the jank.
- Trigger async generation for all maps when the chooser is loaded. This means in practice all previews will be ready by the time the user begins to scroll the selection. Since generation is fast, there is no issue with scrolling straight to the bottom and having to wait for the backlog to clear.
This commit is contained in:
RoosterDragon
2014-05-31 17:55:56 +01:00
parent ee7573d01c
commit 9dbbc23967
7 changed files with 30 additions and 47 deletions

View File

@@ -355,14 +355,16 @@ namespace OpenRA
throw new InvalidDataException("Invalid tile data"); throw new InvalidDataException("Invalid tile data");
// Load tile data // Load tile data
var data = dataStream.ReadBytes(MapSize.X * MapSize.Y * 3);
var d = 0;
for (int i = 0; i < MapSize.X; i++) for (int i = 0; i < MapSize.X; i++)
for (int j = 0; j < MapSize.Y; j++) for (int j = 0; j < MapSize.Y; j++)
{ {
var tile = dataStream.ReadUInt16(); var tile = BitConverter.ToUInt16(data, d);
var index = dataStream.ReadUInt8(); d += 2;
var index = data[d++];
if (index == byte.MaxValue) if (index == byte.MaxValue)
index = (byte)(i % 4 + (j % 4) * 4); index = (byte)(i % 4 + (j % 4) * 4);
tiles[i, j] = new TileReference<ushort, byte>(tile, index); tiles[i, j] = new TileReference<ushort, byte>(tile, index);
} }
} }
@@ -389,14 +391,12 @@ namespace OpenRA
// Skip past tile data // Skip past tile data
dataStream.Seek(3 * MapSize.X * MapSize.Y, SeekOrigin.Current); dataStream.Seek(3 * MapSize.X * MapSize.Y, SeekOrigin.Current);
var data = dataStream.ReadBytes(MapSize.X * MapSize.Y * 2);
var d = 0;
// Load resource data // Load resource data
for (var i = 0; i < MapSize.X; i++) for (var i = 0; i < MapSize.X; i++)
for (var j = 0; j < MapSize.Y; j++) for (var j = 0; j < MapSize.Y; j++)
{ resources[i, j] = new TileReference<byte, byte>(data[d++], data[d++]);
var type = dataStream.ReadUInt8();
var index = dataStream.ReadUInt8();
resources[i, j] = new TileReference<byte, byte>(type, index);
}
} }
return resources; return resources;

View File

@@ -144,7 +144,7 @@ namespace OpenRA
List<MapPreview> todo; List<MapPreview> todo;
lock (syncRoot) lock (syncRoot)
{ {
todo = generateMinimap.Where(p => p.Minimap == null).ToList(); todo = generateMinimap.Where(p => p.GetMinimap() == null).ToList();
generateMinimap.Clear(); generateMinimap.Clear();
} }
if (todo.Count == 0) if (todo.Count == 0)
@@ -168,7 +168,7 @@ namespace OpenRA
// the next render cycle. // the next render cycle.
// (d) Any partially written bytes from the next minimap is in an // (d) Any partially written bytes from the next minimap is in an
// unallocated area, and will be committed in the next cycle. // unallocated area, and will be committed in the next cycle.
p.Minimap = sheetBuilder.Add(bitmap); p.SetMinimap(sheetBuilder.Add(bitmap));
// Yuck... But this helps the UI Jank when opening the map selector significantly. // Yuck... But this helps the UI Jank when opening the map selector significantly.
Thread.Sleep(Environment.ProcessorCount == 1 ? 25 : 5); Thread.Sleep(Environment.ProcessorCount == 1 ? 25 : 5);

View File

@@ -69,27 +69,24 @@ namespace OpenRA
Sprite minimap; Sprite minimap;
bool generatingMinimap; bool generatingMinimap;
public Sprite Minimap public Sprite GetMinimap()
{ {
get if (minimap != null)
return minimap;
if (!generatingMinimap && Status == MapStatus.Available)
{ {
if (minimap != null) generatingMinimap = true;
return minimap; cache.CacheMinimap(this);
if (!generatingMinimap && Status == MapStatus.Available)
{
generatingMinimap = true;
cache.CacheMinimap(this);
}
return null;
} }
set return null;
{ }
minimap = value;
generatingMinimap = false; public void SetMinimap(Sprite minimap)
} {
this.minimap = minimap;
generatingMinimap = false;
} }
public MapPreview(string uid, MapCache cache) public MapPreview(string uid, MapCache cache)

View File

@@ -124,7 +124,7 @@ namespace OpenRA.Widgets
// Stash a copy of the minimap to ensure consistency // Stash a copy of the minimap to ensure consistency
// (it may be modified by another thread) // (it may be modified by another thread)
minimap = preview.Minimap; minimap = preview.GetMinimap();
if (minimap == null) if (minimap == null)
return; return;

View File

@@ -96,7 +96,12 @@ namespace OpenRA.Mods.RA.Widgets.Logic
foreach (var loop in maps) foreach (var loop in maps)
{ {
var preview = loop; var preview = loop;
// Access the minimap to trigger async generation of the minimap.
preview.GetMinimap();
var item = ScrollItemWidget.Setup(preview.Uid, itemTemplate, () => selectedUid == preview.Uid, () => selectedUid = preview.Uid, () => { Ui.CloseWindow(); onSelect(preview.Uid); }); var item = ScrollItemWidget.Setup(preview.Uid, itemTemplate, () => selectedUid == preview.Uid, () => selectedUid = preview.Uid, () => { Ui.CloseWindow(); onSelect(preview.Uid); });
item.IsVisible = () => item.RenderBounds.IntersectsWith(scrollpanel.RenderBounds);
var titleLabel = item.Get<LabelWidget>("TITLE"); var titleLabel = item.Get<LabelWidget>("TITLE");
titleLabel.GetText = () => preview.Title; titleLabel.GetText = () => preview.Title;
@@ -105,11 +110,6 @@ namespace OpenRA.Mods.RA.Widgets.Logic
previewWidget.IgnoreMouseOver = true; previewWidget.IgnoreMouseOver = true;
previewWidget.IgnoreMouseInput = true; previewWidget.IgnoreMouseInput = true;
previewWidget.Preview = () => preview; previewWidget.Preview = () => preview;
previewWidget.IsVisible = () => previewWidget.RenderBounds.IntersectsWith(scrollpanel.RenderBounds);
var previewLoadingWidget = item.GetOrNull<BackgroundWidget>("PREVIEW_PLACEHOLDER");
if (previewLoadingWidget != null)
previewLoadingWidget.IsVisible = () => !previewWidget.Loaded;
var detailsWidget = item.GetOrNull<LabelWidget>("DETAILS"); var detailsWidget = item.GetOrNull<LabelWidget>("DETAILS");
if (detailsWidget != null) if (detailsWidget != null)

View File

@@ -43,13 +43,6 @@ Container@MAPCHOOSER_PANEL:
Y: 0 Y: 0
Visible: false Visible: false
Children: Children:
Background@PREVIEW_PLACEHOLDER:
X: (PARENT_RIGHT - WIDTH)/2
Y: 4
Width: 173
Height: 173
Background: panel-black
ClickThrough: false
MapPreview@PREVIEW: MapPreview@PREVIEW:
X: (PARENT_RIGHT - WIDTH)/2 X: (PARENT_RIGHT - WIDTH)/2
Y: 4 Y: 4

View File

@@ -26,13 +26,6 @@ Background@MAPCHOOSER_PANEL:
Y: 0 Y: 0
Visible: false Visible: false
Children: Children:
Background@PREVIEW_PLACEHOLDER:
X: (PARENT_RIGHT - WIDTH)/2
Y: 4
Width: 184
Height: 184
Background: dialog3
ClickThrough: true
MapPreview@PREVIEW: MapPreview@PREVIEW:
X: (PARENT_RIGHT - WIDTH)/2 X: (PARENT_RIGHT - WIDTH)/2
Y: 4 Y: 4