在开始绘图之前,首先需要初始化PSP的Graphics Unit,简称GU。PSP允许对VRAM(即Video RAM)进行直接读写,因此我们可以直接控制像素的显示,相当于在PC上拥有直接控制显示缓冲区的能力(让人想起了美好的DOS编程时代)。根据pspdev的wiki所言[1],VRAM的起始地址为0×04000000,大小为2MB,但是必须or 0×40000000使用,不然会有意想不到的后果,原因不明。

一些基本定义
#define BUF_WIDTH 512
#define SCREEN_WIDTH 480
#define SCREEN_HEIGHT 272
#define FRAMEBUFFER_SIZE (BUF_WIDTH * SCREEN_HEIGHT * 4)

BUF_WIDTH是帧缓冲区每行的像素宽度,虽然PSP屏幕的宽度是480像素,但是可能是为了考虑内存对齐,缓冲区的每行需要留出32个像素的空余。因此,一个整屏幕的帧缓冲区大小为BUF_WIDTH * SCREEN_HEIGHT * 每像素字节数。PSP支持4种像素格式(RGBA),分别为5650,5551,4444,8888。前三种每个像素使用2个字节表示,在这里我使用第四种,每像素4字节,这样一个帧缓冲区就是544KB大小。

为了编程方便,我们再定义一个Color的type和几种Color:

typedef u32 Color;
#define RGB(r, g, b) ((((b)&0xff)<<16)|(((g)&0xff)<<8)|((r)&0xff))
#define RGBA(r, g, b, a) ((((a)&0xff)<<24)|(((b)&0xff)<<16)|(((g)&0xff)<<8)|((r)&0xff))
#define ColorRed RGB(255, 0, 0)
#define ColorBlue RGB(0, 0, 255)
#define ColorGreen RGB(0, 255, 0)
#define ColorYellow RGB(255, 255, 0)
#define ColorBlack RGB(0, 0, 0)

下面再定义几个指针:

void* g_p_displayList; // holds the data of display list
Color* g_vram_base = (Color*)(0x40000000|0x04000000);  // pointer to the start of VRAM
初始化GU

为了使用GU相关的SDK,有3个新的头文件目前需要用到:

#include <pspdisplay.h>
#include <pspgu.h>
#include <malloc.h>

因为我们现在还没开始研究画图,所以初始化代码很简单,只有5行:

// Init GU
g_p_displayList = memalign(16, 640);
sceGuInit();
sceGuStart(GU_DIRECT, g_p_displayList);
sceDisplayWaitVblankStart();
sceGuDisplay(GU_TRUE);

首先为g_p_displayList分配空间,这里存放一个叫DisplayList的东东,貌似现在用不到,感兴趣的同学可以先参考这篇文章[2]。然后调用sceGuInit,这个函数必须在使用GU之前被调用,接下来调用sceGuStart填充DisplayList。sceDisplayWaitVblankStart等待垂直同步信号,最后调用sceGuDisplay函数启用display。

其实,如果不调用上文中与GU相关的函数,也可以直接写帧缓冲区,但如果想使用GU相关的SDK,则必须按上文所述进行初始化。

使用单色填充屏幕矩形区域

现在我们可以直接向帧缓冲区里写值,然后屏幕就会有对应的显示。为了练手,我们先试着用单色来填充屏幕的一块矩形区域:

void FillScreenRect(Color color, int x0, int y0, int width, int height)
{
    int x, y;
    Color* ptr = g_vram_base + x0 + y0 * BUF_WIDTH;
    for (y = 0; y < height; y++, ptr += BUF_WIDTH - width)
    {
        for (x = 0; x < width; x++, ptr++) *ptr = color;
    }
}

然后在main函数中调用它,来画一个Microsoft的四色徽标:

FillScreenRect(ColorRed, 0, 0, SCREEN_WIDTH / 2, SCREEN_HEIGHT / 2);
FillScreenRect(ColorGreen, SCREEN_WIDTH / 2, 0, SCREEN_WIDTH / 2, SCREEN_HEIGHT / 2);
FillScreenRect(ColorBlue, 0, SCREEN_HEIGHT / 2, SCREEN_WIDTH / 2, SCREEN_HEIGHT / 2);
FillScreenRect(ColorYellow, SCREEN_WIDTH / 2, SCREEN_HEIGHT / 2, SCREEN_WIDTH / 2, SCREEN_HEIGHT / 2);
Build

因为我们使用了GU的相关SDK,所以在Makefile文件里需要增加对GU相关lib的调用。而且考虑到以后还会有更多的cpp文件出现,所以用patsubst和wildcard两个函数增强一下OBJS变量,让Makefile可以自动将所有的cpp文件包含进来:

OBJS = $(patsubst %.cpp,%.o,$(wildcard *.cpp))
LIBS = -lpspgu

最后按F7来Build即可。

程序运行截图

===========cotaku的分割线===========

下一篇应该会研究画三角形神马的了,嗯。

参考资料:
[1] psp:memory_map
[2] PSPGU Tutorial 11: Display Lists
[3] 淺談GU (一)

» 转载请注明来源及链接:未来代码研究所

Related Posts:

Leave a Reply

World Line
Time Machine
Friendly Links
Online Tools