Coverage Report

Created: 2024-09-08 06:20

/src/FreeRDP/libfreerdp/codec/sse/rfx_sse2.c
Line
Count
Source
1
/**
2
 * FreeRDP: A Remote Desktop Protocol Implementation
3
 * RemoteFX Codec Library - SSE2 Optimizations
4
 *
5
 * Copyright 2011 Stephen Erisman
6
 * Copyright 2011 Norbert Federa <norbert.federa@thincast.com>
7
 *
8
 * Licensed under the Apache License, Version 2.0 (the "License");
9
 * you may not use this file except in compliance with the License.
10
 * You may obtain a copy of the License at
11
 *
12
 *     http://www.apache.org/licenses/LICENSE-2.0
13
 *
14
 * Unless required by applicable law or agreed to in writing, software
15
 * distributed under the License is distributed on an "AS IS" BASIS,
16
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17
 * See the License for the specific language governing permissions and
18
 * limitations under the License.
19
 */
20
21
#include <winpr/platform.h>
22
#include <freerdp/config.h>
23
#include <freerdp/log.h>
24
25
#include "../rfx_types.h"
26
#include "rfx_sse2.h"
27
28
#define TAG FREERDP_TAG("codec.rfx.sse2")
29
30
#if defined(WITH_SSE2)
31
#if defined(_M_IX86) || defined(_M_AMD64) || defined(_M_IA64) || defined(_M_IX86_AMD64)
32
#define SSE2_ENABLED
33
#endif
34
#endif
35
36
#if defined(SSE2_ENABLED)
37
#include <stdio.h>
38
#include <stdlib.h>
39
#include <string.h>
40
#include <winpr/sysinfo.h>
41
42
#include <xmmintrin.h>
43
#include <emmintrin.h>
44
45
#ifdef _MSC_VER
46
#define __attribute__(...)
47
#endif
48
49
#define CACHE_LINE_BYTES 64
50
51
#ifndef __clang__
52
#define ATTRIBUTES __gnu_inline__, __always_inline__, __artificial__
53
#else
54
#define ATTRIBUTES __gnu_inline__, __always_inline__
55
#endif
56
57
#define mm_between_epi16(_val, _min, _max)                       \
58
  do                                                           \
59
  {                                                            \
60
    (_val) = _mm_min_epi16(_max, _mm_max_epi16(_val, _min)); \
61
  } while (0)
62
63
static __inline void __attribute__((ATTRIBUTES))
64
mm_prefetch_buffer(char* WINPR_RESTRICT buffer, size_t num_bytes)
65
{
66
  __m128i* buf = (__m128i*)buffer;
67
68
  for (size_t i = 0; i < (num_bytes / sizeof(__m128i)); i += (CACHE_LINE_BYTES / sizeof(__m128i)))
69
  {
70
    _mm_prefetch((char*)(&buf[i]), _MM_HINT_NTA);
71
  }
72
}
73
74
/* rfx_decode_ycbcr_to_rgb_sse2 code now resides in the primitives library. */
75
/* rfx_encode_rgb_to_ycbcr_sse2 code now resides in the primitives library. */
76
77
static __inline void __attribute__((ATTRIBUTES))
78
rfx_quantization_decode_block_sse2(INT16* WINPR_RESTRICT buffer, const size_t buffer_size,
79
                                   const UINT32 factor)
80
{
81
  __m128i a;
82
  __m128i* ptr = (__m128i*)buffer;
83
  __m128i* buf_end = (__m128i*)(buffer + buffer_size);
84
85
  if (factor == 0)
86
    return;
87
88
  do
89
  {
90
    a = _mm_load_si128(ptr);
91
    a = _mm_slli_epi16(a, factor);
92
    _mm_store_si128(ptr, a);
93
    ptr++;
94
  } while (ptr < buf_end);
95
}
96
97
static void rfx_quantization_decode_sse2(INT16* WINPR_RESTRICT buffer,
98
                                         const UINT32* WINPR_RESTRICT quantVals)
