Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
69 changes: 25 additions & 44 deletions Assets/ECS_MLAgents_v0/Core/AgentSystem.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
using Unity.Entities;
using Unity.Jobs;
using UnityEngine;
using Unity.Burst;

namespace ECS_MLAgents_v0.Core
{
Expand Down Expand Up @@ -35,29 +36,27 @@ namespace ECS_MLAgents_v0.Core
* data in batch and generate a new NativeArray<float> that will be used to populate the
* Actuator data of all compatible Entities.
*/
public abstract class AgentSystem<TS, TA> : JobComponentSystem, IAgentSystem
public abstract class AgentSystem<TS, TA> : JobComponentSystem, IAgentSystem<TS, TA>
where TS : struct, IComponentData
where TA : struct, IComponentData
{
private const int INITIAL_MEMORY_SIZE = 1024;
private const int SIZE_OF_FLOAT_IN_MEMORY = 4;

private int _sensorMemorySize = INITIAL_MEMORY_SIZE;
private int _actuatorMemorySize = INITIAL_MEMORY_SIZE;
private int _currentNAgents = INITIAL_MEMORY_SIZE;

public IDecisionRequester DecisionRequester { get; set; }
private int _phase;

public IAgentDecision Decision { get; set; }
public IAgentDecision<TS, TA> Decision { get; set; }

private ComponentGroup _componentGroup;
private int _sensorSize;
private int _actuatorSize;
// TODO : Make sure there is not extra cost for memory allocation here and when copying
private NativeArray<float> _sensorTensor =
new NativeArray<float>(INITIAL_MEMORY_SIZE, Allocator.Persistent);
private NativeArray<float> _actuatorTensor =
new NativeArray<float>(INITIAL_MEMORY_SIZE, Allocator.Persistent);
private NativeArray<TS> _sensorTensor =
new NativeArray<TS>(INITIAL_MEMORY_SIZE, Allocator.Persistent);
private NativeArray<TA> _actuatorTensor =
new NativeArray<TA>(INITIAL_MEMORY_SIZE, Allocator.Persistent);

// TODO : Decide if we want to keep at all
private Logger _logger;
Expand Down Expand Up @@ -138,17 +137,13 @@ protected override JobHandle OnUpdate(JobHandle inputDeps)
* If there was more agents than allowed by the memory allocation of the sensor or
* actuator, then the size is updated to the required size.
*/
if (nAgents * _sensorSize / SIZE_OF_FLOAT_IN_MEMORY > _sensorMemorySize)
if (nAgents > _currentNAgents)
{
_sensorMemorySize = nAgents * _sensorSize / SIZE_OF_FLOAT_IN_MEMORY;
_currentNAgents = nAgents;
_sensorTensor.Dispose();
_sensorTensor = new NativeArray<float>(_sensorMemorySize, Allocator.Persistent);
}
if (nAgents * _actuatorSize / SIZE_OF_FLOAT_IN_MEMORY > _actuatorMemorySize)
{
_actuatorMemorySize = nAgents * _actuatorSize / SIZE_OF_FLOAT_IN_MEMORY;
_actuatorTensor.Dispose();
_actuatorTensor = new NativeArray<float>(_actuatorMemorySize, Allocator.Persistent);
_sensorTensor = new NativeArray<TS>(_currentNAgents, Allocator.Persistent);
_actuatorTensor = new NativeArray<TA>(_currentNAgents, Allocator.Persistent);
}

/*
Expand All @@ -167,68 +162,54 @@ protected override JobHandle OnUpdate(JobHandle inputDeps)
{
Sensors = sensors,
SensorTensor = _sensorTensor,
SensorSize = _sensorSize
};
handle = copySensorsJob.Schedule(nAgents, 64, handle);

handle.Complete();

/*
* The Decision is called here to populate the NativeArray<float> of Actuators.
*/
handle = Decision.DecideBatch(ref _sensorTensor,
ref _actuatorTensor,
_sensorSize / SIZE_OF_FLOAT_IN_MEMORY,
_actuatorSize / SIZE_OF_FLOAT_IN_MEMORY,
nAgents,
handle);

/*

Decision.BatchProcess(ref _sensorTensor, ref _actuatorTensor, 0, nAgents);

/*
* Copy the data from the actuator NativeArray<float> to the actuators of each entity.
*/
var copyActuatorsJob = new CopyActuatorsJob
{
ActuatorTensor = _actuatorTensor,
Actuators = actuators,
ActuatorSize = _actuatorSize

};

return copyActuatorsJob.Schedule(nAgents, 64, handle);

}

/*
* This IJobParallelFor copied the Sensor data into a NativeArray<float>
*/
// [BurstCompile]
[BurstCompile]
private struct CopySensorsJob : IJobParallelFor
{
[ReadOnly] public ComponentDataArray<TS> Sensors;
public NativeArray<float> SensorTensor;
[ReadOnly] public int SensorSize;
public NativeArray<TS> SensorTensor;

public void Execute(int i)
{
TensorUtility.CopyToNativeArray(Sensors[i], SensorTensor, i * SensorSize);
SensorTensor[i] = Sensors[i];
}
}

/*
* This IJobParallelFor copies the Actuator data to the appropriate IComponentData
*/
// [BurstCompile]
[BurstCompile]
private struct CopyActuatorsJob : IJobParallelFor
{

public ComponentDataArray<TA> Actuators;
public NativeArray<float> ActuatorTensor;
[ReadOnly] public int ActuatorSize;
public NativeArray<TA> ActuatorTensor;

public void Execute(int i)
{
var tmp = Actuators[i];
// TODO : Make sure there is no extra cost here
TensorUtility.CopyFromNativeArray(ActuatorTensor, out tmp, i * ActuatorSize);
Actuators[i] = tmp;
Actuators[i] = ActuatorTensor[i];
}
}
}
Expand Down
93 changes: 67 additions & 26 deletions Assets/ECS_MLAgents_v0/Core/ExternalDecision.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,13 @@
using Unity.Collections.LowLevel.Unsafe;
using UnityEngine;
using UnityEngine.Profiling;
using Unity.Entities;


