Il progetto su Godot

Prima di cominciare con questo nuovo articolo, vorrei darmi una bacchettata sulle manine da solo: avrete notato che non ci sono commenti nel codice. Ovviamente è una pratica completamente autolesionista e deprecabile. Sto agendo così perché quel codice è affiancato da tutto questo scrivere, magari un po’ prolisso, ma spero sufficientemente chiaro. Solitamente uso commentare il codice con un preambolo generale che spiega il funzionamento, il senso della classe, se vogliamo. Poi commento anche ogni singolo metodo, per lasciarmi delle molliche di pane, assolutamente necessarie per riprendere il codice in mano in caso fosse necessario, magari a distanza di mesi…

Mi è molto dispiaciuto constatare, invece, che il codice GDExtension annesso alla libreria scaricata con git è assolutamente privo di ogni commento. Sono abbastanza sicuro che il codice nelle mani dei creatori di Godot sia stato trattato molto meglio, cosa tradita da una nota con scritto “// THIS FILE IS GENERATED. EDITS WILL BE LOST.” praticamente all’inizio di ogni file in C++ annesso. Peccato che io ho solo quei file per valutarne il contenuto, per cui qualche chilometro di commenti in più sarebbe stato gradito. Conseguenza diretta è che mi è stato veramente difficile tirare fuori dal cilindro le informazioni che state trovando in questo lavoro. Dovreste stamparvi una mia foto e metterla sulla scrivania a ‘mo di santino… (scherzo, più o meno).

Continuiamo. Se date un’occhiata a Fig.12, troverete una schermata dell’IDE di Godot con aperta la finestra che compare quando si aggiunge una nuova classe ad un progetto. Vedete, chiaramente, che la classe scritta in C++, GDBenchmarkWork, è riconosciuta dall’IDE ed è pronta per essere aggiunta, eventualmente, ad una scena. Così dovrebbe essere se tutto, fin qui, è andato bene. Detto questo, non lo faremo: useremo la classe istanziandola da codice.

La classe Benchmark

Questo lavoro poteva essere affrontato in mille mila modi diversi, ovviamente. Ho preferito stare sul semplice, un po’ per vedere la luce in fondo al tunnel di quest’opera, un po’ perché a farcire troppo il codice lo si raffina da un lato, ma lo si rende più criptico dall’altro. Il primo obiettivo che ci poniamo è quello di scrivere una classe che faccia un po’ da pilota per il benchmark delle successive. Non vogliamo esagerare con i fronzoli, per cui ci accontenteremo di stampare i risultati a console. Però, vogliamo testare le capacità di GDExtension rispetto a GDScript e, per torchiarlo meglio, lo faremo usando la grafica 3D.

E’ stata creata, quindi, una scena che si compone di una classe radice di tipo Benchmark, che poi sarebbe quella scritta da me e che vi andrò ad illustrare a breve. Come figlie, ci sono una classe Camera3D che andremo a posizionare con il codice e una DirectionalLight3D che manterremo con i valori di default. Potete osservare tutto in Fig.13.

Direi che non dovrebbe servire commentare davvero tutto il codice, visto che parto dal presupposto che sappiate destreggiarvi bene in GDScript se volete andare oltre con GDExtension e quindi andiamo ad esaminare i suoi punti salienti. Il primo metodo che vorrei discutere è _prepare_works_array(). Com’è possibile vedere, si occupa di riempire l’array work, dichiarato come proprietà della stessa classe, con nuove istanze di classi del tipo GDBenchmarkWork. Nell’immagine vedrete questa riga di codice:

works.append(Benchmark1000Cubes.new())

Benchmark1000Cubes è una classe che andremo ad implementare in seguito e che, per il momento, potete considerare come un segnaposto. Ad ogni modo, si occuperà di fare un benchmark basato su GDScript. Convenzionalmente, chiameremo GDBenchmark1000Cubes la classe che sarà scritta in C++, con il prefisso GD ad indicare che è scritta con GDExtension. Manterremo questa convenzione anche per le altre.

Sempre nello stesso metodo, un ciclo successivo scorre tutto l’array riempito in precedenza occupandosi di registrare le singole classi come figlie di Benchmark, quella principale, ma anche di collegare i rispettivi segnali job_ended al metodo _work_ended(). Quando un singolo lavoro di benchmark termina, emette un segnale job_ended catturato dal metodo _work_ended() che prima stampa il messaggio ricevuto attraverso il segnale e poi chiama _start_next_work().

_start_next_work() è chiamato una prima volta in _ready(), per innescare tutto il processo, e successivamente in seguito ad ogni chiamata di _work_ended(). _start_next_work() controlla se l’array work ha elementi, che tratta fiduciariamente come classi del tipo GDBenchmarkWork. Se ce ne sono, ne invoca il metodo run(), in caso contrario si limita a stampare un messaggio di fine a console. Abbiamo realizzato una catena di montaggio per cui ogni benchmark lavora da solo fino al termine e poi passa la palla al successivo fino a terminare i lavori previsti nell’array.

Adesso dobbiamo realizzare i singoli benchmark, ovvero i singoli lavori. Creeremo classi sia in GDScript che in C++ figlie di GDBenchmarkWork, che ne erediteranno il codice. Quindi realizzeremo lo stesso lavoro sia in GDScript che in GDExtension, praticamente creando classi gemelle a due a due. Poi, assegnandole in sequenza all’array work, una volta lanciato il programmino, leggeremo i tempi di esecuzione delle due versioni a console.


