Deep Animation 논문

Integrate LibTorch(PyTorch C++) into Unreal Engine (2) – Build LibTorch

이전 글

https://nagnae.wordpress.com/2023/04/10/integrate-libtorchpytorch-c-into-unreal-engine-1-why-and-how/

시작하기

앞선 글의 이유 때문에, PyTorch를 직접 빌드해서 Unreal Engine(이하 UE)에 붙여야 겠다고 마음먹었고, 다양한 시행착오를 겪으며, 빌드 하였다. 헤맬때는 힘들었는데, 알고나니 허무한 점이 참 많다.

LibTorch를 빌드하는 방법이 체계적으로 잘 설명되어 있지 않다. 공식 문서는 아래 주소에 있다.

https://github.com/pytorch/pytorch/blob/master/docs/libtorch.rst

다른 사람이 쓴 글은 여기 있다.

https://datagraphi.com/blog/post/2021/9/13/building-pytorch-from-source-on-windows-to-work-with-an-old-gpu

문서가 있으니까 쉽게 될 것 같지만, 안!된!다!

이 PyTorch가 플랫폼, 언어, Cuda지원 등 여러 옵션이 있어서, 관련 빌드 옵션이 굉장히 많고, 빌드를 위해서 설치해두어야 할 것도 여럿 있다. 원하는 빌드 옵션과 각 컴 환경이 달라서, 모든 경우를 다 설명할 수는 없고, 내가 진행한 방법만들 설명하겠다.

자세히 모든 스텝을 설명하지는 않는다. (설명하기 너무 힘듦). 중요한 포인트를 대략적으로 나마 모두 설명했으니, 참고할만 할 거나고 생각한다.

PyTorche 소스코드 다운로드

먼저 PyTorch source code 를 github 페이지에서 clone한다.

https://github.com/pytorch/pytorch

이때 download zip파일 하지 말고, 반드시 git clone으로 받아야 한다. download zip으로 받으면, 아래와 같은 문제가 생긴다.

  1. PyTorch는 많은 third-party 코드들을 사용하는데, download zip으로 받으면, 이들은 다운로드 되지 않는다. 이 것들은 수동으로 하나씩 download를 또해줘야 한다. 게다가 이들 third-party들이 내부적으로 또 다른 third party를 사용하고, 그것들이 또 third party를 쓰고를 반복한다. 그냥 git clone으로 하면 이들을 다 다운로드 하니 맘 편하다. 뭐 PyTorch를 빌드하겠다고 마음 먹은 사람이 git에 익숙하지 않을리는 없을테니, 속 편하게 git clone하자.
  2. 1번 문제를 수동으로 다 해결했다 하더라도, 나중에 cmake generate 단계에서 실패한다. 얘가 (쓸데없이) .git 폴더를 확인하기 때문이다. 뭐 이 확인하는 코드를 찾아서 없애면 되긴 하는데, 그냥 속편하게 git clone하자.

나는 처음에 clone이 귀찮아서, 그냥 download zip했다가 위 시행착오를 다 겪었다. 여러분은 그럴 필요가 없다.

Python 환경 만들기

그 다음에 공식 문서나 다른 글은 tools/setup.py 와 tools/build_libtorch.py를 실행하라고 되어 있는데, 해보면 잘 안 된다. 심지어는 공식문서에도 “네 필요에 맞게 네가 알아서 고쳐야 할거야” 라고 나와있다. (친절한 건지, 불친절한 건지.) 좀 더 편하게 bat 파일도 있다. scipts/build_windows.bat이 있는데, 이걸 실행해도 역시 동일한 에러 메시지를 잔뜩 볼 수 있다. build_windows.bat 내부를 보면 단순히 build_libtorch.py를 실행하는 것 뿐이다. 따라서 python이 설치가 되어 있지 않거나, path설정이 안 되어 있으면, python.exe 를 찾을 수 없다는 에러 메세지 부터 나온다.

빌드용 파일들을 만들 때 부터, python을 사용하기 때문에, python 실행 환경을 만들어야 한다. python을 다운로드 받고, 필요한 환경을 구축하는 것은 다른 문서를 참고해서 진행하자.

나의 경우에는 PyCharm을 사용하였다. Conda를 많이들 사용하는데, 내 경우에는 jetbrain 제품을 좋아해서 PyCharm을 Python IDE로 사용하였다. 각자 선택한 python 환경을 구축하여 python이 실행가능한 환경을 만들어 놓자. 이 후의 설명은 PyCharm 사용을 기반으로 진행한다.

PyCharm도 그냥 바로 되는게 아니고, interpreter 및 venv설정하고, 내 경우에는 좀 더 편하게 쓰려고 terminal 옵션도 좀 수정했다.

