浏览代码

Merge pull request #142 from Unity-Technologies/global-seed

Replaced individual sampler seeds with one global seed
/main
GitHub 4 年前
当前提交
af13a3d8
共有 33 个文件被更改,包括 237 次插入414 次删除
  1. 8
      com.unity.perception/CHANGELOG.md
  2. 13
      com.unity.perception/Documentation~/Randomization/Parameters.md
  3. 28
      com.unity.perception/Documentation~/Randomization/Samplers.md
  4. 6
      com.unity.perception/Documentation~/Tutorial/Phase1.md
  5. 6
      com.unity.perception/Documentation~/Tutorial/Phase2.md
  6. 2
      com.unity.perception/Editor/Randomization/Editors/RunInUnitySimulationWindow.cs
  7. 1
      com.unity.perception/Editor/Randomization/Uxml/Parameter/CategoricalParameterTemplate.uxml
  8. 3
      com.unity.perception/Editor/Randomization/VisualElements/Parameter/ParameterElement.cs
  9. 6
      com.unity.perception/Editor/Randomization/VisualElements/Randomizer/RandomizerList.cs
  10. 7
      com.unity.perception/Editor/Randomization/VisualElements/Sampler/SamplerElement.cs
  11. 49
      com.unity.perception/Runtime/Randomization/Parameters/Parameter.cs
  12. 6
      com.unity.perception/Runtime/Randomization/Randomizers/Randomizer.cs
  13. 2
      com.unity.perception/Runtime/Randomization/Randomizers/RandomizerExamples/Randomizers/BackgroundObjectPlacementRandomizer.cs
  14. 2
      com.unity.perception/Runtime/Randomization/Randomizers/RandomizerExamples/Randomizers/ForegroundObjectPlacementRandomizer.cs
  15. 24
      com.unity.perception/Runtime/Randomization/Samplers/ISampler.cs
  16. 61
      com.unity.perception/Runtime/Randomization/Samplers/SamplerTypes/ConstantSampler.cs
  17. 84
      com.unity.perception/Runtime/Randomization/Samplers/SamplerTypes/NormalSampler.cs
  18. 77
      com.unity.perception/Runtime/Randomization/Samplers/SamplerTypes/UniformSampler.cs
  19. 57
      com.unity.perception/Runtime/Randomization/Samplers/SamplerUtility.cs
  20. 2
      com.unity.perception/Runtime/Randomization/Scenarios/FixedLengthScenario.cs
  21. 6
      com.unity.perception/Runtime/Randomization/Scenarios/Scenario.cs
  22. 60
      com.unity.perception/Runtime/Randomization/Scenarios/ScenarioBase.cs
  23. 24
      com.unity.perception/Runtime/Randomization/Scenarios/UnitySimulationScenario.cs
  24. 4
      com.unity.perception/Tests/Runtime/Randomization/ParameterTests/StructParameterTests.cs
  25. 2
      com.unity.perception/Tests/Runtime/Randomization/SamplerTests/ConstantSamplerTests.cs
  26. 28
      com.unity.perception/Tests/Runtime/Randomization/SamplerTests/SamplerTestsBase.cs
  27. 7
      com.unity.perception/Tests/Runtime/Randomization/ScenarioTests.cs
  28. 18
      com.unity.perception/Runtime/Randomization/Scenarios/ScenarioConstants.cs
  29. 3
      com.unity.perception/Runtime/Randomization/Scenarios/ScenarioConstants.cs.meta
  30. 26
      com.unity.perception/Runtime/Randomization/Scenarios/UnitySimulationScenarioConstants.cs
  31. 3
      com.unity.perception/Runtime/Randomization/Scenarios/UnitySimulationScenarioConstants.cs.meta
  32. 15
      com.unity.perception/Editor/Randomization/VisualElements/Sampler/RandomSeedField.cs
  33. 11
      com.unity.perception/Editor/Randomization/VisualElements/Sampler/RandomSeedField.cs.meta

8
com.unity.perception/CHANGELOG.md


### Added
Added ScenarioConstants base class for all scenario constants objects
Unique seeds per Sampler have been replaced with one global random seed configured via the ScenarioConstants of a Scenario
Replaced ScenarioBase.GenerateRandomSeed() with ScenarioBase.NextRandomSeed()
Removed ScenarioBase.GenerateRandomSeedFromIndex()
### Fixed

13
com.unity.perception/Documentation~/Randomization/Parameters.md


# Parameters
## Lookup Parameters
To obtain a parameter from a paramter configuration, use the GetParameter() method:
```
// Get a reference to the parameter configuration attached to this GameObject
var parameterConfiguration = GetComponent<ParameterConfiguration>();
// Lookup the parameter "ObjectWidth" by name
var parameter = GetComponent<FloatParameter>("ObjectWidth");
```
Parameters are typically managed by `ParameterConfigurations` in the Unity Editor. However, parameters can be instanced independently like a regular class too:
Parameters are often defined as fields of a randomizer class, but they can also be instanced just like any other C# class:
```
// Create a color parameter
var colorParameter = new HsvaColorParameter();

28
com.unity.perception/Documentation~/Randomization/Samplers.md


Samplers in the perception package are classes that deterministically generate random float values from bounded probability distributions. Although samplers are often used in conjunction with parameters to generate arrays of typed random values, samplers can be instantiated and used from any ordinary script:
```
var sampler = new NormalSampler();
sampler.seed = 123456789u;
sampler.mean = 3;
sampler.stdDev = 2;
sampler.range = new FloatRange(-10, 10);

