1 //
2 // Copyright 2002 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 #include "compiler/translator/hlsl/OutputHLSL.h"
8
9 #include <stdio.h>
10 #include <algorithm>
11 #include <cfloat>
12
13 #include "common/angleutils.h"
14 #include "common/debug.h"
15 #include "common/utilities.h"
16 #include "compiler/translator/BuiltInFunctionEmulator.h"
17 #include "compiler/translator/InfoSink.h"
18 #include "compiler/translator/StaticType.h"
19 #include "compiler/translator/blocklayout.h"
20 #include "compiler/translator/hlsl/AtomicCounterFunctionHLSL.h"
21 #include "compiler/translator/hlsl/BuiltInFunctionEmulatorHLSL.h"
22 #include "compiler/translator/hlsl/ImageFunctionHLSL.h"
23 #include "compiler/translator/hlsl/ResourcesHLSL.h"
24 #include "compiler/translator/hlsl/StructureHLSL.h"
25 #include "compiler/translator/hlsl/TextureFunctionHLSL.h"
26 #include "compiler/translator/hlsl/TranslatorHLSL.h"
27 #include "compiler/translator/hlsl/UtilsHLSL.h"
28 #include "compiler/translator/tree_ops/hlsl/RemoveSwitchFallThrough.h"
29 #include "compiler/translator/tree_util/FindSymbolNode.h"
30 #include "compiler/translator/tree_util/NodeSearch.h"
31 #include "compiler/translator/util.h"
32
33 namespace sh
34 {
35
36 namespace
37 {
38
39 constexpr const char kImage2DFunctionString[] = "// @@ IMAGE2D DECLARATION FUNCTION STRING @@";
40
ArrayHelperFunctionName(const char * prefix,const TType & type)41 TString ArrayHelperFunctionName(const char *prefix, const TType &type)
42 {
43 TStringStream fnName = sh::InitializeStream<TStringStream>();
44 fnName << prefix << "_";
45 if (type.isArray())
46 {
47 for (unsigned int arraySize : type.getArraySizes())
48 {
49 fnName << arraySize << "_";
50 }
51 }
52 fnName << TypeString(type);
53 return fnName.str();
54 }
55
IsDeclarationWrittenOut(TIntermDeclaration * node)56 bool IsDeclarationWrittenOut(TIntermDeclaration *node)
57 {
58 TIntermSequence *sequence = node->getSequence();
59 TIntermTyped *variable = (*sequence)[0]->getAsTyped();
60 ASSERT(sequence->size() == 1);
61 ASSERT(variable);
62 return (variable->getQualifier() == EvqTemporary || variable->getQualifier() == EvqGlobal ||
63 variable->getQualifier() == EvqConst || variable->getQualifier() == EvqShared);
64 }
65
IsInStd140UniformBlock(TIntermTyped * node)66 bool IsInStd140UniformBlock(TIntermTyped *node)
67 {
68 TIntermBinary *binaryNode = node->getAsBinaryNode();
69
70 if (binaryNode)
71 {
72 return IsInStd140UniformBlock(binaryNode->getLeft());
73 }
74
75 const TType &type = node->getType();
76
77 if (type.getQualifier() == EvqUniform)
78 {
79 // determine if we are in the standard layout
80 const TInterfaceBlock *interfaceBlock = type.getInterfaceBlock();
81 if (interfaceBlock)
82 {
83 return (interfaceBlock->blockStorage() == EbsStd140);
84 }
85 }
86
87 return false;
88 }
89
GetInterfaceBlockOfUniformBlockNearestIndexOperator(TIntermTyped * node)90 const TInterfaceBlock *GetInterfaceBlockOfUniformBlockNearestIndexOperator(TIntermTyped *node)
91 {
92 const TIntermBinary *binaryNode = node->getAsBinaryNode();
93 if (binaryNode)
94 {
95 if (binaryNode->getOp() == EOpIndexDirectInterfaceBlock)
96 {
97 return binaryNode->getLeft()->getType().getInterfaceBlock();
98 }
99 }
100
101 const TIntermSymbol *symbolNode = node->getAsSymbolNode();
102 if (symbolNode)
103 {
104 const TVariable &variable = symbolNode->variable();
105 const TType &variableType = variable.getType();
106
107 if (variableType.getQualifier() == EvqUniform &&
108 variable.symbolType() == SymbolType::UserDefined)
109 {
110 return variableType.getInterfaceBlock();
111 }
112 }
113
114 return nullptr;
115 }
116
GetHLSLAtomicFunctionStringAndLeftParenthesis(TOperator op)117 const char *GetHLSLAtomicFunctionStringAndLeftParenthesis(TOperator op)
118 {
119 switch (op)
120 {
121 case EOpAtomicAdd:
122 return "InterlockedAdd(";
123 case EOpAtomicMin:
124 return "InterlockedMin(";
125 case EOpAtomicMax:
126 return "InterlockedMax(";
127 case EOpAtomicAnd:
128 return "InterlockedAnd(";
129 case EOpAtomicOr:
130 return "InterlockedOr(";
131 case EOpAtomicXor:
132 return "InterlockedXor(";
133 case EOpAtomicExchange:
134 return "InterlockedExchange(";
135 case EOpAtomicCompSwap:
136 return "InterlockedCompareExchange(";
137 default:
138 UNREACHABLE();
139 return "";
140 }
141 }
142
IsAtomicFunctionForSharedVariableDirectAssign(const TIntermBinary & node)143 bool IsAtomicFunctionForSharedVariableDirectAssign(const TIntermBinary &node)
144 {
145 TIntermAggregate *aggregateNode = node.getRight()->getAsAggregate();
146 if (aggregateNode == nullptr)
147 {
148 return false;
149 }
150
151 if (node.getOp() == EOpAssign && BuiltInGroup::IsAtomicMemory(aggregateNode->getOp()))
152 {
153 return !IsInShaderStorageBlock((*aggregateNode->getSequence())[0]->getAsTyped()) &&
154 !IsInShaderStorageBlock(node.getLeft());
155 }
156
157 return false;
158 }
159
160 const char *kZeros = "_ANGLE_ZEROS_";
161 constexpr int kZeroCount = 256;
DefineZeroArray()162 std::string DefineZeroArray()
163 {
164 std::stringstream ss = sh::InitializeStream<std::stringstream>();
165 // For 'static', if the declaration does not include an initializer, the value is set to zero.
166 // https://docs.microsoft.com/en-us/windows/desktop/direct3dhlsl/dx-graphics-hlsl-variable-syntax
167 ss << "static uint " << kZeros << "[" << kZeroCount << "];\n";
168 return ss.str();
169 }
170
GetZeroInitializer(size_t size)171 std::string GetZeroInitializer(size_t size)
172 {
173 std::stringstream ss = sh::InitializeStream<std::stringstream>();
174 size_t quotient = size / kZeroCount;
175 size_t reminder = size % kZeroCount;
176
177 for (size_t i = 0; i < quotient; ++i)
178 {
179 if (i != 0)
180 {
181 ss << ", ";
182 }
183 ss << kZeros;
184 }
185
186 for (size_t i = 0; i < reminder; ++i)
187 {
188 if (quotient != 0 || i != 0)
189 {
190 ss << ", ";
191 }
192 ss << "0";
193 }
194
195 return ss.str();
196 }
197
IsFlatInterpolant(TIntermTyped * node)198 bool IsFlatInterpolant(TIntermTyped *node)
199 {
200 TIntermTyped *interpolant = node->getAsBinaryNode() ? node->getAsBinaryNode()->getLeft() : node;
201 return interpolant->getType().getQualifier() == EvqFlatIn;
202 }
203
204 } // anonymous namespace
205
TReferencedBlock(const TInterfaceBlock * aBlock,const TVariable * aInstanceVariable)206 TReferencedBlock::TReferencedBlock(const TInterfaceBlock *aBlock,
207 const TVariable *aInstanceVariable)
208 : block(aBlock), instanceVariable(aInstanceVariable)
209 {}
210
needStructMapping(TIntermTyped * node)211 bool OutputHLSL::needStructMapping(TIntermTyped *node)
212 {
213 ASSERT(node->getBasicType() == EbtStruct);
214 for (unsigned int n = 0u; getAncestorNode(n) != nullptr; ++n)
215 {
216 TIntermNode *ancestor = getAncestorNode(n);
217 const TIntermBinary *ancestorBinary = ancestor->getAsBinaryNode();
218 if (ancestorBinary)
219 {
220 switch (ancestorBinary->getOp())
221 {
222 case EOpIndexDirectStruct:
223 {
224 const TStructure *structure = ancestorBinary->getLeft()->getType().getStruct();
225 const TIntermConstantUnion *index =
226 ancestorBinary->getRight()->getAsConstantUnion();
227 const TField *field = structure->fields()[index->getIConst(0)];
228 if (field->type()->getStruct() == nullptr)
229 {
230 return false;
231 }
232 break;
233 }
234 case EOpIndexDirect:
235 case EOpIndexIndirect:
236 break;
237 default:
238 return true;
239 }
240 }
241 else
242 {
243 const TIntermAggregate *ancestorAggregate = ancestor->getAsAggregate();
244 if (ancestorAggregate)
245 {
246 return true;
247 }
248 return false;
249 }
250 }
251 return true;
252 }
253
writeFloat(TInfoSinkBase & out,float f)254 void OutputHLSL::writeFloat(TInfoSinkBase &out, float f)
255 {
256 // This is known not to work for NaN on all drivers but make the best effort to output NaNs
257 // regardless.
258 if ((gl::isInf(f) || gl::isNaN(f)) && mShaderVersion >= 300 &&
259 mOutputType == SH_HLSL_4_1_OUTPUT)
260 {
261 out << "asfloat(" << gl::bitCast<uint32_t>(f) << "u)";
262 }
263 else
264 {
265 out << std::min(FLT_MAX, std::max(-FLT_MAX, f));
266 }
267 }
268
writeSingleConstant(TInfoSinkBase & out,const TConstantUnion * const constUnion)269 void OutputHLSL::writeSingleConstant(TInfoSinkBase &out, const TConstantUnion *const constUnion)
270 {
271 ASSERT(constUnion != nullptr);
272 switch (constUnion->getType())
273 {
274 case EbtFloat:
275 writeFloat(out, constUnion->getFConst());
276 break;
277 case EbtInt:
278 out << constUnion->getIConst();
279 break;
280 case EbtUInt:
281 out << constUnion->getUConst();
282 break;
283 case EbtBool:
284 out << constUnion->getBConst();
285 break;
286 default:
287 UNREACHABLE();
288 }
289 }
290
writeConstantUnionArray(TInfoSinkBase & out,const TConstantUnion * const constUnion,const size_t size)291 const TConstantUnion *OutputHLSL::writeConstantUnionArray(TInfoSinkBase &out,
292 const TConstantUnion *const constUnion,
293 const size_t size)
294 {
295 const TConstantUnion *constUnionIterated = constUnion;
296 for (size_t i = 0; i < size; i++, constUnionIterated++)
297 {
298 writeSingleConstant(out, constUnionIterated);
299
300 if (i != size - 1)
301 {
302 out << ", ";
303 }
304 }
305 return constUnionIterated;
306 }
307
OutputHLSL(sh::GLenum shaderType,ShShaderSpec shaderSpec,int shaderVersion,const TExtensionBehavior & extensionBehavior,const char * sourcePath,ShShaderOutput outputType,int numRenderTargets,int maxDualSourceDrawBuffers,const std::vector<ShaderVariable> & uniforms,const ShCompileOptions & compileOptions,sh::WorkGroupSize workGroupSize,TSymbolTable * symbolTable,PerformanceDiagnostics * perfDiagnostics,const std::map<int,const TInterfaceBlock * > & uniformBlockOptimizedMap,const std::vector<InterfaceBlock> & shaderStorageBlocks,uint8_t clipDistanceSize,uint8_t cullDistanceSize,bool isEarlyFragmentTestsSpecified)308 OutputHLSL::OutputHLSL(sh::GLenum shaderType,
309 ShShaderSpec shaderSpec,
310 int shaderVersion,
311 const TExtensionBehavior &extensionBehavior,
312 const char *sourcePath,
313 ShShaderOutput outputType,
314 int numRenderTargets,
315 int maxDualSourceDrawBuffers,
316 const std::vector<ShaderVariable> &uniforms,
317 const ShCompileOptions &compileOptions,
318 sh::WorkGroupSize workGroupSize,
319 TSymbolTable *symbolTable,
320 PerformanceDiagnostics *perfDiagnostics,
321 const std::map<int, const TInterfaceBlock *> &uniformBlockOptimizedMap,
322 const std::vector<InterfaceBlock> &shaderStorageBlocks,
323 uint8_t clipDistanceSize,
324 uint8_t cullDistanceSize,
325 bool isEarlyFragmentTestsSpecified)
326 : TIntermTraverser(true, true, true, symbolTable),
327 mShaderType(shaderType),
328 mShaderSpec(shaderSpec),
329 mShaderVersion(shaderVersion),
330 mExtensionBehavior(extensionBehavior),
331 mSourcePath(sourcePath),
332 mOutputType(outputType),
333 mCompileOptions(compileOptions),
334 mInsideFunction(false),
335 mInsideMain(false),
336 mUniformBlockOptimizedMap(uniformBlockOptimizedMap),
337 mNumRenderTargets(numRenderTargets),
338 mMaxDualSourceDrawBuffers(maxDualSourceDrawBuffers),
339 mCurrentFunctionMetadata(nullptr),
340 mWorkGroupSize(workGroupSize),
341 mPerfDiagnostics(perfDiagnostics),
342 mClipDistanceSize(clipDistanceSize),
343 mCullDistanceSize(cullDistanceSize),
344 mIsEarlyFragmentTestsSpecified(isEarlyFragmentTestsSpecified),
345 mNeedStructMapping(false)
346 {
347 mUsesFragColor = false;
348 mUsesFragData = false;
349 mUsesDepthRange = false;
350 mUsesFragCoord = false;
351 mUsesPointCoord = false;
352 mUsesFrontFacing = false;
353 mUsesHelperInvocation = false;
354 mUsesPointSize = false;
355 mUsesInstanceID = false;
356 mHasMultiviewExtensionEnabled =
357 IsExtensionEnabled(mExtensionBehavior, TExtension::OVR_multiview) ||
358 IsExtensionEnabled(mExtensionBehavior, TExtension::OVR_multiview2);
359 mUsesViewID = false;
360 mUsesVertexID = false;
361 mUsesFragDepth = false;
362 mUsesSampleID = false;
363 mUsesSamplePosition = false;
364 mUsesSampleMaskIn = false;
365 mUsesSampleMask = false;
366 mUsesNumSamples = false;
367 mUsesNumWorkGroups = false;
368 mUsesWorkGroupID = false;
369 mUsesLocalInvocationID = false;
370 mUsesGlobalInvocationID = false;
371 mUsesLocalInvocationIndex = false;
372 mUsesXor = false;
373 mUsesDiscardRewriting = false;
374 mUsesNestedBreak = false;
375 mRequiresIEEEStrictCompiling = false;
376 mUseZeroArray = false;
377 mUsesSecondaryColor = false;
378
379 mDepthLayout = EdUnspecified;
380
381 mUniqueIndex = 0;
382
383 mOutputLod0Function = false;
384 mInsideDiscontinuousLoop = false;
385 mNestedLoopDepth = 0;
386
387 mExcessiveLoopIndex = nullptr;
388
389 mStructureHLSL = new StructureHLSL;
390 mTextureFunctionHLSL = new TextureFunctionHLSL;
391 mImageFunctionHLSL = new ImageFunctionHLSL;
392 mAtomicCounterFunctionHLSL =
393 new AtomicCounterFunctionHLSL(compileOptions.forceAtomicValueResolution);
394
395 unsigned int firstUniformRegister = compileOptions.skipD3DConstantRegisterZero ? 1u : 0u;
396 mResourcesHLSL = new ResourcesHLSL(mStructureHLSL, outputType, uniforms, firstUniformRegister);
397
398 if (mOutputType == SH_HLSL_3_0_OUTPUT)
399 {
400 // Fragment shaders need dx_DepthRange, dx_ViewCoords, dx_DepthFront,
401 // and dx_FragCoordOffset.
402 // Vertex shaders need a slightly different set: dx_DepthRange, dx_ViewCoords and
403 // dx_ViewAdjust.
404 if (mShaderType == GL_VERTEX_SHADER)
405 {
406 mResourcesHLSL->reserveUniformRegisters(3);
407 }
408 else
409 {
410 mResourcesHLSL->reserveUniformRegisters(4);
411 }
412 }
413
414 // Reserve registers for the default uniform block and driver constants
415 mResourcesHLSL->reserveUniformBlockRegisters(2);
416
417 mSSBOOutputHLSL = new ShaderStorageBlockOutputHLSL(this, mResourcesHLSL, shaderStorageBlocks);
418 }
419
~OutputHLSL()420 OutputHLSL::~OutputHLSL()
421 {
422 SafeDelete(mSSBOOutputHLSL);
423 SafeDelete(mStructureHLSL);
424 SafeDelete(mResourcesHLSL);
425 SafeDelete(mTextureFunctionHLSL);
426 SafeDelete(mImageFunctionHLSL);
427 SafeDelete(mAtomicCounterFunctionHLSL);
428 for (auto &eqFunction : mStructEqualityFunctions)
429 {
430 SafeDelete(eqFunction);
431 }
432 for (auto &eqFunction : mArrayEqualityFunctions)
433 {
434 SafeDelete(eqFunction);
435 }
436 }
437
output(TIntermNode * treeRoot,TInfoSinkBase & objSink)438 void OutputHLSL::output(TIntermNode *treeRoot, TInfoSinkBase &objSink)
439 {
440 BuiltInFunctionEmulator builtInFunctionEmulator;
441 InitBuiltInFunctionEmulatorForHLSL(&builtInFunctionEmulator);
442 if (mCompileOptions.emulateIsnanFloatFunction)
443 {
444 InitBuiltInIsnanFunctionEmulatorForHLSLWorkarounds(&builtInFunctionEmulator,
445 mShaderVersion);
446 }
447
448 builtInFunctionEmulator.markBuiltInFunctionsForEmulation(treeRoot);
449
450 // Now that we are done changing the AST, do the analyses need for HLSL generation
451 CallDAG::InitResult success = mCallDag.init(treeRoot, nullptr);
452 ASSERT(success == CallDAG::INITDAG_SUCCESS);
453 mASTMetadataList = CreateASTMetadataHLSL(treeRoot, mCallDag);
454
455 const std::vector<MappedStruct> std140Structs = FlagStd140Structs(treeRoot);
456 // TODO(oetuaho): The std140Structs could be filtered based on which ones actually get used in
457 // the shader code. When we add shader storage blocks we might also consider an alternative
458 // solution, since the struct mapping won't work very well for shader storage blocks.
459
460 // Output the body and footer first to determine what has to go in the header
461 mInfoSinkStack.push(&mBody);
462 treeRoot->traverse(this);
463 mInfoSinkStack.pop();
464
465 mInfoSinkStack.push(&mFooter);
466 mInfoSinkStack.pop();
467
468 mInfoSinkStack.push(&mHeader);
469 header(mHeader, std140Structs, &builtInFunctionEmulator);
470 mInfoSinkStack.pop();
471
472 objSink << mHeader.c_str();
473 objSink << mBody.c_str();
474 objSink << mFooter.c_str();
475
476 builtInFunctionEmulator.cleanup();
477 }
478
getShaderStorageBlockRegisterMap() const479 const std::map<std::string, unsigned int> &OutputHLSL::getShaderStorageBlockRegisterMap() const
480 {
481 return mResourcesHLSL->getShaderStorageBlockRegisterMap();
482 }
483
getUniformBlockRegisterMap() const484 const std::map<std::string, unsigned int> &OutputHLSL::getUniformBlockRegisterMap() const
485 {
486 return mResourcesHLSL->getUniformBlockRegisterMap();
487 }
488
getUniformBlockUseStructuredBufferMap() const489 const std::map<std::string, bool> &OutputHLSL::getUniformBlockUseStructuredBufferMap() const
490 {
491 return mResourcesHLSL->getUniformBlockUseStructuredBufferMap();
492 }
493
getUniformRegisterMap() const494 const std::map<std::string, unsigned int> &OutputHLSL::getUniformRegisterMap() const
495 {
496 return mResourcesHLSL->getUniformRegisterMap();
497 }
498
getReadonlyImage2DRegisterIndex() const499 unsigned int OutputHLSL::getReadonlyImage2DRegisterIndex() const
500 {
501 return mResourcesHLSL->getReadonlyImage2DRegisterIndex();
502 }
503
getImage2DRegisterIndex() const504 unsigned int OutputHLSL::getImage2DRegisterIndex() const
505 {
506 return mResourcesHLSL->getImage2DRegisterIndex();
507 }
508
getUsedImage2DFunctionNames() const509 const std::set<std::string> &OutputHLSL::getUsedImage2DFunctionNames() const
510 {
511 return mImageFunctionHLSL->getUsedImage2DFunctionNames();
512 }
513
structInitializerString(int indent,const TType & type,const TString & name) const514 TString OutputHLSL::structInitializerString(int indent,
515 const TType &type,
516 const TString &name) const
517 {
518 TString init;
519
520 TString indentString;
521 for (int spaces = 0; spaces < indent; spaces++)
522 {
523 indentString += " ";
524 }
525
526 if (type.isArray())
527 {
528 init += indentString + "{\n";
529 for (unsigned int arrayIndex = 0u; arrayIndex < type.getOutermostArraySize(); ++arrayIndex)
530 {
531 TStringStream indexedString = sh::InitializeStream<TStringStream>();
532 indexedString << name << "[" << arrayIndex << "]";
533 TType elementType = type;
534 elementType.toArrayElementType();
535 init += structInitializerString(indent + 1, elementType, indexedString.str());
536 if (arrayIndex < type.getOutermostArraySize() - 1)
537 {
538 init += ",";
539 }
540 init += "\n";
541 }
542 init += indentString + "}";
543 }
544 else if (type.getBasicType() == EbtStruct)
545 {
546 init += indentString + "{\n";
547 const TStructure &structure = *type.getStruct();
548 const TFieldList &fields = structure.fields();
549 for (unsigned int fieldIndex = 0; fieldIndex < fields.size(); fieldIndex++)
550 {
551 const TField &field = *fields[fieldIndex];
552 const TString &fieldName = name + "." + Decorate(field.name());
553 const TType &fieldType = *field.type();
554
555 init += structInitializerString(indent + 1, fieldType, fieldName);
556 if (fieldIndex < fields.size() - 1)
557 {
558 init += ",";
559 }
560 init += "\n";
561 }
562 init += indentString + "}";
563 }
564 else
565 {
566 init += indentString + name;
567 }
568
569 return init;
570 }
571
generateStructMapping(const std::vector<MappedStruct> & std140Structs) const572 TString OutputHLSL::generateStructMapping(const std::vector<MappedStruct> &std140Structs) const
573 {
574 TString mappedStructs;
575
576 for (auto &mappedStruct : std140Structs)
577 {
578 const TInterfaceBlock *interfaceBlock =
579 mappedStruct.blockDeclarator->getType().getInterfaceBlock();
580 TQualifier qualifier = mappedStruct.blockDeclarator->getType().getQualifier();
581 switch (qualifier)
582 {
583 case EvqUniform:
584 if (mReferencedUniformBlocks.count(interfaceBlock->uniqueId().get()) == 0)
585 {
586 continue;
587 }
588 break;
589 case EvqBuffer:
590 continue;
591 default:
592 UNREACHABLE();
593 return mappedStructs;
594 }
595
596 unsigned int instanceCount = 1u;
597 bool isInstanceArray = mappedStruct.blockDeclarator->isArray();
598 if (isInstanceArray)
599 {
600 instanceCount = mappedStruct.blockDeclarator->getOutermostArraySize();
601 }
602
603 for (unsigned int instanceArrayIndex = 0; instanceArrayIndex < instanceCount;
604 ++instanceArrayIndex)
605 {
606 TString originalName;
607 TString mappedName("map");
608
609 if (mappedStruct.blockDeclarator->variable().symbolType() != SymbolType::Empty)
610 {
611 const ImmutableString &instanceName =
612 mappedStruct.blockDeclarator->variable().name();
613 unsigned int instanceStringArrayIndex = GL_INVALID_INDEX;
614 if (isInstanceArray)
615 instanceStringArrayIndex = instanceArrayIndex;
616 TString instanceString = mResourcesHLSL->InterfaceBlockInstanceString(
617 instanceName, instanceStringArrayIndex);
618 originalName += instanceString;
619 mappedName += instanceString;
620 originalName += ".";
621 mappedName += "_";
622 }
623
624 TString fieldName = Decorate(mappedStruct.field->name());
625 originalName += fieldName;
626 mappedName += fieldName;
627
628 TType *structType = mappedStruct.field->type();
629 mappedStructs +=
630 "static " + Decorate(structType->getStruct()->name()) + " " + mappedName;
631
632 if (structType->isArray())
633 {
634 mappedStructs += ArrayString(*mappedStruct.field->type()).data();
635 }
636
637 mappedStructs += " =\n";
638 mappedStructs += structInitializerString(0, *structType, originalName);
639 mappedStructs += ";\n";
640 }
641 }
642 return mappedStructs;
643 }
644
writeReferencedAttributes(TInfoSinkBase & out) const645 void OutputHLSL::writeReferencedAttributes(TInfoSinkBase &out) const
646 {
647 for (const auto &attribute : mReferencedAttributes)
648 {
649 const TType &type = attribute.second->getType();
650 const ImmutableString &name = attribute.second->name();
651
652 out << "static " << TypeString(type) << " " << Decorate(name) << ArrayString(type) << " = "
653 << zeroInitializer(type) << ";\n";
654 }
655 }
656
writeReferencedVaryings(TInfoSinkBase & out) const657 void OutputHLSL::writeReferencedVaryings(TInfoSinkBase &out) const
658 {
659 for (const auto &varying : mReferencedVaryings)
660 {
661 const TType &type = varying.second->getType();
662
663 // Program linking depends on this exact format
664 out << "static " << InterpolationString(type.getQualifier()) << " " << TypeString(type)
665 << " " << DecorateVariableIfNeeded(*varying.second) << ArrayString(type) << " = "
666 << zeroInitializer(type) << ";\n";
667 }
668 }
669
header(TInfoSinkBase & out,const std::vector<MappedStruct> & std140Structs,const BuiltInFunctionEmulator * builtInFunctionEmulator) const670 void OutputHLSL::header(TInfoSinkBase &out,
671 const std::vector<MappedStruct> &std140Structs,
672 const BuiltInFunctionEmulator *builtInFunctionEmulator) const
673 {
674 TString mappedStructs;
675 if (mNeedStructMapping)
676 {
677 mappedStructs = generateStructMapping(std140Structs);
678 }
679
680 // Suppress some common warnings:
681 // 3556 : Integer divides might be much slower, try using uints if possible.
682 // 3571 : The pow(f, e) intrinsic function won't work for negative f, use abs(f) or
683 // conditionally handle negative values if you expect them.
684 out << "#pragma warning( disable: 3556 3571 )\n";
685
686 out << mStructureHLSL->structsHeader();
687
688 mResourcesHLSL->uniformsHeader(out, mOutputType, mReferencedUniforms, mSymbolTable);
689 out << mResourcesHLSL->uniformBlocksHeader(mReferencedUniformBlocks, mUniformBlockOptimizedMap);
690 mSSBOOutputHLSL->writeShaderStorageBlocksHeader(mShaderType, out);
691
692 if (!mEqualityFunctions.empty())
693 {
694 out << "\n// Equality functions\n\n";
695 for (const auto &eqFunction : mEqualityFunctions)
696 {
697 out << eqFunction->functionDefinition << "\n";
698 }
699 }
700 if (!mArrayAssignmentFunctions.empty())
701 {
702 out << "\n// Assignment functions\n\n";
703 for (const auto &assignmentFunction : mArrayAssignmentFunctions)
704 {
705 out << assignmentFunction.functionDefinition << "\n";
706 }
707 }
708 if (!mArrayConstructIntoFunctions.empty())
709 {
710 out << "\n// Array constructor functions\n\n";
711 for (const auto &constructIntoFunction : mArrayConstructIntoFunctions)
712 {
713 out << constructIntoFunction.functionDefinition << "\n";
714 }
715 }
716 if (!mFlatEvaluateFunctions.empty())
717 {
718 out << "\n// Evaluate* functions for flat inputs\n\n";
719 for (const auto &flatEvaluateFunction : mFlatEvaluateFunctions)
720 {
721 out << flatEvaluateFunction.functionDefinition << "\n";
722 }
723 }
724
725 if (mUsesDiscardRewriting)
726 {
727 out << "#define ANGLE_USES_DISCARD_REWRITING\n";
728 }
729
730 if (mUsesNestedBreak)
731 {
732 out << "#define ANGLE_USES_NESTED_BREAK\n";
733 }
734
735 if (mRequiresIEEEStrictCompiling)
736 {
737 out << "#define ANGLE_REQUIRES_IEEE_STRICT_COMPILING\n";
738 }
739
740 out << "#ifdef ANGLE_ENABLE_LOOP_FLATTEN\n"
741 "#define LOOP [loop]\n"
742 "#define FLATTEN [flatten]\n"
743 "#else\n"
744 "#define LOOP\n"
745 "#define FLATTEN\n"
746 "#endif\n";
747
748 // array stride for atomic counter buffers is always 4 per original extension
749 // ARB_shader_atomic_counters and discussion on
750 // https://github.com/KhronosGroup/OpenGL-API/issues/5
751 out << "\n#define ATOMIC_COUNTER_ARRAY_STRIDE 4\n\n";
752
753 if (mUseZeroArray)
754 {
755 out << DefineZeroArray() << "\n";
756 }
757
758 if (mShaderType == GL_FRAGMENT_SHADER)
759 {
760 const bool usingMRTExtension =
761 IsExtensionEnabled(mExtensionBehavior, TExtension::EXT_draw_buffers);
762 const bool usingBFEExtension =
763 IsExtensionEnabled(mExtensionBehavior, TExtension::EXT_blend_func_extended);
764
765 out << "// Varyings\n";
766 writeReferencedVaryings(out);
767 out << "\n";
768
769 if ((IsDesktopGLSpec(mShaderSpec) && mShaderVersion >= 130) ||
770 (!IsDesktopGLSpec(mShaderSpec) && mShaderVersion >= 300))
771 {
772 for (const auto &outputVariable : mReferencedOutputVariables)
773 {
774 const ImmutableString &variableName = outputVariable.second->name();
775 const TType &variableType = outputVariable.second->getType();
776
777 out << "static " << TypeString(variableType) << " out_" << variableName
778 << ArrayString(variableType) << " = " << zeroInitializer(variableType) << ";\n";
779 }
780 }
781 else
782 {
783 const unsigned int numColorValues = usingMRTExtension ? mNumRenderTargets : 1;
784
785 out << "static float4 gl_Color[" << numColorValues
786 << "] =\n"
787 "{\n";
788 for (unsigned int i = 0; i < numColorValues; i++)
789 {
790 out << " float4(0, 0, 0, 0)";
791 if (i + 1 != numColorValues)
792 {
793 out << ",";
794 }
795 out << "\n";
796 }
797
798 out << "};\n";
799
800 if (usingBFEExtension && mUsesSecondaryColor)
801 {
802 out << "static float4 gl_SecondaryColor[" << mMaxDualSourceDrawBuffers
803 << "] = \n"
804 "{\n";
805 for (int i = 0; i < mMaxDualSourceDrawBuffers; i++)
806 {
807 out << " float4(0, 0, 0, 0)";
808 if (i + 1 != mMaxDualSourceDrawBuffers)
809 {
810 out << ",";
811 }
812 out << "\n";
813 }
814 out << "};\n";
815 }
816 }
817
818 if (mUsesViewID)
819 {
820 out << "static uint ViewID_OVR = 0;\n";
821 }
822
823 if (mUsesFragDepth)
824 {
825 out << "static float gl_Depth = 0.0;\n";
826 }
827
828 if (mUsesSampleID)
829 {
830 out << "static int gl_SampleID = 0;\n";
831 }
832
833 if (mUsesSamplePosition)
834 {
835 out << "static float2 gl_SamplePosition = float2(0.0, 0.0);\n";
836 }
837
838 if (mUsesSampleMaskIn)
839 {
840 out << "static int gl_SampleMaskIn[1] = {0};\n";
841 }
842
843 if (mUsesSampleMask)
844 {
845 out << "static int gl_SampleMask[1] = {0};\n";
846 }
847
848 if (mUsesNumSamples)
849 {
850 out << "static int gl_NumSamples = GetRenderTargetSampleCount();\n";
851 }
852
853 if (mUsesFragCoord)
854 {
855 out << "static float4 gl_FragCoord = float4(0, 0, 0, 0);\n";
856 }
857
858 if (mUsesPointCoord)
859 {
860 out << "static float2 gl_PointCoord = float2(0.5, 0.5);\n";
861 }
862
863 if (mUsesFrontFacing)
864 {
865 out << "static bool gl_FrontFacing = false;\n";
866 }
867
868 if (mUsesHelperInvocation)
869 {
870 out << "static bool gl_HelperInvocation = false;\n";
871 }
872
873 out << "\n";
874
875 if (mUsesDepthRange)
876 {
877 out << "struct gl_DepthRangeParameters\n"
878 "{\n"
879 " float near;\n"
880 " float far;\n"
881 " float diff;\n"
882 "};\n"
883 "\n";
884 }
885
886 if (mOutputType == SH_HLSL_4_1_OUTPUT || mOutputType == SH_HLSL_4_0_FL9_3_OUTPUT)
887 {
888 out << "cbuffer DriverConstants : register(b1)\n"
889 "{\n";
890
891 if (mUsesDepthRange)
892 {
893 out << " float3 dx_DepthRange : packoffset(c0);\n";
894 }
895
896 if (mUsesFragCoord)
897 {
898 out << " float4 dx_ViewCoords : packoffset(c1);\n";
899 out << " float2 dx_FragCoordOffset : packoffset(c3);\n";
900 }
901
902 if (mUsesFragCoord || mUsesFrontFacing)
903 {
904 out << " float3 dx_DepthFront : packoffset(c2);\n";
905 }
906
907 if (mUsesFragCoord)
908 {
909 // dx_ViewScale is only used in the fragment shader to correct
910 // the value for glFragCoord if necessary
911 out << " float2 dx_ViewScale : packoffset(c3.z);\n";
912 }
913
914 if (mOutputType == SH_HLSL_4_1_OUTPUT)
915 {
916 out << " uint dx_Misc : packoffset(c2.w);\n";
917 unsigned int registerIndex = 4;
918 mResourcesHLSL->samplerMetadataUniforms(out, registerIndex);
919 // Sampler metadata struct must be two 4-vec, 32 bytes.
920 registerIndex += mResourcesHLSL->getSamplerCount() * 2;
921 mResourcesHLSL->imageMetadataUniforms(out, registerIndex);
922 }
923
924 out << "};\n";
925
926 if (mOutputType == SH_HLSL_4_1_OUTPUT && mResourcesHLSL->hasImages())
927 {
928 out << kImage2DFunctionString << "\n";
929 }
930 }
931 else
932 {
933 if (mUsesDepthRange)
934 {
935 out << "uniform float3 dx_DepthRange : register(c0);";
936 }
937
938 if (mUsesFragCoord)
939 {
940 out << "uniform float4 dx_ViewCoords : register(c1);\n";
941 }
942
943 if (mUsesFragCoord || mUsesFrontFacing)
944 {
945 out << "uniform float3 dx_DepthFront : register(c2);\n";
946 out << "uniform float2 dx_FragCoordOffset : register(c3);\n";
947 }
948 }
949
950 out << "\n";
951
952 if (mUsesDepthRange)
953 {
954 out << "static gl_DepthRangeParameters gl_DepthRange = {dx_DepthRange.x, "
955 "dx_DepthRange.y, dx_DepthRange.z};\n"
956 "\n";
957 }
958
959 if (mClipDistanceSize)
960 {
961 out << "static float gl_ClipDistance[" << static_cast<int>(mClipDistanceSize)
962 << "] = {0";
963 for (unsigned int i = 1; i < mClipDistanceSize; i++)
964 {
965 out << ", 0";
966 }
967 out << "};\n";
968 }
969
970 if (mCullDistanceSize)
971 {
972 out << "static float gl_CullDistance[" << static_cast<int>(mCullDistanceSize)
973 << "] = {0";
974 for (unsigned int i = 1; i < mCullDistanceSize; i++)
975 {
976 out << ", 0";
977 }
978 out << "};\n";
979 }
980
981 if (usingMRTExtension && mNumRenderTargets > 1)
982 {
983 out << "#define GL_USES_MRT\n";
984 }
985
986 if (mUsesFragColor)
987 {
988 out << "#define GL_USES_FRAG_COLOR\n";
989 }
990
991 if (mUsesFragData)
992 {
993 out << "#define GL_USES_FRAG_DATA\n";
994 }
995
996 if (mShaderVersion < 300 && usingBFEExtension && mUsesSecondaryColor)
997 {
998 out << "#define GL_USES_SECONDARY_COLOR\n";
999 }
1000 }
1001 else if (mShaderType == GL_VERTEX_SHADER)
1002 {
1003 out << "// Attributes\n";
1004 writeReferencedAttributes(out);
1005 out << "\n"
1006 "static float4 gl_Position = float4(0, 0, 0, 0);\n";
1007
1008 if (mClipDistanceSize)
1009 {
1010 out << "static float gl_ClipDistance[" << static_cast<int>(mClipDistanceSize)
1011 << "] = {0";
1012 for (size_t i = 1; i < mClipDistanceSize; i++)
1013 {
1014 out << ", 0";
1015 }
1016 out << "};\n";
1017 }
1018
1019 if (mCullDistanceSize)
1020 {
1021 out << "static float gl_CullDistance[" << static_cast<int>(mCullDistanceSize)
1022 << "] = {0";
1023 for (size_t i = 1; i < mCullDistanceSize; i++)
1024 {
1025 out << ", 0";
1026 }
1027 out << "};\n";
1028 }
1029
1030 if (mUsesPointSize)
1031 {
1032 out << "static float gl_PointSize = float(1);\n";
1033 }
1034
1035 if (mUsesInstanceID)
1036 {
1037 out << "static int gl_InstanceID;\n";
1038 }
1039
1040 if (mUsesViewID)
1041 {
1042 out << "static uint ViewID_OVR;\n";
1043 }
1044
1045 if (mUsesVertexID)
1046 {
1047 out << "static int gl_VertexID;\n";
1048 }
1049
1050 out << "\n"
1051 "// Varyings\n";
1052 writeReferencedVaryings(out);
1053 out << "\n";
1054
1055 if (mUsesDepthRange)
1056 {
1057 out << "struct gl_DepthRangeParameters\n"
1058 "{\n"
1059 " float near;\n"
1060 " float far;\n"
1061 " float diff;\n"
1062 "};\n"
1063 "\n";
1064 }
1065
1066 if (mOutputType == SH_HLSL_4_1_OUTPUT || mOutputType == SH_HLSL_4_0_FL9_3_OUTPUT)
1067 {
1068 out << "cbuffer DriverConstants : register(b1)\n"
1069 "{\n";
1070
1071 if (mUsesDepthRange)
1072 {
1073 out << " float3 dx_DepthRange : packoffset(c0);\n";
1074 }
1075
1076 // dx_ViewAdjust and dx_ViewCoords will only be used in Feature Level 9
1077 // shaders. However, we declare it for all shaders (including Feature Level 10+).
1078 // The bytecode is the same whether we declare it or not, since D3DCompiler removes it
1079 // if it's unused.
1080 out << " float4 dx_ViewAdjust : packoffset(c1);\n";
1081 out << " float2 dx_ViewCoords : packoffset(c2);\n";
1082 out << " float2 dx_ViewScale : packoffset(c3);\n";
1083
1084 out << " float clipControlOrigin : packoffset(c3.z);\n";
1085 out << " float clipControlZeroToOne : packoffset(c3.w);\n";
1086
1087 if (mOutputType == SH_HLSL_4_1_OUTPUT)
1088 {
1089 mResourcesHLSL->samplerMetadataUniforms(out, 5);
1090 }
1091
1092 if (mUsesVertexID)
1093 {
1094 out << " uint dx_VertexID : packoffset(c4.x);\n";
1095 }
1096
1097 if (mClipDistanceSize)
1098 {
1099 out << " uint clipDistancesEnabled : packoffset(c4.y);\n";
1100 }
1101
1102 out << "};\n"
1103 "\n";
1104 }
1105 else
1106 {
1107 if (mUsesDepthRange)
1108 {
1109 out << "uniform float3 dx_DepthRange : register(c0);\n";
1110 }
1111
1112 out << "uniform float4 dx_ViewAdjust : register(c1);\n";
1113 out << "uniform float2 dx_ViewCoords : register(c2);\n";
1114
1115 out << "static const float clipControlOrigin = -1.0f;\n";
1116 out << "static const float clipControlZeroToOne = 0.0f;\n";
1117
1118 out << "\n";
1119 }
1120
1121 if (mUsesDepthRange)
1122 {
1123 out << "static gl_DepthRangeParameters gl_DepthRange = {dx_DepthRange.x, "
1124 "dx_DepthRange.y, dx_DepthRange.z};\n"
1125 "\n";
1126 }
1127
1128 if (mOutputType == SH_HLSL_4_1_OUTPUT && mResourcesHLSL->hasImages())
1129 {
1130 out << kImage2DFunctionString << "\n";
1131 }
1132 }
1133 else // Compute shader
1134 {
1135 ASSERT(mShaderType == GL_COMPUTE_SHADER);
1136
1137 out << "cbuffer DriverConstants : register(b1)\n"
1138 "{\n";
1139 if (mUsesNumWorkGroups)
1140 {
1141 out << " uint3 gl_NumWorkGroups : packoffset(c0);\n";
1142 }
1143 ASSERT(mOutputType == SH_HLSL_4_1_OUTPUT);
1144 unsigned int registerIndex = 1;
1145 mResourcesHLSL->samplerMetadataUniforms(out, registerIndex);
1146 // Sampler metadata struct must be two 4-vec, 32 bytes.
1147 registerIndex += mResourcesHLSL->getSamplerCount() * 2;
1148 mResourcesHLSL->imageMetadataUniforms(out, registerIndex);
1149 out << "};\n";
1150
1151 out << kImage2DFunctionString << "\n";
1152
1153 std::ostringstream systemValueDeclaration = sh::InitializeStream<std::ostringstream>();
1154 std::ostringstream glBuiltinInitialization = sh::InitializeStream<std::ostringstream>();
1155
1156 systemValueDeclaration << "\nstruct CS_INPUT\n{\n";
1157 glBuiltinInitialization << "\nvoid initGLBuiltins(CS_INPUT input)\n" << "{\n";
1158
1159 if (mUsesWorkGroupID)
1160 {
1161 out << "static uint3 gl_WorkGroupID = uint3(0, 0, 0);\n";
1162 systemValueDeclaration << " uint3 dx_WorkGroupID : " << "SV_GroupID;\n";
1163 glBuiltinInitialization << " gl_WorkGroupID = input.dx_WorkGroupID;\n";
1164 }
1165
1166 if (mUsesLocalInvocationID)
1167 {
1168 out << "static uint3 gl_LocalInvocationID = uint3(0, 0, 0);\n";
1169 systemValueDeclaration << " uint3 dx_LocalInvocationID : " << "SV_GroupThreadID;\n";
1170 glBuiltinInitialization << " gl_LocalInvocationID = input.dx_LocalInvocationID;\n";
1171 }
1172
1173 if (mUsesGlobalInvocationID)
1174 {
1175 out << "static uint3 gl_GlobalInvocationID = uint3(0, 0, 0);\n";
1176 systemValueDeclaration << " uint3 dx_GlobalInvocationID : "
1177 << "SV_DispatchThreadID;\n";
1178 glBuiltinInitialization << " gl_GlobalInvocationID = input.dx_GlobalInvocationID;\n";
1179 }
1180
1181 if (mUsesLocalInvocationIndex)
1182 {
1183 out << "static uint gl_LocalInvocationIndex = uint(0);\n";
1184 systemValueDeclaration << " uint dx_LocalInvocationIndex : " << "SV_GroupIndex;\n";
1185 glBuiltinInitialization
1186 << " gl_LocalInvocationIndex = input.dx_LocalInvocationIndex;\n";
1187 }
1188
1189 systemValueDeclaration << "};\n\n";
1190 glBuiltinInitialization << "};\n\n";
1191
1192 out << systemValueDeclaration.str();
1193 out << glBuiltinInitialization.str();
1194 }
1195
1196 if (!mappedStructs.empty())
1197 {
1198 out << "// Structures from std140 blocks with padding removed\n";
1199 out << "\n";
1200 out << mappedStructs;
1201 out << "\n";
1202 }
1203
1204 bool getDimensionsIgnoresBaseLevel = mCompileOptions.HLSLGetDimensionsIgnoresBaseLevel;
1205 mTextureFunctionHLSL->textureFunctionHeader(out, mOutputType, getDimensionsIgnoresBaseLevel);
1206 mImageFunctionHLSL->imageFunctionHeader(out);
1207 mAtomicCounterFunctionHLSL->atomicCounterFunctionHeader(out);
1208
1209 if (mUsesFragCoord)
1210 {
1211 out << "#define GL_USES_FRAG_COORD\n";
1212 }
1213
1214 if (mUsesPointCoord)
1215 {
1216 out << "#define GL_USES_POINT_COORD\n";
1217 }
1218
1219 if (mUsesFrontFacing)
1220 {
1221 out << "#define GL_USES_FRONT_FACING\n";
1222 }
1223
1224 if (mUsesHelperInvocation)
1225 {
1226 out << "#define GL_USES_HELPER_INVOCATION\n";
1227 }
1228
1229 if (mUsesPointSize)
1230 {
1231 out << "#define GL_USES_POINT_SIZE\n";
1232 }
1233
1234 if (mHasMultiviewExtensionEnabled)
1235 {
1236 out << "#define GL_MULTIVIEW_ENABLED\n";
1237 }
1238
1239 if (mUsesVertexID)
1240 {
1241 out << "#define GL_USES_VERTEX_ID\n";
1242 }
1243
1244 if (mUsesViewID)
1245 {
1246 out << "#define GL_USES_VIEW_ID\n";
1247 }
1248
1249 if (mUsesSampleID)
1250 {
1251 out << "#define GL_USES_SAMPLE_ID\n";
1252 }
1253
1254 if (mUsesSamplePosition)
1255 {
1256 out << "#define GL_USES_SAMPLE_POSITION\n";
1257 }
1258
1259 if (mUsesSampleMaskIn)
1260 {
1261 out << "#define GL_USES_SAMPLE_MASK_IN\n";
1262 }
1263
1264 if (mUsesSampleMask)
1265 {
1266 out << "#define GL_USES_SAMPLE_MASK_OUT\n";
1267 }
1268
1269 if (mUsesFragDepth)
1270 {
1271 switch (mDepthLayout)
1272 {
1273 case EdGreater:
1274 out << "#define GL_USES_FRAG_DEPTH_GREATER\n";
1275 break;
1276 case EdLess:
1277 out << "#define GL_USES_FRAG_DEPTH_LESS\n";
1278 break;
1279 default:
1280 out << "#define GL_USES_FRAG_DEPTH\n";
1281 break;
1282 }
1283 }
1284
1285 if (mUsesDepthRange)
1286 {
1287 out << "#define GL_USES_DEPTH_RANGE\n";
1288 }
1289
1290 if (mUsesXor)
1291 {
1292 out << "bool xor(bool p, bool q)\n"
1293 "{\n"
1294 " return (p || q) && !(p && q);\n"
1295 "}\n"
1296 "\n";
1297 }
1298
1299 builtInFunctionEmulator->outputEmulatedFunctions(out);
1300 }
1301
visitSymbol(TIntermSymbol * node)1302 void OutputHLSL::visitSymbol(TIntermSymbol *node)
1303 {
1304 const TVariable &variable = node->variable();
1305
1306 // Empty symbols can only appear in declarations and function arguments, and in either of those
1307 // cases the symbol nodes are not visited.
1308 ASSERT(variable.symbolType() != SymbolType::Empty);
1309
1310 TInfoSinkBase &out = getInfoSink();
1311
1312 // Handle accessing std140 structs by value
1313 if (IsInStd140UniformBlock(node) && node->getBasicType() == EbtStruct &&
1314 needStructMapping(node))
1315 {
1316 mNeedStructMapping = true;
1317 out << "map";
1318 }
1319
1320 const ImmutableString &name = variable.name();
1321 const TSymbolUniqueId &uniqueId = variable.uniqueId();
1322
1323 if (name == "gl_DepthRange")
1324 {
1325 mUsesDepthRange = true;
1326 out << name;
1327 }
1328 else if (name == "gl_NumSamples")
1329 {
1330 mUsesNumSamples = true;
1331 out << name;
1332 }
1333 else if (IsAtomicCounter(variable.getType().getBasicType()))
1334 {
1335 const TType &variableType = variable.getType();
1336 if (variableType.getQualifier() == EvqUniform)
1337 {
1338 TLayoutQualifier layout = variableType.getLayoutQualifier();
1339 mReferencedUniforms[uniqueId.get()] = &variable;
1340 out << getAtomicCounterNameForBinding(layout.binding) << ", " << layout.offset;
1341 }
1342 else
1343 {
1344 TString varName = DecorateVariableIfNeeded(variable);
1345 out << varName << ", " << varName << "_offset";
1346 }
1347 }
1348 else
1349 {
1350 const TType &variableType = variable.getType();
1351 TQualifier qualifier = variable.getType().getQualifier();
1352
1353 ensureStructDefined(variableType);
1354
1355 if (qualifier == EvqUniform)
1356 {
1357 const TInterfaceBlock *interfaceBlock = variableType.getInterfaceBlock();
1358
1359 if (interfaceBlock)
1360 {
1361 if (mReferencedUniformBlocks.count(interfaceBlock->uniqueId().get()) == 0)
1362 {
1363 const TVariable *instanceVariable = nullptr;
1364 if (variableType.isInterfaceBlock())
1365 {
1366 instanceVariable = &variable;
1367 }
1368 mReferencedUniformBlocks[interfaceBlock->uniqueId().get()] =
1369 new TReferencedBlock(interfaceBlock, instanceVariable);
1370 }
1371 }
1372 else
1373 {
1374 mReferencedUniforms[uniqueId.get()] = &variable;
1375 }
1376
1377 out << DecorateVariableIfNeeded(variable);
1378 }
1379 else if (qualifier == EvqBuffer)
1380 {
1381 UNREACHABLE();
1382 }
1383 else if (qualifier == EvqAttribute || qualifier == EvqVertexIn)
1384 {
1385 mReferencedAttributes[uniqueId.get()] = &variable;
1386 out << Decorate(name);
1387 }
1388 else if (IsVarying(qualifier))
1389 {
1390 mReferencedVaryings[uniqueId.get()] = &variable;
1391 out << DecorateVariableIfNeeded(variable);
1392 }
1393 else if (qualifier == EvqFragmentOut)
1394 {
1395 mReferencedOutputVariables[uniqueId.get()] = &variable;
1396 out << "out_" << name;
1397 }
1398 else if (qualifier == EvqViewIDOVR)
1399 {
1400 out << name;
1401 mUsesViewID = true;
1402 }
1403 else if (qualifier == EvqClipDistance)
1404 {
1405 out << name;
1406 }
1407 else if (qualifier == EvqCullDistance)
1408 {
1409 out << name;
1410 }
1411 else if (qualifier == EvqFragColor)
1412 {
1413 out << "gl_Color[0]";
1414 mUsesFragColor = true;
1415 }
1416 else if (qualifier == EvqFragData)
1417 {
1418 out << "gl_Color";
1419 mUsesFragData = true;
1420 }
1421 else if (qualifier == EvqSecondaryFragColorEXT)
1422 {
1423 out << "gl_SecondaryColor[0]";
1424 mUsesSecondaryColor = true;
1425 }
1426 else if (qualifier == EvqSecondaryFragDataEXT)
1427 {
1428 out << "gl_SecondaryColor";
1429 mUsesSecondaryColor = true;
1430 }
1431 else if (qualifier == EvqFragCoord)
1432 {
1433 mUsesFragCoord = true;
1434 out << name;
1435 }
1436 else if (qualifier == EvqPointCoord)
1437 {
1438 mUsesPointCoord = true;
1439 out << name;
1440 }
1441 else if (qualifier == EvqFrontFacing)
1442 {
1443 mUsesFrontFacing = true;
1444 out << name;
1445 }
1446 else if (qualifier == EvqHelperInvocation)
1447 {
1448 mUsesHelperInvocation = true;
1449 out << name;
1450 }
1451 else if (qualifier == EvqPointSize)
1452 {
1453 mUsesPointSize = true;
1454 out << name;
1455 }
1456 else if (qualifier == EvqInstanceID)
1457 {
1458 mUsesInstanceID = true;
1459 out << name;
1460 }
1461 else if (qualifier == EvqVertexID)
1462 {
1463 mUsesVertexID = true;
1464 out << name;
1465 }
1466 else if (name == "gl_FragDepthEXT" || name == "gl_FragDepth")
1467 {
1468 mUsesFragDepth = true;
1469 mDepthLayout = variableType.getLayoutQualifier().depth;
1470 out << "gl_Depth";
1471 }
1472 else if (qualifier == EvqSampleID)
1473 {
1474 mUsesSampleID = true;
1475 out << name;
1476 }
1477 else if (qualifier == EvqSamplePosition)
1478 {
1479 mUsesSamplePosition = true;
1480 out << name;
1481 }
1482 else if (qualifier == EvqSampleMaskIn)
1483 {
1484 mUsesSampleMaskIn = true;
1485 out << name;
1486 }
1487 else if (qualifier == EvqSampleMask)
1488 {
1489 mUsesSampleMask = true;
1490 out << name;
1491 }
1492 else if (qualifier == EvqNumWorkGroups)
1493 {
1494 mUsesNumWorkGroups = true;
1495 out << name;
1496 }
1497 else if (qualifier == EvqWorkGroupID)
1498 {
1499 mUsesWorkGroupID = true;
1500 out << name;
1501 }
1502 else if (qualifier == EvqLocalInvocationID)
1503 {
1504 mUsesLocalInvocationID = true;
1505 out << name;
1506 }
1507 else if (qualifier == EvqGlobalInvocationID)
1508 {
1509 mUsesGlobalInvocationID = true;
1510 out << name;
1511 }
1512 else if (qualifier == EvqLocalInvocationIndex)
1513 {
1514 mUsesLocalInvocationIndex = true;
1515 out << name;
1516 }
1517 else
1518 {
1519 out << DecorateVariableIfNeeded(variable);
1520 }
1521 }
1522 }
1523
outputEqual(Visit visit,const TType & type,TOperator op,TInfoSinkBase & out)1524 void OutputHLSL::outputEqual(Visit visit, const TType &type, TOperator op, TInfoSinkBase &out)
1525 {
1526 if (type.isScalar() && !type.isArray())
1527 {
1528 if (op == EOpEqual)
1529 {
1530 outputTriplet(out, visit, "(", " == ", ")");
1531 }
1532 else
1533 {
1534 outputTriplet(out, visit, "(", " != ", ")");
1535 }
1536 }
1537 else
1538 {
1539 if (visit == PreVisit && op == EOpNotEqual)
1540 {
1541 out << "!";
1542 }
1543
1544 if (type.isArray())
1545 {
1546 const TString &functionName = addArrayEqualityFunction(type);
1547 outputTriplet(out, visit, (functionName + "(").c_str(), ", ", ")");
1548 }
1549 else if (type.getBasicType() == EbtStruct)
1550 {
1551 const TStructure &structure = *type.getStruct();
1552 const TString &functionName = addStructEqualityFunction(structure);
1553 outputTriplet(out, visit, (functionName + "(").c_str(), ", ", ")");
1554 }
1555 else
1556 {
1557 ASSERT(type.isMatrix() || type.isVector());
1558 outputTriplet(out, visit, "all(", " == ", ")");
1559 }
1560 }
1561 }
1562
outputAssign(Visit visit,const TType & type,TInfoSinkBase & out)1563 void OutputHLSL::outputAssign(Visit visit, const TType &type, TInfoSinkBase &out)
1564 {
1565 if (type.isArray())
1566 {
1567 const TString &functionName = addArrayAssignmentFunction(type);
1568 outputTriplet(out, visit, (functionName + "(").c_str(), ", ", ")");
1569 }
1570 else
1571 {
1572 outputTriplet(out, visit, "(", " = ", ")");
1573 }
1574 }
1575
ancestorEvaluatesToSamplerInStruct()1576 bool OutputHLSL::ancestorEvaluatesToSamplerInStruct()
1577 {
1578 for (unsigned int n = 0u; getAncestorNode(n) != nullptr; ++n)
1579 {
1580 TIntermNode *ancestor = getAncestorNode(n);
1581 const TIntermBinary *ancestorBinary = ancestor->getAsBinaryNode();
1582 if (ancestorBinary == nullptr)
1583 {
1584 return false;
1585 }
1586 switch (ancestorBinary->getOp())
1587 {
1588 case EOpIndexDirectStruct:
1589 {
1590 const TStructure *structure = ancestorBinary->getLeft()->getType().getStruct();
1591 const TIntermConstantUnion *index =
1592 ancestorBinary->getRight()->getAsConstantUnion();
1593 const TField *field = structure->fields()[index->getIConst(0)];
1594 if (IsSampler(field->type()->getBasicType()))
1595 {
1596 return true;
1597 }
1598 break;
1599 }
1600 case EOpIndexDirect:
1601 break;
1602 default:
1603 // Returning a sampler from indirect indexing is not supported.
1604 return false;
1605 }
1606 }
1607 return false;
1608 }
1609
visitSwizzle(Visit visit,TIntermSwizzle * node)1610 bool OutputHLSL::visitSwizzle(Visit visit, TIntermSwizzle *node)
1611 {
1612 TInfoSinkBase &out = getInfoSink();
1613 if (visit == PostVisit)
1614 {
1615 out << ".";
1616 node->writeOffsetsAsXYZW(&out);
1617 }
1618 return true;
1619 }
1620
visitBinary(Visit visit,TIntermBinary * node)1621 bool OutputHLSL::visitBinary(Visit visit, TIntermBinary *node)
1622 {
1623 TInfoSinkBase &out = getInfoSink();
1624
1625 switch (node->getOp())
1626 {
1627 case EOpComma:
1628 outputTriplet(out, visit, "(", ", ", ")");
1629 break;
1630 case EOpAssign:
1631 if (node->isArray())
1632 {
1633 TIntermAggregate *rightAgg = node->getRight()->getAsAggregate();
1634 if (rightAgg != nullptr && rightAgg->isConstructor())
1635 {
1636 const TString &functionName = addArrayConstructIntoFunction(node->getType());
1637 out << functionName << "(";
1638 node->getLeft()->traverse(this);
1639 TIntermSequence *seq = rightAgg->getSequence();
1640 for (auto &arrayElement : *seq)
1641 {
1642 out << ", ";
1643 arrayElement->traverse(this);
1644 }
1645 out << ")";
1646 return false;
1647 }
1648 // ArrayReturnValueToOutParameter should have eliminated expressions where a
1649 // function call is assigned.
1650 ASSERT(rightAgg == nullptr);
1651 }
1652 // Assignment expressions with atomic functions should be transformed into atomic
1653 // function calls in HLSL.
1654 // e.g. original_value = atomicAdd(dest, value) should be translated into
1655 // InterlockedAdd(dest, value, original_value);
1656 else if (IsAtomicFunctionForSharedVariableDirectAssign(*node))
1657 {
1658 TIntermAggregate *atomicFunctionNode = node->getRight()->getAsAggregate();
1659 TOperator atomicFunctionOp = atomicFunctionNode->getOp();
1660 out << GetHLSLAtomicFunctionStringAndLeftParenthesis(atomicFunctionOp);
1661 TIntermSequence *argumentSeq = atomicFunctionNode->getSequence();
1662 ASSERT(argumentSeq->size() >= 2u);
1663 for (auto &argument : *argumentSeq)
1664 {
1665 argument->traverse(this);
1666 out << ", ";
1667 }
1668 node->getLeft()->traverse(this);
1669 out << ")";
1670 return false;
1671 }
1672 else if (IsInShaderStorageBlock(node->getLeft()))
1673 {
1674 mSSBOOutputHLSL->outputStoreFunctionCallPrefix(node->getLeft());
1675 out << ", ";
1676 if (IsInShaderStorageBlock(node->getRight()))
1677 {
1678 mSSBOOutputHLSL->outputLoadFunctionCall(node->getRight());
1679 }
1680 else
1681 {
1682 node->getRight()->traverse(this);
1683 }
1684
1685 out << ")";
1686 return false;
1687 }
1688 else if (IsInShaderStorageBlock(node->getRight()))
1689 {
1690 node->getLeft()->traverse(this);
1691 out << " = ";
1692 mSSBOOutputHLSL->outputLoadFunctionCall(node->getRight());
1693 return false;
1694 }
1695
1696 outputAssign(visit, node->getType(), out);
1697 break;
1698 case EOpInitialize:
1699 if (visit == PreVisit)
1700 {
1701 TIntermSymbol *symbolNode = node->getLeft()->getAsSymbolNode();
1702 ASSERT(symbolNode);
1703 TIntermTyped *initializer = node->getRight();
1704
1705 // Global initializers must be constant at this point.
1706 ASSERT(symbolNode->getQualifier() != EvqGlobal || initializer->hasConstantValue());
1707
1708 // GLSL allows to write things like "float x = x;" where a new variable x is defined
1709 // and the value of an existing variable x is assigned. HLSL uses C semantics (the
1710 // new variable is created before the assignment is evaluated), so we need to
1711 // convert
1712 // this to "float t = x, x = t;".
1713 if (writeSameSymbolInitializer(out, symbolNode, initializer))
1714 {
1715 // Skip initializing the rest of the expression
1716 return false;
1717 }
1718 else if (writeConstantInitialization(out, symbolNode, initializer))
1719 {
1720 return false;
1721 }
1722 }
1723 else if (visit == InVisit)
1724 {
1725 out << " = ";
1726 if (IsInShaderStorageBlock(node->getRight()))
1727 {
1728 mSSBOOutputHLSL->outputLoadFunctionCall(node->getRight());
1729 return false;
1730 }
1731 }
1732 break;
1733 case EOpAddAssign:
1734 outputTriplet(out, visit, "(", " += ", ")");
1735 break;
1736 case EOpSubAssign:
1737 outputTriplet(out, visit, "(", " -= ", ")");
1738 break;
1739 case EOpMulAssign:
1740 outputTriplet(out, visit, "(", " *= ", ")");
1741 break;
1742 case EOpVectorTimesScalarAssign:
1743 outputTriplet(out, visit, "(", " *= ", ")");
1744 break;
1745 case EOpMatrixTimesScalarAssign:
1746 outputTriplet(out, visit, "(", " *= ", ")");
1747 break;
1748 case EOpVectorTimesMatrixAssign:
1749 if (visit == PreVisit)
1750 {
1751 out << "(";
1752 }
1753 else if (visit == InVisit)
1754 {
1755 out << " = mul(";
1756 node->getLeft()->traverse(this);
1757 out << ", transpose(";
1758 }
1759 else
1760 {
1761 out << ")))";
1762 }
1763 break;
1764 case EOpMatrixTimesMatrixAssign:
1765 if (visit == PreVisit)
1766 {
1767 out << "(";
1768 }
1769 else if (visit == InVisit)
1770 {
1771 out << " = transpose(mul(transpose(";
1772 node->getLeft()->traverse(this);
1773 out << "), transpose(";
1774 }
1775 else
1776 {
1777 out << "))))";
1778 }
1779 break;
1780 case EOpDivAssign:
1781 outputTriplet(out, visit, "(", " /= ", ")");
1782 break;
1783 case EOpIModAssign:
1784 outputTriplet(out, visit, "(", " %= ", ")");
1785 break;
1786 case EOpBitShiftLeftAssign:
1787 outputTriplet(out, visit, "(", " <<= ", ")");
1788 break;
1789 case EOpBitShiftRightAssign:
1790 outputTriplet(out, visit, "(", " >>= ", ")");
1791 break;
1792 case EOpBitwiseAndAssign:
1793 outputTriplet(out, visit, "(", " &= ", ")");
1794 break;
1795 case EOpBitwiseXorAssign:
1796 outputTriplet(out, visit, "(", " ^= ", ")");
1797 break;
1798 case EOpBitwiseOrAssign:
1799 outputTriplet(out, visit, "(", " |= ", ")");
1800 break;
1801 case EOpIndexDirect:
1802 {
1803 const TType &leftType = node->getLeft()->getType();
1804 if (leftType.isInterfaceBlock())
1805 {
1806 if (visit == PreVisit)
1807 {
1808 TIntermSymbol *instanceArraySymbol = node->getLeft()->getAsSymbolNode();
1809 const TInterfaceBlock *interfaceBlock = leftType.getInterfaceBlock();
1810
1811 ASSERT(leftType.getQualifier() == EvqUniform);
1812 if (mReferencedUniformBlocks.count(interfaceBlock->uniqueId().get()) == 0)
1813 {
1814 mReferencedUniformBlocks[interfaceBlock->uniqueId().get()] =
1815 new TReferencedBlock(interfaceBlock, &instanceArraySymbol->variable());
1816 }
1817 const int arrayIndex = node->getRight()->getAsConstantUnion()->getIConst(0);
1818 out << mResourcesHLSL->InterfaceBlockInstanceString(
1819 instanceArraySymbol->getName(), arrayIndex);
1820 return false;
1821 }
1822 }
1823 else if (ancestorEvaluatesToSamplerInStruct())
1824 {
1825 // All parts of an expression that access a sampler in a struct need to use _ as
1826 // separator to access the sampler variable that has been moved out of the struct.
1827 outputTriplet(out, visit, "", "_", "");
1828 }
1829 else if (IsAtomicCounter(leftType.getBasicType()))
1830 {
1831 outputTriplet(out, visit, "", " + (", ") * ATOMIC_COUNTER_ARRAY_STRIDE");
1832 }
1833 else
1834 {
1835 outputTriplet(out, visit, "", "[", "]");
1836 if (visit == PostVisit)
1837 {
1838 const TInterfaceBlock *interfaceBlock =
1839 GetInterfaceBlockOfUniformBlockNearestIndexOperator(node->getLeft());
1840 if (interfaceBlock &&
1841 mUniformBlockOptimizedMap.count(interfaceBlock->uniqueId().get()) != 0)
1842 {
1843 // If the uniform block member's type is not structure, we had explicitly
1844 // packed the member into a structure, so need to add an operator of field
1845 // slection.
1846 const TField *field = interfaceBlock->fields()[0];
1847 const TType *fieldType = field->type();
1848 if (fieldType->isMatrix() || fieldType->isVectorArray() ||
1849 fieldType->isScalarArray())
1850 {
1851 out << "." << Decorate(field->name());
1852 }
1853 }
1854 }
1855 }
1856 }
1857 break;
1858 case EOpIndexIndirect:
1859 {
1860 // We do not currently support indirect references to interface blocks
1861 ASSERT(node->getLeft()->getBasicType() != EbtInterfaceBlock);
1862
1863 const TType &leftType = node->getLeft()->getType();
1864 if (IsAtomicCounter(leftType.getBasicType()))
1865 {
1866 outputTriplet(out, visit, "", " + (", ") * ATOMIC_COUNTER_ARRAY_STRIDE");
1867 }
1868 else
1869 {
1870 outputTriplet(out, visit, "", "[", "]");
1871 if (visit == PostVisit)
1872 {
1873 const TInterfaceBlock *interfaceBlock =
1874 GetInterfaceBlockOfUniformBlockNearestIndexOperator(node->getLeft());
1875 if (interfaceBlock &&
1876 mUniformBlockOptimizedMap.count(interfaceBlock->uniqueId().get()) != 0)
1877 {
1878 // If the uniform block member's type is not structure, we had explicitly
1879 // packed the member into a structure, so need to add an operator of field
1880 // slection.
1881 const TField *field = interfaceBlock->fields()[0];
1882 const TType *fieldType = field->type();
1883 if (fieldType->isMatrix() || fieldType->isVectorArray() ||
1884 fieldType->isScalarArray())
1885 {
1886 out << "." << Decorate(field->name());
1887 }
1888 }
1889 }
1890 }
1891 break;
1892 }
1893 case EOpIndexDirectStruct:
1894 {
1895 const TStructure *structure = node->getLeft()->getType().getStruct();
1896 const TIntermConstantUnion *index = node->getRight()->getAsConstantUnion();
1897 const TField *field = structure->fields()[index->getIConst(0)];
1898
1899 // In cases where indexing returns a sampler, we need to access the sampler variable
1900 // that has been moved out of the struct.
1901 bool indexingReturnsSampler = IsSampler(field->type()->getBasicType());
1902 if (visit == PreVisit && indexingReturnsSampler)
1903 {
1904 // Samplers extracted from structs have "angle" prefix to avoid name conflicts.
1905 // This prefix is only output at the beginning of the indexing expression, which
1906 // may have multiple parts.
1907 out << "angle";
1908 }
1909 if (!indexingReturnsSampler)
1910 {
1911 // All parts of an expression that access a sampler in a struct need to use _ as
1912 // separator to access the sampler variable that has been moved out of the struct.
1913 indexingReturnsSampler = ancestorEvaluatesToSamplerInStruct();
1914 }
1915 if (visit == InVisit)
1916 {
1917 if (indexingReturnsSampler)
1918 {
1919 out << "_" << field->name();
1920 }
1921 else
1922 {
1923 out << "." << DecorateField(field->name(), *structure);
1924 }
1925
1926 return false;
1927 }
1928 }
1929 break;
1930 case EOpIndexDirectInterfaceBlock:
1931 {
1932 ASSERT(!IsInShaderStorageBlock(node->getLeft()));
1933 bool structInStd140UniformBlock = node->getBasicType() == EbtStruct &&
1934 IsInStd140UniformBlock(node->getLeft()) &&
1935 needStructMapping(node);
1936 if (visit == PreVisit && structInStd140UniformBlock)
1937 {
1938 mNeedStructMapping = true;
1939 out << "map";
1940 }
1941 if (visit == InVisit)
1942 {
1943 const TInterfaceBlock *interfaceBlock =
1944 node->getLeft()->getType().getInterfaceBlock();
1945 const TIntermConstantUnion *index = node->getRight()->getAsConstantUnion();
1946 const TField *field = interfaceBlock->fields()[index->getIConst(0)];
1947 if (structInStd140UniformBlock ||
1948 mUniformBlockOptimizedMap.count(interfaceBlock->uniqueId().get()) != 0)
1949 {
1950 out << "_";
1951 }
1952 else
1953 {
1954 out << ".";
1955 }
1956 out << Decorate(field->name());
1957
1958 return false;
1959 }
1960 break;
1961 }
1962 case EOpAdd:
1963 outputTriplet(out, visit, "(", " + ", ")");
1964 break;
1965 case EOpSub:
1966 outputTriplet(out, visit, "(", " - ", ")");
1967 break;
1968 case EOpMul:
1969 outputTriplet(out, visit, "(", " * ", ")");
1970 break;
1971 case EOpDiv:
1972 outputTriplet(out, visit, "(", " / ", ")");
1973 break;
1974 case EOpIMod:
1975 outputTriplet(out, visit, "(", " % ", ")");
1976 break;
1977 case EOpBitShiftLeft:
1978 outputTriplet(out, visit, "(", " << ", ")");
1979 break;
1980 case EOpBitShiftRight:
1981 outputTriplet(out, visit, "(", " >> ", ")");
1982 break;
1983 case EOpBitwiseAnd:
1984 outputTriplet(out, visit, "(", " & ", ")");
1985 break;
1986 case EOpBitwiseXor:
1987 outputTriplet(out, visit, "(", " ^ ", ")");
1988 break;
1989 case EOpBitwiseOr:
1990 outputTriplet(out, visit, "(", " | ", ")");
1991 break;
1992 case EOpEqual:
1993 case EOpNotEqual:
1994 outputEqual(visit, node->getLeft()->getType(), node->getOp(), out);
1995 break;
1996 case EOpLessThan:
1997 outputTriplet(out, visit, "(", " < ", ")");
1998 break;
1999 case EOpGreaterThan:
2000 outputTriplet(out, visit, "(", " > ", ")");
2001 break;
2002 case EOpLessThanEqual:
2003 outputTriplet(out, visit, "(", " <= ", ")");
2004 break;
2005 case EOpGreaterThanEqual:
2006 outputTriplet(out, visit, "(", " >= ", ")");
2007 break;
2008 case EOpVectorTimesScalar:
2009 outputTriplet(out, visit, "(", " * ", ")");
2010 break;
2011 case EOpMatrixTimesScalar:
2012 outputTriplet(out, visit, "(", " * ", ")");
2013 break;
2014 case EOpVectorTimesMatrix:
2015 outputTriplet(out, visit, "mul(", ", transpose(", "))");
2016 break;
2017 case EOpMatrixTimesVector:
2018 outputTriplet(out, visit, "mul(transpose(", "), ", ")");
2019 break;
2020 case EOpMatrixTimesMatrix:
2021 outputTriplet(out, visit, "transpose(mul(transpose(", "), transpose(", ")))");
2022 break;
2023 case EOpLogicalOr:
2024 // HLSL doesn't short-circuit ||, so we assume that || affected by short-circuiting have
2025 // been unfolded.
2026 ASSERT(!node->getRight()->hasSideEffects());
2027 outputTriplet(out, visit, "(", " || ", ")");
2028 return true;
2029 case EOpLogicalXor:
2030 mUsesXor = true;
2031 outputTriplet(out, visit, "xor(", ", ", ")");
2032 break;
2033 case EOpLogicalAnd:
2034 // HLSL doesn't short-circuit &&, so we assume that && affected by short-circuiting have
2035 // been unfolded.
2036 ASSERT(!node->getRight()->hasSideEffects());
2037 outputTriplet(out, visit, "(", " && ", ")");
2038 return true;
2039 default:
2040 UNREACHABLE();
2041 }
2042
2043 return true;
2044 }
2045
visitUnary(Visit visit,TIntermUnary * node)2046 bool OutputHLSL::visitUnary(Visit visit, TIntermUnary *node)
2047 {
2048 TInfoSinkBase &out = getInfoSink();
2049
2050 switch (node->getOp())
2051 {
2052 case EOpNegative:
2053 outputTriplet(out, visit, "(-", "", ")");
2054 break;
2055 case EOpPositive:
2056 outputTriplet(out, visit, "(+", "", ")");
2057 break;
2058 case EOpLogicalNot:
2059 outputTriplet(out, visit, "(!", "", ")");
2060 break;
2061 case EOpBitwiseNot:
2062 outputTriplet(out, visit, "(~", "", ")");
2063 break;
2064 case EOpPostIncrement:
2065 outputTriplet(out, visit, "(", "", "++)");
2066 break;
2067 case EOpPostDecrement:
2068 outputTriplet(out, visit, "(", "", "--)");
2069 break;
2070 case EOpPreIncrement:
2071 outputTriplet(out, visit, "(++", "", ")");
2072 break;
2073 case EOpPreDecrement:
2074 outputTriplet(out, visit, "(--", "", ")");
2075 break;
2076 case EOpRadians:
2077 outputTriplet(out, visit, "radians(", "", ")");
2078 break;
2079 case EOpDegrees:
2080 outputTriplet(out, visit, "degrees(", "", ")");
2081 break;
2082 case EOpSin:
2083 outputTriplet(out, visit, "sin(", "", ")");
2084 break;
2085 case EOpCos:
2086 outputTriplet(out, visit, "cos(", "", ")");
2087 break;
2088 case EOpTan:
2089 outputTriplet(out, visit, "tan(", "", ")");
2090 break;
2091 case EOpAsin:
2092 outputTriplet(out, visit, "asin(", "", ")");
2093 break;
2094 case EOpAcos:
2095 outputTriplet(out, visit, "acos(", "", ")");
2096 break;
2097 case EOpAtan:
2098 outputTriplet(out, visit, "atan(", "", ")");
2099 break;
2100 case EOpSinh:
2101 outputTriplet(out, visit, "sinh(", "", ")");
2102 break;
2103 case EOpCosh:
2104 outputTriplet(out, visit, "cosh(", "", ")");
2105 break;
2106 case EOpTanh:
2107 case EOpAsinh:
2108 case EOpAcosh:
2109 case EOpAtanh:
2110 ASSERT(node->getUseEmulatedFunction());
2111 writeEmulatedFunctionTriplet(out, visit, node->getFunction());
2112 break;
2113 case EOpExp:
2114 outputTriplet(out, visit, "exp(", "", ")");
2115 break;
2116 case EOpLog:
2117 outputTriplet(out, visit, "log(", "", ")");
2118 break;
2119 case EOpExp2:
2120 outputTriplet(out, visit, "exp2(", "", ")");
2121 break;
2122 case EOpLog2:
2123 outputTriplet(out, visit, "log2(", "", ")");
2124 break;
2125 case EOpSqrt:
2126 outputTriplet(out, visit, "sqrt(", "", ")");
2127 break;
2128 case EOpInversesqrt:
2129 outputTriplet(out, visit, "rsqrt(", "", ")");
2130 break;
2131 case EOpAbs:
2132 outputTriplet(out, visit, "abs(", "", ")");
2133 break;
2134 case EOpSign:
2135 outputTriplet(out, visit, "sign(", "", ")");
2136 break;
2137 case EOpFloor:
2138 outputTriplet(out, visit, "floor(", "", ")");
2139 break;
2140 case EOpTrunc:
2141 outputTriplet(out, visit, "trunc(", "", ")");
2142 break;
2143 case EOpRound:
2144 outputTriplet(out, visit, "round(", "", ")");
2145 break;
2146 case EOpRoundEven:
2147 ASSERT(node->getUseEmulatedFunction());
2148 writeEmulatedFunctionTriplet(out, visit, node->getFunction());
2149 break;
2150 case EOpCeil:
2151 outputTriplet(out, visit, "ceil(", "", ")");
2152 break;
2153 case EOpFract:
2154 outputTriplet(out, visit, "frac(", "", ")");
2155 break;
2156 case EOpIsnan:
2157 if (node->getUseEmulatedFunction())
2158 writeEmulatedFunctionTriplet(out, visit, node->getFunction());
2159 else
2160 outputTriplet(out, visit, "isnan(", "", ")");
2161 mRequiresIEEEStrictCompiling = true;
2162 break;
2163 case EOpIsinf:
2164 outputTriplet(out, visit, "isinf(", "", ")");
2165 break;
2166 case EOpFloatBitsToInt:
2167 outputTriplet(out, visit, "asint(", "", ")");
2168 break;
2169 case EOpFloatBitsToUint:
2170 outputTriplet(out, visit, "asuint(", "", ")");
2171 break;
2172 case EOpIntBitsToFloat:
2173 outputTriplet(out, visit, "asfloat(", "", ")");
2174 break;
2175 case EOpUintBitsToFloat:
2176 outputTriplet(out, visit, "asfloat(", "", ")");
2177 break;
2178 case EOpPackSnorm2x16:
2179 case EOpPackUnorm2x16:
2180 case EOpPackHalf2x16:
2181 case EOpUnpackSnorm2x16:
2182 case EOpUnpackUnorm2x16:
2183 case EOpUnpackHalf2x16:
2184 case EOpPackUnorm4x8:
2185 case EOpPackSnorm4x8:
2186 case EOpUnpackUnorm4x8:
2187 case EOpUnpackSnorm4x8:
2188 ASSERT(node->getUseEmulatedFunction());
2189 writeEmulatedFunctionTriplet(out, visit, node->getFunction());
2190 break;
2191 case EOpLength:
2192 outputTriplet(out, visit, "length(", "", ")");
2193 break;
2194 case EOpNormalize:
2195 outputTriplet(out, visit, "normalize(", "", ")");
2196 break;
2197 case EOpTranspose:
2198 outputTriplet(out, visit, "transpose(", "", ")");
2199 break;
2200 case EOpDeterminant:
2201 outputTriplet(out, visit, "determinant(transpose(", "", "))");
2202 break;
2203 case EOpInverse:
2204 ASSERT(node->getUseEmulatedFunction());
2205 writeEmulatedFunctionTriplet(out, visit, node->getFunction());
2206 break;
2207
2208 case EOpAny:
2209 outputTriplet(out, visit, "any(", "", ")");
2210 break;
2211 case EOpAll:
2212 outputTriplet(out, visit, "all(", "", ")");
2213 break;
2214 case EOpNotComponentWise:
2215 outputTriplet(out, visit, "(!", "", ")");
2216 break;
2217 case EOpBitfieldReverse:
2218 outputTriplet(out, visit, "reversebits(", "", ")");
2219 break;
2220 case EOpBitCount:
2221 outputTriplet(out, visit, "countbits(", "", ")");
2222 break;
2223 case EOpFindLSB:
2224 // Note that it's unclear from the HLSL docs what this returns for 0, but this is tested
2225 // in GLSLTest and results are consistent with GL.
2226 outputTriplet(out, visit, "firstbitlow(", "", ")");
2227 break;
2228 case EOpFindMSB:
2229 // Note that it's unclear from the HLSL docs what this returns for 0 or -1, but this is
2230 // tested in GLSLTest and results are consistent with GL.
2231 outputTriplet(out, visit, "firstbithigh(", "", ")");
2232 break;
2233 case EOpArrayLength:
2234 {
2235 TIntermTyped *operand = node->getOperand();
2236 ASSERT(IsInShaderStorageBlock(operand));
2237 mSSBOOutputHLSL->outputLengthFunctionCall(operand);
2238 return false;
2239 }
2240 default:
2241 UNREACHABLE();
2242 }
2243
2244 return true;
2245 }
2246
samplerNamePrefixFromStruct(TIntermTyped * node)2247 ImmutableString OutputHLSL::samplerNamePrefixFromStruct(TIntermTyped *node)
2248 {
2249 if (node->getAsSymbolNode())
2250 {
2251 ASSERT(node->getAsSymbolNode()->variable().symbolType() != SymbolType::Empty);
2252 return node->getAsSymbolNode()->getName();
2253 }
2254 TIntermBinary *nodeBinary = node->getAsBinaryNode();
2255 switch (nodeBinary->getOp())
2256 {
2257 case EOpIndexDirect:
2258 {
2259 int index = nodeBinary->getRight()->getAsConstantUnion()->getIConst(0);
2260
2261 std::stringstream prefixSink = sh::InitializeStream<std::stringstream>();
2262 prefixSink << samplerNamePrefixFromStruct(nodeBinary->getLeft()) << "_" << index;
2263 return ImmutableString(prefixSink.str());
2264 }
2265 case EOpIndexDirectStruct:
2266 {
2267 const TStructure *s = nodeBinary->getLeft()->getAsTyped()->getType().getStruct();
2268 int index = nodeBinary->getRight()->getAsConstantUnion()->getIConst(0);
2269 const TField *field = s->fields()[index];
2270
2271 std::stringstream prefixSink = sh::InitializeStream<std::stringstream>();
2272 prefixSink << samplerNamePrefixFromStruct(nodeBinary->getLeft()) << "_"
2273 << field->name();
2274 return ImmutableString(prefixSink.str());
2275 }
2276 default:
2277 UNREACHABLE();
2278 return kEmptyImmutableString;
2279 }
2280 }
2281
visitBlock(Visit visit,TIntermBlock * node)2282 bool OutputHLSL::visitBlock(Visit visit, TIntermBlock *node)
2283 {
2284 TInfoSinkBase &out = getInfoSink();
2285
2286 bool isMainBlock = mInsideMain && getParentNode()->getAsFunctionDefinition();
2287
2288 if (mInsideFunction)
2289 {
2290 outputLineDirective(out, node->getLine().first_line);
2291 out << "{\n";
2292 if (isMainBlock)
2293 {
2294 if (mShaderType == GL_COMPUTE_SHADER)
2295 {
2296 out << "initGLBuiltins(input);\n";
2297 }
2298 else
2299 {
2300 out << "@@ MAIN PROLOGUE @@\n";
2301 }
2302 }
2303 }
2304
2305 for (TIntermNode *statement : *node->getSequence())
2306 {
2307 outputLineDirective(out, statement->getLine().first_line);
2308
2309 statement->traverse(this);
2310
2311 // Don't output ; after case labels, they're terminated by :
2312 // This is needed especially since outputting a ; after a case statement would turn empty
2313 // case statements into non-empty case statements, disallowing fall-through from them.
2314 // Also the output code is clearer if we don't output ; after statements where it is not
2315 // needed:
2316 // * if statements
2317 // * switch statements
2318 // * blocks
2319 // * function definitions
2320 // * loops (do-while loops output the semicolon in VisitLoop)
2321 // * declarations that don't generate output.
2322 if (statement->getAsCaseNode() == nullptr && statement->getAsIfElseNode() == nullptr &&
2323 statement->getAsBlock() == nullptr && statement->getAsLoopNode() == nullptr &&
2324 statement->getAsSwitchNode() == nullptr &&
2325 statement->getAsFunctionDefinition() == nullptr &&
2326 (statement->getAsDeclarationNode() == nullptr ||
2327 IsDeclarationWrittenOut(statement->getAsDeclarationNode())) &&
2328 statement->getAsGlobalQualifierDeclarationNode() == nullptr)
2329 {
2330 out << ";\n";
2331 }
2332 }
2333
2334 if (mInsideFunction)
2335 {
2336 outputLineDirective(out, node->getLine().last_line);
2337 if (isMainBlock && shaderNeedsGenerateOutput())
2338 {
2339 // We could have an empty main, a main function without a branch at the end, or a main
2340 // function with a discard statement at the end. In these cases we need to add a return
2341 // statement.
2342 bool needReturnStatement =
2343 node->getSequence()->empty() || !node->getSequence()->back()->getAsBranchNode() ||
2344 node->getSequence()->back()->getAsBranchNode()->getFlowOp() != EOpReturn;
2345 if (needReturnStatement)
2346 {
2347 out << "return " << generateOutputCall() << ";\n";
2348 }
2349 }
2350 out << "}\n";
2351 }
2352
2353 return false;
2354 }
2355
visitFunctionDefinition(Visit visit,TIntermFunctionDefinition * node)2356 bool OutputHLSL::visitFunctionDefinition(Visit visit, TIntermFunctionDefinition *node)
2357 {
2358 TInfoSinkBase &out = getInfoSink();
2359
2360 ASSERT(mCurrentFunctionMetadata == nullptr);
2361
2362 size_t index = mCallDag.findIndex(node->getFunction()->uniqueId());
2363 ASSERT(index != CallDAG::InvalidIndex);
2364 mCurrentFunctionMetadata = &mASTMetadataList[index];
2365
2366 const TFunction *func = node->getFunction();
2367
2368 if (func->isMain())
2369 {
2370 // The stub strings below are replaced when shader is dynamically defined by its layout:
2371 switch (mShaderType)
2372 {
2373 case GL_VERTEX_SHADER:
2374 out << "@@ VERTEX ATTRIBUTES @@\n\n"
2375 << "@@ VERTEX OUTPUT @@\n\n"
2376 << "VS_OUTPUT main(VS_INPUT input)";
2377 break;
2378 case GL_FRAGMENT_SHADER:
2379 out << "@@ PIXEL OUTPUT @@\n\n";
2380 if (mIsEarlyFragmentTestsSpecified)
2381 {
2382 out << "[earlydepthstencil]\n";
2383 }
2384 out << "PS_OUTPUT main(@@ PIXEL MAIN PARAMETERS @@)";
2385 break;
2386 case GL_COMPUTE_SHADER:
2387 out << "[numthreads(" << mWorkGroupSize[0] << ", " << mWorkGroupSize[1] << ", "
2388 << mWorkGroupSize[2] << ")]\n";
2389 out << "void main(CS_INPUT input)";
2390 break;
2391 default:
2392 UNREACHABLE();
2393 break;
2394 }
2395 }
2396 else
2397 {
2398 out << TypeString(node->getFunctionPrototype()->getType()) << " ";
2399 out << DecorateFunctionIfNeeded(func) << DisambiguateFunctionName(func)
2400 << (mOutputLod0Function ? "Lod0(" : "(");
2401
2402 size_t paramCount = func->getParamCount();
2403 for (unsigned int i = 0; i < paramCount; i++)
2404 {
2405 const TVariable *param = func->getParam(i);
2406 ensureStructDefined(param->getType());
2407
2408 writeParameter(param, out);
2409
2410 if (i < paramCount - 1)
2411 {
2412 out << ", ";
2413 }
2414 }
2415
2416 out << ")\n";
2417 }
2418
2419 mInsideFunction = true;
2420 if (func->isMain())
2421 {
2422 mInsideMain = true;
2423 }
2424 // The function body node will output braces.
2425 node->getBody()->traverse(this);
2426 mInsideFunction = false;
2427 mInsideMain = false;
2428
2429 mCurrentFunctionMetadata = nullptr;
2430
2431 bool needsLod0 = mASTMetadataList[index].mNeedsLod0;
2432 if (needsLod0 && !mOutputLod0Function && mShaderType == GL_FRAGMENT_SHADER)
2433 {
2434 ASSERT(!node->getFunction()->isMain());
2435 mOutputLod0Function = true;
2436 node->traverse(this);
2437 mOutputLod0Function = false;
2438 }
2439
2440 return false;
2441 }
2442
visitDeclaration(Visit visit,TIntermDeclaration * node)2443 bool OutputHLSL::visitDeclaration(Visit visit, TIntermDeclaration *node)
2444 {
2445 if (visit == PreVisit)
2446 {
2447 TIntermSequence *sequence = node->getSequence();
2448 TIntermTyped *declarator = (*sequence)[0]->getAsTyped();
2449 ASSERT(sequence->size() == 1);
2450 ASSERT(declarator);
2451
2452 if (IsDeclarationWrittenOut(node))
2453 {
2454 TInfoSinkBase &out = getInfoSink();
2455 ensureStructDefined(declarator->getType());
2456
2457 if (!declarator->getAsSymbolNode() ||
2458 declarator->getAsSymbolNode()->variable().symbolType() !=
2459 SymbolType::Empty) // Variable declaration
2460 {
2461 if (declarator->getQualifier() == EvqShared)
2462 {
2463 out << "groupshared ";
2464 }
2465 else if (!mInsideFunction)
2466 {
2467 out << "static ";
2468 }
2469
2470 out << TypeString(declarator->getType()) + " ";
2471
2472 TIntermSymbol *symbol = declarator->getAsSymbolNode();
2473
2474 if (symbol)
2475 {
2476 symbol->traverse(this);
2477 out << ArrayString(symbol->getType());
2478 // Temporarily disable shadred memory initialization. It is very slow for D3D11
2479 // drivers to compile a compute shader if we add code to initialize a
2480 // groupshared array variable with a large array size. And maybe produce
2481 // incorrect result. See http://anglebug.com/3226.
2482 if (declarator->getQualifier() != EvqShared)
2483 {
2484 out << " = " + zeroInitializer(symbol->getType());
2485 }
2486 }
2487 else
2488 {
2489 declarator->traverse(this);
2490 }
2491 }
2492 }
2493 else if (IsVaryingOut(declarator->getQualifier()))
2494 {
2495 TIntermSymbol *symbol = declarator->getAsSymbolNode();
2496 ASSERT(symbol); // Varying declarations can't have initializers.
2497
2498 const TVariable &variable = symbol->variable();
2499
2500 if (variable.symbolType() != SymbolType::Empty)
2501 {
2502 // Vertex outputs which are declared but not written to should still be declared to
2503 // allow successful linking.
2504 mReferencedVaryings[symbol->uniqueId().get()] = &variable;
2505 }
2506 }
2507 }
2508 return false;
2509 }
2510
visitGlobalQualifierDeclaration(Visit visit,TIntermGlobalQualifierDeclaration * node)2511 bool OutputHLSL::visitGlobalQualifierDeclaration(Visit visit,
2512 TIntermGlobalQualifierDeclaration *node)
2513 {
2514 // Do not do any translation
2515 return false;
2516 }
2517
visitFunctionPrototype(TIntermFunctionPrototype * node)2518 void OutputHLSL::visitFunctionPrototype(TIntermFunctionPrototype *node)
2519 {
2520 TInfoSinkBase &out = getInfoSink();
2521
2522 size_t index = mCallDag.findIndex(node->getFunction()->uniqueId());
2523 // Skip the prototype if it is not implemented (and thus not used)
2524 if (index == CallDAG::InvalidIndex)
2525 {
2526 return;
2527 }
2528
2529 const TFunction *func = node->getFunction();
2530
2531 TString name = DecorateFunctionIfNeeded(func);
2532 out << TypeString(node->getType()) << " " << name << DisambiguateFunctionName(func)
2533 << (mOutputLod0Function ? "Lod0(" : "(");
2534
2535 size_t paramCount = func->getParamCount();
2536 for (unsigned int i = 0; i < paramCount; i++)
2537 {
2538 writeParameter(func->getParam(i), out);
2539
2540 if (i < paramCount - 1)
2541 {
2542 out << ", ";
2543 }
2544 }
2545
2546 out << ");\n";
2547
2548 // Also prototype the Lod0 variant if needed
2549 bool needsLod0 = mASTMetadataList[index].mNeedsLod0;
2550 if (needsLod0 && !mOutputLod0Function && mShaderType == GL_FRAGMENT_SHADER)
2551 {
2552 mOutputLod0Function = true;
2553 node->traverse(this);
2554 mOutputLod0Function = false;
2555 }
2556 }
2557
visitAggregate(Visit visit,TIntermAggregate * node)2558 bool OutputHLSL::visitAggregate(Visit visit, TIntermAggregate *node)
2559 {
2560 TInfoSinkBase &out = getInfoSink();
2561
2562 switch (node->getOp())
2563 {
2564 case EOpCallFunctionInAST:
2565 case EOpCallInternalRawFunction:
2566 default:
2567 {
2568 TIntermSequence *arguments = node->getSequence();
2569
2570 bool lod0 = (mInsideDiscontinuousLoop || mOutputLod0Function) &&
2571 mShaderType == GL_FRAGMENT_SHADER;
2572
2573 // No raw function is expected.
2574 ASSERT(node->getOp() != EOpCallInternalRawFunction);
2575
2576 if (node->getOp() == EOpCallFunctionInAST)
2577 {
2578 if (node->isArray())
2579 {
2580 UNIMPLEMENTED();
2581 }
2582 size_t index = mCallDag.findIndex(node->getFunction()->uniqueId());
2583 ASSERT(index != CallDAG::InvalidIndex);
2584 lod0 &= mASTMetadataList[index].mNeedsLod0;
2585
2586 out << DecorateFunctionIfNeeded(node->getFunction());
2587 out << DisambiguateFunctionName(node->getSequence());
2588 out << (lod0 ? "Lod0(" : "(");
2589 }
2590 else if (node->getFunction()->isImageFunction())
2591 {
2592 const ImmutableString &name = node->getFunction()->name();
2593 TType type = (*arguments)[0]->getAsTyped()->getType();
2594 const ImmutableString &imageFunctionName = mImageFunctionHLSL->useImageFunction(
2595 name, type.getBasicType(), type.getLayoutQualifier().imageInternalFormat,
2596 type.getMemoryQualifier().readonly);
2597 out << imageFunctionName << "(";
2598 }
2599 else if (node->getFunction()->isAtomicCounterFunction())
2600 {
2601 const ImmutableString &name = node->getFunction()->name();
2602 ImmutableString atomicFunctionName =
2603 mAtomicCounterFunctionHLSL->useAtomicCounterFunction(name);
2604 out << atomicFunctionName << "(";
2605 }
2606 else
2607 {
2608 const ImmutableString &name = node->getFunction()->name();
2609 TBasicType samplerType = (*arguments)[0]->getAsTyped()->getType().getBasicType();
2610 int coords = 0; // textureSize(gsampler2DMS) doesn't have a second argument.
2611 if (arguments->size() > 1)
2612 {
2613 coords = (*arguments)[1]->getAsTyped()->getNominalSize();
2614 }
2615 const ImmutableString &textureFunctionName =
2616 mTextureFunctionHLSL->useTextureFunction(name, samplerType, coords,
2617 arguments->size(), lod0, mShaderType);
2618 out << textureFunctionName << "(";
2619 }
2620
2621 for (TIntermSequence::iterator arg = arguments->begin(); arg != arguments->end(); arg++)
2622 {
2623 TIntermTyped *typedArg = (*arg)->getAsTyped();
2624 if (mOutputType == SH_HLSL_4_0_FL9_3_OUTPUT && IsSampler(typedArg->getBasicType()))
2625 {
2626 out << "texture_";
2627 (*arg)->traverse(this);
2628 out << ", sampler_";
2629 }
2630
2631 (*arg)->traverse(this);
2632
2633 if (typedArg->getType().isStructureContainingSamplers())
2634 {
2635 const TType &argType = typedArg->getType();
2636 TVector<const TVariable *> samplerSymbols;
2637 ImmutableString structName = samplerNamePrefixFromStruct(typedArg);
2638 std::string namePrefix = "angle_";
2639 namePrefix += structName.data();
2640 argType.createSamplerSymbols(ImmutableString(namePrefix), "", &samplerSymbols,
2641 nullptr, mSymbolTable);
2642 for (const TVariable *sampler : samplerSymbols)
2643 {
2644 if (mOutputType == SH_HLSL_4_0_FL9_3_OUTPUT)
2645 {
2646 out << ", texture_" << sampler->name();
2647 out << ", sampler_" << sampler->name();
2648 }
2649 else
2650 {
2651 // In case of HLSL 4.1+, this symbol is the sampler index, and in case
2652 // of D3D9, it's the sampler variable.
2653 out << ", " << sampler->name();
2654 }
2655 }
2656 }
2657
2658 if (arg < arguments->end() - 1)
2659 {
2660 out << ", ";
2661 }
2662 }
2663
2664 out << ")";
2665
2666 return false;
2667 }
2668 case EOpConstruct:
2669 outputConstructor(out, visit, node);
2670 break;
2671 case EOpEqualComponentWise:
2672 outputTriplet(out, visit, "(", " == ", ")");
2673 break;
2674 case EOpNotEqualComponentWise:
2675 outputTriplet(out, visit, "(", " != ", ")");
2676 break;
2677 case EOpLessThanComponentWise:
2678 outputTriplet(out, visit, "(", " < ", ")");
2679 break;
2680 case EOpGreaterThanComponentWise:
2681 outputTriplet(out, visit, "(", " > ", ")");
2682 break;
2683 case EOpLessThanEqualComponentWise:
2684 outputTriplet(out, visit, "(", " <= ", ")");
2685 break;
2686 case EOpGreaterThanEqualComponentWise:
2687 outputTriplet(out, visit, "(", " >= ", ")");
2688 break;
2689 case EOpMod:
2690 ASSERT(node->getUseEmulatedFunction());
2691 writeEmulatedFunctionTriplet(out, visit, node->getFunction());
2692 break;
2693 case EOpModf:
2694 outputTriplet(out, visit, "modf(", ", ", ")");
2695 break;
2696 case EOpPow:
2697 outputTriplet(out, visit, "pow(", ", ", ")");
2698 break;
2699 case EOpAtan:
2700 ASSERT(node->getSequence()->size() == 2); // atan(x) is a unary operator
2701 ASSERT(node->getUseEmulatedFunction());
2702 writeEmulatedFunctionTriplet(out, visit, node->getFunction());
2703 break;
2704 case EOpMin:
2705 outputTriplet(out, visit, "min(", ", ", ")");
2706 break;
2707 case EOpMax:
2708 outputTriplet(out, visit, "max(", ", ", ")");
2709 break;
2710 case EOpClamp:
2711 outputTriplet(out, visit, "clamp(", ", ", ")");
2712 break;
2713 case EOpMix:
2714 {
2715 TIntermTyped *lastParamNode = (*(node->getSequence()))[2]->getAsTyped();
2716 if (lastParamNode->getType().getBasicType() == EbtBool)
2717 {
2718 // There is no HLSL equivalent for ESSL3 built-in "genType mix (genType x, genType
2719 // y, genBType a)",
2720 // so use emulated version.
2721 ASSERT(node->getUseEmulatedFunction());
2722 writeEmulatedFunctionTriplet(out, visit, node->getFunction());
2723 }
2724 else
2725 {
2726 outputTriplet(out, visit, "lerp(", ", ", ")");
2727 }
2728 break;
2729 }
2730 case EOpStep:
2731 outputTriplet(out, visit, "step(", ", ", ")");
2732 break;
2733 case EOpSmoothstep:
2734 outputTriplet(out, visit, "smoothstep(", ", ", ")");
2735 break;
2736 case EOpFma:
2737 outputTriplet(out, visit, "mad(", ", ", ")");
2738 break;
2739 case EOpFrexp:
2740 case EOpLdexp:
2741 ASSERT(node->getUseEmulatedFunction());
2742 writeEmulatedFunctionTriplet(out, visit, node->getFunction());
2743 break;
2744 case EOpDistance:
2745 outputTriplet(out, visit, "distance(", ", ", ")");
2746 break;
2747 case EOpDot:
2748 outputTriplet(out, visit, "dot(", ", ", ")");
2749 break;
2750 case EOpCross:
2751 outputTriplet(out, visit, "cross(", ", ", ")");
2752 break;
2753 case EOpFaceforward:
2754 ASSERT(node->getUseEmulatedFunction());
2755 writeEmulatedFunctionTriplet(out, visit, node->getFunction());
2756 break;
2757 case EOpReflect:
2758 outputTriplet(out, visit, "reflect(", ", ", ")");
2759 break;
2760 case EOpRefract:
2761 outputTriplet(out, visit, "refract(", ", ", ")");
2762 break;
2763 case EOpOuterProduct:
2764 ASSERT(node->getUseEmulatedFunction());
2765 writeEmulatedFunctionTriplet(out, visit, node->getFunction());
2766 break;
2767 case EOpMatrixCompMult:
2768 outputTriplet(out, visit, "(", " * ", ")");
2769 break;
2770 case EOpBitfieldExtract:
2771 case EOpBitfieldInsert:
2772 case EOpUaddCarry:
2773 case EOpUsubBorrow:
2774 case EOpUmulExtended:
2775 case EOpImulExtended:
2776 ASSERT(node->getUseEmulatedFunction());
2777 writeEmulatedFunctionTriplet(out, visit, node->getFunction());
2778 break;
2779 case EOpDFdx:
2780 if (mInsideDiscontinuousLoop || mOutputLod0Function)
2781 {
2782 outputTriplet(out, visit, "(", "", ", 0.0)");
2783 }
2784 else
2785 {
2786 outputTriplet(out, visit, "ddx(", "", ")");
2787 }
2788 break;
2789 case EOpDFdy:
2790 if (mInsideDiscontinuousLoop || mOutputLod0Function)
2791 {
2792 outputTriplet(out, visit, "(", "", ", 0.0)");
2793 }
2794 else
2795 {
2796 outputTriplet(out, visit, "ddy(", "", ")");
2797 }
2798 break;
2799 case EOpFwidth:
2800 if (mInsideDiscontinuousLoop || mOutputLod0Function)
2801 {
2802 outputTriplet(out, visit, "(", "", ", 0.0)");
2803 }
2804 else
2805 {
2806 outputTriplet(out, visit, "fwidth(", "", ")");
2807 }
2808 break;
2809 case EOpInterpolateAtCentroid:
2810 {
2811 TIntermTyped *interpolantNode = (*(node->getSequence()))[0]->getAsTyped();
2812 if (!IsFlatInterpolant(interpolantNode))
2813 {
2814 outputTriplet(out, visit, "EvaluateAttributeCentroid(", "", ")");
2815 }
2816 break;
2817 }
2818 case EOpInterpolateAtSample:
2819 {
2820 TIntermTyped *interpolantNode = (*(node->getSequence()))[0]->getAsTyped();
2821 if (!IsFlatInterpolant(interpolantNode))
2822 {
2823 mUsesNumSamples = true;
2824 outputTriplet(out, visit, "EvaluateAttributeAtSample(", ", clamp(",
2825 ", 0, gl_NumSamples - 1))");
2826 }
2827 else
2828 {
2829 const TString &functionName = addFlatEvaluateFunction(
2830 interpolantNode->getType(), *StaticType::GetBasic<EbtInt, EbpUndefined, 1>());
2831 outputTriplet(out, visit, (functionName + "(").c_str(), ", ", ")");
2832 }
2833 break;
2834 }
2835 case EOpInterpolateAtOffset:
2836 {
2837 TIntermTyped *interpolantNode = (*(node->getSequence()))[0]->getAsTyped();
2838 if (!IsFlatInterpolant(interpolantNode))
2839 {
2840 outputTriplet(out, visit, "EvaluateAttributeSnapped(", ", int2((", ") * 16.0))");
2841 }
2842 else
2843 {
2844 const TString &functionName = addFlatEvaluateFunction(
2845 interpolantNode->getType(), *StaticType::GetBasic<EbtFloat, EbpUndefined, 2>());
2846 outputTriplet(out, visit, (functionName + "(").c_str(), ", ", ")");
2847 }
2848 break;
2849 }
2850 case EOpBarrier:
2851 // barrier() is translated to GroupMemoryBarrierWithGroupSync(), which is the
2852 // cheapest *WithGroupSync() function, without any functionality loss, but
2853 // with the potential for severe performance loss.
2854 outputTriplet(out, visit, "GroupMemoryBarrierWithGroupSync(", "", ")");
2855 break;
2856 case EOpMemoryBarrierShared:
2857 outputTriplet(out, visit, "GroupMemoryBarrier(", "", ")");
2858 break;
2859 case EOpMemoryBarrierAtomicCounter:
2860 case EOpMemoryBarrierBuffer:
2861 case EOpMemoryBarrierImage:
2862 outputTriplet(out, visit, "DeviceMemoryBarrier(", "", ")");
2863 break;
2864 case EOpGroupMemoryBarrier:
2865 case EOpMemoryBarrier:
2866 outputTriplet(out, visit, "AllMemoryBarrier(", "", ")");
2867 break;
2868
2869 // Single atomic function calls without return value.
2870 // e.g. atomicAdd(dest, value) should be translated into InterlockedAdd(dest, value).
2871 case EOpAtomicAdd:
2872 case EOpAtomicMin:
2873 case EOpAtomicMax:
2874 case EOpAtomicAnd:
2875 case EOpAtomicOr:
2876 case EOpAtomicXor:
2877 // The parameter 'original_value' of InterlockedExchange(dest, value, original_value)
2878 // and InterlockedCompareExchange(dest, compare_value, value, original_value) is not
2879 // optional.
2880 // https://docs.microsoft.com/en-us/windows/desktop/direct3dhlsl/interlockedexchange
2881 // https://docs.microsoft.com/en-us/windows/desktop/direct3dhlsl/interlockedcompareexchange
2882 // So all the call of atomicExchange(dest, value) and atomicCompSwap(dest,
2883 // compare_value, value) should all be modified into the form of "int temp; temp =
2884 // atomicExchange(dest, value);" and "int temp; temp = atomicCompSwap(dest,
2885 // compare_value, value);" in the intermediate tree before traversing outputHLSL.
2886 case EOpAtomicExchange:
2887 case EOpAtomicCompSwap:
2888 {
2889 ASSERT(node->getChildCount() > 1);
2890 TIntermTyped *memNode = (*node->getSequence())[0]->getAsTyped();
2891 if (IsInShaderStorageBlock(memNode))
2892 {
2893 // Atomic memory functions for SSBO.
2894 // "_ssbo_atomicXXX_TYPE(RWByteAddressBuffer buffer, uint loc" is written to |out|.
2895 mSSBOOutputHLSL->outputAtomicMemoryFunctionCallPrefix(memNode, node->getOp());
2896 // Write the rest argument list to |out|.
2897 for (size_t i = 1; i < node->getChildCount(); i++)
2898 {
2899 out << ", ";
2900 TIntermTyped *argument = (*node->getSequence())[i]->getAsTyped();
2901 if (IsInShaderStorageBlock(argument))
2902 {
2903 mSSBOOutputHLSL->outputLoadFunctionCall(argument);
2904 }
2905 else
2906 {
2907 argument->traverse(this);
2908 }
2909 }
2910
2911 out << ")";
2912 return false;
2913 }
2914 else
2915 {
2916 // Atomic memory functions for shared variable.
2917 if (node->getOp() != EOpAtomicExchange && node->getOp() != EOpAtomicCompSwap)
2918 {
2919 outputTriplet(out, visit,
2920 GetHLSLAtomicFunctionStringAndLeftParenthesis(node->getOp()), ",",
2921 ")");
2922 }
2923 else
2924 {
2925 UNREACHABLE();
2926 }
2927 }
2928
2929 break;
2930 }
2931 }
2932
2933 return true;
2934 }
2935
writeIfElse(TInfoSinkBase & out,TIntermIfElse * node)2936 void OutputHLSL::writeIfElse(TInfoSinkBase &out, TIntermIfElse *node)
2937 {
2938 out << "if (";
2939
2940 node->getCondition()->traverse(this);
2941
2942 out << ")\n";
2943
2944 outputLineDirective(out, node->getLine().first_line);
2945
2946 bool discard = false;
2947
2948 if (node->getTrueBlock())
2949 {
2950 // The trueBlock child node will output braces.
2951 node->getTrueBlock()->traverse(this);
2952
2953 // Detect true discard
2954 discard = (discard || FindDiscard::search(node->getTrueBlock()));
2955 }
2956 else
2957 {
2958 // TODO(oetuaho): Check if the semicolon inside is necessary.
2959 // It's there as a result of conservative refactoring of the output.
2960 out << "{;}\n";
2961 }
2962
2963 outputLineDirective(out, node->getLine().first_line);
2964
2965 if (node->getFalseBlock())
2966 {
2967 out << "else\n";
2968
2969 outputLineDirective(out, node->getFalseBlock()->getLine().first_line);
2970
2971 // The falseBlock child node will output braces.
2972 node->getFalseBlock()->traverse(this);
2973
2974 outputLineDirective(out, node->getFalseBlock()->getLine().first_line);
2975
2976 // Detect false discard
2977 discard = (discard || FindDiscard::search(node->getFalseBlock()));
2978 }
2979
2980 // ANGLE issue 486: Detect problematic conditional discard
2981 if (discard)
2982 {
2983 mUsesDiscardRewriting = true;
2984 }
2985 }
2986
visitTernary(Visit,TIntermTernary *)2987 bool OutputHLSL::visitTernary(Visit, TIntermTernary *)
2988 {
2989 // Ternary ops should have been already converted to something else in the AST. HLSL ternary
2990 // operator doesn't short-circuit, so it's not the same as the GLSL ternary operator.
2991 UNREACHABLE();
2992 return false;
2993 }
2994
visitIfElse(Visit visit,TIntermIfElse * node)2995 bool OutputHLSL::visitIfElse(Visit visit, TIntermIfElse *node)
2996 {
2997 TInfoSinkBase &out = getInfoSink();
2998
2999 ASSERT(mInsideFunction);
3000
3001 // D3D errors when there is a gradient operation in a loop in an unflattened if.
3002 if (mShaderType == GL_FRAGMENT_SHADER && mCurrentFunctionMetadata->hasGradientLoop(node))
3003 {
3004 out << "FLATTEN ";
3005 }
3006
3007 writeIfElse(out, node);
3008
3009 return false;
3010 }
3011
visitSwitch(Visit visit,TIntermSwitch * node)3012 bool OutputHLSL::visitSwitch(Visit visit, TIntermSwitch *node)
3013 {
3014 TInfoSinkBase &out = getInfoSink();
3015
3016 ASSERT(node->getStatementList());
3017 if (visit == PreVisit)
3018 {
3019 node->setStatementList(RemoveSwitchFallThrough(node->getStatementList(), mPerfDiagnostics));
3020 }
3021 outputTriplet(out, visit, "switch (", ") ", "");
3022 // The curly braces get written when visiting the statementList block.
3023 return true;
3024 }
3025
visitCase(Visit visit,TIntermCase * node)3026 bool OutputHLSL::visitCase(Visit visit, TIntermCase *node)
3027 {
3028 TInfoSinkBase &out = getInfoSink();
3029
3030 if (node->hasCondition())
3031 {
3032 outputTriplet(out, visit, "case (", "", "):\n");
3033 return true;
3034 }
3035 else
3036 {
3037 out << "default:\n";
3038 return false;
3039 }
3040 }
3041
visitConstantUnion(TIntermConstantUnion * node)3042 void OutputHLSL::visitConstantUnion(TIntermConstantUnion *node)
3043 {
3044 TInfoSinkBase &out = getInfoSink();
3045 writeConstantUnion(out, node->getType(), node->getConstantValue());
3046 }
3047
visitLoop(Visit visit,TIntermLoop * node)3048 bool OutputHLSL::visitLoop(Visit visit, TIntermLoop *node)
3049 {
3050 mNestedLoopDepth++;
3051
3052 bool wasDiscontinuous = mInsideDiscontinuousLoop;
3053 mInsideDiscontinuousLoop =
3054 mInsideDiscontinuousLoop || mCurrentFunctionMetadata->mDiscontinuousLoops.count(node) > 0;
3055
3056 TInfoSinkBase &out = getInfoSink();
3057
3058 if (mOutputType == SH_HLSL_3_0_OUTPUT)
3059 {
3060 if (handleExcessiveLoop(out, node))
3061 {
3062 mInsideDiscontinuousLoop = wasDiscontinuous;
3063 mNestedLoopDepth--;
3064
3065 return false;
3066 }
3067 }
3068
3069 const char *unroll = mCurrentFunctionMetadata->hasGradientInCallGraph(node) ? "LOOP" : "";
3070 if (node->getType() == ELoopDoWhile)
3071 {
3072 out << "{" << unroll << " do\n";
3073
3074 outputLineDirective(out, node->getLine().first_line);
3075 }
3076 else
3077 {
3078 out << "{" << unroll << " for(";
3079
3080 if (node->getInit())
3081 {
3082 node->getInit()->traverse(this);
3083 }
3084
3085 out << "; ";
3086
3087 if (node->getCondition())
3088 {
3089 node->getCondition()->traverse(this);
3090 }
3091
3092 out << "; ";
3093
3094 if (node->getExpression())
3095 {
3096 node->getExpression()->traverse(this);
3097 }
3098
3099 out << ")\n";
3100
3101 outputLineDirective(out, node->getLine().first_line);
3102 }
3103
3104 // The loop body node will output braces.
3105 node->getBody()->traverse(this);
3106
3107 outputLineDirective(out, node->getLine().first_line);
3108
3109 if (node->getType() == ELoopDoWhile)
3110 {
3111 outputLineDirective(out, node->getCondition()->getLine().first_line);
3112 out << "while (";
3113
3114 node->getCondition()->traverse(this);
3115
3116 out << ");\n";
3117 }
3118
3119 out << "}\n";
3120
3121 mInsideDiscontinuousLoop = wasDiscontinuous;
3122 mNestedLoopDepth--;
3123
3124 return false;
3125 }
3126
visitBranch(Visit visit,TIntermBranch * node)3127 bool OutputHLSL::visitBranch(Visit visit, TIntermBranch *node)
3128 {
3129 if (visit == PreVisit)
3130 {
3131 TInfoSinkBase &out = getInfoSink();
3132
3133 switch (node->getFlowOp())
3134 {
3135 case EOpKill:
3136 out << "discard";
3137 break;
3138 case EOpBreak:
3139 if (mNestedLoopDepth > 1)
3140 {
3141 mUsesNestedBreak = true;
3142 }
3143
3144 if (mExcessiveLoopIndex)
3145 {
3146 out << "{Break";
3147 mExcessiveLoopIndex->traverse(this);
3148 out << " = true; break;}\n";
3149 }
3150 else
3151 {
3152 out << "break";
3153 }
3154 break;
3155 case EOpContinue:
3156 out << "continue";
3157 break;
3158 case EOpReturn:
3159 if (node->getExpression())
3160 {
3161 ASSERT(!mInsideMain);
3162 out << "return ";
3163 if (IsInShaderStorageBlock(node->getExpression()))
3164 {
3165 mSSBOOutputHLSL->outputLoadFunctionCall(node->getExpression());
3166 return false;
3167 }
3168 }
3169 else
3170 {
3171 if (mInsideMain && shaderNeedsGenerateOutput())
3172 {
3173 out << "return " << generateOutputCall();
3174 }
3175 else
3176 {
3177 out << "return";
3178 }
3179 }
3180 break;
3181 default:
3182 UNREACHABLE();
3183 }
3184 }
3185
3186 return true;
3187 }
3188
3189 // Handle loops with more than 254 iterations (unsupported by D3D9) by splitting them
3190 // (The D3D documentation says 255 iterations, but the compiler complains at anything more than
3191 // 254).
handleExcessiveLoop(TInfoSinkBase & out,TIntermLoop * node)3192 bool OutputHLSL::handleExcessiveLoop(TInfoSinkBase &out, TIntermLoop *node)
3193 {
3194 const int MAX_LOOP_ITERATIONS = 254;
3195
3196 // Parse loops of the form:
3197 // for(int index = initial; index [comparator] limit; index += increment)
3198 TIntermSymbol *index = nullptr;
3199 TOperator comparator = EOpNull;
3200 int initial = 0;
3201 int limit = 0;
3202 int increment = 0;
3203
3204 // Parse index name and intial value
3205 if (node->getInit())
3206 {
3207 TIntermDeclaration *init = node->getInit()->getAsDeclarationNode();
3208
3209 if (init)
3210 {
3211 TIntermSequence *sequence = init->getSequence();
3212 TIntermTyped *variable = (*sequence)[0]->getAsTyped();
3213
3214 if (variable && variable->getQualifier() == EvqTemporary)
3215 {
3216 TIntermBinary *assign = variable->getAsBinaryNode();
3217
3218 if (assign != nullptr && assign->getOp() == EOpInitialize)
3219 {
3220 TIntermSymbol *symbol = assign->getLeft()->getAsSymbolNode();
3221 TIntermConstantUnion *constant = assign->getRight()->getAsConstantUnion();
3222
3223 if (symbol && constant)
3224 {
3225 if (constant->getBasicType() == EbtInt && constant->isScalar())
3226 {
3227 index = symbol;
3228 initial = constant->getIConst(0);
3229 }
3230 }
3231 }
3232 }
3233 }
3234 }
3235
3236 // Parse comparator and limit value
3237 if (index != nullptr && node->getCondition())
3238 {
3239 TIntermBinary *test = node->getCondition()->getAsBinaryNode();
3240
3241 if (test && test->getLeft()->getAsSymbolNode()->uniqueId() == index->uniqueId())
3242 {
3243 TIntermConstantUnion *constant = test->getRight()->getAsConstantUnion();
3244
3245 if (constant)
3246 {
3247 if (constant->getBasicType() == EbtInt && constant->isScalar())
3248 {
3249 comparator = test->getOp();
3250 limit = constant->getIConst(0);
3251 }
3252 }
3253 }
3254 }
3255
3256 // Parse increment
3257 if (index != nullptr && comparator != EOpNull && node->getExpression())
3258 {
3259 TIntermBinary *binaryTerminal = node->getExpression()->getAsBinaryNode();
3260 TIntermUnary *unaryTerminal = node->getExpression()->getAsUnaryNode();
3261
3262 if (binaryTerminal)
3263 {
3264 TOperator op = binaryTerminal->getOp();
3265 TIntermConstantUnion *constant = binaryTerminal->getRight()->getAsConstantUnion();
3266
3267 if (constant)
3268 {
3269 if (constant->getBasicType() == EbtInt && constant->isScalar())
3270 {
3271 int value = constant->getIConst(0);
3272
3273 switch (op)
3274 {
3275 case EOpAddAssign:
3276 increment = value;
3277 break;
3278 case EOpSubAssign:
3279 increment = -value;
3280 break;
3281 default:
3282 UNIMPLEMENTED();
3283 }
3284 }
3285 }
3286 }
3287 else if (unaryTerminal)
3288 {
3289 TOperator op = unaryTerminal->getOp();
3290
3291 switch (op)
3292 {
3293 case EOpPostIncrement:
3294 increment = 1;
3295 break;
3296 case EOpPostDecrement:
3297 increment = -1;
3298 break;
3299 case EOpPreIncrement:
3300 increment = 1;
3301 break;
3302 case EOpPreDecrement:
3303 increment = -1;
3304 break;
3305 default:
3306 UNIMPLEMENTED();
3307 }
3308 }
3309 }
3310
3311 if (index != nullptr && comparator != EOpNull && increment != 0)
3312 {
3313 if (comparator == EOpLessThanEqual)
3314 {
3315 comparator = EOpLessThan;
3316 limit += 1;
3317 }
3318
3319 if (comparator == EOpLessThan)
3320 {
3321 int iterations = (limit - initial) / increment;
3322
3323 if (iterations <= MAX_LOOP_ITERATIONS)
3324 {
3325 return false; // Not an excessive loop
3326 }
3327
3328 TIntermSymbol *restoreIndex = mExcessiveLoopIndex;
3329 mExcessiveLoopIndex = index;
3330
3331 out << "{int ";
3332 index->traverse(this);
3333 out << ";\n"
3334 "bool Break";
3335 index->traverse(this);
3336 out << " = false;\n";
3337
3338 bool firstLoopFragment = true;
3339
3340 while (iterations > 0)
3341 {
3342 int clampedLimit = initial + increment * std::min(MAX_LOOP_ITERATIONS, iterations);
3343
3344 if (!firstLoopFragment)
3345 {
3346 out << "if (!Break";
3347 index->traverse(this);
3348 out << ") {\n";
3349 }
3350
3351 if (iterations <= MAX_LOOP_ITERATIONS) // Last loop fragment
3352 {
3353 mExcessiveLoopIndex = nullptr; // Stops setting the Break flag
3354 }
3355
3356 // for(int index = initial; index < clampedLimit; index += increment)
3357 const char *unroll =
3358 mCurrentFunctionMetadata->hasGradientInCallGraph(node) ? "LOOP" : "";
3359
3360 out << unroll << " for(";
3361 index->traverse(this);
3362 out << " = ";
3363 out << initial;
3364
3365 out << "; ";
3366 index->traverse(this);
3367 out << " < ";
3368 out << clampedLimit;
3369
3370 out << "; ";
3371 index->traverse(this);
3372 out << " += ";
3373 out << increment;
3374 out << ")\n";
3375
3376 outputLineDirective(out, node->getLine().first_line);
3377 out << "{\n";
3378
3379 node->getBody()->traverse(this);
3380
3381 outputLineDirective(out, node->getLine().first_line);
3382 out << ";}\n";
3383
3384 if (!firstLoopFragment)
3385 {
3386 out << "}\n";
3387 }
3388
3389 firstLoopFragment = false;
3390
3391 initial += MAX_LOOP_ITERATIONS * increment;
3392 iterations -= MAX_LOOP_ITERATIONS;
3393 }
3394
3395 out << "}";
3396
3397 mExcessiveLoopIndex = restoreIndex;
3398
3399 return true;
3400 }
3401 else
3402 UNIMPLEMENTED();
3403 }
3404
3405 return false; // Not handled as an excessive loop
3406 }
3407
outputTriplet(TInfoSinkBase & out,Visit visit,const char * preString,const char * inString,const char * postString)3408 void OutputHLSL::outputTriplet(TInfoSinkBase &out,
3409 Visit visit,
3410 const char *preString,
3411 const char *inString,
3412 const char *postString)
3413 {
3414 if (visit == PreVisit)
3415 {
3416 out << preString;
3417 }
3418 else if (visit == InVisit)
3419 {
3420 out << inString;
3421 }
3422 else if (visit == PostVisit)
3423 {
3424 out << postString;
3425 }
3426 }
3427
outputLineDirective(TInfoSinkBase & out,int line)3428 void OutputHLSL::outputLineDirective(TInfoSinkBase &out, int line)
3429 {
3430 if (mCompileOptions.lineDirectives && line > 0)
3431 {
3432 out << "\n";
3433 out << "#line " << line;
3434
3435 if (mSourcePath)
3436 {
3437 out << " \"" << mSourcePath << "\"";
3438 }
3439
3440 out << "\n";
3441 }
3442 }
3443
writeParameter(const TVariable * param,TInfoSinkBase & out)3444 void OutputHLSL::writeParameter(const TVariable *param, TInfoSinkBase &out)
3445 {
3446 const TType &type = param->getType();
3447 TQualifier qualifier = type.getQualifier();
3448
3449 TString nameStr = DecorateVariableIfNeeded(*param);
3450 ASSERT(nameStr != ""); // HLSL demands named arguments, also for prototypes
3451
3452 if (IsSampler(type.getBasicType()))
3453 {
3454 if (mOutputType == SH_HLSL_4_1_OUTPUT)
3455 {
3456 // Samplers are passed as indices to the sampler array.
3457 ASSERT(qualifier != EvqParamOut && qualifier != EvqParamInOut);
3458 out << "const uint " << nameStr << ArrayString(type);
3459 return;
3460 }
3461 if (mOutputType == SH_HLSL_4_0_FL9_3_OUTPUT)
3462 {
3463 out << QualifierString(qualifier) << " " << TextureString(type.getBasicType())
3464 << " texture_" << nameStr << ArrayString(type) << ", " << QualifierString(qualifier)
3465 << " " << SamplerString(type.getBasicType()) << " sampler_" << nameStr
3466 << ArrayString(type);
3467 return;
3468 }
3469 }
3470
3471 // If the parameter is an atomic counter, we need to add an extra parameter to keep track of the
3472 // buffer offset.
3473 if (IsAtomicCounter(type.getBasicType()))
3474 {
3475 out << QualifierString(qualifier) << " " << TypeString(type) << " " << nameStr << ", int "
3476 << nameStr << "_offset";
3477 }
3478 else
3479 {
3480 out << QualifierString(qualifier) << " " << TypeString(type) << " " << nameStr
3481 << ArrayString(type);
3482 }
3483
3484 // If the structure parameter contains samplers, they need to be passed into the function as
3485 // separate parameters. HLSL doesn't natively support samplers in structs.
3486 if (type.isStructureContainingSamplers())
3487 {
3488 ASSERT(qualifier != EvqParamOut && qualifier != EvqParamInOut);
3489 TVector<const TVariable *> samplerSymbols;
3490 std::string namePrefix = "angle";
3491 namePrefix += nameStr.c_str();
3492 type.createSamplerSymbols(ImmutableString(namePrefix), "", &samplerSymbols, nullptr,
3493 mSymbolTable);
3494 for (const TVariable *sampler : samplerSymbols)
3495 {
3496 const TType &samplerType = sampler->getType();
3497 if (mOutputType == SH_HLSL_4_1_OUTPUT)
3498 {
3499 out << ", const uint " << sampler->name() << ArrayString(samplerType);
3500 }
3501 else if (mOutputType == SH_HLSL_4_0_FL9_3_OUTPUT)
3502 {
3503 ASSERT(IsSampler(samplerType.getBasicType()));
3504 out << ", " << QualifierString(qualifier) << " "
3505 << TextureString(samplerType.getBasicType()) << " texture_" << sampler->name()
3506 << ArrayString(samplerType) << ", " << QualifierString(qualifier) << " "
3507 << SamplerString(samplerType.getBasicType()) << " sampler_" << sampler->name()
3508 << ArrayString(samplerType);
3509 }
3510 else
3511 {
3512 ASSERT(IsSampler(samplerType.getBasicType()));
3513 out << ", " << QualifierString(qualifier) << " " << TypeString(samplerType) << " "
3514 << sampler->name() << ArrayString(samplerType);
3515 }
3516 }
3517 }
3518 }
3519
zeroInitializer(const TType & type) const3520 TString OutputHLSL::zeroInitializer(const TType &type) const
3521 {
3522 TString string;
3523
3524 size_t size = type.getObjectSize();
3525 if (size >= kZeroCount)
3526 {
3527 mUseZeroArray = true;
3528 }
3529 string = GetZeroInitializer(size).c_str();
3530
3531 return "{" + string + "}";
3532 }
3533
outputConstructor(TInfoSinkBase & out,Visit visit,TIntermAggregate * node)3534 void OutputHLSL::outputConstructor(TInfoSinkBase &out, Visit visit, TIntermAggregate *node)
3535 {
3536 // Array constructors should have been already pruned from the code.
3537 ASSERT(!node->getType().isArray());
3538
3539 if (visit == PreVisit)
3540 {
3541 TString constructorName;
3542 if (node->getBasicType() == EbtStruct)
3543 {
3544 constructorName = mStructureHLSL->addStructConstructor(*node->getType().getStruct());
3545 }
3546 else
3547 {
3548 constructorName =
3549 mStructureHLSL->addBuiltInConstructor(node->getType(), node->getSequence());
3550 }
3551 out << constructorName << "(";
3552 }
3553 else if (visit == InVisit)
3554 {
3555 out << ", ";
3556 }
3557 else if (visit == PostVisit)
3558 {
3559 out << ")";
3560 }
3561 }
3562
writeConstantUnion(TInfoSinkBase & out,const TType & type,const TConstantUnion * const constUnion)3563 const TConstantUnion *OutputHLSL::writeConstantUnion(TInfoSinkBase &out,
3564 const TType &type,
3565 const TConstantUnion *const constUnion)
3566 {
3567 ASSERT(!type.isArray());
3568
3569 const TConstantUnion *constUnionIterated = constUnion;
3570
3571 const TStructure *structure = type.getStruct();
3572 if (structure)
3573 {
3574 out << mStructureHLSL->addStructConstructor(*structure) << "(";
3575
3576 const TFieldList &fields = structure->fields();
3577
3578 for (size_t i = 0; i < fields.size(); i++)
3579 {
3580 const TType *fieldType = fields[i]->type();
3581 constUnionIterated = writeConstantUnion(out, *fieldType, constUnionIterated);
3582
3583 if (i != fields.size() - 1)
3584 {
3585 out << ", ";
3586 }
3587 }
3588
3589 out << ")";
3590 }
3591 else
3592 {
3593 size_t size = type.getObjectSize();
3594 bool writeType = size > 1;
3595
3596 if (writeType)
3597 {
3598 out << TypeString(type) << "(";
3599 }
3600 constUnionIterated = writeConstantUnionArray(out, constUnionIterated, size);
3601 if (writeType)
3602 {
3603 out << ")";
3604 }
3605 }
3606
3607 return constUnionIterated;
3608 }
3609
writeEmulatedFunctionTriplet(TInfoSinkBase & out,Visit visit,const TFunction * function)3610 void OutputHLSL::writeEmulatedFunctionTriplet(TInfoSinkBase &out,
3611 Visit visit,
3612 const TFunction *function)
3613 {
3614 if (visit == PreVisit)
3615 {
3616 ASSERT(function != nullptr);
3617 BuiltInFunctionEmulator::WriteEmulatedFunctionName(out, function->name().data());
3618 out << "(";
3619 }
3620 else
3621 {
3622 outputTriplet(out, visit, nullptr, ", ", ")");
3623 }
3624 }
3625
writeSameSymbolInitializer(TInfoSinkBase & out,TIntermSymbol * symbolNode,TIntermTyped * expression)3626 bool OutputHLSL::writeSameSymbolInitializer(TInfoSinkBase &out,
3627 TIntermSymbol *symbolNode,
3628 TIntermTyped *expression)
3629 {
3630 ASSERT(symbolNode->variable().symbolType() != SymbolType::Empty);
3631 const TIntermSymbol *symbolInInitializer = FindSymbolNode(expression, symbolNode->getName());
3632
3633 if (symbolInInitializer)
3634 {
3635 // Type already printed
3636 out << "t" + str(mUniqueIndex) + " = ";
3637 expression->traverse(this);
3638 out << ", ";
3639 symbolNode->traverse(this);
3640 out << " = t" + str(mUniqueIndex);
3641
3642 mUniqueIndex++;
3643 return true;
3644 }
3645
3646 return false;
3647 }
3648
writeConstantInitialization(TInfoSinkBase & out,TIntermSymbol * symbolNode,TIntermTyped * initializer)3649 bool OutputHLSL::writeConstantInitialization(TInfoSinkBase &out,
3650 TIntermSymbol *symbolNode,
3651 TIntermTyped *initializer)
3652 {
3653 if (initializer->hasConstantValue())
3654 {
3655 symbolNode->traverse(this);
3656 out << ArrayString(symbolNode->getType());
3657 out << " = {";
3658 writeConstantUnionArray(out, initializer->getConstantValue(),
3659 initializer->getType().getObjectSize());
3660 out << "}";
3661 return true;
3662 }
3663 return false;
3664 }
3665
addStructEqualityFunction(const TStructure & structure)3666 TString OutputHLSL::addStructEqualityFunction(const TStructure &structure)
3667 {
3668 const TFieldList &fields = structure.fields();
3669
3670 for (const auto &eqFunction : mStructEqualityFunctions)
3671 {
3672 if (eqFunction->structure == &structure)
3673 {
3674 return eqFunction->functionName;
3675 }
3676 }
3677
3678 const TString &structNameString = StructNameString(structure);
3679
3680 StructEqualityFunction *function = new StructEqualityFunction();
3681 function->structure = &structure;
3682 function->functionName = "angle_eq_" + structNameString;
3683
3684 TInfoSinkBase fnOut;
3685
3686 fnOut << "bool " << function->functionName << "(" << structNameString << " a, "
3687 << structNameString + " b)\n"
3688 << "{\n"
3689 " return ";
3690
3691 for (size_t i = 0; i < fields.size(); i++)
3692 {
3693 const TField *field = fields[i];
3694 const TType *fieldType = field->type();
3695
3696 const TString &fieldNameA = "a." + Decorate(field->name());
3697 const TString &fieldNameB = "b." + Decorate(field->name());
3698
3699 if (i > 0)
3700 {
3701 fnOut << " && ";
3702 }
3703
3704 fnOut << "(";
3705 outputEqual(PreVisit, *fieldType, EOpEqual, fnOut);
3706 fnOut << fieldNameA;
3707 outputEqual(InVisit, *fieldType, EOpEqual, fnOut);
3708 fnOut << fieldNameB;
3709 outputEqual(PostVisit, *fieldType, EOpEqual, fnOut);
3710 fnOut << ")";
3711 }
3712
3713 fnOut << ";\n" << "}\n";
3714
3715 function->functionDefinition = fnOut.c_str();
3716
3717 mStructEqualityFunctions.push_back(function);
3718 mEqualityFunctions.push_back(function);
3719
3720 return function->functionName;
3721 }
3722
addArrayEqualityFunction(const TType & type)3723 TString OutputHLSL::addArrayEqualityFunction(const TType &type)
3724 {
3725 for (const auto &eqFunction : mArrayEqualityFunctions)
3726 {
3727 if (eqFunction->type == type)
3728 {
3729 return eqFunction->functionName;
3730 }
3731 }
3732
3733 TType elementType(type);
3734 elementType.toArrayElementType();
3735
3736 ArrayHelperFunction *function = new ArrayHelperFunction();
3737 function->type = type;
3738
3739 function->functionName = ArrayHelperFunctionName("angle_eq", type);
3740
3741 TInfoSinkBase fnOut;
3742
3743 const TString &typeName = TypeString(type);
3744 fnOut << "bool " << function->functionName << "(" << typeName << " a" << ArrayString(type)
3745 << ", " << typeName << " b" << ArrayString(type) << ")\n"
3746 << "{\n"
3747 " for (int i = 0; i < "
3748 << type.getOutermostArraySize()
3749 << "; ++i)\n"
3750 " {\n"
3751 " if (";
3752
3753 outputEqual(PreVisit, elementType, EOpNotEqual, fnOut);
3754 fnOut << "a[i]";
3755 outputEqual(InVisit, elementType, EOpNotEqual, fnOut);
3756 fnOut << "b[i]";
3757 outputEqual(PostVisit, elementType, EOpNotEqual, fnOut);
3758
3759 fnOut << ") { return false; }\n"
3760 " }\n"
3761 " return true;\n"
3762 "}\n";
3763
3764 function->functionDefinition = fnOut.c_str();
3765
3766 mArrayEqualityFunctions.push_back(function);
3767 mEqualityFunctions.push_back(function);
3768
3769 return function->functionName;
3770 }
3771
addArrayAssignmentFunction(const TType & type)3772 TString OutputHLSL::addArrayAssignmentFunction(const TType &type)
3773 {
3774 for (const auto &assignFunction : mArrayAssignmentFunctions)
3775 {
3776 if (assignFunction.type == type)
3777 {
3778 return assignFunction.functionName;
3779 }
3780 }
3781
3782 TType elementType(type);
3783 elementType.toArrayElementType();
3784
3785 ArrayHelperFunction function;
3786 function.type = type;
3787
3788 function.functionName = ArrayHelperFunctionName("angle_assign", type);
3789
3790 TInfoSinkBase fnOut;
3791
3792 const TString &typeName = TypeString(type);
3793 fnOut << "void " << function.functionName << "(out " << typeName << " a" << ArrayString(type)
3794 << ", " << typeName << " b" << ArrayString(type) << ")\n"
3795 << "{\n"
3796 " for (int i = 0; i < "
3797 << type.getOutermostArraySize()
3798 << "; ++i)\n"
3799 " {\n"
3800 " ";
3801
3802 outputAssign(PreVisit, elementType, fnOut);
3803 fnOut << "a[i]";
3804 outputAssign(InVisit, elementType, fnOut);
3805 fnOut << "b[i]";
3806 outputAssign(PostVisit, elementType, fnOut);
3807
3808 fnOut << ";\n"
3809 " }\n"
3810 "}\n";
3811
3812 function.functionDefinition = fnOut.c_str();
3813
3814 mArrayAssignmentFunctions.push_back(function);
3815
3816 return function.functionName;
3817 }
3818
addArrayConstructIntoFunction(const TType & type)3819 TString OutputHLSL::addArrayConstructIntoFunction(const TType &type)
3820 {
3821 for (const auto &constructIntoFunction : mArrayConstructIntoFunctions)
3822 {
3823 if (constructIntoFunction.type == type)
3824 {
3825 return constructIntoFunction.functionName;
3826 }
3827 }
3828
3829 TType elementType(type);
3830 elementType.toArrayElementType();
3831
3832 ArrayHelperFunction function;
3833 function.type = type;
3834
3835 function.functionName = ArrayHelperFunctionName("angle_construct_into", type);
3836
3837 TInfoSinkBase fnOut;
3838
3839 const TString &typeName = TypeString(type);
3840 fnOut << "void " << function.functionName << "(out " << typeName << " a" << ArrayString(type);
3841 for (unsigned int i = 0u; i < type.getOutermostArraySize(); ++i)
3842 {
3843 fnOut << ", " << typeName << " b" << i << ArrayString(elementType);
3844 }
3845 fnOut << ")\n"
3846 "{\n";
3847
3848 for (unsigned int i = 0u; i < type.getOutermostArraySize(); ++i)
3849 {
3850 fnOut << " ";
3851 outputAssign(PreVisit, elementType, fnOut);
3852 fnOut << "a[" << i << "]";
3853 outputAssign(InVisit, elementType, fnOut);
3854 fnOut << "b" << i;
3855 outputAssign(PostVisit, elementType, fnOut);
3856 fnOut << ";\n";
3857 }
3858 fnOut << "}\n";
3859
3860 function.functionDefinition = fnOut.c_str();
3861
3862 mArrayConstructIntoFunctions.push_back(function);
3863
3864 return function.functionName;
3865 }
3866
addFlatEvaluateFunction(const TType & type,const TType & parameterType)3867 TString OutputHLSL::addFlatEvaluateFunction(const TType &type, const TType ¶meterType)
3868 {
3869 for (const auto &flatEvaluateFunction : mFlatEvaluateFunctions)
3870 {
3871 if (flatEvaluateFunction.type == type &&
3872 flatEvaluateFunction.parameterType == parameterType)
3873 {
3874 return flatEvaluateFunction.functionName;
3875 }
3876 }
3877
3878 FlatEvaluateFunction function;
3879 function.type = type;
3880 function.parameterType = parameterType;
3881
3882 const TString &typeName = TypeString(type);
3883 const TString ¶meterTypeName = TypeString(parameterType);
3884
3885 function.functionName = "angle_eval_flat_" + typeName + "_" + parameterTypeName;
3886
3887 // If <interpolant> is declared with a "flat" qualifier, the interpolated
3888 // value will have the same value everywhere for a single primitive, so
3889 // the location used for the interpolation has no effect and the functions
3890 // just return that same value.
3891 TInfoSinkBase fnOut;
3892 fnOut << typeName << " " << function.functionName << "(" << typeName << " i, "
3893 << parameterTypeName << " p)\n";
3894 fnOut << "{\n" << " return i;\n" << "}\n";
3895 function.functionDefinition = fnOut.c_str();
3896
3897 mFlatEvaluateFunctions.push_back(function);
3898
3899 return function.functionName;
3900 }
3901
ensureStructDefined(const TType & type)3902 void OutputHLSL::ensureStructDefined(const TType &type)
3903 {
3904 const TStructure *structure = type.getStruct();
3905 if (structure)
3906 {
3907 ASSERT(type.getBasicType() == EbtStruct);
3908 mStructureHLSL->ensureStructDefined(*structure);
3909 }
3910 }
3911
shaderNeedsGenerateOutput() const3912 bool OutputHLSL::shaderNeedsGenerateOutput() const
3913 {
3914 return mShaderType == GL_VERTEX_SHADER || mShaderType == GL_FRAGMENT_SHADER;
3915 }
3916
generateOutputCall() const3917 const char *OutputHLSL::generateOutputCall() const
3918 {
3919 if (mShaderType == GL_VERTEX_SHADER)
3920 {
3921 return "generateOutput(input)";
3922 }
3923 else
3924 {
3925 return "generateOutput()";
3926 }
3927 }
3928 } // namespace sh
3929