From be5b6aea30b00395161f4156f6412b602b118348 Mon Sep 17 00:00:00 2001 From: Reuben Dunnington Date: Sat, 26 Aug 2023 22:13:38 -0700 Subject: [PATCH 1/5] vblank notification: win32 --- scripts/dev.py | 4 +- src/app/win32_app.c | 11 ++ src/app/win32_app.h | 1 + src/graphics/graphics.h | 9 ++ src/graphics/graphics_surface.c | 1 + src/graphics/win32_vsync.c | 206 ++++++++++++++++++++++++++++++++ src/orca.c | 1 + src/runtime.c | 15 +++ 8 files changed, 246 insertions(+), 2 deletions(-) create mode 100644 src/graphics/win32_vsync.c diff --git a/scripts/dev.py b/scripts/dev.py index 6a6ad8a..ea9d3e8 100644 --- a/scripts/dev.py +++ b/scripts/dev.py @@ -128,7 +128,6 @@ def build_platform_layer(target, release): def build_platform_layer_lib_win(release): - embed_text_files("src\\graphics\\glsl_shaders.h", "glsl_", [ "src\\graphics\\glsl_shaders\\common.glsl", "src\\graphics\\glsl_shaders\\blit_vertex.glsl", @@ -157,6 +156,8 @@ def build_platform_layer_lib_win(release): "ole32.lib", "shell32.lib", "shlwapi.lib", + "dxgi.lib", + "dxguid.lib", "/LIBPATH:ext/angle/lib", "libEGL.dll.lib", "libGLESv2.dll.lib", @@ -178,7 +179,6 @@ def build_platform_layer_lib_win(release): "/IMPLIB:build/bin/orca.dll.lib", ], check=True) - def build_platform_layer_lib_mac(release): sdk_dir = "/Library/Developer/CommandLineTools/SDKs/MacOSX.sdk" diff --git a/src/app/win32_app.c b/src/app/win32_app.c index 7312295..c286bb8 100644 --- a/src/app/win32_app.c +++ b/src/app/win32_app.c @@ -9,6 +9,7 @@ #include "app.c" #include "platform/platform_thread.h" +#include "graphics/graphics.h" #include void oc_init_keys() @@ -169,6 +170,8 @@ void oc_init() u32 wheelScrollLines = 3; SystemParametersInfo(SPI_GETWHEELSCROLLLINES, 0, &wheelScrollLines, 0); oc_appData.win32.wheelScrollLines = wheelScrollLines; + + oc_vsync_init(); } } @@ -583,6 +586,14 @@ LRESULT oc_win32_win_proc(HWND windowHandle, UINT message, WPARAM wParam, LPARAM } break; + case OC_WM_USER_VBLANK: + { + oc_event event = { 0 }; + event.type = OC_EVENT_FRAME; + oc_queue_event(&event); + } + break; + default: { handled = false; diff --git a/src/app/win32_app.h b/src/app/win32_app.h index 8f41e84..7f682c4 100644 --- a/src/app/win32_app.h +++ b/src/app/win32_app.h @@ -49,6 +49,7 @@ typedef struct oc_win32_app_data enum OC_WM_USER { OC_WM_USER_DISPATCH_PROC = 0x0400, // WM_USER messages are defined from 0x400 to 0x7FFF + OC_WM_USER_VBLANK = 0x0401, }; #endif __WIN32_APP_H_ diff --git a/src/graphics/graphics.h b/src/graphics/graphics.h index 860a941..0e0ba77 100644 --- a/src/graphics/graphics.h +++ b/src/graphics/graphics.h @@ -125,6 +125,15 @@ ORCA_API oc_vec2 oc_surface_contents_scaling(oc_surface surface); //DOC: returns ORCA_API void oc_surface_bring_to_front(oc_surface surface); //DOC: puts surface on top of the surface stack ORCA_API void oc_surface_send_to_back(oc_surface surface); //DOC: puts surface at the bottom of the surface stack +//------------------------------------------------------------------------------------------ +//SECTION: vsync +//------------------------------------------------------------------------------------------ + +typedef void (*oc_vsync_callback)(void* data); + +ORCA_API void oc_vsync_init(); +ORCA_API void oc_vsync_window(oc_window window); + //------------------------------------------------------------------------------------------ //SECTION: graphics canvas structs //------------------------------------------------------------------------------------------ diff --git a/src/graphics/graphics_surface.c b/src/graphics/graphics_surface.c index d55ba82..246bf53 100644 --- a/src/graphics/graphics_surface.c +++ b/src/graphics/graphics_surface.c @@ -148,6 +148,7 @@ oc_surface oc_surface_create_for_window(oc_window window, oc_surface_api api) if(surface) { surfaceHandle = oc_surface_handle_alloc(surface); + oc_surface_swap_interval(surfaceHandle, 0); oc_surface_select(surfaceHandle); } return (surfaceHandle); diff --git a/src/graphics/win32_vsync.c b/src/graphics/win32_vsync.c new file mode 100644 index 0000000..88ef542 --- /dev/null +++ b/src/graphics/win32_vsync.c @@ -0,0 +1,206 @@ +/************************************************************/ /** +* +* @file: win32_vsync.h +* @author: Reuben Dunnington +* @date: 26/08/2023 +* +*****************************************************************/ + +// #define WIN32_LEAN_AND_MEAN +// #define WIN32_EXTRA_LEAN +// #define NOMINMAX + +// #define D3D11_NO_HELPERS +// #define CINTERFACE +// #define COBJMACROS + +// #define interface struct + +// #include + +#include "platform/platform_thread.h" +#include "app/win32_app.h" + +// This forward declaration is needed to suppress an order of declarations bug in the d3d headers. +// d3d11shader.h(454): warning C4115: 'ID3D11ModuleInstance': named type definition in parentheses +// struct ID3D11ModuleInstance; + +#define COBJMACROS +#define interface struct +#include +#include +#undef interface + +// #include +// #include + +typedef struct oc_vsync_data +{ + IDXGIFactory4* factory; + IDXGIAdapter1* adapter; +} 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() +{ + if(__oc_vsync_data.adapter) + { + return; + } + + IDXGIFactory4* factory = NULL; + { + UINT flags = 0; + flags |= DXGI_CREATE_FACTORY_DEBUG; // TODO make this optional + + HRESULT hr = CreateDXGIFactory2(flags, &IID_IDXGIFactory4, (void**)&factory); + if(!SUCCEEDED(hr)) + { + return; + } + } + + IDXGIAdapter1* adapter = NULL; + IDXGIAdapter1* adapterFallback = NULL; + IDXGIAdapter1* enumeratedAdapter = NULL; + + for(UINT i = 0; DXGI_ERROR_NOT_FOUND != IDXGIFactory1_EnumAdapters1(factory, i, &enumeratedAdapter); ++i) + { + DXGI_ADAPTER_DESC1 adapterDesc; + IDXGIAdapter1_GetDesc1(enumeratedAdapter, &adapterDesc); + if(adapterDesc.Flags & DXGI_ADAPTER_FLAG_SOFTWARE) + { + adapterFallback = enumeratedAdapter; + continue; + } + else + { + adapter = enumeratedAdapter; + } + break; + } + + if(adapter == NULL) + { + adapter = adapterFallback; + if(adapter) + { + oc_log_info("Couldn't find a dedicated hardware DXGI adapater, using software fallback."); + } + } + + if(adapter) + { + __oc_vsync_data = (oc_vsync_data){ + .factory = factory, + .adapter = adapter, + }; + } + else + { + oc_log_info("Couldn't find any DXGI adapters. Vsync will be unavailable."); + IDXGIFactory_Release(factory); + } +} + +void oc_vsync_window(oc_window window) +{ + if(__oc_vsync_data.adapter) + { + oc_vsync_thread_data* data = malloc(sizeof(oc_vsync_thread_data)); + *data = (oc_vsync_thread_data){ + .adapter = __oc_vsync_data.adapter, + .window = window, + .quit = false, + }; + oc_thread_create_with_name(oc_window_vysnc_notification_thread, data, OC_STR8("vblank thread")); + } + else + { + oc_log_error("No DXGI adapter available - unable to start vsync notification thread."); + } +} diff --git a/src/orca.c b/src/orca.c index 996f370..fbe410b 100644 --- a/src/orca.c +++ b/src/orca.c @@ -74,6 +74,7 @@ #if OC_PLATFORM_WINDOWS #include "app/win32_app.c" + #include "graphics/win32_vsync.c" #include "graphics/graphics_common.c" #include "graphics/graphics_surface.c" diff --git a/src/runtime.c b/src/runtime.c index 14ca3a9..da67cfe 100644 --- a/src/runtime.c +++ b/src/runtime.c @@ -676,6 +676,19 @@ i32 orca_runloop(void* user) } 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: break; } @@ -867,6 +880,8 @@ int main(int argc, char** argv) oc_rect windowRect = { .x = 100, .y = 100, .w = 810, .h = 610 }; app->window = oc_window_create(windowRect, OC_STR8("orca"), 0); + oc_vsync_window(app->window); + app->debugOverlay.show = false; app->debugOverlay.surface = oc_surface_create_for_window(app->window, OC_CANVAS); app->debugOverlay.canvas = oc_canvas_create(); -- 2.25.1 From aa1c5ef91532f9550617e0fd5bc80fd524aa0681 Mon Sep 17 00:00:00 2001 From: Reuben Dunnington Date: Sun, 27 Aug 2023 20:45:55 -0700 Subject: [PATCH 2/5] cleanup dead code --- src/graphics/graphics.h | 2 +- src/graphics/win32_vsync.c | 21 +-------------------- 2 files changed, 2 insertions(+), 21 deletions(-) diff --git a/src/graphics/graphics.h b/src/graphics/graphics.h index 0e0ba77..ec69537 100644 --- a/src/graphics/graphics.h +++ b/src/graphics/graphics.h @@ -131,7 +131,7 @@ ORCA_API void oc_surface_send_to_back(oc_surface surface); //DOC: puts surface typedef void (*oc_vsync_callback)(void* data); -ORCA_API void oc_vsync_init(); +ORCA_API void oc_vsync_init(void); ORCA_API void oc_vsync_window(oc_window window); //------------------------------------------------------------------------------------------ diff --git a/src/graphics/win32_vsync.c b/src/graphics/win32_vsync.c index 88ef542..0bfc4c9 100644 --- a/src/graphics/win32_vsync.c +++ b/src/graphics/win32_vsync.c @@ -6,34 +6,15 @@ * *****************************************************************/ -// #define WIN32_LEAN_AND_MEAN -// #define WIN32_EXTRA_LEAN -// #define NOMINMAX - -// #define D3D11_NO_HELPERS -// #define CINTERFACE -// #define COBJMACROS - -// #define interface struct - -// #include - #include "platform/platform_thread.h" #include "app/win32_app.h" -// This forward declaration is needed to suppress an order of declarations bug in the d3d headers. -// d3d11shader.h(454): warning C4115: 'ID3D11ModuleInstance': named type definition in parentheses -// struct ID3D11ModuleInstance; - #define COBJMACROS #define interface struct #include #include #undef interface -// #include -// #include - typedef struct oc_vsync_data { IDXGIFactory4* factory; @@ -125,7 +106,7 @@ static i32 oc_window_vysnc_notification_thread(void* userPointer) return 0; } -void oc_vsync_init() +void oc_vsync_init(void) { if(__oc_vsync_data.adapter) { -- 2.25.1 From d9703519b9915568a65c5c8f53dcc825411be52f Mon Sep 17 00:00:00 2001 From: Reuben Dunnington Date: Sun, 27 Aug 2023 20:47:32 -0700 Subject: [PATCH 3/5] osx vsync notification --- src/app/osx_app.m | 102 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 102 insertions(+) diff --git a/src/app/osx_app.m b/src/app/osx_app.m index b83e219..6449498 100644 --- a/src/app/osx_app.m +++ b/src/app/osx_app.m @@ -304,7 +304,10 @@ oc_key_code oc_label_to_key(oc_str8 label) @interface OCWindow : NSWindow { oc_window_data* mpWindow; + @public + CVDisplayLinkRef displayLink; } + - (id)initWithWindowData:(oc_window_data*)window contentRect:(NSRect)rect styleMask:(uint32)style; @end @@ -703,6 +706,9 @@ void oc_install_keyboard_layout_listener() - (BOOL)windowShouldClose:(id)sender { + OCWindow* ocWindow = (OCWindow*)mpWindow->osx.nsWindow; + CVDisplayLinkStop(ocWindow->displayLink); + mpWindow->shouldClose = true; oc_event event = {}; @@ -1126,6 +1132,8 @@ void oc_init() [NSApp run]; [NSApp setActivationPolicy:NSApplicationActivationPolicyRegular]; [NSApp activateIgnoringOtherApps:YES]; + + oc_vsync_init(); } } } @@ -2336,3 +2344,97 @@ int oc_directory_create(oc_str8 path) } } } + +static CVReturn oc_display_link_callback( + CVDisplayLinkRef displayLink, + const CVTimeStamp* inNow, + const CVTimeStamp* inOutputTime, + CVOptionFlags flagsIn, + CVOptionFlags* flagsOut, + void* displayLinkContext) +{ + oc_event event = { 0 }; + event.type = OC_EVENT_FRAME; + oc_queue_event(&event); + + OCWindow* ocWindow = (OCWindow*)displayLinkContext; + + CGDirectDisplayID displays[4]; + uint32_t matchingDisplayCount; + CGError err = CGGetDisplaysWithRect(ocWindow.frame, 4, displays, &matchingDisplayCount); + if(err == kCGErrorSuccess) + { + // determine which display has the greatest intersecting area + if(matchingDisplayCount > 0) + { + CGDirectDisplayID* selectedDisplay = NULL; + CGFloat selectedIntersectArea = 0.0f; + + for(uint32_t i = 0; i < matchingDisplayCount; ++i) + { + CGRect displayBounds = CGDisplayBounds(displays[i]); + CGRect intersection = CGRectIntersection(ocWindow.frame, displayBounds); + CGFloat intersectArea = intersection.size.width * intersection.size.width; + if(selectedDisplay == NULL || intersectArea < selectedIntersectArea) + { + selectedDisplay = displays + i; + selectedIntersectArea = intersectArea; + } + } + + if(selectedDisplay) + { + CGDirectDisplayID currentDisplay = CVDisplayLinkGetCurrentCGDisplay(ocWindow->displayLink); + if(currentDisplay != *selectedDisplay) + { + CVDisplayLinkSetCurrentCGDisplay(ocWindow->displayLink, *selectedDisplay); + } + } + } + } + else + { + oc_log_error("CGGetDisplaysWithRect failed with error: %d\n", err); + } + + return kCVReturnSuccess; +} + +void oc_vsync_init(void) +{ +} + +void oc_vsync_window(oc_window window) +{ + oc_window_data* windowData = oc_window_ptr_from_handle(window); + if(!windowData) + { + return; + } + + OCWindow* ocWindow = (OCWindow*)windowData->osx.nsWindow; + + CVReturn ret; + + if((ret = CVDisplayLinkCreateWithActiveCGDisplays(&ocWindow->displayLink)) != kCVReturnSuccess) + { + oc_log_error("CVDisplayLinkCreateWithActiveCGDisplays error: %d\n", ret); + } + + CGDirectDisplayID mainDisplay = CGMainDisplayID(); + + if((ret = CVDisplayLinkSetCurrentCGDisplay(ocWindow->displayLink, mainDisplay)) != kCVReturnSuccess) + { + oc_log_error("CVDisplayLinkSetCurrentCGDisplay ret: %d\n", ret); + } + + if((ret = CVDisplayLinkSetOutputCallback(ocWindow->displayLink, oc_display_link_callback, ocWindow)) != kCVReturnSuccess) + { + oc_log_error("CVDisplayLinkSetOutputCallback ret: %d\n", ret); + } + + if((ret = CVDisplayLinkStart(ocWindow->displayLink)) != kCVReturnSuccess) + { + oc_log_error("CVDisplayLinkStart ret: %d\n", ret); + } +} -- 2.25.1 From 6ae129022e816cf49decbd6b22fab3d575f9e562 Mon Sep 17 00:00:00 2001 From: Reuben Dunnington Date: Fri, 8 Sep 2023 21:35:13 -0700 Subject: [PATCH 4/5] rework vsync to be manual wait for win32 --- src/app/osx_app.m | 54 +++++------ src/app/win32_app.c | 8 -- src/app/win32_app.h | 1 - src/graphics/graphics.h | 2 +- src/graphics/graphics_surface.c | 3 + src/graphics/win32_vsync.c | 163 +++++++++++++------------------- src/runtime.c | 17 +--- 7 files changed, 99 insertions(+), 149 deletions(-) diff --git a/src/app/osx_app.m b/src/app/osx_app.m index 6449498..145bf65 100644 --- a/src/app/osx_app.m +++ b/src/app/osx_app.m @@ -1132,8 +1132,6 @@ void oc_init() [NSApp run]; [NSApp setActivationPolicy:NSApplicationActivationPolicyRegular]; [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); - if(!windowData) - { - return; - } + // TODO figure out why this causes stuttering with triple buffering - 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) - { - oc_log_error("CVDisplayLinkCreateWithActiveCGDisplays error: %d\n", ret); - } + // CVReturn 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) - { - oc_log_error("CVDisplayLinkSetCurrentCGDisplay ret: %d\n", ret); - } + // CGDirectDisplayID mainDisplay = CGMainDisplayID(); - if((ret = CVDisplayLinkSetOutputCallback(ocWindow->displayLink, oc_display_link_callback, ocWindow)) != kCVReturnSuccess) - { - oc_log_error("CVDisplayLinkSetOutputCallback ret: %d\n", ret); - } + // if((ret = CVDisplayLinkSetCurrentCGDisplay(ocWindow->displayLink, mainDisplay)) != kCVReturnSuccess) + // { + // oc_log_error("CVDisplayLinkSetCurrentCGDisplay ret: %d\n", ret); + // } - if((ret = CVDisplayLinkStart(ocWindow->displayLink)) != kCVReturnSuccess) - { - oc_log_error("CVDisplayLinkStart ret: %d\n", ret); - } + // if((ret = CVDisplayLinkSetOutputCallback(ocWindow->displayLink, oc_display_link_callback, ocWindow)) != kCVReturnSuccess) + // { + // oc_log_error("CVDisplayLinkSetOutputCallback ret: %d\n", ret); + // } + + // if((ret = CVDisplayLinkStart(ocWindow->displayLink)) != kCVReturnSuccess) + // { + // oc_log_error("CVDisplayLinkStart ret: %d\n", ret); + // } } diff --git a/src/app/win32_app.c b/src/app/win32_app.c index c286bb8..416aaf3 100644 --- a/src/app/win32_app.c +++ b/src/app/win32_app.c @@ -586,14 +586,6 @@ LRESULT oc_win32_win_proc(HWND windowHandle, UINT message, WPARAM wParam, LPARAM } break; - case OC_WM_USER_VBLANK: - { - oc_event event = { 0 }; - event.type = OC_EVENT_FRAME; - oc_queue_event(&event); - } - break; - default: { handled = false; diff --git a/src/app/win32_app.h b/src/app/win32_app.h index 7f682c4..8f41e84 100644 --- a/src/app/win32_app.h +++ b/src/app/win32_app.h @@ -49,7 +49,6 @@ typedef struct oc_win32_app_data enum OC_WM_USER { OC_WM_USER_DISPATCH_PROC = 0x0400, // WM_USER messages are defined from 0x400 to 0x7FFF - OC_WM_USER_VBLANK = 0x0401, }; #endif __WIN32_APP_H_ diff --git a/src/graphics/graphics.h b/src/graphics/graphics.h index ec69537..b9547a8 100644 --- a/src/graphics/graphics.h +++ b/src/graphics/graphics.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); 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 diff --git a/src/graphics/graphics_surface.c b/src/graphics/graphics_surface.c index 246bf53..88175d7 100644 --- a/src/graphics/graphics_surface.c +++ b/src/graphics/graphics_surface.c @@ -148,7 +148,10 @@ oc_surface oc_surface_create_for_window(oc_window window, oc_surface_api api) if(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); +#endif oc_surface_select(surfaceHandle); } return (surfaceHandle); diff --git a/src/graphics/win32_vsync.c b/src/graphics/win32_vsync.c index 0bfc4c9..7c486c0 100644 --- a/src/graphics/win32_vsync.c +++ b/src/graphics/win32_vsync.c @@ -23,89 +23,6 @@ typedef struct 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) { if(__oc_vsync_data.adapter) @@ -116,7 +33,7 @@ void oc_vsync_init(void) IDXGIFactory4* factory = NULL; { 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); if(!SUCCEEDED(hr)) @@ -163,25 +80,77 @@ void oc_vsync_init(void) } 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); } } -void oc_vsync_window(oc_window window) +void oc_vsync_wait(oc_window window) { if(__oc_vsync_data.adapter) { - oc_vsync_thread_data* data = malloc(sizeof(oc_vsync_thread_data)); - *data = (oc_vsync_thread_data){ - .adapter = __oc_vsync_data.adapter, - .window = window, - .quit = false, - }; - oc_thread_create_with_name(oc_window_vysnc_notification_thread, data, OC_STR8("vblank thread")); - } - else - { - oc_log_error("No DXGI adapter available - unable to start vsync notification thread."); + oc_window_data* windowData = oc_window_ptr_from_handle(window); + if(!windowData) + { + oc_log_error("Failed to get window ptr - assuming window was closed."); + return; + } + + RECT windowRect = { 0 }; + if(GetWindowRect(windowData->win32.hWnd, &windowRect) == FALSE) + { + 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?"); + } } } diff --git a/src/runtime.c b/src/runtime.c index da67cfe..a6dc482 100644 --- a/src/runtime.c +++ b/src/runtime.c @@ -676,19 +676,6 @@ i32 orca_runloop(void* user) } 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: break; } @@ -851,6 +838,8 @@ i32 orca_runloop(void* user) oc_surface_present(app->debugOverlay.surface); oc_arena_clear(oc_scratch()); + + oc_vsync_wait(app->window); } 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 }; app->window = oc_window_create(windowRect, OC_STR8("orca"), 0); - oc_vsync_window(app->window); - app->debugOverlay.show = false; app->debugOverlay.surface = oc_surface_create_for_window(app->window, OC_CANVAS); app->debugOverlay.canvas = oc_canvas_create(); -- 2.25.1 From 5a7182a3b90aa2746f34d0fd528a6619af087f47 Mon Sep 17 00:00:00 2001 From: Martin Fouilleul Date: Sat, 9 Sep 2023 14:34:20 +0200 Subject: [PATCH 5/5] make vsync differences between win32 and macos explicit in runtime.c --- src/app/osx_app.m | 49 ++++++++++++++++----------------- src/graphics/graphics_surface.c | 5 +--- src/runtime.c | 6 ++++ 3 files changed, 31 insertions(+), 29 deletions(-) diff --git a/src/app/osx_app.m b/src/app/osx_app.m index 145bf65..8588dac 100644 --- a/src/app/osx_app.m +++ b/src/app/osx_app.m @@ -2405,36 +2405,35 @@ void oc_vsync_init(void) void oc_vsync_wait(oc_window window) { // TODO figure out why this causes stuttering with triple buffering + oc_window_data* windowData = oc_window_ptr_from_handle(window); + if(!windowData) + { + return; + } - // oc_window_data* windowData = oc_window_ptr_from_handle(window); - // if(!windowData) - // { - // return; - // } + OCWindow* ocWindow = (OCWindow*)windowData->osx.nsWindow; - // OCWindow* ocWindow = (OCWindow*)windowData->osx.nsWindow; + CVReturn ret; - // CVReturn ret; + if((ret = CVDisplayLinkCreateWithActiveCGDisplays(&ocWindow->displayLink)) != kCVReturnSuccess) + { + oc_log_error("CVDisplayLinkCreateWithActiveCGDisplays error: %d\n", ret); + } - // if((ret = CVDisplayLinkCreateWithActiveCGDisplays(&ocWindow->displayLink)) != kCVReturnSuccess) - // { - // oc_log_error("CVDisplayLinkCreateWithActiveCGDisplays error: %d\n", ret); - // } + CGDirectDisplayID mainDisplay = CGMainDisplayID(); - // CGDirectDisplayID mainDisplay = CGMainDisplayID(); + if((ret = CVDisplayLinkSetCurrentCGDisplay(ocWindow->displayLink, mainDisplay)) != kCVReturnSuccess) + { + oc_log_error("CVDisplayLinkSetCurrentCGDisplay ret: %d\n", ret); + } - // if((ret = CVDisplayLinkSetCurrentCGDisplay(ocWindow->displayLink, mainDisplay)) != kCVReturnSuccess) - // { - // oc_log_error("CVDisplayLinkSetCurrentCGDisplay ret: %d\n", ret); - // } + if((ret = CVDisplayLinkSetOutputCallback(ocWindow->displayLink, oc_display_link_callback, ocWindow)) != kCVReturnSuccess) + { + oc_log_error("CVDisplayLinkSetOutputCallback ret: %d\n", ret); + } - // if((ret = CVDisplayLinkSetOutputCallback(ocWindow->displayLink, oc_display_link_callback, ocWindow)) != kCVReturnSuccess) - // { - // oc_log_error("CVDisplayLinkSetOutputCallback ret: %d\n", ret); - // } - - // if((ret = CVDisplayLinkStart(ocWindow->displayLink)) != kCVReturnSuccess) - // { - // oc_log_error("CVDisplayLinkStart ret: %d\n", ret); - // } + if((ret = CVDisplayLinkStart(ocWindow->displayLink)) != kCVReturnSuccess) + { + oc_log_error("CVDisplayLinkStart ret: %d\n", ret); + } } diff --git a/src/graphics/graphics_surface.c b/src/graphics/graphics_surface.c index 88175d7..b938d19 100644 --- a/src/graphics/graphics_surface.c +++ b/src/graphics/graphics_surface.c @@ -148,10 +148,7 @@ oc_surface oc_surface_create_for_window(oc_window window, oc_surface_api api) if(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); -#endif + oc_surface_swap_interval(surfaceHandle, 1); oc_surface_select(surfaceHandle); } return (surfaceHandle); diff --git a/src/runtime.c b/src/runtime.c index a6dc482..7ee08d3 100644 --- a/src/runtime.c +++ b/src/runtime.c @@ -209,6 +209,8 @@ i32 orca_surface_callback(void* user) data->surface = oc_surface_create_for_window(data->window, data->api); #if OC_PLATFORM_WINDOWS + //NOTE(martin): on windows we set all surfaces to non-synced, and do a single "manual" wait here. + // on macOS each surface is individually synced to the monitor refresh rate but don't block each other oc_surface_swap_interval(data->surface, 0); #endif @@ -839,7 +841,11 @@ i32 orca_runloop(void* user) oc_arena_clear(oc_scratch()); +#if OC_PLATFORM_WINDOWS + //NOTE(martin): on windows we set all surfaces to non-synced, and do a single "manual" wait here. + // on macOS each surface is individually synced to the monitor refresh rate but don't block each other oc_vsync_wait(app->window); +#endif } if(exports[OC_EXPORT_TERMINATE]) -- 2.25.1