tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

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