1 /**
2 * Test compilation, link, draw time for very large shaders.
3 * Command line arguments:
4 * -v verbose output (print shader code)
5 * -c N generate shaders of complexity N
6 * -n N generate and draw with N shader programs
7 *
8 * Brian Paul
9 * 3 Dec 2015
10 */
11
12
13 #include <assert.h>
14 #include <stdio.h>
15 #include <stdbool.h>
16 #include <stdlib.h>
17 #include <string.h>
18 #include <math.h>
19 #include <glad/glad.h>
20 #include "glut_wrap.h"
21 #include "shaderutil.h"
22
23 #if defined(_MSC_VER)
24 #define snprintf _snprintf
25 #endif
26
27
28 #define MAX_PROGRAMS 10000
29
30 static int Win;
31 static int WinWidth = 400, WinHeight = 400;
32 static int verbose = 0;
33 static int num_shaders = 1;
34 static int complexity = 5;
35 static int programs[MAX_PROGRAMS];
36
37 static const float u1_val[4] = {.1, .2, .3, .4};
38 static const float u2_val[4] = {.6, .7, .8, .9};
39
40 static const char *VS_code =
41 "void main()\n"
42 "{\n"
43 " gl_Position = ftransform();\n"
44 "}\n";
45
46 static const float coords[3][4] = {
47 {0, 0.1, 0, 1},
48 {0, 0, 0, 1},
49 {0, -0.1, 0, 1}
50 };
51
52 #define NUM_POINTS (sizeof(coords) / sizeof(coords[0]))
53
54
55
56 struct dynamic_string {
57 char *buffer;
58 unsigned len;
59 unsigned buffer_size;
60 };
61
62
63 static void
append_string(struct dynamic_string * ds,const char * s)64 append_string(struct dynamic_string *ds, const char *s)
65 {
66 int l = strlen(s);
67 if (ds->len + l >= ds->buffer_size) {
68 /* grow buffer */
69 int newsize = ds->buffer_size + l + 4096;
70 char *newbuf = malloc(newsize);
71 assert(newbuf);
72 if (ds->buffer)
73 strcpy(newbuf, ds->buffer);
74 free(ds->buffer);
75 ds->buffer = newbuf;
76 ds->buffer_size = newsize;
77 }
78 strcpy(ds->buffer + ds->len, s);
79 ds->len += l;
80 assert(strlen(ds->buffer) == ds->len);
81 }
82
83
84 /**
85 * Index is a term put into the shader code to make each shader a little
86 * different.
87 */
88 static char *
gen_large_shader(int num_functions,int index)89 gen_large_shader(int num_functions, int index)
90 {
91 int i;
92 struct dynamic_string ds = {0};
93 char s[100];
94
95 append_string(&ds, "#version 120\n");
96 append_string(&ds, "\nuniform vec4 u1, u2;\n\n");
97
98 for (i = 0; i < num_functions; i++) {
99 snprintf(s, sizeof(s), "vec4 func%d(vec4 a, float b)\n", i);
100 append_string(&ds, s);
101 append_string(&ds, "{\n");
102 if (i == 0) {
103 append_string(&ds, " return a * b;\n");
104 }
105 else {
106 snprintf(s, sizeof(s),
107 " vec4 s = a * func%d(a, float(%d)) + vec4(b);\n",
108 i-1, index);
109 append_string(&ds, s);
110
111 snprintf(s, sizeof(s),
112 " vec4 t = a / func%d(a, 3.0) - vec4(b);\n", i-1);
113 append_string(&ds, s);
114
115 if (i & 1) {
116 append_string(&ds, " vec4 u = max(s, t);\n");
117 }
118 else {
119 /* use a conditional */
120 append_string(&ds, " vec4 u = min(s, t);\n");
121 append_string(&ds, " if (s.x > t.x) {\n");
122 snprintf(s, sizeof(s), " u = vec4(%d);\n", i);
123 append_string(&ds, s);
124 append_string(&ds, " }\n");
125 }
126
127 append_string(&ds, " return u;\n");
128 }
129
130 append_string(&ds, "}\n\n");
131 }
132
133 append_string(&ds, "void main()\n");
134 append_string(&ds, "{\n");
135 snprintf(s, sizeof(s), " gl_FragColor = func%d(u1, u2.x);\n", i-1);
136 append_string(&ds, s);
137 append_string(&ds, "}\n");
138
139 return ds.buffer;
140 }
141
142
143 static void
Draw(void)144 Draw(void)
145 {
146 int t0, t1;
147 int i;
148 int fixed_func_time = 0, glsl_time_1 = 0, glsl_time_2 = 0;
149
150 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
151
152 for (i = 0; i < num_shaders; i++) {
153 UseProgram(0);
154 t0 = glutGet(GLUT_ELAPSED_TIME);
155 glDrawArrays(GL_POINTS, 0, NUM_POINTS);
156 glFinish();
157 t1 = glutGet(GLUT_ELAPSED_TIME);
158 fixed_func_time += t1 - t0;
159
160 UseProgram(programs[i]);
161 t0 = glutGet(GLUT_ELAPSED_TIME);
162 glDrawArrays(GL_POINTS, 0, NUM_POINTS);
163 glFinish();
164 t1 = glutGet(GLUT_ELAPSED_TIME);
165 glsl_time_1 += t1 - t0;
166
167 t0 = glutGet(GLUT_ELAPSED_TIME);
168 glDrawArrays(GL_POINTS, 0, NUM_POINTS);
169 glFinish();
170 t1 = glutGet(GLUT_ELAPSED_TIME);
171 glsl_time_2 += t1 - t0;
172 }
173
174 printf("Time to draw fixed-function points: %d ms\n", fixed_func_time);
175 printf("Time to draw 1st GLSL shader points: %d ms\n", glsl_time_1);
176 printf("Time to draw 2st GLSL shader points: %d ms\n", glsl_time_2);
177
178 glutSwapBuffers();
179 }
180
181
182 static void
Reshape(int width,int height)183 Reshape(int width, int height)
184 {
185 WinWidth = width;
186 WinHeight = height;
187 glViewport(0, 0, width, height);
188 glMatrixMode(GL_PROJECTION);
189 glLoadIdentity();
190 glFrustum(-1.0, 1.0, -1.0, 1.0, 5.0, 25.0);
191 glMatrixMode(GL_MODELVIEW);
192 glLoadIdentity();
193 glTranslatef(0.0, 0.0, -15.0);
194 }
195
196
197 static void
Key(unsigned char key,int x,int y)198 Key(unsigned char key, int x, int y)
199 {
200 if (key == 27) {
201 glutDestroyWindow(Win);
202 exit(0);
203 }
204 glutPostRedisplay();
205 }
206
207
208 static GLuint
create_shader_program(const char * fs_code,int * compile_time,int * link_time)209 create_shader_program(const char *fs_code,
210 int *compile_time, int *link_time)
211 {
212 GLuint fragShader;
213 GLuint vertShader;
214 GLuint program;
215 GLint u1_loc, u2_loc;
216 GLint t0, t1, t2;
217
218 vertShader = CompileShaderText(GL_VERTEX_SHADER, VS_code);
219
220 t0 = glutGet(GLUT_ELAPSED_TIME);
221 fragShader = CompileShaderText(GL_FRAGMENT_SHADER, fs_code);
222 t1 = glutGet(GLUT_ELAPSED_TIME);
223 program = LinkShaders(vertShader, fragShader);
224 t2 = glutGet(GLUT_ELAPSED_TIME);
225
226 UseProgram(program);
227 u1_loc = glGetUniformLocation(program, "u1");
228 u2_loc = glGetUniformLocation(program, "u2");
229 glUniform4fv(u1_loc, 1, u1_val);
230 glUniform4fv(u2_loc, 1, u2_val);
231 UseProgram(0);
232
233 *compile_time = t1 - t0;
234 *link_time = t2 - t1;
235
236 assert(glGetError() == GL_NO_ERROR);
237
238 return program;
239 }
240
241
242 static void
Init(void)243 Init(void)
244 {
245 GLuint vbo;
246 int i, compile_time, link_time, total_compile_time, total_link_time;
247
248 if (!ShadersSupported())
249 exit(1);
250
251 printf("Shader complexity: %d\n", complexity);
252 printf("Num shaders: %d\n", num_shaders);
253
254 total_compile_time = total_link_time = 0;
255
256 /* create the shader programs */
257 for (i = 0; i < num_shaders; i++) {
258 char *fs_code = gen_large_shader(complexity, i);
259
260 if (verbose && i==0) {
261 printf("Shader[0] code:\n%s\n", fs_code);
262 }
263
264 programs[i] = create_shader_program(fs_code, &compile_time, &link_time);
265 total_compile_time += compile_time;
266 total_link_time += link_time;
267
268 free(fs_code);
269 }
270
271 printf("Total glCompileShader() time: %d ms\n", total_compile_time);
272 printf("Total glLinkProgram() time: %d ms\n", total_link_time);
273
274 glGenBuffers(1, &vbo);
275 glBindBuffer(GL_ARRAY_BUFFER, vbo);
276 glBufferData(GL_ARRAY_BUFFER, sizeof(coords), coords, GL_STATIC_DRAW);
277 glVertexPointer(4, GL_FLOAT, 0, NULL);
278 glEnable(GL_VERTEX_ARRAY);
279 }
280
281
282 int
main(int argc,char * argv[])283 main(int argc, char *argv[])
284 {
285 int i;
286
287 glutInit(&argc, argv);
288 glutInitWindowSize(WinWidth, WinHeight);
289 glutInitDisplayMode(GLUT_RGB | GLUT_DOUBLE | GLUT_DEPTH);
290 Win = glutCreateWindow(argv[0]);
291 gladLoadGL();
292 glutReshapeFunc(Reshape);
293 glutKeyboardFunc(Key);
294 glutDisplayFunc(Draw);
295
296 for (i = 1; i < argc; i++) {
297 if (strcmp(argv[i], "-v") == 0) {
298 verbose = 1;
299 }
300 else if (strcmp(argv[i], "-c") == 0) {
301 i++;
302 complexity = atoi(argv[i]);
303 }
304 else if (strcmp(argv[i], "-n") == 0) {
305 i++;
306 num_shaders = atoi(argv[i]);
307 }
308 else {
309 printf("unexpected option: %s\n", argv[i]);
310 exit(1);
311 }
312 }
313
314 Init();
315
316 glutMainLoop();
317 return 0;
318 }
319