blob: 57dddf5d286c9c6801085c6d97d0d2084ba93947 [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//
alokp@chromium.org774d7062010-07-21 18:55:45 +00008// Implement the top-level of interface to the compiler,
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +00009// 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
alokp@chromium.org774d7062010-07-21 18:55:45 +000031// and the shading language compiler.
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +000032//
33
34//
35// Driver must call this first, once, before doing any other
alokp@chromium.org774d7062010-07-21 18:55:45 +000036// compiler operations.
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +000037//
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//
alokp@chromium.org774d7062010-07-21 18:55:45 +000081// Driver calls these to create and destroy compiler objects.
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +000082//
83
alokp@chromium.org29cd91a2010-07-16 19:30:45 +000084ShHandle ShConstructCompiler(EShLanguage language, EShSpec spec)
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +000085{
86 if (!InitThread())
87 return 0;
88
alokp@chromium.org29cd91a2010-07-16 19:30:45 +000089 TShHandleBase* base = static_cast<TShHandleBase*>(ConstructCompiler(language, spec));
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +000090
91 return reinterpret_cast<void*>(base);
92}
93
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +000094void ShDestruct(ShHandle handle)
95{
96 if (handle == 0)
97 return;
98
99 TShHandleBase* base = static_cast<TShHandleBase*>(handle);
100
101 if (base->getAsCompiler())
102 DeleteCompiler(base->getAsCompiler());
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000103}
104
105//
106// Cleanup symbol tables
107//
108int __fastcall ShFinalize()
109{
110 if (PerProcessGPA) {
111 PerProcessGPA->popAll();
112 delete PerProcessGPA;
alokp@chromium.org90033b92010-05-24 15:02:43 +0000113 PerProcessGPA = 0;
114 }
115 for (int i = 0; i < EShLangCount; ++i) {
116 delete SymbolTables[i];
117 SymbolTables[i] = 0;
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000118 }
119 return 1;
120}
121
122bool GenerateBuiltInSymbolTable(const TBuiltInResource* resources, TInfoSink& infoSink, TSymbolTable* symbolTables, EShLanguage language)
123{
124 TBuiltIns builtIns;
125
126 if (resources) {
127 builtIns.initialize(*resources);
128 InitializeSymbolTable(builtIns.getBuiltInStrings(), language, infoSink, resources, symbolTables);
129 } else {
130 builtIns.initialize();
131 InitializeSymbolTable(builtIns.getBuiltInStrings(), EShLangVertex, infoSink, resources, symbolTables);
132 InitializeSymbolTable(builtIns.getBuiltInStrings(), EShLangFragment, infoSink, resources, symbolTables);
133 }
134
135 return true;
136}
137
138bool InitializeSymbolTable(TBuiltInStrings* BuiltInStrings, EShLanguage language, TInfoSink& infoSink, const TBuiltInResource* resources, TSymbolTable* symbolTables)
139{
140 TIntermediate intermediate(infoSink);
141 TSymbolTable* symbolTable;
142
143 if (resources)
144 symbolTable = symbolTables;
145 else
146 symbolTable = &symbolTables[language];
147
alokp@chromium.org613ef312010-07-21 18:54:22 +0000148 // TODO(alokp): Investigate if a parse-context is necessary here and
149 // if symbol-table can be shared between GLES2 and WebGL specs.
150 TParseContext parseContext(*symbolTable, intermediate, language, EShSpecGLES2, infoSink);
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000151
152 GlobalParseContext = &parseContext;
153
154 setInitialState();
155
156 assert(symbolTable->isEmpty() || symbolTable->atSharedBuiltInLevel());
157
158 //
159 // Parse the built-ins. This should only happen once per
160 // language symbol table.
161 //
162 // Push the symbol table to give it an initial scope. This
163 // push should not have a corresponding pop, so that built-ins
164 // are preserved, and the test for an empty table fails.
165 //
166
167 symbolTable->push();
168
169 //Initialize the Preprocessor
170 int ret = InitPreprocessor();
171 if (ret) {
172 infoSink.info.message(EPrefixInternalError, "Unable to intialize the Preprocessor");
173 return false;
174 }
175
176 for (TBuiltInStrings::iterator i = BuiltInStrings[parseContext.language].begin();
177 i != BuiltInStrings[parseContext.language].end();
178 ++i) {
179 const char* builtInShaders[1];
180 int builtInLengths[1];
181
182 builtInShaders[0] = (*i).c_str();
183 builtInLengths[0] = (int) (*i).size();
184
185 if (PaParseStrings(const_cast<char**>(builtInShaders), builtInLengths, 1, parseContext) != 0) {
186 infoSink.info.message(EPrefixInternalError, "Unable to parse built-ins");
187 return false;
188 }
189 }
190
191 if (resources) {
192 IdentifyBuiltIns(parseContext.language, *symbolTable, *resources);
193 } else {
194 IdentifyBuiltIns(parseContext.language, *symbolTable);
195 }
196
197 FinalizePreprocessor();
198
199 return true;
200}
201
202//
203// Do an actual compile on the given strings. The result is left
204// in the given compile object.
205//
206// Return: The return value of ShCompile is really boolean, indicating
207// success or failure.
208//
209int ShCompile(
210 const ShHandle handle,
211 const char* const shaderStrings[],
212 const int numStrings,
213 const EShOptimizationLevel optLevel,
214 const TBuiltInResource* resources,
215 int debugOptions
216 )
217{
218 if (!InitThread())
219 return 0;
220
221 if (handle == 0)
222 return 0;
223
224 TShHandleBase* base = reinterpret_cast<TShHandleBase*>(handle);
225 TCompiler* compiler = base->getAsCompiler();
226 if (compiler == 0)
227 return 0;
228
229 GlobalPoolAllocator.push();
alokp@chromium.org76b82082010-03-24 17:59:39 +0000230 TInfoSink& infoSink = compiler->infoSink;
231 infoSink.info.erase();
232 infoSink.debug.erase();
233 infoSink.obj.erase();
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000234
235 if (numStrings == 0)
236 return 1;
237
alokp@chromium.org76b82082010-03-24 17:59:39 +0000238 TIntermediate intermediate(infoSink);
alokp@chromium.org90033b92010-05-24 15:02:43 +0000239 TSymbolTable symbolTable(*SymbolTables[compiler->getLanguage()]);
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000240
alokp@chromium.org76b82082010-03-24 17:59:39 +0000241 GenerateBuiltInSymbolTable(resources, infoSink, &symbolTable, compiler->getLanguage());
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000242
alokp@chromium.org613ef312010-07-21 18:54:22 +0000243 TParseContext parseContext(symbolTable, intermediate, compiler->getLanguage(), compiler->getSpec(), infoSink);
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000244 parseContext.initializeExtensionBehavior();
245
246 GlobalParseContext = &parseContext;
247
248 setInitialState();
249
250 InitPreprocessor();
251 //
252 // Parse the application's shaders. All the following symbol table
253 // work will be throw-away, so push a new allocation scope that can
254 // be thrown away, then push a scope for the current shader's globals.
255 //
256 bool success = true;
257
258 symbolTable.push();
259 if (!symbolTable.atGlobalLevel())
260 parseContext.infoSink.info.message(EPrefixInternalError, "Wrong symbol table level");
261
262 int ret = PaParseStrings(const_cast<char**>(shaderStrings), 0, numStrings, parseContext);
263 if (ret)
264 success = false;
265
266 if (success && parseContext.treeRoot) {
267 if (optLevel == EShOptNoGeneration)
alokp@chromium.org774d7062010-07-21 18:55:45 +0000268 parseContext.infoSink.info.message(EPrefixNone, "No errors. No code generation was requested.");
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000269 else {
270 success = intermediate.postProcess(parseContext.treeRoot, parseContext.language);
271
272 if (success) {
273
274 if (debugOptions & EDebugOpIntermediate)
275 intermediate.outputTree(parseContext.treeRoot);
276
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000277 //
278 // Call the machine dependent compiler
279 //
alokp@chromium.org76b82082010-03-24 17:59:39 +0000280 if (!compiler->compile(parseContext.treeRoot))
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000281 success = false;
282 }
283 }
284 } else if (!success) {
285 parseContext.infoSink.info.prefix(EPrefixError);
286 parseContext.infoSink.info << parseContext.numErrors << " compilation errors. No code generated.\n\n";
287 success = false;
288 if (debugOptions & EDebugOpIntermediate)
289 intermediate.outputTree(parseContext.treeRoot);
daniel@transgaming.combb2e9632010-04-13 19:53:50 +0000290 } else if (!parseContext.treeRoot) {
291 parseContext.error(1, "Unexpected end of file.", "", "");
292 parseContext.infoSink.info << parseContext.numErrors << " compilation errors. No code generated.\n\n";
293 success = false;
294 if (debugOptions & EDebugOpIntermediate)
295 intermediate.outputTree(parseContext.treeRoot);
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000296 }
297
298 intermediate.remove(parseContext.treeRoot);
299
300 //
301 // Ensure symbol table is returned to the built-in level,
302 // throwing away all but the built-ins.
303 //
304 while (! symbolTable.atSharedBuiltInLevel())
305 symbolTable.pop();
306
307 FinalizePreprocessor();
308 //
309 // Throw away all the temporary memory used by the compilation process.
310 //
311 GlobalPoolAllocator.pop();
312
313 return success ? 1 : 0;
314}
315
316//
alokp@chromium.org774d7062010-07-21 18:55:45 +0000317// Return any compiler log of messages for the application.
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000318//
319const char* ShGetInfoLog(const ShHandle handle)
320{
321 if (!InitThread())
322 return 0;
323
324 if (handle == 0)
325 return 0;
326
327 TShHandleBase* base = static_cast<TShHandleBase*>(handle);
alokp@chromium.org774d7062010-07-21 18:55:45 +0000328 TInfoSink* infoSink = 0;
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000329
330 if (base->getAsCompiler())
331 infoSink = &(base->getAsCompiler()->getInfoSink());
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000332
333 infoSink->info << infoSink->debug.c_str();
334 return infoSink->info.c_str();
335}
336
337//
alokp@chromium.org774d7062010-07-21 18:55:45 +0000338// Return any object code.
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000339//
340const char* ShGetObjectCode(const ShHandle handle)
341{
342 if (!InitThread())
343 return 0;
344
345 if (handle == 0)
346 return 0;
347
348 TShHandleBase* base = static_cast<TShHandleBase*>(handle);
349 TInfoSink* infoSink;
350
351 if (base->getAsCompiler())
352 infoSink = &(base->getAsCompiler()->getInfoSink());
353
354 return infoSink->obj.c_str();
355}