• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2019 The libgav1 Authors
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 //      http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14 
15 #include "src/post_filter.h"
16 
17 #include <algorithm>
18 #include <atomic>
19 #include <cassert>
20 #include <cstddef>
21 #include <cstdint>
22 #include <cstring>
23 
24 #include "src/dsp/constants.h"
25 #include "src/dsp/dsp.h"
26 #include "src/utils/array_2d.h"
27 #include "src/utils/blocking_counter.h"
28 #include "src/utils/common.h"
29 #include "src/utils/constants.h"
30 #include "src/utils/memory.h"
31 #include "src/utils/types.h"
32 
33 namespace libgav1 {
34 namespace {
35 
36 // Import all the constants in the anonymous namespace.
37 #include "src/post_filter/deblock_thresholds.inc"
38 
39 // Row indices of loop restoration border. This is used to populate the
40 // |loop_restoration_border_| when either cdef is on or multithreading is
41 // enabled. The dimension is subsampling_y.
42 constexpr int kLoopRestorationBorderRows[2] = {54, 26};
43 
44 }  // namespace
45 
46 // The following example illustrates how ExtendFrame() extends a frame.
47 // Suppose the frame width is 8 and height is 4, and left, right, top, and
48 // bottom are all equal to 3.
49 //
50 // Before:
51 //
52 //       ABCDEFGH
53 //       IJKLMNOP
54 //       QRSTUVWX
55 //       YZabcdef
56 //
57 // After:
58 //
59 //   AAA|ABCDEFGH|HHH  [3]
60 //   AAA|ABCDEFGH|HHH
61 //   AAA|ABCDEFGH|HHH
62 //   ---+--------+---
63 //   AAA|ABCDEFGH|HHH  [1]
64 //   III|IJKLMNOP|PPP
65 //   QQQ|QRSTUVWX|XXX
66 //   YYY|YZabcdef|fff
67 //   ---+--------+---
68 //   YYY|YZabcdef|fff  [2]
69 //   YYY|YZabcdef|fff
70 //   YYY|YZabcdef|fff
71 //
72 // ExtendFrame() first extends the rows to the left and to the right[1]. Then
73 // it copies the extended last row to the bottom borders[2]. Finally it copies
74 // the extended first row to the top borders[3].
75 // static
76 template <typename Pixel>
ExtendFrame(Pixel * const frame_start,const int width,const int height,const ptrdiff_t stride,const int left,const int right,const int top,const int bottom)77 void PostFilter::ExtendFrame(Pixel* const frame_start, const int width,
78                              const int height, const ptrdiff_t stride,
79                              const int left, const int right, const int top,
80                              const int bottom) {
81   Pixel* src = frame_start;
82   // Copy to left and right borders.
83   int y = height;
84   do {
85     ExtendLine<Pixel>(src, width, left, right);
86     src += stride;
87   } while (--y != 0);
88   // Copy to bottom borders. For performance we copy |stride| pixels
89   // (including some padding pixels potentially) in each row, ending at the
90   // bottom right border pixel. In the diagram the asterisks indicate padding
91   // pixels.
92   //
93   // |<--- stride --->|
94   // **YYY|YZabcdef|fff <-- Copy from the extended last row.
95   // -----+--------+---
96   // **YYY|YZabcdef|fff
97   // **YYY|YZabcdef|fff
98   // **YYY|YZabcdef|fff <-- bottom right border pixel
99   assert(src == frame_start + height * stride);
100   Pixel* dst = src - left;
101   src = dst - stride;
102   for (int y = 0; y < bottom; ++y) {
103     memcpy(dst, src, sizeof(Pixel) * stride);
104     dst += stride;
105   }
106   // Copy to top borders. For performance we copy |stride| pixels (including
107   // some padding pixels potentially) in each row, starting from the top left
108   // border pixel. In the diagram the asterisks indicate padding pixels.
109   //
110   // +-- top left border pixel
111   // |
112   // v
113   // AAA|ABCDEFGH|HHH**
114   // AAA|ABCDEFGH|HHH**
115   // AAA|ABCDEFGH|HHH**
116   // ---+--------+-----
117   // AAA|ABCDEFGH|HHH** <-- Copy from the extended first row.
118   // |<--- stride --->|
119   src = frame_start - left;
120   dst = frame_start - left - top * stride;
121   for (int y = 0; y < top; ++y) {
122     memcpy(dst, src, sizeof(Pixel) * stride);
123     dst += stride;
124   }
125 }
126 
127 template void PostFilter::ExtendFrame<uint8_t>(uint8_t* const frame_start,
128                                                const int width,
129                                                const int height,
130                                                const ptrdiff_t stride,
131                                                const int left, const int right,
132                                                const int top, const int bottom);
133 
134 #if LIBGAV1_MAX_BITDEPTH >= 10
135 template void PostFilter::ExtendFrame<uint16_t>(
136     uint16_t* const frame_start, const int width, const int height,
137     const ptrdiff_t stride, const int left, const int right, const int top,
138     const int bottom);
139 #endif
140 
PostFilter(const ObuFrameHeader & frame_header,const ObuSequenceHeader & sequence_header,FrameScratchBuffer * const frame_scratch_buffer,YuvBuffer * const frame_buffer,const dsp::Dsp * dsp,int do_post_filter_mask)141 PostFilter::PostFilter(const ObuFrameHeader& frame_header,
142                        const ObuSequenceHeader& sequence_header,
143                        FrameScratchBuffer* const frame_scratch_buffer,
144                        YuvBuffer* const frame_buffer, const dsp::Dsp* dsp,
145                        int do_post_filter_mask)
146     : frame_header_(frame_header),
147       loop_restoration_(frame_header.loop_restoration),
148       dsp_(*dsp),
149       // Deblocking filter always uses 64x64 as step size.
150       num_64x64_blocks_per_row_(DivideBy64(frame_header.width + 63)),
151       upscaled_width_(frame_header.upscaled_width),
152       width_(frame_header.width),
153       height_(frame_header.height),
154       bitdepth_(sequence_header.color_config.bitdepth),
155       subsampling_x_{0, sequence_header.color_config.subsampling_x,
156                      sequence_header.color_config.subsampling_x},
157       subsampling_y_{0, sequence_header.color_config.subsampling_y,
158                      sequence_header.color_config.subsampling_y},
159       planes_(sequence_header.color_config.is_monochrome ? kMaxPlanesMonochrome
160                                                          : kMaxPlanes),
161       pixel_size_log2_(static_cast<int>((bitdepth_ == 8) ? sizeof(uint8_t)
162                                                          : sizeof(uint16_t)) -
163                        1),
164       inner_thresh_(kInnerThresh[frame_header.loop_filter.sharpness]),
165       outer_thresh_(kOuterThresh[frame_header.loop_filter.sharpness]),
166       needs_chroma_deblock_(frame_header.loop_filter.level[kPlaneU + 1] != 0 ||
167                             frame_header.loop_filter.level[kPlaneV + 1] != 0),
168       cdef_index_(frame_scratch_buffer->cdef_index),
169       inter_transform_sizes_(frame_scratch_buffer->inter_transform_sizes),
170       restoration_info_(&frame_scratch_buffer->loop_restoration_info),
171       superres_coefficients_{
172           frame_scratch_buffer->superres_coefficients[kPlaneTypeY].get(),
173           frame_scratch_buffer
174               ->superres_coefficients
175                   [(sequence_header.color_config.is_monochrome ||
176                     sequence_header.color_config.subsampling_x == 0)
177                        ? kPlaneTypeY
178                        : kPlaneTypeUV]
179               .get()},
180       superres_line_buffer_(frame_scratch_buffer->superres_line_buffer),
181       block_parameters_(frame_scratch_buffer->block_parameters_holder),
182       frame_buffer_(*frame_buffer),
183       cdef_border_(frame_scratch_buffer->cdef_border),
184       loop_restoration_border_(frame_scratch_buffer->loop_restoration_border),
185       do_post_filter_mask_(do_post_filter_mask),
186       thread_pool_(
187           frame_scratch_buffer->threading_strategy.post_filter_thread_pool()) {
188   const int8_t zero_delta_lf[kFrameLfCount] = {};
189   ComputeDeblockFilterLevels(zero_delta_lf, deblock_filter_levels_);
190   if (DoSuperRes()) {
191     int plane = kPlaneY;
192     do {
193       const int downscaled_width =
194           SubsampledValue(width_, subsampling_x_[plane]);
195       const int upscaled_width =
196           SubsampledValue(upscaled_width_, subsampling_x_[plane]);
197       const int superres_width = downscaled_width << kSuperResScaleBits;
198       super_res_info_[plane].step =
199           (superres_width + upscaled_width / 2) / upscaled_width;
200       const int error =
201           super_res_info_[plane].step * upscaled_width - superres_width;
202       super_res_info_[plane].initial_subpixel_x =
203           ((-((upscaled_width - downscaled_width) << (kSuperResScaleBits - 1)) +
204             DivideBy2(upscaled_width)) /
205                upscaled_width +
206            (1 << (kSuperResExtraBits - 1)) - error / 2) &
207           kSuperResScaleMask;
208       super_res_info_[plane].upscaled_width = upscaled_width;
209     } while (++plane < planes_);
210     if (dsp->super_res_coefficients != nullptr) {
211       int plane = kPlaneY;
212       const int number_loops = (superres_coefficients_[kPlaneTypeY] ==
213                                 superres_coefficients_[kPlaneTypeUV])
214                                    ? kMaxPlanesMonochrome
215                                    : static_cast<int>(kNumPlaneTypes);
216       do {
217         dsp->super_res_coefficients(
218             SubsampledValue(upscaled_width_, subsampling_x_[plane]),
219             super_res_info_[plane].initial_subpixel_x,
220             super_res_info_[plane].step, superres_coefficients_[plane]);
221       } while (++plane < number_loops);
222     }
223   }
224   int plane = kPlaneY;
225   do {
226     loop_restoration_buffer_[plane] = frame_buffer_.data(plane);
227     cdef_buffer_[plane] = frame_buffer_.data(plane);
228     superres_buffer_[plane] = frame_buffer_.data(plane);
229     source_buffer_[plane] = frame_buffer_.data(plane);
230   } while (++plane < planes_);
231   if (DoCdef() || DoRestoration() || DoSuperRes()) {
232     plane = kPlaneY;
233     const int pixel_size_log2 = pixel_size_log2_;
234     do {
235       int horizontal_shift = 0;
236       int vertical_shift = 0;
237       if (DoRestoration() &&
238           loop_restoration_.type[plane] != kLoopRestorationTypeNone) {
239         horizontal_shift += frame_buffer_.alignment();
240         if (!DoCdef() && thread_pool_ == nullptr) {
241           vertical_shift += kRestorationVerticalBorder;
242         }
243         superres_buffer_[plane] +=
244             vertical_shift * frame_buffer_.stride(plane) +
245             (horizontal_shift << pixel_size_log2);
246       }
247       if (DoSuperRes()) {
248         vertical_shift += kSuperResVerticalBorder;
249       }
250       cdef_buffer_[plane] += vertical_shift * frame_buffer_.stride(plane) +
251                              (horizontal_shift << pixel_size_log2);
252       if (DoCdef() && thread_pool_ == nullptr) {
253         horizontal_shift += frame_buffer_.alignment();
254         vertical_shift += kCdefBorder;
255       }
256       assert(horizontal_shift <= frame_buffer_.right_border(plane));
257       assert(vertical_shift <= frame_buffer_.bottom_border(plane));
258       source_buffer_[plane] += vertical_shift * frame_buffer_.stride(plane) +
259                                (horizontal_shift << pixel_size_log2);
260     } while (++plane < planes_);
261   }
262 }
263 
ExtendFrameBoundary(uint8_t * const frame_start,const int width,const int height,const ptrdiff_t stride,const int left,const int right,const int top,const int bottom) const264 void PostFilter::ExtendFrameBoundary(uint8_t* const frame_start,
265                                      const int width, const int height,
266                                      const ptrdiff_t stride, const int left,
267                                      const int right, const int top,
268                                      const int bottom) const {
269 #if LIBGAV1_MAX_BITDEPTH >= 10
270   if (bitdepth_ >= 10) {
271     ExtendFrame<uint16_t>(reinterpret_cast<uint16_t*>(frame_start), width,
272                           height, stride / sizeof(uint16_t), left, right, top,
273                           bottom);
274     return;
275   }
276 #endif
277   ExtendFrame<uint8_t>(frame_start, width, height, stride, left, right, top,
278                        bottom);
279 }
280 
ExtendBordersForReferenceFrame()281 void PostFilter::ExtendBordersForReferenceFrame() {
282   if (frame_header_.refresh_frame_flags == 0) return;
283   int plane = kPlaneY;
284   do {
285     const int plane_width =
286         SubsampledValue(upscaled_width_, subsampling_x_[plane]);
287     const int plane_height = SubsampledValue(height_, subsampling_y_[plane]);
288     assert(frame_buffer_.left_border(plane) >= kMinLeftBorderPixels &&
289            frame_buffer_.right_border(plane) >= kMinRightBorderPixels &&
290            frame_buffer_.top_border(plane) >= kMinTopBorderPixels &&
291            frame_buffer_.bottom_border(plane) >= kMinBottomBorderPixels);
292     // plane subsampling_x_ left_border
293     //   Y        N/A         64, 48
294     //  U,V        0          64, 48
295     //  U,V        1          32, 16
296     assert(frame_buffer_.left_border(plane) >= 16);
297     // The |left| argument to ExtendFrameBoundary() must be at least
298     // kMinLeftBorderPixels (13) for warp.
299     static_assert(16 >= kMinLeftBorderPixels, "");
300     ExtendFrameBoundary(
301         frame_buffer_.data(plane), plane_width, plane_height,
302         frame_buffer_.stride(plane), frame_buffer_.left_border(plane),
303         frame_buffer_.right_border(plane), frame_buffer_.top_border(plane),
304         frame_buffer_.bottom_border(plane));
305   } while (++plane < planes_);
306 }
307 
CopyDeblockedPixels(Plane plane,int row4x4)308 void PostFilter::CopyDeblockedPixels(Plane plane, int row4x4) {
309   const ptrdiff_t src_stride = frame_buffer_.stride(plane);
310   const uint8_t* const src = GetSourceBuffer(plane, row4x4, 0);
311   const int row_offset = DivideBy4(row4x4);
312   const ptrdiff_t dst_stride = loop_restoration_border_.stride(plane);
313   uint8_t* dst = loop_restoration_border_.data(plane) + row_offset * dst_stride;
314   const int num_pixels = SubsampledValue(MultiplyBy4(frame_header_.columns4x4),
315                                          subsampling_x_[plane]);
316   const int row_width = num_pixels << pixel_size_log2_;
317   int last_valid_row = -1;
318   const int plane_height =
319       SubsampledValue(frame_header_.height, subsampling_y_[plane]);
320   int row = kLoopRestorationBorderRows[subsampling_y_[plane]];
321   const int absolute_row = (MultiplyBy4(row4x4) >> subsampling_y_[plane]) + row;
322   for (int i = 0; i < 4; ++i, ++row) {
323     if (absolute_row + i >= plane_height) {
324       if (last_valid_row == -1) break;
325       // If we run out of rows, copy the last valid row (mimics the bottom
326       // border extension).
327       row = last_valid_row;
328     }
329     memcpy(dst, src + row * src_stride, row_width);
330     last_valid_row = row;
331     dst += dst_stride;
332   }
333 }
334 
CopyBordersForOneSuperBlockRow(int row4x4,int sb4x4,bool for_loop_restoration)335 void PostFilter::CopyBordersForOneSuperBlockRow(int row4x4, int sb4x4,
336                                                 bool for_loop_restoration) {
337   // Number of rows to be subtracted from the start position described by
338   // row4x4. We always lag by 8 rows (to account for in-loop post filters).
339   const int row_offset = (row4x4 == 0) ? 0 : 8;
340   // Number of rows to be subtracted from the height described by sb4x4.
341   const int height_offset = (row4x4 == 0) ? 8 : 0;
342   // If cdef is off and post filter multithreading is off, then loop restoration
343   // needs 2 extra rows for the bottom border in each plane.
344   const int extra_rows =
345       (for_loop_restoration && thread_pool_ == nullptr && !DoCdef()) ? 2 : 0;
346   int plane = kPlaneY;
347   do {
348     const int plane_width =
349         SubsampledValue(upscaled_width_, subsampling_x_[plane]);
350     const int plane_height = SubsampledValue(height_, subsampling_y_[plane]);
351     const int row = (MultiplyBy4(row4x4) - row_offset) >> subsampling_y_[plane];
352     assert(row >= 0);
353     if (row >= plane_height) break;
354     const int num_rows =
355         std::min(SubsampledValue(MultiplyBy4(sb4x4) - height_offset,
356                                  subsampling_y_[plane]) +
357                      extra_rows,
358                  plane_height - row);
359     // We only need to track the progress of the Y plane since the progress of
360     // the U and V planes will be inferred from the progress of the Y plane.
361     if (!for_loop_restoration && plane == kPlaneY) {
362       progress_row_ = row + num_rows;
363     }
364     const bool copy_bottom = row + num_rows == plane_height;
365     const int stride = frame_buffer_.stride(plane);
366     uint8_t* const start = (for_loop_restoration ? superres_buffer_[plane]
367                                                  : frame_buffer_.data(plane)) +
368                            row * stride;
369     const int left_border = for_loop_restoration
370                                 ? kRestorationHorizontalBorder
371                                 : frame_buffer_.left_border(plane);
372     const int right_border = for_loop_restoration
373                                  ? kRestorationHorizontalBorder
374                                  : frame_buffer_.right_border(plane);
375     const int top_border =
376         (row == 0) ? (for_loop_restoration ? kRestorationVerticalBorder
377                                            : frame_buffer_.top_border(plane))
378                    : 0;
379     const int bottom_border =
380         copy_bottom
381             ? (for_loop_restoration ? kRestorationVerticalBorder
382                                     : frame_buffer_.bottom_border(plane))
383             : 0;
384     ExtendFrameBoundary(start, plane_width, num_rows, stride, left_border,
385                         right_border, top_border, bottom_border);
386   } while (++plane < planes_);
387 }
388 
SetupLoopRestorationBorder(const int row4x4)389 void PostFilter::SetupLoopRestorationBorder(const int row4x4) {
390   assert(row4x4 >= 0);
391   assert(!DoCdef());
392   assert(DoRestoration());
393   int plane = kPlaneY;
394   do {
395     if (loop_restoration_.type[plane] == kLoopRestorationTypeNone) {
396       continue;
397     }
398     const int row_offset = DivideBy4(row4x4);
399     const int num_pixels =
400         SubsampledValue(upscaled_width_, subsampling_x_[plane]);
401     const int row_width = num_pixels << pixel_size_log2_;
402     const int plane_height = SubsampledValue(height_, subsampling_y_[plane]);
403     const int row = kLoopRestorationBorderRows[subsampling_y_[plane]];
404     const int absolute_row =
405         (MultiplyBy4(row4x4) >> subsampling_y_[plane]) + row;
406     const ptrdiff_t src_stride = frame_buffer_.stride(plane);
407     const uint8_t* src =
408         GetSuperResBuffer(static_cast<Plane>(plane), row4x4, 0) +
409         row * src_stride;
410     const ptrdiff_t dst_stride = loop_restoration_border_.stride(plane);
411     uint8_t* dst =
412         loop_restoration_border_.data(plane) + row_offset * dst_stride;
413     for (int i = 0; i < 4; ++i) {
414       memcpy(dst, src, row_width);
415 #if LIBGAV1_MAX_BITDEPTH >= 10
416       if (bitdepth_ >= 10) {
417         ExtendLine<uint16_t>(dst, num_pixels, kRestorationHorizontalBorder,
418                              kRestorationHorizontalBorder);
419       } else  // NOLINT.
420 #endif
421         ExtendLine<uint8_t>(dst, num_pixels, kRestorationHorizontalBorder,
422                             kRestorationHorizontalBorder);
423       // If we run out of rows, copy the last valid row (mimics the bottom
424       // border extension).
425       if (absolute_row + i < plane_height - 1) src += src_stride;
426       dst += dst_stride;
427     }
428   } while (++plane < planes_);
429 }
430 
SetupLoopRestorationBorder(int row4x4_start,int sb4x4)431 void PostFilter::SetupLoopRestorationBorder(int row4x4_start, int sb4x4) {
432   assert(row4x4_start >= 0);
433   assert(DoCdef());
434   assert(DoRestoration());
435   for (int sb_y = 0; sb_y < sb4x4; sb_y += 16) {
436     const int row4x4 = row4x4_start + sb_y;
437     const int row_offset_start = DivideBy4(row4x4);
438     const std::array<uint8_t*, kMaxPlanes> dst = {
439         loop_restoration_border_.data(kPlaneY) +
440             row_offset_start * loop_restoration_border_.stride(kPlaneY),
441         loop_restoration_border_.data(kPlaneU) +
442             row_offset_start * loop_restoration_border_.stride(kPlaneU),
443         loop_restoration_border_.data(kPlaneV) +
444             row_offset_start * loop_restoration_border_.stride(kPlaneV)};
445     // If SuperRes is enabled, then we apply SuperRes for the rows to be copied
446     // directly with |loop_restoration_border_| as the destination. Otherwise,
447     // we simply copy the rows.
448     if (DoSuperRes()) {
449       std::array<uint8_t*, kMaxPlanes> src;
450       std::array<int, kMaxPlanes> rows;
451       int plane = kPlaneY;
452       do {
453         if (loop_restoration_.type[plane] == kLoopRestorationTypeNone) {
454           rows[plane] = 0;
455           continue;
456         }
457         const int plane_height =
458             SubsampledValue(frame_header_.height, subsampling_y_[plane]);
459         const int row = kLoopRestorationBorderRows[subsampling_y_[plane]];
460         const int absolute_row =
461             (MultiplyBy4(row4x4) >> subsampling_y_[plane]) + row;
462         src[plane] = GetSourceBuffer(static_cast<Plane>(plane), row4x4, 0) +
463                      row * frame_buffer_.stride(plane);
464         rows[plane] = Clip3(plane_height - absolute_row, 0, 4);
465       } while (++plane < planes_);
466       ApplySuperRes(src, rows, /*line_buffer_row=*/-1, dst,
467                     /*dst_is_loop_restoration_border=*/true);
468       // If we run out of rows, copy the last valid row (mimics the bottom
469       // border extension).
470       plane = kPlaneY;
471       do {
472         if (rows[plane] == 0 || rows[plane] >= 4) continue;
473         const ptrdiff_t stride = loop_restoration_border_.stride(plane);
474         uint8_t* dst_line = dst[plane] + rows[plane] * stride;
475         const uint8_t* const src_line = dst_line - stride;
476         const int upscaled_width = super_res_info_[plane].upscaled_width
477                                    << pixel_size_log2_;
478         for (int i = rows[plane]; i < 4; ++i) {
479           memcpy(dst_line, src_line, upscaled_width);
480           dst_line += stride;
481         }
482       } while (++plane < planes_);
483     } else {
484       int plane = kPlaneY;
485       do {
486         CopyDeblockedPixels(static_cast<Plane>(plane), row4x4);
487       } while (++plane < planes_);
488     }
489     // Extend the left and right boundaries needed for loop restoration.
490     int plane = kPlaneY;
491     do {
492       if (loop_restoration_.type[plane] == kLoopRestorationTypeNone) {
493         continue;
494       }
495       uint8_t* dst_line = dst[plane];
496       const int plane_width =
497           SubsampledValue(upscaled_width_, subsampling_x_[plane]);
498       for (int i = 0; i < 4; ++i) {
499 #if LIBGAV1_MAX_BITDEPTH >= 10
500         if (bitdepth_ >= 10) {
501           ExtendLine<uint16_t>(dst_line, plane_width,
502                                kRestorationHorizontalBorder,
503                                kRestorationHorizontalBorder);
504         } else  // NOLINT.
505 #endif
506         {
507           ExtendLine<uint8_t>(dst_line, plane_width,
508                               kRestorationHorizontalBorder,
509                               kRestorationHorizontalBorder);
510         }
511         dst_line += loop_restoration_border_.stride(plane);
512       }
513     } while (++plane < planes_);
514   }
515 }
516 
RunJobs(WorkerFunction worker)517 void PostFilter::RunJobs(WorkerFunction worker) {
518   std::atomic<int> row4x4(0);
519   const int num_workers = thread_pool_->num_threads();
520   BlockingCounter pending_workers(num_workers);
521   for (int i = 0; i < num_workers; ++i) {
522     thread_pool_->Schedule([this, &row4x4, &pending_workers, worker]() {
523       (this->*worker)(&row4x4);
524       pending_workers.Decrement();
525     });
526   }
527   // Run the jobs on the current thread.
528   (this->*worker)(&row4x4);
529   // Wait for the threadpool jobs to finish.
530   pending_workers.Wait();
531 }
532 
ApplyFilteringThreaded()533 void PostFilter::ApplyFilteringThreaded() {
534   if (DoDeblock()) {
535     RunJobs(&PostFilter::DeblockFilterWorker<kLoopFilterTypeVertical>);
536     RunJobs(&PostFilter::DeblockFilterWorker<kLoopFilterTypeHorizontal>);
537   }
538   if (DoCdef() && DoRestoration()) {
539     for (int row4x4 = 0; row4x4 < frame_header_.rows4x4;
540          row4x4 += kNum4x4InLoopFilterUnit) {
541       SetupLoopRestorationBorder(row4x4, kNum4x4InLoopFilterUnit);
542     }
543   }
544   if (DoCdef()) {
545     for (int row4x4 = 0; row4x4 < frame_header_.rows4x4;
546          row4x4 += kNum4x4InLoopFilterUnit) {
547       SetupCdefBorder(row4x4);
548     }
549     RunJobs(&PostFilter::ApplyCdefWorker);
550   }
551   if (DoSuperRes()) ApplySuperResThreaded();
552   if (DoRestoration()) {
553     if (!DoCdef()) {
554       int row4x4 = 0;
555       do {
556         SetupLoopRestorationBorder(row4x4);
557         row4x4 += kNum4x4InLoopFilterUnit;
558       } while (row4x4 < frame_header_.rows4x4);
559     }
560     RunJobs(&PostFilter::ApplyLoopRestorationWorker);
561   }
562   ExtendBordersForReferenceFrame();
563 }
564 
ApplyFilteringForOneSuperBlockRow(int row4x4,int sb4x4,bool is_last_row,bool do_deblock)565 int PostFilter::ApplyFilteringForOneSuperBlockRow(int row4x4, int sb4x4,
566                                                   bool is_last_row,
567                                                   bool do_deblock) {
568   if (row4x4 < 0) return -1;
569   if (DoDeblock() && do_deblock) {
570     ApplyDeblockFilterForOneSuperBlockRow(row4x4, sb4x4);
571   }
572   if (DoRestoration() && DoCdef()) {
573     SetupLoopRestorationBorder(row4x4, sb4x4);
574   }
575   if (DoCdef()) {
576     ApplyCdefForOneSuperBlockRow(row4x4, sb4x4, is_last_row);
577   }
578   if (DoSuperRes()) {
579     ApplySuperResForOneSuperBlockRow(row4x4, sb4x4, is_last_row);
580   }
581   if (DoRestoration()) {
582     CopyBordersForOneSuperBlockRow(row4x4, sb4x4, true);
583     ApplyLoopRestoration(row4x4, sb4x4);
584     if (is_last_row) {
585       // Loop restoration operates with a lag of 8 rows. So make sure to cover
586       // all the rows of the last superblock row.
587       CopyBordersForOneSuperBlockRow(row4x4 + sb4x4, 16, true);
588       ApplyLoopRestoration(row4x4 + sb4x4, 16);
589     }
590   }
591   if (frame_header_.refresh_frame_flags != 0 && DoBorderExtensionInLoop()) {
592     CopyBordersForOneSuperBlockRow(row4x4, sb4x4, false);
593     if (is_last_row) {
594       CopyBordersForOneSuperBlockRow(row4x4 + sb4x4, 16, false);
595     }
596   }
597   if (is_last_row && !DoBorderExtensionInLoop()) {
598     ExtendBordersForReferenceFrame();
599   }
600   return is_last_row ? height_ : progress_row_;
601 }
602 
603 }  // namespace libgav1
604