Coverage Report

Created: 2026-03-04 06:17

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/FreeRDP/libfreerdp/primitives/primitives.c
Line
Count
Source
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 = WINPR_C_ARRAY_INIT;
50
static INIT_ONCE generic_primitives_InitOnce = INIT_ONCE_STATIC_INIT;
51
52
#if defined(HAVE_CPU_OPTIMIZED_PRIMITIVES)
53
static primitives_t pPrimitivesCpu = WINPR_C_ARRAY_INIT;
54
static INIT_ONCE cpu_primitives_InitOnce = INIT_ONCE_STATIC_INIT;
55
56
#endif
57
#if defined(WITH_OPENCL)
58
static primitives_t pPrimitivesGpu = WINPR_C_ARRAY_INIT;
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 = WINPR_C_ARRAY_INIT;
66
67
/* ------------------------------------------------------------------------- */
68
static BOOL primitives_init_generic(primitives_t* prims)
69
2
{
70
2
  primitives_init_add(prims);
71
2
  primitives_init_andor(prims);
72
2
  primitives_init_alphaComp(prims);
73
2
  primitives_init_copy(prims);
74
2
  primitives_init_set(prims);
75
2
  primitives_init_shift(prims);
76
2
  primitives_init_sign(prims);
77
2
  primitives_init_colors(prims);
78
2
  primitives_init_YCoCg(prims);
79
2
  primitives_init_YUV(prims);
80
2
  prims->uninit = nullptr;
81
2
  return TRUE;
82
2
}
83
84
static BOOL CALLBACK primitives_init_generic_cb(PINIT_ONCE once, PVOID param, PVOID* context)
85
1
{
86
1
  WINPR_UNUSED(once);
87
1
  WINPR_UNUSED(param);
88
1
  WINPR_UNUSED(context);
89
1
  return primitives_init_generic(&pPrimitivesGeneric);
90
1
}
91
92
static BOOL primitives_init_optimized(primitives_t* prims)
93
1
{
94
1
  primitives_init_generic(prims);
95
96
1
#if defined(HAVE_CPU_OPTIMIZED_PRIMITIVES)
97
1
  primitives_init_add_opt(prims);
98
1
  primitives_init_andor_opt(prims);
99
1
  primitives_init_alphaComp_opt(prims);
100
1
  primitives_init_copy_opt(prims);
101
1
  primitives_init_set_opt(prims);
102
1
  primitives_init_shift_opt(prims);
103
1
  primitives_init_sign_opt(prims);
104
1
  primitives_init_colors_opt(prims);
105
1
  primitives_init_YCoCg_opt(prims);
106
1
  primitives_init_YUV_opt(prims);
107
1
  prims->flags |= PRIM_FLAGS_HAVE_EXTCPU;
108
1
#endif
109
1
  return TRUE;
110
1
}
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 = 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
165
fail:
166
  primitives_YUV_benchmark_free(ret);
167
  return ret;
168
}
169
170
static 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
203
static BOOL primitives_autodetect_best(primitives_t* prims)
204
1
{
205
1
  BOOL ret = FALSE;
206
1
  struct prim_benchmark
207
1
  {
208
1
    const char* name;
209
1
    primitives_t* prims;
210
1
    primitive_hints flags;
211
1
    UINT32 count;
212
1
  };
213
214
1
  struct prim_benchmark testcases[] = {
215
1
    { "generic", nullptr, PRIMITIVES_PURE_SOFT, 0 },
216
1
#if defined(HAVE_CPU_OPTIMIZED_PRIMITIVES)
217
1
    { "optimized", nullptr, PRIMITIVES_ONLY_CPU, 0 },
218
1
#endif
219
#if defined(WITH_OPENCL)
220
    { "opencl", nullptr, PRIMITIVES_ONLY_GPU, 0 },
221
#endif
222
1
  };
223
1
  const struct prim_benchmark* best = nullptr;
224
225
1
#if !defined(HAVE_CPU_OPTIMIZED_PRIMITIVES) || !defined(WITH_OPENCL)
226
1
  {
227
1
#if defined(HAVE_CPU_OPTIMIZED_PRIMITIVES) || defined(WITH_OPENCL)
228
1
    struct prim_benchmark* cur = &testcases[1];
229
#else
230
    struct prim_benchmark* cur = &testcases[0];
231
#endif
232
1
    cur->prims = primitives_get_by_type(cur->flags);
233
1
    if (!cur->prims)
234
0
    {
235
0
      WLog_WARN(TAG, "Failed to initialize %s primitives", cur->name);
236
0
      return FALSE;
237
0
    }
238
1
    WLog_DBG(TAG, "primitives benchmark: only one backend, skipping...");
239
1
    best = cur;
240
1
  }
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
1
  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
1
  *prims = *best->prims;
280
281
1
  WLog_DBG(TAG, "primitives autodetect, using %s", best->name);
282
1
  ret = TRUE;
283
1
out:
284
1
  if (!ret)
285
0
    *prims = pPrimitivesGeneric;
286
287
1
  return ret;
288
1
}
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
  return primitives_init_opencl(&pPrimitivesGpu);
