Tuesday, October 26, 2010

Gestione della memoria

Come abbiamo visto nell'articolo dedicato all'hardware il Nintendo DS dispone di un totale di 656 KB di memoria video suddiviso in 9 banchi di diversa dimensione e funzionalità.
Di questi 9 banchi, i principali sono i primi 4 (A, B, C e D) e sono tutti di dimensione pari a 128 KB.
I restanti banchi hanno dimensioni che variano da 16 KB a 64 KB.
Andiamo ora ad analizzare le diverse funzioni che è possibile assegnare ai vari banchi:

-LCD (nessuna funzione): il banco è mappato ad un indirizzo fisso, diverso per ognuno, in modo da comporre un unico continuo blocco di memoria, se dovesse servire. Tutti i banchi sono in grado di lavorare in questa modalità.
-Texture memory: il banco viene utilizzato come memoria per le texture che verranno usate per i modelli 3D. Solo i 4 banchi principali A, B, C e D.
-Palette texture memory: il banco viene utilizzato come memoria per le palette (tavolozze colori) delle texture (per quelle texture che ne hanno bisogno). Solo i banchi E, F e G.
-Background memory (Main Engine): il banco viene utilizzato come memoria per i background (indipendentemente da quanti se ne useranno) del motore principale. Tutti i banchi tranne H e I.
-Background memory (Sub Engine): il banco viene utilizzato come memoria per i background del motore secondario.
Solo i banchi C, H e I.
-Sprite memory (Main Engine): il banco viene utilizzato come memoria per gli sprite del motore principale. Solo i banchi A, B, E, F e G.
-Sprite memory (Sub Engine):  il banco viene utilizzato come memoria per gli sprite del motore secondario. Solo i banchi D e I.
-Memoria per le palette estese dei background e degli sprite (Main/Sub Engine): è possibile dichiarare fino a 16 palette (da 16 o da 256 colori) per ogni background ed altre 16 palette per gli sprite. Questo per entrambi i motori grafici.

Le funzioni per impostare le modalità di funzionamento dei vari banchi hanno la seguente sintassi:

void vramSetBankx(VRAM_x_y)


dove x sta per il nome del banco e y sta per il tipo di modalità.

es.
void vramSetBankA(VRAM_A_LCD)
void vramSetBankD(VRAM_D_MAIN_SPRITE)


Riassumiamo qui di seguito le modalità possibili per ogni banco:

Memoria (dimensione) Modalità utilizzabili
VRAM_BANK_A (128 KB) VRAM_A_LCD
VRAM_A_TEXTURE
VRAM_A_MAIN_BG
VRAM_A_MAIN_SPRITE
VRAM_BANK_B (128 KB) VRAM_B_LCD
VRAM_B_TEXTURE
VRAM_B_MAIN_BG
VRAM_B_MAIN_SPRITE
VRAM_BANK_C (128 KB) VRAM_C_LCD
VRAM_C_TEXTURE
VRAM_C_MAIN_BG
VRAM_C_SUB_BG
VRAM_BANK_D (128 KB) VRAM_D_LCD
VRAM_D_TEXTURE
VRAM_D_MAIN_BG
VRAM_D_SUB_SPRITE
VRAM_BANK_E (64 KB) VRAM_E_LCD
VRAM_E_MAIN_BG
VRAM_E_MAIN_SPRITE
VRAM_E_TEX_PALETTE
VRAM_E_BG_EXT_PALETTE
VRAM_BANK_F (16 KB) VRAM_F_LCD
VRAM_F_MAIN_BG
VRAM_F_MAIN_SPRITE
VRAM_F_TEX_PALETTE
VRAM_F_BG_EXT_PALETTE
VRAM_F_SPRITE_EXT_PALETTE
VRAM_BANK_G (16 KB) VRAM_G_LCD
VRAM_G_MAIN_BG
VRAM_G_MAIN_SPRITE
VRAM_G_TEX_PALETTE
VRAM_G_BG_EXT_PALETTE
VRAM_G_SPRITE_EXT_PALETTE
VRAM_BANK_H (32 KB) VRAM_H_LCD
VRAM_H_SUB_BG
VRAM_H_SUB_BG_EXT_PALETTE
VRAM_BANK_I (16 KB) VRAM_I_LCD
VRAM_I_SUB_BG

