frontend: update libpicofe, fix missed callbacks
[pcsx_rearmed.git] / plugins / gpulib / gpu.c
CommitLineData
1ab64c54 1/*
05740673 2 * (C) GraÅžvydas "notaz" Ignotas, 2011-2012
1ab64c54
GI
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
d30279e2 11#include <stdio.h>
308c6e67 12#include <stdlib.h>
1ab64c54 13#include <string.h>
a3c46b7f 14#include <stdlib.h> /* for calloc */
15
56f08d83 16#include "gpu.h"
90ac6fed 17#include "gpu_timing.h"
abf09485 18#include "../../libpcsxcore/gpu.h" // meh
44e76f8a 19#include "../../frontend/plugin_lib.h"
f060f4be 20#include "../../include/compiler_features.h"
1ab64c54 21
44e76f8a 22#ifndef ARRAY_SIZE
1ab64c54 23#define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0]))
44e76f8a 24#endif
1ab64c54 25
deb18d24 26//#define log_io gpu_log
56f08d83 27#define log_io(...)
56f08d83 28
9ee0fd5b 29struct psx_gpu gpu;
1ab64c54 30
0d515420 31static noinline int do_cmd_buffer(struct psx_gpu *gpu, uint32_t *data, int count,
8412166f 32 int *cycles_sum, int *cycles_last);
0d515420 33static noinline void finish_vram_transfer(struct psx_gpu *gpu, int is_read);
48f3d210 34
0d515420 35static noinline void do_cmd_reset(struct psx_gpu *gpu)
48f3d210 36{
90ac6fed 37 int dummy = 0;
79c4d434 38 renderer_sync();
0d515420 39 if (unlikely(gpu->cmd_len > 0))
40 do_cmd_buffer(gpu, gpu->cmd_buffer, gpu->cmd_len, &dummy, &dummy);
41 gpu->cmd_len = 0;
05740673 42
0d515420 43 if (unlikely(gpu->dma.h > 0))
44 finish_vram_transfer(gpu, gpu->dma_start.is_read);
45 gpu->dma.h = 0;
48f3d210 46}
47
0d515420 48static noinline void do_reset(struct psx_gpu *gpu)
1ab64c54 49{
7841712d 50 unsigned int i;
48f3d210 51
0d515420 52 do_cmd_reset(gpu);
53
54 memset(gpu->regs, 0, sizeof(gpu->regs));
55 for (i = 0; i < sizeof(gpu->ex_regs) / sizeof(gpu->ex_regs[0]); i++)
56 gpu->ex_regs[i] = (0xe0 + i) << 24;
57 gpu->status = 0x14802000;
58 gpu->gp0 = 0;
59 gpu->regs[3] = 1;
60 gpu->screen.hres = gpu->screen.w = 256;
61 gpu->screen.vres = gpu->screen.h = 240;
62 gpu->screen.x = gpu->screen.y = 0;
63 renderer_sync_ecmds(gpu->ex_regs);
0b4038f8 64 renderer_notify_res_change();
1ab64c54
GI
65}
66
0d515420 67static noinline void update_width(struct psx_gpu *gpu)
8dd855cd 68{
308c6e67 69 static const short hres_all[8] = { 256, 368, 320, 368, 512, 368, 640, 368 };
70 static const uint8_t hdivs[8] = { 10, 7, 8, 7, 5, 7, 4, 7 };
0d515420 71 uint8_t hdiv = hdivs[(gpu->status >> 16) & 7];
72 int hres = hres_all[(gpu->status >> 16) & 7];
73 int pal = gpu->status & PSX_GPU_STATUS_PAL;
74 int sw = gpu->screen.x2 - gpu->screen.x1;
75 int type = gpu->state.screen_centering_type;
308c6e67 76 int x = 0, x_auto;
9ed80467 77 if (type == C_AUTO)
0d515420 78 type = gpu->state.screen_centering_type_default;
308c6e67 79 if (sw <= 0)
80 /* nothing displayed? */;
81 else {
82 int s = pal ? 656 : 608; // or 600? pal is just a guess
0d515420 83 x = (gpu->screen.x1 - s) / hdiv;
308c6e67 84 x = (x + 1) & ~1; // blitter limitation
85 sw /= hdiv;
86 sw = (sw + 2) & ~3; // according to nocash
f9ffa42c 87
0d515420 88 if (gpu->state.show_overscan == 2) // widescreen hack
f9ffa42c 89 sw = (sw + 63) & ~63;
0d515420 90 if (gpu->state.show_overscan && sw >= hres)
f9ffa42c 91 x = 0, hres = sw;
9ed80467 92 switch (type) {
44e76f8a 93 case C_INGAME:
308c6e67 94 break;
44e76f8a 95 case C_MANUAL:
0d515420 96 x = gpu->state.screen_centering_x;
308c6e67 97 break;
98 default:
99 // correct if slightly miscentered
100 x_auto = (hres - sw) / 2 & ~3;
101 if ((uint32_t)x_auto <= 8u && abs(x) < 24)
102 x = x_auto;
103 }
104 if (x + sw > hres)
105 sw = hres - x;
106 // .x range check is done in vout_update()
107 }
108 // reduce the unpleasant right border that a few games have
0d515420 109 if (gpu->state.screen_centering_type == 0
308c6e67 110 && x <= 4 && hres - (x + sw) >= 4)
111 hres -= 4;
0d515420 112 gpu->screen.x = x;
113 gpu->screen.w = sw;
114 gpu->screen.hres = hres;
115 gpu->state.dims_changed = 1;
116 //printf("xx %d %d (%d) -> %2d, %d / %d\n", gpu->screen.x1,
117 // gpu->screen.x2, gpu->screen.x2 - gpu->screen.x1, x, sw, hres);
8dd855cd 118}
119
0d515420 120static noinline void update_height(struct psx_gpu *gpu)
8dd855cd 121{
0d515420 122 int pal = gpu->status & PSX_GPU_STATUS_PAL;
123 int dheight = gpu->status & PSX_GPU_STATUS_DHEIGHT;
124 int y = gpu->screen.y1 - (pal ? 39 : 16); // 39 for spyro
125 int sh = gpu->screen.y2 - gpu->screen.y1;
308c6e67 126 int center_tol = 16;
127 int vres = 240;
128
0d515420 129 if (pal && (sh > 240 || gpu->screen.vres == 256))
308c6e67 130 vres = 256;
131 if (dheight)
132 y *= 2, sh *= 2, vres *= 2, center_tol *= 2;
133 if (sh <= 0)
134 /* nothing displayed? */;
135 else {
0d515420 136 switch (gpu->state.screen_centering_type) {
44e76f8a 137 case C_INGAME:
138 break;
139 case C_BORDERLESS:
140 y = 0;
308c6e67 141 break;
44e76f8a 142 case C_MANUAL:
0d515420 143 y = gpu->state.screen_centering_y;
238696ae 144 vres += gpu->state.screen_centering_h_adj;
308c6e67 145 break;
146 default:
147 // correct if slightly miscentered
148 if ((uint32_t)(vres - sh) <= 1 && abs(y) <= center_tol)
149 y = 0;
150 }
151 if (y + sh > vres)
152 sh = vres - y;
153 }
0d515420 154 gpu->screen.y = y;
155 gpu->screen.h = sh;
156 gpu->screen.vres = vres;
157 gpu->state.dims_changed = 1;
308c6e67 158 //printf("yy %d %d -> %d, %d / %d\n",
0d515420 159 // gpu->screen.y1, gpu->screen.y2, y, sh, vres);
8dd855cd 160}
161
0d515420 162static noinline void decide_frameskip(struct psx_gpu *gpu)
fc84f618 163{
0d515420 164 *gpu->frameskip.dirty = 1;
79c4d434 165
0d515420 166 if (gpu->frameskip.active)
167 gpu->frameskip.cnt++;
9fe27e25 168 else {
0d515420 169 gpu->frameskip.cnt = 0;
170 gpu->frameskip.frame_ready = 1;
9fe27e25 171 }
fc84f618 172
0d515420 173 if (*gpu->frameskip.force)
174 gpu->frameskip.active = 1;
175 else if (!gpu->frameskip.active && *gpu->frameskip.advice)
176 gpu->frameskip.active = 1;
177 else if (gpu->frameskip.set > 0 && gpu->frameskip.cnt < gpu->frameskip.set)
178 gpu->frameskip.active = 1;
fc84f618 179 else
0d515420 180 gpu->frameskip.active = 0;
fbb4bfff 181
0d515420 182 if (!gpu->frameskip.active && gpu->frameskip.pending_fill[0] != 0) {
90ac6fed 183 int dummy = 0;
0d515420 184 do_cmd_list(gpu->frameskip.pending_fill, 3, &dummy, &dummy, &dummy);
185 gpu->frameskip.pending_fill[0] = 0;
fbb4bfff 186 }
fc84f618 187}
188
0d515420 189static noinline int decide_frameskip_allow(struct psx_gpu *gpu)
9fe27e25 190{
191 // no frameskip if it decides to draw to display area,
192 // but not for interlace since it'll most likely always do that
0d515420 193 uint32_t cmd_e3 = gpu->ex_regs[3];
9fe27e25 194 uint32_t x = cmd_e3 & 0x3ff;
195 uint32_t y = (cmd_e3 >> 10) & 0x3ff;
0d515420 196 gpu->frameskip.allow = (gpu->status & PSX_GPU_STATUS_INTERLACE) ||
197 (uint32_t)(x - gpu->screen.src_x) >= (uint32_t)gpu->screen.w ||
198 (uint32_t)(y - gpu->screen.src_y) >= (uint32_t)gpu->screen.h;
199 return gpu->frameskip.allow;
9fe27e25 200}
201
0d515420 202static void flush_cmd_buffer(struct psx_gpu *gpu);
5fe1a2b1 203
0d515420 204static noinline void get_gpu_info(struct psx_gpu *gpu, uint32_t data)
6e9bdaef 205{
0d515420 206 if (unlikely(gpu->cmd_len > 0))
207 flush_cmd_buffer(gpu);
6e9bdaef 208 switch (data & 0x0f) {
209 case 0x02:
210 case 0x03:
211 case 0x04:
0d515420 212 gpu->gp0 = gpu->ex_regs[data & 7] & 0xfffff;
6e9bdaef 213 break;
d04b8924 214 case 0x05:
0d515420 215 gpu->gp0 = gpu->ex_regs[5] & 0x3fffff;
6e9bdaef 216 break;
217 case 0x07:
0d515420 218 gpu->gp0 = 2;
6e9bdaef 219 break;
220 default:
0d515420 221 // gpu->gp0 unchanged
6e9bdaef 222 break;
223 }
224}
225
a3c46b7f 226#ifndef max
227#define max(a, b) (((a) > (b)) ? (a) : (b))
228#endif
229
230// Minimum 16-byte VRAM alignment needed by gpu_unai's pixel-skipping
231// renderer/downscaler it uses in high res modes:
232#ifdef GCW_ZERO
233 // On GCW platform (MIPS), align to 8192 bytes (1 TLB entry) to reduce # of
234 // fills. (Will change this value if it ever gets large page support)
235 #define VRAM_ALIGN 8192
236#else
2a3e2553 237 #define VRAM_ALIGN 64
a3c46b7f 238#endif
239
cdc1d78f 240// double, for overdraw/overscan guard + at least 1 page before
a3c46b7f 241#define VRAM_SIZE ((1024 * 512 * 2 * 2) + max(VRAM_ALIGN, 4096))
242
243// vram ptr received from mmap/malloc/alloc (will deallocate using this)
244static uint16_t *vram_ptr_orig = NULL;
9ee0fd5b 245
a3c46b7f 246#ifndef GPULIB_USE_MMAP
2a3e2553 247# if defined(__linux__) || defined(_3DS) || defined(HAVE_LIBNX) || defined(VITA)
a3c46b7f 248# define GPULIB_USE_MMAP 1
249# else
250# define GPULIB_USE_MMAP 0
251# endif
252#endif
9ee0fd5b 253static int map_vram(void)
254{
a3c46b7f 255#if GPULIB_USE_MMAP
256 gpu.vram = vram_ptr_orig = gpu.mmap(VRAM_SIZE);
257#else
258 gpu.vram = vram_ptr_orig = calloc(VRAM_SIZE, 1);
259#endif
260 if (gpu.vram != NULL && gpu.vram != (void *)(intptr_t)-1) {
261 // 4kb guard in front
262 gpu.vram += (4096 / 2);
263 // Align
264 gpu.vram = (uint16_t*)(((uintptr_t)gpu.vram + (VRAM_ALIGN-1)) & ~(VRAM_ALIGN-1));
9ee0fd5b 265 return 0;
266 }
267 else {
268 fprintf(stderr, "could not map vram, expect crashes\n");
7e9c3060 269 gpu.vram = NULL;
9ee0fd5b 270 return -1;
271 }
272}
273
6e9bdaef 274long GPUinit(void)
275{
9394ada5 276 int ret;
277 ret = vout_init();
278 ret |= renderer_init();
279
0b4038f8 280 memset(&gpu.state, 0, sizeof(gpu.state));
281 memset(&gpu.frameskip, 0, sizeof(gpu.frameskip));
282 gpu.zero = 0;
3ece2f0c 283 gpu.state.frame_count = &gpu.zero;
deb18d24 284 gpu.state.hcnt = &gpu.zero;
48f3d210 285 gpu.cmd_len = 0;
0d515420 286 do_reset(&gpu);
48f3d210 287
6e9bdaef 288 return ret;
289}
290
291long GPUshutdown(void)
292{
9ee0fd5b 293 long ret;
294
e929dec5 295 renderer_finish();
9ee0fd5b 296 ret = vout_finish();
a3c46b7f 297
298 if (vram_ptr_orig != NULL) {
299#if GPULIB_USE_MMAP
300 gpu.munmap(vram_ptr_orig, VRAM_SIZE);
301#else
302 free(vram_ptr_orig);
303#endif
9ee0fd5b 304 }
a3c46b7f 305 vram_ptr_orig = gpu.vram = NULL;
9ee0fd5b 306
307 return ret;
6e9bdaef 308}
309
1ab64c54
GI
310void GPUwriteStatus(uint32_t data)
311{
1ab64c54 312 uint32_t cmd = data >> 24;
9672b97e 313 uint32_t fb_dirty = 1;
2da2fc76 314 int src_x, src_y;
1ab64c54 315
fc84f618 316 if (cmd < ARRAY_SIZE(gpu.regs)) {
48f3d210 317 if (cmd > 1 && cmd != 5 && gpu.regs[cmd] == data)
fc84f618 318 return;
8dd855cd 319 gpu.regs[cmd] = data;
fc84f618 320 }
321
8dd855cd 322 switch (cmd) {
1ab64c54 323 case 0x00:
0d515420 324 do_reset(&gpu);
1ab64c54 325 break;
48f3d210 326 case 0x01:
0d515420 327 do_cmd_reset(&gpu);
9672b97e 328 fb_dirty = 0;
48f3d210 329 break;
1ab64c54 330 case 0x03:
308c6e67 331 if (data & 1) {
f23b103c 332 gpu.status |= PSX_GPU_STATUS_BLANKING;
308c6e67 333 gpu.state.dims_changed = 1; // for hud clearing
334 }
f23b103c
PC
335 else
336 gpu.status &= ~PSX_GPU_STATUS_BLANKING;
1ab64c54
GI
337 break;
338 case 0x04:
f23b103c
PC
339 gpu.status &= ~PSX_GPU_STATUS_DMA_MASK;
340 gpu.status |= PSX_GPU_STATUS_DMA(data & 3);
9672b97e 341 fb_dirty = 0;
1ab64c54
GI
342 break;
343 case 0x05:
2da2fc76 344 src_x = data & 0x3ff; src_y = (data >> 10) & 0x1ff;
345 if (src_x != gpu.screen.src_x || src_y != gpu.screen.src_y) {
346 gpu.screen.src_x = src_x;
347 gpu.screen.src_y = src_y;
348 renderer_notify_scanout_change(src_x, src_y);
349 if (gpu.frameskip.set) {
0d515420 350 decide_frameskip_allow(&gpu);
2da2fc76 351 if (gpu.frameskip.last_flip_frame != *gpu.state.frame_count) {
0d515420 352 decide_frameskip(&gpu);
2da2fc76 353 gpu.frameskip.last_flip_frame = *gpu.state.frame_count;
354 }
9fe27e25 355 }
fb4c6fba 356 }
1ab64c54 357 break;
8dd855cd 358 case 0x06:
359 gpu.screen.x1 = data & 0xfff;
360 gpu.screen.x2 = (data >> 12) & 0xfff;
0d515420 361 update_width(&gpu);
8dd855cd 362 break;
1ab64c54
GI
363 case 0x07:
364 gpu.screen.y1 = data & 0x3ff;
365 gpu.screen.y2 = (data >> 10) & 0x3ff;
0d515420 366 update_height(&gpu);
1ab64c54
GI
367 break;
368 case 0x08:
f23b103c 369 gpu.status = (gpu.status & ~0x7f0000) | ((data & 0x3F) << 17) | ((data & 0x40) << 10);
0d515420 370 update_width(&gpu);
371 update_height(&gpu);
e929dec5 372 renderer_notify_res_change();
1ab64c54 373 break;
deb18d24 374 default:
375 if ((cmd & 0xf0) == 0x10)
0d515420 376 get_gpu_info(&gpu, data);
9672b97e 377 fb_dirty = 0;
6e9bdaef 378 break;
1ab64c54 379 }
7890a708 380
9672b97e 381 gpu.state.fb_dirty |= fb_dirty;
382
7890a708 383#ifdef GPUwriteStatus_ext
384 GPUwriteStatus_ext(data);
385#endif
1ab64c54
GI
386}
387
56f08d83 388const unsigned char cmd_lengths[256] =
1ab64c54 389{
d30279e2
GI
390 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
391 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
392 3, 3, 3, 3, 6, 6, 6, 6, 4, 4, 4, 4, 8, 8, 8, 8, // 20
393 5, 5, 5, 5, 8, 8, 8, 8, 7, 7, 7, 7, 11, 11, 11, 11,
652c6b8b 394 2, 2, 2, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3, // 40
395 3, 3, 3, 3, 3, 3, 3, 3, 4, 4, 4, 4, 4, 4, 4, 4,
396 2, 2, 2, 2, 3, 3, 3, 3, 1, 1, 1, 1, 0, 0, 0, 0, // 60
d30279e2 397 1, 1, 1, 1, 2, 2, 2, 2, 1, 1, 1, 1, 2, 2, 2, 2,
72583812 398 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, // 80
399 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
400 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, // a0
401 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
402 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, // c0
403 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
d30279e2
GI
404 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // e0
405 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
406};
407
0d515420 408#define VRAM_MEM_XY(vram_, x, y) &vram_[(y) * 1024 + (x)]
d30279e2 409
31a12b07 410// this isn't very useful so should be rare
411static void cpy_mask(uint16_t *dst, const uint16_t *src, int l, uint32_t r6)
72583812 412{
413 int i;
31a12b07 414 if (r6 == 1) {
415 for (i = 0; i < l; i++)
416 dst[i] = src[i] | 0x8000;
417 }
418 else {
419 uint16_t msb = r6 << 15;
420 for (i = 0; i < l; i++) {
421 uint16_t mask = (int16_t)dst[i] >> 15;
422 dst[i] = (dst[i] & mask) | ((src[i] | msb) & ~mask);
423 }
424 }
72583812 425}
426
0d515420 427static inline void do_vram_line(uint16_t *vram_, int x, int y,
428 uint16_t *mem, int l, int is_read, uint32_t r6)
1ab64c54 429{
0d515420 430 uint16_t *vram = VRAM_MEM_XY(vram_, x, y);
72583812 431 if (unlikely(is_read))
d30279e2 432 memcpy(mem, vram, l * 2);
31a12b07 433 else if (unlikely(r6))
434 cpy_mask(vram, mem, l, r6);
d30279e2
GI
435 else
436 memcpy(vram, mem, l * 2);
437}
438
0d515420 439static int do_vram_io(struct psx_gpu *gpu, uint32_t *data, int count, int is_read)
d30279e2
GI
440{
441 int count_initial = count;
0d515420 442 uint32_t r6 = gpu->ex_regs[6] & 3;
d30279e2 443 uint16_t *sdata = (uint16_t *)data;
0d515420 444 uint16_t *vram = gpu->vram;
445 int x = gpu->dma.x, y = gpu->dma.y;
446 int w = gpu->dma.w, h = gpu->dma.h;
447 int o = gpu->dma.offset;
d30279e2
GI
448 int l;
449 count *= 2; // operate in 16bpp pixels
450
79c4d434 451 renderer_sync();
452
0d515420 453 if (gpu->dma.offset) {
454 l = w - gpu->dma.offset;
ddd56f6e 455 if (count < l)
d30279e2 456 l = count;
ddd56f6e 457
0d515420 458 do_vram_line(vram, x + o, y, sdata, l, is_read, r6);
ddd56f6e 459
460 if (o + l < w)
461 o += l;
462 else {
463 o = 0;
464 y++;
465 h--;
466 }
d30279e2
GI
467 sdata += l;
468 count -= l;
d30279e2
GI
469 }
470
471 for (; h > 0 && count >= w; sdata += w, count -= w, y++, h--) {
472 y &= 511;
0d515420 473 do_vram_line(vram, x, y, sdata, w, is_read, r6);
d30279e2
GI
474 }
475
05740673 476 if (h > 0) {
477 if (count > 0) {
478 y &= 511;
0d515420 479 do_vram_line(vram, x, y, sdata, count, is_read, r6);
05740673 480 o = count;
481 count = 0;
482 }
d30279e2 483 }
05740673 484 else
0d515420 485 finish_vram_transfer(gpu, is_read);
486 gpu->dma.y = y;
487 gpu->dma.h = h;
488 gpu->dma.offset = o;
d30279e2 489
6e9bdaef 490 return count_initial - count / 2;
d30279e2
GI
491}
492
0d515420 493static noinline void start_vram_transfer(struct psx_gpu *gpu, uint32_t pos_word,
494 uint32_t size_word, int is_read)
d30279e2 495{
0d515420 496 if (gpu->dma.h)
a1d82827 497 log_anomaly(gpu, "start_vram_transfer while old unfinished\n");
ddd56f6e 498
0d515420 499 gpu->dma.x = pos_word & 0x3ff;
500 gpu->dma.y = (pos_word >> 16) & 0x1ff;
501 gpu->dma.w = ((size_word - 1) & 0x3ff) + 1;
502 gpu->dma.h = (((size_word >> 16) - 1) & 0x1ff) + 1;
503 gpu->dma.offset = 0;
504 gpu->dma.is_read = is_read;
505 gpu->dma_start = gpu->dma;
d30279e2 506
9e146206 507 renderer_flush_queues();
508 if (is_read) {
4170121a 509 const uint16_t *mem = VRAM_MEM_XY(gpu->vram, gpu->dma.x, gpu->dma.y);
0d515420 510 gpu->status |= PSX_GPU_STATUS_IMG;
9e146206 511 // XXX: wrong for width 1
4170121a 512 gpu->gp0 = LE16TOH(mem[0]) | ((uint32_t)LE16TOH(mem[1]) << 16);
0d515420 513 gpu->state.last_vram_read_frame = *gpu->state.frame_count;
9e146206 514 }
d30279e2 515
a1d82827 516 log_io(gpu, "start_vram_transfer %c (%d, %d) %dx%d\n", is_read ? 'r' : 'w',
0d515420 517 gpu->dma.x, gpu->dma.y, gpu->dma.w, gpu->dma.h);
518 if (gpu->gpu_state_change)
b244864a 519 gpu->gpu_state_change(PGS_VRAM_TRANSFER_START, 0);
d30279e2
GI
520}
521
0d515420 522static void finish_vram_transfer(struct psx_gpu *gpu, int is_read)
05740673 523{
524 if (is_read)
0d515420 525 gpu->status &= ~PSX_GPU_STATUS_IMG;
893f780e 526 else {
0d515420 527 int32_t screen_r = gpu->screen.src_x + gpu->screen.hres;
528 int32_t screen_b = gpu->screen.src_y + gpu->screen.vres;
529 int32_t dma_r = gpu->dma_start.x + gpu->dma_start.w;
530 int32_t dma_b = gpu->dma_start.y + gpu->dma_start.h;
9672b97e 531 int32_t not_dirty;
0d515420 532 not_dirty = screen_r - gpu->dma_start.x - 1;
533 not_dirty |= screen_b - gpu->dma_start.y - 1;
534 not_dirty |= dma_r - gpu->screen.src_x - 1;
535 not_dirty |= dma_b - gpu->screen.src_y - 1;
9672b97e 536 not_dirty >>= 31;
a1d82827 537 log_io(gpu, "dma %3d,%3d %dx%d scr %3d,%3d %3dx%3d -> dirty %d\n",
0d515420 538 gpu->dma_start.x, gpu->dma_start.y, gpu->dma_start.w, gpu->dma_start.h,
539 gpu->screen.src_x, gpu->screen.src_y, gpu->screen.hres, gpu->screen.vres, !not_dirty);
540 gpu->state.fb_dirty |= !not_dirty;
541 renderer_update_caches(gpu->dma_start.x, gpu->dma_start.y,
542 gpu->dma_start.w, gpu->dma_start.h, 0);
893f780e 543 }
0d515420 544 if (gpu->gpu_state_change)
b244864a 545 gpu->gpu_state_change(PGS_VRAM_TRANSFER_END, 0);
05740673 546}
547
0d515420 548static void do_vram_copy(struct psx_gpu *gpu, const uint32_t *params, int *cpu_cycles)
72583812 549{
550 const uint32_t sx = LE32TOH(params[0]) & 0x3FF;
551 const uint32_t sy = (LE32TOH(params[0]) >> 16) & 0x1FF;
552 const uint32_t dx = LE32TOH(params[1]) & 0x3FF;
553 const uint32_t dy = (LE32TOH(params[1]) >> 16) & 0x1FF;
554 uint32_t w = ((LE32TOH(params[2]) - 1) & 0x3FF) + 1;
555 uint32_t h = (((LE32TOH(params[2]) >> 16) - 1) & 0x1FF) + 1;
0d515420 556 uint16_t msb = gpu->ex_regs[6] << 15;
557 uint16_t *vram = gpu->vram;
72583812 558 uint16_t lbuf[128];
559 uint32_t x, y;
560
90ac6fed 561 *cpu_cycles += gput_copy(w, h);
72583812 562 if (sx == dx && sy == dy && msb == 0)
563 return;
564
565 renderer_flush_queues();
566
567 if (unlikely((sx < dx && dx < sx + w) || sx + w > 1024 || dx + w > 1024 || msb))
568 {
569 for (y = 0; y < h; y++)
570 {
0d515420 571 const uint16_t *src = VRAM_MEM_XY(vram, 0, (sy + y) & 0x1ff);
572 uint16_t *dst = VRAM_MEM_XY(vram, 0, (dy + y) & 0x1ff);
72583812 573 for (x = 0; x < w; x += ARRAY_SIZE(lbuf))
574 {
575 uint32_t x1, w1 = w - x;
576 if (w1 > ARRAY_SIZE(lbuf))
577 w1 = ARRAY_SIZE(lbuf);
578 for (x1 = 0; x1 < w1; x1++)
579 lbuf[x1] = src[(sx + x + x1) & 0x3ff];
580 for (x1 = 0; x1 < w1; x1++)
581 dst[(dx + x + x1) & 0x3ff] = lbuf[x1] | msb;
582 }
583 }
584 }
585 else
586 {
587 uint32_t sy1 = sy, dy1 = dy;
0d515420 588 for (y = 0; y < h; y++, sy1++, dy1++) {
589 memcpy(VRAM_MEM_XY(vram, dx, dy1 & 0x1ff),
590 VRAM_MEM_XY(vram, sx, sy1 & 0x1ff), w * 2);
591 }
72583812 592 }
593
594 renderer_update_caches(dx, dy, w, h, 0);
595}
596
0d515420 597static noinline int do_cmd_list_skip(struct psx_gpu *gpu, uint32_t *data,
598 int count, int *last_cmd)
b243416b 599{
90ac6fed 600 int cmd = 0, pos = 0, len, dummy = 0, v;
b243416b 601 int skip = 1;
602
0d515420 603 gpu->frameskip.pending_fill[0] = 0;
fbb4bfff 604
b243416b 605 while (pos < count && skip) {
606 uint32_t *list = data + pos;
89df80c6 607 cmd = LE32TOH(list[0]) >> 24;
b243416b 608 len = 1 + cmd_lengths[cmd];
8295d332 609 if (pos + len > count) {
610 cmd = -1;
611 break; // incomplete cmd
612 }
b243416b 613
97e07db9 614 switch (cmd) {
615 case 0x02:
0d515420 616 if ((LE32TOH(list[2]) & 0x3ff) > gpu->screen.w || ((LE32TOH(list[2]) >> 16) & 0x1ff) > gpu->screen.h)
97e07db9 617 // clearing something large, don't skip
8412166f 618 do_cmd_list(list, 3, &dummy, &dummy, &dummy);
97e07db9 619 else
0d515420 620 memcpy(gpu->frameskip.pending_fill, list, 3 * 4);
97e07db9 621 break;
622 case 0x24 ... 0x27:
623 case 0x2c ... 0x2f:
624 case 0x34 ... 0x37:
625 case 0x3c ... 0x3f:
0d515420 626 gpu->ex_regs[1] &= ~0x1ff;
627 gpu->ex_regs[1] |= LE32TOH(list[4 + ((cmd >> 4) & 1)]) & 0x1ff;
97e07db9 628 break;
629 case 0x48 ... 0x4F:
630 for (v = 3; pos + v < count; v++)
631 {
89df80c6 632 if ((list[v] & HTOLE32(0xf000f000)) == HTOLE32(0x50005000))
97e07db9 633 break;
634 }
635 len += v - 3;
636 break;
637 case 0x58 ... 0x5F:
638 for (v = 4; pos + v < count; v += 2)
639 {
89df80c6 640 if ((list[v] & HTOLE32(0xf000f000)) == HTOLE32(0x50005000))
97e07db9 641 break;
642 }
643 len += v - 4;
644 break;
645 default:
0d515420 646 if ((cmd & 0xf8) == 0xe0) {
647 gpu->ex_regs[cmd & 7] = LE32TOH(list[0]);
648 if (cmd == 0xe3)
649 skip = decide_frameskip_allow(gpu);
650 }
97e07db9 651 break;
b243416b 652 }
72583812 653 if (0x80 <= cmd && cmd <= 0xdf)
b243416b 654 break; // image i/o
97e07db9 655
b243416b 656 pos += len;
657 }
658
0d515420 659 renderer_sync_ecmds(gpu->ex_regs);
b243416b 660 *last_cmd = cmd;
661 return pos;
662}
663
0d515420 664static noinline int do_cmd_buffer(struct psx_gpu *gpu, uint32_t *data, int count,
8412166f 665 int *cycles_sum, int *cycles_last)
d30279e2 666{
b243416b 667 int cmd, pos;
0d515420 668 uint32_t old_e3 = gpu->ex_regs[3];
fc84f618 669 int vram_dirty = 0;
d30279e2 670
d30279e2 671 // process buffer
b243416b 672 for (pos = 0; pos < count; )
d30279e2 673 {
0d515420 674 if (gpu->dma.h && !gpu->dma_start.is_read) { // XXX: need to verify
9672b97e 675 // vram_dirty = 1; // handled in finish_vram_transfer()
0d515420 676 pos += do_vram_io(gpu, data + pos, count - pos, 0);
ddd56f6e 677 if (pos == count)
678 break;
d30279e2
GI
679 }
680
89df80c6 681 cmd = LE32TOH(data[pos]) >> 24;
0d515420 682 switch (cmd & 0xe0) {
683 case 0xe0:
684 if (cmd < 0xe8) {
685 if (gpu->ex_regs[cmd & 7] == LE32TOH(data[pos])) {
686 pos++;
687 continue;
688 }
689 }
690 break;
691 case 0xc0:
692 case 0xa0:
7194a46a
DS
693 if (unlikely((pos+2) >= count)) {
694 // incomplete vram write/read cmd, can't consume yet
695 cmd = -1;
696 break;
697 }
698
d30279e2 699 // consume vram write/read cmd
0d515420 700 start_vram_transfer(gpu, LE32TOH(data[pos + 1]),
701 LE32TOH(data[pos + 2]), (cmd & 0xe0) == 0xc0);
b243416b 702 pos += 3;
703 continue;
0d515420 704 case 0x80:
72583812 705 if (unlikely((pos+3) >= count)) {
706 cmd = -1; // incomplete cmd, can't consume yet
707 break;
708 }
79c4d434 709 renderer_sync();
8412166f 710 *cycles_sum += *cycles_last;
711 *cycles_last = 0;
0d515420 712 do_vram_copy(gpu, data + pos + 1, cycles_last);
893f780e 713 vram_dirty = 1;
72583812 714 pos += 4;
715 continue;
0d515420 716 case 0x00:
717 if (cmd == 2)
718 break;
9672b97e 719 if (cmd == 0x1f)
a1d82827 720 log_anomaly(gpu, "irq1?\n");
1cec4719 721 pos++;
722 continue;
723 }
b243416b 724
1e07f71d 725 // 0xex cmds might affect frameskip.allow, so pass to do_cmd_list_skip
0d515420 726 if (gpu->frameskip.active &&
727 (gpu->frameskip.allow || ((LE32TOH(data[pos]) >> 24) & 0xf0) == 0xe0)) {
728 pos += do_cmd_list_skip(gpu, data + pos, count - pos, &cmd);
729 }
b243416b 730 else {
8412166f 731 pos += do_cmd_list(data + pos, count - pos, cycles_sum, cycles_last, &cmd);
b243416b 732 vram_dirty = 1;
733 }
734
735 if (cmd == -1)
736 // incomplete cmd
ddd56f6e 737 break;
d30279e2 738 }
ddd56f6e 739
0d515420 740 gpu->status &= ~0x1fff;
741 gpu->status |= gpu->ex_regs[1] & 0x7ff;
742 gpu->status |= (gpu->ex_regs[6] & 3) << 11;
a3a9f519 743
0d515420 744 gpu->state.fb_dirty |= vram_dirty;
fc84f618 745
0d515420 746 if (old_e3 != gpu->ex_regs[3])
747 decide_frameskip_allow(gpu);
b243416b 748
ddd56f6e 749 return count - pos;
d30279e2
GI
750}
751
0d515420 752static noinline void flush_cmd_buffer(struct psx_gpu *gpu)
d30279e2 753{
b244864a 754 int cycles_last = 0;
90ac6fed 755 int dummy = 0, left;
b244864a 756 left = do_cmd_buffer(gpu, gpu->cmd_buffer, gpu->cmd_len, &dummy, &cycles_last);
d30279e2 757 if (left > 0)
0d515420 758 memmove(gpu->cmd_buffer, gpu->cmd_buffer + gpu->cmd_len - left, left * 4);
759 if (left != gpu->cmd_len) {
0d515420 760 gpu->cmd_len = left;
b244864a 761 if (!gpu->dma.h && gpu->gpu_state_change)
762 gpu->gpu_state_change(PGS_PRIMITIVE_START, cycles_last);
abf09485 763 }
1ab64c54
GI
764}
765
766void GPUwriteDataMem(uint32_t *mem, int count)
767{
90ac6fed 768 int dummy = 0, left;
d30279e2 769
a1d82827 770 log_io(&gpu, "gpu_dma_write %p %d\n", mem, count);
56f08d83 771
d30279e2 772 if (unlikely(gpu.cmd_len > 0))
0d515420 773 flush_cmd_buffer(&gpu);
56f08d83 774
0d515420 775 left = do_cmd_buffer(&gpu, mem, count, &dummy, &dummy);
d30279e2 776 if (left)
a1d82827 777 log_anomaly(&gpu, "GPUwriteDataMem: discarded %d/%d words\n", left, count);
1ab64c54
GI
778}
779
d30279e2 780void GPUwriteData(uint32_t data)
1ab64c54 781{
a1d82827 782 log_io(&gpu, "gpu_write %08x\n", data);
89df80c6 783 gpu.cmd_buffer[gpu.cmd_len++] = HTOLE32(data);
d30279e2 784 if (gpu.cmd_len >= CMD_BUFFER_LEN)
0d515420 785 flush_cmd_buffer(&gpu);
1ab64c54
GI
786}
787
8412166f 788long GPUdmaChain(uint32_t *rambase, uint32_t start_addr,
789 uint32_t *progress_addr, int32_t *cycles_last_cmd)
1ab64c54 790{
24a7af90 791 uint32_t addr, *list, ld_addr;
792 int len, left, count, ld_count = 32;
8412166f 793 int cpu_cycles_sum = 0;
794 int cpu_cycles_last = 0;
d30279e2 795
8f5f2dd5 796 preload(rambase + (start_addr & 0x1fffff) / 4);
797
d30279e2 798 if (unlikely(gpu.cmd_len > 0))
0d515420 799 flush_cmd_buffer(&gpu);
d30279e2 800
a1d82827 801 log_io(&gpu, "gpu_dma_chain\n");
24a7af90 802 addr = ld_addr = start_addr & 0xffffff;
09159d99 803 for (count = 0; (addr & 0x800000) == 0; count++)
ddd56f6e 804 {
ddd56f6e 805 list = rambase + (addr & 0x1fffff) / 4;
89df80c6
PC
806 len = LE32TOH(list[0]) >> 24;
807 addr = LE32TOH(list[0]) & 0xffffff;
8f5f2dd5 808 preload(rambase + (addr & 0x1fffff) / 4);
809
cab87b7e 810 cpu_cycles_sum += 10;
1c72b1c2 811 if (len > 0)
8412166f 812 cpu_cycles_sum += 5 + len;
deb18d24 813
a1d82827 814 log_io(&gpu, ".chain %08lx #%d+%d %u+%u\n",
8412166f 815 (long)(list - rambase) * 4, len, gpu.cmd_len, cpu_cycles_sum, cpu_cycles_last);
ae36bb28 816 if (unlikely(gpu.cmd_len > 0)) {
23948df3 817 if (gpu.cmd_len + len > ARRAY_SIZE(gpu.cmd_buffer)) {
a1d82827 818 log_anomaly(&gpu, "cmd_buffer overflow, likely garbage commands\n");
23948df3 819 gpu.cmd_len = 0;
820 }
ae36bb28 821 memcpy(gpu.cmd_buffer + gpu.cmd_len, list + 1, len * 4);
822 gpu.cmd_len += len;
0d515420 823 flush_cmd_buffer(&gpu);
ae36bb28 824 continue;
825 }
ddd56f6e 826
56f08d83 827 if (len) {
0d515420 828 left = do_cmd_buffer(&gpu, list + 1, len, &cpu_cycles_sum, &cpu_cycles_last);
ae36bb28 829 if (left) {
830 memcpy(gpu.cmd_buffer, list + 1 + len - left, left * 4);
831 gpu.cmd_len = left;
a1d82827 832 log_anomaly(&gpu, "GPUdmaChain: %d/%d words left\n", left, len);
ae36bb28 833 }
56f08d83 834 }
ddd56f6e 835
4a15db3f 836 if (progress_addr) {
837 // hack for bios boot logo race (must be not too fast or too slow)
838 if (gpu.status & PSX_GPU_STATUS_DHEIGHT)
839 cpu_cycles_sum += 5;
840 if (cpu_cycles_sum > 512)
841 break;
842 }
24a7af90 843 if (addr == ld_addr) {
a1d82827 844 log_anomaly(&gpu, "GPUdmaChain: loop @ %08x, cnt=%u\n", addr, count);
24a7af90 845 break;
09159d99 846 }
24a7af90 847 if (count == ld_count) {
848 ld_addr = addr;
849 ld_count *= 2;
09159d99 850 }
d30279e2 851 }
09159d99 852
8412166f 853 //printf(" -> %d %d\n", cpu_cycles_sum, cpu_cycles_last);
3ece2f0c 854 gpu.state.last_list.frame = *gpu.state.frame_count;
deb18d24 855 gpu.state.last_list.hcnt = *gpu.state.hcnt;
8412166f 856 gpu.state.last_list.cycles = cpu_cycles_sum + cpu_cycles_last;
deb18d24 857 gpu.state.last_list.addr = start_addr;
858
78c8d214 859 if (progress_addr)
860 *progress_addr = addr;
8412166f 861 *cycles_last_cmd = cpu_cycles_last;
862 return cpu_cycles_sum;
1ab64c54
GI
863}
864
d30279e2
GI
865void GPUreadDataMem(uint32_t *mem, int count)
866{
a1d82827 867 log_io(&gpu, "gpu_dma_read %p %d\n", mem, count);
56f08d83 868
d30279e2 869 if (unlikely(gpu.cmd_len > 0))
0d515420 870 flush_cmd_buffer(&gpu);
56f08d83 871
d30279e2 872 if (gpu.dma.h)
0d515420 873 do_vram_io(&gpu, mem, count, 1);
d30279e2
GI
874}
875
876uint32_t GPUreadData(void)
877{
9e146206 878 uint32_t ret;
56f08d83 879
880 if (unlikely(gpu.cmd_len > 0))
0d515420 881 flush_cmd_buffer(&gpu);
56f08d83 882
9e146206 883 ret = gpu.gp0;
ae097dfb
PC
884 if (gpu.dma.h) {
885 ret = HTOLE32(ret);
0d515420 886 do_vram_io(&gpu, &ret, 1, 1);
ae097dfb
PC
887 ret = LE32TOH(ret);
888 }
56f08d83 889
a1d82827 890 log_io(&gpu, "gpu_read %08x\n", ret);
9e146206 891 return ret;
d30279e2
GI
892}
893
894uint32_t GPUreadStatus(void)
895{
ddd56f6e 896 uint32_t ret;
56f08d83 897
d30279e2 898 if (unlikely(gpu.cmd_len > 0))
0d515420 899 flush_cmd_buffer(&gpu);
d30279e2 900
f23b103c 901 ret = gpu.status;
a1d82827 902 log_io(&gpu, "gpu_read_status %08x\n", ret);
ddd56f6e 903 return ret;
d30279e2
GI
904}
905
597571ad 906long GPUfreeze(uint32_t type, GPUFreeze_t *freeze)
1ab64c54 907{
fc84f618 908 int i;
909
1ab64c54
GI
910 switch (type) {
911 case 1: // save
d30279e2 912 if (gpu.cmd_len > 0)
0d515420 913 flush_cmd_buffer(&gpu);
79c4d434 914
915 renderer_sync();
9ee0fd5b 916 memcpy(freeze->psxVRam, gpu.vram, 1024 * 512 * 2);
1ab64c54 917 memcpy(freeze->ulControl, gpu.regs, sizeof(gpu.regs));
6e9bdaef 918 memcpy(freeze->ulControl + 0xe0, gpu.ex_regs, sizeof(gpu.ex_regs));
f23b103c 919 freeze->ulStatus = gpu.status;
1ab64c54
GI
920 break;
921 case 0: // load
79c4d434 922 renderer_sync();
9ee0fd5b 923 memcpy(gpu.vram, freeze->psxVRam, 1024 * 512 * 2);
60e4e7d4 924 //memcpy(gpu.regs, freeze->ulControl, sizeof(gpu.regs));
6e9bdaef 925 memcpy(gpu.ex_regs, freeze->ulControl + 0xe0, sizeof(gpu.ex_regs));
f23b103c 926 gpu.status = freeze->ulStatus;
3d47ef17 927 gpu.cmd_len = 0;
60e4e7d4 928 for (i = 8; i > 1; i--)
929 GPUwriteStatus((i << 24) | freeze->ulControl[i]);
5b745e5b 930 renderer_sync_ecmds(gpu.ex_regs);
2da2fc76 931 renderer_update_caches(0, 0, 1024, 512, 0);
1ab64c54
GI
932 break;
933 }
934
935 return 1;
936}
937
5440b88e 938void GPUupdateLace(void)
939{
f665bfda 940 int updated = 0;
941
5440b88e 942 if (gpu.cmd_len > 0)
0d515420 943 flush_cmd_buffer(&gpu);
5440b88e 944 renderer_flush_queues();
945
cb245e56 946#ifndef RAW_FB_DISPLAY
f23b103c 947 if (gpu.status & PSX_GPU_STATUS_BLANKING) {
aafcb4dd 948 if (!gpu.state.blanked) {
949 vout_blank();
950 gpu.state.blanked = 1;
951 gpu.state.fb_dirty = 1;
952 }
953 return;
954 }
955
79c4d434 956 renderer_notify_update_lace(0);
957
aafcb4dd 958 if (!gpu.state.fb_dirty)
5440b88e 959 return;
cb245e56 960#endif
5440b88e 961
962 if (gpu.frameskip.set) {
963 if (!gpu.frameskip.frame_ready) {
964 if (*gpu.state.frame_count - gpu.frameskip.last_flip_frame < 9)
965 return;
966 gpu.frameskip.active = 0;
967 }
968 gpu.frameskip.frame_ready = 0;
969 }
970
f665bfda 971 updated = vout_update();
0b4038f8 972 if (gpu.state.enhancement_active && !gpu.state.enhancement_was_active)
973 renderer_update_caches(0, 0, 1024, 512, 1);
974 gpu.state.enhancement_was_active = gpu.state.enhancement_active;
f665bfda 975 if (updated) {
976 gpu.state.fb_dirty = 0;
977 gpu.state.blanked = 0;
978 }
79c4d434 979 renderer_notify_update_lace(1);
5440b88e 980}
981
72e5023f 982void GPUvBlank(int is_vblank, int lcf)
983{
5440b88e 984 int interlace = gpu.state.allow_interlace
f23b103c
PC
985 && (gpu.status & PSX_GPU_STATUS_INTERLACE)
986 && (gpu.status & PSX_GPU_STATUS_DHEIGHT);
5440b88e 987 // interlace doesn't look nice on progressive displays,
988 // so we have this "auto" mode here for games that don't read vram
989 if (gpu.state.allow_interlace == 2
990 && *gpu.state.frame_count - gpu.state.last_vram_read_frame > 1)
991 {
992 interlace = 0;
993 }
994 if (interlace || interlace != gpu.state.old_interlace) {
995 gpu.state.old_interlace = interlace;
996
997 if (gpu.cmd_len > 0)
0d515420 998 flush_cmd_buffer(&gpu);
5440b88e 999 renderer_flush_queues();
1000 renderer_set_interlace(interlace, !lcf);
1001 }
1002}
1003
ab88daca 1004void GPUgetScreenInfo(int *y, int *base_hres)
1005{
1006 *y = gpu.screen.y;
1007 *base_hres = gpu.screen.vres;
1008 if (gpu.status & PSX_GPU_STATUS_DHEIGHT)
1009 *base_hres >>= 1;
1010}
1011
5440b88e 1012void GPUrearmedCallbacks(const struct rearmed_cbs *cbs)
1013{
1014 gpu.frameskip.set = cbs->frameskip;
1015 gpu.frameskip.advice = &cbs->fskip_advice;
79c4d434 1016 gpu.frameskip.force = &cbs->fskip_force;
1017 gpu.frameskip.dirty = (void *)&cbs->fskip_dirty;
5440b88e 1018 gpu.frameskip.active = 0;
1019 gpu.frameskip.frame_ready = 1;
49f5a273
PC
1020 gpu.state.hcnt = (uint32_t *)cbs->gpu_hcnt;
1021 gpu.state.frame_count = (uint32_t *)cbs->gpu_frame_count;
5440b88e 1022 gpu.state.allow_interlace = cbs->gpu_neon.allow_interlace;
0b02eb77 1023 gpu.state.enhancement_enable = cbs->gpu_neon.enhancement_enable;
9ed80467 1024 gpu.state.screen_centering_type_default = cbs->screen_centering_type_default;
308c6e67 1025 if (gpu.state.screen_centering_type != cbs->screen_centering_type
1026 || gpu.state.screen_centering_x != cbs->screen_centering_x
f9ffa42c 1027 || gpu.state.screen_centering_y != cbs->screen_centering_y
238696ae 1028 || gpu.state.screen_centering_h_adj != cbs->screen_centering_h_adj
f9ffa42c 1029 || gpu.state.show_overscan != cbs->show_overscan) {
308c6e67 1030 gpu.state.screen_centering_type = cbs->screen_centering_type;
1031 gpu.state.screen_centering_x = cbs->screen_centering_x;
1032 gpu.state.screen_centering_y = cbs->screen_centering_y;
238696ae 1033 gpu.state.screen_centering_h_adj = cbs->screen_centering_h_adj;
f9ffa42c 1034 gpu.state.show_overscan = cbs->show_overscan;
0d515420 1035 update_width(&gpu);
1036 update_height(&gpu);
308c6e67 1037 }
5440b88e 1038
9ee0fd5b 1039 gpu.mmap = cbs->mmap;
1040 gpu.munmap = cbs->munmap;
abf09485 1041 gpu.gpu_state_change = cbs->gpu_state_change;
9ee0fd5b 1042
1043 // delayed vram mmap
1044 if (gpu.vram == NULL)
1045 map_vram();
1046
5440b88e 1047 if (cbs->pl_vout_set_raw_vram)
1048 cbs->pl_vout_set_raw_vram(gpu.vram);
1049 renderer_set_config(cbs);
1050 vout_set_config(cbs);
72e5023f 1051}
1052
1ab64c54 1053// vim:shiftwidth=2:expandtab