El blog de Pablo Zurita.

Junio 28, 2006

Podcast – Chromaticity Engine.

Archivado en: General — Pablo Zurita @ 11:36 pm

Decidí hacer un podcast sobre mi nuevo engine. Es un podcast de 32 minutos donde hablo sobre el diseño del engine nuevo y las diferencias con el anterior. Como es el primer podcast en la pagina es una prueba pero si hay interés voy a seguir haciendo podcast porque para mi es muy fácil de grabar y me es mucho mas fácil que escribir.
Pueden bajar el podcast de http://www.pablo-zurita.com.ar/podcasts/ChromaPodcast.mp3

Chromaticity Engine - Sponza Atrium Wireframe

Junio 12, 2006

Tone Mapping y Blooms

Archivado en: General — Pablo Zurita @ 6:26 pm

Últimamente dos de los agregados mas importantes que se le han dado a las escenas en tiempo real es el tonemapping y blooming. Pero por varias razones se ha estado realizando de manera demasiado incorrecta desde el punto de vista psicofísico. La idea de este artículo es analizar de manera superficial como ocurren estos efectos en la vida real y después analizar como simular en tiempo real.

El ojo humano tiene dos formas de células fotosensitivas en la retina, los conos y los bastones. Aunque los dos son similares en estructura, los dos son bastante diferentes e igualmente importantes para la visión. Los bastones son altamente sensibles a la luz lo que les permite responder en situaciones de iluminación baja. Los conos necesitan más luz para responder pero permiten ver un rango de luz más alto (y entre otras cosas permiten ver colores). Tampoco nos podemos olvidar de la pupila que ajusta su tamaño constantemente para mantener la entra de luz en un nivel mas o menos constante. Analizando un poco cada célula fotosensitivas podemos darnos cuenta que podemos ver en un gran rango de intensidades de luz. Estos rangos se los puede dividir en tres regiones. La región fotopica con rango de 10^1 cd/m^2 a 18^8 cd/m^2 y que estimula a los conos. La región escotópica con rango de 10^-1 cd/m^2 a 10^-6 cd/m^2 y que estimula a los bastones. La tercera región es la región mesópica, la cual estimula a los conos y bastones. Pero una parte muy importante es la pupila porque mediante su cambio constante de tamaño permite que nuestro sistema responda a diferentes intensidades de luz. Por ejemplo a bajas intensidades de luz la pupila se abre lo mas posible para dejar entrar luz, en niveles altos de intensidad la pupila se cierra para que menos luz entre y así mantener la visión en una región en la que los conos y bastones puedan generar una respuesta. Pero hay veces que no importa que haga la pupila, no va a haber forma de generar la respuesta esperada, lo que quiere decir que o la intensidad es tan baja que no genera ninguna respuesta o la intensidad es tan alta que la sensibilidad de la visión baja mas y mas. Esto es conocido como compresión de respuesta y es la parte vital para determinar el sistema de tone mapping correcto. Pero hay otro factor, aunque la sensibilidad del ojo cambia de acuerdo a la iluminación predominante, este proceso no es instantáneo. A este mecanismo se le llama adaptación. El periodo de adaptación pueden llegar a ser de hasta 15 minutos en los conos y 40 minutos en los bastones para la adaptación total en un cambio severo de iluminación. Teniendo todo esto claro podemos empezar a hablar del tone mapping en si.

La idea de los operadores de tone mapping es pasar una escena de High Dynamic Range (HDR) a un medio Low Dynamic Range (LDR). Estos operadores se pueden dividir en diferentes categorías según como intentan llevar la información HDR a un medio LDR. Los operadores que aplican la misma función sobre toda una imagen son conocidos como operadores globales. Estos algoritmos tienden a ser muy buenos en performance por su simplicidad, pero tienden a perder mucha información sobretodo si hay un buen espectro de luminancia. Ejemplos de operadores globales son el operador de Ward, Schlick, Tumblin y Drago. Los operadores que dependen de la información de píxeles contiguos son llamadores operadores locales. Los operadores locales son los mejores ya que pueden proveer una gran compresión de la información HDR sin perder detalles. Ejemplos de operadores globales son el operador de Pattanaik, Jonson, Chiu, etc. Como en nuestro caso queremos agregar mas realidad a nuestras escenas simulando el comportamiento del ojo humano, esto quiere decir que vamos a necesitar un operador que tenga en cuenta los conos y bastones, y los tiempos de adaptación.

