RTX란 무엇인가 시리즈


지금까지 레이트레이싱의 원리에 대해 간단하게 알아봤습니다. 복잡한 내용이 많아 잘 기억에 안 남는 부분이 많을 것입니다. 내용을 확실하게 이해하려면 같은 내용을 두번 세번 반복해서 읽어야 할 수도 있습니다.

하지만 이번 글의 내용을 이해하려는데 1편과 2편의 내용을 완벽하게 암기하고 있을 필요는 없습니다. 다음 두 가지만 떠올려 주세요.

  • 레이트레이싱은 기본적으로 선분과 삼각형 사이의 교점을 찾는 수학적 과정이다.
  • 공간에는 삼각형이 너무 많기 때문에 상자 도형을 이용해 교점 존재 가능성이 없는 삼각형을 솎아내려고 노력한다.

이 두 개념이 중요한 이유는 RTX 그래픽카드가 수행하는 일이 정확하게 이것이기 때문입니다. 그럼 지금부터 RTX 그래픽카드의 내부에 대해 자세히 알아보겠습니다.

Ⅰ. RTX GPU

우선 용어부터 확실히 정리하겠습니다. 일반인에게는 그래픽카드가 더 익숙한 이름이겠지만, 사실 그래픽카드는 여러가지 요소가 하나의 제품으로 합쳐진 복합체입니다. 정리하자면 그래픽카드 자신의 CPU와 RAM을 별개로 갖고 있는 셈이 됩니다.

  • GPU : 연산을 수행하는 유닛으로 CPU와 같은 포지션
  • VRAM : GPU가 연산을 수행하는 데 필요한 정보를 저장하는 곳으로 RAM과 같은 포지션

GPU가 VRAM과 빠르게 통신하는 기술, 수천 개의 GPU 코어가 하나의 VRAM과 통신하면서 순서가 꼬이지 않게 하는 기술 등의 복잡한 내용들은 (문과생인) 제 수준 밖의 내용입니다. ㅜㅜ 저희는 순수하게 GPU에 관한 것들만 알아볼 것입니다.

지금부터 RTX 그래픽카드라 하지 않고 RTX GPU라는 용어를 주로 사용하겠습니다. 그리고 RTX 이전의 것, 우리가 오랫동안 사용해 오던 것들은 GTX GPU라고 부르겠습니다. 정식 명칭은 아닙니다. 파스칼 아키텍처(GTX), 튜링 아키텍처(RTX)라는 공식 용어가 있긴 합니다만 별로 안 익숙하잖아요? 그러니 그냥 간편하게 GTX, RTX라고 부르겠습니다.

Ⅱ. GPU가 하는 일

앞서 GPU가 CPU와 같은 포지션이라고 설명 드렸습니다. 구체적으로 말하자면 GPU는 실수(real number, 實數)의 덧셈, 곱셈, 제곱근 등의 연산을 매우 빠르게 수행합니다. 더 정확하게 말하자면 수많은 실수에 대한 연산을 수천 개의 코어에서 병렬로 실행합니다.

비유하자면 CPU는 일 오지게 잘 하는 행보관이고 GPU는 일 적당히 잘 하지만 머릿수가 많은 병사들이라고 할까요? 연병장 잡초뽑기 같은 일은 힘 세고 짬 높은 행보관 혼자 하는 것보단 병사 수십 명 보내는 게 더 빠르잖아요? 행보관 혼자 하면 연병장 가장자리에서 시작해서 조금씩 이동하며 잡초를 제거하겠지만, 병사 수십 명 보내면 각자 자기 자리 잡아서 한꺼번에 뽑기 시작하겠죠. 이것이 GPU에서 일어나는 병렬처리입니다.

연병장 대신 모니터 화면이라고 생각해 볼까요? CPU가 렌더링을 한다면 모니터 좌측 상단의 픽셀부터 시작해서 오른쪽으로 죽 그려나가겠죠. 우측 끝에 다다르면 한 픽셀 밑으로 내려와서 다시 좌에서 우로 죽 그려나갑니다.

