Line data Source code
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 "SkRasterPipeline.h"
12 : #include "SkTemplates.h"
13 :
14 : // A debugging mode that helps prioritize porting stages to SkJumper.
15 : #if 0
16 : #include "SkOnce.h"
17 : #include <atomic>
18 :
19 : #define M(st) {0},
20 : static std::atomic<int> gMissing[] = { SK_RASTER_PIPELINE_STAGES(M) };
21 : #undef M
22 :
23 : #define M(st) #st,
24 : static const char* gNames[] = { SK_RASTER_PIPELINE_STAGES(M) };
25 : #undef M
26 :
27 : #define WHATS_NEXT
28 : #endif
29 :
30 : // We'll use __has_feature(memory_sanitizer) to detect MSAN.
31 : // SkJumper_generated.S is not compiled with MSAN, so MSAN would yell really loud.
32 : #if !defined(__has_feature)
33 : #define __has_feature(x) 0
34 : #endif
35 :
36 : // Stages expect these constants to be set to these values.
37 : // It's fine to rearrange and add new ones if you update SkJumper_constants.
38 : using K = const SkJumper_constants;
39 : static K kConstants = {
40 : {0.0f, 1.0f, 2.0f, 3.0f, 4.0f, 5.0f, 6.0f, 7.0f},
41 : };
42 :
43 : #define STAGES(M) \
44 : M(seed_shader) \
45 : M(constant_color) \
46 : M(clear) \
47 : M(srcatop) \
48 : M(dstatop) \
49 : M(srcin) \
50 : M(dstin) \
51 : M(srcout) \
52 : M(dstout) \
53 : M(srcover) \
54 : M(dstover) \
55 : M(modulate) \
56 : M(multiply) \
57 : M(plus_) \
58 : M(screen) \
59 : M(xor_) \
60 : M(darken) \
61 : M(lighten) \
62 : M(difference) \
63 : M(exclusion) \
64 : M(colorburn) \
65 : M(colordodge) \
66 : M(hardlight) \
67 : M(overlay) \
68 : M(softlight) \
69 : M(clamp_0) \
70 : M(clamp_1) \
71 : M(clamp_a) \
72 : M(set_rgb) \
73 : M(swap_rb) \
74 : M(swap) \
75 : M(move_src_dst) \
76 : M(move_dst_src) \
77 : M(premul) \
78 : M(unpremul) \
79 : M(from_srgb) \
80 : M(to_srgb) \
81 : M(from_2dot2) \
82 : M(to_2dot2) \
83 : M(rgb_to_hsl) \
84 : M(hsl_to_rgb) \
85 : M(scale_1_float) \
86 : M(scale_u8) \
87 : M(lerp_1_float) \
88 : M(lerp_u8) \
89 : M(lerp_565) \
90 : M(load_tables) \
91 : M(byte_tables) \
92 : M(byte_tables_rgb) \
93 : M(load_a8) \
94 : M(gather_a8) \
95 : M(store_a8) \
96 : M(load_g8) \
97 : M(gather_g8) \
98 : M(gather_i8) \
99 : M(load_565) \
100 : M(gather_565) \
101 : M(store_565) \
102 : M(load_4444) \
103 : M(gather_4444) \
104 : M(store_4444) \
105 : M(load_8888) \
106 : M(gather_8888) \
107 : M(store_8888) \
108 : M(load_f16) \
109 : M(gather_f16) \
110 : M(store_f16) \
111 : M(load_u16_be) \
112 : M(store_u16_be) \
113 : M(load_f32) \
114 : M(store_f32) \
115 : M(luminance_to_alpha) \
116 : M(matrix_2x3) \
117 : M(matrix_3x4) \
118 : M(matrix_4x5) \
119 : M(matrix_perspective) \
120 : M(clamp_x) \
121 : M(clamp_y) \
122 : M(repeat_x) \
123 : M(repeat_y) \
124 : M(mirror_x) \
125 : M(mirror_y) \
126 : M(save_xy) \
127 : M(accumulate) \
128 : M(bilinear_nx) M(bilinear_px) M(bilinear_ny) M(bilinear_py) \
129 : M(bicubic_n3x) M(bicubic_n1x) M(bicubic_p1x) M(bicubic_p3x) \
130 : M(bicubic_n3y) M(bicubic_n1y) M(bicubic_p1y) M(bicubic_p3y) \
131 : M(linear_gradient) \
132 : M(linear_gradient_2stops)
133 :
134 : // We can't express the real types of most stage functions portably, so we use a stand-in.
135 : // We'll only ever call start_pipeline(), which then chains into the rest for us.
136 : using StageFn = void(void);
137 :
138 : // Some platforms expect C "name" maps to asm "_name", others to "name".
139 : #if defined(__APPLE__)
140 : #define ASM(name, suffix) sk_##name##_##suffix
141 : #else
142 : #define ASM(name, suffix) _sk_##name##_##suffix
143 : #endif
144 :
145 : extern "C" {
146 :
147 : #if __has_feature(memory_sanitizer)
148 : // We'll just run portable code.
149 :
150 : #elif defined(__aarch64__)
151 : size_t ASM(start_pipeline,aarch64)(size_t, void**, K*, size_t);
152 : StageFn ASM(just_return,aarch64);
153 : #define M(st) StageFn ASM(st,aarch64);
154 : STAGES(M)
155 : #undef M
156 :
157 : #elif defined(__arm__)
158 : size_t ASM(start_pipeline,vfp4)(size_t, void**, K*, size_t);
159 : StageFn ASM(just_return,vfp4);
160 : #define M(st) StageFn ASM(st,vfp4);
161 : STAGES(M)
162 : #undef M
163 :
164 : #elif defined(__x86_64__) || defined(_M_X64)
165 : size_t ASM(start_pipeline,hsw )(size_t, void**, K*, size_t);
166 : size_t ASM(start_pipeline,avx )(size_t, void**, K*, size_t);
167 : size_t ASM(start_pipeline,sse41)(size_t, void**, K*, size_t);
168 : size_t ASM(start_pipeline,sse2 )(size_t, void**, K*, size_t);
169 :
170 : StageFn ASM(just_return,hsw),
171 : ASM(just_return,avx),
172 : ASM(just_return,sse41),
173 : ASM(just_return,sse2);
174 :
175 : #define M(st) StageFn ASM(st,hsw);
176 : STAGES(M)
177 : #undef M
178 : #define M(st) StageFn ASM(st,avx);
179 : STAGES(M)
180 : #undef M
181 : #define M(st) StageFn ASM(st,sse41);
182 : STAGES(M)
183 : #undef M
184 : #define M(st) StageFn ASM(st,sse2);
185 : STAGES(M)
186 : #undef M
187 : #endif
188 :
189 : // Portable, single-pixel stages.
190 : size_t sk_start_pipeline(size_t, void**, K*, size_t);
191 : StageFn sk_just_return;
192 : #define M(st) StageFn sk_##st;
193 : STAGES(M)
194 : #undef M
195 : }
196 :
197 : // Translate SkRasterPipeline's StockStage enum to StageFn function pointers.
198 :
199 : #if __has_feature(memory_sanitizer)
200 : // We'll just run portable code.
201 :
202 : #elif defined(__aarch64__)
203 : static StageFn* lookup_aarch64(SkRasterPipeline::StockStage st) {
204 : switch (st) {
205 : default: return nullptr;
206 : #define M(st) case SkRasterPipeline::st: return ASM(st,aarch64);
207 : STAGES(M)
208 : #undef M
209 : }
210 : }
211 :
212 : #elif defined(__arm__)
213 : static StageFn* lookup_vfp4(SkRasterPipeline::StockStage st) {
214 : switch (st) {
215 : default: return nullptr;
216 : #define M(st) case SkRasterPipeline::st: return ASM(st,vfp4);
217 : STAGES(M)
218 : #undef M
219 : }
220 : }
221 :
222 : #elif defined(__x86_64__) || defined(_M_X64)
223 170 : static StageFn* lookup_hsw(SkRasterPipeline::StockStage st) {
224 170 : switch (st) {
225 : default:
226 : #ifdef WHATS_NEXT
227 : gMissing[st]++;
228 : #endif
229 0 : return nullptr;
230 : #define M(st) case SkRasterPipeline::st: return ASM(st,hsw);
231 0 : STAGES(M)
232 : #undef M
233 : }
234 : }
235 0 : static StageFn* lookup_avx(SkRasterPipeline::StockStage st) {
236 0 : switch (st) {
237 : default:
238 : #ifdef WHATS_NEXT
239 : gMissing[st]++;
240 : #endif
241 0 : return nullptr;
242 : #define M(st) case SkRasterPipeline::st: return ASM(st,avx);
243 0 : STAGES(M)
244 : #undef M
245 : }
246 : }
247 0 : static StageFn* lookup_sse41(SkRasterPipeline::StockStage st) {
248 0 : switch (st) {
249 : default:
250 : #ifdef WHATS_NEXT
251 : gMissing[st]++;
252 : #endif
253 0 : return nullptr;
254 : #define M(st) case SkRasterPipeline::st: return ASM(st,sse41);
255 0 : STAGES(M)
256 : #undef M
257 : }
258 : }
259 0 : static StageFn* lookup_sse2(SkRasterPipeline::StockStage st) {
260 0 : switch (st) {
261 0 : default: return nullptr;
262 : #define M(st) case SkRasterPipeline::st: return ASM(st,sse2);
263 0 : STAGES(M)
264 : #undef M
265 : }
266 : }
267 : #endif
268 :
269 0 : static StageFn* lookup_portable(SkRasterPipeline::StockStage st) {
270 0 : switch (st) {
271 0 : default: return nullptr;
272 : #define M(st) case SkRasterPipeline::st: return sk_##st;
273 0 : STAGES(M)
274 : #undef M
275 : }
276 : }
277 :
278 31 : bool SkRasterPipeline::run_with_jumper(size_t x, size_t n) const {
279 : #ifdef WHATS_NEXT
280 : static SkOnce once;
281 : once([] {
282 : atexit([] {
283 : for (int i = 0; i < (int)SK_ARRAY_COUNT(gMissing); i++) {
284 : if (int n = gMissing[i].load()) {
285 : SkDebugf("%10d %s\n", n, gNames[i]);
286 : }
287 : }
288 : });
289 : });
290 : #endif
291 :
292 62 : SkAutoSTMalloc<64, void*> program(2*fStages.size() + 1);
293 31 : const size_t limit = x+n;
294 :
295 : auto build_and_run = [&](size_t min_stride,
296 : StageFn* (*lookup)(SkRasterPipeline::StockStage),
297 : StageFn* just_return,
298 155 : size_t (*start_pipeline)(size_t, void**, K*, size_t)) {
299 217 : if (x + min_stride <= limit) {
300 62 : void** ip = program.get();
301 201 : for (auto&& st : fStages) {
302 170 : auto fn = lookup(st.stage);
303 170 : if (!fn) {
304 0 : return false;
305 : }
306 170 : *ip++ = (void*)fn;
307 170 : if (st.ctx) {
308 116 : *ip++ = st.ctx;
309 : }
310 : }
311 31 : *ip = (void*)just_return;
312 :
313 93 : x = start_pipeline(x, program.get(), &kConstants, limit);
314 : }
315 155 : return true;
316 31 : };
317 :
318 : // While possible, build and run at full vector stride.
319 : #if __has_feature(memory_sanitizer)
320 : // We'll just run portable code.
321 :
322 : #elif defined(__aarch64__)
323 : if (!build_and_run(4, lookup_aarch64, ASM(just_return,aarch64), ASM(start_pipeline,aarch64))) {
324 : return false;
325 : }
326 :
327 : #elif defined(__arm__)
328 : if (1 && SkCpu::Supports(SkCpu::NEON|SkCpu::NEON_FMA|SkCpu::VFP_FP16)) {
329 : if (!build_and_run(2, lookup_vfp4, ASM(just_return,vfp4), ASM(start_pipeline,vfp4))) {
330 : return false;
331 : }
332 : }
333 :
334 : #elif defined(__x86_64__) || defined(_M_X64)
335 31 : if (1 && SkCpu::Supports(SkCpu::HSW)) {
336 31 : if (!build_and_run(1, lookup_hsw, ASM(just_return,hsw), ASM(start_pipeline,hsw))) {
337 0 : return false;
338 : }
339 : }
340 31 : if (1 && SkCpu::Supports(SkCpu::AVX)) {
341 31 : if (!build_and_run(1, lookup_avx, ASM(just_return,avx), ASM(start_pipeline,avx))) {
342 0 : return false;
343 : }
344 : }
345 31 : if (1 && SkCpu::Supports(SkCpu::SSE41)) {
346 31 : if (!build_and_run(4, lookup_sse41, ASM(just_return,sse41), ASM(start_pipeline,sse41))) {
347 0 : return false;
348 : }
349 : }
350 31 : if (1 && SkCpu::Supports(SkCpu::SSE2)) {
351 31 : if (!build_and_run(4, lookup_sse2, ASM(just_return,sse2), ASM(start_pipeline,sse2))) {
352 0 : return false;
353 : }
354 : }
355 : #endif
356 :
357 : // Finish up any leftover with portable code one pixel at a time.
358 31 : return build_and_run(1, lookup_portable, sk_just_return, sk_start_pipeline);
359 : }
|