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