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
isBuiltIn() const285 bool ShaderVariable::isBuiltIn() const
286 {
287 return (name.size() >= 4 && name[0] == 'g' && name[1] == 'l' && name[2] == '_');
288 }
289
isEmulatedBuiltIn() const290 bool ShaderVariable::isEmulatedBuiltIn() const
291 {
292 return isBuiltIn() && name != mappedName;
293 }
294
isSameVariableAtLinkTime(const ShaderVariable & other,bool matchPrecision,bool matchName) const295 bool ShaderVariable::isSameVariableAtLinkTime(const ShaderVariable &other,
296 bool matchPrecision,
297 bool matchName) const
298 {
299 if (type != other.type)
300 return false;
301 if (matchPrecision && precision != other.precision)
302 return false;
303 if (matchName && name != other.name)
304 return false;
305 ASSERT(!matchName || mappedName == other.mappedName);
306 if (arraySizes != other.arraySizes)
307 return false;
308 if (isRowMajorLayout != other.isRowMajorLayout)
309 return false;
310 if (fields.size() != other.fields.size())
311 return false;
312
313 // [OpenGL ES 3.1 SPEC Chapter 7.4.1]
314 // Variables declared as structures are considered to match in type if and only if structure
315 // members match in name, type, qualification, and declaration order.
316 for (size_t ii = 0; ii < fields.size(); ++ii)
317 {
318 if (!fields[ii].isSameVariableAtLinkTime(other.fields[ii], matchPrecision, true))
319 {
320 return false;
321 }
322 }
323 if (structName != other.structName)
324 return false;
325 return true;
326 }
327
isSameUniformAtLinkTime(const ShaderVariable & other) const328 bool ShaderVariable::isSameUniformAtLinkTime(const ShaderVariable &other) const
329 {
330 // Enforce a consistent match.
331 // https://cvs.khronos.org/bugzilla/show_bug.cgi?id=16261
332 if (binding != -1 && other.binding != -1 && binding != other.binding)
333 {
334 return false;
335 }
336 if (imageUnitFormat != other.imageUnitFormat)
337 {
338 return false;
339 }
340 if (location != -1 && other.location != -1 && location != other.location)
341 {
342 return false;
343 }
344 if (offset != other.offset)
345 {
346 return false;
347 }
348 if (readonly != other.readonly || writeonly != other.writeonly)
349 {
350 return false;
351 }
352 return ShaderVariable::isSameVariableAtLinkTime(other, true, true);
353 }
354
isSameInterfaceBlockFieldAtLinkTime(const ShaderVariable & other) const355 bool ShaderVariable::isSameInterfaceBlockFieldAtLinkTime(const ShaderVariable &other) const
356 {
357 return (ShaderVariable::isSameVariableAtLinkTime(other, true, true));
358 }
359
isSameVaryingAtLinkTime(const ShaderVariable & other) const360 bool ShaderVariable::isSameVaryingAtLinkTime(const ShaderVariable &other) const
361 {
362 return isSameVaryingAtLinkTime(other, 100);
363 }
364
isSameVaryingAtLinkTime(const ShaderVariable & other,int shaderVersion) const365 bool ShaderVariable::isSameVaryingAtLinkTime(const ShaderVariable &other, int shaderVersion) const
366 {
367 return (ShaderVariable::isSameVariableAtLinkTime(other, false, false) &&
368 InterpolationTypesMatch(interpolation, other.interpolation) &&
369 (shaderVersion >= 300 || isInvariant == other.isInvariant) &&
370 (location == other.location) &&
371 (name == other.name || (shaderVersion >= 310 && location >= 0)));
372 }
373
InterfaceBlock()374 InterfaceBlock::InterfaceBlock()
375 : arraySize(0),
376 layout(BLOCKLAYOUT_PACKED),
377 isRowMajorLayout(false),
378 binding(-1),
379 staticUse(false),
380 active(false),
381 blockType(BlockType::BLOCK_UNIFORM)
382 {}
383
~InterfaceBlock()384 InterfaceBlock::~InterfaceBlock() {}
385
InterfaceBlock(const InterfaceBlock & other)386 InterfaceBlock::InterfaceBlock(const InterfaceBlock &other)
387 : name(other.name),
388 mappedName(other.mappedName),
389 instanceName(other.instanceName),
390 arraySize(other.arraySize),
391 layout(other.layout),
392 isRowMajorLayout(other.isRowMajorLayout),
393 binding(other.binding),
394 staticUse(other.staticUse),
395 active(other.active),
396 blockType(other.blockType),
397 fields(other.fields)
398 {}
399
operator =(const InterfaceBlock & other)400 InterfaceBlock &InterfaceBlock::operator=(const InterfaceBlock &other)
401 {
402 name = other.name;
403 mappedName = other.mappedName;
404 instanceName = other.instanceName;
405 arraySize = other.arraySize;
406 layout = other.layout;
407 isRowMajorLayout = other.isRowMajorLayout;
408 binding = other.binding;
409 staticUse = other.staticUse;
410 active = other.active;
411 blockType = other.blockType;
412 fields = other.fields;
413 return *this;
414 }
415
fieldPrefix() const416 std::string InterfaceBlock::fieldPrefix() const
417 {
418 return instanceName.empty() ? "" : name;
419 }
420
fieldMappedPrefix() const421 std::string InterfaceBlock::fieldMappedPrefix() const
422 {
423 return instanceName.empty() ? "" : mappedName;
424 }
425
isSameInterfaceBlockAtLinkTime(const InterfaceBlock & other) const426 bool InterfaceBlock::isSameInterfaceBlockAtLinkTime(const InterfaceBlock &other) const
427 {
428 if (name != other.name || mappedName != other.mappedName || arraySize != other.arraySize ||
429 layout != other.layout || isRowMajorLayout != other.isRowMajorLayout ||
430 binding != other.binding || blockType != other.blockType ||
431 fields.size() != other.fields.size())
432 {
433 return false;
434 }
435
436 for (size_t fieldIndex = 0; fieldIndex < fields.size(); ++fieldIndex)
437 {
438 if (!fields[fieldIndex].isSameInterfaceBlockFieldAtLinkTime(other.fields[fieldIndex]))
439 {
440 return false;
441 }
442 }
443
444 return true;
445 }
446
isBuiltIn() const447 bool InterfaceBlock::isBuiltIn() const
448 {
449 return (name.size() >= 4 && name[0] == 'g' && name[1] == 'l' && name[2] == '_');
450 }
451
fill(int fillValue)452 void WorkGroupSize::fill(int fillValue)
453 {
454 localSizeQualifiers[0] = fillValue;
455 localSizeQualifiers[1] = fillValue;
456 localSizeQualifiers[2] = fillValue;
457 }
458
setLocalSize(int localSizeX,int localSizeY,int localSizeZ)459 void WorkGroupSize::setLocalSize(int localSizeX, int localSizeY, int localSizeZ)
460 {
461 localSizeQualifiers[0] = localSizeX;
462 localSizeQualifiers[1] = localSizeY;
463 localSizeQualifiers[2] = localSizeZ;
464 }
465
466 // check that if one of them is less than 1, then all of them are.
467 // Or if one is positive, then all of them are positive.
isLocalSizeValid() const468 bool WorkGroupSize::isLocalSizeValid() const
469 {
470 return (
471 (localSizeQualifiers[0] < 1 && localSizeQualifiers[1] < 1 && localSizeQualifiers[2] < 1) ||
472 (localSizeQualifiers[0] > 0 && localSizeQualifiers[1] > 0 && localSizeQualifiers[2] > 0));
473 }
474
isAnyValueSet() const475 bool WorkGroupSize::isAnyValueSet() const
476 {
477 return localSizeQualifiers[0] > 0 || localSizeQualifiers[1] > 0 || localSizeQualifiers[2] > 0;
478 }
479
isDeclared() const480 bool WorkGroupSize::isDeclared() const
481 {
482 bool localSizeDeclared = localSizeQualifiers[0] > 0;
483 ASSERT(isLocalSizeValid());
484 return localSizeDeclared;
485 }
486
isWorkGroupSizeMatching(const WorkGroupSize & right) const487 bool WorkGroupSize::isWorkGroupSizeMatching(const WorkGroupSize &right) const
488 {
489 for (size_t i = 0u; i < size(); ++i)
490 {
491 bool result = (localSizeQualifiers[i] == right.localSizeQualifiers[i] ||
492 (localSizeQualifiers[i] == 1 && right.localSizeQualifiers[i] == -1) ||
493 (localSizeQualifiers[i] == -1 && right.localSizeQualifiers[i] == 1));
494 if (!result)
495 {
496 return false;
497 }
498 }
499 return true;
500 }
501
operator [](size_t index)502 int &WorkGroupSize::operator[](size_t index)
503 {
504 ASSERT(index < size());
505 return localSizeQualifiers[index];
506 }
507
operator [](size_t index) const508 int WorkGroupSize::operator[](size_t index) const
509 {
510 ASSERT(index < size());
511 return localSizeQualifiers[index];
512 }
513
size() const514 size_t WorkGroupSize::size() const
515 {
516 return 3u;
517 }
518
519 } // namespace sh
520