그리고 프로젝트로 다운로드된 pytorch 폴더를 선택해서 열고, build_libtorch.py를 실행하면 역시 동일한 에러 메세지가 나온다. 이때 python interpreter가 설정되어 있지 않으면, 이것 부터 해결해 줘야 한다. 또 당연하지만, VS와 cmake이 설치되어 있어야 한다. 나는 VS2019를 사용했다.

Python 빌드 코드 수정

build_libtorch.py를 실행하면 나오는 에러 메세지를 하나씩 해결해 줘야 하는데, 에러 메세지는 대개 두 종류다.

  1. 환경변수 설정 부족
  2. 필요한 모듈 설치 안 됨

환경변수는 파일탐색기나 command로 설정해도 되지만, 편리하게 관리하기 위해, build_libtorch.py를 수정하여 code로 작성했다.

설정가능한 환경변수는 위 코드에 적은 것 말고도 매우 많다. 내경우에는 static-link library로 빌드하고 (BUILD_SHARED_LIBS=OFF), cuda를 연동하지 않는 걸로 했다 (USE_CUDA=OFF). cuda 연동 빌드도 가능하겠지만, 당장은 빌드 복잡도를 줄이기 위해 cuda를 껐다.

Python 추가 모듈 설치

또 다른 에러는 모듈이 없다는 것인데, 이런 모듈은 수동으로 추가해서 해결하였다. 각자 상황에 따라 다르지만 나는 typing-extensions와 yaml이라는 모듈이 없다라고 나와서 terminal 창에서 pip로 추가했다.

OpenMP 관련 Cmake 수정

마지막 문제는 힘들었는데, OpenMp 요구사항이었다. PyTorch의 cmake 설정의 버그였는지, 이게 없다고 에러메세지를 내놓는데, 이건 python 모듈이 아니고, VS를 설치한 순간에 이미 설치된 것인데, 자꾸 이 부분에서 문제가 생겼다. 결국 이건 OpenMP를 설치하는게 아니라, cmake 코드를 수정하여 해결하였다.

그리고 다시 실행하니 드디어 cmake generate 정상 동작했다.

VS 프로젝트 설정

앞서 단계에서 generate가 완료되면, 자동으로 이어서 cmake build가 실행되는데, 이 부분은 중지하고, 직접 VS에서 빌드를 시도했다. VS가 훨씬 익숙할 뿐더러 문제가 생기면 찾고, 해결하기가 훨씬 용이해서다. pytorch의 build 폴더에 가보면 sln, vcproj와 각종 소스코드들 그리고 cmake 파일들이 이쁘게 생성되어 있는 것을 볼 수 있다.

참고로 여기서 생성된 build 폴더를 다른 폴더 위치로 옮겨서 빌드하면 안 된다. 내부 cmake 설정 파일에 절대 경로가 들어가 있어서, 빌드 실패하게 된다. 반드시 파일이 만들어진 그 폴더 그대로 사용해야 한다. (사소한 문제이지만, 나는 폴더를 옮겨봤고 그래서 빌드 실패하고 원인을 찾아 헤맸다. 여러분은 나와 같은 시행착오를 할 필요가 없다.)

sln 파일을 열면 아래와 같이 180개가 넘는 프로젝트 목록을 볼 수 있다.

이 중에서 onnx 폴더의 프로젝트 설정창을 열어서 혹시 아래와 같이 되어 있는지 확인해보자.

이 상태에서도 LibTorch 빌드 자체는 문제가 없는데, UE에 링크를 하면, 링크 에러가 발생한다. 반드시 Runtime Library를 Multi-threaded DLL로 바꿔준다.

onnx 프로젝트 말고도, UE 링크시에 아래와 같은 에러가 발생한다면, 해당 프로젝트의 설정을 위처럼 바꿔서 다시 빌드해서 사용하자.

LINK2038: mismatch detected for 'RuntimeLibrary' : value 'MT_StaticRelease' doesn't match value 'MD_DynamicRelease' ...

소스코드 빌드

ALL_Build 프로젝트를 빌드하면 전체 프로젝트가 다 빌드 된다. 플랫폼은 x64만 있기 때문에 이것을 선택하고, configuration을 RelWithDebInfo을 선택하기를 추천한다. Debug 빌드는 lib 파일이 너무 커져서, 파일 사이즈 오버로 link 실패하고, release 빌드는 pdb 파일을 안 만들기 때문에, pytorch 내부의 디버깅이 되지 않는다. 차후에 연동이 다 잘 되어서 굳이 pytorch 디버깅이 필요하지 않으면, release 빌드를 사용해도 된다.

빌드 실행하면, 꽤 오랜 시간동안 CPU 사용과 IO가 확 늘어나면서 빌드가 진행된다. 사실 이 글도 빌드되는 동안 정리하는 겸 (이라기 보다는 할 일이 없어져서) 쓰는 중이다. (CPU 100%먹을때, 버벅거리는 마우스 커서와 싸우며 글을 썼다.) 빌드가 끝나면 아래와 같은 결과가 나오는데, 105개 프로젝트는 빌드 성공했고, 70개는 실패했다.

