페이지

2021년 12월 23일 목요일

2021년 11월 15일 월요일

How to set TextureSheetAnimationModule.startFrame constant value by script

if 8x8 texture atlas sheet,

Set Texture Sprite of column index = 3, row index = 1.

 

var ps = flame.textureSheetAnimation;
ps.numTilesX = 8;
ps.numTilesY = 8;
ps.animation = ParticleSystemAnimationType.SingleRow;
ps.startFrame = 1f / flameTextureSheets.numTilesX * 3f;
ps.rowIndex = 1;

2021년 7월 1일 목요일

When custom view autolayout doesn't work correclty

if updateLayout() is called in another methods except layoutSubivews(),

view's autolayout doesn't work appropriately.

i don't know why ...

class MyView:UIView {

    override func layoutSubviews() {

        super.layoutSubviews()

        updateLayout()

    }

    func updateLayout(){

        // setting height, constraints

    }

}

2021년 5월 25일 화요일

Using Generic Type Monobehavior class


public class ExGeneric<T> : MonoBehaviour
{
public T data;

}


This ExGeneric<T> component can't be shown in Unity Inspector.


public class ExGenericFloat : ExGeneric<float>
{

}


This ExGenericFloat component is shown in Inspector.


2021년 4월 23일 금요일

Unity Inspector variable Lifecycle



1. Setting Test = 7



output:

Awake: test = 7



2. Setting Test default value

output

Awake: test = 1



3. 



output

ExInspector: test = 2

ExInspector: test = 2

Awake: test = 1




Conclusion : Setting value order

1. Setting class member default value

2. Setting value in Inspector

3. Setting value in Awake()





2021년 3월 16일 화요일

Unity Animation Rigging 사용시 주의점

 Two Bone IK Constraint 를 적용할때

Tip, Mid, Root 에 해당하는 Bone들을 알맞게 설정해야 애니메이션이 정상적으로 작동된다. 그러므로 설정시 주의깊게 체크해서 입력하도록 하자. 



특히나 Tip 에 해당하는 Bone을 보통 Bone 뼈대의 맨 끝 ( 마지막 자식 (예) Bone.003_end )을 설정하고 "Auto Setup from Tip Transform" 메뉴를 선택해서 Mid, Root의 뼈대를 자동으로 설정하게되는 경우가 있는데, 이를 조심해야한다.  Bone.003_end 를 Tip으로 하고 Auto Setup from Tip Transform으로 할 경우, Mid : Bone.003, Root: Bone.002 로 잘못 설정된다.


2족 보행 애니메이션 리깅을 예를 들자면, 

아래 그림에서처럼 Bone.003_end 는 발끝을 의미한다.  다리의 뼈대가 허벅지(Root), 종아리(Mid), 발 이 3가지로 크게 구성될수 있는데, Rigging시 발보다는 발끝 ( 이름뒤에 end가 붙는다) 을 Tip으로 설정해야 한다. Mid : 종아리, Root: 허벅지 로 설정해야한다.




그래서 이 모델에서는

Root : Bone.001

Mid: Bone.002

Tip : Bone.003_end 

가 된다.


2021년 3월 8일 월요일

Unity C# Job System - tips and troubleshooting

  • Job내에 있는 Static data에 접근하지 말라

예를들어 Job내에서 MonoBehaviour 멤버에 접근하면 크래쉬를 야기 시킨다.


  • Flush scheduled batches

다른 Job들이 수행중일때(executing), 즉, 끝나지 않은 상태에서 또 다른 대기 Job들을 JobHandle.ScheduleBatchedJobs 으로 호출하면,  대기순서를 무시하고 바로 실행된다( Execute )

디폴트로, Job들은 Schedule 함수호출로 큐에 놓여져만 있다.  이는 작업자 스레드가 큐에 있는 Job들을 하나씩 가져와서 실행하게 해준다. 
Job 시스템은 ScheduleBatchedJobs를 사용자가 호출하지 않는다면,  현재 수행중인 Job이 끝날때까지  job 수행을 하지 않는다. 왜냐하면, 또 다른 작업자 스레드를 강제로 키는 것은 성능에 않좋기 때문이다. 그래서 많은 양의 Job들이 스케줄되어 있더라도, 순차적으로 스케쥴된 Job들이 수행되게 기다려라. 

