浏览代码

fixed determinism issues affecting USim runs

/main
Steven Leal 4 年前
当前提交
71bcc834
共有 9 个文件被更改,包括 241 次插入76 次删除
  1. 10
      com.unity.perception/CHANGELOG.md
  2. 36
      com.unity.perception/Runtime/Randomization/Randomizers/RandomizerExamples/Utilities/GameObjectOneWayCache.cs
  3. 18
      com.unity.perception/Runtime/Randomization/Randomizers/RandomizerTag.cs
  4. 8
      com.unity.perception/Runtime/Randomization/Randomizers/RandomizerTagManager.cs
  5. 81
      com.unity.perception/Runtime/Randomization/Scenarios/ScenarioBase.cs
  6. 6
      com.unity.perception/Runtime/Randomization/Scenarios/UnitySimulationScenario.cs
  7. 78
      com.unity.perception/Tests/Runtime/Randomization/RandomizerTests/RandomizerTagTests.cs
  8. 77
      com.unity.perception/Runtime/Randomization/Randomizers/OrderedSet.cs
  9. 3
      com.unity.perception/Runtime/Randomization/Randomizers/OrderedSet.cs.meta

10
com.unity.perception/CHANGELOG.md


### Added
Added Register() and Unregister() methods to the RandomizerTag API so users can implement RandomizerTag compatible GameObject caching
Made scenario MonoBehaviour lifecycle functions protected instead of private to enable users to define overrides
The GameObjectOneWayCache has been made public for users to cache GameObjects within their own custom Randomizers
### Deprecated
### Removed

Fixed the math offsetting the iteration index of each Unity Simulation instance directly after they deserialize their app-params
The RandomizerTagManager now uses an OrderedSet data structure to register tags to preserve insertion order determinism in Unity Simulation
GameObjectOneWayCache now correctly registers and unregisters RandomizerTags on cached GameObjects
## [0.7.0-preview.1] - 2021-02-01

36
com.unity.perception/Runtime/Randomization/Randomizers/RandomizerExamples/Utilities/GameObjectOneWayCache.cs


/// Facilitates object pooling for a pre-specified collection of prefabs with the caveat that objects can be fetched
/// from the cache but not returned. Every frame, the cache needs to be reset, which will return all objects to the pool
/// </summary>
class GameObjectOneWayCache
public class GameObjectOneWayCache
// Objects will reset to this origin when not being used
/// <summary>
/// The number of active cache objects in the scene
/// </summary>
/// <summary>
/// Creates a new GameObjectOneWayCache
/// </summary>
/// <param name="parent">The parent object all cached instances will be parented under</param>
/// <param name="prefabs">The prefabs to cache</param>
public GameObjectOneWayCache(Transform parent, GameObject[] prefabs)
{
m_CacheParent = parent;

}
}
/// <summary>
/// Retrieves an existing instance of the given prefab from the cache if available.
/// Otherwise, instantiate a new instance of the given prefab.
/// </summary>
/// <param name="prefab"></param>
/// <returns></returns>
/// <exception cref="ArgumentException"></exception>
public GameObject GetOrInstantiate(GameObject prefab)
{
if (!m_InstanceIdToIndex.TryGetValue(prefab.GetInstanceID(), out var index))

++NumObjectsActive;
GameObject nextObject;
return nextInCache;
nextObject = nextInCache;
}
else
{

m_InstantiatedObjects[index].Add(newObject);
return newObject;
nextObject = newObject;
var tags = nextObject.GetComponents<RandomizerTag>();
foreach (var tag in tags)
tag.Register();
return nextObject;
/// <summary>
/// Return all active cache objects back to an inactive state
/// </summary>
public void ResetAllObjects()
{
using (s_ResetAllObjectsMarker.Auto())

{
// Position outside the frame
obj.transform.localPosition = new Vector3(10000, 0, 0);
var tags = obj.GetComponents<RandomizerTag>();
foreach (var tag in tags)
tag.Unregister();
}
}
}

18
com.unity.perception/Runtime/Randomization/Randomizers/RandomizerTag.cs


