skrpb: evaluate color filters for constant shaders once.
authormtklein <mtklein@chromium.org>
Fri, 4 Nov 2016 20:20:07 +0000 (13:20 -0700)
committerCommit bot <commit-bot@chromium.org>
Fri, 4 Nov 2016 20:20:07 +0000 (13:20 -0700)
The simplest thing to do here is just run shader+color filter pipeline at
construction time to create a new constant color shader (replacing the paint
color).

This reduces a pipeline like:
  - constant_color  (paint color)
  - matrix_4x5
  - clamp_a
  - load_d_foo, xfermode, lerp, store_foo
to
  - constant_color  (paint color -> matrix_4x5 -> clamp_a)
  - load_d_foo, xfermode, lerp, store_foo

To implement this all, we add a new store_f32 stage that writes SkPM4f, and
finally get around to implementing Sk8f::Store4() (store while reinterlacing).
Sk4f::Store4() already exists for both SSE and NEON.

Next step: reduce simple constant_color -> store pipelines (src mode, full
coverage) into non-pipeline memsets.
GOLD_TRYBOT_URL= https://gold.skia.org/search?issue=2480823002

Review-Url: https://codereview.chromium.org/2480823002

src/core/SkRasterPipeline.h
src/core/SkRasterPipelineBlitter.cpp
src/opts/SkNx_sse.h
src/opts/SkRasterPipeline_opts.h

index 0a3cddb..bf886cf 100644 (file)
@@ -56,7 +56,7 @@
 
 #define SK_RASTER_PIPELINE_STAGES(M)                             \
     M(swap_src_dst) M(clamp_0) M(clamp_a) M(unpremul) M(premul)  \
-    M(constant_color)                                            \
+    M(constant_color) M(store_f32)                               \
     M(load_s_565)  M(load_d_565)  M(store_565)                   \
     M(load_s_srgb) M(load_d_srgb) M(store_srgb)                  \
     M(load_s_f16)  M(load_d_f16)  M(store_f16)                   \
