xref: /aosp_15_r20/external/skia/src/gpu/graphite/UniformManager.cpp (revision c8dee2aa9b3f27cf6c858bd81872bdeb2c07ed17)
1 /*
2  * Copyright 2021 Google LLC
3  *
4  * Use of this source code is governed by a BSD-style license that can be
5  * found in the LICENSE file.
6  */
7 
8 #include "src/gpu/graphite/UniformManager.h"
9 
10 #include "src/gpu/graphite/PipelineData.h"
11 
12 // ensure that these types are the sizes the uniform data is expecting
13 static_assert(sizeof(int32_t) == 4);
14 static_assert(sizeof(float) == 4);
15 static_assert(sizeof(SkHalf) == 2);
16 
17 namespace skgpu::graphite {
18 
advanceOffset(SkSLType type,int count)19 int UniformOffsetCalculator::advanceOffset(SkSLType type, int count) {
20     SkASSERT(SkSLTypeCanBeUniformValue(type));
21 
22     int dimension = SkSLTypeMatrixSize(type);
23     if (dimension > 0) {
24         // All SkSL matrices are square and can be interpreted as an array of column vectors
25         count = std::max(count, 1) * dimension;
26     } else {
27         dimension = SkSLTypeVecLength(type);
28     }
29     SkASSERT(1 <= dimension && dimension <= 4);
30 
31     // Bump dimension up to 4 if the array or vec3 consumes 4 primitives per element
32     // NOTE: This affects the size, alignment already rounds up to a power of 2 automatically.
33     const bool isArray = count > Uniform::kNonArray;
34     if ((isArray && LayoutRules::AlignArraysAsVec4(fLayout)) ||
35         (dimension == 3 && (isArray || LayoutRules::PadVec3Size(fLayout)))) {
36         dimension = 4;
37     }
38 
39     const int primitiveSize = LayoutRules::UseFullPrecision(fLayout) ||
40                               SkSLTypeIsFullPrecisionNumericType(type) ? 4 : 2;
41     const int align = SkNextPow2(dimension) * primitiveSize;
42     const int alignedOffset = SkAlignTo(fOffset, align);
43     fOffset = alignedOffset + dimension * primitiveSize * std::max(count, 1);
44     fReqAlignment = std::max(fReqAlignment, align);
45 
46     return alignedOffset;
47 }
48 
advanceStruct(const UniformOffsetCalculator & substruct,int count)49 int UniformOffsetCalculator::advanceStruct(const UniformOffsetCalculator& substruct, int count) {
50     SkASSERT(substruct.fLayout == fLayout); // Invalid if the layout rules used aren't consistent
51 
52     // If array element strides are forced to 16-byte alignment, structs must also have their
53     // base alignment rounded up to 16-byte alignment, which should have been accounted for in
54     // 'substruct's constructor.
55     const int baseAlignment = substruct.requiredAlignment();
56     SkASSERT(!LayoutRules::AlignArraysAsVec4(fLayout) || SkIsAlign16(baseAlignment));
57 
58     // Per layout rule #9, the struct size must be padded to its base alignment
59     // (see https://registry.khronos.org/OpenGL/specs/gl/glspec45.core.pdf#page=159).
60     const int alignedSize = SkAlignTo(substruct.size(), baseAlignment);
61 
62     const int alignedOffset = SkAlignTo(fOffset, baseAlignment);
63     fOffset = alignedOffset + alignedSize * std::max(count, 1);
64     fReqAlignment = std::max(fReqAlignment, baseAlignment);
65 
66     return alignedOffset;
67 }
68 
69 //////////////////////////////////////////////////////////////////////////////
70 
resetWithNewLayout(Layout layout)71 void UniformManager::resetWithNewLayout(Layout layout) {
72     fStorage.clear();
73     fLayout = layout;
74     fReqAlignment = 0;
75     fStructBaseAlignment = 0;
76     fWrotePaintColor = false;
77 
78 #ifdef SK_DEBUG
79     fOffsetCalculator = UniformOffsetCalculator::ForTopLevel(layout);
80     fSubstructCalculator = {};
81     fExpectedUniforms = {};
82     fExpectedUniformIndex = 0;
83 #endif
84 }
85 
adjust_for_matrix_type(SkSLType type,int count)86 static std::pair<SkSLType, int> adjust_for_matrix_type(SkSLType type, int count) {
87     // All Layouts flatten matrices and arrays of matrices into arrays of columns, so update
88     // 'type' to be the column type and either multiply 'count' by the number of columns for
89     // arrays of matrices, or set to exactly the number of columns for a "non-array" matrix.
90     switch(type) {
91         case SkSLType::kFloat2x2: return {SkSLType::kFloat2, 2*std::max(1, count)};
92         case SkSLType::kFloat3x3: return {SkSLType::kFloat3, 3*std::max(1, count)};
93         case SkSLType::kFloat4x4: return {SkSLType::kFloat4, 4*std::max(1, count)};
94 
95         case SkSLType::kHalf2x2:  return {SkSLType::kHalf2,  2*std::max(1, count)};
96         case SkSLType::kHalf3x3:  return {SkSLType::kHalf3,  3*std::max(1, count)};
97         case SkSLType::kHalf4x4:  return {SkSLType::kHalf4,  4*std::max(1, count)};
98 
99         // Otherwise leave type and count alone.
100         default:                  return {type, count};
101     }
102 }
103 
write(const Uniform & u,const void * data)104 void UniformManager::write(const Uniform& u, const void* data) {
105     SkASSERT(SkSLTypeCanBeUniformValue(u.type()));
106     SkASSERT(!u.isPaintColor()); // Must go through writePaintColor()
107 
108     auto [type, count] = adjust_for_matrix_type(u.type(), u.count());
109     SkASSERT(SkSLTypeMatrixSize(type) < 0); // Matrix types should have been flattened
110 
111     const bool fullPrecision = LayoutRules::UseFullPrecision(fLayout) || !IsHalfVector(type);
112     if (count == Uniform::kNonArray) {
113         if (fullPrecision) {
114             switch(SkSLTypeVecLength(type)) {
115                 case 1: this->write<1, /*Half=*/false>(data, type); break;
116                 case 2: this->write<2, /*Half=*/false>(data, type); break;
117                 case 3: this->write<3, /*Half=*/false>(data, type); break;
118                 case 4: this->write<4, /*Half=*/false>(data, type); break;
119             }
120         } else {
121             switch(SkSLTypeVecLength(type)) {
122                 case 1: this->write<1, /*Half=*/true>(data, type); break;
123                 case 2: this->write<2, /*Half=*/true>(data, type); break;
124                 case 3: this->write<3, /*Half=*/true>(data, type); break;
125                 case 4: this->write<4, /*Half=*/true>(data, type); break;
126             }
127         }
128     } else {
129         if (fullPrecision) {
130             switch(SkSLTypeVecLength(type)) {
131                 case 1: this->writeArray<1, /*Half=*/false>(data, count, type); break;
132                 case 2: this->writeArray<2, /*Half=*/false>(data, count, type); break;
133                 case 3: this->writeArray<3, /*Half=*/false>(data, count, type); break;
134                 case 4: this->writeArray<4, /*Half=*/false>(data, count, type); break;
135             }
136         } else {
137             switch(SkSLTypeVecLength(type)) {
138                 case 1: this->writeArray<1, /*Half=*/true>(data, count, type); break;
139                 case 2: this->writeArray<2, /*Half=*/true>(data, count, type); break;
140                 case 3: this->writeArray<3, /*Half=*/true>(data, count, type); break;
141                 case 4: this->writeArray<4, /*Half=*/true>(data, count, type); break;
142             }
143         }
144     }
145 }
146 
147 #if defined(SK_DEBUG)
148 
checkBeginStruct(int baseAlignment)149 bool UniformManager::checkBeginStruct(int baseAlignment) {
150     if (fExpectedUniformIndex > 0) {
151         return false; // Wrote a struct field before the struct was started
152     }
153     if (fSubstructCalculator.layout() == Layout::kInvalid) {
154         return false; // Not expecting to start a struct
155     }
156     if (fStructBaseAlignment > 0) {
157         return false; // Somehow already started a substruct
158     }
159     if (fExpectedUniforms.empty()) {
160         return false; // Empty substructs are not allowed
161     }
162 
163     // Assume the expected uniforms describe the whole substruct
164     auto structCalculator = UniformOffsetCalculator::ForStruct(fLayout);
165     for (const Uniform& f : fExpectedUniforms) {
166         structCalculator.advanceOffset(f.type(), f.count());
167     }
168     if (baseAlignment != structCalculator.requiredAlignment()) {
169         return false;
170     }
171     fSubstructStartingOffset = fOffsetCalculator.advanceStruct(structCalculator);
172     return true;
173 }
174 
checkEndStruct()175 bool UniformManager::checkEndStruct() {
176     if (fExpectedUniformIndex != (int) fExpectedUniforms.size()) {
177         return false; // Didn't write all the expected fields before ending the struct
178     }
179     if (fSubstructCalculator.layout() == Layout::kInvalid) {
180         return false; // Not expecting a struct
181     }
182     if (fStructBaseAlignment <= 0) {
183         return false; // Missing a beginStruct()
184     }
185 
186     // `fStructCalculator` should now have been advanced equivalently to the substruct calculator
187     // used in checkBeginStruct() to calculate the expected starting offset.
188     const int structSize = SkAlignTo(fSubstructCalculator.size(),
189                                      fSubstructCalculator.requiredAlignment());
190     if (fStorage.size() != fSubstructStartingOffset + structSize) {
191         return false; // Somehow didn't end on the correct boundary
192     }
193     if (fReqAlignment != fOffsetCalculator.requiredAlignment() ||
194         fReqAlignment < fSubstructCalculator.requiredAlignment()) {
195         return false; // UniformManager's alignment got out of sync with expected alignment
196     }
197 
198     // Reset the substruct calculator to mark that the struct has been completed
199     fSubstructCalculator = {};
200     return true;
201 }
202 
checkExpected(const void * dst,SkSLType type,int count)203 bool UniformManager::checkExpected(const void* dst, SkSLType type, int count) {
204     if (fExpectedUniformIndex >= SkTo<int>(fExpectedUniforms.size())) {
205         // A write() outside of a UniformExpectationsVisitor or too many uniforms written for what
206         // is expected.
207         return false;
208     }
209     if (fSubstructCalculator.layout() != Layout::kInvalid) {
210         if (fStructBaseAlignment <= 0) {
211             // A write() that should be inside a struct, but missing a call to beginStruct()
212             return false;
213         }
214 
215     } else if (fStructBaseAlignment > 0) {
216         // A substruct was started when it shouldn't have been
217         return false;
218     }
219 
220     const Uniform& expected = fExpectedUniforms[fExpectedUniformIndex++];
221     if (!SkSLTypeCanBeUniformValue(expected.type())) {
222         // Not all types are supported as uniforms or supported by UniformManager
223         return false;
224     }
225 
226     auto [expectedType, expectedCount] = adjust_for_matrix_type(expected.type(), expected.count());
227     if (expectedType != type || expectedCount != count) {
228         return false;
229     }
230 
231     if (dst) {
232         // If we have 'dst', it's the aligned starting offset of the uniform being checked, so
233         // subtracting the address of the first byte in fStorage gives us the offset.
234         int offset = static_cast<int>(reinterpret_cast<intptr_t>(dst) -
235                                       reinterpret_cast<intptr_t>(fStorage.data()));
236 
237         if (fSubstructCalculator.layout() == Layout::kInvalid) {
238             // Pass original expected type and count to the offset calculator for validation.
239             if (offset != fOffsetCalculator.advanceOffset(expected.type(), expected.count())) {
240                 return false;
241             }
242             if (fReqAlignment != fOffsetCalculator.requiredAlignment()) {
243                 return false;
244             }
245 
246             // And if it is the paint color uniform, we should not have already written it
247             return !(fWrotePaintColor && expected.isPaintColor());
248         } else {
249             int relOffset = fSubstructCalculator.advanceOffset(expected.type(), expected.count());
250             if (offset != fSubstructStartingOffset + relOffset) {
251                 return false;
252             }
253             // The overall required alignment might already be higher from prior fields, but should
254             // be at least what's required by the substruct.
255             if (fReqAlignment < fSubstructCalculator.requiredAlignment()) {
256                 return false;
257             }
258 
259             // And it should not be a paint color uniform within a substruct
260             return !expected.isPaintColor();
261         }
262     } else {
263         // If 'dst' is null, it's an already-visited paint color uniform, so it's not being written
264         // and not changing the offset, and should not be part of a substruct.
265         SkASSERT(fWrotePaintColor);
266         SkASSERT(fSubstructCalculator.layout() == Layout::kInvalid);
267         return expected.isPaintColor();
268     }
269 }
270 
isReset() const271 bool UniformManager::isReset() const {
272     return fStorage.empty();
273 }
274 
setExpectedUniforms(SkSpan<const Uniform> expected,bool isSubstruct)275 void UniformManager::setExpectedUniforms(SkSpan<const Uniform> expected, bool isSubstruct) {
276     fExpectedUniforms = expected;
277     fExpectedUniformIndex = 0;
278 
279     if (isSubstruct) {
280         // Start collecting the subsequent uniforms with a 0-based offset to determine their
281         // relative layout and required base alignment of the entire struct.
282         fSubstructCalculator = UniformOffsetCalculator::ForStruct(fLayout);
283     } else {
284         // Expected uniforms will advance fOffsetCalculator directly
285         SkASSERT(fSubstructCalculator.layout() == Layout::kInvalid);
286     }
287 }
288 
doneWithExpectedUniforms()289 void UniformManager::doneWithExpectedUniforms() {
290     SkASSERT(fExpectedUniformIndex == static_cast<int>(fExpectedUniforms.size()));
291     // Any expected substruct should have been ended and validated inside endStruct(); if this fails
292     // it means there is a missing endStruct().
293     SkASSERT(fSubstructCalculator.layout() == Layout::kInvalid);
294     fExpectedUniforms = {};
295 }
296 
297 #endif // SK_DEBUG
298 
299 } // namespace skgpu::graphite
300