1. Constant Sampler
2. Uniform Sampler
3. Normal Sampler
4. Placeholder Range Sampler
#### Constant Sampler
Generates constant valued samples

#### Normal Sampler
Generates random samples from a truncated normal distribution bounded by a specified range
#### Placeholder Range Sampler
Used to define a float range [minimum, maximum] for a particular component of a parameter (example: the hue component of a color parameter). This sampler is useful for configuring sample ranges for non-perception related scripts, particularly when these scripts have a public interface for manipulating a minimum and maximum bounds for their sample range but perform the actual sampling logic internally.
## Custom Samplers
Take a look at the [UniformSampler](../../Runtime/Randomization/Samplers/SamplerTypes/UniformSampler) and [NormalSampler](../../Runtime/Randomization/Samplers/SamplerTypes/NormalSampler) structs as references for implementing your own [ISampler](../../Runtime/Randomization/Samplers/ISampler). Note that the NativeSamples() method in the ISampler interface requires the usage of the Unity Job System. Take a look [here](https://docs.unity3d.com/Manual/JobSystem.html) to learn more about how to create jobs using the Unity Job System.
Samplers are designed to be Unity Burst Compiler and Job System compatible to increase simulation performance when generating large numbers of samples. Below is an example of a simple job that uses a NormalSampler directly to create 100 normally distributed samples:
```
[BurstCompile]
public struct SampleJob : IJob
{
NormalSampler sampler;
public NativeArray<float> samples;
public void Execute()
{
for (var i = 0; i < samples.Length; i++)
samples[i] = sampler.NextSample();
}
}
```
Additionally, samplers have a NativeSamples() method that can schedule a ready-made multi-threaded job intended for generating a large array of samples. Below is an example of how to combine two job handles returned by NativeSamples() to generate two arrays of samples simultaneously:
Samplers have a NativeSamples() method that can schedule a ready-made multi-threaded job intended for generating a large array of samples. Below is an example of how to combine two job handles returned by NativeSamples() to generate two arrays of samples simultaneously:
```
// Create samplers
var uniformSampler = new UniformSampler

uniformSamples.Dispose();
normalSamples.Dispose();
```
## Custom Samplers
Take a look at the [UniformSampler](../../Runtime/Randomization/Samplers/SamplerTypes/UniformSampler) and [NormalSampler](../../Runtime/Randomization/Samplers/SamplerTypes/NormalSampler) structs as references for implementing your own [ISampler](../../Runtime/Randomization/Samplers/ISampler). Note that the NativeSamples() method in the ISampler interface requires the usage of the Unity Job System. Take a look [here](https://docs.unity3d.com/Manual/JobSystem.html) to learn more about how to create jobs using the Unity Job System.

6
com.unity.perception/Documentation~/Tutorial/Phase1.md


As mentioned earlier, one of the core ingredients of the perception workflow is the randomization of various aspects of the simulation, in order to introduce sufficient variation into the generated data.
To start randomizing your simulation you will first need to add a `Scenario` to your scene. Scenarios control the execution flow of your simulation by coordinating all `Randomizer` components added to them. The Perception package comes with a useful set of Randomizers that let you quickly place your foreground objects in the Scene, generate varied backgrounds, as well as randomize various parameters of the simulation over time, including things such as position, scale, and rotation of objects, number of objects within the camera's view, and so on. Randomizers achieve this through coordinating a number of `Parameter`s, which essentially define the most granular randomization behaviors. For instance, for continuous variable types such as floats, vectors, and colors, Parameters can define the range, sampling distribution, and seed for randomization. This is while another class of Parameters let you randomly select one out of a number of categorical options.
To start randomizing your simulation you will first need to add a `Scenario` to your scene. Scenarios control the execution flow of your simulation by coordinating all `Randomizer` components added to them. The Perception package comes with a useful set of Randomizers that let you quickly place your foreground objects in the Scene, generate varied backgrounds, as well as randomize various parameters of the simulation over time, including things such as position, scale, and rotation of objects, number of objects within the camera's view, and so on. Randomizers achieve this through coordinating a number of `Parameter`s, which essentially define the most granular randomization behaviors. For instance, for continuous variable types such as floats, vectors, and colors, Parameters can define the range and sampling distribution for randomization. This is while another class of Parameters let you randomly select one out of a number of categorical options.
To summarize, a sample `Scenario` could look like this:

The background Prefabs are primitive shapes devoid of color or texture. Later Randomizers will take care of those aspects.
* **Action**: Set the rest of the properties (except for `Seed`) according to the image below. That is, `Depth = 0, Layer Count = 2, Separation Distance = 0.5, Placement Area = (6,6)`. The `Seed` attribute is the seed used for the underlying random sampler and does not need to match the image shown.
* **Action**: Set the rest of the properties according to the image below. That is, `Depth = 0, Layer Count = 2, Separation Distance = 0.5, Placement Area = (6,6)`.
<p align="center">
<img src="Images/background_randomizer.png" width = "400"/>

<p align="center">
<img src="Images/all_back_rands.png" width = "400"/>
</p>
Note that the `Seed` values do not need to match the image above.
There is one more important thing left to do, in order to make sure all the above Randomizers operate as expected. Since `BackgroundObjectPlacementRandomizer` spawns objects, it already knows which objects in the Scene it is dealing with; however, the rest of the Randomizers we added are not yet aware of what objects they should target because they don't spawn their own objects.

6
com.unity.perception/Documentation~/Tutorial/Phase2.md


* **Action**: Add `MyLightRandomizer` to the list of Randomizers in `SimulationScenario`.
You will notice that the Randomizer's UI snippet contains one Parameter named `Light Intensity Parameter`. This is the same Parameter we added in the code block above. Here, you can set the sampling distribution (`Value`), `Seed`, and `Range` for this float Parameter:
You will notice that the Randomizer's UI snippet contains one Parameter named `Light Intensity Parameter`. This is the same Parameter we added in the code block above. Here, you can set the sampling distribution (`Value`) and `Range` for this float Parameter:
<p align="center">
<img src="Images/light_rand_1.png" width="420"/>

If you now check the UI snippet for `MyLightRandomizer`, you will notice that `Color Parameter` is added. This Parameter includes four separate randomized values for `Red`, `Green`, `Blue` and `Alpha`. Note that the meaningful range for all of these values is 0-1 (and not 0-255). You can see that the sampling range for red, green, and blue is currently also set to 0-1, which means the parameter covers a full range of colors. A color with (0,0,0) RGB components essentially emits no light. So, let's increase the minimum a bit to avoid such a scenario.
* **Action**: Increase the minimum value for red, green, and blue components to 0.4 (this is an arbitrary number that typically produces good-looking results).
Each value should also already have a unique `Seed` specified. This is the seed which the sampler will use to produce a random value from the specified distribution. If two random parameters have the same seed, range, and distribution, they will always have the same value. In the case of this color, this would lead to the red, green, and blue components having equal values, and thus the produced color always being a shade of grey. As such, in order to get varied colors and not just grey, we need to make sure the seed values are different for our red, green, and blue components.
* **Action**: In the UI snippet for `MyLightRandomizer`, make sure the red, green, and blue components have different `Seed` values. Set the distribution and value for Alpha to `Constant` and 1, as we do not want to randomize the alpha component of the color.
The UI for `My Light Randomizer` should now look like this:

2
com.unity.perception/Editor/Randomization/Editors/RunInUnitySimulationWindow.cs


if (token.IsCancellationRequested)
return null;
var appParamName = $"{m_RunNameField.value}_{i}";
var appParamId = API.UploadAppParam(appParamName, new UnitySimulationConstants
var appParamId = API.UploadAppParam(appParamName, new UnitySimulationScenarioConstants
{
totalIterations = m_TotalIterationsField.value,
instanceCount = m_InstanceCountField.value,

1
com.unity.perception/Editor/Randomization/Uxml/Parameter/CategoricalParameterTemplate.uxml


<UXML xmlns="UnityEngine.UIElements" xmlns:editor="UnityEditor.UIElements">
<Toggle label="Uniform" name="uniform"/>
<editor:IntegerField label="Seed" name="seed"/>
<VisualElement class="parameter__categorical-options-list">
<ListView name="options"/>
</VisualElement>

3
com.unity.perception/Editor/Randomization/VisualElements/Parameter/ParameterElement.cs


else
uniformToggle.RegisterCallback<ChangeEvent<bool>>(evt => ToggleProbabilityFields(evt.newValue));
var seedField = template.Q<IntegerField>("seed");
seedField.BindProperty(m_SerializedProperty.FindPropertyRelative("m_Sampler.<baseSeed>k__BackingField"));
m_PropertiesContainer.Add(template);
}

6
com.unity.perception/Editor/Randomization/VisualElements/Randomizer/RandomizerList.cs


ToolbarMenu m_AddRandomizerMenu;
public HashSet<Type> randomizerTypeSet = new HashSet<Type>();
int m_PreviousListSize;
ScenarioBase scenario => (ScenarioBase)m_Property.serializedObject.targetObject;
VisualElement inspectorContainer

randomizerTypeSet.Clear();
foreach (var randomizer in scenario.randomizers)
randomizerTypeSet.Add(randomizer.GetType());
m_PreviousListSize = m_Property.arraySize;
var newRandomizer = scenario.CreateRandomizer(randomizerType);
newRandomizer.RandomizeParameterSeeds();
scenario.CreateRandomizer(randomizerType);
m_Property.serializedObject.Update();
RefreshList();
}

7
com.unity.perception/Editor/Randomization/VisualElements/Sampler/SamplerElement.cs


void CreateSampler(Type samplerType)
{
var newSampler = (ISampler)Activator.CreateInstance(samplerType);
newSampler.baseSeed = SamplerUtility.GenerateRandomSeed();
if (m_RangeProperty != null)
newSampler.range = new FloatRange(

{
if (SerializedProperty.EqualContents(currentProperty, nextSiblingProperty))
break;
if (currentProperty.name == "<baseSeed>k__BackingField")
{
m_Properties.Add(new RandomSeedField(currentProperty.Copy()));
}
else if (currentProperty.type == "FloatRange")
if (currentProperty.type == "FloatRange")
{
m_RangeProperty = currentProperty.Copy();
m_Properties.Add(new FloatRangeElement(m_RangeProperty));

49
com.unity.perception/Runtime/Randomization/Parameters/Parameter.cs


internal abstract IEnumerable<ISampler> samplers { get; }
/// <summary>
/// Constructs a new parameter
/// </summary>
protected Parameter()
{
InitializeSamplers();
}
/// <summary>
/// Returns the display name of a parameter type
/// </summary>
/// <param name="type">A subclass of Parameter</param>

return type.Name.Replace("Parameter", "");
}
/// <summary>
/// Deterministically ensures that no sampler shares the same seed when a parameter is initialized
/// </summary>
void InitializeSamplers()
{
var i = 0;
foreach (var sampler in samplers)
{
sampler.IterateState(i++);
sampler.ResetState();
}
}
internal void RandomizeSamplers()
{
foreach (var sampler in samplers)
{
sampler.baseSeed = SamplerUtility.GenerateRandomSeed();
sampler.ResetState();
}
}
/// <summary>
/// Resets the state of each sampler employed by this parameter
/// </summary>
public void ResetState()
{
foreach (var sampler in samplers)
sampler.ResetState();
}
/// <summary>
/// Offsets the state of each sampler employed by this parameter
/// </summary>
/// <param name="offsetIndex">Often the current scenario iteration</param>
public void IterateState(int offsetIndex)
{
foreach (var sampler in samplers)
sampler.IterateState(offsetIndex);
}
/// <summary>

6
com.unity.perception/Runtime/Randomization/Randomizers/Randomizer.cs


OnStopRunning();
}
}
internal void RandomizeParameterSeeds()
{
foreach (var parameter in parameters)
parameter.RandomizeSamplers();
}
}
}

2
com.unity.perception/Runtime/Randomization/Randomizers/RandomizerExamples/Randomizers/BackgroundObjectPlacementRandomizer.cs


for (var i = 0; i < layerCount; i++)
{
var seed = scenario.GenerateRandomSeedFromIndex(i);
var seed = scenario.NextRandomState();
var placementSamples = PoissonDiskSampling.GenerateSamples(
placementArea.x, placementArea.y, separationDistance, seed);
var offset = new Vector3(placementArea.x, placementArea.y, 0f) * -0.5f;

2
com.unity.perception/Runtime/Randomization/Randomizers/RandomizerExamples/Randomizers/ForegroundObjectPlacementRandomizer.cs


if (m_SpawnedObjects == null)
m_SpawnedObjects = new List<GameObject>();
var seed = scenario.GenerateRandomSeed();
var seed = scenario.NextRandomState();
var placementSamples = PoissonDiskSampling.GenerateSamples(
placementArea.x, placementArea.y, separationDistance, seed);
var offset = new Vector3(placementArea.x, placementArea.y, 0f) * -0.5f;

24
com.unity.perception/Runtime/Randomization/Samplers/ISampler.cs


public interface ISampler
{
/// <summary>
/// The base seed used to initialize this sampler's state
/// </summary>
uint baseSeed { get; set; }
/// <summary>
/// The current random state of this sampler
/// </summary>
uint state { get; set; }
/// <summary>
/// <summary>
/// Resets a sampler's state to its base random seed
/// </summary>
void ResetState();
/// <summary>
/// Deterministically offsets a sampler's state when generating values within a batched job
/// </summary>
/// <param name="offsetIndex">
/// The index used to offset the sampler's state.
/// Typically set to either the current scenario iteration or a job's batch index.
/// </param>
void IterateState(int offsetIndex);
/// <summary>
/// Generates one sample

61
com.unity.perception/Runtime/Randomization/Samplers/SamplerTypes/ConstantSampler.cs


using System;
using Unity.Burst;
using Unity.Collections;
using Unity.Jobs;

/// Returns a constant value when sampled
/// </summary>
[Serializable]
public struct ConstantSampler : ISampler
public class ConstantSampler : ISampler
{
/// <summary>
/// The value from which samples will be generated

/// <summary>
/// The base seed used to initialize this sampler's state.
/// Note that ConstantSamplers do not utilize a baseSeed.
/// </summary>
public uint baseSeed
{
get => SamplerUtility.largePrime;
set { }
}
/// <summary>
/// The current random state of this sampler.
/// Note that ConstantSamplers do not utilize a random state.
/// </summary>
public uint state
{
get => SamplerUtility.largePrime;
set { }
}
/// <summary>
/// A range bounding the values generated by this sampler
/// </summary>
public FloatRange range

}
/// <summary>
/// Resets a sampler's state to its base random seed.
/// Note that there is no state to reset for ConstantSamplers.
/// </summary>
public void ResetState() { }
/// <summary>
/// Deterministically offsets a sampler's state.
/// Note that ConstantSamplers do not have a state to iterate.
/// </summary>
/// <param name="offsetIndex">
/// The index used to offset the sampler's state.
/// Typically set to either the current scenario iteration or a job's batch index.
/// </param>
public void IterateState(int offsetIndex) { }
/// <summary>
/// Generates one sample
/// </summary>
/// <returns>The generated sample</returns>

/// <returns>A NativeArray of generated samples</returns>
public NativeArray<float> Samples(int sampleCount, out JobHandle jobHandle)
{
return SamplerUtility.GenerateSamples(this, sampleCount, out jobHandle);
var samples = new NativeArray<float>(
sampleCount, Allocator.TempJob, NativeArrayOptions.UninitializedMemory);
jobHandle = new SampleJob
{
value = value,
samples = samples
}.Schedule();
return samples;
}
[BurstCompile]
struct SampleJob : IJob
{
public float value;
public NativeArray<float> samples;
public void Execute()
{
for (var i = 0; i < samples.Length; i++)
samples[i] = value;
}
}
}
}

84
com.unity.perception/Runtime/Randomization/Samplers/SamplerTypes/NormalSampler.cs


using System;
using Unity.Burst;
using UnityEngine.Experimental.Perception.Randomization.Scenarios;
namespace UnityEngine.Experimental.Perception.Randomization.Samplers
{

/// </summary>
[Serializable]
public struct NormalSampler : ISampler
public class NormalSampler : ISampler
[SerializeField, HideInInspector] Unity.Mathematics.Random m_Random;
/// <summary>
/// The mean of the normal distribution to sample from
/// </summary>

public float standardDeviation;
/// <summary>
/// The base seed used to initialize this sampler's state
/// </summary>
[field: SerializeField] public uint baseSeed { get; set; }
/// <summary>
/// The current random state of this sampler
/// </summary>
public uint state
{
get => m_Random.state;
set => m_Random = new Unity.Mathematics.Random { state = value };
}
/// <summary>
/// A range bounding the values generated by this sampler
/// </summary>
[field: SerializeField]

/// <param name="max">The largest value contained within the range</param>
/// <param name="mean">The mean of the normal distribution to sample from</param>
/// <param name="standardDeviation">The standard deviation of the normal distribution to sample from</param>
/// <param name="baseSeed">The base random seed to use for this sampler</param>
float min, float max, float mean, float standardDeviation, uint baseSeed=SamplerUtility.largePrime)
float min, float max, float mean, float standardDeviation)
this.baseSeed = baseSeed;
m_Random.state = baseSeed;
}
/// <summary>
/// Resets a sampler's state to its base random seed
/// </summary>
public void ResetState()
{
state = baseSeed;
}
/// <summary>
/// Deterministically offsets a sampler's state
/// </summary>
/// <param name="offsetIndex">
/// The index used to offset the sampler's state.
/// Typically set to either the current scenario iteration or a job's batch index.
/// </param>
public void IterateState(int offsetIndex)
{
state = SamplerUtility.IterateSeed((uint)offsetIndex, state);
}
/// <summary>

