glfw/tests/inputlag.c
Camilla Löwy d7e30b1c74 Replace glad and the Vulkan SDK with glad2
This removes all dependencies from the GLFW test programs on the Vulkan
SDK.

It also removes support for linking the GLFW shared library (dynamic
library, DLL) against the Vulkan loader static library.
2019-04-15 02:45:48 +02:00

308 lines
8.9 KiB
C

//========================================================================
// 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>
#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);
}