From dab77da14514bad3605365f4ece7fafc5a568b64 Mon Sep 17 00:00:00 2001 From: Pavel Sountsov Date: Sun, 21 May 2023 15:51:28 -0700 Subject: [PATCH] Add a workaround for #1350 Elias investigated the issue in #1418, showing that there was some concurrent OpenGL usage between OS's internals and user code. That specific workaround did not work for me on my Mac, so as an alternative, in this commit we add an option to bypass live resize altogether. In short, the user opts into drawing halt/resume events via a config option, and ceases to draw while live resize is underway. This isn't ideal, but at least is a way to prevent the crashing behavior until we find some better idea. --- allegro5.cfg | 9 ++++++ demos/skater/src/framework.c | 1 + examples/ex_camera.c | 12 ++++++- examples/ex_resize2.c | 13 +++++++- src/macosx/osxgl.h | 4 +++ src/macosx/osxgl.m | 61 ++++++++++++++++++++++++++++++++++++ 6 files changed, 98 insertions(+), 2 deletions(-) diff --git a/allegro5.cfg b/allegro5.cfg index beec7f8939..f87bd92f1a 100644 --- a/allegro5.cfg +++ b/allegro5.cfg @@ -231,6 +231,15 @@ max_page_size = 0 # Uncomment if you want only the characters in the cache_text entry to ever be drawn # skip_cache_misses = true +[osx] + +# If set to false, then Allegro will send ALLEGRO_EVENT_DISPLAY_HALT_DRAWING +# and ALLEGRO_EVENT_DISPLAY_RESUME_DRAWING events when the user resizes a +# window. Drawing while resizing ("live resizing") has historically been buggy, +# so setting this to false allows you to opt out of this behavior and detect +# when the resize happens. +allow_live_resize = true + [compatibility] # Prior to 5.2.4 on Windows you had to manually resize the display when diff --git a/demos/skater/src/framework.c b/demos/skater/src/framework.c index 43b4f5a75a..1a01c8c65b 100644 --- a/demos/skater/src/framework.c +++ b/demos/skater/src/framework.c @@ -336,6 +336,7 @@ void run_framework(void) case ALLEGRO_EVENT_DISPLAY_RESUME_DRAWING: background_mode = false; + al_acknowledge_drawing_resume(screen); break; case ALLEGRO_EVENT_DISPLAY_SWITCH_OUT: diff --git a/examples/ex_camera.c b/examples/ex_camera.c index 2aaf262844..cd28df4b0d 100644 --- a/examples/ex_camera.c +++ b/examples/ex_camera.c @@ -474,6 +474,7 @@ int main(int argc, char **argv) ALLEGRO_TIMER *timer; ALLEGRO_EVENT_QUEUE *queue; int redraw = 0; + bool halt_drawing = false; char const *skybox_name = NULL; if (argc > 1) { @@ -489,6 +490,7 @@ int main(int argc, char **argv) al_install_keyboard(); al_install_mouse(); + al_set_config_value(al_get_system_config(), "osx", "allow_live_resize", "false"); al_set_new_display_option(ALLEGRO_SAMPLE_BUFFERS, 1, ALLEGRO_SUGGEST); al_set_new_display_option(ALLEGRO_SAMPLES, 8, ALLEGRO_SUGGEST); al_set_new_display_option(ALLEGRO_DEPTH_SIZE, 16, ALLEGRO_SUGGEST); @@ -566,12 +568,20 @@ int main(int argc, char **argv) else if (event.type == ALLEGRO_EVENT_MOUSE_BUTTON_UP) { ex.button[event.mouse.button] = 0; } + else if (event.type == ALLEGRO_EVENT_DISPLAY_HALT_DRAWING) { + halt_drawing = true; + al_acknowledge_drawing_halt(display); + } + else if (event.type == ALLEGRO_EVENT_DISPLAY_RESUME_DRAWING) { + halt_drawing = false; + al_acknowledge_drawing_resume(display); + } else if (event.type == ALLEGRO_EVENT_MOUSE_AXES) { ex.mouse_dx += event.mouse.dx; ex.mouse_dy += event.mouse.dy; } - if (redraw && al_is_event_queue_empty(queue)) { + if (!halt_drawing && redraw && al_is_event_queue_empty(queue)) { draw_scene(); al_flip_display(); diff --git a/examples/ex_resize2.c b/examples/ex_resize2.c index 7257d195ab..f5ec01d465 100644 --- a/examples/ex_resize2.c +++ b/examples/ex_resize2.c @@ -20,6 +20,7 @@ int main(int argc, char **argv) ALLEGRO_EVENT event; ALLEGRO_FONT *font; bool redraw; + bool halt_drawing; (void)argc; (void)argv; @@ -31,6 +32,7 @@ int main(int argc, char **argv) al_init_image_addon(); al_init_font_addon(); + al_set_config_value(al_get_system_config(), "osx", "allow_live_resize", "false"); al_set_new_display_flags(ALLEGRO_RESIZABLE | ALLEGRO_GENERATE_EXPOSE_EVENTS); display = al_create_display(640, 480); @@ -51,8 +53,9 @@ int main(int argc, char **argv) al_register_event_source(queue, al_get_keyboard_event_source()); redraw = true; + halt_drawing = false; while (true) { - if (redraw && al_is_event_queue_empty(queue)) { + if (!halt_drawing && redraw && al_is_event_queue_empty(queue)) { al_clear_to_color(al_map_rgb(255, 0, 0)); al_draw_scaled_bitmap(bmp, 0, 0, al_get_bitmap_width(bmp), al_get_bitmap_height(bmp), @@ -81,6 +84,14 @@ int main(int argc, char **argv) if (event.type == ALLEGRO_EVENT_DISPLAY_EXPOSE) { redraw = true; } + if (event.type == ALLEGRO_EVENT_DISPLAY_HALT_DRAWING) { + halt_drawing = true; + al_acknowledge_drawing_halt(display); + } + if (event.type == ALLEGRO_EVENT_DISPLAY_RESUME_DRAWING) { + halt_drawing = false; + al_acknowledge_drawing_resume(display); + } if (event.type == ALLEGRO_EVENT_KEY_DOWN && event.keyboard.keycode == ALLEGRO_KEY_ESCAPE) { break; diff --git a/src/macosx/osxgl.h b/src/macosx/osxgl.h index 19883641fa..c460df701f 100644 --- a/src/macosx/osxgl.h +++ b/src/macosx/osxgl.h @@ -35,6 +35,10 @@ typedef struct ALLEGRO_DISPLAY_OSX_WIN { BOOL in_fullscreen; BOOL single_buffer; CGDisplayModeRef original_mode; + BOOL send_halt_events; + ALLEGRO_MUTEX *halt_mutex; + ALLEGRO_COND *halt_cond; + BOOL halt_event_acknowledged; /* For new (10.14+) vsyncing. */ CVDisplayLinkRef display_link; ALLEGRO_MUTEX *flip_mutex; diff --git a/src/macosx/osxgl.m b/src/macosx/osxgl.m index 2663dca81e..3938aa84ab 100644 --- a/src/macosx/osxgl.m +++ b/src/macosx/osxgl.m @@ -235,6 +235,7 @@ -(void) viewDidMoveToWindow; -(void) viewWillMoveToWindow: (NSWindow*) newWindow; -(void) mouseEntered: (NSEvent*) evt; -(void) mouseExited: (NSEvent*) evt; +-(void) viewWillStartLiveResize; -(void) viewDidEndLiveResize; /* Window delegate methods */ -(void) windowDidBecomeMain:(NSNotification*) notification; @@ -519,6 +520,33 @@ -(void) viewDidChangeBackingProperties } #endif +-(void) viewWillStartLiveResize +{ + ALLEGRO_DISPLAY_OSX_WIN* dpy = (ALLEGRO_DISPLAY_OSX_WIN*) dpy_ptr; + ALLEGRO_EVENT_SOURCE *es = &dpy->parent.es; + + if (dpy->send_halt_events) { + al_lock_mutex(dpy->halt_mutex); + dpy->halt_event_acknowledged = false; + al_unlock_mutex(dpy->halt_mutex); + + _al_event_source_lock(es); + if (_al_event_source_needs_to_generate_event(es)) { + ALLEGRO_EVENT event; + event.display.type = ALLEGRO_EVENT_DISPLAY_HALT_DRAWING; + event.display.timestamp = al_get_time(); + _al_event_source_emit_event(es, &event); + } + _al_event_source_unlock(es); + + al_lock_mutex(dpy->halt_mutex); + while (!dpy->halt_event_acknowledged) { + al_wait_cond(dpy->halt_cond, dpy->halt_mutex); + } + al_unlock_mutex(dpy->halt_mutex); + } +} + -(void) viewDidEndLiveResize { [super viewDidEndLiveResize]; @@ -541,6 +569,11 @@ -(void) viewDidEndLiveResize event.display.height = NSHeight(content); _al_event_source_emit_event(es, &event); ALLEGRO_INFO("Window finished resizing %d x %d\n", event.display.width, event.display.height); + + if (dpy->send_halt_events) { + event.display.type = ALLEGRO_EVENT_DISPLAY_RESUME_DRAWING; + _al_event_source_emit_event(es, &event); + } } _al_event_source_unlock(es); dpy->old_w = NSWidth(content); @@ -1104,6 +1137,19 @@ static void init_new_vsync(ALLEGRO_DISPLAY_OSX_WIN *dpy) CVDisplayLinkStart(dpy->display_link); } +static void init_halt_events(ALLEGRO_DISPLAY_OSX_WIN *dpy) +{ + const char* value = al_get_config_value(al_get_system_config(), "osx", "allow_live_resize"); + if (value && strcmp(value, "false") == 0) { + dpy->send_halt_events = true; + } + else { + dpy->send_halt_events = false; + } + dpy->halt_mutex = al_create_mutex(); + dpy->halt_cond = al_create_cond(); +} + /* create_display_fs: * Create a fullscreen display - capture the display */ @@ -1141,6 +1187,7 @@ static void init_new_vsync(ALLEGRO_DISPLAY_OSX_WIN *dpy) _al_event_source_init(&display->es); dpy->cursor = [[NSCursor arrowCursor] retain]; dpy->display_id = CGMainDisplayID(); + init_halt_events(dpy); /* Get display ID for the requested display */ if (al_get_new_display_adapter() > 0) { @@ -1338,6 +1385,7 @@ static void init_new_vsync(ALLEGRO_DISPLAY_OSX_WIN *dpy) _al_event_source_init(&dpy->parent.es); osx_change_cursor(dpy, [NSCursor arrowCursor]); dpy->show_cursor = YES; + init_halt_events(dpy); // Set up a pixel format to describe the mode we want. osx_set_opengl_pixelformat_attributes(dpy); @@ -1519,6 +1567,7 @@ static void init_new_vsync(ALLEGRO_DISPLAY_OSX_WIN *dpy) _al_event_source_init(&display->es); _al_osx_change_cursor(dpy, [NSCursor arrowCursor]); dpy->show_cursor = YES; + init_halt_events(dpy); // Set up a pixel format to describe the mode we want. osx_set_opengl_pixelformat_attributes(dpy); @@ -1807,6 +1856,8 @@ static void destroy_display(ALLEGRO_DISPLAY* d) _al_set_current_display_only(NULL); } + al_destroy_cond(dpy->halt_cond); + al_destroy_mutex(dpy->halt_mutex); if (dpy->flip_mutex) { al_destroy_mutex(dpy->flip_mutex); al_destroy_cond(dpy->flip_cond); @@ -2497,6 +2548,15 @@ static bool set_display_flag(ALLEGRO_DISPLAY *display, int flag, bool onoff) #endif } +static void acknowledge_drawing_halt(ALLEGRO_DISPLAY *display) +{ + ALLEGRO_DISPLAY_OSX_WIN *dpy = (ALLEGRO_DISPLAY_OSX_WIN *)display; + al_lock_mutex(dpy->halt_mutex); + dpy->halt_event_acknowledged = true; + al_signal_cond(dpy->halt_cond); + al_unlock_mutex(dpy->halt_mutex); +} + ALLEGRO_DISPLAY_INTERFACE* _al_osx_get_display_driver_win(void) { static ALLEGRO_DISPLAY_INTERFACE* vt = NULL; @@ -2527,6 +2587,7 @@ static bool set_display_flag(ALLEGRO_DISPLAY *display, int flag, bool onoff) vt->set_display_flag = set_display_flag; vt->set_icons = set_icons; vt->update_render_state = _al_ogl_update_render_state; + vt->acknowledge_drawing_halt = acknowledge_drawing_halt; _al_ogl_add_drawing_functions(vt); _al_osx_add_clipboard_functions(vt); }