namespace ECS_MLAgents_v0.Core{
public class ExternalDecision : IAgentDecision
public class ExternalDecision<TS, TA> : IAgentDecision<TS, TA>
where TS : struct, IComponentData
where TA : struct, IComponentData
{

// [ Unity Ready (1) , nAgents (4) , sensorSize (4) , actuatorSize (4) , Data
Expand All @@ -26,7 +29,15 @@ public class ExternalDecision : IAgentDecision
private const int ACTUATOR_DATA_POSITION = 100001;


private float[] actuatorData = new float[0];
private TA[] actuatorData = new TA[0];



System.Type _sensorType;
System.Type _actuatorType;

private int _sensorSize;
private int _actuatorSize;

// This is a temporary test file
// TODO : Replace with a file creation system
Expand All @@ -47,42 +58,49 @@ public ExternalDecision()
Debug.Log("Is Ready to Communicate");
}

public JobHandle DecideBatch(
ref NativeArray<float> sensor,
ref NativeArray<float> actuator,
int sensorSize,
int actuatorSize,
int nAgents,
JobHandle handle)
public void BatchProcess(ref NativeArray<TS> sensors, ref NativeArray<TA> actuators, int offset = 0, int size = -1)
{
Profiler.BeginSample("Communicating");
if (sensor.Length > 4 * 50000)
Profiler.BeginSample("__Communicating");

Profiler.BeginSample("__TypeCheck");
VerifySensor(typeof(TS));
VerifyActuator(typeof(TA));
if (size ==-1){
size = sensors.Length - offset;
}
Profiler.EndSample();

Profiler.BeginSample("__VerifyLength");
int batch = size;
if (sensors.Length != actuators.Length)
{
throw new Exception("TOO much data to send");
throw new Exception("Error in the length of the sensors and actuators");
}
if (actuator.Length > 4 * 50000)

if (batch > 50000)
{
throw new Exception("TOO much data to send");
}

if (actuatorData.Length < actuator.Length)
if (actuatorData.Length < _actuatorSize* batch)
{
actuatorData = new float[actuator.Length];
actuatorData = new TA[batch];
}
Profiler.EndSample();

Profiler.BeginSample("__Write");
accessor.Write(NUMBER_AGENTS_POSITION, batch);
accessor.Write(SENSOR_SIZE_POSITION, _sensorSize);
accessor.Write(ACTUATOR_SIZE_POSITION, _actuatorSize);

accessor.Write(NUMBER_AGENTS_POSITION, nAgents);
accessor.Write(SENSOR_SIZE_POSITION, sensorSize);
accessor.Write(ACTUATOR_SIZE_POSITION, actuatorSize);

accessor.WriteArray(SENSOR_DATA_POSITION, sensor.ToArray(), 0, sensor.Length);
accessor.WriteArray(SENSOR_DATA_POSITION, sensors.Slice(offset, batch).ToArray(), 0, batch);

accessor.Write(PYTHON_READY_POSITION, false);

accessor.Write(UNITY_READY_POSITION, true);
Profiler.EndSample();


Profiler.BeginSample("__Wait");
var readyToContinue = false;
int loopIter = 0;
while (!readyToContinue)
Expand All @@ -95,12 +113,35 @@ public JobHandle DecideBatch(
Debug.Log("Missed Communication");
}
}
Profiler.EndSample();

Profiler.BeginSample("__Read");
accessor.ReadArray(ACTUATOR_DATA_POSITION, actuatorData, 0, batch);

actuators.Slice(offset, batch).CopyFrom(actuatorData);

// for(var i = 0; i< batch; i++){
// actuators[i] = actuatorData[i];
// }

accessor.ReadArray(ACTUATOR_DATA_POSITION, actuatorData, 0, actuator.Length);
actuator.CopyFrom(actuatorData);
Profiler.EndSample();
Profiler.EndSample();

Profiler.BeginSample("Communicating");
return handle;
}

