chrono_batch_render/
Coverage:
low: ≥ 0%
medium: ≥ 75.0%
high: ≥ 90.0%
Lines:
165 / 286
57.7%
Branches:
129 / 314
41.1%
Functions:
24 / 31
77.4%

render/render_output.cpp
Line Branch Exec Source
1 // SPDX-License-Identifier: BSD-3-Clause
2 #include "chrono_batch_render/render/render_output.h"
3
4 #include <condition_variable>
5 #include <cstddef>
6 #include <cstring>
7 #include <deque>
8 #include <exception>
9 #include <fstream>
10 #include <functional>
11 #include <mutex>
12 #include <string>
13 #include <thread>
14 #include <utility>
15
16 #include <fmt/format.h>
17
18 #include "chrono_batch_render/device/buffer.h"
19 #include "chrono_batch_render/device/queue.h"
20 #include "chrono_batch_render/io/image.h"
21 #include "chrono_batch_render/util/error.h"
22 #include "chrono_batch_render/util/log.h"
23 #include "chrono_batch_render/util/span.h"
24 #include "chrono_batch_render/util/text.h"
25
26 namespace chrono::batch_render {
27
28 15 const RenderVarOutput& FrameOutput::RenderVar(std::string_view path) const {
29 15 const auto found = renderVars.find(path);
30
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 15 times.
15 if (found == renderVars.end()) {
31
0/1
✗ Branch 2 not taken.
✗ throw Error("The frame has no render var {}", path);
32 }
33 15 return found->second;
34 }
35
36 3421 const RenderVarOutput& FrameOutput::Source(std::string_view sourceName) const {
37
4/4
✓ Branch 0 taken 3420 times.
✓ Branch 1 taken 147 times.
✓ Branch 2 taken 3567 times.
✓ Branch 3 taken 1 time.
3568 for (const auto& [path, var] : renderVars) {
38
2/2
✓ Branch 0 taken 3420 times.
✓ Branch 1 taken 147 times.
3567 if (var.sourceName == sourceName) {
39 3420 return var;
40 }
41 }
42
1/1
✓ Branch 2 taken 1 time.
1 throw Error("The frame has no render var whose source is {}", sourceName);
43 }
44
45 ✗ IdTexts DecodeIdMap(const device::Tensor& map) {
46 ✗ constexpr size_t kRecordWords = 6;
47 ✗ const std::vector<std::byte> bytes = map.ToHostBytes();
48 ✗ uint32_t count = 0;
49
0/2
✗ Branch 0 not taken.
✗ Branch 1 not taken.
✗ if (bytes.size() < sizeof(count)) {
50
0/1
✗ Branch 1 not taken.
✗ throw Error("An id map holds at least its {}-byte count, not {} bytes", sizeof(count),
51
0/1
✗ Branch 2 not taken.
✗ bytes.size());
52 }
53 ✗ const size_t texts = bytes.size() - sizeof(count);
54
0/2
✗ Branch 0 not taken.
✗ Branch 1 not taken.
✗ std::memcpy(&count, bytes.data() + texts, sizeof(count));
55
0/2
✗ Branch 0 not taken.
✗ Branch 1 not taken.
✗ if (size_t(count) * kRecordWords * sizeof(uint32_t) > texts) {
56
0/1
✗ Branch 2 not taken.
✗ throw Error("An id map of {} bytes can't hold {} entries", bytes.size(), count);
57 }
58 ✗ IdTexts entries;
59
0/2
✗ Branch 0 not taken.
✗ Branch 1 not taken.
✗ for (uint32_t entry = 0; entry < count; ++entry) {
60 ✗ uint32_t record[kRecordWords];
61
0/2
✗ Branch 0 not taken.
✗ Branch 1 not taken.
✗ std::memcpy(record, bytes.data() + entry * sizeof(record), sizeof(record));
62 ✗ const size_t size = record[4];
63 ✗ const size_t offset = record[5];
64
0/4
✗ Branch 0 not taken.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✗ if (offset > texts || size > texts - offset) {
65
0/1
✗ Branch 2 not taken.
✗ throw Error("Id map entry {} runs past the map's {} bytes", entry, bytes.size());
66 }
67
0/2
✗ Branch 1 not taken.
✗ Branch 4 not taken.
✗ entries[{record[0], record[1], record[2], record[3]}] =
68
0/1
✗ Branch 1 not taken.
✗ std::string(reinterpret_cast<const char*>(bytes.data()) + offset, size);
69 }
70 ✗ return entries;
71 ✗ }
72
73 namespace {
74
75 ✗ void WriteLabels(const std::filesystem::path& path, const IdMap& map, bool single) {
76 ✗ const auto escaped = [](std::string_view text) {
77 ✗ std::string result;
78
0/2
✗ Branch 0 not taken.
✗ Branch 1 not taken.
✗ for (const char c : text) {
79
0/2
✗ Branch 0 not taken.
✗ Branch 1 not taken.
✗ if (c == '"' || c == '\\') {
80
0/1
✗ Branch 1 not taken.
✗ result += '\\';
81
0/1
✗ Branch 1 not taken.
✗ result += c;
82
0/2
✗ Branch 0 not taken.
✗ Branch 1 not taken.
✗ } else if (uint8_t(c) < 0x20) {
83
0/1
✗ Branch 1 not taken.
✗ result += fmt::format("\\u{:04x}", int(c));
84 } else {
85
0/1
✗ Branch 1 not taken.
✗ result += c;
86 }
87 }
88 ✗ return result;
89 ✗ };
90 ✗ std::string text = "{";
91 ✗ const char* separator = "\n";
92
0/4
✗ Branch 0 not taken.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✗ for (const auto& [id, label] : map) {
93 ✗ const std::array<uint8_t, 4> color =
94
0/3
✗ Branch 0 not taken.
✗ Branch 1 not taken.
✗ Branch 3 not taken.
✗ IdColor(Span<const uint32_t>(id.data(), single ? 1 : id.size()));
95 ✗ const std::string key = single
96
0/2
✗ Branch 0 not taken.
✗ Branch 1 not taken.
✗ ? std::to_string(id[0])
97
0/1
✗ Branch 2 not taken.
✗ : fmt::format("({}, {}, {}, {})", id[0], id[1], id[2], id[3]);
98
0/2
✗ Branch 0 not taken.
✗ Branch 1 not taken.
✗ std::string fields;
99
0/2
✗ Branch 0 not taken.
✗ Branch 1 not taken.
✗ if (!label.path.empty()) {
100
0/2
✗ Branch 1 not taken.
✗ Branch 4 not taken.
✗ fields += fmt::format("\"path\": \"{}\", ", escaped(label.path));
101 }
102
0/1
✗ Branch 1 not taken.
✗ fields += "\"labels\": {";
103 ✗ const char* comma = "";
104
0/3
✗ Branch 1 not taken.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✗ for (const auto& [taxonomy, names] : label.labels) {
105
0/2
✗ Branch 1 not taken.
✗ Branch 4 not taken.
✗ fields += fmt::format("{}\"{}\": [", comma, escaped(taxonomy));
106
0/2
✗ Branch 0 not taken.
✗ Branch 1 not taken.
✗ for (size_t i = 0; i < names.size(); ++i) {
107
0/4
✗ Branch 1 not taken.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✗ Branch 6 not taken.
✗ fields += fmt::format("{}\"{}\"", i > 0 ? ", " : "", escaped(names[i]));
108 }
109
0/1
✗ Branch 1 not taken.
✗ fields += "]";
110 ✗ comma = ", ";
111 }
112
0/1
✗ Branch 1 not taken.
✗ text += fmt::format("{} \"{}\": {{{}}}, \"color\": [{}, {}, {}, {}]}}", separator, key,
113
0/1
✗ Branch 1 not taken.
✗ fields, color[0], color[1], color[2], color[3]);
114 ✗ separator = ",\n";
115 ✗ }
116
0/1
✗ Branch 1 not taken.
✗ text += "\n}\n";
117
0/1
✗ Branch 1 not taken.
✗ std::ofstream file(path, std::ios::binary);
118
0/1
✗ Branch 1 not taken.
✗ file << text;
119
0/2
✗ Branch 0 not taken.
✗ Branch 1 not taken.
✗ if (!file) {
120
0/2
✗ Branch 2 not taken.
✗ Branch 5 not taken.
✗ throw Error("Cannot write {}", path.string());
121 }
122 ✗ }
123
124
4/4
chrono::batch_render::Pixels chrono::batch_render::(anonymous namespace)::Viewable<chrono::batch_render::Pixels>(std::basic_string_view<char, std::char_traits<char> >, chrono::batch_render::device::DType, chrono::batch_render::Pixels const&):
✓ Branch 0 taken 6 times.
✓ Branch 1 taken 6 times.
chrono::batch_render::device::Tensor chrono::batch_render::(anonymous namespace)::Viewable<chrono::batch_render::device::Tensor>(std::basic_string_view<char, std::char_traits<char> >, chrono::batch_render::device::DType, chrono::batch_render::device::Tensor const&):
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 2 times.
16 template <class T> T Viewable(std::string_view source, device::DType type, const T& image) {
125
6/8
chrono::batch_render::Pixels chrono::batch_render::(anonymous namespace)::Viewable<chrono::batch_render::Pixels>(std::basic_string_view<char, std::char_traits<char> >, chrono::batch_render::device::DType, chrono::batch_render::Pixels const&):
✓ Branch 0 taken 6 times.
✓ Branch 1 taken 6 times.
✓ Branch 2 taken 6 times.
✗ Branch 3 not taken.
chrono::batch_render::device::Tensor chrono::batch_render::(anonymous namespace)::Viewable<chrono::batch_render::device::Tensor>(std::basic_string_view<char, std::char_traits<char> >, chrono::batch_render::device::DType, chrono::batch_render::device::Tensor const&):
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 2 times.
✓ Branch 2 taken 2 times.
✗ Branch 3 not taken.
16 if (source == "DistanceToCamera" || source == "DistanceToImagePlane" ||
126
4/8
chrono::batch_render::Pixels chrono::batch_render::(anonymous namespace)::Viewable<chrono::batch_render::Pixels>(std::basic_string_view<char, std::char_traits<char> >, chrono::batch_render::device::DType, chrono::batch_render::Pixels const&):
✗ Branch 0 not taken.
✓ Branch 1 taken 6 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 6 times.
chrono::batch_render::device::Tensor chrono::batch_render::(anonymous namespace)::Viewable<chrono::batch_render::device::Tensor>(std::basic_string_view<char, std::char_traits<char> >, chrono::batch_render::device::DType, chrono::batch_render::device::Tensor const&):
✗ Branch 0 not taken.
✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 2 times.
8 source == "NormalizedDepth") {
127 8 return ColorizeScalars(image, kDepthColors);
128 }
129
2/4
chrono::batch_render::Pixels chrono::batch_render::(anonymous namespace)::Viewable<chrono::batch_render::Pixels>(std::basic_string_view<char, std::char_traits<char> >, chrono::batch_render::device::DType, chrono::batch_render::Pixels const&):
✗ Branch 0 not taken.
✓ Branch 1 taken 6 times.
chrono::batch_render::device::Tensor chrono::batch_render::(anonymous namespace)::Viewable<chrono::batch_render::device::Tensor>(std::basic_string_view<char, std::char_traits<char> >, chrono::batch_render::device::DType, chrono::batch_render::device::Tensor const&):
✗ Branch 0 not taken.
✓ Branch 1 taken 2 times.
8 if (source == "Intensity") {
130 ✗ return ColorizeScalars(image);
131 }
132
2/4
chrono::batch_render::Pixels chrono::batch_render::(anonymous namespace)::Viewable<chrono::batch_render::Pixels>(std::basic_string_view<char, std::char_traits<char> >, chrono::batch_render::device::DType, chrono::batch_render::Pixels const&):
✗ Branch 0 not taken.
✓ Branch 1 taken 6 times.
chrono::batch_render::device::Tensor chrono::batch_render::(anonymous namespace)::Viewable<chrono::batch_render::device::Tensor>(std::basic_string_view<char, std::char_traits<char> >, chrono::batch_render::device::DType, chrono::batch_render::device::Tensor const&):
✗ Branch 0 not taken.
✓ Branch 1 taken 2 times.
8 if (source == "RadialVelocity") {
133 ✗ return ColorizeScalars(image, {.colormap = Colormap::Turbo, .centered = true});
134 }
135
2/4
chrono::batch_render::Pixels chrono::batch_render::(anonymous namespace)::Viewable<chrono::batch_render::Pixels>(std::basic_string_view<char, std::char_traits<char> >, chrono::batch_render::device::DType, chrono::batch_render::Pixels const&):
✗ Branch 0 not taken.
✓ Branch 1 taken 6 times.
chrono::batch_render::device::Tensor chrono::batch_render::(anonymous namespace)::Viewable<chrono::batch_render::device::Tensor>(std::basic_string_view<char, std::char_traits<char> >, chrono::batch_render::device::DType, chrono::batch_render::device::Tensor const&):
✗ Branch 0 not taken.
✓ Branch 1 taken 2 times.
8 if (source == "Normal") {
136 ✗ return ColorizeNormals(image);
137 }
138
2/4
chrono::batch_render::Pixels chrono::batch_render::(anonymous namespace)::Viewable<chrono::batch_render::Pixels>(std::basic_string_view<char, std::char_traits<char> >, chrono::batch_render::device::DType, chrono::batch_render::Pixels const&):
✗ Branch 0 not taken.
✓ Branch 1 taken 6 times.
chrono::batch_render::device::Tensor chrono::batch_render::(anonymous namespace)::Viewable<chrono::batch_render::device::Tensor>(std::basic_string_view<char, std::char_traits<char> >, chrono::batch_render::device::DType, chrono::batch_render::device::Tensor const&):
✗ Branch 0 not taken.
✓ Branch 1 taken 2 times.
8 if (type == device::DType::UInt32) {
139 ✗ return ColorizeIds(image);
140 }
141 8 return image;
142 }
143
144 12 std::filesystem::path WriteVar(std::filesystem::path path, const RenderVarOutput& renderVar,
145 const Pixels& image) {
146
1/4
✗ Branch 0 not taken.
✓ Branch 1 taken 12 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
12 if (renderVar.idMap && image.type == device::DType::UInt32) {
147
0/3
✗ Branch 1 not taken.
✗ Branch 4 not taken.
✗ Branch 7 not taken.
✗ WriteLabels(std::filesystem::path(path).replace_extension(".json"), *renderVar.idMap,
148 ✗ image.channels == 1);
149 }
150
2/3
✓ Branch 2 taken 12 times.
✗ Branch 7 not taken.
✓ Branch 8 taken 12 times.
36 if (Lowercase(path.extension().string()) == ".exr") {
151
0/1
✗ Branch 2 not taken.
✗ return WriteImage(std::move(path), image);
152 }
153
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 12 times.
12 if (!path.has_extension()) {
154 ✗ path += ".png";
155 }
156
1/1
✓ Branch 3 taken 12 times.
24 return WriteImage(std::move(path), Viewable(renderVar.sourceName, image.type, image));
157 }
158
159 4 std::function<Pixels()> Fetch(const device::Tensor& tensor) {
160 4 const Span<const int64_t> shape = tensor.Shape();
161 4 const Span<const int64_t> strides = tensor.Strides();
162
2/4
✓ Branch 0 taken 4 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 4 times.
✗ Branch 3 not taken.
4 bool compact = shape.size() == 2 || shape.size() == 3;
163 int64_t expected = 1;
164
3/4
✓ Branch 0 taken 16 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 12 times.
✓ Branch 3 taken 4 times.
16 for (size_t axis = shape.size(); compact && axis-- > 0;) {
165
2/4
✓ Branch 0 taken 12 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 12 times.
✗ Branch 3 not taken.
12 compact = shape[axis] <= 1 || strides[axis] == expected;
166 12 expected *= shape[axis];
167 }
168
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 4 times.
4 if (!compact) {
169
0/2
✗ Branch 1 not taken.
✗ Branch 8 not taken.
✗ return [pixels = Pixels(tensor)] { return pixels; };
170 }
171 4 const uint64_t bytes = uint64_t(expected) * device::ElementBytes(tensor.Type());
172 4 device::Buffer& buffer = tensor.GetBuffer();
173 4 const device::Download download =
174 4 buffer.GetDevice().GetQueue().ReadAsync(buffer.Slice(tensor.Offset(), bytes));
175
2/2
✓ Branch 5 taken 4 times.
✓ Branch 8 taken 4 times.
12 return [download, tensor] {
176 4 download.Wait();
177 4 const Span<const int64_t> sides = tensor.Shape();
178
1/2
✓ Branch 0 taken 4 times.
✗ Branch 1 not taken.
4 return Pixels(download.Bytes(), tensor.Type(), sides[0], sides[1],
179
1/2
✓ Branch 0 taken 4 times.
✗ Branch 1 not taken.
4 sides.size() == 3 ? sides[2] : 1);
180
1/1
✓ Branch 1 taken 4 times.
4 };
181 4 }
182
183 }
184
185 12 std::filesystem::path WriteRenderVar(std::filesystem::path path, const RenderVarOutput& renderVar,
186 int64_t camera) {
187
2/2
✓ Branch 2 taken 12 times.
✓ Branch 6 taken 12 times.
24 return WriteVar(std::move(path), renderVar, Pixels(renderVar.tensor.Select(camera)));
188 }
189
190 namespace {
191
192 template <class Write>
193 4 std::vector<std::filesystem::path> WriteVars(const std::filesystem::path& directory,
194 const FrameOutput& frame, bool raw, Write write) {
195 4 std::filesystem::create_directories(directory);
196 4 std::vector<std::filesystem::path> written;
197
4/4
std::vector<std::filesystem::__cxx11::path, std::allocator<std::filesystem::__cxx11::path> > chrono::batch_render::(anonymous namespace)::WriteVars<chrono::batch_render::ImageWriter::WriteFrame(std::filesystem::__cxx11::path const&, chrono::batch_render::FrameOutput const&, bool)::{lambda(std::filesystem::__cxx11::path const&, chrono::batch_render::RenderVarOutput const&, long)#1}>(std::filesystem::__cxx11::path const&, chrono::batch_render::FrameOutput const&, bool, chrono::batch_render::ImageWriter::WriteFrame(std::filesystem::__cxx11::path const&, chrono::batch_render::FrameOutput const&, bool)::{lambda(std::filesystem::__cxx11::path const&, chrono::batch_render::RenderVarOutput const&, long)#1}):
✓ Branch 1 taken 2 times.
✓ Branch 2 taken 1 time.
std::vector<std::filesystem::__cxx11::path, std::allocator<std::filesystem::__cxx11::path> > chrono::batch_render::(anonymous namespace)::WriteVars<chrono::batch_render::WriteFrame(std::filesystem::__cxx11::path const&, chrono::batch_render::FrameOutput const&, bool)::{lambda(std::filesystem::__cxx11::path const&, chrono::batch_render::RenderVarOutput const&, long)#1}>(std::filesystem::__cxx11::path const&, chrono::batch_render::FrameOutput const&, bool, chrono::batch_render::WriteFrame(std::filesystem::__cxx11::path const&, chrono::batch_render::FrameOutput const&, bool)::{lambda(std::filesystem::__cxx11::path const&, chrono::batch_render::RenderVarOutput const&, long)#1}):
✓ Branch 1 taken 6 times.
✓ Branch 2 taken 3 times.
12 for (const auto& [path, renderVar] : frame.renderVars) {
198
2/4
std::vector<std::filesystem::__cxx11::path, std::allocator<std::filesystem::__cxx11::path> > chrono::batch_render::(anonymous namespace)::WriteVars<chrono::batch_render::ImageWriter::WriteFrame(std::filesystem::__cxx11::path const&, chrono::batch_render::FrameOutput const&, bool)::{lambda(std::filesystem::__cxx11::path const&, chrono::batch_render::RenderVarOutput const&, long)#1}>(std::filesystem::__cxx11::path const&, chrono::batch_render::FrameOutput const&, bool, chrono::batch_render::ImageWriter::WriteFrame(std::filesystem::__cxx11::path const&, chrono::batch_render::FrameOutput const&, bool)::{lambda(std::filesystem::__cxx11::path const&, chrono::batch_render::RenderVarOutput const&, long)#1}):
✗ Branch 1 not taken.
✓ Branch 2 taken 2 times.
std::vector<std::filesystem::__cxx11::path, std::allocator<std::filesystem::__cxx11::path> > chrono::batch_render::(anonymous namespace)::WriteVars<chrono::batch_render::WriteFrame(std::filesystem::__cxx11::path const&, chrono::batch_render::FrameOutput const&, bool)::{lambda(std::filesystem::__cxx11::path const&, chrono::batch_render::RenderVarOutput const&, long)#1}>(std::filesystem::__cxx11::path const&, chrono::batch_render::FrameOutput const&, bool, chrono::batch_render::WriteFrame(std::filesystem::__cxx11::path const&, chrono::batch_render::FrameOutput const&, bool)::{lambda(std::filesystem::__cxx11::path const&, chrono::batch_render::RenderVarOutput const&, long)#1}):
✗ Branch 1 not taken.
✓ Branch 2 taken 6 times.
8 const Span<const int64_t> shape = renderVar.tensor.Shape();
199
2/4
std::vector<std::filesystem::__cxx11::path, std::allocator<std::filesystem::__cxx11::path> > chrono::batch_render::(anonymous namespace)::WriteVars<chrono::batch_render::ImageWriter::WriteFrame(std::filesystem::__cxx11::path const&, chrono::batch_render::FrameOutput const&, bool)::{lambda(std::filesystem::__cxx11::path const&, chrono::batch_render::RenderVarOutput const&, long)#1}>(std::filesystem::__cxx11::path const&, chrono::batch_render::FrameOutput const&, bool, chrono::batch_render::ImageWriter::WriteFrame(std::filesystem::__cxx11::path const&, chrono::batch_render::FrameOutput const&, bool)::{lambda(std::filesystem::__cxx11::path const&, chrono::batch_render::RenderVarOutput const&, long)#1}):
✗ Branch 0 not taken.
✓ Branch 1 taken 2 times.
std::vector<std::filesystem::__cxx11::path, std::allocator<std::filesystem::__cxx11::path> > chrono::batch_render::(anonymous namespace)::WriteVars<chrono::batch_render::WriteFrame(std::filesystem::__cxx11::path const&, chrono::batch_render::FrameOutput const&, bool)::{lambda(std::filesystem::__cxx11::path const&, chrono::batch_render::RenderVarOutput const&, long)#1}>(std::filesystem::__cxx11::path const&, chrono::batch_render::FrameOutput const&, bool, chrono::batch_render::WriteFrame(std::filesystem::__cxx11::path const&, chrono::batch_render::FrameOutput const&, bool)::{lambda(std::filesystem::__cxx11::path const&, chrono::batch_render::RenderVarOutput const&, long)#1}):
✗ Branch 0 not taken.
✓ Branch 1 taken 6 times.
8 if (shape.size() < 3) {
200 ✗ continue;
201 }
202
2/2
std::vector<std::filesystem::__cxx11::path, std::allocator<std::filesystem::__cxx11::path> > chrono::batch_render::(anonymous namespace)::WriteVars<chrono::batch_render::ImageWriter::WriteFrame(std::filesystem::__cxx11::path const&, chrono::batch_render::FrameOutput const&, bool)::{lambda(std::filesystem::__cxx11::path const&, chrono::batch_render::RenderVarOutput const&, long)#1}>(std::filesystem::__cxx11::path const&, chrono::batch_render::FrameOutput const&, bool, chrono::batch_render::ImageWriter::WriteFrame(std::filesystem::__cxx11::path const&, chrono::batch_render::FrameOutput const&, bool)::{lambda(std::filesystem::__cxx11::path const&, chrono::batch_render::RenderVarOutput const&, long)#1}):
✓ Branch 1 taken 2 times.
std::vector<std::filesystem::__cxx11::path, std::allocator<std::filesystem::__cxx11::path> > chrono::batch_render::(anonymous namespace)::WriteVars<chrono::batch_render::WriteFrame(std::filesystem::__cxx11::path const&, chrono::batch_render::FrameOutput const&, bool)::{lambda(std::filesystem::__cxx11::path const&, chrono::batch_render::RenderVarOutput const&, long)#1}>(std::filesystem::__cxx11::path const&, chrono::batch_render::FrameOutput const&, bool, chrono::batch_render::WriteFrame(std::filesystem::__cxx11::path const&, chrono::batch_render::FrameOutput const&, bool)::{lambda(std::filesystem::__cxx11::path const&, chrono::batch_render::RenderVarOutput const&, long)#1}):
✓ Branch 1 taken 6 times.
8 const std::string name = path.substr(path.rfind('/') + 1);
203
2/8
std::vector<std::filesystem::__cxx11::path, std::allocator<std::filesystem::__cxx11::path> > chrono::batch_render::(anonymous namespace)::WriteVars<chrono::batch_render::ImageWriter::WriteFrame(std::filesystem::__cxx11::path const&, chrono::batch_render::FrameOutput const&, bool)::{lambda(std::filesystem::__cxx11::path const&, chrono::batch_render::RenderVarOutput const&, long)#1}>(std::filesystem::__cxx11::path const&, chrono::batch_render::FrameOutput const&, bool, chrono::batch_render::ImageWriter::WriteFrame(std::filesystem::__cxx11::path const&, chrono::batch_render::FrameOutput const&, bool)::{lambda(std::filesystem::__cxx11::path const&, chrono::batch_render::RenderVarOutput const&, long)#1}):
✗ Branch 0 not taken.
✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
std::vector<std::filesystem::__cxx11::path, std::allocator<std::filesystem::__cxx11::path> > chrono::batch_render::(anonymous namespace)::WriteVars<chrono::batch_render::WriteFrame(std::filesystem::__cxx11::path const&, chrono::batch_render::FrameOutput const&, bool)::{lambda(std::filesystem::__cxx11::path const&, chrono::batch_render::RenderVarOutput const&, long)#1}>(std::filesystem::__cxx11::path const&, chrono::batch_render::FrameOutput const&, bool, chrono::batch_render::WriteFrame(std::filesystem::__cxx11::path const&, chrono::batch_render::FrameOutput const&, bool)::{lambda(std::filesystem::__cxx11::path const&, chrono::batch_render::RenderVarOutput const&, long)#1}):
✗ Branch 0 not taken.
✓ Branch 1 taken 6 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
8 const bool exr = raw && renderVar.tensor.Type() != device::DType::UInt8;
204
4/4
std::vector<std::filesystem::__cxx11::path, std::allocator<std::filesystem::__cxx11::path> > chrono::batch_render::(anonymous namespace)::WriteVars<chrono::batch_render::ImageWriter::WriteFrame(std::filesystem::__cxx11::path const&, chrono::batch_render::FrameOutput const&, bool)::{lambda(std::filesystem::__cxx11::path const&, chrono::batch_render::RenderVarOutput const&, long)#1}>(std::filesystem::__cxx11::path const&, chrono::batch_render::FrameOutput const&, bool, chrono::batch_render::ImageWriter::WriteFrame(std::filesystem::__cxx11::path const&, chrono::batch_render::FrameOutput const&, bool)::{lambda(std::filesystem::__cxx11::path const&, chrono::batch_render::RenderVarOutput const&, long)#1}):
✓ Branch 0 taken 4 times.
✓ Branch 1 taken 2 times.
std::vector<std::filesystem::__cxx11::path, std::allocator<std::filesystem::__cxx11::path> > chrono::batch_render::(anonymous namespace)::WriteVars<chrono::batch_render::WriteFrame(std::filesystem::__cxx11::path const&, chrono::batch_render::FrameOutput const&, bool)::{lambda(std::filesystem::__cxx11::path const&, chrono::batch_render::RenderVarOutput const&, long)#1}>(std::filesystem::__cxx11::path const&, chrono::batch_render::FrameOutput const&, bool, chrono::batch_render::WriteFrame(std::filesystem::__cxx11::path const&, chrono::batch_render::FrameOutput const&, bool)::{lambda(std::filesystem::__cxx11::path const&, chrono::batch_render::RenderVarOutput const&, long)#1}):
✓ Branch 0 taken 12 times.
✓ Branch 1 taken 6 times.
24 for (int64_t camera = 0; camera < shape[0]; ++camera) {
205
8/10
std::vector<std::filesystem::__cxx11::path, std::allocator<std::filesystem::__cxx11::path> > chrono::batch_render::(anonymous namespace)::WriteVars<chrono::batch_render::ImageWriter::WriteFrame(std::filesystem::__cxx11::path const&, chrono::batch_render::FrameOutput const&, bool)::{lambda(std::filesystem::__cxx11::path const&, chrono::batch_render::RenderVarOutput const&, long)#1}>(std::filesystem::__cxx11::path const&, chrono::batch_render::FrameOutput const&, bool, chrono::batch_render::ImageWriter::WriteFrame(std::filesystem::__cxx11::path const&, chrono::batch_render::FrameOutput const&, bool)::{lambda(std::filesystem::__cxx11::path const&, chrono::batch_render::RenderVarOutput const&, long)#1}):
✓ Branch 0 taken 4 times.
✗ Branch 1 not taken.
✓ Branch 3 taken 4 times.
✓ Branch 6 taken 4 times.
✓ Branch 9 taken 4 times.
std::vector<std::filesystem::__cxx11::path, std::allocator<std::filesystem::__cxx11::path> > chrono::batch_render::(anonymous namespace)::WriteVars<chrono::batch_render::WriteFrame(std::filesystem::__cxx11::path const&, chrono::batch_render::FrameOutput const&, bool)::{lambda(std::filesystem::__cxx11::path const&, chrono::batch_render::RenderVarOutput const&, long)#1}>(std::filesystem::__cxx11::path const&, chrono::batch_render::FrameOutput const&, bool, chrono::batch_render::WriteFrame(std::filesystem::__cxx11::path const&, chrono::batch_render::FrameOutput const&, bool)::{lambda(std::filesystem::__cxx11::path const&, chrono::batch_render::RenderVarOutput const&, long)#1}):
✓ Branch 0 taken 12 times.
✗ Branch 1 not taken.
✓ Branch 3 taken 12 times.
✓ Branch 6 taken 12 times.
✓ Branch 9 taken 12 times.
48 const std::filesystem::path file =
206 directory / fmt::format("{}_{}{}", name, camera, exr ? ".exr" : ".png");
207
2/2
std::vector<std::filesystem::__cxx11::path, std::allocator<std::filesystem::__cxx11::path> > chrono::batch_render::(anonymous namespace)::WriteVars<chrono::batch_render::ImageWriter::WriteFrame(std::filesystem::__cxx11::path const&, chrono::batch_render::FrameOutput const&, bool)::{lambda(std::filesystem::__cxx11::path const&, chrono::batch_render::RenderVarOutput const&, long)#1}>(std::filesystem::__cxx11::path const&, chrono::batch_render::FrameOutput const&, bool, chrono::batch_render::ImageWriter::WriteFrame(std::filesystem::__cxx11::path const&, chrono::batch_render::FrameOutput const&, bool)::{lambda(std::filesystem::__cxx11::path const&, chrono::batch_render::RenderVarOutput const&, long)#1}):
✓ Branch 1 taken 4 times.
std::vector<std::filesystem::__cxx11::path, std::allocator<std::filesystem::__cxx11::path> > chrono::batch_render::(anonymous namespace)::WriteVars<chrono::batch_render::WriteFrame(std::filesystem::__cxx11::path const&, chrono::batch_render::FrameOutput const&, bool)::{lambda(std::filesystem::__cxx11::path const&, chrono::batch_render::RenderVarOutput const&, long)#1}>(std::filesystem::__cxx11::path const&, chrono::batch_render::FrameOutput const&, bool, chrono::batch_render::WriteFrame(std::filesystem::__cxx11::path const&, chrono::batch_render::FrameOutput const&, bool)::{lambda(std::filesystem::__cxx11::path const&, chrono::batch_render::RenderVarOutput const&, long)#1}):
✓ Branch 1 taken 12 times.
16 write(file, renderVar, camera);
208
2/2
std::vector<std::filesystem::__cxx11::path, std::allocator<std::filesystem::__cxx11::path> > chrono::batch_render::(anonymous namespace)::WriteVars<chrono::batch_render::ImageWriter::WriteFrame(std::filesystem::__cxx11::path const&, chrono::batch_render::FrameOutput const&, bool)::{lambda(std::filesystem::__cxx11::path const&, chrono::batch_render::RenderVarOutput const&, long)#1}>(std::filesystem::__cxx11::path const&, chrono::batch_render::FrameOutput const&, bool, chrono::batch_render::ImageWriter::WriteFrame(std::filesystem::__cxx11::path const&, chrono::batch_render::FrameOutput const&, bool)::{lambda(std::filesystem::__cxx11::path const&, chrono::batch_render::RenderVarOutput const&, long)#1}):
✓ Branch 1 taken 4 times.
std::vector<std::filesystem::__cxx11::path, std::allocator<std::filesystem::__cxx11::path> > chrono::batch_render::(anonymous namespace)::WriteVars<chrono::batch_render::WriteFrame(std::filesystem::__cxx11::path const&, chrono::batch_render::FrameOutput const&, bool)::{lambda(std::filesystem::__cxx11::path const&, chrono::batch_render::RenderVarOutput const&, long)#1}>(std::filesystem::__cxx11::path const&, chrono::batch_render::FrameOutput const&, bool, chrono::batch_render::WriteFrame(std::filesystem::__cxx11::path const&, chrono::batch_render::FrameOutput const&, bool)::{lambda(std::filesystem::__cxx11::path const&, chrono::batch_render::RenderVarOutput const&, long)#1}):
✓ Branch 1 taken 12 times.
16 written.push_back(file);
209 }
210 }
211
2/4
std::vector<std::filesystem::__cxx11::path, std::allocator<std::filesystem::__cxx11::path> > chrono::batch_render::(anonymous namespace)::WriteVars<chrono::batch_render::ImageWriter::WriteFrame(std::filesystem::__cxx11::path const&, chrono::batch_render::FrameOutput const&, bool)::{lambda(std::filesystem::__cxx11::path const&, chrono::batch_render::RenderVarOutput const&, long)#1}>(std::filesystem::__cxx11::path const&, chrono::batch_render::FrameOutput const&, bool, chrono::batch_render::ImageWriter::WriteFrame(std::filesystem::__cxx11::path const&, chrono::batch_render::FrameOutput const&, bool)::{lambda(std::filesystem::__cxx11::path const&, chrono::batch_render::RenderVarOutput const&, long)#1}):
✗ Branch 0 not taken.
✓ Branch 1 taken 1 time.
std::vector<std::filesystem::__cxx11::path, std::allocator<std::filesystem::__cxx11::path> > chrono::batch_render::(anonymous namespace)::WriteVars<chrono::batch_render::WriteFrame(std::filesystem::__cxx11::path const&, chrono::batch_render::FrameOutput const&, bool)::{lambda(std::filesystem::__cxx11::path const&, chrono::batch_render::RenderVarOutput const&, long)#1}>(std::filesystem::__cxx11::path const&, chrono::batch_render::FrameOutput const&, bool, chrono::batch_render::WriteFrame(std::filesystem::__cxx11::path const&, chrono::batch_render::FrameOutput const&, bool)::{lambda(std::filesystem::__cxx11::path const&, chrono::batch_render::RenderVarOutput const&, long)#1}):
✗ Branch 0 not taken.
✓ Branch 1 taken 3 times.
4 if (!frame.overlay.Empty()) {
212
0/6
std::vector<std::filesystem::__cxx11::path, std::allocator<std::filesystem::__cxx11::path> > chrono::batch_render::(anonymous namespace)::WriteVars<chrono::batch_render::ImageWriter::WriteFrame(std::filesystem::__cxx11::path const&, chrono::batch_render::FrameOutput const&, bool)::{lambda(std::filesystem::__cxx11::path const&, chrono::batch_render::RenderVarOutput const&, long)#1}>(std::filesystem::__cxx11::path const&, chrono::batch_render::FrameOutput const&, bool, chrono::batch_render::ImageWriter::WriteFrame(std::filesystem::__cxx11::path const&, chrono::batch_render::FrameOutput const&, bool)::{lambda(std::filesystem::__cxx11::path const&, chrono::batch_render::RenderVarOutput const&, long)#1}):
✗ Branch 1 not taken.
✗ Branch 4 not taken.
✗ Branch 7 not taken.
std::vector<std::filesystem::__cxx11::path, std::allocator<std::filesystem::__cxx11::path> > chrono::batch_render::(anonymous namespace)::WriteVars<chrono::batch_render::WriteFrame(std::filesystem::__cxx11::path const&, chrono::batch_render::FrameOutput const&, bool)::{lambda(std::filesystem::__cxx11::path const&, chrono::batch_render::RenderVarOutput const&, long)#1}>(std::filesystem::__cxx11::path const&, chrono::batch_render::FrameOutput const&, bool, chrono::batch_render::WriteFrame(std::filesystem::__cxx11::path const&, chrono::batch_render::FrameOutput const&, bool)::{lambda(std::filesystem::__cxx11::path const&, chrono::batch_render::RenderVarOutput const&, long)#1}):
✗ Branch 1 not taken.
✗ Branch 4 not taken.
✗ Branch 7 not taken.
✗ const RenderVarOutput overlay{"overlay", "Overlay", frame.overlay, nullptr};
213
0/4
std::vector<std::filesystem::__cxx11::path, std::allocator<std::filesystem::__cxx11::path> > chrono::batch_render::(anonymous namespace)::WriteVars<chrono::batch_render::ImageWriter::WriteFrame(std::filesystem::__cxx11::path const&, chrono::batch_render::FrameOutput const&, bool)::{lambda(std::filesystem::__cxx11::path const&, chrono::batch_render::RenderVarOutput const&, long)#1}>(std::filesystem::__cxx11::path const&, chrono::batch_render::FrameOutput const&, bool, chrono::batch_render::ImageWriter::WriteFrame(std::filesystem::__cxx11::path const&, chrono::batch_render::FrameOutput const&, bool)::{lambda(std::filesystem::__cxx11::path const&, chrono::batch_render::RenderVarOutput const&, long)#1}):
✗ Branch 1 not taken.
✗ Branch 2 not taken.
std::vector<std::filesystem::__cxx11::path, std::allocator<std::filesystem::__cxx11::path> > chrono::batch_render::(anonymous namespace)::WriteVars<chrono::batch_render::WriteFrame(std::filesystem::__cxx11::path const&, chrono::batch_render::FrameOutput const&, bool)::{lambda(std::filesystem::__cxx11::path const&, chrono::batch_render::RenderVarOutput const&, long)#1}>(std::filesystem::__cxx11::path const&, chrono::batch_render::FrameOutput const&, bool, chrono::batch_render::WriteFrame(std::filesystem::__cxx11::path const&, chrono::batch_render::FrameOutput const&, bool)::{lambda(std::filesystem::__cxx11::path const&, chrono::batch_render::RenderVarOutput const&, long)#1}):
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✗ for (int64_t camera = 0; camera < frame.overlay.Shape()[0]; ++camera) {
214
0/6
std::vector<std::filesystem::__cxx11::path, std::allocator<std::filesystem::__cxx11::path> > chrono::batch_render::(anonymous namespace)::WriteVars<chrono::batch_render::ImageWriter::WriteFrame(std::filesystem::__cxx11::path const&, chrono::batch_render::FrameOutput const&, bool)::{lambda(std::filesystem::__cxx11::path const&, chrono::batch_render::RenderVarOutput const&, long)#1}>(std::filesystem::__cxx11::path const&, chrono::batch_render::FrameOutput const&, bool, chrono::batch_render::ImageWriter::WriteFrame(std::filesystem::__cxx11::path const&, chrono::batch_render::FrameOutput const&, bool)::{lambda(std::filesystem::__cxx11::path const&, chrono::batch_render::RenderVarOutput const&, long)#1}):
✗ Branch 1 not taken.
✗ Branch 4 not taken.
✗ Branch 7 not taken.
std::vector<std::filesystem::__cxx11::path, std::allocator<std::filesystem::__cxx11::path> > chrono::batch_render::(anonymous namespace)::WriteVars<chrono::batch_render::WriteFrame(std::filesystem::__cxx11::path const&, chrono::batch_render::FrameOutput const&, bool)::{lambda(std::filesystem::__cxx11::path const&, chrono::batch_render::RenderVarOutput const&, long)#1}>(std::filesystem::__cxx11::path const&, chrono::batch_render::FrameOutput const&, bool, chrono::batch_render::WriteFrame(std::filesystem::__cxx11::path const&, chrono::batch_render::FrameOutput const&, bool)::{lambda(std::filesystem::__cxx11::path const&, chrono::batch_render::RenderVarOutput const&, long)#1}):
✗ Branch 1 not taken.
✗ Branch 4 not taken.
✗ Branch 7 not taken.
✗ const std::filesystem::path file = directory / fmt::format("overlay_{}.png", camera);
215
0/2
std::vector<std::filesystem::__cxx11::path, std::allocator<std::filesystem::__cxx11::path> > chrono::batch_render::(anonymous namespace)::WriteVars<chrono::batch_render::ImageWriter::WriteFrame(std::filesystem::__cxx11::path const&, chrono::batch_render::FrameOutput const&, bool)::{lambda(std::filesystem::__cxx11::path const&, chrono::batch_render::RenderVarOutput const&, long)#1}>(std::filesystem::__cxx11::path const&, chrono::batch_render::FrameOutput const&, bool, chrono::batch_render::ImageWriter::WriteFrame(std::filesystem::__cxx11::path const&, chrono::batch_render::FrameOutput const&, bool)::{lambda(std::filesystem::__cxx11::path const&, chrono::batch_render::RenderVarOutput const&, long)#1}):
✗ Branch 1 not taken.
std::vector<std::filesystem::__cxx11::path, std::allocator<std::filesystem::__cxx11::path> > chrono::batch_render::(anonymous namespace)::WriteVars<chrono::batch_render::WriteFrame(std::filesystem::__cxx11::path const&, chrono::batch_render::FrameOutput const&, bool)::{lambda(std::filesystem::__cxx11::path const&, chrono::batch_render::RenderVarOutput const&, long)#1}>(std::filesystem::__cxx11::path const&, chrono::batch_render::FrameOutput const&, bool, chrono::batch_render::WriteFrame(std::filesystem::__cxx11::path const&, chrono::batch_render::FrameOutput const&, bool)::{lambda(std::filesystem::__cxx11::path const&, chrono::batch_render::RenderVarOutput const&, long)#1}):
✗ Branch 1 not taken.
✗ write(file, overlay, camera);
216
0/2
std::vector<std::filesystem::__cxx11::path, std::allocator<std::filesystem::__cxx11::path> > chrono::batch_render::(anonymous namespace)::WriteVars<chrono::batch_render::ImageWriter::WriteFrame(std::filesystem::__cxx11::path const&, chrono::batch_render::FrameOutput const&, bool)::{lambda(std::filesystem::__cxx11::path const&, chrono::batch_render::RenderVarOutput const&, long)#1}>(std::filesystem::__cxx11::path const&, chrono::batch_render::FrameOutput const&, bool, chrono::batch_render::ImageWriter::WriteFrame(std::filesystem::__cxx11::path const&, chrono::batch_render::FrameOutput const&, bool)::{lambda(std::filesystem::__cxx11::path const&, chrono::batch_render::RenderVarOutput const&, long)#1}):
✗ Branch 1 not taken.
std::vector<std::filesystem::__cxx11::path, std::allocator<std::filesystem::__cxx11::path> > chrono::batch_render::(anonymous namespace)::WriteVars<chrono::batch_render::WriteFrame(std::filesystem::__cxx11::path const&, chrono::batch_render::FrameOutput const&, bool)::{lambda(std::filesystem::__cxx11::path const&, chrono::batch_render::RenderVarOutput const&, long)#1}>(std::filesystem::__cxx11::path const&, chrono::batch_render::FrameOutput const&, bool, chrono::batch_render::WriteFrame(std::filesystem::__cxx11::path const&, chrono::batch_render::FrameOutput const&, bool)::{lambda(std::filesystem::__cxx11::path const&, chrono::batch_render::RenderVarOutput const&, long)#1}):
✗ Branch 1 not taken.
✗ written.push_back(file);
217 }
218 ✗ }
219 4 return written;
220 ✗ }
221
222 }
223
224 3 std::vector<std::filesystem::path> WriteFrame(const std::filesystem::path& directory,
225 const FrameOutput& frame, bool raw) {
226 3 return WriteVars(directory, frame, raw,
227 12 [](const std::filesystem::path& file, const RenderVarOutput& renderVar,
228
1/1
✓ Branch 3 taken 12 times.
39 int64_t camera) { WriteRenderVar(file, renderVar, camera); });
229 }
230
231 struct ImageWriter::State {
232 std::mutex mutex;
233 std::condition_variable changed;
234 std::deque<std::function<void()>> jobs;
235 std::vector<std::thread> threads;
236 uint32_t maxPending = 0;
237 size_t pending = 0;
238 std::exception_ptr error;
239 bool stopping = false;
240
241 2 void Run() {
242 2 std::unique_lock lock(mutex);
243 10 while (true) {
244
5/5
✓ Branch 0 taken 13 times.
✓ Branch 1 taken 2 times.
✓ Branch 2 taken 9 times.
✓ Branch 3 taken 4 times.
✓ Branch 5 taken 6 times.
21 changed.wait(lock, [&] { return stopping || !jobs.empty(); });
245
2/2
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 4 times.
6 if (jobs.empty()) {
246
1/2
✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
2 return;
247 }
248 4 std::function<void()> job = std::move(jobs.front());
249 4 jobs.pop_front();
250
1/1
✓ Branch 1 taken 4 times.
4 lock.unlock();
251
1/1
✓ Branch 1 taken 4 times.
4 std::exception_ptr failed;
252 4 try {
253
1/1
✓ Branch 1 taken 4 times.
4 job();
254 ✗ } catch (...) {
255
0/2
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✗ failed = std::current_exception();
256 ✗ }
257 4 job = nullptr;
258
1/1
✓ Branch 1 taken 4 times.
4 lock.lock();
259
1/4
✗ Branch 0 not taken.
✓ Branch 1 taken 4 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
4 if (failed && !error) {
260 ✗ error = failed;
261 }
262 4 --pending;
263
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 4 times.
4 changed.notify_all();
264 4 }
265 2 }
266
267 4 void Submit(std::function<void()> job) {
268 4 std::unique_lock lock(mutex);
269
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 4 times.
4 changed.wait(lock, [&] { return pending < maxPending; });
270 4 ++pending;
271
1/1
✓ Branch 1 taken 4 times.
4 jobs.push_back(std::move(job));
272
1/2
✓ Branch 1 taken 4 times.
✗ Branch 2 not taken.
4 changed.notify_all();
273 4 }
274
275 2 void Drain() {
276 2 std::unique_lock lock(mutex);
277
3/4
✓ Branch 0 taken 1 time.
✓ Branch 1 taken 2 times.
✓ Branch 2 taken 2 times.
✗ Branch 3 not taken.
3 changed.wait(lock, [&] { return pending == 0; });
278 2 }
279 };
280
281 1 ImageWriter::ImageWriter(uint32_t threads, uint32_t maxPending)
282 1 : m_state(std::make_unique<State>()) {
283
2/4
✓ Branch 0 taken 1 time.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 1 time.
1 if (threads == 0 || maxPending == 0) {
284 ✗ throw Error(
285 "An image writer needs at least one thread and one pending write, not {} and {}",
286
0/1
✗ Branch 2 not taken.
✗ threads, maxPending);
287 }
288 1 m_state->maxPending = maxPending;
289
2/2
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 1 time.
3 for (uint32_t i = 0; i < threads; ++i) {
290
1/1
✓ Branch 2 taken 2 times.
4 m_state->threads.emplace_back([state = m_state.get()] { state->Run(); });
291 }
292 1 }
293
294 1 ImageWriter::~ImageWriter() {
295 1 m_state->Drain();
296 1 {
297 1 const std::lock_guard lock(m_state->mutex);
298 1 m_state->stopping = true;
299 1 }
300 1 m_state->changed.notify_all();
301
2/2
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 1 time.
3 for (std::thread& thread : m_state->threads) {
302 2 thread.join();
303 }
304
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 time.
1 if (m_state->error) {
305 ✗ try {
306
0/2
✗ Branch 0 not taken.
✗ Branch 1 not taken.
✗ std::rethrow_exception(m_state->error);
307
0/2
✗ Branch 0 not taken.
✗ Branch 1 not taken.
✗ } catch (const std::exception& error) {
308 ✗ Log(LogLevel::Error, "an image writer dropped a failed write: {}", error.what());
309 ✗ }
310 }
311 1 }
312
313 ✗ void ImageWriter::WriteImage(std::filesystem::path path, Pixels image) {
314
0/2
✗ Branch 2 not taken.
✗ Branch 5 not taken.
✗ m_state->Submit([path = std::move(path), image = std::move(image)] {
315
0/1
✗ Branch 2 not taken.
✗ chrono::batch_render::WriteImage(path, image);
316 ✗ });
317 ✗ }
318
319 ✗ void ImageWriter::WriteImage(std::filesystem::path path, const device::Tensor& image) {
320
0/3
✗ Branch 2 not taken.
✗ Branch 5 not taken.
✗ Branch 8 not taken.
✗ m_state->Submit([path = std::move(path), fetch = Fetch(image)] {
321
0/2
✗ Branch 2 not taken.
✗ Branch 5 not taken.
✗ chrono::batch_render::WriteImage(path, fetch());
322 ✗ });
323 ✗ }
324
325 4 void ImageWriter::WriteRenderVar(std::filesystem::path path, const RenderVarOutput& renderVar,
326 int64_t camera) {
327 4 const device::Tensor image = renderVar.tensor.Select(camera);
328
1/1
✓ Branch 1 taken 4 times.
4 device::Tensor shown = image;
329
3/4
✓ Branch 1 taken 4 times.
✓ Branch 4 taken 4 times.
✓ Branch 9 taken 4 times.
✗ Branch 10 not taken.
12 if (Lowercase(path.extension().string()) != ".exr") {
330
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 4 times.
4 if (!path.has_extension()) {
331
0/1
✗ Branch 1 not taken.
✗ path += ".png";
332 }
333
1/1
✓ Branch 1 taken 4 times.
4 shown = Viewable(renderVar.sourceName, image.Type(), image);
334 }
335 4 const Span<const int64_t> shape = image.Shape();
336
3/4
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 2 times.
4 const bool single = shape.size() == 2 || shape[2] == 1;
337
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 4 times.
4 std::shared_ptr<const IdMap> labels =
338
1/4
✗ Branch 0 not taken.
✓ Branch 1 taken 4 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
4 image.Type() == device::DType::UInt32 ? renderVar.idMap : nullptr;
339
3/5
✓ Branch 2 taken 4 times.
✓ Branch 5 taken 4 times.
✓ Branch 8 taken 4 times.
✗ Branch 13 not taken.
✗ Branch 14 not taken.
8 m_state->Submit([path = std::move(path), labels = std::move(labels), single,
340 fetch = Fetch(shown)] {
341
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 4 times.
4 if (labels) {
342
0/3
✗ Branch 2 not taken.
✗ Branch 5 not taken.
✗ Branch 8 not taken.
✗ WriteLabels(std::filesystem::path(path).replace_extension(".json"), *labels, single);
343 }
344
2/2
✓ Branch 2 taken 4 times.
✓ Branch 5 taken 4 times.
12 chrono::batch_render::WriteImage(path, fetch());
345 4 });
346 4 }
347
348 1 std::vector<std::filesystem::path> ImageWriter::WriteFrame(const std::filesystem::path& directory,
349 const FrameOutput& frame, bool raw) {
350 1 return WriteVars(directory, frame, raw,
351 4 [this](const std::filesystem::path& file, const RenderVarOutput& renderVar,
352
1/1
✓ Branch 3 taken 4 times.
9 int64_t camera) { WriteRenderVar(file, renderVar, camera); });
353 }
354
355 1 void ImageWriter::Wait() {
356 1 m_state->Drain();
357 1 const std::lock_guard lock(m_state->mutex);
358
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 time.
1 if (m_state->error) {
359 ✗ std::exception_ptr error = std::exchange(m_state->error, nullptr);
360
0/4
✗ Branch 0 not taken.
✗ Branch 1 not taken.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✗ std::rethrow_exception(error);
361 ✗ }
362 1 }
363
364 1 size_t ImageWriter::Pending() const {
365 1 const std::lock_guard lock(m_state->mutex);
366 1 return m_state->pending;
367 1 }
368
369 3381 const ProductOutput& RenderOutput::operator[](std::string_view product) const {
370 3381 const auto found = m_products.find(product);
371
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 3381 times.
3381 if (found == m_products.end()) {
372
0/1
✗ Branch 2 not taken.
✗ throw Error("The step rendered no product {}", product);
373 }
374 3381 return found->second;
375 }
376
377 }
378