VRAM_I_SUB_SPRITE
VRAM_I_SUB_SPRITE_EXT_PALETTE



Modalità Grafiche

Come abbiamo già visto nell'articolo dedicato all'hardware del Nintendo DS (lo potete trovare qui), il nostro DS dispone di ben due motori grafici, il Main Engine ed il Sub Engine (così chiamati seguendo lo standard delle libnds).
Il primo gestisce sia la grafica 2D che quella 3D mentre il secondo solo la grafica 2D.
In termini di grafica 2D le caratteristiche dei due motori grafici sono pressoché uguali tranne per il fatto che il Main Engine possiede una modalità aggiuntiva chiamata Frame Buffer.
Ma partiamo col descrivere le varie modalità.

1. Frame Buffer
In questa modalità è possibile impostare direttamente i valori RGB di ogni pixel dello schermo ottenendo 32768 colori (15 bpp - bits per pixel) occupando però ben 96 KB di memoria video.
Inoltre in questa modalità viene del tutto "scavalcato" il motore grafico e quindi le sue principali caratteristiche come la gestione degli sprite.

2. Bitmap
In queste modalità è possibile impostare per ogni pixel dello schermo un colore scelto in una palette (tavolozza) di 256 colori.
La palette viene definita in un'area di memoria a parte nella quale per ognuno dei colori, viene definita la terna RGB.
In questo modo ogni immagine a pieno schermo può mostrare contemporaneamente al massimo 256 colori diversi richiedendo però solo 48 KB di memoria video poiché ogni pixel richiede esclusivamente un byte.
E' anche possibile utilizzare le modalità bitmap a 32768 colori in maniera analoga alla modalità Frame Buffer senza però scavalcare il motore grafico e le sue principali funzioni.

3. Text Mode (Tiled Mode - modalità a tessere)
In queste modalità (le più complesse e le più efficenti nello sfruttare l'hardware grafico 2D) si definisce nella memoria video un archivio di immagini da 8x8 pixel a 16 o 256 colori dette tessere.
Queste tessere verranno composte sullo schermo usando una mappa, ovvero una matrice che descrive quali tessere (come fossero caratteri, da qui il nome Text Mode) posizionare una a fianco all'altra (o una sopra all'altra) per formare un'immagine sola.

Background
Sia nelle modalità Text che in quelle Bitmap, ogni schermo potrà disporre di 4 layers di background e la possibilità di mostrare/nascondere determinati pixel di ogni singolo layer, in modo da poter vedere i background sottostanti attraverso quelli sovrastanti.
Inoltre, a seconda della modalità utilizzata, alcuni background potranno essere traslati in orizzontale/verticale (scrolling), ruotati (rotating) ed ingranditi o rimpiccioliti (scaling).

Sprites
Entrambi i motori grafici possono inoltre gestire un massimo di 128 sprites, ognuno di dimensioni che variano dai più piccoli 8x8 pixels fino ai più grandi di 64x64 pixels (di forma sia quadrata che rettangolare).


Nintendo DS Hardware

Prima di procere allo sviluppo di software sul nostro amato Nintendo DS, sarebbe cosa buona e giusta dare prima uno sguardo all'hardware su cui andremo a programmare.
Premetto che non è mia intenzione dare un'approfondita descrizione dell'hardware del DS, ma solo di quei componenti che maggiormente andremo ad usare durante la programmazione.

CPUs
ARM9 (66 MHZ)
Elabora il gameplay e la gestione del rendering video.

