gpu: improve timings of clipped sprites
[pcsx_rearmed.git] / plugins / gpulib / gpu.c
index 49d63ee..e3943a2 100644 (file)
 #include <stdlib.h> /* for calloc */
 
 #include "gpu.h"
+#include "gpu_timing.h"
+#include "../../libpcsxcore/gpu.h" // meh
+#include "../../frontend/plugin_lib.h"
 
+#ifndef ARRAY_SIZE
 #define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0]))
+#endif
 #ifdef __GNUC__
 #define unlikely(x) __builtin_expect((x), 0)
 #define preload __builtin_prefetch
 
 struct psx_gpu gpu;
 
-static noinline int do_cmd_buffer(uint32_t *data, int count);
+static noinline int do_cmd_buffer(uint32_t *data, int count, int *cpu_cycles);
 static void finish_vram_transfer(int is_read);
 
 static noinline void do_cmd_reset(void)
 {
+  int dummy = 0;
   renderer_sync();
-
   if (unlikely(gpu.cmd_len > 0))
-    do_cmd_buffer(gpu.cmd_buffer, gpu.cmd_len);
+    do_cmd_buffer(gpu.cmd_buffer, gpu.cmd_len, &dummy);
   gpu.cmd_len = 0;
 
   if (unlikely(gpu.dma.h > 0))
@@ -74,7 +79,10 @@ static noinline void update_width(void)
   int hres = hres_all[(gpu.status >> 16) & 7];
   int pal = gpu.status & PSX_GPU_STATUS_PAL;
   int sw = gpu.screen.x2 - gpu.screen.x1;
+  int type = gpu.state.screen_centering_type;
   int x = 0, x_auto;
+  if (type == C_AUTO)
+    type = gpu.state.screen_centering_type_default;
   if (sw <= 0)
     /* nothing displayed? */;
   else {
@@ -83,10 +91,10 @@ static noinline void update_width(void)
     x = (x + 1) & ~1;   // blitter limitation
     sw /= hdiv;
     sw = (sw + 2) & ~3; // according to nocash
-    switch (gpu.state.screen_centering_type) {
-    case 1:
+    switch (type) {
+    case C_INGAME:
       break;
-    case 2:
+    case C_MANUAL:
       x = gpu.state.screen_centering_x;
       break;
     default:
@@ -128,9 +136,12 @@ static noinline void update_height(void)
     /* nothing displayed? */;
   else {
     switch (gpu.state.screen_centering_type) {
-    case 1:
+    case C_INGAME:
+      break;
+    case C_BORDERLESS:
+      y = 0;
       break;
-    case 2:
+    case C_MANUAL:
       y = gpu.state.screen_centering_y;
       break;
     default:
@@ -170,8 +181,8 @@ static noinline void decide_frameskip(void)
     gpu.frameskip.active = 0;
 
   if (!gpu.frameskip.active && gpu.frameskip.pending_fill[0] != 0) {
-    int dummy;
-    do_cmd_list(gpu.frameskip.pending_fill, 3, &dummy);
+    int dummy = 0;
+    do_cmd_list(gpu.frameskip.pending_fill, 3, &dummy, &dummy);
     gpu.frameskip.pending_fill[0] = 0;
   }
 }
@@ -212,8 +223,9 @@ static noinline void get_gpu_info(uint32_t data)
   }
 }
 
-// double, for overdraw guard
-#define VRAM_SIZE ((1024 * 512 * 2 * 2) + 4096)
+#ifndef max
+#define max(a, b) (((a) > (b)) ? (a) : (b))
+#endif
 
 //  Minimum 16-byte VRAM alignment needed by gpu_unai's pixel-skipping
 //  renderer/downscaler it uses in high res modes:
@@ -225,18 +237,31 @@ static noinline void get_gpu_info(uint32_t data)
        #define VRAM_ALIGN 16
 #endif
 
+// double, for overdraw guard + at least 1 page before
+#define VRAM_SIZE ((1024 * 512 * 2 * 2) + max(VRAM_ALIGN, 4096))
+
 // vram ptr received from mmap/malloc/alloc (will deallocate using this)
 static uint16_t *vram_ptr_orig = NULL;
 
-#ifdef GPULIB_USE_MMAP
+#ifndef GPULIB_USE_MMAP
+# ifdef __linux__
+#  define GPULIB_USE_MMAP 1
+# else
+#  define GPULIB_USE_MMAP 0
+# endif
+#endif
 static int map_vram(void)
 {
-  gpu.vram = vram_ptr_orig = gpu.mmap(VRAM_SIZE + (VRAM_ALIGN-1));
-  if (gpu.vram != NULL) {
-       // 4kb guard in front
+#if GPULIB_USE_MMAP
+  gpu.vram = vram_ptr_orig = gpu.mmap(VRAM_SIZE);
+#else
+  gpu.vram = vram_ptr_orig = calloc(VRAM_SIZE, 1);
+#endif
+  if (gpu.vram != NULL && gpu.vram != (void *)(intptr_t)-1) {
+    // 4kb guard in front
     gpu.vram += (4096 / 2);
-       // Align
-       gpu.vram = (uint16_t*)(((uintptr_t)gpu.vram + (VRAM_ALIGN-1)) & ~(VRAM_ALIGN-1));
+    // Align
+    gpu.vram = (uint16_t*)(((uintptr_t)gpu.vram + (VRAM_ALIGN-1)) & ~(VRAM_ALIGN-1));
     return 0;
   }
   else {
@@ -244,54 +269,9 @@ static int map_vram(void)
     return -1;
   }
 }
-#else
-static int map_vram(void)
-{
-  gpu.vram = vram_ptr_orig = (uint16_t*)calloc(VRAM_SIZE + (VRAM_ALIGN-1), 1);
-  if (gpu.vram != NULL) {
-       // 4kb guard in front
-    gpu.vram += (4096 / 2);
-       // Align
-       gpu.vram = (uint16_t*)(((uintptr_t)gpu.vram + (VRAM_ALIGN-1)) & ~(VRAM_ALIGN-1));
-    return 0;
-  } else {
-    fprintf(stderr, "could not allocate vram, expect crashes\n");
-    return -1;
-  }
-}
-
-static int allocate_vram(void)
-{
-  gpu.vram = vram_ptr_orig = (uint16_t*)calloc(VRAM_SIZE + (VRAM_ALIGN-1), 1);
-  if (gpu.vram != NULL) {
-       // 4kb guard in front
-    gpu.vram += (4096 / 2);
-       // Align
-       gpu.vram = (uint16_t*)(((uintptr_t)gpu.vram + (VRAM_ALIGN-1)) & ~(VRAM_ALIGN-1));
-    return 0;
-  } else {
-    fprintf(stderr, "could not allocate vram, expect crashes\n");
-    return -1;
-  }
-}
-#endif
 
 long GPUinit(void)
 {
-#ifndef GPULIB_USE_MMAP
-  if (gpu.vram == NULL) {
-    if (allocate_vram() != 0) {
-      printf("ERROR: could not allocate VRAM, exiting..\n");
-         exit(1);
-       }
-  }
-#endif
-
-  //extern uint32_t hSyncCount;         // in psxcounters.cpp
-  //extern uint32_t frame_counter;      // in psxcounters.cpp
-  //gpu.state.hcnt = &hSyncCount;
-  //gpu.state.frame_count = &frame_counter;
-
   int ret;
   ret  = vout_init();
   ret |= renderer_init();
@@ -319,7 +299,7 @@ long GPUshutdown(void)
   ret = vout_finish();
 
   if (vram_ptr_orig != NULL) {
-#ifdef GPULIB_USE_MMAP
+#if GPULIB_USE_MMAP
     gpu.munmap(vram_ptr_orig, VRAM_SIZE);
 #else
     free(vram_ptr_orig);
@@ -333,6 +313,7 @@ long GPUshutdown(void)
 void GPUwriteStatus(uint32_t data)
 {
   uint32_t cmd = data >> 24;
+  int src_x, src_y;
 
   if (cmd < ARRAY_SIZE(gpu.regs)) {
     if (cmd > 1 && cmd != 5 && gpu.regs[cmd] == data)
@@ -362,14 +343,17 @@ void GPUwriteStatus(uint32_t data)
       gpu.status |= PSX_GPU_STATUS_DMA(data & 3);
       break;
     case 0x05:
-      gpu.screen.src_x = data & 0x3ff;
-      gpu.screen.src_y = (data >> 10) & 0x1ff;
-      renderer_notify_scanout_x_change(gpu.screen.src_x, gpu.screen.hres);
-      if (gpu.frameskip.set) {
-        decide_frameskip_allow(gpu.ex_regs[3]);
-        if (gpu.frameskip.last_flip_frame != *gpu.state.frame_count) {
-          decide_frameskip();
-          gpu.frameskip.last_flip_frame = *gpu.state.frame_count;
+      src_x = data & 0x3ff; src_y = (data >> 10) & 0x1ff;
+      if (src_x != gpu.screen.src_x || src_y != gpu.screen.src_y) {
+        gpu.screen.src_x = src_x;
+        gpu.screen.src_y = src_y;
+        renderer_notify_scanout_change(src_x, src_y);
+        if (gpu.frameskip.set) {
+          decide_frameskip_allow(gpu.ex_regs[3]);
+          if (gpu.frameskip.last_flip_frame != *gpu.state.frame_count) {
+            decide_frameskip();
+            gpu.frameskip.last_flip_frame = *gpu.state.frame_count;
+          }
         }
       }
       break;
@@ -517,18 +501,24 @@ static void start_vram_transfer(uint32_t pos_word, uint32_t size_word, int is_re
 
   log_io("start_vram_transfer %c (%d, %d) %dx%d\n", is_read ? 'r' : 'w',
     gpu.dma.x, gpu.dma.y, gpu.dma.w, gpu.dma.h);
+  if (gpu.gpu_state_change)
+    gpu.gpu_state_change(PGS_VRAM_TRANSFER_START);
 }
 
 static void finish_vram_transfer(int is_read)
 {
   if (is_read)
     gpu.status &= ~PSX_GPU_STATUS_IMG;
-  else
+  else {
+    gpu.state.fb_dirty = 1;
     renderer_update_caches(gpu.dma_start.x, gpu.dma_start.y,
                            gpu.dma_start.w, gpu.dma_start.h, 0);
+  }
+  if (gpu.gpu_state_change)
+    gpu.gpu_state_change(PGS_VRAM_TRANSFER_END);
 }
 
-static void do_vram_copy(const uint32_t *params)
+static void do_vram_copy(const uint32_t *params, int *cpu_cycles)
 {
   const uint32_t sx =  LE32TOH(params[0]) & 0x3FF;
   const uint32_t sy = (LE32TOH(params[0]) >> 16) & 0x1FF;
@@ -540,6 +530,7 @@ static void do_vram_copy(const uint32_t *params)
   uint16_t lbuf[128];
   uint32_t x, y;
 
+  *cpu_cycles += gput_copy(w, h);
   if (sx == dx && sy == dy && msb == 0)
     return;
 
@@ -575,7 +566,7 @@ static void do_vram_copy(const uint32_t *params)
 
 static noinline int do_cmd_list_skip(uint32_t *data, int count, int *last_cmd)
 {
-  int cmd = 0, pos = 0, len, dummy, v;
+  int cmd = 0, pos = 0, len, dummy = 0, v;
   int skip = 1;
 
   gpu.frameskip.pending_fill[0] = 0;
@@ -589,7 +580,7 @@ static noinline int do_cmd_list_skip(uint32_t *data, int count, int *last_cmd)
       case 0x02:
         if ((LE32TOH(list[2]) & 0x3ff) > gpu.screen.w || ((LE32TOH(list[2]) >> 16) & 0x1ff) > gpu.screen.h)
           // clearing something large, don't skip
-          do_cmd_list(list, 3, &dummy);
+          do_cmd_list(list, 3, &dummy, &dummy);
         else
           memcpy(gpu.frameskip.pending_fill, list, 3 * 4);
         break;
@@ -639,7 +630,7 @@ static noinline int do_cmd_list_skip(uint32_t *data, int count, int *last_cmd)
   return pos;
 }
 
-static noinline int do_cmd_buffer(uint32_t *data, int count)
+static noinline int do_cmd_buffer(uint32_t *data, int count, int *cpu_cycles)
 {
   int cmd, pos;
   uint32_t old_e3 = gpu.ex_regs[3];
@@ -673,16 +664,22 @@ static noinline int do_cmd_buffer(uint32_t *data, int count)
         cmd = -1; // incomplete cmd, can't consume yet
         break;
       }
-      do_vram_copy(data + pos + 1);
+      do_vram_copy(data + pos + 1, cpu_cycles);
+      vram_dirty = 1;
       pos += 4;
       continue;
     }
+    else if (cmd == 0x1f) {
+      log_anomaly("irq1?\n");
+      pos++;
+      continue;
+    }
 
     // 0xex cmds might affect frameskip.allow, so pass to do_cmd_list_skip
     if (gpu.frameskip.active && (gpu.frameskip.allow || ((LE32TOH(data[pos]) >> 24) & 0xf0) == 0xe0))
       pos += do_cmd_list_skip(data + pos, count - pos, &cmd);
     else {
-      pos += do_cmd_list(data + pos, count - pos, &cmd);
+      pos += do_cmd_list(data + pos, count - pos, cpu_cycles, &cmd);
       vram_dirty = 1;
     }
 
@@ -703,24 +700,29 @@ static noinline int do_cmd_buffer(uint32_t *data, int count)
   return count - pos;
 }
 
-static void flush_cmd_buffer(void)
+static noinline void flush_cmd_buffer(void)
 {
-  int left = do_cmd_buffer(gpu.cmd_buffer, gpu.cmd_len);
+  int dummy = 0, left;
+  left = do_cmd_buffer(gpu.cmd_buffer, gpu.cmd_len, &dummy);
   if (left > 0)
     memmove(gpu.cmd_buffer, gpu.cmd_buffer + gpu.cmd_len - left, left * 4);
-  gpu.cmd_len = left;
+  if (left != gpu.cmd_len) {
+    if (!gpu.dma.h && gpu.gpu_state_change)
+      gpu.gpu_state_change(PGS_PRIMITIVE_START);
+    gpu.cmd_len = left;
+  }
 }
 
 void GPUwriteDataMem(uint32_t *mem, int count)
 {
-  int left;
+  int dummy = 0, left;
 
   log_io("gpu_dma_write %p %d\n", mem, count);
 
   if (unlikely(gpu.cmd_len > 0))
     flush_cmd_buffer();
 
-  left = do_cmd_buffer(mem, count);
+  left = do_cmd_buffer(mem, count, &dummy);
   if (left)
     log_anomaly("GPUwriteDataMem: discarded %d/%d words\n", left, count);
 }
@@ -737,7 +739,7 @@ long GPUdmaChain(uint32_t *rambase, uint32_t start_addr, uint32_t *progress_addr
 {
   uint32_t addr, *list, ld_addr = 0;
   int len, left, count;
-  long cpu_cycles = 0;
+  int cpu_cycles = 0;
 
   preload(rambase + (start_addr & 0x1fffff) / 4);
 
@@ -757,8 +759,8 @@ long GPUdmaChain(uint32_t *rambase, uint32_t start_addr, uint32_t *progress_addr
     if (len > 0)
       cpu_cycles += 5 + len;
 
-    log_io(".chain %08lx #%d+%d\n",
-      (long)(list - rambase) * 4, len, gpu.cmd_len);
+    log_io(".chain %08lx #%d+%d %u\n",
+      (long)(list - rambase) * 4, len, gpu.cmd_len, cpu_cycles);
     if (unlikely(gpu.cmd_len > 0)) {
       if (gpu.cmd_len + len > ARRAY_SIZE(gpu.cmd_buffer)) {
         log_anomaly("cmd_buffer overflow, likely garbage commands\n");
@@ -771,7 +773,7 @@ long GPUdmaChain(uint32_t *rambase, uint32_t start_addr, uint32_t *progress_addr
     }
 
     if (len) {
-      left = do_cmd_buffer(list + 1, len);
+      left = do_cmd_buffer(list + 1, len, &cpu_cycles);
       if (left) {
         memcpy(gpu.cmd_buffer, list + 1 + len - left, left * 4);
         gpu.cmd_len = left;
@@ -892,7 +894,7 @@ long GPUfreeze(uint32_t type, struct GPUFreeze *freeze)
         GPUwriteStatus((i << 24) | (gpu.regs[i] ^ 1));
       }
       renderer_sync_ecmds(gpu.ex_regs);
-      renderer_update_caches(0, 0, 1024, 512, 1);
+      renderer_update_caches(0, 0, 1024, 512, 0);
       break;
   }
 
@@ -969,8 +971,6 @@ void GPUgetScreenInfo(int *y, int *base_hres)
     *base_hres >>= 1;
 }
 
-#include "../../frontend/plugin_lib.h"
-
 void GPUrearmedCallbacks(const struct rearmed_cbs *cbs)
 {
   gpu.frameskip.set = cbs->frameskip;
@@ -983,6 +983,7 @@ void GPUrearmedCallbacks(const struct rearmed_cbs *cbs)
   gpu.state.frame_count = cbs->gpu_frame_count;
   gpu.state.allow_interlace = cbs->gpu_neon.allow_interlace;
   gpu.state.enhancement_enable = cbs->gpu_neon.enhancement_enable;
+  gpu.state.screen_centering_type_default = cbs->screen_centering_type_default;
   if (gpu.state.screen_centering_type != cbs->screen_centering_type
       || gpu.state.screen_centering_x != cbs->screen_centering_x
       || gpu.state.screen_centering_y != cbs->screen_centering_y) {
@@ -995,6 +996,7 @@ void GPUrearmedCallbacks(const struct rearmed_cbs *cbs)
 
   gpu.mmap = cbs->mmap;
   gpu.munmap = cbs->munmap;
+  gpu.gpu_state_change = cbs->gpu_state_change;
 
   // delayed vram mmap
   if (gpu.vram == NULL)