From 77cf460e5df7dd5272e71f9835880ded48fe8d12 Mon Sep 17 00:00:00 2001 From: "bsalomon@google.com" Date: Mon, 22 Apr 2013 21:05:48 +0000 Subject: [PATCH] Add non-separable xfer modes to GPU backend. Review URL: https://codereview.chromium.org/14371014 git-svn-id: http://skia.googlecode.com/svn/trunk@8815 2bbb7eff-a529-9590-31e7-b0007b416f81 --- src/core/SkString.cpp | 2 +- src/core/SkXfermode.cpp | 183 +++++++++++++++++++++++++++++++++++++-- src/gpu/gl/GrGLShaderBuilder.cpp | 12 +-- src/gpu/gl/GrGLShaderVar.h | 17 +++- 4 files changed, 196 insertions(+), 18 deletions(-) diff --git a/src/core/SkString.cpp b/src/core/SkString.cpp index 3f9a99f..aee3de5 100644 --- a/src/core/SkString.cpp +++ b/src/core/SkString.cpp @@ -15,7 +15,7 @@ #include // number of bytes (on the stack) to receive the printf result -static const size_t kBufferSize = 512; +static const size_t kBufferSize = 1024; #ifdef SK_BUILD_FOR_WIN #define VSNPRINTF(buffer, size, format, args) \ diff --git a/src/core/SkXfermode.cpp b/src/core/SkXfermode.cpp index da6d15e..5c75204 100644 --- a/src/core/SkXfermode.cpp +++ b/src/core/SkXfermode.cpp @@ -955,7 +955,7 @@ void SkProcXfermode::toString(SkString* str) const { class XferEffect : public GrEffect { public: static bool IsSupportedMode(SkXfermode::Mode mode) { - return mode > SkXfermode::kLastCoeffMode && mode <= SkXfermode::kLastSeparableMode; + return mode > SkXfermode::kLastCoeffMode && mode <= SkXfermode::kLastMode; } static GrEffectRef* Create(SkXfermode::Mode mode) { @@ -1000,11 +1000,14 @@ public: inputColor = "ones"; } + SkXfermode::Mode mode = drawEffect.castEffect().mode(); + builder->fsCodeAppendf("\t\t// SkXfermode::Mode: %s\n", SkXfermode::ModeName(mode)); + // These all perform src-over on the alpha channel. builder->fsCodeAppendf("\t\t%s.a = %s.a + (1.0 - %s.a) * %s.a;\n", outputColor, inputColor, inputColor, dstColor); - switch (drawEffect.castEffect().mode()) { + switch (mode) { case SkXfermode::kOverlay_Mode: // Overlay is Hard-Light with the src and dst reversed HardLight(builder, outputColor, dstColor, inputColor); @@ -1063,12 +1066,58 @@ public: outputColor, inputColor, dstColor, dstColor, inputColor, inputColor, dstColor); break; - case SkXfermode::kHue_Mode: - case SkXfermode::kSaturation_Mode: - case SkXfermode::kColor_Mode: - case SkXfermode::kLuminosity_Mode: - GrCrash("Unimplemented XferEffect mode."); + case SkXfermode::kHue_Mode: { + // SetLum(SetSat(S * Da, Sat(D * Sa)), Sa*Da, D*Sa) + (1 - Sa) * D + (1 - Da) * S + SkString setSat, setLum; + AddSatFunction(builder, &setSat); + AddLumFunction(builder, &setLum); + builder->fsCodeAppendf("\t\tvec4 dstSrcAlpha = %s * %s.a;\n", + dstColor, inputColor); + builder->fsCodeAppendf("\t\t%s.rgb = %s(%s(%s.rgb * %s.a, dstSrcAlpha.rgb), dstSrcAlpha.a, dstSrcAlpha.rgb);\n", + outputColor, setLum.c_str(), setSat.c_str(), inputColor, + dstColor); + builder->fsCodeAppendf("\t\t%s.rgb += (1.0 - %s.a) * %s.rgb + (1.0 - %s.a) * %s.rgb;\n", + outputColor, inputColor, dstColor, dstColor, inputColor); + break; + } + case SkXfermode::kSaturation_Mode: { + // SetLum(SetSat(D * Sa, Sat(S * Da)), Sa*Da, D*Sa)) + (1 - Sa) * D + (1 - Da) * S + SkString setSat, setLum; + AddSatFunction(builder, &setSat); + AddLumFunction(builder, &setLum); + builder->fsCodeAppendf("\t\tvec4 dstSrcAlpha = %s * %s.a;\n", + dstColor, inputColor); + builder->fsCodeAppendf("\t\t%s.rgb = %s(%s(dstSrcAlpha.rgb, %s.rgb * %s.a), dstSrcAlpha.a, dstSrcAlpha.rgb);\n", + outputColor, setLum.c_str(), setSat.c_str(), inputColor, + dstColor); + builder->fsCodeAppendf("\t\t%s.rgb += (1.0 - %s.a) * %s.rgb + (1.0 - %s.a) * %s.rgb;\n", + outputColor, inputColor, dstColor, dstColor, inputColor); break; + } + case SkXfermode::kColor_Mode: { + // SetLum(S * Da, Sa* Da, D * Sa) + (1 - Sa) * D + (1 - Da) * S + SkString setLum; + AddLumFunction(builder, &setLum); + builder->fsCodeAppendf("\t\tvec4 srcDstAlpha = %s * %s.a;\n", + inputColor, dstColor); + builder->fsCodeAppendf("\t\t%s.rgb = %s(srcDstAlpha.rgb, srcDstAlpha.a, %s.rgb * %s.a);\n", + outputColor, setLum.c_str(), dstColor, inputColor); + builder->fsCodeAppendf("\t\t%s.rgb += (1.0 - %s.a) * %s.rgb + (1.0 - %s.a) * %s.rgb;\n", + outputColor, inputColor, dstColor, dstColor, inputColor); + break; + } + case SkXfermode::kLuminosity_Mode: { + // SetLum(D * Sa, Sa* Da, S * Da) + (1 - Sa) * D + (1 - Da) * S + SkString setLum; + AddLumFunction(builder, &setLum); + builder->fsCodeAppendf("\t\tvec4 srcDstAlpha = %s * %s.a;\n", + inputColor, dstColor); + builder->fsCodeAppendf("\t\t%s.rgb = %s(%s.rgb * %s.a, srcDstAlpha.a, srcDstAlpha.rgb);\n", + outputColor, setLum.c_str(), dstColor, inputColor); + builder->fsCodeAppendf("\t\t%s.rgb += (1.0 - %s.a) * %s.rgb + (1.0 - %s.a) * %s.rgb;\n", + outputColor, inputColor, dstColor, dstColor, inputColor); + break; + } default: GrCrash("Unknown XferEffect mode."); break; @@ -1178,6 +1227,126 @@ public: builder->fsCodeAppendf("\t\t\t}\n"); } + // Adds a function that takes two colors and an alpha as input. It produces a color with the + // hue and saturation of the first color, the luminosity of the second color, and the input + // alpha. It has this signature: + // vec3 set_luminance(vec3 hueSatColor, float alpha, vec3 lumColor). + static void AddLumFunction(GrGLShaderBuilder* builder, SkString* setLumFunction) { + // Emit a helper that gets the luminance of a color. + SkString getFunction; + GrGLShaderVar getLumArgs[] = { + GrGLShaderVar("color", kVec3f_GrSLType), + }; + SkString getLumBody("\treturn dot(vec3(0.3, 0.59, 0.11), color);\n"); + builder->emitFunction(GrGLShaderBuilder::kFragment_ShaderType, + kFloat_GrSLType, + "luminance", + SK_ARRAY_COUNT(getLumArgs), getLumArgs, + getLumBody.c_str(), + &getFunction); + + // Emit the set luminance function. + GrGLShaderVar setLumArgs[] = { + GrGLShaderVar("hueSat", kVec3f_GrSLType), + GrGLShaderVar("alpha", kFloat_GrSLType), + GrGLShaderVar("lumColor", kVec3f_GrSLType), + }; + SkString setLumBody; + setLumBody.printf("\tfloat diff = %s(lumColor - hueSat);\n", getFunction.c_str()); + setLumBody.append("\tvec3 outColor = hueSat + diff;\n"); + setLumBody.appendf("\tfloat outLum = %s(outColor);\n", getFunction.c_str()); + setLumBody.append("\tfloat minComp = min(min(outColor.r, outColor.g), outColor.b);\n" + "\tfloat maxComp = max(max(outColor.r, outColor.g), outColor.b);\n" + "\tif (minComp < 0.0) {\n" + "\t\toutColor = outLum + ((outColor - vec3(outLum, outLum, outLum)) * outLum) / (outLum - minComp);\n" + "\t}\n" + "\tif (maxComp > alpha) {\n" + "\t\toutColor = outLum + ((outColor - vec3(outLum, outLum, outLum)) * (alpha - outLum)) / (maxComp - outLum);\n" + "\t}\n" + "\treturn outColor;\n"); + builder->emitFunction(GrGLShaderBuilder::kFragment_ShaderType, + kVec3f_GrSLType, + "set_luminance", + SK_ARRAY_COUNT(setLumArgs), setLumArgs, + setLumBody.c_str(), + setLumFunction); + } + + // Adds a function that creates a color with the hue and luminosity of one input color and + // the saturation of another color. It will have this signature: + // float set_saturation(vec3 hueLumColor, vec3 satColor) + static void AddSatFunction(GrGLShaderBuilder* builder, SkString* setSatFunction) { + // Emit a helper that gets the saturation of a color + SkString getFunction; + GrGLShaderVar getSatArgs[] = { GrGLShaderVar("color", kVec3f_GrSLType) }; + SkString getSatBody; + getSatBody.printf("\treturn max(max(color.r, color.g), color.b) - " + "min(min(color.r, color.g), color.b);\n"); + builder->emitFunction(GrGLShaderBuilder::kFragment_ShaderType, + kFloat_GrSLType, + "saturation", + SK_ARRAY_COUNT(getSatArgs), getSatArgs, + getSatBody.c_str(), + &getFunction); + + // Emit a helper that sets the saturation given sorted input channels + SkString helperFunction; + GrGLShaderVar helperArgs[] = { + GrGLShaderVar("minComp", kFloat_GrSLType), + GrGLShaderVar("midComp", kFloat_GrSLType), + GrGLShaderVar("maxComp", kFloat_GrSLType), + GrGLShaderVar("sat", kFloat_GrSLType), + }; + helperArgs[0].setTypeModifier(GrGLShaderVar::kInOut_TypeModifier); + helperArgs[1].setTypeModifier(GrGLShaderVar::kInOut_TypeModifier); + helperArgs[2].setTypeModifier(GrGLShaderVar::kInOut_TypeModifier); + SkString helperBody; + helperBody.append("\tif (minComp < maxComp) {\n" + "\t\tmidComp = sat * (midComp - minComp) / (maxComp - minComp);\n" + "\t\tmaxComp = sat;\n" + "\t} else {\n" + "\t\tmidComp = midComp = 0.0;\n" + "\t}\n" + "\tminComp = 0.0;\n"); + builder->emitFunction(GrGLShaderBuilder::kFragment_ShaderType, + kVoid_GrSLType, + "set_saturation_helper", + SK_ARRAY_COUNT(helperArgs), helperArgs, + helperBody.c_str(), + &helperFunction); + + GrGLShaderVar setSatArgs[] = { + GrGLShaderVar("hueLumColor", kVec3f_GrSLType), + GrGLShaderVar("satColor", kVec3f_GrSLType), + }; + const char* helpFunc = helperFunction.c_str(); + SkString setSatBody; + setSatBody.appendf("\tfloat sat = %s(satColor);\n" + "\tif (hueLumColor.r <= hueLumColor.g) {\n" + "\t\tif (hueLumColor.g <= hueLumColor.b) {\n" + "\t\t\t%s(hueLumColor.r, hueLumColor.g, hueLumColor.b, sat);\n" + "\t\t} else {\n" + "\t\t\t%s(hueLumColor.r, hueLumColor.b, hueLumColor.g, sat);\n" + "\t\t}\n" + "\t} else if (hueLumColor.r <= hueLumColor.b) {\n" + "\t\t%s(hueLumColor.g, hueLumColor.r, hueLumColor.b, sat);\n" + "\t} else if (hueLumColor.g <= hueLumColor.b) {\n" + "\t\t%s(hueLumColor.g, hueLumColor.b, hueLumColor.r, sat);\n" + "\t} else {\n" + "\t\t%s(hueLumColor.b, hueLumColor.g, hueLumColor.r, sat);\n" + "\t}\n" + "\treturn hueLumColor;", + getFunction.c_str(), helpFunc, helpFunc, helpFunc, helpFunc,\ + helpFunc); + builder->emitFunction(GrGLShaderBuilder::kFragment_ShaderType, + kVec3f_GrSLType, + "set_saturation", + SK_ARRAY_COUNT(setSatArgs), setSatArgs, + setSatBody.c_str(), + setSatFunction); + + } + typedef GrGLEffect INHERITED; }; diff --git a/src/gpu/gl/GrGLShaderBuilder.cpp b/src/gpu/gl/GrGLShaderBuilder.cpp index 60e9188..752464c 100644 --- a/src/gpu/gl/GrGLShaderBuilder.cpp +++ b/src/gpu/gl/GrGLShaderBuilder.cpp @@ -386,7 +386,7 @@ void GrGLShaderBuilder::addVarying(GrSLType type, const char** fsInName) { fVSOutputs.push_back(); fVSOutputs.back().setType(type); - fVSOutputs.back().setTypeModifier(GrGLShaderVar::kOut_TypeModifier); + fVSOutputs.back().setTypeModifier(GrGLShaderVar::kVaryingOut_TypeModifier); if (kNonStageIdx == fCurrentStageIdx) { fVSOutputs.back().accessName()->printf("v%s", name); } else { @@ -402,12 +402,12 @@ void GrGLShaderBuilder::addVarying(GrSLType type, // and output as non-array. fGSInputs.push_back(); fGSInputs.back().setType(type); - fGSInputs.back().setTypeModifier(GrGLShaderVar::kIn_TypeModifier); + fGSInputs.back().setTypeModifier(GrGLShaderVar::kVaryingIn_TypeModifier); fGSInputs.back().setUnsizedArray(); *fGSInputs.back().accessName() = fVSOutputs.back().getName(); fGSOutputs.push_back(); fGSOutputs.back().setType(type); - fGSOutputs.back().setTypeModifier(GrGLShaderVar::kOut_TypeModifier); + fGSOutputs.back().setTypeModifier(GrGLShaderVar::kVaryingOut_TypeModifier); if (kNonStageIdx == fCurrentStageIdx) { fGSOutputs.back().accessName()->printf("g%s", name); } else { @@ -419,7 +419,7 @@ void GrGLShaderBuilder::addVarying(GrSLType type, } fFSInputs.push_back(); fFSInputs.back().setType(type); - fFSInputs.back().setTypeModifier(GrGLShaderVar::kIn_TypeModifier); + fFSInputs.back().setTypeModifier(GrGLShaderVar::kVaryingIn_TypeModifier); fFSInputs.back().setName(*fsName); if (fsInName) { *fsInName = fsName->c_str(); @@ -487,11 +487,11 @@ void GrGLShaderBuilder::emitFunction(ShaderType shader, GrAssert(kFragment_ShaderType == shader); fFSFunctions.append(GrGLSLTypeString(returnType)); if (kNonStageIdx != fCurrentStageIdx) { - outName->printf(" %s_%d", name, fCurrentStageIdx); + outName->printf("%s_%d", name, fCurrentStageIdx); } else { *outName = name; } - fFSFunctions.append(*outName); + fFSFunctions.appendf(" %s", outName->c_str()); fFSFunctions.append("("); for (int i = 0; i < argCnt; ++i) { args[i].appendDecl(fCtxInfo, &fFSFunctions); diff --git a/src/gpu/gl/GrGLShaderVar.h b/src/gpu/gl/GrGLShaderVar.h index 7f2bf45..c795897 100644 --- a/src/gpu/gl/GrGLShaderVar.h +++ b/src/gpu/gl/GrGLShaderVar.h @@ -29,8 +29,11 @@ public: kNone_TypeModifier, kOut_TypeModifier, kIn_TypeModifier, + kInOut_TypeModifier, kUniform_TypeModifier, - kAttribute_TypeModifier + kAttribute_TypeModifier, + kVaryingIn_TypeModifier, + kVaryingOut_TypeModifier }; enum Precision { @@ -303,14 +306,20 @@ private: switch (t) { case kNone_TypeModifier: return ""; - case kOut_TypeModifier: - return k110_GrGLSLGeneration == gen ? "varying" : "out"; case kIn_TypeModifier: - return k110_GrGLSLGeneration == gen ? "varying" : "in"; + return "in"; + case kInOut_TypeModifier: + return "inout"; + case kOut_TypeModifier: + return "out"; case kUniform_TypeModifier: return "uniform"; case kAttribute_TypeModifier: return k110_GrGLSLGeneration == gen ? "attribute" : "in"; + case kVaryingIn_TypeModifier: + return k110_GrGLSLGeneration == gen ? "varying" : "in"; + case kVaryingOut_TypeModifier: + return k110_GrGLSLGeneration == gen ? "varying" : "out"; default: GrCrash("Unknown shader variable type modifier."); return ""; // suppress warning -- 2.7.4