void Awake()
{
tagManager.AddTag(this);
Register();
{
Unregister();
}
/// <summary>
/// Registers this tag with the tagManager
/// </summary>
public void Register()
{
tagManager.AddTag(this);
}
/// <summary>
/// Unregisters this tag with the tagManager
/// </summary>
public void Unregister()
{
tagManager.RemoveTag(this);
}

8
com.unity.perception/Runtime/Randomization/Randomizers/RandomizerTagManager.cs


public static RandomizerTagManager singleton { get; } = new RandomizerTagManager();
Dictionary<Type, HashSet<Type>> m_TypeTree = new Dictionary<Type, HashSet<Type>>();
Dictionary<Type, HashSet<RandomizerTag>> m_TagMap = new Dictionary<Type, HashSet<RandomizerTag>>();
Dictionary<Type, OrderedSet<RandomizerTag>> m_TagMap = new Dictionary<Type, OrderedSet<RandomizerTag>>();
/// <summary>
/// Enumerates over all RandomizerTags of the given type present in the scene

if (m_TypeTree.ContainsKey(tagType))
return;
m_TagMap.Add(tagType, new HashSet<RandomizerTag>());
m_TagMap.Add(tagType, new OrderedSet<RandomizerTag>());
m_TypeTree.Add(tagType, new HashSet<Type>());
var baseType = tagType.BaseType;

{
m_TagMap.Add(baseType, new HashSet<RandomizerTag>());
m_TagMap.Add(baseType, new OrderedSet<RandomizerTag>());
m_TypeTree[baseType] = new HashSet<Type> { tagType };
}
else

internal void RemoveTag<T>(T tag) where T : RandomizerTag
{
var tagType = typeof(T);
var tagType = tag.GetType();
if (m_TagMap.ContainsKey(tagType) && m_TagMap[tagType].Contains(tag))
m_TagMap[tagType].Remove(tag);
}

81
com.unity.perception/Runtime/Randomization/Scenarios/ScenarioBase.cs


using System.Collections.Generic;
using System.IO;
using Unity.Simulation;
using UnityEditor;
using UnityEngine.Perception.GroundTruth;
using UnityEngine.Perception.GroundTruth;
namespace UnityEngine.Perception.Randomization.Scenarios
{

[DefaultExecutionOrder(-1)]
public abstract class ScenarioBase : MonoBehaviour
{
const string k_ScenarioIterationMetricDefinitionId = "DB1B258E-D1D0-41B6-8751-16F601A2E230";
bool m_FirstScenarioFrame = true;
MetricDefinition m_IterationMetricDefinition;
const string k_ScenarioIterationMetricDefinitionId = "DB1B258E-D1D0-41B6-8751-16F601A2E230";
// ReSharper disable once InconsistentNaming
[SerializeReference] internal List<Randomizer> m_Randomizers = new List<Randomizer>();
bool m_FirstScenarioFrame = true;
MetricDefinition m_IterationMetricDefinition;
/// <summary>
/// If true, this scenario will quit the Unity application when it's finished executing
/// </summary>
[HideInInspector]
public bool quitOnComplete = true;
IEnumerable<Randomizer> activeRandomizers
{

}
}
// ReSharper disable once InconsistentNaming
[SerializeReference] internal List<Randomizer> m_Randomizers = new List<Randomizer>();
/// <summary>
/// Return the list of randomizers attached to this scenario
/// </summary>

/// If true, this scenario will quit the Unity application when it's finished executing
/// </summary>
[HideInInspector] public bool quitOnComplete = true;
/// <summary>
/// The name of the Json file this scenario's constants are serialized to/from.
/// </summary>
public virtual string configFileName => "scenario_configuration";

get
{
#if UNITY_EDITOR
// This compiler define is required to allow samplers to
// iterate the scenario's random state in edit-mode
if (s_ActiveScenario == null)

public void SerializeToFile()
{
Directory.CreateDirectory(Application.dataPath + "/StreamingAssets/");
using (>n class="kt">var writer = new StreamWriter(defaultConfigFilePath, false))
writer.Write(SerializeToJson());
using var writer = new StreamWriter(defaultConfigFilePath, false);
writer.Write(SerializeToJson());
}
/// <summary>

}
/// <summary>
/// This method executed directly after this scenario has been registered and initialized
/// Awake is called when this scenario MonoBehaviour is created or instantiated
protected virtual void OnAwake() { }
void Awake()
protected virtual void Awake()
OnAwake();
foreach (var randomizer in m_Randomizers)
randomizer.Create();
ValidateParameters();

Guid.Parse(k_ScenarioIterationMetricDefinitionId));
}
void OnEnable()
/// <summary>
/// OnEnable is called when this scenario is enabled
/// </summary>
protected virtual void OnEnable()
void OnDisable()
/// <summary>
/// OnEnable is called when this scenario is disabled
/// </summary>
protected virtual void OnDisable()
void Start()
/// <summary>
/// Start is called after Awake but before the first Update method call
/// </summary>
protected virtual void Start()
{
var randomSeedMetricDefinition = DatasetCapture.RegisterMetricDefinition(
"random-seed",

#endif
}
struct IterationMetricData
{
public int iteration;
}
void Update()
/// <summary>
/// Update is called once per frame
/// </summary>
protected virtual void Update()
{
// TODO: remove this check when the perception camera can capture the first frame of output
if (m_SkipFrame)

if (!Manager.FinalUploadsDone)
return;
#if UNITY_EDITOR
UnityEditor.EditorApplication.ExitPlaymode();
EditorApplication.ExitPlaymode();
#else
Application.Quit();
#endif

ResetRandomStateOnIteration();
DatasetCapture.ReportMetric(m_IterationMetricDefinition, new[]
{
new IterationMetricData()
{
iteration = currentIteration
}
new IterationMetricData { iteration = currentIteration }
});
foreach (var randomizer in activeRandomizers)
randomizer.IterationStart();

$"Cannot remove non-randomizer type {randomizerType.Name} from randomizer list");
var removed = false;
for (var i = 0; i < m_Randomizers.Count; i++)
{
if (m_Randomizers[i].GetType() == randomizerType)
{
m_Randomizers.RemoveAt(i);

}
if (!removed)
throw new ScenarioException(
$"No active Randomizer of type {randomizerType.Name} could be removed");

if (randomizer is T)
return i;
}
throw new ScenarioException($"A Randomizer of type {typeof(T).Name} was not added to this scenario");
}

