New XNA 4 book by Kurt Jaegers [Packt Publishing]

Kurt Jaegers has a new book on XNA 4 Game Development. I´ll review it in a few days, by now, I paste here some word from the author itself:

“This book follows the same style as my previous books on 2D game development with XNA, bringing three different 3D games to life. I cover items such as:
- The basic concepts behind 3D graphics and game design
- Generating geometry with triangles
- Converting height map images into terrain
- An introduction to HLSL, including writing shaders that handle lighting and multi-texturing
- Building a 2D button-based interface to overlay on your 3D action
- Implementing skyboxes for full 3D backgrounds”

More info here and here.

Memory limits in a .Net process

This article tries to be an introduction on .Net memory management and about the memory limits both the Runtime and the platform establish for each process. We will also give some tips about dealing with the problems you will face when reaching those limits.

Available memory for a process

As you already know, no matter how much physical memory you install in a computer. Your application will face several issues that will limit the actual memory available for it.
For instance, a 32 bit system cannot have more than 4 GB of physical memory. Needless to say that 2^32 will give you a virtual address space with 4.294.967.296 different entries, and that’s precisely where the 4GB limit comes from. But even having those 4GB available on the system, your application will actually be able to see 2GB only. Why?
Because on 32 bits systems, Windows splits the virtual address space into two equal parts: one for User Mode applications, and another one for the Kernel (system applications). This behavior can be overridden by using the “/3gb” flag in the Windows boot.ini config file. If we do so, the system will then reserve 3GB for user applications, and 1 GB for the kernel.
However, that won’t change the fact that we will be able to see only 2GB from our application, unless we explicitly activate another flag in the application image header: IMAGE_FILE_LARGE_ADDRESS_AWARE. The combination of both flags on a 32bit Operating System is commonly known as: 4GT (4 GigaByte Tuning).
Surprisingly on 64 bit environments, the issue is pretty similar. Even though these systems don’t suffer from the same limitations about physical memory or reserved address space for the kernel (in fact, in those systems the /3gb flag doesn’t apply), processes hit with the same wall when trying to address more than 2 GB. Unless the same flag is set for the executable (IMAGE_FILE_LARGE_ADDRESS_AWARE), the limit will be always the same by default.

Activating the flag: IMAGE_FILE_LARGE_ADDRESS_AWARE

  • In native, Visual C++ application, it’s pretty straightforward to set that flag, as Visual Studio have an option for that. You just need to set the /LARGEADDRESSAWARE Linker parameter, and you are ready to go.
  • In C#, .Net applications:
  1. Applications compiled as 64bit will have that flag set by default, so you will already have access to a 8TB address space (depending on O.S. versions)
  2. Applications compiled as 32bits will need to be modified with the tool called EditBin.exe (distributed with Visual Studio). This tool will set the appropriate flag to your EXE, allowing your application to access a 4GB address space if running in a 64bit Windows, or to a 3GB address space if running in a 32bit Windows with the 4GT tuning enabled.
Next table (taken from here), summarizes the limits in virtual address space, depending on the platform and on the kind of process we are running:
image
This page has much more info on the issue.

System memory limits. Closer than you expect

Nowadays, memory is cheap. However, as explained in the previous chapter, there are many situations where you will end up having only 2 GB available, despite the total amount of physical memory installed in your PC.
In addition to that, if your application is being developed in .Net, you will find that the Runtime itself introduces a remarkable memory overhead (around 600-800 MB). So, it’s not strange to start receiving OutOfMemory exceptions when reaching 1.2 or 1.3 GB of memory used. This blog talks further about this.
So, if you are not in one of those cases, where the address space is expanded beyond 2 GB, and your are developing in .Net, your actual memory limit will be around 1.3 GB.
That’s more than enough for 99% of applications, but others, like intensive computing apps or those related to databases, may need more. Way more…

And things get even worse…

To make things even more complicated, you will soon learn that one thing is having some amount of memory available, and another, completely different story is to find a contiguous block of memory available.
As you all know, as a result of O.S. memory management, techniques like Paging and the creation and destruction of objects, memory gets more and more fragmented. That means that even though there is a certain amount of free memory, it is scattered through a bunch of small holes, instead of having a single, big chunk of memory available.
Modern Operating Systems and the .Net platform itself apply methodologies to prevent fragmentation, like the so called Compaction (moving objects in memory to fuse several free chunks of memory into a single, bigger one). Although these techniques reduce the impact of fragmentation, they do not eliminate it completely. This article describes in detail the .Net Garbage Collector (GC) memory management, and the compaction task it performs.
In the context of this article, fragmentation is a big issue, because if you need to allocate an 10 MB contiguous array, even if there’s 1 GB of free memory available for your process, you will receive an OutOfMemory exception if the system cannot find a contiguous chunk of memory for the array. And this happens more frequently than you may expect when you deal with big arrays.
In .Net, fragmentation and compaction of objects is tightly related to object’s size, so let’s talk a bit about that too:

Allocation of big objects

Maybe you don’t know it, but all versions of .Net until the last one (1.0, 2.0, 3.0, 3.5 and 4.0) have a limit on the maximum size a single object can have: 2 GB. No matter if you are running in a 64bit or 32bit process, you cannot create anything bigger than that, in a single object. It’s only since version 4.5 when that limit has been removed (for 64 bit processes only). However, besides very few exceptions, you are very likely applying a wrong design pattern to your application if you need to create such a big objects.
In the .Net world, the GC classifies objects into two categories: small, and large objects. Where you expecting something more technical? Yeah, me too… But that’s it. Any object smaller than 85000 bytes is considered small, and any object larger than that is considered large. When the CLR is loaded, the Heap assigned for the application is divided into two parts: the SOH (Small Objects Heap) and the LOH (Large Objects Heap). Each kind of object is stored on it’s correspondent Heap.
It’s also remarkable to say that Large object’s compaction is very expensive, so it’s directly not done in current versions of .Net (developers said that this situation might change in the future). The only operation similar to compaction done with Large objects is that two adjacent dead objects are fused together into a single chunk of free memory, but no Large object is currently moved to reduce fragmentation.
This fantastic article has much more information about the LOH.

C# Arrays when reaching memory limits

Simple Arrays (or 1D arrays) are one of the most common ways of consuming memory in C#. As you probably know, the CLR always allocates them as single, contiguous blocks of memory. In other words, when we instantiate an object of type byte[1024], we are requesting 1024 bytes of contiguous memory, and you will get an OutOfMemory exception if the system cannot find any chunk of contiguous, free memory with that size.
When dealing with multi-dimensional arrays, C# offers different approaches:

Jagged arrays, or arrays of arrays: [][]

