Coverage Report

Created: 2023-09-25 06:56

/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
typedef struct
113
{
114
  BYTE* channels[3];
115
  UINT32 steps[3];
116
  prim_size_t roi;
117
  BYTE* outputBuffer;
118
  UINT32 outputStride;
119
  UINT32 testedFormat;
120
} primitives_YUV_benchmark;
121
122
static void primitives_YUV_benchmark_free(primitives_YUV_benchmark* bench)
123
0
{
124
0
  int i;
125
0
  if (!bench)
126
0
    return;
127
128
0
  free(bench->outputBuffer);
129
130
0
  for (i = 0; i < 3; i++)
131
0
    free(bench->channels[i]);
132
0
  memset(bench, 0, sizeof(primitives_YUV_benchmark));
133
0
}
134
135
static primitives_YUV_benchmark* primitives_YUV_benchmark_init(primitives_YUV_benchmark* ret)
136
0
{
137
0
  int i;
138
0
  prim_size_t* roi;
139
0
  if (!ret)
140
0
    return NULL;
141
142
0
  memset(ret, 0, sizeof(primitives_YUV_benchmark));
143
0
  roi = &ret->roi;
144
0
  roi->width = 1024;
145
0
  roi->height = 768;
146
0
  ret->outputStride = roi->width * 4;
147
0
  ret->testedFormat = PIXEL_FORMAT_BGRA32;
148
149
0
  ret->outputBuffer = calloc(ret->outputStride, roi->height);
150
0
  if (!ret->outputBuffer)
151
0
    goto fail;
152
153
0
  for (i = 0; i < 3; i++)
154
0
  {
155
0
    BYTE* buf = ret->channels[i] = calloc(roi->width, roi->height);
156
0
    if (!buf)
157
0
      goto fail;
158
159
0
    winpr_RAND(buf, 1ull * roi->width * roi->height);
160
0
    ret->steps[i] = roi->width;
161
0
  }
162
163
0
  return ret;
164
165
0
fail:
166
0
  primitives_YUV_benchmark_free(ret);
167
0
  return ret;
168
0
}
169
170
static BOOL primitives_YUV_benchmark_run(primitives_YUV_benchmark* bench, primitives_t* prims,
171
                                         UINT64 runTime, UINT32* computations)