Independientemente del operador que decidas usar hay cierta información que hay que obtener siempre como por ejemplo la intensidad global. El problema es que la mayoría de los operadores están pensados para que corran en CPU, pero cuando tenemos que hacer eso en GPU el tema es un poco diferente porque las restricciones son diferentes. Por ejemplo obtener la intensidad global en CPU es tan fácil como hacer un loop, vas sumando la intensidad en cada píxel y después dividís por una constante para obtener la intensidad global. Pero en GPU es un poco mas complicado sobretodo por dos problemas, primero es demasiado intensivo calcular la intensidad global en cada fragment (porque se ejecuta en un fragment shader), y segundo no podemos hacer un fetch del fragment anterior o posterior. Veamos por ejemplo como obtener la luminancia global y máxima de un frame. Teniendo en cuenta las restricciones anteriores, la forma de obtener la luminancia máxima y la global consiste en usar varios FBOs de diferentes resoluciones y ejecutar un shaders para obtener el valor. Podemos empezar con nuestra escena renderizada en un FBO de 512×512, de ahí lo que hacemos es usar el siguiente shader y poner el contenido en un FBO de 256×256:

varying vec2 vTexCoord;//Screen
uniform sampler2D alb_screenfbo; //FBO texture.
uniform float alb_screenfbo_width; //FBO Width.

void main (void)
{
vec4 finalCalc;
vec2 uvCoords;
float maxLum;
float avLum;
float currentLum;

uvCoords.x = vTexCoord.x - 0.5 / alb_screenfbo_width;
uvCoords.y = vTexCoord.y - 0.5 / alb_screenfbo_width;
currentLum = (0.30 * texture2D(alb_screenfbo, uvCoords).x) + (0.59 * texture2D(alb_screenfbo, uvCoords).y) + (0.11 * texture2D(alb_screenfbo, uvCoords).z);
avLum = log(currentLum + 1e-4);
maxLum = currentLum;

uvCoords.x = vTexCoord.x - 0.5 / alb_screenfbo_width;
uvCoords.y = vTexCoord.y + 0.5 / alb_screenfbo_width;
currentLum = (0.30 * texture2D(alb_screenfbo, uvCoords).x) + (0.59 * texture2D(alb_screenfbo, uvCoords).y) + (0.11 * texture2D(alb_screenfbo, uvCoords).z);
avLum = log(currentLum + 1e-4);
if(maxLum < currentLum)
maxLum = currentLum;

uvCoords.x = vTexCoord.x + 0.5 / alb_screenfbo_width;
uvCoords.y = vTexCoord.y - 0.5 / alb_screenfbo_width;
currentLum = (0.30 * texture2D(alb_screenfbo, uvCoords).x) + (0.59 * texture2D(alb_screenfbo, uvCoords).y) + (0.11 * texture2D(alb_screenfbo, uvCoords).z);
avLum = log(currentLum + 1e-4);
if(maxLum < currentLum)
maxLum = currentLum;

uvCoords.x = vTexCoord.x + 0.5 / alb_screenfbo_width;
uvCoords.y = vTexCoord.y + 0.5 / alb_screenfbo_width;
currentLum = (0.30 * texture2D(alb_screenfbo, uvCoords).x) + (0.59 * texture2D(alb_screenfbo, uvCoords).y) + (0.11 * texture2D(alb_screenfbo, uvCoords).z);
avLum = log(currentLum + 1e-4);
if(maxLum < currentLum)
maxLum = currentLum;

avLum = exp(avLum / (alb_screenfbo_width * alb_screenfbo_width));
finalCalc.x = avLum;
finalCalc.y = maxLum;
gl_FragColor = finalCalc;
}

