ECS – BufferTypeHandle
ECS에서 DynamicBuffer<T>
를 처리하려면 BufferTypeHandle<T>
를 사용해야 한다.
즉, “IComponentData가 아닌 버퍼 데이터를 읽고/쓰는 Handle“
1. BufferTypeHandle<T>란?
- ECS에서
DynamicBuffer<T>
를 읽고/쓰기 위한 Handle IJobChunk
내부에서 사용됨GetBufferAccessor(ref BufferTypeHandle<T>)
를 이용해 버퍼 데이터 접근- 쓰기 여부에 따라 읽기 전용(
true
) / 읽기+쓰기(false
) 로 설정 가능
2. 사용 예제
1) OnCreate()에서 초기화
_BufferTypeHandle = state.GetBufferTypeHandle<GridCellBufferComponent>(isReadOnly: false);
isReadOnly: true
→ 읽기 전용isReadOnly: false
→ 읽기 & 쓰기 가능
2) OnUpdate()에서 매 프레임 업데이트
_BufferTypeHandle.Update(ref state);
Update(ref state)
를 호출해야 최신 데이터를 가져올 수 있다.
3. BufferTypeHandle 상세 사용법
1) 기본 사용법 (IJobChunk에서 사용)
[BurstCompile] public struct CountJob : IJobChunk { public BufferTypeHandle<GridCellBufferComponent> BufferTypeHandle; public void Execute(in ArchetypeChunk chunk, int chunkIndex, bool useEnabledMask, in v128 chunkEnabledMask) { var buffers = chunk.GetBufferAccessor(ref BufferTypeHandle); for (int i = 0; i < chunk.Count; i++) { DynamicBuffer<GridCellBufferComponent> buffer = buffers[i]; for (int j = 0; j < buffer.Length; j++) { buffer[j] = new GridCellBufferComponent { Position = buffer[j].Position, IsOccupied = buffer[j].IsOccupied, Count = buffer[j].Count + 1 // Count 증가 }; } } } }
chunk.GetBufferAccessor(ref BufferTypeHandle)
을 사용하여DynamicBuffer
에 접근- 각
chunk
에 속한 모든DynamicBuffer<GridCellBufferComponent>
에 대해 반복문 실행
2) OnUpdate()에서 ScheduleParallel 실행
public void OnUpdate(ref SystemState state) { _BufferTypeHandle.Update(ref state); var job = new CountJob { BufferTypeHandle = _BufferTypeHandle }; state.Dependency = job.ScheduleParallel(_GridQuery, state.Dependency); }
_BufferTypeHandle.Update(ref state);
→ 최신 상태 반영ScheduleParallel()
→ 여러 개의chunk
를 병렬 실행
3) IJobParallelFor에서 BufferTypeHandle를 사용할 때
일반적으로 IJobParallelFor
에서는 직접 사용할 수 없고, AsNativeArray()
를 사용해야 함.
[BurstCompile] public struct CountJobParallelFor : IJobParallelFor { public NativeArray<GridCellBufferComponent> BufferNativeArray; public void Execute(int index) { BufferNativeArray[index] = new GridCellBufferComponent { Position = BufferNativeArray[index].Position, IsOccupied = BufferNativeArray[index].IsOccupied, Count = BufferNativeArray[index].Count + 1 }; } }
OnUpdate()
에서 실행:
public void OnUpdate(ref SystemState state) { _BufferTypeHandle.Update(ref state); var chunks = _GridQuery.ToArchetypeChunkArray(Allocator.TempJob); for (int i = 0; i < chunks.Length; i++) { BufferAccessor<GridCellBufferComponent> bufferAccessor = chunks[i].GetBufferAccessor(ref _BufferTypeHandle); for (int j = 0; j < bufferAccessor.Length; j++) { DynamicBuffer<GridCellBufferComponent> buffer = bufferAccessor[j]; var job = new CountJobParallelFor { BufferNativeArray = buffer.AsNativeArray() }; job.Schedule(buffer.Length, 1).Complete(); // 즉시 실행 } } chunks.Dispose(); }
주의할 점
DynamicBuffer<T>
에서AsNativeArray()
를 호출하여IJobParallelFor
에서 사용.Complete()
없이state.Dependency = job.Schedule();
을 하면 Write Conflict 에러 발생buffer.AsNativeArray()
는 해당 프레임에서만 유효
4. BufferTypeHandle 요약
기능 | 설명 |
---|---|
BufferTypeHandle<T> | DynamicBuffer<T> 를 읽고/쓰기 위한 Handle |
.Update(ref state) | 매 프레임 최신 상태로 갱신 |
GetBufferAccessor(ref BufferTypeHandle<T>) | DynamicBuffer<T> 를 가져오는 함수 |
ScheduleParallel() | 여러 chunk 를 병렬 실행 |
AsNativeArray() | IJobParallelFor 에서 사용 가능 |
BufferTypeHandle<T>는 언제 사용?
- 많은 데이터가 DynamicBuffer에 저장될 때
- ECS에서
IJobChunk
또는IJobParallelFor
을 활용할 때 - 최대 성능을 끌어내기 위해
ScheduleParallel
을 사용할 때