public float Sample()
{
var rng = new Unity.Mathematics.Random(ScenarioBase.activeScenario.NextRandomState());
m_Random.NextFloat(), range.minimum, range.maximum, mean, standardDeviation);
rng.NextFloat(), range.minimum, range.maximum, mean, standardDeviation);
}
/// <summary>

/// <returns>A NativeArray of generated samples</returns>
public NativeArray<float> Samples(int sampleCount, out JobHandle jobHandle)
{
var samples = SamplerUtility.GenerateSamples(this, sampleCount, out jobHandle);
IterateState(sampleCount);
var samples = new NativeArray<float>(
sampleCount, Allocator.TempJob, NativeArrayOptions.UninitializedMemory);
jobHandle = new SampleJob
{
min = range.minimum,
max = range.maximum,
mean = mean,
standardDeviation = standardDeviation,
seed = ScenarioBase.activeScenario.NextRandomState(),
samples = samples
}.ScheduleBatch(sampleCount, SamplerUtility.samplingBatchSize);
}
[BurstCompile]
struct SampleJob : IJobParallelForBatch
{
public float min;
public float max;
public float mean;
public float standardDeviation;
public uint seed;
public NativeArray<float> samples;
public void Execute(int startIndex, int count)
{
var endIndex = startIndex + count;
var batchIndex = startIndex / SamplerUtility.samplingBatchSize;
var rng = new Unity.Mathematics.Random(SamplerUtility.IterateSeed((uint)batchIndex, seed));
for (var i = startIndex; i < endIndex; i++)
{
samples[i] = SamplerUtility.TruncatedNormalSample(
rng.NextFloat(), min, max, mean, standardDeviation);
}
}
}
}
}

