Afficher un message
Vieux 12/10/2010, 11h38   #1 (permalink)
Profil
Wolfi
Membre
Ancienneté  80%
Ancienneté 80%
 
Date d'inscription: mai 2006
Localisation: Rouen
Âge: 36
Pays :
Messages: 675
Téléchargements: 0
Uploads: 0
Merci: 1
Remercié 14 fois dans 7 Posts
Envoyer un message via ICQ à Wolfi Envoyer un message via AIM à Wolfi
Par défaut [TUTO HOMEBREW] Faire son premier homebrew, et surtout le comprendre

Salut, venant de comprendre une bonne partie du lancement/fermeture de programme pour la ps3, je vous fais un petit tuto, pour que d'autres qui ne connaissent pas très bien openGL puissent démarrer concretement plus rapidement.

Le but de ce tuto sera d'initialiser le contexte graphique, d'executer une boucle interruptible par le bouton PS/quitter le jeu, puis de répondre à l'interruption en revenant proprement sur le XMB.

Ce tutorial est utile aux personnes connaissant le C et l'OpenGL, et je développe une petite librairie en C++ pour abstraire tout ce qui pourrait l'être (polygones, brush, manette, affichage de chaines de charactères, reseau, etc).

Tout d'abord, petite introduction, inspirée de diverses présentations trouvables sur les sites officiels de Sony: comment la PS3 dessine-t-elle à l'écran.

Il y a deux librairies disponibles pour les développeurs: GCM et PSGL, GCM étant le pendant bas-niveau de PSGL. J'ai décidé d'utiliser PSGL car on n'a pas vraiment besoin d'un plus bas niveau pour développer des petits homebrew, mais sachez que le développeur anonyme de l'open backup manager utilise GCM, ce qui rend son code difficile à comprendre et trop complexe pour ce qu'il fait en réalité (ce qui est peut-être fait expres).

PSGL est une implémentation made in Sony d'OpenGL ES, une version simplifiée d'OpenGL (110 primitives au lieu de 400) mais qui d'après eux reste aussi puissante. Nous nous contenterons ici de parler des mecanisme d'initialisation et de fermeture, car l'utilisation d'OpenGL n'est pas propre à la PS3 et il existe déja des centaines de cours bien construits.

Enfin, il faut savoir qu'il n'existe aucun autre moyen que ces librairies graphiques pour afficher quelque chose à l'écran, donc pas de sortie standard, pas de printf etc.

Mon tutorial sera sous la forme d'un code commenté pour chaque étape.

Première partie - initialiser la PS3 -

Citation:
// On demande 1 spu pour PSGL sur les 6 disponibles
sys_spu_initialize(6, 1);

// Met en pause la PS3 tant que la sortie HDMI n'est pas prête.
CellVideoOutState videoState;
do {
cellVideoOutGetState(CELL_VIDEO_OUT_PRIMARY, 0, &videoState);
} while(videoState.state != CELL_VIDEO_OUT_OUTPUT_STATE_ENABLED );

// On enregistre nos option d'initialisation.
PSGLinitOptions initOpts;
initOpts.enable = PSGL_INIT_MAX_SPUS | PSGL_INIT_INITIALIZE_SPUS;
initOpts.maxSPUs = 1;
initOpts.initializeSPUs = false;
initOpts.persistentMemorySize = 0;
initOpts.transientMemorySize = 0;
initOpts.errorConsole = 0;
initOpts.fifoSize = 0;
initOpts.hostMemorySize = 128 * 1024 * 1024; // 128 mb

psglInit(&initOpts);

// Tableau des résolution désirées, par ordre de priorité.
const unsigned int resolutions[][3] = {
{CELL_VIDEO_OUT_RESOLUTION_1080, 1920, 1080},
{CELL_VIDEO_OUT_RESOLUTION_720, 1280, 720},
{CELL_VIDEO_OUT_RESOLUTION_576, 720, 576},
{CELL_VIDEO_OUT_RESOLUTION_480, 720, 480}
};
// On choisi la résolution disponible et on enregistre son index dans le
// tableau précédent.
int chosenResolutionIndex = -1;
for (int i = 0; i < 4 && chosenResolutionIndex == -1; i++) {
if(cellVideoOutGetResolutionAvailability(CELL_VIDE O_OUT_PRIMARY,
resolutions[i][0],
CELL_VIDEO_OUT_ASPECT_AUTO,
0)) {
chosenResolutionIndex = i;
}
}

