int r0, g0, b0, r1, g1, b1;
int y0, y1, u, v;
- for (; pixels > 0; src += 2, dst++, pixels -= 2)
+ for (; pixels > 1; src += 2, dst++, pixels -= 2)
{
b0 = (src[0] >> 10) & 0x1f;
g0 = (src[0] >> 5) & 0x1f;
static void *vout_buf;
static void *vout_buf_ptr;
static int vout_width, vout_height;
-static int vout_doffs_old, vout_fb_dirty;
+static int vout_fb_dirty;
static bool vout_can_dupe;
static bool duping_enable;
static bool found_bios;
}
#endif
-static void vout_flip(const void *vram, int stride, int bgr24, int w, int h)
+static void vout_flip(const void *vram, int stride, int bgr24,
+ int x, int y, int w, int h, int dims_changed)
{
unsigned short *dest = vout_buf_ptr;
const unsigned short *src = vram;
int dstride = vout_width, h1 = h;
- int doffs;
- if (vram == NULL)
+ if (vram == NULL || dims_changed)
{
+ memset(vout_buf_ptr, 0, dstride * vout_height * 2);
// blanking
- memset(vout_buf_ptr, 0, dstride * h * 2);
- goto out;
+ if (vram == NULL)
+ goto out;
}
- doffs = (vout_height - h) * dstride;
- doffs += (dstride - w) / 2 & ~1;
- if (doffs != vout_doffs_old)
- {
- // clear borders
- memset(vout_buf_ptr, 0, dstride * h * 2);
- vout_doffs_old = doffs;
- }
- dest += doffs;
+ dest += x + y * dstride;
if (bgr24)
{
if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value)
{
if (strcmp(var.value, "disabled") == 0)
- Config.GpuListWalking = 0;
+ Config.GpuListWalking = 0;
else if (strcmp(var.value, "enabled") == 0)
- Config.GpuListWalking = 1;
+ Config.GpuListWalking = 1;
else // auto
- Config.GpuListWalking = -1;
+ Config.GpuListWalking = -1;
+ }
+
+ var.value = NULL;
+ var.key = "pcsx_rearmed_screen_centering";
+ if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value)
+ {
+ if (strcmp(var.value, "game") == 0)
+ pl_rearmed_cbs.screen_centering_type = 1;
+ else if (strcmp(var.value, "manual") == 0)
+ pl_rearmed_cbs.screen_centering_type = 2;
+ else // auto
+ pl_rearmed_cbs.screen_centering_type = 0;
+ }
+
+ var.value = NULL;
+ var.key = "pcsx_rearmed_screen_centering_x";
+ if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value)
+ {
+ pl_rearmed_cbs.screen_centering_x = atoi(var.value);
+ }
+
+ var.value = NULL;
+ var.key = "pcsx_rearmed_screen_centering_y";
+ if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value)
+ {
+ pl_rearmed_cbs.screen_centering_y = atoi(var.value);
}
#ifdef THREAD_RENDERING
},
"auto",
},
+ {
+ "pcsx_rearmed_screen_centering",
+ "(GPU) Screen centering",
+ NULL,
+ "The PSX has a feature allowing it to shift the image position on screen. Some (mostly PAL) games used this feature in a strange way making the image miscentered and causing borders to appear. With 'Auto' the emulator tries to correct this miscentering automatically. 'Game-controlled' uses the settings supplied by the game. 'Manual' allows to override those values with the settings below.",
+ NULL,
+ "video",
+ {
+ { "auto", "Auto" },
+ { "game", "Game-controlled" },
+ { "manual", "Manual" },
+ { NULL, NULL },
+ },
+ "auto",
+ },
+#define V(x) { #x, NULL }
+ {
+ "pcsx_rearmed_screen_centering_x",
+ "(GPU) Manual screen centering X",
+ NULL,
+ "X offset of the frame buffer. Only effective when 'Screen centering' is set to 'Manual'.",
+ NULL,
+ "video",
+ {
+ V(-16), V(-14), V(-12), V(-10), V(-8), V(-6), V(-4), V(-2), V(0), V(2), V(4), V(6), V(8), V(10), V(12), V(14), V(16),
+ { NULL, NULL },
+ },
+ "0",
+ },
+ {
+ "pcsx_rearmed_screen_centering_y",
+ "(GPU) Manual screen centering Y",
+ NULL,
+ "Y offset of the frame buffer. Only effective when 'Screen centering' is set to 'Manual'.",
+ NULL,
+ "video",
+ {
+ V(-16), V(-15), V(-14), V(-13), V(-12), V(-11), V(-10), V(-9), V(-8), V(-7), V(-6), V(-5), V(-4), V(-3), V(-2), V(-1),
+ V(0), V(1), V(2), V(3), V(4), V(5), V(6), V(7), V(8), V(9), V(10), V(11), V(12), V(13), V(14), V(15), V(16),
+ { NULL, NULL },
+ },
+ "0",
+ },
+#undef V
#ifdef GPU_NEON
{
"pcsx_rearmed_neon_interlace_enable_v2",
MA_OPT_VOUT_MODE,
MA_OPT_SCANLINES,
MA_OPT_SCANLINE_LEVEL,
+ MA_OPT_CENTERING,
} menu_id;
static int last_vout_w, last_vout_h, last_vout_bpp;
CE_INTVAL_P(gpu_peopsgl.iVRamSize),
CE_INTVAL_P(gpu_peopsgl.iTexGarbageCollection),
CE_INTVAL_P(gpu_peopsgl.dwActFixes),
+ CE_INTVAL_P(screen_centering_type),
+ CE_INTVAL_P(screen_centering_x),
+ CE_INTVAL_P(screen_centering_y),
CE_INTVAL(spu_config.iUseReverb),
CE_INTVAL(spu_config.iXAPitch),
CE_INTVAL(spu_config.iUseInterpolation),
#endif
NULL };
static const char *men_dummy[] = { NULL };
+static const char *men_centering[] = { "Auto", "Ingame", "Force", NULL };
static const char h_scaler[] = "int. 2x - scales w. or h. 2x if it fits on screen\n"
"int. 4:3 - uses integer if possible, else fractional";
static const char h_cscaler[] = "Displays the scaler layer, you can resize it\n"
static menu_entry e_menu_gfx_options[] =
{
+ mee_enum ("Screen centering", MA_OPT_CENTERING, pl_rearmed_cbs.screen_centering_type, men_centering),
mee_enum_h ("Scaler", MA_OPT_VARSCALER, g_scaler, men_scaler, h_scaler),
mee_enum ("Video output mode", MA_OPT_VOUT_MODE, plat_target.vout_method, men_dummy),
mee_onoff ("Software Scaling", MA_OPT_SCALER2, soft_scaling, 1),
i = plat_target.cpu_clock_set != NULL
&& plat_target.cpu_clock_get != NULL && cpu_clock_st > 0;
- me_enable(e_menu_gfx_options, MA_OPT_CPU_CLOCKS, i);
+ me_enable(e_menu_options, MA_OPT_CPU_CLOCKS, i);
i = me_id2offset(e_menu_gfx_options, MA_OPT_VOUT_MODE);
e_menu_gfx_options[i].data = plat_target.vout_methods;
#include <fcntl.h>
#include <unistd.h>
#include <pthread.h>
+#include <assert.h>
#include "libpicofe/fonts.h"
#include "libpicofe/input.h"
pl_rearmed_cbs.vsps_cur);
}
-static void print_cpu_usage(int w, int h, int border)
+static void print_cpu_usage(int x, int h)
{
- hud_printf(pl_vout_buf, pl_vout_w, pl_vout_w - border - 28,
+ hud_printf(pl_vout_buf, pl_vout_w, x - 28,
h - HUD_HEIGHT, "%3d", pl_rearmed_cbs.cpu_usage);
}
}
}
-static void print_hud(int w, int h, int xborder)
+static void print_hud(int x, int w, int h)
{
- if (h < 16)
+ if (h < 192)
return;
- if (w < pl_vout_w)
- xborder += (pl_vout_w - w) / 2;
if (h > pl_vout_h)
h = pl_vout_h;
draw_active_chans(w, h);
if (hud_msg[0] != 0)
- print_msg(h, xborder);
+ print_msg(h, x);
else if (g_opts & OPT_SHOWFPS)
- print_fps(h, xborder);
+ print_fps(h, x);
if (g_opts & OPT_SHOWCPU)
- print_cpu_usage(w, h, xborder);
+ print_cpu_usage(x + w, h);
}
/* update scaler target size according to user settings */
if (pl_rearmed_cbs.only_16bpp)
vout_bpp = 16;
- // don't use very low heights
- if (vout_h < 192) {
- buf_yoffset = (192 - vout_h) / 2;
- vout_h = 192;
- }
+ assert(vout_h >= 192);
pl_vout_scale_w = pl_vout_scale_h = 1;
#ifdef __ARM_NEON__
menu_notify_mode_change(pl_vout_w, pl_vout_h, pl_vout_bpp);
}
-static void pl_vout_flip(const void *vram, int stride, int bgr24, int w, int h)
+static void pl_vout_flip(const void *vram, int stride, int bgr24,
+ int x, int y, int w, int h, int dims_changed)
{
- static int doffs_old, clear_counter;
+ static int clear_counter;
unsigned char *dest = pl_vout_buf;
const unsigned short *src = vram;
int dstride = pl_vout_w, h1 = h;
int h_full = pl_vout_h - pl_vout_yoffset;
- int doffs;
+ int xoffs = 0, doffs;
pcnt_start(PCNT_BLIT);
goto out_hud;
}
- // borders
- doffs = (dstride - w * pl_vout_scale_w) / 2 & ~1;
+ assert(x + w <= pl_vout_w);
+ assert(y + h <= pl_vout_h);
+
+ // offset
+ xoffs = x * pl_vout_scale_w;
+ doffs = xoffs + y * dstride;
- if (doffs > doffs_old)
+ if (dims_changed)
clear_counter = 2;
- doffs_old = doffs;
if (clear_counter > 0) {
if (pl_plat_clear)
}
out_hud:
- print_hud(w * pl_vout_scale_w, h * pl_vout_scale_h, 0);
+ print_hud(xoffs, w * pl_vout_scale_w, (y + h) * pl_vout_scale_h);
out:
pcnt_end(PCNT_BLIT);
int (*pl_vout_open)(void);
void (*pl_vout_set_mode)(int w, int h, int raw_w, int raw_h, int bpp);
void (*pl_vout_flip)(const void *vram, int stride, int bgr24,
- int w, int h);
+ int x, int y, int w, int h, int dims_changed);
void (*pl_vout_close)(void);
void *(*mmap)(unsigned int size);
void (*munmap)(void *ptr, unsigned int size);
} gpu_peopsgl;
// misc
int gpu_caps;
+ int screen_centering_type; // 0 - auto, 1 - game conrolled, 2 - manual
+ int screen_centering_x;
+ int screen_centering_y;
};
extern struct rearmed_cbs pl_rearmed_cbs;
#define ENHANCEMENT_BUF_SIZE (1024 * 1024 * 2 * 4 + 4096 * 2)
-static uint16_t *get_enhancement_bufer(int *x, int *y, int *w, int *h,
+static void *get_enhancement_bufer(int *x, int *y, int *w, int *h,
int *vram_h)
{
uint16_t *ret = select_enhancement_buf_ptr(&egpu, *x);
*/
#include <stdio.h>
+#include <stdlib.h>
#include <string.h>
#include "gpu.h"
gpu.regs[3] = 1;
gpu.screen.hres = gpu.screen.w = 256;
gpu.screen.vres = gpu.screen.h = 240;
+ gpu.screen.x = gpu.screen.y = 0;
}
static noinline void update_width(void)
{
+ static const short hres_all[8] = { 256, 368, 320, 368, 512, 368, 640, 368 };
+ static const uint8_t hdivs[8] = { 10, 7, 8, 7, 5, 7, 4, 7 };
+ uint8_t hdiv = hdivs[(gpu.status >> 16) & 7];
+ int hres = hres_all[(gpu.status >> 16) & 7];
+ int pal = gpu.status & PSX_GPU_STATUS_PAL;
int sw = gpu.screen.x2 - gpu.screen.x1;
- if (sw <= 0 || sw >= 2560)
- // full width
- gpu.screen.w = gpu.screen.hres;
- else
- gpu.screen.w = sw * gpu.screen.hres / 2560;
+ int x = 0, x_auto;
+ if (sw <= 0)
+ /* nothing displayed? */;
+ else {
+ int s = pal ? 656 : 608; // or 600? pal is just a guess
+ x = (gpu.screen.x1 - s) / hdiv;
+ x = (x + 1) & ~1; // blitter limitation
+ sw /= hdiv;
+ sw = (sw + 2) & ~3; // according to nocash
+ switch (gpu.state.screen_centering_type) {
+ case 1:
+ break;
+ case 2:
+ x = gpu.state.screen_centering_x;
+ break;
+ default:
+ // correct if slightly miscentered
+ x_auto = (hres - sw) / 2 & ~3;
+ if ((uint32_t)x_auto <= 8u && abs(x) < 24)
+ x = x_auto;
+ }
+ if (x + sw > hres)
+ sw = hres - x;
+ // .x range check is done in vout_update()
+ }
+ // reduce the unpleasant right border that a few games have
+ if (gpu.state.screen_centering_type == 0
+ && x <= 4 && hres - (x + sw) >= 4)
+ hres -= 4;
+ gpu.screen.x = x;
+ gpu.screen.w = sw;
+ gpu.screen.hres = hres;
+ gpu.state.dims_changed = 1;
+ //printf("xx %d %d -> %2d, %d / %d\n",
+ // gpu.screen.x1, gpu.screen.x2, x, sw, hres);
}
static noinline void update_height(void)
{
- // TODO: emulate this properly..
+ int pal = gpu.status & PSX_GPU_STATUS_PAL;
+ int dheight = gpu.status & PSX_GPU_STATUS_DHEIGHT;
+ int y = gpu.screen.y1 - (pal ? 39 : 16); // 39 for spyro
int sh = gpu.screen.y2 - gpu.screen.y1;
- if (gpu.status & PSX_GPU_STATUS_DHEIGHT)
- sh *= 2;
- if (sh <= 0 || sh > gpu.screen.vres)
- sh = gpu.screen.vres;
-
+ int center_tol = 16;
+ int vres = 240;
+
+ if (pal && (sh > 240 || gpu.screen.vres == 256))
+ vres = 256;
+ if (dheight)
+ y *= 2, sh *= 2, vres *= 2, center_tol *= 2;
+ if (sh <= 0)
+ /* nothing displayed? */;
+ else {
+ switch (gpu.state.screen_centering_type) {
+ case 1:
+ break;
+ case 2:
+ y = gpu.state.screen_centering_y;
+ break;
+ default:
+ // correct if slightly miscentered
+ if ((uint32_t)(vres - sh) <= 1 && abs(y) <= center_tol)
+ y = 0;
+ }
+ if (y + sh > vres)
+ sh = vres - y;
+ }
+ gpu.screen.y = y;
gpu.screen.h = sh;
+ gpu.screen.vres = vres;
+ gpu.state.dims_changed = 1;
+ //printf("yy %d %d -> %d, %d / %d\n",
+ // gpu.screen.y1, gpu.screen.y2, y, sh, vres);
}
static noinline void decide_frameskip(void)
uint32_t x = cmd_e3 & 0x3ff;
uint32_t y = (cmd_e3 >> 10) & 0x3ff;
gpu.frameskip.allow = (gpu.status & PSX_GPU_STATUS_INTERLACE) ||
- (uint32_t)(x - gpu.screen.x) >= (uint32_t)gpu.screen.w ||
- (uint32_t)(y - gpu.screen.y) >= (uint32_t)gpu.screen.h;
+ (uint32_t)(x - gpu.screen.src_x) >= (uint32_t)gpu.screen.w ||
+ (uint32_t)(y - gpu.screen.src_y) >= (uint32_t)gpu.screen.h;
return gpu.frameskip.allow;
}
void GPUwriteStatus(uint32_t data)
{
- static const short hres[8] = { 256, 368, 320, 384, 512, 512, 640, 640 };
- static const short vres[4] = { 240, 480, 256, 480 };
uint32_t cmd = data >> 24;
if (cmd < ARRAY_SIZE(gpu.regs)) {
do_cmd_reset();
break;
case 0x03:
- if (data & 1)
+ if (data & 1) {
gpu.status |= PSX_GPU_STATUS_BLANKING;
+ gpu.state.dims_changed = 1; // for hud clearing
+ }
else
gpu.status &= ~PSX_GPU_STATUS_BLANKING;
break;
gpu.status |= PSX_GPU_STATUS_DMA(data & 3);
break;
case 0x05:
- gpu.screen.x = data & 0x3ff;
- gpu.screen.y = (data >> 10) & 0x1ff;
+ gpu.screen.src_x = data & 0x3ff;
+ gpu.screen.src_y = (data >> 10) & 0x1ff;
if (gpu.frameskip.set) {
decide_frameskip_allow(gpu.ex_regs[3]);
if (gpu.frameskip.last_flip_frame != *gpu.state.frame_count) {
break;
case 0x08:
gpu.status = (gpu.status & ~0x7f0000) | ((data & 0x3F) << 17) | ((data & 0x40) << 10);
- gpu.screen.hres = hres[(gpu.status >> 16) & 7];
- gpu.screen.vres = vres[(gpu.status >> 19) & 3];
update_width();
update_height();
renderer_notify_res_change();
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;
+ 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) {
+ 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;
+ update_width();
+ update_height();
+ }
gpu.mmap = cbs->mmap;
gpu.munmap = cbs->munmap;
#define BIT(x) (1 << (x))
#define PSX_GPU_STATUS_DHEIGHT BIT(19)
+#define PSX_GPU_STATUS_PAL BIT(20)
#define PSX_GPU_STATUS_RGB24 BIT(21)
#define PSX_GPU_STATUS_INTERLACE BIT(22)
#define PSX_GPU_STATUS_BLANKING BIT(23)
int x, y, w, h;
int x1, x2;
int y1, y2;
+ int src_x, src_y;
} screen;
struct {
int x, y, w, h;
uint32_t blanked:1;
uint32_t enhancement_enable:1;
uint32_t enhancement_active:1;
+ uint32_t dims_changed:1;
uint32_t *frame_count;
uint32_t *hcnt; /* hsync count */
struct {
} last_list;
uint32_t last_vram_read_frame;
uint32_t w_out_old, h_out_old, status_vo_old;
+ int screen_centering_type; // 0 - auto, 1 - game conrolled, 2 - manual
+ int screen_centering_x;
+ int screen_centering_y;
} state;
struct {
int32_t set:3; /* -1 auto, 0 off, 1-3 fixed */
uint32_t last_flip_frame;
uint32_t pending_fill[3];
} frameskip;
- uint16_t *(*get_enhancement_bufer)
+ void *(*get_enhancement_bufer)
(int *x, int *y, int *w, int *h, int *vram_h);
void *(*mmap)(unsigned int size);
void (*munmap)(void *ptr, unsigned int size);
static void check_mode_change(int force)
{
int w = gpu.screen.hres;
- int h = gpu.screen.h;
+ int h = gpu.screen.vres;
int w_out = w;
int h_out = h;
void vout_update(void)
{
+ int bpp = (gpu.status & PSX_GPU_STATUS_RGB24) ? 24 : 16;
+ uint8_t *vram = (uint8_t *)gpu.vram;
+ int src_x = gpu.screen.src_x;
+ int src_y = gpu.screen.src_y;
int x = gpu.screen.x;
int y = gpu.screen.y;
int w = gpu.screen.w;
int h = gpu.screen.h;
- uint16_t *vram = gpu.vram;
int vram_h = 512;
+ int src_x2 = 0;
+
+ if (x < 0) { w += x; src_x2 = -x; x = 0; }
+ if (y < 0) { h += y; src_y -= y; y = 0; }
- if (w == 0 || h == 0)
+ if (w <= 0 || h <= 0)
return;
check_mode_change(0);
- if (gpu.state.enhancement_active)
- vram = gpu.get_enhancement_bufer(&x, &y, &w, &h, &vram_h);
+ if (gpu.state.enhancement_active) {
+ vram = gpu.get_enhancement_bufer(&src_x, &src_y, &w, &h, &vram_h);
+ x *= 2; y *= 2;
+ }
- if (y + h > vram_h) {
- if (y + h - vram_h > h / 2) {
+ if (src_y + h > vram_h) {
+ if (src_y + h - vram_h > h / 2) {
// wrap
- h -= vram_h - y;
- y = 0;
+ h -= vram_h - src_y;
+ src_y = 0;
}
else
// clip
- h = vram_h - y;
+ h = vram_h - src_y;
}
- vram += y * 1024 + x;
+ vram += (src_y * 1024 + src_x) * 2;
+ vram += src_x2 * bpp / 8;
- cbs->pl_vout_flip(vram, 1024, !!(gpu.status & PSX_GPU_STATUS_RGB24), w, h);
+ cbs->pl_vout_flip(vram, 1024, !!(gpu.status & PSX_GPU_STATUS_RGB24),
+ x, y, w, h, gpu.state.dims_changed);
+ gpu.state.dims_changed = 0;
}
void vout_blank(void)
{
int w = gpu.screen.hres;
- int h = gpu.screen.h;
+ int h = gpu.screen.vres;
check_mode_change(0);
if (gpu.state.enhancement_active) {
w *= 2;
h *= 2;
}
- cbs->pl_vout_flip(NULL, 1024, !!(gpu.status & PSX_GPU_STATUS_RGB24), w, h);
+ cbs->pl_vout_flip(NULL, 1024, !!(gpu.status & PSX_GPU_STATUS_RGB24), 0, 0, w, h, 0);
}
long GPUopen(void **unused)