77
com.unity.perception/Runtime/Randomization/Samplers/SamplerTypes/UniformSampler.cs


using System;
using Unity.Burst;
using UnityEngine.Experimental.Perception.Randomization.Scenarios;
namespace UnityEngine.Experimental.Perception.Randomization.Samplers
{

[Serializable]
public struct UniformSampler : ISampler
public class UniformSampler : ISampler
[SerializeField, HideInInspector] Unity.Mathematics.Random m_Random;
/// <summary>
/// The base seed used to initialize this sampler's state
/// </summary>
[field: SerializeField] public uint baseSeed { get; set; }
/// <summary>
/// The current random state of this sampler
/// </summary>
public uint state
{
get => m_Random.state;
set => m_Random = new Unity.Mathematics.Random { state = value };
}
/// <summary>
/// A range bounding the values generated by this sampler
/// </summary>

/// </summary>
/// <param name="min">The smallest value contained within the range</param>
/// <param name="max">The largest value contained within the range</param>
/// <param name="baseSeed">The base random seed to use for this sampler</param>
public UniformSampler(float min, float max, uint baseSeed=SamplerUtility.largePrime)
public UniformSampler(float min, float max)
this.baseSeed = baseSeed;
m_Random.state = baseSeed;
}
/// <summary>
/// Resets a sampler's state to its base random seed
/// </summary>
public void ResetState()
{
state = baseSeed;
}
/// <summary>
/// Deterministically offsets a sampler's state
/// </summary>
/// <param name="offsetIndex">
/// The index used to offset the sampler's state.
/// Typically set to either the current scenario iteration or a job's batch index.
/// </param>
public void IterateState(int offsetIndex)
{
state = SamplerUtility.IterateSeed((uint)offsetIndex, state);
}
/// <summary>

