1 //
2 // Copyright 2015 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 // VaryingPacking:
7 // Class which describes a mapping from varyings to registers, according
8 // to the spec, or using custom packing algorithms. We also keep a register
9 // allocation list for the D3D renderer.
10 //
11
12 #include "libANGLE/VaryingPacking.h"
13
14 #include "common/utilities.h"
15 #include "libANGLE/Program.h"
16 #include "libANGLE/Shader.h"
17
18 namespace gl
19 {
20
21 namespace
22 {
23
24 // true if varying x has a higher priority in packing than y
ComparePackedVarying(const PackedVarying & x,const PackedVarying & y)25 bool ComparePackedVarying(const PackedVarying &x, const PackedVarying &y)
26 {
27 // If the PackedVarying 'x' or 'y' to be compared is an array element, this clones an equivalent
28 // non-array shader variable 'vx' or 'vy' for actual comparison instead.
29 sh::ShaderVariable vx, vy;
30 const sh::ShaderVariable *px, *py;
31
32 px = &x.varying();
33 py = &y.varying();
34
35 if (x.isArrayElement())
36 {
37 vx = *px;
38 vx.arraySizes.clear();
39 px = &vx;
40 }
41
42 if (y.isArrayElement())
43 {
44 vy = *py;
45 vy.arraySizes.clear();
46 py = &vy;
47 }
48
49 return gl::CompareShaderVar(*px, *py);
50 }
51
52 } // anonymous namespace
53
54 // Implementation of VaryingInShaderRef
VaryingInShaderRef(ShaderType stageIn,const sh::ShaderVariable * varyingIn)55 VaryingInShaderRef::VaryingInShaderRef(ShaderType stageIn, const sh::ShaderVariable *varyingIn)
56 : varying(varyingIn), stage(stageIn)
57 {}
58
59 VaryingInShaderRef::~VaryingInShaderRef() = default;
60
VaryingInShaderRef(VaryingInShaderRef && other)61 VaryingInShaderRef::VaryingInShaderRef(VaryingInShaderRef &&other)
62 {
63 *this = std::move(other);
64 }
65
operator =(VaryingInShaderRef && other)66 VaryingInShaderRef &VaryingInShaderRef::operator=(VaryingInShaderRef &&other)
67 {
68 std::swap(varying, other.varying);
69 std::swap(stage, other.stage);
70 std::swap(parentStructName, other.parentStructName);
71 std::swap(parentStructMappedName, other.parentStructMappedName);
72
73 return *this;
74 }
75
76 // Implementation of PackedVarying
PackedVarying(VaryingInShaderRef && frontVaryingIn,VaryingInShaderRef && backVaryingIn,sh::InterpolationType interpolationIn)77 PackedVarying::PackedVarying(VaryingInShaderRef &&frontVaryingIn,
78 VaryingInShaderRef &&backVaryingIn,
79 sh::InterpolationType interpolationIn)
80 : PackedVarying(std::move(frontVaryingIn), std::move(backVaryingIn), interpolationIn, 0)
81 {}
82
PackedVarying(VaryingInShaderRef && frontVaryingIn,VaryingInShaderRef && backVaryingIn,sh::InterpolationType interpolationIn,GLuint fieldIndexIn)83 PackedVarying::PackedVarying(VaryingInShaderRef &&frontVaryingIn,
84 VaryingInShaderRef &&backVaryingIn,
85 sh::InterpolationType interpolationIn,
86 GLuint fieldIndexIn)
87 : frontVarying(std::move(frontVaryingIn)),
88 backVarying(std::move(backVaryingIn)),
89 interpolation(interpolationIn),
90 arrayIndex(GL_INVALID_INDEX),
91 fieldIndex(fieldIndexIn)
92 {}
93
94 PackedVarying::~PackedVarying() = default;
95
PackedVarying(PackedVarying && other)96 PackedVarying::PackedVarying(PackedVarying &&other)
97 : frontVarying(other.frontVarying.stage, other.frontVarying.varying),
98 backVarying(other.backVarying.stage, other.backVarying.varying)
99 {
100 *this = std::move(other);
101 }
102
operator =(PackedVarying && other)103 PackedVarying &PackedVarying::operator=(PackedVarying &&other)
104 {
105 std::swap(frontVarying, other.frontVarying);
106 std::swap(backVarying, other.backVarying);
107 std::swap(interpolation, other.interpolation);
108 std::swap(arrayIndex, other.arrayIndex);
109 std::swap(fieldIndex, other.fieldIndex);
110
111 return *this;
112 }
113
114 // Implementation of VaryingPacking
VaryingPacking(GLuint maxVaryingVectors,PackMode packMode)115 VaryingPacking::VaryingPacking(GLuint maxVaryingVectors, PackMode packMode)
116 : mRegisterMap(maxVaryingVectors), mPackMode(packMode)
117 {}
118
119 VaryingPacking::~VaryingPacking() = default;
120
reset()121 void VaryingPacking::reset()
122 {
123 clearRegisterMap();
124 mRegisterList.clear();
125 mPackedVaryings.clear();
126
127 for (std::vector<std::string> inactiveVaryingMappedNames : mInactiveVaryingMappedNames)
128 {
129 inactiveVaryingMappedNames.clear();
130 }
131 }
132
clearRegisterMap()133 void VaryingPacking::clearRegisterMap()
134 {
135 std::fill(mRegisterMap.begin(), mRegisterMap.end(), Register());
136 }
137
138 // Packs varyings into generic varying registers, using the algorithm from
139 // See [OpenGL ES Shading Language 1.00 rev. 17] appendix A section 7 page 111
140 // Also [OpenGL ES Shading Language 3.00 rev. 4] Section 11 page 119
141 // Returns false if unsuccessful.
packVarying(const PackedVarying & packedVarying)142 bool VaryingPacking::packVarying(const PackedVarying &packedVarying)
143 {
144 const sh::ShaderVariable &varying = packedVarying.varying();
145
146 // "Non - square matrices of type matCxR consume the same space as a square matrix of type matN
147 // where N is the greater of C and R."
148 // Here we are a bit more conservative and allow packing non-square matrices more tightly.
149 // Make sure we use transposed matrix types to count registers correctly.
150 ASSERT(!varying.isStruct());
151 GLenum transposedType = gl::TransposeMatrixType(varying.type);
152 unsigned int varyingRows = gl::VariableRowCount(transposedType);
153 unsigned int varyingColumns = gl::VariableColumnCount(transposedType);
154
155 // Special pack mode for D3D9. Each varying takes a full register, no sharing.
156 // TODO(jmadill): Implement more sophisticated component packing in D3D9.
157 if (mPackMode == PackMode::ANGLE_NON_CONFORMANT_D3D9)
158 {
159 varyingColumns = 4;
160 }
161
162 // "Variables of type mat2 occupies 2 complete rows."
163 // For non-WebGL contexts, we allow mat2 to occupy only two columns per row.
164 else if (mPackMode == PackMode::WEBGL_STRICT && varying.type == GL_FLOAT_MAT2)
165 {
166 varyingColumns = 4;
167 }
168
169 // "Arrays of size N are assumed to take N times the size of the base type"
170 // GLSL ES 3.10 section 4.3.6: Output variables cannot be arrays of arrays or arrays of
171 // structures, so we may use getBasicTypeElementCount().
172 const unsigned int elementCount = varying.getBasicTypeElementCount();
173 varyingRows *= (packedVarying.isArrayElement() ? 1 : elementCount);
174
175 unsigned int maxVaryingVectors = static_cast<unsigned int>(mRegisterMap.size());
176
177 // Fail if we are packing a single over-large varying.
178 if (varyingRows > maxVaryingVectors)
179 {
180 return false;
181 }
182
183 // "For 2, 3 and 4 component variables packing is started using the 1st column of the 1st row.
184 // Variables are then allocated to successive rows, aligning them to the 1st column."
185 if (varyingColumns >= 2 && varyingColumns <= 4)
186 {
187 for (unsigned int row = 0; row <= maxVaryingVectors - varyingRows; ++row)
188 {
189 if (isFree(row, 0, varyingRows, varyingColumns))
190 {
191 insert(row, 0, packedVarying);
192 return true;
193 }
194 }
195
196 // "For 2 component variables, when there are no spare rows, the strategy is switched to
197 // using the highest numbered row and the lowest numbered column where the variable will
198 // fit."
199 if (varyingColumns == 2)
200 {
201 for (unsigned int r = maxVaryingVectors - varyingRows + 1; r-- >= 1;)
202 {
203 if (isFree(r, 2, varyingRows, 2))
204 {
205 insert(r, 2, packedVarying);
206 return true;
207 }
208 }
209 }
210
211 return false;
212 }
213
214 // "1 component variables have their own packing rule. They are packed in order of size, largest
215 // first. Each variable is placed in the column that leaves the least amount of space in the
216 // column and aligned to the lowest available rows within that column."
217 ASSERT(varyingColumns == 1);
218 unsigned int contiguousSpace[4] = {0};
219 unsigned int bestContiguousSpace[4] = {0};
220 unsigned int totalSpace[4] = {0};
221
222 for (unsigned int row = 0; row < maxVaryingVectors; ++row)
223 {
224 for (unsigned int column = 0; column < 4; ++column)
225 {
226 if (mRegisterMap[row][column])
227 {
228 contiguousSpace[column] = 0;
229 }
230 else
231 {
232 contiguousSpace[column]++;
233 totalSpace[column]++;
234
235 if (contiguousSpace[column] > bestContiguousSpace[column])
236 {
237 bestContiguousSpace[column] = contiguousSpace[column];
238 }
239 }
240 }
241 }
242
243 unsigned int bestColumn = 0;
244 for (unsigned int column = 1; column < 4; ++column)
245 {
246 if (bestContiguousSpace[column] >= varyingRows &&
247 (bestContiguousSpace[bestColumn] < varyingRows ||
248 totalSpace[column] < totalSpace[bestColumn]))
249 {
250 bestColumn = column;
251 }
252 }
253
254 if (bestContiguousSpace[bestColumn] >= varyingRows)
255 {
256 for (unsigned int row = 0; row < maxVaryingVectors; row++)
257 {
258 if (isFree(row, bestColumn, varyingRows, 1))
259 {
260 for (unsigned int arrayIndex = 0; arrayIndex < varyingRows; ++arrayIndex)
261 {
262 // If varyingRows > 1, it must be an array.
263 PackedVaryingRegister registerInfo;
264 registerInfo.packedVarying = &packedVarying;
265 registerInfo.registerRow = row + arrayIndex;
266 registerInfo.registerColumn = bestColumn;
267 registerInfo.varyingArrayIndex =
268 (packedVarying.isArrayElement() ? packedVarying.arrayIndex : arrayIndex);
269 registerInfo.varyingRowIndex = 0;
270 // Do not record register info for builtins.
271 // TODO(jmadill): Clean this up.
272 if (!varying.isBuiltIn())
273 {
274 mRegisterList.push_back(registerInfo);
275 }
276 mRegisterMap[row + arrayIndex][bestColumn] = true;
277 }
278 break;
279 }
280 }
281 return true;
282 }
283
284 return false;
285 }
286
isFree(unsigned int registerRow,unsigned int registerColumn,unsigned int varyingRows,unsigned int varyingColumns) const287 bool VaryingPacking::isFree(unsigned int registerRow,
288 unsigned int registerColumn,
289 unsigned int varyingRows,
290 unsigned int varyingColumns) const
291 {
292 for (unsigned int row = 0; row < varyingRows; ++row)
293 {
294 ASSERT(registerRow + row < mRegisterMap.size());
295 for (unsigned int column = 0; column < varyingColumns; ++column)
296 {
297 ASSERT(registerColumn + column < 4);
298 if (mRegisterMap[registerRow + row][registerColumn + column])
299 {
300 return false;
301 }
302 }
303 }
304
305 return true;
306 }
307
insert(unsigned int registerRow,unsigned int registerColumn,const PackedVarying & packedVarying)308 void VaryingPacking::insert(unsigned int registerRow,
309 unsigned int registerColumn,
310 const PackedVarying &packedVarying)
311 {
312 unsigned int varyingRows = 0;
313 unsigned int varyingColumns = 0;
314
315 const sh::ShaderVariable &varying = packedVarying.varying();
316 ASSERT(!varying.isStruct());
317 GLenum transposedType = gl::TransposeMatrixType(varying.type);
318 varyingRows = gl::VariableRowCount(transposedType);
319 varyingColumns = gl::VariableColumnCount(transposedType);
320
321 PackedVaryingRegister registerInfo;
322 registerInfo.packedVarying = &packedVarying;
323 registerInfo.registerColumn = registerColumn;
324
325 // GLSL ES 3.10 section 4.3.6: Output variables cannot be arrays of arrays or arrays of
326 // structures, so we may use getBasicTypeElementCount().
327 const unsigned int arrayElementCount = varying.getBasicTypeElementCount();
328 for (unsigned int arrayElement = 0; arrayElement < arrayElementCount; ++arrayElement)
329 {
330 if (packedVarying.isArrayElement() && arrayElement != packedVarying.arrayIndex)
331 {
332 continue;
333 }
334 for (unsigned int varyingRow = 0; varyingRow < varyingRows; ++varyingRow)
335 {
336 registerInfo.registerRow = registerRow + (arrayElement * varyingRows) + varyingRow;
337 registerInfo.varyingRowIndex = varyingRow;
338 registerInfo.varyingArrayIndex = arrayElement;
339 // Do not record register info for builtins.
340 // TODO(jmadill): Clean this up.
341 if (!varying.isBuiltIn())
342 {
343 mRegisterList.push_back(registerInfo);
344 }
345
346 for (unsigned int columnIndex = 0; columnIndex < varyingColumns; ++columnIndex)
347 {
348 mRegisterMap[registerInfo.registerRow][registerColumn + columnIndex] = true;
349 }
350 }
351 }
352 }
353
packUserVarying(const ProgramVaryingRef & ref,VaryingUniqueFullNames * uniqueFullNames)354 void VaryingPacking::packUserVarying(const ProgramVaryingRef &ref,
355 VaryingUniqueFullNames *uniqueFullNames)
356 {
357 const sh::ShaderVariable *input = ref.frontShader;
358 const sh::ShaderVariable *output = ref.backShader;
359
360 // Will get the vertex shader interpolation by default.
361 sh::InterpolationType interpolation = input ? input->interpolation : output->interpolation;
362
363 VaryingInShaderRef frontVarying(ref.frontShaderStage, input);
364 VaryingInShaderRef backVarying(ref.backShaderStage, output);
365
366 mPackedVaryings.emplace_back(std::move(frontVarying), std::move(backVarying), interpolation);
367 if (input)
368 {
369 (*uniqueFullNames)[ref.frontShaderStage].insert(
370 mPackedVaryings.back().fullName(ref.frontShaderStage));
371 }
372 if (output)
373 {
374 (*uniqueFullNames)[ref.backShaderStage].insert(
375 mPackedVaryings.back().fullName(ref.backShaderStage));
376 }
377 }
378
packUserVaryingField(const ProgramVaryingRef & ref,GLuint fieldIndex,VaryingUniqueFullNames * uniqueFullNames)379 void VaryingPacking::packUserVaryingField(const ProgramVaryingRef &ref,
380 GLuint fieldIndex,
381 VaryingUniqueFullNames *uniqueFullNames)
382 {
383 const sh::ShaderVariable *input = ref.frontShader;
384 const sh::ShaderVariable *output = ref.backShader;
385
386 // Will get the vertex shader interpolation by default.
387 sh::InterpolationType interpolation = input ? input->interpolation : output->interpolation;
388
389 const sh::ShaderVariable *frontField = input ? &input->fields[fieldIndex] : nullptr;
390 const sh::ShaderVariable *backField = output ? &output->fields[fieldIndex] : nullptr;
391
392 VaryingInShaderRef frontVarying(ref.frontShaderStage, frontField);
393 VaryingInShaderRef backVarying(ref.backShaderStage, backField);
394
395 if (input)
396 {
397 ASSERT(!frontField->isStruct() && !frontField->isArray());
398 frontVarying.parentStructName = input->name;
399 frontVarying.parentStructMappedName = input->mappedName;
400 }
401 if (output)
402 {
403 ASSERT(!backField->isStruct() && !backField->isArray());
404 backVarying.parentStructName = output->name;
405 backVarying.parentStructMappedName = output->mappedName;
406 }
407
408 mPackedVaryings.emplace_back(std::move(frontVarying), std::move(backVarying), interpolation,
409 fieldIndex);
410
411 if (input)
412 {
413 (*uniqueFullNames)[ref.frontShaderStage].insert(
414 mPackedVaryings.back().fullName(ref.frontShaderStage));
415 }
416 if (output)
417 {
418 (*uniqueFullNames)[ref.backShaderStage].insert(
419 mPackedVaryings.back().fullName(ref.backShaderStage));
420 }
421 }
422
packUserVaryingTF(const ProgramVaryingRef & ref,size_t subscript)423 void VaryingPacking::packUserVaryingTF(const ProgramVaryingRef &ref, size_t subscript)
424 {
425 const sh::ShaderVariable *input = ref.frontShader;
426
427 VaryingInShaderRef frontVarying(ref.frontShaderStage, input);
428 VaryingInShaderRef backVarying(ref.backShaderStage, nullptr);
429
430 mPackedVaryings.emplace_back(std::move(frontVarying), std::move(backVarying),
431 input->interpolation);
432 mPackedVaryings.back().arrayIndex = static_cast<GLuint>(subscript);
433 }
434
packUserVaryingFieldTF(const ProgramVaryingRef & ref,const sh::ShaderVariable & field,GLuint fieldIndex)435 void VaryingPacking::packUserVaryingFieldTF(const ProgramVaryingRef &ref,
436 const sh::ShaderVariable &field,
437 GLuint fieldIndex)
438 {
439 const sh::ShaderVariable *input = ref.frontShader;
440
441 VaryingInShaderRef frontVarying(ref.frontShaderStage, &field);
442 VaryingInShaderRef backVarying(ref.backShaderStage, nullptr);
443
444 frontVarying.parentStructName = input->name;
445 frontVarying.parentStructMappedName = input->mappedName;
446
447 mPackedVaryings.emplace_back(std::move(frontVarying), std::move(backVarying),
448 input->interpolation, fieldIndex);
449 }
450
collectAndPackUserVaryings(gl::InfoLog & infoLog,const ProgramMergedVaryings & mergedVaryings,const std::vector<std::string> & tfVaryings,const bool isSeparableProgram)451 bool VaryingPacking::collectAndPackUserVaryings(gl::InfoLog &infoLog,
452 const ProgramMergedVaryings &mergedVaryings,
453 const std::vector<std::string> &tfVaryings,
454 const bool isSeparableProgram)
455 {
456 VaryingUniqueFullNames uniqueFullNames;
457 mPackedVaryings.clear();
458 clearRegisterMap();
459
460 for (const ProgramVaryingRef &ref : mergedVaryings)
461 {
462 const sh::ShaderVariable *input = ref.frontShader;
463 const sh::ShaderVariable *output = ref.backShader;
464
465 // Only pack statically used varyings that have a matched input or output, plus special
466 // builtins. Note that we pack all statically used user-defined varyings even if they are
467 // not active. GLES specs are a bit vague on whether it's allowed to only pack active
468 // varyings, though GLES 3.1 spec section 11.1.2.1 says that "device-dependent
469 // optimizations" may be used to make vertex shader outputs fit.
470 if ((input && output && output->staticUse) ||
471 (input && input->isBuiltIn() && input->active) ||
472 (output && output->isBuiltIn() && output->active) ||
473 (isSeparableProgram && ((input && input->active) || (output && output->active))))
474 {
475 const sh::ShaderVariable *varying = output ? output : input;
476
477 // Don't count gl_Position. Also don't count gl_PointSize for D3D9.
478 if (varying->name != "gl_Position" &&
479 !(varying->name == "gl_PointSize" &&
480 mPackMode == PackMode::ANGLE_NON_CONFORMANT_D3D9))
481 {
482 if (varying->isStruct())
483 {
484 ASSERT(!(varying->isArray() && varying == input));
485
486 for (GLuint fieldIndex = 0; fieldIndex < varying->fields.size(); ++fieldIndex)
487 {
488 packUserVaryingField(ref, fieldIndex, &uniqueFullNames);
489 }
490 if (input)
491 {
492 uniqueFullNames[ref.frontShaderStage].insert(input->name);
493 }
494 if (output)
495 {
496 uniqueFullNames[ref.backShaderStage].insert(output->name);
497 }
498 }
499 else
500 {
501 packUserVarying(ref, &uniqueFullNames);
502 }
503 continue;
504 }
505 }
506
507 // If the varying is not used in the input, we know it is inactive, unless it's a separable
508 // program, in which case the input shader may not exist in this program.
509 if (!input && !isSeparableProgram)
510 {
511 mInactiveVaryingMappedNames[ref.backShaderStage].push_back(output->mappedName);
512 continue;
513 }
514
515 // Keep Transform FB varyings in the merged list always.
516 for (const std::string &tfVarying : tfVaryings)
517 {
518 std::vector<unsigned int> subscripts;
519 std::string baseName = ParseResourceName(tfVarying, &subscripts);
520 size_t subscript = GL_INVALID_INDEX;
521 if (!subscripts.empty())
522 {
523 subscript = subscripts.back();
524 }
525 // Already packed for fragment shader.
526 if (uniqueFullNames[ref.frontShaderStage].count(tfVarying) > 0 ||
527 uniqueFullNames[ref.frontShaderStage].count(baseName) > 0)
528 {
529 continue;
530 }
531 if (input->isStruct())
532 {
533 GLuint fieldIndex = 0;
534 const sh::ShaderVariable *field = input->findField(tfVarying, &fieldIndex);
535 if (field != nullptr)
536 {
537 ASSERT(!field->isStruct() && !field->isArray());
538
539 packUserVaryingFieldTF(ref, *field, fieldIndex);
540 uniqueFullNames[ref.frontShaderStage].insert(tfVarying);
541 }
542 uniqueFullNames[ref.frontShaderStage].insert(input->name);
543 }
544 // Array as a whole and array element conflict has already been checked in
545 // linkValidateTransformFeedback.
546 else if (baseName == input->name)
547 {
548 // only pack varyings that are not builtins.
549 if (tfVarying.compare(0, 3, "gl_") != 0)
550 {
551 packUserVaryingTF(ref, subscript);
552 uniqueFullNames[ref.frontShaderStage].insert(tfVarying);
553 }
554 // Continue to match next array element for 'input' if the current match is array
555 // element.
556 if (subscript == GL_INVALID_INDEX)
557 {
558 break;
559 }
560 }
561 }
562
563 if (input && uniqueFullNames[ref.frontShaderStage].count(input->name) == 0)
564 {
565 mInactiveVaryingMappedNames[ref.frontShaderStage].push_back(input->mappedName);
566 }
567 if (output && uniqueFullNames[ref.backShaderStage].count(output->name) == 0)
568 {
569 mInactiveVaryingMappedNames[ref.backShaderStage].push_back(output->mappedName);
570 }
571 }
572
573 std::sort(mPackedVaryings.begin(), mPackedVaryings.end(), ComparePackedVarying);
574
575 return packUserVaryings(infoLog, mPackedVaryings);
576 }
577
578 // See comment on packVarying.
packUserVaryings(gl::InfoLog & infoLog,const std::vector<PackedVarying> & packedVaryings)579 bool VaryingPacking::packUserVaryings(gl::InfoLog &infoLog,
580 const std::vector<PackedVarying> &packedVaryings)
581 {
582 // "Variables are packed into the registers one at a time so that they each occupy a contiguous
583 // subrectangle. No splitting of variables is permitted."
584 for (const PackedVarying &packedVarying : packedVaryings)
585 {
586 if (!packVarying(packedVarying))
587 {
588 ShaderType eitherStage = packedVarying.frontVarying.varying
589 ? packedVarying.frontVarying.stage
590 : packedVarying.backVarying.stage;
591 infoLog << "Could not pack varying " << packedVarying.fullName(eitherStage);
592
593 // TODO(jmadill): Implement more sophisticated component packing in D3D9.
594 if (mPackMode == PackMode::ANGLE_NON_CONFORMANT_D3D9)
595 {
596 infoLog << "Note: Additional non-conformant packing restrictions are enforced on "
597 "D3D9.";
598 }
599
600 return false;
601 }
602 }
603
604 // Sort the packed register list
605 std::sort(mRegisterList.begin(), mRegisterList.end());
606
607 return true;
608 }
609 } // namespace gl
610