blob: 8b735c4c003082233c7fd9d8b66e835b9147a0f2 [file] [log] [blame]
Olli Etuahob78707c2017-03-09 15:03:11 +00001//
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/UniformLinker.h"
12
13#include "common/utilities.h"
14#include "libANGLE/Caps.h"
15#include "libANGLE/Shader.h"
Jiawei-Shaoddb5eb52017-03-14 13:36:18 +080016#include "libANGLE/features.h"
Olli Etuahob78707c2017-03-09 15:03:11 +000017
18namespace gl
19{
20
21namespace
22{
23
24LinkedUniform *FindUniform(std::vector<LinkedUniform> &list, const std::string &name)
25{
26 for (LinkedUniform &uniform : list)
27 {
28 if (uniform.name == name)
29 return &uniform;
30 }
31
32 return nullptr;
33}
34
35} // anonymouse namespace
36
37UniformLinker::UniformLinker(const ProgramState &state) : mState(state)
38{
39}
40
41void UniformLinker::getResults(std::vector<LinkedUniform> *uniforms,
42 std::vector<VariableLocation> *uniformLocations)
43{
44 uniforms->swap(mUniforms);
45 uniformLocations->swap(mUniformLocations);
46}
47
48bool UniformLinker::link(InfoLog &infoLog,
49 const Caps &caps,
50 const Program::Bindings &uniformLocationBindings)
51{
52 if (mState.getAttachedVertexShader() && mState.getAttachedFragmentShader())
53 {
54 ASSERT(mState.getAttachedComputeShader() == nullptr);
55 if (!validateVertexAndFragmentUniforms(infoLog))
56 {
57 return false;
58 }
59 }
60
61 // Flatten the uniforms list (nested fields) into a simple list (no nesting).
62 // Also check the maximum uniform vector and sampler counts.
63 if (!flattenUniformsAndCheckCaps(caps, infoLog))
64 {
65 return false;
66 }
67
68 if (!indexUniforms(infoLog, uniformLocationBindings))
69 {
70 return false;
71 }
72
73 return true;
74}
75
76bool UniformLinker::validateVertexAndFragmentUniforms(InfoLog &infoLog) const
77{
78 // Check that uniforms defined in the vertex and fragment shaders are identical
79 std::map<std::string, LinkedUniform> linkedUniforms;
80 const std::vector<sh::Uniform> &vertexUniforms =
81 mState.getAttachedVertexShader()->getUniforms();
82 const std::vector<sh::Uniform> &fragmentUniforms =
83 mState.getAttachedFragmentShader()->getUniforms();
84
85 for (const sh::Uniform &vertexUniform : vertexUniforms)
86 {
87 linkedUniforms[vertexUniform.name] = LinkedUniform(vertexUniform);
88 }
89
90 for (const sh::Uniform &fragmentUniform : fragmentUniforms)
91 {
92 auto entry = linkedUniforms.find(fragmentUniform.name);
93 if (entry != linkedUniforms.end())
94 {
95 LinkedUniform *linkedUniform = &entry->second;
96 const std::string &uniformName = "uniform '" + linkedUniform->name + "'";
97 if (!linkValidateUniforms(infoLog, uniformName, *linkedUniform, fragmentUniform))
98 {
99 return false;
100 }
101 }
102 }
103 return true;
104}
105
106// GLSL ES Spec 3.00.3, section 4.3.5.
107bool UniformLinker::linkValidateUniforms(InfoLog &infoLog,
108 const std::string &uniformName,
109 const sh::Uniform &vertexUniform,
110 const sh::Uniform &fragmentUniform)
111{
112#if ANGLE_PROGRAM_LINK_VALIDATE_UNIFORM_PRECISION == ANGLE_ENABLED
113 const bool validatePrecision = true;
114#else
115 const bool validatePrecision = false;
116#endif
117
118 if (!Program::linkValidateVariablesBase(infoLog, uniformName, vertexUniform, fragmentUniform,
119 validatePrecision))
120 {
121 return false;
122 }
123
124 // GLSL ES Spec 3.10.4, section 4.4.5.
125 if (vertexUniform.binding != -1 && fragmentUniform.binding != -1 &&
126 vertexUniform.binding != fragmentUniform.binding)
127 {
128 infoLog << "Binding layout qualifiers for " << uniformName
129 << " differ between vertex and fragment shaders.";
130 return false;
131 }
132
133 // GLSL ES Spec 3.10.4, section 9.2.1.
134 if (vertexUniform.location != -1 && fragmentUniform.location != -1 &&
135 vertexUniform.location != fragmentUniform.location)
136 {
137 infoLog << "Location layout qualifiers for " << uniformName
138 << " differ between vertex and fragment shaders.";
139 return false;
140 }
141
142 return true;
143}
144
145bool UniformLinker::indexUniforms(InfoLog &infoLog,
146 const Program::Bindings &uniformLocationBindings)
147{
148 // All the locations where another uniform can't be located.
149 std::set<GLuint> reservedLocations;
150 // Locations which have been allocated for an unused uniform.
151 std::set<GLuint> ignoredLocations;
152
153 int maxUniformLocation = -1;
154
155 // Gather uniform locations that have been set either using the bindUniformLocation API or by
156 // using a location layout qualifier and check conflicts between them.
157 if (!gatherUniformLocationsAndCheckConflicts(infoLog, uniformLocationBindings,
158 &reservedLocations, &ignoredLocations,
159 &maxUniformLocation))
160 {
161 return false;
162 }
163
164 // Conflicts have been checked, now we can prune non-statically used uniforms. Code further down
165 // the line relies on only having statically used uniforms in mUniforms.
166 pruneUnusedUniforms();
167
168 // Gather uniforms that have their location pre-set and uniforms that don't yet have a location.
169 std::vector<VariableLocation> unlocatedUniforms;
170 std::map<GLuint, VariableLocation> preLocatedUniforms;
171
172 for (size_t uniformIndex = 0; uniformIndex < mUniforms.size(); uniformIndex++)
173 {
174 const LinkedUniform &uniform = mUniforms[uniformIndex];
175
176 if (uniform.isBuiltIn())
177 {
178 continue;
179 }
180
181 int preSetLocation = uniformLocationBindings.getBinding(uniform.name);
182 int shaderLocation = uniform.location;
183
184 if (shaderLocation != -1)
185 {
186 preSetLocation = shaderLocation;
187 }
188
189 for (unsigned int arrayIndex = 0; arrayIndex < uniform.elementCount(); arrayIndex++)
190 {
191 VariableLocation location(uniform.name, arrayIndex,
192 static_cast<unsigned int>(uniformIndex));
193
194 if ((arrayIndex == 0 && preSetLocation != -1) || shaderLocation != -1)
195 {
196 int elementLocation = preSetLocation + arrayIndex;
197 preLocatedUniforms[elementLocation] = location;
198 }
199 else
200 {
201 unlocatedUniforms.push_back(location);
202 }
203 }
204 }
205
206 // Make enough space for all uniforms, with pre-set locations or not.
207 mUniformLocations.resize(
208 std::max(unlocatedUniforms.size() + preLocatedUniforms.size() + ignoredLocations.size(),
209 static_cast<size_t>(maxUniformLocation + 1)));
210
211 // Assign uniforms with pre-set locations
212 for (const auto &uniform : preLocatedUniforms)
213 {
214 mUniformLocations[uniform.first] = uniform.second;
215 }
216
217 // Assign ignored uniforms
218 for (const auto &ignoredLocation : ignoredLocations)
219 {
220 mUniformLocations[ignoredLocation].ignored = true;
221 }
222
223 // Automatically assign locations for the rest of the uniforms
224 size_t nextUniformLocation = 0;
225 for (const auto &unlocatedUniform : unlocatedUniforms)
226 {
227 while (mUniformLocations[nextUniformLocation].used ||
228 mUniformLocations[nextUniformLocation].ignored)
229 {
230 nextUniformLocation++;
231 }
232
233 ASSERT(nextUniformLocation < mUniformLocations.size());
234 mUniformLocations[nextUniformLocation] = unlocatedUniform;
235 nextUniformLocation++;
236 }
237
238 return true;
239}
240
241bool UniformLinker::gatherUniformLocationsAndCheckConflicts(
242 InfoLog &infoLog,
243 const Program::Bindings &uniformLocationBindings,
244 std::set<GLuint> *reservedLocations,
245 std::set<GLuint> *ignoredLocations,
246 int *maxUniformLocation)
247{
248 for (const LinkedUniform &uniform : mUniforms)
249 {
250 if (uniform.isBuiltIn())
251 {
252 continue;
253 }
254
255 int apiBoundLocation = uniformLocationBindings.getBinding(uniform.name);
256 int shaderLocation = uniform.location;
257
258 if (shaderLocation != -1)
259 {
260 for (unsigned int arrayIndex = 0; arrayIndex < uniform.elementCount(); arrayIndex++)
261 {
262 // GLSL ES 3.10 section 4.4.3
263 int elementLocation = shaderLocation + arrayIndex;
264 *maxUniformLocation = std::max(*maxUniformLocation, elementLocation);
265 if (reservedLocations->find(elementLocation) != reservedLocations->end())
266 {
267 infoLog << "Multiple uniforms bound to location " << elementLocation << ".";
268 return false;
269 }
270 reservedLocations->insert(elementLocation);
271 if (!uniform.staticUse)
272 {
273 ignoredLocations->insert(elementLocation);
274 }
275 }
276 }
277 else if (apiBoundLocation != -1 && uniform.staticUse)
278 {
279 // Only the first location is reserved even if the uniform is an array.
280 *maxUniformLocation = std::max(*maxUniformLocation, apiBoundLocation);
281 if (reservedLocations->find(apiBoundLocation) != reservedLocations->end())
282 {
283 infoLog << "Multiple uniforms bound to location " << apiBoundLocation << ".";
284 return false;
285 }
286 reservedLocations->insert(apiBoundLocation);
287 }
288 }
289
290 // Record the uniform locations that were bound using the API for uniforms that were not found
291 // from the shader. Other uniforms should not be assigned to those locations.
292 for (const auto &locationBinding : uniformLocationBindings)
293 {
294 GLuint location = locationBinding.second;
295 if (reservedLocations->find(location) == reservedLocations->end())
296 {
297 ignoredLocations->insert(location);
298 *maxUniformLocation = std::max(*maxUniformLocation, static_cast<int>(location));
299 }
300 }
301
302 return true;
303}
304
305void UniformLinker::pruneUnusedUniforms()
306{
307 auto uniformIter = mUniforms.begin();
308 while (uniformIter != mUniforms.end())
309 {
310 if (uniformIter->staticUse)
311 {
312 ++uniformIter;
313 }
314 else
315 {
316 uniformIter = mUniforms.erase(uniformIter);
317 }
318 }
319}
320
321bool UniformLinker::flattenUniformsAndCheckCapsForShader(
322 const Shader &shader,
323 GLuint maxUniformComponents,
324 GLuint maxTextureImageUnits,
325 const std::string &componentsErrorMessage,
326 const std::string &samplerErrorMessage,
327 std::vector<LinkedUniform> &samplerUniforms,
328 InfoLog &infoLog)
329{
330 VectorAndSamplerCount vasCount;
331 for (const sh::Uniform &uniform : shader.getUniforms())
332 {
333 vasCount += flattenUniform(uniform, &samplerUniforms);
334 }
335
336 if (vasCount.vectorCount > maxUniformComponents)
337 {
338 infoLog << componentsErrorMessage << maxUniformComponents << ").";
339 return false;
340 }
341
342 if (vasCount.samplerCount > maxTextureImageUnits)
343 {
344 infoLog << samplerErrorMessage << maxTextureImageUnits << ").";
345 return false;
346 }
347
348 return true;
349}
350
351bool UniformLinker::flattenUniformsAndCheckCaps(const Caps &caps, InfoLog &infoLog)
352{
353 std::vector<LinkedUniform> samplerUniforms;
354
355 if (mState.getAttachedComputeShader())
356 {
357 const Shader *computeShader = mState.getAttachedComputeShader();
358
359 // TODO (mradev): check whether we need finer-grained component counting
360 if (!flattenUniformsAndCheckCapsForShader(
361 *computeShader, caps.maxComputeUniformComponents / 4,
362 caps.maxComputeTextureImageUnits,
363 "Compute shader active uniforms exceed MAX_COMPUTE_UNIFORM_COMPONENTS (",
364 "Compute shader sampler count exceeds MAX_COMPUTE_TEXTURE_IMAGE_UNITS (",
365 samplerUniforms, infoLog))
366 {
367 return false;
368 }
369 }
370 else
371 {
372 const Shader *vertexShader = mState.getAttachedVertexShader();
373
374 if (!flattenUniformsAndCheckCapsForShader(
375 *vertexShader, caps.maxVertexUniformVectors, caps.maxVertexTextureImageUnits,
376 "Vertex shader active uniforms exceed MAX_VERTEX_UNIFORM_VECTORS (",
377 "Vertex shader sampler count exceeds MAX_VERTEX_TEXTURE_IMAGE_UNITS (",
378 samplerUniforms, infoLog))
379 {
380 return false;
381 }
382 const Shader *fragmentShader = mState.getAttachedFragmentShader();
383
384 if (!flattenUniformsAndCheckCapsForShader(
385 *fragmentShader, caps.maxFragmentUniformVectors, caps.maxTextureImageUnits,
386 "Fragment shader active uniforms exceed MAX_FRAGMENT_UNIFORM_VECTORS (",
387 "Fragment shader sampler count exceeds MAX_TEXTURE_IMAGE_UNITS (", samplerUniforms,
388 infoLog))
389 {
390 return false;
391 }
392 }
393
394 mUniforms.insert(mUniforms.end(), samplerUniforms.begin(), samplerUniforms.end());
395 return true;
396}
397
398UniformLinker::VectorAndSamplerCount UniformLinker::flattenUniform(
399 const sh::Uniform &uniform,
400 std::vector<LinkedUniform> *samplerUniforms)
401{
402 int location = uniform.location;
403 VectorAndSamplerCount uniformVasCount = flattenUniformImpl(
404 uniform, uniform.name, samplerUniforms, uniform.staticUse, uniform.binding, &location);
405 if (uniform.staticUse)
406 {
407 return uniformVasCount;
408 }
409 return VectorAndSamplerCount();
410}
411
412UniformLinker::VectorAndSamplerCount UniformLinker::flattenUniformImpl(
413 const sh::ShaderVariable &uniform,
414 const std::string &fullName,
415 std::vector<LinkedUniform> *samplerUniforms,
416 bool markStaticUse,
417 int binding,
418 int *location)
419{
420 ASSERT(location);
421 VectorAndSamplerCount vectorAndSamplerCount;
422
423 if (uniform.isStruct())
424 {
425 for (unsigned int elementIndex = 0; elementIndex < uniform.elementCount(); elementIndex++)
426 {
427 const std::string &elementString = (uniform.isArray() ? ArrayString(elementIndex) : "");
428
429 for (size_t fieldIndex = 0; fieldIndex < uniform.fields.size(); fieldIndex++)
430 {
431 const sh::ShaderVariable &field = uniform.fields[fieldIndex];
432 const std::string &fieldFullName = (fullName + elementString + "." + field.name);
433
434 vectorAndSamplerCount += flattenUniformImpl(field, fieldFullName, samplerUniforms,
435 markStaticUse, -1, location);
436 }
437 }
438
439 return vectorAndSamplerCount;
440 }
441
442 // Not a struct
443 bool isSampler = IsSamplerType(uniform.type);
444 std::vector<gl::LinkedUniform> *uniformList = &mUniforms;
445 if (isSampler)
446 {
447 // Store sampler uniforms separately, so we'll append them to the end of the list.
448 uniformList = samplerUniforms;
449 }
450 LinkedUniform *existingUniform = FindUniform(*uniformList, fullName);
451 if (existingUniform)
452 {
453 if (binding != -1)
454 {
455 existingUniform->binding = binding;
456 }
457 if (*location != -1)
458 {
459 existingUniform->location = *location;
460 }
461 if (markStaticUse)
462 {
463 existingUniform->staticUse = true;
464 }
465 }
466 else
467 {
468 LinkedUniform linkedUniform(uniform.type, uniform.precision, fullName, uniform.arraySize,
469 binding, *location, -1,
470 sh::BlockMemberInfo::getDefaultBlockInfo());
471 linkedUniform.staticUse = markStaticUse;
472 uniformList->push_back(linkedUniform);
473 }
474
475 unsigned int elementCount = uniform.elementCount();
476
477 // Samplers aren't "real" uniforms, so they don't count towards register usage.
478 // Likewise, don't count "real" uniforms towards sampler count.
479 vectorAndSamplerCount.vectorCount =
480 (isSampler ? 0 : (VariableRegisterCount(uniform.type) * elementCount));
481 vectorAndSamplerCount.samplerCount = (isSampler ? elementCount : 0);
482
483 if (*location != -1)
484 {
485 *location += elementCount;
486 }
487
488 return vectorAndSamplerCount;
489}
490
491} // namespace gl