Skip to content

Commit

Permalink
Revamp the aspect ratio code by introducing the concept of inner and …
Browse files Browse the repository at this point in the history
…outer game area.
  • Loading branch information
Arignir committed Oct 10, 2024
1 parent 2570b81 commit 4403350
Show file tree
Hide file tree
Showing 8 changed files with 155 additions and 159 deletions.
94 changes: 54 additions & 40 deletions include/app/app.h
Original file line number Diff line number Diff line change
Expand Up @@ -58,13 +58,12 @@ enum pixel_scaling_filter_kind {
};

enum aspect_ratio {
ASPECT_RATIO_RESIZE = 0,
ASPECT_RATIO_BORDERS = 1,
ASPECT_RATIO_STRETCH = 2,
ASPECT_RATIO_BORDERS = 0,
ASPECT_RATIO_STRETCH = 1,

ASPECT_RATIO_LEN,
ASPECT_RATIO_MIN = 0,
ASPECT_RATIO_MAX = 2,
ASPECT_RATIO_MAX = 1,
};

enum bind_actions {
Expand Down Expand Up @@ -198,7 +197,7 @@ struct settings {
// Display size
uint32_t display_size;

// Aspect Ratio (Black borders, Auto-Resize, etc.)
// Aspect Ratio (Black borders, Stretch, etc.)
enum aspect_ratio aspect_ratio;

// VSync
Expand Down Expand Up @@ -351,73 +350,86 @@ struct app {
bool exist;
} qsaves[MAX_QUICKSAVES];

bool flush_qsaves_cache; // Set to true if the `mtime` and `exist` field of `qsaves` needs to be refreshed.
// Set to true if both the `mtime` and `exist` field of `qsaves` needs to be refreshed.
bool flush_qsaves_cache;
} file;

struct {
uint32_t resample_frequency;
} audio;

struct {
/* ImGui internal stuff */
// ImGui internal stuff
struct ImGuiIO *ioptr;

struct {
struct ImFont *normal;
struct ImFont *big;
} fonts;

/* High resolution */
// High resolution
float dpi;
uint32_t scale;

/* Display refresh rate */
// Display refresh rate
uint32_t refresh_rate;

/* How many frames before going back to power save mode? */
// How many frames before going back to power save mode?
uint32_t power_save_fcounter;

/* Temporary value used to measure the FPS. */
// Temporary value used to measure the FPS.
uint32_t ticks_last_frame;

/* Temporary value used to measure the time since the last mouse movement (in ms) */
// Temporary value used to measure the time since the last mouse movement (in ms)
float time_elapsed_since_last_mouse_motion_ms;

/*
** The size of the `game` window.
** Usually the size of the window minus the menubar's height (if it is visible).
*/
struct {
int width;
int height;
} game;

/* Size of the FPS counter within the menubar. */
// Width of the FPS counter within the menubar.
float menubar_fps_width;

/* Size of the menu bar */
// Size of the menu bar
ImVec2 menubar_size;

struct {
int width;
int height;
bool maximized;

/* Used when resizing, to know if the new window is bigger or smaller than the previous one. */
uint32_t old_area;

/* Indicates if the window needs to be resized */
bool resize;
// The game area is made of two sub areas: the inner game area and the outer game area.
//
// The outer game area is a subset of the window area that represents where the game should be drawn.
// It's basically the same as the window area but doesn't include any visible UI elements (such as the menubar).
// The inner game area is a subset of the outer game area and trully represent the area where the game is drawn.
//
// The difference between the two is that the outer game area matches the aspect ratio of the window, which might not
// be the same than the one of the GBA. If they mismatch and the aspect ratio is set to "black borders", then the
// inner game area will be the subset where the game is drawn, without the black borders, while the outer game area
// will also include the black borders around the game.
//
// In other words, the inner game area is contained within the outer one, and can only be smaller or equal.
struct {
struct {
uint32_t x;
uint32_t y;
uint32_t width;
uint32_t height;
} inner;

struct {
uint32_t x;
uint32_t y;
uint32_t width;
uint32_t height;
} outer;
} game;

// The size of the window area.
// Exactly equal to the size of the window
struct {
uint32_t width;
uint32_t height;
} win;

/*
** Indicates if the user wants to resize the windows to the given ratio.
** Otherwise, `video->display_size` is taken
**/
bool resize_with_ratio;
float resize_ratio;
} win;
// Set when the window needs to be resized to fit a specific aspect ratio
bool request_resize;
} display;

/* The error message to print, if any. */
// The error message to print, if any.
struct {
char *msg;
bool active;
Expand Down Expand Up @@ -483,6 +495,7 @@ void app_sdl_video_init(struct app *app);
void app_sdl_video_cleanup(struct app *app);
void app_sdl_video_render_frame(struct app *app);
void app_sdl_video_rebuild_pipeline(struct app *app);
void app_sdl_video_resize_window(struct app *app);

/* app/shaders/frag-color-correction.c */
extern char const *SHADER_FRAG_COLOR_CORRECTION;
Expand All @@ -504,6 +517,7 @@ extern char const *SHADER_VERTEX_COMMON;

/* app/windows/game.c */
void app_win_game(struct app *app);
void app_win_game_refresh_game_area(struct app *app);

/* app/windows/menubar.c */
void app_win_menubar(struct app *app);
Expand Down
28 changes: 6 additions & 22 deletions source/app/main.c
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ app_settings_default(
settings->video.pixel_scaling_filter = PIXEL_SCALING_FILTER_LCD_GRID;
settings->video.vsync = false;
settings->video.display_size = 3;
settings->video.aspect_ratio = ASPECT_RATIO_RESIZE;
settings->video.aspect_ratio = ASPECT_RATIO_BORDERS;
settings->video.texture_filter = TEXTURE_FILTER_NEAREST;
settings->audio.mute = false;
settings->audio.level = 1.0f;
Expand All @@ -94,8 +94,7 @@ main(
app.emulation.is_started = false;
app.emulation.is_running = false;
app.audio.resample_frequency = 48000;
app.ui.win.resize = true;
app.ui.win.resize_with_ratio = false;
app.ui.display.request_resize = true;
app_settings_default(&app.settings);
app_bindings_setup_default(&app);

Expand Down Expand Up @@ -205,25 +204,10 @@ main(
}
}

// The window needs to be resized
if (app.ui.win.resize) {
uint32_t new_width;
uint32_t new_height;

// Do we wanna resize it to the aspect ratio given in `app.ui.win.resize_ratio`?
// Otherwise, use `app.video.display_size`.
if (app.ui.win.resize_with_ratio) {
new_width = GBA_SCREEN_WIDTH * app.ui.win.resize_ratio * app.ui.scale;
new_height = app.ui.menubar_size.y + GBA_SCREEN_HEIGHT * app.ui.win.resize_ratio * app.ui.scale;
} else {
new_width = GBA_SCREEN_WIDTH * app.settings.video.display_size * app.ui.scale;
new_height = app.ui.menubar_size.y + GBA_SCREEN_HEIGHT * app.settings.video.display_size * app.ui.scale;
}

SDL_SetWindowMinimumSize(app.sdl.window, GBA_SCREEN_WIDTH * app.ui.scale, app.ui.menubar_size.y + GBA_SCREEN_HEIGHT * app.ui.scale);
SDL_SetWindowSize(app.sdl.window, new_width, new_height);
app.ui.win.resize = false;
app.ui.win.resize_with_ratio = false;
// Handle window resize request
if (app.ui.display.request_resize) {
app_sdl_video_resize_window(&app);
app.ui.display.request_resize = false;
}

elapsed_ms = ((float)(sdl_counters[1] - sdl_counters[0]) / (float)SDL_GetPerformanceFrequency()) * 1000.f;
Expand Down
37 changes: 4 additions & 33 deletions source/app/sdl/event.c
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ app_sdl_handle_events(
break;
};
case SDL_WINDOWEVENT: {
/* Keep only events related to our current window. */
// Keep only events related to our current window.
if (event.window.windowID != SDL_GetWindowID(app->sdl.window)) {
break;
}
Expand All @@ -42,39 +42,10 @@ app_sdl_handle_events(
app->run = false;
break;
};
case SDL_WINDOWEVENT_MAXIMIZED: {
app->ui.win.maximized = true;
break;
};
case SDL_WINDOWEVENT_RESTORED: {
app->ui.win.maximized = false;
break;
};
case SDL_WINDOWEVENT_SIZE_CHANGED: {
app->ui.win.old_area = app->ui.win.width * app->ui.win.height;
app->ui.win.width = event.window.data1;
app->ui.win.height = event.window.data2;
app->ui.game.width = app->ui.win.width;
app->ui.game.height = app->ui.win.height - app->ui.menubar_size.y;
break;
};
case SDL_WINDOWEVENT_RESIZED: {
/*
** The "auto-resize the window to keep the aspect ration" feature conflicts with the "maximized window" feature of modern
** exploitation systems.
**
** In that case, we do not auto-resize the window and display black borders instead.
*/
if (app->settings.video.aspect_ratio == ASPECT_RATIO_RESIZE && !app->ui.win.maximized) {
app->ui.win.resize = true;
app->ui.win.resize_with_ratio = true;

if (app->ui.win.width * app->ui.win.height >= app->ui.win.old_area) { // The window was made bigger
app->ui.win.resize_ratio = max(app->ui.game.width / ((float)GBA_SCREEN_WIDTH * app->ui.scale), app->ui.game.height / ((float)GBA_SCREEN_HEIGHT * app->ui.scale));
} else {
app->ui.win.resize_ratio = min(app->ui.game.width / ((float)GBA_SCREEN_WIDTH * app->ui.scale), app->ui.game.height / ((float)GBA_SCREEN_HEIGHT * app->ui.scale));
}
}
app->ui.display.win.width = event.window.data1;
app->ui.display.win.height = event.window.data2;
app_win_game_refresh_game_area(app);
break;
};
case SDL_WINDOWEVENT_FOCUS_GAINED: {
Expand Down
42 changes: 27 additions & 15 deletions source/app/sdl/video.c
Original file line number Diff line number Diff line change
Expand Up @@ -78,24 +78,24 @@ app_sdl_video_init(
}
#endif

/*
** Create the SDL window
**
** The window is resized after the first frame to take into account the height of the menubar,
** unknown at this stage.
** The size given here is merely a guess as to what the real size will be, hence the magical +19.f for the window's height.
*/
app->ui.game.width = GBA_SCREEN_WIDTH * app->settings.video.display_size * app->ui.scale;
app->ui.game.height = GBA_SCREEN_HEIGHT * app->settings.video.display_size * app->ui.scale;
app->ui.win.width = app->ui.game.width;
app->ui.win.height = app->ui.game.height + 19.f * app->ui.scale;

// Initialise the window area.
//
// The window is resized after the first frame to take into account the height of the menubar,
// unknown at this stage.
//
// The size given here is merely a guess as to what the real size will be, hence the magical +19.f for the window's height.
app->ui.menubar_size.y = 19.f;
app->ui.display.win.width = GBA_SCREEN_WIDTH * app->settings.video.display_size * app->ui.scale;
app->ui.display.win.height = (GBA_SCREEN_HEIGHT * app->settings.video.display_size + app->ui.menubar_size.y) * app->ui.scale;
app_win_game_refresh_game_area(app);

// Create the SDL window
app->sdl.window = SDL_CreateWindow(
"Hades",
SDL_WINDOWPOS_CENTERED,
SDL_WINDOWPOS_CENTERED,
app->ui.win.width,
app->ui.win.height,
app->ui.display.win.width,
app->ui.display.win.height,
SDL_WINDOW_SHOWN | SDL_WINDOW_OPENGL | SDL_WINDOW_RESIZABLE
);

Expand All @@ -104,7 +104,6 @@ app_sdl_video_init(
exit(EXIT_FAILURE);
}


/* Create the OpenGL context */
app->gfx.gl_context = SDL_GL_CreateContext(app->sdl.window);
SDL_GL_MakeCurrent(app->sdl.window, app->gfx.gl_context);
Expand Down Expand Up @@ -189,6 +188,19 @@ app_sdl_video_init(
NFD_Init();
}

void
app_sdl_video_resize_window(
struct app *app
) {
uint32_t w;
uint32_t h;

w = GBA_SCREEN_WIDTH * app->settings.video.display_size * app->ui.scale;
h = GBA_SCREEN_HEIGHT * app->settings.video.display_size * app->ui.scale + app->ui.menubar_size.y;

SDL_SetWindowSize(app->sdl.window, w, h);
}

void
app_sdl_video_rebuild_pipeline(
struct app *app
Expand Down
Loading

0 comments on commit 4403350

Please sign in to comment.