Como se ve en el shader lo que hacemos es obtener el obtener la luminancia de los 4 fragments alrededor del sample. Esto lo hacemos varias veces hasta llegar a un FBO de 1×1, al final de todo el proceso vamos a tener la luminancia global en el canal red y la luminancia máxima en el canal green del FBO 1×1. Parte del proceso se puede ver en el siguiente diagrama.

HDR Chart

Ahora que tenemos nuestra escena lista podemos encargarnos del bloom. El bloom se ha hecho constantemente mal no tanto por el bloom en si sino por la información que usaban al momento de hacer el bloom. Lo correcto es que una vez que tenemos nuestra escena ajustada al nivel de luminancia, si hay algo que quedo fuera del rango LDR entonces desde esos fragments generamos el bloom (independientemente en cada canal). De esta manera nos diferenciamos con el modelo incorrecto, porque en general se tiende a hacer un bright-pass que no tiene nada que ver con la reacción del ojo y crean un blooming por todos lados cuando eso no pasa en la vida real. En vez de usar un bright-pass con una ecuación complicada y que no refleja lo que realmente pasa en la vida real, nuestro bright-pass simplemente consiste en los valores que están por encima de 1.0 después del tone mapping porque en ese momento los conos y bastones se vieron sobrepasados.

Bueno, espero que este artículo les ayude a elegir el operador de tone mapping apropiado, y también a hacer el blooming de manera correcta. Cualquier comentario sobre el tema lo pueden dejar aquí mismo y yo lo responderé a la brevedad.

Abril 8, 2006

Soporte para shaders en un engine.

Archivado en: General — Pablo Zurita @ 8:58 pm

Con la llegada de los GPUs programables, no hemos visto envueltos en un frenesí de shaders y herramientas. Todo el mundo baja herramientas como RenderMonkey, FX Composer, etc. para crear shaders, algunos hacen demos pequeñas mostrando las ultimas características de sus placas, y los shaders mas complejos y extraños. Todo esto es perfecto pero mucha gente no se da cuenta que uno de los problemas mas grandes de la flexibilidad disponible ahora no esta en escribir los shaders en si, sino en diseñar y escribir un engine que soporte los shaders y su flexibilidad. Esto es lo que voy a tratar de encarar en este pequeño artículo.

Introducción
Después de las lecciones aprendidas en la creación del ^FishEngine (un engine 3D muy parecido al del Doom 3), a finales de Febrero del 2005 inicie la producción de un nuevo engine 3D. Desde el primer día sabia que una de las diferencias principales entre el Albedo Engine y el antiguo ^FishEngine era el uso de glSlang, dejando de lado los register combiners y extensiones similares. Este cambio viene directamente de la necesidad de dar más flexibilidad a la hora de trabajar tanto a los programadores como a los artistas. Trabajar con extensiones propietarias que además están limitadas en sus características simplemente no va más. Uno no puede estar manteniendo cientos de shaders con versiones para cada extensión, ahora simplemente se escribir el shader en un lenguaje de alto nivel y listo.

Inicios
Hacer un engine no es una tarea que haces en una semana y que podes remplazar todos los días. Los engines son sistemas complejos de hacer, que toman mucho tiempo y en general no todos tienen el lujo de remplazarlo todos los días. Basado en el hecho que el Albedo Engine tiene que ser un engine que va a ser usado en cualquier proyecto 3D que realice, la flexibilidad y facilidad de uso son sumamente importantes. Y viendo la evolución de los gráficos, sabemos que tenemos que darle una importancia especial a los shaders ya que en el futuro van a ser cada vez mas complejos. Después de unos meses de trabajo, a mediados de Abril del 2005 empieza el complejo proceso de agregar soporte para shaders. Esto era una situación bastante complicada porque esto era una característica totalmente nueva que no estaba implementada en el ^FishEngine antiguo (había soporte para glSlang pero había un solo shader que era usado para toda la geometría de la misma manera que el Doom 3). Pero después de mucha investigación, tiempo de implementación y pruebas llegue a tener un soporte muy bueno.

