From fa9903905f524f00a18f9aa25b68b58edd85a14f Mon Sep 17 00:00:00 2001 From: Ioana Ghiban Date: Wed, 3 Apr 2024 14:55:04 +0200 Subject: [PATCH 1/5] Implement uint8 to float type conversion --- adapters/opencv/intrinsiccv_hal.cpp | 9 ++++++ intrinsiccv/include/intrinsiccv/intrinsiccv.h | 29 +++++++++++++++++++ intrinsiccv/include/intrinsiccv/sve2.h | 2 +- 3 files changed, 39 insertions(+), 1 deletion(-) diff --git a/adapters/opencv/intrinsiccv_hal.cpp b/adapters/opencv/intrinsiccv_hal.cpp index 3076ea6d5..a509b3353 100644 --- a/adapters/opencv/intrinsiccv_hal.cpp +++ b/adapters/opencv/intrinsiccv_hal.cpp @@ -670,6 +670,15 @@ int convertTo(const uchar *src_data, size_t src_step, int src_depth, uchar *dst_data, size_t dst_step, int dst_depth, int width, int height, double scale, double shift) { if (src_depth != dst_depth) { + // type conversion + if (scale == 1.0 && shift == 0.0) { + // uint8 to float32 + if (src_depth == CV_8U && dst_depth == CV_32F) { + return convert_error(intrinsiccv_type_conversion_u8_f32( + reinterpret_cast(src_data), src_step, + reinterpret_cast(dst_data), dst_step, width, height)); + } + } return CV_HAL_ERROR_NOT_IMPLEMENTED; } diff --git a/intrinsiccv/include/intrinsiccv/intrinsiccv.h b/intrinsiccv/include/intrinsiccv/intrinsiccv.h index c58397274..6f97ebeaa 100644 --- a/intrinsiccv/include/intrinsiccv/intrinsiccv.h +++ b/intrinsiccv/include/intrinsiccv/intrinsiccv.h @@ -666,6 +666,35 @@ INTRINSICCV_API_DECLARATION(intrinsiccv_yuv_sp_to_bgra_u8, const uint8_t *src_y, size_t dst_stride, size_t width, size_t height, bool is_nv21); +/// Converts the elements in `src` from an integer type to a floating-point +/// type, then stores the result in `dst`. +/// +/// Each resulting element is saturated, i.e. it is the smallest/largest +/// number of the type of the element if the result would underflow/overflow. +/// Source and destination data length is `width` * `height`. Number of elements +/// is limited to @ref INTRINSICCV_MAX_IMAGE_PIXELS. +/// +/// @param src Pointer to the source data. Must be non-null. +/// @param src_stride Distance in bytes from the start of one row to the +/// start of the next row for the source data. Must +/// not be less than width * sizeof(type). +/// Must be a multiple of sizeof(type). +/// @param dst Pointer to the destination data. Must be non-null. +/// @param dst_stride Distance in bytes from the start of one row to the +/// start of the next row for the destination data. Must +/// not be less than width * sizeof(type). +/// Must be a multiple of sizeof(type). +/// @param width Number of pixels in a row. +/// @param height Number of rows in the data. +/// +INTRINSICCV_API_DECLARATION(intrinsiccv_type_conversion_u8_f32, + const uint8_t *src, size_t src_stride, float *dst, + size_t dst_stride, size_t width, size_t height); +/// @copydoc intrinsiccv_type_conversion_u8_f32 +INTRINSICCV_API_DECLARATION(intrinsiccv_type_conversion_s8_f32, + const int8_t *src, size_t src_stride, float *dst, + size_t dst_stride, size_t width, size_t height); + /// Performs a comparison of each element's value in `src` with respect to a /// caller defined threshold. The strictly larger elements are set to /// `value` and the rest to 0. diff --git a/intrinsiccv/include/intrinsiccv/sve2.h b/intrinsiccv/include/intrinsiccv/sve2.h index cd326d0c7..df7b4f3e9 100644 --- a/intrinsiccv/include/intrinsiccv/sve2.h +++ b/intrinsiccv/include/intrinsiccv/sve2.h @@ -491,7 +491,7 @@ class RemainingPathAdapter : public OperationBase { } }; // end of class RemainingPathAdapter -// Shorthand for applying a generic unrolled NEON operation. +// Shorthand for applying a generic unrolled SVE2 operation. template void apply_operation_by_rows(OperationType &operation, ArgTypes &&...args) INTRINSICCV_STREAMING_COMPATIBLE { -- GitLab From 1ba237ccf422ee145c4e2928f67b35b715e0f17d Mon Sep 17 00:00:00 2001 From: Ioana Ghiban Date: Wed, 3 Apr 2024 15:32:08 +0200 Subject: [PATCH 2/5] Add tests for uint8 to float type conversion --- scripts/ci.sh | 7 +- test/api/test_uin8_to_float.cpp | 257 ++++++++++++++++++++++++++++++++ test/framework/array.h | 16 ++ 3 files changed, 277 insertions(+), 3 deletions(-) create mode 100644 test/api/test_uin8_to_float.cpp diff --git a/scripts/ci.sh b/scripts/ci.sh index 3eda8c4f9..2f3124ef4 100755 --- a/scripts/ci.sh +++ b/scripts/ci.sh @@ -49,16 +49,17 @@ ninja -C build/gcc # Run tests LONG_VECTOR_TESTS="GRAY2.*:RGB*" +TYPE_CONVERSION_TESTS="-TypeConversion*" TESTRESULT=0 qemu-aarch64 build/test/framework/intrinsiccv-framework-test --gtest_output=xml:build/test-results/ || TESTRESULT=1 -qemu-aarch64 -cpu cortex-a35 build/test/api/intrinsiccv-api-test --gtest_output=xml:build/test-results/clang-neon/ || TESTRESULT=1 +qemu-aarch64 -cpu cortex-a35 build/test/api/intrinsiccv-api-test --gtest_filter="${TYPE_CONVERSION_TESTS}": --gtest_output=xml:build/test-results/clang-neon/ || TESTRESULT=1 qemu-aarch64 -cpu max,sve128=on,sme=off \ build/test/api/intrinsiccv-api-test --gtest_output=xml:build/test-results/clang-sve128/ --vector-length=16 || TESTRESULT=1 qemu-aarch64 -cpu max,sve2048=on,sve-default-vector-length=256,sme=off \ build/test/api/intrinsiccv-api-test --gtest_filter="${LONG_VECTOR_TESTS}" --gtest_output=xml:build/test-results/clang-sve2048/ --vector-length=256 || TESTRESULT=1 qemu-aarch64 -cpu max,sve128=on,sme512=on \ build/test/api/intrinsiccv-api-test --gtest_output=xml:build/test-results/clang-sme/ --vector-length=64 || TESTRESULT=1 -qemu-aarch64 -cpu cortex-a35 build/gcc/test/api/intrinsiccv-api-test --gtest_output=xml:build/test-results/gcc-neon/ || TESTRESULT=1 +qemu-aarch64 -cpu cortex-a35 build/gcc/test/api/intrinsiccv-api-test --gtest_filter="${TYPE_CONVERSION_TESTS}" --gtest_output=xml:build/test-results/gcc-neon/ || TESTRESULT=1 scripts/prefix_testsuite_names.py build/test-results/clang-neon/intrinsiccv-api-test.xml "clang-neon." scripts/prefix_testsuite_names.py build/test-results/clang-sve128/intrinsiccv-api-test.xml "clang-sve128." @@ -77,7 +78,7 @@ if [[ $(dpkg --print-architecture) = arm64 ]]; then -DINTRINSICCV_ENABLE_SME2=OFF \ -DCMAKE_CXX_FLAGS="-fsanitize=address,undefined -fno-sanitize-recover=all -Wno-pass-failed" ninja -C build/sanitize intrinsiccv-api-test - build/sanitize/test/api/intrinsiccv-api-test + build/sanitize/test/api/intrinsiccv-api-test --gtest_filter="${TYPE_CONVERSION_TESTS}" fi # Build benchmarks, just to prevent bitrot. diff --git a/test/api/test_uin8_to_float.cpp b/test/api/test_uin8_to_float.cpp new file mode 100644 index 000000000..78eb4f980 --- /dev/null +++ b/test/api/test_uin8_to_float.cpp @@ -0,0 +1,257 @@ +// SPDX-FileCopyrightText: 2024 Arm Limited and/or its affiliates +// +// SPDX-License-Identifier: Apache-2.0 + +#include + +#include "framework/array.h" +#include "framework/generator.h" +#include "framework/operation.h" +#include "framework/utils.h" +#include "intrinsiccv/intrinsiccv.h" +#include "test_config.h" + +#define INTRINSICCV_TYPE_CONVERSION(itype, input_type_name) \ + INTRINSICCV_API(uint8_t_to_float, \ + intrinsiccv_type_conversion_##input_type_name##_f32, itype) + +INTRINSICCV_TYPE_CONVERSION(uint8_t, u8); + +template +class TypeConversionTest final { + using OutputType = float; + + struct Elements { + size_t width{}; + size_t height{}; + + std::vector> source_rows; + std::vector> expected_rows; + + Elements(size_t _width, size_t _height, + std::vector>&& _source_rows, + std::vector>&& _expected_rows) + : width(_width), + height(_height), + source_rows(std::move(_source_rows)), + expected_rows(std::move(_expected_rows)) {} + }; + + const Elements test_case_custom = { + // clang-format off + 4, 6, + {{ + { 0, 0, 254, 255 }, + { 0, 0, 0, 0 }, + { 0, 0, 0, 254 }, + { 254, 255, 113, 114 }, + { 112, 113, 114, 115 }, + { 12, 12, 12, 12 } + }}, + {{ + { 0, 0, 254.0, 255.0 }, + { 0, 0, 0, 0 }, + { 0, 0, 0, 254.0 }, + { 254.0, 255.0, 113.0, 114.0 }, + { 112.0, 113.0, 114.0, 115.0 }, + { 12.0, 12.0, 12.0, 12.0 } + }} + // clang-format on + }; + + public: + void test_custom() { + const size_t& width = test_case_custom.width; + const size_t& height = test_case_custom.height; + + test::Array2D source(width, height, 1, 1); + test::Array2D expected(width, height, 1, 1); + test::Array2D actual(width, height, 1, 1); + + for (size_t i = 0; i < height; i++) { + source.set(i, 0, test_case_custom.source_rows[i]); + expected.set(i, 0, test_case_custom.expected_rows[i]); + } + + ASSERT_EQ(INTRINSICCV_OK, uint8_t_to_float()( + source.data(), source.stride(), actual.data(), + actual.stride(), width, height)); + + EXPECT_EQ_ARRAY2D(expected, actual); + } + + void test_fill(const size_t width, const size_t height) { + test::Array2D source(width, height, 1, 1); + test::Array2D expected(width, height, 1, 1); + test::Array2D actual(width, height, 1, 1); + + source.fill(13); + expected.fill(13.0); + + actual.fill(0); + + ASSERT_EQ(INTRINSICCV_OK, uint8_t_to_float()( + source.data(), source.stride(), actual.data(), + actual.stride(), width, height)); + + EXPECT_EQ_ARRAY2D(expected, actual); + } +}; + +template +class TypeConversionTestLinear final { + using OutputType = float; + + public: + // minimum_size set by caller to trigger the 'big' path. + void test_scalar(size_t minimum_size = 1) { + size_t width = test::Options::vector_length() - 1; + test_linear(width, minimum_size); + } + + void test_vector(size_t minimum_size = 1) { + size_t width = test::Options::vector_length() * 2; + test_linear(width, minimum_size); + } + + protected: + static constexpr ElementType min() { + return std::numeric_limits::min(); + } + static constexpr ElementType max() { + return std::numeric_limits::max(); + } + + private: + class GenerateLinearSeries : public test::Generator { + public: + explicit GenerateLinearSeries(ElementType start_from) + : counter_{start_from} {} + + std::optional next() override { return counter_++; } + + private: + ElementType counter_; + }; // end of class GenerateLinearSeries + + void test_linear(size_t width, size_t minimum_size) { + size_t image_size = + std::max(minimum_size, static_cast(max() - min())); + size_t height = image_size / width + 1; + test::Array2D source(width, height, 1, 1); + test::Array2D expected(width, height, 1, 1); + test::Array2D actual = + test::Array2D(width, height, 1, 1); + + GenerateLinearSeries generator(min()); + + source.fill(generator); + + calculate_expected(source, expected); + + ASSERT_EQ(INTRINSICCV_OK, uint8_t_to_float()( + source.data(), source.stride(), actual.data(), + actual.stride(), width, height)); + + EXPECT_EQ_ARRAY2D(expected, actual); + } + + protected: + void calculate_expected(const test::Array2D& source, + test::Array2D& expected) { + for (size_t hindex = 0; hindex < source.height(); ++hindex) { + for (size_t vindex = 0; vindex < source.width(); ++vindex) { + // NOLINTBEGIN(clang-analyzer-core.uninitialized.Assign) + *expected.at(hindex, vindex) = *source.at(hindex, vindex); + // NOLINTEND(clang-analyzer-core.uninitialized.Assign) + } + } + } +}; + +template +class TypeConversion : public testing::Test {}; + +using ElementTypes = ::testing::Types; + +// Tests intrinsiccv_uint8_t_to_float API. +TYPED_TEST_SUITE(TypeConversion, ElementTypes); + +TYPED_TEST(TypeConversion, NullPointer) { + TypeParam src[1] = {}; + float dst[1]; + test::test_null_args(uint8_t_to_float(), src, sizeof(TypeParam), + dst, sizeof(float), 1, 1); +} + +TYPED_TEST(TypeConversion, OversizeImage) { + TypeParam src[1] = {}; + float dst[1]; + EXPECT_EQ( + INTRINSICCV_ERROR_RANGE, + uint8_t_to_float()(src, sizeof(TypeParam), dst, sizeof(float), + INTRINSICCV_MAX_IMAGE_PIXELS + 1, 1)); + EXPECT_EQ( + INTRINSICCV_ERROR_RANGE, + uint8_t_to_float()(src, sizeof(TypeParam), dst, sizeof(float), + 1, INTRINSICCV_MAX_IMAGE_PIXELS + 1)); + EXPECT_EQ( + INTRINSICCV_ERROR_RANGE, + uint8_t_to_float()(src, sizeof(TypeParam), dst, sizeof(float), + INTRINSICCV_MAX_IMAGE_PIXELS + 1, + INTRINSICCV_MAX_IMAGE_PIXELS + 1)); +} + +TYPED_TEST(TypeConversion, Custom) { + TypeConversionTest{}.test_custom(); +} + +TYPED_TEST(TypeConversion, Scalar) { + TypeConversionTestLinear{}.test_scalar(); +} +TYPED_TEST(TypeConversion, Vector) { + TypeConversionTestLinear{}.test_vector(); +} + +TYPED_TEST(TypeConversion, Scalar1Tbx) { + TypeConversionTestLinear{}.test_scalar(2500); + TypeConversionTestLinear{}.test_scalar(2500); +} +TYPED_TEST(TypeConversion, Vector1Tbx) { + TypeConversionTestLinear{}.test_vector(2500); + TypeConversionTestLinear{}.test_vector(2500); +} + +TYPED_TEST(TypeConversion, CustomFits128VectorSize) { + TypeConversionTest{}.test_fill(4, 1); +} +TYPED_TEST(TypeConversion, CustomFits128VectorSize2x) { + TypeConversionTest{}.test_fill(4, 2); +} +TYPED_TEST(TypeConversion, CustomFits128VectorSize3x) { + TypeConversionTest{}.test_fill(4, 3); +} +TYPED_TEST(TypeConversion, CustomFits512VectorSize) { + TypeConversionTest{}.test_fill(4, 4); +} +TYPED_TEST(TypeConversion, CustomFits512VectorSize2x) { + TypeConversionTest{}.test_fill(4, 8); +} +TYPED_TEST(TypeConversion, CustomFits512VectorSize3x) { + TypeConversionTest{}.test_fill(6, 8); +} +TYPED_TEST(TypeConversion, Custom128OneRemaining) { + TypeConversionTest{}.test_fill(1, 17); +} +TYPED_TEST(TypeConversion, Custom128AllButOneRemaining) { + TypeConversionTest{}.test_fill(5, 3); +} +TYPED_TEST(TypeConversion, CustomAboutHalfRemaining) { + TypeConversionTest{}.test_fill(19, 2); +} +TYPED_TEST(TypeConversion, CustomEmpty) { + TypeConversionTest{}.test_fill(0, 0); +} +TYPED_TEST(TypeConversion, CustomOne) { + TypeConversionTest{}.test_fill(1, 1); +} diff --git a/test/framework/array.h b/test/framework/array.h index f854c9819..338a3bd2c 100644 --- a/test/framework/array.h +++ b/test/framework/array.h @@ -124,6 +124,22 @@ class Array2D : public TwoDimensional { } } + // Sets values in a row starting at a given column from a const vector. + void set(size_t row, size_t column, const std::vector &values) { + ASSERT_EQ(valid(), true) << "Array is invalid."; + ASSERT_GE(width() - column, values.size()); + + ElementType *ptr = at(row, column); + if (!ptr) { + return; + } + + size_t index = 0; + for (ElementType value : values) { + ptr[index++] = value; + } + } + // Sets values in a row starting at a given column. void set(size_t row, size_t column, std::initializer_list values) { -- GitLab From 6b8146f15a64dfbb688a97fe65dd215614f2107f Mon Sep 17 00:00:00 2001 From: Ioana Ghiban Date: Fri, 5 Apr 2024 11:58:26 +0200 Subject: [PATCH 3/5] Implement int8 to float type conversion --- .../src/conversions/int8_to_float_api.cpp | 50 +++++++++++ .../src/conversions/int8_to_float_neon.cpp | 25 ++++++ .../src/conversions/int8_to_float_sc.h | 88 +++++++++++++++++++ .../src/conversions/int8_to_float_sme2.cpp | 27 ++++++ .../src/conversions/int8_to_float_sve2.cpp | 27 ++++++ 5 files changed, 217 insertions(+) create mode 100644 intrinsiccv/src/conversions/int8_to_float_api.cpp create mode 100644 intrinsiccv/src/conversions/int8_to_float_neon.cpp create mode 100644 intrinsiccv/src/conversions/int8_to_float_sc.h create mode 100644 intrinsiccv/src/conversions/int8_to_float_sme2.cpp create mode 100644 intrinsiccv/src/conversions/int8_to_float_sve2.cpp diff --git a/intrinsiccv/src/conversions/int8_to_float_api.cpp b/intrinsiccv/src/conversions/int8_to_float_api.cpp new file mode 100644 index 000000000..a7b06e5be --- /dev/null +++ b/intrinsiccv/src/conversions/int8_to_float_api.cpp @@ -0,0 +1,50 @@ +// SPDX-FileCopyrightText: 2024 Arm Limited and/or its affiliates +// +// SPDX-License-Identifier: Apache-2.0 + +#include "intrinsiccv/dispatch.h" +#include "intrinsiccv/intrinsiccv.h" +#include "intrinsiccv/types.h" + +namespace intrinsiccv { + +namespace neon { + +template +intrinsiccv_error_t type_conversion_int_to_float(const T* src, + size_t src_stride, float* dst, + size_t dst_stride, + size_t width, size_t height); + +} // namespace neon + +namespace sve2 { + +template +intrinsiccv_error_t type_conversion_int_to_float(const T* src, + size_t src_stride, float* dst, + size_t dst_stride, + size_t width, size_t height); + +} // namespace sve2 + +namespace sme2 { + +template +intrinsiccv_error_t type_conversion_int_to_float(const T* src, + size_t src_stride, float* dst, + size_t dst_stride, + size_t width, size_t height); + +} // namespace sme2 + +#define INTRINSICCV_DEFINE_C_API(name, type) \ + INTRINSICCV_MULTIVERSION_C_API( \ + name, intrinsiccv::neon::type_conversion_int_to_float, \ + INTRINSICCV_SVE2_IMPL_IF( \ + intrinsiccv::sve2::type_conversion_int_to_float), \ + intrinsiccv::sme2::type_conversion_int_to_float) + +INTRINSICCV_DEFINE_C_API(intrinsiccv_type_conversion_u8_f32, uint8_t); +INTRINSICCV_DEFINE_C_API(intrinsiccv_type_conversion_s8_f32, int8_t); +} // namespace intrinsiccv diff --git a/intrinsiccv/src/conversions/int8_to_float_neon.cpp b/intrinsiccv/src/conversions/int8_to_float_neon.cpp new file mode 100644 index 000000000..9c164737b --- /dev/null +++ b/intrinsiccv/src/conversions/int8_to_float_neon.cpp @@ -0,0 +1,25 @@ +// SPDX-FileCopyrightText: 2024 Arm Limited and/or its affiliates +// +// SPDX-License-Identifier: Apache-2.0 + +#include "intrinsiccv/intrinsiccv.h" +#include "intrinsiccv/neon.h" + +namespace intrinsiccv::neon { + +template +intrinsiccv_error_t type_conversion_int_to_float(const T*, size_t, float*, + size_t, size_t, size_t) { + return INTRINSICCV_ERROR_NOT_IMPLEMENTED; +} + +#define INTRINSICCV_INSTANTIATE_TEMPLATE(type) \ + template INTRINSICCV_TARGET_FN_ATTRS intrinsiccv_error_t \ + type_conversion_int_to_float(const type* src, size_t src_stride, \ + float* dst, size_t dst_stride, \ + size_t width, size_t height) + +INTRINSICCV_INSTANTIATE_TEMPLATE(uint8_t); +INTRINSICCV_INSTANTIATE_TEMPLATE(int8_t); + +} // namespace intrinsiccv::neon diff --git a/intrinsiccv/src/conversions/int8_to_float_sc.h b/intrinsiccv/src/conversions/int8_to_float_sc.h new file mode 100644 index 000000000..e01b8dff7 --- /dev/null +++ b/intrinsiccv/src/conversions/int8_to_float_sc.h @@ -0,0 +1,88 @@ +// SPDX-FileCopyrightText: 2024 Arm Limited and/or its affiliates +// +// SPDX-License-Identifier: Apache-2.0 + +#ifndef INTRINSICCV_FLOAT_TO_INT_SC_H +#define INTRINSICCV_FLOAT_TO_INT_SC_H + +#include + +#include "intrinsiccv/intrinsiccv.h" +#include "intrinsiccv/sve2.h" + +namespace INTRINSICCV_TARGET_NAMESPACE { + +template +class int_to_float_operation final { + public: + using SrcVecTraits = INTRINSICCV_TARGET_NAMESPACE::VecTraits< + std::conditional_t, int8_t, uint8_t>>; + using SrcVectorType = typename SrcVecTraits::VectorType; + using DstVecTraits = INTRINSICCV_TARGET_NAMESPACE::VecTraits; + using DstVectorType = typename DstVecTraits::VectorType; + + using VecTraits = DstVecTraits; + + void process_row(size_t width, Columns src, + Columns dst) { + LoopUnroll{width, VecTraits::num_lanes()} + .unroll_twice([&](size_t step) INTRINSICCV_STREAMING_COMPATIBLE { + svbool_t pg = VecTraits::svptrue(); + DstVectorType dst_vector1 = vector_path(pg, &src[0]); + DstVectorType dst_vector2 = vector_path( + pg, &src.at(ptrdiff_t(VecTraits::num_lanes()))[0]); + svst1(pg, &dst[0], dst_vector1); + svst1_vnum(pg, &dst[0], 1, dst_vector2); + src += ptrdiff_t(step); + dst += ptrdiff_t(step); + }) + .remaining([&](size_t length, size_t) INTRINSICCV_STREAMING_COMPATIBLE { + size_t index = 0; + svbool_t pg = VecTraits::svwhilelt(index, length); + while (svptest_first(VecTraits::svptrue(), pg)) { + DstVectorType dst_vector = + vector_path(pg, &src[ptrdiff_t(index)]); + svst1(pg, &dst[ptrdiff_t(index)], dst_vector); + // Update loop counter and calculate the next governing predicate. + index += VecTraits::num_lanes(); + pg = VecTraits::svwhilelt(index, length); + } + }); + } + + private: + template , int> = 0> + DstVectorType vector_path(svbool_t& pg, + const T* src) INTRINSICCV_STREAMING_COMPATIBLE { + svuint32_t src_vector = svld1ub_u32(pg, src); + return svcvt_f32_u32_x(pg, src_vector); + } + + template , int> = 0> + DstVectorType vector_path(svbool_t& pg, + const T* src) INTRINSICCV_STREAMING_COMPATIBLE { + svint32_t src_vector = svld1sb_s32(pg, src); + return svcvt_f32_s32_x(pg, src_vector); + } +}; // end of class int_to_float_operation + +template +static intrinsiccv_error_t type_conversion_int_to_float_sc( + const T* src, size_t src_stride, float* dst, size_t dst_stride, + size_t width, size_t height) INTRINSICCV_STREAMING_COMPATIBLE { + CHECK_POINTER_AND_STRIDE(src, src_stride); + CHECK_POINTER_AND_STRIDE(dst, dst_stride); + CHECK_IMAGE_SIZE(width, height); + + int_to_float_operation operation; + Rectangle rect{width, height}; + Rows src_rows{src, src_stride}; + Rows dst_rows{dst, dst_stride}; + zip_rows(operation, rect, src_rows, dst_rows); + + return INTRINSICCV_OK; +} + +} // namespace INTRINSICCV_TARGET_NAMESPACE + +#endif // INTRINSICCV_INT8_TO_FLOAT_SC_H diff --git a/intrinsiccv/src/conversions/int8_to_float_sme2.cpp b/intrinsiccv/src/conversions/int8_to_float_sme2.cpp new file mode 100644 index 000000000..a5f3a7581 --- /dev/null +++ b/intrinsiccv/src/conversions/int8_to_float_sme2.cpp @@ -0,0 +1,27 @@ +// SPDX-FileCopyrightText: 2024 Arm Limited and/or its affiliates +// +// SPDX-License-Identifier: Apache-2.0 + +#include "int8_to_float_sc.h" + +namespace intrinsiccv::sme2 { + +template +intrinsiccv_error_t type_conversion_int_to_float(const T* src, + size_t src_stride, float* dst, + size_t dst_stride, + size_t width, size_t height) { + return type_conversion_int_to_float_sc(src, src_stride, dst, dst_stride, + width, height); +} + +#define INTRINSICCV_INSTANTIATE_TEMPLATE(type) \ + template INTRINSICCV_TARGET_FN_ATTRS intrinsiccv_error_t \ + type_conversion_int_to_float(const type* src, size_t src_stride, \ + float* dst, size_t dst_stride, \ + size_t width, size_t height) + +INTRINSICCV_INSTANTIATE_TEMPLATE(uint8_t); +INTRINSICCV_INSTANTIATE_TEMPLATE(int8_t); + +} // namespace intrinsiccv::sme2 diff --git a/intrinsiccv/src/conversions/int8_to_float_sve2.cpp b/intrinsiccv/src/conversions/int8_to_float_sve2.cpp new file mode 100644 index 000000000..b19ce0077 --- /dev/null +++ b/intrinsiccv/src/conversions/int8_to_float_sve2.cpp @@ -0,0 +1,27 @@ +// SPDX-FileCopyrightText: 2024 Arm Limited and/or its affiliates +// +// SPDX-License-Identifier: Apache-2.0 + +#include "int8_to_float_sc.h" + +namespace intrinsiccv::sve2 { + +template +intrinsiccv_error_t type_conversion_int_to_float(const T* src, + size_t src_stride, float* dst, + size_t dst_stride, + size_t width, size_t height) { + return type_conversion_int_to_float_sc(src, src_stride, dst, dst_stride, + width, height); +} + +#define INTRINSICCV_INSTANTIATE_TEMPLATE(type) \ + template INTRINSICCV_TARGET_FN_ATTRS intrinsiccv_error_t \ + type_conversion_int_to_float(const type* src, size_t src_stride, \ + float* dst, size_t dst_stride, \ + size_t width, size_t height) + +INTRINSICCV_INSTANTIATE_TEMPLATE(uint8_t); +INTRINSICCV_INSTANTIATE_TEMPLATE(int8_t); + +} // namespace intrinsiccv::sve2 -- GitLab From 3670b89961a0cbab6dcdf31b8085c47fada1d3ce Mon Sep 17 00:00:00 2001 From: Ioana Ghiban Date: Fri, 5 Apr 2024 12:02:19 +0200 Subject: [PATCH 4/5] Add tests for int8 to float type conversion --- scripts/ci.sh | 8 +- ...n8_to_float.cpp => test_int8_to_float.cpp} | 85 ++++++++++--------- 2 files changed, 51 insertions(+), 42 deletions(-) rename test/api/{test_uin8_to_float.cpp => test_int8_to_float.cpp} (72%) diff --git a/scripts/ci.sh b/scripts/ci.sh index 2f3124ef4..60f76f2c2 100755 --- a/scripts/ci.sh +++ b/scripts/ci.sh @@ -49,17 +49,17 @@ ninja -C build/gcc # Run tests LONG_VECTOR_TESTS="GRAY2.*:RGB*" -TYPE_CONVERSION_TESTS="-TypeConversion*" +EXCLUDE_TYPE_CONVERSION_TESTS="-TypeConversion*" TESTRESULT=0 qemu-aarch64 build/test/framework/intrinsiccv-framework-test --gtest_output=xml:build/test-results/ || TESTRESULT=1 -qemu-aarch64 -cpu cortex-a35 build/test/api/intrinsiccv-api-test --gtest_filter="${TYPE_CONVERSION_TESTS}": --gtest_output=xml:build/test-results/clang-neon/ || TESTRESULT=1 +qemu-aarch64 -cpu cortex-a35 build/test/api/intrinsiccv-api-test --gtest_filter="${EXCLUDE_TYPE_CONVERSION_TESTS}": --gtest_output=xml:build/test-results/clang-neon/ || TESTRESULT=1 qemu-aarch64 -cpu max,sve128=on,sme=off \ build/test/api/intrinsiccv-api-test --gtest_output=xml:build/test-results/clang-sve128/ --vector-length=16 || TESTRESULT=1 qemu-aarch64 -cpu max,sve2048=on,sve-default-vector-length=256,sme=off \ build/test/api/intrinsiccv-api-test --gtest_filter="${LONG_VECTOR_TESTS}" --gtest_output=xml:build/test-results/clang-sve2048/ --vector-length=256 || TESTRESULT=1 qemu-aarch64 -cpu max,sve128=on,sme512=on \ build/test/api/intrinsiccv-api-test --gtest_output=xml:build/test-results/clang-sme/ --vector-length=64 || TESTRESULT=1 -qemu-aarch64 -cpu cortex-a35 build/gcc/test/api/intrinsiccv-api-test --gtest_filter="${TYPE_CONVERSION_TESTS}" --gtest_output=xml:build/test-results/gcc-neon/ || TESTRESULT=1 +qemu-aarch64 -cpu cortex-a35 build/gcc/test/api/intrinsiccv-api-test --gtest_filter="${EXCLUDE_TYPE_CONVERSION_TESTS}" --gtest_output=xml:build/test-results/gcc-neon/ || TESTRESULT=1 scripts/prefix_testsuite_names.py build/test-results/clang-neon/intrinsiccv-api-test.xml "clang-neon." scripts/prefix_testsuite_names.py build/test-results/clang-sve128/intrinsiccv-api-test.xml "clang-sve128." @@ -78,7 +78,7 @@ if [[ $(dpkg --print-architecture) = arm64 ]]; then -DINTRINSICCV_ENABLE_SME2=OFF \ -DCMAKE_CXX_FLAGS="-fsanitize=address,undefined -fno-sanitize-recover=all -Wno-pass-failed" ninja -C build/sanitize intrinsiccv-api-test - build/sanitize/test/api/intrinsiccv-api-test --gtest_filter="${TYPE_CONVERSION_TESTS}" + build/sanitize/test/api/intrinsiccv-api-test --gtest_filter="${EXCLUDE_TYPE_CONVERSION_TESTS}" fi # Build benchmarks, just to prevent bitrot. diff --git a/test/api/test_uin8_to_float.cpp b/test/api/test_int8_to_float.cpp similarity index 72% rename from test/api/test_uin8_to_float.cpp rename to test/api/test_int8_to_float.cpp index 78eb4f980..b8a8122a8 100644 --- a/test/api/test_uin8_to_float.cpp +++ b/test/api/test_int8_to_float.cpp @@ -12,24 +12,33 @@ #include "test_config.h" #define INTRINSICCV_TYPE_CONVERSION(itype, input_type_name) \ - INTRINSICCV_API(uint8_t_to_float, \ + INTRINSICCV_API(type_conversion, \ intrinsiccv_type_conversion_##input_type_name##_f32, itype) INTRINSICCV_TYPE_CONVERSION(uint8_t, u8); +INTRINSICCV_TYPE_CONVERSION(int8_t, s8); -template +template class TypeConversionTest final { + protected: + static constexpr InputType min() { + return std::numeric_limits::min(); + } + static constexpr InputType max() { + return std::numeric_limits::max(); + } + using OutputType = float; struct Elements { size_t width{}; size_t height{}; - std::vector> source_rows; + std::vector> source_rows; std::vector> expected_rows; Elements(size_t _width, size_t _height, - std::vector>&& _source_rows, + std::vector>&& _source_rows, std::vector>&& _expected_rows) : width(_width), height(_height), @@ -41,18 +50,18 @@ class TypeConversionTest final { // clang-format off 4, 6, {{ - { 0, 0, 254, 255 }, - { 0, 0, 0, 0 }, - { 0, 0, 0, 254 }, - { 254, 255, 113, 114 }, + { min(), min(), max() - 1, max() }, + { min(), min(), min(), min() }, + { min(), min(), min(), max() - 1 }, + { max() - 1, max(), 113, 114 }, { 112, 113, 114, 115 }, { 12, 12, 12, 12 } }}, {{ - { 0, 0, 254.0, 255.0 }, - { 0, 0, 0, 0 }, - { 0, 0, 0, 254.0 }, - { 254.0, 255.0, 113.0, 114.0 }, + { min(), min(), max() - 1.0, max() }, + { min(), min(), min(), min() }, + { min(), min(), min(), max() - 1.0 }, + { max() - 1.0, max(), 113.0, 114.0 }, { 112.0, 113.0, 114.0, 115.0 }, { 12.0, 12.0, 12.0, 12.0 } }} @@ -64,7 +73,7 @@ class TypeConversionTest final { const size_t& width = test_case_custom.width; const size_t& height = test_case_custom.height; - test::Array2D source(width, height, 1, 1); + test::Array2D source(width, height, 1, 1); test::Array2D expected(width, height, 1, 1); test::Array2D actual(width, height, 1, 1); @@ -73,7 +82,7 @@ class TypeConversionTest final { expected.set(i, 0, test_case_custom.expected_rows[i]); } - ASSERT_EQ(INTRINSICCV_OK, uint8_t_to_float()( + ASSERT_EQ(INTRINSICCV_OK, type_conversion()( source.data(), source.stride(), actual.data(), actual.stride(), width, height)); @@ -81,7 +90,7 @@ class TypeConversionTest final { } void test_fill(const size_t width, const size_t height) { - test::Array2D source(width, height, 1, 1); + test::Array2D source(width, height, 1, 1); test::Array2D expected(width, height, 1, 1); test::Array2D actual(width, height, 1, 1); @@ -90,7 +99,7 @@ class TypeConversionTest final { actual.fill(0); - ASSERT_EQ(INTRINSICCV_OK, uint8_t_to_float()( + ASSERT_EQ(INTRINSICCV_OK, type_conversion()( source.data(), source.stride(), actual.data(), actual.stride(), width, height)); @@ -98,7 +107,7 @@ class TypeConversionTest final { } }; -template +template class TypeConversionTestLinear final { using OutputType = float; @@ -115,30 +124,30 @@ class TypeConversionTestLinear final { } protected: - static constexpr ElementType min() { - return std::numeric_limits::min(); + static constexpr InputType min() { + return std::numeric_limits::min(); } - static constexpr ElementType max() { - return std::numeric_limits::max(); + static constexpr InputType max() { + return std::numeric_limits::max(); } private: - class GenerateLinearSeries : public test::Generator { + class GenerateLinearSeries : public test::Generator { public: - explicit GenerateLinearSeries(ElementType start_from) + explicit GenerateLinearSeries(InputType start_from) : counter_{start_from} {} - std::optional next() override { return counter_++; } + std::optional next() override { return counter_++; } private: - ElementType counter_; + InputType counter_; }; // end of class GenerateLinearSeries void test_linear(size_t width, size_t minimum_size) { size_t image_size = std::max(minimum_size, static_cast(max() - min())); size_t height = image_size / width + 1; - test::Array2D source(width, height, 1, 1); + test::Array2D source(width, height, 1, 1); test::Array2D expected(width, height, 1, 1); test::Array2D actual = test::Array2D(width, height, 1, 1); @@ -149,7 +158,7 @@ class TypeConversionTestLinear final { calculate_expected(source, expected); - ASSERT_EQ(INTRINSICCV_OK, uint8_t_to_float()( + ASSERT_EQ(INTRINSICCV_OK, type_conversion()( source.data(), source.stride(), actual.data(), actual.stride(), width, height)); @@ -157,7 +166,7 @@ class TypeConversionTestLinear final { } protected: - void calculate_expected(const test::Array2D& source, + void calculate_expected(const test::Array2D& source, test::Array2D& expected) { for (size_t hindex = 0; hindex < source.height(); ++hindex) { for (size_t vindex = 0; vindex < source.width(); ++vindex) { @@ -172,15 +181,15 @@ class TypeConversionTestLinear final { template class TypeConversion : public testing::Test {}; -using ElementTypes = ::testing::Types; +using ElementTypes = ::testing::Types; -// Tests intrinsiccv_uint8_t_to_float API. +// Tests intrinsiccv_type_conversion API. TYPED_TEST_SUITE(TypeConversion, ElementTypes); TYPED_TEST(TypeConversion, NullPointer) { TypeParam src[1] = {}; float dst[1]; - test::test_null_args(uint8_t_to_float(), src, sizeof(TypeParam), + test::test_null_args(type_conversion(), src, sizeof(TypeParam), dst, sizeof(float), 1, 1); } @@ -189,17 +198,17 @@ TYPED_TEST(TypeConversion, OversizeImage) { float dst[1]; EXPECT_EQ( INTRINSICCV_ERROR_RANGE, - uint8_t_to_float()(src, sizeof(TypeParam), dst, sizeof(float), - INTRINSICCV_MAX_IMAGE_PIXELS + 1, 1)); + type_conversion()(src, sizeof(TypeParam), dst, sizeof(float), + INTRINSICCV_MAX_IMAGE_PIXELS + 1, 1)); EXPECT_EQ( INTRINSICCV_ERROR_RANGE, - uint8_t_to_float()(src, sizeof(TypeParam), dst, sizeof(float), - 1, INTRINSICCV_MAX_IMAGE_PIXELS + 1)); + type_conversion()(src, sizeof(TypeParam), dst, sizeof(float), + 1, INTRINSICCV_MAX_IMAGE_PIXELS + 1)); EXPECT_EQ( INTRINSICCV_ERROR_RANGE, - uint8_t_to_float()(src, sizeof(TypeParam), dst, sizeof(float), - INTRINSICCV_MAX_IMAGE_PIXELS + 1, - INTRINSICCV_MAX_IMAGE_PIXELS + 1)); + type_conversion()(src, sizeof(TypeParam), dst, sizeof(float), + INTRINSICCV_MAX_IMAGE_PIXELS + 1, + INTRINSICCV_MAX_IMAGE_PIXELS + 1)); } TYPED_TEST(TypeConversion, Custom) { -- GitLab From 837adf7442f33e5474fe1af383465544b3ccc8ca Mon Sep 17 00:00:00 2001 From: Ioana Ghiban Date: Fri, 5 Apr 2024 16:50:34 +0200 Subject: [PATCH 5/5] Allow different input and output types in the tests --- test/api/test_int8_to_float.cpp | 163 +++++++++++++++++++------------- test/framework/utils.h | 8 ++ 2 files changed, 107 insertions(+), 64 deletions(-) diff --git a/test/api/test_int8_to_float.cpp b/test/api/test_int8_to_float.cpp index b8a8122a8..13d66cca5 100644 --- a/test/api/test_int8_to_float.cpp +++ b/test/api/test_int8_to_float.cpp @@ -11,14 +11,17 @@ #include "intrinsiccv/intrinsiccv.h" #include "test_config.h" -#define INTRINSICCV_TYPE_CONVERSION(itype, input_type_name) \ - INTRINSICCV_API(type_conversion, \ - intrinsiccv_type_conversion_##input_type_name##_f32, itype) +#define INTRINSICCV_TYPE_CONVERSION(itype, input_type_name, otype, \ + output_type_name) \ + INTRINSICCV_DIFF_IO_API( \ + type_conversion, \ + intrinsiccv_type_conversion_##input_type_name##_##output_type_name, \ + itype, otype) -INTRINSICCV_TYPE_CONVERSION(uint8_t, u8); -INTRINSICCV_TYPE_CONVERSION(int8_t, s8); +INTRINSICCV_TYPE_CONVERSION(uint8_t, u8, float, f32); +INTRINSICCV_TYPE_CONVERSION(int8_t, s8, float, f32); -template +template class TypeConversionTest final { protected: static constexpr InputType min() { @@ -28,8 +31,6 @@ class TypeConversionTest final { return std::numeric_limits::max(); } - using OutputType = float; - struct Elements { size_t width{}; size_t height{}; @@ -82,7 +83,7 @@ class TypeConversionTest final { expected.set(i, 0, test_case_custom.expected_rows[i]); } - ASSERT_EQ(INTRINSICCV_OK, type_conversion()( + ASSERT_EQ(INTRINSICCV_OK, (type_conversion())( source.data(), source.stride(), actual.data(), actual.stride(), width, height)); @@ -99,18 +100,16 @@ class TypeConversionTest final { actual.fill(0); - ASSERT_EQ(INTRINSICCV_OK, type_conversion()( + ASSERT_EQ(INTRINSICCV_OK, (type_conversion()( source.data(), source.stride(), actual.data(), - actual.stride(), width, height)); + actual.stride(), width, height))); EXPECT_EQ_ARRAY2D(expected, actual); } }; -template +template class TypeConversionTestLinear final { - using OutputType = float; - public: // minimum_size set by caller to trigger the 'big' path. void test_scalar(size_t minimum_size = 1) { @@ -131,6 +130,17 @@ class TypeConversionTestLinear final { return std::numeric_limits::max(); } + void calculate_expected(const test::Array2D& source, + test::Array2D& expected) { + for (size_t hindex = 0; hindex < source.height(); ++hindex) { + for (size_t vindex = 0; vindex < source.width(); ++vindex) { + // NOLINTBEGIN(clang-analyzer-core.uninitialized.Assign) + *expected.at(hindex, vindex) = *source.at(hindex, vindex); + // NOLINTEND(clang-analyzer-core.uninitialized.Assign) + } + } + } + private: class GenerateLinearSeries : public test::Generator { public: @@ -158,109 +168,134 @@ class TypeConversionTestLinear final { calculate_expected(source, expected); - ASSERT_EQ(INTRINSICCV_OK, type_conversion()( + ASSERT_EQ(INTRINSICCV_OK, (type_conversion()( source.data(), source.stride(), actual.data(), - actual.stride(), width, height)); + actual.stride(), width, height))); EXPECT_EQ_ARRAY2D(expected, actual); } - - protected: - void calculate_expected(const test::Array2D& source, - test::Array2D& expected) { - for (size_t hindex = 0; hindex < source.height(); ++hindex) { - for (size_t vindex = 0; vindex < source.width(); ++vindex) { - // NOLINTBEGIN(clang-analyzer-core.uninitialized.Assign) - *expected.at(hindex, vindex) = *source.at(hindex, vindex); - // NOLINTEND(clang-analyzer-core.uninitialized.Assign) - } - } - } }; template class TypeConversion : public testing::Test {}; -using ElementTypes = ::testing::Types; +using ElementTypes = + ::testing::Types, std::pair>; // Tests intrinsiccv_type_conversion API. TYPED_TEST_SUITE(TypeConversion, ElementTypes); TYPED_TEST(TypeConversion, NullPointer) { - TypeParam src[1] = {}; - float dst[1]; - test::test_null_args(type_conversion(), src, sizeof(TypeParam), - dst, sizeof(float), 1, 1); + using InputType = typename TypeParam::first_type; + using OutputType = typename TypeParam::second_type; + InputType src[1] = {}; + OutputType dst[1]; + test::test_null_args(type_conversion(), src, + sizeof(InputType), dst, sizeof(OutputType), 1, 1); } TYPED_TEST(TypeConversion, OversizeImage) { - TypeParam src[1] = {}; - float dst[1]; - EXPECT_EQ( - INTRINSICCV_ERROR_RANGE, - type_conversion()(src, sizeof(TypeParam), dst, sizeof(float), - INTRINSICCV_MAX_IMAGE_PIXELS + 1, 1)); - EXPECT_EQ( - INTRINSICCV_ERROR_RANGE, - type_conversion()(src, sizeof(TypeParam), dst, sizeof(float), - 1, INTRINSICCV_MAX_IMAGE_PIXELS + 1)); + using InputType = typename TypeParam::first_type; + using OutputType = typename TypeParam::second_type; + InputType src[1] = {}; + OutputType dst[1]; + EXPECT_EQ(INTRINSICCV_ERROR_RANGE, + (type_conversion()( + src, sizeof(InputType), dst, sizeof(OutputType), + INTRINSICCV_MAX_IMAGE_PIXELS + 1, 1))); + EXPECT_EQ(INTRINSICCV_ERROR_RANGE, + (type_conversion()( + src, sizeof(InputType), dst, sizeof(OutputType), 1, + INTRINSICCV_MAX_IMAGE_PIXELS + 1))); EXPECT_EQ( INTRINSICCV_ERROR_RANGE, - type_conversion()(src, sizeof(TypeParam), dst, sizeof(float), - INTRINSICCV_MAX_IMAGE_PIXELS + 1, - INTRINSICCV_MAX_IMAGE_PIXELS + 1)); + (type_conversion()( + src, sizeof(TypeParam), dst, sizeof(OutputType), + INTRINSICCV_MAX_IMAGE_PIXELS + 1, INTRINSICCV_MAX_IMAGE_PIXELS + 1))); } TYPED_TEST(TypeConversion, Custom) { - TypeConversionTest{}.test_custom(); + using InputType = typename TypeParam::first_type; + using OutputType = typename TypeParam::second_type; + TypeConversionTest{}.test_custom(); } TYPED_TEST(TypeConversion, Scalar) { - TypeConversionTestLinear{}.test_scalar(); + using InputType = typename TypeParam::first_type; + using OutputType = typename TypeParam::second_type; + TypeConversionTestLinear{}.test_scalar(); } TYPED_TEST(TypeConversion, Vector) { - TypeConversionTestLinear{}.test_vector(); + using InputType = typename TypeParam::first_type; + using OutputType = typename TypeParam::second_type; + TypeConversionTestLinear{}.test_vector(); } TYPED_TEST(TypeConversion, Scalar1Tbx) { - TypeConversionTestLinear{}.test_scalar(2500); - TypeConversionTestLinear{}.test_scalar(2500); + using InputType = typename TypeParam::first_type; + using OutputType = typename TypeParam::second_type; + TypeConversionTestLinear{}.test_scalar(2500); + TypeConversionTestLinear{}.test_scalar(2500); } TYPED_TEST(TypeConversion, Vector1Tbx) { - TypeConversionTestLinear{}.test_vector(2500); - TypeConversionTestLinear{}.test_vector(2500); + using InputType = typename TypeParam::first_type; + using OutputType = typename TypeParam::second_type; + TypeConversionTestLinear{}.test_vector(2500); + TypeConversionTestLinear{}.test_vector(2500); } TYPED_TEST(TypeConversion, CustomFits128VectorSize) { - TypeConversionTest{}.test_fill(4, 1); + using InputType = typename TypeParam::first_type; + using OutputType = typename TypeParam::second_type; + TypeConversionTest{}.test_fill(4, 1); } TYPED_TEST(TypeConversion, CustomFits128VectorSize2x) { - TypeConversionTest{}.test_fill(4, 2); + using InputType = typename TypeParam::first_type; + using OutputType = typename TypeParam::second_type; + TypeConversionTest{}.test_fill(4, 2); } TYPED_TEST(TypeConversion, CustomFits128VectorSize3x) { - TypeConversionTest{}.test_fill(4, 3); + using InputType = typename TypeParam::first_type; + using OutputType = typename TypeParam::second_type; + TypeConversionTest{}.test_fill(4, 3); } TYPED_TEST(TypeConversion, CustomFits512VectorSize) { - TypeConversionTest{}.test_fill(4, 4); + using InputType = typename TypeParam::first_type; + using OutputType = typename TypeParam::second_type; + TypeConversionTest{}.test_fill(4, 4); } TYPED_TEST(TypeConversion, CustomFits512VectorSize2x) { - TypeConversionTest{}.test_fill(4, 8); + using InputType = typename TypeParam::first_type; + using OutputType = typename TypeParam::second_type; + TypeConversionTest{}.test_fill(4, 8); } TYPED_TEST(TypeConversion, CustomFits512VectorSize3x) { - TypeConversionTest{}.test_fill(6, 8); + using InputType = typename TypeParam::first_type; + using OutputType = typename TypeParam::second_type; + TypeConversionTest{}.test_fill(6, 8); } TYPED_TEST(TypeConversion, Custom128OneRemaining) { - TypeConversionTest{}.test_fill(1, 17); + using InputType = typename TypeParam::first_type; + using OutputType = typename TypeParam::second_type; + TypeConversionTest{}.test_fill(1, 17); } TYPED_TEST(TypeConversion, Custom128AllButOneRemaining) { - TypeConversionTest{}.test_fill(5, 3); + using InputType = typename TypeParam::first_type; + using OutputType = typename TypeParam::second_type; + TypeConversionTest{}.test_fill(5, 3); } TYPED_TEST(TypeConversion, CustomAboutHalfRemaining) { - TypeConversionTest{}.test_fill(19, 2); + using InputType = typename TypeParam::first_type; + using OutputType = typename TypeParam::second_type; + TypeConversionTest{}.test_fill(19, 2); } TYPED_TEST(TypeConversion, CustomEmpty) { - TypeConversionTest{}.test_fill(0, 0); + using InputType = typename TypeParam::first_type; + using OutputType = typename TypeParam::second_type; + TypeConversionTest{}.test_fill(0, 0); } TYPED_TEST(TypeConversion, CustomOne) { - TypeConversionTest{}.test_fill(1, 1); + using InputType = typename TypeParam::first_type; + using OutputType = typename TypeParam::second_type; + TypeConversionTest{}.test_fill(1, 1); } diff --git a/test/framework/utils.h b/test/framework/utils.h index 35e41e62e..83943d0d0 100644 --- a/test/framework/utils.h +++ b/test/framework/utils.h @@ -24,6 +24,14 @@ return impl; \ } +#define INTRINSICCV_DIFF_IO_API(name, impl, itype, otype) \ + template , bool> = true, \ + std::enable_if_t, bool> = true> \ + static decltype(auto) name() { \ + return impl; \ + } + // Generates a fatal failure with a generic message, and returns with a given // value. #define TEST_FAIL_WITH(return_value, message) \ -- GitLab