Declared as byte[][], this is the classical solution to implement multi-dimensional arrays. In fact, it’s the only approach natively supported in languages like C++.
With regards to memory allocation, they behave as a simple array of elements (one block of memory), where each one of them is another array (another, different block of memory). Therefore, an array like byte[1024][1024] will involve the allocation of 1024 blocks of 1024 bytes memory each.

Multi-Dimensional Arrays: [,]

C# introduces a new kind of arrays: multi-dimensional arrays, declared like byte[,].
Although they are very comfortable to use and easy to instantiate, they behave completely different with regards to memory allocation, as they are allocated in the Heap as a single block of memory, for the total size of the array. In the previous example, an array like byte[1024, 1024] will involve the allocation of one single, contiguous block of 1 MB.
In the next chapter we will make a quick comparison of both types of arrays:

Comparison: [,] vs [][]

2D array [,] (allocated as a single block of memory):
Pros:
  • Consumes less memory (no need to store references to all N blocks of memory)
  • Faster allocation (allocating a bigger, single block of memory is faster than allocating N, smaller blocks)
  • Easier instancing (enough with one single line: new byte[128, 128])
  • Useful tool methods, like GetLength(). Cleaner and easier usage.
Cons:
  • Finding a single block of contiguous memory for them might be a problem, specially if dealing with big arrays, or when reaching memory limits for your process
  • Accessing elements in the array is slower than in jagged arrays (see below)
Jagged arrays [][] (allocated as N blocks of memory):
Pros:
  • It’s easier to find available memory for this kind of arrays, because due to fragmentation, it’s more likely that there will be N blocks of smaller size available than a single, contiguous block of the full size of the array.
  • Accessing elements in the array is faster than in 2D arrays, mostly because the optimizations in the compiler for handling simple, 1D arrays (after all, a jagged array is composed of several 1D arrays).
Cons:
  • Consumes a bit more memory than 2D arrays (need to store references to the N simple arrays).
  • Allocation is slower, as it needs to allocate N elements instead of a single block
  • Instancing is uncomfortable, as you need to loop through array elements to instantiate them too (see below for tip)
  • Doesn’t provide with tool methods, and might be a bit more complex to read and understand
This blog have a great comparison about them too.

Conclusion

Each user should decide which kind of array fits best the specific case he is dealing with. However, a developer that usually needs big amounts of memory, and who cares more about performance than comfort, ease of use or readability, will probably decide to use Jagged arrays ([][]).

Tip: code to automatically instantiate a 2D, jagged array

Instancing a multi-dimensional jagged array can be disturbing, and repetitive. This generic method will do the work for you:
        public static T[][] AllocateArray2D<T>(int pWidth, int pHeight)            
        {
            T[][] ret = new T[pWidth][];
            for (int i = 0; i < pHeight; i++)
                ret[i] = new T[pHeight];

            return ret;
        }
Hope it helps !!

Los límites de la memoria

Este artículo trata de servir como introducción a la gestión de memoria en .Net, los límites que el Runtime y la plataforma establecen para cada proceso, así como algunos Tips para lidiar con los problemas a los que nos enfrentamos al acercarnos a esos límites.

Memoria disponible por proceso

Como muchos de vosotros sabéis, por mucha memoria RAM que tenga instalada un ordenador, existen varias barreras impuestas a la cantidad de memoria usable en nuestras aplicaciones.

Por ejemplo, en un sistema de 32 bits no se pueden instalar más de 4GB de memoria física, evidentemente, porque 2^32 (dos elevado a 32) nos proporciona un espacio de direcciones con 4.294.967.296 entradas distintas (4GB). Pero incluso cuando el sistema cuente con 4GB de memoria física, nuestras aplicaciones se encontrarán con una barrera de 2GB impuesta por el sistema.

En estos entornos de 32 bits, cada proceso puede acceder a un espacio de direcciones de 2GB como máximo, porque el sistema se reserva los otros 2 para las aplicaciones que corren en modo Kernel (aplicaciones del sistema). Este comportamiento por defecto puede cambiarse mediante el uso del flag “/3gb” en el boot.ini del sistema, haciendo que Windows reserve 3GB para las aplicaciones que corren en Modo Usuario y 1GB de memoria para el Kernel.

Aún así, el límite por proceso permanecerá en 2GB, a no ser que explícitamente activemos un flag determinado (IMAGE_FILE_LARGE_ADDRESS_AWARE) en la cabecera de la aplicación. A esta combinación de flags en sistemas x86 se le denomina comúnmente: 4GT (4 GigaByte Tuning).

En sistemas de 64 bits sucede algo parecido. Aunque no tienen la misma limitación en cuanto a memoria física disponible, ni la impuesta por la reserva de direcciones para el kernel (y por lo tanto el flag /3gb no aplica en estos casos), el sistema también establece un límite por defecto de 2 GB para cada proceso, a no ser que se active el mismo flag en la cabecera de la aplicación (IMAGE_FILE_LARGE_ADDRESS_AWARE).

Activando el flag: IMAGE_FILE_LARGE_ADDRESS_AWARE
  • En el caso de aplicaciones nativas (C++), establecer dicho flag es fácil, ya que basta con añadir el parámetro /LARGEADDRESSAWARE a los parámetros del Linker dentro de Visual Studio.
  • En el caso de aplicaciones .Net:
    1. Si están compiladas para 64bits, este flag estará activado por defecto, por lo que podrán acceder a un espacio de direcciones de 8 TB (dependiendo del S.O.)
    2. Si están compiladas para 32bits, el entorno de Visual Studio no nos ofrece ninguna opción para activar dicho flag, por lo que tendremos que hacerlo con la utilidad EditBin.exe, distribuida con Visual Studio, la cual modificará el ejecutable de nuestra aplicación (activándole dicho flag).

La siguiente tabla, obtenida de esta página, muestra de forma resumida los límites en el espacio de direcciones de la memoria virtual, en función de la plataforma y del tipo de aplicación que estemos desarrollando:

image

Esta página tiene mucha más información sobre los límites de memoria según las versiones del S.O.

Los límites del sistema, más cerca de lo que crees

Hoy día, la memoria es barata, pero como ya se ha explicado en el apartado anterior, hay un buen número de casos en los que, por mucha memoria que instalemos en el PC, nuestro proceso solo podrá acceder a 2GB de la misma.

Además de esto, si vuestra aplicación está desarrollada en .Net, os encontraréis con que el propio Runtime introduce un overhead importante en cuestiones de memoria (suele decirse que está en torno a los 600-800 MB), por lo que en una aplicación corriente, es usual empezar a encontrar OutOfMemoryExceptions alrededor de los 1.3 GB de memoria usados. En este blog se discute el tema.

