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/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 0a67af3a0..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() @@ -129,26 +133,36 @@ 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 #-------------------------------------------------------------------- -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") - 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() @@ -158,7 +172,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") @@ -306,6 +323,14 @@ 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_LIBRARIES "${CMAKE_THREAD_LIBS_INIT}") +endif() + #-------------------------------------------------------------------- # Use Cocoa for window creation and NSOpenGL for context creation #-------------------------------------------------------------------- @@ -323,19 +348,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") diff --git a/README.md b/README.md index dc480f0b8..bc2a74aca 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. @@ -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 @@ -104,10 +105,17 @@ 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 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] 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) @@ -149,6 +157,7 @@ skills. - Lambert Clara - Andrew Corrigan - Noel Cower + - Jason Daly - Jarrod Davis - Olivier Delannoy - Paul R. Deppe @@ -160,6 +169,7 @@ skills. - Siavash Eliasi - Michael Fogleman - Gerald Franz + - Mário Freitas - GeO4d - Marcus Geelnard - Eloi Marín Gratacós 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 +*/ 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/docs/Doxyfile.in b/docs/Doxyfile.in index b046f898b..eb11421ad 100644 --- a/docs/Doxyfile.in +++ b/docs/Doxyfile.in @@ -202,8 +202,9 @@ 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:__" + "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 @@ -1576,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/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..c54bdd91e 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 @@ -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 OS X, -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..2b21732a0 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. @@ -96,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 @@ -201,7 +213,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. @@ -248,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/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/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/docs/news.dox b/docs/news.dox index 1e92eb4fa..b0de0daa1 100644 --- a/docs/news.dox +++ b/docs/news.dox @@ -16,6 +16,18 @@ 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. + + +@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 @@ -293,11 +305,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/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/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/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); +} + diff --git a/include/GLFW/glfw3.h b/include/GLFW/glfw3.h index 3e23968ef..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) @@ -116,7 +122,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 @@ -176,7 +182,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) @@ -561,7 +571,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. */ @@ -1128,7 +1138,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 @@ -1247,7 +1257,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). @@ -1621,6 +1631,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 +1655,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 +1690,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. * @@ -1765,8 +1784,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 @@ -1812,19 +1831,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 @@ -1841,6 +1860,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. @@ -1936,7 +1969,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. @@ -1973,12 +2006,16 @@ 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/) * 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 +2043,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 +2077,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 +2152,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 +2195,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 +2234,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 +2306,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 +2334,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 +2464,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 +2517,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 +2535,16 @@ 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. + * + * @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 +2649,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 @@ -2629,8 +2706,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. * @@ -2649,9 +2726,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 +2790,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 +3285,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 @@ -3389,7 +3472,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. @@ -3570,6 +3653,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 @@ -3584,7 +3669,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 +3683,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 +3694,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 +3717,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 +3728,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 +3752,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 +3764,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 +3784,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. * @@ -3735,6 +3820,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 +3850,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 @@ -4141,11 +4230,14 @@ 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. * + * @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. @@ -4226,6 +4318,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. * @@ -4270,11 +4366,17 @@ 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. * + * @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/include/GLFW/glfw3native.h b/include/GLFW/glfw3native.h index 056bc8292..42b50d95d 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 @@ -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 /************************************************************************* @@ -448,6 +452,62 @@ GLFWAPI EGLContext glfwGetEGLContext(GLFWwindow* window); GLFWAPI EGLSurface glfwGetEGLSurface(GLFWwindow* window); #endif +#if defined(GLFW_EXPOSE_NATIVE_OSMESA) +/*! @brief Retrieves the color buffer associated with the specified window. + * + * @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); + +/*! @brief Retrieves the depth buffer associated with the specified window. + * + * @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); + +/*! @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 } #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/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_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/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..fcacf661e 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 // @@ -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_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 57032b56c..41c7dbd5c 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 // @@ -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 // @@ -63,14 +80,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; @@ -144,25 +162,25 @@ 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) { 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; } -// Translates a OS X keycode to a GLFW keycode +// Translates a macOS keycode to a GLFW keycode // static int translateKey(unsigned int key) { @@ -180,16 +198,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; @@ -413,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); @@ -565,7 +593,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 +788,7 @@ static const NSRange kEmptyRange = { NSNotFound, 0 }; - (BOOL)canBecomeKeyWindow { - // Required for NSBorderlessWindowMask windows + // Required for NSWindowStyleMaskBorderless windows return YES; } @@ -781,8 +809,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 +897,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 +929,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. @@ -1011,11 +1042,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; @@ -1162,7 +1193,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)]; } @@ -1358,7 +1389,7 @@ void _glfwPlatformPollEvents(void) { for (;;) { - NSEvent* event = [NSApp nextEventMatchingMask:NSAnyEventMask + NSEvent* event = [NSApp nextEventMatchingMask:NSEventMaskAny untilDate:[NSDate distantPast] inMode:NSDefaultRunLoopMode dequeue:YES]; @@ -1377,7 +1408,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 +1420,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 +1433,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 @@ -1632,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, @@ -1646,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/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/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..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 @@ -110,6 +111,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; @@ -171,6 +173,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 @@ -458,6 +462,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; @@ -608,22 +614,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 +967,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); //======================================================================== @@ -1060,7 +1066,7 @@ GLFWbool _glfwIsPrintable(int key); /*! @ingroup utility */ -GLFWbool _glfwInitVulkan(void); +GLFWbool _glfwInitVulkan(int mode); /*! @ingroup utility */ 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/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; } 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 454a77391..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) @@ -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); diff --git a/src/osmesa_context.c b/src/osmesa_context.c new file mode 100644 index 000000000..4f5450739 --- /dev/null +++ b/src/osmesa_context.c @@ -0,0 +1,367 @@ +//======================================================================== +// 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" + + +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(window->context.osmesa.buffer); + + // Allocate the new buffer (width * height * 8-bit RGBA) + window->context.osmesa.buffer = + calloc(4, window->osmesa.width * window->osmesa.height); + + window->context.osmesa.width = window->osmesa.width; + window->context.osmesa.height = window->osmesa.height; + } + + if (!OSMesaMakeCurrent(window->context.osmesa.handle, + window->context.osmesa.buffer, + GL_UNSIGNED_BYTE, + window->osmesa.width, window->osmesa.height)) + { + _glfwInputError(GLFW_PLATFORM_ERROR, + "OSMesa: Failed to make context current"); + return; + } + } + + _glfwPlatformSetCurrentContext(window); +} + +static GLFWglproc getProcAddressOSMesa(const char* procname) +{ + return (GLFWglproc) OSMesaGetProcAddress(procname); +} + +static void destroyContextOSMesa(_GLFWwindow* window) +{ + if (window->context.osmesa.handle) + { + OSMesaDestroyContext(window->context.osmesa.handle); + window->context.osmesa.handle = NULL; + } + + if (window->context.osmesa.buffer) + { + free(window->context.osmesa.buffer); + window->context.osmesa.width = 0; + window->context.osmesa.height = 0; + } +} + +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 ////// +////////////////////////////////////////////////////////////////////////// + +GLFWbool _glfwInitOSMesa(void) +{ + int i; + const char* sonames[] = + { +#if defined(_WIN32) + "libOSMesa.dll", + "OSMesa.dll", +#elif defined(__APPLE__) + "libOSMesa.8.dylib", +#elif defined(__CYGWIN__) + "libOSMesa-8.so", +#else + "libOSMesa.so.8", + "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.CreateContextExt = (PFNOSMESACREATECONTEXTEXTPROC) + _glfw_dlsym(_glfw.osmesa.handle, "OSMesaCreateContextExt"); + _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.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.CreateContextExt || + !_glfw.osmesa.DestroyContext || + !_glfw.osmesa.MakeCurrent || + !_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; + } +} + +#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 = NULL; + const int accumBits = fbconfig->accumRedBits + + fbconfig->accumGreenBits + + 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; + + if (OSMesaCreateContextAttribs) + { + int index = 0, attribs[40]; + + 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); + } + + if (ctxconfig->forward) + { + _glfwInputError(GLFW_VERSION_UNAVAILABLE, + "OSMesa: Foward-compatible contexts not supported"); + return GLFW_FALSE; + } + + setAttrib(0, 0); + + window->context.osmesa.handle = + OSMesaCreateContextAttribs(attribs, share); + } + else + { + 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 (window->context.osmesa.handle == NULL) + { + _glfwInputError(GLFW_VERSION_UNAVAILABLE, + "OSMesa: Failed to create context"); + return GLFW_FALSE; + } + + 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; +} + +#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 new file mode 100644 index 000000000..5b49b054d --- /dev/null +++ b/src/osmesa_context.h @@ -0,0 +1,98 @@ +//======================================================================== +// 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_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 (* 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 +#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 _GLFWlibraryOSMesa osmesa + + +// OSMesa-specific per-context data +// +typedef struct _GLFWcontextOSMesa +{ + OSMesaContext handle; + int width; + int height; + void* buffer; + +} _GLFWcontextOSMesa; + +// OSMesa-specific global data +// +typedef struct _GLFWlibraryOSMesa +{ + void* handle; + + PFNOSMESACREATECONTEXTEXTPROC CreateContextExt; + PFNOSMESACREATECONTEXTATTRIBSPROC CreateContextAttribs; + PFNOSMESADESTROYCONTEXTPROC DestroyContext; + PFNOSMESAMAKECURRENTPROC MakeCurrent; + PFNOSMESAGETCOLORBUFFERPROC GetColorBuffer; + PFNOSMESAGETDEPTHBUFFERPROC GetDepthBuffer; + PFNOSMESAGETPROCADDRESSPROC GetProcAddress; + +} _GLFWlibraryOSMesa; + + +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..834f9ab2f --- /dev/null +++ b/src/osmesa_init.c @@ -0,0 +1,54 @@ +//======================================================================== +// 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 platform API ////// +////////////////////////////////////////////////////////////////////////// + +int _glfwPlatformInit(void) +{ + if (!_glfwInitThreadLocalStoragePOSIX()) + return GLFW_FALSE; + + _glfwInitTimerPOSIX(); + return GLFW_TRUE; +} + +void _glfwPlatformTerminate(void) +{ + _glfwTerminateOSMesa(); + _glfwTerminateThreadLocalStoragePOSIX(); +} + +const char* _glfwPlatformGetVersionString(void) +{ + return _GLFW_VERSION_NUMBER " none OSMesa"; +} + diff --git a/src/osmesa_monitor.c b/src/osmesa_monitor.c new file mode 100644 index 000000000..64222a92d --- /dev/null +++ b/src/osmesa_monitor.c @@ -0,0 +1,67 @@ +//======================================================================== +// 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 platform API ////// +////////////////////////////////////////////////////////////////////////// + +_GLFWmonitor** _glfwPlatformGetMonitors(int* count) +{ + // OSMesa is headless, so no monitors + *count = 0; + return NULL; +} + +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) +{ +} + diff --git a/src/osmesa_platform.h b/src/osmesa_platform.h new file mode 100644 index 000000000..74c7f6a79 --- /dev/null +++ b/src/osmesa_platform.h @@ -0,0 +1,65 @@ +//======================================================================== +// 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_platform_h_ +#define _glfw3_osmesa_platform_h_ + +#include + +#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 + +#include "osmesa_context.h" +#include "posix_time.h" +#include "posix_tls.h" + +#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; +} _GLFWwindowOSMesa; + + +#endif // _glfw3_osmesa_platform_h_ diff --git a/src/osmesa_window.c b/src/osmesa_window.c new file mode 100644 index 000000000..38acdb216 --- /dev/null +++ b/src/osmesa_window.c @@ -0,0 +1,303 @@ +//======================================================================== +// 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" + + +static int createNativeWindow(_GLFWwindow* window, + const _GLFWwndconfig* wndconfig) +{ + window->osmesa.width = wndconfig->width; + window->osmesa.height = wndconfig->height; + + return GLFW_TRUE; +} + + +////////////////////////////////////////////////////////////////////////// +////// GLFW platform API ////// +////////////////////////////////////////////////////////////////////////// + +int _glfwPlatformCreateWindow(_GLFWwindow* window, + const _GLFWwndconfig* wndconfig, + const _GLFWctxconfig* ctxconfig, + const _GLFWfbconfig* fbconfig) +{ + if (!createNativeWindow(window, wndconfig)) + return GLFW_FALSE; + + if (ctxconfig->client != GLFW_NO_API) + { + 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; + } + } + + 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) +{ +} + +void _glfwPlatformSetWindowPos(_GLFWwindow* window, int xpos, int ypos) +{ +} + +void _glfwPlatformGetWindowSize(_GLFWwindow* window, int* width, int* height) +{ + if (width) + *width = window->osmesa.width; + if (height) + *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) + *width = window->osmesa.width; + if (height) + *height = window->osmesa.height; +} + +void _glfwPlatformGetWindowFrameSize(_GLFWwindow* window, + int* left, int* top, + int* right, int* bottom) +{ + 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 GLFW_FALSE; +} + +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) +{ +} + +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_TRUE; +} + +int _glfwPlatformCreateStandardCursor(_GLFWcursor* cursor, int shape) +{ + return GLFW_TRUE; +} + +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 _glfwPlatformGetKeyScancode(int key) +{ + return -1; +} + +int _glfwPlatformJoystickPresent(int joy) +{ + return GLFW_FALSE; +} + +const float* _glfwPlatformGetJoystickAxes(int joy, int* count) +{ + return NULL; +} + +const unsigned char* _glfwPlatformGetJoystickButtons(int joy, int* count) +{ + return NULL; +} + +const char* _glfwPlatformGetJoystickName(int joy) +{ + return NULL; +} + +void _glfwPlatformGetRequiredInstanceExtensions(char** extensions) +{ +} + +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; +} + diff --git a/src/vulkan.c b/src/vulkan.c index 30f4b8741..1123bb22c 100644 --- a/src/vulkan.c +++ b/src/vulkan.c @@ -31,37 +31,54 @@ #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; 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) + _glfwInputError(GLFW_API_UNAVAILABLE, "Vulkan: Loader not found"); + return GLFW_FALSE; + } _glfw.vk.GetInstanceProcAddr = (PFN_vkGetInstanceProcAddr) _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; @@ -111,6 +128,9 @@ GLFWbool _glfwInitVulkan(void) #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; @@ -136,8 +156,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) @@ -203,7 +225,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) @@ -212,11 +234,11 @@ 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; + + if (!_glfw.vk.extensions[0]) return NULL; - } *count = 2; return (const char**) _glfw.vk.extensions; @@ -229,15 +251,20 @@ 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) + if (!proc) + { + if (strcmp(procname, "vkGetInstanceProcAddr") == 0) + return (GLFWvkproc) vkGetInstanceProcAddr; + } +#else if (!proc) proc = (GLFWvkproc) _glfw_dlsym(_glfw.vk.handle, procname); +#endif return proc; } @@ -248,11 +275,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]) { @@ -279,11 +303,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]) { 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(); diff --git a/src/win32_joystick.c b/src/win32_joystick.c index 49f3b871d..e3e9ec6d0 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)) @@ -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); @@ -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; } @@ -664,7 +664,7 @@ void _glfwInitJoysticksWin32(void) NULL))) { _glfwInputError(GLFW_PLATFORM_ERROR, - "DI: Failed to create interface"); + "Win32: Failed to create interface"); } } @@ -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/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; } diff --git a/src/wl_init.c b/src/wl_init.c index a4be0e8ce..e61f72f70 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; @@ -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) { @@ -186,10 +189,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,23 +208,52 @@ static void keyboardHandleKeymap(void* data, { _glfwInputError(GLFW_PLATFORM_ERROR, "Wayland: Failed to create XKB state"); - xkb_map_unref(keymap); + xkb_keymap_unref(keymap); 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; _glfw.wl.xkb.state = state; - _glfw.wl.xkb.control_mask = - 1 << xkb_map_mod_get_index(_glfw.wl.xkb.keymap, "Control"); - _glfw.wl.xkb.alt_mask = - 1 << xkb_map_mod_get_index(_glfw.wl.xkb.keymap, "Mod1"); - _glfw.wl.xkb.shift_mask = - 1 << xkb_map_mod_get_index(_glfw.wl.xkb.keymap, "Shift"); - _glfw.wl.xkb.super_mask = - 1 << xkb_map_mod_get_index(_glfw.wl.xkb.keymap, "Mod4"); + _glfw.wl.xkb.controlMask = + 1 << xkb_keymap_mod_get_index(_glfw.wl.xkb.keymap, "Control"); + _glfw.wl.xkb.altMask = + 1 << xkb_keymap_mod_get_index(_glfw.wl.xkb.keymap, "Mod1"); + _glfw.wl.xkb.shiftMask = + 1 << xkb_keymap_mod_get_index(_glfw.wl.xkb.keymap, "Shift"); + _glfw.wl.xkb.superMask = + 1 << xkb_keymap_mod_get_index(_glfw.wl.xkb.keymap, "Mod4"); } static void keyboardHandleEnter(void* data, @@ -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, num_syms; + uint32_t code, numSyms; long cp; const xkb_keysym_t *syms; + xkb_keysym_t sym; code = key + 8; - num_syms = xkb_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]); + sym = composeSymbol(syms[0]); + cp = _glfwKeySym2Unicode(sym); if (cp != -1) { const int mods = _glfw.wl.xkb.modifiers; @@ -327,15 +381,17 @@ static void keyboardHandleModifiers(void* data, group); mask = xkb_state_serialize_mods(_glfw.wl.xkb.state, - XKB_STATE_DEPRESSED | - XKB_STATE_LATCHED); - if (mask & _glfw.wl.xkb.control_mask) + XKB_STATE_MODS_DEPRESSED | + XKB_STATE_LAYOUT_DEPRESSED | + XKB_STATE_MODS_LATCHED | + XKB_STATE_LAYOUT_LATCHED); + 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; } @@ -387,10 +443,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) { @@ -643,16 +699,38 @@ 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); + if (_glfw.wl.cursorTheme) 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) diff --git a/src/wl_monitor.c b/src/wl_monitor.c index d6a5a3f46..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, @@ -102,7 +112,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 +128,6 @@ void _glfwAddOutputWayland(uint32_t name, uint32_t version) { _GLFWmonitor *monitor; struct wl_output *output; - char name_str[80]; - - memset(name_str, 0, sizeof(name_str)); - snprintf(name_str, 79, "wl_output@%u", name); if (version < 2) { @@ -130,7 +136,8 @@ void _glfwAddOutputWayland(uint32_t name, uint32_t version) return; } - monitor = _glfwAllocMonitor(name_str, 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, @@ -148,7 +155,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..209f7bca0 100644 --- a/src/wl_platform.h +++ b/src/wl_platform.h @@ -29,6 +29,7 @@ #include #include +#include #include typedef VkFlags VkWaylandSurfaceCreateFlagsKHR; @@ -83,7 +84,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 +120,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 +137,11 @@ 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; + struct xkb_compose_state* composeState; + 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); } 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() 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/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); 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(); 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); 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); } 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; }