Coverage Report

Created: 2026-09-14 06:32

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/FreeRDP/libfreerdp/primitives/sse/prim_copy_avx2.c
Line
Count
Source
1
/* FreeRDP: A Remote Desktop Protocol Client
2
 * Copy operations.
3
 * vi:ts=4 sw=4:
4
 *
5
 * (c) Copyright 2012 Hewlett-Packard Development Company, L.P.
6
 * Licensed under the Apache License, Version 2.0 (the "License"); you may
7
 * not use this file except in compliance with the License. You may obtain
8
 * a copy of the License at http://www.apache.org/licenses/LICENSE-2.0.
9
 * Unless required by applicable law or agreed to in writing, software
10
 * distributed under the License is distributed on an "AS IS" BASIS,
11
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
12
 * or implied. See the License for the specific language governing
13
 * permissions and limitations under the License.
14
 */
15
16
#include <winpr/sysinfo.h>
17
18
#include <freerdp/config.h>
19
20
#include <string.h>
21
#include <freerdp/types.h>
22
#include <freerdp/primitives.h>
23
#include <freerdp/log.h>
24
25
#include "prim_internal.h"
26
#include "prim_copy.h"
27
#include "../codec/color.h"
28
29
#include <freerdp/codec/color.h>
30
31
#if defined(SSE_AVX_INTRINSICS_ENABLED)
32
#include <emmintrin.h>
33
#include <immintrin.h>
34
35
static inline __m256i mm256_set_epu32(uint32_t i0, uint32_t i1, uint32_t i2, uint32_t i3,
36
                                      uint32_t i4, uint32_t i5, uint32_t i6, uint32_t i7)
37
0
{
38
0
  return _mm256_set_epi32((int32_t)i0, (int32_t)i1, (int32_t)i2, (int32_t)i3, (int32_t)i4,
39
0
                          (int32_t)i5, (int32_t)i6, (int32_t)i7);
40
0
}
41
42
static inline pstatus_t avx2_image_copy_bgr24_bgrx32(BYTE* WINPR_RESTRICT pDstData, UINT32 nDstStep,
43
                                                     UINT32 nXDst, UINT32 nYDst, UINT32 nWidth,
44
                                                     UINT32 nHeight,
45
                                                     const BYTE* WINPR_RESTRICT pSrcData,
46
                                                     UINT32 nSrcStep, UINT32 nXSrc, UINT32 nYSrc,
47
                                                     int64_t srcVMultiplier, int64_t srcVOffset,
48
                                                     int64_t dstVMultiplier, int64_t dstVOffset)
