1

Voila, je voudrais faire plusieurs routines dont des routines de sprites 8*8, 32*32 avec des boucles complètement déroulées en utilisant des longs mais je ne sais pas bien coment faire.
Le meilleur résultat que j'obtiens est en faisant ceci :


int deca1 = y-((y/4)*4);
int deca2 = 4-deca1;
j = 8;
			while(j--)
            {
				*(dest_long) = (*(sprite_long) << (deca1*8));
				*(dest_long+1) = (*(sprite_long) >> (deca2*8));
				dest_long++;
				sprite_long++;

			}
			dest_long = dest_long + (240)/4 - 8;


Mais à mon avis il faut un opérateur (je ne sais pas lequel) et puis il doit y avoir autre chose qui foire.
www.wikio.fr/user1921&info=comments

2

-

3

ton *(dest_long+1) il est ecrase au tour d'apres par *(dest_long). C'est comme si cette ligne ne servait pas (sauf pour la derniere serie de pixels)
Le pouvoir aux loutres !!!
(et aussi, vive le rose !)
mes petits programmes GP32: http://yaouank.gp32news.com

4

Pour pouvoir lire ou ecrire dans la memoire en utilisant des mots de 32 bits il faut que l'adresse de ce mot soit alignée sur 32bits.

Si on parle de coordonées de pixels 16bits ca revient a dire que la coordonée X doit etre paire..

Enfin... On entre dans l'epineux probleme des lectures/ecritures non alignées...

Sinon, pour répondre a ta question, le moyen le plus efficace d'afficher un tile (sprite carré sans transparence) est d'utiliser l'asm, et les instructions ldmia et stmia que le compilo semble ne pas connaitre.

Ces instructions permettent de charges n registres avec des adresses consecutives en memoire ou d'ecrire ces n registres a des adresses elles aussi consecutives.

En gros

*src++ = *dst++;
*src++ = *dst++;
*src++ = *dst++;
*src++ = *dst++;
*src++ = *dst++;
*src++ = *dst++;
*src++ = *dst++;
*src++ = *dst++;

est remplacé en asm par :

ldmia r0!,{r4-r11}
stmeqia r1!,{r4-r11}

(r0 = src, r1 = dst)

Tu peux t'arranger pour que ton tile soit stocké a une adresse alignée sur 32bits, et comme ses dimensions sont des puissances de 2 tu ne rencontrera aucun pb d'alignement pour la lecture, par contre pour l'ecriture il faut gerer deux cas : ecriture alignée, et ecriture decalée d'un pixel (pixels impairs).

Pour gerer ce cas la, la solution la plus efficace semble etre de garder les ldmia stmia et de faire la rotation des pixels dans tous les registres entre le chargement et l'ecriture...

Oui je sais c'est plus facile a dire qu'a faire...














developpeur de jeux mobiles chez int13 production --- http://int13.net

5

