Upload
xissburg
View
70
Download
3
Embed Size (px)
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• Metal Programming Guide - developer.apple.com
• Metal Shading Language Guide - developer.apple.com
• http://metalbyexample.com
• iOS 8 Metal Tutorial with Swift: Getting Started - raywenderlich.com
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?