7 days of WordPress plugins, themes & templates - for free!* Unlimited asset downloads! Start 7-Day Free Trial
Advertisement
  1. Code
  2. iOS SDK

iOS 8: Introducción a Metal

Scroll to top
Read Time: 17 mins

Spanish (Español) translation by Esther (you can also view the original English article)

Este tutorial te mostrará cómo empezar a utilizar Metal, un marco introducido en iOS 8 que admite el renderizado de gráficos 3D acelerado por la GPU y las cargas de trabajo de cálculo paralelo de datos. En este tutorial, echaremos un vistazo a los conceptos teóricos que subyacen a Metal. También aprenderás a crear una aplicación Metal que establezca el estado del hardware necesario para los gráficos, que envíe comandos para su ejecución en la GPU y que gestione el buffer, los objetos de textura y los shaders precompilados.

1. Lo primero es lo primero

Este tutorial asume que estás familiarizado con el lenguaje Objective-C y que tienes algo de experiencia con OpenGL, OpenCL o una API de gráficos comparable.

También requiere un dispositivo físico con un procesador Apple A7 o A8. Esto significa que necesitarás un iPhone 5S, 6 o 6 Plus, o un iPad Air o mini (2ª generación). El simulador de iOS le dará errores de compilación.

Este tutorial se centra únicamente en Metal y no cubre el lenguaje de sombreado de Metal. Crearemos un shader, pero solo cubriremos las operaciones básicas para interactuar con él.

Si es la primera vez que utilizas Xcode, asegúrate de añadir tu ID de Apple en la sección de Cuentas de las Preferencias de Xcode. Esto asegurará que no tengas problemas al desplegar una aplicación en tu dispositivo.

Xcode 6 incluye una plantilla de proyecto para Metal, pero para ayudarte a entender mejor Metal, vamos a crear un proyecto desde cero.

Como nota final, en este tutorial utilizaremos Objective-C y es importante que tengas un conocimiento básico de este lenguaje de programación.

2. Introducción

Para aquellos que estén familiarizados con OpenGL u OpenGL ES, Metal es un marco de trabajo de gráficos 3D de bajo nivel, pero con menor sobrecarga. A diferencia de los frameworks Sprite Kit o Scene Kit de Apple, con los que, por defecto, no puedes interactuar con el pipeline de renderizado, con Metal se tiene poder absoluto para crear, controlar y modificar ese pipeline.

Metal tiene las siguientes características:

  • El marco de trabajo proporciona un acceso extremadamente reducido a la GPU A7 y A8, lo que permite un rendimiento increíblemente alto para tareas de cálculo y renderizado de gráficos sofisticados.
  • Metal elimina muchos cuellos de botella de rendimiento, como la costosa validación de estados que se encuentra en las APIs gráficas tradicionales.
  • Está diseñado explícitamente para trasladar todas las costosas operaciones de traducción de estado y compilación fuera del entorno de ejecución y renderización.
  • Proporciona sombreadores precompilados, objetos de estado y programación explícita de comandos para garantizar que tu aplicación logre el mayor rendimiento y eficiencia posibles para el renderizado de gráficos y las tareas computacionales.
  • El marco fue diseñado para explotar las consideraciones arquitectónicas modernas, como el multiprocesamiento y la memoria compartida.
  • Está profundamente integrado con iOS 8, los chipsets A7 y A8 y el hardware de Apple, creando un marco unificado e independiente.

Basta con la teoría, es hora de entender cómo se construye una aplicación Metal.

3. Creación de una aplicación Metal

Una aplicación Metal se caracteriza por un conjunto de pasos necesarios para presentar correctamente los datos en pantalla. Estos pasos suelen crearse en orden y algunas referencias se pasan de uno a otro. Estos pasos son:

  • obtener el dispositivo
  • crear una cola de comandos
  • crear recursos, como buffers, texturas y shaders
  • crear una línea de renderizado
  • crear una vista

Paso 1: Obtener el dispositivo

Este paso implica la creación de un objeto MTLDevice, el corazón de una aplicación Metal. La clase MTLDevice proporciona una forma directa de comunicarse con el controlador y el hardware de la GPU. Para obtener una referencia a una instancia de MTLDevice, es necesario llamar al Dispositivo por defecto del sistema como se muestra a continuación. Con esta referencia, tienes acceso directo al hardware del dispositivo.

Paso 2: Crear una cola de comandos

La clase MTLCommandQueue proporciona una forma de enviar comandos o instrucciones a la GPU. Para inicializar una instancia de la clase MTLCommandQueue, es necesario utilizar el objeto MTLDevice que creamos anteriormente y llamar al método newCommandQueue en él.

Paso 3: Crear recursos

