OpenGL.NET Framework

Aggiorna:29 luglio 2010

Introduzione

OpenGL.NET Framework è un wrapper per la libreria grafica OpenGL (dalla versione 1.1 alla versione 3.0), da utilizzare nelle applicazioni gestite nei sistemi operativi Microsoft Windows che supportano il Framework .NET 2.0 o versione superiore.

Come è noto, esistono molti wrapper per utilizzare OpenGL in .NET liberamente scaricabili dalla rete Internet, ma questa libreria vuol essere una valida alternativa. Nella sua progettazione, si è tenuto conto di molti dettagli che solitamente sono trascurati o parzialmente gestiti dagli altri wrapper. Con questo non significa che questa è la soluzione migliore ma resta in ogni caso un’implementazione alternativa da prendere in seria considerazione per lo sviluppo di applicazioni professionali.

Si precisa inoltre che questo primo rilascio della libreria fornisce solamente le basi essenziali per poter utilizzare OpenGL in .NET. E’ in corso lo sviluppo di altre classi per rendere questo wrapper un vero e proprio Framework orientato agli oggetti allo scopo di integrare al meglio OpenGL nella programmazione di applicazioni grafiche con .NET.

Come sanno tutti i programmatori che scrivono software per OpenGL, la questione centrale per utilizzare OpenGL nei sistemi operativi Microsoft Windows si traduce nella corretta creazione e gestione di un Rendering Context, ovvero della creazione di un handle specifico del sistema operativo che, una volta reso corrente, permette al programmatore di chiamare le funzioni OpenGL. Fare tutto questo è apparentemente semplice, ma, di fatto, non lo è. Infatti, le cose si complicano perché l’implementazione di OpenGL varia da computer a computer, in funzione della scheda grafica (dal processore grafico o GPU) e dall’implementazione fornita dal produttore della scheda stessa. L’implementazione comune a tutti i sistemi operativi Windows è quella fornita da Microsoft, purtroppo ferma (per ragioni strategiche) ancora alla versione 1.1, comunemente detta implementazione generica. Tra le altre cose l’implementazione generica è realizzata via software e quindi non sfrutta l’eventuale implementazione di OpenGL in hardware, cioè non sfrutta l’accelerazione grafica fornita dalle moderne GPU. Inoltre, a complicare le cose, anche le specifiche di OpenGL sono in continuo sviluppo e cambiamento (questo è un fatto positivo) e normalmente le aggiunte delle funzionalità di OpenGL sono implementate prima con il meccanismo delle estensioni e poi a distanza di tempo (e non sempre) recepite nei rilasci ufficiali delle versioni dai vari produttori di schede grafiche accelerate.

Un altro aspetto molto importante sul quale esistono delle difficoltà oggettive di implementazione è quello legato alla natura non gestita della libreria OpenGL, molto dipendente dal funzionamento del sistema operativo e dalle implementazioni fornite con i driver dai produttori delle schede grafiche. Questo aspetto rende l’integrazione con il Framework .NET difficoltoso e limitante.

Tra le limitazioni più importanti c’è sicuramente la mancanza di una completa integrazione della gestione delle risorse non gestite creabili con OpenGL (ad esempio le texture), in particolare l’aspetto relativo al rilascio automatico delle stesse al termine dell’applicazione, voluta o improvvisa. La scelta progettuale (sofferta) adottata in questa implementazione della libreria è stata per il rilascio esplicito delle risorse OpenGL, a cura del programmatore, facendo uso del consueto pattern derivato dall’interfaccia standard IDisposable.

Un altro problema apparentemente non risolvibile completamente è la gestione della superficie di rendering in ambito WPF (tecnologia introdotta a partire dalla versione 3.0 del Framework .NET), intesa come il controllo utente dove sono visualizzate le scene OpenGL. Dato il forte legame di OpenGL con il sistema operativo Windows (rimasto inalterato dal primo rilascio di Windows NT 4), il render sullo schermo è possibile solamente utilizzando una finestra dotata di handle che in .NET si traduce nei controlli WinForm. Ma con l’avvento di WPF questo fatto è divenuto un limite che, come soluzione tampone, ha quella dell’utilizzo del meccanismo di integrazione previsto da WPF, che permette di utilizzare oggetti finestra nativi di Windows all’interno dei controlli visuali WPF. Tale integrazione però ha degli effetti indesiderati, tra i più noti e discussi sono quelli legati alla cosiddetta “violazione dello spazio aereo”. Comunque è in corso di studio e sviluppo per questa libreria, una tecnica che dovrebbe superare tale limitazione in WPF, almeno a livello di controllo utente, evitando così di utilizzare per forza una finestra nativa di Windows all’interno di applicazioni per WPF a scapito però di una riduzione complessiva delle performance lato visualizzazione utente.

