diff --git a/src/common/api.c b/src/common/api.c index 910c48a..5be0750 100644 --- a/src/common/api.c +++ b/src/common/api.c @@ -94,11 +94,6 @@ struct owlfb_sync_info { /////////////////////////////// -// is triple buffering necessary without threaded rendering? -#define GFX_BUFFER_COUNT 2 - -/////////////////////////////// - uint32_t RGB_WHITE; uint32_t RGB_BLACK; uint32_t RGB_LIGHT_GRAY; @@ -106,17 +101,16 @@ uint32_t RGB_GRAY; uint32_t RGB_DARK_GRAY; static struct GFX_Context { + int fb0_fd; + int page; + int resized; + int mode; int vsync; - int fb; - int pitch; - int buffer; - int buffer_size; - int map_size; - void* map; + struct fb_var_screeninfo vinfo; - struct fb_fix_screeninfo finfo; - + void* fb0_buffer; + SDL_Surface* screen; SDL_Surface* assets; } gfx; @@ -159,45 +153,36 @@ SDL_Surface* GFX_init(int mode) { SDL_ShowCursor(0); TTF_Init(); - gfx.vsync = VSYNC_STRICT; - gfx.mode = mode; + ////////////////////////////// - // we're drawing to the (triple-buffered) framebuffer directly - // but we still need to set video mode to initialize input events - SDL_SetVideoMode(SCREEN_WIDTH, SCREEN_HEIGHT, SCREEN_DEPTH, SDL_HWSURFACE); + SDL_SetVideoMode(FIXED_WIDTH, FIXED_HEIGHT, FIXED_DEPTH, SDL_SWSURFACE); - // open framebuffer - gfx.fb = open("/dev/fb0", O_RDWR); - - // configure framebuffer - ioctl(gfx.fb, FBIOGET_VSCREENINFO, &gfx.vinfo); - gfx.vinfo.bits_per_pixel = SCREEN_DEPTH; - gfx.vinfo.xres = SCREEN_WIDTH; - gfx.vinfo.yres = SCREEN_HEIGHT; - gfx.vinfo.xres_virtual = SCREEN_WIDTH; - gfx.vinfo.yres_virtual = SCREEN_HEIGHT * GFX_BUFFER_COUNT; + gfx.fb0_fd = open("/dev/fb0", O_RDWR); + + ioctl(gfx.fb0_fd, FBIOGET_VSCREENINFO, &gfx.vinfo); + gfx.vinfo.bits_per_pixel = FIXED_DEPTH; + gfx.vinfo.xres = FIXED_WIDTH; + gfx.vinfo.yres = FIXED_HEIGHT; + gfx.vinfo.xres_virtual = VIRTUAL_WIDTH; + gfx.vinfo.yres_virtual = VIRTUAL_HEIGHT; gfx.vinfo.xoffset = 0; gfx.vinfo.yoffset = 0; - // gfx.vinfo.activate = FB_ACTIVATE_VBL; - - ioctl(gfx.fb, FBIOPUT_VSCREENINFO, &gfx.vinfo); - - // get fixed screen info - ioctl(gfx.fb, FBIOGET_FSCREENINFO, &gfx.finfo); - gfx.map_size = gfx.finfo.smem_len; - gfx.map = mmap(0, gfx.map_size, PROT_READ | PROT_WRITE, MAP_SHARED, gfx.fb, 0); - memset(gfx.map, 0, gfx.map_size); + ioctl(gfx.fb0_fd, FBIOPUT_VSCREENINFO, &gfx.vinfo); struct owlfb_sync_info sinfo; sinfo.enabled = 1; - ioctl(gfx.fb, OWLFB_VSYNC_EVENT_EN, &sinfo); + ioctl(gfx.fb0_fd, OWLFB_VSYNC_EVENT_EN, &sinfo); - // buffer tracking - gfx.buffer = 1; // start on back buffer - gfx.buffer_size = SCREEN_PITCH * SCREEN_HEIGHT; - - // return screen - gfx.screen = SDL_CreateRGBSurfaceFrom(gfx.map + gfx.buffer_size * gfx.buffer, SCREEN_WIDTH,SCREEN_HEIGHT, SCREEN_DEPTH,SCREEN_PITCH, 0,0,0,0); + gfx.page = 1; // start on the backbuffer + gfx.fb0_buffer = mmap(0, VIRTUAL_SIZE, PROT_READ | PROT_WRITE, MAP_SHARED, gfx.fb0_fd, 0); + memset(gfx.fb0_buffer, 0, VIRTUAL_SIZE); // clear both buffers + + gfx.screen = SDL_CreateRGBSurfaceFrom(gfx.fb0_buffer + (gfx.page * PAGE_SIZE), FIXED_WIDTH,FIXED_HEIGHT, FIXED_DEPTH,FIXED_PITCH, 0,0,0,0); + + ////////////////////////////// + + gfx.vsync = VSYNC_STRICT; + gfx.mode = mode; RGB_WHITE = SDL_MapRGB(gfx.screen->format, TRIAD_WHITE); RGB_BLACK = SDL_MapRGB(gfx.screen->format, TRIAD_BLACK); @@ -230,9 +215,7 @@ SDL_Surface* GFX_init(int mode) { return gfx.screen; } -void GFX_setMode(int mode) { - gfx.mode = mode; -} + void GFX_quit(void) { TTF_CloseFont(font.large); TTF_CloseFont(font.medium); @@ -241,24 +224,27 @@ void GFX_quit(void) { SDL_FreeSurface(gfx.assets); - ioctl(gfx.fb, OWLFB_WAITFORVSYNC, &_); + ioctl(gfx.fb0_fd, OWLFB_WAITFORVSYNC, &_); GFX_clearAll(); - munmap(gfx.map, gfx.map_size); - close(gfx.fb); + munmap(gfx.fb0_buffer, VIRTUAL_SIZE); + close(gfx.fb0_fd); SDL_Quit(); } void GFX_clear(SDL_Surface* screen) { - memset(screen->pixels, 0, gfx.buffer_size); // this buffer is offscreen when cleared + memset(screen->pixels, 0, PAGE_SIZE); // this buffer is offscreen when cleared } void GFX_clearAll(void) { // TODO: one of the buffers is onscreen when cleared producing tearing // so clear our working buffer immediately (screen->pixels) // then set a flag and clear the other two after vsync? - memset(gfx.map, 0, gfx.map_size); + memset(gfx.fb0_buffer, 0, VIRTUAL_SIZE); } +void GFX_setMode(int mode) { + gfx.mode = mode; +} int GFX_getVsync(void) { return gfx.vsync; } @@ -271,26 +257,48 @@ static uint32_t frame_start = 0; void GFX_startFrame(void) { frame_start = SDL_GetTicks(); } +SDL_Surface* GFX_resize(int w, int h, int pitch) { + LOG_info("resize: %ix%i (%i)\n", w,h, pitch); + // callee should decide if resizing is actually necessary + + if (gfx.screen) SDL_FreeSurface(gfx.screen); + + gfx.screen = SDL_CreateRGBSurfaceFrom(gfx.fb0_buffer + (gfx.page * PAGE_SIZE), w,h, FIXED_DEPTH,pitch, 0,0,0,0); + memset(gfx.screen->pixels, 0, PAGE_SIZE); + + gfx.vinfo.xres = w; + gfx.vinfo.yres = h; + + // triggers FBIOPUT_VSCREENINFO instead + // of FBIOPAN_DISPLAY in GFX_flip() + gfx.resized = 1; + + return gfx.screen; +} void GFX_flip(SDL_Surface* screen) { - gfx.vinfo.yoffset = gfx.buffer * SCREEN_HEIGHT; - ioctl(gfx.fb, FBIOPAN_DISPLAY, &gfx.vinfo); - - gfx.buffer += 1; - if (gfx.buffer>=GFX_BUFFER_COUNT) gfx.buffer -= GFX_BUFFER_COUNT; - screen->pixels = gfx.map + (gfx.buffer * gfx.buffer_size); - + // point framebuffer at the first line of the backbuffer + gfx.vinfo.yoffset = gfx.page * PAGE_HEIGHT; + ioctl(gfx.fb0_fd, gfx.resized ? FBIOPUT_VSCREENINFO : FBIOPAN_DISPLAY, &gfx.vinfo); + gfx.resized = 0; + if (gfx.vsync!=VSYNC_OFF) { // this limiting condition helps SuperFX chip games if (gfx.vsync==VSYNC_STRICT || frame_start==0 || SDL_GetTicks()-frame_start=PAGE_COUNT) gfx.page -= PAGE_COUNT; + gfx.screen->pixels = gfx.fb0_buffer + (gfx.page * PAGE_SIZE); + } void GFX_sync(void) { if (gfx.vsync!=VSYNC_OFF) { // this limiting condition helps SuperFX chip games if (gfx.vsync==VSYNC_STRICT || frame_start==0 || SDL_GetTicks()-frame_startpixels, (gfx.map + gfx.buffer_size * buffer), (SCREEN_HEIGHT * SCREEN_PITCH)); + int buffer = gfx.page - 1; + if (buffer<0) buffer += PAGE_COUNT; + SDL_Surface* copy = SDL_CreateRGBSurface(SDL_SWSURFACE, gfx.screen->w,gfx.screen->h,FIXED_DEPTH,0,0,0,0); + SDL_BlitSurface(gfx.screen, NULL, copy, NULL); return copy; } int GFX_truncateText(TTF_Font* font, const char* in_name, char* out_name, int max_width) { diff --git a/src/common/api.h b/src/common/api.h index 37b5585..abc1a70 100644 --- a/src/common/api.h +++ b/src/common/api.h @@ -20,6 +20,27 @@ void LOG_note(int level, const char* fmt, ...); /////////////////////////////// +#define FIXED_WIDTH 640 +#define FIXED_HEIGHT 480 +#define FIXED_BPP 2 +#define FIXED_DEPTH FIXED_BPP * 8 +#define FIXED_PITCH FIXED_WIDTH * FIXED_BPP +#define FIXED_SIZE FIXED_HEIGHT * FIXED_PITCH + +#define PAGE_COUNT 2 +#define PAGE_SCALE 2 +#define PAGE_WIDTH FIXED_WIDTH * PAGE_SCALE +#define PAGE_HEIGHT FIXED_HEIGHT * PAGE_SCALE +#define PAGE_PITCH PAGE_WIDTH * FIXED_BPP +#define PAGE_SIZE PAGE_HEIGHT * PAGE_PITCH + +#define VIRTUAL_WIDTH PAGE_WIDTH +#define VIRTUAL_HEIGHT PAGE_HEIGHT * PAGE_COUNT +#define VIRTUAL_PITCH PAGE_WIDTH * FIXED_BPP +#define VIRTUAL_SIZE VIRTUAL_HEIGHT * VIRTUAL_PITCH + +/////////////////////////////// + extern uint32_t RGB_WHITE; extern uint32_t RGB_BLACK; extern uint32_t RGB_LIGHT_GRAY; @@ -70,6 +91,7 @@ enum { }; SDL_Surface* GFX_init(int mode); +SDL_Surface* GFX_resize(int width, int height, int pitch); void GFX_setMode(int mode); void GFX_clear(SDL_Surface* screen); void GFX_clearAll(void); diff --git a/src/common/defines.h b/src/common/defines.h index da20cf7..ac52737 100644 --- a/src/common/defines.h +++ b/src/common/defines.h @@ -94,6 +94,7 @@ #define MAX(a, b) (a) > (b) ? (a) : (b) #define MIN(a, b) (a) < (b) ? (a) : (b) +#define CEIL_DIV(a,b) ((a) + (b) - 1) / (b) #define SCALE1(a) ((a)*SCREEN_SCALE) #define SCALE2(a,b) ((a)*SCREEN_SCALE),((b)*SCREEN_SCALE) diff --git a/src/minarch/main.c b/src/minarch/main.c index e0488c8..41d3c62 100644 --- a/src/minarch/main.c +++ b/src/minarch/main.c @@ -50,7 +50,14 @@ static SDL_Surface* screen; static int quit; static int show_menu; +enum { + SCALE_NATIVE, + SCALE_ASPECT, + SCALE_FULLSCREEN, +}; + // default frontend options +static int screen_scaling = SCALE_ASPECT; // aspect static int show_scanlines = 0; static int optimize_text = 1; static int prevent_tearing = 1; // lenient @@ -67,6 +74,7 @@ static struct Renderer { int dst_offset; int dst_w; int dst_h; + int dst_p; scale_neon_t scaler; } renderer; @@ -376,6 +384,12 @@ static char* onoff_labels[] = { "On", NULL }; +static char* scaling_labels[] = { + "Native", + "Aspect", + "Fullscreen", + NULL +}; static char* tearing_labels[] = { "Off", "Lenient", @@ -397,6 +411,7 @@ static char* max_ff_labels[] = { /////////////////////////////// enum { + FE_OPT_SCALING, FE_OPT_SCANLINES, FE_OPT_TEXT, FE_OPT_TEARING, @@ -410,6 +425,8 @@ enum { SHORTCUT_SAVE_STATE, SHORTCUT_LOAD_STATE, SHORTCUT_RESET_GAME, + SHORTCUT_CYCLE_SCALE, + SHORTCUT_TOGGLE_SCANLINES, SHORTCUT_TOGGLE_FF, SHORTCUT_HOLD_FF, SHORTCUT_COUNT, @@ -528,10 +545,20 @@ static struct Config { .frontend = (OptionList){ .count = FE_OPT_COUNT, .options = (Option[]){ + [FE_OPT_SCALING] = { + .key = "minarch_screen_scaling", + .name = "Screen Scaling", + .desc = "Native uses integer scaling. Aspect uses the core reported\naspect ratio. Fullscreen will produce non-square pixels. Gross.", + .default_value = 1, + .value = 1, + .count = 3, + .values = scaling_labels, + .labels = scaling_labels, + }, [FE_OPT_SCANLINES] = { .key = "minarch_scanlines_grid", .name = "Scanlines/Grid", - .desc = "Simulate scanlines (or a pixel grid at odd scales). Darkens\nthe overall image by about 50%. Reduces CPU load.", + .desc = "Simulate scanlines (or a pixel grid at odd scales).\nDarkens the overall image by about 50%. Reduces CPU load.\nOnly applies to native scaling.", .default_value = 0, .value = 0, .count = 2, @@ -541,7 +568,7 @@ static struct Config { [FE_OPT_TEXT] = { .key = "minarch_optimize_text", .name = "Optimize Text", - .desc = "Prioritize a consistent stroke width when upscaling single\npixel lines using nearest neighbor scaler. Increases CPU load.", + .desc = "Prioritize a consistent stroke width when upscaling single\npixel lines using nearest neighbor scaler. Increases CPU load.\nOnly applies to native scaling.", .default_value = 1, .value = 1, .count = 2, @@ -592,11 +619,13 @@ static struct Config { } }, .shortcuts = (ButtonMapping[]){ - [SHORTCUT_SAVE_STATE] = {"Save State", -1, BTN_ID_NONE, 0}, - [SHORTCUT_LOAD_STATE] = {"Load State", -1, BTN_ID_NONE, 0}, - [SHORTCUT_RESET_GAME] = {"Reset Game", -1, BTN_ID_NONE, 0}, - [SHORTCUT_TOGGLE_FF] = {"Toggle FF", -1, BTN_ID_NONE, 0}, - [SHORTCUT_HOLD_FF] = {"Hold FF", -1, BTN_ID_NONE, 0}, + [SHORTCUT_SAVE_STATE] = {"Save State", -1, BTN_ID_NONE, 0}, + [SHORTCUT_LOAD_STATE] = {"Load State", -1, BTN_ID_NONE, 0}, + [SHORTCUT_RESET_GAME] = {"Reset Game", -1, BTN_ID_NONE, 0}, + [SHORTCUT_CYCLE_SCALE] = {"Cycle Scale", -1, BTN_ID_NONE, 0}, + [SHORTCUT_TOGGLE_SCANLINES] = {"Toggle Scanlines", -1, BTN_ID_NONE, 0}, + [SHORTCUT_TOGGLE_FF] = {"Toggle FF", -1, BTN_ID_NONE, 0}, + [SHORTCUT_HOLD_FF] = {"Hold FF", -1, BTN_ID_NONE, 0}, {NULL} }, }; @@ -626,6 +655,7 @@ static void setOverclock(int i) { } static void Config_syncFrontend(int i, int value) { switch (i) { + case FE_OPT_SCALING: screen_scaling = value; renderer.src_w = 0; break; case FE_OPT_SCANLINES: show_scanlines = value; renderer.src_w = 0; break; case FE_OPT_TEXT: optimize_text = value; renderer.src_w = 0; break; case FE_OPT_TEARING: prevent_tearing = value; break; @@ -633,6 +663,8 @@ static void Config_syncFrontend(int i, int value) { case FE_OPT_DEBUG: show_debug = value; break; case FE_OPT_MAXFF: max_ff_speed = value; break; } + Option* option = &config.frontend.options[i]; + option->value = value; } static void OptionList_setOptionValue(OptionList* list, const char* key, const char* value); enum { @@ -677,6 +709,7 @@ static void Config_read(void) { for (int i=0; config.controls[i].name; i++) { ButtonMapping* mapping = &config.controls[i]; sprintf(key, "bind %s", mapping->name); + sprintf(value, "NONE"); Config_getValue(cfg, key, value); int id = -1; for (int j=0; button_labels[j]; j++) { @@ -692,6 +725,7 @@ static void Config_read(void) { for (int i=0; config.shortcuts[i].name; i++) { ButtonMapping* mapping = &config.shortcuts[i]; sprintf(key, "bind %s", mapping->name); + sprintf(value, "NONE"); Config_getValue(cfg, key, value); int id = -1; @@ -1092,6 +1126,16 @@ static void input_poll_callback(void) { case SHORTCUT_SAVE_STATE: State_write(); break; case SHORTCUT_LOAD_STATE: State_read(); break; case SHORTCUT_RESET_GAME: core.reset(); break; + case SHORTCUT_CYCLE_SCALE: + screen_scaling += 1; + if (screen_scaling>=3) screen_scaling -= 3; + Config_syncFrontend(FE_OPT_SCALING, screen_scaling); + break; + case SHORTCUT_TOGGLE_SCANLINES: + if (screen_scaling==SCALE_NATIVE) { + Config_syncFrontend(FE_OPT_SCANLINES, !show_scanlines); + } + break; default: break; } @@ -1476,12 +1520,17 @@ static int MSG_blitInt(int num, int x, int y) { i -= n * 100; x = MSG_blitChar(n,x,y); } - + else if (num>99) { + x = MSG_blitChar(0,x,y); + } if (i > 9) { n = i / 10; i -= n * 10; x = MSG_blitChar(n,x,y); } + else if (num>9) { + x = MSG_blitChar(0,x,y); + } n = i; x = MSG_blitChar(n,x,y); @@ -2009,8 +2058,10 @@ static void scaleNN_text_scanline(void* __restrict src, void* __restrict dst, ui } static SDL_Surface* scaler_surface; -static void selectScaler(int width, int height, int pitch) { +static void selectScaler_PAR(int width, int height, int pitch) { renderer.scaler = scaleNull; + renderer.dst_p = SCREEN_PITCH; + int use_nearest = 0; int scale_x = SCREEN_WIDTH / width; @@ -2120,7 +2171,7 @@ static void selectScaler(int width, int height, int pitch) { if (show_scanlines) renderer.scaler = optimize_text ? scaleNN_text_scanline : scaleNN_scanline; else renderer.scaler = optimize_text ? scaleNN_text : scaleNN; else { - sprintf(scaler_name, "%ix", scale); + sprintf(scaler_name, "%iX", scale); if (show_scanlines) { switch (scale) { case 4: renderer.scaler = scale4x_scanline; break; @@ -2150,8 +2201,86 @@ static void selectScaler(int width, int height, int pitch) { } } + ////////////////////////////// + + // DEBUG HUD if (scaler_surface) SDL_FreeSurface(scaler_surface); scaler_surface = TTF_RenderUTF8_Blended(font.tiny, scaler_name, COLOR_WHITE); + + screen = GFX_resize(SCREEN_WIDTH,SCREEN_HEIGHT, SCREEN_PITCH); +} +static void selectScaler_AR(int width, int height, int pitch) { + renderer.scaler = scaleNull; + + int src_w = width; + int src_h = height; + + int x_scale = CEIL_DIV(FIXED_WIDTH, src_w); + int y_scale = CEIL_DIV(FIXED_HEIGHT,src_h); + int scale = MAX(x_scale, y_scale); + + // if (scale>6) scale = 6; + // else + if (scale>2) scale = 4; // TODO: pillar/letterboxing at 3x produces vertical banding (some kind of alignment issue?) + + // reduce scale if we don't have enough memory to accomodate it + // TODO: some resolutions are getting through here unadjusted? oh maybe because of aspect ratio adjustments below? revisit + while (src_w * scale * FIXED_BPP * src_h * scale > PAGE_SIZE) scale -= 1; + + int dst_w = src_w * scale; + int dst_h = src_h * scale; + int target_w = dst_w; + int target_h = dst_h; + + char scaler_name[8]; +#define FOUR_THREE 4.0f / 3 + if (screen_scaling==1) { + sprintf(scaler_name, "AR_%iX", scale); + if (core.aspect_ratio==FOUR_THREE) { + // puts("already 4:3"); + } + else if (core.aspect_ratioh-DIGIT_HEIGHT,bottom_width,DIGIT_HEIGHT}, RGB_BLACK); - renderer.scaler((void*)data,screen->pixels+renderer.dst_offset,width,height,pitch,SCREEN_PITCH); + renderer.scaler((void*)data,screen->pixels+renderer.dst_offset,width,height,pitch,renderer.dst_p); if (0) { static int frame = 0; @@ -2225,7 +2353,7 @@ static void video_refresh_callback(const void *data, unsigned width, unsigned he if (show_debug) { int x = 0; - int y = SCREEN_HEIGHT - DIGIT_HEIGHT; + int y = screen->h - DIGIT_HEIGHT; if (fps_double) x = MSG_blitDouble(fps_double, x,y); @@ -2547,12 +2675,6 @@ static int MenuList_freeItems(MenuList* list, int i) { static int OptionFrontend_optionChanged(MenuList* list, int i) { MenuItem* item = &list->items[i]; - Option* option = &config.frontend.options[i]; - LOG_info("%s (%s) changed from `%s` (%s) to `%s` (%s)\n", item->name, item->key, - item->values[option->value], option->values[option->value], - item->values[item->value], option->values[item->value] - ); - option->value = item->value; Config_syncFrontend(i, item->value); } static MenuList OptionFrontend_menu = { @@ -2649,10 +2771,18 @@ int OptionControls_bind(MenuList* list, int i) { } return MENU_CALLBACK_NEXT_ITEM; } +static int OptionControls_unbind(MenuList* list, int i) { + MenuItem* item = &list->items[i]; + ButtonMapping* button = &config.controls[item->id]; + button->local = -1; + button->mod = 0; + return MENU_CALLBACK_NOP; +} static MenuList OptionControls_menu = { .type = MENU_INPUT, .desc = "Press A to set and X to clear.", .on_confirm = OptionControls_bind, + .on_change = OptionControls_unbind, .items = NULL }; static int OptionControls_openMenu(MenuList* list, int i) { @@ -2711,10 +2841,18 @@ static int OptionShortcuts_bind(MenuList* list, int i) { fflush(stdout); return MENU_CALLBACK_NEXT_ITEM; } +static int OptionShortcuts_unbind(MenuList* list, int i) { + MenuItem* item = &list->items[i]; + ButtonMapping* button = &config.shortcuts[item->id]; + button->local = -1; + button->mod = 0; + return MENU_CALLBACK_NOP; +} static MenuList OptionShortcuts_menu = { .type = MENU_INPUT, .desc = "Press A to set and X to clear.\nSupports single button and MENU+button.", .on_confirm = OptionShortcuts_bind, + .on_change = OptionShortcuts_unbind, .items = NULL }; static char* getSaveDesc(void) { @@ -3207,7 +3345,66 @@ static int Menu_options(MenuList* list) { return 0; } + +static void downsample_ORIGINAL(void* __restrict src, void* __restrict dst, uint32_t w, uint32_t h, uint32_t pitch, uint32_t dst_pitch) { + double x_step = w / (double)FIXED_WIDTH; // TODO: broken when pcsx_rearmed is the active core, returns -0.0 + double y_step = h / (double)FIXED_HEIGHT; + + double ox = 0; + double oy = 0; + for (int y=0; y=FIXED_WIDTH) { + ax -= FIXED_WIDTH; + ox += 1; + } + } + ox = 0; + oy += 1; + ay += dy; + while (ay>=FIXED_HEIGHT) { + ay -= FIXED_HEIGHT; + oy += 1; + } + } +} + static void Menu_loop(void) { + // current screen is on the previous buffer + SDL_Surface* backing = GFX_getBufferCopy(); + SDL_Surface* resized = SDL_CreateRGBSurface(SDL_SWSURFACE, SCREEN_WIDTH,SCREEN_HEIGHT,SCREEN_DEPTH,0,0,0,0); + if (backing->w==SCREEN_WIDTH && backing->h==SCREEN_HEIGHT) SDL_BlitSurface(backing, NULL, resized, NULL); + else { + downsample(backing->pixels,resized->pixels,backing->w,backing->h,backing->pitch,SCREEN_PITCH); + screen = GFX_resize(SCREEN_WIDTH,SCREEN_HEIGHT,SCREEN_PITCH); + } + POW_setCPUSpeed(CPU_SPEED_MENU); // set Hz directly GFX_setVsync(VSYNC_STRICT); @@ -3215,8 +3412,6 @@ static void Menu_loop(void) { POW_enableAutosleep(); PAD_reset(); - // current screen is on the previous buffer - SDL_Surface* backing = GFX_getBufferCopy(); // path and string things char* tmp; @@ -3367,7 +3562,7 @@ static void Menu_loop(void) { state_slot = menu.slot; State_write(); status = STATUS_SAVE; - SDL_Surface* preview = Menu_thumbnail(backing); + SDL_Surface* preview = Menu_thumbnail(resized); SDL_RWops* out = SDL_RWFromFile(bmp_path, "wb"); if (total_discs) { char* disc_path = disc_paths[disc]; @@ -3415,7 +3610,7 @@ static void Menu_loop(void) { POW_update(&dirty, &show_setting, Menu_beforeSleep, Menu_afterSleep); if (dirty) { - SDL_BlitSurface(backing, NULL, screen, NULL); + SDL_BlitSurface(resized, NULL, screen, NULL); SDL_BlitSurface(menu.overlay, NULL, screen, NULL); int ox, oy; @@ -3560,16 +3755,21 @@ static void Menu_loop(void) { PAD_reset(); GFX_clearAll(); - if (!quit) SDL_BlitSurface(backing, NULL, screen, NULL); - SDL_FreeSurface(backing); - GFX_flip(screen); - - POW_disableAutosleep(); - if (!quit) { + if (backing->w!=SCREEN_WIDTH || backing->h!=SCREEN_HEIGHT) { + screen = GFX_resize(renderer.dst_w,renderer.dst_h, renderer.dst_p); + } + + SDL_BlitSurface(backing, NULL, screen, NULL); + GFX_flip(screen); + GFX_setVsync(prevent_tearing); // restore vsync value setOverclock(overclock); // restore overclock value } + + SDL_FreeSurface(backing); + SDL_FreeSurface(resized); + POW_disableAutosleep(); } // TODO: move to POW_*?