에러 내용을 보면 전부 link error다.

한 symbol이 여러 곳에서 선언이 되어 있어서 그런데, 아마 이건 static library 빌드였기 때문에 그런 것 같다. dynamic libarary 로 빌드했으면, 아마 다른 에러메세지가 나왔을 것이다.

다행히 이 link 에러는 신경쓰지 않아도 된다. 모든 플젝을 다 빌드할 필요없이, 필요한 static libarary project만 빌드 성공하면 된다. pytorch/build/lib 폴더에 가면, 빌드에 성공한 static library들이 보인다.

여기까지 오는 것도 쉽지는 않을 것이다. 여기까지 완료가 되면, 이제 반절은 한 거다.

다음글

https://nagnae.wordpress.com/2023/04/10/integrate-libtorchpytorch-c-into-unreal-engine-3-run-ue-with-libtorch

Deep Learning 공부

Integrate LibTorch(PyTorch C++) into Unreal Engine (1) – Why? and How?

  • 시행착오 하면서 적은 글이라, 약간은 자조적으로 글이 작성이 되었습니다.
  • 성공한 방법을 대략적으로 기록하였습니다. 상세한 사항은 생략하였습니다.
  • 라이브러리를 통합한 과정까지만을 설명합니다.

Why?

Deep Learning을 공부를 시작해서 toy project를 진행하다 보니, 이를 Unreal Engine (이하 UE)에 접목시키고 싶었다. Pretrained model을 그냥 사용하는 방법은 UE의 NNI 플러그인이 제공하는 ONNX를 사용하면 어렵지 않게 가능하다. 해당 모델을 ONNX로 컨버팅하여 UE로 가져오면, 자동으로 ONNX용 uasset 파일이 만들어져서 사용이 가능하다. 이 uasset을 사용하는 UNeuralNetwork 객체를 만들고, 적절한 input float array를 넣으면, model이 학습한 network을 사용하여 ouput float array가 나온다.

조금 욕심이 더 생겨서 Deep Learning 라이브러리를 직접 UE에 붙이고 싶었다. ONNX를 통한 간접적인 사용은 아래와 같은 한계가 있기 때문이다.

  1. UE에서는 eval만 가능하다. 훈련은 불가능하다.
  2. UE에서는 tensor 계산을 사용할 수가 없다.
  3. ONNX을 사용하는 코드는 Python으로 작성한 훈련용 코드와 매우 달라져서, 개발, 관리, 디버깅 등에 다 불편하다.
  4. ONNX가 모든 연산자를 다 지원하지는 못한다고 한다. 내가 이 문제를 아직 겪지는 못했지만, 미래에 문제가 발생될 가능성이 있다.

위 한계가 문제되지 않는다면, 그냥 ONNX를 쓰는 것이 훨씬 편하다. 다만 나처럼 위 한계를 해결하고 싶은 사람들을 위해 도움이 될 수 있도록 이 글을 공유해 본다.

What?

가장 대중적인 ML 라이브러리는 Tensorflow와 PyTorch가 있다. 나는 여러가지 이유로 PyTorch를 선택하였다. 이 두가지 라이브러리는 python interface를 제공하며, 개발자들은 대부분 python을 사용하여 자신만의 Deep Leaning을 구현한다. UE와 PyTorch를 통합한 방법이 몇개가 공개되었으며, 나도 처음에는 그것을 참고하여 진행하였다. 다만 공개된 방법들은 python으로 PyTorch를 동작시키고, UE는 이 python process들과 TCP/IP와 같은 간접적인 방법으로 연동을 하도록 구현되었다. 이 방법은 아래와 같은 문제가 있다.

  1. 학습 코드의 핵심 부분들은 여전히 python으로만 작성해야 한다. UE C++에서 일부를 작성할 수 있지만, UE의 데이터를 가공해서 python에 넘겨주는 코드만 주로 작성되기 쉽다.
  2. 코드가 두가지 언어로 분리되어 있어, 작성, 이해, 관리가 어렵다.
  3. 데이터 마샬링과 같은 처리를 해야 하므로 코드 가독성과 성능상으로 손해를 본다.
  4. 별도 python process가 뜨게 된다.
  5. ONNX를 쓰지 않는다면, 배포할 때, python코드도 같이 배포해야 한다.

