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