Inicializando Direct3D – Parte 1/2

Publicado el Martes 20 julio 2010 en DirectX por Thund

¡Por fin, con todos ustedes… el más esperado, el desconocido, el novedoso… DirectX 11! Estoy de acuerdo, vaya chorrada de presentación, pero tenía que hacerlo, lo juro. El presente post y muchos de los sucesivos estarán centrados en Direct3D y DXGI, con pinceladas de DirectWrite o Direct2D, y en ellos trataré de explicar cómo están estructurados, cómo interactúan y cómo se utilizan para distintos propósitos, intercalando teoría y práctica para hacerlo más llevadero y comprensible. Para no complicarlo mucho, el código presentado será el básico e indispensable (en el próximo post ya le añadiré comprobaciones para hacerlo más robusto). Empecemos pues con un corto recorrido alrededor de los principales componentes de Direct3D y los cambios que ésta API ha sufrido respecto a DirectX 9. Como muchos recordaréis, lo primero que se hacía, a parte de la creación de la ventana, era llamar a la función Direct3DCreate9 a la que pasábamos la versión del SDK para obtener un puntero a una interfaz IDirect3D9. Esta última se encargaba de crear el manejador del dispositivo gráfico (IDirect3DDevice9) y la swap chain, y nos surtía de una serie de funciones para comprobar las posibilidades que el hardware ofrecía, de cara a dicha creación. Pues bien, este diseño se ha ido al carajo. Ya no existe una interfaz de entrada a la librería y toda la responsabilidad de comprobación de hardware ha sido desplazada a la API de DXGI. La swap chain ha dejado de estar medio oculta al usuario (tenías que ir específicamente a buscarla para darte cuenta de que estaba ahí) y pasa a primer plano bajo control de interfaces IDXGISwapChain. Como se puede comprobar por el prefijo, también ha pasado a dominio de DXGI. La interfaz del dispositivo (ahora ID3D11Device) se genera gracias a la función global D3D11CreateDevice y ya no se ocupa del envío de instrucciones de dibujo, o al menos no directamente, sino que ha sido dividida en 2 partes: el dispositivo y el contexto del dispositivo (ID3D11DeviceContext); mientras que el primero representa el adaptador en sí y actúa como fábrica de ciertos objetos, el segundo maneja las órdenes de renderizado. Como colofón, hace falta crear, al menos, un render target que apunte al back buffer de nuestra swap chain, y que usaremos para indicarle al device context dónde tiene que dibujar. Sí, Microsoft ha complicado un pelín las cosas. Vamos a verlo en mayor detalle, que así parece muy enrevesado.

 

DirectX Graphics Infrastructure

Nació con DirectX 10 para, según Microsoft, agrupar aquellas porciones de la API gráfica encargadas de realizar operaciones a bajo nivel en una librería independiente. Tales tareas son, por ejemplo, la enumeración de los dispositivos gráficos, el envío de los frames renderizados a los dispositivos de salida (presentación), manejo de transiciones de pantalla completa, etc. Ciertamente, DXGI ha cambiado poco desde que se lanzó, lo único que han hecho ha sido añadir funciones y unas cuantas chorradas* más con la versión 1.1, que aquí ni veremos ni usaremos. Identificaremos fácilmente estas extensiones, ya que los chicos de Redmond han tomado la genial decisión de diseño de añadir un "1" como sufijo, y deberemos tener cuidado de no mezclarla con la versión 1.0, esto es, no se deben usar interfaces o funciones de una y a continuación invocar las de la otra, de lo contrario vendrá Steve Ballmer y os gritará "Developers!" en un oído. Sí bueno, es la típica historia de terror que se cuenta a los niños cuando no se tiene ni puta idea de qué es lo que pasa, como el coco o el efecto invernadero, y con eso hay que conformarse :) . A continuación el conocido esquema sobre DXGI, por cortesía (o no) de Microsoft:

[Diagrama DXGI]

ID3D11Device

Realiza las mismas funciones que su antecesor, menos las relativas al renderizado, como Clear, Draw, Begin, End… Básicamente, es una fábrica de objetos o recursos asociados al dispositivo. Pueden generarse recursos compartidos entre varios dispositivos, pero sólo los podrá usar aquél que los creó. Echando un vistazo a su interfaz os daréis cuenta de que ya no están disponibles los métodos para la creación de vertex buffers e index buffers; de hecho, sí que están, lo que ocurre es que se han unificado los buffers en una única interfaz llamada ID3D11Buffer, que encapsula un objeto cuya funcionalidad puede configurarse en el momento de su creación, de la que se encarga el método CreateBuffer, para que se comporte como index, vertex o constant buffer. Un dispositivo puede dividirse internamente en 2 capas: la capa principal o núcleo (Core Layer), que existe siempre por defecto, y la capa de depuración (Debug Layer), que puede activarse mediante el flag D3D11_CREATE_DEVICE_DEBUG, cuando creamos el manejador del dispositivo. Dependiendo de cómo tengamos configurado el panel de control de DirectX, la capa de depuración nos mostrará una serie de mensajes muy últiles en la ventana de Output de Visual Studio. Cabe destacar que la interfaz ID3D11Device es free-threaded (o thread-safe, como más os guste), lo que significa que se puede llamar a sus métodos desde cualquier hilo simultáneamente (el número de concurrencias dependerá del driver). Al igual que en anteriores versiones, podemos crear un dispositivo de referencia que emulará mediante software todas las características de DirectX. Adicionalmente, existe otro tipo de dispositivo llamado WARP (Windows Advanced Rasterization Platform), que también implementa mediante software (con mucho mejor rendimiento, según tengo entendido) las capacidades de Direct3D 11. Para ver las limitaciones de ambos emuladores echad un ojo a la documentación.

