1 // Copyright 2020 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 <algorithm>
16 #include <atomic>
17
18 #include "src/post_filter.h"
19 #include "src/utils/blocking_counter.h"
20
21 namespace libgav1 {
22
23 template <typename Pixel>
ApplyLoopRestorationForOneRow(const Pixel * src_buffer,const ptrdiff_t stride,const Plane plane,const int plane_height,const int plane_width,const int unit_y,const int unit_row,const int current_process_unit_height,const int plane_unit_size,Pixel * dst_buffer)24 void PostFilter::ApplyLoopRestorationForOneRow(
25 const Pixel* src_buffer, const ptrdiff_t stride, const Plane plane,
26 const int plane_height, const int plane_width, const int unit_y,
27 const int unit_row, const int current_process_unit_height,
28 const int plane_unit_size, Pixel* dst_buffer) {
29 const int num_horizontal_units =
30 restoration_info_->num_horizontal_units(static_cast<Plane>(plane));
31 const RestorationUnitInfo* const restoration_info =
32 restoration_info_->loop_restoration_info(static_cast<Plane>(plane),
33 unit_row * num_horizontal_units);
34 const bool in_place = DoCdef() || thread_pool_ != nullptr;
35 const Pixel* border = nullptr;
36 ptrdiff_t border_stride = 0;
37 src_buffer += unit_y * stride;
38 if (in_place) {
39 const int border_unit_y = std::max(
40 RightShiftWithCeiling(unit_y, 4 - subsampling_y_[plane]) - 4, 0);
41 border_stride = loop_restoration_border_.stride(plane) / sizeof(Pixel);
42 border =
43 reinterpret_cast<const Pixel*>(loop_restoration_border_.data(plane)) +
44 border_unit_y * border_stride;
45 }
46 int unit_column = 0;
47 int column = 0;
48 do {
49 const int current_process_unit_width =
50 std::min(plane_unit_size, plane_width - column);
51 const Pixel* src = src_buffer + column;
52 unit_column = std::min(unit_column, num_horizontal_units - 1);
53 if (restoration_info[unit_column].type == kLoopRestorationTypeNone) {
54 Pixel* dst = dst_buffer + column;
55 if (in_place) {
56 int k = current_process_unit_height;
57 do {
58 memmove(dst, src, current_process_unit_width * sizeof(Pixel));
59 src += stride;
60 dst += stride;
61 } while (--k != 0);
62 } else {
63 CopyPlane(src, stride, current_process_unit_width,
64 current_process_unit_height, dst, stride);
65 }
66 } else {
67 const Pixel* top_border = src - kRestorationVerticalBorder * stride;
68 ptrdiff_t top_border_stride = stride;
69 const Pixel* bottom_border = src + current_process_unit_height * stride;
70 ptrdiff_t bottom_border_stride = stride;
71 const bool frame_bottom_border =
72 (unit_y + current_process_unit_height >= plane_height);
73 if (in_place && (unit_y != 0 || !frame_bottom_border)) {
74 const Pixel* loop_restoration_border = border + column;
75 if (unit_y != 0) {
76 top_border = loop_restoration_border;
77 top_border_stride = border_stride;
78 loop_restoration_border += 4 * border_stride;
79 }
80 if (!frame_bottom_border) {
81 bottom_border = loop_restoration_border +
82 kRestorationVerticalBorder * border_stride;
83 bottom_border_stride = border_stride;
84 }
85 }
86 #if LIBGAV1_MSAN
87 // The optimized loop filter may read past initialized values within the
88 // buffer.
89 RestorationBuffer restoration_buffer = {};
90 #else
91 RestorationBuffer restoration_buffer;
92 #endif
93 const LoopRestorationType type = restoration_info[unit_column].type;
94 assert(type == kLoopRestorationTypeSgrProj ||
95 type == kLoopRestorationTypeWiener);
96 const dsp::LoopRestorationFunc restoration_func =
97 dsp_.loop_restorations[type - 2];
98 restoration_func(restoration_info[unit_column], src, stride, top_border,
99 top_border_stride, bottom_border, bottom_border_stride,
100 current_process_unit_width, current_process_unit_height,
101 &restoration_buffer, dst_buffer + column);
102 }
103 ++unit_column;
104 column += plane_unit_size;
105 } while (column < plane_width);
106 }
107
108 template <typename Pixel>
ApplyLoopRestorationForOneSuperBlockRow(const int row4x4_start,const int sb4x4)109 void PostFilter::ApplyLoopRestorationForOneSuperBlockRow(const int row4x4_start,
110 const int sb4x4) {
111 assert(row4x4_start >= 0);
112 assert(DoRestoration());
113 int plane = kPlaneY;
114 const int upscaled_width = frame_header_.upscaled_width;
115 const int height = frame_header_.height;
116 do {
117 if (loop_restoration_.type[plane] == kLoopRestorationTypeNone) {
118 continue;
119 }
120 const ptrdiff_t stride = frame_buffer_.stride(plane) / sizeof(Pixel);
121 const int unit_height_offset =
122 kRestorationUnitOffset >> subsampling_y_[plane];
123 const int plane_height = SubsampledValue(height, subsampling_y_[plane]);
124 const int plane_width =
125 SubsampledValue(upscaled_width, subsampling_x_[plane]);
126 const int plane_unit_size = 1 << loop_restoration_.unit_size_log2[plane];
127 const int plane_process_unit_height =
128 kRestorationUnitHeight >> subsampling_y_[plane];
129 int y = (row4x4_start == 0)
130 ? 0
131 : (MultiplyBy4(row4x4_start) >> subsampling_y_[plane]) -
132 unit_height_offset;
133 int expected_height = plane_process_unit_height -
134 ((row4x4_start == 0) ? unit_height_offset : 0);
135 int current_process_unit_height;
136 for (int sb_y = 0; sb_y < sb4x4;
137 sb_y += 16, y += current_process_unit_height) {
138 if (y >= plane_height) break;
139 const int unit_row = std::min(
140 (y + unit_height_offset) >> loop_restoration_.unit_size_log2[plane],
141 restoration_info_->num_vertical_units(static_cast<Plane>(plane)) - 1);
142 current_process_unit_height = std::min(expected_height, plane_height - y);
143 expected_height = plane_process_unit_height;
144 ApplyLoopRestorationForOneRow<Pixel>(
145 reinterpret_cast<Pixel*>(superres_buffer_[plane]), stride,
146 static_cast<Plane>(plane), plane_height, plane_width, y, unit_row,
147 current_process_unit_height, plane_unit_size,
148 reinterpret_cast<Pixel*>(loop_restoration_buffer_[plane]) +
149 y * stride);
150 }
151 } while (++plane < planes_);
152 }
153
ApplyLoopRestoration(const int row4x4_start,const int sb4x4)154 void PostFilter::ApplyLoopRestoration(const int row4x4_start, const int sb4x4) {
155 #if LIBGAV1_MAX_BITDEPTH >= 10
156 if (bitdepth_ >= 10) {
157 ApplyLoopRestorationForOneSuperBlockRow<uint16_t>(row4x4_start, sb4x4);
158 return;
159 }
160 #endif
161 ApplyLoopRestorationForOneSuperBlockRow<uint8_t>(row4x4_start, sb4x4);
162 }
163
ApplyLoopRestorationWorker(std::atomic<int> * row4x4_atomic)164 void PostFilter::ApplyLoopRestorationWorker(std::atomic<int>* row4x4_atomic) {
165 int row4x4;
166 // Loop Restoration operates with a lag of 8 rows (4 for chroma with
167 // subsampling) and hence we need to make sure to cover the last 8 rows of the
168 // last superblock row. So we run this loop for an extra iteration to
169 // accomplish that.
170 const int row4x4_end = frame_header_.rows4x4 + kNum4x4InLoopRestorationUnit;
171 while ((row4x4 = row4x4_atomic->fetch_add(kNum4x4InLoopRestorationUnit,
172 std::memory_order_relaxed)) <
173 row4x4_end) {
174 CopyBordersForOneSuperBlockRow(row4x4, kNum4x4InLoopRestorationUnit,
175 /*for_loop_restoration=*/true);
176 #if LIBGAV1_MAX_BITDEPTH >= 10
177 if (bitdepth_ >= 10) {
178 ApplyLoopRestorationForOneSuperBlockRow<uint16_t>(
179 row4x4, kNum4x4InLoopRestorationUnit);
180 continue;
181 }
182 #endif
183 ApplyLoopRestorationForOneSuperBlockRow<uint8_t>(
184 row4x4, kNum4x4InLoopRestorationUnit);
185 }
186 }
187
188 } // namespace libgav1
189