rework vsync to be manual wait for win32
This commit is contained in:
parent
d9703519b9
commit
6ae129022e
|
@ -1132,8 +1132,6 @@ void oc_init()
|
||||||
[NSApp run];
|
[NSApp run];
|
||||||
[NSApp setActivationPolicy:NSApplicationActivationPolicyRegular];
|
[NSApp setActivationPolicy:NSApplicationActivationPolicyRegular];
|
||||||
[NSApp activateIgnoringOtherApps:YES];
|
[NSApp activateIgnoringOtherApps:YES];
|
||||||
|
|
||||||
oc_vsync_init();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2404,37 +2402,39 @@ void oc_vsync_init(void)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
void oc_vsync_window(oc_window window)
|
void oc_vsync_wait(oc_window window)
|
||||||
{
|
{
|
||||||
oc_window_data* windowData = oc_window_ptr_from_handle(window);
|
// TODO figure out why this causes stuttering with triple buffering
|
||||||
if(!windowData)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
OCWindow* ocWindow = (OCWindow*)windowData->osx.nsWindow;
|
// oc_window_data* windowData = oc_window_ptr_from_handle(window);
|
||||||
|
// if(!windowData)
|
||||||
|
// {
|
||||||
|
// return;
|
||||||
|
// }
|
||||||
|
|
||||||
CVReturn ret;
|
// OCWindow* ocWindow = (OCWindow*)windowData->osx.nsWindow;
|
||||||
|
|
||||||
if((ret = CVDisplayLinkCreateWithActiveCGDisplays(&ocWindow->displayLink)) != kCVReturnSuccess)
|
// CVReturn ret;
|
||||||
{
|
|
||||||
oc_log_error("CVDisplayLinkCreateWithActiveCGDisplays error: %d\n", ret);
|
|
||||||
}
|
|
||||||
|
|
||||||
CGDirectDisplayID mainDisplay = CGMainDisplayID();
|
// if((ret = CVDisplayLinkCreateWithActiveCGDisplays(&ocWindow->displayLink)) != kCVReturnSuccess)
|
||||||
|
// {
|
||||||
|
// oc_log_error("CVDisplayLinkCreateWithActiveCGDisplays error: %d\n", ret);
|
||||||
|
// }
|
||||||
|
|
||||||
if((ret = CVDisplayLinkSetCurrentCGDisplay(ocWindow->displayLink, mainDisplay)) != kCVReturnSuccess)
|
// CGDirectDisplayID mainDisplay = CGMainDisplayID();
|
||||||
{
|
|
||||||
oc_log_error("CVDisplayLinkSetCurrentCGDisplay ret: %d\n", ret);
|
|
||||||
}
|
|
||||||
|
|
||||||
if((ret = CVDisplayLinkSetOutputCallback(ocWindow->displayLink, oc_display_link_callback, ocWindow)) != kCVReturnSuccess)
|
// if((ret = CVDisplayLinkSetCurrentCGDisplay(ocWindow->displayLink, mainDisplay)) != kCVReturnSuccess)
|
||||||
{
|
// {
|
||||||
oc_log_error("CVDisplayLinkSetOutputCallback ret: %d\n", ret);
|
// oc_log_error("CVDisplayLinkSetCurrentCGDisplay ret: %d\n", ret);
|
||||||
}
|
// }
|
||||||
|
|
||||||
if((ret = CVDisplayLinkStart(ocWindow->displayLink)) != kCVReturnSuccess)
|
// if((ret = CVDisplayLinkSetOutputCallback(ocWindow->displayLink, oc_display_link_callback, ocWindow)) != kCVReturnSuccess)
|
||||||
{
|
// {
|
||||||
oc_log_error("CVDisplayLinkStart ret: %d\n", ret);
|
// oc_log_error("CVDisplayLinkSetOutputCallback ret: %d\n", ret);
|
||||||
}
|
// }
|
||||||
|
|
||||||
|
// if((ret = CVDisplayLinkStart(ocWindow->displayLink)) != kCVReturnSuccess)
|
||||||
|
// {
|
||||||
|
// oc_log_error("CVDisplayLinkStart ret: %d\n", ret);
|
||||||
|
// }
|
||||||
}
|
}
|
||||||
|
|
|
@ -586,14 +586,6 @@ LRESULT oc_win32_win_proc(HWND windowHandle, UINT message, WPARAM wParam, LPARAM
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case OC_WM_USER_VBLANK:
|
|
||||||
{
|
|
||||||
oc_event event = { 0 };
|
|
||||||
event.type = OC_EVENT_FRAME;
|
|
||||||
oc_queue_event(&event);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
default:
|
||||||
{
|
{
|
||||||
handled = false;
|
handled = false;
|
||||||
|
|
|
@ -49,7 +49,6 @@ typedef struct oc_win32_app_data
|
||||||
enum OC_WM_USER
|
enum OC_WM_USER
|
||||||
{
|
{
|
||||||
OC_WM_USER_DISPATCH_PROC = 0x0400, // WM_USER messages are defined from 0x400 to 0x7FFF
|
OC_WM_USER_DISPATCH_PROC = 0x0400, // WM_USER messages are defined from 0x400 to 0x7FFF
|
||||||
OC_WM_USER_VBLANK = 0x0401,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif __WIN32_APP_H_
|
#endif __WIN32_APP_H_
|
||||||
|
|
|
@ -132,7 +132,7 @@ ORCA_API void oc_surface_send_to_back(oc_surface surface); //DOC: puts surface
|
||||||
typedef void (*oc_vsync_callback)(void* data);
|
typedef void (*oc_vsync_callback)(void* data);
|
||||||
|
|
||||||
ORCA_API void oc_vsync_init(void);
|
ORCA_API void oc_vsync_init(void);
|
||||||
ORCA_API void oc_vsync_window(oc_window window);
|
ORCA_API void oc_vsync_wait(oc_window window);
|
||||||
|
|
||||||
//------------------------------------------------------------------------------------------
|
//------------------------------------------------------------------------------------------
|
||||||
//SECTION: graphics canvas structs
|
//SECTION: graphics canvas structs
|
||||||
|
|
|
@ -148,7 +148,10 @@ oc_surface oc_surface_create_for_window(oc_window window, oc_surface_api api)
|
||||||
if(surface)
|
if(surface)
|
||||||
{
|
{
|
||||||
surfaceHandle = oc_surface_handle_alloc(surface);
|
surfaceHandle = oc_surface_handle_alloc(surface);
|
||||||
|
// On windows, we do a manual wait for vsync in runtime.c so just turn all the swap intervals off
|
||||||
|
#if OC_PLATFORM_WINDOWS
|
||||||
oc_surface_swap_interval(surfaceHandle, 0);
|
oc_surface_swap_interval(surfaceHandle, 0);
|
||||||
|
#endif
|
||||||
oc_surface_select(surfaceHandle);
|
oc_surface_select(surfaceHandle);
|
||||||
}
|
}
|
||||||
return (surfaceHandle);
|
return (surfaceHandle);
|
||||||
|
|
|
@ -23,89 +23,6 @@ typedef struct oc_vsync_data
|
||||||
|
|
||||||
oc_vsync_data __oc_vsync_data;
|
oc_vsync_data __oc_vsync_data;
|
||||||
|
|
||||||
typedef struct oc_vsync_thread_data
|
|
||||||
{
|
|
||||||
IDXGIAdapter1* adapter;
|
|
||||||
oc_window window;
|
|
||||||
bool quit;
|
|
||||||
} oc_vsync_thread_data;
|
|
||||||
|
|
||||||
static i32 oc_window_vysnc_notification_thread(void* userPointer)
|
|
||||||
{
|
|
||||||
oc_vsync_thread_data* threadData = (oc_vsync_thread_data*)userPointer;
|
|
||||||
|
|
||||||
while(!threadData->quit)
|
|
||||||
{
|
|
||||||
oc_window_data* windowData = oc_window_ptr_from_handle(threadData->window);
|
|
||||||
if(!windowData)
|
|
||||||
{
|
|
||||||
oc_log_error("Failed to get window ptr - assuming window was closed.");
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
RECT windowRect = { 0 };
|
|
||||||
if(GetWindowRect(windowData->win32.hWnd, &windowRect) == FALSE)
|
|
||||||
{
|
|
||||||
oc_log_error("Failed to get window rect with error: %d.", GetLastError());
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
//NOTE(Reuben): Wait for VBlank on the display device with which the window has the most intersecting area
|
|
||||||
IDXGIOutput* output = NULL;
|
|
||||||
IDXGIOutput* selectedOutput = NULL;
|
|
||||||
uint32_t selected_intersect_area = 0;
|
|
||||||
for(UINT i = 0; DXGI_ERROR_NOT_FOUND != IDXGIAdapter1_EnumOutputs(threadData->adapter, i, &output); ++i)
|
|
||||||
{
|
|
||||||
DXGI_OUTPUT_DESC outputDesc = { 0 };
|
|
||||||
HRESULT hr = IDXGIOutput_GetDesc(output, &outputDesc);
|
|
||||||
if(SUCCEEDED(hr))
|
|
||||||
{
|
|
||||||
RECT intersectRect = { 0 };
|
|
||||||
if(IntersectRect(&intersectRect, &windowRect, &outputDesc.DesktopCoordinates))
|
|
||||||
{
|
|
||||||
uint32_t outputIntersectArea = (intersectRect.right - intersectRect.left) * (intersectRect.bottom - intersectRect.top);
|
|
||||||
|
|
||||||
if(selectedOutput == NULL || outputIntersectArea > selected_intersect_area)
|
|
||||||
{
|
|
||||||
selectedOutput = output;
|
|
||||||
selected_intersect_area = outputIntersectArea;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
oc_log_error("Failed to get IDXGIOutput desc with error: %d", hr);
|
|
||||||
}
|
|
||||||
|
|
||||||
if(selectedOutput != output)
|
|
||||||
{
|
|
||||||
IDXGIOutput_Release(output);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if(selectedOutput)
|
|
||||||
{
|
|
||||||
HRESULT hr = IDXGIOutput_WaitForVBlank(selectedOutput);
|
|
||||||
|
|
||||||
if(FAILED(hr))
|
|
||||||
{
|
|
||||||
// TODO(reuben) - fall back to software timer
|
|
||||||
oc_log_warning("Failed to wait for vblank with error: %d", hr);
|
|
||||||
}
|
|
||||||
|
|
||||||
SendMessage(windowData->win32.hWnd, OC_WM_USER_VBLANK, 0, 0);
|
|
||||||
|
|
||||||
IDXGIOutput_Release(selectedOutput);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
oc_log_warning("No outputs found. Were all monitors unplugged?");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
void oc_vsync_init(void)
|
void oc_vsync_init(void)
|
||||||
{
|
{
|
||||||
if(__oc_vsync_data.adapter)
|
if(__oc_vsync_data.adapter)
|
||||||
|
@ -116,7 +33,7 @@ void oc_vsync_init(void)
|
||||||
IDXGIFactory4* factory = NULL;
|
IDXGIFactory4* factory = NULL;
|
||||||
{
|
{
|
||||||
UINT flags = 0;
|
UINT flags = 0;
|
||||||
flags |= DXGI_CREATE_FACTORY_DEBUG; // TODO make this optional
|
// flags |= DXGI_CREATE_FACTORY_DEBUG; // TODO make this optional
|
||||||
|
|
||||||
HRESULT hr = CreateDXGIFactory2(flags, &IID_IDXGIFactory4, (void**)&factory);
|
HRESULT hr = CreateDXGIFactory2(flags, &IID_IDXGIFactory4, (void**)&factory);
|
||||||
if(!SUCCEEDED(hr))
|
if(!SUCCEEDED(hr))
|
||||||
|
@ -163,25 +80,77 @@ void oc_vsync_init(void)
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
oc_log_info("Couldn't find any DXGI adapters. Vsync will be unavailable.");
|
oc_log_info("Couldn't find any DXGI adapters - vsync will be unavailable.");
|
||||||
IDXGIFactory_Release(factory);
|
IDXGIFactory_Release(factory);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void oc_vsync_window(oc_window window)
|
void oc_vsync_wait(oc_window window)
|
||||||
{
|
{
|
||||||
if(__oc_vsync_data.adapter)
|
if(__oc_vsync_data.adapter)
|
||||||
{
|
{
|
||||||
oc_vsync_thread_data* data = malloc(sizeof(oc_vsync_thread_data));
|
oc_window_data* windowData = oc_window_ptr_from_handle(window);
|
||||||
*data = (oc_vsync_thread_data){
|
if(!windowData)
|
||||||
.adapter = __oc_vsync_data.adapter,
|
{
|
||||||
.window = window,
|
oc_log_error("Failed to get window ptr - assuming window was closed.");
|
||||||
.quit = false,
|
return;
|
||||||
};
|
}
|
||||||
oc_thread_create_with_name(oc_window_vysnc_notification_thread, data, OC_STR8("vblank thread"));
|
|
||||||
}
|
RECT windowRect = { 0 };
|
||||||
else
|
if(GetWindowRect(windowData->win32.hWnd, &windowRect) == FALSE)
|
||||||
{
|
{
|
||||||
oc_log_error("No DXGI adapter available - unable to start vsync notification thread.");
|
oc_log_error("Failed to get window rect with error: %d.", GetLastError());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Wait for VBlank on the display device with which the window has the most intersecting area
|
||||||
|
IDXGIOutput* output = NULL;
|
||||||
|
IDXGIOutput* selectedOutput = NULL;
|
||||||
|
uint32_t selected_intersect_area = 0;
|
||||||
|
for(UINT i = 0; DXGI_ERROR_NOT_FOUND != IDXGIAdapter1_EnumOutputs(__oc_vsync_data.adapter, i, &output); ++i)
|
||||||
|
{
|
||||||
|
DXGI_OUTPUT_DESC outputDesc = { 0 };
|
||||||
|
HRESULT hr = IDXGIOutput_GetDesc(output, &outputDesc);
|
||||||
|
if(SUCCEEDED(hr))
|
||||||
|
{
|
||||||
|
RECT intersectRect = { 0 };
|
||||||
|
if(IntersectRect(&intersectRect, &windowRect, &outputDesc.DesktopCoordinates))
|
||||||
|
{
|
||||||
|
uint32_t outputIntersectArea = (intersectRect.right - intersectRect.left) * (intersectRect.bottom - intersectRect.top);
|
||||||
|
|
||||||
|
if(selectedOutput == NULL || outputIntersectArea > selected_intersect_area)
|
||||||
|
{
|
||||||
|
selectedOutput = output;
|
||||||
|
selected_intersect_area = outputIntersectArea;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
oc_log_error("Failed to get IDXGIOutput desc with error: %d", hr);
|
||||||
|
}
|
||||||
|
|
||||||
|
if(selectedOutput != output)
|
||||||
|
{
|
||||||
|
IDXGIOutput_Release(output);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if(selectedOutput)
|
||||||
|
{
|
||||||
|
HRESULT hr = IDXGIOutput_WaitForVBlank(selectedOutput);
|
||||||
|
|
||||||
|
if(FAILED(hr))
|
||||||
|
{
|
||||||
|
// TODO(reuben) - fall back to software timer
|
||||||
|
oc_log_warning("Failed to wait for vblank with error: %d", hr);
|
||||||
|
}
|
||||||
|
|
||||||
|
IDXGIOutput_Release(selectedOutput);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
oc_log_warning("No outputs found. Were all monitors unplugged?");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -676,19 +676,6 @@ i32 orca_runloop(void* user)
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case OC_EVENT_FRAME:
|
|
||||||
{
|
|
||||||
if(exports[OC_EXPORT_FRAME_REFRESH])
|
|
||||||
{
|
|
||||||
M3Result res = m3_Call(exports[OC_EXPORT_FRAME_REFRESH], 0, 0);
|
|
||||||
if(res)
|
|
||||||
{
|
|
||||||
ORCA_WASM3_ABORT(app->runtime.m3Runtime, res, "Runtime error");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -851,6 +838,8 @@ i32 orca_runloop(void* user)
|
||||||
oc_surface_present(app->debugOverlay.surface);
|
oc_surface_present(app->debugOverlay.surface);
|
||||||
|
|
||||||
oc_arena_clear(oc_scratch());
|
oc_arena_clear(oc_scratch());
|
||||||
|
|
||||||
|
oc_vsync_wait(app->window);
|
||||||
}
|
}
|
||||||
|
|
||||||
if(exports[OC_EXPORT_TERMINATE])
|
if(exports[OC_EXPORT_TERMINATE])
|
||||||
|
@ -880,8 +869,6 @@ int main(int argc, char** argv)
|
||||||
oc_rect windowRect = { .x = 100, .y = 100, .w = 810, .h = 610 };
|
oc_rect windowRect = { .x = 100, .y = 100, .w = 810, .h = 610 };
|
||||||
app->window = oc_window_create(windowRect, OC_STR8("orca"), 0);
|
app->window = oc_window_create(windowRect, OC_STR8("orca"), 0);
|
||||||
|
|
||||||
oc_vsync_window(app->window);
|
|
||||||
|
|
||||||
app->debugOverlay.show = false;
|
app->debugOverlay.show = false;
|
||||||
app->debugOverlay.surface = oc_surface_create_for_window(app->window, OC_CANVAS);
|
app->debugOverlay.surface = oc_surface_create_for_window(app->window, OC_CANVAS);
|
||||||
app->debugOverlay.canvas = oc_canvas_create();
|
app->debugOverlay.canvas = oc_canvas_create();
|
||||||
|
|
Loading…
Reference in New Issue