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