ARM7
(33 MHZ)
Elabora il suono, il Wi-Fi e i giochi di Game Boy Advance quando è in quella modalità.


Memoria
Main (4 MB): generalmente si occupa di tenere in memoria gli eseguibili e la maggior parte dei dati dei nostri giochi.
Possono accedervi sia l'ARM9 che l' ARM7 in ogni momento.
Ogni conflitto di utilizzo dei bus è gestito dal processore con priorità maggiore (l'ARM7) che mette in attesa l'altro processore fino a quando non ha finito di completare le sue operazioni.
Nonostante ciò, solitamente l'ARM7 utilizza di default la memoria IWRAM che vedremo qui di seguito.

IWRAM
(64 KB)
E' la memoria a cui può avere accesso solo l'ARM7 e contiene gli eseguibili e i dati di giochi elaborati da questo processore.

Video RAM (656 KB Totale)
Il Nintendo DS possiede 9 banchi di memoria video. Essi possono contenere le nostre sprites, le textures per i nostri modelli 3D e i tiles.
Di questi 9 banchi di memoria, quelli di nostro principale interesse sono i 4 più grandi, tutti della dimensione di 128 KB:
  • Bank A (128 KB)
  • Bank B (128 KB)
  • Bank C (128 KB)
  • Bank D (128 KB)
I rimanenti banchi sono i seguenti:
  • Bank E (64 KB)
  • Bank F (16 KB)
  • Bank G (16 KB)
  • Bank H (32 KB)
  • Bank I (16 KB)
Le diverse funzioni che è possibile assegnare ai banchi sono:
-LCD (nessuna funzione): il banco è mappato ad un indirizzo fisso, diverso per ognuno, in modo da comporre un unico continuo blocco di memoria, se dovesse servire. Tutti i banchi sono in grado di lavorare in questa modalità.
-Texture memory: il banco viene utilizzato come memoria per le texture che verranno usate per i modelli 3D. Solo i 4 banchi principali A, B, C e D.
-Palette texture memory: il banco viene utilizzato come memoria per le palette (tavolozze colori) delle texture (per quelle texture che ne hanno bisogno). Solo i banchi E, F e G.
-Background memory (Main Engine): il banco viene utilizzato come memoria per i background (indipendentemente da quanti se ne useranno) del motore principale. Tutti i banchi tranne H e I.
-Background memory (Sub Engine): il banco viene utilizzato come memoria per i background del motore secondario.
Solo i banchi C, H e I.
-Sprite memory (Main Engine): il banco viene utilizzato come memoria per gli sprite del motore principale. Solo i banchi A, B, E, F e G.
-Sprite memory (Sub Engine):  il banco viene utilizzato come memoria per gli sprite del motore secondario. Solo i banchi D e I.
-Memoria per le palette estese dei background e degli sprite (Main/Sub Engine): è possibile dichiarare fino a 16 palette (da 16 o da 256 colori) per ogni background e fino ad altre 16 palette per gli sprite per entrambi i motori grafici.

Virtual Video Ram
Il termine "virtuale" deriva dal fatto che nel Nintendo DS è del tutto assente la presenza di memoria dedicata alla gestione del 2D.
Per rimediare a questo, è possibile mappare i banchi della video ram come spazio dedicato al 2D.

Motori Grafici
Il Nintendo DS dispone di ben due motori grafici ognuno dei quali collegato ad un solo schermo alla volta.

Il Main Engine è in grado di gestire sia la grafica 2D che la grafica 3D.
Il Sub Engine può gestire solo la grafica 2D.
Entrambi possono gestire fino a 4 layers di background contemporaneamente.
Il framerate è di 60 fps.

Specifiche 2D

  • massimo di 128 sprites per motore grafico
  • massimo di 4 layers di background contemporanei
Specifiche 3D
  • Massimo di 2048 triangoli o 6144 vertici per frame
  • 512 KB di memoria per le texture
  • Grandezza massima delle texture 1024x1024 pixels




Friday, October 8, 2010

Vim Autocomplete for Linux

First of all download the following file: omnicppcomplete-0.41.zip

1) Create a directory in your home directory called .vim

