Metal

Preview:

Citation preview

Nilson Soutoxissburg

Venha bater um papo com a galera no IRC @ freenode ;)

#iphonedev #iphonedev-chat

#swift-lang ##OpenGL

Metal?• API gráfica de baixo nível que apresenta uma

abstração muito próxima à arquitetura das GPUs

• “bare metal access”

• “close to the metal”

• Substitui o OpenGL ES

• Camada mais fina entre o software e o hardware

Metal?• Outras tecnologias semelhantes:

• AMD Mantle

• Microsoft DirectX 12

• OpenGL Next

• A ideia é produzir APIs diretamente compatíveis com as arquiteturas das GPUs modernas

• Descartar APIs criadas há mais de 20 anos

Conceitos• Device

• Command Queue

• Command Buffer

• Command Encoder

• Buffers

• Textures

• Functions

• Pipeline

Device

• Representa a GPU

• Funciona como uma factory para criar command queues, buffers, texturas, etc

• Utilizamos a função MTLCreateSystemDefaultDevice para criar uma instância

Command Queue

• Fila de comandos a serem executados pela GPU

• Através dela podemos criar command buffers, depois configurá-los e por fim submetê-los para execução

Command Buffer• Armazena comandos para serem executados pela

GPU

• Em geral, utilizamos um command buffer para desenhar um frame de uma animação

• Possível enfileirar mais que um comando para fazer uma renderização em múltiplos passos

• Podemos ser notificados através de blocos quando o comando terminar sua execução

Command Encoder• Configura um comando para um command buffer

• Podemos criar 4 tipos de comandos:

• Render: um passo de renderização, para desenhar algo na tela ou numa textura

• Compute: permite usar a GPU para fazer cálculos de propósito geral (GPGPU)

• Blit: operações de cópia de memória entre buffers e texturas

• Parallel: permite codificar vários comandos em múltiplas threads

Buffer• Representa um buffer de memória que pode ser

usado pela GPU

• Pode conter qualquer coisa

• Por exemplo, utilizamos buffers para armazenar vértices (vertex buffers) que formam os triângulos que queremos desenhar, matrizes de transformação, e dados de iluminação

Texture

• Representa uma imagem que pode ser utilizada pela GPU

• Pode ser usada para desenhar o conteúdo da imagem na tela ou como destino para o resultado de uma renderização (render to target)

• Pode ter uma, duas ou três dimensões

Function• Representa um shader

• Vertex shader

• Fragment shader

• Compute kernel

• Função que é executada diretamente na GPU (programmable pipeline)

• Deve ser escrita usando a Metal Shading Language, linguagem baseada no C++11

Pipeline• Configura as partes fixas do pipeline gráfico, e.g.

blending e antialiasing

• Especifica quais functions devem ser usadas

• Descreve o formato de dados dos vértices

• Um command encoder deve especificar o pipeline que deve ser utilizado em sua execução

Pipeline

Metal Objects

Criando um Projeto• Single View Application

Criando um Projeto• Adicionar frameworks

Criando um Projeto• Metal.framework e QuartzCore.framework

Criando um Projeto• Criar um shader (Metal File)

Codificando…• Renomeie o arquivo ViewController.m para

ViewController.mm para que o compilador o interprete como um arquivo do tipo Objective-C++

• Assim podemos utilizar o simd.h que possui definições de tipos de dados vetoriais como float2, float3, float4, float3x3…

• Bastante útil para declarar a estrutura de dados dos vértices e fazer cálculos comuns em computação gráfica

Codificando…

• Imports no ViewController.mm:!

#import "ViewController.h" !#import <Metal/Metal.h> #import <QuartzCore/CAMetalLayer.h> #import <simd/simd.h>

Codificando…

• Cada um dos nossos 3 vértices devem ter uma posição (x, y) e uma cor (r, g, b, a)

!typedef struct { simd::float2 position; simd::float4 color; } Vertex;

Codificando…• Nossas properties!@interface ViewController () !@property (nonatomic, strong) id<MTLDevice> device; @property (nonatomic, strong) id<MTLBuffer> vertexBuffer; @property (nonatomic, strong) id<MTLRenderPipelineState> pipeline; @property (nonatomic, strong) id<MTLCommandQueue> commandQueue; @property (nonatomic, strong) CADisplayLink *displayLink; @property (nonatomic, strong) CAMetalLayer *metalLayer; !@end

Codificando…

• O CADisplayLink é utilizado para chamar nosso método de desenho várias vezes por segundo

• O CAMetalLayer é um CALayer capaz de apresentar o conteúdo desenhado pela GPU na tela do dispositivo

Codificando…• Criando o device e o metalLayer no viewDidLoad

self.device = MTLCreateSystemDefaultDevice(); !self.metalLayer = [CAMetalLayer layer];!

self.metalLayer.device = self.device;!

self.metalLayer.pixelFormat = MTLPixelFormatBGRA8Unorm;!

self.metalLayer.framebufferOnly = YES;!

self.metalLayer.frame = self.view.bounds;!

[self.view.layer addSublayer:self.metalLayer];

Codificando…• Criando o vertexBuffer

Vertex vertices[3]; vertices[0].position = { 0, 0.5}; vertices[0].color = {1, 0, 0, 1}; vertices[1].position = {-0.5, -0.5}; vertices[1].color = {0, 1, 0, 1}; vertices[2].position = { 0.5, -0.5}; vertices[2].color = {0, 0, 1, 1}; !self.vertexBuffer = [self.device newBufferWithBytes:vertices length:sizeof(vertices) options:0];