Por lo tanto, si no estamos en uno de esos casos en los que podemos direccionar más de 2GB, y además desarrollamos en .Net, independientemente de la memoria física instalada en el sistema nuestro límite real estará en torno a 1.3 GB de memoria RAM.

Para el 99% de las aplicaciones diarias, es más que suficiente, pero otras que requieren cálculos masivos, o que se relacionan con bases de datos, muy frecuentemente superarán ese límite.

Y lo que es peor…

Para complicar todavía más el asunto, una cosa es tener memoria disponible, y otra muy distinta es tener bloques de memoria contiguos disponibles.

Como todos sabéis, fruto de la gestión que el Sistema Operativo hace de la memoria, de técnicas como la Paginación, y de la creación y destrucción de objetos, la memoria poco a poco va quedando fragmentada. Esto quiere decir que, aunque tengamos suficiente memoria disponible, esta puede estar dividida en muchos bloques pequeños, en lugar de un único hueco con todo el tamaño disponible.

Los Sistemas Operativos modernos, y la propia plataforma .Net, tratan de evitar esto con técnicas de Compactación, y aunque reducen notablemente el problema, no lo eliminan por completo. Este completo artículo describe en detalle la gestión de memoria del Garbage Collector de .Net, y la labor de compactación que realiza.

¿En qué afecta la fragmentación? En mucho, ya que si vuestra aplicación necesita reservar un Array contiguo de 10 MB, y aunque todavía haya 1GB de memoria disponible, si la memoria está muy fragmentada y el sistema no es capaz de encontrar un bloque contiguo de ese tamaño, obtendremos un OutOfMemoryException.

En .Net, la fragmentación y compactación de objetos en memoria guarda una estrecha relación con el tamaño de éstos. Por eso, el siguiente apartado hablará un poco sobre este tema.

Grandes objetos en memoria

A la hora de reservar memoria para un único objeto, la plataforma .Net establece ciertos límites. Por ejemplo, en las versiones de .Net 1.0, 2.0, 3.0, 3.5 y 4.0, ese límite es de 2GB. Tanto para plataformas x86 como x64, ningún objeto único puede ser mayor de ese tamaño. Es así de simple. Únicamente a partir de .Net 4.5 este límite puede ser excedido (en procesos x64 exclusivamente). Aunque sinceramente, salvo rarísimas excepciones, si necesitas reservar más de 2GB de memoria para un único objeto, quizá deberías replantearte el diseño de tu aplicación.

En el mundo .Net, el Garbage Collector clasifica a los objetos en dos tipos: objetos grandes y objetos pequeños. Es una división bastante gruesa, la verdad, pero es así. ¿Qué considera .Net como un objeto pequeño? Todo aquel que ocupe menos de 85000 bytes.

Cuando el CLR de .Net es cargado, se reservan dos porciones de memoria diferentes: un Heap para los objetos pequeños (también llamado SOH, o Small Objects Heap), y otra para los objetos grandes (también llamado LOH, o Large Object Heap), y cada tipo de objeto se almacena en su Heap correspondiente.

¿En qué afecta todo esto al tema que estamos tratando? Sencillo, compactar objetos grandes es costoso, y a día de hoy, simplemente no se hace. Los objetos considerados “Grandes”, y que se introducen en el LOH, no se compactan (aunque el equipo de desarrollo advierte que pueden hacerlo algún día). Como mucho, cuando dos objetos grandes adyacentes son liberados, se fusionan en un único espacio de memoria disponible, pero ningún objeto es “movido” para realizar tareas de compactación.

Este fantástico artículo contiene muchísima más información acerca del LOH y su funcionamiento.

Arrays C# en los límites de la memoria

En C#, los Arrays Simples (de una dimensión) son una de las formas más comunes de consumir memoria, y debes saber que el CLR los reserva siempre como bloques continuos de memoria. Es decir, cuando instanciamos un objeto de tipo byte[1024], estamos solicitando al sistema un único bloque continuo de 1KB, y se generará un OutOfMemoryException si no encuentra ningún hueco contiguo de ese tamaño.

Cuando es necesario utilizar un Array de más de una dimensión, C# nos ofrece distintas opciones:

Arrays anidados, o arrays de arrays

Declarados como byte[][], suponen el método clásico de implementar arrays multi-dimensionales. De hecho, en lenguages como C++, es el único tipo de array multi-dimensional soportado de forma nativa.

En lo relativo a memoria, se comportan como un array simple (un único bloque de memoria), en el que cada elemento es otro array simple (esta vez del tipo declarado, y que también es un bloque único en memoria, pero distinto a los demás). Por lo tanto, en lo que a bloques de memoria se refiere, un array de tipo byte[1024][1024], utilizará 1024 bloques de memoria distintos (cada uno de 1024 bytes).

Arrays Multi-Dimensionales

C# introduce un nuevo tipo de Arrays, soportado de forma nativa: los arrays multi-dimensionales. En el caso de 2 dimensiones, se declaran como byte[,].

Aunque son muy cómodos de utilizar (disponen entre otras cosas de métodos como GetLength, para saber el tamaño de una dimensión), y su instanciación es más sencilla, su representación en memoria es diferente a la de los arrays anidados. Éstos se almacenan como un único bloque de memoria, del tamaño total del array.

En el siguiente apartado estableceremos una comparativa entre ambos tipos:

Comparativa: [,] vs [][]

El array 2D [,] (se almacena en un solo bloque):

Ventajas:

  • Utiliza menos memoria total (no tiene que almacenar las referencias a los n arrays simples)
  • Su creación es más rápida: reservar un bloque grande de memoria para para un solo objeto es más rápido que reservar bloques más pequeños para muchos objetos.
  • Su instanciación es más sencilla: una sola línea basta (new byte[128,128]).
  • Proporciona métodos útiles, como GetLength, y su uso es más claro y limpio.

Inconvenientes:

  • Encontrar un solo bloque de memoria continuo para el array puede ser un problema, si éste es muy grande o nos encontramos cerca del limite de RAM.
  • El acceso a los elementos del array es más lento que en arrays anidados (ver abajo)

El array anidado [][] (que se almacena en N bloques):

Ventajas:

  • Es más fácil encontrar memoria disponible para el array, ya que requiere de n bloques de tamaño más pequeño, lo cual debido a la fragmentación, suele ser más probable que encontrar un único bloque más grande.
  • El acceso a los elementos del array es más rápido que en los arrays 2D, gracias a las optimizaciones del compilador para manejar arrays simples (en definitiva, un array de arrays se compone de muchos arrays 1D).

