mirror of
https://github.com/coop-deluxe/sm64coopdx.git
synced 2025-01-06 23:51:17 +00:00
WIP: uncapped framerate 9
This commit is contained in:
parent
58cee9098b
commit
582cae97ed
13 changed files with 215 additions and 175 deletions
|
@ -124,7 +124,7 @@ struct GraphNodePerspective
|
|||
struct DisplayListNode
|
||||
{
|
||||
Mtx *transform;
|
||||
void *transformPrev;
|
||||
Mtx *transformPrev;
|
||||
void *displayList;
|
||||
struct DisplayListNode *next;
|
||||
};
|
||||
|
|
|
@ -162,9 +162,9 @@ static u8 sShadowInterpCount = 0;
|
|||
|
||||
struct {
|
||||
Gfx *pos;
|
||||
void *mtx;
|
||||
Mtx *mtx;
|
||||
Mtx *mtxPrev;
|
||||
void *displayList;
|
||||
void *mtxPrev;
|
||||
Mtx interp;
|
||||
} gMtxTbl[6400];
|
||||
s32 gMtxTblSize;
|
||||
|
@ -300,11 +300,13 @@ static void geo_process_master_list_sub(struct GraphNodeMasterList *node) {
|
|||
if ((currList = node->listHeads[i]) != NULL) {
|
||||
gDPSetRenderMode(gDisplayListHead++, modeList->modes[i], mode2List->modes[i]);
|
||||
while (currList != NULL) {
|
||||
detect_and_skip_mtx_interpolation(&currList->transform, &currList->transformPrev);
|
||||
if ((u32) gMtxTblSize < sizeof(gMtxTbl) / sizeof(gMtxTbl[0])) {
|
||||
gMtxTbl[gMtxTblSize].pos = gDisplayListHead;
|
||||
gMtxTbl[gMtxTblSize].mtx = currList->transform;
|
||||
gMtxTbl[gMtxTblSize].mtxPrev = currList->transformPrev;
|
||||
gMtxTbl[gMtxTblSize++].displayList = currList->displayList;
|
||||
gMtxTbl[gMtxTblSize].displayList = currList->displayList;
|
||||
gMtxTblSize++;
|
||||
}
|
||||
gSPMatrix(gDisplayListHead++, VIRTUAL_TO_PHYSICAL(currList->transformPrev),
|
||||
G_MTX_MODELVIEW | G_MTX_LOAD | G_MTX_NOPUSH);
|
||||
|
|
|
@ -131,7 +131,8 @@ unsigned int configNetworkSystem = 0;
|
|||
char configPlayerName[MAX_PLAYER_STRING] = "";
|
||||
unsigned int configPlayerModel = 0;
|
||||
unsigned int configPlayerPalette = 0;
|
||||
unsigned int config60Fps = 1;
|
||||
bool configUncappedFramerate = true;
|
||||
unsigned int configFrameLimit = 60;
|
||||
unsigned int configDrawDistance = 5;
|
||||
bool configDisablePopups = 0;
|
||||
bool configDisableDownloadedModels = 0;
|
||||
|
@ -196,9 +197,10 @@ static const struct ConfigOption options[] = {
|
|||
{.name = "debug_offset", .type = CONFIG_TYPE_U64 , .u64Value = &gPcDebug.bhvOffset},
|
||||
{.name = "debug_tags", .type = CONFIG_TYPE_U64 , .u64Value = gPcDebug.tags},
|
||||
// coop-specific
|
||||
{.name = "uncapped_framerate", .type = CONFIG_TYPE_BOOL , .boolValue = &configUncappedFramerate},
|
||||
{.name = "frame_limit" , .type = CONFIG_TYPE_UINT , .uintValue = &configFrameLimit},
|
||||
{.name = "amount_of_players", .type = CONFIG_TYPE_UINT , .uintValue = &configAmountofPlayers},
|
||||
{.name = "bubble_death", .type = CONFIG_TYPE_BOOL , .boolValue = &configBubbleDeath},
|
||||
{.name = "coop_60fps", .type = CONFIG_TYPE_UINT , .uintValue = &config60Fps},
|
||||
{.name = "coop_draw_distance", .type = CONFIG_TYPE_UINT , .uintValue = &configDrawDistance},
|
||||
{.name = "coop_host_port", .type = CONFIG_TYPE_UINT , .uintValue = &configHostPort},
|
||||
{.name = "coop_host_save_slot", .type = CONFIG_TYPE_UINT , .uintValue = &configHostSaveSlot},
|
||||
|
|
|
@ -87,7 +87,8 @@ extern unsigned int configNetworkSystem;
|
|||
extern char configPlayerName[];
|
||||
extern unsigned int configPlayerModel;
|
||||
extern unsigned int configPlayerPalette;
|
||||
extern unsigned int config60Fps;
|
||||
extern bool configUncappedFramerate;
|
||||
extern unsigned int configFrameLimit;
|
||||
extern unsigned int configDrawDistance;
|
||||
extern bool configDisablePopups;
|
||||
extern bool configDisableDownloadedModels;
|
||||
|
|
|
@ -22,10 +22,27 @@
|
|||
// The full height for the body.
|
||||
#define BODY_HEIGHT CHECKBOXES_FULL_SIZE + SELECTION_BOXES_FULL_SIZE + BUTTON_SIZES
|
||||
|
||||
static struct DjuiInputbox* sFrameLimitInput = NULL;
|
||||
|
||||
static void djui_panel_display_apply(UNUSED struct DjuiBase* caller) {
|
||||
configWindow.settings_changed = true;
|
||||
}
|
||||
|
||||
static void djui_panel_display_uncapped_change(UNUSED struct DjuiBase* caller) {
|
||||
djui_base_set_enabled(&sFrameLimitInput->base, !configUncappedFramerate);
|
||||
}
|
||||
|
||||
static void djui_panel_display_frame_limit_text_change(struct DjuiBase* caller) {
|
||||
struct DjuiInputbox* inputbox1 = (struct DjuiInputbox*)caller;
|
||||
s32 frameLimit = atoi(inputbox1->buffer);
|
||||
if (frameLimit >= 30 && frameLimit <= 3000) {
|
||||
djui_inputbox_set_text_color(inputbox1, 0, 0, 0, 255);
|
||||
configFrameLimit = frameLimit;
|
||||
} else {
|
||||
djui_inputbox_set_text_color(inputbox1, 255, 0, 0, 255);
|
||||
}
|
||||
}
|
||||
|
||||
void djui_panel_display_create(struct DjuiBase* caller) {
|
||||
f32 bodyHeight = BODY_HEIGHT;
|
||||
|
||||
|
@ -40,39 +57,60 @@ void djui_panel_display_create(struct DjuiBase* caller) {
|
|||
djui_interactable_hook_value_change(&checkbox1->base, djui_panel_display_apply);
|
||||
defaultBase = &checkbox1->base;
|
||||
|
||||
struct DjuiCheckbox* checkbox5 = djui_checkbox_create(&body->base, "Disable Popups", &configDisablePopups);
|
||||
djui_base_set_size_type(&checkbox5->base, DJUI_SVT_RELATIVE, DJUI_SVT_ABSOLUTE);
|
||||
djui_base_set_size(&checkbox5->base, 1.0f, 32);
|
||||
|
||||
struct DjuiCheckbox* checkbox6 = djui_checkbox_create(&body->base, "Disable Downloaded Models", &configDisableDownloadedModels);
|
||||
djui_base_set_size_type(&checkbox6->base, DJUI_SVT_RELATIVE, DJUI_SVT_ABSOLUTE);
|
||||
djui_base_set_size(&checkbox6->base, 1.0f, 32);
|
||||
|
||||
#ifdef EXTERNAL_DATA
|
||||
struct DjuiCheckbox* checkbox7 = djui_checkbox_create(&body->base, "Preload Textures", &configPrecacheRes);
|
||||
djui_base_set_size_type(&checkbox7->base, DJUI_SVT_RELATIVE, DJUI_SVT_ABSOLUTE);
|
||||
djui_base_set_size(&checkbox7->base, 1.0f, 32);
|
||||
#endif
|
||||
|
||||
struct DjuiCheckbox* checkbox2 = djui_checkbox_create(&body->base, "VSync", &configWindow.vsync);
|
||||
djui_base_set_size_type(&checkbox2->base, DJUI_SVT_RELATIVE, DJUI_SVT_ABSOLUTE);
|
||||
djui_base_set_size(&checkbox2->base, 1.0f, 32);
|
||||
djui_interactable_hook_value_change(&checkbox2->base, djui_panel_display_apply);
|
||||
|
||||
struct DjuiCheckbox* checkbox3 = djui_checkbox_create(&body->base, "HUD", &configHUD);
|
||||
struct DjuiCheckbox* checkbox3 = djui_checkbox_create(&body->base, "Uncapped Framerate", &configUncappedFramerate);
|
||||
djui_base_set_size_type(&checkbox3->base, DJUI_SVT_RELATIVE, DJUI_SVT_ABSOLUTE);
|
||||
djui_base_set_size(&checkbox3->base, 1.0f, 32);
|
||||
djui_interactable_hook_value_change(&checkbox3->base, djui_panel_display_uncapped_change);
|
||||
|
||||
struct DjuiCheckbox* checkbox4 = djui_checkbox_create(&body->base, "Disable Popups", &configDisablePopups);
|
||||
djui_base_set_size_type(&checkbox4->base, DJUI_SVT_RELATIVE, DJUI_SVT_ABSOLUTE);
|
||||
djui_base_set_size(&checkbox4->base, 1.0f, 32);
|
||||
struct DjuiRect* rect1 = djui_rect_create(&body->base);
|
||||
djui_base_set_size_type(&rect1->base, DJUI_SVT_RELATIVE, DJUI_SVT_ABSOLUTE);
|
||||
djui_base_set_size(&rect1->base, 1.0f, 32);
|
||||
djui_base_set_color(&rect1->base, 0, 0, 0, 0);
|
||||
{
|
||||
if (configFrameLimit < 30) { configFrameLimit = 30; }
|
||||
if (configFrameLimit > 3000) { configFrameLimit = 3000; }
|
||||
struct DjuiText* text1 = djui_text_create(&rect1->base, "Frame Limit");
|
||||
djui_base_set_size_type(&text1->base, DJUI_SVT_RELATIVE, DJUI_SVT_ABSOLUTE);
|
||||
djui_base_set_color(&text1->base, 200, 200, 200, 255);
|
||||
djui_base_set_size(&text1->base, 0.485f, 64);
|
||||
djui_base_set_alignment(&text1->base, DJUI_HALIGN_LEFT, DJUI_VALIGN_TOP);
|
||||
|
||||
struct DjuiCheckbox* checkbox5 = djui_checkbox_create(&body->base, "Disable Downloaded Models", &configDisableDownloadedModels);
|
||||
djui_base_set_size_type(&checkbox5->base, DJUI_SVT_RELATIVE, DJUI_SVT_ABSOLUTE);
|
||||
djui_base_set_size(&checkbox5->base, 1.0f, 32);
|
||||
|
||||
#ifdef EXTERNAL_DATA
|
||||
struct DjuiCheckbox* checkbox6 = djui_checkbox_create(&body->base, "Preload Textures", &configPrecacheRes);
|
||||
djui_base_set_size_type(&checkbox6->base, DJUI_SVT_RELATIVE, DJUI_SVT_ABSOLUTE);
|
||||
djui_base_set_size(&checkbox6->base, 1.0f, 32);
|
||||
#endif
|
||||
struct DjuiInputbox* inputbox1 = djui_inputbox_create(&rect1->base, 32);
|
||||
djui_base_set_size_type(&inputbox1->base, DJUI_SVT_RELATIVE, DJUI_SVT_ABSOLUTE);
|
||||
djui_base_set_size(&inputbox1->base, 0.5f, 32);
|
||||
djui_base_set_alignment(&inputbox1->base, DJUI_HALIGN_RIGHT, DJUI_VALIGN_TOP);
|
||||
char frameLimitString[32] = { 0 };
|
||||
snprintf(frameLimitString, 32, "%d", configFrameLimit);
|
||||
djui_inputbox_set_text(inputbox1, frameLimitString);
|
||||
djui_interactable_hook_value_change(&inputbox1->base, djui_panel_display_frame_limit_text_change);
|
||||
djui_base_set_enabled(&inputbox1->base, !configUncappedFramerate);
|
||||
sFrameLimitInput = inputbox1;
|
||||
}
|
||||
|
||||
char* filterChoices[3] = { "Nearest", "Linear", "Tripoint" };
|
||||
struct DjuiSelectionbox* selectionbox1 = djui_selectionbox_create(&body->base, "Filtering", filterChoices, 3, &configFiltering);
|
||||
djui_base_set_size_type(&selectionbox1->base, DJUI_SVT_RELATIVE, DJUI_SVT_ABSOLUTE);
|
||||
djui_base_set_size(&selectionbox1->base, 1.0f, 32);
|
||||
|
||||
char* fpsChoices[3] = { "30", "60" };
|
||||
struct DjuiSelectionbox* selectionbox2 = djui_selectionbox_create(&body->base, "FPS", fpsChoices, 2, &config60Fps);
|
||||
djui_base_set_size_type(&selectionbox2->base, DJUI_SVT_RELATIVE, DJUI_SVT_ABSOLUTE);
|
||||
djui_base_set_size(&selectionbox2->base, 1.0f, 32);
|
||||
|
||||
char* drawDistanceChoices[6] = { "0.5x", "1x", "1.5x", "3x", "10x", "100x" };
|
||||
struct DjuiSelectionbox* selectionbox3 = djui_selectionbox_create(&body->base, "Draw Distance", drawDistanceChoices, 6, &configDrawDistance);
|
||||
djui_base_set_size_type(&selectionbox3->base, DJUI_SVT_RELATIVE, DJUI_SVT_ABSOLUTE);
|
||||
|
|
|
@ -76,20 +76,11 @@ static bool gfx_dummy_wm_start_frame(void) {
|
|||
return true;
|
||||
}
|
||||
|
||||
static inline void sync_framerate_with_timer(void) {
|
||||
f64 curTime = clock_elapsed_f64();
|
||||
if (curTime < sFrameTargetTime) {
|
||||
u32 delayMs = (sFrameTargetTime - curTime) * 1000.0;
|
||||
if (delayMs > 0) {
|
||||
sleep_ms(delayMs);
|
||||
}
|
||||
}
|
||||
f64 frameTime = config60Fps ? (sFrameTime / 2.0) : sFrameTime;
|
||||
sFrameTargetTime += frameTime;
|
||||
static void gfx_dummy_wm_delay(u32 ms) {
|
||||
sleep_ms(ms);
|
||||
}
|
||||
|
||||
static void gfx_dummy_wm_swap_buffers_begin(void) {
|
||||
sync_framerate_with_timer();
|
||||
}
|
||||
|
||||
static void gfx_dummy_wm_swap_buffers_end(void) {
|
||||
|
@ -209,7 +200,8 @@ struct GfxWindowManagerAPI gfx_dummy_wm_api = {
|
|||
gfx_dummy_wm_stop_text_input,
|
||||
gfx_dummy_wm_get_clipboard_text,
|
||||
gfx_dummy_wm_set_clipboard_text,
|
||||
gfx_dummy_wm_set_cursor_visible
|
||||
gfx_dummy_wm_set_cursor_visible,
|
||||
gfx_dummy_wm_delay
|
||||
};
|
||||
|
||||
struct GfxRenderingAPI gfx_dummy_renderer_api = {
|
||||
|
|
|
@ -522,35 +522,12 @@ static bool gfx_dxgi_start_frame(void) {
|
|||
}*/
|
||||
|
||||
dxgi.length_in_vsync_frames = configWindow.vsync;
|
||||
f64 curTime = clock_elapsed_f64();
|
||||
f64 frameTime = config60Fps ? (sFrameTime / 2.0) : sFrameTime;
|
||||
if (curTime > sFrameTargetTime) {
|
||||
sFrameTargetTime += frameTime;
|
||||
if (curTime > sFrameTargetTime + frameTime * 3) {
|
||||
sFrameTargetTime = curTime;
|
||||
}
|
||||
dxgi.dropped_frame = true;
|
||||
return false;
|
||||
}
|
||||
dxgi.dropped_frame = false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static inline void sync_framerate_with_timer(void) {
|
||||
f64 curTime = clock_elapsed_f64();
|
||||
if (curTime < sFrameTargetTime) {
|
||||
u32 delayMs = (sFrameTargetTime - curTime) * 1000.0;
|
||||
if (delayMs > 0) {
|
||||
Sleep(delayMs);
|
||||
}
|
||||
}
|
||||
f64 frameTime = config60Fps ? (sFrameTime / 2.0) : sFrameTime;
|
||||
sFrameTargetTime += frameTime;
|
||||
}
|
||||
|
||||
static void gfx_dxgi_swap_buffers_begin(void) {
|
||||
sync_framerate_with_timer();
|
||||
ThrowIfFailed(dxgi.swap_chain->Present(dxgi.length_in_vsync_frames, 0));
|
||||
UINT this_present_id;
|
||||
if (dxgi.swap_chain->GetLastPresentCount(&this_present_id) == S_OK) {
|
||||
|
@ -654,6 +631,10 @@ ComPtr<IDXGISwapChain1> gfx_dxgi_create_swap_chain(IUnknown *device) {
|
|||
return dxgi.swap_chain;
|
||||
}
|
||||
|
||||
void gfx_dxgi_delay(u32 ms) {
|
||||
Sleep(ms);
|
||||
}
|
||||
|
||||
HWND gfx_dxgi_get_h_wnd(void) {
|
||||
return dxgi.h_wnd;
|
||||
}
|
||||
|
@ -722,6 +703,7 @@ struct GfxWindowManagerAPI gfx_dxgi = {
|
|||
gfx_dxgi_get_clipboard_text,
|
||||
gfx_dxgi_set_clipboard_text,
|
||||
gfx_dxgi_set_cursor_visible,
|
||||
gfx_dxgi_delay,
|
||||
};
|
||||
|
||||
#endif
|
||||
|
|
|
@ -168,18 +168,7 @@ static bool gfx_sdl_start_frame(void) {
|
|||
return true;
|
||||
}
|
||||
|
||||
static inline void sync_framerate_with_timer(void) {
|
||||
static Uint32 last_time = 0;
|
||||
// get base timestamp on the first frame (might be different from 0)
|
||||
if (last_time == 0) last_time = SDL_GetTicks();
|
||||
const int elapsed = SDL_GetTicks() - last_time;
|
||||
if (elapsed < frame_time)
|
||||
SDL_Delay(frame_time - elapsed);
|
||||
last_time += frame_time;
|
||||
}
|
||||
|
||||
static void gfx_sdl_swap_buffers_begin(void) {
|
||||
sync_framerate_with_timer();
|
||||
SDL_GL_SwapBuffers();
|
||||
}
|
||||
|
||||
|
@ -190,6 +179,9 @@ static double gfx_sdl_get_time(void) {
|
|||
return 0.0;
|
||||
}
|
||||
|
||||
static void gfx_sdl_delay(u32 ms) {
|
||||
SDL_Delay(ms);
|
||||
}
|
||||
|
||||
static void gfx_sdl_shutdown(void) {
|
||||
if (SDL_WasInit(0))
|
||||
|
@ -218,6 +210,7 @@ struct GfxWindowManagerAPI gfx_sdl = {
|
|||
gfx_sdl_get_clipboard_text,
|
||||
gfx_sdl_set_clipboard_text,
|
||||
gfx_sdl_set_cursor_visible,
|
||||
gfx_sdl_delay,
|
||||
};
|
||||
|
||||
#endif // BACKEND_WM
|
||||
|
|
|
@ -58,37 +58,6 @@ static void (*kb_text_input)(char*) = NULL;
|
|||
|
||||
#define IS_FULLSCREEN() ((SDL_GetWindowFlags(wnd) & SDL_WINDOW_FULLSCREEN_DESKTOP) != 0)
|
||||
|
||||
int test_vsync(void) {
|
||||
// Even if SDL_GL_SetSwapInterval succeeds, it doesn't mean that VSync actually works.
|
||||
// A 60 Hz monitor should have a swap interval of 16.67 milliseconds.
|
||||
// Try to detect the length of a vsync by swapping buffers some times.
|
||||
// Since the graphics card may enqueue a fixed number of frames,
|
||||
// first send in four dummy frames to hopefully fill the queue.
|
||||
// This method will fail if the refresh rate is changed, which, in
|
||||
// combination with that we can't control the queue size (i.e. lag)
|
||||
// is a reason this generic SDL2 backend should only be used as last resort.
|
||||
|
||||
for (int32_t i = 0; i < 8; ++i)
|
||||
SDL_GL_SwapWindow(wnd);
|
||||
|
||||
Uint32 start = SDL_GetTicks();
|
||||
SDL_GL_SwapWindow(wnd);
|
||||
SDL_GL_SwapWindow(wnd);
|
||||
SDL_GL_SwapWindow(wnd);
|
||||
SDL_GL_SwapWindow(wnd);
|
||||
Uint32 end = SDL_GetTicks();
|
||||
|
||||
const float average = 4.0 * 1000.0 / (end - start);
|
||||
|
||||
if (average > 27.0f && average < 33.0f) return 1;
|
||||
if (average > 57.0f && average < 63.0f) return 2;
|
||||
if (average > 86.0f && average < 94.0f) return 3;
|
||||
if (average > 115.0f && average < 125.0f) return 4;
|
||||
if (average > 234.0f && average < 246.0f) return 8;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline void gfx_sdl_set_vsync(const bool enabled) {
|
||||
SDL_GL_SetSwapInterval(enabled);
|
||||
}
|
||||
|
@ -248,33 +217,9 @@ static void gfx_sdl_set_keyboard_callbacks(kb_callback_t on_key_down, kb_callbac
|
|||
|
||||
static bool gfx_sdl_start_frame(void) {
|
||||
return true;
|
||||
f64 curTime = clock_elapsed_f64();
|
||||
f64 frameTime = config60Fps ? (sFrameTime / 2.0) : sFrameTime;
|
||||
if (curTime > sFrameTargetTime) {
|
||||
sFrameTargetTime += frameTime;
|
||||
if (curTime > sFrameTargetTime + frameTime * 3) {
|
||||
sFrameTargetTime = curTime;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
static inline void sync_framerate_with_timer(void) {
|
||||
return;
|
||||
f64 curTime = clock_elapsed_f64();
|
||||
if (curTime < sFrameTargetTime) {
|
||||
u32 delayMs = (sFrameTargetTime - curTime) * 1000.0;
|
||||
if (delayMs > 0) {
|
||||
SDL_Delay(delayMs);
|
||||
}
|
||||
}
|
||||
f64 frameTime = config60Fps ? (sFrameTime / 2.0) : sFrameTime;
|
||||
sFrameTargetTime += frameTime;
|
||||
}
|
||||
|
||||
static void gfx_sdl_swap_buffers_begin(void) {
|
||||
sync_framerate_with_timer();
|
||||
SDL_GL_SwapWindow(wnd);
|
||||
}
|
||||
|
||||
|
@ -285,6 +230,9 @@ static double gfx_sdl_get_time(void) {
|
|||
return 0.0;
|
||||
}
|
||||
|
||||
static void gfx_sdl_delay(u32 ms) {
|
||||
SDL_Delay(ms);
|
||||
}
|
||||
|
||||
static void gfx_sdl_shutdown(void) {
|
||||
if (SDL_WasInit(0)) {
|
||||
|
@ -316,6 +264,7 @@ struct GfxWindowManagerAPI gfx_sdl = {
|
|||
gfx_sdl_get_clipboard_text,
|
||||
gfx_sdl_set_clipboard_text,
|
||||
gfx_sdl_set_cursor_visible,
|
||||
gfx_sdl_delay,
|
||||
};
|
||||
|
||||
#endif // BACKEND_WM
|
||||
|
|
|
@ -25,6 +25,7 @@ struct GfxWindowManagerAPI {
|
|||
char* (*get_clipboard_text)(void);
|
||||
void (*set_clipboard_text)(char*);
|
||||
void (*set_cursor_visible)(bool);
|
||||
void (*delay)(unsigned int ms);
|
||||
};
|
||||
|
||||
#endif
|
||||
|
|
|
@ -69,6 +69,14 @@ f32 gRenderingDelta = 0;
|
|||
|
||||
f32 gGameSpeed = 1.0f; // DO NOT COMMIT
|
||||
|
||||
#define FRAMERATE 30
|
||||
static const f64 sFrameTime = (1.0 / ((double)FRAMERATE));
|
||||
static f64 sFrameTargetTime = 0;
|
||||
static f64 sFrameTimeStart;
|
||||
static f64 sLastFrameTimeStart;
|
||||
static f32 sAvgFrames = 1;
|
||||
static f32 sAvgFps = 0;
|
||||
|
||||
static struct AudioAPI *audio_api;
|
||||
struct GfxWindowManagerAPI *wm_api;
|
||||
static struct GfxRenderingAPI *rendering_api;
|
||||
|
@ -142,37 +150,59 @@ static inline void patch_interpolations(f32 delta) {
|
|||
patch_djui_interpolated(delta);
|
||||
}
|
||||
|
||||
void produce_uncapped_frames(void) {
|
||||
#define FRAMERATE 30
|
||||
static const f64 sFrameTime = (1.0 / ((double)FRAMERATE));
|
||||
static f64 sFrameTargetTime = 0;
|
||||
static void delay_frame(void) {
|
||||
if (configUncappedFramerate) { return; }
|
||||
static f32 sDelayRounding = 0;
|
||||
f64 targetDelta = 1.0 / (f64)configFrameLimit;
|
||||
|
||||
f64 actualDelta = clock_elapsed_f64() - sLastFrameTimeStart;
|
||||
if (actualDelta < targetDelta) {
|
||||
f64 delay = sDelayRounding + ((targetDelta - actualDelta) * 1000.0);
|
||||
sDelayRounding = delay - (u32)delay;
|
||||
wm_api->delay((u32)delay);
|
||||
}
|
||||
}
|
||||
|
||||
void produce_interpolation_frames_and_delay(void) {
|
||||
gRenderingInterpolated = true;
|
||||
|
||||
f64 startTime = clock_elapsed_f64();
|
||||
f64 curTime = startTime;
|
||||
u64 frames = 0;
|
||||
// sanity check target time to deal with hangs and such
|
||||
f64 curTime = clock_elapsed_f64();
|
||||
if (fabs(sFrameTargetTime - curTime) > 1) {
|
||||
sFrameTargetTime = curTime;
|
||||
}
|
||||
delay_frame();
|
||||
|
||||
u64 frames = 1;
|
||||
while ((curTime = clock_elapsed_f64()) < sFrameTargetTime) {
|
||||
sLastFrameTimeStart = curTime;
|
||||
gfx_start_frame();
|
||||
f32 delta = MIN((curTime - startTime) / (sFrameTargetTime - startTime), 1);
|
||||
f32 delta = (configWindow.vsync || !configUncappedFramerate)
|
||||
? frames / sAvgFrames
|
||||
: MIN((curTime - sFrameTimeStart) / (sFrameTargetTime - sFrameTimeStart), 1);
|
||||
gRenderingDelta = delta;
|
||||
patch_interpolations(delta);
|
||||
send_display_list(gGfxSPTask);
|
||||
gfx_end_frame();
|
||||
frames++;
|
||||
delay_frame();
|
||||
}
|
||||
|
||||
f32 fps = frames / (clock_elapsed_f64() - sFrameTimeStart);
|
||||
sAvgFps = sAvgFps * 0.99 + fps * 0.01;
|
||||
sAvgFrames = sAvgFrames * 0.9 + frames * 0.1;
|
||||
sFrameTargetTime += sFrameTime * gGameSpeed;
|
||||
gRenderingInterpolated = false;
|
||||
|
||||
printf(">> frames %llu | %f\n", frames, gGameSpeed);
|
||||
fflush(stdout);
|
||||
|
||||
sFrameTargetTime += sFrameTime * gGameSpeed;
|
||||
//printf(">>> fpt: %llu, fps: %f :: %f\n", frames, sAvgFps, fps);
|
||||
}
|
||||
|
||||
void produce_one_frame(void) {
|
||||
network_update();
|
||||
|
||||
sFrameTimeStart = clock_elapsed_f64();
|
||||
sLastFrameTimeStart = sFrameTimeStart;
|
||||
|
||||
patch_interpolations_before();
|
||||
gfx_start_frame();
|
||||
|
||||
|
@ -202,10 +232,7 @@ void produce_one_frame(void) {
|
|||
|
||||
gfx_end_frame();
|
||||
|
||||
// uncapped
|
||||
if (config60Fps) {
|
||||
produce_uncapped_frames();
|
||||
}
|
||||
produce_interpolation_frames_and_delay();
|
||||
}
|
||||
|
||||
void audio_shutdown(void) {
|
||||
|
@ -334,7 +361,7 @@ void main_func(void) {
|
|||
wm_api->set_keyboard_callbacks(keyboard_on_key_down, keyboard_on_key_up, keyboard_on_all_keys_up, keyboard_on_text_input);
|
||||
|
||||
#if defined(AAPI_SDL1) || defined(AAPI_SDL2)
|
||||
if (audio_api == NULL && audio_sdl.init())
|
||||
if (audio_api == NULL && audio_sdl.init())
|
||||
audio_api = &audio_sdl;
|
||||
#endif
|
||||
|
||||
|
|
|
@ -10,6 +10,7 @@
|
|||
#include "game/area.h"
|
||||
#include "game/level_update.h"
|
||||
#include "game/save_file.h"
|
||||
#include "engine/math_util.h"
|
||||
|
||||
float smoothstep(float edge0, float edge1, float x) {
|
||||
float t = (x - edge0) / (edge1 - edge0);
|
||||
|
@ -116,6 +117,49 @@ next_get:
|
|||
|
||||
/////////////////
|
||||
|
||||
static f32 sm64_to_radians(f32 val) {
|
||||
return val * M_PI / 0x8000;
|
||||
}
|
||||
|
||||
static f32 radians_to_sm64(f32 val) {
|
||||
return val * 0x8000 / M_PI;
|
||||
}
|
||||
|
||||
static f32 asins(f32 val) {
|
||||
return radians_to_sm64(asin(sm64_to_radians(val)));
|
||||
}
|
||||
|
||||
void mtx_to_euler1(Mtx* mat, Vec3s rot) {
|
||||
if (mat->m[1][1] == 1.0f) {
|
||||
rot[0] = 0;
|
||||
rot[1] = atan2(mat->m[1][3], mat->m[3][4]);
|
||||
rot[2] = 0;
|
||||
} else if (mat->m[1][1] == -1.0f) {
|
||||
rot[0] = 0;
|
||||
rot[1] = atan2(mat->m[1][3], mat->m[3][4]);
|
||||
rot[2] = 0;
|
||||
} else {
|
||||
rot[0] = asin(mat->m[2][1]);
|
||||
rot[1] = atan2(-mat->m[3][1], mat->m[1][1]);
|
||||
rot[2] = atan2(-mat->m[2][3], mat->m[2][2]);
|
||||
}
|
||||
}
|
||||
void mtx_to_euler2(Mtx* mat, Vec3s rot) {
|
||||
if (mat->m[1][1] == 1.0f) {
|
||||
rot[0] = 0;
|
||||
rot[1] = atan2(mat->m[3][1], mat->m[4][3]);
|
||||
rot[2] = 0;
|
||||
} else if (mat->m[1][1] == -1.0f) {
|
||||
rot[0] = 0;
|
||||
rot[1] = atan2(mat->m[3][1], mat->m[4][3]);
|
||||
rot[2] = 0;
|
||||
} else {
|
||||
rot[0] = asin(mat->m[1][2]);
|
||||
rot[1] = atan2(-mat->m[1][3], mat->m[1][1]);
|
||||
rot[2] = atan2(-mat->m[3][2], mat->m[2][2]);
|
||||
}
|
||||
}
|
||||
|
||||
f32 delta_interpolate_f32(f32 start, f32 end, f32 delta) {
|
||||
return start * (1.0f - delta) + end * delta;
|
||||
}
|
||||
|
@ -145,15 +189,6 @@ void delta_interpolate_normal(s8* res, s8* a, s8* b, f32 delta) {
|
|||
res[2] = ((a[2] * antiDelta) + (b[2] * delta));
|
||||
}
|
||||
|
||||
void delta_interpolate_mtx(Mtx* out, Mtx* a, Mtx* b, f32 delta) {
|
||||
f32 antiDelta = 1.0f - delta;
|
||||
for (s32 i = 0; i < 4; i++) {
|
||||
for (s32 j = 0; j < 4; j++) {
|
||||
out->m[i][j] = (a->m[i][j] * antiDelta) + (b->m[i][j] * delta);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void delta_interpolate_rgba(u8* res, u8* a, u8* b, f32 delta) {
|
||||
f32 antiDelta = 1.0f - delta;
|
||||
res[0] = ((a[0] * antiDelta) + (b[0] * delta));
|
||||
|
@ -162,21 +197,7 @@ void delta_interpolate_rgba(u8* res, u8* a, u8* b, f32 delta) {
|
|||
res[3] = ((a[3] * antiDelta) + (b[3] * delta));
|
||||
}
|
||||
|
||||
/*
|
||||
|
||||
void interpolate_vectors(Vec3f res, Vec3f a, Vec3f b) {
|
||||
res[0] = (a[0] + b[0]) / 2.0f;
|
||||
res[1] = (a[1] + b[1]) / 2.0f;
|
||||
res[2] = (a[2] + b[2]) / 2.0f;
|
||||
}
|
||||
|
||||
void interpolate_vectors_s16(Vec3s res, Vec3s a, Vec3s b) {
|
||||
res[0] = (a[0] + b[0]) / 2;
|
||||
res[1] = (a[1] + b[1]) / 2;
|
||||
res[2] = (a[2] + b[2]) / 2;
|
||||
}
|
||||
|
||||
static s16 interpolate_angle(s16 a, s16 b) {
|
||||
static s16 delta_interpolate_angle(s16 a, s16 b, f32 delta) {
|
||||
s32 absDiff = b - a;
|
||||
if (absDiff < 0) {
|
||||
absDiff = -absDiff;
|
||||
|
@ -184,16 +205,47 @@ static s16 interpolate_angle(s16 a, s16 b) {
|
|||
if (absDiff >= 0x4000 && absDiff <= 0xC000) {
|
||||
return b;
|
||||
}
|
||||
|
||||
f32 antiDelta = 1.0f - delta;
|
||||
if (absDiff <= 0x8000) {
|
||||
return (a + b) / 2;
|
||||
return (a * antiDelta) + (b * delta);
|
||||
} else {
|
||||
return (a + b) / 2 + 0x8000;
|
||||
return (a * antiDelta) + (b * delta) + 0x8000;
|
||||
}
|
||||
}
|
||||
|
||||
static void interpolate_angles(Vec3s res, Vec3s a, Vec3s b) {
|
||||
res[0] = interpolate_angle(a[0], b[0]);
|
||||
res[1] = interpolate_angle(a[1], b[1]);
|
||||
res[2] = interpolate_angle(a[2], b[2]);
|
||||
static void delta_interpolate_angles(Vec3s res, Vec3s a, Vec3s b, f32 delta) {
|
||||
res[0] = delta_interpolate_angle(a[0], b[0], delta);
|
||||
res[1] = delta_interpolate_angle(a[1], b[1], delta);
|
||||
res[2] = delta_interpolate_angle(a[2], b[2], delta);
|
||||
}
|
||||
|
||||
void delta_interpolate_mtx(Mtx* out, Mtx* a, Mtx* b, f32 delta) {
|
||||
// this isn't the right way to do things.
|
||||
f32 antiDelta = 1.0f - delta;
|
||||
for (s32 i = 0; i < 4; i++) {
|
||||
for (s32 j = 0; j < 4; j++) {
|
||||
out->m[i][j] = (a->m[i][j] * antiDelta) + (b->m[i][j] * delta);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void detect_and_skip_mtx_interpolation(Mtx** mtxPrev, Mtx** mtx) {
|
||||
// if the matrix has changed "too much", then skip interpolation
|
||||
const f32 minDot = sqrt(2.0f) / -2.0f;
|
||||
Vec3f prevX; vec3f_copy(prevX, (f32*)(*mtxPrev)->m[0]); vec3f_normalize(prevX);
|
||||
Vec3f prevY; vec3f_copy(prevY, (f32*)(*mtxPrev)->m[1]); vec3f_normalize(prevY);
|
||||
Vec3f prevZ; vec3f_copy(prevZ, (f32*)(*mtxPrev)->m[2]); vec3f_normalize(prevZ);
|
||||
|
||||
Vec3f nextX; vec3f_copy(nextX, (f32*)(*mtx)->m[0]); vec3f_normalize(nextX);
|
||||
Vec3f nextY; vec3f_copy(nextY, (f32*)(*mtx)->m[1]); vec3f_normalize(nextY);
|
||||
Vec3f nextZ; vec3f_copy(nextZ, (f32*)(*mtx)->m[2]); vec3f_normalize(nextZ);
|
||||
|
||||
f32 dotX = vec3f_dot(prevX, nextX);
|
||||
f32 dotY = vec3f_dot(prevY, nextY);
|
||||
f32 dotZ = vec3f_dot(prevZ, nextZ);
|
||||
|
||||
if ((dotX < minDot) | (dotY < minDot) || (dotZ < minDot)) {
|
||||
*mtx = *mtxPrev;
|
||||
}
|
||||
}
|
||||
*/
|
|
@ -19,7 +19,8 @@ s32 delta_interpolate_s32(s32 a, s32 b, f32 delta);
|
|||
void delta_interpolate_vec3f(Vec3f res, Vec3f a, Vec3f b, f32 delta);
|
||||
void delta_interpolate_vec3s(Vec3s res, Vec3s a, Vec3s b, f32 delta);
|
||||
void delta_interpolate_normal(s8* res, s8* a, s8* b, f32 delta);
|
||||
void delta_interpolate_mtx(Mtx* out, Mtx* a, Mtx* b, f32 delta);
|
||||
void delta_interpolate_rgba(u8* res, u8* a, u8* b, f32 delta);
|
||||
void delta_interpolate_mtx(Mtx* out, Mtx* a, Mtx* b, f32 delta);
|
||||
void detect_and_skip_mtx_interpolation(Mtx** mtxPrev, Mtx** mtx);
|
||||
|
||||
#endif
|
Loading…
Reference in a new issue