99
{
100
  WINPR_ASSERT(buffer);
101
  WINPR_ASSERT(quantVals);
102
103
  mm_prefetch_buffer((char*)buffer, 4096 * sizeof(INT16));
104
  rfx_quantization_decode_block_sse2(&buffer[0], 1024, quantVals[8] - 1);    /* HL1 */
105
  rfx_quantization_decode_block_sse2(&buffer[1024], 1024, quantVals[7] - 1); /* LH1 */
106
  rfx_quantization_decode_block_sse2(&buffer[2048], 1024, quantVals[9] - 1); /* HH1 */
107
  rfx_quantization_decode_block_sse2(&buffer[3072], 256, quantVals[5] - 1);  /* HL2 */
108
  rfx_quantization_decode_block_sse2(&buffer[3328], 256, quantVals[4] - 1);  /* LH2 */
109
  rfx_quantization_decode_block_sse2(&buffer[3584], 256, quantVals[6] - 1);  /* HH2 */
110
  rfx_quantization_decode_block_sse2(&buffer[3840], 64, quantVals[2] - 1);   /* HL3 */
111
  rfx_quantization_decode_block_sse2(&buffer[3904], 64, quantVals[1] - 1);   /* LH3 */
112
  rfx_quantization_decode_block_sse2(&buffer[3968], 64, quantVals[3] - 1);   /* HH3 */
113
  rfx_quantization_decode_block_sse2(&buffer[4032], 64, quantVals[0] - 1);   /* LL3 */
114
}
115
116
static __inline void __attribute__((ATTRIBUTES))
117
rfx_quantization_encode_block_sse2(INT16* WINPR_RESTRICT buffer, const int buffer_size,
118
                                   const UINT32 factor)
119
{
120
  __m128i a;
121
  __m128i* ptr = (__m128i*)buffer;
122
  __m128i* buf_end = (__m128i*)(buffer + buffer_size);
123
  __m128i half;
124
125
  if (factor == 0)
126
    return;
127
128
  half = _mm_set1_epi16(1 << (factor - 1));
129
130
  do
131
  {
132
    a = _mm_load_si128(ptr);
133
    a = _mm_add_epi16(a, half);
134
    a = _mm_srai_epi16(a, factor);
135
    _mm_store_si128(ptr, a);
136
    ptr++;
137
  } while (ptr < buf_end);
138
}
139
140
static void rfx_quantization_encode_sse2(INT16* WINPR_RESTRICT buffer,
141
                                         const UINT32* WINPR_RESTRICT quantization_values)
142
{
143
  WINPR_ASSERT(buffer);
144
  WINPR_ASSERT(quantization_values);
145
146
  mm_prefetch_buffer((char*)buffer, 4096 * sizeof(INT16));
147
  rfx_quantization_encode_block_sse2(buffer, 1024, quantization_values[8] - 6);        /* HL1 */
148
  rfx_quantization_encode_block_sse2(buffer + 1024, 1024, quantization_values[7] - 6); /* LH1 */
149
  rfx_quantization_encode_block_sse2(buffer + 2048, 1024, quantization_values[9] - 6); /* HH1 */
150
  rfx_quantization_encode_block_sse2(buffer + 3072, 256, quantization_values[5] - 6);  /* HL2 */
151
  rfx_quantization_encode_block_sse2(buffer + 3328, 256, quantization_values[4] - 6);  /* LH2 */
152
  rfx_quantization_encode_block_sse2(buffer + 3584, 256, quantization_values[6] - 6);  /* HH2 */
153
  rfx_quantization_encode_block_sse2(buffer + 3840, 64, quantization_values[2] - 6);   /* HL3 */
154
  rfx_quantization_encode_block_sse2(buffer + 3904, 64, quantization_values[1] - 6);   /* LH3 */
155
  rfx_quantization_encode_block_sse2(buffer + 3968, 64, quantization_values[3] - 6);   /* HH3 */
156
  rfx_quantization_encode_block_sse2(buffer + 4032, 64, quantization_values[0] - 6);   /* LL3 */
157
  rfx_quantization_encode_block_sse2(buffer, 4096, 5);
158
}
159
160
static __inline void __attribute__((ATTRIBUTES))
161
rfx_dwt_2d_decode_block_horiz_sse2(INT16* WINPR_RESTRICT l, INT16* WINPR_RESTRICT h,
162
                                   INT16* WINPR_RESTRICT dst, int subband_width)