Inconvenientes:

  • Utiliza más memoria total (tiene que almacenar las referencias a los n arrays simples)
  • Su creación es más lenta, ya que hay que reservar N bloques de memoria, en lugar de uno solo.
  • Su instanciación es un poco más molesta, ya que hay que recorrer el array instanciando cada uno de sus elementos (ver Tip más abajo).
  • No proporciona los métodos disponibles en los arrays 2D, y su uso puede ser un poco más confuso.

Este blog explica muy bien esta comparativa.

Conclusión

Cada usuario debe escoger el tipo de array que más le convenga en función de su experiencia y el contexto concreto en el que esté. No obstante, un desarrollador que habitualmente utilice gran cantidad de memoria, y preocupado por el rendimiento, tenderá a escoger siempre arrays anidados (o arrays de arrays [][]).

Tip: código generico para instanciar arrays anidados

Dado que instanciar un array de arrays es un poco molesto y repetitivo (y ya dijimos aqui que no conviene duplicar código), el siguiente método genérico se encargará de esa tarea por vosotros:

        public static T[][] Allocate2DArray<T>(int pWidth, int pHeight)            
        {
            T[][] ret = new T[pWidth][];
            for (int i = 0; i < pHeight; i++)
                ret[i] = new T[pHeight];

            return ret;
        }

Espero que os Sirva !!!

Boot time comparison: All generations of iPhone vs Nokia Lumia 800

This video completes the boot time comparison made by iClarified with an additional contender: the Nokia Lumia 800 with Windows Phone 7.5, which beats even the very last iPhone 5 in terms of boot time.

 

Please note: This video is a second version of another one published 3 days ago. It has been remade to fix a small fps conversion error present in the previous version (the iPhone part was treated as if it was 30 fps, when it was 24 fps). That involved it being displayed faster than it should, and therefore showing a wrong boot time measurement for the Lumia (which was displayed correctly, at 30 fps). Nokia Lumia 800 boots in 18 seconds, not 22.

Results

The boot time results are awesome:

          • Nokia Lumia 800: 18.17 secs
          • iPhone 5: 24.88 secs (6.5 seconds slower !!)

I ♥ Nokia        -        I ♥ Windows Phone

Cine y Series Españolas –un desastre muy evitable-

En este post voy a tratar de explicar por qué no me gustan las series Españolas, salvo contadísimas excepciones. Dejando a un margen las comedias facilonas y absurdas (donde no hace falta esmerarse mucho en ningún aspecto, solo hacer reír), y excepciones como “Cuéntame”, que no lo hace tan mal en muchos de los aspectos que comentaremos aquí, las demás son un puñetero desastre. Por muchos motivos.

Empezamos…

1 - Temática

En primer lugar, la originalidad de la temática es, generalmente, nula. Cuando no aprovechan vergonzosamente el tirón de tragedias como la de “Niños Robados”, se limitan a plagiar lo que se hace en USA (tanto en el cine como en la televisión). Este magnífico post deja bien claro de qué hablo. Su autor ha hecho un trabajo genial que me voy a permitir fusilar parcialmente y completar aquí:

Downton Abbey y Gran Hotel

"Downton Abbey" –> "Gran Hotel"

Cheers vs Cheers española

"Cheers" –> "Cheers"

Las chicas de oro originales y las españolas

"Las chicas de oro" –>"Las chicas de oro"

Pirtas

Piratas del Caribe –> "Piratas"… Hasta el cartel y el logo son plagios…

Series policiacas

"CSI", "El mentalista" –> "Génesis: en la mente del asesino" u "Homicidios"

image

"Falcon Crest" –> "Gran reserva"

Los protegidos = Héroes + Los Serrano

"Héroes" –> "Los protegidos"

Ángel o Demonio vs Crepúsculo

"Crepúsculo" –> "Ángel o demonio"

Doctor Mateo vs Doctor en Alaska vs House

"House" + "Doctor en Alaska" = "Doctor Mateo"

"Urgencias" –> "Hospital Central"

image

"Prison Break" –> "La fuga"

image

"Perdidos" + "Waterworld" = "El barco".

Roma vs Hispania

"Roma" –> "Hispania"

image

"Los Borgia" –> “Los Borgia”

image

"Los Tudor" –> "Toledo"

13313_340970587478_5031791_n[1]

Assasin’s Creed –> Águila Roja

image

Mr. Selfridge –> Galerías Velvet

image

Revenge –> Sin identidad

En fin, ¿algo que añadir? Yo me quedo sin palabras al ver esto…

2 – Actores y guionistas

En cuanto a series británicas y americanas, muchas veces las veo en versión original. Pero incluso cuando no es así y a la interpretación del actor le han calzado por encima la voz de otro, incluso en esos casos suelo creerme lo que veo. Por lo general, los actores americanos y británicos se meten muchísimo más en el papel. Para ellos, lo primero es que la interpretación resulte creíble, por encima de su imagen. Estudian el personaje, la forma de hablar de la época, la forma de moverse y expresarse. Se asesoran, y se concentran plenamente en ello. Los actores españoles, salvo excepciones, dejan mucho que desear en todos estos aspectos. Y eso resta realismo.

Si a eso le sumamos que los guiones muchas veces son de risa, pues poco podemos hacer. Cuando veo algún episodio de series como Aguila Roja y compañía, me muero de risa al ver cómo hablan los personajes. Parece que están en el “insti”, colega…

Señores, es muy poco probable que en la antigua Roma, o incluso en el siglo XVII, se hablara así. Y eso resta realismo.

3.- Estética

Yo entiendo que un actor vive de su imagen, y que le joda que le saquen feo. Pero señores, esos peinados seguro que no existían hace 600 años. Esa ropita inmaculada, esas manos de manicura y ese cutis impoluto (con una media barba cuidada para engañar), ese cuero recién salido de la tienda. Esos tejidos modernos que no encajan hace 2000 años. Es que no tienen ni una mancha, oigan… Y eso resta realismo.

Mientras que en EEUU han evolucionado de esto:

Robin-Hood-Errol-Flynn-002

a esto:

En España seguimos igual que estaban ellos hace 50 años. ¿Os fijáis en la imagen de Russel Crowe? Pelo desaliñado, sudado y sangrando, con las manos llenas de mierda y la ropa rota y sucia.

 

Casi casi igual que en Águila Roja:

Camisa blanca inmaculada, manicura perfecta (leñe, hasta hoy en día la gente tiene más roña en la uñas), capa de tejido industrial que SEGURO no existía en el XVII… En fin, que parece que va disfrazado de carnavales. Y eso resta realismo.

4 - Luz, color y formato

Todo lo mencionado en el apartado 3 sería disimulable si las productoras españolas eligieran mejor los formatos y se aplicaran un poquito en la etapa de post-producción.