298
}
299
#endif
300
301
#if defined(HAVE_CPU_OPTIMIZED_PRIMITIVES)
302
static BOOL CALLBACK primitives_init_cpu_cb(PINIT_ONCE once, PVOID param, PVOID* context)
303
1
{
304
1
  WINPR_UNUSED(once);
305
1
  WINPR_UNUSED(param);
306
1
  WINPR_UNUSED(context);
307
308
1
  return (primitives_init_optimized(&pPrimitivesCpu));
309
1
}
310
#endif
311
312
static BOOL CALLBACK primitives_auto_init_cb(PINIT_ONCE once, PVOID param, PVOID* context)
313
1
{
314
1
  WINPR_UNUSED(once);
315
1
  WINPR_UNUSED(param);
316
1
  WINPR_UNUSED(context);
317
318
1
  return primitives_init(&pPrimitives, primitivesHints);
319
1
}
320
321
BOOL primitives_init(primitives_t* p, primitive_hints hints)
322
1
{
323
1
  switch (hints)
324
1
  {
325
1
    case PRIMITIVES_AUTODETECT:
326
1
      return primitives_autodetect_best(p);
327
0
    case PRIMITIVES_PURE_SOFT:
328
0
      *p = pPrimitivesGeneric;
329
0
      return TRUE;
330
0
    case PRIMITIVES_ONLY_CPU:
331
0
#if defined(HAVE_CPU_OPTIMIZED_PRIMITIVES)
332
0
      *p = pPrimitivesCpu;
333
0
      return TRUE;
334
0
#endif
335
0
    case PRIMITIVES_ONLY_GPU:
336
#if defined(WITH_OPENCL)
337
      *p = pPrimitivesGpu;
338
      return TRUE;
339
#endif
340
0
    default:
341
0
      WLog_ERR(TAG, "unknown hint %u", hints);
342
0
      return FALSE;
343
1
  }
344
1
}
345
346
void primitives_uninit(void)
347
0
{
348
#if defined(WITH_OPENCL)
349
  if (pPrimitivesGpu.uninit)
350
    pPrimitivesGpu.uninit();
351
#endif
352
0
#if defined(HAVE_CPU_OPTIMIZED_PRIMITIVES)
353
0
  if (pPrimitivesCpu.uninit)
354
0
    pPrimitivesCpu.uninit();
355
0
#endif
356
0
  if (pPrimitivesGeneric.uninit)
357
0
    pPrimitivesGeneric.uninit();
358
0
}
359
360
/* ------------------------------------------------------------------------- */
361
static void setup(void)
362
22.7k
{
363
22.7k
  if (!InitOnceExecuteOnce(&generic_primitives_InitOnce, primitives_init_generic_cb, nullptr,
364
22.7k
                           nullptr))
365
0
    return;
366
22.7k
#if defined(HAVE_CPU_OPTIMIZED_PRIMITIVES)
367
22.7k
  if (!InitOnceExecuteOnce(&cpu_primitives_InitOnce, primitives_init_cpu_cb, nullptr, nullptr))
368
0
    return;
369
22.7k
#endif
370
#if defined(WITH_OPENCL)
371
  if (!InitOnceExecuteOnce(&gpu_primitives_InitOnce, primitives_init_gpu_cb, nullptr, nullptr))
372
    return;
373
#endif
374
22.7k
  if (!InitOnceExecuteOnce(&auto_primitives_InitOnce, primitives_auto_init_cb, nullptr, nullptr))
375
0
    return;
376
22.7k
}
377
378
primitives_t* primitives_get(void)
379
22.7k
{
380
22.7k
  setup();
381
22.7k
  return &pPrimitives;
382
22.7k
}
383
384
primitives_t* primitives_get_generic(void)
385
5.38k
{
386
5.38k
  if (!InitOnceExecuteOnce(&generic_primitives_InitOnce, primitives_init_generic_cb, nullptr,
387
5.38k
                           nullptr))
388
0
    return nullptr;
389
5.38k
  return &pPrimitivesGeneric;
390
5.38k
}
391
392
primitives_t* primitives_get_by_type(primitive_hints type)
393
1
{
394
1
  if (!InitOnceExecuteOnce(&generic_primitives_InitOnce, primitives_init_generic_cb, nullptr,
395
1
                           nullptr))
396
0
    return nullptr;
397
398
1
  switch (type)
399
1
  {
400
0
    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
1
    case PRIMITIVES_ONLY_CPU:
408
1
#if defined(HAVE_CPU_OPTIMIZED_PRIMITIVES)
409
1
      if (!InitOnceExecuteOnce(&cpu_primitives_InitOnce, primitives_init_cpu_cb, nullptr,
410
1
                               nullptr))
411
0
        return nullptr;
412
1
      return &pPrimitivesCpu;
413
0
#endif
414
0
    case PRIMITIVES_PURE_SOFT:
415
0
    default:
416
0
      return &pPrimitivesGeneric;
417
1
  }
418
1
}
419
420
DWORD primitives_flags(primitives_t* p)
421
0
{
422
0
  return p->flags;
423
0
}
424
425
const char* primitives_avc444_frame_type_str(avc444_frame_type type)
426
0
{
427
0
  switch (type)
428
0
  {
429
0
    case AVC444_LUMA:
430
0
      return "AVC444_LUMA";
431
0
    case AVC444_CHROMAv1:
432
0
      return "AVC444_CHROMAv1";
433
0
    case AVC444_CHROMAv2:
434
0
      return "AVC444_CHROMAv2";
435
0
    default:
436
0
      return "INVALID_FRAME_TYPE";
437
0
  }
438
0
}
439
440
const char* primtives_hint_str(primitive_hints hint)
441
0
{
442
0
  switch (hint)
443
0
  {
444
0
    case PRIMITIVES_PURE_SOFT:
445
0
      return "PRIMITIVES_PURE_SOFT";
446
0
    case PRIMITIVES_ONLY_CPU:
447
0
      return "PRIMITIVES_ONLY_CPU";
448
0
    case PRIMITIVES_ONLY_GPU:
449
0
      return "PRIMITIVES_ONLY_GPU";
450
0
    case PRIMITIVES_AUTODETECT:
451
0
      return "PRIMITIVES_AUTODETECT";
452
0
    default:
453
0
      return "PRIMITIVES_UNKNOWN";
454
0
  }
455
0
}