| /* | |
| * Copyright 2018 Google Inc. | |
| * | |
| * Use of this source code is governed by a BSD-style license that can be | |
| * found in the LICENSE file. | |
| */ | |
| #include "SkSLCPPUniformCTypes.h" | |
| #include "SkSLHCodeGenerator.h" | |
| #include "SkSLStringStream.h" | |
| #include <vector> | |
| namespace SkSL { | |
| ///////////////////////// | |
| // Template evaluation // | |
| ///////////////////////// | |
| static String eval_template(const String& format, const std::vector<String>& tokens, | |
| const std::vector<const String*>& values) { | |
| StringStream stream; | |
| int tokenNameStart = -1; | |
| for (size_t i = 0; i < format.size(); i++) { | |
| if (tokenNameStart >= 0) { | |
| // Within a token name so check if it is the end | |
| if (format[i] == '}') { | |
| // Skip 2 extra characters at the beginning for the $ and {, which must exist since | |
| // otherwise tokenNameStart < 0 | |
| String token(format.c_str() + tokenNameStart + 2, i - tokenNameStart - 2); | |
| // Search for the token in supported list | |
| bool found = false; | |
| for (size_t j = 0; j < tokens.size(); j++) { | |
| if (token == tokens[j]) { | |
| // Found a match so append the value corresponding to j to the output | |
| stream.writeText(values[j]->c_str()); | |
| found = true; | |
| break; | |
| } | |
| } | |
| if (!found) { | |
| // Write out original characters as if we didn't consider it to be a token name | |
| stream.writeText("${"); | |
| stream.writeText(token.c_str()); | |
| stream.writeText("}"); | |
| } | |
| // And end the token name state | |
| tokenNameStart = -1; | |
| } | |
| } else { | |
| // Outside of a token name, so check if this character starts a name: | |
| // i == $ and i+1 == { | |
| if (i < format.size() - 1 && format[i] == '$' && format[i + 1] == '{') { | |
| // Begin parsing the token | |
| tokenNameStart = i; | |
| } else { | |
| // Just a character so append it | |
| stream.write8(format[i]); | |
| } | |
| } | |
| } | |
| return stream.str(); | |
| } | |
| static bool determine_inline_from_template(const String& uniformTemplate) { | |
| // True if there is at most one instance of the ${var} template matcher in fUniformTemplate. | |
| int firstMatch = uniformTemplate.find("${var}"); | |
| if (firstMatch < 0) { | |
| // Template doesn't use the value variable at all, so it can "inlined" | |
| return true; | |
| } | |
| // Check for another occurrence of ${var}, after firstMatch + 6 | |
| int secondMatch = uniformTemplate.find("${var}", firstMatch + strlen("${var}")); | |
| // If there's no second match, then the value can be inlined in the c++ code | |
| return secondMatch < 0; | |
| } | |
| /////////////////////////////////////// | |
| // UniformCTypeMapper implementation // | |
| /////////////////////////////////////// | |
| String UniformCTypeMapper::dirtyExpression(const String& newVar, const String& oldVar) const { | |
| if (fSupportsTracking) { | |
| std::vector<String> tokens = { "newVar", "oldVar" }; | |
| std::vector<const String*> values = { &newVar, &oldVar }; | |
| return eval_template(fDirtyExpressionTemplate, tokens, values); | |
| } else { | |
| return ""; | |
| } | |
| } | |
| String UniformCTypeMapper::saveState(const String& newVar, const String& oldVar) const { | |
| if (fSupportsTracking) { | |
| std::vector<String> tokens = { "newVar", "oldVar" }; | |
| std::vector<const String*> values = { &newVar, &oldVar }; | |
| return eval_template(fSaveStateTemplate, tokens, values); | |
| } else { | |
| return ""; | |
| } | |
| } | |
| String UniformCTypeMapper::setUniform(const String& pdman, const String& uniform, | |
| const String& var) const { | |
| std::vector<String> tokens = { "pdman", "uniform", "var" }; | |
| std::vector<const String*> values = { &pdman, &uniform, &var }; | |
| return eval_template(fUniformTemplate, tokens, values); | |
| } | |
| UniformCTypeMapper::UniformCTypeMapper( | |
| Layout::CType ctype, const std::vector<String>& skslTypes, const String& setUniformFormat, | |
| bool enableTracking, const String& defaultValue, const String& dirtyExpressionFormat, | |
| const String& saveStateFormat) | |
| : fCType(ctype) | |
| , fSKSLTypes(skslTypes) | |
| , fUniformTemplate(setUniformFormat) | |
| , fInlineValue(determine_inline_from_template(setUniformFormat)) | |
| , fSupportsTracking(enableTracking) | |
| , fDefaultValue(defaultValue) | |
| , fDirtyExpressionTemplate(dirtyExpressionFormat) | |
| , fSaveStateTemplate(saveStateFormat) { } | |
| // NOTE: These would be macros, but C++ initialization lists for the sksl type names do not play | |
| // well with macro parsing. | |
| static UniformCTypeMapper REGISTER(Layout::CType ctype, const std::vector<String>& skslTypes, | |
| const char* uniformFormat, const char* defaultValue, | |
| const char* dirtyExpression) { | |
| return UniformCTypeMapper(ctype, skslTypes, uniformFormat, defaultValue, dirtyExpression, | |
| "${oldVar} = ${newVar}"); | |
| } | |
| static UniformCTypeMapper REGISTER(Layout::CType ctype, const std::vector<String>& skslTypes, | |
| const char* uniformFormat, const char* defaultValue) { | |
| return REGISTER(ctype, skslTypes, uniformFormat, defaultValue, | |
| "${oldVar} != ${newVar}"); | |
| } | |
| ////////////////////////////// | |
| // Currently defined ctypes // | |
| ////////////////////////////// | |
| static const std::vector<UniformCTypeMapper>& get_mappers() { | |
| static const std::vector<UniformCTypeMapper> registeredMappers = { | |
| REGISTER(Layout::CType::kSkRect, { "half4", "float4", "double4" }, | |
| "${pdman}.set4fv(${uniform}, 1, reinterpret_cast<const float*>(&${var}))", // to gpu | |
| "SkRect::MakeEmpty()", // default value | |
| "${oldVar}.isEmpty() || ${oldVar} != ${newVar}"), // dirty check | |
| REGISTER(Layout::CType::kSkIRect, { "int4", "short4", "byte4" }, | |
| "${pdman}.set4iv(${uniform}, 1, reinterpret_cast<const int*>(&${var}))", // to gpu | |
| "SkIRect::MakeEmpty()", // default value | |
| "${oldVar}.isEmpty() || ${oldVar} != ${newVar}"), // dirty check | |
| REGISTER(Layout::CType::kSkPMColor4f, { "half4", "float4", "double4" }, | |
| "${pdman}.set4fv(${uniform}, 1, ${var}.vec())", // to gpu | |
| "{SK_FloatNaN, SK_FloatNaN, SK_FloatNaN, SK_FloatNaN}"), // default value | |
| REGISTER(Layout::CType::kSkPoint, { "half2", "float2", "double2" } , | |
| "${pdman}.set2f(${uniform}, ${var}.fX, ${var}.fY)", // to gpu | |
| "SkPoint::Make(SK_FloatNaN, SK_FloatNaN)"), // default value | |
| REGISTER(Layout::CType::kSkIPoint, { "int2", "short2", "byte2" }, | |
| "${pdman}.set2i(${uniform}, ${var}.fX, ${var}.fY)", // to gpu | |
| "SkIPoint::Make(SK_NaN32, SK_NaN32)"), // default value | |
| REGISTER(Layout::CType::kSkMatrix, { "half3x3", "float3x3", "double3x3" }, | |
| "${pdman}.setSkMatrix(${uniform}, ${var})", // to gpu | |
| "SkMatrix::MakeScale(SK_FloatNaN)", // default value | |
| "!${oldVar}.cheapEqualTo(${newVar})"), // dirty check | |
| REGISTER(Layout::CType::kSkMatrix44, { "half4x4", "float4x4", "double4x4" }, | |
| "${pdman}.setSkMatrix44(${uniform}, ${var})", // to gpu | |
| "SkMatrix::MakeScale(SK_FloatNaN)", // default value | |
| "!${oldVar}.cheapEqualTo(${newVar})"), // dirty check | |
| REGISTER(Layout::CType::kFloat, { "half", "float", "double" }, | |
| "${pdman}.set1f(${uniform}, ${var})", // to gpu | |
| "SK_FloatNaN"), // default value | |
| REGISTER(Layout::CType::kInt32, { "int", "short", "byte" }, | |
| "${pdman}.set1i(${uniform}, ${var})", // to gpu | |
| "SK_NaN32"), // default value | |
| }; | |
| return registeredMappers; | |
| } | |
| ///// | |
| // Greedy search through registered handlers for one that has a matching | |
| // ctype and supports the sksl type of the variable. | |
| const UniformCTypeMapper* UniformCTypeMapper::Get(const Context& context, const Type& type, | |
| const Layout& layout) { | |
| const std::vector<UniformCTypeMapper>& registeredMappers = get_mappers(); | |
| Layout::CType ctype = layout.fCType; | |
| // If there's no custom ctype declared in the layout, use the default type mapping | |
| if (ctype == Layout::CType::kDefault) { | |
| ctype = HCodeGenerator::ParameterCType(context, type, layout); | |
| } | |
| const String& skslType = type.name(); | |
| for (size_t i = 0; i < registeredMappers.size(); i++) { | |
| if (registeredMappers[i].ctype() == ctype) { | |
| // Check for sksl support, since some c types (e.g. SkMatrix) can be used in multiple | |
| // uniform types and send data to the gpu differently in those conditions | |
| const std::vector<String> supportedSKSL = registeredMappers[i].supportedTypeNames(); | |
| for (size_t j = 0; j < supportedSKSL.size(); j++) { | |
| if (supportedSKSL[j] == skslType) { | |
| // Found a match, so return it or an explicitly untracked version if tracking is | |
| // disabled in the layout | |
| return ®isteredMappers[i]; | |
| } | |
| } | |
| } | |
| } | |
| // Didn't find a match | |
| return nullptr; | |
| } | |
| } // namespace |