나만의 공부 노트

Tutorial - Chapter 1 정리 본문

Graphics/Metal

Tutorial - Chapter 1 정리

va95 2023. 3. 29. 18:07

기본 구조


모듈

 

import Metal

  • 가장 Low 레벨의 Metal 모듈
  • 그래픽스 API를 가지고 있다.

import MetalKit

  • Metal을 유용하게 쓸 수 있도록 도와주는 유틸리티 모듈
  • Metal, Model I/O 모듈을 가지고 있음
    • Model I/O의 경우, MetalKit, GLKit, SceneKit 등에서 통합해서 사용할 수 있는 모델 로드 모듈

Metal 디바이스

 

guard let device = MTLCreateSystemDefaultDevice() else {

  fatalError("GPU is not supported")

}

 

  • iOS, watchOS 등은 GPU를 하나만 사용하므로 위의 API를 통해 기본 GPU를 얻어올 수 있다.

let devices = MTLCopyAllDevices()

 

or

 

let (devices, observer) = MTLCopyAllDevicesWithObserver() { (device, notification) in self.device(device, issued: notification)}

  • macOS는 GPU가 여러 개일 수 있다. default 디바이스를 얻을 수 있지만, 위의 API처럼 여러 개의 GPU 목록 또는 옵저버를 등록하여 추가 & 제거 여부를 실시간으로 감지할 수 있다.

 

GPU Type isLowPower isRemovable
Integrated true false
Discrete false false
External false true

GPU의 종류는 3가지 인데, macOS에서 default 디바이스를 얻어오면 Discrete 타입이 얻어진다고 한다.

  • Integrated는 인텔 칩 처럼 CPU와 GPU가 같이 있는 GPU 칩을 말하는 것 같다.
    • Integrated graphics means a computer where the graphics processing unit (GPU) is built onto the same die as the CPU. 
  • Discrete는 M1 칩 처럼 GPU가 별도로 구성되어 있는 GPU 칩을 말하는 것 같다.(M1 칩은 CPU와 메모리 공유를 하는게 아닌가? 흠..)
    • Discrete graphics is a GPU that is a separate from the processor. Discrete graphics has its own dedicated memory that is not shared with the CPU.
  • External는 외장 GPU 칩을 말하는 것 같다.

 


MTKView

  • NSView, UIView를 상속받은 뷰
  • delegate에 draw 함수 안에 그리는 로직을 넣으면, fps에 맞게 호출되어 자동으로 그려지는 형태
  • delegate를 안쓰면 매 프레임 갱신되지 않는다!

 


MTKMeshBufferAllocator

  • An interface for allocating a MetalKit buffer that backs the vertex data of a Model I/O mesh, suitable for use in a Metal app.
  • MDLMeshBufferAllocator 프로토콜의 구현체

 

MDLMeshBufferAllocator

  • Classes adopting this protocol provide different ways of handling mesh buffer data. For example, the MTKMeshBufferAllocator class can share mesh data with Metal buffers for use in rendering.
  • 프로토콜

 

메탈 버퍼 형태로 로드 단계부터 가져오기 위해서, MTKMeshBufferAllocator를 사용하는 것 같다.

 


MDLMesh

 

MDLMesh(sphereWithExtent: [0.75, 0.75, 0.75], segments: [100, 100], inwardNormals: false, geometryType: .triangles, allocator: allocator)

 

segments : vertex 갯수 -> [10, 1]의 결과

 

inwardNormals : 법선 백터를 안쪽으로 생성할 것인지 -> false가 당연(?)

 

let mesh = try MTKMesh(mesh: mdlMesh, device: device)

  • MTKMesh로 변환해서 사용해야한다.
  • device를 allocator에서 사용한 device와 다르게 사용하면 어떻게 될까?
    • MDLMesh를 만들 때, 반드시 allocator를 사용하는 것은 아니다.(아니네.. 반드시 필요함)
    • allocator에서 vertex buffer, index buffer를 만든다고 함
    • 즉, MDLMesh에서 Metal Device가 없어서 MTKMesh를 만들 때에도 device를 넣어주어야 하는 것 같다.
    • 왜.. device를 반드시 필요로 하지?? allocator면 충분할 것 같은데..
      • MDLMesh -> MTKMesh 타입 변환하는데, device가 필요한가? 잘 모르겟다.. 문서도 충분한 설명이 없다 ㅜ
      • 그냥 컨버팅하는데 Metal 전용 mesh로 변환하는거라,, 의미상 넣었을 수도

