diff options
Diffstat (limited to 'libs/glfw-3.3.8/tests')
-rw-r--r-- | libs/glfw-3.3.8/tests/CMakeLists.txt | 99 | ||||
-rw-r--r-- | libs/glfw-3.3.8/tests/clipboard.c | 145 | ||||
-rw-r--r-- | libs/glfw-3.3.8/tests/cursor.c | 493 | ||||
-rw-r--r-- | libs/glfw-3.3.8/tests/empty.c | 132 | ||||
-rw-r--r-- | libs/glfw-3.3.8/tests/events.c | 650 | ||||
-rw-r--r-- | libs/glfw-3.3.8/tests/gamma.c | 185 | ||||
-rw-r--r-- | libs/glfw-3.3.8/tests/glfwinfo.c | 943 | ||||
-rw-r--r-- | libs/glfw-3.3.8/tests/icon.c | 149 | ||||
-rw-r--r-- | libs/glfw-3.3.8/tests/iconify.c | 297 | ||||
-rw-r--r-- | libs/glfw-3.3.8/tests/inputlag.c | 308 | ||||
-rw-r--r-- | libs/glfw-3.3.8/tests/joysticks.c | 344 | ||||
-rw-r--r-- | libs/glfw-3.3.8/tests/monitors.c | 260 | ||||
-rw-r--r-- | libs/glfw-3.3.8/tests/msaa.c | 220 | ||||
-rw-r--r-- | libs/glfw-3.3.8/tests/opacity.c | 108 | ||||
-rw-r--r-- | libs/glfw-3.3.8/tests/reopen.c | 240 | ||||
-rw-r--r-- | libs/glfw-3.3.8/tests/tearing.c | 250 | ||||
-rw-r--r-- | libs/glfw-3.3.8/tests/threads.c | 152 | ||||
-rw-r--r-- | libs/glfw-3.3.8/tests/timeout.c | 98 | ||||
-rw-r--r-- | libs/glfw-3.3.8/tests/title.c | 72 | ||||
-rw-r--r-- | libs/glfw-3.3.8/tests/triangle-vulkan.c | 2139 | ||||
-rw-r--r-- | libs/glfw-3.3.8/tests/windows.c | 174 |
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, ¤t.redBits); + glGetIntegerv(GL_GREEN_BITS, ¤t.greenBits); + glGetIntegerv(GL_BLUE_BITS, ¤t.blueBits); + + glfwGetWindowSize(window, ¤t.width, ¤t.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, ©_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); +} + |