Entiendo que no tenga sentido en “Los Serrano” o en “La que se avecina”, pero si quieres rodar una serie de aspecto cinematográfico como “Hispania” u otras, por el amor de Dios, rueda SIEMPRE en 16:9 o mejor aún, en 21:9.

Y lo que es aún más importante: la luz y el color. Es necesario trabajar muchísimo la iluminación. Y esto es, muchas veces, bastante asequible, de hecho. Solo hay que empeñarse, y tener un mínimo de conocimientos.

Un ejemplo: el siguiente video muestra parte de los decorados de Rivendell, en El Señor de Los Anillos. Como está grabado in-situ, con una cámara normal y sin post-producción, podréis apreciar que, de hecho, son bastante cutres. Se nota a la legua que son de cartón piedra o de papel pintado, muy al estilo de las series españolas. Entonces, qué marca la diferencia?

La luz. El color. La ambientación. Los planos. La post-producción. Como veis, no se trata de construir un castillo o un palacio de verdad, piedra a piedra. Y para muestra, un botón:

5.- Ejemplo

Vamos a coger todos los aspectos mencionados en los anteriores apartados, y vamos a aplicarlos a un fotograma de Águila Roja. En concreto a éste:

Es un plano triste, en el que el muchacho parte cabizbajo con un saco al hombro, con sus pertenencias. El formato es correcto (16:9), pero sin embargo, la ambientación, la luz y el color escogidos para el rodaje denotan todo lo contrario a lo que se pretende: alegría, verano, naturaleza, limpieza, aire fresco.

imageSi os fijáis en alguien que sabe hacer las cosas, como Peter Jackson… ¿En qué planos de El Señor de los Anillos utilizó este tipo de luz? ¿Acaso cuando Frodo partía de casa de Bilbo en medio del drama que se le venía encima? No. La utilizó en los primeros planos, en los que se muestra la comarca como un lugar idílico. Cuando todavía no hay problema alguno, y todo va bien. Con una música jovial y risueña, con todos los personajes pasándolo bien y sonriendo. Ahí sí encaja esa puesta en escena (ver a la derecha). Pero no aquí. Han escogido una configuración que no ayuda absolutamente nada a transmitir el dramatismo del momento. En definitiva, están restando credibilidad. Por muy bien que lo haga el chaval. Por muy bien que actúe. No hay nada que hacer, la escena está perdida solo por no tener cuidado con éstas cosas.

En este caso, lo ideal hubiera sido rodar en un día nublado, o mejor aún, con una lluvia que aumentara el dramatismo. Pero dado que ya no es posible cambiar eso, nos fijaremos en lo demás: queremos transmitir tristeza, frialdad. Por lo que habrá que usar colores mucho más azules y fríos, como éstos:

image

Además, añadiremos algo que debería estar presente en TODOS los planos de un rodaje que pretende aparentar ser del siglo XVII: imperfecciones. Las imperfecciones deben estar por todas partes (peinados, ropa, ambientación), y añadirlas a los fotogramas como un proceso de post-producción también ayuda. Sin estar reñido con rodar en FullHD, o resolución 4K, si la imagen está un poquito sucia, un pelín “vieja”, ayudar a transmitir la sensación adecuada. Por eso, añadiremos un par de filtros para acrecentar este efecto:

image

También vamos a romperle un poco la ropa y ensuciarlo, que todos los días veo niños que salen del colegio más sucios que eso… Si la comparas, verás que esta imagen es muchísimo más triste que la inicial (incluida de nuevo abajo):

image

El original:

6 - Otro ejemplo

Con las caras pasa más de lo mismo. No importa cómo de bueno sea el actor, si la ambientación y el maquillaje no ayudan, no tiene nada que hacer. Lo siento, pero Francis Lorenzo, por muy buen actor que sea, no tiene una cara extremadamente dramática.

El peinado tampoco ayuda. Ni el maquillaje. Ni la ausencia total de manchas e imperfecciones. Nuevamente, ¿qué buscan los productores? ¿Que el señor Lorenzo salga fantástico, o que su serie resulte creíble?

Para saber cómo dotar a una cara de más personalidad, o de más fuerza, solo hay que ver este vídeo:

Conclusión

La HBO y la NBC están demostrando que se puede hacer series realmente alucinantes, con calidad cinematográfica, y que se puede ganar mucho dinero con ello. No hay más que ver Juego de Tronos, Érase una vez, o la inminente Revolution:

No creo que en España no haya actores o guionistas de calidad. Si se empeñan, saben hacerlo. Pero hay que escoger al actor adecuado, escoger un guión adecuado, y olvidarse de salir guapos en la foto. Un guerrero romano era un bárbaro, por definición, y seguro que iba muy sucio. Además, Llongeras todavía no había abierto sucursal en Roma, así que el pelo se lo cortarían a machete… Si cuidamos esas cosas, nos exigimos el máximo, y trabajamos bien la post-producción, sin duda que en España podremos hacer cosas como éstas:

image

O los alucinantes efectos especiales de la segunda temporada:

Creo sinceramente que los productores harían bien en tratar de hacer un único buen proyecto, que les reporte muchos millones, que empeñarse en hacer muchas pequeñas versiones abaratadas, plagiadas y cutres de lo que hacen los demás. Se gastan millonadas para obtener un rédito muy dudoso, haciendo un producto que podría mejorarse notablemente simplemente con poner un poquito de atención.

Una pena…

Properly calculating the diffuse contribution of lights in HLSL Shaders

