xref: /aosp_15_r20/external/libgav1/src/yuv_buffer.cc (revision 095378508e87ed692bf8dfeb34008b65b3735891)
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/yuv_buffer.h"
16 
17 #include <cassert>
18 #include <cstddef>
19 #include <new>
20 
21 #include "src/frame_buffer_utils.h"
22 #include "src/utils/common.h"
23 #include "src/utils/compiler_attributes.h"
24 #include "src/utils/logging.h"
25 
26 namespace libgav1 {
27 
28 // Size conventions:
29 // * Widths, heights, and border sizes are in pixels.
30 // * Strides and plane sizes are in bytes.
31 //
32 // YuvBuffer objects may be reused through the BufferPool. Realloc() must
33 // assume that data members (except buffer_alloc_ and buffer_alloc_size_) may
34 // contain stale values from the previous use, and must set all data members
35 // from scratch. In particular, Realloc() must not rely on the initial values
36 // of data members set by the YuvBuffer constructor.
Realloc(int bitdepth,bool is_monochrome,int width,int height,int8_t subsampling_x,int8_t subsampling_y,int left_border,int right_border,int top_border,int bottom_border,GetFrameBufferCallback get_frame_buffer,void * callback_private_data,void ** buffer_private_data)37 bool YuvBuffer::Realloc(int bitdepth, bool is_monochrome, int width, int height,
38                         int8_t subsampling_x, int8_t subsampling_y,
39                         int left_border, int right_border, int top_border,
40                         int bottom_border,
41                         GetFrameBufferCallback get_frame_buffer,
42                         void* callback_private_data,
43                         void** buffer_private_data) {
44   // Only support allocating buffers that have borders that are a multiple of
45   // 2. The border restriction is required because we may subsample the
46   // borders in the chroma planes.
47   if (((left_border | right_border | top_border | bottom_border) & 1) != 0) {
48     LIBGAV1_DLOG(ERROR,
49                  "Borders must be a multiple of 2: left_border = %d, "
50                  "right_border = %d, top_border = %d, bottom_border = %d.",
51                  left_border, right_border, top_border, bottom_border);
52     return false;
53   }
54 
55   // Every row in the plane buffers needs to be kFrameBufferRowAlignment-byte
56   // aligned. Since the strides are multiples of kFrameBufferRowAlignment bytes,
57   // it suffices to just make the plane buffers kFrameBufferRowAlignment-byte
58   // aligned.
59   const int plane_align = kFrameBufferRowAlignment;
60   const int uv_width =
61       is_monochrome ? 0 : SubsampledValue(width, subsampling_x);
62   const int uv_height =
63       is_monochrome ? 0 : SubsampledValue(height, subsampling_y);
64   const int uv_left_border = is_monochrome ? 0 : left_border >> subsampling_x;
65   const int uv_right_border = is_monochrome ? 0 : right_border >> subsampling_x;
66   const int uv_top_border = is_monochrome ? 0 : top_border >> subsampling_y;
67   const int uv_bottom_border =
68       is_monochrome ? 0 : bottom_border >> subsampling_y;
69 
70   if (get_frame_buffer != nullptr) {
71     assert(buffer_private_data != nullptr);
72 
73     const Libgav1ImageFormat image_format =
74         ComposeImageFormat(is_monochrome, subsampling_x, subsampling_y);
75     FrameBuffer frame_buffer;
76     if (get_frame_buffer(callback_private_data, bitdepth, image_format, width,
77                          height, left_border, right_border, top_border,
78                          bottom_border, kFrameBufferRowAlignment,
79                          &frame_buffer) != kStatusOk) {
80       return false;
81     }
82 
83     if (frame_buffer.plane[0] == nullptr ||
84         (!is_monochrome && frame_buffer.plane[1] == nullptr) ||
85         (!is_monochrome && frame_buffer.plane[2] == nullptr)) {
86       assert(false && "The get_frame_buffer callback malfunctioned.");
87       LIBGAV1_DLOG(ERROR, "The get_frame_buffer callback malfunctioned.");
88       return false;
89     }
90 
91     stride_[kPlaneY] = frame_buffer.stride[0];
92     stride_[kPlaneU] = frame_buffer.stride[1];
93     stride_[kPlaneV] = frame_buffer.stride[2];
94     buffer_[kPlaneY] = frame_buffer.plane[0];
95     buffer_[kPlaneU] = frame_buffer.plane[1];
96     buffer_[kPlaneV] = frame_buffer.plane[2];
97     *buffer_private_data = frame_buffer.private_data;
98   } else {
99     assert(callback_private_data == nullptr);
100     assert(buffer_private_data == nullptr);
101 
102     // Calculate y_stride (in bytes). It is padded to a multiple of
103     // kFrameBufferRowAlignment bytes.
104     int y_stride = width + left_border + right_border;
105 #if LIBGAV1_MAX_BITDEPTH >= 10
106     if (bitdepth > 8) y_stride *= sizeof(uint16_t);
107 #endif
108     y_stride = Align(y_stride, kFrameBufferRowAlignment);
109     // Size of the Y plane in bytes.
110     const uint64_t y_plane_size = (height + top_border + bottom_border) *
111                                       static_cast<uint64_t>(y_stride) +
112                                   (plane_align - 1);
113 
114     // Calculate uv_stride (in bytes). It is padded to a multiple of
115     // kFrameBufferRowAlignment bytes.
116     int uv_stride = uv_width + uv_left_border + uv_right_border;
117 #if LIBGAV1_MAX_BITDEPTH >= 10
118     if (bitdepth > 8) uv_stride *= sizeof(uint16_t);
119 #endif
120     uv_stride = Align(uv_stride, kFrameBufferRowAlignment);
121     // Size of the U or V plane in bytes.
122     const uint64_t uv_plane_size =
123         is_monochrome ? 0
124                       : (uv_height + uv_top_border + uv_bottom_border) *
125                                 static_cast<uint64_t>(uv_stride) +
126                             (plane_align - 1);
127 
128     // Allocate unaligned y_buffer, u_buffer, and v_buffer.
129     uint8_t* y_buffer = nullptr;
130     uint8_t* u_buffer = nullptr;
131     uint8_t* v_buffer = nullptr;
132 
133     const uint64_t frame_size = y_plane_size + 2 * uv_plane_size;
134     if (frame_size > buffer_alloc_size_) {
135       // Allocation to hold larger frame, or first allocation.
136       if (frame_size != static_cast<size_t>(frame_size)) return false;
137 
138       buffer_alloc_.reset(new (std::nothrow)
139                               uint8_t[static_cast<size_t>(frame_size)]);
140       if (buffer_alloc_ == nullptr) {
141         buffer_alloc_size_ = 0;
142         return false;
143       }
144 
145       buffer_alloc_size_ = static_cast<size_t>(frame_size);
146     }
147 
148     y_buffer = buffer_alloc_.get();
149     if (!is_monochrome) {
150       u_buffer = y_buffer + y_plane_size;
151       v_buffer = u_buffer + uv_plane_size;
152     }
153 
154     stride_[kPlaneY] = y_stride;
155     stride_[kPlaneU] = stride_[kPlaneV] = uv_stride;
156 
157     int left_border_bytes = left_border;
158     int uv_left_border_bytes = uv_left_border;
159 #if LIBGAV1_MAX_BITDEPTH >= 10
160     if (bitdepth > 8) {
161       left_border_bytes *= sizeof(uint16_t);
162       uv_left_border_bytes *= sizeof(uint16_t);
163     }
164 #endif
165     buffer_[kPlaneY] = AlignAddr(
166         y_buffer + (top_border * y_stride) + left_border_bytes, plane_align);
167     buffer_[kPlaneU] =
168         AlignAddr(u_buffer + (uv_top_border * uv_stride) + uv_left_border_bytes,
169                   plane_align);
170     buffer_[kPlaneV] =
171         AlignAddr(v_buffer + (uv_top_border * uv_stride) + uv_left_border_bytes,
172                   plane_align);
173   }
174 
175   y_width_ = width;
176   y_height_ = height;
177   left_border_[kPlaneY] = left_border;
178   right_border_[kPlaneY] = right_border;
179   top_border_[kPlaneY] = top_border;
180   bottom_border_[kPlaneY] = bottom_border;
181 
182   uv_width_ = uv_width;
183   uv_height_ = uv_height;
184   left_border_[kPlaneU] = left_border_[kPlaneV] = uv_left_border;
185   right_border_[kPlaneU] = right_border_[kPlaneV] = uv_right_border;
186   top_border_[kPlaneU] = top_border_[kPlaneV] = uv_top_border;
187   bottom_border_[kPlaneU] = bottom_border_[kPlaneV] = uv_bottom_border;
188 
189   subsampling_x_ = subsampling_x;
190   subsampling_y_ = subsampling_y;
191 
192   bitdepth_ = bitdepth;
193   is_monochrome_ = is_monochrome;
194   assert(!is_monochrome || stride_[kPlaneU] == 0);
195   assert(!is_monochrome || stride_[kPlaneV] == 0);
196   assert(!is_monochrome || buffer_[kPlaneU] == nullptr);
197   assert(!is_monochrome || buffer_[kPlaneV] == nullptr);
198 
199 #if LIBGAV1_MSAN
200   InitializeFrameBorders();
201 #endif
202 
203   return true;
204 }
205 
206 #if LIBGAV1_MSAN
InitializeFrameBorders()207 void YuvBuffer::InitializeFrameBorders() {
208   const int pixel_size = (bitdepth_ == 8) ? sizeof(uint8_t) : sizeof(uint16_t);
209   const int y_width_in_bytes = y_width_ * pixel_size;
210   // The optimized loop restoration code will overread the visible frame buffer
211   // into the right border. The optimized cfl subsambler uses the right border
212   // as well. Initialize the right border and padding to prevent msan warnings.
213   const int y_right_border_size_in_bytes = right_border_[kPlaneY] * pixel_size;
214   // Calculate the padding bytes for the buffer. Note: The stride of the buffer
215   // is always a multiple of 16. (see yuv_buffer.h)
216   const int y_right_padding_in_bytes =
217       stride_[kPlaneY] - (pixel_size * (y_width_ + left_border_[kPlaneY] +
218                                         right_border_[kPlaneY]));
219   const int y_padded_right_border_size =
220       y_right_border_size_in_bytes + y_right_padding_in_bytes;
221   constexpr uint8_t kRightValue = 0x55;
222   uint8_t* rb = buffer_[kPlaneY] + y_width_in_bytes;
223   for (int i = 0; i < y_height_ + bottom_border_[kPlaneY]; ++i) {
224     memset(rb, kRightValue, y_padded_right_border_size);
225     rb += stride_[kPlaneY];
226   }
227 
228   if (!is_monochrome_) {
229     const int uv_width_in_bytes = uv_width_ * pixel_size;
230     const int uv_right_border_size_in_bytes =
231         right_border_[kPlaneU] * pixel_size;
232     assert(right_border_[kPlaneU] == right_border_[kPlaneV]);
233     const int u_right_padding_in_bytes =
234         stride_[kPlaneU] - (pixel_size * (uv_width_ + left_border_[kPlaneU] +
235                                           right_border_[kPlaneU]));
236     const int u_padded_right_border_size =
237         uv_right_border_size_in_bytes + u_right_padding_in_bytes;
238     rb = buffer_[kPlaneU] + uv_width_in_bytes;
239     for (int i = 0; i < uv_height_; ++i) {
240       memset(rb, kRightValue, u_padded_right_border_size);
241       rb += stride_[kPlaneU];
242     }
243     const int v_right_padding_in_bytes =
244         stride_[kPlaneV] -
245         ((uv_width_ + left_border_[kPlaneV] + right_border_[kPlaneV]) *
246          pixel_size);
247     const int v_padded_right_border_size =
248         uv_right_border_size_in_bytes + v_right_padding_in_bytes;
249     rb = buffer_[kPlaneV] + uv_width_in_bytes;
250     for (int i = 0; i < uv_height_; ++i) {
251       memset(rb, kRightValue, v_padded_right_border_size);
252       rb += stride_[kPlaneV];
253     }
254   }
255 
256   // The optimized cfl subsampler will overread (to the right of the current
257   // block) into the uninitialized visible area. The cfl subsampler can overread
258   // into the bottom border as well. Initialize the both to quiet msan warnings.
259   uint8_t* y_visible = buffer_[kPlaneY];
260   for (int i = 0; i < y_height_ + bottom_border_[kPlaneY]; ++i) {
261     memset(y_visible, kRightValue, y_width_in_bytes);
262     y_visible += stride_[kPlaneY];
263   }
264 }
265 #endif  // LIBGAV1_MSAN
266 
267 }  // namespace libgav1
268