49
0
{
50
51
0
  const int64_t srcByte = 3;
52
0
  const int64_t dstByte = 4;
53
54
0
  const __m256i mask = mm256_set_epu32(0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000,
55
0
                                       0xFF000000, 0xFF000000, 0xFF000000);
56
0
  const __m256i smask = mm256_set_epu32(0xff171615, 0xff141312, 0xff1110ff, 0xffffffff,
57
0
                                        0xff0b0a09, 0xff080706, 0xff050403, 0xff020100);
58
0
  const __m256i shelpmask = mm256_set_epu32(0xffffffff, 0xffffffff, 0xffffff1f, 0xff1e1d1c,
59
0
                                            0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff);
60
0
  const UINT32 rem = nWidth % 8;
61
0
  const int64_t width = nWidth - rem;
62
63
0
  for (int64_t y = 0; y < nHeight; y++)
64
0
  {
65
0
    const BYTE* WINPR_RESTRICT srcLine =
66
0
        &pSrcData[srcVMultiplier * (y + nYSrc) * nSrcStep + srcVOffset];
67
0
    BYTE* WINPR_RESTRICT dstLine =
68
0
        &pDstData[dstVMultiplier * (y + nYDst) * nDstStep + dstVOffset];
69
70
0
    int64_t x = 0;
71
72
    /* Ensure alignment requirements can be met */
73
0
    for (; x < width; x += 8)
74
0
    {
75
0
      const __m256i* src =
76
0
          WINPR_PACKED_ALIGN_CAST(const __m256i*, &srcLine[(x + nXSrc) * srcByte]);
77
0
      __m256i* dst = WINPR_PACKED_ALIGN_CAST(__m256i*, &dstLine[(x + nXDst) * dstByte]);
78
0
      const __m256i s0 = _mm256_loadu_si256(src);
79
0
      __m256i s1 = _mm256_shuffle_epi8(s0, smask);
80
81
      /* _mm256_shuffle_epi8 can not cross 128bit lanes.
82
       * manually copy these bytes with extract/insert */
83
0
      const __m256i sx = _mm256_broadcastsi128_si256(_mm256_extractf128_si256(s0, 0));
84
0
      const __m256i sxx = _mm256_shuffle_epi8(sx, shelpmask);
85
0
      const __m256i bmask = _mm256_set_epi32(0x00000000, 0x00000000, 0x000000FF, 0x00FFFFFF,
86
0
                                             0x00000000, 0x00000000, 0x00000000, 0x00000000);
87
0
      const __m256i merged = _mm256_blendv_epi8(s1, sxx, bmask);
88
89
0
      const __m256i s2 = _mm256_loadu_si256(dst);
90
0
      __m256i d0 = _mm256_blendv_epi8(merged, s2, mask);
91
0
      _mm256_storeu_si256(dst, d0);
92
0
    }
93
94
0
    for (; x < nWidth; x++)
95
0
    {
96
0
      const BYTE* src = &srcLine[(x + nXSrc) * srcByte];
97
0
      BYTE* dst = &dstLine[(x + nXDst) * dstByte];
98
0
      *dst++ = *src++;
99
0
      *dst++ = *src++;
100
0
      *dst++ = *src++;
101
0
    }
102
0
  }
103
104
0
  return PRIMITIVES_SUCCESS;
105
0
}
106
107
static inline pstatus_t avx2_image_copy_bgrx32_bgrx32(BYTE* WINPR_RESTRICT pDstData,
108
                                                      UINT32 nDstStep, UINT32 nXDst, UINT32 nYDst,
109
                                                      UINT32 nWidth, UINT32 nHeight,
110
                                                      const BYTE* WINPR_RESTRICT pSrcData,
111
                                                      UINT32 nSrcStep, UINT32 nXSrc, UINT32 nYSrc,
112
                                                      int64_t srcVMultiplier, int64_t srcVOffset,
113
                                                      int64_t dstVMultiplier, int64_t dstVOffset)
114
0
{
115
116
0
  const int64_t srcByte = 4;
117
0
  const int64_t dstByte = 4;
118
119
0
  const __m256i mask = _mm256_setr_epi8(
120
0
      (char)0xFF, (char)0xFF, (char)0xFF, 0x00, (char)0xFF, (char)0xFF, (char)0xFF, 0x00,
121
0
      (char)0xFF, (char)0xFF, (char)0xFF, 0x00, (char)0xFF, (char)0xFF, (char)0xFF, 0x00,
122
0
      (char)0xFF, (char)0xFF, (char)0xFF, 0x00, (char)0xFF, (char)0xFF, (char)0xFF, 0x00,
123
0
      (char)0xFF, (char)0xFF, (char)0xFF, 0x00, (char)0xFF, (char)0xFF, (char)0xFF, 0x00);
124
0
  const UINT32 rem = nWidth % 8;
125
0
  const int64_t width = nWidth - rem;
126
0
  for (int64_t y = 0; y < nHeight; y++)
127
0
  {
128
0
    const BYTE* WINPR_RESTRICT srcLine =
129
0
        &pSrcData[srcVMultiplier * (y + nYSrc) * nSrcStep + srcVOffset];
130
0
    BYTE* WINPR_RESTRICT dstLine =
131
0
        &pDstData[dstVMultiplier * (y + nYDst) * nDstStep + dstVOffset];
132
133
0
    int64_t x = 0;
134
0
    for (; x < width; x += 8)
135
0
    {
136
0
      const __m256i* src =
137
0
          WINPR_PACKED_ALIGN_CAST(const __m256i*, &srcLine[(x + nXSrc) * srcByte]);
138
0
      __m256i* dst = WINPR_PACKED_ALIGN_CAST(__m256i*, &dstLine[(x + nXDst) * dstByte]);
139
0
      const __m256i s0 = _mm256_loadu_si256(src);
140
0
      const __m256i s1 = _mm256_loadu_si256(dst);
141
0
      __m256i d0 = _mm256_blendv_epi8(s1, s0, mask);
142
0
      _mm256_storeu_si256(dst, d0);
143
0
    }
144
145
0
    for (; x < nWidth; x++)
146
0
    {
147
0
      const BYTE* src = &srcLine[(x + nXSrc) * srcByte];
148
0
      BYTE* dst = &dstLine[(x + nXDst) * dstByte];
149
0
      *dst++ = *src++;
150
0
      *dst++ = *src++;
151
0
      *dst++ = *src++;
152
0
    }
153
0
  }
154
155
0
  return PRIMITIVES_SUCCESS;
156
0
}
157
158
static pstatus_t avx2_image_copy_no_overlap_dst_alpha(
159
    BYTE* WINPR_RESTRICT pDstData, DWORD DstFormat, UINT32 nDstStep, UINT32 nXDst, UINT32 nYDst,
160
    UINT32 nWidth, UINT32 nHeight, const BYTE* WINPR_RESTRICT pSrcData, DWORD SrcFormat,
161
    UINT32 nSrcStep, UINT32 nXSrc, UINT32 nYSrc, const gdiPalette* WINPR_RESTRICT palette,
162
    UINT32 flags, int64_t srcVMultiplier, int64_t srcVOffset, int64_t dstVMultiplier,
163
    int64_t dstVOffset)
