1 //========================================================================
2 // Vsync enabling test
3 // Copyright (c) Camilla Löwy <elmindreda@glfw.org>
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 high contrast, horizontally moving bar, allowing for
27 // visual verification of whether the set swap interval is indeed obeyed
28 //
29 //========================================================================
30
31 #include <glad/gl.h>
32 #define GLFW_INCLUDE_NONE
33 #include <GLFW/glfw3.h>
34
35 #include <stdio.h>
36 #include <stdlib.h>
37 #include <math.h>
38
39 #include "linmath.h"
40
41 static const struct
42 {
43 float x, y;
44 } vertices[4] =
45 {
46 { -0.25f, -1.f },
47 { 0.25f, -1.f },
48 { 0.25f, 1.f },
49 { -0.25f, 1.f }
50 };
51
52 static const char* vertex_shader_text =
53 "#version 110\n"
54 "uniform mat4 MVP;\n"
55 "attribute vec2 vPos;\n"
56 "void main()\n"
57 "{\n"
58 " gl_Position = MVP * vec4(vPos, 0.0, 1.0);\n"
59 "}\n";
60
61 static const char* fragment_shader_text =
62 "#version 110\n"
63 "void main()\n"
64 "{\n"
65 " gl_FragColor = vec4(1.0);\n"
66 "}\n";
67
68 static int swap_tear;
69 static int swap_interval;
70 static double frame_rate;
71
update_window_title(GLFWwindow * window)72 static void update_window_title(GLFWwindow* window)
73 {
74 char title[256];
75
76 snprintf(title, sizeof(title), "Tearing detector (interval %i%s, %0.1f Hz)",
77 swap_interval,
78 (swap_tear && swap_interval < 0) ? " (swap tear)" : "",
79 frame_rate);
80
81 glfwSetWindowTitle(window, title);
82 }
83
set_swap_interval(GLFWwindow * window,int interval)84 static void set_swap_interval(GLFWwindow* window, int interval)
85 {
86 swap_interval = interval;
87 glfwSwapInterval(swap_interval);
88 update_window_title(window);
89 }
90
error_callback(int error,const char * description)91 static void error_callback(int error, const char* description)
92 {
93 fprintf(stderr, "Error: %s\n", description);
94 }
95
key_callback(GLFWwindow * window,int key,int scancode,int action,int mods)96 static void key_callback(GLFWwindow* window, int key, int scancode, int action, int mods)
97 {
98 if (action != GLFW_PRESS)
99 return;
100
101 switch (key)
102 {
103 case GLFW_KEY_UP:
104 {
105 if (swap_interval + 1 > swap_interval)
106 set_swap_interval(window, swap_interval + 1);
107 break;
108 }
109
110 case GLFW_KEY_DOWN:
111 {
112 if (swap_tear)
113 {
114 if (swap_interval - 1 < swap_interval)
115 set_swap_interval(window, swap_interval - 1);
116 }
117 else
118 {
119 if (swap_interval - 1 >= 0)
120 set_swap_interval(window, swap_interval - 1);
121 }
122 break;
123 }
124
125 case GLFW_KEY_ESCAPE:
126 glfwSetWindowShouldClose(window, 1);
127 break;
128
129 case GLFW_KEY_F11:
130 case GLFW_KEY_ENTER:
131 {
132 static int x, y, width, height;
133
134 if (mods != GLFW_MOD_ALT)
135 return;
136
137 if (glfwGetWindowMonitor(window))
138 glfwSetWindowMonitor(window, NULL, x, y, width, height, 0);
139 else
140 {
141 GLFWmonitor* monitor = glfwGetPrimaryMonitor();
142 const GLFWvidmode* mode = glfwGetVideoMode(monitor);
143 glfwGetWindowPos(window, &x, &y);
144 glfwGetWindowSize(window, &width, &height);
145 glfwSetWindowMonitor(window, monitor,
146 0, 0, mode->width, mode->height,
147 mode->refreshRate);
148 }
149
150 break;
151 }
152 }
153 }
154
main(int argc,char ** argv)155 int main(int argc, char** argv)
156 {
157 unsigned long frame_count = 0;
158 double last_time, current_time;
159 GLFWwindow* window;
160 GLuint vertex_buffer, vertex_shader, fragment_shader, program;
161 GLint mvp_location, vpos_location;
162
163 glfwSetErrorCallback(error_callback);
164
165 if (!glfwInit())
166 exit(EXIT_FAILURE);
167
168 glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 2);
169 glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 0);
170
171 window = glfwCreateWindow(640, 480, "Tearing detector", NULL, NULL);
172 if (!window)
173 {
174 glfwTerminate();
175 exit(EXIT_FAILURE);
176 }
177
178 glfwMakeContextCurrent(window);
179 gladLoadGL(glfwGetProcAddress);
180 set_swap_interval(window, 0);
181
182 last_time = glfwGetTime();
183 frame_rate = 0.0;
184 swap_tear = (glfwExtensionSupported("WGL_EXT_swap_control_tear") ||
185 glfwExtensionSupported("GLX_EXT_swap_control_tear"));
186
187 glfwSetKeyCallback(window, key_callback);
188
189 glGenBuffers(1, &vertex_buffer);
190 glBindBuffer(GL_ARRAY_BUFFER, vertex_buffer);
191 glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);
192
193 vertex_shader = glCreateShader(GL_VERTEX_SHADER);
194 glShaderSource(vertex_shader, 1, &vertex_shader_text, NULL);
195 glCompileShader(vertex_shader);
196
197 fragment_shader = glCreateShader(GL_FRAGMENT_SHADER);
198 glShaderSource(fragment_shader, 1, &fragment_shader_text, NULL);
199 glCompileShader(fragment_shader);
200
201 program = glCreateProgram();
202 glAttachShader(program, vertex_shader);
203 glAttachShader(program, fragment_shader);
204 glLinkProgram(program);
205
206 mvp_location = glGetUniformLocation(program, "MVP");
207 vpos_location = glGetAttribLocation(program, "vPos");
208
209 glEnableVertexAttribArray(vpos_location);
210 glVertexAttribPointer(vpos_location, 2, GL_FLOAT, GL_FALSE,
211 sizeof(vertices[0]), (void*) 0);
212
213 while (!glfwWindowShouldClose(window))
214 {
215 int width, height;
216 mat4x4 m, p, mvp;
217 float position = cosf((float) glfwGetTime() * 4.f) * 0.75f;
218
219 glfwGetFramebufferSize(window, &width, &height);
220
221 glViewport(0, 0, width, height);
222 glClear(GL_COLOR_BUFFER_BIT);
223
224 mat4x4_ortho(p, -1.f, 1.f, -1.f, 1.f, 0.f, 1.f);
225 mat4x4_translate(m, position, 0.f, 0.f);
226 mat4x4_mul(mvp, p, m);
227
228 glUseProgram(program);
229 glUniformMatrix4fv(mvp_location, 1, GL_FALSE, (const GLfloat*) mvp);
230 glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
231
232 glfwSwapBuffers(window);
233 glfwPollEvents();
234
235 frame_count++;
236
237 current_time = glfwGetTime();
238 if (current_time - last_time > 1.0)
239 {
240 frame_rate = frame_count / (current_time - last_time);
241 frame_count = 0;
242 last_time = current_time;
243 update_window_title(window);
244 }
245 }
246
247 glfwTerminate();
248 exit(EXIT_SUCCESS);
249 }
250
251