Coverage Report

Created: 2026-09-01 06:56

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/work/libde265/libde265/x86/sse-intrapred.cc
Line
Count
Source
1
/*
2
 * H.265 video codec.
3
 * Copyright (c) 2026 Dirk Farin <dirk.farin@gmail.com>
4
 *
5
 * This file is part of libde265.
6
 *
7
 * libde265 is free software: you can redistribute it and/or modify
8
 * it under the terms of the GNU Lesser General Public License as
9
 * published by the Free Software Foundation, either version 3 of
10
 * the License, or (at your option) any later version.
11
 *
12
 * libde265 is distributed in the hope that it will be useful,
13
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15
 * GNU Lesser General Public License for more details.
16
 *
17
 * You should have received a copy of the GNU Lesser General Public License
18
 * along with libde265.  If not, see <http://www.gnu.org/licenses/>.
19
 */
20
21
#include "x86/sse-intrapred.h"
22
#include "libde265/util.h"
23
24
#ifdef HAVE_CONFIG_H
25
#include "config.h"
26
#endif
27
28
#include <string.h>
29
#include <emmintrin.h> // SSE2
30
31
#if HAVE_SSE4_1
32
#include <smmintrin.h> // SSE4.1
33
34
// angle / inverse-angle lookup tables, defined in intrapred.cc
35
extern const int intraPredAngle_table[1+34];
36
extern const int invAngle_table[25-10];
37
38
namespace {
39
40
const int kMaxBlk = 64; // == MAX_INTRA_PRED_BLOCK_SIZE
41
42
// load 4 / 8 consecutive uint8_t and zero-extend to 8x int16
43
42.5M
inline __m128i load4_epi16(const uint8_t* p) {
44
42.5M
  int32_t t; memcpy(&t, p, 4);
45
42.5M
  return _mm_cvtepu8_epi16(_mm_cvtsi32_si128(t));
46
42.5M
}
47
23.4M
inline __m128i load8_epi16(const uint8_t* p) {
48
23.4M
  return _mm_cvtepu8_epi16(_mm_loadl_epi64((const __m128i*)p));
49
23.4M
}
50
51
// store nT bytes from the low lanes of an u8-packed register (nT in {4,8,16,32})
52
70.9M
inline void store_row(uint8_t* d, __m128i v, int nT) {
53
70.9M
  switch (nT) {
54
45.8M
    case 4:  { int32_t t = _mm_cvtsi128_si32(v); memcpy(d, &t, 4); } break;
55
24.9M
    case 8:  _mm_storel_epi64((__m128i*)d, v); break;
56
130k
    case 16: _mm_storeu_si128((__m128i*)d, v); break;
57
62.7k
    default: _mm_storeu_si128((__m128i*)d, v);
58
62.7k
             _mm_storeu_si128((__m128i*)(d+16), v); break;
59
70.9M
  }
60
70.9M
}
61
62
// copy exactly nT bytes (nT in {4,8,16,32})
63
10.0M
inline void copy_row(uint8_t* d, const uint8_t* s, int nT) {
64
10.0M
  switch (nT) {
65
6.59M
    case 4:  memcpy(d, s, 4); break;
66
2.98M
    case 8:  memcpy(d, s, 8); break;
67
316k
    case 16: _mm_storeu_si128((__m128i*)d, _mm_loadu_si128((const __m128i*)s)); break;
68
196k
    default: _mm_storeu_si128((__m128i*)d,      _mm_loadu_si128((const __m128i*)s));
69
196k
             _mm_storeu_si128((__m128i*)(d+16), _mm_loadu_si128((const __m128i*)(s+16))); break;
70
10.0M
  }
71
10.0M
}
72
73
12.6M
inline int shift_for(int nT) { return (nT==4)?3 : (nT==8)?4 : (nT==16)?5 : 6; } // Log2(nT)+1
74
75
} // namespace
76
77
78
void intra_pred_dc_8_sse4(uint8_t* dst, ptrdiff_t stride, int nT, int cIdx, const uint8_t* border)
79
1.66M
{
80
1.66M
  const int shift = shift_for(nT);
81
82
1.66M
  int dcVal = 0;
83
9.96M
  for (int i=0;i<nT;i++) { dcVal += border[i+1]; dcVal += border[-i-1]; }
84
1.66M
  dcVal += nT;
85
1.66M
  dcVal >>= shift;
86
87
1.66M
  const __m128i v = _mm_set1_epi8((char)dcVal);
88
9.96M
  for (int y=0;y<nT;y++) store_row(dst + y*stride, v, nT);
89
90
  // luma edge smoothing overwrites first row and first column (disjoint cells)
91
1.66M
  if (cIdx==0 && nT<32) {
92
838k
    dst[0] = (uint8_t)((border[-1] + 2*dcVal + border[1] + 2) >> 2);
93
4.88M
    for (int x=1;x<nT;x++) dst[x]          = (uint8_t)((border[ x+1] + 3*dcVal + 2) >> 2);
94
4.88M
    for (int y=1;y<nT;y++) dst[y*stride]   = (uint8_t)((border[-y-1] + 3*dcVal + 2) >> 2);
95
838k
  }
96
1.66M
}
97
98
99
void intra_pred_planar_8_sse4(uint8_t* dst, ptrdiff_t stride, int nT, int cIdx, const uint8_t* border)
100
10.9M
{
101
10.9M
  const int shift = shift_for(nT);
102
10.9M
  const int TR = border[ 1+nT];   // top-right corner sample
103
10.9M
  const int BL = border[-1-nT];   // bottom-left corner sample
104
105
10.9M
  const __m128i base  = _mm_setr_epi16(0,1,2,3,4,5,6,7);
106
10.9M
  const __m128i vTR   = _mm_set1_epi16((short)TR);
107
10.9M
  const __m128i vNTm1 = _mm_set1_epi16((short)(nT-1));
108
10.9M
  const __m128i one   = _mm_set1_epi16(1);
109
110
62.3M
  for (int y=0;y<nT;y++) {
111
51.3M
    const int left_y = border[-1-y];
112
51.3M
    const int Cy = (y+1)*BL + nT;                 // constant term for this row
113
51.3M
    const __m128i vL    = _mm_set1_epi16((short)left_y);
114
51.3M
    const __m128i vNT1Y = _mm_set1_epi16((short)(nT-1-y));
115
51.3M
    const __m128i vC    = _mm_set1_epi16((short)Cy);
116
117
110M
    for (int x=0;x<nT;x+=8) {
118
59.5M
      const __m128i xidx = _mm_add_epi16(_mm_set1_epi16((short)x), base);
119
59.5M
      const __m128i vA   = _mm_sub_epi16(vNTm1, xidx);     // (nT-1-x)
120
59.5M
      const __m128i vB   = _mm_add_epi16(xidx, one);       // (x+1)
121
59.5M
      const __m128i top  = (nT==4) ? load4_epi16(border+1+x) : load8_epi16(border+1+x);
122
123
59.5M
      __m128i acc = _mm_mullo_epi16(vA,  vL);              // (nT-1-x)*border[-1-y]
124
59.5M
      acc = _mm_add_epi16(acc, _mm_mullo_epi16(vB,  vTR)); // (x+1)*border[1+nT]
125
59.5M
      acc = _mm_add_epi16(acc, _mm_mullo_epi16(top, vNT1Y));// (nT-1-y)*border[1+x]
126
59.5M
      acc = _mm_add_epi16(acc, vC);                        // (y+1)*border[-1-nT] + nT
127
59.5M
      acc = _mm_srli_epi16(acc, shift);
128
129
59.5M
      const __m128i p = _mm_packus_epi16(acc, acc);
130
59.5M
      store_row(dst + y*stride + x, p, (nT<8)?nT:8);
131
59.5M
    }
132
51.3M
  }
133
10.9M
}
134
135
136
void intra_pred_angular_8_sse4(uint8_t* dst, ptrdiff_t stride, int bit_depth, int disableBoundaryFilter,
137
                               int xB0, int yB0, int mode, int nT, int cIdx, const uint8_t* border)