Note:  ECS 에서는 batch가 암묵적으로 flush된다. ScheduleBatchedJobs를 호출할 필요가 없다.


  • NativeContainer 데이타를 바로 변경하지 말라

 Job내에서 ref return 이 안되므로, NativeContainer를 참조형 변경을 하지 말라. 항상 복사본을 만들어서 변경시켜라
예)

nativeArray[0]++; // failed


var temp = nativeArray[0];
temp++;
nativeArray[0] = temp;    // success


  • 소유권을 얻기위해서는 JobHandle.Complete 호출하라

메인스레드에서 소유권을 얻으며, safety system이 상태를 리셋시켜서 메모리누수를 방지한다.


  • Schedule, Complete를 메인 스레드에서 사용해라

  • Schedule, Complete를 적절한 시간에 사용해라

Schedule() 함수를 호출하고 Job 결과값이 필요하기 전에는 Complete()함수를 호출하지 말아라. 다른 Job의 수행 결과를 기다릴 필요가 없을때 Schedule() 함수를 호출하는것이 좋은 습관이다. 예를 들어 1 frame latency일 경우에, 한 frame이 끝날때 즈음에 Schedule() 을 호출하고 다음 frame 의 시작시기에 그 결과 데이타를 사용하도록하자.


  • NativeContainer 타입은 readonly로 설정하라

Job은 초기에 NativeContainer에 대한 읽기, 쓰기권한을 갖고 있다. 성능향상을 위해서 ReadOnly만을 설정해라.


  • 데이타 의존성을 체크해라

유니티 프로파일러에, 메인스레드에 "WaitForJobGroup" 문구가 나온다면, 이는 작업 스레드의 Job이 완료되기를 Unity가 기다리고 있다는 의미.
이는 사용자가 어디선가 작업이 끝내기를 기다리는 선작업이 있다는 것으로 , 의존성 데이타를 사용하고 있다는 의미이다. JobHandle.Complete 를 찾아라. 메인스레드가 기다리게 만드는 데이타 의존성을 찾아야 한다. 


  • Debugging jobs

Job은 Run() 함수를 갖고 있다. Schedule() 함수 콜 위치에서 사용할수 있다. 이는 메인스레드의 Job을 바로 실행케하는 것이다. 이는 Debugging 목적으로만 쓰인다.


  • Job내에서 managed memory를 생성하지 말아라

이는 매우 느려지게 하는 원인이다. job은 성능향상을 위해서 Unity Burst Compiler( managed code )를 이용할 수 없다.





2021년 3월 3일 수요일

Unity C# Job System 5

 ParallelFor jobs

IJob 들은 코어당 하나의 Job (Execute )을 수행한다. 그에 반해 IJobParallelFor 은 하나의 코어에 여러개의 Job들을 수행할수 있다. 

Note: ParallelFor Job은 IJobParallelFor 인터페이스를 구현한 structure이다. 



struct IncrementByDeltaTimeJob: IJobParallelFor {

    public NativeArray<float> values;
    public float deltaTime;

    public void Execute(int index){
        float temp = values[index];
        temp += deltaTime;
        values[index] = temp;
    }
}


Scheduling ParallelFor Jobs

이를 사용할때는 NativeArray 데이타 길이를 정해야 한다.  코어는 한정적이고 처리해야할  IJobParallelFor의 Execute들이 많다.이럴때는 몇개의 Job들을 묶어서 하나의 Batch 로 만든다. 이들 여러개의 Batch들을 Job Queue에 넣고 NativeJob 을 생성한다. Native Job System은 각 core당 NativeJob Queue로부터 NativeJob들을 꺼내와서 Batch들을 수행한다.



