blob: 58180434d8c327ef66f9d408cb1c9fa564474ad9 [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
alokp@chromium.org613ef312010-07-21 18:54:22 +0000173 // TODO(alokp): Investigate if a parse-context is necessary here and
174 // if symbol-table can be shared between GLES2 and WebGL specs.
175 TParseContext parseContext(*symbolTable, intermediate, language, EShSpecGLES2, infoSink);
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000176
177 GlobalParseContext = &parseContext;
178
179 setInitialState();
180
181 assert(symbolTable->isEmpty() || symbolTable->atSharedBuiltInLevel());
182
183 //
184 // Parse the built-ins. This should only happen once per
185 // language symbol table.
186 //
187 // Push the symbol table to give it an initial scope. This
188 // push should not have a corresponding pop, so that built-ins
189 // are preserved, and the test for an empty table fails.
190 //
191
192 symbolTable->push();
193
194 //Initialize the Preprocessor
195 int ret = InitPreprocessor();
196 if (ret) {
197 infoSink.info.message(EPrefixInternalError, "Unable to intialize the Preprocessor");
198 return false;
199 }
200
201 for (TBuiltInStrings::iterator i = BuiltInStrings[parseContext.language].begin();
202 i != BuiltInStrings[parseContext.language].end();
203 ++i) {
204 const char* builtInShaders[1];
205 int builtInLengths[1];
206
207 builtInShaders[0] = (*i).c_str();
208 builtInLengths[0] = (int) (*i).size();
209
210 if (PaParseStrings(const_cast<char**>(builtInShaders), builtInLengths, 1, parseContext) != 0) {
211 infoSink.info.message(EPrefixInternalError, "Unable to parse built-ins");
212 return false;
213 }
214 }
215
216 if (resources) {
217 IdentifyBuiltIns(parseContext.language, *symbolTable, *resources);
218 } else {
219 IdentifyBuiltIns(parseContext.language, *symbolTable);
220 }
221
222 FinalizePreprocessor();
223
224 return true;
225}
226
227//
228// Do an actual compile on the given strings. The result is left
229// in the given compile object.
230//
231// Return: The return value of ShCompile is really boolean, indicating
232// success or failure.
233//
234int ShCompile(
235 const ShHandle handle,
236 const char* const shaderStrings[],
237 const int numStrings,
238 const EShOptimizationLevel optLevel,
239 const TBuiltInResource* resources,
240 int debugOptions
241 )
242{
243 if (!InitThread())
244 return 0;
245
246 if (handle == 0)
247 return 0;
248
249 TShHandleBase* base = reinterpret_cast<TShHandleBase*>(handle);
250 TCompiler* compiler = base->getAsCompiler();
251 if (compiler == 0)
252 return 0;
253
254 GlobalPoolAllocator.push();
alokp@chromium.org76b82082010-03-24 17:59:39 +0000255 TInfoSink& infoSink = compiler->infoSink;
256 infoSink.info.erase();
257 infoSink.debug.erase();
258 infoSink.obj.erase();
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000259
260 if (numStrings == 0)
261 return 1;
262
alokp@chromium.org76b82082010-03-24 17:59:39 +0000263 TIntermediate intermediate(infoSink);
alokp@chromium.org90033b92010-05-24 15:02:43 +0000264 TSymbolTable symbolTable(*SymbolTables[compiler->getLanguage()]);
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000265
alokp@chromium.org76b82082010-03-24 17:59:39 +0000266 GenerateBuiltInSymbolTable(resources, infoSink, &symbolTable, compiler->getLanguage());
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000267
alokp@chromium.org613ef312010-07-21 18:54:22 +0000268 TParseContext parseContext(symbolTable, intermediate, compiler->getLanguage(), compiler->getSpec(), infoSink);
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000269 parseContext.initializeExtensionBehavior();
270
271 GlobalParseContext = &parseContext;
272
273 setInitialState();
274
275 InitPreprocessor();
276 //
277 // Parse the application's shaders. All the following symbol table
278 // work will be throw-away, so push a new allocation scope that can
279 // be thrown away, then push a scope for the current shader's globals.
280 //
281 bool success = true;
282
283 symbolTable.push();
284 if (!symbolTable.atGlobalLevel())
285 parseContext.infoSink.info.message(EPrefixInternalError, "Wrong symbol table level");
286
287 int ret = PaParseStrings(const_cast<char**>(shaderStrings), 0, numStrings, parseContext);
288 if (ret)
289 success = false;
290
291 if (success && parseContext.treeRoot) {
292 if (optLevel == EShOptNoGeneration)
293 parseContext.infoSink.info.message(EPrefixNone, "No errors. No code generation or linking was requested.");
294 else {
295 success = intermediate.postProcess(parseContext.treeRoot, parseContext.language);
296
297 if (success) {
298
299 if (debugOptions & EDebugOpIntermediate)
300 intermediate.outputTree(parseContext.treeRoot);
301
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000302 //
303 // Call the machine dependent compiler
304 //
alokp@chromium.org76b82082010-03-24 17:59:39 +0000305 if (!compiler->compile(parseContext.treeRoot))
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000306 success = false;
307 }
308 }
309 } else if (!success) {
310 parseContext.infoSink.info.prefix(EPrefixError);
311 parseContext.infoSink.info << parseContext.numErrors << " compilation errors. No code generated.\n\n";
312 success = false;
313 if (debugOptions & EDebugOpIntermediate)
314 intermediate.outputTree(parseContext.treeRoot);
daniel@transgaming.combb2e9632010-04-13 19:53:50 +0000315 } else if (!parseContext.treeRoot) {
316 parseContext.error(1, "Unexpected end of file.", "", "");
317 parseContext.infoSink.info << parseContext.numErrors << " compilation errors. No code generated.\n\n";
318 success = false;
319 if (debugOptions & EDebugOpIntermediate)
320 intermediate.outputTree(parseContext.treeRoot);
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000321 }
322
323 intermediate.remove(parseContext.treeRoot);
324
325 //
326 // Ensure symbol table is returned to the built-in level,
327 // throwing away all but the built-ins.
328 //
329 while (! symbolTable.atSharedBuiltInLevel())
330 symbolTable.pop();
331
332 FinalizePreprocessor();
333 //
334 // Throw away all the temporary memory used by the compilation process.
335 //
336 GlobalPoolAllocator.pop();
337
338 return success ? 1 : 0;
339}
340
341//
342// Do an actual link on the given compile objects.
343//
344// Return: The return value of is really boolean, indicating
345// success or failure.
346//
347int ShLink(
348 const ShHandle linkHandle,
349 const ShHandle compHandles[],
350 const int numHandles,
351 ShHandle uniformMapHandle,
352 short int** uniformsAccessed,
353 int* numUniformsAccessed)
354
355{
356 if (!InitThread())
357 return 0;
358
359 TShHandleBase* base = reinterpret_cast<TShHandleBase*>(linkHandle);
360 TLinker* linker = static_cast<TLinker*>(base->getAsLinker());
361 if (linker == 0)
362 return 0;
363
364 int returnValue;
365 GlobalPoolAllocator.push();
366 returnValue = ShLinkExt(linkHandle, compHandles, numHandles);
367 GlobalPoolAllocator.pop();
368
369 if (returnValue)
370 return 1;
371
372 return 0;
373}
374//
375// This link method will be eventually used once the ICD supports the new linker interface
376//
377int ShLinkExt(
378 const ShHandle linkHandle,
379 const ShHandle compHandles[],
380 const int numHandles)
381{
382 if (linkHandle == 0 || numHandles == 0)
383 return 0;
384
385 THandleList cObjects;
386
387 {// support MSVC++6.0
388 for (int i = 0; i < numHandles; ++i) {
389 if (compHandles[i] == 0)
390 return 0;
391 TShHandleBase* base = reinterpret_cast<TShHandleBase*>(compHandles[i]);
392 if (base->getAsLinker()) {
393 cObjects.push_back(base->getAsLinker());
394 }
395 if (base->getAsCompiler())
396 cObjects.push_back(base->getAsCompiler());
397
398
399 if (cObjects[i] == 0)
400 return 0;
401 }
402 }
403
404 TShHandleBase* base = reinterpret_cast<TShHandleBase*>(linkHandle);
405 TLinker* linker = static_cast<TLinker*>(base->getAsLinker());
406
407 if (linker == 0)
408 return 0;
409
410 linker->infoSink.info.erase();
411 linker->infoSink.obj.erase();
412
413 {// support MSVC++6.0
414 for (int i = 0; i < numHandles; ++i) {
415 if (cObjects[i]->getAsCompiler()) {
416 if (! cObjects[i]->getAsCompiler()->linkable()) {
417 linker->infoSink.info.message(EPrefixError, "Not all shaders have valid object code.");
418 return 0;
419 }
420 }
421 }
422 }
423
424 bool ret = linker->link(cObjects);
425
426 return ret ? 1 : 0;
427}
428
429//
430// ShSetEncrpytionMethod is a place-holder for specifying
431// how source code is encrypted.
432//
433void ShSetEncryptionMethod(ShHandle handle)
434{
435 if (handle == 0)
436 return;
437}
438
439//
440// Return any compiler/linker/uniformmap log of messages for the application.
441//
442const char* ShGetInfoLog(const ShHandle handle)
443{
444 if (!InitThread())
445 return 0;
446
447 if (handle == 0)
448 return 0;
449
450 TShHandleBase* base = static_cast<TShHandleBase*>(handle);
451 TInfoSink* infoSink;
452
453 if (base->getAsCompiler())
454 infoSink = &(base->getAsCompiler()->getInfoSink());
455 else if (base->getAsLinker())
456 infoSink = &(base->getAsLinker()->getInfoSink());
457
458 infoSink->info << infoSink->debug.c_str();
459 return infoSink->info.c_str();
460}
461
462//
463// Return any unlinked object code.
464//
465const char* ShGetObjectCode(const ShHandle handle)
466{
467 if (!InitThread())
468 return 0;
469
470 if (handle == 0)
471 return 0;
472
473 TShHandleBase* base = static_cast<TShHandleBase*>(handle);
474 TInfoSink* infoSink;
475
476 if (base->getAsCompiler())
477 infoSink = &(base->getAsCompiler()->getInfoSink());
478
479 return infoSink->obj.c_str();
480}
481
482//
483// Return the resulting binary code from the link process. Structure
484// is machine dependent.
485//
486const void* ShGetExecutable(const ShHandle handle)
487{
488 if (!InitThread())
489 return 0;
490
491 if (handle == 0)
492 return 0;
493
494 TShHandleBase* base = reinterpret_cast<TShHandleBase*>(handle);
495
496 TLinker* linker = static_cast<TLinker*>(base->getAsLinker());
497 if (linker == 0)
498 return 0;
499
500 return linker->getObjectCode();
501}
502
503//
504// Let the linker know where the application said it's attributes are bound.
505// The linker does not use these values, they are remapped by the ICD or
506// hardware. It just needs them to know what's aliased.
507//
508// Return: The return value of is really boolean, indicating
509// success or failure.
510//
511int ShSetVirtualAttributeBindings(const ShHandle handle, const ShBindingTable* table)
512{
513 if (!InitThread())
514 return 0;
515
516 if (handle == 0)
517 return 0;
518
519 TShHandleBase* base = reinterpret_cast<TShHandleBase*>(handle);
520 TLinker* linker = static_cast<TLinker*>(base->getAsLinker());
521
522 if (linker == 0)
523 return 0;
524
525 linker->setAppAttributeBindings(table);
526
527 return 1;
528}
529
530//
531// Let the linker know where the predefined attributes have to live.
532//
533int ShSetFixedAttributeBindings(const ShHandle handle, const ShBindingTable* table)
534{
535 if (!InitThread())
536 return 0;
537
538 if (handle == 0)
539 return 0;
540
541 TShHandleBase* base = reinterpret_cast<TShHandleBase*>(handle);
542 TLinker* linker = static_cast<TLinker*>(base->getAsLinker());
543
544 if (linker == 0)
545 return 0;
546
547 linker->setFixedAttributeBindings(table);
548 return 1;
549}
550
551//
552// Some attribute locations are off-limits to the linker...
553//
554int ShExcludeAttributes(const ShHandle handle, int *attributes, int count)
555{
556 if (!InitThread())
557 return 0;
558
559 if (handle == 0)
560 return 0;
561
562 TShHandleBase* base = reinterpret_cast<TShHandleBase*>(handle);
563 TLinker* linker = static_cast<TLinker*>(base->getAsLinker());
564 if (linker == 0)
565 return 0;
566
567 linker->setExcludedAttributes(attributes, count);
568
569 return 1;
570}
571
572//
573// Return the index for OpenGL to use for knowing where a uniform lives.
574//
575// Return: The return value of is really boolean, indicating
576// success or failure.
577//
578int ShGetUniformLocation(const ShHandle handle, const char* name)
579{
580 if (!InitThread())
581 return 0;
582
583 if (handle == 0)
584 return -1;
585
586 TShHandleBase* base = reinterpret_cast<TShHandleBase*>(handle);
587 TUniformMap* uniformMap= base->getAsUniformMap();
588 if (uniformMap == 0)
589 return -1;
590
591 return uniformMap->getLocation(name);
592}
593