138
6.71M
{
139
6.71M
  const int intraPredAngle = intraPredAngle_table[mode];
140
141
6.71M
  uint8_t  ref_mem[4*kMaxBlk+1];
142
6.71M
  uint8_t* ref = &ref_mem[2*kMaxBlk];
143
144
6.71M
  if (mode >= 18) {
145
18.2M
    for (int x=0;x<=nT;x++) ref[x] = border[x];
146
147
2.62M
    if (intraPredAngle<0) {
148
305k
      const int invAngle = invAngle_table[mode-11];
149
305k
      if (((nT*intraPredAngle)>>5) < -1) {
150
757k
        for (int x=(nT*intraPredAngle)>>5; x<=-1; x++)
151
591k
          ref[x] = border[0-((x*invAngle+128)>>8)];
152
166k
      }
153
2.31M
    } else {
154
13.7M
      for (int x=nT+1; x<=2*nT; x++) ref[x] = border[x];
155
2.31M
    }
156
157
15.6M
    for (int y=0;y<nT;y++) {
158
12.9M
      const int iIdx  = ((y+1)*intraPredAngle)>>5;
159
12.9M
      const int iFact = ((y+1)*intraPredAngle)&31;
160
12.9M
      const uint8_t* src = ref + iIdx + 1;
161
12.9M
      uint8_t* d = dst + y*stride;
162
163
12.9M
      if (iFact==0) {
164
10.0M
        copy_row(d, src, nT);                     // dst[x] = ref[x+iIdx+1]
165
10.0M
      } else {
166
2.89M
        const __m128i w0  = _mm_set1_epi16((short)(32-iFact));
167
2.89M
        const __m128i w1  = _mm_set1_epi16((short)iFact);
168
2.89M
        const __m128i r16 = _mm_set1_epi16(16);
169
2.89M
        if (nT==4) {
170
1.75M
          __m128i a = load4_epi16(src), b = load4_epi16(src+1);
171
1.75M
          __m128i acc = _mm_add_epi16(_mm_add_epi16(_mm_mullo_epi16(a,w0), _mm_mullo_epi16(b,w1)), r16);
172
1.75M
          acc = _mm_srli_epi16(acc, 5);
173
1.75M
          store_row(d, _mm_packus_epi16(acc,acc), 4);
174
1.75M
        } else {
175
2.56M
          for (int x=0;x<nT;x+=8) {
176
1.43M
            __m128i a = load8_epi16(src+x), b = load8_epi16(src+x+1);
177
1.43M
            __m128i acc = _mm_add_epi16(_mm_add_epi16(_mm_mullo_epi16(a,w0), _mm_mullo_epi16(b,w1)), r16);
178
1.43M
            acc = _mm_srli_epi16(acc, 5);
179
1.43M
            store_row(d+x, _mm_packus_epi16(acc,acc), 8);
180
1.43M
          }
181
1.13M
        }
182
2.89M
      }
183
12.9M
    }
184
185
2.62M
    if (mode==26 && cIdx==0 && nT<32 && !disableBoundaryFilter) {
186
4.04M
      for (int y=0;y<nT;y++)
187
3.52M
        dst[y*stride] = (uint8_t)Clip_BitDepth(border[1] + ((border[-1-y] - border[0])>>1), bit_depth);
188
522k
    }
189
2.62M
  }
190
4.09M
  else {
191
    // Modes 2..17: the reference projection is transposed (per-column iIdx/iFact and
192
    // a row-indexed reference fetch), which does not map onto contiguous SIMD loads or
193
    // stores. Use the scalar reference path here -- kept bit-identical to
194
    // intra_prediction_angular() in intrapred.h.
195
25.1M
    for (int x=0;x<=nT;x++) ref[x] = border[-x];
196
197
4.09M
    if (intraPredAngle<0) {
198
147k
      const int invAngle = invAngle_table[mode-11];
199
147k
      if (((nT*intraPredAngle)>>5) < -1) {
200
424k
        for (int x=(nT*intraPredAngle)>>5; x<=-1; x++)
201
326k
          ref[x] = border[(x*invAngle+128)>>8];
202
98.1k
      }
203
3.94M
    } else {
204
20.1M
      for (int x=nT+1; x<=2*nT; x++) ref[x] = border[-x];
205
3.94M
    }
206
207
20.9M
    for (int y=0;y<nT;y++)
208
92.0M
      for (int x=0;x<nT;x++) {
209
75.1M
        const int iIdx  = ((x+1)*intraPredAngle)>>5;
210
75.1M
        const int iFact = ((x+1)*intraPredAngle)&31;
211
75.1M
        if (iFact != 0)
212
20.3M
          dst[x+y*stride] = (uint8_t)(((32-iFact)*ref[y+iIdx+1] + iFact*ref[y+iIdx+2] + 16)>>5);
213
54.7M
        else
214
54.7M
          dst[x+y*stride] = ref[y+iIdx+1];
215
75.1M
      }
216
217
4.09M
    if (mode==10 && cIdx==0 && nT<32 && !disableBoundaryFilter) {
218
115k
      for (int x=0;x<nT;x++)
219
97.3k
        dst[x] = (uint8_t)Clip_BitDepth(border[-1] + ((border[1+x] - border[0])>>1), bit_depth);
220
18.5k
    }
221
4.09M
  }
222
6.71M
}
223
224
#endif // HAVE_SSE4_1