ID3D11DeviceContext

La interfaz del dispositivo antes mencionada ha sido dividida en 2 partes, de manera que la responsabilidad del renderizado ahora recae en los llamados "contextos del dispositivo". Los contextos son creados por el propio dispositivo y pueden ser de 2 tipos: inmediatos y postergados (es mi traducción de deferred). El concepto de "contexto" se refiere al modo de tratar las órdenes de renderizado y cambios de estado que se envían al dispositivo. Mientras que en un contexto inmediato los comandos son transmitidos directamente al dispositivo, en un contexto postergado se genera una cola de comandos que serán ejecutados más tarde utilizando el contexto inmediato. Esto permite utilizar una arquitectura multihilo para renderizar en paralelo aunque, al final, acabe realizándose en serie. Sólo puede existir un contexto de tipo inmediato por dispositivo, e infinitos contextos postergados. Para saber a qué tipo pertenece un determinado contexto, podemos usar el método GetType. Al contrario que la interfaz ID3D11Device, ID3D11DeviceContext no es thread-safe, sólo puede usarse cada instancia desde un único hilo simultáneamente.

IDXGISwapChain

Anteriormente, para acceder a la cadena de intercambio de buffers de salida (swap chain en adelante), teníamos que utilizar el método GetSwapChain de la interfaz IDirect3DDevice9, obteniendo así un puntero a la interfaz IDirect3DSwapChain. Su existencia pasaba inadvertida, aunque la utilizáramos sin saberlo al suministrar la estructura D3DPRESENT_PARAMETERS (cuyo equivalente ahora es DXGI_SWAP_CHAIN_DESC) o al llamar al método Present. Con el diseño actual, la swap chain pasa a un discreto primer plano y no sólo eso sino que, como mencioné en varias ocasiones, las interfaces relacionadas con la misma han sido movidas a la librería de DXGI.

ID3D11RenderTargetView

Antes de explicar el significado de ésta interfaz en concreto, creo que es necesario dar a conocer algunos detalles acerca de la estructura de DirectX 11. Existen los llamados "recursos", que no son más que zonas de memoria con datos que son accesibles desde la pipeline de Direct3D. Tales recursos, encapsulados en derivadas de ID3D11Resource, son generados por la interfaz del dispositivo y normalmente contienen datos "en bruto", en forma de buffers (ID3D11Buffer), o texels, formando texturas (ID3D11Texture1D, ID3D11Texture2D ó ID3D11Texture3D); hay varios tipos de recurso más o, mejor dicho, subtipos, que han sido añadidos con la versión 5.0 de los shaders, tales como StructuredBuffer, que no describiré hasta que lo necesitemos para algo en el futuro. La cuestión es que, para manipular tales datos de memoria, necesitamos una "vista", que actúa como canal de acceso; en función del tipo de recurso, tendremos que elegir un tipo de vista u otro, esto es, una u otra interfaz derivada de ID3D11View. ¿Que por qué me pongo a hablar de esto aquí en vez de crear una sección de introducción para los recursos, otra para las vistas, etc.? Pues porque para fusilar y traducir la documentación de referencia ya tenéis la MSDN y el infalible traductor online de Microsoft. Volviendo a lo que nos ocupa, uno de los tipos de vista que nos permiten dibujar sobre una textura (nuestro back buffer es precisamente eso, una textura 2D) es el representado por ID3D11RenderTargetView. Para crearla hay que usar el método CreateRenderTargetView del dispositivo, al cual hay que suministrar la textura (nuestro back buffer, por ejemplo, obtenido mediante el método GetBuffer de la interfaz de la swap chain) y, opcionalmente, una estructura de configuración. Las vistas son objetos pasivos, tan sólo tienen un método o propiedad que nos devuelve sus descripciones. Veremos más tipos de vista en breve, cuando hable del depth y el stencil buffer.

Sé que el principio del post prometía sangre, pero dado que soy un cenutrio incapaz de condensar tanta información para hacer posts agradables y ligeros, me veo obligado a partirlo en 2. No tardaré mucho en publicar la segunda parte, que es la que contiene el ejemplo de código, que nadie se impaciente.

 

*Para los puristas, cuando digo "chorradas" quiero decir "funcionalidad de uso poco frecuente o de propósito muy específico", aunque suponga una puta revolución tecnológica.

Etiquetas: ,

2 Respuestas a 'Inicializando Direct3D – Parte 1/2'

Suscríbete a los comentarios con RSS o TrackBack a 'Inicializando Direct3D – Parte 1/2'.

  1. mijgan dijo:

    I would like to exchange links with your site endlessgame.elalumbramiento.org
    Is this possible?

    on agosto 13th, 2010 at 21:33

  2. Thund dijo:

    Hi mijgan, that’s perfectly possible. What is / will be your blog about?

    on agosto 14th, 2010 at 19:33

Publica un comentario