refactor SkRasterPipelineBlitter
authorMike Klein <mtklein@chromium.org>
Fri, 19 May 2017 16:01:01 +0000 (12:01 -0400)
committerSkia Commit-Bot <skia-commit-bot@chromium.org>
Mon, 22 May 2017 14:28:15 +0000 (14:28 +0000)
This refactors the factories so that the create-from-paint factory is a
front-patch to the create-from-shader-pipeline factory.  Feature-wise,
we make the pre-baked shader pipeline responsible for modulating by
paint alpha; the factory only adds when creating from the paint.

We can fold the alpha into the colors in drawVertices, which makes it
run a bit faster, dropping the need for a scale_1_float runtime stage.
This causes a few invisible diffs on the "vertices" GM, but everything
else draws the same.

Change-Id: I3eeacc9aafbce2023ab18991bbb68c35645e9387
Reviewed-on: https://skia-review.googlesource.com/17395
Reviewed-by: Mike Reed <reed@google.com>
Commit-Queue: Mike Klein <mtklein@chromium.org>

src/core/SkCoreBlitters.h
src/core/SkDraw_vertices.cpp
src/core/SkRasterPipelineBlitter.cpp

index 277c8e74b34e8216fe1da44a60dd2fcaa0e003fe..8dfeb7486a431fd02ac52755ba4876e3cdc6e40f 100644 (file)
@@ -202,12 +202,15 @@ SkBlitter* SkBlitter_ChooseD565(const SkPixmap& device, const SkPaint& paint,
                                 SkArenaAlloc* allocator);
 
 
-// Returns nullptr if no SkRasterPipeline blitter can be constructed for this paint.
+// Neither of these ever returns nullptr, but this first factory may return a SkNullBlitter.
 SkBlitter* SkCreateRasterPipelineBlitter(const SkPixmap&, const SkPaint&, const SkMatrix& ctm,
                                          SkArenaAlloc*);
