diff options
| author | Garett Bass <garettbass@me.com> | 2021-02-25 19:00:05 -0800 |
|---|---|---|
| committer | Garett Bass <garettbass@me.com> | 2021-02-25 19:00:05 -0800 |
| commit | d84f79130dc6d13262f429e98fe9dc60b5320665 (patch) | |
| tree | d010e7c190f741d0d435fd3da963197fd86f6247 | |
| parent | 9a880ce42ed055cdacc9d117dad7d3ed02ea7d28 (diff) | |
| parent | feab36e67fde490bdd08d5e174f524b52aad5cd0 (diff) | |
Merge branch 'master' of https://github.com/floooh/sokol
| -rw-r--r-- | .github/workflows/main.yml | 3 | ||||
| -rw-r--r-- | CHANGELOG.md | 28 | ||||
| -rw-r--r-- | README.md | 2 | ||||
| -rw-r--r-- | sokol_app.h | 140 | ||||
| -rw-r--r-- | sokol_audio.h | 94 | ||||
| -rw-r--r-- | sokol_gfx.h | 6 | ||||
| -rw-r--r-- | util/sokol_imgui.h | 120 | ||||
| -rw-r--r-- | util/sokol_shape.h | 6 |
8 files changed, 343 insertions, 56 deletions
diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 4fd79e3a..9670e317 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -185,6 +185,9 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@v1 + - uses: actions/setup-java@v1 + with: + java-version: '8' - name: prepare run: | mkdir workspace diff --git a/CHANGELOG.md b/CHANGELOG.md index 97780a30..d3c9bea2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,8 +1,30 @@ ## Updates -> NOTE: this list will usually only be updated with changes that affect the public APIs - -- **13-Fev-2021**: A new utility header [sokol_nuklear.h](https://github.com/floooh/sokol/blob/master/util/sokol_nuklear.h) +- **22-Feb-2021**: Mouse input latency in sokol_app.h's macOS backend has been + quite significantly reduced, please see the detailed explanation [in this + PR](https://github.com/floooh/sokol/pull/483). Many thanks to @randrew for + the PR! + +- **19-Feb-2021**: sokol_app.h learned some Windows-specific config options +to redirect stdout/stderr to the parent terminal or a separate console +window, and allow outputting UTF-8 encoded text. For details, search for +"WINDOWS CONSOLE OUTPUT" in +[sokol_app.h](https://github.com/floooh/sokol/blob/master/sokol_app.h). Many +thanks to @garettbass for the initial PR! + +- **17-Feb-2021**: When compiled for iOS, the sokol_audio.h CoreAudio backend now +uses the **AVAudioSession** class to activate and deactivate audio output as needed. +This fixes sokol_audio.h for iPhones (so far, sokol_audio.h accidentally only worked +for iPads). Please see [this issue](https://github.com/floooh/sokol/issues/431) for details. +A somewhat unfortunate side effect of this fix is that sokol_audio.h must now be compiled +as Objective-C when targetting iOS, also note that a new framework must be linked: ```AVFoundation```. +Many thanks to @oviano for providing the PR! + +- **14-Feb-2021**: The Dear ImGui rendering backend in [sokol_imgui.h](https://github.com/floooh/sokol/blob/master/util/sokol_imgui.h) has been rewritten to only do a single +buffer-update per frame each for vertex- and index-data. This addresses performance-problems +with sg_append_buffer() in the GL backend on some platforms (see [this issue](https://github.com/floooh/sokol/issues/399) for details. + +- **13-Feb-2021**: A new utility header [sokol_nuklear.h](https://github.com/floooh/sokol/blob/master/util/sokol_nuklear.h) has been added which implements a rendering backend for [Nuklear](https://github.com/Immediate-Mode-UI/Nuklear) on top of sokol_gfx.h. Also see the new sample [nuklear-sapp](https://floooh.github.io/sokol-html5/nuklear-sapp.html). Many thanks to **@wmerrifield** for the PR! @@ -6,7 +6,7 @@ Simple [STB-style](https://github.com/nothings/stb/blob/master/docs/stb_howto.txt) cross-platform libraries for C and C++, written in C. -[**See what's new**](https://github.com/floooh/sokol/blob/master/CHANGELOG.md) (**12-Feb-2021**: new utility header **sokol_nuklear.h**) +[**See what's new**](https://github.com/floooh/sokol/blob/master/CHANGELOG.md) (**22-Feb-2021** sokol_app.h: mouse latency reduction on macOS) ## Examples and Related Projects diff --git a/sokol_app.h b/sokol_app.h index d0e62318..7c9d67bf 100644 --- a/sokol_app.h +++ b/sokol_app.h @@ -828,6 +828,36 @@ NOTE: SOKOL_NO_ENTRY is currently not supported on Android. + WINDOWS CONSOLE OUTPUT + ====================== + On Windows, regular windowed applications don't show any stdout/stderr text + output, which can be a bit of a hassle for printf() debugging or generally + logging text to the console. Also, console output by default uses a local + codepage setting and thus international UTF-8 encoded text is printed + as garbage. + + To help with these issues, sokol_app.h can be configured at startup + via the following Windows-specific sapp_desc flags: + + sapp_desc.win32_console_utf8 (default: false) + When set to true, the output console codepage will be switched + to UTF-8 (and restored to the original codepage on exit) + + sapp_desc.win32_console_attach (default: false) + When set to true, stdout and stderr will be attached to the + console of the parent process (if the parent process actually + has a console). This means that if the application was started + in a command line window, stdout and stderr output will be printed + to the terminal, just like a regular command line program. But if + the application is started via double-click, it will behave like + a regular UI application, and stdout/stderr will not be visible. + + sapp_desc.win32_console_create (default: false) + When set to true, a new console window will be created and + stdout/stderr will be redirected to that console window. It + doesn't matter if the application is started from the command + line or via double-click. + TEMP NOTE DUMP ============== - onscreen keyboard support on Android requires Java :(, should we even bother? @@ -1120,13 +1150,17 @@ typedef struct sapp_desc { int max_dropped_files; /* max number of dropped files to process (default: 1) */ int max_dropped_file_path_length; /* max length in bytes of a dropped UTF-8 file path (default: 2048) */ + /* backend-specific options */ + bool gl_force_gles2; /* if true, setup GLES2/WebGL even if GLES3/WebGL2 is available */ + bool win32_console_utf8; /* if true, set the output console codepage to UTF-8 */ + bool win32_console_create; /* if true, attach stdout/stderr to a new console window */ + bool win32_console_attach; /* if true, attach stdout/stderr to parent process */ const char* html5_canvas_name; /* the name (id) of the HTML5 canvas element, default is "canvas" */ bool html5_canvas_resize; /* if true, the HTML5 canvas size is set to sapp_desc.width/height, otherwise canvas size is tracked */ bool html5_preserve_drawing_buffer; /* HTML5 only: whether to preserve default framebuffer content between frames */ bool html5_premultiplied_alpha; /* HTML5 only: whether the rendered pixels use premultiplied alpha convention */ bool html5_ask_leave_site; /* initial state of the internal html5_ask_leave_site flag (see sapp_html5_ask_leave_site()) */ bool ios_keyboard_resizes_canvas; /* if true, showing the iOS keyboard shrinks the canvas */ - bool gl_force_gles2; /* if true, setup GLES2/WebGL even if GLES3/WebGL2 is available */ } sapp_desc; /* HTML5 specific: request and response structs for @@ -1454,6 +1488,7 @@ inline void sapp_run(const sapp_desc& desc) { return sapp_run(&desc); } #pragma warning(disable:4055) /* 'type cast': from data pointer */ #pragma warning(disable:4505) /* unreferenced local function has been removed */ #pragma warning(disable:4115) /* /W4: 'ID3D11ModuleInstance': named type definition in parentheses (in d3d11.h) */ + #pragma warning(disable:4996) /* 'freopen': This function or variable may be unsafe. */ #endif #ifndef WIN32_LEAN_AND_MEAN #define WIN32_LEAN_AND_MEAN @@ -1464,9 +1499,14 @@ inline void sapp_run(const sapp_desc& desc) { return sapp_run(&desc); } #include <windows.h> #include <windowsx.h> #include <shellapi.h> - #if !defined(SOKOL_WIN32_FORCE_MAIN) - #pragma comment (linker, "/subsystem:windows") + #if !defined(SOKOL_NO_ENTRY) // if SOKOL_NO_ENTRY is defined, it's the applications' responsibility to use the right subsystem + #if defined(SOKOL_WIN32_FORCE_MAIN) + #pragma comment (linker, "/subsystem:console") + #else + #pragma comment (linker, "/subsystem:windows") + #endif #endif + #include <stdio.h> /* freopen() */ #pragma comment (lib, "kernel32") #pragma comment (lib, "user32") @@ -1694,6 +1734,7 @@ typedef struct { typedef struct { HWND hwnd; HDC dc; + UINT orig_codepage; LONG mouse_locked_x, mouse_locked_y; bool is_win10_or_greater; bool in_create_window; @@ -2917,9 +2958,9 @@ _SOKOL_PRIVATE void _sapp_macos_update_window_title(void) { [_sapp.macos.window setTitle: [NSString stringWithUTF8String:_sapp.window_title]]; } -_SOKOL_PRIVATE void _sapp_macos_update_mouse(void) { +_SOKOL_PRIVATE void _sapp_macos_update_mouse(NSEvent* event) { if (!_sapp.mouse.locked) { - const NSPoint mouse_pos = [_sapp.macos.window mouseLocationOutsideOfEventStream]; + const NSPoint mouse_pos = event.locationInWindow; float new_x = mouse_pos.x * _sapp.dpi_scale; float new_y = _sapp.framebuffer_height - (mouse_pos.y * _sapp.dpi_scale) - 1; /* don't update dx/dy in the very first update */ @@ -2961,19 +3002,16 @@ _SOKOL_PRIVATE void _sapp_macos_lock_mouse(bool lock) { stack with calls to sapp_show_mouse() */ if (_sapp.mouse.locked) { - [NSEvent setMouseCoalescingEnabled:NO]; CGAssociateMouseAndMouseCursorPosition(NO); CGDisplayHideCursor(kCGDirectMainDisplay); } else { CGDisplayShowCursor(kCGDirectMainDisplay); CGAssociateMouseAndMouseCursorPosition(YES); - [NSEvent setMouseCoalescingEnabled:YES]; } } _SOKOL_PRIVATE void _sapp_macos_frame(void) { - _sapp_macos_update_mouse(); _sapp_frame(); if (_sapp.quit_requested || _sapp.quit_ordered) { [_sapp.macos.window performClose:nil]; @@ -3083,6 +3121,7 @@ _SOKOL_PRIVATE void _sapp_macos_frame(void) { } [_sapp.macos.window makeKeyAndOrderFront:nil]; _sapp_macos_update_dimensions(); + [NSEvent setMouseCoalescingEnabled:NO]; } - (BOOL)applicationShouldTerminateAfterLastWindowClosed:(NSApplication*)sender { @@ -3156,7 +3195,9 @@ _SOKOL_PRIVATE void _sapp_macos_frame(void) { backing:(NSBackingStoreType)backingStoreType defer:(BOOL)flag { if (self = [super initWithContentRect:contentRect styleMask:style backing:backingStoreType defer:flag]) { - [self registerForDraggedTypes:[NSArray arrayWithObject:NSPasteboardTypeFileURL]]; + #if __MAC_OS_X_VERSION_MAX_ALLOWED >= 101300 + [self registerForDraggedTypes:[NSArray arrayWithObject:NSPasteboardTypeFileURL]]; + #endif } return self; } @@ -3170,6 +3211,7 @@ _SOKOL_PRIVATE void _sapp_macos_frame(void) { } - (BOOL)performDragOperation:(id<NSDraggingInfo>)sender { + #if __MAC_OS_X_VERSION_MAX_ALLOWED >= 101300 NSPasteboard *pboard = [sender draggingPasteboard]; if ([pboard.types containsObject:NSPasteboardTypeFileURL]) { _sapp_clear_drop_buffer(); @@ -3195,6 +3237,7 @@ _SOKOL_PRIVATE void _sapp_macos_frame(void) { } return YES; } + #endif return NO; } @end @@ -3227,8 +3270,44 @@ _SOKOL_PRIVATE void _sapp_macos_frame(void) { } #endif +_SOKOL_PRIVATE void _sapp_macos_poll_input_events() { + const NSEventMask mask = NSEventMaskLeftMouseDown | + NSEventMaskLeftMouseUp| + NSEventMaskRightMouseDown | + NSEventMaskRightMouseUp | + NSEventMaskMouseMoved | + NSEventMaskLeftMouseDragged | + NSEventMaskRightMouseDragged | + NSEventMaskMouseEntered | + NSEventMaskMouseExited | + NSEventMaskKeyDown | + NSEventMaskKeyUp | + NSEventMaskCursorUpdate | + NSEventMaskScrollWheel | + NSEventMaskTabletPoint | + NSEventMaskTabletProximity | + NSEventMaskOtherMouseDown | + NSEventMaskOtherMouseUp | + NSEventMaskOtherMouseDragged | + NSEventMaskPressure | + NSEventMaskDirectTouch; + @autoreleasepool { + for (;;) { + // NOTE: using NSDefaultRunLoopMode here causes stuttering in the GL backend, + // see: https://github.com/floooh/sokol/issues/486 + NSEvent* event = [NSApp nextEventMatchingMask:mask untilDate:nil inMode:NSEventTrackingRunLoopMode dequeue:YES]; + if (event == nil) { + break; + } + [NSApp sendEvent:event]; + } + } +} + - (void)drawRect:(NSRect)rect { _SOKOL_UNUSED(rect); + /* Catch any last-moment input events */ + _sapp_macos_poll_input_events(); _sapp_macos_frame(); #if !defined(SOKOL_METAL) [[_sapp.macos.view openGLContext] flushBuffer]; @@ -3260,6 +3339,7 @@ _SOKOL_PRIVATE void _sapp_macos_frame(void) { [super updateTrackingAreas]; } - (void)mouseEntered:(NSEvent*)event { + _sapp_macos_update_mouse(event); /* don't send mouse enter/leave while dragging (so that it behaves the same as on Windows while SetCapture is active */ @@ -3268,39 +3348,47 @@ _SOKOL_PRIVATE void _sapp_macos_frame(void) { } } - (void)mouseExited:(NSEvent*)event { + _sapp_macos_update_mouse(event); if (0 == _sapp.macos.mouse_buttons) { _sapp_macos_mouse_event(SAPP_EVENTTYPE_MOUSE_LEAVE, SAPP_MOUSEBUTTON_INVALID, _sapp_macos_mod(event.modifierFlags)); } } - (void)mouseDown:(NSEvent*)event { + _sapp_macos_update_mouse(event); _sapp_macos_mouse_event(SAPP_EVENTTYPE_MOUSE_DOWN, SAPP_MOUSEBUTTON_LEFT, _sapp_macos_mod(event.modifierFlags)); _sapp.macos.mouse_buttons |= (1<<SAPP_MOUSEBUTTON_LEFT); } - (void)mouseUp:(NSEvent*)event { + _sapp_macos_update_mouse(event); _sapp_macos_mouse_event(SAPP_EVENTTYPE_MOUSE_UP, SAPP_MOUSEBUTTON_LEFT, _sapp_macos_mod(event.modifierFlags)); _sapp.macos.mouse_buttons &= ~(1<<SAPP_MOUSEBUTTON_LEFT); } - (void)rightMouseDown:(NSEvent*)event { + _sapp_macos_update_mouse(event); _sapp_macos_mouse_event(SAPP_EVENTTYPE_MOUSE_DOWN, SAPP_MOUSEBUTTON_RIGHT, _sapp_macos_mod(event.modifierFlags)); _sapp.macos.mouse_buttons |= (1<<SAPP_MOUSEBUTTON_RIGHT); } - (void)rightMouseUp:(NSEvent*)event { + _sapp_macos_update_mouse(event); _sapp_macos_mouse_event(SAPP_EVENTTYPE_MOUSE_UP, SAPP_MOUSEBUTTON_RIGHT, _sapp_macos_mod(event.modifierFlags)); _sapp.macos.mouse_buttons &= ~(1<<SAPP_MOUSEBUTTON_RIGHT); } - (void)otherMouseDown:(NSEvent*)event { + _sapp_macos_update_mouse(event); if (2 == event.buttonNumber) { _sapp_macos_mouse_event(SAPP_EVENTTYPE_MOUSE_DOWN, SAPP_MOUSEBUTTON_MIDDLE, _sapp_macos_mod(event.modifierFlags)); _sapp.macos.mouse_buttons |= (1<<SAPP_MOUSEBUTTON_MIDDLE); } } - (void)otherMouseUp:(NSEvent*)event { + _sapp_macos_update_mouse(event); if (2 == event.buttonNumber) { _sapp_macos_mouse_event(SAPP_EVENTTYPE_MOUSE_UP, SAPP_MOUSEBUTTON_MIDDLE, _sapp_macos_mod(event.modifierFlags)); _sapp.macos.mouse_buttons &= (1<<SAPP_MOUSEBUTTON_MIDDLE); } } - (void)otherMouseDragged:(NSEvent*)event { + _sapp_macos_update_mouse(event); if (2 == event.buttonNumber) { if (_sapp.mouse.locked) { _sapp.mouse.dx = [event deltaX]; @@ -3310,6 +3398,7 @@ _SOKOL_PRIVATE void _sapp_macos_frame(void) { } } - (void)mouseMoved:(NSEvent*)event { + _sapp_macos_update_mouse(event); if (_sapp.mouse.locked) { _sapp.mouse.dx = [event deltaX]; _sapp.mouse.dy = [event deltaY]; @@ -3317,6 +3406,7 @@ _SOKOL_PRIVATE void _sapp_macos_frame(void) { _sapp_macos_mouse_event(SAPP_EVENTTYPE_MOUSE_MOVE, SAPP_MOUSEBUTTON_INVALID , _sapp_macos_mod(event.modifierFlags)); } - (void)mouseDragged:(NSEvent*)event { + _sapp_macos_update_mouse(event); if (_sapp.mouse.locked) { _sapp.mouse.dx = [event deltaX]; _sapp.mouse.dy = [event deltaY]; @@ -3324,6 +3414,7 @@ _SOKOL_PRIVATE void _sapp_macos_frame(void) { _sapp_macos_mouse_event(SAPP_EVENTTYPE_MOUSE_MOVE, SAPP_MOUSEBUTTON_INVALID , _sapp_macos_mod(event.modifierFlags)); } - (void)rightMouseDragged:(NSEvent*)event { + _sapp_macos_update_mouse(event); if (_sapp.mouse.locked) { _sapp.mouse.dx = [event deltaX]; _sapp.mouse.dy = [event deltaY]; @@ -3331,6 +3422,7 @@ _SOKOL_PRIVATE void _sapp_macos_frame(void) { _sapp_macos_mouse_event(SAPP_EVENTTYPE_MOUSE_MOVE, SAPP_MOUSEBUTTON_INVALID, _sapp_macos_mod(event.modifierFlags)); } - (void)scrollWheel:(NSEvent*)event { + _sapp_macos_update_mouse(event); if (_sapp_events_enabled()) { float dx = (float) event.scrollingDeltaX; float dy = (float) event.scrollingDeltaY; @@ -3574,7 +3666,7 @@ _SOKOL_PRIVATE void _sapp_ios_show_keyboard(bool shown) { _sapp.ios.view.device = _sapp.ios.mtl_device; _sapp.ios.view.colorPixelFormat = MTLPixelFormatBGRA8Unorm; _sapp.ios.view.depthStencilPixelFormat = MTLPixelFormatDepth32Float_Stencil8; - _sapp.ios.view.sampleCount = _sapp.sample_count; + _sapp.ios.view.sampleCount = (NSUInteger)_sapp.sample_count; if (_sapp.desc.high_dpi) { _sapp.ios.view.contentScaleFactor = 2.0; } @@ -6090,6 +6182,32 @@ _SOKOL_PRIVATE void _sapp_win32_destroy_window(void) { UnregisterClassW(L"SOKOLAPP", GetModuleHandleW(NULL)); } +_SOKOL_PRIVATE void _sapp_win32_init_console(void) { + if (_sapp.desc.win32_console_create || _sapp.desc.win32_console_attach) { + BOOL con_valid = FALSE; + if (_sapp.desc.win32_console_create) { + con_valid = AllocConsole(); + } + else if (_sapp.desc.win32_console_attach) { + con_valid = AttachConsole(ATTACH_PARENT_PROCESS); + } + if (con_valid) { + freopen("CON", "w", stdout); + freopen("CON", "w", stderr); + } + } + if (_sapp.desc.win32_console_utf8) { + _sapp.win32.orig_codepage = GetConsoleOutputCP(); + SetConsoleOutputCP(CP_UTF8); + } +} + +_SOKOL_PRIVATE void _sapp_win32_restore_console(void) { + if (_sapp.desc.win32_console_utf8) { + SetConsoleOutputCP(_sapp.win32.orig_codepage); + } +} + _SOKOL_PRIVATE void _sapp_win32_init_dpi(void) { typedef BOOL(WINAPI * SETPROCESSDPIAWARE_T)(void); @@ -6240,6 +6358,7 @@ _SOKOL_PRIVATE bool _sapp_win32_is_win10_or_greater(void) { _SOKOL_PRIVATE void _sapp_win32_run(const sapp_desc* desc) { _sapp_init_state(desc); + _sapp_win32_init_console(); _sapp.win32.is_win10_or_greater = _sapp_win32_is_win10_or_greater(); _sapp_win32_uwp_init_keytable(); _sapp_win32_uwp_utf8_to_wide(_sapp.window_title, _sapp.window_title_wide, sizeof(_sapp.window_title_wide)); @@ -6303,6 +6422,7 @@ _SOKOL_PRIVATE void _sapp_win32_run(const sapp_desc* desc) { _sapp_wgl_shutdown(); #endif _sapp_win32_destroy_window(); + _sapp_win32_restore_console(); _sapp_discard_state(); } diff --git a/sokol_audio.h b/sokol_audio.h index 5b22913d..b1a9571f 100644 --- a/sokol_audio.h +++ b/sokol_audio.h @@ -41,7 +41,8 @@ - Windows: WASAPI - Linux: ALSA (link with asound) - - macOS/iOS: CoreAudio (link with AudioToolbox) + - macOS: CoreAudio (link with AudioToolbox) + - iOS: CoreAudio+AVAudioSession (link with AudioToolbox and AVFoundation) - emscripten: WebAudio with ScriptProcessorNode - Android: OpenSLES (link with OpenSLES) @@ -307,9 +308,13 @@ THE COREAUDIO BACKEND ===================== The CoreAudio backend is selected on macOS and iOS (__APPLE__ is defined). - Since the CoreAudio API is implemented in C (not Objective-C) the + Since the CoreAudio API is implemented in C (not Objective-C) on macOS the implementation part of Sokol Audio can be included into a C source file. + However on iOS, Sokol Audio must be compiled as Objective-C due to it's + reliance on the AVAudioSession object. The iOS code path support both + being compiled with or without ARC (Automatic Reference Counting). + For thread synchronisation, the CoreAudio backend will use the pthread_mutex_* functions. @@ -509,7 +514,16 @@ inline void saudio_setup(const saudio_desc& desc) { return saudio_setup(&desc); #if defined(SOKOL_DUMMY_BACKEND) // No audio API needed for SOKOL_DUMMY_BACKEND #elif defined(__APPLE__) + #include <TargetConditionals.h> #include <AudioToolbox/AudioToolbox.h> + #if TARGET_OS_IOS + #if !defined(__cplusplus) + #if __has_feature(objc_arc) && !__has_feature(objc_arc_fields) + #error "sokol_audio.h on iOS requires __has_feature(objc_arc_field) if ARC is enabled (use a more recent compiler version)" + #endif + #endif + #include <AVFoundation/AVFoundation.h> + #endif #elif (defined(__linux__) || defined(__unix__)) && !defined(__EMSCRIPTEN__) && !defined(__ANDROID__) #define ALSA_PCM_NEW_HW_PARAMS_API #include <alsa/asoundlib.h> @@ -597,6 +611,9 @@ typedef struct { typedef struct { AudioQueueRef ca_audio_queue; + #if TARGET_OS_IOS + id ca_interruption_handler; + #endif } _saudio_backend_t; /*=== ALSA BACKEND DECLARATIONS ==============================================*/ @@ -972,6 +989,57 @@ _SOKOL_PRIVATE void _saudio_backend_shutdown(void) { }; /*=== COREAUDIO BACKEND IMPLEMENTATION =======================================*/ #elif defined(__APPLE__) +#if TARGET_OS_IOS +#if __has_feature(objc_arc) +#define _SAUDIO_OBJC_RELEASE(obj) { obj = nil; } +#else +#define _SAUDIO_OBJC_RELEASE(obj) { [obj release]; obj = nil; } +#endif + +@interface _saudio_interruption_handler : NSObject { } +@end + +@implementation _saudio_interruption_handler +-(id)init { + self = [super init]; + AVAudioSession* session = [AVAudioSession sharedInstance]; + [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(handle_interruption:) name:AVAudioSessionInterruptionNotification object:session]; + return self; +} + +-(void)dealloc { + [self remove_handler]; + #if !__has_feature(objc_arc) + [super dealloc]; + #endif +} + +-(void)remove_handler { + [[NSNotificationCenter defaultCenter] removeObserver:self name:@"AVAudioSessionInterruptionNotification" object:nil]; +} + +-(void)handle_interruption:(NSNotification*)notification { + AVAudioSession* session = [AVAudioSession sharedInstance]; + SOKOL_ASSERT(session); + NSDictionary* dict = notification.userInfo; + SOKOL_ASSERT(dict); + NSInteger type = [[dict valueForKey:AVAudioSessionInterruptionTypeKey] integerValue]; + switch (type) { + case AVAudioSessionInterruptionTypeBegan: + AudioQueuePause(_saudio.backend.ca_audio_queue); + [session setActive:false error:nil]; + break; + case AVAudioSessionInterruptionTypeEnded: + [session setActive:true error:nil]; + AudioQueueStart(_saudio.backend.ca_audio_queue, NULL); + break; + default: + break; + } +} +@end +#endif + /* NOTE: the buffer data callback is called on a separate thread! */ _SOKOL_PRIVATE void _saudio_coreaudio_callback(void* user_data, AudioQueueRef queue, AudioQueueBufferRef buffer) { _SOKOL_UNUSED(user_data); @@ -994,6 +1062,17 @@ _SOKOL_PRIVATE void _saudio_coreaudio_callback(void* user_data, AudioQueueRef qu _SOKOL_PRIVATE bool _saudio_backend_init(void) { SOKOL_ASSERT(0 == _saudio.backend.ca_audio_queue); + #if TARGET_OS_IOS + /* activate audio session */ + AVAudioSession* session = [AVAudioSession sharedInstance]; + SOKOL_ASSERT(session != nil); + [session setCategory: AVAudioSessionCategoryPlayback withOptions:AVAudioSessionCategoryOptionDefaultToSpeaker error:nil]; + [session setActive:true error:nil]; + + /* create interruption handler */ + _saudio.backend.ca_interruption_handler = [[_saudio_interruption_handler alloc] init]; + #endif + /* create an audio queue with fp32 samples */ AudioStreamBasicDescription fmt; memset(&fmt, 0, sizeof(fmt)); @@ -1033,6 +1112,17 @@ _SOKOL_PRIVATE void _saudio_backend_shutdown(void) { AudioQueueStop(_saudio.backend.ca_audio_queue, true); AudioQueueDispose(_saudio.backend.ca_audio_queue, false); _saudio.backend.ca_audio_queue = NULL; + #if TARGET_OS_IOS + /* remove interruption handler */ + if (_saudio.backend.ca_interruption_handler != nil) { + [_saudio.backend.ca_interruption_handler remove_handler]; + _SAUDIO_OBJC_RELEASE(_saudio.backend.ca_interruption_handler); + } + /* deactivate audio session */ + AVAudioSession* session = [AVAudioSession sharedInstance]; + SOKOL_ASSERT(session); + [session setActive:false error:nil];; + #endif } /*=== ALSA BACKEND IMPLEMENTATION ============================================*/ diff --git a/sokol_gfx.h b/sokol_gfx.h index d9a9e4da..87276666 100644 --- a/sokol_gfx.h +++ b/sokol_gfx.h @@ -99,17 +99,17 @@ --- start rendering to the default frame buffer with: - sg_begin_default_pass(const sg_pass_action* actions, int width, int height) + sg_begin_default_pass(const sg_pass_action* action, int width, int height) ...or alternatively with: - sg_begin_default_passf(const sg_pass_action* actions, float width, float height) + sg_begin_default_passf(const sg_pass_action* action, float width, float height) ...which takes the framebuffer width and height as float values. --- or start rendering to an offscreen framebuffer with: - sg_begin_pass(sg_pass pass, const sg_pass_action* actions) + sg_begin_pass(sg_pass pass, const sg_pass_action* action) --- set the pipeline state for the next draw call with: diff --git a/util/sokol_imgui.h b/util/sokol_imgui.h index 3440e802..c0b94589 100644 --- a/util/sokol_imgui.h +++ b/util/sokol_imgui.h @@ -42,6 +42,8 @@ to override defaults: SOKOL_ASSERT(c) - your own assert macro (default: assert(c)) + SOKOL_MALLOC(s) - your own malloc function (default: malloc(s)) + SOKOL_FREE(p) - your own free function (default: free(p)) SOKOL_IMGUI_API_DECL- public function declaration prefix (default: extern) SOKOL_API_DECL - same as SOKOL_IMGUI_API_DECL SOKOL_API_IMPL - public function implementation prefix (default: -) @@ -286,6 +288,11 @@ inline void simgui_setup(const simgui_desc_t& desc) { return simgui_setup(&desc) #include <assert.h> #define SOKOL_ASSERT(c) assert(c) #endif +#ifndef SOKOL_MALLOC + #include <stdlib.h> + #define SOKOL_MALLOC(s) malloc(s) + #define SOKOL_FREE(p) free(p) +#endif #ifndef _SOKOL_PRIVATE #if defined(__GNUC__) || defined(__clang__) #define _SOKOL_PRIVATE __attribute__((unused)) static @@ -312,6 +319,10 @@ typedef struct { sg_shader shd; sg_pipeline pip; bool is_osx; // return true if running on OSX (or HTML5 OSX), needed for copy/paste + + sg_range vertices; + sg_range indices; + #if !defined(SOKOL_IMGUI_NO_SOKOL_APP) bool btn_down[SAPP_MAX_MOUSEBUTTONS]; bool btn_up[SAPP_MAX_MOUSEBUTTONS]; @@ -1617,6 +1628,15 @@ SOKOL_API_IMPL void simgui_setup(const simgui_desc_t* desc) { since sokol_gfx.h will do its own default-value handling */ + /* allocate an intermediate vertex- and index-buffer */ + SOKOL_ASSERT(_simgui.desc.max_vertices > 0); + _simgui.vertices.size = (size_t)_simgui.desc.max_vertices * sizeof(ImDrawVert); + _simgui.vertices.ptr = SOKOL_MALLOC(_simgui.vertices.size); + SOKOL_ASSERT(_simgui.vertices.ptr); + _simgui.indices.size = (size_t)_simgui.desc.max_vertices * 3 * sizeof(ImDrawIdx); + _simgui.indices.ptr = SOKOL_MALLOC(_simgui.indices.size); + SOKOL_ASSERT(_simgui.indices.ptr); + /* initialize Dear ImGui */ #if defined(__cplusplus) ImGui::CreateContext(); @@ -1672,7 +1692,7 @@ SOKOL_API_IMPL void simgui_setup(const simgui_desc_t* desc) { sg_buffer_desc vb_desc; memset(&vb_desc, 0, sizeof(vb_desc)); vb_desc.usage = SG_USAGE_STREAM; - vb_desc.size = (size_t)_simgui.desc.max_vertices * sizeof(ImDrawVert); + vb_desc.size = _simgui.vertices.size; vb_desc.label = "sokol-imgui-vertices"; _simgui.vbuf = sg_make_buffer(&vb_desc); @@ -1680,7 +1700,7 @@ SOKOL_API_IMPL void simgui_setup(const simgui_desc_t* desc) { memset(&ib_desc, 0, sizeof(ib_desc)); ib_desc.type = SG_BUFFERTYPE_INDEXBUFFER; ib_desc.usage = SG_USAGE_STREAM; - ib_desc.size = (size_t)_simgui.desc.max_vertices * 3 * sizeof(uint16_t); + ib_desc.size = _simgui.indices.size; ib_desc.label = "sokol-imgui-indices"; _simgui.ibuf = sg_make_buffer(&ib_desc); @@ -1814,6 +1834,10 @@ SOKOL_API_IMPL void simgui_shutdown(void) { sg_destroy_buffer(_simgui.ibuf); sg_destroy_buffer(_simgui.vbuf); sg_pop_debug_group(); + SOKOL_ASSERT(_simgui.vertices.ptr); + SOKOL_FREE((void*)_simgui.vertices.ptr); + SOKOL_ASSERT(_simgui.indices.ptr); + SOKOL_FREE((void*)_simgui.indices.ptr); } #if !defined(SOKOL_IMGUI_NO_SOKOL_APP) @@ -1887,10 +1911,58 @@ SOKOL_API_IMPL void simgui_render(void) { if (draw_data->CmdListsCount == 0) { return; } + /* copy vertices and indices into an intermediate buffer so that + they can be updated with a single sg_update_buffer() call each + (sg_append_buffer() has performance problems on some GL platforms), + also keep track of valid number of command lists in case of a + buffer overflow + */ + size_t all_vtx_size = 0; + size_t all_idx_size = 0; + int cmd_list_count = 0; + for (int cl_index = 0; cl_index < draw_data->CmdListsCount; cl_index++, cmd_list_count++) { + ImDrawList* cl = draw_data->CmdLists[cl_index]; + #if defined(__cplusplus) + const size_t vtx_size = cl->VtxBuffer.size() * sizeof(ImDrawVert); + const size_t idx_size = cl->IdxBuffer.size() * sizeof(ImDrawIdx); + const ImDrawVert* vtx_ptr = &cl->VtxBuffer.front(); + const ImDrawIdx* idx_ptr = &cl->IdxBuffer.front(); + #else + const size_t vtx_size = (size_t)cl->VtxBuffer.Size * sizeof(ImDrawVert); + const size_t idx_size = (size_t)cl->IdxBuffer.Size * sizeof(ImDrawIdx); + const ImDrawVert* vtx_ptr = cl->VtxBuffer.Data; + const ImDrawIdx* idx_ptr = cl->IdxBuffer.Data; + #endif + + /* check for buffer overflow */ + if (((all_vtx_size + vtx_size) > _simgui.vertices.size) || + ((all_idx_size + idx_size) > _simgui.indices.size)) + { + break; + } + + /* copy vertices and indices into common buffers */ + void* dst_vtx_ptr = (void*) (((uint8_t*)_simgui.vertices.ptr) + all_vtx_size); + void* dst_idx_ptr = (void*) (((uint8_t*)_simgui.indices.ptr) + all_idx_size); + memcpy(dst_vtx_ptr, vtx_ptr, vtx_size); + memcpy(dst_idx_ptr, idx_ptr, idx_size); + all_vtx_size += vtx_size; + all_idx_size += idx_size; + } + if (0 == cmd_list_count) { + return; + } - /* render the ImGui command list */ + /* update the sokol-gfx vertex- and index-buffer */ sg_push_debug_group("sokol-imgui"); + sg_range vtx_data = _simgui.vertices; + vtx_data.size = all_vtx_size; + sg_range idx_data = _simgui.indices; + idx_data.size = all_idx_size; + sg_update_buffer(_simgui.vbuf, &vtx_data); + sg_update_buffer(_simgui.ibuf, &idx_data); + /* render the ImGui command list */ const float dpi_scale = _simgui.desc.dpi_scale; const int fb_width = (int) (io->DisplaySize.x * dpi_scale); const int fb_height = (int) (io->DisplaySize.y * dpi_scale); @@ -1911,38 +1983,9 @@ SOKOL_API_IMPL void simgui_render(void) { bind.fs_images[0].id = (uint32_t)(uintptr_t)tex_id; int vb_offset = 0; int ib_offset = 0; - for (int cl_index = 0; cl_index < draw_data->CmdListsCount; cl_index++) { - ImDrawList* cl = draw_data->CmdLists[cl_index]; + for (int cl_index = 0; cl_index < cmd_list_count; cl_index++) { + const ImDrawList* cl = draw_data->CmdLists[cl_index]; - /* append vertices and indices to buffers, record start offsets in draw state */ - #if defined(__cplusplus) - const size_t vtx_size = cl->VtxBuffer.size() * sizeof(ImDrawVert); - const size_t idx_size = cl->IdxBuffer.size() * sizeof(ImDrawIdx); - const ImDrawVert* vtx_ptr = &cl->VtxBuffer.front(); - const ImDrawIdx* idx_ptr = &cl->IdxBuffer.front(); - #else - const size_t vtx_size = (size_t)cl->VtxBuffer.Size * sizeof(ImDrawVert); - const size_t idx_size = (size_t)cl->IdxBuffer.Size * sizeof(ImDrawIdx); - const ImDrawVert* vtx_ptr = cl->VtxBuffer.Data; - const ImDrawIdx* idx_ptr = cl->IdxBuffer.Data; - #endif - if (vtx_ptr) { - const sg_range vtx_range = { vtx_ptr, vtx_size }; - vb_offset = sg_append_buffer(bind.vertex_buffers[0], &vtx_range); - } - if (idx_ptr) { - const sg_range idx_range = { idx_ptr, idx_size }; - ib_offset = sg_append_buffer(bind.index_buffer, &idx_range); - } - /* don't render anything if the buffer is in overflow state (this is also - checked internally in sokol_gfx, draw calls that attempt to draw with - overflowed buffers will be silently dropped) - */ - if (sg_query_buffer_overflow(bind.vertex_buffers[0]) || - sg_query_buffer_overflow(bind.index_buffer)) - { - break; - } bind.vertex_buffer_offsets[0] = vb_offset; bind.index_buffer_offset = ib_offset; sg_apply_bindings(&bind); @@ -1981,6 +2024,15 @@ SOKOL_API_IMPL void simgui_render(void) { } base_element += (int)pcmd->ElemCount; } + #if defined(__cplusplus) + const size_t vtx_size = cl->VtxBuffer.size() * sizeof(ImDrawVert); + const size_t idx_size = cl->IdxBuffer.size() * sizeof(ImDrawIdx); + #else + const size_t vtx_size = (size_t)cl->VtxBuffer.Size * sizeof(ImDrawVert); + const size_t idx_size = (size_t)cl->IdxBuffer.Size * sizeof(ImDrawIdx); + #endif + vb_offset += (int)vtx_size; + ib_offset += (int)idx_size; } sg_apply_viewport(0, 0, fb_width, fb_height, true); sg_apply_scissor_rect(0, 0, fb_width, fb_height, true); diff --git a/util/sokol_shape.h b/util/sokol_shape.h index be2a9c6c..195f6232 100644 --- a/util/sokol_shape.h +++ b/util/sokol_shape.h @@ -36,7 +36,7 @@ ================ sokol_shape.h creates vertices and indices for simple shapes and builds structs which can be plugged into sokol-gfx resource - creation descriptor structs. + creation functions: The following shape types are supported: @@ -154,7 +154,7 @@ You can also provide additional creation parameters, like a common vertex color, a debug-helper to randomize colors, tell the shape builder function to merge the new shape with the previous shape into the same draw-element-range, - or a 4x4 transform matrix to move, rotate and scale the generated matrices: + or a 4x4 transform matrix to move, rotate and scale the generated vertices: ```c sshape_buffer_t buf = ...; @@ -179,7 +179,7 @@ assert(buf.valid); ``` - The following helper functions are offered to build a packed + The following helper functions can be used to build a packed color value or to convert from external matrix types: ```c |