164
0
{
165
0
  WINPR_ASSERT(pDstData);
166
0
  WINPR_ASSERT(pSrcData);
167
168
0
  switch (SrcFormat)
169
0
  {
170
0
    case PIXEL_FORMAT_BGR24:
171
0
      switch (DstFormat)
172
0
      {
173
0
        case PIXEL_FORMAT_BGRX32:
174
0
        case PIXEL_FORMAT_BGRA32:
175
0
          return avx2_image_copy_bgr24_bgrx32(
176
0
              pDstData, nDstStep, nXDst, nYDst, nWidth, nHeight, pSrcData, nSrcStep,
177
0
              nXSrc, nYSrc, srcVMultiplier, srcVOffset, dstVMultiplier, dstVOffset);
178
0
        default:
179
0
          break;
180
0
      }
181
0
      break;
182
0
    case PIXEL_FORMAT_BGRX32:
183
0
    case PIXEL_FORMAT_BGRA32:
184
0
      switch (DstFormat)
185
0
      {
186
0
        case PIXEL_FORMAT_BGRX32:
187
0
        case PIXEL_FORMAT_BGRA32:
188
0
          return avx2_image_copy_bgrx32_bgrx32(
189
0
              pDstData, nDstStep, nXDst, nYDst, nWidth, nHeight, pSrcData, nSrcStep,
190
0
              nXSrc, nYSrc, srcVMultiplier, srcVOffset, dstVMultiplier, dstVOffset);
191
0
        default:
192
0
          break;
193
0
      }
194
0
      break;
195
0
    case PIXEL_FORMAT_RGBX32:
196
0
    case PIXEL_FORMAT_RGBA32:
197
0
      switch (DstFormat)
198
0
      {
199
0
        case PIXEL_FORMAT_RGBX32:
200
0
        case PIXEL_FORMAT_RGBA32:
201
0
          return avx2_image_copy_bgrx32_bgrx32(
202
0
              pDstData, nDstStep, nXDst, nYDst, nWidth, nHeight, pSrcData, nSrcStep,
203
0
              nXSrc, nYSrc, srcVMultiplier, srcVOffset, dstVMultiplier, dstVOffset);
204
0
        default:
205
0
          break;
206
0
      }
207
0
      break;
208
0
    default:
209
0
      break;
210
0
  }
211
212
0
  primitives_t* gen = primitives_get_generic();
213
0
  return gen->copy_no_overlap(pDstData, DstFormat, nDstStep, nXDst, nYDst, nWidth, nHeight,
214
0
                              pSrcData, SrcFormat, nSrcStep, nXSrc, nYSrc, palette, flags);
215
0
}
216
217
static pstatus_t avx2_image_copy_no_overlap(BYTE* WINPR_RESTRICT pDstData, DWORD DstFormat,
218
                                            UINT32 nDstStep, UINT32 nXDst, UINT32 nYDst,
219
                                            UINT32 nWidth, UINT32 nHeight,
220
                                            const BYTE* WINPR_RESTRICT pSrcData, DWORD SrcFormat,
221
                                            UINT32 nSrcStep, UINT32 nXSrc, UINT32 nYSrc,
222
                                            const gdiPalette* WINPR_RESTRICT palette, UINT32 flags)
