perf: SyncVar<T> some more inlining

This commit is contained in:
vis2k 2022-02-03 22:12:00 +08:00
parent 0d55ed8448
commit ff1199607e
4 changed files with 47 additions and 5 deletions

View File

@ -16,6 +16,7 @@
// //
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Runtime.CompilerServices;
using UnityEngine; using UnityEngine;
namespace Mirror namespace Mirror
@ -33,6 +34,7 @@ public class SyncVar<T> : SyncObject, IEquatable<T>
// virtual for SyncFieldNetworkIdentity netId trick etc. // virtual for SyncFieldNetworkIdentity netId trick etc.
public virtual T Value public virtual T Value
{ {
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get => _Value; get => _Value;
set set
{ {
@ -74,6 +76,7 @@ public virtual T Value
// OnCallback is responsible for calling the callback. // OnCallback is responsible for calling the callback.
// this is necessary for inheriting classes like SyncVarGameObject, // this is necessary for inheriting classes like SyncVarGameObject,
// where the netIds should be converted to GOs and call the GO hook. // where the netIds should be converted to GOs and call the GO hook.
[MethodImpl(MethodImplOptions.AggressiveInlining)]
protected virtual void InvokeCallback(T oldValue, T newValue) => protected virtual void InvokeCallback(T oldValue, T newValue) =>
Callback?.Invoke(oldValue, newValue); Callback?.Invoke(oldValue, newValue);
@ -107,21 +110,31 @@ public SyncVar(T value)
// SyncVar<T>s are readonly and only initialized by 'Value' once. // SyncVar<T>s are readonly and only initialized by 'Value' once.
// implicit conversion: int value = SyncVar<T> // implicit conversion: int value = SyncVar<T>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static implicit operator T(SyncVar<T> field) => field.Value; public static implicit operator T(SyncVar<T> field) => field.Value;
// implicit conversion: SyncVar<T> = value // implicit conversion: SyncVar<T> = value
// even if SyncVar<T> is readonly, it's still useful: SyncVar<int> = 1; // even if SyncVar<T> is readonly, it's still useful: SyncVar<int> = 1;
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static implicit operator SyncVar<T>(T value) => new SyncVar<T>(value); public static implicit operator SyncVar<T>(T value) => new SyncVar<T>(value);
// serialization (use .Value instead of _Value so hook is called!) // serialization (use .Value instead of _Value so hook is called!)
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public override void OnSerializeAll(NetworkWriter writer) => writer.Write(Value); public override void OnSerializeAll(NetworkWriter writer) => writer.Write(Value);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public override void OnSerializeDelta(NetworkWriter writer) => writer.Write(Value); public override void OnSerializeDelta(NetworkWriter writer) => writer.Write(Value);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public override void OnDeserializeAll(NetworkReader reader) => Value = reader.Read<T>(); public override void OnDeserializeAll(NetworkReader reader) => Value = reader.Read<T>();
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public override void OnDeserializeDelta(NetworkReader reader) => Value = reader.Read<T>(); public override void OnDeserializeDelta(NetworkReader reader) => Value = reader.Read<T>();
// IEquatable should compare Value. // IEquatable should compare Value.
// SyncVar<T> should act invisibly like [SyncVar] before. // SyncVar<T> should act invisibly like [SyncVar] before.
// this way we can do SyncVar<int> health == 0 etc. // this way we can do SyncVar<int> health == 0 etc.
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool Equals(T other) => public bool Equals(T other) =>
// from NetworkBehaviour.SyncVarEquals: // from NetworkBehaviour.SyncVarEquals:
// EqualityComparer method avoids allocations. // EqualityComparer method avoids allocations.

View File

@ -55,7 +55,9 @@ public class SyncVarGameObject : SyncVar<uint>
// .spawned lookup from netId overwrites base uint .Value // .spawned lookup from netId overwrites base uint .Value
public new GameObject Value public new GameObject Value
{ {
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get => GetGameObject(base.Value); get => GetGameObject(base.Value);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
set => base.Value = GetNetId(value); set => base.Value = GetNetId(value);
} }
@ -64,6 +66,7 @@ public class SyncVarGameObject : SyncVar<uint>
public new event Action<GameObject, GameObject> Callback; public new event Action<GameObject, GameObject> Callback;
// overwrite CallCallback to use the GameObject version instead // overwrite CallCallback to use the GameObject version instead
[MethodImpl(MethodImplOptions.AggressiveInlining)]
protected override void InvokeCallback(uint oldValue, uint newValue) => protected override void InvokeCallback(uint oldValue, uint newValue) =>
Callback?.Invoke(GetGameObject(oldValue), GetGameObject(newValue)); Callback?.Invoke(GetGameObject(oldValue), GetGameObject(newValue));
@ -76,6 +79,7 @@ public SyncVarGameObject(GameObject value = null)
: base(GetNetId(value)) {} : base(GetNetId(value)) {}
// helper function to get netId from GameObject (if any) // helper function to get netId from GameObject (if any)
[MethodImpl(MethodImplOptions.AggressiveInlining)]
static uint GetNetId(GameObject go) static uint GetNetId(GameObject go)
{ {
if (go != null) if (go != null)
@ -87,6 +91,7 @@ static uint GetNetId(GameObject go)
} }
// helper function to get GameObject from netId (if spawned) // helper function to get GameObject from netId (if spawned)
[MethodImpl(MethodImplOptions.AggressiveInlining)]
static GameObject GetGameObject(uint netId) static GameObject GetGameObject(uint netId)
{ {
NetworkIdentity spawned = Utils.GetSpawnedInServerOrClient(netId); NetworkIdentity spawned = Utils.GetSpawnedInServerOrClient(netId);
@ -94,10 +99,12 @@ static GameObject GetGameObject(uint netId)
} }
// implicit conversion: GameObject value = SyncFieldGameObject // implicit conversion: GameObject value = SyncFieldGameObject
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static implicit operator GameObject(SyncVarGameObject field) => field.Value; public static implicit operator GameObject(SyncVarGameObject field) => field.Value;
// implicit conversion: SyncFieldGameObject = value // implicit conversion: SyncFieldGameObject = value
// even if SyncField is readonly, it's still useful: SyncFieldGameObject = target; // even if SyncField is readonly, it's still useful: SyncFieldGameObject = target;
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static implicit operator SyncVarGameObject(GameObject value) => new SyncVarGameObject(value); public static implicit operator SyncVarGameObject(GameObject value) => new SyncVarGameObject(value);
// == operator for comparisons like Player.target==monster // == operator for comparisons like Player.target==monster
@ -129,7 +136,10 @@ static GameObject GetGameObject(uint netId)
public static bool operator !=(GameObject a, SyncVarGameObject b) => !(a == b); public static bool operator !=(GameObject a, SyncVarGameObject b) => !(a == b);
// if we overwrite == operators, we also need to overwrite .Equals. // if we overwrite == operators, we also need to overwrite .Equals.
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public override bool Equals(object obj) => obj is SyncVarGameObject value && this == value; public override bool Equals(object obj) => obj is SyncVarGameObject value && this == value;
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public override int GetHashCode() => Value.GetHashCode(); public override int GetHashCode() => Value.GetHashCode();
} }
} }

