gpulib: Add proper support for big-endian
[pcsx_rearmed.git] / plugins / gpulib / gpu.c
1 /*
2  * (C) GraÅžvydas "notaz" Ignotas, 2011-2012
3  *
4  * This work is licensed under the terms of any of these licenses
5  * (at your option):
6  *  - GNU GPL, version 2 or later.
7  *  - GNU LGPL, version 2.1 or later.
8  * See the COPYING file in the top-level directory.
9  */
10
11 #include <stdio.h>
12 #include <string.h>
13 #include "gpu.h"
14
15 #define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0]))
16 #ifdef __GNUC__
17 #define unlikely(x) __builtin_expect((x), 0)
18 #define preload __builtin_prefetch
19 #define noinline __attribute__((noinline))
20 #else
21 #define unlikely(x)
22 #define preload(...)
23 #define noinline
24 #endif
25
26 #define gpu_log(fmt, ...) \
27   printf("%d:%03d: " fmt, *gpu.state.frame_count, *gpu.state.hcnt, ##__VA_ARGS__)
28
29 //#define log_io gpu_log
30 #define log_io(...)
31 //#define log_anomaly gpu_log
32 #define log_anomaly(...)
33
34 struct psx_gpu gpu;
35
36 static noinline int do_cmd_buffer(uint32_t *data, int count);
37 static void finish_vram_transfer(int is_read);
38
39 static noinline void do_cmd_reset(void)
40 {
41   if (unlikely(gpu.cmd_len > 0))
42     do_cmd_buffer(gpu.cmd_buffer, gpu.cmd_len);
43   gpu.cmd_len = 0;
44
45   if (unlikely(gpu.dma.h > 0))
46     finish_vram_transfer(gpu.dma_start.is_read);
47   gpu.dma.h = 0;
48 }
49
50 static noinline void do_reset(void)
51 {
52   unsigned int i;
53
54   do_cmd_reset();
55
56   memset(gpu.regs, 0, sizeof(gpu.regs));
57   for (i = 0; i < sizeof(gpu.ex_regs) / sizeof(gpu.ex_regs[0]); i++)
58     gpu.ex_regs[i] = (0xe0 + i) << 24;
59   gpu.status = 0x14802000;
60   gpu.gp0 = 0;
61   gpu.regs[3] = 1;
62   gpu.screen.hres = gpu.screen.w = 256;
63   gpu.screen.vres = gpu.screen.h = 240;
64 }
65
66 static noinline void update_width(void)
67 {
68   int sw = gpu.screen.x2 - gpu.screen.x1;
69   if (sw <= 0 || sw >= 2560)
70     // full width
71     gpu.screen.w = gpu.screen.hres;
72   else
73     gpu.screen.w = sw * gpu.screen.hres / 2560;
74 }
75
76 static noinline void update_height(void)
77 {
78   // TODO: emulate this properly..
79   int sh = gpu.screen.y2 - gpu.screen.y1;
80   if (gpu.status & PSX_GPU_STATUS_DHEIGHT)
81     sh *= 2;
82   if (sh <= 0 || sh > gpu.screen.vres)
83     sh = gpu.screen.vres;
84
85   gpu.screen.h = sh;
86 }
87
88 static noinline void decide_frameskip(void)
89 {
90   if (gpu.frameskip.active)
91     gpu.frameskip.cnt++;
92   else {
93     gpu.frameskip.cnt = 0;
94     gpu.frameskip.frame_ready = 1;
95   }
96
97   if (!gpu.frameskip.active && *gpu.frameskip.advice)
98     gpu.frameskip.active = 1;
99   else if (gpu.frameskip.set > 0 && gpu.frameskip.cnt < gpu.frameskip.set)
100     gpu.frameskip.active = 1;
101   else
102     gpu.frameskip.active = 0;
103
104   if (!gpu.frameskip.active && gpu.frameskip.pending_fill[0] != 0) {
105     int dummy;
106     do_cmd_list(gpu.frameskip.pending_fill, 3, &dummy);
107     gpu.frameskip.pending_fill[0] = 0;
108   }
109 }
110
111 static noinline int decide_frameskip_allow(uint32_t cmd_e3)
112 {
113   // no frameskip if it decides to draw to display area,
114   // but not for interlace since it'll most likely always do that
115   uint32_t x = cmd_e3 & 0x3ff;
116   uint32_t y = (cmd_e3 >> 10) & 0x3ff;
117   gpu.frameskip.allow = (gpu.status & PSX_GPU_STATUS_INTERLACE) ||
118     (uint32_t)(x - gpu.screen.x) >= (uint32_t)gpu.screen.w ||
119     (uint32_t)(y - gpu.screen.y) >= (uint32_t)gpu.screen.h;
120   return gpu.frameskip.allow;
121 }
122
123 static noinline void get_gpu_info(uint32_t data)
124 {
125   switch (data & 0x0f) {
126     case 0x02:
127     case 0x03:
128     case 0x04:
129     case 0x05:
130       gpu.gp0 = gpu.ex_regs[data & 7] & 0xfffff;
131       break;
132     case 0x06:
133       gpu.gp0 = gpu.ex_regs[5] & 0xfffff;
134       break;
135     case 0x07:
136       gpu.gp0 = 2;
137       break;
138     default:
139       gpu.gp0 = 0;
140       break;
141   }
142 }
143
144 // double, for overdraw guard
145 #define VRAM_SIZE (1024 * 512 * 2 * 2)
146
147 static int map_vram(void)
148 {
149   gpu.vram = gpu.mmap(VRAM_SIZE);
150   if (gpu.vram != NULL) {
151     gpu.vram += 4096 / 2;
152     return 0;
153   }
154   else {
155     fprintf(stderr, "could not map vram, expect crashes\n");
156     return -1;
157   }
158 }
159
160 long GPUinit(void)
161 {
162   int ret;
163   ret  = vout_init();
164   ret |= renderer_init();
165
166   gpu.state.frame_count = &gpu.zero;
167   gpu.state.hcnt = &gpu.zero;
168   gpu.frameskip.active = 0;
169   gpu.cmd_len = 0;
170   do_reset();
171
172   if (gpu.mmap != NULL) {
173     if (map_vram() != 0)
174       ret = -1;
175   }
176   return ret;
177 }
178
179 long GPUshutdown(void)
180 {
181   long ret;
182
183   renderer_finish();
184   ret = vout_finish();
185   if (gpu.vram != NULL) {
186     gpu.vram -= 4096 / 2;
187     gpu.munmap(gpu.vram, VRAM_SIZE);
188   }
189   gpu.vram = NULL;
190
191   return ret;
192 }
193
194 void GPUwriteStatus(uint32_t data)
195 {
196   static const short hres[8] = { 256, 368, 320, 384, 512, 512, 640, 640 };
197   static const short vres[4] = { 240, 480, 256, 480 };
198   uint32_t cmd = data >> 24;
199
200   if (cmd < ARRAY_SIZE(gpu.regs)) {
201     if (cmd > 1 && cmd != 5 && gpu.regs[cmd] == data)
202       return;
203     gpu.regs[cmd] = data;
204   }
205
206   gpu.state.fb_dirty = 1;
207
208   switch (cmd) {
209     case 0x00:
210       do_reset();
211       break;
212     case 0x01:
213       do_cmd_reset();
214       break;
215     case 0x03:
216       if (data & 1)
217         gpu.status |= PSX_GPU_STATUS_BLANKING;
218       else
219         gpu.status &= ~PSX_GPU_STATUS_BLANKING;
220       break;
221     case 0x04:
222       gpu.status &= ~PSX_GPU_STATUS_DMA_MASK;
223       gpu.status |= PSX_GPU_STATUS_DMA(data & 3);
224       break;
225     case 0x05:
226       gpu.screen.x = data & 0x3ff;
227       gpu.screen.y = (data >> 10) & 0x1ff;
228       if (gpu.frameskip.set) {
229         decide_frameskip_allow(gpu.ex_regs[3]);
230         if (gpu.frameskip.last_flip_frame != *gpu.state.frame_count) {
231           decide_frameskip();
232           gpu.frameskip.last_flip_frame = *gpu.state.frame_count;
233         }
234       }
235       break;
236     case 0x06:
237       gpu.screen.x1 = data & 0xfff;
238       gpu.screen.x2 = (data >> 12) & 0xfff;
239       update_width();
240       break;
241     case 0x07:
242       gpu.screen.y1 = data & 0x3ff;
243       gpu.screen.y2 = (data >> 10) & 0x3ff;
244       update_height();
245       break;
246     case 0x08:
247       gpu.status = (gpu.status & ~0x7f0000) | ((data & 0x3F) << 17) | ((data & 0x40) << 10);
248       gpu.screen.hres = hres[(gpu.status >> 16) & 7];
249       gpu.screen.vres = vres[(gpu.status >> 19) & 3];
250       update_width();
251       update_height();
252       renderer_notify_res_change();
253       break;
254     default:
255       if ((cmd & 0xf0) == 0x10)
256         get_gpu_info(data);
257       break;
258   }
259
260 #ifdef GPUwriteStatus_ext
261   GPUwriteStatus_ext(data);
262 #endif
263 }
264
265 const unsigned char cmd_lengths[256] =
266 {
267         0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
268         0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
269         3, 3, 3, 3, 6, 6, 6, 6, 4, 4, 4, 4, 8, 8, 8, 8, // 20
270         5, 5, 5, 5, 8, 8, 8, 8, 7, 7, 7, 7, 11, 11, 11, 11,
271         2, 2, 2, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3, // 40
272         3, 3, 3, 3, 3, 3, 3, 3, 4, 4, 4, 4, 4, 4, 4, 4,
273         2, 2, 2, 2, 3, 3, 3, 3, 1, 1, 1, 1, 0, 0, 0, 0, // 60
274         1, 1, 1, 1, 2, 2, 2, 2, 1, 1, 1, 1, 2, 2, 2, 2,
275         3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 80
276         0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
277         2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // a0
278         0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
279         2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // c0
280         0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
281         0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // e0
282         0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
283 };
284
285 #define VRAM_MEM_XY(x, y) &gpu.vram[(y) * 1024 + (x)]
286
287 static inline void do_vram_line(int x, int y, uint16_t *mem, int l, int is_read)
288 {
289   uint16_t *vram = VRAM_MEM_XY(x, y);
290   if (is_read)
291     memcpy(mem, vram, l * 2);
292   else
293     memcpy(vram, mem, l * 2);
294 }
295
296 static int do_vram_io(uint32_t *data, int count, int is_read)
297 {
298   int count_initial = count;
299   uint16_t *sdata = (uint16_t *)data;
300   int x = gpu.dma.x, y = gpu.dma.y;
301   int w = gpu.dma.w, h = gpu.dma.h;
302   int o = gpu.dma.offset;
303   int l;
304   count *= 2; // operate in 16bpp pixels
305
306   if (gpu.dma.offset) {
307     l = w - gpu.dma.offset;
308     if (count < l)
309       l = count;
310
311     do_vram_line(x + o, y, sdata, l, is_read);
312
313     if (o + l < w)
314       o += l;
315     else {
316       o = 0;
317       y++;
318       h--;
319     }
320     sdata += l;
321     count -= l;
322   }
323
324   for (; h > 0 && count >= w; sdata += w, count -= w, y++, h--) {
325     y &= 511;
326     do_vram_line(x, y, sdata, w, is_read);
327   }
328
329   if (h > 0) {
330     if (count > 0) {
331       y &= 511;
332       do_vram_line(x, y, sdata, count, is_read);
333       o = count;
334       count = 0;
335     }
336   }
337   else
338     finish_vram_transfer(is_read);
339   gpu.dma.y = y;
340   gpu.dma.h = h;
341   gpu.dma.offset = o;
342
343   return count_initial - count / 2;
344 }
345
346 static void start_vram_transfer(uint32_t pos_word, uint32_t size_word, int is_read)
347 {
348   if (gpu.dma.h)
349     log_anomaly("start_vram_transfer while old unfinished\n");
350
351   gpu.dma.x = pos_word & 0x3ff;
352   gpu.dma.y = (pos_word >> 16) & 0x1ff;
353   gpu.dma.w = ((size_word - 1) & 0x3ff) + 1;
354   gpu.dma.h = (((size_word >> 16) - 1) & 0x1ff) + 1;
355   gpu.dma.offset = 0;
356   gpu.dma.is_read = is_read;
357   gpu.dma_start = gpu.dma;
358
359   renderer_flush_queues();
360   if (is_read) {
361     gpu.status |= PSX_GPU_STATUS_IMG;
362     // XXX: wrong for width 1
363     memcpy(&gpu.gp0, VRAM_MEM_XY(gpu.dma.x, gpu.dma.y), 4);
364     gpu.state.last_vram_read_frame = *gpu.state.frame_count;
365   }
366
367   log_io("start_vram_transfer %c (%d, %d) %dx%d\n", is_read ? 'r' : 'w',
368     gpu.dma.x, gpu.dma.y, gpu.dma.w, gpu.dma.h);
369 }
370
371 static void finish_vram_transfer(int is_read)
372 {
373   if (is_read)
374     gpu.status &= ~PSX_GPU_STATUS_IMG;
375   else
376     renderer_update_caches(gpu.dma_start.x, gpu.dma_start.y,
377                            gpu.dma_start.w, gpu.dma_start.h);
378 }
379
380 static noinline int do_cmd_list_skip(uint32_t *data, int count, int *last_cmd)
381 {
382   int cmd = 0, pos = 0, len, dummy, v;
383   int skip = 1;
384
385   gpu.frameskip.pending_fill[0] = 0;
386
387   while (pos < count && skip) {
388     uint32_t *list = data + pos;
389     cmd = LE32TOH(list[0]) >> 24;
390     len = 1 + cmd_lengths[cmd];
391
392     switch (cmd) {
393       case 0x02:
394         if ((LE32TOH(list[2]) & 0x3ff) > gpu.screen.w || ((LE32TOH(list[2]) >> 16) & 0x1ff) > gpu.screen.h)
395           // clearing something large, don't skip
396           do_cmd_list(list, 3, &dummy);
397         else
398           memcpy(gpu.frameskip.pending_fill, list, 3 * 4);
399         break;
400       case 0x24 ... 0x27:
401       case 0x2c ... 0x2f:
402       case 0x34 ... 0x37:
403       case 0x3c ... 0x3f:
404         gpu.ex_regs[1] &= ~0x1ff;
405         gpu.ex_regs[1] |= LE32TOH(list[4 + ((cmd >> 4) & 1)]) & 0x1ff;
406         break;
407       case 0x48 ... 0x4F:
408         for (v = 3; pos + v < count; v++)
409         {
410           if ((list[v] & HTOLE32(0xf000f000)) == HTOLE32(0x50005000))
411             break;
412         }
413         len += v - 3;
414         break;
415       case 0x58 ... 0x5F:
416         for (v = 4; pos + v < count; v += 2)
417         {
418           if ((list[v] & HTOLE32(0xf000f000)) == HTOLE32(0x50005000))
419             break;
420         }
421         len += v - 4;
422         break;
423       default:
424         if (cmd == 0xe3)
425           skip = decide_frameskip_allow(LE32TOH(list[0]));
426         if ((cmd & 0xf8) == 0xe0)
427           gpu.ex_regs[cmd & 7] = LE32TOH(list[0]);
428         break;
429     }
430
431     if (pos + len > count) {
432       cmd = -1;
433       break; // incomplete cmd
434     }
435     if (0xa0 <= cmd && cmd <= 0xdf)
436       break; // image i/o
437
438     pos += len;
439   }
440
441   renderer_sync_ecmds(gpu.ex_regs);
442   *last_cmd = cmd;
443   return pos;
444 }
445
446 static noinline int do_cmd_buffer(uint32_t *data, int count)
447 {
448   int cmd, pos;
449   uint32_t old_e3 = gpu.ex_regs[3];
450   int vram_dirty = 0;
451
452   // process buffer
453   for (pos = 0; pos < count; )
454   {
455     if (gpu.dma.h && !gpu.dma_start.is_read) { // XXX: need to verify
456       vram_dirty = 1;
457       pos += do_vram_io(data + pos, count - pos, 0);
458       if (pos == count)
459         break;
460     }
461
462     cmd = LE32TOH(data[pos]) >> 24;
463     if (0xa0 <= cmd && cmd <= 0xdf) {
464       if (unlikely((pos+2) >= count)) {
465         // incomplete vram write/read cmd, can't consume yet
466         cmd = -1;
467         break;
468       }
469
470       // consume vram write/read cmd
471       start_vram_transfer(LE32TOH(data[pos + 1]), LE32TOH(data[pos + 2]), (cmd & 0xe0) == 0xc0);
472       pos += 3;
473       continue;
474     }
475
476     // 0xex cmds might affect frameskip.allow, so pass to do_cmd_list_skip
477     if (gpu.frameskip.active && (gpu.frameskip.allow || ((LE32TOH(data[pos]) >> 24) & 0xf0) == 0xe0))
478       pos += do_cmd_list_skip(data + pos, count - pos, &cmd);
479     else {
480       pos += do_cmd_list(data + pos, count - pos, &cmd);
481       vram_dirty = 1;
482     }
483
484     if (cmd == -1)
485       // incomplete cmd
486       break;
487   }
488
489   gpu.status &= ~0x1fff;
490   gpu.status |= gpu.ex_regs[1] & 0x7ff;
491   gpu.status |= (gpu.ex_regs[6] & 3) << 11;
492
493   gpu.state.fb_dirty |= vram_dirty;
494
495   if (old_e3 != gpu.ex_regs[3])
496     decide_frameskip_allow(gpu.ex_regs[3]);
497
498   return count - pos;
499 }
500
501 static void flush_cmd_buffer(void)
502 {
503   int left = do_cmd_buffer(gpu.cmd_buffer, gpu.cmd_len);
504   if (left > 0)
505     memmove(gpu.cmd_buffer, gpu.cmd_buffer + gpu.cmd_len - left, left * 4);
506   gpu.cmd_len = left;
507 }
508
509 void GPUwriteDataMem(uint32_t *mem, int count)
510 {
511   int left;
512
513   log_io("gpu_dma_write %p %d\n", mem, count);
514
515   if (unlikely(gpu.cmd_len > 0))
516     flush_cmd_buffer();
517
518   left = do_cmd_buffer(mem, count);
519   if (left)
520     log_anomaly("GPUwriteDataMem: discarded %d/%d words\n", left, count);
521 }
522
523 void GPUwriteData(uint32_t data)
524 {
525   log_io("gpu_write %08x\n", data);
526   gpu.cmd_buffer[gpu.cmd_len++] = HTOLE32(data);
527   if (gpu.cmd_len >= CMD_BUFFER_LEN)
528     flush_cmd_buffer();
529 }
530
531 long GPUdmaChain(uint32_t *rambase, uint32_t start_addr)
532 {
533   uint32_t addr, *list, ld_addr = 0;
534   int len, left, count;
535   long cpu_cycles = 0;
536
537   preload(rambase + (start_addr & 0x1fffff) / 4);
538
539   if (unlikely(gpu.cmd_len > 0))
540     flush_cmd_buffer();
541
542   log_io("gpu_dma_chain\n");
543   addr = start_addr & 0xffffff;
544   for (count = 0; (addr & 0x800000) == 0; count++)
545   {
546     list = rambase + (addr & 0x1fffff) / 4;
547     len = LE32TOH(list[0]) >> 24;
548     addr = LE32TOH(list[0]) & 0xffffff;
549     preload(rambase + (addr & 0x1fffff) / 4);
550
551     cpu_cycles += 10;
552     if (len > 0)
553       cpu_cycles += 5 + len;
554
555     log_io(".chain %08x #%d\n", (list - rambase) * 4, len);
556
557     if (len) {
558       left = do_cmd_buffer(list + 1, len);
559       if (left)
560         log_anomaly("GPUdmaChain: discarded %d/%d words\n", left, len);
561     }
562
563     #define LD_THRESHOLD (8*1024)
564     if (count >= LD_THRESHOLD) {
565       if (count == LD_THRESHOLD) {
566         ld_addr = addr;
567         continue;
568       }
569
570       // loop detection marker
571       // (bit23 set causes DMA error on real machine, so
572       //  unlikely to be ever set by the game)
573       list[0] |= HTOLE32(0x800000);
574     }
575   }
576
577   if (ld_addr != 0) {
578     // remove loop detection markers
579     count -= LD_THRESHOLD + 2;
580     addr = ld_addr & 0x1fffff;
581     while (count-- > 0) {
582       list = rambase + addr / 4;
583       addr = LE32TOH(list[0]) & 0x1fffff;
584       list[0] &= HTOLE32(~0x800000);
585     }
586   }
587
588   gpu.state.last_list.frame = *gpu.state.frame_count;
589   gpu.state.last_list.hcnt = *gpu.state.hcnt;
590   gpu.state.last_list.cycles = cpu_cycles;
591   gpu.state.last_list.addr = start_addr;
592
593   return cpu_cycles;
594 }
595
596 void GPUreadDataMem(uint32_t *mem, int count)
597 {
598   log_io("gpu_dma_read  %p %d\n", mem, count);
599
600   if (unlikely(gpu.cmd_len > 0))
601     flush_cmd_buffer();
602
603   if (gpu.dma.h)
604     do_vram_io(mem, count, 1);
605 }
606
607 uint32_t GPUreadData(void)
608 {
609   uint32_t ret;
610
611   if (unlikely(gpu.cmd_len > 0))
612     flush_cmd_buffer();
613
614   ret = gpu.gp0;
615   if (gpu.dma.h)
616     do_vram_io(&ret, 1, 1);
617
618   log_io("gpu_read %08x\n", ret);
619   return ret;
620 }
621
622 uint32_t GPUreadStatus(void)
623 {
624   uint32_t ret;
625
626   if (unlikely(gpu.cmd_len > 0))
627     flush_cmd_buffer();
628
629   ret = gpu.status;
630   log_io("gpu_read_status %08x\n", ret);
631   return ret;
632 }
633
634 struct GPUFreeze
635 {
636   uint32_t ulFreezeVersion;      // should be always 1 for now (set by main emu)
637   uint32_t ulStatus;             // current gpu status
638   uint32_t ulControl[256];       // latest control register values
639   unsigned char psxVRam[1024*1024*2]; // current VRam image (full 2 MB for ZN)
640 };
641
642 long GPUfreeze(uint32_t type, struct GPUFreeze *freeze)
643 {
644   int i;
645
646   switch (type) {
647     case 1: // save
648       if (gpu.cmd_len > 0)
649         flush_cmd_buffer();
650       memcpy(freeze->psxVRam, gpu.vram, 1024 * 512 * 2);
651       memcpy(freeze->ulControl, gpu.regs, sizeof(gpu.regs));
652       memcpy(freeze->ulControl + 0xe0, gpu.ex_regs, sizeof(gpu.ex_regs));
653       freeze->ulStatus = gpu.status;
654       break;
655     case 0: // load
656       memcpy(gpu.vram, freeze->psxVRam, 1024 * 512 * 2);
657       memcpy(gpu.regs, freeze->ulControl, sizeof(gpu.regs));
658       memcpy(gpu.ex_regs, freeze->ulControl + 0xe0, sizeof(gpu.ex_regs));
659       gpu.status = freeze->ulStatus;
660       gpu.cmd_len = 0;
661       for (i = 8; i > 0; i--) {
662         gpu.regs[i] ^= 1; // avoid reg change detection
663         GPUwriteStatus((i << 24) | (gpu.regs[i] ^ 1));
664       }
665       renderer_sync_ecmds(gpu.ex_regs);
666       renderer_update_caches(0, 0, 1024, 512);
667       break;
668   }
669
670   return 1;
671 }
672
673 void GPUupdateLace(void)
674 {
675   if (gpu.cmd_len > 0)
676     flush_cmd_buffer();
677   renderer_flush_queues();
678
679   if (gpu.status & PSX_GPU_STATUS_BLANKING) {
680     if (!gpu.state.blanked) {
681       vout_blank();
682       gpu.state.blanked = 1;
683       gpu.state.fb_dirty = 1;
684     }
685     return;
686   }
687
688   if (!gpu.state.fb_dirty)
689     return;
690
691   if (gpu.frameskip.set) {
692     if (!gpu.frameskip.frame_ready) {
693       if (*gpu.state.frame_count - gpu.frameskip.last_flip_frame < 9)
694         return;
695       gpu.frameskip.active = 0;
696     }
697     gpu.frameskip.frame_ready = 0;
698   }
699
700   vout_update();
701   gpu.state.fb_dirty = 0;
702   gpu.state.blanked = 0;
703 }
704
705 void GPUvBlank(int is_vblank, int lcf)
706 {
707   int interlace = gpu.state.allow_interlace
708     && (gpu.status & PSX_GPU_STATUS_INTERLACE)
709     && (gpu.status & PSX_GPU_STATUS_DHEIGHT);
710   // interlace doesn't look nice on progressive displays,
711   // so we have this "auto" mode here for games that don't read vram
712   if (gpu.state.allow_interlace == 2
713       && *gpu.state.frame_count - gpu.state.last_vram_read_frame > 1)
714   {
715     interlace = 0;
716   }
717   if (interlace || interlace != gpu.state.old_interlace) {
718     gpu.state.old_interlace = interlace;
719
720     if (gpu.cmd_len > 0)
721       flush_cmd_buffer();
722     renderer_flush_queues();
723     renderer_set_interlace(interlace, !lcf);
724   }
725 }
726
727 #include "../../frontend/plugin_lib.h"
728
729 void GPUrearmedCallbacks(const struct rearmed_cbs *cbs)
730 {
731   gpu.frameskip.set = cbs->frameskip;
732   gpu.frameskip.advice = &cbs->fskip_advice;
733   gpu.frameskip.active = 0;
734   gpu.frameskip.frame_ready = 1;
735   gpu.state.hcnt = cbs->gpu_hcnt;
736   gpu.state.frame_count = cbs->gpu_frame_count;
737   gpu.state.allow_interlace = cbs->gpu_neon.allow_interlace;
738   gpu.state.enhancement_enable = cbs->gpu_neon.enhancement_enable;
739
740   gpu.mmap = cbs->mmap;
741   gpu.munmap = cbs->munmap;
742
743   // delayed vram mmap
744   if (gpu.vram == NULL)
745     map_vram();
746
747   if (cbs->pl_vout_set_raw_vram)
748     cbs->pl_vout_set_raw_vram(gpu.vram);
749   renderer_set_config(cbs);
750   vout_set_config(cbs);
751 }
752
753 // vim:shiftwidth=2:expandtab