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