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