GCC Code Coverage Report


ernie/
Coverage:
low: ≥ 0%
medium: ≥ 75.0%
high: ≥ 90.0%
Lines:
72 / 72
100.0%
Branches:
32 / 48
66.7%
Functions:
12 / 12
100.0%

render/rt/top_levels.cpp
Line Branch Exec Source
1 // SPDX-License-Identifier: BSD-3-Clause
2 /// @file top_levels.cpp
3 /// @brief Writing instance records on the device and building the top level over them.
4 #include "ernie/render/rt/top_levels.h"
5
6 #include "ernie/shaders/rt/write_instances.h"
7 #include "ernie/util/math.h"
8 #include "ernie/util/profiler.h"
9
10 #include <algorithm>
11 #include <array>
12 #include <bit>
13 #include <vector>
14
15 namespace ernie {
16 namespace {
17
18 using shaders::WriteParameters;
19 static_assert(sizeof(shaders::Instance) == sizeof(AccelerationInstance));
20
21 15 std::vector<std::uint32_t> Compile(const ShaderCompiler& compiler) {
22 15 std::vector<ShaderModule> modules(TransformGraph::WorldModules().begin(),
23
1/2
✓ Branch 4 taken 15 times.
✗ Branch 5 not taken.
15 TransformGraph::WorldModules().end());
24
1/2
✓ Branch 1 taken 15 times.
✗ Branch 2 not taken.
15 modules.push_back(shaders::kWriteInstances);
25
1/2
✓ Branch 1 taken 15 times.
✗ Branch 2 not taken.
30 return compiler.Spirv(modules, "main");
26 15 }
27
28 /// @return The capacity for `count` instances: doubling, and at least 64.
29 15 std::uint32_t RoundUpCapacity(std::uint32_t count) {
30
3/4
✓ Branch 0 taken 5 times.
✓ Branch 1 taken 10 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 15 times.
20 return std::max<std::uint32_t>(64, std::bit_ceil(count));
31 }
32
33 } // namespace
34
35 15 TopLevels::TopLevels(DeviceContext& context, const ShaderCompiler& compiler)
36
1/2
✓ Branch 2 taken 15 times.
✗ Branch 3 not taken.
15 : context_(context), writer_(context, Compile(compiler), "write instances",
37 std::uint32_t{sizeof(WriteParameters)}),
38
1/2
✓ Branch 1 taken 15 times.
✗ Branch 2 not taken.
15 pool_(context, "top levels") {}
39
40 27 graph::Acceleration TopLevels::AddBuild(graph::Graph& graph, const TransformGraph& transforms,
41 const InstanceTable& instances,
42 const BottomLevels& bottoms) {
43 27 ERNIE_ZONE("Prepare the top level");
44
1/2
✓ Branch 1 taken 27 times.
✗ Branch 2 not taken.
27 const std::uint32_t count = instances.Count();
45
1/2
✓ Branch 1 taken 27 times.
✗ Branch 2 not taken.
27 EnsureCapacity(count);
46
2/2
✓ Branch 0 taken 5 times.
✓ Branch 1 taken 22 times.
27 graph::Buffer records = graph.Create(
47
3/4
✓ Branch 0 taken 5 times.
✓ Branch 1 taken 22 times.
✓ Branch 3 taken 27 times.
✗ Branch 4 not taken.
32 std::max<std::uint64_t>(count, 1) * sizeof(AccelerationInstance), "instance records");
48
2/2
✓ Branch 0 taken 22 times.
✓ Branch 1 taken 5 times.
27 if (count > 0) {
49
1/2
✓ Branch 1 taken 22 times.
✗ Branch 2 not taken.
22 AddWritePass(graph, transforms, instances, bottoms, records);
50 }
51
1/2
✓ Branch 1 taken 27 times.
✗ Branch 2 not taken.
54 return AddBuildPass(graph, bottoms, records, count);
52 27 }
53
54 27 void TopLevels::EnsureCapacity(std::uint32_t count) {
55
2/2
✓ Branch 0 taken 12 times.
✓ Branch 1 taken 15 times.
27 const std::uint32_t capacity = top_ ? top_->Sizes().primitives[0] : 0;
56
4/8
✓ Branch 0 taken 12 times.
✓ Branch 1 taken 15 times.
✓ Branch 2 taken 12 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✓ Branch 5 taken 12 times.
✗ Branch 6 not taken.
✗ Branch 7 not taken.
27 if (!top_ || count > capacity || (capacity > 64 && count < capacity / 4)) {
57 // Frames in flight keep the old one: destroying it retires it.
58 15 top_ = std::make_unique<AccelerationStructure>(
59
1/2
✓ Branch 2 taken 15 times.
✗ Branch 3 not taken.
30 pool_, Sizes(context_, RoundUpCapacity(count), kRebuilt), "scene");
60 }
61 27 }
62
63 22 void TopLevels::AddWritePass(graph::Graph& graph, const TransformGraph& transforms,
64 const InstanceTable& instances, const BottomLevels& bottoms,
65 graph::Buffer& records) const {
66 // In `WriteParameters`' order.
67 22 const std::array reads{graph.Import(transforms.Worlds()), graph.Import(instances.Nodes()),
68 22 graph.Import(instances.Geometries()), graph.Import(instances.Shapes()),
69 22 graph.Import(bottoms.Addresses())};
70 22 const std::uint32_t count = instances.Count();
71 44 graph.Add(
72 "Write instances", graph::Kind::Compute,
73 66 [&](graph::PassBuilder& pass) {
74
2/2
✓ Branch 0 taken 110 times.
✓ Branch 1 taken 22 times.
132 for (const graph::Buffer& read : reads) {
75 110 pass.ReadAddress(read);
76 }
77 22 records = pass.WriteAddress(records, graph::Use::StorageWrite);
78 22 },
79 22 [this, reads, records, count](graph::PassContext& pass) {
80 110 const auto address = [&](std::size_t read) { return pass.Get(reads[read]).Address(); };
81 66 writer_.Dispatch(pass.commands, {DivideRoundUp(count, 64u), 1, 1},
82 22 WriteParameters{{address(0)},
83 22 {address(1)},
84 22 {address(2)},
85 22 {address(3)},
86 22 {address(4)},
87 22 pass.Get(records).BufferRef<shaders::Instance>(),
88 count});
89 22 });
90 22 }
91
92 27 graph::Acceleration TopLevels::AddBuildPass(graph::Graph& graph, const BottomLevels& bottoms,
93 graph::Buffer records, std::uint32_t count) const {
94 27 graph::Acceleration top = graph.Import(*top_);
95 27 const graph::Acceleration levels = graph.Import(bottoms.Pool());
96 27 const TopBuild build{.target = top_.get(), .count = count};
97 27 graph::Buffer scratch =
98 27 graph.Create(ScratchBytes({}, std::span(&build, 1)), "top-level scratch");
99 54 graph.Add(
100 "Build the top level", graph::Kind::Build,
101 81 [&](graph::PassBuilder& pass) {
102 27 pass.Read(levels, graph::Use::BuildInput);
103
2/2
✓ Branch 0 taken 22 times.
✓ Branch 1 taken 5 times.
27 if (count > 0) {
104 22 pass.Read(records, graph::Use::BuildInput);
105 }
106 27 scratch = pass.Write(scratch, graph::Use::BuildScratch);
107 27 top = pass.Build(top);
108 27 },
109 27 [build, records, scratch, count](graph::PassContext& pass) {
110 27 TopBuild recorded = build;
111
2/2
✓ Branch 0 taken 22 times.
✓ Branch 1 taken 5 times.
27 if (count > 0) {
112 22 const graph::BufferSlice slice = pass.Get(records);
113 22 recorded.instances = {slice.buffer, slice.offset};
114 }
115 27 const graph::BufferSlice space = pass.Get(scratch);
116 27 RecordBuilds(pass.commands, {}, std::span(&recorded, 1), {space.buffer, space.offset});
117 27 });
118 27 return top;
119 }
120
121 } // namespace ernie
122