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 flattenedOffsetInParentArrays(-1)
43 {}
44
ShaderVariable(GLenum typeIn,unsigned int arraySizeIn)45 ShaderVariable::ShaderVariable(GLenum typeIn, unsigned int arraySizeIn) : ShaderVariable(typeIn)
46 {
47 ASSERT(arraySizeIn != 0);
48 arraySizes.push_back(arraySizeIn);
49 }
50
~ShaderVariable()51 ShaderVariable::~ShaderVariable() {}
52
ShaderVariable(const ShaderVariable & other)53 ShaderVariable::ShaderVariable(const ShaderVariable &other)
54 : type(other.type),
55 precision(other.precision),
56 name(other.name),
57 mappedName(other.mappedName),
58 arraySizes(other.arraySizes),
59 staticUse(other.staticUse),
60 active(other.active),
61 fields(other.fields),
62 structName(other.structName),
63 isRowMajorLayout(other.isRowMajorLayout),
64 flattenedOffsetInParentArrays(other.flattenedOffsetInParentArrays)
65 {}
66
operator =(const ShaderVariable & other)67 ShaderVariable &ShaderVariable::operator=(const ShaderVariable &other)
68 {
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 structName = other.structName;
78 isRowMajorLayout = other.isRowMajorLayout;
79 flattenedOffsetInParentArrays = other.flattenedOffsetInParentArrays;
80 return *this;
81 }
82
operator ==(const ShaderVariable & other) const83 bool ShaderVariable::operator==(const ShaderVariable &other) const
84 {
85 if (type != other.type || precision != other.precision || name != other.name ||
86 mappedName != other.mappedName || arraySizes != other.arraySizes ||
87 staticUse != other.staticUse || active != other.active ||
88 fields.size() != other.fields.size() || structName != other.structName ||
89 isRowMajorLayout != other.isRowMajorLayout)
90 {
91 return false;
92 }
93 for (size_t ii = 0; ii < fields.size(); ++ii)
94 {
95 if (fields[ii] != other.fields[ii])
96 return false;
97 }
98 return true;
99 }
100
setArraySize(unsigned int size)101 void ShaderVariable::setArraySize(unsigned int size)
102 {
103 arraySizes.clear();
104 if (size != 0)
105 {
106 arraySizes.push_back(size);
107 }
108 }
109
getInnerArraySizeProduct() const110 unsigned int ShaderVariable::getInnerArraySizeProduct() const
111 {
112 unsigned int arraySizeProduct = 1u;
113 for (size_t index = 1; index < arraySizes.size(); ++index)
114 {
115 arraySizeProduct *= getNestedArraySize(static_cast<unsigned int>(index));
116 }
117 return arraySizeProduct;
118 }
119
getArraySizeProduct() const120 unsigned int ShaderVariable::getArraySizeProduct() const
121 {
122 return gl::ArraySizeProduct(arraySizes);
123 }
124
indexIntoArray(unsigned int arrayIndex)125 void ShaderVariable::indexIntoArray(unsigned int arrayIndex)
126 {
127 ASSERT(isArray());
128 flattenedOffsetInParentArrays = arrayIndex + getOutermostArraySize() * parentArrayIndex();
129 arraySizes.pop_back();
130 }
131
getNestedArraySize(unsigned int arrayNestingIndex) const132 unsigned int ShaderVariable::getNestedArraySize(unsigned int arrayNestingIndex) const
133 {
134 ASSERT(arraySizes.size() > arrayNestingIndex);
135 return arraySizes[arraySizes.size() - 1u - arrayNestingIndex];
136 }
137
getBasicTypeElementCount() const138 unsigned int ShaderVariable::getBasicTypeElementCount() const
139 {
140 // GLES 3.1 Nov 2016 section 7.3.1.1 page 77 specifies that a separate entry should be generated
141 // for each array element when dealing with an array of arrays or an array of structs.
142 ASSERT(!isArrayOfArrays());
143 ASSERT(!isStruct() || !isArray());
144
145 // GLES 3.1 Nov 2016 page 82.
146 if (isArray())
147 {
148 return getOutermostArraySize();
149 }
150 return 1u;
151 }
152
findInfoByMappedName(const std::string & mappedFullName,const ShaderVariable ** leafVar,std::string * originalFullName) const153 bool ShaderVariable::findInfoByMappedName(const std::string &mappedFullName,
154 const ShaderVariable **leafVar,
155 std::string *originalFullName) const
156 {
157 ASSERT(leafVar && originalFullName);
158 // There are three cases:
159 // 1) the top variable is of struct type;
160 // 2) the top variable is an array;
161 // 3) otherwise.
162 size_t pos = mappedFullName.find_first_of(".[");
163
164 if (pos == std::string::npos)
165 {
166 // Case 3.
167 if (mappedFullName != this->mappedName)
168 return false;
169 *originalFullName = this->name;
170 *leafVar = this;
171 return true;
172 }
173 else
174 {
175 std::string topName = mappedFullName.substr(0, pos);
176 if (topName != this->mappedName)
177 return false;
178 std::string originalName = this->name;
179 std::string remaining;
180 if (mappedFullName[pos] == '[')
181 {
182 // Case 2.
183 size_t closePos = mappedFullName.find_first_of(']');
184 if (closePos < pos || closePos == std::string::npos)
185 return false;
186 // Append '[index]'.
187 originalName += mappedFullName.substr(pos, closePos - pos + 1);
188 if (closePos + 1 == mappedFullName.size())
189 {
190 *originalFullName = originalName;
191 *leafVar = this;
192 return true;
193 }
194 else
195 {
196 // In the form of 'a[0].b', so after ']', '.' is expected.
197 if (mappedFullName[closePos + 1] != '.')
198 return false;
199 remaining = mappedFullName.substr(closePos + 2); // Skip "]."
200 }
201 }
202 else
203 {
204 // Case 1.
205 remaining = mappedFullName.substr(pos + 1); // Skip "."
206 }
207 for (size_t ii = 0; ii < this->fields.size(); ++ii)
208 {
209 const ShaderVariable *fieldVar = nullptr;
210 std::string originalFieldName;
211 bool found = fields[ii].findInfoByMappedName(remaining, &fieldVar, &originalFieldName);
212 if (found)
213 {
214 *originalFullName = originalName + "." + originalFieldName;
215 *leafVar = fieldVar;
216 return true;
217 }
218 }
219 return false;
220 }
221 }
222
isBuiltIn() const223 bool ShaderVariable::isBuiltIn() const
224 {
225 return (name.size() >= 4 && name[0] == 'g' && name[1] == 'l' && name[2] == '_');
226 }
227
isEmulatedBuiltIn() const228 bool ShaderVariable::isEmulatedBuiltIn() const
229 {
230 return isBuiltIn() && name != mappedName;
231 }
232
isSameVariableAtLinkTime(const ShaderVariable & other,bool matchPrecision,bool matchName) const233 bool ShaderVariable::isSameVariableAtLinkTime(const ShaderVariable &other,
234 bool matchPrecision,
235 bool matchName) const
236 {
237 if (type != other.type)
238 return false;
239 if (matchPrecision && precision != other.precision)
240 return false;
241 if (matchName && name != other.name)
242 return false;
243 ASSERT(!matchName || mappedName == other.mappedName);
244 if (arraySizes != other.arraySizes)
245 return false;
246 if (isRowMajorLayout != other.isRowMajorLayout)
247 return false;
248 if (fields.size() != other.fields.size())
249 return false;
250
251 // [OpenGL ES 3.1 SPEC Chapter 7.4.1]
252 // Variables declared as structures are considered to match in type if and only if structure
253 // members match in name, type, qualification, and declaration order.
254 for (size_t ii = 0; ii < fields.size(); ++ii)
255 {
256 if (!fields[ii].isSameVariableAtLinkTime(other.fields[ii], matchPrecision, true))
257 {
258 return false;
259 }
260 }
261 if (structName != other.structName)
262 return false;
263 return true;
264 }
265
Uniform()266 Uniform::Uniform()
267 : binding(-1), imageUnitFormat(GL_NONE), offset(-1), readonly(false), writeonly(false)
268 {}
269
~Uniform()270 Uniform::~Uniform() {}
271
Uniform(const Uniform & other)272 Uniform::Uniform(const Uniform &other)
273 : VariableWithLocation(other),
274 binding(other.binding),
275 imageUnitFormat(other.imageUnitFormat),
276 offset(other.offset),
277 readonly(other.readonly),
278 writeonly(other.writeonly)
279 {}
280
operator =(const Uniform & other)281 Uniform &Uniform::operator=(const Uniform &other)
282 {
283 VariableWithLocation::operator=(other);
284 binding = other.binding;
285 imageUnitFormat = other.imageUnitFormat;
286 offset = other.offset;
287 readonly = other.readonly;
288 writeonly = other.writeonly;
289 return *this;
290 }
291
operator ==(const Uniform & other) const292 bool Uniform::operator==(const Uniform &other) const
293 {
294 return VariableWithLocation::operator==(other) && binding == other.binding &&
295 imageUnitFormat == other.imageUnitFormat && offset == other.offset &&
296 readonly == other.readonly && writeonly == other.writeonly;
297 }
298
isSameUniformAtLinkTime(const Uniform & other) const299 bool Uniform::isSameUniformAtLinkTime(const Uniform &other) const
300 {
301 // Enforce a consistent match.
302 // https://cvs.khronos.org/bugzilla/show_bug.cgi?id=16261
303 if (binding != -1 && other.binding != -1 && binding != other.binding)
304 {
305 return false;
306 }
307 if (imageUnitFormat != other.imageUnitFormat)
308 {
309 return false;
310 }
311 if (location != -1 && other.location != -1 && location != other.location)
312 {
313 return false;
314 }
315 if (offset != other.offset)
316 {
317 return false;
318 }
319 if (readonly != other.readonly || writeonly != other.writeonly)
320 {
321 return false;
322 }
323 return VariableWithLocation::isSameVariableAtLinkTime(other, true, true);
324 }
325
VariableWithLocation()326 VariableWithLocation::VariableWithLocation() : location(-1) {}
327
~VariableWithLocation()328 VariableWithLocation::~VariableWithLocation() {}
329
VariableWithLocation(const VariableWithLocation & other)330 VariableWithLocation::VariableWithLocation(const VariableWithLocation &other)
331 : ShaderVariable(other), location(other.location)
332 {}
333
operator =(const VariableWithLocation & other)334 VariableWithLocation &VariableWithLocation::operator=(const VariableWithLocation &other)
335 {
336 ShaderVariable::operator=(other);
337 location = other.location;
338 return *this;
339 }
340
operator ==(const VariableWithLocation & other) const341 bool VariableWithLocation::operator==(const VariableWithLocation &other) const
342 {
343 return (ShaderVariable::operator==(other) && location == other.location);
344 }
345
Attribute()346 Attribute::Attribute() {}
347
~Attribute()348 Attribute::~Attribute() {}
349
Attribute(const Attribute & other)350 Attribute::Attribute(const Attribute &other) : VariableWithLocation(other) {}
351
operator =(const Attribute & other)352 Attribute &Attribute::operator=(const Attribute &other)
353 {
354 VariableWithLocation::operator=(other);
355 return *this;
356 }
357
operator ==(const Attribute & other) const358 bool Attribute::operator==(const Attribute &other) const
359 {
360 return VariableWithLocation::operator==(other);
361 }
362
OutputVariable()363 OutputVariable::OutputVariable() : index(-1) {}
364
~OutputVariable()365 OutputVariable::~OutputVariable() {}
366
367 OutputVariable::OutputVariable(const OutputVariable &other) = default;
368 OutputVariable &OutputVariable::operator=(const OutputVariable &other) = default;
369
operator ==(const OutputVariable & other) const370 bool OutputVariable::operator==(const OutputVariable &other) const
371 {
372 return VariableWithLocation::operator==(other) && index == other.index;
373 }
374
InterfaceBlockField()375 InterfaceBlockField::InterfaceBlockField() {}
376
~InterfaceBlockField()377 InterfaceBlockField::~InterfaceBlockField() {}
378
InterfaceBlockField(const InterfaceBlockField & other)379 InterfaceBlockField::InterfaceBlockField(const InterfaceBlockField &other) : ShaderVariable(other)
380 {}
381
operator =(const InterfaceBlockField & other)382 InterfaceBlockField &InterfaceBlockField::operator=(const InterfaceBlockField &other)
383 {
384 ShaderVariable::operator=(other);
385 return *this;
386 }
387
operator ==(const InterfaceBlockField & other) const388 bool InterfaceBlockField::operator==(const InterfaceBlockField &other) const
389 {
390 return ShaderVariable::operator==(other);
391 }
392
isSameInterfaceBlockFieldAtLinkTime(const InterfaceBlockField & other) const393 bool InterfaceBlockField::isSameInterfaceBlockFieldAtLinkTime(
394 const InterfaceBlockField &other) const
395 {
396 return (ShaderVariable::isSameVariableAtLinkTime(other, true, true));
397 }
398
Varying()399 Varying::Varying() : interpolation(INTERPOLATION_SMOOTH), isInvariant(false) {}
400
~Varying()401 Varying::~Varying() {}
402
Varying(const Varying & other)403 Varying::Varying(const Varying &other)
404 : VariableWithLocation(other),
405 interpolation(other.interpolation),
406 isInvariant(other.isInvariant)
407 {}
408
operator =(const Varying & other)409 Varying &Varying::operator=(const Varying &other)
410 {
411 VariableWithLocation::operator=(other);
412 interpolation = other.interpolation;
413 isInvariant = other.isInvariant;
414 return *this;
415 }
416
operator ==(const Varying & other) const417 bool Varying::operator==(const Varying &other) const
418 {
419 return (VariableWithLocation::operator==(other) && interpolation == other.interpolation &&
420 isInvariant == other.isInvariant);
421 }
422
isSameVaryingAtLinkTime(const Varying & other) const423 bool Varying::isSameVaryingAtLinkTime(const Varying &other) const
424 {
425 return isSameVaryingAtLinkTime(other, 100);
426 }
427
isSameVaryingAtLinkTime(const Varying & other,int shaderVersion) const428 bool Varying::isSameVaryingAtLinkTime(const Varying &other, int shaderVersion) const
429 {
430 return (ShaderVariable::isSameVariableAtLinkTime(other, false, false) &&
431 InterpolationTypesMatch(interpolation, other.interpolation) &&
432 (shaderVersion >= 300 || isInvariant == other.isInvariant) &&
433 (location == other.location) &&
434 (name == other.name || (shaderVersion >= 310 && location >= 0)));
435 }
436
InterfaceBlock()437 InterfaceBlock::InterfaceBlock()
438 : arraySize(0),
439 layout(BLOCKLAYOUT_PACKED),
440 isRowMajorLayout(false),
441 binding(-1),
442 staticUse(false),
443 active(false),
444 blockType(BlockType::BLOCK_UNIFORM)
445 {}
446
~InterfaceBlock()447 InterfaceBlock::~InterfaceBlock() {}
448
InterfaceBlock(const InterfaceBlock & other)449 InterfaceBlock::InterfaceBlock(const InterfaceBlock &other)
450 : name(other.name),
451 mappedName(other.mappedName),
452 instanceName(other.instanceName),
453 arraySize(other.arraySize),
454 layout(other.layout),
455 isRowMajorLayout(other.isRowMajorLayout),
456 binding(other.binding),
457 staticUse(other.staticUse),
458 active(other.active),
459 blockType(other.blockType),
460 fields(other.fields)
461 {}
462
operator =(const InterfaceBlock & other)463 InterfaceBlock &InterfaceBlock::operator=(const InterfaceBlock &other)
464 {
465 name = other.name;
466 mappedName = other.mappedName;
467 instanceName = other.instanceName;
468 arraySize = other.arraySize;
469 layout = other.layout;
470 isRowMajorLayout = other.isRowMajorLayout;
471 binding = other.binding;
472 staticUse = other.staticUse;
473 active = other.active;
474 blockType = other.blockType;
475 fields = other.fields;
476 return *this;
477 }
478
fieldPrefix() const479 std::string InterfaceBlock::fieldPrefix() const
480 {
481 return instanceName.empty() ? "" : name;
482 }
483
fieldMappedPrefix() const484 std::string InterfaceBlock::fieldMappedPrefix() const
485 {
486 return instanceName.empty() ? "" : mappedName;
487 }
488
isSameInterfaceBlockAtLinkTime(const InterfaceBlock & other) const489 bool InterfaceBlock::isSameInterfaceBlockAtLinkTime(const InterfaceBlock &other) const
490 {
491 if (name != other.name || mappedName != other.mappedName || arraySize != other.arraySize ||
492 layout != other.layout || isRowMajorLayout != other.isRowMajorLayout ||
493 binding != other.binding || blockType != other.blockType ||
494 fields.size() != other.fields.size())
495 {
496 return false;
497 }
498
499 for (size_t fieldIndex = 0; fieldIndex < fields.size(); ++fieldIndex)
500 {
501 if (!fields[fieldIndex].isSameInterfaceBlockFieldAtLinkTime(other.fields[fieldIndex]))
502 {
503 return false;
504 }
505 }
506
507 return true;
508 }
509
isBuiltIn() const510 bool InterfaceBlock::isBuiltIn() const
511 {
512 return (name.size() >= 4 && name[0] == 'g' && name[1] == 'l' && name[2] == '_');
513 }
514
fill(int fillValue)515 void WorkGroupSize::fill(int fillValue)
516 {
517 localSizeQualifiers[0] = fillValue;
518 localSizeQualifiers[1] = fillValue;
519 localSizeQualifiers[2] = fillValue;
520 }
521
setLocalSize(int localSizeX,int localSizeY,int localSizeZ)522 void WorkGroupSize::setLocalSize(int localSizeX, int localSizeY, int localSizeZ)
523 {
524 localSizeQualifiers[0] = localSizeX;
525 localSizeQualifiers[1] = localSizeY;
526 localSizeQualifiers[2] = localSizeZ;
527 }
528
529 // check that if one of them is less than 1, then all of them are.
530 // Or if one is positive, then all of them are positive.
isLocalSizeValid() const531 bool WorkGroupSize::isLocalSizeValid() const
532 {
533 return (
534 (localSizeQualifiers[0] < 1 && localSizeQualifiers[1] < 1 && localSizeQualifiers[2] < 1) ||
535 (localSizeQualifiers[0] > 0 && localSizeQualifiers[1] > 0 && localSizeQualifiers[2] > 0));
536 }
537
isAnyValueSet() const538 bool WorkGroupSize::isAnyValueSet() const
539 {
540 return localSizeQualifiers[0] > 0 || localSizeQualifiers[1] > 0 || localSizeQualifiers[2] > 0;
541 }
542
isDeclared() const543 bool WorkGroupSize::isDeclared() const
544 {
545 bool localSizeDeclared = localSizeQualifiers[0] > 0;
546 ASSERT(isLocalSizeValid());
547 return localSizeDeclared;
548 }
549
isWorkGroupSizeMatching(const WorkGroupSize & right) const550 bool WorkGroupSize::isWorkGroupSizeMatching(const WorkGroupSize &right) const
551 {
552 for (size_t i = 0u; i < size(); ++i)
553 {
554 bool result = (localSizeQualifiers[i] == right.localSizeQualifiers[i] ||
555 (localSizeQualifiers[i] == 1 && right.localSizeQualifiers[i] == -1) ||
556 (localSizeQualifiers[i] == -1 && right.localSizeQualifiers[i] == 1));
557 if (!result)
558 {
559 return false;
560 }
561 }
562 return true;
563 }
564
operator [](size_t index)565 int &WorkGroupSize::operator[](size_t index)
566 {
567 ASSERT(index < size());
568 return localSizeQualifiers[index];
569 }
570
operator [](size_t index) const571 int WorkGroupSize::operator[](size_t index) const
572 {
573 ASSERT(index < size());
574 return localSizeQualifiers[index];
575 }
576
size() const577 size_t WorkGroupSize::size() const
578 {
579 return 3u;
580 }
581
582 } // namespace sh
583