Este paso implica la creación de tus objetos buffer, texturas y otros recursos. En este tutorial, crearás vértices. Estos objetos se almacenan en el lado del servidor/GPU y para comunicarte con ellos necesitas crear una estructura de datos específica que debe contener datos similares a los disponibles en el objeto vértice.

Por ejemplo, si necesitas pasar datos para una posición de vértice 2D, debes declarar una estructura de datos que contenga un objeto para esa posición 2D. Entonces, debes declararlo tanto en el cliente, tu aplicación iOS, como en el lado del servidor, el shader Metal. Para aclarar las cosas, mira el siguiente ejemplo.

Ten en cuenta que debes importar la librería GLKMath del framework GLKit como se muestra a continuación.

A continuación, se declara un objeto con las coordenadas correctas.

Paso 4: Crear una línea de renderizado

La creación del pipeline de renderizado es probablemente el paso más complicado, ya que hay que ocuparse de varias inicializaciones y configuraciones, cada una de las cuales se ilustra en el siguiente diagrama.

RenderingPipeline

El proceso de renderización se configura mediante dos clases:

  • MTLRenderPipelineDescriptor: proporciona todos los estados de tu pipeline de renderizado, como las posiciones de los vértices, el color, la profundidad y los buffers de stencil, entre otros
  • MTLRenderPipelineState: la versión compilada de MTLRenderPipelineDescriptor y que se desplegará en el dispositivo

Ten en cuenta que no es necesario crear todos los objetos de la tubería de renderizado. Solo debes crear los que se ajusten a tus necesidades.

El siguiente fragmento de código muestra cómo crear el objeto MTLRenderPipelineDescriptor.

En este punto, has creado el descriptor, pero todavía tienes que configurarlo con al menos el formato de píxeles. Esto es lo que hacemos en el siguiente bloque de código.

Para aplicaciones más avanzadas, también es necesario configurar los sombreadores de vértices y fragmentos por defecto, como se muestra a continuación.

El método newFunctionWithName busca en el archivo fuente de Metal el método SomeVertexMethodName. El nombre del shader en sí no es importante, ya que la búsqueda se realiza directamente a través de los nombres de los métodos. Esto significa que debes definir métodos únicos para operaciones de sombreado únicas. Más adelante profundizaremos en los shaders de Metal.

Con el objeto MTLRenderPipelineDescriptor creado y configurado, el siguiente paso es crear y definir el MTLRenderPipelineState pasando el objeto MTLRenderPipelineDescriptor recién creado.

Paso 5: Crear una vista

Para crear una vista metálica, es necesario subclasificar UIView y anular el método layerClass como se muestra a continuación.

En este tutorial, veremos otra forma de crear una clase CAMetalLayer que da al desarrollador más control sobre las características y la configuración de la capa.

4. Dibujo de una aplicación Metal

Ahora que hemos inicializado los objetos necesarios, tenemos que empezar a dibujar algo en la pantalla. Al igual que la inicialización, hay que seguir una serie de pasos:

  • obtener el buffer de comandos
  • establecer un pase de renderizado
  • dibujar.
  • comprometerse con el buffer de comandos

Paso 1: Obtener el buffer de comandos

El paso inicial es crear un objeto que almacene una lista de comandos en serie para que el dispositivo los ejecute. Creas un objeto MTLCommandBuffer y añades comandos que serán ejecutados secuencialmente por la GPU. El siguiente fragmento de código muestra cómo crear un buffer de comandos. Utilizamos el objeto MTLCommandQueue que creamos anteriormente.

Paso 2: Iniciar un pase de renderizado

En Metal, la configuración del renderizado es compleja y necesitas indicar explícitamente cuándo comienza y cuándo termina el pase de renderizado. Necesitas definir las configuraciones de framebuffer por adelantado para que iOS configure el hardware correctamente para esa configuración específica.

Para aquellos que estén familiarizados con OpenGL y OpenGL ES, este paso es similar ya que el framebuffer tiene las mismas propiedades, la fijación del color (0 a 3), la profundidad y las configuraciones del stencil. Puedes ver una representación visual de este paso en el siguiente diagrama.

RenderingPass

Primero tienes que crear una textura para renderizar. La textura se crea a partir de la clase CAMetalDrawable y utiliza el método nextDrawable para recuperar la siguiente textura a dibujar en la lista.

Esta llamada a nextDrawable puede y sería el cuello de botella de tu aplicación ya que puede bloquear fácilmente tu aplicación. La CPU y la GPU pueden estar desincronizadas y la una debe esperar a la otra, lo que puede provocar un bloqueo. Hay mecanismos síncronos que pueden, y siempre deben, ser implementados para resolver estos problemas, pero no voy a cubrirlos en este tutorial introductorio.

