using System.Collections; using System.Collections.Generic; using UnityEngine; using UnityEngine.Assertions; using System; using UnityEditor.ShaderGraph; using UnityEditor.ShaderGraph.Internal; namespace UnityEditor.Rendering.HighDefinition { internal class HlslGenerator { internal HlslGenerator() { } Dictionary func1s; Dictionary func2s; Dictionary func3s; Dictionary singleFuncs; Dictionary binaryFuncs; internal void Init(HlslTokenizer srcTokenizer, HlslUnityReserved srcUnityReserved, HlslTree srcTree, bool emulate) { tokenizer = srcTokenizer; unityReserved = srcUnityReserved; tree = srcTree; isValid = false; tokenToNativeTable = HlslParser.GenerateTokenToNativeTable(); apdWriter = new PartialDerivUtilWriter(); applyEmulatedDerivatives = emulate; singleFuncs = new Dictionary(); singleFuncs.Add("saturate", SingleFunc.Saturate); singleFuncs.Add("rcp", SingleFunc.Rcp); singleFuncs.Add("log", SingleFunc.Log); singleFuncs.Add("log2", SingleFunc.Log2); singleFuncs.Add("log10", SingleFunc.Log10); singleFuncs.Add("exp", SingleFunc.Exp); singleFuncs.Add("exp2", SingleFunc.Exp2); singleFuncs.Add("sqrt", SingleFunc.Sqrt); singleFuncs.Add("rsqrt", SingleFunc.Rsqrt); singleFuncs.Add("normalize", SingleFunc.Normalize); singleFuncs.Add("frac", SingleFunc.Frac); singleFuncs.Add("cos", SingleFunc.Cos); singleFuncs.Add("cosh", SingleFunc.CosH); singleFuncs.Add("sin", SingleFunc.Sin); singleFuncs.Add("sinh", SingleFunc.SinH); singleFuncs.Add("tan", SingleFunc.Tan); singleFuncs.Add("tanh", SingleFunc.TanH); singleFuncs.Add("abs", SingleFunc.Abs); singleFuncs.Add("floor", SingleFunc.Floor); singleFuncs.Add("ceil", SingleFunc.Ceil); binaryFuncs = new Dictionary(); binaryFuncs.Add("min", BinaryFunc.Min); binaryFuncs.Add("max", BinaryFunc.Max); binaryFuncs.Add("pow", BinaryFunc.Pow); binaryFuncs.Add("reflect", BinaryFunc.Reflect); func1s = new Dictionary(); func1s.Add("length", Func1.Len); func2s = new Dictionary(); func2s.Add("dot", Func2.Dot); func2s.Add("cross", Func2.Cross); func3s = new Dictionary(); func3s.Add("lerp", Func3.Lerp); } internal void LogError(string error) { isValid = false; errList.Add(error); } string GetFunctionNameFromNodeId(int nodeId) { string ret = "(invalid)"; HlslTree.Node baseNode = tree.allNodes[nodeId]; if (baseNode is HlslTree.NodeFunctionPrototype nodeProto) { ret = tokenizer.GetTokenData(nodeProto.nameTokenId); } else if (baseNode is HlslTree.NodeFunctionPrototypeExternal nodeProtoExternal) { ret = nodeProtoExternal.protoInfo.identifier; } else if (baseNode is HlslTree.NodeToken nodeToken) { // if a function was used without a prototype being declared, we assume that this is a function with unknown parameters. ret = tokenizer.GetTokenData(nodeToken.srcTokenId); } else { // invalid prototype id HlslUtil.ParserAssert(false); } return ret; } // note: assumes the children are processed first // traverse through the nodes, and: // for each node // find all the children nodes // for each variable // find all the children nodes void FindChildrenFromParentsRecurse(List[] childList, List[] varibleList, int nodeId) { for (int nodeIter = 0; nodeIter < nodeParents.Length; nodeIter++) { int currParent = nodeParents[nodeIter]; if (currParent >= 0) { childList[currParent].Add(nodeIter); } } } void FindChildrenFromParents() { List[] childList = new List[tree.allNodes.Count]; List[] variableList = new List[tree.allNodes.Count]; for (int i = 0; i < childList.Length; i++) { childList[i] = new List(); } // now that we know the parent, create a helper list for children FindChildrenFromParentsRecurse(childList, variableList, tree.topLevelNode); nodeChildren = new int[tree.allNodes.Count][]; for (int i = 0; i < nodeChildren.Length; i++) { nodeChildren[i] = childList[i].ToArray(); } } void MarkApdNodesAndVariablesIsLegal(out bool[] isNodeApdLegalVec, out bool[] isVariableApdLegalVec) { int numNodes = tree.allNodes.Count; int numVariables = tree.allVariables.Count; isNodeApdLegalVec = new bool[numNodes]; isVariableApdLegalVec = new bool[numVariables]; for (int varIter = 0; varIter < numVariables; varIter++) { HlslTree.VariableInfo variable = tree.allVariables[varIter]; bool isLegal = HlslUtil.IsNativeTypeLegalForApd(variable.typeInfo.nativeType); isVariableApdLegalVec[varIter] = isLegal; } for (int nodeIter = 0; nodeIter < numNodes; nodeIter++) { HlslTree.Node baseNode = tree.allNodes[nodeIter]; HlslTree.FullTypeInfo fullType = tree.fullTypeInfo[nodeIter]; bool isLegal = false; if (baseNode is HlslTree.NodeTopLevel nodeTopLevel) { isLegal = false;// nope } else if (baseNode is HlslTree.NodeStruct nodeStruct) { // struct definitions are not allowed to be APD. The declarations // are child nodes which can be legl, but the actual struct is not. isLegal = false;// nope } else if (baseNode is HlslTree.NodeUnityStruct nodeUnityStruct) { isLegal = false;// nope } else if (baseNode is HlslTree.NodeFunctionPrototype nodeFunctionPrototype) { isLegal = true; } else if (baseNode is HlslTree.NodeFunctionPrototypeExternal nodeFunctionPrototypeExternal) { // we can modify user-defined function prototypes, but we can not modify builtin // function prototypes isLegal = false; } else if (baseNode is HlslTree.NodeBlock nodeBlock) { isLegal = true; // sure, we might change the return type to legal } else if (baseNode is HlslTree.NodeStatement nodeStatement) { isLegal = true; } else if (baseNode is HlslTree.NodeFunction nodeFunction) { // when a function converts to APD, we're only changing the return type. however, all of // the child nodes in the function->prototype->declarations can be APD isLegal = HlslUtil.IsNativeTypeLegalForApd(fullType.nativeType); } else if (baseNode is HlslTree.NodeDeclaration nodeDeclaration) { // is it able to become apd? depends on the type. isLegal = HlslUtil.IsNativeTypeLegalForApd(fullType.nativeType); } else if (baseNode is HlslTree.NodeVariable nodeVariable) { // always depends on type isLegal = HlslUtil.IsNativeTypeLegalForApd(fullType.nativeType); } else if (baseNode is HlslTree.NodeNativeConstructor nodeNativeConstructor) { // constructors can be changed as long as the native type is derivativeable isLegal = HlslUtil.IsNativeTypeLegalForApd(fullType.nativeType); } else if (baseNode is HlslTree.NodeToken nodeToken) { // a token is a token. while we can coerce the format after, we can't change the original token. isLegal = false; } else if (baseNode is HlslTree.NodeNativeType nodeNativeType) { // native types can not be promoted isLegal = false; } else if (baseNode is HlslTree.NodeLiteralOrBool nodeLiteralOrBool) { // constants can be promoted, as long as they are not bools or ints isLegal = false; } else if (baseNode is HlslTree.NodePassthrough nodePassthrough) { // we are passing through the text without understanding it, so no type isLegal = false; } else if (baseNode is HlslTree.NodeExpression nodeExpression) { switch (nodeExpression.op) { case HlslOp.Invalid: // case HlslOp.ScopeReslution: // :: case HlslOp.LogicalNot: // ! case HlslOp.BitwiseNot: // ~ case HlslOp.CStyleCast: // (int) case HlslOp.Dereference: // * - not legal in HLSL? case HlslOp.AddressOf: // & - not legal in HLSL? case HlslOp.Sizeof: // sizeof case HlslOp.PointerToMemberDot: // .* - not legal in HLSL case HlslOp.PointerToMemorArrow: // ->* - not legal in HLSL case HlslOp.Mod: // % case HlslOp.ShiftL: // << case HlslOp.ShiftR: // >> case HlslOp.ThreeWayCompare: // <=> - never used this case HlslOp.LessThan: // < case HlslOp.GreaterThan: // > case HlslOp.LessEqual: // <= case HlslOp.GreaterEqual: // >= case HlslOp.CompareEqual: // == case HlslOp.NotEqual: // != case HlslOp.BitwiseAnd: // & case HlslOp.BitwiseXor: // ^ case HlslOp.BitwiseOr: // | case HlslOp.LogicalAnd: // && case HlslOp.LogicalOr: // || case HlslOp.ModEquals: // %= case HlslOp.ShiftLEquals: // <<= case HlslOp.ShiftREquals: // >>= case HlslOp.AndEquals: // &= case HlslOp.XorEquals: // ^= case HlslOp.OrEquals: // |= isLegal = false; break; case HlslOp.PostIncrement: // ++ case HlslOp.PostDecrement: // -- case HlslOp.FunctionalCast: // int() case HlslOp.FunctionalCall: // func() case HlslOp.Subscript: // [] case HlslOp.MemberAccess: // . case HlslOp.PreIncrement: // ++ case HlslOp.PreDecrement: // -- case HlslOp.UnaryPlus: // + case HlslOp.UnaryMinus: // - case HlslOp.Mul: // * case HlslOp.Div: // / case HlslOp.Add: // + case HlslOp.Sub: // - case HlslOp.TernaryQuestion: // ? case HlslOp.TernaryColon: // : case HlslOp.Assignment: // = case HlslOp.AddEquals: // += case HlslOp.SubEquals: // -= case HlslOp.MulEquals: // *= case HlslOp.DivEquals: // /= case HlslOp.Comma: // : isLegal = HlslUtil.IsNativeTypeLegalForApd(fullType.nativeType); break; default: HlslUtil.ParserAssert(false); break; } } else if (baseNode is HlslTree.NodeMemberVariable nodeMemberVariable) { bool isUnityBuiltin = tree.IsStructUnityBuiltin(nodeMemberVariable.structNodeId); if (isUnityBuiltin) { isLegal = false; } else { isLegal = HlslUtil.IsNativeTypeLegalForApd(fullType.nativeType); } } else if (baseNode is HlslTree.NodeParenthesisGroup nodeParenthesisGroup) { isLegal = HlslUtil.IsNativeTypeLegalForApd(fullType.nativeType); } else if (baseNode is HlslTree.NodeMemberFunction nodeMemberFunction) { bool isUnityBuiltin = tree.IsStructUnityBuiltin(nodeMemberFunction.structNodeId); if (isUnityBuiltin) { isLegal = false; } else { isLegal = HlslUtil.IsNativeTypeLegalForApd(fullType.nativeType); } } else if (baseNode is HlslTree.NodeSwizzle nodeSwizzle) { isLegal = HlslUtil.IsNativeTypeLegalForApd(fullType.nativeType); } else if (baseNode is HlslTree.NodeBlockInitializer nodeblockInit) { isLegal = false; // block initializers are forbidden from being apd } else { // if we got here, then we are missing a type in this if tree HlslUtil.ParserAssert(false); } isNodeApdLegalVec[nodeIter] = isLegal; } } void MarkApdSinks(out bool[] isNodeApdSink) { int numNodes = tree.allNodes.Count; // initialized to false isNodeApdSink = new bool[numNodes]; for (int i = 0; i < numNodes; i++) { HlslTree.Node node = tree.allNodes[i]; if (node is HlslTree.NodeExpression nodeExpression) { if (nodeExpression.op == HlslOp.FunctionalCall) { // for a function, lhs is the function name and paramIds[] are the parameters HlslTree.Node lhsNode = tree.allNodes[nodeExpression.lhsNodeId]; // rhs node can either be a user function or an external function. The only // true sinks are from external functions (such as SAMPLE_TEXTURE2D) if (lhsNode is HlslTree.NodeFunctionPrototypeExternal nodeExternal) { for (int paramIter = 0; paramIter < nodeExternal.protoInfo.paramInfoVec.Length; paramIter++) { HlslParser.TypeInfo typeInfo = nodeExternal.protoInfo.paramInfoVec[paramIter]; if (typeInfo.allowedState == ApdAllowedState.OnlyApd) { int sinkNodeId = nodeExpression.paramIds[paramIter]; isNodeApdSink[sinkNodeId] = true; } } } } else if (nodeExpression.op == HlslOp.MemberAccess) { // for a member acces, the lhs op is variable and the rhs is the member HlslTree.Node rhsNode = tree.allNodes[nodeExpression.rhsNodeId]; // if it's a member function, it could be a texture sample if (rhsNode is HlslTree.NodeMemberFunction nodeMemberFunction) { // todo } } } } } static ApdStatus MergeOpApdStatus(ApdStatus lhs, ApdStatus rhs) { ApdStatus ret = ApdStatus.Unknown; if (lhs == ApdStatus.Valid || rhs == ApdStatus.Valid) { ret = ApdStatus.Valid; } else if (lhs == ApdStatus.Invalid || rhs == ApdStatus.Invalid) { ret = ApdStatus.Invalid; } else if (lhs == ApdStatus.Zero || rhs == ApdStatus.Zero) { ret = ApdStatus.Zero; } else { ret = ApdStatus.Unknown; } return ret; } ApdStatus GetNodeApdStatusForExpression(HlslApdInfo apdInfo, int nodeIdx) { ApdStatus ret = ApdStatus.Unknown; HlslTree.Node baseNode = tree.allNodes[nodeIdx]; HlslUtil.ParserAssert(baseNode is HlslTree.NodeExpression); HlslTree.NodeExpression nodeExpression = (HlslTree.NodeExpression)baseNode; switch (nodeExpression.op) { // these opssimply accept the lhs case HlslOp.PostIncrement: // ++ case HlslOp.PostDecrement: // -- case HlslOp.PreIncrement: // ++ case HlslOp.PreDecrement: // -- case HlslOp.UnaryPlus: // + case HlslOp.UnaryMinus: // - ret = apdInfo.nodeApdStatus[nodeExpression.lhsNodeId]; break; // shift, bitwise, logical, and mod operations are invalid case HlslOp.LogicalNot: // ! case HlslOp.BitwiseNot: // ~ case HlslOp.ShiftL: // << case HlslOp.ShiftR: // >> case HlslOp.ShiftLEquals: // <<= case HlslOp.ShiftREquals: // >>= case HlslOp.AndEquals: // &= case HlslOp.XorEquals: // ^= case HlslOp.OrEquals: // |= case HlslOp.BitwiseAnd: // & case HlslOp.BitwiseXor: // ^ case HlslOp.BitwiseOr: // | case HlslOp.LessThan: // < case HlslOp.GreaterThan: // > case HlslOp.LessEqual: // <= case HlslOp.GreaterEqual: // >= case HlslOp.CompareEqual: // == case HlslOp.NotEqual: // != case HlslOp.LogicalAnd: // && case HlslOp.LogicalOr: // || case HlslOp.ModEquals: // %= case HlslOp.Mod: // % ret = ApdStatus.Invalid; break; // for pure assignment, take the rhs case HlslOp.Assignment: // = ret = apdInfo.nodeApdStatus[nodeExpression.rhsNodeId]; break; // for binary ops merge the lhs and rhs together case HlslOp.Mul: // * case HlslOp.Div: // / case HlslOp.Add: // + case HlslOp.Sub: // - case HlslOp.AddEquals: // += case HlslOp.SubEquals: // -= case HlslOp.MulEquals: // *= case HlslOp.DivEquals: // /= { ApdStatus lhsStatus = apdInfo.nodeApdStatus[nodeExpression.lhsNodeId]; ApdStatus rhsStatus = apdInfo.nodeApdStatus[nodeExpression.rhsNodeId]; ret = MergeOpApdStatus(lhsStatus, rhsStatus); } break; // these ops are not supported case HlslOp.Invalid: // case HlslOp.ScopeReslution: // :: case HlslOp.Dereference: // * - not legal in HLSL? case HlslOp.AddressOf: // & - not legal in HLSL? case HlslOp.PointerToMemberDot: // .* - not legal in HLSL case HlslOp.PointerToMemorArrow: // ->* - not legal in HLSL case HlslOp.ThreeWayCompare: // <=> - never used this ret = ApdStatus.Invalid; break; // use the lhs type case HlslOp.Subscript: // [] ret = apdInfo.nodeApdStatus[nodeExpression.lhsNodeId]; break; // these ops are not supported now, but really should be at some point; case HlslOp.FunctionalCast: // int() ret = ApdStatus.Invalid; break; case HlslOp.FunctionalCall: // func() // lhs node is the function call prototype node id, so we actually just use lhs ret = apdInfo.nodeApdStatus[nodeExpression.lhsNodeId]; break; case HlslOp.MemberAccess: // . { HlslTree.Node nodeRhs = tree.allNodes[nodeExpression.rhsNodeId]; if (nodeRhs is HlslTree.NodeMemberVariable nodeMemberVariable) { // use this field ret = apdInfo.nodeApdStatus[nodeExpression.rhsNodeId]; } else if (nodeRhs is HlslTree.NodeMemberFunction nodeMemberFunction) { // use the return type of the member function ret = apdInfo.nodeApdStatus[nodeExpression.rhsNodeId]; } else if (nodeRhs is HlslTree.NodeSwizzle nodeSwizzle) { // the swizzle will not change the apd status of the lhs side of the expression ret = apdInfo.nodeApdStatus[nodeExpression.lhsNodeId]; } else { // should never happen, since these are the only 3 nodes allowed HlslUtil.ParserAssert(false); } } break; case HlslOp.CStyleCast: // (int) // get the struct id if (nodeExpression.cstyleCastStructId >= 0) { // if the cstyle structId is >= 0, then we use this struct HlslUtil.StructInfo structInfo = tree.GetStructInfoFromNodeId(nodeExpression.cstyleCastStructId, unityReserved); ret = ApdStatus.Invalid; } else { HlslToken tokenType = tokenizer.GetTokenType(nodeExpression.opTokenId); HlslNativeType nativeType = HlslNativeType._invalid; if (tokenToNativeTable.ContainsKey(tokenType)) { nativeType = tokenToNativeTable[tokenType]; } bool isApdLegal = HlslUtil.IsNativeTypeLegalForApd(nativeType); if (isApdLegal) { ret = apdInfo.nodeApdStatus[nodeExpression.lhsNodeId]; } else { ret = ApdStatus.Invalid; } } break; case HlslOp.Sizeof: // sizeof ret = ApdStatus.Invalid; break; case HlslOp.TernaryQuestion: // ? // treat this as the binary op of the middle and right terms? { ApdStatus lhsStatus = apdInfo.nodeApdStatus[nodeExpression.rhsNodeId]; ApdStatus rhsStatus = apdInfo.nodeApdStatus[nodeExpression.rhsNodeId]; ApdStatus rhsRhsStatus = apdInfo.nodeApdStatus[nodeExpression.rhsRhsNodeId]; ret = MergeOpApdStatus(MergeOpApdStatus(lhsStatus, rhsStatus), rhsRhsStatus); } break; case HlslOp.TernaryColon: // : // should never happen, since the three terms of the ternary are grouped into a single expression // with op as HlslOp.TernaryQuestion break; case HlslOp.Comma: // , // simply returns rhs ret = apdInfo.nodeApdStatus[nodeExpression.rhsNodeId]; break; default: // should never happen HlslUtil.ParserAssert(false); break; } return ret; } List FindAllReturnNodesInBlock(HlslApdInfo apdInfo, int nodeId) { List retNodes = new List(); HlslTree.Node node = tree.allNodes[nodeId]; HlslUtil.ParserAssert(node is HlslTree.NodeBlock); HlslTree.NodeBlock nodeBlock = (HlslTree.NodeBlock)node; // simple depth first search Stack nodeStack = new Stack(); for (int i = 0; i < nodeBlock.statements.Length; i++) { nodeStack.Push(nodeBlock.statements[i]); } while (nodeStack.Count > 0) { int topNodeId = nodeStack.Pop(); HlslTree.Node topNode = tree.allNodes[topNodeId]; if (topNode is HlslTree.NodeStatement topNodeStatement) { if (topNodeStatement.type == HlslStatementType.Return) { retNodes.Add(topNodeId); } } // add the children for (int i = 0; i < nodeChildren[topNodeId].Length; i++) { nodeStack.Push(nodeChildren[topNodeId][i]); } } return retNodes; } List FindAllNodesWithStructFieldFromIdPair(HlslApdInfo apdInfo, int structId, int fieldId) { List ret = new List(); // TODO: Add a reverse lookup instead of going through all the nodes int numNodes = tree.allNodes.Count; for (int i = 0; i < numNodes; i++) { HlslTree.Node currNode = tree.allNodes[i]; if (currNode is HlslTree.NodeExpression nodeExpression) { if (nodeExpression.op == HlslOp.MemberAccess) { HlslTree.Node rhsNode = tree.allNodes[nodeExpression.rhsNodeId]; HlslTree.FullTypeInfo lhsTypeInfo = tree.fullTypeInfo[nodeExpression.lhsNodeId]; // lhs of MemberAccess should always be a struct if (lhsTypeInfo.nativeType == HlslNativeType._struct) { // if this is the struct we are working with if (lhsTypeInfo.structId == structId) { // get the right side, which should be a field or a member func. Ignore if it is a MemberFunction. if (rhsNode is HlslTree.NodeMemberVariable nodeMemberVariable) { if (nodeMemberVariable.fieldIndex == fieldId) { ret.Add(nodeExpression.rhsNodeId); } } } } } } } return ret; } // nodeIdx is for the decl List FindAllNodesWithStructFieldFromDecl(HlslApdInfo apdInfo, int nodeIdx) { int parentId = nodeParents[nodeIdx]; // parentId should never be top-level HlslUtil.ParserAssert(parentId >= 0); HlslTree.Node parentNode = tree.allNodes[parentId]; HlslUtil.ParserAssert(parentNode is HlslTree.NodeStruct); HlslTree.NodeStruct parentNodeStruct = (HlslTree.NodeStruct)parentNode; List ret; // for each instance of calling this struct, mark the field { int foundFieldId = -1; for (int i = 0; i < parentNodeStruct.declarations.Length; i++) { if (parentNodeStruct.declarations[i] == nodeIdx) { foundFieldId = i; } } HlslUtil.ParserAssert(foundFieldId >= 0); ret = FindAllNodesWithStructFieldFromIdPair(apdInfo, parentId, foundFieldId); } return ret; } int FindVariableInfoIdForDecl(HlslApdInfo apdInfo, int nodeIdx) { // First, find the variable that refers to this declaration. // TODO: Make an optimization structure for this operation. int foundVariableInfoId = -1; { for (int i = 0; i < tree.allVariables.Count; i++) { if (tree.allVariables[i].declId == nodeIdx) { foundVariableInfoId = i; break; } } } HlslUtil.ParserAssert(foundVariableInfoId >= 0); return foundVariableInfoId; } List FindAllFuncCallExpressionsForPrototype(HlslApdInfo apdInfo, int nodeIdx) { List ret = new List(); HlslTree.Node baseNode = tree.allNodes[nodeIdx]; HlslUtil.ParserAssert(baseNode is HlslTree.NodeFunctionPrototype); HlslTree.NodeFunctionPrototype nodeFunctionPrototype = (HlslTree.NodeFunctionPrototype)baseNode; int numNodes = tree.allNodes.Count; // TODO: Make an accelleration structure. for (int i = 0; i < numNodes; i++) { HlslTree.Node currNode = tree.allNodes[i]; if (currNode is HlslTree.NodeExpression nodeExpression) { if (nodeExpression.op == HlslOp.FunctionalCall) { HlslTree.Node lhsNode = tree.allNodes[nodeExpression.lhsNodeId]; if (lhsNode is HlslTree.NodeFunctionPrototype nodeProto) { string funcExpectedName = tokenizer.GetTokenData(nodeFunctionPrototype.nameTokenId); string funcActualName = tokenizer.GetTokenData(nodeProto.nameTokenId); if (string.Compare(funcExpectedName, funcActualName) == 0) { ret.Add(nodeExpression.lhsNodeId); } } else if (lhsNode is HlslTree.NodeFunctionPrototypeExternal nodeProtoExternal) { // external prototypes can not be converted to hlsl } else { // should never happen because the lhs of a node with a function call op should be either NodeFunctionPrototype or NodeFunctionPrototypeExternal HlslUtil.ParserAssert(false); } } } } return ret; } List FindAllNodeArgumentsForFunctionParam(HlslApdInfo apdInfo, int nodeIdx) { List ret = new List(); int parentId = nodeParents[nodeIdx]; // parentId should never be top-level HlslUtil.ParserAssert(parentId >= 0); HlslTree.Node parentNode = tree.allNodes[parentId]; HlslUtil.ParserAssert(parentNode is HlslTree.NodeFunctionPrototype); HlslTree.NodeFunctionPrototype nodeFuncProto = (HlslTree.NodeFunctionPrototype)parentNode; // figure out which paramter it is int foundParamIndex = -1; for (int i = 0; i < nodeFuncProto.declarations.Length; i++) { if (nodeFuncProto.declarations[i] == nodeIdx) { foundParamIndex = i; break; } } HlslUtil.ParserAssert(foundParamIndex >= 0); // for each instance of calling this function, mark the parameter { // TODO: Add a reverse lookup instead of going through all the nodes int numNodes = tree.allNodes.Count; for (int i = 0; i < numNodes; i++) { HlslTree.Node currNode = tree.allNodes[i]; if (currNode is HlslTree.NodeExpression nodeExpression) { if (nodeExpression.op == HlslOp.FunctionalCall) { HlslTree.Node lhsNode = tree.allNodes[nodeExpression.lhsNodeId]; if (lhsNode is HlslTree.NodeFunctionPrototype nodeProto) { string funcExpectedName = tokenizer.GetTokenData(nodeFuncProto.nameTokenId); string funcActualName = tokenizer.GetTokenData(nodeProto.nameTokenId); if (string.Compare(funcExpectedName, funcActualName) == 0) { // we don't need to update every variable, just the declaration that we changed int nodeToUpdate = nodeExpression.paramIds[foundParamIndex]; ret.Add(nodeToUpdate); } } else if (lhsNode is HlslTree.NodeFunctionPrototypeExternal nodeProtoExternal) { // external prototypes can not be converted to hlsl } else { // should never happen because the lhs of a node with a function call op should be either NodeFunctionPrototype or NodeFunctionPrototypeExternal HlslUtil.ParserAssert(false); } } } } } return ret; } class HlslApdInfo { internal string[] debugNodeNames; internal string[] debugVariableNames; internal bool[] isNodeApdLegalVec; internal bool[] isVariableApdLegalVec; internal bool[] isNodeApdSink; internal int[][] variableNodeVec; internal bool[] isNodeDesiredApdVec; internal bool[] isVariableDesiredApdVec; internal ApdStatus[] nodeApdStatus; internal bool[] isNodeOnApdPath; // total num entries internal int[][] nodeApdDependencies; internal int[][] nodeApdDependenciesRev; } // maybe instead of an array of lists it should be an array of dictionaries? static void AddApdDependency(List[] allNodeApdDeps, int src, int dst) { if (dst < 0) { // no op } else if (!allNodeApdDeps[src].Contains(dst)) { allNodeApdDeps[src].Add(dst); } } // returns the declaration node id int GetProtoParamDeclAndModifier(out HlslToken modifierType, int protoNodeId, int paramIndex) { HlslTree.Node baseNode = tree.allNodes[protoNodeId]; HlslUtil.ParserAssert(baseNode is HlslTree.NodeFunctionPrototype); HlslTree.NodeFunctionPrototype nodeProto = (HlslTree.NodeFunctionPrototype)baseNode; int declId = nodeProto.declarations[paramIndex]; HlslTree.Node baseDecl = tree.allNodes[declId]; HlslUtil.ParserAssert(baseDecl is HlslTree.NodeDeclaration); HlslTree.NodeDeclaration nodeDecl = (HlslTree.NodeDeclaration)baseDecl; modifierType = HlslToken._invalid; if (nodeDecl.modifierTokenId >= 0) { modifierType = tokenizer.GetTokenType(nodeDecl.modifierTokenId); } return declId; } void CalculateApdDependenciesRecurse(List[] allNodeApdDeps, int nodeId) { if (nodeId < 0) { return; } HlslTree.Node baseNode = tree.allNodes[nodeId]; if (baseNode is HlslTree.NodeTopLevel nodeTopLevel) { // top level node doesn't have a type. Just recurse. for (int i = 0; i < nodeTopLevel.statements.Length; i++) { CalculateApdDependenciesRecurse(allNodeApdDeps, nodeTopLevel.statements[i]); } } else if (baseNode is HlslTree.NodeStruct nodeStruct) { // a struct can't be apd, so just recurse for (int i = 0; i < nodeStruct.declarations.Length; i++) { CalculateApdDependenciesRecurse(allNodeApdDeps, nodeStruct.declarations[i]); } } else if (baseNode is HlslTree.NodeUnityStruct nodeUnityStruct) { // unity structs can't be apd and have nothing to recurse, so no op } else if (baseNode is HlslTree.NodeFunctionPrototype nodeFunctionPrototype) { // the apd status of the prototype depends on the nodeFunction, which is its parent int parentId = nodeParents[nodeId]; AddApdDependency(allNodeApdDeps, nodeId, parentId); // all of the individual parameters will be handled by the actual statements that call the function for (int i = 0; i < nodeFunctionPrototype.declarations.Length; i++) { CalculateApdDependenciesRecurse(allNodeApdDeps, nodeFunctionPrototype.declarations[i]); } } else if (baseNode is HlslTree.NodeFunctionPrototypeExternal nodeFunctionPrototypeExternal) { // External functions can not be converted. However expressions which call external functions // may be converted in specific cases (such as texture reads) } else if (baseNode is HlslTree.NodeBlock nodeBlock) { // A block node is APD if any of the functions have a return type of APD. // We now need to go through the code block and change any return types. List retNodes = FindAllReturnNodesInBlock(apdInfo, nodeId); for (int i = 0; i < retNodes.Count; i++) { int retNodeId = retNodes[i]; AddApdDependency(allNodeApdDeps, nodeId, retNodeId); } for (int i = 0; i < nodeBlock.statements.Length; i++) { CalculateApdDependenciesRecurse(allNodeApdDeps, nodeBlock.statements[i]); } } else if (baseNode is HlslTree.NodeStatement nodeStatement) { switch (nodeStatement.type) { case HlslStatementType.If: AddApdDependency(allNodeApdDeps, nodeId, nodeStatement.expression); AddApdDependency(allNodeApdDeps, nodeId, nodeStatement.childBlockOrStatement); AddApdDependency(allNodeApdDeps, nodeId, nodeStatement.elseBlockOrStatement); CalculateApdDependenciesRecurse(allNodeApdDeps, nodeStatement.expression); CalculateApdDependenciesRecurse(allNodeApdDeps, nodeStatement.childBlockOrStatement); CalculateApdDependenciesRecurse(allNodeApdDeps, nodeStatement.elseBlockOrStatement); break; case HlslStatementType.Switch: AddApdDependency(allNodeApdDeps, nodeId, nodeStatement.expression); AddApdDependency(allNodeApdDeps, nodeId, nodeStatement.childBlockOrStatement); CalculateApdDependenciesRecurse(allNodeApdDeps, nodeStatement.expression); CalculateApdDependenciesRecurse(allNodeApdDeps, nodeStatement.childBlockOrStatement); break; case HlslStatementType.Case: // no apd expressions since it must be a compile time constant break; case HlslStatementType.Break: // no dependencies or expressions break; case HlslStatementType.Continue: // no dependencies or expressions break; case HlslStatementType.Default: // no dependencies or expressions break; case HlslStatementType.Goto: // no apd expressions since label is constant break; case HlslStatementType.Label: // label is constant break; case HlslStatementType.For: HlslUtil.ParserAssert(nodeStatement.forExpressions.Length == 3); for (int i = 0; i < 3; i++) { AddApdDependency(allNodeApdDeps, nodeId, nodeStatement.forExpressions[i]); CalculateApdDependenciesRecurse(allNodeApdDeps, nodeStatement.forExpressions[i]); } AddApdDependency(allNodeApdDeps, nodeId, nodeStatement.childBlockOrStatement); CalculateApdDependenciesRecurse(allNodeApdDeps, nodeStatement.childBlockOrStatement); break; case HlslStatementType.Do: AddApdDependency(allNodeApdDeps, nodeId, nodeStatement.expression); CalculateApdDependenciesRecurse(allNodeApdDeps, nodeStatement.expression); AddApdDependency(allNodeApdDeps, nodeId, nodeStatement.childBlockOrStatement); CalculateApdDependenciesRecurse(allNodeApdDeps, nodeStatement.childBlockOrStatement); break; case HlslStatementType.While: AddApdDependency(allNodeApdDeps, nodeId, nodeStatement.expression); CalculateApdDependenciesRecurse(allNodeApdDeps, nodeStatement.expression); AddApdDependency(allNodeApdDeps, nodeId, nodeStatement.childBlockOrStatement); CalculateApdDependenciesRecurse(allNodeApdDeps, nodeStatement.childBlockOrStatement); break; case HlslStatementType.Expression: // the apd type of a single statement expression is just the expression AddApdDependency(allNodeApdDeps, nodeId, nodeStatement.expression); CalculateApdDependenciesRecurse(allNodeApdDeps, nodeStatement.expression); break; case HlslStatementType.Return: // the block depends on all of the return statements in the function, and // the return statements in the function depend on the expressions. AddApdDependency(allNodeApdDeps, nodeId, nodeStatement.expression); CalculateApdDependenciesRecurse(allNodeApdDeps, nodeStatement.expression); break; default: HlslUtil.ParserAssert(false); break; } } else if (baseNode is HlslTree.NodeFunction nodeFunction) { // the function return type depends on the blockId AddApdDependency(allNodeApdDeps, nodeId, nodeFunction.blockId); // the prototypeID depends on the block, but that will be handled by the prototype recursion CalculateApdDependenciesRecurse(allNodeApdDeps, nodeFunction.prototypeId); CalculateApdDependenciesRecurse(allNodeApdDeps, nodeFunction.blockId); } else if (baseNode is HlslTree.NodeDeclaration nodeDeclaration) { // the actual type node depends on the decl parent AddApdDependency(allNodeApdDeps, nodeDeclaration.typeNodeId, nodeId); CalculateApdDependenciesRecurse(allNodeApdDeps, nodeDeclaration.typeNodeId); // node declarations depend on the initializer. if (nodeDeclaration.initializerId >= 0) { AddApdDependency(allNodeApdDeps, nodeId, nodeDeclaration.initializerId); CalculateApdDependenciesRecurse(allNodeApdDeps, nodeDeclaration.initializerId); } } else if (baseNode is HlslTree.NodeVariable nodeVariable) { // this dependency goes both ways. the declaration depends on the variable and the variable // depend on the declaration. int variableId = nodeVariable.nameVariableId; if (variableId >= 0) { // only applicable if the variable is defined in this function, obviously // not true for a global int declNodeId = tree.allVariables[variableId].declId; AddApdDependency(allNodeApdDeps, nodeId, declNodeId); AddApdDependency(allNodeApdDeps, declNodeId, nodeId); } } else if (baseNode is HlslTree.NodeNativeConstructor nodeNativeConstructor) { // this depends on the children for (int i = 0; i < nodeNativeConstructor.paramNodeIds.Length; i++) { int childId = nodeNativeConstructor.paramNodeIds[i]; AddApdDependency(allNodeApdDeps, nodeId, childId); CalculateApdDependenciesRecurse(allNodeApdDeps, childId); } } else if (baseNode is HlslTree.NodeToken nodeToken) { // tokens can't be changed. no op } else if (baseNode is HlslTree.NodeNativeType nodeNativeType) { // it is what it is with no dependencies. no op. } else if (baseNode is HlslTree.NodeLiteralOrBool nodeLiteralOrBool) { // a constant is a constant, no op } else if (baseNode is HlslTree.NodePassthrough nodePassthrough) { // passthrough can't be changed, no op } else if (baseNode is HlslTree.NodeExpression nodeExpression) { // recurse everything that can recurse CalculateApdDependenciesRecurse(allNodeApdDeps, nodeExpression.lhsNodeId); CalculateApdDependenciesRecurse(allNodeApdDeps, nodeExpression.rhsNodeId); CalculateApdDependenciesRecurse(allNodeApdDeps, nodeExpression.rhsRhsNodeId); for (int i = 0; i < nodeExpression.paramIds.Length; i++) { CalculateApdDependenciesRecurse(allNodeApdDeps, nodeExpression.paramIds[i]); } // depends on the type switch (nodeExpression.op) { // these are not supported: case HlslOp.Invalid: // case HlslOp.ScopeReslution: // :: case HlslOp.ThreeWayCompare: // <=> - spaceship---watch out for men in black case HlslOp.Dereference: // * - not legal in HLSL? case HlslOp.AddressOf: // & - not legal in HLSL? case HlslOp.Sizeof: // sizeof case HlslOp.PointerToMemberDot: // .* - not legal in HLSL case HlslOp.PointerToMemorArrow: // ->* - not legal in HLSL break; // increment/decrement/unary plus/minus have no effect on apd status, so just link // the parent (curr) and child together. case HlslOp.PostIncrement: // ++ case HlslOp.PostDecrement: // -- case HlslOp.PreIncrement: // ++ case HlslOp.PreDecrement: // -- case HlslOp.UnaryPlus: // + case HlslOp.UnaryMinus: // - AddApdDependency(allNodeApdDeps, nodeId, nodeExpression.lhsNodeId); break; case HlslOp.FunctionalCast: // int() // functional cast depends on rhs AddApdDependency(allNodeApdDeps, nodeId, nodeExpression.rhsNodeId); break; case HlslOp.FunctionalCall: // func() // the return type of this node depends on the return type of the function. // lhsNodeId refers to the prototype node AddApdDependency(allNodeApdDeps, nodeId, nodeExpression.lhsNodeId); { HlslTree.Node baseProtoNode = tree.allNodes[nodeExpression.lhsNodeId]; if (baseProtoNode is HlslTree.NodeFunctionPrototype nodeFuncProto) { for (int i = 0; i < nodeExpression.paramIds.Length; i++) { HlslToken modifier; int declId = GetProtoParamDeclAndModifier(out modifier, nodeExpression.lhsNodeId, i); bool isIn = (modifier == HlslToken._invalid || modifier == HlslToken._in || modifier == HlslToken._inout); bool isOut = (modifier == HlslToken._out || modifier == HlslToken._inout); int argId = nodeExpression.paramIds[i]; if (isIn) { AddApdDependency(allNodeApdDeps, declId, argId); } if (isOut) { // for out, the types need to be exactly the same so that the output // always has a legal l-value. For example, suppose we have the following: // // void SomeFunc(in float A, out float B) // { /*stuff*/ } // // SumFunc(srcVal,val0); // /* val0 used as apd */ // SumFunc(srcVal,val1); // /* val1 only used as fpd */ // // In this case, the parameter must be APD, (since val0 needs APD). Val1 // doesn't need to be APD for it's value, but it does need to be APD so that // it can be a valid l-value in the function call. The easiest solution is // make sure that all out args are dependent both ways with the parameter, // ensuring that both are always identical. AddApdDependency(allNodeApdDeps, declId, argId); AddApdDependency(allNodeApdDeps, argId, declId); } } } else if (baseProtoNode is HlslTree.NodeFunctionPrototypeExternal nodeFuncProtoExternal) { Dictionary textureSinkList = new Dictionary(); for (int paramIter = 0; paramIter < nodeFuncProtoExternal.protoInfo.paramInfoVec.Length; paramIter++) { HlslParser.TypeInfo typeInfo = nodeFuncProtoExternal.protoInfo.paramInfoVec[paramIter]; if (typeInfo.allowedState == ApdAllowedState.OnlyApd) { //int sinkIndex = textureSinkList[nodeFuncProtoExternal.protoInfo.identifier]; int sinkParamId = nodeExpression.paramIds[paramIter]; AddApdDependency(allNodeApdDeps, nodeExpression.lhsNodeId, sinkParamId); } } // also, link up all the AllowApdVariation params and return types List allApdVariationIds = new List(); if (nodeFuncProtoExternal.protoInfo.returnType.allowedState == ApdAllowedState.AllowApdVariation) { allApdVariationIds.Add(nodeExpression.lhsNodeId); } for (int paramIter = 0; paramIter < nodeFuncProtoExternal.protoInfo.paramInfoVec.Length; paramIter++) { HlslParser.TypeInfo typeInfo = nodeFuncProtoExternal.protoInfo.paramInfoVec[paramIter]; if (typeInfo.allowedState == ApdAllowedState.AllowApdVariation) { int paramId = nodeExpression.paramIds[paramIter]; allApdVariationIds.Add(paramId); } } if (allApdVariationIds.Count >= 2) { for (int lhs = 0; lhs < allApdVariationIds.Count; lhs++) { for (int rhs = 0; rhs < allApdVariationIds.Count; rhs++) { if (lhs != rhs) { AddApdDependency(allNodeApdDeps, allApdVariationIds[lhs], allApdVariationIds[rhs]); } } } } } } break; // partially supported case HlslOp.Subscript: // [] { AddApdDependency(allNodeApdDeps, nodeId, nodeExpression.lhsNodeId); AddApdDependency(allNodeApdDeps, nodeExpression.lhsNodeId, nodeId); } break; // affect the struct case HlslOp.MemberAccess: // . { HlslTree.Node nodeRhs = tree.allNodes[nodeExpression.rhsNodeId]; if (nodeRhs is HlslTree.NodeMemberVariable nodeMemberVariable) { // we will use what the rhs child returns (lhs is struct, rhs is member) AddApdDependency(allNodeApdDeps, nodeId, nodeExpression.rhsNodeId); AddApdDependency(allNodeApdDeps, nodeExpression.rhsNodeId, nodeId); } else if (nodeRhs is HlslTree.NodeMemberFunction nodeMemberFunction) { // also uses rhs, since lhs is struct, and rhs is member AddApdDependency(allNodeApdDeps, nodeId, nodeExpression.rhsNodeId); } else if (nodeRhs is HlslTree.NodeSwizzle nodeSwizzle) { // swizzle, use the lhs (which is the main type) since rhs simply describes the swizzle // of tthat type, but the type is unchanged AddApdDependency(allNodeApdDeps, nodeId, nodeExpression.lhsNodeId); AddApdDependency(allNodeApdDeps, nodeExpression.lhsNodeId, nodeId); } else { // should never happen, since these are the only 3 nodes allowed HlslUtil.ParserAssert(false); } } break; // these are not valid with floats/halfs so no apd dependency case HlslOp.LogicalNot: // ! case HlslOp.BitwiseNot: // ~ case HlslOp.ShiftL: // << case HlslOp.ShiftR: // >> case HlslOp.LessThan: // < case HlslOp.GreaterThan: // > case HlslOp.LessEqual: // <= case HlslOp.GreaterEqual: // >= case HlslOp.CompareEqual: // == case HlslOp.NotEqual: // != case HlslOp.BitwiseAnd: // & case HlslOp.BitwiseXor: // ^ case HlslOp.BitwiseOr: // | case HlslOp.LogicalAnd: // && case HlslOp.LogicalOr: // || case HlslOp.ShiftLEquals: // <<= case HlslOp.ShiftREquals: // >>= case HlslOp.AndEquals: // &= case HlslOp.XorEquals: // ^= case HlslOp.OrEquals: // |= case HlslOp.Mod: // % case HlslOp.ModEquals: // %= break; case HlslOp.CStyleCast: // (int) // in a regular Cstyle case, the lhs depend on the rhs since it's essentially an assignment AddApdDependency(allNodeApdDeps, nodeExpression.lhsNodeId, nodeExpression.rhsNodeId); break; case HlslOp.Mul: // * case HlslOp.Div: // / case HlslOp.Add: // + case HlslOp.Sub: // - // for the 4 basic math ops, the status depends on both AddApdDependency(allNodeApdDeps, nodeId, nodeExpression.lhsNodeId); AddApdDependency(allNodeApdDeps, nodeId, nodeExpression.rhsNodeId); break; case HlslOp.TernaryQuestion: // ? // for the statement: // value = A ? B : C; // the APD status of the expresssion (A ? B : C) depends only on B and C. AddApdDependency(allNodeApdDeps, nodeId, nodeExpression.rhsNodeId); AddApdDependency(allNodeApdDeps, nodeId, nodeExpression.rhsRhsNodeId); break; // should never happen, since the TernaryQuestion stores all three expression ids case HlslOp.TernaryColon: // : HlslUtil.ParserAssert(false); break; case HlslOp.Assignment: // = // for assignment, we simply take the apd status of the right and return it. AddApdDependency(allNodeApdDeps, nodeId, nodeExpression.lhsNodeId); AddApdDependency(allNodeApdDeps, nodeExpression.lhsNodeId, nodeId); AddApdDependency(allNodeApdDeps, nodeExpression.lhsNodeId, nodeExpression.rhsNodeId); break; case HlslOp.AddEquals: // += case HlslOp.SubEquals: // -= case HlslOp.MulEquals: // *= case HlslOp.DivEquals: // /= case HlslOp.Comma: // , // while assignment should lose the lhs status and completely replace it with // rhs, whereas for +=, -=, etc we should merge the status. However, we don't have a concise // way to describe that relationship with current data structures. AddApdDependency(allNodeApdDeps, nodeId, nodeExpression.lhsNodeId); AddApdDependency(allNodeApdDeps, nodeExpression.lhsNodeId, nodeId); AddApdDependency(allNodeApdDeps, nodeExpression.lhsNodeId, nodeExpression.rhsNodeId); break; default: HlslUtil.ParserAssert(false); break; } } else if (baseNode is HlslTree.NodeMemberVariable nodeMemberVariable) { // A member variable depend on the declaration and the declaration depends // on the member variable. That's because it's the union of all types. HlslTree.Node baseStructNode = tree.allNodes[nodeMemberVariable.structNodeId]; if (baseStructNode is HlslTree.NodeUnityStruct unityStruct) { // these are locked as non-apd, so no dependency to add } else if (baseStructNode is HlslTree.NodeStruct srcStructNode) { int declId = srcStructNode.declarations[nodeMemberVariable.fieldIndex]; AddApdDependency(allNodeApdDeps, nodeId, declId); AddApdDependency(allNodeApdDeps, declId, nodeId); // also, nothing to recurse } else { // should never happen HlslUtil.ParserAssert(false); } } else if (baseNode is HlslTree.NodeParenthesisGroup nodeParenthesisGroup) { // both depeend on the other since they are essentially the same //int parentId = nodeParents[nodeId]; AddApdDependency(allNodeApdDeps, nodeParenthesisGroup.childNodeId, nodeId); AddApdDependency(allNodeApdDeps, nodeId, nodeParenthesisGroup.childNodeId); // recurse of course CalculateApdDependenciesRecurse(allNodeApdDeps, nodeParenthesisGroup.childNodeId); } else if (baseNode is HlslTree.NodeMemberFunction nodeMemberFunction) { // For now, we aren't processing custom member functions in structs, but if we decide to // support that we would have to tweak the function, and then tweak all the return values. // For a proper solution: // 1. The return type depends on the function return type. // 2. Each func argument depends on the function parameter. // 3. For out parameters, we also have the reverse where each parameter depends on the argument. // one case that we might have to work with (sooner than later) is tex.Sample(sampler,uv); for (int i = 0; i < nodeMemberFunction.funcParamNodeIds.Length; i++) { int childId = nodeMemberFunction.funcParamNodeIds[i]; AddApdDependency(allNodeApdDeps, nodeId, childId); CalculateApdDependenciesRecurse(allNodeApdDeps, childId); } } else if (baseNode is HlslTree.NodeSwizzle nodeSwizzle) { // the swizzle doesn't actually have a type. In the parent expression, // such as: // something.rgb; // something will be lhs of the parent, and the .rgb swizzle will be this // node in the rhs. The type is defined by the lhs so the .rgb node doesn't // actually have an apd type since the parent will just take the lhs. } else if (baseNode is HlslTree.NodeBlockInitializer nodeBlockInit) { // no dependencies, as everything inside a block initializer is forbidden from // being apd } else { // if we got here, then we are missing a type in this if tree HlslUtil.ParserAssert(false); } } void CalculateApdDependencies() { int numNodes = tree.allNodes.Count; List[] allNodeApdDeps = new List[numNodes]; for (int i = 0; i < numNodes; i++) { allNodeApdDeps[i] = new List(); } HlslUtil.ParserAssert(tree.topLevelNode >= 0); CalculateApdDependenciesRecurse(allNodeApdDeps, tree.topLevelNode); apdInfo.nodeApdDependencies = new int[numNodes][]; for (int i = 0; i < numNodes; i++) { apdInfo.nodeApdDependencies[i] = allNodeApdDeps[i].ToArray(); } // also, create reverse dependencies { List[] allNodeApdDepsRev = new List[numNodes]; for (int i = 0; i < numNodes; i++) { allNodeApdDepsRev[i] = new List(); } for (int i = 0; i < numNodes; i++) { for (int j = 0; j < apdInfo.nodeApdDependencies[i].Length; j++) { int lhs = apdInfo.nodeApdDependencies[i][j]; int rhs = i; HlslUtil.ParserAssert(!allNodeApdDepsRev[lhs].Contains(rhs)); allNodeApdDepsRev[lhs].Add(rhs); } } apdInfo.nodeApdDependenciesRev = new int[numNodes][]; for (int i = 0; i < numNodes; i++) { apdInfo.nodeApdDependenciesRev[i] = allNodeApdDepsRev[i].ToArray(); } } } bool IsDeclPossibleInterpolatorApdInput(HlslApdInfo apdInfo, int nodeId) { bool isApdInput = false; HlslTree.Node baseNode = tree.allNodes[nodeId]; if (baseNode is HlslTree.NodeDeclaration nodeDeclaration) { // is it a global variable? to find out, check if the parent is a top level node. int parentId = nodeParents[nodeId]; HlslUtil.ParserAssert(parentId >= 0); HlslTree.Node parentNode = tree.allNodes[parentId]; if (parentNode is HlslTree.NodeStruct parentStruct) { string parentName = tokenizer.GetTokenData(parentStruct.nameTokenId); string childName = tokenizer.GetTokenData(nodeDeclaration.nameTokenId); // hardcode the names for now if (string.Compare(parentName, "SurfaceDescriptionInputs") == 0) { if (string.Compare(childName, "uv0") == 0 || string.Compare(childName, "uv1") == 0 || string.Compare(childName, "uv2") == 0 || string.Compare(childName, "uv3") == 0) { isApdInput = true; } } } } return isApdInput; } void MarkInitialApdStatusForNode(ApdStatus[] initialStatus, int nodeId) { HlslTree.Node baseNode = tree.allNodes[nodeId]; ApdStatus dstStatus = ApdStatus.Unknown; // if a node does not have a parent, it is an orphaned node (unless it's a top level node, which // is marked invalid anyways int parentId = nodeParents[nodeId]; if (parentId >= 0) { if (baseNode is HlslTree.NodeTopLevel nodeTopLevel) { // top level nods have no derivatives dstStatus = ApdStatus.Invalid; } else if (baseNode is HlslTree.NodeStruct nodeStruct) { // struct types do not have derivatives dstStatus = ApdStatus.Invalid; } else if (baseNode is HlslTree.NodeUnityStruct nodeUnityStruct) { // unity struct types do not have derivatives dstStatus = ApdStatus.Invalid; } else if (baseNode is HlslTree.NodeFunctionPrototype nodeFunctionPrototype) { // not sure, this will depend } else if (baseNode is HlslTree.NodeFunctionPrototypeExternal nodeFunctionPrototypeExternal) { // will depend on inputs } else if (baseNode is HlslTree.NodeBlock nodeBlock) { // depends on inputs } else if (baseNode is HlslTree.NodeStatement nodeStatement) { // depends on parsing expression } else if (baseNode is HlslTree.NodeFunction nodeFunction) { // depends on parsing } else if (baseNode is HlslTree.NodeDeclaration nodeDeclaration) { // a few cases: // 1. If it's a toplevel node and the native type is apd-able, then set to zero. // 2. If it's not apd-able, then obviously invalid. // 3. It could be an interpolator, making it known. // 4. Otherwise, it's unknown. HlslNativeType nativeType = tree.fullTypeInfo[nodeId].nativeType; bool isApdLegal = HlslUtil.IsNativeTypeLegalForApd(nativeType); if (!isApdLegal) { dstStatus = ApdStatus.Invalid; } else { bool isApdInterpolator = IsDeclPossibleInterpolatorApdInput(apdInfo, nodeId); if (isApdInterpolator) { dstStatus = ApdStatus.Valid; } else { HlslTree.Node parentNode = tree.allNodes[parentId]; if (parentNode is HlslTree.NodeTopLevel) { dstStatus = ApdStatus.Zero; } else { dstStatus = ApdStatus.Unknown; } } } } else if (baseNode is HlslTree.NodeVariable nodeVariable) { // will depend on parsing, unknown for now. } else if (baseNode is HlslTree.NodeNativeConstructor nodeNativeConstructor) { // will depend on parsing } else if (baseNode is HlslTree.NodeToken nodeToken) { // unknown } else if (baseNode is HlslTree.NodeNativeType nodeNativeType) { // nothing to change } else if (baseNode is HlslTree.NodeLiteralOrBool nodeLiteralOrBool) { // never allowed to change, as a constant is a constant HlslNativeType nativeType = tree.fullTypeInfo[nodeId].nativeType; bool isApdLegal = HlslUtil.IsNativeTypeLegalForApd(nativeType); dstStatus = isApdLegal ? ApdStatus.Zero : ApdStatus.Invalid; } else if (baseNode is HlslTree.NodePassthrough nodePassthrough) { // passthrough can't be changed dstStatus = ApdStatus.Invalid; } else if (baseNode is HlslTree.NodeExpression nodeExpression) { // definitely needs to be parsed, unknown } else if (baseNode is HlslTree.NodeMemberVariable nodeMemberVariable) { // in most cases we don't know yet, unless it's a known interpolator HlslNativeType nativeType = tree.fullTypeInfo[nodeId].nativeType; bool isApdLegal = HlslUtil.IsNativeTypeLegalForApd(nativeType); dstStatus = isApdLegal ? ApdStatus.Unknown : ApdStatus.Invalid; } else if (baseNode is HlslTree.NodeParenthesisGroup nodeParenthesisGroup) { // let it parse } else if (baseNode is HlslTree.NodeMemberFunction nodeMemberFunction) { // also should be parsed } else if (baseNode is HlslTree.NodeSwizzle nodeSwizzle) { // maybe make this invalid? } else if (baseNode is HlslTree.NodeBlockInitializer nodeBlockInit) { // always invalid dstStatus = ApdStatus.Invalid; } else { // if we got here, then we are missing a type in this if tree HlslUtil.ParserAssert(false); } } initialStatus[nodeId] = dstStatus; } void UpdateNodesOnSinkPathRecurse(bool[] nodesOnSinkPath, int nodeId) { if (nodeId < 0) { // no op } else if (nodesOnSinkPath[nodeId]) { // also a no op, if this was already marked } else { nodesOnSinkPath[nodeId] = true; int[] nodeDeps = apdInfo.nodeApdDependencies[nodeId]; for (int depIter = 0; depIter < nodeDeps.Length; depIter++) { int depId = nodeDeps[depIter]; UpdateNodesOnSinkPathRecurse(nodesOnSinkPath, depId); } } } void UpdateApdNodeStatusRecurse(int nodeId) { if (nodeId < 0) { // no op } else { HlslTree.FullTypeInfo nodeTypeInfo = tree.fullTypeInfo[nodeId]; bool isApdLegal = HlslUtil.IsNativeTypeLegalForApd(nodeTypeInfo.nativeType); ApdStatus startStatus = apdInfo.nodeApdStatus[nodeId]; // if the initial status is unknown, there isn't much to propogate if (startStatus != ApdStatus.Unknown && isApdLegal) { // nodeApdDependencies is which nodes this current node depends on. // nodeApdDependenciesRev is the reverse, i.e. which nodes depend on this one. int[] dependentNodes = apdInfo.nodeApdDependenciesRev[nodeId]; for (int baseDepIter = 0; baseDepIter < dependentNodes.Length; baseDepIter++) { int childId = dependentNodes[baseDepIter]; ApdStatus childStartStatus = apdInfo.nodeApdStatus[childId]; ApdStatus childDstStatus = childStartStatus; int[] childDependencies = apdInfo.nodeApdDependencies[childId]; for (int childDepIter = 0; childDepIter < childDependencies.Length; childDepIter++) { int childDepId = childDependencies[childDepIter]; ApdStatus depStatus = apdInfo.nodeApdStatus[childDepId]; childDstStatus = MergeOpApdStatus(depStatus, childDstStatus); } if (childDstStatus != childStartStatus) { apdInfo.nodeApdStatus[childId] = childDstStatus; UpdateApdNodeStatusRecurse(childId); } } } } } static string ApdStatusAsOneCharacter(ApdStatus status) { string ret = ""; switch (status) { case ApdStatus.Unknown: ret = "u"; break; case ApdStatus.Zero: ret = "z"; break; case ApdStatus.NotNeeded: ret = "n"; break; case ApdStatus.Valid: ret = "V"; break; case ApdStatus.Invalid: ret = "i"; break; } return ret; } void MarkApdNodesAndVariables() { int numNodes = tree.allNodes.Count; int numVariables = tree.allVariables.Count; apdInfo.debugNodeNames = new string[numNodes]; for (int nodeIter = 0; nodeIter < numNodes; nodeIter++) { apdInfo.debugNodeNames[nodeIter] = tree.allNodes[nodeIter].GetShortName(); } apdInfo.debugVariableNames = new string[numVariables]; for (int variableIter = 0; variableIter < numVariables; variableIter++) { apdInfo.debugVariableNames[variableIter] = tree.allVariables[variableIter].variableName; } // first, is each variable and node allowed to be APD, and is each node // allowed to be ADP MarkApdNodesAndVariablesIsLegal(out apdInfo.isNodeApdLegalVec, out apdInfo.isVariableApdLegalVec); apdInfo.isNodeApdSink = new bool[numNodes]; MarkApdSinks(out apdInfo.isNodeApdSink); { List[] variableNodeList = new List[numVariables]; for (int i = 0; i < numVariables; i++) { variableNodeList[i] = new List(); } for (int nodeIter = 0; nodeIter < numNodes; nodeIter++) { HlslTree.Node childNode = tree.allNodes[nodeIter]; if (childNode is HlslTree.NodeVariable childNodeVariable) { if (childNodeVariable.nameVariableId >= 0) { variableNodeList[childNodeVariable.nameVariableId].Add(nodeIter); } } } apdInfo.variableNodeVec = new int[numVariables][]; for (int i = 0; i < numVariables; i++) { apdInfo.variableNodeVec[i] = variableNodeList[i].ToArray(); } } ApdStatus[] initialStatus = new ApdStatus[numNodes]; // start with the nodes that we know, and mark what we know { System.Array.Fill(initialStatus, ApdStatus.Unknown); for (int i = 0; i < numNodes; i++) { MarkInitialApdStatusForNode(initialStatus, i); } apdInfo.nodeApdStatus = new ApdStatus[numNodes]; System.Array.Copy(initialStatus, apdInfo.nodeApdStatus, numNodes); } bool[] nodesOnSinkPath = new bool[numNodes]; ApdStatus[] preTrimmedPath; // propagate { List knownNodes = new List(); for (int i = 0; i < numNodes; i++) { if (apdInfo.nodeApdStatus[i] != ApdStatus.Unknown) { knownNodes.Add(i); } } for (int i = 0; i < knownNodes.Count; i++) { int nodeId = knownNodes[i]; UpdateApdNodeStatusRecurse(nodeId); } preTrimmedPath = new ApdStatus[numNodes]; System.Array.Copy(apdInfo.nodeApdStatus, preTrimmedPath, numNodes); // initialized to false implicitly for (int i = 0; i < numNodes; i++) { if (apdInfo.isNodeApdSink[i]) { UpdateNodesOnSinkPathRecurse(nodesOnSinkPath, i); } } for (int i = 0; i < numNodes; i++) { if (!nodesOnSinkPath[i]) { apdInfo.nodeApdStatus[i] = ApdStatus.Invalid; } HlslTree.FullTypeInfo currTypeInfo = tree.fullTypeInfo[i]; bool isApdLegal = HlslUtil.IsNativeTypeLegalForApd(currTypeInfo.nativeType); if (!isApdLegal) { apdInfo.nodeApdStatus[i] = ApdStatus.Invalid; } } } debugNodeLines = new string[numNodes]; for (int i = 0; i < numNodes; i++) { string debugName = apdInfo.debugNodeNames[i]; if (debugName.Length >= 30) { debugName = debugName.Substring(0, 30); } HlslTree.FullTypeInfo fullType = tree.fullTypeInfo[i]; string typeName = fullType.GetTypeString(); if (typeName.Length >= 15) { typeName = typeName.Substring(0, 15); } string fullLine = ""; fullLine += string.Format("{0,5}:", i); fullLine += string.Format(" {0,-30}", debugName); fullLine += string.Format(" {0,-15}", typeName); fullLine += string.Format(" {0}", ApdStatusAsOneCharacter(initialStatus[i])); fullLine += string.Format(" {0}", ApdStatusAsOneCharacter(preTrimmedPath[i])); fullLine += string.Format(" {0}", ApdStatusAsOneCharacter(apdInfo.nodeApdStatus[i])); fullLine += string.Format(" {0}", apdInfo.isNodeApdSink[i] ? "T" : " "); // very few sinks to make the false version empty for readability fullLine += string.Format(" {0}", nodesOnSinkPath[i] ? "T" : " "); string strDeps = ""; string strDepsRev = ""; { int[] deps = apdInfo.nodeApdDependencies[i]; for (int depIter = 0; depIter < deps.Length; depIter++) { if (depIter != 0) { strDeps += ", "; } strDeps += deps[depIter].ToString(); } int[] depsRev = apdInfo.nodeApdDependenciesRev[i]; for (int depIter = 0; depIter < depsRev.Length; depIter++) { if (depIter != 0) { strDepsRev += ", "; } strDepsRev += depsRev[depIter].ToString(); } } fullLine += string.Format(" [{0,-40}]", strDeps); fullLine += string.Format(" [{0,-40}]", strDepsRev); debugNodeLines[i] = fullLine; } } void ResolveTypeInfoRecurse(int nodeId, int parentNodeId) { // Silently succeed for invalid nodes. Many nodes can have invalid children and // this is easier than having a ton of if statements. if (nodeId < 0) { return; } // while recursing, store the parent for each child nodeParents[nodeId] = parentNodeId; // and for the parent, add this to the list if (parentNodeId >= 0) { } HlslTree.Node baseNode = tree.allNodes[nodeId]; if (baseNode is HlslTree.NodeTopLevel nodeTopLevel) { for (int i = 0; i < nodeTopLevel.statements.Length; i++) { ResolveTypeInfoRecurse(nodeTopLevel.statements[i], nodeId); } } else if (baseNode is HlslTree.NodeStruct nodeStruct) { for (int i = 0; i < nodeStruct.declarations.Length; i++) { ResolveTypeInfoRecurse(nodeStruct.declarations[i], nodeId); } } else if (baseNode is HlslTree.NodeUnityStruct nodeUnityStruct) { } else if (baseNode is HlslTree.NodeFunctionPrototype nodeFunctionPrototype) { for (int i = 0; i < nodeFunctionPrototype.declarations.Length; i++) { ResolveTypeInfoRecurse(nodeFunctionPrototype.declarations[i], nodeId); } } else if (baseNode is HlslTree.NodeFunctionPrototypeExternal nodeFunctionPrototypeExternal) { } else if (baseNode is HlslTree.NodeBlock nodeBlock) { for (int i = 0; i < nodeBlock.statements.Length; i++) { ResolveTypeInfoRecurse(nodeBlock.statements[i], nodeId); } } else if (baseNode is HlslTree.NodeStatement nodeStatement) { HlslTree.FullTypeInfo dstType = HlslTree.FullTypeInfo.MakeValidNoType(); switch (nodeStatement.type) { case HlslStatementType.If: ResolveTypeInfoRecurse(nodeStatement.expression, nodeId); ResolveTypeInfoRecurse(nodeStatement.childBlockOrStatement, nodeId); ResolveTypeInfoRecurse(nodeStatement.elseBlockOrStatement, nodeId); break; case HlslStatementType.Switch: ResolveTypeInfoRecurse(nodeStatement.expression, nodeId); ResolveTypeInfoRecurse(nodeStatement.childBlockOrStatement, nodeId); break; case HlslStatementType.Case: ResolveTypeInfoRecurse(nodeStatement.expression, nodeId); break; case HlslStatementType.Break: break; case HlslStatementType.Continue: break; case HlslStatementType.Default: break; case HlslStatementType.Goto: ResolveTypeInfoRecurse(nodeStatement.expression, nodeId); break; case HlslStatementType.Label: ResolveTypeInfoRecurse(nodeStatement.expression, nodeId); break; case HlslStatementType.For: for (int i = 0; i < 3; i++) { ResolveTypeInfoRecurse(nodeStatement.forExpressions[i], nodeId); } ResolveTypeInfoRecurse(nodeStatement.childBlockOrStatement, nodeId); break; case HlslStatementType.Do: ResolveTypeInfoRecurse(nodeStatement.expression, nodeId); ResolveTypeInfoRecurse(nodeStatement.childBlockOrStatement, nodeId); break; case HlslStatementType.While: ResolveTypeInfoRecurse(nodeStatement.expression, nodeId); ResolveTypeInfoRecurse(nodeStatement.childBlockOrStatement, nodeId); break; case HlslStatementType.Expression: ResolveTypeInfoRecurse(nodeStatement.expression, nodeId); break; case HlslStatementType.Return: ResolveTypeInfoRecurse(nodeStatement.expression, nodeId); break; default: HlslUtil.ParserAssert(false); break; } } else if (baseNode is HlslTree.NodeFunction nodeFunction) { ResolveTypeInfoRecurse(nodeFunction.prototypeId, nodeId); ResolveTypeInfoRecurse(nodeFunction.blockId, nodeId); } else if (baseNode is HlslTree.NodeDeclaration nodeDeclaration) { ResolveTypeInfoRecurse(nodeDeclaration.typeNodeId, nodeId); ResolveTypeInfoRecurse(nodeDeclaration.subTypeNodeId, nodeId); ResolveTypeInfoRecurse(nodeDeclaration.initializerId, nodeId); } else if (baseNode is HlslTree.NodeVariable nodeVariable) { } else if (baseNode is HlslTree.NodeNativeConstructor nodeNativeConstructor) { for (int i = 0; i < nodeNativeConstructor.paramNodeIds.Length; i++) { int childId = nodeNativeConstructor.paramNodeIds[i]; ResolveTypeInfoRecurse(childId, nodeId); } } else if (baseNode is HlslTree.NodeToken nodeToken) { } else if (baseNode is HlslTree.NodeNativeType nodeNativeType) { } else if (baseNode is HlslTree.NodeLiteralOrBool nodeLiteralOrBool) { } else if (baseNode is HlslTree.NodePassthrough nodePassthrough) { } else if (baseNode is HlslTree.NodeExpression nodeExpression) { ResolveTypeInfoRecurse(nodeExpression.lhsNodeId, nodeId); ResolveTypeInfoRecurse(nodeExpression.rhsNodeId, nodeId); ResolveTypeInfoRecurse(nodeExpression.cstyleCastStructId, nodeId); for (int i = 0; i < nodeExpression.paramIds.Length; i++) { ResolveTypeInfoRecurse(nodeExpression.paramIds[i], nodeId); } } else if (baseNode is HlslTree.NodeMemberVariable nodeMemberVariable) { } else if (baseNode is HlslTree.NodeParenthesisGroup nodeParenthesisGroup) { ResolveTypeInfoRecurse(nodeParenthesisGroup.childNodeId, nodeId); } else if (baseNode is HlslTree.NodeMemberFunction nodeMemberFunction) { for (int i = 0; i < nodeMemberFunction.funcParamNodeIds.Length; i++) { ResolveTypeInfoRecurse(nodeMemberFunction.funcParamNodeIds[i], nodeId); } } else if (baseNode is HlslTree.NodeSwizzle nodeSwizzle) { } else if (baseNode is HlslTree.NodeBlockInitializer nodeBlockInit) { for (int i = 0; i < nodeBlockInit.initNodeIds.Length; i++) { ResolveTypeInfoRecurse(nodeBlockInit.initNodeIds[i], nodeId); } } else { // if we got here, then we are missing a type in this if tree HlslUtil.ParserAssert(false); } } void ResolveTypeInfoAndParents() { nodeParents = new int[tree.allNodes.Count]; System.Array.Fill(nodeParents, -1); if (tree.topLevelNode < 0) { LogError("Missing top level node"); } else { ResolveTypeInfoRecurse(tree.topLevelNode, -1); // now that we know the parent, create a helper list for children FindChildrenFromParents(); apdInfo = new HlslApdInfo(); CalculateApdDependencies(); MarkApdNodesAndVariables(); } } void AppendString(string data) { currLine += data; } void FinishLine() { string indent = ""; for (int i = 0; i < indentLevel; i++) { indent += " "; } allLines.Add(indent + currLine); currLine = ""; } void IndentPush() { indentLevel++; } void IndentPop() { indentLevel--; } string MakeImplicitCast(HlslNativeType lhsType, ApdStatus lhsStatus, HlslNativeType srcRhsType, ApdStatus rhsStatus, string rhsText) { // Special hack. If the rhs is (void) and has status invalid, then we actually don't know what type it is, // so we will assume that it is the type that we are expecting and hope for the best. This can happen // when the code snippet calls a function that is not declared in this code snippet. HlslNativeType rhsType = (srcRhsType == HlslNativeType._void) ? lhsType : srcRhsType; bool isLhsApd = (lhsStatus == ApdStatus.Valid); bool isRhsApd = (rhsStatus == ApdStatus.Valid); HlslNativeType lhsNativeBase = HlslUtil.GetNativeBaseType(lhsType); HlslNativeType rhsNativeBase = HlslUtil.GetNativeBaseType(rhsType); bool isLhsScalar = HlslUtil.IsNativeTypeScalar(lhsType); bool isRhsScalar = HlslUtil.IsNativeTypeScalar(rhsType); SgType lhsSgType = new SgType(); lhsSgType = ConvertNativeTypeToSg(lhsType); lhsSgType.apdStatus = lhsStatus; SgType rhsSgType = new SgType(); rhsSgType = ConvertNativeTypeToSg(rhsType); rhsSgType.apdStatus = rhsStatus; // Main rules; // 0. If they are exactly the same, then do nothing // 1. If either is a struct or non-scalar, then we can't do any casting. // 2. If either is APD, then we'll use the writer to handle it // 3. Otherwise, we are going to assume the conversion is safe, and only change the base type. string ret = ""; if (lhsType == rhsType && lhsStatus == rhsStatus) { // exactly the same, so no op ret = rhsText; } else if (!isLhsScalar || !isRhsScalar) { // nothing we can do ret = rhsText; } else if (isLhsApd || isRhsApd) { ret = apdWriter.MakeImplicitCast(lhsSgType.slotType, lhsSgType.apdStatus, lhsSgType.precision, rhsSgType.slotType, rhsSgType.apdStatus, rhsSgType.precision, rhsText); } else { // change the base type and hope for the best int rhsRows = HlslUtil.GetNumRows(rhsType); int rhsCols = HlslUtil.GetNumCols(rhsType); HlslNativeType adjRhsType = HlslUtil.GetBaseTypeWithDims(rhsNativeBase, rhsRows, rhsCols); ret = "((" + HlslUtil.GetNativeTypeString(adjRhsType) + ")" + rhsText + ")"; } return ret; } bool SplitSwizzleAssignFromParentIfAny(out int foundNodeId, out string foundSwizzle, int nodeId) { bool ret = false; foundNodeId = -1; foundSwizzle = ""; HlslTree.Node baseNode = tree.allNodes[nodeId]; if (baseNode is HlslTree.NodeExpression nodeExpression) { if (nodeExpression.op == HlslOp.MemberAccess) { HlslTree.Node rhsNode = tree.allNodes[nodeExpression.rhsNodeId]; if (rhsNode is HlslTree.NodeSwizzle nodeSwizzle) { foundSwizzle = nodeSwizzle.swizzle; foundNodeId = nodeExpression.lhsNodeId; ret = true; } } } return ret; } static int[] GetSwizzleIndices(string swizzle) { int[] swizzleIndices = new int[4] { 0, 0, 0, 0 }; HlslUtil.ParserAssert(swizzle.Length <= 4); for (int i = 0; i < swizzle.Length; i++) { swizzleIndices[i] = HlslUtil.GetSwizzleIndex(swizzle[i]); } return swizzleIndices; } string GenerateNodeExpression(HlslTree.NodeExpression nodeExpression, bool isCurrApd, int currId) { string opName = tokenizer.GetTokenData(nodeExpression.opTokenId); string retStr = ""; bool useExtraParens = true; bool isPixelShader = true; SgType dstSgType = new SgType(); dstSgType = ConvertNativeTypeToSg(tree.fullTypeInfo[currId].nativeType); dstSgType.apdStatus = apdInfo.nodeApdStatus[currId]; HlslNativeType dstNativeType = tree.fullTypeInfo[currId].nativeType; ApdStatus dstApdStatus = apdInfo.nodeApdStatus[currId]; if (nodeExpression.op == HlslOp.FunctionalCall) { HlslTree.Node lhsNode = tree.allNodes[nodeExpression.lhsNodeId]; string[] argText = new string[nodeExpression.paramIds.Length]; SgType[] argSgType = new SgType[nodeExpression.paramIds.Length]; HlslNativeType[] argNativeType = new HlslNativeType[nodeExpression.paramIds.Length]; for (int i = 0; i < nodeExpression.paramIds.Length; i++) { int argId = nodeExpression.paramIds[i]; argText[i] = GenerateLinesRecurse(argId); argSgType[i] = ConvertNativeTypeToSg(tree.fullTypeInfo[argId].nativeType); argNativeType[i] = tree.fullTypeInfo[argId].nativeType; argSgType[i].apdStatus = apdInfo.nodeApdStatus[argId]; } string funcName = GetFunctionNameFromNodeId(nodeExpression.lhsNodeId); if (funcName == "SAMPLE_TEXTURE2D") { string currText = apdWriter.MakeTextureSample2d(dstSgType.slotType, dstSgType.apdStatus, dstSgType.precision, argText[0], argText[1], argSgType[2].slotType, argSgType[2].apdStatus, argSgType[2].precision, argText[2], true, isPixelShader); retStr = currText; } else if (funcName == "SAMPLE_TEXTURE2D_ARRAY") { string currText = apdWriter.MakeTextureSample2dArray(dstSgType.slotType, dstSgType.apdStatus, dstSgType.precision, argText[0], argText[1], argSgType[2].slotType, argSgType[2].apdStatus, argSgType[2].precision, argText[2], true, isPixelShader); retStr = currText; } else if (funcName == "SAMPLE_TEXTURECUBE") { string currText = apdWriter.MakeTextureSampleCube(dstSgType.slotType, dstSgType.apdStatus, dstSgType.precision, argText[0], argText[1], argSgType[2].slotType, argSgType[2].apdStatus, argSgType[2].precision, argText[2], true, isPixelShader); retStr = currText; } else if (funcName == "SAMPLE_TEXTURECUBE_ARRAY") { string currText = apdWriter.MakeTextureSampleCubeArray(dstSgType.slotType, dstSgType.apdStatus, dstSgType.precision, argText[0], argText[1], argSgType[2].slotType, argSgType[2].apdStatus, argSgType[2].precision, argText[2], true, isPixelShader); retStr = currText; } else if (funcName == "SAMPLE_TEXTURE3D") { string currText = apdWriter.MakeTextureSample3d(dstSgType.slotType, dstSgType.apdStatus, dstSgType.precision, argText[0], argText[1], argSgType[2].slotType, argSgType[2].apdStatus, argSgType[2].precision, argText[2], true, isPixelShader); retStr = currText; } else if (funcName == "ddx" || funcName == "ddy") { string baseStr = ""; if (argSgType[0].apdStatus == ApdStatus.Valid) { baseStr = argText[0] + ".m_" + funcName; } else { baseStr = funcName + "(" + argText[0] + ")"; } retStr = MakeImplicitCast(dstNativeType, dstApdStatus, argNativeType[0], ApdStatus.Invalid, baseStr); } else if (singleFuncs.ContainsKey(funcName)) { SingleFunc func = singleFuncs[funcName]; retStr = apdWriter.MakeSingleFunc(dstSgType.slotType, dstSgType.apdStatus, dstSgType.precision, argSgType[0].slotType, argSgType[0].apdStatus, argSgType[0].precision, argText[0], func); } else if (binaryFuncs.ContainsKey(funcName)) { BinaryFunc func = binaryFuncs[funcName]; retStr = apdWriter.MakeBinaryFunc(dstSgType.slotType, dstSgType.apdStatus, dstSgType.precision, argSgType[0].slotType, argSgType[0].apdStatus, argSgType[0].precision, argText[0], argSgType[1].slotType, argSgType[1].apdStatus, argSgType[1].precision, argText[1], func); } else if (func1s.ContainsKey(funcName)) { Func1 func = func1s[funcName]; retStr = apdWriter.MakeFunc1(dstSgType.slotType, dstSgType.apdStatus, dstSgType.precision, argSgType[0].slotType, argSgType[0].apdStatus, argSgType[0].precision, argText[0], func); } else if (func2s.ContainsKey(funcName)) { Func2 func = func2s[funcName]; retStr = apdWriter.MakeFunc2(dstSgType.slotType, dstSgType.apdStatus, dstSgType.precision, argSgType[0].slotType, argSgType[0].apdStatus, argSgType[0].precision, argText[0], argSgType[1].slotType, argSgType[1].apdStatus, argSgType[1].precision, argText[1], func); } else if (func3s.ContainsKey(funcName)) { Func3 func = func3s[funcName]; retStr = apdWriter.MakeFunc3(dstSgType.slotType, dstSgType.apdStatus, dstSgType.precision, argSgType[0].slotType, argSgType[0].apdStatus, argSgType[0].precision, argText[0], argSgType[1].slotType, argSgType[1].apdStatus, argSgType[1].precision, argText[1], argSgType[2].slotType, argSgType[2].apdStatus, argSgType[2].precision, argText[2], func); } else { // the lhs of a function call expression should always be the node prototype HlslTree.Node baseProtoNode = tree.allNodes[nodeExpression.lhsNodeId]; int numParams = nodeExpression.paramIds.Length; SgType[] paramSgType = new SgType[numParams]; HlslNativeType[] paramNativeType = new HlslNativeType[numParams]; if (baseProtoNode is HlslTree.NodeFunctionPrototype nodeFuncProto) { Assert.IsTrue(numParams == nodeFuncProto.declarations.Length); for (int i = 0; i < numParams; i++) { int paramId = nodeFuncProto.declarations[i]; paramSgType[i] = ConvertNativeTypeToSg(tree.fullTypeInfo[paramId].nativeType); paramNativeType[i] = tree.fullTypeInfo[paramId].nativeType; paramSgType[i].apdStatus = apdInfo.nodeApdStatus[paramId]; } } else if (baseProtoNode is HlslTree.NodeFunctionPrototypeExternal nodeFuncProtoExternal) { Assert.IsTrue(nodeFuncProtoExternal.protoInfo.paramInfoVec.Length == numParams); for (int i = 0; i < numParams; i++) { HlslParser.TypeInfo typeInfo = nodeFuncProtoExternal.protoInfo.paramInfoVec[i]; paramSgType[i] = ConvertNativeTypeToSg(typeInfo.nativeType); paramNativeType[i] = typeInfo.nativeType; paramSgType[i].apdStatus = ApdStatus.Invalid; // if it's an unknown external type, the apd is invalid } } else if (baseProtoNode is HlslTree.NodeToken nodeToken) { // if it's a token, then we don't know the prototype so just use it as is with apd invalid for (int i = 0; i < numParams; i++) { paramSgType[i] = argSgType[i]; paramNativeType[i] = argNativeType[i]; // sgtype is a struct so this operation is safe paramSgType[i].apdStatus = ApdStatus.Invalid; } } else { // should never happen HlslUtil.ParserAssert(false); } retStr += (funcName); retStr += ("("); for (int i = 0; i < nodeExpression.paramIds.Length; i++) { if (i >= 1) { retStr += ", "; } string currArg = GenerateLinesRecurse(nodeExpression.paramIds[i]); currArg = apdWriter.MakeImplicitCast(paramSgType[i].slotType, paramSgType[i].apdStatus, paramSgType[i].precision, argSgType[i].slotType, argSgType[i].apdStatus, argSgType[i].precision, currArg); retStr += currArg; } retStr += ")"; } } else { string lhsText = ""; string rhsText = ""; string rhsRhsText = ""; SgType lhsSgType = new SgType(); SgType rhsSgType = new SgType(); SgType rhsRhsSgType = new SgType(); HlslNativeType lhsNativeType = HlslNativeType._invalid; HlslNativeType rhsNativeType = HlslNativeType._invalid; HlslNativeType rhsRhsNativeType = HlslNativeType._invalid; ApdStatus lhsApdStatus = ApdStatus.Invalid; ApdStatus rhsApdStatus = ApdStatus.Invalid; ApdStatus rhsRhsApdStatus = ApdStatus.Invalid; // Node the tweak so that we don't parse function calls. This is to avoid outputting the prototype again. if (nodeExpression.lhsNodeId >= 0) { lhsNativeType = tree.fullTypeInfo[nodeExpression.lhsNodeId].nativeType; lhsApdStatus = apdInfo.nodeApdStatus[nodeExpression.lhsNodeId]; lhsText = GenerateLinesRecurse(nodeExpression.lhsNodeId); lhsSgType = ConvertNativeTypeToSg(tree.fullTypeInfo[nodeExpression.lhsNodeId].nativeType); lhsSgType.apdStatus = apdInfo.nodeApdStatus[nodeExpression.lhsNodeId]; } if (nodeExpression.rhsNodeId >= 0) { rhsNativeType = tree.fullTypeInfo[nodeExpression.rhsNodeId].nativeType; rhsApdStatus = apdInfo.nodeApdStatus[nodeExpression.rhsNodeId]; rhsText = GenerateLinesRecurse(nodeExpression.rhsNodeId); rhsSgType = ConvertNativeTypeToSg(tree.fullTypeInfo[nodeExpression.rhsNodeId].nativeType); rhsSgType.apdStatus = apdInfo.nodeApdStatus[nodeExpression.rhsNodeId]; } if (nodeExpression.rhsRhsNodeId >= 0) { rhsRhsNativeType = tree.fullTypeInfo[nodeExpression.rhsRhsNodeId].nativeType; rhsRhsApdStatus = apdInfo.nodeApdStatus[nodeExpression.rhsRhsNodeId]; rhsRhsText = GenerateLinesRecurse(nodeExpression.rhsRhsNodeId); rhsRhsSgType = ConvertNativeTypeToSg(tree.fullTypeInfo[nodeExpression.rhsRhsNodeId].nativeType); rhsRhsSgType.apdStatus = apdInfo.nodeApdStatus[nodeExpression.rhsRhsNodeId]; } switch (nodeExpression.op) { // the standard path, output lhs, then op, then rhs if valid case HlslOp.Assignment: // = { bool isLhsApd = (lhsSgType.apdStatus == ApdStatus.Valid); // first assing the rhs to lhs, and then convert from lhs to dst. That's because lhs might be apd // whereas dst might not be. string rhsModified = MakeImplicitCast(lhsNativeType, lhsApdStatus, rhsNativeType, rhsApdStatus, rhsText); int foundNodeId; string foundSwizzle; bool foundSplit = SplitSwizzleAssignFromParentIfAny(out foundNodeId, out foundSwizzle, nodeExpression.lhsNodeId); string fullExp; if (foundSplit) { string splitLhsText = GenerateLinesRecurse(foundNodeId); SgType foundSgType = ConvertNativeTypeToSg(tree.fullTypeInfo[foundNodeId].nativeType); foundSgType.apdStatus = apdInfo.nodeApdStatus[foundNodeId]; bool isSrcApd = (lhsApdStatus == ApdStatus.Valid); bool isDstApd = (isCurrApd); int[] swizzleIndices = GetSwizzleIndices(foundSwizzle); string lhsFromRhs = apdWriter.MakeSwizzleAssignApd(foundSgType.slotType, foundSgType.precision, foundSgType.apdStatus, splitLhsText, rhsModified, foundSwizzle.Length, swizzleIndices[0], swizzleIndices[1], swizzleIndices[2], swizzleIndices[3]); fullExp = MakeImplicitCast(dstNativeType, dstApdStatus, lhsNativeType, lhsApdStatus, lhsFromRhs); } else { string lhsFromRhs = lhsText + " = " + rhsModified; fullExp = MakeImplicitCast(dstNativeType, dstApdStatus, lhsNativeType, lhsApdStatus, lhsFromRhs); } retStr = fullExp; } break; case HlslOp.Mul: // * case HlslOp.Div: // / case HlslOp.Mod: // % case HlslOp.Add: // + case HlslOp.Sub: // - { if (isCurrApd) { BinaryFunc func = (BinaryFunc)(-1); if (nodeExpression.op == HlslOp.Mul) { func = BinaryFunc.Mul; } else if (nodeExpression.op == HlslOp.Div) { func = BinaryFunc.Div; } else if (nodeExpression.op == HlslOp.Add) { func = BinaryFunc.Add; } else if (nodeExpression.op == HlslOp.Sub) { func = BinaryFunc.Sub; } else { HlslUtil.ParserAssert(false); } retStr = apdWriter.MakeBinaryFunc(dstSgType.slotType, dstSgType.apdStatus, dstSgType.precision, lhsSgType.slotType, lhsSgType.apdStatus, lhsSgType.precision, lhsText, rhsSgType.slotType, rhsSgType.apdStatus, rhsSgType.precision, rhsText, func); } else { retStr += "("; retStr += GenerateLinesRecurse(nodeExpression.lhsNodeId); retStr += opName; retStr += GenerateLinesRecurse(nodeExpression.rhsNodeId); retStr += ")"; } } break; case HlslOp.AddEquals: // += case HlslOp.SubEquals: // -= case HlslOp.MulEquals: // *= case HlslOp.DivEquals: // /= case HlslOp.ModEquals: // %= case HlslOp.ShiftLEquals: // <<= case HlslOp.ShiftREquals: // >>= case HlslOp.AndEquals: // &= case HlslOp.XorEquals: // ^= case HlslOp.OrEquals: // |= { if (isCurrApd || lhsApdStatus == ApdStatus.Valid) { BinaryFunc func = (BinaryFunc)(-1); if (nodeExpression.op == HlslOp.MulEquals) { func = BinaryFunc.Mul; } else if (nodeExpression.op == HlslOp.DivEquals) { func = BinaryFunc.Div; } else if (nodeExpression.op == HlslOp.AddEquals) { func = BinaryFunc.Add; } else if (nodeExpression.op == HlslOp.SubEquals) { func = BinaryFunc.Sub; } else { HlslUtil.ParserAssert(false); } string opStr = apdWriter.MakeBinaryFunc(dstSgType.slotType, dstSgType.apdStatus, dstSgType.precision, lhsSgType.slotType, lhsSgType.apdStatus, lhsSgType.precision, lhsText, rhsSgType.slotType, rhsSgType.apdStatus, rhsSgType.precision, rhsText, func); int foundNodeId; string foundSwizzle; bool foundSplit = SplitSwizzleAssignFromParentIfAny(out foundNodeId, out foundSwizzle, nodeExpression.lhsNodeId); if (foundSplit) { string splitLhsText = GenerateLinesRecurse(foundNodeId); SgType foundSgType = ConvertNativeTypeToSg(tree.fullTypeInfo[foundNodeId].nativeType); foundSgType.apdStatus = apdInfo.nodeApdStatus[foundNodeId]; bool isSrcApd = (lhsApdStatus == ApdStatus.Valid); bool isDstApd = (isCurrApd); int[] swizzleIndices = GetSwizzleIndices(foundSwizzle); //string lhsFromRhs = "SwizzleAssign(" + splitLhsText + "," + foundSwizzle + "," + rhsModified + ")"; //string lhsFromRhs = apdWriter.MakeSwizzleAssignApd() string lhsFromRhs = apdWriter.MakeSwizzleAssignApd(foundSgType.slotType, foundSgType.precision, foundSgType.apdStatus, splitLhsText, opStr, foundSwizzle.Length, swizzleIndices[0], swizzleIndices[1], swizzleIndices[2], swizzleIndices[3]); retStr = MakeImplicitCast(dstNativeType, dstApdStatus, lhsNativeType, lhsApdStatus, lhsFromRhs); } else { // note that we can actually have some failure cases here I believe, such as: // A++ += B // would turn into: // A++ = AddApd(A++,B) // but this approximation should be good enough for now. retStr = "(" + lhsText + "=" + opStr + ")"; } } else { if (useExtraParens) { retStr += "("; } retStr += GenerateLinesRecurse(nodeExpression.lhsNodeId); retStr += opName; retStr += GenerateLinesRecurse(nodeExpression.rhsNodeId); if (useExtraParens) { retStr += ")"; } } } break; case HlslOp.FunctionalCall: // func() HlslUtil.ParserAssert(false); { } break; case HlslOp.PostIncrement: // ++ case HlslOp.PostDecrement: // -- HlslUtil.ParserAssert(!isCurrApd); retStr += GenerateLinesRecurse(nodeExpression.lhsNodeId) + opName; break; case HlslOp.UnaryMinus: // - if (isCurrApd || lhsApdStatus == ApdStatus.Valid) { retStr = apdWriter.MakeSingleFunc(dstSgType.slotType, dstSgType.apdStatus, dstSgType.precision, lhsSgType.slotType, lhsSgType.apdStatus, lhsSgType.precision, lhsText, SingleFunc.Negate); } else { retStr += opName + GenerateLinesRecurse(nodeExpression.lhsNodeId); } break; case HlslOp.UnaryPlus: // + case HlslOp.LogicalNot: // ! case HlslOp.BitwiseNot: // ~ HlslUtil.ParserAssert(!isCurrApd); retStr += opName + GenerateLinesRecurse(nodeExpression.lhsNodeId); break; case HlslOp.PreIncrement: // ++ case HlslOp.PreDecrement: // -- HlslUtil.ParserAssert(!isCurrApd); retStr += opName + GenerateLinesRecurse(nodeExpression.lhsNodeId); break; case HlslOp.Invalid: // case HlslOp.FunctionalCast: // int() break; case HlslOp.Subscript: // [] { bool isLhsApd = (lhsApdStatus == ApdStatus.Valid); // is the lhs type an array (meaning that the subscript is an index into the array) // or is the lhs type a non-array, meaning the index refers to a channel (equivalent // to a swizzle). We are only going to do a special path for the swizzle equivalent. int arrayDim = tree.fullTypeInfo[nodeExpression.lhsNodeId].arrayDims; if (isLhsApd && arrayDim == 0) { // This path is illegal on the left side of an assignment operator. To support it, // we would have to add support similar to swizzle. retStr = apdWriter.ExtractIndexApd(lhsSgType.slotType, lhsText, lhsSgType.precision, rhsText); } else { retStr += GenerateLinesRecurse(nodeExpression.lhsNodeId); retStr += "["; retStr += GenerateLinesRecurse(nodeExpression.rhsNodeId); retStr += "]"; } } break; case HlslOp.MemberAccess: // . { // is this a swizzle HlslTree.Node rhsNode = tree.allNodes[nodeExpression.rhsNodeId]; if (rhsNode is HlslTree.NodeSwizzle nodeSwizzle) { bool isSrcApd = (lhsApdStatus == ApdStatus.Valid); bool isDstApd = (isCurrApd); int[] swizzleIndices = GetSwizzleIndices(nodeSwizzle.swizzle); if (isSrcApd) { if (isDstApd) { // both are apd, so use the writer swizzle retStr = apdWriter.MakeSwizzleApd(lhsSgType.slotType, lhsSgType.precision, lhsText, nodeSwizzle.swizzle.Length, swizzleIndices[0], swizzleIndices[1], swizzleIndices[2], swizzleIndices[3]); } else { // src is apd, but dst is not, so just use .m_val retStr += lhsText + ".m_val." + nodeSwizzle.swizzle; } } else { if (isDstApd) { // Dst is apd, but src is not, which should never happen but we'll handle it anways. // Swizzle and then cast to apd string baseText = lhsText + "." + nodeSwizzle.swizzle; // get the base type HlslNativeType baseType = HlslUtil.GetNativeBaseType(lhsNativeType); HlslNativeType swizzleType = HlslUtil.GetBaseTypeWithDims(baseType, 1, nodeSwizzle.swizzle.Length); retStr += MakeImplicitCast(swizzleType, lhsApdStatus, dstNativeType, dstApdStatus, baseText); } else { // both src and dst are not apd, so the vanilla swizzle is fine retStr += lhsText + "." + nodeSwizzle.swizzle; } } } else if (rhsNode is HlslTree.NodeMemberFunction nodeMemberFunc) { HlslUtil.StructInfo structInfo = tree.GetStructInfoFromNodeId(nodeMemberFunc.structNodeId, unityReserved); HlslUtil.PrototypeInfo protoInfo = structInfo.prototypes[nodeMemberFunc.funcIndex]; if (structInfo.identifier == "UnityTexture2D" && protoInfo.identifier == "GetTransformedUV") { HlslUtil.ParserAssert(protoInfo.paramInfoVec.Length == 1); string texText = lhsText; int uvNodeId = nodeMemberFunc.funcParamNodeIds[0]; string uvText = GenerateLinesRecurse(uvNodeId); HlslNativeType uvNativeType = tree.fullTypeInfo[uvNodeId].nativeType; ApdStatus uvApdStatus = apdInfo.nodeApdStatus[uvNodeId]; // the internal is always APD, so coerce the input to a float2 apd and coerce the output from a float2 apd string uvCoerceText = MakeImplicitCast(HlslNativeType._float2, ApdStatus.Valid, uvNativeType, uvApdStatus, uvText); string fullFuncCall = "GetTransformedUVApd(" + texText + "," + uvCoerceText + ")"; string dstExpression = MakeImplicitCast(dstNativeType, dstApdStatus, HlslNativeType._float2, ApdStatus.Valid, fullFuncCall); retStr = dstExpression; } else { retStr += GenerateLinesRecurse(nodeExpression.lhsNodeId); retStr += "."; retStr += GenerateLinesRecurse(nodeExpression.rhsNodeId); } } else if (rhsNode is HlslTree.NodeMemberVariable) { // we have a special exception if we are fetching a uv with known derivatives from SurfaceDescriptionInputs int foundUv = -1; { HlslTree.FullTypeInfo lhsInfo = tree.fullTypeInfo[nodeExpression.lhsNodeId]; if (lhsInfo.identifier == "SurfaceDescriptionInputs") { // check the identifer on rhs //HlslTree.NodeMemberVariable rhsMemberNode = (HlslTree.NodeMemberVariable)tree.allNodes[nodeExpression.rhsNodeId]; HlslTree.Node rhsBaseNode = tree.allNodes[nodeExpression.rhsNodeId]; if (rhsBaseNode is HlslTree.NodeMemberVariable rhsMemberNode) { HlslUtil.StructInfo structInfo = tree.GetStructInfoFromNodeId(sdInputsStructId, unityReserved); HlslUtil.FieldInfo fieldInfo = structInfo.fields[rhsMemberNode.fieldIndex]; UVChannel uvIndex = (UVChannel)GetUvIndex(fieldInfo.identifier); if (uvIndex >= 0 && uvDerivatives.Contains(uvIndex)) { foundUv = (int)uvIndex; } } } } if (foundUv >= 0 && isCurrApd) { retStr = "_unityUv" + foundUv.ToString(); } else { retStr += GenerateLinesRecurse(nodeExpression.lhsNodeId); retStr += "."; retStr += GenerateLinesRecurse(nodeExpression.rhsNodeId); } } else { // should be no other legal rhs types HlslUtil.ParserAssert(false); } } break; case HlslOp.CStyleCast: // (int) { retStr += "("; if (nodeExpression.cstyleCastStructId >= 0) { string nativeTypeName = tokenizer.GetTokenData(nodeExpression.opTokenId); retStr += nativeTypeName; } else { HlslTree.FullTypeInfo structInfo = tree.fullTypeInfo[nodeExpression.cstyleCastStructId]; retStr += structInfo.identifier; } retStr += ")"; retStr += GenerateLinesRecurse(nodeExpression.lhsNodeId); } break; case HlslOp.TernaryQuestion: // ? { retStr += "("; retStr += GenerateLinesRecurse(nodeExpression.lhsNodeId); retStr += " ? "; retStr += GenerateLinesRecurse(nodeExpression.rhsNodeId); retStr += " : "; retStr += GenerateLinesRecurse(nodeExpression.rhsRhsNodeId); retStr += ")"; } break; case HlslOp.ShiftL: // << case HlslOp.ShiftR: // >> case HlslOp.ThreeWayCompare: // <=> - never used this case HlslOp.ScopeReslution: // :: case HlslOp.LessThan: // < case HlslOp.GreaterThan: // > case HlslOp.LessEqual: // <= case HlslOp.GreaterEqual: // >= case HlslOp.CompareEqual: // == case HlslOp.NotEqual: // != case HlslOp.BitwiseAnd: // & case HlslOp.BitwiseXor: // ^ case HlslOp.BitwiseOr: // | case HlslOp.LogicalAnd: // && case HlslOp.LogicalOr: // || case HlslOp.Comma: // , { if (useExtraParens) { retStr += "("; } retStr += GenerateLinesRecurse(nodeExpression.lhsNodeId); retStr += opName; retStr += GenerateLinesRecurse(nodeExpression.rhsNodeId); if (useExtraParens) { retStr += ")"; } } break; // unsupported case HlslOp.Dereference: // * - not legal in HLSL? case HlslOp.AddressOf: // & - not legal in HLSL? case HlslOp.Sizeof: // sizeof case HlslOp.PointerToMemberDot: // .* - not legal in HLSL case HlslOp.PointerToMemorArrow: // ->* - not legal in HLSL case HlslOp.TernaryColon: // : retStr = ""; break; default: retStr = ""; break; } } return retStr; } struct SgType { internal bool isValidSg; internal ConcretePrecision precision; internal ConcreteSlotValueType slotType; internal ApdStatus apdStatus; } static bool IsValidSgType(HlslNativeType nativeType) { bool ret = false; switch (nativeType) { case HlslNativeType._float: case HlslNativeType._float1: case HlslNativeType._float2: case HlslNativeType._float3: case HlslNativeType._float4: case HlslNativeType._half: case HlslNativeType._half1: case HlslNativeType._half2: case HlslNativeType._half3: case HlslNativeType._half4: case HlslNativeType._float2x2: case HlslNativeType._half2x2: case HlslNativeType._float3x3: case HlslNativeType._half3x3: case HlslNativeType._float4x4: case HlslNativeType._half4x4: case HlslNativeType._int: case HlslNativeType._int1: case HlslNativeType._int2: case HlslNativeType._int3: case HlslNativeType._int4: case HlslNativeType._int2x2: case HlslNativeType._int3x3: case HlslNativeType._int4x4: case HlslNativeType._uint: case HlslNativeType._uint1: case HlslNativeType._uint2: case HlslNativeType._uint3: case HlslNativeType._uint4: case HlslNativeType._uint2x2: case HlslNativeType._uint3x3: case HlslNativeType._uint4x4: ret = true; break; default: ret = false; break; } return ret; } static ConcreteSlotValueType GetSgSlotType(HlslNativeType nativeType) { ConcreteSlotValueType slotType = (ConcreteSlotValueType)(-1); switch (nativeType) { case HlslNativeType._int: case HlslNativeType._int1: case HlslNativeType._uint: case HlslNativeType._uint1: case HlslNativeType._half: case HlslNativeType._half1: case HlslNativeType._float: case HlslNativeType._float1: slotType = ConcreteSlotValueType.Vector1; break; case HlslNativeType._int2: case HlslNativeType._uint2: case HlslNativeType._half2: case HlslNativeType._float2: slotType = ConcreteSlotValueType.Vector2; break; case HlslNativeType._int3: case HlslNativeType._uint3: case HlslNativeType._half3: case HlslNativeType._float3: slotType = ConcreteSlotValueType.Vector3; break; case HlslNativeType._int4: case HlslNativeType._uint4: case HlslNativeType._half4: case HlslNativeType._float4: slotType = ConcreteSlotValueType.Vector4; break; case HlslNativeType._int2x2: case HlslNativeType._uint2x2: case HlslNativeType._half2x2: case HlslNativeType._float2x2: slotType = ConcreteSlotValueType.Matrix2; break; case HlslNativeType._int3x3: case HlslNativeType._uint3x3: case HlslNativeType._half3x3: case HlslNativeType._float3x3: slotType = ConcreteSlotValueType.Matrix3; break; case HlslNativeType._int4x4: case HlslNativeType._uint4x4: case HlslNativeType._half4x4: case HlslNativeType._float4x4: slotType = ConcreteSlotValueType.Matrix4; break; default: slotType = ConcreteSlotValueType.Vector1; break; } return slotType; } static ConcretePrecision GetSgPrecision(HlslNativeType nativeType) { ConcretePrecision precision = ConcretePrecision.Single; switch (nativeType) { case HlslNativeType._float: case HlslNativeType._float1: case HlslNativeType._float2: case HlslNativeType._float3: case HlslNativeType._float4: case HlslNativeType._float2x2: case HlslNativeType._float3x3: case HlslNativeType._float4x4: precision = ConcretePrecision.Single; break; case HlslNativeType._half: case HlslNativeType._half1: case HlslNativeType._half2: case HlslNativeType._half3: case HlslNativeType._half4: case HlslNativeType._half2x2: case HlslNativeType._half3x3: case HlslNativeType._half4x4: precision = ConcretePrecision.Half; break; // treat ints as floats case HlslNativeType._int: case HlslNativeType._int1: case HlslNativeType._int2: case HlslNativeType._int3: case HlslNativeType._int4: case HlslNativeType._int2x2: case HlslNativeType._int3x3: case HlslNativeType._int4x4: precision = ConcretePrecision.Single; break; // treat uints as floats case HlslNativeType._uint: case HlslNativeType._uint1: case HlslNativeType._uint2: case HlslNativeType._uint3: case HlslNativeType._uint4: case HlslNativeType._uint2x2: case HlslNativeType._uint3x3: case HlslNativeType._uint4x4: precision = ConcretePrecision.Single; break; default: break; } return precision; } static SgType ConvertNativeTypeToSg(HlslNativeType nativeType) { SgType sgType = new SgType(); sgType.isValidSg = IsValidSgType(nativeType); if (sgType.isValidSg) { sgType.precision = GetSgPrecision(nativeType); sgType.slotType = GetSgSlotType(nativeType); } return sgType; } bool IsTokenNativeTypeAndApdValid(HlslToken token) { bool ret = false; if (tokenToNativeTable.ContainsKey(token)) { HlslNativeType nativeType = tokenToNativeTable[token]; ret = HlslUtil.IsNativeTypeLegalForApd(nativeType); } return ret; } // isApd refers to if the calling function is expecting an Apd type. For example, an expression node with // a mul node decides if the child should be Apd or not, and then the child function must coerce // the parameter to match what the parent expects. string GenerateLinesRecurse(int nodeId) { // Silently succeed for invalid nodes. Should fix this later. if (nodeId < 0) { return ""; } nodeStack.Add(nodeId); HlslTree.Node baseNode = tree.allNodes[nodeId]; int parentId = nodeParents[nodeId]; bool isCurrApd = (apdInfo.nodeApdStatus[nodeId] == ApdStatus.Valid); bool isParentApd = false; if (parentId >= 0) { isParentApd = (apdInfo.nodeApdStatus[parentId] == ApdStatus.Valid); } string retStr = ""; HlslTree.FullTypeInfo currTypeInfo = tree.fullTypeInfo[nodeId]; if (isCurrApd) { bool isApdLegal = HlslUtil.IsNativeTypeLegalForApd(currTypeInfo.nativeType); if (!isApdLegal) { HlslUtil.ParserAssert(isApdLegal); } } if (baseNode is HlslTree.NodeTopLevel nodeTopLevel) { AppendString(""); FinishLine(); for (int i = 0; i < nodeTopLevel.statements.Length; i++) { GenerateLinesRecurse(nodeTopLevel.statements[i]); } retStr = ""; } else if (baseNode is HlslTree.NodeExpression nodeExpression) { retStr = GenerateNodeExpression(nodeExpression, isCurrApd, nodeId); } else if (baseNode is HlslTree.NodeStruct nodeStruct) { string typeName = tokenizer.GetTokenData(nodeStruct.nameTokenId); AppendString("struct " + typeName); FinishLine(); AppendString("{"); FinishLine(); { IndentPush(); for (int i = 0; i < nodeStruct.declarations.Length; i++) { string currLine = GenerateLinesRecurse(nodeStruct.declarations[i]); //AppendString(";"); currLine += ";"; AppendString(currLine); FinishLine(); } IndentPop(); } AppendString("};"); FinishLine(); FinishLine(); retStr = ""; } else if (baseNode is HlslTree.NodeUnityStruct nodeUnityStruct) { retStr = ""; } else if (baseNode is HlslTree.NodeFunctionPrototype nodeFunctionPrototype) { string retName = tokenizer.GetTokenData(nodeFunctionPrototype.returnTokenId); string funcName = tokenizer.GetTokenData(nodeFunctionPrototype.nameTokenId); AppendString(retName + " " + funcName + "("); string fullProto = ""; for (int i = 0; i < nodeFunctionPrototype.declarations.Length; i++) { if (i >= 1) { fullProto += ", "; } fullProto += GenerateLinesRecurse(nodeFunctionPrototype.declarations[i]); } AppendString(fullProto); AppendString(")"); retStr = ""; } else if (baseNode is HlslTree.NodeFunctionPrototypeExternal nodeFunctionPrototypeExternal) { // no need to write external protypes, so no op retStr = ""; } else if (baseNode is HlslTree.NodeBlock nodeBlock) { AppendString("{"); FinishLine(); IndentPush(); // if this block is the initial block of SurfaceDescriptionFunction(), then add special UVs variables. if (parentId >= 0) { HlslTree.Node parentNode = tree.allNodes[parentId]; if (parentNode is HlslTree.NodeFunction nodeFunc) { HlslTree.Node node = tree.allNodes[nodeFunc.prototypeId]; if (node is HlslTree.NodeFunctionPrototype nodeFuncProto) { string identifier = tokenizer.GetTokenData(nodeFuncProto.nameTokenId); if (identifier == "SurfaceDescriptionFunction") { AppendString("// main surface function, so adding uv derivatives here"); FinishLine(); for (int uvIter = 0; uvIter < uvDerivatives.Count; uvIter++) { int uv = (int)uvDerivatives[uvIter]; string attrib = "IN.uv" + uv.ToString(); string attribDdx = "IN.uv" + uv.ToString() + "Ddx"; string attribDdy = "IN.uv" + uv.ToString() + "Ddy"; if (applyEmulatedDerivatives) { attribDdx = "ddx(IN.uv" + uv.ToString() + ")"; attribDdy = "ddy(IN.uv" + uv.ToString() + ")"; } string dstVal = apdWriter.MakeStructFromApdDirect(ConcreteSlotValueType.Vector4, attrib, attribDdx, attribDdy, ConcretePrecision.Single); AppendString("FloatApd4 _unityUv" + uv.ToString() + " = " + dstVal + ";"); FinishLine(); } } else { AppendString("// not main surface function, so no IN.uv derivatives here"); FinishLine(); } } } } for (int i = 0; i < nodeBlock.statements.Length; i++) { GenerateLinesRecurse(nodeBlock.statements[i]); } IndentPop(); AppendString("}"); FinishLine(); retStr = ""; } else if (baseNode is HlslTree.NodeStatement nodeStatement) { switch (nodeStatement.type) { case HlslStatementType.If: { string currExp = GenerateLinesRecurse(nodeStatement.expression); AppendString("if (" + currExp + ")"); FinishLine(); { IndentPush(); string childText = GenerateLinesRecurse(nodeStatement.childBlockOrStatement); IndentPop(); } if (nodeStatement.elseBlockOrStatement >= 0) { AppendString("else "); IndentPush(); string childText = GenerateLinesRecurse(nodeStatement.elseBlockOrStatement); IndentPop(); } } break; case HlslStatementType.Switch: { string currExp = GenerateLinesRecurse(nodeStatement.expression); AppendString("switch (" + currExp + ")"); FinishLine(); GenerateLinesRecurse(nodeStatement.childBlockOrStatement); } break; case HlslStatementType.Case: { string currExp = GenerateLinesRecurse(nodeStatement.expression); AppendString("case " + currExp + ":"); FinishLine(); } break; case HlslStatementType.Break: { AppendString("break;"); FinishLine(); } break; case HlslStatementType.Continue: { AppendString("continue;"); FinishLine(); } break; case HlslStatementType.Default: { AppendString("default:"); FinishLine(); } break; case HlslStatementType.Goto: { string currExp = GenerateLinesRecurse(nodeStatement.expression); AppendString("goto " + currExp + ";"); FinishLine(); } break; case HlslStatementType.Label: { string currExp = GenerateLinesRecurse(nodeStatement.expression); AppendString(currExp + ":"); FinishLine(); } break; case HlslStatementType.For: { string firstLine = "for("; firstLine += GenerateLinesRecurse(nodeStatement.forExpressions[0]); firstLine += ";"; firstLine += GenerateLinesRecurse(nodeStatement.forExpressions[1]); firstLine += ";"; firstLine += GenerateLinesRecurse(nodeStatement.forExpressions[2]); firstLine += ")"; AppendString(firstLine); FinishLine(); GenerateLinesRecurse(nodeStatement.childBlockOrStatement); } break; case HlslStatementType.Do: { AppendString("do"); FinishLine(); GenerateLinesRecurse(nodeStatement.childBlockOrStatement); string currExp = GenerateLinesRecurse(nodeStatement.expression); AppendString("while(" + currExp + ")"); FinishLine(); } break; case HlslStatementType.While: { string currExp = GenerateLinesRecurse(nodeStatement.expression); AppendString("while(" + currExp + ")"); FinishLine(); GenerateLinesRecurse(nodeStatement.childBlockOrStatement); } break; case HlslStatementType.Expression: { string currLine = GenerateLinesRecurse(nodeStatement.expression); AppendString(currLine + ";"); FinishLine(); } break; case HlslStatementType.Return: { AppendString("return "); string currLine = GenerateLinesRecurse(nodeStatement.expression); AppendString(currLine + ";"); FinishLine(); } break; default: HlslUtil.ParserAssert(false); break; } retStr = ""; } else if (baseNode is HlslTree.NodeFunction nodeFunction) { if (nodeFunction.parseErr.Length > 0) { AppendString("// Parse error, resorting to direct text: "); FinishLine(); AppendString("// " + nodeFunction.parseErr); FinishLine(); } GenerateLinesRecurse(nodeFunction.prototypeId); FinishLine(); GenerateLinesRecurse(nodeFunction.blockId); FinishLine(); } else if (baseNode is HlslTree.NodeDeclaration nodeDeclaration) { string currStr = ""; currStr = ""; bool isDeclApd = (apdInfo.nodeApdStatus[nodeId] == ApdStatus.Valid); if (nodeDeclaration.isMacroDecl) { string tokenName = tokenizer.GetTokenData(nodeDeclaration.nameTokenId); currStr += (nodeDeclaration.macroDeclString + "(" + tokenName + ")"); } else { bool isLegalForApd = HlslUtil.IsNativeTypeLegalForApd(currTypeInfo.nativeType); if (nodeDeclaration.modifierTokenId >= 0) { string modifierName = tokenizer.GetTokenData(nodeDeclaration.modifierTokenId); currStr += (modifierName); currStr += (" "); } string typeName = currTypeInfo.GetTypeString(); if (isLegalForApd && isCurrApd) { typeName = HlslUtil.GetNativeTypeStringApd(currTypeInfo.nativeType); } currStr += (typeName); currStr += (" "); string tokenName = tokenizer.GetTokenData(nodeDeclaration.nameTokenId); if (nodeDeclaration.subTypeNodeId >= 0) { currStr += ("<"); currStr += (nodeDeclaration.subTypeNodeId); currStr += ("> "); } currStr += (tokenName); for (int i = 0; i < nodeDeclaration.arrayDims.Length; i++) { currStr += "["; currStr += GenerateLinesRecurse(nodeDeclaration.arrayDims[i]); currStr += "]"; } if (nodeDeclaration.initializerId >= 0) { currStr += (" = "); string intializerText = GenerateLinesRecurse(nodeDeclaration.initializerId); HlslTree.FullTypeInfo declInfo = tree.fullTypeInfo[nodeId]; HlslTree.FullTypeInfo initInfo = tree.fullTypeInfo[nodeDeclaration.initializerId]; ApdStatus declStatus = apdInfo.nodeApdStatus[nodeId]; ApdStatus initStatus = apdInfo.nodeApdStatus[nodeDeclaration.initializerId]; string coerceText = MakeImplicitCast(declInfo.nativeType, declStatus, initInfo.nativeType, initStatus, intializerText); currStr += coerceText; } } // parent should always be valid, since the only allowed node without // a parent is the top-level node HlslTree.Node parentNode = tree.allNodes[parentId]; if (parentNode is HlslTree.NodeTopLevel || parentNode is HlslTree.NodeBlock) { AppendString(currStr + ";"); FinishLine(); retStr = ""; } else { retStr = currStr; } } else if (baseNode is HlslTree.NodeVariable nodeVariable) { string tokenName = tokenizer.GetTokenData(nodeVariable.nameTokenId); if (nodeVariable.nameVariableId >= 0) { // if it's defined in this hlsl code, fetch it HlslTree.VariableInfo variableInfo = tree.allVariables[nodeVariable.nameVariableId]; ApdStatus declStatus = apdInfo.nodeApdStatus[variableInfo.declId]; ApdStatus variableStatus = apdInfo.nodeApdStatus[nodeId]; HlslTree.FullTypeInfo fti = tree.fullTypeInfo[nodeId]; string coerceText = MakeImplicitCast(fti.nativeType, variableStatus, fti.nativeType, declStatus, tokenName); retStr = (coerceText); } else { // if it's a global, treat it as is ApdStatus declStatus = ApdStatus.Zero; ApdStatus variableStatus = apdInfo.nodeApdStatus[nodeId]; HlslTree.FullTypeInfo fti = tree.fullTypeInfo[nodeId]; string coerceText = MakeImplicitCast(fti.nativeType, variableStatus, fti.nativeType, declStatus, tokenName); retStr = (coerceText); } } else if (baseNode is HlslTree.NodeNativeConstructor nodeNativeConstructor) { retStr = ""; ApdStatus dstStatus = apdInfo.nodeApdStatus[nodeId]; int numCols = HlslUtil.GetNumCols(nodeNativeConstructor.nativeType); bool isOneChanPerArg = (numCols == nodeNativeConstructor.paramNodeIds.Length); if (dstStatus == ApdStatus.Valid && isOneChanPerArg) { // assume that all constructors are only one component per parameter, otherwise force to fpd // the parser rejects and native constructors with mixed types HlslUtil.ParserAssert(numCols == nodeNativeConstructor.paramNodeIds.Length); HlslNativeType dstBaseType = HlslUtil.GetNativeBaseType(nodeNativeConstructor.nativeType); string[] paramTextVec = new string[numCols]; SgType[] paramSgType = new SgType[numCols]; for (int i = 0; i < numCols; i++) { int argId = nodeNativeConstructor.paramNodeIds[i]; string argText = GenerateLinesRecurse(argId); HlslTree.FullTypeInfo argInfo = tree.fullTypeInfo[argId]; ApdStatus argStatus = apdInfo.nodeApdStatus[argId]; // always convert to apd, might need to convert to float/half string coerceText = MakeImplicitCast(dstBaseType, ApdStatus.Valid, argInfo.nativeType, argStatus, argText); paramTextVec[i] = coerceText; paramSgType[i] = ConvertNativeTypeToSg(argInfo.nativeType); paramSgType[i].apdStatus = ApdStatus.Valid; } SgType dstSgType = new SgType(); dstSgType = ConvertNativeTypeToSg(nodeNativeConstructor.nativeType); dstSgType.apdStatus = dstStatus; // note: args have already been converted to single apd values if (numCols == 1) { retStr = paramTextVec[0]; } else if (numCols == 2) { retStr = apdWriter.MakeMerge2(dstSgType.slotType, dstSgType.apdStatus, dstSgType.precision, paramSgType[0].slotType, paramSgType[0].apdStatus, paramSgType[0].precision, paramTextVec[0], paramSgType[1].slotType, paramSgType[1].apdStatus, paramSgType[1].precision, paramTextVec[1]); } else if (numCols == 3) { retStr = apdWriter.MakeMerge3(dstSgType.slotType, dstSgType.apdStatus, dstSgType.precision, paramSgType[0].slotType, paramSgType[0].apdStatus, paramSgType[0].precision, paramTextVec[0], paramSgType[1].slotType, paramSgType[1].apdStatus, paramSgType[1].precision, paramTextVec[1], paramSgType[2].slotType, paramSgType[2].apdStatus, paramSgType[2].precision, paramTextVec[2]); } else if (numCols == 4) { retStr = apdWriter.MakeMerge4(dstSgType.slotType, dstSgType.apdStatus, dstSgType.precision, paramSgType[0].slotType, paramSgType[0].apdStatus, paramSgType[0].precision, paramTextVec[0], paramSgType[1].slotType, paramSgType[1].apdStatus, paramSgType[1].precision, paramTextVec[1], paramSgType[2].slotType, paramSgType[2].apdStatus, paramSgType[2].precision, paramTextVec[2], paramSgType[3].slotType, paramSgType[3].apdStatus, paramSgType[3].precision, paramTextVec[3]); } else { HlslUtil.ParserAssert(false); } } else { string nativeTypeName = HlslUtil.GetNativeTypeString(nodeNativeConstructor.nativeType); retStr += (nativeTypeName + "("); for (int i = 0; i < nodeNativeConstructor.paramNodeIds.Length; i++) { if (i >= 1) { retStr += ", "; } int argId = nodeNativeConstructor.paramNodeIds[i]; string argText = GenerateLinesRecurse(argId); HlslTree.FullTypeInfo argInfo = tree.fullTypeInfo[argId]; ApdStatus argStatus = apdInfo.nodeApdStatus[argId]; // always convert to fpd string coerceText = MakeImplicitCast(argInfo.nativeType, ApdStatus.Invalid, argInfo.nativeType, argStatus, argText); retStr += coerceText; } retStr += ")"; } } else if (baseNode is HlslTree.NodeToken nodeToken) { retStr = ""; string tokenData = tokenizer.GetTokenData(nodeToken.srcTokenId); retStr += tokenData; } else if (baseNode is HlslTree.NodeNativeType nodeNativeType) { retStr = ""; string nativeTypeName = HlslUtil.GetNativeTypeString(nodeNativeType.nativeType); retStr += nativeTypeName; } else if (baseNode is HlslTree.NodeLiteralOrBool nodeLiteralOrBool) { retStr = ""; string literalToken = tokenizer.GetTokenData(nodeLiteralOrBool.nameTokenId); retStr += literalToken; } else if (baseNode is HlslTree.NodePassthrough nodePassthrough) { retStr = ""; for (int i = 0; i < nodePassthrough.tokenIds.Length; i++) { int tokenId = nodePassthrough.tokenIds[i]; string passthroughToken = tokenizer.GetTokenData(tokenId); HlslToken tokenType = tokenizer.GetTokenType(tokenId); if (tokenType == HlslToken._preprocessor) { string sectionPreproc = "#section"; if (passthroughToken.StartsWith(sectionPreproc)) { passthroughToken = ""; } else { passthroughToken = "\n" + passthroughToken + "\n"; } } else if (tokenType == HlslToken._comment_single) { passthroughToken += "\n"; } if (tokenId < tokenizer.foundTokens.Count - 1) { HlslTokenizer.SingleToken currToken = tokenizer.foundTokens[tokenId + 0]; HlslTokenizer.SingleToken nextToken = tokenizer.foundTokens[tokenId + 1]; if (!currToken.data.Contains('\n') && currToken.marker.indexLine < nextToken.marker.indexLine) { currLine += "\n"; } } retStr += passthroughToken; } // for cases like a passthrough node for an entire block, write out the text. otherwise return // the text and let the parent handle it. if (nodePassthrough.writeDirect) { AppendString(retStr); FinishLine(); retStr = ""; } } else if (baseNode is HlslTree.NodeMemberVariable nodeMemberVariable) { retStr = ""; HlslUtil.StructInfo structInfo = tree.GetStructInfoFromNodeId(nodeMemberVariable.structNodeId, unityReserved); HlslUtil.FieldInfo fieldInfo = structInfo.fields[nodeMemberVariable.fieldIndex]; retStr += fieldInfo.identifier; } else if (baseNode is HlslTree.NodeParenthesisGroup nodeParenthesisGroup) { retStr = ""; retStr += "("; retStr += GenerateLinesRecurse(nodeParenthesisGroup.childNodeId); retStr += ")"; } else if (baseNode is HlslTree.NodeMemberFunction nodeMemberFunction) { retStr = ""; HlslUtil.StructInfo structInfo = tree.GetStructInfoFromNodeId(nodeMemberFunction.structNodeId, unityReserved); HlslUtil.PrototypeInfo protoInfo = structInfo.prototypes[nodeMemberFunction.funcIndex]; retStr += protoInfo.identifier; retStr += "("; for (int i = 0; i < nodeMemberFunction.funcParamNodeIds.Length; i++) { if (i >= 1) { retStr += ", "; } retStr += GenerateLinesRecurse(nodeMemberFunction.funcParamNodeIds[i]); } retStr += ")"; } else if (baseNode is HlslTree.NodeSwizzle nodeSwizzle) { // should never happen, since the swizzle is handled by the parent node (because // it needs to handle the apd conversions) retStr = ""; } else if (baseNode is HlslTree.NodeBlockInitializer nodeBlockInit) { retStr = ""; retStr += "{"; for (int i = 0; i < nodeBlockInit.initNodeIds.Length; i++) { if (i > 0) { retStr += ", "; } // if this node is apd, convert to fps int argId = nodeBlockInit.initNodeIds[i]; string argText = GenerateLinesRecurse(argId); HlslTree.FullTypeInfo argInfo = tree.fullTypeInfo[argId]; ApdStatus argStatus = apdInfo.nodeApdStatus[argId]; // always convert to fpd string coerceText = MakeImplicitCast(argInfo.nativeType, ApdStatus.Invalid, argInfo.nativeType, argStatus, argText); retStr += coerceText; } retStr += "}"; } else { // if we got here, then we are missing a type in this if tree HlslUtil.ParserAssert(false); } { // Each push should have a corresponding pop, unless we have an error/exception, in // which case this node stack should let us track down where it went wrong. int topIndex = nodeStack.Count - 1; HlslUtil.ParserAssert(nodeStack[topIndex] == nodeId); nodeStack.RemoveAt(topIndex); } return retStr; } int FindSurfaceDescriptionInputsStructId() { int sdInputsStructId = -1; int numNodes = tree.allNodes.Count; for (int nodeIter = 0; nodeIter < numNodes; nodeIter++) { HlslTree.Node node = tree.allNodes[nodeIter]; if (node is HlslTree.NodeStruct nodeStruct) { string structIdentifier = tokenizer.GetTokenData(nodeStruct.nameTokenId); if (structIdentifier == "SurfaceDescriptionInputs") { sdInputsStructId = nodeIter; } } } return sdInputsStructId; } static int GetUvIndex(string name) { int uvIndex = -1; if (name.Length == 3) { if (name[0] == 'u' && name[1] == 'v') { uvIndex = name[2] - '0'; if (uvIndex < 0 || uvIndex >= 4) { uvIndex = -1; } } } return uvIndex; } internal static string FlattenStringList(List strList) { string ret = ""; for (int i = 0; i < strList.Count; i++) { ret += strList[i] + "\n"; } return ret; } internal static string FlattenStringVec(string[] strList) { string ret = ""; for (int i = 0; i < strList.Length; i++) { ret += strList[i] + "\n"; } return ret; } internal string[] GenerateLines(List debugNodeStack, string shaderName) { nodeStack = debugNodeStack; // start as valid, disable if we find an error isValid = true; errList = new List(); allLines = new List(); currLine = ""; indentLevel = 0; if (tree.topLevelNode < 0) { LogError("Missing top level node"); } ResolveTypeInfoAndParents(); uvDerivatives = new List(); sdInputsStructId = FindSurfaceDescriptionInputsStructId(); { // find the struct id of SurfaceDescriptionInputs if (sdInputsStructId >= 0) { HlslUtil.ParserAssert(tree.allNodes[sdInputsStructId] is HlslTree.NodeStruct); HlslTree.NodeStruct node = (HlslTree.NodeStruct)tree.allNodes[sdInputsStructId]; for (int declIter = 0; declIter < node.declarations.Length; declIter++) { int declId = node.declarations[declIter]; HlslUtil.ParserAssert(tree.allNodes[declId] is HlslTree.NodeDeclaration); HlslTree.NodeDeclaration nodeDecl = (HlslTree.NodeDeclaration)tree.allNodes[declId]; string name = tokenizer.GetTokenData(nodeDecl.nameTokenId); UVChannel uvIndex = (UVChannel)GetUvIndex(name); if (uvIndex >= 0) { uvDerivatives.Add(uvIndex); } } } } List sectionUnknown = new List(); List sectionGraphFunction = new List(); List sectionGraphPixel = new List(); HlslTree.NodeTopLevel nodeTopLevel = (HlslTree.NodeTopLevel)tree.allNodes[tree.topLevelNode]; debugLog = ""; try { for (int i = 0; i < nodeTopLevel.statements.Length; i++) { GenerateLinesRecurse(nodeTopLevel.statements[i]); HlslTokenizer.CodeSection codeSection = nodeTopLevel.codeSections[i]; switch (codeSection) { case HlslTokenizer.CodeSection.Unknown: sectionUnknown.AddRange(allLines); break; case HlslTokenizer.CodeSection.GraphFunction: sectionGraphFunction.AddRange(allLines); break; case HlslTokenizer.CodeSection.GraphPixel: sectionGraphPixel.AddRange(allLines); break; default: HlslUtil.ParserAssert(false); break; } allLines = new List(); } } catch (Exception e) { string warnText = e.Message + " (derivative generation: " + shaderName + ")"; Debug.LogWarning(warnText); debugLog += warnText + "\n"; debugLog += e.StackTrace; // also, add the generated node stack if we have one debugLog += "\n"; debugLog += "NodeStack: " + debugNodeStack.Count.ToString() + "\n"; for (int i = 0; i < debugNodeStack.Count; i++) { int nodeId = debugNodeStack[i]; debugLog += " " + nodeId.ToString() + "\n"; } isValid = false; } ShaderStringBuilder builder = new ShaderStringBuilder(humanReadable: true); apdWriter.GenerateDefinitionsAndFuncs(builder); dstUnknown = FlattenStringList(sectionUnknown); dstApdFuncs = builder.ToCodeBlock(); dstGraphFunction = FlattenStringList(sectionGraphFunction); dstGraphPixel = FlattenStringList(sectionGraphPixel); List dstLines = new List(); dstLines.Add("// unknown section"); dstLines.Add(dstUnknown); dstLines.Add("// apd funcs"); dstLines.Add(dstApdFuncs); dstLines.Add("// graph function section"); dstLines.Add(dstGraphFunction); dstLines.Add("// graph pixel section"); dstLines.Add(dstGraphPixel); return dstLines.ToArray(); } HlslApdInfo apdInfo; int indentLevel; internal HlslTokenizer tokenizer; internal HlslUnityReserved unityReserved; internal HlslTree tree; internal Dictionary tokenToNativeTable; internal bool isValid; internal List errList; int[] nodeParents; int[][] nodeChildren; int sdInputsStructId; bool applyEmulatedDerivatives; PartialDerivUtilWriter apdWriter; internal List uvDerivatives; internal string[] debugNodeLines; internal string dstUnknown; internal string dstApdFuncs; internal string dstGraphFunction; internal string dstGraphPixel; internal List nodeStack; internal string debugLog; List allLines; string currLine; } }