위 한계가 문제되지 않는다면, 역시 기존의 공개된 통합 방법을 사용해도 괜찮다. 굳이 나 처럼 위 한계가 싫은 사람들은 python대신에 PyTorch의 C++ 라이브러리를 UE에 통합하면 된다. PyTorch는 겉은 python interface로 구성되어 있지만, 코어는 C++로 구현되어 있으며, 이 PyTorch가 C++ interface를 제공해 주기 때문에 가능하다. 이 C++ 라이브러리를 LibTorch라고 부른다. 이렇게 통합하면 아래와 같은 장점이 있겠다. 당연히 이들 장점은 앞서의 python 연동의 단점과 정반대이다.

  1. UE에서 C++ 하나의 언어를 사용하여 개발이 가능하다. 개발, 관리가 상대적으로 쉬어진다.
  2. 데이터 마샬링이나 별도 통신을 할 필요가 없으므로 필요없는 성능 낭비를 피할 수 있다.
  3. 별도 python process가 뜨지 않으며, 배포할 때도 그냥 일반 언리얼 게임처럼 배포할 수 있다.
  4. UE 환경에서 C++을 사용하여 tensor 등의 pytorch 기능을 모두 사용할 수 있다.

PyTorch 공식 사이트에서는 LibTorch를 빌드된 결과를 배포하고 있다.

https://pytorch.org/get-started/locally/

여기서 필요한 플랫폼, 라이브러리 형태, Cuda 사용 여부 등을 선택하면 다운로드가 가능하다. 여기서 LibTorch를 선택하면, dll 형태로 빌드된 라이브러리를 다운로드 받을수 있다. 일반적인 경우에는 이를 그대로 사용하면 되지만, UE에서는 이를 사용할 수가 없다. 이들 dll을 UE에서 로드하려하면 아래와 같은 문제가 발생하기 때문이다.

  1. 위 사이트에서 받은 실행 binary들의 depenency를 챙겨주는 것이 매우 까다롭다.
  2. 1을 해결했다 하더라도, UE5에서 dll 로드 할때, memory trace 기능이 로드되는 모든 dll을 검사하는데, 그 코드에 버그가 있어서, 무한루핑에 빠진다. (좀 더 자세히 말하면, dll header의 특정 op을 처리하지 못하고, 계속 그 op에서 제자리를 뱅뱅돈다.) 이 문제는 plugin으로 만들거나, third-party 라이브러리 추가를 해도 발생한다.
  3. 위 문제를 피하기 위해, win64의 LoadLibraryW()를 직접 사용하여 dll 로드를 하였으나, dependency 문제로 로딩이 안 된다.

위 문제 중에서 2번이 가장 치명적이었다. dependency는 어떻게든 해결해 볼 수 있는데, 2번 문제는 UE 소스코드를 수정해야만 해결이 가능했다. 하지만 나는 UE 소스코드 수정없이 해결해 보고 싶었다. 한번 소스코드를 수정하면, 순정 UE에서는 동작하지 않는 결과가 나오기 때문이다. 보통 회사 프로젝트라면 그렇게 해도 상관없겠으나… 또 3번 문제도 심각하다. 3번 방식으로 해결하면, UE 코드 안에서는 pytorch 코드를 작성할 수 없게 된다. 별도의 dll 프로젝트에서만 pytorch 코드가 사용 가능하고, 디버깅도 불편하게 된다.

UE의 dll 로딩 문제를 피하기 위하여, LibTorch를 static library로 만들어서, UE에 통합하고자 하였다. 이게 가능하도록 PyTorch 코드가 구성은 되어 있는데, 이 부분 자료가 대단히 적다. (ChatGPT님에게 물어보아도 엉뚱한 소리만 하고…) 따라서 직접 시행착오를 하면서 찾아낸 방법을 공유해 본다.

HOW?

  1. LibTorch의 static library를 직접 만들어야 한다. 이를 위해 pytorch 소스코드가 있는 github 사이트로 가서 clone한다.
  2. 빌드용 프로젝트 파일을 생성한다. 제공되는 cmake과 python script를 사용하여 만든다. windows버전의 경우 VS 솔루션과 프로젝트 파일을 만든다.
  3. 빌드한다.
  4. 언리얼에서 빌드된 libtorch를 library link하고 header include한 후에 코드를 작성한다.

위 과정은 third party 라이브러리를 언리얼에 추가하는 아주 일반적인 과정이지만, 매 단계에서 각종 문제가 발생한다. 무려 일주일 동안 문제를 해결하는데 헤맸는데, (그 과정 중에 몇번이고 포기할 뻔 했다. ㅠㅠ) 이 험남한 과정을 후속 문서에서 좀 더 자세히 설명해 보겠다.

라이브러리를 연동한 다음에 아래의 아주 간단한 tensor 조작 코드를 추가하고, 실행한다.

void TestTorch()
{
	torch::Tensor tensor1 = torch::randn({ 3,3 });
	torch::Tensor tensor2 = torch::eye(3);
	torch::Tensor input = torch::ones({ 1, 3, 224, 224 });
}

위 코드가 정상적으로 실행되는데 단계 까지만, 이 시리즈 문서에서 설명할 것이다.

다음글

Uncategorized

FBX animation import crash

Unreal Animation asset 중 1 frame짜리 pose sequence가 가끔 FBX export, import시 import 할 때, crash 되는 경우가 있다.

