]> git.cworth.org Git - apitrace/blob - retrace/glstate_shaders.cpp
Support dumping uniform buffer objects (issue #75).
[apitrace] / retrace / glstate_shaders.cpp
1 /**************************************************************************
2  *
3  * Copyright 2011 Jose Fonseca
4  * All Rights Reserved.
5  *
6  * Permission is hereby granted, free of charge, to any person obtaining a copy
7  * of this software and associated documentation files (the "Software"), to deal
8  * in the Software without restriction, including without limitation the rights
9  * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10  * copies of the Software, and to permit persons to whom the Software is
11  * furnished to do so, subject to the following conditions:
12  *
13  * The above copyright notice and this permission notice shall be included in
14  * all copies or substantial portions of the Software.
15  *
16  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
22  * THE SOFTWARE.
23  *
24  **************************************************************************/
25
26
27 #include <string.h>
28
29 #include <algorithm>
30 #include <iostream>
31 #include <map>
32 #include <sstream>
33
34 #include "json.hpp"
35 #include "glproc.hpp"
36 #include "glsize.hpp"
37 #include "glstate.hpp"
38 #include "glstate_internal.hpp"
39
40
41 namespace glstate {
42
43
44 // Mapping from shader type to shader source, used to accumulated the sources
45 // of different shaders with same type.
46 typedef std::map<std::string, std::string> ShaderMap;
47
48
49 static void
50 getShaderSource(ShaderMap &shaderMap, GLuint shader)
51 {
52     if (!shader) {
53         return;
54     }
55
56     GLint shader_type = 0;
57     glGetShaderiv(shader, GL_SHADER_TYPE, &shader_type);
58     if (!shader_type) {
59         return;
60     }
61
62     GLint source_length = 0;
63     glGetShaderiv(shader, GL_SHADER_SOURCE_LENGTH, &source_length);
64     if (!source_length) {
65         return;
66     }
67
68     GLchar *source = new GLchar[source_length];
69     GLsizei length = 0;
70     source[0] = 0;
71     glGetShaderSource(shader, source_length, &length, source);
72
73     shaderMap[enumToString(shader_type)] += source;
74
75     delete [] source;
76 }
77
78
79 static void
80 getShaderObjSource(ShaderMap &shaderMap, GLhandleARB shaderObj)
81 {
82     if (!shaderObj) {
83         return;
84     }
85
86     GLint object_type = 0;
87     glGetObjectParameterivARB(shaderObj, GL_OBJECT_TYPE_ARB, &object_type);
88     if (object_type != GL_SHADER_OBJECT_ARB) {
89         return;
90     }
91
92     GLint shader_type = 0;
93     glGetObjectParameterivARB(shaderObj, GL_OBJECT_SUBTYPE_ARB, &shader_type);
94     if (!shader_type) {
95         return;
96     }
97
98     GLint source_length = 0;
99     glGetObjectParameterivARB(shaderObj, GL_OBJECT_SHADER_SOURCE_LENGTH_ARB, &source_length);
100     if (!source_length) {
101         return;
102     }
103
104     GLcharARB *source = new GLcharARB[source_length];
105     GLsizei length = 0;
106     source[0] = 0;
107     glGetShaderSource(shaderObj, source_length, &length, source);
108
109     shaderMap[enumToString(shader_type)] += source;
110
111     delete [] source;
112 }
113
114
115 static inline void
116 dumpProgram(JSONWriter &json, GLint program)
117 {
118     GLint attached_shaders = 0;
119     glGetProgramiv(program, GL_ATTACHED_SHADERS, &attached_shaders);
120     if (!attached_shaders) {
121         return;
122     }
123
124     ShaderMap shaderMap;
125
126     GLuint *shaders = new GLuint[attached_shaders];
127     GLsizei count = 0;
128     glGetAttachedShaders(program, attached_shaders, &count, shaders);
129     std::sort(shaders, shaders + count);
130     for (GLsizei i = 0; i < count; ++ i) {
131        getShaderSource(shaderMap, shaders[i]);
132     }
133     delete [] shaders;
134
135     for (ShaderMap::const_iterator it = shaderMap.begin(); it != shaderMap.end(); ++it) {
136         json.beginMember(it->first);
137         json.writeString(it->second);
138         json.endMember();
139     }
140 }
141
142
143 static inline void
144 dumpProgramObj(JSONWriter &json, GLhandleARB programObj)
145 {
146     GLint attached_shaders = 0;
147     glGetObjectParameterivARB(programObj, GL_OBJECT_ATTACHED_OBJECTS_ARB, &attached_shaders);
148     if (!attached_shaders) {
149         return;
150     }
151
152     ShaderMap shaderMap;
153
154     GLhandleARB *shaderObjs = new GLhandleARB[attached_shaders];
155     GLsizei count = 0;
156     glGetAttachedObjectsARB(programObj, attached_shaders, &count, shaderObjs);
157     std::sort(shaderObjs, shaderObjs + count);
158     for (GLsizei i = 0; i < count; ++ i) {
159        getShaderObjSource(shaderMap, shaderObjs[i]);
160     }
161     delete [] shaderObjs;
162
163     for (ShaderMap::const_iterator it = shaderMap.begin(); it != shaderMap.end(); ++it) {
164         json.beginMember(it->first);
165         json.writeString(it->second);
166         json.endMember();
167     }
168 }
169
170 /**
171  * Built-in uniforms can't be queried through glGetUniform*.
172  */
173 static inline bool
174 isBuiltinUniform(const GLchar *name)
175 {
176     return name[0] == 'g' && name[1] == 'l' && name[2] == '_';
177 }
178
179 /*
180  * When fetching the uniform name of an array we usually get name[0]
181  * so we need to cut the trailing "[0]" in order to properly construct
182  * array names later. Otherwise we endup with stuff like
183  * uniformArray[0][0],
184  * uniformArray[0][1],
185  * instead of
186  * uniformArray[0],
187  * uniformArray[1].
188  */
189 static std::string
190 resolveUniformName(const GLchar *name,  GLint size)
191 {
192     std::string qualifiedName(name);
193     if (size > 1) {
194         std::string::size_type nameLength = qualifiedName.length();
195         static const char * const arrayStart = "[0]";
196         static const int arrayStartLen = 3;
197         if (qualifiedName.rfind(arrayStart) == (nameLength - arrayStartLen)) {
198             qualifiedName = qualifiedName.substr(0, nameLength - 3);
199         }
200     }
201     return qualifiedName;
202 }
203
204
205 static void
206 dumpUniformValues(JSONWriter &json, GLenum type, const void *values, GLint matrix_stride = 0, GLboolean is_row_major = GL_FALSE) {
207     GLenum elemType;
208     GLint numCols, numRows;
209     _gl_uniform_size(type, elemType, numCols, numRows);
210     if (!numCols || !numRows) {
211         json.writeNull();
212     }
213
214     size_t elemSize = _gl_type_size(elemType);
215
216     GLint row_stride = 0;
217     GLint col_stride = 0;
218     if (is_row_major) {
219         row_stride = elemSize;
220         col_stride = matrix_stride ? matrix_stride : numRows * elemSize;
221     } else {
222         col_stride = elemSize;
223         row_stride = matrix_stride ? matrix_stride : numCols * elemSize;
224     }
225
226     if (numRows > 1) {
227         json.beginArray();
228     }
229
230     for (GLint row = 0; row < numRows; ++row) {
231         if (numCols > 1) {
232             json.beginArray();
233         }
234
235         for (GLint col = 0; col < numCols; ++col) {
236             union {
237                 const GLbyte *rawvalue;
238                 const GLfloat *fvalue;
239                 const GLdouble *dvalue;
240                 const GLint *ivalue;
241                 const GLuint *uivalue;
242             } u;
243
244             u.rawvalue = (const GLbyte *)values + row*row_stride + col*col_stride;
245
246             switch (elemType) {
247             case GL_FLOAT:
248                 json.writeNumber(*u.fvalue);
249                 break;
250             case GL_DOUBLE:
251                 json.writeNumber(*u.dvalue);
252                 break;
253             case GL_INT:
254                 json.writeNumber(*u.ivalue);
255                 break;
256             case GL_UNSIGNED_INT:
257                 json.writeNumber(*u.uivalue);
258                 break;
259             case GL_BOOL:
260                 json.writeBool(*u.uivalue);
261                 break;
262             default:
263                 assert(0);
264                 json.writeNull();
265                 break;
266             }
267         }
268
269         if (numCols > 1) {
270             json.endArray();
271         }
272     }
273
274     if (numRows > 1) {
275         json.endArray();
276     }
277 }
278
279
280 /**
281  * Dump an uniform that belows to an uniform block.
282  */
283 static void
284 dumpUniformBlock(JSONWriter &json, GLint program, GLint size, GLenum type, const GLchar *name, GLuint index, GLuint block_index) {
285
286     GLint offset = 0;
287     GLint array_stride = 0;
288     GLint matrix_stride = 0;
289     GLint is_row_major = GL_FALSE;
290     glGetActiveUniformsiv(program, 1, &index, GL_UNIFORM_OFFSET, &offset);
291     glGetActiveUniformsiv(program, 1, &index, GL_UNIFORM_ARRAY_STRIDE, &array_stride);
292     glGetActiveUniformsiv(program, 1, &index, GL_UNIFORM_MATRIX_STRIDE, &matrix_stride);
293     glGetActiveUniformsiv(program, 1, &index, GL_UNIFORM_IS_ROW_MAJOR, &is_row_major);
294
295     GLint slot = -1;
296     glGetActiveUniformBlockiv(program, block_index, GL_UNIFORM_BLOCK_BINDING, &slot);
297     if (slot == -1) {
298         return;
299     }
300
301     if (0) {
302         GLint active_uniform_block_max_name_length = 0;
303         glGetProgramiv(program, GL_ACTIVE_UNIFORM_BLOCK_MAX_NAME_LENGTH, &active_uniform_block_max_name_length);
304
305         GLchar* block_name = new GLchar[active_uniform_block_max_name_length];
306
307         GLint block_data_size = 0;
308         glGetActiveUniformBlockiv(program, index, GL_UNIFORM_BLOCK_DATA_SIZE, &block_data_size);
309
310         GLsizei length = 0;
311         glGetActiveUniformBlockName(program, index, active_uniform_block_max_name_length, &length, block_name);
312
313         std::cerr
314             << "uniform `" << name << "`, size " << size << ", type " << enumToString(type) << "\n"
315             << "  block " << block_index << ", name `" << block_name << "`, size " << block_data_size << "; binding " << slot << "; \n"
316             << "  offset " << offset << ", array stride " << array_stride << ", matrix stride " << matrix_stride << ", row_major " << is_row_major << "\n"
317         ;
318
319         delete block_name;
320     }
321
322     GLint ubo = 0;
323     glGetIntegeri_v(GL_UNIFORM_BUFFER_BINDING, slot, &ubo);
324
325     GLint previous_ubo = 0;
326     glGetIntegerv(GL_UNIFORM_BUFFER_BINDING, &previous_ubo);
327
328     glBindBuffer(GL_UNIFORM_BUFFER, ubo);
329
330     const GLbyte *raw_data = (const GLbyte *)glMapBuffer(GL_UNIFORM_BUFFER, GL_READ_ONLY);
331     if (raw_data) {
332         std::string qualifiedName = resolveUniformName(name, size);
333
334         for (GLint i = 0; i < size; ++i) {
335             std::stringstream ss;
336             ss << qualifiedName;
337
338             if (size > 1) {
339                 ss << '[' << i << ']';
340             }
341
342             std::string elemName = ss.str();
343
344             json.beginMember(elemName);
345
346             const GLbyte *row = raw_data + offset + array_stride*i;
347
348             dumpUniformValues(json, type, row, matrix_stride, is_row_major);
349
350             json.endMember();
351         }
352
353         glUnmapBuffer(GL_UNIFORM_BUFFER_BINDING);
354     }
355
356     glBindBuffer(GL_UNIFORM_BUFFER, previous_ubo);
357 }
358
359
360 static void
361 dumpUniform(JSONWriter &json, GLint program, GLint size, GLenum type, const GLchar *name) {
362     GLenum elemType;
363     GLint numCols, numRows;
364     _gl_uniform_size(type, elemType, numCols, numRows);
365     if (elemType == GL_NONE) {
366         return;
367     }
368
369     union {
370         GLfloat fvalues[4*4];
371         GLdouble dvalues[4*4];
372         GLint ivalues[4*4];
373         GLuint uivalues[4*4];
374     } u;
375
376     GLint i;
377
378     std::string qualifiedName = resolveUniformName(name, size);
379
380     for (i = 0; i < size; ++i) {
381         std::stringstream ss;
382         ss << qualifiedName;
383
384         if (size > 1) {
385             ss << '[' << i << ']';
386         }
387
388         std::string elemName = ss.str();
389
390         GLint location = glGetUniformLocation(program, elemName.c_str());
391         assert(location != -1);
392         if (location == -1) {
393             continue;
394         }
395
396         json.beginMember(elemName);
397
398         switch (elemType) {
399         case GL_FLOAT:
400             glGetUniformfv(program, location, u.fvalues);
401             break;
402         case GL_DOUBLE:
403             glGetUniformdv(program, location, u.dvalues);
404             break;
405         case GL_INT:
406             glGetUniformiv(program, location, u.ivalues);
407             break;
408         case GL_UNSIGNED_INT:
409             glGetUniformuiv(program, location, u.uivalues);
410             break;
411         case GL_BOOL:
412             glGetUniformiv(program, location, u.ivalues);
413             break;
414         default:
415             assert(0);
416             break;
417         }
418
419         dumpUniformValues(json, type, &u);
420
421         json.endMember();
422     }
423 }
424
425
426 static void
427 dumpUniformARB(JSONWriter &json, GLhandleARB programObj, GLint size, GLenum type, const GLchar *name) {
428     GLenum elemType;
429     GLint numCols, numRows;
430     _gl_uniform_size(type, elemType, numCols, numRows);
431     GLint numElems = numCols * numRows;
432     if (elemType == GL_NONE) {
433         return;
434     }
435
436     GLfloat fvalues[4*4];
437     union {
438         GLdouble dvalues[4*4];
439         GLfloat fvalues[4*4];
440         GLint ivalues[4*4];
441     } u;
442
443     GLint i, j;
444
445     std::string qualifiedName = resolveUniformName(name, size);
446
447     for (i = 0; i < size; ++i) {
448         std::stringstream ss;
449         ss << qualifiedName;
450
451         if (size > 1) {
452             ss << '[' << i << ']';
453         }
454
455         std::string elemName = ss.str();
456
457         json.beginMember(elemName);
458
459         GLint location = glGetUniformLocationARB(programObj, elemName.c_str());
460         if (location == -1) {
461             continue;
462         }
463
464         switch (elemType) {
465         case GL_DOUBLE:
466             // glGetUniformdvARB does not exists
467             glGetUniformfvARB(programObj, location, fvalues);
468             for (j = 0; j < numElems; ++j) {
469                 u.dvalues[j] = fvalues[j];
470             }
471             break;
472         case GL_FLOAT:
473             glGetUniformfvARB(programObj, location, fvalues);
474             break;
475         case GL_UNSIGNED_INT:
476             // glGetUniformuivARB does not exists
477         case GL_INT:
478             glGetUniformivARB(programObj, location, u.ivalues);
479             break;
480         case GL_BOOL:
481             glGetUniformivARB(programObj, location, u.ivalues);
482             break;
483         default:
484             assert(0);
485             break;
486         }
487
488         dumpUniformValues(json, type, &u);
489
490         json.endMember();
491     }
492 }
493
494
495 static inline void
496 dumpProgramUniforms(JSONWriter &json, GLint program)
497 {
498     GLint active_uniforms = 0;
499     glGetProgramiv(program, GL_ACTIVE_UNIFORMS, &active_uniforms);
500     if (!active_uniforms) {
501         return;
502     }
503
504     GLint active_uniform_max_length = 0;
505     glGetProgramiv(program, GL_ACTIVE_UNIFORM_MAX_LENGTH, &active_uniform_max_length);
506     GLchar *name = new GLchar[active_uniform_max_length];
507     if (!name) {
508         return;
509     }
510
511     for (GLuint index = 0; (GLint)index < active_uniforms; ++index) {
512         GLsizei length = 0;
513         GLint size = 0;
514         GLenum type = GL_NONE;
515         glGetActiveUniform(program, index, active_uniform_max_length, &length, &size, &type, name);
516
517         if (isBuiltinUniform(name)) {
518             continue;
519         }
520
521         GLint location = glGetUniformLocation(program, name);
522         if (location != -1) {
523             dumpUniform(json, program, size, type, name);
524             continue;
525         }
526
527         GLint block_index = -1;
528         glGetActiveUniformsiv(program, 1, &index, GL_UNIFORM_BLOCK_INDEX, &block_index);
529         if (block_index != -1) {
530             dumpUniformBlock(json, program, size, type, name, index, block_index);
531             continue;
532         }
533
534         assert(0);
535     }
536
537     delete [] name;
538 }
539
540
541 static inline void
542 dumpProgramObjUniforms(JSONWriter &json, GLhandleARB programObj)
543 {
544     GLint active_uniforms = 0;
545     glGetObjectParameterivARB(programObj, GL_OBJECT_ACTIVE_UNIFORMS_ARB, &active_uniforms);
546     if (!active_uniforms) {
547         return;
548     }
549
550     GLint active_uniform_max_length = 0;
551     glGetObjectParameterivARB(programObj, GL_OBJECT_ACTIVE_UNIFORM_MAX_LENGTH_ARB, &active_uniform_max_length);
552     GLchar *name = new GLchar[active_uniform_max_length];
553     if (!name) {
554         return;
555     }
556
557     for (GLint index = 0; index < active_uniforms; ++index) {
558         GLsizei length = 0;
559         GLint size = 0;
560         GLenum type = GL_NONE;
561         glGetActiveUniformARB(programObj, index, active_uniform_max_length, &length, &size, &type, name);
562
563     if (isBuiltinUniform(name)) {
564         continue;
565     }
566
567         dumpUniformARB(json, programObj, size, type, name);
568     }
569
570     delete [] name;
571 }
572
573
574 static inline void
575 dumpArbProgram(JSONWriter &json, GLenum target)
576 {
577     if (!glIsEnabled(target)) {
578         return;
579     }
580
581     GLint program_length = 0;
582     glGetProgramivARB(target, GL_PROGRAM_LENGTH_ARB, &program_length);
583     if (!program_length) {
584         return;
585     }
586
587     GLchar *source = new GLchar[program_length + 1];
588     source[0] = 0;
589     glGetProgramStringARB(target, GL_PROGRAM_STRING_ARB, source);
590     source[program_length] = 0;
591
592     json.beginMember(enumToString(target));
593     json.writeString(source);
594     json.endMember();
595
596     delete [] source;
597 }
598
599
600 static inline void
601 dumpArbProgramUniforms(JSONWriter &json, GLenum target, const char *prefix)
602 {
603     if (!glIsEnabled(target)) {
604         return;
605     }
606
607     GLint program_parameters = 0;
608     glGetProgramivARB(target, GL_PROGRAM_PARAMETERS_ARB, &program_parameters);
609     if (!program_parameters) {
610         return;
611     }
612
613     GLint max_program_local_parameters = 0;
614     glGetProgramivARB(target, GL_MAX_PROGRAM_LOCAL_PARAMETERS_ARB, &max_program_local_parameters);
615     for (GLint index = 0; index < max_program_local_parameters; ++index) {
616         GLdouble params[4] = {0, 0, 0, 0};
617         glGetProgramLocalParameterdvARB(target, index, params);
618
619         if (!params[0] && !params[1] && !params[2] && !params[3]) {
620             continue;
621         }
622
623         char name[256];
624         snprintf(name, sizeof name, "%sprogram.local[%i]", prefix, index);
625
626         json.beginMember(name);
627         json.beginArray();
628         json.writeNumber(params[0]);
629         json.writeNumber(params[1]);
630         json.writeNumber(params[2]);
631         json.writeNumber(params[3]);
632         json.endArray();
633         json.endMember();
634     }
635
636     GLint max_program_env_parameters = 0;
637     glGetProgramivARB(target, GL_MAX_PROGRAM_ENV_PARAMETERS_ARB, &max_program_env_parameters);
638     for (GLint index = 0; index < max_program_env_parameters; ++index) {
639         GLdouble params[4] = {0, 0, 0, 0};
640         glGetProgramEnvParameterdvARB(target, index, params);
641
642         if (!params[0] && !params[1] && !params[2] && !params[3]) {
643             continue;
644         }
645
646         char name[256];
647         snprintf(name, sizeof name, "%sprogram.env[%i]", prefix, index);
648
649         json.beginMember(name);
650         json.beginArray();
651         json.writeNumber(params[0]);
652         json.writeNumber(params[1]);
653         json.writeNumber(params[2]);
654         json.writeNumber(params[3]);
655         json.endArray();
656         json.endMember();
657     }
658 }
659
660
661 void
662 dumpShadersUniforms(JSONWriter &json, Context &context)
663 {
664     GLint program = 0;
665     glGetIntegerv(GL_CURRENT_PROGRAM, &program);
666
667     GLhandleARB programObj = 0;
668     if (!context.ES && !program) {
669         programObj = glGetHandleARB(GL_PROGRAM_OBJECT_ARB);
670     }
671
672     json.beginMember("shaders");
673     json.beginObject();
674     if (program) {
675         dumpProgram(json, program);
676     } else if (programObj) {
677         dumpProgramObj(json, programObj);
678     } else {
679         dumpArbProgram(json, GL_FRAGMENT_PROGRAM_ARB);
680         dumpArbProgram(json, GL_VERTEX_PROGRAM_ARB);
681     }
682     json.endObject();
683     json.endMember(); // shaders
684
685     json.beginMember("uniforms");
686     json.beginObject();
687     if (program) {
688         dumpProgramUniforms(json, program);
689     } else if (programObj) {
690         dumpProgramObjUniforms(json, programObj);
691     } else {
692         dumpArbProgramUniforms(json, GL_FRAGMENT_PROGRAM_ARB, "fp.");
693         dumpArbProgramUniforms(json, GL_VERTEX_PROGRAM_ARB, "vp.");
694     }
695     json.endObject();
696     json.endMember(); // uniforms
697 }
698
699
700 } /* namespace glstate */