Preload executable into memory on Windows to prevent stutters (#450)

This commit is contained in:
Wiseguy 2024-07-28 22:00:39 -04:00 committed by GitHub
parent 9981b922dc
commit c90434962f
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194

View File

@ -416,8 +416,123 @@ namespace zelda64 {
}
}
#ifdef _WIN32
struct PreloadContext {
HANDLE handle;
HANDLE mapping_handle;
SIZE_T size;
PVOID view;
};
bool preload_executable(PreloadContext& context) {
wchar_t module_name[MAX_PATH];
GetModuleFileNameW(NULL, module_name, MAX_PATH);
context.handle = CreateFileW(module_name, GENERIC_READ, FILE_SHARE_READ, nullptr, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, nullptr);
if (context.handle == INVALID_HANDLE_VALUE) {
fprintf(stderr, "Failed to load executable into memory!");
context = {};
return false;
}
LARGE_INTEGER module_size;
if (!GetFileSizeEx(context.handle, &module_size)) {
fprintf(stderr, "Failed to get size of executable!");
CloseHandle(context.handle);
context = {};
return false;
}
context.size = module_size.QuadPart;
context.mapping_handle = CreateFileMappingW(context.handle, nullptr, PAGE_READONLY, 0, 0, nullptr);
if (context.mapping_handle == nullptr) {
recompui::message_box("Failed to create file mapping of executable!");
CloseHandle(context.handle);
context = {};
return EXIT_FAILURE;
}
context.view = MapViewOfFile(context.mapping_handle, FILE_MAP_READ, 0, 0, 0);
if (context.view == nullptr) {
fprintf(stderr, "Failed to map view of of executable!");
CloseHandle(context.mapping_handle);
CloseHandle(context.handle);
context = {};
return false;
}
DWORD pid = GetCurrentProcessId();
HANDLE process_handle = OpenProcess(PROCESS_SET_QUOTA | PROCESS_QUERY_INFORMATION, FALSE, pid);
if (process_handle == nullptr) {
fprintf(stderr, "Failed to open own process!");
CloseHandle(context.mapping_handle);
CloseHandle(context.handle);
context = {};
return false;
}
SIZE_T minimum_set_size, maximum_set_size;
if (!GetProcessWorkingSetSize(process_handle, &minimum_set_size, &maximum_set_size)) {
fprintf(stderr, "Failed to get working set size!");
CloseHandle(context.mapping_handle);
CloseHandle(context.handle);
context = {};
return false;
}
if (!SetProcessWorkingSetSize(process_handle, minimum_set_size + context.size, maximum_set_size + context.size)) {
fprintf(stderr, "Failed to set working set size!");
CloseHandle(context.mapping_handle);
CloseHandle(context.handle);
context = {};
return false;
}
if (VirtualLock(context.view, context.size) == 0) {
fprintf(stderr, "Failed to lock view of executable! (Error: %08lx)\n", GetLastError());
CloseHandle(context.mapping_handle);
CloseHandle(context.handle);
context = {};
return false;
}
return true;
}
void release_preload(PreloadContext& context) {
VirtualUnlock(context.view, context.size);
CloseHandle(context.mapping_handle);
CloseHandle(context.handle);
context = {};
}
#else
struct PreloadContext {
};
// TODO implement on other platforms
bool preload_executable(PreloadContext& context) {
return false;
}
void release_preload(PreloadContext& context) {
}
#endif
int main(int argc, char** argv) {
// Map this executable into memory and lock it, which should keep it in physical memory. This ensures
// that there are no stutters from the OS having to load new pages of the executable whenever a new code page is run.
PreloadContext preload_context;
bool preloaded = preload_executable(preload_context);
if (!preloaded) {
fprintf(stderr, "Failed to preload executable!\n");
}
#ifdef _WIN32
// Set up console output to accept UTF-8 on windows
@ -518,5 +633,9 @@ int main(int argc, char** argv) {
NFD_Quit();
if (preloaded) {
release_preload(preload_context);
}
return EXIT_SUCCESS;
}