It’s been many years since Vertex and Pixel Shaders came out, and several years too since the Fixed Pipeline is deprecated, but there are still many questions in the forums out there asking about how to properly calculate the diffuse contribution of Lights. This paper has a great tutorial about the issue, and includes a whole Shader that mimics the Fixed Pipeline behavior. However, we will see here how to perform just the basic calculations, just in case you don’t need to emulate the full pipeline.
First thing is to write some D3D9 code that allows you to switch from the old Fixed Pipeline and your own Shaders, using the same parameters. Doing so, you will easily find any behavior differences in light calculations. You can read more about how D3D9 Fixed Pipeline calculates lighting in this page.
When writing shaders, people tend to calculate the diffuse contribution like:
Out.Color = (materialAmbient * lightAmbient) + (materialDiffuse * lightDiffuse * dot(Normal, L));
Where L is the vector from the vertex position (in world coordinates) to the light.
Apart from not doing any specular or emissive calculations (which could not be necessary in many cases, depending on your scenario), there are several mistakes in that approach:
1.- You don’t want the dot to return negative values, because it will black out colors wrongly. So, you need to clamp it to the 0..1 range, using the saturate operator: saturate(dot(Normal, L))
2.- In order to get the same results as the Fixed Pipeline, you should include Attenuation calculations, because they modify the intensity of light with the distance between the point being lit and the light source. Attenuation (as opposed to what its name suggests), not only attenuates light, but also can increase intensity in some circumstances. (See below how to properly calculate attenuation factors)
3.- Once you are calculating attenuation, you should remove the materialDiffuse factor from the previous equation, as you don’t want it to be attenuated too. You will apply it later, when the entire lighting contribution is properly calculated and attenuated.
Keeping those 3 things in mind, the final calculation in a vertex shader would be:
    float4 LightContrib = (0.f, 0.f, 0.f, 0.f);
    float fAtten = 1.f;

    // 1.- First, we store the total ambient light in the scene (multiplication of material_ambient, light_ambient, and any other global ambient component)
    Out.Color = mMaterialAmbient * mLightAmbient;

    // 2.- Calculate vector from point to Light (both normalized and not-normalized versions, as we might need to calculate its length later)
    float pointToLightDif = mLightPos - P;
    float3 pointToLightNormalized = normalize(pointToLightDif);
    
    // 3.- Calculate dot product between world_normal and pointToLightNormalized
    float NDotL = dot(Nw, pointToLightNormalized);        
    if(NDotL > 0)
    {
        LightContrib = mLightDiffuse * NDotL * mLightDivider;     
            
        float LD = length(pointToLightDif);        
        if(LD > mLightRange)
            fAtten = 0.f;
        else
            fAtten = 1.f/(mLightAtt0 + mLightAtt1*LD + mLightAtt2*LD*LD);
        
        LightContrib *= fAtten;
    }
    Out.Color += LightContrib * mMaterialColor;
    Out.Color = saturate(Out.Color);

 

Comparison

First image is the Programmable version. You can slightly tell it by the reflections on the windows.
image
Second image is the Fixed Pipeline version (no real time reflections on windows):
image

Simax Simulator to be used by the DGT as a diagnostic tool for brain damage

The DGT (Dirección General de Tráfico www.dgt.es), the Spanish organization in charge of traffic legislation and the Imserso (www.imserso.es), the Spanish Institute for the third age and social services, are using one of our Simax Simulators to test the ability to drive on people suffering from some kind of brain damage. The simulator allows them to safely drive through a real environment and to interact with other drivers, while the system automatically monitors and evaluates the way they drive, presenting a complete report with possible failures. Then, experts process that information, along with the results of other tests, to determine if the driver should be suggested to continue driving or not.

More info at: www.simaxvirt.com

[Please navigate to minute 36 to access to the info]

 

Simulador Simax como herramienta de diagnóstico para personas con daño cerebral (minuto 36)

La DGT (Dirección General de Tráfico), así como el Imserso (Instituto de Mayores y Servicios Sociales), están utilizando uno de nuestros simuladores Simax para evaluar la habilidad para conducir en personas que han sufrido algún tipo de daño cerebral. El simulador permite a dichas personas conducir de forma totalmente segura por un entorno real, e interactuar con otros conductores, mientras evalúa y monitoriza automáticamente el modo en que conducen, para presentar finalmente un completo informe con los posibles fallos cometidos durante el proceso. Entonces, expertos en la materia analizan dichos resultados, conjuntamente con los de otros tests realizados, para determinar si es aconsejable que esa persona siga conduciendo o no.

Más información en: www.simaxvirt.com

Cómo controlar el orden de propiedades o categorías en un PropertyGrid

El control PropertyGrid es fantástico para crear herramientas de prototipado rápido, donde podamos cambiar propiedades de objetos de forma rápida y visual. Como ya sabrás, el espacio de nombres System.ComponentModel contiene multitud de atributos y herramientas para personalizar el modo en que las propiedades se agrupan y configuran dentro de un PropertyGrid.

De forma automática, las propiedades se ordenan alfabéticamente según su DisplayName, o se agrupan por categorías (y se aplica el mismo criterio alfabético dentro de éstas) si así lo selecciona el usuario. Lamentablemente, no existe una forma sencilla de poder controlar manualmente el orden de las propiedades o de las categorías.

Existen muchas formas distintas de lograrlo, pero casi todas implican escribir código. Un workaround sencillo, efectivo, y que no implica utilizar código adicional es el siguiente:

1.- Dentro del atributo DisplayName de cada propiedad, o dentro del nombre de cada categoría (atributo Category),  añadiremos por delante tantos caracteres especiales de tipo \u200B como posiciones queramos “subir” dicha propiedad o categoría hacia arriba. Dicho carácter identifica un espacio vacío de longitud 0, por lo que en la práctica no modificará el texto que se muestra en la propiedad, pero sí afectará al algoritmo de ordenación.

En el siguiente ejemplo, se muestra un objeto con dos propiedades Width y Height. De forma natural (por orden alfabético), Height aparecería antes que Width. Para modificar ese comportamiento y lograr el orden inverso, mucho más natural, solo tendremos que modificar los atributos como sigue:

        [Category("Layout")]
        [DisplayName("\u200B\u200BWidth")]
        public float Width
        {
            get { return mWidth; }
            set { mWidth = value; }
        }
        [Category("Layout")]
        [DisplayName("\u200BHeight")]
        public float Height
        {
            get { return mHeight; }
            set { mHeight = value; }
        }

Así, logramos un PropertyGrid correctamente ordenado, como el de la siguiente ilustración:

image

2.- Debemos asegurarnos de que el PropertyGrid utiliza una fuente que soporte dicho carácter, ya que no todas lo hacen. Por ejemplo, la fuente por defecto Microsoft Sans Serif 8.25 lo soporta perfectamente. No obstante, si queréis aseguraros de forma programática de que la fuente es correcta, podéis utilizar este código:

        public UIEditor()
        {
            InitializeComponent();

            this.propertyGrid1.Font = new Font("Microsoft Sans Serif", 8.25f, FontStyle.Regular);
        }

La importancia de la codificación binaria de Shaders en DirectX o Silverlight

Si alguna vez te has topado con un Vertex o Pixel Shader que al menos en apariencia es correcto, pero que sin embargo produce errores al compilar, ten en cuenta que la codificación utilizada para salvar el texto afecta.

Como ya sabrás, por mucho que un archivo contenga texto, en el disco duro de tu ordenador se almacena como datos binarios. Y para ello, es necesario escoger uno de los muchos métodos existentes para transformar el texto a binario, y vice-versa.

Si abrimos un archivo de texto con una herramienta de análisis Hexadecimal, como HxD, podremos observar que los primeros bytes del mismo determinan su codificación. Por ejemplo, la siguiente ilustración muestra un fichero con la cabecera EF BB BF, que determina que el fichero utiliza codificación UTF-8 (la codificación por defecto en Visual Studio).

