1//
2// Copyright (c) 2013-2014 The ANGLE Project Authors. All rights reserved.
3// Use of this source code is governed by a BSD-style license that can be
4// found in the LICENSE file.
5//
6// blocklayout.cpp:
7// Implementation for block layout classes and methods.
8//
9
10#include "compiler/translator/blocklayout.h"
11
12#include "common/mathutil.h"
13#include "common/utilities.h"
14#include "compiler/translator/Common.h"
15
16namespace sh
17{
18
19namespace
20{
21class BlockLayoutMapVisitor : public BlockEncoderVisitor
22{
23 public:
24 BlockLayoutMapVisitor(BlockLayoutMap *blockInfoOut,
25 const std::string &instanceName,
26 BlockLayoutEncoder *encoder)
27 : BlockEncoderVisitor(instanceName, instanceName, encoder), mInfoOut(blockInfoOut)
28 {}
29
30 void encodeVariable(const ShaderVariable &variable,
31 const BlockMemberInfo &variableInfo,
32 const std::string &name,
33 const std::string &mappedName) override
34 {
35 ASSERT(!gl::IsSamplerType(variable.type));
36 (*mInfoOut)[name] = variableInfo;
37 }
38
39 private:
40 BlockLayoutMap *mInfoOut;
41};
42
43template <typename VarT>
44void GetInterfaceBlockInfo(const std::vector<VarT> &fields,
45 const std::string &prefix,
46 BlockLayoutEncoder *encoder,
47 bool inRowMajorLayout,
48 BlockLayoutMap *blockInfoOut)
49{
50 BlockLayoutMapVisitor visitor(blockInfoOut, prefix, encoder);
51 TraverseShaderVariables(fields, inRowMajorLayout, &visitor);
52}
53
54void TraverseStructVariable(const ShaderVariable &variable,
55 bool isRowMajorLayout,
56 ShaderVariableVisitor *visitor)
57{
58 const std::vector<ShaderVariable> &fields = variable.fields;
59
60 visitor->enterStructAccess(variable, isRowMajorLayout);
61 TraverseShaderVariables(fields, isRowMajorLayout, visitor);
62 visitor->exitStructAccess(variable, isRowMajorLayout);
63}
64
65void TraverseStructArrayVariable(const ShaderVariable &variable,
66 bool inRowMajorLayout,
67 ShaderVariableVisitor *visitor)
68{
69 visitor->enterArray(variable);
70
71 // Nested arrays are processed starting from outermost (arrayNestingIndex 0u) and ending at the
72 // innermost. We make a special case for unsized arrays.
73 const unsigned int currentArraySize = variable.getNestedArraySize(0);
74 unsigned int count = std::max(currentArraySize, 1u);
75 for (unsigned int arrayElement = 0u; arrayElement < count; ++arrayElement)
76 {
77 visitor->enterArrayElement(variable, arrayElement);
78 ShaderVariable elementVar = variable;
79 elementVar.indexIntoArray(arrayElement);
80
81 if (variable.arraySizes.size() > 1u)
82 {
83 TraverseStructArrayVariable(elementVar, inRowMajorLayout, visitor);
84 }
85 else
86 {
87 TraverseStructVariable(elementVar, inRowMajorLayout, visitor);
88 }
89
90 visitor->exitArrayElement(variable, arrayElement);
91 }
92
93 visitor->exitArray(variable);
94}
95
96void TraverseArrayOfArraysVariable(const ShaderVariable &variable,
97 unsigned int arrayNestingIndex,
98 bool isRowMajorMatrix,
99 ShaderVariableVisitor *visitor)
100{
101 visitor->enterArray(variable);
102
103 const unsigned int currentArraySize = variable.getNestedArraySize(arrayNestingIndex);
104 unsigned int count = std::max(currentArraySize, 1u);
105 for (unsigned int arrayElement = 0u; arrayElement < count; ++arrayElement)
106 {
107 visitor->enterArrayElement(variable, arrayElement);
108
109 ShaderVariable elementVar = variable;
110 elementVar.indexIntoArray(arrayElement);
111
112 if (arrayNestingIndex + 2u < variable.arraySizes.size())
113 {
114 TraverseArrayOfArraysVariable(elementVar, arrayNestingIndex, isRowMajorMatrix, visitor);
115 }
116 else
117 {
118 if (gl::IsSamplerType(variable.type))
119 {
120 visitor->visitSampler(elementVar);
121 }
122 else
123 {
124 visitor->visitVariable(elementVar, isRowMajorMatrix);
125 }
126 }
127
128 visitor->exitArrayElement(variable, arrayElement);
129 }
130
131 visitor->exitArray(variable);
132}
133
134std::string CollapseNameStack(const std::vector<std::string> &nameStack)
135{
136 std::stringstream strstr = sh::InitializeStream<std::stringstream>();
137 for (const std::string &part : nameStack)
138 {
139 strstr << part;
140 }
141 return strstr.str();
142}
143
144size_t GetStd430BaseAlignment(GLenum variableType, bool isRowMajor)
145{
146 GLenum flippedType = isRowMajor ? variableType : gl::TransposeMatrixType(variableType);
147 size_t numComponents = static_cast<size_t>(gl::VariableColumnCount(flippedType));
148 return ComponentAlignment(numComponents);
149}
150
151class BaseAlignmentVisitor : public ShaderVariableVisitor
152{
153 public:
154 BaseAlignmentVisitor() = default;
155 void visitVariable(const ShaderVariable &variable, bool isRowMajor) override
156 {
157 size_t baseAlignment = GetStd430BaseAlignment(variable.type, isRowMajor);
158 mCurrentAlignment = std::max(mCurrentAlignment, baseAlignment);
159 }
160
161 // This is in components rather than bytes.
162 size_t getBaseAlignment() const { return mCurrentAlignment; }
163
164 private:
165 size_t mCurrentAlignment = 0;
166};
167} // anonymous namespace
168
169// BlockLayoutEncoder implementation.
170BlockLayoutEncoder::BlockLayoutEncoder() : mCurrentOffset(0) {}
171
172BlockMemberInfo BlockLayoutEncoder::encodeType(GLenum type,
173 const std::vector<unsigned int> &arraySizes,
174 bool isRowMajorMatrix)
175{
176 int arrayStride;
177 int matrixStride;
178
179 getBlockLayoutInfo(type, arraySizes, isRowMajorMatrix, &arrayStride, &matrixStride);
180
181 const BlockMemberInfo memberInfo(static_cast<int>(mCurrentOffset * kBytesPerComponent),
182 static_cast<int>(arrayStride * kBytesPerComponent),
183 static_cast<int>(matrixStride * kBytesPerComponent),
184 isRowMajorMatrix);
185
186 advanceOffset(type, arraySizes, isRowMajorMatrix, arrayStride, matrixStride);
187
188 return memberInfo;
189}
190
191size_t BlockLayoutEncoder::getShaderVariableSize(const ShaderVariable &structVar, bool isRowMajor)
192{
193 size_t currentOffset = mCurrentOffset;
194 mCurrentOffset = 0;
195 BlockEncoderVisitor visitor("", "", this);
196 enterAggregateType(structVar);
197 TraverseShaderVariables(structVar.fields, isRowMajor, &visitor);
198 exitAggregateType(structVar);
199 size_t structVarSize = getCurrentOffset();
200 mCurrentOffset = currentOffset;
201 return structVarSize;
202}
203
204// static
205size_t BlockLayoutEncoder::GetBlockRegister(const BlockMemberInfo &info)
206{
207 return (info.offset / kBytesPerComponent) / kComponentsPerRegister;
208}
209
210// static
211size_t BlockLayoutEncoder::GetBlockRegisterElement(const BlockMemberInfo &info)
212{
213 return (info.offset / kBytesPerComponent) % kComponentsPerRegister;
214}
215
216void BlockLayoutEncoder::align(size_t baseAlignment)
217{
218 mCurrentOffset = rx::roundUp<size_t>(mCurrentOffset, baseAlignment);
219}
220
221// DummyBlockEncoder implementation.
222void DummyBlockEncoder::getBlockLayoutInfo(GLenum type,
223 const std::vector<unsigned int> &arraySizes,
224 bool isRowMajorMatrix,
225 int *arrayStrideOut,
226 int *matrixStrideOut)
227{
228 *arrayStrideOut = 0;
229 *matrixStrideOut = 0;
230}
231
232// Std140BlockEncoder implementation.
233Std140BlockEncoder::Std140BlockEncoder() {}
234
235void Std140BlockEncoder::enterAggregateType(const ShaderVariable &structVar)
236{
237 align(getBaseAlignment(structVar));
238}
239
240void Std140BlockEncoder::exitAggregateType(const ShaderVariable &structVar)
241{
242 align(getBaseAlignment(structVar));
243}
244
245void Std140BlockEncoder::getBlockLayoutInfo(GLenum type,
246 const std::vector<unsigned int> &arraySizes,
247 bool isRowMajorMatrix,
248 int *arrayStrideOut,
249 int *matrixStrideOut)
250{
251 // We assume we are only dealing with 4 byte components (no doubles or half-words currently)
252 ASSERT(gl::VariableComponentSize(gl::VariableComponentType(type)) == kBytesPerComponent);
253
254 size_t baseAlignment = 0;
255 int matrixStride = 0;
256 int arrayStride = 0;
257
258 if (gl::IsMatrixType(type))
259 {
260 baseAlignment = getTypeBaseAlignment(type, isRowMajorMatrix);
261 matrixStride = static_cast<int>(getTypeBaseAlignment(type, isRowMajorMatrix));
262
263 if (!arraySizes.empty())
264 {
265 const int numRegisters = gl::MatrixRegisterCount(type, isRowMajorMatrix);
266 arrayStride = static_cast<int>(getTypeBaseAlignment(type, isRowMajorMatrix) * numRegisters);
267 }
268 }
269 else if (!arraySizes.empty())
270 {
271 baseAlignment = static_cast<int>(getTypeBaseAlignment(type, false));
272 arrayStride = static_cast<int>(getTypeBaseAlignment(type, false));
273 }
274 else
275 {
276 const size_t numComponents = static_cast<size_t>(gl::VariableComponentCount(type));
277 baseAlignment = ComponentAlignment(numComponents);
278 }
279
280 mCurrentOffset = rx::roundUp(mCurrentOffset, baseAlignment);
281
282 *matrixStrideOut = matrixStride;
283 *arrayStrideOut = arrayStride;
284}
285
286void Std140BlockEncoder::advanceOffset(GLenum type,
287 const std::vector<unsigned int> &arraySizes,
288 bool isRowMajorMatrix,
289 int arrayStride,
290 int matrixStride)
291{
292 if (!arraySizes.empty())
293 {
294 mCurrentOffset += arrayStride * gl::ArraySizeProduct(arraySizes);
295 }
296 else if (gl::IsMatrixType(type))
297 {
298 const int numRegisters = gl::MatrixRegisterCount(type, isRowMajorMatrix);
299 mCurrentOffset += matrixStride * numRegisters;
300 }
301 else
302 {
303 mCurrentOffset += gl::VariableComponentCount(type);
304 }
305}
306
307size_t Std140BlockEncoder::getBaseAlignment(const ShaderVariable &variable) const
308{
309 return kComponentsPerRegister;
310}
311
312size_t Std140BlockEncoder::getTypeBaseAlignment(GLenum type, bool isRowMajorMatrix) const
313{
314 return kComponentsPerRegister;
315}
316
317// Std430BlockEncoder implementation.
318Std430BlockEncoder::Std430BlockEncoder() {}
319
320size_t Std430BlockEncoder::getBaseAlignment(const ShaderVariable &shaderVar) const
321{
322 if (shaderVar.isStruct())
323 {
324 BaseAlignmentVisitor visitor;
325 TraverseShaderVariables(shaderVar.fields, false, &visitor);
326 return visitor.getBaseAlignment();
327 }
328
329 return GetStd430BaseAlignment(shaderVar.type, shaderVar.isRowMajorLayout);
330}
331
332size_t Std430BlockEncoder::getTypeBaseAlignment(GLenum type, bool isRowMajorMatrix) const
333{
334 return GetStd430BaseAlignment(type, isRowMajorMatrix);
335}
336
337void GetInterfaceBlockInfo(const std::vector<InterfaceBlockField> &fields,
338 const std::string &prefix,
339 BlockLayoutEncoder *encoder,
340 BlockLayoutMap *blockInfoOut)
341{
342 // Matrix packing is always recorded in individual fields, so they'll set the row major layout
343 // flag to true if needed.
344 GetInterfaceBlockInfo(fields, prefix, encoder, false, blockInfoOut);
345}
346
347void GetUniformBlockInfo(const std::vector<Uniform> &uniforms,
348 const std::string &prefix,
349 BlockLayoutEncoder *encoder,
350 BlockLayoutMap *blockInfoOut)
351{
352 // Matrix packing is always recorded in individual fields, so they'll set the row major layout
353 // flag to true if needed.
354 GetInterfaceBlockInfo(uniforms, prefix, encoder, false, blockInfoOut);
355}
356
357// VariableNameVisitor implementation.
358VariableNameVisitor::VariableNameVisitor(const std::string &namePrefix,
359 const std::string &mappedNamePrefix)
360{
361 if (!namePrefix.empty())
362 {
363 mNameStack.push_back(namePrefix + ".");
364 }
365
366 if (!mappedNamePrefix.empty())
367 {
368 mMappedNameStack.push_back(mappedNamePrefix + ".");
369 }
370}
371
372VariableNameVisitor::~VariableNameVisitor() = default;
373
374void VariableNameVisitor::enterStruct(const ShaderVariable &structVar)
375{
376 mNameStack.push_back(structVar.name);
377 mMappedNameStack.push_back(structVar.mappedName);
378}
379
380void VariableNameVisitor::exitStruct(const ShaderVariable &structVar)
381{
382 mNameStack.pop_back();
383 mMappedNameStack.pop_back();
384}
385
386void VariableNameVisitor::enterStructAccess(const ShaderVariable &structVar, bool isRowMajor)
387{
388 mNameStack.push_back(".");
389 mMappedNameStack.push_back(".");
390}
391
392void VariableNameVisitor::exitStructAccess(const ShaderVariable &structVar, bool isRowMajor)
393{
394 mNameStack.pop_back();
395 mMappedNameStack.pop_back();
396}
397
398void VariableNameVisitor::enterArray(const ShaderVariable &arrayVar)
399{
400 if (!arrayVar.hasParentArrayIndex() && !arrayVar.isStruct())
401 {
402 mNameStack.push_back(arrayVar.name);
403 mMappedNameStack.push_back(arrayVar.mappedName);
404 }
405}
406
407void VariableNameVisitor::exitArray(const ShaderVariable &arrayVar)
408{
409 if (!arrayVar.hasParentArrayIndex() && !arrayVar.isStruct())
410 {
411 mNameStack.pop_back();
412 mMappedNameStack.pop_back();
413 }
414}
415
416void VariableNameVisitor::enterArrayElement(const ShaderVariable &arrayVar,
417 unsigned int arrayElement)
418{
419 std::stringstream strstr = sh::InitializeStream<std::stringstream>();
420 strstr << "[" << arrayElement << "]";
421 std::string elementString = strstr.str();
422 mNameStack.push_back(elementString);
423 mMappedNameStack.push_back(elementString);
424}
425
426void VariableNameVisitor::exitArrayElement(const ShaderVariable &arrayVar,
427 unsigned int arrayElement)
428{
429 mNameStack.pop_back();
430 mMappedNameStack.pop_back();
431}
432
433std::string VariableNameVisitor::collapseNameStack() const
434{
435 return CollapseNameStack(mNameStack);
436}
437
438std::string VariableNameVisitor::collapseMappedNameStack() const
439{
440 return CollapseNameStack(mMappedNameStack);
441}
442
443void VariableNameVisitor::visitSampler(const sh::ShaderVariable &sampler)
444{
445 if (!sampler.hasParentArrayIndex())
446 {
447 mNameStack.push_back(sampler.name);
448 mMappedNameStack.push_back(sampler.mappedName);
449 }
450
451 std::string name = collapseNameStack();
452 std::string mappedName = collapseMappedNameStack();
453
454 if (!sampler.hasParentArrayIndex())
455 {
456 mNameStack.pop_back();
457 mMappedNameStack.pop_back();
458 }
459
460 visitNamedSampler(sampler, name, mappedName);
461}
462
463void VariableNameVisitor::visitVariable(const ShaderVariable &variable, bool isRowMajor)
464{
465 if (!variable.hasParentArrayIndex())
466 {
467 mNameStack.push_back(variable.name);
468 mMappedNameStack.push_back(variable.mappedName);
469 }
470
471 std::string name = collapseNameStack();
472 std::string mappedName = collapseMappedNameStack();
473
474 if (!variable.hasParentArrayIndex())
475 {
476 mNameStack.pop_back();
477 mMappedNameStack.pop_back();
478 }
479
480 visitNamedVariable(variable, isRowMajor, name, mappedName);
481}
482
483// BlockEncoderVisitor implementation.
484BlockEncoderVisitor::BlockEncoderVisitor(const std::string &namePrefix,
485 const std::string &mappedNamePrefix,
486 BlockLayoutEncoder *encoder)
487 : VariableNameVisitor(namePrefix, mappedNamePrefix), mEncoder(encoder)
488{}
489
490BlockEncoderVisitor::~BlockEncoderVisitor() = default;
491
492void BlockEncoderVisitor::enterStructAccess(const ShaderVariable &structVar, bool isRowMajor)
493{
494 mStructStackSize++;
495 if (!mIsTopLevelArrayStrideReady)
496 {
497 size_t structSize = mEncoder->getShaderVariableSize(structVar, isRowMajor);
498 mTopLevelArrayStride *= structSize;
499 mIsTopLevelArrayStrideReady = true;
500 }
501
502 VariableNameVisitor::enterStructAccess(structVar, isRowMajor);
503 mEncoder->enterAggregateType(structVar);
504}
505
506void BlockEncoderVisitor::exitStructAccess(const ShaderVariable &structVar, bool isRowMajor)
507{
508 mStructStackSize--;
509 mEncoder->exitAggregateType(structVar);
510 VariableNameVisitor::exitStructAccess(structVar, isRowMajor);
511}
512
513void BlockEncoderVisitor::enterArrayElement(const sh::ShaderVariable &arrayVar,
514 unsigned int arrayElement)
515{
516 if (mStructStackSize == 0 && !arrayVar.hasParentArrayIndex())
517 {
518 // From the ES 3.1 spec "7.3.1.1 Naming Active Resources":
519 // For an active shader storage block member declared as an array of an aggregate type,
520 // an entry will be generated only for the first array element, regardless of its type.
521 // Such block members are referred to as top-level arrays. If the block member is an
522 // aggregate type, the enumeration rules are then applied recursively.
523 if (arrayElement == 0)
524 {
525 mTopLevelArraySize = arrayVar.getOutermostArraySize();
526 mTopLevelArrayStride = arrayVar.getInnerArraySizeProduct();
527 mIsTopLevelArrayStrideReady = false;
528 }
529 else
530 {
531 mSkipEnabled = true;
532 }
533 }
534 VariableNameVisitor::enterArrayElement(arrayVar, arrayElement);
535}
536
537void BlockEncoderVisitor::exitArrayElement(const sh::ShaderVariable &arrayVar,
538 unsigned int arrayElement)
539{
540 if (mStructStackSize == 0 && !arrayVar.hasParentArrayIndex())
541 {
542 mTopLevelArraySize = 1;
543 mTopLevelArrayStride = 0;
544 mIsTopLevelArrayStrideReady = true;
545 mSkipEnabled = false;
546 }
547 VariableNameVisitor::exitArrayElement(arrayVar, arrayElement);
548}
549
550void BlockEncoderVisitor::visitNamedVariable(const ShaderVariable &variable,
551 bool isRowMajor,
552 const std::string &name,
553 const std::string &mappedName)
554{
555 std::vector<unsigned int> innermostArraySize;
556
557 if (variable.isArray())
558 {
559 innermostArraySize.push_back(variable.getNestedArraySize(0));
560 }
561 BlockMemberInfo variableInfo =
562 mEncoder->encodeType(variable.type, innermostArraySize, isRowMajor);
563 if (!mIsTopLevelArrayStrideReady)
564 {
565 ASSERT(mTopLevelArrayStride);
566 mTopLevelArrayStride *= variableInfo.arrayStride;
567 mIsTopLevelArrayStrideReady = true;
568 }
569 variableInfo.topLevelArrayStride = mTopLevelArrayStride;
570 encodeVariable(variable, variableInfo, name, mappedName);
571}
572
573void TraverseShaderVariable(const ShaderVariable &variable,
574 bool isRowMajorLayout,
575 ShaderVariableVisitor *visitor)
576{
577 bool rowMajorLayout = (isRowMajorLayout || variable.isRowMajorLayout);
578 bool isRowMajor = rowMajorLayout && gl::IsMatrixType(variable.type);
579
580 if (variable.isStruct())
581 {
582 visitor->enterStruct(variable);
583 if (variable.isArray())
584 {
585 TraverseStructArrayVariable(variable, rowMajorLayout, visitor);
586 }
587 else
588 {
589 TraverseStructVariable(variable, rowMajorLayout, visitor);
590 }
591 visitor->exitStruct(variable);
592 }
593 else if (variable.isArrayOfArrays())
594 {
595 TraverseArrayOfArraysVariable(variable, 0u, isRowMajor, visitor);
596 }
597 else if (gl::IsSamplerType(variable.type))
598 {
599 visitor->visitSampler(variable);
600 }
601 else
602 {
603 visitor->visitVariable(variable, isRowMajor);
604 }
605}
606} // namespace sh
607