public float Sample()
{
return math.lerp(range.minimum, range.maximum, m_Random.NextFloat());
var rng = new Unity.Mathematics.Random(ScenarioBase.activeScenario.NextRandomState());
return math.lerp(range.minimum, range.maximum, rng.NextFloat());
}
/// <summary>

/// <returns>A NativeArray of generated samples</returns>
public NativeArray<float> Samples(int sampleCount, out JobHandle jobHandle)
{
var samples = SamplerUtility.GenerateSamples(this, sampleCount, out jobHandle);
IterateState(sampleCount);
var samples = new NativeArray<float>(
sampleCount, Allocator.TempJob, NativeArrayOptions.UninitializedMemory);
jobHandle = new SampleJob
{
min = range.minimum,
max = range.maximum,
seed = ScenarioBase.activeScenario.NextRandomState(),
samples = samples
}.ScheduleBatch(sampleCount, SamplerUtility.samplingBatchSize);
}
[BurstCompile]
struct SampleJob : IJobParallelForBatch
{
public float min;
public float max;
public uint seed;
public NativeArray<float> samples;
public void Execute(int startIndex, int count)
{
var endIndex = startIndex + count;
var batchIndex = startIndex / SamplerUtility.samplingBatchSize;
var rng = new Unity.Mathematics.Random(SamplerUtility.IterateSeed((uint)batchIndex, seed));
for (var i = startIndex; i < endIndex; i++)
samples[i] = rng.NextFloat(min, max);
}
}
}
}

