• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright 2018 Google Inc.
3  *
4  * Use of this source code is governed by a BSD-style license that can be
5  * found in the LICENSE file.
6  */
7 
8 #include "src/sksl/SkSLCPPUniformCTypes.h"
9 #include "src/sksl/SkSLHCodeGenerator.h"
10 #include "src/sksl/SkSLStringStream.h"
11 
12 #include <vector>
13 
14 namespace SkSL {
15 
16 /////////////////////////
17 // Template evaluation //
18 /////////////////////////
19 
eval_template(const String & format,const std::vector<String> & tokens,const std::vector<const String * > & values)20 static String eval_template(const String& format, const std::vector<String>& tokens,
21                             const std::vector<const String*>& values) {
22     StringStream stream;
23 
24     int tokenNameStart = -1;
25     for (size_t i = 0; i < format.size(); i++) {
26         if (tokenNameStart >= 0) {
27             // Within a token name so check if it is the end
28             if (format[i] == '}') {
29                 // Skip 2 extra characters at the beginning for the $ and {, which must exist since
30                 // otherwise tokenNameStart < 0
31                 String token(format.c_str() + tokenNameStart + 2, i - tokenNameStart - 2);
32                 // Search for the token in supported list
33                 bool found = false;
34                 for (size_t j = 0; j < tokens.size(); j++) {
35                     if (token == tokens[j]) {
36                         // Found a match so append the value corresponding to j to the output
37                         stream.writeText(values[j]->c_str());
38                         found = true;
39                         break;
40                     }
41                 }
42 
43                 if (!found) {
44                     // Write out original characters as if we didn't consider it to be a token name
45                     stream.writeText("${");
46                     stream.writeText(token.c_str());
47                     stream.writeText("}");
48                 }
49 
50                 // And end the token name state
51                 tokenNameStart = -1;
52             }
53         } else {
54             // Outside of a token name, so check if this character starts a name:
55             // i == $ and i+1 == {
56             if (i < format.size() - 1 && format[i] == '$' && format[i + 1] == '{') {
57                 // Begin parsing the token
58                 tokenNameStart = i;
59             } else {
60                 // Just a character so append it
61                 stream.write8(format[i]);
62             }
63         }
64     }
65 
66     return stream.str();
67 }
68 
determine_inline_from_template(const String & uniformTemplate)69 static bool determine_inline_from_template(const String& uniformTemplate) {
70     // True if there is at most one instance of the ${var} template matcher in fUniformTemplate.
71     int firstMatch = uniformTemplate.find("${var}");
72 
73     if (firstMatch < 0) {
74         // Template doesn't use the value variable at all, so it can "inlined"
75         return true;
76     }
77 
78     // Check for another occurrence of ${var}, after firstMatch + 6
79     int secondMatch = uniformTemplate.find("${var}", firstMatch + strlen("${var}"));
80     // If there's no second match, then the value can be inlined in the c++ code
81     return secondMatch < 0;
82 }
83 
84 ///////////////////////////////////////
85 // UniformCTypeMapper implementation //
86 ///////////////////////////////////////
87 
dirtyExpression(const String & newVar,const String & oldVar) const88 String UniformCTypeMapper::dirtyExpression(const String& newVar, const String& oldVar) const {
89     if (fSupportsTracking) {
90         std::vector<String> tokens = { "newVar", "oldVar" };
91         std::vector<const String*> values = { &newVar, &oldVar };
92         return eval_template(fDirtyExpressionTemplate, tokens, values);
93     } else {
94         return "";
95     }
96 }
97 
saveState(const String & newVar,const String & oldVar) const98 String UniformCTypeMapper::saveState(const String& newVar, const String& oldVar) const {
99     if (fSupportsTracking) {
100         std::vector<String> tokens = { "newVar", "oldVar" };
101         std::vector<const String*> values = { &newVar, &oldVar };
102         return eval_template(fSaveStateTemplate, tokens, values);
103     } else {
104         return "";
105     }
106 }
107 
setUniform(const String & pdman,const String & uniform,const String & var) const108 String UniformCTypeMapper::setUniform(const String& pdman, const String& uniform,
109                                       const String& var) const {
110     std::vector<String> tokens = { "pdman", "uniform", "var" };
111     std::vector<const String*> values = { &pdman, &uniform, &var };
112     return eval_template(fUniformTemplate, tokens, values);
113 }
114 
UniformCTypeMapper(Layout::CType ctype,const std::vector<String> & skslTypes,const String & setUniformFormat,bool enableTracking,const String & defaultValue,const String & dirtyExpressionFormat,const String & saveStateFormat)115 UniformCTypeMapper::UniformCTypeMapper(
116         Layout::CType ctype, const std::vector<String>& skslTypes, const String& setUniformFormat,
117         bool enableTracking, const String& defaultValue, const String& dirtyExpressionFormat,
118         const String& saveStateFormat)
119     : fCType(ctype)
120     , fSKSLTypes(skslTypes)
121     , fUniformTemplate(setUniformFormat)
122     , fInlineValue(determine_inline_from_template(setUniformFormat))
123     , fSupportsTracking(enableTracking)
124     , fDefaultValue(defaultValue)
125     , fDirtyExpressionTemplate(dirtyExpressionFormat)
126     , fSaveStateTemplate(saveStateFormat) { }
127 
128 // NOTE: These would be macros, but C++ initialization lists for the sksl type names do not play
129 // well with macro parsing.
130 
REGISTER(Layout::CType ctype,const std::vector<String> & skslTypes,const char * uniformFormat,const char * defaultValue,const char * dirtyExpression)131 static UniformCTypeMapper REGISTER(Layout::CType ctype, const std::vector<String>& skslTypes,
132                                    const char* uniformFormat, const char* defaultValue,
133                                    const char* dirtyExpression) {
134     return UniformCTypeMapper(ctype, skslTypes, uniformFormat, defaultValue, dirtyExpression,
135                               "${oldVar} = ${newVar}");
136 }
137 
REGISTER(Layout::CType ctype,const std::vector<String> & skslTypes,const char * uniformFormat,const char * defaultValue)138 static UniformCTypeMapper REGISTER(Layout::CType ctype, const std::vector<String>& skslTypes,
139                                    const char* uniformFormat, const char* defaultValue) {
140     return REGISTER(ctype, skslTypes, uniformFormat, defaultValue,
141                     "${oldVar} != ${newVar}");
142 }
143 
144 //////////////////////////////
145 // Currently defined ctypes //
146 //////////////////////////////
147 
get_mappers()148 static const std::vector<UniformCTypeMapper>& get_mappers() {
149     static const std::vector<UniformCTypeMapper> registeredMappers = {
150     REGISTER(Layout::CType::kSkRect, { "half4", "float4", "double4" },
151         "${pdman}.set4fv(${uniform}, 1, reinterpret_cast<const float*>(&${var}))", // to gpu
152         "SkRect::MakeEmpty()",                                                     // default value
153         "${oldVar}.isEmpty() || ${oldVar} != ${newVar}"),                          // dirty check
154 
155     REGISTER(Layout::CType::kSkIRect, { "int4", "short4", "byte4" },
156         "${pdman}.set4iv(${uniform}, 1, reinterpret_cast<const int*>(&${var}))",   // to gpu
157         "SkIRect::MakeEmpty()",                                                    // default value
158         "${oldVar}.isEmpty() || ${oldVar} != ${newVar}"),                          // dirty check
159 
160     REGISTER(Layout::CType::kSkPMColor4f, { "half4", "float4", "double4" },
161         "${pdman}.set4fv(${uniform}, 1, ${var}.vec())",                            // to gpu
162         "{SK_FloatNaN, SK_FloatNaN, SK_FloatNaN, SK_FloatNaN}"),                   // default value
163 
164     REGISTER(Layout::CType::kSkVector4, { "half4", "float4", "double4" },
165         "${pdman}.set4fv(${uniform}, 1, ${var}.fData)",                            // to gpu
166         "SkVector4(SK_MScalarNaN, SK_MScalarNaN, SK_MScalarNaN, SK_MScalarNaN)",   // default value
167         "${oldVar} != (${newVar})"),                                               // dirty check
168 
169     REGISTER(Layout::CType::kSkPoint, { "half2", "float2", "double2" } ,
170         "${pdman}.set2f(${uniform}, ${var}.fX, ${var}.fY)",                        // to gpu
171         "SkPoint::Make(SK_FloatNaN, SK_FloatNaN)"),                                // default value
172 
173     REGISTER(Layout::CType::kSkIPoint, { "int2", "short2", "byte2" },
174         "${pdman}.set2i(${uniform}, ${var}.fX, ${var}.fY)",                        // to gpu
175         "SkIPoint::Make(SK_NaN32, SK_NaN32)"),                                     // default value
176 
177     REGISTER(Layout::CType::kSkMatrix, { "half3x3", "float3x3", "double3x3" },
178         "${pdman}.setSkMatrix(${uniform}, ${var})",                                // to gpu
179         "SkMatrix::MakeScale(SK_FloatNaN)",                                        // default value
180         "!${oldVar}.cheapEqualTo(${newVar})"),                                     // dirty check
181 
182     REGISTER(Layout::CType::kSkMatrix44,  { "half4x4", "float4x4", "double4x4" },
183         "${pdman}.setSkMatrix44(${uniform}, ${var})",                              // to gpu
184         "SkMatrix44(SkMatrix44::kNaN_Constructor)",                                // default value
185         "${oldVar} != (${newVar})"),                                               // dirty check
186 
187     REGISTER(Layout::CType::kFloat,  { "half", "float", "double" },
188         "${pdman}.set1f(${uniform}, ${var})",                                      // to gpu
189         "SK_FloatNaN"),                                                            // default value
190 
191     REGISTER(Layout::CType::kInt32, { "int", "short", "byte" },
192         "${pdman}.set1i(${uniform}, ${var})",                                      // to gpu
193         "SK_NaN32"),                                                               // default value
194     };
195 
196     return registeredMappers;
197 }
198 
199 /////
200 
201 // Greedy search through registered handlers for one that has a matching
202 // ctype and supports the sksl type of the variable.
Get(const Context & context,const Type & type,const Layout & layout)203 const UniformCTypeMapper* UniformCTypeMapper::Get(const Context& context, const Type& type,
204                                                   const Layout& layout) {
205     const std::vector<UniformCTypeMapper>& registeredMappers = get_mappers();
206 
207     Layout::CType ctype = layout.fCType;
208     // If there's no custom ctype declared in the layout, use the default type mapping
209     if (ctype == Layout::CType::kDefault) {
210         ctype = HCodeGenerator::ParameterCType(context, type, layout);
211     }
212 
213     const String& skslType = type.name();
214 
215     for (size_t i = 0; i < registeredMappers.size(); i++) {
216         if (registeredMappers[i].ctype() == ctype) {
217             // Check for sksl support, since some c types (e.g. SkMatrix) can be used in multiple
218             // uniform types and send data to the gpu differently in those conditions
219             const std::vector<String> supportedSKSL = registeredMappers[i].supportedTypeNames();
220             for (size_t j = 0; j < supportedSKSL.size(); j++) {
221                 if (supportedSKSL[j] == skslType) {
222                     // Found a match, so return it or an explicitly untracked version if tracking is
223                     // disabled in the layout
224                     return &registeredMappers[i];
225                 }
226             }
227         }
228     }
229 
230     // Didn't find a match
231     return nullptr;
232 }
233 
234 } // namespace
235