glfw/tests/inputlag.c
Camilla Löwy 089ea9af22 Add GLFW_SCALE_TO_MONITOR
This adds the GLFW_SCALE_TO_MONITOR window hint for automatically
resizing the content area of a window to the requested size times the
monitor content scale each time it is placed on a new monitor.  This
only applies to windowed mode windows and includes the initial placement
at window creation.

This hint only has an effect on platforms where screen coordinates and
pixels always map 1:1 such as Windows and X11.  Platforms like macOS
instead change the resolution of the framebuffer independently of the
window size.

Related to #676.
Related to #1115.
2018-09-03 23:12:39 +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/glad.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);
gladLoadGLLoader((GLADloadproc) 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);
}