Improved efficiency of startup methods.
- ShpReader will copy the input stream into memory just once rather than for every header. - ShpReader.CopyImageData switched to use Array.Copy since that uses some unsafe magic for speed. - In ActorInfo, cache a GetType call and prevent needless materialization in PrerequisitesOf. - In ObjectCreator, cache type and ctor lookups since these are expensive and often repeated. - Implement IReadOnlyDictionary<T, U> on Cache<T, U> to provide some supplementary functions. - In TechTree.GatherOwnedPrerequisites, rearrange a Boolean 'and' expression to evaluate expensive functions later in the chain, and use ContainsKey to speed up name check.
This commit is contained in:
@@ -12,58 +12,58 @@ namespace OpenRA.FileFormats
|
|||||||
{
|
{
|
||||||
public static class Format40
|
public static class Format40
|
||||||
{
|
{
|
||||||
public static int DecodeInto( byte[] src, byte[] dest )
|
public static int DecodeInto(byte[] src, byte[] dest, int srcOffset)
|
||||||
{
|
{
|
||||||
var ctx = new FastByteReader(src);
|
var ctx = new FastByteReader(src, srcOffset);
|
||||||
int destIndex = 0;
|
int destIndex = 0;
|
||||||
|
|
||||||
while( true )
|
while (true)
|
||||||
{
|
{
|
||||||
byte i = ctx.ReadByte();
|
byte i = ctx.ReadByte();
|
||||||
if( ( i & 0x80 ) == 0 )
|
if ((i & 0x80) == 0)
|
||||||
{
|
{
|
||||||
int count = i & 0x7F;
|
int count = i & 0x7F;
|
||||||
if( count == 0 )
|
if (count == 0)
|
||||||
{
|
{
|
||||||
// case 6
|
// case 6
|
||||||
count = ctx.ReadByte();
|
count = ctx.ReadByte();
|
||||||
byte value = ctx.ReadByte();
|
byte value = ctx.ReadByte();
|
||||||
for( int end = destIndex + count ; destIndex < end ; destIndex++ )
|
for (int end = destIndex + count; destIndex < end; destIndex++)
|
||||||
dest[ destIndex ] ^= value;
|
dest[destIndex] ^= value;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
// case 5
|
// case 5
|
||||||
for( int end = destIndex + count ; destIndex < end ; destIndex++ )
|
for (int end = destIndex + count; destIndex < end; destIndex++)
|
||||||
dest[destIndex] ^= ctx.ReadByte();
|
dest[destIndex] ^= ctx.ReadByte();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
int count = i & 0x7F;
|
int count = i & 0x7F;
|
||||||
if( count == 0 )
|
if (count == 0)
|
||||||
{
|
{
|
||||||
count = ctx.ReadWord();
|
count = ctx.ReadWord();
|
||||||
if( count == 0 )
|
if (count == 0)
|
||||||
return destIndex;
|
return destIndex;
|
||||||
|
|
||||||
if( ( count & 0x8000 ) == 0 )
|
if ((count & 0x8000) == 0)
|
||||||
{
|
{
|
||||||
// case 2
|
// case 2
|
||||||
destIndex += ( count & 0x7FFF );
|
destIndex += (count & 0x7FFF);
|
||||||
}
|
}
|
||||||
else if( ( count & 0x4000 ) == 0 )
|
else if ((count & 0x4000) == 0)
|
||||||
{
|
{
|
||||||
// case 3
|
// case 3
|
||||||
for( int end = destIndex + ( count & 0x3FFF ) ; destIndex < end ; destIndex++ )
|
for (int end = destIndex + (count & 0x3FFF); destIndex < end; destIndex++)
|
||||||
dest[destIndex] ^= ctx.ReadByte();
|
dest[destIndex] ^= ctx.ReadByte();
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
// case 4
|
// case 4
|
||||||
byte value = ctx.ReadByte();
|
byte value = ctx.ReadByte();
|
||||||
for( int end = destIndex + ( count & 0x3FFF ) ; destIndex < end ; destIndex++ )
|
for (int end = destIndex + (count & 0x3FFF); destIndex < end; destIndex++)
|
||||||
dest[ destIndex ] ^= value;
|
dest[destIndex] ^= value;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
|||||||
@@ -16,11 +16,12 @@ namespace OpenRA.FileFormats
|
|||||||
class FastByteReader
|
class FastByteReader
|
||||||
{
|
{
|
||||||
readonly byte[] src;
|
readonly byte[] src;
|
||||||
int offset = 0;
|
int offset;
|
||||||
|
|
||||||
public FastByteReader(byte[] src)
|
public FastByteReader(byte[] src, int offset = 0)
|
||||||
{
|
{
|
||||||
this.src = src;
|
this.src = src;
|
||||||
|
this.offset = offset;
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool Done() { return offset >= src.Length; }
|
public bool Done() { return offset >= src.Length; }
|
||||||
@@ -59,9 +60,9 @@ namespace OpenRA.FileFormats
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static int DecodeInto(byte[] src, byte[] dest)
|
public static int DecodeInto(byte[] src, byte[] dest, int srcOffset = 0)
|
||||||
{
|
{
|
||||||
var ctx = new FastByteReader(src);
|
var ctx = new FastByteReader(src, srcOffset);
|
||||||
var destIndex = 0;
|
var destIndex = 0;
|
||||||
|
|
||||||
while (true)
|
while (true)
|
||||||
|
|||||||
@@ -67,6 +67,9 @@ namespace OpenRA.FileFormats
|
|||||||
int recurseDepth = 0;
|
int recurseDepth = 0;
|
||||||
readonly int imageCount;
|
readonly int imageCount;
|
||||||
|
|
||||||
|
readonly long shpBytesFileOffset;
|
||||||
|
readonly byte[] shpBytes;
|
||||||
|
|
||||||
public ShpReader(Stream stream)
|
public ShpReader(Stream stream)
|
||||||
{
|
{
|
||||||
imageCount = stream.ReadUInt16();
|
imageCount = stream.ReadUInt16();
|
||||||
@@ -93,22 +96,16 @@ namespace OpenRA.FileFormats
|
|||||||
throw new InvalidDataException("Reference doesnt point to image data {0}->{1}".F(h.FileOffset, h.RefOffset));
|
throw new InvalidDataException("Reference doesnt point to image data {0}->{1}".F(h.FileOffset, h.RefOffset));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
shpBytesFileOffset = stream.Position;
|
||||||
|
shpBytes = stream.ReadBytes((int)(stream.Length - stream.Position));
|
||||||
|
|
||||||
foreach (var h in headers)
|
foreach (var h in headers)
|
||||||
Decompress(stream, h);
|
Decompress(h);
|
||||||
|
|
||||||
spriteFrames = Exts.Lazy(() => headers.Cast<ISpriteFrame>());
|
spriteFrames = Exts.Lazy(() => headers.Cast<ISpriteFrame>());
|
||||||
}
|
}
|
||||||
|
|
||||||
static byte[] ReadCompressedData(Stream stream, ImageHeader h)
|
void Decompress(ImageHeader h)
|
||||||
{
|
|
||||||
stream.Position = h.FileOffset;
|
|
||||||
|
|
||||||
// Actually, far too big. There's no length field with the correct length though :(
|
|
||||||
var compressedLength = (int)(stream.Length - stream.Position);
|
|
||||||
return stream.ReadBytes(compressedLength);
|
|
||||||
}
|
|
||||||
|
|
||||||
void Decompress(Stream stream, ImageHeader h)
|
|
||||||
{
|
{
|
||||||
// No extra work is required for empty frames
|
// No extra work is required for empty frames
|
||||||
if (h.Size.Width == 0 || h.Size.Height == 0)
|
if (h.Size.Width == 0 || h.Size.Height == 0)
|
||||||
@@ -125,19 +122,19 @@ namespace OpenRA.FileFormats
|
|||||||
if (h.RefImage.Data == null)
|
if (h.RefImage.Data == null)
|
||||||
{
|
{
|
||||||
++recurseDepth;
|
++recurseDepth;
|
||||||
Decompress(stream, h.RefImage);
|
Decompress(h.RefImage);
|
||||||
--recurseDepth;
|
--recurseDepth;
|
||||||
}
|
}
|
||||||
|
|
||||||
h.Data = CopyImageData(h.RefImage.Data);
|
h.Data = CopyImageData(h.RefImage.Data);
|
||||||
Format40.DecodeInto(ReadCompressedData(stream, h), h.Data);
|
Format40.DecodeInto(shpBytes, h.Data, (int)(h.FileOffset - shpBytesFileOffset));
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
case Format.Format80:
|
case Format.Format80:
|
||||||
{
|
{
|
||||||
var imageBytes = new byte[Size.Width * Size.Height];
|
var imageBytes = new byte[Size.Width * Size.Height];
|
||||||
Format80.DecodeInto(ReadCompressedData(stream, h), imageBytes);
|
Format80.DecodeInto(shpBytes, imageBytes, (int)(h.FileOffset - shpBytesFileOffset));
|
||||||
h.Data = imageBytes;
|
h.Data = imageBytes;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -150,9 +147,7 @@ namespace OpenRA.FileFormats
|
|||||||
byte[] CopyImageData(byte[] baseImage)
|
byte[] CopyImageData(byte[] baseImage)
|
||||||
{
|
{
|
||||||
var imageData = new byte[Size.Width * Size.Height];
|
var imageData = new byte[Size.Width * Size.Height];
|
||||||
for (var i = 0; i < Size.Width * Size.Height; i++)
|
Array.Copy(baseImage, imageData, imageData.Length);
|
||||||
imageData[i] = baseImage[i];
|
|
||||||
|
|
||||||
return imageData;
|
return imageData;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -94,7 +94,11 @@ namespace OpenRA
|
|||||||
while (t.Count != 0)
|
while (t.Count != 0)
|
||||||
{
|
{
|
||||||
var prereqs = PrerequisitesOf(t[index]);
|
var prereqs = PrerequisitesOf(t[index]);
|
||||||
var unsatisfied = prereqs.Where(n => !ret.Any(x => x.GetType() == n || n.IsAssignableFrom(x.GetType())));
|
var unsatisfied = prereqs.Where(n => !ret.Any(x =>
|
||||||
|
{
|
||||||
|
var type = x.GetType();
|
||||||
|
return type == n || n.IsAssignableFrom(type);
|
||||||
|
}));
|
||||||
if (!unsatisfied.Any())
|
if (!unsatisfied.Any())
|
||||||
{
|
{
|
||||||
ret.Add(t[index]);
|
ret.Add(t[index]);
|
||||||
@@ -111,14 +115,13 @@ namespace OpenRA
|
|||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
static List<Type> PrerequisitesOf(ITraitInfo info)
|
static IEnumerable<Type> PrerequisitesOf(ITraitInfo info)
|
||||||
{
|
{
|
||||||
return info
|
return info
|
||||||
.GetType()
|
.GetType()
|
||||||
.GetInterfaces()
|
.GetInterfaces()
|
||||||
.Where( t => t.IsGenericType && t.GetGenericTypeDefinition() == typeof( Requires<> ) )
|
.Where(t => t.IsGenericType && t.GetGenericTypeDefinition() == typeof(Requires<>))
|
||||||
.Select( t => t.GetGenericArguments()[ 0 ] )
|
.Select(t => t.GetGenericArguments()[0]);
|
||||||
.ToList();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public IEnumerable<Pair<string, Type>> GetInitKeys()
|
public IEnumerable<Pair<string, Type>> GetInitKeys()
|
||||||
|
|||||||
@@ -19,10 +19,15 @@ namespace OpenRA
|
|||||||
{
|
{
|
||||||
public class ObjectCreator
|
public class ObjectCreator
|
||||||
{
|
{
|
||||||
Pair<Assembly, string>[] assemblies;
|
readonly Cache<string, Type> typeCache;
|
||||||
|
readonly Cache<Type, ConstructorInfo> ctorCache;
|
||||||
|
readonly Pair<Assembly, string>[] assemblies;
|
||||||
|
|
||||||
public ObjectCreator(Manifest manifest)
|
public ObjectCreator(Manifest manifest)
|
||||||
{
|
{
|
||||||
|
typeCache = new Cache<string, Type>(FindType);
|
||||||
|
ctorCache = new Cache<Type, ConstructorInfo>(GetCtor);
|
||||||
|
|
||||||
// All the core namespaces
|
// All the core namespaces
|
||||||
var asms = typeof(Game).Assembly.GetNamespaces() // Game
|
var asms = typeof(Game).Assembly.GetNamespaces() // Game
|
||||||
.Select(c => Pair.New(typeof(Game).Assembly, c))
|
.Select(c => Pair.New(typeof(Game).Assembly, c))
|
||||||
@@ -48,23 +53,18 @@ namespace OpenRA
|
|||||||
|
|
||||||
public T CreateObject<T>(string className, Dictionary<string, object> args)
|
public T CreateObject<T>(string className, Dictionary<string, object> args)
|
||||||
{
|
{
|
||||||
var type = FindType(className);
|
var type = typeCache[className];
|
||||||
if (type == null)
|
if (type == null)
|
||||||
{
|
{
|
||||||
MissingTypeAction(className);
|
MissingTypeAction(className);
|
||||||
return default(T);
|
return default(T);
|
||||||
}
|
}
|
||||||
|
|
||||||
var flags = BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance;
|
var ctor = ctorCache[type];
|
||||||
var ctors = type.GetConstructors(flags)
|
if (ctor == null)
|
||||||
.Where(x => x.HasAttribute<UseCtorAttribute>()).ToList();
|
|
||||||
|
|
||||||
if (ctors.Count == 0)
|
|
||||||
return (T)CreateBasic(type);
|
return (T)CreateBasic(type);
|
||||||
else if (ctors.Count == 1)
|
|
||||||
return (T)CreateUsingArgs(ctors[0], args);
|
|
||||||
else
|
else
|
||||||
throw new InvalidOperationException("ObjectCreator: UseCtor on multiple constructors; invalid.");
|
return (T)CreateUsingArgs(ctor, args);
|
||||||
}
|
}
|
||||||
|
|
||||||
public Type FindType(string className)
|
public Type FindType(string className)
|
||||||
@@ -74,6 +74,21 @@ namespace OpenRA
|
|||||||
.FirstOrDefault(t => t != null);
|
.FirstOrDefault(t => t != null);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public ConstructorInfo GetCtor(Type type)
|
||||||
|
{
|
||||||
|
var flags = BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance;
|
||||||
|
var ctors = type.GetConstructors(flags).Where(x => x.HasAttribute<UseCtorAttribute>());
|
||||||
|
using (var e = ctors.GetEnumerator())
|
||||||
|
{
|
||||||
|
if (!e.MoveNext())
|
||||||
|
return null;
|
||||||
|
var ctor = e.Current;
|
||||||
|
if (!e.MoveNext())
|
||||||
|
return ctor;
|
||||||
|
}
|
||||||
|
throw new InvalidOperationException("ObjectCreator: UseCtor on multiple constructors; invalid.");
|
||||||
|
}
|
||||||
|
|
||||||
public object CreateBasic(Type type)
|
public object CreateBasic(Type type)
|
||||||
{
|
{
|
||||||
return type.GetConstructor(new Type[0]).Invoke(new object[0]);
|
return type.GetConstructor(new Type[0]).Invoke(new object[0]);
|
||||||
|
|||||||
@@ -14,18 +14,17 @@ using System.Collections.Generic;
|
|||||||
|
|
||||||
namespace OpenRA.Primitives
|
namespace OpenRA.Primitives
|
||||||
{
|
{
|
||||||
public class Cache<T, U> : IEnumerable<KeyValuePair<T, U>>
|
public class Cache<T, U> : IReadOnlyDictionary<T, U>
|
||||||
{
|
{
|
||||||
Dictionary<T, U> hax;
|
readonly Dictionary<T, U> cache;
|
||||||
Func<T,U> loader;
|
readonly Func<T, U> loader;
|
||||||
|
|
||||||
public Cache(Func<T,U> loader, IEqualityComparer<T> c)
|
public Cache(Func<T, U> loader, IEqualityComparer<T> c)
|
||||||
{
|
{
|
||||||
hax = new Dictionary<T, U>(c);
|
|
||||||
if (loader == null)
|
if (loader == null)
|
||||||
throw new ArgumentNullException("loader");
|
throw new ArgumentNullException("loader");
|
||||||
|
|
||||||
this.loader = loader;
|
this.loader = loader;
|
||||||
|
cache = new Dictionary<T, U>(c);
|
||||||
}
|
}
|
||||||
|
|
||||||
public Cache(Func<T, U> loader)
|
public Cache(Func<T, U> loader)
|
||||||
@@ -36,18 +35,17 @@ namespace OpenRA.Primitives
|
|||||||
get
|
get
|
||||||
{
|
{
|
||||||
U result;
|
U result;
|
||||||
if (!hax.TryGetValue(key, out result))
|
if (!cache.TryGetValue(key, out result))
|
||||||
hax.Add(key, result = loader(key));
|
cache.Add(key, result = loader(key));
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
public bool ContainsKey(T key) { return cache.ContainsKey(key); }
|
||||||
public IEnumerator<KeyValuePair<T, U>> GetEnumerator() { return hax.GetEnumerator(); }
|
public bool TryGetValue(T key, out U value) { return cache.TryGetValue(key, out value); }
|
||||||
|
public int Count { get { return cache.Count; } }
|
||||||
|
public ICollection<T> Keys { get { return cache.Keys; } }
|
||||||
|
public ICollection<U> Values { get { return cache.Values; } }
|
||||||
|
public IEnumerator<KeyValuePair<T, U>> GetEnumerator() { return cache.GetEnumerator(); }
|
||||||
IEnumerator IEnumerable.GetEnumerator() { return GetEnumerator(); }
|
IEnumerator IEnumerable.GetEnumerator() { return GetEnumerator(); }
|
||||||
|
|
||||||
public IEnumerable<T> Keys { get { return hax.Keys; } }
|
|
||||||
public IEnumerable<U> Values { get { return hax.Values; } }
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -25,8 +25,8 @@ namespace OpenRA
|
|||||||
{
|
{
|
||||||
int Count { get; }
|
int Count { get; }
|
||||||
TValue this[TKey key] { get; }
|
TValue this[TKey key] { get; }
|
||||||
IEnumerable<TKey> Keys { get; }
|
ICollection<TKey> Keys { get; }
|
||||||
IEnumerable<TValue> Values { get; }
|
ICollection<TValue> Values { get; }
|
||||||
|
|
||||||
bool ContainsKey(TKey key);
|
bool ContainsKey(TKey key);
|
||||||
bool TryGetValue(TKey key, out TValue value);
|
bool TryGetValue(TKey key, out TValue value);
|
||||||
@@ -68,9 +68,9 @@ namespace OpenRA
|
|||||||
|
|
||||||
public TValue this[TKey key] { get { return dict[key]; } }
|
public TValue this[TKey key] { get { return dict[key]; } }
|
||||||
|
|
||||||
public IEnumerable<TKey> Keys { get { return dict.Keys; } }
|
public ICollection<TKey> Keys { get { return dict.Keys; } }
|
||||||
|
|
||||||
public IEnumerable<TValue> Values { get { return dict.Values; } }
|
public ICollection<TValue> Values { get { return dict.Values; } }
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
#region IEnumerable implementation
|
#region IEnumerable implementation
|
||||||
|
|||||||
@@ -80,9 +80,13 @@ namespace OpenRA.Mods.RA
|
|||||||
|
|
||||||
// Add buildables that have a build limit set and are not already in the list
|
// Add buildables that have a build limit set and are not already in the list
|
||||||
player.World.ActorsWithTrait<Buildable>()
|
player.World.ActorsWithTrait<Buildable>()
|
||||||
.Where(a => a.Actor.Info.Traits.Get<BuildableInfo>().BuildLimit > 0 && !a.Actor.IsDead() && a.Actor.IsInWorld && a.Actor.Owner == player && ret.Keys.All(k => k != a.Actor.Info.Name))
|
.Where(a =>
|
||||||
.ToList()
|
a.Actor.Owner == player &&
|
||||||
.ForEach(b => ret[b.Actor.Info.Name].Add(b.Actor));
|
a.Actor.IsInWorld &&
|
||||||
|
!a.Actor.IsDead() &&
|
||||||
|
!ret.ContainsKey(a.Actor.Info.Name) &&
|
||||||
|
a.Actor.Info.Traits.Get<BuildableInfo>().BuildLimit > 0)
|
||||||
|
.Do(b => ret[b.Actor.Info.Name].Add(b.Actor));
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
@@ -111,17 +115,17 @@ namespace OpenRA.Mods.RA
|
|||||||
|
|
||||||
bool HasPrerequisites(Cache<string, List<Actor>> ownedPrerequisites)
|
bool HasPrerequisites(Cache<string, List<Actor>> ownedPrerequisites)
|
||||||
{
|
{
|
||||||
return prerequisites.All(p => !(p.Replace("~", "").StartsWith("!") ^ !ownedPrerequisites.Keys.Contains(p.Replace("!", "").Replace("~", ""))));
|
return prerequisites.All(p => !(p.Replace("~", "").StartsWith("!") ^ !ownedPrerequisites.ContainsKey(p.Replace("!", "").Replace("~", ""))));
|
||||||
}
|
}
|
||||||
|
|
||||||
bool IsHidden(Cache<string, List<Actor>> ownedPrerequisites)
|
bool IsHidden(Cache<string, List<Actor>> ownedPrerequisites)
|
||||||
{
|
{
|
||||||
return prerequisites.Any(prereq => prereq.StartsWith("~") && (prereq.Replace("~", "").StartsWith("!") ^ !ownedPrerequisites.Keys.Contains(prereq.Replace("~", "").Replace("!", ""))));
|
return prerequisites.Any(prereq => prereq.StartsWith("~") && (prereq.Replace("~", "").StartsWith("!") ^ !ownedPrerequisites.ContainsKey(prereq.Replace("~", "").Replace("!", ""))));
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Update(Cache<string, List<Actor>> ownedPrerequisites)
|
public void Update(Cache<string, List<Actor>> ownedPrerequisites)
|
||||||
{
|
{
|
||||||
var hasReachedLimit = limit > 0 && ownedPrerequisites.Keys.Contains(Key) && ownedPrerequisites[Key].Count >= limit;
|
var hasReachedLimit = limit > 0 && ownedPrerequisites.ContainsKey(Key) && ownedPrerequisites[Key].Count >= limit;
|
||||||
// The '!' annotation inverts prerequisites: "I'm buildable if this prerequisite *isn't* met"
|
// The '!' annotation inverts prerequisites: "I'm buildable if this prerequisite *isn't* met"
|
||||||
var nowHasPrerequisites = HasPrerequisites(ownedPrerequisites) && !hasReachedLimit;
|
var nowHasPrerequisites = HasPrerequisites(ownedPrerequisites) && !hasReachedLimit;
|
||||||
var nowHidden = IsHidden(ownedPrerequisites);
|
var nowHidden = IsHidden(ownedPrerequisites);
|
||||||
|
|||||||
Reference in New Issue
Block a user