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 = WINPR_C_ARRAY_INIT;
50static INIT_ONCE generic_primitives_InitOnce = INIT_ONCE_STATIC_INIT;
51
52#if defined(HAVE_CPU_OPTIMIZED_PRIMITIVES)
53static primitives_t pPrimitivesCpu = WINPR_C_ARRAY_INIT;
54static INIT_ONCE cpu_primitives_InitOnce = INIT_ONCE_STATIC_INIT;
55
56#endif
57#if defined(WITH_OPENCL)
58static primitives_t pPrimitivesGpu = WINPR_C_ARRAY_INIT;
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 = WINPR_C_ARRAY_INIT;
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 = nullptr;
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 = nullptr;
138 if (!ret)
139 return nullptr;
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 if (winpr_RAND(buf, 1ull * roi->width * roi->height) < 0)
159 goto fail;
160 ret->steps[i] = roi->width;
161 }
162
163 return ret;
164
165fail:
166 primitives_YUV_benchmark_free(ret);
167 return ret;
168}
169
170static BOOL primitives_YUV_benchmark_run(primitives_YUV_benchmark* bench, primitives_t* prims,
171 UINT64 runTime, UINT32* computations)
172{
173 ULONGLONG dueDate = 0;
174 const BYTE* channels[3] = WINPR_C_ARRAY_INIT;
175 pstatus_t status = 0;
176
177 *computations = 0;
178
179 for (size_t i = 0; i < 3; i++)
180 channels[i] = bench->channels[i];
181
182 /* do a first dry run to initialize cache and such */
183 status = prims->YUV420ToRGB_8u_P3AC4R(channels, bench->steps, bench->outputBuffer,
184 bench->outputStride, bench->testedFormat, &bench->roi);
185 if (status != PRIMITIVES_SUCCESS)
186 return FALSE;
187
188 /* let's run the benchmark */
189 dueDate = GetTickCount64() + runTime;
190 while (GetTickCount64() < dueDate)
191 {
192 pstatus_t cstatus =
193 prims->YUV420ToRGB_8u_P3AC4R(channels, bench->steps, bench->outputBuffer,
194 bench->outputStride, bench->testedFormat, &bench->roi);
195 if (cstatus != PRIMITIVES_SUCCESS)
196 return FALSE;
197 *computations = *computations + 1;
198 }
199 return TRUE;
200}
201#endif
202
203static BOOL primitives_autodetect_best(primitives_t* prims)
204{
205 BOOL ret = FALSE;
206 struct prim_benchmark
207 {
208 const char* name;
209 primitives_t* prims;
210 primitive_hints flags;
211 UINT32 count;
212 };
213
214 struct prim_benchmark testcases[] = {
215 { "generic", nullptr, PRIMITIVES_PURE_SOFT, 0 },
216#if defined(HAVE_CPU_OPTIMIZED_PRIMITIVES)
217 { "optimized", nullptr, PRIMITIVES_ONLY_CPU, 0 },
218#endif
219#if defined(WITH_OPENCL)
220 { "opencl", nullptr, PRIMITIVES_ONLY_GPU, 0 },
221#endif
222 };
223 const struct prim_benchmark* best = nullptr;
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 = WINPR_C_ARRAY_INIT;
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 return primitives_init_opencl(&pPrimitivesGpu);
298}
299#endif
300
301#if defined(HAVE_CPU_OPTIMIZED_PRIMITIVES)
302static BOOL CALLBACK primitives_init_cpu_cb(PINIT_ONCE once, PVOID param, PVOID* context)
303{
304 WINPR_UNUSED(once);
305 WINPR_UNUSED(param);
306 WINPR_UNUSED(context);
307
308 return (primitives_init_optimized(&pPrimitivesCpu));
309}
310#endif
311
312static BOOL CALLBACK primitives_auto_init_cb(PINIT_ONCE once, PVOID param, PVOID* context)
313{
314 WINPR_UNUSED(once);
315 WINPR_UNUSED(param);
316 WINPR_UNUSED(context);
317
318 return primitives_init(&pPrimitives, primitivesHints);
319}
320
321BOOL primitives_init(primitives_t* p, primitive_hints hints)
322{
323 switch (hints)
324 {
325 case PRIMITIVES_AUTODETECT:
326 return primitives_autodetect_best(p);
327 case PRIMITIVES_PURE_SOFT:
328 *p = pPrimitivesGeneric;
329 return TRUE;
330 case PRIMITIVES_ONLY_CPU:
331#if defined(HAVE_CPU_OPTIMIZED_PRIMITIVES)
332 *p = pPrimitivesCpu;
333 return TRUE;
334#endif
335 case PRIMITIVES_ONLY_GPU:
336#if defined(WITH_OPENCL)
337 *p = pPrimitivesGpu;
338 return TRUE;
339#endif
340 default:
341 WLog_ERR(TAG, "unknown hint %u", hints);
342 return FALSE;
343 }
344}
345
346void primitives_uninit(void)
347{
348#if defined(WITH_OPENCL)
349 if (pPrimitivesGpu.uninit)
350 pPrimitivesGpu.uninit();
351#endif
352#if defined(HAVE_CPU_OPTIMIZED_PRIMITIVES)
353 if (pPrimitivesCpu.uninit)
354 pPrimitivesCpu.uninit();
355#endif
356 if (pPrimitivesGeneric.uninit)
357 pPrimitivesGeneric.uninit();
358}
359
360/* ------------------------------------------------------------------------- */
361static void setup(void)
362{
363 if (!InitOnceExecuteOnce(&generic_primitives_InitOnce, primitives_init_generic_cb, nullptr,
364 nullptr))
365 return;
366#if defined(HAVE_CPU_OPTIMIZED_PRIMITIVES)
367 if (!InitOnceExecuteOnce(&cpu_primitives_InitOnce, primitives_init_cpu_cb, nullptr, nullptr))
368 return;
369#endif
370#if defined(WITH_OPENCL)
371 if (!InitOnceExecuteOnce(&gpu_primitives_InitOnce, primitives_init_gpu_cb, nullptr, nullptr))
372 return;
373#endif
374 if (!InitOnceExecuteOnce(&auto_primitives_InitOnce, primitives_auto_init_cb, nullptr, nullptr))
375 return;
376}
377
378primitives_t* primitives_get(void)
379{
380 setup();
381 return &pPrimitives;
382}
383
384primitives_t* primitives_get_generic(void)
385{
386 if (!InitOnceExecuteOnce(&generic_primitives_InitOnce, primitives_init_generic_cb, nullptr,
387 nullptr))
388 return nullptr;
389 return &pPrimitivesGeneric;
390}
391
392primitives_t* primitives_get_by_type(primitive_hints type)
393{
394 if (!InitOnceExecuteOnce(&generic_primitives_InitOnce, primitives_init_generic_cb, nullptr,
395 nullptr))
396 return nullptr;
397
398 switch (type)
399 {
400 case PRIMITIVES_ONLY_GPU:
401#if defined(WITH_OPENCL)
402 if (!InitOnceExecuteOnce(&gpu_primitives_InitOnce, primitives_init_gpu_cb, nullptr,
403 nullptr))
404 return nullptr;
405 return &pPrimitivesGpu;
406#endif
407 case PRIMITIVES_ONLY_CPU:
408#if defined(HAVE_CPU_OPTIMIZED_PRIMITIVES)
409 if (!InitOnceExecuteOnce(&cpu_primitives_InitOnce, primitives_init_cpu_cb, nullptr,
410 nullptr))
411 return nullptr;
412 return &pPrimitivesCpu;
413#endif
414 case PRIMITIVES_PURE_SOFT:
415 default:
416 return &pPrimitivesGeneric;
417 }
418}
419
420DWORD primitives_flags(primitives_t* p)
421{
422 return p->flags;
423}
424
425const char* primitives_avc444_frame_type_str(avc444_frame_type type)
426{
427 switch (type)
428 {
429 case AVC444_LUMA:
430 return "AVC444_LUMA";
431 case AVC444_CHROMAv1:
432 return "AVC444_CHROMAv1";
433 case AVC444_CHROMAv2:
434 return "AVC444_CHROMAv2";
435 default:
436 return "INVALID_FRAME_TYPE";
437 }
438}
439
440const char* primtives_hint_str(primitive_hints hint)
441{
442 switch (hint)
443 {
444 case PRIMITIVES_PURE_SOFT:
445 return "PRIMITIVES_PURE_SOFT";
446 case PRIMITIVES_ONLY_CPU:
447 return "PRIMITIVES_ONLY_CPU";
448 case PRIMITIVES_ONLY_GPU:
449 return "PRIMITIVES_ONLY_GPU";
450 case PRIMITIVES_AUTODETECT:
451 return "PRIMITIVES_AUTODETECT";
452 default:
453 return "PRIMITIVES_UNKNOWN";
454 }
455}