WWDC 2014에서 소개
iOS8에서 출시
GPU 가속 3D그래픽용 자체 API
3D 그래픽 하드웨어와 상호 작용하기 위한 저수준 API
OpenGL과의 차이점은 크로스 플랫폼이 아니라는 것
*크로스플랫폼: 여러 종류의 장치 플랫폼에서 동작할 수 있음
Apple 하드웨어에서 매우 효율적으로 설계되어 OpenGL에 비해 빠른 속도와 낮은 오버헤드를 제공한다.
Metal은 OpenGL과 유사한 저수준 3D그래픽 API지만 오버헤드가 낮을수록 성능이 향상된다.
*오버헤드: 어떤 처리를 하기 위해 들어가는 간접적인 처리 시간, 메모리 등
이것은 GPU 위의 매우 얇은 레이어이다.
스프라이트, 3D 모델을 화면에 렌더링하는 것과 같은 거의 모든 작업을 수행하려면 모든 코드를 작성해야한다. 대신 개발자가 완전한 권한과 통제력을 가지고 있다.
반대로 SpriteKit, SceneKit 및 Unity와 같은 상위 수준 게임 프레임워크는 Metal 또는 OpenGL ES와 같은 하위 수준 3D 그래픽 API 위에 구축된다.
게임을 만드는 것 뿐이라면 상위 수준 게임 프레임워크를 사용하는 것이 더 용이하다.
하지만 Metal을 배워야하는 이유도 있다.
1. Metal은 low-level이기 때문에 하드웨어를 한계까지 사용하고 게임 작동 방식을 완전히 제어할 수 있다.
2. 3D 그래픽, 게임 엔진 구현, 게임 프레임워크 작동 방식에 대해 많은 것을 배울 수 있다.
OpenGL은 크로스플랫폼으로 설계되어 C++로 작성할 수 있고 약간의 수정만 거치면 안드로이드와 같은 다른 플랫폼에서 실행할 수 있다.
하지만 Apple이 제품을 설계하는 방식에 있어 OS와 HW, SW를 완전한 패키지로 통합하기 위해 개발된 Apple HW용 그래픽 API가 설계되었다.
최신 기능을 지원하는 동시에 매우 낮은 오버헤드, 높은 성능을 목표로 설계되었다.
그 결과 OpenGL ES와 비교하여 앱에 최대 10✕개의 드로우 콜을 제공할 수 있는 Metal이 탄생했다.
*드로우콜: CPU는 현재 프레임에 어떤 것을 그려야할지 정하고, GPU는 오브젝트를 그리라는 명령을 호출하는데, 이 명령이 드로우콜이다.
렌더링을 시작할 수 있도록 Metal을 설정하는데 필요한 7단계
1. MTLDevice
2. CAMetalLayer
3. Vertex Buffer
4. Vertex Shader
5. Fragment Shader
6. Render Pipeline
7. Command Queue
GPU에 대한 직접적인 연결. 이것을 사용하여 다른 모든 Metal개체(명령 대기열, 버퍼 및 텍스처 등)를 만든다.
iOS에서 화면에 보이는 모든 것은 CALayers의 하위 클래스인 gradient layers, shape layers, replicator layers와 같은 것들로 이루어진다.
Metal을 이용해서 화면에 무엇인가 그리기 위해서는 CAMetalLayer를 호출하여 하위 클래스인 CALayer가 필요하다.
metalLayer = CAMetalLayer() //1
metalLayer.device = device //2
metalLayer.pixelFormat = .bgra8Unorm //3
metalLayer.framebufferOnly = true //4
metalLayer.frame = view.layer.frame //5
view.layer.addSublayer(metalLayer) //6
Metal의 모든 것은 삼각형이 기본이다. 복잡한 3D 모양도 일련의 삼각형으로 분해할 수 있다.
Metal의 기본 좌표계는 정규화된 좌표계이다. 기본적으로 (0, 0, 0.5)에 중심을 둔 2x2x1 큐브로 되어있다.
let vertexData: [Float] = [
0.0, 1.0, 0.0,
-1.0, -1.0, 0.0,
1.0, -1.0, 0.0
]
let dataSize = vertexData.count * MemoryLayout .size(ofValue: vertexData[0]) // 1
vertexBuffer = device.makeBuffer(bytes: vertexData, length: dataSize, options: []) // 2
makeBuffer
를 호출한다. MTLDevice 기본 구성을 위해 빈 배열을 전달한다.이전 섹션에서 만든 정점은 Vertex Shader라고 하는 작은 프로그램에 대한 입력이 된다. Vertex Shader는 단순히 GPU에서 실행되는 작은 프로그램으로, Metal Shading Language라고 하는 C++과 유사한 언어로 작성되었다.
Vertex Shader는 정점당 한 번 호출되며 정점의 색상, 좌표와 같은 정보를 가져와 잠재적으로 수정된 위치 및 기타 데이터를 반환한다.
vertex float4 basic_vertex( // 1
const device packed_float3 * vertex_array [[ buffer( 0 ) ]], // 2
unsigned int vid [[ vertex_id ]]) { // 3
return float4(vertex_array[vid], 1.0 ); // 4
}
vertex
키워드로 시작된다. 함수는 꼭짓점의 최종 위치를 반환한다. float4
를 표시하여 이 작업을 수행하고, 이름을 선언한다.Vertex Shader가 완료된 후 Metal은 각 화면의 Fragment에 대해 Fragment Shader라는 또 다른 셰이더를 호출한다.
Fragment Shader는 Vertex Shader의 출력값을 보간하여 입력 값을 가져온다.
Fragment Shader의 역할은 각 Fragment에dml 최종 색상을 반환하는 것이다.
Vertex, Fragment Shader를 만들었으므로 이제 Render Pipeline이라는 특수 개체로 결합해야한다.
Metal은 셰이더가 미리 컴파일되고 Render Pipeline 구성이 처음 설정한 후에 컴파일된다.
// 1
let defaultLibrary = device.makeDefaultLibrary()!
let fragmentProgram = defaultLibrary.makeFunction(name: "basic_fragment")
let vertexProgram = defaultLibrary.makeFunction(name: "basic_vertex")
// 2
let pipelineStateDescriptor = MTLRenderPipelineDescriptor()
pipelineStateDescriptor.vertexFunction = vertexProgram
pipelineStateDescriptor.fragmentFunction = fragmentProgram
pipelineStateDescriptor.colorAttachments[0].pixelFormat = .bgra8Unorm
// 3
pipelineState = try! device.makeRenderPipelineState(descriptor: pipelineStateDescriptor)
device.makeDefaultLibrary()
를 호출하여 얻은 객체를 통해 프로젝트에서 미리 컴파일된 세이더에 접근할 수 있다. 그리고 각 셰이더를 이름으로 접근할 수 있다.GPU에 한 번에 하나씩 실행하도록 지시하는 명령들의 정렬된 목록이다.
MTLCommandQueue 객체를 생성하고 아래 코드를 실행한다.
commandQueue = device.makeCommandQueue()
아래 5가지 단계로 실행된다.
1. Display Link 생성
2. Render Pass Descriptor 생성
3. Command Buffer 생성
4. Render Command Encoder 생성
5. Command Buffer Commit