하지만 GPU한테 시키면 상황이 다릅니다. GPU 안에 있는 수많은 코어들에게 픽셀을 하나씩 할당하면 각 코어들은 자기가 맡은 일을 일사분란하게 처리합니다. 같은 시간 동안 더 많은 픽셀에 그릴 수 있게 됩니다.

이것이 대략적인 GPU에 대한 설명입니다. 이 이상 자세히 이해하기는 쉽지 않으며 별로 재미도 없습니다. 어쨌든 저희의 목적은 RTX에 대해 이해하기잖아요? RTX 이전의 GPU와 RTX GPU 사이의 차이점이 우리의 큰 관심사입니다. GPU에 대해 자세히 이해할 필요는 없습니다. 그러니 여기서는 RTX GPU만이 가진 특별한 점만을 짚어보겠습니다.

sm_of_turing_architecture

위 그림이 RTX의 SM(Streaming Multiprocessor) 하나의 구조입니다. 하나하나 뜯어봐도 뭐가 뭔지 감도 안 옵니다. 이 그림에 포함된 정보를 모두 이해할 필요는 없습니다. 이해하기 쉬우면서 흥미로운 부분만 골라서 살펴봅시다!

Ⅲ. GTX와 RTX의 공통점

1. 부동소수점 연산

그림에서 초록색 상자 안에 흰 글자로 FP32라고 적혀 있는 곳이 보이시나요? 이것은 floating point number 32 bit의 줄임말인데요. 프로그래밍에 대해 아시는 분이라면 float 타입의 숫자를 의미한다는 걸 바로 눈치채셨을 겁니다. float이 뭔지 잘 모르신다면 그냥 실수(real number, 實數)라고만 알고 계시면 되겠습니다.

결론부터 말하자면, 이 부분은 GTX와 RTX의 공통점 되겠습니다. FP32 코어는 GTX에서는 그냥 코어란 이름으로 내장되어 있습니다. 개수도 그리 큰 차이가 나지는 않습니다. 그럼에도 불구하고 언급하는 이유는, 이 장치가 GPU의 존재 의의이기 때문입니다. 이것이 뭔지 이해하는 것이 GPU 그 자체에 대해 이해하는 데 아주 중요합니다.

실수의 연산은 컴퓨터 그래픽스에서 가장 중요한 요소입니다. 구체적으로 실수의 연산이 어떻게 사용되는지 예시를 들어보겠습니다.

  • 저번 글에서 보셨들이 모든 3차원 물체는 실수(real number)로 표현됩니다.
  • 물체를 공간 안에서 이리저리 이동하고 애니메이션을 적용할 때 행렬곱을 사용하는데, 이것도 내부를 보면 숫자의 덧셈과 곱셈이죠.
  • 물체의 표면에 빛 효과를 적용하는 것은 벡터의 내적, 제곱근, 그리고 덧셈과 곱셈으로 이루어져 있습니다. 3차원 벡터는 보통 3개의 실수로 표현되죠.
  • 그림자는 물체의 위치 정보를 행렬곱을 적용하여 텍스처에 저장, 나중에 이를 역행렬로 되찾아서 값의 대소 비교를 통해 구현됩니다. 역시나 대부분 실수의 연산이지요.
  • 레이트레이싱 없이 구현하는 반사 효과도 마찬가지입니다. 물체의 위치 정보와 색깔 정보를 행렬곱을 적용해 텍스처로 저장한 뒤, 벡터 연산을 통해 올바른 반사상의 위치를 찾아냅니다.

실수의 연산은 그래픽 연산 과정의 90% 이상을 차지합니다 GTX나 RTX 둘 다 실수 연산을 담당하는 FP32 하드웨어를 수천 개(혹은 1XXX개) 때려박아 같은 시간 안에 연산량을 늘리려고 노력합니다. 레이트레이싱도 실수의 연산을 굉장히 많이 하기 때문에, 이 부분은 GTX와 RTX 사이의 차이가 거의 없는 부분이 되겠습니다. (물론 더 최근에 나온 RTX GPU에 장착된 FP32가 좀 더 성능이 좋겠지만요.)

Ⅳ. GTX와 RTX의 차이점

1. 정수 연산

