/src/skia/src/gpu/graphite/UniformManager.cpp
Line | Count | Source (jump to first uncovered line) |
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 | | |
19 | 0 | int UniformOffsetCalculator::advanceOffset(SkSLType type, int count) { |
20 | 0 | SkASSERT(SkSLTypeCanBeUniformValue(type)); |
21 | |
|
22 | 0 | int dimension = SkSLTypeMatrixSize(type); |
23 | 0 | if (dimension > 0) { |
24 | | // All SkSL matrices are square and can be interpreted as an array of column vectors |
25 | 0 | count = std::max(count, 1) * dimension; |
26 | 0 | } else { |
27 | 0 | dimension = SkSLTypeVecLength(type); |
28 | 0 | } |
29 | 0 | 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 | 0 | const bool isArray = count > Uniform::kNonArray; |
34 | 0 | if ((isArray && LayoutRules::AlignArraysAsVec4(fLayout)) || |
35 | 0 | (dimension == 3 && (isArray || LayoutRules::PadVec3Size(fLayout)))) { |
36 | 0 | dimension = 4; |
37 | 0 | } |
38 | |
|
39 | 0 | const int primitiveSize = LayoutRules::UseFullPrecision(fLayout) || |
40 | 0 | SkSLTypeIsFullPrecisionNumericType(type) ? 4 : 2; |
41 | 0 | const int align = SkNextPow2(dimension) * primitiveSize; |
42 | 0 | const int alignedOffset = SkAlignTo(fOffset, align); |
43 | 0 | fOffset = alignedOffset + dimension * primitiveSize * std::max(count, 1); |
44 | 0 | fReqAlignment = std::max(fReqAlignment, align); |
45 | |
|
46 | 0 | return alignedOffset; |
47 | 0 | } Unexecuted instantiation: skgpu::graphite::UniformOffsetCalculator::advanceOffset(SkSLType, int) Unexecuted instantiation: skgpu::graphite::UniformOffsetCalculator::advanceOffset(SkSLType, int) |
48 | | |
49 | | ////////////////////////////////////////////////////////////////////////////// |
50 | | |
51 | 0 | UniformDataBlock UniformManager::finishUniformDataBlock() { |
52 | 0 | size_t size = SkAlignTo(fStorage.size(), fReqAlignment); |
53 | 0 | size_t paddingSize = size - fStorage.size(); |
54 | 0 | if (paddingSize > 0) { |
55 | 0 | char* padding = fStorage.append(paddingSize); |
56 | 0 | memset(padding, 0, paddingSize); |
57 | 0 | } |
58 | 0 | return UniformDataBlock(SkSpan(fStorage.begin(), size)); |
59 | 0 | } |
60 | | |
61 | 0 | void UniformManager::resetWithNewLayout(Layout layout) { |
62 | 0 | fStorage.clear(); |
63 | 0 | fLayout = layout; |
64 | 0 | fReqAlignment = 0; |
65 | 0 | fWrotePaintColor = false; |
66 | |
|
67 | | #ifdef SK_DEBUG |
68 | | fOffsetCalculator = UniformOffsetCalculator(layout, 0); |
69 | | fExpectedUniforms = {}; |
70 | | fExpectedUniformIndex = 0; |
71 | | #endif |
72 | 0 | } |
73 | | |
74 | 0 | static std::pair<SkSLType, int> adjust_for_matrix_type(SkSLType type, int count) { |
75 | | // All Layouts flatten matrices and arrays of matrices into arrays of columns, so update |
76 | | // 'type' to be the column type and either multiply 'count' by the number of columns for |
77 | | // arrays of matrices, or set to exactly the number of columns for a "non-array" matrix. |
78 | 0 | switch(type) { |
79 | 0 | case SkSLType::kFloat2x2: return {SkSLType::kFloat2, 2*std::max(1, count)}; |
80 | 0 | case SkSLType::kFloat3x3: return {SkSLType::kFloat3, 3*std::max(1, count)}; |
81 | 0 | case SkSLType::kFloat4x4: return {SkSLType::kFloat4, 4*std::max(1, count)}; |
82 | | |
83 | 0 | case SkSLType::kHalf2x2: return {SkSLType::kHalf2, 2*std::max(1, count)}; |
84 | 0 | case SkSLType::kHalf3x3: return {SkSLType::kHalf3, 3*std::max(1, count)}; |
85 | 0 | case SkSLType::kHalf4x4: return {SkSLType::kHalf4, 4*std::max(1, count)}; |
86 | | |
87 | | // Otherwise leave type and count alone. |
88 | 0 | default: return {type, count}; |
89 | 0 | } |
90 | 0 | } |
91 | | |
92 | 0 | void UniformManager::write(const Uniform& u, const void* data) { |
93 | 0 | SkASSERT(SkSLTypeCanBeUniformValue(u.type())); |
94 | 0 | SkASSERT(!u.isPaintColor()); // Must go through writePaintColor() |
95 | |
|
96 | 0 | auto [type, count] = adjust_for_matrix_type(u.type(), u.count()); |
97 | 0 | SkASSERT(SkSLTypeMatrixSize(type) < 0); // Matrix types should have been flattened |
98 | |
|
99 | 0 | const bool fullPrecision = LayoutRules::UseFullPrecision(fLayout) || !IsHalfVector(type); |
100 | 0 | if (count == Uniform::kNonArray) { |
101 | 0 | if (fullPrecision) { |
102 | 0 | switch(SkSLTypeVecLength(type)) { |
103 | 0 | case 1: this->write<1, /*Half=*/false>(data, type); break; |
104 | 0 | case 2: this->write<2, /*Half=*/false>(data, type); break; |
105 | 0 | case 3: this->write<3, /*Half=*/false>(data, type); break; |
106 | 0 | case 4: this->write<4, /*Half=*/false>(data, type); break; |
107 | 0 | } |
108 | 0 | } else { |
109 | 0 | switch(SkSLTypeVecLength(type)) { |
110 | 0 | case 1: this->write<1, /*Half=*/true>(data, type); break; |
111 | 0 | case 2: this->write<2, /*Half=*/true>(data, type); break; |
112 | 0 | case 3: this->write<3, /*Half=*/true>(data, type); break; |
113 | 0 | case 4: this->write<4, /*Half=*/true>(data, type); break; |
114 | 0 | } |
115 | 0 | } |
116 | 0 | } else { |
117 | 0 | if (fullPrecision) { |
118 | 0 | switch(SkSLTypeVecLength(type)) { |
119 | 0 | case 1: this->writeArray<1, /*Half=*/false>(data, count, type); break; |
120 | 0 | case 2: this->writeArray<2, /*Half=*/false>(data, count, type); break; |
121 | 0 | case 3: this->writeArray<3, /*Half=*/false>(data, count, type); break; |
122 | 0 | case 4: this->writeArray<4, /*Half=*/false>(data, count, type); break; |
123 | 0 | } |
124 | 0 | } else { |
125 | 0 | switch(SkSLTypeVecLength(type)) { |
126 | 0 | case 1: this->writeArray<1, /*Half=*/true>(data, count, type); break; |
127 | 0 | case 2: this->writeArray<2, /*Half=*/true>(data, count, type); break; |
128 | 0 | case 3: this->writeArray<3, /*Half=*/true>(data, count, type); break; |
129 | 0 | case 4: this->writeArray<4, /*Half=*/true>(data, count, type); break; |
130 | 0 | } |
131 | 0 | } |
132 | 0 | } |
133 | 0 | } Unexecuted instantiation: skgpu::graphite::UniformManager::write(skgpu::graphite::Uniform const&, void const*) Unexecuted instantiation: skgpu::graphite::UniformManager::write(skgpu::graphite::Uniform const&, void const*) |
134 | | |
135 | | #ifdef SK_DEBUG |
136 | | |
137 | 0 | bool UniformManager::checkExpected(const void* dst, SkSLType type, int count) { |
138 | 0 | if (fExpectedUniformIndex >= (int) fExpectedUniforms.size()) { |
139 | | // A write() outside of a UniformExpectationsVisitor or too many uniforms written for what |
140 | | // is expected. |
141 | 0 | return false; |
142 | 0 | } |
143 | | |
144 | 0 | const Uniform& expected = fExpectedUniforms[fExpectedUniformIndex++]; |
145 | 0 | if (!SkSLTypeCanBeUniformValue(expected.type())) { |
146 | | // Not all types are supported as uniforms or supported by UniformManager |
147 | 0 | return false; |
148 | 0 | } |
149 | | |
150 | 0 | auto [expectedType, expectedCount] = adjust_for_matrix_type(expected.type(), expected.count()); |
151 | 0 | if (expectedType != type || expectedCount != count) { |
152 | 0 | return false; |
153 | 0 | } |
154 | | |
155 | 0 | if (dst) { |
156 | | // If we have 'dst', it's the aligned starting offset of the uniform being checked, so |
157 | | // subtracting the address of the first byte in fStorage gives us the offset. |
158 | 0 | int offset = static_cast<int>(reinterpret_cast<intptr_t>(dst) - |
159 | 0 | reinterpret_cast<intptr_t>(fStorage.data())); |
160 | | // Pass original expected type and count to the offset calculator for validation. |
161 | 0 | if (offset != fOffsetCalculator.advanceOffset(expected.type(), expected.count())) { |
162 | 0 | return false; |
163 | 0 | } |
164 | 0 | if (fReqAlignment != fOffsetCalculator.requiredAlignment()) { |
165 | 0 | return false; |
166 | 0 | } |
167 | | // And if it is the paint color uniform, we should not have already written it |
168 | 0 | return !(fWrotePaintColor && expected.isPaintColor()); |
169 | 0 | } else { |
170 | | // If 'dst' is null, it's an already-visited paint color uniform, so it's not being written |
171 | | // and not changing the offset. |
172 | 0 | SkASSERT(fWrotePaintColor); |
173 | 0 | return expected.isPaintColor(); |
174 | 0 | } |
175 | 0 | } |
176 | | |
177 | 0 | bool UniformManager::isReset() const { |
178 | 0 | return fStorage.empty(); |
179 | 0 | } |
180 | | |
181 | 0 | void UniformManager::setExpectedUniforms(SkSpan<const Uniform> expected) { |
182 | 0 | fExpectedUniforms = expected; |
183 | 0 | fExpectedUniformIndex = 0; |
184 | 0 | } |
185 | | |
186 | 0 | void UniformManager::doneWithExpectedUniforms() { |
187 | 0 | SkASSERT(fExpectedUniformIndex == static_cast<int>(fExpectedUniforms.size())); |
188 | 0 | fExpectedUniforms = {}; |
189 | 0 | } |
190 | | |
191 | | #endif // SK_DEBUG |
192 | | |
193 | | } // namespace skgpu::graphite |