index 31c188e..8f9922d 100644 (file)
@@ -20,12 +20,8 @@ class SkRasterPipelineBlitter : public SkBlitter {
 public:
     static SkBlitter* Create(const SkPixmap&, const SkPaint&, SkTBlitterAllocator*);
 
-    SkRasterPipelineBlitter(SkPixmap dst,
-                            SkRasterPipeline shader,
-                            SkBlendMode blend,
-                            SkPM4f paintColor)
+    SkRasterPipelineBlitter(SkPixmap dst, SkBlendMode blend, SkPM4f paintColor)
         : fDst(dst)
-        , fShader(shader)
         , fBlend(blend)
         , fPaintColor(paintColor)
     {}
@@ -45,9 +41,9 @@ private:
     void maybe_clamp  (SkRasterPipeline*) const;
 
     SkPixmap         fDst;
-    SkRasterPipeline fShader;
     SkBlendMode      fBlend;
     SkPM4f           fPaintColor;
+    SkRasterPipeline fShader;
 
     // These functions are compiled lazily when first used.
     std::function<void(size_t, size_t)> fBlitH         = nullptr,
@@ -84,52 +80,69 @@ static bool append_effect_stages(const Effect* effect, SkRasterPipeline* pipelin
     return !effect || effect->appendStages(pipeline);
 }
 
+static SkPM4f paint_color(const SkPixmap& dst, const SkPaint& paint) {
+    auto paintColor = paint.getColor();
+    SkColor4f color;
+    if (dst.info().colorSpace()) {
+        color = SkColor4f::FromColor(paintColor);
+        // TODO: transform from sRGB to dst gamut.
+    } else {
+        swizzle_rb(SkNx_cast<float>(Sk4b::Load(&paintColor)) * (1/255.0f)).store(&color);
+    }
+    return color.premul();
+}
 
 SkBlitter* SkRasterPipelineBlitter::Create(const SkPixmap& dst,
                                            const SkPaint& paint,
                                            SkTBlitterAllocator* alloc) {
-    if (!supported(dst.info())) {
+    auto blitter = alloc->createT<SkRasterPipelineBlitter>(dst,
+                                                           paint.getBlendMode(),
+                                                           paint_color(dst, paint));
+    SkBlendMode*      blend      = &blitter->fBlend;
+    SkPM4f*           paintColor = &blitter->fPaintColor;
+    SkRasterPipeline* pipeline   = &blitter->fShader;
+
+    SkShader*      shader      = paint.getShader();
+    SkColorFilter* colorFilter = paint.getColorFilter();
+
+    // TODO: all temporary
+    if (!supported(dst.info()) || shader || !SkBlendMode_AppendStages(*blend)) {
+        alloc->freeLast();
         return nullptr;
     }
-    if (paint.getShader()) {
-        return nullptr;  // TODO: need to work out how shaders and their contexts work
-    }
-    SkBlendMode blend = paint.getBlendMode();
-    if (!SkBlendMode_AppendStages(blend)) {
-        return nullptr;  // TODO
-    }
 
-    uint32_t paintColor = paint.getColor();
-    bool shaderIsOpaque = (paintColor >> 24) == 0xff;
-
-    SkRasterPipeline shader, colorFilter;
-    if (auto s = paint.getShader()) {
-        shaderIsOpaque = s->isOpaque();
+    bool is_opaque, is_constant;
+    if (shader) {
+        is_opaque   = shader->isOpaque();
+        is_constant = false;  // TODO: shader->isConstant()
+        // TODO: append shader stages, of course!
+    } else {
+        is_opaque   = paintColor->a() == 1.0f;
+        is_constant = true;
+        pipeline->append(SkRasterPipeline::constant_color, paintColor);
     }
-    if (auto cf = paint.getColorFilter()) {
-        if (!cf->appendStages(&colorFilter, shaderIsOpaque)) {
+
+    if (colorFilter) {
+        if (!colorFilter->appendStages(pipeline, is_opaque)) {
+            alloc->freeLast();
             return nullptr;
         }
-        shaderIsOpaque = shaderIsOpaque && (cf->getFlags() & SkColorFilter::kAlphaUnchanged_Flag);
+        is_opaque = is_opaque && (colorFilter->getFlags() & SkColorFilter::kAlphaUnchanged_Flag);
     }
 
-    if (shaderIsOpaque && blend == SkBlendMode::kSrcOver) {
-        blend = SkBlendMode::kSrc;
-    }
+    if (is_constant) {
+        pipeline->append(SkRasterPipeline::store_f32, &paintColor);
+        pipeline->compile()(0,1);
 
-    SkColor4f color;
-    if (dst.info().colorSpace()) {
-        color = SkColor4f::FromColor(paintColor);
-    } else {
-        swizzle_rb(SkNx_cast<float>(Sk4b::Load(&paintColor)) * (1/255.0f)).store(&color);
-    }
+        *pipeline = SkRasterPipeline();
+        pipeline->append(SkRasterPipeline::constant_color, paintColor);
 
-    auto blitter = alloc->createT<SkRasterPipelineBlitter>(dst, shader, blend, color.premul());
+        is_opaque = paintColor->a() == 1.0f;
+    }
 
-    if (!paint.getShader()) {
-        blitter->fShader.append(SkRasterPipeline::constant_color, &blitter->fPaintColor);
+    if (is_opaque && *blend == SkBlendMode::kSrcOver) {
+        *blend = SkBlendMode::kSrc;
     }
-    blitter->fShader.extend(colorFilter);
 
     return blitter;
 }
index a459411..a4783c6 100644 (file)
@@ -544,6 +544,14 @@ public:
         __m256i fVec;
     };
 
+    // _mm256_unpack{lo,hi}_pd() auto-casting to and from __m256d.
+    AI static __m256 unpacklo_pd(__m256 x, __m256 y) {
+        return _mm256_castpd_ps(_mm256_unpacklo_pd(_mm256_castps_pd(x), _mm256_castps_pd(y)));
+    }
+    AI static __m256 unpackhi_pd(__m256 x, __m256 y) {
+        return _mm256_castpd_ps(_mm256_unpackhi_pd(_mm256_castps_pd(x), _mm256_castps_pd(y)));
+    }
+
     template <>
     class SkNx<8, float> {
     public:
@@ -560,6 +568,29 @@ public:
         AI static SkNx Load(const void* ptr) { return _mm256_loadu_ps((const float*)ptr); }
         AI void store(void* ptr) const { _mm256_storeu_ps((float*)ptr, fVec); }
 
+        AI static void Store4(void* ptr,
+                              const SkNx& r, const SkNx& g, const SkNx& b, const SkNx& a) {
+            __m256 rg0145 = _mm256_unpacklo_ps(r.fVec, g.fVec),  // r0 g0 r1 g1 | r4 g4 r5 g5
+                   rg2367 = _mm256_unpackhi_ps(r.fVec, g.fVec),  // r2 ...      | r6 ...
+                   ba0145 = _mm256_unpacklo_ps(b.fVec, a.fVec),  // b0 a0 b1 a1 | b4 a4 b5 a5
+                   ba2367 = _mm256_unpackhi_ps(b.fVec, a.fVec);  // b2 ...      | b6 ...
+
+            __m256 _04 = unpacklo_pd(rg0145, ba0145),  // r0 g0 b0 a0 | r4 g4 b4 a4
+                   _15 = unpackhi_pd(rg0145, ba0145),  // r1 ...      | r5 ...
+                   _26 = unpacklo_pd(rg2367, ba2367),  // r2 ...      | r6 ...
+                   _37 = unpackhi_pd(rg2367, ba2367);  // r3 ...      | r7 ...
+
+            __m256 _01 = _mm256_permute2f128_ps(_04, _15, 16),  // 16 == 010 000 == lo, lo
+                   _23 = _mm256_permute2f128_ps(_26, _37, 16),
+                   _45 = _mm256_permute2f128_ps(_04, _15, 25),  // 25 == 011 001 == hi, hi
+                   _67 = _mm256_permute2f128_ps(_26, _37, 25);
+
+            _mm256_storeu_ps((float*)ptr + 0*8, _01);
+            _mm256_storeu_ps((float*)ptr + 1*8, _23);
+            _mm256_storeu_ps((float*)ptr + 2*8, _45);
+            _mm256_storeu_ps((float*)ptr + 3*8, _67);
+        }
+
         AI SkNx operator+(const SkNx& o) const { return _mm256_add_ps(fVec, o.fVec); }
         AI SkNx operator-(const SkNx& o) const { return _mm256_sub_ps(fVec, o.fVec); }
         AI SkNx operator*(const SkNx& o) const { return _mm256_mul_ps(fVec, o.fVec); }
index bd42632..155558e 100644 (file)
@@ -23,9 +23,9 @@ namespace {
     static constexpr int N = 4;
 #endif
 
-using SkNf = SkNx<N, float>;
-using SkNi = SkNx<N, int>;
-using SkNh = SkNx<N, uint16_t>;
+    using SkNf = SkNx<N, float>;
+    using SkNi = SkNx<N, int>;
+    using SkNh = SkNx<N, uint16_t>;
 
     struct BodyStage;
     struct TailStage;
@@ -379,6 +379,24 @@ STAGE(store_f16, false) {
     }
 }
 
+STAGE(store_f32, false) {
+    auto ptr = *(SkPM4f**)ctx + x;
+
+    SkPM4f buf[8];
+    SkNf::Store4(kIsTail ? buf : ptr, r,g,b,a);
+    if (kIsTail) {
+        switch (tail & (N-1)) {
+            case 7: ptr[6] = buf[6];
+            case 6: ptr[5] = buf[5];
+            case 5: ptr[4] = buf[4];
+            case 4: ptr[3] = buf[3];
+            case 3: ptr[2] = buf[2];
+            case 2: ptr[1] = buf[1];
+        }
+        ptr[0] = buf[0];
+    }
+}
+
 
 // Load 8-bit SkPMColor-order sRGB.
 STAGE(load_d_srgb, true) {