1//
2// Copyright (c) 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
15namespace sh
16{
17
18namespace
19{
20
21InterpolationType 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.
29bool InterpolationTypesMatch(InterpolationType a, InterpolationType b)
30{
31 return (GetNonAuxiliaryInterpolationType(a) == GetNonAuxiliaryInterpolationType(b));
32}
33
34ShaderVariable::ShaderVariable() : ShaderVariable(GL_NONE) {}
35
36ShaderVariable::ShaderVariable(GLenum typeIn)
37 : type(typeIn),
38 precision(0),
39 staticUse(false),
40 active(false),
41 isRowMajorLayout(false),
42 flattenedOffsetInParentArrays(-1)
43{}
44
45ShaderVariable::ShaderVariable(GLenum typeIn, unsigned int arraySizeIn) : ShaderVariable(typeIn)
46{
47 ASSERT(arraySizeIn != 0);
48 arraySizes.push_back(arraySizeIn);
49}
50
51ShaderVariable::~ShaderVariable() {}
52
53ShaderVariable::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
67ShaderVariable &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
83bool 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
101void ShaderVariable::setArraySize(unsigned int size)
102{
103 arraySizes.clear();
104 if (size != 0)
105 {
106 arraySizes.push_back(size);
107 }
108}
109
110unsigned 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
120unsigned int ShaderVariable::getArraySizeProduct() const
121{
122 return gl::ArraySizeProduct(arraySizes);
123}
124
125void ShaderVariable::indexIntoArray(unsigned int arrayIndex)
126{
127 ASSERT(isArray());
128 flattenedOffsetInParentArrays = arrayIndex + getOutermostArraySize() * parentArrayIndex();
129 arraySizes.pop_back();
130}
131
132unsigned int ShaderVariable::getNestedArraySize(unsigned int arrayNestingIndex) const
133{
134 ASSERT(arraySizes.size() > arrayNestingIndex);
135 return arraySizes[arraySizes.size() - 1u - arrayNestingIndex];
136}
137
138unsigned 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
153bool 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
223bool ShaderVariable::isBuiltIn() const
224{
225 return (name.size() >= 4 && name[0] == 'g' && name[1] == 'l' && name[2] == '_');
226}
227
228bool ShaderVariable::isEmulatedBuiltIn() const
229{
230 return isBuiltIn() && name != mappedName;
231}
232
233bool 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
266Uniform::Uniform()
267 : binding(-1), imageUnitFormat(GL_NONE), offset(-1), readonly(false), writeonly(false)
268{}
269
270Uniform::~Uniform() {}
271
272Uniform::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
281Uniform &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
292bool 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
299bool 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
326VariableWithLocation::VariableWithLocation() : location(-1) {}
327
328VariableWithLocation::~VariableWithLocation() {}
329
330VariableWithLocation::VariableWithLocation(const VariableWithLocation &other)
331 : ShaderVariable(other), location(other.location)
332{}
333
334VariableWithLocation &VariableWithLocation::operator=(const VariableWithLocation &other)
335{
336 ShaderVariable::operator=(other);
337 location = other.location;
338 return *this;
339}
340
341bool VariableWithLocation::operator==(const VariableWithLocation &other) const
342{
343 return (ShaderVariable::operator==(other) && location == other.location);
344}
345
346Attribute::Attribute() {}
347
348Attribute::~Attribute() {}
349
350Attribute::Attribute(const Attribute &other) : VariableWithLocation(other) {}
351
352Attribute &Attribute::operator=(const Attribute &other)
353{
354 VariableWithLocation::operator=(other);
355 return *this;
356}
357
358bool Attribute::operator==(const Attribute &other) const
359{
360 return VariableWithLocation::operator==(other);
361}
362
363OutputVariable::OutputVariable() : index(-1) {}
364
365OutputVariable::~OutputVariable() {}
366
367OutputVariable::OutputVariable(const OutputVariable &other) = default;
368OutputVariable &OutputVariable::operator=(const OutputVariable &other) = default;
369
370bool OutputVariable::operator==(const OutputVariable &other) const
371{
372 return VariableWithLocation::operator==(other) && index == other.index;
373}
374
375InterfaceBlockField::InterfaceBlockField() {}
376
377InterfaceBlockField::~InterfaceBlockField() {}
378
379InterfaceBlockField::InterfaceBlockField(const InterfaceBlockField &other) : ShaderVariable(other)
380{}
381
382InterfaceBlockField &InterfaceBlockField::operator=(const InterfaceBlockField &other)
383{
384 ShaderVariable::operator=(other);
385 return *this;
386}
387
388bool InterfaceBlockField::operator==(const InterfaceBlockField &other) const
389{
390 return ShaderVariable::operator==(other);
391}
392
393bool InterfaceBlockField::isSameInterfaceBlockFieldAtLinkTime(
394 const InterfaceBlockField &other) const
395{
396 return (ShaderVariable::isSameVariableAtLinkTime(other, true, true));
397}
398
399Varying::Varying() : interpolation(INTERPOLATION_SMOOTH), isInvariant(false) {}
400
401Varying::~Varying() {}
402
403Varying::Varying(const Varying &other)
404 : VariableWithLocation(other),
405 interpolation(other.interpolation),
406 isInvariant(other.isInvariant)
407{}
408
409Varying &Varying::operator=(const Varying &other)
410{
411 VariableWithLocation::operator=(other);
412 interpolation = other.interpolation;
413 isInvariant = other.isInvariant;
414 return *this;
415}
416
417bool Varying::operator==(const Varying &other) const
418{
419 return (VariableWithLocation::operator==(other) && interpolation == other.interpolation &&
420 isInvariant == other.isInvariant);
421}
422
423bool Varying::isSameVaryingAtLinkTime(const Varying &other) const
424{
425 return isSameVaryingAtLinkTime(other, 100);
426}
427
428bool 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
437InterfaceBlock::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
447InterfaceBlock::~InterfaceBlock() {}
448
449InterfaceBlock::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
463InterfaceBlock &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
479std::string InterfaceBlock::fieldPrefix() const
480{
481 return instanceName.empty() ? "" : name;
482}
483
484std::string InterfaceBlock::fieldMappedPrefix() const
485{
486 return instanceName.empty() ? "" : mappedName;
487}
488
489bool 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
510bool InterfaceBlock::isBuiltIn() const
511{
512 return (name.size() >= 4 && name[0] == 'g' && name[1] == 'l' && name[2] == '_');
513}
514
515void WorkGroupSize::fill(int fillValue)
516{
517 localSizeQualifiers[0] = fillValue;
518 localSizeQualifiers[1] = fillValue;
519 localSizeQualifiers[2] = fillValue;
520}
521
522void 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.
531bool 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
538bool WorkGroupSize::isAnyValueSet() const
539{
540 return localSizeQualifiers[0] > 0 || localSizeQualifiers[1] > 0 || localSizeQualifiers[2] > 0;
541}
542
543bool WorkGroupSize::isDeclared() const
544{
545 bool localSizeDeclared = localSizeQualifiers[0] > 0;
546 ASSERT(isLocalSizeValid());
547 return localSizeDeclared;
548}
549
550bool 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
565int &WorkGroupSize::operator[](size_t index)
566{
567 ASSERT(index < size());
568 return localSizeQualifiers[index];
569}
570
571int WorkGroupSize::operator[](size_t index) const
572{
573 ASSERT(index < size());
574 return localSizeQualifiers[index];
575}
576
577size_t WorkGroupSize::size() const
578{
579 return 3u;
580}
581
582} // namespace sh
583