xref: /aosp_15_r20/external/libgav1/src/motion_vector.cc (revision 095378508e87ed692bf8dfeb34008b65b3735891)
1*09537850SAkhilesh Sanikop // Copyright 2019 The libgav1 Authors
2*09537850SAkhilesh Sanikop //
3*09537850SAkhilesh Sanikop // Licensed under the Apache License, Version 2.0 (the "License");
4*09537850SAkhilesh Sanikop // you may not use this file except in compliance with the License.
5*09537850SAkhilesh Sanikop // You may obtain a copy of the License at
6*09537850SAkhilesh Sanikop //
7*09537850SAkhilesh Sanikop //      http://www.apache.org/licenses/LICENSE-2.0
8*09537850SAkhilesh Sanikop //
9*09537850SAkhilesh Sanikop // Unless required by applicable law or agreed to in writing, software
10*09537850SAkhilesh Sanikop // distributed under the License is distributed on an "AS IS" BASIS,
11*09537850SAkhilesh Sanikop // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12*09537850SAkhilesh Sanikop // See the License for the specific language governing permissions and
13*09537850SAkhilesh Sanikop // limitations under the License.
14*09537850SAkhilesh Sanikop 
15*09537850SAkhilesh Sanikop #include "src/motion_vector.h"
16*09537850SAkhilesh Sanikop 
17*09537850SAkhilesh Sanikop #include <algorithm>
18*09537850SAkhilesh Sanikop #include <cassert>
19*09537850SAkhilesh Sanikop #include <cstdint>
20*09537850SAkhilesh Sanikop #include <cstdlib>
21*09537850SAkhilesh Sanikop #include <memory>
22*09537850SAkhilesh Sanikop 
23*09537850SAkhilesh Sanikop #include "src/dsp/dsp.h"
24*09537850SAkhilesh Sanikop #include "src/utils/bit_mask_set.h"
25*09537850SAkhilesh Sanikop #include "src/utils/common.h"
26*09537850SAkhilesh Sanikop #include "src/utils/constants.h"
27*09537850SAkhilesh Sanikop #include "src/utils/logging.h"
28*09537850SAkhilesh Sanikop 
29*09537850SAkhilesh Sanikop namespace libgav1 {
30*09537850SAkhilesh Sanikop namespace {
31*09537850SAkhilesh Sanikop 
32*09537850SAkhilesh Sanikop // Entry at index i is computed as:
33*09537850SAkhilesh Sanikop // Clip3(std::max(kBlockWidthPixels[i], kBlockHeightPixels[i], 16, 112)).
34*09537850SAkhilesh Sanikop constexpr int kWarpValidThreshold[kMaxBlockSizes] = {
35*09537850SAkhilesh Sanikop     16, 16, 16, 16, 16, 16, 32, 16, 16,  16,  32,
36*09537850SAkhilesh Sanikop     64, 32, 32, 32, 64, 64, 64, 64, 112, 112, 112};
37*09537850SAkhilesh Sanikop 
38*09537850SAkhilesh Sanikop // 7.10.2.10.
LowerMvPrecision(const ObuFrameHeader & frame_header,MotionVector * const mvs)39*09537850SAkhilesh Sanikop void LowerMvPrecision(const ObuFrameHeader& frame_header,
40*09537850SAkhilesh Sanikop                       MotionVector* const mvs) {
41*09537850SAkhilesh Sanikop   if (frame_header.allow_high_precision_mv) return;
42*09537850SAkhilesh Sanikop   if (frame_header.force_integer_mv != 0) {
43*09537850SAkhilesh Sanikop     for (auto& mv : mvs->mv) {
44*09537850SAkhilesh Sanikop       // The next line is equivalent to:
45*09537850SAkhilesh Sanikop       // const int value = (std::abs(static_cast<int>(mv)) + 3) & ~7;
46*09537850SAkhilesh Sanikop       // const int sign = mv >> 15;
47*09537850SAkhilesh Sanikop       // mv = ApplySign(value, sign);
48*09537850SAkhilesh Sanikop       mv = (mv + 3 - (mv >> 15)) & ~7;
49*09537850SAkhilesh Sanikop     }
50*09537850SAkhilesh Sanikop   } else {
51*09537850SAkhilesh Sanikop     for (auto& mv : mvs->mv) {
52*09537850SAkhilesh Sanikop       // The next line is equivalent to:
53*09537850SAkhilesh Sanikop       // if ((mv & 1) != 0) mv += (mv > 0) ? -1 : 1;
54*09537850SAkhilesh Sanikop       mv = (mv - (mv >> 15)) & ~1;
55*09537850SAkhilesh Sanikop     }
56*09537850SAkhilesh Sanikop   }
57*09537850SAkhilesh Sanikop }
58*09537850SAkhilesh Sanikop 
59*09537850SAkhilesh Sanikop // 7.10.2.1.
SetupGlobalMv(const Tile::Block & block,int index,MotionVector * const mv)60*09537850SAkhilesh Sanikop void SetupGlobalMv(const Tile::Block& block, int index,
61*09537850SAkhilesh Sanikop                    MotionVector* const mv) {
62*09537850SAkhilesh Sanikop   const BlockParameters& bp = *block.bp;
63*09537850SAkhilesh Sanikop   const ObuFrameHeader& frame_header = block.tile.frame_header();
64*09537850SAkhilesh Sanikop   ReferenceFrameType reference_type = bp.reference_frame[index];
65*09537850SAkhilesh Sanikop   const auto& gm = frame_header.global_motion[reference_type];
66*09537850SAkhilesh Sanikop   if (reference_type == kReferenceFrameIntra ||
67*09537850SAkhilesh Sanikop       gm.type == kGlobalMotionTransformationTypeIdentity) {
68*09537850SAkhilesh Sanikop     mv->mv32 = 0;
69*09537850SAkhilesh Sanikop     return;
70*09537850SAkhilesh Sanikop   }
71*09537850SAkhilesh Sanikop   if (gm.type == kGlobalMotionTransformationTypeTranslation) {
72*09537850SAkhilesh Sanikop     for (int i = 0; i < 2; ++i) {
73*09537850SAkhilesh Sanikop       mv->mv[i] = gm.params[i] >> (kWarpedModelPrecisionBits - 3);
74*09537850SAkhilesh Sanikop     }
75*09537850SAkhilesh Sanikop     LowerMvPrecision(frame_header, mv);
76*09537850SAkhilesh Sanikop     return;
77*09537850SAkhilesh Sanikop   }
78*09537850SAkhilesh Sanikop   const int x = MultiplyBy4(block.column4x4) + DivideBy2(block.width) - 1;
79*09537850SAkhilesh Sanikop   const int y = MultiplyBy4(block.row4x4) + DivideBy2(block.height) - 1;
80*09537850SAkhilesh Sanikop   const int xc = (gm.params[2] - (1 << kWarpedModelPrecisionBits)) * x +
81*09537850SAkhilesh Sanikop                  gm.params[3] * y + gm.params[0];
82*09537850SAkhilesh Sanikop   const int yc = gm.params[4] * x +
83*09537850SAkhilesh Sanikop                  (gm.params[5] - (1 << kWarpedModelPrecisionBits)) * y +
84*09537850SAkhilesh Sanikop                  gm.params[1];
85*09537850SAkhilesh Sanikop   if (frame_header.allow_high_precision_mv) {
86*09537850SAkhilesh Sanikop     mv->mv[0] = RightShiftWithRoundingSigned(yc, kWarpedModelPrecisionBits - 3);
87*09537850SAkhilesh Sanikop     mv->mv[1] = RightShiftWithRoundingSigned(xc, kWarpedModelPrecisionBits - 3);
88*09537850SAkhilesh Sanikop   } else {
89*09537850SAkhilesh Sanikop     mv->mv[0] = MultiplyBy2(
90*09537850SAkhilesh Sanikop         RightShiftWithRoundingSigned(yc, kWarpedModelPrecisionBits - 2));
91*09537850SAkhilesh Sanikop     mv->mv[1] = MultiplyBy2(
92*09537850SAkhilesh Sanikop         RightShiftWithRoundingSigned(xc, kWarpedModelPrecisionBits - 2));
93*09537850SAkhilesh Sanikop     LowerMvPrecision(frame_header, mv);
94*09537850SAkhilesh Sanikop   }
95*09537850SAkhilesh Sanikop }
96*09537850SAkhilesh Sanikop 
97*09537850SAkhilesh Sanikop constexpr BitMaskSet kPredictionModeNewMvMask(kPredictionModeNewMv,
98*09537850SAkhilesh Sanikop                                               kPredictionModeNewNewMv,
99*09537850SAkhilesh Sanikop                                               kPredictionModeNearNewMv,
100*09537850SAkhilesh Sanikop                                               kPredictionModeNewNearMv,
101*09537850SAkhilesh Sanikop                                               kPredictionModeNearestNewMv,
102*09537850SAkhilesh Sanikop                                               kPredictionModeNewNearestMv);
103*09537850SAkhilesh Sanikop 
104*09537850SAkhilesh Sanikop // 7.10.2.8.
SearchStack(const Tile::Block & block,const BlockParameters & mv_bp,int index,int weight,bool * const found_new_mv,bool * const found_match,int * const num_mv_found)105*09537850SAkhilesh Sanikop void SearchStack(const Tile::Block& block, const BlockParameters& mv_bp,
106*09537850SAkhilesh Sanikop                  int index, int weight, bool* const found_new_mv,
107*09537850SAkhilesh Sanikop                  bool* const found_match, int* const num_mv_found) {
108*09537850SAkhilesh Sanikop   const BlockParameters& bp = *block.bp;
109*09537850SAkhilesh Sanikop   const std::array<GlobalMotion, kNumReferenceFrameTypes>& global_motion =
110*09537850SAkhilesh Sanikop       block.tile.frame_header().global_motion;
111*09537850SAkhilesh Sanikop   PredictionParameters& prediction_parameters = *bp.prediction_parameters;
112*09537850SAkhilesh Sanikop   MotionVector candidate_mv;
113*09537850SAkhilesh Sanikop   // LowerMvPrecision() is not necessary, since the values in
114*09537850SAkhilesh Sanikop   // |prediction_parameters.global_mv| and |mv_bp.mv| were generated by it.
115*09537850SAkhilesh Sanikop   const auto global_motion_type = global_motion[bp.reference_frame[0]].type;
116*09537850SAkhilesh Sanikop   if (IsGlobalMvBlock(mv_bp, global_motion_type)) {
117*09537850SAkhilesh Sanikop     candidate_mv = prediction_parameters.global_mv[0];
118*09537850SAkhilesh Sanikop   } else {
119*09537850SAkhilesh Sanikop     candidate_mv = mv_bp.mv.mv[index];
120*09537850SAkhilesh Sanikop   }
121*09537850SAkhilesh Sanikop   *found_new_mv |= kPredictionModeNewMvMask.Contains(mv_bp.y_mode);
122*09537850SAkhilesh Sanikop   *found_match = true;
123*09537850SAkhilesh Sanikop   MotionVector* const ref_mv_stack = prediction_parameters.ref_mv_stack;
124*09537850SAkhilesh Sanikop   const int num_found = *num_mv_found;
125*09537850SAkhilesh Sanikop   const auto result = std::find_if(ref_mv_stack, ref_mv_stack + num_found,
126*09537850SAkhilesh Sanikop                                    [&candidate_mv](const MotionVector& ref_mv) {
127*09537850SAkhilesh Sanikop                                      return ref_mv.mv32 == candidate_mv.mv32;
128*09537850SAkhilesh Sanikop                                    });
129*09537850SAkhilesh Sanikop   if (result != ref_mv_stack + num_found) {
130*09537850SAkhilesh Sanikop     prediction_parameters.IncreaseWeight(std::distance(ref_mv_stack, result),
131*09537850SAkhilesh Sanikop                                          weight);
132*09537850SAkhilesh Sanikop     return;
133*09537850SAkhilesh Sanikop   }
134*09537850SAkhilesh Sanikop   if (num_found >= kMaxRefMvStackSize) return;
135*09537850SAkhilesh Sanikop   ref_mv_stack[num_found] = candidate_mv;
136*09537850SAkhilesh Sanikop   prediction_parameters.SetWeightIndexStackEntry(num_found, weight);
137*09537850SAkhilesh Sanikop   ++*num_mv_found;
138*09537850SAkhilesh Sanikop }
139*09537850SAkhilesh Sanikop 
140*09537850SAkhilesh Sanikop // 7.10.2.9.
CompoundSearchStack(const Tile::Block & block,const BlockParameters & mv_bp,int weight,bool * const found_new_mv,bool * const found_match,int * const num_mv_found)141*09537850SAkhilesh Sanikop void CompoundSearchStack(const Tile::Block& block, const BlockParameters& mv_bp,
142*09537850SAkhilesh Sanikop                          int weight, bool* const found_new_mv,
143*09537850SAkhilesh Sanikop                          bool* const found_match, int* const num_mv_found) {
144*09537850SAkhilesh Sanikop   const BlockParameters& bp = *block.bp;
145*09537850SAkhilesh Sanikop   const std::array<GlobalMotion, kNumReferenceFrameTypes>& global_motion =
146*09537850SAkhilesh Sanikop       block.tile.frame_header().global_motion;
147*09537850SAkhilesh Sanikop   PredictionParameters& prediction_parameters = *bp.prediction_parameters;
148*09537850SAkhilesh Sanikop   // LowerMvPrecision() is not necessary, since the values in
149*09537850SAkhilesh Sanikop   // |prediction_parameters.global_mv| and |mv_bp.mv| were generated by it.
150*09537850SAkhilesh Sanikop   CompoundMotionVector candidate_mv = mv_bp.mv;
151*09537850SAkhilesh Sanikop   for (int i = 0; i < 2; ++i) {
152*09537850SAkhilesh Sanikop     const auto global_motion_type = global_motion[bp.reference_frame[i]].type;
153*09537850SAkhilesh Sanikop     if (IsGlobalMvBlock(mv_bp, global_motion_type)) {
154*09537850SAkhilesh Sanikop       candidate_mv.mv[i] = prediction_parameters.global_mv[i];
155*09537850SAkhilesh Sanikop     }
156*09537850SAkhilesh Sanikop   }
157*09537850SAkhilesh Sanikop   *found_new_mv |= kPredictionModeNewMvMask.Contains(mv_bp.y_mode);
158*09537850SAkhilesh Sanikop   *found_match = true;
159*09537850SAkhilesh Sanikop   CompoundMotionVector* const compound_ref_mv_stack =
160*09537850SAkhilesh Sanikop       prediction_parameters.compound_ref_mv_stack;
161*09537850SAkhilesh Sanikop   const int num_found = *num_mv_found;
162*09537850SAkhilesh Sanikop   const auto result =
163*09537850SAkhilesh Sanikop       std::find_if(compound_ref_mv_stack, compound_ref_mv_stack + num_found,
164*09537850SAkhilesh Sanikop                    [&candidate_mv](const CompoundMotionVector& ref_mv) {
165*09537850SAkhilesh Sanikop                      return ref_mv.mv64 == candidate_mv.mv64;
166*09537850SAkhilesh Sanikop                    });
167*09537850SAkhilesh Sanikop   if (result != compound_ref_mv_stack + num_found) {
168*09537850SAkhilesh Sanikop     prediction_parameters.IncreaseWeight(
169*09537850SAkhilesh Sanikop         std::distance(compound_ref_mv_stack, result), weight);
170*09537850SAkhilesh Sanikop     return;
171*09537850SAkhilesh Sanikop   }
172*09537850SAkhilesh Sanikop   if (num_found >= kMaxRefMvStackSize) return;
173*09537850SAkhilesh Sanikop   compound_ref_mv_stack[num_found].mv64 = candidate_mv.mv64;
174*09537850SAkhilesh Sanikop   prediction_parameters.SetWeightIndexStackEntry(num_found, weight);
175*09537850SAkhilesh Sanikop   ++*num_mv_found;
176*09537850SAkhilesh Sanikop }
177*09537850SAkhilesh Sanikop 
178*09537850SAkhilesh Sanikop // 7.10.2.7.
AddReferenceMvCandidate(const Tile::Block & block,const BlockParameters & mv_bp,bool is_compound,int weight,bool * const found_new_mv,bool * const found_match,int * const num_mv_found)179*09537850SAkhilesh Sanikop void AddReferenceMvCandidate(const Tile::Block& block,
180*09537850SAkhilesh Sanikop                              const BlockParameters& mv_bp, bool is_compound,
181*09537850SAkhilesh Sanikop                              int weight, bool* const found_new_mv,
182*09537850SAkhilesh Sanikop                              bool* const found_match, int* const num_mv_found) {
183*09537850SAkhilesh Sanikop   if (!mv_bp.is_inter) return;
184*09537850SAkhilesh Sanikop   const BlockParameters& bp = *block.bp;
185*09537850SAkhilesh Sanikop   if (is_compound) {
186*09537850SAkhilesh Sanikop     if (mv_bp.reference_frame[0] == bp.reference_frame[0] &&
187*09537850SAkhilesh Sanikop         mv_bp.reference_frame[1] == bp.reference_frame[1]) {
188*09537850SAkhilesh Sanikop       CompoundSearchStack(block, mv_bp, weight, found_new_mv, found_match,
189*09537850SAkhilesh Sanikop                           num_mv_found);
190*09537850SAkhilesh Sanikop     }
191*09537850SAkhilesh Sanikop     return;
192*09537850SAkhilesh Sanikop   }
193*09537850SAkhilesh Sanikop   for (int i = 0; i < 2; ++i) {
194*09537850SAkhilesh Sanikop     if (mv_bp.reference_frame[i] == bp.reference_frame[0]) {
195*09537850SAkhilesh Sanikop       SearchStack(block, mv_bp, i, weight, found_new_mv, found_match,
196*09537850SAkhilesh Sanikop                   num_mv_found);
197*09537850SAkhilesh Sanikop     }
198*09537850SAkhilesh Sanikop   }
199*09537850SAkhilesh Sanikop }
200*09537850SAkhilesh Sanikop 
GetMinimumStep(int block_width_or_height4x4,int delta_row_or_column)201*09537850SAkhilesh Sanikop int GetMinimumStep(int block_width_or_height4x4, int delta_row_or_column) {
202*09537850SAkhilesh Sanikop   assert(delta_row_or_column < 0);
203*09537850SAkhilesh Sanikop   if (block_width_or_height4x4 >= 16) return 4;
204*09537850SAkhilesh Sanikop   if (delta_row_or_column < -1) return 2;
205*09537850SAkhilesh Sanikop   return 0;
206*09537850SAkhilesh Sanikop }
207*09537850SAkhilesh Sanikop 
208*09537850SAkhilesh Sanikop // 7.10.2.2.
ScanRow(const Tile::Block & block,int mv_column,int delta_row,bool is_compound,bool * const found_new_mv,bool * const found_match,int * const num_mv_found)209*09537850SAkhilesh Sanikop void ScanRow(const Tile::Block& block, int mv_column, int delta_row,
210*09537850SAkhilesh Sanikop              bool is_compound, bool* const found_new_mv,
211*09537850SAkhilesh Sanikop              bool* const found_match, int* const num_mv_found) {
212*09537850SAkhilesh Sanikop   const int mv_row = block.row4x4 + delta_row;
213*09537850SAkhilesh Sanikop   const Tile& tile = block.tile;
214*09537850SAkhilesh Sanikop   if (!tile.IsTopInside(mv_row + 1)) return;
215*09537850SAkhilesh Sanikop   const int width4x4 = block.width4x4;
216*09537850SAkhilesh Sanikop   const int min_step = GetMinimumStep(width4x4, delta_row);
217*09537850SAkhilesh Sanikop   BlockParameters** bps = tile.BlockParametersAddress(mv_row, mv_column);
218*09537850SAkhilesh Sanikop   BlockParameters** const end_bps =
219*09537850SAkhilesh Sanikop       bps + std::min({static_cast<int>(width4x4),
220*09537850SAkhilesh Sanikop                       tile.frame_header().columns4x4 - block.column4x4, 16});
221*09537850SAkhilesh Sanikop   do {
222*09537850SAkhilesh Sanikop     const BlockParameters& mv_bp = **bps;
223*09537850SAkhilesh Sanikop     const int step = std::max(
224*09537850SAkhilesh Sanikop         std::min(width4x4, static_cast<int>(kNum4x4BlocksWide[mv_bp.size])),
225*09537850SAkhilesh Sanikop         min_step);
226*09537850SAkhilesh Sanikop     AddReferenceMvCandidate(block, mv_bp, is_compound, MultiplyBy2(step),
227*09537850SAkhilesh Sanikop                             found_new_mv, found_match, num_mv_found);
228*09537850SAkhilesh Sanikop     bps += step;
229*09537850SAkhilesh Sanikop   } while (bps < end_bps);
230*09537850SAkhilesh Sanikop }
231*09537850SAkhilesh Sanikop 
232*09537850SAkhilesh Sanikop // 7.10.2.3.
ScanColumn(const Tile::Block & block,int mv_row,int delta_column,bool is_compound,bool * const found_new_mv,bool * const found_match,int * const num_mv_found)233*09537850SAkhilesh Sanikop void ScanColumn(const Tile::Block& block, int mv_row, int delta_column,
234*09537850SAkhilesh Sanikop                 bool is_compound, bool* const found_new_mv,
235*09537850SAkhilesh Sanikop                 bool* const found_match, int* const num_mv_found) {
236*09537850SAkhilesh Sanikop   const int mv_column = block.column4x4 + delta_column;
237*09537850SAkhilesh Sanikop   const Tile& tile = block.tile;
238*09537850SAkhilesh Sanikop   if (!tile.IsLeftInside(mv_column + 1)) return;
239*09537850SAkhilesh Sanikop   const int height4x4 = block.height4x4;
240*09537850SAkhilesh Sanikop   const int min_step = GetMinimumStep(height4x4, delta_column);
241*09537850SAkhilesh Sanikop   const ptrdiff_t stride = tile.BlockParametersStride();
242*09537850SAkhilesh Sanikop   BlockParameters** bps = tile.BlockParametersAddress(mv_row, mv_column);
243*09537850SAkhilesh Sanikop   BlockParameters** const end_bps =
244*09537850SAkhilesh Sanikop       bps + stride * std::min({static_cast<int>(height4x4),
245*09537850SAkhilesh Sanikop                                tile.frame_header().rows4x4 - block.row4x4, 16});
246*09537850SAkhilesh Sanikop   do {
247*09537850SAkhilesh Sanikop     const BlockParameters& mv_bp = **bps;
248*09537850SAkhilesh Sanikop     const int step = std::max(
249*09537850SAkhilesh Sanikop         std::min(height4x4, static_cast<int>(kNum4x4BlocksHigh[mv_bp.size])),
250*09537850SAkhilesh Sanikop         min_step);
251*09537850SAkhilesh Sanikop     AddReferenceMvCandidate(block, mv_bp, is_compound, MultiplyBy2(step),
252*09537850SAkhilesh Sanikop                             found_new_mv, found_match, num_mv_found);
253*09537850SAkhilesh Sanikop     bps += step * stride;
254*09537850SAkhilesh Sanikop   } while (bps < end_bps);
255*09537850SAkhilesh Sanikop }
256*09537850SAkhilesh Sanikop 
257*09537850SAkhilesh Sanikop // 7.10.2.4.
ScanPoint(const Tile::Block & block,int delta_row,int delta_column,bool is_compound,bool * const found_new_mv,bool * const found_match,int * const num_mv_found)258*09537850SAkhilesh Sanikop void ScanPoint(const Tile::Block& block, int delta_row, int delta_column,
259*09537850SAkhilesh Sanikop                bool is_compound, bool* const found_new_mv,
260*09537850SAkhilesh Sanikop                bool* const found_match, int* const num_mv_found) {
261*09537850SAkhilesh Sanikop   const int mv_row = block.row4x4 + delta_row;
262*09537850SAkhilesh Sanikop   const int mv_column = block.column4x4 + delta_column;
263*09537850SAkhilesh Sanikop   const Tile& tile = block.tile;
264*09537850SAkhilesh Sanikop   if (!tile.IsInside(mv_row, mv_column) ||
265*09537850SAkhilesh Sanikop       !tile.HasParameters(mv_row, mv_column)) {
266*09537850SAkhilesh Sanikop     return;
267*09537850SAkhilesh Sanikop   }
268*09537850SAkhilesh Sanikop   const BlockParameters& mv_bp = tile.Parameters(mv_row, mv_column);
269*09537850SAkhilesh Sanikop   if (mv_bp.reference_frame[0] == kReferenceFrameNone) return;
270*09537850SAkhilesh Sanikop   AddReferenceMvCandidate(block, mv_bp, is_compound, 4, found_new_mv,
271*09537850SAkhilesh Sanikop                           found_match, num_mv_found);
272*09537850SAkhilesh Sanikop }
273*09537850SAkhilesh Sanikop 
274*09537850SAkhilesh Sanikop // 7.10.2.6.
AddTemporalReferenceMvCandidate(const ObuFrameHeader & frame_header,const int reference_offsets[2],const MotionVector * const temporal_mvs,const int8_t * const temporal_reference_offsets,int count,bool is_compound,int * const zero_mv_context,int * const num_mv_found,PredictionParameters * const prediction_parameters)275*09537850SAkhilesh Sanikop void AddTemporalReferenceMvCandidate(
276*09537850SAkhilesh Sanikop     const ObuFrameHeader& frame_header, const int reference_offsets[2],
277*09537850SAkhilesh Sanikop     const MotionVector* const temporal_mvs,
278*09537850SAkhilesh Sanikop     const int8_t* const temporal_reference_offsets, int count, bool is_compound,
279*09537850SAkhilesh Sanikop     int* const zero_mv_context, int* const num_mv_found,
280*09537850SAkhilesh Sanikop     PredictionParameters* const prediction_parameters) {
281*09537850SAkhilesh Sanikop   const int mv_projection_function_index =
282*09537850SAkhilesh Sanikop       frame_header.allow_high_precision_mv ? 2 : frame_header.force_integer_mv;
283*09537850SAkhilesh Sanikop   const MotionVector* const global_mv = prediction_parameters->global_mv;
284*09537850SAkhilesh Sanikop   if (is_compound) {
285*09537850SAkhilesh Sanikop     alignas(kMaxAlignment)
286*09537850SAkhilesh Sanikop         CompoundMotionVector candidate_mvs[kMaxTemporalMvCandidatesWithPadding];
287*09537850SAkhilesh Sanikop     const dsp::Dsp& dsp = *dsp::GetDspTable(8);
288*09537850SAkhilesh Sanikop     dsp.mv_projection_compound[mv_projection_function_index](
289*09537850SAkhilesh Sanikop         temporal_mvs, temporal_reference_offsets, reference_offsets, count,
290*09537850SAkhilesh Sanikop         candidate_mvs);
291*09537850SAkhilesh Sanikop     if (*zero_mv_context == -1) {
292*09537850SAkhilesh Sanikop       int max_difference =
293*09537850SAkhilesh Sanikop           std::max(std::abs(candidate_mvs[0].mv[0].mv[0] - global_mv[0].mv[0]),
294*09537850SAkhilesh Sanikop                    std::abs(candidate_mvs[0].mv[0].mv[1] - global_mv[0].mv[1]));
295*09537850SAkhilesh Sanikop       max_difference =
296*09537850SAkhilesh Sanikop           std::max(max_difference,
297*09537850SAkhilesh Sanikop                    std::abs(candidate_mvs[0].mv[1].mv[0] - global_mv[1].mv[0]));
298*09537850SAkhilesh Sanikop       max_difference =
299*09537850SAkhilesh Sanikop           std::max(max_difference,
300*09537850SAkhilesh Sanikop                    std::abs(candidate_mvs[0].mv[1].mv[1] - global_mv[1].mv[1]));
301*09537850SAkhilesh Sanikop       *zero_mv_context = static_cast<int>(max_difference >= 16);
302*09537850SAkhilesh Sanikop     }
303*09537850SAkhilesh Sanikop     CompoundMotionVector* const compound_ref_mv_stack =
304*09537850SAkhilesh Sanikop         prediction_parameters->compound_ref_mv_stack;
305*09537850SAkhilesh Sanikop     int num_found = *num_mv_found;
306*09537850SAkhilesh Sanikop     int index = 0;
307*09537850SAkhilesh Sanikop     do {
308*09537850SAkhilesh Sanikop       const CompoundMotionVector& candidate_mv = candidate_mvs[index];
309*09537850SAkhilesh Sanikop       const auto result =
310*09537850SAkhilesh Sanikop           std::find_if(compound_ref_mv_stack, compound_ref_mv_stack + num_found,
311*09537850SAkhilesh Sanikop                        [&candidate_mv](const CompoundMotionVector& ref_mv) {
312*09537850SAkhilesh Sanikop                          return ref_mv.mv64 == candidate_mv.mv64;
313*09537850SAkhilesh Sanikop                        });
314*09537850SAkhilesh Sanikop       if (result != compound_ref_mv_stack + num_found) {
315*09537850SAkhilesh Sanikop         prediction_parameters->IncreaseWeight(
316*09537850SAkhilesh Sanikop             std::distance(compound_ref_mv_stack, result), 2);
317*09537850SAkhilesh Sanikop         continue;
318*09537850SAkhilesh Sanikop       }
319*09537850SAkhilesh Sanikop       if (num_found >= kMaxRefMvStackSize) continue;
320*09537850SAkhilesh Sanikop       compound_ref_mv_stack[num_found].mv64 = candidate_mv.mv64;
321*09537850SAkhilesh Sanikop       prediction_parameters->SetWeightIndexStackEntry(num_found, 2);
322*09537850SAkhilesh Sanikop       ++num_found;
323*09537850SAkhilesh Sanikop     } while (++index < count);
324*09537850SAkhilesh Sanikop     *num_mv_found = num_found;
325*09537850SAkhilesh Sanikop     return;
326*09537850SAkhilesh Sanikop   }
327*09537850SAkhilesh Sanikop   MotionVector* const ref_mv_stack = prediction_parameters->ref_mv_stack;
328*09537850SAkhilesh Sanikop   if (reference_offsets[0] == 0) {
329*09537850SAkhilesh Sanikop     if (*zero_mv_context == -1) {
330*09537850SAkhilesh Sanikop       const int max_difference =
331*09537850SAkhilesh Sanikop           std::max(std::abs(global_mv[0].mv[0]), std::abs(global_mv[0].mv[1]));
332*09537850SAkhilesh Sanikop       *zero_mv_context = static_cast<int>(max_difference >= 16);
333*09537850SAkhilesh Sanikop     }
334*09537850SAkhilesh Sanikop     const MotionVector candidate_mv = {};
335*09537850SAkhilesh Sanikop     const int num_found = *num_mv_found;
336*09537850SAkhilesh Sanikop     const auto result =
337*09537850SAkhilesh Sanikop         std::find_if(ref_mv_stack, ref_mv_stack + num_found,
338*09537850SAkhilesh Sanikop                      [&candidate_mv](const MotionVector& ref_mv) {
339*09537850SAkhilesh Sanikop                        return ref_mv.mv32 == candidate_mv.mv32;
340*09537850SAkhilesh Sanikop                      });
341*09537850SAkhilesh Sanikop     if (result != ref_mv_stack + num_found) {
342*09537850SAkhilesh Sanikop       prediction_parameters->IncreaseWeight(std::distance(ref_mv_stack, result),
343*09537850SAkhilesh Sanikop                                             2 * count);
344*09537850SAkhilesh Sanikop       return;
345*09537850SAkhilesh Sanikop     }
346*09537850SAkhilesh Sanikop     if (num_found >= kMaxRefMvStackSize) return;
347*09537850SAkhilesh Sanikop     ref_mv_stack[num_found] = candidate_mv;
348*09537850SAkhilesh Sanikop     prediction_parameters->SetWeightIndexStackEntry(num_found, 2 * count);
349*09537850SAkhilesh Sanikop     ++*num_mv_found;
350*09537850SAkhilesh Sanikop     return;
351*09537850SAkhilesh Sanikop   }
352*09537850SAkhilesh Sanikop   alignas(kMaxAlignment)
353*09537850SAkhilesh Sanikop       MotionVector candidate_mvs[kMaxTemporalMvCandidatesWithPadding];
354*09537850SAkhilesh Sanikop   const dsp::Dsp& dsp = *dsp::GetDspTable(8);
355*09537850SAkhilesh Sanikop   dsp.mv_projection_single[mv_projection_function_index](
356*09537850SAkhilesh Sanikop       temporal_mvs, temporal_reference_offsets, reference_offsets[0], count,
357*09537850SAkhilesh Sanikop       candidate_mvs);
358*09537850SAkhilesh Sanikop   if (*zero_mv_context == -1) {
359*09537850SAkhilesh Sanikop     const int max_difference =
360*09537850SAkhilesh Sanikop         std::max(std::abs(candidate_mvs[0].mv[0] - global_mv[0].mv[0]),
361*09537850SAkhilesh Sanikop                  std::abs(candidate_mvs[0].mv[1] - global_mv[0].mv[1]));
362*09537850SAkhilesh Sanikop     *zero_mv_context = static_cast<int>(max_difference >= 16);
363*09537850SAkhilesh Sanikop   }
364*09537850SAkhilesh Sanikop   int num_found = *num_mv_found;
365*09537850SAkhilesh Sanikop   int index = 0;
366*09537850SAkhilesh Sanikop   do {
367*09537850SAkhilesh Sanikop     const MotionVector& candidate_mv = candidate_mvs[index];
368*09537850SAkhilesh Sanikop     const auto result =
369*09537850SAkhilesh Sanikop         std::find_if(ref_mv_stack, ref_mv_stack + num_found,
370*09537850SAkhilesh Sanikop                      [&candidate_mv](const MotionVector& ref_mv) {
371*09537850SAkhilesh Sanikop                        return ref_mv.mv32 == candidate_mv.mv32;
372*09537850SAkhilesh Sanikop                      });
373*09537850SAkhilesh Sanikop     if (result != ref_mv_stack + num_found) {
374*09537850SAkhilesh Sanikop       prediction_parameters->IncreaseWeight(std::distance(ref_mv_stack, result),
375*09537850SAkhilesh Sanikop                                             2);
376*09537850SAkhilesh Sanikop       continue;
377*09537850SAkhilesh Sanikop     }
378*09537850SAkhilesh Sanikop     if (num_found >= kMaxRefMvStackSize) continue;
379*09537850SAkhilesh Sanikop     ref_mv_stack[num_found] = candidate_mv;
380*09537850SAkhilesh Sanikop     prediction_parameters->SetWeightIndexStackEntry(num_found, 2);
381*09537850SAkhilesh Sanikop     ++num_found;
382*09537850SAkhilesh Sanikop   } while (++index < count);
383*09537850SAkhilesh Sanikop   *num_mv_found = num_found;
384*09537850SAkhilesh Sanikop }
385*09537850SAkhilesh Sanikop 
386*09537850SAkhilesh Sanikop // Part of 7.10.2.5.
IsWithinTheSame64x64Block(const Tile::Block & block,int delta_row,int delta_column)387*09537850SAkhilesh Sanikop bool IsWithinTheSame64x64Block(const Tile::Block& block, int delta_row,
388*09537850SAkhilesh Sanikop                                int delta_column) {
389*09537850SAkhilesh Sanikop   const int row = (block.row4x4 & 15) + delta_row;
390*09537850SAkhilesh Sanikop   const int column = (block.column4x4 & 15) + delta_column;
391*09537850SAkhilesh Sanikop   // |block.height4x4| is at least 2 for all elements in |kTemporalScanMask|.
392*09537850SAkhilesh Sanikop   // So |row| are all non-negative.
393*09537850SAkhilesh Sanikop   assert(row >= 0);
394*09537850SAkhilesh Sanikop   return row < 16 && column >= 0 && column < 16;
395*09537850SAkhilesh Sanikop }
396*09537850SAkhilesh Sanikop 
397*09537850SAkhilesh Sanikop constexpr BitMaskSet kTemporalScanMask(kBlock8x8, kBlock8x16, kBlock8x32,
398*09537850SAkhilesh Sanikop                                        kBlock16x8, kBlock16x16, kBlock16x32,
399*09537850SAkhilesh Sanikop                                        kBlock32x8, kBlock32x16, kBlock32x32);
400*09537850SAkhilesh Sanikop 
401*09537850SAkhilesh Sanikop // 7.10.2.5.
TemporalScan(const Tile::Block & block,bool is_compound,int * const zero_mv_context,int * const num_mv_found)402*09537850SAkhilesh Sanikop void TemporalScan(const Tile::Block& block, bool is_compound,
403*09537850SAkhilesh Sanikop                   int* const zero_mv_context, int* const num_mv_found) {
404*09537850SAkhilesh Sanikop   const int step_w = (block.width4x4 >= 16) ? 4 : 2;
405*09537850SAkhilesh Sanikop   const int step_h = (block.height4x4 >= 16) ? 4 : 2;
406*09537850SAkhilesh Sanikop   const int row_start = block.row4x4 | 1;
407*09537850SAkhilesh Sanikop   const int column_start = block.column4x4 | 1;
408*09537850SAkhilesh Sanikop   const int row_end =
409*09537850SAkhilesh Sanikop       row_start + std::min(static_cast<int>(block.height4x4), 16);
410*09537850SAkhilesh Sanikop   const int column_end =
411*09537850SAkhilesh Sanikop       column_start + std::min(static_cast<int>(block.width4x4), 16);
412*09537850SAkhilesh Sanikop   const Tile& tile = block.tile;
413*09537850SAkhilesh Sanikop   const TemporalMotionField& motion_field = tile.motion_field();
414*09537850SAkhilesh Sanikop   const int stride = motion_field.mv.columns();
415*09537850SAkhilesh Sanikop   const MotionVector* motion_field_mv = motion_field.mv[0];
416*09537850SAkhilesh Sanikop   const int8_t* motion_field_reference_offset =
417*09537850SAkhilesh Sanikop       motion_field.reference_offset[0];
418*09537850SAkhilesh Sanikop   alignas(kMaxAlignment)
419*09537850SAkhilesh Sanikop       MotionVector temporal_mvs[kMaxTemporalMvCandidatesWithPadding];
420*09537850SAkhilesh Sanikop   int8_t temporal_reference_offsets[kMaxTemporalMvCandidatesWithPadding];
421*09537850SAkhilesh Sanikop   int count = 0;
422*09537850SAkhilesh Sanikop   int offset = stride * (row_start >> 1);
423*09537850SAkhilesh Sanikop   int mv_row = row_start;
424*09537850SAkhilesh Sanikop   do {
425*09537850SAkhilesh Sanikop     int mv_column = column_start;
426*09537850SAkhilesh Sanikop     do {
427*09537850SAkhilesh Sanikop       // Both horizontal and vertical offsets are positive. Only bottom and
428*09537850SAkhilesh Sanikop       // right boundaries need to be checked.
429*09537850SAkhilesh Sanikop       if (tile.IsBottomRightInside(mv_row, mv_column)) {
430*09537850SAkhilesh Sanikop         const int x8 = mv_column >> 1;
431*09537850SAkhilesh Sanikop         const MotionVector temporal_mv = motion_field_mv[offset + x8];
432*09537850SAkhilesh Sanikop         if (temporal_mv.mv[0] == kInvalidMvValue) {
433*09537850SAkhilesh Sanikop           if (mv_row == row_start && mv_column == column_start) {
434*09537850SAkhilesh Sanikop             *zero_mv_context = 1;
435*09537850SAkhilesh Sanikop           }
436*09537850SAkhilesh Sanikop         } else {
437*09537850SAkhilesh Sanikop           temporal_mvs[count] = temporal_mv;
438*09537850SAkhilesh Sanikop           temporal_reference_offsets[count++] =
439*09537850SAkhilesh Sanikop               motion_field_reference_offset[offset + x8];
440*09537850SAkhilesh Sanikop         }
441*09537850SAkhilesh Sanikop       }
442*09537850SAkhilesh Sanikop       mv_column += step_w;
443*09537850SAkhilesh Sanikop     } while (mv_column < column_end);
444*09537850SAkhilesh Sanikop     offset += stride * step_h >> 1;
445*09537850SAkhilesh Sanikop     mv_row += step_h;
446*09537850SAkhilesh Sanikop   } while (mv_row < row_end);
447*09537850SAkhilesh Sanikop   if (kTemporalScanMask.Contains(block.size)) {
448*09537850SAkhilesh Sanikop     const int temporal_sample_positions[3][2] = {
449*09537850SAkhilesh Sanikop         {block.height4x4, -2},
450*09537850SAkhilesh Sanikop         {block.height4x4, block.width4x4},
451*09537850SAkhilesh Sanikop         {block.height4x4 - 2, block.width4x4}};
452*09537850SAkhilesh Sanikop     // Getting the address of an element in Array2D is slow. Precalculate the
453*09537850SAkhilesh Sanikop     // offsets.
454*09537850SAkhilesh Sanikop     int temporal_sample_offsets[3];
455*09537850SAkhilesh Sanikop     temporal_sample_offsets[0] = stride * ((row_start + block.height4x4) >> 1) +
456*09537850SAkhilesh Sanikop                                  ((column_start - 2) >> 1);
457*09537850SAkhilesh Sanikop     temporal_sample_offsets[1] =
458*09537850SAkhilesh Sanikop         temporal_sample_offsets[0] + ((block.width4x4 + 2) >> 1);
459*09537850SAkhilesh Sanikop     temporal_sample_offsets[2] = temporal_sample_offsets[1] - stride;
460*09537850SAkhilesh Sanikop     for (int i = 0; i < 3; i++) {
461*09537850SAkhilesh Sanikop       const int row = temporal_sample_positions[i][0];
462*09537850SAkhilesh Sanikop       const int column = temporal_sample_positions[i][1];
463*09537850SAkhilesh Sanikop       if (!IsWithinTheSame64x64Block(block, row, column)) continue;
464*09537850SAkhilesh Sanikop       const int mv_row = row_start + row;
465*09537850SAkhilesh Sanikop       const int mv_column = column_start + column;
466*09537850SAkhilesh Sanikop       // IsWithinTheSame64x64Block() guarantees the reference block is inside
467*09537850SAkhilesh Sanikop       // the top and left boundary.
468*09537850SAkhilesh Sanikop       if (!tile.IsBottomRightInside(mv_row, mv_column)) continue;
469*09537850SAkhilesh Sanikop       const MotionVector temporal_mv =
470*09537850SAkhilesh Sanikop           motion_field_mv[temporal_sample_offsets[i]];
471*09537850SAkhilesh Sanikop       if (temporal_mv.mv[0] != kInvalidMvValue) {
472*09537850SAkhilesh Sanikop         temporal_mvs[count] = temporal_mv;
473*09537850SAkhilesh Sanikop         temporal_reference_offsets[count++] =
474*09537850SAkhilesh Sanikop             motion_field_reference_offset[temporal_sample_offsets[i]];
475*09537850SAkhilesh Sanikop       }
476*09537850SAkhilesh Sanikop     }
477*09537850SAkhilesh Sanikop   }
478*09537850SAkhilesh Sanikop   if (count != 0) {
479*09537850SAkhilesh Sanikop     BlockParameters* const bp = block.bp;
480*09537850SAkhilesh Sanikop     int reference_offsets[2];
481*09537850SAkhilesh Sanikop     const int offset_0 = tile.current_frame()
482*09537850SAkhilesh Sanikop                              .reference_info()
483*09537850SAkhilesh Sanikop                              ->relative_distance_to[bp->reference_frame[0]];
484*09537850SAkhilesh Sanikop     reference_offsets[0] =
485*09537850SAkhilesh Sanikop         Clip3(offset_0, -kMaxFrameDistance, kMaxFrameDistance);
486*09537850SAkhilesh Sanikop     if (is_compound) {
487*09537850SAkhilesh Sanikop       const int offset_1 = tile.current_frame()
488*09537850SAkhilesh Sanikop                                .reference_info()
489*09537850SAkhilesh Sanikop                                ->relative_distance_to[bp->reference_frame[1]];
490*09537850SAkhilesh Sanikop       reference_offsets[1] =
491*09537850SAkhilesh Sanikop           Clip3(offset_1, -kMaxFrameDistance, kMaxFrameDistance);
492*09537850SAkhilesh Sanikop       // Pad so that SIMD implementations won't read uninitialized memory.
493*09537850SAkhilesh Sanikop       if ((count & 1) != 0) {
494*09537850SAkhilesh Sanikop         temporal_mvs[count].mv32 = 0;
495*09537850SAkhilesh Sanikop         temporal_reference_offsets[count] = 0;
496*09537850SAkhilesh Sanikop       }
497*09537850SAkhilesh Sanikop     } else {
498*09537850SAkhilesh Sanikop       // Pad so that SIMD implementations won't read uninitialized memory.
499*09537850SAkhilesh Sanikop       for (int i = count; i < ((count + 3) & ~3); ++i) {
500*09537850SAkhilesh Sanikop         temporal_mvs[i].mv32 = 0;
501*09537850SAkhilesh Sanikop         temporal_reference_offsets[i] = 0;
502*09537850SAkhilesh Sanikop       }
503*09537850SAkhilesh Sanikop     }
504*09537850SAkhilesh Sanikop     AddTemporalReferenceMvCandidate(
505*09537850SAkhilesh Sanikop         tile.frame_header(), reference_offsets, temporal_mvs,
506*09537850SAkhilesh Sanikop         temporal_reference_offsets, count, is_compound, zero_mv_context,
507*09537850SAkhilesh Sanikop         num_mv_found, &(*bp->prediction_parameters));
508*09537850SAkhilesh Sanikop   }
509*09537850SAkhilesh Sanikop }
510*09537850SAkhilesh Sanikop 
511*09537850SAkhilesh Sanikop // Part of 7.10.2.13.
AddExtraCompoundMvCandidate(const Tile::Block & block,int mv_row,int mv_column,int * const ref_id_count,MotionVector ref_id[2][2],int * const ref_diff_count,MotionVector ref_diff[2][2])512*09537850SAkhilesh Sanikop void AddExtraCompoundMvCandidate(const Tile::Block& block, int mv_row,
513*09537850SAkhilesh Sanikop                                  int mv_column, int* const ref_id_count,
514*09537850SAkhilesh Sanikop                                  MotionVector ref_id[2][2],
515*09537850SAkhilesh Sanikop                                  int* const ref_diff_count,
516*09537850SAkhilesh Sanikop                                  MotionVector ref_diff[2][2]) {
517*09537850SAkhilesh Sanikop   const auto& bp = block.tile.Parameters(mv_row, mv_column);
518*09537850SAkhilesh Sanikop   const std::array<bool, kNumReferenceFrameTypes>& reference_frame_sign_bias =
519*09537850SAkhilesh Sanikop       block.tile.reference_frame_sign_bias();
520*09537850SAkhilesh Sanikop   for (int i = 0; i < 2; ++i) {
521*09537850SAkhilesh Sanikop     const ReferenceFrameType candidate_reference_frame = bp.reference_frame[i];
522*09537850SAkhilesh Sanikop     if (candidate_reference_frame <= kReferenceFrameIntra) continue;
523*09537850SAkhilesh Sanikop     for (int j = 0; j < 2; ++j) {
524*09537850SAkhilesh Sanikop       MotionVector candidate_mv = bp.mv.mv[i];
525*09537850SAkhilesh Sanikop       const ReferenceFrameType block_reference_frame =
526*09537850SAkhilesh Sanikop           block.bp->reference_frame[j];
527*09537850SAkhilesh Sanikop       if (candidate_reference_frame == block_reference_frame &&
528*09537850SAkhilesh Sanikop           ref_id_count[j] < 2) {
529*09537850SAkhilesh Sanikop         ref_id[j][ref_id_count[j]] = candidate_mv;
530*09537850SAkhilesh Sanikop         ++ref_id_count[j];
531*09537850SAkhilesh Sanikop       } else if (ref_diff_count[j] < 2) {
532*09537850SAkhilesh Sanikop         if (reference_frame_sign_bias[candidate_reference_frame] !=
533*09537850SAkhilesh Sanikop             reference_frame_sign_bias[block_reference_frame]) {
534*09537850SAkhilesh Sanikop           candidate_mv.mv[0] *= -1;
535*09537850SAkhilesh Sanikop           candidate_mv.mv[1] *= -1;
536*09537850SAkhilesh Sanikop         }
537*09537850SAkhilesh Sanikop         ref_diff[j][ref_diff_count[j]] = candidate_mv;
538*09537850SAkhilesh Sanikop         ++ref_diff_count[j];
539*09537850SAkhilesh Sanikop       }
540*09537850SAkhilesh Sanikop     }
541*09537850SAkhilesh Sanikop   }
542*09537850SAkhilesh Sanikop }
543*09537850SAkhilesh Sanikop 
544*09537850SAkhilesh Sanikop // Part of 7.10.2.13.
AddExtraSingleMvCandidate(const Tile::Block & block,int mv_row,int mv_column,int * const num_mv_found)545*09537850SAkhilesh Sanikop void AddExtraSingleMvCandidate(const Tile::Block& block, int mv_row,
546*09537850SAkhilesh Sanikop                                int mv_column, int* const num_mv_found) {
547*09537850SAkhilesh Sanikop   const auto& bp = block.tile.Parameters(mv_row, mv_column);
548*09537850SAkhilesh Sanikop   const std::array<bool, kNumReferenceFrameTypes>& reference_frame_sign_bias =
549*09537850SAkhilesh Sanikop       block.tile.reference_frame_sign_bias();
550*09537850SAkhilesh Sanikop   const ReferenceFrameType block_reference_frame = block.bp->reference_frame[0];
551*09537850SAkhilesh Sanikop   PredictionParameters& prediction_parameters =
552*09537850SAkhilesh Sanikop       *block.bp->prediction_parameters;
553*09537850SAkhilesh Sanikop   MotionVector* const ref_mv_stack = prediction_parameters.ref_mv_stack;
554*09537850SAkhilesh Sanikop   int num_found = *num_mv_found;
555*09537850SAkhilesh Sanikop   for (int i = 0; i < 2; ++i) {
556*09537850SAkhilesh Sanikop     const ReferenceFrameType candidate_reference_frame = bp.reference_frame[i];
557*09537850SAkhilesh Sanikop     if (candidate_reference_frame <= kReferenceFrameIntra) continue;
558*09537850SAkhilesh Sanikop     MotionVector candidate_mv = bp.mv.mv[i];
559*09537850SAkhilesh Sanikop     if (reference_frame_sign_bias[candidate_reference_frame] !=
560*09537850SAkhilesh Sanikop         reference_frame_sign_bias[block_reference_frame]) {
561*09537850SAkhilesh Sanikop       candidate_mv.mv[0] *= -1;
562*09537850SAkhilesh Sanikop       candidate_mv.mv[1] *= -1;
563*09537850SAkhilesh Sanikop     }
564*09537850SAkhilesh Sanikop     assert(num_found <= 2);
565*09537850SAkhilesh Sanikop     if ((num_found != 0 && ref_mv_stack[0].mv32 == candidate_mv.mv32) ||
566*09537850SAkhilesh Sanikop         (num_found == 2 && ref_mv_stack[1].mv32 == candidate_mv.mv32)) {
567*09537850SAkhilesh Sanikop       continue;
568*09537850SAkhilesh Sanikop     }
569*09537850SAkhilesh Sanikop     ref_mv_stack[num_found] = candidate_mv;
570*09537850SAkhilesh Sanikop     prediction_parameters.SetWeightIndexStackEntry(num_found, 0);
571*09537850SAkhilesh Sanikop     ++num_found;
572*09537850SAkhilesh Sanikop   }
573*09537850SAkhilesh Sanikop   *num_mv_found = num_found;
574*09537850SAkhilesh Sanikop }
575*09537850SAkhilesh Sanikop 
576*09537850SAkhilesh Sanikop // 7.10.2.12.
ExtraSearch(const Tile::Block & block,bool is_compound,int * const num_mv_found)577*09537850SAkhilesh Sanikop void ExtraSearch(const Tile::Block& block, bool is_compound,
578*09537850SAkhilesh Sanikop                  int* const num_mv_found) {
579*09537850SAkhilesh Sanikop   const Tile& tile = block.tile;
580*09537850SAkhilesh Sanikop   const int num4x4 = std::min({static_cast<int>(block.width4x4),
581*09537850SAkhilesh Sanikop                                tile.frame_header().columns4x4 - block.column4x4,
582*09537850SAkhilesh Sanikop                                static_cast<int>(block.height4x4),
583*09537850SAkhilesh Sanikop                                tile.frame_header().rows4x4 - block.row4x4, 16});
584*09537850SAkhilesh Sanikop   int ref_id_count[2] = {};
585*09537850SAkhilesh Sanikop   MotionVector ref_id[2][2] = {};
586*09537850SAkhilesh Sanikop   int ref_diff_count[2] = {};
587*09537850SAkhilesh Sanikop   MotionVector ref_diff[2][2] = {};
588*09537850SAkhilesh Sanikop   PredictionParameters& prediction_parameters =
589*09537850SAkhilesh Sanikop       *block.bp->prediction_parameters;
590*09537850SAkhilesh Sanikop   for (int pass = 0; pass < 2 && *num_mv_found < 2; ++pass) {
591*09537850SAkhilesh Sanikop     for (int i = 0; i < num4x4;) {
592*09537850SAkhilesh Sanikop       const int mv_row = block.row4x4 + ((pass == 0) ? -1 : i);
593*09537850SAkhilesh Sanikop       const int mv_column = block.column4x4 + ((pass == 0) ? i : -1);
594*09537850SAkhilesh Sanikop       if (!tile.IsTopLeftInside(mv_row + 1, mv_column + 1)) break;
595*09537850SAkhilesh Sanikop       if (is_compound) {
596*09537850SAkhilesh Sanikop         AddExtraCompoundMvCandidate(block, mv_row, mv_column, ref_id_count,
597*09537850SAkhilesh Sanikop                                     ref_id, ref_diff_count, ref_diff);
598*09537850SAkhilesh Sanikop       } else {
599*09537850SAkhilesh Sanikop         AddExtraSingleMvCandidate(block, mv_row, mv_column, num_mv_found);
600*09537850SAkhilesh Sanikop         if (*num_mv_found >= 2) break;
601*09537850SAkhilesh Sanikop       }
602*09537850SAkhilesh Sanikop       const auto& bp = tile.Parameters(mv_row, mv_column);
603*09537850SAkhilesh Sanikop       i +=
604*09537850SAkhilesh Sanikop           (pass == 0) ? kNum4x4BlocksWide[bp.size] : kNum4x4BlocksHigh[bp.size];
605*09537850SAkhilesh Sanikop     }
606*09537850SAkhilesh Sanikop   }
607*09537850SAkhilesh Sanikop   if (is_compound) {
608*09537850SAkhilesh Sanikop     // Merge compound mode extra search into mv stack.
609*09537850SAkhilesh Sanikop     CompoundMotionVector* const compound_ref_mv_stack =
610*09537850SAkhilesh Sanikop         prediction_parameters.compound_ref_mv_stack;
611*09537850SAkhilesh Sanikop     CompoundMotionVector combined_mvs[2] = {};
612*09537850SAkhilesh Sanikop     for (int i = 0; i < 2; ++i) {
613*09537850SAkhilesh Sanikop       int count = 0;
614*09537850SAkhilesh Sanikop       assert(ref_id_count[i] <= 2);
615*09537850SAkhilesh Sanikop       for (int j = 0; j < ref_id_count[i]; ++j, ++count) {
616*09537850SAkhilesh Sanikop         combined_mvs[count].mv[i] = ref_id[i][j];
617*09537850SAkhilesh Sanikop       }
618*09537850SAkhilesh Sanikop       for (int j = 0; j < ref_diff_count[i] && count < 2; ++j, ++count) {
619*09537850SAkhilesh Sanikop         combined_mvs[count].mv[i] = ref_diff[i][j];
620*09537850SAkhilesh Sanikop       }
621*09537850SAkhilesh Sanikop       for (; count < 2; ++count) {
622*09537850SAkhilesh Sanikop         combined_mvs[count].mv[i] = prediction_parameters.global_mv[i];
623*09537850SAkhilesh Sanikop       }
624*09537850SAkhilesh Sanikop     }
625*09537850SAkhilesh Sanikop     if (*num_mv_found == 1) {
626*09537850SAkhilesh Sanikop       if (combined_mvs[0].mv64 == compound_ref_mv_stack[0].mv64) {
627*09537850SAkhilesh Sanikop         compound_ref_mv_stack[1].mv64 = combined_mvs[1].mv64;
628*09537850SAkhilesh Sanikop       } else {
629*09537850SAkhilesh Sanikop         compound_ref_mv_stack[1].mv64 = combined_mvs[0].mv64;
630*09537850SAkhilesh Sanikop       }
631*09537850SAkhilesh Sanikop       prediction_parameters.SetWeightIndexStackEntry(1, 0);
632*09537850SAkhilesh Sanikop     } else {
633*09537850SAkhilesh Sanikop       assert(*num_mv_found == 0);
634*09537850SAkhilesh Sanikop       for (int i = 0; i < 2; ++i) {
635*09537850SAkhilesh Sanikop         compound_ref_mv_stack[i].mv64 = combined_mvs[i].mv64;
636*09537850SAkhilesh Sanikop         prediction_parameters.SetWeightIndexStackEntry(i, 0);
637*09537850SAkhilesh Sanikop       }
638*09537850SAkhilesh Sanikop     }
639*09537850SAkhilesh Sanikop     *num_mv_found = 2;
640*09537850SAkhilesh Sanikop   } else {
641*09537850SAkhilesh Sanikop     // single prediction mode
642*09537850SAkhilesh Sanikop     MotionVector* const ref_mv_stack = prediction_parameters.ref_mv_stack;
643*09537850SAkhilesh Sanikop     for (int i = *num_mv_found; i < 2; ++i) {
644*09537850SAkhilesh Sanikop       ref_mv_stack[i] = prediction_parameters.global_mv[0];
645*09537850SAkhilesh Sanikop       prediction_parameters.SetWeightIndexStackEntry(i, 0);
646*09537850SAkhilesh Sanikop     }
647*09537850SAkhilesh Sanikop   }
648*09537850SAkhilesh Sanikop }
649*09537850SAkhilesh Sanikop 
DescendingOrderTwo(int * const a,int * const b)650*09537850SAkhilesh Sanikop void DescendingOrderTwo(int* const a, int* const b) {
651*09537850SAkhilesh Sanikop   if (*a < *b) {
652*09537850SAkhilesh Sanikop     std::swap(*a, *b);
653*09537850SAkhilesh Sanikop   }
654*09537850SAkhilesh Sanikop }
655*09537850SAkhilesh Sanikop 
656*09537850SAkhilesh Sanikop // Comparator used for sorting candidate motion vectors in descending order of
657*09537850SAkhilesh Sanikop // their weights (as specified in 7.10.2.11).
CompareCandidateMotionVectors(const int16_t & lhs,const int16_t & rhs)658*09537850SAkhilesh Sanikop bool CompareCandidateMotionVectors(const int16_t& lhs, const int16_t& rhs) {
659*09537850SAkhilesh Sanikop   return lhs > rhs;
660*09537850SAkhilesh Sanikop }
661*09537850SAkhilesh Sanikop 
SortWeightIndexStack(const int size,const int sort_to_n,int16_t * const weight_index_stack)662*09537850SAkhilesh Sanikop void SortWeightIndexStack(const int size, const int sort_to_n,
663*09537850SAkhilesh Sanikop                           int16_t* const weight_index_stack) {
664*09537850SAkhilesh Sanikop   if (size <= 1) return;
665*09537850SAkhilesh Sanikop   if (size <= 3) {
666*09537850SAkhilesh Sanikop     // Specialize small sort sizes to speed up.
667*09537850SAkhilesh Sanikop     int weight_index_0 = weight_index_stack[0];
668*09537850SAkhilesh Sanikop     int weight_index_1 = weight_index_stack[1];
669*09537850SAkhilesh Sanikop     DescendingOrderTwo(&weight_index_0, &weight_index_1);
670*09537850SAkhilesh Sanikop     if (size == 3) {
671*09537850SAkhilesh Sanikop       int weight_index_2 = weight_index_stack[2];
672*09537850SAkhilesh Sanikop       DescendingOrderTwo(&weight_index_1, &weight_index_2);
673*09537850SAkhilesh Sanikop       DescendingOrderTwo(&weight_index_0, &weight_index_1);
674*09537850SAkhilesh Sanikop       weight_index_stack[2] = weight_index_2;
675*09537850SAkhilesh Sanikop     }
676*09537850SAkhilesh Sanikop     weight_index_stack[0] = weight_index_0;
677*09537850SAkhilesh Sanikop     weight_index_stack[1] = weight_index_1;
678*09537850SAkhilesh Sanikop     return;
679*09537850SAkhilesh Sanikop   }
680*09537850SAkhilesh Sanikop   if (sort_to_n == 1) {
681*09537850SAkhilesh Sanikop     // std::max_element() is not efficient. Find the max element in a loop.
682*09537850SAkhilesh Sanikop     int16_t max_element = weight_index_stack[0];
683*09537850SAkhilesh Sanikop     int i = 1;
684*09537850SAkhilesh Sanikop     do {
685*09537850SAkhilesh Sanikop       max_element = std::max(max_element, weight_index_stack[i]);
686*09537850SAkhilesh Sanikop     } while (++i < size);
687*09537850SAkhilesh Sanikop     weight_index_stack[0] = max_element;
688*09537850SAkhilesh Sanikop     return;
689*09537850SAkhilesh Sanikop   }
690*09537850SAkhilesh Sanikop   std::partial_sort(&weight_index_stack[0], &weight_index_stack[sort_to_n],
691*09537850SAkhilesh Sanikop                     &weight_index_stack[size], CompareCandidateMotionVectors);
692*09537850SAkhilesh Sanikop }
693*09537850SAkhilesh Sanikop 
694*09537850SAkhilesh Sanikop // 7.10.2.14 (part 2).
ComputeContexts(bool found_new_mv,int nearest_matches,int total_matches,int * new_mv_context,int * reference_mv_context)695*09537850SAkhilesh Sanikop void ComputeContexts(bool found_new_mv, int nearest_matches, int total_matches,
696*09537850SAkhilesh Sanikop                      int* new_mv_context, int* reference_mv_context) {
697*09537850SAkhilesh Sanikop   switch (nearest_matches) {
698*09537850SAkhilesh Sanikop     case 0:
699*09537850SAkhilesh Sanikop       *new_mv_context = std::min(total_matches, 1);
700*09537850SAkhilesh Sanikop       *reference_mv_context = total_matches;
701*09537850SAkhilesh Sanikop       break;
702*09537850SAkhilesh Sanikop     case 1:
703*09537850SAkhilesh Sanikop       *new_mv_context = 3 - static_cast<int>(found_new_mv);
704*09537850SAkhilesh Sanikop       *reference_mv_context = 2 + total_matches;
705*09537850SAkhilesh Sanikop       break;
706*09537850SAkhilesh Sanikop     default:
707*09537850SAkhilesh Sanikop       *new_mv_context = 5 - static_cast<int>(found_new_mv);
708*09537850SAkhilesh Sanikop       *reference_mv_context = 5;
709*09537850SAkhilesh Sanikop       break;
710*09537850SAkhilesh Sanikop   }
711*09537850SAkhilesh Sanikop }
712*09537850SAkhilesh Sanikop 
713*09537850SAkhilesh Sanikop // 7.10.4.2.
AddSample(const Tile::Block & block,int delta_row,int delta_column,int * const num_warp_samples,int * const num_samples_scanned,int candidates[kMaxLeastSquaresSamples][4])714*09537850SAkhilesh Sanikop void AddSample(const Tile::Block& block, int delta_row, int delta_column,
715*09537850SAkhilesh Sanikop                int* const num_warp_samples, int* const num_samples_scanned,
716*09537850SAkhilesh Sanikop                int candidates[kMaxLeastSquaresSamples][4]) {
717*09537850SAkhilesh Sanikop   if (*num_samples_scanned >= kMaxLeastSquaresSamples) return;
718*09537850SAkhilesh Sanikop   const int mv_row = block.row4x4 + delta_row;
719*09537850SAkhilesh Sanikop   const int mv_column = block.column4x4 + delta_column;
720*09537850SAkhilesh Sanikop   const Tile& tile = block.tile;
721*09537850SAkhilesh Sanikop   if (!tile.IsInside(mv_row, mv_column) ||
722*09537850SAkhilesh Sanikop       !tile.HasParameters(mv_row, mv_column)) {
723*09537850SAkhilesh Sanikop     return;
724*09537850SAkhilesh Sanikop   }
725*09537850SAkhilesh Sanikop   const BlockParameters& bp = *block.bp;
726*09537850SAkhilesh Sanikop   const BlockParameters& mv_bp = tile.Parameters(mv_row, mv_column);
727*09537850SAkhilesh Sanikop   if (mv_bp.reference_frame[0] != bp.reference_frame[0] ||
728*09537850SAkhilesh Sanikop       mv_bp.reference_frame[1] != kReferenceFrameNone) {
729*09537850SAkhilesh Sanikop     return;
730*09537850SAkhilesh Sanikop   }
731*09537850SAkhilesh Sanikop   ++*num_samples_scanned;
732*09537850SAkhilesh Sanikop   const int candidate_height4x4 = kNum4x4BlocksHigh[mv_bp.size];
733*09537850SAkhilesh Sanikop   const int candidate_row = mv_row & ~(candidate_height4x4 - 1);
734*09537850SAkhilesh Sanikop   const int candidate_width4x4 = kNum4x4BlocksWide[mv_bp.size];
735*09537850SAkhilesh Sanikop   const int candidate_column = mv_column & ~(candidate_width4x4 - 1);
736*09537850SAkhilesh Sanikop   const BlockParameters& candidate_bp =
737*09537850SAkhilesh Sanikop       tile.Parameters(candidate_row, candidate_column);
738*09537850SAkhilesh Sanikop   const int mv_diff_row =
739*09537850SAkhilesh Sanikop       std::abs(candidate_bp.mv.mv[0].mv[0] - bp.mv.mv[0].mv[0]);
740*09537850SAkhilesh Sanikop   const int mv_diff_column =
741*09537850SAkhilesh Sanikop       std::abs(candidate_bp.mv.mv[0].mv[1] - bp.mv.mv[0].mv[1]);
742*09537850SAkhilesh Sanikop   const bool is_valid =
743*09537850SAkhilesh Sanikop       mv_diff_row + mv_diff_column <= kWarpValidThreshold[block.size];
744*09537850SAkhilesh Sanikop   if (!is_valid && *num_samples_scanned > 1) {
745*09537850SAkhilesh Sanikop     return;
746*09537850SAkhilesh Sanikop   }
747*09537850SAkhilesh Sanikop   const int mid_y =
748*09537850SAkhilesh Sanikop       MultiplyBy4(candidate_row) + MultiplyBy2(candidate_height4x4) - 1;
749*09537850SAkhilesh Sanikop   const int mid_x =
750*09537850SAkhilesh Sanikop       MultiplyBy4(candidate_column) + MultiplyBy2(candidate_width4x4) - 1;
751*09537850SAkhilesh Sanikop   candidates[*num_warp_samples][0] = MultiplyBy8(mid_y);
752*09537850SAkhilesh Sanikop   candidates[*num_warp_samples][1] = MultiplyBy8(mid_x);
753*09537850SAkhilesh Sanikop   candidates[*num_warp_samples][2] =
754*09537850SAkhilesh Sanikop       MultiplyBy8(mid_y) + candidate_bp.mv.mv[0].mv[0];
755*09537850SAkhilesh Sanikop   candidates[*num_warp_samples][3] =
756*09537850SAkhilesh Sanikop       MultiplyBy8(mid_x) + candidate_bp.mv.mv[0].mv[1];
757*09537850SAkhilesh Sanikop   if (is_valid) ++*num_warp_samples;
758*09537850SAkhilesh Sanikop }
759*09537850SAkhilesh Sanikop 
760*09537850SAkhilesh Sanikop // 7.9.2.
761*09537850SAkhilesh Sanikop // In the spec, |dst_sign| is either 1 or -1. Here we set |dst_sign| to either 0
762*09537850SAkhilesh Sanikop // or -1 so that it can be XORed and subtracted directly in ApplySign() and
763*09537850SAkhilesh Sanikop // corresponding SIMD implementations.
MotionFieldProjection(const ObuFrameHeader & frame_header,const std::array<RefCountedBufferPtr,kNumReferenceFrameTypes> & reference_frames,ReferenceFrameType source,int reference_to_current_with_sign,int dst_sign,int y8_start,int y8_end,int x8_start,int x8_end,TemporalMotionField * const motion_field)764*09537850SAkhilesh Sanikop bool MotionFieldProjection(
765*09537850SAkhilesh Sanikop     const ObuFrameHeader& frame_header,
766*09537850SAkhilesh Sanikop     const std::array<RefCountedBufferPtr, kNumReferenceFrameTypes>&
767*09537850SAkhilesh Sanikop         reference_frames,
768*09537850SAkhilesh Sanikop     ReferenceFrameType source, int reference_to_current_with_sign, int dst_sign,
769*09537850SAkhilesh Sanikop     int y8_start, int y8_end, int x8_start, int x8_end,
770*09537850SAkhilesh Sanikop     TemporalMotionField* const motion_field) {
771*09537850SAkhilesh Sanikop   const int source_index =
772*09537850SAkhilesh Sanikop       frame_header.reference_frame_index[source - kReferenceFrameLast];
773*09537850SAkhilesh Sanikop   auto* const source_frame = reference_frames[source_index].get();
774*09537850SAkhilesh Sanikop   assert(source_frame != nullptr);
775*09537850SAkhilesh Sanikop   assert(dst_sign == 0 || dst_sign == -1);
776*09537850SAkhilesh Sanikop   if (source_frame->rows4x4() != frame_header.rows4x4 ||
777*09537850SAkhilesh Sanikop       source_frame->columns4x4() != frame_header.columns4x4 ||
778*09537850SAkhilesh Sanikop       IsIntraFrame(source_frame->frame_type())) {
779*09537850SAkhilesh Sanikop     return false;
780*09537850SAkhilesh Sanikop   }
781*09537850SAkhilesh Sanikop   assert(reference_to_current_with_sign >= -kMaxFrameDistance);
782*09537850SAkhilesh Sanikop   if (reference_to_current_with_sign > kMaxFrameDistance) return true;
783*09537850SAkhilesh Sanikop   const ReferenceInfo& reference_info = *source_frame->reference_info();
784*09537850SAkhilesh Sanikop   const dsp::Dsp& dsp = *dsp::GetDspTable(8);
785*09537850SAkhilesh Sanikop   dsp.motion_field_projection_kernel(
786*09537850SAkhilesh Sanikop       reference_info, reference_to_current_with_sign, dst_sign, y8_start,
787*09537850SAkhilesh Sanikop       y8_end, x8_start, x8_end, motion_field);
788*09537850SAkhilesh Sanikop   return true;
789*09537850SAkhilesh Sanikop }
790*09537850SAkhilesh Sanikop 
791*09537850SAkhilesh Sanikop }  // namespace
792*09537850SAkhilesh Sanikop 
FindMvStack(const Tile::Block & block,bool is_compound,MvContexts * const contexts)793*09537850SAkhilesh Sanikop void FindMvStack(const Tile::Block& block, bool is_compound,
794*09537850SAkhilesh Sanikop                  MvContexts* const contexts) {
795*09537850SAkhilesh Sanikop   PredictionParameters& prediction_parameters =
796*09537850SAkhilesh Sanikop       *block.bp->prediction_parameters;
797*09537850SAkhilesh Sanikop   SetupGlobalMv(block, 0, &prediction_parameters.global_mv[0]);
798*09537850SAkhilesh Sanikop   if (is_compound) SetupGlobalMv(block, 1, &prediction_parameters.global_mv[1]);
799*09537850SAkhilesh Sanikop   bool found_new_mv = false;
800*09537850SAkhilesh Sanikop   bool found_row_match = false;
801*09537850SAkhilesh Sanikop   int num_mv_found = 0;
802*09537850SAkhilesh Sanikop   ScanRow(block, block.column4x4, -1, is_compound, &found_new_mv,
803*09537850SAkhilesh Sanikop           &found_row_match, &num_mv_found);
804*09537850SAkhilesh Sanikop   bool found_column_match = false;
805*09537850SAkhilesh Sanikop   ScanColumn(block, block.row4x4, -1, is_compound, &found_new_mv,
806*09537850SAkhilesh Sanikop              &found_column_match, &num_mv_found);
807*09537850SAkhilesh Sanikop   if (std::max(block.width4x4, block.height4x4) <= 16) {
808*09537850SAkhilesh Sanikop     ScanPoint(block, -1, block.width4x4, is_compound, &found_new_mv,
809*09537850SAkhilesh Sanikop               &found_row_match, &num_mv_found);
810*09537850SAkhilesh Sanikop   }
811*09537850SAkhilesh Sanikop   const int nearest_matches =
812*09537850SAkhilesh Sanikop       static_cast<int>(found_row_match) + static_cast<int>(found_column_match);
813*09537850SAkhilesh Sanikop   prediction_parameters.nearest_mv_count = num_mv_found;
814*09537850SAkhilesh Sanikop   if (block.tile.frame_header().use_ref_frame_mvs) {
815*09537850SAkhilesh Sanikop     // Initialize to invalid value, and it will be set when temporal mv is zero.
816*09537850SAkhilesh Sanikop     contexts->zero_mv = -1;
817*09537850SAkhilesh Sanikop     TemporalScan(block, is_compound, &contexts->zero_mv, &num_mv_found);
818*09537850SAkhilesh Sanikop   } else {
819*09537850SAkhilesh Sanikop     contexts->zero_mv = 0;
820*09537850SAkhilesh Sanikop   }
821*09537850SAkhilesh Sanikop   bool dummy_bool = false;
822*09537850SAkhilesh Sanikop   ScanPoint(block, -1, -1, is_compound, &dummy_bool, &found_row_match,
823*09537850SAkhilesh Sanikop             &num_mv_found);
824*09537850SAkhilesh Sanikop   static constexpr int deltas[2] = {-3, -5};
825*09537850SAkhilesh Sanikop   for (int i = 0; i < 2; ++i) {
826*09537850SAkhilesh Sanikop     if (i == 0 || block.height4x4 > 1) {
827*09537850SAkhilesh Sanikop       ScanRow(block, block.column4x4 | 1, deltas[i] + (block.row4x4 & 1),
828*09537850SAkhilesh Sanikop               is_compound, &dummy_bool, &found_row_match, &num_mv_found);
829*09537850SAkhilesh Sanikop     }
830*09537850SAkhilesh Sanikop     if (i == 0 || block.width4x4 > 1) {
831*09537850SAkhilesh Sanikop       ScanColumn(block, block.row4x4 | 1, deltas[i] + (block.column4x4 & 1),
832*09537850SAkhilesh Sanikop                  is_compound, &dummy_bool, &found_column_match, &num_mv_found);
833*09537850SAkhilesh Sanikop     }
834*09537850SAkhilesh Sanikop   }
835*09537850SAkhilesh Sanikop   if (num_mv_found < 2) {
836*09537850SAkhilesh Sanikop     ExtraSearch(block, is_compound, &num_mv_found);
837*09537850SAkhilesh Sanikop   } else {
838*09537850SAkhilesh Sanikop     // The sort of |weight_index_stack| could be moved to Tile::AssignIntraMv()
839*09537850SAkhilesh Sanikop     // and Tile::AssignInterMv(), and only do a partial sort to the max index we
840*09537850SAkhilesh Sanikop     // need. However, the speed gain is trivial.
841*09537850SAkhilesh Sanikop     // For intra case, only the first 1 or 2 mvs in the stack will be used.
842*09537850SAkhilesh Sanikop     // For inter case, |prediction_parameters.ref_mv_index| is at most 3.
843*09537850SAkhilesh Sanikop     // We only need to do the partial sort up to the first 4 mvs.
844*09537850SAkhilesh Sanikop     SortWeightIndexStack(prediction_parameters.nearest_mv_count, 4,
845*09537850SAkhilesh Sanikop                          prediction_parameters.weight_index_stack);
846*09537850SAkhilesh Sanikop     // When there are 4 or more nearest mvs, the other mvs will not be used.
847*09537850SAkhilesh Sanikop     if (prediction_parameters.nearest_mv_count < 4) {
848*09537850SAkhilesh Sanikop       SortWeightIndexStack(
849*09537850SAkhilesh Sanikop           num_mv_found - prediction_parameters.nearest_mv_count,
850*09537850SAkhilesh Sanikop           4 - prediction_parameters.nearest_mv_count,
851*09537850SAkhilesh Sanikop           prediction_parameters.weight_index_stack +
852*09537850SAkhilesh Sanikop               prediction_parameters.nearest_mv_count);
853*09537850SAkhilesh Sanikop     }
854*09537850SAkhilesh Sanikop   }
855*09537850SAkhilesh Sanikop   prediction_parameters.ref_mv_count = num_mv_found;
856*09537850SAkhilesh Sanikop   const int total_matches =
857*09537850SAkhilesh Sanikop       static_cast<int>(found_row_match) + static_cast<int>(found_column_match);
858*09537850SAkhilesh Sanikop   ComputeContexts(found_new_mv, nearest_matches, total_matches,
859*09537850SAkhilesh Sanikop                   &contexts->new_mv, &contexts->reference_mv);
860*09537850SAkhilesh Sanikop   // The mv stack clamping process is in Tile::AssignIntraMv() and
861*09537850SAkhilesh Sanikop   // Tile::AssignInterMv(), and only up to two mvs are clamped.
862*09537850SAkhilesh Sanikop }
863*09537850SAkhilesh Sanikop 
FindWarpSamples(const Tile::Block & block,int * const num_warp_samples,int * const num_samples_scanned,int candidates[kMaxLeastSquaresSamples][4])864*09537850SAkhilesh Sanikop void FindWarpSamples(const Tile::Block& block, int* const num_warp_samples,
865*09537850SAkhilesh Sanikop                      int* const num_samples_scanned,
866*09537850SAkhilesh Sanikop                      int candidates[kMaxLeastSquaresSamples][4]) {
867*09537850SAkhilesh Sanikop   const Tile& tile = block.tile;
868*09537850SAkhilesh Sanikop   bool top_left = true;
869*09537850SAkhilesh Sanikop   bool top_right = true;
870*09537850SAkhilesh Sanikop   int step = 1;
871*09537850SAkhilesh Sanikop   if (block.top_available[kPlaneY]) {
872*09537850SAkhilesh Sanikop     BlockSize source_size =
873*09537850SAkhilesh Sanikop         tile.Parameters(block.row4x4 - 1, block.column4x4).size;
874*09537850SAkhilesh Sanikop     const int source_width4x4 = kNum4x4BlocksWide[source_size];
875*09537850SAkhilesh Sanikop     if (block.width4x4 <= source_width4x4) {
876*09537850SAkhilesh Sanikop       // The & here is equivalent to % since source_width4x4 is a power of two.
877*09537850SAkhilesh Sanikop       const int column_offset = -(block.column4x4 & (source_width4x4 - 1));
878*09537850SAkhilesh Sanikop       if (column_offset < 0) top_left = false;
879*09537850SAkhilesh Sanikop       if (column_offset + source_width4x4 > block.width4x4) top_right = false;
880*09537850SAkhilesh Sanikop       AddSample(block, -1, 0, num_warp_samples, num_samples_scanned,
881*09537850SAkhilesh Sanikop                 candidates);
882*09537850SAkhilesh Sanikop     } else {
883*09537850SAkhilesh Sanikop       for (int i = 0;
884*09537850SAkhilesh Sanikop            i < std::min(static_cast<int>(block.width4x4),
885*09537850SAkhilesh Sanikop                         tile.frame_header().columns4x4 - block.column4x4);
886*09537850SAkhilesh Sanikop            i += step) {
887*09537850SAkhilesh Sanikop         source_size =
888*09537850SAkhilesh Sanikop             tile.Parameters(block.row4x4 - 1, block.column4x4 + i).size;
889*09537850SAkhilesh Sanikop         step = std::min(static_cast<int>(block.width4x4),
890*09537850SAkhilesh Sanikop                         static_cast<int>(kNum4x4BlocksWide[source_size]));
891*09537850SAkhilesh Sanikop         AddSample(block, -1, i, num_warp_samples, num_samples_scanned,
892*09537850SAkhilesh Sanikop                   candidates);
893*09537850SAkhilesh Sanikop       }
894*09537850SAkhilesh Sanikop     }
895*09537850SAkhilesh Sanikop   }
896*09537850SAkhilesh Sanikop   if (block.left_available[kPlaneY]) {
897*09537850SAkhilesh Sanikop     BlockSize source_size =
898*09537850SAkhilesh Sanikop         tile.Parameters(block.row4x4, block.column4x4 - 1).size;
899*09537850SAkhilesh Sanikop     const int source_height4x4 = kNum4x4BlocksHigh[source_size];
900*09537850SAkhilesh Sanikop     if (block.height4x4 <= source_height4x4) {
901*09537850SAkhilesh Sanikop       const int row_offset = -(block.row4x4 & (source_height4x4 - 1));
902*09537850SAkhilesh Sanikop       if (row_offset < 0) top_left = false;
903*09537850SAkhilesh Sanikop       AddSample(block, 0, -1, num_warp_samples, num_samples_scanned,
904*09537850SAkhilesh Sanikop                 candidates);
905*09537850SAkhilesh Sanikop     } else {
906*09537850SAkhilesh Sanikop       for (int i = 0; i < std::min(static_cast<int>(block.height4x4),
907*09537850SAkhilesh Sanikop                                    tile.frame_header().rows4x4 - block.row4x4);
908*09537850SAkhilesh Sanikop            i += step) {
909*09537850SAkhilesh Sanikop         source_size =
910*09537850SAkhilesh Sanikop             tile.Parameters(block.row4x4 + i, block.column4x4 - 1).size;
911*09537850SAkhilesh Sanikop         step = std::min(static_cast<int>(block.height4x4),
912*09537850SAkhilesh Sanikop                         static_cast<int>(kNum4x4BlocksHigh[source_size]));
913*09537850SAkhilesh Sanikop         AddSample(block, i, -1, num_warp_samples, num_samples_scanned,
914*09537850SAkhilesh Sanikop                   candidates);
915*09537850SAkhilesh Sanikop       }
916*09537850SAkhilesh Sanikop     }
917*09537850SAkhilesh Sanikop   }
918*09537850SAkhilesh Sanikop   if (top_left) {
919*09537850SAkhilesh Sanikop     AddSample(block, -1, -1, num_warp_samples, num_samples_scanned, candidates);
920*09537850SAkhilesh Sanikop   }
921*09537850SAkhilesh Sanikop   if (top_right && block.size <= kBlock64x64) {
922*09537850SAkhilesh Sanikop     AddSample(block, -1, block.width4x4, num_warp_samples, num_samples_scanned,
923*09537850SAkhilesh Sanikop               candidates);
924*09537850SAkhilesh Sanikop   }
925*09537850SAkhilesh Sanikop   if (*num_warp_samples == 0 && *num_samples_scanned > 0) *num_warp_samples = 1;
926*09537850SAkhilesh Sanikop }
927*09537850SAkhilesh Sanikop 
SetupMotionField(const ObuFrameHeader & frame_header,const RefCountedBuffer & current_frame,const std::array<RefCountedBufferPtr,kNumReferenceFrameTypes> & reference_frames,int row4x4_start,int row4x4_end,int column4x4_start,int column4x4_end,TemporalMotionField * const motion_field)928*09537850SAkhilesh Sanikop void SetupMotionField(
929*09537850SAkhilesh Sanikop     const ObuFrameHeader& frame_header, const RefCountedBuffer& current_frame,
930*09537850SAkhilesh Sanikop     const std::array<RefCountedBufferPtr, kNumReferenceFrameTypes>&
931*09537850SAkhilesh Sanikop         reference_frames,
932*09537850SAkhilesh Sanikop     int row4x4_start, int row4x4_end, int column4x4_start, int column4x4_end,
933*09537850SAkhilesh Sanikop     TemporalMotionField* const motion_field) {
934*09537850SAkhilesh Sanikop   assert(frame_header.use_ref_frame_mvs);
935*09537850SAkhilesh Sanikop   const int y8_start = DivideBy2(row4x4_start);
936*09537850SAkhilesh Sanikop   const int y8_end = DivideBy2(std::min(row4x4_end, frame_header.rows4x4));
937*09537850SAkhilesh Sanikop   const int x8_start = DivideBy2(column4x4_start);
938*09537850SAkhilesh Sanikop   const int x8_end =
939*09537850SAkhilesh Sanikop       DivideBy2(std::min(column4x4_end, frame_header.columns4x4));
940*09537850SAkhilesh Sanikop   const int last_index = frame_header.reference_frame_index[0];
941*09537850SAkhilesh Sanikop   const ReferenceInfo& reference_info = *current_frame.reference_info();
942*09537850SAkhilesh Sanikop   if (!IsIntraFrame(reference_frames[last_index]->frame_type())) {
943*09537850SAkhilesh Sanikop     const int last_alternate_order_hint =
944*09537850SAkhilesh Sanikop         reference_frames[last_index]
945*09537850SAkhilesh Sanikop             ->reference_info()
946*09537850SAkhilesh Sanikop             ->order_hint[kReferenceFrameAlternate];
947*09537850SAkhilesh Sanikop     const int current_gold_order_hint =
948*09537850SAkhilesh Sanikop         reference_info.order_hint[kReferenceFrameGolden];
949*09537850SAkhilesh Sanikop     if (last_alternate_order_hint != current_gold_order_hint) {
950*09537850SAkhilesh Sanikop       const int reference_offset_last =
951*09537850SAkhilesh Sanikop           -reference_info.relative_distance_from[kReferenceFrameLast];
952*09537850SAkhilesh Sanikop       if (std::abs(reference_offset_last) <= kMaxFrameDistance) {
953*09537850SAkhilesh Sanikop         MotionFieldProjection(frame_header, reference_frames,
954*09537850SAkhilesh Sanikop                               kReferenceFrameLast, reference_offset_last, -1,
955*09537850SAkhilesh Sanikop                               y8_start, y8_end, x8_start, x8_end, motion_field);
956*09537850SAkhilesh Sanikop       }
957*09537850SAkhilesh Sanikop     }
958*09537850SAkhilesh Sanikop   }
959*09537850SAkhilesh Sanikop   int ref_stamp = 1;
960*09537850SAkhilesh Sanikop   const int reference_offset_backward =
961*09537850SAkhilesh Sanikop       reference_info.relative_distance_from[kReferenceFrameBackward];
962*09537850SAkhilesh Sanikop   if (reference_offset_backward > 0 &&
963*09537850SAkhilesh Sanikop       MotionFieldProjection(frame_header, reference_frames,
964*09537850SAkhilesh Sanikop                             kReferenceFrameBackward, reference_offset_backward,
965*09537850SAkhilesh Sanikop                             0, y8_start, y8_end, x8_start, x8_end,
966*09537850SAkhilesh Sanikop                             motion_field)) {
967*09537850SAkhilesh Sanikop     --ref_stamp;
968*09537850SAkhilesh Sanikop   }
969*09537850SAkhilesh Sanikop   const int reference_offset_alternate2 =
970*09537850SAkhilesh Sanikop       reference_info.relative_distance_from[kReferenceFrameAlternate2];
971*09537850SAkhilesh Sanikop   if (reference_offset_alternate2 > 0 &&
972*09537850SAkhilesh Sanikop       MotionFieldProjection(frame_header, reference_frames,
973*09537850SAkhilesh Sanikop                             kReferenceFrameAlternate2,
974*09537850SAkhilesh Sanikop                             reference_offset_alternate2, 0, y8_start, y8_end,
975*09537850SAkhilesh Sanikop                             x8_start, x8_end, motion_field)) {
976*09537850SAkhilesh Sanikop     --ref_stamp;
977*09537850SAkhilesh Sanikop   }
978*09537850SAkhilesh Sanikop   if (ref_stamp >= 0) {
979*09537850SAkhilesh Sanikop     const int reference_offset_alternate =
980*09537850SAkhilesh Sanikop         reference_info.relative_distance_from[kReferenceFrameAlternate];
981*09537850SAkhilesh Sanikop     if (reference_offset_alternate > 0 &&
982*09537850SAkhilesh Sanikop         MotionFieldProjection(frame_header, reference_frames,
983*09537850SAkhilesh Sanikop                               kReferenceFrameAlternate,
984*09537850SAkhilesh Sanikop                               reference_offset_alternate, 0, y8_start, y8_end,
985*09537850SAkhilesh Sanikop                               x8_start, x8_end, motion_field)) {
986*09537850SAkhilesh Sanikop       --ref_stamp;
987*09537850SAkhilesh Sanikop     }
988*09537850SAkhilesh Sanikop   }
989*09537850SAkhilesh Sanikop   if (ref_stamp >= 0) {
990*09537850SAkhilesh Sanikop     const int reference_offset_last2 =
991*09537850SAkhilesh Sanikop         -reference_info.relative_distance_from[kReferenceFrameLast2];
992*09537850SAkhilesh Sanikop     if (std::abs(reference_offset_last2) <= kMaxFrameDistance) {
993*09537850SAkhilesh Sanikop       MotionFieldProjection(frame_header, reference_frames,
994*09537850SAkhilesh Sanikop                             kReferenceFrameLast2, reference_offset_last2, -1,
995*09537850SAkhilesh Sanikop                             y8_start, y8_end, x8_start, x8_end, motion_field);
996*09537850SAkhilesh Sanikop     }
997*09537850SAkhilesh Sanikop   }
998*09537850SAkhilesh Sanikop }
999*09537850SAkhilesh Sanikop 
1000*09537850SAkhilesh Sanikop }  // namespace libgav1
1001