Coverage Report

Created: 2025-07-01 06:46

/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
}