Oui mais je ne me défais pas de cette habitude ! grin
Par contre il y a u n truc que le compilo ne fera pas, c'est dérouler une boucle à fond et correctement. Avec cette routine peut-être mais pas celle là :
void Water_Effects(Camera *cam,Map *map)
{

if(TICKS > WATER_LAST_TICKS + WATER_SPEED)
{
WAVE++;
if(WAVE>2)
WAVE=-2;
WATER_LAST_TICKS = TICKS;
}

short wave=WAVE;
short x;
short y;
short x_sprite;
short y_sprite;
unsigned short *data= map->data;
unsigned char *sprite_addr=NULL;
short x_tile_pos = (cam->coord.x/32);
short y_tile_pos = (cam->coord.y/32);
short x_tile_offset = cam->coord.x - (x_tile_pos*32);
short y_tile_offset = cam->coord.y - (y_tile_pos*32);
short i;
short j;
unsigned char *p_buf = (unsigned char *) (gpDraw[nflip].ptbuffer);
unsigned char *p_buf_dest;
for(x=x_tile_pos;x<11+x_tile_pos;x++)
{
for(y=y_tile_pos;y<9+y_tile_pos;y++)
{
if((*(data+y*map->width+x))==1)
{
int x_dest;
short y_dest;
x_sprite = ((x-x_tile_pos)*32)-x_tile_offset;
y_sprite = ((y-y_tile_pos)*32)-y_tile_offset;

y_dest = y_sprite + 32;


int x_sprite_pre = 240*x_sprite;
for(j=y_sprite;j<y_sprite+32;j++)
{
y_dest=y_dest-1;
if(wave==2)
wave=-2;
wave++;


if(y_dest>=0 && y_dest<240)
{


unsigned char *p_buf_src = (p_buf +(239-(j-32))+ x_sprite_pre);
unsigned char *p_buf_dest = (p_buf + (239-y_dest)+ 240*wave + x_sprite_pre);

i=x_sprite;


while(i<x_sprite+32)
{
if(i<320 && i>0)
*(p_buf_dest) = *(p_buf_src);
if(i<318 && i>-2)
*(p_buf_dest + 480) = *(p_buf_src + 480);
if(i<316 && i>-4)
*(p_buf_dest + 960) = *(p_buf_src + 960);
if(i<314 && i>-6)
*(p_buf_dest + 1440) = *(p_buf_src + 1440);
if(i<312 && i>-8)
*(p_buf_dest + 1920) = *(p_buf_src + 1920);
if(i<310 && i>-10)
*(p_buf_dest + 2400) = *(p_buf_src + 2400);
if(i<308 && i>-12)
*(p_buf_dest + 2880) = *(p_buf_src + 2880);
if(i<306 && i>-14)
*(p_buf_dest + 3360) = *(p_buf_src + 3360);

p_buf_dest = p_buf_dest + 3840;
p_buf_src = p_buf_src + 3840;

i=i+16;

}
}

}

}

}

}

}

Celui qui arrive à faire plus rapide avec du code pas optimisé et ben il est vraiment fort.


Avec des benchs j'ai vu que c'était plus efficace d'optimiser à la main.
... et puis de toute manière je n'arrive plus à programmer de manière lisible, il faudra que je me force.
www.wikio.fr/user1921&info=comments

6

Sinon, pour répondre a ta question, le moyen le plus efficace d'afficher un tile (sprite carré sans transparence) est d'utiliser l'asm

Oui mais j'ai jamais fait d'asm. C'est vrai que c'est peut-être plus efficace mais je préfère d'abord comprendre comment faire en C, ça sera déjà pas mal.
et les instructions ldmia et stmia que le compilo semble ne pas connaitre.

Peut-être que ARM sdt ou ADS les connaisses... bon à près il faut qu'il sache les utiliser, mais c'est vrai que GCC ne les connais pas toutes. grin
www.wikio.fr/user1921&info=comments

7

sinon pour ton
*(dest_long) = (*(sprite_long) << (deca1*8)); 
*(dest_long+1) = (*(sprite_long) >> (deca2*8));

il faudrait plutot mettre un truc de cette forme
*(dest_long) = (*(sprite_long) << (deca1*8)) + *(dest_long) & (1 <<(deca1*8+1)-1); 
*(dest_long+1) = (*(sprite_long) >> (deca2*8)) + *(dest_long+1) & (1 <<(deca2*8+1)-1);


EDIT: c'est faux mais en gros t'as vaguement l'idee
Le pouvoir aux loutres !!!
(et aussi, vive le rose !)
mes petits programmes GP32: http://yaouank.gp32news.com

8

-

9

Ah ouais d'accord merci, je vois mieux à quoi ça doit ressembler. Bon, c'est pas gagné ! grin
www.wikio.fr/user1921&info=comments

10

Il faut vraiment faire attention quand on deroule des boucles, l'effet contraire a celui souhaité peut vite se produire.

Si on deroule vraiment trop la boucle il faudra plus de temps pour la charger dans le cache et le bench va indiquer des perfs plus mauvaises qu'une boucle moins deroulée.

MAIS, meme si la boucle deroulée est benchée plus rapide il ne faut pas oublier l'impact global sur le reste du soft, si le cache doit etre entierement vidé pour charger cette routine le bench sera peut etre bon mais le reste du soft devra se recharger dans le cache pour s'executer...

C'est evidement tres difficile a mesurer....

La prudence dicte de ne derouler les boucles que lordsque c'est vraiment important et de ne pas derouler des monstres....


developpeur de jeux mobiles chez int13 production --- http://int13.net