libfbxsdk.dll에서 divide by zero 예외사항이 발생하면서 crash 된다.

물론 export / import 대신에, migrate 해도 되는데, 버전 호환이 안 되는 상황에서는 export / import만이 동작하기 때문이다.

그럴 때는 아래처럼 해당 animation sequence를 열고, Preview Mesh로 animation asset을 새로 만든 다음에, 새로 만들어진 animation asset을 export 해서 사용하면, 해결 된다.

Uneal for Programmer

Unreal Android Build 문제점 해결

NDK와 JDK 모두 필요한 버전으로 설치한다.
UE 버전마다 필요한 버전이 다 다르다.

Project Setting에서 각 path의 경로명을 맞춰주고, JAVA_HOME 환경 변수도 JDK 폴더로 맞춰준다.

마지막으로 gradlew 실행시 아래와 같은 문제가 발생할 수 있는데,

“Could not determine java version from ‘11.0.8’”

이건 UE_path/Engine/Build/Android/java/gradle/gradle/wrapper/gradle-wrapper.propreties 파일을 수정해야 한다.

distributionUrl=….

이 라인을 아래처럼 바꾼다.

distributionUrl=https\://services.gradle.org/distributions/gradle-5.6.2-all.zip

다만 여기서 특정한 버전은 미래에는 달라질 가능성도 있다.

마지막 이 문제가 해결이 참 힘들었고, 정보도 없어서 많이 헤맸다.

개발 환경

Chrome Remote – Windows & Mac

1. Chrome remote를 사용하는 이유

윈도우에서 mac를 remote로 사용하고 싶을 때, 윈도우 기본 remote (원격 데스크탑)을 사용하여 연결할 수가 없다.

TeamViewer와 같은 외부 프로그램을 사용할 수 있고, VNC를 설치해서 조금 복잡한 설정을 통해서 원격 연결을 할 수도 있다. 다만 이는 별도 설치해야 하고, 회사에서 사용할 때는 구매해야 할 수도 있고, 무엇보다 큰 문제는 회사에서 이걸 정책적으로 아예 막을 수도 있다. 난 이걸 설치했다가 회사 IT팀에서 삭체해달라는 요청을 다음날 바로 받았다. 원격 연결은 보안 문제도 있기 때문에 좀 더 민간한 것 같다.

