Merge pull request #5505 from pavlos256/map-preview-generation

Map preview generation
This commit is contained in:
Paul Chote
2014-06-10 17:56:21 +12:00
2 changed files with 50 additions and 32 deletions

View File

@@ -21,6 +21,7 @@ namespace OpenRA.Graphics
ITexture texture; ITexture texture;
bool dirty; bool dirty;
byte[] data; byte[] data;
readonly object dirtyLock = new object();
public readonly Size Size; public readonly Size Size;
public byte[] Data { get { return data ?? texture.GetData(); } } public byte[] Data { get { return data ?? texture.GetData(); } }
@@ -68,6 +69,8 @@ namespace OpenRA.Graphics
public ITexture Texture public ITexture Texture
{ {
// This is only called from the main thread but 'dirty'
// is set from other threads too via CommitData().
get get
{ {
if (texture == null) if (texture == null)
@@ -76,10 +79,13 @@ namespace OpenRA.Graphics
dirty = true; dirty = true;
} }
if (dirty) lock (dirtyLock)
{ {
texture.SetData(data, Size.Width, Size.Height); if (dirty)
dirty = false; {
texture.SetData(data, Size.Width, Size.Height);
dirty = false;
}
} }
return texture; return texture;
@@ -140,7 +146,10 @@ namespace OpenRA.Graphics
if (data == null) if (data == null)
throw new InvalidOperationException("Texture-wrappers are read-only"); throw new InvalidOperationException("Texture-wrappers are read-only");
dirty = true; lock (dirtyLock)
{
dirty = true;
}
} }
} }
} }

View File

@@ -131,42 +131,50 @@ namespace OpenRA
void LoadAsyncInternal() void LoadAsyncInternal()
{ {
for (;;) Log.Write("debug", "MapCache.LoadAsyncInternal started");
// Milliseconds to wait on one loop when nothing to do
var emptyDelay = 50;
// Keep the thread alive for at least 5 seconds after the last minimap generation
var maxKeepAlive = 5000 / emptyDelay;
var keepAlive = maxKeepAlive;
while (keepAlive-- > 0)
{ {
MapPreview p; List<MapPreview> todo;
lock (syncRoot) lock (syncRoot)
{ {
if (generateMinimap.Count == 0) todo = generateMinimap.Where(p => p.Minimap == null).ToList();
break; generateMinimap.Clear();
p = generateMinimap.Peek();
// Preview already exists
if (p.Minimap != null)
{
generateMinimap.Dequeue();
continue;
}
} }
if (todo.Count == 0)
{
Thread.Sleep(emptyDelay);
continue;
}
else
keepAlive = maxKeepAlive;
// Render the minimap into the shared sheet // Render the minimap into the shared sheet
// Note: this is not generally thread-safe, but it works here because: foreach (var p in todo)
// (a) This worker is the only thread writing to this sheet {
// (b) The main thread is the only thread reading this sheet // The rendering is thread safe because it only reads from the passed instances and writes to a new bitmap
// (c) The sheet is marked dirty after the write is completed, var bitmap = p.CustomPreview ?? Minimap.RenderMapPreview(modData.DefaultRules.TileSets[p.Map.Tileset], p.Map, modData.DefaultRules, true);
// which causes the main thread to copy this to the texture during // Note: this is not generally thread-safe, but it works here because:
// the next render cycle. // (a) This worker is the only thread writing to this sheet
// (d) Any partially written bytes from the next minimap is in an // (b) The main thread is the only thread reading this sheet
// unallocated area, and will be committed in the next cycle. // (c) The sheet is marked dirty after the write is completed,
var bitmap = p.CustomPreview ?? Minimap.RenderMapPreview(modData.DefaultRules.TileSets[p.Map.Tileset], p.Map, modData.DefaultRules, true); // which causes the main thread to copy this to the texture during
p.Minimap = sheetBuilder.Add(bitmap); // the next render cycle.
// (d) Any partially written bytes from the next minimap is in an
// unallocated area, and will be committed in the next cycle.
p.Minimap = sheetBuilder.Add(bitmap);
lock (syncRoot) // Yuck... But this helps the UI Jank when opening the map selector significantly.
generateMinimap.Dequeue(); Thread.Sleep(Environment.ProcessorCount == 1 ? 25 : 5);
};
// Yuck... But this helps the UI Jank when opening the map selector significantly.
Thread.Sleep(50);
} }
Log.Write("debug", "MapCache.LoadAsyncInternal ended");
} }
public void CacheMinimap(MapPreview preview) public void CacheMinimap(MapPreview preview)
@@ -177,6 +185,7 @@ namespace OpenRA
if (previewLoaderThread == null || !previewLoaderThread.IsAlive) if (previewLoaderThread == null || !previewLoaderThread.IsAlive)
{ {
previewLoaderThread = new Thread(LoadAsyncInternal); previewLoaderThread = new Thread(LoadAsyncInternal);
previewLoaderThread.IsBackground = true;
previewLoaderThread.Start(); previewLoaderThread.Start();
} }
} }