만약 다른 NativeJob들 보다 먼저 batch수행을 끝낸것 이 있다면, 그것은 현재 남아 있는 다른 NativeJob의 batch들을 가져와서 처리한다. 한번에 남아있는 batch들의 반정도 양만 가져온다. 
이를 최적화 하기위해서, batch count를 시스템에 맞게 적절히 조절할 필요가 있다. Batch count가 너무 낮으면,  core에 비해 처리해야할 Queue 가 많아지며, Batch count가 너무 높으면 한 core에서 수행할 작업자 스레드의 양이 증가한다. 

An example of scheduling a ParallelFor job

// Job adding two floating point values together
public struct MyParallelJob: IJobParallelFor {
    [ReadOnly]
    public NativeArray<float> a;
    [ReadOnly]
    public NativeArray<float> b;
    public NativeArray<float> result;

    public void Execute(int i ){
        result[i] = a[i] + b[i];
    }
}


// Main Thread

NativeArray<float> a = new NativeArray<float>(2, Allocator.TempJob);
NativeArray<float> b = new NativeArray<float>(2, Allocator.TempJob);
NativeArray<float> result = new NativeArray<float>(2, Allocator.TempJob);

a[0] = 1.1;
b[0] = 2.2;
a[1] = 3.3;
b[1] = 4.4;

MyParallelJob jobData = new MyParallelJob();
jobData.a = a;
jobData.b = b;
jobData.result = result;

// Schedule the job with one Execute per index in the results array and only 1 item per processing batch
JobHandle handle = jobData.Schedule(result.Length, 1);

// Wait for the job to complete
handle.Complete();

// Free the memory allocated by the arrays
a.Dispose();
b.Dispose();
result.Dispose();

ParallelForTransform jobs

ParallelFor job의 또 다른 타입. 이는 GameObject의 Transform만을 다루기 위해 나왔다.


2021년 3월 1일 월요일

Unity C# Job System 4

 JobHandle and dependencies

Job의 Schedule() 메서드를 호출하면 JobHandle을 반환한다. 이것으로 다른 Job들과 의존성으로 사용할수 있다. 한개의 Job이 다른 Job의 결과에 의존적이라면, 첫번째 Job의 JobHandle을 두번째 Job의 Schedule 메서드에 변수로 넣는다. 


JobHandle firstJobHandle = firstJob.Schedule();

secondJob.Schedule(firstJobHandle);


Combining dependencies

만약 Job이 많은 의존성들을 갖는다면, JobHandle.CombineDependencies  메서드를사용하여 그것들을 병합하라. 

NativeArray<JobHandle> handles = new NativeArray<JobHandle>(numJobs, Allocator.TempJob);
JobHandle jb = JobHandle.CombineDependencies(handles);

Waiting for jobs in the main thread

 메인스레드에서 Job 수행이 끝날때까지 기다리게 하고 싶으면, Jobhandle을 사용해라. 이는 Complete() 메서드를 호출하면 된다. 이점이 안전하게 Job이 실행중에도, 메인스레드에서 NativeContainer를 접근할수 있게 해준다.

Note 

Job은 사용자가 schedule 한다고 해서 실행되지 않는다. 사용자가 메인스레드내에서 Job을 기다린다면, 그리고 Job 이 사용중인 nativeContainer Data를 접근하려 한다면, JobHandle.Complete()를 호출하면 된다.  This method flushed the jobs from memory cache and starts the process of execution. Complete메서드를 호출하는 것은 Job의 소유권을 메인스레드에 넘겨주는 것이다. 그리고 메인스레드에서 다시 NativeContainer type 에 안전하게 접근할수 있다. 
만약 데이타에 접근할 필요가 없다. 명시적으로 배치를 flush할수 있다. 이는 JobHandle.ScheduleBatchedJobs 를 호출 하면 된다. 이는 성능에 않좋은 함수이다. 


//Job adding two floating point values together
public struct MyJob:IJob {
    public float a;
    public float b;
    public NativeArray<float> result;

    public void Execute(){
        result[0] = a + b;
    }
}

