blob: c02a06354fc04f72bc3426e4a2b3530549234f14 [file] [log] [blame]
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +00001//
2// Copyright (c) 2002-2010 The ANGLE Project Authors. All rights reserved.
3// Use of this source code is governed by a BSD-style license that can be
4// found in the LICENSE file.
5//
6
7//
8// Implement the top-level of interface to the compiler/linker,
9// as defined in ShaderLang.h
10//
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +000011
alokp@chromium.orgea0e1af2010-03-22 19:33:14 +000012#include "GLSLANG/ShaderLang.h"
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +000013
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +000014#include "Initialize.h"
alokp@chromium.orgea0e1af2010-03-22 19:33:14 +000015#include "InitializeDll.h"
alokp@chromium.orgea0e1af2010-03-22 19:33:14 +000016#include "ParseHelper.h"
17#include "ShHandle.h"
18#include "SymbolTable.h"
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +000019
20//
21// A symbol table for each language. Each has a different
22// set of built-ins, and we want to preserve that from
23// compile to compile.
24//
25TSymbolTable SymbolTables[EShLangCount];
26
27
28TPoolAllocator* PerProcessGPA = 0;
29//
30// This is the platform independent interface between an OGL driver
31// and the shading language compiler/linker.
32//
33
34//
35// Driver must call this first, once, before doing any other
36// compiler/linker operations.
37//
38int ShInitialize()
39{
40 TInfoSink infoSink;
41 bool ret = true;
42
43 if (!InitProcess())
44 return 0;
45
46 // This method should be called once per process. If its called by multiple threads, then
47 // we need to have thread synchronization code around the initialization of per process
48 // global pool allocator
49 if (!PerProcessGPA) {
50 TPoolAllocator *builtInPoolAllocator = new TPoolAllocator(true);
51 builtInPoolAllocator->push();
52 TPoolAllocator* gPoolAllocator = &GlobalPoolAllocator;
53 SetGlobalPoolAllocatorPtr(builtInPoolAllocator);
54
55 TSymbolTable symTables[EShLangCount];
56 GenerateBuiltInSymbolTable(0, infoSink, symTables);
57
58 PerProcessGPA = new TPoolAllocator(true);
59 PerProcessGPA->push();
60 SetGlobalPoolAllocatorPtr(PerProcessGPA);
61
62 SymbolTables[EShLangVertex].copyTable(symTables[EShLangVertex]);
63 SymbolTables[EShLangFragment].copyTable(symTables[EShLangFragment]);
64
65 SetGlobalPoolAllocatorPtr(gPoolAllocator);
66
67 symTables[EShLangVertex].pop();
68 symTables[EShLangFragment].pop();
69
70 builtInPoolAllocator->popAll();
71 delete builtInPoolAllocator;
72
73 }
74
75 return ret ? 1 : 0;
76}
77
78//
79// Driver calls these to create and destroy compiler/linker
80// objects.
81//
82
83ShHandle ShConstructCompiler(const EShLanguage language, int debugOptions)
84{
85 if (!InitThread())
86 return 0;
87
88 TShHandleBase* base = static_cast<TShHandleBase*>(ConstructCompiler(language, debugOptions));
89
90 return reinterpret_cast<void*>(base);
91}
92
93ShHandle ShConstructLinker(const EShExecutable executable, int debugOptions)
94{
95 if (!InitThread())
96 return 0;
97
98 TShHandleBase* base = static_cast<TShHandleBase*>(ConstructLinker(executable, debugOptions));
99
100 return reinterpret_cast<void*>(base);
101}
102
103ShHandle ShConstructUniformMap()
104{
105 if (!InitThread())
106 return 0;
107
108 TShHandleBase* base = static_cast<TShHandleBase*>(ConstructUniformMap());
109
110 return reinterpret_cast<void*>(base);
111}
112
113void ShDestruct(ShHandle handle)
114{
115 if (handle == 0)
116 return;
117
118 TShHandleBase* base = static_cast<TShHandleBase*>(handle);
119
120 if (base->getAsCompiler())
121 DeleteCompiler(base->getAsCompiler());
122 else if (base->getAsLinker())
123 DeleteLinker(base->getAsLinker());
124 else if (base->getAsUniformMap())
125 DeleteUniformMap(base->getAsUniformMap());
126}
127
128//
129// Cleanup symbol tables
130//
131int __fastcall ShFinalize()
132{
133 if (PerProcessGPA) {
134 PerProcessGPA->popAll();
135 delete PerProcessGPA;
136 }
137 return 1;
138}
139
140bool GenerateBuiltInSymbolTable(const TBuiltInResource* resources, TInfoSink& infoSink, TSymbolTable* symbolTables, EShLanguage language)
141{
142 TBuiltIns builtIns;
143
144 if (resources) {
145 builtIns.initialize(*resources);
146 InitializeSymbolTable(builtIns.getBuiltInStrings(), language, infoSink, resources, symbolTables);
147 } else {
148 builtIns.initialize();
149 InitializeSymbolTable(builtIns.getBuiltInStrings(), EShLangVertex, infoSink, resources, symbolTables);
150 InitializeSymbolTable(builtIns.getBuiltInStrings(), EShLangFragment, infoSink, resources, symbolTables);
151 }
152
153 return true;
154}
155
156bool InitializeSymbolTable(TBuiltInStrings* BuiltInStrings, EShLanguage language, TInfoSink& infoSink, const TBuiltInResource* resources, TSymbolTable* symbolTables)
157{
158 TIntermediate intermediate(infoSink);
159 TSymbolTable* symbolTable;
160
161 if (resources)
162 symbolTable = symbolTables;
163 else
164 symbolTable = &symbolTables[language];
165
166 TParseContext parseContext(*symbolTable, intermediate, language, infoSink);
167
168 GlobalParseContext = &parseContext;
169
170 setInitialState();
171
172 assert(symbolTable->isEmpty() || symbolTable->atSharedBuiltInLevel());
173
174 //
175 // Parse the built-ins. This should only happen once per
176 // language symbol table.
177 //
178 // Push the symbol table to give it an initial scope. This
179 // push should not have a corresponding pop, so that built-ins
180 // are preserved, and the test for an empty table fails.
181 //
182
183 symbolTable->push();
184
185 //Initialize the Preprocessor
186 int ret = InitPreprocessor();
187 if (ret) {
188 infoSink.info.message(EPrefixInternalError, "Unable to intialize the Preprocessor");
189 return false;
190 }
191
192 for (TBuiltInStrings::iterator i = BuiltInStrings[parseContext.language].begin();
193 i != BuiltInStrings[parseContext.language].end();
194 ++i) {
195 const char* builtInShaders[1];
196 int builtInLengths[1];
197
198 builtInShaders[0] = (*i).c_str();
199 builtInLengths[0] = (int) (*i).size();
200
201 if (PaParseStrings(const_cast<char**>(builtInShaders), builtInLengths, 1, parseContext) != 0) {
202 infoSink.info.message(EPrefixInternalError, "Unable to parse built-ins");
203 return false;
204 }
205 }
206
207 if (resources) {
208 IdentifyBuiltIns(parseContext.language, *symbolTable, *resources);
209 } else {
210 IdentifyBuiltIns(parseContext.language, *symbolTable);
211 }
212
213 FinalizePreprocessor();
214
215 return true;
216}
217
218//
219// Do an actual compile on the given strings. The result is left
220// in the given compile object.
221//
222// Return: The return value of ShCompile is really boolean, indicating
223// success or failure.
224//
225int ShCompile(
226 const ShHandle handle,
227 const char* const shaderStrings[],
228 const int numStrings,
229 const EShOptimizationLevel optLevel,
230 const TBuiltInResource* resources,
231 int debugOptions
232 )
233{
234 if (!InitThread())
235 return 0;
236
237 if (handle == 0)
238 return 0;
239
240 TShHandleBase* base = reinterpret_cast<TShHandleBase*>(handle);
241 TCompiler* compiler = base->getAsCompiler();
242 if (compiler == 0)
243 return 0;
244
245 GlobalPoolAllocator.push();
alokp@chromium.org76b82082010-03-24 17:59:39 +0000246 TInfoSink& infoSink = compiler->infoSink;
247 infoSink.info.erase();
248 infoSink.debug.erase();
249 infoSink.obj.erase();
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000250
251 if (numStrings == 0)
252 return 1;
253
alokp@chromium.org76b82082010-03-24 17:59:39 +0000254 TIntermediate intermediate(infoSink);
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000255 TSymbolTable symbolTable(SymbolTables[compiler->getLanguage()]);
256
alokp@chromium.org76b82082010-03-24 17:59:39 +0000257 GenerateBuiltInSymbolTable(resources, infoSink, &symbolTable, compiler->getLanguage());
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000258
alokp@chromium.org76b82082010-03-24 17:59:39 +0000259 TParseContext parseContext(symbolTable, intermediate, compiler->getLanguage(), infoSink);
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000260 parseContext.initializeExtensionBehavior();
261
262 GlobalParseContext = &parseContext;
263
264 setInitialState();
265
266 InitPreprocessor();
267 //
268 // Parse the application's shaders. All the following symbol table
269 // work will be throw-away, so push a new allocation scope that can
270 // be thrown away, then push a scope for the current shader's globals.
271 //
272 bool success = true;
273
274 symbolTable.push();
275 if (!symbolTable.atGlobalLevel())
276 parseContext.infoSink.info.message(EPrefixInternalError, "Wrong symbol table level");
277
278 int ret = PaParseStrings(const_cast<char**>(shaderStrings), 0, numStrings, parseContext);
279 if (ret)
280 success = false;
281
282 if (success && parseContext.treeRoot) {
283 if (optLevel == EShOptNoGeneration)
284 parseContext.infoSink.info.message(EPrefixNone, "No errors. No code generation or linking was requested.");
285 else {
286 success = intermediate.postProcess(parseContext.treeRoot, parseContext.language);
287
288 if (success) {
289
290 if (debugOptions & EDebugOpIntermediate)
291 intermediate.outputTree(parseContext.treeRoot);
292
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000293 //
294 // Call the machine dependent compiler
295 //
alokp@chromium.org76b82082010-03-24 17:59:39 +0000296 if (!compiler->compile(parseContext.treeRoot))
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000297 success = false;
298 }
299 }
300 } else if (!success) {
301 parseContext.infoSink.info.prefix(EPrefixError);
302 parseContext.infoSink.info << parseContext.numErrors << " compilation errors. No code generated.\n\n";
303 success = false;
304 if (debugOptions & EDebugOpIntermediate)
305 intermediate.outputTree(parseContext.treeRoot);
306 }
307
308 intermediate.remove(parseContext.treeRoot);
309
310 //
311 // Ensure symbol table is returned to the built-in level,
312 // throwing away all but the built-ins.
313 //
314 while (! symbolTable.atSharedBuiltInLevel())
315 symbolTable.pop();
316
317 FinalizePreprocessor();
318 //
319 // Throw away all the temporary memory used by the compilation process.
320 //
321 GlobalPoolAllocator.pop();
322
323 return success ? 1 : 0;
324}
325
326//
327// Do an actual link on the given compile objects.
328//
329// Return: The return value of is really boolean, indicating
330// success or failure.
331//
332int ShLink(
333 const ShHandle linkHandle,
334 const ShHandle compHandles[],
335 const int numHandles,
336 ShHandle uniformMapHandle,
337 short int** uniformsAccessed,
338 int* numUniformsAccessed)
339
340{
341 if (!InitThread())
342 return 0;
343
344 TShHandleBase* base = reinterpret_cast<TShHandleBase*>(linkHandle);
345 TLinker* linker = static_cast<TLinker*>(base->getAsLinker());
346 if (linker == 0)
347 return 0;
348
349 int returnValue;
350 GlobalPoolAllocator.push();
351 returnValue = ShLinkExt(linkHandle, compHandles, numHandles);
352 GlobalPoolAllocator.pop();
353
354 if (returnValue)
355 return 1;
356
357 return 0;
358}
359//
360// This link method will be eventually used once the ICD supports the new linker interface
361//
362int ShLinkExt(
363 const ShHandle linkHandle,
364 const ShHandle compHandles[],
365 const int numHandles)
366{
367 if (linkHandle == 0 || numHandles == 0)
368 return 0;
369
370 THandleList cObjects;
371
372 {// support MSVC++6.0
373 for (int i = 0; i < numHandles; ++i) {
374 if (compHandles[i] == 0)
375 return 0;
376 TShHandleBase* base = reinterpret_cast<TShHandleBase*>(compHandles[i]);
377 if (base->getAsLinker()) {
378 cObjects.push_back(base->getAsLinker());
379 }
380 if (base->getAsCompiler())
381 cObjects.push_back(base->getAsCompiler());
382
383
384 if (cObjects[i] == 0)
385 return 0;
386 }
387 }
388
389 TShHandleBase* base = reinterpret_cast<TShHandleBase*>(linkHandle);
390 TLinker* linker = static_cast<TLinker*>(base->getAsLinker());
391
392 if (linker == 0)
393 return 0;
394
395 linker->infoSink.info.erase();
396 linker->infoSink.obj.erase();
397
398 {// support MSVC++6.0
399 for (int i = 0; i < numHandles; ++i) {
400 if (cObjects[i]->getAsCompiler()) {
401 if (! cObjects[i]->getAsCompiler()->linkable()) {
402 linker->infoSink.info.message(EPrefixError, "Not all shaders have valid object code.");
403 return 0;
404 }
405 }
406 }
407 }
408
409 bool ret = linker->link(cObjects);
410
411 return ret ? 1 : 0;
412}
413
414//
415// ShSetEncrpytionMethod is a place-holder for specifying
416// how source code is encrypted.
417//
418void ShSetEncryptionMethod(ShHandle handle)
419{
420 if (handle == 0)
421 return;
422}
423
424//
425// Return any compiler/linker/uniformmap log of messages for the application.
426//
427const char* ShGetInfoLog(const ShHandle handle)
428{
429 if (!InitThread())
430 return 0;
431
432 if (handle == 0)
433 return 0;
434
435 TShHandleBase* base = static_cast<TShHandleBase*>(handle);
436 TInfoSink* infoSink;
437
438 if (base->getAsCompiler())
439 infoSink = &(base->getAsCompiler()->getInfoSink());
440 else if (base->getAsLinker())
441 infoSink = &(base->getAsLinker()->getInfoSink());
442
443 infoSink->info << infoSink->debug.c_str();
444 return infoSink->info.c_str();
445}
446
447//
448// Return any unlinked object code.
449//
450const char* ShGetObjectCode(const ShHandle handle)
451{
452 if (!InitThread())
453 return 0;
454
455 if (handle == 0)
456 return 0;
457
458 TShHandleBase* base = static_cast<TShHandleBase*>(handle);
459 TInfoSink* infoSink;
460
461 if (base->getAsCompiler())
462 infoSink = &(base->getAsCompiler()->getInfoSink());
463
464 return infoSink->obj.c_str();
465}
466
467//
468// Return the resulting binary code from the link process. Structure
469// is machine dependent.
470//
471const void* ShGetExecutable(const ShHandle handle)
472{
473 if (!InitThread())
474 return 0;
475
476 if (handle == 0)
477 return 0;
478
479 TShHandleBase* base = reinterpret_cast<TShHandleBase*>(handle);
480
481 TLinker* linker = static_cast<TLinker*>(base->getAsLinker());
482 if (linker == 0)
483 return 0;
484
485 return linker->getObjectCode();
486}
487
488//
489// Let the linker know where the application said it's attributes are bound.
490// The linker does not use these values, they are remapped by the ICD or
491// hardware. It just needs them to know what's aliased.
492//
493// Return: The return value of is really boolean, indicating
494// success or failure.
495//
496int ShSetVirtualAttributeBindings(const ShHandle handle, const ShBindingTable* table)
497{
498 if (!InitThread())
499 return 0;
500
501 if (handle == 0)
502 return 0;
503
504 TShHandleBase* base = reinterpret_cast<TShHandleBase*>(handle);
505 TLinker* linker = static_cast<TLinker*>(base->getAsLinker());
506
507 if (linker == 0)
508 return 0;
509
510 linker->setAppAttributeBindings(table);
511
512 return 1;
513}
514
515//
516// Let the linker know where the predefined attributes have to live.
517//
518int ShSetFixedAttributeBindings(const ShHandle handle, const ShBindingTable* table)
519{
520 if (!InitThread())
521 return 0;
522
523 if (handle == 0)
524 return 0;
525
526 TShHandleBase* base = reinterpret_cast<TShHandleBase*>(handle);
527 TLinker* linker = static_cast<TLinker*>(base->getAsLinker());
528
529 if (linker == 0)
530 return 0;
531
532 linker->setFixedAttributeBindings(table);
533 return 1;
534}
535
536//
537// Some attribute locations are off-limits to the linker...
538//
539int ShExcludeAttributes(const ShHandle handle, int *attributes, int count)
540{
541 if (!InitThread())
542 return 0;
543
544 if (handle == 0)
545 return 0;
546
547 TShHandleBase* base = reinterpret_cast<TShHandleBase*>(handle);
548 TLinker* linker = static_cast<TLinker*>(base->getAsLinker());
549 if (linker == 0)
550 return 0;
551
552 linker->setExcludedAttributes(attributes, count);
553
554 return 1;
555}
556
557//
558// Return the index for OpenGL to use for knowing where a uniform lives.
559//
560// Return: The return value of is really boolean, indicating
561// success or failure.
562//
563int ShGetUniformLocation(const ShHandle handle, const char* name)
564{
565 if (!InitThread())
566 return 0;
567
568 if (handle == 0)
569 return -1;
570
571 TShHandleBase* base = reinterpret_cast<TShHandleBase*>(handle);
572 TUniformMap* uniformMap= base->getAsUniformMap();
573 if (uniformMap == 0)
574 return -1;
575
576 return uniformMap->getLocation(name);
577}
578