Los shaders en el Albedo Engine
Un shader en el Albedo Engine no se refiere al el código glSlang del shader en si, sino que se refiere también a todos lo necesarios para que ese shader se dibuje de manera correcta. Esto implica cambios de estados como el blending, manejo de parámetros para los vertex shaders y los fragment shaders, manejo de las texturas para los shaders, y más. Por cada shader es posible especificar dos tipos de parámetros, parámetros locales que son parámetros que no cambian por material sino por shader, por ejemplo una constante, y los parámetros externos que pueden cambiar con el material como por ejemplo el valor especular de una superficie. A la vez, estos parámetros pueden ser valores internos del motor como por ejemplo se le puede pasar la posición del usuario, o pueden ser valores escritos por el usuario. En el motor no hay múltiples instancias de un mismo shader, excepto en el caso donde los parámetros internos son distintos, de lo contrario siempre hay una sola instancia de un shader. Por otra parte incluso si hay parámetros diferentes, hay un solo shader object compilado y ejecutado. Por otra parte tenemos un shader manager que se encarga de las cosas más básicas. Se encarga de leer los shaders, crear cada shader que va a manejar el engine, se encarga de liberar todos los recursos usados por los shaders cuando es necesario, pasar parámetros a los shader y demás. Es importante tener un buen shader manager para conectar a el engine con cada shader de una manera simple.

Los shaders y la geometría.
Una parte muy importante es ver la conexión que existe entre la geometría y los shaders. Mirando desde punto de vista de los shaders, podemos ver que un vertex shader aplicado a la geometría puede causar un montón de efectos de los que hay que tener cuidado. Por ejemplo si hacemos el culling de la geometría que no se ve, pero un shader aplicado a esa geometría hace que por ejemplo una esfera se vuelva 2 veces más grande entonces lo que va a pasar es que la esfera original va a salir de nuestro frustum lo que va a causar que de repente la esfera desaparece. Por eso es importante tener ciertos tipos de tags a la geometría como por ejemplo a esa esfera simplemente se dibuja siempre, o se dibuja si estamos en cierto sector. Por otra parte mirando desde el punto de vista de la peformance, es importante evitar lo más posible los cambios de estados o en este caso el cambio de shaders. Por eso es importante que la geometría que se va a dibujar este ordenada por shader o material de esa manera se reduce a uno la cantidad de veces que hay que cambiar el shader activo al momento de renderizar. En el caso del Albedo Engine cada nodo tiene la geometría organizada por material por lo tanto los cambios de material se minimizan lo mas posible. Por otra parte es importante reconocer que hay shaders que se aplican de diferentes maneras con respecto a la geometría para llegar a la solución final. Por ejemplo un efecto de tone mapping no tiene nada que ver con la geometría en si sino que se renderiza la escena final a una textura de punto flotante. Están los shaders que se aplican a toda la geometría por igual como podría ser la textura difusa de una superficie, no importa donde este la geometría y otros aspectos a su alrededor, siempre el componente es igual. Pero hay otros efectos que tienen un volumen de impacto, como podría ser una luz omnidireccional con un radio de decaída definido. En este caso el shader se aplica a toda la geometría que esta dentro de radio de impacto de la luz. Por eso es importante poder diferenciar eso al momento de escribir un engine porque seria una perdida de recursos renderizar toda la geometría cuando en verdad solo un porcentaje de la geometría se ve impactada por un shader volumétrico.