위 그림에서 FP32 옆에 INT32가 있죠? 이것은 integer 32 bit의 약자인데요. Integer의 뜻은 정수(整數)입니다. … -2, -1, 0, 1, 2, 3 …처럼, 자연수에 0과 음수가 추가된 수의 영역 아시죠? 정수에 대한 연산을 담당하는 하드웨어가 RTX에 와서 아주 중대한 업그레이드를 받았답니다.

그런데 프로그래머가 아닌 분들은 여기서 의문이 드실 겁니다. 정수 연산을 하는 하드웨어가 왜 필요한가? 고등학교 수학 수업을 열심히 들었다면 기억하시겠지만, 실수는 모든 정수를 포함하죠. 그러니 그냥 $3 + 5$의 결과를 알고 싶으면 FP32에 넣어서 $3.0 + 5.0 = 8.0$이라는 결과를 얻으면 안 될까요?

사실 정수 연산이 필요한 이유가 다 있습니다. 실수 연산을 위해 사용하는 float형 숫자는 정밀도 문제를 갖는다는 지극히 기술적인 문제도 있습니다만. 레이트레이싱을 수행할 때 정수 연산이 중요하게 사용된다는 것도 원인이이죠.

삼각형과 광선의 교점 찾기 같은 연산은 실수 연산이 맞습니다만. 광선이 어떤 삼각형과 충돌했는지, 그 삼각형이 어떤 바운딩 볼륨 안에 있는지를 나타낼 때 정수를 사용합니다. 논리 연산을 수행할 때는 대부분 정수 연산이지요. 그래서 효율적으로 레이트레이싱을 수행하려면 효율적으로 정수 연산을 할 필요가 있는 것입니다.

Nvidia가 밝힌 바에 따르면 FP32 연산이 100개가 있다면 INT32 연산은 35개, 레이트레이싱 연산은 50개, DLSS(아래에 서술) 연산은 25개가 있다고 합니다. 여기서 연산은 GPU에 전달되는 instruction을 의미합니다. INT32 연산의 비중이 생각보다 크죠? 그러니 병렬처리의 이점이 큰 것입니다.

GTX에도 정수 연산을 수행하는 장치가 있긴 합니다. 하지만 RTX에 와서 크게 개선된 점은, 정수 연산을 병렬로 수행할 수 있다는 점입니다. 즉, INT32가 열심히 정수 연산을 하고 있는 동안 그 옆에 있는 FP32는 다른 셰이딩 연산을 하고 있을 수 있다는 뜻입니다. GTX에서는 정수 연산을 수행하려면 실수 연산을 멈춰야 한다네요. ㄷㄷ

2. RT Core

드디어 대망의 RT 코어까지 왔습니다. 이름만 봐도 딱 눈치가 옵니다. 이것이 바로 레이트레이싱 관련 연산을 수행하는 하드웨어 장치입니다. 현대의 RTX On 게임에서 볼 수 있는 아름다운 반사, 실시간 글로벌 일루미네이션의 마술은 바로 여기서 일어나는 것이지요.

RT 코어는 두 가지 종류의 계산을 합니다.

  • 광선과 삼각형 사이의 교점 구하기
  • 바운딩 볼륨 계층에서 광선이 충돌할 가능성이 있는 볼륨 찾기

광선과 삼각형 사이의 교점 구하기가 뭔지는 바로 아시겠죠? 두 번째 항목이 어려운 용어도 있어서 좀 복잡한데요. 사실 이미 배운 내용입니다. 여러 개의 삼각형을 하나의 박스로 묶은 뒤, 광선과 박스가 충돌하지 않으면 내부의 삼각형을 모두 무시하는 전략 기억하시죠? RT 코어는 이 박스와 광선의 충돌 검사를 수행하여 충돌한 박스를 반환하는 역할도 수행할 수 있다는 뜻입니다.

RTX 이전에는 FP32처럼 실수 연산을 수행하는 장치가 위의 작업도 겸했습니다. 광선과 삼각형의 교점 구하기는 3차원 벡터의 내적, 외적, 거리 측정을 사용하는데 모두 실수의 연산이죠. 광선과 박스의 충돌의 경우도 실수의 연산을 사용하여 구현합니다. Nvidia가 구체적으로 어떻게 구현했는지는 잘 모르겠지만, 분리축 정리(separating axis theorem)을 사용했다면 역시나 벡터의 내적과 외적이 주로 사용되었을 겁니다.

