SkSLCPPUniformCTypes.cpp 10 KB

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