//Job adding one to a value 
public struct AddOneJob:IJob  {
    public NativeArray<float> result;
    public void Execute(){
        result[0] = result[0] + 1;
    }
}

NativeArray<float> result = new NativeArray<float>(1, Allocator.TempJob);

MyJob job1 = new MyJob();
job1.a = 10;
job1.b = 10;
job1.result = result;
JobHandle job1Handle = job1.Schedule();

AddOneJob job2 = new AddOneJob();
job2.result = result;
JobHandle job2Handle = job2.Schedule(job1Handle);

// Wait for job2 to complete
job2Handle.Complete();

// All copies of the NativeArray point to the same memory, you can access the result in your copy of the NativeArray
float aPlusB = result[0];

// Free the memory allocated by the result array
result.Dispose();









2021년 2월 25일 목요일

Unity C# Job System 3

 Creating Jobs

  • Job을 만들기 위해서는 IJob 인터페이스를 구현한다
  • 해당 Job 사용할 멤버변수들을 추가한다
  • Execute 함수를 Job내에 구현한다.

Jobs이 수행되면, Execute 함수는 하나의 코어에 한번 실행된다.

Note :  Job을 설계할때, NativeContainer가 아닌것 data들은 복사본이라는 것을 기억해야 한다. 그래서 메인스레드에서 data를 접근하는 유일한 방법은 NativeContainer를 사용하는 것 밖에 없다.

public struct MyJob:IJob {
    public float a;
    public float b;
    public NativeArray<float> result;
    public void Execute(){
        result[0] = a + b;
    }
}


Scheduling Jobs

메인스레드에서 Job을 스케줄하기 위해서는
  • Instantiate the job
  • Populate the Job's data
  • Call the Schedule method
Schedule함수를 호출하는 것은 해당 Job을 Job Queued에 넣는 다는 것을 의미한다. 그러면 시스템은 적절한 시간에 그 Job을 수행한다. 한번 Schedule 되었으면, 너는 그것을 방해할수 없다.
단지, 메인스레드에서 Schedule 함수만 호출할수 있다.


NativeArray<float> result = new NativeArray<float>(1, Allocator.TempJob);

MyJob jobData = new MyJob();
jobData.a = 10;
jobData.b = 10;
jobData.result = result;

JobHandle handle = jobData.Schedule();

// Wait for the job to complete
handle.Complete();

// All copies of the NativeArray point to the same memory, you can access the result in your copy of the NativeArray
float aPlusB = result[0];

// Free the memory allocated by the result array
result.Dispose();

Unity C# Job System 2

  

The safety system in the C# Job system


Race conditions

멀티스레드 코드를 작성할때, 항상 Race Conditions 에 빠지는 것을 주의해야 한다. 이는 하나의 작업 결과값이 다른 스레드 영역에 있는 처리 결과가 완료될때까지 한없이 기다리게 되는 현상을 말한다.  이를 Race Conditions or Race Hazard라 한다.

이는 디버깅하기가 매우 힘들게 하고, 원인을 찾는 데 매우 어렵다. 그러므로 이런 코드를 작성하면 안된다.


Safety system

Job system은 이런 잠재적인 Race Conditions들을 탐지하고, 그들이 일으킬수도 있는 버그들을 막는다. 
예를 들어, 메인스레드에 있는 데이타의 reference를 Job System이 Job으로 전송했다면, 그 Job이 그 참조 데이타를 변경하려고 할때, 메인스레드에서 동시에 데이타를 읽을수도 있다. 이게 Race Condition 이다.  
 
C# Job System은 데이타의 reference가 아닌 복사본을 만들어서 Job에게 주는 것으로 이 문제를 해결한다.  

데이타 복사본은 Bittable data types 이어야 한다. 이는 Managed( Unity 로 작업한 사용자 코드) 와 Native Code (Unity Job System) 간의 컨버젼이 필요없는 호환 데이타 타입이다.  이 타입은 memcpy로 복사가 되고, 이 두 시스템간에 송수신이 가능하다. 


NativeContainer

