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 namespace jxl {
9 
PrepareForThreadsInternal(size_t num)10 void SimpleRenderPipeline::PrepareForThreadsInternal(size_t num) {
11   if (!channel_data_.empty()) {
12     return;
13   }
14   auto ch_size = [](size_t frame_size, size_t shift) {
15     return DivCeil(frame_size, 1 << shift) + kRenderPipelineXOffset * 2;
16   };
17   for (size_t c = 0; c < channel_shifts_[0].size(); c++) {
18     bool is_color_c =
19         c < 3 || (uses_noise_ && c >= channel_shifts_[0].size() - 3);
20     channel_data_.push_back(
21         ImageF(ch_size(frame_dimensions_.GetUpsampledXSize(is_color_c),
22                        channel_shifts_[0][c].first),
23                ch_size(frame_dimensions_.GetUpsampledYSize(is_color_c),
24                        channel_shifts_[0][c].second)));
25     msan::PoisonImage(channel_data_.back());
26   }
27 }
28 
MakeChannelRect(size_t group_id,size_t channel,bool is_color)29 Rect SimpleRenderPipeline::MakeChannelRect(size_t group_id, size_t channel,
30                                            bool is_color) {
31   size_t base_color_shift =
32       CeilLog2Nonzero(frame_dimensions_.xsize_upsampled_padded /
33                       frame_dimensions_.xsize_padded);
34 
35   const size_t gx = group_id % frame_dimensions_.xsize_groups;
36   const size_t gy = group_id / frame_dimensions_.xsize_groups;
37   size_t xgroupdim = (frame_dimensions_.group_dim << base_color_shift) >>
38                      channel_shifts_[0][channel].first;
39   size_t ygroupdim = (frame_dimensions_.group_dim << base_color_shift) >>
40                      channel_shifts_[0][channel].second;
41   return Rect(kRenderPipelineXOffset + gx * xgroupdim,
42               kRenderPipelineXOffset + gy * ygroupdim, xgroupdim, ygroupdim,
43               kRenderPipelineXOffset +
44                   DivCeil(frame_dimensions_.GetUpsampledXSize(is_color),
45                           1 << channel_shifts_[0][channel].first),
46               kRenderPipelineXOffset +
47                   DivCeil(frame_dimensions_.GetUpsampledYSize(is_color),
48                           1 << channel_shifts_[0][channel].second));
49 }
50 
PrepareBuffers(size_t group_id,size_t thread_id)51 std::vector<std::pair<ImageF*, Rect>> SimpleRenderPipeline::PrepareBuffers(
52     size_t group_id, size_t thread_id) {
53   std::vector<std::pair<ImageF*, Rect>> ret;
54   for (size_t c = 0; c < channel_data_.size(); c++) {
55     bool is_color_c =
56         c < 3 || (uses_noise_ && c >= channel_shifts_[0].size() - 3);
57     ret.emplace_back(&channel_data_[c],
58                      MakeChannelRect(group_id, c, is_color_c));
59   }
60   return ret;
61 }
62 
ProcessBuffers(size_t group_id,size_t thread_id)63 void SimpleRenderPipeline::ProcessBuffers(size_t group_id, size_t thread_id) {
64   if (PassesWithAllInput() <= processed_passes_) return;
65   processed_passes_++;
66 
67   for (size_t c = 0; c < channel_data_.size(); c++) {
68     Rect r = MakeChannelRect(group_id, c, false);
69     (void)r;
70     JXL_CHECK_IMAGE_INITIALIZED(
71         channel_data_[c], Rect(kRenderPipelineXOffset, kRenderPipelineXOffset,
72                                r.xsize(), r.ysize()));
73   }
74 
75   for (size_t stage_id = 0; stage_id < stages_.size(); stage_id++) {
76     const auto& stage = stages_[stage_id];
77     // Prepare buffers for kInOut channels.
78     std::vector<ImageF> new_channels(channel_data_.size());
79     std::vector<ImageF*> output_channels(channel_data_.size());
80 
81     std::vector<std::pair<size_t, size_t>> input_sizes(channel_data_.size());
82     for (size_t c = 0; c < channel_data_.size(); c++) {
83       input_sizes[c] =
84           std::make_pair(channel_data_[c].xsize() - kRenderPipelineXOffset * 2,
85                          channel_data_[c].ysize() - kRenderPipelineXOffset * 2);
86     }
87 
88     for (size_t c = 0; c < channel_data_.size(); c++) {
89       if (stage->GetChannelMode(c) != RenderPipelineChannelMode::kInOut) {
90         continue;
91       }
92       new_channels[c] =
93           ImageF((input_sizes[c].first << stage->settings_.shift_x) +
94                      kRenderPipelineXOffset * 2,
95                  (input_sizes[c].second << stage->settings_.shift_y) +
96                      kRenderPipelineXOffset * 2);
97       output_channels[c] = &new_channels[c];
98     }
99 
100     auto get_row = [&](size_t c, int64_t y) {
101       return channel_data_[c].Row(kRenderPipelineXOffset + y) +
102              kRenderPipelineXOffset;
103     };
104 
105     // Add mirrored pixes to all kInOut channels.
106     for (size_t c = 0; c < channel_data_.size(); c++) {
107       if (stage->GetChannelMode(c) != RenderPipelineChannelMode::kInOut) {
108         continue;
109       }
110       // Horizontal mirroring.
111       for (size_t y = 0; y < input_sizes[c].second; y++) {
112         float* row = get_row(c, y);
113         for (size_t ix = 0; ix < stage->settings_.border_x; ix++) {
114           *(row - ix - 1) = *(row + ix);
115           *(row + ix + input_sizes[c].first) =
116               *(row + input_sizes[c].first - ix - 1);
117         }
118       }
119       // Vertical mirroring.
120       for (int y = 0; y < static_cast<int>(stage->settings_.border_y); y++) {
121         memcpy(get_row(c, -y - 1) - stage->settings_.border_x,
122                get_row(c, y) - stage->settings_.border_x,
123                sizeof(float) *
124                    (input_sizes[c].first + 2 * stage->settings_.border_x));
125         memcpy(
126             get_row(c, input_sizes[c].second + y) - stage->settings_.border_x,
127             get_row(c, input_sizes[c].second - y - 1) -
128                 stage->settings_.border_x,
129             sizeof(float) *
130                 (input_sizes[c].first + 2 * stage->settings_.border_x));
131       }
132     }
133 
134     size_t ysize = 0;
135     size_t xsize = 0;
136     for (size_t c = 0; c < channel_data_.size(); c++) {
137       if (stage->GetChannelMode(c) == RenderPipelineChannelMode::kIgnored) {
138         continue;
139       }
140       ysize = std::max(input_sizes[c].second, ysize);
141       xsize = std::max(input_sizes[c].first, xsize);
142     }
143 
144     JXL_ASSERT(ysize != 0);
145     JXL_ASSERT(xsize != 0);
146 
147     RenderPipelineStage::RowInfo input_rows(channel_data_.size());
148     RenderPipelineStage::RowInfo output_rows(channel_data_.size());
149 
150     // Run the pipeline.
151     {
152       int border_y = stage->settings_.border_y;
153       for (size_t y = 0; y < ysize; y++) {
154         // Prepare input rows.
155         for (size_t c = 0; c < channel_data_.size(); c++) {
156           if (stage->GetChannelMode(c) == RenderPipelineChannelMode::kIgnored) {
157             continue;
158           }
159           input_rows[c].resize(2 * border_y + 1);
160           for (int iy = -border_y; iy <= border_y; iy++) {
161             input_rows[c][iy + border_y] =
162                 channel_data_[c].Row(y + kRenderPipelineXOffset + iy);
163           }
164         }
165         // Prepare output rows.
166         for (size_t c = 0; c < channel_data_.size(); c++) {
167           if (!output_channels[c]) continue;
168           output_rows[c].resize(1 << stage->settings_.shift_y);
169           for (size_t iy = 0; iy < output_rows[c].size(); iy++) {
170             output_rows[c][iy] = output_channels[c]->Row(
171                 (y << stage->settings_.shift_y) + iy + kRenderPipelineXOffset);
172           }
173         }
174         stage->ProcessRow(
175             input_rows, output_rows, /*xextra=*/0, xsize,
176             /*xpos=*/0, y,
177             reinterpret_cast<float*>(temp_buffers_[thread_id].get()));
178       }
179     }
180 
181     // Move new channels to current channels.
182     for (size_t c = 0; c < channel_data_.size(); c++) {
183       if (stage->GetChannelMode(c) != RenderPipelineChannelMode::kInOut) {
184         continue;
185       }
186       channel_data_[c] = std::move(new_channels[c]);
187     }
188     for (size_t c = 0; c < channel_data_.size(); c++) {
189       Rect r = MakeChannelRect(group_id, c, false);
190       (void)r;
191       JXL_CHECK_IMAGE_INITIALIZED(
192           channel_data_[c], Rect(kRenderPipelineXOffset, kRenderPipelineXOffset,
193                                  r.xsize(), r.ysize()));
194     }
195   }
196 }
197 }  // namespace jxl
198