163
{
164
  INT16* l_ptr = l;
165
  INT16* h_ptr = h;
166
  INT16* dst_ptr = dst;
167
  int first = 0;
168
  int last = 0;
169
  __m128i l_n;
170
  __m128i h_n;
171
  __m128i h_n_m;
172
  __m128i tmp_n;
173
  __m128i dst_n;
174
  __m128i dst_n_p;
175
  __m128i dst1;
176
  __m128i dst2;
177
178
  for (int y = 0; y < subband_width; y++)
179
  {
180
    /* Even coefficients */
181
    for (int n = 0; n < subband_width; n += 8)
182
    {
183
      /* dst[2n] = l[n] - ((h[n-1] + h[n] + 1) >> 1); */
184
      l_n = _mm_load_si128((__m128i*)l_ptr);
185
      h_n = _mm_load_si128((__m128i*)h_ptr);
186
      h_n_m = _mm_loadu_si128((__m128i*)(h_ptr - 1));
187
188
      if (n == 0)
189
      {
190
        first = _mm_extract_epi16(h_n_m, 1);
191
        h_n_m = _mm_insert_epi16(h_n_m, first, 0);
192
      }
193
194
      tmp_n = _mm_add_epi16(h_n, h_n_m);
195
      tmp_n = _mm_add_epi16(tmp_n, _mm_set1_epi16(1));
196
      tmp_n = _mm_srai_epi16(tmp_n, 1);
197
      dst_n = _mm_sub_epi16(l_n, tmp_n);
198
      _mm_store_si128((__m128i*)l_ptr, dst_n);
199
      l_ptr += 8;
200
      h_ptr += 8;
201
    }
202
203
    l_ptr -= subband_width;
204
    h_ptr -= subband_width;
205
206
    /* Odd coefficients */
207
    for (int n = 0; n < subband_width; n += 8)
208
    {
209
      /* dst[2n + 1] = (h[n] << 1) + ((dst[2n] + dst[2n + 2]) >> 1); */
210
      h_n = _mm_load_si128((__m128i*)h_ptr);
211
      h_n = _mm_slli_epi16(h_n, 1);
212
      dst_n = _mm_load_si128((__m128i*)(l_ptr));
213
      dst_n_p = _mm_loadu_si128((__m128i*)(l_ptr + 1));
214
215
      if (n == subband_width - 8)
216
      {
217
        last = _mm_extract_epi16(dst_n_p, 6);
218
        dst_n_p = _mm_insert_epi16(dst_n_p, last, 7);
219
      }
220
221
      tmp_n = _mm_add_epi16(dst_n_p, dst_n);
222
      tmp_n = _mm_srai_epi16(tmp_n, 1);
223
      tmp_n = _mm_add_epi16(tmp_n, h_n);
224
      dst1 = _mm_unpacklo_epi16(dst_n, tmp_n);
225
      dst2 = _mm_unpackhi_epi16(dst_n, tmp_n);
226
      _mm_store_si128((__m128i*)dst_ptr, dst1);
227
      _mm_store_si128((__m128i*)(dst_ptr + 8), dst2);
228
      l_ptr += 8;
229
      h_ptr += 8;
230
      dst_ptr += 16;
231
    }
232
  }
233
}
234
235
static __inline void __attribute__((ATTRIBUTES))
236
rfx_dwt_2d_decode_block_vert_sse2(INT16* WINPR_RESTRICT l, INT16* WINPR_RESTRICT h,
237
                                  INT16* WINPR_RESTRICT dst, int subband_width)
