forums.ps2dev.org Forum Index forums.ps2dev.org
Homebrew PS2, PSP & PS3 Development Discussions
 
 FAQFAQ   SearchSearch   MemberlistMemberlist   UsergroupsUsergroups   RegisterRegister 
 ProfileProfile   Log in to check your private messagesLog in to check your private messages   Log inLog in 

sceGuTexImage is slow!

 
Post new topic   Reply to topic    forums.ps2dev.org Forum Index -> PSP Development
View previous topic :: View next topic  
Author Message
Brunni



Joined: 08 Oct 2005
Posts: 186

PostPosted: Sun Nov 27, 2005 9:21 pm    Post subject: sceGuTexImage is slow! Reply with quote

Hello.

I'm doing some tests for 2D sprite drawing. But the sceGuTexImage call (before drawing vertices) is very slow because it copies the texture to another place in VRAM (even if my image is already in VRAM...).

I would not need to copy my sprites at each frame, but as I can't choose where the texture is copied, I must do it for each sprite, except if I draw the same sprite multiple times... (which is never the case in reality, except in benchmarks).

With this call, I can only draw about 13'000 64x64 sprites by second (~100 MB/sec). Without this call, I can draw about 45'000 of those sprites by second (~350 MB/sec), but it's always the same image... (but even 45000 spr/sec is still slow). If my image is in RAM, it's even slower.
Here is the code. Do you know what's so slow? Or I should use another method?

Code:
unsigned short *vmemptr = (void*)(0x04100000);

typedef struct      {
   int larg, haut;
   unsigned short *sprite;
} IMAGE;

#define IMAGE_SIZE(img)      ((img)->larg*(img)->haut*2)

IMAGE CreateNewImage(int larg, int haut, unsigned short *content)      {
   IMAGE img;
   img.larg = larg;
   img.haut = haut;
   img.sprite = vmemptr;
   if (content != NULL)
      memcpy(img.sprite, content, IMAGE_SIZE(&img));
   vmemptr += IMAGE_SIZE(&img);
   return img;
}

void DrawImage(IMAGE *img, int x, int y)            {
      struct Vertex* vertices;

      sceGuTexImage(0,img->larg,img->haut,img->larg,img->sprite);
      sceGuTexScale(1.0f/512.0f,1.0f/512.0f); // scale UVs to 0..1
      sceGuTexOffset(0.0f, 0.0f);

      vertices = (struct Vertex*)sceGuGetMemory(2 * sizeof(struct Vertex));

      vertices[0].u = 0;
      vertices[0].v = 0;
      vertices[0].color = 0;
      vertices[0].x = x;
      vertices[0].y = y;
      vertices[0].z = 0;
      vertices[1].u = img->larg;
      vertices[1].v = img->haut;
      vertices[1].color = 0;
      vertices[1].x = x + img->larg;
      vertices[1].y = y + img->haut;
      vertices[1].z = 0;

      sceGuDrawArray(GU_SPRITES,GU_TEXTURE_16BIT|GU_COLOR_4444|GU_VERTEX_16BIT|GU_TRANSFORM_2D,2,0,vertices);
}

void StartDrawing()      {
   sceGuStart(GU_DIRECT,list);
   sceGuTexMode(GU_PSM_4444,0,0,0);
   sceGuTexFunc(GU_TFX_REPLACE,GU_TCC_RGB);
   sceGuTexFilter(GU_NEAREST,GU_NEAREST);
   sceGuAmbientColor(0xffffffff);
}

void EndDrawing()      {
   sceGuFinish();
   sceGuSync(0,0);
}

void main()
{
   unsigned int x,y;
   IMAGE myImage;

   myImage = CreateNewImage(64, 64, NULL);
   for (y=0;y<64;y++)
      for (x=0;x<64;x++)
         myImage.sprite[y*myImage.larg+x]=x*y;
   sceKernelDcacheWritebackAll();
   while(1)
   {
      StartDrawing();
      for (x=0;x<1000;x++)
         DrawImage(&myImage, val%200, 0);
      EndDrawing();
      sceGuSwapBuffers();
   }
}


Thank you in advance
_________________
Sorry for my bad english
Oldschool library for PSP - PC version released


Last edited by Brunni on Sun Nov 27, 2005 11:25 pm; edited 2 times in total
Back to top
View user's profile Send private message
Shine



Joined: 03 Dec 2004
Posts: 728
Location: Germany

PostPosted: Sun Nov 27, 2005 11:23 pm    Post subject: Re: sceGuTexImage is slow! Reply with quote

I don't think that sceGuTexImage copies the texture to another place, but calling this function might clear the texture cache. It should be faster if you call this function only, when the texture was changed.
Back to top
View user's profile Send private message
Brunni



