blob: 7f4137d8da83d699ebd8913055965a221c61b223 [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);
daniel@transgaming.combb2e9632010-04-13 19:53:50 +0000306 } else if (!parseContext.treeRoot) {
307 parseContext.error(1, "Unexpected end of file.", "", "");
308 parseContext.infoSink.info << parseContext.numErrors << " compilation errors. No code generated.\n\n";
309 success = false;
310 if (debugOptions & EDebugOpIntermediate)
311 intermediate.outputTree(parseContext.treeRoot);
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000312 }
313
314 intermediate.remove(parseContext.treeRoot);
315
316 //
317 // Ensure symbol table is returned to the built-in level,
318 // throwing away all but the built-ins.
319 //
320 while (! symbolTable.atSharedBuiltInLevel())
321 symbolTable.pop();
322
323 FinalizePreprocessor();
324 //
325 // Throw away all the temporary memory used by the compilation process.
326 //
327 GlobalPoolAllocator.pop();
328
329 return success ? 1 : 0;
330}
331
332//
333// Do an actual link on the given compile objects.
334//
335// Return: The return value of is really boolean, indicating
336// success or failure.
337//
338int ShLink(
339 const ShHandle linkHandle,
340 const ShHandle compHandles[],
341 const int numHandles,
342 ShHandle uniformMapHandle,
343 short int** uniformsAccessed,
344 int* numUniformsAccessed)
345
346{
347 if (!InitThread())
348 return 0;
349
350 TShHandleBase* base = reinterpret_cast<TShHandleBase*>(linkHandle);
351 TLinker* linker = static_cast<TLinker*>(base->getAsLinker());
352 if (linker == 0)
353 return 0;
354
355 int returnValue;
356 GlobalPoolAllocator.push();
357 returnValue = ShLinkExt(linkHandle, compHandles, numHandles);
358 GlobalPoolAllocator.pop();
359
360 if (returnValue)
361 return 1;
362
363 return 0;
364}
365//
366// This link method will be eventually used once the ICD supports the new linker interface
367//
368int ShLinkExt(
369 const ShHandle linkHandle,
370 const ShHandle compHandles[],
371 const int numHandles)
372{
373 if (linkHandle == 0 || numHandles == 0)
374 return 0;
375
376 THandleList cObjects;
377
378 {// support MSVC++6.0
379 for (int i = 0; i < numHandles; ++i) {
380 if (compHandles[i] == 0)
381 return 0;
382 TShHandleBase* base = reinterpret_cast<TShHandleBase*>(compHandles[i]);
383 if (base->getAsLinker()) {
384 cObjects.push_back(base->getAsLinker());
385 }
386 if (base->getAsCompiler())
387 cObjects.push_back(base->getAsCompiler());
388
389
390 if (cObjects[i] == 0)
391 return 0;
392 }
393 }
394
395 TShHandleBase* base = reinterpret_cast<TShHandleBase*>(linkHandle);
396 TLinker* linker = static_cast<TLinker*>(base->getAsLinker());
397
398 if (linker == 0)
399 return 0;
400
401 linker->infoSink.info.erase();
402 linker->infoSink.obj.erase();
403
404 {// support MSVC++6.0
405 for (int i = 0; i < numHandles; ++i) {
406 if (cObjects[i]->getAsCompiler()) {
407 if (! cObjects[i]->getAsCompiler()->linkable()) {
408 linker->infoSink.info.message(EPrefixError, "Not all shaders have valid object code.");
409 return 0;
410 }
411 }
412 }
413 }
414
415 bool ret = linker->link(cObjects);
416
417 return ret ? 1 : 0;
418}
419
420//
421// ShSetEncrpytionMethod is a place-holder for specifying
422// how source code is encrypted.
423//
424void ShSetEncryptionMethod(ShHandle handle)
425{
426 if (handle == 0)
427 return;
428}
429
430//
431// Return any compiler/linker/uniformmap log of messages for the application.
432//
433const char* ShGetInfoLog(const ShHandle handle)
434{
435 if (!InitThread())
436 return 0;
437
438 if (handle == 0)
439 return 0;
440
441 TShHandleBase* base = static_cast<TShHandleBase*>(handle);
442 TInfoSink* infoSink;
443
444 if (base->getAsCompiler())
445 infoSink = &(base->getAsCompiler()->getInfoSink());
446 else if (base->getAsLinker())
447 infoSink = &(base->getAsLinker()->getInfoSink());
448
449 infoSink->info << infoSink->debug.c_str();
450 return infoSink->info.c_str();
451}
452
453//
454// Return any unlinked object code.
455//
456const char* ShGetObjectCode(const ShHandle handle)
457{
458 if (!InitThread())
459 return 0;
460
461 if (handle == 0)
462 return 0;
463
464 TShHandleBase* base = static_cast<TShHandleBase*>(handle);
465 TInfoSink* infoSink;
466
467 if (base->getAsCompiler())
468 infoSink = &(base->getAsCompiler()->getInfoSink());
469
470 return infoSink->obj.c_str();
471}
472
473//
474// Return the resulting binary code from the link process. Structure
475// is machine dependent.
476//
477const void* ShGetExecutable(const ShHandle handle)
478{
479 if (!InitThread())
480 return 0;
481
482 if (handle == 0)
483 return 0;
484
485 TShHandleBase* base = reinterpret_cast<TShHandleBase*>(handle);
486
487 TLinker* linker = static_cast<TLinker*>(base->getAsLinker());
488 if (linker == 0)
489 return 0;
490
491 return linker->getObjectCode();
492}
493
494//
495// Let the linker know where the application said it's attributes are bound.
496// The linker does not use these values, they are remapped by the ICD or
497// hardware. It just needs them to know what's aliased.
498//
499// Return: The return value of is really boolean, indicating
500// success or failure.
501//
502int ShSetVirtualAttributeBindings(const ShHandle handle, const ShBindingTable* table)
503{
504 if (!InitThread())
505 return 0;
506
507 if (handle == 0)
508 return 0;
509
510 TShHandleBase* base = reinterpret_cast<TShHandleBase*>(handle);
511 TLinker* linker = static_cast<TLinker*>(base->getAsLinker());
512
513 if (linker == 0)
514 return 0;
515
516 linker->setAppAttributeBindings(table);
517
518 return 1;
519}
520
521//
522// Let the linker know where the predefined attributes have to live.
523//
524int ShSetFixedAttributeBindings(const ShHandle handle, const ShBindingTable* table)
525{
526 if (!InitThread())
527 return 0;
528
529 if (handle == 0)
530 return 0;
531
532 TShHandleBase* base = reinterpret_cast<TShHandleBase*>(handle);
533 TLinker* linker = static_cast<TLinker*>(base->getAsLinker());
534
535 if (linker == 0)
536 return 0;
537
538 linker->setFixedAttributeBindings(table);
539 return 1;
540}
541
542//
543// Some attribute locations are off-limits to the linker...
544//
545int ShExcludeAttributes(const ShHandle handle, int *attributes, int count)
546{
547 if (!InitThread())
548 return 0;
549
550 if (handle == 0)
551 return 0;
552
553 TShHandleBase* base = reinterpret_cast<TShHandleBase*>(handle);
554 TLinker* linker = static_cast<TLinker*>(base->getAsLinker());
555 if (linker == 0)
556 return 0;
557
558 linker->setExcludedAttributes(attributes, count);
559
560 return 1;
561}
562
563//
564// Return the index for OpenGL to use for knowing where a uniform lives.
565//
566// Return: The return value of is really boolean, indicating
567// success or failure.
568//
569int ShGetUniformLocation(const ShHandle handle, const char* name)
570{
571 if (!InitThread())
572 return 0;
573
574 if (handle == 0)
575 return -1;
576
577 TShHandleBase* base = reinterpret_cast<TShHandleBase*>(handle);
578 TUniformMap* uniformMap= base->getAsUniformMap();
579 if (uniformMap == 0)
580 return -1;
581
582 return uniformMap->getLocation(name);
583}
584