Ahora que tienes una textura para renderizar, necesitas crear un objeto MTLRenderPassDescriptor para almacenar la información del framebuffer y la textura. Echa un vistazo al siguiente fragmento de código para ver cómo funciona.

El primer paso establece la textura a dibujar. La segunda define una acción específica a realizar, en este caso borrar la textura y evitar que el contenido de esa textura se cargue en la caché de la GPU. El último paso cambia el color de fondo a un color específico.

Paso 3: Dibujar

Con el framebuffer y la textura configurados, es hora de crear una instancia de MTLRenderCommandEncoder. La clase MTLRenderCommandEncoder es responsable de las interacciones tradicionales con la pantalla y puede ser vista como un contenedor para un estado de renderización de gráficos. También traduce tu código a un formato de comando específico del hardware que será ejecutado por el dispositivo.

Paso 4: Comprometerse con el buffer de comandos

Ahora tenemos un buffer e instrucciones esperando en la memoria. El siguiente paso es enviar los comandos al buffer de comandos y ver los gráficos dibujados en la pantalla. Ten en cuenta que la GPU solo ejecutará el código que tú hayas consignado específicamente para el efecto. Las siguientes líneas de código te permiten programar tu framebuffer y enviar el buffer de comandos a la GPU.

Llegados a este punto, deberías tener una idea general de cómo está estructurada una aplicación Metal. Sin embargo, para entender bien todo esto, hay que hacerlo uno mismo. Ahora es el momento de codificar tu primera aplicación de Metal.

5. Creación de una aplicación Metal

Inicia Xcode 6 y elige Nuevo > Proyecto... en el menú Archivo. Selecciona Aplicación de Vista Única de la lista de plantillas y elige un nombre de producto. Establece Objective-C como lenguaje y selecciona iPhone en el menú Dispositivos.

Abre ViewController.m y añade las siguientes declaraciones de importación en la parte superior.

También hay que añadir los frameworks Metal y QuartzCore en la sección Linked Frameworks and Libraries de las fases de compilación del objetivo. A partir de ahora, tu atención debe dirigirse al archivo de implementación de la clase ViewController.

6. Creación de la estructura de Metal

Como mencioné anteriormente, tu primera tarea es establecer e inicializar los objetos centrales utilizados en toda la aplicación. En el siguiente fragmento de código, declaramos una serie de variables de instancia. Si has leído la primera parte de este tutorial, esto te resultará familiar.

En el método viewDidLoad del controlador de la vista, inicializamos las instancias MTLDevice y CommandQueue.

Ahora puedes interactuar con tu dispositivo y crear colas de comandos. Ahora es el momento de configurar el objeto CAMetalLayer. Tu capa CAMetalLayer debe tener una configuración específica en función del dispositivo, el formato de los píxeles y el tamaño del cuadro. También debes especificar que se utilizará solo el framebuffer y que se añadirá a la capa actual.

Si tienes un problema para configurar el objeto CAMetalLayer, el siguiente fragmento de código te ayudará con esto.

También debes establecer la opacidad de la vista, el color de fondo y el factor de escala del contenido. Esto se ilustra en el siguiente fragmento de código.

El único paso que queda es el de renderizar algo en la pantalla. Inicializa el CADisplayLink, pasando self como destino y @selector(renderScene) como selector. Por último, añade el objeto CADisplayLink al bucle de ejecución actual.

Este es el aspecto que debería tener el método viewDidLoad completado.

Si construyes el proyecto, notarás que Xcode nos da una advertencia. Todavía tenemos que implementar el método renderScene.

El método renderScene se ejecuta en cada fotograma. Hay varios objetos que necesitan ser inicializados para cada nuevo cuadro, como los objetos MTLCommandBuffer y MTLRenderCommandEncoder.

Los pasos que tenemos que dar para renderizar un cuadro son:

  • crear un objeto MTLCommandBuffer
  • inicializar un objeto CAMetalDrawable
  • inicializar un objeto MTLRenderPassDescriptor
  • configurar las propiedades texture, loadAction, clearColor y storeAction del objeto MTLRenderPassDescriptor
  • crear un nuevo objeto MTLRenderCommandEncoder
  • presentar el dibujable y consignar el buffer de comandos

No dudes en revisar lo que hemos visto hasta ahora para resolver este reto por tu cuenta. Si quieres continuar con este tutorial, entonces echa un vistazo a la solución que se muestra a continuación.

También necesitamos implementar el método dealloc del controlador de vista en el que invalidamos el objeto displayLink. Establecemos los objetos mtlDevice y mtlCommandQueue a nil.

7. Dibujar un triángulo

