GCC Code Coverage Report


ernie/
Coverage:
low: ≥ 0%
medium: ≥ 75.0%
high: ≥ 90.0%
Lines:
72 / 74
97.3%
Branches:
44 / 66
66.7%
Functions:
9 / 9
100.0%

render/rt/camera_pass.cpp
Line Branch Exec Source
1 // SPDX-License-Identifier: BSD-3-Clause
2 /// @file camera_pass.cpp
3 /// @brief Tracing pinhole cameras as a frame-graph pass.
4 #include "ernie/render/rt/camera_pass.h"
5
6 #include "ernie/device/vulkan.h"
7 #include "ernie/render/rt/ray_tracer.h"
8 #include "ernie/shaders/device/bindless.h"
9 #include "ernie/shaders/sensors/camera.h"
10 #include "ernie/shaders/spectral/spectrum.h"
11 #include "ernie/util/math.h"
12
13 #include <glm/gtc/matrix_transform.hpp>
14
15 #include <algorithm>
16 #include <cmath>
17 #include <stdexcept>
18 #include <vector>
19
20 namespace ernie {
21 namespace {
22
23 using shaders::CameraParameters;
24 using shaders::View;
25
26 /// The most `vkCmdUpdateBuffer` writes at once.
27 constexpr std::size_t kUpdateBytes = 65536;
28
29 10 std::vector<std::uint32_t> Compile(const ShaderCompiler& compiler) {
30
1/2
✓ Branch 4 taken 10 times.
✗ Branch 5 not taken.
10 std::vector<ShaderModule> modules(RayTracer::Modules().begin(), RayTracer::Modules().end());
31
3/6
✗ Branch 1 not taken.
✓ Branch 2 taken 10 times.
✓ Branch 3 taken 30 times.
✓ Branch 4 taken 10 times.
✗ Branch 6 not taken.
✗ Branch 7 not taken.
40 modules.insert(modules.end(), {shaders::kBindless, shaders::kSpectrum, shaders::kCamera});
32
1/2
✓ Branch 1 taken 10 times.
✗ Branch 2 not taken.
20 return compiler.Spirv(modules, "main");
33
3/8
✓ Branch 1 taken 10 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 10 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 10 times.
✗ Branch 8 not taken.
✗ Branch 10 not taken.
✗ Branch 11 not taken.
20 }
34
35 /// @return A view's record, its output addresses left for the pass to fill in once the graph has
36 /// placed them.
37 /// @return A kernel's handle of an output, or a null one when it is left out.
38 template <typename T>
39
4/4
ernie::shaders::BufferRef<float> ernie::(anonymous namespace)::BufferRefOrNull<float>(ernie::graph::PassContext const&, std::optional<ernie::graph::Buffer> const&):
✓ Branch 0 taken 20 times.
✓ Branch 1 taken 31 times.
ernie::shaders::BufferRef<unsigned int> ernie::(anonymous namespace)::BufferRefOrNull<unsigned int>(ernie::graph::PassContext const&, std::optional<ernie::graph::Buffer> const&):
✓ Branch 0 taken 16 times.
✓ Branch 1 taken 18 times.
85 shaders::BufferRef<T> BufferRefOrNull(const graph::PassContext& pass,
40 const std::optional<graph::Buffer>& buffer) {
41
4/4
ernie::shaders::BufferRef<float> ernie::(anonymous namespace)::BufferRefOrNull<float>(ernie::graph::PassContext const&, std::optional<ernie::graph::Buffer> const&):
✓ Branch 0 taken 20 times.
✓ Branch 1 taken 31 times.
ernie::shaders::BufferRef<unsigned int> ernie::(anonymous namespace)::BufferRefOrNull<unsigned int>(ernie::graph::PassContext const&, std::optional<ernie::graph::Buffer> const&):
✓ Branch 0 taken 16 times.
✓ Branch 1 taken 18 times.
85 return buffer ? pass.Get(*buffer).BufferRef<T>() : shaders::BufferRef<T>{};
42 }
43
44 17 View ToView(const PinholeView& view, const CameraOutputs& outputs, const glm::dvec3& origin) {
45 // Relative to the origin in double, then rounded: values near the camera stay small.
46 17 const glm::dmat4 relative = glm::translate(glm::dmat4(1.0), -origin) * view.cameraToWorld;
47 17 const double tangent = std::tan(0.5 * view.verticalFov);
48 17 const double aspect = static_cast<double>(view.size.x) / static_cast<double>(view.size.y);
49 17 const std::array<glm::vec4, 3> rows = ToAffineRows(relative);
50 17 return {.rows = {rows[0], rows[1], rows[2]},
51 .halfExtent = glm::vec2(tangent * aspect, tangent),
52 .size = view.size,
53 .background = outputs.background,
54
2/2
✓ Branch 0 taken 12 times.
✓ Branch 1 taken 5 times.
17 .colour = outputs.colour ? outputs.colourIndex.value : shaders::kNoImage};
55 }
56
57 } // namespace
58
59 10 CameraPass::CameraPass(DeviceContext& context, const ShaderCompiler& compiler,
60 10 const BindlessHeap& heap)
61
1/2
✓ Branch 2 taken 10 times.
✗ Branch 3 not taken.
10 : kernel_(context, Compile(compiler), "camera", sizeof(CameraParameters), &heap) {}
62
63 15 void CameraPass::Add(graph::Graph& graph, const PinholeView& view, CameraOutputs& outputs) const {
64 15 CameraView one{view, outputs};
65 15 Add(graph, std::span(&one, 1));
66 15 outputs = one.outputs;
67 15 }
68
69 16 void CameraPass::Add(graph::Graph& graph, std::span<CameraView> views) const {
70 16 const TraceScene* scene = graph.Board().Find<TraceScene>();
71
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 16 times.
16 if (scene == nullptr) {
72
0/2
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✗ throw std::invalid_argument("A camera pass needs the ray tracer's TraceScene");
73 }
74
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 16 times.
16 if (views.empty()) {
75 ✗ return;
76 }
77 16 std::vector<View> records;
78 16 glm::uvec2 largest{0};
79
2/2
✓ Branch 0 taken 17 times.
✓ Branch 1 taken 16 times.
33 for (const CameraView& view : views) {
80
1/2
✓ Branch 2 taken 17 times.
✗ Branch 3 not taken.
17 records.push_back(ToView(view.view, view.outputs, scene->origin));
81 17 largest = glm::max(largest, view.view.size);
82 }
83
1/2
✓ Branch 1 taken 16 times.
✗ Branch 2 not taken.
16 graph::Buffer staged = graph.Create(records.size() * sizeof(View), "camera views");
84 // For the recording to look up: any version of a resource names it.
85 16 std::vector<CameraOutputs> outputs;
86
2/2
✓ Branch 0 taken 17 times.
✓ Branch 1 taken 16 times.
33 for (const CameraView& view : views) {
87
1/2
✓ Branch 1 taken 17 times.
✗ Branch 2 not taken.
17 outputs.push_back(view.outputs);
88 }
89 32 graph.Add(
90 "Camera", graph::Kind::Compute,
91 48 [&](graph::PassBuilder& pass) {
92 16 scene->Read(pass);
93 // Written by the pass itself before its dispatch reads it.
94 16 staged = pass.WriteAddress(staged, graph::Use::StorageWrite);
95
2/2
✓ Branch 0 taken 17 times.
✓ Branch 1 taken 16 times.
33 for (CameraView& view : views) {
96 17 CameraOutputs& output = view.outputs;
97
2/2
✓ Branch 0 taken 12 times.
✓ Branch 1 taken 5 times.
17 if (output.colour) {
98
1/2
✓ Branch 1 taken 12 times.
✗ Branch 2 not taken.
24 output.colour = pass.Write(*output.colour, graph::Use::StorageWrite);
99 }
100 85 for (std::optional<graph::Buffer>* buffer :
101 17 {&output.distances, &output.normals, &output.instances, &output.albedos,
102
2/2
✓ Branch 0 taken 85 times.
✓ Branch 1 taken 17 times.
102 &output.ldr}) {
103
2/2
✓ Branch 0 taken 36 times.
✓ Branch 1 taken 49 times.
85 if (*buffer) {
104 72 *buffer = pass.WriteAddress(**buffer, graph::Use::StorageWrite);
105 }
106 }
107 }
108 16 },
109
1/2
✓ Branch 1 taken 16 times.
✗ Branch 2 not taken.
16 [this, staged, largest, records = std::move(records), outputs = std::move(outputs),
110 parameters =
111
1/2
✓ Branch 4 taken 16 times.
✗ Branch 5 not taken.
32 CameraParameters{.tracer = scene->parameters, .colours = scene->colourBuffer}](
112 graph::PassContext& pass) mutable {
113
2/2
✓ Branch 0 taken 17 times.
✓ Branch 1 taken 16 times.
33 for (std::size_t i = 0; i < records.size(); ++i) {
114 17 records[i].distances = BufferRefOrNull<float>(pass, outputs[i].distances);
115 17 records[i].normals = BufferRefOrNull<float>(pass, outputs[i].normals);
116 17 records[i].instances = BufferRefOrNull<shaders::uint>(pass, outputs[i].instances);
117 17 records[i].albedos = BufferRefOrNull<float>(pass, outputs[i].albedos);
118 17 records[i].ldr = BufferRefOrNull<shaders::uint>(pass, outputs[i].ldr);
119 }
120 // The records hold the outputs' addresses, known only now, so the pass writes them
121 // itself before it reads them.
122 16 const graph::BufferSlice slice = pass.Get(staged);
123 16 const auto* bytes = reinterpret_cast<const std::byte*>(records.data());
124 16 const std::size_t size = records.size() * sizeof(View);
125
2/2
✓ Branch 0 taken 16 times.
✓ Branch 1 taken 16 times.
32 for (std::size_t at = 0; at < size; at += kUpdateBytes) {
126 32 vkCmdUpdateBuffer(pass.commands, slice.buffer->Handle(), slice.offset + at,
127
1/2
✓ Branch 0 taken 16 times.
✗ Branch 1 not taken.
32 std::min(kUpdateBytes, size - at), bytes + at);
128 }
129 16 Barrier(pass.commands, VK_PIPELINE_STAGE_2_COPY_BIT, VK_ACCESS_2_TRANSFER_WRITE_BIT,
130 VK_PIPELINE_STAGE_2_COMPUTE_SHADER_BIT, VK_ACCESS_2_SHADER_STORAGE_READ_BIT);
131 16 parameters.views = slice.BufferRef<View>();
132 16 parameters.count = static_cast<std::uint32_t>(records.size());
133 32 kernel_.Dispatch(
134 pass.commands,
135 {DivideRoundUp(largest.x, 8u), DivideRoundUp(largest.y, 8u), parameters.count},
136 16 parameters);
137 16 });
138 16 }
139
140 } // namespace ernie
141