238
{
239
  INT16* l_ptr = l;
240
  INT16* h_ptr = h;
241
  INT16* dst_ptr = dst;
242
  __m128i l_n;
243
  __m128i h_n;
244
  __m128i tmp_n;
245
  __m128i h_n_m;
246
  __m128i dst_n;
247
  __m128i dst_n_m;
248
  __m128i dst_n_p;
249
  int total_width = subband_width + subband_width;
250
251
  /* Even coefficients */
252
  for (int n = 0; n < subband_width; n++)
253
  {
254
    for (int x = 0; x < total_width; x += 8)
255
    {
256
      /* dst[2n] = l[n] - ((h[n-1] + h[n] + 1) >> 1); */
257
      l_n = _mm_load_si128((__m128i*)l_ptr);
258
      h_n = _mm_load_si128((__m128i*)h_ptr);
259
      tmp_n = _mm_add_epi16(h_n, _mm_set1_epi16(1));
260
261
      if (n == 0)
262
        tmp_n = _mm_add_epi16(tmp_n, h_n);
263
      else
264
      {
265
        h_n_m = _mm_loadu_si128((__m128i*)(h_ptr - total_width));
266
        tmp_n = _mm_add_epi16(tmp_n, h_n_m);
267
      }
268
269
      tmp_n = _mm_srai_epi16(tmp_n, 1);
270
      dst_n = _mm_sub_epi16(l_n, tmp_n);
271
      _mm_store_si128((__m128i*)dst_ptr, dst_n);
272
      l_ptr += 8;
273
      h_ptr += 8;
274
      dst_ptr += 8;
275
    }
276
277
    dst_ptr += total_width;
278
  }
279
280
  h_ptr = h;
281
  dst_ptr = dst + total_width;
282
283
  /* Odd coefficients */
284
  for (int n = 0; n < subband_width; n++)
285
  {
286
    for (int x = 0; x < total_width; x += 8)
287
    {
288
      /* dst[2n + 1] = (h[n] << 1) + ((dst[2n] + dst[2n + 2]) >> 1); */
289
      h_n = _mm_load_si128((__m128i*)h_ptr);
290
      dst_n_m = _mm_load_si128((__m128i*)(dst_ptr - total_width));
291
      h_n = _mm_slli_epi16(h_n, 1);
292
      tmp_n = dst_n_m;
293
294
      if (n == subband_width - 1)
295
        tmp_n = _mm_add_epi16(tmp_n, dst_n_m);
296
      else
297
      {
298
        dst_n_p = _mm_loadu_si128((__m128i*)(dst_ptr + total_width));
299
        tmp_n = _mm_add_epi16(tmp_n, dst_n_p);
300
      }
301
302
      tmp_n = _mm_srai_epi16(tmp_n, 1);
303
      dst_n = _mm_add_epi16(tmp_n, h_n);
304
      _mm_store_si128((__m128i*)dst_ptr, dst_n);
305
      h_ptr += 8;
306
      dst_ptr += 8;
307
    }
308
309
    dst_ptr += total_width;
310
  }
311
}
312
313
static __inline void __attribute__((ATTRIBUTES))
314
rfx_dwt_2d_decode_block_sse2(INT16* WINPR_RESTRICT buffer, INT16* WINPR_RESTRICT idwt,
315
                             int subband_width)
316
{
317
  INT16* hl = NULL;
318
  INT16* lh = NULL;
319
  INT16* hh = NULL;
320
  INT16* ll = NULL;
321
  INT16* l_dst = NULL;
322
  INT16* h_dst = NULL;
323
  mm_prefetch_buffer((char*)idwt, 4ULL * subband_width * sizeof(INT16));
324
  /* Inverse DWT in horizontal direction, results in 2 sub-bands in L, H order in tmp buffer idwt.
325
   */
326
  /* The 4 sub-bands are stored in HL(0), LH(1), HH(2), LL(3) order. */
327
  /* The lower part L uses LL(3) and HL(0). */
328
  /* The higher part H uses LH(1) and HH(2). */
329
  ll = buffer + 3ULL * subband_width * subband_width;
330
  hl = buffer;
331
  l_dst = idwt;
332
  rfx_dwt_2d_decode_block_horiz_sse2(ll, hl, l_dst, subband_width);
333
  lh = buffer + 1ULL * subband_width * subband_width;
334
  hh = buffer + 2ULL * subband_width * subband_width;
335
  h_dst = idwt + 2ULL * subband_width * subband_width;
336
  rfx_dwt_2d_decode_block_horiz_sse2(lh, hh, h_dst, subband_width);
337
  /* Inverse DWT in vertical direction, results are stored in original buffer. */
338
  rfx_dwt_2d_decode_block_vert_sse2(l_dst, h_dst, buffer, subband_width);
339
}
340
341
static void rfx_dwt_2d_decode_sse2(INT16* WINPR_RESTRICT buffer, INT16* WINPR_RESTRICT dwt_buffer)
342
{
343
  WINPR_ASSERT(buffer);
344
  WINPR_ASSERT(dwt_buffer);
345
346
  mm_prefetch_buffer((char*)buffer, 4096 * sizeof(INT16));
347
  rfx_dwt_2d_decode_block_sse2(&buffer[3840], dwt_buffer, 8);
348
  rfx_dwt_2d_decode_block_sse2(&buffer[3072], dwt_buffer, 16);
349
  rfx_dwt_2d_decode_block_sse2(&buffer[0], dwt_buffer, 32);
350
}
351
352
static __inline void __attribute__((ATTRIBUTES))
353
rfx_dwt_2d_encode_block_vert_sse2(INT16* WINPR_RESTRICT src, INT16* WINPR_RESTRICT l,
354
                                  INT16* WINPR_RESTRICT h, int subband_width)