Los shaders y sus parámetros.
Lo más importante para un shader son sus parámetros. Esto se puede ver sobretodo con los sistemas de creación de shaders como el RenderMonkey de ATI, o el FX Componer de Nvidia. Mas allá de que son herramientas útiles, trabajar dentro de tu engine en si tiene muchísimo mas valor porque te permite trabajar con los shaders con unos parámetros y en un ambiente que es complicado simular en los shaders previewers. La diferencia de hacer un shaders para iluminación en un shader previewer y trabajar en tu motor mismo es abismal. Los shaders previewers te permite ver un shader específico en general en un entorno mínimo para poder probarlo, pero al momento de ver el producto final, lo importante son los parámetros que se le pasan desde tu engine y como todos los efectos interactúan para lograr un frame final que sea lo que el artista busca. Entonces es importante primero que nada hacerle fácil el trabajo a los diseñadores dándole herramientas intuitivas con un GUI para la edición de parámetros en tiempo real, o por lo menos una pequeña consola que les permita cambiar el valor de un parámetro. Todos esto se tiene que realizar dentro del engine mientras el artista esta en su escena. Es mas, si podes ofrecer edición del shader en si dentro del engine mejor todavía, pero igual ese son el tipo de características que se pueden cortar por tiempo, pero el GUI de edición de parámetros no ya que los beneficios que ofrece son muchos y el tiempo de implementación no es largo (teniendo en cuenta que ya tenes un GUI en tu engine).

Conclusión.
Espero que esto les ayude a ver como encarar la implementación de shaders en sus engines, que les ayude a ver cuales son los potenciales problemas y a que cosas hay que darle importancia. Si tienen cualquier pregunta lo pueden hacer a través de los comentarios.

Febrero 14, 2006

Scripting en el Albedo Engine.

Archivado en: General — Pablo Zurita @ 4:51 pm

Desde la semana pasada estoy trabajando en la integración básica de un sistema de scripting y la verdad es que ha sido muy interesante. Siempre en casos anteriores preferí usar DLLs para favorecer la performance pero ahora que a nivel de CPU podemos hacer mas cosas y teniendo en cuenta que por ejemplo el Albedo Engine no va a usar stencil shadows, hay CPU-time para agregar un sistema de scripting y hacerle mas fácil la vida a la persona que alguna vez llegue a usar el engine. Cuando estuve pensando en usar un sistema de scripting lo primero que me vino a la mente fue usar Lua. Después de todo, es uno de los sistemas de scripting más antiguos, y tiene una solidez que es consecuencia de la cantidad de usuarios que tiene. Pero después hacer varios tests me di cuenta que no es lo que quería. Primero que nada la sintaxis de LUA no me gusta, no concuerda con la sintaxis del engine (que esta escrito en C++). Trabajar con LUA en C++ es bastante molesto incluso si usas herramientas como toLua o usando otras librerías como Luabind (que tampoco es bueno porque no quiero quince capas intermedias para recién poder llegar a LUA). Por otra parte mas allá que no estaba buscando el máximo de performance (si lo hiciera seguiría haciendo DLLs), definitivamente no es que lo sacara de mi mente. Teniendo en cuenta eso empecé a buscar alternativas hasta que encontré AngelScript.

Primero que nada registrar clases es muy fácil, nada de tener que modificar las clases para que se ajuste al sistema de scripting. La sintaxis es muy parecida a C++ lo que me permite pasar código que tenia en el engine directamente al sistema de scripting de manera muy fácil. Es el sistema de scripting más rápido que probé, muy estable, no tiene ningún memory leak (por lo menos que haya visto). También es muy fácil de extender, por ejemplo AngelScript en si no soporta strings por defecto entonces lo que hice fue integrar de manera muy fácil los strings de STL. Realmente estoy muy conforme con el sistema de scripting aunque es verdad que solo tiene una semana, pero realmente no veo nada que pueda ser un inconveniente en el futuro.

Febrero 6, 2006

High Dynamic Range.

Archivado en: General — Pablo Zurita @ 5:19 pm

