Merge tag 'v1.15.2' into merge-wigeon-2 Release v1.15.2 Wigeon Duck 2025-05-28 v1.15.2 "Wigeon Duck" This release fixes CVE-2025-5283 (bug webm:413411335), and is ABI compatible with the previous release. Change-Id: I4ac1e0e96602f26cb0e7883dcc87f3b232bc0554
diff --git a/README b/README index 9f17a63..e197561 100644 --- a/README +++ b/README
@@ -7,7 +7,7 @@ 1. Prerequisites - * All x86 targets require the Yasm[1] assembler be installed[2]. + * All x86 targets require the NASM[0] or Yasm[1] assembler be installed[2]. * All Windows builds require that Cygwin[3] or MSYS2[4] be installed. * Building the documentation requires Doxygen[5]. If you do not have this package, the install-docs option will be disabled. @@ -18,11 +18,15 @@ source[7]. These requirements are optional if not running the unit tests. + [0]: https://www.nasm.us/ [1]: http://www.tortall.net/projects/yasm [2]: For Visual Studio the base yasm binary (not vsyasm) should be in the PATH for Visual Studio. For VS2017 it is sufficient to rename yasm-<version>-<arch>.exe to yasm.exe and place it in: Program Files (x86)/Microsoft Visual Studio/2017/<level>/Common7/Tools/ + The MSYS2 version of the yasm binary can also be used and avoids an + issue caused by a missing Visual C++ Redistributable install (Visual + Studio 2010, MSVCR100.dll). [3]: http://www.cygwin.com [4]: http://www.msys2.org/ [5]: http://www.doxygen.org
diff --git a/args.c b/args.c index 0fb87d9..a1ab054 100644 --- a/args.c +++ b/args.c
@@ -24,7 +24,7 @@ extern void die(const char *fmt, ...); #endif -struct arg arg_init(char **argv) { +static struct arg arg_init(char **argv) { struct arg a; a.argv = argv;
diff --git a/args.h b/args.h index aae8ec0..acab48b 100644 --- a/args.h +++ b/args.h
@@ -45,12 +45,12 @@ #define ARG_DEF_LIST_END \ { 0 } -struct arg arg_init(char **argv); int arg_match(struct arg *arg_, const struct arg_def *def, char **argv); const char *arg_next(struct arg *arg); void arg_show_usage(FILE *fp, const struct arg_def *const *defs); char **argv_dup(int argc, const char **argv); +// Note: arg_match() must be called before invoking these functions. unsigned int arg_parse_uint(const struct arg *arg); int arg_parse_int(const struct arg *arg); struct vpx_rational arg_parse_rational(const struct arg *arg);
diff --git a/build/make/Makefile b/build/make/Makefile index 658b376..39dff82 100644 --- a/build/make/Makefile +++ b/build/make/Makefile
@@ -150,8 +150,8 @@ $(BUILD_PFX)%_neon_i8mm.c.o: CFLAGS += -march=armv8.2-a+dotprod+i8mm $(BUILD_PFX)%_sve.c.d: CFLAGS += -march=armv8.2-a+dotprod+i8mm+sve $(BUILD_PFX)%_sve.c.o: CFLAGS += -march=armv8.2-a+dotprod+i8mm+sve -$(BUILD_PFX)%_sve2.c.d: CFLAGS += -march=armv9-a+sve2 -$(BUILD_PFX)%_sve2.c.o: CFLAGS += -march=armv9-a+sve2 +$(BUILD_PFX)%_sve2.c.d: CFLAGS += -march=armv9-a+i8mm+sve2 +$(BUILD_PFX)%_sve2.c.o: CFLAGS += -march=armv9-a+i8mm+sve2 # POWER $(BUILD_PFX)%_vsx.c.d: CFLAGS += -maltivec -mvsx
diff --git a/build/make/configure.sh b/build/make/configure.sh index cc5bf6c..d1d89aa 100644 --- a/build/make/configure.sh +++ b/build/make/configure.sh
@@ -1229,7 +1229,8 @@ ;; esac - if [ "$(show_darwin_sdk_major_version iphoneos)" -gt 8 ]; then + if [ "$(show_darwin_sdk_major_version iphoneos)" -gt 8 \ + && [ "$(show_xcode_version)" -lt 16 ]; then check_add_cflags -fembed-bitcode check_add_asflags -fembed-bitcode check_add_ldflags -fembed-bitcode
diff --git a/build/make/rtcd.pl b/build/make/rtcd.pl index 9f6aad8..156199a 100755 --- a/build/make/rtcd.pl +++ b/build/make/rtcd.pl
@@ -455,19 +455,35 @@ common_bottom; } +# List of architectures in low-to-high preference order. +my @PRIORITY_ARCH = qw/ + c + mmx sse sse2 sse3 ssse3 sse4_1 sse4_2 avx avx2 avx512 + arm_crc32 neon neon_dotprod neon_i8mm sve sve2 + rvv + vsx + dspr2 msa +/; +my %PRIORITY_INDEX; +for (my $i = 0; $i < @PRIORITY_ARCH; $i++) { + $PRIORITY_INDEX{$PRIORITY_ARCH[$i]} = $i; +} + # # Main Driver # &require("c"); -&require(keys %required); +&require(sort { $PRIORITY_INDEX{$a} <=> $PRIORITY_INDEX{$b} } keys %required); if ($opts{arch} eq 'x86') { @ALL_ARCHS = filter(qw/mmx sse sse2 sse3 ssse3 sse4_1 avx avx2 avx512/); x86; } elsif ($opts{arch} eq 'x86_64') { @ALL_ARCHS = filter(qw/mmx sse sse2 sse3 ssse3 sse4_1 avx avx2 avx512/); - @REQUIRES = filter(qw/mmx sse sse2/); - &require(@REQUIRES); + if (keys %required == 0) { + @REQUIRES = filter(qw/mmx sse sse2/); + &require(@REQUIRES); + } x86; } elsif ($opts{arch} eq 'mips32' || $opts{arch} eq 'mips64') { my $have_dspr2 = 0; @@ -505,8 +521,10 @@ arm; } elsif ($opts{arch} eq 'armv8' || $opts{arch} eq 'arm64' ) { @ALL_ARCHS = filter(qw/neon neon_dotprod neon_i8mm sve sve2/); - @REQUIRES = filter(qw/neon/); - &require(@REQUIRES); + if (keys %required == 0) { + @REQUIRES = filter(qw/neon/); + &require(@REQUIRES); + } arm; } elsif ($opts{arch} =~ /^ppc/ ) { @ALL_ARCHS = filter(qw/vsx/);
diff --git a/configure b/configure index 457bd6b..777cb84 100755 --- a/configure +++ b/configure
@@ -308,7 +308,6 @@ fp_mb_stats emulate_hardware non_greedy_mv - rate_ctrl collect_component_timing " CONFIG_LIST="
diff --git a/examples.mk b/examples.mk index 48fcc29..a586799 100644 --- a/examples.mk +++ b/examples.mk
@@ -369,6 +369,13 @@ $(foreach proj,$(call enabled,PROJECTS),\ $(eval $(call vcproj_template,$(proj)))) +# Generate a list of all enabled sources, in particular for exporting to gyp +# based build systems. +vpxdec_srcs.txt: + @echo " [CREATE] $@" + @echo $(vpxdec.SRCS) | xargs -n1 echo | LC_ALL=C sort -u > $@ +CLEAN-OBJS += vpxdec_srcs.txt + # # Documentation Rules #
diff --git a/examples/vp8_multi_resolution_encoder.c b/examples/vp8_multi_resolution_encoder.c index 62d96de..60161da 100644 --- a/examples/vp8_multi_resolution_encoder.c +++ b/examples/vp8_multi_resolution_encoder.c
@@ -25,7 +25,6 @@ #include <stdio.h> #include <stdlib.h> #include <stdarg.h> -#include <string.h> #include <math.h> #include <assert.h> #include <sys/time.h> @@ -438,7 +437,7 @@ /* Other-resolution encoder settings */ for (i = 1; i < NUM_ENCODERS; i++) { - memcpy(&cfg[i], &cfg[0], sizeof(vpx_codec_enc_cfg_t)); + cfg[i] = cfg[0]; cfg[i].rc_target_bitrate = target_bitrate[i];
diff --git a/examples/vpx_temporal_svc_encoder.c b/examples/vpx_temporal_svc_encoder.c index a800278..01badbe 100644 --- a/examples/vpx_temporal_svc_encoder.c +++ b/examples/vpx_temporal_svc_encoder.c
@@ -607,19 +607,24 @@ } #if ROI_MAP -static void read_mask(FILE *mask_file, int *seg_map) { +static int read_mask(FILE *mask_file, int *seg_map, int allowed_mask_rows, + int allowed_mask_cols) { int mask_rows, mask_cols, i, j; int *map_start = seg_map; - fscanf(mask_file, "%d %d\n", &mask_cols, &mask_rows); + if (fscanf(mask_file, "%d %d\n", &mask_cols, &mask_rows) != 2) return 0; + if (mask_rows != allowed_mask_rows || mask_cols != allowed_mask_cols) { + return 0; + } for (i = 0; i < mask_rows; i++) { for (j = 0; j < mask_cols; j++) { - fscanf(mask_file, "%d ", &seg_map[j]); + if (fscanf(mask_file, "%d ", &seg_map[j]) != 1) return 0; // reverse the bit seg_map[j] = 1 - seg_map[j]; } seg_map += mask_cols; } seg_map = map_start; + return 1; } #endif @@ -965,12 +970,16 @@ argv[argc - 1], frame_cnt); mask_file = fopen(mask_file_name, "r"); if (mask_file != NULL) { - read_mask(mask_file, mask_map); + int mask_is_valid = read_mask(mask_file, mask_map, mask_rows, mask_cols); fclose(mask_file); - // set_roi_map(encoder->name, &cfg, &roi); - set_roi_skip_map(&cfg, &roi, mask_map, prev_mask_map, frame_cnt); - if (vpx_codec_control(&codec, VP9E_SET_ROI_MAP, &roi)) - die_codec(&codec, "Failed to set ROI map"); + if (mask_is_valid) { + // set_roi_map(encoder->name, &cfg, &roi); + set_roi_skip_map(&cfg, &roi, mask_map, prev_mask_map, frame_cnt); + if (vpx_codec_control(&codec, VP9E_SET_ROI_MAP, &roi)) + die_codec(&codec, "Failed to set ROI map"); + } else { + die_codec(&codec, "Mask input is invalid for ROI map"); + } } #endif frame_avail = read_frame(&input_ctx, &raw);
diff --git a/libs.mk b/libs.mk index 4a28b11..c7db0c1 100644 --- a/libs.mk +++ b/libs.mk
@@ -191,16 +191,6 @@ INSTALL-LIBS-$(CONFIG_DEBUG_LIBS) += $(LIBSUBDIR)/libvpx_g.a endif -ifeq ($(CONFIG_VP9_ENCODER)$(CONFIG_RATE_CTRL),yesyes) - SIMPLE_ENCODE_SRCS := $(call enabled,CODEC_SRCS) - SIMPLE_ENCODE_SRCS += $(VP9_PREFIX)simple_encode.cc - SIMPLE_ENCODE_SRCS += $(VP9_PREFIX)simple_encode.h - SIMPLE_ENCODE_SRCS += ivfenc.h - SIMPLE_ENCODE_SRCS += ivfenc.c - INSTALL-SRCS-$(CONFIG_CODEC_SRCS) += $(VP9_PREFIX)simple_encode.cc - INSTALL-SRCS-$(CONFIG_CODEC_SRCS) += $(VP9_PREFIX)simple_encode.h -endif - CODEC_SRCS=$(call enabled,CODEC_SRCS) INSTALL-SRCS-$(CONFIG_CODEC_SRCS) += $(CODEC_SRCS) @@ -214,6 +204,11 @@ @echo $(CODEC_SRCS) | xargs -n1 echo | LC_ALL=C sort -u > $@ CLEAN-OBJS += libvpx_srcs.txt +libvpxrc_srcs.txt: + @echo " [CREATE] $@" + @echo $(RC_RTC_SRCS) | xargs -n1 echo | LC_ALL=C sort -u > $@ +CLEAN-OBJS += libvpxrc_srcs.txt + # Assembly files that are included, but don't define symbols themselves. # Filtered out to avoid Windows build warnings. ASM_INCLUDES := \ @@ -419,13 +414,6 @@ $(BUILD_PFX)libvpxrc_g.a: $(RC_RTC_OBJS) endif -ifeq ($(CONFIG_VP9_ENCODER)$(CONFIG_RATE_CTRL),yesyes) - SIMPLE_ENCODE_OBJS=$(call objs,$(SIMPLE_ENCODE_SRCS)) - OBJS-yes += $(SIMPLE_ENCODE_OBJS) - LIBS-yes += $(BUILD_PFX)libsimple_encode.a $(BUILD_PFX)libsimple_encode_g.a - $(BUILD_PFX)libsimple_encode_g.a: $(SIMPLE_ENCODE_OBJS) -endif - endif # ifeq ($(CONFIG_EXTERNAL_BUILD),yes) libvpx.ver: $(call enabled,CODEC_EXPORTS) @@ -513,11 +501,6 @@ RC_INTERFACE_TEST_OBJS := $(sort $(call objs,$(RC_INTERFACE_TEST_SRCS))) endif -SIMPLE_ENCODE_TEST_BIN=./test_simple_encode$(EXE_SFX) -SIMPLE_ENCODE_TEST_SRCS=$(call addprefix_clean,test/,\ - $(call enabled,SIMPLE_ENCODE_TEST_SRCS)) -SIMPLE_ENCODE_TEST_OBJS := $(sort $(call objs,$(SIMPLE_ENCODE_TEST_SRCS))) - libvpx_test_srcs.txt: @echo " [CREATE] $@" @echo $(LIBVPX_TEST_SRCS) | xargs -n1 echo | LC_ALL=C sort -u > $@ @@ -688,18 +671,6 @@ endif # RC_INTERFACE_TEST endif # CONFIG_ENCODERS -ifneq ($(strip $(SIMPLE_ENCODE_TEST_OBJS)),) -$(SIMPLE_ENCODE_TEST_OBJS) $(SIMPLE_ENCODE_TEST_OBJS:.o=.d): \ - CXXFLAGS += $(GTEST_INCLUDES) -OBJS-yes += $(SIMPLE_ENCODE_TEST_OBJS) -BINS-yes += $(SIMPLE_ENCODE_TEST_BIN) - -$(SIMPLE_ENCODE_TEST_BIN): $(TEST_LIBS) libsimple_encode.a -$(eval $(call linkerxx_template,$(SIMPLE_ENCODE_TEST_BIN), \ - $(SIMPLE_ENCODE_TEST_OBJS) \ - -L. -lsimple_encode -lvpx -lgtest $(extralibs) -lm)) -endif # SIMPLE_ENCODE_TEST - endif # CONFIG_EXTERNAL_BUILD # Install test sources only if codec source is included
diff --git a/test/convolve_test.cc b/test/convolve_test.cc index d947179..541d8c4 100644 --- a/test/convolve_test.cc +++ b/test/convolve_test.cc
@@ -37,6 +37,13 @@ const InterpKernel *filter, int x0_q4, int x_step_q4, int y0_q4, int y_step_q4, int w, int h); +#if !CONFIG_REALTIME_ONLY && CONFIG_VP9_ENCODER +typedef void (*ConvolveFunc12Tap)(const uint8_t *src, ptrdiff_t src_stride, + uint8_t *dst, ptrdiff_t dst_stride, + const InterpKernel12 *filter, int x0_q4, + int x_step_q4, int y0_q4, int y_step_q4, + int w, int h); +#endif typedef void (*WrapperFilterBlock2d8Func)( const uint8_t *src_ptr, const unsigned int src_stride, @@ -80,6 +87,25 @@ typedef std::tuple<int, int, const ConvolveFunctions *> ConvolveParam; +#if !CONFIG_REALTIME_ONLY && CONFIG_VP9_ENCODER +struct ConvolveFunctions12Tap { + ConvolveFunctions12Tap(ConvolveFunc12Tap h12, ConvolveFunc12Tap v12, + ConvolveFunc12Tap hv12, int bd) + : use_highbd_(bd) { + h12_ = h12; + v12_ = v12; + hv12_ = hv12; + } + + ConvolveFunc12Tap h12_; + ConvolveFunc12Tap v12_; + ConvolveFunc12Tap hv12_; + int use_highbd_; // 0 if high bitdepth not used, else the actual bit depth. +}; + +typedef std::tuple<int, int, const ConvolveFunctions12Tap *> Convolve12TapParam; +#endif + #define ALL_SIZES(convolve_fn) \ make_tuple(4, 4, &convolve_fn), make_tuple(8, 4, &convolve_fn), \ make_tuple(4, 8, &convolve_fn), make_tuple(8, 8, &convolve_fn), \ @@ -88,7 +114,13 @@ make_tuple(16, 32, &convolve_fn), make_tuple(32, 32, &convolve_fn), \ make_tuple(64, 32, &convolve_fn), make_tuple(32, 64, &convolve_fn), \ make_tuple(64, 64, &convolve_fn) - +#if !CONFIG_REALTIME_ONLY && CONFIG_VP9_ENCODER +#define ALL_SIZES_12TAP(convolve_fn) \ + make_tuple(8, 8, &convolve_fn), make_tuple(16, 8, &convolve_fn), \ + make_tuple(8, 16, &convolve_fn), make_tuple(16, 16, &convolve_fn), \ + make_tuple(32, 16, &convolve_fn), make_tuple(16, 32, &convolve_fn), \ + make_tuple(32, 32, &convolve_fn) +#endif // Reference 8-tap subpixel filter, slightly modified to fit into this test. #define VP9_FILTER_WEIGHT 128 #define VP9_FILTER_SHIFT 7 @@ -549,6 +581,438 @@ uint16_t *ConvolveTest::output16_ = nullptr; uint16_t *ConvolveTest::output16_ref_ = nullptr; #endif +#if !CONFIG_REALTIME_ONLY && CONFIG_VP9_ENCODER +class ConvolveTest12Tap : public ::testing::TestWithParam<Convolve12TapParam> { + public: + static void SetUpTestSuite() { + // Force input_ to be unaligned, output to be 16 byte aligned. + input_ = reinterpret_cast<uint8_t *>( + vpx_memalign(kDataAlignment, kInputBufferSize + 1)) + + 1; + output_ = reinterpret_cast<uint8_t *>( + vpx_memalign(kDataAlignment, kOutputBufferSize)); +#if CONFIG_VP9_HIGHBITDEPTH + input16_ = reinterpret_cast<uint16_t *>(vpx_memalign( + kDataAlignment, (kInputBufferSize + 1) * sizeof(uint16_t))) + + 1; + output16_ = reinterpret_cast<uint16_t *>( + vpx_memalign(kDataAlignment, (kOutputBufferSize) * sizeof(uint16_t))); +#endif + } + + void TearDown() override { libvpx_test::ClearSystemState(); } + + static void TearDownTestSuite() { + vpx_free(input_ - 1); + input_ = nullptr; + vpx_free(output_); + output_ = nullptr; +#if CONFIG_VP9_HIGHBITDEPTH + vpx_free(input16_ - 1); + input16_ = nullptr; + vpx_free(output16_); + output16_ = nullptr; +#endif + } + + protected: + static const int kDataAlignment = 16; + static const int kOuterBlockSize = 256; + static const int kInputStride = kOuterBlockSize; + static const int kOutputStride = kOuterBlockSize; + static const int kInputBufferSize = kOuterBlockSize * kOuterBlockSize; + static const int kOutputBufferSize = kOuterBlockSize * kOuterBlockSize; + + int Width() const { return GET_PARAM(0); } + int Height() const { return GET_PARAM(1); } + int BorderLeft() const { + const int center = (kOuterBlockSize - Width()) / 2; + return (center + (kDataAlignment - 1)) & ~(kDataAlignment - 1); + } + int BorderTop() const { return (kOuterBlockSize - Height()) / 2; } + + bool IsIndexInBorder(int i) { + return (i < BorderTop() * kOuterBlockSize || + i >= (BorderTop() + Height()) * kOuterBlockSize || + i % kOuterBlockSize < BorderLeft() || + i % kOuterBlockSize >= (BorderLeft() + Width())); + } + + void SetUp() override { + UUT_ = GET_PARAM(2); +#if CONFIG_VP9_HIGHBITDEPTH + if (UUT_->use_highbd_ != 0) { + mask_ = (1 << UUT_->use_highbd_) - 1; + } else { + mask_ = 255; + } +#endif + /* Set up guard blocks for an inner block centered in the outer block */ + for (int i = 0; i < kOutputBufferSize; ++i) { + if (IsIndexInBorder(i)) { + output_[i] = 255; +#if CONFIG_VP9_HIGHBITDEPTH + output16_[i] = mask_; +#endif + } else { + output_[i] = 0; +#if CONFIG_VP9_HIGHBITDEPTH + output16_[i] = 0; +#endif + } + } + + ::libvpx_test::ACMRandom prng; + for (int i = 0; i < kInputBufferSize; ++i) { + if (i & 1) { + input_[i] = 255; +#if CONFIG_VP9_HIGHBITDEPTH + input16_[i] = mask_; +#endif + } else { + input_[i] = prng.Rand8Extremes(); +#if CONFIG_VP9_HIGHBITDEPTH + input16_[i] = prng.Rand16() & mask_; +#endif + } + } + } + + void SetConstantInput(int value) { + memset(input_, value, kInputBufferSize); +#if CONFIG_VP9_HIGHBITDEPTH + vpx_memset16(input16_, value, kInputBufferSize); +#endif + } + + void CheckGuardBlocks() { + for (int i = 0; i < kOutputBufferSize; ++i) { + if (IsIndexInBorder(i)) { + EXPECT_EQ(255, output_[i]); + } + } + } + + uint8_t *input() const { + const int offset = BorderTop() * kOuterBlockSize + BorderLeft(); +#if CONFIG_VP9_HIGHBITDEPTH + if (UUT_->use_highbd_ == 0) { + return input_ + offset; + } else { + return CAST_TO_BYTEPTR(input16_ + offset); + } +#else + return input_ + offset; +#endif + } + + uint8_t *output() const { + const int offset = BorderTop() * kOuterBlockSize + BorderLeft(); +#if CONFIG_VP9_HIGHBITDEPTH + if (UUT_->use_highbd_ == 0) { + return output_ + offset; + } else { + return CAST_TO_BYTEPTR(output16_ + offset); + } +#else + return output_ + offset; +#endif + } + + uint16_t lookup(uint8_t *list, int index) const { +#if CONFIG_VP9_HIGHBITDEPTH + if (UUT_->use_highbd_ == 0) { + return list[index]; + } else { + return CAST_TO_SHORTPTR(list)[index]; + } +#else + return list[index]; +#endif + } + + void assign_val(uint8_t *list, int index, uint16_t val) const { +#if CONFIG_VP9_HIGHBITDEPTH + if (UUT_->use_highbd_ == 0) { + list[index] = (uint8_t)val; + } else { + CAST_TO_SHORTPTR(list)[index] = val; + } +#else + list[index] = (uint8_t)val; +#endif + } + const ConvolveFunctions12Tap *UUT_; + static uint8_t *input_; + static uint8_t *output_; +#if CONFIG_VP9_HIGHBITDEPTH + static uint16_t *input16_; + static uint16_t *output16_; + int mask_; +#endif +}; + +uint8_t *ConvolveTest12Tap::input_ = nullptr; +uint8_t *ConvolveTest12Tap::output_ = nullptr; +#if CONFIG_VP9_HIGHBITDEPTH +uint16_t *ConvolveTest12Tap::input16_ = nullptr; +uint16_t *ConvolveTest12Tap::output16_ = nullptr; +#endif + +TEST_P(ConvolveTest12Tap, MatchesReferenceSubpixelFilter) { + uint8_t *const in = input(); + uint8_t *const out = output(); +#if CONFIG_VP9_HIGHBITDEPTH + uint8_t ref8[kOutputStride * kMaxDimension]; + uint16_t ref16[kOutputStride * kMaxDimension]; + uint8_t *ref; + if (UUT_->use_highbd_ == 0) { + ref = ref8; + } else { + ref = CAST_TO_BYTEPTR(ref16); + } +#else + uint8_t ref[kOutputStride * kMaxDimension]; +#endif + + // Populate ref and out with some random data + ::libvpx_test::ACMRandom prng; + for (int y = 0; y < Height(); ++y) { + for (int x = 0; x < Width(); ++x) { + uint16_t r; +#if CONFIG_VP9_HIGHBITDEPTH + if (UUT_->use_highbd_ == 0 || UUT_->use_highbd_ == 8) { + r = prng.Rand8Extremes(); + } else { + r = prng.Rand16() & mask_; + } +#else + r = prng.Rand8Extremes(); +#endif + + assign_val(out, y * kOutputStride + x, r); + assign_val(ref, y * kOutputStride + x, r); + } + } + + const InterpKernel12 *filters = sub_pel_filters_12; + for (int filter_x = 0; filter_x < 16; ++filter_x) { + for (int filter_y = 0; filter_y < 16; ++filter_y) { +#if CONFIG_VP9_HIGHBITDEPTH + if (UUT_->use_highbd_ == 0) { + vpx_convolve12_c(in, kInputStride, ref, kOutputStride, filters, + filter_x, 16, filter_y, 16, Width(), Height()); + } else { + vpx_highbd_convolve12_c(CAST_TO_SHORTPTR(in), kInputStride, + CAST_TO_SHORTPTR(ref), kOutputStride, filters, + filter_x, 16, filter_y, 16, Width(), Height(), + UUT_->use_highbd_); + } +#else + vpx_convolve12_c(in, kInputStride, ref, kOutputStride, filters, filter_x, + 16, filter_y, 16, Width(), Height()); +#endif + if (filter_x && filter_y) + ASM_REGISTER_STATE_CHECK( + UUT_->hv12_(in, kInputStride, out, kOutputStride, filters, filter_x, + 16, filter_y, 16, Width(), Height())); + else if (filter_y) + ASM_REGISTER_STATE_CHECK(UUT_->v12_(in, kInputStride, out, + kOutputStride, filters, 0, 16, + filter_y, 16, Width(), Height())); + else if (filter_x) + ASM_REGISTER_STATE_CHECK(UUT_->h12_(in, kInputStride, out, + kOutputStride, filters, filter_x, + 16, 0, 16, Width(), Height())); + else + continue; + + CheckGuardBlocks(); + + for (int y = 0; y < Height(); ++y) { + for (int x = 0; x < Width(); ++x) + ASSERT_EQ(lookup(ref, y * kOutputStride + x), + lookup(out, y * kOutputStride + x)) + << "mismatch at (" << x << "," << y << "), " + << "filters (" + << "," << filter_x << "," << filter_y << ")"; + } + } + } +} + +TEST_P(ConvolveTest12Tap, FilterExtremes) { + uint8_t *const in = input(); + uint8_t *const out = output(); +#if CONFIG_VP9_HIGHBITDEPTH + uint8_t ref8[kOutputStride * kMaxDimension]; + uint16_t ref16[kOutputStride * kMaxDimension]; + uint8_t *ref; + if (UUT_->use_highbd_ == 0) { + ref = ref8; + } else { + ref = CAST_TO_BYTEPTR(ref16); + } +#else + uint8_t ref[kOutputStride * kMaxDimension]; +#endif + + // Populate ref and out with some random data + ::libvpx_test::ACMRandom prng; + for (int y = 0; y < Height(); ++y) { + for (int x = 0; x < Width(); ++x) { + uint16_t r; +#if CONFIG_VP9_HIGHBITDEPTH + if (UUT_->use_highbd_ == 0 || UUT_->use_highbd_ == 8) { + r = prng.Rand8Extremes(); + } else { + r = prng.Rand16() & mask_; + } +#else + r = prng.Rand8Extremes(); +#endif + assign_val(out, y * kOutputStride + x, r); + assign_val(ref, y * kOutputStride + x, r); + } + } + + for (int axis = 0; axis < 2; axis++) { + int seed_val = 0; + while (seed_val < 256) { + for (int y = 0; y < 8; ++y) { + for (int x = 0; x < 8; ++x) { +#if CONFIG_VP9_HIGHBITDEPTH + assign_val(in, y * kOutputStride + x - MAX_FILTER_TAP / 2 + 1, + ((seed_val >> (axis ? y : x)) & 1) * mask_); +#else + assign_val(in, y * kOutputStride + x - MAX_FILTER_TAP / 2 + 1, + ((seed_val >> (axis ? y : x)) & 1) * 255); +#endif + if (axis) seed_val++; + } + if (axis) { + seed_val -= 8; + } else { + seed_val++; + } + } + if (axis) seed_val += 8; + + const InterpKernel12 *filters = sub_pel_filters_12; + for (int filter_x = 0; filter_x < 16; ++filter_x) { + for (int filter_y = 0; filter_y < 16; ++filter_y) { +#if CONFIG_VP9_HIGHBITDEPTH + if (UUT_->use_highbd_ == 0) { + vpx_convolve12_c(in, kInputStride, ref, kOutputStride, filters, + filter_x, 16, filter_y, 16, Width(), Height()); + } else { + vpx_highbd_convolve12_c(CAST_TO_SHORTPTR(in), kInputStride, + CAST_TO_SHORTPTR(ref), kOutputStride, + filters, filter_x, 16, filter_y, 16, + Width(), Height(), UUT_->use_highbd_); + } +#else + vpx_convolve12_c(in, kInputStride, ref, kOutputStride, filters, + filter_x, 16, filter_y, 16, Width(), Height()); +#endif + if (filter_x && filter_y) + ASM_REGISTER_STATE_CHECK( + UUT_->hv12_(in, kInputStride, out, kOutputStride, filters, + filter_x, 16, filter_y, 16, Width(), Height())); + else if (filter_y) + ASM_REGISTER_STATE_CHECK( + UUT_->v12_(in, kInputStride, out, kOutputStride, filters, 0, 16, + filter_y, 16, Width(), Height())); + else if (filter_x) + ASM_REGISTER_STATE_CHECK( + UUT_->h12_(in, kInputStride, out, kOutputStride, filters, + filter_x, 16, 0, 16, Width(), Height())); + else + continue; + + for (int y = 0; y < Height(); ++y) { + for (int x = 0; x < Width(); ++x) + ASSERT_EQ(lookup(ref, y * kOutputStride + x), + lookup(out, y * kOutputStride + x)) + << "mismatch at (" << x << "," << y << "), " + << "filters (" + << "," << filter_x << "," << filter_y << ")"; + } + } + } + } + } +} + +TEST_P(ConvolveTest12Tap, DISABLED_12Tap_Speed) { + const uint8_t *const in = input(); + uint8_t *const out = output(); + const InterpKernel12 *const twelvetap = sub_pel_filters_12; + const int kNumTests = 5000000; + const int width = Width(); + const int height = Height(); + vpx_usec_timer timer; + + SetConstantInput(127); + + vpx_usec_timer_start(&timer); + for (int n = 0; n < kNumTests; ++n) { + UUT_->hv12_(in, kInputStride, out, kOutputStride, twelvetap, 8, 16, 8, 16, + width, height); + } + vpx_usec_timer_mark(&timer); + + const int elapsed_time = static_cast<int>(vpx_usec_timer_elapsed(&timer)); + printf("convolve12_%dx%d_%d: %d us\n", width, height, + UUT_->use_highbd_ ? UUT_->use_highbd_ : 8, elapsed_time); +} + +TEST_P(ConvolveTest12Tap, DISABLED_12Tap_Horiz_Speed) { + const uint8_t *const in = input(); + uint8_t *const out = output(); + const InterpKernel12 *const twelvetap = sub_pel_filters_12; + const int kNumTests = 5000000; + const int width = Width(); + const int height = Height(); + vpx_usec_timer timer; + + SetConstantInput(127); + + vpx_usec_timer_start(&timer); + for (int n = 0; n < kNumTests; ++n) { + UUT_->h12_(in, kInputStride, out, kOutputStride, twelvetap, 8, 16, 8, 16, + width, height); + } + vpx_usec_timer_mark(&timer); + + const int elapsed_time = static_cast<int>(vpx_usec_timer_elapsed(&timer)); + printf("convolve12_horiz_%dx%d_%d: %d us\n", width, height, + UUT_->use_highbd_ ? UUT_->use_highbd_ : 8, elapsed_time); +} + +TEST_P(ConvolveTest12Tap, DISABLED_12Tap_Vert_Speed) { + const uint8_t *const in = input(); + uint8_t *const out = output(); + const InterpKernel12 *const twelvetap = sub_pel_filters_12; + const int kNumTests = 5000000; + const int width = Width(); + const int height = Height(); + vpx_usec_timer timer; + + SetConstantInput(127); + + vpx_usec_timer_start(&timer); + for (int n = 0; n < kNumTests; ++n) { + UUT_->v12_(in, kInputStride, out, kOutputStride, twelvetap, 8, 16, 8, 16, + width, height); + } + vpx_usec_timer_mark(&timer); + + const int elapsed_time = static_cast<int>(vpx_usec_timer_elapsed(&timer)); + printf("convolve12_vert_%dx%d_%d: %d us\n", width, height, + UUT_->use_highbd_ ? UUT_->use_highbd_ : 8, elapsed_time); +} +#endif TEST_P(ConvolveTest, GuardBlocks) { CheckGuardBlocks(); } @@ -1304,6 +1768,79 @@ #endif INSTANTIATE_TEST_SUITE_P(C, ConvolveTest, ::testing::ValuesIn(kArrayConvolve_c)); +#if !CONFIG_REALTIME_ONLY && CONFIG_VP9_ENCODER +#if CONFIG_VP9_HIGHBITDEPTH +#define WRAP12TAP(func, bd) \ + void wrap_##func##_##bd( \ + const uint8_t *src, ptrdiff_t src_stride, uint8_t *dst, \ + ptrdiff_t dst_stride, const InterpKernel12 *filter, int x0_q4, \ + int x_step_q4, int y0_q4, int y_step_q4, int w, int h) { \ + vpx_highbd_##func(reinterpret_cast<const uint16_t *>(src), src_stride, \ + reinterpret_cast<uint16_t *>(dst), dst_stride, filter, \ + x0_q4, x_step_q4, y0_q4, y_step_q4, w, h, bd); \ + } + +#if HAVE_AVX2 +WRAP12TAP(convolve12_horiz_avx2, 8) +WRAP12TAP(convolve12_vert_avx2, 8) +WRAP12TAP(convolve12_avx2, 8) +WRAP12TAP(convolve12_horiz_avx2, 10) +WRAP12TAP(convolve12_vert_avx2, 10) +WRAP12TAP(convolve12_avx2, 10) +WRAP12TAP(convolve12_horiz_avx2, 12) +WRAP12TAP(convolve12_vert_avx2, 12) +WRAP12TAP(convolve12_avx2, 12) +#endif // HAVE_AVX2 + +#if HAVE_SSSE3 +WRAP12TAP(convolve12_horiz_ssse3, 8) +WRAP12TAP(convolve12_vert_ssse3, 8) +WRAP12TAP(convolve12_ssse3, 8) +WRAP12TAP(convolve12_horiz_ssse3, 10) +WRAP12TAP(convolve12_vert_ssse3, 10) +WRAP12TAP(convolve12_ssse3, 10) +WRAP12TAP(convolve12_horiz_ssse3, 12) +WRAP12TAP(convolve12_vert_ssse3, 12) +WRAP12TAP(convolve12_ssse3, 12) +#endif // HAVE_SSSE3 + +WRAP12TAP(convolve12_horiz_c, 8) +WRAP12TAP(convolve12_vert_c, 8) +WRAP12TAP(convolve12_c, 8) +WRAP12TAP(convolve12_horiz_c, 10) +WRAP12TAP(convolve12_vert_c, 10) +WRAP12TAP(convolve12_c, 10) +WRAP12TAP(convolve12_horiz_c, 12) +WRAP12TAP(convolve12_vert_c, 12) +WRAP12TAP(convolve12_c, 12) +#undef WRAP12TAP + +const ConvolveFunctions12Tap convolve12tap_8bit_c(wrap_convolve12_horiz_c_8, + wrap_convolve12_vert_c_8, + wrap_convolve12_c_8, 8); + +const ConvolveFunctions12Tap convolve12tap_10bit_c(wrap_convolve12_horiz_c_10, + wrap_convolve12_vert_c_10, + wrap_convolve12_c_10, 10); + +const ConvolveFunctions12Tap convolve12tap_12bit_c(wrap_convolve12_horiz_c_12, + wrap_convolve12_vert_c_12, + wrap_convolve12_c_12, 12); + +const Convolve12TapParam kArrayConvolve12Tap_c[] = { + ALL_SIZES_12TAP(convolve12tap_8bit_c), ALL_SIZES_12TAP(convolve12tap_10bit_c), + ALL_SIZES_12TAP(convolve12tap_12bit_c) +}; +#else +const ConvolveFunctions12Tap convolve12Tap_c(vpx_convolve12_horiz_c, + vpx_convolve12_vert_c, + vpx_convolve12_c, 0); +const Convolve12TapParam kArrayConvolve12Tap_c[] = { ALL_SIZES_12TAP( + convolve12Tap_c) }; +#endif +INSTANTIATE_TEST_SUITE_P(C, ConvolveTest12Tap, + ::testing::ValuesIn(kArrayConvolve12Tap_c)); +#endif #if HAVE_SSE2 && VPX_ARCH_X86_64 #if CONFIG_VP9_HIGHBITDEPTH @@ -1359,6 +1896,37 @@ const ConvolveParam kArrayConvolve8_ssse3[] = { ALL_SIZES(convolve8_ssse3) }; INSTANTIATE_TEST_SUITE_P(SSSE3, ConvolveTest, ::testing::ValuesIn(kArrayConvolve8_ssse3)); + +#if !CONFIG_REALTIME_ONLY && CONFIG_VP9_ENCODER +#if CONFIG_VP9_HIGHBITDEPTH +const ConvolveFunctions12Tap convolve12tap_8bit_ssse3( + wrap_convolve12_horiz_ssse3_8, wrap_convolve12_vert_ssse3_8, + wrap_convolve12_ssse3_8, 8); + +const ConvolveFunctions12Tap convolve12tap_10bit_ssse3( + wrap_convolve12_horiz_ssse3_10, wrap_convolve12_vert_ssse3_10, + wrap_convolve12_ssse3_10, 10); + +const ConvolveFunctions12Tap convolve12tap_12bit_ssse3( + wrap_convolve12_horiz_ssse3_12, wrap_convolve12_vert_ssse3_12, + wrap_convolve12_ssse3_12, 12); + +const Convolve12TapParam kArrayConvolve12Tap_ssse3[] = { + ALL_SIZES_12TAP(convolve12tap_8bit_ssse3), + ALL_SIZES_12TAP(convolve12tap_10bit_ssse3), + ALL_SIZES_12TAP(convolve12tap_12bit_ssse3) +}; +#else +const ConvolveFunctions12Tap convolve12_ssse3(vpx_convolve12_horiz_ssse3, + vpx_convolve12_vert_ssse3, + vpx_convolve12_ssse3, 0); +const Convolve12TapParam kArrayConvolve12Tap_ssse3[] = { ALL_SIZES_12TAP( + convolve12_ssse3) }; +#endif // CONFIG_VP9_HIGHBITDEPTH + +INSTANTIATE_TEST_SUITE_P(SSSE3, ConvolveTest12Tap, + ::testing::ValuesIn(kArrayConvolve12Tap_ssse3)); +#endif // !CONFIG_REALTIME_ONLY && CONFIG_VP9_ENCODER #endif #if HAVE_AVX2 @@ -1402,6 +1970,36 @@ INSTANTIATE_TEST_SUITE_P(AVX2, ConvolveTest, ::testing::ValuesIn(kArrayConvolve8_avx2)); #endif // CONFIG_VP9_HIGHBITDEPTH + +#if !CONFIG_REALTIME_ONLY && CONFIG_VP9_ENCODER +#if CONFIG_VP9_HIGHBITDEPTH +const ConvolveFunctions12Tap convolve12Tap_8bit_avx2( + wrap_convolve12_horiz_avx2_8, wrap_convolve12_vert_avx2_8, + wrap_convolve12_avx2_8, 8); + +const ConvolveFunctions12Tap convolve12Tap_10bit_avx2( + wrap_convolve12_horiz_avx2_10, wrap_convolve12_vert_avx2_10, + wrap_convolve12_avx2_10, 10); + +const ConvolveFunctions12Tap convolve12Tap_12bit_avx2( + wrap_convolve12_horiz_avx2_12, wrap_convolve12_vert_avx2_12, + wrap_convolve12_avx2_12, 12); + +const Convolve12TapParam kArrayConvolve12Tap_avx2[] = { + ALL_SIZES_12TAP(convolve12Tap_8bit_avx2), + ALL_SIZES_12TAP(convolve12Tap_10bit_avx2), + ALL_SIZES_12TAP(convolve12Tap_12bit_avx2) +}; +#else +const ConvolveFunctions12Tap convolve12Tap_avx2(vpx_convolve12_horiz_avx2, + vpx_convolve12_vert_avx2, + vpx_convolve12_avx2, 0); +const Convolve12TapParam kArrayConvolve12Tap_avx2[] = { ALL_SIZES_12TAP( + convolve12Tap_avx2) }; +#endif +INSTANTIATE_TEST_SUITE_P(AVX2, ConvolveTest12Tap, + ::testing::ValuesIn(kArrayConvolve12Tap_avx2)); +#endif #endif // HAVE_AVX2 #if HAVE_NEON @@ -1445,6 +2043,16 @@ #endif // CONFIG_VP9_HIGHBITDEPTH INSTANTIATE_TEST_SUITE_P(NEON, ConvolveTest, ::testing::ValuesIn(kArrayConvolve_neon)); + +#if !CONFIG_REALTIME_ONLY && CONFIG_VP9_ENCODER +const ConvolveFunctions12Tap convolve12Tap_neon(vpx_convolve12_horiz_neon, + vpx_convolve12_vert_neon, + vpx_convolve12_neon, 0); +const Convolve12TapParam kArrayConvolve12Tap_neon[] = { ALL_SIZES_12TAP( + convolve12Tap_neon) }; +INSTANTIATE_TEST_SUITE_P(NEON, ConvolveTest12Tap, + ::testing::ValuesIn(kArrayConvolve12Tap_neon)); +#endif #endif // HAVE_NEON #if HAVE_NEON_DOTPROD @@ -1460,6 +2068,16 @@ convolve8_neon_dotprod) }; INSTANTIATE_TEST_SUITE_P(NEON_DOTPROD, ConvolveTest, ::testing::ValuesIn(kArrayConvolve_neon_dotprod)); + +#if !CONFIG_REALTIME_ONLY && CONFIG_VP9_ENCODER +const ConvolveFunctions12Tap convolve12Tap_neon_dotprod( + vpx_convolve12_horiz_neon_dotprod, vpx_convolve12_vert_neon_dotprod, + vpx_convolve12_neon_dotprod, 0); +const Convolve12TapParam kArrayConvolve12Tap_neon_dotprod[] = { ALL_SIZES_12TAP( + convolve12Tap_neon_dotprod) }; +INSTANTIATE_TEST_SUITE_P(NEON_DOTPROD, ConvolveTest12Tap, + ::testing::ValuesIn(kArrayConvolve12Tap_neon_dotprod)); +#endif #endif // HAVE_NEON_DOTPROD #if HAVE_SVE @@ -1543,6 +2161,16 @@ convolve8_neon_i8mm) }; INSTANTIATE_TEST_SUITE_P(NEON_I8MM, ConvolveTest, ::testing::ValuesIn(kArrayConvolve_neon_i8mm)); + +#if !CONFIG_REALTIME_ONLY && CONFIG_VP9_ENCODER +const ConvolveFunctions12Tap convolve12Tap_neon_i8mm( + vpx_convolve12_horiz_neon_i8mm, vpx_convolve12_vert_neon_i8mm, + vpx_convolve12_neon_i8mm, 0); +const Convolve12TapParam kArrayConvolve12Tap_neon_i8mm[] = { ALL_SIZES_12TAP( + convolve12Tap_neon_i8mm) }; +INSTANTIATE_TEST_SUITE_P(NEON_I8MM, ConvolveTest12Tap, + ::testing::ValuesIn(kArrayConvolve12Tap_neon_i8mm)); +#endif #endif // HAVE_NEON_I8MM #if HAVE_DSPR2
diff --git a/test/decode_corrupted.cc b/test/decode_corrupted.cc index 8e51fb3..55919d0 100644 --- a/test/decode_corrupted.cc +++ b/test/decode_corrupted.cc
@@ -59,7 +59,7 @@ if (pkt->data.frame.flags & VPX_FRAME_IS_KEY) return pkt; if (pkt->kind != VPX_CODEC_CX_FRAME_PKT) return pkt; - memcpy(&modified_pkt_, pkt, sizeof(*pkt)); + modified_pkt_ = *pkt; // Halve the size so it's corrupted to decoder. modified_pkt_.data.frame.sz = modified_pkt_.data.frame.sz / 2;
diff --git a/test/decode_test_driver.cc b/test/decode_test_driver.cc index 794e367..af87bc2 100644 --- a/test/decode_test_driver.cc +++ b/test/decode_test_driver.cc
@@ -111,7 +111,7 @@ } void DecoderTest::set_cfg(const vpx_codec_dec_cfg_t &dec_cfg) { - memcpy(&cfg_, &dec_cfg, sizeof(cfg_)); + cfg_ = dec_cfg; } void DecoderTest::set_flags(const vpx_codec_flags_t flags) { flags_ = flags; }
diff --git a/test/encode_api_test.cc b/test/encode_api_test.cc index 05d31e6..a58552a 100644 --- a/test/encode_api_test.cc +++ b/test/encode_api_test.cc
@@ -700,7 +700,7 @@ cfg[0].g_timebase.num = 1; /* Set fps */ cfg[0].g_timebase.den = framerate; - memcpy(&cfg[1], &cfg[0], sizeof(cfg[0])); + cfg[1] = cfg[0]; cfg[1].rc_target_bitrate = 500; cfg[1].g_w = width_down; cfg[1].g_h = height_down;
diff --git a/test/fdct8x8_test.cc b/test/fdct8x8_test.cc index d8778f9..d02a47c 100644 --- a/test/fdct8x8_test.cc +++ b/test/fdct8x8_test.cc
@@ -133,13 +133,16 @@ #endif // HAVE_SSE2 #endif // CONFIG_VP9_HIGHBITDEPTH -// Visual Studio 2022 (cl.exe) targeting AArch64 with optimizations enabled -// produces invalid code in RunExtremalCheck() and RunInvAccuracyCheck(). -// See: +// Visual Studio 2022 (cl.exe) < 17.12.3 targeting AArch64 with optimizations +// enabled produces invalid code in RunExtremalCheck() and +// RunInvAccuracyCheck(). See: // https://developercommunity.visualstudio.com/t/1770-preview-1:-Misoptimization-for-AR/10369786 -// TODO(jzern): check the compiler version after a fix for the issue is -// released. -#if defined(_MSC_VER) && defined(_M_ARM64) && !defined(__clang__) +#if defined(_MSC_FULL_VER) && _MSC_FULL_VER < 194234435 && \ + defined(_M_ARM64) && !defined(__clang__) +#define AOM_WORK_AROUND_MSVC_BUG_10369786 +#endif + +#ifdef AOM_WORK_AROUND_MSVC_BUG_10369786 #pragma optimize("", off) #endif class FwdTrans8x8TestBase { @@ -533,7 +536,7 @@ vpx_bit_depth_t bit_depth_; int mask_; }; -#if defined(_MSC_VER) && defined(_M_ARM64) && !defined(__clang__) +#ifdef AOM_WORK_AROUND_MSVC_BUG_10369786 #pragma optimize("", on) #endif
diff --git a/test/quantize_test.cc b/test/quantize_test.cc index 2ef0408..cd02e0f 100644 --- a/test/quantize_test.cc +++ b/test/quantize_test.cc
@@ -71,7 +71,7 @@ // Copy macroblockd from the reference to get pre-set-up dequant values. macroblockd_dst_ = reinterpret_cast<MACROBLOCKD *>( vpx_memalign(32, sizeof(*macroblockd_dst_))); - memcpy(macroblockd_dst_, &vp8_comp_->mb.e_mbd, sizeof(*macroblockd_dst_)); + *macroblockd_dst_ = vp8_comp_->mb.e_mbd; // Fix block pointers - currently they point to the blocks in the reference // structure. vp8_setup_block_dptrs(macroblockd_dst_); @@ -80,7 +80,7 @@ void UpdateQuantizer(int q) { vp8_set_quantizer(vp8_comp_, q); - memcpy(macroblockd_dst_, &vp8_comp_->mb.e_mbd, sizeof(*macroblockd_dst_)); + *macroblockd_dst_ = vp8_comp_->mb.e_mbd; vp8_setup_block_dptrs(macroblockd_dst_); }
diff --git a/test/resize_test.cc b/test/resize_test.cc index 93e1bf6..1a83194 100644 --- a/test/resize_test.cc +++ b/test/resize_test.cc
@@ -93,11 +93,20 @@ void ScaleForFrameNumber(unsigned int frame, unsigned int initial_w, unsigned int initial_h, unsigned int *w, unsigned int *h, bool flag_codec, - bool smaller_width_larger_size_) { + bool smaller_width_larger_size, + bool random_input_one_half_only) { *w = initial_w; *h = initial_h; - if (smaller_width_larger_size_) { + if (random_input_one_half_only == 1) { + if (frame < 100) { + return; + } + *w = initial_w / 2; + *h = initial_h / 2; + return; + } + if (smaller_width_larger_size) { if (frame < 30) { return; } @@ -236,13 +245,19 @@ class ResizingVideoSource : public ::libvpx_test::DummyVideoSource { public: - ResizingVideoSource() { - SetSize(kInitialWidth, kInitialHeight); + ResizingVideoSource(int width, int height) + : smaller_width_larger_size_(false), random_input_one_half_only_(false), + configured_width_(width), configured_height_(height) { limit_ = 350; - smaller_width_larger_size_ = false; + SetSize(configured_width_, configured_height_); } bool flag_codec_; bool smaller_width_larger_size_; + bool random_input_one_half_only_; + // configured_width_/height_ is the configured resolution when codec is + // created. + int configured_width_; + int configured_height_; ~ResizingVideoSource() override = default; protected: @@ -250,11 +265,25 @@ ++frame_; unsigned int width = 0; unsigned int height = 0; - ScaleForFrameNumber(frame_, kInitialWidth, kInitialHeight, &width, &height, - flag_codec_, smaller_width_larger_size_); + ScaleForFrameNumber(frame_, configured_width_, configured_height_, &width, + &height, flag_codec_, smaller_width_larger_size_, + random_input_one_half_only_); SetSize(width, height); FillFrame(); } + + void FillFrame() override { + if (img_) { + memset(img_->img_data, 0, raw_sz_); + if (random_input_one_half_only_) { + libvpx_test::ACMRandom rnd(libvpx_test::ACMRandom::DeterministicSeed()); + unsigned char *image = img_->planes[0]; + for (size_t i = 0; i < raw_sz_; ++i) { + image[i] = rnd.Rand8(); + } + } + } + } }; class ResizeTest @@ -296,26 +325,26 @@ }; TEST_P(ResizeTest, TestExternalResizeWorks) { - ResizingVideoSource video; + ResizingVideoSource video(kInitialWidth, kInitialHeight); video.flag_codec_ = false; video.smaller_width_larger_size_ = false; cfg_.g_lag_in_frames = 0; ASSERT_NO_FATAL_FAILURE(RunLoop(&video)); - for (std::vector<FrameInfo>::const_iterator info = frame_info_list_.begin(); - info != frame_info_list_.end(); ++info) { - const unsigned int frame = static_cast<unsigned>(info->pts); + for (const auto &info : frame_info_list_) { + const unsigned int frame = static_cast<unsigned>(info.pts); unsigned int expected_w; unsigned int expected_h; - const size_t idx = info - frame_info_list_.begin(); - ASSERT_EQ(info->w, GetFrameWidth(idx)); - ASSERT_EQ(info->h, GetFrameHeight(idx)); + const size_t idx = &info - &frame_info_list_[0]; + ASSERT_EQ(info.w, GetFrameWidth(idx)); + ASSERT_EQ(info.h, GetFrameHeight(idx)); ScaleForFrameNumber(frame, kInitialWidth, kInitialHeight, &expected_w, &expected_h, video.flag_codec_, - video.smaller_width_larger_size_); - EXPECT_EQ(expected_w, info->w) + video.smaller_width_larger_size_, + /*random_input_one_half_only=*/false); + EXPECT_EQ(expected_w, info.w) << "Frame " << frame << " had unexpected width"; - EXPECT_EQ(expected_h, info->h) + EXPECT_EQ(expected_h, info.h) << "Frame " << frame << " had unexpected height"; } } @@ -419,15 +448,14 @@ cfg_.g_lag_in_frames = 0; ASSERT_NO_FATAL_FAILURE(RunLoop(&video)); - for (std::vector<FrameInfo>::const_iterator info = frame_info_list_.begin(); - info != frame_info_list_.end(); ++info) { - const vpx_codec_pts_t pts = info->pts; + for (const auto &info : frame_info_list_) { + const vpx_codec_pts_t pts = info.pts; if (pts >= kStepDownFrame && pts < kStepUpFrame) { - ASSERT_EQ(282U, info->w) << "Frame " << pts << " had unexpected width"; - ASSERT_EQ(173U, info->h) << "Frame " << pts << " had unexpected height"; + ASSERT_EQ(282U, info.w) << "Frame " << pts << " had unexpected width"; + ASSERT_EQ(173U, info.h) << "Frame " << pts << " had unexpected height"; } else { - EXPECT_EQ(352U, info->w) << "Frame " << pts << " had unexpected width"; - EXPECT_EQ(288U, info->h) << "Frame " << pts << " had unexpected height"; + EXPECT_EQ(352U, info.w) << "Frame " << pts << " had unexpected width"; + EXPECT_EQ(288U, info.h) << "Frame " << pts << " had unexpected height"; } } } @@ -453,6 +481,10 @@ if (video->frame() == 0) { encoder->Control(VP9E_SET_AQ_MODE, 3); encoder->Control(VP8E_SET_CPUUSED, set_cpu_used_); + if (cfg_.g_threads > 0) { + encoder->Control(VP9E_SET_ROW_MT, 1); + encoder->Control(VP9E_SET_TILE_COLUMNS, cfg_.g_threads >> 1); + } } if (change_bitrate_ && video->frame() == 120) { @@ -528,7 +560,7 @@ }; TEST_P(ResizeRealtimeTest, TestExternalResizeWorks) { - ResizingVideoSource video; + ResizingVideoSource video(kInitialWidth, kInitialHeight); video.flag_codec_ = true; video.smaller_width_larger_size_ = false; DefaultConfig(); @@ -539,24 +571,57 @@ mismatch_nframes_ = 0; ASSERT_NO_FATAL_FAILURE(RunLoop(&video)); - for (std::vector<FrameInfo>::const_iterator info = frame_info_list_.begin(); - info != frame_info_list_.end(); ++info) { - const unsigned int frame = static_cast<unsigned>(info->pts); + for (const auto &info : frame_info_list_) { + const unsigned int frame = static_cast<unsigned>(info.pts); unsigned int expected_w; unsigned int expected_h; ScaleForFrameNumber(frame, kInitialWidth, kInitialHeight, &expected_w, &expected_h, video.flag_codec_, - video.smaller_width_larger_size_); - EXPECT_EQ(expected_w, info->w) + video.smaller_width_larger_size_, + /*random_input_one_half_only=*/false); + EXPECT_EQ(expected_w, info.w) << "Frame " << frame << " had unexpected width"; - EXPECT_EQ(expected_h, info->h) + EXPECT_EQ(expected_h, info.h) << "Frame " << frame << " had unexpected height"; - EXPECT_EQ(static_cast<unsigned int>(0), GetMismatchFrames()); + EXPECT_EQ(GetMismatchFrames(), static_cast<unsigned int>(0)); + } +} + +// This test uses 4 threads with small keyframe spacing, random input, +// and uses 640x480 as initial resolution. +TEST_P(ResizeRealtimeTest, TestExternalResizeWorks4Threads) { + ResizingVideoSource video(640, 480); + video.flag_codec_ = true; + video.smaller_width_larger_size_ = false; + video.random_input_one_half_only_ = true; + DefaultConfig(); + // Disable internal resize for this test. + cfg_.rc_resize_allowed = 0; + cfg_.g_threads = 4; + cfg_.kf_max_dist = 50; + cfg_.kf_min_dist = 50; + change_bitrate_ = false; + mismatch_psnr_ = 0.0; + mismatch_nframes_ = 0; + ASSERT_NO_FATAL_FAILURE(RunLoop(&video)); + + for (const auto &info : frame_info_list_) { + const unsigned int frame = static_cast<unsigned>(info.pts); + unsigned int expected_w; + unsigned int expected_h; + ScaleForFrameNumber(frame, 640, 480, &expected_w, &expected_h, + video.flag_codec_, video.smaller_width_larger_size_, + video.random_input_one_half_only_); + EXPECT_EQ(expected_w, info.w) + << "Frame " << frame << " had unexpected width"; + EXPECT_EQ(expected_h, info.h) + << "Frame " << frame << " had unexpected height"; + EXPECT_EQ(GetMismatchFrames(), static_cast<unsigned int>(0)); } } TEST_P(ResizeRealtimeTest, TestExternalResizeSmallerWidthBiggerSize) { - ResizingVideoSource video; + ResizingVideoSource video(kInitialWidth, kInitialHeight); video.flag_codec_ = true; video.smaller_width_larger_size_ = true; DefaultConfig(); @@ -567,19 +632,19 @@ mismatch_nframes_ = 0; ASSERT_NO_FATAL_FAILURE(RunLoop(&video)); - for (std::vector<FrameInfo>::const_iterator info = frame_info_list_.begin(); - info != frame_info_list_.end(); ++info) { - const unsigned int frame = static_cast<unsigned>(info->pts); + for (const auto &info : frame_info_list_) { + const unsigned int frame = static_cast<unsigned>(info.pts); unsigned int expected_w; unsigned int expected_h; ScaleForFrameNumber(frame, kInitialWidth, kInitialHeight, &expected_w, &expected_h, video.flag_codec_, - video.smaller_width_larger_size_); - EXPECT_EQ(expected_w, info->w) + video.smaller_width_larger_size_, + /*random_input_one_half_only=*/false); + EXPECT_EQ(expected_w, info.w) << "Frame " << frame << " had unexpected width"; - EXPECT_EQ(expected_h, info->h) + EXPECT_EQ(expected_h, info.h) << "Frame " << frame << " had unexpected height"; - EXPECT_EQ(static_cast<unsigned int>(0), GetMismatchFrames()); + EXPECT_EQ(GetMismatchFrames(), static_cast<unsigned int>(0)); } } @@ -601,21 +666,20 @@ unsigned int last_w = cfg_.g_w; unsigned int last_h = cfg_.g_h; int resize_count = 0; - for (std::vector<FrameInfo>::const_iterator info = frame_info_list_.begin(); - info != frame_info_list_.end(); ++info) { - if (info->w != last_w || info->h != last_h) { + for (const auto &info : frame_info_list_) { + if (info.w != last_w || info.h != last_h) { // Verify that resize down occurs. - ASSERT_LT(info->w, last_w); - ASSERT_LT(info->h, last_h); - last_w = info->w; - last_h = info->h; + ASSERT_LT(info.w, last_w); + ASSERT_LT(info.h, last_h); + last_w = info.w; + last_h = info.h; resize_count++; } } // Verify that we get 1 resize down event in this test. ASSERT_EQ(1, resize_count) << "Resizing should occur."; - EXPECT_EQ(static_cast<unsigned int>(0), GetMismatchFrames()); + EXPECT_EQ(GetMismatchFrames(), static_cast<unsigned int>(0)); #else GTEST_SKIP() << "Warning: VP9 decoder unavailable, unable to check resize count!\n"; @@ -643,31 +707,30 @@ unsigned int last_w = cfg_.g_w; unsigned int last_h = cfg_.g_h; int resize_count = 0; - for (std::vector<FrameInfo>::const_iterator info = frame_info_list_.begin(); - info != frame_info_list_.end(); ++info) { - const size_t idx = info - frame_info_list_.begin(); - ASSERT_EQ(info->w, GetFrameWidth(idx)); - ASSERT_EQ(info->h, GetFrameHeight(idx)); - if (info->w != last_w || info->h != last_h) { + for (const auto &info : frame_info_list_) { + const size_t idx = &info - &frame_info_list_[0]; + ASSERT_EQ(info.w, GetFrameWidth(idx)); + ASSERT_EQ(info.h, GetFrameHeight(idx)); + if (info.w != last_w || info.h != last_h) { resize_count++; if (resize_count <= 2) { // Verify that resize down occurs. - ASSERT_LT(info->w, last_w); - ASSERT_LT(info->h, last_h); + ASSERT_LT(info.w, last_w); + ASSERT_LT(info.h, last_h); } else if (resize_count > 2) { // Verify that resize up occurs. - ASSERT_GT(info->w, last_w); - ASSERT_GT(info->h, last_h); + ASSERT_GT(info.w, last_w); + ASSERT_GT(info.h, last_h); } - last_w = info->w; - last_h = info->h; + last_w = info.w; + last_h = info.h; } } #if CONFIG_VP9_DECODER // Verify that we get 4 resize events in this test. ASSERT_EQ(resize_count, 4) << "Resizing should occur twice."; - EXPECT_EQ(static_cast<unsigned int>(0), GetMismatchFrames()); + EXPECT_EQ(GetMismatchFrames(), static_cast<unsigned int>(0)); #else GTEST_SKIP() << "Warning: VP9 decoder unavailable, unable to check resize count!\n";
diff --git a/test/sad_test.cc b/test/sad_test.cc index 73beb19..19227cc 100644 --- a/test/sad_test.cc +++ b/test/sad_test.cc
@@ -1888,11 +1888,38 @@ #endif // HAVE_AVX2 #if HAVE_AVX512 +const SadMxNParam avx512_tests[] = { + SadMxNParam(64, 64, &vpx_sad64x64_avx512), + SadMxNParam(64, 32, &vpx_sad64x32_avx512), +}; +INSTANTIATE_TEST_SUITE_P(AVX512, SADTest, ::testing::ValuesIn(avx512_tests)); + +const SadSkipMxNParam skip_avx512_tests[] = { + SadSkipMxNParam(64, 64, &vpx_sad_skip_64x64_avx512), + SadSkipMxNParam(64, 32, &vpx_sad_skip_64x32_avx512), +}; +INSTANTIATE_TEST_SUITE_P(AVX512, SADSkipTest, + ::testing::ValuesIn(skip_avx512_tests)); + +const SadMxNAvgParam avg_avx512_tests[] = { + SadMxNAvgParam(64, 64, &vpx_sad64x64_avg_avx512), + SadMxNAvgParam(64, 32, &vpx_sad64x32_avg_avx512), +}; +INSTANTIATE_TEST_SUITE_P(AVX512, SADavgTest, + ::testing::ValuesIn(avg_avx512_tests)); + const SadMxNx4Param x4d_avx512_tests[] = { SadMxNx4Param(64, 64, &vpx_sad64x64x4d_avx512), }; INSTANTIATE_TEST_SUITE_P(AVX512, SADx4Test, ::testing::ValuesIn(x4d_avx512_tests)); + +const SadSkipMxNx4Param skip_x4d_avx512_tests[] = { + SadSkipMxNx4Param(64, 64, &vpx_sad_skip_64x64x4d_avx512), + SadSkipMxNx4Param(64, 32, &vpx_sad_skip_64x32x4d_avx512), +}; +INSTANTIATE_TEST_SUITE_P(AVX512, SADSkipx4Test, + ::testing::ValuesIn(skip_x4d_avx512_tests)); #endif // HAVE_AVX512 //------------------------------------------------------------------------------
diff --git a/test/simple_encode_test.cc b/test/simple_encode_test.cc deleted file mode 100644 index 0f8671c..0000000 --- a/test/simple_encode_test.cc +++ /dev/null
@@ -1,574 +0,0 @@ -/* - * Copyright (c) 2019 The WebM project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#include <math.h> -#include <memory> -#include <string> -#include <vector> -#include "gtest/gtest.h" -#include "test/video_source.h" -#include "vp9/simple_encode.h" - -namespace vp9 { -namespace { - -double GetBitrateInKbps(size_t bit_size, int num_frames, int frame_rate_num, - int frame_rate_den) { - return static_cast<double>(bit_size) / num_frames * frame_rate_num / - frame_rate_den / 1000.0; -} - -// Returns the number of unit in size of 4. -// For example, if size is 7, return 2. -int GetNumUnit4x4(int size) { return (size + 3) >> 2; } - -class SimpleEncodeTest : public ::testing::Test { - protected: - const int width_ = 352; - const int height_ = 288; - const int frame_rate_num_ = 30; - const int frame_rate_den_ = 1; - const int target_bitrate_ = 1000; - const int num_frames_ = 17; - const int target_level_ = LEVEL_UNKNOWN; - const std::string in_file_path_str_ = - libvpx_test::GetDataPath() + "/bus_352x288_420_f20_b8.yuv"; -}; - -TEST_F(SimpleEncodeTest, ComputeFirstPassStats) { - SimpleEncode simple_encode(width_, height_, frame_rate_num_, frame_rate_den_, - target_bitrate_, num_frames_, target_level_, - in_file_path_str_.c_str()); - simple_encode.ComputeFirstPassStats(); - std::vector<std::vector<double>> frame_stats = - simple_encode.ObserveFirstPassStats(); - EXPECT_EQ(frame_stats.size(), static_cast<size_t>(num_frames_)); - const size_t data_num = frame_stats[0].size(); - // Read ObserveFirstPassStats before changing FIRSTPASS_STATS. - EXPECT_EQ(data_num, static_cast<size_t>(25)); - for (size_t i = 0; i < frame_stats.size(); ++i) { - EXPECT_EQ(frame_stats[i].size(), data_num); - // FIRSTPASS_STATS's first element is frame - EXPECT_EQ(frame_stats[i][0], i); - // FIRSTPASS_STATS's last element is count, and the count is 1 for single - // frame stats - EXPECT_EQ(frame_stats[i][data_num - 1], 1); - } -} - -TEST_F(SimpleEncodeTest, ObserveFirstPassMotionVectors) { - SimpleEncode simple_encode(width_, height_, frame_rate_num_, frame_rate_den_, - target_bitrate_, num_frames_, target_level_, - in_file_path_str_.c_str()); - simple_encode.ComputeFirstPassStats(); - std::vector<std::vector<MotionVectorInfo>> fps_motion_vectors = - simple_encode.ObserveFirstPassMotionVectors(); - EXPECT_EQ(fps_motion_vectors.size(), static_cast<size_t>(num_frames_)); - const size_t num_blocks = ((width_ + 15) >> 4) * ((height_ + 15) >> 4); - EXPECT_EQ(num_blocks, fps_motion_vectors[0].size()); - for (size_t i = 0; i < fps_motion_vectors.size(); ++i) { - EXPECT_EQ(num_blocks, fps_motion_vectors[i].size()); - for (size_t j = 0; j < num_blocks; ++j) { - const int mv_count = fps_motion_vectors[i][j].mv_count; - const int ref_count = - (fps_motion_vectors[i][j].ref_frame[0] != kRefFrameTypeNone) + - (fps_motion_vectors[i][j].ref_frame[1] != kRefFrameTypeNone); - EXPECT_EQ(mv_count, ref_count); - } - } -} - -TEST_F(SimpleEncodeTest, GetCodingFrameNum) { - SimpleEncode simple_encode(width_, height_, frame_rate_num_, frame_rate_den_, - target_bitrate_, num_frames_, target_level_, - in_file_path_str_.c_str()); - simple_encode.ComputeFirstPassStats(); - const int num_coding_frames = simple_encode.GetCodingFrameNum(); - EXPECT_EQ(num_coding_frames, 19); -} - -TEST_F(SimpleEncodeTest, EncodeFrame) { - SimpleEncode simple_encode(width_, height_, frame_rate_num_, frame_rate_den_, - target_bitrate_, num_frames_, target_level_, - in_file_path_str_.c_str()); - simple_encode.ComputeFirstPassStats(); - int num_coding_frames = simple_encode.GetCodingFrameNum(); - EXPECT_GE(num_coding_frames, num_frames_); - simple_encode.StartEncode(); - size_t total_data_bit_size = 0; - int coded_show_frame_count = 0; - int frame_coding_index = 0; - while (coded_show_frame_count < num_frames_) { - const GroupOfPicture group_of_picture = - simple_encode.ObserveGroupOfPicture(); - const std::vector<EncodeFrameInfo> &encode_frame_list = - group_of_picture.encode_frame_list; - for (size_t group_index = 0; group_index < encode_frame_list.size(); - ++group_index) { - EncodeFrameResult encode_frame_result; - simple_encode.EncodeFrame(&encode_frame_result); - EXPECT_EQ(encode_frame_result.show_idx, - encode_frame_list[group_index].show_idx); - EXPECT_EQ(encode_frame_result.frame_type, - encode_frame_list[group_index].frame_type); - EXPECT_EQ(encode_frame_list[group_index].coding_index, - frame_coding_index); - EXPECT_GE(encode_frame_result.psnr, 34) - << "The psnr is supposed to be greater than 34 given the " - "target_bitrate 1000 kbps"; - EXPECT_EQ(encode_frame_result.ref_frame_info, - encode_frame_list[group_index].ref_frame_info); - total_data_bit_size += encode_frame_result.coding_data_bit_size; - ++frame_coding_index; - } - coded_show_frame_count += group_of_picture.show_frame_count; - } - const double bitrate = GetBitrateInKbps(total_data_bit_size, num_frames_, - frame_rate_num_, frame_rate_den_); - const double off_target_threshold = 150; - EXPECT_LE(fabs(target_bitrate_ - bitrate), off_target_threshold); - simple_encode.EndEncode(); -} - -TEST_F(SimpleEncodeTest, ObserveKeyFrameMap) { - SimpleEncode simple_encode(width_, height_, frame_rate_num_, frame_rate_den_, - target_bitrate_, num_frames_, target_level_, - in_file_path_str_.c_str()); - simple_encode.ComputeFirstPassStats(); - std::vector<int> key_frame_map = simple_encode.ObserveKeyFrameMap(); - EXPECT_EQ(key_frame_map.size(), static_cast<size_t>(num_frames_)); - simple_encode.StartEncode(); - int coded_show_frame_count = 0; - while (coded_show_frame_count < num_frames_) { - const GroupOfPicture group_of_picture = - simple_encode.ObserveGroupOfPicture(); - const std::vector<EncodeFrameInfo> &encode_frame_list = - group_of_picture.encode_frame_list; - for (size_t group_index = 0; group_index < encode_frame_list.size(); - ++group_index) { - EncodeFrameResult encode_frame_result; - simple_encode.EncodeFrame(&encode_frame_result); - if (encode_frame_result.frame_type == kFrameTypeKey) { - EXPECT_EQ(key_frame_map[encode_frame_result.show_idx], 1); - } else { - EXPECT_EQ(key_frame_map[encode_frame_result.show_idx], 0); - } - } - coded_show_frame_count += group_of_picture.show_frame_count; - } - simple_encode.EndEncode(); -} - -TEST_F(SimpleEncodeTest, EncodeFrameWithTargetFrameBits) { - SimpleEncode simple_encode(width_, height_, frame_rate_num_, frame_rate_den_, - target_bitrate_, num_frames_, target_level_, - in_file_path_str_.c_str()); - simple_encode.ComputeFirstPassStats(); - const int num_coding_frames = simple_encode.GetCodingFrameNum(); - simple_encode.StartEncode(); - for (int i = 0; i < num_coding_frames; ++i) { - EncodeFrameInfo encode_frame_info = simple_encode.GetNextEncodeFrameInfo(); - int target_frame_bits; - switch (encode_frame_info.frame_type) { - case kFrameTypeInter: target_frame_bits = 20000; break; - case kFrameTypeKey: - case kFrameTypeAltRef: - case kFrameTypeGolden: target_frame_bits = 100000; break; - case kFrameTypeOverlay: target_frame_bits = 2000; break; - default: target_frame_bits = 20000; - } - - double percent_diff = 15; - if (encode_frame_info.frame_type == kFrameTypeOverlay) { - percent_diff = 100; - } - EncodeFrameResult encode_frame_result; - simple_encode.EncodeFrameWithTargetFrameBits( - &encode_frame_result, target_frame_bits, percent_diff); - const int recode_count = encode_frame_result.recode_count; - // TODO(angiebird): Replace 7 by RATE_CTRL_MAX_RECODE_NUM - EXPECT_LE(recode_count, 7); - EXPECT_GE(recode_count, 1); - - const double diff = fabs((double)encode_frame_result.coding_data_bit_size - - target_frame_bits); - EXPECT_LE(diff * 100 / target_frame_bits, percent_diff); - } - simple_encode.EndEncode(); -} - -TEST_F(SimpleEncodeTest, EncodeFrameWithQuantizeIndex) { - SimpleEncode simple_encode(width_, height_, frame_rate_num_, frame_rate_den_, - target_bitrate_, num_frames_, target_level_, - in_file_path_str_.c_str()); - simple_encode.ComputeFirstPassStats(); - const int num_coding_frames = simple_encode.GetCodingFrameNum(); - simple_encode.StartEncode(); - for (int i = 0; i < num_coding_frames; ++i) { - const int assigned_quantize_index = 100 + i; - EncodeFrameResult encode_frame_result; - simple_encode.EncodeFrameWithQuantizeIndex(&encode_frame_result, - assigned_quantize_index); - EXPECT_EQ(encode_frame_result.quantize_index, assigned_quantize_index); - } - simple_encode.EndEncode(); -} - -// This test encodes the video using EncodeFrame(), where quantize indexes -// are selected by vp9 rate control. -// Encode stats and the quantize_indexes are collected. -// Then the test encodes the video again using EncodeFrameWithQuantizeIndex() -// using the quantize indexes collected from the first run. -// Then test whether the encode stats of the two encoding runs match. -TEST_F(SimpleEncodeTest, EncodeConsistencyTest) { - std::vector<int> quantize_index_list; - std::vector<uint64_t> ref_sse_list; - std::vector<double> ref_psnr_list; - std::vector<size_t> ref_bit_size_list; - std::vector<FrameType> ref_frame_type_list; - std::vector<int> ref_show_idx_list; - { - // The first encode. - SimpleEncode simple_encode(width_, height_, frame_rate_num_, - frame_rate_den_, target_bitrate_, num_frames_, - target_level_, in_file_path_str_.c_str()); - simple_encode.ComputeFirstPassStats(); - const int num_coding_frames = simple_encode.GetCodingFrameNum(); - simple_encode.StartEncode(); - for (int i = 0; i < num_coding_frames; ++i) { - EncodeFrameResult encode_frame_result; - simple_encode.EncodeFrame(&encode_frame_result); - quantize_index_list.push_back(encode_frame_result.quantize_index); - ref_sse_list.push_back(encode_frame_result.sse); - ref_psnr_list.push_back(encode_frame_result.psnr); - ref_bit_size_list.push_back(encode_frame_result.coding_data_bit_size); - ref_frame_type_list.push_back(encode_frame_result.frame_type); - ref_show_idx_list.push_back(encode_frame_result.show_idx); - } - simple_encode.EndEncode(); - } - { - // The second encode with quantize index got from the first encode. - SimpleEncode simple_encode(width_, height_, frame_rate_num_, - frame_rate_den_, target_bitrate_, num_frames_, - target_level_, in_file_path_str_.c_str()); - simple_encode.ComputeFirstPassStats(); - const int num_coding_frames = simple_encode.GetCodingFrameNum(); - EXPECT_EQ(static_cast<size_t>(num_coding_frames), - quantize_index_list.size()); - simple_encode.StartEncode(); - for (int i = 0; i < num_coding_frames; ++i) { - EncodeFrameResult encode_frame_result; - simple_encode.EncodeFrameWithQuantizeIndex(&encode_frame_result, - quantize_index_list[i]); - EXPECT_EQ(encode_frame_result.quantize_index, quantize_index_list[i]); - EXPECT_EQ(encode_frame_result.sse, ref_sse_list[i]); - EXPECT_DOUBLE_EQ(encode_frame_result.psnr, ref_psnr_list[i]); - EXPECT_EQ(encode_frame_result.coding_data_bit_size, ref_bit_size_list[i]); - EXPECT_EQ(encode_frame_result.frame_type, ref_frame_type_list[i]); - EXPECT_EQ(encode_frame_result.show_idx, ref_show_idx_list[i]); - } - simple_encode.EndEncode(); - } -} - -// Test the information (partition info and motion vector info) stored in -// encoder is the same between two encode runs. -TEST_F(SimpleEncodeTest, EncodeConsistencyTest2) { - const int num_rows_4x4 = GetNumUnit4x4(width_); - const int num_cols_4x4 = GetNumUnit4x4(height_); - const int num_units_4x4 = num_rows_4x4 * num_cols_4x4; - // The first encode. - SimpleEncode simple_encode(width_, height_, frame_rate_num_, frame_rate_den_, - target_bitrate_, num_frames_, target_level_, - in_file_path_str_.c_str()); - simple_encode.ComputeFirstPassStats(); - const int num_coding_frames = simple_encode.GetCodingFrameNum(); - std::vector<PartitionInfo> partition_info_list(num_units_4x4 * - num_coding_frames); - std::vector<MotionVectorInfo> motion_vector_info_list(num_units_4x4 * - num_coding_frames); - simple_encode.StartEncode(); - for (int i = 0; i < num_coding_frames; ++i) { - EncodeFrameResult encode_frame_result; - simple_encode.EncodeFrame(&encode_frame_result); - for (int j = 0; j < num_rows_4x4 * num_cols_4x4; ++j) { - partition_info_list[i * num_units_4x4 + j] = - encode_frame_result.partition_info[j]; - motion_vector_info_list[i * num_units_4x4 + j] = - encode_frame_result.motion_vector_info[j]; - } - } - simple_encode.EndEncode(); - // The second encode. - SimpleEncode simple_encode_2(width_, height_, frame_rate_num_, - frame_rate_den_, target_bitrate_, num_frames_, - target_level_, in_file_path_str_.c_str()); - simple_encode_2.ComputeFirstPassStats(); - const int num_coding_frames_2 = simple_encode_2.GetCodingFrameNum(); - simple_encode_2.StartEncode(); - for (int i = 0; i < num_coding_frames_2; ++i) { - EncodeFrameResult encode_frame_result; - simple_encode_2.EncodeFrame(&encode_frame_result); - for (int j = 0; j < num_rows_4x4 * num_cols_4x4; ++j) { - EXPECT_EQ(encode_frame_result.partition_info[j].row, - partition_info_list[i * num_units_4x4 + j].row); - EXPECT_EQ(encode_frame_result.partition_info[j].column, - partition_info_list[i * num_units_4x4 + j].column); - EXPECT_EQ(encode_frame_result.partition_info[j].row_start, - partition_info_list[i * num_units_4x4 + j].row_start); - EXPECT_EQ(encode_frame_result.partition_info[j].column_start, - partition_info_list[i * num_units_4x4 + j].column_start); - EXPECT_EQ(encode_frame_result.partition_info[j].width, - partition_info_list[i * num_units_4x4 + j].width); - EXPECT_EQ(encode_frame_result.partition_info[j].height, - partition_info_list[i * num_units_4x4 + j].height); - - EXPECT_EQ(encode_frame_result.motion_vector_info[j].mv_count, - motion_vector_info_list[i * num_units_4x4 + j].mv_count); - EXPECT_EQ(encode_frame_result.motion_vector_info[j].ref_frame[0], - motion_vector_info_list[i * num_units_4x4 + j].ref_frame[0]); - EXPECT_EQ(encode_frame_result.motion_vector_info[j].ref_frame[1], - motion_vector_info_list[i * num_units_4x4 + j].ref_frame[1]); - EXPECT_EQ(encode_frame_result.motion_vector_info[j].mv_row[0], - motion_vector_info_list[i * num_units_4x4 + j].mv_row[0]); - EXPECT_EQ(encode_frame_result.motion_vector_info[j].mv_column[0], - motion_vector_info_list[i * num_units_4x4 + j].mv_column[0]); - EXPECT_EQ(encode_frame_result.motion_vector_info[j].mv_row[1], - motion_vector_info_list[i * num_units_4x4 + j].mv_row[1]); - EXPECT_EQ(encode_frame_result.motion_vector_info[j].mv_column[1], - motion_vector_info_list[i * num_units_4x4 + j].mv_column[1]); - } - } - simple_encode_2.EndEncode(); -} - -// Test the information stored in encoder is the same between two encode runs. -TEST_F(SimpleEncodeTest, EncodeConsistencyTest3) { - std::vector<int> quantize_index_list; - const int num_rows_4x4 = GetNumUnit4x4(width_); - const int num_cols_4x4 = GetNumUnit4x4(height_); - const int num_units_4x4 = num_rows_4x4 * num_cols_4x4; - // The first encode. - SimpleEncode simple_encode(width_, height_, frame_rate_num_, frame_rate_den_, - target_bitrate_, num_frames_, target_level_, - in_file_path_str_.c_str()); - simple_encode.ComputeFirstPassStats(); - const int num_coding_frames = simple_encode.GetCodingFrameNum(); - std::vector<PartitionInfo> partition_info_list(num_units_4x4 * - num_coding_frames); - simple_encode.StartEncode(); - for (int i = 0; i < num_coding_frames; ++i) { - EncodeFrameResult encode_frame_result; - simple_encode.EncodeFrame(&encode_frame_result); - quantize_index_list.push_back(encode_frame_result.quantize_index); - for (int j = 0; j < num_rows_4x4 * num_cols_4x4; ++j) { - partition_info_list[i * num_units_4x4 + j] = - encode_frame_result.partition_info[j]; - } - } - simple_encode.EndEncode(); - // The second encode. - SimpleEncode simple_encode_2(width_, height_, frame_rate_num_, - frame_rate_den_, target_bitrate_, num_frames_, - target_level_, in_file_path_str_.c_str()); - simple_encode_2.ComputeFirstPassStats(); - const int num_coding_frames_2 = simple_encode_2.GetCodingFrameNum(); - simple_encode_2.StartEncode(); - for (int i = 0; i < num_coding_frames_2; ++i) { - EncodeFrameResult encode_frame_result; - simple_encode_2.EncodeFrameWithQuantizeIndex(&encode_frame_result, - quantize_index_list[i]); - for (int j = 0; j < num_rows_4x4 * num_cols_4x4; ++j) { - EXPECT_EQ(encode_frame_result.partition_info[j].row, - partition_info_list[i * num_units_4x4 + j].row); - EXPECT_EQ(encode_frame_result.partition_info[j].column, - partition_info_list[i * num_units_4x4 + j].column); - EXPECT_EQ(encode_frame_result.partition_info[j].row_start, - partition_info_list[i * num_units_4x4 + j].row_start); - EXPECT_EQ(encode_frame_result.partition_info[j].column_start, - partition_info_list[i * num_units_4x4 + j].column_start); - EXPECT_EQ(encode_frame_result.partition_info[j].width, - partition_info_list[i * num_units_4x4 + j].width); - EXPECT_EQ(encode_frame_result.partition_info[j].height, - partition_info_list[i * num_units_4x4 + j].height); - } - } - simple_encode_2.EndEncode(); -} - -// Encode with default VP9 decision first. -// Get QPs and arf locations from the first encode. -// Set external arfs and QPs for the second encode. -// Expect to get matched results. -TEST_F(SimpleEncodeTest, EncodeConsistencySetExternalGroupOfPicturesMap) { - std::vector<int> quantize_index_list; - std::vector<uint64_t> ref_sse_list; - std::vector<double> ref_psnr_list; - std::vector<size_t> ref_bit_size_list; - std::vector<int> gop_map(num_frames_, 0); - { - // The first encode. - SimpleEncode simple_encode(width_, height_, frame_rate_num_, - frame_rate_den_, target_bitrate_, num_frames_, - target_level_, in_file_path_str_.c_str()); - simple_encode.ComputeFirstPassStats(); - simple_encode.StartEncode(); - - int coded_show_frame_count = 0; - while (coded_show_frame_count < num_frames_) { - const GroupOfPicture group_of_picture = - simple_encode.ObserveGroupOfPicture(); - gop_map[coded_show_frame_count] |= kGopMapFlagStart; - if (group_of_picture.use_alt_ref) { - gop_map[coded_show_frame_count] |= kGopMapFlagUseAltRef; - } - const std::vector<EncodeFrameInfo> &encode_frame_list = - group_of_picture.encode_frame_list; - for (size_t group_index = 0; group_index < encode_frame_list.size(); - ++group_index) { - EncodeFrameResult encode_frame_result; - simple_encode.EncodeFrame(&encode_frame_result); - quantize_index_list.push_back(encode_frame_result.quantize_index); - ref_sse_list.push_back(encode_frame_result.sse); - ref_psnr_list.push_back(encode_frame_result.psnr); - ref_bit_size_list.push_back(encode_frame_result.coding_data_bit_size); - } - coded_show_frame_count += group_of_picture.show_frame_count; - } - simple_encode.EndEncode(); - } - { - // The second encode with quantize index got from the first encode. - // The external arfs are the same as the first encode. - SimpleEncode simple_encode(width_, height_, frame_rate_num_, - frame_rate_den_, target_bitrate_, num_frames_, - target_level_, in_file_path_str_.c_str()); - simple_encode.ComputeFirstPassStats(); - simple_encode.SetExternalGroupOfPicturesMap(gop_map.data(), gop_map.size()); - const int num_coding_frames = simple_encode.GetCodingFrameNum(); - EXPECT_EQ(static_cast<size_t>(num_coding_frames), - quantize_index_list.size()); - simple_encode.StartEncode(); - for (int i = 0; i < num_coding_frames; ++i) { - EncodeFrameResult encode_frame_result; - simple_encode.EncodeFrameWithQuantizeIndex(&encode_frame_result, - quantize_index_list[i]); - EXPECT_EQ(encode_frame_result.quantize_index, quantize_index_list[i]); - EXPECT_EQ(encode_frame_result.sse, ref_sse_list[i]); - EXPECT_DOUBLE_EQ(encode_frame_result.psnr, ref_psnr_list[i]); - EXPECT_EQ(encode_frame_result.coding_data_bit_size, ref_bit_size_list[i]); - } - simple_encode.EndEncode(); - } -} - -TEST_F(SimpleEncodeTest, SetExternalGroupOfPicturesMap) { - SimpleEncode simple_encode(width_, height_, frame_rate_num_, frame_rate_den_, - target_bitrate_, num_frames_, target_level_, - in_file_path_str_.c_str()); - simple_encode.ComputeFirstPassStats(); - - std::vector<int> gop_map(num_frames_, 0); - - // Should be the first gop group. - gop_map[0] = 0; - - // Second gop group with an alt ref. - gop_map[5] |= kGopMapFlagStart | kGopMapFlagUseAltRef; - - // Third gop group without an alt ref. - gop_map[10] |= kGopMapFlagStart; - - // Last gop group. - gop_map[14] |= kGopMapFlagStart | kGopMapFlagUseAltRef; - - simple_encode.SetExternalGroupOfPicturesMap(gop_map.data(), gop_map.size()); - - std::vector<int> observed_gop_map = - simple_encode.ObserveExternalGroupOfPicturesMap(); - - // First gop group. - // There is always a key frame at show_idx 0 and key frame should always be - // the start of a gop. We expect ObserveExternalGroupOfPicturesMap() will - // insert an extra gop start here. - EXPECT_EQ(observed_gop_map[0], kGopMapFlagStart | kGopMapFlagUseAltRef); - - // Second gop group with an alt ref. - EXPECT_EQ(observed_gop_map[5], kGopMapFlagStart | kGopMapFlagUseAltRef); - - // Third gop group without an alt ref. - EXPECT_EQ(observed_gop_map[10], kGopMapFlagStart); - - // Last gop group. The last gop is not supposed to use an alt ref. We expect - // ObserveExternalGroupOfPicturesMap() will remove the alt ref flag here. - EXPECT_EQ(observed_gop_map[14], kGopMapFlagStart); - - int ref_gop_show_frame_count_list[4] = { 5, 5, 4, 3 }; - size_t ref_gop_coded_frame_count_list[4] = { 6, 6, 4, 3 }; - int gop_count = 0; - - simple_encode.StartEncode(); - int coded_show_frame_count = 0; - while (coded_show_frame_count < num_frames_) { - const GroupOfPicture group_of_picture = - simple_encode.ObserveGroupOfPicture(); - const std::vector<EncodeFrameInfo> &encode_frame_list = - group_of_picture.encode_frame_list; - EXPECT_EQ(encode_frame_list.size(), - ref_gop_coded_frame_count_list[gop_count]); - EXPECT_EQ(group_of_picture.show_frame_count, - ref_gop_show_frame_count_list[gop_count]); - for (size_t group_index = 0; group_index < encode_frame_list.size(); - ++group_index) { - EncodeFrameResult encode_frame_result; - simple_encode.EncodeFrame(&encode_frame_result); - } - coded_show_frame_count += group_of_picture.show_frame_count; - ++gop_count; - } - EXPECT_EQ(gop_count, 4); - simple_encode.EndEncode(); -} - -TEST_F(SimpleEncodeTest, GetEncodeFrameInfo) { - // Makes sure that the encode_frame_info obtained from GetEncodeFrameInfo() - // matches the counterpart in encode_frame_result obtained from EncodeFrame() - SimpleEncode simple_encode(width_, height_, frame_rate_num_, frame_rate_den_, - target_bitrate_, num_frames_, target_level_, - in_file_path_str_.c_str()); - simple_encode.ComputeFirstPassStats(); - const int num_coding_frames = simple_encode.GetCodingFrameNum(); - simple_encode.StartEncode(); - for (int i = 0; i < num_coding_frames; ++i) { - EncodeFrameInfo encode_frame_info = simple_encode.GetNextEncodeFrameInfo(); - EncodeFrameResult encode_frame_result; - simple_encode.EncodeFrame(&encode_frame_result); - EXPECT_EQ(encode_frame_info.show_idx, encode_frame_result.show_idx); - EXPECT_EQ(encode_frame_info.frame_type, encode_frame_result.frame_type); - } - simple_encode.EndEncode(); -} - -TEST_F(SimpleEncodeTest, GetFramePixelCount) { - SimpleEncode simple_encode(width_, height_, frame_rate_num_, frame_rate_den_, - target_bitrate_, num_frames_, target_level_, - in_file_path_str_.c_str()); - EXPECT_EQ(simple_encode.GetFramePixelCount(), - static_cast<uint64_t>(width_ * height_ * 3 / 2)); -} - -} // namespace -} // namespace vp9 - -int main(int argc, char **argv) { - ::testing::InitGoogleTest(&argc, argv); - return RUN_ALL_TESTS(); -}
diff --git a/test/svc_datarate_test.cc b/test/svc_datarate_test.cc index 0ff0c8a..5e74d7e 100644 --- a/test/svc_datarate_test.cc +++ b/test/svc_datarate_test.cc
@@ -81,10 +81,13 @@ num_resize_down_ = 0; num_resize_up_ = 0; for (int i = 0; i < VPX_MAX_LAYERS; i++) { - prev_frame_width[i] = 320; - prev_frame_height[i] = 240; + prev_frame_width_[i] = 320; + prev_frame_height_[i] = 240; } ksvc_flex_noupd_tlenh_ = false; + external_resize_dynamic_drop_layer_ = false; + external_resize_pattern_ = 0; + superframe_cnt_ = 0; } void BeginPassHook(unsigned int /*pass*/) override {} @@ -207,6 +210,8 @@ if (use_post_encode_drop_) { encoder->Control(VP9E_SET_POSTENCODE_DROP, use_post_encode_drop_); } + // We want to force external resize on the very first frame. + if (external_resize_dynamic_drop_layer_) video->Next(); } if (denoiser_off_on_) { @@ -316,7 +321,98 @@ encoder->Config(&cfg_); } - if (dynamic_drop_layer_ && !single_layer_resize_) { + if (external_resize_dynamic_drop_layer_) { + frame_flags_ = 0; + for (int i = 0; i < 9; ++i) { + svc_params_.min_quantizers[i] = 20; + svc_params_.max_quantizers[i] = 56; + } + if (video->frame() == 1 || video->frame() == 150) { + // Set the new top width/height for external resize. + top_sl_width_ = video->img()->d_w; + top_sl_height_ = video->img()->d_h; + for (int i = 0; i < 9; ++i) { + bitrate_layer_[i] = cfg_.layer_target_bitrate[i]; + } + if (external_resize_pattern_ == 1) { + // Input size is 1/4. 2 top spatial layers are dropped. + // This will trigger skip encoding/dropping of two top spatial layers. + cfg_.rc_target_bitrate -= + cfg_.layer_target_bitrate[5] + cfg_.layer_target_bitrate[8]; + for (int i = 3; i < 9; ++i) { + cfg_.layer_target_bitrate[i] = 0; + } + for (int sl = 0; sl < 3; sl++) { + svc_params_.scaling_factor_num[sl] = 1; + svc_params_.scaling_factor_den[sl] = 1; + } + } else if (external_resize_pattern_ == 2) { + // Input size is 1/2. Top spatial layer is dropped. + // This will trigger skip encoding/dropping of top spatial layer. + cfg_.rc_target_bitrate -= cfg_.layer_target_bitrate[8]; + for (int i = 6; i < 9; ++i) { + cfg_.layer_target_bitrate[i] = 0; + } + svc_params_.scaling_factor_num[0] = 1; + svc_params_.scaling_factor_den[0] = 2; + svc_params_.scaling_factor_num[1] = 1; + svc_params_.scaling_factor_den[1] = 1; + svc_params_.scaling_factor_num[2] = 1; + svc_params_.scaling_factor_den[2] = 1; + } + encoder->Config(&cfg_); + encoder->Control(VP9E_SET_SVC_PARAMETERS, &svc_params_); + } else if (video->frame() == 50 || video->frame() == 200) { + top_sl_width_ = video->img()->d_w; + top_sl_height_ = video->img()->d_h; + if (external_resize_pattern_ == 1) { + // Input size is 1/2. Change layer bitrates to set top layer to 0. + // This will trigger skip encoding/dropping of top spatial layer. + cfg_.rc_target_bitrate += bitrate_layer_[5]; + for (int i = 3; i < 6; ++i) { + cfg_.layer_target_bitrate[i] = bitrate_layer_[i]; + } + svc_params_.scaling_factor_num[0] = 1; + svc_params_.scaling_factor_den[0] = 2; + svc_params_.scaling_factor_num[1] = 1; + svc_params_.scaling_factor_den[1] = 1; + svc_params_.scaling_factor_num[2] = 1; + svc_params_.scaling_factor_den[2] = 1; + } else if (external_resize_pattern_ == 2) { + // Input size is 1/4. Change layer bitrates to set two top layers to + // 0. This will trigger skip encoding/dropping of two top spatial + // layers. + cfg_.rc_target_bitrate -= bitrate_layer_[5]; + for (int i = 3; i < 6; ++i) { + cfg_.layer_target_bitrate[i] = 0; + } + for (int sl = 0; sl < 3; sl++) { + svc_params_.scaling_factor_num[sl] = 1; + svc_params_.scaling_factor_den[sl] = 1; + } + } + encoder->Config(&cfg_); + encoder->Control(VP9E_SET_SVC_PARAMETERS, &svc_params_); + } else if (video->frame() == 100 || video->frame() == 250) { + top_sl_width_ = video->img()->d_w; + top_sl_height_ = video->img()->d_h; + // Input is original size. Change layer bitrates to nonzero for all + // layers. + cfg_.rc_target_bitrate = + bitrate_layer_[2] + bitrate_layer_[5] + bitrate_layer_[8]; + for (int i = 0; i < 9; ++i) { + cfg_.layer_target_bitrate[i] = bitrate_layer_[i]; + } + svc_params_.scaling_factor_num[0] = 1; + svc_params_.scaling_factor_den[0] = 4; + svc_params_.scaling_factor_num[1] = 1; + svc_params_.scaling_factor_den[1] = 2; + svc_params_.scaling_factor_num[2] = 1; + svc_params_.scaling_factor_den[2] = 1; + encoder->Config(&cfg_); + encoder->Control(VP9E_SET_SVC_PARAMETERS, &svc_params_); + } + } else if (dynamic_drop_layer_ && !single_layer_resize_) { if (video->frame() == 0) { // Change layer bitrates to set top layers to 0. This will trigger skip // encoding/dropping of top two spatial layers. @@ -432,6 +528,7 @@ const vpx_rational_t tb = video->timebase(); timebase_ = static_cast<double>(tb.num) / tb.den; duration_ = 0; + superframe_cnt_++; } vpx_codec_err_t parse_superframe_index(const uint8_t *data, size_t data_sz, @@ -476,6 +573,12 @@ last_pts_ = pkt->data.frame.pts; const bool key_frame = (pkt->data.frame.flags & VPX_FRAME_IS_KEY) ? true : false; + if (external_resize_dynamic_drop_layer_) { + // No key frames expected in stream, except for first. + if (cfg_.kf_max_dist > 1000) { + ASSERT_FALSE(key_frame && superframe_cnt_ > 1); + } + } if (key_frame) { // For test that inserts layer sync frames: requesting a layer_sync on // the base layer must force key frame. So if any key frame occurs after @@ -547,7 +650,7 @@ } } - if (!single_layer_resize_) { + if (!single_layer_resize_ && sl < number_spatial_layers_ - 1) { unsigned int scaled_width = top_sl_width_ * svc_params_.scaling_factor_num[sl] / svc_params_.scaling_factor_den[sl]; @@ -559,15 +662,15 @@ if (scaled_height % 2 != 0) scaled_height += 1; ASSERT_EQ(pkt->data.frame.height[sl], scaled_height); } else if (superframe_count_ > 0) { - if (pkt->data.frame.width[sl] < prev_frame_width[sl] && - pkt->data.frame.height[sl] < prev_frame_height[sl]) + if (pkt->data.frame.width[sl] < prev_frame_width_[sl] && + pkt->data.frame.height[sl] < prev_frame_height_[sl]) num_resize_down_ += 1; - if (pkt->data.frame.width[sl] > prev_frame_width[sl] && - pkt->data.frame.height[sl] > prev_frame_height[sl]) + if (pkt->data.frame.width[sl] > prev_frame_width_[sl] && + pkt->data.frame.height[sl] > prev_frame_height_[sl]) num_resize_up_ += 1; } - prev_frame_width[sl] = pkt->data.frame.width[sl]; - prev_frame_height[sl] = pkt->data.frame.height[sl]; + prev_frame_width_[sl] = pkt->data.frame.width[sl]; + prev_frame_height_[sl] = pkt->data.frame.height[sl]; } } @@ -634,9 +737,13 @@ bool denoiser_enable_layers_; int num_resize_up_; int num_resize_down_; - unsigned int prev_frame_width[VPX_MAX_LAYERS]; - unsigned int prev_frame_height[VPX_MAX_LAYERS]; + unsigned int prev_frame_width_[VPX_MAX_LAYERS]; + unsigned int prev_frame_height_[VPX_MAX_LAYERS]; bool ksvc_flex_noupd_tlenh_; + bool external_resize_dynamic_drop_layer_; + int bitrate_layer_[9]; + int external_resize_pattern_; + int superframe_cnt_; private: void SetConfig(const int num_temporal_layer) override { @@ -662,6 +769,80 @@ unsigned int mismatch_nframes_; }; +void ScaleForFrameNumber(unsigned int frame, unsigned int initial_w, + unsigned int initial_h, unsigned int *w, + unsigned int *h, int resize_pattern) { + *w = initial_w; + *h = initial_h; + if (resize_pattern == 1) { + if (frame < 50) { + *w = initial_w / 4; + *h = initial_h / 4; + } else if (frame < 100) { + *w = initial_w / 2; + *h = initial_h / 2; + } else if (frame < 150) { + *w = initial_w; + *h = initial_h; + } else if (frame < 200) { + *w = initial_w / 4; + *h = initial_h / 4; + } else if (frame < 250) { + *w = initial_w / 2; + *h = initial_h / 2; + } + } else if (resize_pattern == 2) { + if (frame < 50) { + *w = initial_w / 2; + *h = initial_h / 2; + } else if (frame < 100) { + *w = initial_w / 4; + *h = initial_h / 4; + } else if (frame < 150) { + *w = initial_w; + *h = initial_h; + } else if (frame < 200) { + *w = initial_w / 2; + *h = initial_h / 2; + } else if (frame < 250) { + *w = initial_w / 4; + *h = initial_h / 4; + } + } +} + +class ResizingVideoSource : public ::libvpx_test::DummyVideoSource { + public: + ResizingVideoSource(int width, int height) { + top_width_ = width; + top_height_ = height; + SetSize(top_width_, top_height_); + limit_ = 300; + } + int external_resize_pattern_ = 1; + int force_zero_source_ = 0; + int top_width_; + int top_height_; + ~ResizingVideoSource() override = default; + + protected: + void Next() override { + ++frame_; + unsigned int width = 0; + unsigned int height = 0; + libvpx_test::ACMRandom rnd(libvpx_test::ACMRandom::DeterministicSeed()); + ScaleForFrameNumber(frame_, top_width_, top_height_, &width, &height, + external_resize_pattern_); + SetSize(width, height); + FillFrame(); + unsigned char *image = img_->planes[0]; + for (size_t i = 0; i < raw_sz_; ++i) { + image[i] = rnd.Rand8(); + if (force_zero_source_ && frame_ % 20 == 0) image[i] = 0; + } + } +}; + // Params: speed setting. class DatarateOnePassCbrSvcSingleBR : public DatarateOnePassCbrSvc, @@ -1070,7 +1251,7 @@ #endif } -// For pass CBR SVC with 1 spatial and 2 temporal layers with dynamic resize +// For 1 pass CBR SVC with 1 spatial and 2 temporal layers with dynamic resize // and denoiser enabled. The resizer will resize the single layer down and back // up again, as the bitrate goes back up. TEST_P(DatarateOnePassCbrSvcSingleBR, OnePassCbrSvc1SL2TL_DenoiseResize) { @@ -1152,6 +1333,172 @@ #endif } +// For 1 pass CBR SVC with 3 spatial and 3 temporal layers with external resize +// and denoiser enabled. The external resizer will resize down and back up, +// setting 0/nonzero bitrate on spatial enhancement layers to disable/enable +// layers. Resizing starts on first frame and the pattern is: +// 1/4 -> 1/2 -> 1 -> 1/4 -> 1/2. +TEST_P(DatarateOnePassCbrSvcSingleBR, + OnePassCbrSvc3SL3TL_DenoiseExternalResizePattern1) { + SetSvcConfig(3, 3); + cfg_.rc_buf_initial_sz = 500; + cfg_.rc_buf_optimal_sz = 500; + cfg_.rc_buf_sz = 1000; + cfg_.rc_min_quantizer = 40; + cfg_.rc_max_quantizer = 63; + cfg_.g_threads = 1; + cfg_.temporal_layering_mode = 3; + cfg_.ts_rate_decimator[0] = 4; + cfg_.ts_rate_decimator[1] = 2; + cfg_.ts_rate_decimator[2] = 1; + cfg_.rc_dropframe_thresh = 1; + cfg_.kf_max_dist = 10000; + cfg_.kf_min_dist = 10000; + cfg_.rc_resize_allowed = 0; + cfg_.g_w = 1280; + cfg_.g_h = 720; + top_sl_width_ = 1280; + top_sl_height_ = 720; + ResizingVideoSource video(1280, 720); + video.external_resize_pattern_ = 1; + video.force_zero_source_ = 0; + cfg_.rc_target_bitrate = 1000; + ResetModel(); + dynamic_drop_layer_ = false; + single_layer_resize_ = false; + denoiser_on_ = 1; + base_speed_setting_ = speed_setting_; + external_resize_dynamic_drop_layer_ = true; + external_resize_pattern_ = video.external_resize_pattern_; + AssignLayerBitrates(); + ASSERT_NO_FATAL_FAILURE(RunLoop(&video)); +} + +// For 1 pass CBR SVC with 3 spatial and 3 temporal layers with external resize +// and denoiser enabled. The external resizer will resize down and back up, +// setting 0/nonzero bitrate on spatial enhancement layers to disable/enable +// layers. Resizing starts on first frame and the pattern is: +// 1/2 -> 1/4 -> 1 -> 1/2 -> 1/4. +TEST_P(DatarateOnePassCbrSvcSingleBR, + OnePassCbrSvc3SL3TL_DenoiseExternalResizePattern2) { + SetSvcConfig(3, 3); + cfg_.rc_buf_initial_sz = 500; + cfg_.rc_buf_optimal_sz = 500; + cfg_.rc_buf_sz = 1000; + cfg_.rc_min_quantizer = 40; + cfg_.rc_max_quantizer = 63; + cfg_.g_threads = 1; + cfg_.temporal_layering_mode = 3; + cfg_.ts_rate_decimator[0] = 4; + cfg_.ts_rate_decimator[1] = 2; + cfg_.ts_rate_decimator[2] = 1; + cfg_.rc_dropframe_thresh = 1; + cfg_.kf_max_dist = 10000; + cfg_.kf_min_dist = 10000; + cfg_.rc_resize_allowed = 0; + cfg_.g_w = 1280; + cfg_.g_h = 720; + top_sl_width_ = 1280; + top_sl_height_ = 720; + ResizingVideoSource video(1280, 720); + video.external_resize_pattern_ = 2; + video.force_zero_source_ = 0; + cfg_.rc_target_bitrate = 1000; + ResetModel(); + dynamic_drop_layer_ = false; + single_layer_resize_ = false; + denoiser_on_ = 1; + base_speed_setting_ = speed_setting_; + external_resize_dynamic_drop_layer_ = true; + external_resize_pattern_ = video.external_resize_pattern_; + AssignLayerBitrates(); + ASSERT_NO_FATAL_FAILURE(RunLoop(&video)); +} + +// For 1 pass CBR SVC with 3 spatial and 3 temporal layers with external resize +// and denoiser enabled. The external resizer will resize down and back up, +// setting 0/nonzero bitrate on spatial enhancement layers to disable/enable +// layers. Resizing starts on first frame and the pattern is: +// 1/2 -> 1/4 -> 1 -> 1/2 -> 1/4. This test uses 4 threads with small keyframe +// spacing, and top resolution is 1280x960. +TEST_P(DatarateOnePassCbrSvcSingleBR, + OnePassCbrSvc3SL3TL_DenoiseExternalResizePattern2Key4Threads) { + SetSvcConfig(3, 3); + cfg_.rc_buf_initial_sz = 500; + cfg_.rc_buf_optimal_sz = 500; + cfg_.rc_buf_sz = 1000; + cfg_.rc_min_quantizer = 40; + cfg_.rc_max_quantizer = 63; + cfg_.g_threads = 4; + cfg_.temporal_layering_mode = 3; + cfg_.ts_rate_decimator[0] = 4; + cfg_.ts_rate_decimator[1] = 2; + cfg_.ts_rate_decimator[2] = 1; + cfg_.rc_dropframe_thresh = 1; + cfg_.kf_max_dist = 40; + cfg_.kf_min_dist = 40; + cfg_.rc_resize_allowed = 0; + cfg_.g_w = 1280; + cfg_.g_h = 960; + top_sl_width_ = cfg_.g_w; + top_sl_height_ = cfg_.g_h; + ResizingVideoSource video(1280, 960); + video.external_resize_pattern_ = 2; + video.force_zero_source_ = 0; + cfg_.rc_target_bitrate = 1000; + ResetModel(); + dynamic_drop_layer_ = false; + single_layer_resize_ = false; + denoiser_on_ = 1; + base_speed_setting_ = speed_setting_; + external_resize_dynamic_drop_layer_ = true; + external_resize_pattern_ = video.external_resize_pattern_; + AssignLayerBitrates(); + ASSERT_NO_FATAL_FAILURE(RunLoop(&video)); +} + +// For 1 pass CBR SVC with 3 spatial and 3 temporal layers with external resize +// and denoiser enabled. The external resizer will resize down and back up, +// setting 0/nonzero bitrate on spatial enhancement layers to disable/enable +// layers. Resizing starts on first frame and the pattern is: +// 1/4 -> 1/2 -> 1 -> 1/4 -> 1/2. The source will be set to 0 every x frames, +// otherwise random values, to trigger scene detection in the encoder. +TEST_P(DatarateOnePassCbrSvcSingleBR, + OnePassCbrSvc3SL3TL_DenoiseExternalResizePattern1SceneChange) { + SetSvcConfig(3, 3); + cfg_.rc_buf_initial_sz = 500; + cfg_.rc_buf_optimal_sz = 500; + cfg_.rc_buf_sz = 1000; + cfg_.rc_min_quantizer = 40; + cfg_.rc_max_quantizer = 63; + cfg_.g_threads = 1; + cfg_.temporal_layering_mode = 3; + cfg_.ts_rate_decimator[0] = 4; + cfg_.ts_rate_decimator[1] = 2; + cfg_.ts_rate_decimator[2] = 1; + cfg_.rc_dropframe_thresh = 1; + cfg_.kf_max_dist = 10000; + cfg_.kf_min_dist = 10000; + cfg_.rc_resize_allowed = 0; + cfg_.g_w = 1280; + cfg_.g_h = 720; + top_sl_width_ = 1280; + top_sl_height_ = 720; + ResizingVideoSource video(1280, 720); + video.external_resize_pattern_ = 1; + video.force_zero_source_ = 1; + cfg_.rc_target_bitrate = 1000; + ResetModel(); + dynamic_drop_layer_ = false; + single_layer_resize_ = false; + denoiser_on_ = 1; + base_speed_setting_ = speed_setting_; + external_resize_dynamic_drop_layer_ = true; + external_resize_pattern_ = video.external_resize_pattern_; + AssignLayerBitrates(); + ASSERT_NO_FATAL_FAILURE(RunLoop(&video)); +} + // Params: speed setting and index for bitrate array. class DatarateOnePassCbrSvcMultiBR : public DatarateOnePassCbrSvc, @@ -1317,6 +1664,71 @@ } // Check basic rate targeting for 1 pass CBR SVC: 3 spatial layers and +// 3 temporal layers. Run HD clip with 4 threads, for 1284x770, which +// likely is the issue for Bug: 366146260. +TEST_P(DatarateOnePassCbrSvcFrameDropMultiBR, + OnePassCbrSvc3SL3TL4Threads1284x770) { + SetSvcConfig(3, 3); + cfg_.rc_buf_initial_sz = 500; + cfg_.rc_buf_optimal_sz = 500; + cfg_.rc_buf_sz = 1000; + cfg_.rc_min_quantizer = 0; + cfg_.rc_max_quantizer = 63; + cfg_.g_threads = 4; + cfg_.rc_dropframe_thresh = 30; + cfg_.kf_max_dist = 9999; + ::libvpx_test::Y4mVideoSource video("niklas_1284_770_30.y4m", 0, 60); + top_sl_width_ = 1284; + top_sl_height_ = 770; + layer_framedrop_ = 0; + const int bitrates[3] = { 200, 400, 600 }; + cfg_.rc_target_bitrate = bitrates[GET_PARAM(3)]; + ResetModel(); + layer_framedrop_ = GET_PARAM(2); + AssignLayerBitrates(); + ASSERT_NO_FATAL_FAILURE(RunLoop(&video)); + CheckLayerRateTargeting(number_spatial_layers_, number_temporal_layers_, 0.58, + 1.2); +#if CONFIG_VP9_DECODER + // The non-reference frames are expected to be mismatched frames as the + // encoder will avoid loopfilter on these frames. + EXPECT_EQ(GetNonRefFrames(), GetMismatchFrames()); +#endif +} + +// Check basic rate targeting for 1 pass CBR SVC: 3 spatial layers and +// 3 temporal layers. Run HD clip with 4 threads, for 1857x167. +TEST_P(DatarateOnePassCbrSvcFrameDropMultiBR, + OnePassCbrSvc3SL3TL4Threads1857x167) { + SetSvcConfig(3, 3); + cfg_.rc_buf_initial_sz = 500; + cfg_.rc_buf_optimal_sz = 500; + cfg_.rc_buf_sz = 1000; + cfg_.rc_min_quantizer = 0; + cfg_.rc_max_quantizer = 63; + cfg_.g_threads = 1; + cfg_.rc_dropframe_thresh = 30; + cfg_.kf_max_dist = 9999; + ::libvpx_test::Y4mVideoSource video("niklas_1857_167_30.y4m", 0, 60); + top_sl_width_ = 1857; + top_sl_height_ = 167; + layer_framedrop_ = 0; + const int bitrates[3] = { 200, 400, 600 }; + cfg_.rc_target_bitrate = bitrates[GET_PARAM(3)]; + ResetModel(); + layer_framedrop_ = GET_PARAM(2); + AssignLayerBitrates(); + ASSERT_NO_FATAL_FAILURE(RunLoop(&video)); + CheckLayerRateTargeting(number_spatial_layers_, number_temporal_layers_, 0.58, + 1.2); +#if CONFIG_VP9_DECODER + // The non-reference frames are expected to be mismatched frames as the + // encoder will avoid loopfilter on these frames. + EXPECT_EQ(GetNonRefFrames(), GetMismatchFrames()); +#endif +} + +// Check basic rate targeting for 1 pass CBR SVC: 3 spatial layers and // 2 temporal layers, for KSVC in flexible mode with no update of reference // frames for all spatial layers on TL > 0 superframes. // Run HD clip with 4 threads.
diff --git a/test/test-data.mk b/test/test-data.mk index 75110fc..118f03e 100644 --- a/test/test-data.mk +++ b/test/test-data.mk
@@ -26,6 +26,8 @@ LIBVPX_TEST_DATA-$(CONFIG_VP9_ENCODER) += desktop_credits.y4m LIBVPX_TEST_DATA-$(CONFIG_VP9_ENCODER) += niklas_1280_720_30.y4m +LIBVPX_TEST_DATA-$(CONFIG_VP9_ENCODER) += niklas_1284_770_30.y4m +LIBVPX_TEST_DATA-$(CONFIG_VP9_ENCODER) += niklas_1857_167_30.y4m LIBVPX_TEST_DATA-$(CONFIG_VP9_ENCODER) += noisy_clip_640_360.y4m LIBVPX_TEST_DATA-$(CONFIG_VP9_ENCODER) += rush_hour_444.y4m LIBVPX_TEST_DATA-$(CONFIG_VP9_ENCODER) += screendata.y4m
diff --git a/test/test-data.sha1 b/test/test-data.sha1 index 49f816f..5df2240 100644 --- a/test/test-data.sha1 +++ b/test/test-data.sha1
@@ -872,3 +872,5 @@ 8a0b2c350539859463d3546a67876c83ff6ff0ac *desktopqvga.320_240.yuv ad9942a073e245585c93f764ea299382a65939a7 *crowd_run_360p_10_150f.y4m f9a73e921552598a5804911e9f84fec2318e056a *repro-oss-fuzz-69906.y4m +320874b648f54e9156339e9d7e322ec4c51cb5f7 *niklas_1284_770_30.y4m +64e876e725f83b93e92a8240457c152dd6f5b77d *niklas_1857_167_30.y4m
diff --git a/test/test.mk b/test/test.mk index 28fe9db..7c5cbf0 100644 --- a/test/test.mk +++ b/test/test.mk
@@ -70,7 +70,6 @@ ifneq ($(CONFIG_REALTIME_ONLY),yes) LIBVPX_TEST_SRCS-$(CONFIG_VP9_ENCODER) += vp9_ext_ratectrl_test.cc endif -LIBVPX_TEST_SRCS-$(CONFIG_VP9_ENCODER) += ../vp9/simple_encode.h LIBVPX_TEST_SRCS-yes += decode_test_driver.cc LIBVPX_TEST_SRCS-yes += decode_test_driver.h @@ -211,10 +210,6 @@ endif LIBVPX_TEST_SRCS-$(CONFIG_VP9_ENCODER) += vp9_arf_freq_test.cc -ifeq ($(CONFIG_VP9_ENCODER),yes) -SIMPLE_ENCODE_TEST_SRCS-$(CONFIG_RATE_CTRL) := simple_encode_test.cc -endif - endif # VP9 ## Multi-codec / unconditional whitebox tests.
diff --git a/test/vp9_ext_ratectrl_test.cc b/test/vp9_ext_ratectrl_test.cc index 8f7372e..b85dee1 100644 --- a/test/vp9_ext_ratectrl_test.cc +++ b/test/vp9_ext_ratectrl_test.cc
@@ -22,7 +22,6 @@ #if CONFIG_VP9_DECODER #include "vpx/vp8dx.h" #endif -#include "vp9/simple_encode.h" #include "vpx/vpx_codec.h" #include "vpx/vpx_encoder.h" #include "vpx/vpx_ext_ratectrl.h" @@ -175,6 +174,9 @@ static_cast<RateControllerForTest *>(rate_ctrl_model); frame_decision->q_index = test_controller->CalculateFrameDecision(frame_gop_index); + frame_decision->rdmult = + frame_decision->q_index * frame_decision->q_index / 2; + frame_decision->delta_q_uv = 0; return VPX_RC_OK; }
diff --git a/third_party/libwebm/mkvparser/mkvparser.cc b/third_party/libwebm/mkvparser/mkvparser.cc index 042a0c5..4fa7b37 100644 --- a/third_party/libwebm/mkvparser/mkvparser.cc +++ b/third_party/libwebm/mkvparser/mkvparser.cc
@@ -8,7 +8,6 @@ #include "mkvparser/mkvparser.h" #if defined(_MSC_VER) && _MSC_VER < 1800 -#include <float.h> // _isnan() / _finite() #define MSC_COMPAT #endif @@ -7891,8 +7890,10 @@ if (frame_size <= 0) return E_FILE_FORMAT_INVALID; +#if LLONG_MAX > LONG_MAX if (frame_size > LONG_MAX) return E_FILE_FORMAT_INVALID; +#endif if ((pos + len) > stop) return E_FILE_FORMAT_INVALID; @@ -7958,8 +7959,10 @@ if (frame_size <= 0) return E_FILE_FORMAT_INVALID; +#if LLONG_MAX > LONG_MAX if (frame_size > LONG_MAX) return E_FILE_FORMAT_INVALID; +#endif curr.len = static_cast<long>(frame_size); // Check if size + curr.len could overflow.
diff --git a/tools.mk b/tools.mk index dd2ebeb..79bb0cb 100644 --- a/tools.mk +++ b/tools.mk
@@ -91,6 +91,13 @@ $(foreach proj,$(call enabled,PROJECTS),\ $(eval $(call vcproj_template,$(proj)))) +# Generate a list of all enabled sources, in particular for exporting to gyp +# based build systems. +tiny_ssim_srcs.txt: + @echo " [CREATE] $@" + @echo $(tiny_ssim.SRCS) | xargs -n1 echo | LC_ALL=C sort -u > $@ +CLEAN-OBJS += tiny_ssim_srcs.txt + # # Documentation Rules #
diff --git a/tools/set_analyzer_env.sh b/tools/set_analyzer_env.sh index 4d659db..a3e8a1a 100644 --- a/tools/set_analyzer_env.sh +++ b/tools/set_analyzer_env.sh
@@ -73,12 +73,18 @@ # Exact backtraces. cflags="${cflags} -fno-optimize-sibling-calls" -if [ "${sanitizer}" = "cfi" ]; then - # https://clang.llvm.org/docs/ControlFlowIntegrity.html - cflags="${cflags} -fno-sanitize-trap=cfi -flto -fvisibility=hidden" - ldflags="${ldflags} -fno-sanitize-trap=cfi -flto -fuse-ld=gold" - export AR="llvm-ar" -fi +case "${sanitizer}" in + cfi) + # https://clang.llvm.org/docs/ControlFlowIntegrity.html + cflags="${cflags} -fno-sanitize-trap=cfi -flto -fvisibility=hidden" + ldflags="${ldflags} -fno-sanitize-trap=cfi -flto -fuse-ld=gold" + export AR="llvm-ar" + ;; + integer|undefined) + # https://clang.llvm.org/docs/UndefinedBehaviorSanitizer.html + cflags="${cflags} -fsanitize=float-cast-overflow" + ;; +esac set -x export CC="clang"
diff --git a/tools/tiny_ssim.c b/tools/tiny_ssim.c index 8cc4d98..c07a9d2 100644 --- a/tools/tiny_ssim.c +++ b/tools/tiny_ssim.c
@@ -340,11 +340,24 @@ } if (argc > 3) { - sscanf(argv[3], "%dx%d", &w, &h); + if (sscanf(argv[3], "%dx%d", &w, &h) != 2) { + fprintf(stderr, "arguments for w/h not assigned!\n"); + goto clean_up; + } + // Limit width/height to 4K. The frame_size set in the function + // open_input_file() will still be within range of int. + if (w < 1 || w > 4096 || h < 1 || h > 4096) { + fprintf(stderr, + "width or height is too large (above 4096) or below 1!\n"); + goto clean_up; + } } if (argc > 6) { - sscanf(argv[6], "%d", &bit_depth); + if (sscanf(argv[6], "%d", &bit_depth) != 1) { + fprintf(stderr, "argument for bit_depth not assigned!\n"); + goto clean_up; + } } if (open_input_file(argv[1], &in[0], w, h, bit_depth) < 0) { @@ -389,7 +402,10 @@ // encoding in mode 10. 7 would be reasonable for comparing TL0 of a 4-layer // encoding. if (argc > 4) { - sscanf(argv[4], "%d", &tl_skip); + if (sscanf(argv[4], "%d", &tl_skip) != 1) { + fprintf(stderr, "argument for tl_skip not assigned!\n"); + goto clean_up; + } if (argc > 5) { framestats = fopen(argv[5], "w"); if (!framestats) {
diff --git a/vp8/decoder/decodeframe.c b/vp8/decoder/decodeframe.c index af9a98c..a20f33d 100644 --- a/vp8/decoder/decodeframe.c +++ b/vp8/decoder/decodeframe.c
@@ -965,8 +965,8 @@ data = data_end; } } else { - memcpy(&xd->pre, yv12_fb_new, sizeof(YV12_BUFFER_CONFIG)); - memcpy(&xd->dst, yv12_fb_new, sizeof(YV12_BUFFER_CONFIG)); + xd->pre = *yv12_fb_new; + xd->dst = *yv12_fb_new; } } if ((!pbi->decoded_key_frame && pc->frame_type != KEY_FRAME)) { @@ -1156,7 +1156,7 @@ if (pbi->ec_active && xd->corrupted) pc->refresh_entropy_probs = 0; #endif if (pc->refresh_entropy_probs == 0) { - memcpy(&pc->lfc, &pc->fc, sizeof(pc->fc)); + pc->lfc = pc->fc; } pc->refresh_last_frame = pc->frame_type == KEY_FRAME || vp8_read_bit(bc); @@ -1245,7 +1245,7 @@ * \n",bc->pos+pbi->bc2.pos); */ if (pc->refresh_entropy_probs == 0) { - memcpy(&pc->fc, &pc->lfc, sizeof(pc->fc)); + pc->fc = pc->lfc; pbi->independent_partitions = prev_independent_partitions; }
diff --git a/vp8/encoder/encodeframe.c b/vp8/encoder/encodeframe.c index fa43340..97855ae 100644 --- a/vp8/encoder/encodeframe.c +++ b/vp8/encoder/encodeframe.c
@@ -747,8 +747,10 @@ #endif { +#if CONFIG_INTERNAL_STATS struct vpx_usec_timer emr_timer; vpx_usec_timer_start(&emr_timer); +#endif #if CONFIG_MULTITHREAD if (vpx_atomic_load_acquire(&cpi->b_multi_threaded)) { @@ -894,8 +896,10 @@ } #endif +#if CONFIG_INTERNAL_STATS vpx_usec_timer_mark(&emr_timer); cpi->time_encode_mb_row += vpx_usec_timer_elapsed(&emr_timer); +#endif } // Work out the segment probabilities if segmentation is enabled
diff --git a/vp8/encoder/encodemb.c b/vp8/encoder/encodemb.c index 3fd8d5f..052d09b 100644 --- a/vp8/encoder/encodemb.c +++ b/vp8/encoder/encodemb.c
@@ -396,8 +396,8 @@ ENTROPY_CONTEXT *ta; ENTROPY_CONTEXT *tl; - memcpy(&t_above, x->e_mbd.above_context, sizeof(ENTROPY_CONTEXT_PLANES)); - memcpy(&t_left, x->e_mbd.left_context, sizeof(ENTROPY_CONTEXT_PLANES)); + t_above = *x->e_mbd.above_context; + t_left = *x->e_mbd.left_context; ta = (ENTROPY_CONTEXT *)&t_above; tl = (ENTROPY_CONTEXT *)&t_left; @@ -437,8 +437,8 @@ if (!x->e_mbd.left_context) return; - memcpy(&t_above, x->e_mbd.above_context, sizeof(ENTROPY_CONTEXT_PLANES)); - memcpy(&t_left, x->e_mbd.left_context, sizeof(ENTROPY_CONTEXT_PLANES)); + t_above = *x->e_mbd.above_context; + t_left = *x->e_mbd.left_context; ta = (ENTROPY_CONTEXT *)&t_above; tl = (ENTROPY_CONTEXT *)&t_left; @@ -470,8 +470,8 @@ if (!x->e_mbd.left_context) return; - memcpy(&t_above, x->e_mbd.above_context, sizeof(ENTROPY_CONTEXT_PLANES)); - memcpy(&t_left, x->e_mbd.left_context, sizeof(ENTROPY_CONTEXT_PLANES)); + t_above = *x->e_mbd.above_context; + t_left = *x->e_mbd.left_context; ta = (ENTROPY_CONTEXT *)&t_above; tl = (ENTROPY_CONTEXT *)&t_left;
diff --git a/vp8/encoder/firstpass.c b/vp8/encoder/firstpass.c index 8a3861e..9871e56 100644 --- a/vp8/encoder/firstpass.c +++ b/vp8/encoder/firstpass.c
@@ -795,7 +795,7 @@ fps.duration = (double)(cpi->source->ts_end - cpi->source->ts_start); /* don't want to do output stats with a stack variable! */ - memcpy(&cpi->twopass.this_frame_stats, &fps, sizeof(FIRSTPASS_STATS)); + cpi->twopass.this_frame_stats = fps; output_stats(cpi->output_pkt_list, &cpi->twopass.this_frame_stats); accumulate_stats(&cpi->twopass.total_stats, &fps); } @@ -1700,7 +1700,7 @@ break; } - memcpy(this_frame, &next_frame, sizeof(*this_frame)); + *this_frame = next_frame; old_boost_score = boost_score; } @@ -2236,7 +2236,7 @@ /* keyframe and section processing ! */ if (cpi->twopass.frames_to_key == 0) { /* Define next KF group and assign bits to it */ - memcpy(&this_frame_copy, &this_frame, sizeof(this_frame)); + this_frame_copy = this_frame; find_next_key_frame(cpi, &this_frame_copy); /* Special case: Error error_resilient_mode mode does not make much @@ -2258,7 +2258,7 @@ /* Is this a GF / ARF (Note that a KF is always also a GF) */ if (cpi->frames_till_gf_update_due == 0) { /* Define next gf group and assign bits to it */ - memcpy(&this_frame_copy, &this_frame, sizeof(this_frame)); + this_frame_copy = this_frame; define_gf_group(cpi, &this_frame_copy); /* If we are going to code an altref frame at the end of the group @@ -2273,7 +2273,7 @@ * to the GF group */ int bak = cpi->per_frame_bandwidth; - memcpy(&this_frame_copy, &this_frame, sizeof(this_frame)); + this_frame_copy = this_frame; assign_std_frame_bits(cpi, &this_frame_copy); cpi->per_frame_bandwidth = bak; } @@ -2293,12 +2293,12 @@ if (cpi->common.frame_type != KEY_FRAME) { /* Assign bits from those allocated to the GF group */ - memcpy(&this_frame_copy, &this_frame, sizeof(this_frame)); + this_frame_copy = this_frame; assign_std_frame_bits(cpi, &this_frame_copy); } } else { /* Assign bits from those allocated to the GF group */ - memcpy(&this_frame_copy, &this_frame, sizeof(this_frame)); + this_frame_copy = this_frame; assign_std_frame_bits(cpi, &this_frame_copy); } } @@ -2430,7 +2430,7 @@ double decay_accumulator = 1.0; double next_iiratio; - memcpy(&local_next_frame, next_frame, sizeof(*next_frame)); + local_next_frame = *next_frame; /* Note the starting file position so we can reset to it */ start_pos = cpi->twopass.stats_in; @@ -2523,7 +2523,7 @@ cpi->twopass.frames_to_key = 1; /* Take a copy of the initial frame details */ - memcpy(&first_frame, this_frame, sizeof(*this_frame)); + first_frame = *this_frame; cpi->twopass.kf_group_bits = 0; cpi->twopass.kf_group_error_left = 0; @@ -2545,7 +2545,7 @@ kf_group_coded_err += this_frame->coded_error; /* Load the next frame's stats. */ - memcpy(&last_frame, this_frame, sizeof(*this_frame)); + last_frame = *this_frame; input_stats(cpi, this_frame); /* Provided that we are not at the end of the file... */ @@ -2608,7 +2608,7 @@ cpi->twopass.frames_to_key /= 2; /* Copy first frame details */ - memcpy(&tmp_frame, &first_frame, sizeof(first_frame)); + tmp_frame = first_frame; /* Reset to the start of the group */ reset_fpf_position(cpi, start_position);
diff --git a/vp8/encoder/onyx_if.c b/vp8/encoder/onyx_if.c index 17726fa..70104ab 100644 --- a/vp8/encoder/onyx_if.c +++ b/vp8/encoder/onyx_if.c
@@ -23,6 +23,7 @@ #include "mcomp.h" #include "firstpass.h" #include "vpx_dsp/psnr.h" +#include "vpx_dsp/vpx_dsp_common.h" #include "vpx_scale/vpx_scale.h" #include "vp8/common/extend.h" #include "ratectrl.h" @@ -1874,8 +1875,7 @@ ? (2 * (cpi->common.mb_rows * cpi->common.mb_cols) / cpi->cyclic_refresh_mode_max_mbs_perframe) : 10; - cpi->gf_interval_onepass_cbr = - VPXMIN(40, VPXMAX(6, cpi->gf_interval_onepass_cbr)); + cpi->gf_interval_onepass_cbr = clamp(cpi->gf_interval_onepass_cbr, 6, 40); cpi->baseline_gf_interval = cpi->gf_interval_onepass_cbr; } @@ -3140,11 +3140,15 @@ if (cm->no_lpf) { cm->filter_level = 0; } else { +#if CONFIG_INTERNAL_STATS struct vpx_usec_timer timer; +#endif vpx_clear_system_state(); +#if CONFIG_INTERNAL_STATS vpx_usec_timer_start(&timer); +#endif if (cpi->sf.auto_filter == 0) { #if CONFIG_TEMPORAL_DENOISING if (cpi->oxcf.noise_sensitivity && cm->frame_type != KEY_FRAME) { @@ -3179,8 +3183,10 @@ vp8cx_set_alt_lf_level(cpi, cm->filter_level); } +#if CONFIG_INTERNAL_STATS vpx_usec_timer_mark(&timer); cpi->time_pick_lpf += vpx_usec_timer_elapsed(&timer); +#endif } #if CONFIG_MULTITHREAD @@ -3936,7 +3942,7 @@ if (cm->refresh_entropy_probs == 0) { /* save a copy for later refresh */ - memcpy(&cm->lfc, &cm->fc, sizeof(cm->fc)); + cm->lfc = cm->fc; } vp8_update_coef_context(cpi); @@ -4784,10 +4790,14 @@ int vp8_receive_raw_frame(VP8_COMP *cpi, unsigned int frame_flags, YV12_BUFFER_CONFIG *sd, int64_t time_stamp, int64_t end_time) { +#if CONFIG_INTERNAL_STATS struct vpx_usec_timer timer; +#endif int res = 0; +#if CONFIG_INTERNAL_STATS vpx_usec_timer_start(&timer); +#endif /* Reinit the lookahead buffer if the frame size changes */ if (sd->y_width != cpi->oxcf.Width || sd->y_height != cpi->oxcf.Height) { @@ -4800,8 +4810,10 @@ cpi->active_map_enabled ? cpi->active_map : NULL)) { res = -1; } +#if CONFIG_INTERNAL_STATS vpx_usec_timer_mark(&timer); cpi->time_receive_data += vpx_usec_timer_elapsed(&timer); +#endif return res; } @@ -4822,16 +4834,19 @@ unsigned char *dest_end, int64_t *time_stamp, int64_t *time_end, int flush) { VP8_COMMON *cm; - struct vpx_usec_timer tsctimer; struct vpx_usec_timer ticktimer; +#if CONFIG_INTERNAL_STATS struct vpx_usec_timer cmptimer; +#endif YV12_BUFFER_CONFIG *force_src_buffer = NULL; if (!cpi) return -1; cm = &cpi->common; +#if CONFIG_INTERNAL_STATS vpx_usec_timer_start(&cmptimer); +#endif cpi->source = NULL; @@ -5005,7 +5020,6 @@ } if (cpi->compressor_speed == 2) { - vpx_usec_timer_start(&tsctimer); vpx_usec_timer_start(&ticktimer); } @@ -5080,7 +5094,6 @@ if (cpi->compressor_speed == 2) { unsigned int duration, duration2; - vpx_usec_timer_mark(&tsctimer); vpx_usec_timer_mark(&ticktimer); duration = (int)(vpx_usec_timer_elapsed(&ticktimer)); @@ -5107,16 +5120,16 @@ } if (cm->refresh_entropy_probs == 0) { - memcpy(&cm->fc, &cm->lfc, sizeof(cm->fc)); + cm->fc = cm->lfc; } /* Save the contexts separately for alt ref, gold and last. */ /* (TODO jbb -> Optimize this with pointers to avoid extra copies. ) */ - if (cm->refresh_alt_ref_frame) memcpy(&cpi->lfc_a, &cm->fc, sizeof(cm->fc)); + if (cm->refresh_alt_ref_frame) cpi->lfc_a = cm->fc; - if (cm->refresh_golden_frame) memcpy(&cpi->lfc_g, &cm->fc, sizeof(cm->fc)); + if (cm->refresh_golden_frame) cpi->lfc_g = cm->fc; - if (cm->refresh_last_frame) memcpy(&cpi->lfc_n, &cm->fc, sizeof(cm->fc)); + if (cm->refresh_last_frame) cpi->lfc_n = cm->fc; /* if it's a dropped frame honor the requests on subsequent frames */ if (*size > 0) { @@ -5133,8 +5146,10 @@ /* Save layer specific state */ if (cpi->oxcf.number_of_layers > 1) vp8_save_layer_context(cpi); +#if CONFIG_INTERNAL_STATS vpx_usec_timer_mark(&cmptimer); cpi->time_compress_data += vpx_usec_timer_elapsed(&cmptimer); +#endif #if CONFIG_MULTITHREAD /* wait for the lpf thread done */
diff --git a/vp8/encoder/onyx_int.h b/vp8/encoder/onyx_int.h index 0f4550a..c2a1373 100644 --- a/vp8/encoder/onyx_int.h +++ b/vp8/encoder/onyx_int.h
@@ -556,10 +556,12 @@ vp8_refining_search_fn_t refining_search_sad; vp8_diamond_search_fn_t diamond_search_sad; vp8_variance_fn_ptr_t fn_ptr[BLOCK_MAX_SEGMENTS]; +#if CONFIG_INTERNAL_STATS uint64_t time_receive_data; uint64_t time_compress_data; uint64_t time_pick_lpf; uint64_t time_encode_mb_row; +#endif int base_skip_false_prob[128];
diff --git a/vp8/encoder/ratectrl.c b/vp8/encoder/ratectrl.c index 2f75ec9..215d9c7 100644 --- a/vp8/encoder/ratectrl.c +++ b/vp8/encoder/ratectrl.c
@@ -259,9 +259,9 @@ /* Make sure we initialize separate contexts for altref,gold, and normal. * TODO shouldn't need 3 different copies of structure to do this! */ - memcpy(&cpi->lfc_a, &cpi->common.fc, sizeof(cpi->common.fc)); - memcpy(&cpi->lfc_g, &cpi->common.fc, sizeof(cpi->common.fc)); - memcpy(&cpi->lfc_n, &cpi->common.fc, sizeof(cpi->common.fc)); + cpi->lfc_a = cpi->common.fc; + cpi->lfc_g = cpi->common.fc; + cpi->lfc_n = cpi->common.fc; cpi->common.filter_level = cpi->common.base_qindex * 3 / 8;
diff --git a/vp8/encoder/rdopt.c b/vp8/encoder/rdopt.c index 671cccd..ad3866c 100644 --- a/vp8/encoder/rdopt.c +++ b/vp8/encoder/rdopt.c
@@ -451,8 +451,8 @@ ENTROPY_CONTEXT *ta; ENTROPY_CONTEXT *tl; - memcpy(&t_above, mb->e_mbd.above_context, sizeof(ENTROPY_CONTEXT_PLANES)); - memcpy(&t_left, mb->e_mbd.left_context, sizeof(ENTROPY_CONTEXT_PLANES)); + t_above = *mb->e_mbd.above_context; + t_left = *mb->e_mbd.left_context; ta = (ENTROPY_CONTEXT *)&t_above; tl = (ENTROPY_CONTEXT *)&t_left; @@ -597,8 +597,8 @@ ENTROPY_CONTEXT *tl; const int *bmode_costs; - memcpy(&t_above, mb->e_mbd.above_context, sizeof(ENTROPY_CONTEXT_PLANES)); - memcpy(&t_left, mb->e_mbd.left_context, sizeof(ENTROPY_CONTEXT_PLANES)); + t_above = *mb->e_mbd.above_context; + t_left = *mb->e_mbd.left_context; ta = (ENTROPY_CONTEXT *)&t_above; tl = (ENTROPY_CONTEXT *)&t_left; @@ -689,8 +689,8 @@ ENTROPY_CONTEXT *ta; ENTROPY_CONTEXT *tl; - memcpy(&t_above, mb->e_mbd.above_context, sizeof(ENTROPY_CONTEXT_PLANES)); - memcpy(&t_left, mb->e_mbd.left_context, sizeof(ENTROPY_CONTEXT_PLANES)); + t_above = *mb->e_mbd.above_context; + t_left = *mb->e_mbd.left_context; ta = (ENTROPY_CONTEXT *)&t_above; tl = (ENTROPY_CONTEXT *)&t_left; @@ -962,8 +962,8 @@ ENTROPY_CONTEXT_PLANES t_above, t_left; ENTROPY_CONTEXT_PLANES t_above_b, t_left_b; - memcpy(&t_above, x->e_mbd.above_context, sizeof(ENTROPY_CONTEXT_PLANES)); - memcpy(&t_left, x->e_mbd.left_context, sizeof(ENTROPY_CONTEXT_PLANES)); + t_above = *x->e_mbd.above_context; + t_left = *x->e_mbd.left_context; vp8_zero(t_above_b); vp8_zero(t_left_b); @@ -1003,8 +1003,8 @@ ENTROPY_CONTEXT *ta_s; ENTROPY_CONTEXT *tl_s; - memcpy(&t_above_s, &t_above, sizeof(ENTROPY_CONTEXT_PLANES)); - memcpy(&t_left_s, &t_left, sizeof(ENTROPY_CONTEXT_PLANES)); + t_above_s = t_above; + t_left_s = t_left; ta_s = (ENTROPY_CONTEXT *)&t_above_s; tl_s = (ENTROPY_CONTEXT *)&t_left_s; @@ -1146,13 +1146,13 @@ mode_selected = this_mode; best_label_rd = this_rd; - memcpy(&t_above_b, &t_above_s, sizeof(ENTROPY_CONTEXT_PLANES)); - memcpy(&t_left_b, &t_left_s, sizeof(ENTROPY_CONTEXT_PLANES)); + t_above_b = t_above_s; + t_left_b = t_left_s; } } /*for each 4x4 mode*/ - memcpy(&t_above, &t_above_b, sizeof(ENTROPY_CONTEXT_PLANES)); - memcpy(&t_left, &t_left_b, sizeof(ENTROPY_CONTEXT_PLANES)); + t_above = t_above_b; + t_left = t_left_b; labels2mode(x, labels, i, mode_selected, &mode_mv[mode_selected], bsi->ref_mv, x->mvcost); @@ -1736,9 +1736,8 @@ (rd->distortion2 - rd->distortion_uv)); best_mode->rd = this_rd; - memcpy(&best_mode->mbmode, &x->e_mbd.mode_info_context->mbmi, - sizeof(MB_MODE_INFO)); - memcpy(&best_mode->partition, x->partition_info, sizeof(PARTITION_INFO)); + best_mode->mbmode = x->e_mbd.mode_info_context->mbmi; + best_mode->partition = *x->partition_info; if ((this_mode == B_PRED) || (this_mode == SPLITMV)) { int i; @@ -2348,8 +2347,7 @@ } /* macroblock modes */ - memcpy(&x->e_mbd.mode_info_context->mbmi, &best_mode.mbmode, - sizeof(MB_MODE_INFO)); + x->e_mbd.mode_info_context->mbmi = best_mode.mbmode; if (best_mode.mbmode.mode == B_PRED) { for (i = 0; i < 16; ++i) { @@ -2362,7 +2360,7 @@ xd->mode_info_context->bmi[i].mv.as_int = best_mode.bmodes[i].mv.as_int; } - memcpy(x->partition_info, &best_mode.partition, sizeof(PARTITION_INFO)); + *x->partition_info = best_mode.partition; x->e_mbd.mode_info_context->mbmi.mv.as_int = x->partition_info->bmi[15].mv.as_int;
diff --git a/vp8/vp8_cx_iface.c b/vp8/vp8_cx_iface.c index 35c94fb..96313ce 100644 --- a/vp8/vp8_cx_iface.c +++ b/vp8/vp8_cx_iface.c
@@ -8,6 +8,7 @@ * be found in the AUTHORS file in the root of the source tree. */ +#include <assert.h> #include <limits.h> #include <stdint.h> #include <stdlib.h> @@ -396,8 +397,7 @@ if (mr_cfg) { oxcf->mr_total_resolutions = mr_cfg->mr_total_resolutions; oxcf->mr_encoder_id = mr_cfg->mr_encoder_id; - oxcf->mr_down_sampling_factor.num = mr_cfg->mr_down_sampling_factor.num; - oxcf->mr_down_sampling_factor.den = mr_cfg->mr_down_sampling_factor.den; + oxcf->mr_down_sampling_factor = mr_cfg->mr_down_sampling_factor; oxcf->mr_low_res_mode_info = mr_cfg->mr_low_res_mode_info; } #else @@ -667,11 +667,22 @@ } #else (void)cfg; - (void)mem_loc; + *mem_loc = NULL; #endif return res; } +static void vp8e_mr_free_mem(void *mem_loc) { +#if CONFIG_MULTI_RES_ENCODING + LOWER_RES_FRAME_INFO *shared_mem_loc = (LOWER_RES_FRAME_INFO *)mem_loc; + free(shared_mem_loc->mb_info); + free(mem_loc); +#else + (void)mem_loc; + assert(!mem_loc); +#endif +} + static vpx_codec_err_t vp8e_init(vpx_codec_ctx_t *ctx, vpx_codec_priv_enc_mr_cfg_t *mr_cfg) { vpx_codec_err_t res = VPX_CODEC_OK; @@ -754,10 +765,7 @@ /* Free multi-encoder shared memory */ if (ctx->oxcf.mr_total_resolutions > 0 && (ctx->oxcf.mr_encoder_id == ctx->oxcf.mr_total_resolutions - 1)) { - LOWER_RES_FRAME_INFO *shared_mem_loc = - (LOWER_RES_FRAME_INFO *)ctx->oxcf.mr_low_res_mode_info; - free(shared_mem_loc->mb_info); - free(ctx->oxcf.mr_low_res_mode_info); + vp8e_mr_free_mem(ctx->oxcf.mr_low_res_mode_info); } #endif @@ -1431,5 +1439,6 @@ NULL, vp8e_get_preview, vp8e_mr_alloc_mem, + vp8e_mr_free_mem, } /* encoder functions */ };
diff --git a/vp8/vp8_dx_iface.c b/vp8/vp8_dx_iface.c index fa7d7be..65a86e2 100644 --- a/vp8/vp8_dx_iface.c +++ b/vp8/vp8_dx_iface.c
@@ -747,6 +747,7 @@ NULL, /* vpx_codec_enc_config_set_fn_t */ NULL, /* vpx_codec_get_global_headers_fn_t */ NULL, /* vpx_codec_get_preview_frame_fn_t */ - NULL /* vpx_codec_enc_mr_get_mem_loc_fn_t */ + NULL, /* vpx_codec_enc_mr_get_mem_loc_fn_t */ + NULL /* vpx_codec_enc_mr_free_mem_loc_fn_t */ } };
diff --git a/vp9/common/vp9_rtcd_defs.pl b/vp9/common/vp9_rtcd_defs.pl index af3ff0e..85fbde6 100644 --- a/vp9/common/vp9_rtcd_defs.pl +++ b/vp9/common/vp9_rtcd_defs.pl
@@ -18,6 +18,9 @@ #include "vp9/common/vp9_common.h" #include "vp9/common/vp9_enums.h" #include "vp9/common/vp9_filter.h" +#if !CONFIG_REALTIME_ONLY && CONFIG_VP9_ENCODER +#include "vp9/encoder/vp9_temporal_filter.h" +#endif struct macroblockd; @@ -115,7 +118,6 @@ # Encoder functions below this point. # if (vpx_config("CONFIG_VP9_ENCODER") eq "yes") { - # ENCODEMB INVOKE # @@ -189,6 +191,30 @@ } } +# +# 12-tap filter used in prediction data generation during temporal filtering +# +if (vpx_config("CONFIG_REALTIME_ONLY") ne "yes") { + add_proto qw/void vpx_convolve12_vert/, "const uint8_t *src, ptrdiff_t src_stride, uint8_t *dst, ptrdiff_t dst_stride, const InterpKernel12 *filter, int x0_q4, int x_step_q4, int y0_q4, int y_step_q4, int w, int h"; + specialize qw/vpx_convolve12_vert ssse3 avx2 neon neon_dotprod neon_i8mm/; + + add_proto qw/void vpx_convolve12_horiz/, "const uint8_t *src, ptrdiff_t src_stride, uint8_t *dst, ptrdiff_t dst_stride, const InterpKernel12 *filter, int x0_q4, int x_step_q4, int y0_q4, int y_step_q4, int w, int h"; + specialize qw/vpx_convolve12_horiz ssse3 avx2 neon neon_dotprod neon_i8mm/; + + add_proto qw/void vpx_convolve12/, "const uint8_t *src, ptrdiff_t src_stride, uint8_t *dst, ptrdiff_t dst_stride, const InterpKernel12 *filter, int x0_q4, int x_step_q4, int y0_q4, int y_step_q4, int w, int h"; + specialize qw/vpx_convolve12 ssse3 avx2 neon neon_dotprod neon_i8mm/; + + if (vpx_config("CONFIG_VP9_HIGHBITDEPTH") eq "yes") { + add_proto qw/void vpx_highbd_convolve12_vert/, "const uint16_t *src, ptrdiff_t src_stride, uint16_t *dst, ptrdiff_t dst_stride, const InterpKernel12 *filter, int x0_q4, int x_step_q4, int y0_q4, int y_step_q4, int w, int h, int bd"; + specialize qw/vpx_highbd_convolve12_vert ssse3 avx2/; + + add_proto qw/void vpx_highbd_convolve12_horiz/, "const uint16_t *src, ptrdiff_t src_stride, uint16_t *dst, ptrdiff_t dst_stride, const InterpKernel12 *filter, int x0_q4, int x_step_q4, int y0_q4, int y_step_q4, int w, int h, int bd"; + specialize qw/vpx_highbd_convolve12_horiz ssse3 avx2/; + + add_proto qw/void vpx_highbd_convolve12/, "const uint16_t *src, ptrdiff_t src_stride, uint16_t *dst, ptrdiff_t dst_stride, const InterpKernel12 *filter, int x0_q4, int x_step_q4, int y0_q4, int y_step_q4, int w, int h, int bd"; + specialize qw/vpx_highbd_convolve12 ssse3 avx2/; + } +} if (vpx_config("CONFIG_VP9_HIGHBITDEPTH") eq "yes") {
diff --git a/vp9/encoder/arm/neon/vp9_temporal_filter_neon.c b/vp9/encoder/arm/neon/vp9_temporal_filter_neon.c index a651a15..b6cce39 100644 --- a/vp9/encoder/arm/neon/vp9_temporal_filter_neon.c +++ b/vp9/encoder/arm/neon/vp9_temporal_filter_neon.c
@@ -14,6 +14,8 @@ #include "./vp9_rtcd.h" #include "./vpx_config.h" #include "vpx/vpx_integer.h" +#include "vpx_dsp/arm/mem_neon.h" +#include "vpx_dsp/arm/transpose_neon.h" #include "vp9/encoder/vp9_encoder.h" #include "vp9/encoder/vp9_temporal_filter.h" #include "vp9/encoder/vp9_temporal_filter_constants.h" @@ -847,3 +849,255 @@ use_whole_blk, u_accum, u_count, v_accum, v_count, y_dist_ptr, u_dist_ptr, v_dist_ptr); } + +static INLINE uint8x8_t convolve12_8(const int16x8_t s0, const int16x8_t s1, + const int16x8_t s2, const int16x8_t s3, + const int16x8_t s4, const int16x8_t s5, + const int16x8_t s6, const int16x8_t s7, + const int16x8_t s8, const int16x8_t s9, + const int16x8_t sA, const int16x8_t sB, + const int16x8_t filter_0_7, + const int16x4_t filter_8_11) { + const int16x4_t filter_0_3 = vget_low_s16(filter_0_7); + const int16x4_t filter_4_7 = vget_high_s16(filter_0_7); + + int16x8_t sum = vmulq_lane_s16(s0, filter_0_3, 0); + sum = vmlaq_lane_s16(sum, s1, filter_0_3, 1); + sum = vmlaq_lane_s16(sum, s2, filter_0_3, 2); + sum = vmlaq_lane_s16(sum, s3, filter_0_3, 3); + sum = vmlaq_lane_s16(sum, s4, filter_4_7, 0); + + sum = vmlaq_lane_s16(sum, s7, filter_4_7, 3); + sum = vmlaq_lane_s16(sum, s8, filter_8_11, 0); + sum = vmlaq_lane_s16(sum, s9, filter_8_11, 1); + sum = vmlaq_lane_s16(sum, sA, filter_8_11, 2); + sum = vmlaq_lane_s16(sum, sB, filter_8_11, 3); + + // Saturating addition is required for the largest filter taps to avoid + // overflow (while staying in 16-bit elements.) + sum = vqaddq_s16(sum, vmulq_lane_s16(s5, filter_4_7, 1)); + sum = vqaddq_s16(sum, vmulq_lane_s16(s6, filter_4_7, 2)); + + return vqrshrun_n_s16(sum, FILTER_BITS); +} + +void vpx_convolve12_horiz_neon(const uint8_t *src, ptrdiff_t src_stride, + uint8_t *dst, ptrdiff_t dst_stride, + const InterpKernel12 *filter, int x0_q4, + int x_step_q4, int y0_q4, int y_step_q4, int w, + int h) { + // Scaling not supported by Neon implementation. + if (x_step_q4 != 16) { + vpx_convolve12_horiz_c(src, src_stride, dst, dst_stride, filter, x0_q4, + x_step_q4, y0_q4, y_step_q4, w, h); + return; + } + + assert(w == 32 || w == 16 || w == 8); + assert(h % 4 == 0); + + const int16x8_t filter_0_7 = vld1q_s16(filter[x0_q4]); + const int16x4_t filter_8_11 = vld1_s16(filter[x0_q4] + 8); + + src -= MAX_FILTER_TAP / 2 - 1; + + do { + const uint8_t *s = src; + uint8_t *d = dst; + int width = w; + + uint8x8_t t0, t1, t2, t3; + load_u8_8x4(s, src_stride, &t0, &t1, &t2, &t3); + transpose_u8_8x4(&t0, &t1, &t2, &t3); + + int16x4_t s0 = vget_low_s16(vreinterpretq_s16_u16(vmovl_u8(t0))); + int16x4_t s1 = vget_low_s16(vreinterpretq_s16_u16(vmovl_u8(t1))); + int16x4_t s2 = vget_low_s16(vreinterpretq_s16_u16(vmovl_u8(t2))); + int16x4_t s3 = vget_low_s16(vreinterpretq_s16_u16(vmovl_u8(t3))); + int16x4_t s4 = vget_high_s16(vreinterpretq_s16_u16(vmovl_u8(t0))); + int16x4_t s5 = vget_high_s16(vreinterpretq_s16_u16(vmovl_u8(t1))); + int16x4_t s6 = vget_high_s16(vreinterpretq_s16_u16(vmovl_u8(t2))); + int16x4_t s7 = vget_high_s16(vreinterpretq_s16_u16(vmovl_u8(t3))); + + int16x8_t s0s1 = vcombine_s16(s0, s1); + int16x8_t s1s2 = vcombine_s16(s1, s2); + int16x8_t s2s3 = vcombine_s16(s2, s3); + int16x8_t s3s4 = vcombine_s16(s3, s4); + int16x8_t s4s5 = vcombine_s16(s4, s5); + int16x8_t s5s6 = vcombine_s16(s5, s6); + int16x8_t s6s7 = vcombine_s16(s6, s7); + + load_u8_8x4(s + 8, src_stride, &t0, &t1, &t2, &t3); + transpose_u8_8x4(&t0, &t1, &t2, &t3); + + int16x4_t s8 = vget_low_s16(vreinterpretq_s16_u16(vmovl_u8(t0))); + int16x4_t s9 = vget_low_s16(vreinterpretq_s16_u16(vmovl_u8(t1))); + int16x4_t sA = vget_low_s16(vreinterpretq_s16_u16(vmovl_u8(t2))); + + int16x8_t s7s8 = vcombine_s16(s7, s8); + int16x8_t s8s9 = vcombine_s16(s8, s9); + int16x8_t s9sA = vcombine_s16(s9, sA); + + s += 11; + + do { + load_u8_8x4(s, src_stride, &t0, &t1, &t2, &t3); + transpose_u8_8x4(&t0, &t1, &t2, &t3); + + int16x4_t sB = vget_low_s16(vreinterpretq_s16_u16(vmovl_u8(t0))); + int16x4_t sC = vget_low_s16(vreinterpretq_s16_u16(vmovl_u8(t1))); + int16x4_t sD = vget_low_s16(vreinterpretq_s16_u16(vmovl_u8(t2))); + int16x4_t sE = vget_low_s16(vreinterpretq_s16_u16(vmovl_u8(t3))); + + int16x8_t sAsB = vcombine_s16(sA, sB); + int16x8_t sBsC = vcombine_s16(sB, sC); + int16x8_t sCsD = vcombine_s16(sC, sD); + int16x8_t sDsE = vcombine_s16(sD, sE); + + uint8x8_t d01 = + convolve12_8(s0s1, s1s2, s2s3, s3s4, s4s5, s5s6, s6s7, s7s8, s8s9, + s9sA, sAsB, sBsC, filter_0_7, filter_8_11); + uint8x8_t d23 = + convolve12_8(s2s3, s3s4, s4s5, s5s6, s6s7, s7s8, s8s9, s9sA, sAsB, + sBsC, sCsD, sDsE, filter_0_7, filter_8_11); + + transpose_u8_4x4(&d01, &d23); + + store_u8(d + 0 * dst_stride, 2 * dst_stride, d01); + store_u8(d + 1 * dst_stride, 2 * dst_stride, d23); + + s0s1 = s4s5; + s1s2 = s5s6; + s2s3 = s6s7; + s3s4 = s7s8; + s4s5 = s8s9; + s5s6 = s9sA; + s6s7 = sAsB; + s7s8 = sBsC; + s8s9 = sCsD; + s9sA = sDsE; + sA = sE; + s += 4; + d += 4; + width -= 4; + } while (width != 0); + src += 4 * src_stride; + dst += 4 * dst_stride; + h -= 4; + } while (h != 0); +} + +void vpx_convolve12_vert_neon(const uint8_t *src, ptrdiff_t src_stride, + uint8_t *dst, ptrdiff_t dst_stride, + const InterpKernel12 *filter, int x0_q4, + int x_step_q4, int y0_q4, int y_step_q4, int w, + int h) { + // Scaling not supported by Neon implementation. + if (y_step_q4 != 16) { + vpx_convolve12_vert_c(src, src_stride, dst, dst_stride, filter, x0_q4, + x_step_q4, y0_q4, y_step_q4, w, h); + return; + } + + assert(w == 32 || w == 16 || w == 8); + assert(h == 32 || h == 16 || h == 8); + + const int16x8_t filter_0_7 = vld1q_s16(filter[y0_q4]); + const int16x4_t filter_8_11 = vld1_s16(filter[y0_q4] + 8); + + src -= src_stride * (MAX_FILTER_TAP / 2 - 1); + + do { + const uint8_t *s = src; + uint8_t *d = dst; + int height = h; + + uint8x8_t t0, t1, t2, t3, t4, t5, t6, t7, t8, t9, tA; + load_u8_8x11(s, src_stride, &t0, &t1, &t2, &t3, &t4, &t5, &t6, &t7, &t8, + &t9, &tA); + int16x8_t s0 = vreinterpretq_s16_u16(vmovl_u8(t0)); + int16x8_t s1 = vreinterpretq_s16_u16(vmovl_u8(t1)); + int16x8_t s2 = vreinterpretq_s16_u16(vmovl_u8(t2)); + int16x8_t s3 = vreinterpretq_s16_u16(vmovl_u8(t3)); + int16x8_t s4 = vreinterpretq_s16_u16(vmovl_u8(t4)); + int16x8_t s5 = vreinterpretq_s16_u16(vmovl_u8(t5)); + int16x8_t s6 = vreinterpretq_s16_u16(vmovl_u8(t6)); + int16x8_t s7 = vreinterpretq_s16_u16(vmovl_u8(t7)); + int16x8_t s8 = vreinterpretq_s16_u16(vmovl_u8(t8)); + int16x8_t s9 = vreinterpretq_s16_u16(vmovl_u8(t9)); + int16x8_t sA = vreinterpretq_s16_u16(vmovl_u8(tA)); + + s += 11 * src_stride; + + do { + uint8x8_t tB, tC, tD, tE; + load_u8_8x4(s, src_stride, &tB, &tC, &tD, &tE); + + int16x8_t sB = vreinterpretq_s16_u16(vmovl_u8(tB)); + int16x8_t sC = vreinterpretq_s16_u16(vmovl_u8(tC)); + int16x8_t sD = vreinterpretq_s16_u16(vmovl_u8(tD)); + int16x8_t sE = vreinterpretq_s16_u16(vmovl_u8(tE)); + + uint8x8_t d0 = convolve12_8(s0, s1, s2, s3, s4, s5, s6, s7, s8, s9, sA, + sB, filter_0_7, filter_8_11); + uint8x8_t d1 = convolve12_8(s1, s2, s3, s4, s5, s6, s7, s8, s9, sA, sB, + sC, filter_0_7, filter_8_11); + uint8x8_t d2 = convolve12_8(s2, s3, s4, s5, s6, s7, s8, s9, sA, sB, sC, + sD, filter_0_7, filter_8_11); + uint8x8_t d3 = convolve12_8(s3, s4, s5, s6, s7, s8, s9, sA, sB, sC, sD, + sE, filter_0_7, filter_8_11); + + store_u8_8x4(d, dst_stride, d0, d1, d2, d3); + + s0 = s4; + s1 = s5; + s2 = s6; + s3 = s7; + s4 = s8; + s5 = s9; + s6 = sA; + s7 = sB; + s8 = sC; + s9 = sD; + sA = sE; + s += 4 * src_stride; + d += 4 * dst_stride; + height -= 4; + } while (height != 0); + src += 8; + dst += 8; + w -= 8; + } while (w != 0); +} + +void vpx_convolve12_neon(const uint8_t *src, ptrdiff_t src_stride, uint8_t *dst, + ptrdiff_t dst_stride, const InterpKernel12 *filter, + int x0_q4, int x_step_q4, int y0_q4, int y_step_q4, + int w, int h) { + // Scaling not supported by Neon implementation. + if (x_step_q4 != 16 || y_step_q4 != 16) { + vpx_convolve12_c(src, src_stride, dst, dst_stride, filter, x0_q4, x_step_q4, + y0_q4, y_step_q4, w, h); + return; + } + + assert(w == 32 || w == 16 || w == 8); + assert(h == 32 || h == 16 || h == 8); + + DECLARE_ALIGNED(32, uint8_t, im_block[BW * (BH + MAX_FILTER_TAP)]); + + const int im_stride = BW; + // Account for the vertical pass needing MAX_FILTER_TAP / 2 - 1 lines prior + // and MAX_FILTER_TAP / 2 lines post. (+1 to make total divisible by 4.) + const int im_height = h + MAX_FILTER_TAP; + const ptrdiff_t border_offset = MAX_FILTER_TAP / 2 - 1; + + // Filter starting border_offset rows up. + vpx_convolve12_horiz_neon(src - src_stride * border_offset, src_stride, + im_block, im_stride, filter, x0_q4, x_step_q4, + y0_q4, y_step_q4, w, im_height); + + vpx_convolve12_vert_neon(im_block + im_stride * border_offset, im_stride, dst, + dst_stride, filter, x0_q4, x_step_q4, y0_q4, + y_step_q4, w, h); +}
diff --git a/vp9/encoder/arm/neon/vp9_temporal_filter_neon_dotprod.c b/vp9/encoder/arm/neon/vp9_temporal_filter_neon_dotprod.c new file mode 100644 index 0000000..5fea75a --- /dev/null +++ b/vp9/encoder/arm/neon/vp9_temporal_filter_neon_dotprod.c
@@ -0,0 +1,392 @@ +/* + * Copyright (c) 2025 The WebM project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include <assert.h> +#include <arm_neon.h> + +#include "./vp9_rtcd.h" +#include "./vpx_config.h" +#include "vpx/vpx_integer.h" +#include "vpx_dsp/arm/mem_neon.h" +#include "vp9/encoder/vp9_temporal_filter.h" + +DECLARE_ALIGNED(16, static const uint8_t, kDotProdPermuteTbl[48]) = { + // clang-format off + 0, 1, 2, 3, 1, 2, 3, 4, 2, 3, 4, 5, 3, 4, 5, 6, + 4, 5, 6, 7, 5, 6, 7, 8, 6, 7, 8, 9, 7, 8, 9, 10, + 8, 9, 10, 11, 9, 10, 11, 12, 10, 11, 12, 13, 11, 12, 13, 14 + // clang-format on +}; + +DECLARE_ALIGNED(16, static const uint8_t, kDotProdMergeBlockTbl[48]) = { + // clang-format off + // Shift left and insert new last column in transposed 4x4 block. + 1, 2, 3, 16, 5, 6, 7, 20, 9, 10, 11, 24, 13, 14, 15, 28, + // Shift left and insert two new columns in transposed 4x4 block. + 2, 3, 16, 17, 6, 7, 20, 21, 10, 11, 24, 25, 14, 15, 28, 29, + // Shift left and insert three new columns in transposed 4x4 block. + 3, 16, 17, 18, 7, 20, 21, 22, 11, 24, 25, 26, 15, 28, 29, 30 + // clang-format on +}; + +static INLINE uint8x8_t convolve12_8_h(uint8x16_t samples[2], + const int8x16_t filter, + const uint8x16x3_t perm_tbl) { + // Transform sample range to [-128, 127] for 8-bit signed dot product. + int8x16_t samples_128[2] = { + vreinterpretq_s8_u8(vsubq_u8(samples[0], vdupq_n_u8(128))), + vreinterpretq_s8_u8(vsubq_u8(samples[1], vdupq_n_u8(128))) + }; + + // Permute samples ready for dot product. + // { 0, 1, 2, 3, 1, 2, 3, 4, 2, 3, 4, 5, 3, 4, 5, 6 } + // { 4, 5, 6, 7, 5, 6, 7, 8, 6, 7, 8, 9, 7, 8, 9, 10 } + // { 8, 9, 10, 11, 9, 10, 11, 12, 10, 11, 12, 13, 11, 12, 13, 14 } + // { 12, 13, 14, 15, 13, 14, 15, 16, 14, 15, 16, 17, 15, 16, 17, 18 } + int8x16_t perm_samples[4] = { vqtbl1q_s8(samples_128[0], perm_tbl.val[0]), + vqtbl1q_s8(samples_128[0], perm_tbl.val[1]), + vqtbl1q_s8(samples_128[0], perm_tbl.val[2]), + vqtbl1q_s8(samples_128[1], perm_tbl.val[2]) }; + + // Accumulate into 128 << FILTER_BITS to account for range transform. + int32x4_t acc = vdupq_n_s32(128 << FILTER_BITS); + + int32x4_t sum0123 = vdotq_laneq_s32(acc, perm_samples[0], filter, 0); + sum0123 = vdotq_laneq_s32(sum0123, perm_samples[1], filter, 1); + sum0123 = vdotq_laneq_s32(sum0123, perm_samples[2], filter, 2); + + int32x4_t sum4567 = vdotq_laneq_s32(acc, perm_samples[1], filter, 0); + sum4567 = vdotq_laneq_s32(sum4567, perm_samples[2], filter, 1); + sum4567 = vdotq_laneq_s32(sum4567, perm_samples[3], filter, 2); + + // Narrow and re-pack. + int16x8_t sum_s16 = vcombine_s16(vqrshrn_n_s32(sum0123, FILTER_BITS), + vqrshrn_n_s32(sum4567, FILTER_BITS)); + return vqmovun_s16(sum_s16); +} + +void vpx_convolve12_horiz_neon_dotprod(const uint8_t *src, ptrdiff_t src_stride, + uint8_t *dst, ptrdiff_t dst_stride, + const InterpKernel12 *filter, int x0_q4, + int x_step_q4, int y0_q4, int y_step_q4, + int w, int h) { + // Scaling not supported by Neon implementation. + if (x_step_q4 != 16) { + vpx_convolve12_horiz_c(src, src_stride, dst, dst_stride, filter, x0_q4, + x_step_q4, y0_q4, y_step_q4, w, h); + return; + } + + assert(w == 32 || w == 16 || w == 8); + assert(h == 32 || h == 16 || h == 8); + + const int16x8_t x_filter_0_7 = vld1q_s16(filter[x0_q4]); + const int16x4_t x_filter_8_11 = vld1_s16(filter[x0_q4] + 8); + const int16x8_t x_filter_8_15 = vcombine_s16(x_filter_8_11, vdup_n_s16(0)); + const int8x16_t x_filter = + vcombine_s8(vmovn_s16(x_filter_0_7), vmovn_s16(x_filter_8_15)); + + const uint8x16x3_t permute_tbl = vld1q_u8_x3(kDotProdPermuteTbl); + + src -= MAX_FILTER_TAP / 2 - 1; + + do { + const uint8_t *s = src; + uint8_t *d = dst; + int width = w; + + do { + uint8x16_t s0[2], s1[2], s2[2], s3[2]; + load_u8_16x4(s, src_stride, &s0[0], &s1[0], &s2[0], &s3[0]); + load_u8_16x4(s + 4, src_stride, &s0[1], &s1[1], &s2[1], &s3[1]); + + uint8x8_t d0 = convolve12_8_h(s0, x_filter, permute_tbl); + uint8x8_t d1 = convolve12_8_h(s1, x_filter, permute_tbl); + uint8x8_t d2 = convolve12_8_h(s2, x_filter, permute_tbl); + uint8x8_t d3 = convolve12_8_h(s3, x_filter, permute_tbl); + + store_u8_8x4(d, dst_stride, d0, d1, d2, d3); + + s += 8; + d += 8; + width -= 8; + } while (width != 0); + src += 4 * src_stride; + dst += 4 * dst_stride; + h -= 4; + } while (h != 0); +} + +static INLINE uint8x8_t convolve12_8_v( + const int8x16_t s0_lo, const int8x16_t s0_hi, const int8x16_t s1_lo, + const int8x16_t s1_hi, const int8x16_t s2_lo, const int8x16_t s2_hi, + const int8x8_t filters_0_7, const int8x8_t filters_4_11) { + // The sample range transform and permutation are performed by the caller. + // Accumulate into 128 << FILTER_BITS to account for range transform. + int32x4_t acc = vdupq_n_s32(128 << FILTER_BITS); + + int32x4_t sum0123 = vdotq_lane_s32(acc, s0_lo, filters_0_7, 0); + sum0123 = vdotq_lane_s32(sum0123, s1_lo, filters_0_7, 1); + sum0123 = vdotq_lane_s32(sum0123, s2_lo, filters_4_11, 1); + + int32x4_t sum4567 = vdotq_lane_s32(acc, s0_hi, filters_0_7, 0); + sum4567 = vdotq_lane_s32(sum4567, s1_hi, filters_0_7, 1); + sum4567 = vdotq_lane_s32(sum4567, s2_hi, filters_4_11, 1); + + // Narrow and re-pack. + int16x8_t sum = vcombine_s16(vqmovn_s32(sum0123), vqmovn_s32(sum4567)); + return vqrshrun_n_s16(sum, FILTER_BITS); +} + +static INLINE void transpose_concat_8x4(int8x8_t a0, int8x8_t a1, int8x8_t a2, + int8x8_t a3, int8x16_t *b0, + int8x16_t *b1) { + // Transpose 8-bit elements and concatenate result rows as follows: + // a0: 00, 01, 02, 03, 04, 05, 06, 07 + // a1: 10, 11, 12, 13, 14, 15, 16, 17 + // a2: 20, 21, 22, 23, 24, 25, 26, 27 + // a3: 30, 31, 32, 33, 34, 35, 36, 37 + // + // b0: 00, 10, 20, 30, 01, 11, 21, 31, 02, 12, 22, 32, 03, 13, 23, 33 + // b1: 04, 14, 24, 34, 05, 15, 25, 35, 06, 16, 26, 36, 07, 17, 27, 37 + + int8x16_t a0q = vcombine_s8(a0, vdup_n_s8(0)); + int8x16_t a1q = vcombine_s8(a1, vdup_n_s8(0)); + int8x16_t a2q = vcombine_s8(a2, vdup_n_s8(0)); + int8x16_t a3q = vcombine_s8(a3, vdup_n_s8(0)); + + int8x16_t a02 = vzipq_s8(a0q, a2q).val[0]; + int8x16_t a13 = vzipq_s8(a1q, a3q).val[0]; + + int8x16x2_t a0123 = vzipq_s8(a02, a13); + + *b0 = a0123.val[0]; + *b1 = a0123.val[1]; +} + +void vpx_convolve12_vert_neon_dotprod(const uint8_t *src, ptrdiff_t src_stride, + uint8_t *dst, ptrdiff_t dst_stride, + const InterpKernel12 *filter, int x0_q4, + int x_step_q4, int y0_q4, int y_step_q4, + int w, int h) { + // Scaling not supported by Neon implementation. + if (y_step_q4 != 16) { + vpx_convolve12_vert_c(src, src_stride, dst, dst_stride, filter, x0_q4, + x_step_q4, y0_q4, y_step_q4, w, h); + return; + } + + assert(w == 32 || w == 16 || w == 8); + assert(h == 32 || h == 16 || h == 8); + + const int8x8_t filter_0_7 = vmovn_s16(vld1q_s16(filter[y0_q4])); + const int8x8_t filter_4_11 = vmovn_s16(vld1q_s16(filter[y0_q4] + 4)); + + const uint8x16x3_t merge_block_tbl = vld1q_u8_x3(kDotProdMergeBlockTbl); + + src -= src_stride * (MAX_FILTER_TAP / 2 - 1); + + do { + int height = h; + const uint8_t *s = src; + uint8_t *d = dst; + + uint8x8_t t0, t1, t2, t3, t4, t5, t6, t7, t8, t9, tA; + load_u8_8x11(s, src_stride, &t0, &t1, &t2, &t3, &t4, &t5, &t6, &t7, &t8, + &t9, &tA); + s += 11 * src_stride; + + // Transform sample range to [-128, 127] for 8-bit signed dot product. + int8x8_t s0 = vreinterpret_s8_u8(vsub_u8(t0, vdup_n_u8(128))); + int8x8_t s1 = vreinterpret_s8_u8(vsub_u8(t1, vdup_n_u8(128))); + int8x8_t s2 = vreinterpret_s8_u8(vsub_u8(t2, vdup_n_u8(128))); + int8x8_t s3 = vreinterpret_s8_u8(vsub_u8(t3, vdup_n_u8(128))); + int8x8_t s4 = vreinterpret_s8_u8(vsub_u8(t4, vdup_n_u8(128))); + int8x8_t s5 = vreinterpret_s8_u8(vsub_u8(t5, vdup_n_u8(128))); + int8x8_t s6 = vreinterpret_s8_u8(vsub_u8(t6, vdup_n_u8(128))); + int8x8_t s7 = vreinterpret_s8_u8(vsub_u8(t7, vdup_n_u8(128))); + int8x8_t s8 = vreinterpret_s8_u8(vsub_u8(t8, vdup_n_u8(128))); + int8x8_t s9 = vreinterpret_s8_u8(vsub_u8(t9, vdup_n_u8(128))); + int8x8_t sA = vreinterpret_s8_u8(vsub_u8(tA, vdup_n_u8(128))); + + // This operation combines a conventional transpose and the sample permute + // (see horizontal case) required before computing the dot product. + int8x16_t s0123_lo, s0123_hi, s1234_lo, s1234_hi, s2345_lo, s2345_hi, + s3456_lo, s3456_hi, s4567_lo, s4567_hi, s5678_lo, s5678_hi, s6789_lo, + s6789_hi, s789A_lo, s789A_hi; + transpose_concat_8x4(s0, s1, s2, s3, &s0123_lo, &s0123_hi); + transpose_concat_8x4(s1, s2, s3, s4, &s1234_lo, &s1234_hi); + transpose_concat_8x4(s2, s3, s4, s5, &s2345_lo, &s2345_hi); + transpose_concat_8x4(s3, s4, s5, s6, &s3456_lo, &s3456_hi); + transpose_concat_8x4(s4, s5, s6, s7, &s4567_lo, &s4567_hi); + transpose_concat_8x4(s5, s6, s7, s8, &s5678_lo, &s5678_hi); + transpose_concat_8x4(s6, s7, s8, s9, &s6789_lo, &s6789_hi); + transpose_concat_8x4(s7, s8, s9, sA, &s789A_lo, &s789A_hi); + + do { + uint8x8_t tB, tC, tD, tE; + load_u8_8x4(s, src_stride, &tB, &tC, &tD, &tE); + + int8x8_t sB = vreinterpret_s8_u8(vsub_u8(tB, vdup_n_u8(128))); + int8x8_t sC = vreinterpret_s8_u8(vsub_u8(tC, vdup_n_u8(128))); + int8x8_t sD = vreinterpret_s8_u8(vsub_u8(tD, vdup_n_u8(128))); + int8x8_t sE = vreinterpret_s8_u8(vsub_u8(tE, vdup_n_u8(128))); + + int8x16_t s89AB_lo, s89AB_hi, s9ABC_lo, s9ABC_hi, sABCD_lo, sABCD_hi, + sBCDE_lo, sBCDE_hi; + transpose_concat_8x4(sB, sC, sD, sE, &sBCDE_lo, &sBCDE_hi); + + // Merge new data into block from previous iteration. + int8x16x2_t samples_LUT_lo = { { s789A_lo, sBCDE_lo } }; + s89AB_lo = vqtbl2q_s8(samples_LUT_lo, merge_block_tbl.val[0]); + s9ABC_lo = vqtbl2q_s8(samples_LUT_lo, merge_block_tbl.val[1]); + sABCD_lo = vqtbl2q_s8(samples_LUT_lo, merge_block_tbl.val[2]); + + int8x16x2_t samples_LUT_hi = { { s789A_hi, sBCDE_hi } }; + s89AB_hi = vqtbl2q_s8(samples_LUT_hi, merge_block_tbl.val[0]); + s9ABC_hi = vqtbl2q_s8(samples_LUT_hi, merge_block_tbl.val[1]); + sABCD_hi = vqtbl2q_s8(samples_LUT_hi, merge_block_tbl.val[2]); + + uint8x8_t d0 = + convolve12_8_v(s0123_lo, s0123_hi, s4567_lo, s4567_hi, s89AB_lo, + s89AB_hi, filter_0_7, filter_4_11); + uint8x8_t d1 = + convolve12_8_v(s1234_lo, s1234_hi, s5678_lo, s5678_hi, s9ABC_lo, + s9ABC_hi, filter_0_7, filter_4_11); + uint8x8_t d2 = + convolve12_8_v(s2345_lo, s2345_hi, s6789_lo, s6789_hi, sABCD_lo, + sABCD_hi, filter_0_7, filter_4_11); + uint8x8_t d3 = + convolve12_8_v(s3456_lo, s3456_hi, s789A_lo, s789A_hi, sBCDE_lo, + sBCDE_hi, filter_0_7, filter_4_11); + + store_u8_8x4(d, dst_stride, d0, d1, d2, d3); + + // Prepare block for next iteration - re-using as much as possible. + // Shuffle everything up four rows. + s0123_lo = s4567_lo; + s0123_hi = s4567_hi; + s1234_lo = s5678_lo; + s1234_hi = s5678_hi; + s2345_lo = s6789_lo; + s2345_hi = s6789_hi; + s3456_lo = s789A_lo; + s3456_hi = s789A_hi; + s4567_lo = s89AB_lo; + s4567_hi = s89AB_hi; + s5678_lo = s9ABC_lo; + s5678_hi = s9ABC_hi; + s6789_lo = sABCD_lo; + s6789_hi = sABCD_hi; + s789A_lo = sBCDE_lo; + s789A_hi = sBCDE_hi; + + s += 4 * src_stride; + d += 4 * dst_stride; + height -= 4; + } while (height != 0); + src += 8; + dst += 8; + w -= 8; + } while (w != 0); +} + +static INLINE void vpx_convolve12_2d_horiz_neon_dotprod( + const uint8_t *src, ptrdiff_t src_stride, uint8_t *dst, + ptrdiff_t dst_stride, const InterpKernel12 *filter, int x0_q4, int w, + int h) { + assert(w == 32 || w == 16 || w == 8); + assert(h % 4 == 3); + + const int16x8_t x_filter_0_7 = vld1q_s16(filter[x0_q4]); + const int16x4_t x_filter_8_11 = vld1_s16(filter[x0_q4] + 8); + const int16x8_t x_filter_8_15 = vcombine_s16(x_filter_8_11, vdup_n_s16(0)); + const int8x16_t x_filter = + vcombine_s8(vmovn_s16(x_filter_0_7), vmovn_s16(x_filter_8_15)); + + const uint8x16x3_t permute_tbl = vld1q_u8_x3(kDotProdPermuteTbl); + + src -= MAX_FILTER_TAP / 2 - 1; + + do { + const uint8_t *s = src; + uint8_t *d = dst; + int width = w; + + do { + uint8x16_t s0[2], s1[2], s2[2], s3[2]; + load_u8_16x4(s, src_stride, &s0[0], &s1[0], &s2[0], &s3[0]); + load_u8_16x4(s + 4, src_stride, &s0[1], &s1[1], &s2[1], &s3[1]); + + uint8x8_t d0 = convolve12_8_h(s0, x_filter, permute_tbl); + uint8x8_t d1 = convolve12_8_h(s1, x_filter, permute_tbl); + uint8x8_t d2 = convolve12_8_h(s2, x_filter, permute_tbl); + uint8x8_t d3 = convolve12_8_h(s3, x_filter, permute_tbl); + + store_u8_8x4(d, dst_stride, d0, d1, d2, d3); + + s += 8; + d += 8; + width -= 8; + } while (width != 0); + src += 4 * src_stride; + dst += 4 * dst_stride; + h -= 4; + } while (h != 3); + + do { + uint8x16_t s0[2], s1[2], s2[2]; + load_u8_16x3(src, src_stride, &s0[0], &s1[0], &s2[0]); + load_u8_16x3(src + 4, src_stride, &s0[1], &s1[1], &s2[1]); + + uint8x8_t d0 = convolve12_8_h(s0, x_filter, permute_tbl); + uint8x8_t d1 = convolve12_8_h(s1, x_filter, permute_tbl); + uint8x8_t d2 = convolve12_8_h(s2, x_filter, permute_tbl); + + store_u8_8x3(dst, dst_stride, d0, d1, d2); + + src += 8; + dst += 8; + w -= 8; + } while (w != 0); +} + +void vpx_convolve12_neon_dotprod(const uint8_t *src, ptrdiff_t src_stride, + uint8_t *dst, ptrdiff_t dst_stride, + const InterpKernel12 *filter, int x0_q4, + int x_step_q4, int y0_q4, int y_step_q4, int w, + int h) { + // Scaling not supported by Neon implementation. + if (x_step_q4 != 16 || y_step_q4 != 16) { + vpx_convolve12_c(src, src_stride, dst, dst_stride, filter, x0_q4, x_step_q4, + y0_q4, y_step_q4, w, h); + return; + } + + assert(w == 32 || w == 16 || w == 8); + assert(h == 32 || h == 16 || h == 8); + + DECLARE_ALIGNED(32, uint8_t, im_block[BW * (BH + MAX_FILTER_TAP)]); + + const int im_stride = BW; + // Account for the vertical pass needing MAX_FILTER_TAP / 2 - 1 lines prior + // and MAX_FILTER_TAP / 2 lines post. + const int im_height = h + MAX_FILTER_TAP - 1; + const ptrdiff_t border_offset = MAX_FILTER_TAP / 2 - 1; + + // Filter starting border_offset rows up. + vpx_convolve12_2d_horiz_neon_dotprod(src - src_stride * border_offset, + src_stride, im_block, im_stride, filter, + x0_q4, w, im_height); + + vpx_convolve12_vert_neon_dotprod(im_block + im_stride * border_offset, + im_stride, dst, dst_stride, filter, x0_q4, + x_step_q4, y0_q4, y_step_q4, w, h); +}
diff --git a/vp9/encoder/arm/neon/vp9_temporal_filter_neon_i8mm.c b/vp9/encoder/arm/neon/vp9_temporal_filter_neon_i8mm.c new file mode 100644 index 0000000..9e1f39b --- /dev/null +++ b/vp9/encoder/arm/neon/vp9_temporal_filter_neon_i8mm.c
@@ -0,0 +1,376 @@ +/* + * Copyright (c) 2025 The WebM project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include <assert.h> +#include <arm_neon.h> + +#include "./vp9_rtcd.h" +#include "./vpx_config.h" +#include "vpx/vpx_integer.h" +#include "vpx_dsp/arm/mem_neon.h" +#include "vp9/encoder/vp9_temporal_filter.h" + +DECLARE_ALIGNED(16, static const uint8_t, kMatMulPermuteTbl[32]) = { + // clang-format off + 0, 1, 2, 3, 4, 5, 6, 7, 2, 3, 4, 5, 6, 7, 8, 9, + 4, 5, 6, 7, 8, 9, 10, 11, 6, 7, 8, 9, 10, 11, 12, 13 + // clang-format on +}; + +DECLARE_ALIGNED(16, static const uint8_t, kDotProdMergeBlockTbl[48]) = { + // clang-format off + // Shift left and insert new last column in transposed 4x4 block. + 1, 2, 3, 16, 5, 6, 7, 20, 9, 10, 11, 24, 13, 14, 15, 28, + // Shift left and insert two new columns in transposed 4x4 block. + 2, 3, 16, 17, 6, 7, 20, 21, 10, 11, 24, 25, 14, 15, 28, 29, + // Shift left and insert three new columns in transposed 4x4 block. + 3, 16, 17, 18, 7, 20, 21, 22, 11, 24, 25, 26, 15, 28, 29, 30 + // clang-format on +}; + +static INLINE uint8x8_t convolve12_8_h(uint8x16_t samples[2], + const int8x16_t filter[2], + const uint8x16x2_t perm_tbl) { + // Permute samples ready for matrix multiply. + // { 0, 1, 2, 3, 4, 5, 6, 7, 2, 3, 4, 5, 6, 7, 8, 9 } + // { 4, 5, 6, 7, 8, 9, 10, 11, 6, 7, 8, 9, 10, 11, 12, 13 } + // { 6, 7, 8, 9, 10, 11, 12, 13, 8, 9, 10, 11, 12, 13, 14, 15 } + // { 10, 11, 12, 13, 14, 15, 16, 17, 12, 13, 14, 15, 16, 17, 18, 19 } + uint8x16_t perm_samples[4] = { vqtbl1q_u8(samples[0], perm_tbl.val[0]), + vqtbl1q_u8(samples[0], perm_tbl.val[1]), + vqtbl1q_u8(samples[1], perm_tbl.val[0]), + vqtbl1q_u8(samples[1], perm_tbl.val[1]) }; + + // These instructions multiply a 2x8 matrix (samples) by an 8x2 matrix + // (filter), destructively accumulating into the destination register. + int32x4_t sum0123 = vusmmlaq_s32(vdupq_n_s32(0), perm_samples[0], filter[0]); + int32x4_t sum4567 = vusmmlaq_s32(vdupq_n_s32(0), perm_samples[1], filter[0]); + sum0123 = vusmmlaq_s32(sum0123, perm_samples[2], filter[1]); + sum4567 = vusmmlaq_s32(sum4567, perm_samples[3], filter[1]); + + // Narrow and re-pack. + int16x8_t sum_s16 = vcombine_s16(vqrshrn_n_s32(sum0123, FILTER_BITS), + vqrshrn_n_s32(sum4567, FILTER_BITS)); + return vqmovun_s16(sum_s16); +} + +void vpx_convolve12_horiz_neon_i8mm(const uint8_t *src, ptrdiff_t src_stride, + uint8_t *dst, ptrdiff_t dst_stride, + const InterpKernel12 *filter, int x0_q4, + int x_step_q4, int y0_q4, int y_step_q4, + int w, int h) { + // Scaling not supported by Neon implementation. + if (x_step_q4 != 16) { + vpx_convolve12_horiz_c(src, src_stride, dst, dst_stride, filter, x0_q4, + x_step_q4, y0_q4, y_step_q4, w, h); + return; + } + + assert(w == 32 || w == 16 || w == 8); + assert(h == 32 || h == 16 || h == 8); + + // Split 12-tap filter into two 6-tap filters, masking the top two elements. + // { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0, 0 } + const int8x8_t mask = vcreate_s8(0x0000ffffffffffff); + const int8x8_t filter_0 = vand_s8(vmovn_s16(vld1q_s16(filter[x0_q4])), mask); + const int8x8_t filter_1 = + vext_s8(vmovn_s16(vld1q_s16(filter[x0_q4] + 4)), vdup_n_s8(0), 2); + + // Stagger each 6-tap filter to enable use of matrix multiply instructions. + // { f0, f1, f2, f3, f4, f5, 0, 0, 0, f0, f1, f2, f3, f4, f5, 0 } + const int8x16_t x_filter[2] = { + vcombine_s8(filter_0, vext_s8(filter_0, filter_0, 7)), + vcombine_s8(filter_1, vext_s8(filter_1, filter_1, 7)) + }; + + const uint8x16x2_t permute_tbl = vld1q_u8_x2(kMatMulPermuteTbl); + + src -= MAX_FILTER_TAP / 2 - 1; + + do { + const uint8_t *s = src; + uint8_t *d = dst; + int width = w; + + do { + uint8x16_t s0[2], s1[2], s2[2], s3[2]; + load_u8_16x4(s, src_stride, &s0[0], &s1[0], &s2[0], &s3[0]); + load_u8_16x4(s + 6, src_stride, &s0[1], &s1[1], &s2[1], &s3[1]); + + uint8x8_t d0 = convolve12_8_h(s0, x_filter, permute_tbl); + uint8x8_t d1 = convolve12_8_h(s1, x_filter, permute_tbl); + uint8x8_t d2 = convolve12_8_h(s2, x_filter, permute_tbl); + uint8x8_t d3 = convolve12_8_h(s3, x_filter, permute_tbl); + + store_u8_8x4(d, dst_stride, d0, d1, d2, d3); + + s += 8; + d += 8; + width -= 8; + } while (width != 0); + src += 4 * src_stride; + dst += 4 * dst_stride; + h -= 4; + } while (h != 0); +} + +static INLINE uint8x8_t convolve12_8_v( + const uint8x16_t s0_lo, const uint8x16_t s0_hi, const uint8x16_t s1_lo, + const uint8x16_t s1_hi, const uint8x16_t s2_lo, const uint8x16_t s2_hi, + const int8x8_t filters_0_7, const int8x8_t filters_4_11) { + // The sample range transform and permutation are performed by the caller. + int32x4_t sum0123 = vusdotq_lane_s32(vdupq_n_s32(0), s0_lo, filters_0_7, 0); + sum0123 = vusdotq_lane_s32(sum0123, s1_lo, filters_0_7, 1); + sum0123 = vusdotq_lane_s32(sum0123, s2_lo, filters_4_11, 1); + + int32x4_t sum4567 = vusdotq_lane_s32(vdupq_n_s32(0), s0_hi, filters_0_7, 0); + sum4567 = vusdotq_lane_s32(sum4567, s1_hi, filters_0_7, 1); + sum4567 = vusdotq_lane_s32(sum4567, s2_hi, filters_4_11, 1); + + // Narrow and re-pack. + int16x8_t sum = vcombine_s16(vqmovn_s32(sum0123), vqmovn_s32(sum4567)); + return vqrshrun_n_s16(sum, FILTER_BITS); +} + +static INLINE void transpose_concat_8x4(uint8x8_t a0, uint8x8_t a1, + uint8x8_t a2, uint8x8_t a3, + uint8x16_t *b0, uint8x16_t *b1) { + // Transpose 8-bit elements and concatenate result rows as follows: + // a0: 00, 01, 02, 03, 04, 05, 06, 07 + // a1: 10, 11, 12, 13, 14, 15, 16, 17 + // a2: 20, 21, 22, 23, 24, 25, 26, 27 + // a3: 30, 31, 32, 33, 34, 35, 36, 37 + // + // b0: 00, 10, 20, 30, 01, 11, 21, 31, 02, 12, 22, 32, 03, 13, 23, 33 + // b1: 04, 14, 24, 34, 05, 15, 25, 35, 06, 16, 26, 36, 07, 17, 27, 37 + + uint8x16_t a0q = vcombine_u8(a0, vdup_n_u8(0)); + uint8x16_t a1q = vcombine_u8(a1, vdup_n_u8(0)); + uint8x16_t a2q = vcombine_u8(a2, vdup_n_u8(0)); + uint8x16_t a3q = vcombine_u8(a3, vdup_n_u8(0)); + + uint8x16_t a02 = vzipq_u8(a0q, a2q).val[0]; + uint8x16_t a13 = vzipq_u8(a1q, a3q).val[0]; + + uint8x16x2_t a0123 = vzipq_u8(a02, a13); + + *b0 = a0123.val[0]; + *b1 = a0123.val[1]; +} + +void vpx_convolve12_vert_neon_i8mm(const uint8_t *src, ptrdiff_t src_stride, + uint8_t *dst, ptrdiff_t dst_stride, + const InterpKernel12 *filter, int x0_q4, + int x_step_q4, int y0_q4, int y_step_q4, + int w, int h) { + // Scaling not supported by Neon implementation. + if (y_step_q4 != 16) { + vpx_convolve12_vert_c(src, src_stride, dst, dst_stride, filter, x0_q4, + x_step_q4, y0_q4, y_step_q4, w, h); + return; + } + + assert(w == 32 || w == 16 || w == 8); + assert(h == 32 || h == 16 || h == 8); + + const int8x8_t filter_0_7 = vmovn_s16(vld1q_s16(filter[y0_q4])); + const int8x8_t filter_4_11 = vmovn_s16(vld1q_s16(filter[y0_q4] + 4)); + + const uint8x16x3_t merge_block_tbl = vld1q_u8_x3(kDotProdMergeBlockTbl); + + src -= src_stride * (MAX_FILTER_TAP / 2 - 1); + + do { + int height = h; + const uint8_t *s = src; + uint8_t *d = dst; + + uint8x8_t s0, s1, s2, s3, s4, s5, s6, s7, s8, s9, sA; + load_u8_8x11(s, src_stride, &s0, &s1, &s2, &s3, &s4, &s5, &s6, &s7, &s8, + &s9, &sA); + s += 11 * src_stride; + + // This operation combines a conventional transpose and the sample permute + // (see horizontal case) required before computing the dot product. + uint8x16_t s0123_lo, s0123_hi, s1234_lo, s1234_hi, s2345_lo, s2345_hi, + s3456_lo, s3456_hi, s4567_lo, s4567_hi, s5678_lo, s5678_hi, s6789_lo, + s6789_hi, s789A_lo, s789A_hi; + transpose_concat_8x4(s0, s1, s2, s3, &s0123_lo, &s0123_hi); + transpose_concat_8x4(s1, s2, s3, s4, &s1234_lo, &s1234_hi); + transpose_concat_8x4(s2, s3, s4, s5, &s2345_lo, &s2345_hi); + transpose_concat_8x4(s3, s4, s5, s6, &s3456_lo, &s3456_hi); + transpose_concat_8x4(s4, s5, s6, s7, &s4567_lo, &s4567_hi); + transpose_concat_8x4(s5, s6, s7, s8, &s5678_lo, &s5678_hi); + transpose_concat_8x4(s6, s7, s8, s9, &s6789_lo, &s6789_hi); + transpose_concat_8x4(s7, s8, s9, sA, &s789A_lo, &s789A_hi); + + do { + uint8x8_t sB, sC, sD, sE; + load_u8_8x4(s, src_stride, &sB, &sC, &sD, &sE); + + uint8x16_t s89AB_lo, s89AB_hi, s9ABC_lo, s9ABC_hi, sABCD_lo, sABCD_hi, + sBCDE_lo, sBCDE_hi; + transpose_concat_8x4(sB, sC, sD, sE, &sBCDE_lo, &sBCDE_hi); + + // Merge new data into block from previous iteration. + uint8x16x2_t samples_LUT_lo = { { s789A_lo, sBCDE_lo } }; + s89AB_lo = vqtbl2q_u8(samples_LUT_lo, merge_block_tbl.val[0]); + s9ABC_lo = vqtbl2q_u8(samples_LUT_lo, merge_block_tbl.val[1]); + sABCD_lo = vqtbl2q_u8(samples_LUT_lo, merge_block_tbl.val[2]); + + uint8x16x2_t samples_LUT_hi = { { s789A_hi, sBCDE_hi } }; + s89AB_hi = vqtbl2q_u8(samples_LUT_hi, merge_block_tbl.val[0]); + s9ABC_hi = vqtbl2q_u8(samples_LUT_hi, merge_block_tbl.val[1]); + sABCD_hi = vqtbl2q_u8(samples_LUT_hi, merge_block_tbl.val[2]); + + uint8x8_t d0 = + convolve12_8_v(s0123_lo, s0123_hi, s4567_lo, s4567_hi, s89AB_lo, + s89AB_hi, filter_0_7, filter_4_11); + uint8x8_t d1 = + convolve12_8_v(s1234_lo, s1234_hi, s5678_lo, s5678_hi, s9ABC_lo, + s9ABC_hi, filter_0_7, filter_4_11); + uint8x8_t d2 = + convolve12_8_v(s2345_lo, s2345_hi, s6789_lo, s6789_hi, sABCD_lo, + sABCD_hi, filter_0_7, filter_4_11); + uint8x8_t d3 = + convolve12_8_v(s3456_lo, s3456_hi, s789A_lo, s789A_hi, sBCDE_lo, + sBCDE_hi, filter_0_7, filter_4_11); + + store_u8_8x4(d, dst_stride, d0, d1, d2, d3); + + // Prepare block for next iteration - re-using as much as possible. + // Shuffle everything up four rows. + s0123_lo = s4567_lo; + s0123_hi = s4567_hi; + s1234_lo = s5678_lo; + s1234_hi = s5678_hi; + s2345_lo = s6789_lo; + s2345_hi = s6789_hi; + s3456_lo = s789A_lo; + s3456_hi = s789A_hi; + s4567_lo = s89AB_lo; + s4567_hi = s89AB_hi; + s5678_lo = s9ABC_lo; + s5678_hi = s9ABC_hi; + s6789_lo = sABCD_lo; + s6789_hi = sABCD_hi; + s789A_lo = sBCDE_lo; + s789A_hi = sBCDE_hi; + + s += 4 * src_stride; + d += 4 * dst_stride; + height -= 4; + } while (height != 0); + src += 8; + dst += 8; + w -= 8; + } while (w != 0); +} + +static INLINE void vpx_convolve12_2d_horiz_neon_i8mm( + const uint8_t *src, ptrdiff_t src_stride, uint8_t *dst, + ptrdiff_t dst_stride, const InterpKernel12 *filter, int x0_q4, int w, + int h) { + assert(w == 32 || w == 16 || w == 8); + assert(h % 4 == 3); + + // Split 12-tap filter into two 6-tap filters, masking the top two elements. + // { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0, 0 } + const int8x8_t mask = vcreate_s8(0x0000ffffffffffff); + const int8x8_t filter_0 = vand_s8(vmovn_s16(vld1q_s16(filter[x0_q4])), mask); + const int8x8_t filter_1 = + vext_s8(vmovn_s16(vld1q_s16(filter[x0_q4] + 4)), vdup_n_s8(0), 2); + + // Stagger each 6-tap filter to enable use of matrix multiply instructions. + // { f0, f1, f2, f3, f4, f5, 0, 0, 0, f0, f1, f2, f3, f4, f5, 0 } + const int8x16_t x_filter[2] = { + vcombine_s8(filter_0, vext_s8(filter_0, filter_0, 7)), + vcombine_s8(filter_1, vext_s8(filter_1, filter_1, 7)) + }; + + const uint8x16x2_t permute_tbl = vld1q_u8_x2(kMatMulPermuteTbl); + + src -= MAX_FILTER_TAP / 2 - 1; + + do { + const uint8_t *s = src; + uint8_t *d = dst; + int width = w; + + do { + uint8x16_t s0[2], s1[2], s2[2], s3[2]; + load_u8_16x4(s, src_stride, &s0[0], &s1[0], &s2[0], &s3[0]); + load_u8_16x4(s + 6, src_stride, &s0[1], &s1[1], &s2[1], &s3[1]); + + uint8x8_t d0 = convolve12_8_h(s0, x_filter, permute_tbl); + uint8x8_t d1 = convolve12_8_h(s1, x_filter, permute_tbl); + uint8x8_t d2 = convolve12_8_h(s2, x_filter, permute_tbl); + uint8x8_t d3 = convolve12_8_h(s3, x_filter, permute_tbl); + + store_u8_8x4(d, dst_stride, d0, d1, d2, d3); + + s += 8; + d += 8; + width -= 8; + } while (width != 0); + src += 4 * src_stride; + dst += 4 * dst_stride; + h -= 4; + } while (h != 3); + + do { + uint8x16_t s0[2], s1[2], s2[2]; + load_u8_16x3(src, src_stride, &s0[0], &s1[0], &s2[0]); + load_u8_16x3(src + 6, src_stride, &s0[1], &s1[1], &s2[1]); + + uint8x8_t d0 = convolve12_8_h(s0, x_filter, permute_tbl); + uint8x8_t d1 = convolve12_8_h(s1, x_filter, permute_tbl); + uint8x8_t d2 = convolve12_8_h(s2, x_filter, permute_tbl); + + store_u8_8x3(dst, dst_stride, d0, d1, d2); + + src += 8; + dst += 8; + w -= 8; + } while (w != 0); +} + +void vpx_convolve12_neon_i8mm(const uint8_t *src, ptrdiff_t src_stride, + uint8_t *dst, ptrdiff_t dst_stride, + const InterpKernel12 *filter, int x0_q4, + int x_step_q4, int y0_q4, int y_step_q4, int w, + int h) { + // Scaling not supported by Neon implementation. + if (x_step_q4 != 16 || y_step_q4 != 16) { + vpx_convolve12_c(src, src_stride, dst, dst_stride, filter, x0_q4, x_step_q4, + y0_q4, y_step_q4, w, h); + return; + } + + assert(w == 32 || w == 16 || w == 8); + assert(h == 32 || h == 16 || h == 8); + + DECLARE_ALIGNED(32, uint8_t, im_block[BW * (BH + MAX_FILTER_TAP)]); + + const int im_stride = BW; + // Account for the vertical pass needing MAX_FILTER_TAP / 2 - 1 lines prior + // and MAX_FILTER_TAP / 2 lines post. + const int im_height = h + MAX_FILTER_TAP - 1; + const ptrdiff_t border_offset = MAX_FILTER_TAP / 2 - 1; + + // Filter starting border_offset rows up. + vpx_convolve12_2d_horiz_neon_i8mm(src - src_stride * border_offset, + src_stride, im_block, im_stride, filter, + x0_q4, w, im_height); + + vpx_convolve12_vert_neon_i8mm(im_block + im_stride * border_offset, im_stride, + dst, dst_stride, filter, x0_q4, x_step_q4, + y0_q4, y_step_q4, w, h); +}
diff --git a/vp9/encoder/vp9_aq_cyclicrefresh.c b/vp9/encoder/vp9_aq_cyclicrefresh.c index 109ba0c..92a31eb 100644 --- a/vp9/encoder/vp9_aq_cyclicrefresh.c +++ b/vp9/encoder/vp9_aq_cyclicrefresh.c
@@ -603,14 +603,16 @@ (cpi->use_svc && cpi->svc.high_source_sad_superframe); if (cm->current_video_frame == 0) cr->low_content_avg = 0.0; // Reset if resoluton change has occurred. - if (cpi->resize_pending != 0) vp9_cyclic_refresh_reset_resize(cpi); + if (cpi->resize_pending != 0 && cpi->svc.temporal_layer_id == 0) + vp9_cyclic_refresh_reset_resize(cpi); if (!cr->apply_cyclic_refresh || (cpi->force_update_segmentation) || scene_change_detected) { // Set segmentation map to 0 and disable. unsigned char *const seg_map = cpi->segmentation_map; memset(seg_map, 0, cm->mi_rows * cm->mi_cols); vp9_disable_segmentation(&cm->seg); - if (cm->frame_type == KEY_FRAME || scene_change_detected) { + if ((cm->frame_type == KEY_FRAME || scene_change_detected) && + cpi->svc.temporal_layer_id == 0) { memset(cr->last_coded_q_map, MAXQ, cm->mi_rows * cm->mi_cols * sizeof(*cr->last_coded_q_map)); cr->sb_index = 0;
diff --git a/vp9/encoder/vp9_bitstream.c b/vp9/encoder/vp9_bitstream.c index be34271..0be2b1f 100644 --- a/vp9/encoder/vp9_bitstream.c +++ b/vp9/encoder/vp9_bitstream.c
@@ -1249,6 +1249,7 @@ } } else { assert(cm->profile == PROFILE_1 || cm->profile == PROFILE_3); + assert(cm->subsampling_x == 0 && cm->subsampling_y == 0); vpx_wb_write_bit(wb, 0); // unused } }
diff --git a/vp9/encoder/vp9_encodeframe.c b/vp9/encoder/vp9_encodeframe.c index 937f22e..4c991e3 100644 --- a/vp9/encoder/vp9_encodeframe.c +++ b/vp9/encoder/vp9_encodeframe.c
@@ -965,6 +965,10 @@ return 0; } +// Set the partition for mi_col/row_high (current resolution) based on +// the previous spatial layer (mi_col/row). Returns 0 if partition is set, +// returns 1 if no scale partitioning is done. Return 1 means the variance +// partitioning will be used. static int scale_partitioning_svc(VP9_COMP *cpi, MACROBLOCK *x, MACROBLOCKD *xd, BLOCK_SIZE bsize, int mi_row, int mi_col, int mi_row_high, int mi_col_high) { @@ -974,31 +978,29 @@ // Variables with _high are for higher resolution. int bsize_high = 0; int subsize_high = 0; - const int bsl_high = b_width_log2_lookup[bsize]; - const int bs_high = (1 << bsl_high) >> 2; - const int has_rows = (mi_row_high + bs_high) < cm->mi_rows; - const int has_cols = (mi_col_high + bs_high) < cm->mi_cols; + const int bsl = b_width_log2_lookup[bsize]; + const int bs = (1 << bsl) >> 2; + const int has_rows = (mi_row_high + bs) < cm->mi_rows; + const int has_cols = (mi_col_high + bs) < cm->mi_cols; - const int row_boundary_block_scale_factor[BLOCK_SIZES] = { 13, 13, 13, 1, 0, - 1, 1, 0, 1, 1, - 0, 1, 0 }; - const int col_boundary_block_scale_factor[BLOCK_SIZES] = { 13, 13, 13, 2, 2, - 0, 2, 2, 0, 2, - 2, 0, 0 }; int start_pos; BLOCK_SIZE bsize_low; PARTITION_TYPE partition_high; - if (mi_row_high >= cm->mi_rows || mi_col_high >= cm->mi_cols) return 0; + // If the lower layer frame is outside the boundary (this can happen for + // odd size resolutions) then do not scale partitioning from the lower + // layer. Do variance based partitioning instead (return 1). if (mi_row >= svc->mi_rows[svc->spatial_layer_id - 1] || mi_col >= svc->mi_cols[svc->spatial_layer_id - 1]) - return 0; + return 1; + + // Do not scale partitioning from lower layers on the boundary. Do + // variance based partitioning instead (return 1). + if (!has_rows || !has_cols) return 1; // Find corresponding (mi_col/mi_row) block down-scaled by 2x2. start_pos = mi_row * (svc->mi_stride[svc->spatial_layer_id - 1]) + mi_col; bsize_low = prev_part[start_pos]; - // The block size is too big for boundaries. Do variance based partitioning. - if ((!has_rows || !has_cols) && bsize_low > BLOCK_16X16) return 1; // For reference frames: return 1 (do variance-based partitioning) if the // superblock is not low source sad and lower-resoln bsize is below 32x32. @@ -1012,23 +1014,13 @@ } else if (bsize_low >= BLOCK_32X32) { bsize_high = BLOCK_64X64; } - // Scale up blocks on boundary. - if (!has_cols && has_rows) { - bsize_high = bsize_low + row_boundary_block_scale_factor[bsize_low]; - } else if (has_cols && !has_rows) { - bsize_high = bsize_low + col_boundary_block_scale_factor[bsize_low]; - } else if (!has_cols && !has_rows) { - bsize_high = bsize_low; - } - partition_high = partition_lookup[bsl_high][bsize_high]; + partition_high = partition_lookup[bsl][bsize_high]; subsize_high = get_subsize(bsize, partition_high); if (subsize_high < BLOCK_8X8) { set_block_size(cpi, x, xd, mi_row_high, mi_col_high, bsize_high); } else { - const int bsl = b_width_log2_lookup[bsize]; - const int bs = (1 << bsl) >> 2; switch (partition_high) { case PARTITION_NONE: set_block_size(cpi, x, xd, mi_row_high, mi_col_high, bsize_high); @@ -1036,13 +1028,13 @@ case PARTITION_HORZ: set_block_size(cpi, x, xd, mi_row_high, mi_col_high, subsize_high); if (subsize_high < BLOCK_64X64) - set_block_size(cpi, x, xd, mi_row_high + bs_high, mi_col_high, + set_block_size(cpi, x, xd, mi_row_high + bs, mi_col_high, subsize_high); break; case PARTITION_VERT: set_block_size(cpi, x, xd, mi_row_high, mi_col_high, subsize_high); if (subsize_high < BLOCK_64X64) - set_block_size(cpi, x, xd, mi_row_high, mi_col_high + bs_high, + set_block_size(cpi, x, xd, mi_row_high, mi_col_high + bs, subsize_high); break; default: @@ -1051,15 +1043,15 @@ mi_row_high, mi_col_high)) return 1; if (scale_partitioning_svc(cpi, x, xd, subsize_high, mi_row + (bs >> 1), - mi_col, mi_row_high + bs_high, mi_col_high)) + mi_col, mi_row_high + bs, mi_col_high)) return 1; if (scale_partitioning_svc(cpi, x, xd, subsize_high, mi_row, mi_col + (bs >> 1), mi_row_high, - mi_col_high + bs_high)) + mi_col_high + bs)) return 1; if (scale_partitioning_svc(cpi, x, xd, subsize_high, mi_row + (bs >> 1), - mi_col + (bs >> 1), mi_row_high + bs_high, - mi_col_high + bs_high)) + mi_col + (bs >> 1), mi_row_high + bs, + mi_col_high + bs)) return 1; break; } @@ -3741,6 +3733,17 @@ if (cpi->twopass.gf_group.layer_depth[gf_group_index] > 1) return orig_rdmult; + if (cpi->ext_ratectrl.ready && + (cpi->ext_ratectrl.funcs.rc_type & VPX_RC_QP) != 0 && + cpi->ext_ratectrl.funcs.get_encodeframe_decision != NULL) { + int sb_size = num_8x8_blocks_wide_lookup[BLOCK_64X64] * MI_SIZE; + int sb_stride = (cpi->common.width + sb_size - 1) / sb_size; + int sby = mi_row / 8; + int sbx = mi_col / 8; + return (int)((cpi->sb_mul_scale[sby * sb_stride + sbx] * orig_rdmult) / + 256); + } + for (row = mi_row; row < mi_row + mi_high; ++row) { for (col = mi_col; col < mi_col + mi_wide; ++col) { TplDepStats *this_stats = &tpl_stats[row * tpl_stride + col]; @@ -3759,225 +3762,13 @@ beta = r0 / rk; dr = vp9_get_adaptive_rdmult(cpi, beta); - dr = VPXMIN(dr, orig_rdmult * 3 / 2); - dr = VPXMAX(dr, orig_rdmult * 1 / 2); - + dr = clamp(dr, orig_rdmult * 1 / 2, orig_rdmult * 3 / 2); dr = VPXMAX(1, dr); return dr; } #endif // !CONFIG_REALTIME_ONLY -#if CONFIG_RATE_CTRL -static void assign_partition_info( - const int row_start_4x4, const int col_start_4x4, const int block_width_4x4, - const int block_height_4x4, const int num_unit_rows, - const int num_unit_cols, PARTITION_INFO *partition_info) { - int i, j; - for (i = 0; i < block_height_4x4; ++i) { - for (j = 0; j < block_width_4x4; ++j) { - const int row_4x4 = row_start_4x4 + i; - const int col_4x4 = col_start_4x4 + j; - const int unit_index = row_4x4 * num_unit_cols + col_4x4; - if (row_4x4 >= num_unit_rows || col_4x4 >= num_unit_cols) continue; - partition_info[unit_index].row = row_4x4 << 2; - partition_info[unit_index].column = col_4x4 << 2; - partition_info[unit_index].row_start = row_start_4x4 << 2; - partition_info[unit_index].column_start = col_start_4x4 << 2; - partition_info[unit_index].width = block_width_4x4 << 2; - partition_info[unit_index].height = block_height_4x4 << 2; - } - } -} - -static void assign_motion_vector_info(const int block_width_4x4, - const int block_height_4x4, - const int row_start_4x4, - const int col_start_4x4, - const int num_unit_rows, - const int num_unit_cols, MV *source_mv[2], - MV_REFERENCE_FRAME source_ref_frame[2], - MOTION_VECTOR_INFO *motion_vector_info) { - int i, j; - for (i = 0; i < block_height_4x4; ++i) { - for (j = 0; j < block_width_4x4; ++j) { - const int row_4x4 = row_start_4x4 + i; - const int col_4x4 = col_start_4x4 + j; - const int unit_index = row_4x4 * num_unit_cols + col_4x4; - if (row_4x4 >= num_unit_rows || col_4x4 >= num_unit_cols) continue; - if (source_ref_frame[1] == NO_REF_FRAME) { - assert(source_mv[1]->row == 0 && source_mv[1]->col == 0); - } - motion_vector_info[unit_index].ref_frame[0] = source_ref_frame[0]; - motion_vector_info[unit_index].ref_frame[1] = source_ref_frame[1]; - motion_vector_info[unit_index].mv[0].as_mv.row = source_mv[0]->row; - motion_vector_info[unit_index].mv[0].as_mv.col = source_mv[0]->col; - motion_vector_info[unit_index].mv[1].as_mv.row = source_mv[1]->row; - motion_vector_info[unit_index].mv[1].as_mv.col = source_mv[1]->col; - } - } -} - -static void store_superblock_info( - const PC_TREE *const pc_tree, MODE_INFO **mi_grid_visible, - const int mi_stride, const int square_size_4x4, const int num_unit_rows, - const int num_unit_cols, const int row_start_4x4, const int col_start_4x4, - PARTITION_INFO *partition_info, MOTION_VECTOR_INFO *motion_vector_info) { - const int subblock_square_size_4x4 = square_size_4x4 >> 1; - if (row_start_4x4 >= num_unit_rows || col_start_4x4 >= num_unit_cols) return; - assert(pc_tree->partitioning != PARTITION_INVALID); - // End node, no split. - if (pc_tree->partitioning == PARTITION_NONE || - pc_tree->partitioning == PARTITION_HORZ || - pc_tree->partitioning == PARTITION_VERT || square_size_4x4 == 1) { - const int mi_row = row_start_4x4 >> 1; - const int mi_col = col_start_4x4 >> 1; - const int mi_idx = mi_stride * mi_row + mi_col; - MODE_INFO **mi = mi_grid_visible + mi_idx; - MV *source_mv[2]; - MV_REFERENCE_FRAME source_ref_frame[2]; - - // partition info - const int block_width_4x4 = (pc_tree->partitioning == PARTITION_VERT) - ? square_size_4x4 >> 1 - : square_size_4x4; - const int block_height_4x4 = (pc_tree->partitioning == PARTITION_HORZ) - ? square_size_4x4 >> 1 - : square_size_4x4; - assign_partition_info(row_start_4x4, col_start_4x4, block_width_4x4, - block_height_4x4, num_unit_rows, num_unit_cols, - partition_info); - if (pc_tree->partitioning == PARTITION_VERT) { - assign_partition_info(row_start_4x4, col_start_4x4 + block_width_4x4, - block_width_4x4, block_height_4x4, num_unit_rows, - num_unit_cols, partition_info); - } else if (pc_tree->partitioning == PARTITION_HORZ) { - assign_partition_info(row_start_4x4 + block_height_4x4, col_start_4x4, - block_width_4x4, block_height_4x4, num_unit_rows, - num_unit_cols, partition_info); - } - - // motion vector info - if (pc_tree->partitioning == PARTITION_HORZ) { - int is_valid_second_rectangle = 0; - assert(square_size_4x4 > 1); - // First rectangle. - source_ref_frame[0] = mi[0]->ref_frame[0]; - source_ref_frame[1] = mi[0]->ref_frame[1]; - source_mv[0] = &mi[0]->mv[0].as_mv; - source_mv[1] = &mi[0]->mv[1].as_mv; - assign_motion_vector_info(block_width_4x4, block_height_4x4, - row_start_4x4, col_start_4x4, num_unit_rows, - num_unit_cols, source_mv, source_ref_frame, - motion_vector_info); - // Second rectangle. - if (square_size_4x4 == 2) { - is_valid_second_rectangle = 1; - source_ref_frame[0] = mi[0]->ref_frame[0]; - source_ref_frame[1] = mi[0]->ref_frame[1]; - source_mv[0] = &mi[0]->bmi[2].as_mv[0].as_mv; - source_mv[1] = &mi[0]->bmi[2].as_mv[1].as_mv; - } else { - const int mi_row_2 = mi_row + (block_height_4x4 >> 1); - const int mi_col_2 = mi_col; - if (mi_row_2 * 2 < num_unit_rows && mi_col_2 * 2 < num_unit_cols) { - const int mi_idx_2 = mi_stride * mi_row_2 + mi_col_2; - is_valid_second_rectangle = 1; - mi = mi_grid_visible + mi_idx_2; - source_ref_frame[0] = mi[0]->ref_frame[0]; - source_ref_frame[1] = mi[0]->ref_frame[1]; - source_mv[0] = &mi[0]->mv[0].as_mv; - source_mv[1] = &mi[0]->mv[1].as_mv; - } - } - if (is_valid_second_rectangle) { - assign_motion_vector_info( - block_width_4x4, block_height_4x4, row_start_4x4 + block_height_4x4, - col_start_4x4, num_unit_rows, num_unit_cols, source_mv, - source_ref_frame, motion_vector_info); - } - } else if (pc_tree->partitioning == PARTITION_VERT) { - int is_valid_second_rectangle = 0; - assert(square_size_4x4 > 1); - // First rectangle. - source_ref_frame[0] = mi[0]->ref_frame[0]; - source_ref_frame[1] = mi[0]->ref_frame[1]; - source_mv[0] = &mi[0]->mv[0].as_mv; - source_mv[1] = &mi[0]->mv[1].as_mv; - assign_motion_vector_info(block_width_4x4, block_height_4x4, - row_start_4x4, col_start_4x4, num_unit_rows, - num_unit_cols, source_mv, source_ref_frame, - motion_vector_info); - // Second rectangle. - if (square_size_4x4 == 2) { - is_valid_second_rectangle = 1; - source_ref_frame[0] = mi[0]->ref_frame[0]; - source_ref_frame[1] = mi[0]->ref_frame[1]; - source_mv[0] = &mi[0]->bmi[1].as_mv[0].as_mv; - source_mv[1] = &mi[0]->bmi[1].as_mv[1].as_mv; - } else { - const int mi_row_2 = mi_row; - const int mi_col_2 = mi_col + (block_width_4x4 >> 1); - if (mi_row_2 * 2 < num_unit_rows && mi_col_2 * 2 < num_unit_cols) { - const int mi_idx_2 = mi_stride * mi_row_2 + mi_col_2; - is_valid_second_rectangle = 1; - mi = mi_grid_visible + mi_idx_2; - source_ref_frame[0] = mi[0]->ref_frame[0]; - source_ref_frame[1] = mi[0]->ref_frame[1]; - source_mv[0] = &mi[0]->mv[0].as_mv; - source_mv[1] = &mi[0]->mv[1].as_mv; - } - } - if (is_valid_second_rectangle) { - assign_motion_vector_info( - block_width_4x4, block_height_4x4, row_start_4x4, - col_start_4x4 + block_width_4x4, num_unit_rows, num_unit_cols, - source_mv, source_ref_frame, motion_vector_info); - } - } else { - assert(pc_tree->partitioning == PARTITION_NONE || square_size_4x4 == 1); - source_ref_frame[0] = mi[0]->ref_frame[0]; - source_ref_frame[1] = mi[0]->ref_frame[1]; - if (square_size_4x4 == 1) { - const int sub8x8_row = row_start_4x4 % 2; - const int sub8x8_col = col_start_4x4 % 2; - const int sub8x8_idx = sub8x8_row * 2 + sub8x8_col; - source_mv[0] = &mi[0]->bmi[sub8x8_idx].as_mv[0].as_mv; - source_mv[1] = &mi[0]->bmi[sub8x8_idx].as_mv[1].as_mv; - } else { - source_mv[0] = &mi[0]->mv[0].as_mv; - source_mv[1] = &mi[0]->mv[1].as_mv; - } - assign_motion_vector_info(block_width_4x4, block_height_4x4, - row_start_4x4, col_start_4x4, num_unit_rows, - num_unit_cols, source_mv, source_ref_frame, - motion_vector_info); - } - - return; - } - // recursively traverse partition tree when partition is split. - assert(pc_tree->partitioning == PARTITION_SPLIT); - store_superblock_info(pc_tree->u.split[0], mi_grid_visible, mi_stride, - subblock_square_size_4x4, num_unit_rows, num_unit_cols, - row_start_4x4, col_start_4x4, partition_info, - motion_vector_info); - store_superblock_info(pc_tree->u.split[1], mi_grid_visible, mi_stride, - subblock_square_size_4x4, num_unit_rows, num_unit_cols, - row_start_4x4, col_start_4x4 + subblock_square_size_4x4, - partition_info, motion_vector_info); - store_superblock_info(pc_tree->u.split[2], mi_grid_visible, mi_stride, - subblock_square_size_4x4, num_unit_rows, num_unit_cols, - row_start_4x4 + subblock_square_size_4x4, col_start_4x4, - partition_info, motion_vector_info); - store_superblock_info(pc_tree->u.split[3], mi_grid_visible, mi_stride, - subblock_square_size_4x4, num_unit_rows, num_unit_cols, - row_start_4x4 + subblock_square_size_4x4, - col_start_4x4 + subblock_square_size_4x4, - partition_info, motion_vector_info); -} -#endif // CONFIG_RATE_CTRL - #if !CONFIG_REALTIME_ONLY // TODO(jingning,jimbankoski,rbultje): properly skip partition types that are // unlikely to be selected depending on previous rate-distortion optimization @@ -4460,21 +4251,6 @@ #if CONFIG_COLLECT_COMPONENT_TIMING end_timing(cpi, encode_sb_time); #endif -#if CONFIG_RATE_CTRL - if (oxcf->use_simple_encode_api) { - // Store partition, motion vector of the superblock. - if (output_enabled) { - const int num_unit_rows = - get_num_unit_4x4(cpi->frame_info.frame_height); - const int num_unit_cols = get_num_unit_4x4(cpi->frame_info.frame_width); - store_superblock_info(pc_tree, cm->mi_grid_visible, cm->mi_stride, - num_4x4_blocks_wide_lookup[BLOCK_64X64], - num_unit_rows, num_unit_cols, mi_row << 1, - mi_col << 1, cpi->partition_info, - cpi->motion_vector_info); - } - } -#endif // CONFIG_RATE_CTRL } if (bsize == BLOCK_64X64) { @@ -6176,8 +5952,10 @@ if (cpi->oxcf.aq_mode == PERCEPTUAL_AQ) build_kmeans_segmentation(cpi); { +#if CONFIG_INTERNAL_STATS struct vpx_usec_timer emr_timer; vpx_usec_timer_start(&emr_timer); +#endif if (!cpi->row_mt) { cpi->row_mt_sync_read_ptr = vp9_row_mt_sync_read_dummy; @@ -6194,8 +5972,10 @@ vp9_encode_tiles_row_mt(cpi); } +#if CONFIG_INTERNAL_STATS vpx_usec_timer_mark(&emr_timer); cpi->time_encode_sb_row += vpx_usec_timer_elapsed(&emr_timer); +#endif } sf->skip_encode_frame =
diff --git a/vp9/encoder/vp9_encoder.c b/vp9/encoder/vp9_encoder.c index 59179fa..9b22ecb 100644 --- a/vp9/encoder/vp9_encoder.c +++ b/vp9/encoder/vp9_encoder.c
@@ -78,6 +78,7 @@ #include "vp9/encoder/vp9_multi_thread.h" #include "vp9/encoder/vp9_noise_estimate.h" #include "vp9/encoder/vp9_picklpf.h" +#include "vp9/encoder/vp9_quantize.h" #include "vp9/encoder/vp9_ratectrl.h" #include "vp9/encoder/vp9_rd.h" #include "vp9/encoder/vp9_resize.h" @@ -1021,18 +1022,12 @@ vpx_free(cpi->mb_wiener_variance); cpi->mb_wiener_variance = NULL; + vpx_free(cpi->sb_mul_scale); + cpi->sb_mul_scale = NULL; + vpx_free(cpi->mi_ssim_rdmult_scaling_factors); cpi->mi_ssim_rdmult_scaling_factors = NULL; -#if CONFIG_RATE_CTRL - if (cpi->oxcf.use_simple_encode_api) { - free_partition_info(cpi); - free_motion_vector_info(cpi); - free_fp_motion_vector_info(cpi); - free_tpl_stats_info(cpi); - } -#endif - vp9_free_ref_frame_buffers(cm->buffer_pool); #if CONFIG_VP9_POSTPROC vp9_free_postproc_buffers(cm); @@ -2065,6 +2060,24 @@ cpi->copied_frame_cnt = NULL; } +#if CONFIG_VP9_TEMPORAL_DENOISING +static void setup_denoiser_buffer(VP9_COMP *cpi) { + VP9_COMMON *const cm = &cpi->common; + if (cpi->oxcf.noise_sensitivity > 0 && + !cpi->denoiser.frame_buffer_initialized) { + if (vp9_denoiser_alloc(cm, &cpi->svc, &cpi->denoiser, cpi->use_svc, + cpi->oxcf.noise_sensitivity, cm->width, cm->height, + cm->subsampling_x, cm->subsampling_y, +#if CONFIG_VP9_HIGHBITDEPTH + cm->use_highbitdepth, +#endif + VP9_ENC_BORDER_IN_PIXELS)) + vpx_internal_error(&cm->error, VPX_CODEC_MEM_ERROR, + "Failed to allocate denoiser"); + } +} +#endif + void vp9_change_config(struct VP9_COMP *cpi, const VP9EncoderConfig *oxcf) { VP9_COMMON *const cm = &cpi->common; RATE_CONTROL *const rc = &cpi->rc; @@ -2172,9 +2185,46 @@ &cm->error, cpi->skin_map, vpx_calloc(cm->mi_rows * cm->mi_cols, sizeof(*cpi->skin_map))); + if (cpi->svc.number_spatial_layers > 1) { +#if CONFIG_VP9_TEMPORAL_DENOISING + // Reset the denoiser for svc on the resize change. + if (cpi->oxcf.noise_sensitivity > 0) { + vp9_denoiser_free(&cpi->denoiser); + setup_denoiser_buffer(cpi); + } +#endif + if (cpi->oxcf.aq_mode == CYCLIC_REFRESH_AQ) { + for (int sl = 0; sl < cpi->svc.number_spatial_layers; ++sl) { + const int layer = + LAYER_IDS_TO_IDX(sl, 0, cpi->svc.number_temporal_layers); + LAYER_CONTEXT *const lc = &cpi->svc.layer_context[layer]; + lc->sb_index = 0; + lc->actual_num_seg1_blocks = 0; + lc->actual_num_seg2_blocks = 0; + lc->counter_encode_maxq_scene_change = 0; + vpx_free(lc->map); + CHECK_MEM_ERROR( + &cm->error, lc->map, + vpx_calloc(cm->mi_rows * cm->mi_cols, sizeof(*lc->map))); + vpx_free(lc->last_coded_q_map); + CHECK_MEM_ERROR(&cm->error, lc->last_coded_q_map, + vpx_malloc(cm->mi_rows * cm->mi_cols * + sizeof(*lc->last_coded_q_map))); + memset(lc->last_coded_q_map, MAXQ, cm->mi_rows * cm->mi_cols); + vpx_free(lc->consec_zero_mv); + CHECK_MEM_ERROR(&cm->error, lc->consec_zero_mv, + vpx_calloc(cm->mi_rows * cm->mi_cols, + sizeof(*lc->consec_zero_mv))); + } + cpi->refresh_golden_frame = 1; + cpi->refresh_alt_ref_frame = 1; + } + } + free_copy_partition_data(cpi); alloc_copy_partition_data(cpi); - if (cpi->oxcf.aq_mode == CYCLIC_REFRESH_AQ) + if (cpi->oxcf.aq_mode == CYCLIC_REFRESH_AQ && + cpi->svc.number_spatial_layers == 1) vp9_cyclic_refresh_reset_resize(cpi); rc->rc_1_frame = 0; rc->rc_2_frame = 0; @@ -2306,8 +2356,17 @@ cm->use_highbitdepth = use_highbitdepth; #endif alloc_util_frame_buffers(cpi); - cpi->initial_width = cm->width; - cpi->initial_height = cm->height; + // The initial_width/height is used to clamp the encoding width/height in + // vp9_set_size_literal(). The check below is added to avoid setting the + // initial_width/height to a smaller resolution than the one configured. + // This can happen when the user passes in a lower resolution on the very + // first frame (after creating the encoder with a larger resolution). For + // spatial layers this will prevent user from going back up in resolution + // (i.e., the top layer will get stuck at the lower resolution). + if (cm->width > cpi->initial_width || cm->height > cpi->initial_height) { + cpi->initial_width = cm->width; + cpi->initial_height = cm->height; + } cpi->initial_mbs = cm->MBs; } } @@ -2342,24 +2401,6 @@ return fmt & VPX_IMG_FMT_HIGHBITDEPTH; } -#if CONFIG_VP9_TEMPORAL_DENOISING -static void setup_denoiser_buffer(VP9_COMP *cpi) { - VP9_COMMON *const cm = &cpi->common; - if (cpi->oxcf.noise_sensitivity > 0 && - !cpi->denoiser.frame_buffer_initialized) { - if (vp9_denoiser_alloc(cm, &cpi->svc, &cpi->denoiser, cpi->use_svc, - cpi->oxcf.noise_sensitivity, cm->width, cm->height, - cm->subsampling_x, cm->subsampling_y, -#if CONFIG_VP9_HIGHBITDEPTH - cm->use_highbitdepth, -#endif - VP9_ENC_BORDER_IN_PIXELS)) - vpx_internal_error(&cm->error, VPX_CODEC_MEM_ERROR, - "Failed to allocate denoiser"); - } -} -#endif - void vp9_update_compressor_with_img_fmt(VP9_COMP *cpi, vpx_img_fmt_t img_fmt) { const VP9EncoderConfig *oxcf = &cpi->oxcf; unsigned int subsampling_x, subsampling_y; @@ -2427,6 +2468,8 @@ vp9_init_rd_parameters(cpi); init_frame_indexes(cm); + cpi->initial_width = cpi->oxcf.width; + cpi->initial_height = cpi->oxcf.height; cpi->tile_data = NULL; realloc_segmentation_maps(cpi); @@ -2753,16 +2796,6 @@ cm->error.setjmp = 0; -#if CONFIG_RATE_CTRL - encode_command_init(&cpi->encode_command); - if (oxcf->use_simple_encode_api) { - partition_info_init(cpi); - motion_vector_info_init(cpi); - fp_motion_vector_info_init(cpi); - tpl_stats_info_init(cpi); - } -#endif - return cpi; } @@ -3448,11 +3481,15 @@ lf->filter_level = 0; lf->last_filt_level = 0; } else { +#if CONFIG_INTERNAL_STATS struct vpx_usec_timer timer; +#endif vpx_clear_system_state(); +#if CONFIG_INTERNAL_STATS vpx_usec_timer_start(&timer); +#endif if (!cpi->rc.is_src_frame_alt_ref) { if ((cpi->common.frame_type == KEY_FRAME) && @@ -3465,8 +3502,10 @@ lf->filter_level = 0; } +#if CONFIG_INTERNAL_STATS vpx_usec_timer_mark(&timer); cpi->time_pick_lpf += vpx_usec_timer_elapsed(&timer); +#endif } if (lf->filter_level > 0 && is_reference_frame) { @@ -4213,7 +4252,7 @@ cpi->oxcf.rc_mode == VPX_CBR && cm->frame_type != KEY_FRAME; - vp9_set_quantizer(cpi, q); + vp9_set_quantizer(cpi, q, 0); vp9_set_variance_partition_thresholds(cpi, q, 0); setup_frame(cpi); @@ -4242,7 +4281,7 @@ (cpi->rc.high_source_sad || (cpi->use_svc && svc->high_source_sad_superframe))) { if (vp9_encodedframe_overshoot(cpi, -1, &q)) { - vp9_set_quantizer(cpi, q); + vp9_set_quantizer(cpi, q, 0); vp9_set_variance_partition_thresholds(cpi, q, 0); } } @@ -4303,7 +4342,7 @@ // adjust some rate control parameters, and return to re-encode the frame. if (vp9_encodedframe_overshoot(cpi, frame_size, &q)) { vpx_clear_system_state(); - vp9_set_quantizer(cpi, q); + vp9_set_quantizer(cpi, q, 0); vp9_set_variance_partition_thresholds(cpi, q, 0); suppress_active_map(cpi); // Turn-off cyclic refresh for re-encoded frame. @@ -4362,150 +4401,8 @@ return VPXMIN(qstep, MAX_QSTEP_ADJ); } -#if CONFIG_RATE_CTRL -static void init_rq_history(RATE_QINDEX_HISTORY *rq_history) { - rq_history->recode_count = 0; - rq_history->q_index_high = 255; - rq_history->q_index_low = 0; -} - -static void update_rq_history(RATE_QINDEX_HISTORY *rq_history, int target_bits, - int actual_bits, int q_index) { - rq_history->q_index_history[rq_history->recode_count] = q_index; - rq_history->rate_history[rq_history->recode_count] = actual_bits; - if (actual_bits <= target_bits) { - rq_history->q_index_high = q_index; - } - if (actual_bits >= target_bits) { - rq_history->q_index_low = q_index; - } - rq_history->recode_count += 1; -} - -static int guess_q_index_from_model(const RATE_QSTEP_MODEL *rq_model, - int target_bits) { - // The model predicts bits as follows. - // target_bits = bias - ratio * log2(q_step) - // Given the target_bits, we compute the q_step as follows. - double q_step; - assert(rq_model->ratio > 0); - q_step = pow(2.0, (rq_model->bias - target_bits) / rq_model->ratio); - // TODO(angiebird): Make this function support highbitdepth. - return vp9_convert_q_to_qindex(q_step, VPX_BITS_8); -} - -static int guess_q_index_linear(int prev_q_index, int target_bits, - int actual_bits, int gap) { - int q_index = prev_q_index; - if (actual_bits < target_bits) { - q_index -= gap; - q_index = VPXMAX(q_index, 0); - } else { - q_index += gap; - q_index = VPXMIN(q_index, 255); - } - return q_index; -} - -static double get_bits_percent_diff(int target_bits, int actual_bits) { - double diff; - target_bits = VPXMAX(target_bits, 1); - diff = abs(target_bits - actual_bits) * 1. / target_bits; - return diff * 100; -} - -static int rq_model_predict_q_index(const RATE_QSTEP_MODEL *rq_model, - const RATE_QINDEX_HISTORY *rq_history, - int target_bits) { - int q_index = 128; - if (rq_history->recode_count > 0) { - const int actual_bits = - rq_history->rate_history[rq_history->recode_count - 1]; - const int prev_q_index = - rq_history->q_index_history[rq_history->recode_count - 1]; - const double percent_diff = get_bits_percent_diff(target_bits, actual_bits); - if (percent_diff > 50) { - // Binary search. - // When the actual_bits and target_bits are far apart, binary search - // q_index is faster. - q_index = (rq_history->q_index_low + rq_history->q_index_high) / 2; - } else { - if (rq_model->ready) { - q_index = guess_q_index_from_model(rq_model, target_bits); - } else { - // TODO(angiebird): Find a better way to set the gap. - q_index = - guess_q_index_linear(prev_q_index, target_bits, actual_bits, 20); - } - } - } else { - if (rq_model->ready) { - q_index = guess_q_index_from_model(rq_model, target_bits); - } - } - - assert(rq_history->q_index_low <= rq_history->q_index_high); - if (q_index <= rq_history->q_index_low) { - q_index = rq_history->q_index_low + 1; - } - if (q_index >= rq_history->q_index_high) { - q_index = rq_history->q_index_high - 1; - } - return q_index; -} - -static void rq_model_update(const RATE_QINDEX_HISTORY *rq_history, - int target_bits, RATE_QSTEP_MODEL *rq_model) { - const int recode_count = rq_history->recode_count; - const double delta = 0.00001; - if (recode_count >= 2) { - const int q_index1 = rq_history->q_index_history[recode_count - 2]; - const int q_index2 = rq_history->q_index_history[recode_count - 1]; - const int r1 = rq_history->rate_history[recode_count - 2]; - const int r2 = rq_history->rate_history[recode_count - 1]; - int valid = 0; - // lower q_index should yield higher bit rate - if (q_index1 < q_index2) { - valid = r1 > r2; - } else if (q_index1 > q_index2) { - valid = r1 < r2; - } - // Only update the model when the q_index and rate behave normally. - if (valid) { - // Fit the ratio and bias of rq_model based on last two recode histories. - const double s1 = vp9_convert_qindex_to_q(q_index1, VPX_BITS_8); - const double s2 = vp9_convert_qindex_to_q(q_index2, VPX_BITS_8); - if (fabs(log2(s1) - log2(s2)) > delta) { - rq_model->ratio = (r2 - r1) / (log2(s1) - log2(s2)); - rq_model->bias = r1 + (rq_model->ratio) * log2(s1); - if (rq_model->ratio > delta && rq_model->bias > delta) { - rq_model->ready = 1; - } - } - } - } else if (recode_count == 1) { - if (rq_model->ready) { - // Update the ratio only when the initial model exists and we only have - // one recode history. - const int prev_q = rq_history->q_index_history[recode_count - 1]; - const double prev_q_step = vp9_convert_qindex_to_q(prev_q, VPX_BITS_8); - if (fabs(log2(prev_q_step)) > delta) { - const int actual_bits = rq_history->rate_history[recode_count - 1]; - rq_model->ratio = - rq_model->ratio + (target_bits - actual_bits) / log2(prev_q_step); - } - } - } -} -#endif // CONFIG_RATE_CTRL - static void encode_with_recode_loop(VP9_COMP *cpi, size_t *size, uint8_t *dest, - size_t dest_size -#if CONFIG_RATE_CTRL - , - RATE_QINDEX_HISTORY *rq_history -#endif // CONFIG_RATE_CTRL -) { + size_t dest_size) { const VP9EncoderConfig *const oxcf = &cpi->oxcf; VP9_COMMON *const cm = &cpi->common; RATE_CONTROL *const rc = &cpi->rc; @@ -4525,17 +4422,6 @@ const int orig_rc_max_frame_bandwidth = rc->max_frame_bandwidth; -#if CONFIG_RATE_CTRL - RATE_QSTEP_MODEL *rq_model; - { - const FRAME_UPDATE_TYPE update_type = - cpi->twopass.gf_group.update_type[cpi->twopass.gf_group.index]; - const ENCODE_FRAME_TYPE frame_type = get_encode_frame_type(update_type); - rq_model = &cpi->rq_model[frame_type]; - } - init_rq_history(rq_history); -#endif // CONFIG_RATE_CTRL - if (cm->show_existing_frame) { rc->this_frame_target = 0; if (is_psnr_calc_enabled(cpi)) set_raw_source_frame(cpi); @@ -4624,36 +4510,38 @@ vp9_scale_references(cpi); } -#if CONFIG_RATE_CTRL - // TODO(angiebird): This is a hack for making sure the encoder use the - // external_quantize_index exactly. Avoid this kind of hack later. - if (cpi->oxcf.use_simple_encode_api) { - if (cpi->encode_command.use_external_target_frame_bits) { - q = rq_model_predict_q_index(rq_model, rq_history, - rc->this_frame_target); - } - if (cpi->encode_command.use_external_quantize_index) { - q = cpi->encode_command.external_quantize_index; - } - } -#endif // CONFIG_RATE_CTRL const GF_GROUP *gf_group = &cpi->twopass.gf_group; + int ext_rc_delta_q_uv = 0; if (cpi->ext_ratectrl.ready && (cpi->ext_ratectrl.funcs.rc_type & VPX_RC_QP) != 0 && cpi->ext_ratectrl.funcs.get_encodeframe_decision != NULL) { vpx_codec_err_t codec_status; vpx_rc_encodeframe_decision_t encode_frame_decision; + int sb_size = num_8x8_blocks_wide_lookup[BLOCK_64X64] * MI_SIZE; + int frame_height_sb = (cm->height + sb_size - 1) / sb_size; + int frame_width_sb = (cm->width + sb_size - 1) / sb_size; + CHECK_MEM_ERROR(&cm->error, encode_frame_decision.sb_params_list, + (sb_params *)vpx_calloc( + frame_height_sb * frame_width_sb, + sizeof(*encode_frame_decision.sb_params_list))); codec_status = vp9_extrc_get_encodeframe_decision( &cpi->ext_ratectrl, gf_group->index, &encode_frame_decision); if (codec_status != VPX_CODEC_OK) { vpx_internal_error(&cm->error, codec_status, "vp9_extrc_get_encodeframe_decision() failed"); } + for (int idx = 0; idx < frame_height_sb * frame_width_sb; ++idx) { + cpi->sb_mul_scale[idx] = + (((int64_t)encode_frame_decision.sb_params_list[idx].rdmult * 256) / + (encode_frame_decision.rdmult + 1)); + } + vpx_free(encode_frame_decision.sb_params_list); // If the external model recommends a reserved value, we use // libvpx's default q. if (encode_frame_decision.q_index != VPX_DEFAULT_Q) { q = encode_frame_decision.q_index; } + ext_rc_delta_q_uv = encode_frame_decision.delta_q_uv; } if (cpi->ext_ratectrl.ready && cpi->ext_ratectrl.log_file) { @@ -4662,7 +4550,7 @@ gf_group->index, gf_group->update_type[gf_group->index], q); } - vp9_set_quantizer(cpi, q); + vp9_set_quantizer(cpi, q, ext_rc_delta_q_uv); if (loop_count == 0) setup_frame(cpi); @@ -4705,39 +4593,6 @@ (cpi->ext_ratectrl.funcs.rc_type & VPX_RC_QP) != 0) { break; } -#if CONFIG_RATE_CTRL - if (cpi->oxcf.use_simple_encode_api) { - // This part needs to be after save_coding_context() because - // restore_coding_context will be called in the end of this function. - // TODO(angiebird): This is a hack for making sure the encoder use the - // external_quantize_index exactly. Avoid this kind of hack later. - if (cpi->encode_command.use_external_quantize_index) { - break; - } - - if (cpi->encode_command.use_external_target_frame_bits) { - const double percent_diff = get_bits_percent_diff( - rc->this_frame_target, rc->projected_frame_size); - update_rq_history(rq_history, rc->this_frame_target, - rc->projected_frame_size, q); - loop_count += 1; - - rq_model_update(rq_history, rc->this_frame_target, rq_model); - - // Check if we hit the target bitrate. - if (percent_diff <= - cpi->encode_command.target_frame_bits_error_percent || - rq_history->recode_count >= RATE_CTRL_MAX_RECODE_NUM || - rq_history->q_index_low >= rq_history->q_index_high) { - break; - } - - loop = 1; - restore_coding_context(cpi); - continue; - } - } -#endif // CONFIG_RATE_CTRL if (oxcf->rc_mode == VPX_Q) { loop = 0; @@ -5184,8 +5039,7 @@ // Base the filter strength on the current active max Q. const int q = (int)(vp9_convert_qindex_to_q(twopass->active_worst_quality, cm->bit_depth)); - int strength = - VPXMAX(oxcf->arnr_strength >> 2, VPXMIN(oxcf->arnr_strength, (q >> 4))); + int strength = clamp(q >> 4, oxcf->arnr_strength >> 2, oxcf->arnr_strength); // Denoise each of Y,U and V buffers. spatial_denoise_buffer(cpi, src->y_buffer, src->y_stride, src->y_width, @@ -5332,6 +5186,23 @@ cpi->mb_wiener_var_cols = cm->mb_cols; } +static void init_sb_mul_scale_buffer(VP9_COMP *cpi) { + VP9_COMMON *cm = &cpi->common; + + if (cpi->mb_wiener_var_rows >= cm->mb_rows && + cpi->mb_wiener_var_cols >= cm->mb_cols) + return; + + vpx_free(cpi->sb_mul_scale); + cpi->sb_mul_scale = NULL; + + CHECK_MEM_ERROR( + &cm->error, cpi->sb_mul_scale, + vpx_calloc(cm->mb_rows * cm->mb_cols, sizeof(*cpi->sb_mul_scale))); + cpi->mb_wiener_var_rows = cm->mb_rows; + cpi->mb_wiener_var_cols = cm->mb_cols; +} + static void set_mb_wiener_variance(VP9_COMP *cpi) { VP9_COMMON *cm = &cpi->common; uint8_t *buffer = cpi->Source->y_buffer; @@ -5451,69 +5322,6 @@ encode_frame_result->update_type = update_type; encode_frame_result->quantize_index = quantize_index; } - -#if CONFIG_RATE_CTRL -static void yv12_buffer_to_image_buffer(const YV12_BUFFER_CONFIG *yv12_buffer, - IMAGE_BUFFER *image_buffer) { - const uint8_t *src_buf_ls[3] = { yv12_buffer->y_buffer, yv12_buffer->u_buffer, - yv12_buffer->v_buffer }; - const int src_stride_ls[3] = { yv12_buffer->y_stride, yv12_buffer->uv_stride, - yv12_buffer->uv_stride }; - const int w_ls[3] = { yv12_buffer->y_crop_width, yv12_buffer->uv_crop_width, - yv12_buffer->uv_crop_width }; - const int h_ls[3] = { yv12_buffer->y_crop_height, yv12_buffer->uv_crop_height, - yv12_buffer->uv_crop_height }; - int plane; - for (plane = 0; plane < 3; ++plane) { - const int src_stride = src_stride_ls[plane]; - const int w = w_ls[plane]; - const int h = h_ls[plane]; - const uint8_t *src_buf = src_buf_ls[plane]; - uint8_t *dst_buf = image_buffer->plane_buffer[plane]; - int r; - assert(image_buffer->plane_width[plane] == w); - assert(image_buffer->plane_height[plane] == h); - for (r = 0; r < h; ++r) { - memcpy(dst_buf, src_buf, sizeof(*src_buf) * w); - src_buf += src_stride; - dst_buf += w; - } - } -} - -// This function will update extra information specific for simple_encode APIs -static void update_encode_frame_result_simple_encode( - int ref_frame_flags, FRAME_UPDATE_TYPE update_type, - const YV12_BUFFER_CONFIG *source_frame, const RefCntBuffer *coded_frame_buf, - RefCntBuffer *ref_frame_bufs[MAX_INTER_REF_FRAMES], int quantize_index, - uint32_t bit_depth, uint32_t input_bit_depth, const FRAME_COUNTS *counts, - const PARTITION_INFO *partition_info, - const MOTION_VECTOR_INFO *motion_vector_info, - const TplDepStats *tpl_stats_info, - ENCODE_FRAME_RESULT *encode_frame_result) { - PSNR_STATS psnr; - update_encode_frame_result_basic(update_type, coded_frame_buf->frame_index, - quantize_index, encode_frame_result); - compute_psnr_stats(source_frame, &coded_frame_buf->buf, bit_depth, - input_bit_depth); - encode_frame_result->frame_coding_index = coded_frame_buf->frame_coding_index; - - vp9_get_ref_frame_info(update_type, ref_frame_flags, ref_frame_bufs, - encode_frame_result->ref_frame_coding_indexes, - encode_frame_result->ref_frame_valid_list); - - encode_frame_result->psnr = psnr.psnr[0]; - encode_frame_result->sse = psnr.sse[0]; - encode_frame_result->frame_counts = *counts; - encode_frame_result->partition_info = partition_info; - encode_frame_result->motion_vector_info = motion_vector_info; - encode_frame_result->tpl_stats_info = tpl_stats_info; - if (encode_frame_result->coded_frame.allocated) { - yv12_buffer_to_image_buffer(&coded_frame_buf->buf, - &encode_frame_result->coded_frame); - } -} -#endif // CONFIG_RATE_CTRL #endif // !CONFIG_REALTIME_ONLY static void encode_frame_to_data_rate( @@ -5588,6 +5396,8 @@ set_mb_wiener_variance(cpi); } + init_sb_mul_scale_buffer(cpi); + vpx_clear_system_state(); #if CONFIG_INTERNAL_STATS @@ -5628,10 +5438,6 @@ if (!encode_without_recode_loop(cpi, size, dest, dest_size)) return; } else { #if !CONFIG_REALTIME_ONLY -#if CONFIG_RATE_CTRL - encode_with_recode_loop(cpi, size, dest, dest_size, - &encode_frame_result->rq_history); -#else // CONFIG_RATE_CTRL #if CONFIG_COLLECT_COMPONENT_TIMING start_timing(cpi, encode_with_recode_loop_time); #endif @@ -5639,7 +5445,6 @@ #if CONFIG_COLLECT_COMPONENT_TIMING end_timing(cpi, encode_with_recode_loop_time); #endif -#endif // CONFIG_RATE_CTRL #endif // !CONFIG_REALTIME_ONLY } @@ -5729,7 +5534,7 @@ #if CONFIG_REALTIME_ONLY (void)encode_frame_result; assert(encode_frame_result == NULL); -#else // CONFIG_REALTIME_ONLY +#else // CONFIG_REALTIME_ONLY if (encode_frame_result != NULL) { const RefCntBuffer *coded_frame_buf = get_ref_cnt_buffer(cm, cm->new_fb_idx); @@ -5765,19 +5570,6 @@ "ENCODE_FRAME_RESULT gop_index %d psnr %f bits %zu\n", cpi->twopass.gf_group.index, psnr.psnr[0], (*size) << 3); } - -#if CONFIG_RATE_CTRL - if (cpi->oxcf.use_simple_encode_api) { - const int ref_frame_flags = get_ref_frame_flags(cpi); - update_encode_frame_result_simple_encode( - ref_frame_flags, - cpi->twopass.gf_group.update_type[cpi->twopass.gf_group.index], - cpi->Source, coded_frame_buf, ref_frame_bufs, quantize_index, - cm->bit_depth, cpi->oxcf.input_bit_depth, cpi->td.counts, - cpi->partition_info, cpi->motion_vector_info, cpi->tpl_stats_info, - encode_frame_result); - } -#endif // CONFIG_RATE_CTRL } #endif // CONFIG_REALTIME_ONLY @@ -5943,7 +5735,9 @@ YV12_BUFFER_CONFIG *sd, int64_t time_stamp, int64_t end_time) { VP9_COMMON *const cm = &cpi->common; +#if CONFIG_INTERNAL_STATS struct vpx_usec_timer timer; +#endif int res = 0; const int subsampling_x = sd->subsampling_x; const int subsampling_y = sd->subsampling_y; @@ -5960,13 +5754,17 @@ alloc_raw_frame_buffers(cpi); +#if CONFIG_INTERNAL_STATS vpx_usec_timer_start(&timer); +#endif if (vp9_lookahead_push(cpi->lookahead, sd, time_stamp, end_time, use_highbitdepth, frame_flags)) res = -1; +#if CONFIG_INTERNAL_STATS vpx_usec_timer_mark(&timer); cpi->time_receive_data += vpx_usec_timer_elapsed(&timer); +#endif if ((cm->profile == PROFILE_0 || cm->profile == PROFILE_2) && (subsampling_x != 1 || subsampling_y != 1)) { @@ -5980,7 +5778,18 @@ "4:2:0 color format requires profile 0 or 2"); res = -1; } - + if (cm->color_space == VPX_CS_SRGB) { + if (cm->profile == PROFILE_0 || cm->profile == PROFILE_2) { + vpx_internal_error(&cm->error, VPX_CODEC_INVALID_PARAM, + "SRGB color space requires profile 1 or 3"); + res = -1; + } + if (subsampling_x != 0 || subsampling_y != 0) { + vpx_internal_error(&cm->error, VPX_CODEC_INVALID_PARAM, + "SRGB color space requires 4:4:4"); + res = -1; + } + } return res; } @@ -6339,12 +6148,6 @@ void vp9_init_encode_frame_result(ENCODE_FRAME_RESULT *encode_frame_result) { encode_frame_result->show_idx = -1; // Actual encoding doesn't happen. -#if CONFIG_RATE_CTRL - encode_frame_result->frame_coding_index = -1; - vp9_zero(encode_frame_result->coded_frame); - encode_frame_result->coded_frame.allocated = 0; - init_rq_history(&encode_frame_result->rq_history); -#endif // CONFIG_RATE_CTRL } // Returns if TPL stats need to be calculated. @@ -6374,7 +6177,9 @@ VP9_COMMON *const cm = &cpi->common; BufferPool *const pool = cm->buffer_pool; RATE_CONTROL *const rc = &cpi->rc; +#if CONFIG_INTERNAL_STATS struct vpx_usec_timer cmptimer; +#endif YV12_BUFFER_CONFIG *force_src_buffer = NULL; struct lookahead_entry *last_source = NULL; struct lookahead_entry *source = NULL; @@ -6390,7 +6195,9 @@ vp9_one_pass_svc_start_layer(cpi); } +#if CONFIG_INTERNAL_STATS vpx_usec_timer_start(&cmptimer); +#endif vp9_set_high_precision_mv(cpi, ALTREF_HIGH_PRECISION_MV); @@ -6731,8 +6538,10 @@ if (cpi->svc.spatial_layer_id == cpi->svc.number_spatial_layers - 1) cpi->fixed_qp_onepass = 0; +#if CONFIG_INTERNAL_STATS vpx_usec_timer_mark(&cmptimer); cpi->time_compress_data += vpx_usec_timer_elapsed(&cmptimer); +#endif if (cpi->keep_level_stats && oxcf->pass != 1) update_level_info(cpi, size, arf_src_index);
diff --git a/vp9/encoder/vp9_encoder.h b/vp9/encoder/vp9_encoder.h index 8afea3a..f7ae7d7 100644 --- a/vp9/encoder/vp9_encoder.h +++ b/vp9/encoder/vp9_encoder.h
@@ -285,7 +285,6 @@ int row_mt; unsigned int motion_vector_unit_test; int delta_q_uv; - int use_simple_encode_api; // Use SimpleEncode APIs or not } VP9EncoderConfig; static INLINE int is_lossless_requested(const VP9EncoderConfig *cfg) { @@ -525,143 +524,6 @@ int group_idx; } KMEANS_DATA; -#if CONFIG_RATE_CTRL -typedef struct PARTITION_INFO { - int row; // row pixel offset of current 4x4 block - int column; // column pixel offset of current 4x4 block - int row_start; // row pixel offset of the start of the prediction block - int column_start; // column pixel offset of the start of the prediction block - int width; // prediction block width - int height; // prediction block height -} PARTITION_INFO; - -typedef struct MOTION_VECTOR_INFO { - MV_REFERENCE_FRAME ref_frame[2]; - int_mv mv[2]; -} MOTION_VECTOR_INFO; - -typedef struct GOP_COMMAND { - int use; // use this command to set gop or not. If not, use vp9's decision. - int show_frame_count; - int use_alt_ref; -} GOP_COMMAND; - -static INLINE void gop_command_on(GOP_COMMAND *gop_command, - int show_frame_count, int use_alt_ref) { - gop_command->use = 1; - gop_command->show_frame_count = show_frame_count; - gop_command->use_alt_ref = use_alt_ref; -} - -static INLINE void gop_command_off(GOP_COMMAND *gop_command) { - gop_command->use = 0; - gop_command->show_frame_count = 0; - gop_command->use_alt_ref = 0; -} - -static INLINE int gop_command_coding_frame_count( - const GOP_COMMAND *gop_command) { - if (gop_command->use == 0) { - assert(0); - return -1; - } - return gop_command->show_frame_count + gop_command->use_alt_ref; -} - -// TODO(angiebird): See if we can merge this one with FrameType in -// simple_encode.h -typedef enum ENCODE_FRAME_TYPE { - ENCODE_FRAME_TYPE_KEY, - ENCODE_FRAME_TYPE_INTER, - ENCODE_FRAME_TYPE_ALTREF, - ENCODE_FRAME_TYPE_OVERLAY, - ENCODE_FRAME_TYPE_GOLDEN, - ENCODE_FRAME_TYPES, -} ENCODE_FRAME_TYPE; - -// TODO(angiebird): Merge this function with get_frame_type_from_update_type() -static INLINE ENCODE_FRAME_TYPE -get_encode_frame_type(FRAME_UPDATE_TYPE update_type) { - switch (update_type) { - case KF_UPDATE: return ENCODE_FRAME_TYPE_KEY; - case ARF_UPDATE: return ENCODE_FRAME_TYPE_ALTREF; - case GF_UPDATE: return ENCODE_FRAME_TYPE_GOLDEN; - case OVERLAY_UPDATE: return ENCODE_FRAME_TYPE_OVERLAY; - case LF_UPDATE: return ENCODE_FRAME_TYPE_INTER; - default: - fprintf(stderr, "Unsupported update_type %d\n", update_type); - abort(); - return ENCODE_FRAME_TYPE_INTER; - } -} - -typedef struct RATE_QSTEP_MODEL { - // The rq model predicts the bit usage as follows. - // rate = bias - ratio * log2(q_step) - int ready; - double bias; - double ratio; -} RATE_QSTEP_MODEL; - -typedef struct ENCODE_COMMAND { - int use_external_quantize_index; - int external_quantize_index; - - int use_external_target_frame_bits; - int target_frame_bits; - double target_frame_bits_error_percent; - - GOP_COMMAND gop_command; -} ENCODE_COMMAND; - -static INLINE void encode_command_set_gop_command( - ENCODE_COMMAND *encode_command, GOP_COMMAND gop_command) { - encode_command->gop_command = gop_command; -} - -static INLINE void encode_command_set_external_quantize_index( - ENCODE_COMMAND *encode_command, int quantize_index) { - encode_command->use_external_quantize_index = 1; - encode_command->external_quantize_index = quantize_index; -} - -static INLINE void encode_command_reset_external_quantize_index( - ENCODE_COMMAND *encode_command) { - encode_command->use_external_quantize_index = 0; - encode_command->external_quantize_index = -1; -} - -static INLINE void encode_command_set_target_frame_bits( - ENCODE_COMMAND *encode_command, int target_frame_bits, - double target_frame_bits_error_percent) { - encode_command->use_external_target_frame_bits = 1; - encode_command->target_frame_bits = target_frame_bits; - encode_command->target_frame_bits_error_percent = - target_frame_bits_error_percent; -} - -static INLINE void encode_command_reset_target_frame_bits( - ENCODE_COMMAND *encode_command) { - encode_command->use_external_target_frame_bits = 0; - encode_command->target_frame_bits = -1; - encode_command->target_frame_bits_error_percent = 0; -} - -static INLINE void encode_command_init(ENCODE_COMMAND *encode_command) { - vp9_zero(*encode_command); - encode_command_reset_external_quantize_index(encode_command); - encode_command_reset_target_frame_bits(encode_command); - gop_command_off(&encode_command->gop_command); -} - -// Returns number of units in size of 4, if not multiple not a multiple of 4, -// round it up. For example, size is 7, return 2. -static INLINE int get_num_unit_4x4(int size) { return (size + 3) >> 2; } -// Returns number of units in size of 16, if not multiple not a multiple of 16, -// round it up. For example, size is 17, return 2. -static INLINE int get_num_unit_16x16(int size) { return (size + 15) >> 4; } -#endif // CONFIG_RATE_CTRL - #if CONFIG_COLLECT_COMPONENT_TIMING #include "vpx_ports/vpx_timer.h" // Adjust the following to add new components. @@ -803,6 +665,8 @@ int mb_wiener_var_cols; double *mi_ssim_rdmult_scaling_factors; + int64_t *sb_mul_scale; + YV12_BUFFER_CONFIG last_frame_uf; TOKENEXTRA *tile_tok[4][1 << 6]; @@ -865,10 +729,12 @@ struct scale_factors me_sf; vp9_diamond_search_fn_t diamond_search_sad; vp9_variance_fn_ptr_t fn_ptr[BLOCK_SIZES]; +#if CONFIG_INTERNAL_STATS uint64_t time_receive_data; uint64_t time_compress_data; uint64_t time_pick_lpf; uint64_t time_encode_sb_row; +#endif TWO_PASS twopass; @@ -1035,15 +901,6 @@ vpx_roi_map_t roi; LOOPFILTER_CONTROL loopfilter_ctrl; -#if CONFIG_RATE_CTRL - ENCODE_COMMAND encode_command; - PARTITION_INFO *partition_info; - MOTION_VECTOR_INFO *motion_vector_info; - MOTION_VECTOR_INFO *fp_motion_vector_info; - TplDepStats *tpl_stats_info; - - RATE_QSTEP_MODEL rq_model[ENCODE_FRAME_TYPES]; -#endif EXT_RATECTRL ext_ratectrl; int fixed_qp_onepass; @@ -1072,139 +929,9 @@ #endif } VP9_COMP; -#if CONFIG_RATE_CTRL -// Allocates memory for the partition information. -// The unit size is each 4x4 block. -// Only called once in vp9_create_compressor(). -static INLINE void partition_info_init(struct VP9_COMP *cpi) { - VP9_COMMON *const cm = &cpi->common; - const int unit_width = get_num_unit_4x4(cpi->frame_info.frame_width); - const int unit_height = get_num_unit_4x4(cpi->frame_info.frame_height); - CHECK_MEM_ERROR(&cm->error, cpi->partition_info, - (PARTITION_INFO *)vpx_calloc(unit_width * unit_height, - sizeof(PARTITION_INFO))); - memset(cpi->partition_info, 0, - unit_width * unit_height * sizeof(PARTITION_INFO)); -} - -// Frees memory of the partition information. -// Only called once in dealloc_compressor_data(). -static INLINE void free_partition_info(struct VP9_COMP *cpi) { - vpx_free(cpi->partition_info); - cpi->partition_info = NULL; -} - -static INLINE void reset_mv_info(MOTION_VECTOR_INFO *mv_info) { - mv_info->ref_frame[0] = NO_REF_FRAME; - mv_info->ref_frame[1] = NO_REF_FRAME; - mv_info->mv[0].as_int = INVALID_MV; - mv_info->mv[1].as_int = INVALID_MV; -} - -// Allocates memory for the motion vector information. -// The unit size is each 4x4 block. -// Only called once in vp9_create_compressor(). -static INLINE void motion_vector_info_init(struct VP9_COMP *cpi) { - VP9_COMMON *const cm = &cpi->common; - const int unit_width = get_num_unit_4x4(cpi->frame_info.frame_width); - const int unit_height = get_num_unit_4x4(cpi->frame_info.frame_height); - CHECK_MEM_ERROR(&cm->error, cpi->motion_vector_info, - (MOTION_VECTOR_INFO *)vpx_calloc(unit_width * unit_height, - sizeof(MOTION_VECTOR_INFO))); - memset(cpi->motion_vector_info, 0, - unit_width * unit_height * sizeof(MOTION_VECTOR_INFO)); -} - -// Frees memory of the motion vector information. -// Only called once in dealloc_compressor_data(). -static INLINE void free_motion_vector_info(struct VP9_COMP *cpi) { - vpx_free(cpi->motion_vector_info); - cpi->motion_vector_info = NULL; -} - -// Allocates memory for the tpl stats information. -// Only called once in vp9_create_compressor(). -static INLINE void tpl_stats_info_init(struct VP9_COMP *cpi) { - VP9_COMMON *const cm = &cpi->common; - CHECK_MEM_ERROR( - &cm->error, cpi->tpl_stats_info, - (TplDepStats *)vpx_calloc(MAX_LAG_BUFFERS, sizeof(TplDepStats))); - memset(cpi->tpl_stats_info, 0, MAX_LAG_BUFFERS * sizeof(TplDepStats)); -} - -// Frees memory of the tpl stats information. -// Only called once in dealloc_compressor_data(). -static INLINE void free_tpl_stats_info(struct VP9_COMP *cpi) { - vpx_free(cpi->tpl_stats_info); - cpi->tpl_stats_info = NULL; -} - -// Allocates memory for the first pass motion vector information. -// The unit size is each 16x16 block. -// Only called once in vp9_create_compressor(). -static INLINE void fp_motion_vector_info_init(struct VP9_COMP *cpi) { - VP9_COMMON *const cm = &cpi->common; - const int unit_width = get_num_unit_16x16(cpi->frame_info.frame_width); - const int unit_height = get_num_unit_16x16(cpi->frame_info.frame_height); - CHECK_MEM_ERROR(&cm->error, cpi->fp_motion_vector_info, - (MOTION_VECTOR_INFO *)vpx_calloc(unit_width * unit_height, - sizeof(MOTION_VECTOR_INFO))); -} - -static INLINE void fp_motion_vector_info_reset( - int frame_width, int frame_height, - MOTION_VECTOR_INFO *fp_motion_vector_info) { - const int unit_width = get_num_unit_16x16(frame_width); - const int unit_height = get_num_unit_16x16(frame_height); - int i; - for (i = 0; i < unit_width * unit_height; ++i) { - reset_mv_info(fp_motion_vector_info + i); - } -} - -// Frees memory of the first pass motion vector information. -// Only called once in dealloc_compressor_data(). -static INLINE void free_fp_motion_vector_info(struct VP9_COMP *cpi) { - vpx_free(cpi->fp_motion_vector_info); - cpi->fp_motion_vector_info = NULL; -} - -// This is the c-version counter part of ImageBuffer -typedef struct IMAGE_BUFFER { - int allocated; - int plane_width[3]; - int plane_height[3]; - uint8_t *plane_buffer[3]; -} IMAGE_BUFFER; - -#define RATE_CTRL_MAX_RECODE_NUM 7 - -typedef struct RATE_QINDEX_HISTORY { - int recode_count; - int q_index_history[RATE_CTRL_MAX_RECODE_NUM]; - int rate_history[RATE_CTRL_MAX_RECODE_NUM]; - int q_index_high; - int q_index_low; -} RATE_QINDEX_HISTORY; - -#endif // CONFIG_RATE_CTRL - typedef struct ENCODE_FRAME_RESULT { int show_idx; FRAME_UPDATE_TYPE update_type; -#if CONFIG_RATE_CTRL - int frame_coding_index; - int ref_frame_coding_indexes[MAX_INTER_REF_FRAMES]; - int ref_frame_valid_list[MAX_INTER_REF_FRAMES]; - double psnr; - uint64_t sse; - FRAME_COUNTS frame_counts; - const PARTITION_INFO *partition_info; - const MOTION_VECTOR_INFO *motion_vector_info; - const TplDepStats *tpl_stats_info; - IMAGE_BUFFER coded_frame; - RATE_QINDEX_HISTORY rq_history; -#endif // CONFIG_RATE_CTRL int quantize_index; } ENCODE_FRAME_RESULT;
diff --git a/vp9/encoder/vp9_ethread.c b/vp9/encoder/vp9_ethread.c index c3b7950..efe4725 100644 --- a/vp9/encoder/vp9_ethread.c +++ b/vp9/encoder/vp9_ethread.c
@@ -278,13 +278,13 @@ tile_data->fp_data.sum_in_vectors += tile_data_t->fp_data.sum_in_vectors; tile_data->fp_data.intra_smooth_count += tile_data_t->fp_data.intra_smooth_count; + const int min_start_row = VPXMIN(tile_data->fp_data.image_data_start_row, + tile_data_t->fp_data.image_data_start_row); tile_data->fp_data.image_data_start_row = - VPXMIN(tile_data->fp_data.image_data_start_row, - tile_data_t->fp_data.image_data_start_row) == INVALID_ROW + (min_start_row == INVALID_ROW) ? VPXMAX(tile_data->fp_data.image_data_start_row, tile_data_t->fp_data.image_data_start_row) - : VPXMIN(tile_data->fp_data.image_data_start_row, - tile_data_t->fp_data.image_data_start_row); + : min_start_row; } #endif // !CONFIG_REALTIME_ONLY
diff --git a/vp9/encoder/vp9_firstpass.c b/vp9/encoder/vp9_firstpass.c index a41eca9..32ddacf 100644 --- a/vp9/encoder/vp9_firstpass.c +++ b/vp9/encoder/vp9_firstpass.c
@@ -863,31 +863,15 @@ this_tile->fp_data.sum_mvcs += fp_acc_data->sum_mvcs; this_tile->fp_data.sum_in_vectors += fp_acc_data->sum_in_vectors; this_tile->fp_data.intra_smooth_count += fp_acc_data->intra_smooth_count; + const int min_start_row = VPXMIN(this_tile->fp_data.image_data_start_row, + fp_acc_data->image_data_start_row); this_tile->fp_data.image_data_start_row = - VPXMIN(this_tile->fp_data.image_data_start_row, - fp_acc_data->image_data_start_row) == INVALID_ROW + (min_start_row == INVALID_ROW) ? VPXMAX(this_tile->fp_data.image_data_start_row, fp_acc_data->image_data_start_row) - : VPXMIN(this_tile->fp_data.image_data_start_row, - fp_acc_data->image_data_start_row); + : min_start_row; } -#if CONFIG_RATE_CTRL -static void store_fp_motion_vector(VP9_COMP *cpi, const MV *mv, - const int mb_row, const int mb_col, - MV_REFERENCE_FRAME frame_type, - const int mv_idx) { - VP9_COMMON *const cm = &cpi->common; - const int mb_index = mb_row * cm->mb_cols + mb_col; - MOTION_VECTOR_INFO *this_motion_vector_info = - &cpi->fp_motion_vector_info[mb_index]; - this_motion_vector_info->ref_frame[mv_idx] = frame_type; - if (frame_type != INTRA_FRAME) { - this_motion_vector_info->mv[mv_idx].as_mv = *mv; - } -} -#endif // CONFIG_RATE_CTRL - #define NZ_MOTION_PENALTY 128 #define INTRA_MODE_PENALTY 1024 void vp9_first_pass_encode_tile_mb_row(VP9_COMP *cpi, ThreadData *td, @@ -1118,13 +1102,6 @@ struct buf_2d unscaled_last_source_buf_2d; vp9_variance_fn_ptr_t v_fn_ptr = cpi->fn_ptr[bsize]; -#if CONFIG_RATE_CTRL - if (cpi->oxcf.use_simple_encode_api) { - // Store zero mv as default - store_fp_motion_vector(cpi, &mv, mb_row, mb_col, LAST_FRAME, 0); - } -#endif // CONFIG_RAGE_CTRL - xd->plane[0].pre[0].buf = first_ref_buf->y_buffer + recon_yoffset; #if CONFIG_VP9_HIGHBITDEPTH if (xd->cur_buf->flags & YV12_FLAG_HIGHBITDEPTH) { @@ -1189,11 +1166,6 @@ vp9_get_mvpred_var(x, &tmp_mv, &zero_mv, &v_fn_ptr, 0); } } -#if CONFIG_RATE_CTRL - if (cpi->oxcf.use_simple_encode_api) { - store_fp_motion_vector(cpi, &mv, mb_row, mb_col, LAST_FRAME, 0); - } -#endif // CONFIG_RAGE_CTRL // Search in an older reference frame. if ((cm->current_video_frame > 1) && gld_yv12 != NULL) { @@ -1215,12 +1187,6 @@ #endif // CONFIG_VP9_HIGHBITDEPTH first_pass_motion_search(cpi, x, &zero_mv, &tmp_mv, &gf_motion_error); -#if CONFIG_RATE_CTRL - if (cpi->oxcf.use_simple_encode_api) { - store_fp_motion_vector(cpi, &tmp_mv, mb_row, mb_col, GOLDEN_FRAME, - 1); - } -#endif // CONFIG_RAGE_CTRL if (gf_motion_error < motion_error && gf_motion_error < this_error) ++(fp_acc_data->second_ref_count); @@ -1346,11 +1312,6 @@ } } else { fp_acc_data->sr_coded_error += (int64_t)this_error; -#if CONFIG_RATE_CTRL - if (cpi->oxcf.use_simple_encode_api) { - store_fp_motion_vector(cpi, NULL, mb_row, mb_col, INTRA_FRAME, 0); - } -#endif // CONFIG_RAGE_CTRL } fp_acc_data->coded_error += (int64_t)this_error; @@ -1380,13 +1341,6 @@ // Tiling is ignored in the first pass. vp9_tile_init(tile, cm, 0, 0); tile_data.firstpass_top_mv = zero_mv; -#if CONFIG_RATE_CTRL - if (cpi->oxcf.use_simple_encode_api) { - fp_motion_vector_info_reset(cpi->frame_info.frame_width, - cpi->frame_info.frame_height, - cpi->fp_motion_vector_info); - } -#endif for (mb_row = 0; mb_row < cm->mb_rows; ++mb_row) { best_ref_mv = zero_mv; @@ -1420,7 +1374,7 @@ assert(frame_is_intra_only(cm) || (lst_yv12 != NULL)); set_first_pass_params(cpi); - vp9_set_quantizer(cpi, find_fp_qindex(cm->bit_depth)); + vp9_set_quantizer(cpi, find_fp_qindex(cm->bit_depth), 0); vp9_setup_block_planes(&x->e_mbd, cm->subsampling_x, cm->subsampling_y); @@ -1582,9 +1536,9 @@ last_group_rate_err = (double)twopass->rolling_arf_group_actual_bits / DOUBLE_DIVIDE_CHECK((double)twopass->rolling_arf_group_target_bits); - last_group_rate_err = VPXMAX(0.25, VPXMIN(4.0, last_group_rate_err)); + last_group_rate_err = fclamp(last_group_rate_err, 0.25, 4.0); twopass->bpm_factor *= (3.0 + last_group_rate_err) / 4.0; - twopass->bpm_factor = VPXMAX(0.25, VPXMIN(4.0, twopass->bpm_factor)); + twopass->bpm_factor = fclamp(twopass->bpm_factor, 0.25, 4.0); #endif if (target_rate <= 0) { @@ -1608,9 +1562,9 @@ last_group_rate_err = (double)twopass->rolling_arf_group_actual_bits / DOUBLE_DIVIDE_CHECK((double)twopass->rolling_arf_group_target_bits); - last_group_rate_err = VPXMAX(0.25, VPXMIN(4.0, last_group_rate_err)); + last_group_rate_err = fclamp(last_group_rate_err, 0.25, 4.0); twopass->bpm_factor *= (3.0 + last_group_rate_err) / 4.0; - twopass->bpm_factor = VPXMAX(0.25, VPXMIN(4.0, twopass->bpm_factor)); + twopass->bpm_factor = fclamp(twopass->bpm_factor, 0.25, 4.0); #endif // Try and pick a max Q that will be high enough to encode the @@ -2626,9 +2580,6 @@ * rc->frames_since_key * rc->source_alt_ref_active * - * Special case: if CONFIG_RATE_CTRL is true, the external arf indexes will - * determine the arf position. - * * TODO(angiebird): Separate the dynamic fields and static fields into two * structs. */ @@ -2746,21 +2697,6 @@ return gop_coding_frames; } -static RANGE get_active_gf_inverval_range_simple(int min_gf_interval, - int arf_active_or_kf, - int frames_to_key) { - RANGE active_gf_interval; - active_gf_interval.min = min_gf_interval + arf_active_or_kf + 2; - active_gf_interval.max = 16 + arf_active_or_kf; - - if ((active_gf_interval.max <= frames_to_key) && - (active_gf_interval.max >= (frames_to_key - min_gf_interval))) { - active_gf_interval.min = frames_to_key / 2; - active_gf_interval.max = frames_to_key / 2; - } - return active_gf_interval; -} - static RANGE get_active_gf_inverval_range( const FRAME_INFO *frame_info, const RATE_CONTROL *rc, int arf_active_or_kf, int gf_start_show_idx, int active_worst_quality, int last_boosted_qindex) { @@ -2883,14 +2819,9 @@ vpx_clear_system_state(); - if (oxcf->use_simple_encode_api) { - active_gf_interval = get_active_gf_inverval_range_simple( - rc->min_gf_interval, arf_active_or_kf, rc->frames_to_key); - } else { - active_gf_interval = get_active_gf_inverval_range( - frame_info, rc, arf_active_or_kf, gf_start_show_idx, - twopass->active_worst_quality, rc->last_boosted_qindex); - } + active_gf_interval = get_active_gf_inverval_range( + frame_info, rc, arf_active_or_kf, gf_start_show_idx, + twopass->active_worst_quality, rc->last_boosted_qindex); if (cpi->multi_layer_arf) { int arf_layers = get_arf_layers(cpi->multi_layer_arf, oxcf->enable_auto_arf, @@ -2911,19 +2842,6 @@ use_alt_ref = gop_decision.use_alt_ref; } -#if CONFIG_RATE_CTRL - // If the external gop_command is on, we will override the decisions - // of gop_coding_frames and use_alt_ref. - if (cpi->oxcf.use_simple_encode_api) { - const GOP_COMMAND *gop_command = &cpi->encode_command.gop_command; - assert(allow_alt_ref == 1); - if (gop_command->use) { - gop_coding_frames = gop_command_coding_frame_count(gop_command); - use_alt_ref = gop_command->use_alt_ref; - } - } -#endif - // Was the group length constrained by the requirement for a new KF? rc->constrained_gf_group = (gop_coding_frames >= rc->frames_to_key) ? 1 : 0; @@ -3015,11 +2933,6 @@ int show_idx = gf_start_show_idx + j; const FIRSTPASS_STATS *frame_stats = fps_get_frame_stats(first_pass_info, show_idx); - // TODO(b/345831640): Why do we set gop_coding_frames as the upperbound of - // the for loop here? gop_coding_frames does not reflect the "show frame - // count" in a GOP. Therefore, it's possible to get a NULL pointer from - // fps_get_frame_stats(). Here we mitigate the issue using break whenever - // frame_stats == NULL. Show we set the upperbound to show frame count? if (frame_stats == NULL) { if (cpi->ext_ratectrl.ready && (cpi->ext_ratectrl.funcs.rc_type & VPX_RC_GOP) != 0 && @@ -3031,9 +2944,7 @@ // computing gf_group_err which will be used to compute gf_group_bits // for libvpx internal rate control. Since ext_ratectrl is using // external rate control module, this part becomes non-critical. - // Hence, we can safely turn off this error reporting. In the future, - // we should refactor the code so that this part is not used by - // ext_ratectrl. + // Hence, we can safely turn off this error reporting. break; } vpx_internal_error(&cm->error, VPX_CODEC_ERROR, @@ -3507,8 +3418,8 @@ kf_boost_scan_frames = (int)(VPXMAX(64 * zero_motion_avg - 16, 160 * motion_compensable_avg - 112)); kf_boost_scan_frames = - VPXMAX(VPXMIN(kf_boost_scan_frames, MAX_SCAN_FRAMES_FOR_KF_BOOST), - MIN_SCAN_FRAMES_FOR_KF_BOOST); + clamp(kf_boost_scan_frames, MIN_SCAN_FRAMES_FOR_KF_BOOST, + MAX_SCAN_FRAMES_FOR_KF_BOOST); } reset_fpf_position(twopass, start_position); @@ -3936,146 +3847,6 @@ } } -#if CONFIG_RATE_CTRL -void vp9_get_next_group_of_picture(const VP9_COMP *cpi, int *first_is_key_frame, - int *use_alt_ref, int *coding_frame_count, - int *first_show_idx, - int *last_gop_use_alt_ref) { - const GOP_COMMAND *gop_command = &cpi->encode_command.gop_command; - // We make a copy of rc here because we want to get information from the - // encoder without changing its state. - // TODO(angiebird): Avoid copying rc here. - RATE_CONTROL rc = cpi->rc; - const int multi_layer_arf = 0; - const int allow_alt_ref = 1; - // We assume that current_video_frame is updated to the show index of the - // frame we are about to called. Note that current_video_frame is updated at - // the end of encode_frame_to_data_rate(). - // TODO(angiebird): Avoid this kind of fragile style. - *first_show_idx = cpi->common.current_video_frame; - *last_gop_use_alt_ref = rc.source_alt_ref_active; - - *first_is_key_frame = 0; - if (rc.frames_to_key == 0) { - rc.frames_to_key = vp9_get_frames_to_next_key( - &cpi->oxcf, &cpi->twopass, *first_show_idx, rc.min_gf_interval); - rc.frames_since_key = 0; - *first_is_key_frame = 1; - } - - if (gop_command->use) { - *coding_frame_count = gop_command_coding_frame_count(gop_command); - *use_alt_ref = gop_command->use_alt_ref; - assert(gop_command->show_frame_count <= rc.frames_to_key); - } else { - *coding_frame_count = vp9_get_gop_coding_frame_count( - &cpi->oxcf, &cpi->twopass, &cpi->frame_info, &rc, *first_show_idx, - multi_layer_arf, allow_alt_ref, *first_is_key_frame, - *last_gop_use_alt_ref, use_alt_ref); - } -} - -int vp9_get_gop_coding_frame_count(const VP9EncoderConfig *oxcf, - const TWO_PASS *const twopass, - const FRAME_INFO *frame_info, - const RATE_CONTROL *rc, int show_idx, - int multi_layer_arf, int allow_alt_ref, - int first_is_key_frame, - int last_gop_use_alt_ref, int *use_alt_ref) { - int frame_count; - double gop_intra_factor; - const int arf_active_or_kf = last_gop_use_alt_ref || first_is_key_frame; - RANGE active_gf_interval; - int arf_layers; - int end_of_sequence = 0; - if (oxcf->use_simple_encode_api) { - active_gf_interval = get_active_gf_inverval_range_simple( - rc->min_gf_interval, arf_active_or_kf, rc->frames_to_key); - } else { - active_gf_interval = get_active_gf_inverval_range( - frame_info, rc, arf_active_or_kf, show_idx, /*active_worst_quality=*/0, - /*last_boosted_qindex=*/0); - } - - arf_layers = get_arf_layers(multi_layer_arf, oxcf->enable_auto_arf, - active_gf_interval.max); - if (multi_layer_arf) { - gop_intra_factor = 1.0 + 0.25 * arf_layers; - } else { - gop_intra_factor = 1.0; - } - - frame_count = get_gop_coding_frame_num( - use_alt_ref, frame_info, twopass, rc, show_idx, &active_gf_interval, - gop_intra_factor, oxcf->lag_in_frames, &end_of_sequence); - *use_alt_ref &= allow_alt_ref; - return frame_count; -} - -// Under CONFIG_RATE_CTRL, once the first_pass_info is ready, the number of -// coding frames (including show frame and alt ref) can be determined. -int vp9_get_coding_frame_num(const VP9EncoderConfig *oxcf, - const TWO_PASS *const twopass, - const FRAME_INFO *frame_info, int multi_layer_arf, - int allow_alt_ref) { - const FIRST_PASS_INFO *first_pass_info = &twopass->first_pass_info; - int coding_frame_num = 0; - RATE_CONTROL rc; - int gop_coding_frame_count; - int gop_show_frames; - int show_idx = 0; - int last_gop_use_alt_ref = 0; - vp9_rc_init(oxcf, 1, &rc); - - while (show_idx < first_pass_info->num_frames) { - int use_alt_ref; - int first_is_key_frame = 0; - if (rc.frames_to_key == 0) { - rc.frames_to_key = vp9_get_frames_to_next_key(oxcf, twopass, show_idx, - rc.min_gf_interval); - rc.frames_since_key = 0; - first_is_key_frame = 1; - } - - gop_coding_frame_count = vp9_get_gop_coding_frame_count( - oxcf, twopass, frame_info, &rc, show_idx, multi_layer_arf, - allow_alt_ref, first_is_key_frame, last_gop_use_alt_ref, &use_alt_ref); - - rc.source_alt_ref_active = use_alt_ref; - last_gop_use_alt_ref = use_alt_ref; - gop_show_frames = gop_coding_frame_count - use_alt_ref; - rc.frames_to_key -= gop_show_frames; - rc.frames_since_key += gop_show_frames; - show_idx += gop_show_frames; - coding_frame_num += gop_show_frames + use_alt_ref; - } - return coding_frame_num; -} - -void vp9_get_key_frame_map(const VP9EncoderConfig *oxcf, - const TWO_PASS *const twopass, int *key_frame_map) { - const FIRST_PASS_INFO *first_pass_info = &twopass->first_pass_info; - int show_idx = 0; - RATE_CONTROL rc; - vp9_rc_init(oxcf, 1, &rc); - - // key_frame_map points to an int array with size equal to - // first_pass_info->num_frames, which is also the number of show frames in the - // video. - memset(key_frame_map, 0, - sizeof(*key_frame_map) * first_pass_info->num_frames); - while (show_idx < first_pass_info->num_frames) { - int key_frame_group_size; - key_frame_map[show_idx] = 1; - key_frame_group_size = - vp9_get_frames_to_next_key(oxcf, twopass, show_idx, rc.min_gf_interval); - assert(key_frame_group_size > 0); - show_idx += key_frame_group_size; - } - assert(show_idx == first_pass_info->num_frames); -} -#endif // CONFIG_RATE_CTRL - FIRSTPASS_STATS vp9_get_frame_stats(const TWO_PASS *twopass) { return twopass->this_frame_stats; }
diff --git a/vp9/encoder/vp9_firstpass.h b/vp9/encoder/vp9_firstpass.h index 94b0837..9fdd5fc 100644 --- a/vp9/encoder/vp9_firstpass.h +++ b/vp9/encoder/vp9_firstpass.h
@@ -227,54 +227,6 @@ int vp9_get_frames_to_next_key(const struct VP9EncoderConfig *oxcf, const TWO_PASS *const twopass, int kf_show_idx, int min_gf_interval); -#if CONFIG_RATE_CTRL -/* Call this function to get info about the next group of pictures. - * This function should be called after vp9_create_compressor() when encoding - * starts or after vp9_get_compressed_data() when the encoding process of - * the last group of pictures is just finished. - */ -void vp9_get_next_group_of_picture(const struct VP9_COMP *cpi, - int *first_is_key_frame, int *use_alt_ref, - int *coding_frame_count, int *first_show_idx, - int *last_gop_use_alt_ref); - -/*!\brief Call this function before coding a new group of pictures to get - * information about it. - * \param[in] oxcf Encoder config - * \param[in] twopass Twopass info - * \param[in] frame_info Frame info - * \param[in] rc Rate control state - * \param[in] show_idx Show index of the first frame in the group - * \param[in] multi_layer_arf Is multi-layer alternate reference used - * \param[in] allow_alt_ref Is alternate reference allowed - * \param[in] first_is_key_frame Is the first frame in the group a key frame - * \param[in] last_gop_use_alt_ref Does the last group use alternate reference - * - * \param[out] use_alt_ref Does this group use alternate reference - * - * \return Returns coding frame count - */ -int vp9_get_gop_coding_frame_count(const struct VP9EncoderConfig *oxcf, - const TWO_PASS *const twopass, - const FRAME_INFO *frame_info, - const RATE_CONTROL *rc, int show_idx, - int multi_layer_arf, int allow_alt_ref, - int first_is_key_frame, - int last_gop_use_alt_ref, int *use_alt_ref); - -int vp9_get_coding_frame_num(const struct VP9EncoderConfig *oxcf, - const TWO_PASS *const twopass, - const FRAME_INFO *frame_info, int multi_layer_arf, - int allow_alt_ref); - -/*!\brief Compute a key frame binary map indicates whether key frames appear - * in the corresponding positions. The passed in key_frame_map must point to an - * integer array with length equal to twopass->first_pass_info.num_frames, - * which is the number of show frames in the video. - */ -void vp9_get_key_frame_map(const struct VP9EncoderConfig *oxcf, - const TWO_PASS *const twopass, int *key_frame_map); -#endif // CONFIG_RATE_CTRL FIRSTPASS_STATS vp9_get_frame_stats(const TWO_PASS *twopass); FIRSTPASS_STATS vp9_get_total_stats(const TWO_PASS *twopass);
diff --git a/vp9/encoder/vp9_lookahead.c b/vp9/encoder/vp9_lookahead.c index b6be4f8..ba4fe3d 100644 --- a/vp9/encoder/vp9_lookahead.c +++ b/vp9/encoder/vp9_lookahead.c
@@ -132,6 +132,7 @@ vpx_free_frame_buffer(&buf->img); buf->img = new_img; } else if (new_dimensions) { + int aligned_width = ALIGN_POWER_OF_TWO(width, 3); buf->img.y_width = src->y_width; buf->img.y_height = src->y_height; buf->img.uv_width = src->uv_width; @@ -142,6 +143,13 @@ buf->img.uv_crop_height = src->uv_crop_height; buf->img.subsampling_x = src->subsampling_x; buf->img.subsampling_y = src->subsampling_y; + // Here the new width (src->y_crop_width) is <= the previous width + // (since otherwise it would enter the "larger_dimensions" code), so + // it is safe here to update the stride. + // The stride setting is taken from vpx_alloc_frame_buffer(). + buf->img.y_stride = + ALIGN_POWER_OF_TWO((aligned_width + 2 * buf->img.border), 5); + buf->img.uv_stride = buf->img.y_stride >> subsampling_x; } vp9_copy_and_extend_frame(src, &buf->img);
diff --git a/vp9/encoder/vp9_mcomp.c b/vp9/encoder/vp9_mcomp.c index cbe1c40..1f7f174 100644 --- a/vp9/encoder/vp9_mcomp.c +++ b/vp9/encoder/vp9_mcomp.c
@@ -1868,8 +1868,8 @@ // Check size of proposed first range against magnitude of the centre // value used as a starting point. - range = VPXMAX(range, (5 * VPXMAX(abs(temp_mv.row), abs(temp_mv.col))) / 4); - range = VPXMIN(range, MAX_RANGE); + range = clamp(range, (5 * VPXMAX(abs(temp_mv.row), abs(temp_mv.col))) / 4, + MAX_RANGE); interval = VPXMAX(interval, range / baseline_interval_divisor); // initial search @@ -2629,8 +2629,8 @@ // Check size of proposed first range against magnitude of the centre // value used as a starting point. - range = VPXMAX(range, (5 * VPXMAX(abs(temp_mv.row), abs(temp_mv.col))) / 4); - range = VPXMIN(range, MAX_RANGE); + range = clamp(range, (5 * VPXMAX(abs(temp_mv.row), abs(temp_mv.col))) / 4, + MAX_RANGE); interval = VPXMAX(interval, range / baseline_interval_divisor); // initial search
diff --git a/vp9/encoder/vp9_pickmode.c b/vp9/encoder/vp9_pickmode.c index d561b50..b47dc6c 100644 --- a/vp9/encoder/vp9_pickmode.c +++ b/vp9/encoder/vp9_pickmode.c
@@ -12,6 +12,7 @@ #include <limits.h> #include <math.h> #include <stdio.h> +#include <stdlib.h> #include "./vp9_rtcd.h" #include "./vpx_dsp_rtcd.h" @@ -128,15 +129,24 @@ !cpi->svc.layer_context[cpi->svc.temporal_layer_id].is_key_frame && ref_frame == LAST_FRAME) { // Get base layer mv. - MV_REF *candidate = - &cm->prev_frame - ->mvs[(mi_col >> 1) + (mi_row >> 1) * (cm->mi_cols >> 1)]; - if (candidate->mv[0].as_int != INVALID_MV) { - base_mv->as_mv.row = (candidate->mv[0].as_mv.row * 2); - base_mv->as_mv.col = (candidate->mv[0].as_mv.col * 2); - clamp_mv_ref(&base_mv->as_mv, xd); - } else { - base_mv->as_int = INVALID_MV; + const int prev_layer = cpi->svc.spatial_layer_id - 1; + const int index = + (mi_col >> 1) + (mi_row >> 1) * cpi->svc.mi_cols[prev_layer]; + // prev_frame->mvs[] is allocated to size mi_cols * mi_rows corresponding + // to the previous spatial layer, so the index check is against + // svc.mi_col/rows[prev_layer]. + if (index < cpi->svc.mi_cols[prev_layer] * cpi->svc.mi_rows[prev_layer]) { + MV_REF *candidate = &cm->prev_frame->mvs[index]; + // Avoid using base_mv if scaled mv is out of range, for either component. + if (candidate->mv[0].as_int != INVALID_MV && + abs(candidate->mv[0].as_mv.row) <= INT16_MAX >> 1 && + abs(candidate->mv[0].as_mv.col) <= INT16_MAX >> 1) { + base_mv->as_mv.row = candidate->mv[0].as_mv.row * 2; + base_mv->as_mv.col = candidate->mv[0].as_mv.col * 2; + clamp_mv_ref(&base_mv->as_mv, xd); + } else { + base_mv->as_int = INVALID_MV; + } } }
diff --git a/vp9/encoder/vp9_quantize.c b/vp9/encoder/vp9_quantize.c index d37e020..a1e0b44 100644 --- a/vp9/encoder/vp9_quantize.c +++ b/vp9/encoder/vp9_quantize.c
@@ -289,7 +289,7 @@ vp9_init_plane_quantizers(cpi, &cpi->td.mb); } -void vp9_set_quantizer(VP9_COMP *cpi, int q) { +void vp9_set_quantizer(VP9_COMP *cpi, int q, int ext_rc_delta_q_uv) { VP9_COMMON *cm = &cpi->common; // quantizer has to be reinitialized with vp9_init_quantizer() if any // delta_q changes. @@ -297,6 +297,13 @@ cm->y_dc_delta_q = 0; cm->uv_dc_delta_q = 0; cm->uv_ac_delta_q = 0; + + if (ext_rc_delta_q_uv != 0) { + cm->uv_dc_delta_q = cm->uv_ac_delta_q = ext_rc_delta_q_uv; + vp9_init_quantizer(cpi); + return; + } + if (cpi->oxcf.delta_q_uv != 0) { cm->uv_dc_delta_q = cm->uv_ac_delta_q = cpi->oxcf.delta_q_uv; vp9_init_quantizer(cpi);
diff --git a/vp9/encoder/vp9_quantize.h b/vp9/encoder/vp9_quantize.h index f626f06..bca2e05 100644 --- a/vp9/encoder/vp9_quantize.h +++ b/vp9/encoder/vp9_quantize.h
@@ -46,7 +46,7 @@ void vp9_init_quantizer(struct VP9_COMP *cpi); -void vp9_set_quantizer(struct VP9_COMP *cm, int q); +void vp9_set_quantizer(struct VP9_COMP *cpi, int q, int ext_rc_delta_q_uv); int vp9_quantizer_to_qindex(int quantizer);
diff --git a/vp9/encoder/vp9_ratectrl.c b/vp9/encoder/vp9_ratectrl.c index e95bcc8..f6ebf0c 100644 --- a/vp9/encoder/vp9_ratectrl.c +++ b/vp9/encoder/vp9_ratectrl.c
@@ -1299,6 +1299,30 @@ } else { q = vp9_rc_regulate_q(cpi, rc->this_frame_target, active_best_quality, active_worst_quality); + + // For no lookahead: if buffer_level indicates overshoot, then avoid going + // to very low QP. This reduces overshoot observed in Issue: 376707227. + // Note the buffer_level is updated for every encoded frame as: + // buffer_level - starting_buffer_level += (avg_frame_bandwidth - + // encoded_frame_size). So normalizing this with framerate and #encoded + // frames (current_video_frame) gives the difference/error between target + // and encoding bitrate. The additional avg_frame_bandwidth term is to + // compensate for the pre-encoded buffer update (in + // vp9_rc_get_one_pass_vbr_params). + const int qp_thresh = 32; + const int64_t bitrate_err = + (int64_t)(cpi->framerate * + (rc->buffer_level - rc->starting_buffer_level - + rc->avg_frame_bandwidth) / + (cm->current_video_frame + 1)); + // Threshold may be tuned, but for now condition this on low QP. + if (cpi->oxcf.lag_in_frames == 0 && bitrate_err / 1000 < -10 && + qp_thresh < rc->worst_quality && + (q < qp_thresh || *top_index < qp_thresh)) { + q = qp_thresh; + *top_index = VPXMAX(*top_index, q); + } + if (q > *top_index) { // Special case when we are targeting the max allowed rate if (rc->this_frame_target >= rc->max_frame_bandwidth) @@ -1710,14 +1734,6 @@ rate_thresh_mult[rc->frame_size_selector]); } -#if CONFIG_RATE_CTRL - if (cpi->oxcf.use_simple_encode_api) { - if (cpi->encode_command.use_external_target_frame_bits) { - rc->this_frame_target = cpi->encode_command.target_frame_bits; - } - } -#endif // CONFIG_RATE_CTRL - // Target rate per SB64 (including partial SB64s. const int64_t sb64_target_rate = ((int64_t)rc->this_frame_target * 64 * 64) / (cm->width * cm->height); @@ -2126,6 +2142,7 @@ else target = vp9_calc_pframe_target_size_one_pass_vbr(cpi); vp9_rc_set_frame_target(cpi, target); + if (cm->show_frame) vp9_update_buffer_level_preencode(cpi); if (cpi->oxcf.aq_mode == CYCLIC_REFRESH_AQ && cpi->oxcf.pass == 0) vp9_cyclic_refresh_update_parameters(cpi); } @@ -2138,15 +2155,16 @@ const int64_t one_pct_bits = 1 + rc->optimal_buffer_level / 100; int min_frame_target = VPXMAX(rc->avg_frame_bandwidth >> 4, FRAME_OVERHEAD_BITS); - int target; + int64_t target; if (oxcf->gf_cbr_boost_pct) { const int af_ratio_pct = oxcf->gf_cbr_boost_pct + 100; target = cpi->refresh_golden_frame - ? (rc->avg_frame_bandwidth * rc->baseline_gf_interval * - af_ratio_pct) / + ? ((int64_t)rc->avg_frame_bandwidth * + rc->baseline_gf_interval * af_ratio_pct) / (rc->baseline_gf_interval * 100 + af_ratio_pct - 100) - : (rc->avg_frame_bandwidth * rc->baseline_gf_interval * 100) / + : ((int64_t)rc->avg_frame_bandwidth * + rc->baseline_gf_interval * 100) / (rc->baseline_gf_interval * 100 + af_ratio_pct - 100); } else { target = rc->avg_frame_bandwidth; @@ -2164,19 +2182,20 @@ if (diff > 0) { // Lower the target bandwidth for this frame. const int pct_low = (int)VPXMIN(diff / one_pct_bits, oxcf->under_shoot_pct); - target -= (int)(((int64_t)target * pct_low) / 200); + target -= (target * pct_low) / 200; } else if (diff < 0) { // Increase the target bandwidth for this frame. const int pct_high = (int)VPXMIN(-diff / one_pct_bits, oxcf->over_shoot_pct); - target += (int)(((int64_t)target * pct_high) / 200); + target += (target * pct_high) / 200; } if (oxcf->rc_max_inter_bitrate_pct) { - const int max_rate = - rc->avg_frame_bandwidth * oxcf->rc_max_inter_bitrate_pct / 100; + const int64_t max_rate = + (int64_t)rc->avg_frame_bandwidth * oxcf->rc_max_inter_bitrate_pct / 100; target = VPXMIN(target, max_rate); } - return VPXMAX(min_frame_target, target); + if (target > INT_MAX) target = INT_MAX; + return VPXMAX(min_frame_target, (int)target); } int vp9_calc_iframe_target_size_one_pass_cbr(const VP9_COMP *cpi) { @@ -2585,13 +2604,6 @@ // Set Maximum gf/arf interval rc->max_gf_interval = oxcf->max_gf_interval; rc->min_gf_interval = oxcf->min_gf_interval; -#if CONFIG_RATE_CTRL - if (oxcf->use_simple_encode_api) { - // In this experiment, we avoid framerate being changed dynamically during - // encoding. - framerate = oxcf->init_framerate; - } -#endif // CONFIG_RATE_CTRL if (rc->min_gf_interval == 0) { rc->min_gf_interval = vp9_rc_get_default_min_gf_interval( oxcf->width, oxcf->height, framerate); @@ -3254,12 +3266,22 @@ double rate_correction_factor = cpi->rc.rate_correction_factors[INTER_NORMAL]; const int target_size = cpi->rc.avg_frame_bandwidth; + const uint64_t sad_thr = 64 * 64 * 32; + int force_maxqp = 1; double new_correction_factor; int target_bits_per_mb; double q2; int enumerator; - // Force a re-encode, and for now use max-QP. - *q = cpi->rc.worst_quality; + // Set a larger QP. + if (cpi->oxcf.content != VP9E_CONTENT_SCREEN && + cm->width * cm->height >= 1280 * 720 && + (rc->buffer_level > (3 * rc->optimal_buffer_level) >> 2) && + (cpi->rc.avg_source_sad[0] < sad_thr)) { + *q = (*q + cpi->rc.worst_quality) >> 1; + force_maxqp = 0; + } else { + *q = cpi->rc.worst_quality; + } cpi->cyclic_refresh->counter_encode_maxq_scene_change = 0; cpi->rc.re_encode_maxq_scene_change = 1; // If the frame_size is much larger than the threshold (big content change) @@ -3333,7 +3355,7 @@ lrc->rc_1_frame = 0; lrc->rc_2_frame = 0; lrc->rate_correction_factors[INTER_NORMAL] = rate_correction_factor; - lrc->force_max_q = 1; + lrc->force_max_q = force_maxqp; } } }
diff --git a/vp9/encoder/vp9_rdopt.c b/vp9/encoder/vp9_rdopt.c index 260aa0a..93a4d96 100644 --- a/vp9/encoder/vp9_rdopt.c +++ b/vp9/encoder/vp9_rdopt.c
@@ -2319,8 +2319,7 @@ if (!subpelmv && have_ref && ref_bsi->rdstat[block][mode_idx].brdcost < INT64_MAX) { - memcpy(&bsi->rdstat[block][mode_idx], - &ref_bsi->rdstat[block][mode_idx], sizeof(SEG_RDSTAT)); + bsi->rdstat[block][mode_idx] = ref_bsi->rdstat[block][mode_idx]; if (num_4x4_blocks_wide > 1) bsi->rdstat[block + 1][mode_idx].eobs = ref_bsi->rdstat[block + 1][mode_idx].eobs; @@ -4893,8 +4892,7 @@ if (!is_inter_block(&best_mbmode)) { for (i = 0; i < 4; i++) xd->mi[0]->bmi[i].as_mode = best_bmodes[i].as_mode; } else { - for (i = 0; i < 4; ++i) - memcpy(&xd->mi[0]->bmi[i], &best_bmodes[i], sizeof(b_mode_info)); + for (i = 0; i < 4; ++i) xd->mi[0]->bmi[i] = best_bmodes[i]; mi->mv[0].as_int = xd->mi[0]->bmi[3].as_mv[0].as_int; mi->mv[1].as_int = xd->mi[0]->bmi[3].as_mv[1].as_int;
diff --git a/vp9/encoder/vp9_svc_layercontext.c b/vp9/encoder/vp9_svc_layercontext.c index 6bc329a..6e9405e 100644 --- a/vp9/encoder/vp9_svc_layercontext.c +++ b/vp9/encoder/vp9_svc_layercontext.c
@@ -457,7 +457,13 @@ int *height_out) { int w, h; - if (width_out == NULL || height_out == NULL || den == 0) return; + if (width_out == NULL || height_out == NULL) return; + + if (den == 0 || num == 0) { + *width_out = width_org; + *height_out = height_org; + return; + } w = width_org * num / den; h = height_org * num / den; @@ -787,9 +793,6 @@ svc->use_partition_reuse = 1; } svc->force_zero_mode_spatial_ref = 1; - svc->mi_stride[svc->spatial_layer_id] = cpi->common.mi_stride; - svc->mi_rows[svc->spatial_layer_id] = cpi->common.mi_rows; - svc->mi_cols[svc->spatial_layer_id] = cpi->common.mi_cols; // For constrained_from_above drop mode: before encoding superframe (i.e., // at SL0 frame) check all spatial layers (starting from top) for possible @@ -991,6 +994,9 @@ if (vp9_set_size_literal(cpi, width, height) != 0) return VPX_CODEC_INVALID_PARAM; + svc->mi_stride[svc->spatial_layer_id] = cpi->common.mi_stride; + svc->mi_rows[svc->spatial_layer_id] = cpi->common.mi_rows; + svc->mi_cols[svc->spatial_layer_id] = cpi->common.mi_cols; return 0; }
diff --git a/vp9/encoder/vp9_temporal_filter.c b/vp9/encoder/vp9_temporal_filter.c index 2d696f4..46f36c3 100644 --- a/vp9/encoder/vp9_temporal_filter.c +++ b/vp9/encoder/vp9_temporal_filter.c
@@ -45,36 +45,17 @@ 0U, 991146300U }; #endif // CONFIG_VP9_HIGHBITDEPTH -// Prediction function using 12-tap interpolation filter. -// TODO(yunqingwang@google.com): add SIMD optimization. -#define MAX_FILTER_TAP 12 +static const MV kZeroMv = { 0, 0 }; #define TF_INTERP_EXTEND 6 -typedef int16_t InterpKernel12[MAX_FILTER_TAP]; -// 12-tap filter (used by the encoder only). -DECLARE_ALIGNED(256, static const InterpKernel12, - sub_pel_filters_12[SUBPEL_SHIFTS]) = { - { 0, 0, 0, 0, 0, 128, 0, 0, 0, 0, 0, 0 }, - { 0, 1, -2, 3, -7, 127, 8, -4, 2, -1, 1, 0 }, - { -1, 2, -3, 6, -13, 124, 18, -8, 4, -2, 2, -1 }, - { -1, 3, -4, 8, -18, 120, 28, -12, 7, -4, 2, -1 }, - { -1, 3, -6, 10, -21, 115, 38, -15, 8, -5, 3, -1 }, - { -2, 4, -6, 12, -24, 108, 49, -18, 10, -6, 3, -2 }, - { -2, 4, -7, 13, -25, 100, 60, -21, 11, -7, 4, -2 }, - { -2, 4, -7, 13, -26, 91, 71, -24, 13, -7, 4, -2 }, - { -2, 4, -7, 13, -25, 81, 81, -25, 13, -7, 4, -2 }, - { -2, 4, -7, 13, -24, 71, 91, -26, 13, -7, 4, -2 }, - { -2, 4, -7, 11, -21, 60, 100, -25, 13, -7, 4, -2 }, - { -2, 3, -6, 10, -18, 49, 108, -24, 12, -6, 4, -2 }, - { -1, 3, -5, 8, -15, 38, 115, -21, 10, -6, 3, -1 }, - { -1, 2, -4, 7, -12, 28, 120, -18, 8, -4, 3, -1 }, - { -1, 2, -2, 4, -8, 18, 124, -13, 6, -3, 2, -1 }, - { 0, 1, -1, 2, -4, 8, 127, -7, 3, -2, 1, 0 } -}; -static void convolve_horiz_12(const uint8_t *src, ptrdiff_t src_stride, - uint8_t *dst, ptrdiff_t dst_stride, - const InterpKernel12 *x_filters, int x0_q4, - int x_step_q4, int w, int h) { +// Prediction function using 12-tap interpolation filter. +void vpx_convolve12_horiz_c(const uint8_t *src, ptrdiff_t src_stride, + uint8_t *dst, ptrdiff_t dst_stride, + const InterpKernel12 *filter, int x0_q4, + int x_step_q4, int y0_q4, int y_step_q4, int w, + int h) { + (void)y0_q4; + (void)y_step_q4; int x, y; src -= MAX_FILTER_TAP / 2 - 1; @@ -82,7 +63,7 @@ int x_q4 = x0_q4; for (x = 0; x < w; ++x) { const uint8_t *const src_x = &src[x_q4 >> SUBPEL_BITS]; - const int16_t *const x_filter = x_filters[x_q4 & SUBPEL_MASK]; + const int16_t *const x_filter = filter[x_q4 & SUBPEL_MASK]; int k, sum = 0; for (k = 0; k < MAX_FILTER_TAP; ++k) sum += src_x[k] * x_filter[k]; dst[x] = clip_pixel(ROUND_POWER_OF_TWO(sum, FILTER_BITS)); @@ -93,10 +74,13 @@ } } -static void convolve_vert_12(const uint8_t *src, ptrdiff_t src_stride, - uint8_t *dst, ptrdiff_t dst_stride, - const InterpKernel12 *y_filters, int y0_q4, - int y_step_q4, int w, int h) { +void vpx_convolve12_vert_c(const uint8_t *src, ptrdiff_t src_stride, + uint8_t *dst, ptrdiff_t dst_stride, + const InterpKernel12 *filter, int x0_q4, + int x_step_q4, int y0_q4, int y_step_q4, int w, + int h) { + (void)x0_q4; + (void)x_step_q4; int x, y; src -= src_stride * (MAX_FILTER_TAP / 2 - 1); @@ -104,7 +88,7 @@ int y_q4 = y0_q4; for (y = 0; y < h; ++y) { const uint8_t *src_y = &src[(y_q4 >> SUBPEL_BITS) * src_stride]; - const int16_t *const y_filter = y_filters[y_q4 & SUBPEL_MASK]; + const int16_t *const y_filter = filter[y_q4 & SUBPEL_MASK]; int k, sum = 0; for (k = 0; k < MAX_FILTER_TAP; ++k) sum += src_y[k * src_stride] * y_filter[k]; @@ -117,21 +101,21 @@ } // Copied from vpx_convolve8_c(). Possible block sizes are 32x32, 16x16, 8x8. -static void vpx_convolve8_12_c(const uint8_t *src, ptrdiff_t src_stride, - uint8_t *dst, ptrdiff_t dst_stride, - const InterpKernel12 *filter, int x0_q4, - int x_step_q4, int y0_q4, int y_step_q4, int w, - int h) { +void vpx_convolve12_c(const uint8_t *src, ptrdiff_t src_stride, uint8_t *dst, + ptrdiff_t dst_stride, const InterpKernel12 *filter, + int x0_q4, int x_step_q4, int y0_q4, int y_step_q4, int w, + int h) { uint8_t temp[BW * (BH + MAX_FILTER_TAP - 1)]; const int temp_stride = BW; const int intermediate_height = (((h - 1) * y_step_q4 + y0_q4) >> SUBPEL_BITS) + MAX_FILTER_TAP; - convolve_horiz_12(src - src_stride * (MAX_FILTER_TAP / 2 - 1), src_stride, - temp, temp_stride, filter, x0_q4, x_step_q4, w, - intermediate_height); - convolve_vert_12(temp + temp_stride * (MAX_FILTER_TAP / 2 - 1), temp_stride, - dst, dst_stride, filter, y0_q4, y_step_q4, w, h); + vpx_convolve12_horiz_c(src - src_stride * (MAX_FILTER_TAP / 2 - 1), + src_stride, temp, temp_stride, filter, x0_q4, + x_step_q4, y0_q4, y_step_q4, w, intermediate_height); + vpx_convolve12_vert_c(temp + temp_stride * (MAX_FILTER_TAP / 2 - 1), + temp_stride, dst, dst_stride, filter, x0_q4, x_step_q4, + y0_q4, y_step_q4, w, h); } static void vp9_build_inter_predictor_12( @@ -148,15 +132,29 @@ src += (mv.row >> SUBPEL_BITS) * src_stride + (mv.col >> SUBPEL_BITS); - vpx_convolve8_12_c(src, src_stride, dst, dst_stride, kernel, subpel_x, - sf->x_step_q4, subpel_y, sf->y_step_q4, w, h); + if (subpel_x == 0 && subpel_y == 0) { + vpx_convolve_copy(src, src_stride, dst, dst_stride, NULL, subpel_x, + sf->x_step_q4, subpel_y, sf->y_step_q4, w, h); + } else if (subpel_x == 0 && subpel_y != 0) { + vpx_convolve12_vert(src, src_stride, dst, dst_stride, kernel, subpel_x, + sf->x_step_q4, subpel_y, sf->y_step_q4, w, h); + } else if (subpel_x != 0 && subpel_y == 0) { + vpx_convolve12_horiz(src, src_stride, dst, dst_stride, kernel, subpel_x, + sf->x_step_q4, subpel_y, sf->y_step_q4, w, h); + } else { + vpx_convolve12(src, src_stride, dst, dst_stride, kernel, subpel_x, + sf->x_step_q4, subpel_y, sf->y_step_q4, w, h); + } } #if CONFIG_VP9_HIGHBITDEPTH -static void highbd_convolve_horiz_12(const uint16_t *src, ptrdiff_t src_stride, - uint16_t *dst, ptrdiff_t dst_stride, - const InterpKernel12 *x_filters, int x0_q4, - int x_step_q4, int w, int h, int bd) { +void vpx_highbd_convolve12_horiz_c(const uint16_t *src, ptrdiff_t src_stride, + uint16_t *dst, ptrdiff_t dst_stride, + const InterpKernel12 *filter, int x0_q4, + int x_step_q4, int y0_q4, int y_step_q4, + int w, int h, int bd) { + (void)y0_q4; + (void)y_step_q4; int x, y; src -= MAX_FILTER_TAP / 2 - 1; @@ -164,7 +162,7 @@ int x_q4 = x0_q4; for (x = 0; x < w; ++x) { const uint16_t *const src_x = &src[x_q4 >> SUBPEL_BITS]; - const int16_t *const x_filter = x_filters[x_q4 & SUBPEL_MASK]; + const int16_t *const x_filter = filter[x_q4 & SUBPEL_MASK]; int k, sum = 0; for (k = 0; k < MAX_FILTER_TAP; ++k) sum += src_x[k] * x_filter[k]; dst[x] = clip_pixel_highbd(ROUND_POWER_OF_TWO(sum, FILTER_BITS), bd); @@ -175,10 +173,13 @@ } } -static void highbd_convolve_vert_12(const uint16_t *src, ptrdiff_t src_stride, - uint16_t *dst, ptrdiff_t dst_stride, - const InterpKernel12 *y_filters, int y0_q4, - int y_step_q4, int w, int h, int bd) { +void vpx_highbd_convolve12_vert_c(const uint16_t *src, ptrdiff_t src_stride, + uint16_t *dst, ptrdiff_t dst_stride, + const InterpKernel12 *filter, int x0_q4, + int x_step_q4, int y0_q4, int y_step_q4, + int w, int h, int bd) { + (void)x0_q4; + (void)x_step_q4; int x, y; src -= src_stride * (MAX_FILTER_TAP / 2 - 1); @@ -186,7 +187,7 @@ int y_q4 = y0_q4; for (y = 0; y < h; ++y) { const uint16_t *src_y = &src[(y_q4 >> SUBPEL_BITS) * src_stride]; - const int16_t *const y_filter = y_filters[y_q4 & SUBPEL_MASK]; + const int16_t *const y_filter = filter[y_q4 & SUBPEL_MASK]; int k, sum = 0; for (k = 0; k < MAX_FILTER_TAP; ++k) sum += src_y[k * src_stride] * y_filter[k]; @@ -199,32 +200,33 @@ } } -static void highbd_convolve_12(const uint16_t *src, ptrdiff_t src_stride, - uint16_t *dst, ptrdiff_t dst_stride, - const InterpKernel12 *filter, int x0_q4, - int x_step_q4, int y0_q4, int y_step_q4, int w, - int h, int bd) { +static void highbd_convolve12(const uint16_t *src, ptrdiff_t src_stride, + uint16_t *dst, ptrdiff_t dst_stride, + const InterpKernel12 *filter, int x0_q4, + int x_step_q4, int y0_q4, int y_step_q4, int w, + int h, int bd) { uint16_t temp[BW * (BH + MAX_FILTER_TAP - 1)]; const int temp_stride = BW; const int intermediate_height = (((h - 1) * y_step_q4 + y0_q4) >> SUBPEL_BITS) + MAX_FILTER_TAP; - highbd_convolve_horiz_12(src - src_stride * (MAX_FILTER_TAP / 2 - 1), - src_stride, temp, temp_stride, filter, x0_q4, - x_step_q4, w, intermediate_height, bd); - highbd_convolve_vert_12(temp + temp_stride * (MAX_FILTER_TAP / 2 - 1), - temp_stride, dst, dst_stride, filter, y0_q4, - y_step_q4, w, h, bd); + vpx_highbd_convolve12_horiz_c(src - src_stride * (MAX_FILTER_TAP / 2 - 1), + src_stride, temp, temp_stride, filter, x0_q4, + x_step_q4, y0_q4, y_step_q4, w, + intermediate_height, bd); + vpx_highbd_convolve12_vert_c(temp + temp_stride * (MAX_FILTER_TAP / 2 - 1), + temp_stride, dst, dst_stride, filter, x0_q4, + x_step_q4, y0_q4, y_step_q4, w, h, bd); } // Copied from vpx_highbd_convolve8_c() -static void vpx_highbd_convolve8_12_c(const uint16_t *src, ptrdiff_t src_stride, - uint16_t *dst, ptrdiff_t dst_stride, - const InterpKernel12 *filter, int x0_q4, - int x_step_q4, int y0_q4, int y_step_q4, - int w, int h, int bd) { - highbd_convolve_12(src, src_stride, dst, dst_stride, filter, x0_q4, x_step_q4, - y0_q4, y_step_q4, w, h, bd); +void vpx_highbd_convolve12_c(const uint16_t *src, ptrdiff_t src_stride, + uint16_t *dst, ptrdiff_t dst_stride, + const InterpKernel12 *filter, int x0_q4, + int x_step_q4, int y0_q4, int y_step_q4, int w, + int h, int bd) { + highbd_convolve12(src, src_stride, dst, dst_stride, filter, x0_q4, x_step_q4, + y0_q4, y_step_q4, w, h, bd); } static void vp9_highbd_build_inter_predictor_12( @@ -242,8 +244,21 @@ src += (mv.row >> SUBPEL_BITS) * src_stride + (mv.col >> SUBPEL_BITS); - vpx_highbd_convolve8_12_c(src, src_stride, dst, dst_stride, kernel, subpel_x, - sf->x_step_q4, subpel_y, sf->y_step_q4, w, h, bd); + if (subpel_x == 0 && subpel_y == 0) { + vpx_highbd_convolve_copy(src, src_stride, dst, dst_stride, NULL, subpel_x, + sf->x_step_q4, subpel_y, sf->y_step_q4, w, h, bd); + } else if (subpel_x == 0 && subpel_y != 0) { + vpx_highbd_convolve12_vert(src, src_stride, dst, dst_stride, kernel, + subpel_x, sf->x_step_q4, subpel_y, sf->y_step_q4, + w, h, bd); + } else if (subpel_x != 0 && subpel_y == 0) { + vpx_highbd_convolve12_horiz(src, src_stride, dst, dst_stride, kernel, + subpel_x, sf->x_step_q4, subpel_y, + sf->y_step_q4, w, h, bd); + } else { + vpx_highbd_convolve12(src, src_stride, dst, dst_stride, kernel, subpel_x, + sf->x_step_q4, subpel_y, sf->y_step_q4, w, h, bd); + } } #endif // CONFIG_VP9_HIGHBITDEPTH @@ -754,7 +769,7 @@ static uint32_t temporal_filter_find_matching_mb_c( VP9_COMP *cpi, ThreadData *td, uint8_t *arf_frame_buf, uint8_t *frame_ptr_buf, int stride, MV *ref_mv, MV *blk_mvs, - int *blk_bestsme) { + int *blk_bestsme, int *is_dc_diff_large) { MACROBLOCK *const x = &td->mb; MACROBLOCKD *const xd = &x->e_mbd; MV_SPEED_FEATURES *const mv_sf = &cpi->sf.mv; @@ -784,6 +799,7 @@ x->plane[0].src.stride = stride; xd->plane[0].pre[0].buf = frame_ptr_buf; xd->plane[0].pre[0].stride = stride; + *is_dc_diff_large = 0; step_param = mv_sf->reduce_first_step_size; step_param = VPXMIN(step_param, MAX_MVSEARCH_STEPS - 2); @@ -805,6 +821,7 @@ x->errorperbit, &cpi->fn_ptr[TF_BLOCK], 0, mv_sf->subpel_search_level, cond_cost_list(cpi, cost_list), NULL, NULL, &distortion, &sse, NULL, BW, BH, USE_8_TAPS_SHARP); + *is_dc_diff_large = 50 * bestsme < sse; // DO motion search on 4 16x16 sub_blocks. best_ref_mv1.row = ref_mv->row; @@ -958,12 +975,17 @@ const int thresh_low = 10000; const int thresh_high = 20000; int blk_bestsme[4] = { INT_MAX, INT_MAX, INT_MAX, INT_MAX }; + int is_dc_diff_large = 0; // Find best match in this frame by MC int err = temporal_filter_find_matching_mb_c( cpi, td, frames[alt_ref_index]->y_buffer + mb_y_offset, frames[frame]->y_buffer + mb_y_offset, frames[frame]->y_stride, - &ref_mv, blk_mvs, blk_bestsme); + &ref_mv, blk_mvs, blk_bestsme, &is_dc_diff_large); + + if (cpi->oxcf.enable_keyframe_filtering == 1 && + cpi->common.frame_type == KEY_FRAME && is_dc_diff_large) + strength = VPXMIN(strength, 1); int err16 = blk_bestsme[0] + blk_bestsme[1] + blk_bestsme[2] + blk_bestsme[3]; @@ -1245,7 +1267,7 @@ if (oxcf->pass == 2) { base_strength = oxcf->arnr_strength + cpi->twopass.arnr_strength_adjustment; // Clip to allowed range. - base_strength = VPXMIN(6, VPXMAX(0, base_strength)); + base_strength = clamp(base_strength, 0, 6); } else { base_strength = oxcf->arnr_strength; }
diff --git a/vp9/encoder/vp9_temporal_filter.h b/vp9/encoder/vp9_temporal_filter.h index 553a468..59fb71f 100644 --- a/vp9/encoder/vp9_temporal_filter.h +++ b/vp9/encoder/vp9_temporal_filter.h
@@ -16,7 +16,8 @@ #endif #define ARNR_FILT_QINDEX 128 -static const MV kZeroMv = { 0, 0 }; +struct VP9_COMP; +struct ThreadData; // Block size used in temporal filtering #define TF_BLOCK BLOCK_32X32 @@ -31,13 +32,37 @@ #define TF_SUB_BLOCK BLOCK_16X16 #define SUB_BH 16 #define SUB_BW 16 +#define MAX_FILTER_TAP 12 + +typedef int16_t InterpKernel12[MAX_FILTER_TAP]; + +// 12-tap filter (used by the encoder only). +DECLARE_ALIGNED(256, static const InterpKernel12, + sub_pel_filters_12[SUBPEL_SHIFTS]) = { + { 0, 0, 0, 0, 0, 128, 0, 0, 0, 0, 0, 0 }, + { 0, 1, -2, 3, -7, 127, 8, -4, 2, -1, 1, 0 }, + { -1, 2, -3, 6, -13, 124, 18, -8, 4, -2, 2, -1 }, + { -1, 3, -4, 8, -18, 120, 28, -12, 7, -4, 2, -1 }, + { -1, 3, -6, 10, -21, 115, 38, -15, 8, -5, 3, -1 }, + { -2, 4, -6, 12, -24, 108, 49, -18, 10, -6, 3, -2 }, + { -2, 4, -7, 13, -25, 100, 60, -21, 11, -7, 4, -2 }, + { -2, 4, -7, 13, -26, 91, 71, -24, 13, -7, 4, -2 }, + { -2, 4, -7, 13, -25, 81, 81, -25, 13, -7, 4, -2 }, + { -2, 4, -7, 13, -24, 71, 91, -26, 13, -7, 4, -2 }, + { -2, 4, -7, 11, -21, 60, 100, -25, 13, -7, 4, -2 }, + { -2, 3, -6, 10, -18, 49, 108, -24, 12, -6, 4, -2 }, + { -1, 3, -5, 8, -15, 38, 115, -21, 10, -6, 3, -1 }, + { -1, 2, -4, 7, -12, 28, 120, -18, 8, -4, 3, -1 }, + { -1, 2, -2, 4, -8, 18, 124, -13, 6, -3, 2, -1 }, + { 0, 1, -1, 2, -4, 8, 127, -7, 3, -2, 1, 0 } +}; void vp9_temporal_filter_init(void); -void vp9_temporal_filter(VP9_COMP *cpi, int distance); +void vp9_temporal_filter(struct VP9_COMP *cpi, int distance); -void vp9_temporal_filter_iterate_row_c(VP9_COMP *cpi, ThreadData *td, - int mb_row, int mb_col_start, - int mb_col_end); +void vp9_temporal_filter_iterate_row_c(struct VP9_COMP *cpi, + struct ThreadData *td, int mb_row, + int mb_col_start, int mb_col_end); #ifdef __cplusplus } // extern "C"
diff --git a/vp9/encoder/vp9_tpl_model.c b/vp9/encoder/vp9_tpl_model.c index c40d71e..f65c98f 100644 --- a/vp9/encoder/vp9_tpl_model.c +++ b/vp9/encoder/vp9_tpl_model.c
@@ -78,7 +78,9 @@ for (i = 0; i < 3; ++i) ref_table[i] = -REFS_PER_FRAME; gf_picture[0].frame = - &cm->buffer_pool->frame_bufs[gf_group->update_ref_idx[0]].buf; + &cm->buffer_pool + ->frame_bufs[cm->ref_frame_map[gf_group->update_ref_idx[0]]] + .buf; ref_table[gf_group->update_ref_idx[0]] = 0; for (i = 0; i < 3; ++i) gf_picture[0].ref_frame[i] = -REFS_PER_FRAME; @@ -606,8 +608,9 @@ TplDepStats *tpl_stats, int mi_row, int mi_col, BLOCK_SIZE bsize, int src_stride, int64_t recon_error, - int64_t rate_cost, int ref_frame_idx, - int mi_rows, int mi_cols) { + int64_t pred_error, int64_t rate_cost, + int ref_frame_idx, int mi_rows, + int mi_cols) { const int mi_height = num_8x8_blocks_high_lookup[bsize]; const int mi_width = num_8x8_blocks_wide_lookup[bsize]; const TplDepStats *src_stats = &tpl_stats[mi_row * src_stride + mi_col]; @@ -628,8 +631,11 @@ tpl_block_stats_ptr->intra_pred_err = src_stats->intra_cost; tpl_block_stats_ptr->srcrf_dist = recon_error << TPL_DEP_COST_SCALE_LOG2; tpl_block_stats_ptr->srcrf_rate = rate_cost << TPL_DEP_COST_SCALE_LOG2; - tpl_block_stats_ptr->mv_r = src_stats->mv.as_mv.row; - tpl_block_stats_ptr->mv_c = src_stats->mv.as_mv.col; + tpl_block_stats_ptr->pred_error = pred_error << TPL_DEP_COST_SCALE_LOG2; + tpl_block_stats_ptr->mv_r = (src_stats->mv.as_mv.row >= 0 ? 1 : -1) * + (abs(src_stats->mv.as_mv.row) + 4) / 8; + tpl_block_stats_ptr->mv_c = (src_stats->mv.as_mv.col >= 0 ? 1 : -1) * + (abs(src_stats->mv.as_mv.col) + 4) / 8; tpl_block_stats_ptr->ref_frame_index = ref_frame_idx; } } @@ -1469,7 +1475,7 @@ tpl_store_before_propagation( tpl_frame_stats_before_propagation->block_stats_list, tpl_frame->tpl_stats_ptr, mi_row, mi_col, bsize, tpl_frame->stride, - recon_error, rate_cost, ref_frame_idx, tpl_frame->mi_rows, + recon_error, sse, rate_cost, ref_frame_idx, tpl_frame->mi_rows, tpl_frame->mi_cols); tpl_model_update(cpi->tpl_stats, tpl_frame->tpl_stats_ptr, mi_row, mi_col, @@ -1671,49 +1677,8 @@ free_tpl_frame_stats_list(&cpi->tpl_gop_stats); } -#if CONFIG_RATE_CTRL -static void accumulate_frame_tpl_stats(VP9_COMP *cpi) { - VP9_COMMON *const cm = &cpi->common; - const GF_GROUP *gf_group = &cpi->twopass.gf_group; - int show_frame_count = 0; - int frame_idx; - // Accumulate tpl stats for each frame in the current group of picture. - for (frame_idx = 1; frame_idx < gf_group->gf_group_size; ++frame_idx) { - TplDepFrame *tpl_frame = &cpi->tpl_stats[frame_idx]; - TplDepStats *tpl_stats = tpl_frame->tpl_stats_ptr; - const int tpl_stride = tpl_frame->stride; - int64_t intra_cost_base = 0; - int64_t inter_cost_base = 0; - int64_t mc_dep_cost_base = 0; - int64_t mc_ref_cost_base = 0; - int64_t mc_flow_base = 0; - int row, col; - - if (!tpl_frame->is_valid) continue; - - for (row = 0; row < cm->mi_rows && tpl_frame->is_valid; ++row) { - for (col = 0; col < cm->mi_cols; ++col) { - TplDepStats *this_stats = &tpl_stats[row * tpl_stride + col]; - intra_cost_base += this_stats->intra_cost; - inter_cost_base += this_stats->inter_cost; - mc_dep_cost_base += this_stats->mc_dep_cost; - mc_ref_cost_base += this_stats->mc_ref_cost; - mc_flow_base += this_stats->mc_flow; - } - } - - cpi->tpl_stats_info[show_frame_count].intra_cost = intra_cost_base; - cpi->tpl_stats_info[show_frame_count].inter_cost = inter_cost_base; - cpi->tpl_stats_info[show_frame_count].mc_dep_cost = mc_dep_cost_base; - cpi->tpl_stats_info[show_frame_count].mc_ref_cost = mc_ref_cost_base; - cpi->tpl_stats_info[show_frame_count].mc_flow = mc_flow_base; - - ++show_frame_count; - } -} -#endif // CONFIG_RATE_CTRL - void vp9_estimate_tpl_qp_gop(VP9_COMP *cpi) { + VP9_COMMON *cm = &cpi->common; int gop_length = cpi->twopass.gf_group.gf_group_size; int bottom_index, top_index; int idx; @@ -1721,6 +1686,19 @@ const int is_src_frame_alt_ref = cpi->rc.is_src_frame_alt_ref; const int refresh_frame_context = cpi->common.refresh_frame_context; + const int sb_size = num_8x8_blocks_wide_lookup[BLOCK_64X64] * MI_SIZE; + const int frame_height_sb = (cm->height + sb_size - 1) / sb_size; + const int frame_width_sb = (cm->width + sb_size - 1) / sb_size; + + vpx_codec_err_t codec_status; + const GF_GROUP *gf_group = &cpi->twopass.gf_group; + vpx_rc_encodeframe_decision_t encode_frame_decision; + + CHECK_MEM_ERROR( + &cm->error, encode_frame_decision.sb_params_list, + (sb_params *)vpx_malloc(frame_height_sb * frame_width_sb * + sizeof(*encode_frame_decision.sb_params_list))); + for (idx = gf_index; idx <= gop_length; ++idx) { TplDepFrame *tpl_frame = &cpi->tpl_stats[idx]; int target_rate = cpi->twopass.gf_group.bit_allocation[idx]; @@ -1730,17 +1708,21 @@ if (cpi->ext_ratectrl.ready && (cpi->ext_ratectrl.funcs.rc_type & VPX_RC_QP) != 0 && cpi->ext_ratectrl.funcs.get_encodeframe_decision != NULL) { - VP9_COMMON *cm = &cpi->common; - vpx_codec_err_t codec_status; - const GF_GROUP *gf_group = &cpi->twopass.gf_group; - vpx_rc_encodeframe_decision_t encode_frame_decision; if (idx == gop_length) break; + memset(encode_frame_decision.sb_params_list, 0, + sizeof(*encode_frame_decision.sb_params_list) * frame_height_sb * + frame_width_sb); codec_status = vp9_extrc_get_encodeframe_decision( &cpi->ext_ratectrl, gf_group->index, &encode_frame_decision); if (codec_status != VPX_CODEC_OK) { vpx_internal_error(&cm->error, codec_status, "vp9_extrc_get_encodeframe_decision() failed"); } + for (int i = 0; i < frame_height_sb * frame_width_sb; ++i) { + cpi->sb_mul_scale[i] = + (((int64_t)encode_frame_decision.sb_params_list[i].rdmult * 256) / + (encode_frame_decision.rdmult + 1)); + } tpl_frame->base_qindex = encode_frame_decision.q_index; } else { tpl_frame->base_qindex = vp9_rc_pick_q_and_bounds_two_pass( @@ -1753,6 +1735,8 @@ cpi->rc.is_src_frame_alt_ref = is_src_frame_alt_ref; cpi->common.refresh_frame_context = refresh_frame_context; vp9_configure_buffer_updates(cpi, gf_index); + + vpx_free(encode_frame_decision.sb_params_list); } void vp9_setup_tpl_stats(VP9_COMP *cpi) { @@ -1783,7 +1767,7 @@ if (cpi->ext_ratectrl.ready && cpi->ext_ratectrl.funcs.send_tpl_gop_stats != NULL) { // Intra search on key frame - if (gf_picture[0].update_type != OVERLAY_UPDATE) { + if (gf_group->update_type[0] != OVERLAY_UPDATE) { mc_flow_dispenser(cpi, gf_picture, 0, cpi->tpl_bsize); } // TPL stats has extra frames from next GOP. Trim those extra frames for @@ -1804,10 +1788,4 @@ dump_tpl_stats(cpi, tpl_group_frames, gf_group, gf_picture, cpi->tpl_bsize); #endif // DUMP_TPL_STATS #endif // CONFIG_NON_GREEDY_MV - -#if CONFIG_RATE_CTRL - if (cpi->oxcf.use_simple_encode_api) { - accumulate_frame_tpl_stats(cpi); - } -#endif // CONFIG_RATE_CTRL }
diff --git a/vp9/encoder/x86/highbd_temporal_filter_avx2.c b/vp9/encoder/x86/highbd_temporal_filter_avx2.c new file mode 100644 index 0000000..418edd6 --- /dev/null +++ b/vp9/encoder/x86/highbd_temporal_filter_avx2.c
@@ -0,0 +1,263 @@ +/* + * Copyright (c) 2024 The WebM project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include <assert.h> +#include <immintrin.h> + +#include "./vp9_rtcd.h" +#include "./vpx_dsp_rtcd.h" +#include "vp9/encoder/vp9_temporal_filter.h" + +static INLINE void highbd_shuffle_12tap_filter_avx2(const int16_t *filter, + __m256i *f) { + const __m256i f_low = + _mm256_broadcastsi128_si256(_mm_loadu_si128((const __m128i *)filter)); + const __m256i f_high = _mm256_broadcastsi128_si256( + _mm_loadl_epi64((const __m128i *)(filter + 8))); + + f[0] = _mm256_shuffle_epi32(f_low, 0x00); + f[1] = _mm256_shuffle_epi32(f_low, 0x55); + f[2] = _mm256_shuffle_epi32(f_low, 0xaa); + f[3] = _mm256_shuffle_epi32(f_low, 0xff); + f[4] = _mm256_shuffle_epi32(f_high, 0x00); + f[5] = _mm256_shuffle_epi32(f_high, 0x55); +} + +static INLINE __m256i highbd_convolve_12tap(const __m256i *s, + const __m256i *f) { + const __m256i res_0 = _mm256_madd_epi16(s[0], f[0]); + const __m256i res_1 = _mm256_madd_epi16(s[1], f[1]); + const __m256i res_2 = _mm256_madd_epi16(s[2], f[2]); + const __m256i res_3 = _mm256_madd_epi16(s[3], f[3]); + const __m256i res_4 = _mm256_madd_epi16(s[4], f[4]); + const __m256i res_5 = _mm256_madd_epi16(s[5], f[5]); + + const __m256i res = + _mm256_add_epi32(_mm256_add_epi32(res_0, res_1), + _mm256_add_epi32(_mm256_add_epi32(res_2, res_3), + _mm256_add_epi32(res_4, res_5))); + return res; +} + +static INLINE void reuse_src_data_avx2(const __m256i *src, __m256i *des) { + des[0] = src[0]; + des[1] = src[1]; + des[2] = src[2]; + des[3] = src[3]; + des[4] = src[4]; +} + +void vpx_highbd_convolve12_horiz_avx2(const uint16_t *src, ptrdiff_t src_stride, + uint16_t *dst, ptrdiff_t dst_stride, + const InterpKernel12 *filter, int x0_q4, + int x_step_q4, int y0_q4, int y_step_q4, + int w, int h, int bd) { + assert(x_step_q4 == 16); + (void)y0_q4; + (void)x_step_q4; + (void)y_step_q4; + const uint16_t *src_ptr = src; + src_ptr -= MAX_FILTER_TAP / 2 - 1; + __m256i s[6], f[6]; + const __m256i rounding = _mm256_set1_epi32(1 << (FILTER_BITS - 1)); + const __m256i max = _mm256_set1_epi16((1 << bd) - 1); + highbd_shuffle_12tap_filter_avx2(filter[x0_q4], f); + + for (int j = 0; j < w; j += 8) { + for (int i = 0; i < h; i += 2) { + // s00 s01 s02 s03 s04 s05 s06 s07 s08 s09 s010 s011 s012 s013 s014 s015 + const __m256i row0 = + _mm256_loadu_si256((const __m256i *)&src_ptr[i * src_stride + j]); + // s10 s11 s12 s13 s14 s15 s16 s17 s18 s19 s110 s111 s112 s113 s114 + // s115 + const __m256i row1 = _mm256_loadu_si256( + (const __m256i *)&src_ptr[(i + 1) * src_stride + j]); + // s016 s017 s018 s019 s020 s021 s022 s023 + const __m128i row0_16 = + _mm_loadu_si128((const __m128i *)&src_ptr[i * src_stride + j + 16]); + // s116 s117 s118 s119 s120 s121 s122 s123 + const __m128i row1_16 = _mm_loadu_si128( + (const __m128i *)&src_ptr[(i + 1) * src_stride + j + 16]); + + // s00 s01 s02 s03 s04 s05 s06 s07 | s10 s11 s12 s13 s14 s15 s16 s17 + const __m256i r0 = _mm256_permute2x128_si256(row0, row1, 0x20); + // s08 s09 s010 s011 s012 s013 s014 s015 | s18 s19 s110 s111 s112 s113 + // s114 s115 + const __m256i r1 = _mm256_permute2x128_si256(row0, row1, 0x31); + // s016 s017 s018 s019 s020 s021 s022 s023 | s116 s117 s118 s119 s120 s121 + // s122 s123 + const __m256i r2 = + _mm256_inserti128_si256(_mm256_castsi128_si256(row0_16), row1_16, 1); + + // even pixels + s[0] = r0; + s[1] = _mm256_alignr_epi8(r1, r0, 4); + s[2] = _mm256_alignr_epi8(r1, r0, 8); + s[3] = _mm256_alignr_epi8(r1, r0, 12); + s[4] = r1; + s[5] = _mm256_alignr_epi8(r2, r1, 4); + + // 00 02 04 06 | 10 12 14 16 + __m256i res_even = highbd_convolve_12tap(s, f); + res_even = + _mm256_srai_epi32(_mm256_add_epi32(res_even, rounding), FILTER_BITS); + + // odd pixels + s[0] = _mm256_alignr_epi8(r1, r0, 2); + s[1] = _mm256_alignr_epi8(r1, r0, 6); + s[2] = _mm256_alignr_epi8(r1, r0, 10); + s[3] = _mm256_alignr_epi8(r1, r0, 14); + s[4] = _mm256_alignr_epi8(r2, r1, 2); + s[5] = _mm256_alignr_epi8(r2, r1, 6); + + // 01 03 05 07 | 11 13 15 17 + __m256i res_odd = highbd_convolve_12tap(s, f); + res_odd = + _mm256_srai_epi32(_mm256_add_epi32(res_odd, rounding), FILTER_BITS); + + // 00 01 02 03 | 10 11 12 13 + const __m256i res_0 = _mm256_unpacklo_epi32(res_even, res_odd); + // 04 05 06 07 | 14 15 16 17 + const __m256i res_1 = _mm256_unpackhi_epi32(res_even, res_odd); + // 00 01 02 03 | 04 05 06 07 | 10 11 12 13 | 14 15 16 17 + const __m256i res_2 = _mm256_packus_epi32(res_0, res_1); + const __m256i res = _mm256_min_epi16(res_2, max); + _mm_storeu_si128((__m128i *)&dst[i * dst_stride + j], + _mm256_castsi256_si128(res)); + if (i + 1 < h) { + _mm_storeu_si128((__m128i *)(&dst[(i + 1) * dst_stride + j]), + _mm256_extractf128_si256(res, 1)); + } + } + } +} + +void vpx_highbd_convolve12_vert_avx2(const uint16_t *src, ptrdiff_t src_stride, + uint16_t *dst, ptrdiff_t dst_stride, + const InterpKernel12 *filter, int x0_q4, + int x_step_q4, int y0_q4, int y_step_q4, + int w, int h, int bd) { + assert(y_step_q4 == 16); + (void)x0_q4; + (void)x_step_q4; + (void)y_step_q4; + const uint16_t *src_ptr = src; + src_ptr -= src_stride * (MAX_FILTER_TAP / 2 - 1); + __m256i s[12], f[6]; + const __m256i rounding = _mm256_set1_epi32(((1 << FILTER_BITS) >> 1)); + const __m256i max = _mm256_set1_epi16((1 << bd) - 1); + highbd_shuffle_12tap_filter_avx2(filter[y0_q4], f); + + for (int j = 0; j < w; j += 8) { + __m128i s0 = + _mm_loadu_si128((const __m128i *)(src_ptr + 0 * src_stride + j)); + __m128i s1 = + _mm_loadu_si128((const __m128i *)(src_ptr + 1 * src_stride + j)); + __m128i s2 = + _mm_loadu_si128((const __m128i *)(src_ptr + 2 * src_stride + j)); + __m128i s3 = + _mm_loadu_si128((const __m128i *)(src_ptr + 3 * src_stride + j)); + __m128i s4 = + _mm_loadu_si128((const __m128i *)(src_ptr + 4 * src_stride + j)); + __m128i s5 = + _mm_loadu_si128((const __m128i *)(src_ptr + 5 * src_stride + j)); + __m128i s6 = + _mm_loadu_si128((const __m128i *)(src_ptr + 6 * src_stride + j)); + __m128i s7 = + _mm_loadu_si128((const __m128i *)(src_ptr + 7 * src_stride + j)); + __m128i s8 = + _mm_loadu_si128((const __m128i *)(src_ptr + 8 * src_stride + j)); + __m128i s9 = + _mm_loadu_si128((const __m128i *)(src_ptr + 9 * src_stride + j)); + __m128i s10t = + _mm_loadu_si128((const __m128i *)(src_ptr + 10 * src_stride + j)); + + __m256i r01 = _mm256_inserti128_si256(_mm256_castsi128_si256(s0), s1, 1); + __m256i r12 = _mm256_inserti128_si256(_mm256_castsi128_si256(s1), s2, 1); + __m256i r23 = _mm256_inserti128_si256(_mm256_castsi128_si256(s2), s3, 1); + __m256i r34 = _mm256_inserti128_si256(_mm256_castsi128_si256(s3), s4, 1); + __m256i r45 = _mm256_inserti128_si256(_mm256_castsi128_si256(s4), s5, 1); + __m256i r56 = _mm256_inserti128_si256(_mm256_castsi128_si256(s5), s6, 1); + __m256i r67 = _mm256_inserti128_si256(_mm256_castsi128_si256(s6), s7, 1); + __m256i r78 = _mm256_inserti128_si256(_mm256_castsi128_si256(s7), s8, 1); + __m256i r89 = _mm256_inserti128_si256(_mm256_castsi128_si256(s8), s9, 1); + __m256i r910 = _mm256_inserti128_si256(_mm256_castsi128_si256(s9), s10t, 1); + + s[0] = _mm256_unpacklo_epi16(r01, r12); + s[1] = _mm256_unpacklo_epi16(r23, r34); + s[2] = _mm256_unpacklo_epi16(r45, r56); + s[3] = _mm256_unpacklo_epi16(r67, r78); + s[4] = _mm256_unpacklo_epi16(r89, r910); + + s[6] = _mm256_unpackhi_epi16(r01, r12); + s[7] = _mm256_unpackhi_epi16(r23, r34); + s[8] = _mm256_unpackhi_epi16(r45, r56); + s[9] = _mm256_unpackhi_epi16(r67, r78); + s[10] = _mm256_unpackhi_epi16(r89, r910); + for (int i = 0; i < h; i += 2) { + const __m128i s10 = _mm_loadu_si128( + (const __m128i *)(src_ptr + (i + 10) * src_stride + j)); + const __m128i s11 = _mm_loadu_si128( + (const __m128i *)(src_ptr + (i + 11) * src_stride + j)); + const __m128i s12 = _mm_loadu_si128( + (const __m128i *)(src_ptr + (i + 12) * src_stride + j)); + __m256i r1011 = + _mm256_inserti128_si256(_mm256_castsi128_si256(s10), s11, 1); + __m256i r1112 = + _mm256_inserti128_si256(_mm256_castsi128_si256(s11), s12, 1); + + s[5] = _mm256_unpacklo_epi16(r1011, r1112); + s[11] = _mm256_unpackhi_epi16(r1011, r1112); + + // 00 01 02 03 | 10 11 12 13 + const __m256i res_a = highbd_convolve_12tap(s, f); + __m256i res_a_round = + _mm256_srai_epi32(_mm256_add_epi32(res_a, rounding), FILTER_BITS); + // 04 05 06 07 | 14 15 16 17 + const __m256i res_b = highbd_convolve_12tap(s + 6, f); + __m256i res_b_round = + _mm256_srai_epi32(_mm256_add_epi32(res_b, rounding), FILTER_BITS); + + // 00 01 02 03 | 04 05 06 07 | 10 11 12 13 | 14 15 16 17 + const __m256i res_0 = _mm256_packus_epi32(res_a_round, res_b_round); + const __m256i res = _mm256_min_epi16(res_0, max); + _mm_storeu_si128((__m128i *)&dst[i * dst_stride + j], + _mm256_castsi256_si128(res)); + + _mm_storeu_si128((__m128i *)(&dst[(i + 1) * dst_stride + j]), + _mm256_extractf128_si256(res, 1)); + + reuse_src_data_avx2(s + 1, s); + reuse_src_data_avx2(s + 7, s + 6); + } + } +} + +void vpx_highbd_convolve12_avx2(const uint16_t *src, ptrdiff_t src_stride, + uint16_t *dst, ptrdiff_t dst_stride, + const InterpKernel12 *filter, int x0_q4, + int x_step_q4, int y0_q4, int y_step_q4, int w, + int h, int bd) { + assert(x_step_q4 == 16 && y_step_q4 == 16); + assert(h == 32 || h == 16 || h == 8); + assert(w == 32 || w == 16 || w == 8); + DECLARE_ALIGNED(32, uint16_t, temp[BW * (BH + MAX_FILTER_TAP - 1)]); + const int temp_stride = BW; + const int intermediate_height = + (((h - 1) * y_step_q4 + y0_q4) >> SUBPEL_BITS) + MAX_FILTER_TAP; + + vpx_highbd_convolve12_horiz_avx2(src - src_stride * (MAX_FILTER_TAP / 2 - 1), + src_stride, temp, temp_stride, filter, x0_q4, + x_step_q4, y0_q4, y_step_q4, w, + intermediate_height, bd); + vpx_highbd_convolve12_vert_avx2(temp + temp_stride * (MAX_FILTER_TAP / 2 - 1), + temp_stride, dst, dst_stride, filter, x0_q4, + x_step_q4, y0_q4, y_step_q4, w, h, bd); +}
diff --git a/vp9/encoder/x86/highbd_temporal_filter_ssse3.c b/vp9/encoder/x86/highbd_temporal_filter_ssse3.c new file mode 100644 index 0000000..4540dca --- /dev/null +++ b/vp9/encoder/x86/highbd_temporal_filter_ssse3.c
@@ -0,0 +1,233 @@ +/* + * Copyright (c) 2024 The WebM project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include <assert.h> +#include <tmmintrin.h> // SSSE3 + +#include "./vp9_rtcd.h" +#include "./vpx_dsp_rtcd.h" +#include "vp9/encoder/vp9_temporal_filter.h" + +static INLINE void highbd_shuffle_12tap_filter_ssse3(const int16_t *filter, + __m128i *f) { + const __m128i f_low = _mm_loadu_si128((const __m128i *)filter); + const __m128i f_high = _mm_loadl_epi64((const __m128i *)(filter + 8)); + + f[0] = _mm_shuffle_epi32(f_low, 0x00); + f[1] = _mm_shuffle_epi32(f_low, 0x55); + f[2] = _mm_shuffle_epi32(f_low, 0xaa); + f[3] = _mm_shuffle_epi32(f_low, 0xff); + f[4] = _mm_shuffle_epi32(f_high, 0x00); + f[5] = _mm_shuffle_epi32(f_high, 0x55); +} + +static INLINE void unpacklo_src_ssse3(__m128i *a, __m128i *s) { + s[0] = _mm_unpacklo_epi16(a[0], a[1]); + s[1] = _mm_unpacklo_epi16(a[2], a[3]); + s[2] = _mm_unpacklo_epi16(a[4], a[5]); + s[3] = _mm_unpacklo_epi16(a[6], a[7]); + s[4] = _mm_unpacklo_epi16(a[8], a[9]); +} + +static INLINE void unpackhi_src_ssse3(__m128i *a, __m128i *s) { + s[0] = _mm_unpackhi_epi16(a[0], a[1]); + s[1] = _mm_unpackhi_epi16(a[2], a[3]); + s[2] = _mm_unpackhi_epi16(a[4], a[5]); + s[3] = _mm_unpackhi_epi16(a[6], a[7]); + s[4] = _mm_unpackhi_epi16(a[8], a[9]); +} + +static INLINE __m128i highbd_convolve_12tap(const __m128i *s, + const __m128i *f) { + const __m128i rounding = _mm_set1_epi32(1 << (FILTER_BITS - 1)); + const __m128i res_0 = _mm_madd_epi16(s[0], f[0]); + const __m128i res_1 = _mm_madd_epi16(s[1], f[1]); + const __m128i res_2 = _mm_madd_epi16(s[2], f[2]); + const __m128i res_3 = _mm_madd_epi16(s[3], f[3]); + const __m128i res_4 = _mm_madd_epi16(s[4], f[4]); + const __m128i res_5 = _mm_madd_epi16(s[5], f[5]); + + const __m128i res_6 = _mm_add_epi32( + _mm_add_epi32(res_0, res_1), + _mm_add_epi32(_mm_add_epi32(res_2, res_3), _mm_add_epi32(res_4, res_5))); + const __m128i res = + _mm_srai_epi32(_mm_add_epi32(res_6, rounding), FILTER_BITS); + return res; +} + +static INLINE void reuse_src_data_ssse3(const __m128i *src, __m128i *des) { + des[0] = src[0]; + des[1] = src[1]; + des[2] = src[2]; + des[3] = src[3]; + des[4] = src[4]; +} + +void vpx_highbd_convolve12_horiz_ssse3(const uint16_t *src, + ptrdiff_t src_stride, uint16_t *dst, + ptrdiff_t dst_stride, + const InterpKernel12 *filter, int x0_q4, + int x_step_q4, int y0_q4, int y_step_q4, + int w, int h, int bd) { + assert(x_step_q4 == 16); + (void)y0_q4; + (void)x_step_q4; + (void)y_step_q4; + const uint16_t *src_ptr = src; + src_ptr -= MAX_FILTER_TAP / 2 - 1; + __m128i s[6], f[6]; + const __m128i max = _mm_set1_epi16((1 << bd) - 1); + const __m128i min = _mm_setzero_si128(); + highbd_shuffle_12tap_filter_ssse3(filter[x0_q4], f); + + for (int j = 0; j < w; j += 8) { + for (int i = 0; i < h; i++) { + // s00 s01 s02 s03 s04 s05 s06 s07 + const __m128i r0 = + _mm_loadu_si128((const __m128i *)&src_ptr[i * src_stride + j]); + // s08 s09 s010 s011 s012 s013 s014 s015 + const __m128i r1 = + _mm_loadu_si128((const __m128i *)&src_ptr[i * src_stride + j + 8]); + // s016 s017 s018 s019 s020 s021 s022 s023 + const __m128i r2 = + _mm_loadu_si128((const __m128i *)&src_ptr[i * src_stride + j + 16]); + + // even pixels + s[0] = r0; + s[1] = _mm_alignr_epi8(r1, r0, 4); + s[2] = _mm_alignr_epi8(r1, r0, 8); + s[3] = _mm_alignr_epi8(r1, r0, 12); + s[4] = r1; + s[5] = _mm_alignr_epi8(r2, r1, 4); + + // 00 02 04 06 + __m128i res_even = highbd_convolve_12tap(s, f); + + // odd pixels + s[0] = _mm_alignr_epi8(r1, r0, 2); + s[1] = _mm_alignr_epi8(r1, r0, 6); + s[2] = _mm_alignr_epi8(r1, r0, 10); + s[3] = _mm_alignr_epi8(r1, r0, 14); + s[4] = _mm_alignr_epi8(r2, r1, 2); + s[5] = _mm_alignr_epi8(r2, r1, 6); + + // 01 03 05 07 + __m128i res_odd = highbd_convolve_12tap(s, f); + + // 00 01 02 03 + const __m128i res_0 = _mm_unpacklo_epi32(res_even, res_odd); + // 04 05 06 07 + const __m128i res_1 = _mm_unpackhi_epi32(res_even, res_odd); + // 00 01 02 03 | 04 05 06 07 + const __m128i res_2 = _mm_packs_epi32(res_0, res_1); + const __m128i res = _mm_max_epi16(_mm_min_epi16(res_2, max), min); + _mm_storeu_si128((__m128i *)&dst[i * dst_stride + j], res); + } + } +} + +void vpx_highbd_convolve12_vert_ssse3(const uint16_t *src, ptrdiff_t src_stride, + uint16_t *dst, ptrdiff_t dst_stride, + const InterpKernel12 *filter, int x0_q4, + int x_step_q4, int y0_q4, int y_step_q4, + int w, int h, int bd) { + assert(y_step_q4 == 16); + (void)x0_q4; + (void)x_step_q4; + (void)y_step_q4; + const uint16_t *src_ptr = src; + src_ptr -= src_stride * (MAX_FILTER_TAP / 2 - 1); + __m128i s[12], r[12], a[11], f[6]; + const __m128i max = _mm_set1_epi16((1 << bd) - 1); + const __m128i min = _mm_setzero_si128(); + highbd_shuffle_12tap_filter_ssse3(filter[y0_q4], f); + + for (int j = 0; j < w; j += 8) { + a[0] = _mm_loadu_si128((const __m128i *)(src_ptr + 0 * src_stride + j)); + a[1] = _mm_loadu_si128((const __m128i *)(src_ptr + 1 * src_stride + j)); + a[2] = _mm_loadu_si128((const __m128i *)(src_ptr + 2 * src_stride + j)); + a[3] = _mm_loadu_si128((const __m128i *)(src_ptr + 3 * src_stride + j)); + a[4] = _mm_loadu_si128((const __m128i *)(src_ptr + 4 * src_stride + j)); + a[5] = _mm_loadu_si128((const __m128i *)(src_ptr + 5 * src_stride + j)); + a[6] = _mm_loadu_si128((const __m128i *)(src_ptr + 6 * src_stride + j)); + a[7] = _mm_loadu_si128((const __m128i *)(src_ptr + 7 * src_stride + j)); + a[8] = _mm_loadu_si128((const __m128i *)(src_ptr + 8 * src_stride + j)); + a[9] = _mm_loadu_si128((const __m128i *)(src_ptr + 9 * src_stride + j)); + a[10] = _mm_loadu_si128((const __m128i *)(src_ptr + 10 * src_stride + j)); + + // even row + unpacklo_src_ssse3(a, s); + unpackhi_src_ssse3(a, s + 6); + // odd row + unpacklo_src_ssse3(a + 1, r); + unpackhi_src_ssse3(a + 1, r + 6); + + for (int i = 0; i < h; i += 2) { + const __m128i s0 = _mm_loadu_si128( + (const __m128i *)(src_ptr + (i + 10) * src_stride + j)); + const __m128i s1 = _mm_loadu_si128( + (const __m128i *)(src_ptr + (i + 11) * src_stride + j)); + const __m128i s2 = _mm_loadu_si128( + (const __m128i *)(src_ptr + (i + 12) * src_stride + j)); + + s[5] = _mm_unpacklo_epi16(s0, s1); + r[5] = _mm_unpacklo_epi16(s1, s2); + + s[11] = _mm_unpackhi_epi16(s0, s1); + r[11] = _mm_unpackhi_epi16(s1, s2); + + // 00 01 02 03 + const __m128i res_a = highbd_convolve_12tap(s, f); + // 04 05 06 07 + const __m128i res_b = highbd_convolve_12tap(s + 6, f); + // 10 11 12 13 + const __m128i res_c = highbd_convolve_12tap(r, f); + // 14 15 16 17 + const __m128i res_d = highbd_convolve_12tap(r + 6, f); + + // 00 01 02 03 | 04 05 06 07 + const __m128i res_0 = _mm_packs_epi32(res_a, res_b); + // 10 11 12 13 | 14 15 16 17 + const __m128i res_1 = _mm_packs_epi32(res_c, res_d); + const __m128i res_r0 = _mm_max_epi16(_mm_min_epi16(res_0, max), min); + const __m128i res_r1 = _mm_max_epi16(_mm_min_epi16(res_1, max), min); + + _mm_storeu_si128((__m128i *)&dst[i * dst_stride + j], res_r0); + _mm_storeu_si128((__m128i *)&dst[(i + 1) * dst_stride + j], res_r1); + + reuse_src_data_ssse3(s + 1, s); + reuse_src_data_ssse3(s + 7, s + 6); + reuse_src_data_ssse3(r + 1, r); + reuse_src_data_ssse3(r + 7, r + 6); + } + } +} + +void vpx_highbd_convolve12_ssse3(const uint16_t *src, ptrdiff_t src_stride, + uint16_t *dst, ptrdiff_t dst_stride, + const InterpKernel12 *filter, int x0_q4, + int x_step_q4, int y0_q4, int y_step_q4, int w, + int h, int bd) { + assert(x_step_q4 == 16 && y_step_q4 == 16); + assert(h == 32 || h == 16 || h == 8); + assert(w == 32 || w == 16 || w == 8); + DECLARE_ALIGNED(32, uint16_t, temp[BW * (BH + MAX_FILTER_TAP - 1)]); + const int temp_stride = BW; + const int intermediate_height = + (((h - 1) * y_step_q4 + y0_q4) >> SUBPEL_BITS) + MAX_FILTER_TAP; + + vpx_highbd_convolve12_horiz_ssse3(src - src_stride * (MAX_FILTER_TAP / 2 - 1), + src_stride, temp, temp_stride, filter, + x0_q4, x_step_q4, y0_q4, y_step_q4, w, + intermediate_height, bd); + vpx_highbd_convolve12_vert_ssse3( + temp + temp_stride * (MAX_FILTER_TAP / 2 - 1), temp_stride, dst, + dst_stride, filter, x0_q4, x_step_q4, y0_q4, y_step_q4, w, h, bd); +}
diff --git a/vp9/encoder/x86/temporal_filter_avx2.c b/vp9/encoder/x86/temporal_filter_avx2.c new file mode 100644 index 0000000..9d94ff8 --- /dev/null +++ b/vp9/encoder/x86/temporal_filter_avx2.c
@@ -0,0 +1,441 @@ +/* + * Copyright (c) 2024 The WebM project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include <assert.h> +#include <immintrin.h> + +#include "./vp9_rtcd.h" +#include "vp9/encoder/vp9_temporal_filter.h" + +DECLARE_ALIGNED(32, static const uint8_t, + shuffle_src_mask1_avx2[32]) = { 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, + 6, 6, 7, 7, 8, 0, 1, 1, 2, 2, 3, + 3, 4, 4, 5, 5, 6, 6, 7, 7, 8 }; + +DECLARE_ALIGNED(32, static const uint8_t, shuffle_src_mask2_avx2[32]) = { + 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, 8, 9, 9, 10, + 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, 8, 9, 9, 10 +}; + +DECLARE_ALIGNED(32, static const uint8_t, shuffle_src_mask3_avx2[32]) = { + 4, 5, 5, 6, 6, 7, 7, 8, 8, 9, 9, 10, 10, 11, 11, 12, + 4, 5, 5, 6, 6, 7, 7, 8, 8, 9, 9, 10, 10, 11, 11, 12 +}; + +DECLARE_ALIGNED(32, static const uint8_t, shuffle_src_mask4_avx2[32]) = { + 6, 7, 7, 8, 8, 9, 9, 10, 10, 11, 11, 12, 12, 13, 13, 14, + 6, 7, 7, 8, 8, 9, 9, 10, 10, 11, 11, 12, 12, 13, 13, 14 +}; + +static INLINE void shuffle_12tap_filter_avx2(const int16_t *filter, + __m256i *f) { + const __m256i f_low = + _mm256_broadcastsi128_si256(_mm_loadu_si128((const __m128i *)filter)); + const __m256i f_high = _mm256_broadcastsi128_si256( + _mm_loadl_epi64((const __m128i *)(filter + 8))); + + f[0] = _mm256_shuffle_epi8(f_low, _mm256_set1_epi16(0x0200u)); + f[1] = _mm256_shuffle_epi8(f_low, _mm256_set1_epi16(0x0604u)); + f[2] = _mm256_shuffle_epi8(f_low, _mm256_set1_epi16(0x0a08u)); + f[3] = _mm256_shuffle_epi8(f_low, _mm256_set1_epi16(0x0e0cu)); + f[4] = _mm256_shuffle_epi8(f_high, _mm256_set1_epi16(0x0200u)); + f[5] = _mm256_shuffle_epi8(f_high, _mm256_set1_epi16(0x0604u)); +} + +static INLINE void shuffle_src_data_avx2(const __m256i *r1, const __m256i *r2, + const __m256i *f, __m256i *s) { + s[0] = _mm256_shuffle_epi8(*r1, f[0]); + s[1] = _mm256_shuffle_epi8(*r1, f[1]); + s[2] = _mm256_shuffle_epi8(*r1, f[2]); + s[3] = _mm256_shuffle_epi8(*r1, f[3]); + s[4] = _mm256_shuffle_epi8(*r2, f[0]); + s[5] = _mm256_shuffle_epi8(*r2, f[1]); +} + +static INLINE void reuse_src_data_avx2(const __m256i *src, __m256i *des) { + des[0] = src[0]; + des[1] = src[1]; + des[2] = src[2]; + des[3] = src[3]; + des[4] = src[4]; +} + +static INLINE __m256i convolve12_16_avx2(const __m256i *s, const __m256i *f) { + // multiply 2 adjacent elements with the filter and add the result + const __m256i k_64 = _mm256_set1_epi16(1 << (FILTER_BITS - 1)); + const __m256i x0 = _mm256_maddubs_epi16(s[0], f[0]); + const __m256i x1 = _mm256_maddubs_epi16(s[1], f[1]); + const __m256i x2 = _mm256_maddubs_epi16(s[2], f[2]); + const __m256i x3 = _mm256_maddubs_epi16(s[3], f[3]); + const __m256i x4 = _mm256_maddubs_epi16(s[4], f[4]); + const __m256i x5 = _mm256_maddubs_epi16(s[5], f[5]); + __m256i sum1, sum2, sum3; + + sum1 = _mm256_add_epi16(x0, x2); + sum2 = _mm256_add_epi16(x3, x5); + sum3 = _mm256_add_epi16(x1, x4); + sum3 = _mm256_add_epi16(sum3, k_64); + + const __m256i s0 = _mm256_cvtepi16_epi32(_mm256_castsi256_si128(sum1)); + const __m256i s1 = _mm256_cvtepi16_epi32(_mm256_extracti128_si256(sum1, 1)); + const __m256i s2 = _mm256_cvtepi16_epi32(_mm256_castsi256_si128(sum2)); + const __m256i s3 = _mm256_cvtepi16_epi32(_mm256_extracti128_si256(sum2, 1)); + const __m256i s4 = _mm256_cvtepi16_epi32(_mm256_castsi256_si128(sum3)); + const __m256i s5 = _mm256_cvtepi16_epi32(_mm256_extracti128_si256(sum3, 1)); + + sum1 = _mm256_add_epi32(s0, s2); + sum2 = _mm256_add_epi32(s1, s3); + sum1 = _mm256_add_epi32(sum1, s4); + sum2 = _mm256_add_epi32(sum2, s5); + + // round and shift by 7 bit each 32 bit + // 0 1 2 3 4 5 6 7 + sum1 = _mm256_srai_epi32(sum1, FILTER_BITS); + // 8 9 10 11 12 13 14 15 + sum2 = _mm256_srai_epi32(sum2, FILTER_BITS); + + // 0 1 2 3 8 9 10 11 4 5 6 7 12 13 14 15 + // 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 + __m256i const res = + _mm256_permute4x64_epi64(_mm256_packus_epi32(sum1, sum2), 0xD8); + return res; +} + +void vpx_convolve12_horiz_avx2(const uint8_t *src, ptrdiff_t src_stride, + uint8_t *dst, ptrdiff_t dst_stride, + const InterpKernel12 *filter, int x0_q4, + int x_step_q4, int y0_q4, int y_step_q4, int w, + int h) { + assert(x_step_q4 == 16); + assert(w == 32 || w == 16 || w == 8); + (void)y0_q4; + (void)x_step_q4; + (void)y_step_q4; + const uint8_t *src_ptr = src; + src_ptr -= MAX_FILTER_TAP / 2 - 1; + __m256i s[6], f[6], src_mask[4]; + + shuffle_12tap_filter_avx2(filter[x0_q4], f); + src_mask[0] = _mm256_load_si256((__m256i const *)shuffle_src_mask1_avx2); + src_mask[1] = _mm256_load_si256((__m256i const *)shuffle_src_mask2_avx2); + src_mask[2] = _mm256_load_si256((__m256i const *)shuffle_src_mask3_avx2); + src_mask[3] = _mm256_load_si256((__m256i const *)shuffle_src_mask4_avx2); + if (w == 8) { + for (int i = 0; i < h; i += 4) { + // s00 s01 s02 s03 s04 s05 s06 s07 s08 s09 s010 s011 s012 s013 s014 s015 + const __m128i row0 = + _mm_loadu_si128((const __m128i *)&src_ptr[i * src_stride]); + // s08 s09 s010 s011 s012 s013 s014 s015 s016 s017 s018 s019 s020 s021 + // s022 s023 + const __m128i row0_8 = + _mm_loadu_si128((const __m128i *)&src_ptr[i * src_stride + 8]); + // s10 s11 s12 s13 s14 s15 s16 s17 s18 s19 s110 s111 s112 s113 s114 s115 + const __m128i row1 = + _mm_loadu_si128((const __m128i *)&src_ptr[(i + 1) * src_stride]); + const __m128i row1_8 = + _mm_loadu_si128((const __m128i *)&src_ptr[(i + 1) * src_stride + 8]); + // s20 s21 s22 s23 s24 s25 s26 s27 s28 s29 s210 s211 s212 s213 s214 s215 + const __m128i row2 = + _mm_loadu_si128((const __m128i *)&src_ptr[(i + 2) * src_stride]); + const __m128i row2_8 = + _mm_loadu_si128((const __m128i *)&src_ptr[(i + 2) * src_stride + 8]); + // s30 s31 s32 s33 s34 s35 s36 s37 s38 s39 s310 s311 s312 s313 s314 s115 + const __m128i row3 = + _mm_loadu_si128((const __m128i *)&src_ptr[(i + 3) * src_stride]); + const __m128i row3_8 = + _mm_loadu_si128((const __m128i *)&src_ptr[(i + 3) * src_stride + 8]); + // s00 s01 s02 s03 s04 s05 s06 s07 s08 s09 s010 s011 s012 s013 s014 s015 | + // s10 s11 s12 s13 s14 s15 s16 s17 s18 s19 s110 s111 s112 s113 s114 s115 + const __m256i row01 = + _mm256_inserti128_si256(_mm256_castsi128_si256(row0), row1, 1); + // s20 s21 s22 s23 s24 s25 s26 s27 s28 s29 s210 s211 s212 s213 s214 s215 | + // s30 s31 s32 s33 s34 s35 s36 s37 s38 s39 s310 s311 s312 s313 s314 s115 + const __m256i row23 = + _mm256_inserti128_si256(_mm256_castsi128_si256(row2), row3, 1); + // s08 s09 s010 s011 s012 s013 s014 s015 s016 s017 s018 s019 s020 s021 + // s022 s023 | s18 s19 s110 s111 s112 s113 s114 s115 s116 s117 s118 s119 + // s120 s121 s122 s123 + const __m256i row01_8 = + _mm256_inserti128_si256(_mm256_castsi128_si256(row0_8), row1_8, 1); + const __m256i row23_8 = + _mm256_inserti128_si256(_mm256_castsi128_si256(row2_8), row3_8, 1); + + shuffle_src_data_avx2(&row01, &row01_8, src_mask, s); + const __m256i res_0 = convolve12_16_avx2(s, f); + + shuffle_src_data_avx2(&row23, &row23_8, src_mask, s); + const __m256i res_1 = convolve12_16_avx2(s, f); + + // 00 01 02 03 04 05 06 07 | 10 11 12 13 14 15 16 17 | 08 09 010 011 012 + // 013 014 015 | 18 19 110 111 112 113 114 115 + const __m256i res = _mm256_packus_epi16(res_0, res_1); + const __m128i res_lo = _mm256_castsi256_si128(res); + const __m128i res_hi = _mm256_extracti128_si256(res, 1); + + _mm_storel_epi64((__m128i *)&dst[i * dst_stride], res_lo); + _mm_storel_epi64((__m128i *)&dst[(i + 1) * dst_stride], res_hi); + _mm_storel_epi64((__m128i *)&dst[(i + 2) * dst_stride], + _mm_srli_si128(res_lo, 8)); + _mm_storel_epi64((__m128i *)&dst[(i + 3) * dst_stride], + _mm_srli_si128(res_hi, 8)); + } + } else { + for (int j = 0; j < w; j += 16) { + for (int i = 0; i < h; i += 2) { + // s00 s01 s02 s03 s04 s05 s06 s07 s08 s09 s010 s011 s012 s013 s014 s015 + const __m128i row0 = + _mm_loadu_si128((const __m128i *)&src_ptr[i * src_stride + j]); + // s016 s017 s018 s019 s020 s021 s022 s023 s024 s025 s026 s027 s028 s029 + // s030 s031 + const __m128i row0_16 = + _mm_loadu_si128((const __m128i *)&src_ptr[i * src_stride + j + 16]); + // s10 s11 s12 s13 s14 s15 s16 s17 s18 s19 s110 s111 s112 s113 s114 + // s115 + const __m128i row1 = _mm_loadu_si128( + (const __m128i *)&src_ptr[(i + 1) * src_stride + j]); + // s116 s117 s118 s119 s120 s121 s122 s123 s124 s125 s126 s127 s128 + // s129 s130 s131 + const __m128i row1_16 = _mm_loadu_si128( + (const __m128i *)&src_ptr[(i + 1) * src_stride + j + 16]); + + // s00 s01 s02 s03 s04 s05 s06 s07 s08 s09 s010 s011 s012 s013 s014 s015 + // | s10 s11 s12 s13 s14 s15 s16 s17 s18 s19 s110 s111 s112 s113 s114 + // s115 + const __m256i r0 = + _mm256_inserti128_si256(_mm256_castsi128_si256(row0), row1, 1); + // s016 s017 s018 s019 s020 s021 s022 s023 s024 s025 s026 s027 s028 s029 + // s030 s031 | s116 s117 s118 s119 s120 s121 s122 s123 s124 s125 s126 + // s127 s128 s129 s130 s131 + const __m256i r2 = _mm256_inserti128_si256( + _mm256_castsi128_si256(row0_16), row1_16, 1); + + // s08 s09 s010 s011 s012 s013 s014 s015 s016 s017 s018 s019 s020 s021 + // s022 s023 | s18 s19 s110 s111 s112 s113 s114 s115 s116 s117 s118 s119 + // s120 s121 s122 s123 + const __m256i r1 = _mm256_alignr_epi8(r2, r0, 8); + + shuffle_src_data_avx2(&r0, &r1, src_mask, s); + const __m256i res_0 = convolve12_16_avx2(s, f); + + shuffle_src_data_avx2(&r1, &r2, src_mask, s); + const __m256i res_1 = convolve12_16_avx2(s, f); + + const __m256i res = _mm256_packus_epi16(res_0, res_1); + + _mm_storeu_si128((__m128i *)&dst[i * dst_stride + j], + _mm256_castsi256_si128(res)); + if (i + 1 < h) { + _mm_storeu_si128((__m128i *)&dst[(i + 1) * dst_stride + j], + _mm256_extracti128_si256(res, 1)); + } + } + } + } +} + +void vpx_convolve12_vert_avx2(const uint8_t *src, ptrdiff_t src_stride, + uint8_t *dst, ptrdiff_t dst_stride, + const InterpKernel12 *filter, int x0_q4, + int x_step_q4, int y0_q4, int y_step_q4, int w, + int h) { + assert(y_step_q4 == 16); + assert(h == 32 || h == 16 || h == 8); + assert(w == 32 || w == 16 || w == 8); + (void)x0_q4; + (void)x_step_q4; + (void)y_step_q4; + const uint8_t *src_ptr = src; + src_ptr -= src_stride * (MAX_FILTER_TAP / 2 - 1); + __m256i s[12], f[6]; + + shuffle_12tap_filter_avx2(filter[y0_q4], f); + if (w == 8) { + const __m128i s0 = + _mm_loadl_epi64((const __m128i *)(src_ptr + 0 * src_stride)); + const __m128i s1 = + _mm_loadl_epi64((const __m128i *)(src_ptr + 1 * src_stride)); + const __m128i s2 = + _mm_loadl_epi64((const __m128i *)(src_ptr + 2 * src_stride)); + const __m128i s3 = + _mm_loadl_epi64((const __m128i *)(src_ptr + 3 * src_stride)); + const __m128i s4 = + _mm_loadl_epi64((const __m128i *)(src_ptr + 4 * src_stride)); + const __m128i s5 = + _mm_loadl_epi64((const __m128i *)(src_ptr + 5 * src_stride)); + const __m128i s6 = + _mm_loadl_epi64((const __m128i *)(src_ptr + 6 * src_stride)); + const __m128i s7 = + _mm_loadl_epi64((const __m128i *)(src_ptr + 7 * src_stride)); + const __m128i s8 = + _mm_loadl_epi64((const __m128i *)(src_ptr + 8 * src_stride)); + const __m128i s9 = + _mm_loadl_epi64((const __m128i *)(src_ptr + 9 * src_stride)); + const __m128i s10t = + _mm_loadl_epi64((const __m128i *)(src_ptr + 10 * src_stride)); + + const __m256i r01 = + _mm256_inserti128_si256(_mm256_castsi128_si256(s0), s1, 1); + const __m256i r12 = + _mm256_inserti128_si256(_mm256_castsi128_si256(s1), s2, 1); + const __m256i r23 = + _mm256_inserti128_si256(_mm256_castsi128_si256(s2), s3, 1); + const __m256i r34 = + _mm256_inserti128_si256(_mm256_castsi128_si256(s3), s4, 1); + const __m256i r45 = + _mm256_inserti128_si256(_mm256_castsi128_si256(s4), s5, 1); + const __m256i r56 = + _mm256_inserti128_si256(_mm256_castsi128_si256(s5), s6, 1); + const __m256i r67 = + _mm256_inserti128_si256(_mm256_castsi128_si256(s6), s7, 1); + const __m256i r78 = + _mm256_inserti128_si256(_mm256_castsi128_si256(s7), s8, 1); + const __m256i r89 = + _mm256_inserti128_si256(_mm256_castsi128_si256(s8), s9, 1); + const __m256i r910 = + _mm256_inserti128_si256(_mm256_castsi128_si256(s9), s10t, 1); + + s[0] = _mm256_unpacklo_epi8(r01, r12); + s[1] = _mm256_unpacklo_epi8(r23, r34); + s[2] = _mm256_unpacklo_epi8(r45, r56); + s[3] = _mm256_unpacklo_epi8(r67, r78); + s[4] = _mm256_unpacklo_epi8(r89, r910); + for (int i = 0; i < h; i += 2) { + const __m128i s10 = + _mm_loadl_epi64((const __m128i *)(src_ptr + (i + 10) * src_stride)); + const __m128i s11 = + _mm_loadl_epi64((const __m128i *)(src_ptr + (i + 11) * src_stride)); + const __m128i s12 = + _mm_loadl_epi64((const __m128i *)(src_ptr + (i + 12) * src_stride)); + + const __m256i r1011 = + _mm256_inserti128_si256(_mm256_castsi128_si256(s10), s11, 1); + const __m256i r1112 = + _mm256_inserti128_si256(_mm256_castsi128_si256(s11), s12, 1); + s[5] = _mm256_unpacklo_epi8(r1011, r1112); + const __m256i res_0 = convolve12_16_avx2(s, f); + + __m256i res = _mm256_packus_epi16(res_0, res_0); + + _mm_storel_epi64((__m128i *)&dst[i * dst_stride], + _mm256_castsi256_si128(res)); + _mm_storel_epi64((__m128i *)&dst[(i + 1) * dst_stride], + _mm256_extracti128_si256(res, 1)); + + reuse_src_data_avx2(s + 1, s); + } + } else { + for (int j = 0; j < w; j += 16) { + const __m128i s0 = + _mm_loadu_si128((const __m128i *)(src_ptr + 0 * src_stride + j)); + const __m128i s1 = + _mm_loadu_si128((const __m128i *)(src_ptr + 1 * src_stride + j)); + const __m128i s2 = + _mm_loadu_si128((const __m128i *)(src_ptr + 2 * src_stride + j)); + const __m128i s3 = + _mm_loadu_si128((const __m128i *)(src_ptr + 3 * src_stride + j)); + const __m128i s4 = + _mm_loadu_si128((const __m128i *)(src_ptr + 4 * src_stride + j)); + const __m128i s5 = + _mm_loadu_si128((const __m128i *)(src_ptr + 5 * src_stride + j)); + const __m128i s6 = + _mm_loadu_si128((const __m128i *)(src_ptr + 6 * src_stride + j)); + const __m128i s7 = + _mm_loadu_si128((const __m128i *)(src_ptr + 7 * src_stride + j)); + const __m128i s8 = + _mm_loadu_si128((const __m128i *)(src_ptr + 8 * src_stride + j)); + const __m128i s9 = + _mm_loadu_si128((const __m128i *)(src_ptr + 9 * src_stride + j)); + const __m128i s10t = + _mm_loadu_si128((const __m128i *)(src_ptr + 10 * src_stride + j)); + + const __m256i r01 = + _mm256_inserti128_si256(_mm256_castsi128_si256(s0), s1, 1); + const __m256i r12 = + _mm256_inserti128_si256(_mm256_castsi128_si256(s1), s2, 1); + const __m256i r23 = + _mm256_inserti128_si256(_mm256_castsi128_si256(s2), s3, 1); + const __m256i r34 = + _mm256_inserti128_si256(_mm256_castsi128_si256(s3), s4, 1); + const __m256i r45 = + _mm256_inserti128_si256(_mm256_castsi128_si256(s4), s5, 1); + const __m256i r56 = + _mm256_inserti128_si256(_mm256_castsi128_si256(s5), s6, 1); + const __m256i r67 = + _mm256_inserti128_si256(_mm256_castsi128_si256(s6), s7, 1); + const __m256i r78 = + _mm256_inserti128_si256(_mm256_castsi128_si256(s7), s8, 1); + const __m256i r89 = + _mm256_inserti128_si256(_mm256_castsi128_si256(s8), s9, 1); + const __m256i r910 = + _mm256_inserti128_si256(_mm256_castsi128_si256(s9), s10t, 1); + + s[0] = _mm256_unpacklo_epi8(r01, r12); + s[1] = _mm256_unpacklo_epi8(r23, r34); + s[2] = _mm256_unpacklo_epi8(r45, r56); + s[3] = _mm256_unpacklo_epi8(r67, r78); + s[4] = _mm256_unpacklo_epi8(r89, r910); + + s[6] = _mm256_unpackhi_epi8(r01, r12); + s[7] = _mm256_unpackhi_epi8(r23, r34); + s[8] = _mm256_unpackhi_epi8(r45, r56); + s[9] = _mm256_unpackhi_epi8(r67, r78); + s[10] = _mm256_unpackhi_epi8(r89, r910); + for (int i = 0; i < h; i += 2) { + const __m128i s10 = _mm_loadu_si128( + (const __m128i *)(src_ptr + (i + 10) * src_stride + j)); + const __m128i s11 = _mm_loadu_si128( + (const __m128i *)(src_ptr + (i + 11) * src_stride + j)); + const __m128i s12 = _mm_loadu_si128( + (const __m128i *)(src_ptr + (i + 12) * src_stride + j)); + + const __m256i r1011 = + _mm256_inserti128_si256(_mm256_castsi128_si256(s10), s11, 1); + const __m256i r1112 = + _mm256_inserti128_si256(_mm256_castsi128_si256(s11), s12, 1); + + s[5] = _mm256_unpacklo_epi8(r1011, r1112); + s[11] = _mm256_unpackhi_epi8(r1011, r1112); + + const __m256i res_0 = convolve12_16_avx2(s, f); + const __m256i res_1 = convolve12_16_avx2(s + 6, f); + + __m256i res = _mm256_packus_epi16(res_0, res_1); + + _mm_storeu_si128((__m128i *)&dst[i * dst_stride + j], + _mm256_castsi256_si128(res)); + _mm_storeu_si128((__m128i *)&dst[(i + 1) * dst_stride + j], + _mm256_extracti128_si256(res, 1)); + + reuse_src_data_avx2(s + 1, s); + reuse_src_data_avx2(s + 7, s + 6); + } + } + } +} + +void vpx_convolve12_avx2(const uint8_t *src, ptrdiff_t src_stride, uint8_t *dst, + ptrdiff_t dst_stride, const InterpKernel12 *filter, + int x0_q4, int x_step_q4, int y0_q4, int y_step_q4, + int w, int h) { + assert(x_step_q4 == 16 && y_step_q4 == 16); + assert(h == 32 || h == 16 || h == 8); + assert(w == 32 || w == 16 || w == 8); + DECLARE_ALIGNED(32, uint8_t, temp[BW * (BH + MAX_FILTER_TAP - 1)]); + const int temp_stride = BW; + const int intermediate_height = + (((h - 1) * y_step_q4 + y0_q4) >> SUBPEL_BITS) + MAX_FILTER_TAP; + vpx_convolve12_horiz_avx2(src - src_stride * (MAX_FILTER_TAP / 2 - 1), + src_stride, temp, temp_stride, filter, x0_q4, + x_step_q4, y0_q4, y_step_q4, w, + intermediate_height); + vpx_convolve12_vert_avx2(temp + temp_stride * (MAX_FILTER_TAP / 2 - 1), + temp_stride, dst, dst_stride, filter, x0_q4, + x_step_q4, y0_q4, y_step_q4, w, h); +}
diff --git a/vp9/encoder/x86/temporal_filter_ssse3.c b/vp9/encoder/x86/temporal_filter_ssse3.c new file mode 100644 index 0000000..abf0ae1 --- /dev/null +++ b/vp9/encoder/x86/temporal_filter_ssse3.c
@@ -0,0 +1,279 @@ +/* + * Copyright (c) 2024 The WebM project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include <assert.h> +#include <tmmintrin.h> // SSSE3 + +#include "./vp9_rtcd.h" +#include "./vpx_dsp_rtcd.h" +#include "vp9/encoder/vp9_temporal_filter.h" + +DECLARE_ALIGNED(16, static const uint8_t, + shuffle_src_mask1_ssse3[32]) = { 0, 1, 1, 2, 2, 3, 3, 4, + 4, 5, 5, 6, 6, 7, 7, 8 }; + +DECLARE_ALIGNED(16, static const uint8_t, + shuffle_src_mask2_ssse3[32]) = { 2, 3, 3, 4, 4, 5, 5, 6, + 6, 7, 7, 8, 8, 9, 9, 10 }; + +DECLARE_ALIGNED(16, static const uint8_t, + shuffle_src_mask3_ssse3[32]) = { 4, 5, 5, 6, 6, 7, 7, 8, + 8, 9, 9, 10, 10, 11, 11, 12 }; + +DECLARE_ALIGNED(16, static const uint8_t, shuffle_src_mask4_ssse3[32]) = { + 6, 7, 7, 8, 8, 9, 9, 10, 10, 11, 11, 12, 12, 13, 13, 14 +}; + +static INLINE void sign_extend_16bit_to_32bit_ssse3(__m128i in, __m128i zero, + __m128i *out_lo, + __m128i *out_hi) { + const __m128i sign_bits = _mm_cmpgt_epi16(zero, in); + *out_lo = _mm_unpacklo_epi16(in, sign_bits); + *out_hi = _mm_unpackhi_epi16(in, sign_bits); +} + +static INLINE void shuffle_12tap_filter_ssse3(const int16_t *filter, + __m128i *f) { + const __m128i f_low = _mm_loadu_si128((const __m128i *)filter); + const __m128i f_high = _mm_loadl_epi64((const __m128i *)(filter + 8)); + + f[0] = _mm_shuffle_epi8(f_low, _mm_set1_epi16(0x0200u)); + f[1] = _mm_shuffle_epi8(f_low, _mm_set1_epi16(0x0604u)); + f[2] = _mm_shuffle_epi8(f_low, _mm_set1_epi16(0x0a08u)); + f[3] = _mm_shuffle_epi8(f_low, _mm_set1_epi16(0x0e0cu)); + f[4] = _mm_shuffle_epi8(f_high, _mm_set1_epi16(0x0200u)); + f[5] = _mm_shuffle_epi8(f_high, _mm_set1_epi16(0x0604u)); +} + +static INLINE void shuffle_src_data_ssse3(const __m128i *r1, const __m128i *r2, + const __m128i *f, __m128i *s) { + s[0] = _mm_shuffle_epi8(*r1, f[0]); + s[1] = _mm_shuffle_epi8(*r1, f[1]); + s[2] = _mm_shuffle_epi8(*r1, f[2]); + s[3] = _mm_shuffle_epi8(*r1, f[3]); + s[4] = _mm_shuffle_epi8(*r2, f[0]); + s[5] = _mm_shuffle_epi8(*r2, f[1]); +} + +static INLINE void reuse_src_data_ssse3(const __m128i *src, __m128i *des) { + des[0] = src[0]; + des[1] = src[1]; + des[2] = src[2]; + des[3] = src[3]; + des[4] = src[4]; +} + +static INLINE __m128i convolve12_16_ssse3(const __m128i *const s, + const __m128i *const f) { + // multiply 2 adjacent elements with the filter and add the result + const __m128i k_64 = _mm_set1_epi16(1 << (FILTER_BITS - 1)); + const __m128i x0 = _mm_maddubs_epi16(s[0], f[0]); + const __m128i x1 = _mm_maddubs_epi16(s[1], f[1]); + const __m128i x2 = _mm_maddubs_epi16(s[2], f[2]); + const __m128i x3 = _mm_maddubs_epi16(s[3], f[3]); + const __m128i x4 = _mm_maddubs_epi16(s[4], f[4]); + const __m128i x5 = _mm_maddubs_epi16(s[5], f[5]); + __m128i sum1, sum2, sum3, s0, s1, s2, s3, s4, s5; + + sum1 = _mm_add_epi16(x0, x2); + sum2 = _mm_add_epi16(x3, x5); + sum3 = _mm_add_epi16(x1, x4); + sum3 = _mm_add_epi16(sum3, k_64); + + sign_extend_16bit_to_32bit_ssse3(sum1, _mm_setzero_si128(), &s0, &s1); + sign_extend_16bit_to_32bit_ssse3(sum2, _mm_setzero_si128(), &s2, &s3); + sign_extend_16bit_to_32bit_ssse3(sum3, _mm_setzero_si128(), &s4, &s5); + sum1 = _mm_add_epi32(s0, s2); + sum2 = _mm_add_epi32(s1, s3); + sum1 = _mm_add_epi32(sum1, s4); + sum2 = _mm_add_epi32(sum2, s5); + + // round and shift by 7 bit each 32 bit + // 0 1 2 3 + sum1 = _mm_srai_epi32(sum1, FILTER_BITS); + // 4 5 6 7 + sum2 = _mm_srai_epi32(sum2, FILTER_BITS); + + // 0 1 2 3 4 5 6 7 + __m128i const res = _mm_packs_epi32(sum1, sum2); + return res; +} + +void vpx_convolve12_horiz_ssse3(const uint8_t *src, ptrdiff_t src_stride, + uint8_t *dst, ptrdiff_t dst_stride, + const InterpKernel12 *filter, int x0_q4, + int x_step_q4, int y0_q4, int y_step_q4, int w, + int h) { + assert(x_step_q4 == 16); + assert(w == 32 || w == 16 || w == 8); + (void)y0_q4; + (void)x_step_q4; + (void)y_step_q4; + const uint8_t *src_ptr = src; + src_ptr -= MAX_FILTER_TAP / 2 - 1; + __m128i s[6], f[6], src_mask[4]; + + shuffle_12tap_filter_ssse3(filter[x0_q4], f); + src_mask[0] = _mm_load_si128((__m128i const *)shuffle_src_mask1_ssse3); + src_mask[1] = _mm_load_si128((__m128i const *)shuffle_src_mask2_ssse3); + src_mask[2] = _mm_load_si128((__m128i const *)shuffle_src_mask3_ssse3); + src_mask[3] = _mm_load_si128((__m128i const *)shuffle_src_mask4_ssse3); + if (w == 8) { + for (int i = 0; i < h; i += 2) { + // s00 s01 s02 s03 s04 s05 s06 s07 s08 s09 s010 s011 s012 s013 s014 s015 + const __m128i row0 = + _mm_loadu_si128((const __m128i *)&src_ptr[i * src_stride]); + // s08 s09 s010 s011 s012 s013 s014 s015 s016 s017 s018 s019 s020 s021 + // s022 s023 + const __m128i row0_8 = + _mm_loadu_si128((const __m128i *)&src_ptr[i * src_stride + 8]); + // s10 s11 s12 s13 s14 s15 s16 s17 s18 s19 s110 s111 s112 s113 s114 s115 + const __m128i row1 = + _mm_loadu_si128((const __m128i *)&src_ptr[(i + 1) * src_stride]); + const __m128i row1_8 = + _mm_loadu_si128((const __m128i *)&src_ptr[(i + 1) * src_stride + 8]); + + shuffle_src_data_ssse3(&row0, &row0_8, src_mask, s); + const __m128i res_0 = convolve12_16_ssse3(s, f); + + shuffle_src_data_ssse3(&row1, &row1_8, src_mask, s); + const __m128i res_1 = convolve12_16_ssse3(s, f); + + const __m128i res = _mm_packus_epi16(res_0, res_1); + _mm_storel_epi64((__m128i *)&dst[i * dst_stride], res); + _mm_storel_epi64((__m128i *)&dst[(i + 1) * dst_stride], + _mm_srli_si128(res, 8)); + } + } else { + for (int j = 0; j < w; j += 16) { + for (int i = 0; i < h; i++) { + // s00 s01 s02 s03 s04 s05 s06 s07 s08 s09 s010 s011 s012 s013 s014 s015 + const __m128i r0 = + _mm_loadu_si128((const __m128i *)&src_ptr[i * src_stride + j]); + // s016 s017 s018 s019 s020 s021 s022 s023 s024 s025 s026 s027 s028 s029 + // s030 s031 + const __m128i r2 = + _mm_loadu_si128((const __m128i *)&src_ptr[i * src_stride + j + 16]); + + // s08 s09 s010 s011 s012 s013 s014 s015 s016 s017 s018 s019 s020 s021 + // s022 s023 + const __m128i r1 = _mm_alignr_epi8(r2, r0, 8); + + shuffle_src_data_ssse3(&r0, &r1, src_mask, s); + const __m128i res_0 = convolve12_16_ssse3(s, f); + + shuffle_src_data_ssse3(&r1, &r2, src_mask, s); + const __m128i res_1 = convolve12_16_ssse3(s, f); + + const __m128i res = _mm_packus_epi16(res_0, res_1); + _mm_storeu_si128((__m128i *)&dst[i * dst_stride + j], res); + } + } + } +} + +void vpx_convolve12_vert_ssse3(const uint8_t *src, ptrdiff_t src_stride, + uint8_t *dst, ptrdiff_t dst_stride, + const InterpKernel12 *filter, int x0_q4, + int x_step_q4, int y0_q4, int y_step_q4, int w, + int h) { + assert(y_step_q4 == 16); + assert(h == 32 || h == 16 || h == 8); + assert(w == 32 || w == 16 || w == 8); + (void)x0_q4; + (void)x_step_q4; + (void)y_step_q4; + const uint8_t *src_ptr = src; + src_ptr -= src_stride * (MAX_FILTER_TAP / 2 - 1); + __m128i s[12], f[6]; + + shuffle_12tap_filter_ssse3(filter[y0_q4], f); + for (int j = 0; j < w; j += 8) { + const __m128i s0 = + _mm_loadl_epi64((const __m128i *)(src_ptr + 0 * src_stride + j)); + const __m128i s1 = + _mm_loadl_epi64((const __m128i *)(src_ptr + 1 * src_stride + j)); + const __m128i s2 = + _mm_loadl_epi64((const __m128i *)(src_ptr + 2 * src_stride + j)); + const __m128i s3 = + _mm_loadl_epi64((const __m128i *)(src_ptr + 3 * src_stride + j)); + const __m128i s4 = + _mm_loadl_epi64((const __m128i *)(src_ptr + 4 * src_stride + j)); + const __m128i s5 = + _mm_loadl_epi64((const __m128i *)(src_ptr + 5 * src_stride + j)); + const __m128i s6 = + _mm_loadl_epi64((const __m128i *)(src_ptr + 6 * src_stride + j)); + const __m128i s7 = + _mm_loadl_epi64((const __m128i *)(src_ptr + 7 * src_stride + j)); + const __m128i s8 = + _mm_loadl_epi64((const __m128i *)(src_ptr + 8 * src_stride + j)); + const __m128i s9 = + _mm_loadl_epi64((const __m128i *)(src_ptr + 9 * src_stride + j)); + const __m128i s10t = + _mm_loadl_epi64((const __m128i *)(src_ptr + 10 * src_stride + j)); + + // 00 10 01 11 02 12 03 13 04 14 05 15 06 16 07 17 + s[0] = _mm_unpacklo_epi8(s0, s1); + s[1] = _mm_unpacklo_epi8(s2, s3); + s[2] = _mm_unpacklo_epi8(s4, s5); + s[3] = _mm_unpacklo_epi8(s6, s7); + s[4] = _mm_unpacklo_epi8(s8, s9); + + s[6] = _mm_unpacklo_epi8(s1, s2); + s[7] = _mm_unpacklo_epi8(s3, s4); + s[8] = _mm_unpacklo_epi8(s5, s6); + s[9] = _mm_unpacklo_epi8(s7, s8); + s[10] = _mm_unpacklo_epi8(s9, s10t); + for (int i = 0; i < h; i += 2) { + const __m128i s10 = _mm_loadl_epi64( + (const __m128i *)(src_ptr + (i + 10) * src_stride + j)); + const __m128i s11 = _mm_loadl_epi64( + (const __m128i *)(src_ptr + (i + 11) * src_stride + j)); + const __m128i s12 = _mm_loadl_epi64( + (const __m128i *)(src_ptr + (i + 12) * src_stride + j)); + + s[5] = _mm_unpacklo_epi8(s10, s11); + s[11] = _mm_unpacklo_epi8(s11, s12); + + const __m128i res_0 = convolve12_16_ssse3(s, f); + const __m128i res_1 = convolve12_16_ssse3(s + 6, f); + + __m128i res = _mm_packus_epi16(res_0, res_1); + + _mm_storel_epi64((__m128i *)&dst[i * dst_stride + j], res); + _mm_storel_epi64((__m128i *)&dst[(i + 1) * dst_stride + j], + _mm_srli_si128(res, 8)); + + reuse_src_data_ssse3(s + 1, s); + reuse_src_data_ssse3(s + 7, s + 6); + } + } +} + +void vpx_convolve12_ssse3(const uint8_t *src, ptrdiff_t src_stride, + uint8_t *dst, ptrdiff_t dst_stride, + const InterpKernel12 *filter, int x0_q4, + int x_step_q4, int y0_q4, int y_step_q4, int w, + int h) { + assert(x_step_q4 == 16 && y_step_q4 == 16); + assert(h == 32 || h == 16 || h == 8); + assert(w == 32 || w == 16 || w == 8); + DECLARE_ALIGNED(32, uint8_t, temp[BW * (BH + MAX_FILTER_TAP - 1)]); + const int temp_stride = BW; + const int intermediate_height = + (((h - 1) * y_step_q4 + y0_q4) >> SUBPEL_BITS) + MAX_FILTER_TAP; + vpx_convolve12_horiz_ssse3(src - src_stride * (MAX_FILTER_TAP / 2 - 1), + src_stride, temp, temp_stride, filter, x0_q4, + x_step_q4, y0_q4, y_step_q4, w, + intermediate_height); + vpx_convolve12_vert_ssse3(temp + temp_stride * (MAX_FILTER_TAP / 2 - 1), + temp_stride, dst, dst_stride, filter, x0_q4, + x_step_q4, y0_q4, y_step_q4, w, h); +}
diff --git a/vp9/simple_encode.cc b/vp9/simple_encode.cc deleted file mode 100644 index 54b4f38..0000000 --- a/vp9/simple_encode.cc +++ /dev/null
@@ -1,1345 +0,0 @@ -/* - * Copyright (c) 2019 The WebM project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#include <stdio.h> -#include <stdlib.h> - -#include <memory> -#include <vector> - -#include "./ivfenc.h" -#include "vp9/common/vp9_entropymode.h" -#include "vp9/common/vp9_enums.h" -#include "vp9/common/vp9_onyxc_int.h" -#include "vp9/vp9_iface_common.h" -#include "vp9/encoder/vp9_encoder.h" -#include "vp9/encoder/vp9_firstpass.h" -#include "vp9/simple_encode.h" -#include "vp9/vp9_cx_iface.h" - -namespace vp9 { - -static int get_plane_height(vpx_img_fmt_t img_fmt, int frame_height, - int plane) { - assert(plane < 3); - if (plane == 0) { - return frame_height; - } - switch (img_fmt) { - case VPX_IMG_FMT_I420: - case VPX_IMG_FMT_I440: - case VPX_IMG_FMT_YV12: - case VPX_IMG_FMT_I42016: - case VPX_IMG_FMT_I44016: return (frame_height + 1) >> 1; - default: return frame_height; - } -} - -static int get_plane_width(vpx_img_fmt_t img_fmt, int frame_width, int plane) { - assert(plane < 3); - if (plane == 0) { - return frame_width; - } - switch (img_fmt) { - case VPX_IMG_FMT_I420: - case VPX_IMG_FMT_YV12: - case VPX_IMG_FMT_I422: - case VPX_IMG_FMT_I42016: - case VPX_IMG_FMT_I42216: return (frame_width + 1) >> 1; - default: return frame_width; - } -} - -// TODO(angiebird): Merge this function with vpx_img_plane_width() -static int img_plane_width(const vpx_image_t *img, int plane) { - if (plane > 0 && img->x_chroma_shift > 0) - return (img->d_w + 1) >> img->x_chroma_shift; - else - return img->d_w; -} - -// TODO(angiebird): Merge this function with vpx_img_plane_height() -static int img_plane_height(const vpx_image_t *img, int plane) { - if (plane > 0 && img->y_chroma_shift > 0) - return (img->d_h + 1) >> img->y_chroma_shift; - else - return img->d_h; -} - -// TODO(angiebird): Merge this function with vpx_img_read() -static int img_read(vpx_image_t *img, FILE *file) { - int plane; - - for (plane = 0; plane < 3; ++plane) { - unsigned char *buf = img->planes[plane]; - const int stride = img->stride[plane]; - const int w = img_plane_width(img, plane) * - ((img->fmt & VPX_IMG_FMT_HIGHBITDEPTH) ? 2 : 1); - const int h = img_plane_height(img, plane); - int y; - - for (y = 0; y < h; ++y) { - if (fread(buf, 1, w, file) != (size_t)w) return 0; - buf += stride; - } - } - - return 1; -} - -// Assume every config in VP9EncoderConfig is less than 100 characters. -#define ENCODE_CONFIG_BUF_SIZE 100 -struct EncodeConfig { - char name[ENCODE_CONFIG_BUF_SIZE]; - char value[ENCODE_CONFIG_BUF_SIZE]; -}; - -class SimpleEncode::EncodeImpl { - public: - VP9_COMP *cpi; - vpx_img_fmt_t img_fmt; - vpx_image_t tmp_img; - std::vector<FIRSTPASS_STATS> first_pass_stats; - std::vector<EncodeConfig> encode_config_list; -}; - -static VP9_COMP *init_encoder(const VP9EncoderConfig *oxcf, - vpx_img_fmt_t img_fmt) { - VP9_COMP *cpi; - BufferPool *buffer_pool = (BufferPool *)vpx_calloc(1, sizeof(*buffer_pool)); - if (!buffer_pool) return NULL; - vp9_initialize_enc(); - cpi = vp9_create_compressor(oxcf, buffer_pool); - vp9_update_compressor_with_img_fmt(cpi, img_fmt); - return cpi; -} - -static void free_encoder(VP9_COMP *cpi) { - BufferPool *buffer_pool = cpi->common.buffer_pool; - vp9_remove_compressor(cpi); - // buffer_pool needs to be free after cpi because buffer_pool contains - // allocated buffers that will be free in vp9_remove_compressor() - vpx_free(buffer_pool); -} - -static INLINE vpx_rational_t make_vpx_rational(int num, int den) { - vpx_rational_t v; - v.num = num; - v.den = den; - return v; -} - -static INLINE FrameType -get_frame_type_from_update_type(FRAME_UPDATE_TYPE update_type) { - switch (update_type) { - case KF_UPDATE: return kFrameTypeKey; - case ARF_UPDATE: return kFrameTypeAltRef; - case GF_UPDATE: return kFrameTypeGolden; - case OVERLAY_UPDATE: return kFrameTypeOverlay; - case LF_UPDATE: return kFrameTypeInter; - default: - fprintf(stderr, "Unsupported update_type %d\n", update_type); - abort(); - } -} - -static void update_partition_info(const PARTITION_INFO *input_partition_info, - const int num_rows_4x4, - const int num_cols_4x4, - PartitionInfo *output_partition_info) { - const int num_units_4x4 = num_rows_4x4 * num_cols_4x4; - for (int i = 0; i < num_units_4x4; ++i) { - output_partition_info[i].row = input_partition_info[i].row; - output_partition_info[i].column = input_partition_info[i].column; - output_partition_info[i].row_start = input_partition_info[i].row_start; - output_partition_info[i].column_start = - input_partition_info[i].column_start; - output_partition_info[i].width = input_partition_info[i].width; - output_partition_info[i].height = input_partition_info[i].height; - } -} - -// translate MV_REFERENCE_FRAME to RefFrameType -static RefFrameType mv_ref_frame_to_ref_frame_type( - MV_REFERENCE_FRAME mv_ref_frame) { - switch (mv_ref_frame) { - case LAST_FRAME: return kRefFrameTypeLast; - case GOLDEN_FRAME: return kRefFrameTypePast; - case ALTREF_FRAME: return kRefFrameTypeFuture; - default: return kRefFrameTypeNone; - } -} - -static void update_motion_vector_info( - const MOTION_VECTOR_INFO *input_motion_vector_info, const int num_rows_4x4, - const int num_cols_4x4, MotionVectorInfo *output_motion_vector_info, - int motion_vector_scale) { - const int num_units_4x4 = num_rows_4x4 * num_cols_4x4; - for (int i = 0; i < num_units_4x4; ++i) { - const MV_REFERENCE_FRAME *in_ref_frame = - input_motion_vector_info[i].ref_frame; - output_motion_vector_info[i].mv_count = - (in_ref_frame[0] == INTRA_FRAME) - ? 0 - : ((in_ref_frame[1] == NO_REF_FRAME) ? 1 : 2); - if (in_ref_frame[0] == NO_REF_FRAME) { - fprintf(stderr, "in_ref_frame[0] shouldn't be NO_REF_FRAME\n"); - abort(); - } - output_motion_vector_info[i].ref_frame[0] = - mv_ref_frame_to_ref_frame_type(in_ref_frame[0]); - output_motion_vector_info[i].ref_frame[1] = - mv_ref_frame_to_ref_frame_type(in_ref_frame[1]); - output_motion_vector_info[i].mv_row[0] = - (double)input_motion_vector_info[i].mv[0].as_mv.row / - motion_vector_scale; - output_motion_vector_info[i].mv_column[0] = - (double)input_motion_vector_info[i].mv[0].as_mv.col / - motion_vector_scale; - output_motion_vector_info[i].mv_row[1] = - (double)input_motion_vector_info[i].mv[1].as_mv.row / - motion_vector_scale; - output_motion_vector_info[i].mv_column[1] = - (double)input_motion_vector_info[i].mv[1].as_mv.col / - motion_vector_scale; - } -} - -static void update_tpl_stats_info(const TplDepStats *input_tpl_stats_info, - const int show_frame_count, - TplStatsInfo *output_tpl_stats_info) { - int frame_idx; - for (frame_idx = 0; frame_idx < show_frame_count; ++frame_idx) { - output_tpl_stats_info[frame_idx].intra_cost = - input_tpl_stats_info[frame_idx].intra_cost; - output_tpl_stats_info[frame_idx].inter_cost = - input_tpl_stats_info[frame_idx].inter_cost; - output_tpl_stats_info[frame_idx].mc_flow = - input_tpl_stats_info[frame_idx].mc_flow; - output_tpl_stats_info[frame_idx].mc_dep_cost = - input_tpl_stats_info[frame_idx].mc_dep_cost; - output_tpl_stats_info[frame_idx].mc_ref_cost = - input_tpl_stats_info[frame_idx].mc_ref_cost; - } -} - -static void update_frame_counts(const FRAME_COUNTS *input_counts, - FrameCounts *output_counts) { - // Init array sizes. - output_counts->y_mode.resize(BLOCK_SIZE_GROUPS); - for (int i = 0; i < BLOCK_SIZE_GROUPS; ++i) { - output_counts->y_mode[i].resize(INTRA_MODES); - } - - output_counts->uv_mode.resize(INTRA_MODES); - for (int i = 0; i < INTRA_MODES; ++i) { - output_counts->uv_mode[i].resize(INTRA_MODES); - } - - output_counts->partition.resize(PARTITION_CONTEXTS); - for (int i = 0; i < PARTITION_CONTEXTS; ++i) { - output_counts->partition[i].resize(PARTITION_TYPES); - } - - output_counts->coef.resize(TX_SIZES); - output_counts->eob_branch.resize(TX_SIZES); - for (int i = 0; i < TX_SIZES; ++i) { - output_counts->coef[i].resize(PLANE_TYPES); - output_counts->eob_branch[i].resize(PLANE_TYPES); - for (int j = 0; j < PLANE_TYPES; ++j) { - output_counts->coef[i][j].resize(REF_TYPES); - output_counts->eob_branch[i][j].resize(REF_TYPES); - for (int k = 0; k < REF_TYPES; ++k) { - output_counts->coef[i][j][k].resize(COEF_BANDS); - output_counts->eob_branch[i][j][k].resize(COEF_BANDS); - for (int l = 0; l < COEF_BANDS; ++l) { - output_counts->coef[i][j][k][l].resize(COEFF_CONTEXTS); - output_counts->eob_branch[i][j][k][l].resize(COEFF_CONTEXTS); - for (int m = 0; m < COEFF_CONTEXTS; ++m) { - output_counts->coef[i][j][k][l][m].resize(UNCONSTRAINED_NODES + 1); - } - } - } - } - } - - output_counts->switchable_interp.resize(SWITCHABLE_FILTER_CONTEXTS); - for (int i = 0; i < SWITCHABLE_FILTER_CONTEXTS; ++i) { - output_counts->switchable_interp[i].resize(SWITCHABLE_FILTERS); - } - - output_counts->inter_mode.resize(INTER_MODE_CONTEXTS); - for (int i = 0; i < INTER_MODE_CONTEXTS; ++i) { - output_counts->inter_mode[i].resize(INTER_MODES); - } - - output_counts->intra_inter.resize(INTRA_INTER_CONTEXTS); - for (int i = 0; i < INTRA_INTER_CONTEXTS; ++i) { - output_counts->intra_inter[i].resize(2); - } - - output_counts->comp_inter.resize(COMP_INTER_CONTEXTS); - for (int i = 0; i < COMP_INTER_CONTEXTS; ++i) { - output_counts->comp_inter[i].resize(2); - } - - output_counts->single_ref.resize(REF_CONTEXTS); - for (int i = 0; i < REF_CONTEXTS; ++i) { - output_counts->single_ref[i].resize(2); - for (int j = 0; j < 2; ++j) { - output_counts->single_ref[i][j].resize(2); - } - } - - output_counts->comp_ref.resize(REF_CONTEXTS); - for (int i = 0; i < REF_CONTEXTS; ++i) { - output_counts->comp_ref[i].resize(2); - } - - output_counts->skip.resize(SKIP_CONTEXTS); - for (int i = 0; i < SKIP_CONTEXTS; ++i) { - output_counts->skip[i].resize(2); - } - - output_counts->tx.p32x32.resize(TX_SIZE_CONTEXTS); - output_counts->tx.p16x16.resize(TX_SIZE_CONTEXTS); - output_counts->tx.p8x8.resize(TX_SIZE_CONTEXTS); - for (int i = 0; i < TX_SIZE_CONTEXTS; i++) { - output_counts->tx.p32x32[i].resize(TX_SIZES); - output_counts->tx.p16x16[i].resize(TX_SIZES - 1); - output_counts->tx.p8x8[i].resize(TX_SIZES - 2); - } - output_counts->tx.tx_totals.resize(TX_SIZES); - - output_counts->mv.joints.resize(MV_JOINTS); - output_counts->mv.comps.resize(2); - for (int i = 0; i < 2; ++i) { - output_counts->mv.comps[i].sign.resize(2); - output_counts->mv.comps[i].classes.resize(MV_CLASSES); - output_counts->mv.comps[i].class0.resize(CLASS0_SIZE); - output_counts->mv.comps[i].bits.resize(MV_OFFSET_BITS); - for (int j = 0; j < MV_OFFSET_BITS; ++j) { - output_counts->mv.comps[i].bits[j].resize(2); - } - output_counts->mv.comps[i].class0_fp.resize(CLASS0_SIZE); - for (int j = 0; j < CLASS0_SIZE; ++j) { - output_counts->mv.comps[i].class0_fp[j].resize(MV_FP_SIZE); - } - output_counts->mv.comps[i].fp.resize(MV_FP_SIZE); - output_counts->mv.comps[i].class0_hp.resize(2); - output_counts->mv.comps[i].hp.resize(2); - } - - // Populate counts. - for (int i = 0; i < BLOCK_SIZE_GROUPS; ++i) { - for (int j = 0; j < INTRA_MODES; ++j) { - output_counts->y_mode[i][j] = input_counts->y_mode[i][j]; - } - } - for (int i = 0; i < INTRA_MODES; ++i) { - for (int j = 0; j < INTRA_MODES; ++j) { - output_counts->uv_mode[i][j] = input_counts->uv_mode[i][j]; - } - } - for (int i = 0; i < PARTITION_CONTEXTS; ++i) { - for (int j = 0; j < PARTITION_TYPES; ++j) { - output_counts->partition[i][j] = input_counts->partition[i][j]; - } - } - for (int i = 0; i < TX_SIZES; ++i) { - for (int j = 0; j < PLANE_TYPES; ++j) { - for (int k = 0; k < REF_TYPES; ++k) { - for (int l = 0; l < COEF_BANDS; ++l) { - for (int m = 0; m < COEFF_CONTEXTS; ++m) { - output_counts->eob_branch[i][j][k][l][m] = - input_counts->eob_branch[i][j][k][l][m]; - for (int n = 0; n < UNCONSTRAINED_NODES + 1; n++) { - output_counts->coef[i][j][k][l][m][n] = - input_counts->coef[i][j][k][l][m][n]; - } - } - } - } - } - } - for (int i = 0; i < SWITCHABLE_FILTER_CONTEXTS; ++i) { - for (int j = 0; j < SWITCHABLE_FILTERS; ++j) { - output_counts->switchable_interp[i][j] = - input_counts->switchable_interp[i][j]; - } - } - for (int i = 0; i < INTER_MODE_CONTEXTS; ++i) { - for (int j = 0; j < INTER_MODES; ++j) { - output_counts->inter_mode[i][j] = input_counts->inter_mode[i][j]; - } - } - for (int i = 0; i < INTRA_INTER_CONTEXTS; ++i) { - for (int j = 0; j < 2; ++j) { - output_counts->intra_inter[i][j] = input_counts->intra_inter[i][j]; - } - } - for (int i = 0; i < COMP_INTER_CONTEXTS; ++i) { - for (int j = 0; j < 2; ++j) { - output_counts->comp_inter[i][j] = input_counts->comp_inter[i][j]; - } - } - for (int i = 0; i < REF_CONTEXTS; ++i) { - for (int j = 0; j < 2; ++j) { - for (int k = 0; k < 2; ++k) { - output_counts->single_ref[i][j][k] = input_counts->single_ref[i][j][k]; - } - } - } - for (int i = 0; i < REF_CONTEXTS; ++i) { - for (int j = 0; j < 2; ++j) { - output_counts->comp_ref[i][j] = input_counts->comp_ref[i][j]; - } - } - for (int i = 0; i < SKIP_CONTEXTS; ++i) { - for (int j = 0; j < 2; ++j) { - output_counts->skip[i][j] = input_counts->skip[i][j]; - } - } - for (int i = 0; i < TX_SIZE_CONTEXTS; i++) { - for (int j = 0; j < TX_SIZES; j++) { - output_counts->tx.p32x32[i][j] = input_counts->tx.p32x32[i][j]; - } - for (int j = 0; j < TX_SIZES - 1; j++) { - output_counts->tx.p16x16[i][j] = input_counts->tx.p16x16[i][j]; - } - for (int j = 0; j < TX_SIZES - 2; j++) { - output_counts->tx.p8x8[i][j] = input_counts->tx.p8x8[i][j]; - } - } - for (int i = 0; i < TX_SIZES; i++) { - output_counts->tx.tx_totals[i] = input_counts->tx.tx_totals[i]; - } - for (int i = 0; i < MV_JOINTS; i++) { - output_counts->mv.joints[i] = input_counts->mv.joints[i]; - } - for (int k = 0; k < 2; k++) { - const nmv_component_counts *const comps_t = &input_counts->mv.comps[k]; - for (int i = 0; i < 2; i++) { - output_counts->mv.comps[k].sign[i] = comps_t->sign[i]; - output_counts->mv.comps[k].class0_hp[i] = comps_t->class0_hp[i]; - output_counts->mv.comps[k].hp[i] = comps_t->hp[i]; - } - for (int i = 0; i < MV_CLASSES; i++) { - output_counts->mv.comps[k].classes[i] = comps_t->classes[i]; - } - for (int i = 0; i < CLASS0_SIZE; i++) { - output_counts->mv.comps[k].class0[i] = comps_t->class0[i]; - for (int j = 0; j < MV_FP_SIZE; j++) { - output_counts->mv.comps[k].class0_fp[i][j] = comps_t->class0_fp[i][j]; - } - } - for (int i = 0; i < MV_OFFSET_BITS; i++) { - for (int j = 0; j < 2; j++) { - output_counts->mv.comps[k].bits[i][j] = comps_t->bits[i][j]; - } - } - for (int i = 0; i < MV_FP_SIZE; i++) { - output_counts->mv.comps[k].fp[i] = comps_t->fp[i]; - } - } -} - -void output_image_buffer(const ImageBuffer &image_buffer, std::FILE *out_file) { - for (int plane = 0; plane < 3; ++plane) { - const int w = image_buffer.plane_width[plane]; - const int h = image_buffer.plane_height[plane]; - const uint8_t *buf = image_buffer.plane_buffer[plane].get(); - fprintf(out_file, "%d %d\n", h, w); - for (int i = 0; i < w * h; ++i) { - fprintf(out_file, "%d ", (int)buf[i]); - } - fprintf(out_file, "\n"); - } -} - -static bool init_image_buffer(ImageBuffer *image_buffer, int frame_width, - int frame_height, vpx_img_fmt_t img_fmt) { - for (int plane = 0; plane < 3; ++plane) { - const int w = get_plane_width(img_fmt, frame_width, plane); - const int h = get_plane_height(img_fmt, frame_height, plane); - image_buffer->plane_width[plane] = w; - image_buffer->plane_height[plane] = h; - image_buffer->plane_buffer[plane].reset(new (std::nothrow) uint8_t[w * h]); - if (image_buffer->plane_buffer[plane].get() == nullptr) { - return false; - } - } - return true; -} - -static void ImageBuffer_to_IMAGE_BUFFER(const ImageBuffer &image_buffer, - IMAGE_BUFFER *image_buffer_c) { - image_buffer_c->allocated = 1; - for (int plane = 0; plane < 3; ++plane) { - image_buffer_c->plane_width[plane] = image_buffer.plane_width[plane]; - image_buffer_c->plane_height[plane] = image_buffer.plane_height[plane]; - image_buffer_c->plane_buffer[plane] = - image_buffer.plane_buffer[plane].get(); - } -} - -static size_t get_max_coding_data_byte_size(int frame_width, int frame_height) { - return frame_width * frame_height * 3; -} - -static bool init_encode_frame_result(EncodeFrameResult *encode_frame_result, - int frame_width, int frame_height, - vpx_img_fmt_t img_fmt) { - const size_t max_coding_data_byte_size = - get_max_coding_data_byte_size(frame_width, frame_height); - - encode_frame_result->coding_data.reset( - new (std::nothrow) uint8_t[max_coding_data_byte_size]); - encode_frame_result->max_coding_data_byte_size = max_coding_data_byte_size; - - encode_frame_result->num_rows_4x4 = get_num_unit_4x4(frame_height); - encode_frame_result->num_cols_4x4 = get_num_unit_4x4(frame_width); - encode_frame_result->partition_info.resize(encode_frame_result->num_rows_4x4 * - encode_frame_result->num_cols_4x4); - encode_frame_result->motion_vector_info.resize( - encode_frame_result->num_rows_4x4 * encode_frame_result->num_cols_4x4); - encode_frame_result->tpl_stats_info.resize(MAX_LAG_BUFFERS); - - if (encode_frame_result->coding_data.get() == nullptr) { - encode_frame_result->max_coding_data_byte_size = 0; - return false; - } - return init_image_buffer(&encode_frame_result->coded_frame, frame_width, - frame_height, img_fmt); -} - -static void encode_frame_result_update_rq_history( - const RATE_QINDEX_HISTORY *rq_history, - EncodeFrameResult *encode_frame_result) { - encode_frame_result->recode_count = rq_history->recode_count; - for (int i = 0; i < encode_frame_result->recode_count; ++i) { - const int q_index = rq_history->q_index_history[i]; - const int rate = rq_history->rate_history[i]; - encode_frame_result->q_index_history.push_back(q_index); - encode_frame_result->rate_history.push_back(rate); - } -} - -static void update_encode_frame_result( - EncodeFrameResult *encode_frame_result, const int show_frame_count, - const ENCODE_FRAME_RESULT *encode_frame_info) { - encode_frame_result->coding_data_bit_size = - encode_frame_result->coding_data_byte_size * 8; - encode_frame_result->show_idx = encode_frame_info->show_idx; - encode_frame_result->coding_idx = encode_frame_info->frame_coding_index; - assert(kRefFrameTypeMax == MAX_INTER_REF_FRAMES); - for (int i = 0; i < kRefFrameTypeMax; ++i) { - encode_frame_result->ref_frame_info.coding_indexes[i] = - encode_frame_info->ref_frame_coding_indexes[i]; - encode_frame_result->ref_frame_info.valid_list[i] = - encode_frame_info->ref_frame_valid_list[i]; - } - encode_frame_result->frame_type = - get_frame_type_from_update_type(encode_frame_info->update_type); - encode_frame_result->psnr = encode_frame_info->psnr; - encode_frame_result->sse = encode_frame_info->sse; - encode_frame_result->quantize_index = encode_frame_info->quantize_index; - update_partition_info(encode_frame_info->partition_info, - encode_frame_result->num_rows_4x4, - encode_frame_result->num_cols_4x4, - &encode_frame_result->partition_info[0]); - update_motion_vector_info(encode_frame_info->motion_vector_info, - encode_frame_result->num_rows_4x4, - encode_frame_result->num_cols_4x4, - &encode_frame_result->motion_vector_info[0], - kMotionVectorSubPixelPrecision); - update_frame_counts(&encode_frame_info->frame_counts, - &encode_frame_result->frame_counts); - if (encode_frame_result->frame_type == kFrameTypeAltRef) { - update_tpl_stats_info(encode_frame_info->tpl_stats_info, show_frame_count, - &encode_frame_result->tpl_stats_info[0]); - } - encode_frame_result_update_rq_history(&encode_frame_info->rq_history, - encode_frame_result); -} - -static void IncreaseGroupOfPictureIndex(GroupOfPicture *group_of_picture) { - ++group_of_picture->next_encode_frame_index; -} - -static int IsGroupOfPictureFinished(const GroupOfPicture &group_of_picture) { - return static_cast<size_t>(group_of_picture.next_encode_frame_index) == - group_of_picture.encode_frame_list.size(); -} - -bool operator==(const RefFrameInfo &a, const RefFrameInfo &b) { - bool match = true; - for (int i = 0; i < kRefFrameTypeMax; ++i) { - match &= a.coding_indexes[i] == b.coding_indexes[i]; - match &= a.valid_list[i] == b.valid_list[i]; - } - return match; -} - -static void InitRefFrameInfo(RefFrameInfo *ref_frame_info) { - for (int i = 0; i < kRefFrameTypeMax; ++i) { - ref_frame_info->coding_indexes[i] = -1; - ref_frame_info->valid_list[i] = 0; - } -} - -// After finishing coding a frame, this function will update the coded frame -// into the ref_frame_info based on the frame_type and the coding_index. -static void PostUpdateRefFrameInfo(FrameType frame_type, int frame_coding_index, - RefFrameInfo *ref_frame_info) { - // This part is written based on the logics in vp9_configure_buffer_updates() - // and update_ref_frames() - int *ref_frame_coding_indexes = ref_frame_info->coding_indexes; - switch (frame_type) { - case kFrameTypeKey: - ref_frame_coding_indexes[kRefFrameTypeLast] = frame_coding_index; - ref_frame_coding_indexes[kRefFrameTypePast] = frame_coding_index; - ref_frame_coding_indexes[kRefFrameTypeFuture] = frame_coding_index; - break; - case kFrameTypeInter: - ref_frame_coding_indexes[kRefFrameTypeLast] = frame_coding_index; - break; - case kFrameTypeAltRef: - ref_frame_coding_indexes[kRefFrameTypeFuture] = frame_coding_index; - break; - case kFrameTypeOverlay: - // Reserve the past coding_index in the future slot. This logic is from - // update_ref_frames() with condition vp9_preserve_existing_gf() == 1 - // TODO(angiebird): Invetegate why we need this. - ref_frame_coding_indexes[kRefFrameTypeFuture] = - ref_frame_coding_indexes[kRefFrameTypePast]; - ref_frame_coding_indexes[kRefFrameTypePast] = frame_coding_index; - break; - case kFrameTypeGolden: - ref_frame_coding_indexes[kRefFrameTypePast] = frame_coding_index; - ref_frame_coding_indexes[kRefFrameTypeLast] = frame_coding_index; - break; - } - - // This part is written based on the logics in get_ref_frame_flags() but we - // rename the flags alt, golden to future, past respectively. Mark - // non-duplicated reference frames as valid. The priorities are - // kRefFrameTypeLast > kRefFrameTypePast > kRefFrameTypeFuture. - const int last_index = ref_frame_coding_indexes[kRefFrameTypeLast]; - const int past_index = ref_frame_coding_indexes[kRefFrameTypePast]; - const int future_index = ref_frame_coding_indexes[kRefFrameTypeFuture]; - - int *ref_frame_valid_list = ref_frame_info->valid_list; - for (int ref_frame_idx = 0; ref_frame_idx < kRefFrameTypeMax; - ++ref_frame_idx) { - ref_frame_valid_list[ref_frame_idx] = 1; - } - - if (past_index == last_index) { - ref_frame_valid_list[kRefFrameTypePast] = 0; - } - - if (future_index == last_index) { - ref_frame_valid_list[kRefFrameTypeFuture] = 0; - } - - if (future_index == past_index) { - ref_frame_valid_list[kRefFrameTypeFuture] = 0; - } -} - -static void SetGroupOfPicture(int first_is_key_frame, int use_alt_ref, - int coding_frame_count, int first_show_idx, - int last_gop_use_alt_ref, int start_coding_index, - const RefFrameInfo &start_ref_frame_info, - GroupOfPicture *group_of_picture) { - // Clean up the state of previous group of picture. - group_of_picture->encode_frame_list.clear(); - group_of_picture->next_encode_frame_index = 0; - group_of_picture->show_frame_count = coding_frame_count - use_alt_ref; - group_of_picture->start_show_index = first_show_idx; - group_of_picture->start_coding_index = start_coding_index; - group_of_picture->first_is_key_frame = first_is_key_frame; - group_of_picture->use_alt_ref = use_alt_ref; - group_of_picture->last_gop_use_alt_ref = last_gop_use_alt_ref; - - // We need to make a copy of start reference frame info because we - // use it to simulate the ref frame update. - RefFrameInfo ref_frame_info = start_ref_frame_info; - - { - // First frame in the group of pictures. It's either key frame or show inter - // frame. - EncodeFrameInfo encode_frame_info; - // Set frame_type - if (first_is_key_frame) { - encode_frame_info.frame_type = kFrameTypeKey; - } else { - if (last_gop_use_alt_ref) { - encode_frame_info.frame_type = kFrameTypeOverlay; - } else { - encode_frame_info.frame_type = kFrameTypeGolden; - } - } - - encode_frame_info.show_idx = first_show_idx; - encode_frame_info.coding_index = start_coding_index; - - encode_frame_info.ref_frame_info = ref_frame_info; - PostUpdateRefFrameInfo(encode_frame_info.frame_type, - encode_frame_info.coding_index, &ref_frame_info); - - group_of_picture->encode_frame_list.push_back(encode_frame_info); - } - - const int show_frame_count = coding_frame_count - use_alt_ref; - if (use_alt_ref) { - // If there is alternate reference, it is always coded at the second place. - // Its show index (or timestamp) is at the last of this group - EncodeFrameInfo encode_frame_info; - encode_frame_info.frame_type = kFrameTypeAltRef; - encode_frame_info.show_idx = first_show_idx + show_frame_count; - encode_frame_info.coding_index = start_coding_index + 1; - - encode_frame_info.ref_frame_info = ref_frame_info; - PostUpdateRefFrameInfo(encode_frame_info.frame_type, - encode_frame_info.coding_index, &ref_frame_info); - - group_of_picture->encode_frame_list.push_back(encode_frame_info); - } - - // Encode the rest show inter frames. - for (int i = 1; i < show_frame_count; ++i) { - EncodeFrameInfo encode_frame_info; - encode_frame_info.frame_type = kFrameTypeInter; - encode_frame_info.show_idx = first_show_idx + i; - encode_frame_info.coding_index = start_coding_index + use_alt_ref + i; - - encode_frame_info.ref_frame_info = ref_frame_info; - PostUpdateRefFrameInfo(encode_frame_info.frame_type, - encode_frame_info.coding_index, &ref_frame_info); - - group_of_picture->encode_frame_list.push_back(encode_frame_info); - } -} - -// Gets group of picture information from VP9's decision, and update -// |group_of_picture| accordingly. -// This is called at the starting of encoding of each group of picture. -static void UpdateGroupOfPicture(const VP9_COMP *cpi, int start_coding_index, - const RefFrameInfo &start_ref_frame_info, - GroupOfPicture *group_of_picture) { - int first_is_key_frame; - int use_alt_ref; - int coding_frame_count; - int first_show_idx; - int last_gop_use_alt_ref; - vp9_get_next_group_of_picture(cpi, &first_is_key_frame, &use_alt_ref, - &coding_frame_count, &first_show_idx, - &last_gop_use_alt_ref); - SetGroupOfPicture(first_is_key_frame, use_alt_ref, coding_frame_count, - first_show_idx, last_gop_use_alt_ref, start_coding_index, - start_ref_frame_info, group_of_picture); -} - -#define SET_STRUCT_VALUE(config, structure, ret, field) \ - do { \ - if (strcmp(config.name, #field) == 0) { \ - structure->field = atoi(config.value); \ - ret = 1; \ - } \ - } while (false) - -static void UpdateEncodeConfig(const EncodeConfig &config, - VP9EncoderConfig *oxcf) { - int ret = 0; - SET_STRUCT_VALUE(config, oxcf, ret, key_freq); - SET_STRUCT_VALUE(config, oxcf, ret, two_pass_vbrmin_section); - SET_STRUCT_VALUE(config, oxcf, ret, two_pass_vbrmax_section); - SET_STRUCT_VALUE(config, oxcf, ret, under_shoot_pct); - SET_STRUCT_VALUE(config, oxcf, ret, over_shoot_pct); - SET_STRUCT_VALUE(config, oxcf, ret, max_threads); - SET_STRUCT_VALUE(config, oxcf, ret, frame_parallel_decoding_mode); - SET_STRUCT_VALUE(config, oxcf, ret, tile_columns); - SET_STRUCT_VALUE(config, oxcf, ret, arnr_max_frames); - SET_STRUCT_VALUE(config, oxcf, ret, arnr_strength); - SET_STRUCT_VALUE(config, oxcf, ret, lag_in_frames); - SET_STRUCT_VALUE(config, oxcf, ret, encode_breakout); - SET_STRUCT_VALUE(config, oxcf, ret, enable_tpl_model); - SET_STRUCT_VALUE(config, oxcf, ret, enable_auto_arf); - if (strcmp(config.name, "rc_mode") == 0) { - int rc_mode = atoi(config.value); - if (rc_mode >= VPX_VBR && rc_mode <= VPX_Q) { - oxcf->rc_mode = (enum vpx_rc_mode)rc_mode; - ret = 1; - } else { - fprintf(stderr, "Invalid rc_mode value: %d\n", rc_mode); - } - } - SET_STRUCT_VALUE(config, oxcf, ret, cq_level); - if (ret == 0) { - fprintf(stderr, "Ignored unsupported encode_config %s\n", config.name); - } -} - -static VP9EncoderConfig GetEncodeConfig( - int frame_width, int frame_height, vpx_rational_t frame_rate, - int target_bitrate, int encode_speed, int target_level, - vpx_enc_pass enc_pass, - const std::vector<EncodeConfig> &encode_config_list) { - VP9EncoderConfig oxcf = vp9_get_encoder_config( - frame_width, frame_height, frame_rate, target_bitrate, encode_speed, - target_level, enc_pass); - for (const auto &config : encode_config_list) { - UpdateEncodeConfig(config, &oxcf); - } - if (enc_pass == VPX_RC_FIRST_PASS) { - oxcf.lag_in_frames = 0; - } - oxcf.use_simple_encode_api = 1; - return oxcf; -} - -SimpleEncode::SimpleEncode(int frame_width, int frame_height, - int frame_rate_num, int frame_rate_den, - int target_bitrate, int num_frames, int target_level, - const char *infile_path, const char *outfile_path) { - impl_ptr_ = std::unique_ptr<EncodeImpl>(new EncodeImpl()); - frame_width_ = frame_width; - frame_height_ = frame_height; - frame_rate_num_ = frame_rate_num; - frame_rate_den_ = frame_rate_den; - target_bitrate_ = target_bitrate; - num_frames_ = num_frames; - encode_speed_ = 0; - target_level_ = target_level; - - frame_coding_index_ = 0; - show_frame_count_ = 0; - - key_frame_group_index_ = 0; - key_frame_group_size_ = 0; - - // TODO(angirbid): Should we keep a file pointer here or keep the file_path? - assert(infile_path != nullptr); - in_file_ = fopen(infile_path, "r"); - if (outfile_path != nullptr) { - out_file_ = fopen(outfile_path, "w"); - } else { - out_file_ = nullptr; - } - impl_ptr_->cpi = nullptr; - impl_ptr_->img_fmt = VPX_IMG_FMT_I420; - - InitRefFrameInfo(&ref_frame_info_); -} - -void SimpleEncode::SetEncodeSpeed(int encode_speed) { - encode_speed_ = encode_speed; -} - -StatusCode SimpleEncode::SetEncodeConfig(const char *name, const char *value) { - if (name == nullptr || value == nullptr) { - fprintf(stderr, "SetEncodeConfig: null pointer, name %p value %p\n", name, - value); - return StatusError; - } - EncodeConfig config; - snprintf(config.name, ENCODE_CONFIG_BUF_SIZE, "%s", name); - snprintf(config.value, ENCODE_CONFIG_BUF_SIZE, "%s", value); - impl_ptr_->encode_config_list.push_back(config); - return StatusOk; -} - -StatusCode SimpleEncode::DumpEncodeConfigs(int pass, FILE *fp) { - if (fp == nullptr) { - fprintf(stderr, "DumpEncodeConfigs: null pointer, fp %p\n", fp); - return StatusError; - } - vpx_enc_pass enc_pass; - if (pass == 1) { - enc_pass = VPX_RC_FIRST_PASS; - } else { - enc_pass = VPX_RC_LAST_PASS; - } - const vpx_rational_t frame_rate = - make_vpx_rational(frame_rate_num_, frame_rate_den_); - const VP9EncoderConfig oxcf = GetEncodeConfig( - frame_width_, frame_height_, frame_rate, target_bitrate_, encode_speed_, - target_level_, enc_pass, impl_ptr_->encode_config_list); - vp9_dump_encoder_config(&oxcf, fp); - return StatusOk; -} - -void SimpleEncode::ComputeFirstPassStats() { - vpx_rational_t frame_rate = - make_vpx_rational(frame_rate_num_, frame_rate_den_); - const VP9EncoderConfig oxcf = GetEncodeConfig( - frame_width_, frame_height_, frame_rate, target_bitrate_, encode_speed_, - target_level_, VPX_RC_FIRST_PASS, impl_ptr_->encode_config_list); - impl_ptr_->cpi = init_encoder(&oxcf, impl_ptr_->img_fmt); - struct lookahead_ctx *lookahead = impl_ptr_->cpi->lookahead; - int i; - int use_highbitdepth = 0; - const int num_rows_16x16 = get_num_unit_16x16(frame_height_); - const int num_cols_16x16 = get_num_unit_16x16(frame_width_); -#if CONFIG_VP9_HIGHBITDEPTH - use_highbitdepth = impl_ptr_->cpi->common.use_highbitdepth; -#endif - vpx_image_t img; - if (impl_ptr_->img_fmt == VPX_IMG_FMT_NV12) { - fprintf(stderr, "VPX_IMG_FMT_NV12 is not supported\n"); - abort(); - } - vpx_img_alloc(&img, impl_ptr_->img_fmt, frame_width_, frame_height_, 1); - rewind(in_file_); - impl_ptr_->first_pass_stats.clear(); - for (i = 0; i < num_frames_; ++i) { - assert(!vp9_lookahead_full(lookahead)); - if (img_read(&img, in_file_)) { - int next_show_idx = vp9_lookahead_next_show_idx(lookahead); - int64_t ts_start = - timebase_units_to_ticks(&oxcf.g_timebase_in_ts, next_show_idx); - int64_t ts_end = - timebase_units_to_ticks(&oxcf.g_timebase_in_ts, next_show_idx + 1); - YV12_BUFFER_CONFIG sd; - image2yuvconfig(&img, &sd); - vp9_lookahead_push(lookahead, &sd, ts_start, ts_end, use_highbitdepth, 0); - { - int64_t time_stamp; - int64_t time_end; - int flush = 1; // Makes vp9_get_compressed_data process a frame - size_t size; - unsigned int frame_flags = 0; - ENCODE_FRAME_RESULT encode_frame_info; - vp9_init_encode_frame_result(&encode_frame_info); - // TODO(angiebird): Call vp9_first_pass directly - vp9_get_compressed_data(impl_ptr_->cpi, &frame_flags, &size, nullptr, 0, - &time_stamp, &time_end, flush, - &encode_frame_info); - // vp9_get_compressed_data only generates first pass stats not - // compresses data - assert(size == 0); - // Get vp9 first pass motion vector info. - std::vector<MotionVectorInfo> mv_info(num_rows_16x16 * num_cols_16x16); - update_motion_vector_info( - impl_ptr_->cpi->fp_motion_vector_info, num_rows_16x16, - num_cols_16x16, mv_info.data(), kMotionVectorFullPixelPrecision); - fp_motion_vector_info_.push_back(mv_info); - } - impl_ptr_->first_pass_stats.push_back( - vp9_get_frame_stats(&impl_ptr_->cpi->twopass)); - } - } - // TODO(angiebird): Store the total_stats apart form first_pass_stats - impl_ptr_->first_pass_stats.push_back( - vp9_get_total_stats(&impl_ptr_->cpi->twopass)); - vp9_end_first_pass(impl_ptr_->cpi); - - // Generate key_frame_map based on impl_ptr_->first_pass_stats. - key_frame_map_ = ComputeKeyFrameMap(); - - free_encoder(impl_ptr_->cpi); - impl_ptr_->cpi = nullptr; - rewind(in_file_); - vpx_img_free(&img); -} - -std::vector<std::vector<double>> SimpleEncode::ObserveFirstPassStats() { - std::vector<std::vector<double>> output_stats; - // TODO(angiebird): This function make several assumptions of - // FIRSTPASS_STATS. 1) All elements in FIRSTPASS_STATS are double except the - // last one. 2) The last entry of first_pass_stats is the total_stats. - // Change the code structure, so that we don't have to make these assumptions - - // Note the last entry of first_pass_stats is the total_stats, we don't need - // it. - for (size_t i = 0; i < impl_ptr_->first_pass_stats.size() - 1; ++i) { - double *buf_start = - reinterpret_cast<double *>(&impl_ptr_->first_pass_stats[i]); - // We use - 1 here because the last member in FIRSTPASS_STATS is not double - double *buf_end = - buf_start + sizeof(impl_ptr_->first_pass_stats[i]) / sizeof(*buf_end) - - 1; - std::vector<double> this_stats(buf_start, buf_end); - output_stats.push_back(this_stats); - } - return output_stats; -} - -std::vector<std::vector<MotionVectorInfo>> -SimpleEncode::ObserveFirstPassMotionVectors() { - return fp_motion_vector_info_; -} - -void SimpleEncode::SetExternalGroupOfPicturesMap(int *gop_map, - int gop_map_size) { - for (int i = 0; i < gop_map_size; ++i) { - gop_map_.push_back(gop_map[i]); - } - // The following will check and modify gop_map_ to make sure the - // gop_map_ satisfies the constraints. - // 1) Each key frame position should be at the start of a gop. - // 2) The last gop should not use an alt ref. - assert(gop_map_.size() == key_frame_map_.size()); - int last_gop_start = 0; - for (int i = 0; static_cast<size_t>(i) < gop_map_.size(); ++i) { - if (key_frame_map_[i] == 1 && gop_map_[i] == 0) { - fprintf(stderr, "Add an extra gop start at show_idx %d\n", i); - // Insert a gop start at key frame location. - gop_map_[i] |= kGopMapFlagStart; - gop_map_[i] |= kGopMapFlagUseAltRef; - } - if (gop_map_[i] & kGopMapFlagStart) { - last_gop_start = i; - } - } - if (gop_map_[last_gop_start] & kGopMapFlagUseAltRef) { - fprintf(stderr, - "Last group of pictures starting at show_idx %d shouldn't use alt " - "ref\n", - last_gop_start); - gop_map_[last_gop_start] &= ~kGopMapFlagUseAltRef; - } -} - -std::vector<int> SimpleEncode::ObserveExternalGroupOfPicturesMap() { - return gop_map_; -} - -template <typename T> -T *GetVectorData(const std::vector<T> &v) { - if (v.empty()) { - return nullptr; - } - return const_cast<T *>(v.data()); -} - -static GOP_COMMAND GetGopCommand(const std::vector<int> &gop_map, - int start_show_index) { - GOP_COMMAND gop_command; - if (static_cast<size_t>(start_show_index) < gop_map.size()) { - assert((gop_map[start_show_index] & kGopMapFlagStart) != 0); - int end_show_index = start_show_index + 1; - // gop_map[end_show_index] & kGopMapFlagStart == 0 means this is - // the start of a gop. - while (static_cast<size_t>(end_show_index) < gop_map.size() && - (gop_map[end_show_index] & kGopMapFlagStart) == 0) { - ++end_show_index; - } - const int show_frame_count = end_show_index - start_show_index; - int use_alt_ref = (gop_map[start_show_index] & kGopMapFlagUseAltRef) != 0; - if (static_cast<size_t>(end_show_index) == gop_map.size()) { - // This is the last gop group, there must be no altref. - use_alt_ref = 0; - } - gop_command_on(&gop_command, show_frame_count, use_alt_ref); - } else { - gop_command_off(&gop_command); - } - return gop_command; -} - -void SimpleEncode::StartEncode() { - assert(impl_ptr_->first_pass_stats.size() > 0); - vpx_rational_t frame_rate = - make_vpx_rational(frame_rate_num_, frame_rate_den_); - VP9EncoderConfig oxcf = GetEncodeConfig( - frame_width_, frame_height_, frame_rate, target_bitrate_, encode_speed_, - target_level_, VPX_RC_LAST_PASS, impl_ptr_->encode_config_list); - - vpx_fixed_buf_t stats; - stats.buf = GetVectorData(impl_ptr_->first_pass_stats); - stats.sz = sizeof(impl_ptr_->first_pass_stats[0]) * - impl_ptr_->first_pass_stats.size(); - - vp9_set_first_pass_stats(&oxcf, &stats); - assert(impl_ptr_->cpi == nullptr); - impl_ptr_->cpi = init_encoder(&oxcf, impl_ptr_->img_fmt); - if (impl_ptr_->img_fmt == VPX_IMG_FMT_NV12) { - fprintf(stderr, "VPX_IMG_FMT_NV12 is not supported\n"); - abort(); - } - vpx_img_alloc(&impl_ptr_->tmp_img, impl_ptr_->img_fmt, frame_width_, - frame_height_, 1); - - frame_coding_index_ = 0; - show_frame_count_ = 0; - - assert(impl_ptr_->cpi != nullptr); - FRAME_INFO frame_info = vp9_get_frame_info(&oxcf); - unsigned int screen_area = frame_info.frame_width * frame_info.frame_height; - vp9_init_vizier_params(&impl_ptr_->cpi->twopass, screen_area); - - UpdateKeyFrameGroup(show_frame_count_); - - const GOP_COMMAND gop_command = GetGopCommand(gop_map_, show_frame_count_); - encode_command_set_gop_command(&impl_ptr_->cpi->encode_command, gop_command); - UpdateGroupOfPicture(impl_ptr_->cpi, frame_coding_index_, ref_frame_info_, - &group_of_picture_); - rewind(in_file_); - - if (out_file_ != nullptr) { - const char *fourcc = "VP90"; - // In SimpleEncode, we use time_base = 1 / TICKS_PER_SEC. - // Based on that, the ivf_timestamp for each image is set to - // show_idx * TICKS_PER_SEC / frame_rate - // such that each image's actual timestamp in seconds can be computed as - // ivf_timestamp * time_base == show_idx / frame_rate - // TODO(angiebird): 1) Add unit test for ivf timestamp. - // 2) Simplify the frame_rate setting process. - vpx_rational_t time_base = make_vpx_rational(1, TICKS_PER_SEC); - ivf_write_file_header_with_video_info(out_file_, *(const uint32_t *)fourcc, - num_frames_, frame_width_, - frame_height_, time_base); - } -} - -void SimpleEncode::EndEncode() { - free_encoder(impl_ptr_->cpi); - impl_ptr_->cpi = nullptr; - vpx_img_free(&impl_ptr_->tmp_img); - rewind(in_file_); -} - -void SimpleEncode::UpdateKeyFrameGroup(int key_frame_show_index) { - const VP9_COMP *cpi = impl_ptr_->cpi; - key_frame_group_index_ = 0; - key_frame_group_size_ = vp9_get_frames_to_next_key( - &cpi->oxcf, &cpi->twopass, key_frame_show_index, cpi->rc.min_gf_interval); - assert(key_frame_group_size_ > 0); - // Init the reference frame info when a new key frame group appears. - InitRefFrameInfo(&ref_frame_info_); -} - -void SimpleEncode::PostUpdateKeyFrameGroupIndex(FrameType frame_type) { - if (frame_type != kFrameTypeAltRef) { - // key_frame_group_index_ only counts show frames - ++key_frame_group_index_; - } -} - -int SimpleEncode::GetKeyFrameGroupSize() const { return key_frame_group_size_; } - -GroupOfPicture SimpleEncode::ObserveGroupOfPicture() const { - return group_of_picture_; -} - -EncodeFrameInfo SimpleEncode::GetNextEncodeFrameInfo() const { - return group_of_picture_ - .encode_frame_list[group_of_picture_.next_encode_frame_index]; -} - -void SimpleEncode::PostUpdateState( - const EncodeFrameResult &encode_frame_result) { - // This function needs to be called before the increament of - // frame_coding_index_ - PostUpdateRefFrameInfo(encode_frame_result.frame_type, frame_coding_index_, - &ref_frame_info_); - ++frame_coding_index_; - if (encode_frame_result.frame_type != kFrameTypeAltRef) { - // Only kFrameTypeAltRef is not a show frame - ++show_frame_count_; - } - - PostUpdateKeyFrameGroupIndex(encode_frame_result.frame_type); - if (key_frame_group_index_ == key_frame_group_size_) { - UpdateKeyFrameGroup(show_frame_count_); - } - - IncreaseGroupOfPictureIndex(&group_of_picture_); - if (IsGroupOfPictureFinished(group_of_picture_)) { - const GOP_COMMAND gop_command = GetGopCommand(gop_map_, show_frame_count_); - encode_command_set_gop_command(&impl_ptr_->cpi->encode_command, - gop_command); - // This function needs to be called after ref_frame_info_ is updated - // properly in PostUpdateRefFrameInfo() and UpdateKeyFrameGroup(). - UpdateGroupOfPicture(impl_ptr_->cpi, frame_coding_index_, ref_frame_info_, - &group_of_picture_); - } -} - -void SimpleEncode::EncodeFrame(EncodeFrameResult *encode_frame_result) { - VP9_COMP *cpi = impl_ptr_->cpi; - struct lookahead_ctx *lookahead = cpi->lookahead; - int use_highbitdepth = 0; -#if CONFIG_VP9_HIGHBITDEPTH - use_highbitdepth = cpi->common.use_highbitdepth; -#endif - // The lookahead's size is set to oxcf->lag_in_frames. - // We want to fill lookahead to it's max capacity if possible so that the - // encoder can construct alt ref frame in time. - // In the other words, we hope vp9_get_compressed_data to encode a frame - // every time in the function - while (!vp9_lookahead_full(lookahead)) { - // TODO(angiebird): Check whether we can move this file read logics to - // lookahead - if (img_read(&impl_ptr_->tmp_img, in_file_)) { - int next_show_idx = vp9_lookahead_next_show_idx(lookahead); - int64_t ts_start = - timebase_units_to_ticks(&cpi->oxcf.g_timebase_in_ts, next_show_idx); - int64_t ts_end = timebase_units_to_ticks(&cpi->oxcf.g_timebase_in_ts, - next_show_idx + 1); - YV12_BUFFER_CONFIG sd; - image2yuvconfig(&impl_ptr_->tmp_img, &sd); - vp9_lookahead_push(lookahead, &sd, ts_start, ts_end, use_highbitdepth, 0); - } else { - break; - } - } - - if (init_encode_frame_result(encode_frame_result, frame_width_, frame_height_, - impl_ptr_->img_fmt)) { - int64_t time_stamp; - int64_t time_end; - int flush = 1; // Make vp9_get_compressed_data encode a frame - unsigned int frame_flags = 0; - ENCODE_FRAME_RESULT encode_frame_info; - vp9_init_encode_frame_result(&encode_frame_info); - ImageBuffer_to_IMAGE_BUFFER(encode_frame_result->coded_frame, - &encode_frame_info.coded_frame); - vp9_get_compressed_data(cpi, &frame_flags, - &encode_frame_result->coding_data_byte_size, - encode_frame_result->coding_data.get(), - encode_frame_result->max_coding_data_byte_size, - &time_stamp, &time_end, flush, &encode_frame_info); - if (out_file_ != nullptr) { - ivf_write_frame_header(out_file_, time_stamp, - encode_frame_result->coding_data_byte_size); - fwrite(encode_frame_result->coding_data.get(), 1, - encode_frame_result->coding_data_byte_size, out_file_); - } - - // vp9_get_compressed_data is expected to encode a frame every time, so the - // data size should be greater than zero. - if (encode_frame_result->coding_data_byte_size <= 0) { - fprintf(stderr, "Coding data size <= 0\n"); - abort(); - } - if (encode_frame_result->coding_data_byte_size > - encode_frame_result->max_coding_data_byte_size) { - fprintf(stderr, "Coding data size exceeds the maximum.\n"); - abort(); - } - - const GroupOfPicture group_of_picture = this->ObserveGroupOfPicture(); - const int show_frame_count = group_of_picture.show_frame_count; - update_encode_frame_result(encode_frame_result, show_frame_count, - &encode_frame_info); - PostUpdateState(*encode_frame_result); - } else { - // TODO(angiebird): Clean up encode_frame_result. - fprintf(stderr, "init_encode_frame_result() failed.\n"); - this->EndEncode(); - } -} - -void SimpleEncode::EncodeFrameWithQuantizeIndex( - EncodeFrameResult *encode_frame_result, int quantize_index) { - encode_command_set_external_quantize_index(&impl_ptr_->cpi->encode_command, - quantize_index); - EncodeFrame(encode_frame_result); - encode_command_reset_external_quantize_index(&impl_ptr_->cpi->encode_command); -} - -void SimpleEncode::EncodeFrameWithTargetFrameBits( - EncodeFrameResult *encode_frame_result, int target_frame_bits, - double percent_diff) { - encode_command_set_target_frame_bits(&impl_ptr_->cpi->encode_command, - target_frame_bits, percent_diff); - EncodeFrame(encode_frame_result); - encode_command_reset_target_frame_bits(&impl_ptr_->cpi->encode_command); -} - -static int GetCodingFrameNumFromGopMap(const std::vector<int> &gop_map) { - int start_show_index = 0; - int coding_frame_count = 0; - while (static_cast<size_t>(start_show_index) < gop_map.size()) { - const GOP_COMMAND gop_command = GetGopCommand(gop_map, start_show_index); - start_show_index += gop_command.show_frame_count; - coding_frame_count += gop_command_coding_frame_count(&gop_command); - } - assert(static_cast<size_t>(start_show_index) == gop_map.size()); - return coding_frame_count; -} - -int SimpleEncode::GetCodingFrameNum() const { - assert(impl_ptr_->first_pass_stats.size() > 0); - if (gop_map_.size() > 0) { - return GetCodingFrameNumFromGopMap(gop_map_); - } - - // These are the default settings for now. - TWO_PASS twopass; - const int multi_layer_arf = 0; - const int allow_alt_ref = 1; - vpx_rational_t frame_rate = - make_vpx_rational(frame_rate_num_, frame_rate_den_); - const VP9EncoderConfig oxcf = GetEncodeConfig( - frame_width_, frame_height_, frame_rate, target_bitrate_, encode_speed_, - target_level_, VPX_RC_LAST_PASS, impl_ptr_->encode_config_list); - FRAME_INFO frame_info = vp9_get_frame_info(&oxcf); - fps_init_first_pass_info(&twopass.first_pass_info, - GetVectorData(impl_ptr_->first_pass_stats), - num_frames_); - unsigned int screen_area = frame_info.frame_width * frame_info.frame_height; - vp9_init_vizier_params(&twopass, screen_area); - return vp9_get_coding_frame_num(&oxcf, &twopass, &frame_info, multi_layer_arf, - allow_alt_ref); -} - -std::vector<int> SimpleEncode::ComputeKeyFrameMap() const { - // The last entry of first_pass_stats is the overall stats. - assert(impl_ptr_->first_pass_stats.size() == - static_cast<size_t>(num_frames_) + 1); - vpx_rational_t frame_rate = - make_vpx_rational(frame_rate_num_, frame_rate_den_); - const VP9EncoderConfig oxcf = GetEncodeConfig( - frame_width_, frame_height_, frame_rate, target_bitrate_, encode_speed_, - target_level_, VPX_RC_LAST_PASS, impl_ptr_->encode_config_list); - TWO_PASS twopass; - fps_init_first_pass_info(&twopass.first_pass_info, - GetVectorData(impl_ptr_->first_pass_stats), - num_frames_); - std::vector<int> key_frame_map(num_frames_, 0); - vp9_get_key_frame_map(&oxcf, &twopass, GetVectorData(key_frame_map)); - return key_frame_map; -} - -std::vector<int> SimpleEncode::ObserveKeyFrameMap() const { - return key_frame_map_; -} - -uint64_t SimpleEncode::GetFramePixelCount() const { - assert(frame_width_ % 2 == 0); - assert(frame_height_ % 2 == 0); - switch (impl_ptr_->img_fmt) { - case VPX_IMG_FMT_I420: return frame_width_ * frame_height_ * 3 / 2; - case VPX_IMG_FMT_I422: return frame_width_ * frame_height_ * 2; - case VPX_IMG_FMT_I444: return frame_width_ * frame_height_ * 3; - case VPX_IMG_FMT_I440: return frame_width_ * frame_height_ * 2; - case VPX_IMG_FMT_I42016: return frame_width_ * frame_height_ * 3 / 2; - case VPX_IMG_FMT_I42216: return frame_width_ * frame_height_ * 2; - case VPX_IMG_FMT_I44416: return frame_width_ * frame_height_ * 3; - case VPX_IMG_FMT_I44016: return frame_width_ * frame_height_ * 2; - default: return 0; - } -} - -SimpleEncode::~SimpleEncode() { - if (in_file_ != nullptr) { - fclose(in_file_); - } - if (out_file_ != nullptr) { - fclose(out_file_); - } -} - -} // namespace vp9
diff --git a/vp9/simple_encode.h b/vp9/simple_encode.h deleted file mode 100644 index 94ecbf2..0000000 --- a/vp9/simple_encode.h +++ /dev/null
@@ -1,584 +0,0 @@ -/* - * Copyright (c) 2019 The WebM project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#ifndef VPX_VP9_SIMPLE_ENCODE_H_ -#define VPX_VP9_SIMPLE_ENCODE_H_ - -#include <cstddef> -#include <cstdint> -#include <cstdio> -#include <memory> -#include <vector> - -namespace vp9 { - -enum StatusCode { - StatusOk = 0, - StatusError, -}; - -// TODO(angiebird): Add description for each frame type. -enum FrameType { - kFrameTypeKey = 0, - kFrameTypeInter = 1, - kFrameTypeAltRef = 2, - kFrameTypeOverlay = 3, - kFrameTypeGolden = 4, -}; - -// TODO(angiebird): Add description for each reference frame type. -// This enum numbers have to be contiguous and start from zero except -// kNoneRefFrame. -enum RefFrameType { - kRefFrameTypeLast = 0, - kRefFrameTypePast = 1, - kRefFrameTypeFuture = 2, - kRefFrameTypeMax = 3, - kRefFrameTypeNone = -1, -}; - -enum VP9_LEVEL { - LEVEL_UNKNOWN = 0, - LEVEL_AUTO = 1, - LEVEL_1 = 10, - LEVEL_1_1 = 11, - LEVEL_2 = 20, - LEVEL_2_1 = 21, - LEVEL_3 = 30, - LEVEL_3_1 = 31, - LEVEL_4 = 40, - LEVEL_4_1 = 41, - LEVEL_5 = 50, - LEVEL_5_1 = 51, - LEVEL_5_2 = 52, - LEVEL_6 = 60, - LEVEL_6_1 = 61, - LEVEL_6_2 = 62, - LEVEL_MAX = 255 -}; - -enum GopMapFlag { - kGopMapFlagStart = - 1 << 0, // Indicate this location is the start of a group of pictures. - kGopMapFlagUseAltRef = - 1 << 1, // Indicate this group of pictures will use an alt ref. Only set - // this flag when kGopMapFlagStart is set. -}; - -// The frame is split to 4x4 blocks. -// This structure contains the information of each 4x4 block. -struct PartitionInfo { - int row; // row pixel offset of current 4x4 block - int column; // column pixel offset of current 4x4 block - int row_start; // row pixel offset of the start of the prediction block - int column_start; // column pixel offset of the start of the prediction block - int width; // prediction block width - int height; // prediction block height -}; - -constexpr int kMotionVectorSubPixelPrecision = 8; -constexpr int kMotionVectorFullPixelPrecision = 1; - -// In the first pass. The frame is split to 16x16 blocks. -// This structure contains the information of each 16x16 block. -// In the second pass. The frame is split to 4x4 blocks. -// This structure contains the information of each 4x4 block. -struct MotionVectorInfo { - // Number of valid motion vectors, always 0 if this block is in the key frame. - // For inter frames, it could be 1 or 2. - int mv_count; - // The reference frame for motion vectors. If the second motion vector does - // not exist (mv_count = 1), the reference frame is kNoneRefFrame. - // Otherwise, the reference frame is either kRefFrameTypeLast, or - // kRefFrameTypePast, or kRefFrameTypeFuture. - RefFrameType ref_frame[2]; - // The row offset of motion vectors in the unit of pixel. - // If the second motion vector does not exist, the value is 0. - double mv_row[2]; - // The column offset of motion vectors in the unit of pixel. - // If the second motion vector does not exist, the value is 0. - double mv_column[2]; -}; - -// Accumulated tpl stats of all blocks in one frame. -// For each frame, the tpl stats are computed per 32x32 block. -struct TplStatsInfo { - // Intra complexity: the sum of absolute transform difference (SATD) of - // intra predicted residuals. - int64_t intra_cost; - // Inter complexity: the SATD of inter predicted residuals. - int64_t inter_cost; - // Motion compensated information flow. It measures how much information - // is propagated from the current frame to other frames. - int64_t mc_flow; - // Motion compensated dependency cost. It equals to its own intra_cost - // plus the mc_flow. - int64_t mc_dep_cost; - // Motion compensated reference cost. - int64_t mc_ref_cost; -}; - -struct RefFrameInfo { - int coding_indexes[kRefFrameTypeMax]; - - // Indicate whether the reference frames are available or not. - // When the reference frame type is not valid, it means either the to-be-coded - // frame is a key frame or the reference frame already appears in other - // reference frame type. vp9 always keeps three types of reference frame - // available. However, the duplicated reference frames will not be - // chosen by the encoder. The priorities of choosing reference frames are - // kRefFrameTypeLast > kRefFrameTypePast > kRefFrameTypeFuture. - // For example, if kRefFrameTypeLast and kRefFrameTypePast both point to the - // same frame, kRefFrameTypePast will be set to invalid. - // 1: the ref frame type is available 0: the ref frame type is not available - int valid_list[kRefFrameTypeMax]; -}; - -bool operator==(const RefFrameInfo &a, const RefFrameInfo &b); - -struct EncodeFrameInfo { - int show_idx; - - // Each show or no show frame is assigned with a coding index based on its - // coding order (starting from zero) in the coding process of the entire - // video. The coding index for each frame is unique. - int coding_index; - RefFrameInfo ref_frame_info; - FrameType frame_type; -}; - -// This structure is a copy of vp9 |nmv_component_counts|. -struct NewMotionvectorComponentCounts { - std::vector<unsigned int> sign; - std::vector<unsigned int> classes; - std::vector<unsigned int> class0; - std::vector<std::vector<unsigned int>> bits; - std::vector<std::vector<unsigned int>> class0_fp; - std::vector<unsigned int> fp; - std::vector<unsigned int> class0_hp; - std::vector<unsigned int> hp; -}; - -// This structure is a copy of vp9 |nmv_context_counts|. -struct NewMotionVectorContextCounts { - std::vector<unsigned int> joints; - std::vector<NewMotionvectorComponentCounts> comps; -}; - -using UintArray2D = std::vector<std::vector<unsigned int>>; -using UintArray3D = std::vector<std::vector<std::vector<unsigned int>>>; -using UintArray5D = std::vector< - std::vector<std::vector<std::vector<std::vector<unsigned int>>>>>; -using UintArray6D = std::vector<std::vector< - std::vector<std::vector<std::vector<std::vector<unsigned int>>>>>>; - -// This structure is a copy of vp9 |tx_counts|. -struct TransformSizeCounts { - // Transform size found in blocks of partition size 32x32. - // First dimension: transform size contexts (2). - // Second dimension: transform size type (3: 32x32, 16x16, 8x8) - UintArray2D p32x32; - // Transform size found in blocks of partition size 16x16. - // First dimension: transform size contexts (2). - // Second dimension: transform size type (2: 16x16, 8x8) - UintArray2D p16x16; - // Transform size found in blocks of partition size 8x8. - // First dimension: transform size contexts (2). - // Second dimension: transform size type (1: 8x8) - UintArray2D p8x8; - // Overall transform size count. - std::vector<unsigned int> tx_totals; -}; - -// This structure is a copy of vp9 |FRAME_COUNTS|. -struct FrameCounts { - // Intra prediction mode for luma plane. First dimension: block size (4). - // Second dimension: intra prediction mode (10). - UintArray2D y_mode; - // Intra prediction mode for chroma plane. First and second dimension: - // intra prediction mode (10). - UintArray2D uv_mode; - // Partition type. First dimension: partition contexts (16). - // Second dimension: partition type (4). - UintArray2D partition; - // Transform coefficient. - UintArray6D coef; - // End of block (the position of the last non-zero transform coefficient) - UintArray5D eob_branch; - // Interpolation filter type. First dimension: switchable filter contexts (4). - // Second dimension: filter types (3). - UintArray2D switchable_interp; - // Inter prediction mode (the motion vector type). - // First dimension: inter mode contexts (7). - // Second dimension: mode type (4). - UintArray2D inter_mode; - // Block is intra or inter predicted. First dimension: contexts (4). - // Second dimension: type (0 for intra, 1 for inter). - UintArray2D intra_inter; - // Block is compound predicted (predicted from average of two blocks). - // First dimension: contexts (5). - // Second dimension: type (0 for single, 1 for compound prediction). - UintArray2D comp_inter; - // Type of the reference frame. Only one reference frame. - // First dimension: context (5). Second dimension: context (2). - // Third dimension: count (2). - UintArray3D single_ref; - // Type of the two reference frames. - // First dimension: context (5). Second dimension: count (2). - UintArray2D comp_ref; - // Block skips transform and quantization, uses prediction as reconstruction. - // First dimension: contexts (3). Second dimension: type (0 not skip, 1 skip). - UintArray2D skip; - // Transform size. - TransformSizeCounts tx; - // New motion vector. - NewMotionVectorContextCounts mv; -}; - -struct ImageBuffer { - // The image data is stored in raster order, - // i.e. image[plane][r][c] = - // plane_buffer[plane][r * plane_width[plane] + plane_height[plane]]. - std::unique_ptr<unsigned char[]> plane_buffer[3]; - int plane_width[3]; - int plane_height[3]; -}; - -void output_image_buffer(const ImageBuffer &image_buffer, std::FILE *out_file); - -struct EncodeFrameResult { - int show_idx; - FrameType frame_type; - int coding_idx; - RefFrameInfo ref_frame_info; - size_t coding_data_bit_size; - size_t coding_data_byte_size; - // The EncodeFrame will allocate a buffer, write the coding data into the - // buffer and give the ownership of the buffer to coding_data. - std::unique_ptr<unsigned char[]> coding_data; - size_t max_coding_data_byte_size; - double psnr; - uint64_t sse; - int quantize_index; - FrameCounts frame_counts; - int num_rows_4x4; // number of row units, in size of 4. - int num_cols_4x4; // number of column units, in size of 4. - // A vector of the partition information of the frame. - // The number of elements is |num_rows_4x4| * |num_cols_4x4|. - // The frame is divided 4x4 blocks of |num_rows_4x4| rows and - // |num_cols_4x4| columns. - // Each 4x4 block contains the current pixel position (|row|, |column|), - // the start pixel position of the partition (|row_start|, |column_start|), - // and the |width|, |height| of the partition. - // The current pixel position can be the same as the start pixel position - // if the 4x4 block is the top-left block in the partition. Otherwise, they - // are different. - // Within the same partition, all 4x4 blocks have the same |row_start|, - // |column_start|, |width| and |height|. - // For example, if the frame is partitioned to a 32x32 block, - // starting at (0, 0). Then, there're 64 4x4 blocks within this partition. - // They all have the same |row_start|, |column_start|, |width|, |height|, - // which can be used to figure out the start of the current partition and - // the start of the next partition block. - // Horizontal next: |column_start| + |width|, - // Vertical next: |row_start| + |height|. - std::vector<PartitionInfo> partition_info; - // A vector of the motion vector information of the frame. - // The number of elements is |num_rows_4x4| * |num_cols_4x4|. - // The frame is divided into 4x4 blocks of |num_rows_4x4| rows and - // |num_cols_4x4| columns. - // Each 4x4 block contains 0 motion vector if this is an intra predicted - // frame (for example, the key frame). If the frame is inter predicted, - // each 4x4 block contains either 1 or 2 motion vectors. - // Similar to partition info, all 4x4 blocks inside the same partition block - // share the same motion vector information. - std::vector<MotionVectorInfo> motion_vector_info; - // A vector of the tpl stats information. - // The tpl stats measure the complexity of a frame, as well as the - // information propagated along the motion trajectory between frames, in - // the reference frame structure. - // The tpl stats could be used as a more accurate spatial and temporal - // complexity measure in addition to the first pass stats. - // The vector contains tpl stats for all show frames in a GOP. - // The tpl stats stored in the vector is according to the encoding order. - // For example, suppose there are N show frames for the current GOP. - // Then tpl_stats_info[0] stores the information of the first frame to be - // encoded for this GOP, i.e., the AltRef frame. - std::vector<TplStatsInfo> tpl_stats_info; - ImageBuffer coded_frame; - - // recode_count, q_index_history and rate_history are only available when - // EncodeFrameWithTargetFrameBits() is used. - int recode_count; - std::vector<int> q_index_history; - std::vector<int> rate_history; -}; - -struct GroupOfPicture { - // This list will be updated internally in StartEncode() and - // EncodeFrame()/EncodeFrameWithQuantizeIndex(). - // In EncodeFrame()/EncodeFrameWithQuantizeIndex(), the update will only be - // triggered when the coded frame is the last one in the previous group of - // pictures. - std::vector<EncodeFrameInfo> encode_frame_list; - - // Indicates the index of the next coding frame in encode_frame_list. - // In other words, EncodeFrameInfo of the next coding frame can be - // obtained with encode_frame_list[next_encode_frame_index]. - // Internally, next_encode_frame_index will be set to zero after the last - // frame of the group of pictures is coded. Otherwise, next_encode_frame_index - // will be increased after each EncodeFrame()/EncodeFrameWithQuantizeIndex() - // call. - int next_encode_frame_index; - - // Number of show frames in this group of pictures. - int show_frame_count; - - // The show index/timestamp of the earliest show frame in the group of - // pictures. - int start_show_index; - - // The coding index of the first coding frame in the group of pictures. - int start_coding_index; - - // Indicates whether this group of pictures starts with a key frame. - int first_is_key_frame; - - // Indicates whether this group of pictures uses an alt ref. - int use_alt_ref; - - // Indicates whether previous group of pictures used an alt ref. - int last_gop_use_alt_ref; -}; - -class SimpleEncode { - public: - // When outfile_path is set, the encoder will output the bitstream in ivf - // format. - SimpleEncode(int frame_width, int frame_height, int frame_rate_num, - int frame_rate_den, int target_bitrate, int num_frames, - int target_level, const char *infile_path, - const char *outfile_path = nullptr); - ~SimpleEncode(); - SimpleEncode(SimpleEncode &) = delete; - SimpleEncode &operator=(const SimpleEncode &) = delete; - - // Adjusts the encoder's coding speed. - // If this function is not called, the encoder will use default encode_speed - // 0. Call this function before ComputeFirstPassStats() if needed. - // The encode_speed is equivalent to --cpu-used of the vpxenc command. - // The encode_speed's range should be [0, 9]. - // Setting the encode_speed to a higher level will yield faster coding - // at the cost of lower compression efficiency. - void SetEncodeSpeed(int encode_speed); - - // Set encoder config - // The following configs in VP9EncoderConfig are allowed to change in this - // function. See https://ffmpeg.org/ffmpeg-codecs.html#libvpx for each - // config's meaning. - // Configs in VP9EncoderConfig: Equivalent configs in ffmpeg: - // 1 key_freq -g - // 2 two_pass_vbrmin_section -minrate * 100LL / bit_rate - // 3 two_pass_vbrmax_section -maxrate * 100LL / bit_rate - // 4 under_shoot_pct -undershoot-pct - // 5 over_shoot_pct -overshoot-pct - // 6 max_threads -threads - // 7 frame_parallel_decoding_mode -frame-parallel - // 8 tile_column -tile-columns - // 9 arnr_max_frames -arnr-maxframes - // 10 arnr_strength -arnr-strength - // 11 lag_in_frames -rc_lookahead - // 12 encode_breakout -static-thresh - // 13 enable_tpl_model -enable-tpl - // 14 enable_auto_arf -auto-alt-ref - // 15 rc_mode - // Possible Settings: - // 0 - Variable Bit Rate (VPX_VBR) -b:v <bit_rate> - // 1 - Constant Bit Rate (VPX_CBR) -b:v <bit_rate> -minrate <bit_rate> - // -maxrate <bit_rate> - // two_pass_vbrmin_section == 100 i.e. bit_rate == minrate == maxrate - // two_pass_vbrmax_section == 100 - // 2 - Constrained Quality (VPX_CQ) -crf <cq_level> -b:v bit_rate - // 3 - Constant Quality (VPX_Q) -crf <cq_level> -b:v 0 - // See https://trac.ffmpeg.org/wiki/Encode/VP9 for more details. - // 16 cq_level see rc_mode for details. - StatusCode SetEncodeConfig(const char *name, const char *value); - - // A debug function that dumps configs from VP9EncoderConfig - // pass = 1: first pass, pass = 2: second pass - // fp: file pointer for dumping config - StatusCode DumpEncodeConfigs(int pass, FILE *fp); - - // Makes encoder compute the first pass stats and store it at - // impl_ptr_->first_pass_stats. key_frame_map_ is also computed based on the - // first pass stats. - void ComputeFirstPassStats(); - - // Outputs the first pass stats represented by a 2-D vector. - // One can use the frame index at first dimension to retrieve the stats for - // each video frame. The stats of each video frame is a vector of 25 double - // values. For details, please check FIRSTPASS_STATS in vp9_firstpass.h - std::vector<std::vector<double>> ObserveFirstPassStats(); - - // Outputs the first pass motion vectors represented by a 2-D vector. - // One can use the frame index at first dimension to retrieve the mvs for - // each video frame. The frame is divided into 16x16 blocks. The number of - // elements is round_up(|num_rows_4x4| / 4) * round_up(|num_cols_4x4| / 4). - std::vector<std::vector<MotionVectorInfo>> ObserveFirstPassMotionVectors(); - - // Ouputs a copy of key_frame_map_, a binary vector with size equal to the - // number of show frames in the video. For each entry in the vector, 1 - // indicates the position is a key frame and 0 indicates it's not a key frame. - // This function should be called after ComputeFirstPassStats() - std::vector<int> ObserveKeyFrameMap() const; - - // Sets group of pictures map for coding the entire video. - // Each entry in the gop_map corresponds to a show frame in the video. - // Therefore, the size of gop_map should equal to the number of show frames in - // the entire video. - // If a given entry's kGopMapFlagStart is set, it means this is the start of a - // gop. Once kGopMapFlagStart is set, one can set kGopMapFlagUseAltRef to - // indicate whether this gop use altref. - // If a given entry is zero, it means it's in the middle of a gop. - // This function should be called only once after ComputeFirstPassStats(), - // before StartEncode(). - // This API will check and modify the gop_map to satisfy the following - // constraints. - // 1) Each key frame position should be at the start of a gop. - // 2) The last gop should not use an alt ref. - void SetExternalGroupOfPicturesMap(int *gop_map, int gop_map_size); - - // Observe the group of pictures map set through - // SetExternalGroupOfPicturesMap(). This function should be called after - // SetExternalGroupOfPicturesMap(). - std::vector<int> ObserveExternalGroupOfPicturesMap(); - - // Initializes the encoder for actual encoding. - // This function should be called after ComputeFirstPassStats(). - void StartEncode(); - - // Frees the encoder. - // This function should be called after StartEncode() or EncodeFrame(). - void EndEncode(); - - // The key frame group size includes one key frame plus the number of - // following inter frames. Note that the key frame group size only counts the - // show frames. The number of no show frames like alternate refereces are not - // counted. - int GetKeyFrameGroupSize() const; - - // Provides the group of pictures that the next coding frame is in. - // Only call this function between StartEncode() and EndEncode() - GroupOfPicture ObserveGroupOfPicture() const; - - // Gets encode_frame_info for the next coding frame. - // Only call this function between StartEncode() and EndEncode() - EncodeFrameInfo GetNextEncodeFrameInfo() const; - - // Encodes a frame - // This function should be called after StartEncode() and before EndEncode(). - void EncodeFrame(EncodeFrameResult *encode_frame_result); - - // Encodes a frame with a specific quantize index. - // This function should be called after StartEncode() and before EndEncode(). - void EncodeFrameWithQuantizeIndex(EncodeFrameResult *encode_frame_result, - int quantize_index); - - // Encode a frame with target frame bits usage. - // The encoder will find a quantize index to make the actual frame bits usage - // match the target. EncodeFrameWithTargetFrameBits() will recode the frame - // up to 7 times to find a q_index to make the actual_frame_bits satisfy the - // following inequality. |actual_frame_bits - target_frame_bits| * 100 / - // target_frame_bits - // <= percent_diff. - void EncodeFrameWithTargetFrameBits(EncodeFrameResult *encode_frame_result, - int target_frame_bits, - double percent_diff); - - // Gets the number of coding frames for the video. The coding frames include - // show frame and no show frame. - // This function should be called after ComputeFirstPassStats(). - int GetCodingFrameNum() const; - - // Gets the total number of pixels of YUV planes per frame. - uint64_t GetFramePixelCount() const; - - private: - // Compute the key frame locations of the video based on first pass stats. - // The results are returned as a binary vector with 1s indicating keyframes - // and 0s indicating non keyframes. - // It has to be called after impl_ptr_->first_pass_stats is computed. - std::vector<int> ComputeKeyFrameMap() const; - - // Updates key_frame_group_size_, reset key_frame_group_index_ and init - // ref_frame_info_. - void UpdateKeyFrameGroup(int key_frame_show_index); - - // Update key_frame_group_index_. - void PostUpdateKeyFrameGroupIndex(FrameType frame_type); - - void PostUpdateState(const EncodeFrameResult &encode_frame_result); - - class EncodeImpl; - - int frame_width_; // frame width in pixels. - int frame_height_; // frame height in pixels. - int frame_rate_num_; - int frame_rate_den_; - int target_bitrate_; - int num_frames_; - int encode_speed_; - int target_level_; - - std::FILE *in_file_; - std::FILE *out_file_; - std::unique_ptr<EncodeImpl> impl_ptr_; - - std::vector<int> key_frame_map_; - std::vector<int> gop_map_; - GroupOfPicture group_of_picture_; - - // The key frame group size includes one key frame plus the number of - // following inter frames. Note that the key frame group size only counts the - // show frames. The number of no show frames like alternate references are not - // counted. - int key_frame_group_size_; - - // The index for the to-be-coded show frame in the key frame group. - int key_frame_group_index_; - - // Each show or no show frame is assigned with a coding index based on its - // coding order (starting from zero) in the coding process of the entire - // video. The coding index of the to-be-coded frame. - int frame_coding_index_; - - // Number of show frames we have coded so far. - int show_frame_count_; - - // TODO(angiebird): Do we need to reset ref_frames_info_ when the next key - // frame appears? - // Reference frames info of the to-be-coded frame. - RefFrameInfo ref_frame_info_; - - // A 2-D vector of motion vector information of the frame collected - // from the first pass. The first dimension is the frame index. - // Each frame is divided into 16x16 blocks. The number of elements is - // round_up(|num_rows_4x4| / 4) * round_up(|num_cols_4x4| / 4). - // Each 16x16 block contains 0 motion vector if this is an intra predicted - // frame (for example, the key frame). If the frame is inter predicted, - // each 16x16 block contains either 1 or 2 motion vectors. - // The first motion vector is always from the LAST_FRAME. - // The second motion vector is always from the GOLDEN_FRAME. - std::vector<std::vector<MotionVectorInfo>> fp_motion_vector_info_; -}; - -} // namespace vp9 - -#endif // VPX_VP9_SIMPLE_ENCODE_H_
diff --git a/vp9/vp9_cx_iface.c b/vp9/vp9_cx_iface.c index 55a2d0b..072ab27 100644 --- a/vp9/vp9_cx_iface.c +++ b/vp9/vp9_cx_iface.c
@@ -17,6 +17,7 @@ #include "vpx/vpx_encoder.h" #include "vpx/vpx_ext_ratectrl.h" #include "vpx_dsp/psnr.h" +#include "vpx_dsp/vpx_dsp_common.h" #include "vpx_ports/static_assert.h" #include "vpx_ports/system_state.h" #include "vpx_util/vpx_timestamp.h" @@ -507,7 +508,6 @@ static vpx_codec_err_t set_encoder_config( VP9EncoderConfig *oxcf, vpx_codec_enc_cfg_t *cfg, const struct vp9_extracfg *extra_cfg) { - const int is_vbr = cfg->rc_end_usage == VPX_VBR; int sl, tl; unsigned int raw_target_rate; oxcf->profile = cfg->g_profile; @@ -570,9 +570,9 @@ oxcf->resize_mode = RESIZE_NONE; } - oxcf->maximum_buffer_size_ms = is_vbr ? 240000 : cfg->rc_buf_sz; - oxcf->starting_buffer_level_ms = is_vbr ? 60000 : cfg->rc_buf_initial_sz; - oxcf->optimal_buffer_level_ms = is_vbr ? 60000 : cfg->rc_buf_optimal_sz; + oxcf->maximum_buffer_size_ms = cfg->rc_buf_sz; + oxcf->starting_buffer_level_ms = cfg->rc_buf_initial_sz; + oxcf->optimal_buffer_level_ms = cfg->rc_buf_optimal_sz; oxcf->drop_frames_water_mark = cfg->rc_dropframe_thresh; @@ -671,7 +671,6 @@ } if (get_level_index(oxcf->target_level) >= 0) config_target_level(oxcf); - oxcf->use_simple_encode_api = 0; // vp9_dump_encoder_config(oxcf, stderr); return VPX_CODEC_OK; } @@ -811,14 +810,17 @@ // Note: function encoder_set_config() is allowed to be called multiple // times. However, when the original frame width or height is less than two // times of the new frame width or height, a forced key frame should be - // used. To make sure the correct detection of a forced key frame, we need + // used (for the case of single spatial layer, since otherwise a previous + // encoded frame at a lower layer may be the desired reference). To make + // sure the correct detection of a forced key frame, we need // to update the frame width and height only when the actual encoding is // performed. cpi->last_coded_width and cpi->last_coded_height are used to // track the actual coded frame size. if ((ctx->cpi->last_coded_width && ctx->cpi->last_coded_height && - !valid_ref_frame_size(ctx->cpi->last_coded_width, - ctx->cpi->last_coded_height, cfg->g_w, - cfg->g_h)) || + (!valid_ref_frame_size(ctx->cpi->last_coded_width, + ctx->cpi->last_coded_height, cfg->g_w, + cfg->g_h) && + ctx->cpi->svc.number_spatial_layers == 1)) || (ctx->cpi->initial_width && (int)cfg->g_w > ctx->cpi->initial_width) || (ctx->cpi->initial_height && (int)cfg->g_h > ctx->cpi->initial_height)) { @@ -911,8 +913,7 @@ struct vp9_extracfg extra_cfg = ctx->extra_cfg; // Use fastest speed setting (speed 9 or -9) if it's set beyond the range. extra_cfg.cpu_used = CAST(VP8E_SET_CPUUSED, args); - extra_cfg.cpu_used = VPXMIN(9, extra_cfg.cpu_used); - extra_cfg.cpu_used = VPXMAX(-9, extra_cfg.cpu_used); + extra_cfg.cpu_used = clamp(extra_cfg.cpu_used, -9, 9); #if CONFIG_REALTIME_ONLY if (extra_cfg.cpu_used > -5 && extra_cfg.cpu_used < 5) extra_cfg.cpu_used = (extra_cfg.cpu_used > 0) ? 5 : -5; @@ -1878,6 +1879,12 @@ LAYER_CONTEXT *lc = &cpi->svc.layer_context[layer]; lc->max_q = params->max_quantizers[layer]; lc->min_q = params->min_quantizers[layer]; + // Checks on valid scale factors. + if (params->scaling_factor_num[sl] < 1 || + params->scaling_factor_den[sl] < 1 || + (params->scaling_factor_num[sl] > params->scaling_factor_den[sl])) { + return VPX_CODEC_INVALID_PARAM; + } lc->scaling_factor_num = params->scaling_factor_num[sl]; lc->scaling_factor_den = params->scaling_factor_den[sl]; lc->speed = params->speed_per_layer[sl]; @@ -1973,7 +1980,7 @@ va_list args) { struct vp9_extracfg extra_cfg = ctx->extra_cfg; int data = va_arg(args, int); - data = VPXMIN(VPXMAX(data, -15), 15); + data = clamp(data, -15, 15); extra_cfg.delta_q_uv = data; return update_extra_cfg(ctx, &extra_cfg); } @@ -2285,7 +2292,8 @@ encoder_set_config, // vpx_codec_enc_config_set_fn_t encoder_get_global_headers, // vpx_codec_get_global_headers_fn_t encoder_get_preview, // vpx_codec_get_preview_frame_fn_t - NULL // vpx_codec_enc_mr_get_mem_loc_fn_t + NULL, // vpx_codec_enc_mr_get_mem_loc_fn_t + NULL // vpx_codec_enc_mr_free_mem_loc_fn_t } }; @@ -2490,7 +2498,6 @@ DUMP_STRUCT_VALUE(fp, oxcf, row_mt); DUMP_STRUCT_VALUE(fp, oxcf, motion_vector_unit_test); DUMP_STRUCT_VALUE(fp, oxcf, delta_q_uv); - DUMP_STRUCT_VALUE(fp, oxcf, use_simple_encode_api); } FRAME_INFO vp9_get_frame_info(const VP9EncoderConfig *oxcf) {
diff --git a/vp9/vp9_dx_iface.c b/vp9/vp9_dx_iface.c index 7567910..b6eeee0 100644 --- a/vp9/vp9_dx_iface.c +++ b/vp9/vp9_dx_iface.c
@@ -737,6 +737,7 @@ NULL, // vpx_codec_enc_config_set_fn_t NULL, // vpx_codec_get_global_headers_fn_t NULL, // vpx_codec_get_preview_frame_fn_t - NULL // vpx_codec_enc_mr_get_mem_loc_fn_t + NULL, // vpx_codec_enc_mr_get_mem_loc_fn_t + NULL // vpx_codec_enc_mr_free_mem_loc_fn_t } };
diff --git a/vp9/vp9cx.mk b/vp9/vp9cx.mk index 7a0e2d8..2248e6b 100644 --- a/vp9/vp9cx.mk +++ b/vp9/vp9cx.mk
@@ -110,10 +110,14 @@ VP9_CX_SRCS-yes += encoder/vp9_mbgraph.c VP9_CX_SRCS-yes += encoder/vp9_mbgraph.h +VP9_CX_SRCS-$(HAVE_SSSE3) += encoder/x86/temporal_filter_ssse3.c VP9_CX_SRCS-$(HAVE_SSE4_1) += encoder/x86/temporal_filter_sse4.c +VP9_CX_SRCS-$(HAVE_AVX2) += encoder/x86/temporal_filter_avx2.c VP9_CX_SRCS-$(HAVE_SSE4_1) += encoder/vp9_temporal_filter_constants.h VP9_CX_SRCS-$(HAVE_NEON) += encoder/arm/neon/vp9_temporal_filter_neon.c VP9_CX_SRCS-$(HAVE_NEON) += encoder/vp9_temporal_filter_constants.h +VP9_CX_SRCS-$(HAVE_NEON_DOTPROD) += encoder/arm/neon/vp9_temporal_filter_neon_dotprod.c +VP9_CX_SRCS-$(HAVE_NEON_I8MM) += encoder/arm/neon/vp9_temporal_filter_neon_i8mm.c VP9_CX_SRCS-$(HAVE_SSE2) += encoder/x86/vp9_quantize_sse2.c VP9_CX_SRCS-$(HAVE_SSSE3) += encoder/x86/vp9_quantize_ssse3.c @@ -121,7 +125,9 @@ VP9_CX_SRCS-$(HAVE_NEON) += encoder/arm/neon/vp9_diamond_search_sad_neon.c ifeq ($(CONFIG_VP9_HIGHBITDEPTH),yes) VP9_CX_SRCS-$(HAVE_SSE2) += encoder/x86/vp9_highbd_block_error_intrin_sse2.c +VP9_CX_SRCS-$(HAVE_SSSE3) += encoder/x86/highbd_temporal_filter_ssse3.c VP9_CX_SRCS-$(HAVE_SSE4_1) += encoder/x86/highbd_temporal_filter_sse4.c +VP9_CX_SRCS-$(HAVE_AVX2) += encoder/x86/highbd_temporal_filter_avx2.c VP9_CX_SRCS-$(HAVE_NEON) += encoder/arm/neon/vp9_highbd_temporal_filter_neon.c endif @@ -162,10 +168,16 @@ VP9_CX_SRCS_REMOVE-$(CONFIG_REALTIME_ONLY) += encoder/vp9_firstpass.c VP9_CX_SRCS_REMOVE-$(CONFIG_REALTIME_ONLY) += encoder/vp9_mbgraph.c VP9_CX_SRCS_REMOVE-$(CONFIG_REALTIME_ONLY) += encoder/vp9_temporal_filter.c +VP9_CX_SRCS_REMOVE-$(CONFIG_REALTIME_ONLY) += encoder/x86/temporal_filter_ssse3.c VP9_CX_SRCS_REMOVE-$(CONFIG_REALTIME_ONLY) += encoder/x86/temporal_filter_sse4.c +VP9_CX_SRCS_REMOVE-$(CONFIG_REALTIME_ONLY) += encoder/x86/temporal_filter_avx2.c VP9_CX_SRCS_REMOVE-$(CONFIG_REALTIME_ONLY) += encoder/vp9_temporal_filter_constants.h +VP9_CX_SRCS_REMOVE-$(CONFIG_REALTIME_ONLY) += encoder/x86/highbd_temporal_filter_ssse3.c VP9_CX_SRCS_REMOVE-$(CONFIG_REALTIME_ONLY) += encoder/x86/highbd_temporal_filter_sse4.c +VP9_CX_SRCS_REMOVE-$(CONFIG_REALTIME_ONLY) += encoder/x86/highbd_temporal_filter_avx2.c VP9_CX_SRCS_REMOVE-$(CONFIG_REALTIME_ONLY) += encoder/arm/neon/vp9_temporal_filter_neon.c +VP9_CX_SRCS_REMOVE-$(CONFIG_REALTIME_ONLY) += encoder/arm/neon/vp9_temporal_filter_neon_dotprod.c +VP9_CX_SRCS_REMOVE-$(CONFIG_REALTIME_ONLY) += encoder/arm/neon/vp9_temporal_filter_neon_i8mm.c VP9_CX_SRCS_REMOVE-$(CONFIG_REALTIME_ONLY) += encoder/arm/neon/vp9_highbd_temporal_filter_neon.c VP9_CX_SRCS_REMOVE-$(CONFIG_REALTIME_ONLY) += encoder/vp9_alt_ref_aq.h VP9_CX_SRCS_REMOVE-$(CONFIG_REALTIME_ONLY) += encoder/vp9_alt_ref_aq.c
diff --git a/vpx/internal/vpx_codec_internal.h b/vpx/internal/vpx_codec_internal.h index 275b6a4..ff51881 100644 --- a/vpx/internal/vpx_codec_internal.h +++ b/vpx/internal/vpx_codec_internal.h
@@ -269,6 +269,8 @@ typedef vpx_codec_err_t (*vpx_codec_enc_mr_get_mem_loc_fn_t)( const vpx_codec_enc_cfg_t *cfg, void **mem_loc); +typedef void (*vpx_codec_enc_mr_free_mem_loc_fn_t)(void *mem_loc); + /*!\brief usage configuration mapping * * This structure stores the mapping between usage identifiers and @@ -318,6 +320,8 @@ get_preview; /**< \copydoc ::vpx_codec_get_preview_frame_fn_t */ vpx_codec_enc_mr_get_mem_loc_fn_t mr_get_mem_loc; /**< \copydoc ::vpx_codec_enc_mr_get_mem_loc_fn_t */ + vpx_codec_enc_mr_free_mem_loc_fn_t + mr_free_mem_loc; /**< \copydoc ::vpx_codec_enc_mr_free_mem_loc_fn_t */ } enc; };
diff --git a/vpx/src/vpx_encoder.c b/vpx/src/vpx_encoder.c index 3af4cea..36dfa51 100644 --- a/vpx/src/vpx_encoder.c +++ b/vpx/src/vpx_encoder.c
@@ -15,10 +15,9 @@ #include <assert.h> #include <limits.h> #include <stdint.h> -#include <stdlib.h> #include <string.h> -#include "vp8/common/blockd.h" #include "vpx_config.h" +#include "vpx/vpx_encoder.h" #include "vpx/internal/vpx_codec_internal.h" #define SAVE_STATUS(ctx, var) ((ctx) ? ((ctx)->err = (var)) : (var)) @@ -68,13 +67,14 @@ } vpx_codec_err_t vpx_codec_enc_init_multi_ver( - vpx_codec_ctx_t *ctx, vpx_codec_iface_t *iface, vpx_codec_enc_cfg_t *cfg, - int num_enc, vpx_codec_flags_t flags, vpx_rational_t *dsf, int ver) { + vpx_codec_ctx_t *ctx, vpx_codec_iface_t *iface, + const vpx_codec_enc_cfg_t *cfg, int num_enc, vpx_codec_flags_t flags, + const vpx_rational_t *dsf, int ver) { vpx_codec_err_t res = VPX_CODEC_OK; if (ver != VPX_ENCODER_ABI_VERSION) res = VPX_CODEC_ABI_MISMATCH; - else if (!ctx || !iface || !cfg || (num_enc > 16 || num_enc < 1)) + else if (!ctx || !iface || !cfg || (num_enc > 16 || num_enc < 1) || !dsf) res = VPX_CODEC_INVALID_PARAM; else if (iface->abi_version != VPX_CODEC_INTERNAL_ABI_VERSION) res = VPX_CODEC_ABI_MISMATCH; @@ -90,7 +90,7 @@ #if CONFIG_MULTI_RES_ENCODING int mem_loc_owned = 0; #endif - void *mem_loc = NULL; + void *mem_loc; if (iface->enc.mr_get_mem_loc == NULL) return VPX_CODEC_INCAPABLE; @@ -106,8 +106,7 @@ mr_cfg.mr_low_res_mode_info = mem_loc; mr_cfg.mr_total_resolutions = num_enc; mr_cfg.mr_encoder_id = num_enc - 1 - i; - mr_cfg.mr_down_sampling_factor.num = dsf->num; - mr_cfg.mr_down_sampling_factor.den = dsf->den; + mr_cfg.mr_down_sampling_factor = *dsf; ctx->iface = iface; ctx->name = iface->name; @@ -136,8 +135,7 @@ #if CONFIG_MULTI_RES_ENCODING if (!mem_loc_owned) { assert(mem_loc); - free(((LOWER_RES_FRAME_INFO *)mem_loc)->mb_info); - free(mem_loc); + iface->enc.mr_free_mem_loc(mem_loc); } #endif return SAVE_STATUS(ctx, res);
diff --git a/vpx/vp8cx.h b/vpx/vp8cx.h index 4e29725..2665630 100644 --- a/vpx/vp8cx.h +++ b/vpx/vp8cx.h
@@ -877,7 +877,7 @@ VP8_EIGHT_TOKENPARTITION = 3 } vp8e_token_partitions; -/*!brief VP9 encoder content type */ +/*!\brief VP9 encoder content type */ typedef enum { VP9E_CONTENT_DEFAULT, VP9E_CONTENT_SCREEN,
diff --git a/vpx/vpx_decoder.h b/vpx/vpx_decoder.h index e2c31ea..0536d5d 100644 --- a/vpx/vpx_decoder.h +++ b/vpx/vpx_decoder.h
@@ -60,7 +60,7 @@ #define VPX_CODEC_CAP_INPUT_FRAGMENTS 0x100000 /*!\brief Can support frame-based multi-threading */ #define VPX_CODEC_CAP_FRAME_THREADING 0x200000 -/*!brief Can support external frame buffers */ +/*!\brief Can support external frame buffers */ #define VPX_CODEC_CAP_EXTERNAL_FRAME_BUFFER 0x400000 /*! \brief Initialization-time Feature Enabling
diff --git a/vpx/vpx_encoder.h b/vpx/vpx_encoder.h index 501723b..a26c8b2 100644 --- a/vpx/vpx_encoder.h +++ b/vpx/vpx_encoder.h
@@ -879,7 +879,7 @@ * * \param[in] ctx Pointer to this instance's context. * \param[in] iface Pointer to the algorithm interface to use. - * \param[in] cfg Configuration to use, if known. May be NULL. + * \param[in] cfg Configuration to use. * \param[in] flags Bitfield of VPX_CODEC_USE_* flags * \param[in] ver ABI version number. Must be set to * VPX_ENCODER_ABI_VERSION @@ -902,27 +902,32 @@ /*!\brief Initialize multi-encoder instance * - * Initializes multi-encoder context using the given interface. + * Initializes multiple encoder contexts using the given interface. * Applications should call the vpx_codec_enc_init_multi convenience macro * instead of this function directly, to ensure that the ABI version number * parameter is properly initialized. * - * \param[in] ctx Pointer to this instance's context. + * \param[in] ctx Pointer to an array of num_enc instances' contexts. * \param[in] iface Pointer to the algorithm interface to use. - * \param[in] cfg Configuration to use, if known. May be NULL. + * \param[in] cfg An array of num_enc configurations to use. * \param[in] num_enc Total number of encoders. * \param[in] flags Bitfield of VPX_CODEC_USE_* flags - * \param[in] dsf Pointer to down-sampling factors. + * \param[in] dsf Pointer to an array of num_enc down-sampling factors. * \param[in] ver ABI version number. Must be set to * VPX_ENCODER_ABI_VERSION * \retval #VPX_CODEC_OK * The encoder algorithm has been initialized. * \retval #VPX_CODEC_MEM_ERROR * Memory allocation failed. + * + * \note + * This is only supported by VP8. iface must point to the interface to the VP8 + * encoder. */ vpx_codec_err_t vpx_codec_enc_init_multi_ver( - vpx_codec_ctx_t *ctx, vpx_codec_iface_t *iface, vpx_codec_enc_cfg_t *cfg, - int num_enc, vpx_codec_flags_t flags, vpx_rational_t *dsf, int ver); + vpx_codec_ctx_t *ctx, vpx_codec_iface_t *iface, + const vpx_codec_enc_cfg_t *cfg, int num_enc, vpx_codec_flags_t flags, + const vpx_rational_t *dsf, int ver); /*!\brief Convenience macro for vpx_codec_enc_init_multi_ver() *
diff --git a/vpx/vpx_ext_ratectrl.h b/vpx/vpx_ext_ratectrl.h index 4820d36..db4f04d 100644 --- a/vpx/vpx_ext_ratectrl.h +++ b/vpx/vpx_ext_ratectrl.h
@@ -132,14 +132,29 @@ */ #define VPX_DEFAULT_RDMULT -1 +/*!\brief Superblock quantization parameters + * Store the superblock quantiztaion parameters + */ +typedef struct sb_parameters { + int q_index; /**< Quantizer step index [0..255]*/ + int rdmult; /**< Superblock level Lagrangian multiplier*/ +} sb_params; + /*!\brief Encode frame decision made by the external rate control model * * The encoder will receive the decision from the external rate control model * through vpx_rc_funcs_t::get_encodeframe_decision(). */ typedef struct vpx_rc_encodeframe_decision { - int q_index; /**< Quantizer step index [0..255]*/ - int rdmult; /**< Frame level Lagrangian multiplier*/ + int q_index; /**< Required: Quantizer step index [0..255]*/ + int rdmult; /**< Required: Frame level Lagrangian multiplier*/ + int delta_q_uv; /**< Required: Delta QP for UV */ + /*! + * Optional: Superblock quantization parameters + * It is zero initialized by default. It will be set for key and ARF frames + * but not leaf frames. + */ + sb_params *sb_params_list; } vpx_rc_encodeframe_decision_t; /*!\brief Information for the frame to be encoded.
diff --git a/vpx/vpx_tpl.h b/vpx/vpx_tpl.h index e14eefc..064c566 100644 --- a/vpx/vpx_tpl.h +++ b/vpx/vpx_tpl.h
@@ -38,10 +38,11 @@ int16_t col; /**< Pixel col of the top left corner */ int64_t intra_cost; /**< Intra cost */ int64_t inter_cost; /**< Inter cost */ - int16_t mv_r; /**< Motion vector row */ - int16_t mv_c; /**< Motion vector col */ + int16_t mv_r; /**< Motion vector row in pixel */ + int16_t mv_c; /**< Motion vector col in pixel */ int64_t srcrf_rate; /**< Rate from source ref frame */ int64_t srcrf_dist; /**< Distortion from source ref frame */ + int64_t pred_error; /**< Prediction error */ int64_t inter_pred_err; /**< Inter prediction error */ int64_t intra_pred_err; /**< Intra prediction error */ int ref_frame_index; /**< Ref frame index in the ref frame buffer */
diff --git a/vpx_dsp/arm/fdct_partial_neon.c b/vpx_dsp/arm/fdct_partial_neon.c index df0da54..ee9e599 100644 --- a/vpx_dsp/arm/fdct_partial_neon.c +++ b/vpx_dsp/arm/fdct_partial_neon.c
@@ -37,13 +37,15 @@ output[1] = 0; } -// Visual Studio 2022 (cl.exe) targeting AArch64 with optimizations enabled -// will fail with an internal compiler error. -// See: +// Visual Studio 2022 (cl.exe) < 17.7 targeting AArch64 with optimizations +// enabled will fail with an internal compiler error. See: // https://developercommunity.visualstudio.com/t/Compiler-crash-C1001-when-building-a-for/10346110 -// TODO(jzern): check the compiler version after a fix for the issue is -// released. -#if defined(_MSC_VER) && defined(_M_ARM64) && !defined(__clang__) +#if defined(_MSC_VER) && _MSC_VER < 1937 && defined(_M_ARM64) && \ + !defined(__clang__) +#define AOM_WORK_AROUND_MSVC_BUG_10346110 +#endif + +#ifdef AOM_WORK_AROUND_MSVC_BUG_10346110 #pragma optimize("", off) #endif void vpx_fdct8x8_1_neon(const int16_t *input, tran_low_t *output, int stride) { @@ -58,9 +60,10 @@ output[0] = (tran_low_t)horizontal_add_int16x8(sum); output[1] = 0; } -#if defined(_MSC_VER) && defined(_M_ARM64) && !defined(__clang__) +#ifdef AOM_WORK_AROUND_MSVC_BUG_10346110 #pragma optimize("", on) #endif +#undef AOM_WORK_AROUND_MSVC_BUG_10346110 void vpx_fdct16x16_1_neon(const int16_t *input, tran_low_t *output, int stride) {
diff --git a/vpx_dsp/arm/highbd_vpx_convolve8_sve2.c b/vpx_dsp/arm/highbd_vpx_convolve8_sve2.c index bf0ee96..06be652 100644 --- a/vpx_dsp/arm/highbd_vpx_convolve8_sve2.c +++ b/vpx_dsp/arm/highbd_vpx_convolve8_sve2.c
@@ -53,13 +53,13 @@ int16x8_t s2q = vcombine_s16(s2, vdup_n_s16(0)); int16x8_t s3q = vcombine_s16(s3, vdup_n_s16(0)); - int32x4_t s01 = vreinterpretq_s32_s16(vzip1q_s16(s0q, s1q)); - int32x4_t s23 = vreinterpretq_s32_s16(vzip1q_s16(s2q, s3q)); + int16x8_t s02 = vzip1q_s16(s0q, s2q); + int16x8_t s13 = vzip1q_s16(s1q, s3q); - int32x4x2_t t0123 = vzipq_s32(s01, s23); + int16x8x2_t s0123 = vzipq_s16(s02, s13); - res[0] = vreinterpretq_s16_s32(t0123.val[0]); - res[1] = vreinterpretq_s16_s32(t0123.val[1]); + res[0] = s0123.val[0]; + res[1] = s0123.val[1]; } static INLINE void transpose_concat_8x4(const int16x8_t s0, const int16x8_t s1, @@ -76,18 +76,16 @@ // res[2]: 04 14 24 34 05 15 25 35 // res[3]: 06 16 26 36 07 17 27 37 - int16x8x2_t s01 = vzipq_s16(s0, s1); - int16x8x2_t s23 = vzipq_s16(s2, s3); + int16x8x2_t s02 = vzipq_s16(s0, s2); + int16x8x2_t s13 = vzipq_s16(s1, s3); - int32x4x2_t t0123_lo = vzipq_s32(vreinterpretq_s32_s16(s01.val[0]), - vreinterpretq_s32_s16(s23.val[0])); - int32x4x2_t t0123_hi = vzipq_s32(vreinterpretq_s32_s16(s01.val[1]), - vreinterpretq_s32_s16(s23.val[1])); + int16x8x2_t s0123_lo = vzipq_s16(s02.val[0], s13.val[0]); + int16x8x2_t s0123_hi = vzipq_s16(s02.val[1], s13.val[1]); - res[0] = vreinterpretq_s16_s32(t0123_lo.val[0]); - res[1] = vreinterpretq_s16_s32(t0123_lo.val[1]); - res[2] = vreinterpretq_s16_s32(t0123_hi.val[0]); - res[3] = vreinterpretq_s16_s32(t0123_hi.val[1]); + res[0] = s0123_lo.val[0]; + res[1] = s0123_lo.val[1]; + res[2] = s0123_hi.val[0]; + res[3] = s0123_hi.val[1]; } static INLINE void vpx_tbl2x4_s16(int16x8_t s0[4], int16x8_t s1[4],
diff --git a/vpx_dsp/arm/mem_neon.h b/vpx_dsp/arm/mem_neon.h index 268c4bd..de78a6e 100644 --- a/vpx_dsp/arm/mem_neon.h +++ b/vpx_dsp/arm/mem_neon.h
@@ -372,6 +372,36 @@ *s7 = vld1_u8(s); } +static INLINE void load_u8_8x11(const uint8_t *s, ptrdiff_t p, + uint8x8_t *const s0, uint8x8_t *const s1, + uint8x8_t *const s2, uint8x8_t *const s3, + uint8x8_t *const s4, uint8x8_t *const s5, + uint8x8_t *const s6, uint8x8_t *const s7, + uint8x8_t *const s8, uint8x8_t *const s9, + uint8x8_t *const s10) { + *s0 = vld1_u8(s); + s += p; + *s1 = vld1_u8(s); + s += p; + *s2 = vld1_u8(s); + s += p; + *s3 = vld1_u8(s); + s += p; + *s4 = vld1_u8(s); + s += p; + *s5 = vld1_u8(s); + s += p; + *s6 = vld1_u8(s); + s += p; + *s7 = vld1_u8(s); + s += p; + *s8 = vld1_u8(s); + s += p; + *s9 = vld1_u8(s); + s += p; + *s10 = vld1_u8(s); +} + static INLINE void store_u8_8x8(uint8_t *s, const ptrdiff_t p, const uint8x8_t s0, const uint8x8_t s1, const uint8x8_t s2, const uint8x8_t s3,
diff --git a/vpx_dsp/arm/vpx_convolve8_neon_dotprod.c b/vpx_dsp/arm/vpx_convolve8_neon_dotprod.c index b05a49d..ad5ed37 100644 --- a/vpx_dsp/arm/vpx_convolve8_neon_dotprod.c +++ b/vpx_dsp/arm/vpx_convolve8_neon_dotprod.c
@@ -375,13 +375,10 @@ int8x16_t a2q = vcombine_s8(a2, vdup_n_s8(0)); int8x16_t a3q = vcombine_s8(a3, vdup_n_s8(0)); - int8x16_t a01 = vzipq_s8(a0q, a1q).val[0]; - int8x16_t a23 = vzipq_s8(a2q, a3q).val[0]; + int8x16_t a02 = vzipq_s8(a0q, a2q).val[0]; + int8x16_t a13 = vzipq_s8(a1q, a3q).val[0]; - int16x8_t a0123 = - vzipq_s16(vreinterpretq_s16_s8(a01), vreinterpretq_s16_s8(a23)).val[0]; - - *b = vreinterpretq_s8_s16(a0123); + *b = vzipq_s8(a02, a13).val[0]; } static INLINE void transpose_concat_8x4(int8x8_t a0, int8x8_t a1, int8x8_t a2, @@ -401,14 +398,13 @@ int8x16_t a2q = vcombine_s8(a2, vdup_n_s8(0)); int8x16_t a3q = vcombine_s8(a3, vdup_n_s8(0)); - int8x16_t a01 = vzipq_s8(a0q, a1q).val[0]; - int8x16_t a23 = vzipq_s8(a2q, a3q).val[0]; + int8x16_t a02 = vzipq_s8(a0q, a2q).val[0]; + int8x16_t a13 = vzipq_s8(a1q, a3q).val[0]; - int16x8x2_t a0123 = - vzipq_s16(vreinterpretq_s16_s8(a01), vreinterpretq_s16_s8(a23)); + int8x16x2_t a0123 = vzipq_s8(a02, a13); - *b0 = vreinterpretq_s8_s16(a0123.val[0]); - *b1 = vreinterpretq_s8_s16(a0123.val[1]); + *b0 = a0123.val[0]; + *b1 = a0123.val[1]; } static INLINE int16x4_t convolve8_4_v(const int8x16_t samples_lo,
diff --git a/vpx_dsp/arm/vpx_convolve8_neon_i8mm.c b/vpx_dsp/arm/vpx_convolve8_neon_i8mm.c index e582004..b9abe80 100644 --- a/vpx_dsp/arm/vpx_convolve8_neon_i8mm.c +++ b/vpx_dsp/arm/vpx_convolve8_neon_i8mm.c
@@ -356,13 +356,10 @@ uint8x16_t a2q = vcombine_u8(a2, vdup_n_u8(0)); uint8x16_t a3q = vcombine_u8(a3, vdup_n_u8(0)); - uint8x16_t a01 = vzipq_u8(a0q, a1q).val[0]; - uint8x16_t a23 = vzipq_u8(a2q, a3q).val[0]; + uint8x16_t a02 = vzipq_u8(a0q, a2q).val[0]; + uint8x16_t a13 = vzipq_u8(a1q, a3q).val[0]; - uint16x8_t a0123 = - vzipq_u16(vreinterpretq_u16_u8(a01), vreinterpretq_u16_u8(a23)).val[0]; - - *b = vreinterpretq_u8_u16(a0123); + *b = vzipq_u8(a02, a13).val[0]; } static INLINE void transpose_concat_8x4(uint8x8_t a0, uint8x8_t a1, @@ -382,14 +379,13 @@ uint8x16_t a2q = vcombine_u8(a2, vdup_n_u8(0)); uint8x16_t a3q = vcombine_u8(a3, vdup_n_u8(0)); - uint8x16_t a01 = vzipq_u8(a0q, a1q).val[0]; - uint8x16_t a23 = vzipq_u8(a2q, a3q).val[0]; + uint8x16_t a02 = vzipq_u8(a0q, a2q).val[0]; + uint8x16_t a13 = vzipq_u8(a1q, a3q).val[0]; - uint16x8x2_t a0123 = - vzipq_u16(vreinterpretq_u16_u8(a01), vreinterpretq_u16_u8(a23)); + uint8x16x2_t a0123 = vzipq_u8(a02, a13); - *b0 = vreinterpretq_u8_u16(a0123.val[0]); - *b1 = vreinterpretq_u8_u16(a0123.val[1]); + *b0 = a0123.val[0]; + *b1 = a0123.val[1]; } static INLINE int16x4_t convolve8_4_v(const uint8x16_t samples_lo,
diff --git a/vpx_dsp/loongarch/quantize_lsx.c b/vpx_dsp/loongarch/quantize_lsx.c index 9bb1691..1299e75 100644 --- a/vpx_dsp/loongarch/quantize_lsx.c +++ b/vpx_dsp/loongarch/quantize_lsx.c
@@ -91,7 +91,7 @@ #if !CONFIG_VP9_HIGHBITDEPTH -void vpx_quantize_b_lsx(const int16_t *coeff_ptr, intptr_t n_coeffs, +void vpx_quantize_b_lsx(const tran_low_t *coeff_ptr, intptr_t n_coeffs, const struct macroblock_plane *const mb_plane, tran_low_t *qcoeff_ptr, tran_low_t *dqcoeff_ptr, const int16_t *dequant_ptr, uint16_t *eob_ptr,
diff --git a/vpx_dsp/vpx_dsp.mk b/vpx_dsp/vpx_dsp.mk index e3de5bc..357ad08 100644 --- a/vpx_dsp/vpx_dsp.mk +++ b/vpx_dsp/vpx_dsp.mk
@@ -395,6 +395,7 @@ DSP_SRCS-$(HAVE_AVX2) += x86/sad_avx2.c DSP_SRCS-$(HAVE_AVX2) += x86/subtract_avx2.c DSP_SRCS-$(HAVE_AVX512) += x86/sad4d_avx512.c +DSP_SRCS-$(HAVE_AVX512) += x86/sad_avx512.c DSP_SRCS-$(HAVE_SSE2) += x86/sad4d_sse2.asm DSP_SRCS-$(HAVE_SSE2) += x86/sad_sse2.asm
diff --git a/vpx_dsp/vpx_dsp_common.h b/vpx_dsp/vpx_dsp_common.h index 528f33f..9d26682 100644 --- a/vpx_dsp/vpx_dsp_common.h +++ b/vpx_dsp/vpx_dsp_common.h
@@ -47,13 +47,12 @@ typedef int16_t tran_coef_t; -// Visual Studio 2022 (cl.exe) targeting AArch64 with optimizations enabled -// produces invalid code for clip_pixel() when the return type is uint8_t. -// See: +// Visual Studio 2022 (cl.exe) < 17.7 targeting AArch64 with optimizations +// enabled produces invalid code for clip_pixel() when the return type is +// uint8_t. See: // https://developercommunity.visualstudio.com/t/Misoptimization-for-ARM64-in-VS-2022-17/10363361 -// TODO(jzern): check the compiler version after a fix for the issue is -// released. -#if defined(_MSC_VER) && defined(_M_ARM64) && !defined(__clang__) +#if defined(_MSC_VER) && _MSC_VER < 1937 && defined(_M_ARM64) && \ + !defined(__clang__) static INLINE int clip_pixel(int val) { return (val > 255) ? 255 : (val < 0) ? 0 : val; }
diff --git a/vpx_dsp/vpx_dsp_rtcd_defs.pl b/vpx_dsp/vpx_dsp_rtcd_defs.pl index d845830..db508e9 100644 --- a/vpx_dsp/vpx_dsp_rtcd_defs.pl +++ b/vpx_dsp/vpx_dsp_rtcd_defs.pl
@@ -723,10 +723,10 @@ # if (vpx_config("CONFIG_VP9_ENCODER") eq "yes") { add_proto qw/void vpx_quantize_b/, "const tran_low_t *coeff_ptr, intptr_t n_coeffs, const struct macroblock_plane *const mb_plane, tran_low_t *qcoeff_ptr, tran_low_t *dqcoeff_ptr, const int16_t *dequant_ptr, uint16_t *eob_ptr, const struct ScanOrder *const scan_order"; - specialize qw/vpx_quantize_b neon sse2 ssse3 avx avx2 vsx lsx/; + specialize qw/vpx_quantize_b neon sse2 ssse3 avx avx2 vsx/; add_proto qw/void vpx_quantize_b_32x32/, "const tran_low_t *coeff_ptr, const struct macroblock_plane *const mb_plane, tran_low_t *qcoeff_ptr, tran_low_t *dqcoeff_ptr, const int16_t *dequant_ptr, uint16_t *eob_ptr, const struct ScanOrder *const scan_order"; - specialize qw/vpx_quantize_b_32x32 neon ssse3 avx avx2 vsx lsx/; + specialize qw/vpx_quantize_b_32x32 neon ssse3 avx avx2 vsx/; if (vpx_config("CONFIG_VP9_HIGHBITDEPTH") eq "yes") { add_proto qw/void vpx_highbd_quantize_b/, "const tran_low_t *coeff_ptr, intptr_t n_coeffs, const struct macroblock_plane *const mb_plane, tran_low_t *qcoeff_ptr, tran_low_t *dqcoeff_ptr, const int16_t *dequant_ptr, uint16_t *eob_ptr, const struct ScanOrder *const scan_order"; @@ -734,7 +734,11 @@ add_proto qw/void vpx_highbd_quantize_b_32x32/, "const tran_low_t *coeff_ptr, const struct macroblock_plane *const mb_plane, tran_low_t *qcoeff_ptr, tran_low_t *dqcoeff_ptr, const int16_t *dequant_ptr, uint16_t *eob_ptr, const struct ScanOrder *const scan_order"; specialize qw/vpx_highbd_quantize_b_32x32 neon sse2 avx2/; - } # CONFIG_VP9_HIGHBITDEPTH + } else { + specialize qw/vpx_quantize_b lsx/; + + specialize qw/vpx_quantize_b_32x32 lsx/; + } # CONFIG_VP9_HIGHBITDEPTH } # CONFIG_VP9_ENCODER if (vpx_config("CONFIG_ENCODERS") eq "yes") { @@ -751,10 +755,10 @@ # Single block SAD # add_proto qw/unsigned int vpx_sad64x64/, "const uint8_t *src_ptr, int src_stride, const uint8_t *ref_ptr, int ref_stride"; -specialize qw/vpx_sad64x64 neon neon_dotprod avx2 msa sse2 vsx mmi lsx/; +specialize qw/vpx_sad64x64 neon neon_dotprod avx512 avx2 msa sse2 vsx mmi lsx/; add_proto qw/unsigned int vpx_sad64x32/, "const uint8_t *src_ptr, int src_stride, const uint8_t *ref_ptr, int ref_stride"; -specialize qw/vpx_sad64x32 neon neon_dotprod avx2 msa sse2 vsx mmi/; +specialize qw/vpx_sad64x32 neon neon_dotprod avx512 avx2 msa sse2 vsx mmi/; add_proto qw/unsigned int vpx_sad32x64/, "const uint8_t *src_ptr, int src_stride, const uint8_t *ref_ptr, int ref_stride"; specialize qw/vpx_sad32x64 neon neon_dotprod avx2 msa sse2 vsx mmi/; @@ -790,10 +794,10 @@ specialize qw/vpx_sad4x4 neon msa sse2 mmi/; add_proto qw/unsigned int vpx_sad_skip_64x64/, "const uint8_t *src_ptr, int src_stride, const uint8_t *ref_ptr, int ref_stride"; -specialize qw/vpx_sad_skip_64x64 neon neon_dotprod avx2 sse2/; +specialize qw/vpx_sad_skip_64x64 neon neon_dotprod avx512 avx2 sse2/; add_proto qw/unsigned int vpx_sad_skip_64x32/, "const uint8_t *src_ptr, int src_stride, const uint8_t *ref_ptr, int ref_stride"; -specialize qw/vpx_sad_skip_64x32 neon neon_dotprod avx2 sse2/; +specialize qw/vpx_sad_skip_64x32 neon neon_dotprod avx512 avx2 sse2/; add_proto qw/unsigned int vpx_sad_skip_32x64/, "const uint8_t *src_ptr, int src_stride, const uint8_t *ref_ptr, int ref_stride"; specialize qw/vpx_sad_skip_32x64 neon neon_dotprod avx2 sse2/; @@ -889,10 +893,10 @@ } # CONFIG_VP9_ENCODER add_proto qw/unsigned int vpx_sad64x64_avg/, "const uint8_t *src_ptr, int src_stride, const uint8_t *ref_ptr, int ref_stride, const uint8_t *second_pred"; -specialize qw/vpx_sad64x64_avg neon neon_dotprod avx2 msa sse2 vsx mmi lsx/; +specialize qw/vpx_sad64x64_avg neon neon_dotprod avx512 avx2 msa sse2 vsx mmi lsx/; add_proto qw/unsigned int vpx_sad64x32_avg/, "const uint8_t *src_ptr, int src_stride, const uint8_t *ref_ptr, int ref_stride, const uint8_t *second_pred"; -specialize qw/vpx_sad64x32_avg neon neon_dotprod avx2 msa sse2 vsx mmi/; +specialize qw/vpx_sad64x32_avg neon neon_dotprod avx512 avx2 msa sse2 vsx mmi/; add_proto qw/unsigned int vpx_sad32x64_avg/, "const uint8_t *src_ptr, int src_stride, const uint8_t *ref_ptr, int ref_stride, const uint8_t *second_pred"; specialize qw/vpx_sad32x64_avg neon neon_dotprod avx2 msa sse2 vsx mmi/; @@ -970,10 +974,10 @@ specialize qw/vpx_sad4x4x4d neon msa sse2 mmi/; add_proto qw/void vpx_sad_skip_64x64x4d/, "const uint8_t *src_ptr, int src_stride, const uint8_t *const ref_array[4], int ref_stride, uint32_t sad_array[4]"; -specialize qw/vpx_sad_skip_64x64x4d neon neon_dotprod avx2 sse2/; +specialize qw/vpx_sad_skip_64x64x4d neon neon_dotprod avx512 avx2 sse2/; add_proto qw/void vpx_sad_skip_64x32x4d/, "const uint8_t *src_ptr, int src_stride, const uint8_t *const ref_array[4], int ref_stride, uint32_t sad_array[4]"; -specialize qw/vpx_sad_skip_64x32x4d neon neon_dotprod avx2 sse2/; +specialize qw/vpx_sad_skip_64x32x4d neon neon_dotprod avx512 avx2 sse2/; add_proto qw/void vpx_sad_skip_32x64x4d/, "const uint8_t *src_ptr, int src_stride, const uint8_t *const ref_array[4], int ref_stride, uint32_t sad_array[4]"; specialize qw/vpx_sad_skip_32x64x4d neon neon_dotprod avx2 sse2/;
diff --git a/vpx_dsp/x86/sad4d_avx512.c b/vpx_dsp/x86/sad4d_avx512.c index cfd23fe..cc36cae 100644 --- a/vpx_dsp/x86/sad4d_avx512.c +++ b/vpx_dsp/x86/sad4d_avx512.c
@@ -11,9 +11,10 @@ #include "./vpx_dsp_rtcd.h" #include "vpx/vpx_integer.h" -void vpx_sad64x64x4d_avx512(const uint8_t *src_ptr, int src_stride, - const uint8_t *const ref_array[4], int ref_stride, - uint32_t sad_array[4]) { +static INLINE void sad64xhx4d_avx512(const uint8_t *src_ptr, int src_stride, + const uint8_t *const ref_array[4], + int ref_stride, int h, + uint32_t sad_array[4]) { __m512i src_reg, ref0_reg, ref1_reg, ref2_reg, ref3_reg; __m512i sum_ref0, sum_ref1, sum_ref2, sum_ref3; __m512i sum_mlow, sum_mhigh; @@ -28,7 +29,7 @@ sum_ref1 = _mm512_set1_epi16(0); sum_ref2 = _mm512_set1_epi16(0); sum_ref3 = _mm512_set1_epi16(0); - for (i = 0; i < 64; i++) { + for (i = 0; i < h; i++) { // load src and all ref[] src_reg = _mm512_loadu_si512((const __m512i *)src_ptr); ref0_reg = _mm512_loadu_si512((const __m512i *)ref0); @@ -81,3 +82,24 @@ _mm_storeu_si128((__m128i *)(sad_array), sum128); } } + +void vpx_sad64x64x4d_avx512(const uint8_t *src, int src_stride, + const uint8_t *const ref_array[4], int ref_stride, + uint32_t sad_array[4]) { + sad64xhx4d_avx512(src, src_stride, ref_array, ref_stride, 64, sad_array); +} + +#define SADS64_H(h) \ + void vpx_sad_skip_64x##h##x4d_avx512( \ + const uint8_t *src, int src_stride, const uint8_t *const ref_array[4], \ + int ref_stride, uint32_t sad_array[4]) { \ + sad64xhx4d_avx512(src, 2 * src_stride, ref_array, 2 * ref_stride, \ + ((h) >> 1), sad_array); \ + sad_array[0] <<= 1; \ + sad_array[1] <<= 1; \ + sad_array[2] <<= 1; \ + sad_array[3] <<= 1; \ + } + +SADS64_H(64) +SADS64_H(32)
diff --git a/vpx_dsp/x86/sad_avx512.c b/vpx_dsp/x86/sad_avx512.c new file mode 100644 index 0000000..38bd3be --- /dev/null +++ b/vpx_dsp/x86/sad_avx512.c
@@ -0,0 +1,88 @@ +/* + * Copyright (c) 2025 The WebM project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ +#include <immintrin.h> +#include "./vpx_dsp_rtcd.h" +#include "vpx_ports/mem.h" + +static INLINE unsigned int sad64xh_avx512(const uint8_t *src_ptr, + int src_stride, + const uint8_t *ref_ptr, + int ref_stride, int h) { + int i, res; + __m512i sad_reg, ref_reg; + __m512i sum_sad = _mm512_setzero_si512(); + for (i = 0; i < h; i++) { + ref_reg = _mm512_loadu_si512((const __m512i *)ref_ptr); + sad_reg = + _mm512_sad_epu8(ref_reg, _mm512_loadu_si512((__m512 const *)src_ptr)); + sum_sad = _mm512_add_epi32(sum_sad, sad_reg); + ref_ptr += ref_stride; + src_ptr += src_stride; + } + res = _mm512_reduce_add_epi32(sum_sad); + return res; +} + +#define FSAD64_H(h) \ + unsigned int vpx_sad64x##h##_avx512(const uint8_t *src_ptr, int src_stride, \ + const uint8_t *ref_ptr, \ + int ref_stride) { \ + return sad64xh_avx512(src_ptr, src_stride, ref_ptr, ref_stride, h); \ + } + +#define FSADS64_H(h) \ + unsigned int vpx_sad_skip_64x##h##_avx512( \ + const uint8_t *src_ptr, int src_stride, const uint8_t *ref_ptr, \ + int ref_stride) { \ + return 2 * sad64xh_avx512(src_ptr, src_stride * 2, ref_ptr, \ + ref_stride * 2, h / 2); \ + } + +#define FSAD64 \ + FSAD64_H(64) \ + FSAD64_H(32) \ + FSADS64_H(64) \ + FSADS64_H(32) + +FSAD64 + +#undef FSAD64 +#undef FSAD64_H +#undef FSADS64_H + +#define FSADAVG64_H(h) \ + unsigned int vpx_sad64x##h##_avg_avx512( \ + const uint8_t *src_ptr, int src_stride, const uint8_t *ref_ptr, \ + int ref_stride, const uint8_t *second_pred) { \ + int i; \ + __m512i sad_reg, ref_reg; \ + __m512i sum_sad = _mm512_setzero_si512(); \ + for (i = 0; i < h; i++) { \ + ref_reg = _mm512_loadu_si512((const __m512i *)ref_ptr); \ + ref_reg = _mm512_avg_epu8( \ + ref_reg, _mm512_loadu_si512((const __m512i *)second_pred)); \ + sad_reg = _mm512_sad_epu8(ref_reg, \ + _mm512_loadu_si512((const __m512i *)src_ptr)); \ + sum_sad = _mm512_add_epi32(sum_sad, sad_reg); \ + ref_ptr += ref_stride; \ + src_ptr += src_stride; \ + second_pred += 64; \ + } \ + return (unsigned int)_mm512_reduce_add_epi32(sum_sad); \ + } + +#define FSADAVG64 \ + FSADAVG64_H(64) \ + FSADAVG64_H(32) + +FSADAVG64 + +#undef FSADAVG64 +#undef FSADAVG64_H
diff --git a/vpx_ports/vpx_timer.h b/vpx_ports/vpx_timer.h index 4934d52..55e7891 100644 --- a/vpx_ports/vpx_timer.h +++ b/vpx_ports/vpx_timer.h
@@ -31,17 +31,17 @@ /* * POSIX specific includes */ -#include <sys/time.h> +#include <time.h> /* timersub is not provided by msys at this time. */ -#ifndef timersub -#define timersub(a, b, result) \ +#ifndef timersub_ns +#define timersub_ns(a, b, result) \ do { \ (result)->tv_sec = (a)->tv_sec - (b)->tv_sec; \ - (result)->tv_usec = (a)->tv_usec - (b)->tv_usec; \ - if ((result)->tv_usec < 0) { \ + (result)->tv_nsec = (a)->tv_nsec - (b)->tv_nsec; \ + if ((result)->tv_nsec < 0) { \ --(result)->tv_sec; \ - (result)->tv_usec += 1000000; \ + (result)->tv_nsec += 1000000000; \ } \ } while (0) #endif @@ -51,23 +51,27 @@ #if defined(_WIN32) LARGE_INTEGER begin, end; #else - struct timeval begin, end; + struct timespec begin, end; #endif }; static INLINE void vpx_usec_timer_start(struct vpx_usec_timer *t) { #if defined(_WIN32) QueryPerformanceCounter(&t->begin); +#elif defined(CLOCK_MONOTONIC_RAW) + clock_gettime(CLOCK_MONOTONIC_RAW, &t->begin); #else - gettimeofday(&t->begin, NULL); + clock_gettime(CLOCK_MONOTONIC, &t->begin); #endif } static INLINE void vpx_usec_timer_mark(struct vpx_usec_timer *t) { #if defined(_WIN32) QueryPerformanceCounter(&t->end); +#elif defined(CLOCK_MONOTONIC_RAW) + clock_gettime(CLOCK_MONOTONIC_RAW, &t->end); #else - gettimeofday(&t->end, NULL); + clock_gettime(CLOCK_MONOTONIC, &t->end); #endif } @@ -80,18 +84,18 @@ QueryPerformanceFrequency(&freq); return diff.QuadPart * 1000000 / freq.QuadPart; #else - struct timeval diff; + struct timespec diff; - timersub(&t->end, &t->begin, &diff); - return (int64_t)diff.tv_sec * 1000000 + diff.tv_usec; + timersub_ns(&t->end, &t->begin, &diff); + return (int64_t)diff.tv_sec * 1000000 + diff.tv_nsec / 1000; #endif } #else /* CONFIG_OS_SUPPORT = 0*/ /* Empty timer functions if CONFIG_OS_SUPPORT = 0 */ -#ifndef timersub -#define timersub(a, b, result) +#ifndef timersub_ns +#define timersub_ns(a, b, result) #endif struct vpx_usec_timer {
diff --git a/vpx_ports/x86.h b/vpx_ports/x86.h index 3fa50b2..4e450f8 100644 --- a/vpx_ports/x86.h +++ b/vpx_ports/x86.h
@@ -164,6 +164,22 @@ #define BIT(n) (1u << (n)) #endif +#define MMX_BITS BIT(23) +#define SSE_BITS BIT(25) +#define SSE2_BITS BIT(26) +#define SSE3_BITS BIT(0) +#define SSSE3_BITS BIT(9) +#define SSE4_1_BITS BIT(19) +// Bits 27 (OSXSAVE) & 28 (256-bit AVX) +#define AVX_BITS (BIT(27) | BIT(28)) +#define AVX2_BITS BIT(5) +// Bits 16 (AVX-512F) & 17 (AVX-512DQ) & 28 (AVX-512CD) & 30 (AVX-512BW) +// & 31 (AVX-512VL) +#define AVX512_BITS (BIT(16) | BIT(17) | BIT(28) | BIT(30) | BIT(31)) + +#define FEATURE_SET(reg, feature) \ + (((reg) & (feature##_BITS)) == (feature##_BITS)) + static INLINE int x86_simd_caps(void) { unsigned int flags = 0; unsigned int mask = ~0u; @@ -173,58 +189,41 @@ /* See if the CPU capabilities are being overridden by the environment */ env = getenv("VPX_SIMD_CAPS"); - if (env && *env) return (int)strtol(env, NULL, 0); env = getenv("VPX_SIMD_CAPS_MASK"); - if (env && *env) mask = (unsigned int)strtoul(env, NULL, 0); /* Ensure that the CPUID instruction supports extended features */ cpuid(0, 0, max_cpuid_val, reg_ebx, reg_ecx, reg_edx); - if (max_cpuid_val < 1) return 0; /* Get the standard feature flags */ cpuid(1, 0, reg_eax, reg_ebx, reg_ecx, reg_edx); - if (reg_edx & BIT(23)) flags |= HAS_MMX; + flags |= FEATURE_SET(reg_edx, MMX) ? HAS_MMX : 0; + flags |= FEATURE_SET(reg_edx, SSE) ? HAS_SSE : 0; + flags |= FEATURE_SET(reg_edx, SSE2) ? HAS_SSE2 : 0; + flags |= FEATURE_SET(reg_ecx, SSE3) ? HAS_SSE3 : 0; + flags |= FEATURE_SET(reg_ecx, SSSE3) ? HAS_SSSE3 : 0; + flags |= FEATURE_SET(reg_ecx, SSE4_1) ? HAS_SSE4_1 : 0; - if (reg_edx & BIT(25)) flags |= HAS_SSE; /* aka xmm */ - - if (reg_edx & BIT(26)) flags |= HAS_SSE2; /* aka wmt */ - - if (reg_ecx & BIT(0)) flags |= HAS_SSE3; - - if (reg_ecx & BIT(9)) flags |= HAS_SSSE3; - - if (reg_ecx & BIT(19)) flags |= HAS_SSE4_1; - - // bits 27 (OSXSAVE) & 28 (256-bit AVX) - if ((reg_ecx & (BIT(27) | BIT(28))) == (BIT(27) | BIT(28))) { + if (FEATURE_SET(reg_ecx, AVX)) { // Check for OS-support of YMM state. Necessary for AVX and AVX2. if ((xgetbv() & 0x6) == 0x6) { flags |= HAS_AVX; - if (max_cpuid_val >= 7) { /* Get the leaf 7 feature flags. Needed to check for AVX2 support */ cpuid(7, 0, reg_eax, reg_ebx, reg_ecx, reg_edx); - - if (reg_ebx & BIT(5)) flags |= HAS_AVX2; - - // bits 16 (AVX-512F) & 17 (AVX-512DQ) & 28 (AVX-512CD) & - // 30 (AVX-512BW) & 32 (AVX-512VL) - if ((reg_ebx & (BIT(16) | BIT(17) | BIT(28) | BIT(30) | BIT(31))) == - (BIT(16) | BIT(17) | BIT(28) | BIT(30) | BIT(31))) { + flags |= FEATURE_SET(reg_ebx, AVX2) ? HAS_AVX2 : 0; + if (FEATURE_SET(reg_ebx, AVX512)) { // Check for OS-support of ZMM and YMM state. Necessary for AVX-512. if ((xgetbv() & 0xe6) == 0xe6) flags |= HAS_AVX512; } } } } - (void)reg_eax; // Avoid compiler warning on unused-but-set variable. - return flags & mask; }
diff --git a/vpx_scale/generic/yv12config.c b/vpx_scale/generic/yv12config.c index c52dab0..c04315a 100644 --- a/vpx_scale/generic/yv12config.c +++ b/vpx_scale/generic/yv12config.c
@@ -10,6 +10,7 @@ #include <assert.h> #include <limits.h> +#include <stdint.h> #include "vpx_scale/yv12config.h" #include "vpx_mem/vpx_mem.h" @@ -199,11 +200,13 @@ if (frame_size > VPX_MAX_ALLOCABLE_MEMORY / REF_FRAMES) return -1; #endif // VPX_MAX_ALLOCABLE_MEMORY +#if UINT64_MAX > SIZE_MAX // frame_size is stored in buffer_alloc_sz, which is a size_t. If it won't // fit, fail early. if (frame_size > SIZE_MAX) { return -1; } +#endif if (cb != NULL) { const int align_addr_extra_size = 31;
diff --git a/vpxenc.c b/vpxenc.c index 5e9fdde..cab7d34 100644 --- a/vpxenc.c +++ b/vpxenc.c
@@ -852,7 +852,7 @@ } if (prev) { - memcpy(stream, prev, sizeof(*stream)); + *stream = *prev; stream->index++; prev->next = stream; } else {