Merge branch 'port' of https://github.com/fgsfdsfgs/perfect_dark into port-net

This commit is contained in:
fgsfds committed 2025-03-02 13:38:12 +01:00
commit ae7b21cf56
12 files changed
+778 -53

No files matched your search

+3 -2
View File
@@ -282,7 +282,7 @@ jobs:
bundle: io.github.fgsfdsfgs.perfect_dark.flatpak
manifest-path: dist/linux/io.github.fgsfdsfgs.perfect_dark.yaml
cache-key: flatpak-builder-${{ github.sha }}
upload-artifact: true
upload-artifact: false
# - name: Build Flatpak bundle
# run: |
@@ -302,7 +302,7 @@ jobs:
publish-latest-build:
runs-on: ubuntu-latest
if: github.ref == 'refs/heads/port'
needs: [build-i686-windows, build-i686-linux, build-x86_64-windows, build-x86_64-linux, build-arm64-nswitch, build-x86_64-osx, build-arm64-osx]
needs: [build-i686-windows, build-i686-linux, build-x86_64-windows, build-x86_64-linux, build-arm64-nswitch, build-x86_64-osx, build-arm64-osx, build-flatpak-bundle]
permissions:
contents: write
steps:
@@ -323,6 +323,7 @@ jobs:
zip -r ../ci-release/pd-i686-windows.zip pd-i686-windows
zip -r ../ci-release/pd-x86_64-windows.zip pd-x86_64-windows
zip -r ../ci-release/pd-arm64-nswitch.zip pd-arm64-nswitch
mv io.github.fgsfdsfgs.perfect_dark.flatpak/io.github.fgsfdsfgs.perfect_dark.flatpak ../ci-release/
popd
git tag --force ci-dev-build port
git push --force origin ci-dev-build
+1 -1
View File
@@ -41,7 +41,7 @@ There are minor graphics- and gameplay-related issues, and possibly occasional c
* basic mod support, currently enough to load a few custom levels;
* slightly expanded memory heap size;
* experimental high framerate support (up to 240 FPS):
* set `Game.TickRateDivisor` to `0` in `pd.ini` to activate;
* enable `Uncap Tickrate` in `Extended Video Options` to activate;
* in practice the game will have issues running faster than ~165 FPS, so use VSync or `Video.FramerateLimit` to cap it.
* emulate the Transfer Pak functionality the game has on the Nintendo 64 to unlock some cheats automatically.
+118 -7
View File
@@ -30,10 +30,30 @@ static uint64_t qpc_freq;
#define FRAME_INTERVAL_US_NUMERATOR 1000000
#define FRAME_INTERVAL_US_DENOMINATOR (target_fps)
static void set_fullscreen(bool on, bool call_callback) {
if (fullscreen_state == on) {
return;
static int32_t gfx_sdl_get_maximized_state(void) {
return (int32_t)maximized_state;
}
static int32_t gfx_sdl_get_fullscreen_state(void) {
return (int32_t)fullscreen_state;
}
static int32_t gfx_sdl_get_fullscreen_flag_mode(void) {
return fullscreen_flag == SDL_WINDOW_FULLSCREEN_DESKTOP ? 0 : 1;
}
static void gfx_sdl_set_fullscreen_flag(int32_t mode) {
switch (mode) {
case 0: {
fullscreen_flag = SDL_WINDOW_FULLSCREEN_DESKTOP;
} break;
case 1: {
fullscreen_flag = SDL_WINDOW_FULLSCREEN;
} break;
}
}
static void set_fullscreen(bool on, bool call_callback) {
fullscreen_state = on;
SDL_SetWindowFullscreen(wnd, on ? fullscreen_flag : 0);
if (call_callback && on_fullscreen_changed_callback) {
@@ -42,9 +62,6 @@ static void set_fullscreen(bool on, bool call_callback) {
}
static void set_maximize_window(bool on) {
if (maximized_state == on) {
return;
}
maximized_state = on;
if (on) {
SDL_MaximizeWindow(wnd);
@@ -95,6 +112,13 @@ static void gfx_sdl_init(const struct GfxWindowInitSettings *set) {
posY = 100;
}
if (set->centered) {
SDL_DisplayMode mode = {};
SDL_GetCurrentDisplayMode(0, &mode);
posX = mode.w / 2 - window_width / 2;
posY = mode.h / 2 - window_height / 2;
}
if (set->fullscreen_is_exclusive) {
fullscreen_flag = SDL_WINDOW_FULLSCREEN;
}
@@ -222,6 +246,43 @@ static void gfx_sdl_set_cursor_visibility(bool visible) {
}
}
static void get_centered_positions_native(int32_t width, int32_t height, int32_t *posX, int32_t *posY) {
const int disp_idx = SDL_GetWindowDisplayIndex(wnd);
SDL_DisplayMode mode = {};
SDL_GetDesktopDisplayMode(disp_idx, &mode);
*posX = mode.w / 2 - width / 2;
*posY = mode.h / 2 - height / 2;
}
static void gfx_sdl_get_centered_positions(int32_t width, int32_t height, int32_t *posX, int32_t *posY) {
const int disp_idx = SDL_GetWindowDisplayIndex(wnd);
SDL_DisplayMode mode = {};
SDL_GetCurrentDisplayMode(disp_idx, &mode);
*posX = mode.w / 2 - width / 2;
*posY = mode.h / 2 - height / 2;
}
static void gfx_sdl_set_closest_resolution(int32_t width, int32_t height, bool should_center) {
const SDL_DisplayMode mode = {.w = width, .h = height};
const int disp_idx = SDL_GetWindowDisplayIndex(wnd);
SDL_DisplayMode closest = {};
if (SDL_GetClosestDisplayMode(disp_idx, &mode, &closest)) {
SDL_SetWindowDisplayMode(wnd, &closest);
SDL_SetWindowSize(wnd, closest.w, closest.h);
if (should_center) {
int32_t posX = 0;
int32_t posY = 0;
get_centered_positions_native(closest.w, closest.h, &posX, &posY);
SDL_SetWindowPosition(wnd, posX, posY);
}
}
}
static void gfx_sdl_set_dimensions(uint32_t width, uint32_t height, int32_t posX, int32_t posY) {
SDL_SetWindowSize(wnd, width, height);
SDL_SetWindowPosition(wnd, posX, posY);
}
static void gfx_sdl_get_dimensions(uint32_t* width, uint32_t* height, int32_t* posX, int32_t* posY) {
SDL_GL_GetDrawableSize(wnd, static_cast<int*>((void*)width), static_cast<int*>((void*)height));
SDL_GetWindowPosition(wnd, static_cast<int*>(posX), static_cast<int*>(posY));
@@ -240,6 +301,9 @@ static void gfx_sdl_handle_events(void) {
case SDL_WINDOWEVENT:
if (event.window.event == SDL_WINDOWEVENT_SIZE_CHANGED) {
SDL_GL_GetDrawableSize(wnd, &window_width, &window_height);
if (!fullscreen_state) {
maximized_state = SDL_GetWindowFlags(wnd) & SDL_WINDOW_MAXIMIZED ? true : false;
}
} else if (event.window.event == SDL_WINDOWEVENT_CLOSE &&
event.window.windowID == SDL_GetWindowID(wnd)) {
// We listen specifically for main window close because closing main window
@@ -304,6 +368,10 @@ static double gfx_sdl_get_time(void) {
return SDL_GetPerformanceCounter() / (double)qpc_freq;
}
static int32_t gfx_sdl_get_target_fps(void) {
return target_fps;
}
static void gfx_sdl_set_target_fps(int fps) {
target_fps = fps;
}
@@ -320,6 +388,10 @@ static void gfx_sdl_set_window_title(const char *title) {
SDL_SetWindowTitle(wnd, title);
}
static int gfx_sdl_get_swap_interval(void) {
return SDL_GL_GetSwapInterval();
}
static bool gfx_sdl_set_swap_interval(int interval) {
const bool success = SDL_GL_SetSwapInterval(interval) >= 0;
vsync_enabled = success && (interval != 0);
@@ -329,24 +401,63 @@ static bool gfx_sdl_set_swap_interval(int interval) {
return success;
}
struct GfxWindowManagerAPI gfx_sdl = {
int gfx_sdl_get_display_mode(int modenum, int *out_w, int *out_h) {
const int display_in_use = SDL_GetWindowDisplayIndex(wnd);
SDL_DisplayMode sdlmode;
if (SDL_GetDisplayMode(display_in_use, modenum, &sdlmode) == 0) {
*out_w = sdlmode.w;
*out_h = sdlmode.h;
return 1;
}
return 0;
}
int gfx_sdl_get_current_display_mode(int *out_w, int *out_h) {
const int display_in_use = SDL_GetWindowDisplayIndex(wnd);
SDL_DisplayMode sdlmode;
if (SDL_GetCurrentDisplayMode(display_in_use, &sdlmode) == 0) {
*out_w = sdlmode.w;
*out_h = sdlmode.h;
return 1;
}
return 0;
}
int gfx_sdl_get_num_display_modes(void) {
const int display_in_use = SDL_GetWindowDisplayIndex(wnd);
return SDL_GetNumDisplayModes(display_in_use);
}
struct GfxWindowManagerAPI gfx_sdl = {
gfx_sdl_init,
gfx_sdl_close,
gfx_sdl_get_display_mode,
gfx_sdl_get_current_display_mode,
gfx_sdl_get_num_display_modes,
gfx_sdl_get_fullscreen_state,
gfx_sdl_set_fullscreen_changed_callback,
gfx_sdl_set_fullscreen,
gfx_sdl_set_fullscreen_exclusive,
gfx_sdl_set_fullscreen_flag,
gfx_sdl_get_fullscreen_flag_mode,
gfx_sdl_get_maximized_state,
gfx_sdl_set_maximize_window,
gfx_sdl_get_active_window_refresh_rate,
gfx_sdl_set_cursor_visibility,
gfx_sdl_set_closest_resolution,
gfx_sdl_set_dimensions,
gfx_sdl_get_dimensions,
gfx_sdl_get_centered_positions,
gfx_sdl_handle_events,
gfx_sdl_start_frame,
gfx_sdl_swap_buffers_begin,
gfx_sdl_swap_buffers_end,
gfx_sdl_get_time,
gfx_sdl_get_target_fps,
gfx_sdl_set_target_fps,
gfx_sdl_can_disable_vsync,
gfx_sdl_get_window_handle,
gfx_sdl_set_window_title,
gfx_sdl_get_swap_interval,
gfx_sdl_set_swap_interval,
};
+13
View File
@@ -13,28 +13,41 @@ struct GfxWindowInitSettings {
bool fullscreen;
bool fullscreen_is_exclusive;
bool maximized;
bool centered;
bool allow_hidpi;
};
struct GfxWindowManagerAPI {
void (*init)(const struct GfxWindowInitSettings *settings);
void (*close)(void);
int (*get_display_mode)(int modenum, int *out_w, int *out_h);
int (*get_current_display_mode)(int *out_w, int *out_h);
int (*get_num_display_modes)(void);
int32_t (*get_fullscreen_state)(void);
void (*set_fullscreen_changed_callback)(void (*on_fullscreen_changed)(bool is_now_fullscreen));
void (*set_fullscreen)(bool enable);
void (*set_fullscreen_exclusive)(bool exc);
void (*set_fullscreen_flag)(int32_t mode);
int32_t (*get_fullscreen_flag_mode)(void);
int32_t (*get_maximized_state)(void);
void (*set_maximize)(bool enable);
void (*get_active_window_refresh_rate)(uint32_t* refresh_rate);
void (*set_cursor_visibility)(bool visible);
void (*set_closest_resolution)(int32_t width, int32_t height, bool should_center);
void (*set_dimensions)(uint32_t width, uint32_t height, int32_t posX, int32_t posY);
void (*get_dimensions)(uint32_t* width, uint32_t* height, int32_t* posX, int32_t* posY);
void (*get_centered_positions)(int32_t width, int32_t height, int32_t *posX, int32_t *posY);
void (*handle_events)(void);
bool (*start_frame)(void);
void (*swap_buffers_begin)(void);
void (*swap_buffers_end)(void);
double (*get_time)(void); // For debug
int32_t (*get_target_fps)(void);
void (*set_target_fps)(int fps);
bool (*can_disable_vsync)(void);
void *(*get_window_handle)(void);
void (*set_window_title)(const char *);
int (*get_swap_interval)(void);
bool (*set_swap_interval)(int);
};
+28 -1
View File
@@ -11,6 +11,11 @@
#define VIDEO_MAX_FPS 240
#endif
typedef struct {
s32 width;
s32 height;
} displaymode;
s32 videoInit(void);
void videoStartFrame(void);
void videoSubmitCommands(Gfx *cmds);
@@ -27,18 +32,38 @@ s32 videoGetWidth(void);
s32 videoGetHeight(void);
f32 videoGetAspect(void);
s32 videoGetFullscreen(void);
s32 videoGetFullscreenMode(void);
s32 videoGetMaximizeWindow(void);
void videoSetMaximizeWindow(s32 fs);
s32 videoGetCenterWindow(void);
void videoSetCenterWindow(s32 center);
u32 videoGetTextureFilter(void);
s32 videoGetTextureFilter2D(void);
s32 videoGetDetailTextures(void);
s32 videoGetDisplayModeIndex(void);
s32 videoGetDisplayMode(displaymode *out, const s32 index);
s32 videoGetNumDisplayModes(void);
s32 videoGetVsync(void);
s32 videoGetFramerateLimit(void);
s32 videoGetDisplayFPS(void);
s32 videoGetMSAA(void);
f32 videoGetAverageFPS(void);
f64 videoGetLastRenderTime(void);
void videoCapFramerate(s32 limit);
void videoSetWindowOffset(s32 x, s32 y);
void videoSetFullscreen(s32 fs);
void videoSetFullscreenMode(s32 mode);
void videoSetTextureFilter(u32 filter);
void videoSetTextureFilter2D(s32 filter);
void videoSetDetailTextures(s32 detail);
void videoCapFramerate(s32 limit);
void videoSetDisplayMode(const s32 index);
void videoSetVsync(const s32 vsync);
void videoSetFramerateLimit(const s32 limit);
void videoSetDisplayFPS(const s32 displayfps);
void videoSetMSAA(const s32 msaa);
s32 videoCreateFramebuffer(u32 w, u32 h, s32 upscale, s32 autoresize);
void videoSetFramebuffer(s32 target);
@@ -50,4 +75,6 @@ s32 videoFramebuffersSupported(void);
void videoResetTextureCache(void);
void videoFreeCachedTexture(const void *texptr);
void videoShutdown(void);
#endif
+7 -2
View File
@@ -10,7 +10,7 @@
#define CONFIG_MAX_SECNAME 128
#define CONFIG_MAX_KEYNAME 256
#define CONFIG_MAX_SETTINGS 256
#define CONFIG_MAX_SETTINGS 300
typedef enum {
CFG_NONE,
@@ -34,6 +34,7 @@ struct configentry {
} settings[CONFIG_MAX_SETTINGS];
static s32 numSettings = 0;
static u8 configMaxWarningLogged = 0;
static inline s32 configClampInt(s32 val, s32 min, s32 max)
{
@@ -64,11 +65,15 @@ static inline struct configentry *configAddEntry(const char *key)
{
if (numSettings < CONFIG_MAX_SETTINGS) {
struct configentry *cfg = &settings[numSettings++];
strncpy(cfg->key, key, CONFIG_MAX_KEYNAME);
snprintf(cfg->key, CONFIG_MAX_KEYNAME, "%s", key);
const char *delim = strrchr(cfg->key, '.');
cfg->seclen = delim ? (delim - cfg->key) : 0;
return cfg;
}
if (!configMaxWarningLogged) {
sysLogPrintf(LOG_WARNING, "Maximum number of configuration entries exceeded: %d", CONFIG_MAX_SETTINGS);
configMaxWarningLogged = 1;
}
return NULL;
}
+1
View File
@@ -91,6 +91,7 @@ static void cleanup(void)
netDisconnect();
inputSaveBinds();
configSave(CONFIG_PATH);
videoShutdown();
crashShutdown();
// TODO: actually shut down all subsystems
}
+261 -10
View File
@@ -698,19 +698,164 @@ static MenuItemHandlerResult menuhandlerFullScreen(s32 operation, struct menuite
return 0;
}
static MenuItemHandlerResult menuhandlerMaximizeWindow(s32 operation, struct menuitem *item, union handlerdata *data)
static MenuItemHandlerResult menuhandlerFullScreenMode(s32 operation, struct menuitem *item, union handlerdata *data)
{
static const char *opts[] = {
"Borderless",
"Exclusive"
};
switch (operation) {
case MENUOP_GETOPTIONCOUNT:
data->dropdown.value = ARRAYCOUNT(opts);
break;
case MENUOP_GETOPTIONTEXT:
return (intptr_t)opts[data->dropdown.value];
case MENUOP_SET:
videoSetFullscreenMode(data->dropdown.value);
break;
case MENUOP_GETSELECTEDINDEX:
data->dropdown.value = videoGetFullscreenMode();
}
return 0;
}
static MenuItemHandlerResult menuhandlerCenterWindow(s32 operation, struct menuitem *item, union handlerdata *data)
{
switch (operation) {
case MENUOP_GET:
return videoGetMaximizeWindow();
return videoGetCenterWindow();
case MENUOP_SET:
videoSetMaximizeWindow(data->checkbox.value);
videoSetCenterWindow(data->checkbox.value);
break;
}
return 0;
}
static MenuItemHandlerResult menuhandlerVsync(s32 operation, struct menuitem *item, union handlerdata *data)
{
static const s32 numOpts = 10;
static const char *constOpts[] = {
"Adaptive",
"Off",
"On"
};
static char dynOpt[20];
s32 vblanks;
switch (operation) {
case MENUOP_GETOPTIONCOUNT:
data->dropdown.value = numOpts;
break;
case MENUOP_GETOPTIONTEXT:
if (data->dropdown.value < ARRAYCOUNT(constOpts))
return (intptr_t)constOpts[data->dropdown.value];
vblanks = (s32)data->dropdown.value - 1;
snprintf(dynOpt, sizeof(dynOpt), "On (%d frames)", vblanks);
return (intptr_t)dynOpt;
case MENUOP_SET:
videoSetVsync(data->dropdown.value - 1);
case MENUOP_GETSELECTEDINDEX:
data->dropdown.value = videoGetVsync() + 1;
}
return 0;
}
static MenuItemHandlerResult menuhandlerFramerateLimit(s32 operation, struct menuitem *item, union handlerdata *data)
{
switch (operation) {
case MENUOP_GETSLIDER:
data->slider.value = videoGetFramerateLimit();
break;
case MENUOP_SET:
videoSetFramerateLimit(data->slider.value);
break;
case MENUOP_GETSLIDERLABEL:
// NOTE: data->slider.label length must not exceed 15.
if (data->slider.value == 0) {
strcpy(data->slider.label, "Off");
} else {
sprintf(data->slider.label, "%d FPS", data->slider.value);
}
}
return 0;
}
static MenuItemHandlerResult menuhandlerMSAA(s32 operation, struct menuitem *item, union handlerdata *data)
{
s32 msaa;
static const char *opts[] = {
"Off",
"2x (MSAA)",
"4x (MSAA)",
"8x (MSAA)",
"16x (MSAA)"
};
switch (operation) {
case MENUOP_GETOPTIONCOUNT:
data->dropdown.value = ARRAYCOUNT(opts);
break;
case MENUOP_GETOPTIONTEXT:
return (intptr_t)opts[data->dropdown.value];
case MENUOP_SET:
videoSetMSAA(1 << data->dropdown.value);
break;
case MENUOP_GETSELECTEDINDEX:
msaa = videoGetMSAA();
if (msaa < 2) {
data->dropdown.value = 0;
} else if (msaa < 4) {
data->dropdown.value = 1;
} else if (msaa < 8) {
data->dropdown.value = 2;
} else if (msaa < 16) {
data->dropdown.value = 3;
} else {
data->dropdown.value = 4;
}
}
return 0;
}
static MenuItemHandlerResult menuhandlerResolution(s32 operation, struct menuitem *item, union handlerdata *data)
{
static char resstring[32];
static const char *rescustom = "Custom";
displaymode mode;
switch (operation) {
case MENUOP_CHECKDISABLED:
if (videoGetFullscreen() && videoGetFullscreenMode() == 0) {
return true;
}
break;
case MENUOP_GETOPTIONCOUNT:
data->dropdown.value = videoGetNumDisplayModes();
break;
case MENUOP_GETOPTIONTEXT:
videoGetDisplayMode(&mode, data->dropdown.value);
if (mode.width == 0 && mode.height == 0) {
return (intptr_t)rescustom;
} else {
snprintf(resstring, sizeof(resstring), "%dx%d", mode.width, mode.height);
}
return (intptr_t)resstring;
case MENUOP_SET:
videoSetDisplayMode(data->dropdown.value);
break;
case MENUOP_GETSELECTEDINDEX:
data->dropdown.value = videoGetDisplayModeIndex();
}
return 0;
}
static MenuItemHandlerResult menuhandlerTexFilter(s32 operation, struct menuitem *item, union handlerdata *data)
{
static const char *opts[] = {
@@ -755,12 +900,25 @@ static MenuItemHandlerResult menuhandlerTexFilter2D(s32 operation, struct menuit
return videoGetTextureFilter2D();
case MENUOP_SET:
videoSetTextureFilter2D(data->checkbox.value);
g_TexFilter2D = videoGetTextureFilter2D() ? G_TF_BILERP : G_TF_POINT;
break;
}
return 0;
}
static MenuItemHandlerResult menuhandlerDisplayFPS(s32 operation, struct menuitem *item, union handlerdata *data)
{
switch (operation) {
case MENUOP_GET:
return videoGetDisplayFPS();
case MENUOP_SET:
videoSetDisplayFPS(data->checkbox.value);
}
return 0;
}
static MenuItemHandlerResult menuhandlerGeMuzzleFlashes(s32 operation, struct menuitem *item, union handlerdata *data)
{
switch (operation) {
@@ -774,6 +932,19 @@ static MenuItemHandlerResult menuhandlerGeMuzzleFlashes(s32 operation, struct me
return 0;
}
static MenuItemHandlerResult menuhandlerUncapTickrate(s32 operation, struct menuitem *item, union handlerdata *data)
{
switch (operation) {
case MENUOP_GET:
return (g_TickRateDiv == 0);
case MENUOP_SET:
g_TickRateDiv = !data->checkbox.value;
break;
}
return 0;
}
static MenuItemHandlerResult menuhandlerCenterHUD(s32 operation, struct menuitem *item, union handlerdata *data)
{
static const char *opts[] = {
@@ -832,20 +1003,84 @@ struct menuitem g_ExtendedVideoMenuItems[] = {
menuhandlerFullScreen,
},
{
MENUITEMTYPE_CHECKBOX,
MENUITEMTYPE_DROPDOWN,
0,
MENUITEMFLAG_LITERAL_TEXT,
(uintptr_t)"Maximize Window",
(uintptr_t)"Full Screen Mode",
0,
menuhandlerMaximizeWindow,
menuhandlerFullScreenMode,
},
{
MENUITEMTYPE_DROPDOWN,
0,
MENUITEMFLAG_LITERAL_TEXT,
(uintptr_t)"Resolution",
0,
menuhandlerResolution,
},
{
MENUITEMTYPE_CHECKBOX,
0,
MENUITEMFLAG_LITERAL_TEXT,
(uintptr_t)"Detail Textures",
(uintptr_t)"Center Window",
0,
menuhandlerTexDetail,
menuhandlerCenterWindow,
},
{
MENUITEMTYPE_DROPDOWN,
0,
MENUITEMFLAG_LITERAL_TEXT,
(uintptr_t)"Anti-aliasing",
0,
menuhandlerMSAA,
},
{
MENUITEMTYPE_SEPARATOR,
0,
0,
0,
0,
NULL,
},
{
MENUITEMTYPE_DROPDOWN,
0,
MENUITEMFLAG_LITERAL_TEXT,
(uintptr_t)"Vsync",
0,
menuhandlerVsync,
},
{
MENUITEMTYPE_SLIDER,
0,
MENUITEMFLAG_LITERAL_TEXT | MENUITEMFLAG_SLIDER_WIDE | MENUITEMFLAG_SLIDER_DEFERRED,
(uintptr_t)"Framerate Limit",
VIDEO_MAX_FPS,
menuhandlerFramerateLimit,
},
{
MENUITEMTYPE_CHECKBOX,
0,
MENUITEMFLAG_LITERAL_TEXT,
(uintptr_t)"Uncap Tickrate",
0,
menuhandlerUncapTickrate,
},
{
MENUITEMTYPE_CHECKBOX,
0,
MENUITEMFLAG_LITERAL_TEXT,
(uintptr_t)"Display FPS",
0,
menuhandlerDisplayFPS,
},
{
MENUITEMTYPE_SEPARATOR,
0,
0,
0,
0,
NULL,
},
{
MENUITEMTYPE_DROPDOWN,
@@ -867,9 +1102,17 @@ struct menuitem g_ExtendedVideoMenuItems[] = {
MENUITEMTYPE_CHECKBOX,
0,
MENUITEMFLAG_LITERAL_TEXT,
(uintptr_t)"GE64-style Muzzle Flashes",
(uintptr_t)"Detail Textures",
0,
menuhandlerGeMuzzleFlashes,
menuhandlerTexDetail,
},
{
MENUITEMTYPE_SEPARATOR,
0,
0,
0,
0,
NULL,
},
{
MENUITEMTYPE_DROPDOWN,
@@ -879,6 +1122,14 @@ struct menuitem g_ExtendedVideoMenuItems[] = {
0,
menuhandlerCenterHUD,
},
{
MENUITEMTYPE_CHECKBOX,
0,
MENUITEMFLAG_LITERAL_TEXT,
(uintptr_t)"GE64-style Muzzle Flashes",
0,
menuhandlerGeMuzzleFlashes,
},
{
MENUITEMTYPE_SLIDER,
0,
+254 -18
View File
@@ -6,6 +6,7 @@
#include <PR/gbi.h>
#include "platform.h"
#include "config.h"
#include "system.h"
#include "video.h"
#include "../fast3d/gfx_api.h"
@@ -35,19 +36,33 @@ static s32 vidFramebuffers = true;
static s32 vidFullscreen = DEFAULT_VID_FULLSCREEN;
static s32 vidFullscreenExclusive = DEFAULT_VID_FULLSCREEN_EXCLUSIVE;
static s32 vidMaximize = false;
static s32 vidCenter = false;
static s32 vidAllowHiDpi = false;
static s32 vidVsync = 1;
static s32 vidMSAA = 1;
static s32 vidFramerateLimit = 0;
static s32 vidDisplayFPS = 0;
static f32 vidDisplayFPSInterval = 1.f;
static f32 vidAvgFPS = 0;
static f64 vidLastRenderTime;
static s32 vidNumModes = 1;
static displaymode vidModeDefault;
static displaymode *vidModes = &vidModeDefault;
static s32 texFilter = FILTER_LINEAR;
static s32 texFilter2D = true;
static s32 texDetail = true;
static s32 texDetail = false;
static u32 dlcount = 0;
static u32 frames = 0;
static u32 framesPerSec = 0;
static f64 startTime, endTime, fpsTime;
static f64 startTime, endTime;
static f64 accumDelta = 0.0;
static f64 fpsTime = 0.0;
static s32 fpsNumFrames = 0;
static s32 videoInitDisplayModes(void);
s32 videoInit(void)
{
@@ -73,22 +88,17 @@ s32 videoInit(void)
.fullscreen = vidFullscreen,
.fullscreen_is_exclusive = vidFullscreenExclusive,
.maximized = vidMaximize,
.centered = vidCenter,
.allow_hidpi = vidAllowHiDpi
}
};
gfx_init(&set);
if (!wmAPI->set_swap_interval(vidVsync)) {
vidVsync = 0;
}
videoInitDisplayModes();
videoSetVsync(vidVsync);
videoSetFramerateLimit(vidFramerateLimit);
if (vidVsync == 0 && vidFramerateLimit == 0) {
// cap FPS if there's no vsync to prevent the game from exploding
vidFramerateLimit = VIDEO_MAX_FPS;
}
wmAPI->set_target_fps(vidFramerateLimit); // disabled because vsync is on
gfx_set_texture_filter((enum FilteringMode)texFilter);
initDone = true;
@@ -101,6 +111,10 @@ void videoStartFrame(void)
startTime = wmAPI->get_time();
gfx_start_frame();
}
// Synchronize with their backend counterparts.
vidFullscreen = videoGetFullscreen();
vidMaximize = videoGetMaximizeWindow();
}
void videoSubmitCommands(Gfx *cmds)
@@ -119,20 +133,35 @@ void videoEndFrame(void)
gfx_end_frame();
endTime = wmAPI->get_time();
++frames;
++framesPerSec;
++fpsNumFrames;
const f64 flipTime = wmAPI->get_time();
accumDelta += flipTime - endTime;
endTime = flipTime;
vidLastRenderTime = endTime - startTime;
if (endTime >= fpsTime) {
char tmp[128];
snprintf(tmp, sizeof(tmp), "fps %3u frt %lf frm %u", framesPerSec, endTime - startTime, frames);
vidAvgFPS = fpsNumFrames ? ((f64)fpsNumFrames / accumDelta) : 0.f;
fpsNumFrames = 0;
accumDelta = 0.0;
snprintf(tmp, sizeof(tmp), "fps %4.1f frt %lf frm %u", vidAvgFPS, vidLastRenderTime, frames);
wmAPI->set_window_title(tmp);
framesPerSec = 0;
fpsTime = endTime + 1.0;
fpsTime = endTime + vidDisplayFPSInterval;
}
}
f64 videoGetLastRenderTime(void)
{
return vidLastRenderTime;
}
f32 videoGetAverageFPS(void)
{
return vidAvgFPS;
}
void videoClearScreen(void)
{
videoStartFrame();
@@ -177,19 +206,159 @@ s32 videoGetHeight(void)
s32 videoGetFullscreen(void)
{
vidFullscreen = wmAPI->get_fullscreen_state();
return vidFullscreen;
}
s32 videoGetFullscreenMode(void)
{
vidFullscreenExclusive = wmAPI->get_fullscreen_flag_mode();
return vidFullscreenExclusive;
}
s32 videoGetMaximizeWindow(void)
{
vidMaximize = wmAPI->get_maximized_state();
return vidMaximize;
}
s32 videoGetCenterWindow(void)
{
return vidCenter;
}
f32 videoGetAspect(void)
{
return gfx_current_dimensions.aspect_ratio;
}
s32 videoGetDisplayModeIndex(void)
{
for (s32 i = 1; i < vidNumModes; ++i) {
if (vidModes[i].width == gfx_current_dimensions.width &&
vidModes[i].height == gfx_current_dimensions.height) {
return i;
}
}
// Current dimensions don't match any known mode, so return index 0, "Custom".
return 0;
}
s32 videoGetMSAA(void)
{
vidMSAA = (s32)gfx_msaa_level;
return vidMSAA;
}
s32 videoGetVsync(void)
{
vidVsync = wmAPI->get_swap_interval();
return vidVsync;
}
s32 videoGetFramerateLimit(void)
{
vidFramerateLimit = wmAPI->get_target_fps();
return vidFramerateLimit;
}
s32 videoGetDisplayFPS(void)
{
return vidDisplayFPS;
}
static s32 videoInitDisplayModes(void)
{
if (!wmAPI->get_current_display_mode(&vidModeDefault.width, &vidModeDefault.height)) {
vidModeDefault.width = 640;
vidModeDefault.height = 480;
return false;
}
const s32 numBaseModes = wmAPI->get_num_display_modes();
if (!numBaseModes) {
return false;
}
const s32 numCustomModes = 1;
displaymode *modeList = sysMemZeroAlloc((numBaseModes + numCustomModes) * sizeof(displaymode));
if (!modeList) {
return false;
}
modeList[0].width = 0;
modeList[0].height = 0;
s32 numModes = 1;
s32 w = -1, h = w, neww = w, newh = w;
// SDL modes are guaranteed to be sorted high to low
for (s32 i = 0; i < numBaseModes; ++i) {
wmAPI->get_display_mode(i, &neww, &newh);
if (neww != w || newh != h) {
w = neww;
h = newh;
modeList[numModes].width = w;
modeList[numModes].height = h;
++numModes;
}
}
modeList = sysMemRealloc(modeList, numModes * sizeof(displaymode));
if (!modeList) {
return false;
}
vidModes = modeList;
vidNumModes = numModes;
return true;
}
s32 videoGetDisplayMode(displaymode *out, const s32 index)
{
if (index >= 0 && index < vidNumModes) {
*out = vidModes[index];
return true;
}
return false;
}
s32 videoGetNumDisplayModes(void)
{
return vidNumModes;
}
void videoSetDisplayMode(const s32 index)
{
const displaymode dm = vidModes[index];
if (index == 0) {
// "Custom" video mode.
return;
}
vidWidth = dm.width;
vidHeight = dm.height;
s32 posX = 100;
s32 posY = 100;
if (vidCenter) {
wmAPI->get_centered_positions(vidWidth, vidHeight, &posX, &posY);
}
if (vidFullscreen) {
wmAPI->set_closest_resolution(vidWidth, vidHeight, vidCenter);
} else {
if (vidMaximize) {
videoSetMaximizeWindow(false);
} else {
wmAPI->set_dimensions(vidWidth, vidHeight, posX, posY);
}
}
}
s32 videoGetTextureFilter2D(void)
{
return texFilter2D;
@@ -215,7 +384,22 @@ void videoSetFullscreen(s32 fs)
{
if (fs != vidFullscreen) {
vidFullscreen = !!fs;
wmAPI->set_closest_resolution(vidWidth, vidHeight, vidCenter);
wmAPI->set_fullscreen(vidFullscreen);
if (!vidFullscreen && vidMaximize) {
wmAPI->set_maximize(false);
wmAPI->set_maximize(true);
}
}
}
void videoSetFullscreenMode(s32 mode)
{
vidFullscreenExclusive = mode;
wmAPI->set_fullscreen_flag(mode);
if (vidFullscreen) {
wmAPI->set_fullscreen(false);
wmAPI->set_fullscreen(true);
}
}
@@ -224,6 +408,23 @@ void videoSetMaximizeWindow(s32 fs)
if (fs != vidMaximize) {
vidMaximize = !!fs;
wmAPI->set_maximize(vidMaximize);
if (vidCenter && !vidMaximize) {
s32 posX = 0;
s32 posY = 0;
wmAPI->get_centered_positions(vidWidth, vidHeight, &posX, &posY);
wmAPI->set_dimensions(vidWidth, vidHeight, posX, posY);
}
}
}
void videoSetCenterWindow(s32 center)
{
vidCenter = center;
if (vidCenter && !vidMaximize) {
s32 posX = 0;
s32 posY = 0;
wmAPI->get_centered_positions(vidWidth, vidHeight, &posX, &posY);
wmAPI->set_dimensions(vidWidth, vidHeight, posX, posY);
}
}
@@ -259,6 +460,33 @@ s32 videoCreateFramebuffer(u32 w, u32 h, s32 upscale, s32 autoresize)
return gfx_create_framebuffer(w, h, upscale, autoresize);
}
void videoSetMSAA(const s32 msaa)
{
vidMSAA = msaa;
gfx_msaa_level = (u32)vidMSAA;
}
void videoSetVsync(const s32 vsync)
{
vidVsync = wmAPI->set_swap_interval(vsync) ? vsync : 0;
if (vidVsync == 0 && vidFramerateLimit == 0) {
// cap FPS if there's no vsync to prevent the game from exploding
videoSetFramerateLimit(VIDEO_MAX_FPS);
}
}
void videoSetFramerateLimit(const s32 limit)
{
vidFramerateLimit = (vidVsync == 0 && limit == 0) ? VIDEO_MAX_FPS : limit;
wmAPI->set_target_fps(vidFramerateLimit);
}
void videoSetDisplayFPS(const s32 displayfps)
{
vidDisplayFPS = displayfps;
}
void videoSetFramebuffer(s32 target)
{
return gfx_set_framebuffer(target, 1.f);
@@ -295,6 +523,11 @@ void videoFreeCachedTexture(const void *texptr)
gfx_texture_cache_delete(texptr);
}
void videoShutdown(void)
{
free(vidModes);
}
PD_CONSTRUCTOR static void videoConfigInit(void)
{
configRegisterInt("Video.DefaultFullscreen", &vidFullscreen, 0, 1);
@@ -302,10 +535,13 @@ PD_CONSTRUCTOR static void videoConfigInit(void)
configRegisterInt("Video.DefaultWidth", &vidWidth, 0, 32767);
configRegisterInt("Video.DefaultHeight", &vidHeight, 0, 32767);
configRegisterInt("Video.ExclusiveFullscreen", &vidFullscreenExclusive, 0, 1);
configRegisterInt("Video.CenterWindow", &vidCenter, 0, 1);
configRegisterInt("Video.AllowHiDpi", &vidAllowHiDpi, 0, 1);
configRegisterInt("Video.VSync", &vidVsync, -1, 10);
configRegisterInt("Video.FramebufferEffects", &vidFramebuffers, 0, 1);
configRegisterInt("Video.FramerateLimit", &vidFramerateLimit, 0, VIDEO_MAX_FPS);
configRegisterInt("Video.DisplayFPS", &vidDisplayFPS, 0, 1);
configRegisterFloat("Video.DisplayFPSInterval", &vidDisplayFPSInterval, 0.01f, 32.f);
configRegisterInt("Video.MSAA", &vidMSAA, 1, 16);
configRegisterInt("Video.TextureFilter", &texFilter, 0, 2);
configRegisterInt("Video.TextureFilter2D", &texFilter2D, 0, 1);
+46
View File
@@ -99,6 +99,7 @@
#ifndef PLATFORM_N64
#include "net/net.h"
#include "net/netmsg.h"
#include "video.h"
#endif
struct sndstate *g_MiscSfxAudioHandles[3];
@@ -971,6 +972,45 @@ void lvFindThreats(void)
}
}
#ifndef PLATFORM_N64
Gfx *lvRenderFPS(Gfx *gdl)
{
const f32 fps = videoGetAverageFPS();
const u8 a = 160;
s32 x = 27, y = 13;
u32 color;
char buffer[16];
if (fps <= 30.f) {
// red -> yellow
color = 0xff000000 | a | ((u32)((fps / 30.f) * 255.f) << 16);
} else if (fps <= 60.f) {
// yellow -> green
color = 0x00ff0000 | a | ((u32)((1.f - (fps - 30.f) / 30.f) * 255.f) << 24);
} else if (fps <= 90.f) {
// green -> cyan
color = 0x00ff0000 | a | ((u32)(((fps - 60.f) / 30.f) * 255.f) << 8);
} else {
// cyan
color = 0x00ffff00 | a;
}
if (g_CharsNumeric && g_FontNumeric) {
snprintf(buffer, sizeof buffer, "%.2f", fps);
gSPSetExtraGeometryModeEXT(gdl++, g_HudAlignModeL);
gdl = text0f153628(gdl);
gdl = textRender(gdl, &x, &y, buffer, g_CharsNumeric, g_FontNumeric, color, 0x000000a0, viGetWidth(), viGetHeight(), 0, 0);
gdl = text0f153780(gdl);
gSPClearExtraGeometryModeEXT(gdl++, g_HudAlignModeL);
}
return gdl;
}
#endif
/**
* Renders a complete frame for all players, and also does some other game logic
* that really doesn't belong here.
@@ -1781,6 +1821,12 @@ Gfx *lvRender(Gfx *gdl)
gDPSetScissor(gdl++, G_SC_NON_INTERLACE, 0, 0, viGetWidth(), viGetHeight());
#ifndef PLATFORM_N64
if (videoGetDisplayFPS()) {
gdl = lvRenderFPS(gdl);
}
#endif
#if VERSION < VERSION_NTSC_1_0
if ((uintptr_t)gdl < (uintptr_t)g_GfxBuffers[g_GfxActiveBufferIndex]
|| (uintptr_t)gdl > (uintptr_t)g_GfxBuffers[g_GfxActiveBufferIndex + 1]) {
+44 -11
View File
@@ -51,6 +51,8 @@ u8 g_KeyboardKeys[5][10] = {
{ '1','2','1','2','1','2','3','1','2','3' },
};
static s32 deferredindex = -1;
s32 func0f0e5ce0(s32 value)
{
if (value < var800711a4) {
@@ -2269,7 +2271,7 @@ Gfx* menuitemColorBoxRender(Gfx *gdl, struct menurendercontext *context)
return gdl;
}
#endif
#endif
Gfx *menuitemSelectableRender(Gfx *gdl, struct menurendercontext *context)
{
@@ -2430,8 +2432,15 @@ Gfx *menuitemSliderRender(Gfx *gdl, struct menurendercontext *context)
extray = 0;
if (context->item->handler != NULL) {
context->item->handler(MENUOP_GETSLIDER, context->item, &data);
slidervalue = (s16) data.slider.value;
if ((context->item->flags & MENUITEMFLAG_SLIDER_DEFERRED) &&
deferredindex != -1 &&
context->dialog->dimmed &&
context->focused) {
slidervalue = (s16) deferredindex;
} else {
context->item->handler(MENUOP_GETSLIDER, context->item, &data);
slidervalue = (s16) data.slider.value;
}
} else {
slidervalue = 0;
}
@@ -2570,9 +2579,14 @@ bool menuitemSliderTick(struct menuitem *item, struct menudialog *dialog, struct
index = item->param3;
}
if (item->handler) {
item->handler(MENUOP_GET, item, &handlerdata);
handlerdata.slider.value = index;
item->handler(MENUOP_SET, item, &handlerdata);
if ((item->flags & MENUITEMFLAG_SLIDER_DEFERRED) &&
(tickflags & MENUTICKFLAG_DIALOGISDIMMED)) {
deferredindex = index;
} else {
item->handler(MENUOP_GET, item, &handlerdata);
handlerdata.slider.value = index;
item->handler(MENUOP_SET, item, &handlerdata);
}
}
return true;
}
@@ -2582,8 +2596,17 @@ bool menuitemSliderTick(struct menuitem *item, struct menudialog *dialog, struct
if (tickflags & MENUTICKFLAG_DIALOGISDIMMED) {
if (item->handler) {
item->handler(MENUOP_GETSLIDER, item, &handlerdata);
index = (s16) handlerdata.slider.value;
if (item->flags & MENUITEMFLAG_SLIDER_DEFERRED) {
if (deferredindex == -1) {
item->handler(MENUOP_GETSLIDER, item, &handlerdata);
deferredindex = (s16) handlerdata.slider.value;
}
index = deferredindex;
} else {
item->handler(MENUOP_GETSLIDER, item, &handlerdata);
index = (s16) handlerdata.slider.value;
}
} else {
index = 0;
}
@@ -2663,11 +2686,21 @@ bool menuitemSliderTick(struct menuitem *item, struct menudialog *dialog, struct
inputs->leftright = 0;
handlerdata.slider.value = index;
if (item->handler) {
item->handler(MENUOP_SET, item, &handlerdata);
if (item->flags & MENUITEMFLAG_SLIDER_DEFERRED) {
deferredindex = index;
} else {
if (item->handler) {
item->handler(MENUOP_SET, item, &handlerdata);
}
}
if (inputs->select) {
if (item->flags & MENUITEMFLAG_SLIDER_DEFERRED) {
deferredindex = -1;
if (item->handler) {
item->handler(MENUOP_SET, item, &handlerdata);
}
}
dialog->dimmed = false;
}
} else {
@@ -2707,7 +2740,7 @@ Gfx *menuitemCarouselRender(Gfx *gdl, struct menurendercontext *context)
#ifdef PLATFORM_N64
s16 chevronOffset = 0;
#else
#else
s16 chevronOffset = 3;
#endif
+2 -1
View File
@@ -1690,6 +1690,7 @@
#define MENUITEMFLAG_CAROUSEL_04000000 0x04000000
#define MENUITEMFLAG_LITERAL_TEXT 0x08000000
#define MENUITEMFLAG_SLIDER_WIDE 0x10000000
#define MENUITEMFLAG_SLIDER_DEFERRED 0x20000000
#define MENUITEMTYPE_LABEL 0x01
#define MENUITEMTYPE_LIST 0x02
@@ -1720,7 +1721,7 @@
#ifndef PLATFORM_N64
#define MENUITEMTYPE_COLORBOX 0x1b
#endif
#endif
#define MENUMODELFLAG_HASSCALE 0x01
#define MENUMODELFLAG_HASPOSITION 0x02