Con la llegada de placas que soportan formatos HDR (High Dynamic Range), y con el lanzamientos de monitores HDR como el BrightSide DR37-P es importante analizar porque manejar todo en HDR es importante, incluso cuando se hace la salida final a un dispositivo o formato LDR (Low Dynamic Range). Pero antes que nada veamos lo más básico, que quiere decir Low o High Dynamic Range.
Dynamic Range es un termino usado en diferentes campos de investigación para describir el radio entre el valor mas pequeño y mas grande posible en un indicador de cantidad variable. Si el dynamic range es Low (Bajo) entonces quiere decir que el radio entre el valor mas bajo y el mas alto es pequeño, y si es High (Alto) entonces el radio entre el valor mas bajo y el mas alto es grande.

En el campo de la computación grafica, sobretodo en la sección de simulaciones en tiempo real, siempre predomino el Low Dynamic Range ya que los valores en general eran guardados en variables de 8-bits con valores entre 0.0 y 1.0. Por ejemplo un lightmap en un píxel tiene el valor [1.0 1.0 0.5] entonces esta iluminado totalmente, y a eso se lo modulaba con el valor de una textura difusa con el valor por ejemplo [0.5 1.0 1.0] entonces se puede determinar que el valor final es [0.5 1.0 0.5].
Aunque eso se ve relativamente bien, ¿que pasa cuando tenemos que definir en el lightmap que la iluminación es dos veces mas fuerte que en el caso anterior? Ahí nos damos cuenta porque los valores LDR entre 0.0 y 1.0 limitan muchísimo por ejemplo en este caso la iluminación de una escena. En cambio si uno tiene un lightmap HDR entonces es posible guardar el valor [2.0 2.0 1.0] entonces al momento de modular el valor con el de la textura difusa el valor termina siendo [1.0 2.0 2.0]. Pero incluso cuando el beneficio de usar HDR no es tan evidente como en la iluminación, es importante ver que incluso cuando la salida final es a un medio LDR, todos los cálculos intermedios tendrían que estar en HDR. Porque en realidad, todo lo que se estuvo haciendo en el pasado con LDR era aproximar lo que realmente uno como programador grafico quería hacer. Pero cuando la tecnología llega para realizar algo de la manera correcta sin hacer aproximaciones, tenemos que dentro de lo posible utilizar al máximo esa tecnología porque hacer las cosas correctamente nos ayuda a mejorar en general nuestro producto.

Para tener en cuenta el impacto de tener formatos HDR internos podemos ver que ahora es posible hacer procesos de propósitos generales en el GPU sin necesidad de usar el CPU. Aunque la parte mas importante reside en el uso de mas que nada fragment programs, sin tener formatos HDR las posibilidades para implementar algoritmos básicos eran muy bajos por lo limitado en la parte matemática. En cambio con HDR es posible guardar valores de 16-bits o 32-bits por componente dando muchísima mas libertad para realizar algoritmos complejos donde los pasos intermedios y los resultados están todos en HDR. Para mas información sobre el tema pueden ir al sitio GPGPU.

Si tienen cualquier duda sobre el tema por favor dejen un comentario en este post y apenas lo vea lo respondo.

Noviembre 7, 2005

Distribución de aplicaciones C++ compiladas con Visual Studio 2005

Archivado en: General — Pablo Zurita @ 8:22 pm