223
5.20k
{
224
5.20k
  const BOOL vSrcVFlip = (flags & FREERDP_FLIP_VERTICAL) != 0;
225
5.20k
  int64_t srcVOffset = 0;
226
5.20k
  int64_t srcVMultiplier = 1;
227
5.20k
  int64_t dstVOffset = 0;
228
5.20k
  int64_t dstVMultiplier = 1;
229
230
5.20k
  if ((nWidth == 0) || (nHeight == 0))
231
0
    return PRIMITIVES_SUCCESS;
232
233
5.20k
  if ((nHeight > INT32_MAX) || (nWidth > INT32_MAX))
234
0
    return -1;
235
236
5.20k
  if (!pDstData || !pSrcData)
237
0
    return -1;
238
239
5.20k
  if (nDstStep == 0)
240
0
    nDstStep = nWidth * FreeRDPGetBytesPerPixel(DstFormat);
241
242
5.20k
  if (nSrcStep == 0)
243
0
    nSrcStep = nWidth * FreeRDPGetBytesPerPixel(SrcFormat);
244
245
5.20k
  if (vSrcVFlip)
246
4.19k
  {
247
4.19k
    srcVOffset = (nHeight - 1ll) * nSrcStep;
248
4.19k
    srcVMultiplier = -1;
249
4.19k
  }
250
251
5.20k
  if (((flags & FREERDP_KEEP_DST_ALPHA) != 0) && FreeRDPColorHasAlpha(DstFormat))
252
0
    return avx2_image_copy_no_overlap_dst_alpha(pDstData, DstFormat, nDstStep, nXDst, nYDst,
253
0
                                                nWidth, nHeight, pSrcData, SrcFormat, nSrcStep,
254
0
                                                nXSrc, nYSrc, palette, flags, srcVMultiplier,
255
0
                                                srcVOffset, dstVMultiplier, dstVOffset);
256
5.20k
  else if (FreeRDPAreColorFormatsEqualNoAlpha(SrcFormat, DstFormat))
257
296
    return generic_image_copy_no_overlap_memcpy(pDstData, DstFormat, nDstStep, nXDst, nYDst,
258
296
                                                nWidth, nHeight, pSrcData, SrcFormat, nSrcStep,
259
296
                                                nXSrc, nYSrc, palette, srcVMultiplier,
260
296
                                                srcVOffset, dstVMultiplier, dstVOffset, flags);
261
4.91k
  else
262
4.91k
  {
263
4.91k
    primitives_t* gen = primitives_get_generic();
264
4.91k
    return gen->copy_no_overlap(pDstData, DstFormat, nDstStep, nXDst, nYDst, nWidth, nHeight,
265
4.91k
                                pSrcData, SrcFormat, nSrcStep, nXSrc, nYSrc, palette, flags);
266
4.91k
  }
267
5.20k
}
268
#endif
269
270
/* ------------------------------------------------------------------------- */
271
void primitives_init_copy_avx2_int(primitives_t* WINPR_RESTRICT prims)
272
2
{
273
2
#if defined(SSE_AVX_INTRINSICS_ENABLED)
274
2
  WLog_VRB(PRIM_TAG, "AVX2 optimizations");
275
2
  prims->copy_no_overlap = avx2_image_copy_no_overlap;
276
#else
277
  WLog_VRB(PRIM_TAG, "undefined WITH_SIMD or WITH_AVX2 or AVX2 intrinsics not available");
278
  WINPR_UNUSED(prims);
279
#endif
280
2
}