1 //
2 // Copyright (c) 2017 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 
7 // UniformLinker.cpp: implements link-time checks for default block uniforms, and generates uniform
8 // locations. Populates data structures related to uniforms so that they can be stored in program
9 // state.
10 
11 #include "libANGLE/ProgramLinkedResources.h"
12 
13 #include "common/string_utils.h"
14 #include "common/utilities.h"
15 #include "libANGLE/Caps.h"
16 #include "libANGLE/Context.h"
17 #include "libANGLE/Shader.h"
18 #include "libANGLE/features.h"
19 
20 namespace gl
21 {
22 
23 namespace
24 {
25 
FindUniform(std::vector<LinkedUniform> & list,const std::string & name)26 LinkedUniform *FindUniform(std::vector<LinkedUniform> &list, const std::string &name)
27 {
28     for (LinkedUniform &uniform : list)
29     {
30         if (uniform.name == name)
31             return &uniform;
32     }
33 
34     return nullptr;
35 }
36 
GetUniformLocationBinding(const Program::Bindings & uniformLocationBindings,const sh::Uniform & uniform)37 int GetUniformLocationBinding(const Program::Bindings &uniformLocationBindings,
38                               const sh::Uniform &uniform)
39 {
40     int binding = uniformLocationBindings.getBinding(uniform.name);
41     if (uniform.isArray() && binding == -1)
42     {
43         // Bindings for array uniforms can be set either with or without [0] in the end.
44         ASSERT(angle::EndsWith(uniform.name, "[0]"));
45         std::string nameWithoutIndex = uniform.name.substr(0u, uniform.name.length() - 3u);
46         return uniformLocationBindings.getBinding(nameWithoutIndex);
47     }
48     return binding;
49 }
50 
51 }  // anonymous namespace
52 
UniformLinker(const ProgramState & state)53 UniformLinker::UniformLinker(const ProgramState &state) : mState(state)
54 {
55 }
56 
57 UniformLinker::~UniformLinker() = default;
58 
getResults(std::vector<LinkedUniform> * uniforms,std::vector<VariableLocation> * uniformLocations)59 void UniformLinker::getResults(std::vector<LinkedUniform> *uniforms,
60                                std::vector<VariableLocation> *uniformLocations)
61 {
62     uniforms->swap(mUniforms);
63     uniformLocations->swap(mUniformLocations);
64 }
65 
link(const Context * context,InfoLog & infoLog,const Program::Bindings & uniformLocationBindings)66 bool UniformLinker::link(const Context *context,
67                          InfoLog &infoLog,
68                          const Program::Bindings &uniformLocationBindings)
69 {
70     if (mState.getAttachedVertexShader() && mState.getAttachedFragmentShader())
71     {
72         ASSERT(mState.getAttachedComputeShader() == nullptr);
73         if (!validateVertexAndFragmentUniforms(context, infoLog))
74         {
75             return false;
76         }
77     }
78 
79     // Flatten the uniforms list (nested fields) into a simple list (no nesting).
80     // Also check the maximum uniform vector and sampler counts.
81     if (!flattenUniformsAndCheckCaps(context, infoLog))
82     {
83         return false;
84     }
85 
86     if (!checkMaxCombinedAtomicCounters(context->getCaps(), infoLog))
87     {
88         return false;
89     }
90 
91     if (!indexUniforms(infoLog, uniformLocationBindings))
92     {
93         return false;
94     }
95 
96     return true;
97 }
98 
validateVertexAndFragmentUniforms(const Context * context,InfoLog & infoLog) const99 bool UniformLinker::validateVertexAndFragmentUniforms(const Context *context,
100                                                       InfoLog &infoLog) const
101 {
102     // Check that uniforms defined in the vertex and fragment shaders are identical
103     std::map<std::string, sh::Uniform> linkedUniforms;
104     const std::vector<sh::Uniform> &vertexUniforms =
105         mState.getAttachedVertexShader()->getUniforms(context);
106     const std::vector<sh::Uniform> &fragmentUniforms =
107         mState.getAttachedFragmentShader()->getUniforms(context);
108 
109     for (const sh::Uniform &vertexUniform : vertexUniforms)
110     {
111         linkedUniforms[vertexUniform.name] = vertexUniform;
112     }
113 
114     for (const sh::Uniform &fragmentUniform : fragmentUniforms)
115     {
116         auto entry = linkedUniforms.find(fragmentUniform.name);
117         if (entry != linkedUniforms.end())
118         {
119             const sh::Uniform &linkedUniform = entry->second;
120             const std::string &uniformName   = "uniform '" + linkedUniform.name + "'";
121             if (!linkValidateUniforms(infoLog, uniformName, linkedUniform, fragmentUniform))
122             {
123                 return false;
124             }
125         }
126     }
127     return true;
128 }
129 
130 // GLSL ES Spec 3.00.3, section 4.3.5.
linkValidateUniforms(InfoLog & infoLog,const std::string & uniformName,const sh::Uniform & vertexUniform,const sh::Uniform & fragmentUniform)131 bool UniformLinker::linkValidateUniforms(InfoLog &infoLog,
132                                          const std::string &uniformName,
133                                          const sh::Uniform &vertexUniform,
134                                          const sh::Uniform &fragmentUniform)
135 {
136 #if ANGLE_PROGRAM_LINK_VALIDATE_UNIFORM_PRECISION == ANGLE_ENABLED
137     const bool validatePrecision = true;
138 #else
139     const bool validatePrecision = false;
140 #endif
141 
142     if (!Program::linkValidateVariablesBase(infoLog, uniformName, vertexUniform, fragmentUniform,
143                                             validatePrecision))
144     {
145         return false;
146     }
147 
148     // GLSL ES Spec 3.10.4, section 4.4.5.
149     if (vertexUniform.binding != -1 && fragmentUniform.binding != -1 &&
150         vertexUniform.binding != fragmentUniform.binding)
151     {
152         infoLog << "Binding layout qualifiers for " << uniformName
153                 << " differ between vertex and fragment shaders.";
154         return false;
155     }
156 
157     // GLSL ES Spec 3.10.4, section 9.2.1.
158     if (vertexUniform.location != -1 && fragmentUniform.location != -1 &&
159         vertexUniform.location != fragmentUniform.location)
160     {
161         infoLog << "Location layout qualifiers for " << uniformName
162                 << " differ between vertex and fragment shaders.";
163         return false;
164     }
165     if (vertexUniform.offset != fragmentUniform.offset)
166     {
167         infoLog << "Offset layout qualifiers for " << uniformName
168                 << " differ between vertex and fragment shaders.";
169         return false;
170     }
171 
172     return true;
173 }
174 
indexUniforms(InfoLog & infoLog,const Program::Bindings & uniformLocationBindings)175 bool UniformLinker::indexUniforms(InfoLog &infoLog,
176                                   const Program::Bindings &uniformLocationBindings)
177 {
178     // All the locations where another uniform can't be located.
179     std::set<GLuint> reservedLocations;
180     // Locations which have been allocated for an unused uniform.
181     std::set<GLuint> ignoredLocations;
182 
183     int maxUniformLocation = -1;
184 
185     // Gather uniform locations that have been set either using the bindUniformLocation API or by
186     // using a location layout qualifier and check conflicts between them.
187     if (!gatherUniformLocationsAndCheckConflicts(infoLog, uniformLocationBindings,
188                                                  &reservedLocations, &ignoredLocations,
189                                                  &maxUniformLocation))
190     {
191         return false;
192     }
193 
194     // Conflicts have been checked, now we can prune non-statically used uniforms. Code further down
195     // the line relies on only having statically used uniforms in mUniforms.
196     pruneUnusedUniforms();
197 
198     // Gather uniforms that have their location pre-set and uniforms that don't yet have a location.
199     std::vector<VariableLocation> unlocatedUniforms;
200     std::map<GLuint, VariableLocation> preLocatedUniforms;
201 
202     for (size_t uniformIndex = 0; uniformIndex < mUniforms.size(); uniformIndex++)
203     {
204         const LinkedUniform &uniform = mUniforms[uniformIndex];
205 
206         if (uniform.isBuiltIn() || IsAtomicCounterType(uniform.type))
207         {
208             continue;
209         }
210 
211         int preSetLocation = GetUniformLocationBinding(uniformLocationBindings, uniform);
212         int shaderLocation = uniform.location;
213 
214         if (shaderLocation != -1)
215         {
216             preSetLocation = shaderLocation;
217         }
218 
219         unsigned int elementCount = uniform.getBasicTypeElementCount();
220         for (unsigned int arrayIndex = 0; arrayIndex < elementCount; arrayIndex++)
221         {
222             VariableLocation location(arrayIndex, static_cast<unsigned int>(uniformIndex));
223 
224             if ((arrayIndex == 0 && preSetLocation != -1) || shaderLocation != -1)
225             {
226                 int elementLocation                 = preSetLocation + arrayIndex;
227                 preLocatedUniforms[elementLocation] = location;
228             }
229             else
230             {
231                 unlocatedUniforms.push_back(location);
232             }
233         }
234     }
235 
236     // Make enough space for all uniforms, with pre-set locations or not.
237     mUniformLocations.resize(
238         std::max(unlocatedUniforms.size() + preLocatedUniforms.size() + ignoredLocations.size(),
239                  static_cast<size_t>(maxUniformLocation + 1)));
240 
241     // Assign uniforms with pre-set locations
242     for (const auto &uniform : preLocatedUniforms)
243     {
244         mUniformLocations[uniform.first] = uniform.second;
245     }
246 
247     // Assign ignored uniforms
248     for (const auto &ignoredLocation : ignoredLocations)
249     {
250         mUniformLocations[ignoredLocation].markIgnored();
251     }
252 
253     // Automatically assign locations for the rest of the uniforms
254     size_t nextUniformLocation = 0;
255     for (const auto &unlocatedUniform : unlocatedUniforms)
256     {
257         while (mUniformLocations[nextUniformLocation].used() ||
258                mUniformLocations[nextUniformLocation].ignored)
259         {
260             nextUniformLocation++;
261         }
262 
263         ASSERT(nextUniformLocation < mUniformLocations.size());
264         mUniformLocations[nextUniformLocation] = unlocatedUniform;
265         nextUniformLocation++;
266     }
267 
268     return true;
269 }
270 
gatherUniformLocationsAndCheckConflicts(InfoLog & infoLog,const Program::Bindings & uniformLocationBindings,std::set<GLuint> * reservedLocations,std::set<GLuint> * ignoredLocations,int * maxUniformLocation)271 bool UniformLinker::gatherUniformLocationsAndCheckConflicts(
272     InfoLog &infoLog,
273     const Program::Bindings &uniformLocationBindings,
274     std::set<GLuint> *reservedLocations,
275     std::set<GLuint> *ignoredLocations,
276     int *maxUniformLocation)
277 {
278     for (const LinkedUniform &uniform : mUniforms)
279     {
280         if (uniform.isBuiltIn())
281         {
282             continue;
283         }
284 
285         int apiBoundLocation = GetUniformLocationBinding(uniformLocationBindings, uniform);
286         int shaderLocation   = uniform.location;
287 
288         if (shaderLocation != -1)
289         {
290             unsigned int elementCount = uniform.getBasicTypeElementCount();
291 
292             for (unsigned int arrayIndex = 0; arrayIndex < elementCount; arrayIndex++)
293             {
294                 // GLSL ES 3.10 section 4.4.3
295                 int elementLocation = shaderLocation + arrayIndex;
296                 *maxUniformLocation = std::max(*maxUniformLocation, elementLocation);
297                 if (reservedLocations->find(elementLocation) != reservedLocations->end())
298                 {
299                     infoLog << "Multiple uniforms bound to location " << elementLocation << ".";
300                     return false;
301                 }
302                 reservedLocations->insert(elementLocation);
303                 if (!uniform.staticUse)
304                 {
305                     ignoredLocations->insert(elementLocation);
306                 }
307             }
308         }
309         else if (apiBoundLocation != -1 && uniform.staticUse)
310         {
311             // Only the first location is reserved even if the uniform is an array.
312             *maxUniformLocation = std::max(*maxUniformLocation, apiBoundLocation);
313             if (reservedLocations->find(apiBoundLocation) != reservedLocations->end())
314             {
315                 infoLog << "Multiple uniforms bound to location " << apiBoundLocation << ".";
316                 return false;
317             }
318             reservedLocations->insert(apiBoundLocation);
319         }
320     }
321 
322     // Record the uniform locations that were bound using the API for uniforms that were not found
323     // from the shader. Other uniforms should not be assigned to those locations.
324     for (const auto &locationBinding : uniformLocationBindings)
325     {
326         GLuint location = locationBinding.second;
327         if (reservedLocations->find(location) == reservedLocations->end())
328         {
329             ignoredLocations->insert(location);
330             *maxUniformLocation = std::max(*maxUniformLocation, static_cast<int>(location));
331         }
332     }
333 
334     return true;
335 }
336 
pruneUnusedUniforms()337 void UniformLinker::pruneUnusedUniforms()
338 {
339     auto uniformIter = mUniforms.begin();
340     while (uniformIter != mUniforms.end())
341     {
342         if (uniformIter->staticUse)
343         {
344             ++uniformIter;
345         }
346         else
347         {
348             uniformIter = mUniforms.erase(uniformIter);
349         }
350     }
351 }
352 
flattenUniformsAndCheckCapsForShader(const Context * context,Shader * shader,GLuint maxUniformComponents,GLuint maxTextureImageUnits,GLuint maxImageUnits,GLuint maxAtomicCounters,const std::string & componentsErrorMessage,const std::string & samplerErrorMessage,const std::string & imageErrorMessage,const std::string & atomicCounterErrorMessage,std::vector<LinkedUniform> & samplerUniforms,std::vector<LinkedUniform> & imageUniforms,std::vector<LinkedUniform> & atomicCounterUniforms,InfoLog & infoLog)353 bool UniformLinker::flattenUniformsAndCheckCapsForShader(
354     const Context *context,
355     Shader *shader,
356     GLuint maxUniformComponents,
357     GLuint maxTextureImageUnits,
358     GLuint maxImageUnits,
359     GLuint maxAtomicCounters,
360     const std::string &componentsErrorMessage,
361     const std::string &samplerErrorMessage,
362     const std::string &imageErrorMessage,
363     const std::string &atomicCounterErrorMessage,
364     std::vector<LinkedUniform> &samplerUniforms,
365     std::vector<LinkedUniform> &imageUniforms,
366     std::vector<LinkedUniform> &atomicCounterUniforms,
367     InfoLog &infoLog)
368 {
369     ShaderUniformCount shaderUniformCount;
370     for (const sh::Uniform &uniform : shader->getUniforms(context))
371     {
372         shaderUniformCount += flattenUniform(uniform, &samplerUniforms, &imageUniforms,
373                                              &atomicCounterUniforms, shader->getType());
374     }
375 
376     if (shaderUniformCount.vectorCount > maxUniformComponents)
377     {
378         infoLog << componentsErrorMessage << maxUniformComponents << ").";
379         return false;
380     }
381 
382     if (shaderUniformCount.samplerCount > maxTextureImageUnits)
383     {
384         infoLog << samplerErrorMessage << maxTextureImageUnits << ").";
385         return false;
386     }
387 
388     if (shaderUniformCount.imageCount > maxImageUnits)
389     {
390         infoLog << imageErrorMessage << maxImageUnits << ").";
391         return false;
392     }
393 
394     if (shaderUniformCount.atomicCounterCount > maxAtomicCounters)
395     {
396         infoLog << atomicCounterErrorMessage << maxAtomicCounters << ").";
397         return false;
398     }
399 
400     return true;
401 }
402 
flattenUniformsAndCheckCaps(const Context * context,InfoLog & infoLog)403 bool UniformLinker::flattenUniformsAndCheckCaps(const Context *context, InfoLog &infoLog)
404 {
405     std::vector<LinkedUniform> samplerUniforms;
406     std::vector<LinkedUniform> imageUniforms;
407     std::vector<LinkedUniform> atomicCounterUniforms;
408 
409     const Caps &caps = context->getCaps();
410 
411     if (mState.getAttachedComputeShader())
412     {
413         Shader *computeShader = mState.getAttachedComputeShader();
414 
415         // TODO (mradev): check whether we need finer-grained component counting
416         if (!flattenUniformsAndCheckCapsForShader(
417                 context, computeShader, caps.maxComputeUniformComponents / 4,
418                 caps.maxComputeTextureImageUnits, caps.maxComputeImageUniforms,
419                 caps.maxComputeAtomicCounters,
420                 "Compute shader active uniforms exceed MAX_COMPUTE_UNIFORM_COMPONENTS (",
421                 "Compute shader sampler count exceeds MAX_COMPUTE_TEXTURE_IMAGE_UNITS (",
422                 "Compute shader image count exceeds MAX_COMPUTE_IMAGE_UNIFORMS (",
423                 "Compute shader atomic counter count exceeds MAX_COMPUTE_ATOMIC_COUNTERS (",
424                 samplerUniforms, imageUniforms, atomicCounterUniforms, infoLog))
425         {
426             return false;
427         }
428     }
429     else
430     {
431         Shader *vertexShader = mState.getAttachedVertexShader();
432 
433         if (!flattenUniformsAndCheckCapsForShader(
434                 context, vertexShader, caps.maxVertexUniformVectors,
435                 caps.maxVertexTextureImageUnits, caps.maxVertexImageUniforms,
436                 caps.maxVertexAtomicCounters,
437                 "Vertex shader active uniforms exceed MAX_VERTEX_UNIFORM_VECTORS (",
438                 "Vertex shader sampler count exceeds MAX_VERTEX_TEXTURE_IMAGE_UNITS (",
439                 "Vertex shader image count exceeds MAX_VERTEX_IMAGE_UNIFORMS (",
440                 "Vertex shader atomic counter count exceeds MAX_VERTEX_ATOMIC_COUNTERS (",
441                 samplerUniforms, imageUniforms, atomicCounterUniforms, infoLog))
442         {
443             return false;
444         }
445 
446         Shader *fragmentShader = mState.getAttachedFragmentShader();
447 
448         if (!flattenUniformsAndCheckCapsForShader(
449                 context, fragmentShader, caps.maxFragmentUniformVectors, caps.maxTextureImageUnits,
450                 caps.maxFragmentImageUniforms, caps.maxFragmentAtomicCounters,
451                 "Fragment shader active uniforms exceed MAX_FRAGMENT_UNIFORM_VECTORS (",
452                 "Fragment shader sampler count exceeds MAX_TEXTURE_IMAGE_UNITS (",
453                 "Fragment shader image count exceeds MAX_FRAGMENT_IMAGE_UNIFORMS (",
454                 "Fragment shader atomic counter count exceeds MAX_FRAGMENT_ATOMIC_COUNTERS (",
455                 samplerUniforms, imageUniforms, atomicCounterUniforms, infoLog))
456         {
457             return false;
458         }
459     }
460 
461     mUniforms.insert(mUniforms.end(), samplerUniforms.begin(), samplerUniforms.end());
462     mUniforms.insert(mUniforms.end(), imageUniforms.begin(), imageUniforms.end());
463     mUniforms.insert(mUniforms.end(), atomicCounterUniforms.begin(), atomicCounterUniforms.end());
464     return true;
465 }
466 
flattenUniform(const sh::Uniform & uniform,std::vector<LinkedUniform> * samplerUniforms,std::vector<LinkedUniform> * imageUniforms,std::vector<LinkedUniform> * atomicCounterUniforms,GLenum shaderType)467 UniformLinker::ShaderUniformCount UniformLinker::flattenUniform(
468     const sh::Uniform &uniform,
469     std::vector<LinkedUniform> *samplerUniforms,
470     std::vector<LinkedUniform> *imageUniforms,
471     std::vector<LinkedUniform> *atomicCounterUniforms,
472     GLenum shaderType)
473 {
474     int location = uniform.location;
475     ShaderUniformCount shaderUniformCount =
476         flattenUniformImpl(uniform, uniform.name, uniform.mappedName, samplerUniforms,
477                            imageUniforms, atomicCounterUniforms, shaderType, uniform.staticUse,
478                            uniform.binding, uniform.offset, &location);
479     if (uniform.staticUse)
480     {
481         return shaderUniformCount;
482     }
483     return ShaderUniformCount();
484 }
485 
flattenArrayOfStructsUniform(const sh::ShaderVariable & uniform,unsigned int arrayNestingIndex,const std::string & namePrefix,const std::string & mappedNamePrefix,std::vector<LinkedUniform> * samplerUniforms,std::vector<LinkedUniform> * imageUniforms,std::vector<LinkedUniform> * atomicCounterUniforms,GLenum shaderType,bool markStaticUse,int binding,int offset,int * location)486 UniformLinker::ShaderUniformCount UniformLinker::flattenArrayOfStructsUniform(
487     const sh::ShaderVariable &uniform,
488     unsigned int arrayNestingIndex,
489     const std::string &namePrefix,
490     const std::string &mappedNamePrefix,
491     std::vector<LinkedUniform> *samplerUniforms,
492     std::vector<LinkedUniform> *imageUniforms,
493     std::vector<LinkedUniform> *atomicCounterUniforms,
494     GLenum shaderType,
495     bool markStaticUse,
496     int binding,
497     int offset,
498     int *location)
499 {
500     // Nested arrays are processed starting from outermost (arrayNestingIndex 0u) and ending at the
501     // innermost.
502     ShaderUniformCount shaderUniformCount;
503     const unsigned int currentArraySize = uniform.getNestedArraySize(arrayNestingIndex);
504     for (unsigned int arrayElement = 0u; arrayElement < currentArraySize; ++arrayElement)
505     {
506         const std::string elementName       = namePrefix + ArrayString(arrayElement);
507         const std::string elementMappedName = mappedNamePrefix + ArrayString(arrayElement);
508         if (arrayNestingIndex + 1u < uniform.arraySizes.size())
509         {
510             shaderUniformCount += flattenArrayOfStructsUniform(
511                 uniform, arrayNestingIndex + 1u, elementName, elementMappedName, samplerUniforms,
512                 imageUniforms, atomicCounterUniforms, shaderType, markStaticUse, binding, offset,
513                 location);
514         }
515         else
516         {
517             shaderUniformCount += flattenStructUniform(
518                 uniform.fields, elementName, elementMappedName, samplerUniforms, imageUniforms,
519                 atomicCounterUniforms, shaderType, markStaticUse, binding, offset, location);
520         }
521     }
522     return shaderUniformCount;
523 }
524 
flattenStructUniform(const std::vector<sh::ShaderVariable> & fields,const std::string & namePrefix,const std::string & mappedNamePrefix,std::vector<LinkedUniform> * samplerUniforms,std::vector<LinkedUniform> * imageUniforms,std::vector<LinkedUniform> * atomicCounterUniforms,GLenum shaderType,bool markStaticUse,int binding,int offset,int * location)525 UniformLinker::ShaderUniformCount UniformLinker::flattenStructUniform(
526     const std::vector<sh::ShaderVariable> &fields,
527     const std::string &namePrefix,
528     const std::string &mappedNamePrefix,
529     std::vector<LinkedUniform> *samplerUniforms,
530     std::vector<LinkedUniform> *imageUniforms,
531     std::vector<LinkedUniform> *atomicCounterUniforms,
532     GLenum shaderType,
533     bool markStaticUse,
534     int binding,
535     int offset,
536     int *location)
537 {
538     ShaderUniformCount shaderUniformCount;
539     for (const sh::ShaderVariable &field : fields)
540     {
541         const std::string &fieldName       = namePrefix + "." + field.name;
542         const std::string &fieldMappedName = mappedNamePrefix + "." + field.mappedName;
543 
544         shaderUniformCount +=
545             flattenUniformImpl(field, fieldName, fieldMappedName, samplerUniforms, imageUniforms,
546                                atomicCounterUniforms, shaderType, markStaticUse, -1, -1, location);
547     }
548     return shaderUniformCount;
549 }
550 
flattenArrayUniform(const sh::ShaderVariable & uniform,const std::string & namePrefix,const std::string & mappedNamePrefix,std::vector<LinkedUniform> * samplerUniforms,std::vector<LinkedUniform> * imageUniforms,std::vector<LinkedUniform> * atomicCounterUniforms,GLenum shaderType,bool markStaticUse,int binding,int offset,int * location)551 UniformLinker::ShaderUniformCount UniformLinker::flattenArrayUniform(
552     const sh::ShaderVariable &uniform,
553     const std::string &namePrefix,
554     const std::string &mappedNamePrefix,
555     std::vector<LinkedUniform> *samplerUniforms,
556     std::vector<LinkedUniform> *imageUniforms,
557     std::vector<LinkedUniform> *atomicCounterUniforms,
558     GLenum shaderType,
559     bool markStaticUse,
560     int binding,
561     int offset,
562     int *location)
563 {
564     ShaderUniformCount shaderUniformCount;
565 
566     ASSERT(uniform.isArray());
567     for (unsigned int arrayElement = 0u; arrayElement < uniform.getOutermostArraySize();
568          ++arrayElement)
569     {
570         sh::ShaderVariable uniformElement = uniform;
571         uniformElement.indexIntoArray(arrayElement);
572         const std::string elementName       = namePrefix + ArrayString(arrayElement);
573         const std::string elementMappedName = mappedNamePrefix + ArrayString(arrayElement);
574 
575         shaderUniformCount += flattenUniformImpl(
576             uniformElement, elementName, elementMappedName, samplerUniforms, imageUniforms,
577             atomicCounterUniforms, shaderType, markStaticUse, binding, offset, location);
578     }
579     return shaderUniformCount;
580 }
581 
flattenUniformImpl(const sh::ShaderVariable & uniform,const std::string & fullName,const std::string & fullMappedName,std::vector<LinkedUniform> * samplerUniforms,std::vector<LinkedUniform> * imageUniforms,std::vector<LinkedUniform> * atomicCounterUniforms,GLenum shaderType,bool markStaticUse,int binding,int offset,int * location)582 UniformLinker::ShaderUniformCount UniformLinker::flattenUniformImpl(
583     const sh::ShaderVariable &uniform,
584     const std::string &fullName,
585     const std::string &fullMappedName,
586     std::vector<LinkedUniform> *samplerUniforms,
587     std::vector<LinkedUniform> *imageUniforms,
588     std::vector<LinkedUniform> *atomicCounterUniforms,
589     GLenum shaderType,
590     bool markStaticUse,
591     int binding,
592     int offset,
593     int *location)
594 {
595     ASSERT(location);
596     ShaderUniformCount shaderUniformCount;
597 
598     if (uniform.isStruct())
599     {
600         if (uniform.isArray())
601         {
602             shaderUniformCount += flattenArrayOfStructsUniform(
603                 uniform, 0u, fullName, fullMappedName, samplerUniforms, imageUniforms,
604                 atomicCounterUniforms, shaderType, markStaticUse, binding, offset, location);
605         }
606         else
607         {
608             shaderUniformCount += flattenStructUniform(
609                 uniform.fields, fullName, fullMappedName, samplerUniforms, imageUniforms,
610                 atomicCounterUniforms, shaderType, markStaticUse, binding, offset, location);
611         }
612         return shaderUniformCount;
613     }
614     if (uniform.isArrayOfArrays())
615     {
616         // GLES 3.1 November 2016 section 7.3.1 page 77:
617         // "For an active variable declared as an array of an aggregate data type (structures or
618         // arrays), a separate entry will be generated for each active array element"
619         return flattenArrayUniform(uniform, fullName, fullMappedName, samplerUniforms,
620                                    imageUniforms, atomicCounterUniforms, shaderType, markStaticUse,
621                                    binding, offset, location);
622     }
623 
624     // Not a struct
625     bool isSampler                              = IsSamplerType(uniform.type);
626     bool isImage                                = IsImageType(uniform.type);
627     bool isAtomicCounter                        = IsAtomicCounterType(uniform.type);
628     std::vector<gl::LinkedUniform> *uniformList = &mUniforms;
629     if (isSampler)
630     {
631         uniformList = samplerUniforms;
632     }
633     else if (isImage)
634     {
635         uniformList = imageUniforms;
636     }
637     else if (isAtomicCounter)
638     {
639         uniformList = atomicCounterUniforms;
640     }
641 
642     std::string fullNameWithArrayIndex(fullName);
643     std::string fullMappedNameWithArrayIndex(fullMappedName);
644 
645     if (uniform.isArray())
646     {
647         // We're following the GLES 3.1 November 2016 spec section 7.3.1.1 Naming Active Resources
648         // and including [0] at the end of array variable names.
649         fullNameWithArrayIndex += "[0]";
650         fullMappedNameWithArrayIndex += "[0]";
651     }
652 
653     LinkedUniform *existingUniform = FindUniform(*uniformList, fullNameWithArrayIndex);
654     if (existingUniform)
655     {
656         if (binding != -1)
657         {
658             existingUniform->binding = binding;
659         }
660         if (offset != -1)
661         {
662             existingUniform->offset = offset;
663         }
664         if (*location != -1)
665         {
666             existingUniform->location = *location;
667         }
668         if (markStaticUse)
669         {
670             existingUniform->staticUse = true;
671             existingUniform->setStaticUse(shaderType, true);
672         }
673     }
674     else
675     {
676         ASSERT(uniform.arraySizes.size() <= 1u);
677         LinkedUniform linkedUniform(uniform.type, uniform.precision, fullNameWithArrayIndex,
678                                     uniform.arraySizes, binding, offset, *location, -1,
679                                     sh::BlockMemberInfo::getDefaultBlockInfo());
680         linkedUniform.mappedName                    = fullMappedNameWithArrayIndex;
681         linkedUniform.staticUse                     = markStaticUse;
682         linkedUniform.flattenedOffsetInParentArrays = uniform.flattenedOffsetInParentArrays;
683         if (markStaticUse)
684         {
685             linkedUniform.setStaticUse(shaderType, true);
686         }
687 
688         uniformList->push_back(linkedUniform);
689     }
690 
691     // Struct and array of arrays uniforms get flattened so we can use getBasicTypeElementCount().
692     unsigned int elementCount = uniform.getBasicTypeElementCount();
693 
694     // Samplers and images aren't "real" uniforms, so they don't count towards register usage.
695     // Likewise, don't count "real" uniforms towards opaque count.
696     shaderUniformCount.vectorCount =
697         (IsOpaqueType(uniform.type) ? 0 : (VariableRegisterCount(uniform.type) * elementCount));
698     shaderUniformCount.samplerCount       = (isSampler ? elementCount : 0);
699     shaderUniformCount.imageCount         = (isImage ? elementCount : 0);
700     shaderUniformCount.atomicCounterCount = (isAtomicCounter ? elementCount : 0);
701 
702     if (*location != -1)
703     {
704         *location += elementCount;
705     }
706 
707     return shaderUniformCount;
708 }
709 
checkMaxCombinedAtomicCounters(const Caps & caps,InfoLog & infoLog)710 bool UniformLinker::checkMaxCombinedAtomicCounters(const Caps &caps, InfoLog &infoLog)
711 {
712     unsigned int atomicCounterCount = 0;
713     for (const auto &uniform : mUniforms)
714     {
715         if (IsAtomicCounterType(uniform.type) && uniform.staticUse)
716         {
717             atomicCounterCount += uniform.getBasicTypeElementCount();
718             if (atomicCounterCount > caps.maxCombinedAtomicCounters)
719             {
720                 infoLog << "atomic counter count exceeds MAX_COMBINED_ATOMIC_COUNTERS"
721                         << caps.maxCombinedAtomicCounters << ").";
722                 return false;
723             }
724         }
725     }
726     return true;
727 }
728 
729 // InterfaceBlockLinker implementation.
InterfaceBlockLinker(std::vector<InterfaceBlock> * blocksOut)730 InterfaceBlockLinker::InterfaceBlockLinker(std::vector<InterfaceBlock> *blocksOut)
731     : mBlocksOut(blocksOut)
732 {
733 }
734 
~InterfaceBlockLinker()735 InterfaceBlockLinker::~InterfaceBlockLinker()
736 {
737 }
738 
addShaderBlocks(GLenum shader,const std::vector<sh::InterfaceBlock> * blocks)739 void InterfaceBlockLinker::addShaderBlocks(GLenum shader,
740                                            const std::vector<sh::InterfaceBlock> *blocks)
741 {
742     mShaderBlocks.push_back(std::make_pair(shader, blocks));
743 }
744 
linkBlocks(const GetBlockSize & getBlockSize,const GetBlockMemberInfo & getMemberInfo) const745 void InterfaceBlockLinker::linkBlocks(const GetBlockSize &getBlockSize,
746                                       const GetBlockMemberInfo &getMemberInfo) const
747 {
748     ASSERT(mBlocksOut->empty());
749 
750     std::set<std::string> visitedList;
751 
752     for (const auto &shaderBlocks : mShaderBlocks)
753     {
754         const GLenum shaderType = shaderBlocks.first;
755 
756         for (const auto &block : *shaderBlocks.second)
757         {
758             // Only 'packed' blocks are allowed to be considered inactive.
759             if (!block.staticUse && block.layout == sh::BLOCKLAYOUT_PACKED)
760                 continue;
761 
762             if (visitedList.count(block.name) > 0)
763             {
764                 if (block.staticUse)
765                 {
766                     for (InterfaceBlock &priorBlock : *mBlocksOut)
767                     {
768                         if (block.name == priorBlock.name)
769                         {
770                             priorBlock.setStaticUse(shaderType, true);
771                             // TODO(jiajia.qin@intel.com): update the block members static use.
772                         }
773                     }
774                 }
775             }
776             else
777             {
778                 defineInterfaceBlock(getBlockSize, getMemberInfo, block, shaderType);
779                 visitedList.insert(block.name);
780             }
781         }
782     }
783 }
784 
785 template <typename VarT>
defineArrayOfStructsBlockMembers(const GetBlockMemberInfo & getMemberInfo,const VarT & field,unsigned int arrayNestingIndex,const std::string & prefix,const std::string & mappedPrefix,int blockIndex,bool singleEntryForTopLevelArray,int topLevelArraySize) const786 void InterfaceBlockLinker::defineArrayOfStructsBlockMembers(const GetBlockMemberInfo &getMemberInfo,
787                                                             const VarT &field,
788                                                             unsigned int arrayNestingIndex,
789                                                             const std::string &prefix,
790                                                             const std::string &mappedPrefix,
791                                                             int blockIndex,
792                                                             bool singleEntryForTopLevelArray,
793                                                             int topLevelArraySize) const
794 {
795     // Nested arrays are processed starting from outermost (arrayNestingIndex 0u) and ending at the
796     // innermost.
797     unsigned int entryGenerationArraySize = field.getNestedArraySize(arrayNestingIndex);
798     if (singleEntryForTopLevelArray)
799     {
800         entryGenerationArraySize = 1;
801     }
802     for (unsigned int arrayElement = 0u; arrayElement < entryGenerationArraySize; ++arrayElement)
803     {
804         const std::string elementName       = prefix + ArrayString(arrayElement);
805         const std::string elementMappedName = mappedPrefix + ArrayString(arrayElement);
806         if (arrayNestingIndex + 1u < field.arraySizes.size())
807         {
808             defineArrayOfStructsBlockMembers(getMemberInfo, field, arrayNestingIndex + 1u,
809                                              elementName, elementMappedName, blockIndex, false,
810                                              topLevelArraySize);
811         }
812         else
813         {
814             defineBlockMembers(getMemberInfo, field.fields, elementName, elementMappedName,
815                                blockIndex, false, topLevelArraySize);
816         }
817     }
818 }
819 
820 template <typename VarT>
defineBlockMembers(const GetBlockMemberInfo & getMemberInfo,const std::vector<VarT> & fields,const std::string & prefix,const std::string & mappedPrefix,int blockIndex,bool singleEntryForTopLevelArray,int topLevelArraySize) const821 void InterfaceBlockLinker::defineBlockMembers(const GetBlockMemberInfo &getMemberInfo,
822                                               const std::vector<VarT> &fields,
823                                               const std::string &prefix,
824                                               const std::string &mappedPrefix,
825                                               int blockIndex,
826                                               bool singleEntryForTopLevelArray,
827                                               int topLevelArraySize) const
828 {
829     for (const VarT &field : fields)
830     {
831         std::string fullName = (prefix.empty() ? field.name : prefix + "." + field.name);
832         std::string fullMappedName =
833             (mappedPrefix.empty() ? field.mappedName : mappedPrefix + "." + field.mappedName);
834 
835         defineBlockMember(getMemberInfo, field, fullName, fullMappedName, blockIndex,
836                           singleEntryForTopLevelArray, topLevelArraySize);
837     }
838 }
839 
840 template <typename VarT>
defineBlockMember(const GetBlockMemberInfo & getMemberInfo,const VarT & field,const std::string & fullName,const std::string & fullMappedName,int blockIndex,bool singleEntryForTopLevelArray,int topLevelArraySize) const841 void InterfaceBlockLinker::defineBlockMember(const GetBlockMemberInfo &getMemberInfo,
842                                              const VarT &field,
843                                              const std::string &fullName,
844                                              const std::string &fullMappedName,
845                                              int blockIndex,
846                                              bool singleEntryForTopLevelArray,
847                                              int topLevelArraySize) const
848 {
849     int nextArraySize = topLevelArraySize;
850     if (((field.isArray() && field.isStruct()) || field.isArrayOfArrays()) &&
851         singleEntryForTopLevelArray)
852     {
853         // In OpenGL ES 3.10 spec, session 7.3.1.1 'For an active shader storage block
854         // member declared as an array of an aggregate type, an entry will be generated only
855         // for the first array element, regardless of its type.'
856         nextArraySize = field.getOutermostArraySize();
857     }
858 
859     if (field.isStruct())
860     {
861         if (field.isArray())
862         {
863             defineArrayOfStructsBlockMembers(getMemberInfo, field, 0u, fullName, fullMappedName,
864                                              blockIndex, singleEntryForTopLevelArray,
865                                              nextArraySize);
866         }
867         else
868         {
869             ASSERT(nextArraySize == topLevelArraySize);
870             defineBlockMembers(getMemberInfo, field.fields, fullName, fullMappedName, blockIndex,
871                                false, nextArraySize);
872         }
873         return;
874     }
875     if (field.isArrayOfArrays())
876     {
877         unsigned int entryGenerationArraySize = field.getOutermostArraySize();
878         if (singleEntryForTopLevelArray)
879         {
880             entryGenerationArraySize = 1u;
881         }
882         for (unsigned int arrayElement = 0u; arrayElement < entryGenerationArraySize;
883              ++arrayElement)
884         {
885             VarT fieldElement = field;
886             fieldElement.indexIntoArray(arrayElement);
887             const std::string elementName       = fullName + ArrayString(arrayElement);
888             const std::string elementMappedName = fullMappedName + ArrayString(arrayElement);
889 
890             defineBlockMember(getMemberInfo, fieldElement, elementName, elementMappedName,
891                               blockIndex, false, nextArraySize);
892         }
893         return;
894     }
895 
896     // If getBlockMemberInfo returns false, the variable is optimized out.
897     sh::BlockMemberInfo memberInfo;
898     if (!getMemberInfo(fullName, fullMappedName, &memberInfo))
899     {
900         return;
901     }
902 
903     std::string fullNameWithArrayIndex       = fullName;
904     std::string fullMappedNameWithArrayIndex = fullMappedName;
905 
906     if (field.isArray())
907     {
908         fullNameWithArrayIndex += "[0]";
909         fullMappedNameWithArrayIndex += "[0]";
910     }
911 
912     ASSERT(nextArraySize == topLevelArraySize);
913     defineBlockMemberImpl(field, fullNameWithArrayIndex, fullMappedNameWithArrayIndex, blockIndex,
914                           memberInfo, nextArraySize);
915 }
916 
defineInterfaceBlock(const GetBlockSize & getBlockSize,const GetBlockMemberInfo & getMemberInfo,const sh::InterfaceBlock & interfaceBlock,GLenum shaderType) const917 void InterfaceBlockLinker::defineInterfaceBlock(const GetBlockSize &getBlockSize,
918                                                 const GetBlockMemberInfo &getMemberInfo,
919                                                 const sh::InterfaceBlock &interfaceBlock,
920                                                 GLenum shaderType) const
921 {
922     size_t blockSize = 0;
923     std::vector<unsigned int> blockIndexes;
924 
925     int blockIndex = static_cast<int>(mBlocksOut->size());
926     // Track the first and last block member index to determine the range of active block members in
927     // the block.
928     size_t firstBlockMemberIndex = getCurrentBlockMemberIndex();
929     defineBlockMembers(getMemberInfo, interfaceBlock.fields, interfaceBlock.fieldPrefix(),
930                        interfaceBlock.fieldMappedPrefix(), blockIndex,
931                        interfaceBlock.blockType == sh::BlockType::BLOCK_BUFFER, 1);
932     size_t lastBlockMemberIndex = getCurrentBlockMemberIndex();
933 
934     for (size_t blockMemberIndex = firstBlockMemberIndex; blockMemberIndex < lastBlockMemberIndex;
935          ++blockMemberIndex)
936     {
937         blockIndexes.push_back(static_cast<unsigned int>(blockMemberIndex));
938     }
939 
940     // ESSL 3.10 section 4.4.4 page 58:
941     // Any uniform or shader storage block declared without a binding qualifier is initially
942     // assigned to block binding point zero.
943     int blockBinding = (interfaceBlock.binding == -1 ? 0 : interfaceBlock.binding);
944     for (unsigned int arrayElement = 0; arrayElement < interfaceBlock.elementCount();
945          ++arrayElement)
946     {
947         std::string blockArrayName       = interfaceBlock.name;
948         std::string blockMappedArrayName = interfaceBlock.mappedName;
949         if (interfaceBlock.isArray())
950         {
951             blockArrayName += ArrayString(arrayElement);
952             blockMappedArrayName += ArrayString(arrayElement);
953         }
954 
955         // Don't define this block at all if it's not active in the implementation.
956         if (!getBlockSize(blockArrayName, blockMappedArrayName, &blockSize))
957         {
958             continue;
959         }
960 
961         InterfaceBlock block(interfaceBlock.name, interfaceBlock.mappedName,
962                              interfaceBlock.isArray(), arrayElement, blockBinding + arrayElement);
963         block.memberIndexes = blockIndexes;
964         block.setStaticUse(shaderType, interfaceBlock.staticUse);
965 
966         // Since all block elements in an array share the same active interface blocks, they
967         // will all be active once any block member is used. So, since interfaceBlock.name[0]
968         // was active, here we will add every block element in the array.
969         block.dataSize = static_cast<unsigned int>(blockSize);
970         mBlocksOut->push_back(block);
971     }
972 }
973 
974 // UniformBlockLinker implementation.
UniformBlockLinker(std::vector<InterfaceBlock> * blocksOut,std::vector<LinkedUniform> * uniformsOut)975 UniformBlockLinker::UniformBlockLinker(std::vector<InterfaceBlock> *blocksOut,
976                                        std::vector<LinkedUniform> *uniformsOut)
977     : InterfaceBlockLinker(blocksOut), mUniformsOut(uniformsOut)
978 {
979 }
980 
~UniformBlockLinker()981 UniformBlockLinker::~UniformBlockLinker()
982 {
983 }
984 
defineBlockMemberImpl(const sh::ShaderVariable & field,const std::string & fullName,const std::string & fullMappedName,int blockIndex,const sh::BlockMemberInfo & memberInfo,int) const985 void UniformBlockLinker::defineBlockMemberImpl(const sh::ShaderVariable &field,
986                                                const std::string &fullName,
987                                                const std::string &fullMappedName,
988                                                int blockIndex,
989                                                const sh::BlockMemberInfo &memberInfo,
990                                                int /*topLevelArraySize*/) const
991 {
992     LinkedUniform newUniform(field.type, field.precision, fullName, field.arraySizes, -1, -1, -1,
993                              blockIndex, memberInfo);
994     newUniform.mappedName = fullMappedName;
995     // TODO(jiajia.qin@intel.com): update the block memeber static use.
996 
997     // Since block uniforms have no location, we don't need to store them in the uniform locations
998     // list.
999     mUniformsOut->push_back(newUniform);
1000 }
1001 
getCurrentBlockMemberIndex() const1002 size_t UniformBlockLinker::getCurrentBlockMemberIndex() const
1003 {
1004     return mUniformsOut->size();
1005 }
1006 
1007 // ShaderStorageBlockLinker implementation.
ShaderStorageBlockLinker(std::vector<InterfaceBlock> * blocksOut,std::vector<BufferVariable> * bufferVariablesOut)1008 ShaderStorageBlockLinker::ShaderStorageBlockLinker(std::vector<InterfaceBlock> *blocksOut,
1009                                                    std::vector<BufferVariable> *bufferVariablesOut)
1010     : InterfaceBlockLinker(blocksOut), mBufferVariablesOut(bufferVariablesOut)
1011 {
1012 }
1013 
~ShaderStorageBlockLinker()1014 ShaderStorageBlockLinker::~ShaderStorageBlockLinker()
1015 {
1016 }
1017 
defineBlockMemberImpl(const sh::ShaderVariable & field,const std::string & fullName,const std::string & fullMappedName,int blockIndex,const sh::BlockMemberInfo & memberInfo,int topLevelArraySize) const1018 void ShaderStorageBlockLinker::defineBlockMemberImpl(const sh::ShaderVariable &field,
1019                                                      const std::string &fullName,
1020                                                      const std::string &fullMappedName,
1021                                                      int blockIndex,
1022                                                      const sh::BlockMemberInfo &memberInfo,
1023                                                      int topLevelArraySize) const
1024 {
1025     BufferVariable newBufferVariable(field.type, field.precision, fullName, field.arraySizes,
1026                                      blockIndex, memberInfo);
1027     newBufferVariable.mappedName = fullMappedName;
1028     // TODO(jiajia.qin@intel.com): update the block memeber static use.
1029 
1030     newBufferVariable.topLevelArraySize = topLevelArraySize;
1031 
1032     mBufferVariablesOut->push_back(newBufferVariable);
1033 }
1034 
getCurrentBlockMemberIndex() const1035 size_t ShaderStorageBlockLinker::getCurrentBlockMemberIndex() const
1036 {
1037     return mBufferVariablesOut->size();
1038 }
1039 
1040 }  // namespace gl
1041