Il progetto su Godot

Prima di cominciare con questo nuovo articolo, vorrei darmi una bacchettata sulle manine da solo: avrete notato che non ci sono commenti nel codice. Ovviamente è una pratica completamente autolesionista e deprecabile. Sto agendo così perché quel codice è affiancato da tutto questo scrivere, magari un po’ prolisso, ma spero sufficientemente chiaro. Solitamente uso commentare il codice con un preambolo generale che spiega il funzionamento, il senso della classe, se vogliamo. Poi commento anche ogni singolo metodo, per lasciarmi delle molliche di pane, assolutamente necessarie per riprendere il codice in mano in caso fosse necessario, magari a distanza di mesi…

Mi è molto dispiaciuto constatare, invece, che il codice GDExtension annesso alla libreria scaricata con git è assolutamente privo di ogni commento. Sono abbastanza sicuro che il codice nelle mani dei creatori di Godot sia stato trattato molto meglio, cosa tradita da una nota con scritto “// THIS FILE IS GENERATED. EDITS WILL BE LOST.” praticamente all’inizio di ogni file in C++ annesso. Peccato che io ho solo quei file per valutarne il contenuto, per cui qualche chilometro di commenti in più sarebbe stato gradito. Conseguenza diretta è che mi è stato veramente difficile tirare fuori dal cilindro le informazioni che state trovando in questo lavoro. Dovreste stamparvi una mia foto e metterla sulla scrivania a ‘mo di santino… (scherzo, più o meno).

Continuiamo. Se date un’occhiata a Fig.12, troverete una schermata dell’IDE di Godot con aperta la finestra che compare quando si aggiunge una nuova classe ad un progetto. Vedete, chiaramente, che la classe scritta in C++, GDBenchmarkWork, è riconosciuta dall’IDE ed è pronta per essere aggiunta, eventualmente, ad una scena. Così dovrebbe essere se tutto, fin qui, è andato bene. Detto questo, non lo faremo: useremo la classe istanziandola da codice.

La classe Benchmark

Questo lavoro poteva essere affrontato in mille mila modi diversi, ovviamente. Ho preferito stare sul semplice, un po’ per vedere la luce in fondo al tunnel di quest’opera, un po’ perché a farcire troppo il codice lo si raffina da un lato, ma lo si rende più criptico dall’altro. Il primo obiettivo che ci poniamo è quello di scrivere una classe che faccia un po’ da pilota per il benchmark delle successive. Non vogliamo esagerare con i fronzoli, per cui ci accontenteremo di stampare i risultati a console. Però, vogliamo testare le capacità di GDExtension rispetto a GDScript e, per torchiarlo meglio, lo faremo usando la grafica 3D.

E’ stata creata, quindi, una scena che si compone di una classe radice di tipo Benchmark, che poi sarebbe quella scritta da me e che vi andrò ad illustrare a breve. Come figlie, ci sono una classe Camera3D che andremo a posizionare con il codice e una DirectionalLight3D che manterremo con i valori di default. Potete osservare tutto in Fig.13.

Direi che non dovrebbe servire commentare davvero tutto il codice, visto che parto dal presupposto che sappiate destreggiarvi bene in GDScript se volete andare oltre con GDExtension e quindi andiamo ad esaminare i suoi punti salienti. Il primo metodo che vorrei discutere è _prepare_works_array(). Com’è possibile vedere, si occupa di riempire l’array work, dichiarato come proprietà della stessa classe, con nuove istanze di classi del tipo GDBenchmarkWork. Nell’immagine vedrete questa riga di codice:

works.append(Benchmark1000Cubes.new())

Benchmark1000Cubes è una classe che andremo ad implementare in seguito e che, per il momento, potete considerare come un segnaposto. Ad ogni modo, si occuperà di fare un benchmark basato su GDScript. Convenzionalmente, chiameremo GDBenchmark1000Cubes la classe che sarà scritta in C++, con il prefisso GD ad indicare che è scritta con GDExtension. Manterremo questa convenzione anche per le altre.

Sempre nello stesso metodo, un ciclo successivo scorre tutto l’array riempito in precedenza occupandosi di registrare le singole classi come figlie di Benchmark, quella principale, ma anche di collegare i rispettivi segnali job_ended al metodo _work_ended(). Quando un singolo lavoro di benchmark termina, emette un segnale job_ended catturato dal metodo _work_ended() che prima stampa il messaggio ricevuto attraverso il segnale e poi chiama _start_next_work().

_start_next_work() è chiamato una prima volta in _ready(), per innescare tutto il processo, e successivamente in seguito ad ogni chiamata di _work_ended(). _start_next_work() controlla se l’array work ha elementi, che tratta fiduciariamente come classi del tipo GDBenchmarkWork. Se ce ne sono, ne invoca il metodo run(), in caso contrario si limita a stampare un messaggio di fine a console. Abbiamo realizzato una catena di montaggio per cui ogni benchmark lavora da solo fino al termine e poi passa la palla al successivo fino a terminare i lavori previsti nell’array.

Adesso dobbiamo realizzare i singoli benchmark, ovvero i singoli lavori. Creeremo classi sia in GDScript che in C++ figlie di GDBenchmarkWork, che ne erediteranno il codice. Quindi realizzeremo lo stesso lavoro sia in GDScript che in GDExtension, praticamente creando classi gemelle a due a due. Poi, assegnandole in sequenza all’array work, una volta lanciato il programmino, leggeremo i tempi di esecuzione delle due versioni a console.


Ultima modifica: