PadToAttributeMutator.cpp 24 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540
  1. /*
  2. * Copyright (c) Contributors to the Open 3D Engine Project.
  3. * For complete copyright and license terms please see the LICENSE at the root of this distribution.
  4. *
  5. * SPDX-License-Identifier: Apache-2.0 OR MIT
  6. *
  7. */
  8. #include "PadToAttributeMutator.h"
  9. #include "AzslcIntermediateRepresentation.h"
  10. namespace AZ::ShaderCompiler
  11. {
  12. void PadToAttributeMutator::ProcessPadToAttribute(const AttributeInfo& attrInfo)
  13. {
  14. //The pad_to(N) attribute only accepts one input argument.
  15. if (attrInfo.m_argList.size() != 1)
  16. {
  17. auto errorMsg = FormatString("The [[pad_to(N)]] attribute only accepts one argument of integral type. %zu arguments were given.", attrInfo.m_argList.size());
  18. m_ir.ThrowAzslcIrException(IR_INVALID_PAD_TO_ARGUMENTS, attrInfo.m_lineNumber, errorMsg);
  19. }
  20. //Is the argument integral?
  21. if (!holds_alternative<ConstNumericVal>(attrInfo.m_argList[0]))
  22. {
  23. string errorMsg("The [[pad_to(N)]] attribute only accepts one argument of integral type. A non integral argument was given.");
  24. m_ir.ThrowAzslcIrException(IR_INVALID_PAD_TO_ARGUMENTS, attrInfo.m_lineNumber, errorMsg);
  25. }
  26. // Read the integral.
  27. auto pad_to_value = ExtractValueAs<uint32_t>(get<ConstNumericVal>(attrInfo.m_argList[0]), uint32_t(0));
  28. if (!pad_to_value)
  29. {
  30. string errorMsg("Failed to read input integral to [[pad_to(N)]].");
  31. m_ir.ThrowAzslcIrException(IR_INVALID_PAD_TO_ARGUMENTS, attrInfo.m_lineNumber, errorMsg);
  32. }
  33. // Must be a multiple of 4.
  34. static const uint32_t MultipleOf = 4;
  35. if (pad_to_value & (MultipleOf-1))
  36. {
  37. auto errorMsg = FormatString("Invalid integral in [[pad_to(N)]]. %u is not a multiple of %u", pad_to_value, MultipleOf);
  38. m_ir.ThrowAzslcIrException(IR_INVALID_PAD_TO_ARGUMENTS, attrInfo.m_lineNumber, errorMsg);
  39. }
  40. auto& [curScopeId, curScopeKindInfo] = m_ir.GetCurrentScopeIdAndKind();
  41. if (curScopeKindInfo.IsKindOneOf(Kind::Struct, Kind::Class, Kind::ShaderResourceGroup))
  42. {
  43. // We need to get the variable declared before this attribute.
  44. auto varUid = m_ir.GetLastMemberVariable(curScopeId);
  45. if (varUid.IsEmpty())
  46. {
  47. auto errorMsg = FormatString("The [[pad_to(N)]] attribute must be added after a member variable."
  48. " The current scope '%.*s' doesn't have a declared variable yet.",
  49. static_cast<int>(curScopeId.GetName().size()), curScopeId.GetName().data());
  50. m_ir.ThrowAzslcIrException(IR_INVALID_PAD_TO_LOCATION, attrInfo.m_lineNumber, errorMsg);
  51. }
  52. auto structItor = m_scopesToPad.find(curScopeId);
  53. if (structItor == m_scopesToPad.end())
  54. {
  55. m_scopesToPad[curScopeId] = MapOfVarInfoUidToPadding();
  56. }
  57. MapOfVarInfoUidToPadding& varInfoUidToPadMap = m_scopesToPad[curScopeId];
  58. auto varInfoItor = varInfoUidToPadMap.find(varUid);
  59. if (varInfoItor != varInfoUidToPadMap.end())
  60. {
  61. // It appears that there are two consecutive [[pad_to(N)]] attributes. This is an error.
  62. auto errorMsg = string("Two consecutive [[pad_to(N)]] attributes are not allowed inside 'struct'.");
  63. m_ir.ThrowAzslcIrException(IR_INVALID_PAD_TO_LOCATION, attrInfo.m_lineNumber, errorMsg);
  64. }
  65. varInfoUidToPadMap[varUid] = pad_to_value;
  66. }
  67. else
  68. {
  69. auto errorMsg = FormatString("The [[pad_to(N)]] attribute is only supported inside inside 'struct', 'class' or 'ShaderResourceGroup'."
  70. " The current scope '%.*s' is not one of those scope types.",
  71. static_cast<int>(curScopeId.GetName().size()), curScopeId.GetName().data());
  72. m_ir.ThrowAzslcIrException(IR_INVALID_PAD_TO_LOCATION, attrInfo.m_lineNumber, errorMsg);
  73. }
  74. }
  75. void PadToAttributeMutator::RunMutationsForPadToAttributes(const MiddleEndConfiguration& middleEndconfigration)
  76. {
  77. if (m_scopesToPad.empty())
  78. {
  79. return;
  80. }
  81. // Build sorted list from the keys in @m_scopesToPad so that the first 'structs' don't reference other structs that require padding.
  82. // In other words, We must first pad the structs that don't reference other structs that need padding.
  83. const auto sortedStructUids = GetSortedScopeUidList(m_scopesToPad);
  84. for (const auto& scopeUid : sortedStructUids)
  85. {
  86. const auto& varInfoUidToPadMap = m_scopesToPad[scopeUid];
  87. ClassInfo* classInfo = nullptr;
  88. auto kind = m_ir.GetKind(scopeUid);
  89. if (kind.IsOneOf(Kind::Struct, Kind::Class))
  90. {
  91. classInfo = m_ir.GetSymbolSubAs<ClassInfo>(scopeUid.GetName());
  92. }
  93. else if (kind.IsOneOf(Kind::ShaderResourceGroup))
  94. {
  95. auto srgInfo = m_ir.GetSymbolSubAs<SRGInfo>(scopeUid.GetName());
  96. classInfo = &srgInfo->m_implicitStruct;
  97. }
  98. if (!classInfo)
  99. {
  100. auto errorMsg = FormatString("Error during struct padding: couldn't find ClassInfo for scope %.*s", scopeUid.GetName().size(), scopeUid.GetName().data());
  101. throw std::logic_error(errorMsg);
  102. }
  103. InsertScopePaddings(classInfo, scopeUid, varInfoUidToPadMap, middleEndconfigration);
  104. }
  105. }
  106. vector<IdentifierUID> PadToAttributeMutator::GetSortedScopeUidList(const MapOfScopeUidToPaddingMap& scopesToPad) const
  107. {
  108. vector<IdentifierUID> sortedList;
  109. unordered_set<IdentifierUID> visitedStructs;
  110. unordered_set<IdentifierUID> unvisitedStructs;
  111. for (const auto &item : scopesToPad)
  112. {
  113. unvisitedStructs.insert(item.first);
  114. }
  115. while (!unvisitedStructs.empty())
  116. {
  117. const IdentifierUID unvisitedStructUid = *unvisitedStructs.begin();
  118. ScopeUidSortVisitFunction(unvisitedStructUid, visitedStructs, sortedList);
  119. unvisitedStructs.erase(unvisitedStructUid);
  120. }
  121. return sortedList;
  122. }
  123. void PadToAttributeMutator::ScopeUidSortVisitFunction(const IdentifierUID& scopeUid, unordered_set<IdentifierUID>& visitedScopes, vector<IdentifierUID>& sortedList) const
  124. {
  125. if (visitedScopes.count(scopeUid))
  126. {
  127. return;
  128. }
  129. ClassInfo* classInfo = nullptr;
  130. auto kind = m_ir.GetKind(scopeUid);
  131. if (kind.IsOneOf(Kind::Struct, Kind::Class))
  132. {
  133. classInfo = m_ir.GetSymbolSubAs<ClassInfo>(scopeUid.GetName());
  134. }
  135. else if (kind.IsOneOf(Kind::ShaderResourceGroup))
  136. {
  137. auto srgInfo = m_ir.GetSymbolSubAs<SRGInfo>(scopeUid.GetName());
  138. classInfo = &srgInfo->m_implicitStruct;
  139. }
  140. const auto listOfPairs = GetVariablesOfScopeTypeThatRequirePadding(classInfo);
  141. for (const auto& [typeUid, varUid] : listOfPairs)
  142. {
  143. ScopeUidSortVisitFunction(typeUid, visitedScopes, sortedList);
  144. }
  145. visitedScopes.insert(scopeUid);
  146. sortedList.push_back(scopeUid);
  147. }
  148. vector<pair<IdentifierUID, IdentifierUID>> PadToAttributeMutator::GetVariablesOfScopeTypeThatRequirePadding(const ClassInfo* classInfo) const
  149. {
  150. vector<pair<IdentifierUID, IdentifierUID>> retList;
  151. const auto& memberFields = classInfo->GetMemberFields();
  152. for (const auto &memberUid : memberFields)
  153. {
  154. const auto* varInfoPtr = m_ir.GetSymbolSubAs<VarInfo>(memberUid.m_name);
  155. if (!varInfoPtr)
  156. {
  157. continue;
  158. }
  159. const auto& typeUid = varInfoPtr->GetTypeId();
  160. const auto kind = m_ir.GetKind(typeUid);
  161. if (!kind.IsOneOf(Kind::Struct, Kind::Class)) // No need to check for SRG types, as SRGs can not be variables.
  162. {
  163. continue;
  164. }
  165. if (m_scopesToPad.find(typeUid) == m_scopesToPad.end())
  166. {
  167. // It's of struct type, but doesn't require padding.
  168. continue;
  169. }
  170. retList.emplace_back(typeUid, memberUid);
  171. }
  172. return retList;
  173. }
  174. void PadToAttributeMutator::InsertScopePaddings(ClassInfo* classInfo,
  175. const IdentifierUID& scopeUid,
  176. const MapOfVarInfoUidToPadding& varInfoUidToPadMap,
  177. const MiddleEndConfiguration& middleEndconfigration)
  178. {
  179. uint32_t nextMemberOffset = 0;
  180. auto& memberFields = classInfo->GetMemberFields();
  181. for (size_t idx = 0; idx < memberFields.size(); idx++)
  182. {
  183. // Calculate current offset & size.
  184. const auto& varUid = memberFields[idx];
  185. CalculateMemberLayout(varUid, false, middleEndconfigration.m_isRowMajor, middleEndconfigration.m_packDataBuffers, nextMemberOffset);
  186. // Nothing else to do, if this variable doesn't need padding.
  187. const auto varItor = varInfoUidToPadMap.find(varUid);
  188. if (varItor == varInfoUidToPadMap.end())
  189. {
  190. continue;
  191. }
  192. const uint32_t padToBoundary = varItor->second;
  193. uint32_t bytesToAdd = 0;
  194. if (padToBoundary < nextMemberOffset)
  195. {
  196. // We will only AlignUp if padToBoundary is a power of two.
  197. if (!IsPowerOfTwo(padToBoundary))
  198. {
  199. //Runtime error.
  200. const string errorMsg = FormatString("Offset %u after Member variable %.*s of struct %.*s "
  201. "is bigger than requested boundary = [[pad_to(%u)]], and this case requires a power of two boundary.",
  202. nextMemberOffset,
  203. static_cast<int>(varUid.m_name.size()), varUid.m_name.data(),
  204. static_cast<int>(scopeUid.m_name.size()), scopeUid.m_name.data(),
  205. padToBoundary);
  206. const auto * varInfoPtr= m_ir.GetSymbolSubAs<VarInfo>(varUid.m_name);
  207. m_ir.ThrowAzslcIrException(IR_PAD_TO_CASE_REQUIRES_POWER_OF_TWO, varInfoPtr->GetOriginalLineNumber(), errorMsg);
  208. }
  209. const uint32_t alignedOffset = Packing::AlignUp(nextMemberOffset, padToBoundary);
  210. bytesToAdd = alignedOffset - nextMemberOffset;
  211. }
  212. else
  213. {
  214. bytesToAdd = padToBoundary - nextMemberOffset;
  215. }
  216. if (!bytesToAdd)
  217. {
  218. // Nothing to do.
  219. continue;
  220. }
  221. idx += InsertPaddingVariables(classInfo, scopeUid, idx+1, nextMemberOffset, bytesToAdd);
  222. nextMemberOffset += bytesToAdd;
  223. }
  224. }
  225. uint32_t PadToAttributeMutator::CalculateMemberLayout(const IdentifierUID& memberId,
  226. const bool isArrayItr,
  227. const bool emitRowMajor,
  228. const AZ::ShaderCompiler::Packing::Layout layoutPacking,
  229. uint32_t& offset) const
  230. {
  231. const auto* varInfoPtr = m_ir.GetSymbolSubAs<VarInfo>(memberId.m_name);
  232. uint32_t size = 0;
  233. if (varInfoPtr)
  234. {
  235. const auto& varInfo = *varInfoPtr;
  236. // View types should only be called from GetViewStride until we decide to support them as struct constants
  237. assert(!IsChameleon(varInfo.GetTypeClass()));
  238. auto exportedType = varInfo.m_typeInfoExt.m_coreType;
  239. if (!exportedType.IsPackable())
  240. {
  241. throw std::logic_error{"reflection error: unpackable type ("
  242. + exportedType.m_typeId.m_name
  243. + ") in layout member "
  244. + memberId.m_name};
  245. }
  246. TypeClass varClass = exportedType.m_typeClass;
  247. bool isPrefedined = IsPredefinedType(varClass);
  248. size = varInfo.m_typeInfoExt.GetTotalSize(layoutPacking, emitRowMajor);
  249. auto startAt = offset;
  250. // Alignment start
  251. if (exportedType.m_arithmeticInfo.IsMatrix() || exportedType.m_arithmeticInfo.IsVector())
  252. {
  253. const auto rows = exportedType.m_arithmeticInfo.m_rows;
  254. const auto cols = exportedType.m_arithmeticInfo.m_cols;
  255. const auto packAlignment = exportedType.m_arithmeticInfo.IsMatrix() ? Packing::Alignment::asMatrixStart : Packing::Alignment::asVectorStart;
  256. startAt = offset = Packing::AlignOffset(layoutPacking, offset, packAlignment, rows, cols);
  257. }
  258. uint32_t totalArraySize = 1;
  259. ArrayDimensions listOfArrayDim = varInfo.m_typeInfoExt.GetDimensions();
  260. std::reverse(listOfArrayDim.m_dimensions.begin(), listOfArrayDim.m_dimensions.end());
  261. for (const auto dim : varInfo.m_typeInfoExt.GetDimensions())
  262. {
  263. totalArraySize *= dim;
  264. }
  265. if (varInfo.m_typeInfoExt.IsArray() && !isArrayItr)
  266. {
  267. startAt = offset = Packing::AlignOffset(layoutPacking, offset, Packing::Alignment::asArrayStart, 0, 0);
  268. uint32_t arrayOffset = startAt;
  269. for (uint32_t i = 0; i < totalArraySize; i++)
  270. {
  271. if (!m_ir.GetIdAndKindInfo(varInfo.GetTypeId().m_name))
  272. {
  273. continue;
  274. }
  275. // If array is a structure
  276. if (IsProductType(varClass))
  277. {
  278. startAt = offset;
  279. size = CalculateUserDefinedMemberLayout(exportedType.m_typeId, emitRowMajor, layoutPacking, startAt);
  280. offset = Packing::PackNextChunk(layoutPacking, size, startAt);
  281. // Add packing into array
  282. size = Packing::PackIntoArray(layoutPacking, size, varInfo.m_typeInfoExt.GetDimensions());
  283. }
  284. else
  285. {
  286. // Alignment start
  287. startAt = offset = Packing::AlignOffset(layoutPacking, offset, Packing::Alignment::asArrayStart, 0, 0);
  288. // We want to calculate the offset for each array element
  289. uint32_t tempOffset = startAt;
  290. CalculateMemberLayout(memberId, true, emitRowMajor, layoutPacking, tempOffset);
  291. // Alignment end
  292. tempOffset = Packing::AlignOffset(layoutPacking, tempOffset, Packing::Alignment::asArrayEnd, 0, 0);
  293. size = tempOffset - startAt;
  294. offset = Packing::PackNextChunk(layoutPacking, size, startAt);
  295. // Add packing into array
  296. size = Packing::PackIntoArray(layoutPacking, size, varInfo.m_typeInfoExt.GetDimensions());
  297. }
  298. }
  299. startAt = arrayOffset;
  300. }
  301. else if (IsProductType(varClass))
  302. {
  303. size = CalculateUserDefinedMemberLayout(exportedType.m_typeId, emitRowMajor, layoutPacking, startAt);
  304. // Add packing into array
  305. size = Packing::PackIntoArray(layoutPacking, size, varInfo.m_typeInfoExt.GetDimensions());
  306. }
  307. else if (varInfo.m_typeInfoExt.IsArray())
  308. {
  309. // Get the size of one element from total size
  310. size = varInfo.m_typeInfoExt.GetSingleElementSize(layoutPacking, emitRowMajor);
  311. }
  312. else if (varInfo.GetTypeClass() == TypeClass::Enum)
  313. {
  314. auto* asClassInfo = m_ir.GetSymbolSubAs<ClassInfo>(varInfo.GetTypeId().GetName());
  315. size = asClassInfo->Get<EnumerationInfo>()->m_underlyingType.m_arithmeticInfo.GetBaseSize();
  316. }
  317. offset = Packing::PackNextChunk(layoutPacking, size, startAt);
  318. // Alignment end
  319. if (exportedType.m_arithmeticInfo.IsMatrix() || exportedType.m_arithmeticInfo.IsVector())
  320. {
  321. const auto rows = exportedType.m_arithmeticInfo.m_rows;
  322. const auto cols = exportedType.m_arithmeticInfo.m_cols;
  323. const auto packAlignment = exportedType.m_arithmeticInfo.IsMatrix() ? Packing::Alignment::asMatrixEnd : Packing::Alignment::asVectorEnd;
  324. offset = Packing::AlignOffset(layoutPacking, offset, packAlignment, rows, cols);
  325. }
  326. if (varInfo.m_typeInfoExt.IsArray())
  327. {
  328. offset = Packing::AlignOffset(layoutPacking, offset, Packing::Alignment::asArrayEnd, 0, 0);
  329. }
  330. size = offset - startAt;
  331. }
  332. return size;
  333. }
  334. uint32_t PadToAttributeMutator::CalculateUserDefinedMemberLayout(
  335. const IdentifierUID& exportedTypeId,
  336. const bool emitRowMajors,
  337. const AZ::ShaderCompiler::Packing::Layout layoutPacking,
  338. uint32_t& startAt) const
  339. {
  340. // Alignment start
  341. uint32_t tempOffset = startAt = Packing::AlignOffset(layoutPacking, startAt, Packing::Alignment::asStructStart, 0, 0);
  342. uint32_t largestMemberSize = 0;
  343. const auto* classInfo = m_ir.GetSymbolSubAs<ClassInfo>(exportedTypeId.m_name);
  344. for (const auto& memberField : classInfo->GetMemberFields())
  345. {
  346. const auto currentStride = tempOffset;
  347. CalculateMemberLayout(memberField, false, emitRowMajors, GetExtendedLayout(layoutPacking), tempOffset);
  348. largestMemberSize = std::max(largestMemberSize, tempOffset - currentStride);
  349. }
  350. tempOffset = Packing::AlignStructToLargestMember(layoutPacking, tempOffset, largestMemberSize);
  351. // Alignment end
  352. tempOffset = Packing::AlignOffset(layoutPacking, tempOffset, Packing::Alignment::asStructEnd, 0, 0);
  353. // Total size equals the end offset less the starting address
  354. return tempOffset - startAt;
  355. }
  356. size_t PadToAttributeMutator::InsertPaddingVariables(ClassInfo* classInfo, const IdentifierUID& scopeUid,
  357. size_t insertionIndex, uint32_t startingOffset, uint32_t numBytesToAdd)
  358. {
  359. auto getFloatTypeNameOfSize = +[](uint32_t sizeInBytes) -> const char *
  360. {
  361. static const char * floatNames[4] = {
  362. "float", "float2", "float3", "float4"
  363. };
  364. const uint32_t idx = (sizeInBytes >> 2) - 1;
  365. return floatNames[idx];
  366. };
  367. auto createVariableInSymbolTable = [&](QualifiedNameView parentName, const string& typeName, UnqualifiedName varName, uint32_t itemsCount = 0) -> IdentifierUID
  368. {
  369. QualifiedName dummySymbolFieldName{ JoinPath(parentName, varName) };
  370. // Add the dummy field to the symbol table.
  371. auto& [newVarUid, newVarKind] = m_ir.m_symbols.AddIdentifier(dummySymbolFieldName, Kind::Variable);
  372. // Fill up the data.
  373. VarInfo newVarInfo;
  374. newVarInfo.m_declNode = nullptr;
  375. newVarInfo.m_isPublic = false;
  376. ExtractedTypeExt padType = { UnqualifiedNameView(typeName), nullptr };
  377. if (itemsCount < 1)
  378. {
  379. newVarInfo.m_typeInfoExt = ExtendedTypeInfo{ m_ir.m_sema.CreateTypeRefInfo(padType),
  380. {}, {}, {}, Packing::MatrixMajor::Default };
  381. }
  382. else
  383. {
  384. newVarInfo.m_typeInfoExt = ExtendedTypeInfo{ m_ir.m_sema.CreateTypeRefInfo(padType),
  385. {}, {{itemsCount}}, {}, Packing::MatrixMajor::Default };
  386. }
  387. newVarKind.GetSubRefAs<VarInfo>() = newVarInfo;
  388. return newVarUid;
  389. };
  390. // The key idea is to add, at most, three variables. They will be added depending on keeping a 16-byte alignment from @startingOffset
  391. // 1- The first variable will be added if @startingOffset is not 16-bytes aligned. It will be a float, float2 or float3.
  392. // 2- If more bytes are still needed, then We'll add ONE float4[N] array, Until (N * 16) bytes fit within the bytes that are left to add.
  393. // 3- Finally, if there are more remaining bytes to the be added, a third float, float2 or float3 will be added.
  394. auto& memberFields = classInfo->GetMemberFields();
  395. IdentifierUID insertBeforeThisUid;
  396. if (insertionIndex <= memberFields.size() - 1)
  397. {
  398. insertBeforeThisUid = memberFields[insertionIndex];
  399. }
  400. size_t numAddedVariables = 0;
  401. // 1st variable.
  402. // This is why the 1st variable is needed:
  403. // For non-ConstantBuffer packing the float4 is not automatically aligned to 16 bytes.
  404. // Example:
  405. // struct MyStructA
  406. // {
  407. // float m_data;
  408. // float4 m_arr[2];
  409. // };
  410. // For ConstantBuffer case you'll get these offsets:
  411. // float m_data; ; Offset: 0
  412. // float4 m_arr[2]; ; Offset: 16
  413. // For StructuredBuffer case you'll get:
  414. // float m_data; ; Offset: 0
  415. // float4 m_arr[2]; ; Offset: 4
  416. {
  417. const auto alignedOffset = Packing::AlignUp(startingOffset, 16);
  418. const auto deltaBytes = alignedOffset - startingOffset;
  419. if (deltaBytes < numBytesToAdd)
  420. {
  421. string typeName = getFloatTypeNameOfSize(deltaBytes);
  422. auto variableName = FormatString("__pad_at%u", startingOffset);
  423. IdentifierUID newVarUid = createVariableInSymbolTable(scopeUid.GetName(), typeName, UnqualifiedName{variableName});
  424. if (insertBeforeThisUid.IsEmpty())
  425. {
  426. classInfo->PushMember(newVarUid, Kind::Variable);
  427. }
  428. else
  429. {
  430. classInfo->InsertBefore(newVarUid, Kind::Variable, insertBeforeThisUid);
  431. }
  432. numAddedVariables++;
  433. numBytesToAdd -= deltaBytes;
  434. startingOffset = alignedOffset;
  435. }
  436. }
  437. // 2nd variable. The Array of 'float4'
  438. {
  439. const auto numFloat4s = numBytesToAdd >> 4;
  440. if (numFloat4s)
  441. {
  442. auto variableName = FormatString("__pad_at%u", startingOffset);
  443. IdentifierUID newVarUid = createVariableInSymbolTable(scopeUid.GetName(), "float4", UnqualifiedName{variableName}, numFloat4s);
  444. if (insertBeforeThisUid.IsEmpty())
  445. {
  446. classInfo->PushMember(newVarUid, Kind::Variable);
  447. }
  448. else
  449. {
  450. classInfo->InsertBefore(newVarUid, Kind::Variable, insertBeforeThisUid);
  451. }
  452. numAddedVariables++;
  453. numBytesToAdd -= (numFloat4s << 4);
  454. startingOffset += (numFloat4s << 4);
  455. }
  456. }
  457. // 3rd variable. The remainder
  458. if (numBytesToAdd > 0)
  459. {
  460. auto variableName = FormatString("__pad_at%u", startingOffset);
  461. string typeName = getFloatTypeNameOfSize(numBytesToAdd);
  462. IdentifierUID newVarUid = createVariableInSymbolTable(scopeUid.GetName(), typeName, UnqualifiedName{variableName});
  463. if (insertBeforeThisUid.IsEmpty())
  464. {
  465. classInfo->PushMember(newVarUid, Kind::Variable);
  466. }
  467. else
  468. {
  469. classInfo->InsertBefore(newVarUid, Kind::Variable, insertBeforeThisUid);
  470. }
  471. numAddedVariables++;
  472. }
  473. return numAddedVariables;
  474. }
  475. } //namespace AZ::ShaderCompiler