FreeRDP
Loading...
Searching...
No Matches
primitives.c
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 */
35static primitive_hints primitivesHints = PRIMITIVES_AUTODETECT;
36static BOOL primitives_init_optimized(primitives_t* prims);
37
38void primitives_set_hints(primitive_hints hints)
39{
40 primitivesHints = hints;
41}
42
43primitive_hints primitives_get_hints(void)
44{
45 return primitivesHints;
46}
47
48/* Singleton pointer used throughout the program when requested. */
49static primitives_t pPrimitivesGeneric = { 0 };
50static INIT_ONCE generic_primitives_InitOnce = INIT_ONCE_STATIC_INIT;
51
52#if defined(HAVE_CPU_OPTIMIZED_PRIMITIVES)
53static primitives_t pPrimitivesCpu = { 0 };
54static INIT_ONCE cpu_primitives_InitOnce = INIT_ONCE_STATIC_INIT;
55
56#endif
57#if defined(WITH_OPENCL)
58static primitives_t pPrimitivesGpu = { 0 };
59static INIT_ONCE gpu_primitives_InitOnce = INIT_ONCE_STATIC_INIT;
60
61#endif
62
63static INIT_ONCE auto_primitives_InitOnce = INIT_ONCE_STATIC_INIT;
64
65static primitives_t pPrimitives = { 0 };
66
67/* ------------------------------------------------------------------------- */
68static BOOL primitives_init_generic(primitives_t* prims)
69{
70 primitives_init_add(prims);
71 primitives_init_andor(prims);
72 primitives_init_alphaComp(prims);
73 primitives_init_copy(prims);
74 primitives_init_set(prims);
75 primitives_init_shift(prims);
76 primitives_init_sign(prims);
77 primitives_init_colors(prims);
78 primitives_init_YCoCg(prims);
79 primitives_init_YUV(prims);
80 prims->uninit = NULL;
81 return TRUE;
82}
83
84static BOOL CALLBACK primitives_init_generic_cb(PINIT_ONCE once, PVOID param, PVOID* context)
85{
86 WINPR_UNUSED(once);
87 WINPR_UNUSED(param);
88 WINPR_UNUSED(context);
89 return primitives_init_generic(&pPrimitivesGeneric);
90}
91
92static BOOL primitives_init_optimized(primitives_t* prims)
93{
94 primitives_init_generic(prims);
95
96#if defined(HAVE_CPU_OPTIMIZED_PRIMITIVES)
97 primitives_init_add_opt(prims);
98 primitives_init_andor_opt(prims);
99 primitives_init_alphaComp_opt(prims);
100 primitives_init_copy_opt(prims);
101 primitives_init_set_opt(prims);
102 primitives_init_shift_opt(prims);
103 primitives_init_sign_opt(prims);
104 primitives_init_colors_opt(prims);
105 primitives_init_YCoCg_opt(prims);
106 primitives_init_YUV_opt(prims);
107 prims->flags |= PRIM_FLAGS_HAVE_EXTCPU;
108#endif
109 return TRUE;
110}
111
112#if defined(HAVE_CPU_OPTIMIZED_PRIMITIVES) && defined(WITH_OPENCL)
113typedef 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
123static 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
135static 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
164fail:
165 primitives_YUV_benchmark_free(ret);
166 return ret;
167}
168
169static 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
202static BOOL primitives_autodetect_best(primitives_t* prims)
203{
204 BOOL ret = FALSE;
205 struct prim_benchmark
206 {
207 const char* name;
208 primitives_t* prims;
209 UINT32 flags;
210 UINT32 count;
211 };
212
213 struct prim_benchmark testcases[] =
214 {
215 { "generic", NULL, PRIMITIVES_PURE_SOFT, 0 },
216#if defined(HAVE_CPU_OPTIMIZED_PRIMITIVES)
217 { "optimized", NULL, PRIMITIVES_ONLY_CPU, 0 },
218#endif
219#if defined(WITH_OPENCL)
220 { "opencl", NULL, PRIMITIVES_ONLY_GPU, 0 },
221#endif
222 };
223 const struct prim_benchmark* best = NULL;
224
225#if !defined(HAVE_CPU_OPTIMIZED_PRIMITIVES) || !defined(WITH_OPENCL)
226 {
227#if defined(HAVE_CPU_OPTIMIZED_PRIMITIVES) || defined(WITH_OPENCL)
228 struct prim_benchmark* cur = &testcases[1];
229#else
230 struct prim_benchmark* cur = &testcases[0];
231#endif
232 cur->prims = primitives_get_by_type(cur->flags);
233 if (!cur->prims)
234 {
235 WLog_WARN(TAG, "Failed to initialize %s primitives", cur->name);
236 return FALSE;
237 }
238 WLog_DBG(TAG, "primitives benchmark: only one backend, skipping...");
239 best = cur;
240 }
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 if (!best)
274 {
275 WLog_ERR(TAG, "No primitives to test, aborting.");
276 goto out;
277 }
278 /* finally compute the results */
279 *prims = *best->prims;
280
281 WLog_DBG(TAG, "primitives autodetect, using %s", best->name);
282 ret = TRUE;
283out:
284 if (!ret)
285 *prims = pPrimitivesGeneric;
286
287 return ret;
288}
289
290#if defined(WITH_OPENCL)
291static 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)
305static BOOL CALLBACK primitives_init_cpu_cb(PINIT_ONCE once, PVOID param, PVOID* context)
306{
307 WINPR_UNUSED(once);
308 WINPR_UNUSED(param);
309 WINPR_UNUSED(context);
310
311 if (!primitives_init_optimized(&pPrimitivesCpu))
312 return FALSE;
313
314 return TRUE;
315}
316#endif
317
318static BOOL CALLBACK primitives_auto_init_cb(PINIT_ONCE once, PVOID param, PVOID* context)
319{
320 WINPR_UNUSED(once);
321 WINPR_UNUSED(param);
322 WINPR_UNUSED(context);
323
324 return primitives_init(&pPrimitives, primitivesHints);
325}
326
327BOOL primitives_init(primitives_t* p, primitive_hints hints)
328{
329 switch (hints)
330 {
331 case PRIMITIVES_AUTODETECT:
332 return primitives_autodetect_best(p);
333 case PRIMITIVES_PURE_SOFT:
334 *p = pPrimitivesGeneric;
335 return TRUE;
336 case PRIMITIVES_ONLY_CPU:
337#if defined(HAVE_CPU_OPTIMIZED_PRIMITIVES)
338 *p = pPrimitivesCpu;
339 return TRUE;
340#endif
341 case PRIMITIVES_ONLY_GPU:
342#if defined(WITH_OPENCL)
343 *p = pPrimitivesGpu;
344 return TRUE;
345#endif
346 default:
347 WLog_ERR(TAG, "unknown hint %d", hints);
348 return FALSE;
349 }
350}
351
352void primitives_uninit(void)
353{
354#if defined(WITH_OPENCL)
355 if (pPrimitivesGpu.uninit)
356 pPrimitivesGpu.uninit();
357#endif
358#if defined(HAVE_CPU_OPTIMIZED_PRIMITIVES)
359 if (pPrimitivesCpu.uninit)
360 pPrimitivesCpu.uninit();
361#endif
362 if (pPrimitivesGeneric.uninit)
363 pPrimitivesGeneric.uninit();
364}
365
366/* ------------------------------------------------------------------------- */
367static void setup(void)
368{
369 InitOnceExecuteOnce(&generic_primitives_InitOnce, primitives_init_generic_cb, NULL, NULL);
370#if defined(HAVE_CPU_OPTIMIZED_PRIMITIVES)
371 InitOnceExecuteOnce(&cpu_primitives_InitOnce, primitives_init_cpu_cb, NULL, NULL);
372#endif
373#if defined(WITH_OPENCL)
374 InitOnceExecuteOnce(&gpu_primitives_InitOnce, primitives_init_gpu_cb, NULL, NULL);
375#endif
376 InitOnceExecuteOnce(&auto_primitives_InitOnce, primitives_auto_init_cb, NULL, NULL);
377}
378
379primitives_t* primitives_get(void)
380{
381 setup();
382 return &pPrimitives;
383}
384
385primitives_t* primitives_get_generic(void)
386{
387 InitOnceExecuteOnce(&generic_primitives_InitOnce, primitives_init_generic_cb, NULL, NULL);
388 return &pPrimitivesGeneric;
389}
390
391primitives_t* primitives_get_by_type(primitive_hints type)
392{
393 InitOnceExecuteOnce(&generic_primitives_InitOnce, primitives_init_generic_cb, NULL, NULL);
394
395 switch (type)
396 {
397 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 case PRIMITIVES_ONLY_CPU:
404#if defined(HAVE_CPU_OPTIMIZED_PRIMITIVES)
405 if (!InitOnceExecuteOnce(&cpu_primitives_InitOnce, primitives_init_cpu_cb, NULL, NULL))
406 return NULL;
407 return &pPrimitivesCpu;
408#endif
409 case PRIMITIVES_PURE_SOFT:
410 default:
411 return &pPrimitivesGeneric;
412 }
413}
414
415DWORD primitives_flags(primitives_t* p)
416{
417 return p->flags;
418}
419
420const char* primitives_avc444_frame_type_str(avc444_frame_type type)
421{
422 switch (type)
423 {
424 case AVC444_LUMA:
425 return "AVC444_LUMA";
426 case AVC444_CHROMAv1:
427 return "AVC444_CHROMAv1";
428 case AVC444_CHROMAv2:
429 return "AVC444_CHROMAv2";
430 default:
431 return "INVALID_FRAME_TYPE";
432 }
433}
434
435const char* primtives_hint_str(primitive_hints hint)
436{
437 switch (hint)
438 {
439 case PRIMITIVES_PURE_SOFT:
440 return "PRIMITIVES_PURE_SOFT";
441 case PRIMITIVES_ONLY_CPU:
442 return "PRIMITIVES_ONLY_CPU";
443 case PRIMITIVES_ONLY_GPU:
444 return "PRIMITIVES_ONLY_GPU";
445 case PRIMITIVES_AUTODETECT:
446 return "PRIMITIVES_AUTODETECT";
447 default:
448 return "PRIMITIVES_UNKNOWN";
449 }
450}