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