cd ~
mkdir .vim


2) Copy the downloaded file inside the .vim directory and unzip it

cd .vim
unzip omnicppcomplete-0.41.zip

Now inside our .vim directory we should have the following directories:

after
autoload
doc

4) Inside the .vim directory create a new directory called tags

mkdir tags

5) Enter inside our tags directory and run the following command:

cd tags
ctags -R --sort=yes --c++-kinds=+p --fields=+iaS --extra=+q --language-force=C++ -f gl /usr/include/GL/
 
this will create a tag file named "gl" of the directory /user/include/GL/
Repeat this step for any include directory you want by changing the name of the tag file and the path
of the directory.
 
6) Edit our .vimrc file as follows:
set tags+=~/.vim/tags/gl
 
7) If you want to build tags of your own project (by pressing Ctrl-F12) add as follows to our .vimrc:
map  :!ctags -R --sort=yes --c++-kinds=+p --fields=+iaS --extra=+q .
 
That's it! 




Saturday, December 26, 2009

Maya Shelf

Hold "CTRL + SHIFT + Element to add" to add icons to the custom shelf
Hold "SHIFT+ ALT+ Element to add" to add icons to the custom shelf in Linux

Wednesday, December 9, 2009

Compiling wxWidgets using mingw32-make

Some months ago I showed you how to compile wxWidgets using the ./configure method. Today I've tried to compile it using mingw32-make so here it is:

First let's set the path to our mingw and mingw32 bin directories:
set path=D:\MinGW\mingw\bin;c:\MinGW\mingw\mingw32\bin;
 
Next we enter in our WXWIDGETS\build\msw directory and type the following statement:
mingw32-make -f makefile.gcc SHARED=1 MONOLITHIC=1 BUILD=debug UNICODE=1
And that's it!
 

Wednesday, November 18, 2009

Raw Input Introduction Part 1: Keyboard Input

Since I've found very little information about Raw Input API on the net, I thought it could be of some help to write an article as an introduction to it. So here it is.
Let's get started!

Registering Raw Input Devices
The first thing we need to do is registering the input device we'd like our application to monitor.
The main and probably most important difference of Raw Input from DirectInput and standard Win32 messages, is that Raw Input can retrieve which device has sent the input and not just the input.
That means that if you have two keyboards connected, Raw Input will be able to recognise the input and which one of the two keyboards has sent it.
That's why we need to register our input devices.
Let's make a header file called RawInputUtils.h and write the following code:

void InitRawInput(HWND hWnd)
{
RAWINPUTDEVICE Rid[1];
Rid[0].usUsagePage = 0x01;
Rid[0].usUsage = 0x06;
Rid[0].dwFlags = RIDEV_INPUTSINK;
Rid[0].hwndTarget = hWnd;

if (RegisterRawDataInputDevices(Rid,1,sizeof(Rid[0])) == false)
{
  MessageBox(NULL, "Device Registration failed", "Error",MB_OK);
  return;
}
}

RAWINPUTDEVICE is a structure used to describe the input device we want to monitor. Its parameters are the following:
  • usUsagePage Top level HID usage page. For most HIDs the value is 0x01
  • usUsage Number indicating which type of device should be monitored. For keyboard the value is 0x06, for mouse it's 0x02. Here you can find a complete list of the possible values.
  • dwFlags Determine how the data should be handled. Here a complete list of the possible values.
  • hwndTarget Handle to the window which will monitor this device.