또 AppStore에 MS remote desktop이 올라와 있는데, (https://apps.apple.com/kr/app/microsoft-remote-desktop/id1295203466) 이 앱은 windows에서 Mac 연결을 하는게 아니라, Mac에서 다른 PC에 원격 연결을 할 수 있는
것으로 보인다. 사용은 안 해봐서 잘은 모르겠다.

아무튼 이런 모든 문제는 chrome remote를 사용하면 피할 수 있다. 회사에서 pc나 mac에 chrome 설치한다가 보통 문제 삼지 않기 때문이다.

2. Keyboard 문제

다만 키보드 조합이 Mac과 Windows가 달라서, 실제 사용시 문제가 생기는데, 대표적으로 Control키이다. chrome remote를 전체화면 상태로 mac에 연결해서 사용하다가, Control키를 누르면, 그 다음부터 전반적으로 키보드가 꼬여서, windows 사용에서도 문제가 생긴다. 그때는 windows 상태에서 (chrome remote에 keyboard focus가 안 간 상태에서) control 키를 눌렀다 때어줘서, 이 키 꼬임 상태를 풀어야 한다.

이를 해결할 수 있는 방법은 chrome remote에서 키보드 세팅을 해주는 것이다. chrome remote의 오른쪽 에서 “>” 버튼을 눌러 나오는 설정창에서 “키 매핑 구성”을 누른다.

그리고 아래와 같이 키매핑 하나를 추가한다.

이제 Control 키를 누르면 mac에서 command 키 입력으로 처리되고, 다양한 단축키를 windows 스타일로 쓸 수 있다. 아래와 같은 것들이다.

Cotrol+CCommand+CClipboard Copy
Cotrol+XCommand+XClipboard Cut
Cotrol+VCommand+VClipboard Paste
Cotrol+ZCommand+ZUndo
Cotrol+YCommand+YRedo

이 문제로 오래 고통 받았는데, 좀 만 찾아서 간단히 해결~!

Happy remote!

Uncategorized

Shader 에서 uniform 변수를 조건으로 사용한 if 문은 성능상 별 문제 없습니다.

HLSL 작성시 if문을 사용하면 성능상의 문제가 발생하기 때문에 쓰지 말라고 얘기합니다. if문은 dynamic branch를 만들어 내고, 이는 1000개 이상의 thread가 같은 CP를 가지고 실행되는 GPU 특성상 성능 문제를 야기하기 때문이죠. 틀린 말은 아닌데, 무조건적으로 피하는 것도 문제가 있다 생각합니다. 조금만 복잡한 시스템을 만들면 조건문이 들어갈 수 밖에 없습니다. 실제로 Unreal 엔진 내부 코드를 보아도, 여러 군데에서 if문이 사용되었습니다. 저도 if문을 필요하다면 종종 사용합니다. 사용에 주의를 하긴 해야 합니다.

경우에 따라서는 if 문이 dynamic branch를 만들어 내지 않기도 합니다. 주로 간단한 대입문의 경우가 그렇습니다. 아래와 같은 HLSL 코드는 일견 복잡해 보일 수도 있지만..

어셈블리어로 번역되면 아래와 같이 바뀝니다.

조건의 결과문인 bool 데이터가 만들어 지고 나면, 그 이후 if문들은 대입문으로 바뀝니다. 위 결과에서 movc 가 이 역활을 합니다.

movc 결과저장register, 조건register, 입력1resgier, 입력2register

조건이 true이면 입력1을, false 이면 입력2를 저장합니다. C 계열 코드의 삼항 연산자 ? : 와 동일한 명령어 입니다. movc 덕분에 dynamic branch가 발생하지 않습니다. 만약 if 문의 각 항의 내부가 복잡한 코드들이 있다면, 조건문과 관계없이 각 항을 다 실행해야 해서, 성능낭비는 발생합니다. 그런데 생각보다 많은 경우에 그정도 성능 낭비는 받아들일 수도 있습니다.

if문이 성능에 악영향을 주지 않기 위해서는 if 조건문에 따라 실행되는 각 내용이 성능에 부담되지 않는 경우여야 합니다. 코드로 부연 설명을 하면, 아래와 같은 코드에서

if(조건문)
{
      내용1
}
else
{
     내용2
}

위 코드에서 조건문의 복잡도는 if문의 성능에 영향을 아무런 영향을 주지 않습니다. 영향을 주는 것은 내용1과 내용2 입니다. 내용 코드들이 충분히 작다면, 내용1과 내용2를 모두 연산하고, 조건문에 따라 한쪽 결과를 버리는 방식으로 GPU가 동작합니다. 이 본문에서 예를 든 경우는 그 중 특수한 경우로 삼항연산자로 대체가 가능한 경우에 movc로 컴파일되어서 그 과정을 어셈블리어로 더 명확히 볼 수 있습니다.

또한 현대 연산장치들은 메모리 접근이 순수한 연산보다 훨씬 비쌉니다. 이 부분을 데이터로 프로파일링 해보았는데, 포스팅할 기회가 있을지 모르겠네요. 즉 가능하면 내용1이나 내용2에 텍스처나 버퍼 접근이 포함되지 않는게 좋습니다. 즉 코드의 양이 중요한게 아니라 실질적으로 성능을 많이 차지하는 경우에 if문이 성능 부담이 됩니다. 예를들어 아래와 같은 코드는 얄짤없이 dynamic branch 처리됩니다.

또 하나 설명하고 싶은 것은 if문에 너무 겁먹지 말았으면 합니다. 전 위와 같은 코드를 모바일 플랫폼에서 실제로 사용합니다. 성능적으로 아주아주 가벼운 플젝을 만들겠다면 모르겠는데, 품질이 중요한 경우에는 적당한 if 문 사용은 피할 필요가 없다고 생각합니다. 중요한 것은 테스트입니다. 해놓고 프로파일러를 돌려보시면, 시간이 얼마나 걸리는지 나오고, 생각보다 상당 수의 경우에 별 문제가 안 된다는 것을 발견 할 수 있습니다. if문으로 분기되는 각 코드를 가능한 성능에 부담 안 되는 방식으로 작게 만들고 (가능한 어셈블리어 레벨에서) 프로파일링을 해보면, 안심하고 쓸 수있는 방법을 찾을 수도 있습니다. 저도 그렇게 하고 있습니다. 저도 제가 만든 코드를 못 믿기 때문이죠. 제가 주장하는 것은 if 문이 무조건 안전하다는게 아니라, 해보고 테스트 해보라는 것입니다.

아무튼 shader 작성시 if문은 반드시 써서 안되는게 아니고, 잘 쓰면 유용하다는 게 제 의견입니다. 쉐이더에서 명령어 몇개 아꼈다고 성능 향상은 거의 없습니다.

Uncategorized

Unreal Editor 실행시 Epic Launcher 안 뜨게 하는 방법

UE로 개발시에 Editor는 자주 실행시킵니다. 그런데 그 때마다 Epic Launcher도 같이 자동으로 실행되어서 매우 귀찮습니다. Epic launcher가 무슨 무료행사 등등의 noti를 같이 보여주기 때문이죠. 이걸 끄기 위해서 소스코드를 고쳐야 하나… 싶었는데, 의외로 쉬운 방법이 있습니다.

UE가 설치된 폴더에 가서 그 아래의 ‘/Engine/Build/’폴더로 이동한 후에, PerforceBuild.txt 라는 빈 파일을 만들면 됩니다.

이렇게 하면 허무할 정도로 자연스럽게 launcher가 뜨지 않습니다. Epic 내부에서는 perforce로 버전관리를 하고 있으니, 아마 내부개발자들 편하기 위해 만든 기능 같습니다. 이럴 거면 그냥 무슨 옵션 정도로 제공해주지 싶습니다.

참고로 EngineBuildSettings.cpp 파일에는 이런 식으로 몇가지 정해진 txt 파일의 존재 여부만으로 엔진 동작을 다르게 하는 코드가 있습니다.

Uncategorized

Inside the Unreal Engine technology I expect

Unreal Engine 5 데모가 공개되고 업계에 상당한 충격을 주고 있습니다.

공개된 데모에는 여러가지 기술들이 소개되고 있는데, 가장 주요한 두가지 기술은 아래의 두가지 입니다.

  1. Nanite
  2. Lumen

이 두가지 기술에 대해서 epic games의 개발자가 언급한 내용과 제 개인 연구에 기반하여 내부 원리를 추측하여 봅니다. 저도 나름대로의 의견을 제시하는 것 뿐이니, 다른 의견이 있으시면 자유롭게 댓글이나 기타 방법으로 알려주세요. 현재 얻을 수 있는 개발자가 언급한 가장 자세한 정보는 아래 링크에 있습니다.

https://www.eurogamer.net/articles/digitalfoundry-2020-unreal-engine-5-playstation-5-tech-demo-analysis

Nanite

UE5에서 구현한 micro-polygon 표현 기술의 네이밍 입니다.

개발자는 Primitive shader를 사용했고, screen space 연산이 있으며, detail texture가 사용되고, SW (Software) rasterizer를 구현했다고 언급 합니다. 마침 제가 같은 목표로 연구를 1년 정도 해왔습니다. 그의 언급 중에서 primitive shader를 제외한 다른 내용은 저의 연구 결과와 동일한 방법입니다. 저는 이 과정을 depth pass 후에 수행합니다. 제 연구 결과의 영상은 아래 링크로 작년에 공유했습니다. UE5와 같은 멋진 데모는 못됩니다. 현재 개발중인 프로젝트에 사용되고 있지만 아직 게임 공개 이전이라 그 결과물을 사용할 수 없고, 개발의 편의성을 때문에 Landscape에만 사용되었고, Realtime GI는 없습니다. 딱 논문에 사용될만한 수준의 데모 영상입니다.

영상에는 tessellation뿐만 아니라 또 다른 연구 결과도 포함하고 있습니다. 영상은 작년에 만든 것이고, 노말처리와 TAA때문에 발생되는 flickering문제도 보이는데, 현재는 모두 해결된 상태입니다. (영상 자체 퀄리티는 용서해 주세요. ㅠㅠ 제가 사용한 편집툴이 뭔지 아시면 놀랄 겁니다.)

SS (Screen-Space) tessel에 대한 자세한 구현 내용은 이 문서에서 설명하지 않습니다. 문제는 SS는 여러가지 artifact를 가지고 있는데, 화면에 보이지 않는 pixel (화면 밖 or 다른 물체에 가려진 픽셀)이 displacement되어 화면 안으로 보여지게 될 때 대한 처리가 되지 않습니다. 또 다른 문제도 있는데, 이것도 이 문서에서 설명하지 않습니다. 앞서의 문제를 저는 화면보다 좀 더 크게 그리는 방식으로 문제를 줄였으며, 이는 depth pass에 국한된거라 성능 저하가 거의 없습니다. 다른 물체에 가려진 픽셀은 이걸로 해결이 안 되는데, HW (Hardware) tessel을 사용할 계획을 가지고 있습니다. 즉 큰 규모의 displacement에 HW tessel를 사용하고, 디테일한 규모에는 SS pixel tessel을 사용하는 방식입니다. HW tessel만으로 디테일한 displacement까지 다 표현하려면 성능과 품질 모두 심각하게 좋지 않은데, 이렇게 디테일한 표현은 SS방식으로 넘겨 버리고 큰 규모만 처리해 준다면 꽤 멋진 방법이라고 생각합니다.

이 외에 normal vector 계산과 uv 등의 정보 저장 문제가 있는데, 약간의 cost만 지불하면 다 해결 가능합니다.전 개인적으로 normal vector의 제대로 된 처리를 연구하는데 상당한 시간을 소비했습니다. 이게 생각보다 매우 어렵더군요. 물론 UE5가 실제로 어떤 방법을 사용하는지는 공개되어야 확실해 지겠지만, 디테일 규모의 displacemt인 SS는 저와 유사한 방식이 아닐까 예상해 봅니다.큰 규모의 displacement는 HW tessel보다는 primitive shader(또는 mesh shader)를 사용했을 듯 한데 자세한 방법은 예상을 못하겠습니다. (일단 제가 mesh shader를 잘 모릅니다.)

이렇게 displacement 규모에 따라서 다른 두가지 (또는 그 이상의) 기술을 섞어서 구현하면 UE5 데모와 같은 micro-polygon에 유사한 결과를 얻는다고 확신합니다. 저는 제 개인 연구를 통해서 확인한 바가 있습니다. 제 견해로는 tessel 뿐만 아니라 모든 screen space 접근 방식은 디테일한 표현에는 상상 이상의 힘을 발휘하는데 규모가 좀 커지는 현상에서는 여러 artifact를 발생합니다. 이에 대한 다른 기술의 도움을 받아야 한다고 생각합니다.

SS tessel 방식은 render pipeline상 아주 큰 변화를 요구합니다. depth pass까지는 큰 변화가 없는데, base pass에서 기존과 완전히 다른 방식으로 mesh를 render 해야 합니다. tessel이 이미 화면공간에서 이루어 졌고, base pass에서 이를 사용하려면 원래의 mesh를 render해서는 불가능합니다. 저는 bouding box를 사용하는 방법을 사용하고 landscape에서만 이 기술을 사용하는 것으로 구현의 복잡함을 회피했는데, 이 기능을 일반화 하려면 material id를 기록하고, material의 shader compile 결과도 기존과 완전히 바뀌어 져야 합니다. 제가 UE를 그 정도 수준으로 뜯어 고치면 유지보수가 안 되어서 거기까지 가지는 않았습니다. UE5는 static mesh에 적용이 되는 것으로 보아 이러한 변화를 거친 것으로 판단 됩니다. 아직 skeletal mesh등 다른 데는 적용이 안 된 듯 한데, 이 기술의 한계는 아니고 다만 구현에 시간이 좀 더 걸릴 문제라 생각합니다.

별도의 기술로 소개된 pixel accurate shadow map은 jaggy 없는 shadow map자체는 별도의 연구가 필요했겠지만, shadow mesh의 디테일 표현은 nanite의 tessel 방식을 그대로 사용했을 겁니다. 제 개인 연구에서도 shadow 생성도 pixel tessel된 결과로 잘 처리되고 심지어는 pixel tessel로 튀어나온 면에 캐릭터의 다리 IK가 잘 처리되도록 구현하였습니다. 성능 저하 거의 없이 말이죠.

개인적으로 이 기술에 대한 논문을 준비중인데, UE5 데모를 보고 나서는 준비를 계속 해야 하나 고민 중입니다. (늦장을 부렸던 자신에 대한 통렬한 반성 중입니다.)

Lumen

UE5에서 Realtime GI를 구현한 기술의 네이밍 입니다.

전 이 분야에 대해서는 아직 잘 모르기 때문에 제 개인 의견을 피력할 것은 없습니다. 그래서 개발자가 공개한 내용을 정리해서 말씀드리겠습니다.

완벽에 가까운 실시간 GI를 구현하기 위하여 세가지 규모로 나누어 GI를 구현합니다.

큰 규모에는 Voxel 데이터에 기반한 GI를 수행하고, 중간 규모에는 SDF를 사용한 GI를 수행합니다. 그리고 세부 디테일 GI는 SS에서 수행합니다. 역시 여기에서도 SS의 디테일한 표현이 힘을 발휘합니다. SS는 앞서 Nanite에서 말한 것처럼 여러가지 artifact와 단점을 갖는데, UE5에서는 역시 이를 규모에 따라 다른 기술을 사용하여 해결했습니다.

또한 GI는 여러번 ray를 bouding하는 계산을 반복해야 하는데, 이게 많은 계산량을 요구하죠. 이 문제를 여러 frame으로 계산을 나누는 방식으로 해결했습니다. 각 frame마다 한 번의 bouding 계산만 처리하고 그 계산을 계속 누적해 나가는 식입니다. 따라서 UE5의 방식은 계산량을 줄인 대신에, 조명 변화에 대한 반응에 약간의 delay가 생긴다는 artifact가 있습니다.

결론

이 글은 UE5를 보고 스스로 예상해본 내부 원리에 대한 글입니다. UE5 데모는 업계에 강한 임팩트를 줄 정도의 엄청난 기술을 가지고 있습니다. 사실 개인적인 연구 결과의 발표를 못하게 될 것 같은 아쉬움이 이 글을 작성한 동기가 되었습니다. 암튼 그래픽 기술은 끊임없이 변하고, 오프라인의 멋진 기술들이 계속해서 실시간으로 오는 넘어 오는 것에 대해 큰 흥분을 저 역시 가지고 있습니다. 긴 시간을 들여 개발한 (개발자 얘기로는 10년 전부터 구상했다고 하네요.)에게 큰 존경을 표하고, 이 업계에 계신 모든 분들 파이팅 입니다.