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