xref: /aosp_15_r20/external/skia/src/gpu/graphite/UniformManager.h (revision c8dee2aa9b3f27cf6c858bd81872bdeb2c07ed17)
1*c8dee2aaSAndroid Build Coastguard Worker /*
2*c8dee2aaSAndroid Build Coastguard Worker  * Copyright 2021 Google LLC
3*c8dee2aaSAndroid Build Coastguard Worker  *
4*c8dee2aaSAndroid Build Coastguard Worker  * Use of this source code is governed by a BSD-style license that can be
5*c8dee2aaSAndroid Build Coastguard Worker  * found in the LICENSE file.
6*c8dee2aaSAndroid Build Coastguard Worker  */
7*c8dee2aaSAndroid Build Coastguard Worker 
8*c8dee2aaSAndroid Build Coastguard Worker #ifndef skgpu_UniformManager_DEFINED
9*c8dee2aaSAndroid Build Coastguard Worker #define skgpu_UniformManager_DEFINED
10*c8dee2aaSAndroid Build Coastguard Worker 
11*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkM44.h"
12*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkMatrix.h"
13*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkPoint.h"
14*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkPoint3.h"
15*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkRect.h"
16*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkRefCnt.h"
17*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkSize.h"
18*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkSpan.h"
19*c8dee2aaSAndroid Build Coastguard Worker #include "include/private/SkColorData.h"
20*c8dee2aaSAndroid Build Coastguard Worker #include "include/private/base/SkAlign.h"
21*c8dee2aaSAndroid Build Coastguard Worker #include "include/private/base/SkTDArray.h"
22*c8dee2aaSAndroid Build Coastguard Worker #include "src/base/SkHalf.h"
23*c8dee2aaSAndroid Build Coastguard Worker #include "src/base/SkMathPriv.h"
24*c8dee2aaSAndroid Build Coastguard Worker #include "src/core/SkMatrixPriv.h"
25*c8dee2aaSAndroid Build Coastguard Worker #include "src/core/SkSLTypeShared.h"
26*c8dee2aaSAndroid Build Coastguard Worker #include "src/gpu/graphite/ResourceTypes.h"
27*c8dee2aaSAndroid Build Coastguard Worker #include "src/gpu/graphite/Uniform.h"
28*c8dee2aaSAndroid Build Coastguard Worker 
29*c8dee2aaSAndroid Build Coastguard Worker #include <algorithm>
30*c8dee2aaSAndroid Build Coastguard Worker #include <memory>
31*c8dee2aaSAndroid Build Coastguard Worker 
32*c8dee2aaSAndroid Build Coastguard Worker namespace skgpu::graphite {
33*c8dee2aaSAndroid Build Coastguard Worker 
34*c8dee2aaSAndroid Build Coastguard Worker class UniformDataBlock;
35*c8dee2aaSAndroid Build Coastguard Worker 
36*c8dee2aaSAndroid Build Coastguard Worker /**
37*c8dee2aaSAndroid Build Coastguard Worker  * Layout::kStd140
38*c8dee2aaSAndroid Build Coastguard Worker  * ===============
39*c8dee2aaSAndroid Build Coastguard Worker  *
40*c8dee2aaSAndroid Build Coastguard Worker  * From OpenGL Specification Section 7.6.2.2 "Standard Uniform Block Layout"
41*c8dee2aaSAndroid Build Coastguard Worker  * [https://registry.khronos.org/OpenGL/specs/gl/glspec45.core.pdf#page=159]:
42*c8dee2aaSAndroid Build Coastguard Worker  *  1. If the member is a scalar consuming N basic machine units, the base alignment is N.
43*c8dee2aaSAndroid Build Coastguard Worker  *  2. If the member is a two- or four-component vector with components consuming N basic machine
44*c8dee2aaSAndroid Build Coastguard Worker  *     units, the base alignment is 2N or 4N, respectively.
45*c8dee2aaSAndroid Build Coastguard Worker  *  3. If the member is a three-component vector with components consuming N
46*c8dee2aaSAndroid Build Coastguard Worker  *     basic machine units, the base alignment is 4N.
47*c8dee2aaSAndroid Build Coastguard Worker  *  4. If the member is an array of scalars or vectors, the base alignment and array
48*c8dee2aaSAndroid Build Coastguard Worker  *     stride are set to match the base alignment of a single array element, according
49*c8dee2aaSAndroid Build Coastguard Worker  *     to rules (1), (2), and (3), and rounded up to the base alignment of a vec4. The
50*c8dee2aaSAndroid Build Coastguard Worker  *     array may have padding at the end; the base offset of the member following
51*c8dee2aaSAndroid Build Coastguard Worker  *     the array is rounded up to the next multiple of the base alignment.
52*c8dee2aaSAndroid Build Coastguard Worker  *  5. If the member is a column-major matrix with C columns and R rows, the
53*c8dee2aaSAndroid Build Coastguard Worker  *     matrix is stored identically to an array of C column vectors with R components each,
54*c8dee2aaSAndroid Build Coastguard Worker  *     according to rule (4).
55*c8dee2aaSAndroid Build Coastguard Worker  *  6. If the member is an array of S column-major matrices with C columns and
56*c8dee2aaSAndroid Build Coastguard Worker  *     R rows, the matrix is stored identically to a row of S × C column vectors
57*c8dee2aaSAndroid Build Coastguard Worker  *     with R components each, according to rule (4).
58*c8dee2aaSAndroid Build Coastguard Worker  *  7. If the member is a row-major matrix with C columns and R rows, the matrix
59*c8dee2aaSAndroid Build Coastguard Worker  *     is stored identically to an array of R row vectors with C components each,
60*c8dee2aaSAndroid Build Coastguard Worker  *     according to rule (4).
61*c8dee2aaSAndroid Build Coastguard Worker  *  8. If the member is an array of S row-major matrices with C columns and R
62*c8dee2aaSAndroid Build Coastguard Worker  *     rows, the matrix is stored identically to a row of S × R row vectors with C
63*c8dee2aaSAndroid Build Coastguard Worker  *    components each, according to rule (4).
64*c8dee2aaSAndroid Build Coastguard Worker  *  9. If the member is a structure, the base alignment of the structure is N, where
65*c8dee2aaSAndroid Build Coastguard Worker  *     N is the largest base alignment value of any of its members, and rounded
66*c8dee2aaSAndroid Build Coastguard Worker  *     up to the base alignment of a vec4. The individual members of this substructure are then
67*c8dee2aaSAndroid Build Coastguard Worker  *     assigned offsets by applying this set of rules recursively,
68*c8dee2aaSAndroid Build Coastguard Worker  *     where the base offset of the first member of the sub-structure is equal to the
69*c8dee2aaSAndroid Build Coastguard Worker  *     aligned offset of the structure. The structure may have padding at the end;
70*c8dee2aaSAndroid Build Coastguard Worker  *     the base offset of the member following the sub-structure is rounded up to
71*c8dee2aaSAndroid Build Coastguard Worker  *     the next multiple of the base alignment of the structure.
72*c8dee2aaSAndroid Build Coastguard Worker  * 10. If the member is an array of S structures, the S elements of the array are laid
73*c8dee2aaSAndroid Build Coastguard Worker  *     out in order, according to rule (9).
74*c8dee2aaSAndroid Build Coastguard Worker  *
75*c8dee2aaSAndroid Build Coastguard Worker  * Layout::kStd430
76*c8dee2aaSAndroid Build Coastguard Worker  * ===============
77*c8dee2aaSAndroid Build Coastguard Worker  *
78*c8dee2aaSAndroid Build Coastguard Worker  * When using the std430 storage layout, shader storage blocks will be laid out in buffer storage
79*c8dee2aaSAndroid Build Coastguard Worker  * identically to uniform and shader storage blocks using the std140 layout, except that the base
80*c8dee2aaSAndroid Build Coastguard Worker  * alignment and stride of arrays of scalars and vectors in rule 4 and of structures in rule 9 are
81*c8dee2aaSAndroid Build Coastguard Worker  * not rounded up a multiple of the base alignment of a vec4.
82*c8dee2aaSAndroid Build Coastguard Worker  *
83*c8dee2aaSAndroid Build Coastguard Worker  * NOTE: While not explicitly stated, the layout rules for WebGPU and WGSL are identical to std430
84*c8dee2aaSAndroid Build Coastguard Worker  * for SSBOs and nearly identical to std140 for UBOs. The default mat2x2 type is treated as two
85*c8dee2aaSAndroid Build Coastguard Worker  * float2's (not an array), so its size is 16 and alignment is 8 (vs. a size of 32 and alignment of
86*c8dee2aaSAndroid Build Coastguard Worker  * 16 in std140). When emitting WGSL from SkSL, prepareUniformPolyfillsForInterfaceBlock() defined
87*c8dee2aaSAndroid Build Coastguard Worker  * in WGSLCodeGenerator, will modify the type declaration to match std140 exactly. This allows the
88*c8dee2aaSAndroid Build Coastguard Worker  * UniformManager and UniformOffsetCalculator to avoid having WebGPU-specific layout rules
89*c8dee2aaSAndroid Build Coastguard Worker  * (whereas SkSL::MemoryLayout has more complete rules).
90*c8dee2aaSAndroid Build Coastguard Worker  *
91*c8dee2aaSAndroid Build Coastguard Worker  * Layout::kMetal
92*c8dee2aaSAndroid Build Coastguard Worker  * ===============
93*c8dee2aaSAndroid Build Coastguard Worker  *
94*c8dee2aaSAndroid Build Coastguard Worker  * SkSL converts its types to the non-packed SIMD vector types in MSL. The size and alignment rules
95*c8dee2aaSAndroid Build Coastguard Worker  * are equivalent to std430 with the exception of half3 and float3. In std430, the size consumed
96*c8dee2aaSAndroid Build Coastguard Worker  * by non-array uniforms of these types is 3N while Metal consumes 4N (which is equal to the
97*c8dee2aaSAndroid Build Coastguard Worker  * alignment of a vec3 in both Layouts).
98*c8dee2aaSAndroid Build Coastguard Worker  *
99*c8dee2aaSAndroid Build Coastguard Worker  * Half vs. Float Uniforms
100*c8dee2aaSAndroid Build Coastguard Worker  * =======================
101*c8dee2aaSAndroid Build Coastguard Worker  *
102*c8dee2aaSAndroid Build Coastguard Worker  * Regardless of the precision when the shader is executed, std140 and std430 layouts consume
103*c8dee2aaSAndroid Build Coastguard Worker  * "half"-based uniforms in full 32-bit precision. Metal consumes "half"-based uniforms expecting
104*c8dee2aaSAndroid Build Coastguard Worker  * them to have already been converted to f16. WebGPU has an extension to support f16 types, which
105*c8dee2aaSAndroid Build Coastguard Worker  * behave like this, but we do not currently utilize it.
106*c8dee2aaSAndroid Build Coastguard Worker  *
107*c8dee2aaSAndroid Build Coastguard Worker  * The rules for std430 can be easily extended to f16 by applying N = 2 instead of N = 4 for the
108*c8dee2aaSAndroid Build Coastguard Worker  * base primitive alignment.
109*c8dee2aaSAndroid Build Coastguard Worker  *
110*c8dee2aaSAndroid Build Coastguard Worker  * NOTE: This could also apply to the int vs. short or uint vs. ushort types, but these smaller
111*c8dee2aaSAndroid Build Coastguard Worker  * integer types are not supported on all platforms as uniforms. We disallow short integer uniforms
112*c8dee2aaSAndroid Build Coastguard Worker  * entirely, and if the data savings are required, packing should be implemented manually.
113*c8dee2aaSAndroid Build Coastguard Worker  * Short integer vertex attributes are supported when the vector type lets it pack into 32 bits
114*c8dee2aaSAndroid Build Coastguard Worker  * (e.g. int16x2 or int8x4).
115*c8dee2aaSAndroid Build Coastguard Worker  *
116*c8dee2aaSAndroid Build Coastguard Worker  *
117*c8dee2aaSAndroid Build Coastguard Worker  * Generalized Layout Rules
118*c8dee2aaSAndroid Build Coastguard Worker  * ========================
119*c8dee2aaSAndroid Build Coastguard Worker  *
120*c8dee2aaSAndroid Build Coastguard Worker  * From the Layout descriptions above, the following simpler rules are sufficient:
121*c8dee2aaSAndroid Build Coastguard Worker  *
122*c8dee2aaSAndroid Build Coastguard Worker  * 1. If the base primitive type is "half" and the Layout expects half floats, N = 2; else, N = 4.
123*c8dee2aaSAndroid Build Coastguard Worker  *
124*c8dee2aaSAndroid Build Coastguard Worker  * 2. For arrays of scalars or vectors (with # of components, M = 1,2,3,4):
125*c8dee2aaSAndroid Build Coastguard Worker  *    a. If arrays must be aligned on vec4 boundaries OR M=3, then align and stride = 4*N.
126*c8dee2aaSAndroid Build Coastguard Worker  *    b. Otherwise, the align and stride = M*N.
127*c8dee2aaSAndroid Build Coastguard Worker  *
128*c8dee2aaSAndroid Build Coastguard Worker  *    In both cases, the total size required for the uniform is "array size"*stride.
129*c8dee2aaSAndroid Build Coastguard Worker  *
130*c8dee2aaSAndroid Build Coastguard Worker  * 3. For single scalars or vectors (M = 1,2,3,4), the align is SkNextPow2(M)*N (e.g. N,2N,4N,4N).
131*c8dee2aaSAndroid Build Coastguard Worker  *    a. If M = 3 and the Layout aligns the size with the alignment, the size is 4*N and N
132*c8dee2aaSAndroid Build Coastguard Worker  *       padding bytes must be zero'ed out afterwards.
133*c8dee2aaSAndroid Build Coastguard Worker  *    b. Otherwise, the align and size = M*N
134*c8dee2aaSAndroid Build Coastguard Worker  *
135*c8dee2aaSAndroid Build Coastguard Worker  * 4. The starting offset to write data is the current offset aligned to the calculated align value.
136*c8dee2aaSAndroid Build Coastguard Worker  *    The current offset is then incremented by the total size of the uniform.
137*c8dee2aaSAndroid Build Coastguard Worker  *
138*c8dee2aaSAndroid Build Coastguard Worker  *    For arrays and padded vec3's, the padding is included in the stride and total size, meeting
139*c8dee2aaSAndroid Build Coastguard Worker  *    the requirements of the original rule 4 in std140. When a single float3 that is not padded
140*c8dee2aaSAndroid Build Coastguard Worker  *    is written, the next offset only advances 12 bytes allowing a smaller type to pack tightly
141*c8dee2aaSAndroid Build Coastguard Worker  *    next to the Z coordinate.
142*c8dee2aaSAndroid Build Coastguard Worker  *
143*c8dee2aaSAndroid Build Coastguard Worker  * When N = 4, the CPU and GPU primitives are compatible, regardless of being float, int, or uint.
144*c8dee2aaSAndroid Build Coastguard Worker  * Contiguous ranges between any padding (for alignment or for array stride) can be memcpy'ed.
145*c8dee2aaSAndroid Build Coastguard Worker  * When N = 2, the CPU data is float and the GPU data f16, so values must be converted one primitive
146*c8dee2aaSAndroid Build Coastguard Worker  * at a time using SkFloatToHalf or skvx::to_half.
147*c8dee2aaSAndroid Build Coastguard Worker  *
148*c8dee2aaSAndroid Build Coastguard Worker  * The UniformManager will zero out any padding bytes (either prepended for starting alignment,
149*c8dee2aaSAndroid Build Coastguard Worker  * or appended for stride alignment). This is so that the final byte array can be hashed for uniform
150*c8dee2aaSAndroid Build Coastguard Worker  * value de-duplication before uploading to the GPU.
151*c8dee2aaSAndroid Build Coastguard Worker  *
152*c8dee2aaSAndroid Build Coastguard Worker  * While SkSL supports non-square matrices, the SkSLType enum and Graphite only expose support for
153*c8dee2aaSAndroid Build Coastguard Worker  * square matrices. Graphite assumes all matrix uniforms are in column-major order. This matches the
154*c8dee2aaSAndroid Build Coastguard Worker  * data layout of SkM44 already and UniformManager automatically transposes SkMatrix (which is in
155*c8dee2aaSAndroid Build Coastguard Worker  * row-major data) to be column-major. Thus, for layout purposes, a matrix or an array of matrices
156*c8dee2aaSAndroid Build Coastguard Worker  * can be laid out equivalently to an array of the column type with an array count multiplied by the
157*c8dee2aaSAndroid Build Coastguard Worker  * number of columns.
158*c8dee2aaSAndroid Build Coastguard Worker  *
159*c8dee2aaSAndroid Build Coastguard Worker  * Graphite does not embed structs within structs for its UBO or SSBO declarations for paint or
160*c8dee2aaSAndroid Build Coastguard Worker  * RenderSteps. However, when the "uniforms" are defined for use with SSBO random access, the
161*c8dee2aaSAndroid Build Coastguard Worker  * ordered set of uniforms is actually defining a struct instead of just a top-level interface.
162*c8dee2aaSAndroid Build Coastguard Worker  * As such, once all uniforms are recorded, the size must be rounded up to the maximum alignment
163*c8dee2aaSAndroid Build Coastguard Worker  * encountered for its members to satisfy alignment rules for all Layouts.
164*c8dee2aaSAndroid Build Coastguard Worker  *
165*c8dee2aaSAndroid Build Coastguard Worker  * If Graphite starts to define sub-structs, UniformOffsetCalculator can be used recursively.
166*c8dee2aaSAndroid Build Coastguard Worker  */
167*c8dee2aaSAndroid Build Coastguard Worker namespace LayoutRules {
168*c8dee2aaSAndroid Build Coastguard Worker     // The three diverging behaviors across the different Layouts:
PadVec3Size(Layout layout)169*c8dee2aaSAndroid Build Coastguard Worker     static constexpr bool PadVec3Size(Layout layout) { return layout == Layout::kMetal; }
AlignArraysAsVec4(Layout layout)170*c8dee2aaSAndroid Build Coastguard Worker     static constexpr bool AlignArraysAsVec4(Layout layout) { return layout == Layout::kStd140; }
UseFullPrecision(Layout layout)171*c8dee2aaSAndroid Build Coastguard Worker     static constexpr bool UseFullPrecision(Layout layout) { return layout != Layout::kMetal; }
172*c8dee2aaSAndroid Build Coastguard Worker }
173*c8dee2aaSAndroid Build Coastguard Worker 
174*c8dee2aaSAndroid Build Coastguard Worker class UniformOffsetCalculator {
175*c8dee2aaSAndroid Build Coastguard Worker public:
176*c8dee2aaSAndroid Build Coastguard Worker     UniformOffsetCalculator() = default;
177*c8dee2aaSAndroid Build Coastguard Worker 
178*c8dee2aaSAndroid Build Coastguard Worker     static UniformOffsetCalculator ForTopLevel(Layout layout, int offset = 0) {
179*c8dee2aaSAndroid Build Coastguard Worker         return UniformOffsetCalculator(layout, offset, /*reqAlignment=*/1);
180*c8dee2aaSAndroid Build Coastguard Worker     }
181*c8dee2aaSAndroid Build Coastguard Worker 
ForStruct(Layout layout)182*c8dee2aaSAndroid Build Coastguard Worker     static UniformOffsetCalculator ForStruct(Layout layout) {
183*c8dee2aaSAndroid Build Coastguard Worker         const int reqAlignment = LayoutRules::AlignArraysAsVec4(layout) ? 16 : 1;
184*c8dee2aaSAndroid Build Coastguard Worker         return UniformOffsetCalculator(layout, /*offset=*/0, reqAlignment);
185*c8dee2aaSAndroid Build Coastguard Worker     }
186*c8dee2aaSAndroid Build Coastguard Worker 
layout()187*c8dee2aaSAndroid Build Coastguard Worker     Layout layout() const { return fLayout; }
188*c8dee2aaSAndroid Build Coastguard Worker 
189*c8dee2aaSAndroid Build Coastguard Worker     // NOTE: The returned size represents the last consumed byte (if the recorded
190*c8dee2aaSAndroid Build Coastguard Worker     // uniforms are embedded within a struct, this will need to be rounded up to a multiple of
191*c8dee2aaSAndroid Build Coastguard Worker     // requiredAlignment()).
size()192*c8dee2aaSAndroid Build Coastguard Worker     int size() const { return fOffset; }
requiredAlignment()193*c8dee2aaSAndroid Build Coastguard Worker     int requiredAlignment() const { return fReqAlignment; }
194*c8dee2aaSAndroid Build Coastguard Worker 
195*c8dee2aaSAndroid Build Coastguard Worker     // Returns the correctly aligned offset to accommodate `count` instances of `type` and
196*c8dee2aaSAndroid Build Coastguard Worker     // advances the internal offset.
197*c8dee2aaSAndroid Build Coastguard Worker     //
198*c8dee2aaSAndroid Build Coastguard Worker     // After a call to this method, `size()` will return the offset to the end of `count` instances
199*c8dee2aaSAndroid Build Coastguard Worker     // of `type` (while the return value equals the aligned start offset). Subsequent calls will
200*c8dee2aaSAndroid Build Coastguard Worker     // calculate the new start offset starting at `size()`.
201*c8dee2aaSAndroid Build Coastguard Worker     int advanceOffset(SkSLType type, int count = Uniform::kNonArray);
202*c8dee2aaSAndroid Build Coastguard Worker 
203*c8dee2aaSAndroid Build Coastguard Worker     // Returns the correctly aligned offset to accommodate `count` instances of a custom struct
204*c8dee2aaSAndroid Build Coastguard Worker     // type that has had its own fields passed into the `substruct` offset calculator.
205*c8dee2aaSAndroid Build Coastguard Worker     //
206*c8dee2aaSAndroid Build Coastguard Worker     // After a call to this method, `size()` will return the offset to the end of `count` instances
207*c8dee2aaSAndroid Build Coastguard Worker     // of the struct types (while the return value equals the aligned start offset). This includes
208*c8dee2aaSAndroid Build Coastguard Worker     // any required padding of the struct size per rule #9.
209*c8dee2aaSAndroid Build Coastguard Worker     int advanceStruct(const UniformOffsetCalculator& substruct, int count = Uniform::kNonArray);
210*c8dee2aaSAndroid Build Coastguard Worker 
211*c8dee2aaSAndroid Build Coastguard Worker private:
UniformOffsetCalculator(Layout layout,int offset,int reqAlignment)212*c8dee2aaSAndroid Build Coastguard Worker     UniformOffsetCalculator(Layout layout, int offset, int reqAlignment)
213*c8dee2aaSAndroid Build Coastguard Worker             : fLayout(layout), fOffset(offset), fReqAlignment((reqAlignment)) {}
214*c8dee2aaSAndroid Build Coastguard Worker 
215*c8dee2aaSAndroid Build Coastguard Worker     Layout fLayout    = Layout::kInvalid;
216*c8dee2aaSAndroid Build Coastguard Worker     int fOffset       = 0;
217*c8dee2aaSAndroid Build Coastguard Worker     int fReqAlignment = 1;
218*c8dee2aaSAndroid Build Coastguard Worker };
219*c8dee2aaSAndroid Build Coastguard Worker 
220*c8dee2aaSAndroid Build Coastguard Worker class UniformManager {
221*c8dee2aaSAndroid Build Coastguard Worker public:
UniformManager(Layout layout)222*c8dee2aaSAndroid Build Coastguard Worker     UniformManager(Layout layout) { this->resetWithNewLayout(layout); }
223*c8dee2aaSAndroid Build Coastguard Worker 
finish()224*c8dee2aaSAndroid Build Coastguard Worker     SkSpan<const char> finish() {
225*c8dee2aaSAndroid Build Coastguard Worker         this->alignTo(fReqAlignment);
226*c8dee2aaSAndroid Build Coastguard Worker         return SkSpan(fStorage);
227*c8dee2aaSAndroid Build Coastguard Worker     }
228*c8dee2aaSAndroid Build Coastguard Worker 
size()229*c8dee2aaSAndroid Build Coastguard Worker     size_t size() const { return fStorage.size(); }
230*c8dee2aaSAndroid Build Coastguard Worker 
231*c8dee2aaSAndroid Build Coastguard Worker     void resetWithNewLayout(Layout layout);
reset()232*c8dee2aaSAndroid Build Coastguard Worker     void reset() { this->resetWithNewLayout(fLayout); }
233*c8dee2aaSAndroid Build Coastguard Worker 
234*c8dee2aaSAndroid Build Coastguard Worker     // scalars
write(float f)235*c8dee2aaSAndroid Build Coastguard Worker     void write(float f)     { this->write<SkSLType::kFloat>(&f); }
write(int32_t i)236*c8dee2aaSAndroid Build Coastguard Worker     void write(int32_t i)   { this->write<SkSLType::kInt  >(&i); }
writeHalf(float f)237*c8dee2aaSAndroid Build Coastguard Worker     void writeHalf(float f) { this->write<SkSLType::kHalf >(&f); }
238*c8dee2aaSAndroid Build Coastguard Worker 
239*c8dee2aaSAndroid Build Coastguard Worker     // [i|h]vec4 and arrays thereof (just add overloads as needed)
write(const SkPMColor4f & c)240*c8dee2aaSAndroid Build Coastguard Worker     void write(const SkPMColor4f& c) { this->write<SkSLType::kFloat4>(c.vec()); }
write(const SkRect & r)241*c8dee2aaSAndroid Build Coastguard Worker     void write(const SkRect& r)      { this->write<SkSLType::kFloat4>(r.asScalars()); }
write(const SkV4 & v)242*c8dee2aaSAndroid Build Coastguard Worker     void write(const SkV4& v)        { this->write<SkSLType::kFloat4>(v.ptr()); }
243*c8dee2aaSAndroid Build Coastguard Worker 
write(const SkIRect & r)244*c8dee2aaSAndroid Build Coastguard Worker     void write(const SkIRect& r)     { this->write<SkSLType::kInt4>(&r); }
245*c8dee2aaSAndroid Build Coastguard Worker 
writeHalf(const SkPMColor4f & c)246*c8dee2aaSAndroid Build Coastguard Worker     void writeHalf(const SkPMColor4f& c) { this->write<SkSLType::kHalf4>(c.vec()); }
writeHalf(const SkRect & r)247*c8dee2aaSAndroid Build Coastguard Worker     void writeHalf(const SkRect& r)      { this->write<SkSLType::kHalf4>(r.asScalars()); }
writeHalf(const SkV4 & v)248*c8dee2aaSAndroid Build Coastguard Worker     void writeHalf(const SkV4& v)        { this->write<SkSLType::kHalf4>(v.ptr()); }
249*c8dee2aaSAndroid Build Coastguard Worker 
writeArray(SkSpan<const SkV4> v)250*c8dee2aaSAndroid Build Coastguard Worker     void writeArray(SkSpan<const SkV4> v) {
251*c8dee2aaSAndroid Build Coastguard Worker         this->writeArray<SkSLType::kFloat4>(v.data(), v.size());
252*c8dee2aaSAndroid Build Coastguard Worker     }
writeArray(SkSpan<const SkPMColor4f> c)253*c8dee2aaSAndroid Build Coastguard Worker     void writeArray(SkSpan<const SkPMColor4f> c) {
254*c8dee2aaSAndroid Build Coastguard Worker         this->writeArray<SkSLType::kFloat4>(c.data(), c.size());
255*c8dee2aaSAndroid Build Coastguard Worker     }
writeHalfArray(SkSpan<const SkPMColor4f> c)256*c8dee2aaSAndroid Build Coastguard Worker     void writeHalfArray(SkSpan<const SkPMColor4f> c) {
257*c8dee2aaSAndroid Build Coastguard Worker         this->writeArray<SkSLType::kHalf4>(c.data(), c.size());
258*c8dee2aaSAndroid Build Coastguard Worker     }
259*c8dee2aaSAndroid Build Coastguard Worker 
260*c8dee2aaSAndroid Build Coastguard Worker     // [i|h]vec3
write(const SkV3 & v)261*c8dee2aaSAndroid Build Coastguard Worker     void write(const SkV3& v)     { this->write<SkSLType::kFloat3>(v.ptr()); }
write(const SkPoint3 & p)262*c8dee2aaSAndroid Build Coastguard Worker     void write(const SkPoint3& p) { this->write<SkSLType::kFloat3>(&p); }
263*c8dee2aaSAndroid Build Coastguard Worker 
writeHalf(const SkV3 & v)264*c8dee2aaSAndroid Build Coastguard Worker     void writeHalf(const SkV3& v)     { this->write<SkSLType::kHalf3>(v.ptr()); }
writeHalf(const SkPoint3 & p)265*c8dee2aaSAndroid Build Coastguard Worker     void writeHalf(const SkPoint3& p) { this->write<SkSLType::kHalf3>(&p); }
266*c8dee2aaSAndroid Build Coastguard Worker 
267*c8dee2aaSAndroid Build Coastguard Worker     // NOTE: 3-element vectors never pack efficiently in arrays, so avoid using them
268*c8dee2aaSAndroid Build Coastguard Worker 
269*c8dee2aaSAndroid Build Coastguard Worker     // [i|h]vec2
write(const SkV2 & v)270*c8dee2aaSAndroid Build Coastguard Worker     void write(const SkV2& v)    { this->write<SkSLType::kFloat2>(v.ptr()); }
write(const SkSize & s)271*c8dee2aaSAndroid Build Coastguard Worker     void write(const SkSize& s)  { this->write<SkSLType::kFloat2>(&s); }
write(const SkPoint & p)272*c8dee2aaSAndroid Build Coastguard Worker     void write(const SkPoint& p) { this->write<SkSLType::kFloat2>(&p); }
273*c8dee2aaSAndroid Build Coastguard Worker 
write(const SkISize & s)274*c8dee2aaSAndroid Build Coastguard Worker     void write(const SkISize& s) { this->write<SkSLType::kInt2>(&s); }
275*c8dee2aaSAndroid Build Coastguard Worker 
writeHalf(const SkV2 & v)276*c8dee2aaSAndroid Build Coastguard Worker     void writeHalf(const SkV2& v)    { this->write<SkSLType::kHalf2>(v.ptr()); }
writeHalf(const SkSize & s)277*c8dee2aaSAndroid Build Coastguard Worker     void writeHalf(const SkSize& s)  { this->write<SkSLType::kHalf2>(&s); }
writeHalf(const SkPoint & p)278*c8dee2aaSAndroid Build Coastguard Worker     void writeHalf(const SkPoint& p) { this->write<SkSLType::kHalf2>(&p); }
279*c8dee2aaSAndroid Build Coastguard Worker 
280*c8dee2aaSAndroid Build Coastguard Worker     // NOTE: 2-element vectors don't pack efficiently in std140, so avoid using them
281*c8dee2aaSAndroid Build Coastguard Worker 
282*c8dee2aaSAndroid Build Coastguard Worker     // matrices
write(const SkM44 & m)283*c8dee2aaSAndroid Build Coastguard Worker     void write(const SkM44& m) {
284*c8dee2aaSAndroid Build Coastguard Worker         // All Layouts treat a 4x4 column-major matrix as an array of vec4's, which is exactly how
285*c8dee2aaSAndroid Build Coastguard Worker         // SkM44 already stores its data.
286*c8dee2aaSAndroid Build Coastguard Worker         this->writeArray<SkSLType::kFloat4>(SkMatrixPriv::M44ColMajor(m), 4);
287*c8dee2aaSAndroid Build Coastguard Worker     }
288*c8dee2aaSAndroid Build Coastguard Worker 
writeHalf(const SkM44 & m)289*c8dee2aaSAndroid Build Coastguard Worker     void writeHalf(const SkM44& m) {
290*c8dee2aaSAndroid Build Coastguard Worker         this->writeArray<SkSLType::kHalf4>(SkMatrixPriv::M44ColMajor(m), 4);
291*c8dee2aaSAndroid Build Coastguard Worker     }
292*c8dee2aaSAndroid Build Coastguard Worker 
write(const SkMatrix & m)293*c8dee2aaSAndroid Build Coastguard Worker     void write(const SkMatrix& m) {
294*c8dee2aaSAndroid Build Coastguard Worker         // SkMatrix is row-major, so rewrite to column major. All Layouts treat a 3x3 column
295*c8dee2aaSAndroid Build Coastguard Worker         // major matrix as an array of vec3's.
296*c8dee2aaSAndroid Build Coastguard Worker         float colMajor[9] = {m[0], m[3], m[6],
297*c8dee2aaSAndroid Build Coastguard Worker                              m[1], m[4], m[7],
298*c8dee2aaSAndroid Build Coastguard Worker                              m[2], m[5], m[8]};
299*c8dee2aaSAndroid Build Coastguard Worker         this->writeArray<SkSLType::kFloat3>(colMajor, 3);
300*c8dee2aaSAndroid Build Coastguard Worker     }
writeHalf(const SkMatrix & m)301*c8dee2aaSAndroid Build Coastguard Worker     void writeHalf(const SkMatrix& m) {
302*c8dee2aaSAndroid Build Coastguard Worker         float colMajor[9] = {m[0], m[3], m[6],
303*c8dee2aaSAndroid Build Coastguard Worker                              m[1], m[4], m[7],
304*c8dee2aaSAndroid Build Coastguard Worker                              m[2], m[5], m[8]};
305*c8dee2aaSAndroid Build Coastguard Worker         this->writeArray<SkSLType::kHalf3>(colMajor, 3);
306*c8dee2aaSAndroid Build Coastguard Worker     }
307*c8dee2aaSAndroid Build Coastguard Worker 
308*c8dee2aaSAndroid Build Coastguard Worker     // NOTE: 2x2 matrices can be manually packed the same or better as a vec4, so prefer that
309*c8dee2aaSAndroid Build Coastguard Worker 
310*c8dee2aaSAndroid Build Coastguard Worker     // This is a specialized uniform writing entry point intended to deduplicate the paint
311*c8dee2aaSAndroid Build Coastguard Worker     // color. If a more general system is required, the deduping logic can be added to the
312*c8dee2aaSAndroid Build Coastguard Worker     // other write methods (and this specialized method would be removed).
writePaintColor(const SkPMColor4f & color)313*c8dee2aaSAndroid Build Coastguard Worker     void writePaintColor(const SkPMColor4f& color) {
314*c8dee2aaSAndroid Build Coastguard Worker         if (fWrotePaintColor) {
315*c8dee2aaSAndroid Build Coastguard Worker             // Validate expected uniforms, but don't write a second copy since the paint color
316*c8dee2aaSAndroid Build Coastguard Worker             // uniform can only ever be declared once in the final SkSL program.
317*c8dee2aaSAndroid Build Coastguard Worker             SkASSERT(this->checkExpected(/*dst=*/nullptr, SkSLType::kFloat4, Uniform::kNonArray));
318*c8dee2aaSAndroid Build Coastguard Worker         } else {
319*c8dee2aaSAndroid Build Coastguard Worker             this->write<SkSLType::kFloat4>(&color);
320*c8dee2aaSAndroid Build Coastguard Worker             fWrotePaintColor = true;
321*c8dee2aaSAndroid Build Coastguard Worker         }
322*c8dee2aaSAndroid Build Coastguard Worker     }
323*c8dee2aaSAndroid Build Coastguard Worker 
324*c8dee2aaSAndroid Build Coastguard Worker     // Copy from `src` using Uniform array-count semantics.
325*c8dee2aaSAndroid Build Coastguard Worker     void write(const Uniform&, const void* src);
326*c8dee2aaSAndroid Build Coastguard Worker 
327*c8dee2aaSAndroid Build Coastguard Worker     // UniformManager has basic support for writing substructs with the caveats:
328*c8dee2aaSAndroid Build Coastguard Worker     // 1. The base alignment of the substruct must be known a priori so the first member can be
329*c8dee2aaSAndroid Build Coastguard Worker     //    written immediately.
330*c8dee2aaSAndroid Build Coastguard Worker     // 2. Nested substructs are not supported (but could be if the padded-struct size was also
331*c8dee2aaSAndroid Build Coastguard Worker     //    provided to endStruct()).
332*c8dee2aaSAndroid Build Coastguard Worker     //
333*c8dee2aaSAndroid Build Coastguard Worker     // Call beginStruct(baseAlignment) before writing the first field. Then call the regular
334*c8dee2aaSAndroid Build Coastguard Worker     // write functions for each of the substruct's fields in order. Lastly, call endStruct() to
335*c8dee2aaSAndroid Build Coastguard Worker     // go back to writing fields in the top-level interface block.
beginStruct(int baseAlignment)336*c8dee2aaSAndroid Build Coastguard Worker     void beginStruct(int baseAlignment) {
337*c8dee2aaSAndroid Build Coastguard Worker         SkASSERT(this->checkBeginStruct(baseAlignment)); // verifies baseAlignment matches layout
338*c8dee2aaSAndroid Build Coastguard Worker 
339*c8dee2aaSAndroid Build Coastguard Worker         this->alignTo(baseAlignment);
340*c8dee2aaSAndroid Build Coastguard Worker         fStructBaseAlignment = baseAlignment;
341*c8dee2aaSAndroid Build Coastguard Worker         fReqAlignment = std::max(fReqAlignment, baseAlignment);
342*c8dee2aaSAndroid Build Coastguard Worker     }
endStruct()343*c8dee2aaSAndroid Build Coastguard Worker     void endStruct() {
344*c8dee2aaSAndroid Build Coastguard Worker         SkASSERT(fStructBaseAlignment >= 1); // Must have started a struct
345*c8dee2aaSAndroid Build Coastguard Worker         this->alignTo(fStructBaseAlignment);
346*c8dee2aaSAndroid Build Coastguard Worker         SkASSERT(this->checkEndStruct()); // validate after padding out to struct's alignment
347*c8dee2aaSAndroid Build Coastguard Worker         fStructBaseAlignment = 0;
348*c8dee2aaSAndroid Build Coastguard Worker     }
349*c8dee2aaSAndroid Build Coastguard Worker 
350*c8dee2aaSAndroid Build Coastguard Worker     // Debug-only functions to control uniform expectations.
351*c8dee2aaSAndroid Build Coastguard Worker #ifdef SK_DEBUG
352*c8dee2aaSAndroid Build Coastguard Worker     bool isReset() const;
353*c8dee2aaSAndroid Build Coastguard Worker     void setExpectedUniforms(SkSpan<const Uniform> expected, bool isSubstruct);
354*c8dee2aaSAndroid Build Coastguard Worker     void doneWithExpectedUniforms();
355*c8dee2aaSAndroid Build Coastguard Worker #endif // SK_DEBUG
356*c8dee2aaSAndroid Build Coastguard Worker 
357*c8dee2aaSAndroid Build Coastguard Worker private:
358*c8dee2aaSAndroid Build Coastguard Worker     // All public write() functions in UniformManager already match scalar/vector SkSLTypes or have
359*c8dee2aaSAndroid Build Coastguard Worker     // explicitly converted matrix SkSLTypes to a writeArray<column type> so this does not need to
360*c8dee2aaSAndroid Build Coastguard Worker     // check anything beyond half[2,3,4].
IsHalfVector(SkSLType type)361*c8dee2aaSAndroid Build Coastguard Worker     static constexpr bool IsHalfVector(SkSLType type) {
362*c8dee2aaSAndroid Build Coastguard Worker         return type >= SkSLType::kHalf && type <= SkSLType::kHalf4;
363*c8dee2aaSAndroid Build Coastguard Worker     }
364*c8dee2aaSAndroid Build Coastguard Worker 
365*c8dee2aaSAndroid Build Coastguard Worker     // Other than validation, actual layout doesn't care about 'type' and the logic can be
366*c8dee2aaSAndroid Build Coastguard Worker     // based on vector length and whether or not it's half or full precision.
367*c8dee2aaSAndroid Build Coastguard Worker     template <int N, bool Half> void write(const void* src, SkSLType type);
368*c8dee2aaSAndroid Build Coastguard Worker     template <int N, bool Half> void writeArray(const void* src, int count, SkSLType type);
369*c8dee2aaSAndroid Build Coastguard Worker 
370*c8dee2aaSAndroid Build Coastguard Worker     // Helpers to select dimensionality and convert to full precision if required by the Layout.
write(const void * src)371*c8dee2aaSAndroid Build Coastguard Worker     template <SkSLType Type> void write(const void* src) {
372*c8dee2aaSAndroid Build Coastguard Worker         static constexpr int N = SkSLTypeVecLength(Type);
373*c8dee2aaSAndroid Build Coastguard Worker         if (IsHalfVector(Type) && !LayoutRules::UseFullPrecision(fLayout)) {
374*c8dee2aaSAndroid Build Coastguard Worker             this->write<N, /*Half=*/true>(src, Type);
375*c8dee2aaSAndroid Build Coastguard Worker         } else {
376*c8dee2aaSAndroid Build Coastguard Worker             this->write<N, /*Half=*/false>(src, Type);
377*c8dee2aaSAndroid Build Coastguard Worker         }
378*c8dee2aaSAndroid Build Coastguard Worker     }
writeArray(const void * src,int count)379*c8dee2aaSAndroid Build Coastguard Worker     template <SkSLType Type> void writeArray(const void* src, int count) {
380*c8dee2aaSAndroid Build Coastguard Worker         static constexpr int N = SkSLTypeVecLength(Type);
381*c8dee2aaSAndroid Build Coastguard Worker         if (IsHalfVector(Type) && !LayoutRules::UseFullPrecision(fLayout)) {
382*c8dee2aaSAndroid Build Coastguard Worker             this->writeArray<N, /*Half=*/true>(src, count, Type);
383*c8dee2aaSAndroid Build Coastguard Worker         } else {
384*c8dee2aaSAndroid Build Coastguard Worker             this->writeArray<N, /*Half=*/false>(src, count, Type);
385*c8dee2aaSAndroid Build Coastguard Worker         }
386*c8dee2aaSAndroid Build Coastguard Worker     }
387*c8dee2aaSAndroid Build Coastguard Worker 
388*c8dee2aaSAndroid Build Coastguard Worker     // This is marked 'inline' so that it can be defined below with write() and writeArray() and
389*c8dee2aaSAndroid Build Coastguard Worker     // still link correctly.
390*c8dee2aaSAndroid Build Coastguard Worker     inline char* append(int alignment, int size);
391*c8dee2aaSAndroid Build Coastguard Worker     inline void alignTo(int alignment);
392*c8dee2aaSAndroid Build Coastguard Worker 
393*c8dee2aaSAndroid Build Coastguard Worker     SkTDArray<char> fStorage;
394*c8dee2aaSAndroid Build Coastguard Worker 
395*c8dee2aaSAndroid Build Coastguard Worker     Layout fLayout;
396*c8dee2aaSAndroid Build Coastguard Worker     int fReqAlignment = 0;
397*c8dee2aaSAndroid Build Coastguard Worker     int fStructBaseAlignment = 0;
398*c8dee2aaSAndroid Build Coastguard Worker     // The paint color is treated special and we only add its uniform once.
399*c8dee2aaSAndroid Build Coastguard Worker     bool fWrotePaintColor = false;
400*c8dee2aaSAndroid Build Coastguard Worker 
401*c8dee2aaSAndroid Build Coastguard Worker     // Debug-only verification that UniformOffsetCalculator is consistent and that write() calls
402*c8dee2aaSAndroid Build Coastguard Worker     // match the expected uniform declaration order.
403*c8dee2aaSAndroid Build Coastguard Worker #ifdef SK_DEBUG
404*c8dee2aaSAndroid Build Coastguard Worker     UniformOffsetCalculator fOffsetCalculator; // should match implicit offsets from append()
405*c8dee2aaSAndroid Build Coastguard Worker     UniformOffsetCalculator fSubstructCalculator; // 0-based, used when inside a substruct
406*c8dee2aaSAndroid Build Coastguard Worker     int fSubstructStartingOffset = -1; // offset within fOffsetCalculator of first field
407*c8dee2aaSAndroid Build Coastguard Worker 
408*c8dee2aaSAndroid Build Coastguard Worker     SkSpan<const Uniform> fExpectedUniforms;
409*c8dee2aaSAndroid Build Coastguard Worker     int fExpectedUniformIndex = 0;
410*c8dee2aaSAndroid Build Coastguard Worker 
411*c8dee2aaSAndroid Build Coastguard Worker     bool checkExpected(const void* dst, SkSLType, int count);
412*c8dee2aaSAndroid Build Coastguard Worker     bool checkBeginStruct(int baseAlignment);
413*c8dee2aaSAndroid Build Coastguard Worker     bool checkEndStruct();
414*c8dee2aaSAndroid Build Coastguard Worker #endif // SK_DEBUG
415*c8dee2aaSAndroid Build Coastguard Worker };
416*c8dee2aaSAndroid Build Coastguard Worker 
417*c8dee2aaSAndroid Build Coastguard Worker ///////////////////////////////////////////////////////////////////////////////////////////////////
418*c8dee2aaSAndroid Build Coastguard Worker // Definitions
419*c8dee2aaSAndroid Build Coastguard Worker 
420*c8dee2aaSAndroid Build Coastguard Worker // Shared helper for both write() and writeArray()
421*c8dee2aaSAndroid Build Coastguard Worker template <int N, bool Half>
422*c8dee2aaSAndroid Build Coastguard Worker struct LayoutTraits {
423*c8dee2aaSAndroid Build Coastguard Worker     static_assert(1 <= N && N <= 4);
424*c8dee2aaSAndroid Build Coastguard Worker 
425*c8dee2aaSAndroid Build Coastguard Worker     static constexpr int kElemSize = Half ? sizeof(SkHalf) : sizeof(float);
426*c8dee2aaSAndroid Build Coastguard Worker     static constexpr int kSize     = N * kElemSize;
427*c8dee2aaSAndroid Build Coastguard Worker     static constexpr int kAlign    = SkNextPow2_portable(N) * kElemSize;
428*c8dee2aaSAndroid Build Coastguard Worker 
429*c8dee2aaSAndroid Build Coastguard Worker     // Reads kSize bytes from 'src' and copies or converts (float->half) the N values
430*c8dee2aaSAndroid Build Coastguard Worker     // into 'dst'. Does not add any other padding that may depend on usage and Layout.
CopyLayoutTraits431*c8dee2aaSAndroid Build Coastguard Worker     static void Copy(const void* src, void* dst) {
432*c8dee2aaSAndroid Build Coastguard Worker         if constexpr (Half) {
433*c8dee2aaSAndroid Build Coastguard Worker             using VecF = skvx::Vec<SkNextPow2_portable(N), float>;
434*c8dee2aaSAndroid Build Coastguard Worker             VecF srcData;
435*c8dee2aaSAndroid Build Coastguard Worker             if constexpr (N == 3) {
436*c8dee2aaSAndroid Build Coastguard Worker                 // Load the 3 values into a float4 to take advantage of vectorized conversion.
437*c8dee2aaSAndroid Build Coastguard Worker                 // The 4th value will not be copied to dst.
438*c8dee2aaSAndroid Build Coastguard Worker                 const float* srcF = static_cast<const float*>(src);
439*c8dee2aaSAndroid Build Coastguard Worker                 srcData = VecF{srcF[0], srcF[1], srcF[2], 0.f};
440*c8dee2aaSAndroid Build Coastguard Worker             } else {
441*c8dee2aaSAndroid Build Coastguard Worker                 srcData = VecF::Load(src);
442*c8dee2aaSAndroid Build Coastguard Worker             }
443*c8dee2aaSAndroid Build Coastguard Worker 
444*c8dee2aaSAndroid Build Coastguard Worker             auto dstData = to_half(srcData);
445*c8dee2aaSAndroid Build Coastguard Worker             // NOTE: this is identical to Vec::store() for N=1,2,4 and correctly drops the 4th
446*c8dee2aaSAndroid Build Coastguard Worker             // lane when N=3.
447*c8dee2aaSAndroid Build Coastguard Worker             memcpy(dst, &dstData, kSize);
448*c8dee2aaSAndroid Build Coastguard Worker         } else {
449*c8dee2aaSAndroid Build Coastguard Worker             memcpy(dst, src, kSize);
450*c8dee2aaSAndroid Build Coastguard Worker         }
451*c8dee2aaSAndroid Build Coastguard Worker     }
452*c8dee2aaSAndroid Build Coastguard Worker 
453*c8dee2aaSAndroid Build Coastguard Worker #ifdef SK_DEBUG
ValidateLayoutTraits454*c8dee2aaSAndroid Build Coastguard Worker     static void Validate(const void* src, SkSLType type, Layout layout) {
455*c8dee2aaSAndroid Build Coastguard Worker         // Src validation
456*c8dee2aaSAndroid Build Coastguard Worker         SkASSERT(src);
457*c8dee2aaSAndroid Build Coastguard Worker         // All primitives on the CPU side should be 4 byte aligned
458*c8dee2aaSAndroid Build Coastguard Worker         SkASSERT(SkIsAlign4(reinterpret_cast<intptr_t>(src)));
459*c8dee2aaSAndroid Build Coastguard Worker 
460*c8dee2aaSAndroid Build Coastguard Worker         // Type and validation layout
461*c8dee2aaSAndroid Build Coastguard Worker         SkASSERT(SkSLTypeCanBeUniformValue(type));
462*c8dee2aaSAndroid Build Coastguard Worker         SkASSERT(SkSLTypeVecLength(type) == N); // Matrix types should have been flattened already
463*c8dee2aaSAndroid Build Coastguard Worker         if constexpr (Half) {
464*c8dee2aaSAndroid Build Coastguard Worker             SkASSERT(SkSLTypeIsFloatType(type));
465*c8dee2aaSAndroid Build Coastguard Worker             SkASSERT(!SkSLTypeIsFullPrecisionNumericType(type));
466*c8dee2aaSAndroid Build Coastguard Worker             SkASSERT(!LayoutRules::UseFullPrecision(layout));
467*c8dee2aaSAndroid Build Coastguard Worker         } else {
468*c8dee2aaSAndroid Build Coastguard Worker             SkASSERT(SkSLTypeIsFullPrecisionNumericType(type) ||
469*c8dee2aaSAndroid Build Coastguard Worker                      LayoutRules::UseFullPrecision(layout));
470*c8dee2aaSAndroid Build Coastguard Worker         }
471*c8dee2aaSAndroid Build Coastguard Worker     }
472*c8dee2aaSAndroid Build Coastguard Worker #endif
473*c8dee2aaSAndroid Build Coastguard Worker };
474*c8dee2aaSAndroid Build Coastguard Worker 
475*c8dee2aaSAndroid Build Coastguard Worker template<int N, bool Half>
write(const void * src,SkSLType type)476*c8dee2aaSAndroid Build Coastguard Worker void UniformManager::write(const void* src, SkSLType type) {
477*c8dee2aaSAndroid Build Coastguard Worker     using L = LayoutTraits<N, Half>;
478*c8dee2aaSAndroid Build Coastguard Worker     SkDEBUGCODE(L::Validate(src, type, fLayout);)
479*c8dee2aaSAndroid Build Coastguard Worker 
480*c8dee2aaSAndroid Build Coastguard Worker     // Layouts diverge in how vec3 size is determined for non-array usage
481*c8dee2aaSAndroid Build Coastguard Worker     char* dst = (N == 3 && LayoutRules::PadVec3Size(fLayout))
482*c8dee2aaSAndroid Build Coastguard Worker             ? this->append(L::kAlign, L::kSize + L::kElemSize)
483*c8dee2aaSAndroid Build Coastguard Worker             : this->append(L::kAlign, L::kSize);
484*c8dee2aaSAndroid Build Coastguard Worker     SkASSERT(this->checkExpected(dst, type, Uniform::kNonArray));
485*c8dee2aaSAndroid Build Coastguard Worker 
486*c8dee2aaSAndroid Build Coastguard Worker     L::Copy(src, dst);
487*c8dee2aaSAndroid Build Coastguard Worker     if (N == 3 && LayoutRules::PadVec3Size(fLayout)) {
488*c8dee2aaSAndroid Build Coastguard Worker         memset(dst + L::kSize, 0, L::kElemSize);
489*c8dee2aaSAndroid Build Coastguard Worker     }
490*c8dee2aaSAndroid Build Coastguard Worker }
491*c8dee2aaSAndroid Build Coastguard Worker 
492*c8dee2aaSAndroid Build Coastguard Worker template<int N, bool Half>
writeArray(const void * src,int count,SkSLType type)493*c8dee2aaSAndroid Build Coastguard Worker void UniformManager::writeArray(const void* src, int count, SkSLType type) {
494*c8dee2aaSAndroid Build Coastguard Worker     using L = LayoutTraits<N, Half>;
495*c8dee2aaSAndroid Build Coastguard Worker     static constexpr int kSrcStride = N * 4; // Source data is always in multiples of 4 bytes.
496*c8dee2aaSAndroid Build Coastguard Worker 
497*c8dee2aaSAndroid Build Coastguard Worker     SkDEBUGCODE(L::Validate(src, type, fLayout);)
498*c8dee2aaSAndroid Build Coastguard Worker     SkASSERT(count > 0);
499*c8dee2aaSAndroid Build Coastguard Worker 
500*c8dee2aaSAndroid Build Coastguard Worker     if (Half || N == 3 || (N != 4 && LayoutRules::AlignArraysAsVec4(fLayout))) {
501*c8dee2aaSAndroid Build Coastguard Worker         // A non-dense array (N == 3 is always padded to vec4, or the Layout requires it),
502*c8dee2aaSAndroid Build Coastguard Worker         // or we have to perform half conversion so iterate over each element.
503*c8dee2aaSAndroid Build Coastguard Worker         static constexpr int kStride  = Half ? L::kAlign : 4*L::kElemSize;
504*c8dee2aaSAndroid Build Coastguard Worker         SkASSERT(!(Half && LayoutRules::AlignArraysAsVec4(fLayout))); // should be exclusive
505*c8dee2aaSAndroid Build Coastguard Worker 
506*c8dee2aaSAndroid Build Coastguard Worker         const char* srcBytes = reinterpret_cast<const char*>(src);
507*c8dee2aaSAndroid Build Coastguard Worker         char* dst = this->append(kStride, kStride*count);
508*c8dee2aaSAndroid Build Coastguard Worker         SkASSERT(this->checkExpected(dst, type, count));
509*c8dee2aaSAndroid Build Coastguard Worker 
510*c8dee2aaSAndroid Build Coastguard Worker         for (int i = 0; i < count; ++i) {
511*c8dee2aaSAndroid Build Coastguard Worker             L::Copy(srcBytes, dst);
512*c8dee2aaSAndroid Build Coastguard Worker             if constexpr (kStride - L::kSize > 0) {
513*c8dee2aaSAndroid Build Coastguard Worker                 memset(dst + L::kSize, 0, kStride - L::kSize);
514*c8dee2aaSAndroid Build Coastguard Worker             }
515*c8dee2aaSAndroid Build Coastguard Worker 
516*c8dee2aaSAndroid Build Coastguard Worker             dst += kStride;
517*c8dee2aaSAndroid Build Coastguard Worker             srcBytes += kSrcStride;
518*c8dee2aaSAndroid Build Coastguard Worker         }
519*c8dee2aaSAndroid Build Coastguard Worker     } else {
520*c8dee2aaSAndroid Build Coastguard Worker         // A dense array with no type conversion, so copy in one go.
521*c8dee2aaSAndroid Build Coastguard Worker         SkASSERT(L::kAlign == L::kSize && kSrcStride == L::kSize);
522*c8dee2aaSAndroid Build Coastguard Worker         char* dst = this->append(L::kAlign, L::kSize*count);
523*c8dee2aaSAndroid Build Coastguard Worker         SkASSERT(this->checkExpected(dst, type, count));
524*c8dee2aaSAndroid Build Coastguard Worker 
525*c8dee2aaSAndroid Build Coastguard Worker         memcpy(dst, src, L::kSize*count);
526*c8dee2aaSAndroid Build Coastguard Worker     }
527*c8dee2aaSAndroid Build Coastguard Worker }
528*c8dee2aaSAndroid Build Coastguard Worker 
alignTo(int alignment)529*c8dee2aaSAndroid Build Coastguard Worker void UniformManager::alignTo(int alignment) {
530*c8dee2aaSAndroid Build Coastguard Worker     SkASSERT(alignment >= 1 && SkIsPow2(alignment));
531*c8dee2aaSAndroid Build Coastguard Worker     if ((fStorage.size() & (alignment - 1)) != 0) {
532*c8dee2aaSAndroid Build Coastguard Worker         this->append(alignment, /*size=*/0);
533*c8dee2aaSAndroid Build Coastguard Worker     }
534*c8dee2aaSAndroid Build Coastguard Worker }
535*c8dee2aaSAndroid Build Coastguard Worker 
append(int alignment,int size)536*c8dee2aaSAndroid Build Coastguard Worker char* UniformManager::append(int alignment, int size) {
537*c8dee2aaSAndroid Build Coastguard Worker     // The base alignment for a struct should have been calculated for the current layout using
538*c8dee2aaSAndroid Build Coastguard Worker     // UniformOffsetCalculator, so every field appended within the struct should have an alignment
539*c8dee2aaSAndroid Build Coastguard Worker     // less than or equal to that base alignment.
540*c8dee2aaSAndroid Build Coastguard Worker     SkASSERT(fStructBaseAlignment <= 0 || alignment <= fStructBaseAlignment);
541*c8dee2aaSAndroid Build Coastguard Worker 
542*c8dee2aaSAndroid Build Coastguard Worker     const int offset = fStorage.size();
543*c8dee2aaSAndroid Build Coastguard Worker     const int padding = SkAlignTo(offset, alignment) - offset;
544*c8dee2aaSAndroid Build Coastguard Worker 
545*c8dee2aaSAndroid Build Coastguard Worker     // These are just asserts not aborts because SkSL compilation imposes limits on the size of
546*c8dee2aaSAndroid Build Coastguard Worker     // runtime effect arrays, and internal shaders should not be using excessive lengths.
547*c8dee2aaSAndroid Build Coastguard Worker     SkASSERT(std::numeric_limits<int>::max() - alignment >= offset);
548*c8dee2aaSAndroid Build Coastguard Worker     SkASSERT(std::numeric_limits<int>::max() - size >= padding);
549*c8dee2aaSAndroid Build Coastguard Worker 
550*c8dee2aaSAndroid Build Coastguard Worker     char* dst = fStorage.append(size + padding);
551*c8dee2aaSAndroid Build Coastguard Worker     if (padding > 0) {
552*c8dee2aaSAndroid Build Coastguard Worker         memset(dst, 0, padding);
553*c8dee2aaSAndroid Build Coastguard Worker         dst += padding;
554*c8dee2aaSAndroid Build Coastguard Worker     }
555*c8dee2aaSAndroid Build Coastguard Worker 
556*c8dee2aaSAndroid Build Coastguard Worker     fReqAlignment = std::max(fReqAlignment, alignment);
557*c8dee2aaSAndroid Build Coastguard Worker     return dst;
558*c8dee2aaSAndroid Build Coastguard Worker }
559*c8dee2aaSAndroid Build Coastguard Worker 
560*c8dee2aaSAndroid Build Coastguard Worker }  // namespace skgpu::graphite
561*c8dee2aaSAndroid Build Coastguard Worker 
562*c8dee2aaSAndroid Build Coastguard Worker #endif // skgpu_UniformManager_DEFINED
563