// On peut maintenant passer à l'initialisation de PSGL
if (chosenResolutionIndex != -1) {
// On cherche à obtenir les information de largeur et longueur de la
// résolution choisie.
unsigned int width = resolutions[chosenResolutionIndex][1];
unsigned int height = resolutions[chosenResolutionIndex][2];

// On crée une sortie PSGL paramétrée avec nos dimensions.
PSGLdeviceParameters params;
// On choisi les parametres a faire prendre en compte par createDevice
params.enable = PSGL_DEVICE_PARAMETERS_COLOR_FORMAT
| PSGL_DEVICE_PARAMETERS_DEPTH_FORMAT
| PSGL_DEVICE_PARAMETERS_MULTISAMPLING_MODE
| PSGL_DEVICE_PARAMETERS_WIDTH_HEIGHT;
// 4 composantes disponibles pr les vertex
params.colorFormat = GL_ARGB_SCE;
// 24 bits dispo pour les couleurs
params.depthFormat = GL_DEPTH_COMPONENT24;
params.multisamplingMode = GL_MULTISAMPLING_NONE_SCE;
params.width = width;
params.height = height;

device = psglCreateDeviceExtended(&params);

context = psglCreateContext();
psglMakeCurrent(context, device);
psglResetCurrentContext();

// On initialise le Viewport - Partie OpenGL indispensable

// On cherche à obtenir les dimensions effectives de dessin.
GLuint renderWidth, renderHeight;
psglGetRenderBufferDimensions(device, &renderWidth, &renderHeight);

// On fixe la taille du Viewport
glViewport(0, 0, renderWidth, renderHeight);

// On met en place la projection orthogonale.
GLfloat aspectRatio = psglGetDeviceAspectRatio(device);
float l=aspectRatio, r=-l, b=-1, t=1;
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
glOrthof(l,r,b,t,0,1);

glClearColor(0.f, 0.f, 0.f, 1.f);
glDisable(GL_CULL_FACE);

// Il faut effacer au moins une fois l'ecran.
glClear(GL_COLOR_BUFFER_BIT
| GL_DEPTH_BUFFER_BIT
| GL_STENCIL_BUFFER_BIT);
psglSwap();

// On enregistre l'event handler sur le slot 0.
cellSysutilRegisterCallback(0, systemCallback, NULL);
}
La dernière ligne prépare la prochaine étape: l'execution de la boucle principale.
En effet comme dans toute application OpenGL, on doit nous même boucler pour que le programme de s'arrête pas. Cependant il existe un cas ou nous aimerions que le programme s'arrête: quand l'utilisateur éteind la ps3 ou demande au programme de s'arreter via sa manette. Pour cela, on utilise un gestionnaire d'evenement (event handler) qui appelera a chaque evenement une fonction "réponse" (callback) qu'il faut enregistrer au préalable grace à l'appel cellSysutilRegisterCallback.

Le premier argument de cette fonction est le "slot", il y en a 4 disponibles, de 0 à 3. Le dernier argument est un pointeur vers un buffer contenant diverse information utiles au callback, mais je n'en ai pas vraiment besoin, je le met donc à NULL. Enfin, le deuxieme argument est une fonction à executer quand un évènement aura eu lieu.

Deuxieme Etape - Execution de la boucle principale

Juste apres l'initialisation du contexte, on doit executer une boucle interruptible comme cela:
Citation:
while(!systemExited) {
cellSysutilCheckCallback(); // On vérifie les interruptions
// ... On execute des instructions de dessin
}
Si un evenement a lieu, une fonction enregistrée comme callback sera appelée, en voici un exemple:

Citation:
void systemCallback(const uint64_t status, const uint64_t param,
void *userdata) {
(void)userdata; // Pour supprimer les warnings
(void)param;
switch (status) {
case CELL_SYSUTIL_REQUEST_EXITGAME: systemExited = true;
break;
case CELL_SYSUTIL_DRAWING_BEGIN:
case CELL_SYSUTIL_DRAWING_END:
default: break;
}
}
Le prototype doit être exactement identique (3 arguments, renvoie void).
L'arguement status est le code d'interruption, on cherche donc lequel c'est grace à un branchement conditionnel, et si c'est une requête de sortie de programme, on modifie la valeur de systemExited, qui lui meme permettra d'arréter la boucle.

Une fois la boucle interrompue, on doit fermer le contexte OpenGL pour eviter que la PS3 bug completement et retourne plutot calmement au XMB:

Citation:
glFinish();
psglDestroyContext(context);
psglDestroyDevice(device);
psglExit();
Ces primitives parlent d'elles-même. A noter que pour compiler vous aurez besoin des header <psgl/psgl.h> <cell/sysmodule.h> et <sys/spu_initialize.h>.

Si ce tuto vous a plu, n'hésitez pas à m'en demander d'autre, comme la création d'un Makefile efficace, je prépare aussi la gestion complete de la manette.

Si vous avez le sentiment que mon code pourrait être simplifié/amélioré dites-le moi aussi
__________________
le loup est un loup pour le loup

Nintendo: N64, NGC
Sega: Dreamcast
Sony: PSP, PS3
Wolfi est déconnecté   Réponse avec citation
Ces 4 utilisateurs disent Merci à Wolfi pour ce poste utile:
Apocalypse59 (12/10/2010), DazZ (12/10/2010), oMc (12/10/2010), Sphax (27/10/2010)