Opt-in burst mechanism for pipeline shaders

Add a dedicated virtual for shaders to select burst mode.

Enabled for linear gradients with more than 8 stops, as a start.

BUG=skia:6710

Change-Id: I1b19124f42d1d805de27a5db6a26601c386bb9ff
Reviewed-on: https://skia-review.googlesource.com/18628
Commit-Queue: Florin Malita <fmalita@chromium.org>
Reviewed-by: Mike Reed <reed@google.com>
Reviewed-by: Mike Klein <mtklein@chromium.org>
diff --git a/src/core/SkRasterPipelineBlitter.cpp b/src/core/SkRasterPipelineBlitter.cpp
index cba39f0..3502174 100644
--- a/src/core/SkRasterPipelineBlitter.cpp
+++ b/src/core/SkRasterPipelineBlitter.cpp
@@ -25,14 +25,17 @@
     // This is our common entrypoint for creating the blitter once we've sorted out shaders.
     static SkBlitter* Create(const SkPixmap&, const SkPaint&, SkArenaAlloc*,
                              const SkRasterPipeline& shaderPipeline,
+                             SkShaderBase::Context*,
                              bool is_opaque, bool is_constant, bool wants_dither);
 
     SkRasterPipelineBlitter(SkPixmap dst,
                             SkBlendMode blend,
-                            SkArenaAlloc* alloc)
+                            SkArenaAlloc* alloc,
+                            SkShaderBase::Context* burstCtx)
         : fDst(dst)
         , fBlend(blend)
         , fAlloc(alloc)
+        , fBurstCtx(burstCtx)
         , fColorPipeline(alloc)
     {}
 
@@ -51,9 +54,13 @@
     void maybe_clamp  (SkRasterPipeline*) const;
     void append_store (SkRasterPipeline*) const;
 
+    // If we have an burst context, use it to fill our shader buffer.
+    void maybe_shade(int x, int y, int w);
+
     SkPixmap               fDst;
     SkBlendMode            fBlend;
     SkArenaAlloc*          fAlloc;
+    SkShaderBase::Context* fBurstCtx;
     SkRasterPipeline       fColorPipeline;
 
     // We may be able to specialize blitH() into a memset.
@@ -68,11 +75,14 @@
 
     // These values are pointed to by the blit pipelines above,
     // which allows us to adjust them from call to call.
+    void*              fShaderOutput    = nullptr;
     void*              fDstPtr          = nullptr;
     const void*        fMaskPtr         = nullptr;
     float              fCurrentCoverage = 0.0f;
     float              fDitherRate      = 0.0f;
 
+    std::vector<SkPM4f> fShaderBuffer;
+
     typedef SkBlitter INHERITED;
 };
 
@@ -96,20 +106,31 @@
         bool is_opaque    = paintColor->a() == 1.0f,
              is_constant  = true;
         return SkRasterPipelineBlitter::Create(dst, paint, alloc,
-                                               shaderPipeline,
+                                               shaderPipeline, nullptr,
                                                is_opaque, is_constant, wants_dither);
     }
 
     bool is_opaque    = shader->isOpaque() && paintColor->a() == 1.0f;
     bool is_constant  = shader->isConstant();
+
+    // Check whether the shader prefers to run in burst mode.
+    if (auto* burstCtx = shader->makeBurstPipelineContext(
+        SkShaderBase::ContextRec(paint, ctm, nullptr, SkShaderBase::ContextRec::kPM4f_DstType,
+                                 dstCS), alloc)) {
+        return SkRasterPipelineBlitter::Create(dst, paint, alloc,
+                                               shaderPipeline, burstCtx,
+                                               is_opaque, is_constant, wants_dither);
+    }
+
     if (shader->appendStages(&shaderPipeline, dstCS, alloc, ctm, paint)) {
         if (paintColor->a() != 1.0f) {
             shaderPipeline.append(SkRasterPipeline::scale_1_float, &paintColor->fVec[SkPM4f::A]);
         }
-        return SkRasterPipelineBlitter::Create(dst, paint, alloc, shaderPipeline,
+        return SkRasterPipelineBlitter::Create(dst, paint, alloc, shaderPipeline, nullptr,
                                                is_opaque, is_constant, wants_dither);
     }
 
+    // The shader has opted out of drawing anything.
     return alloc->make<SkNullBlitter>();
 }
 
@@ -120,7 +141,7 @@
                                          bool wants_dither,
                                          SkArenaAlloc* alloc) {
     bool is_constant = false;  // If this were the case, it'd be better to just set a paint color.
-    return SkRasterPipelineBlitter::Create(dst, paint, alloc, shaderPipeline,
+    return SkRasterPipelineBlitter::Create(dst, paint, alloc, shaderPipeline, nullptr,
                                            is_opaque, is_constant, wants_dither);
 }
 
@@ -128,12 +149,14 @@
                                            const SkPaint& paint,
                                            SkArenaAlloc* alloc,
                                            const SkRasterPipeline& shaderPipeline,
+                                           SkShaderBase::Context* burstCtx,
                                            bool is_opaque,
                                            bool is_constant,
                                            bool wants_dither) {
     auto blitter = alloc->make<SkRasterPipelineBlitter>(dst,
                                                         paint.getBlendMode(),
-                                                        alloc);
+                                                        alloc,
+                                                        burstCtx);
 
     // Our job in this factory is to fill out the blitter's color pipeline.
     // This is the common front of the full blit pipelines, each constructed lazily on first use.
@@ -141,7 +164,11 @@
     auto colorPipeline = &blitter->fColorPipeline;
 
     // Let's get the shader in first.
-    colorPipeline->extend(shaderPipeline);
+    if (burstCtx) {
+        colorPipeline->append(SkRasterPipeline::load_f32, &blitter->fShaderOutput);
+    } else {
+        colorPipeline->extend(shaderPipeline);
+    }
 
     // If there's a color filter it comes next.
     if (auto colorFilter = paint.getColorFilter()) {
@@ -250,6 +277,17 @@
     }
 }
 
+void SkRasterPipelineBlitter::maybe_shade(int x, int y, int w) {
+    if (fBurstCtx) {
+        if (w > SkToInt(fShaderBuffer.size())) {
+            fShaderBuffer.resize(w);
+        }
+        fBurstCtx->shadeSpan4f(x,y, fShaderBuffer.data(), w);
+        // We'll be reading from fShaderOutput + x.
+        fShaderOutput = fShaderBuffer.data() - x;
+    }
+}
+
 void SkRasterPipelineBlitter::blitH(int x, int y, int w) {
     fDstPtr = fDst.writable_addr(0,y);
 
@@ -281,6 +319,7 @@
         }
         fBlitH = p.compile();
     }
+    this->maybe_shade(x,y,w);
     fBlitH(x,y,w);
 }
 
@@ -308,6 +347,7 @@
             case 0x00:                       break;
             case 0xff: this->blitH(x,y,run); break;
             default:
+                this->maybe_shade(x,y,run);
                 fCurrentCoverage = *aa * (1/255.0f);
                 fBlitAntiH(x,y,run);
         }
@@ -368,6 +408,7 @@
     for (int y = clip.top(); y < clip.bottom(); y++) {
         fDstPtr = fDst.writable_addr(0,y);
 
+        this->maybe_shade(x,y,clip.width());
         switch (mask.fFormat) {
             case SkMask::kA8_Format:
                 fMaskPtr = mask.getAddr8(x,y)-x;