그런데 레이트레이싱은 아주아주 시간이 오래 걸리는 작업입니다. 광선과 물체의 교차 검사를 한다고 FP32 코어를 달달 볶으면 다른 셰이딩 연산을 할 귀중한 시간을 뺐기는 꼴이 됩니다. 그래서 RT 코어를 도입하여 레이트레이싱 연산을 수행할 코어를 별도로 제공함으로써 이 문제를 해결한 것입니다.

3. Tensor Core

RTX로 넘어오면서 새로 추가된 것에는 텐서 코어도 있습니다. 이번에는 상당히 생소한 이름인데요. 이 코어는 간단히 설명하면 인공지능의 연산을 수행하는 코어입니다.

텐서(tensor)가 무엇일까요? 저도 아직 공부중인 부분이라 엄밀하게 이해하고 있지는 않습니다만. 아는 만큼만 언급한 뒤 추후 더 공부하여 내용을 추가하도록 하겠습니다.

행렬(matrix)은 다들 잘 알고 계실 겁니다. 고등학교 수학 시간에 다들 배웠으니까요. 자세한 계산 방법은 기억이 안 나지만, 커다란 괄호 안에 숫자를 2행 2열로 나열한 모습 정도는 기억하실 겁니다. 이번 RTX 시리즈 포스트에서도 수도 없이 언급하기도 했고요.

행렬은 숫자를 2차원으로 나열한 것입니다. $2 \times 2$ 행렬, $3 \times 3$ 행렬, $12 \times 6$ 행렬 등등.

ma-th\begin{bmatrix} x_{11} & x_{12} \\ x_{21} & x_{22} \end{bmatrix}, \begin{bmatrix} x_{11} & x_{12} & x_{13} \\ x_{21} & x_{22} & x_{23} \end{bmatrix}, \begin{bmatrix} x_{11} & x_{12} & x_{13} \\ x_{21} & x_{22} & x_{23} \\ x_{31} & x_{32} & x_{33} \end{bmatrix}ma-th

그런데 숫자를 3차원으로 나열할 수도 있겠죠? $2 \times 2 \times 2$ 행렬, $3 \times 3 \times 3$ 행렬, $12 \times 2 \times 4$ 행렬처럼 말입니다. 그럼 5차원 행렬, 6차원 행렬, 이를 일반화해서 n차원 행렬이 존재할 겁니다. 이 n차원 행렬을 텐서라고 부르는 모양입니다.

한 예로 PyTorch라는 Python 머신러닝 라이브러리에서는 텐서가 다음과 같이 생겼습니다. 출처

data = [[1, 2],[3, 4]]
x_data = torch.tensor(data)

x_ones = torch.ones_like(x_data) # retains the properties of x_data
print(f"Ones Tensor: \n {x_ones} \n")

x_rand = torch.rand_like(x_data, dtype=torch.float) # overrides the datatype of x_data
print(f"Random Tensor: \n {x_rand} \n")

결과 화면

Ones Tensor:
 tensor([[1, 1],
        [1, 1]])

Random Tensor:
 tensor([[0.6147, 0.7112],
        [0.5486, 0.8505]])

2차원 행렬과 똑같이 생겼음을 확인할 수 있습니다.

인공지능 구현에 주로 사용되는 머신러닝은 이 텐서의 연산 비중이 아주 큽니다. 저도 문과생이기 때문에(…) 텐서에 대한 설명은 여기까지만 하겠습니다.

텐서코어가 셰이더에서 행렬 연산을 돕는 것 같지는 않습니다. 애초에 행렬 연산의 결과값은 셰이더에서 즉시 사용해야 하기 때문에 병렬처리가 곤란하니까요. 대신 텐서 코어는 렌더링 과정의 곁다리에서 참여하는데요. 바로 DLSS(Deep Learning Super Sampling)를 통해서입니다.

