blob: 567c95d6c869cc64b242f06aa03000d7bfb93de3 [file]
// Copyright 2020 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// -----------------------------------------------------------------------------
//
// Scoring functions
//
// Author: Skal (pascal.massimino@gmail.com)
#include <algorithm>
#include <cstdint>
#include "src/dsp/dsp.h"
namespace WP2 {
void GetBlockMinMaxGeneric(const int16_t* src, uint32_t step, uint32_t w,
uint32_t h, int16_t* const min, int16_t* const max) {
*min = src[0];
*max = src[0];
for (uint32_t j = 0; j < h; ++j) {
const auto p = std::minmax_element(src, src + w);
*min = std::min(*min, *p.first);
*max = std::max(*max, *p.second);
src += step;
}
}
namespace {
//------------------------------------------------------------------------------
void GetBlockMinMax_C(const int16_t* src, uint32_t step, uint32_t num_blocks,
int16_t min[], int16_t max[]) {
for (uint32_t n = 0; n < num_blocks; ++n) {
const int16_t* p = src + n * 4;
min[n] = std::min({p[0], p[1], p[2], p[3]});
max[n] = std::max({p[0], p[1], p[2], p[3]});
for (uint32_t i = 1; i < 4; ++i) {
p += step;
min[n] = std::min({min[n], p[0], p[1], p[2], p[3]});
max[n] = std::max({max[n], p[0], p[1], p[2], p[3]});
}
}
}
void GetBlockMinMax_5x5_C(const int16_t* src, uint32_t step, int16_t* const min,
int16_t* const max) {
GetBlockMinMaxGeneric(src, step, 5, 5, min, max);
}
//------------------------------------------------------------------------------
// SSE4.1 implementation
#if defined(WP2_USE_SSE)
static void DoMinMax8x4(const __m128i& A, const __m128i& B, const __m128i& C,
const __m128i& D, __m128i* const m, __m128i* const M) {
const __m128i m0 = _mm_min_epi16(A, B);
const __m128i M0 = _mm_max_epi16(A, B);
const __m128i m1 = _mm_min_epi16(C, D);
const __m128i M1 = _mm_max_epi16(C, D);
const __m128i m2 = _mm_min_epi16(m0, m1);
const __m128i M2 = _mm_max_epi16(M0, M1);
const __m128i m3 = _mm_min_epi16(m2, _mm_srli_epi64(m2, 32));
const __m128i M3 = _mm_max_epi16(M2, _mm_srli_epi64(M2, 32));
*m = _mm_min_epi16(m3, _mm_srli_epi64(m3, 16));
*M = _mm_max_epi16(M3, _mm_srli_epi64(M3, 16));
}
void GetBlockMinMax_SSE(const int16_t* src, uint32_t step, uint32_t num_blocks,
int16_t min[], int16_t max[]) {
uint32_t n = 0;
for (; n + 2 <= num_blocks; n += 2, src += 8) {
const __m128i A = _mm_loadu_si128((const __m128i*)(src + 0 * step));
const __m128i B = _mm_loadu_si128((const __m128i*)(src + 1 * step));
const __m128i C = _mm_loadu_si128((const __m128i*)(src + 2 * step));
const __m128i D = _mm_loadu_si128((const __m128i*)(src + 3 * step));
__m128i m, M;
DoMinMax8x4(A, B, C, D, &m, &M);
min[n + 0] = _mm_extract_epi16(m, 0);
min[n + 1] = _mm_extract_epi16(m, 4);
max[n + 0] = _mm_extract_epi16(M, 0);
max[n + 1] = _mm_extract_epi16(M, 4);
}
if (num_blocks & 1) {
const __m128i A = _mm_loadl_epi64((const __m128i*)(src + 0 * step));
const __m128i B = _mm_loadl_epi64((const __m128i*)(src + 1 * step));
const __m128i C = _mm_loadl_epi64((const __m128i*)(src + 2 * step));
const __m128i D = _mm_loadl_epi64((const __m128i*)(src + 3 * step));
__m128i m, M;
DoMinMax8x4(A, B, C, D, &m, &M);
min[n + 0] = _mm_extract_epi16(m, 0);
max[n + 0] = _mm_extract_epi16(M, 0);
}
}
void GetBlockMinMax_5x5_SSE(const int16_t* src, uint32_t step,
int16_t* const min, int16_t* const max) {
const __m128i A = _mm_loadu_si128((const __m128i*)(src + 0 * step));
const __m128i B = _mm_loadu_si128((const __m128i*)(src + 1 * step));
const __m128i C = _mm_loadu_si128((const __m128i*)(src + 2 * step));
const __m128i D = _mm_loadu_si128((const __m128i*)(src + 3 * step));
const __m128i E = _mm_loadu_si128((const __m128i*)(src + 4 * step));
const __m128i m0 = _mm_min_epi16(A, B);
const __m128i M0 = _mm_max_epi16(A, B);
const __m128i m1 = _mm_min_epi16(C, D);
const __m128i M1 = _mm_max_epi16(C, D);
const __m128i m2 = _mm_min_epi16(m0, m1);
const __m128i M2 = _mm_max_epi16(M0, M1);
const __m128i m3 = _mm_min_epi16(m2, E);
const __m128i M3 = _mm_max_epi16(M2, E);
min[0] = _mm_extract_epi16(m3, 4);
max[0] = _mm_extract_epi16(M3, 4);
const __m128i m4 = _mm_min_epi16(m3, _mm_srli_epi64(m3, 32));
const __m128i M4 = _mm_max_epi16(M3, _mm_srli_epi64(M3, 32));
const __m128i m5 = _mm_min_epi16(m4, _mm_srli_epi64(m4, 16));
const __m128i M5 = _mm_max_epi16(M4, _mm_srli_epi64(M4, 16));
min[0] = std::min(min[0], (int16_t)_mm_extract_epi16(m5, 0));
max[0] = std::max(max[0], (int16_t)_mm_extract_epi16(M5, 0));
}
WP2_TSAN_IGNORE_FUNCTION void ScoreDspInitSSE() {
GetBlockMinMax = GetBlockMinMax_SSE;
GetBlockMinMax_5x5 = GetBlockMinMax_5x5_SSE;
}
#endif // WP2_USE_SSE
//------------------------------------------------------------------------------
} // namespace
void (*GetBlockMinMax)(const int16_t* src, uint32_t step, uint32_t num_blocks,
int16_t min[], int16_t max[]) = nullptr;
void (*GetBlockMinMax_5x5)(const int16_t* src, uint32_t step,
int16_t* const min, int16_t* const max) = nullptr;
static volatile WP2CPUInfo score_last_cpuinfo_used =
(WP2CPUInfo)&score_last_cpuinfo_used;
WP2_TSAN_IGNORE_FUNCTION void ScoreDspInit() {
if (score_last_cpuinfo_used == WP2GetCPUInfo) return;
GetBlockMinMax = GetBlockMinMax_C;
GetBlockMinMax_5x5 = GetBlockMinMax_5x5_C;
if (WP2GetCPUInfo != nullptr) {
#if defined(WP2_USE_SSE)
if (WP2GetCPUInfo(kSSE)) ScoreDspInitSSE();
#endif
}
score_last_cpuinfo_used = WP2GetCPUInfo;
}
} // namespace WP2
//------------------------------------------------------------------------------