private void VerifySensor(System.Type t){
if (! t.Equals(_sensorType)){
TensorUtility.DebugCheckStructure(t);
_sensorSize = System.Runtime.InteropServices.Marshal.SizeOf(t) / 4;
_sensorType = t;
}
}
private void VerifyActuator(System.Type t){
if (! t.Equals(_actuatorType)){
TensorUtility.DebugCheckStructure(t);
_actuatorSize = System.Runtime.InteropServices.Marshal.SizeOf(t) / 4;
_actuatorType = t;
}
}
}
}
35 changes: 14 additions & 21 deletions Assets/ECS_MLAgents_v0/Core/IAgentDecision.cs
Original file line number Diff line number Diff line change
@@ -1,34 +1,27 @@
using Unity.Collections;
using Unity.Jobs;
using Unity.Entities;

namespace ECS_MLAgents_v0.Core
{
/*
* The Interface to define a Decision process by which a bach of agent updates its actuator
* based on the information present in the sensor.
*/
public interface IAgentDecision
public interface IAgentDecision<TS, TA>
where TS : struct
where TA : struct
{
/// <summary>
/// DecideBatch updates the aggregated actuators of the agents present in the batch from
/// the aggregated actuators.
/// DecideBatch updates the actuators of the agents present in the batch from
/// the data present in the sensors.
/// </summary>
/// <param name="sensor">The aggregated data for the sensor information present in the
/// batch. The sensor data is linearly arranged.</param>
/// <param name="actuator">The aggregated data for the actuator information present in the
/// batch. The sensor data is linearly arranged.</param>
/// <param name="sensorSize">The number of float values present in a sensor for one agent
/// </param>
/// <param name="actuatorSize">The number of float values present in an actuator
/// for one agent</param>
/// <param name="nAgents">The number of agents present in the batch</param>
/// <param name="handle">The JobHandle for the input dependencies.</param>
/// <returns>The Job Handle for the output dependencies.</returns>
JobHandle DecideBatch(ref NativeArray<float> sensor,
ref NativeArray<float> actuator,
int sensorSize,
int actuatorSize,
int nAgents,
JobHandle handle);
/// <param name="sensors">The aggregated data for the sensor information present in the
/// batch. T.</param>
/// <param name="actuators">The aggregated data for the actuator information present in the
/// batch. </param>
void BatchProcess(ref NativeArray<TS> sensors, ref NativeArray<TA> actuators, int offset = 0, int size = -1);

// TODO : It is debatable wether or not we want to enforce the type here
}

}
6 changes: 4 additions & 2 deletions Assets/ECS_MLAgents_v0/Core/IAgentSystem.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,9 @@

namespace ECS_MLAgents_v0.Core
{
public interface IAgentSystem
public interface IAgentSystem<TS, TA>
where TS : struct , IComponentData
where TA:struct, IComponentData
{
/// <summary>
/// If true, the AgentSystem will perform on the agents
Expand All @@ -12,7 +14,7 @@ public interface IAgentSystem
/// <summary>
/// The IAgentDecision that will be used to update the Actuators of compatible Entities.
/// </summary>
IAgentDecision Decision { get; set; }
IAgentDecision<TS, TA> Decision { get; set; }

/// <summary>
/// This method defines what are the required ComponentType that are needed on an Entity
Expand Down
Loading