blob: 1d7015448de4219747014127b212a72f642aabfe [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"
Jamie Madillbd044ed2017-06-05 12:59:21 -040015#include "libANGLE/Context.h"
Olli Etuahob78707c2017-03-09 15:03:11 +000016#include "libANGLE/Shader.h"
Jiawei-Shaoddb5eb52017-03-14 13:36:18 +080017#include "libANGLE/features.h"
Olli Etuahob78707c2017-03-09 15:03:11 +000018
19namespace gl
20{
21
22namespace
23{
24
25LinkedUniform *FindUniform(std::vector<LinkedUniform> &list, const std::string &name)
26{
27 for (LinkedUniform &uniform : list)
28 {
29 if (uniform.name == name)
30 return &uniform;
31 }
32
33 return nullptr;
34}
35
36} // anonymouse namespace
37
38UniformLinker::UniformLinker(const ProgramState &state) : mState(state)
39{
40}
41
42void UniformLinker::getResults(std::vector<LinkedUniform> *uniforms,
43 std::vector<VariableLocation> *uniformLocations)
44{
45 uniforms->swap(mUniforms);
46 uniformLocations->swap(mUniformLocations);
47}
48
Jamie Madillbd044ed2017-06-05 12:59:21 -040049bool UniformLinker::link(const Context *context,
50 InfoLog &infoLog,
Olli Etuahob78707c2017-03-09 15:03:11 +000051 const Program::Bindings &uniformLocationBindings)
52{
53 if (mState.getAttachedVertexShader() && mState.getAttachedFragmentShader())
54 {
55 ASSERT(mState.getAttachedComputeShader() == nullptr);
Jamie Madillbd044ed2017-06-05 12:59:21 -040056 if (!validateVertexAndFragmentUniforms(context, infoLog))
Olli Etuahob78707c2017-03-09 15:03:11 +000057 {
58 return false;
59 }
60 }
61
62 // Flatten the uniforms list (nested fields) into a simple list (no nesting).
63 // Also check the maximum uniform vector and sampler counts.
Jamie Madillbd044ed2017-06-05 12:59:21 -040064 if (!flattenUniformsAndCheckCaps(context, infoLog))
Olli Etuahob78707c2017-03-09 15:03:11 +000065 {
66 return false;
67 }
68
69 if (!indexUniforms(infoLog, uniformLocationBindings))
70 {
71 return false;
72 }
73
74 return true;
75}
76
Jamie Madillbd044ed2017-06-05 12:59:21 -040077bool UniformLinker::validateVertexAndFragmentUniforms(const Context *context,
78 InfoLog &infoLog) const
Olli Etuahob78707c2017-03-09 15:03:11 +000079{
80 // Check that uniforms defined in the vertex and fragment shaders are identical
81 std::map<std::string, LinkedUniform> linkedUniforms;
82 const std::vector<sh::Uniform> &vertexUniforms =
Jamie Madillbd044ed2017-06-05 12:59:21 -040083 mState.getAttachedVertexShader()->getUniforms(context);
Olli Etuahob78707c2017-03-09 15:03:11 +000084 const std::vector<sh::Uniform> &fragmentUniforms =
Jamie Madillbd044ed2017-06-05 12:59:21 -040085 mState.getAttachedFragmentShader()->getUniforms(context);
Olli Etuahob78707c2017-03-09 15:03:11 +000086
87 for (const sh::Uniform &vertexUniform : vertexUniforms)
88 {
89 linkedUniforms[vertexUniform.name] = LinkedUniform(vertexUniform);
90 }
91
92 for (const sh::Uniform &fragmentUniform : fragmentUniforms)
93 {
94 auto entry = linkedUniforms.find(fragmentUniform.name);
95 if (entry != linkedUniforms.end())
96 {
97 LinkedUniform *linkedUniform = &entry->second;
98 const std::string &uniformName = "uniform '" + linkedUniform->name + "'";
99 if (!linkValidateUniforms(infoLog, uniformName, *linkedUniform, fragmentUniform))
100 {
101 return false;
102 }
103 }
104 }
105 return true;
106}
107
108// GLSL ES Spec 3.00.3, section 4.3.5.
109bool UniformLinker::linkValidateUniforms(InfoLog &infoLog,
110 const std::string &uniformName,
111 const sh::Uniform &vertexUniform,
112 const sh::Uniform &fragmentUniform)
113{
114#if ANGLE_PROGRAM_LINK_VALIDATE_UNIFORM_PRECISION == ANGLE_ENABLED
115 const bool validatePrecision = true;
116#else
117 const bool validatePrecision = false;
118#endif
119
120 if (!Program::linkValidateVariablesBase(infoLog, uniformName, vertexUniform, fragmentUniform,
121 validatePrecision))
122 {
123 return false;
124 }
125
126 // GLSL ES Spec 3.10.4, section 4.4.5.
127 if (vertexUniform.binding != -1 && fragmentUniform.binding != -1 &&
128 vertexUniform.binding != fragmentUniform.binding)
129 {
130 infoLog << "Binding layout qualifiers for " << uniformName
131 << " differ between vertex and fragment shaders.";
132 return false;
133 }
134
135 // GLSL ES Spec 3.10.4, section 9.2.1.
136 if (vertexUniform.location != -1 && fragmentUniform.location != -1 &&
137 vertexUniform.location != fragmentUniform.location)
138 {
139 infoLog << "Location layout qualifiers for " << uniformName
140 << " differ between vertex and fragment shaders.";
141 return false;
142 }
143
144 return true;
145}
146
147bool UniformLinker::indexUniforms(InfoLog &infoLog,
148 const Program::Bindings &uniformLocationBindings)
149{
150 // All the locations where another uniform can't be located.
151 std::set<GLuint> reservedLocations;
152 // Locations which have been allocated for an unused uniform.
153 std::set<GLuint> ignoredLocations;
154
155 int maxUniformLocation = -1;
156
157 // Gather uniform locations that have been set either using the bindUniformLocation API or by
158 // using a location layout qualifier and check conflicts between them.
159 if (!gatherUniformLocationsAndCheckConflicts(infoLog, uniformLocationBindings,
160 &reservedLocations, &ignoredLocations,
161 &maxUniformLocation))
162 {
163 return false;
164 }
165
166 // Conflicts have been checked, now we can prune non-statically used uniforms. Code further down
167 // the line relies on only having statically used uniforms in mUniforms.
168 pruneUnusedUniforms();
169
170 // Gather uniforms that have their location pre-set and uniforms that don't yet have a location.
171 std::vector<VariableLocation> unlocatedUniforms;
172 std::map<GLuint, VariableLocation> preLocatedUniforms;
173
174 for (size_t uniformIndex = 0; uniformIndex < mUniforms.size(); uniformIndex++)
175 {
176 const LinkedUniform &uniform = mUniforms[uniformIndex];
177
178 if (uniform.isBuiltIn())
179 {
180 continue;
181 }
182
183 int preSetLocation = uniformLocationBindings.getBinding(uniform.name);
184 int shaderLocation = uniform.location;
185
186 if (shaderLocation != -1)
187 {
188 preSetLocation = shaderLocation;
189 }
190
191 for (unsigned int arrayIndex = 0; arrayIndex < uniform.elementCount(); arrayIndex++)
192 {
193 VariableLocation location(uniform.name, arrayIndex,
194 static_cast<unsigned int>(uniformIndex));
195
196 if ((arrayIndex == 0 && preSetLocation != -1) || shaderLocation != -1)
197 {
198 int elementLocation = preSetLocation + arrayIndex;
199 preLocatedUniforms[elementLocation] = location;
200 }
201 else
202 {
203 unlocatedUniforms.push_back(location);
204 }
205 }
206 }
207
208 // Make enough space for all uniforms, with pre-set locations or not.
209 mUniformLocations.resize(
210 std::max(unlocatedUniforms.size() + preLocatedUniforms.size() + ignoredLocations.size(),
211 static_cast<size_t>(maxUniformLocation + 1)));
212
213 // Assign uniforms with pre-set locations
214 for (const auto &uniform : preLocatedUniforms)
215 {
216 mUniformLocations[uniform.first] = uniform.second;
217 }
218
219 // Assign ignored uniforms
220 for (const auto &ignoredLocation : ignoredLocations)
221 {
222 mUniformLocations[ignoredLocation].ignored = true;
223 }
224
225 // Automatically assign locations for the rest of the uniforms
226 size_t nextUniformLocation = 0;
227 for (const auto &unlocatedUniform : unlocatedUniforms)
228 {
229 while (mUniformLocations[nextUniformLocation].used ||
230 mUniformLocations[nextUniformLocation].ignored)
231 {
232 nextUniformLocation++;
233 }
234
235 ASSERT(nextUniformLocation < mUniformLocations.size());
236 mUniformLocations[nextUniformLocation] = unlocatedUniform;
237 nextUniformLocation++;
238 }
239
240 return true;
241}
242
243bool UniformLinker::gatherUniformLocationsAndCheckConflicts(
244 InfoLog &infoLog,
245 const Program::Bindings &uniformLocationBindings,
246 std::set<GLuint> *reservedLocations,
247 std::set<GLuint> *ignoredLocations,
248 int *maxUniformLocation)
249{
250 for (const LinkedUniform &uniform : mUniforms)
251 {
252 if (uniform.isBuiltIn())
253 {
254 continue;
255 }
256
257 int apiBoundLocation = uniformLocationBindings.getBinding(uniform.name);
258 int shaderLocation = uniform.location;
259
260 if (shaderLocation != -1)
261 {
262 for (unsigned int arrayIndex = 0; arrayIndex < uniform.elementCount(); arrayIndex++)
263 {
264 // GLSL ES 3.10 section 4.4.3
265 int elementLocation = shaderLocation + arrayIndex;
266 *maxUniformLocation = std::max(*maxUniformLocation, elementLocation);
267 if (reservedLocations->find(elementLocation) != reservedLocations->end())
268 {
269 infoLog << "Multiple uniforms bound to location " << elementLocation << ".";
270 return false;
271 }
272 reservedLocations->insert(elementLocation);
273 if (!uniform.staticUse)
274 {
275 ignoredLocations->insert(elementLocation);
276 }
277 }
278 }
279 else if (apiBoundLocation != -1 && uniform.staticUse)
280 {
281 // Only the first location is reserved even if the uniform is an array.
282 *maxUniformLocation = std::max(*maxUniformLocation, apiBoundLocation);
283 if (reservedLocations->find(apiBoundLocation) != reservedLocations->end())
284 {
285 infoLog << "Multiple uniforms bound to location " << apiBoundLocation << ".";
286 return false;
287 }
288 reservedLocations->insert(apiBoundLocation);
289 }
290 }
291
292 // Record the uniform locations that were bound using the API for uniforms that were not found
293 // from the shader. Other uniforms should not be assigned to those locations.
294 for (const auto &locationBinding : uniformLocationBindings)
295 {
296 GLuint location = locationBinding.second;
297 if (reservedLocations->find(location) == reservedLocations->end())
298 {
299 ignoredLocations->insert(location);
300 *maxUniformLocation = std::max(*maxUniformLocation, static_cast<int>(location));
301 }
302 }
303
304 return true;
305}
306
307void UniformLinker::pruneUnusedUniforms()
308{
309 auto uniformIter = mUniforms.begin();
310 while (uniformIter != mUniforms.end())
311 {
312 if (uniformIter->staticUse)
313 {
314 ++uniformIter;
315 }
316 else
317 {
318 uniformIter = mUniforms.erase(uniformIter);
319 }
320 }
321}
322
323bool UniformLinker::flattenUniformsAndCheckCapsForShader(
Jamie Madillbd044ed2017-06-05 12:59:21 -0400324 const Context *context,
325 Shader *shader,
Olli Etuahob78707c2017-03-09 15:03:11 +0000326 GLuint maxUniformComponents,
327 GLuint maxTextureImageUnits,
Xinghua Cao65ec0b22017-03-28 16:10:52 +0800328 GLuint maxImageUnits,
Olli Etuahob78707c2017-03-09 15:03:11 +0000329 const std::string &componentsErrorMessage,
330 const std::string &samplerErrorMessage,
Xinghua Cao65ec0b22017-03-28 16:10:52 +0800331 const std::string &imageErrorMessage,
Olli Etuahob78707c2017-03-09 15:03:11 +0000332 std::vector<LinkedUniform> &samplerUniforms,
Xinghua Cao65ec0b22017-03-28 16:10:52 +0800333 std::vector<LinkedUniform> &imageUniforms,
Olli Etuahob78707c2017-03-09 15:03:11 +0000334 InfoLog &infoLog)
335{
Xinghua Cao65ec0b22017-03-28 16:10:52 +0800336 ShaderUniformCount shaderUniformCount;
Jamie Madillbd044ed2017-06-05 12:59:21 -0400337 for (const sh::Uniform &uniform : shader->getUniforms(context))
Olli Etuahob78707c2017-03-09 15:03:11 +0000338 {
Xinghua Cao65ec0b22017-03-28 16:10:52 +0800339 shaderUniformCount += flattenUniform(uniform, &samplerUniforms, &imageUniforms);
Olli Etuahob78707c2017-03-09 15:03:11 +0000340 }
341
Xinghua Cao65ec0b22017-03-28 16:10:52 +0800342 if (shaderUniformCount.vectorCount > maxUniformComponents)
Olli Etuahob78707c2017-03-09 15:03:11 +0000343 {
344 infoLog << componentsErrorMessage << maxUniformComponents << ").";
345 return false;
346 }
347
Xinghua Cao65ec0b22017-03-28 16:10:52 +0800348 if (shaderUniformCount.samplerCount > maxTextureImageUnits)
Olli Etuahob78707c2017-03-09 15:03:11 +0000349 {
350 infoLog << samplerErrorMessage << maxTextureImageUnits << ").";
351 return false;
352 }
353
Xinghua Cao65ec0b22017-03-28 16:10:52 +0800354 if (shaderUniformCount.imageCount > maxImageUnits)
355 {
356 infoLog << imageErrorMessage << maxImageUnits << ").";
357 return false;
358 }
359
Olli Etuahob78707c2017-03-09 15:03:11 +0000360 return true;
361}
362
Jamie Madillbd044ed2017-06-05 12:59:21 -0400363bool UniformLinker::flattenUniformsAndCheckCaps(const Context *context, InfoLog &infoLog)
Olli Etuahob78707c2017-03-09 15:03:11 +0000364{
365 std::vector<LinkedUniform> samplerUniforms;
Xinghua Cao65ec0b22017-03-28 16:10:52 +0800366 std::vector<LinkedUniform> imageUniforms;
Olli Etuahob78707c2017-03-09 15:03:11 +0000367
Jamie Madillbd044ed2017-06-05 12:59:21 -0400368 const Caps &caps = context->getCaps();
369
Olli Etuahob78707c2017-03-09 15:03:11 +0000370 if (mState.getAttachedComputeShader())
371 {
Jamie Madillbd044ed2017-06-05 12:59:21 -0400372 Shader *computeShader = mState.getAttachedComputeShader();
Olli Etuahob78707c2017-03-09 15:03:11 +0000373
374 // TODO (mradev): check whether we need finer-grained component counting
375 if (!flattenUniformsAndCheckCapsForShader(
Jamie Madillbd044ed2017-06-05 12:59:21 -0400376 context, computeShader, caps.maxComputeUniformComponents / 4,
Xinghua Cao65ec0b22017-03-28 16:10:52 +0800377 caps.maxComputeTextureImageUnits, caps.maxComputeImageUniforms,
Olli Etuahob78707c2017-03-09 15:03:11 +0000378 "Compute shader active uniforms exceed MAX_COMPUTE_UNIFORM_COMPONENTS (",
379 "Compute shader sampler count exceeds MAX_COMPUTE_TEXTURE_IMAGE_UNITS (",
Xinghua Cao65ec0b22017-03-28 16:10:52 +0800380 "Compute shader image count exceeds MAX_COMPUTE_IMAGE_UNIFORMS (", samplerUniforms,
381 imageUniforms, infoLog))
Olli Etuahob78707c2017-03-09 15:03:11 +0000382 {
383 return false;
384 }
385 }
386 else
387 {
Jamie Madillbd044ed2017-06-05 12:59:21 -0400388 Shader *vertexShader = mState.getAttachedVertexShader();
Olli Etuahob78707c2017-03-09 15:03:11 +0000389
390 if (!flattenUniformsAndCheckCapsForShader(
Jamie Madillbd044ed2017-06-05 12:59:21 -0400391 context, vertexShader, caps.maxVertexUniformVectors,
Xinghua Cao65ec0b22017-03-28 16:10:52 +0800392 caps.maxVertexTextureImageUnits, caps.maxVertexImageUniforms,
Olli Etuahob78707c2017-03-09 15:03:11 +0000393 "Vertex shader active uniforms exceed MAX_VERTEX_UNIFORM_VECTORS (",
394 "Vertex shader sampler count exceeds MAX_VERTEX_TEXTURE_IMAGE_UNITS (",
Xinghua Cao65ec0b22017-03-28 16:10:52 +0800395 "Vertex shader image count exceeds MAX_VERTEX_IMAGE_UNIFORMS (", samplerUniforms,
396 imageUniforms, infoLog))
Olli Etuahob78707c2017-03-09 15:03:11 +0000397 {
398 return false;
399 }
Jamie Madillbd044ed2017-06-05 12:59:21 -0400400
401 Shader *fragmentShader = mState.getAttachedFragmentShader();
Olli Etuahob78707c2017-03-09 15:03:11 +0000402
403 if (!flattenUniformsAndCheckCapsForShader(
Jamie Madillbd044ed2017-06-05 12:59:21 -0400404 context, fragmentShader, caps.maxFragmentUniformVectors, caps.maxTextureImageUnits,
Xinghua Cao65ec0b22017-03-28 16:10:52 +0800405 caps.maxFragmentImageUniforms,
Olli Etuahob78707c2017-03-09 15:03:11 +0000406 "Fragment shader active uniforms exceed MAX_FRAGMENT_UNIFORM_VECTORS (",
Xinghua Cao65ec0b22017-03-28 16:10:52 +0800407 "Fragment shader sampler count exceeds MAX_TEXTURE_IMAGE_UNITS (",
408 "Fragment shader image count exceeds MAX_FRAGMENT_IMAGE_UNIFORMS (",
409 samplerUniforms, imageUniforms, infoLog))
Olli Etuahob78707c2017-03-09 15:03:11 +0000410 {
411 return false;
412 }
413 }
414
415 mUniforms.insert(mUniforms.end(), samplerUniforms.begin(), samplerUniforms.end());
Xinghua Cao65ec0b22017-03-28 16:10:52 +0800416 mUniforms.insert(mUniforms.end(), imageUniforms.begin(), imageUniforms.end());
Olli Etuahob78707c2017-03-09 15:03:11 +0000417 return true;
418}
419
Xinghua Cao65ec0b22017-03-28 16:10:52 +0800420UniformLinker::ShaderUniformCount UniformLinker::flattenUniform(
Olli Etuahob78707c2017-03-09 15:03:11 +0000421 const sh::Uniform &uniform,
Xinghua Cao65ec0b22017-03-28 16:10:52 +0800422 std::vector<LinkedUniform> *samplerUniforms,
423 std::vector<LinkedUniform> *imageUniforms)
Olli Etuahob78707c2017-03-09 15:03:11 +0000424{
425 int location = uniform.location;
Xinghua Cao65ec0b22017-03-28 16:10:52 +0800426 ShaderUniformCount shaderUniformCount =
427 flattenUniformImpl(uniform, uniform.name, samplerUniforms, imageUniforms, uniform.staticUse,
428 uniform.binding, &location);
Olli Etuahob78707c2017-03-09 15:03:11 +0000429 if (uniform.staticUse)
430 {
Xinghua Cao65ec0b22017-03-28 16:10:52 +0800431 return shaderUniformCount;
Olli Etuahob78707c2017-03-09 15:03:11 +0000432 }
Xinghua Cao65ec0b22017-03-28 16:10:52 +0800433 return ShaderUniformCount();
Olli Etuahob78707c2017-03-09 15:03:11 +0000434}
435
Xinghua Cao65ec0b22017-03-28 16:10:52 +0800436UniformLinker::ShaderUniformCount UniformLinker::flattenUniformImpl(
Olli Etuahob78707c2017-03-09 15:03:11 +0000437 const sh::ShaderVariable &uniform,
438 const std::string &fullName,
439 std::vector<LinkedUniform> *samplerUniforms,
Xinghua Cao65ec0b22017-03-28 16:10:52 +0800440 std::vector<LinkedUniform> *imageUniforms,
Olli Etuahob78707c2017-03-09 15:03:11 +0000441 bool markStaticUse,
442 int binding,
443 int *location)
444{
445 ASSERT(location);
Xinghua Cao65ec0b22017-03-28 16:10:52 +0800446 ShaderUniformCount shaderUniformCount;
Olli Etuahob78707c2017-03-09 15:03:11 +0000447
448 if (uniform.isStruct())
449 {
450 for (unsigned int elementIndex = 0; elementIndex < uniform.elementCount(); elementIndex++)
451 {
452 const std::string &elementString = (uniform.isArray() ? ArrayString(elementIndex) : "");
453
454 for (size_t fieldIndex = 0; fieldIndex < uniform.fields.size(); fieldIndex++)
455 {
456 const sh::ShaderVariable &field = uniform.fields[fieldIndex];
457 const std::string &fieldFullName = (fullName + elementString + "." + field.name);
458
Xinghua Cao65ec0b22017-03-28 16:10:52 +0800459 shaderUniformCount +=
460 flattenUniformImpl(field, fieldFullName, samplerUniforms, imageUniforms,
461 markStaticUse, -1, location);
Olli Etuahob78707c2017-03-09 15:03:11 +0000462 }
463 }
464
Xinghua Cao65ec0b22017-03-28 16:10:52 +0800465 return shaderUniformCount;
Olli Etuahob78707c2017-03-09 15:03:11 +0000466 }
467
468 // Not a struct
469 bool isSampler = IsSamplerType(uniform.type);
Xinghua Cao65ec0b22017-03-28 16:10:52 +0800470 bool isImage = IsImageType(uniform.type);
Olli Etuahob78707c2017-03-09 15:03:11 +0000471 std::vector<gl::LinkedUniform> *uniformList = &mUniforms;
472 if (isSampler)
473 {
474 // Store sampler uniforms separately, so we'll append them to the end of the list.
475 uniformList = samplerUniforms;
476 }
Xinghua Cao65ec0b22017-03-28 16:10:52 +0800477 else if (isImage)
478 {
479 uniformList = imageUniforms;
480 }
Olli Etuahob78707c2017-03-09 15:03:11 +0000481 LinkedUniform *existingUniform = FindUniform(*uniformList, fullName);
482 if (existingUniform)
483 {
484 if (binding != -1)
485 {
486 existingUniform->binding = binding;
487 }
488 if (*location != -1)
489 {
490 existingUniform->location = *location;
491 }
492 if (markStaticUse)
493 {
494 existingUniform->staticUse = true;
495 }
496 }
497 else
498 {
499 LinkedUniform linkedUniform(uniform.type, uniform.precision, fullName, uniform.arraySize,
500 binding, *location, -1,
501 sh::BlockMemberInfo::getDefaultBlockInfo());
502 linkedUniform.staticUse = markStaticUse;
503 uniformList->push_back(linkedUniform);
504 }
505
506 unsigned int elementCount = uniform.elementCount();
507
Xinghua Cao65ec0b22017-03-28 16:10:52 +0800508 // Samplers and images aren't "real" uniforms, so they don't count towards register usage.
509 // Likewise, don't count "real" uniforms towards sampler and image count.
510 shaderUniformCount.vectorCount =
511 ((isSampler || isImage) ? 0 : (VariableRegisterCount(uniform.type) * elementCount));
512 shaderUniformCount.samplerCount = (isSampler ? elementCount : 0);
513 shaderUniformCount.imageCount = (isImage ? elementCount : 0);
Olli Etuahob78707c2017-03-09 15:03:11 +0000514
515 if (*location != -1)
516 {
517 *location += elementCount;
518 }
519
Xinghua Cao65ec0b22017-03-28 16:10:52 +0800520 return shaderUniformCount;
Olli Etuahob78707c2017-03-09 15:03:11 +0000521}
522
523} // namespace gl