172
0
{
173
0
  ULONGLONG dueDate;
174
0
  const BYTE* channels[3] = { 0 };
175
0
  size_t i;
176
0
  pstatus_t status;
177
178
0
  *computations = 0;
179
180
0
  for (i = 0; i < 3; i++)
181
0
    channels[i] = bench->channels[i];
182
183
  /* do a first dry run to initialize cache and such */
184
0
  status = prims->YUV420ToRGB_8u_P3AC4R(channels, bench->steps, bench->outputBuffer,
185
0
                                        bench->outputStride, bench->testedFormat, &bench->roi);
186
0
  if (status != PRIMITIVES_SUCCESS)
187
0
    return FALSE;
188
189
  /* let's run the benchmark */
190
0
  dueDate = GetTickCount64() + runTime;
191
0
  while (GetTickCount64() < dueDate)
192
0
  {
193
0
    pstatus_t cstatus =
194
0
        prims->YUV420ToRGB_8u_P3AC4R(channels, bench->steps, bench->outputBuffer,
195
0
                                     bench->outputStride, bench->testedFormat, &bench->roi);
196
0
    if (cstatus != PRIMITIVES_SUCCESS)
197
0
      return FALSE;
198
0
    *computations = *computations + 1;
199
0
  }
200
0
  return TRUE;
201
0
}
202
203
static BOOL primitives_autodetect_best(primitives_t* prims)
204
0
{
205
0
  size_t x;
206
0
  BOOL ret = FALSE;
207
0
  UINT64 benchDuration = 150; /* 150 ms */
208
0
  struct prim_benchmark
209
0
  {
210
0
    const char* name;
211
0
    primitives_t* prims;
212
0
    UINT32 flags;
213
0
    UINT32 count;
214
0
  };
215
216
0
  struct prim_benchmark testcases[] =
217
0
  {
218
0
    { "generic", NULL, PRIMITIVES_PURE_SOFT, 0 },
219
0
#if defined(HAVE_CPU_OPTIMIZED_PRIMITIVES)
220
0
    { "optimized", NULL, PRIMITIVES_ONLY_CPU, 0 },
221
0
#endif
222
#if defined(WITH_OPENCL)
223
    { "opencl", NULL, PRIMITIVES_ONLY_GPU, 0 },
224
#endif
225
0
  };
226
0
  const struct prim_benchmark* best = NULL;
227
228
0
  primitives_YUV_benchmark bench;
229
0
  primitives_YUV_benchmark* yuvBench = primitives_YUV_benchmark_init(&bench);
230
0
  if (!yuvBench)
231
0
    return FALSE;
232
233
0
  WLog_DBG(TAG, "primitives benchmark result:");
234
0
  for (x = 0; x < ARRAYSIZE(testcases); x++)
235
0
  {
236
0
    struct prim_benchmark* cur = &testcases[x];
237
0
    cur->prims = primitives_get_by_type(cur->flags);
238
0
    if (!cur->prims)
239
0
    {
240
0
      WLog_WARN(TAG, "Failed to initialize %s primitives", cur->name);
241
0
      continue;
242
0
    }
243
0
    if (!primitives_YUV_benchmark_run(yuvBench, cur->prims, benchDuration, &cur->count))
244
0
    {
245
0
      WLog_WARN(TAG, "error running %s YUV bench", cur->name);
246
0
      continue;
247
0
    }
248
249
0
    WLog_DBG(TAG, " * %s= %" PRIu32, cur->name, cur->count);
250
0
    if (!best || (best->count < cur->count))
251
0
      best = cur;
252
0
  }
253
254
0
  if (!best)
255
0
  {
256
0
    WLog_ERR(TAG, "No primitives to test, aborting.");
257
0
    goto out;
258
0
  }
259
  /* finally compute the results */
260
0
  *prims = *best->prims;
261
262
0
  WLog_DBG(TAG, "primitives autodetect, using %s", best->name);
263
0
  ret = TRUE;
264
0
out:
265
0
  if (!ret)
266
0
    *prims = pPrimitivesGeneric;
267
0
  primitives_YUV_benchmark_free(yuvBench);
268
0
  return ret;
269
0
}
270
271
#if defined(WITH_OPENCL)
272
static BOOL CALLBACK primitives_init_gpu_cb(PINIT_ONCE once, PVOID param, PVOID* context)
273
{
274
  WINPR_UNUSED(once);
275
  WINPR_UNUSED(param);
276
  WINPR_UNUSED(context);
277
278
  if (!primitives_init_opencl(&pPrimitivesGpu))
279
    return FALSE;
280
281
  return TRUE;
282
}
283
#endif
284
285
#if defined(HAVE_CPU_OPTIMIZED_PRIMITIVES)
286
static BOOL CALLBACK primitives_init_cpu_cb(PINIT_ONCE once, PVOID param, PVOID* context)
287
0
{
288
0
  WINPR_UNUSED(once);
289
0
  WINPR_UNUSED(param);
290
0
  WINPR_UNUSED(context);
291
292
0
  if (!primitives_init_optimized(&pPrimitivesCpu))
293
0
    return FALSE;
294
295
0
  return TRUE;
296
0
}
297
#endif
298
299
static BOOL CALLBACK primitives_auto_init_cb(PINIT_ONCE once, PVOID param, PVOID* context)
300
0
{
301
0
  WINPR_UNUSED(once);
302
0
  WINPR_UNUSED(param);
303
0
  WINPR_UNUSED(context);
304
305
0
  return primitives_init(&pPrimitives, primitivesHints);
306
0
}
307
308
BOOL primitives_init(primitives_t* p, primitive_hints hints)
309
0
{
310
0
  switch (hints)
311
0
  {
312
0
    case PRIMITIVES_AUTODETECT:
313
0
      return primitives_autodetect_best(p);
314
0
    case PRIMITIVES_PURE_SOFT:
315
0
      *p = pPrimitivesGeneric;
316
0
      return TRUE;
317
0
    case PRIMITIVES_ONLY_CPU:
318
0
#if defined(HAVE_CPU_OPTIMIZED_PRIMITIVES)
319
0
      *p = pPrimitivesCpu;
320
0
      return TRUE;
321
0
#endif
322
0
    case PRIMITIVES_ONLY_GPU:
323
#if defined(WITH_OPENCL)
324
      *p = pPrimitivesGpu;
325
      return TRUE;
326
#endif
327
0
    default:
328
0
      WLog_ERR(TAG, "unknown hint %d", hints);
329
0
      return FALSE;
330
0
  }
331
0
}
332
333
void primitives_uninit(void)
334
0
{
335
#if defined(WITH_OPENCL)
336
  if (pPrimitivesGpu.uninit)
337
    pPrimitivesGpu.uninit();
338
#endif
339
0
#if defined(HAVE_CPU_OPTIMIZED_PRIMITIVES)
340
0
  if (pPrimitivesCpu.uninit)
341
0
    pPrimitivesCpu.uninit();
342
0
#endif
343
0
  if (pPrimitivesGeneric.uninit)
344
0
    pPrimitivesGeneric.uninit();
345
0
}
346
347
/* ------------------------------------------------------------------------- */
348
static void setup(void)
349
0
{
350
0
  InitOnceExecuteOnce(&generic_primitives_InitOnce, primitives_init_generic_cb, NULL, NULL);
351
0
#if defined(HAVE_CPU_OPTIMIZED_PRIMITIVES)
352
0
  InitOnceExecuteOnce(&cpu_primitives_InitOnce, primitives_init_cpu_cb, NULL, NULL);
353
0
#endif
354
#if defined(WITH_OPENCL)
355
  InitOnceExecuteOnce(&gpu_primitives_InitOnce, primitives_init_gpu_cb, NULL, NULL);
356
#endif
357
0
  InitOnceExecuteOnce(&auto_primitives_InitOnce, primitives_auto_init_cb, NULL, NULL);
358
0
}
359
360
primitives_t* primitives_get(void)
361
0
{
362
0
  setup();
363
0
  return &pPrimitives;
364
0
}
365
366
primitives_t* primitives_get_generic(void)
367
0
{
368
0
  InitOnceExecuteOnce(&generic_primitives_InitOnce, primitives_init_generic_cb, NULL, NULL);
369
0
  return &pPrimitivesGeneric;
370
0
}
371
372
primitives_t* primitives_get_by_type(DWORD type)
373
0
{
374
0
  InitOnceExecuteOnce(&generic_primitives_InitOnce, primitives_init_generic_cb, NULL, NULL);
375
376
0
  switch (type)
377
0
  {
378
0
    case PRIMITIVES_ONLY_GPU:
379
#if defined(WITH_OPENCL)
380
      if (!InitOnceExecuteOnce(&gpu_primitives_InitOnce, primitives_init_gpu_cb, NULL, NULL))
381
        return NULL;
382
      return &pPrimitivesGpu;
383
#endif
384
0
    case PRIMITIVES_ONLY_CPU:
385
0
#if defined(HAVE_CPU_OPTIMIZED_PRIMITIVES)
386
0
      if (!InitOnceExecuteOnce(&cpu_primitives_InitOnce, primitives_init_cpu_cb, NULL, NULL))
387
0
        return NULL;
388
0
      return &pPrimitivesCpu;
389
0
#endif
390
0
    case PRIMITIVES_PURE_SOFT:
391
0
    default:
392
0
      return &pPrimitivesGeneric;
393
0
  }
394
0
}
395
396
DWORD primitives_flags(primitives_t* p)
397
0
{
398
0
  return p->flags;
399
0
}