Command

  • Command queue
    • 보통 일반적으로 하나의 device, command queue를 쓴다.
  • Command buffer
    • command buffer에 commit을 호출함으로써, Command encoder를 한꺼번에 날릴 수 있다.
    • render말구 transfer, compute 등도 한 command buffer 안에 묶을 수 도 있을 듯?
      • shadow를 별도 render pass로 빼는 이유가, 물체의 외형만 따서 그림자 그리기 위해(몰랐다..) 최적화를 더 시킬 수 있는 것 같다.
    • 뭔가 한 Scene을 구성하는 모든 커맨드를 모아놓는 곳이 Command buffer? 라는 생각이 든다.
      • 한 프레임당 한 개의 커맨드 버퍼를 생성한다고 하는 것을 보니.. 맞는듯!?
  • Command encoder

렌더 인코더는 이런식..


MSL

 

쉐이더 코드는 추후에.. 상세히 분석

  • MTLLibrary
    • let library = try device.makeLibrary(source: shader, options: nil) 으로 만들 수 있음
    • options쪽에는 language version, dynamic library 링킹 등.. 유용한 옵션들을 설정할 수 있다고 한다.
  • 컴파일이 잘 되면(sync로 동작한다고 함)
    • let vertexFunction = library.makeFunction(name: "vertex_main") 처럼 함수 꺼내올 수 있다.
    • 타입은 MTLFunction

MTLRenderPipeline

  • MTLRenderPipelineDescriptor : MTLRenderPipelineState를 만들기 위해 필요한 인자들을 모아놓는 structure 같다. 재사용 가능, light 한 듯
    • colorAttachments[], vertexFunction, fragmentFunction, vertexDescriptor 등을 셋팅
    • 포맷, 쉐이더, 데이터 저장 방식 등을 저장하기 위한 것 같다.(그래서 데이터가 큰 버텍스 버퍼, 텍스쳐 등은 여기서 셋팅하지 않는듯?)
  • MTLRenderPipelineState : sync로 생성되며, 처리 시간이 상당함. 맨 처음 앱 구동(?) 시에 필요한 state들을 미리 생성해놓고 사용하는 패턴

Render Pass

이게 맞는거 아닐까? Command buffer랑 Render Pass Descriptor는 연관이 없는게 아닌가..

  1. 일단 커맨드 큐로부터, 커맨드 버퍼 생성
  2. MTLRenderPassDescriptor 생성
    1. texture attachments들을 지니고 있다고 함(?)
    2. 보니깐 MTKView에서 currentRenderPassDescriptor를 꺼내오면, MTKView가 원래 가지고 있는 텍스쳐가 colorAttachments[0]에 들어있는 것 같다.(아마 레퍼런스인듯)
    3. MTLRenderPipelineDescriptor의 colorAttachments[0].pixelFormat과 일치해야하는 것 같다.
    4. 아직 잘 모르겠다.. 추후에 깊게 파자.
  3. MTLRenderCommandEncoder 생성
    1. MTLRenderPipelineState 셋팅
      1. 이제보니 state는 데이터는 일절 가지고 있지 않고, format & 타입 같은 메타 정보만 담고 있는 것 같다.
      2. colorAttachments[0].pixelFormat = .bgra8Unorm 을 보니, 픽셀 포맷이 뭔지만 알려주었었다.
      3. 여기서 설정한 colorAttachments에 맞는 MTLRenderPassDescriptor에 텍스쳐를 올려야하는 거 같다!
    2. MTLBuffer 셋팅
      1. setVertexBuffer로 버텍스 버퍼 올려주기
  4. MTLRenderCommandEncoder에 draw 콜 여러 번 호출
  5. MTLRenderCommandEncoder에 endEncoding 콜 호출
  6. MTLRenderCommandEncoder에 present 콜 호출
  7. MTLRenderCommandEncoder에 commit 콜 호출

'Graphics > Metal' 카테고리의 다른 글

Tutorial - Chapter 3 정리  (0) 2023.03.31
Tutorial - Chapter 2 정리  (0) 2023.03.29