 |
forums.ps2dev.org Homebrew PS2, PSP & PS3 Development Discussions
|
| View previous topic :: View next topic |
| Author |
Message |
Brunni
Joined: 08 Oct 2005 Posts: 186
|
Posted: Sun Nov 27, 2005 9:21 pm Post subject: sceGuTexImage is slow! |
|
|
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 |
|
 |
Shine
Joined: 03 Dec 2004 Posts: 728 Location: Germany
|
Posted: Sun Nov 27, 2005 11:23 pm Post subject: Re: sceGuTexImage is slow! |
|
|
| 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 |
|
 |
Brunni
Joined: 08 Oct 2005 Posts: 186
|
Posted: Sun Nov 27, 2005 11:25 pm Post subject: |
|
|
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 |
|
 |
Shine
Joined: 03 Dec 2004 Posts: 728 Location: Germany
|
Posted: Sun Nov 27, 2005 11:36 pm Post subject: |
|
|
| 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 |
|
 |
Brunni
Joined: 08 Oct 2005 Posts: 186
|
Posted: Mon Nov 28, 2005 12:27 am Post subject: |
|
|
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 |
|
 |
ector
Joined: 12 May 2005 Posts: 195
|
Posted: Mon Nov 28, 2005 4:53 am Post subject: |
|
|
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 |
|
 |
memon
Joined: 03 Oct 2005 Posts: 63
|
Posted: Mon Nov 28, 2005 5:21 am Post subject: |
|
|
| 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 |
|
 |
weak
Joined: 13 Jan 2005 Posts: 114 Location: Vienna, Austria
|
Posted: Mon Nov 28, 2005 4:09 pm Post subject: |
|
|
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 |
|
 |
chp
Joined: 23 Jun 2004 Posts: 313
|
Posted: Mon Nov 28, 2005 7:51 pm Post subject: |
|
|
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 |
|
 |
weak
Joined: 13 Jan 2005 Posts: 114 Location: Vienna, Austria
|
Posted: Mon Nov 28, 2005 8:09 pm Post subject: |
|
|
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 |
|
 |
memon
Joined: 03 Oct 2005 Posts: 63
|
Posted: Tue Nov 29, 2005 12:36 am Post subject: |
|
|
| 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 |
|
 |
chp
Joined: 23 Jun 2004 Posts: 313
|
Posted: Tue Nov 29, 2005 1:45 am Post subject: |
|
|
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 |
|
 |
Brunni
Joined: 08 Oct 2005 Posts: 186
|
Posted: Thu Dec 01, 2005 5:46 am Post subject: |
|
|
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 |
|
 |
ector
Joined: 12 May 2005 Posts: 195
|
|
| Back to top |
|
 |
chp
Joined: 23 Jun 2004 Posts: 313
|
Posted: Thu Dec 01, 2005 5:15 pm Post subject: |
|
|
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 |
|
 |
|
|
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
|