Roll external/skia b3d2760e2..34dafd547 (7 commits)
https://skia.googlesource.com/skia.git/+log/b3d2760e2..34dafd547
2018-11-16 mtklein@google.com I think the _DXDY_ code may all be dead.
2018-11-16 michaelludwig@google.com Update threshold for degenerate gradients
2018-11-16 brianosman@google.com Use GrVertexWriter for GrRegionOp, add writeQuad()
2018-11-16 brianosman@google.com Do CCPR hairline coverage scaling in floats, rather than bytes
2018-11-16 skia-autoroll@skia-public.iam.gserviceaccount.com Roll third_party/externals/swiftshader 44994a88c9cc..000df8b42041 (1 commits)
2018-11-16 herb@google.com Fun with flags
2018-11-16 csmartdalton@google.com sksl: Polyfill fma() when not present in GLSL
The AutoRoll server is located here: https://autoroll-internal.skia.org/r/android-master-autoroll
Documentation for the AutoRoller is here:
https://skia.googlesource.com/buildbot/+/master/autoroll/README.md
If the roll is causing failures, please contact the current sheriff, who should
be CC'd on the roll, and stop the roller if necessary.
Test: Presubmit checks will test this change.
Change-Id: Id0616e8fca2bf1ead872fbd8221e19669152da58
Exempt-From-Owner-Approval: The autoroll bot does not require owner approval.
diff --git a/DEPS b/DEPS
index eb19786..996c1c3 100644
--- a/DEPS
+++ b/DEPS
@@ -30,7 +30,7 @@
"third_party/externals/sfntly" : "https://chromium.googlesource.com/external/github.com/googlei18n/sfntly.git@b18b09b6114b9b7fe6fc2f96d8b15e8a72f66916",
"third_party/externals/spirv-headers" : "https://skia.googlesource.com/external/github.com/KhronosGroup/SPIRV-Headers.git@661ad91124e6af2272afd00f804d8aa276e17107",
"third_party/externals/spirv-tools" : "https://skia.googlesource.com/external/github.com/KhronosGroup/SPIRV-Tools.git@e9e4393b1c5aad7553c05782acefbe32b42644bd",
- "third_party/externals/swiftshader" : "https://swiftshader.googlesource.com/SwiftShader@44994a88c9cc899c44a55376ace7445bd4ce3896",
+ "third_party/externals/swiftshader" : "https://swiftshader.googlesource.com/SwiftShader@000df8b42041ddf416f6b296636b40b4ede6ce35",
#"third_party/externals/v8" : "https://chromium.googlesource.com/v8/v8.git@5f1ae66d5634e43563b2d25ea652dfb94c31a3b4",
"third_party/externals/wuffs" : "https://github.com/google/wuffs.git@b5c47e273f7f8862bcf04976453d0ec81e6e6650",
"third_party/externals/zlib" : "https://chromium.googlesource.com/chromium/src/third_party/zlib@ea3ba903faac98b64b2bf8de5e98cd97b335a474",
diff --git a/include/private/GrColor.h b/include/private/GrColor.h
index f2f449f..61b25ff 100644
--- a/include/private/GrColor.h
+++ b/include/private/GrColor.h
@@ -76,14 +76,6 @@
#define GrColor_WHITE 0xFFFFFFFF
-static inline GrColor GrColorMul(GrColor c0, GrColor c1) {
- U8CPU r = SkMulDiv255Round(GrColorUnpackR(c0), GrColorUnpackR(c1));
- U8CPU g = SkMulDiv255Round(GrColorUnpackG(c0), GrColorUnpackG(c1));
- U8CPU b = SkMulDiv255Round(GrColorUnpackB(c0), GrColorUnpackB(c1));
- U8CPU a = SkMulDiv255Round(GrColorUnpackA(c0), GrColorUnpackA(c1));
- return GrColorPackRGBA(r, g, b, a);
-}
-
/** Normalizes and coverts an uint8_t to a float. [0, 255] -> [0.0, 1.0] */
static inline float GrNormalizeByteToFloat(uint8_t value) {
static const float ONE_OVER_255 = 1.f / 255.f;
diff --git a/src/core/SkBitmapProcState.cpp b/src/core/SkBitmapProcState.cpp
index 26e5ac9..6e72908 100644
--- a/src/core/SkBitmapProcState.cpp
+++ b/src/core/SkBitmapProcState.cpp
@@ -227,6 +227,7 @@
bool SkBitmapProcState::chooseScanlineProcs(bool trivialMatrix, bool clampClamp) {
SkASSERT(fPixmap.colorType() == kN32_SkColorType);
+ SkASSERT(fInvType <= (SkMatrix::kTranslate_Mask | SkMatrix::kScale_Mask));
fMatrixProc = this->chooseMatrixProc(trivialMatrix);
// TODO(dominikg): SkASSERT(fMatrixProc) instead? chooseMatrixProc never returns nullptr.
diff --git a/src/core/SkPointPriv.h b/src/core/SkPointPriv.h
index 06c83e6..338905d 100644
--- a/src/core/SkPointPriv.h
+++ b/src/core/SkPointPriv.h
@@ -126,22 +126,6 @@
static void SetRectTriStrip(SkPoint v[], const SkRect& rect, size_t stride) {
SetRectTriStrip(v, rect.fLeft, rect.fTop, rect.fRight, rect.fBottom, stride);
}
-
- // Get the idx'th point in a TriStrip (as above). Meant for use with GrVertexWriter
- static SkPoint TriStripPoint(int idx, const float* ltrb) {
- switch (idx) {
- case 0: return { ltrb[0], ltrb[1] };
- case 1: return { ltrb[0], ltrb[3] };
- case 2: return { ltrb[2], ltrb[1] };
- case 3: return { ltrb[2], ltrb[3] };
- }
- SkDEBUGFAIL("Invalid index");
- return { ltrb[0], ltrb[1] };
- }
-
- static SkPoint TriStripPoint(int idx, const SkRect& r) {
- return TriStripPoint(idx, r.asScalars());
- }
};
#endif
diff --git a/src/gpu/GrShaderCaps.cpp b/src/gpu/GrShaderCaps.cpp
index c0b9eb5..43a194d 100644
--- a/src/gpu/GrShaderCaps.cpp
+++ b/src/gpu/GrShaderCaps.cpp
@@ -51,6 +51,8 @@
fFPManipulationSupport = false;
fFloatIs32Bits = true;
fHalfIs32Bits = false;
+ fUnsignedSupport = false;
+ fBuiltinFMASupport = false;
fVersionDeclString = nullptr;
fShaderDerivativeExtensionString = nullptr;
@@ -120,6 +122,7 @@
writer->appendBool("Floating point manipulation support", fFPManipulationSupport);
writer->appendBool("float == fp32", fFloatIs32Bits);
writer->appendBool("half == fp32", fHalfIs32Bits);
+ writer->appendBool("Builtin fma() support", fBuiltinFMASupport);
writer->appendS32("Max FS Samplers", fMaxFragmentSamplers);
writer->appendString("Advanced blend equation interaction",
diff --git a/src/gpu/GrShaderCaps.h b/src/gpu/GrShaderCaps.h
index b0836c4..fb1b17f 100644
--- a/src/gpu/GrShaderCaps.h
+++ b/src/gpu/GrShaderCaps.h
@@ -86,6 +86,9 @@
bool unsignedSupport() const { return fUnsignedSupport; }
+ // SkSL only.
+ bool builtinFMASupport() const { return fBuiltinFMASupport; }
+
AdvBlendEqInteraction advBlendEqInteraction() const { return fAdvBlendEqInteraction; }
bool mustEnableAdvBlendEqs() const {
@@ -254,6 +257,9 @@
bool fHalfIs32Bits : 1;
bool fUnsignedSupport : 1;
+ // Used by SkSL to know when to generate polyfills.
+ bool fBuiltinFMASupport : 1;
+
// Used for specific driver bug work arounds
bool fCanUseAnyFunctionInShader : 1;
bool fCanUseMinAndAbsTogether : 1;
diff --git a/src/gpu/GrVertexWriter.h b/src/gpu/GrVertexWriter.h
index 8bfcef7..6026348 100644
--- a/src/gpu/GrVertexWriter.h
+++ b/src/gpu/GrVertexWriter.h
@@ -21,6 +21,8 @@
* thereof.
*/
struct GrVertexWriter {
+ void* fPtr;
+
template <typename T, typename... Args>
void write(const T& val, const Args&... remainder) {
static_assert(std::is_pod<T>::value, "");
@@ -41,7 +43,51 @@
void write() {}
- void* fPtr;
+ /**
+ * Specialized utility for writing a four-vertices, with some data being replicated at each
+ * vertex, and other data being the appropriate 2-components from an SkRect to construct a
+ * triangle strip.
+ *
+ * writeQuad(A, B, C, ...) is similar to write(A, B, C, ...), except that:
+ *
+ * - Four sets of data will be written
+ * - For any arguments of type TriStrip, a unique SkPoint will be written at each vertex,
+ * in this order: left-top, left-bottom, right-top, right-bottom.
+ */
+ struct TriStrip { const SkRect& fRect; };
+
+ template <typename... Args>
+ void writeQuad(const Args&... remainder) {
+ this->writeQuadVert<0>(remainder...);
+ this->writeQuadVert<1>(remainder...);
+ this->writeQuadVert<2>(remainder...);
+ this->writeQuadVert<3>(remainder...);
+ }
+
+private:
+ template <int corner, typename T, typename... Args>
+ void writeQuadVert(const T& val, const Args&... remainder) {
+ this->writeQuadValue<corner>(val);
+ this->writeQuadVert<corner>(remainder...);
+ }
+
+ template <int corner>
+ void writeQuadVert() {}
+
+ template <int corner, typename T>
+ void writeQuadValue(const T& val) {
+ this->write(val);
+ }
+
+ template <int corner>
+ void writeQuadValue(const TriStrip& r) {
+ switch (corner) {
+ case 0: this->write(r.fRect.fLeft , r.fRect.fTop); break;
+ case 1: this->write(r.fRect.fLeft , r.fRect.fBottom); break;
+ case 2: this->write(r.fRect.fRight, r.fRect.fTop); break;
+ case 3: this->write(r.fRect.fRight, r.fRect.fBottom); break;
+ }
+ }
};
#endif
diff --git a/src/gpu/ccpr/GrCCDrawPathsOp.cpp b/src/gpu/ccpr/GrCCDrawPathsOp.cpp
index b1a70268..c1384fe 100644
--- a/src/gpu/ccpr/GrCCDrawPathsOp.cpp
+++ b/src/gpu/ccpr/GrCCDrawPathsOp.cpp
@@ -192,15 +192,14 @@
hairlineStroke.setStrokeStyle(0);
// How transparent does a 1px stroke have to be in order to appear as thin as the real one?
- GrColor coverageAsAlpha = GrColorPackA4(SkScalarFloorToInt(draw->fStrokeDevWidth * 255));
+ float coverage = draw->fStrokeDevWidth;
draw->fShape = GrShape(path, GrStyle(hairlineStroke, nullptr));
draw->fStrokeDevWidth = 1;
// TODO4F: Preserve float colors
// fShapeConservativeIBounds already accounted for this possibility of inflating the stroke.
- draw->fColor = SkPMColor4f::FromBytes_RGBA(
- GrColorMul(draw->fColor.toBytes_RGBA(), coverageAsAlpha));
+ draw->fColor = draw->fColor * coverage;
}
return RequiresDstTexture(analysis.requiresDstTexture());
diff --git a/src/gpu/gl/GrGLCaps.cpp b/src/gpu/gl/GrGLCaps.cpp
index 9dc76b6..74772ed 100644
--- a/src/gpu/gl/GrGLCaps.cpp
+++ b/src/gpu/gl/GrGLCaps.cpp
@@ -798,6 +798,12 @@
// Unsigned integers only supported in and after GLSL 1.30.
shaderCaps->fUnsignedSupport = ctxInfo.glslGeneration() >= k130_GrGLSLGeneration;
+
+ if (kGL_GrGLStandard == standard) {
+ shaderCaps->fBuiltinFMASupport = ctxInfo.glslGeneration() >= k400_GrGLSLGeneration;
+ } else {
+ shaderCaps->fBuiltinFMASupport = ctxInfo.glslGeneration() >= k320es_GrGLSLGeneration;
+ }
}
bool GrGLCaps::hasPathRenderingSupport(const GrGLContextInfo& ctxInfo, const GrGLInterface* gli) {
diff --git a/src/gpu/ops/GrLatticeOp.cpp b/src/gpu/ops/GrLatticeOp.cpp
index 7db5463..4cc766b 100644
--- a/src/gpu/ops/GrLatticeOp.cpp
+++ b/src/gpu/ops/GrLatticeOp.cpp
@@ -16,7 +16,6 @@
#include "SkBitmap.h"
#include "SkLatticeIter.h"
#include "SkMatrixPriv.h"
-#include "SkPointPriv.h"
#include "SkRect.h"
#include "glsl/GrGLSLColorSpaceXformHelper.h"
#include "glsl/GrGLSLGeometryProcessor.h"
@@ -251,17 +250,15 @@
coords = kFlipMuls * coords + kFlipOffsets;
domain = SkNx_shuffle<0, 3, 2, 1>(kFlipMuls * domain + kFlipOffsets);
}
- float texDomain[4];
- float texCoords[4];
- domain.store(texDomain);
- coords.store(texCoords);
+ SkRect texDomain;
+ SkRect texCoords;
+ domain.store(&texDomain);
+ coords.store(&texCoords);
- for (int j = 0; j < kVertsPerRect; ++j) {
- vertices.write(SkPointPriv::TriStripPoint(j, dstR),
- SkPointPriv::TriStripPoint(j, texCoords),
+ vertices.writeQuad(GrVertexWriter::TriStrip{ dstR },
+ GrVertexWriter::TriStrip{ texCoords },
texDomain,
patchColor);
- }
}
// If we didn't handle it above, apply the matrix here.
diff --git a/src/gpu/ops/GrRegionOp.cpp b/src/gpu/ops/GrRegionOp.cpp
index 5268582..d824609 100644
--- a/src/gpu/ops/GrRegionOp.cpp
+++ b/src/gpu/ops/GrRegionOp.cpp
@@ -12,8 +12,8 @@
#include "GrOpFlushState.h"
#include "GrResourceProvider.h"
#include "GrSimpleMeshDrawOpHelper.h"
+#include "GrVertexWriter.h"
#include "SkMatrixPriv.h"
-#include "SkPointPriv.h"
#include "SkRegion.h"
static const int kVertsPerInstance = 4;
@@ -27,30 +27,6 @@
viewMatrix);
}
-static void tesselate_region(intptr_t vertices,
- size_t vertexStride,
- GrColor color,
- const SkRegion& region) {
- SkRegion::Iterator iter(region);
-
- intptr_t verts = vertices;
- while (!iter.done()) {
- SkRect rect = SkRect::Make(iter.rect());
- SkPoint* position = (SkPoint*)verts;
- SkPointPriv::SetRectTriStrip(position, rect, vertexStride);
-
- static const int kColorOffset = sizeof(SkPoint);
- GrColor* vertColor = reinterpret_cast<GrColor*>(verts + kColorOffset);
- for (int i = 0; i < kVertsPerInstance; i++) {
- *vertColor = color;
- vertColor = (GrColor*)((intptr_t)vertColor + vertexStride);
- }
-
- verts += vertexStride * kVertsPerInstance;
- iter.next();
- }
-}
-
namespace {
class RegionOp final : public GrMeshDrawOp {
@@ -119,7 +95,6 @@
SkDebugf("Couldn't create GrGeometryProcessor\n");
return;
}
- size_t kVertexStride = gp->vertexStride();
int numRegions = fRegions.count();
int numRects = 0;
@@ -131,21 +106,24 @@
return;
}
sk_sp<const GrBuffer> indexBuffer = target->resourceProvider()->refQuadIndexBuffer();
- PatternHelper helper(target, GrPrimitiveType::kTriangles, kVertexStride, indexBuffer.get(),
- kVertsPerInstance, kIndicesPerInstance, numRects);
- void* vertices = helper.vertices();
- if (!vertices || !indexBuffer) {
+ PatternHelper helper(target, GrPrimitiveType::kTriangles, gp->vertexStride(),
+ indexBuffer.get(), kVertsPerInstance, kIndicesPerInstance, numRects);
+ GrVertexWriter vertices{helper.vertices()};
+ if (!vertices.fPtr || !indexBuffer) {
SkDebugf("Could not allocate vertices\n");
return;
}
- intptr_t verts = reinterpret_cast<intptr_t>(vertices);
for (int i = 0; i < numRegions; i++) {
// TODO4F: Preserve float colors
- tesselate_region(verts, kVertexStride, fRegions[i].fColor.toBytes_RGBA(),
- fRegions[i].fRegion);
- int numRectsInRegion = fRegions[i].fRegion.computeRegionComplexity();
- verts += numRectsInRegion * kVertsPerInstance * kVertexStride;
+ GrColor color = fRegions[i].fColor.toBytes_RGBA();
+
+ SkRegion::Iterator iter(fRegions[i].fRegion);
+ while (!iter.done()) {
+ SkRect rect = SkRect::Make(iter.rect());
+ vertices.writeQuad(GrVertexWriter::TriStrip{ rect }, color);
+ iter.next();
+ }
}
auto pipe = fHelper.makePipeline(target);
helper.recordDraw(target, std::move(gp), pipe.fPipeline, pipe.fFixedDynamicState);
diff --git a/src/gpu/text/GrTextBlob.h b/src/gpu/text/GrTextBlob.h
index 8a1e0ec..7d4450c 100644
--- a/src/gpu/text/GrTextBlob.h
+++ b/src/gpu/text/GrTextBlob.h
@@ -329,35 +329,18 @@
SkScalar* transX, SkScalar* transY);
// df properties
- void setDrawAsDistanceFields() { fFlags |= kDrawAsSDF_Flag; }
- bool drawAsDistanceFields() const { return SkToBool(fFlags & kDrawAsSDF_Flag); }
- void setUseLCDText(bool useLCDText) {
- fFlags = useLCDText ? fFlags | kUseLCDText_Flag : fFlags & ~kUseLCDText_Flag;
- }
- bool hasUseLCDText() const { return SkToBool(fFlags & kUseLCDText_Flag); }
- void setAntiAliased(bool antiAliased) {
- fFlags = antiAliased ? fFlags | kAntiAliased_Flag : fFlags & ~kAntiAliased_Flag;
- }
- bool isAntiAliased() const { return SkToBool(fFlags & kAntiAliased_Flag); }
- void setHasWCoord(bool hasW) {
- fFlags = hasW ? (fFlags | kHasWCoord_Flag) : fFlags & ~kHasWCoord_Flag;
- }
- bool hasWCoord() const { return SkToBool(fFlags & kHasWCoord_Flag); }
- void setNeedsTransform(bool needsTransform) {
- fFlags = needsTransform ? (fFlags | kNeedsTransform_Flag)
- : fFlags & ~kNeedsTransform_Flag;
- }
- bool needsTransform() const { return SkToBool(fFlags & kNeedsTransform_Flag); }
+ void setDrawAsDistanceFields() { fFlags.drawAsSdf = true; }
+ bool drawAsDistanceFields() const { return fFlags.drawAsSdf; }
+ void setUseLCDText(bool useLCDText) { fFlags.useLCDText = useLCDText; }
+ bool hasUseLCDText() const { return fFlags.useLCDText; }
+ void setAntiAliased(bool antiAliased) { fFlags.antiAliased = antiAliased; }
+ bool isAntiAliased() const { return fFlags.antiAliased; }
+ void setHasWCoord(bool hasW) { fFlags.hasWCoord = hasW; }
+ bool hasWCoord() const { return fFlags.hasWCoord; }
+ void setNeedsTransform(bool needsTransform) { fFlags.needsTransform = needsTransform; }
+ bool needsTransform() const { return fFlags.needsTransform; }
private:
- enum Flag {
- kDrawAsSDF_Flag = 0x01,
- kUseLCDText_Flag = 0x02,
- kAntiAliased_Flag = 0x04,
- kHasWCoord_Flag = 0x08,
- kNeedsTransform_Flag = 0x10
- };
-
GrDrawOpAtlas::BulkUseTokenUpdater fBulkUseToken;
sk_sp<GrTextStrike> fStrike;
SkMatrix fCurrentViewMatrix;
@@ -371,7 +354,13 @@
SkScalar fY;
GrColor fColor{GrColor_ILLEGAL};
GrMaskFormat fMaskFormat{kA8_GrMaskFormat};
- uint32_t fFlags{0};
+ struct {
+ bool drawAsSdf:1;
+ bool useLCDText:1;
+ bool antiAliased:1;
+ bool hasWCoord:1;
+ bool needsTransform:1;
+ } fFlags{false, false, false, false, false};
}; // SubRunInfo
diff --git a/src/shaders/gradients/SkGradientShader.cpp b/src/shaders/gradients/SkGradientShader.cpp
index 4726f09..2a9bfdd 100644
--- a/src/shaders/gradients/SkGradientShader.cpp
+++ b/src/shaders/gradients/SkGradientShader.cpp
@@ -557,6 +557,10 @@
return avg;
}
+// The default SkScalarNearlyZero threshold of .0024 is too big and causes regressions for svg
+// gradients defined in the wild.
+static constexpr SkScalar kDegenerateThreshold = SK_Scalar1 / (1 << 15);
+
// Except for special circumstances of clamped gradients, every gradient shape--when degenerate--
// can be mapped to the same fallbacks. The specific shape factories must account for special
// clamped conditions separately, this will always return the last color for clamped gradients.
@@ -686,7 +690,7 @@
return nullptr;
}
- if (SkScalarNearlyZero((pts[1] - pts[0]).length())) {
+ if (SkScalarNearlyZero((pts[1] - pts[0]).length(), kDegenerateThreshold)) {
// Degenerate gradient, the only tricky complication is when in clamp mode, the limit of
// the gradient approaches two half planes of solid color (first and last). However, they
// are divided by the line perpendicular to the start and end point, which becomes undefined
@@ -733,7 +737,7 @@
return nullptr;
}
- if (SkScalarNearlyZero(radius)) {
+ if (SkScalarNearlyZero(radius, kDegenerateThreshold)) {
// Degenerate gradient optimization, and no special logic needed for clamped radial gradient
return make_degenerate_gradient(colors, pos, colorCount, std::move(colorSpace), mode);
}
@@ -778,15 +782,15 @@
if (!valid_grad(colors, pos, colorCount, mode)) {
return nullptr;
}
- if (SkScalarNearlyZero((start - end).length())) {
+ if (SkScalarNearlyZero((start - end).length(), kDegenerateThreshold)) {
// If the center positions are the same, then the gradient is the radial variant of a 2 pt
// conical gradient, an actual radial gradient (startRadius == 0), or it is fully degenerate
// (startRadius == endRadius).
- if (SkScalarNearlyEqual(startRadius, endRadius)) {
+ if (SkScalarNearlyEqual(startRadius, endRadius, kDegenerateThreshold)) {
// Degenerate case, where the interpolation region area approaches zero. The proper
// behavior depends on the tile mode, which is consistent with the default degenerate
// gradient behavior, except when mode = clamp and the radii > 0.
- if (mode == SkShader::TileMode::kClamp_TileMode && endRadius > SK_ScalarNearlyZero) {
+ if (mode == SkShader::TileMode::kClamp_TileMode && endRadius > kDegenerateThreshold) {
// The interpolation region becomes an infinitely thin ring at the radius, so the
// final gradient will be the first color repeated from p=0 to 1, and then a hard
// stop switching to the last color at p=1.
@@ -799,7 +803,7 @@
return make_degenerate_gradient(
colors, pos, colorCount, std::move(colorSpace), mode);
}
- } else if (SkScalarNearlyZero(startRadius)) {
+ } else if (SkScalarNearlyZero(startRadius, kDegenerateThreshold)) {
// We can treat this gradient as radial, which is faster. If we got here, we know
// that endRadius is not equal to 0, so this produces a meaningful gradient
return MakeRadial(start, endRadius, colors, std::move(colorSpace), pos, colorCount,
@@ -859,10 +863,10 @@
return nullptr;
}
- if (SkScalarNearlyEqual(startAngle, endAngle)) {
+ if (SkScalarNearlyEqual(startAngle, endAngle, kDegenerateThreshold)) {
// Degenerate gradient, which should follow default degenerate behavior unless it is
// clamped and the angle is greater than 0.
- if (mode == SkShader::kClamp_TileMode && endAngle > SK_ScalarNearlyZero) {
+ if (mode == SkShader::kClamp_TileMode && endAngle > kDegenerateThreshold) {
// In this case, the first color is repeated from 0 to the angle, then a hardstop
// switches to the last color (all other colors are compressed to the infinitely thin
// interpolation region).
diff --git a/src/sksl/SkSLGLSLCodeGenerator.cpp b/src/sksl/SkSLGLSLCodeGenerator.cpp
index d56d611..26bda18 100644
--- a/src/sksl/SkSLGLSLCodeGenerator.cpp
+++ b/src/sksl/SkSLGLSLCodeGenerator.cpp
@@ -467,6 +467,7 @@
(*fFunctionClasses)["dFdx"] = FunctionClass::kDerivative;
(*fFunctionClasses)["dFdy"] = FunctionClass::kDerivative;
(*fFunctionClasses)["fwidth"] = FunctionClass::kDerivative;
+ (*fFunctionClasses)["fma"] = FunctionClass::kFMA;
(*fFunctionClasses)["fract"] = FunctionClass::kFract;
(*fFunctionClasses)["inverse"] = FunctionClass::kInverse;
(*fFunctionClasses)["inverseSqrt"] = FunctionClass::kInverseSqrt;
@@ -535,6 +536,19 @@
return;
}
break;
+ case FunctionClass::kFMA:
+ if (!fProgram.fSettings.fCaps->builtinFMASupport()) {
+ SkASSERT(c.fArguments.size() == 3);
+ this->write("((");
+ this->writeExpression(*c.fArguments[0], kSequence_Precedence);
+ this->write(") * (");
+ this->writeExpression(*c.fArguments[1], kSequence_Precedence);
+ this->write(") + (");
+ this->writeExpression(*c.fArguments[2], kSequence_Precedence);
+ this->write("))");
+ return;
+ }
+ break;
case FunctionClass::kFract:
if (!fProgram.fSettings.fCaps->canUseFractForNegativeValues()) {
SkASSERT(c.fArguments.size() == 1);
diff --git a/src/sksl/SkSLGLSLCodeGenerator.h b/src/sksl/SkSLGLSLCodeGenerator.h
index 906c1b0..99e7d47 100644
--- a/src/sksl/SkSLGLSLCodeGenerator.h
+++ b/src/sksl/SkSLGLSLCodeGenerator.h
@@ -228,6 +228,7 @@
kAtan,
kDeterminant,
kDerivative,
+ kFMA,
kFract,
kInverse,
kInverseSqrt,