Los últimos tres días he estado perdiendo tanto tiempo con esto que decidí hacer un post sobre el tema, espero que esta información le llegue a todos a través de Google y demás buscadores. Bueno, el tema es el siguiente, el Visual Studio 2005 tiene un sistema nuevo para el despliegue de aplicaciones en clientes. Esto se define en tipo “isolated applications” y “side-by-side assemblies”. Ahora las librerías como ATL, MFC y CRT son side-by-side assemblies que luego son usadas por las aplicaciones compiladas por Visual Studio 2005. Los beneficios principales de este sistema es que el despliegue de updates para una aplicación es menos dolorosa, y también se evita los problemas de versiones entre DLLs. Si necesitas mas información lee “About Isolated Applications and Side-by-side Assemblies”. El tema es el siguiente, cuando uno tiene instalado Visual Studio 2005 todo anda de lujo, uno puede copiar la aplicación a un directorio y todo anda perfecto. El problema es que cuando haces por ejemplo un zip de la aplicación y lo pasas a una computadora sin Visual Studio 2005 un error muy general es generado “The application has failed to start because the application configuration is incorrect. Reinstalling the application may fix this problem.”(Perdón pero no tengo la versión en español). Eso obviamente no te dice nada y uno empieza a buscar de manera interminable cual es el problema. La búsqueda en Google obtiene como resultado problemas similares en aplicaciones como el PuTTY, pero en general no hay una respuesta clara. Después de buscar tanto me puse a investigar y lo primero que note es que tenia que incluir un manifest file con mi aplicación. En Visual Studio 2005 esto se puede incluir dentro del EXE o es posible tenerlo en un archivo separado pero este archivo tiene que ser incluido obligatoriamente. Pero esto no resuelve el problema, la segunda parte implica instalar el runtime redistribuible del Visual Studio 2005 en el cliente donde va a ser corrida la aplicación. Lo podes bajar de acá. Una vez que esta eso entonces tu aplicación compilada en Visual Studio 2005 tiene que andar de manera adecuada en cualquier cliente (aunque hay algunas partes especificas para Windows 98 y Windows 2000 ya que ninguno de los dos soporta este método de despliegue de aplicaciones). Espero que esto les sirva y que no pierdan tiempo como yo lo perdí.

Octubre 22, 2005

Performance.

Archivado en: General — Pablo Zurita @ 3:52 pm

Si hay algo que todos los desarrolladores de software queremos, sobretodos los que trabajamos en la parte grafica, es buena performance. Mayor performance le permite al desarrollador dar mas características a un programa, le permite al usuario tener retroalimentación mas rápidamente sobre lo hizo con el software, y demás. Pero últimamente he estado mas pendiente sobre el error que muchos desarrolladores producen al momento de pensar en performance y de optimizar. Hay mucha gente que al momento de darse cuenta que tienen una problema de performance pregunta por ejemplo, si vale la pena hacer un expandir los bucles, si usar un array unidimensional seria mejor que uno bidimensional, o cosas de ese tipo. Y la respuesta real es, talvez. Pero en general, esos cambios no son los más importantes, y por otra parte uno no sabe si ese es en verdad el causante del problema de performance. El problema básicamente es que hay mucha gente tratando de resolver problemas de performance a ciegas. Si uno no sabe donde realmente esta el problema, cual es el causante del problema de performance va a ser muy difícil arreglar eso, o obtener buenas ganancias de performance. Yo lo que hago para optimizar es seguir seis pasos que en general me sirven muchísimo como para arreglar los problemas.

1. Vista preliminar del problema.
Lo primero que hay que hacer es hacer una vista preliminar del problema. ¿Cuál es el problema principal? ¿Cuanto más rápido tendría que ser el programa si no tuviera problemas de performance? Estas simples preguntas te permiten determinar bien que es lo que queres mejorar, te da las primeras ideas sobre cual es potencialmente le problema. Uno podría por ejemplo empezar a sospechar que la performance esta delimitada por el GPU, por el CPU, o lo que sea. Pero esto ya te da las primeras ideas.

2. Identificar los cuellos de botella.
Esto es una de las partes más importantes. Uno tiene que determinar cuales son los cuellos de botella. Lo que queremos hacer es mirar diferentes contadores de performance para determinar bien cuales son los puntos calientes donde se pierde la mayor parte del tiempo en el proceso. Por ejemplo, yo uso el Performance Analysis de Compuware DevPartner o el Intel VTune para ver los contadores generales. Mira el uso del CPU, el uso de memoria, y uso del disco (y dependiendo sobre lo que estas trabajando, puede ser bueno ver el uso del GPU o por ejemplo el uso del networking). Una vez que tenes estos números ya podes empezar a ver donde están los problemas principales. Hay que tener cuidado con las diferencias que hay entre la conclusión del paso uno y el paso dos. Cuando hay diferencias entre los dos es posible que te estés olvidando de algún factor muy importante por lo que puede ser necesario volver al paso uno y analizar mejor el tema.

