
GPU의 Shader 파일에서 위치 및 색상 배열을 하드 코딩하는 대신 CPU에서 하드 코딩하고 vertexBuffer에서 GPU로 보낸다. 이것은 큰 차이가 아닌 것처럼 보이지만 하드 코딩된 정점 배열을 파일에서 가져온 3D 모델로 대체하게 만든다.
이와 같이 삼각형을 그릴 때 Swift에서는 세 개의 정점 위치로 배열을 만든다. GPU가 이 배열을 읽을 수 있도록 MTLBuffer 개체에 복사한다. 적절한 길이의 이 버퍼를 생성하도록 GPU 장치에 요청한다. 인수 테이블(Arugment Table)을 사용하여 MTLBuffer를 Shader에 Madpping한다. Metal에는 Buffer, Texture 등 다양한 유형의 리소스가 있으며 각 리소스 유형에는 고유한 인수 테이블이 있다.
예를 들어 두 개의 MTLBuffer를 Indexing하는 방법이 있다.
positionBuffer의 인덱스는 0이고 colorBuffer의 인덱스는 1이다.
장치 및 리소스 유형에 따라 각 인수 테이블에는 31개의 Slot이 있으므로 한 번에 최대 31개의 버퍼와 31개의 Texutre를 가질 수 있다.
Renderer.swift
let positionLength = MemoryLayout<SIMD4<Float>>.stride * positionArray.count
positionBuffer = device.makeBuffer(bytes: positionArray, length: positionLength, options: [])!
let colorLength = MemoryLayout<SIMD3<Float>>.stride * colorArray.count
colorBuffer = device.makeBuffer(bytes: colorArray, length: colorLength, options: [])!
먼저 필요한 길이를 계산한다. 과거에 sizeOf를 사용하여 개체의 크기를 얻었을 수도 있지만 Buffer의 필요한 길이를 계산하는 것보다 정확한 방법은 객체의 stride를 사용하는 것이다.
float4의 stride는 두 float4 사이의 offset이며 각 괄호 안에 있는 type과 함께 MemoryLayout을 사용한다.
그리기에서는 이제 RenderCommandEncoder에서 명령을 생성하여 이러한 Buffer를 GPU로 보낸다. Index는 모든 Buffer가 메모리에 있는 위치를 추적하는 인수 테이블의 항목이다.
GPU에서 Buffer 0을 참조하는 Vertex Function에 매개변수를 전달하면 정점 positionBuffer를 가리킨다. 따라서 다음으로 할 일은 정점 함수의 프로토타입을 변경하는 것이다. 위치 배열을 제거하고 색상 배열을 주석 처리한다.
commandEncoder.setVertexBuffer(positionBuffer, offset: 0, index: 0)
commandEncoder.setVertexBuffer(colorBuffer, offset: 0, index: 1)
이 Buffer를 장치 주소 공간에 넣는다. GPU 칩 내부에는 다양한 메모리 캐시가 있다. 나중에 상수 메모리와 텍스처 메모리를 사용하게 된다. 상수 주소 공간은 여러 정점 함수를 통해 동일한 변수에 병렬로 액세스하는 데 최적화되어 있다. 장치 주소 공간은 병렬 기능을 통해 버퍼의 다른 부분에 액세스할 때 가장 적합하다.