From b6ff26aa5be4167402b68ffe8a6e978a142e50b7 Mon Sep 17 00:00:00 2001 From: UnknownShadow200 Date: Wed, 8 May 2024 21:29:49 +1000 Subject: [PATCH] PS1: Backface culling and better VRAM usage --- src/Graphics_PS1.c | 162 ++++++++++++++++++++++++++++++--------------- 1 file changed, 109 insertions(+), 53 deletions(-) diff --git a/src/Graphics_PS1.c b/src/Graphics_PS1.c index f57280281..bf932cdd4 100644 --- a/src/Graphics_PS1.c +++ b/src/Graphics_PS1.c @@ -38,6 +38,7 @@ static int active_buffer; static RenderBuffer* buffer; static cc_bool rendering2D; static void* lastPoly; +static cc_bool cullingEnabled; static void OnBufferUpdated(void) { buffer = &buffers[active_buffer]; @@ -102,7 +103,7 @@ void Gfx_FreeState(void) { void Gfx_Create(void) { Gfx.MaxTexWidth = 128; - Gfx.MaxTexHeight = 128; + Gfx.MaxTexHeight = 256; Gfx.Created = true; Gfx_RestoreState(); @@ -128,30 +129,41 @@ void Gfx_Free(void) { // VRAM can be divided into texture pages // 32 texture pages total - each page is 64 x 256 // 10 texture pages are occupied by the doublebuffered display -// 22 texture packs are usable, and are then divided into -// - 4 pages for 256 wide textures, 8 for 128 wide, 10 for 64 -#define TPAGE_START_HOR 5 -#define TPAGES_PER_HALF 16 - +// 22 texture pages are usable for textures +// These 22 pages are then divided into: +// - 5 for 128+ wide horizontal, 6 otherwise +// - 11 pages for vertical textures #define TPAGE_WIDTH 64 #define TPAGE_HEIGHT 256 -#define MAX_TEX_PAGES 22 -static cc_uint8 vram_used[(MAX_TEX_PAGES * TPAGE_HEIGHT) / 8]; +#define TPAGES_PER_HALF 16 +// Horizontally oriented textures (first group of 16) +#define TPAGE_START_HOR 5 +#define MAX_HOR_TEX_PAGES 11 +#define MAX_HOR_TEX_LINES (MAX_HOR_TEX_PAGES * TPAGE_HEIGHT) + +// Horizontally oriented textures (second group of 16) +#define TPAGE_START_VER (16 + 5) +#define MAX_VER_TEX_PAGES 11 +#define MAX_VER_TEX_LINES (MAX_VER_TEX_PAGES * TPAGE_WIDTH) + +static cc_uint8 vram_used[(MAX_HOR_TEX_LINES + MAX_VER_TEX_LINES) / 8]; #define VRAM_SetUsed(line) (vram_used[(line) / 8] |= (1 << ((line) % 8))) #define VRAM_UnUsed(line) (vram_used[(line) / 8] &= ~(1 << ((line) % 8))) #define VRAM_IsUsed(line) (vram_used[(line) / 8] & (1 << ((line) % 8))) -static void VRAM_GetBlockRange(int width, int* beg, int* end) { - if (width >= 256) { - *beg = 0; - *end = 4 * TPAGE_HEIGHT; +#define VRAM_BoundingAxis(width, height) height > width ? width : height + +static void VRAM_GetBlockRange(int width, int height, int* beg, int* end) { + if (height > width) { + *beg = MAX_HOR_TEX_LINES; + *end = MAX_HOR_TEX_LINES + MAX_VER_TEX_LINES; } else if (width >= 128) { - *beg = 4 * TPAGE_HEIGHT; - *end = 12 * TPAGE_HEIGHT; + *beg = 0; + *end = 5 * TPAGE_HEIGHT; } else { - *beg = 12 * TPAGE_HEIGHT; - *end = 22 * TPAGE_HEIGHT; + *beg = 5 * TPAGE_HEIGHT; + *end = MAX_HOR_TEX_LINES; } } @@ -165,7 +177,7 @@ static cc_bool VRAM_IsRangeFree(int beg, int end) { static int VRAM_FindFreeBlock(int width, int height) { int beg, end; - VRAM_GetBlockRange(width, &beg, &end); + VRAM_GetBlockRange(width, height, &beg, &end); // TODO kinda inefficient for (int i = beg; i < end - height; i++) @@ -177,13 +189,41 @@ static int VRAM_FindFreeBlock(int width, int height) { return -1; } +static void VRAM_AllocBlock(int line, int width, int height) { + int lines = VRAM_BoundingAxis(width, height); + for (int i = line; i < line + lines; i++) + { + VRAM_SetUsed(i); + } +} + +static void VRAM_FreeBlock(int line, int width, int height) { + int lines = VRAM_BoundingAxis(width, height); + for (int i = line; i < line + lines; i++) + { + VRAM_UnUsed(i); + } +} + +static int VRAM_CalcPage(int line) { + if (line < MAX_HOR_TEX_LINES) { + return TPAGE_START_HOR + (line / TPAGE_HEIGHT); + } else { + line -= MAX_HOR_TEX_LINES; + return TPAGE_START_VER + (line / TPAGE_WIDTH); + } +} + + #define TEXTURES_MAX_COUNT 64 typedef struct GPUTexture { cc_uint16 width, height; + cc_uint8 width_mask, height_mask; cc_uint16 line, tpage; + cc_uint8 xOffset, yOffset; } GPUTexture; static GPUTexture textures[TEXTURES_MAX_COUNT]; -static GPUTexture* active_tex; +static GPUTexture* curTex; #define BGRA8_to_PS1(src) \ ((src[2] & 0xF8) >> 3) | ((src[1] & 0xF8) << 2) | ((src[0] & 0xF8) << 7) | ((src[3] & 0x80) << 8) @@ -207,28 +247,30 @@ static void* AllocTextureAt(int i, struct Bitmap* bmp, int rowWidth) { int line = VRAM_FindFreeBlock(bmp->width, bmp->height); if (line == -1) { Mem_Free(tmp); return NULL; } - tex->width = bmp->width; - tex->height = bmp->height; + tex->width = bmp->width; tex->width_mask = bmp->width - 1; + tex->height = bmp->height; tex->height_mask = bmp->height - 1; tex->line = line; - int page = TPAGE_START_HOR + (line / TPAGE_HEIGHT); - // In bottom half of VRAM? Need to offset horizontally again - if (page >= TPAGES_PER_HALF) page += TPAGE_START_HOR; - - int pageX = (page % TPAGES_PER_HALF); - int pageY = (page / TPAGES_PER_HALF); - - for (int i = tex->line; i < tex->line + tex->height; i++) - { - VRAM_SetUsed(i); - } + int page = VRAM_CalcPage(line); + int pageX = (page % TPAGES_PER_HALF); + int pageY = (page / TPAGES_PER_HALF); tex->tpage = (2 << 7) | (pageY << 4) | pageX; + + VRAM_AllocBlock(line, bmp->width, bmp->height); + if (bmp->height > bmp->width) { + tex->xOffset = line % TPAGE_WIDTH; + tex->yOffset = 0; + } else { + tex->xOffset = 0; + tex->yOffset = line % TPAGE_HEIGHT; + } + Platform_Log3("%i x %i = %i", &bmp->width, &bmp->height, &line); Platform_Log3(" at %i (%i, %i)", &page, &pageX, &pageY); RECT rect; - rect.x = pageX * TPAGE_WIDTH; - rect.y = pageY * TPAGE_HEIGHT + (line % TPAGE_HEIGHT); + rect.x = pageX * TPAGE_WIDTH + tex->xOffset; + rect.y = pageY * TPAGE_HEIGHT + tex->yOffset; rect.w = bmp->width; rect.h = bmp->height; @@ -253,7 +295,7 @@ static GfxResourceID Gfx_AllocTexture(struct Bitmap* bmp, int rowWidth, cc_uint8 void Gfx_BindTexture(GfxResourceID texId) { if (!texId) texId = white_square; - active_tex = (GPUTexture*)texId; + curTex = (GPUTexture*)texId; } void Gfx_DeleteTexture(GfxResourceID* texId) { @@ -261,10 +303,7 @@ void Gfx_DeleteTexture(GfxResourceID* texId) { if (!data) return; GPUTexture* tex = (GPUTexture*)data; - for (int i = tex->line; i < tex->line + tex->height; i++) - { - VRAM_UnUsed(i); - } + VRAM_FreeBlock(tex->line, tex->width, tex->height); tex->width = 0; tex->height = 0; *texId = NULL; } @@ -287,7 +326,7 @@ void Gfx_SetFogEnd(float value) { } void Gfx_SetFogMode(FogFunc func) { } void Gfx_SetFaceCulling(cc_bool enabled) { - // TODO + cullingEnabled = enabled; } static void SetAlphaTest(cc_bool enabled) { @@ -561,7 +600,7 @@ static void DrawColouredQuads2D(int verticesCount, int startVertex) { } static void DrawTexturedQuads2D(int verticesCount, int startVertex) { - int pageOffset = active_tex->line % TPAGE_HEIGHT; + int uOffset = curTex->xOffset, vOffset = curTex->yOffset; for (int i = 0; i < verticesCount; i += 4) { @@ -569,14 +608,19 @@ static void DrawTexturedQuads2D(int verticesCount, int startVertex) { POLY_FT4* poly = new_primitive(sizeof(POLY_FT4)); setPolyFT4(poly); - poly->tpage = active_tex->tpage; + poly->tpage = curTex->tpage; poly->clut = 0; // TODO & instead of % - poly->x0 = v[1].x; poly->y0 = v[1].y; poly->u0 = (int)(v[1].U * active_tex->width) % active_tex->width; poly->v0 = ((int)(v[1].V * active_tex->height) % active_tex->height) + pageOffset; - poly->x1 = v[0].x; poly->y1 = v[0].y; poly->u1 = (int)(v[0].U * active_tex->width) % active_tex->width; poly->v1 = ((int)(v[0].V * active_tex->height) % active_tex->height) + pageOffset; - poly->x2 = v[2].x; poly->y2 = v[2].y; poly->u2 = (int)(v[2].U * active_tex->width) % active_tex->width; poly->v2 = ((int)(v[2].V * active_tex->height) % active_tex->height) + pageOffset; - poly->x3 = v[3].x; poly->y3 = v[3].y; poly->u3 = (int)(v[3].U * active_tex->width) % active_tex->width; poly->v3 = ((int)(v[3].V * active_tex->height) % active_tex->height) + pageOffset; + poly->x0 = v[1].x; poly->y0 = v[1].y; + poly->x1 = v[0].x; poly->y1 = v[0].y; + poly->x2 = v[2].x; poly->y2 = v[2].y; + poly->x3 = v[3].x; poly->y3 = v[3].y; + + poly->u0 = ((int)(v[1].U * curTex->width) & curTex->width_mask) + uOffset; poly->v0 = ((int)(v[1].V * curTex->height) & curTex->height_mask) + vOffset; + poly->u1 = ((int)(v[0].U * curTex->width) & curTex->width_mask) + uOffset; poly->v1 = ((int)(v[0].V * curTex->height) & curTex->height_mask) + vOffset; + poly->u2 = ((int)(v[2].U * curTex->width) & curTex->width_mask) + uOffset; poly->v2 = ((int)(v[2].V * curTex->height) & curTex->height_mask) + vOffset; + poly->u3 = ((int)(v[3].U * curTex->width) & curTex->width_mask) + uOffset; poly->v3 = ((int)(v[3].V * curTex->height) & curTex->height_mask) + vOffset; poly->r0 = PackedCol_R(v->Col); poly->g0 = PackedCol_G(v->Col); @@ -629,7 +673,7 @@ static void DrawColouredQuads3D(int verticesCount, int startVertex) { } static void DrawTexturedQuads3D(int verticesCount, int startVertex) { - int pageOffset = active_tex->line % TPAGE_HEIGHT; + int uOffset = curTex->xOffset, vOffset = curTex->yOffset; for (int i = 0; i < verticesCount; i += 4) { @@ -637,7 +681,7 @@ static void DrawTexturedQuads3D(int verticesCount, int startVertex) { POLY_FT4* poly = new_primitive(sizeof(POLY_FT4)); setPolyFT4(poly); - poly->tpage = active_tex->tpage; + poly->tpage = curTex->tpage; poly->clut = 0; Vec3 coords[4]; @@ -647,12 +691,24 @@ static void DrawTexturedQuads3D(int verticesCount, int startVertex) { Transform(&coords[3], &v[3], &mvp); // TODO & instead of % - poly->x0 = coords[1].x; poly->y0 = coords[1].y; poly->u0 = (int)(v[1].U * active_tex->width) % active_tex->width; poly->v0 = ((int)(v[1].V * active_tex->height) % active_tex->height) + pageOffset; - poly->x1 = coords[0].x; poly->y1 = coords[0].y; poly->u1 = (int)(v[0].U * active_tex->width) % active_tex->width; poly->v1 = ((int)(v[0].V * active_tex->height) % active_tex->height) + pageOffset; - poly->x2 = coords[2].x; poly->y2 = coords[2].y; poly->u2 = (int)(v[2].U * active_tex->width) % active_tex->width; poly->v2 = ((int)(v[2].V * active_tex->height) % active_tex->height) + pageOffset; - poly->x3 = coords[3].x; poly->y3 = coords[3].y; poly->u3 = (int)(v[3].U * active_tex->width) % active_tex->width; poly->v3 = ((int)(v[3].V * active_tex->height) % active_tex->height) + pageOffset; - - //int P = active_tex->height, page = poly->tpage & 0xFF, ll = active_tex->line % TPAGE_HEIGHT; + poly->x0 = coords[1].x; poly->y0 = coords[1].y; + poly->x1 = coords[0].x; poly->y1 = coords[0].y; + poly->x2 = coords[2].x; poly->y2 = coords[2].y; + poly->x3 = coords[3].x; poly->y3 = coords[3].y; + + if (cullingEnabled) { + // https://gamedev.stackexchange.com/questions/203694/how-to-make-backface-culling-work-correctly-in-both-orthographic-and-perspective + int signA = (poly->x0 - poly->x1) * (poly->y2 - poly->y1); + int signB = (poly->x2 - poly->x1) * (poly->y0 - poly->y1); + if (signA > signB) continue; + } + + poly->u0 = ((int)(v[1].U * curTex->width) & curTex->width_mask) + uOffset; poly->v0 = ((int)(v[1].V * curTex->height) & curTex->height_mask) + vOffset; + poly->u1 = ((int)(v[0].U * curTex->width) & curTex->width_mask) + uOffset; poly->v1 = ((int)(v[0].V * curTex->height) & curTex->height_mask) + vOffset; + poly->u2 = ((int)(v[2].U * curTex->width) & curTex->width_mask) + uOffset; poly->v2 = ((int)(v[2].V * curTex->height) & curTex->height_mask) + vOffset; + poly->u3 = ((int)(v[3].U * curTex->width) & curTex->width_mask) + uOffset; poly->v3 = ((int)(v[3].V * curTex->height) & curTex->height_mask) + vOffset; + + //int P = curTex->height, page = poly->tpage & 0xFF, ll = curTex->yOffset; //Platform_Log4("XYZ: %f3 x %i, %i, %i", &v[0].V, &P, &page, &ll); int p = (coords[0].z + coords[1].z + coords[2].z + coords[3].z) / 4; if (p < 0 || p >= OT_LENGTH) continue;