Renders on PSP

This commit is contained in:
2026-06-19 21:47:50 -05:00
parent 1f67e817ae
commit 9ad481d8f3
18 changed files with 290 additions and 351 deletions
+3 -7
View File
@@ -9,11 +9,11 @@
#include "display/render/renderpsp.h"
#include "display/display.h"
#include "assert/assert.h"
#include "log/log.h"
#include <pspgu.h>
#include <pspgum.h>
#include <pspdisplay.h>
#include <pspge.h>
#include <pspkernel.h>
/* GU framebuffer stride must be power-of-two; 512 is the standard for 480-wide. */
#define PSP_BUF_W 512
@@ -25,7 +25,6 @@
static uint32_t __attribute__((aligned(64))) displayList[0x10000];
errorret_t displayPSPInit(void) {
logDebug("[PSP] displayPSPInit: start\n");
DISPLAY.whichBuffer = 0;
sceGuInit();
@@ -58,23 +57,19 @@ errorret_t displayPSPInit(void) {
sceDisplaySetMode(0, PSP_SCREEN_W, PSP_SCREEN_H);
sceGuDisplay(GU_TRUE);
logDebug("[PSP] displayPSPInit: GU setup done, calling renderPSPInit\n");
errorChain(renderPSPInit());
logDebug("[PSP] displayPSPInit: done\n");
errorOk();
}
errorret_t displayPSPFlush(ropbuffer_t *buf) {
logDebug("[PSP] displayPSPFlush: enter\n");
assertNotNull(buf, "PSP flush: null ropbuffer");
errorChain(renderPSPFlush(buf));
logDebug("[PSP] displayPSPFlush: done\n");
errorOk();
}
errorret_t displayPSPSwap(void) {
logDebug("[PSP] displayPSPSwap\n");
sceDisplayWaitVblankStart();
sceGuSwapBuffers();
errorOk();
}
@@ -83,4 +78,5 @@ void displayPSPDispose(void) {
renderPSPDispose();
sceGuDisplay(GU_FALSE);
sceGuTerm();
sceKernelExitGame();
}
+58 -58
View File
@@ -10,7 +10,6 @@
#include "display/color.h"
#include "assert/assert.h"
#include "util/memory.h"
#include "log/log.h"
#include <malloc.h>
#include <psputils.h> /* sceKernelDcacheWritebackRange */
#include <pspgu.h>
@@ -29,15 +28,16 @@ static uint32_t __attribute__((aligned(64))) displayList[DISPLAY_LIST_SIZE / 4];
/* GU T8 (8-bit indexed) textures.
* cpuIndices : w*h row-major bytes, user-writable source of truth.
* gpuIndices : tbw*h padded for sceGuTexImage, re-derived at bind.
* gpuIndices : tbw*tph padded for sceGuTexImage, re-derived at bind.
* palette : 256 RGBA entries, user-writable source of truth.
* Converted to ABGR on-the-fly at bind into pspAbgrBuf.
* tbw : power-of-two stride ≥ 8 required by the GU. */
* tbw : power-of-two stride ≥ 16 required by T8 (16-byte cache line).
* tph : power-of-two height required by sceGuTexImage (≥ 1). */
typedef struct {
uint8_t *cpuIndices;
uint8_t *gpuIndices;
color_t palette[256];
uint16_t w, h, tbw;
uint16_t w, h, tbw, tph;
} psptexentry_t;
/* Shared 16-byte-aligned ABGR buffer used during every CLUT load. */
@@ -48,11 +48,13 @@ static uint16_t pspTexNext = 1; /* 0 = white fallback */
/* ---- Vertex types -------------------------------------------------------- */
/* 2D sprite: two corner vertices define the rect (GU_SPRITES uses 2 verts). */
/* 2D sprite: two corner vertices define the rect (GU_SPRITES uses 2 verts).
* Must match GU_TEXTURE_32BITF|GU_COLOR_8888|GU_VERTEX_32BITF — the PSP GU
* reference samples always use 32-bit float coords for textured 2D sprites. */
typedef struct {
uint16_t u, v; /* texel coords (integer, not normalised) */
float u, v; /* texel coords (float, in texels) */
uint32_t color; /* ABGR */
int16_t x, y, z;
float x, y, z;
} __attribute__((packed)) GuVert2D;
/* 3D triangle vertex */
@@ -71,8 +73,15 @@ static uint32_t toABGR(color_t c) {
((uint32_t)c.r);
}
/* Smallest power-of-two ≥ n and ≥ 8 (PSP GU minimum stride for T8). */
/* Smallest power-of-two ≥ n and ≥ 16 (T8 cache line is 16 bytes = 16 pixels). */
static uint16_t texturePow2(uint16_t n) {
uint16_t p = 16;
while(p < n) p = (uint16_t)(p << 1);
return p;
}
/* Smallest power-of-two ≥ n and ≥ 8 (PSP minimum texture dimension > 4). */
static uint16_t texturePow2Height(uint16_t n) {
uint16_t p = 8;
while(p < n) p = (uint16_t)(p << 1);
return p;
@@ -81,21 +90,20 @@ static uint16_t texturePow2(uint16_t n) {
/* ---- Init ---------------------------------------------------------------- */
errorret_t renderPSPInit(void) {
logDebug("[PSP] renderPSPInit: start\n");
/* White 1×1 fallback: index 0 → palette[0] = white */
/* White 8×8 fallback: all pixels = index 0 → palette[0] = white.
* PSP requires texture width/height > 4; tbw >= 16 for T8. */
psptexentry_t *e = &pspTexTable[0];
e->cpuIndices = (uint8_t *)memalign(16, 1);
e->cpuIndices = (uint8_t *)memalign(16, 8 * 8);
assertNotNull(e->cpuIndices, "PSP: failed to allocate fallback cpu index buffer");
e->cpuIndices[0] = 0;
memoryZero(e->cpuIndices, 8 * 8);
e->gpuIndices = (uint8_t *)memalign(16, 8); /* tbw=8 minimum */
e->gpuIndices = (uint8_t *)memalign(16, 16 * 8); /* tbw=16, tph=8 */
assertNotNull(e->gpuIndices, "PSP: failed to allocate fallback gpu index buffer");
memoryZero(e->gpuIndices, 8);
memoryZero(e->gpuIndices, 16 * 8);
memoryZero(e->palette, 256 * sizeof(color_t));
e->palette[0] = COLOR_WHITE;
e->w = 1; e->h = 1; e->tbw = 8;
logDebug("[PSP] renderPSPInit: done\n");
e->w = 8; e->h = 8; e->tbw = 16; e->tph = 8;
errorOk();
}
@@ -108,6 +116,7 @@ rtexture_t renderPSPTextureCreate(
assertTrue(pspTexNext < PSP_RTEXTURE_MAX, "PSP texture table full");
uint16_t tbw = texturePow2(w);
uint16_t tph = texturePow2Height(h);
rtexture_t handle = (rtexture_t)pspTexNext++;
psptexentry_t *e = &pspTexTable[handle];
@@ -116,7 +125,8 @@ rtexture_t renderPSPTextureCreate(
assertNotNull(e->cpuIndices, "PSP: failed to allocate cpu index buffer");
memoryCopy(e->cpuIndices, indices, cpuBytes);
uint32_t gpuBytes = (uint32_t)tbw * h;
/* GPU buffer must cover tbw*tph rows — sceGuTexImage requires power-of-2 height. */
uint32_t gpuBytes = (uint32_t)tbw * tph;
e->gpuIndices = (uint8_t *)memalign(16, gpuBytes);
assertNotNull(e->gpuIndices, "PSP: failed to allocate gpu index buffer");
memoryZero(e->gpuIndices, gpuBytes);
@@ -124,7 +134,7 @@ rtexture_t renderPSPTextureCreate(
memoryCopy(e->gpuIndices + row * tbw, indices + row * w, w);
memoryCopy(e->palette, palette, 256 * sizeof(color_t));
e->w = w; e->h = h; e->tbw = tbw;
e->w = w; e->h = h; e->tbw = tbw; e->tph = tph;
return handle;
}
@@ -150,22 +160,26 @@ static void bindTexture(rtexture_t tex) {
? &pspTexTable[tex]
: &pspTexTable[0];
/* Re-pad cpuIndices → gpuIndices (stride tbw, rows w wide). */
memoryZero(e->gpuIndices, (uint32_t)e->tbw * e->h);
for(uint16_t row = 0; row < e->h; row++)
memoryCopy(e->gpuIndices + row * e->tbw, e->cpuIndices + row * e->w, e->w);
sceKernelDcacheWritebackRange(e->gpuIndices, (uint32_t)e->tbw * e->h);
/* Write texture indices and palette via uncached alias so data lands in
* physical RAM immediately — the GE DMA reads physical RAM, not CPU cache,
* so writing through the uncached alias (|0x40000000) avoids stale reads. */
uint8_t *gpuUC = (uint8_t *)((uint32_t)e->gpuIndices | 0x40000000u);
uint32_t *clutUC = (uint32_t *)((uint32_t)pspAbgrBuf | 0x40000000u);
/* Convert palette color_t RGBA → ABGR into the shared aligned buffer. */
for(int i = 0; i < 256; i++) pspAbgrBuf[i] = toABGR(e->palette[i]);
sceKernelDcacheWritebackRange(pspAbgrBuf, 256 * sizeof(uint32_t));
memoryZero(gpuUC, (uint32_t)e->tbw * e->tph);
for(uint16_t row = 0; row < e->h; row++)
memoryCopy(gpuUC + row * e->tbw, e->cpuIndices + row * e->w, e->w);
for(int i = 0; i < 256; i++) clutUC[i] = toABGR(e->palette[i]);
sceGuTexMode(GU_PSM_T8, 0, 0, GU_FALSE);
sceGuTexImage(0, texturePow2Height(e->w), texturePow2Height(e->h), e->tbw, e->gpuIndices);
sceGuClutMode(GU_PSM_8888, 0, 0xFF, 0);
sceGuClutLoad(32, pspAbgrBuf); /* 32 × 8 entries = 256 */
sceGuTexImage(0, e->tbw, e->h, e->tbw, e->gpuIndices);
sceGuClutLoad(32, pspAbgrBuf);
sceGuTexFlush();
sceGuTexFunc(GU_TFX_MODULATE, GU_TCC_RGBA);
sceGuTexFilter(GU_NEAREST, GU_NEAREST);
sceGuTexWrap(GU_CLAMP, GU_CLAMP);
sceGuTexScale(1.0f, 1.0f);
sceGuTexOffset(0.0f, 0.0f);
}
@@ -183,33 +197,38 @@ static void draw2DSprite(const ropsprite_t *s) {
float v1 = ((s->uvY + s->uvH) / 255.0f) * (float)e->h;
bindTexture(s->texture);
sceGuAmbientColor(abgr);
GuVert2D *verts = (GuVert2D *)sceGuGetMemory(2 * sizeof(GuVert2D));
assertNotNull(verts, "PSP: failed to allocate sprite vertices");
verts[0].u = (uint16_t)u0; verts[0].v = (uint16_t)v0;
verts[0].u = u0; verts[0].v = v0;
verts[0].color = abgr;
verts[0].x = s->x; verts[0].y = s->y; verts[0].z = 0;
verts[0].x = (float)s->x; verts[0].y = (float)s->y; verts[0].z = 0.0f;
verts[1].u = (uint16_t)u1; verts[1].v = (uint16_t)v1;
verts[1].u = u1; verts[1].v = v1;
verts[1].color = abgr;
verts[1].x = (int16_t)(s->x + s->w);
verts[1].y = (int16_t)(s->y + s->h);
verts[1].z = 0;
verts[1].x = (float)(s->x + s->w);
verts[1].y = (float)(s->y + s->h);
verts[1].z = 0.0f;
sceGuDrawArray(
GU_SPRITES,
GU_TEXTURE_16BIT | GU_COLOR_8888 | GU_VERTEX_16BIT | GU_TRANSFORM_2D,
GU_TEXTURE_32BITF | GU_COLOR_8888 | GU_VERTEX_32BITF | GU_TRANSFORM_2D,
2, 0, verts
);
}
static void draw3DQuad(const ropquad3d_t *q) {
logDebug("[PSP] draw3DQuad: enter tex=%u\n", (unsigned)q->texture);
psptexentry_t *e = (q->texture < PSP_RTEXTURE_MAX && pspTexTable[q->texture].cpuIndices)
? &pspTexTable[q->texture]
: &pspTexTable[0];
uint32_t abgr = toABGR(q->tint);
float u0 = q->uvX / 255.0f, v0 = q->uvY / 255.0f;
float u1 = (q->uvX + q->uvW) / 255.0f;
float v1 = (q->uvY + q->uvH) / 255.0f;
/* UV in 0-255 range → scale to texel coords the same way draw2DSprite does. */
float u0 = (q->uvX / 255.0f) * (float)e->w;
float v0 = (q->uvY / 255.0f) * (float)e->h;
float u1 = ((q->uvX + q->uvW) / 255.0f) * (float)e->w;
float v1 = ((q->uvY + q->uvH) / 255.0f) * (float)e->h;
float cx = (float)q->cx, cy = (float)q->cy, cz = (float)q->cz;
float rx = (float)q->rx, ry = (float)q->ry, rz = (float)q->rz;
@@ -220,12 +239,9 @@ static void draw3DQuad(const ropquad3d_t *q) {
float blx = cx-rx-ux, bly = cy-ry-uy, blz = cz-rz-uz;
float brx = cx+rx-ux, bry = cy+ry-uy, brz = cz+rz-uz;
logDebug("[PSP] draw3DQuad: bindTexture\n");
bindTexture(q->texture);
logDebug("[PSP] draw3DQuad: getMemory\n");
GuVert3D *verts = (GuVert3D *)sceGuGetMemory(6 * sizeof(GuVert3D));
logDebug("[PSP] draw3DQuad: verts=0x%08x\n", (unsigned)verts);
assertNotNull(verts, "PSP: failed to allocate 3D quad vertices");
verts[0] = (GuVert3D){u0,v0, abgr, tlx,tly,tlz};
@@ -235,21 +251,16 @@ static void draw3DQuad(const ropquad3d_t *q) {
verts[4] = (GuVert3D){u1,v1, abgr, brx,bry,brz};
verts[5] = (GuVert3D){u1,v0, abgr, trx,try_,trz};
logDebug("[PSP] draw3DQuad: sceGuDrawArray\n");
sceGuDrawArray(
GU_TRIANGLES,
GU_TEXTURE_32BITF | GU_COLOR_8888 | GU_VERTEX_32BITF | GU_TRANSFORM_3D,
6, 0, verts
);
logDebug("[PSP] draw3DQuad: done\n");
}
/* ---- Flush --------------------------------------------------------------- */
errorret_t renderPSPFlush(ropbuffer_t *buf) {
logDebug("[PSP] renderPSPFlush: byteCount=%u count=%u\n",
(unsigned)buf->byteCount, (unsigned)buf->count);
sceGuStart(GU_DIRECT, displayList);
sceGuEnable(GU_TEXTURE_2D);
@@ -257,14 +268,12 @@ errorret_t renderPSPFlush(ropbuffer_t *buf) {
sceGuDepthFunc(GU_GEQUAL); /* PSP uses reversed depth */
uint32_t offset = 0;
uint32_t opIdx = 0;
while(offset < buf->byteCount) {
const ropheader_t *hdr = (const ropheader_t *)(buf->data + offset);
ropop_t op = (ropop_t)hdr->op;
switch(op) {
case ROP_CLEAR: {
logDebug("[PSP] op[%u] ROP_CLEAR\n", (unsigned)opIdx);
const ropclear_t *c = (const ropclear_t *)hdr;
uint32_t abgr = toABGR(c->color);
sceGuClearColor(abgr);
@@ -273,12 +282,10 @@ errorret_t renderPSPFlush(ropbuffer_t *buf) {
break;
}
case ROP_DRAW_SPRITE:
logDebug("[PSP] op[%u] ROP_DRAW_SPRITE\n", (unsigned)opIdx);
draw2DSprite((const ropsprite_t *)hdr);
break;
case ROP_SET_PROJECTION: {
logDebug("[PSP] op[%u] ROP_SET_PROJECTION\n", (unsigned)opIdx);
const ropprojection_t *p = (const ropprojection_t *)hdr;
sceGumMatrixMode(GU_PROJECTION);
sceGumLoadIdentity();
@@ -296,7 +303,6 @@ errorret_t renderPSPFlush(ropbuffer_t *buf) {
break;
}
case ROP_SET_VIEW: {
logDebug("[PSP] op[%u] ROP_SET_VIEW\n", (unsigned)opIdx);
const ropview_t *v = (const ropview_t *)hdr;
ScePspFVector3 eye = {(float)v->eyeX, (float)v->eyeY, (float)v->eyeZ};
ScePspFVector3 target = {(float)v->tgtX, (float)v->tgtY, (float)v->tgtZ};
@@ -309,23 +315,17 @@ errorret_t renderPSPFlush(ropbuffer_t *buf) {
break;
}
case ROP_DRAW_QUAD_3D:
logDebug("[PSP] op[%u] ROP_DRAW_QUAD_3D\n", (unsigned)opIdx);
draw3DQuad((const ropquad3d_t *)hdr);
break;
default:
logDebug("[PSP] op[%u] unknown op=%u offset=%u\n",
(unsigned)opIdx, (unsigned)op, (unsigned)offset);
break;
}
offset += ropOpSize(op);
opIdx++;
}
logDebug("[PSP] renderPSPFlush: sceGuFinish\n");
sceGuFinish();
sceGuSync(0, 0);
logDebug("[PSP] renderPSPFlush: done\n");
errorOk();
}