DLSS는 화면을 조금 작게 렌더링 한 다음, 인공지능을 이용해 여러분 모니터 크기로 키워주는 기술을 말합니다. 그냥 포토샵으로 이미지 크기를 늘리는 것과는 다릅니다. 엔비디아와 개발사가 협력해서 해당 게임의 화면 리사이징에 특화된 인공지능을 훈련시켜 배포하는 겁니다. GeForce Experience의 그래픽 드라이버 업데이트에서 언급이 됩니다.

dlss_ref_in_nvida_drive_update

그럼 작은 이미지에 렌더링 하여 크기를 늘리는 전략이 얼마나 좋은지 알아 볼까요? 저번 글을 읽으신 분이라면, 화면의 픽셀 개수에 비례하는 수의 광선을 발사해야 한다는 걸 기억하실 텐데요. 일반적인 FHD 모니터를 가진 컴퓨터에서는 $1920 \times 1080 \times \alpha = 2,073,600 \times \alpha$개의 광선이 필요합니다. 그런데 가로세로 길이가 80%인 이미지에 렌더링 하는 경우를 볼까요?

ma-th(1920 \times 0.8) \times (1080 \times 0.8) \times \alpha = 2,073,600 \times 0.8^2 \times \alphama-th

결과적으로 원래의 64%의 광선만 발사하면 됩니다. 이를 일반화 하면, 이미지의 가로 크기를 $a$배, 세로 길이를 $b$배 하면 광선의 수는 $ab$배가 되는 것입니다. 게다가 이미지 크기를 늘리는 연산은 텐서 코어에서 병렬로 실행되기 때문에, 그 옆에서는 셰이더와 레이트레이싱이 동시에 수행될 수 있지요.

Ⅴ. 마치며

이로써 우리는 일반인보다는 깊은 수준의 레이트레이싱 지식을 습득했습니다. 레이트레이싱에서 사용되는 기본적인 기하학 연산이 무엇인지 알았습니다. 선분과 삼각형의 교점 구하기, 선분과 박스의 충돌 여부 검사 등. 광선 개수도 많고 삼각형의 개수도 엄청 많아서 시간이 오래 걸릴 수밖에 없다는 점도 이해했습니다.

레이트레이싱 없이 반사를 구현하기 위해 어떤 편법을 사용하는지도 배웠습니다. 복잡한 공간의 물체들의 형태에 관한 정보를 텍스처에 저장한 뒤, 그 텍스처 안에서 광선을 발사합니다. 3차원 공간의 정보를 2차원 텍스처에 저장했기에 손실이 있어 반사가 불완전하다는 것도 눈으로 확인했습니다. 반사 뿐만 아니라 앰비언트 오클루전(주변광 차폐)나 글로벌 일루미네이션도 비슷하게 구현된다는 걸 알았습니다.

Nvidia는 이 문제를 해결하기 위해 소프트웨어적 접근과 하드웨어적 접근을 사용했습니다. 하이브리드 렌더링이라는 이름 하에, 많은 부분을 전통적인 방식으로 렌더링 하되 반사상처럼 그 효용이 극대화되는 지점에서 제한된 양의 레이트레이싱만 구사하도록 소프트웨어를 설계했습니다. 작은 이미지에 렌더링 하여 크기를 늘리는 아이디어도 내놓았죠. 그리고 이 연산을 병렬로 수행할 수 있는 하드웨어를 설계했습니다.

이 분야에 완전 문외한인 분이시라면 한 번만 읽어서는 제대로 이해하기 힘들 것입니다. 죄송합니다 제가 욕심을 부려 너무 깊은 내용까지 다 포함해버려서 그렇습니다. 그러니 이해가 안 되는 부분이 있다면 댓글이나 이메일로 얼마든지 질문 보내주세요!

그리고 같은 내용을 여러 번 읽어 주세요. 1주일 뒤 다시 읽어도 좋고, 한 달 뒤도 좋습니다. 왜냐하면 미래의 당신은 지금의 당신보다 훨씬 똑똑한 사람이기 때문입니다. 그리고 몇 주 뒤에는 제가 글을 좀 수정해서 더 읽기 쉽게 만들었을지도 모릅니다.

Ⅵ. 참고자료