데이타 복사처리의 단점은 이들 데이터들이 각 쓰레드 영역에서 분리되어 있다는 것이다. 이를 보완하기위해 나온것이 NativeContainer라 불리는 공유 메모리 타입이다. 


What is a NativeContainer?

이는 Managed value type 이다. Native 메모리를 안전하게  c# Wrapper를 가지고 있다. unmanaged 메모리 할당영역에 대한 포인터를 가지고 있다.  Unity C# Job System 사용시, 이는 Job이 메인스레드와 함께 접근할수 있도록 한다. 

What types of NativeContainer are available?

Unity는 NativeArray라 불리는 NativeContainer를 사용한다.  You can also manipulate a NativeArray with NativeSlice to get a subset of the NativeArray from a particular position to a certain length.

Note: ECS 패키지는 다음 타입들이 있는 Unity.Collections namespace 가지고 있다.
  • NativeList - a resizable NativeArray
  • NativeHashMap - key and value pairs
  • NativeMultiHashMap - multiple values per key
  • NativeQueue - a first in , first out (FIFO) queue

NativeContainer and the safety system

The safety system은 모두 NativeContainer 타입으로 이루어져 있다. 이것은 무엇이 읽고 있고, 어느 NativeContainer에 쓰고 있는지 추적한다.

Note: NativeContainer 타입의 안전 체크( out of bounds check, deallocation check, race condition check)는 UnityEditor, PlayMode 상에서만 작동합니다.

이 시스템은 DisposeSentinel, AtomicSafetyHandle 기술을 포함하고 있다. 
DisposeSentinel은 메모리 누수를 감지하고, 사용자의 메모리를 잘 해제안시키면, 에러를 발생시킨다. 
NativeContainer의 ownership을 코드상에서 전달해주고 싶을때는 AtomicSafetyHandle을 사용해라. 예를 들어, 두개의 Jobs들이 동시에 같은 NativeArray에 쓰기를 할때, 이 시스템은 에러를 출력하고 어떻게 해결할지를 알려준다. 이 경우에는 jobs들간의 의존성을 만들어서 해결 할수 있다. 첫번째 Job이 먼저 NativeContainer에게 쓰게하고 그 Job이 끝나면, 다음 Job이 안전하게 그것들을 읽고 같은 NativeContainer에게 쓰게 한다. 이런 쓰기 , 읽기 제한은 메인스레드에 있는 데이타를 접근할때도 적용된다. 이는 다수의 Jobs 들이 병렬로( 어떻게 이게 병렬이지? 병렬처럼 보이게 한다는 것인가?) 같은 데이타를 읽게 해준다. 

초기에, Job은 NativeContainer에 접근을 하면, 읽기, 쓰기 권한을 모두 갖는다. 이 설정은 성능에 않좋다.  이 시스템은 어떤 Job이 쓰고 있는 데이타에 동시에 또 다른 Job 이 쓰도록 권한을 주지 않는다.

만약 Job이 NativeContainer에게 쓰기권힌이 필요 없을때는 [ReadOnly] 를 선언시 붙여주자.

[ReadOnly]
public NativeArray<int> input;


Note: Job안에 있는 static 선언 데이타에 대해서는 이 안전시스템이 작동되지 않는다. Unity 가 죽는 요소일수 있으니 주의하기 바란다.

NativeContainer Allocator


NativeContainer를 만들때 반드시 필요한 메모리 할당 타입을 지정해야 한다. 이는 Job 을 수행하는 시간에 영향을 미친다. 각각의 상황별로 최고의 성능향상을 위해서는 이를 잘 지정해야한다.  아래와 같은 3가지 할당타입이 있다. 

  • Allocator.Temp
    • 가장 빠른 할당.
    • 1 frame 혹은 그보다 적은 수명을 갖는다. 
    • you should not pass NativeContainer allocations using Temp to Jobs.
    • Dispose 메서드를 호출해야 한다. ( Native 에서 Managed로 복귀할때)
  • Allocator.TempJob
    • Temp보다는 느리지만, Persistent 보다 빠른 할당.
    • 4 frame 의 수명. thread-safe.
    • 4 frame내에서 dispose를 호출하지 않으면, Warning이 뜬다. 
    • 대부분의 작은 Job들이 사용하는 타입이다.
  • Allocator.Persistent
    • 가장 느린 할당. 필요하다면 앱이 실행중인 내내 지속된다.
    • 작업량이 많은 Job일 경우 사용.
    • 성능이 필수적이라면 이 타입의 사용을 자제한다.

