inputlag.c 9.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310
  1. //========================================================================
  2. // Input lag test
  3. // Copyright (c) Camilla Löwy <[email protected]>
  4. //
  5. // This software is provided 'as-is', without any express or implied
  6. // warranty. In no event will the authors be held liable for any damages
  7. // arising from the use of this software.
  8. //
  9. // Permission is granted to anyone to use this software for any purpose,
  10. // including commercial applications, and to alter it and redistribute it
  11. // freely, subject to the following restrictions:
  12. //
  13. // 1. The origin of this software must not be misrepresented; you must not
  14. // claim that you wrote the original software. If you use this software
  15. // in a product, an acknowledgment in the product documentation would
  16. // be appreciated but is not required.
  17. //
  18. // 2. Altered source versions must be plainly marked as such, and must not
  19. // be misrepresented as being the original software.
  20. //
  21. // 3. This notice may not be removed or altered from any source
  22. // distribution.
  23. //
  24. //========================================================================
  25. //
  26. // This test renders a marker at the cursor position reported by GLFW to
  27. // check how much it lags behind the hardware mouse cursor
  28. //
  29. //========================================================================
  30. #define GLAD_GL_IMPLEMENTATION
  31. #include <glad/gl.h>
  32. #define GLFW_INCLUDE_NONE
  33. #include <GLFW/glfw3.h>
  34. #define NK_IMPLEMENTATION
  35. #define NK_INCLUDE_FIXED_TYPES
  36. #define NK_INCLUDE_FONT_BAKING
  37. #define NK_INCLUDE_DEFAULT_FONT
  38. #define NK_INCLUDE_DEFAULT_ALLOCATOR
  39. #define NK_INCLUDE_VERTEX_BUFFER_OUTPUT
  40. #define NK_INCLUDE_STANDARD_VARARGS
  41. #include <nuklear.h>
  42. #define NK_GLFW_GL2_IMPLEMENTATION
  43. #include <nuklear_glfw_gl2.h>
  44. #include <stdio.h>
  45. #include <stdlib.h>
  46. #include <string.h>
  47. #include "getopt.h"
  48. void usage(void)
  49. {
  50. printf("Usage: inputlag [-h] [-f]\n");
  51. printf("Options:\n");
  52. printf(" -f create full screen window\n");
  53. printf(" -h show this help\n");
  54. }
  55. struct nk_vec2 cursor_new, cursor_pos, cursor_vel;
  56. enum { cursor_sync_query, cursor_input_message } cursor_method = cursor_sync_query;
  57. void sample_input(GLFWwindow* window)
  58. {
  59. float a = .25; // exponential smoothing factor
  60. if (cursor_method == cursor_sync_query) {
  61. double x, y;
  62. glfwGetCursorPos(window, &x, &y);
  63. cursor_new.x = (float) x;
  64. cursor_new.y = (float) y;
  65. }
  66. cursor_vel.x = (cursor_new.x - cursor_pos.x) * a + cursor_vel.x * (1 - a);
  67. cursor_vel.y = (cursor_new.y - cursor_pos.y) * a + cursor_vel.y * (1 - a);
  68. cursor_pos = cursor_new;
  69. }
  70. void cursor_pos_callback(GLFWwindow* window, double xpos, double ypos)
  71. {
  72. cursor_new.x = (float) xpos;
  73. cursor_new.y = (float) ypos;
  74. }
  75. int enable_vsync = nk_true;
  76. void update_vsync()
  77. {
  78. glfwSwapInterval(enable_vsync == nk_true ? 1 : 0);
  79. }
  80. int swap_clear = nk_false;
  81. int swap_finish = nk_true;
  82. int swap_occlusion_query = nk_false;
  83. int swap_read_pixels = nk_false;
  84. GLuint occlusion_query;
  85. void swap_buffers(GLFWwindow* window)
  86. {
  87. glfwSwapBuffers(window);
  88. if (swap_clear)
  89. glClear(GL_COLOR_BUFFER_BIT);
  90. if (swap_finish)
  91. glFinish();
  92. if (swap_occlusion_query) {
  93. GLint occlusion_result;
  94. if (!occlusion_query)
  95. glGenQueries(1, &occlusion_query);
  96. glBeginQuery(GL_SAMPLES_PASSED, occlusion_query);
  97. glBegin(GL_POINTS);
  98. glVertex2f(0, 0);
  99. glEnd();
  100. glEndQuery(GL_SAMPLES_PASSED);
  101. glGetQueryObjectiv(occlusion_query, GL_QUERY_RESULT, &occlusion_result);
  102. }
  103. if (swap_read_pixels) {
  104. unsigned char rgba[4];
  105. glReadPixels(0, 0, 1, 1, GL_RGBA, GL_UNSIGNED_BYTE, rgba);
  106. }
  107. }
  108. void error_callback(int error, const char* description)
  109. {
  110. fprintf(stderr, "Error: %s\n", description);
  111. }
  112. void key_callback(GLFWwindow* window, int key, int scancode, int action, int mods)
  113. {
  114. if (action != GLFW_PRESS)
  115. return;
  116. switch (key)
  117. {
  118. case GLFW_KEY_ESCAPE:
  119. glfwSetWindowShouldClose(window, 1);
  120. break;
  121. }
  122. }
  123. void draw_marker(struct nk_command_buffer* canvas, int lead, struct nk_vec2 pos)
  124. {
  125. 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) };
  126. struct nk_rect rect = { -5 + pos.x, -5 + pos.y, 10, 10 };
  127. nk_fill_circle(canvas, rect, colors[lead]);
  128. }
  129. int main(int argc, char** argv)
  130. {
  131. int ch, width, height;
  132. unsigned long frame_count = 0;
  133. double last_time, current_time;
  134. double frame_rate = 0;
  135. int fullscreen = GLFW_FALSE;
  136. GLFWmonitor* monitor = NULL;
  137. GLFWwindow* window;
  138. struct nk_context* nk;
  139. struct nk_font_atlas* atlas;
  140. int show_forecasts = nk_true;
  141. while ((ch = getopt(argc, argv, "fh")) != -1)
  142. {
  143. switch (ch)
  144. {
  145. case 'h':
  146. usage();
  147. exit(EXIT_SUCCESS);
  148. case 'f':
  149. fullscreen = GLFW_TRUE;
  150. break;
  151. }
  152. }
  153. glfwSetErrorCallback(error_callback);
  154. if (!glfwInit())
  155. exit(EXIT_FAILURE);
  156. if (fullscreen)
  157. {
  158. const GLFWvidmode* mode;
  159. monitor = glfwGetPrimaryMonitor();
  160. mode = glfwGetVideoMode(monitor);
  161. width = mode->width;
  162. height = mode->height;
  163. }
  164. else
  165. {
  166. width = 640;
  167. height = 480;
  168. }
  169. glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 2);
  170. glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 0);
  171. glfwWindowHint(GLFW_SCALE_TO_MONITOR, GLFW_TRUE);
  172. glfwWindowHint(GLFW_WIN32_KEYBOARD_MENU, GLFW_TRUE);
  173. window = glfwCreateWindow(width, height, "Input lag test", monitor, NULL);
  174. if (!window)
  175. {
  176. glfwTerminate();
  177. exit(EXIT_FAILURE);
  178. }
  179. glfwMakeContextCurrent(window);
  180. gladLoadGL(glfwGetProcAddress);
  181. update_vsync();
  182. last_time = glfwGetTime();
  183. nk = nk_glfw3_init(window, NK_GLFW3_INSTALL_CALLBACKS);
  184. nk_glfw3_font_stash_begin(&atlas);
  185. nk_glfw3_font_stash_end();
  186. glfwSetKeyCallback(window, key_callback);
  187. glfwSetCursorPosCallback(window, cursor_pos_callback);
  188. while (!glfwWindowShouldClose(window))
  189. {
  190. int width, height;
  191. struct nk_rect area;
  192. glfwPollEvents();
  193. sample_input(window);
  194. glfwGetWindowSize(window, &width, &height);
  195. area = nk_rect(0.f, 0.f, (float) width, (float) height);
  196. glClear(GL_COLOR_BUFFER_BIT);
  197. nk_glfw3_new_frame();
  198. if (nk_begin(nk, "", area, 0))
  199. {
  200. nk_flags align_left = NK_TEXT_ALIGN_LEFT | NK_TEXT_ALIGN_MIDDLE;
  201. struct nk_command_buffer *canvas = nk_window_get_canvas(nk);
  202. int lead;
  203. for (lead = show_forecasts ? 3 : 0; lead >= 0; lead--)
  204. draw_marker(canvas, lead, nk_vec2(cursor_pos.x + cursor_vel.x * lead,
  205. cursor_pos.y + cursor_vel.y * lead));
  206. // print instructions
  207. nk_layout_row_dynamic(nk, 20, 1);
  208. nk_label(nk, "Move mouse uniformly and check marker under cursor:", align_left);
  209. for (lead = 0; lead <= 3; lead++) {
  210. nk_layout_row_begin(nk, NK_STATIC, 12, 2);
  211. nk_layout_row_push(nk, 25);
  212. draw_marker(canvas, lead, nk_layout_space_to_screen(nk, nk_vec2(20, 5)));
  213. nk_label(nk, "", 0);
  214. nk_layout_row_push(nk, 500);
  215. if (lead == 0)
  216. nk_label(nk, "- current cursor position (no input lag)", align_left);
  217. else
  218. nk_labelf(nk, align_left, "- %d-frame forecast (input lag is %d frame)", lead, lead);
  219. nk_layout_row_end(nk);
  220. }
  221. nk_layout_row_dynamic(nk, 20, 1);
  222. nk_checkbox_label(nk, "Show forecasts", &show_forecasts);
  223. nk_label(nk, "Input method:", align_left);
  224. if (nk_option_label(nk, "glfwGetCursorPos (sync query)", cursor_method == cursor_sync_query))
  225. cursor_method = cursor_sync_query;
  226. if (nk_option_label(nk, "glfwSetCursorPosCallback (latest input message)", cursor_method == cursor_input_message))
  227. cursor_method = cursor_input_message;
  228. nk_label(nk, "", 0); // separator
  229. nk_value_float(nk, "FPS", (float) frame_rate);
  230. if (nk_checkbox_label(nk, "Enable vsync", &enable_vsync))
  231. update_vsync();
  232. nk_label(nk, "", 0); // separator
  233. nk_label(nk, "After swap:", align_left);
  234. nk_checkbox_label(nk, "glClear", &swap_clear);
  235. nk_checkbox_label(nk, "glFinish", &swap_finish);
  236. nk_checkbox_label(nk, "draw with occlusion query", &swap_occlusion_query);
  237. nk_checkbox_label(nk, "glReadPixels", &swap_read_pixels);
  238. }
  239. nk_end(nk);
  240. nk_glfw3_render(NK_ANTI_ALIASING_ON);
  241. swap_buffers(window);
  242. frame_count++;
  243. current_time = glfwGetTime();
  244. if (current_time - last_time > 1.0)
  245. {
  246. frame_rate = frame_count / (current_time - last_time);
  247. frame_count = 0;
  248. last_time = current_time;
  249. }
  250. }
  251. glfwTerminate();
  252. exit(EXIT_SUCCESS);
  253. }