11

Il faut vraiment faire attention quand on deroule des boucles, l'effet contraire a celui souhaité peut vite se produire.

Merci de me mettre au courant, parce-que je ne le savais pas du tout.
Jusqu'à maintenant je n'ai pas eu l'effet inverse autant dans le jeux qu'avec les benchs.
En gros il vaut mieux que j'utilise les fonctions du sdk ? grin

Ce que je vais faire c'est que je vais les dérouler suffisament pour que ça soit plus rapide que les routines du sdk et puis c'est tout.
www.wikio.fr/user1921&info=comments

12

" Par contre il y a u n truc que le compilo ne fera pas, c'est dérouler une boucle à fond et correctement" -O3

Oui c'est ce que je dis ! En déroulant la boucle en -O3 4 ou 8 fois à la main ça allait plus vite avec ma routine, qu'en la laissant pas dérouler (en -O3 toujours).
www.wikio.fr/user1921&info=comments

13

Tu connais peut etre le truc du "Duffs device" ?

Tres pratique pour derouler des petits bouts de code, l'ideal etant d'en faire une macro, et de pouvoir l'activer ou la desactiver par un simple switch de compilation.

Facile ensuite de mesurer les perfs avec et sans.


developpeur de jeux mobiles chez int13 production --- http://int13.net

14

Tu connais peut etre le truc du "Duffs device" ?

Non je ne connais pas.
Facile ensuite de mesurer les perfs avec et sans.

Ouais. Parce-que c'est que dans le jeux que l'ont peut voir si le fait de dérouler les boucle fini par ralentir.
Par exemple si je veux faire une lib utilisable par tous (c'est qu'un exemple grin), il ne faut pas que je les déroule ? Ou alors si je veux réutiliser les mêmes routines pour un autre jeu ce sera pareil ?

Je ne savais pas que c'étais si compliqué. sad
*(dest_long) = (*(sprite_long) << (deca1*8)) + *(dest_long) & (1 <<(deca1*8+1)-1); *(dest_long+1) = (*(sprite_long) >> (deca2*8)) + *(dest_long+1) & (1 <<(deca2*8+1)-1);

C'est aussi long que ça ?



Parce-que quand j'utilise cette ligne tout seule : *(dest_long) = (*(sprite_long) << (deca1*8)); j'ai l'impression qu'il y en a la moitié de fait. Quand je tombe sur un multiple de 4 tout est afficher (on pourrais croire que la routine marche) et puis après il manque de 1 à 3 pixels suivant y.
ton *(dest_long+1) il est ecrase au tour d'apres par *(dest_long). C'est comme si cette ligne ne servait pas (sauf pour la derniere serie de pixels)

En utilisant un opérateur (pour ne pas écraser ce qui a été écrit avant) ça ne corrige pas le problème ?
www.wikio.fr/user1921&info=comments

15

Raphaël
:
*(dest_long) = (*(sprite_long) << (deca1*8)) + *(dest_long) & (1 <<(deca1*8+1)-1); *(dest_long+1) = (*(sprite_long) >> (deca2*8)) + *(dest_long+1) & (1 <<(deca2*8+1)-1);

C'est aussi long que ça ?

Pour corriger ton code afin qu'il fonctionne correctement, oui c'est aussi long que ca.
Mais les deux masques (1 <<(deca1*8+1)-1) et son complementaire (1 <<(deca2*8+1)-1) peuvent etre precalcules. voire codes en dur du style
switch (decal1)
{
case 0:
mask1=0xFFFF; mask2=0x0000; break;
case 1:
mask1=0x0FFF; mask2=0xF000; break;
case 2:
mask1=0x00FF; mask2=0xFF00; break;
case 3:
mask1=0x000F; mask2=0xFFF0; break;
}


P.S: les lignes de code que je donne comme ca n'ont pas ete testees. C'est juste pour que tu aies l'idee. Il y a surement encore quelques details a corriger dedans
Le pouvoir aux loutres !!!
(et aussi, vive le rose !)
mes petits programmes GP32: http://yaouank.gp32news.com

16

Ok, merci beaucoup yaouank ! smile Je vais regarder tout ça.

Bon voilà la routine :

