summaryrefslogtreecommitdiff
path: root/libs/glfw-3.3.8/tests
diff options
context:
space:
mode:
authorsanine <sanine.not@pm.me>2022-08-21 20:27:25 -0500
committersanine <sanine.not@pm.me>2022-08-21 20:27:25 -0500
commit5a1275f661f2d879411411a1376b790f8aec7947 (patch)
treee552e2290883d3c6a40c4c123fed1ff5bdedeff9 /libs/glfw-3.3.8/tests
parentced4c513947132cae08c6627fd2030b4345eb39d (diff)
add glfw library
Diffstat (limited to 'libs/glfw-3.3.8/tests')
-rw-r--r--libs/glfw-3.3.8/tests/CMakeLists.txt99
-rw-r--r--libs/glfw-3.3.8/tests/clipboard.c145
-rw-r--r--libs/glfw-3.3.8/tests/cursor.c493
-rw-r--r--libs/glfw-3.3.8/tests/empty.c132
-rw-r--r--libs/glfw-3.3.8/tests/events.c650
-rw-r--r--libs/glfw-3.3.8/tests/gamma.c185
-rw-r--r--libs/glfw-3.3.8/tests/glfwinfo.c943
-rw-r--r--libs/glfw-3.3.8/tests/icon.c149
-rw-r--r--libs/glfw-3.3.8/tests/iconify.c297
-rw-r--r--libs/glfw-3.3.8/tests/inputlag.c308
-rw-r--r--libs/glfw-3.3.8/tests/joysticks.c344
-rw-r--r--libs/glfw-3.3.8/tests/monitors.c260
-rw-r--r--libs/glfw-3.3.8/tests/msaa.c220
-rw-r--r--libs/glfw-3.3.8/tests/opacity.c108
-rw-r--r--libs/glfw-3.3.8/tests/reopen.c240
-rw-r--r--libs/glfw-3.3.8/tests/tearing.c250
-rw-r--r--libs/glfw-3.3.8/tests/threads.c152
-rw-r--r--libs/glfw-3.3.8/tests/timeout.c98
-rw-r--r--libs/glfw-3.3.8/tests/title.c72
-rw-r--r--libs/glfw-3.3.8/tests/triangle-vulkan.c2139
-rw-r--r--libs/glfw-3.3.8/tests/windows.c174
21 files changed, 7458 insertions, 0 deletions
diff --git a/libs/glfw-3.3.8/tests/CMakeLists.txt b/libs/glfw-3.3.8/tests/CMakeLists.txt
new file mode 100644
index 0000000..91374b2
--- /dev/null
+++ b/libs/glfw-3.3.8/tests/CMakeLists.txt
@@ -0,0 +1,99 @@
+
+link_libraries(glfw)
+
+include_directories("${GLFW_SOURCE_DIR}/deps")
+
+if (MATH_LIBRARY)
+ link_libraries("${MATH_LIBRARY}")
+endif()
+
+# Workaround for the MS CRT deprecating parts of the standard library
+if (MSVC OR CMAKE_C_SIMULATE_ID STREQUAL "MSVC")
+ add_definitions(-D_CRT_SECURE_NO_WARNINGS)
+endif()
+
+set(GLAD_GL "${GLFW_SOURCE_DIR}/deps/glad/gl.h"
+ "${GLFW_SOURCE_DIR}/deps/glad_gl.c")
+set(GLAD_VULKAN "${GLFW_SOURCE_DIR}/deps/glad/vulkan.h"
+ "${GLFW_SOURCE_DIR}/deps/glad_vulkan.c")
+set(GETOPT "${GLFW_SOURCE_DIR}/deps/getopt.h"
+ "${GLFW_SOURCE_DIR}/deps/getopt.c")
+set(TINYCTHREAD "${GLFW_SOURCE_DIR}/deps/tinycthread.h"
+ "${GLFW_SOURCE_DIR}/deps/tinycthread.c")
+
+if (${CMAKE_VERSION} VERSION_EQUAL "3.1.0" OR
+ ${CMAKE_VERSION} VERSION_GREATER "3.1.0")
+ set(CMAKE_C_STANDARD 99)
+else()
+ # Remove this fallback when removing support for CMake version less than 3.1
+ add_compile_options("$<$<C_COMPILER_ID:AppleClang>:-std=c99>"
+ "$<$<C_COMPILER_ID:Clang>:-std=c99>"
+ "$<$<C_COMPILER_ID:GNU>:-std=c99>")
+
+endif()
+
+add_executable(clipboard clipboard.c ${GETOPT} ${GLAD_GL})
+add_executable(events events.c ${GETOPT} ${GLAD_GL})
+add_executable(msaa msaa.c ${GETOPT} ${GLAD_GL})
+add_executable(glfwinfo glfwinfo.c ${GETOPT} ${GLAD_GL} ${GLAD_VULKAN})
+add_executable(iconify iconify.c ${GETOPT} ${GLAD_GL})
+add_executable(monitors monitors.c ${GETOPT} ${GLAD_GL})
+add_executable(reopen reopen.c ${GLAD_GL})
+add_executable(cursor cursor.c ${GLAD_GL})
+
+add_executable(empty WIN32 MACOSX_BUNDLE empty.c ${TINYCTHREAD} ${GLAD_GL})
+add_executable(gamma WIN32 MACOSX_BUNDLE gamma.c ${GLAD_GL})
+add_executable(icon WIN32 MACOSX_BUNDLE icon.c ${GLAD_GL})
+add_executable(inputlag WIN32 MACOSX_BUNDLE inputlag.c ${GETOPT} ${GLAD_GL})
+add_executable(joysticks WIN32 MACOSX_BUNDLE joysticks.c ${GLAD_GL})
+add_executable(opacity WIN32 MACOSX_BUNDLE opacity.c ${GLAD_GL})
+add_executable(tearing WIN32 MACOSX_BUNDLE tearing.c ${GLAD_GL})
+add_executable(threads WIN32 MACOSX_BUNDLE threads.c ${TINYCTHREAD} ${GLAD_GL})
+add_executable(timeout WIN32 MACOSX_BUNDLE timeout.c ${GLAD_GL})
+add_executable(title WIN32 MACOSX_BUNDLE title.c ${GLAD_GL})
+add_executable(triangle-vulkan WIN32 triangle-vulkan.c ${GLAD_VULKAN})
+add_executable(windows WIN32 MACOSX_BUNDLE windows.c ${GETOPT} ${GLAD_GL})
+
+target_link_libraries(empty "${CMAKE_THREAD_LIBS_INIT}")
+target_link_libraries(threads "${CMAKE_THREAD_LIBS_INIT}")
+if (RT_LIBRARY)
+ target_link_libraries(empty "${RT_LIBRARY}")
+ target_link_libraries(threads "${RT_LIBRARY}")
+endif()
+
+set(GUI_ONLY_BINARIES empty gamma icon inputlag joysticks opacity tearing
+ threads timeout title triangle-vulkan windows)
+set(CONSOLE_BINARIES clipboard events msaa glfwinfo iconify monitors reopen
+ cursor)
+
+set_target_properties(${GUI_ONLY_BINARIES} ${CONSOLE_BINARIES} PROPERTIES
+ FOLDER "GLFW3/Tests")
+
+if (MSVC)
+ # Tell MSVC to use main instead of WinMain
+ set_target_properties(${GUI_ONLY_BINARIES} PROPERTIES
+ LINK_FLAGS "/ENTRY:mainCRTStartup")
+elseif (CMAKE_C_SIMULATE_ID STREQUAL "MSVC")
+ # Tell Clang using MS CRT to use main instead of WinMain
+ set_target_properties(${GUI_ONLY_BINARIES} PROPERTIES
+ LINK_FLAGS "-Wl,/entry:mainCRTStartup")
+endif()
+
+if (APPLE)
+ set_target_properties(empty PROPERTIES MACOSX_BUNDLE_BUNDLE_NAME "Empty Event")
+ set_target_properties(gamma PROPERTIES MACOSX_BUNDLE_BUNDLE_NAME "Gamma")
+ set_target_properties(inputlag PROPERTIES MACOSX_BUNDLE_BUNDLE_NAME "Input Lag")
+ set_target_properties(joysticks PROPERTIES MACOSX_BUNDLE_BUNDLE_NAME "Joysticks")
+ set_target_properties(opacity PROPERTIES MACOSX_BUNDLE_BUNDLE_NAME "Opacity")
+ set_target_properties(tearing PROPERTIES MACOSX_BUNDLE_BUNDLE_NAME "Tearing")
+ set_target_properties(threads PROPERTIES MACOSX_BUNDLE_BUNDLE_NAME "Threads")
+ set_target_properties(timeout PROPERTIES MACOSX_BUNDLE_BUNDLE_NAME "Timeout")
+ set_target_properties(title PROPERTIES MACOSX_BUNDLE_BUNDLE_NAME "Title")
+ set_target_properties(windows PROPERTIES MACOSX_BUNDLE_BUNDLE_NAME "Windows")
+
+ set_target_properties(${GUI_ONLY_BINARIES} PROPERTIES
+ MACOSX_BUNDLE_SHORT_VERSION_STRING ${GLFW_VERSION}
+ MACOSX_BUNDLE_LONG_VERSION_STRING ${GLFW_VERSION}
+ MACOSX_BUNDLE_INFO_PLIST "${GLFW_SOURCE_DIR}/CMake/MacOSXBundleInfo.plist.in")
+endif()
+
diff --git a/libs/glfw-3.3.8/tests/clipboard.c b/libs/glfw-3.3.8/tests/clipboard.c
new file mode 100644
index 0000000..41454a3
--- /dev/null
+++ b/libs/glfw-3.3.8/tests/clipboard.c
@@ -0,0 +1,145 @@
+//========================================================================
+// Clipboard test program
+// Copyright (c) Camilla Löwy <elmindreda@glfw.org>
+//
+// 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.
+//
+//========================================================================
+//
+// This program is used to test the clipboard functionality.
+//
+//========================================================================
+
+#include <glad/gl.h>
+#define GLFW_INCLUDE_NONE
+#include <GLFW/glfw3.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+
+#include "getopt.h"
+
+#if defined(__APPLE__)
+ #define MODIFIER GLFW_MOD_SUPER
+#else
+ #define MODIFIER GLFW_MOD_CONTROL
+#endif
+
+static void usage(void)
+{
+ printf("Usage: clipboard [-h]\n");
+}
+
+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 (action != GLFW_PRESS)
+ return;
+
+ switch (key)
+ {
+ case GLFW_KEY_ESCAPE:
+ glfwSetWindowShouldClose(window, GLFW_TRUE);
+ break;
+
+ case GLFW_KEY_V:
+ if (mods == MODIFIER)
+ {
+ const char* string;
+
+ string = glfwGetClipboardString(NULL);
+ if (string)
+ printf("Clipboard contains \"%s\"\n", string);
+ else
+ printf("Clipboard does not contain a string\n");
+ }
+ break;
+
+ case GLFW_KEY_C:
+ if (mods == MODIFIER)
+ {
+ const char* string = "Hello GLFW World!";
+ glfwSetClipboardString(NULL, string);
+ printf("Setting clipboard to \"%s\"\n", string);
+ }
+ break;
+ }
+}
+
+int main(int argc, char** argv)
+{
+ int ch;
+ GLFWwindow* window;
+
+ while ((ch = getopt(argc, argv, "h")) != -1)
+ {
+ switch (ch)
+ {
+ case 'h':
+ usage();
+ exit(EXIT_SUCCESS);
+
+ default:
+ usage();
+ exit(EXIT_FAILURE);
+ }
+ }
+
+ glfwSetErrorCallback(error_callback);
+
+ if (!glfwInit())
+ {
+ fprintf(stderr, "Failed to initialize GLFW\n");
+ exit(EXIT_FAILURE);
+ }
+
+ window = glfwCreateWindow(200, 200, "Clipboard Test", NULL, NULL);
+ if (!window)
+ {
+ glfwTerminate();
+
+ fprintf(stderr, "Failed to open GLFW window\n");
+ exit(EXIT_FAILURE);
+ }
+
+ glfwMakeContextCurrent(window);
+ gladLoadGL(glfwGetProcAddress);
+ glfwSwapInterval(1);
+
+ glfwSetKeyCallback(window, key_callback);
+
+ glClearColor(0.5f, 0.5f, 0.5f, 0);
+
+ while (!glfwWindowShouldClose(window))
+ {
+ glClear(GL_COLOR_BUFFER_BIT);
+
+ glfwSwapBuffers(window);
+ glfwWaitEvents();
+ }
+
+ glfwTerminate();
+ exit(EXIT_SUCCESS);
+}
+
diff --git a/libs/glfw-3.3.8/tests/cursor.c b/libs/glfw-3.3.8/tests/cursor.c
new file mode 100644
index 0000000..b6288f6
--- /dev/null
+++ b/libs/glfw-3.3.8/tests/cursor.c
@@ -0,0 +1,493 @@
+//========================================================================
+// Cursor & input mode tests
+// Copyright (c) Camilla Löwy <elmindreda@glfw.org>
+//
+// 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.
+//
+//========================================================================
+//
+// This test provides an interface to the cursor image and cursor mode
+// parts of the API.
+//
+// Custom cursor image generation by urraka.
+//
+//========================================================================
+
+#include <glad/gl.h>
+#define GLFW_INCLUDE_NONE
+#include <GLFW/glfw3.h>
+
+#if defined(_MSC_VER)
+ // Make MS math.h define M_PI
+ #define _USE_MATH_DEFINES
+#endif
+
+#include <math.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+#include "linmath.h"
+
+#define CURSOR_FRAME_COUNT 60
+
+static const char* vertex_shader_text =
+"#version 110\n"
+"uniform mat4 MVP;\n"
+"attribute vec2 vPos;\n"
+"void main()\n"
+"{\n"
+" gl_Position = MVP * vec4(vPos, 0.0, 1.0);\n"
+"}\n";
+
+static const char* fragment_shader_text =
+"#version 110\n"
+"void main()\n"
+"{\n"
+" gl_FragColor = vec4(1.0);\n"
+"}\n";
+
+static double cursor_x;
+static double cursor_y;
+static int swap_interval = 1;
+static int wait_events = GLFW_TRUE;
+static int animate_cursor = GLFW_FALSE;
+static int track_cursor = GLFW_FALSE;
+static GLFWcursor* standard_cursors[6];
+static GLFWcursor* tracking_cursor = NULL;
+
+static void error_callback(int error, const char* description)
+{
+ fprintf(stderr, "Error: %s\n", description);
+}
+
+static float star(int x, int y, float t)
+{
+ const float c = 64 / 2.f;
+
+ const float i = (0.25f * (float) sin(2.f * M_PI * t) + 0.75f);
+ const float k = 64 * 0.046875f * i;
+
+ const float dist = (float) sqrt((x - c) * (x - c) + (y - c) * (y - c));
+
+ const float salpha = 1.f - dist / c;
+ const float xalpha = (float) x == c ? c : k / (float) fabs(x - c);
+ const float yalpha = (float) y == c ? c : k / (float) fabs(y - c);
+
+ return (float) fmax(0.f, fmin(1.f, i * salpha * 0.2f + salpha * xalpha * yalpha));
+}
+
+static GLFWcursor* create_cursor_frame(float t)
+{
+ int i = 0, x, y;
+ unsigned char buffer[64 * 64 * 4];
+ const GLFWimage image = { 64, 64, buffer };
+
+ for (y = 0; y < image.width; y++)
+ {
+ for (x = 0; x < image.height; x++)
+ {
+ buffer[i++] = 255;
+ buffer[i++] = 255;
+ buffer[i++] = 255;
+ buffer[i++] = (unsigned char) (255 * star(x, y, t));
+ }
+ }
+
+ return glfwCreateCursor(&image, image.width / 2, image.height / 2);
+}
+
+static GLFWcursor* create_tracking_cursor(void)
+{
+ int i = 0, x, y;
+ unsigned char buffer[32 * 32 * 4];
+ const GLFWimage image = { 32, 32, buffer };
+
+ for (y = 0; y < image.width; y++)
+ {
+ for (x = 0; x < image.height; x++)
+ {
+ if (x == 7 || y == 7)
+ {
+ buffer[i++] = 255;
+ buffer[i++] = 0;
+ buffer[i++] = 0;
+ buffer[i++] = 255;
+ }
+ else
+ {
+ buffer[i++] = 0;
+ buffer[i++] = 0;
+ buffer[i++] = 0;
+ buffer[i++] = 0;
+ }
+ }
+ }
+
+ return glfwCreateCursor(&image, 7, 7);
+}
+
+static void cursor_position_callback(GLFWwindow* window, double x, double y)
+{
+ printf("%0.3f: Cursor position: %f %f (%+f %+f)\n",
+ glfwGetTime(),
+ x, y, x - cursor_x, y - cursor_y);
+
+ cursor_x = x;
+ cursor_y = y;
+}
+
+static void key_callback(GLFWwindow* window, int key, int scancode, int action, int mods)
+{
+ if (action != GLFW_PRESS)
+ return;
+
+ switch (key)
+ {
+ case GLFW_KEY_A:
+ {
+ animate_cursor = !animate_cursor;
+ if (!animate_cursor)
+ glfwSetCursor(window, NULL);
+
+ break;
+ }
+
+ case GLFW_KEY_ESCAPE:
+ {
+ if (glfwGetInputMode(window, GLFW_CURSOR) != GLFW_CURSOR_DISABLED)
+ {
+ glfwSetWindowShouldClose(window, GLFW_TRUE);
+ break;
+ }
+
+ /* FALLTHROUGH */
+ }
+
+ case GLFW_KEY_N:
+ glfwSetInputMode(window, GLFW_CURSOR, GLFW_CURSOR_NORMAL);
+ glfwGetCursorPos(window, &cursor_x, &cursor_y);
+ printf("(( cursor is normal ))\n");
+ break;
+
+ case GLFW_KEY_D:
+ glfwSetInputMode(window, GLFW_CURSOR, GLFW_CURSOR_DISABLED);
+ printf("(( cursor is disabled ))\n");
+ break;
+
+ case GLFW_KEY_H:
+ glfwSetInputMode(window, GLFW_CURSOR, GLFW_CURSOR_HIDDEN);
+ printf("(( cursor is hidden ))\n");
+ break;
+
+ case GLFW_KEY_R:
+ if (!glfwRawMouseMotionSupported())
+ break;
+
+ if (glfwGetInputMode(window, GLFW_RAW_MOUSE_MOTION))
+ {
+ glfwSetInputMode(window, GLFW_RAW_MOUSE_MOTION, GLFW_FALSE);
+ printf("(( raw input is disabled ))\n");
+ }
+ else
+ {
+ glfwSetInputMode(window, GLFW_RAW_MOUSE_MOTION, GLFW_TRUE);
+ printf("(( raw input is enabled ))\n");
+ }
+ break;
+
+ case GLFW_KEY_SPACE:
+ swap_interval = 1 - swap_interval;
+ printf("(( swap interval: %i ))\n", swap_interval);
+ glfwSwapInterval(swap_interval);
+ break;
+
+ case GLFW_KEY_W:
+ wait_events = !wait_events;
+ printf("(( %sing for events ))\n", wait_events ? "wait" : "poll");
+ break;
+
+ case GLFW_KEY_T:
+ track_cursor = !track_cursor;
+ if (track_cursor)
+ glfwSetCursor(window, tracking_cursor);
+ else
+ glfwSetCursor(window, NULL);
+
+ break;
+
+ case GLFW_KEY_P:
+ {
+ double x, y;
+ glfwGetCursorPos(window, &x, &y);
+
+ printf("Query before set: %f %f (%+f %+f)\n",
+ x, y, x - cursor_x, y - cursor_y);
+ cursor_x = x;
+ cursor_y = y;
+
+ glfwSetCursorPos(window, cursor_x, cursor_y);
+ glfwGetCursorPos(window, &x, &y);
+
+ printf("Query after set: %f %f (%+f %+f)\n",
+ x, y, x - cursor_x, y - cursor_y);
+ cursor_x = x;
+ cursor_y = y;
+ break;
+ }
+
+ case GLFW_KEY_UP:
+ glfwSetCursorPos(window, 0, 0);
+ glfwGetCursorPos(window, &cursor_x, &cursor_y);
+ break;
+
+ case GLFW_KEY_DOWN:
+ {
+ int width, height;
+ glfwGetWindowSize(window, &width, &height);
+ glfwSetCursorPos(window, width - 1, height - 1);
+ glfwGetCursorPos(window, &cursor_x, &cursor_y);
+ break;
+ }
+
+ case GLFW_KEY_0:
+ glfwSetCursor(window, NULL);
+ break;
+
+ case GLFW_KEY_1:
+ glfwSetCursor(window, standard_cursors[0]);
+ break;
+
+ case GLFW_KEY_2:
+ glfwSetCursor(window, standard_cursors[1]);
+ break;
+
+ case GLFW_KEY_3:
+ glfwSetCursor(window, standard_cursors[2]);
+ break;
+
+ case GLFW_KEY_4:
+ glfwSetCursor(window, standard_cursors[3]);
+ break;
+
+ case GLFW_KEY_5:
+ glfwSetCursor(window, standard_cursors[4]);
+ break;
+
+ case GLFW_KEY_6:
+ glfwSetCursor(window, standard_cursors[5]);
+ break;
+
+ case GLFW_KEY_F11:
+ case GLFW_KEY_ENTER:
+ {
+ static int x, y, width, height;
+
+ if (mods != GLFW_MOD_ALT)
+ return;
+
+ if (glfwGetWindowMonitor(window))
+ glfwSetWindowMonitor(window, NULL, x, y, width, height, 0);
+ else
+ {
+ GLFWmonitor* monitor = glfwGetPrimaryMonitor();
+ const GLFWvidmode* mode = glfwGetVideoMode(monitor);
+ glfwGetWindowPos(window, &x, &y);
+ glfwGetWindowSize(window, &width, &height);
+ glfwSetWindowMonitor(window, monitor,
+ 0, 0, mode->width, mode->height,
+ mode->refreshRate);
+ }
+
+ glfwGetCursorPos(window, &cursor_x, &cursor_y);
+ break;
+ }
+ }
+}
+
+int main(void)
+{
+ int i;
+ GLFWwindow* window;
+ GLFWcursor* star_cursors[CURSOR_FRAME_COUNT];
+ GLFWcursor* current_frame = NULL;
+ GLuint vertex_buffer, vertex_shader, fragment_shader, program;
+ GLint mvp_location, vpos_location;
+
+ glfwSetErrorCallback(error_callback);
+
+ if (!glfwInit())
+ exit(EXIT_FAILURE);
+
+ tracking_cursor = create_tracking_cursor();
+ if (!tracking_cursor)
+ {
+ glfwTerminate();
+ exit(EXIT_FAILURE);
+ }
+
+ for (i = 0; i < CURSOR_FRAME_COUNT; i++)
+ {
+ star_cursors[i] = create_cursor_frame(i / (float) CURSOR_FRAME_COUNT);
+ if (!star_cursors[i])
+ {
+ glfwTerminate();
+ exit(EXIT_FAILURE);
+ }
+ }
+
+ for (i = 0; i < sizeof(standard_cursors) / sizeof(standard_cursors[0]); i++)
+ {
+ const int shapes[] = {
+ GLFW_ARROW_CURSOR,
+ GLFW_IBEAM_CURSOR,
+ GLFW_CROSSHAIR_CURSOR,
+ GLFW_HAND_CURSOR,
+ GLFW_HRESIZE_CURSOR,
+ GLFW_VRESIZE_CURSOR
+ };
+
+ standard_cursors[i] = glfwCreateStandardCursor(shapes[i]);
+ if (!standard_cursors[i])
+ {
+ glfwTerminate();
+ exit(EXIT_FAILURE);
+ }
+ }
+
+ glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 2);
+ glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 0);
+
+ window = glfwCreateWindow(640, 480, "Cursor Test", NULL, NULL);
+ if (!window)
+ {
+ glfwTerminate();
+ exit(EXIT_FAILURE);
+ }
+
+ glfwMakeContextCurrent(window);
+ gladLoadGL(glfwGetProcAddress);
+
+ glGenBuffers(1, &vertex_buffer);
+ glBindBuffer(GL_ARRAY_BUFFER, vertex_buffer);
+
+ 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");
+
+ glEnableVertexAttribArray(vpos_location);
+ glVertexAttribPointer(vpos_location, 2, GL_FLOAT, GL_FALSE,
+ sizeof(vec2), (void*) 0);
+ glUseProgram(program);
+
+ glfwGetCursorPos(window, &cursor_x, &cursor_y);
+ printf("Cursor position: %f %f\n", cursor_x, cursor_y);
+
+ glfwSetCursorPosCallback(window, cursor_position_callback);
+ glfwSetKeyCallback(window, key_callback);
+
+ while (!glfwWindowShouldClose(window))
+ {
+ glClear(GL_COLOR_BUFFER_BIT);
+
+ if (track_cursor)
+ {
+ int wnd_width, wnd_height, fb_width, fb_height;
+ float scale;
+ vec2 vertices[4];
+ mat4x4 mvp;
+
+ glfwGetWindowSize(window, &wnd_width, &wnd_height);
+ glfwGetFramebufferSize(window, &fb_width, &fb_height);
+
+ glViewport(0, 0, fb_width, fb_height);
+
+ scale = (float) fb_width / (float) wnd_width;
+ vertices[0][0] = 0.5f;
+ vertices[0][1] = (float) (fb_height - floor(cursor_y * scale) - 1.f + 0.5f);
+ vertices[1][0] = (float) fb_width + 0.5f;
+ vertices[1][1] = (float) (fb_height - floor(cursor_y * scale) - 1.f + 0.5f);
+ vertices[2][0] = (float) floor(cursor_x * scale) + 0.5f;
+ vertices[2][1] = 0.5f;
+ vertices[3][0] = (float) floor(cursor_x * scale) + 0.5f;
+ vertices[3][1] = (float) fb_height + 0.5f;
+
+ glBufferData(GL_ARRAY_BUFFER,
+ sizeof(vertices),
+ vertices,
+ GL_STREAM_DRAW);
+
+ mat4x4_ortho(mvp, 0.f, (float) fb_width, 0.f, (float) fb_height, 0.f, 1.f);
+ glUniformMatrix4fv(mvp_location, 1, GL_FALSE, (const GLfloat*) mvp);
+
+ glDrawArrays(GL_LINES, 0, 4);
+ }
+
+ glfwSwapBuffers(window);
+
+ if (animate_cursor)
+ {
+ const int i = (int) (glfwGetTime() * 30.0) % CURSOR_FRAME_COUNT;
+ if (current_frame != star_cursors[i])
+ {
+ glfwSetCursor(window, star_cursors[i]);
+ current_frame = star_cursors[i];
+ }
+ }
+ else
+ current_frame = NULL;
+
+ if (wait_events)
+ {
+ if (animate_cursor)
+ glfwWaitEventsTimeout(1.0 / 30.0);
+ else
+ glfwWaitEvents();
+ }
+ else
+ glfwPollEvents();
+
+ // Workaround for an issue with msvcrt and mintty
+ fflush(stdout);
+ }
+
+ glfwDestroyWindow(window);
+
+ for (i = 0; i < CURSOR_FRAME_COUNT; i++)
+ glfwDestroyCursor(star_cursors[i]);
+
+ for (i = 0; i < sizeof(standard_cursors) / sizeof(standard_cursors[0]); i++)
+ glfwDestroyCursor(standard_cursors[i]);
+
+ glfwTerminate();
+ exit(EXIT_SUCCESS);
+}
+
diff --git a/libs/glfw-3.3.8/tests/empty.c b/libs/glfw-3.3.8/tests/empty.c
new file mode 100644
index 0000000..c3877a7
--- /dev/null
+++ b/libs/glfw-3.3.8/tests/empty.c
@@ -0,0 +1,132 @@
+//========================================================================
+// Empty event test
+// Copyright (c) Camilla Löwy <elmindreda@glfw.org>
+//
+// 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.
+//
+//========================================================================
+//
+// This test is intended to verify that posting of empty events works
+//
+//========================================================================
+
+#include "tinycthread.h"
+
+#include <glad/gl.h>
+#define GLFW_INCLUDE_NONE
+#include <GLFW/glfw3.h>
+
+#include <math.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+static volatile int running = GLFW_TRUE;
+
+static void error_callback(int error, const char* description)
+{
+ fprintf(stderr, "Error: %s\n", description);
+}
+
+static int thread_main(void* data)
+{
+ struct timespec time;
+
+ while (running)
+ {
+ clock_gettime(CLOCK_REALTIME, &time);
+ time.tv_sec += 1;
+ thrd_sleep(&time, NULL);
+
+ glfwPostEmptyEvent();
+ }
+
+ return 0;
+}
+
+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 float nrand(void)
+{
+ return (float) rand() / (float) RAND_MAX;
+}
+
+int main(void)
+{
+ int result;
+ thrd_t thread;
+ GLFWwindow* window;
+
+ srand((unsigned int) time(NULL));
+
+ glfwSetErrorCallback(error_callback);
+
+ if (!glfwInit())
+ exit(EXIT_FAILURE);
+
+ window = glfwCreateWindow(640, 480, "Empty Event Test", NULL, NULL);
+ if (!window)
+ {
+ glfwTerminate();
+ exit(EXIT_FAILURE);
+ }
+
+ glfwMakeContextCurrent(window);
+ gladLoadGL(glfwGetProcAddress);
+ glfwSetKeyCallback(window, key_callback);
+
+ if (thrd_create(&thread, thread_main, NULL) != thrd_success)
+ {
+ fprintf(stderr, "Failed to create secondary thread\n");
+
+ glfwTerminate();
+ exit(EXIT_FAILURE);
+ }
+
+ while (running)
+ {
+ int width, height;
+ float r = nrand(), g = nrand(), b = nrand();
+ float l = (float) sqrt(r * r + g * g + b * b);
+
+ glfwGetFramebufferSize(window, &width, &height);
+
+ glViewport(0, 0, width, height);
+ glClearColor(r / l, g / l, b / l, 1.f);
+ glClear(GL_COLOR_BUFFER_BIT);
+ glfwSwapBuffers(window);
+
+ glfwWaitEvents();
+
+ if (glfwWindowShouldClose(window))
+ running = GLFW_FALSE;
+ }
+
+ glfwHideWindow(window);
+ thrd_join(thread, &result);
+ glfwDestroyWindow(window);
+
+ glfwTerminate();
+ exit(EXIT_SUCCESS);
+}
+
diff --git a/libs/glfw-3.3.8/tests/events.c b/libs/glfw-3.3.8/tests/events.c
new file mode 100644
index 0000000..f29dfbb
--- /dev/null
+++ b/libs/glfw-3.3.8/tests/events.c
@@ -0,0 +1,650 @@
+//========================================================================
+// Event linter (event spewer)
+// Copyright (c) Camilla Löwy <elmindreda@glfw.org>
+//
+// 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.
+//
+//========================================================================
+//
+// This test hooks every available callback and outputs their arguments
+//
+// Log messages go to stdout, error messages to stderr
+//
+// Every event also gets a (sequential) number to aid discussion of logs
+//
+//========================================================================
+
+#include <glad/gl.h>
+#define GLFW_INCLUDE_NONE
+#include <GLFW/glfw3.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <ctype.h>
+#include <string.h>
+#include <locale.h>
+
+#include "getopt.h"
+
+// Event index
+static unsigned int counter = 0;
+
+typedef struct
+{
+ GLFWwindow* window;
+ int number;
+ int closeable;
+} Slot;
+
+static void usage(void)
+{
+ printf("Usage: events [-f] [-h] [-n WINDOWS]\n");
+ printf("Options:\n");
+ printf(" -f use full screen\n");
+ printf(" -h show this help\n");
+ printf(" -n the number of windows to create\n");
+}
+
+static const char* get_key_name(int key)
+{
+ switch (key)
+ {
+ // Printable keys
+ case GLFW_KEY_A: return "A";
+ case GLFW_KEY_B: return "B";
+ case GLFW_KEY_C: return "C";
+ case GLFW_KEY_D: return "D";
+ case GLFW_KEY_E: return "E";
+ case GLFW_KEY_F: return "F";
+ case GLFW_KEY_G: return "G";
+ case GLFW_KEY_H: return "H";
+ case GLFW_KEY_I: return "I";
+ case GLFW_KEY_J: return "J";
+ case GLFW_KEY_K: return "K";
+ case GLFW_KEY_L: return "L";
+ case GLFW_KEY_M: return "M";
+ case GLFW_KEY_N: return "N";
+ case GLFW_KEY_O: return "O";
+ case GLFW_KEY_P: return "P";
+ case GLFW_KEY_Q: return "Q";
+ case GLFW_KEY_R: return "R";
+ case GLFW_KEY_S: return "S";
+ case GLFW_KEY_T: return "T";
+ case GLFW_KEY_U: return "U";
+ case GLFW_KEY_V: return "V";
+ case GLFW_KEY_W: return "W";
+ case GLFW_KEY_X: return "X";
+ case GLFW_KEY_Y: return "Y";
+ case GLFW_KEY_Z: return "Z";
+ case GLFW_KEY_1: return "1";
+ case GLFW_KEY_2: return "2";
+ case GLFW_KEY_3: return "3";
+ case GLFW_KEY_4: return "4";
+ case GLFW_KEY_5: return "5";
+ case GLFW_KEY_6: return "6";
+ case GLFW_KEY_7: return "7";
+ case GLFW_KEY_8: return "8";
+ case GLFW_KEY_9: return "9";
+ case GLFW_KEY_0: return "0";
+ case GLFW_KEY_SPACE: return "SPACE";
+ case GLFW_KEY_MINUS: return "MINUS";
+ case GLFW_KEY_EQUAL: return "EQUAL";
+ case GLFW_KEY_LEFT_BRACKET: return "LEFT BRACKET";
+ case GLFW_KEY_RIGHT_BRACKET: return "RIGHT BRACKET";
+ case GLFW_KEY_BACKSLASH: return "BACKSLASH";
+ case GLFW_KEY_SEMICOLON: return "SEMICOLON";
+ case GLFW_KEY_APOSTROPHE: return "APOSTROPHE";
+ case GLFW_KEY_GRAVE_ACCENT: return "GRAVE ACCENT";
+ case GLFW_KEY_COMMA: return "COMMA";
+ case GLFW_KEY_PERIOD: return "PERIOD";
+ case GLFW_KEY_SLASH: return "SLASH";
+ case GLFW_KEY_WORLD_1: return "WORLD 1";
+ case GLFW_KEY_WORLD_2: return "WORLD 2";
+
+ // Function keys
+ case GLFW_KEY_ESCAPE: return "ESCAPE";
+ case GLFW_KEY_F1: return "F1";
+ case GLFW_KEY_F2: return "F2";
+ case GLFW_KEY_F3: return "F3";
+ case GLFW_KEY_F4: return "F4";
+ case GLFW_KEY_F5: return "F5";
+ case GLFW_KEY_F6: return "F6";
+ case GLFW_KEY_F7: return "F7";
+ case GLFW_KEY_F8: return "F8";
+ case GLFW_KEY_F9: return "F9";
+ case GLFW_KEY_F10: return "F10";
+ case GLFW_KEY_F11: return "F11";
+ case GLFW_KEY_F12: return "F12";
+ case GLFW_KEY_F13: return "F13";
+ case GLFW_KEY_F14: return "F14";
+ case GLFW_KEY_F15: return "F15";
+ case GLFW_KEY_F16: return "F16";
+ case GLFW_KEY_F17: return "F17";
+ case GLFW_KEY_F18: return "F18";
+ case GLFW_KEY_F19: return "F19";
+ case GLFW_KEY_F20: return "F20";
+ case GLFW_KEY_F21: return "F21";
+ case GLFW_KEY_F22: return "F22";
+ case GLFW_KEY_F23: return "F23";
+ case GLFW_KEY_F24: return "F24";
+ case GLFW_KEY_F25: return "F25";
+ case GLFW_KEY_UP: return "UP";
+ case GLFW_KEY_DOWN: return "DOWN";
+ case GLFW_KEY_LEFT: return "LEFT";
+ case GLFW_KEY_RIGHT: return "RIGHT";
+ case GLFW_KEY_LEFT_SHIFT: return "LEFT SHIFT";
+ case GLFW_KEY_RIGHT_SHIFT: return "RIGHT SHIFT";
+ case GLFW_KEY_LEFT_CONTROL: return "LEFT CONTROL";
+ case GLFW_KEY_RIGHT_CONTROL: return "RIGHT CONTROL";
+ case GLFW_KEY_LEFT_ALT: return "LEFT ALT";
+ case GLFW_KEY_RIGHT_ALT: return "RIGHT ALT";
+ case GLFW_KEY_TAB: return "TAB";
+ case GLFW_KEY_ENTER: return "ENTER";
+ case GLFW_KEY_BACKSPACE: return "BACKSPACE";
+ case GLFW_KEY_INSERT: return "INSERT";
+ case GLFW_KEY_DELETE: return "DELETE";
+ case GLFW_KEY_PAGE_UP: return "PAGE UP";
+ case GLFW_KEY_PAGE_DOWN: return "PAGE DOWN";
+ case GLFW_KEY_HOME: return "HOME";
+ case GLFW_KEY_END: return "END";
+ case GLFW_KEY_KP_0: return "KEYPAD 0";
+ case GLFW_KEY_KP_1: return "KEYPAD 1";
+ case GLFW_KEY_KP_2: return "KEYPAD 2";
+ case GLFW_KEY_KP_3: return "KEYPAD 3";
+ case GLFW_KEY_KP_4: return "KEYPAD 4";
+ case GLFW_KEY_KP_5: return "KEYPAD 5";
+ case GLFW_KEY_KP_6: return "KEYPAD 6";
+ case GLFW_KEY_KP_7: return "KEYPAD 7";
+ case GLFW_KEY_KP_8: return "KEYPAD 8";
+ case GLFW_KEY_KP_9: return "KEYPAD 9";
+ case GLFW_KEY_KP_DIVIDE: return "KEYPAD DIVIDE";
+ case GLFW_KEY_KP_MULTIPLY: return "KEYPAD MULTIPLY";
+ case GLFW_KEY_KP_SUBTRACT: return "KEYPAD SUBTRACT";
+ case GLFW_KEY_KP_ADD: return "KEYPAD ADD";
+ case GLFW_KEY_KP_DECIMAL: return "KEYPAD DECIMAL";
+ case GLFW_KEY_KP_EQUAL: return "KEYPAD EQUAL";
+ case GLFW_KEY_KP_ENTER: return "KEYPAD ENTER";
+ case GLFW_KEY_PRINT_SCREEN: return "PRINT SCREEN";
+ case GLFW_KEY_NUM_LOCK: return "NUM LOCK";
+ case GLFW_KEY_CAPS_LOCK: return "CAPS LOCK";
+ case GLFW_KEY_SCROLL_LOCK: return "SCROLL LOCK";
+ case GLFW_KEY_PAUSE: return "PAUSE";
+ case GLFW_KEY_LEFT_SUPER: return "LEFT SUPER";
+ case GLFW_KEY_RIGHT_SUPER: return "RIGHT SUPER";
+ case GLFW_KEY_MENU: return "MENU";
+
+ default: return "UNKNOWN";
+ }
+}
+
+static const char* get_action_name(int action)
+{
+ switch (action)
+ {
+ case GLFW_PRESS:
+ return "pressed";
+ case GLFW_RELEASE:
+ return "released";
+ case GLFW_REPEAT:
+ return "repeated";
+ }
+
+ return "caused unknown action";
+}
+
+static const char* get_button_name(int button)
+{
+ switch (button)
+ {
+ case GLFW_MOUSE_BUTTON_LEFT:
+ return "left";
+ case GLFW_MOUSE_BUTTON_RIGHT:
+ return "right";
+ case GLFW_MOUSE_BUTTON_MIDDLE:
+ return "middle";
+ default:
+ {
+ static char name[16];
+ snprintf(name, sizeof(name), "%i", button);
+ return name;
+ }
+ }
+}
+
+static const char* get_mods_name(int mods)
+{
+ static char name[512];
+
+ if (mods == 0)
+ return " no mods";
+
+ name[0] = '\0';
+
+ if (mods & GLFW_MOD_SHIFT)
+ strcat(name, " shift");
+ if (mods & GLFW_MOD_CONTROL)
+ strcat(name, " control");
+ if (mods & GLFW_MOD_ALT)
+ strcat(name, " alt");
+ if (mods & GLFW_MOD_SUPER)
+ strcat(name, " super");
+ if (mods & GLFW_MOD_CAPS_LOCK)
+ strcat(name, " capslock-on");
+ if (mods & GLFW_MOD_NUM_LOCK)
+ strcat(name, " numlock-on");
+
+ return name;
+}
+
+static size_t encode_utf8(char* s, unsigned int ch)
+{
+ size_t count = 0;
+
+ if (ch < 0x80)
+ s[count++] = (char) ch;
+ else if (ch < 0x800)
+ {
+ s[count++] = (ch >> 6) | 0xc0;
+ s[count++] = (ch & 0x3f) | 0x80;
+ }
+ else if (ch < 0x10000)
+ {
+ s[count++] = (ch >> 12) | 0xe0;
+ s[count++] = ((ch >> 6) & 0x3f) | 0x80;
+ s[count++] = (ch & 0x3f) | 0x80;
+ }
+ else if (ch < 0x110000)
+ {
+ s[count++] = (ch >> 18) | 0xf0;
+ s[count++] = ((ch >> 12) & 0x3f) | 0x80;
+ s[count++] = ((ch >> 6) & 0x3f) | 0x80;
+ s[count++] = (ch & 0x3f) | 0x80;
+ }
+
+ return count;
+}
+
+static void error_callback(int error, const char* description)
+{
+ fprintf(stderr, "Error: %s\n", description);
+}
+
+static void window_pos_callback(GLFWwindow* window, int x, int y)
+{
+ Slot* slot = glfwGetWindowUserPointer(window);
+ printf("%08x to %i at %0.3f: Window position: %i %i\n",
+ counter++, slot->number, glfwGetTime(), x, y);
+}
+
+static void window_size_callback(GLFWwindow* window, int width, int height)
+{
+ Slot* slot = glfwGetWindowUserPointer(window);
+ printf("%08x to %i at %0.3f: Window size: %i %i\n",
+ counter++, slot->number, glfwGetTime(), width, height);
+}
+
+static void framebuffer_size_callback(GLFWwindow* window, int width, int height)
+{
+ Slot* slot = glfwGetWindowUserPointer(window);
+ printf("%08x to %i at %0.3f: Framebuffer size: %i %i\n",
+ counter++, slot->number, glfwGetTime(), width, height);
+}
+
+static void window_content_scale_callback(GLFWwindow* window, float xscale, float yscale)
+{
+ Slot* slot = glfwGetWindowUserPointer(window);
+ printf("%08x to %i at %0.3f: Window content scale: %0.3f %0.3f\n",
+ counter++, slot->number, glfwGetTime(), xscale, yscale);
+}
+
+static void window_close_callback(GLFWwindow* window)
+{
+ Slot* slot = glfwGetWindowUserPointer(window);
+ printf("%08x to %i at %0.3f: Window close\n",
+ counter++, slot->number, glfwGetTime());
+
+ glfwSetWindowShouldClose(window, slot->closeable);
+}
+
+static void window_refresh_callback(GLFWwindow* window)
+{
+ Slot* slot = glfwGetWindowUserPointer(window);
+ printf("%08x to %i at %0.3f: Window refresh\n",
+ counter++, slot->number, glfwGetTime());
+
+ glfwMakeContextCurrent(window);
+ glClear(GL_COLOR_BUFFER_BIT);
+ glfwSwapBuffers(window);
+}
+
+static void window_focus_callback(GLFWwindow* window, int focused)
+{
+ Slot* slot = glfwGetWindowUserPointer(window);
+ printf("%08x to %i at %0.3f: Window %s\n",
+ counter++, slot->number, glfwGetTime(),
+ focused ? "focused" : "defocused");
+}
+
+static void window_iconify_callback(GLFWwindow* window, int iconified)
+{
+ Slot* slot = glfwGetWindowUserPointer(window);
+ printf("%08x to %i at %0.3f: Window was %s\n",
+ counter++, slot->number, glfwGetTime(),
+ iconified ? "iconified" : "uniconified");
+}
+
+static void window_maximize_callback(GLFWwindow* window, int maximized)
+{
+ Slot* slot = glfwGetWindowUserPointer(window);
+ printf("%08x to %i at %0.3f: Window was %s\n",
+ counter++, slot->number, glfwGetTime(),
+ maximized ? "maximized" : "unmaximized");
+}
+
+static void mouse_button_callback(GLFWwindow* window, int button, int action, int mods)
+{
+ Slot* slot = glfwGetWindowUserPointer(window);
+ printf("%08x to %i at %0.3f: Mouse button %i (%s) (with%s) was %s\n",
+ counter++, slot->number, glfwGetTime(), button,
+ get_button_name(button),
+ get_mods_name(mods),
+ get_action_name(action));
+}
+
+static void cursor_position_callback(GLFWwindow* window, double x, double y)
+{
+ Slot* slot = glfwGetWindowUserPointer(window);
+ printf("%08x to %i at %0.3f: Cursor position: %f %f\n",
+ counter++, slot->number, glfwGetTime(), x, y);
+}
+
+static void cursor_enter_callback(GLFWwindow* window, int entered)
+{
+ Slot* slot = glfwGetWindowUserPointer(window);
+ printf("%08x to %i at %0.3f: Cursor %s window\n",
+ counter++, slot->number, glfwGetTime(),
+ entered ? "entered" : "left");
+}
+
+static void scroll_callback(GLFWwindow* window, double x, double y)
+{
+ Slot* slot = glfwGetWindowUserPointer(window);
+ printf("%08x to %i at %0.3f: Scroll: %0.3f %0.3f\n",
+ counter++, slot->number, glfwGetTime(), x, y);
+}
+
+static void key_callback(GLFWwindow* window, int key, int scancode, int action, int mods)
+{
+ Slot* slot = glfwGetWindowUserPointer(window);
+ const char* name = glfwGetKeyName(key, scancode);
+
+ if (name)
+ {
+ printf("%08x to %i at %0.3f: Key 0x%04x Scancode 0x%04x (%s) (%s) (with%s) was %s\n",
+ counter++, slot->number, glfwGetTime(), key, scancode,
+ get_key_name(key),
+ name,
+ get_mods_name(mods),
+ get_action_name(action));
+ }
+ else
+ {
+ printf("%08x to %i at %0.3f: Key 0x%04x Scancode 0x%04x (%s) (with%s) was %s\n",
+ counter++, slot->number, glfwGetTime(), key, scancode,
+ get_key_name(key),
+ get_mods_name(mods),
+ get_action_name(action));
+ }
+
+ if (action != GLFW_PRESS)
+ return;
+
+ switch (key)
+ {
+ case GLFW_KEY_C:
+ {
+ slot->closeable = !slot->closeable;
+
+ printf("(( closing %s ))\n", slot->closeable ? "enabled" : "disabled");
+ break;
+ }
+
+ case GLFW_KEY_L:
+ {
+ const int state = glfwGetInputMode(window, GLFW_LOCK_KEY_MODS);
+ glfwSetInputMode(window, GLFW_LOCK_KEY_MODS, !state);
+
+ printf("(( lock key mods %s ))\n", !state ? "enabled" : "disabled");
+ break;
+ }
+ }
+}
+
+static void char_callback(GLFWwindow* window, unsigned int codepoint)
+{
+ Slot* slot = glfwGetWindowUserPointer(window);
+ char string[5] = "";
+
+ encode_utf8(string, codepoint);
+ printf("%08x to %i at %0.3f: Character 0x%08x (%s) input\n",
+ counter++, slot->number, glfwGetTime(), codepoint, string);
+}
+
+static void drop_callback(GLFWwindow* window, int count, const char* paths[])
+{
+ int i;
+ Slot* slot = glfwGetWindowUserPointer(window);
+
+ printf("%08x to %i at %0.3f: Drop input\n",
+ counter++, slot->number, glfwGetTime());
+
+ for (i = 0; i < count; i++)
+ printf(" %i: \"%s\"\n", i, paths[i]);
+}
+
+static void monitor_callback(GLFWmonitor* monitor, int event)
+{
+ if (event == GLFW_CONNECTED)
+ {
+ int x, y, widthMM, heightMM;
+ const GLFWvidmode* mode = glfwGetVideoMode(monitor);
+
+ glfwGetMonitorPos(monitor, &x, &y);
+ glfwGetMonitorPhysicalSize(monitor, &widthMM, &heightMM);
+
+ printf("%08x at %0.3f: Monitor %s (%ix%i at %ix%i, %ix%i mm) was connected\n",
+ counter++,
+ glfwGetTime(),
+ glfwGetMonitorName(monitor),
+ mode->width, mode->height,
+ x, y,
+ widthMM, heightMM);
+ }
+ else if (event == GLFW_DISCONNECTED)
+ {
+ printf("%08x at %0.3f: Monitor %s was disconnected\n",
+ counter++,
+ glfwGetTime(),
+ glfwGetMonitorName(monitor));
+ }
+}
+
+static void joystick_callback(int jid, int event)
+{
+ if (event == GLFW_CONNECTED)
+ {
+ int axisCount, buttonCount, hatCount;
+
+ glfwGetJoystickAxes(jid, &axisCount);
+ glfwGetJoystickButtons(jid, &buttonCount);
+ glfwGetJoystickHats(jid, &hatCount);
+
+ printf("%08x at %0.3f: Joystick %i (%s) was connected with %i axes, %i buttons, and %i hats\n",
+ counter++, glfwGetTime(),
+ jid,
+ glfwGetJoystickName(jid),
+ axisCount,
+ buttonCount,
+ hatCount);
+ }
+ else
+ {
+ printf("%08x at %0.3f: Joystick %i was disconnected\n",
+ counter++, glfwGetTime(), jid);
+ }
+}
+
+int main(int argc, char** argv)
+{
+ Slot* slots;
+ GLFWmonitor* monitor = NULL;
+ int ch, i, width, height, count = 1;
+
+ glfwSetErrorCallback(error_callback);
+
+ if (!glfwInit())
+ exit(EXIT_FAILURE);
+
+ printf("Library initialized\n");
+
+ glfwSetMonitorCallback(monitor_callback);
+ glfwSetJoystickCallback(joystick_callback);
+
+ while ((ch = getopt(argc, argv, "hfn:")) != -1)
+ {
+ switch (ch)
+ {
+ case 'h':
+ usage();
+ exit(EXIT_SUCCESS);
+
+ case 'f':
+ monitor = glfwGetPrimaryMonitor();
+ break;
+
+ case 'n':
+ count = (int) strtoul(optarg, NULL, 10);
+ break;
+
+ default:
+ usage();
+ exit(EXIT_FAILURE);
+ }
+ }
+
+ if (monitor)
+ {
+ const GLFWvidmode* mode = glfwGetVideoMode(monitor);
+
+ glfwWindowHint(GLFW_REFRESH_RATE, mode->refreshRate);
+ glfwWindowHint(GLFW_RED_BITS, mode->redBits);
+ glfwWindowHint(GLFW_GREEN_BITS, mode->greenBits);
+ glfwWindowHint(GLFW_BLUE_BITS, mode->blueBits);
+
+ width = mode->width;
+ height = mode->height;
+ }
+ else
+ {
+ width = 640;
+ height = 480;
+ }
+
+ slots = calloc(count, sizeof(Slot));
+
+ for (i = 0; i < count; i++)
+ {
+ char title[128];
+
+ slots[i].closeable = GLFW_TRUE;
+ slots[i].number = i + 1;
+
+ snprintf(title, sizeof(title), "Event Linter (Window %i)", slots[i].number);
+
+ if (monitor)
+ {
+ printf("Creating full screen window %i (%ix%i on %s)\n",
+ slots[i].number,
+ width, height,
+ glfwGetMonitorName(monitor));
+ }
+ else
+ {
+ printf("Creating windowed mode window %i (%ix%i)\n",
+ slots[i].number,
+ width, height);
+ }
+
+ slots[i].window = glfwCreateWindow(width, height, title, monitor, NULL);
+ if (!slots[i].window)
+ {
+ free(slots);
+ glfwTerminate();
+ exit(EXIT_FAILURE);
+ }
+
+ glfwSetWindowUserPointer(slots[i].window, slots + i);
+
+ glfwSetWindowPosCallback(slots[i].window, window_pos_callback);
+ glfwSetWindowSizeCallback(slots[i].window, window_size_callback);
+ glfwSetFramebufferSizeCallback(slots[i].window, framebuffer_size_callback);
+ glfwSetWindowContentScaleCallback(slots[i].window, window_content_scale_callback);
+ glfwSetWindowCloseCallback(slots[i].window, window_close_callback);
+ glfwSetWindowRefreshCallback(slots[i].window, window_refresh_callback);
+ glfwSetWindowFocusCallback(slots[i].window, window_focus_callback);
+ glfwSetWindowIconifyCallback(slots[i].window, window_iconify_callback);
+ glfwSetWindowMaximizeCallback(slots[i].window, window_maximize_callback);
+ glfwSetMouseButtonCallback(slots[i].window, mouse_button_callback);
+ glfwSetCursorPosCallback(slots[i].window, cursor_position_callback);
+ glfwSetCursorEnterCallback(slots[i].window, cursor_enter_callback);
+ glfwSetScrollCallback(slots[i].window, scroll_callback);
+ glfwSetKeyCallback(slots[i].window, key_callback);
+ glfwSetCharCallback(slots[i].window, char_callback);
+ glfwSetDropCallback(slots[i].window, drop_callback);
+
+ glfwMakeContextCurrent(slots[i].window);
+ gladLoadGL(glfwGetProcAddress);
+ glfwSwapInterval(1);
+ }
+
+ printf("Main loop starting\n");
+
+ for (;;)
+ {
+ for (i = 0; i < count; i++)
+ {
+ if (glfwWindowShouldClose(slots[i].window))
+ break;
+ }
+
+ if (i < count)
+ break;
+
+ glfwWaitEvents();
+
+ // Workaround for an issue with msvcrt and mintty
+ fflush(stdout);
+ }
+
+ free(slots);
+ glfwTerminate();
+ exit(EXIT_SUCCESS);
+}
+
diff --git a/libs/glfw-3.3.8/tests/gamma.c b/libs/glfw-3.3.8/tests/gamma.c
new file mode 100644
index 0000000..7419592
--- /dev/null
+++ b/libs/glfw-3.3.8/tests/gamma.c
@@ -0,0 +1,185 @@
+//========================================================================
+// Gamma correction test program
+// Copyright (c) Camilla Löwy <elmindreda@glfw.org>
+//
+// 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.
+//
+//========================================================================
+//
+// This program is used to test the gamma correction functionality for
+// both full screen and windowed mode windows
+//
+//========================================================================
+
+#include <glad/gl.h>
+#define GLFW_INCLUDE_NONE
+#include <GLFW/glfw3.h>
+
+#define NK_IMPLEMENTATION
+#define NK_INCLUDE_FIXED_TYPES
+#define NK_INCLUDE_FONT_BAKING
+#define NK_INCLUDE_DEFAULT_FONT
+#define NK_INCLUDE_DEFAULT_ALLOCATOR
+#define NK_INCLUDE_VERTEX_BUFFER_OUTPUT
+#define NK_INCLUDE_STANDARD_VARARGS
+#define NK_BUTTON_TRIGGER_ON_RELEASE
+#include <nuklear.h>
+
+#define NK_GLFW_GL2_IMPLEMENTATION
+#include <nuklear_glfw_gl2.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+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 (action == GLFW_PRESS && key == GLFW_KEY_ESCAPE)
+ glfwSetWindowShouldClose(window, GLFW_TRUE);
+}
+
+static void chart_ramp_array(struct nk_context* nk,
+ struct nk_color color,
+ int count, unsigned short int* values)
+{
+ if (nk_chart_begin_colored(nk, NK_CHART_LINES,
+ color, nk_rgb(255, 255, 255),
+ count, 0, 65535))
+ {
+ int i;
+ for (i = 0; i < count; i++)
+ {
+ char buffer[1024];
+ if (nk_chart_push(nk, values[i]))
+ {
+ snprintf(buffer, sizeof(buffer), "#%u: %u (%0.5f) ",
+ i, values[i], values[i] / 65535.f);
+ nk_tooltip(nk, buffer);
+ }
+ }
+
+ nk_chart_end(nk);
+ }
+}
+
+int main(int argc, char** argv)
+{
+ GLFWmonitor* monitor = NULL;
+ GLFWwindow* window;
+ GLFWgammaramp orig_ramp;
+ struct nk_context* nk;
+ struct nk_font_atlas* atlas;
+ float gamma_value = 1.f;
+
+ glfwSetErrorCallback(error_callback);
+
+ if (!glfwInit())
+ exit(EXIT_FAILURE);
+
+ monitor = glfwGetPrimaryMonitor();
+
+ glfwWindowHint(GLFW_SCALE_TO_MONITOR, GLFW_TRUE);
+
+ window = glfwCreateWindow(800, 400, "Gamma Test", NULL, NULL);
+ if (!window)
+ {
+ glfwTerminate();
+ exit(EXIT_FAILURE);
+ }
+
+ {
+ const GLFWgammaramp* ramp = glfwGetGammaRamp(monitor);
+ if (!ramp)
+ {
+ glfwTerminate();
+ exit(EXIT_FAILURE);
+ }
+
+ const size_t array_size = ramp->size * sizeof(short);
+ orig_ramp.size = ramp->size;
+ orig_ramp.red = malloc(array_size);
+ orig_ramp.green = malloc(array_size);
+ orig_ramp.blue = malloc(array_size);
+ memcpy(orig_ramp.red, ramp->red, array_size);
+ memcpy(orig_ramp.green, ramp->green, array_size);
+ memcpy(orig_ramp.blue, ramp->blue, array_size);
+ }
+
+ glfwMakeContextCurrent(window);
+ gladLoadGL(glfwGetProcAddress);
+ glfwSwapInterval(1);
+
+ nk = nk_glfw3_init(window, NK_GLFW3_INSTALL_CALLBACKS);
+ nk_glfw3_font_stash_begin(&atlas);
+ nk_glfw3_font_stash_end();
+
+ glfwSetKeyCallback(window, key_callback);
+
+ while (!glfwWindowShouldClose(window))
+ {
+ int width, height;
+ struct nk_rect area;
+
+ glfwGetWindowSize(window, &width, &height);
+ area = nk_rect(0.f, 0.f, (float) width, (float) height);
+ nk_window_set_bounds(nk, "", area);
+
+ glClear(GL_COLOR_BUFFER_BIT);
+ nk_glfw3_new_frame();
+ if (nk_begin(nk, "", area, 0))
+ {
+ const GLFWgammaramp* ramp;
+
+ nk_layout_row_dynamic(nk, 30, 3);
+ if (nk_slider_float(nk, 0.1f, &gamma_value, 5.f, 0.1f))
+ glfwSetGamma(monitor, gamma_value);
+ nk_labelf(nk, NK_TEXT_LEFT, "%0.1f", gamma_value);
+ if (nk_button_label(nk, "Revert"))
+ glfwSetGammaRamp(monitor, &orig_ramp);
+
+ ramp = glfwGetGammaRamp(monitor);
+
+ nk_layout_row_dynamic(nk, height - 60.f, 3);
+ chart_ramp_array(nk, nk_rgb(255, 0, 0), ramp->size, ramp->red);
+ chart_ramp_array(nk, nk_rgb(0, 255, 0), ramp->size, ramp->green);
+ chart_ramp_array(nk, nk_rgb(0, 0, 255), ramp->size, ramp->blue);
+ }
+
+ nk_end(nk);
+ nk_glfw3_render(NK_ANTI_ALIASING_ON);
+
+ glfwSwapBuffers(window);
+ glfwWaitEventsTimeout(1.0);
+ }
+
+ free(orig_ramp.red);
+ free(orig_ramp.green);
+ free(orig_ramp.blue);
+
+ nk_glfw3_shutdown();
+ glfwTerminate();
+ exit(EXIT_SUCCESS);
+}
+
diff --git a/libs/glfw-3.3.8/tests/glfwinfo.c b/libs/glfw-3.3.8/tests/glfwinfo.c
new file mode 100644
index 0000000..f681e0a
--- /dev/null
+++ b/libs/glfw-3.3.8/tests/glfwinfo.c
@@ -0,0 +1,943 @@
+//========================================================================
+// Context creation and information tool
+// Copyright (c) Camilla Löwy <elmindreda@glfw.org>
+//
+// 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 <glad/gl.h>
+#include <glad/vulkan.h>
+#define GLFW_INCLUDE_NONE
+#include <GLFW/glfw3.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "getopt.h"
+
+#ifdef _MSC_VER
+#define strcasecmp(x, y) _stricmp(x, y)
+#endif
+
+#define API_NAME_OPENGL "gl"
+#define API_NAME_OPENGL_ES "es"
+
+#define API_NAME_NATIVE "native"
+#define API_NAME_EGL "egl"
+#define API_NAME_OSMESA "osmesa"
+
+#define PROFILE_NAME_CORE "core"
+#define PROFILE_NAME_COMPAT "compat"
+
+#define STRATEGY_NAME_NONE "none"
+#define STRATEGY_NAME_LOSE "lose"
+
+#define BEHAVIOR_NAME_NONE "none"
+#define BEHAVIOR_NAME_FLUSH "flush"
+
+static void usage(void)
+{
+ printf("Usage: glfwinfo [OPTION]...\n");
+ printf("Options:\n");
+ printf(" -a, --client-api=API the client API to use ("
+ API_NAME_OPENGL " or "
+ API_NAME_OPENGL_ES ")\n");
+ printf(" -b, --behavior=BEHAVIOR the release behavior to use ("
+ BEHAVIOR_NAME_NONE " or "
+ BEHAVIOR_NAME_FLUSH ")\n");
+ printf(" -c, --context-api=API the context creation API to use ("
+ API_NAME_NATIVE " or "
+ API_NAME_EGL " or "
+ API_NAME_OSMESA ")\n");
+ printf(" -d, --debug request a debug context\n");
+ printf(" -f, --forward require a forward-compatible context\n");
+ printf(" -h, --help show this help\n");
+ printf(" -l, --list-extensions list all Vulkan and client API extensions\n");
+ printf(" --list-layers list all Vulkan layers\n");
+ printf(" -m, --major=MAJOR the major number of the required "
+ "client API version\n");
+ printf(" -n, --minor=MINOR the minor number of the required "
+ "client API version\n");
+ printf(" -p, --profile=PROFILE the OpenGL profile to use ("
+ PROFILE_NAME_CORE " or "
+ PROFILE_NAME_COMPAT ")\n");
+ printf(" -s, --robustness=STRATEGY the robustness strategy to use ("
+ STRATEGY_NAME_NONE " or "
+ STRATEGY_NAME_LOSE ")\n");
+ printf(" -v, --version print version information\n");
+ printf(" --red-bits=N the number of red bits to request\n");
+ printf(" --green-bits=N the number of green bits to request\n");
+ printf(" --blue-bits=N the number of blue bits to request\n");
+ printf(" --alpha-bits=N the number of alpha bits to request\n");
+ printf(" --depth-bits=N the number of depth bits to request\n");
+ printf(" --stencil-bits=N the number of stencil bits to request\n");
+ printf(" --accum-red-bits=N the number of red bits to request\n");
+ printf(" --accum-green-bits=N the number of green bits to request\n");
+ printf(" --accum-blue-bits=N the number of blue bits to request\n");
+ printf(" --accum-alpha-bits=N the number of alpha bits to request\n");
+ printf(" --aux-buffers=N the number of aux buffers to request\n");
+ printf(" --samples=N the number of MSAA samples to request\n");
+ printf(" --stereo request stereo rendering\n");
+ printf(" --srgb request an sRGB capable framebuffer\n");
+ printf(" --singlebuffer request single-buffering\n");
+ printf(" --no-error request a context that does not emit errors\n");
+ printf(" --graphics-switching request macOS graphics switching\n");
+}
+
+static void error_callback(int error, const char* description)
+{
+ fprintf(stderr, "Error: %s\n", description);
+}
+
+static const char* get_device_type_name(VkPhysicalDeviceType type)
+{
+ if (type == VK_PHYSICAL_DEVICE_TYPE_OTHER)
+ return "other";
+ else if (type == VK_PHYSICAL_DEVICE_TYPE_INTEGRATED_GPU)
+ return "integrated GPU";
+ else if (type == VK_PHYSICAL_DEVICE_TYPE_DISCRETE_GPU)
+ return "discrete GPU";
+ else if (type == VK_PHYSICAL_DEVICE_TYPE_VIRTUAL_GPU)
+ return "virtual GPU";
+ else if (type == VK_PHYSICAL_DEVICE_TYPE_CPU)
+ return "CPU";
+
+ return "unknown";
+}
+
+static const char* get_api_name(int api)
+{
+ if (api == GLFW_OPENGL_API)
+ return "OpenGL";
+ else if (api == GLFW_OPENGL_ES_API)
+ return "OpenGL ES";
+
+ return "Unknown API";
+}
+
+static const char* get_profile_name_gl(GLint mask)
+{
+ if (mask & GL_CONTEXT_COMPATIBILITY_PROFILE_BIT)
+ return PROFILE_NAME_COMPAT;
+ if (mask & GL_CONTEXT_CORE_PROFILE_BIT)
+ return PROFILE_NAME_CORE;
+
+ return "unknown";
+}
+
+static const char* get_profile_name_glfw(int profile)
+{
+ if (profile == GLFW_OPENGL_COMPAT_PROFILE)
+ return PROFILE_NAME_COMPAT;
+ if (profile == GLFW_OPENGL_CORE_PROFILE)
+ return PROFILE_NAME_CORE;
+
+ return "unknown";
+}
+
+static const char* get_strategy_name_gl(GLint strategy)
+{
+ if (strategy == GL_LOSE_CONTEXT_ON_RESET_ARB)
+ return STRATEGY_NAME_LOSE;
+ if (strategy == GL_NO_RESET_NOTIFICATION_ARB)
+ return STRATEGY_NAME_NONE;
+
+ return "unknown";
+}
+
+static const char* get_strategy_name_glfw(int strategy)
+{
+ if (strategy == GLFW_LOSE_CONTEXT_ON_RESET)
+ return STRATEGY_NAME_LOSE;
+ if (strategy == GLFW_NO_RESET_NOTIFICATION)
+ return STRATEGY_NAME_NONE;
+
+ return "unknown";
+}
+
+static void list_context_extensions(int client, int major, int minor)
+{
+ int i;
+ GLint count;
+ const GLubyte* extensions;
+
+ printf("%s context extensions:\n", get_api_name(client));
+
+ if (client == GLFW_OPENGL_API && major > 2)
+ {
+ glGetIntegerv(GL_NUM_EXTENSIONS, &count);
+
+ for (i = 0; i < count; i++)
+ printf(" %s\n", (const char*) glGetStringi(GL_EXTENSIONS, i));
+ }
+ else
+ {
+ extensions = glGetString(GL_EXTENSIONS);
+ while (*extensions != '\0')
+ {
+ putchar(' ');
+
+ while (*extensions != '\0' && *extensions != ' ')
+ {
+ putchar(*extensions);
+ extensions++;
+ }
+
+ while (*extensions == ' ')
+ extensions++;
+
+ putchar('\n');
+ }
+ }
+}
+
+static void list_vulkan_instance_layers(void)
+{
+ uint32_t i, lp_count = 0;
+ VkLayerProperties* lp;
+
+ printf("Vulkan instance layers:\n");
+
+ if (vkEnumerateInstanceLayerProperties(&lp_count, NULL) != VK_SUCCESS)
+ return;
+
+ lp = calloc(lp_count, sizeof(VkLayerProperties));
+
+ if (vkEnumerateInstanceLayerProperties(&lp_count, lp) != VK_SUCCESS)
+ {
+ free(lp);
+ return;
+ }
+
+ for (i = 0; i < lp_count; i++)
+ {
+ printf(" %s (v%u) \"%s\"\n",
+ lp[i].layerName,
+ lp[i].specVersion >> 22,
+ lp[i].description);
+ }
+
+ free(lp);
+}
+
+static void list_vulkan_device_layers(VkInstance instance, VkPhysicalDevice device)
+{
+ uint32_t i, lp_count;
+ VkLayerProperties* lp;
+
+ printf("Vulkan device layers:\n");
+
+ if (vkEnumerateDeviceLayerProperties(device, &lp_count, NULL) != VK_SUCCESS)
+ return;
+
+ lp = calloc(lp_count, sizeof(VkLayerProperties));
+
+ if (vkEnumerateDeviceLayerProperties(device, &lp_count, lp) != VK_SUCCESS)
+ {
+ free(lp);
+ return;
+ }
+
+ for (i = 0; i < lp_count; i++)
+ {
+ printf(" %s (v%u) \"%s\"\n",
+ lp[i].layerName,
+ lp[i].specVersion >> 22,
+ lp[i].description);
+ }
+
+ free(lp);
+}
+
+static int valid_version(void)
+{
+ int major, minor, revision;
+ glfwGetVersion(&major, &minor, &revision);
+
+ if (major != GLFW_VERSION_MAJOR)
+ {
+ printf("*** ERROR: GLFW major version mismatch! ***\n");
+ return GLFW_FALSE;
+ }
+
+ if (minor != GLFW_VERSION_MINOR || revision != GLFW_VERSION_REVISION)
+ printf("*** WARNING: GLFW version mismatch! ***\n");
+
+ return GLFW_TRUE;
+}
+
+static void print_version(void)
+{
+ int major, minor, revision;
+ glfwGetVersion(&major, &minor, &revision);
+
+ printf("GLFW header version: %u.%u.%u\n",
+ GLFW_VERSION_MAJOR,
+ GLFW_VERSION_MINOR,
+ GLFW_VERSION_REVISION);
+ printf("GLFW library version: %u.%u.%u\n", major, minor, revision);
+ printf("GLFW library version string: \"%s\"\n", glfwGetVersionString());
+}
+
+int main(int argc, char** argv)
+{
+ int ch, client, major, minor, revision, profile;
+ GLint redbits, greenbits, bluebits, alphabits, depthbits, stencilbits;
+ int list_extensions = GLFW_FALSE, list_layers = GLFW_FALSE;
+ GLenum error;
+ GLFWwindow* window;
+
+ enum { CLIENT, CONTEXT, BEHAVIOR, DEBUG_CONTEXT, FORWARD, HELP,
+ EXTENSIONS, LAYERS,
+ MAJOR, MINOR, PROFILE, ROBUSTNESS, VERSION,
+ REDBITS, GREENBITS, BLUEBITS, ALPHABITS, DEPTHBITS, STENCILBITS,
+ ACCUMREDBITS, ACCUMGREENBITS, ACCUMBLUEBITS, ACCUMALPHABITS,
+ AUXBUFFERS, SAMPLES, STEREO, SRGB, SINGLEBUFFER, NOERROR_SRSLY,
+ GRAPHICS_SWITCHING };
+ const struct option options[] =
+ {
+ { "behavior", 1, NULL, BEHAVIOR },
+ { "client-api", 1, NULL, CLIENT },
+ { "context-api", 1, NULL, CONTEXT },
+ { "debug", 0, NULL, DEBUG_CONTEXT },
+ { "forward", 0, NULL, FORWARD },
+ { "help", 0, NULL, HELP },
+ { "list-extensions", 0, NULL, EXTENSIONS },
+ { "list-layers", 0, NULL, LAYERS },
+ { "major", 1, NULL, MAJOR },
+ { "minor", 1, NULL, MINOR },
+ { "profile", 1, NULL, PROFILE },
+ { "robustness", 1, NULL, ROBUSTNESS },
+ { "version", 0, NULL, VERSION },
+ { "red-bits", 1, NULL, REDBITS },
+ { "green-bits", 1, NULL, GREENBITS },
+ { "blue-bits", 1, NULL, BLUEBITS },
+ { "alpha-bits", 1, NULL, ALPHABITS },
+ { "depth-bits", 1, NULL, DEPTHBITS },
+ { "stencil-bits", 1, NULL, STENCILBITS },
+ { "accum-red-bits", 1, NULL, ACCUMREDBITS },
+ { "accum-green-bits", 1, NULL, ACCUMGREENBITS },
+ { "accum-blue-bits", 1, NULL, ACCUMBLUEBITS },
+ { "accum-alpha-bits", 1, NULL, ACCUMALPHABITS },
+ { "aux-buffers", 1, NULL, AUXBUFFERS },
+ { "samples", 1, NULL, SAMPLES },
+ { "stereo", 0, NULL, STEREO },
+ { "srgb", 0, NULL, SRGB },
+ { "singlebuffer", 0, NULL, SINGLEBUFFER },
+ { "no-error", 0, NULL, NOERROR_SRSLY },
+ { "graphics-switching", 0, NULL, GRAPHICS_SWITCHING },
+ { NULL, 0, NULL, 0 }
+ };
+
+ // Initialize GLFW and create window
+
+ if (!valid_version())
+ exit(EXIT_FAILURE);
+
+ glfwSetErrorCallback(error_callback);
+
+ glfwInitHint(GLFW_COCOA_MENUBAR, GLFW_FALSE);
+
+ if (!glfwInit())
+ exit(EXIT_FAILURE);
+
+ while ((ch = getopt_long(argc, argv, "a:b:c:dfhlm:n:p:s:v", options, NULL)) != -1)
+ {
+ switch (ch)
+ {
+ case 'a':
+ case CLIENT:
+ if (strcasecmp(optarg, API_NAME_OPENGL) == 0)
+ glfwWindowHint(GLFW_CLIENT_API, GLFW_OPENGL_API);
+ else if (strcasecmp(optarg, API_NAME_OPENGL_ES) == 0)
+ glfwWindowHint(GLFW_CLIENT_API, GLFW_OPENGL_ES_API);
+ else
+ {
+ usage();
+ exit(EXIT_FAILURE);
+ }
+ break;
+ case 'b':
+ case BEHAVIOR:
+ if (strcasecmp(optarg, BEHAVIOR_NAME_NONE) == 0)
+ {
+ glfwWindowHint(GLFW_CONTEXT_RELEASE_BEHAVIOR,
+ GLFW_RELEASE_BEHAVIOR_NONE);
+ }
+ else if (strcasecmp(optarg, BEHAVIOR_NAME_FLUSH) == 0)
+ {
+ glfwWindowHint(GLFW_CONTEXT_RELEASE_BEHAVIOR,
+ GLFW_RELEASE_BEHAVIOR_FLUSH);
+ }
+ else
+ {
+ usage();
+ exit(EXIT_FAILURE);
+ }
+ break;
+ case 'c':
+ case CONTEXT:
+ if (strcasecmp(optarg, API_NAME_NATIVE) == 0)
+ glfwWindowHint(GLFW_CONTEXT_CREATION_API, GLFW_NATIVE_CONTEXT_API);
+ else if (strcasecmp(optarg, API_NAME_EGL) == 0)
+ glfwWindowHint(GLFW_CONTEXT_CREATION_API, GLFW_EGL_CONTEXT_API);
+ else if (strcasecmp(optarg, API_NAME_OSMESA) == 0)
+ glfwWindowHint(GLFW_CONTEXT_CREATION_API, GLFW_OSMESA_CONTEXT_API);
+ else
+ {
+ usage();
+ exit(EXIT_FAILURE);
+ }
+ break;
+ case 'd':
+ case DEBUG_CONTEXT:
+ glfwWindowHint(GLFW_OPENGL_DEBUG_CONTEXT, GLFW_TRUE);
+ break;
+ case 'f':
+ case FORWARD:
+ glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GLFW_TRUE);
+ break;
+ case 'h':
+ case HELP:
+ usage();
+ exit(EXIT_SUCCESS);
+ case 'l':
+ case EXTENSIONS:
+ list_extensions = GLFW_TRUE;
+ break;
+ case LAYERS:
+ list_layers = GLFW_TRUE;
+ break;
+ case 'm':
+ case MAJOR:
+ glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, atoi(optarg));
+ break;
+ case 'n':
+ case MINOR:
+ glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, atoi(optarg));
+ break;
+ case 'p':
+ case PROFILE:
+ if (strcasecmp(optarg, PROFILE_NAME_CORE) == 0)
+ {
+ glfwWindowHint(GLFW_OPENGL_PROFILE,
+ GLFW_OPENGL_CORE_PROFILE);
+ }
+ else if (strcasecmp(optarg, PROFILE_NAME_COMPAT) == 0)
+ {
+ glfwWindowHint(GLFW_OPENGL_PROFILE,
+ GLFW_OPENGL_COMPAT_PROFILE);
+ }
+ else
+ {
+ usage();
+ exit(EXIT_FAILURE);
+ }
+ break;
+ case 's':
+ case ROBUSTNESS:
+ if (strcasecmp(optarg, STRATEGY_NAME_NONE) == 0)
+ {
+ glfwWindowHint(GLFW_CONTEXT_ROBUSTNESS,
+ GLFW_NO_RESET_NOTIFICATION);
+ }
+ else if (strcasecmp(optarg, STRATEGY_NAME_LOSE) == 0)
+ {
+ glfwWindowHint(GLFW_CONTEXT_ROBUSTNESS,
+ GLFW_LOSE_CONTEXT_ON_RESET);
+ }
+ else
+ {
+ usage();
+ exit(EXIT_FAILURE);
+ }
+ break;
+ case 'v':
+ case VERSION:
+ print_version();
+ exit(EXIT_SUCCESS);
+ case REDBITS:
+ if (strcmp(optarg, "-") == 0)
+ glfwWindowHint(GLFW_RED_BITS, GLFW_DONT_CARE);
+ else
+ glfwWindowHint(GLFW_RED_BITS, atoi(optarg));
+ break;
+ case GREENBITS:
+ if (strcmp(optarg, "-") == 0)
+ glfwWindowHint(GLFW_GREEN_BITS, GLFW_DONT_CARE);
+ else
+ glfwWindowHint(GLFW_GREEN_BITS, atoi(optarg));
+ break;
+ case BLUEBITS:
+ if (strcmp(optarg, "-") == 0)
+ glfwWindowHint(GLFW_BLUE_BITS, GLFW_DONT_CARE);
+ else
+ glfwWindowHint(GLFW_BLUE_BITS, atoi(optarg));
+ break;
+ case ALPHABITS:
+ if (strcmp(optarg, "-") == 0)
+ glfwWindowHint(GLFW_ALPHA_BITS, GLFW_DONT_CARE);
+ else
+ glfwWindowHint(GLFW_ALPHA_BITS, atoi(optarg));
+ break;
+ case DEPTHBITS:
+ if (strcmp(optarg, "-") == 0)
+ glfwWindowHint(GLFW_DEPTH_BITS, GLFW_DONT_CARE);
+ else
+ glfwWindowHint(GLFW_DEPTH_BITS, atoi(optarg));
+ break;
+ case STENCILBITS:
+ if (strcmp(optarg, "-") == 0)
+ glfwWindowHint(GLFW_STENCIL_BITS, GLFW_DONT_CARE);
+ else
+ glfwWindowHint(GLFW_STENCIL_BITS, atoi(optarg));
+ break;
+ case ACCUMREDBITS:
+ if (strcmp(optarg, "-") == 0)
+ glfwWindowHint(GLFW_ACCUM_RED_BITS, GLFW_DONT_CARE);
+ else
+ glfwWindowHint(GLFW_ACCUM_RED_BITS, atoi(optarg));
+ break;
+ case ACCUMGREENBITS:
+ if (strcmp(optarg, "-") == 0)
+ glfwWindowHint(GLFW_ACCUM_GREEN_BITS, GLFW_DONT_CARE);
+ else
+ glfwWindowHint(GLFW_ACCUM_GREEN_BITS, atoi(optarg));
+ break;
+ case ACCUMBLUEBITS:
+ if (strcmp(optarg, "-") == 0)
+ glfwWindowHint(GLFW_ACCUM_BLUE_BITS, GLFW_DONT_CARE);
+ else
+ glfwWindowHint(GLFW_ACCUM_BLUE_BITS, atoi(optarg));
+ break;
+ case ACCUMALPHABITS:
+ if (strcmp(optarg, "-") == 0)
+ glfwWindowHint(GLFW_ACCUM_ALPHA_BITS, GLFW_DONT_CARE);
+ else
+ glfwWindowHint(GLFW_ACCUM_ALPHA_BITS, atoi(optarg));
+ break;
+ case AUXBUFFERS:
+ if (strcmp(optarg, "-") == 0)
+ glfwWindowHint(GLFW_AUX_BUFFERS, GLFW_DONT_CARE);
+ else
+ glfwWindowHint(GLFW_AUX_BUFFERS, atoi(optarg));
+ break;
+ case SAMPLES:
+ if (strcmp(optarg, "-") == 0)
+ glfwWindowHint(GLFW_SAMPLES, GLFW_DONT_CARE);
+ else
+ glfwWindowHint(GLFW_SAMPLES, atoi(optarg));
+ break;
+ case STEREO:
+ glfwWindowHint(GLFW_STEREO, GLFW_TRUE);
+ break;
+ case SRGB:
+ glfwWindowHint(GLFW_SRGB_CAPABLE, GLFW_TRUE);
+ break;
+ case SINGLEBUFFER:
+ glfwWindowHint(GLFW_DOUBLEBUFFER, GLFW_FALSE);
+ break;
+ case NOERROR_SRSLY:
+ glfwWindowHint(GLFW_CONTEXT_NO_ERROR, GLFW_TRUE);
+ break;
+ case GRAPHICS_SWITCHING:
+ glfwWindowHint(GLFW_COCOA_GRAPHICS_SWITCHING, GLFW_TRUE);
+ break;
+ default:
+ usage();
+ exit(EXIT_FAILURE);
+ }
+ }
+
+ print_version();
+
+ glfwWindowHint(GLFW_VISIBLE, GLFW_FALSE);
+
+ window = glfwCreateWindow(200, 200, "Version", NULL, NULL);
+ if (!window)
+ {
+ glfwTerminate();
+ exit(EXIT_FAILURE);
+ }
+
+ glfwMakeContextCurrent(window);
+ gladLoadGL(glfwGetProcAddress);
+
+ error = glGetError();
+ if (error != GL_NO_ERROR)
+ printf("*** OpenGL error after make current: 0x%08x ***\n", error);
+
+ // Report client API version
+
+ client = glfwGetWindowAttrib(window, GLFW_CLIENT_API);
+ major = glfwGetWindowAttrib(window, GLFW_CONTEXT_VERSION_MAJOR);
+ minor = glfwGetWindowAttrib(window, GLFW_CONTEXT_VERSION_MINOR);
+ revision = glfwGetWindowAttrib(window, GLFW_CONTEXT_REVISION);
+ profile = glfwGetWindowAttrib(window, GLFW_OPENGL_PROFILE);
+
+ printf("%s context version string: \"%s\"\n",
+ get_api_name(client),
+ glGetString(GL_VERSION));
+
+ printf("%s context version parsed by GLFW: %u.%u.%u\n",
+ get_api_name(client),
+ major, minor, revision);
+
+ // Report client API context properties
+
+ if (client == GLFW_OPENGL_API)
+ {
+ if (major >= 3)
+ {
+ GLint flags;
+
+ glGetIntegerv(GL_CONTEXT_FLAGS, &flags);
+ printf("%s context flags (0x%08x):", get_api_name(client), flags);
+
+ if (flags & GL_CONTEXT_FLAG_FORWARD_COMPATIBLE_BIT)
+ printf(" forward-compatible");
+ if (flags & 2/*GL_CONTEXT_FLAG_DEBUG_BIT*/)
+ printf(" debug");
+ if (flags & GL_CONTEXT_FLAG_ROBUST_ACCESS_BIT_ARB)
+ printf(" robustness");
+ if (flags & 8/*GL_CONTEXT_FLAG_NO_ERROR_BIT_KHR*/)
+ printf(" no-error");
+ putchar('\n');
+
+ printf("%s context flags parsed by GLFW:", get_api_name(client));
+
+ if (glfwGetWindowAttrib(window, GLFW_OPENGL_FORWARD_COMPAT))
+ printf(" forward-compatible");
+ if (glfwGetWindowAttrib(window, GLFW_OPENGL_DEBUG_CONTEXT))
+ printf(" debug");
+ if (glfwGetWindowAttrib(window, GLFW_CONTEXT_ROBUSTNESS) == GLFW_LOSE_CONTEXT_ON_RESET)
+ printf(" robustness");
+ if (glfwGetWindowAttrib(window, GLFW_CONTEXT_NO_ERROR))
+ printf(" no-error");
+ putchar('\n');
+ }
+
+ if (major >= 4 || (major == 3 && minor >= 2))
+ {
+ GLint mask;
+ glGetIntegerv(GL_CONTEXT_PROFILE_MASK, &mask);
+
+ printf("%s profile mask (0x%08x): %s\n",
+ get_api_name(client),
+ mask,
+ get_profile_name_gl(mask));
+
+ printf("%s profile mask parsed by GLFW: %s\n",
+ get_api_name(client),
+ get_profile_name_glfw(profile));
+ }
+
+ if (GLAD_GL_ARB_robustness)
+ {
+ const int robustness = glfwGetWindowAttrib(window, GLFW_CONTEXT_ROBUSTNESS);
+ GLint strategy;
+ glGetIntegerv(GL_RESET_NOTIFICATION_STRATEGY_ARB, &strategy);
+
+ printf("%s robustness strategy (0x%08x): %s\n",
+ get_api_name(client),
+ strategy,
+ get_strategy_name_gl(strategy));
+
+ printf("%s robustness strategy parsed by GLFW: %s\n",
+ get_api_name(client),
+ get_strategy_name_glfw(robustness));
+ }
+ }
+
+ printf("%s context renderer string: \"%s\"\n",
+ get_api_name(client),
+ glGetString(GL_RENDERER));
+ printf("%s context vendor string: \"%s\"\n",
+ get_api_name(client),
+ glGetString(GL_VENDOR));
+
+ if (major >= 2)
+ {
+ printf("%s context shading language version: \"%s\"\n",
+ get_api_name(client),
+ glGetString(GL_SHADING_LANGUAGE_VERSION));
+ }
+
+ printf("%s framebuffer:\n", get_api_name(client));
+
+ if (client == GLFW_OPENGL_API && profile == GLFW_OPENGL_CORE_PROFILE)
+ {
+ glGetFramebufferAttachmentParameteriv(GL_FRAMEBUFFER,
+ GL_BACK_LEFT,
+ GL_FRAMEBUFFER_ATTACHMENT_RED_SIZE,
+ &redbits);
+ glGetFramebufferAttachmentParameteriv(GL_FRAMEBUFFER,
+ GL_BACK_LEFT,
+ GL_FRAMEBUFFER_ATTACHMENT_GREEN_SIZE,
+ &greenbits);
+ glGetFramebufferAttachmentParameteriv(GL_FRAMEBUFFER,
+ GL_BACK_LEFT,
+ GL_FRAMEBUFFER_ATTACHMENT_BLUE_SIZE,
+ &bluebits);
+ glGetFramebufferAttachmentParameteriv(GL_FRAMEBUFFER,
+ GL_BACK_LEFT,
+ GL_FRAMEBUFFER_ATTACHMENT_ALPHA_SIZE,
+ &alphabits);
+ glGetFramebufferAttachmentParameteriv(GL_FRAMEBUFFER,
+ GL_DEPTH,
+ GL_FRAMEBUFFER_ATTACHMENT_DEPTH_SIZE,
+ &depthbits);
+ glGetFramebufferAttachmentParameteriv(GL_FRAMEBUFFER,
+ GL_STENCIL,
+ GL_FRAMEBUFFER_ATTACHMENT_STENCIL_SIZE,
+ &stencilbits);
+ }
+ else
+ {
+ glGetIntegerv(GL_RED_BITS, &redbits);
+ glGetIntegerv(GL_GREEN_BITS, &greenbits);
+ glGetIntegerv(GL_BLUE_BITS, &bluebits);
+ glGetIntegerv(GL_ALPHA_BITS, &alphabits);
+ glGetIntegerv(GL_DEPTH_BITS, &depthbits);
+ glGetIntegerv(GL_STENCIL_BITS, &stencilbits);
+ }
+
+ printf(" red: %u green: %u blue: %u alpha: %u depth: %u stencil: %u\n",
+ redbits, greenbits, bluebits, alphabits, depthbits, stencilbits);
+
+ if (client == GLFW_OPENGL_ES_API ||
+ GLAD_GL_ARB_multisample ||
+ major > 1 || minor >= 3)
+ {
+ GLint samples, samplebuffers;
+ glGetIntegerv(GL_SAMPLES, &samples);
+ glGetIntegerv(GL_SAMPLE_BUFFERS, &samplebuffers);
+
+ printf(" samples: %u sample buffers: %u\n", samples, samplebuffers);
+ }
+
+ if (client == GLFW_OPENGL_API && profile != GLFW_OPENGL_CORE_PROFILE)
+ {
+ GLint accumredbits, accumgreenbits, accumbluebits, accumalphabits;
+ GLint auxbuffers;
+
+ glGetIntegerv(GL_ACCUM_RED_BITS, &accumredbits);
+ glGetIntegerv(GL_ACCUM_GREEN_BITS, &accumgreenbits);
+ glGetIntegerv(GL_ACCUM_BLUE_BITS, &accumbluebits);
+ glGetIntegerv(GL_ACCUM_ALPHA_BITS, &accumalphabits);
+ glGetIntegerv(GL_AUX_BUFFERS, &auxbuffers);
+
+ printf(" accum red: %u accum green: %u accum blue: %u accum alpha: %u aux buffers: %u\n",
+ accumredbits, accumgreenbits, accumbluebits, accumalphabits, auxbuffers);
+ }
+
+ if (list_extensions)
+ list_context_extensions(client, major, minor);
+
+ printf("Vulkan loader: %s\n",
+ glfwVulkanSupported() ? "available" : "missing");
+
+ if (glfwVulkanSupported())
+ {
+ uint32_t loader_version = VK_API_VERSION_1_0;
+ int portability_enumeration = GLFW_FALSE;
+ uint32_t i, j, glfw_re_count, re_count, pd_count, ep_count;
+ const char** glfw_re;
+ const char** re;
+ VkApplicationInfo ai = {0};
+ VkInstanceCreateInfo ici = {0};
+ VkInstance instance;
+ VkPhysicalDevice* pd;
+
+ gladLoadVulkanUserPtr(NULL, (GLADuserptrloadfunc) glfwGetInstanceProcAddress, NULL);
+
+ if (vkEnumerateInstanceVersion)
+ {
+ uint32_t version;
+ if (vkEnumerateInstanceVersion(&version) == VK_SUCCESS)
+ loader_version = version;
+ }
+
+ printf("Vulkan loader API version: %i.%i\n",
+ VK_VERSION_MAJOR(loader_version),
+ VK_VERSION_MINOR(loader_version));
+
+ glfw_re = glfwGetRequiredInstanceExtensions(&glfw_re_count);
+
+ re_count = glfw_re_count;
+ re = calloc(glfw_re_count, sizeof(char*));
+
+ printf("Vulkan window surface required instance extensions:\n");
+ if (glfw_re)
+ {
+ for (i = 0; i < glfw_re_count; i++)
+ {
+ printf(" %s\n", glfw_re[i]);
+ re[i] = glfw_re[i];
+ }
+ }
+ else
+ printf(" missing\n");
+
+ vkEnumerateInstanceExtensionProperties(NULL, &ep_count, NULL);
+ VkExtensionProperties* ep = calloc(ep_count, sizeof(VkExtensionProperties));
+ vkEnumerateInstanceExtensionProperties(NULL, &ep_count, ep);
+
+ if (list_extensions)
+ {
+ printf("Vulkan instance extensions:\n");
+
+ for (i = 0; i < ep_count; i++)
+ printf(" %s (spec version %u)\n", ep[i].extensionName, ep[i].specVersion);
+ }
+
+ for (i = 0; i < ep_count; i++)
+ {
+ if (strcmp(ep[i].extensionName, "VK_KHR_portability_enumeration") != 0)
+ continue;
+
+ re_count++;
+ re = realloc((void*) re, sizeof(char*) * re_count);
+ re[re_count - 1] = "VK_KHR_portability_enumeration";
+ portability_enumeration = GLFW_TRUE;
+ }
+
+ free(ep);
+
+ if (list_layers)
+ list_vulkan_instance_layers();
+
+ ai.sType = VK_STRUCTURE_TYPE_APPLICATION_INFO;
+ ai.pApplicationName = "glfwinfo";
+ ai.applicationVersion = VK_MAKE_VERSION(GLFW_VERSION_MAJOR,
+ GLFW_VERSION_MINOR,
+ GLFW_VERSION_REVISION);
+
+ if (loader_version >= VK_API_VERSION_1_1)
+ ai.apiVersion = VK_API_VERSION_1_1;
+ else
+ ai.apiVersion = VK_API_VERSION_1_0;
+
+ ici.sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO;
+ ici.pApplicationInfo = &ai;
+ ici.enabledExtensionCount = re_count;
+ ici.ppEnabledExtensionNames = re;
+
+ if (portability_enumeration)
+ ici.flags |= VK_INSTANCE_CREATE_ENUMERATE_PORTABILITY_BIT_KHR;
+
+ if (vkCreateInstance(&ici, NULL, &instance) != VK_SUCCESS)
+ {
+ glfwTerminate();
+ exit(EXIT_FAILURE);
+ }
+
+ free((void*) re);
+
+ gladLoadVulkanUserPtr(NULL, (GLADuserptrloadfunc) glfwGetInstanceProcAddress, instance);
+
+ vkEnumeratePhysicalDevices(instance, &pd_count, NULL);
+ pd = calloc(pd_count, sizeof(VkPhysicalDevice));
+ vkEnumeratePhysicalDevices(instance, &pd_count, pd);
+
+ for (i = 0; i < pd_count; i++)
+ {
+ VkPhysicalDeviceProperties pdp;
+ uint32_t qfp_count, ep_count;
+
+ vkGetPhysicalDeviceProperties(pd[i], &pdp);
+
+ printf("Vulkan %s device: \"%s\" API version %i.%i\n",
+ get_device_type_name(pdp.deviceType),
+ pdp.deviceName,
+ VK_VERSION_MAJOR(pdp.apiVersion),
+ VK_VERSION_MINOR(pdp.apiVersion));
+
+ vkGetPhysicalDeviceQueueFamilyProperties(pd[i], &qfp_count, NULL);
+
+ vkEnumerateDeviceExtensionProperties(pd[i], NULL, &ep_count, NULL);
+ VkExtensionProperties* ep = calloc(ep_count, sizeof(VkExtensionProperties));
+ vkEnumerateDeviceExtensionProperties(pd[i], NULL, &ep_count, ep);
+
+ if (portability_enumeration)
+ {
+ int conformant = GLFW_TRUE;
+
+ for (j = 0; j < ep_count; j++)
+ {
+ if (strcmp(ep[j].extensionName, "VK_KHR_portability_subset") == 0)
+ {
+ conformant = GLFW_FALSE;
+ break;
+ }
+ }
+
+ printf("Vulkan %s %s device: \"%s\" (API version %i.%i)\n",
+ conformant ? "conformant" : "non-conformant",
+ get_device_type_name(pdp.deviceType),
+ pdp.deviceName,
+ VK_VERSION_MAJOR(pdp.apiVersion),
+ VK_VERSION_MINOR(pdp.apiVersion));
+ }
+ else
+ {
+ printf("Vulkan %s device: \"%s\" (API version %i.%i)\n",
+ get_device_type_name(pdp.deviceType),
+ pdp.deviceName,
+ VK_VERSION_MAJOR(pdp.apiVersion),
+ VK_VERSION_MINOR(pdp.apiVersion));
+ }
+
+ if (glfw_re_count)
+ {
+ printf("Vulkan device queue family presentation support:\n");
+ for (j = 0; j < qfp_count; j++)
+ {
+ printf(" %u: ", j);
+ if (glfwGetPhysicalDevicePresentationSupport(instance, pd[i], j))
+ printf("supported\n");
+ else
+ printf("no\n");
+ }
+ }
+
+ if (list_extensions)
+ {
+ printf("Vulkan device extensions:\n");
+ for (j = 0; j < ep_count; j++)
+ printf(" %s (spec version %u)\n", ep[j].extensionName, ep[j].specVersion);
+ }
+
+ free(ep);
+
+ if (list_layers)
+ list_vulkan_device_layers(instance, pd[i]);
+ }
+
+ free(pd);
+ vkDestroyInstance(instance, NULL);
+ }
+
+ glfwTerminate();
+ exit(EXIT_SUCCESS);
+}
+
diff --git a/libs/glfw-3.3.8/tests/icon.c b/libs/glfw-3.3.8/tests/icon.c
new file mode 100644
index 0000000..aa7ee18
--- /dev/null
+++ b/libs/glfw-3.3.8/tests/icon.c
@@ -0,0 +1,149 @@
+//========================================================================
+// Window icon test program
+// Copyright (c) Camilla Löwy <elmindreda@glfw.org>
+//
+// 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.
+//
+//========================================================================
+//
+// This program is used to test the icon feature.
+//
+//========================================================================
+
+#include <glad/gl.h>
+#define GLFW_INCLUDE_NONE
+#include <GLFW/glfw3.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+// a simple glfw logo
+const char* const logo[] =
+{
+ "................",
+ "................",
+ "...0000..0......",
+ "...0.....0......",
+ "...0.00..0......",
+ "...0..0..0......",
+ "...0000..0000...",
+ "................",
+ "................",
+ "...000..0...0...",
+ "...0....0...0...",
+ "...000..0.0.0...",
+ "...0....0.0.0...",
+ "...0....00000...",
+ "................",
+ "................"
+};
+
+const unsigned char icon_colors[5][4] =
+{
+ { 0, 0, 0, 255 }, // black
+ { 255, 0, 0, 255 }, // red
+ { 0, 255, 0, 255 }, // green
+ { 0, 0, 255, 255 }, // blue
+ { 255, 255, 255, 255 } // white
+};
+
+static int cur_icon_color = 0;
+
+static void set_icon(GLFWwindow* window, int icon_color)
+{
+ int x, y;
+ unsigned char pixels[16 * 16 * 4];
+ unsigned char* target = pixels;
+ GLFWimage img = { 16, 16, pixels };
+
+ for (y = 0; y < img.width; y++)
+ {
+ for (x = 0; x < img.height; x++)
+ {
+ if (logo[y][x] == '0')
+ memcpy(target, icon_colors[icon_color], 4);
+ else
+ memset(target, 0, 4);
+
+ target += 4;
+ }
+ }
+
+ glfwSetWindowIcon(window, 1, &img);
+}
+
+static void key_callback(GLFWwindow* window, int key, int scancode, int action, int mods)
+{
+ if (action != GLFW_PRESS)
+ return;
+
+ switch (key)
+ {
+ case GLFW_KEY_ESCAPE:
+ glfwSetWindowShouldClose(window, GLFW_TRUE);
+ break;
+ case GLFW_KEY_SPACE:
+ cur_icon_color = (cur_icon_color + 1) % 5;
+ set_icon(window, cur_icon_color);
+ break;
+ case GLFW_KEY_X:
+ glfwSetWindowIcon(window, 0, NULL);
+ break;
+ }
+}
+
+int main(int argc, char** argv)
+{
+ GLFWwindow* window;
+
+ if (!glfwInit())
+ {
+ fprintf(stderr, "Failed to initialize GLFW\n");
+ exit(EXIT_FAILURE);
+ }
+
+ window = glfwCreateWindow(200, 200, "Window Icon", NULL, NULL);
+ if (!window)
+ {
+ glfwTerminate();
+
+ fprintf(stderr, "Failed to open GLFW window\n");
+ exit(EXIT_FAILURE);
+ }
+
+ glfwMakeContextCurrent(window);
+ gladLoadGL(glfwGetProcAddress);
+
+ glfwSetKeyCallback(window, key_callback);
+ set_icon(window, cur_icon_color);
+
+ while (!glfwWindowShouldClose(window))
+ {
+ glClear(GL_COLOR_BUFFER_BIT);
+ glfwSwapBuffers(window);
+ glfwWaitEvents();
+ }
+
+ glfwDestroyWindow(window);
+ glfwTerminate();
+ exit(EXIT_SUCCESS);
+}
+
diff --git a/libs/glfw-3.3.8/tests/iconify.c b/libs/glfw-3.3.8/tests/iconify.c
new file mode 100644
index 0000000..27dcdf9
--- /dev/null
+++ b/libs/glfw-3.3.8/tests/iconify.c
@@ -0,0 +1,297 @@
+//========================================================================
+// Iconify/restore test program
+// Copyright (c) Camilla Löwy <elmindreda@glfw.org>
+//
+// 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.
+//
+//========================================================================
+//
+// This program is used to test the iconify/restore functionality for
+// both full screen and windowed mode windows
+//
+//========================================================================
+
+#include <glad/gl.h>
+#define GLFW_INCLUDE_NONE
+#include <GLFW/glfw3.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+
+#include "getopt.h"
+
+static int windowed_xpos, windowed_ypos, windowed_width = 640, windowed_height = 480;
+
+static void usage(void)
+{
+ printf("Usage: iconify [-h] [-f [-a] [-n]]\n");
+ printf("Options:\n");
+ printf(" -a create windows for all monitors\n");
+ printf(" -f create full screen window(s)\n");
+ printf(" -h show this help\n");
+}
+
+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)
+{
+ printf("%0.2f Key %s\n",
+ glfwGetTime(),
+ action == GLFW_PRESS ? "pressed" : "released");
+
+ if (action != GLFW_PRESS)
+ return;
+
+ switch (key)
+ {
+ case GLFW_KEY_I:
+ glfwIconifyWindow(window);
+ break;
+ case GLFW_KEY_M:
+ glfwMaximizeWindow(window);
+ break;
+ case GLFW_KEY_R:
+ glfwRestoreWindow(window);
+ break;
+ case GLFW_KEY_ESCAPE:
+ glfwSetWindowShouldClose(window, GLFW_TRUE);
+ break;
+ case GLFW_KEY_A:
+ glfwSetWindowAttrib(window, GLFW_AUTO_ICONIFY, !glfwGetWindowAttrib(window, GLFW_AUTO_ICONIFY));
+ break;
+ case GLFW_KEY_B:
+ glfwSetWindowAttrib(window, GLFW_RESIZABLE, !glfwGetWindowAttrib(window, GLFW_RESIZABLE));
+ break;
+ case GLFW_KEY_D:
+ glfwSetWindowAttrib(window, GLFW_DECORATED, !glfwGetWindowAttrib(window, GLFW_DECORATED));
+ break;
+ case GLFW_KEY_F:
+ glfwSetWindowAttrib(window, GLFW_FLOATING, !glfwGetWindowAttrib(window, GLFW_FLOATING));
+ break;
+ case GLFW_KEY_F11:
+ case GLFW_KEY_ENTER:
+ {
+ if (mods != GLFW_MOD_ALT)
+ return;
+
+ if (glfwGetWindowMonitor(window))
+ {
+ glfwSetWindowMonitor(window, NULL,
+ windowed_xpos, windowed_ypos,
+ windowed_width, windowed_height,
+ 0);
+ }
+ else
+ {
+ GLFWmonitor* monitor = glfwGetPrimaryMonitor();
+ if (monitor)
+ {
+ const GLFWvidmode* mode = glfwGetVideoMode(monitor);
+ glfwGetWindowPos(window, &windowed_xpos, &windowed_ypos);
+ glfwGetWindowSize(window, &windowed_width, &windowed_height);
+ glfwSetWindowMonitor(window, monitor,
+ 0, 0, mode->width, mode->height,
+ mode->refreshRate);
+ }
+ }
+
+ break;
+ }
+ }
+}
+
+static void window_size_callback(GLFWwindow* window, int width, int height)
+{
+ printf("%0.2f Window resized to %ix%i\n", glfwGetTime(), width, height);
+}
+
+static void framebuffer_size_callback(GLFWwindow* window, int width, int height)
+{
+ printf("%0.2f Framebuffer resized to %ix%i\n", glfwGetTime(), width, height);
+}
+
+static void window_focus_callback(GLFWwindow* window, int focused)
+{
+ printf("%0.2f Window %s\n",
+ glfwGetTime(),
+ focused ? "focused" : "defocused");
+}
+
+static void window_iconify_callback(GLFWwindow* window, int iconified)
+{
+ printf("%0.2f Window %s\n",
+ glfwGetTime(),
+ iconified ? "iconified" : "uniconified");
+}
+
+static void window_maximize_callback(GLFWwindow* window, int maximized)
+{
+ printf("%0.2f Window %s\n",
+ glfwGetTime(),
+ maximized ? "maximized" : "unmaximized");
+}
+
+static void window_refresh_callback(GLFWwindow* window)
+{
+ printf("%0.2f Window refresh\n", glfwGetTime());
+
+ glfwMakeContextCurrent(window);
+
+ glClear(GL_COLOR_BUFFER_BIT);
+ glfwSwapBuffers(window);
+}
+
+static GLFWwindow* create_window(GLFWmonitor* monitor)
+{
+ int width, height;
+ GLFWwindow* window;
+
+ if (monitor)
+ {
+ const GLFWvidmode* mode = glfwGetVideoMode(monitor);
+
+ glfwWindowHint(GLFW_REFRESH_RATE, mode->refreshRate);
+ glfwWindowHint(GLFW_RED_BITS, mode->redBits);
+ glfwWindowHint(GLFW_GREEN_BITS, mode->greenBits);
+ glfwWindowHint(GLFW_BLUE_BITS, mode->blueBits);
+
+ width = mode->width;
+ height = mode->height;
+ }
+ else
+ {
+ width = windowed_width;
+ height = windowed_height;
+ }
+
+ window = glfwCreateWindow(width, height, "Iconify", monitor, NULL);
+ if (!window)
+ {
+ glfwTerminate();
+ exit(EXIT_FAILURE);
+ }
+
+ glfwMakeContextCurrent(window);
+ gladLoadGL(glfwGetProcAddress);
+
+ return window;
+}
+
+int main(int argc, char** argv)
+{
+ int ch, i, window_count;
+ int fullscreen = GLFW_FALSE, all_monitors = GLFW_FALSE;
+ GLFWwindow** windows;
+
+ while ((ch = getopt(argc, argv, "afhn")) != -1)
+ {
+ switch (ch)
+ {
+ case 'a':
+ all_monitors = GLFW_TRUE;
+ break;
+
+ case 'h':
+ usage();
+ exit(EXIT_SUCCESS);
+
+ case 'f':
+ fullscreen = GLFW_TRUE;
+ break;
+
+ default:
+ usage();
+ exit(EXIT_FAILURE);
+ }
+ }
+
+ glfwSetErrorCallback(error_callback);
+
+ if (!glfwInit())
+ exit(EXIT_FAILURE);
+
+ if (fullscreen && all_monitors)
+ {
+ int monitor_count;
+ GLFWmonitor** monitors = glfwGetMonitors(&monitor_count);
+
+ window_count = monitor_count;
+ windows = calloc(window_count, sizeof(GLFWwindow*));
+
+ for (i = 0; i < monitor_count; i++)
+ {
+ windows[i] = create_window(monitors[i]);
+ if (!windows[i])
+ break;
+ }
+ }
+ else
+ {
+ GLFWmonitor* monitor = NULL;
+
+ if (fullscreen)
+ monitor = glfwGetPrimaryMonitor();
+
+ window_count = 1;
+ windows = calloc(window_count, sizeof(GLFWwindow*));
+ windows[0] = create_window(monitor);
+ }
+
+ for (i = 0; i < window_count; i++)
+ {
+ glfwSetKeyCallback(windows[i], key_callback);
+ glfwSetFramebufferSizeCallback(windows[i], framebuffer_size_callback);
+ glfwSetWindowSizeCallback(windows[i], window_size_callback);
+ glfwSetWindowFocusCallback(windows[i], window_focus_callback);
+ glfwSetWindowIconifyCallback(windows[i], window_iconify_callback);
+ glfwSetWindowMaximizeCallback(windows[i], window_maximize_callback);
+ glfwSetWindowRefreshCallback(windows[i], window_refresh_callback);
+
+ window_refresh_callback(windows[i]);
+
+ printf("Window is %s and %s\n",
+ glfwGetWindowAttrib(windows[i], GLFW_ICONIFIED) ? "iconified" : "restored",
+ glfwGetWindowAttrib(windows[i], GLFW_FOCUSED) ? "focused" : "defocused");
+ }
+
+ for (;;)
+ {
+ glfwWaitEvents();
+
+ for (i = 0; i < window_count; i++)
+ {
+ if (glfwWindowShouldClose(windows[i]))
+ break;
+ }
+
+ if (i < window_count)
+ break;
+
+ // Workaround for an issue with msvcrt and mintty
+ fflush(stdout);
+ }
+
+ glfwTerminate();
+ exit(EXIT_SUCCESS);
+}
+
diff --git a/libs/glfw-3.3.8/tests/inputlag.c b/libs/glfw-3.3.8/tests/inputlag.c
new file mode 100644
index 0000000..269a0c8
--- /dev/null
+++ b/libs/glfw-3.3.8/tests/inputlag.c
@@ -0,0 +1,308 @@
+//========================================================================
+// Input lag test
+// Copyright (c) Camilla Löwy <elmindreda@glfw.org>
+//
+// 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.
+//
+//========================================================================
+//
+// This test renders a marker at the cursor position reported by GLFW to
+// check how much it lags behind the hardware mouse cursor
+//
+//========================================================================
+
+#include <glad/gl.h>
+#define GLFW_INCLUDE_NONE
+#include <GLFW/glfw3.h>
+
+#define NK_IMPLEMENTATION
+#define NK_INCLUDE_FIXED_TYPES
+#define NK_INCLUDE_FONT_BAKING
+#define NK_INCLUDE_DEFAULT_FONT
+#define NK_INCLUDE_DEFAULT_ALLOCATOR
+#define NK_INCLUDE_VERTEX_BUFFER_OUTPUT
+#define NK_INCLUDE_STANDARD_VARARGS
+#include <nuklear.h>
+
+#define NK_GLFW_GL2_IMPLEMENTATION
+#include <nuklear_glfw_gl2.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "getopt.h"
+
+void usage(void)
+{
+ printf("Usage: inputlag [-h] [-f]\n");
+ printf("Options:\n");
+ printf(" -f create full screen window\n");
+ printf(" -h show this help\n");
+}
+
+struct nk_vec2 cursor_new, cursor_pos, cursor_vel;
+enum { cursor_sync_query, cursor_input_message } cursor_method = cursor_sync_query;
+
+void sample_input(GLFWwindow* window)
+{
+ float a = .25; // exponential smoothing factor
+
+ if (cursor_method == cursor_sync_query) {
+ double x, y;
+ glfwGetCursorPos(window, &x, &y);
+ cursor_new.x = (float) x;
+ cursor_new.y = (float) y;
+ }
+
+ cursor_vel.x = (cursor_new.x - cursor_pos.x) * a + cursor_vel.x * (1 - a);
+ cursor_vel.y = (cursor_new.y - cursor_pos.y) * a + cursor_vel.y * (1 - a);
+ cursor_pos = cursor_new;
+}
+
+void cursor_pos_callback(GLFWwindow* window, double xpos, double ypos)
+{
+ cursor_new.x = (float) xpos;
+ cursor_new.y = (float) ypos;
+}
+
+int enable_vsync = nk_true;
+
+void update_vsync()
+{
+ glfwSwapInterval(enable_vsync == nk_true ? 1 : 0);
+}
+
+int swap_clear = nk_false;
+int swap_finish = nk_true;
+int swap_occlusion_query = nk_false;
+int swap_read_pixels = nk_false;
+GLuint occlusion_query;
+
+void swap_buffers(GLFWwindow* window)
+{
+ glfwSwapBuffers(window);
+
+ if (swap_clear)
+ glClear(GL_COLOR_BUFFER_BIT);
+
+ if (swap_finish)
+ glFinish();
+
+ if (swap_occlusion_query) {
+ GLint occlusion_result;
+ if (!occlusion_query)
+ glGenQueries(1, &occlusion_query);
+ glBeginQuery(GL_SAMPLES_PASSED, occlusion_query);
+ glBegin(GL_POINTS);
+ glVertex2f(0, 0);
+ glEnd();
+ glEndQuery(GL_SAMPLES_PASSED);
+ glGetQueryObjectiv(occlusion_query, GL_QUERY_RESULT, &occlusion_result);
+ }
+
+ if (swap_read_pixels) {
+ unsigned char rgba[4];
+ glReadPixels(0, 0, 1, 1, GL_RGBA, GL_UNSIGNED_BYTE, rgba);
+ }
+}
+
+void error_callback(int error, const char* description)
+{
+ fprintf(stderr, "Error: %s\n", description);
+}
+
+void key_callback(GLFWwindow* window, int key, int scancode, int action, int mods)
+{
+ if (action != GLFW_PRESS)
+ return;
+
+ switch (key)
+ {
+ case GLFW_KEY_ESCAPE:
+ glfwSetWindowShouldClose(window, 1);
+ break;
+ }
+}
+
+void draw_marker(struct nk_command_buffer* canvas, int lead, struct nk_vec2 pos)
+{
+ struct nk_color colors[4] = { nk_rgb(255,0,0), nk_rgb(255,255,0), nk_rgb(0,255,0), nk_rgb(0,96,255) };
+ struct nk_rect rect = { -5 + pos.x, -5 + pos.y, 10, 10 };
+ nk_fill_circle(canvas, rect, colors[lead]);
+}
+
+int main(int argc, char** argv)
+{
+ int ch, width, height;
+ unsigned long frame_count = 0;
+ double last_time, current_time;
+ double frame_rate = 0;
+ int fullscreen = GLFW_FALSE;
+ GLFWmonitor* monitor = NULL;
+ GLFWwindow* window;
+ struct nk_context* nk;
+ struct nk_font_atlas* atlas;
+
+ int show_forecasts = nk_true;
+
+ while ((ch = getopt(argc, argv, "fh")) != -1)
+ {
+ switch (ch)
+ {
+ case 'h':
+ usage();
+ exit(EXIT_SUCCESS);
+
+ case 'f':
+ fullscreen = GLFW_TRUE;
+ break;
+ }
+ }
+
+ glfwSetErrorCallback(error_callback);
+
+ if (!glfwInit())
+ exit(EXIT_FAILURE);
+
+ if (fullscreen)
+ {
+ const GLFWvidmode* mode;
+
+ monitor = glfwGetPrimaryMonitor();
+ mode = glfwGetVideoMode(monitor);
+
+ width = mode->width;
+ height = mode->height;
+ }
+ else
+ {
+ width = 640;
+ height = 480;
+ }
+
+ glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 2);
+ glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 0);
+
+ glfwWindowHint(GLFW_SCALE_TO_MONITOR, GLFW_TRUE);
+
+ window = glfwCreateWindow(width, height, "Input lag test", monitor, NULL);
+ if (!window)
+ {
+ glfwTerminate();
+ exit(EXIT_FAILURE);
+ }
+
+ glfwMakeContextCurrent(window);
+ gladLoadGL(glfwGetProcAddress);
+ update_vsync();
+
+ last_time = glfwGetTime();
+
+ nk = nk_glfw3_init(window, NK_GLFW3_INSTALL_CALLBACKS);
+ nk_glfw3_font_stash_begin(&atlas);
+ nk_glfw3_font_stash_end();
+
+ glfwSetKeyCallback(window, key_callback);
+ glfwSetCursorPosCallback(window, cursor_pos_callback);
+
+ while (!glfwWindowShouldClose(window))
+ {
+ int width, height;
+ struct nk_rect area;
+
+ glfwPollEvents();
+ sample_input(window);
+
+ glfwGetWindowSize(window, &width, &height);
+ area = nk_rect(0.f, 0.f, (float) width, (float) height);
+
+ glClear(GL_COLOR_BUFFER_BIT);
+ nk_glfw3_new_frame();
+ if (nk_begin(nk, "", area, 0))
+ {
+ nk_flags align_left = NK_TEXT_ALIGN_LEFT | NK_TEXT_ALIGN_MIDDLE;
+ struct nk_command_buffer *canvas = nk_window_get_canvas(nk);
+ int lead;
+
+ for (lead = show_forecasts ? 3 : 0; lead >= 0; lead--)
+ draw_marker(canvas, lead, nk_vec2(cursor_pos.x + cursor_vel.x * lead,
+ cursor_pos.y + cursor_vel.y * lead));
+
+ // print instructions
+ nk_layout_row_dynamic(nk, 20, 1);
+ nk_label(nk, "Move mouse uniformly and check marker under cursor:", align_left);
+ for (lead = 0; lead <= 3; lead++) {
+ nk_layout_row_begin(nk, NK_STATIC, 12, 2);
+ nk_layout_row_push(nk, 25);
+ draw_marker(canvas, lead, nk_layout_space_to_screen(nk, nk_vec2(20, 5)));
+ nk_label(nk, "", 0);
+ nk_layout_row_push(nk, 500);
+ if (lead == 0)
+ nk_label(nk, "- current cursor position (no input lag)", align_left);
+ else
+ nk_labelf(nk, align_left, "- %d-frame forecast (input lag is %d frame)", lead, lead);
+ nk_layout_row_end(nk);
+ }
+
+ nk_layout_row_dynamic(nk, 20, 1);
+
+ nk_checkbox_label(nk, "Show forecasts", &show_forecasts);
+ nk_label(nk, "Input method:", align_left);
+ if (nk_option_label(nk, "glfwGetCursorPos (sync query)", cursor_method == cursor_sync_query))
+ cursor_method = cursor_sync_query;
+ if (nk_option_label(nk, "glfwSetCursorPosCallback (latest input message)", cursor_method == cursor_input_message))
+ cursor_method = cursor_input_message;
+
+ nk_label(nk, "", 0); // separator
+
+ nk_value_float(nk, "FPS", (float) frame_rate);
+ if (nk_checkbox_label(nk, "Enable vsync", &enable_vsync))
+ update_vsync();
+
+ nk_label(nk, "", 0); // separator
+
+ nk_label(nk, "After swap:", align_left);
+ nk_checkbox_label(nk, "glClear", &swap_clear);
+ nk_checkbox_label(nk, "glFinish", &swap_finish);
+ nk_checkbox_label(nk, "draw with occlusion query", &swap_occlusion_query);
+ nk_checkbox_label(nk, "glReadPixels", &swap_read_pixels);
+ }
+
+ nk_end(nk);
+ nk_glfw3_render(NK_ANTI_ALIASING_ON);
+
+ swap_buffers(window);
+
+ frame_count++;
+
+ current_time = glfwGetTime();
+ if (current_time - last_time > 1.0)
+ {
+ frame_rate = frame_count / (current_time - last_time);
+ frame_count = 0;
+ last_time = current_time;
+ }
+ }
+
+ glfwTerminate();
+ exit(EXIT_SUCCESS);
+}
+
diff --git a/libs/glfw-3.3.8/tests/joysticks.c b/libs/glfw-3.3.8/tests/joysticks.c
new file mode 100644
index 0000000..8eae021
--- /dev/null
+++ b/libs/glfw-3.3.8/tests/joysticks.c
@@ -0,0 +1,344 @@
+//========================================================================
+// Joystick input test
+// Copyright (c) Camilla Löwy <elmindreda@glfw.org>
+//
+// 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.
+//
+//========================================================================
+//
+// This test displays the state of every button and axis of every connected
+// joystick and/or gamepad
+//
+//========================================================================
+
+#include <glad/gl.h>
+#define GLFW_INCLUDE_NONE
+#include <GLFW/glfw3.h>
+
+#define NK_IMPLEMENTATION
+#define NK_INCLUDE_FIXED_TYPES
+#define NK_INCLUDE_FONT_BAKING
+#define NK_INCLUDE_DEFAULT_FONT
+#define NK_INCLUDE_DEFAULT_ALLOCATOR
+#define NK_INCLUDE_VERTEX_BUFFER_OUTPUT
+#define NK_INCLUDE_STANDARD_VARARGS
+#define NK_BUTTON_TRIGGER_ON_RELEASE
+#include <nuklear.h>
+
+#define NK_GLFW_GL2_IMPLEMENTATION
+#include <nuklear_glfw_gl2.h>
+
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+
+#ifdef _MSC_VER
+#define strdup(x) _strdup(x)
+#endif
+
+static GLFWwindow* window;
+static int joysticks[GLFW_JOYSTICK_LAST + 1];
+static int joystick_count = 0;
+
+static void error_callback(int error, const char* description)
+{
+ fprintf(stderr, "Error: %s\n", description);
+}
+
+static void joystick_callback(int jid, int event)
+{
+ if (event == GLFW_CONNECTED)
+ joysticks[joystick_count++] = jid;
+ else if (event == GLFW_DISCONNECTED)
+ {
+ int i;
+
+ for (i = 0; i < joystick_count; i++)
+ {
+ if (joysticks[i] == jid)
+ break;
+ }
+
+ for (i = i + 1; i < joystick_count; i++)
+ joysticks[i - 1] = joysticks[i];
+
+ joystick_count--;
+ }
+
+ if (!glfwGetWindowAttrib(window, GLFW_FOCUSED))
+ glfwRequestWindowAttention(window);
+}
+
+static void drop_callback(GLFWwindow* window, int count, const char* paths[])
+{
+ int i;
+
+ for (i = 0; i < count; i++)
+ {
+ long size;
+ char* text;
+ FILE* stream = fopen(paths[i], "rb");
+ if (!stream)
+ continue;
+
+ fseek(stream, 0, SEEK_END);
+ size = ftell(stream);
+ fseek(stream, 0, SEEK_SET);
+
+ text = malloc(size + 1);
+ text[size] = '\0';
+ if (fread(text, 1, size, stream) == size)
+ glfwUpdateGamepadMappings(text);
+
+ free(text);
+ fclose(stream);
+ }
+}
+
+static const char* joystick_label(int jid)
+{
+ static char label[1024];
+ snprintf(label, sizeof(label), "%i: %s", jid + 1, glfwGetJoystickName(jid));
+ return label;
+}
+
+static void hat_widget(struct nk_context* nk, unsigned char state)
+{
+ float radius;
+ struct nk_rect area;
+ struct nk_vec2 center;
+
+ if (nk_widget(&area, nk) == NK_WIDGET_INVALID)
+ return;
+
+ center = nk_vec2(area.x + area.w / 2.f, area.y + area.h / 2.f);
+ radius = NK_MIN(area.w, area.h) / 2.f;
+
+ nk_stroke_circle(nk_window_get_canvas(nk),
+ nk_rect(center.x - radius,
+ center.y - radius,
+ radius * 2.f,
+ radius * 2.f),
+ 1.f,
+ nk_rgb(175, 175, 175));
+
+ if (state)
+ {
+ const float angles[] =
+ {
+ 0.f, 0.f,
+ NK_PI * 1.5f, NK_PI * 1.75f,
+ NK_PI, 0.f,
+ NK_PI * 1.25f, 0.f,
+ NK_PI * 0.5f, NK_PI * 0.25f,
+ 0.f, 0.f,
+ NK_PI * 0.75f, 0.f,
+ };
+ const float cosa = nk_cos(angles[state]);
+ const float sina = nk_sin(angles[state]);
+ const struct nk_vec2 p0 = nk_vec2(0.f, -radius);
+ const struct nk_vec2 p1 = nk_vec2( radius / 2.f, -radius / 3.f);
+ const struct nk_vec2 p2 = nk_vec2(-radius / 2.f, -radius / 3.f);
+
+ nk_fill_triangle(nk_window_get_canvas(nk),
+ center.x + cosa * p0.x + sina * p0.y,
+ center.y + cosa * p0.y - sina * p0.x,
+ center.x + cosa * p1.x + sina * p1.y,
+ center.y + cosa * p1.y - sina * p1.x,
+ center.x + cosa * p2.x + sina * p2.y,
+ center.y + cosa * p2.y - sina * p2.x,
+ nk_rgb(175, 175, 175));
+ }
+}
+
+int main(void)
+{
+ int jid, hat_buttons = GLFW_FALSE;
+ struct nk_context* nk;
+ struct nk_font_atlas* atlas;
+
+ memset(joysticks, 0, sizeof(joysticks));
+
+ glfwSetErrorCallback(error_callback);
+
+ if (!glfwInit())
+ exit(EXIT_FAILURE);
+
+ glfwWindowHint(GLFW_SCALE_TO_MONITOR, GLFW_TRUE);
+
+ window = glfwCreateWindow(800, 600, "Joystick Test", NULL, NULL);
+ if (!window)
+ {
+ glfwTerminate();
+ exit(EXIT_FAILURE);
+ }
+
+ glfwMakeContextCurrent(window);
+ gladLoadGL(glfwGetProcAddress);
+ glfwSwapInterval(1);
+
+ nk = nk_glfw3_init(window, NK_GLFW3_INSTALL_CALLBACKS);
+ nk_glfw3_font_stash_begin(&atlas);
+ nk_glfw3_font_stash_end();
+
+ for (jid = GLFW_JOYSTICK_1; jid <= GLFW_JOYSTICK_LAST; jid++)
+ {
+ if (glfwJoystickPresent(jid))
+ joysticks[joystick_count++] = jid;
+ }
+
+ glfwSetJoystickCallback(joystick_callback);
+ glfwSetDropCallback(window, drop_callback);
+
+ while (!glfwWindowShouldClose(window))
+ {
+ int i, width, height;
+
+ glfwGetWindowSize(window, &width, &height);
+
+ glClear(GL_COLOR_BUFFER_BIT);
+ nk_glfw3_new_frame();
+
+ if (nk_begin(nk,
+ "Joysticks",
+ nk_rect(width - 200.f, 0.f, 200.f, (float) height),
+ NK_WINDOW_MINIMIZABLE |
+ NK_WINDOW_TITLE))
+ {
+ nk_layout_row_dynamic(nk, 30, 1);
+
+ nk_checkbox_label(nk, "Hat buttons", &hat_buttons);
+
+ if (joystick_count)
+ {
+ for (i = 0; i < joystick_count; i++)
+ {
+ if (nk_button_label(nk, joystick_label(joysticks[i])))
+ nk_window_set_focus(nk, joystick_label(joysticks[i]));
+ }
+ }
+ else
+ nk_label(nk, "No joysticks connected", NK_TEXT_LEFT);
+ }
+
+ nk_end(nk);
+
+ for (i = 0; i < joystick_count; i++)
+ {
+ if (nk_begin(nk,
+ joystick_label(joysticks[i]),
+ nk_rect(i * 20.f, i * 20.f, 550.f, 570.f),
+ NK_WINDOW_BORDER |
+ NK_WINDOW_MOVABLE |
+ NK_WINDOW_SCALABLE |
+ NK_WINDOW_MINIMIZABLE |
+ NK_WINDOW_TITLE))
+ {
+ int j, axis_count, button_count, hat_count;
+ const float* axes;
+ const unsigned char* buttons;
+ const unsigned char* hats;
+ GLFWgamepadstate state;
+
+ nk_layout_row_dynamic(nk, 30, 1);
+ nk_labelf(nk, NK_TEXT_LEFT, "Hardware GUID %s",
+ glfwGetJoystickGUID(joysticks[i]));
+ nk_label(nk, "Joystick state", NK_TEXT_LEFT);
+
+ axes = glfwGetJoystickAxes(joysticks[i], &axis_count);
+ buttons = glfwGetJoystickButtons(joysticks[i], &button_count);
+ hats = glfwGetJoystickHats(joysticks[i], &hat_count);
+
+ if (!hat_buttons)
+ button_count -= hat_count * 4;
+
+ for (j = 0; j < axis_count; j++)
+ nk_slide_float(nk, -1.f, axes[j], 1.f, 0.1f);
+
+ nk_layout_row_dynamic(nk, 30, 12);
+
+ for (j = 0; j < button_count; j++)
+ {
+ char name[16];
+ snprintf(name, sizeof(name), "%i", j + 1);
+ nk_select_label(nk, name, NK_TEXT_CENTERED, buttons[j]);
+ }
+
+ nk_layout_row_dynamic(nk, 30, 8);
+
+ for (j = 0; j < hat_count; j++)
+ hat_widget(nk, hats[j]);
+
+ nk_layout_row_dynamic(nk, 30, 1);
+
+ if (glfwGetGamepadState(joysticks[i], &state))
+ {
+ int hat = 0;
+ const char* names[GLFW_GAMEPAD_BUTTON_LAST + 1 - 4] =
+ {
+ "A", "B", "X", "Y",
+ "LB", "RB",
+ "Back", "Start", "Guide",
+ "LT", "RT",
+ };
+
+ nk_labelf(nk, NK_TEXT_LEFT,
+ "Gamepad state: %s",
+ glfwGetGamepadName(joysticks[i]));
+
+ nk_layout_row_dynamic(nk, 30, 2);
+
+ for (j = 0; j <= GLFW_GAMEPAD_AXIS_LAST; j++)
+ nk_slide_float(nk, -1.f, state.axes[j], 1.f, 0.1f);
+
+ nk_layout_row_dynamic(nk, 30, GLFW_GAMEPAD_BUTTON_LAST + 1 - 4);
+
+ for (j = 0; j <= GLFW_GAMEPAD_BUTTON_LAST - 4; j++)
+ nk_select_label(nk, names[j], NK_TEXT_CENTERED, state.buttons[j]);
+
+ if (state.buttons[GLFW_GAMEPAD_BUTTON_DPAD_UP])
+ hat |= GLFW_HAT_UP;
+ if (state.buttons[GLFW_GAMEPAD_BUTTON_DPAD_RIGHT])
+ hat |= GLFW_HAT_RIGHT;
+ if (state.buttons[GLFW_GAMEPAD_BUTTON_DPAD_DOWN])
+ hat |= GLFW_HAT_DOWN;
+ if (state.buttons[GLFW_GAMEPAD_BUTTON_DPAD_LEFT])
+ hat |= GLFW_HAT_LEFT;
+
+ nk_layout_row_dynamic(nk, 30, 8);
+ hat_widget(nk, hat);
+ }
+ else
+ nk_label(nk, "Joystick has no gamepad mapping", NK_TEXT_LEFT);
+ }
+
+ nk_end(nk);
+ }
+
+ nk_glfw3_render(NK_ANTI_ALIASING_ON);
+
+ glfwSwapBuffers(window);
+ glfwPollEvents();
+ }
+
+ glfwTerminate();
+ exit(EXIT_SUCCESS);
+}
+
diff --git a/libs/glfw-3.3.8/tests/monitors.c b/libs/glfw-3.3.8/tests/monitors.c
new file mode 100644
index 0000000..2b75d7b
--- /dev/null
+++ b/libs/glfw-3.3.8/tests/monitors.c
@@ -0,0 +1,260 @@
+//========================================================================
+// Monitor information tool
+// Copyright (c) Camilla Löwy <elmindreda@glfw.org>
+//
+// 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.
+//
+//========================================================================
+//
+// This test prints monitor and video mode information or verifies video
+// modes
+//
+//========================================================================
+
+#include <glad/gl.h>
+#define GLFW_INCLUDE_NONE
+#include <GLFW/glfw3.h>
+
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+
+#include "getopt.h"
+
+enum Mode
+{
+ LIST_MODE,
+ TEST_MODE
+};
+
+static void usage(void)
+{
+ printf("Usage: monitors [-t]\n");
+ printf(" monitors -h\n");
+}
+
+static int euclid(int a, int b)
+{
+ return b ? euclid(b, a % b) : a;
+}
+
+static const char* format_mode(const GLFWvidmode* mode)
+{
+ static char buffer[512];
+ const int gcd = euclid(mode->width, mode->height);
+
+ snprintf(buffer,
+ sizeof(buffer),
+ "%i x %i x %i (%i:%i) (%i %i %i) %i Hz",
+ mode->width, mode->height,
+ mode->redBits + mode->greenBits + mode->blueBits,
+ mode->width / gcd, mode->height / gcd,
+ mode->redBits, mode->greenBits, mode->blueBits,
+ mode->refreshRate);
+
+ buffer[sizeof(buffer) - 1] = '\0';
+ return buffer;
+}
+
+static void error_callback(int error, const char* description)
+{
+ fprintf(stderr, "Error: %s\n", description);
+}
+
+static void framebuffer_size_callback(GLFWwindow* window, int width, int height)
+{
+ printf("Framebuffer resized to %ix%i\n", width, height);
+
+ glViewport(0, 0, width, height);
+}
+
+static void key_callback(GLFWwindow* window, int key, int scancode, int action, int mods)
+{
+ if (key == GLFW_KEY_ESCAPE)
+ glfwSetWindowShouldClose(window, GLFW_TRUE);
+}
+
+static void list_modes(GLFWmonitor* monitor)
+{
+ int count, x, y, width_mm, height_mm, i;
+ int workarea_x, workarea_y, workarea_width, workarea_height;
+ float xscale, yscale;
+
+ const GLFWvidmode* mode = glfwGetVideoMode(monitor);
+ const GLFWvidmode* modes = glfwGetVideoModes(monitor, &count);
+
+ glfwGetMonitorPos(monitor, &x, &y);
+ glfwGetMonitorPhysicalSize(monitor, &width_mm, &height_mm);
+ glfwGetMonitorContentScale(monitor, &xscale, &yscale);
+ glfwGetMonitorWorkarea(monitor, &workarea_x, &workarea_y, &workarea_width, &workarea_height);
+
+ printf("Name: %s (%s)\n",
+ glfwGetMonitorName(monitor),
+ glfwGetPrimaryMonitor() == monitor ? "primary" : "secondary");
+ printf("Current mode: %s\n", format_mode(mode));
+ printf("Virtual position: %i, %i\n", x, y);
+ printf("Content scale: %f x %f\n", xscale, yscale);
+
+ printf("Physical size: %i x %i mm (%0.2f dpi at %i x %i)\n",
+ width_mm, height_mm, mode->width * 25.4f / width_mm, mode->width, mode->height);
+ printf("Monitor work area: %i x %i starting at %i, %i\n",
+ workarea_width, workarea_height, workarea_x, workarea_y);
+
+ printf("Modes:\n");
+
+ for (i = 0; i < count; i++)
+ {
+ printf("%3u: %s", (unsigned int) i, format_mode(modes + i));
+
+ if (memcmp(mode, modes + i, sizeof(GLFWvidmode)) == 0)
+ printf(" (current mode)");
+
+ putchar('\n');
+ }
+}
+
+static void test_modes(GLFWmonitor* monitor)
+{
+ int i, count;
+ GLFWwindow* window;
+ const GLFWvidmode* modes = glfwGetVideoModes(monitor, &count);
+
+ for (i = 0; i < count; i++)
+ {
+ const GLFWvidmode* mode = modes + i;
+ GLFWvidmode current;
+
+ glfwWindowHint(GLFW_RED_BITS, mode->redBits);
+ glfwWindowHint(GLFW_GREEN_BITS, mode->greenBits);
+ glfwWindowHint(GLFW_BLUE_BITS, mode->blueBits);
+ glfwWindowHint(GLFW_REFRESH_RATE, mode->refreshRate);
+
+ printf("Testing mode %u on monitor %s: %s\n",
+ (unsigned int) i,
+ glfwGetMonitorName(monitor),
+ format_mode(mode));
+
+ window = glfwCreateWindow(mode->width, mode->height,
+ "Video Mode Test",
+ glfwGetPrimaryMonitor(),
+ NULL);
+ if (!window)
+ {
+ printf("Failed to enter mode %u: %s\n",
+ (unsigned int) i,
+ format_mode(mode));
+ continue;
+ }
+
+ glfwSetFramebufferSizeCallback(window, framebuffer_size_callback);
+ glfwSetKeyCallback(window, key_callback);
+
+ glfwMakeContextCurrent(window);
+ gladLoadGL(glfwGetProcAddress);
+ glfwSwapInterval(1);
+
+ glfwSetTime(0.0);
+
+ while (glfwGetTime() < 5.0)
+ {
+ glClear(GL_COLOR_BUFFER_BIT);
+ glfwSwapBuffers(window);
+ glfwPollEvents();
+
+ if (glfwWindowShouldClose(window))
+ {
+ printf("User terminated program\n");
+
+ glfwTerminate();
+ exit(EXIT_SUCCESS);
+ }
+ }
+
+ glGetIntegerv(GL_RED_BITS, &current.redBits);
+ glGetIntegerv(GL_GREEN_BITS, &current.greenBits);
+ glGetIntegerv(GL_BLUE_BITS, &current.blueBits);
+
+ glfwGetWindowSize(window, &current.width, &current.height);
+
+ if (current.redBits != mode->redBits ||
+ current.greenBits != mode->greenBits ||
+ current.blueBits != mode->blueBits)
+ {
+ printf("*** Color bit mismatch: (%i %i %i) instead of (%i %i %i)\n",
+ current.redBits, current.greenBits, current.blueBits,
+ mode->redBits, mode->greenBits, mode->blueBits);
+ }
+
+ if (current.width != mode->width || current.height != mode->height)
+ {
+ printf("*** Size mismatch: %ix%i instead of %ix%i\n",
+ current.width, current.height,
+ mode->width, mode->height);
+ }
+
+ printf("Closing window\n");
+
+ glfwDestroyWindow(window);
+ window = NULL;
+
+ glfwPollEvents();
+ }
+}
+
+int main(int argc, char** argv)
+{
+ int ch, i, count, mode = LIST_MODE;
+ GLFWmonitor** monitors;
+
+ while ((ch = getopt(argc, argv, "th")) != -1)
+ {
+ switch (ch)
+ {
+ case 'h':
+ usage();
+ exit(EXIT_SUCCESS);
+ case 't':
+ mode = TEST_MODE;
+ break;
+ default:
+ usage();
+ exit(EXIT_FAILURE);
+ }
+ }
+
+ glfwSetErrorCallback(error_callback);
+
+ if (!glfwInit())
+ exit(EXIT_FAILURE);
+
+ monitors = glfwGetMonitors(&count);
+
+ for (i = 0; i < count; i++)
+ {
+ if (mode == LIST_MODE)
+ list_modes(monitors[i]);
+ else if (mode == TEST_MODE)
+ test_modes(monitors[i]);
+ }
+
+ glfwTerminate();
+ exit(EXIT_SUCCESS);
+}
+
diff --git a/libs/glfw-3.3.8/tests/msaa.c b/libs/glfw-3.3.8/tests/msaa.c
new file mode 100644
index 0000000..33e2ccc
--- /dev/null
+++ b/libs/glfw-3.3.8/tests/msaa.c
@@ -0,0 +1,220 @@
+//========================================================================
+// Multisample anti-aliasing test
+// Copyright (c) Camilla Löwy <elmindreda@glfw.org>
+//
+// 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.
+//
+//========================================================================
+//
+// This test renders two high contrast, slowly rotating quads, one aliased
+// and one (hopefully) anti-aliased, thus allowing for visual verification
+// of whether MSAA is indeed enabled
+//
+//========================================================================
+
+#include <glad/gl.h>
+#define GLFW_INCLUDE_NONE
+#include <GLFW/glfw3.h>
+
+#if defined(_MSC_VER)
+ // Make MS math.h define M_PI
+ #define _USE_MATH_DEFINES
+#endif
+
+#include "linmath.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+
+#include "getopt.h"
+
+static const vec2 vertices[4] =
+{
+ { -0.6f, -0.6f },
+ { 0.6f, -0.6f },
+ { 0.6f, 0.6f },
+ { -0.6f, 0.6f }
+};
+
+static const char* vertex_shader_text =
+"#version 110\n"
+"uniform mat4 MVP;\n"
+"attribute vec2 vPos;\n"
+"void main()\n"
+"{\n"
+" gl_Position = MVP * vec4(vPos, 0.0, 1.0);\n"
+"}\n";
+
+static const char* fragment_shader_text =
+"#version 110\n"
+"void main()\n"
+"{\n"
+" gl_FragColor = vec4(1.0);\n"
+"}\n";
+
+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 (action != GLFW_PRESS)
+ return;
+
+ switch (key)
+ {
+ case GLFW_KEY_SPACE:
+ glfwSetTime(0.0);
+ break;
+ case GLFW_KEY_ESCAPE:
+ glfwSetWindowShouldClose(window, GLFW_TRUE);
+ break;
+ }
+}
+
+static void usage(void)
+{
+ printf("Usage: msaa [-h] [-s SAMPLES]\n");
+}
+
+int main(int argc, char** argv)
+{
+ int ch, samples = 4;
+ GLFWwindow* window;
+ GLuint vertex_buffer, vertex_shader, fragment_shader, program;
+ GLint mvp_location, vpos_location;
+
+ while ((ch = getopt(argc, argv, "hs:")) != -1)
+ {
+ switch (ch)
+ {
+ case 'h':
+ usage();
+ exit(EXIT_SUCCESS);
+ case 's':
+ samples = atoi(optarg);
+ break;
+ default:
+ usage();
+ exit(EXIT_FAILURE);
+ }
+ }
+
+ glfwSetErrorCallback(error_callback);
+
+ if (!glfwInit())
+ exit(EXIT_FAILURE);
+
+ if (samples)
+ printf("Requesting MSAA with %i samples\n", samples);
+ else
+ printf("Requesting that MSAA not be available\n");
+
+ glfwWindowHint(GLFW_SAMPLES, samples);
+ glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 2);
+ glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 0);
+
+ window = glfwCreateWindow(800, 400, "Aliasing Detector", NULL, NULL);
+ if (!window)
+ {
+ glfwTerminate();
+ exit(EXIT_FAILURE);
+ }
+
+ glfwSetKeyCallback(window, key_callback);
+
+ glfwMakeContextCurrent(window);
+ gladLoadGL(glfwGetProcAddress);
+ glfwSwapInterval(1);
+
+ glGetIntegerv(GL_SAMPLES, &samples);
+ if (samples)
+ printf("Context reports MSAA is available with %i samples\n", samples);
+ else
+ printf("Context reports MSAA is unavailable\n");
+
+ 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");
+
+ glEnableVertexAttribArray(vpos_location);
+ glVertexAttribPointer(vpos_location, 2, GL_FLOAT, GL_FALSE,
+ sizeof(vertices[0]), (void*) 0);
+
+ while (!glfwWindowShouldClose(window))
+ {
+ float ratio;
+ int width, height;
+ mat4x4 m, p, mvp;
+ const double angle = glfwGetTime() * M_PI / 180.0;
+
+ glfwGetFramebufferSize(window, &width, &height);
+ ratio = width / (float) height;
+
+ glViewport(0, 0, width, height);
+ glClear(GL_COLOR_BUFFER_BIT);
+
+ glUseProgram(program);
+
+ mat4x4_ortho(p, -ratio, ratio, -1.f, 1.f, 0.f, 1.f);
+
+ mat4x4_translate(m, -1.f, 0.f, 0.f);
+ mat4x4_rotate_Z(m, m, (float) angle);
+ mat4x4_mul(mvp, p, m);
+
+ glUniformMatrix4fv(mvp_location, 1, GL_FALSE, (const GLfloat*) mvp);
+ glDisable(GL_MULTISAMPLE);
+ glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
+
+ mat4x4_translate(m, 1.f, 0.f, 0.f);
+ mat4x4_rotate_Z(m, m, (float) angle);
+ mat4x4_mul(mvp, p, m);
+
+ glUniformMatrix4fv(mvp_location, 1, GL_FALSE, (const GLfloat*) mvp);
+ glEnable(GL_MULTISAMPLE);
+ glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
+
+ glfwSwapBuffers(window);
+ glfwPollEvents();
+ }
+
+ glfwDestroyWindow(window);
+
+ glfwTerminate();
+ exit(EXIT_SUCCESS);
+}
+
diff --git a/libs/glfw-3.3.8/tests/opacity.c b/libs/glfw-3.3.8/tests/opacity.c
new file mode 100644
index 0000000..47f28b1
--- /dev/null
+++ b/libs/glfw-3.3.8/tests/opacity.c
@@ -0,0 +1,108 @@
+//========================================================================
+// Window opacity test program
+// Copyright (c) Camilla Löwy <elmindreda@glfw.org>
+//
+// 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 <glad/gl.h>
+#define GLFW_INCLUDE_NONE
+#include <GLFW/glfw3.h>
+
+#define NK_IMPLEMENTATION
+#define NK_INCLUDE_FIXED_TYPES
+#define NK_INCLUDE_FONT_BAKING
+#define NK_INCLUDE_DEFAULT_FONT
+#define NK_INCLUDE_DEFAULT_ALLOCATOR
+#define NK_INCLUDE_VERTEX_BUFFER_OUTPUT
+#define NK_INCLUDE_STANDARD_VARARGS
+#include <nuklear.h>
+
+#define NK_GLFW_GL2_IMPLEMENTATION
+#include <nuklear_glfw_gl2.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+
+static void error_callback(int error, const char* description)
+{
+ fprintf(stderr, "Error: %s\n", description);
+}
+
+int main(int argc, char** argv)
+{
+ GLFWwindow* window;
+ struct nk_context* nk;
+ struct nk_font_atlas* atlas;
+
+ glfwSetErrorCallback(error_callback);
+
+ if (!glfwInit())
+ exit(EXIT_FAILURE);
+
+ glfwWindowHint(GLFW_SCALE_TO_MONITOR, GLFW_TRUE);
+
+ window = glfwCreateWindow(400, 400, "Opacity", NULL, NULL);
+ if (!window)
+ {
+ glfwTerminate();
+ exit(EXIT_FAILURE);
+ }
+
+ glfwMakeContextCurrent(window);
+ gladLoadGL(glfwGetProcAddress);
+ glfwSwapInterval(1);
+
+ nk = nk_glfw3_init(window, NK_GLFW3_INSTALL_CALLBACKS);
+ nk_glfw3_font_stash_begin(&atlas);
+ nk_glfw3_font_stash_end();
+
+ while (!glfwWindowShouldClose(window))
+ {
+ int width, height;
+ struct nk_rect area;
+
+ glfwGetWindowSize(window, &width, &height);
+ area = nk_rect(0.f, 0.f, (float) width, (float) height);
+
+ glClear(GL_COLOR_BUFFER_BIT);
+ nk_glfw3_new_frame();
+ if (nk_begin(nk, "", area, 0))
+ {
+ float opacity = glfwGetWindowOpacity(window);
+ nk_layout_row_dynamic(nk, 30, 2);
+ if (nk_slider_float(nk, 0.f, &opacity, 1.f, 0.001f))
+ glfwSetWindowOpacity(window, opacity);
+ nk_labelf(nk, NK_TEXT_LEFT, "%0.3f", opacity);
+ }
+
+ nk_end(nk);
+ nk_glfw3_render(NK_ANTI_ALIASING_ON);
+
+ glfwSwapBuffers(window);
+ glfwWaitEventsTimeout(1.0);
+ }
+
+ nk_glfw3_shutdown();
+ glfwTerminate();
+ exit(EXIT_SUCCESS);
+}
+
diff --git a/libs/glfw-3.3.8/tests/reopen.c b/libs/glfw-3.3.8/tests/reopen.c
new file mode 100644
index 0000000..10d22b2
--- /dev/null
+++ b/libs/glfw-3.3.8/tests/reopen.c
@@ -0,0 +1,240 @@
+//========================================================================
+// Window re-opener (open/close stress test)
+// Copyright (c) Camilla Löwy <elmindreda@glfw.org>
+//
+// 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.
+//
+//========================================================================
+//
+// This test came about as the result of bug #1262773
+//
+// It closes and re-opens the GLFW window every five seconds, alternating
+// between windowed and full screen mode
+//
+// It also times and logs opening and closing actions and attempts to separate
+// user initiated window closing from its own
+//
+//========================================================================
+
+#include <glad/gl.h>
+#define GLFW_INCLUDE_NONE
+#include <GLFW/glfw3.h>
+
+#include <time.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+#include "linmath.h"
+
+static const char* vertex_shader_text =
+"#version 110\n"
+"uniform mat4 MVP;\n"
+"attribute vec2 vPos;\n"
+"void main()\n"
+"{\n"
+" gl_Position = MVP * vec4(vPos, 0.0, 1.0);\n"
+"}\n";
+
+static const char* fragment_shader_text =
+"#version 110\n"
+"void main()\n"
+"{\n"
+" gl_FragColor = vec4(1.0);\n"
+"}\n";
+
+static const vec2 vertices[4] =
+{
+ { -0.5f, -0.5f },
+ { 0.5f, -0.5f },
+ { 0.5f, 0.5f },
+ { -0.5f, 0.5f }
+};
+
+static void error_callback(int error, const char* description)
+{
+ fprintf(stderr, "Error: %s\n", description);
+}
+
+static void window_close_callback(GLFWwindow* window)
+{
+ printf("Close callback triggered\n");
+}
+
+static void key_callback(GLFWwindow* window, int key, int scancode, int action, int mods)
+{
+ if (action != GLFW_PRESS)
+ return;
+
+ switch (key)
+ {
+ case GLFW_KEY_Q:
+ case GLFW_KEY_ESCAPE:
+ glfwSetWindowShouldClose(window, GLFW_TRUE);
+ break;
+ }
+}
+
+static void close_window(GLFWwindow* window)
+{
+ double base = glfwGetTime();
+ glfwDestroyWindow(window);
+ printf("Closing window took %0.3f seconds\n", glfwGetTime() - base);
+}
+
+int main(int argc, char** argv)
+{
+ int count = 0;
+ double base;
+ GLFWwindow* window;
+
+ srand((unsigned int) time(NULL));
+
+ glfwSetErrorCallback(error_callback);
+
+ if (!glfwInit())
+ exit(EXIT_FAILURE);
+
+ glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 2);
+ glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 0);
+
+ for (;;)
+ {
+ int width, height;
+ GLFWmonitor* monitor = NULL;
+ GLuint vertex_shader, fragment_shader, program, vertex_buffer;
+ GLint mvp_location, vpos_location;
+
+ if (count & 1)
+ {
+ int monitorCount;
+ GLFWmonitor** monitors = glfwGetMonitors(&monitorCount);
+ monitor = monitors[rand() % monitorCount];
+ }
+
+ if (monitor)
+ {
+ const GLFWvidmode* mode = glfwGetVideoMode(monitor);
+ width = mode->width;
+ height = mode->height;
+ }
+ else
+ {
+ width = 640;
+ height = 480;
+ }
+
+ base = glfwGetTime();
+
+ window = glfwCreateWindow(width, height, "Window Re-opener", monitor, NULL);
+ if (!window)
+ {
+ glfwTerminate();
+ exit(EXIT_FAILURE);
+ }
+
+ if (monitor)
+ {
+ printf("Opening full screen window on monitor %s took %0.3f seconds\n",
+ glfwGetMonitorName(monitor),
+ glfwGetTime() - base);
+ }
+ else
+ {
+ printf("Opening regular window took %0.3f seconds\n",
+ glfwGetTime() - base);
+ }
+
+ glfwSetWindowCloseCallback(window, window_close_callback);
+ glfwSetKeyCallback(window, key_callback);
+
+ glfwMakeContextCurrent(window);
+ gladLoadGL(glfwGetProcAddress);
+ glfwSwapInterval(1);
+
+ 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");
+
+ glGenBuffers(1, &vertex_buffer);
+ glBindBuffer(GL_ARRAY_BUFFER, vertex_buffer);
+ glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);
+
+ glEnableVertexAttribArray(vpos_location);
+ glVertexAttribPointer(vpos_location, 2, GL_FLOAT, GL_FALSE,
+ sizeof(vertices[0]), (void*) 0);
+
+ glfwSetTime(0.0);
+
+ while (glfwGetTime() < 5.0)
+ {
+ float ratio;
+ int width, height;
+ mat4x4 m, p, mvp;
+
+ glfwGetFramebufferSize(window, &width, &height);
+ ratio = width / (float) height;
+
+ glViewport(0, 0, width, height);
+ glClear(GL_COLOR_BUFFER_BIT);
+
+ mat4x4_ortho(p, -ratio, ratio, -1.f, 1.f, 0.f, 1.f);
+
+ mat4x4_identity(m);
+ mat4x4_rotate_Z(m, m, (float) glfwGetTime());
+ mat4x4_mul(mvp, p, m);
+
+ glUseProgram(program);
+ glUniformMatrix4fv(mvp_location, 1, GL_FALSE, (const GLfloat*) mvp);
+ glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
+
+ glfwSwapBuffers(window);
+ glfwPollEvents();
+
+ if (glfwWindowShouldClose(window))
+ {
+ close_window(window);
+ printf("User closed window\n");
+
+ glfwTerminate();
+ exit(EXIT_SUCCESS);
+ }
+ }
+
+ printf("Closing window\n");
+ close_window(window);
+
+ count++;
+ }
+
+ glfwTerminate();
+}
+
diff --git a/libs/glfw-3.3.8/tests/tearing.c b/libs/glfw-3.3.8/tests/tearing.c
new file mode 100644
index 0000000..1760121
--- /dev/null
+++ b/libs/glfw-3.3.8/tests/tearing.c
@@ -0,0 +1,250 @@
+//========================================================================
+// Vsync enabling test
+// Copyright (c) Camilla Löwy <elmindreda@glfw.org>
+//
+// 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.
+//
+//========================================================================
+//
+// This test renders a high contrast, horizontally moving bar, allowing for
+// visual verification of whether the set swap interval is indeed obeyed
+//
+//========================================================================
+
+#include <glad/gl.h>
+#define GLFW_INCLUDE_NONE
+#include <GLFW/glfw3.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <math.h>
+
+#include "linmath.h"
+
+static const struct
+{
+ float x, y;
+} vertices[4] =
+{
+ { -0.25f, -1.f },
+ { 0.25f, -1.f },
+ { 0.25f, 1.f },
+ { -0.25f, 1.f }
+};
+
+static const char* vertex_shader_text =
+"#version 110\n"
+"uniform mat4 MVP;\n"
+"attribute vec2 vPos;\n"
+"void main()\n"
+"{\n"
+" gl_Position = MVP * vec4(vPos, 0.0, 1.0);\n"
+"}\n";
+
+static const char* fragment_shader_text =
+"#version 110\n"
+"void main()\n"
+"{\n"
+" gl_FragColor = vec4(1.0);\n"
+"}\n";
+
+static int swap_tear;
+static int swap_interval;
+static double frame_rate;
+
+static void update_window_title(GLFWwindow* window)
+{
+ char title[256];
+
+ snprintf(title, sizeof(title), "Tearing detector (interval %i%s, %0.1f Hz)",
+ swap_interval,
+ (swap_tear && swap_interval < 0) ? " (swap tear)" : "",
+ frame_rate);
+
+ glfwSetWindowTitle(window, title);
+}
+
+static void set_swap_interval(GLFWwindow* window, int interval)
+{
+ swap_interval = interval;
+ glfwSwapInterval(swap_interval);
+ update_window_title(window);
+}
+
+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 (action != GLFW_PRESS)
+ return;
+
+ switch (key)
+ {
+ case GLFW_KEY_UP:
+ {
+ if (swap_interval + 1 > swap_interval)
+ set_swap_interval(window, swap_interval + 1);
+ break;
+ }
+
+ case GLFW_KEY_DOWN:
+ {
+ if (swap_tear)
+ {
+ if (swap_interval - 1 < swap_interval)
+ set_swap_interval(window, swap_interval - 1);
+ }
+ else
+ {
+ if (swap_interval - 1 >= 0)
+ set_swap_interval(window, swap_interval - 1);
+ }
+ break;
+ }
+
+ case GLFW_KEY_ESCAPE:
+ glfwSetWindowShouldClose(window, 1);
+ break;
+
+ case GLFW_KEY_F11:
+ case GLFW_KEY_ENTER:
+ {
+ static int x, y, width, height;
+
+ if (mods != GLFW_MOD_ALT)
+ return;
+
+ if (glfwGetWindowMonitor(window))
+ glfwSetWindowMonitor(window, NULL, x, y, width, height, 0);
+ else
+ {
+ GLFWmonitor* monitor = glfwGetPrimaryMonitor();
+ const GLFWvidmode* mode = glfwGetVideoMode(monitor);
+ glfwGetWindowPos(window, &x, &y);
+ glfwGetWindowSize(window, &width, &height);
+ glfwSetWindowMonitor(window, monitor,
+ 0, 0, mode->width, mode->height,
+ mode->refreshRate);
+ }
+
+ break;
+ }
+ }
+}
+
+int main(int argc, char** argv)
+{
+ unsigned long frame_count = 0;
+ double last_time, current_time;
+ GLFWwindow* window;
+ GLuint vertex_buffer, vertex_shader, fragment_shader, program;
+ GLint mvp_location, vpos_location;
+
+ glfwSetErrorCallback(error_callback);
+
+ if (!glfwInit())
+ exit(EXIT_FAILURE);
+
+ glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 2);
+ glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 0);
+
+ window = glfwCreateWindow(640, 480, "Tearing detector", NULL, NULL);
+ if (!window)
+ {
+ glfwTerminate();
+ exit(EXIT_FAILURE);
+ }
+
+ glfwMakeContextCurrent(window);
+ gladLoadGL(glfwGetProcAddress);
+ set_swap_interval(window, 0);
+
+ last_time = glfwGetTime();
+ frame_rate = 0.0;
+ swap_tear = (glfwExtensionSupported("WGL_EXT_swap_control_tear") ||
+ glfwExtensionSupported("GLX_EXT_swap_control_tear"));
+
+ glfwSetKeyCallback(window, key_callback);
+
+ 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");
+
+ glEnableVertexAttribArray(vpos_location);
+ glVertexAttribPointer(vpos_location, 2, GL_FLOAT, GL_FALSE,
+ sizeof(vertices[0]), (void*) 0);
+
+ while (!glfwWindowShouldClose(window))
+ {
+ int width, height;
+ mat4x4 m, p, mvp;
+ float position = cosf((float) glfwGetTime() * 4.f) * 0.75f;
+
+ glfwGetFramebufferSize(window, &width, &height);
+
+ glViewport(0, 0, width, height);
+ glClear(GL_COLOR_BUFFER_BIT);
+
+ mat4x4_ortho(p, -1.f, 1.f, -1.f, 1.f, 0.f, 1.f);
+ mat4x4_translate(m, position, 0.f, 0.f);
+ mat4x4_mul(mvp, p, m);
+
+ glUseProgram(program);
+ glUniformMatrix4fv(mvp_location, 1, GL_FALSE, (const GLfloat*) mvp);
+ glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
+
+ glfwSwapBuffers(window);
+ glfwPollEvents();
+
+ frame_count++;
+
+ current_time = glfwGetTime();
+ if (current_time - last_time > 1.0)
+ {
+ frame_rate = frame_count / (current_time - last_time);
+ frame_count = 0;
+ last_time = current_time;
+ update_window_title(window);
+ }
+ }
+
+ glfwTerminate();
+ exit(EXIT_SUCCESS);
+}
+
diff --git a/libs/glfw-3.3.8/tests/threads.c b/libs/glfw-3.3.8/tests/threads.c
new file mode 100644
index 0000000..9829493
--- /dev/null
+++ b/libs/glfw-3.3.8/tests/threads.c
@@ -0,0 +1,152 @@
+//========================================================================
+// Multi-threading test
+// Copyright (c) Camilla Löwy <elmindreda@glfw.org>
+//
+// 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.
+//
+//========================================================================
+//
+// This test is intended to verify whether the OpenGL context part of
+// the GLFW API is able to be used from multiple threads
+//
+//========================================================================
+
+#include "tinycthread.h"
+
+#include <glad/gl.h>
+#define GLFW_INCLUDE_NONE
+#include <GLFW/glfw3.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <math.h>
+
+typedef struct
+{
+ GLFWwindow* window;
+ const char* title;
+ float r, g, b;
+ thrd_t id;
+} Thread;
+
+static volatile int running = GLFW_TRUE;
+
+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;
+
+ glfwMakeContextCurrent(thread->window);
+ glfwSwapInterval(1);
+
+ while (running)
+ {
+ const float v = (float) fabs(sin(glfwGetTime() * 2.f));
+ glClearColor(thread->r * v, thread->g * v, thread->b * v, 0.f);
+
+ glClear(GL_COLOR_BUFFER_BIT);
+ glfwSwapBuffers(thread->window);
+ }
+
+ glfwMakeContextCurrent(NULL);
+ return 0;
+}
+
+int main(void)
+{
+ int i, result;
+ Thread threads[] =
+ {
+ { NULL, "Red", 1.f, 0.f, 0.f, 0 },
+ { NULL, "Green", 0.f, 1.f, 0.f, 0 },
+ { NULL, "Blue", 0.f, 0.f, 1.f, 0 }
+ };
+ const int count = sizeof(threads) / sizeof(Thread);
+
+ glfwSetErrorCallback(error_callback);
+
+ if (!glfwInit())
+ exit(EXIT_FAILURE);
+
+ glfwWindowHint(GLFW_VISIBLE, GLFW_FALSE);
+
+ for (i = 0; i < count; i++)
+ {
+ threads[i].window = glfwCreateWindow(200, 200,
+ threads[i].title,
+ NULL, NULL);
+ if (!threads[i].window)
+ {
+ glfwTerminate();
+ exit(EXIT_FAILURE);
+ }
+
+ glfwSetKeyCallback(threads[i].window, key_callback);
+
+ glfwSetWindowPos(threads[i].window, 200 + 250 * i, 200);
+ glfwShowWindow(threads[i].window);
+ }
+
+ glfwMakeContextCurrent(threads[0].window);
+ gladLoadGL(glfwGetProcAddress);
+ glfwMakeContextCurrent(NULL);
+
+ for (i = 0; i < count; i++)
+ {
+ if (thrd_create(&threads[i].id, thread_main, threads + i) !=
+ thrd_success)
+ {
+ fprintf(stderr, "Failed to create secondary thread\n");
+
+ glfwTerminate();
+ exit(EXIT_FAILURE);
+ }
+ }
+
+ while (running)
+ {
+ glfwWaitEvents();
+
+ for (i = 0; i < count; i++)
+ {
+ if (glfwWindowShouldClose(threads[i].window))
+ running = GLFW_FALSE;
+ }
+ }
+
+ for (i = 0; i < count; i++)
+ glfwHideWindow(threads[i].window);
+
+ for (i = 0; i < count; i++)
+ thrd_join(threads[i].id, &result);
+
+ exit(EXIT_SUCCESS);
+}
+
diff --git a/libs/glfw-3.3.8/tests/timeout.c b/libs/glfw-3.3.8/tests/timeout.c
new file mode 100644
index 0000000..bda2560
--- /dev/null
+++ b/libs/glfw-3.3.8/tests/timeout.c
@@ -0,0 +1,98 @@
+//========================================================================
+// Event wait timeout test
+// Copyright (c) Camilla Löwy <elmindreda@glfw.org>
+//
+// 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.
+//
+//========================================================================
+//
+// This test is intended to verify that waiting for events with timeout works
+//
+//========================================================================
+
+#include <glad/gl.h>
+#define GLFW_INCLUDE_NONE
+#include <GLFW/glfw3.h>
+
+#include <time.h>
+#include <math.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+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 float nrand(void)
+{
+ return (float) rand() / (float) RAND_MAX;
+}
+
+int main(void)
+{
+ GLFWwindow* window;
+
+ srand((unsigned int) time(NULL));
+
+ glfwSetErrorCallback(error_callback);
+
+ if (!glfwInit())
+ exit(EXIT_FAILURE);
+
+ window = glfwCreateWindow(640, 480, "Event Wait Timeout Test", NULL, NULL);
+ if (!window)
+ {
+ glfwTerminate();
+ exit(EXIT_FAILURE);
+ }
+
+ glfwMakeContextCurrent(window);
+ gladLoadGL(glfwGetProcAddress);
+ glfwSetKeyCallback(window, key_callback);
+
+ while (!glfwWindowShouldClose(window))
+ {
+ int width, height;
+ float r = nrand(), g = nrand(), b = nrand();
+ float l = (float) sqrt(r * r + g * g + b * b);
+
+ glfwGetFramebufferSize(window, &width, &height);
+
+ glViewport(0, 0, width, height);
+ glClearColor(r / l, g / l, b / l, 1.f);
+ glClear(GL_COLOR_BUFFER_BIT);
+ glfwSwapBuffers(window);
+
+ glfwWaitEventsTimeout(1.0);
+ }
+
+ glfwDestroyWindow(window);
+
+ glfwTerminate();
+ exit(EXIT_SUCCESS);
+}
+
diff --git a/libs/glfw-3.3.8/tests/title.c b/libs/glfw-3.3.8/tests/title.c
new file mode 100644
index 0000000..a5bad34
--- /dev/null
+++ b/libs/glfw-3.3.8/tests/title.c
@@ -0,0 +1,72 @@
+//========================================================================
+// UTF-8 window title test
+// Copyright (c) Camilla Löwy <elmindreda@glfw.org>
+//
+// 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.
+//
+//========================================================================
+//
+// This test sets a UTF-8 window title
+//
+//========================================================================
+
+#include <glad/gl.h>
+#define GLFW_INCLUDE_NONE
+#include <GLFW/glfw3.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+
+static void error_callback(int error, const char* description)
+{
+ fprintf(stderr, "Error: %s\n", description);
+}
+
+int main(void)
+{
+ GLFWwindow* window;
+
+ glfwSetErrorCallback(error_callback);
+
+ if (!glfwInit())
+ exit(EXIT_FAILURE);
+
+ window = glfwCreateWindow(400, 400, "English 日本語 русский язык 官話", NULL, NULL);
+ if (!window)
+ {
+ glfwTerminate();
+ exit(EXIT_FAILURE);
+ }
+
+ glfwMakeContextCurrent(window);
+ gladLoadGL(glfwGetProcAddress);
+ glfwSwapInterval(1);
+
+ while (!glfwWindowShouldClose(window))
+ {
+ glClear(GL_COLOR_BUFFER_BIT);
+ glfwSwapBuffers(window);
+ glfwWaitEvents();
+ }
+
+ glfwTerminate();
+ exit(EXIT_SUCCESS);
+}
+
diff --git a/libs/glfw-3.3.8/tests/triangle-vulkan.c b/libs/glfw-3.3.8/tests/triangle-vulkan.c
new file mode 100644
index 0000000..3a4bfb1
--- /dev/null
+++ b/libs/glfw-3.3.8/tests/triangle-vulkan.c
@@ -0,0 +1,2139 @@
+/*
+ * Copyright (c) 2015-2016 The Khronos Group Inc.
+ * Copyright (c) 2015-2016 Valve Corporation
+ * Copyright (c) 2015-2016 LunarG, Inc.
+ *
+ * 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
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * 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 <olvaffe@gmail.com>
+ * Author: Cody Northrop <cody@lunarg.com>
+ * Author: Courtney Goeltzenleuchter <courtney@LunarG.com>
+ * Author: Ian Elliott <ian@LunarG.com>
+ * Author: Jon Ashburn <jon@lunarg.com>
+ * Author: Piers Daniell <pdaniell@nvidia.com>
+ * Author: Gwan-gyeong Mun <elongbug@gmail.com>
+ * Porter: Camilla Löwy <elmindreda@glfw.org>
+ */
+/*
+ * Draw a textured triangle with depth testing. This is written against Intel
+ * ICD. It does not do state transition nor object memory binding like it
+ * should. It also does no error checking.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdbool.h>
+#include <assert.h>
+#include <signal.h>
+
+#ifdef _WIN32
+#include <windows.h>
+#endif
+
+#include <glad/vulkan.h>
+#define GLFW_INCLUDE_NONE
+#include <GLFW/glfw3.h>
+
+#define DEMO_TEXTURE_COUNT 1
+#define VERTEX_BUFFER_BIND_ID 0
+#define APP_SHORT_NAME "tri"
+#define APP_LONG_NAME "The Vulkan Triangle Demo Program"
+
+#define ARRAY_SIZE(a) (sizeof(a) / sizeof(a[0]))
+
+#if defined(NDEBUG) && defined(__GNUC__)
+#define U_ASSERT_ONLY __attribute__((unused))
+#else
+#define U_ASSERT_ONLY
+#endif
+
+#define ERR_EXIT(err_msg, err_class) \
+ do { \
+ printf(err_msg); \
+ fflush(stdout); \
+ exit(1); \
+ } while (0)
+
+static const uint32_t fragShaderCode[] = {
+ 0x07230203,0x00010000,0x00080007,0x00000014,0x00000000,0x00020011,0x00000001,0x0006000b,
+ 0x00000001,0x4c534c47,0x6474732e,0x3035342e,0x00000000,0x0003000e,0x00000000,0x00000001,
+ 0x0007000f,0x00000004,0x00000004,0x6e69616d,0x00000000,0x00000009,0x00000011,0x00030010,
+ 0x00000004,0x00000007,0x00030003,0x00000002,0x00000190,0x00090004,0x415f4c47,0x735f4252,
+ 0x72617065,0x5f657461,0x64616873,0x6f5f7265,0x63656a62,0x00007374,0x00090004,0x415f4c47,
+ 0x735f4252,0x69646168,0x6c5f676e,0x75676e61,0x5f656761,0x70303234,0x006b6361,0x00040005,
+ 0x00000004,0x6e69616d,0x00000000,0x00050005,0x00000009,0x61724675,0x6c6f4367,0x0000726f,
+ 0x00030005,0x0000000d,0x00786574,0x00050005,0x00000011,0x63786574,0x64726f6f,0x00000000,
+ 0x00040047,0x00000009,0x0000001e,0x00000000,0x00040047,0x0000000d,0x00000022,0x00000000,
+ 0x00040047,0x0000000d,0x00000021,0x00000000,0x00040047,0x00000011,0x0000001e,0x00000000,
+ 0x00020013,0x00000002,0x00030021,0x00000003,0x00000002,0x00030016,0x00000006,0x00000020,
+ 0x00040017,0x00000007,0x00000006,0x00000004,0x00040020,0x00000008,0x00000003,0x00000007,
+ 0x0004003b,0x00000008,0x00000009,0x00000003,0x00090019,0x0000000a,0x00000006,0x00000001,
+ 0x00000000,0x00000000,0x00000000,0x00000001,0x00000000,0x0003001b,0x0000000b,0x0000000a,
+ 0x00040020,0x0000000c,0x00000000,0x0000000b,0x0004003b,0x0000000c,0x0000000d,0x00000000,
+ 0x00040017,0x0000000f,0x00000006,0x00000002,0x00040020,0x00000010,0x00000001,0x0000000f,
+ 0x0004003b,0x00000010,0x00000011,0x00000001,0x00050036,0x00000002,0x00000004,0x00000000,
+ 0x00000003,0x000200f8,0x00000005,0x0004003d,0x0000000b,0x0000000e,0x0000000d,0x0004003d,
+ 0x0000000f,0x00000012,0x00000011,0x00050057,0x00000007,0x00000013,0x0000000e,0x00000012,
+ 0x0003003e,0x00000009,0x00000013,0x000100fd,0x00010038
+};
+
+static const uint32_t vertShaderCode[] = {
+ 0x07230203,0x00010000,0x00080007,0x00000018,0x00000000,0x00020011,0x00000001,0x0006000b,
+ 0x00000001,0x4c534c47,0x6474732e,0x3035342e,0x00000000,0x0003000e,0x00000000,0x00000001,
+ 0x0009000f,0x00000000,0x00000004,0x6e69616d,0x00000000,0x00000009,0x0000000b,0x00000010,
+ 0x00000014,0x00030003,0x00000002,0x00000190,0x00090004,0x415f4c47,0x735f4252,0x72617065,
+ 0x5f657461,0x64616873,0x6f5f7265,0x63656a62,0x00007374,0x00090004,0x415f4c47,0x735f4252,
+ 0x69646168,0x6c5f676e,0x75676e61,0x5f656761,0x70303234,0x006b6361,0x00040005,0x00000004,
+ 0x6e69616d,0x00000000,0x00050005,0x00000009,0x63786574,0x64726f6f,0x00000000,0x00040005,
+ 0x0000000b,0x72747461,0x00000000,0x00060005,0x0000000e,0x505f6c67,0x65567265,0x78657472,
+ 0x00000000,0x00060006,0x0000000e,0x00000000,0x505f6c67,0x7469736f,0x006e6f69,0x00030005,
+ 0x00000010,0x00000000,0x00030005,0x00000014,0x00736f70,0x00040047,0x00000009,0x0000001e,
+ 0x00000000,0x00040047,0x0000000b,0x0000001e,0x00000001,0x00050048,0x0000000e,0x00000000,
+ 0x0000000b,0x00000000,0x00030047,0x0000000e,0x00000002,0x00040047,0x00000014,0x0000001e,
+ 0x00000000,0x00020013,0x00000002,0x00030021,0x00000003,0x00000002,0x00030016,0x00000006,
+ 0x00000020,0x00040017,0x00000007,0x00000006,0x00000002,0x00040020,0x00000008,0x00000003,
+ 0x00000007,0x0004003b,0x00000008,0x00000009,0x00000003,0x00040020,0x0000000a,0x00000001,
+ 0x00000007,0x0004003b,0x0000000a,0x0000000b,0x00000001,0x00040017,0x0000000d,0x00000006,
+ 0x00000004,0x0003001e,0x0000000e,0x0000000d,0x00040020,0x0000000f,0x00000003,0x0000000e,
+ 0x0004003b,0x0000000f,0x00000010,0x00000003,0x00040015,0x00000011,0x00000020,0x00000001,
+ 0x0004002b,0x00000011,0x00000012,0x00000000,0x00040020,0x00000013,0x00000001,0x0000000d,
+ 0x0004003b,0x00000013,0x00000014,0x00000001,0x00040020,0x00000016,0x00000003,0x0000000d,
+ 0x00050036,0x00000002,0x00000004,0x00000000,0x00000003,0x000200f8,0x00000005,0x0004003d,
+ 0x00000007,0x0000000c,0x0000000b,0x0003003e,0x00000009,0x0000000c,0x0004003d,0x0000000d,
+ 0x00000015,0x00000014,0x00050041,0x00000016,0x00000017,0x00000010,0x00000012,0x0003003e,
+ 0x00000017,0x00000015,0x000100fd,0x00010038
+};
+
+struct texture_object {
+ VkSampler sampler;
+
+ VkImage image;
+ VkImageLayout imageLayout;
+
+ VkDeviceMemory mem;
+ VkImageView view;
+ int32_t tex_width, tex_height;
+};
+
+static int validation_error = 0;
+
+VKAPI_ATTR VkBool32 VKAPI_CALL
+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
+
+ return false;
+}
+
+typedef struct {
+ VkImage image;
+ VkCommandBuffer cmd;
+ VkImageView view;
+} SwapchainBuffers;
+
+struct demo {
+ GLFWwindow* window;
+ VkSurfaceKHR surface;
+ bool use_staging_buffer;
+
+ 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];
+ const char *enabled_layers[64];
+
+ int width, height;
+ VkFormat format;
+ VkColorSpaceKHR color_space;
+
+ uint32_t swapchainImageCount;
+ VkSwapchainKHR swapchain;
+ SwapchainBuffers *buffers;
+
+ VkCommandPool cmd_pool;
+
+ struct {
+ VkFormat format;
+
+ VkImage image;
+ VkDeviceMemory mem;
+ VkImageView view;
+ } depth;
+
+ struct texture_object textures[DEMO_TEXTURE_COUNT];
+
+ struct {
+ VkBuffer buf;
+ VkDeviceMemory mem;
+
+ VkPipelineVertexInputStateCreateInfo vi;
+ VkVertexInputBindingDescription vi_bindings[1];
+ VkVertexInputAttributeDescription vi_attrs[2];
+ } vertices;
+
+ VkCommandBuffer setup_cmd; // Command Buffer for initialization commands
+ VkCommandBuffer draw_cmd; // Command Buffer for drawing commands
+ VkPipelineLayout pipeline_layout;
+ VkDescriptorSetLayout desc_layout;
+ VkPipelineCache pipelineCache;
+ VkRenderPass render_pass;
+ VkPipeline pipeline;
+
+ VkShaderModule vert_shader_module;
+ VkShaderModule frag_shader_module;
+
+ VkDescriptorPool desc_pool;
+ VkDescriptorSet desc_set;
+
+ VkFramebuffer *framebuffers;
+
+ VkPhysicalDeviceMemoryProperties memory_properties;
+
+ int32_t curFrame;
+ int32_t frameCount;
+ bool validate;
+ bool use_break;
+ VkDebugReportCallbackEXT msg_callback;
+
+ float depthStencil;
+ float depthIncrement;
+
+ uint32_t current_buffer;
+ 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);
+
+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 < VK_MAX_MEMORY_TYPES; i++) {
+ if ((typeBits & 1) == 1) {
+ // Type is available, does it match user properties?
+ if ((demo->memory_properties.memoryTypes[i].propertyFlags &
+ requirements_mask) == requirements_mask) {
+ *typeIndex = i;
+ return true;
+ }
+ }
+ typeBits >>= 1;
+ }
+ // No memory types matched, return failure
+ return false;
+}
+
+static void demo_flush_init_cmd(struct demo *demo) {
+ VkResult U_ASSERT_ONLY err;
+
+ if (demo->setup_cmd == VK_NULL_HANDLE)
+ return;
+
+ err = vkEndCommandBuffer(demo->setup_cmd);
+ assert(!err);
+
+ const VkCommandBuffer cmd_bufs[] = {demo->setup_cmd};
+ VkFence nullFence = {VK_NULL_HANDLE};
+ VkSubmitInfo submit_info = {.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO,
+ .pNext = NULL,
+ .waitSemaphoreCount = 0,
+ .pWaitSemaphores = NULL,
+ .pWaitDstStageMask = NULL,
+ .commandBufferCount = 1,
+ .pCommandBuffers = cmd_bufs,
+ .signalSemaphoreCount = 0,
+ .pSignalSemaphores = NULL};
+
+ err = vkQueueSubmit(demo->queue, 1, &submit_info, nullFence);
+ assert(!err);
+
+ err = vkQueueWaitIdle(demo->queue);
+ assert(!err);
+
+ vkFreeCommandBuffers(demo->device, demo->cmd_pool, 1, cmd_bufs);
+ demo->setup_cmd = VK_NULL_HANDLE;
+}
+
+static void demo_set_image_layout(struct demo *demo, VkImage image,
+ VkImageAspectFlags aspectMask,
+ VkImageLayout old_image_layout,
+ VkImageLayout new_image_layout,
+ VkAccessFlagBits srcAccessMask) {
+
+ VkResult U_ASSERT_ONLY err;
+
+ if (demo->setup_cmd == VK_NULL_HANDLE) {
+ const VkCommandBufferAllocateInfo cmd = {
+ .sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO,
+ .pNext = NULL,
+ .commandPool = demo->cmd_pool,
+ .level = VK_COMMAND_BUFFER_LEVEL_PRIMARY,
+ .commandBufferCount = 1,
+ };
+
+ err = vkAllocateCommandBuffers(demo->device, &cmd, &demo->setup_cmd);
+ assert(!err);
+
+ VkCommandBufferBeginInfo cmd_buf_info = {
+ .sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO,
+ .pNext = NULL,
+ .flags = 0,
+ .pInheritanceInfo = NULL,
+ };
+ err = vkBeginCommandBuffer(demo->setup_cmd, &cmd_buf_info);
+ assert(!err);
+ }
+
+ VkImageMemoryBarrier image_memory_barrier = {
+ .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER,
+ .pNext = NULL,
+ .srcAccessMask = srcAccessMask,
+ .dstAccessMask = 0,
+ .oldLayout = old_image_layout,
+ .newLayout = new_image_layout,
+ .image = image,
+ .subresourceRange = {aspectMask, 0, 1, 0, 1}};
+
+ if (new_image_layout == VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL) {
+ /* Make sure anything that was copying from this image has completed */
+ image_memory_barrier.dstAccessMask = VK_ACCESS_TRANSFER_READ_BIT;
+ }
+
+ if (new_image_layout == VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL) {
+ image_memory_barrier.dstAccessMask =
+ VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT;
+ }
+
+ if (new_image_layout == VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL) {
+ image_memory_barrier.dstAccessMask =
+ VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT;
+ }
+
+ if (new_image_layout == VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL) {
+ /* Make sure any Copy or CPU writes to image are flushed */
+ image_memory_barrier.dstAccessMask =
+ VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_INPUT_ATTACHMENT_READ_BIT;
+ }
+
+ VkImageMemoryBarrier *pmemory_barrier = &image_memory_barrier;
+
+ VkPipelineStageFlags src_stages = VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT;
+ VkPipelineStageFlags dest_stages = VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT;
+
+ vkCmdPipelineBarrier(demo->setup_cmd, src_stages, dest_stages, 0, 0, NULL,
+ 0, NULL, 1, pmemory_barrier);
+}
+
+static void demo_draw_build_cmd(struct demo *demo) {
+ const VkCommandBufferBeginInfo cmd_buf_info = {
+ .sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO,
+ .pNext = NULL,
+ .flags = 0,
+ .pInheritanceInfo = NULL,
+ };
+ const VkClearValue clear_values[2] = {
+ [0] = {.color.float32 = {0.2f, 0.2f, 0.2f, 0.2f}},
+ [1] = {.depthStencil = {demo->depthStencil, 0}},
+ };
+ const VkRenderPassBeginInfo rp_begin = {
+ .sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO,
+ .pNext = NULL,
+ .renderPass = demo->render_pass,
+ .framebuffer = demo->framebuffers[demo->current_buffer],
+ .renderArea.offset.x = 0,
+ .renderArea.offset.y = 0,
+ .renderArea.extent.width = demo->width,
+ .renderArea.extent.height = demo->height,
+ .clearValueCount = 2,
+ .pClearValues = clear_values,
+ };
+ VkResult U_ASSERT_ONLY err;
+
+ 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);
+ vkCmdBindDescriptorSets(demo->draw_cmd, VK_PIPELINE_BIND_POINT_GRAPHICS,
+ demo->pipeline_layout, 0, 1, &demo->desc_set, 0,
+ NULL);
+
+ VkViewport viewport;
+ memset(&viewport, 0, sizeof(viewport));
+ viewport.height = (float)demo->height;
+ viewport.width = (float)demo->width;
+ viewport.minDepth = (float)0.0f;
+ viewport.maxDepth = (float)1.0f;
+ vkCmdSetViewport(demo->draw_cmd, 0, 1, &viewport);
+
+ VkRect2D scissor;
+ memset(&scissor, 0, sizeof(scissor));
+ scissor.extent.width = demo->width;
+ scissor.extent.height = demo->height;
+ scissor.offset.x = 0;
+ scissor.offset.y = 0;
+ vkCmdSetScissor(demo->draw_cmd, 0, 1, &scissor);
+
+ VkDeviceSize offsets[1] = {0};
+ vkCmdBindVertexBuffers(demo->draw_cmd, VERTEX_BUFFER_BIND_ID, 1,
+ &demo->vertices.buf, offsets);
+
+ vkCmdDraw(demo->draw_cmd, 3, 1, 0, 0);
+ vkCmdEndRenderPass(demo->draw_cmd);
+
+ VkImageMemoryBarrier prePresentBarrier = {
+ .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER,
+ .pNext = NULL,
+ .srcAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT,
+ .dstAccessMask = VK_ACCESS_MEMORY_READ_BIT,
+ .oldLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL,
+ .newLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR,
+ .srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
+ .dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
+ .subresourceRange = {VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0, 1}};
+
+ prePresentBarrier.image = demo->buffers[demo->current_buffer].image;
+ VkImageMemoryBarrier *pmemory_barrier = &prePresentBarrier;
+ vkCmdPipelineBarrier(demo->draw_cmd, VK_PIPELINE_STAGE_ALL_COMMANDS_BIT,
+ VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT, 0, 0, NULL, 0,
+ NULL, 1, pmemory_barrier);
+
+ err = vkEndCommandBuffer(demo->draw_cmd);
+ assert(!err);
+}
+
+static void demo_draw(struct demo *demo) {
+ VkResult U_ASSERT_ONLY err;
+ VkSemaphore imageAcquiredSemaphore, drawCompleteSemaphore;
+ VkSemaphoreCreateInfo semaphoreCreateInfo = {
+ .sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO,
+ .pNext = NULL,
+ .flags = 0,
+ };
+
+ 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 = vkAcquireNextImageKHR(demo->device, demo->swapchain, UINT64_MAX,
+ imageAcquiredSemaphore,
+ (VkFence)0, // TODO: Show use of fence
+ &demo->current_buffer);
+ if (err == VK_ERROR_OUT_OF_DATE_KHR) {
+ // demo->swapchain is out of date (e.g. the window was resized) and
+ // must be recreated:
+ demo_resize(demo);
+ demo_draw(demo);
+ 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
+ // presentation engine will still present the image correctly.
+ } else {
+ assert(!err);
+ }
+
+ demo_flush_init_cmd(demo);
+
+ // Wait for the present complete semaphore to be signaled to ensure
+ // that the image won't be rendered to until the presentation
+ // engine has fully released ownership to the application, and it is
+ // okay to render to the image.
+
+ demo_draw_build_cmd(demo);
+ VkFence nullFence = VK_NULL_HANDLE;
+ VkPipelineStageFlags pipe_stage_flags =
+ VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT;
+ VkSubmitInfo submit_info = {.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO,
+ .pNext = NULL,
+ .waitSemaphoreCount = 1,
+ .pWaitSemaphores = &imageAcquiredSemaphore,
+ .pWaitDstStageMask = &pipe_stage_flags,
+ .commandBufferCount = 1,
+ .pCommandBuffers = &demo->draw_cmd,
+ .signalSemaphoreCount = 1,
+ .pSignalSemaphores = &drawCompleteSemaphore};
+
+ err = vkQueueSubmit(demo->queue, 1, &submit_info, nullFence);
+ assert(!err);
+
+ VkPresentInfoKHR present = {
+ .sType = VK_STRUCTURE_TYPE_PRESENT_INFO_KHR,
+ .pNext = NULL,
+ .waitSemaphoreCount = 1,
+ .pWaitSemaphores = &drawCompleteSemaphore,
+ .swapchainCount = 1,
+ .pSwapchains = &demo->swapchain,
+ .pImageIndices = &demo->current_buffer,
+ };
+
+ err = vkQueuePresentKHR(demo->queue, &present);
+ if (err == VK_ERROR_OUT_OF_DATE_KHR) {
+ // demo->swapchain is out of date (e.g. the window was resized) and
+ // must be recreated:
+ demo_resize(demo);
+ } else if (err == VK_SUBOPTIMAL_KHR) {
+ // demo->swapchain is not as optimal as it could be, but the platform's
+ // presentation engine will still present the image correctly.
+ } else {
+ assert(!err);
+ }
+
+ err = vkQueueWaitIdle(demo->queue);
+ assert(err == VK_SUCCESS);
+
+ vkDestroySemaphore(demo->device, imageAcquiredSemaphore, NULL);
+ vkDestroySemaphore(demo->device, drawCompleteSemaphore, NULL);
+}
+
+static void demo_prepare_buffers(struct demo *demo) {
+ VkResult U_ASSERT_ONLY err;
+ VkSwapchainKHR oldSwapchain = demo->swapchain;
+
+ // Check the surface capabilities and formats
+ VkSurfaceCapabilitiesKHR surfCapabilities;
+ err = vkGetPhysicalDeviceSurfaceCapabilitiesKHR(
+ demo->gpu, demo->surface, &surfCapabilities);
+ assert(!err);
+
+ uint32_t presentModeCount;
+ err = vkGetPhysicalDeviceSurfacePresentModesKHR(
+ demo->gpu, demo->surface, &presentModeCount, NULL);
+ assert(!err);
+ VkPresentModeKHR *presentModes =
+ (VkPresentModeKHR *)malloc(presentModeCount * sizeof(VkPresentModeKHR));
+ assert(presentModes);
+ err = vkGetPhysicalDeviceSurfacePresentModesKHR(
+ demo->gpu, demo->surface, &presentModeCount, presentModes);
+ assert(!err);
+
+ VkExtent2D swapchainExtent;
+ // 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;
+ demo->width = surfCapabilities.currentExtent.width;
+ demo->height = surfCapabilities.currentExtent.height;
+ }
+
+ VkPresentModeKHR swapchainPresentMode = VK_PRESENT_MODE_FIFO_KHR;
+
+ // 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) &&
+ (desiredNumOfSwapchainImages > surfCapabilities.maxImageCount)) {
+ // Application must settle for fewer images than desired:
+ desiredNumOfSwapchainImages = surfCapabilities.maxImageCount;
+ }
+
+ VkSurfaceTransformFlagsKHR preTransform;
+ if (surfCapabilities.supportedTransforms &
+ VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR) {
+ preTransform = VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR;
+ } else {
+ preTransform = surfCapabilities.currentTransform;
+ }
+
+ const VkSwapchainCreateInfoKHR swapchain = {
+ .sType = VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR,
+ .pNext = NULL,
+ .surface = demo->surface,
+ .minImageCount = desiredNumOfSwapchainImages,
+ .imageFormat = demo->format,
+ .imageColorSpace = demo->color_space,
+ .imageExtent =
+ {
+ .width = swapchainExtent.width, .height = swapchainExtent.height,
+ },
+ .imageUsage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT,
+ .preTransform = preTransform,
+ .compositeAlpha = VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR,
+ .imageArrayLayers = 1,
+ .imageSharingMode = VK_SHARING_MODE_EXCLUSIVE,
+ .queueFamilyIndexCount = 0,
+ .pQueueFamilyIndices = NULL,
+ .presentMode = swapchainPresentMode,
+ .oldSwapchain = oldSwapchain,
+ .clipped = true,
+ };
+ uint32_t i;
+
+ err = vkCreateSwapchainKHR(demo->device, &swapchain, NULL, &demo->swapchain);
+ assert(!err);
+
+ // If we just re-created an existing swapchain, we should destroy the old
+ // swapchain at this point.
+ // Note: destroying the swapchain also cleans up all its associated
+ // presentable images once the platform is done with them.
+ if (oldSwapchain != VK_NULL_HANDLE) {
+ vkDestroySwapchainKHR(demo->device, oldSwapchain, NULL);
+ }
+
+ err = vkGetSwapchainImagesKHR(demo->device, demo->swapchain,
+ &demo->swapchainImageCount, NULL);
+ assert(!err);
+
+ VkImage *swapchainImages =
+ (VkImage *)malloc(demo->swapchainImageCount * sizeof(VkImage));
+ assert(swapchainImages);
+ err = vkGetSwapchainImagesKHR(demo->device, demo->swapchain,
+ &demo->swapchainImageCount,
+ swapchainImages);
+ assert(!err);
+
+ demo->buffers = (SwapchainBuffers *)malloc(sizeof(SwapchainBuffers) *
+ demo->swapchainImageCount);
+ assert(demo->buffers);
+
+ for (i = 0; i < demo->swapchainImageCount; i++) {
+ VkImageViewCreateInfo color_attachment_view = {
+ .sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO,
+ .pNext = NULL,
+ .format = demo->format,
+ .components =
+ {
+ .r = VK_COMPONENT_SWIZZLE_R,
+ .g = VK_COMPONENT_SWIZZLE_G,
+ .b = VK_COMPONENT_SWIZZLE_B,
+ .a = VK_COMPONENT_SWIZZLE_A,
+ },
+ .subresourceRange = {.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT,
+ .baseMipLevel = 0,
+ .levelCount = 1,
+ .baseArrayLayer = 0,
+ .layerCount = 1},
+ .viewType = VK_IMAGE_VIEW_TYPE_2D,
+ .flags = 0,
+ };
+
+ demo->buffers[i].image = swapchainImages[i];
+
+ color_attachment_view.image = demo->buffers[i].image;
+
+ err = vkCreateImageView(demo->device, &color_attachment_view, NULL,
+ &demo->buffers[i].view);
+ assert(!err);
+ }
+
+ demo->current_buffer = 0;
+
+ if (NULL != presentModes) {
+ free(presentModes);
+ }
+}
+
+static void demo_prepare_depth(struct demo *demo) {
+ const VkFormat depth_format = VK_FORMAT_D16_UNORM;
+ const VkImageCreateInfo image = {
+ .sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO,
+ .pNext = NULL,
+ .imageType = VK_IMAGE_TYPE_2D,
+ .format = depth_format,
+ .extent = {demo->width, demo->height, 1},
+ .mipLevels = 1,
+ .arrayLayers = 1,
+ .samples = VK_SAMPLE_COUNT_1_BIT,
+ .tiling = VK_IMAGE_TILING_OPTIMAL,
+ .usage = VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT,
+ .flags = 0,
+ };
+ VkMemoryAllocateInfo mem_alloc = {
+ .sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO,
+ .pNext = NULL,
+ .allocationSize = 0,
+ .memoryTypeIndex = 0,
+ };
+ VkImageViewCreateInfo view = {
+ .sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO,
+ .pNext = NULL,
+ .image = VK_NULL_HANDLE,
+ .format = depth_format,
+ .subresourceRange = {.aspectMask = VK_IMAGE_ASPECT_DEPTH_BIT,
+ .baseMipLevel = 0,
+ .levelCount = 1,
+ .baseArrayLayer = 0,
+ .layerCount = 1},
+ .flags = 0,
+ .viewType = VK_IMAGE_VIEW_TYPE_2D,
+ };
+
+ VkMemoryRequirements mem_reqs;
+ VkResult U_ASSERT_ONLY err;
+ bool U_ASSERT_ONLY pass;
+
+ demo->depth.format = depth_format;
+
+ /* create image */
+ err = vkCreateImage(demo->device, &image, NULL, &demo->depth.image);
+ assert(!err);
+
+ /* get memory requirements for this object */
+ vkGetImageMemoryRequirements(demo->device, demo->depth.image, &mem_reqs);
+
+ /* select memory size and type */
+ mem_alloc.allocationSize = mem_reqs.size;
+ pass = memory_type_from_properties(demo, mem_reqs.memoryTypeBits,
+ 0, /* No requirements */
+ &mem_alloc.memoryTypeIndex);
+ assert(pass);
+
+ /* allocate memory */
+ err = vkAllocateMemory(demo->device, &mem_alloc, NULL, &demo->depth.mem);
+ assert(!err);
+
+ /* bind memory */
+ err =
+ vkBindImageMemory(demo->device, demo->depth.image, demo->depth.mem, 0);
+ assert(!err);
+
+ demo_set_image_layout(demo, demo->depth.image, VK_IMAGE_ASPECT_DEPTH_BIT,
+ VK_IMAGE_LAYOUT_UNDEFINED,
+ VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL,
+ 0);
+
+ /* create image view */
+ view.image = demo->depth.image;
+ err = vkCreateImageView(demo->device, &view, NULL, &demo->depth.view);
+ assert(!err);
+}
+
+static void
+demo_prepare_texture_image(struct demo *demo, const uint32_t *tex_colors,
+ struct texture_object *tex_obj, VkImageTiling tiling,
+ VkImageUsageFlags usage, VkFlags required_props) {
+ const VkFormat tex_format = VK_FORMAT_B8G8R8A8_UNORM;
+ const int32_t tex_width = 2;
+ const int32_t tex_height = 2;
+ VkResult U_ASSERT_ONLY err;
+ bool U_ASSERT_ONLY pass;
+
+ tex_obj->tex_width = tex_width;
+ tex_obj->tex_height = tex_height;
+
+ const VkImageCreateInfo image_create_info = {
+ .sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO,
+ .pNext = NULL,
+ .imageType = VK_IMAGE_TYPE_2D,
+ .format = tex_format,
+ .extent = {tex_width, tex_height, 1},
+ .mipLevels = 1,
+ .arrayLayers = 1,
+ .samples = VK_SAMPLE_COUNT_1_BIT,
+ .tiling = tiling,
+ .usage = usage,
+ .flags = 0,
+ .initialLayout = VK_IMAGE_LAYOUT_PREINITIALIZED
+ };
+ VkMemoryAllocateInfo mem_alloc = {
+ .sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO,
+ .pNext = NULL,
+ .allocationSize = 0,
+ .memoryTypeIndex = 0,
+ };
+
+ VkMemoryRequirements mem_reqs;
+
+ err =
+ vkCreateImage(demo->device, &image_create_info, NULL, &tex_obj->image);
+ assert(!err);
+
+ vkGetImageMemoryRequirements(demo->device, tex_obj->image, &mem_reqs);
+
+ mem_alloc.allocationSize = mem_reqs.size;
+ pass =
+ memory_type_from_properties(demo, mem_reqs.memoryTypeBits,
+ required_props, &mem_alloc.memoryTypeIndex);
+ assert(pass);
+
+ /* allocate memory */
+ err = vkAllocateMemory(demo->device, &mem_alloc, NULL, &tex_obj->mem);
+ assert(!err);
+
+ /* bind memory */
+ err = vkBindImageMemory(demo->device, tex_obj->image, tex_obj->mem, 0);
+ assert(!err);
+
+ if (required_props & VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT) {
+ const VkImageSubresource subres = {
+ .aspectMask = VK_IMAGE_ASPECT_COLOR_BIT,
+ .mipLevel = 0,
+ .arrayLayer = 0,
+ };
+ VkSubresourceLayout layout;
+ void *data;
+ int32_t x, y;
+
+ vkGetImageSubresourceLayout(demo->device, tex_obj->image, &subres,
+ &layout);
+
+ err = vkMapMemory(demo->device, tex_obj->mem, 0,
+ mem_alloc.allocationSize, 0, &data);
+ assert(!err);
+
+ for (y = 0; y < tex_height; y++) {
+ uint32_t *row = (uint32_t *)((char *)data + layout.rowPitch * y);
+ for (x = 0; x < tex_width; x++)
+ row[x] = tex_colors[(x & 1) ^ (y & 1)];
+ }
+
+ vkUnmapMemory(demo->device, tex_obj->mem);
+ }
+
+ 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_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 */
+}
+
+static void demo_destroy_texture_image(struct demo *demo,
+ struct texture_object *tex_obj) {
+ /* clean up staging resources */
+ vkDestroyImage(demo->device, tex_obj->image, NULL);
+ vkFreeMemory(demo->device, tex_obj->mem, NULL);
+}
+
+static void demo_prepare_textures(struct demo *demo) {
+ const VkFormat tex_format = VK_FORMAT_B8G8R8A8_UNORM;
+ VkFormatProperties props;
+ const uint32_t tex_colors[DEMO_TEXTURE_COUNT][2] = {
+ {0xffff0000, 0xff00ff00},
+ };
+ uint32_t i;
+ VkResult U_ASSERT_ONLY err;
+
+ vkGetPhysicalDeviceFormatProperties(demo->gpu, tex_format, &props);
+
+ for (i = 0; i < DEMO_TEXTURE_COUNT; i++) {
+ if ((props.linearTilingFeatures &
+ 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 |
+ 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 |
+ VK_MEMORY_PROPERTY_HOST_COHERENT_BIT);
+
+ demo_prepare_texture_image(
+ demo, tex_colors[i], &demo->textures[i],
+ VK_IMAGE_TILING_OPTIMAL,
+ (VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_SAMPLED_BIT),
+ VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT);
+
+ demo_set_image_layout(demo, staging_texture.image,
+ VK_IMAGE_ASPECT_COLOR_BIT,
+ staging_texture.imageLayout,
+ 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,
+ 0);
+
+ VkImageCopy copy_region = {
+ .srcSubresource = {VK_IMAGE_ASPECT_COLOR_BIT, 0, 0, 1},
+ .srcOffset = {0, 0, 0},
+ .dstSubresource = {VK_IMAGE_ASPECT_COLOR_BIT, 0, 0, 1},
+ .dstOffset = {0, 0, 0},
+ .extent = {staging_texture.tex_width,
+ staging_texture.tex_height, 1},
+ };
+ vkCmdCopyImage(
+ demo->setup_cmd, staging_texture.image,
+ VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, demo->textures[i].image,
+ VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, &copy_region);
+
+ demo_set_image_layout(demo, demo->textures[i].image,
+ VK_IMAGE_ASPECT_COLOR_BIT,
+ VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
+ demo->textures[i].imageLayout,
+ 0);
+
+ demo_flush_init_cmd(demo);
+
+ demo_destroy_texture_image(demo, &staging_texture);
+ } else {
+ /* Can't support VK_FORMAT_B8G8R8A8_UNORM !? */
+ assert(!"No support for B8G8R8A8_UNORM as texture image format");
+ }
+
+ const VkSamplerCreateInfo sampler = {
+ .sType = VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO,
+ .pNext = NULL,
+ .magFilter = VK_FILTER_NEAREST,
+ .minFilter = VK_FILTER_NEAREST,
+ .mipmapMode = VK_SAMPLER_MIPMAP_MODE_NEAREST,
+ .addressModeU = VK_SAMPLER_ADDRESS_MODE_REPEAT,
+ .addressModeV = VK_SAMPLER_ADDRESS_MODE_REPEAT,
+ .addressModeW = VK_SAMPLER_ADDRESS_MODE_REPEAT,
+ .mipLodBias = 0.0f,
+ .anisotropyEnable = VK_FALSE,
+ .maxAnisotropy = 1,
+ .compareOp = VK_COMPARE_OP_NEVER,
+ .minLod = 0.0f,
+ .maxLod = 0.0f,
+ .borderColor = VK_BORDER_COLOR_FLOAT_OPAQUE_WHITE,
+ .unnormalizedCoordinates = VK_FALSE,
+ };
+ VkImageViewCreateInfo view = {
+ .sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO,
+ .pNext = NULL,
+ .image = VK_NULL_HANDLE,
+ .viewType = VK_IMAGE_VIEW_TYPE_2D,
+ .format = tex_format,
+ .components =
+ {
+ VK_COMPONENT_SWIZZLE_R, VK_COMPONENT_SWIZZLE_G,
+ VK_COMPONENT_SWIZZLE_B, VK_COMPONENT_SWIZZLE_A,
+ },
+ .subresourceRange = {VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0, 1},
+ .flags = 0,
+ };
+
+ /* create sampler */
+ err = vkCreateSampler(demo->device, &sampler, NULL,
+ &demo->textures[i].sampler);
+ assert(!err);
+
+ /* create image view */
+ view.image = demo->textures[i].image;
+ err = vkCreateImageView(demo->device, &view, NULL,
+ &demo->textures[i].view);
+ assert(!err);
+ }
+}
+
+static void demo_prepare_vertices(struct demo *demo) {
+ // clang-format off
+ const float vb[3][5] = {
+ /* position texcoord */
+ { -1.0f, -1.0f, 0.25f, 0.0f, 0.0f },
+ { 1.0f, -1.0f, 0.25f, 1.0f, 0.0f },
+ { 0.0f, 1.0f, 1.0f, 0.5f, 1.0f },
+ };
+ // clang-format on
+ const VkBufferCreateInfo buf_info = {
+ .sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO,
+ .pNext = NULL,
+ .size = sizeof(vb),
+ .usage = VK_BUFFER_USAGE_VERTEX_BUFFER_BIT,
+ .flags = 0,
+ };
+ VkMemoryAllocateInfo mem_alloc = {
+ .sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO,
+ .pNext = NULL,
+ .allocationSize = 0,
+ .memoryTypeIndex = 0,
+ };
+ VkMemoryRequirements mem_reqs;
+ VkResult U_ASSERT_ONLY err;
+ bool U_ASSERT_ONLY pass;
+ void *data;
+
+ memset(&demo->vertices, 0, sizeof(demo->vertices));
+
+ err = vkCreateBuffer(demo->device, &buf_info, NULL, &demo->vertices.buf);
+ assert(!err);
+
+ vkGetBufferMemoryRequirements(demo->device, demo->vertices.buf, &mem_reqs);
+ assert(!err);
+
+ 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_COHERENT_BIT,
+ &mem_alloc.memoryTypeIndex);
+ assert(pass);
+
+ err = vkAllocateMemory(demo->device, &mem_alloc, NULL, &demo->vertices.mem);
+ assert(!err);
+
+ err = vkMapMemory(demo->device, demo->vertices.mem, 0,
+ mem_alloc.allocationSize, 0, &data);
+ assert(!err);
+
+ memcpy(data, vb, sizeof(vb));
+
+ vkUnmapMemory(demo->device, demo->vertices.mem);
+
+ err = vkBindBufferMemory(demo->device, demo->vertices.buf,
+ demo->vertices.mem, 0);
+ assert(!err);
+
+ demo->vertices.vi.sType =
+ VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO;
+ demo->vertices.vi.pNext = NULL;
+ demo->vertices.vi.vertexBindingDescriptionCount = 1;
+ demo->vertices.vi.pVertexBindingDescriptions = demo->vertices.vi_bindings;
+ demo->vertices.vi.vertexAttributeDescriptionCount = 2;
+ demo->vertices.vi.pVertexAttributeDescriptions = demo->vertices.vi_attrs;
+
+ demo->vertices.vi_bindings[0].binding = VERTEX_BUFFER_BIND_ID;
+ demo->vertices.vi_bindings[0].stride = sizeof(vb[0]);
+ demo->vertices.vi_bindings[0].inputRate = VK_VERTEX_INPUT_RATE_VERTEX;
+
+ demo->vertices.vi_attrs[0].binding = VERTEX_BUFFER_BIND_ID;
+ demo->vertices.vi_attrs[0].location = 0;
+ demo->vertices.vi_attrs[0].format = VK_FORMAT_R32G32B32_SFLOAT;
+ demo->vertices.vi_attrs[0].offset = 0;
+
+ demo->vertices.vi_attrs[1].binding = VERTEX_BUFFER_BIND_ID;
+ demo->vertices.vi_attrs[1].location = 1;
+ demo->vertices.vi_attrs[1].format = VK_FORMAT_R32G32_SFLOAT;
+ demo->vertices.vi_attrs[1].offset = sizeof(float) * 3;
+}
+
+static void demo_prepare_descriptor_layout(struct demo *demo) {
+ const VkDescriptorSetLayoutBinding layout_binding = {
+ .binding = 0,
+ .descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER,
+ .descriptorCount = DEMO_TEXTURE_COUNT,
+ .stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT,
+ .pImmutableSamplers = NULL,
+ };
+ const VkDescriptorSetLayoutCreateInfo descriptor_layout = {
+ .sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO,
+ .pNext = NULL,
+ .bindingCount = 1,
+ .pBindings = &layout_binding,
+ };
+ VkResult U_ASSERT_ONLY err;
+
+ err = vkCreateDescriptorSetLayout(demo->device, &descriptor_layout, NULL,
+ &demo->desc_layout);
+ assert(!err);
+
+ const VkPipelineLayoutCreateInfo pPipelineLayoutCreateInfo = {
+ .sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO,
+ .pNext = NULL,
+ .setLayoutCount = 1,
+ .pSetLayouts = &demo->desc_layout,
+ };
+
+ err = vkCreatePipelineLayout(demo->device, &pPipelineLayoutCreateInfo, NULL,
+ &demo->pipeline_layout);
+ assert(!err);
+}
+
+static void demo_prepare_render_pass(struct demo *demo) {
+ const VkAttachmentDescription attachments[2] = {
+ [0] =
+ {
+ .format = demo->format,
+ .samples = VK_SAMPLE_COUNT_1_BIT,
+ .loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR,
+ .storeOp = VK_ATTACHMENT_STORE_OP_STORE,
+ .stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE,
+ .stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE,
+ .initialLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL,
+ .finalLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL,
+ },
+ [1] =
+ {
+ .format = demo->depth.format,
+ .samples = VK_SAMPLE_COUNT_1_BIT,
+ .loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR,
+ .storeOp = VK_ATTACHMENT_STORE_OP_DONT_CARE,
+ .stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE,
+ .stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE,
+ .initialLayout =
+ VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL,
+ .finalLayout =
+ VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL,
+ },
+ };
+ const VkAttachmentReference color_reference = {
+ .attachment = 0, .layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL,
+ };
+ const VkAttachmentReference depth_reference = {
+ .attachment = 1,
+ .layout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL,
+ };
+ const VkSubpassDescription subpass = {
+ .pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS,
+ .flags = 0,
+ .inputAttachmentCount = 0,
+ .pInputAttachments = NULL,
+ .colorAttachmentCount = 1,
+ .pColorAttachments = &color_reference,
+ .pResolveAttachments = NULL,
+ .pDepthStencilAttachment = &depth_reference,
+ .preserveAttachmentCount = 0,
+ .pPreserveAttachments = NULL,
+ };
+ const VkRenderPassCreateInfo rp_info = {
+ .sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO,
+ .pNext = NULL,
+ .attachmentCount = 2,
+ .pAttachments = attachments,
+ .subpassCount = 1,
+ .pSubpasses = &subpass,
+ .dependencyCount = 0,
+ .pDependencies = NULL,
+ };
+ VkResult U_ASSERT_ONLY err;
+
+ err = vkCreateRenderPass(demo->device, &rp_info, NULL, &demo->render_pass);
+ assert(!err);
+}
+
+static VkShaderModule
+demo_prepare_shader_module(struct demo *demo, const void *code, size_t size) {
+ VkShaderModuleCreateInfo moduleCreateInfo;
+ VkShaderModule module;
+ VkResult U_ASSERT_ONLY err;
+
+ moduleCreateInfo.sType = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO;
+ moduleCreateInfo.pNext = NULL;
+
+ moduleCreateInfo.codeSize = size;
+ moduleCreateInfo.pCode = code;
+ moduleCreateInfo.flags = 0;
+ err = vkCreateShaderModule(demo->device, &moduleCreateInfo, NULL, &module);
+ assert(!err);
+
+ return module;
+}
+
+static VkShaderModule demo_prepare_vs(struct demo *demo) {
+ size_t size = sizeof(vertShaderCode);
+
+ demo->vert_shader_module =
+ demo_prepare_shader_module(demo, vertShaderCode, size);
+
+ return demo->vert_shader_module;
+}
+
+static VkShaderModule demo_prepare_fs(struct demo *demo) {
+ size_t size = sizeof(fragShaderCode);
+
+ demo->frag_shader_module =
+ demo_prepare_shader_module(demo, fragShaderCode, size);
+
+ return demo->frag_shader_module;
+}
+
+static void demo_prepare_pipeline(struct demo *demo) {
+ VkGraphicsPipelineCreateInfo pipeline;
+ VkPipelineCacheCreateInfo pipelineCache;
+
+ VkPipelineVertexInputStateCreateInfo vi;
+ VkPipelineInputAssemblyStateCreateInfo ia;
+ VkPipelineRasterizationStateCreateInfo rs;
+ VkPipelineColorBlendStateCreateInfo cb;
+ VkPipelineDepthStencilStateCreateInfo ds;
+ VkPipelineViewportStateCreateInfo vp;
+ VkPipelineMultisampleStateCreateInfo ms;
+ VkDynamicState dynamicStateEnables[2];
+ VkPipelineDynamicStateCreateInfo dynamicState;
+
+ VkResult U_ASSERT_ONLY err;
+
+ memset(dynamicStateEnables, 0, sizeof dynamicStateEnables);
+ memset(&dynamicState, 0, sizeof dynamicState);
+ dynamicState.sType = VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO;
+ dynamicState.pDynamicStates = dynamicStateEnables;
+
+ memset(&pipeline, 0, sizeof(pipeline));
+ pipeline.sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO;
+ pipeline.layout = demo->pipeline_layout;
+
+ vi = demo->vertices.vi;
+
+ memset(&ia, 0, sizeof(ia));
+ ia.sType = VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO;
+ ia.topology = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST;
+
+ memset(&rs, 0, sizeof(rs));
+ rs.sType = VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO;
+ rs.polygonMode = VK_POLYGON_MODE_FILL;
+ rs.cullMode = VK_CULL_MODE_BACK_BIT;
+ rs.frontFace = VK_FRONT_FACE_CLOCKWISE;
+ 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;
+ VkPipelineColorBlendAttachmentState att_state[1];
+ memset(att_state, 0, sizeof(att_state));
+ att_state[0].colorWriteMask = 0xf;
+ att_state[0].blendEnable = VK_FALSE;
+ cb.attachmentCount = 1;
+ cb.pAttachments = att_state;
+
+ memset(&vp, 0, sizeof(vp));
+ vp.sType = VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO;
+ vp.viewportCount = 1;
+ dynamicStateEnables[dynamicState.dynamicStateCount++] =
+ VK_DYNAMIC_STATE_VIEWPORT;
+ vp.scissorCount = 1;
+ dynamicStateEnables[dynamicState.dynamicStateCount++] =
+ VK_DYNAMIC_STATE_SCISSOR;
+
+ memset(&ds, 0, sizeof(ds));
+ ds.sType = VK_STRUCTURE_TYPE_PIPELINE_DEPTH_STENCIL_STATE_CREATE_INFO;
+ ds.depthTestEnable = VK_TRUE;
+ ds.depthWriteEnable = VK_TRUE;
+ ds.depthCompareOp = VK_COMPARE_OP_LESS_OR_EQUAL;
+ ds.depthBoundsTestEnable = VK_FALSE;
+ ds.back.failOp = VK_STENCIL_OP_KEEP;
+ ds.back.passOp = VK_STENCIL_OP_KEEP;
+ ds.back.compareOp = VK_COMPARE_OP_ALWAYS;
+ ds.stencilTestEnable = VK_FALSE;
+ ds.front = ds.back;
+
+ memset(&ms, 0, sizeof(ms));
+ ms.sType = VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO;
+ ms.pSampleMask = NULL;
+ ms.rasterizationSamples = VK_SAMPLE_COUNT_1_BIT;
+
+ // Two stages: vs and fs
+ pipeline.stageCount = 2;
+ VkPipelineShaderStageCreateInfo shaderStages[2];
+ memset(&shaderStages, 0, 2 * sizeof(VkPipelineShaderStageCreateInfo));
+
+ shaderStages[0].sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO;
+ shaderStages[0].stage = VK_SHADER_STAGE_VERTEX_BIT;
+ shaderStages[0].module = demo_prepare_vs(demo);
+ shaderStages[0].pName = "main";
+
+ shaderStages[1].sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO;
+ shaderStages[1].stage = VK_SHADER_STAGE_FRAGMENT_BIT;
+ shaderStages[1].module = demo_prepare_fs(demo);
+ shaderStages[1].pName = "main";
+
+ pipeline.pVertexInputState = &vi;
+ pipeline.pInputAssemblyState = &ia;
+ pipeline.pRasterizationState = &rs;
+ pipeline.pColorBlendState = &cb;
+ pipeline.pMultisampleState = &ms;
+ pipeline.pViewportState = &vp;
+ pipeline.pDepthStencilState = &ds;
+ pipeline.pStages = shaderStages;
+ pipeline.renderPass = demo->render_pass;
+ pipeline.pDynamicState = &dynamicState;
+
+ memset(&pipelineCache, 0, sizeof(pipelineCache));
+ pipelineCache.sType = VK_STRUCTURE_TYPE_PIPELINE_CACHE_CREATE_INFO;
+
+ err = vkCreatePipelineCache(demo->device, &pipelineCache, NULL,
+ &demo->pipelineCache);
+ assert(!err);
+ err = vkCreateGraphicsPipelines(demo->device, demo->pipelineCache, 1,
+ &pipeline, NULL, &demo->pipeline);
+ assert(!err);
+
+ vkDestroyPipelineCache(demo->device, demo->pipelineCache, NULL);
+
+ vkDestroyShaderModule(demo->device, demo->frag_shader_module, NULL);
+ vkDestroyShaderModule(demo->device, demo->vert_shader_module, NULL);
+}
+
+static void demo_prepare_descriptor_pool(struct demo *demo) {
+ const VkDescriptorPoolSize type_count = {
+ .type = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER,
+ .descriptorCount = DEMO_TEXTURE_COUNT,
+ };
+ const VkDescriptorPoolCreateInfo descriptor_pool = {
+ .sType = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO,
+ .pNext = NULL,
+ .maxSets = 1,
+ .poolSizeCount = 1,
+ .pPoolSizes = &type_count,
+ };
+ VkResult U_ASSERT_ONLY err;
+
+ err = vkCreateDescriptorPool(demo->device, &descriptor_pool, NULL,
+ &demo->desc_pool);
+ assert(!err);
+}
+
+static void demo_prepare_descriptor_set(struct demo *demo) {
+ VkDescriptorImageInfo tex_descs[DEMO_TEXTURE_COUNT];
+ VkWriteDescriptorSet write;
+ VkResult U_ASSERT_ONLY err;
+ uint32_t i;
+
+ VkDescriptorSetAllocateInfo alloc_info = {
+ .sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO,
+ .pNext = NULL,
+ .descriptorPool = demo->desc_pool,
+ .descriptorSetCount = 1,
+ .pSetLayouts = &demo->desc_layout};
+ err = vkAllocateDescriptorSets(demo->device, &alloc_info, &demo->desc_set);
+ assert(!err);
+
+ memset(&tex_descs, 0, sizeof(tex_descs));
+ for (i = 0; i < DEMO_TEXTURE_COUNT; i++) {
+ tex_descs[i].sampler = demo->textures[i].sampler;
+ tex_descs[i].imageView = demo->textures[i].view;
+ tex_descs[i].imageLayout = VK_IMAGE_LAYOUT_GENERAL;
+ }
+
+ memset(&write, 0, sizeof(write));
+ write.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
+ write.dstSet = demo->desc_set;
+ write.descriptorCount = DEMO_TEXTURE_COUNT;
+ write.descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
+ write.pImageInfo = tex_descs;
+
+ vkUpdateDescriptorSets(demo->device, 1, &write, 0, NULL);
+}
+
+static void demo_prepare_framebuffers(struct demo *demo) {
+ VkImageView attachments[2];
+ attachments[1] = demo->depth.view;
+
+ const VkFramebufferCreateInfo fb_info = {
+ .sType = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO,
+ .pNext = NULL,
+ .renderPass = demo->render_pass,
+ .attachmentCount = 2,
+ .pAttachments = attachments,
+ .width = demo->width,
+ .height = demo->height,
+ .layers = 1,
+ };
+ VkResult U_ASSERT_ONLY err;
+ uint32_t i;
+
+ demo->framebuffers = (VkFramebuffer *)malloc(demo->swapchainImageCount *
+ sizeof(VkFramebuffer));
+ assert(demo->framebuffers);
+
+ for (i = 0; i < demo->swapchainImageCount; i++) {
+ attachments[0] = demo->buffers[i].view;
+ err = vkCreateFramebuffer(demo->device, &fb_info, NULL,
+ &demo->framebuffers[i]);
+ assert(!err);
+ }
+}
+
+static void demo_prepare(struct demo *demo) {
+ VkResult U_ASSERT_ONLY err;
+
+ const VkCommandPoolCreateInfo cmd_pool_info = {
+ .sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO,
+ .pNext = NULL,
+ .queueFamilyIndex = demo->graphics_queue_node_index,
+ .flags = VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT,
+ };
+ err = vkCreateCommandPool(demo->device, &cmd_pool_info, NULL,
+ &demo->cmd_pool);
+ assert(!err);
+
+ const VkCommandBufferAllocateInfo cmd = {
+ .sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO,
+ .pNext = NULL,
+ .commandPool = demo->cmd_pool,
+ .level = VK_COMMAND_BUFFER_LEVEL_PRIMARY,
+ .commandBufferCount = 1,
+ };
+ err = vkAllocateCommandBuffers(demo->device, &cmd, &demo->draw_cmd);
+ assert(!err);
+
+ demo_prepare_buffers(demo);
+ demo_prepare_depth(demo);
+ demo_prepare_textures(demo);
+ demo_prepare_vertices(demo);
+ demo_prepare_descriptor_layout(demo);
+ demo_prepare_render_pass(demo);
+ demo_prepare_pipeline(demo);
+
+ demo_prepare_descriptor_pool(demo);
+ demo_prepare_descriptor_set(demo);
+
+ demo_prepare_framebuffers(demo);
+}
+
+static void demo_error_callback(int error, const char* description) {
+ printf("GLFW error: %s\n", description);
+ fflush(stdout);
+}
+
+static void demo_key_callback(GLFWwindow* window, int key, int scancode, int action, int mods) {
+ if (key == GLFW_KEY_ESCAPE && action == GLFW_RELEASE)
+ glfwSetWindowShouldClose(window, GLFW_TRUE);
+}
+
+static void demo_refresh_callback(GLFWwindow* window) {
+ struct demo* demo = glfwGetWindowUserPointer(window);
+ demo_draw(demo);
+}
+
+static void demo_resize_callback(GLFWwindow* window, int width, int height) {
+ struct demo* demo = glfwGetWindowUserPointer(window);
+ demo->width = width;
+ demo->height = height;
+ demo_resize(demo);
+}
+
+static void demo_run(struct demo *demo) {
+ while (!glfwWindowShouldClose(demo->window)) {
+ glfwPollEvents();
+
+ demo_draw(demo);
+
+ if (demo->depthStencil > 0.99f)
+ demo->depthIncrement = -0.001f;
+ if (demo->depthStencil < 0.8f)
+ demo->depthIncrement = 0.001f;
+
+ demo->depthStencil += demo->depthIncrement;
+
+ // 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);
+ }
+}
+
+static void demo_create_window(struct demo *demo) {
+ glfwWindowHint(GLFW_CLIENT_API, GLFW_NO_API);
+
+ demo->window = glfwCreateWindow(demo->width,
+ demo->height,
+ APP_LONG_NAME,
+ NULL,
+ NULL);
+ if (!demo->window) {
+ // It didn't work, so try to give a useful error:
+ printf("Cannot create a window in which to draw!\n");
+ fflush(stdout);
+ exit(1);
+ }
+
+ glfwSetWindowUserPointer(demo->window, demo);
+ glfwSetWindowRefreshCallback(demo->window, demo_refresh_callback);
+ glfwSetFramebufferSizeCallback(demo->window, demo_resize_callback);
+ glfwSetKeyCallback(demo->window, demo_key_callback);
+}
+
+/*
+ * 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, const char **check_names,
+ uint32_t layer_count,
+ VkLayerProperties *layers) {
+ uint32_t i, j;
+ for (i = 0; i < check_count; i++) {
+ VkBool32 found = 0;
+ for (j = 0; j < layer_count; j++) {
+ if (!strcmp(check_names[i], layers[j].layerName)) {
+ found = 1;
+ break;
+ }
+ }
+ if (!found) {
+ fprintf(stderr, "Cannot find layer: %s\n", check_names[i]);
+ return 0;
+ }
+ }
+ return 1;
+}
+
+static void demo_init_vk(struct demo *demo) {
+ VkResult err;
+ VkBool32 portability_enumeration = VK_FALSE;
+ uint32_t i = 0;
+ uint32_t required_extension_count = 0;
+ uint32_t instance_extension_count = 0;
+ uint32_t instance_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_alt1[] = {
+ "VK_LAYER_LUNARG_standard_validation"
+ };
+
+ 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;
+ if (demo->validate) {
+
+ err = vkEnumerateInstanceLayerProperties(&instance_layer_count, NULL);
+ assert(!err);
+
+ instance_validation_layers = (const char**) 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_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 =
+ (const char**) 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);
+ }
+
+ 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 */
+ required_extensions = glfwGetRequiredInstanceExtensions(&required_extension_count);
+ if (!required_extensions) {
+ ERR_EXIT("glfwGetRequiredInstanceExtensions failed to find the "
+ "platform surface extensions.\n\nDo you have a compatible "
+ "Vulkan installable client driver (ICD) installed?\nPlease "
+ "look at the Getting Started guide for additional "
+ "information.\n",
+ "vkCreateInstance Failure");
+ }
+
+ for (i = 0; i < required_extension_count; i++) {
+ demo->extension_names[demo->enabled_extension_count++] = required_extensions[i];
+ assert(demo->enabled_extension_count < 64);
+ }
+
+ err = vkEnumerateInstanceExtensionProperties(
+ NULL, &instance_extension_count, NULL);
+ assert(!err);
+
+ if (instance_extension_count > 0) {
+ VkExtensionProperties *instance_extensions =
+ malloc(sizeof(VkExtensionProperties) * instance_extension_count);
+ err = vkEnumerateInstanceExtensionProperties(
+ NULL, &instance_extension_count, instance_extensions);
+ assert(!err);
+ for (i = 0; i < instance_extension_count; i++) {
+ if (!strcmp(VK_EXT_DEBUG_REPORT_EXTENSION_NAME,
+ instance_extensions[i].extensionName)) {
+ if (demo->validate) {
+ demo->extension_names[demo->enabled_extension_count++] =
+ VK_EXT_DEBUG_REPORT_EXTENSION_NAME;
+ }
+ }
+ assert(demo->enabled_extension_count < 64);
+ if (!strcmp(VK_KHR_PORTABILITY_ENUMERATION_EXTENSION_NAME,
+ instance_extensions[i].extensionName)) {
+ demo->extension_names[demo->enabled_extension_count++] =
+ VK_KHR_PORTABILITY_ENUMERATION_EXTENSION_NAME;
+ portability_enumeration = VK_TRUE;
+ }
+ assert(demo->enabled_extension_count < 64);
+ }
+
+ free(instance_extensions);
+ }
+
+ const VkApplicationInfo app = {
+ .sType = VK_STRUCTURE_TYPE_APPLICATION_INFO,
+ .pNext = NULL,
+ .pApplicationName = APP_SHORT_NAME,
+ .applicationVersion = 0,
+ .pEngineName = APP_SHORT_NAME,
+ .engineVersion = 0,
+ .apiVersion = VK_API_VERSION_1_0,
+ };
+ VkInstanceCreateInfo inst_info = {
+ .sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO,
+ .pNext = NULL,
+ .pApplicationInfo = &app,
+ .enabledLayerCount = demo->enabled_layer_count,
+ .ppEnabledLayerNames = (const char *const *)instance_validation_layers,
+ .enabledExtensionCount = demo->enabled_extension_count,
+ .ppEnabledExtensionNames = (const char *const *)demo->extension_names,
+ };
+
+ if (portability_enumeration)
+ inst_info.flags |= VK_INSTANCE_CREATE_ENUMERATE_PORTABILITY_BIT_KHR;
+
+ uint32_t gpu_count;
+
+ 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 "
+ "additional information.\n",
+ "vkCreateInstance Failure");
+ } else if (err == VK_ERROR_EXTENSION_NOT_PRESENT) {
+ ERR_EXIT("Cannot find a specified extension library"
+ ".\nMake sure your layers path is set appropriately\n",
+ "vkCreateInstance Failure");
+ } else if (err) {
+ ERR_EXIT("vkCreateInstance failed.\n\nDo you have a compatible Vulkan "
+ "installable client driver (ICD) installed?\nPlease look at "
+ "the Getting Started guide for additional information.\n",
+ "vkCreateInstance Failure");
+ }
+
+ gladLoadVulkanUserPtr(NULL, (GLADuserptrloadfunc) glfwGetInstanceProcAddress, demo->inst);
+
+ /* Make initial call to query gpu_count, then second call for gpu info*/
+ err = vkEnumeratePhysicalDevices(demo->inst, &gpu_count, NULL);
+ assert(!err && gpu_count > 0);
+
+ if (gpu_count > 0) {
+ VkPhysicalDevice *physical_devices =
+ malloc(sizeof(VkPhysicalDevice) * gpu_count);
+ err = vkEnumeratePhysicalDevices(demo->inst, &gpu_count,
+ physical_devices);
+ assert(!err);
+ /* For tri demo we just grab the first physical device */
+ demo->gpu = physical_devices[0];
+ free(physical_devices);
+ } else {
+ ERR_EXIT("vkEnumeratePhysicalDevices reported zero accessible devices."
+ "\n\nDo you have a compatible Vulkan installable client"
+ " driver (ICD) installed?\nPlease look at the Getting Started"
+ " guide for additional information.\n",
+ "vkEnumeratePhysicalDevices Failure");
+ }
+
+ gladLoadVulkanUserPtr(demo->gpu, (GLADuserptrloadfunc) glfwGetInstanceProcAddress, demo->inst);
+
+ /* Look for device extensions */
+ uint32_t device_extension_count = 0;
+ VkBool32 swapchainExtFound = 0;
+ demo->enabled_extension_count = 0;
+
+ err = vkEnumerateDeviceExtensionProperties(demo->gpu, NULL,
+ &device_extension_count, NULL);
+ assert(!err);
+
+ if (device_extension_count > 0) {
+ VkExtensionProperties *device_extensions =
+ malloc(sizeof(VkExtensionProperties) * device_extension_count);
+ err = vkEnumerateDeviceExtensionProperties(
+ demo->gpu, NULL, &device_extension_count, device_extensions);
+ assert(!err);
+
+ for (i = 0; i < device_extension_count; i++) {
+ if (!strcmp(VK_KHR_SWAPCHAIN_EXTENSION_NAME,
+ device_extensions[i].extensionName)) {
+ swapchainExtFound = 1;
+ demo->extension_names[demo->enabled_extension_count++] =
+ VK_KHR_SWAPCHAIN_EXTENSION_NAME;
+ }
+ assert(demo->enabled_extension_count < 64);
+ }
+
+ free(device_extensions);
+ }
+
+ if (!swapchainExtFound) {
+ ERR_EXIT("vkEnumerateDeviceExtensionProperties failed to find "
+ "the " VK_KHR_SWAPCHAIN_EXTENSION_NAME
+ " extension.\n\nDo you have a compatible "
+ "Vulkan installable client driver (ICD) installed?\nPlease "
+ "look at the Getting Started guide for additional "
+ "information.\n",
+ "vkCreateInstance Failure");
+ }
+
+ if (demo->validate) {
+ 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 = demo->use_break ? BreakCallback : dbgFunc;
+ dbgCreateInfo.pUserData = demo;
+ dbgCreateInfo.pNext = NULL;
+ err = vkCreateDebugReportCallbackEXT(demo->inst, &dbgCreateInfo, NULL,
+ &demo->msg_callback);
+ switch (err) {
+ case VK_SUCCESS:
+ break;
+ case VK_ERROR_OUT_OF_HOST_MEMORY:
+ ERR_EXIT("CreateDebugReportCallback: out of host memory\n",
+ "CreateDebugReportCallback Failure");
+ break;
+ default:
+ ERR_EXIT("CreateDebugReportCallback: unknown failure\n",
+ "CreateDebugReportCallback Failure");
+ break;
+ }
+ }
+
+ vkGetPhysicalDeviceProperties(demo->gpu, &demo->gpu_props);
+
+ // Query with NULL data to get count
+ vkGetPhysicalDeviceQueueFamilyProperties(demo->gpu, &demo->queue_count,
+ NULL);
+
+ demo->queue_props = (VkQueueFamilyProperties *)malloc(
+ demo->queue_count * sizeof(VkQueueFamilyProperties));
+ vkGetPhysicalDeviceQueueFamilyProperties(demo->gpu, &demo->queue_count,
+ 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
+}
+
+static void demo_init_device(struct demo *demo) {
+ VkResult U_ASSERT_ONLY err;
+
+ float queue_priorities[1] = {0.0};
+ const VkDeviceQueueCreateInfo queue = {
+ .sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO,
+ .pNext = NULL,
+ .queueFamilyIndex = demo->graphics_queue_node_index,
+ .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 = 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);
+ assert(!err);
+}
+
+static void demo_init_vk_swapchain(struct demo *demo) {
+ VkResult U_ASSERT_ONLY err;
+ uint32_t i;
+
+ // Create a WSI surface for the window:
+ glfwCreateWindowSurface(demo->inst, demo->window, NULL, &demo->surface);
+
+ // Iterate over each queue to learn whether it supports presenting:
+ VkBool32 *supportsPresent =
+ (VkBool32 *)malloc(demo->queue_count * sizeof(VkBool32));
+ for (i = 0; i < demo->queue_count; i++) {
+ vkGetPhysicalDeviceSurfaceSupportKHR(demo->gpu, i, demo->surface,
+ &supportsPresent[i]);
+ }
+
+ // Search for a graphics and a present queue in the array of queue
+ // families, try to find one that supports both
+ uint32_t graphicsQueueNodeIndex = UINT32_MAX;
+ uint32_t presentQueueNodeIndex = UINT32_MAX;
+ for (i = 0; i < demo->queue_count; i++) {
+ if ((demo->queue_props[i].queueFlags & VK_QUEUE_GRAPHICS_BIT) != 0) {
+ if (graphicsQueueNodeIndex == UINT32_MAX) {
+ graphicsQueueNodeIndex = i;
+ }
+
+ if (supportsPresent[i] == VK_TRUE) {
+ graphicsQueueNodeIndex = i;
+ presentQueueNodeIndex = i;
+ break;
+ }
+ }
+ }
+ if (presentQueueNodeIndex == UINT32_MAX) {
+ // If didn't find a queue that supports both graphics and present, then
+ // find a separate present queue.
+ for (i = 0; i < demo->queue_count; ++i) {
+ if (supportsPresent[i] == VK_TRUE) {
+ presentQueueNodeIndex = i;
+ break;
+ }
+ }
+ }
+ free(supportsPresent);
+
+ // Generate error if could not find both a graphics and a present queue
+ if (graphicsQueueNodeIndex == UINT32_MAX ||
+ presentQueueNodeIndex == UINT32_MAX) {
+ ERR_EXIT("Could not find a graphics and a present queue\n",
+ "Swapchain Initialization Failure");
+ }
+
+ // TODO: Add support for separate queues, including presentation,
+ // synchronization, and appropriate tracking for QueueSubmit.
+ // NOTE: While it is possible for an application to use a separate graphics
+ // and a present queues, this demo program assumes it is only using
+ // one:
+ if (graphicsQueueNodeIndex != presentQueueNodeIndex) {
+ ERR_EXIT("Could not find a common graphics and a present queue\n",
+ "Swapchain Initialization Failure");
+ }
+
+ demo->graphics_queue_node_index = graphicsQueueNodeIndex;
+
+ demo_init_device(demo);
+
+ vkGetDeviceQueue(demo->device, demo->graphics_queue_node_index, 0,
+ &demo->queue);
+
+ // Get the list of VkFormat's that are supported:
+ uint32_t formatCount;
+ err = vkGetPhysicalDeviceSurfaceFormatsKHR(demo->gpu, demo->surface,
+ &formatCount, NULL);
+ assert(!err);
+ VkSurfaceFormatKHR *surfFormats =
+ (VkSurfaceFormatKHR *)malloc(formatCount * sizeof(VkSurfaceFormatKHR));
+ err = vkGetPhysicalDeviceSurfaceFormatsKHR(demo->gpu, demo->surface,
+ &formatCount, surfFormats);
+ assert(!err);
+ // If the format list includes just one entry of VK_FORMAT_UNDEFINED,
+ // the surface has no preferred format. Otherwise, at least one
+ // supported format will be returned.
+ if (formatCount == 1 && surfFormats[0].format == VK_FORMAT_UNDEFINED) {
+ demo->format = VK_FORMAT_B8G8R8A8_UNORM;
+ } else {
+ assert(formatCount >= 1);
+ demo->format = surfFormats[0].format;
+ }
+ demo->color_space = surfFormats[0].colorSpace;
+
+ demo->curFrame = 0;
+
+ // Get Memory information and properties
+ vkGetPhysicalDeviceMemoryProperties(demo->gpu, &demo->memory_properties);
+}
+
+static void demo_init_connection(struct demo *demo) {
+ glfwSetErrorCallback(demo_error_callback);
+
+ if (!glfwInit()) {
+ printf("Cannot initialize GLFW.\nExiting ...\n");
+ fflush(stdout);
+ exit(1);
+ }
+
+ if (!glfwVulkanSupported()) {
+ printf("GLFW failed to find the Vulkan loader.\nExiting ...\n");
+ fflush(stdout);
+ exit(1);
+ }
+
+ gladLoadVulkanUserPtr(NULL, (GLADuserptrloadfunc) glfwGetInstanceProcAddress, NULL);
+}
+
+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 = 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 <framecount>]\n",
+ APP_SHORT_NAME);
+ fflush(stderr);
+ exit(1);
+ }
+
+ demo_init_connection(demo);
+ demo_init_vk(demo);
+
+ demo->width = 300;
+ demo->height = 300;
+ demo->depthStencil = 1.0;
+ demo->depthIncrement = -0.01f;
+}
+
+static void demo_cleanup(struct demo *demo) {
+ uint32_t i;
+
+ for (i = 0; i < demo->swapchainImageCount; i++) {
+ vkDestroyFramebuffer(demo->device, demo->framebuffers[i], NULL);
+ }
+ free(demo->framebuffers);
+ vkDestroyDescriptorPool(demo->device, demo->desc_pool, NULL);
+
+ if (demo->setup_cmd) {
+ vkFreeCommandBuffers(demo->device, demo->cmd_pool, 1, &demo->setup_cmd);
+ }
+ vkFreeCommandBuffers(demo->device, demo->cmd_pool, 1, &demo->draw_cmd);
+ vkDestroyCommandPool(demo->device, demo->cmd_pool, NULL);
+
+ vkDestroyPipeline(demo->device, demo->pipeline, NULL);
+ vkDestroyRenderPass(demo->device, demo->render_pass, NULL);
+ vkDestroyPipelineLayout(demo->device, demo->pipeline_layout, NULL);
+ vkDestroyDescriptorSetLayout(demo->device, demo->desc_layout, NULL);
+
+ vkDestroyBuffer(demo->device, demo->vertices.buf, NULL);
+ vkFreeMemory(demo->device, demo->vertices.mem, NULL);
+
+ for (i = 0; i < DEMO_TEXTURE_COUNT; i++) {
+ vkDestroyImageView(demo->device, demo->textures[i].view, NULL);
+ vkDestroyImage(demo->device, demo->textures[i].image, NULL);
+ vkFreeMemory(demo->device, demo->textures[i].mem, NULL);
+ vkDestroySampler(demo->device, demo->textures[i].sampler, NULL);
+ }
+
+ for (i = 0; i < demo->swapchainImageCount; i++) {
+ vkDestroyImageView(demo->device, demo->buffers[i].view, NULL);
+ }
+
+ vkDestroyImageView(demo->device, demo->depth.view, NULL);
+ vkDestroyImage(demo->device, demo->depth.image, NULL);
+ vkFreeMemory(demo->device, demo->depth.mem, NULL);
+
+ vkDestroySwapchainKHR(demo->device, demo->swapchain, NULL);
+ free(demo->buffers);
+
+ vkDestroyDevice(demo->device, NULL);
+ if (demo->validate) {
+ vkDestroyDebugReportCallbackEXT(demo->inst, demo->msg_callback, NULL);
+ }
+ vkDestroySurfaceKHR(demo->inst, demo->surface, NULL);
+ vkDestroyInstance(demo->inst, NULL);
+
+ free(demo->queue_props);
+
+ glfwDestroyWindow(demo->window);
+ glfwTerminate();
+}
+
+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);
+ }
+ free(demo->framebuffers);
+ vkDestroyDescriptorPool(demo->device, demo->desc_pool, NULL);
+
+ if (demo->setup_cmd) {
+ vkFreeCommandBuffers(demo->device, demo->cmd_pool, 1, &demo->setup_cmd);
+ demo->setup_cmd = VK_NULL_HANDLE;
+ }
+ vkFreeCommandBuffers(demo->device, demo->cmd_pool, 1, &demo->draw_cmd);
+ vkDestroyCommandPool(demo->device, demo->cmd_pool, NULL);
+
+ vkDestroyPipeline(demo->device, demo->pipeline, NULL);
+ vkDestroyRenderPass(demo->device, demo->render_pass, NULL);
+ vkDestroyPipelineLayout(demo->device, demo->pipeline_layout, NULL);
+ vkDestroyDescriptorSetLayout(demo->device, demo->desc_layout, NULL);
+
+ vkDestroyBuffer(demo->device, demo->vertices.buf, NULL);
+ vkFreeMemory(demo->device, demo->vertices.mem, NULL);
+
+ for (i = 0; i < DEMO_TEXTURE_COUNT; i++) {
+ vkDestroyImageView(demo->device, demo->textures[i].view, NULL);
+ vkDestroyImage(demo->device, demo->textures[i].image, NULL);
+ vkFreeMemory(demo->device, demo->textures[i].mem, NULL);
+ vkDestroySampler(demo->device, demo->textures[i].sampler, NULL);
+ }
+
+ for (i = 0; i < demo->swapchainImageCount; i++) {
+ vkDestroyImageView(demo->device, demo->buffers[i].view, NULL);
+ }
+
+ vkDestroyImageView(demo->device, demo->depth.view, NULL);
+ vkDestroyImage(demo->device, demo->depth.image, NULL);
+ vkFreeMemory(demo->device, demo->depth.mem, NULL);
+
+ free(demo->buffers);
+
+ // Second, re-perform the demo_prepare() function, which will re-create the
+ // swapchain:
+ demo_prepare(demo);
+}
+
+int main(const int argc, const char *argv[]) {
+ struct demo demo;
+
+ demo_init(&demo, argc, argv);
+ demo_create_window(&demo);
+ demo_init_vk_swapchain(&demo);
+
+ demo_prepare(&demo);
+ demo_run(&demo);
+
+ demo_cleanup(&demo);
+
+ return validation_error;
+}
+
diff --git a/libs/glfw-3.3.8/tests/windows.c b/libs/glfw-3.3.8/tests/windows.c
new file mode 100644
index 0000000..6669856
--- /dev/null
+++ b/libs/glfw-3.3.8/tests/windows.c
@@ -0,0 +1,174 @@
+//========================================================================
+// Simple multi-window test
+// Copyright (c) Camilla Löwy <elmindreda@glfw.org>
+//
+// 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.
+//
+//========================================================================
+//
+// This test creates four windows and clears each in a different color
+//
+//========================================================================
+
+#include <glad/gl.h>
+#define GLFW_INCLUDE_NONE
+#include <GLFW/glfw3.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+
+#include "getopt.h"
+
+static const char* titles[] =
+{
+ "Red",
+ "Green",
+ "Blue",
+ "Yellow"
+};
+
+static const struct
+{
+ float r, g, b;
+} colors[] =
+{
+ { 0.95f, 0.32f, 0.11f },
+ { 0.50f, 0.80f, 0.16f },
+ { 0.f, 0.68f, 0.94f },
+ { 0.98f, 0.74f, 0.04f }
+};
+
+static void usage(void)
+{
+ printf("Usage: windows [-h] [-b] [-f] \n");
+ printf("Options:\n");
+ printf(" -b create decorated windows\n");
+ printf(" -f set focus on show off for all but first window\n");
+ printf(" -h show this help\n");
+}
+
+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 (action != GLFW_PRESS)
+ return;
+
+ switch (key)
+ {
+ case GLFW_KEY_SPACE:
+ {
+ int xpos, ypos;
+ glfwGetWindowPos(window, &xpos, &ypos);
+ glfwSetWindowPos(window, xpos, ypos);
+ break;
+ }
+
+ case GLFW_KEY_ESCAPE:
+ glfwSetWindowShouldClose(window, GLFW_TRUE);
+ break;
+ }
+}
+
+int main(int argc, char** argv)
+{
+ int i, ch;
+ int decorated = GLFW_FALSE;
+ int focusOnShow = GLFW_TRUE;
+ int running = GLFW_TRUE;
+ GLFWwindow* windows[4];
+
+ while ((ch = getopt(argc, argv, "bfh")) != -1)
+ {
+ switch (ch)
+ {
+ case 'b':
+ decorated = GLFW_TRUE;
+ break;
+ case 'f':
+ focusOnShow = GLFW_FALSE;
+ break;
+ case 'h':
+ usage();
+ exit(EXIT_SUCCESS);
+ default:
+ usage();
+ exit(EXIT_FAILURE);
+ }
+ }
+
+ glfwSetErrorCallback(error_callback);
+
+ if (!glfwInit())
+ exit(EXIT_FAILURE);
+
+ glfwWindowHint(GLFW_DECORATED, decorated);
+ glfwWindowHint(GLFW_VISIBLE, GLFW_FALSE);
+
+ for (i = 0; i < 4; i++)
+ {
+ int left, top, right, bottom;
+ if (i)
+ glfwWindowHint(GLFW_FOCUS_ON_SHOW, focusOnShow);
+
+ windows[i] = glfwCreateWindow(200, 200, titles[i], NULL, NULL);
+ if (!windows[i])
+ {
+ glfwTerminate();
+ exit(EXIT_FAILURE);
+ }
+
+ glfwSetKeyCallback(windows[i], key_callback);
+
+ glfwMakeContextCurrent(windows[i]);
+ gladLoadGL(glfwGetProcAddress);
+ glClearColor(colors[i].r, colors[i].g, colors[i].b, 1.f);
+
+ glfwGetWindowFrameSize(windows[i], &left, &top, &right, &bottom);
+ glfwSetWindowPos(windows[i],
+ 100 + (i & 1) * (200 + left + right),
+ 100 + (i >> 1) * (200 + top + bottom));
+ }
+
+ for (i = 0; i < 4; i++)
+ glfwShowWindow(windows[i]);
+
+ while (running)
+ {
+ for (i = 0; i < 4; i++)
+ {
+ glfwMakeContextCurrent(windows[i]);
+ glClear(GL_COLOR_BUFFER_BIT);
+ glfwSwapBuffers(windows[i]);
+
+ if (glfwWindowShouldClose(windows[i]))
+ running = GLFW_FALSE;
+ }
+
+ glfwWaitEvents();
+ }
+
+ glfwTerminate();
+ exit(EXIT_SUCCESS);
+}
+