So what we have done here is:
  1. RAWINPUTDEVICE Rid[1]; We have declared an array of a single element that will hold a RawInputDevice structure that describes our device (in our case the keyboard).
  2. Rid[0].usUsage = 0x06; We specify that our device is a keyboard
  3. Rid[0].dwFlags = RIDEV_INPUTSINK; This means that our application will retrieve input even if it loses focus.
  4. Rid[0].hwndTarget = hWnd; We specifies the handle of the application window that will monitor our device. In our case the one we pass to the InitRawInput function.
After we have described our devices we need to register it:

if (RegisterRawDataInputDevices(Rid,1,sizeof(Rid[0])) == false) { //Error}

The RegisterRawDataInputDevices() function returns false if the device registration fails.
Its arguments are the following:
  1. pRawInputDevices Pointer to an array of RAWINPUTDEVICE structures that represent the devices we want to register (in our case we have just a single element, the keyboard)
  2. uiNumDevices Number of RAWINPUTDEVICE structures we have inside our RAWINPUTDEVICE array
  3. cbSize Size in bytes of a RAWINPUTDEVICE structure.
The next step is to call our InitRawInput() function somewhere inside our WinMain() functon so we can register our device for our application.
First of all, let's add an include to our RawInputUtils header we have just wrote.
Then add this code in the WinMain() function just after the ShowWindow() function call (don't worry if you don't get where you have to put it, you can find the complete source code at the bottom):


InitRawInput(hWnd);


The InitRawInput() call should have described our device and registered it. If nothing went wrong we can proceed further.

Retrieving raw input
Now, inside our WndProc() function we need to add the WM_INPUT case inside our switch(msg) statement:

case WM_INPUT:
{
GetRawInputData((HRAWINPUT)lParam, RID_INPUT, NULL, &dwSize, sizeof(RAWINPUTHEADER));
.....

GetRawInputData() accept the following arguments:
  1. hRawInput Handle to the RAWINPUT structure containing the input data we want to process. This is provided by the lParam in WM_INPUT message.
  2. uiCommand Flag that sets whether to retrieve the input data or the header information from the RAWINPUT structure. Possible valures are RID_INPUT or RID_HEADER.
  3. pData  If we set it to NULL, the size of the buffer required to contain the data is returned in the pcbSize variable. Otherwise, pData must be a pointer to allocated memory that can hold the RAWINPUT structure provided by the WM_INPUT message.
  4. pcbSize A variable that returns or specifies the size of the data pointed by pData.
  5. cbSizeHeader Size of a RAWINPUTHEADER structure.
Once we called this function, our dwSize variable will correspond to the exact number of bytes needed to store the raw input data.
Now, we declare a pointer to the right amount of memory to store the rawinput data:

lpb = new BYTE[dwSize];

Next we call the GetRawInputData() function for the second time to ensure that lpb points to a right amount of memory to store the raw input data of dwSize size.

if (GetRawInputData((HRAWINPUT)lParam, RID_INPUT, lpb, &dwSize,sizeof(RAWINPUTHEADER)) != dwSize)
{
//Error
}


Now we need to "cast" our lpb pointer to RAWINPUT structure which gives easy access to the data various members.

raw = (RAWINPUT*)lpb;

Processing raw input
First we check if the raw input data is of keyboard type:

if (raw->header.dwType == RIM_TYPEKEYBOARD)
{ 

Next we ensure to retrieve both system and non-system key down events:

if (raw->data.keyboard.Message == WM_KEYDOWN || raw->data.keyboard.Message == WM_SYSKEYDOWN)
{ 

Then we returns in the window title the key code of the key we press.

USHORT usKey;
usKey = raw->data.keyboard.VKey;
CHAR szTest[4];
_itoa_s((int)usKey,szTest,10);
SetWindowText(hWnd,szTest);
}
}
delete[] lpb;

This little example should show you the three main steps to follow to implement Raw Input inside your application.
Comments and questions are welcome.
Hope this article will be useful. Sorry for my bad style of coding ^^.
Until next time, cheers!

Source code: