Unity DOTS

DOTS는 Data-Oriented Technology Stack의 약자. 기존의 Object-Oriented 디자인이었던 유니티의 전통적인 방식이 아닌 데이터에 초점을 맞춘 아키텍처.

Unity DOTS 설명 링크

  • ECS - entity, component, system 으로 구성된 data-oriented 프로그래밍 디자인

  • C# Job System - 멀티 스레딩을 안전한 환경에서 사용하며, 멀티 코어를 활용할 수 있는 시스템

  • Burst compiler -새로운 LLVM 기반 백엔드 컴파일러 기술을 통해 C# 잡을 수행하고 고도로 최적화된 머신 코드를 생성

Unite Austin 2017 - Writing High Performance C# Scripts의 한 장면 캡처한 것


ECS

Entity Component System의 약자. 기본 세 가지 요소는 Entity, Component(data)와 System(behavior)이다. Data에 중점을 두고 있으며, System Entity가 인덱싱 한 Component Data의 스트림을 읽어 입력 상태에서 출력 상태로 데이터를 변환한다.

  • Entity는 Component들을 담는 index

  • Component는 Data

  • System은 Behaviour.

    core ecs unity manual에 설명이 잘 되어있음 (영어로)

Concepts

C# Job System + ECS usage and demo with Intel - Unite LA

CPU 성능과 RAM성능의 차이는 점점 늘어나고 있다.

데이터를 촘촘하게 저장하여 캐시 적중률을 높여 자주 쓰이는 데이터를 메모리를 통하지 않고 빠르게 읽어올 수 있다.

classic 한 방법으론 메모리에 데이터가 여기저기 흩어져 저장되어 읽어오는 속도가 느리다.

ECS는 같은 component들을 가진 Entity들을 Archetype로 분류하고 메모리 Chunk에 저장하여 data를 체계화한다.


Entity

Entity는 behavior도 data도 가지고 있지 않다. (Systems-behavior 제공, Components-data 저장). Entity는 어떤 data Component를 가지고 있는지 식별하는 ID이다. 이름이 없는 초경량 GameObject라고 생각할 수 있다.

Entity Manager

World 당 하나의 EntityManager를 가지고 있다. EntityManager은 해당 World의 모든 Entity들을 관리한다. Entity리스트를 유지하고 Entity와 연관된 data를 체계화한다.

Entity 생성 코드

namespace Unity.Entities
{
  public sealed class EntityManager
  {
    ...
    public Entity CreateEntity(params ComponentType[] types);
    public Entity CreateEntity();
    public void CreateEntity(EntityArchetype archetype, NativeArray<Entity> entities);
    public Entity CreateEntity(EntityArchetype archetype);
    ...
  }
}

Entity가 타입을 가지진 않지만 Data Components 타입별로 분류될 수 있다. = Archetype

Entity들을 생성하고 Component들을 추가하면 EntityManager가 원래 있던 Entity들과 같은 Archetype을 찾는다.

Archetype

같은 Component를 가진 Entity들의 조합.

Entity Component들을 추가할 때, EntityManager EntityArchetype구조체를 생성한다.

원래 있던 EntityArchetype을 사용하여 해당 Archetype과 일치하는 새로운 Entity들을 생성할 수 있으며 미리 EntityArchetype을 생성하고 Entity들을 생성할 수도 있다.

EntityArchetype archetype = EntityManager.CreateArchetype(
typeof(Position),
typeof(Rotation),
typeof(LinearMovement));

var entity = EntityManager.CreateEntity(archetype);

 

Memory Chunks

같은 ArcheType Entity들을 저장하는 메모리 블록 scripting API

Entity들이 만들어졌을 때 메모리 chunk 가 꽉 차면 새로운 메모리 chunk가 할당된다. ()

Component가 추가되거나 제거되면서 Archetpe이 바뀌면 해당 Entity Component들은 다른 chunk로 이동된다.

Chunk는 배열들로 구성되어 있는데, 위의 이미지처럼 data는 각각의 배열을 가진다. 각 배열들의 길이가 같으며 배열의 길이는 Chunk에서 저장하는 Entity개수와 같다. (그림을 보면 Position, Rotation, Renderer 데이터를 가진 Entity가 12개 있다는 뜻)

Chunk는 16k로 크기가 정해져 있기 때문에 Entity가 가지고 있는 Component가 많을수록 한 Chunk에서 저장할 수 있는 Entity는 적어진다.

 

Component

원래 Unity Component는 data와 behavior을 가지고 있는 Object-Oriented 클래스이지만, ECS에서 Component는 Data만 가지고 있다.

General Purpose Components

  • IComponetData

// IComponetData 예시

[GenerateAuthoringComponent]
public struct RotationSpeed_ForEach : IComponentData
{
  public float RadiansPerSecond;
}

기본적으로 참조가 아닌 값으로 복사된다.

Shared Components

  • Shared Component Data : 특별한 종류의 데이터 컴포넌트. 많은 Entity들이 공통적으로 공유하는 data로 사용하는 것이 적합

// ISharedComponentData 예시

[System.Serializable]
public struct RenderMesh : ISharedComponentData
{
  public Mesh mesh;
  public Material material;

  public ShadowCastingMode castShadows;
  public bool receiveShadows;
}

이외의 여러 Component종류가 있는데 unity manual ecs component 에서 확인 가능함

System

ECS 중 Behavior을 담당. Component Data를 현재 상태에서 다음 상태로 변환하는 로직

// System 예시
// This system updates all entities in the scene with both a RotationSpeed_ForEach and Rotation component.
// ReSharper disable once InconsistentNaming

public class RotationSpeedSystem_ForEach : JobComponentSystem
{

// OnUpdate runs on the main thread.

  protected override JobHandle OnUpdate(JobHandle inputDependencies)
  {
    // Entity를 생성
    EntityManager.CreateEntity();

    float deltaTime = Time.DeltaTime;

    // Schedule job to rotate around up vector
    var jobHandle = Entities
                   .WithName("RotationSpeedSystem_ForEach")
                   .ForEach((ref Rotation rotation, in RotationSpeed_ForEach rotationSpeed) =>
                   {
                     rotation.Value = math.mul(math.normalize(rotation.Value),
                                               quaternion.AxisAngle(math.up(),
                                               rotationSpeed.RadiansPerSecond * deltaTime));
                   }).Schedule(inputDependencies);

    // Return job handle as the dependency for this system
    return jobHandle;
  }
}
  • 네이버 블러그 공유하기
  • 네이버 밴드에 공유하기
  • 페이스북 공유하기
  • 카카오스토리 공유하기