void Fast_Draw_Sprite_32(unsigned char *sprite, unsigned int x, unsigned int y)
{
		short i,j;



        int deca1 = y-((y/4)*4);
        int deca2 = 4-deca1;



        char DEC[10];
        /*
		gp_str_func.sprintf(&DEC,"%d",deca1);
		GpTextOut( NULL,&gpDraw[nflip],20,20,DEC,0x00);
		*/

		unsigned long *sprite_long = sprite;
		unsigned long *dest_long =  gpDraw[nflip].ptbuffer + (240*x + (239-y-31));
		i = 32;
		while(i--)
		{
			j = 8;
			while(j--)
            {

				*(dest_long) = (*(sprite_long) << (deca1*8)) + *(dest_long) & (1 <<(deca1*8+1)-1);
                *(dest_long+1) = (*(sprite_long) >> (deca2*8)) + *(dest_long+1) & (1 <<(deca2*8+1)-1);
				dest_long++;
				sprite_long++;

			}
			dest_long = dest_long + (240)/4 - 8;

		}

}


C'est assez loin de marché parce-que les couleurs sont bleu et noir avec des sprite verts et le sprite est mal décalé.
www.wikio.fr/user1921&info=comments

17

Bon, bah c'est pas la peine, je n'arrive pas du tout à corriger la routine. sad
www.wikio.fr/user1921&info=comments

18