image

Podéis encontrar más información sobre cabeceras de archivos de texto aqui.

Lamentablemente, el compilador de Shaders de DirectX solo admite determinados tipos de codificación, y UTF-8 no está entre ellos. Por eso, por mucho que el código de ese shader sea correcto, si tratamos de compilarlo recibiremos el siguiente error (u otros, dependiendo del entorno en el que nos encontremos):

“X3000: Illegal character in shader file“

Si esto sucede, solo tenemos que cambiar la codificación con la que se salva el archivo a disco, utilizando una sencilla opción de Visual Studio (Archivo->Opciones avanzadas de Salvado):

image

Aqui, podremos escoger qué codificación utilizar para guardar el archivo. Por ejemplo, podemos escoger “Western European (Windows) – Codepage 1252”, que es una codificación ASCII simple, para que el compilador de shaders funcione correctamente:

image

Mas info:

http://blog.pixelingene.com/2008/07/file-encodings-matter-when-writing-pixel-shaders/

http://www.cplotts.com/2008/08/22/encodings-matter-with-fx-files/

Conduce un simulador Simax Bentley Continental GT V8

Como parte de la promoción del Campeonato del Mundo FIA GT1 que tendrá lugar la semana que viene en el Circuito de Navarra, estos días tenéis la oportunidad de rodar virtualmente con un Bentley Continental GT V8 a los mandos de un simulador Simax. En el Parque Comercial Galaria (c.c. La Morea) - Pamplona -

WP_000252

Bentley Continental GT V8 Simulator, by Simax

Ayer lanzamos un video sobre el último producto Simax: el simulador del nuevo Bentley Continental GT V8, utilizado por la marca en la premiere mundial para prensa especializada este año. Tuvo gran aceptación entre gente como Tiff Needell (Fifth Gear), Frank Marcus (MotorTrend USA) o Guy Smith (ganador de LeMans 2003). Este último estuvo casi 2 horas conduciendo en el simulador, bajando tiempos vuelta tras vuelta. Más info en: www.simaxvirt.com

--

Yesterdary, we released a video about the latest Simax product: the Bentley Continental GT V8 simulator, which was used by the brand in the world press premiere last February. People like Tiff Needell (Fifth Gear), Frank Marcus (MotorTrend USA) or Guy Smith (2003 LeMans winner) had the chance to try it. Mr. Smith was driving for almost 2 hours in the sim, getting better and better times each lap. More info at: www.simaxvirt.com

Developing a MatrixStack in pure managed C# code (ready for XNA)

Some time ago, we already talked about the possibility of creating your own Math library directly in C#, with no native code. If you take enough care, it can be as fast as performing interop with a native one.
Today, we are showing an additional example on this matter, and we are going to develop our own fast MatrixStack class, all in safe C# code, with no COM interop.

Why?

I never understood well why the MatrixStack class remains to be an iDisposable COM object. Don´t know what kind of optimizations it has internally that justify having disposable resources, but it’s annoying to have the iDisposable overhead with no need for it.
Besides that, MatrixStacks are used in most cases as simple matrix helpers, to traverse object hierarchies. So, replacing the API MatrixStack with your own one should be a piece of cake, and will definitely help you if trying to port your code to some other platform.
Last, but not least, XNA does not have a MatrixStack class. So this C# implementation fits perfectly on it for all that want to use it.
I this example, I will be comparing my own class with the SlimDX MatrixStack, which is nothing more than a wrapper over the D3DX Matrix Stack.

The interface

In order to make the SlimDX stack replacement painless, I will keep the exact same interface in my class (except the COM-related stuff, which is no longer necessary). So, it will have to be something like this:
image

How it works

A MatrixStack, basically supplies a mechanism to enable matrices to be pushed onto and popped off of a matrix stack. Implementing a matrix stack is an efficient way to track matrices while traversing a transform hierarchy.
So, we can clear the stack to the Identity or to any other matrix, we can operate with the top of the stack, and we can add (push) or remove (pop) nodes (or levels, if you want) to the stack.
Example: for a robot arm hierarchy, we would go like this:

1.- Initialize the stack, and load the matrix of the first node in the hierarchy (the upper arm, for example). Now you can use the Top matrix to draw the upper arm.
2.- Create another level on the stack (Push) for the lower arm, and multiply the lower arm matrix. Use the Top matrix to draw the lower arm.
3.- Create another level on the stack (Push) for the hand, and multiply the hand matrix. Use the Top matrix to draw the hand.
The stack itself does nothing you cannot do with regular Matrix multiplications, except that it keeps track of the previous levels you have been creating. So you can go back to the upper node whenever you want. After the previous operations, for instance, if we perform a Pop, we would remove the top node of the stack, and go back to the previous. This way, the new Top node would represent the lower arm matrix, instead of the hand matrix.

The code

