/src/skia/src/core/SkMesh.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 "include/core/SkMesh.h" |
9 | | |
10 | | #include "include/core/SkAlphaType.h" |
11 | | #include "include/core/SkColorSpace.h" |
12 | | #include "include/core/SkData.h" |
13 | | #include "include/private/SkSLSampleUsage.h" |
14 | | #include "include/private/base/SkAlign.h" |
15 | | #include "include/private/base/SkAssert.h" |
16 | | #include "include/private/base/SkMath.h" |
17 | | #include "include/private/base/SkTArray.h" |
18 | | #include "include/private/base/SkTo.h" |
19 | | #include "src/base/SkSafeMath.h" |
20 | | #include "src/core/SkChecksum.h" |
21 | | #include "src/core/SkMeshPriv.h" |
22 | | #include "src/core/SkRuntimeEffectPriv.h" |
23 | | #include "src/sksl/SkSLAnalysis.h" |
24 | | #include "src/sksl/SkSLBuiltinTypes.h" |
25 | | #include "src/sksl/SkSLCompiler.h" |
26 | | #include "src/sksl/SkSLContext.h" |
27 | | #include "src/sksl/SkSLProgramKind.h" |
28 | | #include "src/sksl/SkSLProgramSettings.h" |
29 | | #include "src/sksl/analysis/SkSLProgramVisitor.h" |
30 | | #include "src/sksl/ir/SkSLExpression.h" |
31 | | #include "src/sksl/ir/SkSLFieldAccess.h" |
32 | | #include "src/sksl/ir/SkSLFunctionDeclaration.h" |
33 | | #include "src/sksl/ir/SkSLFunctionDefinition.h" |
34 | | #include "src/sksl/ir/SkSLModifierFlags.h" |
35 | | #include "src/sksl/ir/SkSLProgram.h" |
36 | | #include "src/sksl/ir/SkSLProgramElement.h" |
37 | | #include "src/sksl/ir/SkSLReturnStatement.h" |
38 | | #include "src/sksl/ir/SkSLStatement.h" |
39 | | #include "src/sksl/ir/SkSLStructDefinition.h" |
40 | | #include "src/sksl/ir/SkSLType.h" |
41 | | #include "src/sksl/ir/SkSLVarDeclarations.h" |
42 | | #include "src/sksl/ir/SkSLVariable.h" |
43 | | #include "src/sksl/ir/SkSLVariableReference.h" |
44 | | |
45 | | #include <algorithm> |
46 | | #include <locale> |
47 | | #include <optional> |
48 | | #include <string> |
49 | | #include <tuple> |
50 | | #include <utility> |
51 | | |
52 | | using namespace skia_private; |
53 | | |
54 | | using Attribute = SkMeshSpecification::Attribute; |
55 | | using Varying = SkMeshSpecification::Varying; |
56 | | |
57 | | using IndexBuffer = SkMesh::IndexBuffer; |
58 | | using VertexBuffer = SkMesh::VertexBuffer; |
59 | | |
60 | 0 | #define RETURN_FAILURE(...) return Result{nullptr, SkStringPrintf(__VA_ARGS__)} |
61 | | |
62 | 0 | #define RETURN_ERROR(...) return std::make_tuple(false, SkStringPrintf(__VA_ARGS__)) |
63 | | |
64 | 4 | #define RETURN_SUCCESS return std::make_tuple(true, SkString{}) |
65 | | |
66 | | using Uniform = SkMeshSpecification::Uniform; |
67 | | using Child = SkMeshSpecification::Child; |
68 | | |
69 | | static std::vector<Uniform>::iterator find_uniform(std::vector<Uniform>& uniforms, |
70 | 0 | std::string_view name) { |
71 | 0 | return std::find_if(uniforms.begin(), uniforms.end(), |
72 | 0 | [name](const SkMeshSpecification::Uniform& u) { return u.name == name; }); |
73 | 0 | } |
74 | | |
75 | | static std::tuple<bool, SkString> |
76 | | gather_uniforms_and_check_for_main(const SkSL::Program& program, |
77 | | std::vector<Uniform>* uniforms, |
78 | | std::vector<Child>* children, |
79 | | SkMeshSpecification::Uniform::Flags stage, |
80 | 8 | size_t* offset) { |
81 | 8 | bool foundMain = false; |
82 | 20 | for (const SkSL::ProgramElement* elem : program.elements()) { |
83 | 20 | if (elem->is<SkSL::FunctionDefinition>()) { |
84 | 8 | const SkSL::FunctionDefinition& defn = elem->as<SkSL::FunctionDefinition>(); |
85 | 8 | const SkSL::FunctionDeclaration& decl = defn.declaration(); |
86 | 8 | if (decl.isMain()) { |
87 | 8 | foundMain = true; |
88 | 8 | } |
89 | 12 | } else if (elem->is<SkSL::GlobalVarDeclaration>()) { |
90 | 0 | const SkSL::GlobalVarDeclaration& global = elem->as<SkSL::GlobalVarDeclaration>(); |
91 | 0 | const SkSL::VarDeclaration& varDecl = global.declaration()->as<SkSL::VarDeclaration>(); |
92 | 0 | const SkSL::Variable& var = *varDecl.var(); |
93 | 0 | if (var.modifierFlags().isUniform()) { |
94 | 0 | if (var.type().isEffectChild()) { |
95 | | // This is a child effect; add it to our list of children. |
96 | 0 | children->push_back(SkRuntimeEffectPriv::VarAsChild(var, children->size())); |
97 | 0 | } else { |
98 | | // This is a uniform variable; make sure it exists in our list of uniforms, and |
99 | | // ensure that the type and layout matches between VS and FS. |
100 | 0 | auto iter = find_uniform(*uniforms, var.name()); |
101 | 0 | const auto& context = *program.fContext; |
102 | 0 | if (iter == uniforms->end()) { |
103 | 0 | uniforms->push_back(SkRuntimeEffectPriv::VarAsUniform(var, context, |
104 | 0 | offset)); |
105 | 0 | uniforms->back().flags |= stage; |
106 | 0 | } else { |
107 | | // Check that the two declarations are equivalent |
108 | 0 | size_t ignoredOffset = 0; |
109 | 0 | auto uniform = SkRuntimeEffectPriv::VarAsUniform(var, context, |
110 | 0 | &ignoredOffset); |
111 | 0 | if (uniform.isArray() != iter->isArray() || |
112 | 0 | uniform.type != iter->type || |
113 | 0 | uniform.count != iter->count) { |
114 | 0 | return {false, |
115 | 0 | SkStringPrintf("Uniform %.*s declared with different types" |
116 | 0 | " in vertex and fragment shaders.", |
117 | 0 | (int)var.name().size(), var.name().data())}; |
118 | 0 | } |
119 | 0 | if (uniform.isColor() != iter->isColor()) { |
120 | 0 | return {false, |
121 | 0 | SkStringPrintf("Uniform %.*s declared with different color" |
122 | 0 | " layout in vertex and fragment shaders.", |
123 | 0 | (int)var.name().size(), var.name().data())}; |
124 | 0 | } |
125 | 0 | (*iter).flags |= stage; |
126 | 0 | } |
127 | 0 | } |
128 | 0 | } |
129 | 0 | } |
130 | 20 | } |
131 | 8 | if (!foundMain) { |
132 | 0 | return {false, SkString("No main function found.")}; |
133 | 0 | } |
134 | 8 | return {true, {}}; |
135 | 8 | } |
136 | | |
137 | | using ColorType = SkMeshSpecificationPriv::ColorType; |
138 | | |
139 | 4 | ColorType get_fs_color_type(const SkSL::Program& fsProgram) { |
140 | 8 | for (const SkSL::ProgramElement* elem : fsProgram.elements()) { |
141 | 8 | if (elem->is<SkSL::FunctionDefinition>()) { |
142 | 4 | const SkSL::FunctionDefinition& defn = elem->as<SkSL::FunctionDefinition>(); |
143 | 4 | const SkSL::FunctionDeclaration& decl = defn.declaration(); |
144 | 4 | if (decl.isMain()) { |
145 | 4 | SkASSERT(decl.parameters().size() == 1 || decl.parameters().size() == 2); |
146 | 4 | if (decl.parameters().size() == 1) { |
147 | 2 | return ColorType::kNone; |
148 | 2 | } |
149 | 2 | const SkSL::Type& paramType = decl.parameters()[1]->type(); |
150 | 2 | SkASSERT(paramType.matches(*fsProgram.fContext->fTypes.fHalf4) || |
151 | 2 | paramType.matches(*fsProgram.fContext->fTypes.fFloat4)); |
152 | 2 | return paramType.matches(*fsProgram.fContext->fTypes.fHalf4) ? ColorType::kHalf4 |
153 | 2 | : ColorType::kFloat4; |
154 | 4 | } |
155 | 4 | } |
156 | 8 | } |
157 | 4 | SkUNREACHABLE; |
158 | 4 | } |
159 | | |
160 | | // This is a non-exhaustive check for the validity of a variable name. The SkSL compiler will |
161 | | // actually process the name. We're just guarding against having multiple tokens embedded in the |
162 | | // name before we put it into a struct definition. |
163 | 16 | static bool check_name(const SkString& name) { |
164 | 16 | if (name.isEmpty()) { |
165 | 0 | return false; |
166 | 0 | } |
167 | 92 | for (size_t i = 0; i < name.size(); ++i) { |
168 | 76 | if (name[i] != '_' && !std::isalnum(name[i], std::locale::classic())) { |
169 | 0 | return false; |
170 | 0 | } |
171 | 76 | } |
172 | 16 | return true; |
173 | 16 | } |
174 | | |
175 | 8 | static size_t attribute_type_size(Attribute::Type type) { |
176 | 8 | switch (type) { |
177 | 0 | case Attribute::Type::kFloat: return 4; |
178 | 6 | case Attribute::Type::kFloat2: return 2*4; |
179 | 0 | case Attribute::Type::kFloat3: return 3*4; |
180 | 0 | case Attribute::Type::kFloat4: return 4*4; |
181 | 2 | case Attribute::Type::kUByte4_unorm: return 4; |
182 | 8 | } |
183 | 8 | SkUNREACHABLE; |
184 | 8 | } |
185 | | |
186 | 8 | static const char* attribute_type_string(Attribute::Type type) { |
187 | 8 | switch (type) { |
188 | 0 | case Attribute::Type::kFloat: return "float"; |
189 | 6 | case Attribute::Type::kFloat2: return "float2"; |
190 | 0 | case Attribute::Type::kFloat3: return "float3"; |
191 | 0 | case Attribute::Type::kFloat4: return "float4"; |
192 | 2 | case Attribute::Type::kUByte4_unorm: return "half4"; |
193 | 8 | } |
194 | 8 | SkUNREACHABLE; |
195 | 8 | } |
196 | | |
197 | 8 | static const char* varying_type_string(Varying::Type type) { |
198 | 8 | switch (type) { |
199 | 0 | case Varying::Type::kFloat: return "float"; |
200 | 6 | case Varying::Type::kFloat2: return "float2"; |
201 | 0 | case Varying::Type::kFloat3: return "float3"; |
202 | 0 | case Varying::Type::kFloat4: return "float4"; |
203 | 0 | case Varying::Type::kHalf: return "half"; |
204 | 0 | case Varying::Type::kHalf2: return "half2"; |
205 | 0 | case Varying::Type::kHalf3: return "half3"; |
206 | 2 | case Varying::Type::kHalf4: return "half4"; |
207 | 8 | } |
208 | 8 | SkUNREACHABLE; |
209 | 8 | } |
210 | | |
211 | | std::tuple<bool, SkString> |
212 | | check_vertex_offsets_and_stride(SkSpan<const Attribute> attributes, |
213 | 4 | size_t stride) { |
214 | | // Vulkan 1.0 has a minimum maximum attribute count of 2048. |
215 | 4 | static_assert(SkMeshSpecification::kMaxStride <= 2048); |
216 | | // ES 2 has a max of 8. |
217 | 4 | static_assert(SkMeshSpecification::kMaxAttributes <= 8); |
218 | | // Four bytes alignment is required by Metal. |
219 | 4 | static_assert(SkMeshSpecification::kStrideAlignment >= 4); |
220 | 4 | static_assert(SkMeshSpecification::kOffsetAlignment >= 4); |
221 | | // ES2 has a minimum maximum of 8. We may need one for a broken gl_FragCoord workaround and |
222 | | // one for local coords. |
223 | 4 | static_assert(SkMeshSpecification::kMaxVaryings <= 6); |
224 | | |
225 | 4 | if (attributes.empty()) { |
226 | 0 | RETURN_ERROR("At least 1 attribute is required."); |
227 | 0 | } |
228 | 4 | if (attributes.size() > SkMeshSpecification::kMaxAttributes) { |
229 | 0 | RETURN_ERROR("A maximum of %zu attributes is allowed.", |
230 | 0 | SkMeshSpecification::kMaxAttributes); |
231 | 0 | } |
232 | 4 | static_assert(SkIsPow2(SkMeshSpecification::kStrideAlignment)); |
233 | 4 | if (stride == 0 || stride & (SkMeshSpecification::kStrideAlignment - 1)) { |
234 | 0 | RETURN_ERROR("Vertex stride must be a non-zero multiple of %zu.", |
235 | 0 | SkMeshSpecification::kStrideAlignment); |
236 | 0 | } |
237 | 4 | if (stride > SkMeshSpecification::kMaxStride) { |
238 | 0 | RETURN_ERROR("Stride cannot exceed %zu.", SkMeshSpecification::kMaxStride); |
239 | 0 | } |
240 | 8 | for (const auto& a : attributes) { |
241 | 8 | if (a.offset & (SkMeshSpecification::kOffsetAlignment - 1)) { |
242 | 0 | RETURN_ERROR("Attribute offset must be a multiple of %zu.", |
243 | 0 | SkMeshSpecification::kOffsetAlignment); |
244 | 0 | } |
245 | | // This equivalent to vertexAttributeAccessBeyondStride==VK_FALSE in |
246 | | // VK_KHR_portability_subset. First check is to avoid overflow in second check. |
247 | 8 | if (a.offset >= stride || a.offset + attribute_type_size(a.type) > stride) { |
248 | 0 | RETURN_ERROR("Attribute offset plus size cannot exceed stride."); |
249 | 0 | } |
250 | 8 | } |
251 | 4 | RETURN_SUCCESS; |
252 | 4 | } |
253 | | |
254 | | int check_for_passthrough_local_coords_and_dead_varyings(const SkSL::Program& fsProgram, |
255 | 4 | uint32_t* deadVaryingMask) { |
256 | 4 | SkASSERT(deadVaryingMask); |
257 | | |
258 | 4 | using namespace SkSL; |
259 | 4 | static constexpr int kFailed = -2; |
260 | | |
261 | 4 | class Visitor final : public SkSL::ProgramVisitor { |
262 | 4 | public: |
263 | 4 | Visitor(const Context& context) : fContext(context) {} |
264 | | |
265 | 4 | void visit(const Program& program) { ProgramVisitor::visit(program); } |
266 | | |
267 | 4 | int passthroughFieldIndex() const { return fPassthroughFieldIndex; } |
268 | | |
269 | 4 | uint32_t fieldUseMask() const { return fFieldUseMask; } |
270 | | |
271 | 4 | protected: |
272 | 8 | bool visitProgramElement(const ProgramElement& p) override { |
273 | 8 | if (p.is<StructDefinition>()) { |
274 | 4 | const auto& def = p.as<StructDefinition>(); |
275 | 4 | if (def.type().name() == "Varyings") { |
276 | 4 | fVaryingsType = &def.type(); |
277 | 4 | } |
278 | | // No reason to keep looking at this type definition. |
279 | 4 | return false; |
280 | 4 | } |
281 | 4 | if (p.is<FunctionDefinition>() && p.as<FunctionDefinition>().declaration().isMain()) { |
282 | 4 | SkASSERT(!fVaryings); |
283 | 4 | fVaryings = p.as<FunctionDefinition>().declaration().parameters()[0]; |
284 | | |
285 | 4 | SkASSERT(fVaryingsType && fVaryingsType->matches(fVaryings->type())); |
286 | | |
287 | 4 | fInMain = true; |
288 | 4 | bool result = ProgramVisitor::visitProgramElement(p); |
289 | 4 | fInMain = false; |
290 | 4 | return result; |
291 | 4 | } |
292 | 0 | return ProgramVisitor::visitProgramElement(p); |
293 | 4 | } |
294 | | |
295 | 10 | bool visitStatement(const Statement& s) override { |
296 | 10 | if (!fInMain) { |
297 | 0 | return ProgramVisitor::visitStatement(s); |
298 | 0 | } |
299 | | // We should only get here if are in main and therefore found the varyings parameter. |
300 | 10 | SkASSERT(fVaryings); |
301 | 10 | SkASSERT(fVaryingsType); |
302 | | |
303 | 10 | if (fPassthroughFieldIndex == kFailed) { |
304 | | // We've already determined there are return statements that aren't passthrough |
305 | | // or return different fields. |
306 | 0 | return ProgramVisitor::visitStatement(s); |
307 | 0 | } |
308 | 10 | if (!s.is<ReturnStatement>()) { |
309 | 6 | return ProgramVisitor::visitStatement(s); |
310 | 6 | } |
311 | | |
312 | | // We just detect simple cases like "return varyings.foo;" |
313 | 4 | const auto& rs = s.as<ReturnStatement>(); |
314 | 4 | SkASSERT(rs.expression()); |
315 | 4 | if (!rs.expression()->is<FieldAccess>()) { |
316 | 0 | this->passthroughFailed(); |
317 | 0 | return ProgramVisitor::visitStatement(s); |
318 | 0 | } |
319 | 4 | const auto& fa = rs.expression()->as<FieldAccess>(); |
320 | 4 | if (!fa.base()->is<VariableReference>()) { |
321 | 0 | this->passthroughFailed(); |
322 | 0 | return ProgramVisitor::visitStatement(s); |
323 | 0 | } |
324 | 4 | const auto& baseRef = fa.base()->as<VariableReference>(); |
325 | 4 | if (baseRef.variable() != fVaryings) { |
326 | 0 | this->passthroughFailed(); |
327 | 0 | return ProgramVisitor::visitStatement(s); |
328 | 0 | } |
329 | 4 | if (fPassthroughFieldIndex >= 0) { |
330 | | // We already found an OK return statement. Check if this one returns the same |
331 | | // field. |
332 | 0 | if (fa.fieldIndex() != fPassthroughFieldIndex) { |
333 | 0 | this->passthroughFailed(); |
334 | 0 | return ProgramVisitor::visitStatement(s); |
335 | 0 | } |
336 | | // We don't call our base class here because we don't want to hit visitExpression |
337 | | // and mark the returned field as used. |
338 | 0 | return false; |
339 | 0 | } |
340 | 4 | const Field& field = fVaryings->type().fields()[fa.fieldIndex()]; |
341 | 4 | if (!field.fType->matches(*fContext.fTypes.fFloat2)) { |
342 | 0 | this->passthroughFailed(); |
343 | 0 | return ProgramVisitor::visitStatement(s); |
344 | 0 | } |
345 | 4 | fPassthroughFieldIndex = fa.fieldIndex(); |
346 | | // We don't call our base class here because we don't want to hit visitExpression and |
347 | | // mark the returned field as used. |
348 | 4 | return false; |
349 | 4 | } SkMesh.cpp:check_for_passthrough_local_coords_and_dead_varyings(SkSL::Program const&, unsigned int*)::Visitor::visitStatement(SkSL::Statement const&) Line | Count | Source | 295 | 10 | bool visitStatement(const Statement& s) override { | 296 | 10 | if (!fInMain) { | 297 | 0 | return ProgramVisitor::visitStatement(s); | 298 | 0 | } | 299 | | // We should only get here if are in main and therefore found the varyings parameter. | 300 | 10 | SkASSERT(fVaryings); | 301 | 10 | SkASSERT(fVaryingsType); | 302 | | | 303 | 10 | if (fPassthroughFieldIndex == kFailed) { | 304 | | // We've already determined there are return statements that aren't passthrough | 305 | | // or return different fields. | 306 | 0 | return ProgramVisitor::visitStatement(s); | 307 | 0 | } | 308 | 10 | if (!s.is<ReturnStatement>()) { | 309 | 6 | return ProgramVisitor::visitStatement(s); | 310 | 6 | } | 311 | | | 312 | | // We just detect simple cases like "return varyings.foo;" | 313 | 4 | const auto& rs = s.as<ReturnStatement>(); | 314 | 4 | SkASSERT(rs.expression()); | 315 | 4 | if (!rs.expression()->is<FieldAccess>()) { | 316 | 0 | this->passthroughFailed(); | 317 | 0 | return ProgramVisitor::visitStatement(s); | 318 | 0 | } | 319 | 4 | const auto& fa = rs.expression()->as<FieldAccess>(); | 320 | 4 | if (!fa.base()->is<VariableReference>()) { | 321 | 0 | this->passthroughFailed(); | 322 | 0 | return ProgramVisitor::visitStatement(s); | 323 | 0 | } | 324 | 4 | const auto& baseRef = fa.base()->as<VariableReference>(); | 325 | 4 | if (baseRef.variable() != fVaryings) { | 326 | 0 | this->passthroughFailed(); | 327 | 0 | return ProgramVisitor::visitStatement(s); | 328 | 0 | } | 329 | 4 | if (fPassthroughFieldIndex >= 0) { | 330 | | // We already found an OK return statement. Check if this one returns the same | 331 | | // field. | 332 | 0 | if (fa.fieldIndex() != fPassthroughFieldIndex) { | 333 | 0 | this->passthroughFailed(); | 334 | 0 | return ProgramVisitor::visitStatement(s); | 335 | 0 | } | 336 | | // We don't call our base class here because we don't want to hit visitExpression | 337 | | // and mark the returned field as used. | 338 | 0 | return false; | 339 | 0 | } | 340 | 4 | const Field& field = fVaryings->type().fields()[fa.fieldIndex()]; | 341 | 4 | if (!field.fType->matches(*fContext.fTypes.fFloat2)) { | 342 | 0 | this->passthroughFailed(); | 343 | 0 | return ProgramVisitor::visitStatement(s); | 344 | 0 | } | 345 | 4 | fPassthroughFieldIndex = fa.fieldIndex(); | 346 | | // We don't call our base class here because we don't want to hit visitExpression and | 347 | | // mark the returned field as used. | 348 | 4 | return false; | 349 | 4 | } |
Unexecuted instantiation: SkMesh.cpp:check_for_passthrough_local_coords_and_dead_varyings(SkSL::Program const&, unsigned int*)::Visitor::visitStatement(SkSL::Statement const&) |
350 | | |
351 | 24 | bool visitExpression(const Expression& e) override { |
352 | | // Anything before the Varyings struct is defined doesn't matter. |
353 | 24 | if (!fVaryingsType) { |
354 | 0 | return false; |
355 | 0 | } |
356 | 24 | if (!e.is<FieldAccess>()) { |
357 | 18 | return ProgramVisitor::visitExpression(e); |
358 | 18 | } |
359 | 6 | const auto& fa = e.as<FieldAccess>(); |
360 | 6 | if (!fa.base()->type().matches(*fVaryingsType)) { |
361 | 0 | return ProgramVisitor::visitExpression(e); |
362 | 0 | } |
363 | 6 | fFieldUseMask |= 1 << fa.fieldIndex(); |
364 | 6 | return false; |
365 | 6 | } |
366 | | |
367 | 4 | private: |
368 | 4 | void passthroughFailed() { |
369 | 0 | if (fPassthroughFieldIndex >= 0) { |
370 | 0 | fFieldUseMask |= 1 << fPassthroughFieldIndex; |
371 | 0 | } |
372 | 0 | fPassthroughFieldIndex = kFailed; |
373 | 0 | } |
374 | | |
375 | 4 | const Context& fContext; |
376 | 4 | const Type* fVaryingsType = nullptr; |
377 | 4 | const Variable* fVaryings = nullptr; |
378 | 4 | int fPassthroughFieldIndex = -1; |
379 | 4 | bool fInMain = false; |
380 | 4 | uint32_t fFieldUseMask = 0; |
381 | 4 | }; |
382 | | |
383 | 4 | Visitor v(*fsProgram.fContext); |
384 | 4 | v.visit(fsProgram); |
385 | 4 | *deadVaryingMask = ~v.fieldUseMask(); |
386 | 4 | return v.passthroughFieldIndex(); |
387 | 4 | } check_for_passthrough_local_coords_and_dead_varyings(SkSL::Program const&, unsigned int*) Line | Count | Source | 255 | 4 | uint32_t* deadVaryingMask) { | 256 | 4 | SkASSERT(deadVaryingMask); | 257 | | | 258 | 4 | using namespace SkSL; | 259 | 4 | static constexpr int kFailed = -2; | 260 | | | 261 | 4 | class Visitor final : public SkSL::ProgramVisitor { | 262 | 4 | public: | 263 | 4 | Visitor(const Context& context) : fContext(context) {} | 264 | | | 265 | 4 | void visit(const Program& program) { ProgramVisitor::visit(program); } | 266 | | | 267 | 4 | int passthroughFieldIndex() const { return fPassthroughFieldIndex; } | 268 | | | 269 | 4 | uint32_t fieldUseMask() const { return fFieldUseMask; } | 270 | | | 271 | 4 | protected: | 272 | 4 | bool visitProgramElement(const ProgramElement& p) override { | 273 | 4 | if (p.is<StructDefinition>()) { | 274 | 4 | const auto& def = p.as<StructDefinition>(); | 275 | 4 | if (def.type().name() == "Varyings") { | 276 | 4 | fVaryingsType = &def.type(); | 277 | 4 | } | 278 | | // No reason to keep looking at this type definition. | 279 | 4 | return false; | 280 | 4 | } | 281 | 4 | if (p.is<FunctionDefinition>() && p.as<FunctionDefinition>().declaration().isMain()) { | 282 | 4 | SkASSERT(!fVaryings); | 283 | 4 | fVaryings = p.as<FunctionDefinition>().declaration().parameters()[0]; | 284 | | | 285 | 4 | SkASSERT(fVaryingsType && fVaryingsType->matches(fVaryings->type())); | 286 | | | 287 | 4 | fInMain = true; | 288 | 4 | bool result = ProgramVisitor::visitProgramElement(p); | 289 | 4 | fInMain = false; | 290 | 4 | return result; | 291 | 4 | } | 292 | 4 | return ProgramVisitor::visitProgramElement(p); | 293 | 4 | } | 294 | | | 295 | 4 | bool visitStatement(const Statement& s) override { | 296 | 4 | if (!fInMain) { | 297 | 4 | return ProgramVisitor::visitStatement(s); | 298 | 4 | } | 299 | | // We should only get here if are in main and therefore found the varyings parameter. | 300 | 4 | SkASSERT(fVaryings); | 301 | 4 | SkASSERT(fVaryingsType); | 302 | | | 303 | 4 | if (fPassthroughFieldIndex == kFailed) { | 304 | | // We've already determined there are return statements that aren't passthrough | 305 | | // or return different fields. | 306 | 4 | return ProgramVisitor::visitStatement(s); | 307 | 4 | } | 308 | 4 | if (!s.is<ReturnStatement>()) { | 309 | 4 | return ProgramVisitor::visitStatement(s); | 310 | 4 | } | 311 | | | 312 | | // We just detect simple cases like "return varyings.foo;" | 313 | 4 | const auto& rs = s.as<ReturnStatement>(); | 314 | 4 | SkASSERT(rs.expression()); | 315 | 4 | if (!rs.expression()->is<FieldAccess>()) { | 316 | 4 | this->passthroughFailed(); | 317 | 4 | return ProgramVisitor::visitStatement(s); | 318 | 4 | } | 319 | 4 | const auto& fa = rs.expression()->as<FieldAccess>(); | 320 | 4 | if (!fa.base()->is<VariableReference>()) { | 321 | 4 | this->passthroughFailed(); | 322 | 4 | return ProgramVisitor::visitStatement(s); | 323 | 4 | } | 324 | 4 | const auto& baseRef = fa.base()->as<VariableReference>(); | 325 | 4 | if (baseRef.variable() != fVaryings) { | 326 | 4 | this->passthroughFailed(); | 327 | 4 | return ProgramVisitor::visitStatement(s); | 328 | 4 | } | 329 | 4 | if (fPassthroughFieldIndex >= 0) { | 330 | | // We already found an OK return statement. Check if this one returns the same | 331 | | // field. | 332 | 4 | if (fa.fieldIndex() != fPassthroughFieldIndex) { | 333 | 4 | this->passthroughFailed(); | 334 | 4 | return ProgramVisitor::visitStatement(s); | 335 | 4 | } | 336 | | // We don't call our base class here because we don't want to hit visitExpression | 337 | | // and mark the returned field as used. | 338 | 4 | return false; | 339 | 4 | } | 340 | 4 | const Field& field = fVaryings->type().fields()[fa.fieldIndex()]; | 341 | 4 | if (!field.fType->matches(*fContext.fTypes.fFloat2)) { | 342 | 4 | this->passthroughFailed(); | 343 | 4 | return ProgramVisitor::visitStatement(s); | 344 | 4 | } | 345 | 4 | fPassthroughFieldIndex = fa.fieldIndex(); | 346 | | // We don't call our base class here because we don't want to hit visitExpression and | 347 | | // mark the returned field as used. | 348 | 4 | return false; | 349 | 4 | } | 350 | | | 351 | 4 | bool visitExpression(const Expression& e) override { | 352 | | // Anything before the Varyings struct is defined doesn't matter. | 353 | 4 | if (!fVaryingsType) { | 354 | 4 | return false; | 355 | 4 | } | 356 | 4 | if (!e.is<FieldAccess>()) { | 357 | 4 | return ProgramVisitor::visitExpression(e); | 358 | 4 | } | 359 | 4 | const auto& fa = e.as<FieldAccess>(); | 360 | 4 | if (!fa.base()->type().matches(*fVaryingsType)) { | 361 | 4 | return ProgramVisitor::visitExpression(e); | 362 | 4 | } | 363 | 4 | fFieldUseMask |= 1 << fa.fieldIndex(); | 364 | 4 | return false; | 365 | 4 | } | 366 | | | 367 | 4 | private: | 368 | 4 | void passthroughFailed() { | 369 | 4 | if (fPassthroughFieldIndex >= 0) { | 370 | 4 | fFieldUseMask |= 1 << fPassthroughFieldIndex; | 371 | 4 | } | 372 | 4 | fPassthroughFieldIndex = kFailed; | 373 | 4 | } | 374 | | | 375 | 4 | const Context& fContext; | 376 | 4 | const Type* fVaryingsType = nullptr; | 377 | 4 | const Variable* fVaryings = nullptr; | 378 | 4 | int fPassthroughFieldIndex = -1; | 379 | 4 | bool fInMain = false; | 380 | 4 | uint32_t fFieldUseMask = 0; | 381 | 4 | }; | 382 | | | 383 | 4 | Visitor v(*fsProgram.fContext); | 384 | 4 | v.visit(fsProgram); | 385 | 4 | *deadVaryingMask = ~v.fieldUseMask(); | 386 | 4 | return v.passthroughFieldIndex(); | 387 | 4 | } |
Unexecuted instantiation: check_for_passthrough_local_coords_and_dead_varyings(SkSL::Program const&, unsigned int*) |
388 | | |
389 | | SkMeshSpecification::Result SkMeshSpecification::Make(SkSpan<const Attribute> attributes, |
390 | | size_t vertexStride, |
391 | | SkSpan<const Varying> varyings, |
392 | | const SkString& vs, |
393 | 4 | const SkString& fs) { |
394 | 4 | return Make(attributes, |
395 | 4 | vertexStride, |
396 | 4 | varyings, |
397 | 4 | vs, |
398 | 4 | fs, |
399 | 4 | SkColorSpace::MakeSRGB(), |
400 | 4 | kPremul_SkAlphaType); |
401 | 4 | } |
402 | | |
403 | | SkMeshSpecification::Result SkMeshSpecification::Make(SkSpan<const Attribute> attributes, |
404 | | size_t vertexStride, |
405 | | SkSpan<const Varying> varyings, |
406 | | const SkString& vs, |
407 | | const SkString& fs, |
408 | 0 | sk_sp<SkColorSpace> cs) { |
409 | 0 | return Make(attributes, vertexStride, varyings, vs, fs, std::move(cs), kPremul_SkAlphaType); |
410 | 0 | } |
411 | | |
412 | | SkMeshSpecification::Result SkMeshSpecification::Make(SkSpan<const Attribute> attributes, |
413 | | size_t vertexStride, |
414 | | SkSpan<const Varying> varyings, |
415 | | const SkString& vs, |
416 | | const SkString& fs, |
417 | | sk_sp<SkColorSpace> cs, |
418 | 4 | SkAlphaType at) { |
419 | 4 | SkString attributesStruct("struct Attributes {\n"); |
420 | 8 | for (const auto& a : attributes) { |
421 | 8 | attributesStruct.appendf(" %s %s;\n", attribute_type_string(a.type), a.name.c_str()); |
422 | 8 | } |
423 | 4 | attributesStruct.append("};\n"); |
424 | | |
425 | 4 | bool userProvidedPositionVarying = false; |
426 | 4 | for (const auto& v : varyings) { |
427 | 4 | if (v.name.equals("position")) { |
428 | 0 | if (v.type != Varying::Type::kFloat2) { |
429 | 0 | return {nullptr, SkString("Varying \"position\" must have type float2.")}; |
430 | 0 | } |
431 | 0 | userProvidedPositionVarying = true; |
432 | 0 | } |
433 | 4 | } |
434 | | |
435 | 4 | STArray<kMaxVaryings, Varying> tempVaryings; |
436 | 4 | if (!userProvidedPositionVarying) { |
437 | | // Even though we check the # of varyings in MakeFromSourceWithStructs we check here, too, |
438 | | // to avoid overflow with + 1. |
439 | 4 | if (varyings.size() > kMaxVaryings - 1) { |
440 | 0 | RETURN_FAILURE("A maximum of %zu varyings is allowed.", kMaxVaryings); |
441 | 0 | } |
442 | 4 | for (const auto& v : varyings) { |
443 | 4 | tempVaryings.push_back(v); |
444 | 4 | } |
445 | 4 | tempVaryings.push_back(Varying{Varying::Type::kFloat2, SkString("position")}); |
446 | 4 | varyings = tempVaryings; |
447 | 4 | } |
448 | | |
449 | 4 | SkString varyingStruct("struct Varyings {\n"); |
450 | 8 | for (const auto& v : varyings) { |
451 | 8 | varyingStruct.appendf(" %s %s;\n", varying_type_string(v.type), v.name.c_str()); |
452 | 8 | } |
453 | 4 | varyingStruct.append("};\n"); |
454 | | |
455 | 4 | SkString fullVS; |
456 | 4 | fullVS.append(varyingStruct.c_str()); |
457 | 4 | fullVS.append(attributesStruct.c_str()); |
458 | 4 | fullVS.append(vs.c_str()); |
459 | | |
460 | 4 | SkString fullFS; |
461 | 4 | fullFS.append(varyingStruct.c_str()); |
462 | 4 | fullFS.append(fs.c_str()); |
463 | | |
464 | 4 | return MakeFromSourceWithStructs(attributes, |
465 | 4 | vertexStride, |
466 | 4 | varyings, |
467 | 4 | fullVS, |
468 | 4 | fullFS, |
469 | 4 | std::move(cs), |
470 | 4 | at); |
471 | 4 | } |
472 | | |
473 | | SkMeshSpecification::Result SkMeshSpecification::MakeFromSourceWithStructs( |
474 | | SkSpan<const Attribute> attributes, |
475 | | size_t stride, |
476 | | SkSpan<const Varying> varyings, |
477 | | const SkString& vs, |
478 | | const SkString& fs, |
479 | | sk_sp<SkColorSpace> cs, |
480 | 4 | SkAlphaType at) { |
481 | 4 | if (auto [ok, error] = check_vertex_offsets_and_stride(attributes, stride); !ok) { |
482 | 0 | return {nullptr, error}; |
483 | 0 | } |
484 | | |
485 | 8 | for (const auto& a : attributes) { |
486 | 8 | if (!check_name(a.name)) { |
487 | 0 | RETURN_FAILURE("\"%s\" is not a valid attribute name.", a.name.c_str()); |
488 | 0 | } |
489 | 8 | } |
490 | | |
491 | 4 | if (varyings.size() > kMaxVaryings) { |
492 | 0 | RETURN_FAILURE("A maximum of %zu varyings is allowed.", kMaxVaryings); |
493 | 0 | } |
494 | | |
495 | 8 | for (const auto& v : varyings) { |
496 | 8 | if (!check_name(v.name)) { |
497 | 0 | return {nullptr, SkStringPrintf("\"%s\" is not a valid varying name.", v.name.c_str())}; |
498 | 0 | } |
499 | 8 | } |
500 | | |
501 | 4 | std::vector<Uniform> uniforms; |
502 | 4 | std::vector<Child> children; |
503 | 4 | size_t offset = 0; |
504 | | |
505 | 4 | SkSL::Compiler compiler; |
506 | | |
507 | | // Disable memory pooling; this might slow down compilation slightly, but it will ensure that a |
508 | | // long-lived mesh specification doesn't waste memory. |
509 | 4 | SkSL::ProgramSettings settings; |
510 | 4 | settings.fUseMemoryPool = false; |
511 | | |
512 | | // TODO(skia:11209): Add SkCapabilities to the API, check against required version. |
513 | 4 | std::unique_ptr<SkSL::Program> vsProgram = compiler.convertProgram( |
514 | 4 | SkSL::ProgramKind::kMeshVertex, |
515 | 4 | std::string(vs.c_str()), |
516 | 4 | settings); |
517 | 4 | if (!vsProgram) { |
518 | 0 | RETURN_FAILURE("VS: %s", compiler.errorText().c_str()); |
519 | 0 | } |
520 | | |
521 | 4 | if (auto [result, error] = gather_uniforms_and_check_for_main( |
522 | 4 | *vsProgram, |
523 | 4 | &uniforms, |
524 | 4 | &children, |
525 | 4 | SkMeshSpecification::Uniform::Flags::kVertex_Flag, |
526 | 4 | &offset); |
527 | 4 | !result) { |
528 | 0 | return {nullptr, std::move(error)}; |
529 | 0 | } |
530 | | |
531 | 4 | if (SkSL::Analysis::CallsColorTransformIntrinsics(*vsProgram)) { |
532 | 0 | RETURN_FAILURE("Color transform intrinsics are not permitted in custom mesh shaders"); |
533 | 0 | } |
534 | | |
535 | 4 | std::unique_ptr<SkSL::Program> fsProgram = compiler.convertProgram( |
536 | 4 | SkSL::ProgramKind::kMeshFragment, |
537 | 4 | std::string(fs.c_str()), |
538 | 4 | settings); |
539 | | |
540 | 4 | if (!fsProgram) { |
541 | 0 | RETURN_FAILURE("FS: %s", compiler.errorText().c_str()); |
542 | 0 | } |
543 | | |
544 | 4 | if (auto [result, error] = gather_uniforms_and_check_for_main( |
545 | 4 | *fsProgram, |
546 | 4 | &uniforms, |
547 | 4 | &children, |
548 | 4 | SkMeshSpecification::Uniform::Flags::kFragment_Flag, |
549 | 4 | &offset); |
550 | 4 | !result) { |
551 | 0 | return {nullptr, std::move(error)}; |
552 | 0 | } |
553 | | |
554 | 4 | if (SkSL::Analysis::CallsColorTransformIntrinsics(*fsProgram)) { |
555 | 0 | RETURN_FAILURE("Color transform intrinsics are not permitted in custom mesh shaders"); |
556 | 0 | } |
557 | | |
558 | 4 | ColorType ct = get_fs_color_type(*fsProgram); |
559 | | |
560 | 4 | if (ct == ColorType::kNone) { |
561 | 2 | cs = nullptr; |
562 | 2 | at = kPremul_SkAlphaType; |
563 | 2 | } else { |
564 | 2 | if (!cs) { |
565 | 0 | return {nullptr, SkString{"Must provide a color space if FS returns a color."}}; |
566 | 0 | } |
567 | 2 | if (at == kUnknown_SkAlphaType) { |
568 | 0 | return {nullptr, SkString{"Must provide a valid alpha type if FS returns a color."}}; |
569 | 0 | } |
570 | 2 | } |
571 | | |
572 | 4 | uint32_t deadVaryingMask; |
573 | 4 | int passthroughLocalCoordsVaryingIndex = |
574 | 4 | check_for_passthrough_local_coords_and_dead_varyings(*fsProgram, &deadVaryingMask); |
575 | | |
576 | 4 | if (passthroughLocalCoordsVaryingIndex >= 0) { |
577 | 4 | SkASSERT(varyings[passthroughLocalCoordsVaryingIndex].type == Varying::Type::kFloat2); |
578 | 4 | } |
579 | | |
580 | 4 | return {sk_sp<SkMeshSpecification>(new SkMeshSpecification(attributes, |
581 | 4 | stride, |
582 | 4 | varyings, |
583 | 4 | passthroughLocalCoordsVaryingIndex, |
584 | 4 | deadVaryingMask, |
585 | 4 | std::move(uniforms), |
586 | 4 | std::move(children), |
587 | 4 | std::move(vsProgram), |
588 | 4 | std::move(fsProgram), |
589 | 4 | ct, |
590 | 4 | std::move(cs), |
591 | 4 | at)), |
592 | 4 | /*error=*/{}}; |
593 | 4 | } |
594 | | |
595 | 0 | SkMeshSpecification::~SkMeshSpecification() = default; |
596 | | |
597 | | SkMeshSpecification::SkMeshSpecification( |
598 | | SkSpan<const Attribute> attributes, |
599 | | size_t stride, |
600 | | SkSpan<const Varying> varyings, |
601 | | int passthroughLocalCoordsVaryingIndex, |
602 | | uint32_t deadVaryingMask, |
603 | | std::vector<Uniform> uniforms, |
604 | | std::vector<Child> children, |
605 | | std::unique_ptr<const SkSL::Program> vs, |
606 | | std::unique_ptr<const SkSL::Program> fs, |
607 | | ColorType ct, |
608 | | sk_sp<SkColorSpace> cs, |
609 | | SkAlphaType at) |
610 | | : fAttributes(attributes.begin(), attributes.end()) |
611 | | , fVaryings(varyings.begin(), varyings.end()) |
612 | | , fUniforms(std::move(uniforms)) |
613 | | , fChildren(std::move(children)) |
614 | | , fVS(std::move(vs)) |
615 | | , fFS(std::move(fs)) |
616 | | , fStride(stride) |
617 | | , fPassthroughLocalCoordsVaryingIndex(passthroughLocalCoordsVaryingIndex) |
618 | | , fDeadVaryingMask(deadVaryingMask) |
619 | | , fColorType(ct) |
620 | | , fColorSpace(std::move(cs)) |
621 | 4 | , fAlphaType(at) { |
622 | 4 | fHash = SkChecksum::Hash32(fVS->fSource->c_str(), fVS->fSource->size(), 0); |
623 | 4 | fHash = SkChecksum::Hash32(fFS->fSource->c_str(), fFS->fSource->size(), fHash); |
624 | | |
625 | | // The attributes and varyings SkSL struct declarations are included in the program source. |
626 | | // However, the attribute offsets and types need to be included, the latter because the SkSL |
627 | | // struct definition has the GPU type but not the CPU data format. |
628 | 8 | for (const auto& a : fAttributes) { |
629 | 8 | fHash = SkChecksum::Hash32(&a.offset, sizeof(a.offset), fHash); |
630 | 8 | fHash = SkChecksum::Hash32(&a.type, sizeof(a.type), fHash); |
631 | 8 | } |
632 | | |
633 | 4 | fHash = SkChecksum::Hash32(&stride, sizeof(stride), fHash); |
634 | | |
635 | 4 | uint64_t csHash = fColorSpace ? fColorSpace->hash() : 0; |
636 | 4 | fHash = SkChecksum::Hash32(&csHash, sizeof(csHash), fHash); |
637 | | |
638 | 4 | auto atInt = static_cast<uint32_t>(fAlphaType); |
639 | 4 | fHash = SkChecksum::Hash32(&atInt, sizeof(atInt), fHash); |
640 | 4 | } |
641 | | |
642 | 0 | size_t SkMeshSpecification::uniformSize() const { |
643 | 0 | return fUniforms.empty() ? 0 |
644 | 0 | : SkAlign4(fUniforms.back().offset + fUniforms.back().sizeInBytes()); |
645 | 0 | } |
646 | | |
647 | 0 | const Uniform* SkMeshSpecification::findUniform(std::string_view name) const { |
648 | 0 | for (const Uniform& uniform : fUniforms) { |
649 | 0 | if (uniform.name == name) { |
650 | 0 | return &uniform; |
651 | 0 | } |
652 | 0 | } |
653 | 0 | return nullptr; |
654 | 0 | } |
655 | | |
656 | 0 | const Child* SkMeshSpecification::findChild(std::string_view name) const { |
657 | 0 | for (const Child& child : fChildren) { |
658 | 0 | if (child.name == name) { |
659 | 0 | return &child; |
660 | 0 | } |
661 | 0 | } |
662 | 0 | return nullptr; |
663 | 0 | } |
664 | | |
665 | 0 | const Attribute* SkMeshSpecification::findAttribute(std::string_view name) const { |
666 | 0 | for (const Attribute& attr : fAttributes) { |
667 | 0 | if (name == attr.name.c_str()) { |
668 | 0 | return &attr; |
669 | 0 | } |
670 | 0 | } |
671 | 0 | return nullptr; |
672 | 0 | } |
673 | | |
674 | 0 | const Varying* SkMeshSpecification::findVarying(std::string_view name) const { |
675 | 0 | for (const Varying& varying : fVaryings) { |
676 | 0 | if (name == varying.name.c_str()) { |
677 | 0 | return &varying; |
678 | 0 | } |
679 | 0 | } |
680 | 0 | return nullptr; |
681 | 0 | } |
682 | | |
683 | | ////////////////////////////////////////////////////////////////////////////// |
684 | | |
685 | 0 | SkMesh::SkMesh() = default; |
686 | 0 | SkMesh::~SkMesh() = default; |
687 | | |
688 | 0 | SkMesh::SkMesh(const SkMesh&) = default; |
689 | 0 | SkMesh::SkMesh(SkMesh&&) = default; |
690 | | |
691 | 0 | SkMesh& SkMesh::operator=(const SkMesh&) = default; |
692 | 0 | SkMesh& SkMesh::operator=(SkMesh&&) = default; |
693 | | |
694 | | SkMesh::Result SkMesh::Make(sk_sp<SkMeshSpecification> spec, |
695 | | Mode mode, |
696 | | sk_sp<VertexBuffer> vb, |
697 | | size_t vertexCount, |
698 | | size_t vertexOffset, |
699 | | sk_sp<const SkData> uniforms, |
700 | | SkSpan<ChildPtr> children, |
701 | 0 | const SkRect& bounds) { |
702 | 0 | SkMesh mesh; |
703 | 0 | mesh.fSpec = std::move(spec); |
704 | 0 | mesh.fMode = mode; |
705 | 0 | mesh.fVB = std::move(vb); |
706 | 0 | mesh.fUniforms = std::move(uniforms); |
707 | 0 | mesh.fChildren.push_back_n(children.size(), children.data()); |
708 | 0 | mesh.fVCount = vertexCount; |
709 | 0 | mesh.fVOffset = vertexOffset; |
710 | 0 | mesh.fBounds = bounds; |
711 | 0 | auto [valid, msg] = mesh.validate(); |
712 | 0 | if (!valid) { |
713 | 0 | mesh = {}; |
714 | 0 | } |
715 | 0 | return {std::move(mesh), std::move(msg)}; |
716 | 0 | } |
717 | | |
718 | | SkMesh::Result SkMesh::MakeIndexed(sk_sp<SkMeshSpecification> spec, |
719 | | Mode mode, |
720 | | sk_sp<VertexBuffer> vb, |
721 | | size_t vertexCount, |
722 | | size_t vertexOffset, |
723 | | sk_sp<IndexBuffer> ib, |
724 | | size_t indexCount, |
725 | | size_t indexOffset, |
726 | | sk_sp<const SkData> uniforms, |
727 | | SkSpan<ChildPtr> children, |
728 | 0 | const SkRect& bounds) { |
729 | 0 | if (!ib) { |
730 | | // We check this before calling validate to disambiguate from a non-indexed mesh where |
731 | | // IB is expected to be null. |
732 | 0 | return {{}, SkString{"An index buffer is required."}}; |
733 | 0 | } |
734 | 0 | SkMesh mesh; |
735 | 0 | mesh.fSpec = std::move(spec); |
736 | 0 | mesh.fMode = mode; |
737 | 0 | mesh.fVB = std::move(vb); |
738 | 0 | mesh.fVCount = vertexCount; |
739 | 0 | mesh.fVOffset = vertexOffset; |
740 | 0 | mesh.fIB = std::move(ib); |
741 | 0 | mesh.fUniforms = std::move(uniforms); |
742 | 0 | mesh.fChildren.push_back_n(children.size(), children.data()); |
743 | 0 | mesh.fICount = indexCount; |
744 | 0 | mesh.fIOffset = indexOffset; |
745 | 0 | mesh.fBounds = bounds; |
746 | 0 | auto [valid, msg] = mesh.validate(); |
747 | 0 | if (!valid) { |
748 | 0 | mesh = {}; |
749 | 0 | } |
750 | 0 | return {std::move(mesh), std::move(msg)}; |
751 | 0 | } |
752 | | |
753 | 0 | bool SkMesh::isValid() const { |
754 | 0 | bool valid = SkToBool(fSpec); |
755 | 0 | SkASSERT(valid == std::get<0>(this->validate())); |
756 | 0 | return valid; |
757 | 0 | } Unexecuted instantiation: SkMesh::isValid() const Unexecuted instantiation: SkMesh::isValid() const |
758 | | |
759 | 0 | static size_t min_vcount_for_mode(SkMesh::Mode mode) { |
760 | 0 | switch (mode) { |
761 | 0 | case SkMesh::Mode::kTriangles: return 3; |
762 | 0 | case SkMesh::Mode::kTriangleStrip: return 3; |
763 | 0 | } |
764 | 0 | SkUNREACHABLE; |
765 | 0 | } |
766 | | |
767 | 0 | std::tuple<bool, SkString> SkMesh::validate() const { |
768 | 0 | #define FAIL_MESH_VALIDATE(...) return std::make_tuple(false, SkStringPrintf(__VA_ARGS__)) |
769 | 0 | if (!fSpec) { |
770 | 0 | FAIL_MESH_VALIDATE("SkMeshSpecification is required."); |
771 | 0 | } |
772 | | |
773 | 0 | if (!fVB) { |
774 | 0 | FAIL_MESH_VALIDATE("A vertex buffer is required."); |
775 | 0 | } |
776 | | |
777 | 0 | if (fSpec->children().size() != SkToSizeT(fChildren.size())) { |
778 | 0 | FAIL_MESH_VALIDATE("The mesh specification declares %zu child effects, " |
779 | 0 | "but the mesh supplies %d.", |
780 | 0 | fSpec->children().size(), |
781 | 0 | fChildren.size()); |
782 | 0 | } |
783 | | |
784 | 0 | for (int index = 0; index < fChildren.size(); ++index) { |
785 | 0 | const SkRuntimeEffect::Child& meshSpecChild = fSpec->children()[index]; |
786 | 0 | if (fChildren[index].type().has_value()) { |
787 | 0 | if (meshSpecChild.type != fChildren[index].type()) { |
788 | 0 | FAIL_MESH_VALIDATE("Child effect '%.*s' was specified as a %s, but passed as a %s.", |
789 | 0 | (int)meshSpecChild.name.size(), meshSpecChild.name.data(), |
790 | 0 | SkRuntimeEffectPriv::ChildTypeToStr(meshSpecChild.type), |
791 | 0 | SkRuntimeEffectPriv::ChildTypeToStr(*fChildren[index].type())); |
792 | 0 | } |
793 | 0 | } |
794 | 0 | } |
795 | | |
796 | 0 | auto vb = static_cast<SkMeshPriv::VB*>(fVB.get()); |
797 | 0 | auto ib = static_cast<SkMeshPriv::IB*>(fIB.get()); |
798 | |
|
799 | 0 | SkSafeMath sm; |
800 | 0 | size_t vsize = sm.mul(fSpec->stride(), fVCount); |
801 | 0 | if (sm.add(vsize, fVOffset) > vb->size()) { |
802 | 0 | FAIL_MESH_VALIDATE("The vertex buffer offset and vertex count reads beyond the end of the" |
803 | 0 | " vertex buffer."); |
804 | 0 | } |
805 | | |
806 | 0 | if (fVOffset%fSpec->stride() != 0) { |
807 | 0 | FAIL_MESH_VALIDATE("The vertex offset (%zu) must be a multiple of the vertex stride (%zu).", |
808 | 0 | fVOffset, |
809 | 0 | fSpec->stride()); |
810 | 0 | } |
811 | | |
812 | 0 | if (size_t uniformSize = fSpec->uniformSize()) { |
813 | 0 | if (!fUniforms || fUniforms->size() < uniformSize) { |
814 | 0 | FAIL_MESH_VALIDATE("The uniform data is %zu bytes but must be at least %zu.", |
815 | 0 | fUniforms->size(), |
816 | 0 | uniformSize); |
817 | 0 | } |
818 | 0 | } |
819 | | |
820 | 0 | auto modeToStr = [](Mode m) { |
821 | 0 | switch (m) { |
822 | 0 | case Mode::kTriangles: return "triangles"; |
823 | 0 | case Mode::kTriangleStrip: return "triangle-strip"; |
824 | 0 | } |
825 | 0 | SkUNREACHABLE; |
826 | 0 | }; |
827 | 0 | if (ib) { |
828 | 0 | if (fICount < min_vcount_for_mode(fMode)) { |
829 | 0 | FAIL_MESH_VALIDATE("%s mode requires at least %zu indices but index count is %zu.", |
830 | 0 | modeToStr(fMode), |
831 | 0 | min_vcount_for_mode(fMode), |
832 | 0 | fICount); |
833 | 0 | } |
834 | 0 | size_t isize = sm.mul(sizeof(uint16_t), fICount); |
835 | 0 | if (sm.add(isize, fIOffset) > ib->size()) { |
836 | 0 | FAIL_MESH_VALIDATE("The index buffer offset and index count reads beyond the end of the" |
837 | 0 | " index buffer."); |
838 | |
|
839 | 0 | } |
840 | | // If we allow 32 bit indices then this should enforce 4 byte alignment in that case. |
841 | 0 | if (!SkIsAlign2(fIOffset)) { |
842 | 0 | FAIL_MESH_VALIDATE("The index offset must be a multiple of 2."); |
843 | 0 | } |
844 | 0 | } else { |
845 | 0 | if (fVCount < min_vcount_for_mode(fMode)) { |
846 | 0 | FAIL_MESH_VALIDATE("%s mode requires at least %zu vertices but vertex count is %zu.", |
847 | 0 | modeToStr(fMode), |
848 | 0 | min_vcount_for_mode(fMode), |
849 | 0 | fICount); |
850 | 0 | } |
851 | 0 | SkASSERT(!fICount); |
852 | 0 | SkASSERT(!fIOffset); |
853 | 0 | } |
854 | | |
855 | 0 | if (!sm.ok()) { |
856 | 0 | FAIL_MESH_VALIDATE("Overflow"); |
857 | 0 | } |
858 | 0 | #undef FAIL_MESH_VALIDATE |
859 | 0 | return {true, {}}; |
860 | 0 | } Unexecuted instantiation: SkMesh::validate() const Unexecuted instantiation: SkMesh::validate() const |
861 | | |
862 | | ////////////////////////////////////////////////////////////////////////////// |
863 | | |
864 | 0 | static inline bool check_update(const void* data, size_t offset, size_t size, size_t bufferSize) { |
865 | 0 | SkSafeMath sm; |
866 | 0 | return data && |
867 | 0 | size && |
868 | 0 | SkIsAlign4(offset) && |
869 | 0 | SkIsAlign4(size) && |
870 | 0 | sm.add(offset, size) <= bufferSize && |
871 | 0 | sm.ok(); |
872 | 0 | } |
873 | | |
874 | | bool SkMesh::IndexBuffer::update(GrDirectContext* dc, |
875 | | const void* data, |
876 | | size_t offset, |
877 | 0 | size_t size) { |
878 | 0 | return check_update(data, offset, size, this->size()) && this->onUpdate(dc, data, offset, size); |
879 | 0 | } |
880 | | |
881 | | bool SkMesh::VertexBuffer::update(GrDirectContext* dc, |
882 | | const void* data, |
883 | | size_t offset, |
884 | 0 | size_t size) { |
885 | 0 | return check_update(data, offset, size, this->size()) && this->onUpdate(dc, data, offset, size); |
886 | 0 | } |
887 | | |
888 | | namespace SkMeshes { |
889 | 0 | sk_sp<IndexBuffer> MakeIndexBuffer(const void* data, size_t size) { |
890 | 0 | return SkMeshPriv::CpuIndexBuffer::Make(data, size); |
891 | 0 | } |
892 | | |
893 | 0 | sk_sp<IndexBuffer> CopyIndexBuffer(const sk_sp<IndexBuffer>& src) { |
894 | 0 | if (!src) { |
895 | 0 | return nullptr; |
896 | 0 | } |
897 | 0 | auto* ib = static_cast<SkMeshPriv::IB*>(src.get()); |
898 | 0 | const void* data = ib->peek(); |
899 | 0 | if (!data) { |
900 | 0 | return nullptr; |
901 | 0 | } |
902 | 0 | return MakeIndexBuffer(data, ib->size()); |
903 | 0 | } |
904 | | |
905 | 0 | sk_sp<VertexBuffer> MakeVertexBuffer(const void* data, size_t size) { |
906 | 0 | return SkMeshPriv::CpuVertexBuffer::Make(data, size); |
907 | 0 | } |
908 | | |
909 | 0 | sk_sp<VertexBuffer> CopyVertexBuffer(const sk_sp<VertexBuffer>& src) { |
910 | 0 | if (!src) { |
911 | 0 | return nullptr; |
912 | 0 | } |
913 | 0 | auto* vb = static_cast<SkMeshPriv::VB*>(src.get()); |
914 | 0 | const void* data = vb->peek(); |
915 | 0 | if (!data) { |
916 | 0 | return nullptr; |
917 | 0 | } |
918 | 0 | return MakeVertexBuffer(data, vb->size()); |
919 | 0 | } |
920 | | } // namespace SkMeshes |