2 * Copyright 2017 Google Inc.
4 * Use of this source code is governed by a BSD-style license that can be
5 * found in the LICENSE file.
8 #include "SkColorPriv.h"
12 #include "SkRasterPipeline.h"
13 #include "SkTemplates.h"
15 // We'll use __has_feature(memory_sanitizer) to detect MSAN.
16 // SkJumper_generated.S is not compiled with MSAN, so MSAN would yell really loud.
17 #if !defined(__has_feature)
18 #define __has_feature(x) 0
21 // Stages expect these constants to be set to these values.
22 // It's fine to rearrange and add new ones if you update SkJumper_constants.
23 using K = const SkJumper_constants;
24 static K kConstants = {
29 // We can't express the real types of most stage functions portably, so we use a stand-in.
30 // We'll only ever call start_pipeline(), which then chains into the rest for us.
31 using StageFn = void(void);
33 // Some platforms expect C "name" maps to asm "_name", others to "name".
34 #if defined(__APPLE__)
35 #define ASM(name, suffix) sk_##name##_##suffix
37 #define ASM(name, suffix) _sk_##name##_##suffix
42 #if __has_feature(memory_sanitizer)
43 // We'll just run portable code.
45 #elif defined(__aarch64__)
46 size_t ASM(start_pipeline,aarch64)(size_t, void**, K*, size_t);
47 StageFn ASM(just_return,aarch64);
48 #define M(st) StageFn ASM(st,aarch64);
49 SK_RASTER_PIPELINE_STAGES(M)
52 #elif defined(__arm__)
53 size_t ASM(start_pipeline,vfp4)(size_t, void**, K*, size_t);
54 StageFn ASM(just_return,vfp4);
55 #define M(st) StageFn ASM(st,vfp4);
56 SK_RASTER_PIPELINE_STAGES(M)
59 #elif defined(__x86_64__) || defined(_M_X64)
60 size_t ASM(start_pipeline,hsw )(size_t, void**, K*, size_t);
61 size_t ASM(start_pipeline,avx )(size_t, void**, K*, size_t);
62 size_t ASM(start_pipeline,sse41)(size_t, void**, K*, size_t);
63 size_t ASM(start_pipeline,sse2 )(size_t, void**, K*, size_t);
65 StageFn ASM(just_return,hsw),
67 ASM(just_return,sse41),
68 ASM(just_return,sse2);
70 #define M(st) StageFn ASM(st,hsw);
71 SK_RASTER_PIPELINE_STAGES(M)
73 #define M(st) StageFn ASM(st,avx);
74 SK_RASTER_PIPELINE_STAGES(M)
76 #define M(st) StageFn ASM(st,sse41);
77 SK_RASTER_PIPELINE_STAGES(M)
79 #define M(st) StageFn ASM(st,sse2);
80 SK_RASTER_PIPELINE_STAGES(M)
84 // Portable, single-pixel stages.
85 size_t sk_start_pipeline(size_t, void**, K*, size_t);
86 StageFn sk_just_return;
87 #define M(st) StageFn sk_##st;
88 SK_RASTER_PIPELINE_STAGES(M)
93 static const int kNumStages = SK_RASTER_PIPELINE_STAGES(M);
96 // Engines comprise everything we need to run SkRasterPipelines.
97 struct SkJumper_Engine {
98 StageFn* stages[kNumStages];
100 size_t (*start_pipeline)(size_t, void**, K*, size_t);
101 StageFn* just_return;
104 // We'll always have two engines. A portable engine with guaranteed min_stride = 1...
105 static const SkJumper_Engine kPortable = {
106 #define M(stage) sk_##stage,
107 { SK_RASTER_PIPELINE_STAGES(M) },
113 // ...and a platform-specific engine chosen on first use based on CPU features.
114 static SkJumper_Engine gPlatform = kPortable;
115 static SkOnce gChooseEngineOnce;
117 static SkJumper_Engine choose_engine() {
118 #if __has_feature(memory_sanitizer)
119 // We'll just run portable code.
121 #elif defined(__aarch64__)
123 #define M(stage) ASM(stage, aarch64),
124 { SK_RASTER_PIPELINE_STAGES(M) },
125 4, M(start_pipeline) M(just_return)
129 #elif defined(__arm__)
130 if (1 && SkCpu::Supports(SkCpu::NEON|SkCpu::NEON_FMA|SkCpu::VFP_FP16)) {
132 #define M(stage) ASM(stage, vfp4),
133 { SK_RASTER_PIPELINE_STAGES(M) },
134 2, M(start_pipeline) M(just_return)
139 #elif defined(__x86_64__) || defined(_M_X64)
140 if (1 && SkCpu::Supports(SkCpu::HSW)) {
142 #define M(stage) ASM(stage, hsw),
143 { SK_RASTER_PIPELINE_STAGES(M) },
144 1, M(start_pipeline) M(just_return)
148 if (1 && SkCpu::Supports(SkCpu::AVX)) {
150 #define M(stage) ASM(stage, avx),
151 { SK_RASTER_PIPELINE_STAGES(M) },
152 1, M(start_pipeline) M(just_return)
156 if (1 && SkCpu::Supports(SkCpu::SSE41)) {
158 #define M(stage) ASM(stage, sse41),
159 { SK_RASTER_PIPELINE_STAGES(M) },
160 4, M(start_pipeline) M(just_return)
164 if (1 && SkCpu::Supports(SkCpu::SSE2)) {
166 #define M(stage) ASM(stage, sse2),
167 { SK_RASTER_PIPELINE_STAGES(M) },
168 4, M(start_pipeline) M(just_return)
176 void SkRasterPipeline::BuildPipeline(const StageList* st,
177 const SkJumper_Engine& engine, void** ip) {
178 // We're building the pipeline backwards, so we start with the final stage just_return.
179 *--ip = (void*)engine.just_return;
181 // Still going backwards, each stage's context pointer then its StageFn.
182 for (; st; st = st->prev) {
186 *--ip = (void*)engine.stages[st->stage];
190 void SkRasterPipeline::run(size_t x, size_t n) const {
194 gChooseEngineOnce([]{ gPlatform = choose_engine(); });
196 // Best to not use fAlloc here... we can't bound how often run() will be called.
197 SkAutoSTMalloc<64, void*> program(fSlotsNeeded);
198 const size_t limit = x+n;
200 if (x + gPlatform.min_stride <= limit) {
201 BuildPipeline(fStages, gPlatform, program.get() + fSlotsNeeded);
202 x = gPlatform.start_pipeline(x, program.get(), &kConstants, limit);
205 BuildPipeline(fStages, kPortable, program.get() + fSlotsNeeded);
206 kPortable.start_pipeline(x, program.get(), &kConstants, limit);
210 std::function<void(size_t, size_t)> SkRasterPipeline::compile() const {
212 return [](size_t, size_t) {};
214 gChooseEngineOnce([]{ gPlatform = choose_engine(); });
216 void** platform = fAlloc->makeArray<void*>(fSlotsNeeded);
217 BuildPipeline(fStages, gPlatform, platform + fSlotsNeeded);
219 if (gPlatform.min_stride == 1) {
220 return [=](size_t x, size_t n) {
221 const size_t limit = x+n;
222 gPlatform.start_pipeline(x, platform, &kConstants, limit);
226 void** portable = fAlloc->makeArray<void*>(fSlotsNeeded);
227 BuildPipeline(fStages, kPortable, portable + fSlotsNeeded);
229 return [=](size_t x, size_t n) {
230 const size_t limit = x+n;
231 if (x + gPlatform.min_stride <= limit) {
232 x = gPlatform.start_pipeline(x, platform, &kConstants, limit);
235 kPortable.start_pipeline(x, portable, &kConstants, limit);