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