From 7f9840cecfe5d10d02d272447cd5c8765e4f50e3 Mon Sep 17 00:00:00 2001 From: Camilla Berglund Date: Sun, 9 Oct 2016 01:39:31 +0200 Subject: [PATCH 01/40] Cocoa: Fix macOS 10.12 deprecation warnings --- deps/tinycthread.h | 4 +++- src/cocoa_window.m | 46 +++++++++++++++++++++++++--------------------- src/nsgl_context.m | 8 ++++++++ 3 files changed, 36 insertions(+), 22 deletions(-) diff --git a/deps/tinycthread.h b/deps/tinycthread.h index 6da30d703..42958c393 100644 --- a/deps/tinycthread.h +++ b/deps/tinycthread.h @@ -123,7 +123,9 @@ typedef int _tthread_clockid_t; /* Emulate clock_gettime */ int _tthread_clock_gettime(clockid_t clk_id, struct timespec *ts); #define clock_gettime _tthread_clock_gettime -#define CLOCK_REALTIME 0 +#ifndef CLOCK_REALTIME + #define CLOCK_REALTIME 0 +#endif #endif diff --git a/src/cocoa_window.m b/src/cocoa_window.m index 57032b56c..ddd3edada 100644 --- a/src/cocoa_window.m +++ b/src/cocoa_window.m @@ -63,14 +63,15 @@ static NSUInteger getStyleMask(_GLFWwindow* window) NSUInteger styleMask = 0; if (window->monitor || !window->decorated) - styleMask |= NSBorderlessWindowMask; + styleMask |= NSWindowStyleMaskBorderless; else { - styleMask |= NSTitledWindowMask | NSClosableWindowMask | - NSMiniaturizableWindowMask; + styleMask |= NSWindowStyleMaskTitled | + NSWindowStyleMaskClosable | + NSWindowStyleMaskMiniaturizable; if (window->resizable) - styleMask |= NSResizableWindowMask; + styleMask |= NSWindowStyleMaskResizable; } return styleMask; @@ -150,13 +151,13 @@ static int translateFlags(NSUInteger flags) { int mods = 0; - if (flags & NSShiftKeyMask) + if (flags & NSEventModifierFlagShift) mods |= GLFW_MOD_SHIFT; - if (flags & NSControlKeyMask) + if (flags & NSEventModifierFlagControl) mods |= GLFW_MOD_CONTROL; - if (flags & NSAlternateKeyMask) + if (flags & NSEventModifierFlagOption) mods |= GLFW_MOD_ALT; - if (flags & NSCommandKeyMask) + if (flags & NSEventModifierFlagCommand) mods |= GLFW_MOD_SUPER; return mods; @@ -180,16 +181,16 @@ static NSUInteger translateKeyToModifierFlag(int key) { case GLFW_KEY_LEFT_SHIFT: case GLFW_KEY_RIGHT_SHIFT: - return NSShiftKeyMask; + return NSEventModifierFlagShift; case GLFW_KEY_LEFT_CONTROL: case GLFW_KEY_RIGHT_CONTROL: - return NSControlKeyMask; + return NSEventModifierFlagControl; case GLFW_KEY_LEFT_ALT: case GLFW_KEY_RIGHT_ALT: - return NSAlternateKeyMask; + return NSEventModifierFlagOption; case GLFW_KEY_LEFT_SUPER: case GLFW_KEY_RIGHT_SUPER: - return NSCommandKeyMask; + return NSEventModifierFlagCommand; } return 0; @@ -565,7 +566,7 @@ static const NSRange kEmptyRange = { NSNotFound, 0 }; { int action; const unsigned int modifierFlags = - [event modifierFlags] & NSDeviceIndependentModifierFlagsMask; + [event modifierFlags] & NSEventModifierFlagDeviceIndependentFlagsMask; const int key = translateKey([event keyCode]); const int mods = translateFlags(modifierFlags); const NSUInteger keyFlag = translateKeyToModifierFlag(key); @@ -760,7 +761,7 @@ static const NSRange kEmptyRange = { NSNotFound, 0 }; - (BOOL)canBecomeKeyWindow { - // Required for NSBorderlessWindowMask windows + // Required for NSWindowStyleMaskBorderless windows return YES; } @@ -781,8 +782,11 @@ static const NSRange kEmptyRange = { NSNotFound, 0 }; // down the command key don't get sent to the key window. - (void)sendEvent:(NSEvent *)event { - if ([event type] == NSKeyUp && ([event modifierFlags] & NSCommandKeyMask)) + if ([event type] == NSEventTypeKeyUp && + ([event modifierFlags] & NSEventModifierFlagCommand)) + { [[self keyWindow] sendEvent:event]; + } else [super sendEvent:event]; } @@ -866,7 +870,7 @@ static void createMenuBar(void) [[appMenu addItemWithTitle:@"Hide Others" action:@selector(hideOtherApplications:) keyEquivalent:@"h"] - setKeyEquivalentModifierMask:NSAlternateKeyMask | NSCommandKeyMask]; + setKeyEquivalentModifierMask:NSEventModifierFlagOption | NSEventModifierFlagCommand]; [appMenu addItemWithTitle:@"Show All" action:@selector(unhideAllApplications:) keyEquivalent:@""]; @@ -898,7 +902,7 @@ static void createMenuBar(void) [[windowMenu addItemWithTitle:@"Enter Full Screen" action:@selector(toggleFullScreen:) keyEquivalent:@"f"] - setKeyEquivalentModifierMask:NSControlKeyMask | NSCommandKeyMask]; + setKeyEquivalentModifierMask:NSEventModifierFlagControl | NSEventModifierFlagCommand]; // Prior to Snow Leopard, we need to use this oddly-named semi-private API // to get the application menu working properly. @@ -1358,7 +1362,7 @@ void _glfwPlatformPollEvents(void) { for (;;) { - NSEvent* event = [NSApp nextEventMatchingMask:NSAnyEventMask + NSEvent* event = [NSApp nextEventMatchingMask:NSEventMaskAny untilDate:[NSDate distantPast] inMode:NSDefaultRunLoopMode dequeue:YES]; @@ -1377,7 +1381,7 @@ void _glfwPlatformWaitEvents(void) // I wanted to pass NO to dequeue:, and rely on PollEvents to // dequeue and send. For reasons not at all clear to me, passing // NO to dequeue: causes this method never to return. - NSEvent *event = [NSApp nextEventMatchingMask:NSAnyEventMask + NSEvent *event = [NSApp nextEventMatchingMask:NSEventMaskAny untilDate:[NSDate distantFuture] inMode:NSDefaultRunLoopMode dequeue:YES]; @@ -1389,7 +1393,7 @@ void _glfwPlatformWaitEvents(void) void _glfwPlatformWaitEventsTimeout(double timeout) { NSDate* date = [NSDate dateWithTimeIntervalSinceNow:timeout]; - NSEvent* event = [NSApp nextEventMatchingMask:NSAnyEventMask + NSEvent* event = [NSApp nextEventMatchingMask:NSEventMaskAny untilDate:date inMode:NSDefaultRunLoopMode dequeue:YES]; @@ -1402,7 +1406,7 @@ void _glfwPlatformWaitEventsTimeout(double timeout) void _glfwPlatformPostEmptyEvent(void) { NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init]; - NSEvent* event = [NSEvent otherEventWithType:NSApplicationDefined + NSEvent* event = [NSEvent otherEventWithType:NSEventTypeApplicationDefined location:NSMakePoint(0, 0) modifierFlags:0 timestamp:0 diff --git a/src/nsgl_context.m b/src/nsgl_context.m index 454a77391..6c1f5a208 100644 --- a/src/nsgl_context.m +++ b/src/nsgl_context.m @@ -225,7 +225,15 @@ GLFWbool _glfwCreateContextNSGL(_GLFWwindow* window, ADD_ATTR2(NSOpenGLPFAStencilSize, fbconfig->stencilBits); if (fbconfig->stereo) + { +#if MAC_OS_X_VERSION_MAX_ALLOWED >= 101200 + _glfwInputError(GLFW_FORMAT_UNAVAILABLE, + "NSGL: Stereo rendering is deprecated"); + return GLFW_FALSE; +#else ADD_ATTR(NSOpenGLPFAStereo); +#endif + } if (fbconfig->doublebuffer) ADD_ATTR(NSOpenGLPFADoubleBuffer); From 73ddbc3acdd2c7765d4ae953dd9b31da93acf433 Mon Sep 17 00:00:00 2001 From: Camilla Berglund Date: Sun, 9 Oct 2016 12:37:01 +0200 Subject: [PATCH 02/40] Cocoa: Add workaround for hasty deprecation --- src/cocoa_window.m | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/src/cocoa_window.m b/src/cocoa_window.m index ddd3edada..cad8715d4 100644 --- a/src/cocoa_window.m +++ b/src/cocoa_window.m @@ -32,6 +32,23 @@ // Needed for _NSGetProgname #include +// HACK: The 10.12 SDK adds new symbols and immediately deprecates the old ones +#if MAC_OS_X_VERSION_MAX_ALLOWED < 101200 + #define NSWindowStyleMaskBorderless NSBorderlessWindowMask + #define NSWindowStyleMaskClosable NSClosableWindowMask + #define NSWindowStyleMaskMiniaturizable NSMiniaturizableWindowMask + #define NSWindowStyleMaskResizable NSResizableWindowMask + #define NSWindowStyleMaskTitled NSTitledWindowMask + #define NSEventModifierFlagCommand NSCommandKeyMask + #define NSEventModifierFlagControl NSControlKeyMask + #define NSEventModifierFlagOption NSAlternateKeyMask + #define NSEventModifierFlagShift NSShiftKeyMask + #define NSEventModifierFlagDeviceIndependentFlagsMask NSDeviceIndependentModifierFlagsMask + #define NSEventMaskAny NSAnyEventMask + #define NSEventTypeApplicationDefined NSApplicationDefined + #define NSEventTypeKeyUp NSKeyUp +#endif + // Returns the specified standard cursor // From 1c8a74b661f675bab56f996b7ebb73f3cd90a269 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Sat, 1 Oct 2016 20:01:01 +0100 Subject: [PATCH 03/40] Wayland: Properly destroy all bound objects. --- src/wl_init.c | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/src/wl_init.c b/src/wl_init.c index a4be0e8ce..415f62e3f 100644 --- a/src/wl_init.c +++ b/src/wl_init.c @@ -647,12 +647,29 @@ void _glfwPlatformTerminate(void) wl_cursor_theme_destroy(_glfw.wl.cursorTheme); if (_glfw.wl.cursorSurface) wl_surface_destroy(_glfw.wl.cursorSurface); + if (_glfw.wl.compositor) + wl_compositor_destroy(_glfw.wl.compositor); + if (_glfw.wl.shm) + wl_shm_destroy(_glfw.wl.shm); + if (_glfw.wl.shell) + wl_shell_destroy(_glfw.wl.shell); + if (_glfw.wl.pointer) + wl_pointer_destroy(_glfw.wl.pointer); + if (_glfw.wl.keyboard) + wl_keyboard_destroy(_glfw.wl.keyboard); + if (_glfw.wl.seat) + wl_seat_destroy(_glfw.wl.seat); + if (_glfw.wl.relativePointerManager) + zwp_relative_pointer_manager_v1_destroy(_glfw.wl.relativePointerManager); + if (_glfw.wl.pointerConstraints) + zwp_pointer_constraints_v1_destroy(_glfw.wl.pointerConstraints); if (_glfw.wl.registry) wl_registry_destroy(_glfw.wl.registry); if (_glfw.wl.display) + { wl_display_flush(_glfw.wl.display); - if (_glfw.wl.display) wl_display_disconnect(_glfw.wl.display); + } } const char* _glfwPlatformGetVersionString(void) From be935debe9ceedf939f9e41e647c2674f7626610 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Tue, 11 Oct 2016 01:43:53 +0100 Subject: [PATCH 04/40] Wayland: Properly unref all xkbcommon objects. --- src/wl_init.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/wl_init.c b/src/wl_init.c index 415f62e3f..0affe943f 100644 --- a/src/wl_init.c +++ b/src/wl_init.c @@ -643,6 +643,10 @@ void _glfwPlatformTerminate(void) _glfwTerminateJoysticksLinux(); _glfwTerminateThreadLocalStoragePOSIX(); + xkb_keymap_unref(_glfw.wl.xkb.keymap); + xkb_state_unref(_glfw.wl.xkb.state); + xkb_context_unref(_glfw.wl.xkb.context); + if (_glfw.wl.cursorTheme) wl_cursor_theme_destroy(_glfw.wl.cursorTheme); if (_glfw.wl.cursorSurface) From aa10ec6e455a4c767c5feed83e2e8623adb3e775 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Tue, 11 Oct 2016 01:44:22 +0100 Subject: [PATCH 05/40] Wayland: Replace all deprecated xkbcommon aliases. --- src/wl_init.c | 26 ++++++++++++++------------ 1 file changed, 14 insertions(+), 12 deletions(-) diff --git a/src/wl_init.c b/src/wl_init.c index 0affe943f..2a7a4fbb9 100644 --- a/src/wl_init.c +++ b/src/wl_init.c @@ -186,10 +186,10 @@ static void keyboardHandleKeymap(void* data, return; } - keymap = xkb_map_new_from_string(_glfw.wl.xkb.context, - mapStr, - XKB_KEYMAP_FORMAT_TEXT_V1, - 0); + keymap = xkb_keymap_new_from_string(_glfw.wl.xkb.context, + mapStr, + XKB_KEYMAP_FORMAT_TEXT_V1, + 0); munmap(mapStr, size); close(fd); @@ -205,7 +205,7 @@ static void keyboardHandleKeymap(void* data, { _glfwInputError(GLFW_PLATFORM_ERROR, "Wayland: Failed to create XKB state"); - xkb_map_unref(keymap); + xkb_keymap_unref(keymap); return; } @@ -215,13 +215,13 @@ static void keyboardHandleKeymap(void* data, _glfw.wl.xkb.state = state; _glfw.wl.xkb.control_mask = - 1 << xkb_map_mod_get_index(_glfw.wl.xkb.keymap, "Control"); + 1 << xkb_keymap_mod_get_index(_glfw.wl.xkb.keymap, "Control"); _glfw.wl.xkb.alt_mask = - 1 << xkb_map_mod_get_index(_glfw.wl.xkb.keymap, "Mod1"); + 1 << xkb_keymap_mod_get_index(_glfw.wl.xkb.keymap, "Mod1"); _glfw.wl.xkb.shift_mask = - 1 << xkb_map_mod_get_index(_glfw.wl.xkb.keymap, "Shift"); + 1 << xkb_keymap_mod_get_index(_glfw.wl.xkb.keymap, "Shift"); _glfw.wl.xkb.super_mask = - 1 << xkb_map_mod_get_index(_glfw.wl.xkb.keymap, "Mod4"); + 1 << xkb_keymap_mod_get_index(_glfw.wl.xkb.keymap, "Mod4"); } static void keyboardHandleEnter(void* data, @@ -265,7 +265,7 @@ static void inputChar(_GLFWwindow* window, uint32_t key) const xkb_keysym_t *syms; code = key + 8; - num_syms = xkb_key_get_syms(_glfw.wl.xkb.state, code, &syms); + num_syms = xkb_state_key_get_syms(_glfw.wl.xkb.state, code, &syms); if (num_syms == 1) { @@ -327,8 +327,10 @@ static void keyboardHandleModifiers(void* data, group); mask = xkb_state_serialize_mods(_glfw.wl.xkb.state, - XKB_STATE_DEPRESSED | - XKB_STATE_LATCHED); + XKB_STATE_MODS_DEPRESSED | + XKB_STATE_LAYOUT_DEPRESSED | + XKB_STATE_MODS_LATCHED | + XKB_STATE_LAYOUT_LATCHED); if (mask & _glfw.wl.xkb.control_mask) modifiers |= GLFW_MOD_CONTROL; if (mask & _glfw.wl.xkb.alt_mask) From a49601ba875245fa6789d699d51031d1dec80b93 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Sat, 1 Oct 2016 20:05:29 +0100 Subject: [PATCH 06/40] Wayland: Rename snake_case identifiers to camelCase for consistency --- src/wl_init.c | 34 +++++++++++++++++----------------- src/wl_monitor.c | 12 ++++++------ src/wl_platform.h | 12 ++++++------ src/wl_window.c | 46 +++++++++++++++++++++++----------------------- 4 files changed, 52 insertions(+), 52 deletions(-) diff --git a/src/wl_init.c b/src/wl_init.c index 2a7a4fbb9..4627c599b 100644 --- a/src/wl_init.c +++ b/src/wl_init.c @@ -128,7 +128,7 @@ static void pointerHandleAxis(void* data, wl_fixed_t value) { _GLFWwindow* window = _glfw.wl.pointerFocus; - double scroll_factor; + double scrollFactor; double x, y; if (!window) @@ -137,17 +137,17 @@ static void pointerHandleAxis(void* data, /* Wayland scroll events are in pointer motion coordinate space (think * two finger scroll). The factor 10 is commonly used to convert to * "scroll step means 1.0. */ - scroll_factor = 1.0/10.0; + scrollFactor = 1.0/10.0; switch (axis) { case WL_POINTER_AXIS_HORIZONTAL_SCROLL: - x = wl_fixed_to_double(value) * scroll_factor; + x = wl_fixed_to_double(value) * scrollFactor; y = 0.0; break; case WL_POINTER_AXIS_VERTICAL_SCROLL: x = 0.0; - y = wl_fixed_to_double(value) * scroll_factor; + y = wl_fixed_to_double(value) * scrollFactor; break; default: break; @@ -214,13 +214,13 @@ static void keyboardHandleKeymap(void* data, _glfw.wl.xkb.keymap = keymap; _glfw.wl.xkb.state = state; - _glfw.wl.xkb.control_mask = + _glfw.wl.xkb.controlMask = 1 << xkb_keymap_mod_get_index(_glfw.wl.xkb.keymap, "Control"); - _glfw.wl.xkb.alt_mask = + _glfw.wl.xkb.altMask = 1 << xkb_keymap_mod_get_index(_glfw.wl.xkb.keymap, "Mod1"); - _glfw.wl.xkb.shift_mask = + _glfw.wl.xkb.shiftMask = 1 << xkb_keymap_mod_get_index(_glfw.wl.xkb.keymap, "Shift"); - _glfw.wl.xkb.super_mask = + _glfw.wl.xkb.superMask = 1 << xkb_keymap_mod_get_index(_glfw.wl.xkb.keymap, "Mod4"); } @@ -260,14 +260,14 @@ static int toGLFWKeyCode(uint32_t key) static void inputChar(_GLFWwindow* window, uint32_t key) { - uint32_t code, num_syms; + uint32_t code, numSyms; long cp; const xkb_keysym_t *syms; code = key + 8; - num_syms = xkb_state_key_get_syms(_glfw.wl.xkb.state, code, &syms); + numSyms = xkb_state_key_get_syms(_glfw.wl.xkb.state, code, &syms); - if (num_syms == 1) + if (numSyms == 1) { cp = _glfwKeySym2Unicode(syms[0]); if (cp != -1) @@ -331,13 +331,13 @@ static void keyboardHandleModifiers(void* data, XKB_STATE_LAYOUT_DEPRESSED | XKB_STATE_MODS_LATCHED | XKB_STATE_LAYOUT_LATCHED); - if (mask & _glfw.wl.xkb.control_mask) + if (mask & _glfw.wl.xkb.controlMask) modifiers |= GLFW_MOD_CONTROL; - if (mask & _glfw.wl.xkb.alt_mask) + if (mask & _glfw.wl.xkb.altMask) modifiers |= GLFW_MOD_ALT; - if (mask & _glfw.wl.xkb.shift_mask) + if (mask & _glfw.wl.xkb.shiftMask) modifiers |= GLFW_MOD_SHIFT; - if (mask & _glfw.wl.xkb.super_mask) + if (mask & _glfw.wl.xkb.superMask) modifiers |= GLFW_MOD_SUPER; _glfw.wl.xkb.modifiers = modifiers; } @@ -389,10 +389,10 @@ static void registryHandleGlobal(void* data, { if (strcmp(interface, "wl_compositor") == 0) { - _glfw.wl.wl_compositor_version = min(3, version); + _glfw.wl.compositorVersion = min(3, version); _glfw.wl.compositor = wl_registry_bind(registry, name, &wl_compositor_interface, - _glfw.wl.wl_compositor_version); + _glfw.wl.compositorVersion); } else if (strcmp(interface, "wl_shm") == 0) { diff --git a/src/wl_monitor.c b/src/wl_monitor.c index d6a5a3f46..7a830513b 100644 --- a/src/wl_monitor.c +++ b/src/wl_monitor.c @@ -102,7 +102,7 @@ static void scale(void* data, monitor->wl.scale = factor; } -static const struct wl_output_listener output_listener = { +static const struct wl_output_listener outputListener = { geometry, mode, done, @@ -118,10 +118,10 @@ void _glfwAddOutputWayland(uint32_t name, uint32_t version) { _GLFWmonitor *monitor; struct wl_output *output; - char name_str[80]; + char nameStr[80]; - memset(name_str, 0, sizeof(name_str)); - snprintf(name_str, 79, "wl_output@%u", name); + memset(nameStr, 0, sizeof(nameStr)); + snprintf(nameStr, 79, "wl_output@%u", name); if (version < 2) { @@ -130,7 +130,7 @@ void _glfwAddOutputWayland(uint32_t name, uint32_t version) return; } - monitor = _glfwAllocMonitor(name_str, 0, 0); + monitor = _glfwAllocMonitor(nameStr, 0, 0); output = wl_registry_bind(_glfw.wl.registry, name, @@ -148,7 +148,7 @@ void _glfwAddOutputWayland(uint32_t name, uint32_t version) monitor->wl.scale = 1; monitor->wl.output = output; - wl_output_add_listener(output, &output_listener, monitor); + wl_output_add_listener(output, &outputListener, monitor); if (_glfw.wl.monitorsCount + 1 >= _glfw.wl.monitorsSize) { diff --git a/src/wl_platform.h b/src/wl_platform.h index cec52b906..f60626e22 100644 --- a/src/wl_platform.h +++ b/src/wl_platform.h @@ -83,7 +83,7 @@ typedef struct _GLFWwindowWayland GLFWbool maximized; struct wl_surface* surface; struct wl_egl_window* native; - struct wl_shell_surface* shell_surface; + struct wl_shell_surface* shellSurface; struct wl_callback* callback; _GLFWcursor* currentCursor; @@ -119,7 +119,7 @@ typedef struct _GLFWlibraryWayland struct zwp_relative_pointer_manager_v1* relativePointerManager; struct zwp_pointer_constraints_v1* pointerConstraints; - int wl_compositor_version; + int compositorVersion; struct wl_cursor_theme* cursorTheme; struct wl_surface* cursorSurface; @@ -136,10 +136,10 @@ typedef struct _GLFWlibraryWayland struct xkb_context* context; struct xkb_keymap* keymap; struct xkb_state* state; - xkb_mod_mask_t control_mask; - xkb_mod_mask_t alt_mask; - xkb_mod_mask_t shift_mask; - xkb_mod_mask_t super_mask; + xkb_mod_mask_t controlMask; + xkb_mod_mask_t altMask; + xkb_mod_mask_t shiftMask; + xkb_mod_mask_t superMask; unsigned int modifiers; } xkb; diff --git a/src/wl_window.c b/src/wl_window.c index 0761de64d..a2bae3732 100644 --- a/src/wl_window.c +++ b/src/wl_window.c @@ -105,7 +105,7 @@ static void checkScaleChange(_GLFWwindow* window) int monitorScale; // Check if we will be able to set the buffer scale or not. - if (_glfw.wl.wl_compositor_version < 3) + if (_glfw.wl.compositorVersion < 3) return; // Get the scale factor from the highest scale monitor. @@ -220,33 +220,33 @@ static GLFWbool createSurface(_GLFWwindow* window, static GLFWbool createShellSurface(_GLFWwindow* window) { - window->wl.shell_surface = wl_shell_get_shell_surface(_glfw.wl.shell, - window->wl.surface); - if (!window->wl.shell_surface) + window->wl.shellSurface = wl_shell_get_shell_surface(_glfw.wl.shell, + window->wl.surface); + if (!window->wl.shellSurface) return GLFW_FALSE; - wl_shell_surface_add_listener(window->wl.shell_surface, + wl_shell_surface_add_listener(window->wl.shellSurface, &shellSurfaceListener, window); if (window->wl.title) - wl_shell_surface_set_title(window->wl.shell_surface, window->wl.title); + wl_shell_surface_set_title(window->wl.shellSurface, window->wl.title); if (window->monitor) { wl_shell_surface_set_fullscreen( - window->wl.shell_surface, + window->wl.shellSurface, WL_SHELL_SURFACE_FULLSCREEN_METHOD_DEFAULT, 0, window->monitor->wl.output); } else if (window->wl.maximized) { - wl_shell_surface_set_maximized(window->wl.shell_surface, NULL); + wl_shell_surface_set_maximized(window->wl.shellSurface, NULL); } else { - wl_shell_surface_set_toplevel(window->wl.shell_surface); + wl_shell_surface_set_toplevel(window->wl.shellSurface); } return GLFW_TRUE; @@ -411,7 +411,7 @@ int _glfwPlatformCreateWindow(_GLFWwindow* window, } else { - window->wl.shell_surface = NULL; + window->wl.shellSurface = NULL; window->wl.visible = GLFW_FALSE; } @@ -443,8 +443,8 @@ void _glfwPlatformDestroyWindow(_GLFWwindow* window) if (window->wl.native) wl_egl_window_destroy(window->wl.native); - if (window->wl.shell_surface) - wl_shell_surface_destroy(window->wl.shell_surface); + if (window->wl.shellSurface) + wl_shell_surface_destroy(window->wl.shellSurface); if (window->wl.surface) wl_surface_destroy(window->wl.surface); @@ -458,8 +458,8 @@ void _glfwPlatformSetWindowTitle(_GLFWwindow* window, const char* title) if (window->wl.title) free(window->wl.title); window->wl.title = strdup(title); - if (window->wl.shell_surface) - wl_shell_surface_set_title(window->wl.shell_surface, title); + if (window->wl.shellSurface) + wl_shell_surface_set_title(window->wl.shellSurface, title); } void _glfwPlatformSetWindowIcon(_GLFWwindow* window, @@ -546,8 +546,8 @@ void _glfwPlatformRestoreWindow(_GLFWwindow* window) // TODO: also do the same for iconified. if (window->monitor || window->wl.maximized) { - if (window->wl.shell_surface) - wl_shell_surface_set_toplevel(window->wl.shell_surface); + if (window->wl.shellSurface) + wl_shell_surface_set_toplevel(window->wl.shellSurface); window->wl.maximized = GLFW_FALSE; } @@ -557,10 +557,10 @@ void _glfwPlatformMaximizeWindow(_GLFWwindow* window) { if (!window->monitor && !window->wl.maximized) { - if (window->wl.shell_surface) + if (window->wl.shellSurface) { // Let the compositor select the best output. - wl_shell_surface_set_maximized(window->wl.shell_surface, NULL); + wl_shell_surface_set_maximized(window->wl.shellSurface, NULL); } window->wl.maximized = GLFW_TRUE; } @@ -570,7 +570,7 @@ void _glfwPlatformShowWindow(_GLFWwindow* window) { if (!window->monitor) { - if (!window->wl.shell_surface) + if (!window->wl.shellSurface) createShellSurface(window); window->wl.visible = GLFW_TRUE; } @@ -580,8 +580,8 @@ void _glfwPlatformHideWindow(_GLFWwindow* window) { if (!window->monitor) { - if (window->wl.shell_surface) - wl_shell_surface_destroy(window->wl.shell_surface); + if (window->wl.shellSurface) + wl_shell_surface_destroy(window->wl.shellSurface); window->wl.visible = GLFW_FALSE; } } @@ -601,14 +601,14 @@ void _glfwPlatformSetWindowMonitor(_GLFWwindow* window, if (monitor) { wl_shell_surface_set_fullscreen( - window->wl.shell_surface, + window->wl.shellSurface, WL_SHELL_SURFACE_FULLSCREEN_METHOD_DEFAULT, refreshRate * 1000, // Convert Hz to mHz. monitor->wl.output); } else { - wl_shell_surface_set_toplevel(window->wl.shell_surface); + wl_shell_surface_set_toplevel(window->wl.shellSurface); } _glfwInputWindowMonitorChange(window, monitor); } From 238ebb600d88159070cae239103ffbcf23770abc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?M=C3=A1rio=20Freitas?= Date: Thu, 1 Sep 2016 00:23:22 +0100 Subject: [PATCH 07/40] Cocoa: Fix assert on disabling window aspect ratio When disabling window aspect ratio, a system assert would trigger. The correct way to disable window aspect ratio is to set a resize increment. Closes #852. --- src/cocoa_window.m | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/cocoa_window.m b/src/cocoa_window.m index cad8715d4..6b6415851 100644 --- a/src/cocoa_window.m +++ b/src/cocoa_window.m @@ -1183,7 +1183,7 @@ void _glfwPlatformSetWindowSizeLimits(_GLFWwindow* window, void _glfwPlatformSetWindowAspectRatio(_GLFWwindow* window, int numer, int denom) { if (numer == GLFW_DONT_CARE || denom == GLFW_DONT_CARE) - [window->ns.object setContentAspectRatio:NSMakeSize(0, 0)]; + [window->ns.object setResizeIncrements:NSMakeSize(1.0, 1.0)]; else [window->ns.object setContentAspectRatio:NSMakeSize(numer, denom)]; } From ca084d40c1fecdd87e45a49f58448c5b77766488 Mon Sep 17 00:00:00 2001 From: Camilla Berglund Date: Mon, 10 Oct 2016 18:09:05 +0200 Subject: [PATCH 08/40] Cleanup Related to #852. --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index dc480f0b8..e4e4ca115 100644 --- a/README.md +++ b/README.md @@ -108,6 +108,7 @@ information on what to include when reporting a bug. - Bugfix: `GLFW_INCLUDE_VULKAN` could not be combined with the corresponding OpenGL and OpenGL ES header macros - [Win32] Bugfix: Undecorated windows could not be iconified by the user (#861) +- [Cocoa] Bugfix: Disabling window aspect ratio would assert (#852) - [EGL] Added support for `EGL_KHR_get_all_proc_addresses` (#871) @@ -160,6 +161,7 @@ skills. - Siavash Eliasi - Michael Fogleman - Gerald Franz + - Mário Freitas - GeO4d - Marcus Geelnard - Eloi Marín Gratacós From 967e837c73f0e0d7f9f27c7f6bb1aaa1d55dae54 Mon Sep 17 00:00:00 2001 From: Sergey Tikhomirov Date: Sat, 15 Oct 2016 15:25:42 +0300 Subject: [PATCH 09/40] Cocoa: Fix 'first responder' error on macOS 10.12 Error message is displayed during the startup because Cocoa view is set as the first responder for window but this view isn't in this window (actually it is not in any window at all). Fixes #876. Closes #883. --- src/cocoa_window.m | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/cocoa_window.m b/src/cocoa_window.m index 6b6415851..1cc2ffebe 100644 --- a/src/cocoa_window.m +++ b/src/cocoa_window.m @@ -1032,11 +1032,11 @@ static GLFWbool createNativeWindow(_GLFWwindow* window, [window->ns.view setWantsBestResolutionOpenGLSurface:YES]; #endif /*_GLFW_USE_RETINA*/ + [window->ns.object setContentView:window->ns.view]; [window->ns.object makeFirstResponder:window->ns.view]; [window->ns.object setTitle:[NSString stringWithUTF8String:wndconfig->title]]; [window->ns.object setDelegate:window->ns.delegate]; [window->ns.object setAcceptsMouseMovedEvents:YES]; - [window->ns.object setContentView:window->ns.view]; [window->ns.object setRestorable:NO]; return GLFW_TRUE; From 99c65bb67cf7cd705ab5ec67d0c96d9bc0bbe621 Mon Sep 17 00:00:00 2001 From: Camilla Berglund Date: Sun, 16 Oct 2016 13:59:21 +0200 Subject: [PATCH 10/40] Cleanup Related to #883. --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index e4e4ca115..30e07efec 100644 --- a/README.md +++ b/README.md @@ -109,6 +109,7 @@ information on what to include when reporting a bug. OpenGL and OpenGL ES header macros - [Win32] Bugfix: Undecorated windows could not be iconified by the user (#861) - [Cocoa] Bugfix: Disabling window aspect ratio would assert (#852) +- [Cocoa] Bugfix: Window creation failed to set first responder (#876,#883) - [EGL] Added support for `EGL_KHR_get_all_proc_addresses` (#871) From ef1573516110a11a915c488813484cf94928e643 Mon Sep 17 00:00:00 2001 From: Camilla Berglund Date: Mon, 10 Oct 2016 01:24:16 +0200 Subject: [PATCH 11/40] Make Escape exit threads test --- tests/threads.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/tests/threads.c b/tests/threads.c index 699a0ad13..d5e348384 100644 --- a/tests/threads.c +++ b/tests/threads.c @@ -52,6 +52,12 @@ static void error_callback(int error, const char* description) fprintf(stderr, "Error: %s\n", description); } +static void key_callback(GLFWwindow* window, int key, int scancode, int action, int mods) +{ + if (key == GLFW_KEY_ESCAPE && action == GLFW_PRESS) + glfwSetWindowShouldClose(window, GLFW_TRUE); +} + static int thread_main(void* data) { const Thread* thread = data; @@ -101,6 +107,8 @@ int main(void) exit(EXIT_FAILURE); } + glfwSetKeyCallback(threads[i].window, key_callback); + glfwSetWindowPos(threads[i].window, 200 + 250 * i, 200); glfwShowWindow(threads[i].window); } From bf747e32b4ea263dfc761f1a7920db45ade473f2 Mon Sep 17 00:00:00 2001 From: Camilla Berglund Date: Mon, 10 Oct 2016 01:24:29 +0200 Subject: [PATCH 12/40] Remove unused variables in gamma test --- tests/gamma.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/tests/gamma.c b/tests/gamma.c index dc1d9c3b7..79fba3956 100644 --- a/tests/gamma.c +++ b/tests/gamma.c @@ -46,8 +46,6 @@ #include #include -static int windowed_xpos, windowed_ypos, windowed_width, windowed_height; - static void error_callback(int error, const char* description) { fprintf(stderr, "Error: %s\n", description); From efc6b35615e1091a74d6fd5af6c4a85647ccf32f Mon Sep 17 00:00:00 2001 From: Camilla Berglund Date: Mon, 10 Oct 2016 03:24:07 +0200 Subject: [PATCH 13/40] Rename joystick ID variables --- docs/input.dox | 2 +- include/GLFW/glfw3.h | 18 ++++++------- src/cocoa_joystick.m | 46 +++++++++++++++---------------- src/input.c | 36 ++++++++++++------------- src/internal.h | 12 ++++----- src/linux_joystick.c | 34 +++++++++++------------ src/win32_joystick.c | 64 ++++++++++++++++++++++---------------------- tests/events.c | 12 ++++----- tests/joysticks.c | 14 +++++----- 9 files changed, 119 insertions(+), 119 deletions(-) diff --git a/docs/input.dox b/docs/input.dox index 78b4880a5..497fe6769 100644 --- a/docs/input.dox +++ b/docs/input.dox @@ -572,7 +572,7 @@ The callback function receives the ID of the joystick that has been connected and disconnected and the event that occurred. @code -void joystick_callback(int joy, int event) +void joystick_callback(int jid, int event) { if (event == GLFW_CONNECTED) { diff --git a/include/GLFW/glfw3.h b/include/GLFW/glfw3.h index 3e23968ef..1b29698d8 100644 --- a/include/GLFW/glfw3.h +++ b/include/GLFW/glfw3.h @@ -1128,7 +1128,7 @@ typedef void (* GLFWmonitorfun)(GLFWmonitor*,int); * This is the function signature for joystick configuration callback * functions. * - * @param[in] joy The joystick that was connected or disconnected. + * @param[in] jid The joystick that was connected or disconnected. * @param[in] event One of `GLFW_CONNECTED` or `GLFW_DISCONNECTED`. * * @sa @ref joystick_event @@ -3584,7 +3584,7 @@ GLFWAPI GLFWdropfun glfwSetDropCallback(GLFWwindow* window, GLFWdropfun cbfun); * * This function returns whether the specified joystick is present. * - * @param[in] joy The [joystick](@ref joysticks) to query. + * @param[in] jid The [joystick](@ref joysticks) to query. * @return `GLFW_TRUE` if the joystick is present, or `GLFW_FALSE` otherwise. * * @errors Possible errors include @ref GLFW_NOT_INITIALIZED, @ref @@ -3598,7 +3598,7 @@ GLFWAPI GLFWdropfun glfwSetDropCallback(GLFWwindow* window, GLFWdropfun cbfun); * * @ingroup input */ -GLFWAPI int glfwJoystickPresent(int joy); +GLFWAPI int glfwJoystickPresent(int jid); /*! @brief Returns the values of all axes of the specified joystick. * @@ -3609,7 +3609,7 @@ GLFWAPI int glfwJoystickPresent(int joy); * cause this function to return `NULL`. Call @ref glfwJoystickPresent to * check device presence. * - * @param[in] joy The [joystick](@ref joysticks) to query. + * @param[in] jid The [joystick](@ref joysticks) to query. * @param[out] count Where to store the number of axis values in the returned * array. This is set to zero if the joystick is not present or an error * occurred. @@ -3632,7 +3632,7 @@ GLFWAPI int glfwJoystickPresent(int joy); * * @ingroup input */ -GLFWAPI const float* glfwGetJoystickAxes(int joy, int* count); +GLFWAPI const float* glfwGetJoystickAxes(int jid, int* count); /*! @brief Returns the state of all buttons of the specified joystick. * @@ -3643,7 +3643,7 @@ GLFWAPI const float* glfwGetJoystickAxes(int joy, int* count); * cause this function to return `NULL`. Call @ref glfwJoystickPresent to * check device presence. * - * @param[in] joy The [joystick](@ref joysticks) to query. + * @param[in] jid The [joystick](@ref joysticks) to query. * @param[out] count Where to store the number of button states in the returned * array. This is set to zero if the joystick is not present or an error * occurred. @@ -3667,7 +3667,7 @@ GLFWAPI const float* glfwGetJoystickAxes(int joy, int* count); * * @ingroup input */ -GLFWAPI const unsigned char* glfwGetJoystickButtons(int joy, int* count); +GLFWAPI const unsigned char* glfwGetJoystickButtons(int jid, int* count); /*! @brief Returns the name of the specified joystick. * @@ -3679,7 +3679,7 @@ GLFWAPI const unsigned char* glfwGetJoystickButtons(int joy, int* count); * cause this function to return `NULL`. Call @ref glfwJoystickPresent to * check device presence. * - * @param[in] joy The [joystick](@ref joysticks) to query. + * @param[in] jid The [joystick](@ref joysticks) to query. * @return The UTF-8 encoded name of the joystick, or `NULL` if the joystick * is not present or an [error](@ref error_handling) occurred. * @@ -3699,7 +3699,7 @@ GLFWAPI const unsigned char* glfwGetJoystickButtons(int joy, int* count); * * @ingroup input */ -GLFWAPI const char* glfwGetJoystickName(int joy); +GLFWAPI const char* glfwGetJoystickName(int jid); /*! @brief Sets the joystick configuration callback. * diff --git a/src/cocoa_joystick.m b/src/cocoa_joystick.m index 439918fd7..72cebcfcd 100644 --- a/src/cocoa_joystick.m +++ b/src/cocoa_joystick.m @@ -272,24 +272,24 @@ static void matchCallback(void* context, IOHIDDeviceRef deviceRef) { _GLFWjoystickNS* js; - int joy; + int jid; - for (joy = GLFW_JOYSTICK_1; joy <= GLFW_JOYSTICK_LAST; joy++) + for (jid = GLFW_JOYSTICK_1; jid <= GLFW_JOYSTICK_LAST; jid++) { - if (_glfw.ns_js[joy].present && _glfw.ns_js[joy].deviceRef == deviceRef) + if (_glfw.ns_js[jid].present && _glfw.ns_js[jid].deviceRef == deviceRef) return; } - for (joy = GLFW_JOYSTICK_1; joy <= GLFW_JOYSTICK_LAST; joy++) + for (jid = GLFW_JOYSTICK_1; jid <= GLFW_JOYSTICK_LAST; jid++) { - if (!_glfw.ns_js[joy].present) + if (!_glfw.ns_js[jid].present) break; } - if (joy > GLFW_JOYSTICK_LAST) + if (jid > GLFW_JOYSTICK_LAST) return; - js = _glfw.ns_js + joy; + js = _glfw.ns_js + jid; js->present = GLFW_TRUE; js->deviceRef = deviceRef; @@ -324,7 +324,7 @@ static void matchCallback(void* context, js->buttons = calloc(CFArrayGetCount(js->buttonElements) + CFArrayGetCount(js->hatElements) * 4, 1); - _glfwInputJoystickChange(joy, GLFW_CONNECTED); + _glfwInputJoystickChange(jid, GLFW_CONNECTED); } // Callback for user-initiated joystick removal @@ -334,13 +334,13 @@ static void removeCallback(void* context, void* sender, IOHIDDeviceRef deviceRef) { - int joy; + int jid; - for (joy = GLFW_JOYSTICK_1; joy <= GLFW_JOYSTICK_LAST; joy++) + for (jid = GLFW_JOYSTICK_1; jid <= GLFW_JOYSTICK_LAST; jid++) { - if (_glfw.ns_js[joy].deviceRef == deviceRef) + if (_glfw.ns_js[jid].deviceRef == deviceRef) { - removeJoystick(_glfw.ns_js + joy); + removeJoystick(_glfw.ns_js + jid); break; } } @@ -456,11 +456,11 @@ void _glfwInitJoysticksNS(void) // void _glfwTerminateJoysticksNS(void) { - int joy; + int jid; - for (joy = GLFW_JOYSTICK_1; joy <= GLFW_JOYSTICK_LAST; joy++) + for (jid = GLFW_JOYSTICK_1; jid <= GLFW_JOYSTICK_LAST; jid++) { - _GLFWjoystickNS* js = _glfw.ns_js + joy; + _GLFWjoystickNS* js = _glfw.ns_js + jid; removeJoystick(js); } @@ -473,15 +473,15 @@ void _glfwTerminateJoysticksNS(void) ////// GLFW platform API ////// ////////////////////////////////////////////////////////////////////////// -int _glfwPlatformJoystickPresent(int joy) +int _glfwPlatformJoystickPresent(int jid) { - _GLFWjoystickNS* js = _glfw.ns_js + joy; + _GLFWjoystickNS* js = _glfw.ns_js + jid; return js->present; } -const float* _glfwPlatformGetJoystickAxes(int joy, int* count) +const float* _glfwPlatformGetJoystickAxes(int jid, int* count) { - _GLFWjoystickNS* js = _glfw.ns_js + joy; + _GLFWjoystickNS* js = _glfw.ns_js + jid; if (!pollJoystickAxisEvents(js)) return NULL; @@ -489,9 +489,9 @@ const float* _glfwPlatformGetJoystickAxes(int joy, int* count) return js->axes; } -const unsigned char* _glfwPlatformGetJoystickButtons(int joy, int* count) +const unsigned char* _glfwPlatformGetJoystickButtons(int jid, int* count) { - _GLFWjoystickNS* js = _glfw.ns_js + joy; + _GLFWjoystickNS* js = _glfw.ns_js + jid; if (!pollJoystickButtonEvents(js)) return NULL; @@ -500,9 +500,9 @@ const unsigned char* _glfwPlatformGetJoystickButtons(int joy, int* count) return js->buttons; } -const char* _glfwPlatformGetJoystickName(int joy) +const char* _glfwPlatformGetJoystickName(int jid) { - _GLFWjoystickNS* js = _glfw.ns_js + joy; + _GLFWjoystickNS* js = _glfw.ns_js + jid; if (!js->present) return NULL; diff --git a/src/input.c b/src/input.c index c09599212..cceae35da 100644 --- a/src/input.c +++ b/src/input.c @@ -124,10 +124,10 @@ void _glfwInputDrop(_GLFWwindow* window, int count, const char** paths) window->callbacks.drop((GLFWwindow*) window, count, paths); } -void _glfwInputJoystickChange(int joy, int event) +void _glfwInputJoystickChange(int jid, int event) { if (_glfw.callbacks.joystick) - _glfw.callbacks.joystick(joy, event); + _glfw.callbacks.joystick(jid, event); } @@ -553,62 +553,62 @@ GLFWAPI GLFWdropfun glfwSetDropCallback(GLFWwindow* handle, GLFWdropfun cbfun) return cbfun; } -GLFWAPI int glfwJoystickPresent(int joy) +GLFWAPI int glfwJoystickPresent(int jid) { _GLFW_REQUIRE_INIT_OR_RETURN(0); - if (joy < 0 || joy > GLFW_JOYSTICK_LAST) + if (jid < 0 || jid > GLFW_JOYSTICK_LAST) { - _glfwInputError(GLFW_INVALID_ENUM, "Invalid joystick %i", joy); + _glfwInputError(GLFW_INVALID_ENUM, "Invalid joystick %i", jid); return 0; } - return _glfwPlatformJoystickPresent(joy); + return _glfwPlatformJoystickPresent(jid); } -GLFWAPI const float* glfwGetJoystickAxes(int joy, int* count) +GLFWAPI const float* glfwGetJoystickAxes(int jid, int* count) { assert(count != NULL); *count = 0; _GLFW_REQUIRE_INIT_OR_RETURN(NULL); - if (joy < 0 || joy > GLFW_JOYSTICK_LAST) + if (jid < 0 || jid > GLFW_JOYSTICK_LAST) { - _glfwInputError(GLFW_INVALID_ENUM, "Invalid joystick %i", joy); + _glfwInputError(GLFW_INVALID_ENUM, "Invalid joystick %i", jid); return NULL; } - return _glfwPlatformGetJoystickAxes(joy, count); + return _glfwPlatformGetJoystickAxes(jid, count); } -GLFWAPI const unsigned char* glfwGetJoystickButtons(int joy, int* count) +GLFWAPI const unsigned char* glfwGetJoystickButtons(int jid, int* count) { assert(count != NULL); *count = 0; _GLFW_REQUIRE_INIT_OR_RETURN(NULL); - if (joy < 0 || joy > GLFW_JOYSTICK_LAST) + if (jid < 0 || jid > GLFW_JOYSTICK_LAST) { - _glfwInputError(GLFW_INVALID_ENUM, "Invalid joystick %i", joy); + _glfwInputError(GLFW_INVALID_ENUM, "Invalid joystick %i", jid); return NULL; } - return _glfwPlatformGetJoystickButtons(joy, count); + return _glfwPlatformGetJoystickButtons(jid, count); } -GLFWAPI const char* glfwGetJoystickName(int joy) +GLFWAPI const char* glfwGetJoystickName(int jid) { _GLFW_REQUIRE_INIT_OR_RETURN(NULL); - if (joy < 0 || joy > GLFW_JOYSTICK_LAST) + if (jid < 0 || jid > GLFW_JOYSTICK_LAST) { - _glfwInputError(GLFW_INVALID_ENUM, "Invalid joystick %i", joy); + _glfwInputError(GLFW_INVALID_ENUM, "Invalid joystick %i", jid); return NULL; } - return _glfwPlatformGetJoystickName(joy); + return _glfwPlatformGetJoystickName(jid); } GLFWAPI GLFWjoystickfun glfwSetJoystickCallback(GLFWjoystickfun cbfun) diff --git a/src/internal.h b/src/internal.h index 4d92c554a..a623262e8 100644 --- a/src/internal.h +++ b/src/internal.h @@ -608,22 +608,22 @@ const char* _glfwPlatformGetClipboardString(_GLFWwindow* window); /*! @copydoc glfwJoystickPresent * @ingroup platform */ -int _glfwPlatformJoystickPresent(int joy); +int _glfwPlatformJoystickPresent(int jid); /*! @copydoc glfwGetJoystickAxes * @ingroup platform */ -const float* _glfwPlatformGetJoystickAxes(int joy, int* count); +const float* _glfwPlatformGetJoystickAxes(int jid, int* count); /*! @copydoc glfwGetJoystickButtons * @ingroup platform */ -const unsigned char* _glfwPlatformGetJoystickButtons(int joy, int* count); +const unsigned char* _glfwPlatformGetJoystickButtons(int jid, int* count); /*! @copydoc glfwGetJoystickName * @ingroup platform */ -const char* _glfwPlatformGetJoystickName(int joy); +const char* _glfwPlatformGetJoystickName(int jid); /*! @copydoc glfwGetTimerValue * @ingroup platform @@ -961,11 +961,11 @@ void _glfwInputError(int error, const char* format, ...); void _glfwInputDrop(_GLFWwindow* window, int count, const char** names); /*! @brief Notifies shared code of a joystick connection/disconnection event. - * @param[in] joy The joystick that was connected or disconnected. + * @param[in] jid The joystick that was connected or disconnected. * @param[in] event One of `GLFW_CONNECTED` or `GLFW_DISCONNECTED`. * @ingroup event */ -void _glfwInputJoystickChange(int joy, int event); +void _glfwInputJoystickChange(int jid, int event); //======================================================================== diff --git a/src/linux_joystick.c b/src/linux_joystick.c index ad249955b..d5b9a28ef 100644 --- a/src/linux_joystick.c +++ b/src/linux_joystick.c @@ -50,25 +50,25 @@ static GLFWbool openJoystickDevice(const char* path) { char axisCount, buttonCount; char name[256] = ""; - int joy, fd, version; + int jid, fd, version; _GLFWjoystickLinux* js; - for (joy = GLFW_JOYSTICK_1; joy <= GLFW_JOYSTICK_LAST; joy++) + for (jid = GLFW_JOYSTICK_1; jid <= GLFW_JOYSTICK_LAST; jid++) { - if (!_glfw.linux_js.js[joy].present) + if (!_glfw.linux_js.js[jid].present) continue; - if (strcmp(_glfw.linux_js.js[joy].path, path) == 0) + if (strcmp(_glfw.linux_js.js[jid].path, path) == 0) return GLFW_FALSE; } - for (joy = GLFW_JOYSTICK_1; joy <= GLFW_JOYSTICK_LAST; joy++) + for (jid = GLFW_JOYSTICK_1; jid <= GLFW_JOYSTICK_LAST; jid++) { - if (!_glfw.linux_js.js[joy].present) + if (!_glfw.linux_js.js[jid].present) break; } - if (joy > GLFW_JOYSTICK_LAST) + if (jid > GLFW_JOYSTICK_LAST) return GLFW_FALSE; fd = open(path, O_RDONLY | O_NONBLOCK); @@ -87,7 +87,7 @@ static GLFWbool openJoystickDevice(const char* path) if (ioctl(fd, JSIOCGNAME(sizeof(name)), name) < 0) strncpy(name, "Unknown", sizeof(name)); - js = _glfw.linux_js.js + joy; + js = _glfw.linux_js.js + jid; js->present = GLFW_TRUE; js->name = strdup(name); js->path = strdup(path); @@ -101,7 +101,7 @@ static GLFWbool openJoystickDevice(const char* path) js->buttonCount = (int) buttonCount; js->buttons = calloc(buttonCount, 1); - _glfwInputJoystickChange(joy, GLFW_CONNECTED); + _glfwInputJoystickChange(jid, GLFW_CONNECTED); return GLFW_TRUE; } #endif // __linux__ @@ -304,15 +304,15 @@ void _glfwPollJoystickEvents(void) ////// GLFW platform API ////// ////////////////////////////////////////////////////////////////////////// -int _glfwPlatformJoystickPresent(int joy) +int _glfwPlatformJoystickPresent(int jid) { - _GLFWjoystickLinux* js = _glfw.linux_js.js + joy; + _GLFWjoystickLinux* js = _glfw.linux_js.js + jid; return pollJoystickEvents(js); } -const float* _glfwPlatformGetJoystickAxes(int joy, int* count) +const float* _glfwPlatformGetJoystickAxes(int jid, int* count) { - _GLFWjoystickLinux* js = _glfw.linux_js.js + joy; + _GLFWjoystickLinux* js = _glfw.linux_js.js + jid; if (!pollJoystickEvents(js)) return NULL; @@ -320,9 +320,9 @@ const float* _glfwPlatformGetJoystickAxes(int joy, int* count) return js->axes; } -const unsigned char* _glfwPlatformGetJoystickButtons(int joy, int* count) +const unsigned char* _glfwPlatformGetJoystickButtons(int jid, int* count) { - _GLFWjoystickLinux* js = _glfw.linux_js.js + joy; + _GLFWjoystickLinux* js = _glfw.linux_js.js + jid; if (!pollJoystickEvents(js)) return NULL; @@ -330,9 +330,9 @@ const unsigned char* _glfwPlatformGetJoystickButtons(int joy, int* count) return js->buttons; } -const char* _glfwPlatformGetJoystickName(int joy) +const char* _glfwPlatformGetJoystickName(int jid) { - _GLFWjoystickLinux* js = _glfw.linux_js.js + joy; + _GLFWjoystickLinux* js = _glfw.linux_js.js + jid; if (!pollJoystickEvents(js)) return NULL; diff --git a/src/win32_joystick.c b/src/win32_joystick.c index 49f3b871d..5d2e21c36 100644 --- a/src/win32_joystick.c +++ b/src/win32_joystick.c @@ -332,26 +332,26 @@ static BOOL CALLBACK deviceObjectCallback(const DIDEVICEOBJECTINSTANCEW* doi, // static BOOL CALLBACK deviceCallback(const DIDEVICEINSTANCE* di, void* user) { - int joy = 0; + int jid = 0; DIDEVCAPS dc; DIPROPDWORD dipd; IDirectInputDevice8* device; _GLFWobjenumWin32 data; _GLFWjoystickWin32* js; - for (joy = GLFW_JOYSTICK_1; joy <= GLFW_JOYSTICK_LAST; joy++) + for (jid = GLFW_JOYSTICK_1; jid <= GLFW_JOYSTICK_LAST; jid++) { - if (memcmp(&_glfw.win32_js[joy].guid, &di->guidInstance, sizeof(GUID)) == 0) + if (memcmp(&_glfw.win32_js[jid].guid, &di->guidInstance, sizeof(GUID)) == 0) return DIENUM_CONTINUE; } - for (joy = GLFW_JOYSTICK_1; joy <= GLFW_JOYSTICK_LAST; joy++) + for (jid = GLFW_JOYSTICK_1; jid <= GLFW_JOYSTICK_LAST; jid++) { - if (!_glfw.win32_js[joy].present) + if (!_glfw.win32_js[jid].present) break; } - if (joy > GLFW_JOYSTICK_LAST) + if (jid > GLFW_JOYSTICK_LAST) return DIENUM_STOP; if (supportsXInput(&di->guidProduct)) @@ -426,7 +426,7 @@ static BOOL CALLBACK deviceCallback(const DIDEVICEINSTANCE* di, void* user) sizeof(_GLFWjoyobjectWin32), compareJoystickObjects); - js = _glfw.win32_js + joy; + js = _glfw.win32_js + jid; js->device = device; js->guid = di->guidInstance; js->axisCount = data.axisCount + data.sliderCount; @@ -438,7 +438,7 @@ static BOOL CALLBACK deviceCallback(const DIDEVICEINSTANCE* di, void* user) js->name = _glfwCreateUTF8FromWideStringWin32(di->tszInstanceName); js->present = GLFW_TRUE; - _glfwInputJoystickChange(joy, GLFW_CONNECTED); + _glfwInputJoystickChange(jid, GLFW_CONNECTED); return DIENUM_CONTINUE; } @@ -447,33 +447,33 @@ static BOOL CALLBACK deviceCallback(const DIDEVICEINSTANCE* di, void* user) // static GLFWbool openXinputDevice(DWORD index) { - int joy; + int jid; XINPUT_CAPABILITIES xic; _GLFWjoystickWin32* js; - for (joy = GLFW_JOYSTICK_1; joy <= GLFW_JOYSTICK_LAST; joy++) + for (jid = GLFW_JOYSTICK_1; jid <= GLFW_JOYSTICK_LAST; jid++) { - if (_glfw.win32_js[joy].present && - _glfw.win32_js[joy].device == NULL && - _glfw.win32_js[joy].index == index) + if (_glfw.win32_js[jid].present && + _glfw.win32_js[jid].device == NULL && + _glfw.win32_js[jid].index == index) { return GLFW_FALSE; } } - for (joy = GLFW_JOYSTICK_1; joy <= GLFW_JOYSTICK_LAST; joy++) + for (jid = GLFW_JOYSTICK_1; jid <= GLFW_JOYSTICK_LAST; jid++) { - if (!_glfw.win32_js[joy].present) + if (!_glfw.win32_js[jid].present) break; } - if (joy > GLFW_JOYSTICK_LAST) + if (jid > GLFW_JOYSTICK_LAST) return GLFW_FALSE; if (_glfw_XInputGetCapabilities(index, 0, &xic) != ERROR_SUCCESS) return GLFW_FALSE; - js = _glfw.win32_js + joy; + js = _glfw.win32_js + jid; js->axisCount = 6; js->axes = calloc(js->axisCount, sizeof(float)); js->buttonCount = 14; @@ -482,7 +482,7 @@ static GLFWbool openXinputDevice(DWORD index) js->name = strdup(getDeviceDescription(&xic)); js->index = index; - _glfwInputJoystickChange(joy, GLFW_CONNECTED); + _glfwInputJoystickChange(jid, GLFW_CONNECTED); return GLFW_TRUE; } @@ -675,10 +675,10 @@ void _glfwInitJoysticksWin32(void) // void _glfwTerminateJoysticksWin32(void) { - int joy; + int jid; - for (joy = GLFW_JOYSTICK_1; joy <= GLFW_JOYSTICK_LAST; joy++) - closeJoystick(_glfw.win32_js + joy); + for (jid = GLFW_JOYSTICK_1; jid <= GLFW_JOYSTICK_LAST; jid++) + closeJoystick(_glfw.win32_js + jid); if (_glfw.win32.dinput8.api) IDirectInput8_Release(_glfw.win32.dinput8.api); @@ -715,10 +715,10 @@ void _glfwDetectJoystickConnectionWin32(void) // void _glfwDetectJoystickDisconnectionWin32(void) { - int joy; + int jid; - for (joy = GLFW_JOYSTICK_1; joy <= GLFW_JOYSTICK_LAST; joy++) - pollJoystickState(_glfw.win32_js + joy, _GLFW_PRESENCE_ONLY); + for (jid = GLFW_JOYSTICK_1; jid <= GLFW_JOYSTICK_LAST; jid++) + pollJoystickState(_glfw.win32_js + jid, _GLFW_PRESENCE_ONLY); } @@ -726,15 +726,15 @@ void _glfwDetectJoystickDisconnectionWin32(void) ////// GLFW platform API ////// ////////////////////////////////////////////////////////////////////////// -int _glfwPlatformJoystickPresent(int joy) +int _glfwPlatformJoystickPresent(int jid) { - _GLFWjoystickWin32* js = _glfw.win32_js + joy; + _GLFWjoystickWin32* js = _glfw.win32_js + jid; return pollJoystickState(js, _GLFW_PRESENCE_ONLY); } -const float* _glfwPlatformGetJoystickAxes(int joy, int* count) +const float* _glfwPlatformGetJoystickAxes(int jid, int* count) { - _GLFWjoystickWin32* js = _glfw.win32_js + joy; + _GLFWjoystickWin32* js = _glfw.win32_js + jid; if (!pollJoystickState(js, _GLFW_UPDATE_STATE)) return NULL; @@ -742,9 +742,9 @@ const float* _glfwPlatformGetJoystickAxes(int joy, int* count) return js->axes; } -const unsigned char* _glfwPlatformGetJoystickButtons(int joy, int* count) +const unsigned char* _glfwPlatformGetJoystickButtons(int jid, int* count) { - _GLFWjoystickWin32* js = _glfw.win32_js + joy; + _GLFWjoystickWin32* js = _glfw.win32_js + jid; if (!pollJoystickState(js, _GLFW_UPDATE_STATE)) return NULL; @@ -752,9 +752,9 @@ const unsigned char* _glfwPlatformGetJoystickButtons(int joy, int* count) return js->buttons; } -const char* _glfwPlatformGetJoystickName(int joy) +const char* _glfwPlatformGetJoystickName(int jid) { - _GLFWjoystickWin32* js = _glfw.win32_js + joy; + _GLFWjoystickWin32* js = _glfw.win32_js + jid; if (!pollJoystickState(js, _GLFW_PRESENCE_ONLY)) return NULL; diff --git a/tests/events.c b/tests/events.c index 09da5657d..fa14ae11b 100644 --- a/tests/events.c +++ b/tests/events.c @@ -459,26 +459,26 @@ static void monitor_callback(GLFWmonitor* monitor, int event) } } -static void joystick_callback(int joy, int event) +static void joystick_callback(int jid, int event) { if (event == GLFW_CONNECTED) { int axisCount, buttonCount; - glfwGetJoystickAxes(joy, &axisCount); - glfwGetJoystickButtons(joy, &buttonCount); + glfwGetJoystickAxes(jid, &axisCount); + glfwGetJoystickButtons(jid, &buttonCount); printf("%08x at %0.3f: Joystick %i (%s) was connected with %i axes and %i buttons\n", counter++, glfwGetTime(), - joy, - glfwGetJoystickName(joy), + jid, + glfwGetJoystickName(jid), axisCount, buttonCount); } else { printf("%08x at %0.3f: Joystick %i was disconnected\n", - counter++, glfwGetTime(), joy); + counter++, glfwGetTime(), jid); } } diff --git a/tests/joysticks.c b/tests/joysticks.c index 75bc42f61..ca51511fd 100644 --- a/tests/joysticks.c +++ b/tests/joysticks.c @@ -60,17 +60,17 @@ static void error_callback(int error, const char* description) fprintf(stderr, "Error: %s\n", description); } -static void joystick_callback(int joy, int event) +static void joystick_callback(int jid, int event) { if (event == GLFW_CONNECTED) - joysticks[joystick_count++] = joy; + joysticks[joystick_count++] = jid; else if (event == GLFW_DISCONNECTED) { int i; for (i = 0; i < joystick_count; i++) { - if (joysticks[i] == joy) + if (joysticks[i] == jid) break; } @@ -90,7 +90,7 @@ static const char* joystick_label(int jid) int main(void) { - int joy; + int jid; GLFWwindow* window; struct nk_context* nk; struct nk_font_atlas* atlas; @@ -102,10 +102,10 @@ int main(void) if (!glfwInit()) exit(EXIT_FAILURE); - for (joy = GLFW_JOYSTICK_1; joy <= GLFW_JOYSTICK_LAST; joy++) + for (jid = GLFW_JOYSTICK_1; jid <= GLFW_JOYSTICK_LAST; jid++) { - if (glfwJoystickPresent(joy)) - joystick_callback(joy, GLFW_CONNECTED); + if (glfwJoystickPresent(jid)) + joystick_callback(jid, GLFW_CONNECTED); } glfwSetJoystickCallback(joystick_callback); From 046d281abc4e088c4eb685bdf8756c59a51c1b4d Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Tue, 11 Oct 2016 00:02:15 +0100 Subject: [PATCH 14/40] Wayland: Implement compose key for character input MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This commit has been copied almost verbatim from Bryce Harrington’s patch against Weston’s toytoolkit[1]. He gave his agreement to relicense it under zlib[2]. [1] https://patchwork.freedesktop.org/patch/114661/ [2] https://github.com/glfw/glfw/pull/879#issuecomment-252988257 --- src/wl_init.c | 57 ++++++++++++++++++++++++++++++++++++++++++++++- src/wl_platform.h | 2 ++ 2 files changed, 58 insertions(+), 1 deletion(-) diff --git a/src/wl_init.c b/src/wl_init.c index 4627c599b..e61f72f70 100644 --- a/src/wl_init.c +++ b/src/wl_init.c @@ -172,7 +172,10 @@ static void keyboardHandleKeymap(void* data, { struct xkb_keymap* keymap; struct xkb_state* state; + struct xkb_compose_table* composeTable; + struct xkb_compose_state* composeState; char* mapStr; + const char* locale; if (format != WL_KEYBOARD_KEYMAP_FORMAT_XKB_V1) { @@ -209,6 +212,35 @@ static void keyboardHandleKeymap(void* data, return; } + // Look up the preferred locale, falling back to "C" as default. + locale = getenv("LC_ALL"); + if (!locale) + locale = getenv("LC_CTYPE"); + if (!locale) + locale = getenv("LANG"); + if (!locale) + locale = "C"; + + composeTable = + xkb_compose_table_new_from_locale(_glfw.wl.xkb.context, locale, + XKB_COMPOSE_COMPILE_NO_FLAGS); + if (composeTable) + { + composeState = + xkb_compose_state_new(composeTable, XKB_COMPOSE_STATE_NO_FLAGS); + xkb_compose_table_unref(composeTable); + if (composeState) + _glfw.wl.xkb.composeState = composeState; + else + _glfwInputError(GLFW_PLATFORM_ERROR, + "Wayland: Failed to create XKB compose state"); + } + else + { + _glfwInputError(GLFW_PLATFORM_ERROR, + "Wayland: Failed to create XKB compose table"); + } + xkb_keymap_unref(_glfw.wl.xkb.keymap); xkb_state_unref(_glfw.wl.xkb.state); _glfw.wl.xkb.keymap = keymap; @@ -258,18 +290,40 @@ static int toGLFWKeyCode(uint32_t key) return GLFW_KEY_UNKNOWN; } +static xkb_keysym_t composeSymbol(xkb_keysym_t sym) +{ + if (sym == XKB_KEY_NoSymbol) + return sym; + if (xkb_compose_state_feed(_glfw.wl.xkb.composeState, sym) + != XKB_COMPOSE_FEED_ACCEPTED) + return sym; + switch (xkb_compose_state_get_status(_glfw.wl.xkb.composeState)) + { + case XKB_COMPOSE_COMPOSED: + return xkb_compose_state_get_one_sym(_glfw.wl.xkb.composeState); + case XKB_COMPOSE_COMPOSING: + case XKB_COMPOSE_CANCELLED: + return XKB_KEY_NoSymbol; + case XKB_COMPOSE_NOTHING: + default: + return sym; + } +} + static void inputChar(_GLFWwindow* window, uint32_t key) { uint32_t code, numSyms; long cp; const xkb_keysym_t *syms; + xkb_keysym_t sym; code = key + 8; numSyms = xkb_state_key_get_syms(_glfw.wl.xkb.state, code, &syms); if (numSyms == 1) { - cp = _glfwKeySym2Unicode(syms[0]); + sym = composeSymbol(syms[0]); + cp = _glfwKeySym2Unicode(sym); if (cp != -1) { const int mods = _glfw.wl.xkb.modifiers; @@ -645,6 +699,7 @@ void _glfwPlatformTerminate(void) _glfwTerminateJoysticksLinux(); _glfwTerminateThreadLocalStoragePOSIX(); + xkb_compose_state_unref(_glfw.wl.xkb.composeState); xkb_keymap_unref(_glfw.wl.xkb.keymap); xkb_state_unref(_glfw.wl.xkb.state); xkb_context_unref(_glfw.wl.xkb.context); diff --git a/src/wl_platform.h b/src/wl_platform.h index f60626e22..209f7bca0 100644 --- a/src/wl_platform.h +++ b/src/wl_platform.h @@ -29,6 +29,7 @@ #include #include +#include #include typedef VkFlags VkWaylandSurfaceCreateFlagsKHR; @@ -136,6 +137,7 @@ typedef struct _GLFWlibraryWayland struct xkb_context* context; struct xkb_keymap* keymap; struct xkb_state* state; + struct xkb_compose_state* composeState; xkb_mod_mask_t controlMask; xkb_mod_mask_t altMask; xkb_mod_mask_t shiftMask; From bc8b0480e9832811dce25dd705eb541009665c76 Mon Sep 17 00:00:00 2001 From: linkmauve Date: Sun, 16 Oct 2016 15:52:39 +0100 Subject: [PATCH 15/40] Wayland: Document when behaviour differs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This should describe all of the Wayland-specific points, mostly in three categories: - Missing GLFW features, for example window frame, screensaver inhibition, clipboard or file drop. - Missing protocols, like setting gamma on a monitor or specifying a window icon. - Fundamental incompatibilities with Wayland concepts, like trying to handle global positioning of a window in a 2D space, trying to bring a window to front, or trying to change the monitor’s mode. Closes #881. --- docs/Doxyfile.in | 1 + include/GLFW/glfw3.h | 83 +++++++++++++++++++++++++++++++++++++++++--- 2 files changed, 80 insertions(+), 4 deletions(-) diff --git a/docs/Doxyfile.in b/docs/Doxyfile.in index b046f898b..062ac53c1 100644 --- a/docs/Doxyfile.in +++ b/docs/Doxyfile.in @@ -202,6 +202,7 @@ ALIASES = "thread_safety=@par Thread safety\n" \ "errors=@par Errors\n" \ "glfw3=@par\n__GLFW 3:__" \ "x11=__X11:__" \ + "wayland=__Wayland:__" \ "win32=__Windows:__" \ "osx=__OS X:__" diff --git a/include/GLFW/glfw3.h b/include/GLFW/glfw3.h index 1b29698d8..ebe178cfd 100644 --- a/include/GLFW/glfw3.h +++ b/include/GLFW/glfw3.h @@ -1621,6 +1621,9 @@ GLFWAPI const GLFWvidmode* glfwGetVideoMode(GLFWmonitor* monitor); * @errors Possible errors include @ref GLFW_NOT_INITIALIZED, @ref * GLFW_INVALID_VALUE and @ref GLFW_PLATFORM_ERROR. * + * @remark @wayland Gamma handling is currently unavailable, this function will + * always emit @ref GLFW_PLATFORM_ERROR. + * * @thread_safety This function must only be called from the main thread. * * @sa @ref monitor_gamma @@ -1642,6 +1645,9 @@ GLFWAPI void glfwSetGamma(GLFWmonitor* monitor, float gamma); * @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref * GLFW_PLATFORM_ERROR. * + * @remark @wayland Gamma handling is currently unavailable, this function will + * always return `NULL` and emit @ref GLFW_PLATFORM_ERROR. + * * @pointer_lifetime The returned structure and its arrays are allocated and * freed by GLFW. You should not free them yourself. They are valid until the * specified monitor is disconnected, this function is called again for that @@ -1674,6 +1680,9 @@ GLFWAPI const GLFWgammaramp* glfwGetGammaRamp(GLFWmonitor* monitor); * * @remark @win32 The gamma ramp size must be 256. * + * @remark @wayland Gamma handling is currently unavailable, this function will + * always emit @ref GLFW_PLATFORM_ERROR. + * * @pointer_lifetime The specified gamma ramp is copied before this function * returns. * @@ -1841,6 +1850,20 @@ GLFWAPI void glfwWindowHint(int hint, int value); * query the final size, position or other attributes directly after window * creation. * + * @remark @wayland The window frame is currently unimplemented, as if + * `GLFW_DECORATED` was always set to `GLFW_FALSE`. A compositor can still + * emit close, resize or maximize events, using for example a keybind + * mechanism. + * + * @remark @wayland A full screen window will not attempt to change the mode, + * no matter what the requested size or refresh rate. + * + * @remark @wayland The wl_shell protocol does not support window + * icons, the window will inherit the one defined in the application's + * desktop file, so this function emits @ref GLFW_PLATFORM_ERROR. + * + * @remark @wayland Screensaver inhibition is currently unimplemented. + * * @reentrancy This function must not be called from a callback. * * @thread_safety This function must only be called from the main thread. @@ -1979,6 +2002,10 @@ GLFWAPI void glfwSetWindowTitle(GLFWwindow* window, const char* title); * [Bundle Programming Guide](https://developer.apple.com/library/mac/documentation/CoreFoundation/Conceptual/CFBundles/) * in the Mac Developer Library. * + * @remark @wayland The wl_shell protocol does not support icons, the window + * will inherit the one defined in the application's desktop file, so this + * function emits @ref GLFW_PLATFORM_ERROR. + * * @thread_safety This function must only be called from the main thread. * * @sa @ref window_icon @@ -2006,6 +2033,10 @@ GLFWAPI void glfwSetWindowIcon(GLFWwindow* window, int count, const GLFWimage* i * @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref * GLFW_PLATFORM_ERROR. * + * @remark @wayland There is no way for an application to retrieve the global + * position of its windows, this function will always emit @ref + * GLFW_PLATFORM_ERROR. + * * @thread_safety This function must only be called from the main thread. * * @sa @ref window_pos @@ -2036,6 +2067,10 @@ GLFWAPI void glfwGetWindowPos(GLFWwindow* window, int* xpos, int* ypos); * @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref * GLFW_PLATFORM_ERROR. * + * @remark @wayland There is no way for an application to set the global + * position of its windows, this function will always emit @ref + * GLFW_PLATFORM_ERROR. + * * @thread_safety This function must only be called from the main thread. * * @sa @ref window_pos @@ -2107,6 +2142,9 @@ GLFWAPI void glfwGetWindowSize(GLFWwindow* window, int* width, int* height); * @remark If you set size limits and an aspect ratio that conflict, the * results are undefined. * + * @remark @wayland The size limits will not be applied until the window is + * actually resized, either by the user or by the compositor. + * * @thread_safety This function must only be called from the main thread. * * @sa @ref window_sizelimits @@ -2147,6 +2185,9 @@ GLFWAPI void glfwSetWindowSizeLimits(GLFWwindow* window, int minwidth, int minhe * @remark If you set size limits and an aspect ratio that conflict, the * results are undefined. * + * @remark @wayland The aspect ratio will not be applied until the window is + * actually resized, either by the user or by the compositor. + * * @thread_safety This function must only be called from the main thread. * * @sa @ref window_sizelimits @@ -2183,6 +2224,9 @@ GLFWAPI void glfwSetWindowAspectRatio(GLFWwindow* window, int numer, int denom); * @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref * GLFW_PLATFORM_ERROR. * + * @remark @wayland A full screen window will not attempt to change the mode, + * no matter what the requested size. + * * @thread_safety This function must only be called from the main thread. * * @sa @ref window_size @@ -2252,6 +2296,10 @@ GLFWAPI void glfwGetFramebufferSize(GLFWwindow* window, int* width, int* height) * @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref * GLFW_PLATFORM_ERROR. * + * @remark @wayland The window frame is currently unimplemented, as if + * `GLFW_DECORATED` was always set to `GLFW_FALSE`, so the returned values + * will always be zero. + * * @thread_safety This function must only be called from the main thread. * * @sa @ref window_size @@ -2276,6 +2324,9 @@ GLFWAPI void glfwGetWindowFrameSize(GLFWwindow* window, int* left, int* top, int * @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref * GLFW_PLATFORM_ERROR. * + * @remark @wayland There is no concept of iconification in wl_shell, this + * function will always emit @ref GLFW_PLATFORM_ERROR. + * * @thread_safety This function must only be called from the main thread. * * @sa @ref window_iconify @@ -2403,6 +2454,9 @@ GLFWAPI void glfwHideWindow(GLFWwindow* window); * @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref * GLFW_PLATFORM_ERROR. * + * @remark @wayland It is not possible for an application to bring its windows + * to front, this function will always emit @ref GLFW_PLATFORM_ERROR. + * * @thread_safety This function must only be called from the main thread. * * @sa @ref window_focus @@ -2453,7 +2507,7 @@ GLFWAPI GLFWmonitor* glfwGetWindowMonitor(GLFWwindow* window); * * When a window transitions from full screen to windowed mode, this function * restores any previous window settings such as whether it is decorated, - * floating, resizable, has size or aspect ratio limits, etc.. + * floating, resizable, has size or aspect ratio limits, etc. * * @param[in] window The window whose monitor, size or video mode to set. * @param[in] monitor The desired monitor, or `NULL` to set windowed mode. @@ -2471,6 +2525,12 @@ GLFWAPI GLFWmonitor* glfwGetWindowMonitor(GLFWwindow* window); * @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref * GLFW_PLATFORM_ERROR. * + * @remark @wayland The desired window position is ignored, as there is no way + * for an application to set this property. + * + * @remark @wayland Setting the window to full screen will not attempt to + * change the mode, no matter what the requested size or refresh rate. + * * @thread_safety This function must only be called from the main thread. * * @sa @ref window_monitor @@ -2575,6 +2635,9 @@ GLFWAPI void* glfwGetWindowUserPointer(GLFWwindow* window); * * @errors Possible errors include @ref GLFW_NOT_INITIALIZED. * + * @remark @wayland This callback will never be called, as there is no way for + * an application to know its global position. + * * @thread_safety This function must only be called from the main thread. * * @sa @ref window_pos @@ -2649,9 +2712,9 @@ GLFWAPI GLFWwindowclosefun glfwSetWindowCloseCallback(GLFWwindow* window, GLFWwi * called when the client area of the window needs to be redrawn, for example * if the window has been exposed after having been covered by another window. * - * On compositing window systems such as Aero, Compiz or Aqua, where the window - * contents are saved off-screen, this callback may be called only very - * infrequently or never at all. + * On compositing window systems such as Aero, Compiz, Aqua or Wayland, where + * the window contents are saved off-screen, this callback may be called only + * very infrequently or never at all. * * @param[in] window The window whose callback to set. * @param[in] cbfun The new callback, or `NULL` to remove the currently set @@ -2713,6 +2776,9 @@ GLFWAPI GLFWwindowfocusfun glfwSetWindowFocusCallback(GLFWwindow* window, GLFWwi * * @errors Possible errors include @ref GLFW_NOT_INITIALIZED. * + * @remark @wayland The wl_shell protocol has no concept of iconification, + * this callback will never be called. + * * @thread_safety This function must only be called from the main thread. * * @sa @ref window_iconify @@ -3205,6 +3271,9 @@ GLFWAPI void glfwGetCursorPos(GLFWwindow* window, double* xpos, double* ypos); * @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref * GLFW_PLATFORM_ERROR. * + * @remark @wayland This function will only work when the cursor mode is + * `GLFW_CURSOR_DISABLED`, otherwise it will do nothing. + * * @thread_safety This function must only be called from the main thread. * * @sa @ref cursor_pos @@ -3570,6 +3639,8 @@ GLFWAPI GLFWscrollfun glfwSetScrollCallback(GLFWwindow* window, GLFWscrollfun cb * * @errors Possible errors include @ref GLFW_NOT_INITIALIZED. * + * @remark @wayland File drop is currently unimplemented. + * * @thread_safety This function must only be called from the main thread. * * @sa @ref path_drop @@ -3735,6 +3806,8 @@ GLFWAPI GLFWjoystickfun glfwSetJoystickCallback(GLFWjoystickfun cbfun); * @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref * GLFW_PLATFORM_ERROR. * + * @remark @wayland Clipboard is currently unimplemented. + * * @pointer_lifetime The specified string is copied before this function * returns. * @@ -3763,6 +3836,8 @@ GLFWAPI void glfwSetClipboardString(GLFWwindow* window, const char* string); * @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref * GLFW_PLATFORM_ERROR. * + * @remark @wayland Clipboard is currently unimplemented. + * * @pointer_lifetime The returned string is allocated and freed by GLFW. You * should not free it yourself. It is valid until the next call to @ref * glfwGetClipboardString or @ref glfwSetClipboardString, or until the library From 8d6f265441959d0eff2d3fd97edfb99b98ce2777 Mon Sep 17 00:00:00 2001 From: Camilla Berglund Date: Thu, 20 Oct 2016 00:50:54 +0200 Subject: [PATCH 16/40] Update default name from OS X to macOS Note that earlier releases are still referred to by their proper names; OS X or Mac OS X . --- README.md | 2 +- docs/Doxyfile.in | 2 +- docs/build.dox | 16 ++++++++-------- docs/compat.dox | 4 ++-- docs/compile.dox | 11 ++++++----- docs/context.dox | 2 +- docs/news.dox | 11 ++++++----- docs/quick.dox | 2 +- docs/window.dox | 2 +- include/GLFW/glfw3.h | 22 +++++++++++----------- src/cocoa_init.m | 2 +- src/cocoa_monitor.m | 2 +- src/cocoa_platform.h | 2 +- src/cocoa_time.c | 2 +- src/cocoa_window.m | 6 +++--- src/nsgl_context.h | 2 +- src/nsgl_context.m | 16 ++++++++-------- src/win32_window.c | 2 +- 18 files changed, 55 insertions(+), 53 deletions(-) diff --git a/README.md b/README.md index 30e07efec..a17895952 100644 --- a/README.md +++ b/README.md @@ -36,7 +36,7 @@ does not need the headers for any context creation API (WGL, GLX, EGL, NSGL) or rendering API (OpenGL, OpenGL ES, Vulkan) to enable support for them. GLFW supports compilation on Windows with Visual C++ 2010 and later, MinGW and -MinGW-w64, on OS X with Clang and on Linux and other Unix-like systems with GCC +MinGW-w64, on macOS with Clang and on Linux and other Unix-like systems with GCC and Clang. It will likely compile in other environments as well, but this is not regularly tested. diff --git a/docs/Doxyfile.in b/docs/Doxyfile.in index 062ac53c1..6dd8dbe44 100644 --- a/docs/Doxyfile.in +++ b/docs/Doxyfile.in @@ -204,7 +204,7 @@ ALIASES = "thread_safety=@par Thread safety\n" \ "x11=__X11:__" \ "wayland=__Wayland:__" \ "win32=__Windows:__" \ - "osx=__OS X:__" + "macos=__macOS:__" # This tag can be used to specify a number of word-keyword mappings (TCL only). # A mapping has the form "name=value". For example adding diff --git a/docs/build.dox b/docs/build.dox index 20352b4ba..05c086144 100644 --- a/docs/build.dox +++ b/docs/build.dox @@ -65,7 +65,7 @@ The following macros control which OpenGL or OpenGL ES API header is included. Only one of these may be defined at a time. `GLFW_INCLUDE_GLCOREARB` makes the GLFW header include the modern -`GL/glcorearb.h` header (`OpenGL/gl3.h` on OS X) instead of the regular OpenGL +`GL/glcorearb.h` header (`OpenGL/gl3.h` on macOS) instead of the regular OpenGL header. `GLFW_INCLUDE_ES1` makes the GLFW header include the OpenGL ES 1.x `GLES/gl.h` @@ -84,7 +84,7 @@ header instead of the regular OpenGL header. header. This is useful in combination with an extension loading library. If none of the above inclusion macros are defined, the standard OpenGL `GL/gl.h` -header (`OpenGL/gl.h` on OS X) is included. +header (`OpenGL/gl.h` on macOS) is included. The following macros control the inclusion of additional API headers. Any number of these may be defined simultaneously, and/or together with one of the @@ -119,7 +119,7 @@ a shared library / dynamic library / DLL then it takes care of these links. However, if you are using GLFW as a static library then your executable will need to link against these libraries. -On Windows and OS X, the list of system libraries is static and can be +On Windows and macOS, the list of system libraries is static and can be hard-coded into your build environment. See the section for your development environment below. On Linux and other Unix-like operating systems, the list varies but can be retrieved in various ways as described below. @@ -289,7 +289,7 @@ env PKG_CONFIG_PATH=path/to/glfw/src cc `pkg-config --cflags glfw3` -o myprog my @endcode The dependencies do not include OpenGL or GLU, as GLFW loads any OpenGL, OpenGL -ES or Vulkan libraries it needs at runtime and does not use GLU. On OS X, GLU +ES or Vulkan libraries it needs at runtime and does not use GLU. On macOS, GLU is built into the OpenGL framework, so if you need GLU you don't need to do anything extra. If you need GLU and are using Linux or BSD, you should add the `glu` pkg-config package. @@ -309,7 +309,7 @@ cc `pkg-config --cflags glfw3 glu` -o myprog myprog.c `pkg-config --static --lib @endcode -@subsection build_link_xcode With Xcode on OS X +@subsection build_link_xcode With Xcode on macOS If you are using the dynamic library version of GLFW, simply add it to the project dependencies. @@ -319,10 +319,10 @@ OpenGL, IOKit and CoreVideo frameworks to the project as dependencies. They can all be found in `/System/Library/Frameworks`. -@subsection build_link_osx With command-line on OS X +@subsection build_link_osx With command-line on macOS It is recommended that you use [pkg-config](@ref build_link_pkgconfig) when -building from the command line on OS X. That way you will get any new +building from the command line on macOS. That way you will get any new dependencies added automatically. If you still wish to build manually, you need to add the required frameworks and libraries to your command-line yourself using the `-l` and `-framework` switches. @@ -342,6 +342,6 @@ against it from the command-line. The OpenGL framework contains both the OpenGL and GLU APIs, so there is nothing special to do when using GLU. Also note that even though your machine may have `libGL`-style OpenGL libraries, they are for use with the X Window System and -will _not_ work with the OS X native version of GLFW. +will _not_ work with the macOS native version of GLFW. */ diff --git a/docs/compat.dox b/docs/compat.dox index 175036b91..624dc3b17 100644 --- a/docs/compat.dox +++ b/docs/compat.dox @@ -160,7 +160,7 @@ extensions to provide support for sRGB framebuffers. Where both of these extension are unavailable, the `GLFW_SRGB_CAPABLE` hint will have no effect. -@section compat_osx OpenGL 3.2 and later on OS X +@section compat_osx OpenGL 3.2 and later on macOS Support for OpenGL 3.2 and above was introduced with OS X 10.7 and even then only forward-compatible, core profile contexts are supported. Support for @@ -217,7 +217,7 @@ surfaces on Mir. If any of these extensions are not available, @ref glfwGetRequiredInstanceExtensions will return an empty list and window surface creation will fail. -GLFW does not support any extensions for window surface creation on OS X, +GLFW does not support any extensions for window surface creation on macOS, meaning@ref glfwGetRequiredInstanceExtensions will return an empty list and window surface creation will fail. diff --git a/docs/compile.dox b/docs/compile.dox index c77a26c0e..002b26bf7 100644 --- a/docs/compile.dox +++ b/docs/compile.dox @@ -14,7 +14,8 @@ GLFW uses [CMake](http://www.cmake.org/) to generate project files or makefiles for a particular development environment. If you are on a Unix-like system such as Linux or FreeBSD or have a package system like Fink, MacPorts, Cygwin or Homebrew, you can simply install its CMake package. If not, you can download -installers for Windows and OS X from the [CMake website](http://www.cmake.org/). +installers for Windows and macOS from the +[CMake website](http://www.cmake.org/). @note CMake only generates project files or makefiles. It does not compile the actual GLFW library. To compile GLFW, first generate these files for your @@ -76,11 +77,11 @@ the CMake wiki. Once you have this set up, move on to @ref compile_generate. -@subsubsection compile_deps_xcode Dependencies for Xcode on OS X +@subsubsection compile_deps_xcode Dependencies for Xcode on macOS Xcode comes with all necessary tools except for CMake. The required headers -and libraries are included in the core OS X frameworks. Xcode can be downloaded -from the Mac App Store or from the ADC Member Center. +and libraries are included in the core macOS frameworks. Xcode can be +downloaded from the Mac App Store or from the ADC Member Center. Once you have Xcode installed, move on to @ref compile_generate. @@ -201,7 +202,7 @@ the library. statically into the application. -@subsubsection compile_options_osx OS X specific CMake options +@subsubsection compile_options_osx macOS specific CMake options `GLFW_USE_CHDIR` determines whether `glfwInit` changes the current directory of bundled applications to the `Contents/Resources` directory. diff --git a/docs/context.dox b/docs/context.dox index ad3b1848a..3f972828c 100644 --- a/docs/context.dox +++ b/docs/context.dox @@ -84,7 +84,7 @@ objects are recommended for rendering with such contexts. You should still [process events](@ref events) as long as you have at least one window, even if none of them are visible. -__OS X:__ The first time a window is created the menu bar is populated with +@macos The first time a window is created the menu bar is populated with common commands like Hide, Quit and About. This is not desirable for example when writing a command-line only application. The menu bar setup can be disabled with a [compile-time option](@ref compile_options_osx). diff --git a/docs/news.dox b/docs/news.dox index 1e92eb4fa..330191577 100644 --- a/docs/news.dox +++ b/docs/news.dox @@ -293,11 +293,12 @@ through CMake options. @subsection news_30_hidpi High-DPI support -GLFW now supports high-DPI monitors on both Windows and OS X, giving windows full -resolution framebuffers where other UI elements are scaled up. To achieve this, -@ref glfwGetFramebufferSize and @ref glfwSetFramebufferSizeCallback have been -added. These work with pixels, while the rest of the GLFW API works with screen -coordinates. This is important as OpenGL uses pixels, not screen coordinates. +GLFW now supports high-DPI monitors on both Windows and macOS, giving windows +full resolution framebuffers where other UI elements are scaled up. To achieve +this, @ref glfwGetFramebufferSize and @ref glfwSetFramebufferSizeCallback have +been added. These work with pixels, while the rest of the GLFW API works with +screen coordinates. This is important as OpenGL uses pixels, not screen +coordinates. @subsection news_30_error Error callback diff --git a/docs/quick.dox b/docs/quick.dox index dccc834d9..dc1a2f8d9 100644 --- a/docs/quick.dox +++ b/docs/quick.dox @@ -340,7 +340,7 @@ The program above can be found in the [source package](http://www.glfw.org/download.html) as `examples/simple.c` and is compiled along with all other examples when you build GLFW. If you built GLFW from the source package then already have this as `simple.exe` on -Windows, `simple` on Linux or `simple.app` on OS X. +Windows, `simple` on Linux or `simple.app` on macOS. This tutorial used only a few of the many functions GLFW provides. There are guides for each of the areas covered by GLFW. Each guide will introduce all the diff --git a/docs/window.dox b/docs/window.dox index 1ad097422..d725106a3 100644 --- a/docs/window.dox +++ b/docs/window.dox @@ -258,7 +258,7 @@ create the context. Possible values are `GLFW_NATIVE_CONTEXT_API` and requested, this hint is ignored. @par -__OS X:__ The EGL API is not available on this platform and requests to use it +@macos The EGL API is not available on this platform and requests to use it will fail. @par diff --git a/include/GLFW/glfw3.h b/include/GLFW/glfw3.h index ebe178cfd..73ebedd2c 100644 --- a/include/GLFW/glfw3.h +++ b/include/GLFW/glfw3.h @@ -116,7 +116,7 @@ extern "C" { #endif /* CALLBACK */ /* Include because most Windows GLU headers need wchar_t and - * the OS X OpenGL header blocks the definition of ptrdiff_t by glext.h. + * the macOS OpenGL header blocks the definition of ptrdiff_t by glext.h. * Include it unconditionally to avoid surprising side-effects. */ #include @@ -561,7 +561,7 @@ extern "C" { * @par * Some pre-installed Windows graphics drivers do not support OpenGL. AMD only * supports OpenGL ES via EGL, while Nvidia and Intel only support it via - * a WGL or GLX extension. OS X does not provide OpenGL ES at all. The Mesa + * a WGL or GLX extension. macOS does not provide OpenGL ES at all. The Mesa * EGL, OpenGL and OpenGL ES libraries do not interface with the Nvidia binary * driver. Older graphics drivers do not support Vulkan. */ @@ -1247,7 +1247,7 @@ typedef struct GLFWimage * * @errors Possible errors include @ref GLFW_PLATFORM_ERROR. * - * @remark @osx This function will change the current directory of the + * @remark @macos This function will change the current directory of the * application to the `Contents/Resources` subdirectory of the application's * bundle, if present. This can be disabled with a * [compile-time option](@ref compile_options_osx). @@ -1821,19 +1821,19 @@ GLFWAPI void glfwWindowHint(int hint, int value); * @remark @win32 The context to share resources with must not be current on * any other thread. * - * @remark @osx The GLFW window has no icon, as it is not a document + * @remark @macos The GLFW window has no icon, as it is not a document * window, but the dock icon will be the same as the application bundle's icon. * For more information on bundles, see the * [Bundle Programming Guide](https://developer.apple.com/library/mac/documentation/CoreFoundation/Conceptual/CFBundles/) * in the Mac Developer Library. * - * @remark @osx The first time a window is created the menu bar is populated + * @remark @macos The first time a window is created the menu bar is populated * with common commands like Hide, Quit and About. The About entry opens * a minimal about dialog with information from the application's bundle. The * menu bar can be disabled with a * [compile-time option](@ref compile_options_osx). * - * @remark @osx On OS X 10.10 and later the window frame will not be rendered + * @remark @macos On OS X 10.10 and later the window frame will not be rendered * at full resolution on Retina displays unless the `NSHighResolutionCapable` * key is enabled in the application bundle's `Info.plist`. For more * information, see @@ -1959,7 +1959,7 @@ GLFWAPI void glfwSetWindowShouldClose(GLFWwindow* window, int value); * @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref * GLFW_PLATFORM_ERROR. * - * @remark @osx The window title will not be updated until the next time you + * @remark @macos The window title will not be updated until the next time you * process events. * * @thread_safety This function must only be called from the main thread. @@ -1996,7 +1996,7 @@ GLFWAPI void glfwSetWindowTitle(GLFWwindow* window, const char* title); * @pointer_lifetime The specified image data is copied before this function * returns. * - * @remark @osx The GLFW window has no icon, as it is not a document + * @remark @macos The GLFW window has no icon, as it is not a document * window, so this function does nothing. The dock icon will be the same as * the application bundle's icon. For more information on bundles, see the * [Bundle Programming Guide](https://developer.apple.com/library/mac/documentation/CoreFoundation/Conceptual/CFBundles/) @@ -2692,8 +2692,8 @@ GLFWAPI GLFWwindowsizefun glfwSetWindowSizeCallback(GLFWwindow* window, GLFWwind * * @errors Possible errors include @ref GLFW_NOT_INITIALIZED. * - * @remark @osx Selecting Quit from the application menu will trigger the close - * callback for all windows. + * @remark @macos Selecting Quit from the application menu will trigger the + * close callback for all windows. * * @thread_safety This function must only be called from the main thread. * @@ -3458,7 +3458,7 @@ GLFWAPI GLFWkeyfun glfwSetKeyCallback(GLFWwindow* window, GLFWkeyfun cbfun); * * The character callback behaves as system text input normally does and will * not be called if modifier keys are held down that would prevent normal text - * input on that platform, for example a Super (Command) key on OS X or Alt key + * input on that platform, for example a Super (Command) key on macOS or Alt key * on Windows. There is a * [character with modifiers callback](@ref glfwSetCharModsCallback) that * receives these events. diff --git a/src/cocoa_init.m b/src/cocoa_init.m index 3c92fb4f0..fba749dbc 100644 --- a/src/cocoa_init.m +++ b/src/cocoa_init.m @@ -1,5 +1,5 @@ //======================================================================== -// GLFW 3.3 OS X - www.glfw.org +// GLFW 3.3 macOS - www.glfw.org //------------------------------------------------------------------------ // Copyright (c) 2009-2016 Camilla Berglund // diff --git a/src/cocoa_monitor.m b/src/cocoa_monitor.m index e7c931e38..0d6cb8fd8 100644 --- a/src/cocoa_monitor.m +++ b/src/cocoa_monitor.m @@ -1,5 +1,5 @@ //======================================================================== -// GLFW 3.3 OS X - www.glfw.org +// GLFW 3.3 macOS - www.glfw.org //------------------------------------------------------------------------ // Copyright (c) 2002-2006 Marcus Geelnard // Copyright (c) 2006-2016 Camilla Berglund diff --git a/src/cocoa_platform.h b/src/cocoa_platform.h index d76d7de74..6147e538b 100644 --- a/src/cocoa_platform.h +++ b/src/cocoa_platform.h @@ -1,5 +1,5 @@ //======================================================================== -// GLFW 3.3 OS X - www.glfw.org +// GLFW 3.3 macOS - www.glfw.org //------------------------------------------------------------------------ // Copyright (c) 2009-2016 Camilla Berglund // diff --git a/src/cocoa_time.c b/src/cocoa_time.c index 9a0bf6a2b..5e6d893af 100644 --- a/src/cocoa_time.c +++ b/src/cocoa_time.c @@ -1,5 +1,5 @@ //======================================================================== -// GLFW 3.3 OS X - www.glfw.org +// GLFW 3.3 macOS - www.glfw.org //------------------------------------------------------------------------ // Copyright (c) 2009-2016 Camilla Berglund // diff --git a/src/cocoa_window.m b/src/cocoa_window.m index 1cc2ffebe..5db64cc8d 100644 --- a/src/cocoa_window.m +++ b/src/cocoa_window.m @@ -1,5 +1,5 @@ //======================================================================== -// GLFW 3.3 OS X - www.glfw.org +// GLFW 3.3 macOS - www.glfw.org //------------------------------------------------------------------------ // Copyright (c) 2009-2016 Camilla Berglund // @@ -162,7 +162,7 @@ static void releaseMonitor(_GLFWwindow* window) _glfwRestoreVideoModeNS(window->monitor); } -// Translates OS X key modifiers into GLFW ones +// Translates macOS key modifiers into GLFW ones // static int translateFlags(NSUInteger flags) { @@ -180,7 +180,7 @@ static int translateFlags(NSUInteger flags) return mods; } -// Translates a OS X keycode to a GLFW keycode +// Translates a macOS keycode to a GLFW keycode // static int translateKey(unsigned int key) { diff --git a/src/nsgl_context.h b/src/nsgl_context.h index aa42d2375..62b194c89 100644 --- a/src/nsgl_context.h +++ b/src/nsgl_context.h @@ -1,5 +1,5 @@ //======================================================================== -// GLFW 3.3 OS X - www.glfw.org +// GLFW 3.3 macOS - www.glfw.org //------------------------------------------------------------------------ // Copyright (c) 2009-2016 Camilla Berglund // diff --git a/src/nsgl_context.m b/src/nsgl_context.m index 6c1f5a208..eea4e50ad 100644 --- a/src/nsgl_context.m +++ b/src/nsgl_context.m @@ -1,5 +1,5 @@ //======================================================================== -// GLFW 3.3 OS X - www.glfw.org +// GLFW 3.3 macOS - www.glfw.org //------------------------------------------------------------------------ // Copyright (c) 2009-2016 Camilla Berglund // @@ -124,14 +124,14 @@ GLFWbool _glfwCreateContextNSGL(_GLFWwindow* window, if (ctxconfig->client == GLFW_OPENGL_ES_API) { _glfwInputError(GLFW_API_UNAVAILABLE, - "NSGL: OpenGL ES is not available on OS X"); + "NSGL: OpenGL ES is not available on macOS"); return GLFW_FALSE; } if (ctxconfig->major == 3 && ctxconfig->minor < 2) { _glfwInputError(GLFW_VERSION_UNAVAILABLE, - "NSGL: The targeted version of OS X does not support OpenGL 3.0 or 3.1"); + "NSGL: The targeted version of macOS does not support OpenGL 3.0 or 3.1"); return GLFW_FALSE; } @@ -140,23 +140,23 @@ GLFWbool _glfwCreateContextNSGL(_GLFWwindow* window, if (!ctxconfig->forward) { _glfwInputError(GLFW_VERSION_UNAVAILABLE, - "NSGL: The targeted version of OS X only supports forward-compatible contexts for OpenGL 3.2 and above"); + "NSGL: The targeted version of macOS only supports forward-compatible contexts for OpenGL 3.2 and above"); return GLFW_FALSE; } if (ctxconfig->profile != GLFW_OPENGL_CORE_PROFILE) { _glfwInputError(GLFW_VERSION_UNAVAILABLE, - "NSGL: The targeted version of OS X only supports core profile contexts for OpenGL 3.2 and above"); + "NSGL: The targeted version of macOS only supports core profile contexts for OpenGL 3.2 and above"); return GLFW_FALSE; } } // Context robustness modes (GL_KHR_robustness) are not yet supported on - // OS X but are not a hard constraint, so ignore and continue + // macOS but are not a hard constraint, so ignore and continue // Context release behaviors (GL_KHR_context_flush_control) are not yet - // supported on OS X but are not a hard constraint, so ignore and continue + // supported on macOS but are not a hard constraint, so ignore and continue #define ADD_ATTR(x) { attributes[attributeCount++] = x; } #define ADD_ATTR2(x, y) { ADD_ATTR(x); ADD_ATTR(y); } @@ -206,7 +206,7 @@ GLFWbool _glfwCreateContextNSGL(_GLFWwindow* window, fbconfig->greenBits + fbconfig->blueBits; - // OS X needs non-zero color size, so set reasonable values + // macOS needs non-zero color size, so set reasonable values if (colorBits == 0) colorBits = 24; else if (colorBits < 15) diff --git a/src/win32_window.c b/src/win32_window.c index db24c75dd..c88f343d5 100644 --- a/src/win32_window.c +++ b/src/win32_window.c @@ -668,7 +668,7 @@ static LRESULT CALLBACK windowProc(HWND hWnd, UINT uMsg, case WM_MOUSEHWHEEL: { // This message is only sent on Windows Vista and later - // NOTE: The X-axis is inverted for consistency with OS X and X11. + // NOTE: The X-axis is inverted for consistency with macOS and X11 _glfwInputScroll(window, -((SHORT) HIWORD(wParam) / (double) WHEEL_DELTA), 0.0); return 0; } From 7420814fe20346d8e8b074ede9d77bc5546bb5fe Mon Sep 17 00:00:00 2001 From: Camilla Berglund Date: Tue, 25 Oct 2016 21:25:04 +0200 Subject: [PATCH 17/40] Win32: Poll only helper window messages at init --- src/win32_init.c | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/src/win32_init.c b/src/win32_init.c index 4c0979daf..5c4e9b629 100644 --- a/src/win32_init.c +++ b/src/win32_init.c @@ -304,6 +304,7 @@ static void createKeyTables(void) // static HWND createHelperWindow(void) { + MSG msg; HWND window = CreateWindowExW(WS_EX_OVERLAPPEDWINDOW, _GLFW_WNDCLASSNAME, L"GLFW helper window", @@ -336,6 +337,12 @@ static HWND createHelperWindow(void) DEVICE_NOTIFY_WINDOW_HANDLE); } + while (PeekMessageW(&msg, _glfw.win32.helperWindowHandle, 0, 0, PM_REMOVE)) + { + TranslateMessage(&msg); + DispatchMessageW(&msg); + } + return window; } @@ -423,8 +430,6 @@ int _glfwPlatformInit(void) if (!_glfw.win32.helperWindowHandle) return GLFW_FALSE; - _glfwPlatformPollEvents(); - _glfwInitTimerWin32(); _glfwInitJoysticksWin32(); From 3d4bd9667ac0312dfc938f4829b84a8ab0d37b55 Mon Sep 17 00:00:00 2001 From: Camilla Berglund Date: Tue, 25 Oct 2016 21:28:18 +0200 Subject: [PATCH 18/40] Win32: Fix joystick error message prefix --- src/win32_joystick.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/win32_joystick.c b/src/win32_joystick.c index 5d2e21c36..e3e9ec6d0 100644 --- a/src/win32_joystick.c +++ b/src/win32_joystick.c @@ -362,14 +362,14 @@ static BOOL CALLBACK deviceCallback(const DIDEVICEINSTANCE* di, void* user) &device, NULL))) { - _glfwInputError(GLFW_PLATFORM_ERROR, "DI: Failed to create device"); + _glfwInputError(GLFW_PLATFORM_ERROR, "Win32: Failed to create device"); return DIENUM_CONTINUE; } if (FAILED(IDirectInputDevice8_SetDataFormat(device, &_glfwDataFormat))) { _glfwInputError(GLFW_PLATFORM_ERROR, - "DI: Failed to set device data format"); + "Win32: Failed to set device data format"); IDirectInputDevice8_Release(device); return DIENUM_CONTINUE; @@ -381,7 +381,7 @@ static BOOL CALLBACK deviceCallback(const DIDEVICEINSTANCE* di, void* user) if (FAILED(IDirectInputDevice8_GetCapabilities(device, &dc))) { _glfwInputError(GLFW_PLATFORM_ERROR, - "DI: Failed to query device capabilities"); + "Win32: Failed to query device capabilities"); IDirectInputDevice8_Release(device); return DIENUM_CONTINUE; @@ -398,7 +398,7 @@ static BOOL CALLBACK deviceCallback(const DIDEVICEINSTANCE* di, void* user) &dipd.diph))) { _glfwInputError(GLFW_PLATFORM_ERROR, - "DI: Failed to set device axis mode"); + "Win32: Failed to set device axis mode"); IDirectInputDevice8_Release(device); return DIENUM_CONTINUE; @@ -415,7 +415,7 @@ static BOOL CALLBACK deviceCallback(const DIDEVICEINSTANCE* di, void* user) DIDFT_AXIS | DIDFT_BUTTON | DIDFT_POV))) { _glfwInputError(GLFW_PLATFORM_ERROR, - "DI: Failed to enumerate device objects"); + "Win32: Failed to enumerate device objects"); IDirectInputDevice8_Release(device); free(data.objects); @@ -664,7 +664,7 @@ void _glfwInitJoysticksWin32(void) NULL))) { _glfwInputError(GLFW_PLATFORM_ERROR, - "DI: Failed to create interface"); + "Win32: Failed to create interface"); } } From ec17161651cd57282d98ab34f9a1e6ba97f3b124 Mon Sep 17 00:00:00 2001 From: Camilla Berglund Date: Fri, 28 Oct 2016 06:20:20 +0200 Subject: [PATCH 19/40] Note that mode switching does not affect context --- include/GLFW/glfw3.h | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/include/GLFW/glfw3.h b/include/GLFW/glfw3.h index 73ebedd2c..45e270045 100644 --- a/include/GLFW/glfw3.h +++ b/include/GLFW/glfw3.h @@ -2525,6 +2525,10 @@ GLFWAPI GLFWmonitor* glfwGetWindowMonitor(GLFWwindow* window); * @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref * GLFW_PLATFORM_ERROR. * + * @remark The OpenGL or OpenGL ES context will not be destroyed or otherwise + * affected by any resizing or mode switching, although you may need to update + * your viewport if the framebuffer size has changed. + * * @remark @wayland The desired window position is ignored, as there is no way * for an application to set this property. * From 85f867983fcf7b14f496fcf095dbc0bbdcb4746c Mon Sep 17 00:00:00 2001 From: Camilla Berglund Date: Tue, 1 Nov 2016 20:04:46 +0100 Subject: [PATCH 20/40] Update Vulkan test to 1.0.26.0 Fixes #727. Fixes #758. --- tests/vulkan.c | 531 +++++++++++++++++++++++++++---------------------- 1 file changed, 288 insertions(+), 243 deletions(-) diff --git a/tests/vulkan.c b/tests/vulkan.c index 1b62d6814..bbfcb17d7 100644 --- a/tests/vulkan.c +++ b/tests/vulkan.c @@ -3,24 +3,17 @@ * Copyright (c) 2015-2016 Valve Corporation * Copyright (c) 2015-2016 LunarG, Inc. * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and/or associated documentation files (the "Materials"), to - * deal in the Materials without restriction, including without limitation the - * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or - * sell copies of the Materials, and to permit persons to whom the Materials are - * furnished to do so, subject to the following conditions: + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * The above copyright notice(s) and this permission notice shall be included in - * all copies or substantial portions of the Materials. + * http://www.apache.org/licenses/LICENSE-2.0 * - * THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. - * - * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, - * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR - * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE MATERIALS OR THE - * USE OR OTHER DEALINGS IN THE MATERIALS. + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * * Author: Chia-I Wu * Author: Cody Northrop @@ -28,6 +21,8 @@ * Author: Ian Elliott * Author: Jon Ashburn * Author: Piers Daniell + * Author: Gwan-gyeong Mun + * Porter: Camilla Berglund */ /* * Draw a textured triangle with depth testing. This is written against Intel @@ -35,22 +30,24 @@ * should. It also does no error checking. */ -#ifndef _MSC_VER -#define _ISOC11_SOURCE /* for aligned_alloc() */ -#endif - #include #include #include #include #include +#include -#include +#ifdef _WIN32 +#include +#endif + +#define GLFW_INCLUDE_NONE +#define GLFW_INCLUDE_VULKAN #include #define DEMO_TEXTURE_COUNT 1 #define VERTEX_BUFFER_BIND_ID 0 -#define APP_SHORT_NAME "vulkan" +#define APP_SHORT_NAME "tri" #define APP_LONG_NAME "The Vulkan Triangle Demo Program" #define ARRAY_SIZE(a) (sizeof(a) / sizeof(a[0])) @@ -245,39 +242,23 @@ struct texture_object { int32_t tex_width, tex_height; }; +static int validation_error = 0; + VKAPI_ATTR VkBool32 VKAPI_CALL -dbgFunc(VkFlags msgFlags, VkDebugReportObjectTypeEXT objType, - uint64_t srcObject, size_t location, int32_t msgCode, - const char *pLayerPrefix, const char *pMsg, void *pUserData) { - char *message = (char *)malloc(strlen(pMsg) + 100); +BreakCallback(VkFlags msgFlags, VkDebugReportObjectTypeEXT objType, + uint64_t srcObject, size_t location, int32_t msgCode, + const char *pLayerPrefix, const char *pMsg, + void *pUserData) { +#ifdef _WIN32 + DebugBreak(); +#else + raise(SIGTRAP); +#endif - assert(message); - - if (msgFlags & VK_DEBUG_REPORT_ERROR_BIT_EXT) { - sprintf(message, "ERROR: [%s] Code %d : %s", pLayerPrefix, msgCode, - pMsg); - } else if (msgFlags & VK_DEBUG_REPORT_WARNING_BIT_EXT) { - sprintf(message, "WARNING: [%s] Code %d : %s", pLayerPrefix, msgCode, - pMsg); - } else { - return false; - } - - printf("%s\n", message); - fflush(stdout); - free(message); - - /* - * false indicates that layer should not bail-out of an - * API call that had validation failures. This may mean that the - * app dies inside the driver due to invalid parameter(s). - * That's what would happen without validation layers, so we'll - * keep that behavior here. - */ return false; } -typedef struct _SwapchainBuffers { +typedef struct { VkImage image; VkCommandBuffer cmd; VkImageView view; @@ -288,20 +269,19 @@ struct demo { VkSurfaceKHR surface; bool use_staging_buffer; - VkAllocationCallbacks allocator; - VkInstance inst; VkPhysicalDevice gpu; VkDevice device; VkQueue queue; VkPhysicalDeviceProperties gpu_props; + VkPhysicalDeviceFeatures gpu_features; VkQueueFamilyProperties *queue_props; uint32_t graphics_queue_node_index; uint32_t enabled_extension_count; uint32_t enabled_layer_count; const char *extension_names[64]; - char *device_validation_layers[64]; + const char *enabled_layers[64]; int width, height; VkFormat format; @@ -363,10 +343,14 @@ struct demo { VkPhysicalDeviceMemoryProperties memory_properties; + int32_t curFrame; + int32_t frameCount; bool validate; + bool use_break; PFN_vkCreateDebugReportCallbackEXT CreateDebugReportCallback; PFN_vkDestroyDebugReportCallbackEXT DestroyDebugReportCallback; VkDebugReportCallbackEXT msg_callback; + PFN_vkDebugReportMessageEXT DebugReportMessage; float depthStencil; float depthIncrement; @@ -375,6 +359,40 @@ struct demo { uint32_t queue_count; }; +VKAPI_ATTR VkBool32 VKAPI_CALL +dbgFunc(VkFlags msgFlags, VkDebugReportObjectTypeEXT objType, + uint64_t srcObject, size_t location, int32_t msgCode, + const char *pLayerPrefix, const char *pMsg, void *pUserData) { + char *message = (char *)malloc(strlen(pMsg) + 100); + + assert(message); + + validation_error = 1; + + if (msgFlags & VK_DEBUG_REPORT_ERROR_BIT_EXT) { + sprintf(message, "ERROR: [%s] Code %d : %s", pLayerPrefix, msgCode, + pMsg); + } else if (msgFlags & VK_DEBUG_REPORT_WARNING_BIT_EXT) { + sprintf(message, "WARNING: [%s] Code %d : %s", pLayerPrefix, msgCode, + pMsg); + } else { + return false; + } + + printf("%s\n", message); + fflush(stdout); + free(message); + + /* + * false indicates that layer should not bail-out of an + * API call that had validation failures. This may mean that the + * app dies inside the driver due to invalid parameter(s). + * That's what would happen without validation layers, so we'll + * keep that behavior here. + */ + return false; +} + // Forward declaration: static void demo_resize(struct demo *demo); @@ -382,9 +400,8 @@ static bool memory_type_from_properties(struct demo *demo, uint32_t typeBits, VkFlags requirements_mask, uint32_t *typeIndex) { uint32_t i; - // Search memtypes to find first index with those properties - for (i = 0; i < 32; i++) { + for (i = 0; i < VK_MAX_MEMORY_TYPES; i++) { if ((typeBits & 1) == 1) { // Type is available, does it match user properties? if ((demo->memory_properties.memoryTypes[i].propertyFlags & @@ -433,7 +450,9 @@ static void demo_flush_init_cmd(struct demo *demo) { static void demo_set_image_layout(struct demo *demo, VkImage image, VkImageAspectFlags aspectMask, VkImageLayout old_image_layout, - VkImageLayout new_image_layout) { + VkImageLayout new_image_layout, + VkAccessFlagBits srcAccessMask) { + VkResult U_ASSERT_ONLY err; if (demo->setup_cmd == VK_NULL_HANDLE) { @@ -448,21 +467,11 @@ static void demo_set_image_layout(struct demo *demo, VkImage image, err = vkAllocateCommandBuffers(demo->device, &cmd, &demo->setup_cmd); assert(!err); - VkCommandBufferInheritanceInfo cmd_buf_hinfo = { - .sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_INHERITANCE_INFO, - .pNext = NULL, - .renderPass = VK_NULL_HANDLE, - .subpass = 0, - .framebuffer = VK_NULL_HANDLE, - .occlusionQueryEnable = VK_FALSE, - .queryFlags = 0, - .pipelineStatistics = 0, - }; VkCommandBufferBeginInfo cmd_buf_info = { .sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO, .pNext = NULL, .flags = 0, - .pInheritanceInfo = &cmd_buf_hinfo, + .pInheritanceInfo = NULL, }; err = vkBeginCommandBuffer(demo->setup_cmd, &cmd_buf_info); assert(!err); @@ -471,7 +480,7 @@ static void demo_set_image_layout(struct demo *demo, VkImage image, VkImageMemoryBarrier image_memory_barrier = { .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER, .pNext = NULL, - .srcAccessMask = 0, + .srcAccessMask = srcAccessMask, .dstAccessMask = 0, .oldLayout = old_image_layout, .newLayout = new_image_layout, @@ -509,21 +518,11 @@ static void demo_set_image_layout(struct demo *demo, VkImage image, } static void demo_draw_build_cmd(struct demo *demo) { - const VkCommandBufferInheritanceInfo cmd_buf_hinfo = { - .sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_INHERITANCE_INFO, - .pNext = NULL, - .renderPass = VK_NULL_HANDLE, - .subpass = 0, - .framebuffer = VK_NULL_HANDLE, - .occlusionQueryEnable = VK_FALSE, - .queryFlags = 0, - .pipelineStatistics = 0, - }; const VkCommandBufferBeginInfo cmd_buf_info = { .sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO, .pNext = NULL, .flags = 0, - .pInheritanceInfo = &cmd_buf_hinfo, + .pInheritanceInfo = NULL, }; const VkClearValue clear_values[2] = { [0] = {.color.float32 = {0.2f, 0.2f, 0.2f, 0.2f}}, @@ -546,6 +545,23 @@ static void demo_draw_build_cmd(struct demo *demo) { err = vkBeginCommandBuffer(demo->draw_cmd, &cmd_buf_info); assert(!err); + // We can use LAYOUT_UNDEFINED as a wildcard here because we don't care what + // happens to the previous contents of the image + VkImageMemoryBarrier image_memory_barrier = { + .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER, + .pNext = NULL, + .srcAccessMask = 0, + .dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT, + .oldLayout = VK_IMAGE_LAYOUT_UNDEFINED, + .newLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, + .srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, + .dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, + .image = demo->buffers[demo->current_buffer].image, + .subresourceRange = {VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0, 1}}; + + vkCmdPipelineBarrier(demo->draw_cmd, VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, + VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT, 0, 0, NULL, 0, + NULL, 1, &image_memory_barrier); vkCmdBeginRenderPass(demo->draw_cmd, &rp_begin, VK_SUBPASS_CONTENTS_INLINE); vkCmdBindPipeline(demo->draw_cmd, VK_PIPELINE_BIND_POINT_GRAPHICS, demo->pipeline); @@ -599,20 +615,24 @@ static void demo_draw_build_cmd(struct demo *demo) { static void demo_draw(struct demo *demo) { VkResult U_ASSERT_ONLY err; - VkSemaphore presentCompleteSemaphore; - VkSemaphoreCreateInfo presentCompleteSemaphoreCreateInfo = { + VkSemaphore imageAcquiredSemaphore, drawCompleteSemaphore; + VkSemaphoreCreateInfo semaphoreCreateInfo = { .sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO, .pNext = NULL, .flags = 0, }; - err = vkCreateSemaphore(demo->device, &presentCompleteSemaphoreCreateInfo, - NULL, &presentCompleteSemaphore); + err = vkCreateSemaphore(demo->device, &semaphoreCreateInfo, + NULL, &imageAcquiredSemaphore); + assert(!err); + + err = vkCreateSemaphore(demo->device, &semaphoreCreateInfo, + NULL, &drawCompleteSemaphore); assert(!err); // Get the index of the next available swapchain image: err = demo->fpAcquireNextImageKHR(demo->device, demo->swapchain, UINT64_MAX, - presentCompleteSemaphore, + imageAcquiredSemaphore, (VkFence)0, // TODO: Show use of fence &demo->current_buffer); if (err == VK_ERROR_OUT_OF_DATE_KHR) { @@ -620,7 +640,8 @@ static void demo_draw(struct demo *demo) { // must be recreated: demo_resize(demo); demo_draw(demo); - vkDestroySemaphore(demo->device, presentCompleteSemaphore, NULL); + vkDestroySemaphore(demo->device, imageAcquiredSemaphore, NULL); + vkDestroySemaphore(demo->device, drawCompleteSemaphore, NULL); return; } else if (err == VK_SUBOPTIMAL_KHR) { // demo->swapchain is not as optimal as it could be, but the platform's @@ -629,12 +650,6 @@ static void demo_draw(struct demo *demo) { assert(!err); } - // Assume the command buffer has been run on current_buffer before so - // we need to set the image layout back to COLOR_ATTACHMENT_OPTIMAL - demo_set_image_layout(demo, demo->buffers[demo->current_buffer].image, - VK_IMAGE_ASPECT_COLOR_BIT, - VK_IMAGE_LAYOUT_PRESENT_SRC_KHR, - VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL); demo_flush_init_cmd(demo); // Wait for the present complete semaphore to be signaled to ensure @@ -642,7 +657,6 @@ static void demo_draw(struct demo *demo) { // engine has fully released ownership to the application, and it is // okay to render to the image. - // FIXME/TODO: DEAL WITH VK_IMAGE_LAYOUT_PRESENT_SRC_KHR demo_draw_build_cmd(demo); VkFence nullFence = VK_NULL_HANDLE; VkPipelineStageFlags pipe_stage_flags = @@ -650,12 +664,12 @@ static void demo_draw(struct demo *demo) { VkSubmitInfo submit_info = {.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO, .pNext = NULL, .waitSemaphoreCount = 1, - .pWaitSemaphores = &presentCompleteSemaphore, + .pWaitSemaphores = &imageAcquiredSemaphore, .pWaitDstStageMask = &pipe_stage_flags, .commandBufferCount = 1, .pCommandBuffers = &demo->draw_cmd, - .signalSemaphoreCount = 0, - .pSignalSemaphores = NULL}; + .signalSemaphoreCount = 1, + .pSignalSemaphores = &drawCompleteSemaphore}; err = vkQueueSubmit(demo->queue, 1, &submit_info, nullFence); assert(!err); @@ -663,12 +677,13 @@ static void demo_draw(struct demo *demo) { VkPresentInfoKHR present = { .sType = VK_STRUCTURE_TYPE_PRESENT_INFO_KHR, .pNext = NULL, + .waitSemaphoreCount = 1, + .pWaitSemaphores = &drawCompleteSemaphore, .swapchainCount = 1, .pSwapchains = &demo->swapchain, .pImageIndices = &demo->current_buffer, }; - // TBD/TODO: SHOULD THE "present" PARAMETER BE "const" IN THE HEADER? err = demo->fpQueuePresentKHR(demo->queue, &present); if (err == VK_ERROR_OUT_OF_DATE_KHR) { // demo->swapchain is out of date (e.g. the window was resized) and @@ -684,7 +699,8 @@ static void demo_draw(struct demo *demo) { err = vkQueueWaitIdle(demo->queue); assert(err == VK_SUCCESS); - vkDestroySemaphore(demo->device, presentCompleteSemaphore, NULL); + vkDestroySemaphore(demo->device, imageAcquiredSemaphore, NULL); + vkDestroySemaphore(demo->device, drawCompleteSemaphore, NULL); } static void demo_prepare_buffers(struct demo *demo) { @@ -709,12 +725,25 @@ static void demo_prepare_buffers(struct demo *demo) { assert(!err); VkExtent2D swapchainExtent; - // width and height are either both -1, or both not -1. - if (surfCapabilities.currentExtent.width == (uint32_t)-1) { - // If the surface size is undefined, the size is set to - // the size of the images requested. + // width and height are either both 0xFFFFFFFF, or both not 0xFFFFFFFF. + if (surfCapabilities.currentExtent.width == 0xFFFFFFFF) { + // If the surface size is undefined, the size is set to the size + // of the images requested, which must fit within the minimum and + // maximum values. swapchainExtent.width = demo->width; swapchainExtent.height = demo->height; + + if (swapchainExtent.width < surfCapabilities.minImageExtent.width) { + swapchainExtent.width = surfCapabilities.minImageExtent.width; + } else if (swapchainExtent.width > surfCapabilities.maxImageExtent.width) { + swapchainExtent.width = surfCapabilities.maxImageExtent.width; + } + + if (swapchainExtent.height < surfCapabilities.minImageExtent.height) { + swapchainExtent.height = surfCapabilities.minImageExtent.height; + } else if (swapchainExtent.height > surfCapabilities.maxImageExtent.height) { + swapchainExtent.height = surfCapabilities.maxImageExtent.height; + } } else { // If the surface size is defined, the swap chain size must match swapchainExtent = surfCapabilities.currentExtent; @@ -724,15 +753,16 @@ static void demo_prepare_buffers(struct demo *demo) { VkPresentModeKHR swapchainPresentMode = VK_PRESENT_MODE_FIFO_KHR; - // Determine the number of VkImage's to use in the swap chain (we desire to - // own only 1 image at a time, besides the images being displayed and - // queued for display): - uint32_t desiredNumberOfSwapchainImages = - surfCapabilities.minImageCount + 1; + // Determine the number of VkImage's to use in the swap chain. + // Application desires to only acquire 1 image at a time (which is + // "surfCapabilities.minImageCount"). + uint32_t desiredNumOfSwapchainImages = surfCapabilities.minImageCount; + // If maxImageCount is 0, we can ask for as many images as we want; + // otherwise we're limited to maxImageCount if ((surfCapabilities.maxImageCount > 0) && - (desiredNumberOfSwapchainImages > surfCapabilities.maxImageCount)) { + (desiredNumOfSwapchainImages > surfCapabilities.maxImageCount)) { // Application must settle for fewer images than desired: - desiredNumberOfSwapchainImages = surfCapabilities.maxImageCount; + desiredNumOfSwapchainImages = surfCapabilities.maxImageCount; } VkSurfaceTransformFlagsKHR preTransform; @@ -747,7 +777,7 @@ static void demo_prepare_buffers(struct demo *demo) { .sType = VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR, .pNext = NULL, .surface = demo->surface, - .minImageCount = desiredNumberOfSwapchainImages, + .minImageCount = desiredNumOfSwapchainImages, .imageFormat = demo->format, .imageColorSpace = demo->color_space, .imageExtent = @@ -818,14 +848,6 @@ static void demo_prepare_buffers(struct demo *demo) { demo->buffers[i].image = swapchainImages[i]; - // Render loop will expect image to have been used before and in - // VK_IMAGE_LAYOUT_PRESENT_SRC_KHR - // layout and will change to COLOR_ATTACHMENT_OPTIMAL, so init the image - // to that state - demo_set_image_layout( - demo, demo->buffers[i].image, VK_IMAGE_ASPECT_COLOR_BIT, - VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_PRESENT_SRC_KHR); - color_attachment_view.image = demo->buffers[i].image; err = vkCreateImageView(demo->device, &color_attachment_view, NULL, @@ -906,7 +928,8 @@ static void demo_prepare_depth(struct demo *demo) { demo_set_image_layout(demo, demo->depth.image, VK_IMAGE_ASPECT_DEPTH_BIT, VK_IMAGE_LAYOUT_UNDEFINED, - VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL); + VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL, + 0); /* create image view */ view.image = demo->depth.image; @@ -939,6 +962,7 @@ demo_prepare_texture_image(struct demo *demo, const uint32_t *tex_colors, .tiling = tiling, .usage = usage, .flags = 0, + .initialLayout = VK_IMAGE_LAYOUT_PREINITIALIZED }; VkMemoryAllocateInfo mem_alloc = { .sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO, @@ -997,7 +1021,8 @@ demo_prepare_texture_image(struct demo *demo, const uint32_t *tex_colors, tex_obj->imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; demo_set_image_layout(demo, tex_obj->image, VK_IMAGE_ASPECT_COLOR_BIT, - VK_IMAGE_LAYOUT_UNDEFINED, tex_obj->imageLayout); + VK_IMAGE_LAYOUT_PREINITIALIZED, tex_obj->imageLayout, + VK_ACCESS_HOST_WRITE_BIT); /* setting the image layout does not reference the actual memory so no need * to add a mem ref */ } @@ -1025,20 +1050,22 @@ static void demo_prepare_textures(struct demo *demo) { VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT) && !demo->use_staging_buffer) { /* Device can texture using linear textures */ - demo_prepare_texture_image(demo, tex_colors[i], &demo->textures[i], - VK_IMAGE_TILING_LINEAR, - VK_IMAGE_USAGE_SAMPLED_BIT, - VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT); + demo_prepare_texture_image( + demo, tex_colors[i], &demo->textures[i], VK_IMAGE_TILING_LINEAR, + VK_IMAGE_USAGE_SAMPLED_BIT, + VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | + VK_MEMORY_PROPERTY_HOST_COHERENT_BIT); } else if (props.optimalTilingFeatures & VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT) { /* Must use staging buffer to copy linear texture to optimized */ struct texture_object staging_texture; memset(&staging_texture, 0, sizeof(staging_texture)); - demo_prepare_texture_image(demo, tex_colors[i], &staging_texture, - VK_IMAGE_TILING_LINEAR, - VK_IMAGE_USAGE_TRANSFER_SRC_BIT, - VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT); + demo_prepare_texture_image( + demo, tex_colors[i], &staging_texture, VK_IMAGE_TILING_LINEAR, + VK_IMAGE_USAGE_TRANSFER_SRC_BIT, + VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | + VK_MEMORY_PROPERTY_HOST_COHERENT_BIT); demo_prepare_texture_image( demo, tex_colors[i], &demo->textures[i], @@ -1049,12 +1076,14 @@ static void demo_prepare_textures(struct demo *demo) { demo_set_image_layout(demo, staging_texture.image, VK_IMAGE_ASPECT_COLOR_BIT, staging_texture.imageLayout, - VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL); + VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, + 0); demo_set_image_layout(demo, demo->textures[i].image, VK_IMAGE_ASPECT_COLOR_BIT, demo->textures[i].imageLayout, - VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL); + VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, + 0); VkImageCopy copy_region = { .srcSubresource = {VK_IMAGE_ASPECT_COLOR_BIT, 0, 0, 1}, @@ -1072,7 +1101,8 @@ static void demo_prepare_textures(struct demo *demo) { demo_set_image_layout(demo, demo->textures[i].image, VK_IMAGE_ASPECT_COLOR_BIT, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, - demo->textures[i].imageLayout); + demo->textures[i].imageLayout, + 0); demo_flush_init_cmd(demo); @@ -1165,7 +1195,8 @@ static void demo_prepare_vertices(struct demo *demo) { mem_alloc.allocationSize = mem_reqs.size; pass = memory_type_from_properties(demo, mem_reqs.memoryTypeBits, - VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT, + VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | + VK_MEMORY_PROPERTY_HOST_COHERENT_BIT, &mem_alloc.memoryTypeIndex); assert(pass); @@ -1376,6 +1407,7 @@ static void demo_prepare_pipeline(struct demo *demo) { rs.depthClampEnable = VK_FALSE; rs.rasterizerDiscardEnable = VK_FALSE; rs.depthBiasEnable = VK_FALSE; + rs.lineWidth = 1.0f; memset(&cb, 0, sizeof(cb)); cb.sType = VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO; @@ -1608,6 +1640,9 @@ static void demo_run(struct demo *demo) { // Wait for work to finish before updating MVP. vkDeviceWaitIdle(demo->device); + demo->curFrame++; + if (demo->frameCount != INT32_MAX && demo->curFrame == demo->frameCount) + glfwSetWindowShouldClose(demo->window, GLFW_TRUE); } } @@ -1636,7 +1671,7 @@ static void demo_create_window(struct demo *demo) { * Return 1 (true) if all layer names specified in check_names * can be found in given layer properties. */ -static VkBool32 demo_check_layers(uint32_t check_count, char **check_names, +static VkBool32 demo_check_layers(uint32_t check_count, const char **check_names, uint32_t layer_count, VkLayerProperties *layers) { uint32_t i, j; @@ -1656,79 +1691,77 @@ static VkBool32 demo_check_layers(uint32_t check_count, char **check_names, return 1; } -VKAPI_ATTR void *VKAPI_CALL myrealloc(void *pUserData, void *pOriginal, - size_t size, size_t alignment, - VkSystemAllocationScope allocationScope) { - return realloc(pOriginal, size); -} - -VKAPI_ATTR void *VKAPI_CALL myalloc(void *pUserData, size_t size, - size_t alignment, - VkSystemAllocationScope allocationScope) { -#ifdef _MSC_VER - return _aligned_malloc(size, alignment); -#else - return aligned_alloc(alignment, size); -#endif -} - -VKAPI_ATTR void VKAPI_CALL myfree(void *pUserData, void *pMemory) { -#ifdef _MSC_VER - _aligned_free(pMemory); -#else - free(pMemory); -#endif -} - static void demo_init_vk(struct demo *demo) { VkResult err; - uint32_t required_extension_count; - const char** required_extensions; - uint32_t i; + uint32_t i = 0; + uint32_t required_extension_count = 0; uint32_t instance_extension_count = 0; uint32_t instance_layer_count = 0; - uint32_t device_validation_layer_count = 0; + uint32_t validation_layer_count = 0; + const char **required_extensions = NULL; + const char **instance_validation_layers = NULL; demo->enabled_extension_count = 0; demo->enabled_layer_count = 0; - char *instance_validation_layers[] = { - "VK_LAYER_LUNARG_mem_tracker", - "VK_LAYER_GOOGLE_unique_objects", + char *instance_validation_layers_alt1[] = { + "VK_LAYER_LUNARG_standard_validation" }; - demo->device_validation_layers[0] = "VK_LAYER_LUNARG_mem_tracker"; - demo->device_validation_layers[1] = "VK_LAYER_GOOGLE_unique_objects"; - device_validation_layer_count = 2; + char *instance_validation_layers_alt2[] = { + "VK_LAYER_GOOGLE_threading", "VK_LAYER_LUNARG_parameter_validation", + "VK_LAYER_LUNARG_object_tracker", "VK_LAYER_LUNARG_image", + "VK_LAYER_LUNARG_core_validation", "VK_LAYER_LUNARG_swapchain", + "VK_LAYER_GOOGLE_unique_objects" + }; /* Look for validation layers */ VkBool32 validation_found = 0; - err = vkEnumerateInstanceLayerProperties(&instance_layer_count, NULL); - assert(!err); + if (demo->validate) { - if (instance_layer_count > 0) { - VkLayerProperties *instance_layers = - malloc(sizeof(VkLayerProperties) * instance_layer_count); - err = vkEnumerateInstanceLayerProperties(&instance_layer_count, - instance_layers); + err = vkEnumerateInstanceLayerProperties(&instance_layer_count, NULL); assert(!err); - if (demo->validate) { + instance_validation_layers = instance_validation_layers_alt1; + if (instance_layer_count > 0) { + VkLayerProperties *instance_layers = + malloc(sizeof (VkLayerProperties) * instance_layer_count); + err = vkEnumerateInstanceLayerProperties(&instance_layer_count, + instance_layers); + assert(!err); + + validation_found = demo_check_layers( - ARRAY_SIZE(instance_validation_layers), - instance_validation_layers, instance_layer_count, - instance_layers); - demo->enabled_layer_count = ARRAY_SIZE(instance_validation_layers); + ARRAY_SIZE(instance_validation_layers_alt1), + instance_validation_layers, instance_layer_count, + instance_layers); + if (validation_found) { + demo->enabled_layer_count = ARRAY_SIZE(instance_validation_layers_alt1); + demo->enabled_layers[0] = "VK_LAYER_LUNARG_standard_validation"; + validation_layer_count = 1; + } else { + // use alternative set of validation layers + instance_validation_layers = instance_validation_layers_alt2; + demo->enabled_layer_count = ARRAY_SIZE(instance_validation_layers_alt2); + validation_found = demo_check_layers( + ARRAY_SIZE(instance_validation_layers_alt2), + instance_validation_layers, instance_layer_count, + instance_layers); + validation_layer_count = + ARRAY_SIZE(instance_validation_layers_alt2); + for (i = 0; i < validation_layer_count; i++) { + demo->enabled_layers[i] = instance_validation_layers[i]; + } + } + free(instance_layers); } - free(instance_layers); - } - - if (demo->validate && !validation_found) { - ERR_EXIT("vkEnumerateInstanceLayerProperties failed to find" - "required validation layer.\n\n" - "Please look at the Getting Started guide for additional " - "information.\n", - "vkCreateInstance Failure"); + if (!validation_found) { + ERR_EXIT("vkEnumerateInstanceLayerProperties failed to find " + "required validation layer.\n\n" + "Please look at the Getting Started guide for additional " + "information.\n", + "vkCreateInstance Failure"); + } } /* Look for instance extensions */ @@ -1792,11 +1825,7 @@ static void demo_init_vk(struct demo *demo) { uint32_t gpu_count; - demo->allocator.pfnAllocation = myalloc; - demo->allocator.pfnFree = myfree; - demo->allocator.pfnReallocation = myrealloc; - - err = vkCreateInstance(&inst_info, &demo->allocator, &demo->inst); + err = vkCreateInstance(&inst_info, NULL, &demo->inst); if (err == VK_ERROR_INCOMPATIBLE_DRIVER) { ERR_EXIT("Cannot find a compatible Vulkan installable client driver " "(ICD).\n\nPlease look at the Getting Started guide for " @@ -1834,40 +1863,6 @@ static void demo_init_vk(struct demo *demo) { "vkEnumeratePhysicalDevices Failure"); } - /* Look for validation layers */ - validation_found = 0; - demo->enabled_layer_count = 0; - uint32_t device_layer_count = 0; - err = - vkEnumerateDeviceLayerProperties(demo->gpu, &device_layer_count, NULL); - assert(!err); - - if (device_layer_count > 0) { - VkLayerProperties *device_layers = - malloc(sizeof(VkLayerProperties) * device_layer_count); - err = vkEnumerateDeviceLayerProperties(demo->gpu, &device_layer_count, - device_layers); - assert(!err); - - if (demo->validate) { - validation_found = demo_check_layers(device_validation_layer_count, - demo->device_validation_layers, - device_layer_count, - device_layers); - demo->enabled_layer_count = device_validation_layer_count; - } - - free(device_layers); - } - - if (demo->validate && !validation_found) { - ERR_EXIT("vkEnumerateDeviceLayerProperties failed to find " - "a required validation layer.\n\n" - "Please look at the Getting Started guide for additional " - "information.\n", - "vkCreateDevice Failure"); - } - /* Look for device extensions */ uint32_t device_extension_count = 0; VkBool32 swapchainExtFound = 0; @@ -1911,17 +1906,33 @@ static void demo_init_vk(struct demo *demo) { demo->CreateDebugReportCallback = (PFN_vkCreateDebugReportCallbackEXT)vkGetInstanceProcAddr( demo->inst, "vkCreateDebugReportCallbackEXT"); + demo->DestroyDebugReportCallback = + (PFN_vkDestroyDebugReportCallbackEXT)vkGetInstanceProcAddr( + demo->inst, "vkDestroyDebugReportCallbackEXT"); if (!demo->CreateDebugReportCallback) { ERR_EXIT( "GetProcAddr: Unable to find vkCreateDebugReportCallbackEXT\n", "vkGetProcAddr Failure"); } + if (!demo->DestroyDebugReportCallback) { + ERR_EXIT( + "GetProcAddr: Unable to find vkDestroyDebugReportCallbackEXT\n", + "vkGetProcAddr Failure"); + } + demo->DebugReportMessage = + (PFN_vkDebugReportMessageEXT)vkGetInstanceProcAddr( + demo->inst, "vkDebugReportMessageEXT"); + if (!demo->DebugReportMessage) { + ERR_EXIT("GetProcAddr: Unable to find vkDebugReportMessageEXT\n", + "vkGetProcAddr Failure"); + } + VkDebugReportCallbackCreateInfoEXT dbgCreateInfo; dbgCreateInfo.sType = VK_STRUCTURE_TYPE_DEBUG_REPORT_CREATE_INFO_EXT; dbgCreateInfo.flags = VK_DEBUG_REPORT_ERROR_BIT_EXT | VK_DEBUG_REPORT_WARNING_BIT_EXT; - dbgCreateInfo.pfnCallback = dbgFunc; - dbgCreateInfo.pUserData = NULL; + dbgCreateInfo.pfnCallback = demo->use_break ? BreakCallback : dbgFunc; + dbgCreateInfo.pUserData = demo; dbgCreateInfo.pNext = NULL; err = demo->CreateDebugReportCallback(demo->inst, &dbgCreateInfo, NULL, &demo->msg_callback); @@ -1945,11 +1956,6 @@ static void demo_init_vk(struct demo *demo) { GET_INSTANCE_PROC_ADDR(demo->inst, GetPhysicalDeviceSurfaceFormatsKHR); GET_INSTANCE_PROC_ADDR(demo->inst, GetPhysicalDeviceSurfacePresentModesKHR); GET_INSTANCE_PROC_ADDR(demo->inst, GetPhysicalDeviceSurfaceSupportKHR); - GET_INSTANCE_PROC_ADDR(demo->inst, CreateSwapchainKHR); - GET_INSTANCE_PROC_ADDR(demo->inst, DestroySwapchainKHR); - GET_INSTANCE_PROC_ADDR(demo->inst, GetSwapchainImagesKHR); - GET_INSTANCE_PROC_ADDR(demo->inst, AcquireNextImageKHR); - GET_INSTANCE_PROC_ADDR(demo->inst, QueuePresentKHR); vkGetPhysicalDeviceProperties(demo->gpu, &demo->gpu_props); @@ -1963,6 +1969,8 @@ static void demo_init_vk(struct demo *demo) { demo->queue_props); assert(demo->queue_count >= 1); + vkGetPhysicalDeviceFeatures(demo->gpu, &demo->gpu_features); + // Graphics queue and MemMgr queue can be separate. // TODO: Add support for separate queues, including synchronization, // and appropriate tracking for QueueSubmit @@ -1979,18 +1987,23 @@ static void demo_init_device(struct demo *demo) { .queueCount = 1, .pQueuePriorities = queue_priorities}; + + VkPhysicalDeviceFeatures features; + memset(&features, 0, sizeof(features)); + if (demo->gpu_features.shaderClipDistance) { + features.shaderClipDistance = VK_TRUE; + } + VkDeviceCreateInfo device = { .sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO, .pNext = NULL, .queueCreateInfoCount = 1, .pQueueCreateInfos = &queue, - .enabledLayerCount = demo->enabled_layer_count, - .ppEnabledLayerNames = - (const char *const *)((demo->validate) - ? demo->device_validation_layers - : NULL), + .enabledLayerCount = 0, + .ppEnabledLayerNames = NULL, .enabledExtensionCount = demo->enabled_extension_count, .ppEnabledExtensionNames = (const char *const *)demo->extension_names, + .pEnabledFeatures = &features, }; err = vkCreateDevice(demo->gpu, &device, NULL, &demo->device); @@ -2092,6 +2105,8 @@ static void demo_init_vk_swapchain(struct demo *demo) { } demo->color_space = surfFormats[0].colorSpace; + demo->curFrame = 0; + // Get Memory information and properties vkGetPhysicalDeviceMemoryProperties(demo->gpu, &demo->memory_properties); } @@ -2115,12 +2130,34 @@ static void demo_init_connection(struct demo *demo) { static void demo_init(struct demo *demo, const int argc, const char *argv[]) { int i; - memset(demo, 0, sizeof(*demo)); + demo->frameCount = INT32_MAX; - for (i = 0; i < argc; i++) { - if (strncmp(argv[i], "--use_staging", strlen("--use_staging")) == 0) + for (i = 1; i < argc; i++) { + if (strcmp(argv[i], "--use_staging") == 0) { demo->use_staging_buffer = true; + continue; + } + if (strcmp(argv[i], "--break") == 0) { + demo->use_break = true; + continue; + } + if (strcmp(argv[i], "--validate") == 0) { + demo->validate = true; + continue; + } + if (strcmp(argv[i], "--c") == 0 && demo->frameCount == INT32_MAX && + i < argc - 1 && sscanf(argv[i + 1], "%d", &demo->frameCount) == 1 && + demo->frameCount >= 0) { + i++; + continue; + } + + fprintf(stderr, "Usage:\n %s [--use_staging] [--validate] [--break] " + "[--c ]\n", + APP_SHORT_NAME); + fflush(stderr); + exit(1); } demo_init_connection(demo); @@ -2174,8 +2211,11 @@ static void demo_cleanup(struct demo *demo) { free(demo->buffers); vkDestroyDevice(demo->device, NULL); + if (demo->validate) { + demo->DestroyDebugReportCallback(demo->inst, demo->msg_callback, NULL); + } vkDestroySurfaceKHR(demo->inst, demo->surface, NULL); - vkDestroyInstance(demo->inst, &demo->allocator); + vkDestroyInstance(demo->inst, NULL); free(demo->queue_props); @@ -2186,6 +2226,11 @@ static void demo_cleanup(struct demo *demo) { static void demo_resize(struct demo *demo) { uint32_t i; + // In order to properly resize the window, we must re-create the swapchain + // AND redo the command buffers, etc. + // + // First, perform part of the demo_cleanup() function: + for (i = 0; i < demo->swapchainImageCount; i++) { vkDestroyFramebuffer(demo->device, demo->framebuffers[i], NULL); } @@ -2240,6 +2285,6 @@ int main(const int argc, const char *argv[]) { demo_cleanup(&demo); - return 0; + return validation_error; } From 67931bdeb28eb48ac106f536c3a71039e5dce9a5 Mon Sep 17 00:00:00 2001 From: Camilla Berglund Date: Wed, 26 Oct 2016 16:58:56 +0200 Subject: [PATCH 21/40] Replace last @remarks with @remark --- include/GLFW/glfw3.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/include/GLFW/glfw3.h b/include/GLFW/glfw3.h index 45e270045..c4841769c 100644 --- a/include/GLFW/glfw3.h +++ b/include/GLFW/glfw3.h @@ -4220,7 +4220,7 @@ GLFWAPI int glfwVulkanSupported(void); * @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref * GLFW_API_UNAVAILABLE. * - * @remarks Additional extensions may be required by future versions of GLFW. + * @remark Additional extensions may be required by future versions of GLFW. * You should check if any extensions you wish to enable are already in the * returned array, as it is an error to specify an extension more than once in * the `VkInstanceCreateInfo` struct. @@ -4349,7 +4349,7 @@ GLFWAPI int glfwGetPhysicalDevicePresentationSupport(VkInstance instance, VkPhys * @errors Possible errors include @ref GLFW_NOT_INITIALIZED, @ref * GLFW_API_UNAVAILABLE and @ref GLFW_PLATFORM_ERROR. * - * @remarks If an error occurs before the creation call is made, GLFW returns + * @remark If an error occurs before the creation call is made, GLFW returns * the Vulkan error code most appropriate for the error. Appropriate use of * @ref glfwVulkanSupported and @ref glfwGetRequiredInstanceExtensions should * eliminate almost all occurrences of these errors. From a9282bad29f951eabcacefc91fa07e68b05fc14b Mon Sep 17 00:00:00 2001 From: Camilla Berglund Date: Sun, 30 Oct 2016 00:02:11 +0200 Subject: [PATCH 22/40] Disable all dl* calls for _GLFW_VULKAN_STATIC --- src/vulkan.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/vulkan.c b/src/vulkan.c index 30f4b8741..2d3bfe6ce 100644 --- a/src/vulkan.c +++ b/src/vulkan.c @@ -136,8 +136,10 @@ GLFWbool _glfwInitVulkan(void) void _glfwTerminateVulkan(void) { +#if !defined(_GLFW_VULKAN_STATIC) if (_glfw.vk.handle) _glfw_dlclose(_glfw.vk.handle); +#endif } const char* _glfwGetVulkanResultString(VkResult result) @@ -236,8 +238,10 @@ GLFWAPI GLFWvkproc glfwGetInstanceProcAddress(VkInstance instance, } proc = (GLFWvkproc) vkGetInstanceProcAddr(instance, procname); +#if !defined(_GLFW_VULKAN_STATIC) if (!proc) proc = (GLFWvkproc) _glfw_dlsym(_glfw.vk.handle, procname); +#endif return proc; } From fef25ea9346d5fa4cc37d3957f7db48c7bac708e Mon Sep 17 00:00:00 2001 From: Camilla Berglund Date: Mon, 31 Oct 2016 01:42:04 +0100 Subject: [PATCH 23/40] Emit all Vulkan init errors from init function --- src/internal.h | 2 +- src/vulkan.c | 32 ++++++++++++++------------------ 2 files changed, 15 insertions(+), 19 deletions(-) diff --git a/src/internal.h b/src/internal.h index a623262e8..77af1d1c6 100644 --- a/src/internal.h +++ b/src/internal.h @@ -1060,7 +1060,7 @@ GLFWbool _glfwIsPrintable(int key); /*! @ingroup utility */ -GLFWbool _glfwInitVulkan(void); +GLFWbool _glfwInitVulkan(int mode); /*! @ingroup utility */ diff --git a/src/vulkan.c b/src/vulkan.c index 2d3bfe6ce..9908511ff 100644 --- a/src/vulkan.c +++ b/src/vulkan.c @@ -31,12 +31,15 @@ #include #include +#define _GLFW_FIND_LOADER 1 +#define _GLFW_REQUIRE_LOADER 2 + ////////////////////////////////////////////////////////////////////////// ////// GLFW internal API ////// ////////////////////////////////////////////////////////////////////////// -GLFWbool _glfwInitVulkan(void) +GLFWbool _glfwInitVulkan(int mode) { VkResult err; VkExtensionProperties* ep; @@ -54,7 +57,12 @@ GLFWbool _glfwInitVulkan(void) _glfw.vk.handle = _glfw_dlopen(name); if (!_glfw.vk.handle) + { + if (mode == _GLFW_REQUIRE_LOADER) + _glfwInputError(GLFW_API_UNAVAILABLE, "Vulkan: Loader not found"); + return GLFW_FALSE; + } _glfw.vk.GetInstanceProcAddr = (PFN_vkGetInstanceProcAddr) _glfw_dlsym(_glfw.vk.handle, "vkGetInstanceProcAddr"); @@ -205,7 +213,7 @@ const char* _glfwGetVulkanResultString(VkResult result) GLFWAPI int glfwVulkanSupported(void) { _GLFW_REQUIRE_INIT_OR_RETURN(GLFW_FALSE); - return _glfwInitVulkan(); + return _glfwInitVulkan(_GLFW_FIND_LOADER); } GLFWAPI const char** glfwGetRequiredInstanceExtensions(uint32_t* count) @@ -214,11 +222,8 @@ GLFWAPI const char** glfwGetRequiredInstanceExtensions(uint32_t* count) _GLFW_REQUIRE_INIT_OR_RETURN(NULL); - if (!_glfwInitVulkan()) - { - _glfwInputError(GLFW_API_UNAVAILABLE, "Vulkan: API not available"); + if (!_glfwInitVulkan(_GLFW_REQUIRE_LOADER)) return NULL; - } *count = 2; return (const char**) _glfw.vk.extensions; @@ -231,11 +236,8 @@ GLFWAPI GLFWvkproc glfwGetInstanceProcAddress(VkInstance instance, _GLFW_REQUIRE_INIT_OR_RETURN(NULL); - if (!_glfwInitVulkan()) - { - _glfwInputError(GLFW_API_UNAVAILABLE, "Vulkan: API not available"); + if (!_glfwInitVulkan(_GLFW_REQUIRE_LOADER)) return NULL; - } proc = (GLFWvkproc) vkGetInstanceProcAddr(instance, procname); #if !defined(_GLFW_VULKAN_STATIC) @@ -252,11 +254,8 @@ GLFWAPI int glfwGetPhysicalDevicePresentationSupport(VkInstance instance, { _GLFW_REQUIRE_INIT_OR_RETURN(GLFW_FALSE); - if (!_glfwInitVulkan()) - { - _glfwInputError(GLFW_API_UNAVAILABLE, "Vulkan: API not available"); + if (!_glfwInitVulkan(_GLFW_REQUIRE_LOADER)) return GLFW_FALSE; - } if (!_glfw.vk.extensions[0]) { @@ -283,11 +282,8 @@ GLFWAPI VkResult glfwCreateWindowSurface(VkInstance instance, _GLFW_REQUIRE_INIT_OR_RETURN(VK_ERROR_INITIALIZATION_FAILED); - if (!_glfwInitVulkan()) - { - _glfwInputError(GLFW_API_UNAVAILABLE, "Vulkan: API not available"); + if (!_glfwInitVulkan(_GLFW_REQUIRE_LOADER)) return VK_ERROR_INITIALIZATION_FAILED; - } if (!_glfw.vk.extensions[0]) { From 017162e3fdb3b32f23f0ecc3ed8d0f322243d7ac Mon Sep 17 00:00:00 2001 From: Camilla Berglund Date: Mon, 31 Oct 2016 01:59:22 +0100 Subject: [PATCH 24/40] Cocoa: Stop searching for system frameworks --- CMakeLists.txt | 18 +++++------------- 1 file changed, 5 insertions(+), 13 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 0a67af3a0..5215ca808 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -323,19 +323,11 @@ if (_GLFW_COCOA) set(_GLFW_USE_RETINA 1) endif() - # Set up library and include paths - find_library(COCOA_FRAMEWORK Cocoa) - find_library(IOKIT_FRAMEWORK IOKit) - find_library(CORE_FOUNDATION_FRAMEWORK CoreFoundation) - find_library(CORE_VIDEO_FRAMEWORK CoreVideo) - mark_as_advanced(COCOA_FRAMEWORK - IOKIT_FRAMEWORK - CORE_FOUNDATION_FRAMEWORK - CORE_VIDEO_FRAMEWORK) - list(APPEND glfw_LIBRARIES "${COCOA_FRAMEWORK}" - "${IOKIT_FRAMEWORK}" - "${CORE_FOUNDATION_FRAMEWORK}" - "${CORE_VIDEO_FRAMEWORK}") + list(APPEND glfw_LIBRARIES + "-framework Cocoa" + "-framework IOKit" + "-framework CoreFoundation" + "-framework CoreVideo") set(glfw_PKG_DEPS "") set(glfw_PKG_LIBS "-framework Cocoa -framework IOKit -framework CoreFoundation -framework CoreVideo") From c3db1cae3f42e36d091532930685222e07796548 Mon Sep 17 00:00:00 2001 From: Camilla Berglund Date: Tue, 1 Nov 2016 19:24:36 +0100 Subject: [PATCH 25/40] Fix glfwGetInstanceProcAddress for static linking --- README.md | 2 ++ src/vulkan.c | 8 +++++++- 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index a17895952..00be04ea4 100644 --- a/README.md +++ b/README.md @@ -107,6 +107,8 @@ information on what to include when reporting a bug. - Bugfix: Calling `glfwMaximizeWindow` on a full screen window was not ignored - Bugfix: `GLFW_INCLUDE_VULKAN` could not be combined with the corresponding OpenGL and OpenGL ES header macros +- Bugfix: `glfwGetInstanceProcAddress` returned `NULL` for + `vkGetInstanceProcAddr` when `_GLFW_VULKAN_STATIC` was enabled - [Win32] Bugfix: Undecorated windows could not be iconified by the user (#861) - [Cocoa] Bugfix: Disabling window aspect ratio would assert (#852) - [Cocoa] Bugfix: Window creation failed to set first responder (#876,#883) diff --git a/src/vulkan.c b/src/vulkan.c index 9908511ff..b4e701bd0 100644 --- a/src/vulkan.c +++ b/src/vulkan.c @@ -240,7 +240,13 @@ GLFWAPI GLFWvkproc glfwGetInstanceProcAddress(VkInstance instance, return NULL; proc = (GLFWvkproc) vkGetInstanceProcAddr(instance, procname); -#if !defined(_GLFW_VULKAN_STATIC) +#if defined(_GLFW_VULKAN_STATIC) + if (!proc) + { + if (strcmp(procname, "vkGetInstanceProcAddr") == 0) + return (GLFWvkproc) vkGetInstanceProcAddr; + } +#else if (!proc) proc = (GLFWvkproc) _glfw_dlsym(_glfw.vk.handle, procname); #endif From e94d16667b1696e90da6c1b5c551815b0f873534 Mon Sep 17 00:00:00 2001 From: Camilla Berglund Date: Fri, 14 Oct 2016 01:46:56 +0200 Subject: [PATCH 26/40] Cocoa: Add basic support for Vulkan via MoltenVK This adds basic support for MoltenVK, a Vulkan implementation on top of Metal, on macOS 10.11 and later. It looks for MoltenVK in the process via RTLD_DEFAULT symbol lookup if _GLFW_VULKAN_STATIC is disabled. glfwCreateWindowSurface now creates and sets a CAMetalLayer for the window content view, which is required for MoltenVK to function. You must help CMake find MoltenVK for the Vulkan test to be built. Fixes #870. --- CMake/modules/FindVulkan.cmake | 7 ++++ CMakeLists.txt | 14 ++++++- README.md | 1 + docs/compat.dox | 18 +++++---- docs/news.dox | 6 +++ docs/vulkan.dox | 26 ++++++++++++- include/GLFW/glfw3.h | 19 +++++++++- src/cocoa_platform.h | 13 +++++++ src/cocoa_window.m | 67 +++++++++++++++++++++++++++++++++- src/internal.h | 3 ++ src/vulkan.c | 28 ++++++++++---- tests/CMakeLists.txt | 2 +- 12 files changed, 182 insertions(+), 22 deletions(-) diff --git a/CMake/modules/FindVulkan.cmake b/CMake/modules/FindVulkan.cmake index d3a664a8b..2ea1200f5 100644 --- a/CMake/modules/FindVulkan.cmake +++ b/CMake/modules/FindVulkan.cmake @@ -20,6 +20,13 @@ if (WIN32) "$ENV{VULKAN_SDK}/Bin32" "$ENV{VK_SDK_PATH}/Bin32") endif() +elseif (APPLE) + find_library(VULKAN_LIBRARY MoltenVK) + if (VULKAN_LIBRARY) + set(VULKAN_STATIC_LIBRARY ${VULKAN_LIBRARY}) + find_path(VULKAN_INCLUDE_DIR NAMES vulkan/vulkan.h HINTS + "${VULKAN_LIBRARY}/Headers") + endif() else() find_path(VULKAN_INCLUDE_DIR NAMES vulkan/vulkan.h HINTS "$ENV{VULKAN_SDK}/include") diff --git a/CMakeLists.txt b/CMakeLists.txt index 5215ca808..388a555ff 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -129,6 +129,15 @@ if (MINGW) endif() endif() +if (APPLE) + # Dependencies required by the MoltenVK static library + set(GLFW_VULKAN_DEPS + "-lc++" + "-framework Cocoa" + "-framework Metal" + "-framework QuartzCore") +endif() + #-------------------------------------------------------------------- # Detect and select backend APIs #-------------------------------------------------------------------- @@ -158,7 +167,10 @@ endif() #-------------------------------------------------------------------- if (GLFW_VULKAN_STATIC) if (VULKAN_FOUND AND VULKAN_STATIC_LIBRARY) - list(APPEND glfw_LIBRARIES ${VULKAN_STATIC_LIBRARY}) + list(APPEND glfw_LIBRARIES ${VULKAN_STATIC_LIBRARY} ${GLFW_VULKAN_DEPS}) + if (BUILD_SHARED_LIBS) + message(WARNING "Linking Vulkan loader static library into GLFW") + endif() else() if (BUILD_SHARED_LIBS OR GLFW_BUILD_EXAMPLES OR GLFW_BUILD_TESTS) message(FATAL_ERROR "Vulkan loader static library not found") diff --git a/README.md b/README.md index 00be04ea4..68d257f93 100644 --- a/README.md +++ b/README.md @@ -110,6 +110,7 @@ information on what to include when reporting a bug. - Bugfix: `glfwGetInstanceProcAddress` returned `NULL` for `vkGetInstanceProcAddr` when `_GLFW_VULKAN_STATIC` was enabled - [Win32] Bugfix: Undecorated windows could not be iconified by the user (#861) +- [Cocoa] Added support for Vulkan window surface creation via MoltenVK (#870) - [Cocoa] Bugfix: Disabling window aspect ratio would assert (#852) - [Cocoa] Bugfix: Window creation failed to set first responder (#876,#883) - [EGL] Added support for `EGL_KHR_get_all_proc_addresses` (#871) diff --git a/docs/compat.dox b/docs/compat.dox index 624dc3b17..c54bdd91e 100644 --- a/docs/compat.dox +++ b/docs/compat.dox @@ -185,10 +185,11 @@ a non-default value will cause @ref glfwCreateWindow to fail and the @section compat_vulkan Vulkan loader and API -GLFW uses the standard system-wide Vulkan loader to access the Vulkan API. -This should be installed by graphics drivers and Vulkan SDKs. If this is not -available, @ref glfwVulkanSupported will return `GLFW_FALSE` and all other -Vulkan-related functions will fail with an @ref GLFW_API_UNAVAILABLE error. +By default, GLFW uses the standard system-wide Vulkan loader to access the +Vulkan API on all platforms except macOS. This is installed by both graphics +drivers and Vulkan SDKs. If the loader is not found, @ref glfwVulkanSupported +will return `GLFW_FALSE` and all other Vulkan-related functions will fail with +an @ref GLFW_API_UNAVAILABLE error. @section compat_wsi Vulkan WSI extensions @@ -201,6 +202,11 @@ surfaces on Microsoft Windows. If any of these extensions are not available, @ref glfwGetRequiredInstanceExtensions will return an empty list and window surface creation will fail. +GLFW uses the `VK_KHR_surface` and `VK_MVK_macos_surface` extensions to create +surfaces on macOS. If any of these extensions are not available, @ref +glfwGetRequiredInstanceExtensions will return an empty list and window surface +creation will fail. + GLFW uses the `VK_KHR_surface` and either the `VK_KHR_xlib_surface` or `VK_KHR_xcb_surface` extensions to create surfaces on X11. If `VK_KHR_surface` or both `VK_KHR_xlib_surface` and `VK_KHR_xcb_surface` are not available, @ref @@ -217,8 +223,4 @@ surfaces on Mir. If any of these extensions are not available, @ref glfwGetRequiredInstanceExtensions will return an empty list and window surface creation will fail. -GLFW does not support any extensions for window surface creation on macOS, -meaning@ref glfwGetRequiredInstanceExtensions will return an empty list and -window surface creation will fail. - */ diff --git a/docs/news.dox b/docs/news.dox index 330191577..707bd55ab 100644 --- a/docs/news.dox +++ b/docs/news.dox @@ -16,6 +16,12 @@ GLFW now supports querying the platform dependent scancode of any key with @ref glfwGetKeyScancode. +@subsection news_33_moltenvk Support for Vulkan on macOS via MoltenVK + +GLFW now supports the `VK_MVK_macos_surface` window surface creation extension +provided by MoltenVK. + + @section news_32 New features in 3.2 diff --git a/docs/vulkan.dox b/docs/vulkan.dox index e704222ac..32b964635 100644 --- a/docs/vulkan.dox +++ b/docs/vulkan.dox @@ -11,8 +11,10 @@ with Vulkan concepts like loaders, devices, queues and surfaces and leaves it to the Vulkan documentation to explain the details of Vulkan functions. To develop for Vulkan you should install an SDK for your platform, for example -the [LunarG Vulkan SDK](https://vulkan.lunarg.com/). Apart from the headers and -libraries, it also provides the validation layers necessary for development. +the [LunarG Vulkan SDK](https://vulkan.lunarg.com/) for Windows and Linux or +[MoltenVK](https://moltengl.com/moltenvk/) for macOS. Apart from headers and +link libraries, they should also provide the validation layers necessary for +development. The GLFW library does not need the Vulkan SDK to enable support for Vulkan. However, any Vulkan-specific test and example programs are built only if the @@ -28,6 +30,26 @@ are also guides for the other areas of the GLFW API. - @ref input_guide +@section vulkan_loader Linking against the Vulkan loader + +By default, GLFW will look for the Vulkan loader on demand at runtime via its +standard name (`vulkan-1.dll` on Windows, `libvulkan.so.1` on Linux and other +Unix-like systems). This means that GLFW does not need to be linked against the +loader. However, it also means that if you are using the static library form of +the Vulkan loader GLFW will either fail to find it or (worse) use the wrong one. + +The [GLFW_VULKAN_STATIC](@ref compile_options_shared) CMake option makes GLFW +link directly against the static form. Not linking against the Vulkan loader +will then be a compile-time error. + +@macos MoltenVK only provides the static library form of the Vulkan loader, but +GLFW is able to find it without +[GLFW_VULKAN_STATIC](@ref compile_options_shared) as long as it is linked into +any of the binaries already loaded into the process. As it is a static library, +you must also link against its dependencies: the `Cocoa`, `Metal` and +`QuartzCore` frameworks and the `libc++` library. + + @section vulkan_include Including the Vulkan and GLFW header files To include the Vulkan header, define [GLFW_INCLUDE_VULKAN](@ref build_macros) diff --git a/include/GLFW/glfw3.h b/include/GLFW/glfw3.h index c4841769c..db2bd8a9a 100644 --- a/include/GLFW/glfw3.h +++ b/include/GLFW/glfw3.h @@ -176,7 +176,11 @@ extern "C" { #endif #endif #if defined(GLFW_INCLUDE_VULKAN) - #include + #if defined(__APPLE__) + #include + #else + #include + #endif #endif #if defined(GLFW_DLL) && defined(_GLFW_BUILD_DLL) @@ -4225,6 +4229,9 @@ GLFWAPI int glfwVulkanSupported(void); * returned array, as it is an error to specify an extension more than once in * the `VkInstanceCreateInfo` struct. * + * @remark @macos This function currently only supports the + * `VK_MVK_macos_surface` extension from MoltenVK. + * * @pointer_lifetime The returned array is allocated and freed by GLFW. You * should not free it yourself. It is guaranteed to be valid only until the * library is terminated. @@ -4305,6 +4312,10 @@ GLFWAPI GLFWvkproc glfwGetInstanceProcAddress(VkInstance instance, const char* p * @errors Possible errors include @ref GLFW_NOT_INITIALIZED, @ref * GLFW_API_UNAVAILABLE and @ref GLFW_PLATFORM_ERROR. * + * @remark @macos This function currently always returns `GLFW_TRUE`, as the + * `VK_MVK_macos_surface` extension does not provide + * a `vkGetPhysicalDevice*PresentationSupport` type function. + * * @thread_safety This function may be called from any thread. For * synchronization details of Vulkan objects, see the Vulkan specification. * @@ -4354,6 +4365,12 @@ GLFWAPI int glfwGetPhysicalDevicePresentationSupport(VkInstance instance, VkPhys * @ref glfwVulkanSupported and @ref glfwGetRequiredInstanceExtensions should * eliminate almost all occurrences of these errors. * + * @remark @macos This function currently only supports the + * `VK_MVK_macos_surface` extension from MoltenVK. + * + * @remark @macos This function creates and sets a `CAMetalLayer` instance for + * the window content view, which is required for MoltenVK to function. + * * @thread_safety This function may be called from any thread. For * synchronization details of Vulkan objects, see the Vulkan specification. * diff --git a/src/cocoa_platform.h b/src/cocoa_platform.h index 6147e538b..fcacf661e 100644 --- a/src/cocoa_platform.h +++ b/src/cocoa_platform.h @@ -39,6 +39,18 @@ typedef void* id; #endif +typedef VkFlags VkMacOSSurfaceCreateFlagsMVK; + +typedef struct VkMacOSSurfaceCreateInfoMVK +{ + VkStructureType sType; + const void* pNext; + VkMacOSSurfaceCreateFlagsMVK flags; + const void* pView; +} VkMacOSSurfaceCreateInfoMVK; + +typedef VkResult (APIENTRY *PFN_vkCreateMacOSSurfaceMVK)(VkInstance,const VkMacOSSurfaceCreateInfoMVK*,const VkAllocationCallbacks*,VkSurfaceKHR*); + #include "posix_tls.h" #include "cocoa_joystick.h" #include "nsgl_context.h" @@ -74,6 +86,7 @@ typedef struct _GLFWwindowNS id object; id delegate; id view; + id layer; GLFWbool maximized; diff --git a/src/cocoa_window.m b/src/cocoa_window.m index 5db64cc8d..41c7dbd5c 100644 --- a/src/cocoa_window.m +++ b/src/cocoa_window.m @@ -431,6 +431,16 @@ static const NSRange kEmptyRange = { NSNotFound, 0 }; return YES; } +- (BOOL)wantsUpdateLayer +{ + return YES; +} + +- (id)makeBackingLayer +{ + return window->ns.layer; +} + - (void)cursorUpdate:(NSEvent *)event { updateCursorImage(window); @@ -1653,13 +1663,18 @@ const char* _glfwPlatformGetClipboardString(_GLFWwindow* window) void _glfwPlatformGetRequiredInstanceExtensions(char** extensions) { + if (!_glfw.vk.KHR_surface || !_glfw.vk.MVK_macos_surface) + return; + + extensions[0] = "VK_KHR_surface"; + extensions[1] = "VK_MVK_macos_surface"; } int _glfwPlatformGetPhysicalDevicePresentationSupport(VkInstance instance, VkPhysicalDevice device, uint32_t queuefamily) { - return GLFW_FALSE; + return GLFW_TRUE; } VkResult _glfwPlatformCreateWindowSurface(VkInstance instance, @@ -1667,7 +1682,57 @@ VkResult _glfwPlatformCreateWindowSurface(VkInstance instance, const VkAllocationCallbacks* allocator, VkSurfaceKHR* surface) { +#if MAC_OS_X_VERSION_MAX_ALLOWED >= 101100 + VkResult err; + VkMacOSSurfaceCreateInfoMVK sci; + PFN_vkCreateMacOSSurfaceMVK vkCreateMacOSSurfaceMVK; + + vkCreateMacOSSurfaceMVK = (PFN_vkCreateMacOSSurfaceMVK) + vkGetInstanceProcAddr(instance, "vkCreateMacOSSurfaceMVK"); + if (!vkCreateMacOSSurfaceMVK) + { + _glfwInputError(GLFW_API_UNAVAILABLE, + "Cocoa: Vulkan instance missing VK_MVK_macos_surface extension"); + return VK_ERROR_EXTENSION_NOT_PRESENT; + } + + // HACK: Dynamically load Core Animation to avoid adding an extra + // dependency for the majority who don't use MoltenVK + NSBundle* bundle = [NSBundle bundleWithPath:@"/System/Library/Frameworks/QuartzCore.framework"]; + if (!bundle) + { + _glfwInputError(GLFW_PLATFORM_ERROR, + "Cocoa: Failed to find QuartzCore.framework"); + return VK_ERROR_EXTENSION_NOT_PRESENT; + } + + // NOTE: Create the layer here as makeBackingLayer should not return nil + window->ns.layer = [[bundle classNamed:@"CAMetalLayer"] layer]; + if (!window->ns.layer) + { + _glfwInputError(GLFW_PLATFORM_ERROR, + "Cocoa: Failed to create layer for view"); + return VK_ERROR_EXTENSION_NOT_PRESENT; + } + + [window->ns.view setWantsLayer:YES]; + + memset(&sci, 0, sizeof(sci)); + sci.sType = VK_STRUCTURE_TYPE_MACOS_SURFACE_CREATE_INFO_MVK; + sci.pView = window->ns.view; + + err = vkCreateMacOSSurfaceMVK(instance, &sci, allocator, surface); + if (err) + { + _glfwInputError(GLFW_PLATFORM_ERROR, + "Cocoa: Failed to create Vulkan surface: %s", + _glfwGetVulkanResultString(err)); + } + + return err; +#else return VK_ERROR_EXTENSION_NOT_PRESENT; +#endif } diff --git a/src/internal.h b/src/internal.h index 77af1d1c6..3dc336911 100644 --- a/src/internal.h +++ b/src/internal.h @@ -110,6 +110,7 @@ typedef enum VkStructureType VK_STRUCTURE_TYPE_WAYLAND_SURFACE_CREATE_INFO_KHR = 1000006000, VK_STRUCTURE_TYPE_MIR_SURFACE_CREATE_INFO_KHR = 1000007000, VK_STRUCTURE_TYPE_WIN32_SURFACE_CREATE_INFO_KHR = 1000009000, + VK_STRUCTURE_TYPE_MACOS_SURFACE_CREATE_INFO_MVK = 1000053000, VK_STRUCTURE_TYPE_MAX_ENUM = 0x7FFFFFFF } VkStructureType; @@ -458,6 +459,8 @@ struct _GLFWlibrary GLFWbool KHR_surface; #if defined(_GLFW_WIN32) GLFWbool KHR_win32_surface; +#elif defined(_GLFW_COCOA) + GLFWbool MVK_macos_surface; #elif defined(_GLFW_X11) GLFWbool KHR_xlib_surface; GLFWbool KHR_xcb_surface; diff --git a/src/vulkan.c b/src/vulkan.c index b4e701bd0..c39711b69 100644 --- a/src/vulkan.c +++ b/src/vulkan.c @@ -45,17 +45,18 @@ GLFWbool _glfwInitVulkan(int mode) VkExtensionProperties* ep; uint32_t i, count; -#if !defined(_GLFW_VULKAN_STATIC) -#if defined(_GLFW_WIN32) - const char* name = "vulkan-1.dll"; -#else - const char* name = "libvulkan.so.1"; -#endif - if (_glfw.vk.available) return GLFW_TRUE; - _glfw.vk.handle = _glfw_dlopen(name); +#if !defined(_GLFW_VULKAN_STATIC) +#if defined(_GLFW_WIN32) + _glfw.vk.handle = _glfw_dlopen("vulkan-1.dll"); +#elif defined(_GLFW_COCOA) + // NULL maps to RTLD_DEFAULT, which searches all loaded binaries + _glfw.vk.handle = _glfw_dlopen(NULL); +#else + _glfw.vk.handle = _glfw_dlopen("libvulkan.so.1"); +#endif if (!_glfw.vk.handle) { if (mode == _GLFW_REQUIRE_LOADER) @@ -68,8 +69,16 @@ GLFWbool _glfwInitVulkan(int mode) _glfw_dlsym(_glfw.vk.handle, "vkGetInstanceProcAddr"); if (!_glfw.vk.GetInstanceProcAddr) { +#if defined(_GLFW_COCOA) + if (mode == _GLFW_REQUIRE_LOADER) + { + _glfwInputError(GLFW_API_UNAVAILABLE, + "Vulkan: vkGetInstanceProcAddr not found in process"); + } +#else _glfwInputError(GLFW_API_UNAVAILABLE, "Vulkan: Loader does not export vkGetInstanceProcAddr"); +#endif _glfwTerminateVulkan(); return GLFW_FALSE; @@ -119,6 +128,9 @@ GLFWbool _glfwInitVulkan(int mode) #if defined(_GLFW_WIN32) else if (strcmp(ep[i].extensionName, "VK_KHR_win32_surface") == 0) _glfw.vk.KHR_win32_surface = GLFW_TRUE; +#elif defined(_GLFW_COCOA) + else if (strcmp(ep[i].extensionName, "VK_MVK_macos_surface") == 0) + _glfw.vk.MVK_macos_surface = GLFW_TRUE; #elif defined(_GLFW_X11) else if (strcmp(ep[i].extensionName, "VK_KHR_xlib_surface") == 0) _glfw.vk.KHR_xlib_surface = GLFW_TRUE; diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 4a97b0382..2d88ec159 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -50,7 +50,7 @@ if (VULKAN_FOUND) add_executable(vulkan WIN32 vulkan.c ${ICON}) target_include_directories(vulkan PRIVATE "${VULKAN_INCLUDE_DIR}") if (NOT GLFW_VULKAN_STATIC) - target_link_libraries(vulkan "${VULKAN_LIBRARY}") + target_link_libraries(vulkan "${VULKAN_LIBRARY}" ${GLFW_VULKAN_DEPS}) endif() list(APPEND WINDOWS_BINARIES vulkan) endif() From a28baabefe7f0a50c2017f57347e9a543865f1a8 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Sun, 16 Oct 2016 13:35:23 +0100 Subject: [PATCH 27/40] Allow a monitor to be created without a name. --- src/monitor.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/monitor.c b/src/monitor.c index df32a3c48..dcb9796a2 100644 --- a/src/monitor.c +++ b/src/monitor.c @@ -175,10 +175,12 @@ void _glfwInputMonitorWindowChange(_GLFWmonitor* monitor, _GLFWwindow* window) _GLFWmonitor* _glfwAllocMonitor(const char* name, int widthMM, int heightMM) { _GLFWmonitor* monitor = calloc(1, sizeof(_GLFWmonitor)); - monitor->name = strdup(name); monitor->widthMM = widthMM; monitor->heightMM = heightMM; + if (name) + monitor->name = strdup(name); + return monitor; } From 8210f89b127a7c085ec38ba4342602da6b298244 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Tue, 11 Oct 2016 02:40:16 +0100 Subject: [PATCH 28/40] Wayland: Set a proper name for outputs. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit glfwGetMonitorName()’s documentation says “this function returns a human-readable name”, which “typically reflects the make and model of the monitor”. We get these two strings in the geometry event, so we only set the name at this point. --- src/wl_monitor.c | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/src/wl_monitor.c b/src/wl_monitor.c index 7a830513b..73567a5b0 100644 --- a/src/wl_monitor.c +++ b/src/wl_monitor.c @@ -50,11 +50,21 @@ static void geometry(void* data, int32_t transform) { struct _GLFWmonitor *monitor = data; + char* name; + size_t nameLength; monitor->wl.x = x; monitor->wl.y = y; monitor->widthMM = physicalWidth; monitor->heightMM = physicalHeight; + + nameLength = strlen(make) + 1 + strlen(model) + 1; + name = realloc(monitor->name, nameLength); + if (name) + { + sprintf(name, "%s %s", make, model); + monitor->name = name; + } } static void mode(void* data, @@ -118,10 +128,6 @@ void _glfwAddOutputWayland(uint32_t name, uint32_t version) { _GLFWmonitor *monitor; struct wl_output *output; - char nameStr[80]; - - memset(nameStr, 0, sizeof(nameStr)); - snprintf(nameStr, 79, "wl_output@%u", name); if (version < 2) { @@ -130,7 +136,8 @@ void _glfwAddOutputWayland(uint32_t name, uint32_t version) return; } - monitor = _glfwAllocMonitor(nameStr, 0, 0); + // The actual name of this output will be set in the geometry handler. + monitor = _glfwAllocMonitor(NULL, 0, 0); output = wl_registry_bind(_glfw.wl.registry, name, From 4d322a97e19a2b35ee6adda9bad2418c55777923 Mon Sep 17 00:00:00 2001 From: Camilla Berglund Date: Mon, 7 Nov 2016 19:43:15 +0100 Subject: [PATCH 29/40] Fix Vulkan extension count when none were found --- src/vulkan.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/vulkan.c b/src/vulkan.c index c39711b69..1123bb22c 100644 --- a/src/vulkan.c +++ b/src/vulkan.c @@ -237,6 +237,9 @@ GLFWAPI const char** glfwGetRequiredInstanceExtensions(uint32_t* count) if (!_glfwInitVulkan(_GLFW_REQUIRE_LOADER)) return NULL; + if (!_glfw.vk.extensions[0]) + return NULL; + *count = 2; return (const char**) _glfw.vk.extensions; } From 2aee11495022e3827959efd00967fd0a7fd4bf95 Mon Sep 17 00:00:00 2001 From: Camilla Berglund Date: Mon, 7 Nov 2016 19:44:56 +0100 Subject: [PATCH 30/40] Clarify glfwinfo output for no Vulkan extensions --- tests/glfwinfo.c | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/tests/glfwinfo.c b/tests/glfwinfo.c index 26d4b9455..157abfab8 100644 --- a/tests/glfwinfo.c +++ b/tests/glfwinfo.c @@ -820,9 +820,14 @@ int main(int argc, char** argv) re = glfwGetRequiredInstanceExtensions(&re_count); printf("Vulkan required instance extensions:"); - for (i = 0; i < re_count; i++) - printf(" %s", re[i]); - putchar('\n'); + if (re) + { + for (i = 0; i < re_count; i++) + printf(" %s", re[i]); + putchar('\n'); + } + else + printf(" missing\n"); if (list_extensions) list_vulkan_instance_extensions(); From e8c3e54dda488238f66b55a3e1552d732bff18a9 Mon Sep 17 00:00:00 2001 From: Camilla Berglund Date: Sun, 6 Nov 2016 16:05:16 +0100 Subject: [PATCH 31/40] Documentation work [ci skip] --- include/GLFW/glfw3native.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/GLFW/glfw3native.h b/include/GLFW/glfw3native.h index 056bc8292..cf0bc8ce3 100644 --- a/include/GLFW/glfw3native.h +++ b/include/GLFW/glfw3native.h @@ -50,7 +50,7 @@ extern "C" { * doing and how to fix problems caused by using them. If you don't, you * shouldn't be using them.** * - * Before the inclusion of @ref glfw3native.h, you may define exactly one + * Before the inclusion of @ref glfw3native.h, you may define zero or more * window system API macro and zero or more context creation API macros. * * The chosen backends must match those the library was compiled for. Failure From e83be1d73a16bf6f6f5eefa12c024e6aaa880a65 Mon Sep 17 00:00:00 2001 From: Camilla Berglund Date: Tue, 8 Nov 2016 12:19:06 +0100 Subject: [PATCH 32/40] Note that mode switching does not affect context --- include/GLFW/glfw3.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/include/GLFW/glfw3.h b/include/GLFW/glfw3.h index db2bd8a9a..547b89926 100644 --- a/include/GLFW/glfw3.h +++ b/include/GLFW/glfw3.h @@ -1778,8 +1778,8 @@ GLFWAPI void glfwWindowHint(int hint, int value); * or _borderless full screen_ windows, see @ref window_windowed_full_screen. * * Once you have created the window, you can switch it between windowed and - * full screen mode with @ref glfwSetWindowMonitor. If the window has an - * OpenGL or OpenGL ES context, it will be unaffected. + * full screen mode with @ref glfwSetWindowMonitor. This will not affect its + * OpenGL or OpenGL ES context. * * By default, newly created windows use the placement recommended by the * window system. To create the window at a specific position, make it From a90ee65f7b0264a672634fc4f5891a2ab57a47ae Mon Sep 17 00:00:00 2001 From: Camilla Berglund Date: Tue, 8 Nov 2016 12:39:18 +0100 Subject: [PATCH 33/40] Add definition of GLAPIENTRY --- README.md | 1 + include/GLFW/glfw3.h | 6 ++++++ 2 files changed, 7 insertions(+) diff --git a/README.md b/README.md index 68d257f93..56b03ded8 100644 --- a/README.md +++ b/README.md @@ -104,6 +104,7 @@ information on what to include when reporting a bug. scancodes for keys (#830) - Added `glfwSetWindowMaximizeCallback` and `GLFWwindowmaximizefun` for receiving window maximization events (#778) +- Added definition of `GLAPIENTRY` to public header - Bugfix: Calling `glfwMaximizeWindow` on a full screen window was not ignored - Bugfix: `GLFW_INCLUDE_VULKAN` could not be combined with the corresponding OpenGL and OpenGL ES header macros diff --git a/include/GLFW/glfw3.h b/include/GLFW/glfw3.h index 547b89926..385a04728 100644 --- a/include/GLFW/glfw3.h +++ b/include/GLFW/glfw3.h @@ -101,6 +101,12 @@ extern "C" { #endif #endif /* APIENTRY */ +/* Some OpenGL related headers use GLAPIENTRY instead. + */ +#ifndef GLAPIENTRY + #define GLAPIENTRY APIENTRY +#endif /* GLAPIENTRY */ + /* Some Windows OpenGL headers need this. */ #if !defined(WINGDIAPI) && defined(_WIN32) From 368fa9475def48da30eb6cbfc03d5439070ab48d Mon Sep 17 00:00:00 2001 From: Jason Daly Date: Tue, 30 Aug 2016 15:53:19 -0700 Subject: [PATCH 34/40] Add headless OSMesa backend Allows creation and drawing to in-memory OpenGL contexts. This backend does not provide input. Related to #850. --- CMake/modules/Findosmesa.cmake | 18 +++ CMakeLists.txt | 16 ++ include/GLFW/glfw3native.h | 24 +++ src/CMakeLists.txt | 6 + src/glfw_config.h.in | 2 + src/internal.h | 2 + src/osmesa_context.c | 213 +++++++++++++++++++++++++++ src/osmesa_context.h | 87 +++++++++++ src/osmesa_init.c | 35 +++++ src/osmesa_monitor.c | 43 ++++++ src/osmesa_platform.h | 61 ++++++++ src/osmesa_window.c | 259 +++++++++++++++++++++++++++++++++ 12 files changed, 766 insertions(+) create mode 100644 CMake/modules/Findosmesa.cmake create mode 100644 src/osmesa_context.c create mode 100644 src/osmesa_context.h create mode 100644 src/osmesa_init.c create mode 100644 src/osmesa_monitor.c create mode 100644 src/osmesa_platform.h create mode 100644 src/osmesa_window.c diff --git a/CMake/modules/Findosmesa.cmake b/CMake/modules/Findosmesa.cmake new file mode 100644 index 000000000..3194bd91a --- /dev/null +++ b/CMake/modules/Findosmesa.cmake @@ -0,0 +1,18 @@ +# Try to find OSMesa on a Unix system +# +# This will define: +# +# OSMESA_LIBRARIES - Link these to use OSMesa +# OSMESA_INCLUDE_DIR - Include directory for OSMesa +# +# Copyright (c) 2014 Brandon Schaefer + +if (NOT WIN32) + + find_package (PkgConfig) + pkg_check_modules (PKG_OSMESA QUIET osmesa) + + set (OSMESA_INCLUDE_DIR ${PKG_OSMESA_INCLUDE_DIRS}) + set (OSMESA_LIBRARIES ${PKG_OSMESA_LIBRARIES}) + +endif () diff --git a/CMakeLists.txt b/CMakeLists.txt index 388a555ff..8f623bcbf 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -41,6 +41,7 @@ endif() if (UNIX AND NOT APPLE) option(GLFW_USE_WAYLAND "Use Wayland for window creation" OFF) option(GLFW_USE_MIR "Use Mir for window creation" OFF) + option(GLFW_USE_OSMESA "Use OSMesa for offscreen context creation" OFF) endif() if (MSVC) @@ -154,6 +155,9 @@ elseif (UNIX) elseif (GLFW_USE_MIR) set(_GLFW_MIR 1) message(STATUS "Using Mir for window creation") + elseif (GLFW_USE_OSMESA) + set(_GLFW_OSMESA 1) + message(STATUS "Using OSMesa for windowless context creation") else() set(_GLFW_X11 1) message(STATUS "Using X11 for window creation") @@ -318,6 +322,18 @@ if (_GLFW_MIR) list(APPEND glfw_LIBRARIES "${XKBCOMMON_LIBRARY}") endif() +#-------------------------------------------------------------------- +# Use OSMesa for offscreen context creation +#-------------------------------------------------------------------- +if (_GLFW_OSMESA) + find_package(osmesa REQUIRED) + list(APPEND glfw_PKG_DEPS "osmesa") + + list(APPEND glfw_INCLUDE_DIRS "${OSMESA_INCLUDE_DIR}") + list(APPEND glfw_LIBRARIES "${OSMESA_LIBRARIES}" + "${CMAKE_THREAD_LIBS_INIT}") +endif() + #-------------------------------------------------------------------- # Use Cocoa for window creation and NSOpenGL for context creation #-------------------------------------------------------------------- diff --git a/include/GLFW/glfw3native.h b/include/GLFW/glfw3native.h index cf0bc8ce3..6c9b87ac2 100644 --- a/include/GLFW/glfw3native.h +++ b/include/GLFW/glfw3native.h @@ -448,6 +448,30 @@ GLFWAPI EGLContext glfwGetEGLContext(GLFWwindow* window); GLFWAPI EGLSurface glfwGetEGLSurface(GLFWwindow* window); #endif +#if defined(GLFW_EXPOSE_NATIVE_OSMESA) +/*! @brief Returns the color buffer associated with the specified window. + * + * @param[out] width The width of the color buffer. + * @param[out] height The height of the color buffer. + * @param[out] format The pixel format of the color buffer (OSMESA_FORMAT_*). + * @param[out] buffer The buffer data. + * @return 1 if successful, or 0 if not. + */ +GLFWAPI int glfwGetOSMesaColorBuffer(GLFWwindow* window, int* width, + int* height, int* format, void** buffer); + +/*! @brief Returns the depth buffer associated with the specified window. + * + * @param[out] width The width of the depth buffer. + * @param[out] height The height of the depth buffer. + * @param[out] bytesPerValue The number of bytes per depth buffer element. + * @param[out] buffer The buffer data. + * @return 1 if successful, or 0 if not. + */ +GLFWAPI int glfwGetOSMesaDepthBuffer(GLFWwindow* window, int* width, + int* height, int* bytesPerValue, void** buffer); +#endif + #ifdef __cplusplus } #endif diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 5042aba38..dd50d8d7a 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -45,6 +45,12 @@ elseif (_GLFW_MIR) set(glfw_SOURCES ${common_SOURCES} mir_init.c mir_monitor.c mir_window.c linux_joystick.c posix_time.c posix_tls.c xkb_unicode.c egl_context.c) +elseif (_GLFW_OSMESA) + set(glfw_HEADERS ${common_HEADERS} osmesa_platform.h + posix_time.h posix_tls.h osmesa_context.h) + set(glfw_SOURCES ${common_SOURCES} osmesa_init.c osmesa_monitor.c + osmesa_window.c posix_time.c posix_tls.c + osmesa_context.c) endif() if (APPLE) diff --git a/src/glfw_config.h.in b/src/glfw_config.h.in index f709726f0..153979f53 100644 --- a/src/glfw_config.h.in +++ b/src/glfw_config.h.in @@ -44,6 +44,8 @@ #cmakedefine _GLFW_WAYLAND // Define this to 1 if building GLFW for Mir #cmakedefine _GLFW_MIR +// Define this to 1 if building GLFW for OSMesa +#cmakedefine _GLFW_OSMESA // Define this to 1 if building as a shared library / dynamic library / DLL #cmakedefine _GLFW_BUILD_DLL diff --git a/src/internal.h b/src/internal.h index 3dc336911..f4ad122b5 100644 --- a/src/internal.h +++ b/src/internal.h @@ -172,6 +172,8 @@ typedef void (APIENTRY * PFN_vkVoidFunction)(void); #include "wl_platform.h" #elif defined(_GLFW_MIR) #include "mir_platform.h" +#elif defined(_GLFW_OSMESA) + #include "osmesa_platform.h" #else #error "No supported window creation API selected" #endif diff --git a/src/osmesa_context.c b/src/osmesa_context.c new file mode 100644 index 000000000..a04a7dc14 --- /dev/null +++ b/src/osmesa_context.c @@ -0,0 +1,213 @@ + +#include +#include + +#include "internal.h" +#include "osmesa_context.h" + +static void makeContextCurrentOSMesa(_GLFWwindow* window) +{ + if (window) + { + // Check to see if we need to allocate a new buffer. + if ((window->context.osmesa.buffer == NULL) || + (window->osmesa.width != window->context.osmesa.width) || + (window->osmesa.height != window->context.osmesa.height)) + { + // Free the current buffer, if necessary. + if (window->context.osmesa.buffer != NULL) + { + free(window->context.osmesa.buffer); + } + + // Allocate the new buffer (width * height * 1 byte per RGBA + // channel). + window->context.osmesa.buffer = (unsigned char *) malloc( + window->osmesa.width * window->osmesa.height * 4); + + // Update the context size. + window->context.osmesa.width = window->osmesa.width; + window->context.osmesa.height = window->osmesa.height; + } + + // Make the context current. + if (!OSMesaMakeCurrent(window->context.osmesa.handle, + window->context.osmesa.buffer, + 0x1401, // GL_UNSIGNED_BYTE + window->osmesa.width, window->osmesa.height)) + { + _glfwInputError(GLFW_PLATFORM_ERROR, + "OSMesa: Failed to make context current."); + return; + } + } + else + { + _glfwInputError(GLFW_PLATFORM_ERROR, + "OSMesa: Releasing the current context is not supported."); + } + + _glfwPlatformSetCurrentContext(window); +} + +static GLFWglproc getProcAddressOSMesa(const char* procname) +{ + _GLFWwindow* window = _glfwPlatformGetCurrentContext(); + + if (window->context.osmesa.handle) + { + return (GLFWglproc) OSMesaGetProcAddress(procname); + } + + return NULL; +} + +static void destroyContextOSMesa(_GLFWwindow* window) +{ + if (window->context.osmesa.handle != NULL) + { + OSMesaDestroyContext(window->context.osmesa.handle); + window->context.osmesa.handle = NULL; + } + + if (window->context.osmesa.buffer != NULL) + { + free(window->context.osmesa.buffer); + window->context.osmesa.width = 0; + window->context.osmesa.height = 0; + } +} + +static void swapBuffersOSMesa(_GLFWwindow* window) {} + +static void swapIntervalOSMesa(int interval) {} + +static int extensionSupportedOSMesa() +{ + return GLFW_FALSE; +} + +////////////////////////////////////////////////////////////////////////// +////// GLFW internal API ////// +////////////////////////////////////////////////////////////////////////// + +GLFWbool _glfwInitOSMesa(void) +{ + int i; + const char* sonames[] = + { +#if defined(_GLFW_WIN32) + "libOSMesa.dll", + "OSMesa.dll", +#elif defined(_GLFW_COCOA) + "libOSMesa.dylib", +#else + "libOSMesa.so.6", +#endif + NULL + }; + + if (_glfw.osmesa.handle) + return GLFW_TRUE; + + for (i = 0; sonames[i]; i++) + { + _glfw.osmesa.handle = _glfw_dlopen(sonames[i]); + if (_glfw.osmesa.handle) + break; + } + + if (!_glfw.osmesa.handle) + { + _glfwInputError(GLFW_API_UNAVAILABLE, "OSMesa: Library not found"); + return GLFW_FALSE; + } + + _glfw.osmesa.prefix = (strncmp(sonames[i], "lib", 3) == 0); + + _glfw.osmesa.CreateContext = (PFNOSMESACREATECONTEXTPROC) + _glfw_dlsym(_glfw.osmesa.handle, "OSMesaCreateContext"); + _glfw.osmesa.DestroyContext = (PFNOSMESADESTROYCONTEXTPROC) + _glfw_dlsym(_glfw.osmesa.handle, "OSMesaDestroyContext"); + _glfw.osmesa.MakeCurrent = (PFNOSMESAMAKECURRENTPROC) + _glfw_dlsym(_glfw.osmesa.handle, "OSMesaMakeCurrent"); + _glfw.osmesa.GetCurrentContext = (PFNOSMESAGETCURRENTCONTEXTPROC) + _glfw_dlsym(_glfw.osmesa.handle, "OSMesaGetCurrentContext"); + _glfw.osmesa.PixelStore = (PFNOSMESAPIXELSTOREPROC) + _glfw_dlsym(_glfw.osmesa.handle, "OSMesaPixelStore"); + _glfw.osmesa.GetIntegerv = (PFNOSMESAGETINTEGERVPROC) + _glfw_dlsym(_glfw.osmesa.handle, "OSMesaGetIntegerv"); + _glfw.osmesa.GetColorBuffer = (PFNOSMESAGETCOLORBUFFERPROC) + _glfw_dlsym(_glfw.osmesa.handle, "OSMesaGetColorBuffer"); + _glfw.osmesa.GetDepthBuffer = (PFNOSMESAGETDEPTHBUFFERPROC) + _glfw_dlsym(_glfw.osmesa.handle, "OSMesaGetDepthBuffer"); + _glfw.osmesa.GetProcAddress = (PFNOSMESAGETPROCADDRESSPROC) + _glfw_dlsym(_glfw.osmesa.handle, "OSMesaGetProcAddress"); + + if (!_glfw.osmesa.CreateContext || + !_glfw.osmesa.DestroyContext || + !_glfw.osmesa.MakeCurrent || + !_glfw.osmesa.GetCurrentContext || + !_glfw.osmesa.PixelStore || + !_glfw.osmesa.GetIntegerv || + !_glfw.osmesa.GetColorBuffer || + !_glfw.osmesa.GetDepthBuffer || + !_glfw.osmesa.GetProcAddress) + { + _glfwInputError(GLFW_PLATFORM_ERROR, + "OSMesa: Failed to load required entry points"); + + _glfwTerminateOSMesa(); + return GLFW_FALSE; + } + + return GLFW_TRUE; +} + +void _glfwTerminateOSMesa(void) +{ + if (_glfw.osmesa.handle) + { + _glfw_dlclose(_glfw.osmesa.handle); + _glfw.osmesa.handle = NULL; + } +} + +GLFWbool _glfwCreateContextOSMesa(_GLFWwindow* window, + const _GLFWctxconfig* ctxconfig, + const _GLFWfbconfig* fbconfig) +{ + OSMesaContext share; + + if (ctxconfig->share) + share = ctxconfig->share->context.osmesa.handle; + + // Initialize the context. + window->context.osmesa.buffer = NULL; + window->context.osmesa.width = 0; + window->context.osmesa.height = 0; + + // Create to create an OSMesa context. + window->context.osmesa.handle = OSMesaCreateContext(OSMESA_RGBA, share); + if (window->context.osmesa.handle == 0) + { + _glfwInputError(GLFW_VERSION_UNAVAILABLE, + "OSMesa: Failed to create context."); + return GLFW_FALSE; + } + + // Set up the context API. + window->context.makeCurrent = makeContextCurrentOSMesa; + window->context.swapBuffers = swapBuffersOSMesa; + window->context.swapInterval = swapIntervalOSMesa; + window->context.extensionSupported = extensionSupportedOSMesa; + window->context.getProcAddress = getProcAddressOSMesa; + window->context.destroy = destroyContextOSMesa; + + return GLFW_TRUE; +} + +////////////////////////////////////////////////////////////////////////// +////// GLFW native API ////// +////////////////////////////////////////////////////////////////////////// + diff --git a/src/osmesa_context.h b/src/osmesa_context.h new file mode 100644 index 000000000..2283cf741 --- /dev/null +++ b/src/osmesa_context.h @@ -0,0 +1,87 @@ + +#ifndef _glfw3_osmesa_context_h_ +#define _glfw3_osmesa_context_h_ + +#define OSMESA_COLOR_INDEX GL_COLOR_INDEX +#define OSMESA_RGBA 0x1908 +#define OSMESA_BGRA 0x1 +#define OSMESA_ARGB 0x2 +#define OSMESA_RGB 0x1907 +#define OSMESA_BGR 0x4 +#define OSMESA_RGB_565 0x5 + +#define OSMESA_ROW_LENGTH 0x10 +#define OSMESA_Y_UP 0x11 + +#define OSMESA_WIDTH 0x20 +#define OSMESA_HEIGHT 0x21 +#define OSMESA_FORMAT 0x22 +#define OSMESA_TYPE 0x23 +#define OSMESA_MAX_WIDTH 0x24 +#define OSMESA_MAX_HEIGHT 0x25 + +typedef void* OSMesaContext; +typedef void (*OSMESAproc)(); + +typedef OSMesaContext (* PFNOSMESACREATECONTEXTPROC)(GLenum, OSMesaContext); +typedef void (* PFNOSMESADESTROYCONTEXTPROC)(OSMesaContext); +typedef int (* PFNOSMESAMAKECURRENTPROC)(OSMesaContext, void*, int, int, int); +typedef OSMesaContext (* PFNOSMESAGETCURRENTCONTEXTPROC)(); +typedef void (* PFNOSMESAPIXELSTOREPROC)(int, int); +typedef void (* PFNOSMESAGETINTEGERVPROC)(int, int*); +typedef int (* PFNOSMESAGETCOLORBUFFERPROC)(OSMesaContext, int*, int*, int*, void**); +typedef int (* PFNOSMESAGETDEPTHBUFFERPROC)(OSMesaContext, int*, int*, int*, void**); +typedef GLFWglproc (* PFNOSMESAGETPROCADDRESSPROC)(const char*); +#define OSMesaCreateContext _glfw.osmesa.CreateContext +#define OSMesaDestroyContext _glfw.osmesa.DestroyContext +#define OSMesaMakeCurrent _glfw.osmesa.MakeCurrent +#define OSMesaGetCurrentContext _glfw.osmesa.GetCurrentContext +#define OSMesaPixelStore _glfw.osmesa.PixelStore +#define OSMesaGetIntegerv _glfw.osmesa.GetIntegerv +#define OSMesaGetColorBuffer _glfw.osmesa.GetColorBuffer +#define OSMesaGetDepthBuffer _glfw.osmesa.GetDepthBuffer +#define OSMesaGetProcAddress _glfw.osmesa.GetProcAddress + +#define _GLFW_PLATFORM_CONTEXT_STATE _GLFWcontextOSMesa osmesa +#define _GLFW_PLATFORM_LIBRARY_CONTEXT_STATE _GLFWctxlibraryOSMesa osmesa + + +// OSMesa-specific per-context data +// +typedef struct _GLFWcontextOSMesa +{ + OSMesaContext handle; + int width; + int height; + void * buffer; + +} _GLFWcontextOSMesa; + +// OSMesa-specific global data +// +typedef struct _GLFWctxlibraryOSMesa +{ + GLFWbool prefix; + + void* handle; + + PFNOSMESACREATECONTEXTPROC CreateContext; + PFNOSMESADESTROYCONTEXTPROC DestroyContext; + PFNOSMESAMAKECURRENTPROC MakeCurrent; + PFNOSMESAGETCURRENTCONTEXTPROC GetCurrentContext; + PFNOSMESAPIXELSTOREPROC PixelStore; + PFNOSMESAGETINTEGERVPROC GetIntegerv; + PFNOSMESAGETCOLORBUFFERPROC GetColorBuffer; + PFNOSMESAGETDEPTHBUFFERPROC GetDepthBuffer; + PFNOSMESAGETPROCADDRESSPROC GetProcAddress; + +} _GLFWctxlibraryOSMesa; + + +GLFWbool _glfwInitOSMesa(void); +void _glfwTerminateOSMesa(void); +GLFWbool _glfwCreateContextOSMesa(_GLFWwindow* window, + const _GLFWctxconfig* ctxconfig, + const _GLFWfbconfig* fbconfig); + +#endif // _glfw3_osmesa_context_h_ diff --git a/src/osmesa_init.c b/src/osmesa_init.c new file mode 100644 index 000000000..46f2458d4 --- /dev/null +++ b/src/osmesa_init.c @@ -0,0 +1,35 @@ + +#include "internal.h" + +////////////////////////////////////////////////////////////////////////// +////// GLFW internal API ////// +////////////////////////////////////////////////////////////////////////// + + +////////////////////////////////////////////////////////////////////////// +////// GLFW platform API ////// +////////////////////////////////////////////////////////////////////////// + +int _glfwPlatformInit(void) +{ + if (!_glfwInitThreadLocalStoragePOSIX()) { + return GLFW_FALSE; + } + + _glfwInitTimerPOSIX(); + + return GLFW_TRUE; +} + +void _glfwPlatformTerminate(void) +{ + _glfwTerminateOSMesa(); + _glfwTerminateThreadLocalStoragePOSIX(); +} + +const char* _glfwPlatformGetVersionString(void) +{ + const char* version = _GLFW_VERSION_NUMBER " OSMESA"; + return version; +} + diff --git a/src/osmesa_monitor.c b/src/osmesa_monitor.c new file mode 100644 index 000000000..fb9ede251 --- /dev/null +++ b/src/osmesa_monitor.c @@ -0,0 +1,43 @@ + +#include "internal.h" + +////////////////////////////////////////////////////////////////////////// +////// GLFW internal API ////// +////////////////////////////////////////////////////////////////////////// + +////////////////////////////////////////////////////////////////////////// +////// GLFW platform API ////// +////////////////////////////////////////////////////////////////////////// + +_GLFWmonitor** _glfwPlatformGetMonitors(int* count) +{ + // OSMesa is headless, so no monitors. + _GLFWmonitor** monitors = NULL; + if (count != NULL) { + *count = 0; + } + return monitors; +} + +int _glfwPlatformIsSameMonitor(_GLFWmonitor* first, _GLFWmonitor* second) +{ + return GLFW_FALSE; +} + +void _glfwPlatformGetMonitorPos(_GLFWmonitor* monitor, int* xpos, int* ypos) {} + +GLFWvidmode* _glfwPlatformGetVideoModes(_GLFWmonitor* monitor, int* found) +{ + return NULL; +} + +void _glfwPlatformGetVideoMode(_GLFWmonitor* monitor, GLFWvidmode* mode) {} + +void _glfwPlatformGetGammaRamp(_GLFWmonitor* monitor, GLFWgammaramp* ramp) {} + +void _glfwPlatformSetGammaRamp(_GLFWmonitor* monitor, + const GLFWgammaramp* ramp) {} + +////////////////////////////////////////////////////////////////////////// +////// GLFW native API ////// +////////////////////////////////////////////////////////////////////////// diff --git a/src/osmesa_platform.h b/src/osmesa_platform.h new file mode 100644 index 000000000..f5e114e3f --- /dev/null +++ b/src/osmesa_platform.h @@ -0,0 +1,61 @@ + +#ifndef _osmesa_platform_h_ +#define _osmesa_platform_h_ + +#include + +#define _GLFW_PLATFORM_WINDOW_STATE _GLFWwindowOSMesa osmesa +#define _GLFW_PLATFORM_MONITOR_STATE _GLFWmonitorOSMesa osmesa +#define _GLFW_PLATFORM_CURSOR_STATE _GLFWcursorOSMesa osmesa + +#define _GLFW_PLATFORM_LIBRARY_WINDOW_STATE _GLFWwinlibraryOSMesa osmesawin + +#define _GLFW_PLATFORM_LIBRARY_JOYSTICK_STATE +#define _GLFW_EGL_CONTEXT_STATE +#define _GLFW_EGL_LIBRARY_CONTEXT_STATE + +#include "osmesa_context.h" +#include "posix_time.h" +#include "posix_tls.h" + +#define _glfw_dlopen(name) dlopen(name, RTLD_LAZY | RTLD_LOCAL) +#define _glfw_dlclose(handle) dlclose(handle) +#define _glfw_dlsym(handle, name) dlsym(handle, name) + +// TODO(dalyj) "PLACEHOLDER" variables are there to silence "empty struct" +// warnings + +// OSMesa-specific per-window data +// +typedef struct _GLFWwindowOSMesa +{ + int width; + int height; +} _GLFWwindowOSMesa; + + +// OSMesa-specific global data +// +typedef struct _GLFWwinlibraryOSMesa +{ + int PLACEHOLDER; +} _GLFWwinlibraryOSMesa; + + +// OSMesa-specific per-monitor data +// +typedef struct _GLFWmonitorOSMesa +{ + int PLACEHOLDER; +} _GLFWmonitorOSMesa; + + +// OSMesa-specific per-cursor data +// +typedef struct _GLFWcursorOSMesa +{ + int PLACEHOLDER; +} _GLFWcursorOSMesa; + + +#endif // _osmesa_platform_h_ diff --git a/src/osmesa_window.c b/src/osmesa_window.c new file mode 100644 index 000000000..c171d6290 --- /dev/null +++ b/src/osmesa_window.c @@ -0,0 +1,259 @@ + +#include "internal.h" + +#include + +int createWindow(_GLFWwindow* window, const _GLFWwndconfig* wndconfig) +{ + window->osmesa.width = wndconfig->width; + window->osmesa.height = wndconfig->height; + + return GLFW_TRUE; +} + +////////////////////////////////////////////////////////////////////////// +////// GLFW internal API ////// +////////////////////////////////////////////////////////////////////////// + +////////////////////////////////////////////////////////////////////////// +////// GLFW platform API ////// +////////////////////////////////////////////////////////////////////////// + +int _glfwPlatformCreateWindow(_GLFWwindow* window, + const _GLFWwndconfig* wndconfig, + const _GLFWctxconfig* ctxconfig, + const _GLFWfbconfig* fbconfig) +{ + if (!_glfwInitOSMesa()) + return GLFW_FALSE; + + if (!_glfwCreateContextOSMesa(window, ctxconfig, fbconfig)) + return GLFW_FALSE; + + if (!createWindow(window, wndconfig)) + return GLFW_FALSE; + + return GLFW_TRUE; +} + +void _glfwPlatformDestroyWindow(_GLFWwindow* window) +{ + if (window->context.destroy) + window->context.destroy(window); +} + +void _glfwPlatformSetWindowTitle(_GLFWwindow* window, const char* title) {} + +void _glfwPlatformSetWindowIcon(_GLFWwindow* window, int count, + const GLFWimage* images) {} + +void _glfwPlatformSetWindowMonitor(_GLFWwindow* window, + _GLFWmonitor* monitor, + int xpos, int ypos, + int width, int height, + int refreshRate) {} + +void _glfwPlatformGetWindowPos(_GLFWwindow* window, int* xpos, int* ypos) +{ + if (xpos != NULL) *xpos = 0; + if (ypos != NULL) *ypos = 0; +} + +void _glfwPlatformSetWindowPos(_GLFWwindow* window, int xpos, int ypos) {} + +void _glfwPlatformGetWindowSize(_GLFWwindow* window, int* width, int* height) +{ + if (width != NULL) *width = window->osmesa.width; + if (height != NULL) *height = window->osmesa.height; +} + +void _glfwPlatformSetWindowSize(_GLFWwindow* window, int width, int height) +{ + window->osmesa.width = width; + window->osmesa.height = height; +} + +void _glfwPlatformSetWindowSizeLimits(_GLFWwindow* window, + int minwidth, int minheight, + int maxwidth, int maxheight) {} + +void _glfwPlatformSetWindowAspectRatio(_GLFWwindow* window, int n, int d) {} + +void _glfwPlatformGetFramebufferSize(_GLFWwindow* window, int* width, + int* height) +{ + if (width != NULL) *width = window->osmesa.width; + if (height != NULL) *height = window->osmesa.height; +} + +void _glfwPlatformGetWindowFrameSize(_GLFWwindow* window, int* left, int* top, + int* right, int* bottom) +{ + if (left != NULL) *left = 0; + if (top != NULL) *top = 0; + if (right != NULL) *right = window->osmesa.width; + if (bottom != NULL) *top = window->osmesa.height; +} + +void _glfwPlatformIconifyWindow(_GLFWwindow* window) {} + +void _glfwPlatformRestoreWindow(_GLFWwindow* window) {} + +void _glfwPlatformMaximizeWindow(_GLFWwindow* window) {} + +int _glfwPlatformWindowMaximized(_GLFWwindow* window) { + return 0; +} + +void _glfwPlatformShowWindow(_GLFWwindow* window) {} + +void _glfwPlatformUnhideWindow(_GLFWwindow* window) {} + +void _glfwPlatformHideWindow(_GLFWwindow* window) {} + +void _glfwPlatformFocusWindow(_GLFWwindow* window) {} + +int _glfwPlatformWindowFocused(_GLFWwindow* window) { return GLFW_FALSE; } + +int _glfwPlatformWindowIconified(_GLFWwindow* window) { return GLFW_FALSE; } + +int _glfwPlatformWindowVisible(_GLFWwindow* window) { return GLFW_FALSE; } + +void _glfwPlatformPollEvents(void) {} + +void _glfwPlatformWaitEvents(void) {} + +void _glfwPlatformWaitEventsTimeout(double timeout) {} + +void _glfwPlatformPostEmptyEvent(void) {} + +void _glfwPlatformGetCursorPos(_GLFWwindow* window, double* xpos, double* ypos) { + if (xpos != NULL) *xpos = 0; + if (ypos != NULL) *ypos = 0; +} + +void _glfwPlatformSetCursorPos(_GLFWwindow* window, double x, double y) {} + +void _glfwPlatformSetCursorMode(_GLFWwindow* window, int mode) {} + +void _glfwPlatformApplyCursorMode(_GLFWwindow* window) {} + +int _glfwPlatformCreateCursor(_GLFWcursor* cursor, const GLFWimage* image, + int xhot, int yhot) +{ + return GLFW_FALSE; +} + +int _glfwPlatformCreateStandardCursor(_GLFWcursor* cursor, int shape) +{ + return GLFW_FALSE; +} + +void _glfwPlatformDestroyCursor(_GLFWcursor* cursor) {} + +void _glfwPlatformSetCursor(_GLFWwindow* window, _GLFWcursor* cursor) {} + +void _glfwPlatformSetClipboardString(_GLFWwindow* window, const char* string) {} + +const char* _glfwPlatformGetClipboardString(_GLFWwindow* window) +{ + return NULL; +} + +const char* _glfwPlatformGetKeyName(int key, int scancode) { return ""; } + +int _glfwPlatformJoystickPresent(int joy) { return 0; } + +const float* _glfwPlatformGetJoystickAxes(int joy, int* count) +{ + if (count != NULL) *count = 0; + return NULL; +} + +const unsigned char* _glfwPlatformGetJoystickButtons(int joy, int* count) +{ + if (count != NULL) *count = 0; + return NULL; +} + +const char* _glfwPlatformGetJoystickName(int joy) { return NULL; } + +char** _glfwPlatformGetRequiredInstanceExtensions(uint32_t* count) +{ + if (count != NULL) *count = 0; + return NULL; +} + +int _glfwPlatformGetPhysicalDevicePresentationSupport(VkInstance instance, + VkPhysicalDevice device, + uint32_t queuefamily) +{ + return GLFW_FALSE; +} + +VkResult _glfwPlatformCreateWindowSurface(VkInstance instance, + _GLFWwindow* window, + const VkAllocationCallbacks* allocator, + VkSurfaceKHR* surface) +{ + // This seems like the most appropriate error to return here. + return VK_ERROR_INITIALIZATION_FAILED; +} + +////////////////////////////////////////////////////////////////////////// +////// GLFW native API ////// +////////////////////////////////////////////////////////////////////////// + +GLFWAPI int glfwGetOSMesaColorBuffer(GLFWwindow* window, int* width, + int* height, int* format, void** buffer) +{ + GLint mesaWidth; + GLint mesaHeight; + GLint mesaFormat; + void* mesaBuffer; + + assert(window != NULL); + + OSMesaContext ctx = ((_GLFWwindow*) window)->context.osmesa.handle; + + // Query OSMesa for the color buffer data. + int result = OSMesaGetColorBuffer( + ctx, &mesaWidth, &mesaHeight, &mesaFormat, &mesaBuffer); + if (result) { + // Copy the values returned by OSMesa. + if (width != NULL) *width = mesaWidth; + if (height != NULL) *height = mesaHeight; + if (format != NULL) *format = mesaFormat; + if (buffer != NULL) *buffer = mesaBuffer; + } + + return result; +} + +GLFWAPI int glfwGetOSMesaDepthBuffer(GLFWwindow* window, int* width, + int* height, int* bytesPerValue, + void** buffer) +{ + GLint mesaWidth; + GLint mesaHeight; + GLint mesaBytes; + void* mesaBuffer; + + assert(window != NULL); + + OSMesaContext ctx = ((_GLFWwindow*) window)->context.osmesa.handle; + + // Query OSMesa for the color buffer data. + int result = OSMesaGetDepthBuffer( + ctx, &mesaWidth, &mesaHeight, &mesaBytes, &mesaBuffer); + if (result) { + // Copy the values returned by OSMesa. + if (width != NULL) *width = mesaWidth; + if (height != NULL) *height = mesaHeight; + if (bytesPerValue != NULL) *bytesPerValue = mesaBytes; + if (buffer != NULL) *buffer = mesaBuffer; + } + + return result; +} + From fef21361c595c7eba2050775b5e1c4c72942d735 Mon Sep 17 00:00:00 2001 From: Camilla Berglund Date: Thu, 13 Oct 2016 17:24:51 +0200 Subject: [PATCH 35/40] OSMesa: Cleanup Fixes formatting, semantics and documentation. Adds glfwGetOSMesaContext. Adds support for OSMesa context attributes. Updates changelog and credits. Adds license and copyright headers. Removes superfluous code (the shared code provides many conveniences). Removes loading of unused OSMesa functions. Removes empty platform structs. Fixes version string format. Removes build dependency on the OSMesa header and library (only the library is needed and only at runtime). Closes #850. --- .../{Findosmesa.cmake => FindOSMesa.cmake} | 0 CMakeLists.txt | 39 +-- README.md | 2 + docs/Doxyfile.in | 1 + docs/compile.dox | 12 + docs/news.dox | 6 + include/GLFW/glfw3native.h | 68 +++- src/internal.h | 1 + src/osmesa_context.c | 238 ++++++++++---- src/osmesa_context.h | 96 +++--- src/osmesa_init.c | 47 ++- src/osmesa_monitor.c | 60 +++- src/osmesa_platform.h | 84 ++--- src/osmesa_window.c | 307 ++++++++++-------- 14 files changed, 611 insertions(+), 350 deletions(-) rename CMake/modules/{Findosmesa.cmake => FindOSMesa.cmake} (100%) diff --git a/CMake/modules/Findosmesa.cmake b/CMake/modules/FindOSMesa.cmake similarity index 100% rename from CMake/modules/Findosmesa.cmake rename to CMake/modules/FindOSMesa.cmake diff --git a/CMakeLists.txt b/CMakeLists.txt index 8f623bcbf..2025e4391 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -28,6 +28,10 @@ option(GLFW_INSTALL "Generate installation target" ON) option(GLFW_VULKAN_STATIC "Use the Vulkan loader statically linked into application" OFF) option(GLFW_DOCUMENT_INTERNALS "Include internals in documentation" OFF) +if (UNIX) + option(GLFW_USE_OSMESA "Use OSMesa for offscreen context creation" OFF) +endif() + if (WIN32) option(GLFW_USE_HYBRID_HPG "Force use of high-performance GPU on hybrid systems" OFF) endif() @@ -41,7 +45,6 @@ endif() if (UNIX AND NOT APPLE) option(GLFW_USE_WAYLAND "Use Wayland for window creation" OFF) option(GLFW_USE_MIR "Use Mir for window creation" OFF) - option(GLFW_USE_OSMESA "Use OSMesa for offscreen context creation" OFF) endif() if (MSVC) @@ -142,26 +145,24 @@ endif() #-------------------------------------------------------------------- # Detect and select backend APIs #-------------------------------------------------------------------- -if (WIN32) +if (GLFW_USE_WAYLAND) + set(_GLFW_WAYLAND 1) + message(STATUS "Using Wayland for window creation") +elseif (GLFW_USE_MIR) + set(_GLFW_MIR 1) + message(STATUS "Using Mir for window creation") +elseif (GLFW_USE_OSMESA) + set(_GLFW_OSMESA 1) + message(STATUS "Using OSMesa for headless context creation") +elseif (WIN32) set(_GLFW_WIN32 1) message(STATUS "Using Win32 for window creation") elseif (APPLE) set(_GLFW_COCOA 1) message(STATUS "Using Cocoa for window creation") elseif (UNIX) - if (GLFW_USE_WAYLAND) - set(_GLFW_WAYLAND 1) - message(STATUS "Using Wayland for window creation") - elseif (GLFW_USE_MIR) - set(_GLFW_MIR 1) - message(STATUS "Using Mir for window creation") - elseif (GLFW_USE_OSMESA) - set(_GLFW_OSMESA 1) - message(STATUS "Using OSMesa for windowless context creation") - else() - set(_GLFW_X11 1) - message(STATUS "Using X11 for window creation") - endif() + set(_GLFW_X11 1) + message(STATUS "Using X11 for window creation") else() message(FATAL_ERROR "No supported platform was detected") endif() @@ -326,12 +327,8 @@ endif() # Use OSMesa for offscreen context creation #-------------------------------------------------------------------- if (_GLFW_OSMESA) - find_package(osmesa REQUIRED) - list(APPEND glfw_PKG_DEPS "osmesa") - - list(APPEND glfw_INCLUDE_DIRS "${OSMESA_INCLUDE_DIR}") - list(APPEND glfw_LIBRARIES "${OSMESA_LIBRARIES}" - "${CMAKE_THREAD_LIBS_INIT}") + find_package(OSMesa REQUIRED) + list(APPEND glfw_LIBRARIES "${CMAKE_THREAD_LIBS_INIT}") endif() #-------------------------------------------------------------------- diff --git a/README.md b/README.md index 56b03ded8..a0dc71913 100644 --- a/README.md +++ b/README.md @@ -104,6 +104,7 @@ information on what to include when reporting a bug. scancodes for keys (#830) - Added `glfwSetWindowMaximizeCallback` and `GLFWwindowmaximizefun` for receiving window maximization events (#778) +- Added headless [OSMesa](http://mesa3d.org/osmesa.html) backend (#281,#850) - Added definition of `GLAPIENTRY` to public header - Bugfix: Calling `glfwMaximizeWindow` on a full screen window was not ignored - Bugfix: `GLFW_INCLUDE_VULKAN` could not be combined with the corresponding @@ -155,6 +156,7 @@ skills. - Lambert Clara - Andrew Corrigan - Noel Cower + - Jason Daly - Jarrod Davis - Olivier Delannoy - Paul R. Deppe diff --git a/docs/Doxyfile.in b/docs/Doxyfile.in index 6dd8dbe44..eb11421ad 100644 --- a/docs/Doxyfile.in +++ b/docs/Doxyfile.in @@ -1577,6 +1577,7 @@ PREDEFINED = GLFWAPI= \ GLFW_EXPOSE_NATIVE_COCOA \ GLFW_EXPOSE_NATIVE_NSGL \ GLFW_EXPOSE_NATIVE_EGL \ + GLFW_EXPOSE_NATIVE_OSMESA \ VK_VERSION_1_0 # If the MACRO_EXPANSION and EXPAND_ONLY_PREDEF tags are set to YES then diff --git a/docs/compile.dox b/docs/compile.dox index 002b26bf7..2b21732a0 100644 --- a/docs/compile.dox +++ b/docs/compile.dox @@ -97,6 +97,17 @@ Once you have installed the necessary packages, move on to @ref compile_generate. +@subsection compile_deps_osmesa Dependencies for Linux and OSMesa + +To compile GLFW for OSMesa, you need to install the OSMesa library and header +packages. For example, on Ubuntu and other distributions based on Debian +GNU/Linux, you need to install the `libosmesa6-dev` package. The OSMesa library +is required at runtime for context creation and is loaded on demand. + +Once you have installed the necessary packages, move on to @ref +compile_generate. + + @subsection compile_generate Generating build files with CMake Once you have all necessary dependencies it is time to generate the project @@ -249,6 +260,7 @@ ramps and clipboard. The options are: - `_GLFW_X11` to use the X Window System - `_GLFW_WAYLAND` to use the Wayland API (experimental and incomplete) - `_GLFW_MIR` to use the Mir API (experimental and incomplete) + - `_GLFW_OSMESA` to use the OSMesa API (headless and non-interactive) If you are building GLFW as a shared library / dynamic library / DLL then you must also define `_GLFW_BUILD_DLL`. Otherwise, you must not define it. diff --git a/docs/news.dox b/docs/news.dox index 707bd55ab..b0de0daa1 100644 --- a/docs/news.dox +++ b/docs/news.dox @@ -22,6 +22,12 @@ GLFW now supports the `VK_MVK_macos_surface` window surface creation extension provided by MoltenVK. +@subsection news_33_osmesa OSMesa backend for headless software rendering + +GLFW now supports headless context creation and software rendering via OSMesa, +intended for automated testing. This backend does not provide input. + + @section news_32 New features in 3.2 diff --git a/include/GLFW/glfw3native.h b/include/GLFW/glfw3native.h index 6c9b87ac2..42b50d95d 100644 --- a/include/GLFW/glfw3native.h +++ b/include/GLFW/glfw3native.h @@ -68,6 +68,7 @@ extern "C" { * * `GLFW_EXPOSE_NATIVE_NSGL` * * `GLFW_EXPOSE_NATIVE_GLX` * * `GLFW_EXPOSE_NATIVE_EGL` + * * `GLFW_EXPOSE_NATIVE_OSMESA` * * These macros select which of the native access functions that are declared * and which platform-specific headers to include. It is then up your (by @@ -114,6 +115,9 @@ extern "C" { #if defined(GLFW_EXPOSE_NATIVE_EGL) #include #endif +#if defined(GLFW_EXPOSE_NATIVE_OSMESA) + #include +#endif /************************************************************************* @@ -449,27 +453,59 @@ GLFWAPI EGLSurface glfwGetEGLSurface(GLFWwindow* window); #endif #if defined(GLFW_EXPOSE_NATIVE_OSMESA) -/*! @brief Returns the color buffer associated with the specified window. +/*! @brief Retrieves the color buffer associated with the specified window. * - * @param[out] width The width of the color buffer. - * @param[out] height The height of the color buffer. - * @param[out] format The pixel format of the color buffer (OSMESA_FORMAT_*). - * @param[out] buffer The buffer data. - * @return 1 if successful, or 0 if not. + * @param[out] width Where to store the width of the color buffer, or `NULL`. + * @param[out] height Where to store the height of the color buffer, or `NULL`. + * @param[out] format Where to store the OSMesa pixel format of the color + * buffer, or `NULL`. + * @param[out] buffer Where to store the address of the color buffer, or + * `NULL`. + * @return `GLFW_TRUE` if successful, or `GLFW_FALSE` if an + * [error](@ref error_handling) occurred. + * + * @thread_safety This function may be called from any thread. Access is not + * synchronized. + * + * @since Added in version 3.3. + * + * @ingroup native */ -GLFWAPI int glfwGetOSMesaColorBuffer(GLFWwindow* window, int* width, - int* height, int* format, void** buffer); +GLFWAPI int glfwGetOSMesaColorBuffer(GLFWwindow* window, int* width, int* height, int* format, void** buffer); -/*! @brief Returns the depth buffer associated with the specified window. +/*! @brief Retrieves the depth buffer associated with the specified window. * - * @param[out] width The width of the depth buffer. - * @param[out] height The height of the depth buffer. - * @param[out] bytesPerValue The number of bytes per depth buffer element. - * @param[out] buffer The buffer data. - * @return 1 if successful, or 0 if not. + * @param[out] width Where to store the width of the depth buffer, or `NULL`. + * @param[out] height Where to store the height of the depth buffer, or `NULL`. + * @param[out] bytesPerValue Where to store the number of bytes per depth + * buffer element, or `NULL`. + * @param[out] buffer Where to store the address of the depth buffer, or + * `NULL`. + * @return `GLFW_TRUE` if successful, or `GLFW_FALSE` if an + * [error](@ref error_handling) occurred. + * + * @thread_safety This function may be called from any thread. Access is not + * synchronized. + * + * @since Added in version 3.3. + * + * @ingroup native */ -GLFWAPI int glfwGetOSMesaDepthBuffer(GLFWwindow* window, int* width, - int* height, int* bytesPerValue, void** buffer); +GLFWAPI int glfwGetOSMesaDepthBuffer(GLFWwindow* window, int* width, int* height, int* bytesPerValue, void** buffer); + +/*! @brief Returns the `OSMesaContext` of the specified window. + * + * @return The `OSMesaContext` of the specified window, or `NULL` if an + * [error](@ref error_handling) occurred. + * + * @thread_safety This function may be called from any thread. Access is not + * synchronized. + * + * @since Added in version 3.3. + * + * @ingroup native + */ +GLFWAPI OSMesaContext glfwGetOSMesaContext(GLFWwindow* window); #endif #ifdef __cplusplus diff --git a/src/internal.h b/src/internal.h index f4ad122b5..7aea807b5 100644 --- a/src/internal.h +++ b/src/internal.h @@ -69,6 +69,7 @@ typedef void (* _GLFWdestroycontextfun)(_GLFWwindow*); #define GL_VERSION 0x1f02 #define GL_NONE 0 #define GL_COLOR_BUFFER_BIT 0x00004000 +#define GL_UNSIGNED_BYTE 0x1401 #define GL_EXTENSIONS 0x1f03 #define GL_NUM_EXTENSIONS 0x821d #define GL_CONTEXT_FLAGS 0x821e diff --git a/src/osmesa_context.c b/src/osmesa_context.c index a04a7dc14..65fb6e927 100644 --- a/src/osmesa_context.c +++ b/src/osmesa_context.c @@ -1,76 +1,84 @@ +//======================================================================== +// GLFW 3.3 OSMesa - www.glfw.org +//------------------------------------------------------------------------ +// Copyright (c) 2016 Google Inc. +// Copyright (c) 2006-2016 Camilla Berglund +// +// This software is provided 'as-is', without any express or implied +// warranty. In no event will the authors be held liable for any damages +// arising from the use of this software. +// +// Permission is granted to anyone to use this software for any purpose, +// including commercial applications, and to alter it and redistribute it +// freely, subject to the following restrictions: +// +// 1. The origin of this software must not be misrepresented; you must not +// claim that you wrote the original software. If you use this software +// in a product, an acknowledgment in the product documentation would +// be appreciated but is not required. +// +// 2. Altered source versions must be plainly marked as such, and must not +// be misrepresented as being the original software. +// +// 3. This notice may not be removed or altered from any source +// distribution. +// +//======================================================================== #include #include +#include #include "internal.h" -#include "osmesa_context.h" + static void makeContextCurrentOSMesa(_GLFWwindow* window) { if (window) { - // Check to see if we need to allocate a new buffer. + // Check to see if we need to allocate a new buffer if ((window->context.osmesa.buffer == NULL) || (window->osmesa.width != window->context.osmesa.width) || (window->osmesa.height != window->context.osmesa.height)) { - // Free the current buffer, if necessary. - if (window->context.osmesa.buffer != NULL) - { - free(window->context.osmesa.buffer); - } + free(window->context.osmesa.buffer); - // Allocate the new buffer (width * height * 1 byte per RGBA - // channel). - window->context.osmesa.buffer = (unsigned char *) malloc( - window->osmesa.width * window->osmesa.height * 4); + // Allocate the new buffer (width * height * 8-bit RGBA) + window->context.osmesa.buffer = + calloc(4, window->osmesa.width * window->osmesa.height); - // Update the context size. window->context.osmesa.width = window->osmesa.width; window->context.osmesa.height = window->osmesa.height; } - // Make the context current. if (!OSMesaMakeCurrent(window->context.osmesa.handle, window->context.osmesa.buffer, - 0x1401, // GL_UNSIGNED_BYTE + GL_UNSIGNED_BYTE, window->osmesa.width, window->osmesa.height)) { _glfwInputError(GLFW_PLATFORM_ERROR, - "OSMesa: Failed to make context current."); + "OSMesa: Failed to make context current"); return; } } - else - { - _glfwInputError(GLFW_PLATFORM_ERROR, - "OSMesa: Releasing the current context is not supported."); - } _glfwPlatformSetCurrentContext(window); } static GLFWglproc getProcAddressOSMesa(const char* procname) { - _GLFWwindow* window = _glfwPlatformGetCurrentContext(); - - if (window->context.osmesa.handle) - { - return (GLFWglproc) OSMesaGetProcAddress(procname); - } - - return NULL; + return (GLFWglproc) OSMesaGetProcAddress(procname); } static void destroyContextOSMesa(_GLFWwindow* window) { - if (window->context.osmesa.handle != NULL) + if (window->context.osmesa.handle) { OSMesaDestroyContext(window->context.osmesa.handle); window->context.osmesa.handle = NULL; } - if (window->context.osmesa.buffer != NULL) + if (window->context.osmesa.buffer) { free(window->context.osmesa.buffer); window->context.osmesa.width = 0; @@ -78,15 +86,23 @@ static void destroyContextOSMesa(_GLFWwindow* window) } } -static void swapBuffersOSMesa(_GLFWwindow* window) {} - -static void swapIntervalOSMesa(int interval) {} - -static int extensionSupportedOSMesa() +static void swapBuffersOSMesa(_GLFWwindow* window) { + // No double buffering on OSMesa +} + +static void swapIntervalOSMesa(int interval) +{ + // No swap interval on OSMesa +} + +static int extensionSupportedOSMesa(const char* extension) +{ + // OSMesa does not have extensions return GLFW_FALSE; } + ////////////////////////////////////////////////////////////////////////// ////// GLFW internal API ////// ////////////////////////////////////////////////////////////////////////// @@ -96,11 +112,13 @@ GLFWbool _glfwInitOSMesa(void) int i; const char* sonames[] = { -#if defined(_GLFW_WIN32) +#if defined(_WIN32) "libOSMesa.dll", "OSMesa.dll", -#elif defined(_GLFW_COCOA) - "libOSMesa.dylib", +#elif defined(__APPLE__) + "libOSMesa.8.dylib", +#elif defined(__CYGWIN__) + "libOSMesa-8.so", #else "libOSMesa.so.6", #endif @@ -123,20 +141,12 @@ GLFWbool _glfwInitOSMesa(void) return GLFW_FALSE; } - _glfw.osmesa.prefix = (strncmp(sonames[i], "lib", 3) == 0); - - _glfw.osmesa.CreateContext = (PFNOSMESACREATECONTEXTPROC) - _glfw_dlsym(_glfw.osmesa.handle, "OSMesaCreateContext"); + _glfw.osmesa.CreateContextAttribs = (PFNOSMESACREATECONTEXTATTRIBSPROC) + _glfw_dlsym(_glfw.osmesa.handle, "OSMesaCreateContextAttribs"); _glfw.osmesa.DestroyContext = (PFNOSMESADESTROYCONTEXTPROC) _glfw_dlsym(_glfw.osmesa.handle, "OSMesaDestroyContext"); _glfw.osmesa.MakeCurrent = (PFNOSMESAMAKECURRENTPROC) _glfw_dlsym(_glfw.osmesa.handle, "OSMesaMakeCurrent"); - _glfw.osmesa.GetCurrentContext = (PFNOSMESAGETCURRENTCONTEXTPROC) - _glfw_dlsym(_glfw.osmesa.handle, "OSMesaGetCurrentContext"); - _glfw.osmesa.PixelStore = (PFNOSMESAPIXELSTOREPROC) - _glfw_dlsym(_glfw.osmesa.handle, "OSMesaPixelStore"); - _glfw.osmesa.GetIntegerv = (PFNOSMESAGETINTEGERVPROC) - _glfw_dlsym(_glfw.osmesa.handle, "OSMesaGetIntegerv"); _glfw.osmesa.GetColorBuffer = (PFNOSMESAGETCOLORBUFFERPROC) _glfw_dlsym(_glfw.osmesa.handle, "OSMesaGetColorBuffer"); _glfw.osmesa.GetDepthBuffer = (PFNOSMESAGETDEPTHBUFFERPROC) @@ -144,12 +154,9 @@ GLFWbool _glfwInitOSMesa(void) _glfw.osmesa.GetProcAddress = (PFNOSMESAGETPROCADDRESSPROC) _glfw_dlsym(_glfw.osmesa.handle, "OSMesaGetProcAddress"); - if (!_glfw.osmesa.CreateContext || + if (!_glfw.osmesa.CreateContextAttribs || !_glfw.osmesa.DestroyContext || !_glfw.osmesa.MakeCurrent || - !_glfw.osmesa.GetCurrentContext || - !_glfw.osmesa.PixelStore || - !_glfw.osmesa.GetIntegerv || !_glfw.osmesa.GetColorBuffer || !_glfw.osmesa.GetDepthBuffer || !_glfw.osmesa.GetProcAddress) @@ -173,30 +180,56 @@ void _glfwTerminateOSMesa(void) } } +#define setAttrib(attribName, attribValue) \ +{ \ + attribs[index++] = attribName; \ + attribs[index++] = attribValue; \ + assert((size_t) index < sizeof(attribs) / sizeof(attribs[0])); \ +} + GLFWbool _glfwCreateContextOSMesa(_GLFWwindow* window, const _GLFWctxconfig* ctxconfig, const _GLFWfbconfig* fbconfig) { - OSMesaContext share; + OSMesaContext share = NULL; + int index = 0, attribs[40]; if (ctxconfig->share) share = ctxconfig->share->context.osmesa.handle; - // Initialize the context. - window->context.osmesa.buffer = NULL; - window->context.osmesa.width = 0; - window->context.osmesa.height = 0; + setAttrib(OSMESA_FORMAT, OSMESA_RGBA); + setAttrib(OSMESA_DEPTH_BITS, fbconfig->depthBits); + setAttrib(OSMESA_STENCIL_BITS, fbconfig->stencilBits); + setAttrib(OSMESA_ACCUM_BITS, fbconfig->accumRedBits + + fbconfig->accumGreenBits + + fbconfig->accumBlueBits + + fbconfig->accumAlphaBits); - // Create to create an OSMesa context. - window->context.osmesa.handle = OSMesaCreateContext(OSMESA_RGBA, share); - if (window->context.osmesa.handle == 0) + if (ctxconfig->profile == GLFW_OPENGL_CORE_PROFILE) + { + setAttrib(OSMESA_PROFILE, OSMESA_CORE_PROFILE); + } + else if (ctxconfig->profile == GLFW_OPENGL_COMPAT_PROFILE) + { + setAttrib(OSMESA_PROFILE, OSMESA_COMPAT_PROFILE); + } + + if (ctxconfig->major != 1 || ctxconfig->minor != 0) + { + setAttrib(OSMESA_CONTEXT_MAJOR_VERSION, ctxconfig->major); + setAttrib(OSMESA_CONTEXT_MINOR_VERSION, ctxconfig->minor); + } + + setAttrib(0, 0); + + window->context.osmesa.handle = OSMesaCreateContextAttribs(attribs, share); + if (window->context.osmesa.handle == NULL) { _glfwInputError(GLFW_VERSION_UNAVAILABLE, - "OSMesa: Failed to create context."); + "OSMesa: Failed to create context"); return GLFW_FALSE; } - // Set up the context API. window->context.makeCurrent = makeContextCurrentOSMesa; window->context.swapBuffers = swapBuffersOSMesa; window->context.swapInterval = swapIntervalOSMesa; @@ -207,7 +240,88 @@ GLFWbool _glfwCreateContextOSMesa(_GLFWwindow* window, return GLFW_TRUE; } +#undef setAttrib + + ////////////////////////////////////////////////////////////////////////// ////// GLFW native API ////// ////////////////////////////////////////////////////////////////////////// +GLFWAPI int glfwGetOSMesaColorBuffer(GLFWwindow* handle, int* width, + int* height, int* format, void** buffer) +{ + void* mesaBuffer; + GLint mesaWidth, mesaHeight, mesaFormat; + _GLFWwindow* window = (_GLFWwindow*) handle; + assert(window != NULL); + + _GLFW_REQUIRE_INIT_OR_RETURN(GLFW_FALSE); + + if (!OSMesaGetColorBuffer(window->context.osmesa.handle, + &mesaWidth, &mesaHeight, + &mesaFormat, &mesaBuffer)) + { + _glfwInputError(GLFW_PLATFORM_ERROR, + "OSMesa: Failed to retrieve color buffer"); + return GLFW_FALSE; + } + + if (width) + *width = mesaWidth; + if (height) + *height = mesaHeight; + if (format) + *format = mesaFormat; + if (buffer) + *buffer = mesaBuffer; + + return GLFW_TRUE; +} + +GLFWAPI int glfwGetOSMesaDepthBuffer(GLFWwindow* handle, + int* width, int* height, + int* bytesPerValue, + void** buffer) +{ + void* mesaBuffer; + GLint mesaWidth, mesaHeight, mesaBytes; + _GLFWwindow* window = (_GLFWwindow*) handle; + assert(window != NULL); + + _GLFW_REQUIRE_INIT_OR_RETURN(GLFW_FALSE); + + if (!OSMesaGetDepthBuffer(window->context.osmesa.handle, + &mesaWidth, &mesaHeight, + &mesaBytes, &mesaBuffer)) + { + _glfwInputError(GLFW_PLATFORM_ERROR, + "OSMesa: Failed to retrieve depth buffer"); + return GLFW_FALSE; + } + + if (width) + *width = mesaWidth; + if (height) + *height = mesaHeight; + if (bytesPerValue) + *bytesPerValue = mesaBytes; + if (buffer) + *buffer = mesaBuffer; + + return GLFW_TRUE; +} + +GLFWAPI OSMesaContext glfwGetOSMesaContext(GLFWwindow* handle) +{ + _GLFWwindow* window = (_GLFWwindow*) handle; + _GLFW_REQUIRE_INIT_OR_RETURN(NULL); + + if (window->context.client == GLFW_NO_API) + { + _glfwInputError(GLFW_NO_WINDOW_CONTEXT, NULL); + return NULL; + } + + return window->context.osmesa.handle; +} + diff --git a/src/osmesa_context.h b/src/osmesa_context.h index 2283cf741..2dbac9dd2 100644 --- a/src/osmesa_context.h +++ b/src/osmesa_context.h @@ -1,49 +1,62 @@ +//======================================================================== +// GLFW 3.3 OSMesa - www.glfw.org +//------------------------------------------------------------------------ +// Copyright (c) 2016 Google Inc. +// Copyright (c) 2006-2016 Camilla Berglund +// +// This software is provided 'as-is', without any express or implied +// warranty. In no event will the authors be held liable for any damages +// arising from the use of this software. +// +// Permission is granted to anyone to use this software for any purpose, +// including commercial applications, and to alter it and redistribute it +// freely, subject to the following restrictions: +// +// 1. The origin of this software must not be misrepresented; you must not +// claim that you wrote the original software. If you use this software +// in a product, an acknowledgment in the product documentation would +// be appreciated but is not required. +// +// 2. Altered source versions must be plainly marked as such, and must not +// be misrepresented as being the original software. +// +// 3. This notice may not be removed or altered from any source +// distribution. +// +//======================================================================== #ifndef _glfw3_osmesa_context_h_ #define _glfw3_osmesa_context_h_ -#define OSMESA_COLOR_INDEX GL_COLOR_INDEX -#define OSMESA_RGBA 0x1908 -#define OSMESA_BGRA 0x1 -#define OSMESA_ARGB 0x2 -#define OSMESA_RGB 0x1907 -#define OSMESA_BGR 0x4 -#define OSMESA_RGB_565 0x5 - -#define OSMESA_ROW_LENGTH 0x10 -#define OSMESA_Y_UP 0x11 - -#define OSMESA_WIDTH 0x20 -#define OSMESA_HEIGHT 0x21 -#define OSMESA_FORMAT 0x22 -#define OSMESA_TYPE 0x23 -#define OSMESA_MAX_WIDTH 0x24 -#define OSMESA_MAX_HEIGHT 0x25 +#define OSMESA_RGBA 0x1908 +#define OSMESA_FORMAT 0x22 +#define OSMESA_DEPTH_BITS 0x30 +#define OSMESA_STENCIL_BITS 0x31 +#define OSMESA_ACCUM_BITS 0x32 +#define OSMESA_PROFILE 0x33 +#define OSMESA_CORE_PROFILE 0x34 +#define OSMESA_COMPAT_PROFILE 0x35 +#define OSMESA_CONTEXT_MAJOR_VERSION 0x36 +#define OSMESA_CONTEXT_MINOR_VERSION 0x37 typedef void* OSMesaContext; typedef void (*OSMESAproc)(); -typedef OSMesaContext (* PFNOSMESACREATECONTEXTPROC)(GLenum, OSMesaContext); +typedef OSMesaContext (* PFNOSMESACREATECONTEXTATTRIBSPROC)(const int*,OSMesaContext); typedef void (* PFNOSMESADESTROYCONTEXTPROC)(OSMesaContext); -typedef int (* PFNOSMESAMAKECURRENTPROC)(OSMesaContext, void*, int, int, int); -typedef OSMesaContext (* PFNOSMESAGETCURRENTCONTEXTPROC)(); -typedef void (* PFNOSMESAPIXELSTOREPROC)(int, int); -typedef void (* PFNOSMESAGETINTEGERVPROC)(int, int*); -typedef int (* PFNOSMESAGETCOLORBUFFERPROC)(OSMesaContext, int*, int*, int*, void**); -typedef int (* PFNOSMESAGETDEPTHBUFFERPROC)(OSMesaContext, int*, int*, int*, void**); +typedef int (* PFNOSMESAMAKECURRENTPROC)(OSMesaContext,void*,int,int,int); +typedef int (* PFNOSMESAGETCOLORBUFFERPROC)(OSMesaContext,int*,int*,int*,void**); +typedef int (* PFNOSMESAGETDEPTHBUFFERPROC)(OSMesaContext,int*,int*,int*,void**); typedef GLFWglproc (* PFNOSMESAGETPROCADDRESSPROC)(const char*); -#define OSMesaCreateContext _glfw.osmesa.CreateContext +#define OSMesaCreateContextAttribs _glfw.osmesa.CreateContextAttribs #define OSMesaDestroyContext _glfw.osmesa.DestroyContext #define OSMesaMakeCurrent _glfw.osmesa.MakeCurrent -#define OSMesaGetCurrentContext _glfw.osmesa.GetCurrentContext -#define OSMesaPixelStore _glfw.osmesa.PixelStore -#define OSMesaGetIntegerv _glfw.osmesa.GetIntegerv #define OSMesaGetColorBuffer _glfw.osmesa.GetColorBuffer #define OSMesaGetDepthBuffer _glfw.osmesa.GetDepthBuffer #define OSMesaGetProcAddress _glfw.osmesa.GetProcAddress -#define _GLFW_PLATFORM_CONTEXT_STATE _GLFWcontextOSMesa osmesa -#define _GLFW_PLATFORM_LIBRARY_CONTEXT_STATE _GLFWctxlibraryOSMesa osmesa +#define _GLFW_PLATFORM_CONTEXT_STATE _GLFWcontextOSMesa osmesa +#define _GLFW_PLATFORM_LIBRARY_CONTEXT_STATE _GLFWlibraryOSMesa osmesa // OSMesa-specific per-context data @@ -53,29 +66,24 @@ typedef struct _GLFWcontextOSMesa OSMesaContext handle; int width; int height; - void * buffer; + void* buffer; } _GLFWcontextOSMesa; // OSMesa-specific global data // -typedef struct _GLFWctxlibraryOSMesa +typedef struct _GLFWlibraryOSMesa { - GLFWbool prefix; - void* handle; - PFNOSMESACREATECONTEXTPROC CreateContext; - PFNOSMESADESTROYCONTEXTPROC DestroyContext; - PFNOSMESAMAKECURRENTPROC MakeCurrent; - PFNOSMESAGETCURRENTCONTEXTPROC GetCurrentContext; - PFNOSMESAPIXELSTOREPROC PixelStore; - PFNOSMESAGETINTEGERVPROC GetIntegerv; - PFNOSMESAGETCOLORBUFFERPROC GetColorBuffer; - PFNOSMESAGETDEPTHBUFFERPROC GetDepthBuffer; - PFNOSMESAGETPROCADDRESSPROC GetProcAddress; + PFNOSMESACREATECONTEXTATTRIBSPROC CreateContextAttribs; + PFNOSMESADESTROYCONTEXTPROC DestroyContext; + PFNOSMESAMAKECURRENTPROC MakeCurrent; + PFNOSMESAGETCOLORBUFFERPROC GetColorBuffer; + PFNOSMESAGETDEPTHBUFFERPROC GetDepthBuffer; + PFNOSMESAGETPROCADDRESSPROC GetProcAddress; -} _GLFWctxlibraryOSMesa; +} _GLFWlibraryOSMesa; GLFWbool _glfwInitOSMesa(void); diff --git a/src/osmesa_init.c b/src/osmesa_init.c index 46f2458d4..834f9ab2f 100644 --- a/src/osmesa_init.c +++ b/src/osmesa_init.c @@ -1,10 +1,32 @@ +//======================================================================== +// GLFW 3.3 OSMesa - www.glfw.org +//------------------------------------------------------------------------ +// Copyright (c) 2016 Google Inc. +// Copyright (c) 2006-2016 Camilla Berglund +// +// This software is provided 'as-is', without any express or implied +// warranty. In no event will the authors be held liable for any damages +// arising from the use of this software. +// +// Permission is granted to anyone to use this software for any purpose, +// including commercial applications, and to alter it and redistribute it +// freely, subject to the following restrictions: +// +// 1. The origin of this software must not be misrepresented; you must not +// claim that you wrote the original software. If you use this software +// in a product, an acknowledgment in the product documentation would +// be appreciated but is not required. +// +// 2. Altered source versions must be plainly marked as such, and must not +// be misrepresented as being the original software. +// +// 3. This notice may not be removed or altered from any source +// distribution. +// +//======================================================================== #include "internal.h" -////////////////////////////////////////////////////////////////////////// -////// GLFW internal API ////// -////////////////////////////////////////////////////////////////////////// - ////////////////////////////////////////////////////////////////////////// ////// GLFW platform API ////// @@ -12,24 +34,21 @@ int _glfwPlatformInit(void) { - if (!_glfwInitThreadLocalStoragePOSIX()) { - return GLFW_FALSE; - } + if (!_glfwInitThreadLocalStoragePOSIX()) + return GLFW_FALSE; - _glfwInitTimerPOSIX(); - - return GLFW_TRUE; + _glfwInitTimerPOSIX(); + return GLFW_TRUE; } void _glfwPlatformTerminate(void) { - _glfwTerminateOSMesa(); - _glfwTerminateThreadLocalStoragePOSIX(); + _glfwTerminateOSMesa(); + _glfwTerminateThreadLocalStoragePOSIX(); } const char* _glfwPlatformGetVersionString(void) { - const char* version = _GLFW_VERSION_NUMBER " OSMESA"; - return version; + return _GLFW_VERSION_NUMBER " none OSMesa"; } diff --git a/src/osmesa_monitor.c b/src/osmesa_monitor.c index fb9ede251..64222a92d 100644 --- a/src/osmesa_monitor.c +++ b/src/osmesa_monitor.c @@ -1,9 +1,32 @@ +//======================================================================== +// GLFW 3.3 OSMesa - www.glfw.org +//------------------------------------------------------------------------ +// Copyright (c) 2016 Google Inc. +// Copyright (c) 2006-2016 Camilla Berglund +// +// This software is provided 'as-is', without any express or implied +// warranty. In no event will the authors be held liable for any damages +// arising from the use of this software. +// +// Permission is granted to anyone to use this software for any purpose, +// including commercial applications, and to alter it and redistribute it +// freely, subject to the following restrictions: +// +// 1. The origin of this software must not be misrepresented; you must not +// claim that you wrote the original software. If you use this software +// in a product, an acknowledgment in the product documentation would +// be appreciated but is not required. +// +// 2. Altered source versions must be plainly marked as such, and must not +// be misrepresented as being the original software. +// +// 3. This notice may not be removed or altered from any source +// distribution. +// +//======================================================================== #include "internal.h" -////////////////////////////////////////////////////////////////////////// -////// GLFW internal API ////// -////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////// ////// GLFW platform API ////// @@ -11,33 +34,34 @@ _GLFWmonitor** _glfwPlatformGetMonitors(int* count) { - // OSMesa is headless, so no monitors. - _GLFWmonitor** monitors = NULL; - if (count != NULL) { + // OSMesa is headless, so no monitors *count = 0; - } - return monitors; + return NULL; } int _glfwPlatformIsSameMonitor(_GLFWmonitor* first, _GLFWmonitor* second) { - return GLFW_FALSE; + return GLFW_FALSE; } -void _glfwPlatformGetMonitorPos(_GLFWmonitor* monitor, int* xpos, int* ypos) {} +void _glfwPlatformGetMonitorPos(_GLFWmonitor* monitor, int* xpos, int* ypos) +{ +} GLFWvidmode* _glfwPlatformGetVideoModes(_GLFWmonitor* monitor, int* found) { - return NULL; + return NULL; } -void _glfwPlatformGetVideoMode(_GLFWmonitor* monitor, GLFWvidmode* mode) {} +void _glfwPlatformGetVideoMode(_GLFWmonitor* monitor, GLFWvidmode* mode) +{ +} -void _glfwPlatformGetGammaRamp(_GLFWmonitor* monitor, GLFWgammaramp* ramp) {} +void _glfwPlatformGetGammaRamp(_GLFWmonitor* monitor, GLFWgammaramp* ramp) +{ +} -void _glfwPlatformSetGammaRamp(_GLFWmonitor* monitor, - const GLFWgammaramp* ramp) {} +void _glfwPlatformSetGammaRamp(_GLFWmonitor* monitor, const GLFWgammaramp* ramp) +{ +} -////////////////////////////////////////////////////////////////////////// -////// GLFW native API ////// -////////////////////////////////////////////////////////////////////////// diff --git a/src/osmesa_platform.h b/src/osmesa_platform.h index f5e114e3f..74c7f6a79 100644 --- a/src/osmesa_platform.h +++ b/src/osmesa_platform.h @@ -1,15 +1,40 @@ +//======================================================================== +// GLFW 3.3 OSMesa - www.glfw.org +//------------------------------------------------------------------------ +// Copyright (c) 2016 Google Inc. +// Copyright (c) 2006-2016 Camilla Berglund +// +// This software is provided 'as-is', without any express or implied +// warranty. In no event will the authors be held liable for any damages +// arising from the use of this software. +// +// Permission is granted to anyone to use this software for any purpose, +// including commercial applications, and to alter it and redistribute it +// freely, subject to the following restrictions: +// +// 1. The origin of this software must not be misrepresented; you must not +// claim that you wrote the original software. If you use this software +// in a product, an acknowledgment in the product documentation would +// be appreciated but is not required. +// +// 2. Altered source versions must be plainly marked as such, and must not +// be misrepresented as being the original software. +// +// 3. This notice may not be removed or altered from any source +// distribution. +// +//======================================================================== -#ifndef _osmesa_platform_h_ -#define _osmesa_platform_h_ +#ifndef _glfw3_osmesa_platform_h_ +#define _glfw3_osmesa_platform_h_ #include -#define _GLFW_PLATFORM_WINDOW_STATE _GLFWwindowOSMesa osmesa -#define _GLFW_PLATFORM_MONITOR_STATE _GLFWmonitorOSMesa osmesa -#define _GLFW_PLATFORM_CURSOR_STATE _GLFWcursorOSMesa osmesa - -#define _GLFW_PLATFORM_LIBRARY_WINDOW_STATE _GLFWwinlibraryOSMesa osmesawin +#define _GLFW_PLATFORM_WINDOW_STATE _GLFWwindowOSMesa osmesa +#define _GLFW_PLATFORM_MONITOR_STATE +#define _GLFW_PLATFORM_CURSOR_STATE +#define _GLFW_PLATFORM_LIBRARY_WINDOW_STATE #define _GLFW_PLATFORM_LIBRARY_JOYSTICK_STATE #define _GLFW_EGL_CONTEXT_STATE #define _GLFW_EGL_LIBRARY_CONTEXT_STATE @@ -18,44 +43,23 @@ #include "posix_time.h" #include "posix_tls.h" -#define _glfw_dlopen(name) dlopen(name, RTLD_LAZY | RTLD_LOCAL) -#define _glfw_dlclose(handle) dlclose(handle) -#define _glfw_dlsym(handle, name) dlsym(handle, name) - -// TODO(dalyj) "PLACEHOLDER" variables are there to silence "empty struct" -// warnings +#if defined(_GLFW_WIN32) + #define _glfw_dlopen(name) LoadLibraryA(name) + #define _glfw_dlclose(handle) FreeLibrary((HMODULE) handle) + #define _glfw_dlsym(handle, name) GetProcAddress((HMODULE) handle, name) +#else + #define _glfw_dlopen(name) dlopen(name, RTLD_LAZY | RTLD_LOCAL) + #define _glfw_dlclose(handle) dlclose(handle) + #define _glfw_dlsym(handle, name) dlsym(handle, name) +#endif // OSMesa-specific per-window data // typedef struct _GLFWwindowOSMesa { - int width; - int height; + int width; + int height; } _GLFWwindowOSMesa; -// OSMesa-specific global data -// -typedef struct _GLFWwinlibraryOSMesa -{ - int PLACEHOLDER; -} _GLFWwinlibraryOSMesa; - - -// OSMesa-specific per-monitor data -// -typedef struct _GLFWmonitorOSMesa -{ - int PLACEHOLDER; -} _GLFWmonitorOSMesa; - - -// OSMesa-specific per-cursor data -// -typedef struct _GLFWcursorOSMesa -{ - int PLACEHOLDER; -} _GLFWcursorOSMesa; - - -#endif // _osmesa_platform_h_ +#endif // _glfw3_osmesa_platform_h_ diff --git a/src/osmesa_window.c b/src/osmesa_window.c index c171d6290..56923ec78 100644 --- a/src/osmesa_window.c +++ b/src/osmesa_window.c @@ -1,9 +1,35 @@ +//======================================================================== +// GLFW 3.3 OSMesa - www.glfw.org +//------------------------------------------------------------------------ +// Copyright (c) 2016 Google Inc. +// Copyright (c) 2006-2016 Camilla Berglund +// +// This software is provided 'as-is', without any express or implied +// warranty. In no event will the authors be held liable for any damages +// arising from the use of this software. +// +// Permission is granted to anyone to use this software for any purpose, +// including commercial applications, and to alter it and redistribute it +// freely, subject to the following restrictions: +// +// 1. The origin of this software must not be misrepresented; you must not +// claim that you wrote the original software. If you use this software +// in a product, an acknowledgment in the product documentation would +// be appreciated but is not required. +// +// 2. Altered source versions must be plainly marked as such, and must not +// be misrepresented as being the original software. +// +// 3. This notice may not be removed or altered from any source +// distribution. +// +//======================================================================== #include "internal.h" -#include -int createWindow(_GLFWwindow* window, const _GLFWwndconfig* wndconfig) +static int createNativeWindow(_GLFWwindow* window, + const _GLFWwndconfig* wndconfig) { window->osmesa.width = wndconfig->width; window->osmesa.height = wndconfig->height; @@ -11,9 +37,6 @@ int createWindow(_GLFWwindow* window, const _GLFWwndconfig* wndconfig) return GLFW_TRUE; } -////////////////////////////////////////////////////////////////////////// -////// GLFW internal API ////// -////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////// ////// GLFW platform API ////// @@ -27,10 +50,13 @@ int _glfwPlatformCreateWindow(_GLFWwindow* window, if (!_glfwInitOSMesa()) return GLFW_FALSE; - if (!_glfwCreateContextOSMesa(window, ctxconfig, fbconfig)) - return GLFW_FALSE; + if (ctxconfig->client != GLFW_NO_API) + { + if (!_glfwCreateContextOSMesa(window, ctxconfig, fbconfig)) + return GLFW_FALSE; + } - if (!createWindow(window, wndconfig)) + if (!createNativeWindow(window, wndconfig)) return GLFW_FALSE; return GLFW_TRUE; @@ -42,29 +68,37 @@ void _glfwPlatformDestroyWindow(_GLFWwindow* window) window->context.destroy(window); } -void _glfwPlatformSetWindowTitle(_GLFWwindow* window, const char* title) {} +void _glfwPlatformSetWindowTitle(_GLFWwindow* window, const char* title) +{ +} void _glfwPlatformSetWindowIcon(_GLFWwindow* window, int count, - const GLFWimage* images) {} + const GLFWimage* images) +{ +} void _glfwPlatformSetWindowMonitor(_GLFWwindow* window, _GLFWmonitor* monitor, int xpos, int ypos, int width, int height, - int refreshRate) {} + int refreshRate) +{ +} void _glfwPlatformGetWindowPos(_GLFWwindow* window, int* xpos, int* ypos) { - if (xpos != NULL) *xpos = 0; - if (ypos != NULL) *ypos = 0; } -void _glfwPlatformSetWindowPos(_GLFWwindow* window, int xpos, int ypos) {} +void _glfwPlatformSetWindowPos(_GLFWwindow* window, int xpos, int ypos) +{ +} void _glfwPlatformGetWindowSize(_GLFWwindow* window, int* width, int* height) { - if (width != NULL) *width = window->osmesa.width; - if (height != NULL) *height = window->osmesa.height; + if (width) + *width = window->osmesa.width; + if (height) + *height = window->osmesa.height; } void _glfwPlatformSetWindowSize(_GLFWwindow* window, int width, int height) @@ -75,70 +109,114 @@ void _glfwPlatformSetWindowSize(_GLFWwindow* window, int width, int height) void _glfwPlatformSetWindowSizeLimits(_GLFWwindow* window, int minwidth, int minheight, - int maxwidth, int maxheight) {} - -void _glfwPlatformSetWindowAspectRatio(_GLFWwindow* window, int n, int d) {} - -void _glfwPlatformGetFramebufferSize(_GLFWwindow* window, int* width, - int* height) + int maxwidth, int maxheight) { - if (width != NULL) *width = window->osmesa.width; - if (height != NULL) *height = window->osmesa.height; } -void _glfwPlatformGetWindowFrameSize(_GLFWwindow* window, int* left, int* top, +void _glfwPlatformSetWindowAspectRatio(_GLFWwindow* window, int n, int d) +{ +} + +void _glfwPlatformGetFramebufferSize(_GLFWwindow* window, int* width, int* height) +{ + if (width) + *width = window->osmesa.width; + if (height) + *height = window->osmesa.height; +} + +void _glfwPlatformGetWindowFrameSize(_GLFWwindow* window, + int* left, int* top, int* right, int* bottom) { - if (left != NULL) *left = 0; - if (top != NULL) *top = 0; - if (right != NULL) *right = window->osmesa.width; - if (bottom != NULL) *top = window->osmesa.height; + if (right) + *right = window->osmesa.width; + if (bottom) + *top = window->osmesa.height; } -void _glfwPlatformIconifyWindow(_GLFWwindow* window) {} - -void _glfwPlatformRestoreWindow(_GLFWwindow* window) {} - -void _glfwPlatformMaximizeWindow(_GLFWwindow* window) {} - -int _glfwPlatformWindowMaximized(_GLFWwindow* window) { - return 0; +void _glfwPlatformIconifyWindow(_GLFWwindow* window) +{ } -void _glfwPlatformShowWindow(_GLFWwindow* window) {} - -void _glfwPlatformUnhideWindow(_GLFWwindow* window) {} - -void _glfwPlatformHideWindow(_GLFWwindow* window) {} - -void _glfwPlatformFocusWindow(_GLFWwindow* window) {} - -int _glfwPlatformWindowFocused(_GLFWwindow* window) { return GLFW_FALSE; } - -int _glfwPlatformWindowIconified(_GLFWwindow* window) { return GLFW_FALSE; } - -int _glfwPlatformWindowVisible(_GLFWwindow* window) { return GLFW_FALSE; } - -void _glfwPlatformPollEvents(void) {} - -void _glfwPlatformWaitEvents(void) {} - -void _glfwPlatformWaitEventsTimeout(double timeout) {} - -void _glfwPlatformPostEmptyEvent(void) {} - -void _glfwPlatformGetCursorPos(_GLFWwindow* window, double* xpos, double* ypos) { - if (xpos != NULL) *xpos = 0; - if (ypos != NULL) *ypos = 0; +void _glfwPlatformRestoreWindow(_GLFWwindow* window) +{ } -void _glfwPlatformSetCursorPos(_GLFWwindow* window, double x, double y) {} +void _glfwPlatformMaximizeWindow(_GLFWwindow* window) +{ +} -void _glfwPlatformSetCursorMode(_GLFWwindow* window, int mode) {} +int _glfwPlatformWindowMaximized(_GLFWwindow* window) +{ + return GLFW_FALSE; +} -void _glfwPlatformApplyCursorMode(_GLFWwindow* window) {} +void _glfwPlatformShowWindow(_GLFWwindow* window) +{ +} -int _glfwPlatformCreateCursor(_GLFWcursor* cursor, const GLFWimage* image, +void _glfwPlatformUnhideWindow(_GLFWwindow* window) +{ +} + +void _glfwPlatformHideWindow(_GLFWwindow* window) +{ +} + +void _glfwPlatformFocusWindow(_GLFWwindow* window) +{ +} + +int _glfwPlatformWindowFocused(_GLFWwindow* window) +{ + return GLFW_FALSE; +} + +int _glfwPlatformWindowIconified(_GLFWwindow* window) +{ + return GLFW_FALSE; +} + +int _glfwPlatformWindowVisible(_GLFWwindow* window) +{ + return GLFW_FALSE; +} + +void _glfwPlatformPollEvents(void) +{ +} + +void _glfwPlatformWaitEvents(void) +{ +} + +void _glfwPlatformWaitEventsTimeout(double timeout) +{ +} + +void _glfwPlatformPostEmptyEvent(void) +{ +} + +void _glfwPlatformGetCursorPos(_GLFWwindow* window, double* xpos, double* ypos) +{ +} + +void _glfwPlatformSetCursorPos(_GLFWwindow* window, double x, double y) +{ +} + +void _glfwPlatformSetCursorMode(_GLFWwindow* window, int mode) +{ +} + +void _glfwPlatformApplyCursorMode(_GLFWwindow* window) +{ +} + +int _glfwPlatformCreateCursor(_GLFWcursor* cursor, + const GLFWimage* image, int xhot, int yhot) { return GLFW_FALSE; @@ -149,41 +227,57 @@ int _glfwPlatformCreateStandardCursor(_GLFWcursor* cursor, int shape) return GLFW_FALSE; } -void _glfwPlatformDestroyCursor(_GLFWcursor* cursor) {} +void _glfwPlatformDestroyCursor(_GLFWcursor* cursor) +{ +} -void _glfwPlatformSetCursor(_GLFWwindow* window, _GLFWcursor* cursor) {} +void _glfwPlatformSetCursor(_GLFWwindow* window, _GLFWcursor* cursor) +{ +} -void _glfwPlatformSetClipboardString(_GLFWwindow* window, const char* string) {} +void _glfwPlatformSetClipboardString(_GLFWwindow* window, const char* string) +{ +} const char* _glfwPlatformGetClipboardString(_GLFWwindow* window) { return NULL; } -const char* _glfwPlatformGetKeyName(int key, int scancode) { return ""; } +const char* _glfwPlatformGetKeyName(int key, int scancode) +{ + return ""; +} -int _glfwPlatformJoystickPresent(int joy) { return 0; } +int _glfwPlatformGetKeyScancode(int key) +{ + return -1; +} + +int _glfwPlatformJoystickPresent(int joy) +{ + return GLFW_FALSE; +} const float* _glfwPlatformGetJoystickAxes(int joy, int* count) { - if (count != NULL) *count = 0; return NULL; } const unsigned char* _glfwPlatformGetJoystickButtons(int joy, int* count) { - if (count != NULL) *count = 0; return NULL; } -const char* _glfwPlatformGetJoystickName(int joy) { return NULL; } - -char** _glfwPlatformGetRequiredInstanceExtensions(uint32_t* count) +const char* _glfwPlatformGetJoystickName(int joy) { - if (count != NULL) *count = 0; return NULL; } +void _glfwPlatformGetRequiredInstanceExtensions(char** extensions) +{ +} + int _glfwPlatformGetPhysicalDevicePresentationSupport(VkInstance instance, VkPhysicalDevice device, uint32_t queuefamily) @@ -196,64 +290,7 @@ VkResult _glfwPlatformCreateWindowSurface(VkInstance instance, const VkAllocationCallbacks* allocator, VkSurfaceKHR* surface) { - // This seems like the most appropriate error to return here. + // This seems like the most appropriate error to return here return VK_ERROR_INITIALIZATION_FAILED; } -////////////////////////////////////////////////////////////////////////// -////// GLFW native API ////// -////////////////////////////////////////////////////////////////////////// - -GLFWAPI int glfwGetOSMesaColorBuffer(GLFWwindow* window, int* width, - int* height, int* format, void** buffer) -{ - GLint mesaWidth; - GLint mesaHeight; - GLint mesaFormat; - void* mesaBuffer; - - assert(window != NULL); - - OSMesaContext ctx = ((_GLFWwindow*) window)->context.osmesa.handle; - - // Query OSMesa for the color buffer data. - int result = OSMesaGetColorBuffer( - ctx, &mesaWidth, &mesaHeight, &mesaFormat, &mesaBuffer); - if (result) { - // Copy the values returned by OSMesa. - if (width != NULL) *width = mesaWidth; - if (height != NULL) *height = mesaHeight; - if (format != NULL) *format = mesaFormat; - if (buffer != NULL) *buffer = mesaBuffer; - } - - return result; -} - -GLFWAPI int glfwGetOSMesaDepthBuffer(GLFWwindow* window, int* width, - int* height, int* bytesPerValue, - void** buffer) -{ - GLint mesaWidth; - GLint mesaHeight; - GLint mesaBytes; - void* mesaBuffer; - - assert(window != NULL); - - OSMesaContext ctx = ((_GLFWwindow*) window)->context.osmesa.handle; - - // Query OSMesa for the color buffer data. - int result = OSMesaGetDepthBuffer( - ctx, &mesaWidth, &mesaHeight, &mesaBytes, &mesaBuffer); - if (result) { - // Copy the values returned by OSMesa. - if (width != NULL) *width = mesaWidth; - if (height != NULL) *height = mesaHeight; - if (bytesPerValue != NULL) *bytesPerValue = mesaBytes; - if (buffer != NULL) *buffer = mesaBuffer; - } - - return result; -} - From ac836396316dd9a4eca85482391f02c127547468 Mon Sep 17 00:00:00 2001 From: Camilla Berglund Date: Thu, 13 Oct 2016 17:42:44 +0200 Subject: [PATCH 36/40] Add stb_image_write --- README.md | 1 + deps/stb_image_write.h | 1048 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 1049 insertions(+) create mode 100644 deps/stb_image_write.h diff --git a/README.md b/README.md index a0dc71913..bc2a74aca 100644 --- a/README.md +++ b/README.md @@ -81,6 +81,7 @@ located in the `deps/` directory. - [linmath.h](https://github.com/datenwolf/linmath.h) for linear algebra in examples - [Nuklear](https://github.com/vurtun/nuklear) for test and example UI + - [stb\_image\_write](https://github.com/nothings/stb) for writing images to disk - [Vulkan headers](https://www.khronos.org/registry/vulkan/) for Vulkan tests The Vulkan example additionally requires the Vulkan SDK to be installed, or it diff --git a/deps/stb_image_write.h b/deps/stb_image_write.h new file mode 100644 index 000000000..4319c0de1 --- /dev/null +++ b/deps/stb_image_write.h @@ -0,0 +1,1048 @@ +/* stb_image_write - v1.02 - public domain - http://nothings.org/stb/stb_image_write.h + writes out PNG/BMP/TGA images to C stdio - Sean Barrett 2010-2015 + no warranty implied; use at your own risk + + Before #including, + + #define STB_IMAGE_WRITE_IMPLEMENTATION + + in the file that you want to have the implementation. + + Will probably not work correctly with strict-aliasing optimizations. + +ABOUT: + + This header file is a library for writing images to C stdio. It could be + adapted to write to memory or a general streaming interface; let me know. + + The PNG output is not optimal; it is 20-50% larger than the file + written by a decent optimizing implementation. This library is designed + for source code compactness and simplicity, not optimal image file size + or run-time performance. + +BUILDING: + + You can #define STBIW_ASSERT(x) before the #include to avoid using assert.h. + You can #define STBIW_MALLOC(), STBIW_REALLOC(), and STBIW_FREE() to replace + malloc,realloc,free. + You can define STBIW_MEMMOVE() to replace memmove() + +USAGE: + + There are four functions, one for each image file format: + + int stbi_write_png(char const *filename, int w, int h, int comp, const void *data, int stride_in_bytes); + int stbi_write_bmp(char const *filename, int w, int h, int comp, const void *data); + int stbi_write_tga(char const *filename, int w, int h, int comp, const void *data); + int stbi_write_hdr(char const *filename, int w, int h, int comp, const float *data); + + There are also four equivalent functions that use an arbitrary write function. You are + expected to open/close your file-equivalent before and after calling these: + + int stbi_write_png_to_func(stbi_write_func *func, void *context, int w, int h, int comp, const void *data, int stride_in_bytes); + int stbi_write_bmp_to_func(stbi_write_func *func, void *context, int w, int h, int comp, const void *data); + int stbi_write_tga_to_func(stbi_write_func *func, void *context, int w, int h, int comp, const void *data); + int stbi_write_hdr_to_func(stbi_write_func *func, void *context, int w, int h, int comp, const float *data); + + where the callback is: + void stbi_write_func(void *context, void *data, int size); + + You can define STBI_WRITE_NO_STDIO to disable the file variant of these + functions, so the library will not use stdio.h at all. However, this will + also disable HDR writing, because it requires stdio for formatted output. + + Each function returns 0 on failure and non-0 on success. + + The functions create an image file defined by the parameters. The image + is a rectangle of pixels stored from left-to-right, top-to-bottom. + Each pixel contains 'comp' channels of data stored interleaved with 8-bits + per channel, in the following order: 1=Y, 2=YA, 3=RGB, 4=RGBA. (Y is + monochrome color.) The rectangle is 'w' pixels wide and 'h' pixels tall. + The *data pointer points to the first byte of the top-left-most pixel. + For PNG, "stride_in_bytes" is the distance in bytes from the first byte of + a row of pixels to the first byte of the next row of pixels. + + PNG creates output files with the same number of components as the input. + The BMP format expands Y to RGB in the file format and does not + output alpha. + + PNG supports writing rectangles of data even when the bytes storing rows of + data are not consecutive in memory (e.g. sub-rectangles of a larger image), + by supplying the stride between the beginning of adjacent rows. The other + formats do not. (Thus you cannot write a native-format BMP through the BMP + writer, both because it is in BGR order and because it may have padding + at the end of the line.) + + HDR expects linear float data. Since the format is always 32-bit rgb(e) + data, alpha (if provided) is discarded, and for monochrome data it is + replicated across all three channels. + + TGA supports RLE or non-RLE compressed data. To use non-RLE-compressed + data, set the global variable 'stbi_write_tga_with_rle' to 0. + +CREDITS: + + PNG/BMP/TGA + Sean Barrett + HDR + Baldur Karlsson + TGA monochrome: + Jean-Sebastien Guay + misc enhancements: + Tim Kelsey + TGA RLE + Alan Hickman + initial file IO callback implementation + Emmanuel Julien + bugfixes: + github:Chribba + Guillaume Chereau + github:jry2 + github:romigrou + Sergio Gonzalez + Jonas Karlsson + Filip Wasil + Thatcher Ulrich + +LICENSE + +This software is dual-licensed to the public domain and under the following +license: you are granted a perpetual, irrevocable license to copy, modify, +publish, and distribute this file as you see fit. + +*/ + +#ifndef INCLUDE_STB_IMAGE_WRITE_H +#define INCLUDE_STB_IMAGE_WRITE_H + +#ifdef __cplusplus +extern "C" { +#endif + +#ifdef STB_IMAGE_WRITE_STATIC +#define STBIWDEF static +#else +#define STBIWDEF extern +extern int stbi_write_tga_with_rle; +#endif + +#ifndef STBI_WRITE_NO_STDIO +STBIWDEF int stbi_write_png(char const *filename, int w, int h, int comp, const void *data, int stride_in_bytes); +STBIWDEF int stbi_write_bmp(char const *filename, int w, int h, int comp, const void *data); +STBIWDEF int stbi_write_tga(char const *filename, int w, int h, int comp, const void *data); +STBIWDEF int stbi_write_hdr(char const *filename, int w, int h, int comp, const float *data); +#endif + +typedef void stbi_write_func(void *context, void *data, int size); + +STBIWDEF int stbi_write_png_to_func(stbi_write_func *func, void *context, int w, int h, int comp, const void *data, int stride_in_bytes); +STBIWDEF int stbi_write_bmp_to_func(stbi_write_func *func, void *context, int w, int h, int comp, const void *data); +STBIWDEF int stbi_write_tga_to_func(stbi_write_func *func, void *context, int w, int h, int comp, const void *data); +STBIWDEF int stbi_write_hdr_to_func(stbi_write_func *func, void *context, int w, int h, int comp, const float *data); + +#ifdef __cplusplus +} +#endif + +#endif//INCLUDE_STB_IMAGE_WRITE_H + +#ifdef STB_IMAGE_WRITE_IMPLEMENTATION + +#ifdef _WIN32 + #ifndef _CRT_SECURE_NO_WARNINGS + #define _CRT_SECURE_NO_WARNINGS + #endif + #ifndef _CRT_NONSTDC_NO_DEPRECATE + #define _CRT_NONSTDC_NO_DEPRECATE + #endif +#endif + +#ifndef STBI_WRITE_NO_STDIO +#include +#endif // STBI_WRITE_NO_STDIO + +#include +#include +#include +#include + +#if defined(STBIW_MALLOC) && defined(STBIW_FREE) && (defined(STBIW_REALLOC) || defined(STBIW_REALLOC_SIZED)) +// ok +#elif !defined(STBIW_MALLOC) && !defined(STBIW_FREE) && !defined(STBIW_REALLOC) && !defined(STBIW_REALLOC_SIZED) +// ok +#else +#error "Must define all or none of STBIW_MALLOC, STBIW_FREE, and STBIW_REALLOC (or STBIW_REALLOC_SIZED)." +#endif + +#ifndef STBIW_MALLOC +#define STBIW_MALLOC(sz) malloc(sz) +#define STBIW_REALLOC(p,newsz) realloc(p,newsz) +#define STBIW_FREE(p) free(p) +#endif + +#ifndef STBIW_REALLOC_SIZED +#define STBIW_REALLOC_SIZED(p,oldsz,newsz) STBIW_REALLOC(p,newsz) +#endif + + +#ifndef STBIW_MEMMOVE +#define STBIW_MEMMOVE(a,b,sz) memmove(a,b,sz) +#endif + + +#ifndef STBIW_ASSERT +#include +#define STBIW_ASSERT(x) assert(x) +#endif + +#define STBIW_UCHAR(x) (unsigned char) ((x) & 0xff) + +typedef struct +{ + stbi_write_func *func; + void *context; +} stbi__write_context; + +// initialize a callback-based context +static void stbi__start_write_callbacks(stbi__write_context *s, stbi_write_func *c, void *context) +{ + s->func = c; + s->context = context; +} + +#ifndef STBI_WRITE_NO_STDIO + +static void stbi__stdio_write(void *context, void *data, int size) +{ + fwrite(data,1,size,(FILE*) context); +} + +static int stbi__start_write_file(stbi__write_context *s, const char *filename) +{ + FILE *f = fopen(filename, "wb"); + stbi__start_write_callbacks(s, stbi__stdio_write, (void *) f); + return f != NULL; +} + +static void stbi__end_write_file(stbi__write_context *s) +{ + fclose((FILE *)s->context); +} + +#endif // !STBI_WRITE_NO_STDIO + +typedef unsigned int stbiw_uint32; +typedef int stb_image_write_test[sizeof(stbiw_uint32)==4 ? 1 : -1]; + +#ifdef STB_IMAGE_WRITE_STATIC +static int stbi_write_tga_with_rle = 1; +#else +int stbi_write_tga_with_rle = 1; +#endif + +static void stbiw__writefv(stbi__write_context *s, const char *fmt, va_list v) +{ + while (*fmt) { + switch (*fmt++) { + case ' ': break; + case '1': { unsigned char x = STBIW_UCHAR(va_arg(v, int)); + s->func(s->context,&x,1); + break; } + case '2': { int x = va_arg(v,int); + unsigned char b[2]; + b[0] = STBIW_UCHAR(x); + b[1] = STBIW_UCHAR(x>>8); + s->func(s->context,b,2); + break; } + case '4': { stbiw_uint32 x = va_arg(v,int); + unsigned char b[4]; + b[0]=STBIW_UCHAR(x); + b[1]=STBIW_UCHAR(x>>8); + b[2]=STBIW_UCHAR(x>>16); + b[3]=STBIW_UCHAR(x>>24); + s->func(s->context,b,4); + break; } + default: + STBIW_ASSERT(0); + return; + } + } +} + +static void stbiw__writef(stbi__write_context *s, const char *fmt, ...) +{ + va_list v; + va_start(v, fmt); + stbiw__writefv(s, fmt, v); + va_end(v); +} + +static void stbiw__write3(stbi__write_context *s, unsigned char a, unsigned char b, unsigned char c) +{ + unsigned char arr[3]; + arr[0] = a, arr[1] = b, arr[2] = c; + s->func(s->context, arr, 3); +} + +static void stbiw__write_pixel(stbi__write_context *s, int rgb_dir, int comp, int write_alpha, int expand_mono, unsigned char *d) +{ + unsigned char bg[3] = { 255, 0, 255}, px[3]; + int k; + + if (write_alpha < 0) + s->func(s->context, &d[comp - 1], 1); + + switch (comp) { + case 1: + s->func(s->context,d,1); + break; + case 2: + if (expand_mono) + stbiw__write3(s, d[0], d[0], d[0]); // monochrome bmp + else + s->func(s->context, d, 1); // monochrome TGA + break; + case 4: + if (!write_alpha) { + // composite against pink background + for (k = 0; k < 3; ++k) + px[k] = bg[k] + ((d[k] - bg[k]) * d[3]) / 255; + stbiw__write3(s, px[1 - rgb_dir], px[1], px[1 + rgb_dir]); + break; + } + /* FALLTHROUGH */ + case 3: + stbiw__write3(s, d[1 - rgb_dir], d[1], d[1 + rgb_dir]); + break; + } + if (write_alpha > 0) + s->func(s->context, &d[comp - 1], 1); +} + +static void stbiw__write_pixels(stbi__write_context *s, int rgb_dir, int vdir, int x, int y, int comp, void *data, int write_alpha, int scanline_pad, int expand_mono) +{ + stbiw_uint32 zero = 0; + int i,j, j_end; + + if (y <= 0) + return; + + if (vdir < 0) + j_end = -1, j = y-1; + else + j_end = y, j = 0; + + for (; j != j_end; j += vdir) { + for (i=0; i < x; ++i) { + unsigned char *d = (unsigned char *) data + (j*x+i)*comp; + stbiw__write_pixel(s, rgb_dir, comp, write_alpha, expand_mono, d); + } + s->func(s->context, &zero, scanline_pad); + } +} + +static int stbiw__outfile(stbi__write_context *s, int rgb_dir, int vdir, int x, int y, int comp, int expand_mono, void *data, int alpha, int pad, const char *fmt, ...) +{ + if (y < 0 || x < 0) { + return 0; + } else { + va_list v; + va_start(v, fmt); + stbiw__writefv(s, fmt, v); + va_end(v); + stbiw__write_pixels(s,rgb_dir,vdir,x,y,comp,data,alpha,pad, expand_mono); + return 1; + } +} + +static int stbi_write_bmp_core(stbi__write_context *s, int x, int y, int comp, const void *data) +{ + int pad = (-x*3) & 3; + return stbiw__outfile(s,-1,-1,x,y,comp,1,(void *) data,0,pad, + "11 4 22 4" "4 44 22 444444", + 'B', 'M', 14+40+(x*3+pad)*y, 0,0, 14+40, // file header + 40, x,y, 1,24, 0,0,0,0,0,0); // bitmap header +} + +STBIWDEF int stbi_write_bmp_to_func(stbi_write_func *func, void *context, int x, int y, int comp, const void *data) +{ + stbi__write_context s; + stbi__start_write_callbacks(&s, func, context); + return stbi_write_bmp_core(&s, x, y, comp, data); +} + +#ifndef STBI_WRITE_NO_STDIO +STBIWDEF int stbi_write_bmp(char const *filename, int x, int y, int comp, const void *data) +{ + stbi__write_context s; + if (stbi__start_write_file(&s,filename)) { + int r = stbi_write_bmp_core(&s, x, y, comp, data); + stbi__end_write_file(&s); + return r; + } else + return 0; +} +#endif //!STBI_WRITE_NO_STDIO + +static int stbi_write_tga_core(stbi__write_context *s, int x, int y, int comp, void *data) +{ + int has_alpha = (comp == 2 || comp == 4); + int colorbytes = has_alpha ? comp-1 : comp; + int format = colorbytes < 2 ? 3 : 2; // 3 color channels (RGB/RGBA) = 2, 1 color channel (Y/YA) = 3 + + if (y < 0 || x < 0) + return 0; + + if (!stbi_write_tga_with_rle) { + return stbiw__outfile(s, -1, -1, x, y, comp, 0, (void *) data, has_alpha, 0, + "111 221 2222 11", 0, 0, format, 0, 0, 0, 0, 0, x, y, (colorbytes + has_alpha) * 8, has_alpha * 8); + } else { + int i,j,k; + + stbiw__writef(s, "111 221 2222 11", 0,0,format+8, 0,0,0, 0,0,x,y, (colorbytes + has_alpha) * 8, has_alpha * 8); + + for (j = y - 1; j >= 0; --j) { + unsigned char *row = (unsigned char *) data + j * x * comp; + int len; + + for (i = 0; i < x; i += len) { + unsigned char *begin = row + i * comp; + int diff = 1; + len = 1; + + if (i < x - 1) { + ++len; + diff = memcmp(begin, row + (i + 1) * comp, comp); + if (diff) { + const unsigned char *prev = begin; + for (k = i + 2; k < x && len < 128; ++k) { + if (memcmp(prev, row + k * comp, comp)) { + prev += comp; + ++len; + } else { + --len; + break; + } + } + } else { + for (k = i + 2; k < x && len < 128; ++k) { + if (!memcmp(begin, row + k * comp, comp)) { + ++len; + } else { + break; + } + } + } + } + + if (diff) { + unsigned char header = STBIW_UCHAR(len - 1); + s->func(s->context, &header, 1); + for (k = 0; k < len; ++k) { + stbiw__write_pixel(s, -1, comp, has_alpha, 0, begin + k * comp); + } + } else { + unsigned char header = STBIW_UCHAR(len - 129); + s->func(s->context, &header, 1); + stbiw__write_pixel(s, -1, comp, has_alpha, 0, begin); + } + } + } + } + return 1; +} + +int stbi_write_tga_to_func(stbi_write_func *func, void *context, int x, int y, int comp, const void *data) +{ + stbi__write_context s; + stbi__start_write_callbacks(&s, func, context); + return stbi_write_tga_core(&s, x, y, comp, (void *) data); +} + +#ifndef STBI_WRITE_NO_STDIO +int stbi_write_tga(char const *filename, int x, int y, int comp, const void *data) +{ + stbi__write_context s; + if (stbi__start_write_file(&s,filename)) { + int r = stbi_write_tga_core(&s, x, y, comp, (void *) data); + stbi__end_write_file(&s); + return r; + } else + return 0; +} +#endif + +// ************************************************************************************************* +// Radiance RGBE HDR writer +// by Baldur Karlsson +#ifndef STBI_WRITE_NO_STDIO + +#define stbiw__max(a, b) ((a) > (b) ? (a) : (b)) + +void stbiw__linear_to_rgbe(unsigned char *rgbe, float *linear) +{ + int exponent; + float maxcomp = stbiw__max(linear[0], stbiw__max(linear[1], linear[2])); + + if (maxcomp < 1e-32f) { + rgbe[0] = rgbe[1] = rgbe[2] = rgbe[3] = 0; + } else { + float normalize = (float) frexp(maxcomp, &exponent) * 256.0f/maxcomp; + + rgbe[0] = (unsigned char)(linear[0] * normalize); + rgbe[1] = (unsigned char)(linear[1] * normalize); + rgbe[2] = (unsigned char)(linear[2] * normalize); + rgbe[3] = (unsigned char)(exponent + 128); + } +} + +void stbiw__write_run_data(stbi__write_context *s, int length, unsigned char databyte) +{ + unsigned char lengthbyte = STBIW_UCHAR(length+128); + STBIW_ASSERT(length+128 <= 255); + s->func(s->context, &lengthbyte, 1); + s->func(s->context, &databyte, 1); +} + +void stbiw__write_dump_data(stbi__write_context *s, int length, unsigned char *data) +{ + unsigned char lengthbyte = STBIW_UCHAR(length); + STBIW_ASSERT(length <= 128); // inconsistent with spec but consistent with official code + s->func(s->context, &lengthbyte, 1); + s->func(s->context, data, length); +} + +void stbiw__write_hdr_scanline(stbi__write_context *s, int width, int ncomp, unsigned char *scratch, float *scanline) +{ + unsigned char scanlineheader[4] = { 2, 2, 0, 0 }; + unsigned char rgbe[4]; + float linear[3]; + int x; + + scanlineheader[2] = (width&0xff00)>>8; + scanlineheader[3] = (width&0x00ff); + + /* skip RLE for images too small or large */ + if (width < 8 || width >= 32768) { + for (x=0; x < width; x++) { + switch (ncomp) { + case 4: /* fallthrough */ + case 3: linear[2] = scanline[x*ncomp + 2]; + linear[1] = scanline[x*ncomp + 1]; + linear[0] = scanline[x*ncomp + 0]; + break; + default: + linear[0] = linear[1] = linear[2] = scanline[x*ncomp + 0]; + break; + } + stbiw__linear_to_rgbe(rgbe, linear); + s->func(s->context, rgbe, 4); + } + } else { + int c,r; + /* encode into scratch buffer */ + for (x=0; x < width; x++) { + switch(ncomp) { + case 4: /* fallthrough */ + case 3: linear[2] = scanline[x*ncomp + 2]; + linear[1] = scanline[x*ncomp + 1]; + linear[0] = scanline[x*ncomp + 0]; + break; + default: + linear[0] = linear[1] = linear[2] = scanline[x*ncomp + 0]; + break; + } + stbiw__linear_to_rgbe(rgbe, linear); + scratch[x + width*0] = rgbe[0]; + scratch[x + width*1] = rgbe[1]; + scratch[x + width*2] = rgbe[2]; + scratch[x + width*3] = rgbe[3]; + } + + s->func(s->context, scanlineheader, 4); + + /* RLE each component separately */ + for (c=0; c < 4; c++) { + unsigned char *comp = &scratch[width*c]; + + x = 0; + while (x < width) { + // find first run + r = x; + while (r+2 < width) { + if (comp[r] == comp[r+1] && comp[r] == comp[r+2]) + break; + ++r; + } + if (r+2 >= width) + r = width; + // dump up to first run + while (x < r) { + int len = r-x; + if (len > 128) len = 128; + stbiw__write_dump_data(s, len, &comp[x]); + x += len; + } + // if there's a run, output it + if (r+2 < width) { // same test as what we break out of in search loop, so only true if we break'd + // find next byte after run + while (r < width && comp[r] == comp[x]) + ++r; + // output run up to r + while (x < r) { + int len = r-x; + if (len > 127) len = 127; + stbiw__write_run_data(s, len, comp[x]); + x += len; + } + } + } + } + } +} + +static int stbi_write_hdr_core(stbi__write_context *s, int x, int y, int comp, float *data) +{ + if (y <= 0 || x <= 0 || data == NULL) + return 0; + else { + // Each component is stored separately. Allocate scratch space for full output scanline. + unsigned char *scratch = (unsigned char *) STBIW_MALLOC(x*4); + int i, len; + char buffer[128]; + char header[] = "#?RADIANCE\n# Written by stb_image_write.h\nFORMAT=32-bit_rle_rgbe\n"; + s->func(s->context, header, sizeof(header)-1); + + len = sprintf(buffer, "EXPOSURE= 1.0000000000000\n\n-Y %d +X %d\n", y, x); + s->func(s->context, buffer, len); + + for(i=0; i < y; i++) + stbiw__write_hdr_scanline(s, x, comp, scratch, data + comp*i*x); + STBIW_FREE(scratch); + return 1; + } +} + +int stbi_write_hdr_to_func(stbi_write_func *func, void *context, int x, int y, int comp, const float *data) +{ + stbi__write_context s; + stbi__start_write_callbacks(&s, func, context); + return stbi_write_hdr_core(&s, x, y, comp, (float *) data); +} + +int stbi_write_hdr(char const *filename, int x, int y, int comp, const float *data) +{ + stbi__write_context s; + if (stbi__start_write_file(&s,filename)) { + int r = stbi_write_hdr_core(&s, x, y, comp, (float *) data); + stbi__end_write_file(&s); + return r; + } else + return 0; +} +#endif // STBI_WRITE_NO_STDIO + + +////////////////////////////////////////////////////////////////////////////// +// +// PNG writer +// + +// stretchy buffer; stbiw__sbpush() == vector<>::push_back() -- stbiw__sbcount() == vector<>::size() +#define stbiw__sbraw(a) ((int *) (a) - 2) +#define stbiw__sbm(a) stbiw__sbraw(a)[0] +#define stbiw__sbn(a) stbiw__sbraw(a)[1] + +#define stbiw__sbneedgrow(a,n) ((a)==0 || stbiw__sbn(a)+n >= stbiw__sbm(a)) +#define stbiw__sbmaybegrow(a,n) (stbiw__sbneedgrow(a,(n)) ? stbiw__sbgrow(a,n) : 0) +#define stbiw__sbgrow(a,n) stbiw__sbgrowf((void **) &(a), (n), sizeof(*(a))) + +#define stbiw__sbpush(a, v) (stbiw__sbmaybegrow(a,1), (a)[stbiw__sbn(a)++] = (v)) +#define stbiw__sbcount(a) ((a) ? stbiw__sbn(a) : 0) +#define stbiw__sbfree(a) ((a) ? STBIW_FREE(stbiw__sbraw(a)),0 : 0) + +static void *stbiw__sbgrowf(void **arr, int increment, int itemsize) +{ + int m = *arr ? 2*stbiw__sbm(*arr)+increment : increment+1; + void *p = STBIW_REALLOC_SIZED(*arr ? stbiw__sbraw(*arr) : 0, *arr ? (stbiw__sbm(*arr)*itemsize + sizeof(int)*2) : 0, itemsize * m + sizeof(int)*2); + STBIW_ASSERT(p); + if (p) { + if (!*arr) ((int *) p)[1] = 0; + *arr = (void *) ((int *) p + 2); + stbiw__sbm(*arr) = m; + } + return *arr; +} + +static unsigned char *stbiw__zlib_flushf(unsigned char *data, unsigned int *bitbuffer, int *bitcount) +{ + while (*bitcount >= 8) { + stbiw__sbpush(data, STBIW_UCHAR(*bitbuffer)); + *bitbuffer >>= 8; + *bitcount -= 8; + } + return data; +} + +static int stbiw__zlib_bitrev(int code, int codebits) +{ + int res=0; + while (codebits--) { + res = (res << 1) | (code & 1); + code >>= 1; + } + return res; +} + +static unsigned int stbiw__zlib_countm(unsigned char *a, unsigned char *b, int limit) +{ + int i; + for (i=0; i < limit && i < 258; ++i) + if (a[i] != b[i]) break; + return i; +} + +static unsigned int stbiw__zhash(unsigned char *data) +{ + stbiw_uint32 hash = data[0] + (data[1] << 8) + (data[2] << 16); + hash ^= hash << 3; + hash += hash >> 5; + hash ^= hash << 4; + hash += hash >> 17; + hash ^= hash << 25; + hash += hash >> 6; + return hash; +} + +#define stbiw__zlib_flush() (out = stbiw__zlib_flushf(out, &bitbuf, &bitcount)) +#define stbiw__zlib_add(code,codebits) \ + (bitbuf |= (code) << bitcount, bitcount += (codebits), stbiw__zlib_flush()) +#define stbiw__zlib_huffa(b,c) stbiw__zlib_add(stbiw__zlib_bitrev(b,c),c) +// default huffman tables +#define stbiw__zlib_huff1(n) stbiw__zlib_huffa(0x30 + (n), 8) +#define stbiw__zlib_huff2(n) stbiw__zlib_huffa(0x190 + (n)-144, 9) +#define stbiw__zlib_huff3(n) stbiw__zlib_huffa(0 + (n)-256,7) +#define stbiw__zlib_huff4(n) stbiw__zlib_huffa(0xc0 + (n)-280,8) +#define stbiw__zlib_huff(n) ((n) <= 143 ? stbiw__zlib_huff1(n) : (n) <= 255 ? stbiw__zlib_huff2(n) : (n) <= 279 ? stbiw__zlib_huff3(n) : stbiw__zlib_huff4(n)) +#define stbiw__zlib_huffb(n) ((n) <= 143 ? stbiw__zlib_huff1(n) : stbiw__zlib_huff2(n)) + +#define stbiw__ZHASH 16384 + +unsigned char * stbi_zlib_compress(unsigned char *data, int data_len, int *out_len, int quality) +{ + static unsigned short lengthc[] = { 3,4,5,6,7,8,9,10,11,13,15,17,19,23,27,31,35,43,51,59,67,83,99,115,131,163,195,227,258, 259 }; + static unsigned char lengtheb[]= { 0,0,0,0,0,0,0, 0, 1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 5, 0 }; + static unsigned short distc[] = { 1,2,3,4,5,7,9,13,17,25,33,49,65,97,129,193,257,385,513,769,1025,1537,2049,3073,4097,6145,8193,12289,16385,24577, 32768 }; + static unsigned char disteb[] = { 0,0,0,0,1,1,2,2,3,3,4,4,5,5,6,6,7,7,8,8,9,9,10,10,11,11,12,12,13,13 }; + unsigned int bitbuf=0; + int i,j, bitcount=0; + unsigned char *out = NULL; + unsigned char ***hash_table = (unsigned char***) STBIW_MALLOC(stbiw__ZHASH * sizeof(char**)); + if (quality < 5) quality = 5; + + stbiw__sbpush(out, 0x78); // DEFLATE 32K window + stbiw__sbpush(out, 0x5e); // FLEVEL = 1 + stbiw__zlib_add(1,1); // BFINAL = 1 + stbiw__zlib_add(1,2); // BTYPE = 1 -- fixed huffman + + for (i=0; i < stbiw__ZHASH; ++i) + hash_table[i] = NULL; + + i=0; + while (i < data_len-3) { + // hash next 3 bytes of data to be compressed + int h = stbiw__zhash(data+i)&(stbiw__ZHASH-1), best=3; + unsigned char *bestloc = 0; + unsigned char **hlist = hash_table[h]; + int n = stbiw__sbcount(hlist); + for (j=0; j < n; ++j) { + if (hlist[j]-data > i-32768) { // if entry lies within window + int d = stbiw__zlib_countm(hlist[j], data+i, data_len-i); + if (d >= best) best=d,bestloc=hlist[j]; + } + } + // when hash table entry is too long, delete half the entries + if (hash_table[h] && stbiw__sbn(hash_table[h]) == 2*quality) { + STBIW_MEMMOVE(hash_table[h], hash_table[h]+quality, sizeof(hash_table[h][0])*quality); + stbiw__sbn(hash_table[h]) = quality; + } + stbiw__sbpush(hash_table[h],data+i); + + if (bestloc) { + // "lazy matching" - check match at *next* byte, and if it's better, do cur byte as literal + h = stbiw__zhash(data+i+1)&(stbiw__ZHASH-1); + hlist = hash_table[h]; + n = stbiw__sbcount(hlist); + for (j=0; j < n; ++j) { + if (hlist[j]-data > i-32767) { + int e = stbiw__zlib_countm(hlist[j], data+i+1, data_len-i-1); + if (e > best) { // if next match is better, bail on current match + bestloc = NULL; + break; + } + } + } + } + + if (bestloc) { + int d = (int) (data+i - bestloc); // distance back + STBIW_ASSERT(d <= 32767 && best <= 258); + for (j=0; best > lengthc[j+1]-1; ++j); + stbiw__zlib_huff(j+257); + if (lengtheb[j]) stbiw__zlib_add(best - lengthc[j], lengtheb[j]); + for (j=0; d > distc[j+1]-1; ++j); + stbiw__zlib_add(stbiw__zlib_bitrev(j,5),5); + if (disteb[j]) stbiw__zlib_add(d - distc[j], disteb[j]); + i += best; + } else { + stbiw__zlib_huffb(data[i]); + ++i; + } + } + // write out final bytes + for (;i < data_len; ++i) + stbiw__zlib_huffb(data[i]); + stbiw__zlib_huff(256); // end of block + // pad with 0 bits to byte boundary + while (bitcount) + stbiw__zlib_add(0,1); + + for (i=0; i < stbiw__ZHASH; ++i) + (void) stbiw__sbfree(hash_table[i]); + STBIW_FREE(hash_table); + + { + // compute adler32 on input + unsigned int s1=1, s2=0; + int blocklen = (int) (data_len % 5552); + j=0; + while (j < data_len) { + for (i=0; i < blocklen; ++i) s1 += data[j+i], s2 += s1; + s1 %= 65521, s2 %= 65521; + j += blocklen; + blocklen = 5552; + } + stbiw__sbpush(out, STBIW_UCHAR(s2 >> 8)); + stbiw__sbpush(out, STBIW_UCHAR(s2)); + stbiw__sbpush(out, STBIW_UCHAR(s1 >> 8)); + stbiw__sbpush(out, STBIW_UCHAR(s1)); + } + *out_len = stbiw__sbn(out); + // make returned pointer freeable + STBIW_MEMMOVE(stbiw__sbraw(out), out, *out_len); + return (unsigned char *) stbiw__sbraw(out); +} + +static unsigned int stbiw__crc32(unsigned char *buffer, int len) +{ + static unsigned int crc_table[256] = + { + 0x00000000, 0x77073096, 0xEE0E612C, 0x990951BA, 0x076DC419, 0x706AF48F, 0xE963A535, 0x9E6495A3, + 0x0eDB8832, 0x79DCB8A4, 0xE0D5E91E, 0x97D2D988, 0x09B64C2B, 0x7EB17CBD, 0xE7B82D07, 0x90BF1D91, + 0x1DB71064, 0x6AB020F2, 0xF3B97148, 0x84BE41DE, 0x1ADAD47D, 0x6DDDE4EB, 0xF4D4B551, 0x83D385C7, + 0x136C9856, 0x646BA8C0, 0xFD62F97A, 0x8A65C9EC, 0x14015C4F, 0x63066CD9, 0xFA0F3D63, 0x8D080DF5, + 0x3B6E20C8, 0x4C69105E, 0xD56041E4, 0xA2677172, 0x3C03E4D1, 0x4B04D447, 0xD20D85FD, 0xA50AB56B, + 0x35B5A8FA, 0x42B2986C, 0xDBBBC9D6, 0xACBCF940, 0x32D86CE3, 0x45DF5C75, 0xDCD60DCF, 0xABD13D59, + 0x26D930AC, 0x51DE003A, 0xC8D75180, 0xBFD06116, 0x21B4F4B5, 0x56B3C423, 0xCFBA9599, 0xB8BDA50F, + 0x2802B89E, 0x5F058808, 0xC60CD9B2, 0xB10BE924, 0x2F6F7C87, 0x58684C11, 0xC1611DAB, 0xB6662D3D, + 0x76DC4190, 0x01DB7106, 0x98D220BC, 0xEFD5102A, 0x71B18589, 0x06B6B51F, 0x9FBFE4A5, 0xE8B8D433, + 0x7807C9A2, 0x0F00F934, 0x9609A88E, 0xE10E9818, 0x7F6A0DBB, 0x086D3D2D, 0x91646C97, 0xE6635C01, + 0x6B6B51F4, 0x1C6C6162, 0x856530D8, 0xF262004E, 0x6C0695ED, 0x1B01A57B, 0x8208F4C1, 0xF50FC457, + 0x65B0D9C6, 0x12B7E950, 0x8BBEB8EA, 0xFCB9887C, 0x62DD1DDF, 0x15DA2D49, 0x8CD37CF3, 0xFBD44C65, + 0x4DB26158, 0x3AB551CE, 0xA3BC0074, 0xD4BB30E2, 0x4ADFA541, 0x3DD895D7, 0xA4D1C46D, 0xD3D6F4FB, + 0x4369E96A, 0x346ED9FC, 0xAD678846, 0xDA60B8D0, 0x44042D73, 0x33031DE5, 0xAA0A4C5F, 0xDD0D7CC9, + 0x5005713C, 0x270241AA, 0xBE0B1010, 0xC90C2086, 0x5768B525, 0x206F85B3, 0xB966D409, 0xCE61E49F, + 0x5EDEF90E, 0x29D9C998, 0xB0D09822, 0xC7D7A8B4, 0x59B33D17, 0x2EB40D81, 0xB7BD5C3B, 0xC0BA6CAD, + 0xEDB88320, 0x9ABFB3B6, 0x03B6E20C, 0x74B1D29A, 0xEAD54739, 0x9DD277AF, 0x04DB2615, 0x73DC1683, + 0xE3630B12, 0x94643B84, 0x0D6D6A3E, 0x7A6A5AA8, 0xE40ECF0B, 0x9309FF9D, 0x0A00AE27, 0x7D079EB1, + 0xF00F9344, 0x8708A3D2, 0x1E01F268, 0x6906C2FE, 0xF762575D, 0x806567CB, 0x196C3671, 0x6E6B06E7, + 0xFED41B76, 0x89D32BE0, 0x10DA7A5A, 0x67DD4ACC, 0xF9B9DF6F, 0x8EBEEFF9, 0x17B7BE43, 0x60B08ED5, + 0xD6D6A3E8, 0xA1D1937E, 0x38D8C2C4, 0x4FDFF252, 0xD1BB67F1, 0xA6BC5767, 0x3FB506DD, 0x48B2364B, + 0xD80D2BDA, 0xAF0A1B4C, 0x36034AF6, 0x41047A60, 0xDF60EFC3, 0xA867DF55, 0x316E8EEF, 0x4669BE79, + 0xCB61B38C, 0xBC66831A, 0x256FD2A0, 0x5268E236, 0xCC0C7795, 0xBB0B4703, 0x220216B9, 0x5505262F, + 0xC5BA3BBE, 0xB2BD0B28, 0x2BB45A92, 0x5CB36A04, 0xC2D7FFA7, 0xB5D0CF31, 0x2CD99E8B, 0x5BDEAE1D, + 0x9B64C2B0, 0xEC63F226, 0x756AA39C, 0x026D930A, 0x9C0906A9, 0xEB0E363F, 0x72076785, 0x05005713, + 0x95BF4A82, 0xE2B87A14, 0x7BB12BAE, 0x0CB61B38, 0x92D28E9B, 0xE5D5BE0D, 0x7CDCEFB7, 0x0BDBDF21, + 0x86D3D2D4, 0xF1D4E242, 0x68DDB3F8, 0x1FDA836E, 0x81BE16CD, 0xF6B9265B, 0x6FB077E1, 0x18B74777, + 0x88085AE6, 0xFF0F6A70, 0x66063BCA, 0x11010B5C, 0x8F659EFF, 0xF862AE69, 0x616BFFD3, 0x166CCF45, + 0xA00AE278, 0xD70DD2EE, 0x4E048354, 0x3903B3C2, 0xA7672661, 0xD06016F7, 0x4969474D, 0x3E6E77DB, + 0xAED16A4A, 0xD9D65ADC, 0x40DF0B66, 0x37D83BF0, 0xA9BCAE53, 0xDEBB9EC5, 0x47B2CF7F, 0x30B5FFE9, + 0xBDBDF21C, 0xCABAC28A, 0x53B39330, 0x24B4A3A6, 0xBAD03605, 0xCDD70693, 0x54DE5729, 0x23D967BF, + 0xB3667A2E, 0xC4614AB8, 0x5D681B02, 0x2A6F2B94, 0xB40BBE37, 0xC30C8EA1, 0x5A05DF1B, 0x2D02EF8D + }; + + unsigned int crc = ~0u; + int i; + for (i=0; i < len; ++i) + crc = (crc >> 8) ^ crc_table[buffer[i] ^ (crc & 0xff)]; + return ~crc; +} + +#define stbiw__wpng4(o,a,b,c,d) ((o)[0]=STBIW_UCHAR(a),(o)[1]=STBIW_UCHAR(b),(o)[2]=STBIW_UCHAR(c),(o)[3]=STBIW_UCHAR(d),(o)+=4) +#define stbiw__wp32(data,v) stbiw__wpng4(data, (v)>>24,(v)>>16,(v)>>8,(v)); +#define stbiw__wptag(data,s) stbiw__wpng4(data, s[0],s[1],s[2],s[3]) + +static void stbiw__wpcrc(unsigned char **data, int len) +{ + unsigned int crc = stbiw__crc32(*data - len - 4, len+4); + stbiw__wp32(*data, crc); +} + +static unsigned char stbiw__paeth(int a, int b, int c) +{ + int p = a + b - c, pa = abs(p-a), pb = abs(p-b), pc = abs(p-c); + if (pa <= pb && pa <= pc) return STBIW_UCHAR(a); + if (pb <= pc) return STBIW_UCHAR(b); + return STBIW_UCHAR(c); +} + +unsigned char *stbi_write_png_to_mem(unsigned char *pixels, int stride_bytes, int x, int y, int n, int *out_len) +{ + int ctype[5] = { -1, 0, 4, 2, 6 }; + unsigned char sig[8] = { 137,80,78,71,13,10,26,10 }; + unsigned char *out,*o, *filt, *zlib; + signed char *line_buffer; + int i,j,k,p,zlen; + + if (stride_bytes == 0) + stride_bytes = x * n; + + filt = (unsigned char *) STBIW_MALLOC((x*n+1) * y); if (!filt) return 0; + line_buffer = (signed char *) STBIW_MALLOC(x * n); if (!line_buffer) { STBIW_FREE(filt); return 0; } + for (j=0; j < y; ++j) { + static int mapping[] = { 0,1,2,3,4 }; + static int firstmap[] = { 0,1,0,5,6 }; + int *mymap = j ? mapping : firstmap; + int best = 0, bestval = 0x7fffffff; + for (p=0; p < 2; ++p) { + for (k= p?best:0; k < 5; ++k) { + int type = mymap[k],est=0; + unsigned char *z = pixels + stride_bytes*j; + for (i=0; i < n; ++i) + switch (type) { + case 0: line_buffer[i] = z[i]; break; + case 1: line_buffer[i] = z[i]; break; + case 2: line_buffer[i] = z[i] - z[i-stride_bytes]; break; + case 3: line_buffer[i] = z[i] - (z[i-stride_bytes]>>1); break; + case 4: line_buffer[i] = (signed char) (z[i] - stbiw__paeth(0,z[i-stride_bytes],0)); break; + case 5: line_buffer[i] = z[i]; break; + case 6: line_buffer[i] = z[i]; break; + } + for (i=n; i < x*n; ++i) { + switch (type) { + case 0: line_buffer[i] = z[i]; break; + case 1: line_buffer[i] = z[i] - z[i-n]; break; + case 2: line_buffer[i] = z[i] - z[i-stride_bytes]; break; + case 3: line_buffer[i] = z[i] - ((z[i-n] + z[i-stride_bytes])>>1); break; + case 4: line_buffer[i] = z[i] - stbiw__paeth(z[i-n], z[i-stride_bytes], z[i-stride_bytes-n]); break; + case 5: line_buffer[i] = z[i] - (z[i-n]>>1); break; + case 6: line_buffer[i] = z[i] - stbiw__paeth(z[i-n], 0,0); break; + } + } + if (p) break; + for (i=0; i < x*n; ++i) + est += abs((signed char) line_buffer[i]); + if (est < bestval) { bestval = est; best = k; } + } + } + // when we get here, best contains the filter type, and line_buffer contains the data + filt[j*(x*n+1)] = (unsigned char) best; + STBIW_MEMMOVE(filt+j*(x*n+1)+1, line_buffer, x*n); + } + STBIW_FREE(line_buffer); + zlib = stbi_zlib_compress(filt, y*( x*n+1), &zlen, 8); // increase 8 to get smaller but use more memory + STBIW_FREE(filt); + if (!zlib) return 0; + + // each tag requires 12 bytes of overhead + out = (unsigned char *) STBIW_MALLOC(8 + 12+13 + 12+zlen + 12); + if (!out) return 0; + *out_len = 8 + 12+13 + 12+zlen + 12; + + o=out; + STBIW_MEMMOVE(o,sig,8); o+= 8; + stbiw__wp32(o, 13); // header length + stbiw__wptag(o, "IHDR"); + stbiw__wp32(o, x); + stbiw__wp32(o, y); + *o++ = 8; + *o++ = STBIW_UCHAR(ctype[n]); + *o++ = 0; + *o++ = 0; + *o++ = 0; + stbiw__wpcrc(&o,13); + + stbiw__wp32(o, zlen); + stbiw__wptag(o, "IDAT"); + STBIW_MEMMOVE(o, zlib, zlen); + o += zlen; + STBIW_FREE(zlib); + stbiw__wpcrc(&o, zlen); + + stbiw__wp32(o,0); + stbiw__wptag(o, "IEND"); + stbiw__wpcrc(&o,0); + + STBIW_ASSERT(o == out + *out_len); + + return out; +} + +#ifndef STBI_WRITE_NO_STDIO +STBIWDEF int stbi_write_png(char const *filename, int x, int y, int comp, const void *data, int stride_bytes) +{ + FILE *f; + int len; + unsigned char *png = stbi_write_png_to_mem((unsigned char *) data, stride_bytes, x, y, comp, &len); + if (png == NULL) return 0; + f = fopen(filename, "wb"); + if (!f) { STBIW_FREE(png); return 0; } + fwrite(png, 1, len, f); + fclose(f); + STBIW_FREE(png); + return 1; +} +#endif + +STBIWDEF int stbi_write_png_to_func(stbi_write_func *func, void *context, int x, int y, int comp, const void *data, int stride_bytes) +{ + int len; + unsigned char *png = stbi_write_png_to_mem((unsigned char *) data, stride_bytes, x, y, comp, &len); + if (png == NULL) return 0; + func(context, png, len); + STBIW_FREE(png); + return 1; +} + +#endif // STB_IMAGE_WRITE_IMPLEMENTATION + +/* Revision history + 1.02 (2016-04-02) + avoid allocating large structures on the stack + 1.01 (2016-01-16) + STBIW_REALLOC_SIZED: support allocators with no realloc support + avoid race-condition in crc initialization + minor compile issues + 1.00 (2015-09-14) + installable file IO function + 0.99 (2015-09-13) + warning fixes; TGA rle support + 0.98 (2015-04-08) + added STBIW_MALLOC, STBIW_ASSERT etc + 0.97 (2015-01-18) + fixed HDR asserts, rewrote HDR rle logic + 0.96 (2015-01-17) + add HDR output + fix monochrome BMP + 0.95 (2014-08-17) + add monochrome TGA output + 0.94 (2014-05-31) + rename private functions to avoid conflicts with stb_image.h + 0.93 (2014-05-27) + warning fixes + 0.92 (2010-08-01) + casts to unsigned char to fix warnings + 0.91 (2010-07-17) + first public release + 0.90 first internal release +*/ From b8c71e7f2d363be2176eab6379d5e6f5cd97260e Mon Sep 17 00:00:00 2001 From: Camilla Berglund Date: Sun, 6 Nov 2016 16:34:11 +0100 Subject: [PATCH 37/40] Add offscreen rendering example --- examples/CMakeLists.txt | 5 ++ examples/offscreen.c | 171 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 176 insertions(+) create mode 100644 examples/offscreen.c diff --git a/examples/CMakeLists.txt b/examples/CMakeLists.txt index 138a78166..fe946f5c3 100644 --- a/examples/CMakeLists.txt +++ b/examples/CMakeLists.txt @@ -11,6 +11,10 @@ if (MSVC) add_definitions(-D_CRT_SECURE_NO_WARNINGS) endif() +if (GLFW_USE_OSMESA) + add_definitions(-DUSE_NATIVE_OSMESA) +endif() + include_directories("${GLFW_SOURCE_DIR}/deps") if (WIN32) @@ -31,6 +35,7 @@ set(TINYCTHREAD "${GLFW_SOURCE_DIR}/deps/tinycthread.h" add_executable(boing WIN32 MACOSX_BUNDLE boing.c ${ICON} ${GLAD}) add_executable(gears WIN32 MACOSX_BUNDLE gears.c ${ICON} ${GLAD}) add_executable(heightmap WIN32 MACOSX_BUNDLE heightmap.c ${ICON} ${GLAD}) +add_executable(offscreen offscreen.c ${ICON} ${GLAD}) add_executable(particles WIN32 MACOSX_BUNDLE particles.c ${ICON} ${TINYCTHREAD} ${GETOPT} ${GLAD}) add_executable(simple WIN32 MACOSX_BUNDLE simple.c ${ICON} ${GLAD}) add_executable(splitview WIN32 MACOSX_BUNDLE splitview.c ${ICON} ${GLAD}) diff --git a/examples/offscreen.c b/examples/offscreen.c new file mode 100644 index 000000000..68ac8dbf6 --- /dev/null +++ b/examples/offscreen.c @@ -0,0 +1,171 @@ +//======================================================================== +// Offscreen rendering example +// Copyright (c) Camilla Berglund +// +// This software is provided 'as-is', without any express or implied +// warranty. In no event will the authors be held liable for any damages +// arising from the use of this software. +// +// Permission is granted to anyone to use this software for any purpose, +// including commercial applications, and to alter it and redistribute it +// freely, subject to the following restrictions: +// +// 1. The origin of this software must not be misrepresented; you must not +// claim that you wrote the original software. If you use this software +// in a product, an acknowledgment in the product documentation would +// be appreciated but is not required. +// +// 2. Altered source versions must be plainly marked as such, and must not +// be misrepresented as being the original software. +// +// 3. This notice may not be removed or altered from any source +// distribution. +// +//======================================================================== + +#include +#include + +#if USE_NATIVE_OSMESA + #define GLFW_EXPOSE_NATIVE_OSMESA + #include +#endif + +#include "linmath.h" + +#include +#include + +#define STB_IMAGE_WRITE_IMPLEMENTATION +#include + +static const struct +{ + float x, y; + float r, g, b; +} vertices[3] = +{ + { -0.6f, -0.4f, 1.f, 0.f, 0.f }, + { 0.6f, -0.4f, 0.f, 1.f, 0.f }, + { 0.f, 0.6f, 0.f, 0.f, 1.f } +}; + +static const char* vertex_shader_text = +"uniform mat4 MVP;\n" +"attribute vec3 vCol;\n" +"attribute vec2 vPos;\n" +"varying vec3 color;\n" +"void main()\n" +"{\n" +" gl_Position = MVP * vec4(vPos, 0.0, 1.0);\n" +" color = vCol;\n" +"}\n"; + +static const char* fragment_shader_text = +"varying vec3 color;\n" +"void main()\n" +"{\n" +" gl_FragColor = vec4(color, 1.0);\n" +"}\n"; + +static void error_callback(int error, const char* description) +{ + fprintf(stderr, "Error: %s\n", description); +} + +int main(void) +{ + GLFWwindow* window; + GLuint vertex_buffer, vertex_shader, fragment_shader, program; + GLint mvp_location, vpos_location, vcol_location; + float ratio; + int width, height; + mat4x4 mvp; + char* buffer; + + glfwSetErrorCallback(error_callback); + + if (!glfwInit()) + exit(EXIT_FAILURE); + + glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 2); + glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 0); + glfwWindowHint(GLFW_VISIBLE, GLFW_FALSE); + + window = glfwCreateWindow(640, 480, "Simple example", NULL, NULL); + if (!window) + { + glfwTerminate(); + exit(EXIT_FAILURE); + } + + glfwMakeContextCurrent(window); + gladLoadGLLoader((GLADloadproc) glfwGetProcAddress); + + // NOTE: OpenGL error checks have been omitted for brevity + + glGenBuffers(1, &vertex_buffer); + glBindBuffer(GL_ARRAY_BUFFER, vertex_buffer); + glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW); + + vertex_shader = glCreateShader(GL_VERTEX_SHADER); + glShaderSource(vertex_shader, 1, &vertex_shader_text, NULL); + glCompileShader(vertex_shader); + + fragment_shader = glCreateShader(GL_FRAGMENT_SHADER); + glShaderSource(fragment_shader, 1, &fragment_shader_text, NULL); + glCompileShader(fragment_shader); + + program = glCreateProgram(); + glAttachShader(program, vertex_shader); + glAttachShader(program, fragment_shader); + glLinkProgram(program); + + mvp_location = glGetUniformLocation(program, "MVP"); + vpos_location = glGetAttribLocation(program, "vPos"); + vcol_location = glGetAttribLocation(program, "vCol"); + + glEnableVertexAttribArray(vpos_location); + glVertexAttribPointer(vpos_location, 2, GL_FLOAT, GL_FALSE, + sizeof(vertices[0]), (void*) 0); + glEnableVertexAttribArray(vcol_location); + glVertexAttribPointer(vcol_location, 3, GL_FLOAT, GL_FALSE, + sizeof(vertices[0]), (void*) (sizeof(float) * 2)); + + glfwGetFramebufferSize(window, &width, &height); + ratio = width / (float) height; + + glViewport(0, 0, width, height); + glClear(GL_COLOR_BUFFER_BIT); + + mat4x4_ortho(mvp, -ratio, ratio, -1.f, 1.f, 1.f, -1.f); + + glUseProgram(program); + glUniformMatrix4fv(mvp_location, 1, GL_FALSE, (const GLfloat*) mvp); + glDrawArrays(GL_TRIANGLES, 0, 3); + +#if USE_NATIVE_OSMESA + glfwGetOSMesaColorBuffer(window, &width, &height, NULL, (void**) &buffer); +#else + buffer = calloc(4, width * height); + glReadPixels(0, 0, width, height, GL_RGBA, GL_UNSIGNED_BYTE, buffer); +#endif + + // Write image Y-flipped because OpenGL + stbi_write_png("offscreen.png", + width, height, 4, + buffer + (width * 4 * (height - 1)), + -width * 4); + +#if USE_NATIVE_OSMESA + // Here is where there's nothing +#else + free(buffer); +#endif + + glfwDestroyWindow(window); + + glfwTerminate(); + exit(EXIT_SUCCESS); +} + From 62012e3c68ea1e39aaf50045c863f0b90c29065f Mon Sep 17 00:00:00 2001 From: Camilla Berglund Date: Tue, 8 Nov 2016 22:44:59 +0100 Subject: [PATCH 38/40] OSMesa: Add fallback to OSMesaCreateContextExt --- src/osmesa_context.c | 72 ++++++++++++++++++++++++++++++-------------- src/osmesa_context.h | 3 ++ 2 files changed, 52 insertions(+), 23 deletions(-) diff --git a/src/osmesa_context.c b/src/osmesa_context.c index 65fb6e927..852d4e541 100644 --- a/src/osmesa_context.c +++ b/src/osmesa_context.c @@ -120,6 +120,7 @@ GLFWbool _glfwInitOSMesa(void) #elif defined(__CYGWIN__) "libOSMesa-8.so", #else + "libOSMesa.so.8", "libOSMesa.so.6", #endif NULL @@ -141,6 +142,8 @@ GLFWbool _glfwInitOSMesa(void) return GLFW_FALSE; } + _glfw.osmesa.CreateContextExt = (PFNOSMESACREATECONTEXTEXTPROC) + _glfw_dlsym(_glfw.osmesa.handle, "OSMesaCreateContextExt"); _glfw.osmesa.CreateContextAttribs = (PFNOSMESACREATECONTEXTATTRIBSPROC) _glfw_dlsym(_glfw.osmesa.handle, "OSMesaCreateContextAttribs"); _glfw.osmesa.DestroyContext = (PFNOSMESADESTROYCONTEXTPROC) @@ -154,7 +157,7 @@ GLFWbool _glfwInitOSMesa(void) _glfw.osmesa.GetProcAddress = (PFNOSMESAGETPROCADDRESSPROC) _glfw_dlsym(_glfw.osmesa.handle, "OSMesaGetProcAddress"); - if (!_glfw.osmesa.CreateContextAttribs || + if (!_glfw.osmesa.CreateContextExt || !_glfw.osmesa.DestroyContext || !_glfw.osmesa.MakeCurrent || !_glfw.osmesa.GetColorBuffer || @@ -192,37 +195,60 @@ GLFWbool _glfwCreateContextOSMesa(_GLFWwindow* window, const _GLFWfbconfig* fbconfig) { OSMesaContext share = NULL; - int index = 0, attribs[40]; + const int accumBits = fbconfig->accumRedBits + + fbconfig->accumGreenBits + + fbconfig->accumBlueBits + + fbconfig->accumAlphaBits; if (ctxconfig->share) share = ctxconfig->share->context.osmesa.handle; - setAttrib(OSMESA_FORMAT, OSMESA_RGBA); - setAttrib(OSMESA_DEPTH_BITS, fbconfig->depthBits); - setAttrib(OSMESA_STENCIL_BITS, fbconfig->stencilBits); - setAttrib(OSMESA_ACCUM_BITS, fbconfig->accumRedBits + - fbconfig->accumGreenBits + - fbconfig->accumBlueBits + - fbconfig->accumAlphaBits); + if (OSMesaCreateContextAttribs) + { + int index = 0, attribs[40]; - if (ctxconfig->profile == GLFW_OPENGL_CORE_PROFILE) - { - setAttrib(OSMESA_PROFILE, OSMESA_CORE_PROFILE); + setAttrib(OSMESA_FORMAT, OSMESA_RGBA); + setAttrib(OSMESA_DEPTH_BITS, fbconfig->depthBits); + setAttrib(OSMESA_STENCIL_BITS, fbconfig->stencilBits); + setAttrib(OSMESA_ACCUM_BITS, accumBits); + + if (ctxconfig->profile == GLFW_OPENGL_CORE_PROFILE) + { + setAttrib(OSMESA_PROFILE, OSMESA_CORE_PROFILE); + } + else if (ctxconfig->profile == GLFW_OPENGL_COMPAT_PROFILE) + { + setAttrib(OSMESA_PROFILE, OSMESA_COMPAT_PROFILE); + } + + if (ctxconfig->major != 1 || ctxconfig->minor != 0) + { + setAttrib(OSMESA_CONTEXT_MAJOR_VERSION, ctxconfig->major); + setAttrib(OSMESA_CONTEXT_MINOR_VERSION, ctxconfig->minor); + } + + setAttrib(0, 0); + + window->context.osmesa.handle = + OSMesaCreateContextAttribs(attribs, share); } - else if (ctxconfig->profile == GLFW_OPENGL_COMPAT_PROFILE) + else { - setAttrib(OSMESA_PROFILE, OSMESA_COMPAT_PROFILE); + if (ctxconfig->profile) + { + _glfwInputError(GLFW_VERSION_UNAVAILABLE, + "OSMesa: OpenGL profiles unavailable"); + return GLFW_FALSE; + } + + window->context.osmesa.handle = + OSMesaCreateContextExt(OSMESA_RGBA, + fbconfig->depthBits, + fbconfig->stencilBits, + accumBits, + share); } - if (ctxconfig->major != 1 || ctxconfig->minor != 0) - { - setAttrib(OSMESA_CONTEXT_MAJOR_VERSION, ctxconfig->major); - setAttrib(OSMESA_CONTEXT_MINOR_VERSION, ctxconfig->minor); - } - - setAttrib(0, 0); - - window->context.osmesa.handle = OSMesaCreateContextAttribs(attribs, share); if (window->context.osmesa.handle == NULL) { _glfwInputError(GLFW_VERSION_UNAVAILABLE, diff --git a/src/osmesa_context.h b/src/osmesa_context.h index 2dbac9dd2..5b49b054d 100644 --- a/src/osmesa_context.h +++ b/src/osmesa_context.h @@ -43,11 +43,13 @@ typedef void* OSMesaContext; typedef void (*OSMESAproc)(); typedef OSMesaContext (* PFNOSMESACREATECONTEXTATTRIBSPROC)(const int*,OSMesaContext); +typedef OSMesaContext (* PFNOSMESACREATECONTEXTEXTPROC)(GLenum,GLint,GLint,GLint,OSMesaContext); typedef void (* PFNOSMESADESTROYCONTEXTPROC)(OSMesaContext); typedef int (* PFNOSMESAMAKECURRENTPROC)(OSMesaContext,void*,int,int,int); typedef int (* PFNOSMESAGETCOLORBUFFERPROC)(OSMesaContext,int*,int*,int*,void**); typedef int (* PFNOSMESAGETDEPTHBUFFERPROC)(OSMesaContext,int*,int*,int*,void**); typedef GLFWglproc (* PFNOSMESAGETPROCADDRESSPROC)(const char*); +#define OSMesaCreateContextExt _glfw.osmesa.CreateContextExt #define OSMesaCreateContextAttribs _glfw.osmesa.CreateContextAttribs #define OSMesaDestroyContext _glfw.osmesa.DestroyContext #define OSMesaMakeCurrent _glfw.osmesa.MakeCurrent @@ -76,6 +78,7 @@ typedef struct _GLFWlibraryOSMesa { void* handle; + PFNOSMESACREATECONTEXTEXTPROC CreateContextExt; PFNOSMESACREATECONTEXTATTRIBSPROC CreateContextAttribs; PFNOSMESADESTROYCONTEXTPROC DestroyContext; PFNOSMESAMAKECURRENTPROC MakeCurrent; From 0e8d129efbd304361617aa9fff10316e707253d7 Mon Sep 17 00:00:00 2001 From: Camilla Berglund Date: Tue, 8 Nov 2016 23:43:45 +0100 Subject: [PATCH 39/40] OSMesa: Context creation compliance fixes --- src/osmesa_context.c | 14 ++++++++++++++ src/osmesa_window.c | 17 ++++++++++++----- 2 files changed, 26 insertions(+), 5 deletions(-) diff --git a/src/osmesa_context.c b/src/osmesa_context.c index 852d4e541..4f5450739 100644 --- a/src/osmesa_context.c +++ b/src/osmesa_context.c @@ -200,6 +200,13 @@ GLFWbool _glfwCreateContextOSMesa(_GLFWwindow* window, fbconfig->accumBlueBits + fbconfig->accumAlphaBits; + if (ctxconfig->client == GLFW_OPENGL_ES_API) + { + _glfwInputError(GLFW_API_UNAVAILABLE, + "OSMesa: OpenGL ES is not available on OSMesa"); + return GLFW_FALSE; + } + if (ctxconfig->share) share = ctxconfig->share->context.osmesa.handle; @@ -227,6 +234,13 @@ GLFWbool _glfwCreateContextOSMesa(_GLFWwindow* window, setAttrib(OSMESA_CONTEXT_MINOR_VERSION, ctxconfig->minor); } + if (ctxconfig->forward) + { + _glfwInputError(GLFW_VERSION_UNAVAILABLE, + "OSMesa: Foward-compatible contexts not supported"); + return GLFW_FALSE; + } + setAttrib(0, 0); window->context.osmesa.handle = diff --git a/src/osmesa_window.c b/src/osmesa_window.c index 56923ec78..c010be53d 100644 --- a/src/osmesa_window.c +++ b/src/osmesa_window.c @@ -47,18 +47,25 @@ int _glfwPlatformCreateWindow(_GLFWwindow* window, const _GLFWctxconfig* ctxconfig, const _GLFWfbconfig* fbconfig) { - if (!_glfwInitOSMesa()) + if (!createNativeWindow(window, wndconfig)) return GLFW_FALSE; if (ctxconfig->client != GLFW_NO_API) { - if (!_glfwCreateContextOSMesa(window, ctxconfig, fbconfig)) + if (ctxconfig->source == GLFW_NATIVE_CONTEXT_API) + { + if (!_glfwInitOSMesa()) + return GLFW_FALSE; + if (!_glfwCreateContextOSMesa(window, ctxconfig, fbconfig)) + return GLFW_FALSE; + } + else + { + _glfwInputError(GLFW_API_UNAVAILABLE, "OSMesa: EGL not available"); return GLFW_FALSE; + } } - if (!createNativeWindow(window, wndconfig)) - return GLFW_FALSE; - return GLFW_TRUE; } From 5b8051581e2873fdac5bbbb9b8a10460fb6c3965 Mon Sep 17 00:00:00 2001 From: Camilla Berglund Date: Wed, 9 Nov 2016 00:23:19 +0100 Subject: [PATCH 40/40] OSMesa: Allow cursor creation --- src/osmesa_window.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/osmesa_window.c b/src/osmesa_window.c index c010be53d..38acdb216 100644 --- a/src/osmesa_window.c +++ b/src/osmesa_window.c @@ -226,12 +226,12 @@ int _glfwPlatformCreateCursor(_GLFWcursor* cursor, const GLFWimage* image, int xhot, int yhot) { - return GLFW_FALSE; + return GLFW_TRUE; } int _glfwPlatformCreateStandardCursor(_GLFWcursor* cursor, int shape) { - return GLFW_FALSE; + return GLFW_TRUE; } void _glfwPlatformDestroyCursor(_GLFWcursor* cursor)