Joined: 08 Oct 2005
Posts: 186

PostPosted: Sun Nov 27, 2005 11:25 pm    Post subject: Reply with quote

Thanks. In fact, I figured out I rather have to copy one time the texture (containing all different sprites) and blit only parts of this texture by setting vertices' u and v coordinates.
But anyways it's unbelievably slow (about 50'000 64x64 non scaled sprites per second).
I'm a total beginner, but where are the 33 million polygons per second the PSP could theoretically handle? This should make 16 million sprites/sec rather than 50'000... no?
_________________
Sorry for my bad english
Oldschool library for PSP - PC version released
Back to top
View user's profile Send private message
Shine



Joined: 03 Dec 2004
Posts: 728
Location: Germany

PostPosted: Sun Nov 27, 2005 11:36 pm    Post subject: Reply with quote

Brunni wrote:
I'm a total beginner, but where are the 33 million polygons per second the PSP could theoretically handle? This should make 16 million sprites/sec rather than 50'000... no?


http://en.wikipedia.org/wiki/PlayStation_Portable says:

Quote:
Specifications state that the PSP is capable of rendering 33 million flat-shaded polygons per second, with a 664 million pixel per second fill rate


This means about 33 million polygons, each with a size e.g. of 4x5 pixels (20 pixels * 33 million polygons = 664 million pixels per second), with not textures.
Back to top
View user's profile Send private message
Brunni



Joined: 08 Oct 2005
Posts: 186

PostPosted: Mon Nov 28, 2005 12:27 am    Post subject: Reply with quote

Okay, thanks.
But anyways, by calculating:
Fillrate is: 664 Mpixels/sec -> my texture is 64x64 = 4096 pixels, 8 kB -> 162 thousand per second (or 81 thousand if a pixel is considered 8 bits).
VRAM is: 111 MHz * 512 bits = 6.6 (documented 5.3 GB/s) -> 640 thousand per second
Total: 129'630 sprites/sec, or 72'030 sprites/sec if 664 Mpixels means 8 bits pixels.
And I can just draw 45'000... It's quite far from what I could expect... So there is a problem in my code.
I can't upload it, so I post it here (you can compile it directly, no additionnal file needed). Is there a faster mean to do this? The guTexImage is only called once now.

[Edit]
Now I am at 66'000 sprites per second, which is correct assuming that the 664 Mpixels/sec means 8-bit pixels (and is divided by two in 16-bit and by four in 32-bits). Can anyone confirm this?


Thank you in advance ^^

Code:
#include <pspkernel.h>
#include <pspdisplay.h>
#include <pspdebug.h>
#include <stdlib.h>
#include <stdio.h>
#include <math.h>
#include <string.h>
#include <time.h>
#include <pspctrl.h>

#include <pspgu.h>

PSP_MODULE_INFO("Blit Sample", 0, 1, 1);
PSP_MAIN_THREAD_ATTR(THREAD_ATTR_USER);

static unsigned int __attribute__((aligned(16))) list[262144];

int done = 0;

/* Exit callback */
int exit_callback(int arg1, int arg2, void *common)
{
   done = 1;
   return 0;
}

/* Callback thread */
int CallbackThread(SceSize args, void *argp)
{
   int cbid;

   cbid = sceKernelCreateCallback("Exit Callback", exit_callback, NULL);
   sceKernelRegisterExitCallback(cbid);

   sceKernelSleepThreadCB();

   return 0;
}

/* Sets up the callback thread and returns its thread id */
int SetupCallbacks(void)
{
   int thid = 0;

   thid = sceKernelCreateThread("update_thread", CallbackThread, 0x11, 0xFA0, 0, 0);
   if(thid >= 0)
   {
      sceKernelStartThread(thid, 0, 0);
   }

   return thid;
}

struct Vertex
{
   unsigned short u, v;
   unsigned short color;
   short x, y, z;
};

/*
   SPECIAL
*/

unsigned short *vmemptr = (void*)(0x04100000);

typedef struct      {
   int sizeX, sizeY;
   float zoom;
   int offsetX, offsetY;
   unsigned short *sprite;
} IMAGE;

#define IMAGE_SIZE(img)      ((img)->sizeX*(img)->sizeY*2)

IMAGE CreateNewImage(int larg, int haut, unsigned short *content)      {
   IMAGE img;
   memset(&img, 0, sizeof(img));
   img.sizeX = larg;
   img.sizeY = haut;
   img.sprite = vmemptr;
   img.zoom = 1;
   if (content != NULL)
      memcpy(img.sprite, content, IMAGE_SIZE(&img));
   vmemptr += IMAGE_SIZE(&img);
   return img;
}

