SkRasterPipeline in SkArenaAlloc
[platform/upstream/libSkiaSharp.git] / src / jumper / SkJumper.cpp
1 /*
2  * Copyright 2017 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 "SkColorPriv.h"
9 #include "SkCpu.h"
10 #include "SkJumper.h"
11 #include "SkOnce.h"
12 #include "SkRasterPipeline.h"
13 #include "SkTemplates.h"
14
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
19 #endif
20
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 = {
25     {0,1,2,3,4,5,6,7},
26     {0,1,2,3,4,5,6,7},
27 };
28
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);
32
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
36 #else
37     #define ASM(name, suffix) _sk_##name##_##suffix
38 #endif
39
40 extern "C" {
41
42 #if __has_feature(memory_sanitizer)
43     // We'll just run portable code.
44
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)
50     #undef M
51
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)
57     #undef M
58
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);
64
65     StageFn ASM(just_return,hsw),
66             ASM(just_return,avx),
67             ASM(just_return,sse41),
68             ASM(just_return,sse2);
69
70     #define M(st) StageFn ASM(st,hsw);
71         SK_RASTER_PIPELINE_STAGES(M)
72     #undef M
73     #define M(st) StageFn ASM(st,avx);
74         SK_RASTER_PIPELINE_STAGES(M)
75     #undef M
76     #define M(st) StageFn ASM(st,sse41);
77         SK_RASTER_PIPELINE_STAGES(M)
78     #undef M
79     #define M(st) StageFn ASM(st,sse2);
80         SK_RASTER_PIPELINE_STAGES(M)
81     #undef M
82 #endif
83
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)
89     #undef M
90 }
91
92 #define M(st) +1
93 static const int kNumStages = SK_RASTER_PIPELINE_STAGES(M);
94 #undef M
95
96 // Engines comprise everything we need to run SkRasterPipelines.
97 struct SkJumper_Engine {
98     StageFn* stages[kNumStages];
99     int      min_stride;
100     size_t (*start_pipeline)(size_t, void**, K*, size_t);
101     StageFn* just_return;
102 };
103
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) },
108 #undef M
109     1,
110     sk_start_pipeline,
111     sk_just_return,
112 };
113 // ...and a platform-specific engine chosen on first use based on CPU features.
114 static SkJumper_Engine gPlatform = kPortable;
115 static SkOnce gChooseEngineOnce;
116
117 static SkJumper_Engine choose_engine() {
118 #if __has_feature(memory_sanitizer)
119     // We'll just run portable code.
120
121 #elif defined(__aarch64__)
122     return {
123     #define M(stage) ASM(stage, aarch64),
124         { SK_RASTER_PIPELINE_STAGES(M) },
125         4, M(start_pipeline) M(just_return)
126     #undef M
127     };
128
129 #elif defined(__arm__)
130     if (1 && SkCpu::Supports(SkCpu::NEON|SkCpu::NEON_FMA|SkCpu::VFP_FP16)) {
131         return {
132         #define M(stage) ASM(stage, vfp4),
133             { SK_RASTER_PIPELINE_STAGES(M) },
134             2, M(start_pipeline) M(just_return)
135         #undef M
136         };
137     }
138
139 #elif defined(__x86_64__) || defined(_M_X64)
140     if (1 && SkCpu::Supports(SkCpu::HSW)) {
141         return {
142         #define M(stage) ASM(stage, hsw),
143             { SK_RASTER_PIPELINE_STAGES(M) },
144             1, M(start_pipeline) M(just_return)
145         #undef M
146         };
147     }
148     if (1 && SkCpu::Supports(SkCpu::AVX)) {
149         return {
150         #define M(stage) ASM(stage, avx),
151             { SK_RASTER_PIPELINE_STAGES(M) },
152             1, M(start_pipeline) M(just_return)
153         #undef M
154         };
155     }
156     if (1 && SkCpu::Supports(SkCpu::SSE41)) {
157         return {
158         #define M(stage) ASM(stage, sse41),
159             { SK_RASTER_PIPELINE_STAGES(M) },
160             4, M(start_pipeline) M(just_return)
161         #undef M
162         };
163     }
164     if (1 && SkCpu::Supports(SkCpu::SSE2)) {
165         return {
166         #define M(stage) ASM(stage, sse2),
167             { SK_RASTER_PIPELINE_STAGES(M) },
168             4, M(start_pipeline) M(just_return)
169         #undef M
170         };
171     }
172 #endif
173     return kPortable;
174 }
175
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;
180
181     // Still going backwards, each stage's context pointer then its StageFn.
182     for (; st; st = st->prev) {
183         if (st->ctx) {
184             *--ip = st->ctx;
185         }
186         *--ip = (void*)engine.stages[st->stage];
187     }
188 }
189
190 void SkRasterPipeline::run(size_t x, size_t n) const {
191     if (this->empty()) {
192         return;
193     }
194     gChooseEngineOnce([]{ gPlatform = choose_engine(); });
195
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;
199
200     if (x + gPlatform.min_stride <= limit) {
201         BuildPipeline(fStages, gPlatform, program.get() + fSlotsNeeded);
202         x = gPlatform.start_pipeline(x, program.get(), &kConstants, limit);
203     }
204     if (x < limit) {
205         BuildPipeline(fStages, kPortable, program.get() + fSlotsNeeded);
206         kPortable.start_pipeline(x, program.get(), &kConstants, limit);
207     }
208 }
209
210 std::function<void(size_t, size_t)> SkRasterPipeline::compile() const {
211     if (this->empty()) {
212         return [](size_t, size_t) {};
213     }
214     gChooseEngineOnce([]{ gPlatform = choose_engine(); });
215
216     void** platform = fAlloc->makeArray<void*>(fSlotsNeeded);
217     BuildPipeline(fStages, gPlatform, platform + fSlotsNeeded);
218
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);
223         };
224     }
225
226     void** portable = fAlloc->makeArray<void*>(fSlotsNeeded);
227     BuildPipeline(fStages, kPortable, portable + fSlotsNeeded);
228
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);
233         }
234         if (x < limit) {
235             kPortable.start_pipeline(x, portable, &kConstants, limit);
236         }
237     };
238 }