viernes, 13 de marzo de 2015

Aplicaciones y Rendmimiento (Primera Parte)


Hasta ahora hemos utilizado métodos muy sencillos para mostrar gráficos. Básicamente porque son ejemplos simples que utilizan pocos recursos, pero en una aplicación real, donde se necesita controlar cientos de objetos al mismo tiempo, las cosas pueden ponerse feas muy rápidamente.







Sabemos que las aplicaciones generadas para plataformas móviles y de escritorio con OpenFL tiene como máximo una tasa de 60fps (Frames per Second o Cuadros por Segundo).

Corriendo a esa frecuencia, si tenemos muchos objetos con cada renderización, el tiempo restante (dentro de ese segundo) que el CPU tiene para las demás tareas (Lógica, manejo de eventos, etc) es relativamente escaso.
En ese momento, cuando el CPU (+ el GPU) están abarrotados, nuestra aplicación comienza a sufrir un deterioro de la performance.



Las dos imágenes que se encuentran debajo, muestran un ejemplo práctico de lo que estoy hablando: La aplicación genera cada segundo 100 objetos (imágenes) al azar y los pone en pantalla incrementando de esa forma la carga que tiene el procesador (o los procesadores).



En la aplicación, que veremos en detalle a continuación, hay tres eventos:

  • Un temporizador: que se encarga de cargar los datos que van a ser mostrados en pantalla.
  • Un evento del Mouse (click): que activa y desactiva el temporizador.
  • El evento de dibujo de objetos al entrar al cuadro.




Puede verse en el reporte generado por Firefox que el tiempo utilizado por el procesador para la renderización de imágenes (en el caso de firefox el engine encargado de la renderización es Gecko)  supera ampliamente a los demás procesos (atender los otros eventos Timer y Mouse)

Obviamente que la capacidad de procesamiento está íntimamente ligada al hardware, pero eso no debería ser excusa para poder producir código más eficiente.

Para un mejor manejo de gráficos, Openfl cuenta con un par de métodos que nos van a servir de ayuda.

En los ejemplos anteriores, usamos BitmapData  - beginBitmapFill como método para cargar imágenes en nuestros sprites. Todo bien, cuando se utilizan pocos.

El problema es que este método tiene varios inconvenientes cuando se utilizan a gran escala:

La primera y quizás ya se habrán dado cuenta es que se necesita un archivo con la imagen por cada uno de los objetos que deseamos pintar con ella. Esto se traduce en código no sólo al inicio del programa, sino cuando removemos los objetos de la escena.

Luego está el problema de que por cada carga y descarga las rutinas invocadas son llamadas muy frecuentemente, restando tiempo que podría utilizarse en nuestro código.

Por último, estos métodos no están optimizados para que OpenFL pueda hacer uso de los coprocesadores gráficos (gpu).

Existe un método más eficiente para manipular imágenes que es casi un standard en la industria de software dedicada a los juegos que se basa en la técnica de los Tiles (o mosaicos) que veremos en la próxima entrada.

Por el momento, quiero hablar de dos funciones que nos van a ayudar a mejorar (o por lo menos ver el rendimiento) de nuestra aplicación:

La primera es la función FPS que OpenFL tiene incorporada dentro de su conjunto de librerías:

Prácticamente viene lista para usar y comenzamos por importarla:

import openfl.display.FPS;

Luego la declaramos:

var fps:FPS = new FPS(10, 10, 0xedf505);

Los parámetros son simples: Coordenadas X,Y y color

Por último la agregamos a la escena y listo..va a mostrarnos su valor sin necesidad de hacer uso de otra cosa.

this.addChild(fps);








Luego tenemos una función que es propia de las librerías de Haxe (heredado de NME): System

import openfl.system.System;

Cuenta con varias cosas interesantes para experimentar, aquí solo hago uso de ella para que me muestre la cantidad de memoria utilizada por la aplicación.




Como el valor que trae la función está expresado en bytes y sería muy grande de mostrar, lo pasamos a Mb. Lamentablemente no es soportado en HTML5

Una introducción a los Tiles en la próxima entrada.

Pueden descargar el código desde Git