Renderizar es el proceso de generar en pantalla una imagen a partir de unos datos. El responsable de representar por pantalla todo lo que sucede en una película Flash, tanto en el escenario como en el código, es el sistema de renderizado del runtime.
Debido a que ActionScript es un lenguaje 100% visual, tener un conocimiento profundo de este proceso nos ayudará a la hora de escribir y optimizar nuestro código, y evitará que cometamos errores normalmente difíciles de detectar.
En este artículo veremos cómo funciona.
El sistema de renderizado del Flash Player
El sistema de actualización de pantalla de Flash Player es automatizado y no hay forma de controlarlo.
Su funcionamiento interno está estrechamente ligado a la línea de tiempo de una película: se alcanza un frame, se ejecuta su código y se procesa la imagen. Incluso películas generadas sólo con código, sin ningún frame, o películas compiladas en Flex, que no cuenta con linea de tiempo, están gobernadas por el mismo mecanismo. A efectos del runtime de Flash, ejecuta cada película como si tuviera una linea de tiempo infinita.
El periodo de tiempo para visualizar cada frame viene marcado por el framerate del swf, que se mide en frames por segundo (fps).
Para hacernos mejor una idea, vamos a crear una serie de casos y a apoyar la explicación con gráficos y ejemplos.
Caso 1
Empecemos con una película con el framerate a 1 segundo, con código en el primer fotograma que crea contenido gráfico. Supongamos que Flash Player tarda 200 milisegundos en procesar el código, y que tarda 200 milisegundos más en hacer una actualización de la pantalla. El proceso sería el siguiente:
Procesamiento de 1 frame a 1 fps
Así pues, la cabeza lectora alcanza el primer fotograma y empieza a ejecutar el código. Una vez que ha acabado la pantalla no se actualiza inmediatamente, ya que el refresco viene determinado por el framerate, que está puesto a un segundo. Se entra en un tiempo de espera. Cuando se cumple el ciclo del framerate se procede a actualizar la pantalla, acción que tampoco es inmediata. Una vez que el Flash Player ha conseguido dibujar todo el contenido por pantalla, salta a un nuevo frame y vuelve a empezar el proceso.
Caso 2
Ahora imaginemos que la porción de código del frame es considerable y el programa tarda 1500ms en realizar dicha ejecución. ¿Qué sucede cuando se cumple el ciclo del framerate y no se ha procesado todo el código? Pues que el renderizado no se efectuará, se pondrá a la cola hasta que el código haya sido procesado.
El runtime de Flash nunca interrumpe la ejecución de código para actualizar la pantalla.
Podemos demostrarlo con un sencillo ejemplo. Tenemos una película con 5 frames y un framerate de 1 segundo. El quinto fotograma tiene un script que, en función de si está o no activo el checkbox, genera un bucle 9 mil millones de veces con el único objetivo de ralentizar el procesador.
Si el checkbox está activado al entrar en el frame 5, Flash Player comienza a ejecutar código que no puede procesar antes del ciclo del framerate, por lo que la pantalla no se actualiza y vemos durante más tiempo el número “4”.
Nota: cada swf tiene un tiempo máximo para ejecutar un script. Si se supera, aparece el famoso mensaje de “Un script de esta película está provocando que el Reproductor de Flash se ejecute lentamente“.
En AS3 está documentado como el error #1502: “El tiempo de ejecución del script ha superado el tiempo de espera predeterminado de 15 segundos“.
El valor del tiempo se puede configurar en las propiedades de publicación.
Caso 3
Pasemos ahora a una película con un sólo frame (hay que recordar que aunque sólo haya un frame o esté pausado, a efectos del runtime es como si hubiera una linea de tiempo infinita). En este frame se añade un listener al stage para escuchar un evento MouseEvent.CLICK, y con cada click se pinta una redonda en la posición del puntero. El framerate del swf está puesto a 0.4 (dos segundos y medio). Es notable la diferencia de tiempo desde que se ejecuta el código hasta que se dibujan las redondas en pantalla.
Si pensamos en el gráfico del “caso 1” sabemos que el código que registra el listener se ejecuta al principio, y que a cada ciclo se renderiza la pantalla. Pero… ¿cuándo se ejecuta el código asociado al evento click? Veamos otro gráfico más detallado.
Procesamiento de 1 frame a 1 fps
Cuando se entra en “Tiempo de espera”, cualquier código generado como resultado de un evento (de mouse, de teclado, de un Loader, …) se ejecuta en este periodo y se muestra en pantalla en el siguiente ciclo.
Debido al exagerado framerate utilizado en este ejemplo, en cada frame se puede generar varias veces código para pintar los puntos, y en “Tiempo de renderizado” se actualizan todas a la vez.
Caso 4
Añadamos un simple botón al ejemplo anterior. Probemos a hacer algunos clicks para dibujar puntos y acto seguido rollover sobre el botón.
Curiosamente, al hacer rollover se dibujan inmediatamente los puntos, aunque no hayan pasado los 2’5 segundos del framerate. Esto es así porque ciertos eventos pueden forzar al Flash Player a renderizar la pantalla. Como en este caso, cada vez que se interactúa con un botón se indica al runtime que debe actualizar la pantalla para reflejar el cambio de estado del mismo. Esta actualización no afecta únicamente al objeto, sino a todos los que están esperando para ser procesados.
Al margen de los botones, acciones como pasar el puntero sobre un Sprite o usar el tabulador entre cajas de texto también fuerzan a renderizar.
Y aún hay más, porque mediante el método updateAfterEvent, que muchos recordaréis de versiones anteriores de ActionScript, podemos indicar al Player que actualice la pantalla. En AS3 se puede invocar en las siguientes clases: MouseEvent, KeyboardEvent y TimerEvent.
En el ejemplo anterior, sin modificar el framerate, añadamos un updateAfterEvent al evento click y veremos como se pintan los puntos sin demora.
Así pues, podemos hablar de dos categorías a la hora de renderizar:
- La que se genera a intervalos regulados por el framerate
- La que se genera bajo demanda de ciertos eventos
Al principio del artículo comentaba que el sistema de actualización de pantalla de Flash Player era
automatizado. El hecho de poder usar updateAfterEvent no cambia nada, ya que realmente este método no
actualiza la pantalla directamente, sólo indica al runtime la necesidad de hacerlo. Sin en el momento de la
petición se encontrara ejecutando código no lo interrumpiría.
Es importante recordar que el renderizado siempre está gobernado internamente.
Caso 5
Veamos ahora un último ejemplo para entender como encaja el evento enterFrame en este rompecabezas.
En AS3 se puede definir un evento enterFrame en cualquier objeto del tipo display object (se encuentre o no en la displayList) y obviamente está sincronizado con el framerate de la película. Una vez definido, este evento se dispara cuando la cabeza lectora alcanza un nuevo fotograma, y lo hace antes de ejecutar cualquier código.
Procesamiento de 1 frame a 1 fps con un evento EnterFrame
Así pues, se alcanza un nuevo frame, se dispara un Event.ENTER_FRAME y se ejecuta el código asociado, acto seguido se ejecuta el código del frame y finalmente se entra en tiempo de espera hasta el siguiente renderizado.
Es importante notar que una vez que se dispara un Event.ENTER_FRAME lo hace a todos los objetos que están suscritos, aunque estén en diferentes líneas de tiempo, y se ejecuta siempre el código asociado de todos antes que el código del frame.
Para ejemplificar lo comentado, dejo un fla que define varios enterFrame en diferentes momentos y con código en cada frame. En la ventana de output se puede ver el orden de ejecución.
Comprobación de renderizado
En los gráficos que hemos visto, una vez pasado el “tiempo de espera” se entraba directamente en el “tiempo de renderizado”. Esto no es exactamente así.
Al finalizar el tiempo de espera se evalúa si ha habido cambios “visuales” desde el último frame. Si hay nuevos datos que dibujar o modificar, se procede a actualizar la pantalla, pero si el contenido del último frame y del actual son visualmente idénticos no se entra en “tiempo de renderizado”. De esta manera, el runtime evita hacer operaciones innecesarias.
Además, Flash Player sólo renderiza las zonas sensibles al cambio, no hace un update de toda la pantalla. Al conjunto de las zonas que hay que pintar se le llama “regiones de dibujo”, conocido en inglés como “redraw region”. Si miramos en el menú contextual cuando ejecutamos una película en el IDE (o en una versión debug), veremos la siguiente opción:
y al activarla encontraremos una serie de rectángulo rojos, que son las zonas que el Flash Player ha dibujado en el último render.
Framerate real
Durante todo el artículo hemos dicho varias veces que el framerate marca los ciclos en los que hay que actualizar la pantalla. Aunque esto es verdad, el framerate que le indicamos a nuestra película no suele alcanzarse la mayoría de veces, ya que en función de factores internos (la complejidad del código a ejecutar) y externos (la potencia de la máquina en la que se ejecuta Flash), el número de frames por segundo será menor al indicado. La cantidad de frames que es capaz de procesar el Flash Player en un segundo se conoce como framerate real.
Para mis experimentos utilizo una clase muy simple que cuenta los frames reales que se procesan en un segundo y los muestra por pantalla. Un ejemplo de su uso:
El ejemplo está puesto a 31 fps. Lo primero que hay que tener en cuenta es que por el hecho de correr una película en un navegador ya pierde 4 o 5 frames. A medida que vayamos pintando el framerate real cada vez irá bajando más y más, debido a la carga de procesamiento. Hay que advertir que si dejamos de interactuar, el framerate se recupera aproximadamente a los valores iniciales, ya que en ese momento no se procesa código y debido a que no hay cambios visuales el Flash Player no pasa por el proceso de renderizado.
Nota: la clase FPS da un valor aproximado de la cantidad de fps. Se debe utilizar únicamente para depurar ya que no limpia sus recursos.
Para acabar, veamos un resumen con todos los puntos claves del artículo.
Resumiendo
Flash Player cuenta con un sistema basado en frames para procesar código, y éste se combina con un motor de renderizado para dibujar los gráficos en pantalla.
La frecuencia de dicho procesamiento viene marcada por el framerate del swf.
El proceso de renderizado es automatizado.
Flash Player nunca interrumpe la ejecución de código para actualizar la pantalla.
Hay dos tipos de situaciones que desencadenan el renderizado: la que se genera a intervalos regulares marcados por el framerate y la que se genera tras la petición de ciertos eventos.
Cada vez que se alcanza un frame se sigue este proceso:
- Se dispara un evento ENTER_FRAME en caso de estar definido.
- Se ejecuta en código asociado al enterFrame.
- Se ejecuta el código del frame actual.
- Se entra en un modo de espera hasta que toque renderizar la pantalla.
- Si durante la ejecución de código anterior se ha definido eventos, se recogen y procesan en este tiempo de espera.
- Una vez cumplido el ciclo del framerate se procede a una comprobación: si ha habido cambios gráficos se renderiza, sino, se salta al siguiente frame.
Una vez iniciada una película, el Flash Player la trata como si tuviera una linea de tiempo infinita, no se deja de ejecutar hasta que se descarga.
El framerate asignado a una película no suele cumplirse, siempre suele ser menor debido a factores como la cantidad de código a ejecutar, la complejidad de éste, la potencia del ordenador, los recursos del sistema operativo, etc.
[…] ya alguna semanas escribí un post titulado El sistema de renderizado del Flash Player. Ese post estaba pensado inicialmente para explicarlo en este apartado, pero debido a lo extenso […]
[…] 2008/05/24/el-sistema-de-renderizado-de-flash-player/ […]