void SimpleDrawImage(IMAGE *img, int x, int y)            {
      struct Vertex* vertices;

      // setup the source buffer as a texture
//      sceGuTexImage(0,img->sizeX,img->sizeY,img->sizeX,img->sprite);
      sceGuTexScale(1.0f/512.0f,1.0f/512.0f);      // scale UVs to 0..1
      sceGuTexOffset(0.0f, 0.0f);

      vertices = (struct Vertex*)sceGuGetMemory(2 * sizeof(struct Vertex));

      vertices[0].u = 0;
      vertices[0].v = 0;
      vertices[0].color = 0;
      vertices[0].x = x;
      vertices[0].y = y;
      vertices[0].z = 0;
      vertices[1].u = img->sizeX;
      vertices[1].v = img->sizeY;
      vertices[1].color = 0;
      vertices[1].x = x + img->sizeX;
      vertices[1].y = y + img->sizeY;
      vertices[1].z = 0;

/*      int i;
      for (i=0;i<1000;i++)*/
         sceGuDrawArray(GU_SPRITES,GU_TEXTURE_16BIT|GU_COLOR_4444|GU_VERTEX_16BIT|GU_TRANSFORM_2D,2,0,vertices);
}

void SetTexture(IMAGE *img)      {
   sceGuTexImage(0, img->sizeX, img->sizeY, img->sizeX, img->sprite);
}

void StartDrawing()      {
   sceGuStart(GU_DIRECT,list);
   sceGuTexMode(GU_PSM_4444,0,0,0);
   sceGuTexFunc(GU_TFX_REPLACE,GU_TCC_RGB);
   sceGuTexFilter(GU_NEAREST,GU_NEAREST);
   sceGuAmbientColor(0xffffffff);
}

void EndDrawing()      {
   sceGuFinish();
   sceGuSync(0,0);
}

/*
   END SPECIAL
*/


int main(int argc, char* argv[])
{
   unsigned int x,y;
   SceCtrlData ctl;
   IMAGE myImage;

   pspDebugScreenInit();
   SetupCallbacks();

   sceGuInit();

   // setup
   sceGuStart(GU_DIRECT,list);
   sceGuDrawBuffer(GU_PSM_4444,(void*)0,512);
   sceGuDispBuffer(480,272,(void*)0x88000,512);
   sceGuDepthBuffer((void*)0x110000,512);
   sceGuOffset(2048 - (480/2),2048 - (272/2));
   sceGuViewport(2048,2048,480,272);
   sceGuDepthRange(0xc350,0x2710);
   sceGuScissor(0,0,480,272);
   sceGuEnable(GU_SCISSOR_TEST);
   sceGuFrontFace(GU_CW);
   sceGuEnable(GU_TEXTURE_2D);
   sceGuClear(GU_COLOR_BUFFER_BIT|GU_DEPTH_BUFFER_BIT);
   sceGuFinish();
   sceGuSync(0,0);

   sceDisplayWaitVblankStart();
   sceGuDisplay(1);

   int val = 0;

   // generate dummy image to blit
   myImage = CreateNewImage(64, 64, NULL);
   for (y=0;y<64;y++)
      for (x=0;x<64;x++)
         myImage.sprite[y*myImage.sizeX+x]=x*y;
   sceKernelDcacheWritebackAll();

//   memcpy((void*)(0x04100000), pixels, sizeof(pixels));
//   sceGuCopyImage(GU_PSM_4444,0,0,480,272,512,pixels,0,0,512,(void*)(0x04100000));

   float curr_ms = 1.0f;
   struct timeval time_slices[16];

   while (!done)
   {
      StartDrawing();
      SetTexture(&myImage);
//      myImage.zoom = 1.0f/((float)(val%200+1)/50.f);
      for (x=0;x<1000;x++)
         SimpleDrawImage(&myImage, val%200, 0);
      EndDrawing();
//      sceDisplayWaitVblankStart();
      sceGuSwapBuffers();
      val++;
   }

   sceGuTerm();

   sceKernelExitGame();
   return 0;
}

_________________
Sorry for my bad english
Oldschool library for PSP - PC version released
Back to top
View user's profile Send private message
ector



Joined: 12 May 2005
Posts: 195

PostPosted: Mon Nov 28, 2005 4:53 am    Post subject: Reply with quote

The sceGuDrawArray has some overhead, it might be faster if you fill larger arrays of coordinates and draw multiple sprites per call.
_________________
http://www.dtek.chalmers.se/~tronic/PSPTexTool.zip Free texture converter for PSP with source. More to come.
Back to top
View user's profile Send private message
memon



Joined: 03 Oct 2005
Posts: 63

PostPosted: Mon Nov 28, 2005 5:21 am    Post subject: Reply with quote

