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 | |
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 | |
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 | |
88 | 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 | |
98 | 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 | |
108 | 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 | |
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 | |
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 | |
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 | |
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::kSkV4, { "half4" , "float4" , "double4" }, |
165 | "${pdman}.set4fv(${uniform}, 1, ${var}.ptr())" , // to gpu |
166 | "SkV4{SK_FloatNaN, SK_FloatNaN, SK_FloatNaN, SK_FloatNaN}" , // 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::kSkM44, { "half4x4" , "float4x4" , "double4x4" }, |
183 | "${pdman}.setSkM44(${uniform}, ${var})" , // to gpu |
184 | "SkM44(SkM44::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. |
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 ®isteredMappers[i]; |
225 | } |
226 | } |
227 | } |
228 | } |
229 | |
230 | // Didn't find a match |
231 | return nullptr; |
232 | } |
233 | |
234 | } // namespace |
235 | |