-SkBlitter* SkCreateRasterPipelineBlitter(const SkPixmap&, const SkPaint&, const SkMatrix& ctm,
+// Use this if you've pre-baked a shader pipeline, including modulating with paint alpha.
+// This factory never returns an SkNullBlitter.
+SkBlitter* SkCreateRasterPipelineBlitter(const SkPixmap&, const SkPaint&,
                                          const SkRasterPipeline& shaderPipeline,
-                                         bool shader_is_opaque, bool shader_wants_dither,
+                                         bool shader_is_opaque,
+                                         bool shader_wants_dither,
                                          SkArenaAlloc*);
 
 #endif
index 9735fa8a256217bf0c110500768c8a4d7b7c78e7..ceeb3c2ddc14c5b6166d924ab6f354ff9cc04346 100644 (file)
@@ -257,9 +257,9 @@ void SkTriColorShader::TriColorShaderContext::shadeSpan4f(int x, int y, SkPM4f d
 #ifndef SK_IGNORE_TO_STRING
 void SkTriColorShader::toString(SkString* str) const {
     str->append("SkTriColorShader: (");
-    
+
     this->INHERITED::toString(str);
-    
+
     str->append(")");
 }
 #endif
@@ -472,6 +472,17 @@ void SkDraw::drawVertices(SkVertices::VertexMode vmode, int count,
         //
         SkPM4f* dstColors = convert_colors(colors, count, fDst.colorSpace(), &alloc);
 
+        bool is_opaque;
+        if (paint.getAlpha() == 0xff) {
+            is_opaque = compute_is_opaque(colors, count);
+        } else {
+            is_opaque = false;
+            Sk4f alpha = paint.getAlpha() * (1/255.0f);
+            for (int i = 0; i < count; i++) {
+                (dstColors[i].to4f() * alpha).store(dstColors + i);
+            }
+        }
+
         shaderPipeline.append(SkRasterPipeline::matrix_4x3, &matrix43);
         // In theory we should never need to clamp. However, either due to imprecision in our
         // matrix43, or the scan converter passing us pixel centers that in fact are not within
@@ -480,9 +491,8 @@ void SkDraw::drawVertices(SkVertices::VertexMode vmode, int count,
         shaderPipeline.append(SkRasterPipeline::clamp_0);
         shaderPipeline.append(SkRasterPipeline::clamp_a);
 
-        bool is_opaque = compute_is_opaque(colors, count),
-             wants_dither = paint.isDither();
-        auto blitter = SkCreateRasterPipelineBlitter(fDst, paint, *fMatrix, shaderPipeline,
+        bool wants_dither = paint.isDither();
+        auto blitter = SkCreateRasterPipelineBlitter(fDst, paint, shaderPipeline,
                                                      is_opaque, wants_dither, &alloc);
         SkASSERT(!blitter->isNullBlitter());
 
index b62b43f1ce198d4c17e3a4da3c2718262ac80335..6397cae8d9a4abcda7a9f18ec0acec1062a3ca37 100644 (file)
 
 class SkRasterPipelineBlitter : public SkBlitter {
 public:
-    static SkBlitter* Create(const SkPixmap&, const SkPaint&, const SkMatrix& ctm,
-                             SkArenaAlloc*);
-    static SkBlitter* Create(const SkPixmap&, const SkPaint&, const SkMatrix& ctm,
+    // Create using paint.getShader() or paint.getColor() if there is no shader.
+    // If there's a shader, we will modulate the shader color by the paint alpha.
+    static SkBlitter* Create(const SkPixmap&, const SkPaint&, SkArenaAlloc*, const SkMatrix& ctm);
+
+    // Create using pre-built shader pipeline.
+    // This pre-built pipeline must already have handled modulating with the paint alpha.
+    static SkBlitter* Create(const SkPixmap&, const SkPaint&, SkArenaAlloc*,
                              const SkRasterPipeline& shaderPipeline,
-                             bool is_opaque, bool wants_dither,
-                             SkArenaAlloc*);
+                             bool is_opaque, bool is_constant, bool wants_dither);
 
-    SkRasterPipelineBlitter(SkPixmap dst, SkBlendMode blend, SkPM4f paintColor)
+    SkRasterPipelineBlitter(SkPixmap dst, SkBlendMode blend)
         : fDst(dst)
         , fBlend(blend)
-        , fPaintColor(paintColor)
     {}
 
     void blitH    (int x, int y, int w)                            override;
@@ -42,9 +44,6 @@ public:
     // blits using something like a SkRasterPipeline::runFew() method.
 
 private:
-    void finishBuilding(const SkPaint& paint, const SkMatrix& ctm, bool is_oapque,
-                        bool is_constant, bool wants_dither, SkArenaAlloc* alloc);
-
     void append_load_d(SkRasterPipeline*) const;
     void append_blend (SkRasterPipeline*) const;
     void maybe_clamp  (SkRasterPipeline*) const;
@@ -52,8 +51,7 @@ private:
 
     SkPixmap         fDst;
     SkBlendMode      fBlend;
-    SkPM4f           fPaintColor;
-    SkRasterPipeline fShader;
+    SkRasterPipeline fColorPipeline;
 
     // We may be able to specialize blitH() into a memset.
     bool     fCanMemsetInBlitH = false;
@@ -80,138 +78,125 @@ SkBlitter* SkCreateRasterPipelineBlitter(const SkPixmap& dst,
                                          const SkPaint& paint,
                                          const SkMatrix& ctm,
                                          SkArenaAlloc* alloc) {
-    return SkRasterPipelineBlitter::Create(dst, paint, ctm, alloc);
+    return SkRasterPipelineBlitter::Create(dst, paint, alloc, ctm);
+}
+
+SkBlitter* SkCreateRasterPipelineBlitter(const SkPixmap& dst,
+                                         const SkPaint& paint,
+                                         const SkRasterPipeline& shaderPipeline,
+                                         bool is_opaque,
+                                         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, is_opaque, is_constant, wants_dither);
 }
 
 SkBlitter* SkRasterPipelineBlitter::Create(const SkPixmap& dst,
                                            const SkPaint& paint,
-                                           const SkMatrix& ctm,
-                                           SkArenaAlloc* alloc) {
-    auto blitter = alloc->make<SkRasterPipelineBlitter>(
-            dst,
-            paint.getBlendMode(),
-            SkPM4f_from_SkColor(paint.getColor(), dst.colorSpace()));
-
-    SkPM4f*           paintColor  = &blitter->fPaintColor;
-    SkRasterPipeline* pipeline    = &blitter->fShader;
-    SkShader*         shader      = paint.getShader();
-
-    bool is_opaque    = paintColor->a() == 1.0f,
-         is_constant  = true,
-         wants_dither = false;
-
-    if (shader) {
-        pipeline->append(SkRasterPipeline::seed_shader, &blitter->fCurrentY);
-        if (!shader->appendStages(pipeline, dst.colorSpace(), alloc, ctm, paint)) {
+                                           SkArenaAlloc* alloc,
+                                           const SkMatrix& ctm) {
+    auto paintColor = alloc->make<SkPM4f>(SkPM4f_from_SkColor(paint.getColor(),
+                                                              dst.colorSpace()));
+    if (auto shader = paint.getShader()) {
+        SkRasterPipeline shaderPipeline;
+        if (!shader->appendStages(&shaderPipeline, dst.colorSpace(), alloc, ctm, paint)) {
             // When a shader fails to append stages, it means it has vetoed drawing entirely.
             return alloc->make<SkNullBlitter>();
         }
 
-        if (!is_opaque) {
-            pipeline->append(SkRasterPipeline::scale_1_float, &paintColor->fVec[SkPM4f::A]);
+        bool is_opaque = shader->isOpaque();
+        if (paintColor->a() != 1.0f) {
+            shaderPipeline.append(SkRasterPipeline::scale_1_float, &paintColor->fVec[SkPM4f::A]);
+            is_opaque = false;
         }
-        is_opaque    = is_opaque && shader->isOpaque();
-        is_constant  = shader->isConstant();
-        wants_dither = shader->asAGradient(nullptr) >= SkShader::kLinear_GradientType;
 
-    } else {
-        pipeline->append(SkRasterPipeline::constant_color, paintColor);
+        bool is_constant  = shader->isConstant();
+        bool wants_dither = shader->asAGradient(nullptr) >= SkShader::kLinear_GradientType;
+        return Create(dst, paint, alloc, shaderPipeline, is_opaque, is_constant, wants_dither);
     }
 
-    blitter->finishBuilding(paint, ctm, is_opaque, is_constant, wants_dither, alloc);
-    return blitter;
-}
-
-SkBlitter* SkCreateRasterPipelineBlitter(const SkPixmap& dst,
-                                         const SkPaint& paint,
-                                         const SkMatrix& ctm,
-                                         const SkRasterPipeline& shaderPipeline,
-                                         bool is_opaque, bool wants_dither,
-                                         SkArenaAlloc* alloc) {
-    return SkRasterPipelineBlitter::Create(dst, paint, ctm, shaderPipeline,
-                                           is_opaque, wants_dither, alloc);
+    SkRasterPipeline shaderPipeline;
+    shaderPipeline.append(SkRasterPipeline::constant_color, paintColor);
+    bool is_opaque    = paintColor->a() == 1.0f,
+         is_constant  = true,
+         wants_dither = false;
+    return Create(dst, paint, alloc, shaderPipeline, is_opaque, is_constant, wants_dither);
 }
 
 SkBlitter* SkRasterPipelineBlitter::Create(const SkPixmap& dst,
                                            const SkPaint& paint,
-                                           const SkMatrix& ctm,
+                                           SkArenaAlloc* alloc,
                                            const SkRasterPipeline& shaderPipeline,
-                                           bool is_opaque, bool wants_dither,
-                                           SkArenaAlloc* alloc) {
-    auto blitter = alloc->make<SkRasterPipelineBlitter>(
-            dst,
-            paint.getBlendMode(),
-            SkPM4f_from_SkColor(paint.getColor(), dst.colorSpace()));
-
-    bool              is_constant = false;   // we figure a custom shaderPipeline is never constant
-    SkPM4f*           paintColor  = &blitter->fPaintColor;
-    SkRasterPipeline* pipeline    = &blitter->fShader;
-
-    pipeline->append(SkRasterPipeline::seed_shader, &blitter->fCurrentY);
-    pipeline->extend(shaderPipeline);
-
-    if (paintColor->a() != 1.0f) {
-        pipeline->append(SkRasterPipeline::scale_1_float, &paintColor->fVec[SkPM4f::A]);
-        is_opaque = false;
+                                           bool is_opaque,
+                                           bool is_constant,
+                                           bool wants_dither) {
+    auto blitter = alloc->make<SkRasterPipelineBlitter>(dst, paint.getBlendMode());
+
+    // 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.
+    // The full blit pipelines handle reading and writing the dst, blending, coverage, dithering.
+    auto colorPipeline = &blitter->fColorPipeline;
+
+    // Let's get the shader in first.  If the shader's not constant, it'll need seeding with x,y.
+    if (!is_constant) {
+        colorPipeline->append(SkRasterPipeline::seed_shader, &blitter->fCurrentY);
     }
+    colorPipeline->extend(shaderPipeline);
 
-    blitter->finishBuilding(paint, ctm, is_opaque, is_constant, wants_dither, alloc);
-    return blitter;
-}
+    // If there's a color filter it comes next.
+    if (auto colorFilter = paint.getColorFilter()) {
+        colorFilter->appendStages(colorPipeline, dst.colorSpace(), alloc, is_opaque);
+        is_opaque = is_opaque && (colorFilter->getFlags() & SkColorFilter::kAlphaUnchanged_Flag);
+    }
 
-void SkRasterPipelineBlitter::finishBuilding(const SkPaint& paint, const SkMatrix& ctm,
-                                             bool is_opaque, bool is_constant, bool wants_dither,
-                                             SkArenaAlloc* alloc) {
-    SkBlendMode*      blend       = &fBlend;
-    SkPM4f*           paintColor  = &fPaintColor;
-    SkRasterPipeline* pipeline    = &fShader;
-    SkColorFilter*    colorFilter = paint.getColorFilter();
-
-    SkASSERT(fDitherCtx.rate == 0);
-    if ((paint.isDither() && fDst.info().colorType() == kRGB_565_SkColorType) || wants_dither) {
-        switch (fDst.info().colorType()) {
-            case kRGB_565_SkColorType:
-                fDitherCtx.rate =  1/63.0f;
-                is_constant = false;
-                break;
+    // We'll dither if the shader wants to, or if we're drawing 565 and the paint wants to.
+    // Not all formats make sense to dither (think, F16).  We set their dither rate to zero.
+    // We need to decide if we're going to dither now to keep is_constant accurate.
+    if (wants_dither ||
+            (paint.isDither() && dst.info().colorType() == kRGB_565_SkColorType)) {
+        switch (dst.info().colorType()) {
+            default:                     blitter->fDitherCtx.rate =     0.0f; break;
+            case   kRGB_565_SkColorType: blitter->fDitherCtx.rate =  1/63.0f; break;
             case kRGBA_8888_SkColorType:
-            case kBGRA_8888_SkColorType:
-                fDitherCtx.rate = 1/255.0f;
-                is_constant = false;
-                break;
-            default:
-                break;
+            case kBGRA_8888_SkColorType: blitter->fDitherCtx.rate = 1/255.0f; break;
         }
     }
+    is_constant = is_constant && (blitter->fDitherCtx.rate == 0.0f);
 
-    if (colorFilter) {
-        colorFilter->appendStages(pipeline, fDst.colorSpace(), alloc, is_opaque);
-        is_opaque = is_opaque && (colorFilter->getFlags() & SkColorFilter::kAlphaUnchanged_Flag);
-    }
+    // We're logically done here.  The code between here and return blitter is all optimization.
 
+    // A pipeline that's still constant here can collapse back into a constant color.
     if (is_constant) {
-        pipeline->append(SkRasterPipeline::store_f32, &paintColor);
-        pipeline->run(0,1);
+        auto constantColor = alloc->make<SkPM4f>();
+        colorPipeline->append(SkRasterPipeline::store_f32, &constantColor);
+        colorPipeline->run(0,1);
+        *colorPipeline = SkRasterPipeline();
+        colorPipeline->append(SkRasterPipeline::constant_color, constantColor);
 
-        *pipeline = SkRasterPipeline();
-        pipeline->append(SkRasterPipeline::constant_color, paintColor);
-
-        is_opaque = paintColor->a() == 1.0f;
+        is_opaque = constantColor->a() == 1.0f;
     }
 
-    if (is_opaque && *blend == SkBlendMode::kSrcOver) {
-        *blend = SkBlendMode::kSrc;
+    // We can strength-reduce SrcOver into Src when opaque.
+    if (is_opaque && blitter->fBlend == SkBlendMode::kSrcOver) {
+        blitter->fBlend = SkBlendMode::kSrc;
     }
 
-    if (is_constant && *blend == SkBlendMode::kSrc) {
+    // When we're drawing a constant color in Src mode, we can sometimes just memset.
+    // (The previous two optimizations help find more opportunities for this one.)
+    if (is_constant && blitter->fBlend == SkBlendMode::kSrc) {
+        // Run our color pipeline all the way through to produce what we'd memset when we can.
+        // Not all blits can memset, so we need to keep colorPipeline too.
         SkRasterPipeline p;
-        p.extend(*pipeline);
-        fDstPtr = &fMemsetColor;
-        this->append_store(&p);
+        p.extend(*colorPipeline);
+        blitter->fDstPtr = &blitter->fMemsetColor;
+        blitter->append_store(&p);
         p.run(0,1);
 
-        fCanMemsetInBlitH = true;
+        blitter->fCanMemsetInBlitH = true;
     }
+
+    return blitter;
 }
 
 void SkRasterPipelineBlitter::append_load_d(SkRasterPipeline* p) const {
@@ -282,7 +267,7 @@ void SkRasterPipelineBlitter::blitH(int x, int y, int w) {
 
     auto& p = fBlitH;
     if (p.empty()) {
-        p.extend(fShader);
+        p.extend(fColorPipeline);
         if (fBlend != SkBlendMode::kSrc) {
             this->append_load_d(&p);
             this->append_blend(&p);
@@ -296,7 +281,7 @@ void SkRasterPipelineBlitter::blitH(int x, int y, int w) {
 void SkRasterPipelineBlitter::blitAntiH(int x, int y, const SkAlpha aa[], const int16_t runs[]) {
     auto& p = fBlitAntiH;
     if (p.empty()) {
-        p.extend(fShader);
+        p.extend(fColorPipeline);
         if (fBlend == SkBlendMode::kSrcOver) {
             p.append(SkRasterPipeline::scale_1_float, &fCurrentCoverage);
             this->append_load_d(&p);
@@ -334,7 +319,7 @@ void SkRasterPipelineBlitter::blitMask(const SkMask& mask, const SkIRect& clip)
 
     if (mask.fFormat == SkMask::kA8_Format && fBlitMaskA8.empty()) {
         auto& p = fBlitMaskA8;
-        p.extend(fShader);
+        p.extend(fColorPipeline);
         if (fBlend == SkBlendMode::kSrcOver) {
             p.append(SkRasterPipeline::scale_u8, &fMaskPtr);
             this->append_load_d(&p);
@@ -350,7 +335,7 @@ void SkRasterPipelineBlitter::blitMask(const SkMask& mask, const SkIRect& clip)
 
     if (mask.fFormat == SkMask::kLCD16_Format && fBlitMaskLCD16.empty()) {
         auto& p = fBlitMaskLCD16;
-        p.extend(fShader);
+        p.extend(fColorPipeline);
         this->append_load_d(&p);
         this->append_blend(&p);
         p.append(SkRasterPipeline::lerp_565, &fMaskPtr);