chrono_batch_render/
Coverage:
low: ≥ 0%
medium: ≥ 75.0%
high: ≥ 90.0%
Lines:
149 / 151
98.7%
Branches:
155 / 178
87.1%
Functions:
7 / 7
100.0%

scene/tables/subsets.cpp
Line Branch Exec Source
1 // SPDX-License-Identifier: BSD-3-Clause
2 #include "chrono_batch_render/scene/tables/subsets.h"
3
4 #include <algorithm>
5
6 #include "chrono_batch_render/device/commands.h"
7 #include "chrono_batch_render/device/queue.h"
8 #include "chrono_batch_render/scene/kernels.h"
9 #include "chrono_batch_render/scene/records/render_tables.h"
10 #include "chrono_batch_render/scene/schema/columns.h"
11 #include "chrono_batch_render/scene/tables/kernels/subsets.h"
12 #include "chrono_batch_render/stage/publish/change_set.h"
13 #include "chrono_batch_render/util/profiler.h"
14 #include "chrono_batch_render/util/span.h"
15
16 namespace chrono::batch_render {
17
18 namespace {
19
20 constexpr ColumnRef kInputs[] = {kGeomSubsetElementType, kGeomSubsetIndices, kGeomSubsetFamilyName,
21 kMaterialBindingAPIMaterialBinding, kMeshFaceVertexCounts};
22
23 }
24
25 2320 std::span<const ColumnRef> SubsetTable::Inputs() const {
26 2320 return kInputs;
27 }
28
29 466 void SubsetTable::Describe(RenderTables& tables) const {
30
1/2
✓ Branch 0 taken 466 times.
✗ Branch 1 not taken.
466 tables.subsets.address = m_records.Buffer() ? m_records.Buffer()->Address() : 0;
31 466 }
32
33 466 void SubsetTable::Run(const ChangeSet& changes, TableVersions&) {
34
4/5
✓ Branch 0 taken 5 times.
✓ Branch 1 taken 461 times.
✓ Branch 3 taken 5 times.
✗ Branch 4 not taken.
✓ Branch 6 taken 5 times.
466 CHRONO_BATCH_RENDER_ZONE("Subsets/Run");
35 466 m_stats.rebuilt = false;
36
2/2
✓ Branch 0 taken 192 times.
✓ Branch 1 taken 274 times.
466 const Prims& prims = m_structure.GetPrims();
37
2/2
✓ Branch 0 taken 192 times.
✓ Branch 1 taken 274 times.
466 ColumnStore& store = m_structure.GetColumnStore();
38 7568 const auto isSubset = [&](uint32_t prim) {
39
4/4
✓ Branch 1 taken 7086 times.
✓ Branch 2 taken 16 times.
✓ Branch 4 taken 7078 times.
✓ Branch 5 taken 8 times.
7102 return prims.Alive(prim) && prims.Record(prim).schema == SchemaId::GeomSubset;
40 466 };
41
3/4
✓ Branch 0 taken 192 times.
✓ Branch 1 taken 274 times.
✓ Branch 2 taken 192 times.
✗ Branch 3 not taken.
466 bool dirty = !m_built || changes.resync;
42 192 if (dirty) {
43 274 m_subsets.clear();
44
2/2
✓ Branch 0 taken 1856 times.
✓ Branch 1 taken 274 times.
2130 for (uint32_t prim = 0; prim < prims.Size(); ++prim) {
45
3/3
✓ Branch 1 taken 1856 times.
✓ Branch 3 taken 8 times.
✓ Branch 4 taken 1848 times.
1856 if (isSubset(prim)) {
46
1/1
✓ Branch 1 taken 8 times.
8 m_subsets.insert(prim);
47 }
48 }
49 } else {
50
2/2
✓ Branch 0 taken 141 times.
✓ Branch 1 taken 192 times.
333 for (const StructureEntry& entry : changes.removed) {
51
3/4
✓ Branch 1 taken 140 times.
✓ Branch 2 taken 1 time.
✓ Branch 3 taken 140 times.
✗ Branch 4 not taken.
281 dirty = m_subsets.erase(entry.prim) > 0 || dirty;
52 }
53
2/2
✓ Branch 0 taken 5246 times.
✓ Branch 1 taken 192 times.
5438 for (const StructureEntry& entry : changes.added) {
54
2/6
✓ Branch 1 taken 5246 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 5246 times.
✗ Branch 6 not taken.
✗ Branch 8 not taken.
✗ Branch 9 not taken.
5246 if (isSubset(entry.prim) && prims.Generation(entry.prim) == entry.generation) {
55
0/1
✗ Branch 1 not taken.
✗ m_subsets.insert(entry.prim);
56 dirty = true;
57 }
58 }
59
2/2
✓ Branch 0 taken 12 times.
✓ Branch 1 taken 192 times.
204 for (const StructureEntry& entry : changes.apis) {
60
1/2
✓ Branch 0 taken 12 times.
✗ Branch 1 not taken.
24 dirty = dirty ||
61
1/4
✗ Branch 0 not taken.
✓ Branch 1 taken 12 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
12 (entry.api == SchemaId::SemanticsLabelsAPI && m_subsets.contains(entry.prim));
62 }
63 }
64
6/7
✓ Branch 0 taken 191 times.
✓ Branch 1 taken 275 times.
✓ Branch 2 taken 3 times.
✓ Branch 3 taken 188 times.
✓ Branch 5 taken 3 times.
✓ Branch 7 taken 3 times.
✗ Branch 8 not taken.
466 dirty = dirty || (!m_subsets.empty() && std::ranges::any_of(kInputs, [&](ColumnRef column) {
65 9 return store.LastWrite(column) > m_seen;
66 }));
67
2/2
✓ Branch 0 taken 278 times.
✓ Branch 1 taken 188 times.
466 m_seen = store.WriteSequence();
68 466 m_built = true;
69
2/2
✓ Branch 0 taken 278 times.
✓ Branch 1 taken 188 times.
466 if (dirty) {
70
1/1
✓ Branch 1 taken 278 times.
278 Rebuild();
71 }
72
2/2
✓ Branch 1 taken 466 times.
✓ Branch 4 taken 466 times.
466 m_records.Reserve(store.Schema(SchemaId::Mesh).Capacity());
73
1/1
✓ Branch 1 taken 466 times.
466 m_records.Upload(store.GetDevice());
74 466 }
75
76 278 void SubsetTable::Rebuild() {
77
4/5
✓ Branch 0 taken 5 times.
✓ Branch 1 taken 273 times.
✓ Branch 3 taken 5 times.
✗ Branch 4 not taken.
✓ Branch 6 taken 5 times.
278 CHRONO_BATCH_RENDER_ZONE("Subsets/Rebuild");
78 278 m_stats = {.rebuilt = true};
79
1/1
✓ Branch 1 taken 278 times.
278 const Prims& prims = m_structure.GetPrims();
80
1/1
✓ Branch 1 taken 278 times.
278 const Paths& paths = m_structure.GetPaths();
81
1/1
✓ Branch 1 taken 278 times.
278 ColumnStore& store = m_structure.GetColumnStore();
82
1/1
✓ Branch 1 taken 278 times.
278 ArrayPool& ints = store.Arrays(ColumnType::IntArray);
83
3/3
✓ Branch 1 taken 3 times.
✓ Branch 3 taken 3 times.
✓ Branch 4 taken 278 times.
284 for (const auto& [meshRow, record] : m_written) {
84
1/1
✓ Branch 1 taken 3 times.
3 ints.Free(record.materials);
85
1/1
✓ Branch 1 taken 3 times.
3 ints.Free(record.parts);
86
1/1
✓ Branch 1 taken 3 times.
3 m_records.Set(meshRow, {});
87 }
88 278 m_written.clear();
89
2/2
✓ Branch 0 taken 7 times.
✓ Branch 1 taken 271 times.
278 if (m_subsets.empty()) {
90 return;
91 }
92
93
1/1
✓ Branch 1 taken 7 times.
7 ColumnTable& table = store.Schema(SchemaId::GeomSubset);
94
1/1
✓ Branch 1 taken 7 times.
7 const std::vector<uint32_t> elements =
95
2/2
✓ Branch 1 taken 7 times.
✓ Branch 4 taken 7 times.
14 Items<uint32_t>(table.Read(kGeomSubsetElementType.index, 0, table.Rows()));
96
1/1
✓ Branch 1 taken 7 times.
7 const std::vector<uint32_t> families =
97
2/2
✓ Branch 1 taken 7 times.
✓ Branch 4 taken 7 times.
14 Items<uint32_t>(table.Read(kGeomSubsetFamilyName.index, 0, table.Rows()));
98
1/1
✓ Branch 1 taken 7 times.
7 const uint32_t materialBind = m_structure.GetTokens().Intern("materialBind").id;
99 7 std::set<uint32_t> meshes;
100
2/2
✓ Branch 0 taken 21 times.
✓ Branch 1 taken 7 times.
28 for (const uint32_t subset : m_subsets) {
101
1/1
✓ Branch 1 taken 21 times.
21 const uint32_t parent = paths.Parent(subset);
102
4/7
✓ Branch 0 taken 21 times.
✗ Branch 1 not taken.
✓ Branch 3 taken 21 times.
✓ Branch 5 taken 21 times.
✗ Branch 6 not taken.
✓ Branch 7 taken 21 times.
✗ Branch 8 not taken.
42 if (parent != kNoPrim && prims.Alive(parent) &&
103
2/2
✓ Branch 1 taken 21 times.
✓ Branch 4 taken 21 times.
21 IsA(prims.Record(parent).schema, SchemaId::Mesh)) {
104
1/1
✓ Branch 1 taken 21 times.
21 meshes.insert(parent);
105 }
106 }
107
108 7 std::vector<SubsetJob> jobs;
109 7 std::vector<SubsetOutput> outputs;
110 7 uint32_t indexCount = 0;
111 7 uint32_t entryCount = 0;
112
2/2
✓ Branch 0 taken 7 times.
✓ Branch 1 taken 7 times.
14 for (const uint32_t mesh : meshes) {
113
1/1
✓ Branch 1 taken 7 times.
7 const uint32_t meshRow = prims.Row(mesh, SchemaId::Mesh);
114
1/1
✓ Branch 1 taken 7 times.
7 const uint32_t faces = store.Array(kMeshFaceVertexCounts, meshRow).count;
115
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 7 times.
7 if (faces == 0) {
116 ✗ continue;
117 }
118
1/1
✓ Branch 1 taken 7 times.
7 const std::vector<uint32_t> children = paths.Children(mesh);
119 7 SubsetRecord record{};
120
2/2
✓ Branch 0 taken 14 times.
✓ Branch 1 taken 7 times.
21 for (const SubsetMap map : {SubsetMap::Materials, SubsetMap::Parts}) {
121 14 const size_t firstJob = jobs.size();
122
2/2
✓ Branch 0 taken 42 times.
✓ Branch 1 taken 14 times.
56 for (const uint32_t child : children) {
123
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 42 times.
42 if (!m_subsets.contains(child)) {
124 28 continue;
125 }
126
1/1
✓ Branch 1 taken 42 times.
42 const uint32_t row = prims.Row(child, SchemaId::GeomSubset);
127
1/1
✓ Branch 1 taken 42 times.
42 const ArrayRef indices = store.Array(kGeomSubsetIndices, row);
128 42 const bool wanted = map == SubsetMap::Materials
129
2/2
✓ Branch 0 taken 21 times.
✓ Branch 1 taken 21 times.
42 ? families[row] == materialBind
130
1/1
✓ Branch 1 taken 21 times.
21 : prims.HasApi(child, SchemaId::SemanticsLabelsAPI);
131
5/6
✓ Branch 0 taken 17 times.
✓ Branch 1 taken 25 times.
✓ Branch 2 taken 14 times.
✓ Branch 3 taken 3 times.
✓ Branch 4 taken 14 times.
✗ Branch 5 not taken.
42 if (!wanted || elements[row] != uint32_t(ElementType::Face) || indices.count == 0) {
132 28 continue;
133 }
134 14 SubsetJob job{};
135 14 job.indices.address = indices.address;
136 14 job.subset = child;
137 14 job.count = indices.count;
138 14 job.firstIndex = indexCount;
139 14 job.firstEntry = entryCount;
140 14 job.faceCount = faces;
141 14 job.map = uint32_t(map);
142
1/1
✓ Branch 1 taken 14 times.
14 jobs.push_back(job);
143 14 indexCount += indices.count;
144 14 ++m_stats.subsets;
145 }
146
2/2
✓ Branch 0 taken 6 times.
✓ Branch 1 taken 8 times.
14 if (jobs.size() == firstJob) {
147 6 continue;
148 }
149
1/1
✓ Branch 1 taken 8 times.
8 const ArrayRef values = ints.Allocate(faces);
150
2/2
✓ Branch 0 taken 5 times.
✓ Branch 1 taken 3 times.
8 (map == SubsetMap::Materials ? record.materials : record.parts) = values;
151 8 SubsetOutput output{};
152 8 output.values.address = values.address;
153 8 output.firstEntry = entryCount;
154 8 output.count = faces;
155
1/1
✓ Branch 1 taken 8 times.
8 outputs.push_back(output);
156 8 entryCount += faces;
157 }
158
4/4
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 5 times.
✓ Branch 2 taken 1 time.
✓ Branch 3 taken 1 time.
7 if (record.materials.count > 0 || record.parts.count > 0) {
159
1/1
✓ Branch 1 taken 6 times.
6 m_written[meshRow] = record;
160
1/1
✓ Branch 1 taken 6 times.
6 m_records.Set(meshRow, record);
161 6 ++m_stats.meshes;
162 }
163 7 }
164
2/2
✓ Branch 0 taken 1 time.
✓ Branch 1 taken 6 times.
7 if (jobs.empty()) {
165 1 return;
166 }
167
168
1/1
✓ Branch 1 taken 6 times.
6 store.Flush();
169
1/1
✓ Branch 1 taken 6 times.
6 device::Device& device = store.GetDevice();
170
1/1
✓ Branch 1 taken 6 times.
6 const Ref<device::Buffer> jobBuffer =
171
1/1
✓ Branch 1 taken 6 times.
6 device.CreateBuffer({.label = "subset jobs"}, std::span<const SubsetJob>(jobs));
172 6 const Ref<device::Buffer> outputBuffer =
173
2/2
✓ Branch 1 taken 6 times.
✓ Branch 4 taken 6 times.
6 device.CreateBuffer({.label = "subset outputs"}, std::span<const SubsetOutput>(outputs));
174 6 const Ref<device::Buffer> ranks =
175
1/1
✓ Branch 1 taken 6 times.
6 device.CreateBuffer({.bytes = uint64_t(entryCount) * 4, .label = "subset face ranks"});
176 6 SubsetParams params{};
177 12 params.scene.address = store.DeviceData();
178
2/2
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 4 times.
6 params.jobs.address = jobBuffer->Address();
179 6 params.outputs.address = outputBuffer->Address();
180 6 params.ranks.address = ranks->Address();
181
2/2
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 4 times.
6 params.jobCount = uint32_t(jobs.size());
182 6 params.outputCount = uint32_t(outputs.size());
183 6 params.indexCount = indexCount;
184 6 params.entryCount = entryCount;
185
4/5
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 4 times.
✓ Branch 3 taken 2 times.
✗ Branch 4 not taken.
✓ Branch 6 taken 2 times.
6 static const profiler::ZoneId zone = profiler::ZoneId::Named("Subsets/Write");
186
1/1
✓ Branch 1 taken 6 times.
6 device::Queue& queue = device.GetQueue();
187
1/1
✓ Branch 1 taken 6 times.
6 device::CommandEncoder encoder = queue.CreateEncoder();
188 6 {
189
1/1
✓ Branch 1 taken 6 times.
6 const device::ZoneScope scope(encoder, zone);
190
2/2
✓ Branch 1 taken 6 times.
✓ Branch 4 taken 6 times.
6 encoder.Fill(ranks->Slice(0), ~0u);
191
3/3
✓ Branch 1 taken 6 times.
✓ Branch 4 taken 6 times.
✓ Branch 7 taken 6 times.
6 encoder.Dispatch(Kernels().Entry("ClaimFaces"), params,
192
1/1
✓ Branch 1 taken 6 times.
6 {.x = device::GroupCount(indexCount, kSubsetGroup)});
193
3/3
✓ Branch 1 taken 6 times.
✓ Branch 4 taken 6 times.
✓ Branch 7 taken 6 times.
6 encoder.Dispatch(Kernels().Entry("WriteFaces"), params,
194
1/1
✓ Branch 1 taken 6 times.
6 {.x = device::GroupCount(entryCount, kSubsetGroup)});
195 6 }
196
2/2
✓ Branch 1 taken 6 times.
✓ Branch 4 taken 6 times.
6 queue.Submit(encoder.Finish());
197 320 }
198
199 353 void SubsetTable::Prepare() {
200 353 Kernels();
201 353 }
202
203 365 device::Program& SubsetTable::Kernels() {
204
2/2
✓ Branch 0 taken 353 times.
✓ Branch 1 taken 12 times.
365 if (!m_program) {
205
2/2
✓ Branch 2 taken 353 times.
✓ Branch 5 taken 353 times.
1059 m_program = m_structure.GetColumnStore().GetDevice().CreateProgram(
206 {.modules = {"chrono_batch_render.scene.tables.kernels.subsets"},
207 .library = &kSceneKernels,
208 .entries = {"ClaimFaces", "WriteFaces"},
209
2/3
✓ Branch 1 taken 353 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 353 times.
706 .label = "subsets"});
210 }
211 365 return *m_program;
212 706 }
213
214 }
215