57
com.unity.perception/Runtime/Randomization/Samplers/SamplerUtility.cs


using System;
using System.Runtime.CompilerServices;
using Unity.Burst;
using Unity.Collections;
using Unity.Jobs;
using Unity.Mathematics;
namespace UnityEngine.Experimental.Perception.Randomization.Samplers

/// </summary>
public static class SamplerUtility
{
internal const uint largePrime = 0x202A96CF;
const int k_SamplingBatchSize = 64;
/// <summary>
/// A large prime number
/// </summary>
public const uint largePrime = 0x202A96CF;
/// <summary>
/// The number of samples to generate per job batch in an IJobParallelForBatch job
/// </summary>
public const int samplingBatchSize = 64;
/// <summary>
/// Returns the sampler's display name

/// <param name="x">Unsigned integer to hash</param>
/// <returns>The calculated hash value</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
static uint Hash32(uint x) {
public static uint Hash32(uint x)
{
x = ((x >> 16) ^ x) * 0x45d9f3b;
x = ((x >> 16) ^ x) * 0x45d9f3b;
x = (x >> 16) ^ x;

/// <param name="x">64-bit value to hash</param>
/// <returns>The calculated hash value</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
static ulong Hash64(ulong x) {
public static ulong Hash64(ulong x)
{
x = (x ^ (x >> 30)) * 0xbf58476d1ce4e5b9ul;
x = (x ^ (x >> 27)) * 0x94d049bb133111ebul;
x ^= (x >> 31);

{
var state = (uint)Hash64(((ulong)index << 32) | baseSeed);
return state == 0u ? largePrime : state;
}
/// <summary>
/// Schedules a multi-threaded job to generate an array of samples
/// </summary>
/// <param name="sampler">The sampler to generate samples from</param>
/// <param name="sampleCount">The number of samples to generate</param>
/// <param name="jobHandle">The handle of the scheduled job</param>
/// <typeparam name="T">The type of sampler to sample</typeparam>
/// <returns>A NativeArray of generated samples</returns>
public static NativeArray<float> GenerateSamples<T>(
T sampler, int sampleCount, out JobHandle jobHandle) where T : struct, ISampler
{
var samples = new NativeArray<float>(
sampleCount, Allocator.TempJob, NativeArrayOptions.UninitializedMemory);
jobHandle = new SampleJob<T>
{
sampler = sampler,
samples = samples
}.ScheduleBatch(sampleCount, k_SamplingBatchSize);
return samples;
}
[BurstCompile]
struct SampleJob<T> : IJobParallelForBatch where T : ISampler
{
public T sampler;
public NativeArray<float> samples;
public void Execute(int startIndex, int count)
{
var endIndex = startIndex + count;
var batchIndex = startIndex / k_SamplingBatchSize;
sampler.IterateState(batchIndex);
for (var i = startIndex; i < endIndex; i++)
samples[i] = sampler.Sample();
}
}
/// <summary>

2
com.unity.perception/Runtime/Randomization/Scenarios/FixedLengthScenario.cs


/// Constants describing the execution of this scenario
/// </summary>
[Serializable]
public class Constants : UnitySimulationConstants
public class Constants : UnitySimulationScenarioConstants
{
/// <summary>
/// The number of frames to generate per iteration

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


/// <summary>
/// The base class of scenarios with serializable constants
/// </summary>
/// <typeparam name="T">The type of constants to serialize</typeparam>
public abstract class Scenario<T> : ScenarioBase where T : new()
/// <typeparam name="T">The type of scenario constants to serialize</typeparam>
public abstract class Scenario<T> : ScenarioBase where T : ScenarioConstants, new()
{
/// <summary>
/// A construct containing serializable constants that control the execution of this scenario

/// <summary>
/// Returns this scenario's non-typed serialized constants
/// </summary>
public override object genericConstants => constants;
public override ScenarioConstants genericConstants => constants;
/// <summary>
/// Serializes this scenario's constants to a json file in the Unity StreamingAssets folder

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


using UnityEngine.Experimental.Perception.Randomization.Randomizers;
using UnityEngine.Experimental.Perception.Randomization.Samplers;
using UnityEngine.Perception.GroundTruth;
using UnityEngine.Rendering;
namespace UnityEngine.Experimental.Perception.Randomization.Scenarios
{

{
static ScenarioBase s_ActiveScenario;
uint m_RandomState = SamplerUtility.largePrime;
bool m_SkipFrame = true;
bool m_FirstScenarioFrame = true;
bool m_WaitingForFinalUploads;

[HideInInspector] public bool quitOnComplete = true;
/// <summary>
/// The random state of the scenario
/// </summary>
public uint randomState => m_RandomState;
/// <summary>
[HideInInspector] public string serializedConstantsFileName = "constants";
public virtual string serializedConstantsFileName => "constants";
/// <summary>
/// Returns the active parameter scenario in the scene

/// <summary>
/// Returns this scenario's non-typed serialized constants
/// </summary>
public abstract object genericConstants { get; }
public abstract ScenarioConstants genericConstants { get; }
/// <summary>
/// The number of frames that have elapsed since the current scenario iteration was Setup

s_ActiveScenario = null;
}
void Reset()
{
activeScenario = this;
}
var randomSeedMetricDefinition = DatasetCapture.RegisterMetricDefinition(
"random-seed",
"The random seed used to initialize the random state of the simulation. Only triggered once per simulation.",
Guid.Parse("14adb394-46c0-47e8-a3f0-99e754483b76"));
DatasetCapture.ReportMetric(randomSeedMetricDefinition, new[] { genericConstants.randomSeed });
Deserialize();
}

if (currentIterationFrame == 0)
{
DatasetCapture.StartNewSequence();
IterateParameterStates();
m_RandomState = SamplerUtility.IterateSeed((uint)currentIteration, genericConstants.randomSeed);
foreach (var randomizer in activeRandomizers)
randomizer.IterationStart();
}

}
/// <summary>
/// Generates a random seed by hashing the current scenario iteration with a given base random seed
/// </summary>
/// <param name="baseSeed">Used to offset the seed generator</param>
/// <returns>The generated random seed</returns>
public uint GenerateRandomSeed(uint baseSeed = SamplerUtility.largePrime)
{
var seed = SamplerUtility.IterateSeed((uint)currentIteration, baseSeed);
return SamplerUtility.IterateSeed((uint)currentIteration, seed);
}
/// <summary>
/// Generates a random seed by hashing three values together: an arbitrary index value,
/// the current scenario iteration, and a base random seed. This method is useful for deterministically
/// generating random seeds from within a for-loop.
/// Generates a new random state and overwrites the old random state with the newly generated value
/// <param name="iteration">An offset value hashed inside the seed generator</param>
/// <param name="baseSeed">An offset value hashed inside the seed generator</param>
/// <returns>The generated random seed</returns>
public uint GenerateRandomSeedFromIndex(int iteration, uint baseSeed = SamplerUtility.largePrime)
/// <returns>The newly generated random state</returns>
public uint NextRandomState()
var seed = SamplerUtility.IterateSeed((uint)iteration, baseSeed);
return SamplerUtility.IterateSeed((uint)currentIteration, seed);
m_RandomState = SamplerUtility.Hash32(m_RandomState);
return m_RandomState;
}
void ValidateParameters()

parameter.Validate();
}
void IterateParameterStates()
{
foreach (var randomizer in m_Randomizers)
{
foreach (var parameter in randomizer.parameters)
{
parameter.ResetState();
parameter.IterateState(currentIteration);
}
}
}
}
}

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


/// Defines a scenario that is compatible with the Run in Unity Simulation window
/// </summary>
/// <typeparam name="T">The type of constants to serialize</typeparam>
public abstract class UnitySimulationScenario<T> : Scenario<T> where T : UnitySimulationConstants, new()
public abstract class UnitySimulationScenario<T> : Scenario<T> where T : UnitySimulationScenarioConstants, new()
{
/// <summary>
/// Returns whether the entire scenario has completed

base.Deserialize();
currentIteration = constants.instanceIndex;
}
}
/// <summary>
/// A class encapsulating the scenario constants fields required for Unity Simulation cloud execution
/// </summary>
[Serializable]
public class UnitySimulationConstants
{
/// <summary>
/// The total number of iterations to run a scenario for
/// </summary>
public int totalIterations = 100;
/// <summary>
/// The number of Unity Simulation instances assigned to executed this scenario
/// </summary>
public int instanceCount = 1;
/// <summary>
/// The Unity Simulation instance index of the currently executing worker
/// </summary>
public int instanceIndex;
}
}

