aboutsummaryrefslogtreecommitdiff
path: root/src/tile.h
blob: 83c34233f18729c0437b716108fce47b9ffb135d (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
/*
 * Copyright 2019 The libgav1 Authors
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#ifndef LIBGAV1_SRC_TILE_H_
#define LIBGAV1_SRC_TILE_H_

#include <algorithm>
#include <array>
#include <cassert>
#include <condition_variable>  // NOLINT (unapproved c++11 header)
#include <cstddef>
#include <cstdint>
#include <memory>
#include <mutex>  // NOLINT (unapproved c++11 header)
#include <vector>

#include "src/buffer_pool.h"
#include "src/decoder_state.h"
#include "src/dsp/common.h"
#include "src/dsp/constants.h"
#include "src/dsp/dsp.h"
#include "src/frame_scratch_buffer.h"
#include "src/loop_restoration_info.h"
#include "src/obu_parser.h"
#include "src/post_filter.h"
#include "src/quantizer.h"
#include "src/residual_buffer_pool.h"
#include "src/symbol_decoder_context.h"
#include "src/tile_scratch_buffer.h"
#include "src/utils/array_2d.h"
#include "src/utils/block_parameters_holder.h"
#include "src/utils/blocking_counter.h"
#include "src/utils/common.h"
#include "src/utils/compiler_attributes.h"
#include "src/utils/constants.h"
#include "src/utils/entropy_decoder.h"
#include "src/utils/memory.h"
#include "src/utils/segmentation_map.h"
#include "src/utils/threadpool.h"
#include "src/utils/types.h"
#include "src/yuv_buffer.h"

namespace libgav1 {

// Indicates what the ProcessSuperBlock() and TransformBlock() functions should
// do. "Parse" refers to consuming the bitstream, reading the transform
// coefficients and performing the dequantization. "Decode" refers to computing
// the prediction, applying the inverse transforms and adding the residual.
enum ProcessingMode {
  kProcessingModeParseOnly,
  kProcessingModeDecodeOnly,
  kProcessingModeParseAndDecode,
};

// The alignment requirement is due to the SymbolDecoderContext member
// symbol_decoder_context_.
class Tile : public MaxAlignedAllocable {
 public:
  static std::unique_ptr<Tile> Create(
      int tile_number, const uint8_t* const data, size_t size,
      const ObuSequenceHeader& sequence_header,
      const ObuFrameHeader& frame_header, RefCountedBuffer* const current_frame,
      const DecoderState& state, FrameScratchBuffer* const frame_scratch_buffer,
      const WedgeMaskArray& wedge_masks,
      const QuantizerMatrix& quantizer_matrix,
      SymbolDecoderContext* const saved_symbol_decoder_context,
      const SegmentationMap* prev_segment_ids, PostFilter* const post_filter,
      const dsp::Dsp* const dsp, ThreadPool* const thread_pool,
      BlockingCounterWithStatus* const pending_tiles, bool frame_parallel,
      bool use_intra_prediction_buffer) {
    std::unique_ptr<Tile> tile(new (std::nothrow) Tile(
        tile_number, data, size, sequence_header, frame_header, current_frame,
        state, frame_scratch_buffer, wedge_masks, quantizer_matrix,
        saved_symbol_decoder_context, prev_segment_ids, post_filter, dsp,
        thread_pool, pending_tiles, frame_parallel,
        use_intra_prediction_buffer));
    return (tile != nullptr && tile->Init()) ? std::move(tile) : nullptr;
  }

  // Move only.
  Tile(Tile&& tile) noexcept;
  Tile& operator=(Tile&& tile) noexcept;
  Tile(const Tile&) = delete;
  Tile& operator=(const Tile&) = delete;

  struct Block;  // Defined after this class.

  // Parses the entire tile.
  bool Parse();
  // Decodes the entire tile. |superblock_row_progress| and
  // |superblock_row_progress_condvar| are arrays of size equal to the number of
  // superblock rows in the frame. Increments |superblock_row_progress[i]| after
  // each superblock row at index |i| is decoded. If the count reaches the
  // number of tile columns, then it notifies
  // |superblock_row_progress_condvar[i]|.
  bool Decode(std::mutex* mutex, int* superblock_row_progress,
              std::condition_variable* superblock_row_progress_condvar);
  // Parses and decodes the entire tile. Depending on the configuration of this
  // Tile, this function may do multithreaded decoding.
  bool ParseAndDecode();  // 5.11.2.
  // Processes all the columns of the superblock row at |row4x4| that are within
  // this Tile. If |save_symbol_decoder_context| is true, then
  // SaveSymbolDecoderContext() is invoked for the last superblock row.
  template <ProcessingMode processing_mode, bool save_symbol_decoder_context>
  bool ProcessSuperBlockRow(int row4x4, TileScratchBuffer* scratch_buffer);

  const ObuSequenceHeader& sequence_header() const { return sequence_header_; }
  const ObuFrameHeader& frame_header() const { return frame_header_; }
  const RefCountedBuffer& current_frame() const { return current_frame_; }
  const TemporalMotionField& motion_field() const { return motion_field_; }
  const std::array<bool, kNumReferenceFrameTypes>& reference_frame_sign_bias()
      const {
    return reference_frame_sign_bias_;
  }

  bool IsRow4x4Inside(int row4x4) const {
    return row4x4 >= row4x4_start_ && row4x4 < row4x4_end_;
  }

  // 5.11.51.
  bool IsInside(int row4x4, int column4x4) const {
    return IsRow4x4Inside(row4x4) && column4x4 >= column4x4_start_ &&
           column4x4 < column4x4_end_;
  }

  bool IsLeftInside(int column4x4) const {
    // We use "larger than" as the condition. Don't pass in the left column
    // offset column4x4 - 1.
    assert(column4x4 <= column4x4_end_);
    return column4x4 > column4x4_start_;
  }

  bool IsTopInside(int row4x4) const {
    // We use "larger than" as the condition. Don't pass in the top row offset
    // row4x4 - 1.
    assert(row4x4 <= row4x4_end_);
    return row4x4 > row4x4_start_;
  }

  bool IsTopLeftInside(int row4x4, int column4x4) const {
    // We use "larger than" as the condition. Don't pass in the top row offset
    // row4x4 - 1 or the left column offset column4x4 - 1.
    assert(row4x4 <= row4x4_end_);
    assert(column4x4 <= column4x4_end_);
    return row4x4 > row4x4_start_ && column4x4 > column4x4_start_;
  }

  bool IsBottomRightInside(int row4x4, int column4x4) const {
    assert(row4x4 >= row4x4_start_);
    assert(column4x4 >= column4x4_start_);
    return row4x4 < row4x4_end_ && column4x4 < column4x4_end_;
  }

  BlockParameters** BlockParametersAddress(int row4x4, int column4x4) const {
    return block_parameters_holder_.Address(row4x4, column4x4);
  }

  int BlockParametersStride() const {
    return block_parameters_holder_.columns4x4();
  }

  // Returns true if Parameters() can be called with |row| and |column| as
  // inputs, false otherwise.
  bool HasParameters(int row, int column) const {
    return block_parameters_holder_.Find(row, column) != nullptr;
  }
  const BlockParameters& Parameters(int row, int column) const {
    return *block_parameters_holder_.Find(row, column);
  }

  int number() const { return number_; }
  int superblock_rows() const { return superblock_rows_; }
  int superblock_columns() const { return superblock_columns_; }
  int row4x4_start() const { return row4x4_start_; }
  int column4x4_start() const { return column4x4_start_; }
  int column4x4_end() const { return column4x4_end_; }

 private:
  // Stores the transform tree state when reading variable size transform trees
  // and when applying the transform tree. When applying the transform tree,
  // |depth| is not used.
  struct TransformTreeNode {
    // The default constructor is invoked by the Stack<TransformTreeNode, n>
    // constructor. Stack<> does not use the default-constructed elements, so it
    // is safe for the default constructor to not initialize the members.
    TransformTreeNode() = default;
    TransformTreeNode(int x, int y, TransformSize tx_size, int depth = -1)
        : x(x), y(y), tx_size(tx_size), depth(depth) {}

    int x;
    int y;
    TransformSize tx_size;
    int depth;
  };

  // Enum to track the processing state of a superblock.
  enum SuperBlockState : uint8_t {
    kSuperBlockStateNone,       // Not yet parsed or decoded.
    kSuperBlockStateParsed,     // Parsed but not yet decoded.
    kSuperBlockStateScheduled,  // Scheduled for decoding.
    kSuperBlockStateDecoded     // Parsed and decoded.
  };

  // Parameters used to facilitate multi-threading within the Tile.
  struct ThreadingParameters {
    std::mutex mutex;
    // 2d array of size |superblock_rows_| by |superblock_columns_| containing
    // the processing state of each superblock.
    Array2D<SuperBlockState> sb_state LIBGAV1_GUARDED_BY(mutex);
    // Variable used to indicate either parse or decode failure.
    bool abort LIBGAV1_GUARDED_BY(mutex) = false;
    int pending_jobs LIBGAV1_GUARDED_BY(mutex) = 0;
    std::condition_variable pending_jobs_zero_condvar;
  };

  // The residual pointer is used to traverse the |residual_buffer_|. It is
  // used in two different ways.
  // If |split_parse_and_decode_| is true:
  //    The pointer points to the beginning of the |residual_buffer_| when the
  //    "parse" and "decode" steps begin. It is then moved forward tx_size in
  //    each iteration of the "parse" and the "decode" steps. In this case, the
  //    ResidualPtr variable passed into various functions starting from
  //    ProcessSuperBlock is used as an in/out parameter to keep track of the
  //    residual pointer.
  // If |split_parse_and_decode_| is false:
  //    The pointer is reset to the beginning of the |residual_buffer_| for
  //    every transform block.
  using ResidualPtr = uint8_t*;

  Tile(int tile_number, const uint8_t* data, size_t size,
       const ObuSequenceHeader& sequence_header,
       const ObuFrameHeader& frame_header, RefCountedBuffer* current_frame,
       const DecoderState& state, FrameScratchBuffer* frame_scratch_buffer,
       const WedgeMaskArray& wedge_masks,
       const QuantizerMatrix& quantizer_matrix,
       SymbolDecoderContext* saved_symbol_decoder_context,
       const SegmentationMap* prev_segment_ids, PostFilter* post_filter,
       const dsp::Dsp* dsp, ThreadPool* thread_pool,
       BlockingCounterWithStatus* pending_tiles, bool frame_parallel,
       bool use_intra_prediction_buffer);

  // Performs member initializations that may fail. Helper function used by
  // Create().
  LIBGAV1_MUST_USE_RESULT bool Init();

  // Saves the symbol decoder context of this tile into
  // |saved_symbol_decoder_context_| if necessary.
  void SaveSymbolDecoderContext();

  // Entry point for multi-threaded decoding. This function performs the same
  // functionality as ParseAndDecode(). The current thread does the "parse" step
  // while the worker threads do the "decode" step.
  bool ThreadedParseAndDecode();

  // Returns whether or not the prerequisites for decoding the superblock at
  // |row_index| and |column_index| are satisfied. |threading_.mutex| must be
  // held when calling this function.
  bool CanDecode(int row_index, int column_index) const;

  // This function is run by the worker threads when multi-threaded decoding is
  // enabled. Once a superblock is decoded, this function will set the
  // corresponding |threading_.sb_state| entry to kSuperBlockStateDecoded. On
  // failure, |threading_.abort| will be set to true. If at any point
  // |threading_.abort| becomes true, this function will return as early as it
  // can. If the decoding succeeds, this function will also schedule the
  // decoding jobs for the superblock to the bottom-left and the superblock to
  // the right of this superblock (if it is allowed).
  void DecodeSuperBlock(int row_index, int column_index, int block_width4x4);

  // If |use_intra_prediction_buffer_| is true, then this function copies the
  // last row of the superblockrow starting at |row4x4| into the
  // |intra_prediction_buffer_| (which may be used by the intra prediction
  // process for the next superblock row).
  void PopulateIntraPredictionBuffer(int row4x4);

  uint16_t* GetPartitionCdf(int row4x4, int column4x4, BlockSize block_size);
  bool ReadPartition(int row4x4, int column4x4, BlockSize block_size,
                     bool has_rows, bool has_columns, Partition* partition);
  // Processes the Partition starting at |row4x4_start|, |column4x4_start|
  // iteratively. It performs a DFS traversal over the partition tree to process
  // the blocks in the right order.
  bool ProcessPartition(
      int row4x4_start, int column4x4_start, TileScratchBuffer* scratch_buffer,
      ResidualPtr* residual);  // Iterative implementation of 5.11.4.
  bool ProcessBlock(int row4x4, int column4x4, BlockSize block_size,
                    TileScratchBuffer* scratch_buffer,
                    ResidualPtr* residual);   // 5.11.5.
  void ResetCdef(int row4x4, int column4x4);  // 5.11.55.

  // This function is used to decode a superblock when the parsing has already
  // been done for that superblock.
  bool DecodeSuperBlock(int sb_row_index, int sb_column_index,
                        TileScratchBuffer* scratch_buffer);
  // Helper function used by DecodeSuperBlock(). Note that the decode_block()
  // function in the spec is equivalent to ProcessBlock() in the code.
  bool DecodeBlock(int row4x4, int column4x4, BlockSize block_size,
                   TileScratchBuffer* scratch_buffer, ResidualPtr* residual);

  void ClearBlockDecoded(TileScratchBuffer* scratch_buffer, int row4x4,
                         int column4x4);  // 5.11.3.
  bool ProcessSuperBlock(int row4x4, int column4x4,
                         TileScratchBuffer* scratch_buffer,
                         ProcessingMode mode);
  void ResetLoopRestorationParams();
  void ReadLoopRestorationCoefficients(int row4x4, int column4x4,
                                       BlockSize block_size);  // 5.11.57.

  // Helper functions for DecodeBlock.
  bool ReadSegmentId(const Block& block);       // 5.11.9.
  bool ReadIntraSegmentId(const Block& block);  // 5.11.8.
  void ReadSkip(const Block& block);            // 5.11.11.
  bool ReadSkipMode(const Block& block);        // 5.11.10.
  void ReadCdef(const Block& block);            // 5.11.56.
  // Returns the new value. |cdf| is an array of size kDeltaSymbolCount + 1.
  int ReadAndClipDelta(uint16_t* cdf, int delta_small, int scale, int min_value,
                       int max_value, int value);
  void ReadQuantizerIndexDelta(const Block& block);  // 5.11.12.
  void ReadLoopFilterDelta(const Block& block);      // 5.11.13.
  // Populates |BlockParameters::deblock_filter_level| for the given |block|
  // using |deblock_filter_levels_|.
  void PopulateDeblockFilterLevel(const Block& block);
  void PopulateCdefSkip(const Block& block);
  void ReadPredictionModeY(const Block& block, bool intra_y_mode);
  void ReadIntraAngleInfo(const Block& block,
                          PlaneType plane_type);  // 5.11.42 and 5.11.43.
  void ReadPredictionModeUV(const Block& block);
  void ReadCflAlpha(const Block& block);  // 5.11.45.
  int GetPaletteCache(const Block& block, PlaneType plane_type,
                      uint16_t* cache);
  void ReadPaletteColors(const Block& block, Plane plane);
  void ReadPaletteModeInfo(const Block& block);      // 5.11.46.
  void ReadFilterIntraModeInfo(const Block& block);  // 5.11.24.
  int ReadMotionVectorComponent(const Block& block,
                                int component);                // 5.11.32.
  void ReadMotionVector(const Block& block, int index);        // 5.11.31.
  bool DecodeIntraModeInfo(const Block& block);                // 5.11.7.
  int8_t ComputePredictedSegmentId(const Block& block) const;  // 5.11.21.
  bool ReadInterSegmentId(const Block& block, bool pre_skip);  // 5.11.19.
  void ReadIsInter(const Block& block, bool skip_mode);        // 5.11.20.
  bool ReadIntraBlockModeInfo(const Block& block,
                              bool intra_y_mode);  // 5.11.22.
  CompoundReferenceType ReadCompoundReferenceType(const Block& block);
  template <bool is_single, bool is_backward, int index>
  uint16_t* GetReferenceCdf(const Block& block, CompoundReferenceType type =
                                                    kNumCompoundReferenceTypes);
  void ReadReferenceFrames(const Block& block, bool skip_mode);  // 5.11.25.
  void ReadInterPredictionModeY(const Block& block,
                                const MvContexts& mode_contexts,
                                bool skip_mode);
  void ReadRefMvIndex(const Block& block);
  void ReadInterIntraMode(const Block& block, bool is_compound,
                          bool skip_mode);        // 5.11.28.
  bool IsScaled(ReferenceFrameType type) const {  // Part of 5.11.27.
    const int index =
        frame_header_.reference_frame_index[type - kReferenceFrameLast];
    return reference_frames_[index]->upscaled_width() != frame_header_.width ||
           reference_frames_[index]->frame_height() != frame_header_.height;
  }
  void ReadMotionMode(const Block& block, bool is_compound,
                      bool skip_mode);  // 5.11.27.
  uint16_t* GetIsExplicitCompoundTypeCdf(const Block& block);
  uint16_t* GetIsCompoundTypeAverageCdf(const Block& block);
  void ReadCompoundType(const Block& block, bool is_compound, bool skip_mode,
                        bool* is_explicit_compound_type,
                        bool* is_compound_type_average);  // 5.11.29.
  uint16_t* GetInterpolationFilterCdf(const Block& block, int direction);
  void ReadInterpolationFilter(const Block& block, bool skip_mode);
  bool ReadInterBlockModeInfo(const Block& block, bool skip_mode);  // 5.11.23.
  bool DecodeInterModeInfo(const Block& block);                     // 5.11.18.
  bool DecodeModeInfo(const Block& block);                          // 5.11.6.
  bool IsMvValid(const Block& block, bool is_compound) const;       // 6.10.25.
  bool AssignInterMv(const Block& block, bool is_compound);         // 5.11.26.
  bool AssignIntraMv(const Block& block);                           // 5.11.26.
  int GetTopTransformWidth(const Block& block, int row4x4, int column4x4,
                           bool ignore_skip);
  int GetLeftTransformHeight(const Block& block, int row4x4, int column4x4,
                             bool ignore_skip);
  TransformSize ReadFixedTransformSize(const Block& block);  // 5.11.15.
  // Iterative implementation of 5.11.17.
  void ReadVariableTransformTree(const Block& block, int row4x4, int column4x4,
                                 TransformSize tx_size);
  void DecodeTransformSize(const Block& block);  // 5.11.16.
  bool ComputePrediction(const Block& block);    // 5.11.33.
  // |x4| and |y4| are the column and row positions of the 4x4 block. |w4| and
  // |h4| are the width and height in 4x4 units of |tx_size|.
  int GetTransformAllZeroContext(const Block& block, Plane plane,
                                 TransformSize tx_size, int x4, int y4, int w4,
                                 int h4);
  TransformSet GetTransformSet(TransformSize tx_size,
                               bool is_inter) const;  // 5.11.48.
  TransformType ComputeTransformType(const Block& block, Plane plane,
                                     TransformSize tx_size, int block_x,
                                     int block_y);  // 5.11.40.
  void ReadTransformType(const Block& block, int x4, int y4,
                         TransformSize tx_size);  // 5.11.47.
  template <typename ResidualType>
  void ReadCoeffBase2D(
      const uint16_t* scan, TransformSize tx_size, int adjusted_tx_width_log2,
      int eob,
      uint16_t coeff_base_cdf[kCoeffBaseContexts][kCoeffBaseSymbolCount + 1],
      uint16_t coeff_base_range_cdf[kCoeffBaseRangeContexts]
                                   [kCoeffBaseRangeSymbolCount + 1],
      ResidualType* quantized_buffer, uint8_t* level_buffer);
  template <typename ResidualType>
  void ReadCoeffBaseHorizontal(
      const uint16_t* scan, TransformSize tx_size, int adjusted_tx_width_log2,
      int eob,
      uint16_t coeff_base_cdf[kCoeffBaseContexts][kCoeffBaseSymbolCount + 1],
      uint16_t coeff_base_range_cdf[kCoeffBaseRangeContexts]
                                   [kCoeffBaseRangeSymbolCount + 1],
      ResidualType* quantized_buffer, uint8_t* level_buffer);
  template <typename ResidualType>
  void ReadCoeffBaseVertical(
      const uint16_t* scan, TransformSize tx_size, int adjusted_tx_width_log2,
      int eob,
      uint16_t coeff_base_cdf[kCoeffBaseContexts][kCoeffBaseSymbolCount + 1],
      uint16_t coeff_base_range_cdf[kCoeffBaseRangeContexts]
                                   [kCoeffBaseRangeSymbolCount + 1],
      ResidualType* quantized_buffer, uint8_t* level_buffer);
  int GetDcSignContext(int x4, int y4, int w4, int h4, Plane plane);
  void SetEntropyContexts(int x4, int y4, int w4, int h4, Plane plane,
                          uint8_t coefficient_level, int8_t dc_category);
  void InterIntraPrediction(
      uint16_t* prediction_0, const uint8_t* prediction_mask,
      ptrdiff_t prediction_mask_stride,
      const PredictionParameters& prediction_parameters, int prediction_width,
      int prediction_height, int subsampling_x, int subsampling_y,
      uint8_t* dest,
      ptrdiff_t dest_stride);  // Part of section 7.11.3.1 in the spec.
  void CompoundInterPrediction(
      const Block& block, const uint8_t* prediction_mask,
      ptrdiff_t prediction_mask_stride, int prediction_width,
      int prediction_height, int subsampling_x, int subsampling_y,
      int candidate_row, int candidate_column, uint8_t* dest,
      ptrdiff_t dest_stride);  // Part of section 7.11.3.1 in the spec.
  GlobalMotion* GetWarpParams(const Block& block, Plane plane,
                              int prediction_width, int prediction_height,
                              const PredictionParameters& prediction_parameters,
                              ReferenceFrameType reference_type,
                              bool* is_local_valid,
                              GlobalMotion* global_motion_params,
                              GlobalMotion* local_warp_params)
      const;  // Part of section 7.11.3.1 in the spec.
  bool InterPrediction(const Block& block, Plane plane, int x, int y,
                       int prediction_width, int prediction_height,
                       int candidate_row, int candidate_column,
                       bool* is_local_valid,
                       GlobalMotion* local_warp_params);  // 7.11.3.1.
  void ScaleMotionVector(const MotionVector& mv, Plane plane,
                         int reference_frame_index, int x, int y, int* start_x,
                         int* start_y, int* step_x, int* step_y);  // 7.11.3.3.
  // If the method returns false, the caller only uses the output parameters
  // *ref_block_start_x and *ref_block_start_y. If the method returns true, the
  // caller uses all three output parameters.
  static bool GetReferenceBlockPosition(
      int reference_frame_index, bool is_scaled, int width, int height,
      int ref_start_x, int ref_last_x, int ref_start_y, int ref_last_y,
      int start_x, int start_y, int step_x, int step_y, int left_border,
      int right_border, int top_border, int bottom_border,
      int* ref_block_start_x, int* ref_block_start_y, int* ref_block_end_x);

  template <typename Pixel>
  void BuildConvolveBlock(Plane plane, int reference_frame_index,
                          bool is_scaled, int height, int ref_start_x,
                          int ref_last_x, int ref_start_y, int ref_last_y,
                          int step_y, int ref_block_start_x,
                          int ref_block_end_x, int ref_block_start_y,
                          uint8_t* block_buffer,
                          ptrdiff_t convolve_buffer_stride,
                          ptrdiff_t block_extended_width);
  bool BlockInterPrediction(const Block& block, Plane plane,
                            int reference_frame_index, const MotionVector& mv,
                            int x, int y, int width, int height,
                            int candidate_row, int candidate_column,
                            uint16_t* prediction, bool is_compound,
                            bool is_inter_intra, uint8_t* dest,
                            ptrdiff_t dest_stride);  // 7.11.3.4.
  bool BlockWarpProcess(const Block& block, Plane plane, int index,
                        int block_start_x, int block_start_y, int width,
                        int height, GlobalMotion* warp_params, bool is_compound,
                        bool is_inter_intra, uint8_t* dest,
                        ptrdiff_t dest_stride);  // 7.11.3.5.
  bool ObmcBlockPrediction(const Block& block, const MotionVector& mv,
                           Plane plane, int reference_frame_index, int width,
                           int height, int x, int y, int candidate_row,
                           int candidate_column,
                           ObmcDirection blending_direction);
  bool ObmcPrediction(const Block& block, Plane plane, int width,
                      int height);  // 7.11.3.9.
  void DistanceWeightedPrediction(void* prediction_0, void* prediction_1,
                                  int width, int height, int candidate_row,
                                  int candidate_column, uint8_t* dest,
                                  ptrdiff_t dest_stride);  // 7.11.3.15.
  // This function specializes the parsing of DC coefficient by removing some of
  // the branches when i == 0 (since scan[0] is always 0 and scan[i] is always
  // non-zero for all other possible values of i). |dc_category| is an output
  // parameter that is populated when |is_dc_coefficient| is true.
  // |coefficient_level| is an output parameter which accumulates the
  // coefficient level.
  template <typename ResidualType, bool is_dc_coefficient>
  LIBGAV1_ALWAYS_INLINE bool ReadSignAndApplyDequantization(
      const uint16_t* scan, int i, int q_value, const uint8_t* quantizer_matrix,
      int shift, int max_value, uint16_t* dc_sign_cdf, int8_t* dc_category,
      int* coefficient_level,
      ResidualType* residual_buffer);     // Part of 5.11.39.
  int ReadCoeffBaseRange(uint16_t* cdf);  // Part of 5.11.39.
  // Returns the number of non-zero coefficients that were read. |tx_type| is an
  // output parameter that stores the computed transform type for the plane
  // whose coefficients were read. Returns -1 on failure.
  template <typename ResidualType>
  int ReadTransformCoefficients(const Block& block, Plane plane, int start_x,
                                int start_y, TransformSize tx_size,
                                TransformType* tx_type);  // 5.11.39.
  bool TransformBlock(const Block& block, Plane plane, int base_x, int base_y,
                      TransformSize tx_size, int x, int y,
                      ProcessingMode mode);  // 5.11.35.
  // Iterative implementation of 5.11.36.
  bool TransformTree(const Block& block, int start_x, int start_y,
                     BlockSize plane_size, ProcessingMode mode);
  void ReconstructBlock(const Block& block, Plane plane, int start_x,
                        int start_y, TransformSize tx_size,
                        TransformType tx_type,
                        int non_zero_coeff_count);         // Part of 7.12.3.
  bool Residual(const Block& block, ProcessingMode mode);  // 5.11.34.
  // part of 5.11.5 (reset_block_context() in the spec).
  void ResetEntropyContext(const Block& block);
  // Populates the |color_context| and |color_order| for the |i|th iteration
  // with entries counting down from |start| to |end| (|start| > |end|).
  void PopulatePaletteColorContexts(
      const Block& block, PlaneType plane_type, int i, int start, int end,
      uint8_t color_order[kMaxPaletteSquare][kMaxPaletteSize],
      uint8_t color_context[kMaxPaletteSquare]);  // 5.11.50.
  bool ReadPaletteTokens(const Block& block);     // 5.11.49.
  template <typename Pixel>
  void IntraPrediction(const Block& block, Plane plane, int x, int y,
                       bool has_left, bool has_top, bool has_top_right,
                       bool has_bottom_left, PredictionMode mode,
                       TransformSize tx_size);
  int GetIntraEdgeFilterType(const Block& block,
                             Plane plane) const;  // 7.11.2.8.
  template <typename Pixel>
  void DirectionalPrediction(const Block& block, Plane plane, int x, int y,
                             bool has_left, bool has_top, bool needs_left,
                             bool needs_top, int prediction_angle, int width,
                             int height, int max_x, int max_y,
                             TransformSize tx_size, Pixel* top_row,
                             Pixel* left_column);  // 7.11.2.4.
  template <typename Pixel>
  void PalettePrediction(const Block& block, Plane plane, int start_x,
                         int start_y, int x, int y,
                         TransformSize tx_size);  // 7.11.4.
  template <typename Pixel>
  void ChromaFromLumaPrediction(const Block& block, Plane plane, int start_x,
                                int start_y,
                                TransformSize tx_size);  // 7.11.5.
  // Section 7.19. Applies some filtering and reordering to the motion vectors
  // for the given |block| and stores them into |current_frame_|.
  void StoreMotionFieldMvsIntoCurrentFrame(const Block& block);

  // SetCdfContext*() functions will populate the |left_context_| and
  // |top_context_| for the |block|.
  void SetCdfContextUsePredictedSegmentId(const Block& block,
                                          bool use_predicted_segment_id);
  void SetCdfContextCompoundType(const Block& block,
                                 bool is_explicit_compound_type,
                                 bool is_compound_type_average);
  void SetCdfContextSkipMode(const Block& block, bool skip_mode);
  void SetCdfContextPaletteSize(const Block& block);
  void SetCdfContextUVMode(const Block& block);

  // Returns the zero-based index of the super block that contains |row4x4|
  // relative to the start of this tile.
  int SuperBlockRowIndex(int row4x4) const {
    return (row4x4 - row4x4_start_) >>
           (sequence_header_.use_128x128_superblock ? 5 : 4);
  }

  // Returns the zero-based index of the super block that contains |column4x4|
  // relative to the start of this tile.
  int SuperBlockColumnIndex(int column4x4) const {
    return (column4x4 - column4x4_start_) >>
           (sequence_header_.use_128x128_superblock ? 5 : 4);
  }

  // Returns the zero-based index of the block that starts at row4x4 or
  // column4x4 relative to the start of the superblock that contains the block.
  // This is used to index into the members of |left_context_| and
  // |top_context_|.
  int CdfContextIndex(int row_or_column4x4) const {
    return row_or_column4x4 -
           (row_or_column4x4 &
            (sequence_header_.use_128x128_superblock ? ~31 : ~15));
  }

  BlockSize SuperBlockSize() const {
    return sequence_header_.use_128x128_superblock ? kBlock128x128
                                                   : kBlock64x64;
  }
  int PlaneCount() const {
    return sequence_header_.color_config.is_monochrome ? kMaxPlanesMonochrome
                                                       : kMaxPlanes;
  }

  const int number_;
  const int row_;
  const int column_;
  const uint8_t* const data_;
  size_t size_;
  int row4x4_start_;
  int row4x4_end_;
  int column4x4_start_;
  int column4x4_end_;
  int superblock_rows_;
  int superblock_columns_;
  bool read_deltas_;
  const int8_t subsampling_x_[kMaxPlanes];
  const int8_t subsampling_y_[kMaxPlanes];

  // The dimensions (in order) are: segment_id, level_index (based on plane and
  // direction), reference_frame and mode_id.
  uint8_t deblock_filter_levels_[kMaxSegments][kFrameLfCount]
                                [kNumReferenceFrameTypes][2];

  // current_quantizer_index_ is in the range [0, 255].
  uint8_t current_quantizer_index_;
  // These two arrays (|coefficient_levels_| and |dc_categories_|) are used to
  // store the entropy context. Their dimensions are as follows: First -
  // left/top; Second - plane; Third - row4x4 (if first dimension is
  // left)/column4x4 (if first dimension is top).
  //
  // This is equivalent to the LeftLevelContext and AboveLevelContext arrays in
  // the spec. In the spec, it stores values from 0 through 63 (inclusive). The
  // stored values are used to compute the left and top contexts in
  // GetTransformAllZeroContext. In that function, we only care about the
  // following values: 0, 1, 2, 3 and >= 4. So instead of clamping to 63, we
  // clamp to 4 (i.e.) all the values greater than 4 are stored as 4.
  std::array<Array2D<uint8_t>, 2> coefficient_levels_;
  // This is equivalent to the LeftDcContext and AboveDcContext arrays in the
  // spec. In the spec, it can store 3 possible values: 0, 1 and 2 (where 1
  // means the value is < 0, 2 means the value is > 0 and 0 means the value is
  // equal to 0).
  //
  // The stored values are used in two places:
  //  * GetTransformAllZeroContext: Here, we only care about whether the
  //  value is 0 or not (whether it is 1 or 2 is irrelevant).
  //  * GetDcSignContext: Here, we do the following computation: if the
  //  stored value is 1, we decrement a counter. If the stored value is 2
  //  we increment a counter.
  //
  // Based on this usage, we can simply replace 1 with -1 and 2 with 1 and
  // use that value to compute the counter.
  //
  // The usage on GetTransformAllZeroContext is unaffected since there we
  // only care about whether it is 0 or not.
  std::array<Array2D<int8_t>, 2> dc_categories_;
  const ObuSequenceHeader& sequence_header_;
  const ObuFrameHeader& frame_header_;
  const std::array<bool, kNumReferenceFrameTypes>& reference_frame_sign_bias_;
  const std::array<RefCountedBufferPtr, kNumReferenceFrameTypes>&
      reference_frames_;
  TemporalMotionField& motion_field_;
  const std::array<uint8_t, kNumReferenceFrameTypes>& reference_order_hint_;
  const WedgeMaskArray& wedge_masks_;
  const QuantizerMatrix& quantizer_matrix_;
  EntropyDecoder reader_;
  SymbolDecoderContext symbol_decoder_context_;
  SymbolDecoderContext* const saved_symbol_decoder_context_;
  const SegmentationMap* prev_segment_ids_;
  const dsp::Dsp& dsp_;
  PostFilter& post_filter_;
  BlockParametersHolder& block_parameters_holder_;
  Quantizer quantizer_;
  // When there is no multi-threading within the Tile, |residual_buffer_| is
  // used. When there is multi-threading within the Tile,
  // |residual_buffer_threaded_| is used. In the following comment,
  // |residual_buffer| refers to either |residual_buffer_| or
  // |residual_buffer_threaded_| depending on whether multi-threading is enabled
  // within the Tile or not.
  // The |residual_buffer| is used to help with the dequantization and the
  // inverse transform processes. It is declared as a uint8_t, but is always
  // accessed either as an int16_t or int32_t depending on |bitdepth|. Here is
  // what it stores at various stages of the decoding process (in the order
  // which they happen):
  //   1) In ReadTransformCoefficients(), this buffer is used to store the
  //   dequantized values.
  //   2) In Reconstruct(), this buffer is used as the input to the row
  //   transform process.
  // The size of this buffer would be:
  //    For |residual_buffer_|: (4096 + 32 * |kResidualPaddingVertical|) *
  //        |residual_size_|. Where 4096 = 64x64 which is the maximum transform
  //        size, and 32 * |kResidualPaddingVertical| is the padding to avoid
  //        bottom boundary checks when parsing quantized coefficients. This
  //        memory is allocated and owned by the Tile class.
  //    For |residual_buffer_threaded_|: See the comment below. This memory is
  //        not allocated or owned by the Tile class.
  AlignedUniquePtr<uint8_t> residual_buffer_;
  // This is a 2d array of pointers of size |superblock_rows_| by
  // |superblock_columns_| where each pointer points to a ResidualBuffer for a
  // single super block. The array is populated when the parsing process begins
  // by calling |residual_buffer_pool_->Get()| and the memory is released back
  // to the pool by calling |residual_buffer_pool_->Release()| when the decoding
  // process is complete.
  Array2D<std::unique_ptr<ResidualBuffer>> residual_buffer_threaded_;
  // sizeof(int16_t or int32_t) depending on |bitdepth|.
  const size_t residual_size_;
  // Number of superblocks on the top-right that will have to be decoded before
  // the current superblock can be decoded. This will be 1 if allow_intrabc is
  // false. If allow_intrabc is true, then this value will be
  // use_128x128_superblock ? 3 : 5. This is the allowed range of reference for
  // the top rows for intrabc.
  const int intra_block_copy_lag_;

  // In the Tile class, we use the "current_frame" in two ways:
  //   1) To write the decoded output into (using the |buffer_| view).
  //   2) To read the pixels for intra block copy (using the |current_frame_|
  //      reference).
  //
  // When intra block copy is off, |buffer_| and |current_frame_| may or may not
  // point to the same plane pointers. But it is okay since |current_frame_| is
  // never used in this case.
  //
  // When intra block copy is on, |buffer_| and |current_frame_| always point to
  // the same plane pointers (since post filtering is disabled). So the usage in
  // both case 1 and case 2 remain valid.
  Array2DView<uint8_t> buffer_[kMaxPlanes];
  RefCountedBuffer& current_frame_;

  Array2D<int8_t>& cdef_index_;
  Array2D<uint8_t>& cdef_skip_;
  Array2D<TransformSize>& inter_transform_sizes_;
  std::array<RestorationUnitInfo, kMaxPlanes> reference_unit_info_;
  // If |thread_pool_| is nullptr, the calling thread will do the parsing and
  // the decoding in one pass. If |thread_pool_| is not nullptr, then the main
  // thread will do the parsing while the thread pool workers will do the
  // decoding.
  ThreadPool* const thread_pool_;
  ThreadingParameters threading_;
  ResidualBufferPool* const residual_buffer_pool_;
  TileScratchBufferPool* const tile_scratch_buffer_pool_;
  BlockingCounterWithStatus* const pending_tiles_;
  bool split_parse_and_decode_;
  // This is used only when |split_parse_and_decode_| is false.
  std::unique_ptr<PredictionParameters> prediction_parameters_ = nullptr;
  // Stores the |transform_type| for the super block being decoded at a 4x4
  // granularity. The spec uses absolute indices for this array but it is
  // sufficient to use indices relative to the super block being decoded.
  TransformType transform_types_[32][32];
  // delta_lf_[i] is in the range [-63, 63].
  int8_t delta_lf_[kFrameLfCount];
  // True if all the values in |delta_lf_| are zero. False otherwise.
  bool delta_lf_all_zero_;
  const bool frame_parallel_;
  const bool use_intra_prediction_buffer_;
  // Buffer used to store the unfiltered pixels that are necessary for decoding
  // the next superblock row (for the intra prediction process). Used only if
  // |use_intra_prediction_buffer_| is true. The |frame_scratch_buffer| contains
  // one row buffer for each tile row. This tile will have to use the buffer
  // corresponding to this tile's row.
  IntraPredictionBuffer* const intra_prediction_buffer_;
  // Stores the progress of the reference frames. This will be used to avoid
  // unnecessary calls into RefCountedBuffer::WaitUntil().
  std::array<int, kNumReferenceFrameTypes> reference_frame_progress_cache_;
  // Stores the CDF contexts necessary for the "left" block.
  BlockCdfContext left_context_;
  // Stores the CDF contexts necessary for the "top" block. The size of this
  // buffer is the number of superblock columns in this tile. For each block,
  // the access index will be the corresponding SuperBlockColumnIndex()'th
  // entry.
  DynamicBuffer<BlockCdfContext> top_context_;
};

struct Tile::Block {
  Block(Tile* tile_ptr, BlockSize size, int row4x4, int column4x4,
        TileScratchBuffer* const scratch_buffer, ResidualPtr* residual)
      : tile(*tile_ptr),
        size(size),
        row4x4(row4x4),
        column4x4(column4x4),
        width(kBlockWidthPixels[size]),
        height(kBlockHeightPixels[size]),
        width4x4(width >> 2),
        height4x4(height >> 2),
        scratch_buffer(scratch_buffer),
        residual(residual),
        top_context(tile.top_context_.get() +
                    tile.SuperBlockColumnIndex(column4x4)),
        top_context_index(tile.CdfContextIndex(column4x4)),
        left_context_index(tile.CdfContextIndex(row4x4)) {
    assert(size != kBlockInvalid);
    residual_size[kPlaneY] = kPlaneResidualSize[size][0][0];
    residual_size[kPlaneU] = residual_size[kPlaneV] =
        kPlaneResidualSize[size][tile.subsampling_x_[kPlaneU]]
                          [tile.subsampling_y_[kPlaneU]];
    assert(residual_size[kPlaneY] != kBlockInvalid);
    if (tile.PlaneCount() > 1) {
      assert(residual_size[kPlaneU] != kBlockInvalid);
    }
    if ((row4x4 & 1) == 0 &&
        (tile.sequence_header_.color_config.subsampling_y & height4x4) == 1) {
      has_chroma = false;
    } else if ((column4x4 & 1) == 0 &&
               (tile.sequence_header_.color_config.subsampling_x & width4x4) ==
                   1) {
      has_chroma = false;
    } else {
      has_chroma = !tile.sequence_header_.color_config.is_monochrome;
    }
    top_available[kPlaneY] = tile.IsTopInside(row4x4);
    left_available[kPlaneY] = tile.IsLeftInside(column4x4);
    if (has_chroma) {
      // top_available[kPlaneU] and top_available[kPlaneV] are valid only if
      // has_chroma is true.
      // The next 3 lines are equivalent to:
      // top_available[kPlaneU] = top_available[kPlaneV] =
      //     top_available[kPlaneY] &&
      //     ((tile.sequence_header_.color_config.subsampling_y & height4x4) ==
      //     0 || tile.IsTopInside(row4x4 - 1));
      top_available[kPlaneU] = top_available[kPlaneV] = tile.IsTopInside(
          row4x4 -
          (tile.sequence_header_.color_config.subsampling_y & height4x4));
      // left_available[kPlaneU] and left_available[kPlaneV] are valid only if
      // has_chroma is true.
      // The next 3 lines are equivalent to:
      // left_available[kPlaneU] = left_available[kPlaneV] =
      //     left_available[kPlaneY] &&
      //     ((tile.sequence_header_.color_config.subsampling_x & width4x4) == 0
      //      || tile.IsLeftInside(column4x4 - 1));
      left_available[kPlaneU] = left_available[kPlaneV] = tile.IsLeftInside(
          column4x4 -
          (tile.sequence_header_.color_config.subsampling_x & width4x4));
    }
    const ptrdiff_t stride = tile.BlockParametersStride();
    BlockParameters** const bps =
        tile.BlockParametersAddress(row4x4, column4x4);
    bp = *bps;
    // bp_top is valid only if top_available[kPlaneY] is true.
    if (top_available[kPlaneY]) {
      bp_top = *(bps - stride);
    }
    // bp_left is valid only if left_available[kPlaneY] is true.
    if (left_available[kPlaneY]) {
      bp_left = *(bps - 1);
    }
  }

  bool HasChroma() const { return has_chroma; }

  // These return values of these group of functions are valid only if the
  // corresponding top_available or left_available is true.
  ReferenceFrameType TopReference(int index) const {
    return bp_top->reference_frame[index];
  }

  ReferenceFrameType LeftReference(int index) const {
    return bp_left->reference_frame[index];
  }

  bool IsTopIntra() const { return TopReference(0) <= kReferenceFrameIntra; }
  bool IsLeftIntra() const { return LeftReference(0) <= kReferenceFrameIntra; }

  bool IsTopSingle() const { return TopReference(1) <= kReferenceFrameIntra; }
  bool IsLeftSingle() const { return LeftReference(1) <= kReferenceFrameIntra; }

  int CountReferences(ReferenceFrameType type) const {
    return static_cast<int>(top_available[kPlaneY] &&
                            bp_top->reference_frame[0] == type) +
           static_cast<int>(top_available[kPlaneY] &&
                            bp_top->reference_frame[1] == type) +
           static_cast<int>(left_available[kPlaneY] &&
                            bp_left->reference_frame[0] == type) +
           static_cast<int>(left_available[kPlaneY] &&
                            bp_left->reference_frame[1] == type);
  }

  // 7.10.3.
  // Checks if there are any inter blocks to the left or above. If so, it
  // returns true indicating that the block has neighbors that are suitable for
  // use by overlapped motion compensation.
  bool HasOverlappableCandidates() const {
    const ptrdiff_t stride = tile.BlockParametersStride();
    BlockParameters** const bps = tile.BlockParametersAddress(0, 0);
    if (top_available[kPlaneY]) {
      BlockParameters** bps_top = bps + (row4x4 - 1) * stride + (column4x4 | 1);
      const int columns = std::min(tile.frame_header_.columns4x4 - column4x4,
                                   static_cast<int>(width4x4));
      BlockParameters** const bps_top_end = bps_top + columns;
      do {
        if ((*bps_top)->reference_frame[0] > kReferenceFrameIntra) {
          return true;
        }
        bps_top += 2;
      } while (bps_top < bps_top_end);
    }
    if (left_available[kPlaneY]) {
      BlockParameters** bps_left = bps + (row4x4 | 1) * stride + column4x4 - 1;
      const int rows = std::min(tile.frame_header_.rows4x4 - row4x4,
                                static_cast<int>(height4x4));
      BlockParameters** const bps_left_end = bps_left + rows * stride;
      do {
        if ((*bps_left)->reference_frame[0] > kReferenceFrameIntra) {
          return true;
        }
        bps_left += 2 * stride;
      } while (bps_left < bps_left_end);
    }
    return false;
  }

  Tile& tile;
  bool has_chroma;
  const BlockSize size;
  bool top_available[kMaxPlanes];
  bool left_available[kMaxPlanes];
  BlockSize residual_size[kMaxPlanes];
  const int row4x4;
  const int column4x4;
  const int width;
  const int height;
  const int width4x4;
  const int height4x4;
  const BlockParameters* bp_top;
  const BlockParameters* bp_left;
  BlockParameters* bp;
  TileScratchBuffer* const scratch_buffer;
  ResidualPtr* const residual;
  BlockCdfContext* const top_context;
  const int top_context_index;
  const int left_context_index;
};

extern template bool
Tile::ProcessSuperBlockRow<kProcessingModeDecodeOnly, false>(
    int row4x4, TileScratchBuffer* scratch_buffer);
extern template bool
Tile::ProcessSuperBlockRow<kProcessingModeParseAndDecode, true>(
    int row4x4, TileScratchBuffer* scratch_buffer);

}  // namespace libgav1

#endif  // LIBGAV1_SRC_TILE_H_