355
{
356
  int total_width = 0;
357
  __m128i src_2n;
358
  __m128i src_2n_1;
359
  __m128i src_2n_2;
360
  __m128i h_n;
361
  __m128i h_n_m;
362
  __m128i l_n;
363
  total_width = subband_width << 1;
364
365
  for (int n = 0; n < subband_width; n++)
366
  {
367
    for (int x = 0; x < total_width; x += 8)
368
    {
369
      src_2n = _mm_load_si128((__m128i*)src);
370
      src_2n_1 = _mm_load_si128((__m128i*)(src + total_width));
371
372
      if (n < subband_width - 1)
373
        src_2n_2 = _mm_load_si128((__m128i*)(src + 2ULL * total_width));
374
      else
375
        src_2n_2 = src_2n;
376
377
      /* h[n] = (src[2n + 1] - ((src[2n] + src[2n + 2]) >> 1)) >> 1 */
378
      h_n = _mm_add_epi16(src_2n, src_2n_2);
379
      h_n = _mm_srai_epi16(h_n, 1);
380
      h_n = _mm_sub_epi16(src_2n_1, h_n);
381
      h_n = _mm_srai_epi16(h_n, 1);
382
      _mm_store_si128((__m128i*)h, h_n);
383
384
      if (n == 0)
385
        h_n_m = h_n;
386
      else
387
        h_n_m = _mm_load_si128((__m128i*)(h - total_width));
388
389
      /* l[n] = src[2n] + ((h[n - 1] + h[n]) >> 1) */
390
      l_n = _mm_add_epi16(h_n_m, h_n);
391
      l_n = _mm_srai_epi16(l_n, 1);
392
      l_n = _mm_add_epi16(l_n, src_2n);
393
      _mm_store_si128((__m128i*)l, l_n);
394
      src += 8;
395
      l += 8;
396
      h += 8;
397
    }
398
399
    src += total_width;
400
  }
401
}
402
403
static __inline void __attribute__((ATTRIBUTES))
404
rfx_dwt_2d_encode_block_horiz_sse2(INT16* WINPR_RESTRICT src, INT16* WINPR_RESTRICT l,
405
                                   INT16* WINPR_RESTRICT h, int subband_width)
406
{
407
  int first = 0;
408
  __m128i src_2n;
409
  __m128i src_2n_1;
410
  __m128i src_2n_2;
411
  __m128i h_n;
412
  __m128i h_n_m;
413
  __m128i l_n;
414
415
  for (int y = 0; y < subband_width; y++)
416
  {
417
    for (int n = 0; n < subband_width; n += 8)
418
    {
419
      /* The following 3 Set operations consumes more than half of the total DWT processing
420
       * time! */
421
      src_2n =
422
          _mm_set_epi16(src[14], src[12], src[10], src[8], src[6], src[4], src[2], src[0]);
423
      src_2n_1 =
424
          _mm_set_epi16(src[15], src[13], src[11], src[9], src[7], src[5], src[3], src[1]);
425
      src_2n_2 = _mm_set_epi16(n == subband_width - 8 ? src[14] : src[16], src[14], src[12],
426
                               src[10], src[8], src[6], src[4], src[2]);
427
      /* h[n] = (src[2n + 1] - ((src[2n] + src[2n + 2]) >> 1)) >> 1 */
428
      h_n = _mm_add_epi16(src_2n, src_2n_2);
429
      h_n = _mm_srai_epi16(h_n, 1);
430
      h_n = _mm_sub_epi16(src_2n_1, h_n);
431
      h_n = _mm_srai_epi16(h_n, 1);
432
      _mm_store_si128((__m128i*)h, h_n);
433
      h_n_m = _mm_loadu_si128((__m128i*)(h - 1));
434
435
      if (n == 0)
436
      {
437
        first = _mm_extract_epi16(h_n_m, 1);
438
        h_n_m = _mm_insert_epi16(h_n_m, first, 0);
439
      }
440
441
      /* l[n] = src[2n] + ((h[n - 1] + h[n]) >> 1) */
442
      l_n = _mm_add_epi16(h_n_m, h_n);
443
      l_n = _mm_srai_epi16(l_n, 1);
444
      l_n = _mm_add_epi16(l_n, src_2n);
445
      _mm_store_si128((__m128i*)l, l_n);
446
      src += 16;
447
      l += 8;
448
      h += 8;
449
    }
450
  }
451
}
452
453
static __inline void __attribute__((ATTRIBUTES))
454
rfx_dwt_2d_encode_block_sse2(INT16* WINPR_RESTRICT buffer, INT16* WINPR_RESTRICT dwt,
455
                             int subband_width)