4
com.unity.perception/Tests/Runtime/Randomization/ParameterTests/StructParameterTests.cs


using NUnit.Framework;
using UnityEngine;
using UnityEngine.Experimental.Perception.Randomization.Parameters;
using UnityEngine.Experimental.Perception.Randomization.Scenarios;
using Object = UnityEngine.Object;
namespace RandomizationTests.ParameterTests

public void Setup()
{
m_TestObject = new GameObject();
m_TestObject.AddComponent<FixedLengthScenario>();
m_Tests = new BaseStructParameterTest[]
{
new NumericParameterTest<bool>(new BooleanParameter()),

}
[Test]
public void EquivalentManagedAndNativeSamples()
public void CorrectNumberOfNativeSamplesAreGenerated()
{
foreach (var test in m_Tests)
test.GeneratesNativeSamples();

2
com.unity.perception/Tests/Runtime/Randomization/SamplerTests/ConstantSamplerTests.cs


[Test]
public void ConstantSamplerGeneratesConstantValues()
{
var constantSampler = new ConstantSampler();
var constantSampler = new ConstantSampler(0f);
var sample1 = constantSampler.Sample();
var sample2 = constantSampler.Sample();
Assert.AreEqual(sample1, sample2);

28
com.unity.perception/Tests/Runtime/Randomization/SamplerTests/SamplerTestsBase.cs


using System;
using NUnit.Framework;
using Unity.Jobs;
using UnityEngine;
using UnityEngine.Experimental.Perception.Randomization.Scenarios;
using Object = UnityEngine.Object;
public abstract class RangedSamplerTests<T> where T : struct, ISampler
public abstract class RangedSamplerTests<T> where T : ISampler
GameObject m_ScenarioObj;
static ScenarioBase activeScenario => ScenarioBase.activeScenario;
m_ScenarioObj = new GameObject("Scenario");
m_ScenarioObj.AddComponent<FixedLengthScenario>();
}
[TearDown]
public void TearDown()
{
Object.DestroyImmediate(m_ScenarioObj);
}
[Test]

[Test]
public void ConsecutiveSamplesChangesState()
{
var state0 = m_Sampler.state;
var state0 = activeScenario.randomState;
var state1 = m_Sampler.state;
var state1 = activeScenario.randomState;
var state2 = m_Sampler.state;
var state2 = activeScenario.randomState;;
Assert.AreNotEqual(state0, state1);
Assert.AreNotEqual(state1, state2);

public void ConsecutiveSampleBatchesChangesState()
{
var state0 = m_Sampler.state;
var state0 = activeScenario.randomState;
var state1 = m_Sampler.state;
var state1 = activeScenario.randomState;
var state2 = m_Sampler.state;
var state2 = activeScenario.randomState;
JobHandle.CombineDependencies(handle1, handle2).Complete();

7
com.unity.perception/Tests/Runtime/Randomization/ScenarioTests.cs


public IEnumerator OverwritesConstantsOnSerialization()
{
yield return CreateNewScenario(10, 10);
m_Scenario.serializedConstantsFileName = "perception_serialization_test";
var constants = new FixedLengthScenario.Constants
{

public IEnumerator GeneratedRandomSeedsChangeWithScenarioIteration()
{
yield return CreateNewScenario(3, 1);
var seed = m_Scenario.GenerateRandomSeed();
seeds[i] = m_Scenario.GenerateRandomSeedFromIndex(i);
seeds[i] = m_Scenario.NextRandomState();
Assert.AreNotEqual(seed, m_Scenario.GenerateRandomSeed());
Assert.AreNotEqual(seeds[i], m_Scenario.GenerateRandomSeedFromIndex(i));
Assert.AreNotEqual(seeds[i], m_Scenario.NextRandomState());
}
PerceptionCamera SetupPerceptionCamera()

18
com.unity.perception/Runtime/Randomization/Scenarios/ScenarioConstants.cs


using System;
using UnityEngine.Experimental.Perception.Randomization.Samplers;
namespace UnityEngine.Experimental.Perception.Randomization.Scenarios
{
/// <summary>
/// The base class for implementing custom scenario constants classes
/// </summary>
[Serializable]
public class ScenarioConstants
{
/// <summary>
/// The starting value initializing all random values sequences generated through Samplers, Parameters, and
/// Randomizers attached to a Scenario
/// </summary>
public uint randomSeed = SamplerUtility.largePrime;
}
}

3
com.unity.perception/Runtime/Randomization/Scenarios/ScenarioConstants.cs.meta


fileFormatVersion: 2
guid: a38190d91c3848f98817192bad820027
timeCreated: 1608664905

26
com.unity.perception/Runtime/Randomization/Scenarios/UnitySimulationScenarioConstants.cs


using System;
namespace UnityEngine.Experimental.Perception.Randomization.Scenarios
{
/// <summary>
/// A class encapsulating the scenario constants fields required for Unity Simulation cloud execution
/// </summary>
[Serializable]
public class UnitySimulationScenarioConstants : ScenarioConstants
{
/// <summary>
/// The total number of iterations to run a scenario for
/// </summary>
public int totalIterations = 100;
/// <summary>
/// The number of Unity Simulation instances assigned to executed this scenario
/// </summary>
public int instanceCount = 1;
/// <summary>
/// The Unity Simulation instance index of the currently executing worker
/// </summary>
public int instanceIndex;
}
}

3
com.unity.perception/Runtime/Randomization/Scenarios/UnitySimulationScenarioConstants.cs.meta


fileFormatVersion: 2
guid: 362fc5761e034959aebfaf497d7f7b75
timeCreated: 1608664937

15
com.unity.perception/Editor/Randomization/VisualElements/Sampler/RandomSeedField.cs


using UnityEditor;
using UnityEditor.UIElements;
using UnityEngine.UIElements;
namespace UnityEngine.Experimental.Perception.Randomization.Editor
{
class RandomSeedField : IntegerField
{
public RandomSeedField(SerializedProperty property)
{
label = "Seed";
this.BindProperty(property);
}
}
}

11
com.unity.perception/Editor/Randomization/VisualElements/Sampler/RandomSeedField.cs.meta


fileFormatVersion: 2
guid: 7df00c41d9394fa0ae239349350cb563
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
正在加载...
取消
保存