Here is my implementation of the MatrixStack. Please keep in mind that it has not been intensively tested, and might contain errors. Use it at your own risk:
    public class MatrixStack
    {
        /// <summary>
        /// Retrieves the Top node matrix of the stack
        /// </summary>
        public Matrix Top = Matrix.Identity;        
        public object Tag = null;
        private List<Matrix> mStack = new List<Matrix>();
        
        /// <summary>
        ///
        /// </summary>
        public MatrixStack()
        {
            LoadIdentity();
        }
        /// <summary>
        /// Clears the stack and loads the Identity Matrix in the top of the stack
        /// </summary>
        public void LoadIdentity()
        {
            mStack.Clear();
            Top = Matrix.Identity;
        }
        /// <summary>
        /// Clears the Stack, and loads the matrix in the top of the stack
        /// </summary>
        /// <param name="pMat"></param>
        public void LoadMatrix(Matrix pMat)
        {
            mStack.Clear();
            Top = pMat;
        }
        /// <summary>
        /// Adds a new level to the stack, cloning the current TOP matrix of the stack
        /// </summary>
        public void Push()
        {
            mStack.Add(Top);
        }
        /// <summary>
        /// Removes the current TOP matrix of the stacks, returning back to the previous one
        /// </summary>
        public void Pop()
        {
            if (mStack.Count > 0)
            {
                Top = mStack[mStack.Count - 1];
                mStack.RemoveAt(mStack.Count - 1);                
            }
        }
        /// <summary>
        /// This method right-multiplies the given matrix to the current matrix (transformation is about the current world origin).
        /// This method does not add an item to the stack, it replaces the current matrix with the product of the current matrix and the given matrix.
        /// </summary>
        /// <param name="pMat"></param>
        public void MultiplyMatrix(Matrix pMat)
        {
            Matrix.Multiply(ref Top, ref pMat, out Top);
        }
        /// <summary>
        /// This method left-multiplies the given matrix to the current matrix (transformation is about the local origin of the object).
        /// This method does not add an item to the stack, it replaces the current matrix with the product of the given matrix and the current matrix.
        /// </summary>
        /// <param name="pMat"></param>
        public void MultiplyMatrixLocal(Matrix pMat)
        {
            Matrix.Multiply(ref pMat, ref Top, out Top);            
        }      
        /// <summary>
        /// Rotates (relative to world coordinate space) around an arbitrary axis.
        /// </summary>
        public void RotateAxis(Vector3 pAxis, float pAngle)
        {
            Matrix tmp;
            Matrix.RotationAxisAngle(ref pAxis, pAngle, out tmp);
            Matrix.Multiply(ref Top, ref tmp, out Top);           
        }
        /// <summary>
        /// Rotates (relative to world coordinate space) around an arbitrary axis.
        /// </summary>
        public void RotateAxisLocal(Vector3 pAxis, float pAngle)
        {
            Matrix tmp;
            Matrix.RotationAxisAngle(ref pAxis, pAngle, out tmp);
            Matrix.Multiply(ref tmp, ref Top, out Top);           
        }
        /// <summary>
        /// Rotates (relative to world coordinate space) the specified Euler Angles
        /// </summary>
        public void RotateYawPitchRoll(float pYaw, float pPitch, float pRoll)
        {
            Matrix tmp;
            Matrix.CreateFromYawPitchRoll(pYaw, pPitch, pRoll, out tmp);
            Matrix.Multiply(ref Top, ref tmp, out Top);            
        }
        /// <summary>
        /// Rotates (relative to world coordinate space) the specified Euler Angles
        /// </summary>
        public void RotateYawPitchRollLocal(float pYaw, float pPitch, float pRoll)
        {
            Matrix tmp;
            Matrix.CreateFromYawPitchRoll(pYaw, pPitch, pRoll, out tmp);
            Matrix.Multiply(ref tmp, ref Top, out Top);           
        }
        /// <summary>
        /// Scale the current matrix about the world coordinate origin
        /// </summary>
        public void Scale(float pX, float pY, float pZ)
        {
            Matrix tmp;
            Matrix.CreateScale(pX, pY, pZ, out tmp);
            Matrix.Multiply(ref Top, ref tmp, out Top);
        }
        /// <summary>
        /// Scale the current matrix about the world coordinate origin
        /// </summary>
        public void ScaleLocal(float pX, float pY, float pZ)
        {
            Matrix tmp;
            Matrix.CreateScale(pX, pY, pZ, out tmp);
            Matrix.Multiply(ref tmp, ref Top, out Top);           
        }
        /// <summary>
        /// Determines the product of the current matrix and the computed translation matrix determined by the given factors (x, y, and z).
        /// </summary>
        public void Translate(float pX, float pY, float pZ)
        {
            Matrix tmp;
            Matrix.CreateTranslation(pX, pY, pZ, out tmp);
            Matrix.Multiply(ref Top, ref tmp, out Top);           
        }
        /// <summary>
        /// Determines the product of the current matrix and the computed translation matrix determined by the given factors (x, y, and z).
        /// </summary>
        public void TranslateLocal(float pX, float pY, float pZ)
        {
            Matrix tmp;
            Matrix.CreateTranslation(pX, pY, pZ, out tmp);
            Matrix.Multiply(ref tmp, ref Top, out Top);
        }
    }

It has to be fast

When you start coding your own MatrixStack, you will soon realize that .Net includes a Generic Collection called Stack. You can use it, although I didn’t. Why?
Because I have separated the management of the Top Matrix of the stack to a member variable, and for the rest I just preferred to use a simple list to keep track of the previous nodes.
The Top Matrix is stored as a member variable to be able to pass it By Reference to the Matrix Multiplication methods. The speed increase avoiding to pass a whole matrix by value is significant. In the example below, it was around a 40% faster.

Test 1 – Reliability

I just made several random operations with the matrix stack, trying to test some of its features by comparing the end Top Matrix, both with a SlimDX MatrixStack and my own. The test operations are:
matrixStack.LoadIdentity();
matrixStack.MultiplyMatrix(Matrix.PerspectiveFovLH(0.8f, 1.6f, 0.1f, 999f));
matrixStack.Translate(10, 10, 10);
matrixStack.Scale(2, 2, 2);
matrixStack.RotateYawPitchRoll(1f, 0f, 0f);
matrixStack.RotateAxis(Vector3.UnitY, 0.75f);
matrixStack.Push();
matrixStack.TranslateLocal(-5, -5, -5);
matrixStack.ScaleLocal(0.1f, 0.1f, 0.1f);
matrixStack.Pop();
matrixStack.MultiplyMatrixLocal(Matrix.RotationZ(1.45f));
The resulting top matrix is:
SlimDX MatrixStack:
  1. [M11:-0.06350367 M12:4.695973 M13:-0.3505643 M14:0]
  2. [M21:0.5231493 M22:0.5700315 M23:2.887983 M24:0]
  3. [M31:18.08297 M32:20 M33:-23.60117 M34:1]
  4. [M41:-0.1968169 M42:0 M43:0.03565279 M44:0]
MyMatrixStack:
  1. {M11:-0.06350368 M12:4.695973 M13:-0.3505643 M14:0}
  2. {M21:0.5231493 M22:0.5700315 M23:2.887982 M24:0}
  3. {M31:18.08297 M32:20 M33:-23.60117 M34:1}
  4. {M41:-0.1968169 M42:0 M43:0.0356528 M44:0}
As you can see, the result is exactly the same.

Test 2 - Speed

Speed is important, so I decided to run the above mentioned operation 10 million times, to se how long it takes to complete both using SlimDX and my own code.
Obviously, if we run in Debug mode (disabling optimizations), there will be a huge performance difference, as the SlimDX dll is already compiled with optimizations. But what happens if we turn all optimizations on when compiling our code?
Here is the result of a small test application:
image
As you can see, the .Net Framework alone is faster than SlimDX, thanks to its optimizations and to the absence of the interop layer.
What happens if we increase the number of iterations to 60 million? The difference is obviously bigger (1.36 seconds faster):
image
Note: This test has been done on an intel i7 CPU at 3.8 Ghz, running on Windows 7 x64 with .Net Framework 4.0.
Note2: SlimDX MatrixStack uses its own Matrix class and operations. My implementation uses my own Matrix implementation, also written in pure C# code.
Conclusion: .Net Rocks. A purely native C++ code would be even faster of course, but if you put in the equation the huge amount of benefits .Net will give you, I really think it’s worth it. Don’t you think?
Cheers !