Ahora tienes una aplicación muy básica de Metal. Es el momento de añadir tu primera primitiva gráfica, un triángulo. El primer paso es crear una estructura para el triángulo.

No olvides añadir una declaración de importación para la biblioteca GLKMath en la parte superior de ViewController.m.

Para renderizar el triángulo, es necesario crear un objeto MTLRenderPipelineDescriptor y un objeto MTLRenderPipelineState. Además, cada objeto que se dibuja en la pantalla pertenece a la clase MTLBuffer.

Con estas variables de instancia declaradas, ahora debes inicializarlas en el método viewDidLoad como expliqué anteriormente.

Para sombrear el triángulo, necesitaremos sombreadores de Metal. Los shaders de Metal deben ser asignados al objeto MTLRenderPipelineDescriptor y encapsulados a través de un protocolo MTLLibrary. Puede parecer complejo, pero solo hay que utilizar las siguientes líneas de código:

La primera línea crea un objeto que se ajusta al protocolo de la MTLLibrary. En la segunda línea, le decimos a la librería qué método debe ser invocado dentro del shader para operar el pase de vértices dentro del pipeline de renderizado. En la tercera línea, repetimos este paso a nivel de píxel, los fragmentos. Finalmente, en la última línea creamos un objeto MTLRenderPipelineState.

En Metal se pueden definir las coordenadas del sistema, pero en este tutorial se utilizará el sistema de coordenadas por defecto, es decir, las coordenadas del centro de la pantalla son (0,0).

En el siguiente bloque de código, creamos un objeto triángulo con tres coordenadas, (-.5f, 0.0f), (0.5f, 0.0f), (0.0f, 0.5f).

A continuación, añadimos el triángulo al objeto <MTLBuffer>, que crea un buffer para el triángulo.

Este es el aspecto que debería tener el método viewDidLoad completado.

El método viewDidLoad está completo, pero falta un último paso, crear los shaders.

8. Creación de sombreadores

Para crear un sombreador de Metal, selecciona Nuevo > Archivo... en el menú Archivo, elige Fuente > Archivo de Metal en la sección iOS, y nómbralo MyShader. Xcode creará entonces un nuevo archivo para ti, MyShader.metal.

En la parte superior, debería ver las siguientes dos líneas de código. El primero incluye la Biblioteca Estándar de Metal mientras que el segundo utiliza el espacio de nombres de metal.

El primer paso es copiar la estructura del triángulo al shader. Los sombreadores suelen dividirse en dos operaciones diferentes, vértices y píxeles (fragmentos). La primera está relacionada con la posición del vértice mientras que la segunda está relacionada con el color final de ese vértice y con todas las posiciones de los píxeles dentro del polígono. Puedes verlo así, el primero rasterizará el polígono, los píxeles del polígono, y el segundo sombreará esos mismos píxeles.

Como tienen que comunicarse de forma unidireccional, de vértice a fragmento, lo mejor es crear una estructura para los datos que se pasarán. En este caso, solo pasamos la posición.

Ahora, vamos a crear los métodos de vértices y fragmentos. ¿Recuerdas cuando programaste el objeto RenderPipelineDescriptor tanto para vértices como para fragmentos? Has utilizado el método newFunctionWithName, pasando un objeto NSString. Esa cadena es el nombre del método que se llama dentro del shader. Esto significa que necesitas declarar dos métodos con esos nombres, VertexColor y FragmentColor.

¿Qué significa esto? Puedes crear tus shaders y nombrarlos como quieras, pero necesitas llamar a los métodos exactamente como los declaras y deben tener nombres únicos.

Dentro de tus shaders, añade el siguiente bloque de código.

El método VertexColor recibirá los datos almacenados en la posición 0 del buffer (memoria asignada) y el vertex_id del vértice. Como hemos declarado un triángulo de tres vértices, los vertex_id serán 0, 1 y 2. Se emite un objeto TriangleOutput que es recibido automáticamente por el FragmentColor. Finalmente, sombreará cada píxel dentro de esos tres vértices utilizando un color rojo.

Eso es todo. Construye y ejecuta tu aplicación y disfruta de tu primera y flamante aplicación de Metal a 60fps.

9. Recursos externos

Si quieres saber más sobre el framework Metal y su funcionamiento, puedes consultar otros recursos:

Conclusión

Con esto concluye nuestro tutorial de introducción al nuevo framework Metal. Si tienes alguna pregunta o comentario, no dudes en escribirlo en los comentarios.

Advertisement
Did you find this post useful?
Want a weekly email summary?
Subscribe below and we’ll send you a weekly email summary of all new Code tutorials. Never miss out on learning about the next big thing.
Advertisement
Looking for something to help kick start your next project?
Envato Market has a range of items for sale to help get you started.