View File

@ -25,7 +25,9 @@ public class SyncVarNetworkBehaviour<T> : SyncVar<ulong>
// .spawned lookup from netId overwrites base uint .Value // .spawned lookup from netId overwrites base uint .Value
public new T Value public new T Value
{ {
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get => ULongToNetworkBehaviour(base.Value); get => ULongToNetworkBehaviour(base.Value);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
set => base.Value = NetworkBehaviourToULong(value); set => base.Value = NetworkBehaviourToULong(value);
} }
@ -34,6 +36,7 @@ public class SyncVarNetworkBehaviour<T> : SyncVar<ulong>
public new event Action<T, T> Callback; public new event Action<T, T> Callback;
// overwrite CallCallback to use the NetworkIdentity version instead // overwrite CallCallback to use the NetworkIdentity version instead
[MethodImpl(MethodImplOptions.AggressiveInlining)]
protected override void InvokeCallback(ulong oldValue, ulong newValue) => protected override void InvokeCallback(ulong oldValue, ulong newValue) =>
Callback?.Invoke(ULongToNetworkBehaviour(oldValue), ULongToNetworkBehaviour(newValue)); Callback?.Invoke(ULongToNetworkBehaviour(oldValue), ULongToNetworkBehaviour(newValue));
@ -46,10 +49,12 @@ public SyncVarNetworkBehaviour(T value = null)
: base(NetworkBehaviourToULong(value)) {} : base(NetworkBehaviourToULong(value)) {}
// implicit conversion: NetworkBehaviour value = SyncFieldNetworkBehaviour // implicit conversion: NetworkBehaviour value = SyncFieldNetworkBehaviour
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static implicit operator T(SyncVarNetworkBehaviour<T> field) => field.Value; public static implicit operator T(SyncVarNetworkBehaviour<T> field) => field.Value;
// implicit conversion: SyncFieldNetworkBehaviour = value // implicit conversion: SyncFieldNetworkBehaviour = value
// even if SyncField is readonly, it's still useful: SyncFieldNetworkBehaviour = target; // even if SyncField is readonly, it's still useful: SyncFieldNetworkBehaviour = target;
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static implicit operator SyncVarNetworkBehaviour<T>(T value) => new SyncVarNetworkBehaviour<T>(value); public static implicit operator SyncVarNetworkBehaviour<T>(T value) => new SyncVarNetworkBehaviour<T>(value);
// NOTE: overloading all == operators blocks '== null' checks with an // NOTE: overloading all == operators blocks '== null' checks with an
@ -97,16 +102,18 @@ public SyncVarNetworkBehaviour(T value = null)
public static bool operator !=(T a, SyncVarNetworkBehaviour<T> b) => !(a == b); public static bool operator !=(T a, SyncVarNetworkBehaviour<T> b) => !(a == b);
// if we overwrite == operators, we also need to overwrite .Equals. // if we overwrite == operators, we also need to overwrite .Equals.
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public override bool Equals(object obj) => obj is SyncVarNetworkBehaviour<T> value && this == value; public override bool Equals(object obj) => obj is SyncVarNetworkBehaviour<T> value && this == value;
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public override int GetHashCode() => Value.GetHashCode(); public override int GetHashCode() => Value.GetHashCode();
// helper functions to get/set netId, componentIndex from ulong // helper functions to get/set netId, componentIndex from ulong
internal static ulong Pack(uint netId, byte componentIndex) // netId on the 4 left bytes. compIndex on the right most byte.
{ [MethodImpl(MethodImplOptions.AggressiveInlining)]
// netId on the 4 left bytes. compIndex on the right most byte. internal static ulong Pack(uint netId, byte componentIndex) =>
return (ulong)netId << 32 | componentIndex; (ulong)netId << 32 | componentIndex;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
internal static void Unpack(ulong value, out uint netId, out byte componentIndex) internal static void Unpack(ulong value, out uint netId, out byte componentIndex)
{ {
netId = (uint)(value >> 32); netId = (uint)(value >> 32);
@ -133,6 +140,7 @@ static ulong NetworkBehaviourToULong(T value)
} }
// Serialize should only write 4+1 bytes, not 8 bytes ulong // Serialize should only write 4+1 bytes, not 8 bytes ulong
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public override void OnSerializeAll(NetworkWriter writer) public override void OnSerializeAll(NetworkWriter writer)
{ {
Unpack(base.Value, out uint netId, out byte componentIndex); Unpack(base.Value, out uint netId, out byte componentIndex);
@ -140,10 +148,12 @@ public override void OnSerializeAll(NetworkWriter writer)
writer.WriteByte(componentIndex); writer.WriteByte(componentIndex);
} }
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public override void OnSerializeDelta(NetworkWriter writer) => public override void OnSerializeDelta(NetworkWriter writer) =>
OnSerializeAll(writer); OnSerializeAll(writer);
// Deserialize should only write 4+1 bytes, not 8 bytes ulong // Deserialize should only write 4+1 bytes, not 8 bytes ulong
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public override void OnDeserializeAll(NetworkReader reader) public override void OnDeserializeAll(NetworkReader reader)
{ {
uint netId = reader.ReadUInt(); uint netId = reader.ReadUInt();
@ -151,6 +161,7 @@ public override void OnDeserializeAll(NetworkReader reader)
base.Value = Pack(netId, componentIndex); base.Value = Pack(netId, componentIndex);
} }
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public override void OnDeserializeDelta(NetworkReader reader) => public override void OnDeserializeDelta(NetworkReader reader) =>
OnDeserializeAll(reader); OnDeserializeAll(reader);
} }