Codificando…• Sistema de coordenadas

Codificando…• Descrevendo a estrutura dos vértices

MTLVertexDescriptor *vertexDescriptor = [[MTLVertexDescriptor alloc] init]; [vertexDescriptor.attributes objectAtIndexedSubscript:0].format = MTLVertexFormatFloat2; [vertexDescriptor.attributes objectAtIndexedSubscript:0].bufferIndex = 0; [vertexDescriptor.attributes objectAtIndexedSubscript:0].offset = offsetof(Vertex, position); [vertexDescriptor.attributes objectAtIndexedSubscript:1].format = MTLVertexFormatFloat4; [vertexDescriptor.attributes objectAtIndexedSubscript:1].bufferIndex = 0; [vertexDescriptor.attributes objectAtIndexedSubscript:1].offset = offsetof(Vertex, color);

Codificando…

• Obtendo referências para as funções do nosso shader

id<MTLLibrary> library = [self.device newDefaultLibrary]; id<MTLFunction> vertexFunction = [library newFunctionWithName:@"basic_vertex"]; id<MTLFunction> fragmentFunction = [library newFunctionWithName:@"basic_fragment"];

Codificando…• Criar o pipeline gráfico

MTLRenderPipelineDescriptor *pipelineDescriptor = [[MTLRenderPipelineDescriptor alloc] init]; pipelineDescriptor.vertexDescriptor = vertexDescriptor; pipelineDescriptor.vertexFunction = vertexFunction; pipelineDescriptor.fragmentFunction = fragmentFunction; [pipelineDescriptor.colorAttachments objectAtIndexedSubscript:0].pixelFormat = self.metalLayer.pixelFormat; self.pipeline = [self.device newRenderPipelineStateWithDescriptor:pipelineDescriptor error:nil];

Codificando…

• Command queue e display link

self.commandQueue = [self.device newCommandQueue];

!self.displayLink = [CADisplayLink displayLinkWithTarget:self selector:@selector(render)]; [self.displayLink addToRunLoop:[NSRunLoop mainRunLoop] forMode:NSDefaultRunLoopMode];

Codificando…• Agora é só renderizar…

• Em nosso método render, primeiro obtemos o drawable do nosso layer que servirá como alvo da renderização

- (void)render { id<CAMetalDrawable> drawable = [self.metalLayer nextDrawable];

Codificando…• Configuramos um render pass descriptor que será

usado para criar um command encoder

MTLRenderPassDescriptor *renderPassDescriptor = [[MTLRenderPassDescriptor alloc] init]; MTLRenderPassColorAttachmentDescriptor *colorAttachment = [renderPassDescriptor.colorAttachments objectAtIndexedSubscript:0]; colorAttachment.texture = drawable.texture; colorAttachment.loadAction = MTLLoadActionClear; colorAttachment.clearColor = MTLClearColorMake(0, 0.4, 0.02, 1);

Codificando…

• Criamos um command buffer e em seguida um command encoder…

id<MTLCommandBuffer> commandBuffer = [self.commandQueue commandBuffer]; id<MTLRenderCommandEncoder> commandEncoder = [commandBuffer renderCommandEncoderWithDescriptor:renderPassDescriptor];

Codificando…• Configuramos o command encoder

[commandEncoder setRenderPipelineState:self.pipeline]; [commandEncoder setVertexBuffer:self.vertexBuffer offset:0 atIndex:0]; [commandEncoder drawPrimitives:MTLPrimitiveTypeTriangle vertexStart:0 vertexCount:3]; [commandEncoder endEncoding];

Codificando…

• E finalizamos submetendo o command buffer para execução

[commandBuffer presentDrawable:drawable]; [commandBuffer commit];

Codificando…• Mas ainda faltam os shaders!

#include <metal_stdlib> using namespace metal; !struct VertexInput { float2 position [[attribute(0)]]; float4 color [[attribute(1)]]; }; !struct VertexOutput { float4 position [[position]]; float4 color; };

Codificando…

• Vertex shader

vertex VertexOutput basic_vertex(VertexInput in [[stage_in]]) { VertexOutput out; out.position = float4(in.position, 0, 1); out.color = in.color; return out; }

Codificando…

• Fragment shader

fragment float4 basic_fragment(VertexOutput in [[stage_in]]) { return in.color; }

Codificando…• Pronto!

Ferramentas• O Xcode 6 possui poderosas ferramentas de

debugging para o Metal

• Permite capturar um frame da app e analisar cada buffer, textura, comando, etc

• Facilita encontrar o que pode estar causando problemas obscuros como obter uma tela vazia após desenhar vários objetos

Ferramentas

Ferramentas

Projeto exemplo• https://github.com/xissburg/MetalExamples

Compatibilidade

• iOS 8 em diante

• Não funciona no simulador

Compatibilidade

Compatibilidade

Compatibilidade

Conclusões• O Metal apresenta uma API concisa e

relativamente fácil de usar

• Permite maior controle sobre a GPU

• Maior performance, low-overhead

• Compatibilidade ainda limitada

• Dificulta desenvolvimento cross-platform

Referências

• Things that drive me nuts about OpenGL - Rich Geldreich's Tech Blog

• OpenGL 4.5 released, next-gen OpenGL unveiled: Cross-platform Mantle killer, DX12 competitor

Obrigado pela atenção!

• Dúvidas?

• Sugestões?

• Comentários?