GCC Code Coverage Report


ernie/
Coverage:
low: ≥ 0%
medium: ≥ 75.0%
high: ≥ 90.0%
Lines:
59 / 61
96.7%
Branches:
49 / 92
53.3%
Functions:
6 / 6
100.0%

render/renderer.cpp
Line Branch Exec Source
1 // SPDX-License-Identifier: BSD-3-Clause
2 /// @file renderer.cpp
3 /// @brief Rendering a scene's render settings: the device scene brought up to date, the camera
4 /// traced into a column, and the column read back.
5 #include "ernie/render/renderer.h"
6
7 #include "ernie/device/bindless.h"
8 #include "ernie/device/context.h"
9 #include "ernie/device/staging.h"
10 #include "ernie/render/device_scene.h"
11 #include "ernie/render/rt/camera_pass.h"
12 #include "ernie/scene/schema/camera.h"
13 #include "ernie/scene/schema/render.h"
14 #include "ernie/util/profiler.h"
15
16 #include <algorithm>
17 #include <cmath>
18 #include <cstring>
19 #include <stdexcept>
20 #include <vector>
21
22 namespace ernie {
23 namespace {
24
25 /// @return The camera a render setting names, as a pinhole view of `size` pixels. The vertical
26 /// field of view widens until the aperture's width fits too, as `expandAperture` conforms it.
27 3 PinholeView ReadPinholeView(const Scene& scene, const Path& path, glm::uvec2 size) {
28 3 const Camera camera(scene, path);
29
1/2
✓ Branch 1 taken 3 times.
✗ Branch 2 not taken.
3 const double focal = camera.GetFocalLength();
30 3 const double aspect = static_cast<double>(size.x) / static_cast<double>(size.y);
31
1/2
✓ Branch 0 taken 3 times.
✗ Branch 1 not taken.
3 const double tangent = std::max(0.5 * camera.GetVerticalAperture() / focal,
32
2/4
✓ Branch 1 taken 3 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 3 times.
✗ Branch 5 not taken.
3 0.5 * camera.GetHorizontalAperture() / focal / aspect);
33
1/2
✓ Branch 1 taken 3 times.
✗ Branch 2 not taken.
3 const std::optional<PrimId> prim = scene.Find(path);
34
1/2
✓ Branch 1 taken 3 times.
✗ Branch 2 not taken.
3 return {.cameraToWorld = scene.WorldTransform(*prim),
35 3 .verticalFov = 2.0 * std::atan(tangent),
36 3 .size = size};
37 3 }
38
39 } // namespace
40
41 /// What a renderer keeps on its device.
42 struct Renderer::Device {
43 ShaderCompiler compiler;
44 std::unique_ptr<DeviceContext> context;
45 std::unique_ptr<Arenas> arenas;
46 std::unique_ptr<BindlessHeap> heap;
47 std::unique_ptr<CameraPass> camera;
48 /// The scene `deviceScene` follows, by `Scene::Id`: an address can be a new scene's.
49 std::uint64_t sceneId = 0;
50 std::unique_ptr<DeviceScene> deviceScene;
51 std::unique_ptr<DeviceColumn> ldr;
52 };
53
54 3 Renderer::Renderer(const RendererOptions& options) : device_(std::make_unique<Device>()) {
55
1/2
✓ Branch 1 taken 3 times.
✗ Branch 2 not taken.
3 Device& device = *device_;
56 12 device.context = std::make_unique<DeviceContext>(
57
2/4
✓ Branch 1 taken 3 times.
✗ Branch 2 not taken.
✓ Branch 5 taken 3 times.
✗ Branch 6 not taken.
3 DeviceContextOptions{.validation = options.validation, .device = options.device});
58
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 3 times.
3 if (!device.context->Capabilities().rayQuery) {
59 ✗ throw DeviceError(DeviceErrorKind::Unsupported,
60
0/2
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✗ device.context->Info().name + " cannot ray trace");
61 }
62 9 device.arenas = std::make_unique<Arenas>(*device.context, MemoryUse::Device, "renderer",
63
1/2
✓ Branch 1 taken 3 times.
✗ Branch 2 not taken.
3 std::uint64_t{16} << 20);
64
1/2
✓ Branch 1 taken 3 times.
✗ Branch 2 not taken.
3 device.heap = std::make_unique<BindlessHeap>(*device.context);
65
1/2
✓ Branch 1 taken 3 times.
✗ Branch 2 not taken.
3 device.camera = std::make_unique<CameraPass>(*device.context, device.compiler, *device.heap);
66
1/2
✓ Branch 1 taken 3 times.
✗ Branch 2 not taken.
3 device.ldr = std::make_unique<DeviceColumn>(*device.arenas, "ldr colour", DType::UInt8,
67
1/2
✓ Branch 1 taken 3 times.
✗ Branch 2 not taken.
6 std::vector<std::int64_t>{4}, 0);
68 3 }
69
70 3 Renderer::~Renderer() {
71 3 device_->context->WaitIdle();
72 3 }
73
74 2 DeviceContext& Renderer::Context() {
75 2 return *device_->context;
76 }
77
78 5 Tensor Renderer::Render(const Scene& scene) {
79 5 ERNIE_ZONE("Render");
80
1/2
✓ Branch 1 taken 5 times.
✗ Branch 2 not taken.
5 const std::vector<Path> settings = scene.Prims(RenderSettings::kName);
81
2/2
✓ Branch 0 taken 1 time.
✓ Branch 1 taken 4 times.
5 if (settings.empty()) {
82
1/2
✓ Branch 2 taken 1 time.
✗ Branch 3 not taken.
1 throw std::invalid_argument("The scene has no RenderSettings to render");
83 }
84
1/2
✓ Branch 1 taken 4 times.
✗ Branch 2 not taken.
4 const RenderSettings render(scene, settings.front());
85
1/2
✓ Branch 1 taken 4 times.
✗ Branch 2 not taken.
4 const Path camera = render.GetCamera();
86
4/6
✓ Branch 0 taken 3 times.
✓ Branch 1 taken 1 time.
✓ Branch 3 taken 3 times.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
✓ Branch 6 taken 3 times.
4 if (camera.IsEmpty() || !scene.Find(camera)) {
87
2/4
✓ Branch 2 taken 1 time.
✗ Branch 3 not taken.
✓ Branch 5 taken 1 time.
✗ Branch 6 not taken.
3 throw std::invalid_argument("'" + settings.front().String() + "' names no camera");
88 }
89
1/2
✓ Branch 1 taken 3 times.
✗ Branch 2 not taken.
3 const glm::dvec2 resolution = render.GetResolution();
90
3/6
✓ Branch 0 taken 3 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 3 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 3 times.
✗ Branch 6 not taken.
9 const glm::uvec2 size(std::max(1.0, resolution.x), std::max(1.0, resolution.y));
91
1/2
✓ Branch 1 taken 3 times.
✗ Branch 2 not taken.
3 const PinholeView view = ReadPinholeView(scene, camera, size);
92
93
2/2
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 1 time.
3 Device& device = *device_;
94
2/2
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 1 time.
3 if (scene.Id() != device.sceneId) {
95
1/2
✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
2 device.context->WaitIdle();
96 2 device.deviceScene =
97
1/2
✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
2 std::make_unique<DeviceScene>(*device.context, *device.arenas, scene, device.compiler);
98 2 device.sceneId = scene.Id();
99 }
100 3 const std::size_t pixels = std::size_t{size.x} * size.y;
101
1/2
✓ Branch 1 taken 3 times.
✗ Branch 2 not taken.
3 device.ldr->Resize(pixels, device.context->Main());
102
1/2
✓ Branch 1 taken 3 times.
✗ Branch 2 not taken.
3 device.deviceScene->Follow(glm::dvec3(view.cameraToWorld[3]));
103
1/2
✓ Branch 1 taken 3 times.
✗ Branch 2 not taken.
3 const Ticket done = device.deviceScene->Execute([&](graph::Graph& graph) {
104 3 CameraOutputs outputs{.ldr = graph.Import(*device.ldr, device.ldr->Name())};
105 3 device.camera->Add(graph, view, outputs);
106 3 });
107
2/4
✓ Branch 1 taken 3 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 3 times.
✗ Branch 5 not taken.
3 Readback readback(*device.context, device.context->Main(), device.ldr->Storage(),
108
2/4
✓ Branch 1 taken 3 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 3 times.
✗ Branch 5 not taken.
4 device.ldr->Range().offset, pixels * 4, std::span(&done, 1));
109 3 return Tensor::Copy(
110 std::span<const std::uint8_t>(
111
2/4
✓ Branch 1 taken 3 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 3 times.
✗ Branch 5 not taken.
3 reinterpret_cast<const std::uint8_t*>(readback.Bytes().data()), pixels * 4),
112
2/4
✓ Branch 1 taken 3 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 3 times.
✗ Branch 5 not taken.
9 {size.y, size.x, 4});
113 8 }
114
115 } // namespace ernie
116