3. Análisis de el/los cuellos de botella.
Acá se presenta un problema si no hiciste el paso uno y dos. Si no hiciste los pasos anteriores es muy probable empieces a analizar intensivamente algo que en verdad no es causante del cuello de botella. Uno puede esta sumamente concentrado sobre el uso del CPU cuando en verdad el problema esta en el uso del disco, el GPU, o lo que sea. Entonces para evitar perdidas de tiempo, hace los pasos uno y dos, son muy importantes. Ahora que ya sabes a que tenes que darle importancia es hora de hacer un análisis mas intensivo al cuello de botella. Ayudándote con las herramientas usadas anteriormente más un debugger uno ya puede empezar a detectar función por función y línea por línea adonde están los problemas más grandes.

4. Hipótesis y verificación de hipótesis.
En este punto uno ya tiene una buena idea sobre cual es la raíz de todo el problema. Pero para estar seguro lo mejor es hacer una hipótesis y verificar esa hipótesis. Por ejemplo si yo a esta altura me doy cuenta que me lector de shaders es muy lento yo podría hacer una hipótesis por ejemplo “Si yo en vez de tener 10 shaders tengo 100 con 1000 instrucciones cada uno entonces el problema tiene que ser mucho peor”. Entonces cuando verificas tu hipótesis podes ver si en verdad determinaste bien el problema o si tenes que volver al paso tres. Si no haces la verificación es posible que pierdas mucho tiempo en el futuro tratando de mejorar un cuello de botella que en verdad no existe.

5. Arreglar el cuello de botella
Acá esta el paso que generalmente la gente hace equivocadamente como primer y único paso al momento de intentar mejorar la performance. Uno puede perder mucho tiempo cambiando estructuras, expandiendo bucles y haciendo cosas así cuando en verdad uno podría determinar por el paso dos que, por ejemplo, en verdad el problema estaba en el tiempo de lectura en el disco. Pero si hiciste correctamente los pasos anteriores, en este momento podes encarar con bastante seguridad los problemas a resolver. Hace los cambios necesarios para mejorar la performance. Si podes hacerlo paso a paso y verificando cada paso es mejor para evitar romper todo.

6. Testear.
Ahora es momento de volver a sacar las herramientas que usaste en el paso dos y ver si en verdad arreglaste el cuello de botella. Si lo hiciste correctamente y ya no hay mas un cuello de botella, felicitaciones. Pero si no es así volve tantos pasos atrás como sea necesario, puede ser posible que tengas que volver al paso cinco o puede ser que tengas que volver al paso uno.

Esos fueron todos los pasos que yo seguí para por ejemplo obtener una ganancia del 20% en mi mesh loador. Acá esta el screenshot del antes y el después.

Antes.

Despues.

Espero que esto les sirva para usar mas efectivamente el tiempo, y para poder arreglar los problemas de performance lo mejor posible. Cualquier comentario no duden en usar el click en Comment.

Octubre 9, 2005

Bienvenidos.

Archivado en: General — Pablo Zurita @ 3:53 pm

Bienvenidos a la nueva versión de mi blog. La decisión de actualizar el blog y cambiarlo fue más que nada causa del PHP-Nuke. Ese sistema es simplemente demasiado grande para un blog, tiene varios problemas de seguridad bastante importante, y simplemente era una tortura manejar todo eso a pesar que actualizaba el blog una vez cada par de meses. Así que acá esta la nueva versión del blog, espero que les guste.

« Entradas anteriores

Gestionado con WordPress