{
foreach (var randomizer in m_Randomizers)
foreach (var parameter in randomizer.parameters)
{
try
{
parameter.Validate();

Debug.LogException(exception, this);
}
}
}
struct IterationMetricData
{
// ReSharper disable once NotAccessedField.Local
public int iteration;
}
}
}

6
com.unity.perception/Runtime/Randomization/Scenarios/UnitySimulationScenario.cs


using System;
using System.IO;
using UnityEngine.Perception.Randomization.Samplers;
namespace UnityEngine.Perception.Randomization.Scenarios
{

}
/// <inheritdoc/>
public sealed override void DeserializeFromFile(string configFilePath)
protected override void Start()
base.DeserializeFromFile(configFilePath);
base.Start();
currentIteration = constants.instanceIndex;
}
}

78
com.unity.perception/Tests/Runtime/Randomization/RandomizerTests/RandomizerTagTests.cs


using System.Linq;
using System.Collections.Generic;
using System.Linq;
using UnityEngine.Perception.Randomization.Scenarios;
using Assert = Unity.Assertions.Assert;
namespace RandomizationTests.RandomizerTests

{
public class ParentTag : RandomizerTag { }
public class ChildTag : ParentTag { }
GameObject m_TestObject;
FixedLengthScenario m_Scenario;
public class BaseTag : RandomizerTag { }
[SetUp]
public void Setup()
{
m_TestObject = new GameObject();
m_Scenario = m_TestObject.AddComponent<FixedLengthScenario>();
m_Scenario.quitOnComplete = false;
}
public class DerivedTag : BaseTag { }
public void TearDown()
public void Teardown()
Object.DestroyImmediate(m_TestObject);
var tags = Object.FindObjectsOfType<BaseTag>(true);
foreach (var tag in tags)
{
if (tag != null && tag.gameObject != null)
Object.DestroyImmediate(tag.gameObject);
}
public void TagQueryFindsCorrectNumberOfGameObjects()
public void TagInheritanceWorksInTagQueries()
gameObject.AddComponent<ParentTag>();
gameObject.AddComponent<BaseTag>();
gameObject2.AddComponent<ChildTag>();
gameObject2.AddComponent<DerivedTag>();
var queriedObjects = tagManager.Query<ParentTag>().ToArray();
Assert.AreEqual(queriedObjects.Length, copyCount);
var queriedBaseTags = tagManager.Query<BaseTag>().ToArray();
Assert.AreEqual(queriedBaseTags.Length, copyCount);
queriedObjects = tagManager.Query<ChildTag>().ToArray();
Assert.AreEqual(queriedObjects.Length, copyCount);
var queriedDerivedTags = tagManager.Query<DerivedTag>().ToArray();
Assert.AreEqual(queriedDerivedTags.Length, copyCount);
queriedBaseTags = tagManager.Query<BaseTag>(true).ToArray();
Assert.AreEqual(queriedBaseTags.Length, copyCount * 2);
}
[Test]
public void TagQueriesPreserveInsertionOrder()
{
const int copyCount = 5;
const int destroyCount = 3;
var testObj = new GameObject();
testObj.AddComponent<BaseTag>();
var testObjects = new List<GameObject> { testObj };
for (var i = 0; i < copyCount - 1; i++)
testObjects.Add(Object.Instantiate(testObj));
for (var i = 0; i < destroyCount; i++)
{
Object.DestroyImmediate(testObjects[1]);
testObjects.RemoveAt(1);
}
for (var i = 0; i < copyCount + destroyCount; i++)
testObjects.Add(Object.Instantiate(testObj));
var tagManager = RandomizerTagManager.singleton;
var tags = tagManager.Query<BaseTag>();
var tagsArray = tags.ToArray();
queriedObjects = tagManager.Query<ParentTag>(true).ToArray();
Assert.AreEqual(queriedObjects.Length, copyCount * 2);
var index = 0;
foreach (var tag in tagsArray)
Assert.AreEqual(tag, testObjects[index++].GetComponent<BaseTag>());
}
}
}