I think you want to swizzle your textures for better perfonmance. Also I have heard that drawing sprites of width 32pixels is the optimal solution to draw 2D stuff (it would be that it was only for non-swizzled textures, can't remember the details). Does someone have more info on that?
Back to top
View user's profile Send private message
weak



Joined: 13 Jan 2005
Posts: 114
Location: Vienna, Austria

PostPosted: Mon Nov 28, 2005 4:09 pm    Post subject: Reply with quote

64px performs as good as 32, so that seems to be the optimal width for a stripped blit.

texture swizzeling is a problem with 2d (game) stuff. most likely you'll need the accurate pixel information for collision detection...
Back to top
View user's profile Send private message
chp



Joined: 23 Jun 2004
Posts: 313

PostPosted: Mon Nov 28, 2005 7:51 pm    Post subject: Reply with quote

It depends on what source-psm you use for the texture how wide sprites you can blit without ruining cache performance:

32x32 in 32-bit mode
64x32 in 16-bit mode
128x64 in 8-bit mode
128x128 in 4-bit mode

And why not store collision masks as 1-bit patterns instead of using alpha or similar? Not that much more memory, and checking large areas for early rejection should be rather quick.
_________________
GE Dominator
Back to top
View user's profile Send private message
weak



Joined: 13 Jan 2005
Posts: 114
Location: Vienna, Austria

PostPosted: Mon Nov 28, 2005 8:09 pm    Post subject: Reply with quote

didn't think about the psm. good point.

and well, 1bit patterns would of course work too, just extra work. but if you need the performance that's actually a good idea
Back to top
View user's profile Send private message
memon



Joined: 03 Oct 2005
Posts: 63

PostPosted: Tue Nov 29, 2005 12:36 am    Post subject: Reply with quote

Chp, how about if I want to do a fullscreen blit, like doing some multipass blur or similar. In that case the source is linear texture. It is still better to draw vertical strips? Say, I have 512x272 offscreen surface and I want to blit that to frame buffer. If I have downsamepled that to 256x136? (apparently it is slightly different because it depends on the texture cache)
Back to top
View user's profile Send private message
chp



Joined: 23 Jun 2004
Posts: 313

PostPosted: Tue Nov 29, 2005 1:45 am    Post subject: Reply with quote

It's always best to blit in stripes aligned to the cache-boundary. If you have to refill the cache for every line, you're going to have major issues.
_________________
GE Dominator
Back to top
View user's profile Send private message
Brunni



Joined: 08 Oct 2005
Posts: 186

PostPosted: Thu Dec 01, 2005 5:46 am    Post subject: Reply with quote

Thanks a lot for you replies.
I don't know what texture swizzling is, but I'll take a look at it.
Now I've another question about performances, it seems to be possible to set sceGuTexImage and sceGuDrawBuffer to non power of two / block aligned buffer width (e.g sceGuDrawBuffer(GU_PSM_4444, address, 250) and sceGuTexImage(0, 256, 256, 250, address)). It works fine, but why is it said it must be block aligned in the documentation?
Setting non power of 2 values improves memory usage a lot, but maybe cripples performances? Has anyone tested?
Anyways, I'm sorry I have no precise benchmarking tool at the moment, but I didn't see a performance hurt by sight:
- Run test: draw a 64x64 image to the buffer (in VRAM) then blit it on the framebuffer. Virtual buffer size is 250x224, physical is one time 256x256, the other 250x224 (althrough only 250x224 is blitted in each case), 16 bits, 20 times loop:
- Run time with 250 pixels aligned: 15.6 sec
- Run time with 256 pixels (block) aligned: 16.5 sec
It seems even to be faster when non aligned (cache...).
Can anyone confirm this?
_________________
Sorry for my bad english
Oldschool library for PSP - PC version released
Back to top
View user's profile Send private message
ector



Joined: 12 May 2005
Posts: 195

PostPosted: Thu Dec 01, 2005 6:46 am    Post subject: Reply with quote

The docs may be wrong :)
_________________
http://www.dtek.chalmers.se/~tronic/PSPTexTool.zip Free texture converter for PSP with source. More to come.
Back to top
View user's profile Send private message
chp



Joined: 23 Jun 2004
Posts: 313

PostPosted: Thu Dec 01, 2005 5:15 pm    Post subject: Reply with quote

I did have issues when I first used that parameter, but it might have been something else at the time. I might take a look at that later this week.
_________________
GE Dominator
Back to top
View user's profile Send private message
Display posts from previous:   
Post new topic   Reply to topic    forums.ps2dev.org Forum Index -> PSP Development All times are GMT + 10 Hours
Page 1 of 1

 
Jump to:  
You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum
You cannot vote in polls in this forum


Powered by phpBB © 2001, 2005 phpBB Group