View File

@ -48,7 +48,9 @@ public class SyncVarNetworkIdentity : SyncVar<uint>
// .spawned lookup from netId overwrites base uint .Value // .spawned lookup from netId overwrites base uint .Value
public new NetworkIdentity Value public new NetworkIdentity Value
{ {
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get => Utils.GetSpawnedInServerOrClient(base.Value); get => Utils.GetSpawnedInServerOrClient(base.Value);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
set => base.Value = value != null ? value.netId : 0; set => base.Value = value != null ? value.netId : 0;
} }
@ -57,6 +59,7 @@ public class SyncVarNetworkIdentity : SyncVar<uint>
public new event Action<NetworkIdentity, NetworkIdentity> Callback; public new event Action<NetworkIdentity, NetworkIdentity> Callback;
// overwrite CallCallback to use the NetworkIdentity version instead // overwrite CallCallback to use the NetworkIdentity version instead
[MethodImpl(MethodImplOptions.AggressiveInlining)]
protected override void InvokeCallback(uint oldValue, uint newValue) => protected override void InvokeCallback(uint oldValue, uint newValue) =>
Callback?.Invoke(Utils.GetSpawnedInServerOrClient(oldValue), Utils.GetSpawnedInServerOrClient(newValue)); Callback?.Invoke(Utils.GetSpawnedInServerOrClient(oldValue), Utils.GetSpawnedInServerOrClient(newValue));
@ -69,10 +72,12 @@ public SyncVarNetworkIdentity(NetworkIdentity value = null)
: base(value != null ? value.netId : 0) {} : base(value != null ? value.netId : 0) {}
// implicit conversion: NetworkIdentity value = SyncFieldNetworkIdentity // implicit conversion: NetworkIdentity value = SyncFieldNetworkIdentity
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static implicit operator NetworkIdentity(SyncVarNetworkIdentity field) => field.Value; public static implicit operator NetworkIdentity(SyncVarNetworkIdentity field) => field.Value;
// implicit conversion: SyncFieldNetworkIdentity = value // implicit conversion: SyncFieldNetworkIdentity = value
// even if SyncField is readonly, it's still useful: SyncFieldNetworkIdentity = target; // even if SyncField is readonly, it's still useful: SyncFieldNetworkIdentity = target;
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static implicit operator SyncVarNetworkIdentity(NetworkIdentity value) => new SyncVarNetworkIdentity(value); public static implicit operator SyncVarNetworkIdentity(NetworkIdentity value) => new SyncVarNetworkIdentity(value);
// NOTE: overloading all == operators blocks '== null' checks with an // NOTE: overloading all == operators blocks '== null' checks with an
@ -104,7 +109,10 @@ public SyncVarNetworkIdentity(NetworkIdentity value = null)
public static bool operator !=(NetworkIdentity a, SyncVarNetworkIdentity b) => !(a == b); public static bool operator !=(NetworkIdentity a, SyncVarNetworkIdentity b) => !(a == b);
// if we overwrite == operators, we also need to overwrite .Equals. // if we overwrite == operators, we also need to overwrite .Equals.
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public override bool Equals(object obj) => obj is SyncVarNetworkIdentity value && this == value; public override bool Equals(object obj) => obj is SyncVarNetworkIdentity value && this == value;
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public override int GetHashCode() => Value.GetHashCode(); public override int GetHashCode() => Value.GetHashCode();
} }
} }