Voir la version complète : [GBA][Aide] Vitesse du Mode 4
Bon, je réfléchi à la réalisation d'un petit jeu en 3D (TRES TRES basique la 3D, même pas texturé).
Le mode 4 s'impose de lui même donc, mais je ne trouve pas beaucoup de document sur celui-ci...
bref, voici ou j'en suis rendu pour le moment :
#include <gba_console.h>
#include <gba_video.h>
#include <gba_interrupt.h>
#include <gba_systemcalls.h>
#include <gba_input.h>
#include <gba_sprites.h>
#include <stdio.h>
#include <string.h>
#define BGPaletteMem ((u16*)0x5000000)
#define RGB(r,g,b) ((((b)>>3)<<10)+(((g)>>3)<<5)+((r)>>3))
u16 *VideoBuffer = (u16*)0x6000000;
u16 *FrontBuffer = (u16*)0x6000000;
u16 *BackBuffer = (u16*)0x600A000;
int frame = 0;
void Vblank(){frame++;}
void flip_buffer(){
VBlankIntrWait();
if (*(u16*) 0x4000000 & 0x10){
*(u16*) 0x4000000 &= ~0x10;
VideoBuffer = BackBuffer;
}
else {
*(u16*) 0x4000000 |= 0x10;
VideoBuffer = FrontBuffer;
}
}
void PlotPixel(u16 x, u16 y, u16 index) {
u16 half, peek, send;
half = x>>1;
peek = VideoBuffer[half+y*120];
if (x!=(half<<1))
send = (0x00ff&peek) + (index<<8);
else
send = (0xff00&peek) + index;
VideoBuffer[half+y*120] = send; }
int main(void) {
int i,j,c=0;
irqInit();
irqSet(IRQ_VBLANK, Vblank);
irqEnable(IRQ_VBLANK);
SetMode (MODE_4 | BG2_ENABLE) ;
BGPaletteMem[0]=RGB(0,0,0);
BGPaletteMem[1]=RGB(255,255,255);
c=0;
while(1){
for (i=0;i<160;i++)
for (j=0;j<240;j++)
PlotPixel(j,i,c);
flip_buffer();
c=!c;
}
return 0;
}
Comme vous pouvez le voir je ne fais que passer d'une image blanche à une image noir... mais c'est lent, trop lent !
Y aurai-t-il quelquechose que j'aurai mal fait/oublié ?
Dr.Vince
15/09/2006, 11h00
bah il me semble que tu peux plotter tes pixels par 2 ou 4 suivant si les transfère se font par 16 ou 32 bits nan ??
hello !!
Il faudrait nous en dire plus sur le type de rendu que tu voudrais obtenir ?
rendu fil de fer, polygone à couleur unique ?
Mais sinon, comme le dit dr vince, les pixels fauda au mini les écrire par 2 ou 4, car imagine que ton remplissage d'écran actuel fait appel 38400 fois à ta fonction plotpixels ! Rien qu'en branchement, ton cpu perd un temps fou.
Sinon, de toute façon pour ce gene de routine critique -> iwram.
Après pour ce qui est de la possibilité de performance de vitesse du mode 4 sur la gba, si on ne tient compte que de l'aspect rendu graphique, tu peux esperer:
-remplissage pur de l'écran en poly flat : 30 à 60 fps en fonction de la taille et du nombre de tes polys.
-remplissage pur de l'écran en poly texturés : 15,20 ou 30 fps ( pareil , ça dépend du nombre de poly et de leur taille )
-remplissage de l'écran par du tile software 32*32 ( type background gt racer ), en une demi frame.
Voilà, je pense qu'on pourra plus t'éclairer si on connait le type de rend que tu recherches .
Polygones de couleurs (et un peu de fil de fer, mais ce n'est pas le plus important)
Alekmaul
15/09/2006, 22h10
je dirais comme les autres mets au minimum tes pixels par 2
Change ton plot par un static inline ou define pour le mettre dans le code et éviter 240x160 appels (et donc empillage d'adresses de retour)
Change to y*120 par un (y<<6)+(y<<5)+(y<<4)+(y<<3), cela économisera aussi certainement un peu de cycles CPU non ?
Alekmaul, bien vu pour la fonction, oui, ca changera tout... (j'avais fait des tests à l'époque sur GBA pour voir la différence niveau vitesse, et c'était sans comparaison)
De souvenir, il existe pas une option dans les transferts DMA pour faire un équivalent "memset" ?
(car je viens de voir ça sur DS mais je crois qu'il existe aussi sur GBA)
je dirais comme les autres mets au minimum tes pixels par 2
Je ne vois pas comment savoir à l'avance l'état des pixels...
Je dois mettre dans un tableau la couleur de chaque pixel et ensuite les envoyés 2 par 2 dans le buffer ?
Pour info je n'ai jamais touché a l'iwram, ni au mode4 jusqu'a présent, donc j'avoue sans honte avoir un peu mal ...
(EDit : ni aux DMA d'ailleur...)
Alekmaul
15/09/2006, 22h44
oui, pour les pixels par deux, c'est peut être un peu dur ...
mais tente déja le reste , cela permettra déjà de l'optimisation.
Aussi, quelles sont tes options de compilations, cela peut jouer (et tu es en elf ou eabi ?)
La compilation est en .elf, makefile de base de devkitpro
Alekmaul
15/09/2006, 22h54
Alors, ensuite, si tu cherche niveau asm, voici un peu de "search" niveau gbadev :
http://forum.gbadev.org/viewtopic.php?t=3302
http://forum.gbadev.org/viewtopic.php?t=3006
http://forum.gbadev.org/viewtopic.php?t=2684
http://forum.gbadev.org/viewtopic.php?t=3302
TOut cela concerne , soit le mode 3, soit le mode 4, donc, je pense que tu y trouveras ton bonheur ;)
Alors, ensuite, si tu cherche niveau asm, voici un peu de "search" niveau gbadev :
http://forum.gbadev.org/viewtopic.php?t=3302
http://forum.gbadev.org/viewtopic.php?t=3006
http://forum.gbadev.org/viewtopic.php?t=2684
http://forum.gbadev.org/viewtopic.php?t=3302
TOut cela concerne , soit le mode 3, soit le mode 4, donc, je pense que tu y trouveras ton bonheur ;)
Je ne sais pas pourquoi mais les sujets sur GBAdev me lourdes de plus en plus :whst:
Merci quand même v_v
Trop interressant tout ça les gars!
bon, si c'est pour de l'affichage de polygone à couleur unique ( rendu flat ), il te faut déja un bel algo de tracé de ligne horizontales ( oublie le plotpixel ).
Ca va te permettre d'afficher tes triangles ou tes quads,( ou la aussi il te faudra un algo qui speed ... )
Bon, sinon, pour les lignes, t'auras donc un x de départ, un x de fin, ça te donne une longueur de ligne, tu peux donc en déduire le nombre de fois ou tu peux plotter 4 pixels ou 2 pixels d'un coup.
exemple: tu dois tracer une ligne de x=4 à x=64, ça te fais donc une ligne de 60 pixels de long, soit 15 x 4 pixels d'un coup. Vu que c'est du rendu à pixel unique, ça simplifie pas mal .
Toute la subtilité du mode 4, c'est que tu dois tenir compte du début de ligne et de la fin de ligne .
exemple: si ta ligne commence à x=3 et vu que si tu plotte par 4 , il faut etre aligné, il faut donc plotter à x=0 en prélevant la couleur des pixels 0 à 3, masquer le pixel 3, et y réintroduire ta couleur de polygone. Idem pour la fin de ligne.
Pour le millieu de ligne t'as juste à plotter par 4 directement.
ça confirme ce qui je pensais, j'ai pas le niveau :p
(comprendre : pour faire ce que je veux rapidement/facilement, sans me prendre la tête 50 ans)
#include <gba_console.h>
#include <gba_video.h>
#include <gba_interrupt.h>
#include <gba_systemcalls.h>
#include <gba_input.h>
#include <gba_sprites.h>
#include <stdio.h>
#include <string.h>
#define CODE_IN_IWRAM __attribute__ ((section (".iwram"), long_call))
#define VAR_IN_IWRAM __attribute__ ((section (".iwram")))
#define CODE_IN_EXRAM __attribute__ ((section (".ewram"), long_call))
#define VAR_IN_EXRAM __attribute__ ((section (".ewram")))
#define BGPaletteMem ((u16*)0x5000000)
#define RGB(r,g,b) ((((b)>>3)<<10)+(((g)>>3)<<5)+((r)>>3))
u16 *VideoBuffer = (u16*)0x6000000;
u16 *FrontBuffer = (u16*)0x6000000;
u16 *BackBuffer = (u16*)0x600A000;
int frame = 0;
int keys_pressed, keys_released;
void Vblank(){frame++;}
void flip_buffer(){
VBlankIntrWait();
if (*(u16*) 0x4000000 & 0x10){
*(u16*) 0x4000000 &= ~0x10;
VideoBuffer = BackBuffer;
}
else {
*(u16*) 0x4000000 |= 0x10;
VideoBuffer = FrontBuffer;
}
}
static inline void CODE_IN_IWRAM PlotPixel(int x,int y,int c) {
u16 half, peek, send;
if (x>=0 && x<240 && y>=0 && y<160){
half = x>>1;
peek = VideoBuffer[half+y*120];
if (x!=(half<<1)) send = (0x00ff&peek) + (c<<8);
else send = (0xff00&peek) + c;
VideoBuffer[half+y*120] = send;
}
}
typedef struct point_3D{
int x3d;
int y3d;
int z3d;
} point_3D;
typedef struct point_2D{
int x2d;
int y2d;
} point_2D;
point_3D cube_3D[8];
point_2D cube_2D[8];
static inline void CODE_IN_IWRAM DrawLine(int x1,int y1,int x2,int y2,unsigned short color){
int yStep = 240;
int xStep = 1;;
int xDiff = x2 - x1;
int yDiff = y2 - y1;
int errorTerm = 0;
int offset = y1 * 240 + x1;
int i;
if (yDiff < 0){
yDiff = -yDiff;
yStep = -yStep;
}
if (xDiff < 0){
xDiff = -xDiff;
xStep = -xStep;
}
if (xDiff > yDiff){
for (i = 0; i < xDiff; i++){
PlotPixel(offset%240,offset/240,color);
offset += xStep;
errorTerm += yDiff;
if (errorTerm >= xDiff){
errorTerm -= xDiff;
offset += yStep;
}
}
}
else {
for (i = 0; i < yDiff; i++){
PlotPixel(offset%240,offset/240,color);
offset += yStep;
errorTerm += xDiff;
if (errorTerm >= yDiff){
errorTerm -= yDiff;
offset += xStep;
}
}
}
}
void init_points_cube_3D(point_3D cube_3D[8],int z){
cube_3D[0].x3d = -100; cube_3D[0].y3d = 100 ; cube_3D[0].z3d = z-100 ;
cube_3D[1].x3d = 100 ; cube_3D[1].y3d = 100 ; cube_3D[1].z3d = z-100 ;
cube_3D[2].x3d = 100 ; cube_3D[2].y3d = 100 ; cube_3D[2].z3d = z+100 ;
cube_3D[3].x3d = -100; cube_3D[3].y3d = 100 ; cube_3D[3].z3d = z+100 ;
cube_3D[4].x3d = -100; cube_3D[4].y3d = -100 ; cube_3D[4].z3d = z-100 ;
cube_3D[5].x3d = 100 ; cube_3D[5].y3d = -100 ; cube_3D[5].z3d = z-100 ;
cube_3D[6].x3d = 100 ; cube_3D[6].y3d = -100 ; cube_3D[6].z3d = z+100 ;
cube_3D[7].x3d = -100; cube_3D[7].y3d = -100 ; cube_3D[7].z3d = z+100 ;
}
void cube_3D_2D(point_3D cube_3D[8], point_2D cube_2D[8],int x, int y){
cube_2D[0].x2d = (((cube_3D[0].x3d + x)*25600/cube_3D[0].z3d) + 12000)/100;
cube_2D[0].y2d =(8000- ((cube_3D[0].y3d + y)*25600/cube_3D[0].z3d))/100;
cube_2D[1].x2d = (((cube_3D[1].x3d + x)*25600/cube_3D[1].z3d) + 12000)/100;
cube_2D[1].y2d =(8000- ((cube_3D[1].y3d + y)*25600/cube_3D[1].z3d))/100;
cube_2D[2].x2d = (((cube_3D[2].x3d + x)*25600/cube_3D[2].z3d) + 12000)/100;
cube_2D[2].y2d =(8000- ((cube_3D[2].y3d + y)*25600/cube_3D[2].z3d))/100;
cube_2D[3].x2d = (((cube_3D[3].x3d + x)*25600/cube_3D[3].z3d) + 12000)/100;
cube_2D[3].y2d =(8000- ((cube_3D[3].y3d + y)*25600/cube_3D[3].z3d))/100;
cube_2D[4].x2d = (((cube_3D[4].x3d + x)*25600/cube_3D[4].z3d) + 12000)/100;
cube_2D[4].y2d =(8000- ((cube_3D[4].y3d + y)*25600/cube_3D[4].z3d))/100;
cube_2D[5].x2d = (((cube_3D[5].x3d + x)*25600/cube_3D[5].z3d) + 12000)/100;
cube_2D[5].y2d =(8000- ((cube_3D[5].y3d + y)*25600/cube_3D[5].z3d))/100;
cube_2D[6].x2d = (((cube_3D[6].x3d + x)*25600/cube_3D[6].z3d) + 12000)/100;
cube_2D[6].y2d =(8000- ((cube_3D[6].y3d + y)*25600/cube_3D[6].z3d))/100;
cube_2D[7].x2d = (((cube_3D[7].x3d + x)*25600/cube_3D[7].z3d) + 12000)/100;
cube_2D[7].y2d =(8000- ((cube_3D[7].y3d + y)*25600/cube_3D[7].z3d))/100;
}
void aff_cube_2D(point_2D cube_2D[8]){
DrawLine(cube_2D[0].x2d,cube_2D[0].y2d,cube_2D[1].x2d,cube_2D[1].y2d,1);
DrawLine(cube_2D[1].x2d,cube_2D[1].y2d,cube_2D[2].x2d,cube_2D[2].y2d,1);
DrawLine(cube_2D[2].x2d,cube_2D[2].y2d,cube_2D[3].x2d,cube_2D[3].y2d,1);
DrawLine(cube_2D[3].x2d,cube_2D[3].y2d,cube_2D[0].x2d,cube_2D[0].y2d,1);
DrawLine(cube_2D[4].x2d,cube_2D[4].y2d,cube_2D[5].x2d,cube_2D[5].y2d,1);
DrawLine(cube_2D[5].x2d,cube_2D[5].y2d,cube_2D[6].x2d,cube_2D[6].y2d,1);
DrawLine(cube_2D[6].x2d,cube_2D[6].y2d,cube_2D[7].x2d,cube_2D[7].y2d,1);
DrawLine(cube_2D[7].x2d,cube_2D[7].y2d,cube_2D[4].x2d,cube_2D[4].y2d,1);
DrawLine(cube_2D[0].x2d,cube_2D[0].y2d,cube_2D[4].x2d,cube_2D[4].y2d,1);
DrawLine(cube_2D[1].x2d,cube_2D[1].y2d,cube_2D[5].x2d,cube_2D[5].y2d,1);
DrawLine(cube_2D[2].x2d,cube_2D[2].y2d,cube_2D[6].x2d,cube_2D[6].y2d,1);
DrawLine(cube_2D[3].x2d,cube_2D[3].y2d,cube_2D[7].x2d,cube_2D[7].y2d,1);
}
int main(void) {
int i,j;
int x=0,y=0;
irqInit();
irqSet(IRQ_VBLANK, Vblank);
irqEnable(IRQ_VBLANK);
SetMode (MODE_4 | BG2_ENABLE) ;
BGPaletteMem[0]=RGB(0,0,0);
BGPaletteMem[1]=RGB(255,255,255);
while(1){
for (i=0;i<19200;i++)VideoBuffer[i]=0;
init_points_cube_3D(cube_3D,500);
cube_3D_2D(cube_3D,cube_2D,x,y);
aff_cube_2D(cube_2D);
init_points_cube_3D(cube_3D,600);
cube_3D_2D(cube_3D,cube_2D,x,y);
aff_cube_2D(cube_2D);
init_points_cube_3D(cube_3D,700);
cube_3D_2D(cube_3D,cube_2D,x,y);
aff_cube_2D(cube_2D);
keysDown();
scanKeys();
keys_pressed = keysDown();
keys_released = keysUp();
if (!(REG_KEYINPUT & KEY_DOWN) && y<80) y+=10;
if (!(REG_KEYINPUT & KEY_UP) && y>-80) y-=10;
if (!(REG_KEYINPUT & KEY_RIGHT) && x<80) x+=10;
if (!(REG_KEYINPUT & KEY_LEFT) && x>-80) x-=10;
flip_buffer();
}
return 0;
}
Je ne vois vraiment pas comment faire mieux que ça...
j'ai regardé vite fait,
tu peux largement optimiser la remise à zero de ton buffer, mais là une boucle de 19200 pour mettre à 0 les pixels, c'est trop. C'est le type meme de routine qu'il faut déboucler au maximum.
ta fonction plotpixel, j'ai vu que tu testais à chaque fois si tu plottais en dehors de l'écran, ça prend un temps fou. ça, ça doit etre fait dans l'algo de tracage de ligne, pas au moment de plotter le pixel.
->vaut mieux tester les 8 points de ton cube, plutot que les centaines de points qui vont composer les lignes du cube.
Donc, après la projection 2d, si 1 des points est en dehors, il te faudra faire ce qu'on apelle un clipping, c'est à dire trouver le point qui se trouvera à l'intersection sur le bord de l'écran. C'est les formules d'équations paramétriques. ça serait très facile à expliquer avec un bon croquis.:)
Si tu fais ça, l'avantage, c'est qu'aprés tu es sur que toutes tes lignes seront dans l'écran et tu n'auras plus de tests à faire après.
Bon, après le clipping de polygones, c'est un plus gros morceau, mais si tu piges le clipping de points, ça t'aidera pour la suite, mais c'est vraiment indispensable de la faire.
Bon, sinon tous tes calculs de projections peuvent largement etre optimisés, et sinon je reviens sur ce que je t'avais dit avant, si tu tiens absolument à garder ta fonction plotpixel, réécris là à l'intérieur de ta fonction drawlines, de façon à ce que ton cpu fasse pas des appels incéssants à cette fonction.
Quand t'auras déja modifié tous ça, tu verras que ça ira déja bcp plus vite.
ps: un autre truc, j'ai remarqué que ton code s'éxécutait pas depuis l'iwram, mais depuis la ram, bizarre, mais je laisse ça aux pros de la compilation du C.... :S
ps: un autre truc, j'ai remarqué que ton code s'éxécutait pas depuis l'iwram, mais depuis la ram, bizarre, mais je laisse ça aux pros de la compilation du C.... :SJe vais p'tre dire une betise, mais le fait que les fonctions taggees comme devant etre en iwram sont "inline" doit poser probleme : puisqu'elles sont "inline", le compilo copie le code directement a l'interieur du code de la fonction appelante... Puisque "aff_cube_2D" n'est pas en iwram, au final de code de "DrawLine" ne peut pas y etre, et donc celui de "PlotPixel" non plus...
Sinon, je suis evidemment d'accord avec tout ce qu'a dit MIKEGBA (faut dire qu'il domine son sujet hein ! B)) ; je rajouterais juste qu'il pourrait etre interessant :
- De clarifier le code en faisant un fonction de projection 3D-2D pour 1 point (plutot que de projeter tous les points de ton cube dans une seule fonction) ;
- De travailler avec des matrices pour permettre le placement de tes objets devant la camera (changement de repere).
Si ca t'interesse je dois encore avoir le code d'un p'tit moteur 3D que j'avais fait il y a quelque temps sur PC (heu... 8 ans deja !) ; j'avais des polygones textures (interpolation bi-lineaire), des vrais spheres texturees, et le tout avec z-buffer pour gerer les collisions. A la fin j'avais fait une sorte de 4x4 qui roulait sur un monde bossele (polygones pour la caisse, spheres applaties pour les roues, et voxel spacing pour le sol). L'executable est toujours dispo ici (http://www.nrx.free.fr/PC/Nrx_4x4/), mais il faut le lancer en mode DOS exculsif pour qu'il marche (= booter en mode DOS). Je vais essayer de remettre la main sur mon source :ph34r:.
Dr.Vince
16/09/2006, 14h14
Je vais p'tre dire une betise, mais le fait que les fonctions taggees comme devant etre en iwram sont "inline" doit poser probleme : puisqu'elles sont "inline", le compilo copie le code directement a l'interieur du code de la fonction appelante... Puisque "aff_cube_2D" n'est pas en iwram, au final de code de "DrawLine" ne peut pas y etre, et donc celui de "PlotPixel" non plus...
tout à fait d'accords avec toi Nrx
pour le reste je sais pas car je comprend rien à ce que vous racontez :p
Bon, je vais repartir de 0...
Quitte a faire ça, je pense que ça pourrais donner des idées de tutos a ceux capables d'en faire (même des "mini tutos", ou tout du moins des liens vers des texte clairs que l'ont pourrais exploiter facilement/traduire), on ne devrait pas avoir a dépendre de GBADev, nous sommes une communauté francophone après tout...
Donc pour résumer ce j'ai à faire et les infos qu'il me faudrait :
1) Faire une fonction de remplissage du buffer avec DMA ou CpuFastSet
> Un petit tuto français sur les DMA ?
> Une explication sur l'integration d'assembleur dans un code en C ? (le tuto sur l'assembleur de mikegba m'aurai surement aidé sur ce point, mais vu qu'il n'y a pas les sources, impossible de voir comme ça se met en place...)
2) Gestion de la 3D de base (points 3D>2D), position caméra, position écran
> Un petit tutos avec schémas pour mieux se représenter les choses ? Il y déjà mon sujet sur la représentation d'un cube en 2D sur l'ancien forum, mais l'abscence de visuel fait grandement défaut.
3) Gestion du tracé de ligne avec clipping
> Pas grand chose, juste des maths et de l'organisation dans le code...
Et on s'arrete là pour le moment.
Bien sur je pourrais me casser le cul a faire tout ça moi même en cherchant, mais il y a des pro de la 3D sur GBA parmis nous, alors autant les mettres a contribution....:rolleyes:
Avec l'avalanche de programmeurs qui n'utilise PA que comme un catalogue le coté entre aide est un peu trop mis de coté ces derniers temps...
Dr.Vince
16/09/2006, 15h34
hum.... ça me rappelle quelque chose tout ça
donc tout à fait d'accrod avec toi DJP
sinon Mikegba devrai nous sortir un tuto tout beau tout frais sur l'assembleur d'ici quelque temps
et sinon va voir sur l'ancien forul sur le topic du tuto il me semble que quelqu'un explique l'intégration de l'assembleur dans des sources C
Pour l'assembleur on trouve des infos sur Google en tapant "inline assembly gcc", sinon ca marche comme ca en gros:
asm ( "mov r0,%0\nswi 0xff" : : "r" ( txt ) ); // écrit txt dans le log de vba
Mais je me sens vraiment pas d'expliquer le coup des %0,%1, "r", et autres, donc je prefere donner le lien qui m'a permis de comprendre: http://www.ibiblio.org/gferg/ldp/GCC-Inline-Assembly-HOWTO.html
par exemple pour ça :
ldr r1, =VideoBuffer
ldr r0, =0x00000000
ldr r2, =0x1002580
swi 0xc0000
on obtiendrait quoi au final ?
par exemple pour ça :
ldr r1, =VideoBuffer
ldr r0, =0x00000000
ldr r2, =0x1002580
swi 0xc0000
on obtiendrait quoi au final ?
Il faudrait faire:
asm("mov r1, %0\n\
ldr r0, =0x00000000\n\
ldr r2, =0x1002580\n\
swi 0xc0000\n" : : "r" ( VideoBuffer ) );
Voire plutot (si le code est en Iwram, donc mode ARM) :
#ifdef __thumb__
asm("mov r1, %0\n\
ldr r0, =0x00000000\n\
ldr r2, =0x1002580\n\
swi 0xc\n" : : "r" ( VideoBuffer ) );
#else
asm("mov r1, %0\n\
ldr r0, =0x00000000\n\
ldr r2, =0x1002580\n\
swi 0xc << 16\n" : : "r" ( VideoBuffer ) );
#endif
Et logiquement ca devrait bien mettre ce VideoBuffer à zéro. Mais j'ai pas testé.
Si tu as beaucoup de code assembleur, la meilleure solution est de le placer dans un fichier différent (.s) et de le compiler à part.
il n'y aurai pas un probleme avec les parenthèses ?
Et il ne faudrait pas un truc genre
.arm
.align 2
quelquepart ?
Effectivement je me suis raté avec les parenthèses :D
Sinon pour les instructions avec des . c'est dans les fichier .s justement.
yop,
petit source en arm , à mettre en iwram pour vider le buffer et flipper l'écran.
sinon, pour la fonction du bios 0x0c, r0 contient l'adresse source, r1 l'adresse de destination et r2 la taille du transfert.
si le bit 24 de r1 est mis à 1, alors r0 devient la valeur qui remplit la destination.
cette fonction de bios marche, mais c'est assez lent, j'avais mesuré le temps quelque part, je vous redirais.
/**************************************/
/* */
/* EFFACE L'ECRAN ET FLIPPE LE BUFFER */
/* ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ */
/* */
/**************************************/
.align
EFFACE_ECRAN: Stmfd sp!,{r14}
ldr r2,Adr_1_MEMOIRE_VIDEO /* flip l'image */
ldr r4,[r2]
MOV r0,#AdresseRamVideo
cmp r4,r0
addeq r0,r0,#0xa000
str r0,[r2]
ldr r1,Adr_1_REG_DISPCNT
ldrh r2,[r1]
eor r2,r2,#0b10000
strh r2,[r1]
mov r14,#25
mov r1,#0
mov r2,r1
mov r3,r2
mov r4,r2
mov r5,r2
mov r6,r2
mov r7,r2
mov r8,r2
mov r9,r2
mov r10,r2
mov r11,r2
mov r12,r2
loop_efface: stmia r0!,{r1-r12}
stmia r0!,{r1-r12}
stmia r0!,{r1-r12}
stmia r0!,{r1-r12}
stmia r0!,{r1-r12}
stmia r0!,{r1-r12}
stmia r0!,{r1-r12}
stmia r0!,{r1-r12}
stmia r0!,{r1-r12}
stmia r0!,{r1-r12}
stmia r0!,{r1-r12}
stmia r0!,{r1-r12}
stmia r0!,{r1-r12}
stmia r0!,{r1-r12}
stmia r0!,{r1-r12}
stmia r0!,{r1-r12}
stmia r0!,{r1-r12}
stmia r0!,{r1-r12}
stmia r0!,{r1-r12}
stmia r0!,{r1-r12}
stmia r0!,{r1-r12}
stmia r0!,{r1-r12}
stmia r0!,{r1-r12}
stmia r0!,{r1-r12}
stmia r0!,{r1-r12}
stmia r0!,{r1-r12}
stmia r0!,{r1-r12}
stmia r0!,{r1-r12}
stmia r0!,{r1-r12}
stmia r0!,{r1-r12}
stmia r0!,{r1-r12}
stmia r0!,{r1-r12}
subs r14,r14,#1
bne loop_efface
ldmfd sp!,{r15}
Adr_1_REG_DISPCNT: .word REG_DISPCNT
Adr_1_MEMOIRE_VIDEO: .word MEMOIRE_VIDEO
Comment est-ce que l'on inclus un fichier assembleur .s tel que celui-ci ?
Tu le rajoutes à ton makefile.
Et tu déclares la routine:
.global EFFACE_ECRAN
Et en C tu fais dans un .h que tu inclus:
void EFFACE_ECRAN();
main.c :
===============================================
u16 *VideoBuffer = (u16*)0x6000000;
asm.s :
===============================================
.global EFFACE_ECRAN
.align
ADR_REG_DISPCNT: .word 0x4000000
EFFACE_ECRAN: Stmfd sp!,{r14}
ldr r2,=VideoBuffer
ldr r4,[r2]
MOV r0,#0x6000000
cmp r4,r0
addeq r0,r0,#0xa000
str r0,[r2]
ldr r1,ADR_REG_DISPCNT
ldrh r2,[r1]
eor r2,r2,#0b10000
strh r2,[r1]
mov r14,#25
mov r1,#0
mov r2,r1
mov r3,r2
mov r4,r2
mov r5,r2
mov r6,r2
mov r7,r2
mov r8,r2
mov r9,r2
mov r10,r2
mov r11,r2
mov r12,r2
loop_efface: stmia r0!,{r1-r12}
stmia r0!,{r1-r12}
stmia r0!,{r1-r12}
stmia r0!,{r1-r12}
stmia r0!,{r1-r12}
stmia r0!,{r1-r12}
stmia r0!,{r1-r12}
stmia r0!,{r1-r12}
stmia r0!,{r1-r12}
stmia r0!,{r1-r12}
stmia r0!,{r1-r12}
stmia r0!,{r1-r12}
stmia r0!,{r1-r12}
stmia r0!,{r1-r12}
stmia r0!,{r1-r12}
stmia r0!,{r1-r12}
stmia r0!,{r1-r12}
stmia r0!,{r1-r12}
stmia r0!,{r1-r12}
stmia r0!,{r1-r12}
stmia r0!,{r1-r12}
stmia r0!,{r1-r12}
stmia r0!,{r1-r12}
stmia r0!,{r1-r12}
stmia r0!,{r1-r12}
stmia r0!,{r1-r12}
stmia r0!,{r1-r12}
stmia r0!,{r1-r12}
stmia r0!,{r1-r12}
stmia r0!,{r1-r12}
stmia r0!,{r1-r12}
stmia r0!,{r1-r12}
subs r14,r14,#1
bne loop_efface
ldmfd sp!,{r15}
Ca devrait marcher non ? Bah non ça réagit pas du tout comme il faut...
Y te manque un "bx lr" à la fin de la fonction asm, pour que tu puisse revenir dans le programme principal.
Et la sauvegarde des registres aussi (mike n'en a pas besoin mais GCC oui) ^^
Essaie ca donc:
.global EFFACE_ECRAN
.align
ADR_REG_DISPCNT: .word 0x4000000
EFFACE_ECRAN: stmia sp!,{r0-r14} @ sauvegarde des registres
ldr r2,=VideoBuffer
ldr r4,[r2]
MOV r0,#0x6000000
cmp r4,r0
addeq r0,r0,#0xa000
str r0,[r2]
ldr r1,ADR_REG_DISPCNT
ldrh r2,[r1]
eor r2,r2,#0b10000
strh r2,[r1]
mov r14,#25
mov r1,#0
mov r2,r1
mov r3,r2
mov r4,r2
mov r5,r2
mov r6,r2
mov r7,r2
mov r8,r2
mov r9,r2
mov r10,r2
mov r11,r2
mov r12,r2
loop_efface: stmia r0!,{r1-r12}
stmia r0!,{r1-r12}
stmia r0!,{r1-r12}
stmia r0!,{r1-r12}
stmia r0!,{r1-r12}
stmia r0!,{r1-r12}
stmia r0!,{r1-r12}
stmia r0!,{r1-r12}
stmia r0!,{r1-r12}
stmia r0!,{r1-r12}
stmia r0!,{r1-r12}
stmia r0!,{r1-r12}
stmia r0!,{r1-r12}
stmia r0!,{r1-r12}
stmia r0!,{r1-r12}
stmia r0!,{r1-r12}
stmia r0!,{r1-r12}
stmia r0!,{r1-r12}
stmia r0!,{r1-r12}
stmia r0!,{r1-r12}
stmia r0!,{r1-r12}
stmia r0!,{r1-r12}
stmia r0!,{r1-r12}
stmia r0!,{r1-r12}
stmia r0!,{r1-r12}
stmia r0!,{r1-r12}
stmia r0!,{r1-r12}
stmia r0!,{r1-r12}
stmia r0!,{r1-r12}
stmia r0!,{r1-r12}
stmia r0!,{r1-r12}
stmia r0!,{r1-r12}
subs r14,r14,#1
bne loop_efface
ldmia sp!, {r0-r14} @ restauration des registres
bx lr @ retour
[HS] Il m'a fallu 2 heures pour comprendre ce que ce code fait:
ldr r1,ADR_REG_DISPCNT
ldrh r2,[r1]
eor r2,r2,#0b10000
strh r2,[r1]
Y'a pas à dire le C est quand même plus clair :p
ça ne marche pas. J'arrive même pas a comprendre ce qui se passe, il flip le buffer tous les 36 du mois, il ne clean pas, et impossible de changer le contenu du buffer après le premier appel...
while(1){
init_points_cube_3D(cube_3D,500);
cube_3D_2D(cube_3D,cube_2D,x,y);
aff_cube_2D(cube_2D);
EFFACE_ECRAN();
y+=10;
}
(y c'est le déplacement vertical du point de vue)
Et un joli warning :
c:/devkitPro/examples/gba/3D/source/assembleur.s: Assembler messages:
c:/devkitPro/examples/gba/3D/source/assembleur.s:6: Warning: if writeback register is in list, it must be the lowest reg in the list
c:/devkitPro/examples/gba/3D/source/assembleur.s:69: Warning: writeback of base register when in register list is UNPREDICTABLE
Tu sauves/restaures r13, c'est pour ça que tu as ce warning. Enlève-le de la liste (style {r0-r12, r14}).
rom + sources...
pourtant avec un memset, no prob...
rom + sources...
pourtant avec un memset, no prob...
yop !!
viens de voir en vitesse le post !
j'te modofie le source pour mardi, mais en gros tu n'attends pas la vbl pour appeler la fonction EFFACEECRAN, le buffer est bien flippé en hardware, mais ta variable en c videobuffer change jamais, donc c'est toujours le frontbuffer qui est écrit, jamais le backbuffer, c'est pour ça que ça clignotte...
Sinon, l'intégration du source asm dans le c, c'est bon.
je te file ça mardi, tu vas voire, ça va gazer !!
à plus !:)
Génial.
Vu que le memset fonctionne en attendant, je commence comme le remplissage de triangle :p
Conseil de lecture : Programmation Graphique C/C++/Asm, préfacé par John Carmack, editions Sybex.
Autant l'asm pour x86 on peut s'en passer autant il y a beaucoup de théorie et d'explications TRES bien venues
:) yop......
Alors DJP, ci dessous le nouveau code asm que tu dois changer pour la fonction efface ecran:
.global EFFACE_ECRAN
.align
ADR_REG_DISPCNT: .word 0x4000000
EFFACE_ECRAN: stmia sp!,{r0-r12, r14} @ sauvegarde des registres
ldr r1,ADR_REG_DISPCNT
ldrh r2,[r1]
eor r3,r2,#0b10000
strh r3,[r1]
mov r0,#0x6000000
ands r4,r2,#0b10000
addne r0,r0,#0xa000
mov r14,#25
mov r1,#0
mov r2,r1
mov r3,r2
mov r4,r2
mov r5,r2
mov r6,r2
mov r7,r2
mov r8,r2
mov r9,r2
mov r10,r2
mov r11,r2
mov r12,r2
loop_efface: stmia r0!,{r1-r12}
stmia r0!,{r1-r12}
stmia r0!,{r1-r12}
stmia r0!,{r1-r12}
stmia r0!,{r1-r12}
stmia r0!,{r1-r12}
stmia r0!,{r1-r12}
stmia r0!,{r1-r12}
stmia r0!,{r1-r12}
stmia r0!,{r1-r12}
stmia r0!,{r1-r12}
stmia r0!,{r1-r12}
stmia r0!,{r1-r12}
stmia r0!,{r1-r12}
stmia r0!,{r1-r12}
stmia r0!,{r1-r12}
stmia r0!,{r1-r12}
stmia r0!,{r1-r12}
stmia r0!,{r1-r12}
stmia r0!,{r1-r12}
stmia r0!,{r1-r12}
stmia r0!,{r1-r12}
stmia r0!,{r1-r12}
stmia r0!,{r1-r12}
stmia r0!,{r1-r12}
stmia r0!,{r1-r12}
stmia r0!,{r1-r12}
stmia r0!,{r1-r12}
stmia r0!,{r1-r12}
stmia r0!,{r1-r12}
stmia r0!,{r1-r12}
stmia r0!,{r1-r12}
subs r14,r14,#1
bne loop_efface
ldmia sp!, {r0-r12, r14} @ restauration des registres
bx lr @ retour
Ce qui change dans ce code, c'est que je ne me sers plus de ta variable videobuffer, je teste directement le registre DISCPNT, pour savoir quel buffer était affiché, et donc quel buffer il faut effacer.
La fonction flippe toujours les buffers, et efface celui qui vient d'etre caché.
Par contre, il est important que dans ton source en C, tu attende bien la vbl pour appeler la fonction, et que SURTOUT tu changes ta variable en C VideoBuffer avec un code du genre:
if ( VideoBuffer = FrontBuffer )
VideoBuffer = BackBuffer;
else
VideoBuffer = FrontBuffer;
Bon, sinon, pour que la routine ait la patate, mets la en Iwram.
à plus !
Genial, merci.
J'ai terminé une methode "simple" de remplissage de triangle (coefficient directeur des cotes...), je vais maintenant chercher a améliorer la vitesse de plot...
Genial, merci.
J'ai terminé une methode "simple" de remplissage de triangle (coefficient directeur des cotes...), je vais maintenant chercher a améliorer la vitesse de plot...
Ca m'intéresserait comme algorithme. Tu pourrais m'expliquer ta méthode, stp ? Parce qu'il y a beaucoup de doc sur le parcours des polygones, mais pas des triangles (et c'est débile de faire un parcours de poly juste pour un triangle :S )
hum pour faire simple :
1) calcul du point le plus haut, du point le plus bas, du point du milieu.
2) calcul de l'équation de la droite "haut/milieu" et "haut/bas"
3) tracages des lignes horizontales entre la droite "haut/milieu" et "haut/bas", depuis le haut jusqu'au milieu.
4) calcul de l'équation de la droite "bas/milieu" et "haut/bas"
5) tracages des lignes horizontales entre la droite "bas/milieu" et "haut/bas", depuis le bas jusqu'au milieu.
je ne sais pas si c'est très clair...
OK merci ! En gros, tu "parcours" deux droites et tu les "relies" à la troisième. Bon bah, je vais m'y mettre :lol: .
vBulletin® v.3.7.2, Copyright ©2000-2008, Jelsoft Enterprises Ltd. Tous droits réservés - Version française vbulletin-fr.org