blob: 3b264886554137b4abcd92f8d74d49ac3df0e264 [file] [log] [blame]
John Kessenicha0af4732012-12-12 21:15:54 +00001//
2//Copyright (C) 2002-2005 3Dlabs Inc. Ltd.
John Kessenichbd0747d2013-02-17 06:01:50 +00003//Copyright (C) 2013 LunarG, Inc.
4//
John Kessenicha0af4732012-12-12 21:15:54 +00005//All rights reserved.
6//
7//Redistribution and use in source and binary forms, with or without
8//modification, are permitted provided that the following conditions
9//are met:
10//
11// Redistributions of source code must retain the above copyright
12// notice, this list of conditions and the following disclaimer.
13//
14// Redistributions in binary form must reproduce the above
15// copyright notice, this list of conditions and the following
16// disclaimer in the documentation and/or other materials provided
17// with the distribution.
18//
19// Neither the name of 3Dlabs Inc. Ltd. nor the names of its
20// contributors may be used to endorse or promote products derived
21// from this software without specific prior written permission.
22//
23//THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
24//"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
25//LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
26//FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
27//COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
28//INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
29//BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
30//LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
31//CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
32//LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
33//ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
34//POSSIBILITY OF SUCH DAMAGE.
35//
John Kessenich2b07c7e2013-07-31 18:44:13 +000036#include "Worklist.h"
John Kessenicha0af4732012-12-12 21:15:54 +000037#include "./../glslang/Include/ShHandle.h"
38#include "./../glslang/Public/ShaderLang.h"
39#include <string.h>
John Kessenichcfd643e2013-03-08 23:14:42 +000040#include <stdlib.h>
John Kessenicha0af4732012-12-12 21:15:54 +000041#include <math.h>
42
John Kessenich2b07c7e2013-07-31 18:44:13 +000043#include "osinclude.h"
John Kessenicha0af4732012-12-12 21:15:54 +000044
45extern "C" {
46 SH_IMPORT_EXPORT void ShOutputHtml();
47}
48
John Kessenich94a81fb2013-08-31 02:41:30 +000049// Command-line options
50enum TOptions {
51 EOptionNone = 0x000,
52 EOptionIntermediate = 0x001,
53 EOptionSuppressInfolog = 0x002,
54 EOptionMemoryLeakMode = 0x004,
55 EOptionRelaxedErrors = 0x008,
56 EOptionGiveWarnings = 0x010,
57 EOptionsLinkProgram = 0x020,
58};
59
John Kessenicha0af4732012-12-12 21:15:54 +000060//
61// Return codes from main.
62//
63enum TFailCode {
64 ESuccess = 0,
65 EFailUsage,
66 EFailCompile,
67 EFailLink,
68 EFailCompilerCreate,
John Kessenich2b07c7e2013-07-31 18:44:13 +000069 EFailThreadCreate,
John Kessenicha0af4732012-12-12 21:15:54 +000070 EFailLinkerCreate
71};
72
73//
74// Just placeholders for testing purposes. The stand-alone environment
75// can't actually do a full link without something specifying real
76// attribute bindings.
77//
78ShBinding FixedAttributeBindings[] = {
79 { "gl_Vertex", 15 },
80 { "gl_Color", 10 },
81 { "gl_Normal", 7 },
82};
83
84ShBindingTable FixedAttributeTable = { 3, FixedAttributeBindings };
85
John Kessenichb603f912013-08-29 00:39:25 +000086EShLanguage FindLanguage(const std::string& name);
John Kessenich52ac67e2013-05-05 23:46:22 +000087bool CompileFile(const char *fileName, ShHandle, int options, const TBuiltInResource*);
John Kessenicha0af4732012-12-12 21:15:54 +000088void usage();
89void FreeFileData(char **data);
John Kessenich54d8cda2013-02-11 22:36:01 +000090char** ReadFileData(const char *fileName);
91void InfoLogMsg(const char* msg, const char* name, const int num);
John Kessenich41cf6b52013-06-25 18:10:05 +000092
93// Use to test breaking a single shader file into multiple strings.
94int NumShaderStrings = 1;
John Kessenicha0af4732012-12-12 21:15:54 +000095
96//
97// Set up the per compile resources
98//
99void GenerateResources(TBuiltInResource& resources)
John Kessenichb51f62c2013-04-11 16:31:09 +0000100{
John Kessenicha0af4732012-12-12 21:15:54 +0000101 resources.maxLights = 32;
102 resources.maxClipPlanes = 6;
103 resources.maxTextureUnits = 32;
104 resources.maxTextureCoords = 32;
105 resources.maxVertexAttribs = 64;
106 resources.maxVertexUniformComponents = 4096;
107 resources.maxVaryingFloats = 64;
108 resources.maxVertexTextureImageUnits = 32;
109 resources.maxCombinedTextureImageUnits = 32;
110 resources.maxTextureImageUnits = 32;
111 resources.maxFragmentUniformComponents = 4096;
112 resources.maxDrawBuffers = 32;
John Kessenichbd0747d2013-02-17 06:01:50 +0000113 resources.maxVertexUniformVectors = 128;
114 resources.maxVaryingVectors = 8;
115 resources.maxFragmentUniformVectors = 16;
116 resources.maxVertexOutputVectors = 16;
John Kessenich1f2a36b2013-02-20 04:42:42 +0000117 resources.maxFragmentInputVectors = 15;
John Kessenichbd0747d2013-02-17 06:01:50 +0000118 resources.minProgramTexelOffset = -8;
119 resources.maxProgramTexelOffset = 7;
John Kessenicha0af4732012-12-12 21:15:54 +0000120}
121
John Kessenich2b07c7e2013-07-31 18:44:13 +0000122glslang::TWorklist Worklist;
John Kessenich94a81fb2013-08-31 02:41:30 +0000123int Options = 0;
John Kessenich2b07c7e2013-07-31 18:44:13 +0000124bool Delay = false;
125
126bool ProcessArguments(int argc, char* argv[])
127{
128 argc--;
129 argv++;
130 for (; argc >= 1; argc--, argv++) {
131 if (argv[0][0] == '-') {
132 switch (argv[0][1]) {
133 case 'd':
134 Delay = true;
135 break;
136 case 'i':
John Kessenich94a81fb2013-08-31 02:41:30 +0000137 Options |= EOptionIntermediate;
John Kessenich2b07c7e2013-07-31 18:44:13 +0000138 break;
139 case 'l':
John Kessenich94a81fb2013-08-31 02:41:30 +0000140 Options |= EOptionsLinkProgram;
141 break;
142 case 'm':
143 Options |= EOptionMemoryLeakMode;
John Kessenich2b07c7e2013-07-31 18:44:13 +0000144 break;
145 case 'r':
John Kessenich94a81fb2013-08-31 02:41:30 +0000146 Options |= EOptionRelaxedErrors;
John Kessenich2b07c7e2013-07-31 18:44:13 +0000147 break;
148 case 's':
John Kessenich94a81fb2013-08-31 02:41:30 +0000149 Options |= EOptionSuppressInfolog;
John Kessenich2b07c7e2013-07-31 18:44:13 +0000150 break;
John Kessenich2b07c7e2013-07-31 18:44:13 +0000151 default:
John Kessenich2b07c7e2013-07-31 18:44:13 +0000152 return false;
153 }
154 } else
155 Worklist.add(std::string(argv[0]));
156 }
157
John Kessenich54f6e562013-08-03 00:04:10 +0000158 if (Worklist.empty())
159 return false;
160
John Kessenich2b07c7e2013-07-31 18:44:13 +0000161 return true;
162}
163
John Kessenich69f4b512013-09-04 21:19:27 +0000164// Thread entry point, for non-linking asynchronous mode.
John Kessenichee6a9c82013-07-31 23:19:17 +0000165unsigned int
166#ifdef _WIN32
167 __stdcall
168#endif
John Kessenich94a81fb2013-08-31 02:41:30 +0000169CompileShaders(void*)
John Kessenich2b07c7e2013-07-31 18:44:13 +0000170{
John Kessenich2b07c7e2013-07-31 18:44:13 +0000171 std::string shaderName;
172 while (Worklist.remove(shaderName)) {
John Kessenich69f4b512013-09-04 21:19:27 +0000173 ShHandle compiler = ShConstructCompiler(FindLanguage(shaderName), Options);
John Kessenich2b07c7e2013-07-31 18:44:13 +0000174 if (compiler == 0)
175 return false;
176
177 TBuiltInResource resources;
178 GenerateResources(resources);
John Kessenich94a81fb2013-08-31 02:41:30 +0000179 CompileFile(shaderName.c_str(), compiler, Options, &resources);
John Kessenich2b07c7e2013-07-31 18:44:13 +0000180
John Kessenich94a81fb2013-08-31 02:41:30 +0000181 if (! (Options & EOptionSuppressInfolog))
John Kessenich2b07c7e2013-07-31 18:44:13 +0000182 puts(ShGetInfoLog(compiler));
183
184 ShDestruct(compiler);
185 }
186
187 return 0;
188}
189
John Kessenich69f4b512013-09-04 21:19:27 +0000190//
191// For linking mode: Will independently parse each item in the worklist, but then put them
192// in the same program and link them together.
193//
194// Uses the new C++ interface instead of the old handle-based interface.
195//
196void CompileAndLinkShaders()
197{
198 // keep track of what to free
199 std::list<glslang::TShader*> shaders;
200
201 EShMessages messages = EShMsgDefault;
202 if (Options & EOptionRelaxedErrors)
203 messages = (EShMessages)(messages | EShMsgRelaxedErrors);
204 if (Options & EOptionIntermediate)
205 messages = (EShMessages)(messages | EShMsgAST);
206
207 TBuiltInResource resources;
208 GenerateResources(resources);
209
210 //
211 // Per-shader processing...
212 //
213
214 glslang::TProgram program;
215 std::string shaderName;
216 while (Worklist.remove(shaderName)) {
217 EShLanguage stage = FindLanguage(shaderName);
218 glslang::TShader* shader = new glslang::TShader(stage);
219 shaders.push_back(shader);
220
221 char** shaderStrings = ReadFileData(shaderName.c_str());
222 if (! shaderStrings) {
223 usage();
224 return;
225 }
226
227 shader->setStrings(shaderStrings, 1);
228
229 shader->parse(&resources, 100, false, messages);
230
231 program.addShader(shader);
232
233 if (! (Options & EOptionSuppressInfolog)) {
234 puts(shaderName.c_str());
235 puts(shader->getInfoLog());
236 puts(shader->getInfoDebugLog());
237 }
238
239 FreeFileData(shaderStrings);
240 }
241
242 //
243 // Program-level processing...
244 //
245
246 program.link(messages);
247 if (! (Options & EOptionSuppressInfolog)) {
248 puts(program.getInfoLog());
249 puts(program.getInfoDebugLog());
250 }
251
252 // free everything up
253 while (shaders.size() > 0) {
254 delete shaders.back();
255 shaders.pop_back();
256 }
257
258 // TODO: memory: for each compile, need a GetThreadPoolAllocator().pop();
259}
260
John Kessenicha0af4732012-12-12 21:15:54 +0000261int C_DECL main(int argc, char* argv[])
262{
John Kessenicha0af4732012-12-12 21:15:54 +0000263 bool compileFailed = false;
264 bool linkFailed = false;
John Kessenicha0af4732012-12-12 21:15:54 +0000265
John Kessenich2b07c7e2013-07-31 18:44:13 +0000266 // Init for front-end proper
John Kessenicha0af4732012-12-12 21:15:54 +0000267 ShInitialize();
268
John Kessenich2b07c7e2013-07-31 18:44:13 +0000269 // Init for for standalone
270 glslang::InitGlobalLock();
John Kessenicha0af4732012-12-12 21:15:54 +0000271
John Kessenich54f6e562013-08-03 00:04:10 +0000272 if (! ProcessArguments(argc, argv)) {
273 usage();
John Kessenich2b07c7e2013-07-31 18:44:13 +0000274 return EFailUsage;
John Kessenich54f6e562013-08-03 00:04:10 +0000275 }
John Kessenich2b07c7e2013-07-31 18:44:13 +0000276
John Kessenich69f4b512013-09-04 21:19:27 +0000277 //
278 // Two modes:
279 // 1) linking all arguments together, single-threaded
280 // 2) independent arguments, can be tackled by multiple asynchronous threads, for testing thread safety
281 //
282
John Kessenich2b07c7e2013-07-31 18:44:13 +0000283 // TODO: finish threading, allow external control over number of threads
284 const int NumThreads = 1;
285 if (NumThreads > 1) {
286 void* threads[NumThreads];
287 for (int t = 0; t < NumThreads; ++t) {
288 threads[t] = glslang::OS_CreateThread(&CompileShaders);
289 if (! threads[t]) {
290 printf("Failed to create thread\n");
291 return EFailThreadCreate;
John Kessenicha0af4732012-12-12 21:15:54 +0000292 }
293 }
John Kessenich2b07c7e2013-07-31 18:44:13 +0000294 glslang::OS_WaitForAllThreads(threads, NumThreads);
295 } else {
John Kessenich69f4b512013-09-04 21:19:27 +0000296 if (Options & EOptionsLinkProgram) {
297 CompileAndLinkShaders();
298 } else {
299 if (! CompileShaders(0))
300 compileFailed = true;
301 }
John Kessenicha0af4732012-12-12 21:15:54 +0000302 }
John Kessenich2b07c7e2013-07-31 18:44:13 +0000303
304 if (Delay)
305 glslang::OS_Sleep(1000000);
John Kessenicha0af4732012-12-12 21:15:54 +0000306
307 if (compileFailed)
308 return EFailCompile;
309 if (linkFailed)
310 return EFailLink;
311
312 return 0;
313}
314
315//
316// Deduce the language from the filename. Files must end in one of the
317// following extensions:
318//
John Kessenich2b07c7e2013-07-31 18:44:13 +0000319// .vert = vertex
320// .tesc = tessellation control
321// .tese = tessellation evaluation
322// .geom = geometry
323// .frag = fragment
John Kessenich94a81fb2013-08-31 02:41:30 +0000324// .comp = compute
John Kessenicha0af4732012-12-12 21:15:54 +0000325//
John Kessenichb603f912013-08-29 00:39:25 +0000326EShLanguage FindLanguage(const std::string& name)
John Kessenicha0af4732012-12-12 21:15:54 +0000327{
John Kessenich2b07c7e2013-07-31 18:44:13 +0000328 size_t ext = name.rfind('.');
329 if (ext == std::string::npos) {
330 usage();
John Kesseniche95ecc52012-12-12 21:34:14 +0000331 return EShLangVertex;
John Kessenicha0af4732012-12-12 21:15:54 +0000332 }
333
John Kessenich2b07c7e2013-07-31 18:44:13 +0000334 std::string suffix = name.substr(ext + 1, std::string::npos);
335 if (suffix == "vert")
336 return EShLangVertex;
337 else if (suffix == "tesc")
338 return EShLangTessControl;
339 else if (suffix == "tese")
340 return EShLangTessEvaluation;
341 else if (suffix == "geom")
342 return EShLangGeometry;
343 else if (suffix == "frag")
344 return EShLangFragment;
John Kessenichc0275792013-08-09 17:14:49 +0000345 else if (suffix == "comp")
346 return EShLangCompute;
John Kessenich2b07c7e2013-07-31 18:44:13 +0000347
348 usage();
John Kesseniche95ecc52012-12-12 21:34:14 +0000349 return EShLangVertex;
John Kessenicha0af4732012-12-12 21:15:54 +0000350}
351
John Kessenicha0af4732012-12-12 21:15:54 +0000352//
John Kessenich69f4b512013-09-04 21:19:27 +0000353// Read a file's data into a string, and compile it using the old interface ShCompile,
354// for non-linkable results.
John Kessenicha0af4732012-12-12 21:15:54 +0000355//
John Kessenich69f4b512013-09-04 21:19:27 +0000356bool CompileFile(const char *fileName, ShHandle compiler, int Options, const TBuiltInResource* resources)
John Kessenicha0af4732012-12-12 21:15:54 +0000357{
358 int ret;
John Kessenich41cf6b52013-06-25 18:10:05 +0000359 char** shaderStrings = ReadFileData(fileName);
John Kessenichdb4cd542013-06-26 22:42:55 +0000360 if (! shaderStrings) {
361 usage();
362 return false;
363 }
364
John Kessenich41cf6b52013-06-25 18:10:05 +0000365 int* lengths = new int[NumShaderStrings];
366
367 // move to length-based strings, rather than null-terminated strings
368 for (int s = 0; s < NumShaderStrings; ++s)
369 lengths[s] = strlen(shaderStrings[s]);
John Kessenich09da79e2013-04-17 19:34:23 +0000370
John Kessenich41cf6b52013-06-25 18:10:05 +0000371 if (! shaderStrings)
John Kessenicha0af4732012-12-12 21:15:54 +0000372 return false;
373
John Kessenich52ac67e2013-05-05 23:46:22 +0000374 EShMessages messages = EShMsgDefault;
John Kessenich94a81fb2013-08-31 02:41:30 +0000375 if (Options & EOptionRelaxedErrors)
John Kessenich52ac67e2013-05-05 23:46:22 +0000376 messages = (EShMessages)(messages | EShMsgRelaxedErrors);
John Kessenich94a81fb2013-08-31 02:41:30 +0000377 if (Options & EOptionIntermediate)
378 messages = (EShMessages)(messages | EShMsgAST);
John Kessenich69f4b512013-09-04 21:19:27 +0000379
John Kessenich94a81fb2013-08-31 02:41:30 +0000380 for (int i = 0; i < ((Options & EOptionMemoryLeakMode) ? 100 : 1); ++i) {
381 for (int j = 0; j < ((Options & EOptionMemoryLeakMode) ? 100 : 1); ++j) {
382 //ret = ShCompile(compiler, shaderStrings, NumShaderStrings, lengths, EShOptNone, resources, Options, 100, false, messages);
383 ret = ShCompile(compiler, shaderStrings, NumShaderStrings, 0, EShOptNone, resources, Options, 100, false, messages);
John Kessenich41cf6b52013-06-25 18:10:05 +0000384 //const char* multi[4] = { "# ve", "rsion", " 300 e", "s" };
385 //const char* multi[7] = { "/", "/", "\\", "\n", "\n", "#", "version 300 es" };
John Kessenich94a81fb2013-08-31 02:41:30 +0000386 //ret = ShCompile(compiler, multi, 4, 0, EShOptNone, resources, Options, 100, false, messages);
John Kessenich41cf6b52013-06-25 18:10:05 +0000387 }
John Kessenicha0af4732012-12-12 21:15:54 +0000388
John Kessenich94a81fb2013-08-31 02:41:30 +0000389 if (Options & EOptionMemoryLeakMode)
John Kessenich2b07c7e2013-07-31 18:44:13 +0000390 glslang::OS_DumpMemoryCounters();
John Kessenicha0af4732012-12-12 21:15:54 +0000391 }
John Kessenicha0af4732012-12-12 21:15:54 +0000392
John Kessenich41cf6b52013-06-25 18:10:05 +0000393 delete [] lengths;
394 FreeFileData(shaderStrings);
John Kessenicha0af4732012-12-12 21:15:54 +0000395
396 return ret ? true : false;
397}
398
John Kessenicha0af4732012-12-12 21:15:54 +0000399//
400// print usage to stdout
401//
402void usage()
403{
John Kessenich8df53cc2013-04-14 19:23:50 +0000404 printf("Usage: standalone [ options ] filename\n"
John Kessenichc0275792013-08-09 17:14:49 +0000405 "Where: filename is a name ending in\n"
406 " .vert for a vertex shader\n"
407 " .tesc for a tessellation control shader\n"
408 " .tese for a tessellation evaluation shader\n"
409 " .geom for a geometry shader\n"
410 " .frag for a fragment shader\n"
411 " .comp for a compute shader\n\n"
John Kessenich4586dbd2013-08-05 15:52:03 +0000412 "Compilation warnings and errors will be printed to stdout.\n"
413 "To get other information, use one of the following options:\n"
414 "-i: intermediate tree (glslang AST) is printed out\n"
415 "-d: delay exit\n"
John Kessenich94a81fb2013-08-31 02:41:30 +0000416 "-l: link validation of all input files\n"
417 "-m: memory leak mode\n"
John Kessenich4586dbd2013-08-05 15:52:03 +0000418 "-s: silent mode\n"
419 "-r: relaxed semantic error-checking mode\n");
John Kessenicha0af4732012-12-12 21:15:54 +0000420}
421
John Kessenichcfd643e2013-03-08 23:14:42 +0000422#ifndef _WIN32
423
424#include <errno.h>
425
426int fopen_s(
427 FILE** pFile,
428 const char *filename,
429 const char *mode
430)
431{
432 if (!pFile || !filename || !mode) {
433 return EINVAL;
434 }
435
436 FILE* f = fopen(filename, mode);
437 if (! f) {
438 if (errno != 0) {
439 return errno;
440 } else {
441 return ENOENT;
442 }
443 }
444 *pFile = f;
445
446 return 0;
447}
448
449#endif
450
John Kessenicha0af4732012-12-12 21:15:54 +0000451//
452// Malloc a string of sufficient size and read a string into it.
453//
John Kessenich54d8cda2013-02-11 22:36:01 +0000454char** ReadFileData(const char *fileName)
John Kessenicha0af4732012-12-12 21:15:54 +0000455{
John Kessenich200b2732012-12-12 21:21:23 +0000456 FILE *in;
457 int errorCode = fopen_s(&in, fileName, "r");
John Kessenicha0af4732012-12-12 21:15:54 +0000458 char *fdata;
459 int count = 0;
John Kessenichcfd643e2013-03-08 23:14:42 +0000460 const int maxSourceStrings = 5;
461 char** return_data = (char**)malloc(maxSourceStrings+1);
John Kessenicha0af4732012-12-12 21:15:54 +0000462
463 //return_data[MAX_SOURCE_STRINGS]=NULL;
John Kessenich200b2732012-12-12 21:21:23 +0000464 if (errorCode) {
John Kessenicha0af4732012-12-12 21:15:54 +0000465 printf("Error: unable to open input file: %s\n", fileName);
466 return 0;
467 }
468
469 while (fgetc(in) != EOF)
470 count++;
471
472 fseek(in, 0, SEEK_SET);
473
474
475 if (!(fdata = (char *)malloc(count+2))) {
476 printf("Error allocating memory\n");
477 return 0;
478 }
479 if (fread(fdata,1,count, in)!=count) {
480 printf("Error reading input file: %s\n", fileName);
481 return 0;
482 }
483 fdata[count] = '\0';
484 fclose(in);
485 if(count==0){
486 return_data[0]=(char*)malloc(count+2);
487 return_data[0][0]='\0';
John Kessenich41cf6b52013-06-25 18:10:05 +0000488 NumShaderStrings=0;
John Kessenicha0af4732012-12-12 21:15:54 +0000489 return return_data;
490 }
491
John Kessenich41cf6b52013-06-25 18:10:05 +0000492 int len = (int)(ceil)((float)count/(float)NumShaderStrings);
John Kessenicha0af4732012-12-12 21:15:54 +0000493 int ptr_len=0,i=0;
494 while(count>0){
495 return_data[i]=(char*)malloc(len+2);
496 memcpy(return_data[i],fdata+ptr_len,len);
497 return_data[i][len]='\0';
498 count-=(len);
499 ptr_len+=(len);
500 if(count<len){
501 if(count==0){
John Kessenich41cf6b52013-06-25 18:10:05 +0000502 NumShaderStrings=(i+1);
John Kessenicha0af4732012-12-12 21:15:54 +0000503 break;
504 }
505 len = count;
506 }
507 ++i;
508 }
509 return return_data;
510}
511
John Kessenicha0af4732012-12-12 21:15:54 +0000512void FreeFileData(char **data)
513{
John Kessenich41cf6b52013-06-25 18:10:05 +0000514 for(int i=0;i<NumShaderStrings;i++)
John Kessenicha0af4732012-12-12 21:15:54 +0000515 free(data[i]);
516}
517
John Kessenich54d8cda2013-02-11 22:36:01 +0000518void InfoLogMsg(const char* msg, const char* name, const int num)
John Kessenicha0af4732012-12-12 21:15:54 +0000519{
520 printf(num >= 0 ? "#### %s %s %d INFO LOG ####\n" :
521 "#### %s %s INFO LOG ####\n", msg, name, num);
522}