Un’altra cosa importante da sottolineare, soprattutto con riferimento ad altri wrapper esistenti, è il fatto che ogni Rendering Context OpenGL caratterizza e stabilisce implicitamente quali comandi ed estensioni di OpenGL sono effettivamente disponibili al programmatore. Quindi si è ritenuto corretto in questa implementazione, considerare il fatto che ogni contesto deve possedere la propria lista dei metodi effettivamente implementati e quindi utilizzabili dal programmatore. Tale gestione tuttavia comporta un leggero sovraccarico in termini di memoria, dato che stiamo parlando di oltre duecento metodi per ogni contesto creato (ed in crescita continua ad ogni rilascio di una nuova versione di OpenGL), ma ininfluente in termini di performance. Negli altri wrapper, in generale, non si tiene conto di questo.

Descrizione in generale

L’architettura della libreria è stata pensata e realizzata basandosi su alcuni punti chiave:

  • La creazione di un Rendering Context è fatta esternamente ed indipendentemente dall’oggetto wrapper del contesto utilizzato per il rendering.
  • Ogni contesto ha i propri metodi di accesso alle funzioni OpenGL ed estensioni.
  • La gestione dei contesti creati è gestita centralmente, così come il passaggio da un contesto ad un altro.
  • Esiste sempre un contesto valido e attivo, sul quale hanno effetto i comandi.
  • Il rilascio delle risorse OpenGL è esplicita, a cura e responsabilità del programmatore.

La classe che incapsula il Rendering Context OpenGL è la classe OpenGLContext che non è comunque instanziabile direttamente, ma solo indirettamente, tramite la relativa classe fabbrica OpenGLContextFactory attraverso i vari metodi Create. Ogni metodo Create crea effettivamente un contesto di tipo e di formato in base ai parametri passati. E’ possibile creare contesti per il rendering in memoria (offscreen render) o in una finestra Windows (un qualsiasi controllo WinForm dotato di handle finestra). E’ possibile, ma non obbligatorio, specificare il formato del pixel desiderato attraverso un’istanza alla classe OpenGLFormat. Non è detto che dopo la creazione l’effettivo formato sia lo stesso di quello richiesto.

Solo a creazione avvenuta, il programmatore potrà utilizzare le funzioni OpenGL, GLU e WGL, e le altre estensioni, attraverso il meccanismo dei metodi delegati. Ogni contesto creato avrà tutti i metodi delegati alcuni valorizzati e altri posti a null, rispettivamente se la corrispondente funzione è supportata dal relativo Rendering Context oppure no.

L’invocazione del metodo delegato sarà comunque indiretto, tramite le classi statiche GL, Glu e Wgl, che espongono tutti i metodi il cui effetto dell’invocazione del comando andrà a riferirsi al contesto correntemente attivo al momento della chiamata. Il nome dei metodi nelle classi GL, Glu e Wgl è pressoché lo stesso dei rispettivi comandi nativi, tolti rispettivamente i prefissi “gl”, “glu” e “wgl”.

Il concetto di contesto corrente è di fondamentale importanza ed è basato sul fatto che OpenGL esegue i comandi richiesti solo se esiste un Rendering Context attivo nel thread corrente, con il limite che in un thread può esistere solo un contesto corrente alla volta. Nella libreria questo meccanismo di gestione del contesto è attuato da una classe, non accessibile al programmatore, responsabile della gestione di tutti i contesti. Il contesto corrente è sempre riportato dalla proprietà statica OpenGLContext.CurrentContext.

All’avvio dell’applicazione, la classe OpenGLContextFactory, crea sempre un contesto predefinito, il cosiddetto contesto nascosto o Hidden Context. In questo modo esisterà sempre un contesto attivo creato nel thread principale dell’applicazione. Questo contesto normalmente non sarà mai utilizzato direttamente dal programmatore, ma in ogni caso potrà sempre servire per invocare funzioni di OpenGL senza crearne di nuovi.

Il rilascio delle risorse non gestite create con il contesto OpenGL, deve essere eseguito esplicitamente, a cura e responsabilità del programmatore, utilizzando il metodo Dispose della classe OpenGLContext, normalmente poco prima dell’uscita dall’applicazione.

1 trackbacks

  1. 2interrogation Trackback | 2022/01/08