OpenGL ed il Framework .NET: un approccio diverso

4 gennaio 2013 | Categorie: Framework .NET, Informatica, OpenGL, Programmazione | Tag:

L’ostinazione di Microsoft a non sviluppare OpenGL per i propri sistemi operativi e di conseguenza dotare il Framework .NET di un supporto nativo a OpenGL, è giustificabile da un punto di vista commerciale, ma per i programmatori come me è diventato sempre più una forte limitazione nello sviluppo di applicazioni OpenGL anche in ambiente .NET. Per questo motivo, fin dalle prime versioni di .NET, come decine di altri programmatori entusiasti di OpenGL e del Framework di Microsoft, hanno cercato di mettere a punto i vari wrapper, più o meno funzionali, allo scopo di continuare ad utilizzare OpenGL anche con i liguaggi .NET. Devo ammettere che molti di questi hanno raggiunto un grado elevato di maturità e sono impiegati con successo in molti progetti.

Anche io, dopo aver realizzato l’ennesimo wrapper, mi sono trovato a dover decidere se utilizzarlo in un  nuovo progetto per una applicazione grafica 3D. Credevo che la soluzione del mio wrapper potesse andare bene, rispetto all’uso del 3D di WPF e rimanendo fedele a OpenGL rispetto all’uso di DirectX.
Invece, un improvviso ed opportuno ripensamento, mi ha fatto vedere la cosa da un altro punto di vista, banale finché vogliamo, ma che non avevo mai considerato (forse per una mia eccessiva ottusità o ignoranza).

L’idea è basata su alcuni presupposti:

  • abbandonare l’idea di un wrapper OpenGL;
  • sfruttare al massimo le capacità hardware di OpenGL fornite dai driver delle schede video, ma senza rinunciare alla possibilità di usare ancora la vecchia implementazione legacy via software (OpenGL 1.1);
  • utilizzare gli shader, quando disponibili;
  • implementare solo le funzionalità che servono veramente al progetto, mantenendo comunque aperta la strada per future nuove integrazioni;
  • integrare OpenGL nel Framework .NET senza utilizzare i servizi Interop per l’incorporazione di finestre Win32 dentro al sistema grafico WPF (evitare l’effetto “airspace violation”);
  • lasciare tutte le dipendenze dall’implementazione di OpenGL in ambito non gestito, come ad esempio l’intero ciclo di vita del Rendering Context OpenGL.

Da questi presupposti, è nata la convinzione di sviluppare una libreria di funzioni non gestite (classica DLL) scritta in linguaggio C, che si occupi di tutta la gestione e l’utilizzo delle sole funzioni di OpenGL in base alle reali esigenze dell’applicazione in via di sviluppo, senza realizzare una libreria ad uso generico.
L’unione con il mondo gestito del Framework .NET avviene tramite l’invocazione delle funzioni della libreria non gestita con i delegati di importazione.
Tutto qua! Estremamente banale, semplice e funzionale!

Questo approccio mi ha dato l’opportunità di scrivere codice ottimizzato per quanto richiesto, ma la sua semplicità d’implementazione è stata solo “apparente”. Quando si usa OpenGL si deve fare i conti con lo strato di software da scrivere per la creazione del Rendering Context, dipendente dal sistema operativo in esecuzione (ad esempio, le librerie GLUT offrono da molto tempo tale supporto per le varie piattaforme presenti sul mercato).
In Microsoft Windows ci sono le funzione WGL, oltre alle classiche GDI Win32. Ma la gestione corretta del ciclo di vita di un Rendering Context è pieno di difficoltà, soprattutto quando la visualizzazione non può essere fatta direttamente utilizzando una finestra Win32 (ricordate l’obiettivo finale? la scena deve essere visualizzata in ambito WPF) e si vuole utilizzare lo shader e tecniche di render offscreen.
La scelta è ricaduta nell’implementare tutta le gestione del Rendering Context e delle chiamate alle funzioni OpenGL utilizzando un thread separato per ogni Rendering Context. In questo modo non esiste la necessità di fare dei context switch su più Rendering Context ovvero per impostare quello attivo in un certo momento (uso di wglMakeCurrent, ecc.). Una volta creato un Rendering Context esso rimarrà attivo all’interno del suo thread fino alla sua distruzione. Questo permette anche di gestire la memoria necessaria in ogni Rendering Context utilizzando un heap esclusivo creato nel proprio thread.

C’è molto altro ancora da descrivere… ma per ora mi fermo qua.

2 trackbacks

  1. eduardo Trackback | 2014/08/13
  2. max Trackback | 2014/11/14