/src/FreeRDP/libfreerdp/primitives/primitives.c
Line | Count | Source (jump to first uncovered line) |
1 | | /* primitives.c |
2 | | * This code queries processor features and calls the init/deinit routines. |
3 | | * vi:ts=4 sw=4 |
4 | | * |
5 | | * Copyright 2011 Martin Fleisz <martin.fleisz@thincast.com> |
6 | | * (c) Copyright 2012 Hewlett-Packard Development Company, L.P. |
7 | | * Copyright 2019 David Fort <contact@hardening-consulting.com> |
8 | | * |
9 | | * Licensed under the Apache License, Version 2.0 (the "License"); you may |
10 | | * not use this file except in compliance with the License. You may obtain |
11 | | * a copy of the License at http://www.apache.org/licenses/LICENSE-2.0. |
12 | | * Unless required by applicable law or agreed to in writing, software |
13 | | * distributed under the License is distributed on an "AS IS" BASIS, |
14 | | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express |
15 | | * or implied. See the License for the specific language governing |
16 | | * permissions and limitations under the License. |
17 | | */ |
18 | | |
19 | | #include <freerdp/config.h> |
20 | | |
21 | | #include <string.h> |
22 | | #include <stdlib.h> |
23 | | |
24 | | #include <winpr/synch.h> |
25 | | #include <winpr/sysinfo.h> |
26 | | #include <winpr/crypto.h> |
27 | | #include <freerdp/primitives.h> |
28 | | |
29 | | #include "prim_internal.h" |
30 | | |
31 | | #include <freerdp/log.h> |
32 | | #define TAG FREERDP_TAG("primitives") |
33 | | |
34 | | /* hints to know which kind of primitives to use */ |
35 | | static primitive_hints primitivesHints = PRIMITIVES_AUTODETECT; |
36 | | static BOOL primitives_init_optimized(primitives_t* prims); |
37 | | |
38 | | void primitives_set_hints(primitive_hints hints) |
39 | 0 | { |
40 | 0 | primitivesHints = hints; |
41 | 0 | } |
42 | | |
43 | | primitive_hints primitives_get_hints(void) |
44 | 0 | { |
45 | 0 | return primitivesHints; |
46 | 0 | } |
47 | | |
48 | | /* Singleton pointer used throughout the program when requested. */ |
49 | | static primitives_t pPrimitivesGeneric = { 0 }; |
50 | | static INIT_ONCE generic_primitives_InitOnce = INIT_ONCE_STATIC_INIT; |
51 | | |
52 | | #if defined(HAVE_CPU_OPTIMIZED_PRIMITIVES) |
53 | | static primitives_t pPrimitivesCpu = { 0 }; |
54 | | static INIT_ONCE cpu_primitives_InitOnce = INIT_ONCE_STATIC_INIT; |
55 | | |
56 | | #endif |
57 | | #if defined(WITH_OPENCL) |
58 | | static primitives_t pPrimitivesGpu = { 0 }; |
59 | | static INIT_ONCE gpu_primitives_InitOnce = INIT_ONCE_STATIC_INIT; |
60 | | |
61 | | #endif |
62 | | |
63 | | static INIT_ONCE auto_primitives_InitOnce = INIT_ONCE_STATIC_INIT; |
64 | | |
65 | | static primitives_t pPrimitives = { 0 }; |
66 | | |
67 | | /* ------------------------------------------------------------------------- */ |
68 | | static BOOL primitives_init_generic(primitives_t* prims) |
69 | 0 | { |
70 | 0 | primitives_init_add(prims); |
71 | 0 | primitives_init_andor(prims); |
72 | 0 | primitives_init_alphaComp(prims); |
73 | 0 | primitives_init_copy(prims); |
74 | 0 | primitives_init_set(prims); |
75 | 0 | primitives_init_shift(prims); |
76 | 0 | primitives_init_sign(prims); |
77 | 0 | primitives_init_colors(prims); |
78 | 0 | primitives_init_YCoCg(prims); |
79 | 0 | primitives_init_YUV(prims); |
80 | 0 | prims->uninit = NULL; |
81 | 0 | return TRUE; |
82 | 0 | } |
83 | | |
84 | | static BOOL CALLBACK primitives_init_generic_cb(PINIT_ONCE once, PVOID param, PVOID* context) |
85 | 0 | { |
86 | 0 | WINPR_UNUSED(once); |
87 | 0 | WINPR_UNUSED(param); |
88 | 0 | WINPR_UNUSED(context); |
89 | 0 | return primitives_init_generic(&pPrimitivesGeneric); |
90 | 0 | } |
91 | | |
92 | | static BOOL primitives_init_optimized(primitives_t* prims) |
93 | 0 | { |
94 | 0 | primitives_init_generic(prims); |
95 | |
|
96 | 0 | #if defined(HAVE_CPU_OPTIMIZED_PRIMITIVES) |
97 | 0 | primitives_init_add_opt(prims); |
98 | 0 | primitives_init_andor_opt(prims); |
99 | 0 | primitives_init_alphaComp_opt(prims); |
100 | 0 | primitives_init_copy_opt(prims); |
101 | 0 | primitives_init_set_opt(prims); |
102 | 0 | primitives_init_shift_opt(prims); |
103 | 0 | primitives_init_sign_opt(prims); |
104 | 0 | primitives_init_colors_opt(prims); |
105 | 0 | primitives_init_YCoCg_opt(prims); |
106 | 0 | primitives_init_YUV_opt(prims); |
107 | 0 | prims->flags |= PRIM_FLAGS_HAVE_EXTCPU; |
108 | 0 | #endif |
109 | 0 | return TRUE; |
110 | 0 | } |
111 | | |
112 | | #if defined(HAVE_CPU_OPTIMIZED_PRIMITIVES) && defined(WITH_OPENCL) |
113 | | typedef struct |
114 | | { |
115 | | BYTE* channels[3]; |
116 | | UINT32 steps[3]; |
117 | | prim_size_t roi; |
118 | | BYTE* outputBuffer; |
119 | | UINT32 outputStride; |
120 | | UINT32 testedFormat; |
121 | | } primitives_YUV_benchmark; |
122 | | |
123 | | static void primitives_YUV_benchmark_free(primitives_YUV_benchmark* bench) |
124 | | { |
125 | | if (!bench) |
126 | | return; |
127 | | |
128 | | free(bench->outputBuffer); |
129 | | |
130 | | for (int i = 0; i < 3; i++) |
131 | | free(bench->channels[i]); |
132 | | memset(bench, 0, sizeof(primitives_YUV_benchmark)); |
133 | | } |
134 | | |
135 | | static primitives_YUV_benchmark* primitives_YUV_benchmark_init(primitives_YUV_benchmark* ret) |
136 | | { |
137 | | prim_size_t* roi = NULL; |
138 | | if (!ret) |
139 | | return NULL; |
140 | | |
141 | | memset(ret, 0, sizeof(primitives_YUV_benchmark)); |
142 | | roi = &ret->roi; |
143 | | roi->width = 1024; |
144 | | roi->height = 768; |
145 | | ret->outputStride = roi->width * 4; |
146 | | ret->testedFormat = PIXEL_FORMAT_BGRA32; |
147 | | |
148 | | ret->outputBuffer = calloc(ret->outputStride, roi->height); |
149 | | if (!ret->outputBuffer) |
150 | | goto fail; |
151 | | |
152 | | for (int i = 0; i < 3; i++) |
153 | | { |
154 | | BYTE* buf = ret->channels[i] = calloc(roi->width, roi->height); |
155 | | if (!buf) |
156 | | goto fail; |
157 | | |
158 | | winpr_RAND(buf, 1ull * roi->width * roi->height); |
159 | | ret->steps[i] = roi->width; |
160 | | } |
161 | | |
162 | | return ret; |
163 | | |
164 | | fail: |
165 | | primitives_YUV_benchmark_free(ret); |
166 | | return ret; |
167 | | } |
168 | | |
169 | | static BOOL primitives_YUV_benchmark_run(primitives_YUV_benchmark* bench, primitives_t* prims, |
170 | | UINT64 runTime, UINT32* computations) |
171 | | { |
172 | | ULONGLONG dueDate = 0; |
173 | | const BYTE* channels[3] = { 0 }; |
174 | | pstatus_t status = 0; |
175 | | |
176 | | *computations = 0; |
177 | | |
178 | | for (size_t i = 0; i < 3; i++) |
179 | | channels[i] = bench->channels[i]; |
180 | | |
181 | | /* do a first dry run to initialize cache and such */ |
182 | | status = prims->YUV420ToRGB_8u_P3AC4R(channels, bench->steps, bench->outputBuffer, |
183 | | bench->outputStride, bench->testedFormat, &bench->roi); |
184 | | if (status != PRIMITIVES_SUCCESS) |
185 | | return FALSE; |
186 | | |
187 | | /* let's run the benchmark */ |
188 | | dueDate = GetTickCount64() + runTime; |
189 | | while (GetTickCount64() < dueDate) |
190 | | { |
191 | | pstatus_t cstatus = |
192 | | prims->YUV420ToRGB_8u_P3AC4R(channels, bench->steps, bench->outputBuffer, |
193 | | bench->outputStride, bench->testedFormat, &bench->roi); |
194 | | if (cstatus != PRIMITIVES_SUCCESS) |
195 | | return FALSE; |
196 | | *computations = *computations + 1; |
197 | | } |
198 | | return TRUE; |
199 | | } |
200 | | #endif |
201 | | |
202 | | static BOOL primitives_autodetect_best(primitives_t* prims) |
203 | 0 | { |
204 | 0 | BOOL ret = FALSE; |
205 | 0 | struct prim_benchmark |
206 | 0 | { |
207 | 0 | const char* name; |
208 | 0 | primitives_t* prims; |
209 | 0 | UINT32 flags; |
210 | 0 | UINT32 count; |
211 | 0 | }; |
212 | |
|
213 | 0 | struct prim_benchmark testcases[] = |
214 | 0 | { |
215 | 0 | { "generic", NULL, PRIMITIVES_PURE_SOFT, 0 }, |
216 | 0 | #if defined(HAVE_CPU_OPTIMIZED_PRIMITIVES) |
217 | 0 | { "optimized", NULL, PRIMITIVES_ONLY_CPU, 0 }, |
218 | 0 | #endif |
219 | | #if defined(WITH_OPENCL) |
220 | | { "opencl", NULL, PRIMITIVES_ONLY_GPU, 0 }, |
221 | | #endif |
222 | 0 | }; |
223 | 0 | const struct prim_benchmark* best = NULL; |
224 | |
|
225 | 0 | #if !defined(HAVE_CPU_OPTIMIZED_PRIMITIVES) || !defined(WITH_OPENCL) |
226 | 0 | { |
227 | 0 | #if defined(HAVE_CPU_OPTIMIZED_PRIMITIVES) || defined(WITH_OPENCL) |
228 | 0 | struct prim_benchmark* cur = &testcases[1]; |
229 | | #else |
230 | | struct prim_benchmark* cur = &testcases[0]; |
231 | | #endif |
232 | 0 | cur->prims = primitives_get_by_type(cur->flags); |
233 | 0 | if (!cur->prims) |
234 | 0 | { |
235 | 0 | WLog_WARN(TAG, "Failed to initialize %s primitives", cur->name); |
236 | 0 | return FALSE; |
237 | 0 | } |
238 | 0 | WLog_DBG(TAG, "primitives benchmark: only one backend, skipping..."); |
239 | 0 | best = cur; |
240 | 0 | } |
241 | | #else |
242 | | { |
243 | | UINT64 benchDuration = 150; /* 150 ms */ |
244 | | primitives_YUV_benchmark bench = { 0 }; |
245 | | primitives_YUV_benchmark* yuvBench = primitives_YUV_benchmark_init(&bench); |
246 | | if (!yuvBench) |
247 | | return FALSE; |
248 | | |
249 | | WLog_DBG(TAG, "primitives benchmark result:"); |
250 | | for (size_t x = 0; x < ARRAYSIZE(testcases); x++) |
251 | | { |
252 | | struct prim_benchmark* cur = &testcases[x]; |
253 | | cur->prims = primitives_get_by_type(cur->flags); |
254 | | if (!cur->prims) |
255 | | { |
256 | | WLog_WARN(TAG, "Failed to initialize %s primitives", cur->name); |
257 | | continue; |
258 | | } |
259 | | if (!primitives_YUV_benchmark_run(yuvBench, cur->prims, benchDuration, &cur->count)) |
260 | | { |
261 | | WLog_WARN(TAG, "error running %s YUV bench", cur->name); |
262 | | continue; |
263 | | } |
264 | | |
265 | | WLog_DBG(TAG, " * %s= %" PRIu32, cur->name, cur->count); |
266 | | if (!best || (best->count < cur->count)) |
267 | | best = cur; |
268 | | } |
269 | | primitives_YUV_benchmark_free(yuvBench); |
270 | | } |
271 | | #endif |
272 | | |
273 | 0 | if (!best) |
274 | 0 | { |
275 | 0 | WLog_ERR(TAG, "No primitives to test, aborting."); |
276 | 0 | goto out; |
277 | 0 | } |
278 | | /* finally compute the results */ |
279 | 0 | *prims = *best->prims; |
280 | |
|
281 | 0 | WLog_DBG(TAG, "primitives autodetect, using %s", best->name); |
282 | 0 | ret = TRUE; |
283 | 0 | out: |
284 | 0 | if (!ret) |
285 | 0 | *prims = pPrimitivesGeneric; |
286 | |
|
287 | 0 | return ret; |
288 | 0 | } |
289 | | |
290 | | #if defined(WITH_OPENCL) |
291 | | static BOOL CALLBACK primitives_init_gpu_cb(PINIT_ONCE once, PVOID param, PVOID* context) |
292 | | { |
293 | | WINPR_UNUSED(once); |
294 | | WINPR_UNUSED(param); |
295 | | WINPR_UNUSED(context); |
296 | | |
297 | | if (!primitives_init_opencl(&pPrimitivesGpu)) |
298 | | return FALSE; |
299 | | |
300 | | return TRUE; |
301 | | } |
302 | | #endif |
303 | | |
304 | | #if defined(HAVE_CPU_OPTIMIZED_PRIMITIVES) |
305 | | static BOOL CALLBACK primitives_init_cpu_cb(PINIT_ONCE once, PVOID param, PVOID* context) |
306 | 0 | { |
307 | 0 | WINPR_UNUSED(once); |
308 | 0 | WINPR_UNUSED(param); |
309 | 0 | WINPR_UNUSED(context); |
310 | |
|
311 | 0 | if (!primitives_init_optimized(&pPrimitivesCpu)) |
312 | 0 | return FALSE; |
313 | | |
314 | 0 | return TRUE; |
315 | 0 | } |
316 | | #endif |
317 | | |
318 | | static BOOL CALLBACK primitives_auto_init_cb(PINIT_ONCE once, PVOID param, PVOID* context) |
319 | 0 | { |
320 | 0 | WINPR_UNUSED(once); |
321 | 0 | WINPR_UNUSED(param); |
322 | 0 | WINPR_UNUSED(context); |
323 | |
|
324 | 0 | return primitives_init(&pPrimitives, primitivesHints); |
325 | 0 | } |
326 | | |
327 | | BOOL primitives_init(primitives_t* p, primitive_hints hints) |
328 | 0 | { |
329 | 0 | switch (hints) |
330 | 0 | { |
331 | 0 | case PRIMITIVES_AUTODETECT: |
332 | 0 | return primitives_autodetect_best(p); |
333 | 0 | case PRIMITIVES_PURE_SOFT: |
334 | 0 | *p = pPrimitivesGeneric; |
335 | 0 | return TRUE; |
336 | 0 | case PRIMITIVES_ONLY_CPU: |
337 | 0 | #if defined(HAVE_CPU_OPTIMIZED_PRIMITIVES) |
338 | 0 | *p = pPrimitivesCpu; |
339 | 0 | return TRUE; |
340 | 0 | #endif |
341 | 0 | case PRIMITIVES_ONLY_GPU: |
342 | | #if defined(WITH_OPENCL) |
343 | | *p = pPrimitivesGpu; |
344 | | return TRUE; |
345 | | #endif |
346 | 0 | default: |
347 | 0 | WLog_ERR(TAG, "unknown hint %d", hints); |
348 | 0 | return FALSE; |
349 | 0 | } |
350 | 0 | } |
351 | | |
352 | | void primitives_uninit(void) |
353 | 0 | { |
354 | | #if defined(WITH_OPENCL) |
355 | | if (pPrimitivesGpu.uninit) |
356 | | pPrimitivesGpu.uninit(); |
357 | | #endif |
358 | 0 | #if defined(HAVE_CPU_OPTIMIZED_PRIMITIVES) |
359 | 0 | if (pPrimitivesCpu.uninit) |
360 | 0 | pPrimitivesCpu.uninit(); |
361 | 0 | #endif |
362 | 0 | if (pPrimitivesGeneric.uninit) |
363 | 0 | pPrimitivesGeneric.uninit(); |
364 | 0 | } |
365 | | |
366 | | /* ------------------------------------------------------------------------- */ |
367 | | static void setup(void) |
368 | 0 | { |
369 | 0 | InitOnceExecuteOnce(&generic_primitives_InitOnce, primitives_init_generic_cb, NULL, NULL); |
370 | 0 | #if defined(HAVE_CPU_OPTIMIZED_PRIMITIVES) |
371 | 0 | InitOnceExecuteOnce(&cpu_primitives_InitOnce, primitives_init_cpu_cb, NULL, NULL); |
372 | 0 | #endif |
373 | | #if defined(WITH_OPENCL) |
374 | | InitOnceExecuteOnce(&gpu_primitives_InitOnce, primitives_init_gpu_cb, NULL, NULL); |
375 | | #endif |
376 | 0 | InitOnceExecuteOnce(&auto_primitives_InitOnce, primitives_auto_init_cb, NULL, NULL); |
377 | 0 | } |
378 | | |
379 | | primitives_t* primitives_get(void) |
380 | 0 | { |
381 | 0 | setup(); |
382 | 0 | return &pPrimitives; |
383 | 0 | } |
384 | | |
385 | | primitives_t* primitives_get_generic(void) |
386 | 0 | { |
387 | 0 | InitOnceExecuteOnce(&generic_primitives_InitOnce, primitives_init_generic_cb, NULL, NULL); |
388 | 0 | return &pPrimitivesGeneric; |
389 | 0 | } |
390 | | |
391 | | primitives_t* primitives_get_by_type(primitive_hints type) |
392 | 0 | { |
393 | 0 | InitOnceExecuteOnce(&generic_primitives_InitOnce, primitives_init_generic_cb, NULL, NULL); |
394 | |
|
395 | 0 | switch (type) |
396 | 0 | { |
397 | 0 | case PRIMITIVES_ONLY_GPU: |
398 | | #if defined(WITH_OPENCL) |
399 | | if (!InitOnceExecuteOnce(&gpu_primitives_InitOnce, primitives_init_gpu_cb, NULL, NULL)) |
400 | | return NULL; |
401 | | return &pPrimitivesGpu; |
402 | | #endif |
403 | 0 | case PRIMITIVES_ONLY_CPU: |
404 | 0 | #if defined(HAVE_CPU_OPTIMIZED_PRIMITIVES) |
405 | 0 | if (!InitOnceExecuteOnce(&cpu_primitives_InitOnce, primitives_init_cpu_cb, NULL, NULL)) |
406 | 0 | return NULL; |
407 | 0 | return &pPrimitivesCpu; |
408 | 0 | #endif |
409 | 0 | case PRIMITIVES_PURE_SOFT: |
410 | 0 | default: |
411 | 0 | return &pPrimitivesGeneric; |
412 | 0 | } |
413 | 0 | } |
414 | | |
415 | | DWORD primitives_flags(primitives_t* p) |
416 | 0 | { |
417 | 0 | return p->flags; |
418 | 0 | } |
419 | | |
420 | | const char* primitives_avc444_frame_type_str(avc444_frame_type type) |
421 | 0 | { |
422 | 0 | switch (type) |
423 | 0 | { |
424 | 0 | case AVC444_LUMA: |
425 | 0 | return "AVC444_LUMA"; |
426 | 0 | case AVC444_CHROMAv1: |
427 | 0 | return "AVC444_CHROMAv1"; |
428 | 0 | case AVC444_CHROMAv2: |
429 | 0 | return "AVC444_CHROMAv2"; |
430 | 0 | default: |
431 | 0 | return "INVALID_FRAME_TYPE"; |
432 | 0 | } |
433 | 0 | } |
434 | | |
435 | | const char* primtives_hint_str(primitive_hints hint) |
436 | 0 | { |
437 | 0 | switch (hint) |
438 | 0 | { |
439 | 0 | case PRIMITIVES_PURE_SOFT: |
440 | 0 | return "PRIMITIVES_PURE_SOFT"; |
441 | 0 | case PRIMITIVES_ONLY_CPU: |
442 | 0 | return "PRIMITIVES_ONLY_CPU"; |
443 | 0 | case PRIMITIVES_ONLY_GPU: |
444 | 0 | return "PRIMITIVES_ONLY_GPU"; |
445 | 0 | case PRIMITIVES_AUTODETECT: |
446 | 0 | return "PRIMITIVES_AUTODETECT"; |
447 | 0 | default: |
448 | 0 | return "PRIMITIVES_UNKNOWN"; |
449 | 0 | } |
450 | 0 | } |