blob: 37e7036f7e63776c9459c1d500e65a05e65fd99b [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
Olli Etuahod2551232017-10-26 20:03:33 +030013#include "common/string_utils.h"
Olli Etuahob78707c2017-03-09 15:03:11 +000014#include "common/utilities.h"
15#include "libANGLE/Caps.h"
Jamie Madillbd044ed2017-06-05 12:59:21 -040016#include "libANGLE/Context.h"
Olli Etuahob78707c2017-03-09 15:03:11 +000017#include "libANGLE/Shader.h"
Jiawei-Shaoddb5eb52017-03-14 13:36:18 +080018#include "libANGLE/features.h"
Olli Etuahob78707c2017-03-09 15:03:11 +000019
20namespace gl
21{
22
23namespace
24{
25
26LinkedUniform *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
Olli Etuahod2551232017-10-26 20:03:33 +030037int 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
Olli Etuahob78707c2017-03-09 15:03:11 +000052
53UniformLinker::UniformLinker(const ProgramState &state) : mState(state)
54{
55}
56
57void UniformLinker::getResults(std::vector<LinkedUniform> *uniforms,
58 std::vector<VariableLocation> *uniformLocations)
59{
60 uniforms->swap(mUniforms);
61 uniformLocations->swap(mUniformLocations);
62}
63
Jamie Madillbd044ed2017-06-05 12:59:21 -040064bool UniformLinker::link(const Context *context,
65 InfoLog &infoLog,
Olli Etuahob78707c2017-03-09 15:03:11 +000066 const Program::Bindings &uniformLocationBindings)
67{
68 if (mState.getAttachedVertexShader() && mState.getAttachedFragmentShader())
69 {
70 ASSERT(mState.getAttachedComputeShader() == nullptr);
Jamie Madillbd044ed2017-06-05 12:59:21 -040071 if (!validateVertexAndFragmentUniforms(context, infoLog))
Olli Etuahob78707c2017-03-09 15:03:11 +000072 {
73 return false;
74 }
75 }
76
77 // Flatten the uniforms list (nested fields) into a simple list (no nesting).
78 // Also check the maximum uniform vector and sampler counts.
Jamie Madillbd044ed2017-06-05 12:59:21 -040079 if (!flattenUniformsAndCheckCaps(context, infoLog))
Olli Etuahob78707c2017-03-09 15:03:11 +000080 {
81 return false;
82 }
83
jchen10eaef1e52017-06-13 10:44:11 +080084 if (!checkMaxCombinedAtomicCounters(context->getCaps(), infoLog))
85 {
86 return false;
87 }
88
Olli Etuahob78707c2017-03-09 15:03:11 +000089 if (!indexUniforms(infoLog, uniformLocationBindings))
90 {
91 return false;
92 }
93
94 return true;
95}
96
Jamie Madillbd044ed2017-06-05 12:59:21 -040097bool UniformLinker::validateVertexAndFragmentUniforms(const Context *context,
98 InfoLog &infoLog) const
Olli Etuahob78707c2017-03-09 15:03:11 +000099{
100 // Check that uniforms defined in the vertex and fragment shaders are identical
101 std::map<std::string, LinkedUniform> linkedUniforms;
102 const std::vector<sh::Uniform> &vertexUniforms =
Jamie Madillbd044ed2017-06-05 12:59:21 -0400103 mState.getAttachedVertexShader()->getUniforms(context);
Olli Etuahob78707c2017-03-09 15:03:11 +0000104 const std::vector<sh::Uniform> &fragmentUniforms =
Jamie Madillbd044ed2017-06-05 12:59:21 -0400105 mState.getAttachedFragmentShader()->getUniforms(context);
Olli Etuahob78707c2017-03-09 15:03:11 +0000106
107 for (const sh::Uniform &vertexUniform : vertexUniforms)
108 {
109 linkedUniforms[vertexUniform.name] = LinkedUniform(vertexUniform);
110 }
111
112 for (const sh::Uniform &fragmentUniform : fragmentUniforms)
113 {
114 auto entry = linkedUniforms.find(fragmentUniform.name);
115 if (entry != linkedUniforms.end())
116 {
117 LinkedUniform *linkedUniform = &entry->second;
118 const std::string &uniformName = "uniform '" + linkedUniform->name + "'";
119 if (!linkValidateUniforms(infoLog, uniformName, *linkedUniform, fragmentUniform))
120 {
121 return false;
122 }
123 }
124 }
125 return true;
126}
127
128// GLSL ES Spec 3.00.3, section 4.3.5.
129bool UniformLinker::linkValidateUniforms(InfoLog &infoLog,
130 const std::string &uniformName,
131 const sh::Uniform &vertexUniform,
132 const sh::Uniform &fragmentUniform)
133{
134#if ANGLE_PROGRAM_LINK_VALIDATE_UNIFORM_PRECISION == ANGLE_ENABLED
135 const bool validatePrecision = true;
136#else
137 const bool validatePrecision = false;
138#endif
139
140 if (!Program::linkValidateVariablesBase(infoLog, uniformName, vertexUniform, fragmentUniform,
141 validatePrecision))
142 {
143 return false;
144 }
145
146 // GLSL ES Spec 3.10.4, section 4.4.5.
147 if (vertexUniform.binding != -1 && fragmentUniform.binding != -1 &&
148 vertexUniform.binding != fragmentUniform.binding)
149 {
150 infoLog << "Binding layout qualifiers for " << uniformName
151 << " differ between vertex and fragment shaders.";
152 return false;
153 }
154
155 // GLSL ES Spec 3.10.4, section 9.2.1.
156 if (vertexUniform.location != -1 && fragmentUniform.location != -1 &&
157 vertexUniform.location != fragmentUniform.location)
158 {
159 infoLog << "Location layout qualifiers for " << uniformName
160 << " differ between vertex and fragment shaders.";
161 return false;
162 }
jchen10eaef1e52017-06-13 10:44:11 +0800163 if (vertexUniform.offset != fragmentUniform.offset)
164 {
165 infoLog << "Offset layout qualifiers for " << uniformName
166 << " differ between vertex and fragment shaders.";
167 return false;
168 }
Olli Etuahob78707c2017-03-09 15:03:11 +0000169
170 return true;
171}
172
173bool UniformLinker::indexUniforms(InfoLog &infoLog,
174 const Program::Bindings &uniformLocationBindings)
175{
176 // All the locations where another uniform can't be located.
177 std::set<GLuint> reservedLocations;
178 // Locations which have been allocated for an unused uniform.
179 std::set<GLuint> ignoredLocations;
180
181 int maxUniformLocation = -1;
182
183 // Gather uniform locations that have been set either using the bindUniformLocation API or by
184 // using a location layout qualifier and check conflicts between them.
185 if (!gatherUniformLocationsAndCheckConflicts(infoLog, uniformLocationBindings,
186 &reservedLocations, &ignoredLocations,
187 &maxUniformLocation))
188 {
189 return false;
190 }
191
192 // Conflicts have been checked, now we can prune non-statically used uniforms. Code further down
193 // the line relies on only having statically used uniforms in mUniforms.
194 pruneUnusedUniforms();
195
196 // Gather uniforms that have their location pre-set and uniforms that don't yet have a location.
197 std::vector<VariableLocation> unlocatedUniforms;
198 std::map<GLuint, VariableLocation> preLocatedUniforms;
199
200 for (size_t uniformIndex = 0; uniformIndex < mUniforms.size(); uniformIndex++)
201 {
202 const LinkedUniform &uniform = mUniforms[uniformIndex];
203
jchen10baf5d942017-08-28 20:45:48 +0800204 if (uniform.isBuiltIn() || IsAtomicCounterType(uniform.type))
Olli Etuahob78707c2017-03-09 15:03:11 +0000205 {
206 continue;
207 }
208
Olli Etuahod2551232017-10-26 20:03:33 +0300209 int preSetLocation = GetUniformLocationBinding(uniformLocationBindings, uniform);
Olli Etuahob78707c2017-03-09 15:03:11 +0000210 int shaderLocation = uniform.location;
211
212 if (shaderLocation != -1)
213 {
214 preSetLocation = shaderLocation;
215 }
216
217 for (unsigned int arrayIndex = 0; arrayIndex < uniform.elementCount(); arrayIndex++)
218 {
Jamie Madillfb997ec2017-09-20 15:44:27 -0400219 VariableLocation location(arrayIndex, static_cast<unsigned int>(uniformIndex));
Olli Etuahob78707c2017-03-09 15:03:11 +0000220
221 if ((arrayIndex == 0 && preSetLocation != -1) || shaderLocation != -1)
222 {
223 int elementLocation = preSetLocation + arrayIndex;
224 preLocatedUniforms[elementLocation] = location;
225 }
226 else
227 {
228 unlocatedUniforms.push_back(location);
229 }
230 }
231 }
232
233 // Make enough space for all uniforms, with pre-set locations or not.
234 mUniformLocations.resize(
235 std::max(unlocatedUniforms.size() + preLocatedUniforms.size() + ignoredLocations.size(),
236 static_cast<size_t>(maxUniformLocation + 1)));
237
238 // Assign uniforms with pre-set locations
239 for (const auto &uniform : preLocatedUniforms)
240 {
241 mUniformLocations[uniform.first] = uniform.second;
242 }
243
244 // Assign ignored uniforms
245 for (const auto &ignoredLocation : ignoredLocations)
246 {
Jamie Madillfb997ec2017-09-20 15:44:27 -0400247 mUniformLocations[ignoredLocation].markIgnored();
Olli Etuahob78707c2017-03-09 15:03:11 +0000248 }
249
250 // Automatically assign locations for the rest of the uniforms
251 size_t nextUniformLocation = 0;
252 for (const auto &unlocatedUniform : unlocatedUniforms)
253 {
Jamie Madillfb997ec2017-09-20 15:44:27 -0400254 while (mUniformLocations[nextUniformLocation].used() ||
Olli Etuahob78707c2017-03-09 15:03:11 +0000255 mUniformLocations[nextUniformLocation].ignored)
256 {
257 nextUniformLocation++;
258 }
259
260 ASSERT(nextUniformLocation < mUniformLocations.size());
261 mUniformLocations[nextUniformLocation] = unlocatedUniform;
262 nextUniformLocation++;
263 }
264
265 return true;
266}
267
268bool UniformLinker::gatherUniformLocationsAndCheckConflicts(
269 InfoLog &infoLog,
270 const Program::Bindings &uniformLocationBindings,
271 std::set<GLuint> *reservedLocations,
272 std::set<GLuint> *ignoredLocations,
273 int *maxUniformLocation)
274{
275 for (const LinkedUniform &uniform : mUniforms)
276 {
277 if (uniform.isBuiltIn())
278 {
279 continue;
280 }
281
Olli Etuahod2551232017-10-26 20:03:33 +0300282 int apiBoundLocation = GetUniformLocationBinding(uniformLocationBindings, uniform);
Olli Etuahob78707c2017-03-09 15:03:11 +0000283 int shaderLocation = uniform.location;
284
285 if (shaderLocation != -1)
286 {
287 for (unsigned int arrayIndex = 0; arrayIndex < uniform.elementCount(); arrayIndex++)
288 {
289 // GLSL ES 3.10 section 4.4.3
290 int elementLocation = shaderLocation + arrayIndex;
291 *maxUniformLocation = std::max(*maxUniformLocation, elementLocation);
292 if (reservedLocations->find(elementLocation) != reservedLocations->end())
293 {
294 infoLog << "Multiple uniforms bound to location " << elementLocation << ".";
295 return false;
296 }
297 reservedLocations->insert(elementLocation);
298 if (!uniform.staticUse)
299 {
300 ignoredLocations->insert(elementLocation);
301 }
302 }
303 }
304 else if (apiBoundLocation != -1 && uniform.staticUse)
305 {
306 // Only the first location is reserved even if the uniform is an array.
307 *maxUniformLocation = std::max(*maxUniformLocation, apiBoundLocation);
308 if (reservedLocations->find(apiBoundLocation) != reservedLocations->end())
309 {
310 infoLog << "Multiple uniforms bound to location " << apiBoundLocation << ".";
311 return false;
312 }
313 reservedLocations->insert(apiBoundLocation);
314 }
315 }
316
317 // Record the uniform locations that were bound using the API for uniforms that were not found
318 // from the shader. Other uniforms should not be assigned to those locations.
319 for (const auto &locationBinding : uniformLocationBindings)
320 {
321 GLuint location = locationBinding.second;
322 if (reservedLocations->find(location) == reservedLocations->end())
323 {
324 ignoredLocations->insert(location);
325 *maxUniformLocation = std::max(*maxUniformLocation, static_cast<int>(location));
326 }
327 }
328
329 return true;
330}
331
332void UniformLinker::pruneUnusedUniforms()
333{
334 auto uniformIter = mUniforms.begin();
335 while (uniformIter != mUniforms.end())
336 {
337 if (uniformIter->staticUse)
338 {
339 ++uniformIter;
340 }
341 else
342 {
343 uniformIter = mUniforms.erase(uniformIter);
344 }
345 }
346}
347
348bool UniformLinker::flattenUniformsAndCheckCapsForShader(
Jamie Madillbd044ed2017-06-05 12:59:21 -0400349 const Context *context,
350 Shader *shader,
Olli Etuahob78707c2017-03-09 15:03:11 +0000351 GLuint maxUniformComponents,
352 GLuint maxTextureImageUnits,
Xinghua Cao65ec0b22017-03-28 16:10:52 +0800353 GLuint maxImageUnits,
jchen10eaef1e52017-06-13 10:44:11 +0800354 GLuint maxAtomicCounters,
Olli Etuahob78707c2017-03-09 15:03:11 +0000355 const std::string &componentsErrorMessage,
356 const std::string &samplerErrorMessage,
Xinghua Cao65ec0b22017-03-28 16:10:52 +0800357 const std::string &imageErrorMessage,
jchen10eaef1e52017-06-13 10:44:11 +0800358 const std::string &atomicCounterErrorMessage,
Olli Etuahob78707c2017-03-09 15:03:11 +0000359 std::vector<LinkedUniform> &samplerUniforms,
Xinghua Cao65ec0b22017-03-28 16:10:52 +0800360 std::vector<LinkedUniform> &imageUniforms,
jchen10eaef1e52017-06-13 10:44:11 +0800361 std::vector<LinkedUniform> &atomicCounterUniforms,
Olli Etuahob78707c2017-03-09 15:03:11 +0000362 InfoLog &infoLog)
363{
Xinghua Cao65ec0b22017-03-28 16:10:52 +0800364 ShaderUniformCount shaderUniformCount;
Jamie Madillbd044ed2017-06-05 12:59:21 -0400365 for (const sh::Uniform &uniform : shader->getUniforms(context))
Olli Etuahob78707c2017-03-09 15:03:11 +0000366 {
jchen10baf5d942017-08-28 20:45:48 +0800367 shaderUniformCount += flattenUniform(uniform, &samplerUniforms, &imageUniforms,
368 &atomicCounterUniforms, shader->getType());
Olli Etuahob78707c2017-03-09 15:03:11 +0000369 }
370
Xinghua Cao65ec0b22017-03-28 16:10:52 +0800371 if (shaderUniformCount.vectorCount > maxUniformComponents)
Olli Etuahob78707c2017-03-09 15:03:11 +0000372 {
373 infoLog << componentsErrorMessage << maxUniformComponents << ").";
374 return false;
375 }
376
Xinghua Cao65ec0b22017-03-28 16:10:52 +0800377 if (shaderUniformCount.samplerCount > maxTextureImageUnits)
Olli Etuahob78707c2017-03-09 15:03:11 +0000378 {
379 infoLog << samplerErrorMessage << maxTextureImageUnits << ").";
380 return false;
381 }
382
Xinghua Cao65ec0b22017-03-28 16:10:52 +0800383 if (shaderUniformCount.imageCount > maxImageUnits)
384 {
385 infoLog << imageErrorMessage << maxImageUnits << ").";
386 return false;
387 }
388
jchen10eaef1e52017-06-13 10:44:11 +0800389 if (shaderUniformCount.atomicCounterCount > maxAtomicCounters)
390 {
391 infoLog << atomicCounterErrorMessage << maxAtomicCounters << ").";
392 return false;
393 }
394
Olli Etuahob78707c2017-03-09 15:03:11 +0000395 return true;
396}
397
Jamie Madillbd044ed2017-06-05 12:59:21 -0400398bool UniformLinker::flattenUniformsAndCheckCaps(const Context *context, InfoLog &infoLog)
Olli Etuahob78707c2017-03-09 15:03:11 +0000399{
400 std::vector<LinkedUniform> samplerUniforms;
Xinghua Cao65ec0b22017-03-28 16:10:52 +0800401 std::vector<LinkedUniform> imageUniforms;
jchen10eaef1e52017-06-13 10:44:11 +0800402 std::vector<LinkedUniform> atomicCounterUniforms;
Olli Etuahob78707c2017-03-09 15:03:11 +0000403
Jamie Madillbd044ed2017-06-05 12:59:21 -0400404 const Caps &caps = context->getCaps();
405
Olli Etuahob78707c2017-03-09 15:03:11 +0000406 if (mState.getAttachedComputeShader())
407 {
Jamie Madillbd044ed2017-06-05 12:59:21 -0400408 Shader *computeShader = mState.getAttachedComputeShader();
Olli Etuahob78707c2017-03-09 15:03:11 +0000409
410 // TODO (mradev): check whether we need finer-grained component counting
411 if (!flattenUniformsAndCheckCapsForShader(
Jamie Madillbd044ed2017-06-05 12:59:21 -0400412 context, computeShader, caps.maxComputeUniformComponents / 4,
Xinghua Cao65ec0b22017-03-28 16:10:52 +0800413 caps.maxComputeTextureImageUnits, caps.maxComputeImageUniforms,
jchen10eaef1e52017-06-13 10:44:11 +0800414 caps.maxComputeAtomicCounters,
Olli Etuahob78707c2017-03-09 15:03:11 +0000415 "Compute shader active uniforms exceed MAX_COMPUTE_UNIFORM_COMPONENTS (",
416 "Compute shader sampler count exceeds MAX_COMPUTE_TEXTURE_IMAGE_UNITS (",
jchen10eaef1e52017-06-13 10:44:11 +0800417 "Compute shader image count exceeds MAX_COMPUTE_IMAGE_UNIFORMS (",
418 "Compute shader atomic counter count exceeds MAX_COMPUTE_ATOMIC_COUNTERS (",
419 samplerUniforms, imageUniforms, atomicCounterUniforms, infoLog))
Olli Etuahob78707c2017-03-09 15:03:11 +0000420 {
421 return false;
422 }
423 }
424 else
425 {
Jamie Madillbd044ed2017-06-05 12:59:21 -0400426 Shader *vertexShader = mState.getAttachedVertexShader();
Olli Etuahob78707c2017-03-09 15:03:11 +0000427
428 if (!flattenUniformsAndCheckCapsForShader(
Jamie Madillbd044ed2017-06-05 12:59:21 -0400429 context, vertexShader, caps.maxVertexUniformVectors,
Xinghua Cao65ec0b22017-03-28 16:10:52 +0800430 caps.maxVertexTextureImageUnits, caps.maxVertexImageUniforms,
jchen10eaef1e52017-06-13 10:44:11 +0800431 caps.maxVertexAtomicCounters,
Olli Etuahob78707c2017-03-09 15:03:11 +0000432 "Vertex shader active uniforms exceed MAX_VERTEX_UNIFORM_VECTORS (",
433 "Vertex shader sampler count exceeds MAX_VERTEX_TEXTURE_IMAGE_UNITS (",
jchen10eaef1e52017-06-13 10:44:11 +0800434 "Vertex shader image count exceeds MAX_VERTEX_IMAGE_UNIFORMS (",
435 "Vertex shader atomic counter count exceeds MAX_VERTEX_ATOMIC_COUNTERS (",
436 samplerUniforms, imageUniforms, atomicCounterUniforms, infoLog))
Olli Etuahob78707c2017-03-09 15:03:11 +0000437 {
438 return false;
439 }
Jamie Madillbd044ed2017-06-05 12:59:21 -0400440
441 Shader *fragmentShader = mState.getAttachedFragmentShader();
Olli Etuahob78707c2017-03-09 15:03:11 +0000442
443 if (!flattenUniformsAndCheckCapsForShader(
Jamie Madillbd044ed2017-06-05 12:59:21 -0400444 context, fragmentShader, caps.maxFragmentUniformVectors, caps.maxTextureImageUnits,
jchen10eaef1e52017-06-13 10:44:11 +0800445 caps.maxFragmentImageUniforms, caps.maxFragmentAtomicCounters,
Olli Etuahob78707c2017-03-09 15:03:11 +0000446 "Fragment shader active uniforms exceed MAX_FRAGMENT_UNIFORM_VECTORS (",
Xinghua Cao65ec0b22017-03-28 16:10:52 +0800447 "Fragment shader sampler count exceeds MAX_TEXTURE_IMAGE_UNITS (",
448 "Fragment shader image count exceeds MAX_FRAGMENT_IMAGE_UNIFORMS (",
jchen10eaef1e52017-06-13 10:44:11 +0800449 "Fragment shader atomic counter count exceeds MAX_FRAGMENT_ATOMIC_COUNTERS (",
450 samplerUniforms, imageUniforms, atomicCounterUniforms, infoLog))
Olli Etuahob78707c2017-03-09 15:03:11 +0000451 {
452 return false;
453 }
454 }
455
456 mUniforms.insert(mUniforms.end(), samplerUniforms.begin(), samplerUniforms.end());
Xinghua Cao65ec0b22017-03-28 16:10:52 +0800457 mUniforms.insert(mUniforms.end(), imageUniforms.begin(), imageUniforms.end());
jchen10eaef1e52017-06-13 10:44:11 +0800458 mUniforms.insert(mUniforms.end(), atomicCounterUniforms.begin(), atomicCounterUniforms.end());
Olli Etuahob78707c2017-03-09 15:03:11 +0000459 return true;
460}
461
Xinghua Cao65ec0b22017-03-28 16:10:52 +0800462UniformLinker::ShaderUniformCount UniformLinker::flattenUniform(
Olli Etuahob78707c2017-03-09 15:03:11 +0000463 const sh::Uniform &uniform,
Xinghua Cao65ec0b22017-03-28 16:10:52 +0800464 std::vector<LinkedUniform> *samplerUniforms,
jchen10eaef1e52017-06-13 10:44:11 +0800465 std::vector<LinkedUniform> *imageUniforms,
jchen10baf5d942017-08-28 20:45:48 +0800466 std::vector<LinkedUniform> *atomicCounterUniforms,
467 GLenum shaderType)
Olli Etuahob78707c2017-03-09 15:03:11 +0000468{
Jamie Madill977abce2017-11-07 08:03:19 -0500469 int location = uniform.location;
jchen10baf5d942017-08-28 20:45:48 +0800470 ShaderUniformCount shaderUniformCount =
471 flattenUniformImpl(uniform, uniform.name, uniform.mappedName, samplerUniforms,
472 imageUniforms, atomicCounterUniforms, shaderType, uniform.staticUse,
473 uniform.binding, uniform.offset, &location);
Olli Etuahob78707c2017-03-09 15:03:11 +0000474 if (uniform.staticUse)
475 {
Xinghua Cao65ec0b22017-03-28 16:10:52 +0800476 return shaderUniformCount;
Olli Etuahob78707c2017-03-09 15:03:11 +0000477 }
Xinghua Cao65ec0b22017-03-28 16:10:52 +0800478 return ShaderUniformCount();
Olli Etuahob78707c2017-03-09 15:03:11 +0000479}
480
Xinghua Cao65ec0b22017-03-28 16:10:52 +0800481UniformLinker::ShaderUniformCount UniformLinker::flattenUniformImpl(
Olli Etuahob78707c2017-03-09 15:03:11 +0000482 const sh::ShaderVariable &uniform,
483 const std::string &fullName,
Olli Etuaho855d9642017-05-17 14:05:06 +0300484 const std::string &fullMappedName,
Olli Etuahob78707c2017-03-09 15:03:11 +0000485 std::vector<LinkedUniform> *samplerUniforms,
Xinghua Cao65ec0b22017-03-28 16:10:52 +0800486 std::vector<LinkedUniform> *imageUniforms,
jchen10eaef1e52017-06-13 10:44:11 +0800487 std::vector<LinkedUniform> *atomicCounterUniforms,
jchen10baf5d942017-08-28 20:45:48 +0800488 GLenum shaderType,
Olli Etuahob78707c2017-03-09 15:03:11 +0000489 bool markStaticUse,
490 int binding,
jchen10eaef1e52017-06-13 10:44:11 +0800491 int offset,
Olli Etuahob78707c2017-03-09 15:03:11 +0000492 int *location)
493{
494 ASSERT(location);
Xinghua Cao65ec0b22017-03-28 16:10:52 +0800495 ShaderUniformCount shaderUniformCount;
Olli Etuahob78707c2017-03-09 15:03:11 +0000496
497 if (uniform.isStruct())
498 {
499 for (unsigned int elementIndex = 0; elementIndex < uniform.elementCount(); elementIndex++)
500 {
501 const std::string &elementString = (uniform.isArray() ? ArrayString(elementIndex) : "");
502
503 for (size_t fieldIndex = 0; fieldIndex < uniform.fields.size(); fieldIndex++)
504 {
505 const sh::ShaderVariable &field = uniform.fields[fieldIndex];
506 const std::string &fieldFullName = (fullName + elementString + "." + field.name);
Olli Etuaho855d9642017-05-17 14:05:06 +0300507 const std::string &fieldFullMappedName =
508 (fullMappedName + elementString + "." + field.mappedName);
Olli Etuahob78707c2017-03-09 15:03:11 +0000509
Olli Etuaho855d9642017-05-17 14:05:06 +0300510 shaderUniformCount += flattenUniformImpl(
511 field, fieldFullName, fieldFullMappedName, samplerUniforms, imageUniforms,
jchen10baf5d942017-08-28 20:45:48 +0800512 atomicCounterUniforms, shaderType, markStaticUse, -1, -1, location);
Olli Etuahob78707c2017-03-09 15:03:11 +0000513 }
514 }
515
Xinghua Cao65ec0b22017-03-28 16:10:52 +0800516 return shaderUniformCount;
Olli Etuahob78707c2017-03-09 15:03:11 +0000517 }
518
519 // Not a struct
520 bool isSampler = IsSamplerType(uniform.type);
Xinghua Cao65ec0b22017-03-28 16:10:52 +0800521 bool isImage = IsImageType(uniform.type);
jchen10eaef1e52017-06-13 10:44:11 +0800522 bool isAtomicCounter = IsAtomicCounterType(uniform.type);
Olli Etuahob78707c2017-03-09 15:03:11 +0000523 std::vector<gl::LinkedUniform> *uniformList = &mUniforms;
524 if (isSampler)
525 {
Olli Etuahob78707c2017-03-09 15:03:11 +0000526 uniformList = samplerUniforms;
527 }
Xinghua Cao65ec0b22017-03-28 16:10:52 +0800528 else if (isImage)
529 {
530 uniformList = imageUniforms;
531 }
jchen10eaef1e52017-06-13 10:44:11 +0800532 else if (isAtomicCounter)
533 {
534 uniformList = atomicCounterUniforms;
535 }
Olli Etuahod2551232017-10-26 20:03:33 +0300536
537 std::string fullNameWithArrayIndex(fullName);
538 std::string fullMappedNameWithArrayIndex(fullMappedName);
539
540 if (uniform.isArray())
541 {
542 // We're following the GLES 3.1 November 2016 spec section 7.3.1.1 Naming Active Resources
543 // and including [0] at the end of array variable names.
544 fullNameWithArrayIndex += "[0]";
545 fullMappedNameWithArrayIndex += "[0]";
546 }
547
548 LinkedUniform *existingUniform = FindUniform(*uniformList, fullNameWithArrayIndex);
Olli Etuahob78707c2017-03-09 15:03:11 +0000549 if (existingUniform)
550 {
551 if (binding != -1)
552 {
553 existingUniform->binding = binding;
554 }
jchen10eaef1e52017-06-13 10:44:11 +0800555 if (offset != -1)
556 {
557 existingUniform->offset = offset;
558 }
Olli Etuahob78707c2017-03-09 15:03:11 +0000559 if (*location != -1)
560 {
561 existingUniform->location = *location;
562 }
563 if (markStaticUse)
564 {
565 existingUniform->staticUse = true;
jchen104ef25032017-11-01 09:36:51 +0800566 existingUniform->setStaticUse(shaderType, true);
Olli Etuahob78707c2017-03-09 15:03:11 +0000567 }
568 }
569 else
570 {
Olli Etuahod2551232017-10-26 20:03:33 +0300571 LinkedUniform linkedUniform(uniform.type, uniform.precision, fullNameWithArrayIndex,
572 uniform.arraySize, binding, offset, *location, -1,
Olli Etuahob78707c2017-03-09 15:03:11 +0000573 sh::BlockMemberInfo::getDefaultBlockInfo());
Olli Etuahod2551232017-10-26 20:03:33 +0300574 linkedUniform.mappedName = fullMappedNameWithArrayIndex;
Olli Etuahob78707c2017-03-09 15:03:11 +0000575 linkedUniform.staticUse = markStaticUse;
jchen10baf5d942017-08-28 20:45:48 +0800576 if (markStaticUse)
577 {
jchen104ef25032017-11-01 09:36:51 +0800578 linkedUniform.setStaticUse(shaderType, true);
jchen10baf5d942017-08-28 20:45:48 +0800579 }
jchen10eaef1e52017-06-13 10:44:11 +0800580
Olli Etuahob78707c2017-03-09 15:03:11 +0000581 uniformList->push_back(linkedUniform);
582 }
583
584 unsigned int elementCount = uniform.elementCount();
585
Xinghua Cao65ec0b22017-03-28 16:10:52 +0800586 // Samplers and images aren't "real" uniforms, so they don't count towards register usage.
jchen10eaef1e52017-06-13 10:44:11 +0800587 // Likewise, don't count "real" uniforms towards opaque count.
Xinghua Cao65ec0b22017-03-28 16:10:52 +0800588 shaderUniformCount.vectorCount =
jchen10eaef1e52017-06-13 10:44:11 +0800589 (IsOpaqueType(uniform.type) ? 0 : (VariableRegisterCount(uniform.type) * elementCount));
Xinghua Cao65ec0b22017-03-28 16:10:52 +0800590 shaderUniformCount.samplerCount = (isSampler ? elementCount : 0);
Jamie Madill977abce2017-11-07 08:03:19 -0500591 shaderUniformCount.imageCount = (isImage ? elementCount : 0);
jchen10eaef1e52017-06-13 10:44:11 +0800592 shaderUniformCount.atomicCounterCount = (isAtomicCounter ? elementCount : 0);
Olli Etuahob78707c2017-03-09 15:03:11 +0000593
594 if (*location != -1)
595 {
596 *location += elementCount;
597 }
598
Xinghua Cao65ec0b22017-03-28 16:10:52 +0800599 return shaderUniformCount;
Olli Etuahob78707c2017-03-09 15:03:11 +0000600}
601
jchen10eaef1e52017-06-13 10:44:11 +0800602bool UniformLinker::checkMaxCombinedAtomicCounters(const Caps &caps, InfoLog &infoLog)
603{
604 unsigned int atomicCounterCount = 0;
605 for (const auto &uniform : mUniforms)
606 {
607 if (IsAtomicCounterType(uniform.type) && uniform.staticUse)
608 {
609 atomicCounterCount += uniform.elementCount();
610 if (atomicCounterCount > caps.maxCombinedAtomicCounters)
611 {
612 infoLog << "atomic counter count exceeds MAX_COMBINED_ATOMIC_COUNTERS"
613 << caps.maxCombinedAtomicCounters << ").";
614 return false;
615 }
616 }
617 }
618 return true;
619}
620
Jamie Madill977abce2017-11-07 08:03:19 -0500621// InterfaceBlockLinker implementation.
622InterfaceBlockLinker::InterfaceBlockLinker(std::vector<InterfaceBlock> *blocksOut)
623 : mBlocksOut(blocksOut)
624{
625}
626
627InterfaceBlockLinker::~InterfaceBlockLinker()
628{
629}
630
631void InterfaceBlockLinker::addShaderBlocks(GLenum shader,
632 const std::vector<sh::InterfaceBlock> *blocks)
633{
634 mShaderBlocks.push_back(std::make_pair(shader, blocks));
635}
636
637void InterfaceBlockLinker::linkBlocks(const GetBlockSize &getBlockSize,
638 const GetBlockMemberInfo &getMemberInfo) const
639{
640 std::set<std::string> visitedList;
641
642 for (const auto &shaderBlocks : mShaderBlocks)
643 {
644 const GLenum shaderType = shaderBlocks.first;
645
646 for (const auto &block : *shaderBlocks.second)
647 {
648 // Only 'packed' blocks are allowed to be considered inactive.
649 if (!block.staticUse && block.layout == sh::BLOCKLAYOUT_PACKED)
650 continue;
651
652 if (visitedList.count(block.name) > 0)
653 {
654 if (block.staticUse)
655 {
656 for (InterfaceBlock &priorBlock : *mBlocksOut)
657 {
658 if (block.name == priorBlock.name)
659 {
660 priorBlock.setStaticUse(shaderType, true);
661 }
662 }
663 }
664 }
665 else
666 {
667 defineInterfaceBlock(getBlockSize, getMemberInfo, block, shaderType);
668 visitedList.insert(block.name);
669 }
670 }
671 }
672}
673
674template <typename VarT>
675void InterfaceBlockLinker::defineBlockMembers(const GetBlockMemberInfo &getMemberInfo,
676 const std::vector<VarT> &fields,
677 const std::string &prefix,
678 const std::string &mappedPrefix,
679 int blockIndex) const
680{
681 for (const VarT &field : fields)
682 {
683 std::string fullName = (prefix.empty() ? field.name : prefix + "." + field.name);
684
685 std::string fullMappedName =
686 (mappedPrefix.empty() ? field.mappedName : mappedPrefix + "." + field.mappedName);
687
688 if (field.isStruct())
689 {
690 for (unsigned int arrayElement = 0; arrayElement < field.elementCount(); arrayElement++)
691 {
692 const std::string elementName =
693 fullName + (field.isArray() ? ArrayString(arrayElement) : "");
694 const std::string elementMappedName =
695 fullMappedName + (field.isArray() ? ArrayString(arrayElement) : "");
696 defineBlockMembers(getMemberInfo, field.fields, elementName, elementMappedName,
697 blockIndex);
698 }
699 }
700 else
701 {
702 // If getBlockMemberInfo returns false, the variable is optimized out.
703 sh::BlockMemberInfo memberInfo;
704 if (!getMemberInfo(fullName, fullMappedName, &memberInfo))
705 {
706 continue;
707 }
708
709 if (field.isArray())
710 {
711 fullName += "[0]";
712 fullMappedName += "[0]";
713 }
714
715 defineBlockMember(field, fullName, fullMappedName, blockIndex, memberInfo);
716 }
717 }
718}
719
720void InterfaceBlockLinker::defineInterfaceBlock(const GetBlockSize &getBlockSize,
721 const GetBlockMemberInfo &getMemberInfo,
722 const sh::InterfaceBlock &interfaceBlock,
723 GLenum shaderType) const
724{
725 size_t blockSize = 0;
726 std::vector<unsigned int> blockIndexes;
727
728 int blockIndex = static_cast<int>(mBlocksOut->size());
729 // Track the first and last uniform index to determine the range of active uniforms in the
730 // block.
731 size_t firstBlockMemberIndex = getCurrentBlockMemberIndex();
732 defineBlockMembers(getMemberInfo, interfaceBlock.fields, interfaceBlock.fieldPrefix(),
733 interfaceBlock.fieldMappedPrefix(), blockIndex);
734 size_t lastBlockMemberIndex = getCurrentBlockMemberIndex();
735
736 for (size_t blockMemberIndex = firstBlockMemberIndex; blockMemberIndex < lastBlockMemberIndex;
737 ++blockMemberIndex)
738 {
739 blockIndexes.push_back(static_cast<unsigned int>(blockMemberIndex));
740 }
741
742 // ESSL 3.10 section 4.4.4 page 58:
743 // Any uniform or shader storage block declared without a binding qualifier is initially
744 // assigned to block binding point zero.
745 int blockBinding = (interfaceBlock.binding == -1 ? 0 : interfaceBlock.binding);
746 for (unsigned int arrayElement = 0; arrayElement < interfaceBlock.elementCount();
747 ++arrayElement)
748 {
749 // We don't currently have the getBlockSize implemented for SSBOs.
750 // TODO(jiajia.qin@intel.com): Remove the if when we have getBlockSize for SSBOs.
751 if (interfaceBlock.blockType == sh::BlockType::BLOCK_UNIFORM)
752 {
753 std::string blockArrayName = interfaceBlock.name;
754 std::string blockMappedArrayName = interfaceBlock.mappedName;
755 if (interfaceBlock.isArray())
756 {
757 blockArrayName += ArrayString(arrayElement);
758 blockMappedArrayName += ArrayString(arrayElement);
759 }
760
761 // Don't define this block at all if it's not active in the implementation.
762 if (!getBlockSize(blockArrayName, blockMappedArrayName, &blockSize))
763 {
764 continue;
765 }
766 }
767
768 InterfaceBlock block(interfaceBlock.name, interfaceBlock.mappedName,
769 interfaceBlock.isArray(), arrayElement, blockBinding + arrayElement);
770 block.memberIndexes = blockIndexes;
771 block.setStaticUse(shaderType, interfaceBlock.staticUse);
772
773 // Since all block elements in an array share the same active interface blocks, they
774 // will all be active once any block member is used. So, since interfaceBlock.name[0]
775 // was active, here we will add every block element in the array.
776 block.dataSize = static_cast<unsigned int>(blockSize);
777 mBlocksOut->push_back(block);
778 }
779}
780
781// UniformBlockLinker implementation.
782UniformBlockLinker::UniformBlockLinker(std::vector<InterfaceBlock> *blocksOut,
783 std::vector<LinkedUniform> *uniformsOut)
784 : InterfaceBlockLinker(blocksOut), mUniformsOut(uniformsOut)
785{
786}
787
788UniformBlockLinker::~UniformBlockLinker()
789{
790}
791
792void UniformBlockLinker::defineBlockMember(const sh::ShaderVariable &field,
793 const std::string &fullName,
794 const std::string &fullMappedName,
795 int blockIndex,
796 const sh::BlockMemberInfo &memberInfo) const
797{
798 LinkedUniform newUniform(field.type, field.precision, fullName, field.arraySize, -1, -1, -1,
799 blockIndex, memberInfo);
800 newUniform.mappedName = fullMappedName;
801
802 // Since block uniforms have no location, we don't need to store them in the uniform locations
803 // list.
804 mUniformsOut->push_back(newUniform);
805}
806
807size_t UniformBlockLinker::getCurrentBlockMemberIndex() const
808{
809 return mUniformsOut->size();
810}
811
812// ShaderStorageBlockLinker implementation.
813ShaderStorageBlockLinker::ShaderStorageBlockLinker(std::vector<InterfaceBlock> *blocksOut)
814 : InterfaceBlockLinker(blocksOut)
815{
816}
817
818ShaderStorageBlockLinker::~ShaderStorageBlockLinker()
819{
820}
821
822void ShaderStorageBlockLinker::defineBlockMember(const sh::ShaderVariable &field,
823 const std::string &fullName,
824 const std::string &fullMappedName,
825 int blockIndex,
826 const sh::BlockMemberInfo &memberInfo) const
827{
828 // TODO(jiajia.qin@intel.com): Add buffer variables support.
829}
830
831size_t ShaderStorageBlockLinker::getCurrentBlockMemberIndex() const
832{
833 // TODO(jiajia.qin@intel.com): Add buffer variables support.
834 return 0;
835}
836
Olli Etuahob78707c2017-03-09 15:03:11 +0000837} // namespace gl