tu aurais gagne a utiliser mask1 et mask2. Si si ! (quand je te disais que je t'avais pondu ca vite fait, que c'etait faux, que c'etait juste pour te donner l'idee, je ne mentais pas expres pour te forcer a le refaire. C'etait vraiment faux)
*(dest_long) = (*(sprite_long) << (deca1*8)) + *(dest_long) & mask1; 
*(dest_long+1) = (*(sprite_long) >> (deca2*8)) + *(dest_long+1) & mask2; 

Ensuite decal = y-((y/4)*4) ca equivaut pas a decal = y%4 ? (ou a decal = y&0x03; si t'as envie de te la peter grave)

EDIT: je viens de regarder vite fait. Je crois que mask1 et mask2 sont encore faux. A toi de trouver les bonnes valeurs.
Le pouvoir aux loutres !!!
(et aussi, vive le rose !)
mes petits programmes GP32: http://yaouank.gp32news.com

19

Allez, finalement j'ai trouve vite fait, ca te fera ca de moins a chercher. Je pense que ca doit plutot etre comme ca
switch (decal1) 
{ 
case 0: 
mask1=0x0000; mask2=0xFFFF; break; 
case 1: 
mask1=0x000F; mask2=0xFFF0; break; 
case 2: 
mask1=0x00FF; mask2=0xFF00; break; 
case 3: 
mask1=0x0FFF; mask2=0xF000; break; 
}
Le pouvoir aux loutres !!!
(et aussi, vive le rose !)
mes petits programmes GP32: http://yaouank.gp32news.com

20

Ensuite decal = y-((y/4)*4) ca equivaut pas a decal = y%4 ? (ou a decal = y&0x03; si t'as envie de te la peter grave)

Orion_ m'avais dit y&3 il me semble. Mais c'est pour que le code soit plus clair. grin
tu aurais gagne a utiliser mask1 et mask2

Oui je sais mais ça ne marchais pas non plus.

Et tu penses que c'est bon ? Bon de toute manière je vais essayer. Merci !
www.wikio.fr/user1921&info=comments

21

C'est toujours pas ça sad
Le plus près de la vérité serait :

void Fast_Draw_Sprite_32(unsigned char *sprite, unsigned int x, unsigned int y)
{

	short i,j;



        int deca1 = y&3;
        int deca2 = 4-deca1;

        unsigned long mask1=0;
        unsigned long mask2=0;

        switch (deca1)
		{
		case 0:
		mask1=0x0000; mask2=0xFFFF; break;
		case 1:
		mask1=0x000F; mask2=0xFFF0; break;
		case 2:
		mask1=0x00FF; mask2=0xFF00; break;
		case 3:
		mask1=0x0FFF; mask2=0xF000; break;
        }


/*
        char DEC[10];

		gp_str_func.sprintf(&DEC,"%d",deca1);
		GpTextOut( NULL,&gpDraw[nflip],20,20,DEC,0x00);
*/

		unsigned long *sprite_long = sprite;
		unsigned long *dest_long =  gpDraw[nflip].ptbuffer + (240*x + (239-y-31));
		i = 32;
		while(i--)
		{
			j = 8;
			while(j--)
                     {


                                *(dest_long) = (*(sprite_long) << (deca1*8)) + *(dest_long) & mask2;
                               *(dest_long+1) = (*(sprite_long) >> (deca2*8)) + *(dest_long+1) & mask1;

				dest_long++;
				sprite_long++;

			}
			dest_long = dest_long + (240)/4 - 8;

		}

}
www.wikio.fr/user1921&info=comments

22

t'as inverse mask1 et mask2 dans ta boucle
Le pouvoir aux loutres !!!
(et aussi, vive le rose !)
mes petits programmes GP32: http://yaouank.gp32news.com

23

t'as inverse mask1 et mask2 dans ta boucle

Oui parce-que comme je l'ai dis c'est comme ça que ça marche le mieux.
www.wikio.fr/user1921&info=comments

24

Raaaahhhh, je vais devenir fou !
Comme beaucoup, j'essaye de faire une fonction qui affiche un bloc en 16x16 sans transparence et sans clipping. Histoire d'accéler un peu les choses, je cherche à passer par des accès 32bits et.... ça foire : la console redémarre.

voilà le source coupable (mais pour des blocs de 8x8):

void GpTileBltLong(GPDRAWSURFACE * _dest,unsigned short _x,unsigned short _y,unsigned char * _src)
{
unsigned long * dest_adr=(unsigned long*)((_dest->ptbuffer)+(240*_x+232-_y));
unsigned long * src_adr=(unsigned long *)_src;
unsigned char j=0;
for(j=0;j<8;j++)
{
*(dest_adr++)=*(src_adr++);
*(dest_adr++)=*(src_adr++);
dest_adr=dest_adr+58;
}
}


Ce qui m'agace, c'est qu'en remplacent *(src_adr+++) par une valeur (au hasard 0xFFFFFFFF), ça passe nickel.
Si quelqu'un voit ce qui coince avec l'adressage des données à afficher, je suis preneur car je coince complètement.

edit : après modif, j'ai transformé mon tile en tableau de unsigned long au lieu de tableau de unsigned char -> ça fonctionne mais je ne vois pas pourquoi la logique précédente ne passe pas
avatar

25

requiem :
edit : après modif, j'ai transformé mon tile en tableau de unsigned long au lieu de tableau de unsigned char -> ça fonctionne mais je ne vois pas pourquoi la logique précédente ne passe pas

Je te propose "l'alignement memoire"
Si ton tableau commence a une adresse impaire et que tu lis un 16 ou un 32bits a cette adresse, ca risque de ne pas marcher exactement comme tu veux.

D'ailleurs, ta routine ne deconne-t-elle pas quand tu lui donnes une valeure impaire pour y ?
Le pouvoir aux loutres !!!
(et aussi, vive le rose !)
mes petits programmes GP32: http://yaouank.gp32news.com

26

Il n'y a pas d'alignement automatique ??? Hum, ça doit venir de là alors. Je vais essayer de voir un peu l'adresse de mon tile.
J'étais persuadé que chaque nouvelle zone déclarée était alignée correctement.
Concernant ma routine, je ne donne jamais d'adresse impaire en paramètre : tous les tiles ont une adresse multiple de 8 (ou 16 en fct de la taille du tile).

avatar

27

Je ne suis toujours pas arrivé à faire une routine de sprite 32*32 avec des longs, alors si l'un d'entre vous a une routine qui marche , est-ce que vous pourriez la poster dans ce topic ?
www.wikio.fr/user1921&info=comments

28

requiem :
Il n'y a pas d'alignement automatique ??? Hum, ça doit venir de là alors. Je vais essayer de voir un peu l'adresse de mon tile.
J'étais persuadé que chaque nouvelle zone déclarée était alignée correctement.

C'etait juste une suggestion. Pour l'instant je ne vois que ca qui puisse expliquer un tel comportement.
Le pouvoir aux loutres !!!
(et aussi, vive le rose !)
mes petits programmes GP32: http://yaouank.gp32news.com