사용예)
NativeArray<float> result = new NativeArray<float>(1, Allocator.TempJob);

1은 NativeArray의 사이즈를 말한다. 이 경우 하나의 element를 갖는 array를 말한다.




Unity C# Job System

Job System

Unity에서 간단하고 안전하고, 성능좋은 멀티스레드를 수행하는 기술.
Burst compiler 와 함께 사용하면, 더 좋다.
Job system은 Unity내부적으로 돌아가는 Native Job system과 자연스럽게 통합된다. 그래서 유저가 작성한 Job System코드와 유니티가 worker thread를 공유한다.  이러한 협력이 cpu cores보다 더 많은 스레드를 만드는것을 막아준다. 이로 인한 cpu 자원을 낭비하는 것을 줄일수 있다.

What is Multithreading?

싱글스레드란, 하나의 작업을 한번에 수행하고, 결과가 이후에 나오는 것을 말한다. 멀티스레드는 다수의 cpu 의 장점을 이용한 프로그래밍 기술이다. 싱글스레드와는 달리 동시에 여러작업을 수행하게끔한다. 

프로그램시작시, 하나의 스레드가 기본적으로 실행된다. 이를 우린 Main Thread 라고 부른다. 그리고 그 이후에 여러 작업들을 하기위해 스레드들을 병렬로실행시킬수 있고 그 결과들을 Main Thread와 동기화시킬수 있다. 

작업량이 적다면, 몇개의 스레드로 원하는 바를 이룰수 있겠지만, 게임 개발에서는 많은 작업량들이 필요로 해진다. 이는 cpu와 OS 처리능력을 최대로 사용하게 된다. pool of Threads  라는 기술로 이를 보완할수 있지만, 이는 동시에 많은 활성화된 스레드들을 사용하게 되서, CPU자원을 소모하게 된다. 이는 잦은 context switching을 야기시키기에 성능에 않좋다. 

* Context Switching

하나의 스레드가 다른 스레드를 실행할때 현재 시점, 상태를 저장하고 멈춘다. 그리고 이후 실행된 스레드가 끝나면, 이전 스레드를 찾아가서 저장된 시점부터 다시 실행하게 하는데, 이를 Context Switching이라 부른다. 이는 cpu 자원소모가 큰 작업이다.  그래서 가급적 이를 피해야 한다.

What is a Job system?

Job system은 멀티스레드 코드를 스레드대신에 Jobs을 생성해서 운영한다. 멀티코어를 이용해 작업자 스레드들을 하나의 그룹으로 다룬다. 이는 보통 context switching을 피하기 위해 하나의 로직컬 cpu core당 하나의 작업자 스레드를 만든다. (비록 여분의 core가 남아 있다고 해도.)

Job system은 Jobs을 Job queue에 넣어서 실행한다. 작업자 스레드는 Job queue로부터 온 작업들을 수행한다. 정확한 순서로 작업하는지를 관리한다. 

What is a Job?

작업 하나를 의미한다.  Job은 변수들을 받고, 데이터들을 작업하고 ...
A job receives parameters and operates on data, similar to how a method call behaves. Jobs들은 독립적일수 있고 다른 Job들과 연계되어 실행될수도 있다.

What are job dependencies?

게임 개발같이 복잡한 시스템내에서는 각 Job들이 다른 Job들과 연계되는 경향이 많다. 하나의 Job이 보통 다음 Job 을위해 데이타를 준비한다. 이러한 의존성 작업들을 하기위해서 dependencies를 지원한다. 만약 JobA 가 JobB에 의존적이라면, JobA는 JobB가 끝날때까지는 시작하지 않는다.
 



출처