Skip to content

Commit 1427496

Browse files
committed
ConvertToPlanarRGB YUV->RGB bits=32 keep integer arith. accuracy before conversion
1 parent 7bd8cce commit 1427496

3 files changed

Lines changed: 62 additions & 25 deletions

File tree

avs_core/convert/convert_planar.cpp

Lines changed: 22 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -890,7 +890,15 @@ static void convert_yuv444p16_to_rgb16_c(BYTE* dstp, const BYTE* srcY, const BYT
890890
template<typename pixel_t, bool lessthan16bit, bool need_float_conversion>
891891
static void convert_yuv_to_planarrgb_uintN_c(BYTE* dstp[3], int dstPitch[3], const BYTE* srcp[3], const int srcPitch[3], int width, int height, const ConversionMatrix& matrix, const int bits_per_pixel)
892892
{
893-
auto round_mask_plus_rgb_offset_i = 4096 + (matrix.offset_rgb << 13);
893+
// Float conversion extra rules
894+
// - no rounding
895+
// - no integer scaling back 13 bits
896+
// - no clamping to bit depth limits
897+
// - the 13-bit scaling factor is integrated into the bits_per_pixel shift
898+
899+
constexpr int ROUNDER = need_float_conversion ? 0 : (1 << 12); // 4096
900+
901+
auto round_mask_plus_rgb_offset_i = ROUNDER + (matrix.offset_rgb << 13);
894902

895903
// int64_t needed for exact 16 bit pixels.
896904
// unlike SIMD versions which optimize it by moving into signed int16 then back to uint16, taking care of overflows.
@@ -907,7 +915,8 @@ static void convert_yuv_to_planarrgb_uintN_c(BYTE* dstp[3], int dstPitch[3], con
907915
max_pixel_value = (1 << bits_per_pixel) - 1;
908916
}
909917

910-
const float inv_max = 1.0f / max_pixel_value;
918+
constexpr int int_arithmetic_shift = 1 << 13;
919+
const float scale_f = 1.0f / static_cast<float>(int_arithmetic_shift * max_pixel_value);
911920

912921
for (int y = 0; y < height; y++) {
913922
const pixel_t* srcY = reinterpret_cast<const pixel_t*>(srcp[0]);
@@ -920,17 +929,21 @@ static void convert_yuv_to_planarrgb_uintN_c(BYTE* dstp[3], int dstPitch[3], con
920929
const int U = static_cast<int>(srcU[x]) - half_pixel_value;
921930
const int V = static_cast<int>(srcV[x]) - half_pixel_value;
922931

923-
int b = static_cast<int>(((safe_int_t)matrix.y_b * Y + (safe_int_t)matrix.u_b * U + (safe_int_t)matrix.v_b * V + round_mask_plus_rgb_offset_i) >> 13);
924-
int g = static_cast<int>(((safe_int_t)matrix.y_g * Y + (safe_int_t)matrix.u_g * U + (safe_int_t)matrix.v_g * V + round_mask_plus_rgb_offset_i) >> 13);
925-
int r = static_cast<int>(((safe_int_t)matrix.y_r * Y + (safe_int_t)matrix.u_r * U + (safe_int_t)matrix.v_r * V + round_mask_plus_rgb_offset_i) >> 13);
932+
int b = static_cast<int>(((safe_int_t)matrix.y_b * Y + (safe_int_t)matrix.u_b * U + (safe_int_t)matrix.v_b * V + round_mask_plus_rgb_offset_i));
933+
int g = static_cast<int>(((safe_int_t)matrix.y_g * Y + (safe_int_t)matrix.u_g * U + (safe_int_t)matrix.v_g * V + round_mask_plus_rgb_offset_i));
934+
int r = static_cast<int>(((safe_int_t)matrix.y_r * Y + (safe_int_t)matrix.u_r * U + (safe_int_t)matrix.v_r * V + round_mask_plus_rgb_offset_i));
926935

927936
if constexpr (need_float_conversion) {
928-
// no clamp when converting to float
929-
reinterpret_cast<float*>(d0)[x] = static_cast<float>(g) * inv_max;
930-
reinterpret_cast<float*>(d1)[x] = static_cast<float>(b) * inv_max;
931-
reinterpret_cast<float*>(d2)[x] = static_cast<float>(r) * inv_max;
937+
// no clamp when converting to float, scale back integrated into scale_f
938+
reinterpret_cast<float*>(d0)[x] = static_cast<float>(g) * scale_f;
939+
reinterpret_cast<float*>(d1)[x] = static_cast<float>(b) * scale_f;
940+
reinterpret_cast<float*>(d2)[x] = static_cast<float>(r) * scale_f;
932941
}
933942
else {
943+
// scale back
944+
g = g >> 13;
945+
b = b >> 13;
946+
r = r >> 13;
934947
// Clamp
935948
g = clamp(g, 0, max_pixel_value);
936949
b = clamp(b, 0, max_pixel_value);

avs_core/convert/intel/convert_planar_avx2.cpp

Lines changed: 19 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -375,8 +375,9 @@ void convert_yuv_to_planarrgb_uintN_avx2(BYTE* (&dstp)[3], int(&dstPitch)[3], co
375375
4. Output rgb offset is also added to the precalculated patch.
376376
*/
377377
const __m256i half = _mm256_set1_epi16((short)(1 << (bits_per_pixel - 1))); // 128
378-
const __m256i limit = _mm256_set1_epi16((short)((1 << bits_per_pixel) - 1)); // 255
379-
const __m256i offset = _mm256_set1_epi16((short)(m.offset_y));
378+
const int max_pixel_value = (1 << bits_per_pixel) - 1;
379+
const __m256i limit = _mm256_set1_epi16((short)max_pixel_value); // 255
380+
const __m256i offset = _mm256_set1_epi16((short)m.offset_y);
380381

381382
// to be able to use it as signed 16 bit in madd; 4096+(16<<13) would not fit into i16
382383
// multiplier is 4096 instead of 1:
@@ -387,19 +388,27 @@ void convert_yuv_to_planarrgb_uintN_avx2(BYTE* (&dstp)[3], int(&dstPitch)[3], co
387388
constexpr int ROUND_SCALE = 4096; // 1 << 12, for 13 bit integer arithmetic: "0.5"
388389
const __m256i m256i_round_scale = _mm256_set1_epi16(ROUND_SCALE);
389390

391+
// Float conversion extra rules
392+
// - no rounding
393+
// - no integer scaling back 13 bits
394+
// - no clamping to bit depth limits
395+
// - the 13-bit scaling factor is integrated into the bits_per_pixel shift
396+
397+
constexpr int ROUNDER = need_float_conversion ? 0 : (1 << 12); // 4096
398+
390399
int round_mask_plus_rgb_offset_scaled_i;
391400
__m256i v_patch_G, v_patch_B, v_patch_R;
392401
__m256i sign_flip_mask = _mm256_set1_epi16((short)0x8000); // for 16 bit pivot
393402

394403
if constexpr (lessthan16bit) {
395404
// 8-14 bit
396-
round_mask_plus_rgb_offset_scaled_i = (4096 + (m.offset_rgb << 13)) / ROUND_SCALE;
405+
round_mask_plus_rgb_offset_scaled_i = (ROUNDER + (m.offset_rgb << 13)) / ROUND_SCALE;
397406
v_patch_G = v_patch_B = v_patch_R = _mm256_setzero_si256(); // No patch needed
398407
}
399408
else {
400409
// exact 16 bit
401410
// keep madd simple: only handle the rounding (ROUND_SCALE * 1)
402-
round_mask_plus_rgb_offset_scaled_i = 1; // effectively 1
411+
round_mask_plus_rgb_offset_scaled_i = ROUNDER / ROUND_SCALE; // effectively 1 or 0 (need_float_conversion)
403412

404413
// move BOTH the pivot and the output RGB offset to the 32-bit patch
405414
// Since we have to do the patching anyway, we can combine both adjustments here
@@ -413,7 +422,8 @@ void convert_yuv_to_planarrgb_uintN_avx2(BYTE* (&dstp)[3], int(&dstPitch)[3], co
413422
}
414423

415424
__m256i zero = _mm256_setzero_si256();
416-
const __m256 scale_f = _mm256_set1_ps(1.0f / static_cast<float>((1u << bits_per_pixel) - 1u));
425+
constexpr int int_arithmetic_shift = 1 << 13;
426+
const __m256 scale_f = _mm256_set1_ps(1.0f / static_cast<float>(int_arithmetic_shift * max_pixel_value));
417427

418428
const __m256i m_uy_G = _mm256_set1_epi32(int((static_cast<uint16_t>(m.y_g) << 16) | static_cast<uint16_t>(m.u_g))); // y and u
419429
const __m256i m_vR_G = _mm256_set1_epi32(int((static_cast<uint16_t>(round_mask_plus_rgb_offset_scaled_i) << 16) | static_cast<uint16_t>(m.v_g))); // rounding 13 bit >> 1 and v
@@ -459,7 +469,7 @@ void convert_yuv_to_planarrgb_uintN_avx2(BYTE* (&dstp)[3], int(&dstPitch)[3], co
459469
// Avisynth FRAME_ALIGN == 64, so when need_float_conversion, we cannot process 32 pixels at once at the end of the line
460470
bool safe_last_16float_64bytes;
461471
if constexpr (need_float_conversion)
462-
safe_last_16float_64bytes = (x + 64) <= rowsize;
472+
safe_last_16float_64bytes = (x + 16 * sizeof(pixel_t)) <= rowsize;
463473
else
464474
safe_last_16float_64bytes = false;
465475

@@ -508,7 +518,9 @@ void convert_yuv_to_planarrgb_uintN_avx2(BYTE* (&dstp)[3], int(&dstPitch)[3], co
508518
__m256i sum = _mm256_add_epi32(_mm256_madd_epi16(m_uy, uy), _mm256_madd_epi16(m_vr, vr));
509519
// 16-bit adjustment (signed patch, offset, output rgb offset)
510520
if constexpr (!lessthan16bit) sum = _mm256_add_epi32(sum, v_patch);
511-
return _mm256_srai_epi32(sum, 13); // 13 bit fixed point shift
521+
if constexpr (!need_float_conversion)
522+
sum = _mm256_srai_epi32(sum, 13); // 13 bit fixed point shift
523+
return sum;
512524
};
513525

514526
__m256i res1_lo = madd_scale(uy1_lo, vr1_lo); // Pixels 0-3, 8-11

avs_core/convert/intel/convert_planar_sse.cpp

Lines changed: 21 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1072,8 +1072,9 @@ void convert_yuv_to_planarrgb_uintN_sse2(BYTE *(&dstp)[3], int (&dstPitch)[3], c
10721072
*/
10731073

10741074
__m128i half = _mm_set1_epi16((short)(1 << (bits_per_pixel - 1))); // 128
1075-
__m128i limit = _mm_set1_epi16((short)((1 << bits_per_pixel) - 1)); // 255
1076-
__m128i offset = _mm_set1_epi16((short)(m.offset_y));
1075+
const int max_pixel_value = (1 << bits_per_pixel) - 1;
1076+
__m128i limit = _mm_set1_epi16((short)max_pixel_value); // 255
1077+
__m128i offset = _mm_set1_epi16((short)m.offset_y);
10771078

10781079
// to be able to use it as signed 16 bit in madd; 4096+(16<<13) would not fit into i16
10791080
// multiplier is 4096 instead of 1:
@@ -1084,19 +1085,27 @@ void convert_yuv_to_planarrgb_uintN_sse2(BYTE *(&dstp)[3], int (&dstPitch)[3], c
10841085
constexpr int ROUND_SCALE = 4096; // 1 << 12, for 13 bit integer arithmetic: "0.5"
10851086
const __m128i m128i_round_scale = _mm_set1_epi16(ROUND_SCALE);
10861087

1088+
// Float conversion extra rules
1089+
// - no rounding
1090+
// - no integer scaling back 13 bits
1091+
// - no clamping to bit depth limits
1092+
// - the 13-bit scaling factor is integrated into the bits_per_pixel shift
1093+
1094+
constexpr int ROUNDER = need_float_conversion ? 0 : (1 << 12); // 4096
1095+
10871096
int round_mask_plus_rgb_offset_scaled_i;
10881097
__m128i v_patch_G, v_patch_B, v_patch_R;
10891098
__m128i sign_flip_mask = _mm_set1_epi16((short)0x8000); // for 16 bit pivot
10901099

10911100
if constexpr (lessthan16bit) {
10921101
// 8-14 bit
1093-
round_mask_plus_rgb_offset_scaled_i = (4096 + (m.offset_rgb << 13)) / ROUND_SCALE;
1102+
round_mask_plus_rgb_offset_scaled_i = (ROUNDER + (m.offset_rgb << 13)) / ROUND_SCALE;
10941103
v_patch_G = v_patch_B = v_patch_R = _mm_setzero_si128(); // No patch needed
10951104
}
10961105
else {
10971106
// exact 16 bit
10981107
// keep madd simple: only handle the rounding (ROUND_SCALE * 1)
1099-
round_mask_plus_rgb_offset_scaled_i = 1; // effectively 1
1108+
round_mask_plus_rgb_offset_scaled_i = ROUNDER / ROUND_SCALE; // effectively 1 or 0 (need_float_conversion)
11001109

11011110
// move BOTH the pivot and the output RGB offset to the 32-bit patch
11021111
// Since we have to do the patching anyway, we can combine both adjustments here
@@ -1110,7 +1119,8 @@ void convert_yuv_to_planarrgb_uintN_sse2(BYTE *(&dstp)[3], int (&dstPitch)[3], c
11101119
}
11111120

11121121
__m128i zero = _mm_setzero_si128();
1113-
const __m128 scale_f_sse2 = _mm_set1_ps(1.0f / static_cast<float>((1u << bits_per_pixel) - 1u));
1122+
constexpr int int_arithmetic_shift = 1 << 13;
1123+
const __m128 scale_f_sse2 = _mm_set1_ps(1.0f / static_cast<float>(int_arithmetic_shift * max_pixel_value));
11141124

11151125
const __m128i m_uy_G = _mm_set1_epi32((static_cast<uint16_t>(m.y_g) << 16) | static_cast<uint16_t>(m.u_g));
11161126
const __m128i m_vr_G = _mm_set1_epi32((static_cast<uint16_t>(round_mask_plus_rgb_offset_scaled_i) << 16) | static_cast<uint16_t>(m.v_g));
@@ -1170,11 +1180,13 @@ void convert_yuv_to_planarrgb_uintN_sse2(BYTE *(&dstp)[3], int (&dstPitch)[3], c
11701180
auto process_plane_sse2 = [&](BYTE* plane_ptr, __m128i m_uy, __m128i m_vr, __m128i v_patch) {
11711181

11721182
auto madd_shift = [&](__m128i uy, __m128i vr) {
1173-
__m128i res = _mm_add_epi32(_mm_madd_epi16(m_uy, uy), _mm_madd_epi16(m_vr, vr));
1183+
__m128i sum = _mm_add_epi32(_mm_madd_epi16(m_uy, uy), _mm_madd_epi16(m_vr, vr));
11741184
// 16-bit adjustment (signed patch, offset, output rgb offset)
1175-
if constexpr (!lessthan16bit) res = _mm_add_epi32(res, v_patch);
1176-
return _mm_srai_epi32(res, 13); // 13 bit fixed point shift
1177-
};
1185+
if constexpr (!lessthan16bit) sum = _mm_add_epi32(sum, v_patch);
1186+
if constexpr(!need_float_conversion)
1187+
sum = _mm_srai_epi32(sum, 13); // 13 bit fixed point shift
1188+
return sum;
1189+
};
11781190

11791191
__m128i res_lo = madd_shift(uy_lo, vr_lo); // Pixels 0, 1, 2, 3
11801192
__m128i res_hi = madd_shift(uy_hi, vr_hi); // Pixels 4, 5, 6, 7

0 commit comments

Comments
 (0)