456
{
457
  INT16* hl = NULL;
458
  INT16* lh = NULL;
459
  INT16* hh = NULL;
460
  INT16* ll = NULL;
461
  INT16* l_src = NULL;
462
  INT16* h_src = NULL;
463
  mm_prefetch_buffer((char*)dwt, 4ULL * subband_width * sizeof(INT16));
464
  /* DWT in vertical direction, results in 2 sub-bands in L, H order in tmp buffer dwt. */
465
  l_src = dwt;
466
  h_src = dwt + 2ULL * subband_width * subband_width;
467
  rfx_dwt_2d_encode_block_vert_sse2(buffer, l_src, h_src, subband_width);
468
  /* DWT in horizontal direction, results in 4 sub-bands in HL(0), LH(1), HH(2), LL(3) order,
469
   * stored in original buffer. */
470
  /* The lower part L generates LL(3) and HL(0). */
471
  /* The higher part H generates LH(1) and HH(2). */
472
  ll = buffer + 3ULL * subband_width * subband_width;
473
  hl = buffer;
474
  lh = buffer + 1ULL * subband_width * subband_width;
475
  hh = buffer + 2ULL * subband_width * subband_width;
476
  rfx_dwt_2d_encode_block_horiz_sse2(l_src, ll, hl, subband_width);
477
  rfx_dwt_2d_encode_block_horiz_sse2(h_src, lh, hh, subband_width);
478
}
479
480
static void rfx_dwt_2d_encode_sse2(INT16* WINPR_RESTRICT buffer, INT16* WINPR_RESTRICT dwt_buffer)
481
{
482
  WINPR_ASSERT(buffer);
483
  WINPR_ASSERT(dwt_buffer);
484
485
  mm_prefetch_buffer((char*)buffer, 4096 * sizeof(INT16));
486
  rfx_dwt_2d_encode_block_sse2(buffer, dwt_buffer, 32);
487
  rfx_dwt_2d_encode_block_sse2(buffer + 3072, dwt_buffer, 16);
488
  rfx_dwt_2d_encode_block_sse2(buffer + 3840, dwt_buffer, 8);
489
}
490
#endif
491
492
void rfx_init_sse2(RFX_CONTEXT* context)
493
10.8k
{
494
#if defined(SSE2_ENABLED)
495
  if (!IsProcessorFeaturePresent(PF_XMMI64_INSTRUCTIONS_AVAILABLE))
496
    return;
497
498
  PROFILER_RENAME(context->priv->prof_rfx_quantization_decode, "rfx_quantization_decode_sse2")
499
  PROFILER_RENAME(context->priv->prof_rfx_quantization_encode, "rfx_quantization_encode_sse2")
500
  PROFILER_RENAME(context->priv->prof_rfx_dwt_2d_decode, "rfx_dwt_2d_decode_sse2")
501
  PROFILER_RENAME(context->priv->prof_rfx_dwt_2d_encode, "rfx_dwt_2d_encode_sse2")
502
  context->quantization_decode = rfx_quantization_decode_sse2;
503
  context->quantization_encode = rfx_quantization_encode_sse2;
504
  context->dwt_2d_decode = rfx_dwt_2d_decode_sse2;
505
  context->dwt_2d_encode = rfx_dwt_2d_encode_sse2;
506
#else
507
10.8k
  WINPR_UNUSED(context);
508
10.8k
#endif
509
10.8k
}