aboutsummaryrefslogtreecommitdiff
path: root/libpsn00b/psxgpu
diff options
context:
space:
mode:
authorspicyjpeg <thatspicyjpeg@gmail.com>2022-12-18 16:30:02 +0100
committerGitHub <noreply@github.com>2022-12-18 16:30:02 +0100
commitb12b716a9e54c4f1892795a136d6ffeb088ff001 (patch)
tree5888e4a77b207f24ccd9ad148d1c74e9c33400e9 /libpsn00b/psxgpu
parent77306e187ef1a7ad5d3508ae9acb38edc5b68255 (diff)
parenta3359c0e7d85bf4752cda3b00892ecd5ef691077 (diff)
downloadpsn00bsdk-b12b716a9e54c4f1892795a136d6ffeb088ff001.tar.gz
Merge pull request #68 from spicyjpeg/bugfix
Bugfixes, psxcd rewrite, psxgpu and CMake tweaks (v0.22)
Diffstat (limited to 'libpsn00b/psxgpu')
-rw-r--r--libpsn00b/psxgpu/common.c31
-rw-r--r--libpsn00b/psxgpu/env.c99
-rw-r--r--libpsn00b/psxgpu/image.c44
3 files changed, 110 insertions, 64 deletions
diff --git a/libpsn00b/psxgpu/common.c b/libpsn00b/psxgpu/common.c
index 9f45f10..e41bd31 100644
--- a/libpsn00b/psxgpu/common.c
+++ b/libpsn00b/psxgpu/common.c
@@ -38,9 +38,6 @@ static volatile uint16_t _last_hblank;
/* Private interrupt handlers */
-#define _ENTER_CRITICAL() uint16_t mask = IRQ_MASK; IRQ_MASK = 0;
-#define _EXIT_CRITICAL() IRQ_MASK = mask;
-
static void _vblank_handler(void) {
_vblank_counter++;
@@ -74,8 +71,8 @@ void ResetGraph(int mode) {
// the first time.
if (!ResetCallback()) {
EnterCriticalSection();
- InterruptCallback(0, &_vblank_handler);
- DMACallback(2, &_gpu_dma_handler);
+ InterruptCallback(IRQ_VBLANK, &_vblank_handler);
+ DMACallback(DMA_GPU, &_gpu_dma_handler);
_gpu_video_mode = (GPU_GP1 >> 20) & 1;
ExitCriticalSection();
@@ -149,22 +146,22 @@ int VSync(int mode) {
}
void *VSyncHaltFunction(void (*func)(void)) {
- //_ENTER_CRITICAL();
+ //FastEnterCriticalSection();
void *old_callback = _vsync_halt_func;
_vsync_halt_func = func;
- //_EXIT_CRITICAL();
+ //FastExitCriticalSection();
return old_callback;
}
void *VSyncCallback(void (*func)(void)) {
- _ENTER_CRITICAL();
+ FastEnterCriticalSection();
void *old_callback = _vsync_callback;
_vsync_callback = func;
- _EXIT_CRITICAL();
+ FastExitCriticalSection();
return old_callback;
}
@@ -184,18 +181,18 @@ int EnqueueDrawOp(
// race condition where the DMA transfer could end while interrupts are
// being disabled. Interrupts are disabled through the IRQ_MASK register
// rather than via syscalls for performance reasons.
- _ENTER_CRITICAL();
+ FastEnterCriticalSection();
int length = _queue_length;
if (!length) {
_queue_length = 1;
- _EXIT_CRITICAL();
+ FastExitCriticalSection();
func(arg1, arg2, arg3);
return 0;
}
if (length >= QUEUE_LENGTH) {
- _EXIT_CRITICAL();
+ FastExitCriticalSection();
_sdk_log("draw queue overflow, dropping commands\n");
return -1;
@@ -211,7 +208,7 @@ int EnqueueDrawOp(
entry->arg2 = arg2;
entry->arg3 = arg3;
- _EXIT_CRITICAL();
+ FastExitCriticalSection();
return length;
}
@@ -242,12 +239,12 @@ int DrawSync(int mode) {
}
void *DrawSyncCallback(void (*func)(void)) {
- _ENTER_CRITICAL();
+ FastEnterCriticalSection();
void *old_callback = _drawsync_callback;
_drawsync_callback = func;
- _EXIT_CRITICAL();
+ FastExitCriticalSection();
return old_callback;
}
@@ -258,8 +255,8 @@ void ClearOTagR(uint32_t *ot, size_t length) {
DMA_BCR(6) = length & 0xffff;
DMA_CHCR(6) = 0x11000002;
- //while (DMA_CHCR(6) & (1 << 24))
- //__asm__ volatile("");
+ while (DMA_CHCR(6) & (1 << 24))
+ __asm__ volatile("");
}
void ClearOTag(uint32_t *ot, size_t length) {
diff --git a/libpsn00b/psxgpu/env.c b/libpsn00b/psxgpu/env.c
index f513727..8784947 100644
--- a/libpsn00b/psxgpu/env.c
+++ b/libpsn00b/psxgpu/env.c
@@ -11,6 +11,28 @@
extern GPU_VideoMode _gpu_video_mode;
+/* Private utilities */
+
+// Converts a texture window size value (a power of two in 8-128 range) into a
+// bit mask by setting the leading zeroes of the value:
+// 0 = 0b00000000 -> 0b00000
+// 8 = 0b00001000 -> 0b11111
+// 16 = 0b00010000 -> 0b11110
+// 32 = 0b00100000 -> 0b11100
+// 64 = 0b01000000 -> 0b11000
+// 128 = 0b10000000 -> 0b10000
+// The GPU uses the mask to process texture coordinates as follows:
+// x &= ~(mask << 3)
+// x |= (offset << 3) & (mask << 3)
+static inline uint32_t _get_window_mask(int size) {
+ uint32_t mask = size >> 3;
+
+ mask |= mask << 1;
+ mask |= mask << 2;
+ mask |= mask << 4;
+ return mask & 0x1f;
+}
+
/* Drawing API */
DRAWENV *SetDefDrawEnv(DRAWENV *env, int x, int y, int w, int h) {
@@ -24,8 +46,8 @@ DRAWENV *SetDefDrawEnv(DRAWENV *env, int x, int y, int w, int h) {
env->tw.x = 0;
env->tw.y = 0;
- env->tw.w = 0;
- env->tw.h = 0;
+ env->tw.w = 256;
+ env->tw.h = 256;
env->tpage = 0x0a;
env->dtd = 1;
@@ -41,50 +63,53 @@ int DrawOTagEnv(const uint32_t *ot, DRAWENV *env) {
DR_ENV *prim = &(env->dr_env);
// All commands are grouped into a single display list packet for
- // performance reasons (keep in mind that the GPU doesn't care about this
- // as the display list is parsed by the DMA unit in the CPU and only the
- // payload is sent to the GPU).
+ // performance reasons (the GPU does not care about the grouping as the
+ // display list is parsed by the DMA unit in the CPU).
setaddr(prim, ot);
- setlen(prim, 4);
+ setlen(prim, 5);
+
+ // Texture page (reset active page and set dither/mask bits)
+ prim->code[0] = 0xe1000000 | env->tpage;
+ prim->code[0] |= (env->dtd & 1) << 9;
+ prim->code[0] |= (env->dfe & 1) << 10;
+
+ // Texture window
+ prim->code[1] = 0xe2000000;
+ prim->code[1] |= _get_window_mask(env->tw.w);
+ prim->code[1] |= _get_window_mask(env->tw.h) << 5;
+ prim->code[1] |= (env->tw.x & 0xf8) << 7; // ((tw.x / 8) & 0x1f) << 10
+ prim->code[1] |= (env->tw.y & 0xf8) << 12; // ((tw.y / 8) & 0x1f) << 15
// Set drawing area top left
- prim->code[0] = 0xe3000000;
- prim->code[0] |= env->clip.x & 0x3ff;
- prim->code[0] |= (env->clip.y & 0x3ff) << 10;
+ prim->code[2] = 0xe3000000;
+ prim->code[2] |= env->clip.x & 0x3ff;
+ prim->code[2] |= (env->clip.y & 0x3ff) << 10;
// Set drawing area bottom right
- prim->code[1] = 0xe4000000;
- prim->code[1] |= (env->clip.x + (env->clip.w - 1)) & 0x3ff;
- prim->code[1] |= ((env->clip.y + (env->clip.h - 1)) & 0x3ff) << 10;
+ prim->code[3] = 0xe4000000;
+ prim->code[3] |= (env->clip.x + (env->clip.w - 1)) & 0x3ff;
+ prim->code[3] |= ((env->clip.y + (env->clip.h - 1)) & 0x3ff) << 10;
// Set drawing offset
- prim->code[2] = 0xe5000000;
- prim->code[2] |= (env->clip.x + env->ofs[0]) & 0x7ff;
- prim->code[2] |= ((env->clip.y + env->ofs[1]) & 0x7ff) << 11;
-
- // Texture page (reset active page and set dither/mask bits)
- prim->code[3] = 0xe1000000;
- prim->code[3] |= (env->dtd & 1) << 9;
- prim->code[3] |= (env->dfe & 1) << 10;
+ prim->code[4] = 0xe5000000;
+ prim->code[4] |= (env->clip.x + env->ofs[0]) & 0x7ff;
+ prim->code[4] |= ((env->clip.y + env->ofs[1]) & 0x7ff) << 11;
if (env->isbg) {
- setlen(prim, 7);
+ setlen(prim, 8);
// Rectangle fill
// FIXME: reportedly this command doesn't accept height values >511...
- prim->code[4] = 0x02000000;
- //prim->code[4] |= env->r0 | (env->g0 << 8) | (env->b0 << 16);
- prim->code[4] |= *((const uint32_t *) &(env->isbg)) >> 8;
- //prim->code[5] = env->clip.x;
- //prim->code[5] |= env->clip.y << 16;
- prim->code[5] = *((const uint32_t *) &(env->clip.x));
- prim->code[6] = env->clip.w;
- prim->code[6] |= _min(env->clip.h, 0x1ff) << 16;
+ prim->code[5] = 0x02000000;
+ //prim->code[5] |= env->r0 | (env->g0 << 8) | (env->b0 << 16);
+ //prim->code[6] = env->clip.x;
+ //prim->code[6] |= env->clip.y << 16;
+ prim->code[5] |= *((const uint32_t *) &(env->isbg)) >> 8;
+ prim->code[6] = *((const uint32_t *) &(env->clip.x));
+ prim->code[7] = env->clip.w;
+ prim->code[7] |= _min(env->clip.h, 0x1ff) << 16;
}
- //while (!(GPU_GP1 & (1 << 26)))
- //__asm__ volatile("");
-
return EnqueueDrawOp((void *) &DrawOTag2, (uint32_t) prim, 0, 0);
}
@@ -96,12 +121,10 @@ void PutDrawEnv(DRAWENV *env) {
// useful if the DRAWENV structure is never modified (which is the case most of
// the time).
void PutDrawEnvFast(DRAWENV *env) {
- if (!(env->dr_env.tag)) {
+ if (!(env->dr_env.tag))
DrawOTagEnv((const uint32_t *) 0x00ffffff, env);
- return;
- }
-
- DrawOTag((const uint32_t *) &(env->dr_env));
+ else
+ DrawOTag((const uint32_t *) &(env->dr_env));
}
/* Display API */
@@ -132,7 +155,7 @@ void PutDispEnv(const DISPENV *env) {
mode |= (env->isinter & 1) << 5;
mode |= (env->reverse & 1) << 7;
- if (env->disp.h >= 256)
+ if (env->disp.h > 256)
mode |= 1 << 2;
// Calculate the horizontal display range values. The original code was
diff --git a/libpsn00b/psxgpu/image.c b/libpsn00b/psxgpu/image.c
index bbdb7c8..fc018a4 100644
--- a/libpsn00b/psxgpu/image.c
+++ b/libpsn00b/psxgpu/image.c
@@ -1,6 +1,10 @@
/*
* PSn00bSDK GPU library (image and VRAM transfer functions)
* (C) 2022 spicyjpeg - MPL licensed
+ *
+ * TODO: MoveImage() is currently commented out as it won't trigger a DMA IRQ,
+ * making it unusable as a draw queue command. A way around this (perhaps using
+ * the GPU IRQ?) shall be found.
*/
#include <stdint.h>
@@ -8,7 +12,17 @@
#include <psxgpu.h>
#include <hwregs_c.h>
-#define DMA_CHUNK_LENGTH 8
+#define QUEUE_LENGTH 16
+#define DMA_CHUNK_LENGTH 8
+
+/* Internal globals */
+
+// LoadImage() and StoreImage() run asynchronously but may be called with a
+// pointer to a RECT struct in the stack, which might no longer be valid by the
+// time the transfer is actually started. This buffer is used to store a copy
+// of all RECTs passed to LoadImage()/StoreImage() as a workaround.
+static RECT _saved_rects[QUEUE_LENGTH];
+static int _next_saved_rect = 0;
/* Private utilities */
@@ -47,21 +61,33 @@ static void _dma_transfer(const RECT *rect, uint32_t *data, int write) {
/* VRAM transfer API */
int LoadImage(const RECT *rect, const uint32_t *data) {
+ int index = _next_saved_rect;
+
+ _saved_rects[index] = *rect;
+ _next_saved_rect = (index + 1) % QUEUE_LENGTH;
+
return EnqueueDrawOp(
- (void *) &_dma_transfer, (uint32_t) rect, (uint32_t) data, 1
+ (void *) &_dma_transfer,
+ (uint32_t) &_saved_rects[index],
+ (uint32_t) data,
+ 1
);
}
int StoreImage(const RECT *rect, uint32_t *data) {
+ int index = _next_saved_rect;
+
+ _saved_rects[index] = *rect;
+ _next_saved_rect = (index + 1) % QUEUE_LENGTH;
+
return EnqueueDrawOp(
- (void *) &_dma_transfer, (uint32_t) rect, (uint32_t) data, 0
+ (void *) &_dma_transfer,
+ (uint32_t) &_saved_rects[index],
+ (uint32_t) data,
+ 0
);
}
-int MoveImage(const RECT *rect, int x, int y) {
- return EnqueueDrawOp((void *) &MoveImage2, (uint32_t) rect, x, y);
-}
-
void LoadImage2(const RECT *rect, const uint32_t *data) {
_dma_transfer(rect, (uint32_t *) data, 1);
}
@@ -70,14 +96,14 @@ void StoreImage2(const RECT *rect, uint32_t *data) {
_dma_transfer(rect, data, 0);
}
-void MoveImage2(const RECT *rect, int x, int y) {
+/*void MoveImage2(const RECT *rect, int x, int y) {
GPU_GP0 = 0x80000000;
//GPU_GP0 = rect->x | (rect->y << 16);
GPU_GP0 = *((const uint32_t *) &(rect->x));
GPU_GP0 = (x & 0xffff) | (y << 16);
//GPU_GP0 = rect->w | (rect->h << 16);
GPU_GP0 = *((const uint32_t *) &(rect->w));
-}
+}*/
/* .TIM image parsers */