simple_render_pipeline.cc (11161B)
1 // Copyright (c) the JPEG XL Project Authors. All rights reserved. 2 // 3 // Use of this source code is governed by a BSD-style 4 // license that can be found in the LICENSE file. 5 6 #include "lib/jxl/render_pipeline/simple_render_pipeline.h" 7 8 #include <hwy/base.h> 9 10 #include "lib/jxl/base/status.h" 11 #include "lib/jxl/image_ops.h" 12 #include "lib/jxl/render_pipeline/render_pipeline_stage.h" 13 #include "lib/jxl/sanitizers.h" 14 15 namespace jxl { 16 17 Status SimpleRenderPipeline::PrepareForThreadsInternal(size_t num, 18 bool use_group_ids) { 19 if (!channel_data_.empty()) { 20 return true; 21 } 22 auto ch_size = [](size_t frame_size, size_t shift) { 23 return DivCeil(frame_size, 1 << shift) + kRenderPipelineXOffset * 2; 24 }; 25 for (size_t c = 0; c < channel_shifts_[0].size(); c++) { 26 JXL_ASSIGN_OR_RETURN( 27 ImageF ch, ImageF::Create(ch_size(frame_dimensions_.xsize_upsampled, 28 channel_shifts_[0][c].first), 29 ch_size(frame_dimensions_.ysize_upsampled, 30 channel_shifts_[0][c].second))); 31 channel_data_.push_back(std::move(ch)); 32 msan::PoisonImage(channel_data_.back()); 33 } 34 return true; 35 } 36 37 Rect SimpleRenderPipeline::MakeChannelRect(size_t group_id, size_t channel) { 38 size_t base_color_shift = 39 CeilLog2Nonzero(frame_dimensions_.xsize_upsampled_padded / 40 frame_dimensions_.xsize_padded); 41 42 const size_t gx = group_id % frame_dimensions_.xsize_groups; 43 const size_t gy = group_id / frame_dimensions_.xsize_groups; 44 size_t xgroupdim = (frame_dimensions_.group_dim << base_color_shift) >> 45 channel_shifts_[0][channel].first; 46 size_t ygroupdim = (frame_dimensions_.group_dim << base_color_shift) >> 47 channel_shifts_[0][channel].second; 48 return Rect( 49 kRenderPipelineXOffset + gx * xgroupdim, 50 kRenderPipelineXOffset + gy * ygroupdim, xgroupdim, ygroupdim, 51 kRenderPipelineXOffset + DivCeil(frame_dimensions_.xsize_upsampled, 52 1 << channel_shifts_[0][channel].first), 53 kRenderPipelineXOffset + 54 DivCeil(frame_dimensions_.ysize_upsampled, 55 1 << channel_shifts_[0][channel].second)); 56 } 57 58 std::vector<std::pair<ImageF*, Rect>> SimpleRenderPipeline::PrepareBuffers( 59 size_t group_id, size_t thread_id) { 60 std::vector<std::pair<ImageF*, Rect>> ret; 61 for (size_t c = 0; c < channel_data_.size(); c++) { 62 ret.emplace_back(&channel_data_[c], MakeChannelRect(group_id, c)); 63 } 64 return ret; 65 } 66 67 Status SimpleRenderPipeline::ProcessBuffers(size_t group_id, size_t thread_id) { 68 for (size_t c = 0; c < channel_data_.size(); c++) { 69 Rect r = MakeChannelRect(group_id, c); 70 (void)r; 71 JXL_CHECK_PLANE_INITIALIZED(channel_data_[c], r, c); 72 } 73 74 if (PassesWithAllInput() <= processed_passes_) return true; 75 processed_passes_++; 76 77 for (size_t stage_id = 0; stage_id < stages_.size(); stage_id++) { 78 const auto& stage = stages_[stage_id]; 79 // Prepare buffers for kInOut channels. 80 std::vector<ImageF> new_channels(channel_data_.size()); 81 std::vector<ImageF*> output_channels(channel_data_.size()); 82 83 std::vector<std::pair<size_t, size_t>> input_sizes(channel_data_.size()); 84 for (size_t c = 0; c < channel_data_.size(); c++) { 85 input_sizes[c] = 86 std::make_pair(channel_data_[c].xsize() - kRenderPipelineXOffset * 2, 87 channel_data_[c].ysize() - kRenderPipelineXOffset * 2); 88 } 89 90 for (size_t c = 0; c < channel_data_.size(); c++) { 91 if (stage->GetChannelMode(c) != RenderPipelineChannelMode::kInOut) { 92 continue; 93 } 94 // Ensure that the newly allocated channels are large enough to avoid 95 // problems with padding. 96 JXL_ASSIGN_OR_RETURN( 97 new_channels[c], 98 ImageF::Create(frame_dimensions_.xsize_upsampled_padded + 99 kRenderPipelineXOffset * 2 + 100 hwy::kMaxVectorSize * 8, 101 frame_dimensions_.ysize_upsampled_padded + 102 kRenderPipelineXOffset * 2)); 103 new_channels[c].ShrinkTo( 104 (input_sizes[c].first << stage->settings_.shift_x) + 105 kRenderPipelineXOffset * 2, 106 (input_sizes[c].second << stage->settings_.shift_y) + 107 kRenderPipelineXOffset * 2); 108 output_channels[c] = &new_channels[c]; 109 } 110 111 auto get_row = [&](size_t c, int64_t y) { 112 return channel_data_[c].Row(kRenderPipelineXOffset + y) + 113 kRenderPipelineXOffset; 114 }; 115 116 // Add mirrored pixes to all kInOut channels. 117 for (size_t c = 0; c < channel_data_.size(); c++) { 118 if (stage->GetChannelMode(c) != RenderPipelineChannelMode::kInOut) { 119 continue; 120 } 121 // Horizontal mirroring. 122 for (size_t y = 0; y < input_sizes[c].second; y++) { 123 float* row = get_row(c, y); 124 for (size_t ix = 0; ix < stage->settings_.border_x; ix++) { 125 *(row - ix - 1) = 126 row[Mirror(-static_cast<ssize_t>(ix) - 1, input_sizes[c].first)]; 127 } 128 for (size_t ix = 0; ix < stage->settings_.border_x; ix++) { 129 *(row + ix + input_sizes[c].first) = 130 row[Mirror(ix + input_sizes[c].first, input_sizes[c].first)]; 131 } 132 } 133 // Vertical mirroring. 134 for (int y = 0; y < static_cast<int>(stage->settings_.border_y); y++) { 135 memcpy(get_row(c, -y - 1) - stage->settings_.border_x, 136 get_row(c, Mirror(-static_cast<ssize_t>(y) - 1, 137 input_sizes[c].second)) - 138 stage->settings_.border_x, 139 sizeof(float) * 140 (input_sizes[c].first + 2 * stage->settings_.border_x)); 141 } 142 for (int y = 0; y < static_cast<int>(stage->settings_.border_y); y++) { 143 memcpy( 144 get_row(c, input_sizes[c].second + y) - stage->settings_.border_x, 145 get_row(c, 146 Mirror(input_sizes[c].second + y, input_sizes[c].second)) - 147 stage->settings_.border_x, 148 sizeof(float) * 149 (input_sizes[c].first + 2 * stage->settings_.border_x)); 150 } 151 } 152 153 size_t ysize = 0; 154 size_t xsize = 0; 155 for (size_t c = 0; c < channel_data_.size(); c++) { 156 if (stage->GetChannelMode(c) == RenderPipelineChannelMode::kIgnored) { 157 continue; 158 } 159 ysize = std::max(input_sizes[c].second, ysize); 160 xsize = std::max(input_sizes[c].first, xsize); 161 } 162 163 JXL_ASSERT(ysize != 0); 164 JXL_ASSERT(xsize != 0); 165 166 RenderPipelineStage::RowInfo input_rows(channel_data_.size()); 167 RenderPipelineStage::RowInfo output_rows(channel_data_.size()); 168 169 // Run the pipeline. 170 { 171 JXL_RETURN_IF_ERROR(stage->SetInputSizes(input_sizes)); 172 int border_y = stage->settings_.border_y; 173 for (size_t y = 0; y < ysize; y++) { 174 // Prepare input rows. 175 for (size_t c = 0; c < channel_data_.size(); c++) { 176 if (stage->GetChannelMode(c) == RenderPipelineChannelMode::kIgnored) { 177 continue; 178 } 179 input_rows[c].resize(2 * border_y + 1); 180 for (int iy = -border_y; iy <= border_y; iy++) { 181 input_rows[c][iy + border_y] = 182 channel_data_[c].Row(y + kRenderPipelineXOffset + iy); 183 } 184 } 185 // Prepare output rows. 186 for (size_t c = 0; c < channel_data_.size(); c++) { 187 if (!output_channels[c]) continue; 188 output_rows[c].resize(1 << stage->settings_.shift_y); 189 for (size_t iy = 0; iy < output_rows[c].size(); iy++) { 190 output_rows[c][iy] = output_channels[c]->Row( 191 (y << stage->settings_.shift_y) + iy + kRenderPipelineXOffset); 192 } 193 } 194 JXL_RETURN_IF_ERROR(stage->ProcessRow(input_rows, output_rows, 195 /*xextra=*/0, xsize, 196 /*xpos=*/0, y, thread_id)); 197 } 198 } 199 200 // Move new channels to current channels. 201 for (size_t c = 0; c < channel_data_.size(); c++) { 202 if (stage->GetChannelMode(c) != RenderPipelineChannelMode::kInOut) { 203 continue; 204 } 205 channel_data_[c] = std::move(new_channels[c]); 206 } 207 for (size_t c = 0; c < channel_data_.size(); c++) { 208 size_t next_stage = std::min(stage_id + 1, channel_shifts_.size() - 1); 209 size_t xsize = DivCeil(frame_dimensions_.xsize_upsampled, 210 1 << channel_shifts_[next_stage][c].first); 211 size_t ysize = DivCeil(frame_dimensions_.ysize_upsampled, 212 1 << channel_shifts_[next_stage][c].second); 213 channel_data_[c].ShrinkTo(xsize + 2 * kRenderPipelineXOffset, 214 ysize + 2 * kRenderPipelineXOffset); 215 JXL_CHECK_PLANE_INITIALIZED( 216 channel_data_[c], 217 Rect(kRenderPipelineXOffset, kRenderPipelineXOffset, xsize, ysize), 218 c); 219 } 220 221 if (stage->SwitchToImageDimensions()) { 222 size_t image_xsize; 223 size_t image_ysize; 224 FrameOrigin frame_origin; 225 stage->GetImageDimensions(&image_xsize, &image_ysize, &frame_origin); 226 frame_dimensions_.Set(image_xsize, image_ysize, 0, 0, 0, false, 1); 227 std::vector<ImageF> old_channels = std::move(channel_data_); 228 channel_data_.clear(); 229 channel_data_.reserve(old_channels.size()); 230 for (size_t c = 0; c < old_channels.size(); c++) { 231 JXL_ASSIGN_OR_RETURN( 232 ImageF ch, 233 ImageF::Create(2 * kRenderPipelineXOffset + image_xsize, 234 2 * kRenderPipelineXOffset + image_ysize)); 235 channel_data_.emplace_back(std::move(ch)); 236 } 237 for (size_t y = 0; y < image_ysize; ++y) { 238 for (size_t c = 0; c < channel_data_.size(); c++) { 239 output_rows[c].resize(1); 240 output_rows[c][0] = channel_data_[c].Row(kRenderPipelineXOffset + y); 241 } 242 // TODO(sboukortt): consider doing this only on the parts of the 243 // background that won't be occluded. 244 stage->ProcessPaddingRow(output_rows, image_xsize, 0, y); 245 } 246 ssize_t x0 = frame_origin.x0; 247 ssize_t y0 = frame_origin.y0; 248 size_t x0_fg = 0; 249 size_t y0_fg = 0; 250 if (x0 < 0) { 251 xsize += x0; 252 x0_fg -= x0; 253 x0 = 0; 254 } 255 if (x0 + xsize > image_xsize) { 256 xsize = image_xsize - x0; 257 } 258 if (y0 < 0) { 259 ysize += y0; 260 y0_fg -= x0; 261 y0 = 0; 262 } 263 if (y0 + ysize > image_ysize) { 264 ysize = image_ysize - y0; 265 } 266 const Rect rect_fg_relative_to_image = 267 Rect(x0, y0, xsize, ysize) 268 .Translate(kRenderPipelineXOffset, kRenderPipelineXOffset); 269 const Rect rect_fg = 270 Rect(x0_fg, y0_fg, xsize, ysize) 271 .Translate(kRenderPipelineXOffset, kRenderPipelineXOffset); 272 for (size_t c = 0; c < channel_data_.size(); c++) { 273 CopyImageTo(rect_fg, old_channels[c], rect_fg_relative_to_image, 274 &channel_data_[c]); 275 } 276 } 277 } 278 return true; 279 } 280 } // namespace jxl