[graphite] Add first version of Blend Shader support
This CL adds children to the SkPaintParamsKey and ShaderInfo objects and then uses them to implement the blend shader glue code.
Bug: skia:12701
Change-Id: I6750fe375b20a5c1cda315336bfcb3feda8cab28
Reviewed-on: https://skia-review.googlesource.com/c/skia/+/505297
Reviewed-by: Michael Ludwig <michaelludwig@google.com>
Reviewed-by: Brian Osman <brianosman@google.com>
Commit-Queue: Robert Phillips <robertphillips@google.com>
diff --git a/src/core/SkKeyHelpers.cpp b/src/core/SkKeyHelpers.cpp
index efb957e..440133b 100644
--- a/src/core/SkKeyHelpers.cpp
+++ b/src/core/SkKeyHelpers.cpp
@@ -547,7 +547,7 @@
if (backend == SkBackend::kGraphite) {
int headerOffset = key->beginBlock(SkBuiltInCodeSnippetID::kBlendShader);
- add_blendmode_to_key(key, blendData.fBM);
+ // Child blocks always go right after the parent block's header
int start = key->sizeInBytes();
as_SB(blendData.fDst)->addToKey(dict, backend, key, uniformBlock);
int firstShaderSize = key->sizeInBytes() - start;
@@ -556,6 +556,8 @@
as_SB(blendData.fSrc)->addToKey(dict, backend, key, uniformBlock);
int secondShaderSize = key->sizeInBytes() - start;
+ add_blendmode_to_key(key, blendData.fBM);
+
key->endBlock(headerOffset, SkBuiltInCodeSnippetID::kBlendShader);
int expectedBlockSize = 1 + firstShaderSize + secondShaderSize;
@@ -581,18 +583,19 @@
int runningOffset = headerOffset + SkPaintParamsKey::kBlockHeaderSizeInBytes;
- uint8_t data = key.byte(runningOffset);
- SkBlendMode bm = to_blendmode(data);
-
- SkDebugf("BlendMode: %s\n", SkBlendMode_Name(bm));
- runningOffset += 1; // 1 byte for blendmode
-
SkDebugf("\nDst: ");
int firstBlockSize = SkPaintParamsKey::DumpBlock(key, runningOffset);
runningOffset += firstBlockSize;
SkDebugf("Src: ");
int secondBlockSize = SkPaintParamsKey::DumpBlock(key, runningOffset);
+ runningOffset += secondBlockSize;
+
+ uint8_t data = key.byte(runningOffset);
+ SkBlendMode bm = to_blendmode(data);
+
+ SkDebugf("BlendMode: %s\n", SkBlendMode_Name(bm));
+ runningOffset += 1; // 1 byte for blendmode
int calculatedBlockSize = SkPaintParamsKey::kBlockHeaderSizeInBytes +
firstBlockSize + secondBlockSize + 1;
diff --git a/src/core/SkPaintParamsKey.cpp b/src/core/SkPaintParamsKey.cpp
index e2b4dd8..6458b01 100644
--- a/src/core/SkPaintParamsKey.cpp
+++ b/src/core/SkPaintParamsKey.cpp
@@ -84,6 +84,16 @@
result->add(*entry);
+ // The child blocks appear right after the parent block's header in the key and go
+ // right after the parent's SnippetEntry in the shader info
+ int childOffset = headerOffset + kBlockHeaderSizeInBytes;
+ for (int i = 0; i < entry->fNumChildren; ++i) {
+ SkASSERT(childOffset < headerOffset + blockSize);
+
+ int childBlockSize = AddBlockToShaderInfo(dict, key, childOffset, result);
+ childOffset += childBlockSize;
+ }
+
if (codeSnippetID != SkBuiltInCodeSnippetID::kDepthStencilOnlyDraw) {
result->setWritesColor();
}
diff --git a/src/core/SkPaintParamsKey.h b/src/core/SkPaintParamsKey.h
index 5fd9bf6..e315ecf 100644
--- a/src/core/SkPaintParamsKey.h
+++ b/src/core/SkPaintParamsKey.h
@@ -23,8 +23,12 @@
// This class is a compact representation of the shader needed to implement a given
// PaintParams. Its structure is a series of blocks where each block has a
-// header that consists of 2-bytes - a 1-byte code-snippet ID and a 1-byte number-of-bytes-in-the-
-// block field. The rest of the data in the block is dependent on the individual code snippet.
+// header that consists of 2-bytes:
+// a 1-byte code-snippet ID
+// a 1-byte number-of-bytes-in-the-block field (incl. the space for the header)
+// The rest of the data in the block is dependent on the individual code snippet.
+// If a given block has child blocks, they appear in the key right after their
+// parent block's header.
class SkPaintParamsKey {
public:
static const int kBlockHeaderSizeInBytes = 2;
diff --git a/src/core/SkShaderCodeDictionary.cpp b/src/core/SkShaderCodeDictionary.cpp
index c225651..38bf1d6 100644
--- a/src/core/SkShaderCodeDictionary.cpp
+++ b/src/core/SkShaderCodeDictionary.cpp
@@ -36,7 +36,12 @@
//
// half4 outColor%d;
// {
-// /* emitted snippet sksl assigns to outColor%d */
+// half4 child-outColor%d; // for each child
+// {
+// /* emitted snippet sksl assigns to child-outColor%d */
+// }
+//
+// /* emitted snippet sksl assigns to outColor%d - taking a vector of child var names */
// }
// Where the %d is filled in with 'entryIndex'.
std::string SkShaderInfo::emitGlueCodeForEntry(int* entryIndex,
@@ -52,7 +57,17 @@
add_indent(result, indent);
*result += "{\n";
- *result += (entry.fGlueCodeGenerator)(scopeOutputVar, curEntryIndex, entry, indent+1);
+ // Although the children appear after the parent in the shader info they are emitted
+ // before the parent
+ std::vector<std::string> childNames;
+ for (int j = 0; j < entry.fNumChildren; ++j) {
+ *entryIndex += 1;
+ std::string childOutputVar = this->emitGlueCodeForEntry(entryIndex, result, indent+1);
+ childNames.push_back(childOutputVar);
+ }
+
+ *result += (entry.fGlueCodeGenerator)(scopeOutputVar, curEntryIndex,
+ entry, childNames, indent+1);
add_indent(result, indent);
*result += "}\n";
@@ -160,7 +175,10 @@
std::string GenerateDefaultGlueCode(const std::string& resultName,
int entryIndex,
const SkShaderInfo::SnippetEntry& entry,
+ const std::vector<std::string>& childNames,
int indent) {
+ SkASSERT(childNames.empty());
+
std::string result;
add_indent(&result, indent);
@@ -247,6 +265,99 @@
"}\n";
//--------------------------------------------------------------------------------------------------
+static constexpr int kNumBlendShaderUniforms = 0;
+static constexpr int kNumBlendShaderChildren = 2;
+
+// Note: we're counting on the compiler to inline this code and trim it down to just the used
+// branch(es).
+static const char* kBlendShaderName = "blend_shader";
+static const char* kBlendShaderSkSL =
+ "const int kClear = 0;\n"
+ "const int kSrc = 1;\n"
+ "const int kDst = 2;\n"
+ "const int kSrcOver = 3;\n"
+ "const int kDstOver = 4;\n"
+ "const int kSrcIn = 5;\n"
+ "const int kDstIn = 6;\n"
+ "const int kSrcOut = 7;\n"
+ "const int kDstOut = 8;\n"
+ "const int kSrcATop = 9;\n"
+ "const int kDstATop = 10;\n"
+ "const int kXor = 11;\n"
+ "const int kPlus = 12;\n"
+ "const int kModulate = 13;\n"
+ "const int kScreen = 14;\n"
+ "const int kOverlay = 15;\n"
+ "const int kDarken = 16;\n"
+ "const int kLighten = 17;\n"
+ "const int kColorDodge = 18;\n"
+ "const int kColorBurn = 19;\n"
+ "const int kHardLight = 20;\n"
+ "const int kSoftLight = 21;\n"
+ "const int kDifference = 22;\n"
+ "const int kExclusion = 23;\n"
+ "const int kMultiply = 24;\n"
+ "const int kHue = 25;\n"
+ "const int kSaturation = 26;\n"
+ "const int kColor = 27;\n"
+ "const int kLuminosity = 28;\n"
+ "\n"
+ "half4 blend(int mode, half4 src, half4 dst) {\n"
+ " switch (mode) {\n"
+ " case kClear: { return blend_clear(src, dst); }\n"
+ " case kSrc: { return blend_src(src, dst); }\n"
+ " case kDst: { return blend_dst(src, dst); }\n"
+ " case kSrcOver: { return blend_src_over(src, dst); }\n"
+ " case kDstOver: { return blend_dst_over(src, dst); }\n"
+ " case kSrcIn: { return blend_src_in(src, dst); }\n"
+ " case kDstIn: { return blend_dst_in(src, dst); }\n"
+ " case kSrcOut: { return blend_src_out(src, dst); }\n"
+ " case kDstOut: { return blend_dst_out(src, dst); }\n"
+ " case kSrcATop: { return blend_src_atop(src, dst); }\n"
+ " case kDstATop: { return blend_dst_atop(src, dst); }\n"
+ " case kXor: { return blend_xor(src, dst); }\n"
+ " case kPlus: { return blend_plus(src, dst); }\n"
+ " case kModulate: { return blend_modulate(src, dst); }\n"
+ " case kScreen: { return blend_screen(src, dst); }\n"
+ " case kOverlay: { return blend_overlay(src, dst); }\n"
+ " case kDarken: { return blend_darken(src, dst); }\n"
+ " case kLighten: { return blend_lighten(src, dst); }\n"
+ " case kColorDodge: { return blend_color_dodge(src, dst); }\n"
+ " case kColorBurn: { return blend_color_burn(src, dst); }\n"
+ " case kHardLight: { return blend_hard_light(src, dst); }\n"
+ " case kSoftLight: { return blend_soft_light(src, dst); }\n"
+ " case kDifference: { return blend_difference(src, dst); }\n"
+ " case kExclusion: { return blend_exclusion(src, dst); }\n"
+ " case kMultiply: { return blend_multiply(src, dst); }\n"
+ " case kHue: { return blend_hue(src, dst); }\n"
+ " case kSaturation: { return blend_saturation(src, dst); }\n"
+ " case kColor: { return blend_color(src, dst); }\n"
+ " case kLuminosity: { return blend_luminosity(src, dst); }\n"
+ " default: return half4(0); // Avoids 'blend can exit without returning a value' error\n"
+ " }\n"
+ "}\n";
+
+std::string GenerateBlendShaderGlueCode(const std::string& resultName,
+ int entryIndex,
+ const SkShaderInfo::SnippetEntry& entry,
+ const std::vector<std::string>& childNames,
+ int indent) {
+ SkASSERT(childNames.size() == kNumBlendShaderChildren);
+
+ std::string result;
+
+ add_indent(&result, indent);
+ // TODO: actually feed in the blend mode either through a uniform or, somehow, from the
+ // SkPaintParamsKey
+ SkSL::String::appendf(&result, "%s = blend(kModulate, %s, %s);\n",
+ resultName.c_str(),
+ childNames[1].c_str(),
+ childNames[0].c_str());
+
+ return result;
+}
+
+//--------------------------------------------------------------------------------------------------
static constexpr int kNumErrorUniforms = 0;
static const char* kErrorName = "error";
static const char* kErrorSkSL =
@@ -256,6 +367,8 @@
} // anonymous namespace
+static constexpr int kNoChildren = 0;
+
SkShaderCodeDictionary::SkShaderCodeDictionary() {
// The 0th index is reserved as invalid
fEntryVector.push_back(nullptr);
@@ -264,45 +377,54 @@
{ nullptr, kNumErrorUniforms },
kErrorName, kErrorSkSL,
GenerateDefaultGlueCode,
+ kNoChildren
};
fCodeSnippets[(int) SkBuiltInCodeSnippetID::kSolidColorShader] = {
SkMakeSpan(kSolidShaderUniforms, kNumSolidShaderUniforms),
kSolidShaderName, kSolidShaderSkSL,
GenerateDefaultGlueCode,
+ kNoChildren
};
fCodeSnippets[(int) SkBuiltInCodeSnippetID::kLinearGradientShader] = {
SkMakeSpan(kGradientUniforms, kNumGradientUniforms),
kLinearGradient4Name, kLinearGradient4SkSL,
GenerateDefaultGlueCode,
+ kNoChildren
};
fCodeSnippets[(int) SkBuiltInCodeSnippetID::kRadialGradientShader] = {
SkMakeSpan(kGradientUniforms, kNumGradientUniforms),
kLinearGradient4Name, kLinearGradient4SkSL,
GenerateDefaultGlueCode,
+ kNoChildren
};
fCodeSnippets[(int) SkBuiltInCodeSnippetID::kSweepGradientShader] = {
SkMakeSpan(kGradientUniforms, kNumGradientUniforms),
kLinearGradient4Name, kLinearGradient4SkSL,
GenerateDefaultGlueCode,
+ kNoChildren
};
fCodeSnippets[(int) SkBuiltInCodeSnippetID::kConicalGradientShader] = {
SkMakeSpan(kGradientUniforms, kNumGradientUniforms),
kLinearGradient4Name, kLinearGradient4SkSL,
GenerateDefaultGlueCode,
+ kNoChildren
};
fCodeSnippets[(int) SkBuiltInCodeSnippetID::kImageShader] = {
{ nullptr, kNumImageShaderUniforms },
kImageShaderName, kImageShaderSkSL,
GenerateDefaultGlueCode,
+ kNoChildren
};
fCodeSnippets[(int) SkBuiltInCodeSnippetID::kBlendShader] = {
- { nullptr, kNumErrorUniforms },
- kErrorName, kErrorSkSL,
- GenerateDefaultGlueCode,
+ { nullptr, kNumBlendShaderUniforms },
+ kBlendShaderName, kBlendShaderSkSL,
+ GenerateBlendShaderGlueCode,
+ kNumBlendShaderChildren
};
fCodeSnippets[(int) SkBuiltInCodeSnippetID::kSimpleBlendMode] = {
{ nullptr, kNumErrorUniforms },
kErrorName, kErrorSkSL,
GenerateDefaultGlueCode,
+ kNoChildren
};
}
diff --git a/src/core/SkShaderCodeDictionary.h b/src/core/SkShaderCodeDictionary.h
index 0dcaf03..666299e 100644
--- a/src/core/SkShaderCodeDictionary.h
+++ b/src/core/SkShaderCodeDictionary.h
@@ -24,6 +24,7 @@
using GenerateGlueCodeForEntry = std::string (*)(const std::string& resultName,
int entryIndex, // for uniform name mangling
const SnippetEntry&,
+ const std::vector<std::string>& childNames,
int indent);
struct SnippetEntry {
@@ -31,6 +32,7 @@
const char* fStaticFunctionName;
const char* fStaticSkSL;
GenerateGlueCodeForEntry fGlueCodeGenerator;
+ int fNumChildren;
};
void add(const SnippetEntry& entry) {