ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • 개요, UObjectBase - 클래스
    언리얼 엔진 소스코드 분석 2024. 6. 30. 22:21

    * 저작권에 의해 혹시 몰라 직접적인 코드는 올리지 않습니다.

     

    언리얼 엔진에서 UMyActorComponent를 만들면

    UMyActorComponent가 자동적으로 UActorComponent를 상속하는 것을 볼 수 있다.

    언리얼엔진에서 코드를 받아서

    ActorComponent.h 파일을 찾아갔다.

    여기에 UActorComponent 클래스가 있다.

     

    UActorComponent부터 처근차근 따라가니

    계속 타고 들어가니 UObjectBase 클래스가 나왔다. 관련하여 찾아보니 

    https://docs.unrealengine.com/4.27/en-US/API/

     

    Unreal Engine API Reference

    Unreal Engine API Reference

    docs.unrealengine.com

    언리얼 엔진 4 버전에서 위 문서를 찾았다

    분석하는 건 언리얼 엔진 5이긴 한데, 간단히 보기엔 이쪽이 편해서 간단히 방향을 잡기 위해 일단 이쪽으로 봤다.

    확인해보니 언리얼 엔진에서 코어 시스템이라 부를 부분은 위에 있는 폴더들에 존재하는 것 같다.

    이 코드들을 위주로 분석을 진행할 것이다.

     

    언리얼 엔진이 1994년 개발이 시작됐다.

    현재 2024년이니 30년 간 쌓인 코드들이라 다 보는 건 무리가 있고, 최대한 핵심적인 부분들 위주로 분석할 것이다.


    https://dev.epicgames.com/documentation/en-us/unreal-engine/API/Runtime/CoreUObject/UObject

     

    언리얼 엔진 5의 API 문서이다.

    UObjectBase 부터 분석을 시작한다.

     

    기본적으로 UObjectBase의 저레벨 구현이라 한다.

    https://dev.epicgames.com/documentation/en-us/unreal-engine/API/Runtime/CoreUObject/UObject/UObjectBase

    해당 문서에서 어떤 함수들이 있는지 정리가 되어 있다.

     

    # AtomicallyClearFlags

    멀티 스레드 환경에서 원자적으로 플래그를 클리어하는 코드이다.

    클리어하는 위치의 플래그, 예를 들어 1111 1101이면 1인 위치를 전부 클리어 해야 한다.

    이 경우 not 연산으로 1과 0을 뒤집고 and 연산을 하면 된다.

     

    이때, 계산 결과가 예상과 같으면 문제가 생기지 않은 것으로 판단하고 그냥 return하는데

    그렇지 않으면 원자적으로 재시도한다. (InterlockedAnd)

     

    이때, 함수가 FORCENOINLINE 매크로가 적용되어 있다.

    플래그 클리어의 경우 자주 호출될 수 있는데 인라인으로 처리하면 코드 크기가 증가하는게 문제일 것이고,

    한가지 이유가 더 있다면 원자성과 관계가 있는 것으로 보인다.

     

    예전 운영체제 포스팅(락 구현 관련)에서도 알아봤듯이 원자성은 하드웨어(CPU)의 도움을 받아 구현된다.

    이때, 인라인화된 코드를 컴파일러가 제대로 인식하고 최적화하지 않으면 원자적 연산이 분할되거나 최적화될 수 있어 원자성을 해칠 수 있다.

     

    # AtomicallySetFlags

    원자적으로 플래그를 세팅하는 함수이다.

    클리어랑 비슷하게 진행된다.

     

    # DeferredRegister

    가상 함수로 정의되어 있다.

    부트 스트랩으로 등록된 클래스를 실제 객체로 만들고 uobject 배열에 추가한다, 고 적혀있다.

    간단히 말하면 메타데이터만 있는 클래스를 실제 객체로 만든다.

     

    # GetClass

    해당 오브젝트의 UClass를 반환받는다.

     

    # GetExternalPackage / SetExternalPackage / GetExternalPackageInternal

    관련 패키지의 getter, setter

    GetExternalPackageInternal는 스레드 세이프하지 않고, 내부 gc가 사용

     

    # GetFlagsInternal

    클래스 내부에서(private) 원자적으로 플래그를 읽어올 때 사용한다.

     

    # GetFlags

    public에 제공되어 플래그를 제공해주는 함수

    checkfSlow는 디버깅용 매크로이다. (Assert)

    조건이 만족하지 않으면 중단시키고 오류 정보를 출력한다.

     

    * Assert

    check - 릴리스 빌드에서는 비활성화됨

    checkSlow - 부하는 세 매크로 중 중간 수준

    checkfSlow - 부하는 가장 심하지만, 가장 상세한 디버깅 정보 출력

     

    # GetFName

    오브젝트의 논리적 이름 리턴

    FName은 언리얼에서 사용하는 불변의 문자열 타입 (Fast Name의 줄임)

    리소스 식별이나 객체 이름 지정을 위해서 사용함

     

    # GetFNameForStatID

    * COREUOBJECT_API: 다른 모듈에서 접근할 수 있도록 하는 매크로

    통계 식별을 위한 논리적인 이름을 반환하는 함수

     

    # GetOuter

    해당 객체가 속한 UObject 반환

     

    # GetUniqueID

    유니크 ID 반환

     

    # IsValidLowLevel, IsValidLowLevelFast

    유효한 오브젝트인지 검사 (Null ptr)

    Fast 버전은 덜 정확할 수 있음

     

    * IsValidLowLevelFast의 인자 bRecursive

    true로 설정되어 있으면 객체에 속하는 멤버들까지 재귀적으로 유효성 검사를 진행

    false인 경우 해당 객체만 유효성 검사를 진행함

     

    # LowLevelRename

    FName과 외부를 변경하고 네임 해시 테이블에 리해시

     

    # PrefetchClass, PrefetchOuter

    * offsetof 매크로

    멤버의 오프셋 계산

    void FPlatformMisc::Prefetch(void* Address, size_t NumBytes)

    메모리 프리패치 함수

    * 프리패치: CPU가 데이터를 필요로 하기 전에 미리 패치해둬서 성능 향상시키기 위해 사용

    오브젝트 주소랑 읽을 바이트 수를 인자로 넣어주면 미리 패치해줌

     

    결국 프리패치해서 성능 향상시키려고 쓰는 함수들

     

    # Register

    언리얼 엔진 관리 체계에 오브젝트 등록

    패키지 이름, 오브젝트 이름

    * TCHAR

    윈도우에서는 유니코드, 그 외에서는 아스키코드로 인식

    플랫폼 독립적인 문자(열) 지원을 위해 사용

     

    # RegisterDependencies

    종속성을 가지는 클래스들을 먼저 등록하도록 강제 가능

    서브 클래스에서 알아서 필요한 거 등록시키면 됨

     

    Register 된 클래스들을 실제 객체화 시키는게 DeferredRegister 함수

    왜 필요한가?

    거대한 오픈월드 게임이라 예시를 들면, 맵 로딩같은 경우엔 한번에 하면 엄청 시간이 오래 걸림

    필요할 때 로딩을 하라고 지연시킬 수 있음

     

    # RemoveClassPrefix

    UObject의 U 같은 접두사 제거

Designed by Tistory.