• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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/SkCustomMesh.h"
9 
10 #ifdef SK_ENABLE_SKSL
11 
12 #include "src/core/SkCustomMeshPriv.h"
13 #include "src/gpu/GrShaderCaps.h"
14 #include "src/sksl/SkSLCompiler.h"
15 #include "src/sksl/SkSLSharedCompiler.h"
16 #include "src/sksl/ir/SkSLFunctionDefinition.h"
17 #include "src/sksl/ir/SkSLProgram.h"
18 #include "src/sksl/ir/SkSLVarDeclarations.h"
19 
20 #include <locale>
21 
22 using Attribute = SkCustomMeshSpecification::Attribute;
23 using Varying   = SkCustomMeshSpecification::Varying;
24 
25 #define RETURN_FAILURE(...) return Result{nullptr, SkStringPrintf(__VA_ARGS__)}
26 
27 #define RETURN_ERROR(...) return std::make_tuple(false, SkStringPrintf(__VA_ARGS__))
28 
29 #define RETURN_SUCCESS return std::make_tuple(true, SkString{})
30 
has_main(const SkSL::Program & p)31 static bool has_main(const SkSL::Program& p) {
32     for (const SkSL::ProgramElement* elem : p.elements()) {
33         if (elem->is<SkSL::FunctionDefinition>()) {
34             const SkSL::FunctionDefinition& defn = elem->as<SkSL::FunctionDefinition>();
35             const SkSL::FunctionDeclaration& decl = defn.declaration();
36             if (decl.isMain()) {
37                 return true;
38             }
39         }
40     }
41     return false;
42 }
43 
44 using ColorType = SkCustomMeshSpecificationPriv::ColorType;
45 
46 static std::tuple<ColorType, bool>
get_fs_color_type_and_local_coords(const SkSL::Program & fsProgram)47 get_fs_color_type_and_local_coords(const SkSL::Program& fsProgram) {
48     for (const SkSL::ProgramElement* elem : fsProgram.elements()) {
49         if (elem->is<SkSL::FunctionDefinition>()) {
50             const SkSL::FunctionDefinition& defn = elem->as<SkSL::FunctionDefinition>();
51             const SkSL::FunctionDeclaration& decl = defn.declaration();
52             if (decl.isMain()) {
53 
54                 SkCustomMeshSpecificationPriv::ColorType ct;
55                 SkASSERT(decl.parameters().size() == 1 || decl.parameters().size() == 2);
56                 if (decl.parameters().size() == 1) {
57                     ct = ColorType::kNone;
58                 } else {
59                     const SkSL::Type& paramType = decl.parameters()[1]->type();
60                     SkASSERT(paramType.matches(*fsProgram.fContext->fTypes.fHalf4) ||
61                              paramType.matches(*fsProgram.fContext->fTypes.fFloat4));
62                     ct = paramType.matches(*fsProgram.fContext->fTypes.fHalf4)
63                                  ? ColorType::kHalf4
64                                  : ColorType::kFloat4;
65                 }
66 
67                 const SkSL::Type& returnType = decl.returnType();
68                 SkASSERT(returnType.matches(*fsProgram.fContext->fTypes.fVoid) ||
69                          returnType.matches(*fsProgram.fContext->fTypes.fFloat2));
70                 bool hasLocalCoords = returnType.matches(*fsProgram.fContext->fTypes.fFloat2);
71 
72                 return std::make_tuple(ct, hasLocalCoords);
73             }
74         }
75     }
76     SkUNREACHABLE;
77 }
78 
79 // This is a non-exhaustive check for the validity of a variable name. The SkSL compiler will
80 // actually process the name. We're just guarding against having multiple tokens embedded in the
81 // name before we put it into a struct definition.
check_name(const SkString & name)82 static bool check_name(const SkString& name) {
83     if (name.isEmpty()) {
84         return false;
85     }
86     for (size_t i = 0; i < name.size(); ++i) {
87         if (name[i] != '_' && !std::isalnum(name[i], std::locale::classic())) {
88             return false;
89         }
90     }
91     return true;
92 }
93 
attribute_type_size(Attribute::Type type)94 static size_t attribute_type_size(Attribute::Type type) {
95     switch (type) {
96         case Attribute::Type::kFloat:         return 4;
97         case Attribute::Type::kFloat2:        return 2*4;
98         case Attribute::Type::kFloat3:        return 3*4;
99         case Attribute::Type::kFloat4:        return 4*4;
100         case Attribute::Type::kUByte4_unorm:  return 4;
101     }
102     SkUNREACHABLE;
103 };
104 
attribute_type_string(Attribute::Type type)105 static const char* attribute_type_string(Attribute::Type type) {
106     switch (type) {
107         case Attribute::Type::kFloat:         return "float";
108         case Attribute::Type::kFloat2:        return "float2";
109         case Attribute::Type::kFloat3:        return "float3";
110         case Attribute::Type::kFloat4:        return "float4";
111         case Attribute::Type::kUByte4_unorm:  return "half4";
112     }
113     SkUNREACHABLE;
114 };
115 
varying_type_string(Varying::Type type)116 static const char* varying_type_string(Varying::Type type) {
117     switch (type) {
118         case Varying::Type::kFloat:  return "float";
119         case Varying::Type::kFloat2: return "float2";
120         case Varying::Type::kFloat3: return "float3";
121         case Varying::Type::kFloat4: return "float4";
122         case Varying::Type::kHalf:   return "half";
123         case Varying::Type::kHalf2:  return "half2";
124         case Varying::Type::kHalf3:  return "half3";
125         case Varying::Type::kHalf4:  return "half4";
126     }
127     SkUNREACHABLE;
128 };
129 
130 std::tuple<bool, SkString>
check_vertex_offsets_and_stride(SkSpan<const Attribute> attributes,size_t stride)131 check_vertex_offsets_and_stride(SkSpan<const Attribute> attributes,
132                                 size_t                  stride) {
133     // Vulkan 1.0 has a minimum maximum attribute count of 2048.
134     static_assert(SkCustomMeshSpecification::kMaxStride       <= 2048);
135     // ES 2 has a max of 8.
136     static_assert(SkCustomMeshSpecification::kMaxAttributes   <= 8);
137     // Four bytes alignment is required by Metal.
138     static_assert(SkCustomMeshSpecification::kStrideAlignment >= 4);
139     static_assert(SkCustomMeshSpecification::kOffsetAlignment >= 4);
140     // ES2 has a minimum maximum of 8. We may need one for a broken gl_FragCoord workaround and
141     // one for local coords.
142     static_assert(SkCustomMeshSpecification::kMaxVaryings     <= 6);
143 
144     if (attributes.empty()) {
145         RETURN_ERROR("At least 1 attribute is required.");
146     }
147     if (attributes.size() > SkCustomMeshSpecification::kMaxAttributes) {
148         RETURN_ERROR("A maximum of %zu attributes is allowed.",
149                      SkCustomMeshSpecification::kMaxAttributes);
150     }
151     static_assert(SkIsPow2(SkCustomMeshSpecification::kStrideAlignment));
152     if (stride == 0 || stride & (SkCustomMeshSpecification::kStrideAlignment - 1)) {
153         RETURN_ERROR("Vertex stride must be a non-zero multiple of %zu.",
154                      SkCustomMeshSpecification::kStrideAlignment);
155     }
156     if (stride > SkCustomMeshSpecification::kMaxStride) {
157         RETURN_ERROR("Stride cannot exceed %zu.", SkCustomMeshSpecification::kMaxStride);
158     }
159     for (const auto& a : attributes) {
160         if (a.offset & (SkCustomMeshSpecification::kOffsetAlignment - 1)) {
161             RETURN_ERROR("Attribute offset must be a multiple of %zu.",
162                          SkCustomMeshSpecification::kOffsetAlignment);
163         }
164         // This equivalent to vertexAttributeAccessBeyondStride==VK_FALSE in
165         // VK_KHR_portability_subset. First check is to avoid overflow in second check.
166         if (a.offset >= stride || a.offset + attribute_type_size(a.type) > stride) {
167             RETURN_ERROR("Attribute offset plus size cannot exceed stride.");
168         }
169     }
170     RETURN_SUCCESS;
171 }
172 
Make(SkSpan<const Attribute> attributes,size_t vertexStride,SkSpan<const Varying> varyings,const SkString & vs,const SkString & fs,sk_sp<SkColorSpace> cs,SkAlphaType at)173 SkCustomMeshSpecification::Result SkCustomMeshSpecification::Make(
174         SkSpan<const Attribute> attributes,
175         size_t                  vertexStride,
176         SkSpan<const Varying>   varyings,
177         const SkString&         vs,
178         const SkString&         fs,
179         sk_sp<SkColorSpace>     cs,
180         SkAlphaType             at) {
181     SkString attributesStruct("struct Attributes {\n");
182     for (const auto& a : attributes) {
183         attributesStruct.appendf("  %s %s;\n", attribute_type_string(a.type), a.name.c_str());
184     }
185     attributesStruct.append("};\n");
186 
187     SkString varyingStruct("struct Varyings {\n");
188     for (const auto& v : varyings) {
189         varyingStruct.appendf("  %s %s;\n", varying_type_string(v.type), v.name.c_str());
190     }
191     // Throw in an unused variable to avoid an empty struct, which is illegal.
192     if (varyings.empty()) {
193         varyingStruct.append("  bool _empty_;\n");
194     }
195     varyingStruct.append("};\n");
196 
197     SkString fullVS;
198     fullVS.append(varyingStruct.c_str());
199     fullVS.append(attributesStruct.c_str());
200     fullVS.append(vs.c_str());
201 
202     SkString fullFS;
203     fullFS.append(varyingStruct.c_str());
204     fullFS.append(fs.c_str());
205 
206     return MakeFromSourceWithStructs(attributes,
207                                      vertexStride,
208                                      varyings,
209                                      fullVS,
210                                      fullFS,
211                                      std::move(cs),
212                                      at);
213 }
214 
MakeFromSourceWithStructs(SkSpan<const Attribute> attributes,size_t stride,SkSpan<const Varying> varyings,const SkString & vs,const SkString & fs,sk_sp<SkColorSpace> cs,SkAlphaType at)215 SkCustomMeshSpecification::Result SkCustomMeshSpecification::MakeFromSourceWithStructs(
216         SkSpan<const Attribute> attributes,
217         size_t                  stride,
218         SkSpan<const Varying>   varyings,
219         const SkString&         vs,
220         const SkString&         fs,
221         sk_sp<SkColorSpace>     cs,
222         SkAlphaType             at) {
223     if (auto [ok, error] = check_vertex_offsets_and_stride(attributes, stride); !ok) {
224         return {nullptr, error};
225     }
226 
227     for (const auto& a : attributes) {
228         if (!check_name(a.name)) {
229             RETURN_FAILURE("\"%s\" is not a valid attribute name.", a.name.c_str());
230         }
231     }
232 
233     if (varyings.size() > kMaxVaryings) {
234         RETURN_FAILURE("A maximum of %zu varyings is allowed.", kMaxVaryings);
235     }
236 
237     for (const auto& v : varyings) {
238         if (!check_name(v.name)) {
239             return {nullptr, SkStringPrintf("\"%s\" is not a valid varying name.", v.name.c_str())};
240         }
241     }
242 
243     SkSL::SharedCompiler compiler;
244     SkSL::Program::Settings settings;
245     settings.fEnforceES2Restrictions = true;
246     std::unique_ptr<SkSL::Program> vsProgram = compiler->convertProgram(
247             SkSL::ProgramKind::kCustomMeshVertex,
248             std::string(vs.c_str()),
249             settings);
250     if (!vsProgram) {
251         RETURN_FAILURE("VS: %s", compiler->errorText().c_str());
252     }
253     if (!has_main(*vsProgram)) {
254         RETURN_FAILURE("Vertex shader must have main function.");
255     }
256     if (SkSL::Analysis::CallsColorTransformIntrinsics(*vsProgram)) {
257         RETURN_FAILURE("Color transform intrinsics are not permitted in custom mesh shaders");
258     }
259 
260     std::unique_ptr<SkSL::Program> fsProgram = compiler->convertProgram(
261             SkSL::ProgramKind::kCustomMeshFragment,
262             std::string(fs.c_str()),
263             settings);
264 
265     if (!fsProgram) {
266         RETURN_FAILURE("FS: %s", compiler->errorText().c_str());
267     }
268     if (!has_main(*fsProgram)) {
269         RETURN_FAILURE("Fragment shader must have main function.");
270     }
271     if (SkSL::Analysis::CallsColorTransformIntrinsics(*fsProgram)) {
272         RETURN_FAILURE("Color transform intrinsics are not permitted in custom mesh shaders");
273     }
274 
275     auto [ct, hasLocalCoords] = get_fs_color_type_and_local_coords(*fsProgram);
276 
277     if (ct == ColorType::kNone) {
278         cs = nullptr;
279         at = kPremul_SkAlphaType;
280     } else {
281         if (!cs) {
282             return {nullptr, SkString{"Must provide a color space if FS returns a color."}};
283         }
284         if (at == kUnknown_SkAlphaType) {
285             return {nullptr, SkString{"Must provide a valid alpha type if FS returns a color."}};
286         }
287     }
288 
289     return {sk_sp<SkCustomMeshSpecification>(new SkCustomMeshSpecification(attributes,
290                                                                            stride,
291                                                                            varyings,
292                                                                            std::move(vsProgram),
293                                                                            std::move(fsProgram),
294                                                                            ct,
295                                                                            hasLocalCoords,
296                                                                            std::move(cs),
297                                                                            at)),
298             /*error=*/ {}};
299 }
300 
301 SkCustomMeshSpecification::~SkCustomMeshSpecification() = default;
302 
SkCustomMeshSpecification(SkSpan<const Attribute> attributes,size_t stride,SkSpan<const Varying> varyings,std::unique_ptr<SkSL::Program> vs,std::unique_ptr<SkSL::Program> fs,ColorType ct,bool hasLocalCoords,sk_sp<SkColorSpace> cs,SkAlphaType at)303 SkCustomMeshSpecification::SkCustomMeshSpecification(SkSpan<const Attribute>        attributes,
304                                                      size_t                         stride,
305                                                      SkSpan<const Varying>          varyings,
306                                                      std::unique_ptr<SkSL::Program> vs,
307                                                      std::unique_ptr<SkSL::Program> fs,
308                                                      ColorType                      ct,
309                                                      bool                           hasLocalCoords,
310                                                      sk_sp<SkColorSpace>            cs,
311                                                      SkAlphaType                    at)
312         : fAttributes(attributes.begin(), attributes.end())
313         , fVaryings(varyings.begin(), varyings.end())
314         , fVS(std::move(vs))
315         , fFS(std::move(fs))
316         , fStride(stride)
317         , fColorType(ct)
318         , fHasLocalCoords(hasLocalCoords)
319         , fColorSpace(std::move(cs))
320         , fAlphaType(at) {
321     fHash = SkOpts::hash_fn(fVS->fSource->c_str(), fVS->fSource->size(), 0);
322     fHash = SkOpts::hash_fn(fFS->fSource->c_str(), fFS->fSource->size(), fHash);
323 
324     // The attributes and varyings SkSL struct declarations are included in the program source.
325     // However, the attribute offsets and types need to be included, the latter because the SkSL
326     // struct definition has the GPU type but not the CPU data format.
327     for (const auto& a : fAttributes) {
328         fHash = SkOpts::hash_fn(&a.offset, sizeof(a.offset), fHash);
329         fHash = SkOpts::hash_fn(&a.type,   sizeof(a.type),   fHash);
330     }
331 
332     fHash = SkOpts::hash_fn(&stride, sizeof(stride), fHash);
333 
334     uint64_t csHash = fColorSpace ? fColorSpace->hash() : 0;
335     fHash = SkOpts::hash_fn(&csHash, sizeof(csHash), fHash);
336 
337     auto atInt = static_cast<uint32_t>(fAlphaType);
338     fHash = SkOpts::hash_fn(&atInt, sizeof(atInt), fHash);
339 }
340 #endif //SK_ENABLE_SKSL
341