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