Tutorial 0 β€” Preparation

Before writing any Metal code, this page walks you through cloning the project, understanding its structure, and the key concepts behind Metal rendering.

Prerequisites

Before diving into Metal, it’s helpful to have a solid foundation in key concepts.

Swift

Graphics Fundamentals

  • Why: Understanding how graphics work under the hood helps you optimize performance and troubleshoot issues.
  • Resources:
    • Learn OpenGL β€” Great for understanding the basics of rendering pipelines.
    • Scratchapixel β€” In-depth tutorials on graphics algorithms.

Metal API

  • Why: Metal is Apple’s low-level graphics framework, optimized for performance on Apple Silicon and GPUs. It abstracts hardware details while giving you fine-grained control over rendering pipelines.
  • Resources:

Useful references to keep open while working through the tutorials:


Setting up the project

Clone the repository

git clone https://github.com/Fe0437/MetalTutorials.git

Launch Xcode and open the MetalTutorials.xcodeproj file to begin exploring the project structure and tutorials.

Switching between tutorials

To switch between tutorials, open MTMetalTutorialsApp.swift and modify the content view by changing the function call in the WindowGroup:

@main
struct MetalTutorialsApp: App {
    var body: some Scene {
        WindowGroup {
            // substitute here to choose the tutorial
            MT1ContentView()   // ← change to MT2ContentView(), MT3ContentView(), …
        }
    }
}

Each MTxContentView wires up the corresponding MTKView and renderer for that tutorial.


Files

MetalTutorials/
└── Shared/
    β”œβ”€β”€ MTMetalTutorialsApp.swift                    ← entry point, switch tutorials here
    β”œβ”€β”€ Tutorial 1 - Hello/
    β”‚   β”œβ”€β”€ MT1ContentView.swift
    β”‚   β”œβ”€β”€ MT1Simple2DTriangleMetalView.swift
    β”‚   β”œβ”€β”€ MT1HelloShaders.metal
    β”‚   └── MT1Vertex.h
    β”œβ”€β”€ Tutorial 2 - Sample Object/
    β”‚   β”œβ”€β”€ MT2ContentView.swift
    β”‚   β”œβ”€β”€ MT2SampleObjectMetalView.swift
    β”‚   β”œβ”€β”€ MT2ObjRenderer.swift
    β”‚   β”œβ”€β”€ MT2SampleObjectShaders.metal
    β”‚   └── MT2Uniforms.h
    β”œβ”€β”€ Tutorial 3 - Deferred Rendering Object/
    β”‚   β”œβ”€β”€ MT3ContentView.swift
    β”‚   β”œβ”€β”€ MT3DeferredMetalView.swift
    β”‚   β”œβ”€β”€ MT3DeferredRenderer.swift
    β”‚   β”œβ”€β”€ MT3DeferredRendering.metal
    β”‚   β”œβ”€β”€ MT3GBuffer.h
    β”‚   └── MT3Uniforms.h
    β”œβ”€β”€ Tutorial 4 - Shadows/
    β”‚   β”œβ”€β”€ MT4ContentView.swift
    β”‚   └── …
    β”œβ”€β”€ Tutorial 5 - Tiled Rendering/
    β”‚   β”œβ”€β”€ MT5ContentView.swift
    β”‚   └── …
    β”œβ”€β”€ Tutorial 6 - GPU Rendering/
    β”‚   β”œβ”€β”€ MT6ContentView.swift
    β”‚   └── …
    └── Resources/
        └── Obj/bunny.obj                            ← Stanford bunny used in tutorials 2+

Each tutorial folder is self-contained and builds on the previous one. The Metal shader namespaces (MT1::, MT2::, MT3::) keep everything clearly separated.


How Metal works

GPU Render Pipeline

flowchart TD
    A["Vertex Shader"] --> B["Rasterization"]
    B --> C["Fragment Shader"]
    C --> D["Blending"]
    D --> E["Framebuffer"]

The Metal rendering loop

To understand each tutorial, follow this basic Metal rendering loop pattern:

flowchart TD
    A["App launch"] --> B["MTKView created with MTLDevice"]
    B --> C["MTKViewDelegate set"]
    C --> D["mtkView drawableSizeWillChange"]
    C --> E["draw in view called each frame"]
    E --> F["MTLCommandBuffer"]
    F --> G["MTLRenderCommandEncoder"]
    G --> H["Draw calls"]
    H --> I["present and commit"]

Understanding this loop before diving into the tutorials will make the code much easier to follow.

UIViewRepresentable pattern

flowchart TD
    A["struct MyMetalView: UIViewRepresentable"] --> B["makeUIView returns MTKView"]
    B --> C["makeCoordinator returns MTRenderer"]
    C --> D["MTRenderer is the MTKViewDelegate"]

The tutorials target iOS and use UIViewRepresentable to embed the MTKView (a UIKit class) into SwiftUI:

struct MyMetalView: UIViewRepresentable {
    typealias UIViewType = MTKView

    func makeUIView(context: Context) -> MTKView { … }
    func updateUIView(_ uiView: MTKView, context: Context) { }

    // the coordinator IS the renderer / MTKViewDelegate
    func makeCoordinator() -> MyRenderer { … }
}

The Coordinator pattern lets the renderer own the MTKView delegate without the view being recreated on every SwiftUI update.


Code

Tutorial 0 is a preparation guide β€” no code to write yet! Head to Tutorial 1 for your first Metal project.


Key concepts recap

  • GPU render pipeline β€” Vertices are transformed by the vertex shader, rasterized into fragments, colored by the fragment shader, blended, and written to the framebuffer.
  • CPU β†’ GPU data flow β€” The CPU prepares vertex buffers, index buffers, and uniform buffers, then hands them to the GPU via Metal command encoders.
  • Metal rendering loop β€” Each frame, the app creates a command buffer, encodes draw calls with a render command encoder, presents the drawable, and commits the buffer.
  • UIViewRepresentable pattern β€” SwiftUI wraps the UIKit-based MTKView using UIViewRepresentable, with a Coordinator that acts as the MTKViewDelegate and renderer.

πŸŽ‰ Congrats! You finished Tutorial 0!

Tutorial 1 β€” Hello Triangle