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