• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright 2016 Google Inc.
3  *
4  * Use of this source code is governed by a BSD-style license that can be
5  * found in the LICENSE file.
6  */
7 
8 #include "src/core/SkRasterPipeline.h"
9 
10 #include "include/core/SkColorType.h"
11 #include "include/core/SkImageInfo.h"
12 #include "include/core/SkMatrix.h"
13 #include "include/private/base/SkTemplates.h"
14 #include "modules/skcms/skcms.h"
15 #include "src/base/SkVx.h"
16 #include "src/core/SkImageInfoPriv.h"
17 #include "src/core/SkOpts.h"
18 
19 #include <algorithm>
20 #include <cstring>
21 #include <vector>
22 
23 using namespace skia_private;
24 using Op = SkRasterPipelineOp;
25 
26 bool gForceHighPrecisionRasterPipeline;
27 
SkRasterPipeline(SkArenaAlloc * alloc)28 SkRasterPipeline::SkRasterPipeline(SkArenaAlloc* alloc) : fAlloc(alloc) {
29     this->reset();
30 }
reset()31 void SkRasterPipeline::reset() {
32     fRewindCtx = nullptr;
33     fStages    = nullptr;
34     fNumStages = 0;
35 }
36 
append(SkRasterPipelineOp op,void * ctx)37 void SkRasterPipeline::append(SkRasterPipelineOp op, void* ctx) {
38     SkASSERT(op != Op::uniform_color);            // Please use append_constant_color().
39     SkASSERT(op != Op::unbounded_uniform_color);  // Please use append_constant_color().
40     SkASSERT(op != Op::set_rgb);                  // Please use append_set_rgb().
41     SkASSERT(op != Op::unbounded_set_rgb);        // Please use append_set_rgb().
42     SkASSERT(op != Op::parametric);               // Please use append_transfer_function().
43     SkASSERT(op != Op::gamma_);                   // Please use append_transfer_function().
44     SkASSERT(op != Op::PQish);                    // Please use append_transfer_function().
45     SkASSERT(op != Op::HLGish);                   // Please use append_transfer_function().
46     SkASSERT(op != Op::HLGinvish);                // Please use append_transfer_function().
47     SkASSERT(op != Op::stack_checkpoint);         // Please use append_stack_rewind().
48     SkASSERT(op != Op::stack_rewind);             // Please use append_stack_rewind().
49     this->unchecked_append(op, ctx);
50 }
unchecked_append(SkRasterPipelineOp op,void * ctx)51 void SkRasterPipeline::unchecked_append(SkRasterPipelineOp op, void* ctx) {
52     fStages = fAlloc->make<StageList>(StageList{fStages, op, ctx});
53     fNumStages += 1;
54 }
append(SkRasterPipelineOp op,uintptr_t ctx)55 void SkRasterPipeline::append(SkRasterPipelineOp op, uintptr_t ctx) {
56     void* ptrCtx;
57     memcpy(&ptrCtx, &ctx, sizeof(ctx));
58     this->append(op, ptrCtx);
59 }
60 
extend(const SkRasterPipeline & src)61 void SkRasterPipeline::extend(const SkRasterPipeline& src) {
62     if (src.empty()) {
63         return;
64     }
65     auto stages = fAlloc->makeArrayDefault<StageList>(src.fNumStages);
66 
67     int n = src.fNumStages;
68     const StageList* st = src.fStages;
69     while (n --> 1) {
70         stages[n]      = *st;
71         stages[n].prev = &stages[n-1];
72         st = st->prev;
73     }
74     stages[0]      = *st;
75     stages[0].prev = fStages;
76 
77     fStages = &stages[src.fNumStages - 1];
78     fNumStages += src.fNumStages;
79 }
80 
GetOpName(SkRasterPipelineOp op)81 const char* SkRasterPipeline::GetOpName(SkRasterPipelineOp op) {
82     const char* name = "";
83     switch (op) {
84     #define M(x) case Op::x: name = #x; break;
85         SK_RASTER_PIPELINE_OPS_ALL(M)
86     #undef M
87     }
88     return name;
89 }
90 
dump() const91 void SkRasterPipeline::dump() const {
92     SkDebugf("SkRasterPipeline, %d stages\n", fNumStages);
93     std::vector<const char*> stages;
94     for (auto st = fStages; st; st = st->prev) {
95         stages.push_back(GetOpName(st->stage));
96     }
97     std::reverse(stages.begin(), stages.end());
98     for (const char* name : stages) {
99         SkDebugf("\t%s\n", name);
100     }
101     SkDebugf("\n");
102 }
103 
append_set_rgb(SkArenaAlloc * alloc,const float rgb[3])104 void SkRasterPipeline::append_set_rgb(SkArenaAlloc* alloc, const float rgb[3]) {
105     auto arg = alloc->makeArrayDefault<float>(3);
106     arg[0] = rgb[0];
107     arg[1] = rgb[1];
108     arg[2] = rgb[2];
109 
110     auto op = Op::unbounded_set_rgb;
111     if (0 <= rgb[0] && rgb[0] <= 1 &&
112         0 <= rgb[1] && rgb[1] <= 1 &&
113         0 <= rgb[2] && rgb[2] <= 1)
114     {
115         op = Op::set_rgb;
116     }
117 
118     this->unchecked_append(op, arg);
119 }
120 
append_constant_color(SkArenaAlloc * alloc,const float rgba[4])121 void SkRasterPipeline::append_constant_color(SkArenaAlloc* alloc, const float rgba[4]) {
122     // r,g,b might be outside [0,1], but alpha should probably always be in [0,1].
123     SkASSERT(0 <= rgba[3] && rgba[3] <= 1);
124 
125     if (rgba[0] == 0 && rgba[1] == 0 && rgba[2] == 0 && rgba[3] == 1) {
126         this->append(Op::black_color);
127     } else if (rgba[0] == 1 && rgba[1] == 1 && rgba[2] == 1 && rgba[3] == 1) {
128         this->append(Op::white_color);
129     } else {
130         auto ctx = alloc->make<SkRasterPipeline_UniformColorCtx>();
131         skvx::float4 color = skvx::float4::Load(rgba);
132         color.store(&ctx->r);
133 
134         // uniform_color requires colors in range and can go lowp,
135         // while unbounded_uniform_color supports out-of-range colors too but not lowp.
136         if (0 <= rgba[0] && rgba[0] <= rgba[3] &&
137             0 <= rgba[1] && rgba[1] <= rgba[3] &&
138             0 <= rgba[2] && rgba[2] <= rgba[3]) {
139             // To make loads more direct, we store 8-bit values in 16-bit slots.
140             color = color * 255.0f + 0.5f;
141             ctx->rgba[0] = (uint16_t)color[0];
142             ctx->rgba[1] = (uint16_t)color[1];
143             ctx->rgba[2] = (uint16_t)color[2];
144             ctx->rgba[3] = (uint16_t)color[3];
145             this->unchecked_append(Op::uniform_color, ctx);
146         } else {
147             this->unchecked_append(Op::unbounded_uniform_color, ctx);
148         }
149     }
150 }
151 
append_matrix(SkArenaAlloc * alloc,const SkMatrix & matrix)152 void SkRasterPipeline::append_matrix(SkArenaAlloc* alloc, const SkMatrix& matrix) {
153     SkMatrix::TypeMask mt = matrix.getType();
154 
155     if (mt == SkMatrix::kIdentity_Mask) {
156         return;
157     }
158     if (mt == SkMatrix::kTranslate_Mask) {
159         float* trans = alloc->makeArrayDefault<float>(2);
160         trans[0] = matrix.getTranslateX();
161         trans[1] = matrix.getTranslateY();
162         this->append(Op::matrix_translate, trans);
163     } else if ((mt | (SkMatrix::kScale_Mask | SkMatrix::kTranslate_Mask)) ==
164                      (SkMatrix::kScale_Mask | SkMatrix::kTranslate_Mask)) {
165         float* scaleTrans = alloc->makeArrayDefault<float>(4);
166         scaleTrans[0] = matrix.getScaleX();
167         scaleTrans[1] = matrix.getScaleY();
168         scaleTrans[2] = matrix.getTranslateX();
169         scaleTrans[3] = matrix.getTranslateY();
170         this->append(Op::matrix_scale_translate, scaleTrans);
171     } else {
172         float* storage = alloc->makeArrayDefault<float>(9);
173         matrix.get9(storage);
174         if (!matrix.hasPerspective()) {
175             // note: asAffine and the 2x3 stage really only need 6 entries
176             this->append(Op::matrix_2x3, storage);
177         } else {
178             this->append(Op::matrix_perspective, storage);
179         }
180     }
181 }
182 
append_load(SkColorType ct,const SkRasterPipeline_MemoryCtx * ctx)183 void SkRasterPipeline::append_load(SkColorType ct, const SkRasterPipeline_MemoryCtx* ctx) {
184     switch (ct) {
185         case kUnknown_SkColorType: SkASSERT(false); break;
186 
187         case kAlpha_8_SkColorType:           this->append(Op::load_a8,      ctx); break;
188         case kA16_unorm_SkColorType:         this->append(Op::load_a16,     ctx); break;
189         case kA16_float_SkColorType:         this->append(Op::load_af16,    ctx); break;
190         case kRGB_565_SkColorType:           this->append(Op::load_565,     ctx); break;
191         case kARGB_4444_SkColorType:         this->append(Op::load_4444,    ctx); break;
192         case kR8G8_unorm_SkColorType:        this->append(Op::load_rg88,    ctx); break;
193         case kR16G16_unorm_SkColorType:      this->append(Op::load_rg1616,  ctx); break;
194         case kR16G16_float_SkColorType:      this->append(Op::load_rgf16,   ctx); break;
195         case kRGBA_8888_SkColorType:         this->append(Op::load_8888,    ctx); break;
196         case kRGBA_1010102_SkColorType:      this->append(Op::load_1010102, ctx); break;
197         case kR16G16B16A16_unorm_SkColorType:this->append(Op::load_16161616,ctx); break;
198         case kRGBA_F16Norm_SkColorType:
199         case kRGBA_F16_SkColorType:          this->append(Op::load_f16,     ctx); break;
200         case kRGBA_F32_SkColorType:          this->append(Op::load_f32,     ctx); break;
201 
202         case kGray_8_SkColorType:            this->append(Op::load_a8, ctx);
203                                              this->append(Op::alpha_to_gray);
204                                              break;
205 
206         case kR8_unorm_SkColorType:          this->append(Op::load_a8, ctx);
207                                              this->append(Op::alpha_to_red);
208                                              break;
209 
210         case kRGB_888x_SkColorType:          this->append(Op::load_8888, ctx);
211                                              this->append(Op::force_opaque);
212                                              break;
213 
214         case kBGRA_1010102_SkColorType:      this->append(Op::load_1010102, ctx);
215                                              this->append(Op::swap_rb);
216                                              break;
217 
218         case kRGB_101010x_SkColorType:       this->append(Op::load_1010102, ctx);
219                                              this->append(Op::force_opaque);
220                                              break;
221 
222         case kBGR_101010x_SkColorType:       this->append(Op::load_1010102, ctx);
223                                              this->append(Op::force_opaque);
224                                              this->append(Op::swap_rb);
225                                              break;
226 
227         case kBGR_101010x_XR_SkColorType:    this->append(Op::load_1010102_xr, ctx);
228                                              this->append(Op::force_opaque);
229                                              this->append(Op::swap_rb);
230                                              break;
231 
232         case kBGRA_8888_SkColorType:         this->append(Op::load_8888, ctx);
233                                              this->append(Op::swap_rb);
234                                              break;
235 
236         case kSRGBA_8888_SkColorType:
237             this->append(Op::load_8888, ctx);
238             this->append_transfer_function(*skcms_sRGB_TransferFunction());
239             break;
240     }
241 }
242 
append_load_dst(SkColorType ct,const SkRasterPipeline_MemoryCtx * ctx)243 void SkRasterPipeline::append_load_dst(SkColorType ct, const SkRasterPipeline_MemoryCtx* ctx) {
244     switch (ct) {
245         case kUnknown_SkColorType: SkASSERT(false); break;
246 
247         case kAlpha_8_SkColorType:            this->append(Op::load_a8_dst,      ctx); break;
248         case kA16_unorm_SkColorType:          this->append(Op::load_a16_dst,     ctx); break;
249         case kA16_float_SkColorType:          this->append(Op::load_af16_dst,    ctx); break;
250         case kRGB_565_SkColorType:            this->append(Op::load_565_dst,     ctx); break;
251         case kARGB_4444_SkColorType:          this->append(Op::load_4444_dst,    ctx); break;
252         case kR8G8_unorm_SkColorType:         this->append(Op::load_rg88_dst,    ctx); break;
253         case kR16G16_unorm_SkColorType:       this->append(Op::load_rg1616_dst,  ctx); break;
254         case kR16G16_float_SkColorType:       this->append(Op::load_rgf16_dst,   ctx); break;
255         case kRGBA_8888_SkColorType:          this->append(Op::load_8888_dst,    ctx); break;
256         case kRGBA_1010102_SkColorType:       this->append(Op::load_1010102_dst, ctx); break;
257         case kR16G16B16A16_unorm_SkColorType: this->append(Op::load_16161616_dst,ctx); break;
258         case kRGBA_F16Norm_SkColorType:
259         case kRGBA_F16_SkColorType:           this->append(Op::load_f16_dst,     ctx); break;
260         case kRGBA_F32_SkColorType:           this->append(Op::load_f32_dst,     ctx); break;
261 
262         case kGray_8_SkColorType:             this->append(Op::load_a8_dst, ctx);
263                                               this->append(Op::alpha_to_gray_dst);
264                                               break;
265 
266         case kR8_unorm_SkColorType:           this->append(Op::load_a8_dst, ctx);
267                                               this->append(Op::alpha_to_red_dst);
268                                               break;
269 
270         case kRGB_888x_SkColorType:           this->append(Op::load_8888_dst, ctx);
271                                               this->append(Op::force_opaque_dst);
272                                               break;
273 
274         case kBGRA_1010102_SkColorType:       this->append(Op::load_1010102_dst, ctx);
275                                               this->append(Op::swap_rb_dst);
276                                               break;
277 
278         case kRGB_101010x_SkColorType:        this->append(Op::load_1010102_dst, ctx);
279                                               this->append(Op::force_opaque_dst);
280                                               break;
281 
282         case kBGR_101010x_SkColorType:        this->append(Op::load_1010102_dst, ctx);
283                                               this->append(Op::force_opaque_dst);
284                                               this->append(Op::swap_rb_dst);
285                                               break;
286 
287         case kBGR_101010x_XR_SkColorType:     this->append(Op::load_1010102_xr_dst, ctx);
288                                               this->append(Op::force_opaque_dst);
289                                               this->append(Op::swap_rb_dst);
290                                               break;
291 
292         case kBGRA_8888_SkColorType:          this->append(Op::load_8888_dst, ctx);
293                                               this->append(Op::swap_rb_dst);
294                                               break;
295 
296         case kSRGBA_8888_SkColorType:
297             // TODO: We could remove the double-swap if we had _dst versions of all the TF stages
298             this->append(Op::load_8888_dst, ctx);
299             this->append(Op::swap_src_dst);
300             this->append_transfer_function(*skcms_sRGB_TransferFunction());
301             this->append(Op::swap_src_dst);
302             break;
303     }
304 }
305 
append_store(SkColorType ct,const SkRasterPipeline_MemoryCtx * ctx)306 void SkRasterPipeline::append_store(SkColorType ct, const SkRasterPipeline_MemoryCtx* ctx) {
307     switch (ct) {
308         case kUnknown_SkColorType: SkASSERT(false); break;
309 
310         case kAlpha_8_SkColorType:            this->append(Op::store_a8,      ctx); break;
311         case kR8_unorm_SkColorType:           this->append(Op::store_r8,      ctx); break;
312         case kA16_unorm_SkColorType:          this->append(Op::store_a16,     ctx); break;
313         case kA16_float_SkColorType:          this->append(Op::store_af16,    ctx); break;
314         case kRGB_565_SkColorType:            this->append(Op::store_565,     ctx); break;
315         case kARGB_4444_SkColorType:          this->append(Op::store_4444,    ctx); break;
316         case kR8G8_unorm_SkColorType:         this->append(Op::store_rg88,    ctx); break;
317         case kR16G16_unorm_SkColorType:       this->append(Op::store_rg1616,  ctx); break;
318         case kR16G16_float_SkColorType:       this->append(Op::store_rgf16,   ctx); break;
319         case kRGBA_8888_SkColorType:          this->append(Op::store_8888,    ctx); break;
320         case kRGBA_1010102_SkColorType:       this->append(Op::store_1010102, ctx); break;
321         case kR16G16B16A16_unorm_SkColorType: this->append(Op::store_16161616,ctx); break;
322         case kRGBA_F16Norm_SkColorType:
323         case kRGBA_F16_SkColorType:           this->append(Op::store_f16,     ctx); break;
324         case kRGBA_F32_SkColorType:           this->append(Op::store_f32,     ctx); break;
325 
326         case kRGB_888x_SkColorType:           this->append(Op::force_opaque);
327                                               this->append(Op::store_8888, ctx);
328                                               break;
329 
330         case kBGRA_1010102_SkColorType:       this->append(Op::swap_rb);
331                                               this->append(Op::store_1010102, ctx);
332                                               break;
333 
334         case kRGB_101010x_SkColorType:        this->append(Op::force_opaque);
335                                               this->append(Op::store_1010102, ctx);
336                                               break;
337 
338         case kBGR_101010x_SkColorType:        this->append(Op::force_opaque);
339                                               this->append(Op::swap_rb);
340                                               this->append(Op::store_1010102, ctx);
341                                               break;
342 
343         case kBGR_101010x_XR_SkColorType:     this->append(Op::force_opaque);
344                                               this->append(Op::swap_rb);
345                                               this->append(Op::store_1010102_xr, ctx);
346                                               break;
347 
348         case kGray_8_SkColorType:             this->append(Op::bt709_luminance_or_luma_to_alpha);
349                                               this->append(Op::store_a8, ctx);
350                                               break;
351 
352         case kBGRA_8888_SkColorType:          this->append(Op::swap_rb);
353                                               this->append(Op::store_8888, ctx);
354                                               break;
355 
356         case kSRGBA_8888_SkColorType:
357             this->append_transfer_function(*skcms_sRGB_Inverse_TransferFunction());
358             this->append(Op::store_8888, ctx);
359             break;
360     }
361 }
362 
append_transfer_function(const skcms_TransferFunction & tf)363 void SkRasterPipeline::append_transfer_function(const skcms_TransferFunction& tf) {
364     void* ctx = const_cast<void*>(static_cast<const void*>(&tf));
365     switch (skcms_TransferFunction_getType(&tf)) {
366         case skcms_TFType_Invalid: SkASSERT(false); break;
367 
368         case skcms_TFType_sRGBish:
369             if (tf.a == 1 && tf.b == 0 && tf.c == 0 && tf.d == 0 && tf.e == 0 && tf.f == 0) {
370                 this->unchecked_append(Op::gamma_, ctx);
371             } else {
372                 this->unchecked_append(Op::parametric, ctx);
373             }
374             break;
375         case skcms_TFType_PQish:     this->unchecked_append(Op::PQish,     ctx); break;
376         case skcms_TFType_HLGish:    this->unchecked_append(Op::HLGish,    ctx); break;
377         case skcms_TFType_HLGinvish: this->unchecked_append(Op::HLGinvish, ctx); break;
378     }
379 }
380 
381 // GPUs clamp all color channels to the limits of the format just before the blend step. To match
382 // that auto-clamp, the RP blitter uses this helper immediately before appending blending stages.
append_clamp_if_normalized(const SkImageInfo & info)383 void SkRasterPipeline::append_clamp_if_normalized(const SkImageInfo& info) {
384     if (SkColorTypeIsNormalized(info.colorType())) {
385         this->unchecked_append(Op::clamp_01, nullptr);
386     }
387 }
388 
append_stack_rewind()389 void SkRasterPipeline::append_stack_rewind() {
390     if (!fRewindCtx) {
391         fRewindCtx = fAlloc->make<SkRasterPipeline_RewindCtx>();
392     }
393     this->unchecked_append(Op::stack_rewind, fRewindCtx);
394 }
395 
prepend_to_pipeline(SkRasterPipelineStage * & ip,SkOpts::StageFn stageFn,void * ctx)396 static void prepend_to_pipeline(SkRasterPipelineStage*& ip, SkOpts::StageFn stageFn, void* ctx) {
397     --ip;
398     ip->fn = stageFn;
399     ip->ctx = ctx;
400 }
401 
build_lowp_pipeline(SkRasterPipelineStage * ip) const402 bool SkRasterPipeline::build_lowp_pipeline(SkRasterPipelineStage* ip) const {
403     if (gForceHighPrecisionRasterPipeline || fRewindCtx) {
404         return false;
405     }
406     // Stages are stored backwards in fStages; to compensate, we assemble the pipeline in reverse
407     // here, back to front.
408     prepend_to_pipeline(ip, SkOpts::just_return_lowp, /*ctx=*/nullptr);
409     for (const StageList* st = fStages; st; st = st->prev) {
410         int opIndex = (int)st->stage;
411         if (opIndex >= kNumRasterPipelineLowpOps || !SkOpts::ops_lowp[opIndex]) {
412             // This program contains a stage that doesn't exist in lowp.
413             return false;
414         }
415         prepend_to_pipeline(ip, SkOpts::ops_lowp[opIndex], st->ctx);
416     }
417     return true;
418 }
419 
build_highp_pipeline(SkRasterPipelineStage * ip) const420 void SkRasterPipeline::build_highp_pipeline(SkRasterPipelineStage* ip) const {
421     // We assemble the pipeline in reverse, since the stage list is stored backwards.
422     prepend_to_pipeline(ip, SkOpts::just_return_highp, /*ctx=*/nullptr);
423     for (const StageList* st = fStages; st; st = st->prev) {
424         int opIndex = (int)st->stage;
425         prepend_to_pipeline(ip, SkOpts::ops_highp[opIndex], st->ctx);
426     }
427 
428     // stack_checkpoint and stack_rewind are only implemented in highp. We only need these stages
429     // when generating long (or looping) pipelines from SkSL. The other stages used by the SkSL
430     // Raster Pipeline generator will only have highp implementations, because we can't execute SkSL
431     // code without floating point.
432     if (fRewindCtx) {
433         const int rewindIndex = (int)Op::stack_checkpoint;
434         prepend_to_pipeline(ip, SkOpts::ops_highp[rewindIndex], fRewindCtx);
435     }
436 }
437 
build_pipeline(SkRasterPipelineStage * ip) const438 SkRasterPipeline::StartPipelineFn SkRasterPipeline::build_pipeline(
439         SkRasterPipelineStage* ip) const {
440     // We try to build a lowp pipeline first; if that fails, we fall back to a highp float pipeline.
441     if (this->build_lowp_pipeline(ip)) {
442         return SkOpts::start_pipeline_lowp;
443     }
444 
445     this->build_highp_pipeline(ip);
446     return SkOpts::start_pipeline_highp;
447 }
448 
stages_needed() const449 int SkRasterPipeline::stages_needed() const {
450     // Add 1 to budget for a `just_return` stage at the end.
451     int stages = fNumStages + 1;
452 
453     // If we have any stack_rewind stages, we will need to inject a stack_checkpoint stage.
454     if (fRewindCtx) {
455         stages += 1;
456     }
457     return stages;
458 }
459 
run(size_t x,size_t y,size_t w,size_t h) const460 void SkRasterPipeline::run(size_t x, size_t y, size_t w, size_t h) const {
461     if (this->empty()) {
462         return;
463     }
464 
465     int stagesNeeded = this->stages_needed();
466 
467     // Best to not use fAlloc here... we can't bound how often run() will be called.
468     AutoSTMalloc<32, SkRasterPipelineStage> program(stagesNeeded);
469 
470     auto start_pipeline = this->build_pipeline(program.get() + stagesNeeded);
471     start_pipeline(x,y,x+w,y+h, program.get());
472 }
473 
compile() const474 std::function<void(size_t, size_t, size_t, size_t)> SkRasterPipeline::compile() const {
475     if (this->empty()) {
476         return [](size_t, size_t, size_t, size_t) {};
477     }
478 
479     int stagesNeeded = this->stages_needed();
480 
481     SkRasterPipelineStage* program = fAlloc->makeArray<SkRasterPipelineStage>(stagesNeeded);
482 
483     auto start_pipeline = this->build_pipeline(program + stagesNeeded);
484     return [=](size_t x, size_t y, size_t w, size_t h) {
485         start_pipeline(x,y,x+w,y+h, program);
486     };
487 }
488