77
com.unity.perception/Runtime/Randomization/Randomizers/OrderedSet.cs


using System.Collections;
using System.Collections.Generic;
namespace UnityEngine.Perception.Randomization.Randomizers
{
/// <summary>
/// This collection has the properties of a HashSet that also preserves insertion order. As such, this data
/// structure demonstrates the following time complexities:
/// O(1) lookup, O(1) add, O(1) remove, and O(n) traversal
/// </summary>
/// <typeparam name="T">The item type to store in this collection</typeparam>
class OrderedSet<T> : ICollection<T>
{
readonly IDictionary<T, LinkedListNode<T>> m_Dictionary;
readonly LinkedList<T> m_LinkedList;
public OrderedSet() : this(EqualityComparer<T>.Default) { }
public OrderedSet(IEqualityComparer<T> comparer)
{
m_Dictionary = new Dictionary<T, LinkedListNode<T>>(comparer);
m_LinkedList = new LinkedList<T>();
}
public int Count => m_Dictionary.Count;
public virtual bool IsReadOnly => m_Dictionary.IsReadOnly;
void ICollection<T>.Add(T item)
{
Add(item);
}
public bool Add(T item)
{
if (m_Dictionary.ContainsKey(item)) return false;
var node = m_LinkedList.AddLast(item);
m_Dictionary.Add(item, node);
return true;
}
public void Clear()
{
m_LinkedList.Clear();
m_Dictionary.Clear();
}
public bool Remove(T item)
{
var found = m_Dictionary.TryGetValue(item, out var node);
if (!found) return false;
m_Dictionary.Remove(item);
m_LinkedList.Remove(node);
return true;
}
public IEnumerator<T> GetEnumerator()
{
return m_LinkedList.GetEnumerator();
}
IEnumerator IEnumerable.GetEnumerator()
{
return GetEnumerator();
}
public bool Contains(T item)
{
return m_Dictionary.ContainsKey(item);
}
public void CopyTo(T[] array, int arrayIndex)
{
m_LinkedList.CopyTo(array, arrayIndex);
}
}
}

3
com.unity.perception/Runtime/Randomization/Randomizers/OrderedSet.cs.meta


fileFormatVersion: 2
guid: 2b6c83991e2c4c88af9b2b43c90121c7
timeCreated: 1612730504
正在加载...
取消
保存