blob: 6995594e7606d9b59d3457d1c4f7bc3a069737f3 [file] [log] [blame]
zmo@google.com5601ea02011-06-10 18:23:25 +00001//
Jamie Madill02f20dd2013-09-12 12:07:42 -04002// Copyright (c) 2002-2013 The ANGLE Project Authors. All rights reserved.
zmo@google.com5601ea02011-06-10 18:23:25 +00003// Use of this source code is governed by a BSD-style license that can be
4// found in the LICENSE file.
5//
6
Geoff Lang17732822013-08-29 13:46:49 -04007#include "compiler/translator/OutputGLSLBase.h"
8#include "compiler/translator/compilerdebug.h"
zmo@google.com5601ea02011-06-10 18:23:25 +00009
daniel@transgaming.com773ff742013-01-11 04:12:51 +000010#include <cfloat>
daniel@transgaming.com6c1203f2013-01-11 04:12:43 +000011
zmo@google.com5601ea02011-06-10 18:23:25 +000012namespace
13{
Zhenyao Mo9eedea02014-05-12 16:02:35 -070014TString arrayBrackets(const TType &type)
zmo@google.com5601ea02011-06-10 18:23:25 +000015{
16 ASSERT(type.isArray());
17 TInfoSinkBase out;
18 out << "[" << type.getArraySize() << "]";
19 return TString(out.c_str());
20}
21
Zhenyao Mo9eedea02014-05-12 16:02:35 -070022bool isSingleStatement(TIntermNode *node)
23{
24 if (const TIntermAggregate *aggregate = node->getAsAggregate())
zmo@google.com5601ea02011-06-10 18:23:25 +000025 {
26 return (aggregate->getOp() != EOpFunction) &&
27 (aggregate->getOp() != EOpSequence);
28 }
Zhenyao Mo9eedea02014-05-12 16:02:35 -070029 else if (const TIntermSelection *selection = node->getAsSelectionNode())
zmo@google.com5601ea02011-06-10 18:23:25 +000030 {
31 // Ternary operators are usually part of an assignment operator.
32 // This handles those rare cases in which they are all by themselves.
33 return selection->usesTernaryOperator();
34 }
35 else if (node->getAsLoopNode())
36 {
37 return false;
38 }
39 return true;
40}
41} // namespace
42
Zhenyao Mo9eedea02014-05-12 16:02:35 -070043TOutputGLSLBase::TOutputGLSLBase(TInfoSinkBase &objSink,
shannon.woods@transgaming.com1d432bb2013-01-25 21:57:28 +000044 ShArrayIndexClampingStrategy clampingStrategy,
daniel@transgaming.com0aa3b5a2012-11-28 19:43:24 +000045 ShHashFunction64 hashFunction,
Zhenyao Mo9eedea02014-05-12 16:02:35 -070046 NameMap &nameMap,
47 TSymbolTable &symbolTable,
Jamie Madill02f20dd2013-09-12 12:07:42 -040048 int shaderVersion)
zmo@google.com5601ea02011-06-10 18:23:25 +000049 : TIntermTraverser(true, true, true),
50 mObjSink(objSink),
daniel@transgaming.com0aa3b5a2012-11-28 19:43:24 +000051 mDeclaringVariables(false),
shannon.woods@transgaming.com1d432bb2013-01-25 21:57:28 +000052 mClampingStrategy(clampingStrategy),
daniel@transgaming.com0aa3b5a2012-11-28 19:43:24 +000053 mHashFunction(hashFunction),
54 mNameMap(nameMap),
Jamie Madill02f20dd2013-09-12 12:07:42 -040055 mSymbolTable(symbolTable),
56 mShaderVersion(shaderVersion)
zmo@google.com5601ea02011-06-10 18:23:25 +000057{
Zhenyao Mo904a9162014-05-09 14:07:45 -070058 // Set up global scope.
59 mDeclaredStructs.push_back(ScopedDeclaredStructs());
zmo@google.com5601ea02011-06-10 18:23:25 +000060}
61
Zhenyao Mo9eedea02014-05-12 16:02:35 -070062void TOutputGLSLBase::writeTriplet(
63 Visit visit, const char *preStr, const char *inStr, const char *postStr)
zmo@google.com5601ea02011-06-10 18:23:25 +000064{
Zhenyao Mo9eedea02014-05-12 16:02:35 -070065 TInfoSinkBase &out = objSink();
zmo@google.com5601ea02011-06-10 18:23:25 +000066 if (visit == PreVisit && preStr)
zmo@google.com5601ea02011-06-10 18:23:25 +000067 out << preStr;
zmo@google.com5601ea02011-06-10 18:23:25 +000068 else if (visit == InVisit && inStr)
zmo@google.com5601ea02011-06-10 18:23:25 +000069 out << inStr;
zmo@google.com5601ea02011-06-10 18:23:25 +000070 else if (visit == PostVisit && postStr)
zmo@google.com5601ea02011-06-10 18:23:25 +000071 out << postStr;
zmo@google.com5601ea02011-06-10 18:23:25 +000072}
73
Zhenyao Mo9eedea02014-05-12 16:02:35 -070074void TOutputGLSLBase::writeBuiltInFunctionTriplet(
75 Visit visit, const char *preStr, bool useEmulatedFunction)
zmo@google.com5601ea02011-06-10 18:23:25 +000076{
Zhenyao Mo9eedea02014-05-12 16:02:35 -070077 TString preString = useEmulatedFunction ?
78 BuiltInFunctionEmulator::GetEmulatedFunctionName(preStr) : preStr;
79 writeTriplet(visit, preString.c_str(), ", ", ")");
80}
81
82void TOutputGLSLBase::writeVariableType(const TType &type)
83{
84 TInfoSinkBase &out = objSink();
zmo@google.com5601ea02011-06-10 18:23:25 +000085 TQualifier qualifier = type.getQualifier();
86 // TODO(alokp): Validate qualifier for variable declarations.
Zhenyao Mo9eedea02014-05-12 16:02:35 -070087 if (qualifier != EvqTemporary && qualifier != EvqGlobal)
zmo@google.com5601ea02011-06-10 18:23:25 +000088 out << type.getQualifierString() << " ";
89 // Declare the struct if we have not done so already.
Zhenyao Mo9eedea02014-05-12 16:02:35 -070090 if (type.getBasicType() == EbtStruct && !structDeclared(type.getStruct()))
zmo@google.com5601ea02011-06-10 18:23:25 +000091 {
Jamie Madill98493dd2013-07-08 14:39:03 -040092 declareStruct(type.getStruct());
Zhenyao Mo904a9162014-05-09 14:07:45 -070093 mDeclaredStructs[mDeclaredStructs.size() - 1].push_back(type.getStruct());
zmo@google.com5601ea02011-06-10 18:23:25 +000094 }
95 else
96 {
97 if (writeVariablePrecision(type.getPrecision()))
98 out << " ";
99 out << getTypeName(type);
100 }
101}
102
Zhenyao Mo9eedea02014-05-12 16:02:35 -0700103void TOutputGLSLBase::writeFunctionParameters(const TIntermSequence &args)
zmo@google.com5601ea02011-06-10 18:23:25 +0000104{
Zhenyao Mo9eedea02014-05-12 16:02:35 -0700105 TInfoSinkBase &out = objSink();
zmo@google.com5601ea02011-06-10 18:23:25 +0000106 for (TIntermSequence::const_iterator iter = args.begin();
107 iter != args.end(); ++iter)
108 {
Zhenyao Mo9eedea02014-05-12 16:02:35 -0700109 const TIntermSymbol *arg = (*iter)->getAsSymbolNode();
zmo@google.com5601ea02011-06-10 18:23:25 +0000110 ASSERT(arg != NULL);
111
Zhenyao Mo9eedea02014-05-12 16:02:35 -0700112 const TType &type = arg->getType();
zmo@google.com189be2f2011-06-16 18:28:53 +0000113 writeVariableType(type);
zmo@google.com5601ea02011-06-10 18:23:25 +0000114
Zhenyao Mo9eedea02014-05-12 16:02:35 -0700115 const TString &name = arg->getSymbol();
zmo@google.com5601ea02011-06-10 18:23:25 +0000116 if (!name.empty())
daniel@transgaming.com0aa3b5a2012-11-28 19:43:24 +0000117 out << " " << hashName(name);
zmo@google.com5601ea02011-06-10 18:23:25 +0000118 if (type.isArray())
119 out << arrayBrackets(type);
120
121 // Put a comma if this is not the last argument.
122 if (iter != args.end() - 1)
123 out << ", ";
124 }
125}
126
Zhenyao Mo9eedea02014-05-12 16:02:35 -0700127const ConstantUnion *TOutputGLSLBase::writeConstantUnion(
128 const TType &type, const ConstantUnion *pConstUnion)
zmo@google.com5601ea02011-06-10 18:23:25 +0000129{
Zhenyao Mo9eedea02014-05-12 16:02:35 -0700130 TInfoSinkBase &out = objSink();
zmo@google.com5601ea02011-06-10 18:23:25 +0000131
132 if (type.getBasicType() == EbtStruct)
133 {
Zhenyao Mo9eedea02014-05-12 16:02:35 -0700134 const TStructure *structure = type.getStruct();
Jamie Madill98493dd2013-07-08 14:39:03 -0400135 out << hashName(structure->name()) << "(";
136
Zhenyao Mo9eedea02014-05-12 16:02:35 -0700137 const TFieldList &fields = structure->fields();
Jamie Madill98493dd2013-07-08 14:39:03 -0400138 for (size_t i = 0; i < fields.size(); ++i)
zmo@google.com5601ea02011-06-10 18:23:25 +0000139 {
Zhenyao Mo9eedea02014-05-12 16:02:35 -0700140 const TType *fieldType = fields[i]->type();
zmo@google.com5601ea02011-06-10 18:23:25 +0000141 ASSERT(fieldType != NULL);
142 pConstUnion = writeConstantUnion(*fieldType, pConstUnion);
Zhenyao Mo9eedea02014-05-12 16:02:35 -0700143 if (i != fields.size() - 1)
144 out << ", ";
zmo@google.com5601ea02011-06-10 18:23:25 +0000145 }
146 out << ")";
147 }
148 else
149 {
Jamie Madill94bf7f22013-07-08 13:31:15 -0400150 size_t size = type.getObjectSize();
zmo@google.com5601ea02011-06-10 18:23:25 +0000151 bool writeType = size > 1;
Zhenyao Mo9eedea02014-05-12 16:02:35 -0700152 if (writeType)
153 out << getTypeName(type) << "(";
Jamie Madill94bf7f22013-07-08 13:31:15 -0400154 for (size_t i = 0; i < size; ++i, ++pConstUnion)
zmo@google.com5601ea02011-06-10 18:23:25 +0000155 {
156 switch (pConstUnion->getType())
157 {
Zhenyao Mo9eedea02014-05-12 16:02:35 -0700158 case EbtFloat:
159 out << std::min(FLT_MAX, std::max(-FLT_MAX, pConstUnion->getFConst()));
160 break;
161 case EbtInt:
162 out << pConstUnion->getIConst();
163 break;
164 case EbtBool:
165 out << pConstUnion->getBConst();
166 break;
167 default: UNREACHABLE();
zmo@google.com5601ea02011-06-10 18:23:25 +0000168 }
Zhenyao Mo9eedea02014-05-12 16:02:35 -0700169 if (i != size - 1)
170 out << ", ";
zmo@google.com5601ea02011-06-10 18:23:25 +0000171 }
Zhenyao Mo9eedea02014-05-12 16:02:35 -0700172 if (writeType)
173 out << ")";
zmo@google.com5601ea02011-06-10 18:23:25 +0000174 }
175 return pConstUnion;
176}
177
Zhenyao Mo9eedea02014-05-12 16:02:35 -0700178void TOutputGLSLBase::visitSymbol(TIntermSymbol *node)
zmo@google.com5601ea02011-06-10 18:23:25 +0000179{
Zhenyao Mo9eedea02014-05-12 16:02:35 -0700180 TInfoSinkBase &out = objSink();
Zhenyao Mo550c6002014-02-26 15:40:48 -0800181 if (mLoopUnrollStack.needsToReplaceSymbolWithValue(node))
182 out << mLoopUnrollStack.getLoopIndexValue(node);
zmo@google.com5601ea02011-06-10 18:23:25 +0000183 else
daniel@transgaming.com0aa3b5a2012-11-28 19:43:24 +0000184 out << hashVariableName(node->getSymbol());
zmo@google.com5601ea02011-06-10 18:23:25 +0000185
186 if (mDeclaringVariables && node->getType().isArray())
187 out << arrayBrackets(node->getType());
188}
189
Zhenyao Mo9eedea02014-05-12 16:02:35 -0700190void TOutputGLSLBase::visitConstantUnion(TIntermConstantUnion *node)
zmo@google.com5601ea02011-06-10 18:23:25 +0000191{
192 writeConstantUnion(node->getType(), node->getUnionArrayPointer());
193}
194
Zhenyao Mo9eedea02014-05-12 16:02:35 -0700195bool TOutputGLSLBase::visitBinary(Visit visit, TIntermBinary *node)
zmo@google.com5601ea02011-06-10 18:23:25 +0000196{
197 bool visitChildren = true;
Zhenyao Mo9eedea02014-05-12 16:02:35 -0700198 TInfoSinkBase &out = objSink();
zmo@google.com5601ea02011-06-10 18:23:25 +0000199 switch (node->getOp())
200 {
Zhenyao Mo9eedea02014-05-12 16:02:35 -0700201 case EOpInitialize:
202 if (visit == InVisit)
203 {
204 out << " = ";
205 // RHS of initialize is not being declared.
206 mDeclaringVariables = false;
207 }
208 break;
209 case EOpAssign:
210 writeTriplet(visit, "(", " = ", ")");
211 break;
212 case EOpAddAssign:
213 writeTriplet(visit, "(", " += ", ")");
214 break;
215 case EOpSubAssign:
216 writeTriplet(visit, "(", " -= ", ")");
217 break;
218 case EOpDivAssign:
219 writeTriplet(visit, "(", " /= ", ")");
220 break;
221 // Notice the fall-through.
222 case EOpMulAssign:
223 case EOpVectorTimesMatrixAssign:
224 case EOpVectorTimesScalarAssign:
225 case EOpMatrixTimesScalarAssign:
226 case EOpMatrixTimesMatrixAssign:
227 writeTriplet(visit, "(", " *= ", ")");
228 break;
229
230 case EOpIndexDirect:
231 writeTriplet(visit, NULL, "[", "]");
232 break;
233 case EOpIndexIndirect:
234 if (node->getAddIndexClamp())
235 {
zmo@google.com5601ea02011-06-10 18:23:25 +0000236 if (visit == InVisit)
237 {
Zhenyao Mo9eedea02014-05-12 16:02:35 -0700238 if (mClampingStrategy == SH_CLAMP_WITH_CLAMP_INTRINSIC)
239 out << "[int(clamp(float(";
240 else
241 out << "[webgl_int_clamp(";
zmo@google.com5601ea02011-06-10 18:23:25 +0000242 }
Zhenyao Mo9eedea02014-05-12 16:02:35 -0700243 else if (visit == PostVisit)
244 {
245 int maxSize;
246 TIntermTyped *left = node->getLeft();
247 TType leftType = left->getType();
zmo@google.com5601ea02011-06-10 18:23:25 +0000248
Zhenyao Mo9eedea02014-05-12 16:02:35 -0700249 if (left->isArray())
250 {
251 // The shader will fail validation if the array length is not > 0.
252 maxSize = leftType.getArraySize() - 1;
253 }
254 else
255 {
256 maxSize = leftType.getNominalSize() - 1;
257 }
258
259 if (mClampingStrategy == SH_CLAMP_WITH_CLAMP_INTRINSIC)
260 out << "), 0.0, float(" << maxSize << ")))]";
261 else
262 out << ", 0, " << maxSize << ")]";
263 }
264 }
265 else
266 {
zmo@google.com5601ea02011-06-10 18:23:25 +0000267 writeTriplet(visit, NULL, "[", "]");
Zhenyao Mo9eedea02014-05-12 16:02:35 -0700268 }
269 break;
270 case EOpIndexDirectStruct:
271 if (visit == InVisit)
272 {
273 // Here we are writing out "foo.bar", where "foo" is struct
274 // and "bar" is field. In AST, it is represented as a binary
275 // node, where left child represents "foo" and right child "bar".
276 // The node itself represents ".". The struct field "bar" is
277 // actually stored as an index into TStructure::fields.
278 out << ".";
279 const TStructure *structure = node->getLeft()->getType().getStruct();
280 const TIntermConstantUnion *index = node->getRight()->getAsConstantUnion();
281 const TField *field = structure->fields()[index->getIConst(0)];
282
283 TString fieldName = field->name();
284 if (!mSymbolTable.findBuiltIn(structure->name(), mShaderVersion))
285 fieldName = hashName(fieldName);
286
287 out << fieldName;
288 visitChildren = false;
289 }
290 break;
291 case EOpVectorSwizzle:
292 if (visit == InVisit)
293 {
294 out << ".";
295 TIntermAggregate *rightChild = node->getRight()->getAsAggregate();
296 TIntermSequence &sequence = rightChild->getSequence();
297 for (TIntermSequence::iterator sit = sequence.begin(); sit != sequence.end(); ++sit)
daniel@transgaming.com4167cc92013-01-11 04:11:53 +0000298 {
Zhenyao Mo9eedea02014-05-12 16:02:35 -0700299 TIntermConstantUnion *element = (*sit)->getAsConstantUnion();
300 ASSERT(element->getBasicType() == EbtInt);
301 ASSERT(element->getNominalSize() == 1);
302 const ConstantUnion& data = element->getUnionArrayPointer()[0];
303 ASSERT(data.getType() == EbtInt);
304 switch (data.getIConst())
daniel@transgaming.com4167cc92013-01-11 04:11:53 +0000305 {
Zhenyao Mo9eedea02014-05-12 16:02:35 -0700306 case 0:
307 out << "x";
308 break;
309 case 1:
310 out << "y";
311 break;
312 case 2:
313 out << "z";
314 break;
315 case 3:
316 out << "w";
317 break;
318 default:
319 UNREACHABLE();
daniel@transgaming.com4167cc92013-01-11 04:11:53 +0000320 }
321 }
Zhenyao Mo9eedea02014-05-12 16:02:35 -0700322 visitChildren = false;
323 }
324 break;
daniel@transgaming.com97b16d12013-02-01 03:20:42 +0000325
Zhenyao Mo9eedea02014-05-12 16:02:35 -0700326 case EOpAdd:
327 writeTriplet(visit, "(", " + ", ")");
328 break;
329 case EOpSub:
330 writeTriplet(visit, "(", " - ", ")");
331 break;
332 case EOpMul:
333 writeTriplet(visit, "(", " * ", ")");
334 break;
335 case EOpDiv:
336 writeTriplet(visit, "(", " / ", ")");
337 break;
338 case EOpMod:
339 UNIMPLEMENTED();
340 break;
341 case EOpEqual:
342 writeTriplet(visit, "(", " == ", ")");
343 break;
344 case EOpNotEqual:
345 writeTriplet(visit, "(", " != ", ")");
346 break;
347 case EOpLessThan:
348 writeTriplet(visit, "(", " < ", ")");
349 break;
350 case EOpGreaterThan:
351 writeTriplet(visit, "(", " > ", ")");
352 break;
353 case EOpLessThanEqual:
354 writeTriplet(visit, "(", " <= ", ")");
355 break;
356 case EOpGreaterThanEqual:
357 writeTriplet(visit, "(", " >= ", ")");
358 break;
daniel@transgaming.com97b16d12013-02-01 03:20:42 +0000359
Zhenyao Mo9eedea02014-05-12 16:02:35 -0700360 // Notice the fall-through.
361 case EOpVectorTimesScalar:
362 case EOpVectorTimesMatrix:
363 case EOpMatrixTimesVector:
364 case EOpMatrixTimesScalar:
365 case EOpMatrixTimesMatrix:
366 writeTriplet(visit, "(", " * ", ")");
367 break;
zmo@google.com5601ea02011-06-10 18:23:25 +0000368
Zhenyao Mo9eedea02014-05-12 16:02:35 -0700369 case EOpLogicalOr:
370 writeTriplet(visit, "(", " || ", ")");
371 break;
372 case EOpLogicalXor:
373 writeTriplet(visit, "(", " ^^ ", ")");
374 break;
375 case EOpLogicalAnd:
376 writeTriplet(visit, "(", " && ", ")");
377 break;
378 default:
379 UNREACHABLE();
zmo@google.com5601ea02011-06-10 18:23:25 +0000380 }
381
382 return visitChildren;
383}
384
Zhenyao Mo9eedea02014-05-12 16:02:35 -0700385bool TOutputGLSLBase::visitUnary(Visit visit, TIntermUnary *node)
zmo@google.com5601ea02011-06-10 18:23:25 +0000386{
zmo@google.com32e97312011-08-24 01:03:11 +0000387 TString preString;
388 TString postString = ")";
389
zmo@google.com5601ea02011-06-10 18:23:25 +0000390 switch (node->getOp())
391 {
Zhenyao Mo9eedea02014-05-12 16:02:35 -0700392 case EOpNegative: preString = "(-"; break;
393 case EOpVectorLogicalNot: preString = "not("; break;
394 case EOpLogicalNot: preString = "(!"; break;
zmo@google.com5601ea02011-06-10 18:23:25 +0000395
Zhenyao Mo9eedea02014-05-12 16:02:35 -0700396 case EOpPostIncrement: preString = "("; postString = "++)"; break;
397 case EOpPostDecrement: preString = "("; postString = "--)"; break;
398 case EOpPreIncrement: preString = "(++"; break;
399 case EOpPreDecrement: preString = "(--"; break;
zmo@google.com5601ea02011-06-10 18:23:25 +0000400
Zhenyao Mo9eedea02014-05-12 16:02:35 -0700401 case EOpConvIntToBool:
402 case EOpConvFloatToBool:
403 switch (node->getOperand()->getType().getNominalSize())
404 {
405 case 1:
406 preString = "bool(";
zmo@google.com5601ea02011-06-10 18:23:25 +0000407 break;
Zhenyao Mo9eedea02014-05-12 16:02:35 -0700408 case 2:
409 preString = "bvec2(";
zmo@google.com5601ea02011-06-10 18:23:25 +0000410 break;
Zhenyao Mo9eedea02014-05-12 16:02:35 -0700411 case 3:
412 preString = "bvec3(";
zmo@google.com5601ea02011-06-10 18:23:25 +0000413 break;
Zhenyao Mo9eedea02014-05-12 16:02:35 -0700414 case 4:
415 preString = "bvec4(";
416 break;
417 default:
418 UNREACHABLE();
419 }
420 break;
421 case EOpConvBoolToFloat:
422 case EOpConvIntToFloat:
423 switch (node->getOperand()->getType().getNominalSize())
424 {
425 case 1:
426 preString = "float(";
427 break;
428 case 2:
429 preString = "vec2(";
430 break;
431 case 3:
432 preString = "vec3(";
433 break;
434 case 4:
435 preString = "vec4(";
436 break;
437 default:
438 UNREACHABLE();
439 }
440 break;
441 case EOpConvFloatToInt:
442 case EOpConvBoolToInt:
443 switch (node->getOperand()->getType().getNominalSize())
444 {
445 case 1:
446 preString = "int(";
447 break;
448 case 2:
449 preString = "ivec2(";
450 break;
451 case 3:
452 preString = "ivec3(";
453 break;
454 case 4:
455 preString = "ivec4(";
456 break;
457 default:
458 UNREACHABLE();
459 }
460 break;
zmo@google.com5601ea02011-06-10 18:23:25 +0000461
Zhenyao Mo9eedea02014-05-12 16:02:35 -0700462 case EOpRadians:
463 preString = "radians(";
464 break;
465 case EOpDegrees:
466 preString = "degrees(";
467 break;
468 case EOpSin:
469 preString = "sin(";
470 break;
471 case EOpCos:
472 preString = "cos(";
473 break;
474 case EOpTan:
475 preString = "tan(";
476 break;
477 case EOpAsin:
478 preString = "asin(";
479 break;
480 case EOpAcos:
481 preString = "acos(";
482 break;
483 case EOpAtan:
484 preString = "atan(";
485 break;
zmo@google.com5601ea02011-06-10 18:23:25 +0000486
Zhenyao Mo9eedea02014-05-12 16:02:35 -0700487 case EOpExp:
488 preString = "exp(";
489 break;
490 case EOpLog:
491 preString = "log(";
492 break;
493 case EOpExp2:
494 preString = "exp2(";
495 break;
496 case EOpLog2:
497 preString = "log2(";
498 break;
499 case EOpSqrt:
500 preString = "sqrt(";
501 break;
502 case EOpInverseSqrt:
503 preString = "inversesqrt(";
504 break;
zmo@google.com5601ea02011-06-10 18:23:25 +0000505
Zhenyao Mo9eedea02014-05-12 16:02:35 -0700506 case EOpAbs:
507 preString = "abs(";
508 break;
509 case EOpSign:
510 preString = "sign(";
511 break;
512 case EOpFloor:
513 preString = "floor(";
514 break;
515 case EOpCeil:
516 preString = "ceil(";
517 break;
518 case EOpFract:
519 preString = "fract(";
520 break;
zmo@google.com5601ea02011-06-10 18:23:25 +0000521
Zhenyao Mo9eedea02014-05-12 16:02:35 -0700522 case EOpLength:
523 preString = "length(";
524 break;
525 case EOpNormalize:
526 preString = "normalize(";
527 break;
zmo@google.com5601ea02011-06-10 18:23:25 +0000528
Zhenyao Mo9eedea02014-05-12 16:02:35 -0700529 case EOpDFdx:
530 preString = "dFdx(";
531 break;
532 case EOpDFdy:
533 preString = "dFdy(";
534 break;
535 case EOpFwidth:
536 preString = "fwidth(";
537 break;
zmo@google.com5601ea02011-06-10 18:23:25 +0000538
Zhenyao Mo9eedea02014-05-12 16:02:35 -0700539 case EOpAny:
540 preString = "any(";
541 break;
542 case EOpAll:
543 preString = "all(";
544 break;
zmo@google.com5601ea02011-06-10 18:23:25 +0000545
Zhenyao Mo9eedea02014-05-12 16:02:35 -0700546 default:
547 UNREACHABLE();
zmo@google.com5601ea02011-06-10 18:23:25 +0000548 }
549
zmo@google.com32e97312011-08-24 01:03:11 +0000550 if (visit == PreVisit && node->getUseEmulatedFunction())
551 preString = BuiltInFunctionEmulator::GetEmulatedFunctionName(preString);
552 writeTriplet(visit, preString.c_str(), NULL, postString.c_str());
553
zmo@google.com5601ea02011-06-10 18:23:25 +0000554 return true;
555}
556
Zhenyao Mo9eedea02014-05-12 16:02:35 -0700557bool TOutputGLSLBase::visitSelection(Visit visit, TIntermSelection *node)
zmo@google.com5601ea02011-06-10 18:23:25 +0000558{
Zhenyao Mo9eedea02014-05-12 16:02:35 -0700559 TInfoSinkBase &out = objSink();
zmo@google.com5601ea02011-06-10 18:23:25 +0000560
561 if (node->usesTernaryOperator())
562 {
563 // Notice two brackets at the beginning and end. The outer ones
564 // encapsulate the whole ternary expression. This preserves the
565 // order of precedence when ternary expressions are used in a
566 // compound expression, i.e., c = 2 * (a < b ? 1 : 2).
567 out << "((";
568 node->getCondition()->traverse(this);
569 out << ") ? (";
570 node->getTrueBlock()->traverse(this);
571 out << ") : (";
572 node->getFalseBlock()->traverse(this);
573 out << "))";
574 }
575 else
576 {
577 out << "if (";
578 node->getCondition()->traverse(this);
579 out << ")\n";
580
Zhenyao Mo7cab38b2013-10-15 12:59:30 -0700581 incrementDepth(node);
zmo@google.com5601ea02011-06-10 18:23:25 +0000582 visitCodeBlock(node->getTrueBlock());
583
584 if (node->getFalseBlock())
585 {
586 out << "else\n";
587 visitCodeBlock(node->getFalseBlock());
588 }
589 decrementDepth();
590 }
591 return false;
592}
593
Zhenyao Mo9eedea02014-05-12 16:02:35 -0700594bool TOutputGLSLBase::visitAggregate(Visit visit, TIntermAggregate *node)
zmo@google.com5601ea02011-06-10 18:23:25 +0000595{
596 bool visitChildren = true;
Zhenyao Mo9eedea02014-05-12 16:02:35 -0700597 TInfoSinkBase &out = objSink();
zmo@google.comf420c422011-09-12 18:27:59 +0000598 TString preString;
Zhenyao Mo9eedea02014-05-12 16:02:35 -0700599 bool useEmulatedFunction = (visit == PreVisit && node->getUseEmulatedFunction());
zmo@google.com5601ea02011-06-10 18:23:25 +0000600 switch (node->getOp())
601 {
Zhenyao Mo9eedea02014-05-12 16:02:35 -0700602 case EOpSequence:
603 // Scope the sequences except when at the global scope.
604 if (depth > 0)
605 {
606 out << "{\n";
607 pushDeclaredStructsScope();
zmo@google.com5601ea02011-06-10 18:23:25 +0000608 }
zmo@google.com5601ea02011-06-10 18:23:25 +0000609
Zhenyao Mo9eedea02014-05-12 16:02:35 -0700610 incrementDepth(node);
611 for (TIntermSequence::const_iterator iter = node->getSequence().begin();
612 iter != node->getSequence().end(); ++iter)
613 {
614 TIntermNode *node = *iter;
615 ASSERT(node != NULL);
616 node->traverse(this);
617
618 if (isSingleStatement(node))
619 out << ";\n";
620 }
621 decrementDepth();
622
623 // Scope the sequences except when at the global scope.
624 if (depth > 0)
625 {
626 popDeclaredStructsScope();
627 out << "}\n";
628 }
629 visitChildren = false;
630 break;
631 case EOpPrototype:
632 // Function declaration.
633 ASSERT(visit == PreVisit);
634 writeVariableType(node->getType());
635 out << " " << hashName(node->getName());
636
637 out << "(";
638 writeFunctionParameters(node->getSequence());
639 out << ")";
640
641 visitChildren = false;
642 break;
643 case EOpFunction: {
644 // Function definition.
645 ASSERT(visit == PreVisit);
646 writeVariableType(node->getType());
647 out << " " << hashFunctionName(node->getName());
648
649 incrementDepth(node);
650 // Function definition node contains one or two children nodes
651 // representing function parameters and function body. The latter
652 // is not present in case of empty function bodies.
653 const TIntermSequence &sequence = node->getSequence();
654 ASSERT((sequence.size() == 1) || (sequence.size() == 2));
655 TIntermSequence::const_iterator seqIter = sequence.begin();
656
657 // Traverse function parameters.
658 TIntermAggregate *params = (*seqIter)->getAsAggregate();
659 ASSERT(params != NULL);
660 ASSERT(params->getOp() == EOpParameters);
661 params->traverse(this);
662
663 // Traverse function body.
664 TIntermAggregate *body = ++seqIter != sequence.end() ?
665 (*seqIter)->getAsAggregate() : NULL;
666 visitCodeBlock(body);
667 decrementDepth();
668
669 // Fully processed; no need to visit children.
670 visitChildren = false;
671 break;
672 }
673 case EOpFunctionCall:
674 // Function call.
675 if (visit == PreVisit)
676 out << hashFunctionName(node->getName()) << "(";
677 else if (visit == InVisit)
678 out << ", ";
679 else
zmo@google.com5601ea02011-06-10 18:23:25 +0000680 out << ")";
Zhenyao Mo9eedea02014-05-12 16:02:35 -0700681 break;
682 case EOpParameters:
683 // Function parameters.
684 ASSERT(visit == PreVisit);
685 out << "(";
686 writeFunctionParameters(node->getSequence());
687 out << ")";
688 visitChildren = false;
689 break;
690 case EOpDeclaration:
691 // Variable declaration.
692 if (visit == PreVisit)
693 {
694 const TIntermSequence &sequence = node->getSequence();
695 const TIntermTyped *variable = sequence.front()->getAsTyped();
696 writeVariableType(variable->getType());
697 out << " ";
698 mDeclaringVariables = true;
zmo@google.com5601ea02011-06-10 18:23:25 +0000699 }
Zhenyao Mo9eedea02014-05-12 16:02:35 -0700700 else if (visit == InVisit)
701 {
702 out << ", ";
703 mDeclaringVariables = true;
zmo@google.com5601ea02011-06-10 18:23:25 +0000704 }
Zhenyao Mo9eedea02014-05-12 16:02:35 -0700705 else
706 {
707 mDeclaringVariables = false;
708 }
709 break;
710 case EOpConstructFloat:
711 writeTriplet(visit, "float(", NULL, ")");
712 break;
713 case EOpConstructVec2:
714 writeBuiltInFunctionTriplet(visit, "vec2(", false);
715 break;
716 case EOpConstructVec3:
717 writeBuiltInFunctionTriplet(visit, "vec3(", false);
718 break;
719 case EOpConstructVec4:
720 writeBuiltInFunctionTriplet(visit, "vec4(", false);
721 break;
722 case EOpConstructBool:
723 writeTriplet(visit, "bool(", NULL, ")");
724 break;
725 case EOpConstructBVec2:
726 writeBuiltInFunctionTriplet(visit, "bvec2(", false);
727 break;
728 case EOpConstructBVec3:
729 writeBuiltInFunctionTriplet(visit, "bvec3(", false);
730 break;
731 case EOpConstructBVec4:
732 writeBuiltInFunctionTriplet(visit, "bvec4(", false);
733 break;
734 case EOpConstructInt:
735 writeTriplet(visit, "int(", NULL, ")");
736 break;
737 case EOpConstructIVec2:
738 writeBuiltInFunctionTriplet(visit, "ivec2(", false);
739 break;
740 case EOpConstructIVec3:
741 writeBuiltInFunctionTriplet(visit, "ivec3(", false);
742 break;
743 case EOpConstructIVec4:
744 writeBuiltInFunctionTriplet(visit, "ivec4(", false);
745 break;
746 case EOpConstructMat2:
747 writeBuiltInFunctionTriplet(visit, "mat2(", false);
748 break;
749 case EOpConstructMat3:
750 writeBuiltInFunctionTriplet(visit, "mat3(", false);
751 break;
752 case EOpConstructMat4:
753 writeBuiltInFunctionTriplet(visit, "mat4(", false);
754 break;
755 case EOpConstructStruct:
756 if (visit == PreVisit)
757 {
758 const TType &type = node->getType();
759 ASSERT(type.getBasicType() == EbtStruct);
760 out << hashName(type.getStruct()->name()) << "(";
761 }
762 else if (visit == InVisit)
763 {
764 out << ", ";
765 }
766 else
767 {
zmo@google.com5601ea02011-06-10 18:23:25 +0000768 out << ")";
zmo@google.com5601ea02011-06-10 18:23:25 +0000769 }
Zhenyao Mo9eedea02014-05-12 16:02:35 -0700770 break;
zmo@google.com5601ea02011-06-10 18:23:25 +0000771
Zhenyao Mo9eedea02014-05-12 16:02:35 -0700772 case EOpLessThan:
773 writeBuiltInFunctionTriplet(visit, "lessThan(", useEmulatedFunction);
774 break;
775 case EOpGreaterThan:
776 writeBuiltInFunctionTriplet(visit, "greaterThan(", useEmulatedFunction);
777 break;
778 case EOpLessThanEqual:
779 writeBuiltInFunctionTriplet(visit, "lessThanEqual(", useEmulatedFunction);
780 break;
781 case EOpGreaterThanEqual:
782 writeBuiltInFunctionTriplet(visit, "greaterThanEqual(", useEmulatedFunction);
783 break;
784 case EOpVectorEqual:
785 writeBuiltInFunctionTriplet(visit, "equal(", useEmulatedFunction);
786 break;
787 case EOpVectorNotEqual:
788 writeBuiltInFunctionTriplet(visit, "notEqual(", useEmulatedFunction);
789 break;
790 case EOpComma:
791 writeTriplet(visit, NULL, ", ", NULL);
792 break;
zmo@google.com5601ea02011-06-10 18:23:25 +0000793
Zhenyao Mo9eedea02014-05-12 16:02:35 -0700794 case EOpMod:
795 writeBuiltInFunctionTriplet(visit, "mod(", useEmulatedFunction);
796 break;
797 case EOpPow:
798 writeBuiltInFunctionTriplet(visit, "pow(", useEmulatedFunction);
799 break;
800 case EOpAtan:
801 writeBuiltInFunctionTriplet(visit, "atan(", useEmulatedFunction);
802 break;
803 case EOpMin:
804 writeBuiltInFunctionTriplet(visit, "min(", useEmulatedFunction);
805 break;
806 case EOpMax:
807 writeBuiltInFunctionTriplet(visit, "max(", useEmulatedFunction);
808 break;
809 case EOpClamp:
810 writeBuiltInFunctionTriplet(visit, "clamp(", useEmulatedFunction);
811 break;
812 case EOpMix:
813 writeBuiltInFunctionTriplet(visit, "mix(", useEmulatedFunction);
814 break;
815 case EOpStep:
816 writeBuiltInFunctionTriplet(visit, "step(", useEmulatedFunction);
817 break;
818 case EOpSmoothStep:
819 writeBuiltInFunctionTriplet(visit, "smoothstep(", useEmulatedFunction);
820 break;
821 case EOpDistance:
822 writeBuiltInFunctionTriplet(visit, "distance(", useEmulatedFunction);
823 break;
824 case EOpDot:
825 writeBuiltInFunctionTriplet(visit, "dot(", useEmulatedFunction);
826 break;
827 case EOpCross:
828 writeBuiltInFunctionTriplet(visit, "cross(", useEmulatedFunction);
829 break;
830 case EOpFaceForward:
831 writeBuiltInFunctionTriplet(visit, "faceforward(", useEmulatedFunction);
832 break;
833 case EOpReflect:
834 writeBuiltInFunctionTriplet(visit, "reflect(", useEmulatedFunction);
835 break;
836 case EOpRefract:
837 writeBuiltInFunctionTriplet(visit, "refract(", useEmulatedFunction);
838 break;
839 case EOpMul:
840 writeBuiltInFunctionTriplet(visit, "matrixCompMult(", useEmulatedFunction);
841 break;
zmo@google.com5601ea02011-06-10 18:23:25 +0000842
Zhenyao Mo9eedea02014-05-12 16:02:35 -0700843 default:
844 UNREACHABLE();
zmo@google.com5601ea02011-06-10 18:23:25 +0000845 }
zmo@google.com5601ea02011-06-10 18:23:25 +0000846 return visitChildren;
847}
848
Zhenyao Mo9eedea02014-05-12 16:02:35 -0700849bool TOutputGLSLBase::visitLoop(Visit visit, TIntermLoop *node)
zmo@google.com5601ea02011-06-10 18:23:25 +0000850{
Zhenyao Mo9eedea02014-05-12 16:02:35 -0700851 TInfoSinkBase &out = objSink();
zmo@google.com5601ea02011-06-10 18:23:25 +0000852
Zhenyao Mo7cab38b2013-10-15 12:59:30 -0700853 incrementDepth(node);
zmo@google.com5601ea02011-06-10 18:23:25 +0000854 // Loop header.
855 TLoopType loopType = node->getType();
856 if (loopType == ELoopFor) // for loop
857 {
Zhenyao Mo550c6002014-02-26 15:40:48 -0800858 if (!node->getUnrollFlag())
859 {
zmo@google.com5601ea02011-06-10 18:23:25 +0000860 out << "for (";
861 if (node->getInit())
862 node->getInit()->traverse(this);
863 out << "; ";
864
865 if (node->getCondition())
866 node->getCondition()->traverse(this);
867 out << "; ";
868
869 if (node->getExpression())
870 node->getExpression()->traverse(this);
871 out << ")\n";
872 }
Zhenyao Mo550c6002014-02-26 15:40:48 -0800873 else
874 {
875 // Need to put a one-iteration loop here to handle break.
876 TIntermSequence &declSeq =
877 node->getInit()->getAsAggregate()->getSequence();
878 TIntermSymbol *indexSymbol =
879 declSeq[0]->getAsBinaryNode()->getLeft()->getAsSymbolNode();
880 TString name = hashVariableName(indexSymbol->getSymbol());
881 out << "for (int " << name << " = 0; "
882 << name << " < 1; "
883 << "++" << name << ")\n";
884 }
zmo@google.com5601ea02011-06-10 18:23:25 +0000885 }
886 else if (loopType == ELoopWhile) // while loop
887 {
888 out << "while (";
889 ASSERT(node->getCondition() != NULL);
890 node->getCondition()->traverse(this);
891 out << ")\n";
892 }
893 else // do-while loop
894 {
895 ASSERT(loopType == ELoopDoWhile);
896 out << "do\n";
897 }
898
899 // Loop body.
900 if (node->getUnrollFlag())
901 {
Zhenyao Mo550c6002014-02-26 15:40:48 -0800902 out << "{\n";
903 mLoopUnrollStack.push(node);
904 while (mLoopUnrollStack.satisfiesLoopCondition())
zmo@google.com5601ea02011-06-10 18:23:25 +0000905 {
906 visitCodeBlock(node->getBody());
Zhenyao Mo550c6002014-02-26 15:40:48 -0800907 mLoopUnrollStack.step();
zmo@google.com5601ea02011-06-10 18:23:25 +0000908 }
Zhenyao Mo550c6002014-02-26 15:40:48 -0800909 mLoopUnrollStack.pop();
910 out << "}\n";
zmo@google.com5601ea02011-06-10 18:23:25 +0000911 }
912 else
913 {
914 visitCodeBlock(node->getBody());
915 }
916
917 // Loop footer.
918 if (loopType == ELoopDoWhile) // do-while loop
919 {
920 out << "while (";
921 ASSERT(node->getCondition() != NULL);
922 node->getCondition()->traverse(this);
923 out << ");\n";
924 }
925 decrementDepth();
926
927 // No need to visit children. They have been already processed in
928 // this function.
929 return false;
930}
931
Zhenyao Mo9eedea02014-05-12 16:02:35 -0700932bool TOutputGLSLBase::visitBranch(Visit visit, TIntermBranch *node)
zmo@google.com5601ea02011-06-10 18:23:25 +0000933{
934 switch (node->getFlowOp())
935 {
Zhenyao Mo9eedea02014-05-12 16:02:35 -0700936 case EOpKill:
937 writeTriplet(visit, "discard", NULL, NULL);
938 break;
939 case EOpBreak:
940 writeTriplet(visit, "break", NULL, NULL);
941 break;
942 case EOpContinue:
943 writeTriplet(visit, "continue", NULL, NULL);
944 break;
945 case EOpReturn:
946 writeTriplet(visit, "return ", NULL, NULL);
947 break;
948 default:
949 UNREACHABLE();
zmo@google.com5601ea02011-06-10 18:23:25 +0000950 }
951
952 return true;
953}
954
Zhenyao Mo9eedea02014-05-12 16:02:35 -0700955void TOutputGLSLBase::visitCodeBlock(TIntermNode *node)
956{
zmo@google.com5601ea02011-06-10 18:23:25 +0000957 TInfoSinkBase &out = objSink();
958 if (node != NULL)
959 {
960 node->traverse(this);
961 // Single statements not part of a sequence need to be terminated
962 // with semi-colon.
963 if (isSingleStatement(node))
964 out << ";\n";
965 }
966 else
967 {
968 out << "{\n}\n"; // Empty code block.
969 }
970}
daniel@transgaming.com0aa3b5a2012-11-28 19:43:24 +0000971
Zhenyao Mo9eedea02014-05-12 16:02:35 -0700972TString TOutputGLSLBase::getTypeName(const TType &type)
daniel@transgaming.com0aa3b5a2012-11-28 19:43:24 +0000973{
974 TInfoSinkBase out;
975 if (type.isMatrix())
976 {
977 out << "mat";
978 out << type.getNominalSize();
979 }
980 else if (type.isVector())
981 {
982 switch (type.getBasicType())
983 {
Zhenyao Mo9eedea02014-05-12 16:02:35 -0700984 case EbtFloat:
985 out << "vec";
986 break;
987 case EbtInt:
988 out << "ivec";
989 break;
990 case EbtBool:
991 out << "bvec";
992 break;
993 default:
994 UNREACHABLE();
daniel@transgaming.com0aa3b5a2012-11-28 19:43:24 +0000995 }
996 out << type.getNominalSize();
997 }
998 else
999 {
1000 if (type.getBasicType() == EbtStruct)
Jamie Madill98493dd2013-07-08 14:39:03 -04001001 out << hashName(type.getStruct()->name());
daniel@transgaming.com0aa3b5a2012-11-28 19:43:24 +00001002 else
1003 out << type.getBasicString();
1004 }
1005 return TString(out.c_str());
1006}
1007
Zhenyao Mo9eedea02014-05-12 16:02:35 -07001008TString TOutputGLSLBase::hashName(const TString &name)
daniel@transgaming.com0aa3b5a2012-11-28 19:43:24 +00001009{
1010 if (mHashFunction == NULL || name.empty())
1011 return name;
1012 NameMap::const_iterator it = mNameMap.find(name.c_str());
1013 if (it != mNameMap.end())
1014 return it->second.c_str();
1015 TString hashedName = TIntermTraverser::hash(name, mHashFunction);
1016 mNameMap[name.c_str()] = hashedName.c_str();
1017 return hashedName;
1018}
1019
Zhenyao Mo9eedea02014-05-12 16:02:35 -07001020TString TOutputGLSLBase::hashVariableName(const TString &name)
daniel@transgaming.com0aa3b5a2012-11-28 19:43:24 +00001021{
Jamie Madill02f20dd2013-09-12 12:07:42 -04001022 if (mSymbolTable.findBuiltIn(name, mShaderVersion) != NULL)
daniel@transgaming.com0aa3b5a2012-11-28 19:43:24 +00001023 return name;
1024 return hashName(name);
1025}
1026
Zhenyao Mo9eedea02014-05-12 16:02:35 -07001027TString TOutputGLSLBase::hashFunctionName(const TString &mangled_name)
daniel@transgaming.com0aa3b5a2012-11-28 19:43:24 +00001028{
1029 TString name = TFunction::unmangleName(mangled_name);
Jamie Madill02f20dd2013-09-12 12:07:42 -04001030 if (mSymbolTable.findBuiltIn(mangled_name, mShaderVersion) != NULL || name == "main")
Nicolas Capens46485082014-04-15 13:12:50 -04001031 return translateTextureFunction(name);
daniel@transgaming.com0aa3b5a2012-11-28 19:43:24 +00001032 return hashName(name);
1033}
Jamie Madill98493dd2013-07-08 14:39:03 -04001034
Zhenyao Mo9eedea02014-05-12 16:02:35 -07001035bool TOutputGLSLBase::structDeclared(const TStructure *structure) const
Jamie Madill98493dd2013-07-08 14:39:03 -04001036{
Zhenyao Mo904a9162014-05-09 14:07:45 -07001037 ASSERT(structure);
1038 ASSERT(mDeclaredStructs.size() > 0);
1039 for (size_t ii = mDeclaredStructs.size(); ii > 0; --ii)
1040 {
Zhenyao Mo9eedea02014-05-12 16:02:35 -07001041 const ScopedDeclaredStructs &scope = mDeclaredStructs[ii - 1];
Zhenyao Mo904a9162014-05-09 14:07:45 -07001042 for (size_t jj = 0; jj < scope.size(); ++jj)
1043 {
1044 if (scope[jj]->equals(*structure))
1045 return true;
1046 }
1047 }
1048 return false;
Jamie Madill98493dd2013-07-08 14:39:03 -04001049}
1050
Zhenyao Mo9eedea02014-05-12 16:02:35 -07001051void TOutputGLSLBase::declareStruct(const TStructure *structure)
Jamie Madill98493dd2013-07-08 14:39:03 -04001052{
Zhenyao Mo9eedea02014-05-12 16:02:35 -07001053 TInfoSinkBase &out = objSink();
Jamie Madill98493dd2013-07-08 14:39:03 -04001054
1055 out << "struct " << hashName(structure->name()) << "{\n";
Zhenyao Mo9eedea02014-05-12 16:02:35 -07001056 const TFieldList &fields = structure->fields();
Jamie Madill98493dd2013-07-08 14:39:03 -04001057 for (size_t i = 0; i < fields.size(); ++i)
1058 {
Zhenyao Mo9eedea02014-05-12 16:02:35 -07001059 const TField *field = fields[i];
Jamie Madill98493dd2013-07-08 14:39:03 -04001060 if (writeVariablePrecision(field->type()->getPrecision()))
1061 out << " ";
1062 out << getTypeName(*field->type()) << " " << hashName(field->name());
1063 if (field->type()->isArray())
1064 out << arrayBrackets(*field->type());
1065 out << ";\n";
1066 }
1067 out << "}";
Zhenyao Mo904a9162014-05-09 14:07:45 -07001068}
Jamie Madill98493dd2013-07-08 14:39:03 -04001069
Zhenyao Mo904a9162014-05-09 14:07:45 -07001070void TOutputGLSLBase::pushDeclaredStructsScope()
1071{
1072 mDeclaredStructs.push_back(ScopedDeclaredStructs());
1073}
1074
1075void TOutputGLSLBase::popDeclaredStructsScope()
1076{
1077 // We should never pop the global scope.
1078 ASSERT(mDeclaredStructs.size() >= 2);
1079 mDeclaredStructs.pop_back();
Jamie Madill98493dd2013-07-08 14:39:03 -04001080}