1 //
2 // Copyright 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 // ShaderVars.cpp:
7 //  Methods for GL variable types (varyings, uniforms, etc)
8 //
9 
10 #include <GLSLANG/ShaderLang.h>
11 
12 #include "common/debug.h"
13 #include "common/utilities.h"
14 
15 namespace sh
16 {
17 
18 namespace
19 {
20 
GetNonAuxiliaryInterpolationType(InterpolationType interpolation)21 InterpolationType GetNonAuxiliaryInterpolationType(InterpolationType interpolation)
22 {
23     return (interpolation == INTERPOLATION_CENTROID ? INTERPOLATION_SMOOTH : interpolation);
24 }
25 }  // namespace
26 // The ES 3.0 spec is not clear on this point, but the ES 3.1 spec, and discussion
27 // on Khronos.org, clarifies that a smooth/flat mismatch produces a link error,
28 // but auxiliary qualifier mismatch (centroid) does not.
InterpolationTypesMatch(InterpolationType a,InterpolationType b)29 bool InterpolationTypesMatch(InterpolationType a, InterpolationType b)
30 {
31     return (GetNonAuxiliaryInterpolationType(a) == GetNonAuxiliaryInterpolationType(b));
32 }
33 
ShaderVariable()34 ShaderVariable::ShaderVariable() : ShaderVariable(GL_NONE) {}
35 
ShaderVariable(GLenum typeIn)36 ShaderVariable::ShaderVariable(GLenum typeIn)
37     : type(typeIn),
38       precision(0),
39       staticUse(false),
40       active(false),
41       isRowMajorLayout(false),
42       location(-1),
43       hasImplicitLocation(false),
44       binding(-1),
45       imageUnitFormat(GL_NONE),
46       offset(-1),
47       readonly(false),
48       writeonly(false),
49       isFragmentInOut(false),
50       index(-1),
51       yuv(false),
52       interpolation(INTERPOLATION_SMOOTH),
53       isInvariant(false),
54       isShaderIOBlock(false),
55       isPatch(false),
56       texelFetchStaticUse(false),
57       flattenedOffsetInParentArrays(-1)
58 {}
59 
ShaderVariable(GLenum typeIn,unsigned int arraySizeIn)60 ShaderVariable::ShaderVariable(GLenum typeIn, unsigned int arraySizeIn) : ShaderVariable(typeIn)
61 {
62     ASSERT(arraySizeIn != 0);
63     arraySizes.push_back(arraySizeIn);
64 }
65 
~ShaderVariable()66 ShaderVariable::~ShaderVariable() {}
67 
ShaderVariable(const ShaderVariable & other)68 ShaderVariable::ShaderVariable(const ShaderVariable &other)
69     : type(other.type),
70       precision(other.precision),
71       name(other.name),
72       mappedName(other.mappedName),
73       arraySizes(other.arraySizes),
74       staticUse(other.staticUse),
75       active(other.active),
76       fields(other.fields),
77       structOrBlockName(other.structOrBlockName),
78       mappedStructOrBlockName(other.mappedStructOrBlockName),
79       isRowMajorLayout(other.isRowMajorLayout),
80       location(other.location),
81       hasImplicitLocation(other.hasImplicitLocation),
82       binding(other.binding),
83       imageUnitFormat(other.imageUnitFormat),
84       offset(other.offset),
85       readonly(other.readonly),
86       writeonly(other.writeonly),
87       isFragmentInOut(other.isFragmentInOut),
88       index(other.index),
89       yuv(other.yuv),
90       interpolation(other.interpolation),
91       isInvariant(other.isInvariant),
92       isShaderIOBlock(other.isShaderIOBlock),
93       isPatch(other.isPatch),
94       texelFetchStaticUse(other.texelFetchStaticUse),
95       flattenedOffsetInParentArrays(other.flattenedOffsetInParentArrays)
96 {}
97 
operator =(const ShaderVariable & other)98 ShaderVariable &ShaderVariable::operator=(const ShaderVariable &other)
99 {
100     type                          = other.type;
101     precision                     = other.precision;
102     name                          = other.name;
103     mappedName                    = other.mappedName;
104     arraySizes                    = other.arraySizes;
105     staticUse                     = other.staticUse;
106     active                        = other.active;
107     fields                        = other.fields;
108     structOrBlockName             = other.structOrBlockName;
109     mappedStructOrBlockName       = other.mappedStructOrBlockName;
110     isRowMajorLayout              = other.isRowMajorLayout;
111     flattenedOffsetInParentArrays = other.flattenedOffsetInParentArrays;
112     location                      = other.location;
113     hasImplicitLocation           = other.hasImplicitLocation;
114     binding                       = other.binding;
115     imageUnitFormat               = other.imageUnitFormat;
116     offset                        = other.offset;
117     readonly                      = other.readonly;
118     writeonly                     = other.writeonly;
119     isFragmentInOut               = other.isFragmentInOut;
120     index                         = other.index;
121     yuv                           = other.yuv;
122     interpolation                 = other.interpolation;
123     isInvariant                   = other.isInvariant;
124     isShaderIOBlock               = other.isShaderIOBlock;
125     isPatch                       = other.isPatch;
126     texelFetchStaticUse           = other.texelFetchStaticUse;
127     return *this;
128 }
129 
operator ==(const ShaderVariable & other) const130 bool ShaderVariable::operator==(const ShaderVariable &other) const
131 {
132     if (type != other.type || precision != other.precision || name != other.name ||
133         mappedName != other.mappedName || arraySizes != other.arraySizes ||
134         staticUse != other.staticUse || active != other.active ||
135         fields.size() != other.fields.size() || structOrBlockName != other.structOrBlockName ||
136         mappedStructOrBlockName != other.mappedStructOrBlockName ||
137         isRowMajorLayout != other.isRowMajorLayout || location != other.location ||
138         hasImplicitLocation != other.hasImplicitLocation || binding != other.binding ||
139         imageUnitFormat != other.imageUnitFormat || offset != other.offset ||
140         readonly != other.readonly || writeonly != other.writeonly || index != other.index ||
141         yuv != other.yuv || interpolation != other.interpolation ||
142         isInvariant != other.isInvariant || isShaderIOBlock != other.isShaderIOBlock ||
143         isPatch != other.isPatch || texelFetchStaticUse != other.texelFetchStaticUse ||
144         isFragmentInOut != other.isFragmentInOut)
145     {
146         return false;
147     }
148     for (size_t ii = 0; ii < fields.size(); ++ii)
149     {
150         if (fields[ii] != other.fields[ii])
151             return false;
152     }
153     return true;
154 }
155 
setArraySize(unsigned int size)156 void ShaderVariable::setArraySize(unsigned int size)
157 {
158     arraySizes.clear();
159     if (size != 0)
160     {
161         arraySizes.push_back(size);
162     }
163 }
164 
getInnerArraySizeProduct() const165 unsigned int ShaderVariable::getInnerArraySizeProduct() const
166 {
167     unsigned int arraySizeProduct = 1u;
168     for (size_t idx = 1; idx < arraySizes.size(); ++idx)
169     {
170         arraySizeProduct *= getNestedArraySize(static_cast<unsigned int>(idx));
171     }
172     return arraySizeProduct;
173 }
174 
getArraySizeProduct() const175 unsigned int ShaderVariable::getArraySizeProduct() const
176 {
177     return gl::ArraySizeProduct(arraySizes);
178 }
179 
indexIntoArray(unsigned int arrayIndex)180 void ShaderVariable::indexIntoArray(unsigned int arrayIndex)
181 {
182     ASSERT(isArray());
183     flattenedOffsetInParentArrays = arrayIndex + getOutermostArraySize() * parentArrayIndex();
184     arraySizes.pop_back();
185 }
186 
getNestedArraySize(unsigned int arrayNestingIndex) const187 unsigned int ShaderVariable::getNestedArraySize(unsigned int arrayNestingIndex) const
188 {
189     ASSERT(arraySizes.size() > arrayNestingIndex);
190     unsigned int arraySize = arraySizes[arraySizes.size() - 1u - arrayNestingIndex];
191 
192     if (arraySize == 0)
193     {
194         // Unsized array, so give it at least 1 entry
195         arraySize = 1;
196     }
197 
198     return arraySize;
199 }
200 
getBasicTypeElementCount() const201 unsigned int ShaderVariable::getBasicTypeElementCount() const
202 {
203     // GLES 3.1 Nov 2016 section 7.3.1.1 page 77 specifies that a separate entry should be generated
204     // for each array element when dealing with an array of arrays or an array of structs.
205     ASSERT(!isArrayOfArrays());
206     ASSERT(!isStruct() || !isArray());
207 
208     // GLES 3.1 Nov 2016 page 82.
209     if (isArray())
210     {
211         return getOutermostArraySize();
212     }
213     return 1u;
214 }
215 
getExternalSize() const216 unsigned int ShaderVariable::getExternalSize() const
217 {
218     unsigned int memorySize = 0;
219 
220     if (isStruct())
221     {
222         // Have a structure, need to compute the structure size.
223         for (const auto &field : fields)
224         {
225             memorySize += field.getExternalSize();
226         }
227     }
228     else
229     {
230         memorySize += gl::VariableExternalSize(type);
231     }
232 
233     // multiply by array size to get total memory size of this variable / struct.
234     memorySize *= getArraySizeProduct();
235 
236     return memorySize;
237 }
238 
findInfoByMappedName(const std::string & mappedFullName,const ShaderVariable ** leafVar,std::string * originalFullName) const239 bool ShaderVariable::findInfoByMappedName(const std::string &mappedFullName,
240                                           const ShaderVariable **leafVar,
241                                           std::string *originalFullName) const
242 {
243     ASSERT(leafVar && originalFullName);
244     // There are three cases:
245     // 1) the top variable is of struct type;
246     // 2) the top variable is an array;
247     // 3) otherwise.
248     size_t pos = mappedFullName.find_first_of(".[");
249 
250     if (pos == std::string::npos)
251     {
252         // Case 3.
253         if (mappedFullName != this->mappedName)
254             return false;
255         *originalFullName = this->name;
256         *leafVar          = this;
257         return true;
258     }
259     else
260     {
261         std::string topName = mappedFullName.substr(0, pos);
262         if (topName != this->mappedName)
263             return false;
264         std::string originalName = this->name;
265         std::string remaining;
266         if (mappedFullName[pos] == '[')
267         {
268             // Case 2.
269             size_t closePos = mappedFullName.find_first_of(']');
270             if (closePos < pos || closePos == std::string::npos)
271                 return false;
272             // Append '[index]'.
273             originalName += mappedFullName.substr(pos, closePos - pos + 1);
274             if (closePos + 1 == mappedFullName.size())
275             {
276                 *originalFullName = originalName;
277                 *leafVar          = this;
278                 return true;
279             }
280             else
281             {
282                 // In the form of 'a[0].b', so after ']', '.' is expected.
283                 if (mappedFullName[closePos + 1] != '.')
284                     return false;
285                 remaining = mappedFullName.substr(closePos + 2);  // Skip "]."
286             }
287         }
288         else
289         {
290             // Case 1.
291             remaining = mappedFullName.substr(pos + 1);  // Skip "."
292         }
293         for (size_t ii = 0; ii < this->fields.size(); ++ii)
294         {
295             const ShaderVariable *fieldVar = nullptr;
296             std::string originalFieldName;
297             bool found = fields[ii].findInfoByMappedName(remaining, &fieldVar, &originalFieldName);
298             if (found)
299             {
300                 *originalFullName = originalName + "." + originalFieldName;
301                 *leafVar          = fieldVar;
302                 return true;
303             }
304         }
305         return false;
306     }
307 }
308 
findField(const std::string & fullName,uint32_t * fieldIndexOut) const309 const sh::ShaderVariable *ShaderVariable::findField(const std::string &fullName,
310                                                     uint32_t *fieldIndexOut) const
311 {
312     if (fields.empty())
313     {
314         return nullptr;
315     }
316     size_t pos = fullName.find_first_of(".");
317     std::string topName, fieldName;
318     if (pos == std::string::npos)
319     {
320         // If this is a shader I/O block without an instance name, return the field given only the
321         // field name.
322         if (!isShaderIOBlock || !name.empty())
323         {
324             return nullptr;
325         }
326 
327         fieldName = fullName;
328     }
329     else
330     {
331         std::string baseName = isShaderIOBlock ? structOrBlockName : name;
332         topName              = fullName.substr(0, pos);
333         if (topName != baseName)
334         {
335             return nullptr;
336         }
337         fieldName = fullName.substr(pos + 1);
338     }
339     if (fieldName.empty())
340     {
341         return nullptr;
342     }
343     for (size_t field = 0; field < fields.size(); ++field)
344     {
345         if (fields[field].name == fieldName)
346         {
347             *fieldIndexOut = static_cast<GLuint>(field);
348             return &fields[field];
349         }
350     }
351     return nullptr;
352 }
353 
isBuiltIn() const354 bool ShaderVariable::isBuiltIn() const
355 {
356     return gl::IsBuiltInName(name);
357 }
358 
isEmulatedBuiltIn() const359 bool ShaderVariable::isEmulatedBuiltIn() const
360 {
361     return isBuiltIn() && name != mappedName;
362 }
363 
isSameVariableAtLinkTime(const ShaderVariable & other,bool matchPrecision,bool matchName) const364 bool ShaderVariable::isSameVariableAtLinkTime(const ShaderVariable &other,
365                                               bool matchPrecision,
366                                               bool matchName) const
367 {
368     if (type != other.type)
369         return false;
370     if (matchPrecision && precision != other.precision)
371         return false;
372     if (matchName && name != other.name)
373         return false;
374     ASSERT(!matchName || mappedName == other.mappedName);
375     if (arraySizes != other.arraySizes)
376         return false;
377     if (isRowMajorLayout != other.isRowMajorLayout)
378         return false;
379     if (fields.size() != other.fields.size())
380         return false;
381 
382     // [OpenGL ES 3.1 SPEC Chapter 7.4.1]
383     // Variables declared as structures are considered to match in type if and only if structure
384     // members match in name, type, qualification, and declaration order.
385     for (size_t ii = 0; ii < fields.size(); ++ii)
386     {
387         if (!fields[ii].isSameVariableAtLinkTime(other.fields[ii], matchPrecision, true))
388         {
389             return false;
390         }
391     }
392     if (structOrBlockName != other.structOrBlockName ||
393         mappedStructOrBlockName != other.mappedStructOrBlockName)
394         return false;
395     return true;
396 }
397 
updateEffectiveLocation(const sh::ShaderVariable & parent)398 void ShaderVariable::updateEffectiveLocation(const sh::ShaderVariable &parent)
399 {
400     if ((location < 0 || hasImplicitLocation) && !parent.hasImplicitLocation)
401     {
402         location = parent.location;
403     }
404 }
405 
resetEffectiveLocation()406 void ShaderVariable::resetEffectiveLocation()
407 {
408     if (hasImplicitLocation)
409     {
410         location = -1;
411     }
412 }
413 
isSameUniformAtLinkTime(const ShaderVariable & other) const414 bool ShaderVariable::isSameUniformAtLinkTime(const ShaderVariable &other) const
415 {
416     // Enforce a consistent match.
417     // https://cvs.khronos.org/bugzilla/show_bug.cgi?id=16261
418     if (binding != -1 && other.binding != -1 && binding != other.binding)
419     {
420         return false;
421     }
422     if (imageUnitFormat != other.imageUnitFormat)
423     {
424         return false;
425     }
426     if (location != -1 && other.location != -1 && location != other.location)
427     {
428         return false;
429     }
430     if (offset != other.offset)
431     {
432         return false;
433     }
434     if (readonly != other.readonly || writeonly != other.writeonly)
435     {
436         return false;
437     }
438     return ShaderVariable::isSameVariableAtLinkTime(other, true, true);
439 }
440 
isSameInterfaceBlockFieldAtLinkTime(const ShaderVariable & other) const441 bool ShaderVariable::isSameInterfaceBlockFieldAtLinkTime(const ShaderVariable &other) const
442 {
443     return (ShaderVariable::isSameVariableAtLinkTime(other, true, true));
444 }
445 
isSameVaryingAtLinkTime(const ShaderVariable & other) const446 bool ShaderVariable::isSameVaryingAtLinkTime(const ShaderVariable &other) const
447 {
448     return isSameVaryingAtLinkTime(other, 100);
449 }
450 
isSameVaryingAtLinkTime(const ShaderVariable & other,int shaderVersion) const451 bool ShaderVariable::isSameVaryingAtLinkTime(const ShaderVariable &other, int shaderVersion) const
452 {
453     return ShaderVariable::isSameVariableAtLinkTime(other, false, false) &&
454            InterpolationTypesMatch(interpolation, other.interpolation) &&
455            (shaderVersion >= 300 || isInvariant == other.isInvariant) &&
456            (isPatch == other.isPatch) && location == other.location &&
457            (isSameNameAtLinkTime(other) || (shaderVersion >= 310 && location >= 0));
458 }
459 
isSameNameAtLinkTime(const ShaderVariable & other) const460 bool ShaderVariable::isSameNameAtLinkTime(const ShaderVariable &other) const
461 {
462     if (isShaderIOBlock != other.isShaderIOBlock)
463     {
464         return false;
465     }
466 
467     if (isShaderIOBlock)
468     {
469         // Shader I/O blocks match by block name.
470         return structOrBlockName == other.structOrBlockName;
471     }
472 
473     // Otherwise match by name.
474     return name == other.name;
475 }
476 
InterfaceBlock()477 InterfaceBlock::InterfaceBlock()
478     : arraySize(0),
479       layout(BLOCKLAYOUT_PACKED),
480       isRowMajorLayout(false),
481       binding(-1),
482       staticUse(false),
483       active(false),
484       blockType(BlockType::BLOCK_UNIFORM)
485 {}
486 
~InterfaceBlock()487 InterfaceBlock::~InterfaceBlock() {}
488 
InterfaceBlock(const InterfaceBlock & other)489 InterfaceBlock::InterfaceBlock(const InterfaceBlock &other)
490     : name(other.name),
491       mappedName(other.mappedName),
492       instanceName(other.instanceName),
493       arraySize(other.arraySize),
494       layout(other.layout),
495       isRowMajorLayout(other.isRowMajorLayout),
496       binding(other.binding),
497       staticUse(other.staticUse),
498       active(other.active),
499       blockType(other.blockType),
500       fields(other.fields)
501 {}
502 
operator =(const InterfaceBlock & other)503 InterfaceBlock &InterfaceBlock::operator=(const InterfaceBlock &other)
504 {
505     name             = other.name;
506     mappedName       = other.mappedName;
507     instanceName     = other.instanceName;
508     arraySize        = other.arraySize;
509     layout           = other.layout;
510     isRowMajorLayout = other.isRowMajorLayout;
511     binding          = other.binding;
512     staticUse        = other.staticUse;
513     active           = other.active;
514     blockType        = other.blockType;
515     fields           = other.fields;
516     return *this;
517 }
518 
fieldPrefix() const519 std::string InterfaceBlock::fieldPrefix() const
520 {
521     return instanceName.empty() ? "" : name;
522 }
523 
fieldMappedPrefix() const524 std::string InterfaceBlock::fieldMappedPrefix() const
525 {
526     return instanceName.empty() ? "" : mappedName;
527 }
528 
isSameInterfaceBlockAtLinkTime(const InterfaceBlock & other) const529 bool InterfaceBlock::isSameInterfaceBlockAtLinkTime(const InterfaceBlock &other) const
530 {
531     if (name != other.name || mappedName != other.mappedName || arraySize != other.arraySize ||
532         layout != other.layout || isRowMajorLayout != other.isRowMajorLayout ||
533         binding != other.binding || blockType != other.blockType ||
534         fields.size() != other.fields.size())
535     {
536         return false;
537     }
538 
539     for (size_t fieldIndex = 0; fieldIndex < fields.size(); ++fieldIndex)
540     {
541         if (!fields[fieldIndex].isSameInterfaceBlockFieldAtLinkTime(other.fields[fieldIndex]))
542         {
543             return false;
544         }
545     }
546 
547     return true;
548 }
549 
isBuiltIn() const550 bool InterfaceBlock::isBuiltIn() const
551 {
552     return gl::IsBuiltInName(name);
553 }
554 
fill(int fillValue)555 void WorkGroupSize::fill(int fillValue)
556 {
557     localSizeQualifiers[0] = fillValue;
558     localSizeQualifiers[1] = fillValue;
559     localSizeQualifiers[2] = fillValue;
560 }
561 
setLocalSize(int localSizeX,int localSizeY,int localSizeZ)562 void WorkGroupSize::setLocalSize(int localSizeX, int localSizeY, int localSizeZ)
563 {
564     localSizeQualifiers[0] = localSizeX;
565     localSizeQualifiers[1] = localSizeY;
566     localSizeQualifiers[2] = localSizeZ;
567 }
568 
569 // check that if one of them is less than 1, then all of them are.
570 // Or if one is positive, then all of them are positive.
isLocalSizeValid() const571 bool WorkGroupSize::isLocalSizeValid() const
572 {
573     return (
574         (localSizeQualifiers[0] < 1 && localSizeQualifiers[1] < 1 && localSizeQualifiers[2] < 1) ||
575         (localSizeQualifiers[0] > 0 && localSizeQualifiers[1] > 0 && localSizeQualifiers[2] > 0));
576 }
577 
isAnyValueSet() const578 bool WorkGroupSize::isAnyValueSet() const
579 {
580     return localSizeQualifiers[0] > 0 || localSizeQualifiers[1] > 0 || localSizeQualifiers[2] > 0;
581 }
582 
isDeclared() const583 bool WorkGroupSize::isDeclared() const
584 {
585     bool localSizeDeclared = localSizeQualifiers[0] > 0;
586     ASSERT(isLocalSizeValid());
587     return localSizeDeclared;
588 }
589 
isWorkGroupSizeMatching(const WorkGroupSize & right) const590 bool WorkGroupSize::isWorkGroupSizeMatching(const WorkGroupSize &right) const
591 {
592     for (size_t i = 0u; i < size(); ++i)
593     {
594         bool result = (localSizeQualifiers[i] == right.localSizeQualifiers[i] ||
595                        (localSizeQualifiers[i] == 1 && right.localSizeQualifiers[i] == -1) ||
596                        (localSizeQualifiers[i] == -1 && right.localSizeQualifiers[i] == 1));
597         if (!result)
598         {
599             return false;
600         }
601     }
602     return true;
603 }
604 
operator [](size_t index)605 int &WorkGroupSize::operator[](size_t index)
606 {
607     ASSERT(index < size());
608     return localSizeQualifiers[index];
609 }
610 
operator [](size_t index) const611 int WorkGroupSize::operator[](size_t index) const
612 {
613     ASSERT(index < size());
614     return localSizeQualifiers[index];
615 }
616 
size() const617 size_t WorkGroupSize::size() const
618 {
619     return 3u;
620 }
621 
622 }  // namespace sh
623