Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
35 changes: 22 additions & 13 deletions thirdparty/nvapi/nvapi.c
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,7 @@ void nvapi_finalize(void)
}
}

bool nvapi_setup_profile(NvApiProfileOpts opts)
bool nvapi_setup_profile(NvApiProfileOpts opts, NvApiProfileState *active_state)
{
if (g_hnvapi == NULL) {
return false;
Expand Down Expand Up @@ -138,6 +138,27 @@ bool nvapi_setup_profile(NvApiProfileOpts opts)
LOG("Added application to profile");
}

NVDRS_SETTING vsync_setting = {
.version = NVDRS_SETTING_VER,
};
if (NvAPI_DRS_GetSetting(session, profile, VSYNCMODE_ID, &vsync_setting)) {
LOG("NvAPI_DRS_GetSetting for settingId %x failed", VSYNCMODE_ID);
active_state->vsync_mode = VSYNC_MODE_APP_CONTROLLED;
} else {
switch (vsync_setting.u32CurrentValue) {
case VSYNCMODE_PASSIVE:
active_state->vsync_mode = VSYNC_MODE_APP_CONTROLLED;
break;
case VSYNCMODE_FORCEOFF:
case VSYNCMODE_VIRTUAL:
active_state->vsync_mode = VSYNC_MODE_FORCE_OFF;
break;
default:
active_state->vsync_mode = VSYNC_MODE_FORCE_ON;
break;
}
}

NVDRS_SETTING setting = {
.version = NVDRS_SETTING_VER,
.settingId = OGL_THREAD_CONTROL_ID,
Expand All @@ -152,18 +173,6 @@ bool nvapi_setup_profile(NvApiProfileOpts opts)
goto cleanup;
}

NVDRS_SETTING setting_dxpresent = {
.version = NVDRS_SETTING_VER,
.settingId = OGL_CPL_PREFER_DXPRESENT_ID,
.settingType = NVDRS_DWORD_TYPE,
.u32CurrentValue = opts.present_method,
};
if (NvAPI_DRS_SetSetting(session, profile, &setting_dxpresent)) {
LOG("NvAPI_DRS_SetSetting for settingId %x failed",
setting_dxpresent.settingId);
goto cleanup;
}

if (NvAPI_DRS_SaveSettings(session)) {
LOG("NvAPI_DRS_SaveSettings failed");
goto cleanup;
Expand Down
22 changes: 12 additions & 10 deletions thirdparty/nvapi/nvapi.h
Original file line number Diff line number Diff line change
Expand Up @@ -27,23 +27,25 @@
#include <windows.h>
#include <stdbool.h>

enum EValues_OGL_CPL_PREFER_DXPRESENT {
OGL_CPL_PREFER_DXPRESENT_PREFER_DISABLED = 0x00000000,
OGL_CPL_PREFER_DXPRESENT_PREFER_ENABLED = 0x00000001,
OGL_CPL_PREFER_DXPRESENT_AUTO = 0x00000002,
OGL_CPL_PREFER_DXPRESENT_NUM_VALUES = 3,
OGL_CPL_PREFER_DXPRESENT_DEFAULT = OGL_CPL_PREFER_DXPRESENT_AUTO
};

typedef struct NvApiProfileOpts {
const wchar_t *profile_name;
const wchar_t *executable_name;
bool threaded_optimization;
enum EValues_OGL_CPL_PREFER_DXPRESENT present_method;
} NvApiProfileOpts;

typedef enum NvApiVsyncMode {
VSYNC_MODE_APP_CONTROLLED,
VSYNC_MODE_FORCE_OFF,
VSYNC_MODE_FORCE_ON,
} NvApiVsyncMode;

typedef struct NvApiProfileState {
NvApiVsyncMode vsync_mode;
} NvApiProfileState;

bool nvapi_init(void);
bool nvapi_setup_profile(NvApiProfileOpts opts);
bool nvapi_setup_profile(NvApiProfileOpts opts,
NvApiProfileState *active_state);
void nvapi_finalize(void);

#endif
16 changes: 15 additions & 1 deletion thirdparty/nvapi/nvapi_defs.h
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@

enum ESetting {
OGL_THREAD_CONTROL_ID = 0x20C1221E,
OGL_CPL_PREFER_DXPRESENT_ID = 0x20D690F8,
VSYNCMODE_ID = 0x00A879CF,
};

enum EValues_OGL_THREAD_CONTROL {
Expand All @@ -42,6 +42,18 @@ enum EValues_OGL_THREAD_CONTROL {
OGL_THREAD_CONTROL_DEFAULT = 0U
};

enum EValues_VSYNCMODE {
VSYNCMODE_PASSIVE = 0x60925292,
VSYNCMODE_FORCEOFF = 0x08416747,
VSYNCMODE_FORCEON = 0x47814940,
VSYNCMODE_FLIPINTERVAL2 = 0x32610244,
VSYNCMODE_FLIPINTERVAL3 = 0x71271021,
VSYNCMODE_FLIPINTERVAL4 = 0x13245256,
VSYNCMODE_VIRTUAL = 0x18888888,
VSYNCMODE_NUM_VALUES = 7,
VSYNCMODE_DEFAULT = VSYNCMODE_PASSIVE
};

typedef uint32_t NvU32;
typedef uint16_t NvU16;
typedef uint8_t NvU8;
Expand Down Expand Up @@ -224,6 +236,7 @@ typedef NVDRS_PROFILE_V1 NVDRS_PROFILE;
FUNC(NvAPI_DRS_LoadSettings, 0x375DBD6B) \
FUNC(NvAPI_DRS_SaveSettings, 0xFCBC7E14) \
FUNC(NvAPI_DRS_SetSetting, 0x577DD202) \
FUNC(NvAPI_DRS_GetSetting, 0x73BF8338) \
FUNC(NvAPI_Initialize, 0x0150E828) \
FUNC(NvAPI_Unload, 0xD22BDD7E)

Expand All @@ -236,6 +249,7 @@ typedef int(__cdecl *NvAPI_DRS_GetApplicationInfo_t)(NvDRSSessionHandle, NvDRSPr
typedef int(__cdecl *NvAPI_DRS_LoadSettings_t)(NvDRSSessionHandle);
typedef int(__cdecl *NvAPI_DRS_SaveSettings_t)(NvDRSSessionHandle);
typedef int(__cdecl *NvAPI_DRS_SetSetting_t)(NvDRSSessionHandle, NvDRSProfileHandle, NVDRS_SETTING *);
typedef int(__cdecl *NvAPI_DRS_GetSetting_t)(NvDRSSessionHandle, NvDRSProfileHandle, NvU32, NVDRS_SETTING *);
typedef int(__cdecl *NvAPI_Initialize_t)(void);
typedef int(__cdecl *NvAPI_Unload_t)(void);
typedef void *(__cdecl *NvAPI_QueryInterface_t)(unsigned int interface_id);
Expand Down
104 changes: 97 additions & 7 deletions ui/xemu.c
Original file line number Diff line number Diff line change
Expand Up @@ -796,6 +796,53 @@ static void report_stats(void)
}
#endif

enum VsyncOverrideBehavior {
VOB_FORCE_OFF,
VOB_FORCE_ON,
VOB_NO_OVERRIDE,
};

static enum VsyncOverrideBehavior host_vsync_override = VOB_NO_OVERRIDE;
static int64_t host_vblank_period_ns = 0;
static bool force_vsync_resync = true;

// Time reserved for SDL_GL_SwapWindow to perform a swap. This value is chosen
// empirically as the best balance between minimizing guest stalls while still
// hitting the target host refresh rate.
#define VSYNC_SWAP_GRACE_PERIOD_NS 1400000LL
#define VSYNC_RESYNC_FRAMES 2

static inline void vsync_swap_window(SDL_Window *window)
{
static int64_t last_swap_complete = 0;
int64_t now = qemu_clock_get_ns(QEMU_CLOCK_REALTIME);

if (last_swap_complete > 0 && !force_vsync_resync) {
int64_t next_vblank = last_swap_complete + host_vblank_period_ns -
VSYNC_SWAP_GRACE_PERIOD_NS;
int64_t sleep_duration_ns = next_vblank - now;
if (sleep_duration_ns > 0) {
SDL_DelayPrecise(sleep_duration_ns);
}
}

SDL_GL_SwapWindow(window);

int64_t post_swap = qemu_clock_get_ns(QEMU_CLOCK_REALTIME);

if (!last_swap_complete || force_vsync_resync ||
post_swap >
last_swap_complete + VSYNC_RESYNC_FRAMES * host_vblank_period_ns) {
last_swap_complete = post_swap;
force_vsync_resync = false;
} else {
int64_t elapsed_frames =
(post_swap - last_swap_complete + host_vblank_period_ns / 2) /
host_vblank_period_ns;
last_swap_complete += elapsed_frames * host_vblank_period_ns;
}
}

/**
* Renders the main interface. Usually called from the main thread,
* but may sometimes be called from another thread.
Expand Down Expand Up @@ -860,7 +907,14 @@ static void gl_render_frame(struct xemu_console *scon)
}

nv2a_release_framebuffer_surface();
SDL_GL_SwapWindow(scon->real_window);

if (host_vsync_override == VOB_FORCE_ON ||
(host_vsync_override == VOB_NO_OVERRIDE &&
g_config.display.window.vsync)) {
vsync_swap_window(scon->real_window);
} else {
SDL_GL_SwapWindow(scon->real_window);
}
assert(glGetError() == GL_NO_ERROR);

qatomic_set(&rendering, false);
Expand All @@ -870,13 +924,37 @@ static void gl_render_frame(struct xemu_console *scon)
#endif
}

static void calculate_vsync_interval_ns(void)
{
SDL_DisplayID display_idx = SDL_GetDisplayForWindow(m_window);
const SDL_DisplayMode *mode = SDL_GetCurrentDisplayMode(display_idx);
if (mode && mode->refresh_rate_numerator > 0 &&
mode->refresh_rate_denominator > 0) {
host_vblank_period_ns =
(1000000000LL * mode->refresh_rate_denominator) /
mode->refresh_rate_numerator;
} else if (mode && mode->refresh_rate > 0) {
host_vblank_period_ns = (int64_t)(1000000000.0 / mode->refresh_rate);
} else {
host_vblank_period_ns = 1000000000LL / 60;
}
force_vsync_resync = 1;
}

static bool event_watch_callback(void *userdata, SDL_Event *event)
{
struct xemu_console *scon = (struct xemu_console *)userdata;

if (event->type == SDL_EVENT_WINDOW_EXPOSED ||
event->type == SDL_EVENT_WINDOW_RESIZED) {
gl_render_frame(scon);
} else if (event->type == SDL_EVENT_DISPLAY_CURRENT_MODE_CHANGED ||
event->type == SDL_EVENT_WINDOW_DISPLAY_CHANGED) {
calculate_vsync_interval_ns();
} else if (event->type == SDL_EVENT_WINDOW_RESTORED ||
event->type == SDL_EVENT_WINDOW_MAXIMIZED ||
event->type == SDL_EVENT_WINDOW_FOCUS_GAINED) {
force_vsync_resync = 1;
}
Comment thread
abaire marked this conversation as resolved.

return true; // Ignored
Expand Down Expand Up @@ -1147,6 +1225,8 @@ static void display_init(DisplayState *ds, DisplayOptions *o)
scon_list[0].real_window = m_window;
scon_list[0].winctx = m_context;

calculate_vsync_interval_ns();

mouse_mode_notifier.notify = mouse_mode_change;
qemu_add_mouse_mode_change_notifier(&mouse_mode_notifier);

Expand Down Expand Up @@ -1250,13 +1330,23 @@ static void setup_nvidia_profile(void)
}

if (nvapi_init()) {
nvapi_setup_profile((NvApiProfileOpts){
.profile_name = L"xemu",
.executable_name = exe_name,
.threaded_optimization = false,
.present_method = OGL_CPL_PREFER_DXPRESENT_PREFER_DISABLED,
});
NvApiProfileState current_state;
nvapi_setup_profile(
(NvApiProfileOpts){
.profile_name = L"xemu",
.executable_name = exe_name,
.threaded_optimization = false,
},
&current_state);
nvapi_finalize();

if (current_state.vsync_mode == VSYNC_MODE_FORCE_OFF) {
host_vsync_override = VOB_FORCE_OFF;
} else if (current_state.vsync_mode == VSYNC_MODE_FORCE_ON) {
host_vsync_override = VOB_FORCE_ON;
} else {
host_vsync_override = VOB_NO_OVERRIDE;
}
}
}
#endif
Expand Down
Loading