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