chrono_batch_render/
Coverage:
low: ≥ 0%
medium: ≥ 75.0%
high: ≥ 90.0%
Lines:
700 / 783
89.4%
Branches:
591 / 788
75.0%
Functions:
37 / 37, 1 excl
100.0%

scene/tables/materials.cpp
Line Branch Exec Source
1 // SPDX-License-Identifier: BSD-3-Clause
2 #include "chrono_batch_render/scene/tables/materials.h"
3
4 #include <set>
5
6 #include <algorithm>
7 #include <bit>
8 #include <cstddef>
9 #include <memory>
10 #include <string_view>
11
12 #include "chrono_batch_render/device/commands.h"
13 #include "chrono_batch_render/device/queue.h"
14 #include "chrono_batch_render/graph/graph.h"
15 #include "chrono_batch_render/material/codegen.h"
16 #include "chrono_batch_render/scene/kernels.h"
17 #include "chrono_batch_render/scene/records/render_tables.h"
18 #include "chrono_batch_render/scene/schema/columns.h"
19 #include "chrono_batch_render/scene/tables/connections.h"
20 #include "chrono_batch_render/scene/tables/kernels/materials.h"
21 #include "chrono_batch_render/scene/tables/textures.h"
22 #include "chrono_batch_render/stage/publish/change_set.h"
23 #include "chrono_batch_render/util/log.h"
24 #include "chrono_batch_render/util/profiler.h"
25 #include "chrono_batch_render/util/span.h"
26
27 namespace chrono::batch_render {
28
29 namespace {
30
31 3374 constexpr uint32_t Word(size_t offset) {
32
3/3
✓ Branch 1 taken 28 times.
✓ Branch 4 taken 2 times.
✓ Branch 7 taken 14 times.
44 return uint32_t(offset / sizeof(uint32_t));
33 }
34
35 constexpr std::string_view kPreviewInputNames[kPreviewInputs] = {
36 "diffuseColor", "emissiveColor", "specularColor", "metallic", "roughness",
37 "clearcoat", "clearcoatRoughness", "opacity", "occlusion", "normal",
38 "detailNormal", "detailAlbedo", "displacement",
39 };
40
41 constexpr Float4 kItuMaterials[] = {
42 {1, 0, 0, 0},
43 {5.24f, 0, 0.0462f, 0.7822f},
44 {3.91f, 0, 0.0238f, 0.16f},
45 {2.73f, 0, 0.0085f, 0.9395f},
46 {1.99f, 0, 0.0047f, 1.0718f},
47 {6.31f, 0, 0.0036f, 1.3394f},
48 {1.48f, 0, 0.0011f, 1.0750f},
49 {2.58f, 0, 0.0217f, 0.7800f},
50 {2.71f, 0, 0.33f, 0},
51 {7.074f, 0, 0.0055f, 0.9262f},
52 {3.66f, 0, 0.0044f, 1.3515f},
53 {1, 0, 1e7f, 0},
54 {3, 0, 0.00015f, 2.52f},
55 {15, -0.1f, 0.035f, 1.63f},
56 {30, -0.4f, 0.15f, 1.30f},
57 };
58
59 constexpr float kVisibleFirstNm = 380;
60 constexpr float kVisibleLastNm = 780;
61
62 constexpr bool Spectral(PreviewInput input) {
63 return input == PreviewInput::DiffuseColor || input == PreviewInput::EmissiveColor ||
64 input == PreviewInput::SpecularColor;
65 }
66
67 7 constexpr bool ColorInput(uint32_t input) {
68 7 return input == uint32_t(PreviewInput::DiffuseColor) ||
69 7 input == uint32_t(PreviewInput::EmissiveColor) ||
70 7 input == uint32_t(PreviewInput::SpecularColor) ||
71
1/3
✓ Branch 0 taken 7 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
7 input == uint32_t(PreviewInput::Normal) ||
72 input == uint32_t(PreviewInput::DetailNormal) ||
73 7 input == uint32_t(PreviewInput::DetailAlbedo);
74 }
75
76 14 bool Channel(std::string_view output, bool color, TextureChannel& channel) {
77
2/2
✓ Branch 0 taken 13 times.
✓ Branch 1 taken 1 time.
14 if (output == "rgb") {
78
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 13 times.
13 channel = color ? TextureChannel::Rgb : TextureChannel::R;
79
1/2
✓ Branch 0 taken 1 time.
✗ Branch 1 not taken.
1 } else if (output == "r") {
80 1 channel = TextureChannel::R;
81
0/2
✗ Branch 0 not taken.
✗ Branch 1 not taken.
✗ } else if (output == "g") {
82 ✗ channel = TextureChannel::G;
83
0/2
✗ Branch 0 not taken.
✗ Branch 1 not taken.
✗ } else if (output == "b") {
84 ✗ channel = TextureChannel::B;
85
0/2
✗ Branch 0 not taken.
✗ Branch 1 not taken.
✗ } else if (output == "a") {
86 ✗ channel = TextureChannel::A;
87 } else {
88 return false;
89 }
90 return true;
91 }
92
93 constexpr SchemaId kBuiltinSurfaces[] = {SchemaId::HapkeSurface, SchemaId::LommelSeeligerSurface,
94 SchemaId::LunarLambertSurface, SchemaId::OrenNayarSurface};
95
96 constexpr std::string_view kBuiltinSurfaceModule =
97 "chrono_batch_render.render.kernels.material.surfaces.planetary";
98
99 33 std::shared_ptr<const material::MaterialNodeDefinition> BuiltinSurface(SchemaId schema) {
100 2 static const std::vector<std::shared_ptr<const material::MaterialNodeDefinition>> nodes = [] {
101 2 std::vector<std::shared_ptr<const material::MaterialNodeDefinition>> built;
102
2/2
✓ Branch 0 taken 8 times.
✓ Branch 1 taken 2 times.
10 for (const SchemaId id : kBuiltinSurfaces) {
103
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 8 times.
8 material::MaterialNodeDefinition node{.name = std::string(Info(id).name),
104 .role = material::MaterialNodeRole::Surface,
105 .schema = uint32_t(id),
106
4/5
✗ Branch 0 not taken.
✓ Branch 1 taken 8 times.
✓ Branch 3 taken 8 times.
✓ Branch 6 taken 8 times.
✓ Branch 9 taken 8 times.
16 .module = std::string(kBuiltinSurfaceModule)};
107
3/3
✓ Branch 1 taken 8 times.
✓ Branch 3 taken 30 times.
✓ Branch 4 taken 8 times.
38 for (const ColumnInfo& column : Columns(id)) {
108 30 const bool color = column.type == ColumnType::Float3;
109
3/4
✓ Branch 0 taken 8 times.
✓ Branch 1 taken 22 times.
✓ Branch 2 taken 8 times.
✗ Branch 3 not taken.
30 if (color && column.field == "albedo") {
110 8 node.detailAlbedo = uint32_t(node.inputs.size());
111 }
112
1/1
✓ Branch 1 taken 30 times.
60 node.inputs.push_back({.name = std::string(column.field),
113
2/2
✓ Branch 0 taken 22 times.
✓ Branch 1 taken 8 times.
30 .type = color ? material::MaterialInputType::Spectral
114 : material::MaterialInputType::Float});
115 }
116 8 node.detail = true;
117
2/3
✓ Branch 1 taken 8 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 8 times.
16 built.push_back(
118
1/1
✓ Branch 1 taken 8 times.
8 std::make_shared<const material::MaterialNodeDefinition>(std::move(node)));
119 8 }
120 2 return built;
121
6/7
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 31 times.
✓ Branch 3 taken 2 times.
✗ Branch 4 not taken.
✓ Branch 6 taken 2 times.
✓ Branch 12 taken 30 times.
✓ Branch 15 taken 30 times.
123 }();
122 33 const auto found = std::ranges::find(kBuiltinSurfaces, schema);
123 33 return found == std::end(kBuiltinSurfaces)
124
2/2
✓ Branch 0 taken 18 times.
✓ Branch 1 taken 15 times.
33 ? nullptr
125
1/2
✓ Branch 0 taken 15 times.
✗ Branch 1 not taken.
48 : nodes[size_t(found - std::begin(kBuiltinSurfaces))];
126 }
127
128 275 std::vector<ColumnRef> MaterialInputs() {
129 275 std::vector<ColumnRef> inputs = {kPlaneAxis, kDetectorAPIDetectorSpectralRange,
130 275 kThermalAPIThermalTemperature};
131 3575 for (const SchemaId schema :
132 {SchemaId::Connectable, SchemaId::UsdPreviewSurface, SchemaId::SurfaceDetailAPI,
133 SchemaId::UsdUVTexture, SchemaId::UsdPrimvarReader_float2, SchemaId::UsdTransform2d,
134 SchemaId::TexturePlacement, SchemaId::ItuMaterial, SchemaId::CheckerboardAPI,
135 SchemaId::HapkeSurface, SchemaId::LommelSeeligerSurface, SchemaId::LunarLambertSurface,
136
2/2
✓ Branch 0 taken 3575 times.
✓ Branch 1 taken 275 times.
3850 SchemaId::OrenNayarSurface}) {
137
1/1
✓ Branch 1 taken 3575 times.
3575 const Span<const ColumnInfo> columns = Columns(schema);
138
2/2
✓ Branch 0 taken 21450 times.
✓ Branch 1 taken 3575 times.
25025 for (uint32_t index = 0; index < columns.size(); ++index) {
139
1/1
✓ Branch 1 taken 21450 times.
21450 inputs.push_back({schema, &columns[index], index});
140 }
141 }
142 275 return inputs;
143 ✗ }
144
145 }
146
147 536 bool MaterialTable::Restructured(const ChangeSet& changes) const {
148
2/2
✓ Branch 0 taken 262 times.
✓ Branch 1 taken 274 times.
536 if (changes.resync) {
149 return true;
150 }
151 720 for (const ColumnRef column : {kConnectableConnectionsSources, kConnectableConnectionsInputs,
152
2/2
✓ Branch 0 taken 742 times.
✓ Branch 1 taken 240 times.
982 kConnectableConnectionsOutputs}) {
153
2/2
✓ Branch 1 taken 22 times.
✓ Branch 2 taken 720 times.
742 if (!changes.ArrayRows(column).empty()) {
154 22 return true;
155 }
156 }
157 240 const Prims& prims = m_structure.GetPrims();
158
4/4
✓ Branch 0 taken 38 times.
✓ Branch 1 taken 5327 times.
✓ Branch 3 taken 25 times.
✓ Branch 4 taken 13 times.
5365 const auto touched = [&](uint32_t prim) { return prim < m_touched.size() && m_touched[prim]; };
159
2/2
✓ Branch 0 taken 5231 times.
✓ Branch 1 taken 226 times.
5457 for (const StructureEntry& entry : changes.added) {
160
6/6
✓ Branch 1 taken 5218 times.
✓ Branch 2 taken 13 times.
✓ Branch 4 taken 5203 times.
✓ Branch 5 taken 15 times.
✓ Branch 6 taken 1 time.
✓ Branch 7 taken 5202 times.
10434 if (touched(entry.prim) || (prims.Alive(entry.prim) &&
161 5203 IsA(prims.Record(entry.prim).schema, SchemaId::Connectable))) {
162 14 return true;
163 }
164 }
165
2/2
✓ Branch 0 taken 122 times.
✓ Branch 1 taken 226 times.
348 for (const StructureEntry& entry : changes.removed) {
166
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 122 times.
122 if (touched(entry.prim)) {
167 ✗ return true;
168 }
169 }
170
2/2
✓ Branch 0 taken 12 times.
✓ Branch 1 taken 226 times.
238 for (const StructureEntry& entry : changes.apis) {
171
2/4
✓ Branch 1 taken 12 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 12 times.
✗ Branch 4 not taken.
12 if (touched(entry.prim) || entry.api == SchemaId::SurfaceDetailAPI) {
172 ✗ return true;
173 }
174 }
175 226 return false;
176 }
177
178 435 bool MaterialTable::Changed(SchemaId schema) const {
179 435 const ColumnStore& store = m_structure.GetColumnStore();
180 435 return std::ranges::any_of(Inputs(), [&](ColumnRef column) {
181
4/4
✓ Branch 0 taken 841 times.
✓ Branch 1 taken 35173 times.
✓ Branch 3 taken 824 times.
✓ Branch 4 taken 17 times.
36014 return column.schema == schema && store.LastWrite(column) > m_writes;
182 435 });
183 }
184
185 2825 std::span<const ColumnRef> MaterialTable::Inputs() const {
186
2/2
✓ Branch 1 taken 275 times.
✓ Branch 2 taken 2550 times.
2825 if (m_inputSchemas != SchemaCount()) {
187 275 m_inputs = MaterialInputs();
188
2/2
✓ Branch 1 taken 822 times.
✓ Branch 2 taken 275 times.
1097 for (uint32_t index = uint32_t(SchemaId::Count); index < SchemaCount(); ++index) {
189 822 const SchemaId schema = SchemaId(index);
190 822 const Span<const ColumnInfo> columns = Columns(schema);
191
2/2
✓ Branch 0 taken 1348 times.
✓ Branch 1 taken 822 times.
2170 for (uint32_t column = 0; column < columns.size(); ++column) {
192 1348 m_inputs.push_back({schema, &columns[column], column});
193 }
194 }
195 275 m_inputSchemas = SchemaCount();
196 }
197 2825 return m_inputs;
198 }
199
200 536 void MaterialTable::Describe(RenderTables& tables) const {
201
1/2
✓ Branch 0 taken 536 times.
✗ Branch 1 not taken.
536 tables.materials.address = m_records.Buffer() ? m_records.Buffer()->Address() : 0;
202
1/2
✓ Branch 0 taken 536 times.
✗ Branch 1 not taken.
536 tables.materialWords.address = m_words.Address();
203
1/2
✓ Branch 0 taken 536 times.
✗ Branch 1 not taken.
536 tables.checkers.address = m_checkers.Address();
204
1/2
✓ Branch 0 taken 536 times.
✗ Branch 1 not taken.
536 tables.temperatures.address = m_temperatures.Address();
205
1/2
✓ Branch 0 taken 536 times.
✗ Branch 1 not taken.
536 tables.radios.address = m_radios.Buffer() ? m_radios.Buffer()->Address() : 0;
206 536 tables.temperaturePrimvar = m_temperaturePrimvar;
207 536 }
208
209 291 std::span<const MaterialTable::Connection> MaterialTable::Connections(uint32_t prim) {
210 291 const std::span<const Connection> connections = m_connections.Get(prim);
211
1/2
✓ Branch 0 taken 291 times.
✗ Branch 1 not taken.
291 if (prim < m_touched.size()) {
212 291 m_touched[prim] = true;
213 }
214
2/2
✓ Branch 0 taken 207 times.
✓ Branch 1 taken 291 times.
498 for (const Connection& connection : connections) {
215
2/2
✓ Branch 0 taken 205 times.
✓ Branch 1 taken 2 times.
207 if (connection.source < m_touched.size()) {
216 205 m_touched[connection.source] = true;
217 }
218 }
219 291 return connections;
220 }
221
222 1392 void MaterialTable::CopyCell(ColumnRef column, uint32_t prim, uint32_t destination) {
223 1392 m_pack.Copy(column, m_structure.GetPrims().Row(prim, column.schema), destination);
224 1392 }
225
226 69 void MaterialTable::ResolvePlacement(uint32_t placement, uint32_t mapping) {
227
2/2
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 67 times.
69 const Prims& prims = m_structure.GetPrims();
228 962 const auto at = [&](size_t offset) { return mapping + Word(offset); };
229 879 const auto literal = [&](size_t offset, uint32_t value) { m_pack.Literal(at(offset), value); };
230 676 const auto real = [&](size_t offset, float value) {
231 607 literal(offset, std::bit_cast<uint32_t>(value));
232 67 };
233
2/2
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 67 times.
69 const SchemaId schema = placement == kNoPrim ? SchemaId::None : prims.Record(placement).schema;
234
2/2
✓ Branch 0 taken 1 time.
✓ Branch 1 taken 1 time.
2 if (schema == SchemaId::TexturePlacement) {
235 1 CopyCell(kTexturePlacementProjection, placement, at(offsetof(MappingRecord, projection)));
236 1 CopyCell(kTexturePlacementProjectionSpace, placement,
237 at(offsetof(MappingRecord, projectionSpace)));
238 1 CopyCell(kTexturePlacementUvPrimvar, placement, at(offsetof(MappingRecord, uvPrimvar)));
239 1 CopyCell(kTexturePlacementTileSize, placement, at(offsetof(MappingRecord, tileSize)));
240 1 CopyCell(kTexturePlacementTriplanarBlend, placement,
241 at(offsetof(MappingRecord, triplanarBlend)));
242 1 CopyCell(kTexturePlacementUvScale, placement, at(offsetof(MappingRecord, uvScale)));
243 1 CopyCell(kTexturePlacementUvOffset, placement, at(offsetof(MappingRecord, uvOffset)));
244 1 CopyCell(kTexturePlacementUvRotation, placement, at(offsetof(MappingRecord, uvRotation)));
245 1 CopyCell(kTexturePlacementHexTiling, placement, at(offsetof(MappingRecord, hexTiling)));
246 1 CopyCell(kTexturePlacementHexTilingContrast, placement,
247 at(offsetof(MappingRecord, hexTilingContrast)));
248 1 CopyCell(kTexturePlacementHexTilingRotation, placement,
249 at(offsetof(MappingRecord, hexTilingRotation)));
250 1 return;
251 }
252 68 literal(offsetof(MappingRecord, projection), 0);
253 68 literal(offsetof(MappingRecord, projectionSpace), 0);
254 68 real(offsetof(MappingRecord, tileSize), 1);
255 68 real(offsetof(MappingRecord, triplanarBlend), 0.25f);
256 68 literal(offsetof(MappingRecord, hexTiling), 0);
257 68 real(offsetof(MappingRecord, hexTilingContrast), 0.5f);
258 68 real(offsetof(MappingRecord, hexTilingRotation), 180);
259 68 uint32_t reader = placement;
260
2/2
✓ Branch 0 taken 1 time.
✓ Branch 1 taken 67 times.
68 if (schema == SchemaId::UsdTransform2d) {
261 1 CopyCell(kUsdTransform2dScale, placement, at(offsetof(MappingRecord, uvScale)));
262 1 CopyCell(kUsdTransform2dTranslation, placement, at(offsetof(MappingRecord, uvOffset)));
263 1 CopyCell(kUsdTransform2dRotation, placement, at(offsetof(MappingRecord, uvRotation)));
264 1 reader = kNoPrim;
265
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 1 time.
1 for (const Connection& connection : Connections(placement)) {
266
0/2
✗ Branch 0 not taken.
✗ Branch 1 not taken.
✗ if (connection.input == "in") {
267 ✗ reader = connection.source;
268 }
269 }
270 } else {
271 67 real(offsetof(MappingRecord, uvScale), 1);
272 67 real(offsetof(MappingRecord, uvScale) + sizeof(float), 1);
273 67 real(offsetof(MappingRecord, uvOffset), 0);
274 67 real(offsetof(MappingRecord, uvOffset) + sizeof(float), 0);
275 67 real(offsetof(MappingRecord, uvRotation), 0);
276 }
277
1/4
✗ Branch 0 not taken.
✓ Branch 1 taken 68 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
68 if (reader != kNoPrim && prims.Record(reader).schema == SchemaId::UsdPrimvarReader_float2) {
278 ✗ CopyCell(kUsdPrimvarReader_float2Varname, reader, at(offsetof(MappingRecord, uvPrimvar)));
279 } else {
280
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 68 times.
68 if (reader != kNoPrim) {
281
0/1
✗ Branch 1 not taken.
✗ Log(LogLevel::Warning,
282 "{} reads its coordinates from {}, which isn't a UsdPrimvarReader_float2; it "
283 "reads the st primvar instead",
284
0/1
✗ Branch 2 not taken.
✗ m_structure.GetPaths().Path(placement), m_structure.GetPaths().Path(reader));
285 }
286 68 literal(offsetof(MappingRecord, uvPrimvar), m_structure.GetTokens().Intern("st").id);
287 }
288 }
289
290 52 void MaterialTable::ResolvePreview(uint32_t material, uint32_t shader) {
291 52 const Prims& prims = m_structure.GetPrims();
292 52 const Paths& paths = m_structure.GetPaths();
293 52 const uint32_t base = m_wordCount;
294 52 m_wordCount += Word(sizeof(PreviewParams));
295 3165 const auto at = [&](size_t offset) { return base + Word(offset); };
296 52 CopyCell(kUsdPreviewSurfaceDiffuseColor, shader, at(offsetof(PreviewParams, diffuseColor)));
297 52 CopyCell(kUsdPreviewSurfaceUseSpecularWorkflow, shader,
298 at(offsetof(PreviewParams, useSpecularWorkflow)));
299 52 CopyCell(kUsdPreviewSurfaceEmissiveColor, shader, at(offsetof(PreviewParams, emissiveColor)));
300 52 CopyCell(kUsdPreviewSurfaceMetallic, shader, at(offsetof(PreviewParams, metallic)));
301 52 CopyCell(kUsdPreviewSurfaceSpecularColor, shader, at(offsetof(PreviewParams, specularColor)));
302 52 CopyCell(kUsdPreviewSurfaceRoughness, shader, at(offsetof(PreviewParams, roughness)));
303 52 CopyCell(kUsdPreviewSurfaceIor, shader, at(offsetof(PreviewParams, ior)));
304 52 CopyCell(kUsdPreviewSurfaceClearcoat, shader, at(offsetof(PreviewParams, clearcoat)));
305 52 CopyCell(kUsdPreviewSurfaceClearcoatRoughness, shader,
306 at(offsetof(PreviewParams, clearcoatRoughness)));
307 52 CopyCell(kUsdPreviewSurfaceOpacity, shader, at(offsetof(PreviewParams, opacity)));
308 52 const size_t detail = offsetof(PreviewParams, detail);
309 52 CopyCell(kSurfaceDetailAPINormal, shader, at(detail + offsetof(DetailRecord, normal)));
310 52 CopyCell(kSurfaceDetailAPINormalStrength, shader,
311 at(detail + offsetof(DetailRecord, normalStrength)));
312 52 CopyCell(kSurfaceDetailAPIDetailNormal, shader,
313 at(detail + offsetof(DetailRecord, detailNormal)));
314 52 CopyCell(kSurfaceDetailAPIDetailNormalStrength, shader,
315 at(detail + offsetof(DetailRecord, detailNormalStrength)));
316 52 CopyCell(kSurfaceDetailAPIDetailAlbedo, shader,
317 at(detail + offsetof(DetailRecord, detailAlbedo)));
318 52 CopyCell(kSurfaceDetailAPIDetailAlbedoStrength, shader,
319 at(detail + offsetof(DetailRecord, detailAlbedoStrength)));
320 52 CopyCell(kSurfaceDetailAPIDetailScale, shader,
321 at(detail + offsetof(DetailRecord, detailScale)));
322 52 CopyCell(kSurfaceDetailAPIDetailOffset, shader,
323 at(detail + offsetof(DetailRecord, detailOffset)));
324 52 CopyCell(kSurfaceDetailAPIDetailRotation, shader,
325 at(detail + offsetof(DetailRecord, detailRotation)));
326 52 CopyCell(kSurfaceDetailAPIDetailHexTiling, shader,
327 at(detail + offsetof(DetailRecord, detailHexTiling)));
328 52 CopyCell(kSurfaceDetailAPIEnableDetail, shader,
329 at(detail + offsetof(DetailRecord, enableDetail)));
330
331 52 const std::span<const Connection> connections = Connections(shader);
332 52 uint32_t placement = kNoPrim;
333 52 bool placed = false;
334 52 uint32_t textured = 0;
335
2/2
✓ Branch 0 taken 676 times.
✓ Branch 1 taken 52 times.
728 for (uint32_t input = 0; input < kPreviewInputs; ++input) {
336 676 const size_t slot = offsetof(PreviewParams, inputs) + input * sizeof(TextureInput);
337 1345 const auto unbound = [&] {
338 669 m_pack.Literal(at(slot + offsetof(TextureInput, handle)), 0);
339 669 m_pack.Literal(at(slot + offsetof(TextureInput, handle)) + 1, 0);
340 1345 };
341 676 const auto found =
342 676 std::ranges::find(connections, kPreviewInputNames[input], &Connection::input);
343
3/4
✓ Branch 0 taken 12 times.
✓ Branch 1 taken 664 times.
✓ Branch 2 taken 12 times.
✗ Branch 3 not taken.
676 const bool spectral = found != connections.end() && found->source != kNoPrim &&
344
2/2
✓ Branch 1 taken 5 times.
✓ Branch 2 taken 7 times.
12 prims.Record(found->source).schema == SchemaId::SampledSpectrum;
345 676 uint32_t spectrum = kNoSpectrum;
346
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 5 times.
676 if (spectral && Spectral(PreviewInput(input))) {
347 5 spectrum = prims.Row(found->source, SchemaId::SampledSpectrum);
348 5 m_spectral.insert(material);
349
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 671 times.
671 } else if (spectral) {
350
0/1
✗ Branch 1 not taken.
✗ Log(LogLevel::Warning,
351 "{}'s {} connects to the spectrum {}, but only diffuseColor, emissiveColor and "
352 "specularColor take spectra; the input keeps its value",
353
0/1
✗ Branch 1 not taken.
✗ m_structure.GetPaths().Path(shader), kPreviewInputNames[input],
354 ✗ m_structure.GetPaths().Path(found->source));
355 }
356 676 m_pack.Literal(at(slot + offsetof(TextureInput, spectrum)), spectrum);
357
4/4
✓ Branch 0 taken 664 times.
✓ Branch 1 taken 12 times.
✓ Branch 2 taken 7 times.
✓ Branch 3 taken 5 times.
676 if (found == connections.end() || spectral) {
358 669 unbound();
359 669 continue;
360 }
361 7 const uint32_t texture = found->source;
362 7 TextureChannel channel = TextureChannel::Rgb;
363
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 7 times.
7 if (!BindTexture(shader, *found, kPreviewInputNames[input], ColorInput(input), at(slot),
364 channel)) {
365 ✗ unbound();
366 ✗ continue;
367 }
368 7 ++textured;
369
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 7 times.
7 if (input == uint32_t(PreviewInput::DetailNormal) ||
370 input == uint32_t(PreviewInput::DetailAlbedo)) {
371 ✗ continue;
372 }
373 7 uint32_t coordinates = kNoPrim;
374
2/2
✓ Branch 1 taken 1 time.
✓ Branch 2 taken 7 times.
8 for (const Connection& connection : Connections(texture)) {
375
1/2
✓ Branch 0 taken 1 time.
✗ Branch 1 not taken.
1 if (connection.input == "st") {
376 1 coordinates = connection.source;
377 }
378 }
379
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 7 times.
7 if (!placed) {
380 placement = coordinates;
381 placed = true;
382
0/2
✗ Branch 0 not taken.
✗ Branch 1 not taken.
✗ } else if (coordinates != placement) {
383
0/1
✗ Branch 1 not taken.
✗ Log(LogLevel::Warning,
384 "{}'s textures read coordinates from more than one source; {} reads them through "
385 "the placement of its other textures",
386
0/1
✗ Branch 2 not taken.
✗ paths.Path(shader), paths.Path(texture));
387 }
388 }
389 52 ResolvePlacement(placement, at(offsetof(PreviewParams, mapping)));
390
2/2
✓ Branch 0 taken 45 times.
✓ Branch 1 taken 7 times.
97 Define(material, textured > 0 ? kDefinitionPreviewTextured : kDefinitionPreviewConstant, base);
391
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 52 times.
52 if (GetLogLevel() <= LogLevel::Debug) {
392
0/1
✗ Branch 1 not taken.
✗ Log(LogLevel::Debug, "{} shades with {} through {}, with {} textures in {} words",
393 ✗ paths.Path(material),
394
0/3
✗ Branch 0 not taken.
✗ Branch 1 not taken.
✗ Branch 3 not taken.
✗ textured > 0 ? "a textured UsdPreviewSurface" : "UsdPreviewSurface", paths.Path(shader),
395 ✗ textured, Word(sizeof(PreviewParams)));
396 }
397 52 }
398
399 15 bool MaterialTable::ResolvePlanetary(uint32_t material, uint32_t shader) {
400 15 const Prims& prims = m_structure.GetPrims();
401 15 const Paths& paths = m_structure.GetPaths();
402 15 const SchemaId schema = prims.Record(shader).schema;
403 15 const std::span<const Connection> connections = Connections(shader);
404
2/2
✓ Branch 0 taken 6 times.
✓ Branch 1 taken 15 times.
21 for (const Connection& connection : connections) {
405 6 const SchemaId source =
406
1/2
✓ Branch 0 taken 6 times.
✗ Branch 1 not taken.
6 connection.source == kNoPrim ? SchemaId::None : prims.Record(connection.source).schema;
407
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 6 times.
6 if (IsRuntime(source)) {
408
0/2
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✗ if (GetLogLevel() <= LogLevel::Debug) {
409
0/1
✗ Branch 1 not taken.
✗ Log(LogLevel::Debug,
410 "{} reads the Slang node {}, so it shades through a generated definition",
411
0/1
✗ Branch 2 not taken.
✗ paths.Path(shader), paths.Path(connection.source));
412 }
413 ✗ return false;
414 }
415 }
416 15 uint32_t definition = kDefinitionHapke;
417
2/2
✓ Branch 0 taken 12 times.
✓ Branch 1 taken 3 times.
15 if (schema == SchemaId::LommelSeeligerSurface) {
418 definition = kDefinitionLommelSeeliger;
419
2/2
✓ Branch 0 taken 10 times.
✓ Branch 1 taken 2 times.
12 } else if (schema == SchemaId::LunarLambertSurface) {
420 definition = kDefinitionLunarLambert;
421
2/2
✓ Branch 0 taken 3 times.
✓ Branch 1 taken 7 times.
10 } else if (schema == SchemaId::OrenNayarSurface) {
422 3 definition = kDefinitionOrenNayar;
423 }
424 15 m_wordCount = (m_wordCount + 1) & ~1u;
425 15 const uint32_t base = m_wordCount;
426 15 m_wordCount += Word(sizeof(PlanetaryParams));
427 477 const auto at = [&](size_t offset) { return base + Word(offset); };
428 15 const Span<const ColumnInfo> columns = Columns(schema);
429
3/4
✗ Branch 0 not taken.
✓ Branch 1 taken 15 times.
✓ Branch 2 taken 36 times.
✓ Branch 3 taken 15 times.
51 for (uint32_t i = std::max<uint32_t>(uint32_t(columns.size()), 1) - 1; i < kPlanetaryValues;
430 ++i) {
431 36 m_pack.Literal(at(offsetof(PlanetaryParams, values) + i * sizeof(float)), 0);
432 }
433 15 uint32_t textured = 0;
434 15 uint32_t coordinates = kNoPrim;
435
3/4
✓ Branch 0 taken 69 times.
✓ Branch 1 taken 15 times.
✓ Branch 2 taken 69 times.
✗ Branch 3 not taken.
84 for (uint32_t i = 0; i < columns.size() && i < kPlanetaryInputs; ++i) {
436
2/2
✓ Branch 0 taken 15 times.
✓ Branch 1 taken 54 times.
69 const ColumnInfo& info = columns[i];
437 69 const bool color = i == 0;
438
2/2
✓ Branch 1 taken 15 times.
✓ Branch 2 taken 54 times.
138 CopyCell({schema, &info, i}, shader,
439 15 color ? at(offsetof(PlanetaryParams, albedo))
440 54 : at(offsetof(PlanetaryParams, values) + (i - 1) * sizeof(float)));
441 69 const size_t slot = offsetof(PlanetaryParams, inputs) + i * sizeof(TextureInput);
442 135 const auto unbound = [&] {
443 66 m_pack.Literal(at(slot + offsetof(TextureInput, handle)), 0);
444 66 m_pack.Literal(at(slot + offsetof(TextureInput, handle)) + 1, 0);
445 135 };
446 69 const auto found = std::ranges::find(connections, info.field, &Connection::input);
447
3/4
✓ Branch 0 taken 3 times.
✓ Branch 1 taken 66 times.
✓ Branch 2 taken 3 times.
✗ Branch 3 not taken.
69 const bool spectral = found != connections.end() && found->source != kNoPrim &&
448
1/2
✓ Branch 1 taken 3 times.
✗ Branch 2 not taken.
3 prims.Record(found->source).schema == SchemaId::SampledSpectrum;
449 69 uint32_t spectrum = kNoSpectrum;
450
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 69 times.
69 if (spectral && color) {
451 ✗ spectrum = prims.Row(found->source, SchemaId::SampledSpectrum);
452 ✗ m_spectral.insert(material);
453
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 69 times.
69 } else if (spectral) {
454
0/1
✗ Branch 1 not taken.
✗ Log(LogLevel::Warning,
455 "{}'s {} connects to the spectrum {}, but only its albedo takes spectra; the "
456 "input keeps its value",
457
0/1
✗ Branch 2 not taken.
✗ paths.Path(shader), info.field, paths.Path(found->source));
458 }
459 69 m_pack.Literal(at(slot + offsetof(TextureInput, spectrum)), spectrum);
460 69 TextureChannel channel = TextureChannel::Rgb;
461
4/6
✓ Branch 0 taken 3 times.
✓ Branch 1 taken 66 times.
✓ Branch 2 taken 3 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 3 times.
✗ Branch 5 not taken.
72 if (found == connections.end() || spectral ||
462 3 !BindTexture(shader, *found, info.field, color, at(slot), channel)) {
463 66 unbound();
464 66 continue;
465 }
466 3 ++textured;
467
2/2
✓ Branch 1 taken 1 time.
✓ Branch 2 taken 3 times.
4 for (const Connection& read : Connections(found->source)) {
468
2/4
✓ Branch 0 taken 1 time.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 1 time.
1 if (read.input == "st" && coordinates == kNoPrim) {
469 1 coordinates = read.source;
470 }
471 }
472 }
473
2/2
✓ Branch 0 taken 36 times.
✓ Branch 1 taken 15 times.
51 for (uint32_t i = uint32_t(columns.size()); i < kPlanetaryInputs; ++i) {
474 36 const size_t slot = offsetof(PlanetaryParams, inputs) + i * sizeof(TextureInput);
475 36 m_pack.Literal(at(slot + offsetof(TextureInput, handle)), 0);
476 36 m_pack.Literal(at(slot + offsetof(TextureInput, handle)) + 1, 0);
477 36 m_pack.Literal(at(slot + offsetof(TextureInput, spectrum)), kNoSpectrum);
478 }
479 15 m_pack.Literal(at(offsetof(PlanetaryParams, textured)), textured);
480 15 const uint32_t detailed = PackDetail(shader, at(offsetof(PlanetaryParams, detail)),
481 at(offsetof(PlanetaryParams, details)));
482
2/2
✓ Branch 0 taken 1 time.
✓ Branch 1 taken 14 times.
16 ResolvePlacement(coordinates != kNoPrim ? coordinates : detailed,
483 at(offsetof(PlanetaryParams, mapping)));
484 15 Define(material, definition, base);
485
2/2
✓ Branch 1 taken 5 times.
✓ Branch 2 taken 10 times.
15 if (GetLogLevel() <= LogLevel::Debug) {
486
1/1
✓ Branch 1 taken 5 times.
5 Log(LogLevel::Debug, "{} shades with the built-in {} through {}, with {} textures",
487
1/1
✓ Branch 2 taken 5 times.
15 paths.Path(material), Info(schema).name, paths.Path(shader), textured);
488 }
489 return true;
490 }
491
492 15 uint32_t MaterialTable::PackDetail(uint32_t shader, uint32_t detail, uint32_t textures) {
493 165 const auto at = [&](size_t offset) { return detail + Word(offset); };
494 15 CopyCell(kSurfaceDetailAPINormal, shader, at(offsetof(DetailRecord, normal)));
495 15 CopyCell(kSurfaceDetailAPINormalStrength, shader, at(offsetof(DetailRecord, normalStrength)));
496 15 CopyCell(kSurfaceDetailAPIDetailNormal, shader, at(offsetof(DetailRecord, detailNormal)));
497 15 CopyCell(kSurfaceDetailAPIDetailNormalStrength, shader,
498 at(offsetof(DetailRecord, detailNormalStrength)));
499 15 CopyCell(kSurfaceDetailAPIDetailAlbedo, shader, at(offsetof(DetailRecord, detailAlbedo)));
500 15 CopyCell(kSurfaceDetailAPIDetailAlbedoStrength, shader,
501 at(offsetof(DetailRecord, detailAlbedoStrength)));
502 15 CopyCell(kSurfaceDetailAPIDetailScale, shader, at(offsetof(DetailRecord, detailScale)));
503 15 CopyCell(kSurfaceDetailAPIDetailOffset, shader, at(offsetof(DetailRecord, detailOffset)));
504 15 CopyCell(kSurfaceDetailAPIDetailRotation, shader, at(offsetof(DetailRecord, detailRotation)));
505 15 CopyCell(kSurfaceDetailAPIDetailHexTiling, shader, at(offsetof(DetailRecord, detailHexTiling)));
506 15 CopyCell(kSurfaceDetailAPIEnableDetail, shader, at(offsetof(DetailRecord, enableDetail)));
507 15 constexpr std::string_view kNames[material::kDetailTextures] = {"normal", "detailNormal",
508 "detailAlbedo"};
509 15 const std::span<const Connection> connections = Connections(shader);
510 15 uint32_t coordinates = kNoPrim;
511
2/2
✓ Branch 1 taken 45 times.
✓ Branch 2 taken 15 times.
60 for (uint32_t i = 0; i < material::kDetailTextures; ++i) {
512 45 const uint32_t slot = textures + Word(i * material::kTextureInputBytes);
513 45 const auto found = std::ranges::find(connections, kNames[i], &Connection::input);
514 45 TextureChannel channel = TextureChannel::Rgb;
515
1/4
✗ Branch 0 not taken.
✓ Branch 1 taken 45 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
45 if (found == connections.end() ||
516 ✗ !BindTexture(shader, *found, kNames[i], true, slot, channel)) {
517 45 const uint32_t handle = slot + Word(offsetof(TextureInput, handle));
518 45 m_pack.Literal(handle, 0);
519 45 m_pack.Literal(handle + 1, 0);
520 45 continue;
521 45 }
522
0/2
✗ Branch 0 not taken.
✗ Branch 1 not taken.
✗ if (i != 0) {
523 ✗ continue;
524 }
525
0/2
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✗ for (const Connection& connection : Connections(found->source)) {
526
0/2
✗ Branch 0 not taken.
✗ Branch 1 not taken.
✗ if (connection.input == "st") {
527 ✗ coordinates = connection.source;
528 }
529 }
530 }
531 15 return coordinates;
532 }
533
534 14 bool MaterialTable::BindTexture(uint32_t shader, const Connection& connection,
535 std::string_view input, bool color, uint32_t slot,
536 TextureChannel& channel) {
537
1/2
✓ Branch 0 taken 14 times.
✗ Branch 1 not taken.
14 const Prims& prims = m_structure.GetPrims();
538
1/2
✓ Branch 0 taken 14 times.
✗ Branch 1 not taken.
14 const Paths& paths = m_structure.GetPaths();
539 14 const uint32_t texture = connection.source;
540
2/4
✓ Branch 0 taken 14 times.
✗ Branch 1 not taken.
✗ Branch 3 not taken.
✓ Branch 4 taken 14 times.
14 if (texture == kNoPrim || prims.Record(texture).schema != SchemaId::UsdUVTexture) {
541
0/1
✗ Branch 1 not taken.
✗ Log(LogLevel::Warning,
542 "{}'s {} connects to {}, which isn't a UsdUVTexture; the input keeps its value",
543
0/1
✗ Branch 1 not taken.
✗ paths.Path(shader), input,
544
0/2
✗ Branch 0 not taken.
✗ Branch 1 not taken.
✗ texture == kNoPrim ? std::string("nothing") : paths.Path(texture));
545 ✗ return false;
546 }
547
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 14 times.
14 if (!Channel(connection.output, color, channel)) {
548
0/1
✗ Branch 1 not taken.
✗ Log(LogLevel::Warning,
549 "{}'s {} reads output {} of {}, which isn't rgb, r, g, b or a; the input keeps "
550 "its value",
551
0/1
✗ Branch 2 not taken.
✗ paths.Path(shader), input, connection.output, paths.Path(texture));
552 ✗ return false;
553 }
554
2/2
✓ Branch 0 taken 12 times.
✓ Branch 1 taken 2 times.
14 if (slot == kNoPrim) {
555 return true;
556 }
557 12 const uint32_t handle = slot + Word(offsetof(TextureInput, handle));
558 12 m_bindings.push_back({texture, m_pack.Literal(handle, 0), m_pack.Literal(handle + 1, 0)});
559 12 m_pack.Literal(slot + Word(offsetof(TextureInput, channel)), uint32_t(channel));
560 12 CopyCell(kUsdUVTextureScale, texture, slot + Word(offsetof(TextureInput, scale)));
561 12 CopyCell(kUsdUVTextureBias, texture, slot + Word(offsetof(TextureInput, bias)));
562 12 return true;
563 }
564
565 89 void MaterialTable::Define(uint32_t material, uint32_t definition, uint32_t words) {
566 89 m_records.Set(material, {.definition = definition,
567 .words = words,
568 .previousDefinition = kDefinitionPlaceholder,
569 .previousWords = 0});
570 89 }
571
572 std::vector<MaterialTable::Reached>
573 15 MaterialTable::ReachNodes(uint32_t shader,
574 std::shared_ptr<const material::MaterialNodeDefinition> surface) {
575
1/1
✓ Branch 2 taken 15 times.
15 const Prims& prims = m_structure.GetPrims();
576
1/1
✓ Branch 2 taken 15 times.
15 const Paths& paths = m_structure.GetPaths();
577
2/4
✓ Branch 0 taken 15 times.
✓ Branch 1 taken 15 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
30 std::vector<Reached> reached = {{.prim = shader, .node = std::move(surface)}};
578
2/2
✓ Branch 0 taken 18 times.
✓ Branch 1 taken 15 times.
33 for (uint32_t k = 0; k < reached.size(); ++k) {
579
1/2
✓ Branch 0 taken 18 times.
✗ Branch 1 not taken.
18 const uint32_t prim = reached[k].prim;
580
1/2
✓ Branch 0 taken 18 times.
✗ Branch 1 not taken.
18 const std::shared_ptr<const material::MaterialNodeDefinition> node = reached[k].node;
581 18 const std::span<const Connection> connections = Connections(prim);
582
1/3
✓ Branch 1 taken 18 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
18 std::vector<material::InputBinding> inputs(node->inputs.size());
583
1/1
✓ Branch 1 taken 18 times.
18 std::vector<Connection> textures(node->inputs.size());
584
1/1
✓ Branch 1 taken 18 times.
18 std::vector<uint32_t> spectra(node->inputs.size(), kNoPrim);
585
2/2
✓ Branch 0 taken 28 times.
✓ Branch 1 taken 18 times.
46 for (uint32_t i = 0; i < node->inputs.size(); ++i) {
586 28 const material::MaterialNodeInput& input = node->inputs[i];
587 28 const auto found = std::ranges::find(connections, input.name, &Connection::input);
588
2/2
✓ Branch 0 taken 21 times.
✓ Branch 1 taken 7 times.
28 if (found == connections.end()) {
589 26 continue;
590 }
591 7 const uint32_t source = found->source;
592 7 const SchemaId schema =
593
2/3
✓ Branch 0 taken 7 times.
✗ Branch 1 not taken.
✓ Branch 3 taken 7 times.
7 source == kNoPrim ? SchemaId::None : prims.Record(source).schema;
594 7 std::shared_ptr<const material::MaterialNodeDefinition> pattern =
595
4/4
✓ Branch 0 taken 4 times.
✓ Branch 1 taken 3 times.
✓ Branch 3 taken 4 times.
✓ Branch 6 taken 4 times.
7 IsRuntime(schema) ? material::FindMaterialNode(Info(schema).name) : nullptr;
596
2/2
✓ Branch 0 taken 1 time.
✓ Branch 1 taken 6 times.
7 if (schema == SchemaId::SampledSpectrum) {
597
1/2
✓ Branch 0 taken 1 time.
✗ Branch 1 not taken.
1 if (input.type == material::MaterialInputType::Spectral) {
598 1 spectra[i] = source;
599 } else {
600
0/1
✗ Branch 1 not taken.
✗ Log(LogLevel::Warning,
601 "{}'s {} connects to the spectrum {}, but only SpectralValue inputs take "
602 "spectra; the input keeps its value",
603
0/2
✗ Branch 1 not taken.
✗ Branch 4 not taken.
✗ paths.Path(prim), input.name, paths.Path(source));
604 }
605 1 continue;
606 }
607 12 const material::MaterialInputType fed =
608 6 input.type == material::MaterialInputType::Spectral
609
1/2
✓ Branch 0 taken 6 times.
✗ Branch 1 not taken.
6 ? material::MaterialInputType::Float3
610 : input.type;
611
3/4
✓ Branch 0 taken 4 times.
✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 4 times.
6 if (pattern && pattern->role == material::MaterialNodeRole::Pattern) {
612
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 4 times.
4 if (pattern->result != fed) {
613
0/1
✗ Branch 1 not taken.
✗ Log(LogLevel::Warning,
614 "{}'s {} connects to {}, whose result has another type; the input keeps "
615 "its value",
616
0/2
✗ Branch 1 not taken.
✗ Branch 4 not taken.
✗ paths.Path(prim), input.name, paths.Path(source));
617 ✗ continue;
618 }
619 4 auto at = std::ranges::find(reached, source, &Reached::prim);
620
2/2
✓ Branch 0 taken 3 times.
✓ Branch 1 taken 1 time.
4 if (at == reached.end()) {
621 3 reached.push_back({.prim = source, .node = std::move(pattern)});
622 3 at = reached.end() - 1;
623 }
624 4 inputs[i] = {material::BindingKind::Pattern, uint32_t(at - reached.begin())};
625 4 continue;
626 4 }
627
2/3
✓ Branch 1 taken 2 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 2 times.
2 if (!material::Texturable(input.type)) {
628
0/1
✗ Branch 1 not taken.
✗ Log(LogLevel::Warning,
629 "{}'s {} connects to {}, but only float and float3 inputs take textures; "
630 "the input keeps its value",
631
0/1
✗ Branch 1 not taken.
✗ paths.Path(prim), input.name,
632
0/6
✗ Branch 0 not taken.
✗ Branch 1 not taken.
✗ Branch 3 not taken.
✗ Branch 6 not taken.
✗ Branch 8 not taken.
✗ Branch 9 not taken.
✗ source == kNoPrim ? std::string("nothing") : paths.Path(source));
633 ✗ continue;
634 }
635 2 TextureChannel channel = TextureChannel::Rgb;
636
1/2
✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
2 if (BindTexture(prim, *found, input.name,
637
1/1
✓ Branch 1 taken 2 times.
2 input.type != material::MaterialInputType::Float, kNoPrim, channel)) {
638 2 inputs[i] = {material::BindingKind::Texture, 0};
639 2 textures[i] = *found;
640 }
641 7 }
642 18 reached[k].inputs = std::move(inputs);
643 18 reached[k].textures = std::move(textures);
644 18 reached[k].spectra = std::move(spectra);
645 36 }
646 15 return reached;
647 18 }
648
649 15 std::vector<uint32_t> MaterialTable::OrderPatterns(std::vector<Reached>& reached) const {
650 15 const Paths& paths = m_structure.GetPaths();
651 15 std::vector<uint32_t> order;
652 17 while (true) {
653 16 graph::Graph patterns;
654
2/2
✓ Branch 0 taken 21 times.
✓ Branch 1 taken 16 times.
37 for (size_t node = 0; node < reached.size(); ++node) {
655
1/1
✓ Branch 1 taken 21 times.
21 patterns.Add(0);
656 }
657
2/2
✓ Branch 0 taken 21 times.
✓ Branch 1 taken 16 times.
37 for (uint32_t k = 0; k < reached.size(); ++k) {
658
2/2
✓ Branch 0 taken 31 times.
✓ Branch 1 taken 21 times.
52 for (uint32_t i = 0; i < reached[k].inputs.size(); ++i) {
659
2/2
✓ Branch 0 taken 6 times.
✓ Branch 1 taken 25 times.
31 if (reached[k].inputs[i].kind == material::BindingKind::Pattern) {
660
1/1
✓ Branch 1 taken 6 times.
6 patterns.Connect(k, i, reached[k].inputs[i].source, 0);
661 }
662 }
663 }
664
1/1
✓ Branch 1 taken 16 times.
16 const graph::Ordering ordering = patterns.Order();
665
2/2
✓ Branch 0 taken 15 times.
✓ Branch 1 taken 1 time.
16 if (ordering.cycle.empty()) {
666
1/1
✓ Branch 1 taken 15 times.
15 order = ordering.order;
667 15 break;
668 }
669 1 const uint32_t closing = ordering.cycle.back();
670
2/2
✓ Branch 0 taken 1 time.
✓ Branch 1 taken 1 time.
2 for (material::InputBinding& binding : reached[closing].inputs) {
671
2/4
✓ Branch 0 taken 1 time.
✗ Branch 1 not taken.
✓ Branch 2 taken 1 time.
✗ Branch 3 not taken.
1 if (binding.kind == material::BindingKind::Pattern &&
672
1/2
✓ Branch 0 taken 1 time.
✗ Branch 1 not taken.
1 binding.source == ordering.cycle.front()) {
673 1 binding = {};
674 }
675 }
676
1/1
✓ Branch 1 taken 1 time.
1 Log(LogLevel::Warning, "{} feeds back into itself through {}; that input keeps its value",
677
2/2
✓ Branch 1 taken 1 time.
✓ Branch 4 taken 1 time.
2 paths.Path(reached[ordering.cycle.front()].prim), paths.Path(reached[closing].prim));
678 16 }
679
1/1
✓ Branch 2 taken 15 times.
15 order.resize(size_t(std::ranges::find(order, 0u) - order.begin()) + 1);
680 15 return order;
681 ✗ }
682
683 15 void MaterialTable::ResolveNode(uint32_t material, uint32_t shader,
684 std::shared_ptr<const material::MaterialNodeDefinition> surface) {
685
1/1
✓ Branch 1 taken 15 times.
15 const Prims& prims = m_structure.GetPrims();
686
1/1
✓ Branch 1 taken 15 times.
15 const Paths& paths = m_structure.GetPaths();
687
1/1
✓ Branch 1 taken 15 times.
15 std::vector<Reached> reached = ReachNodes(shader, std::move(surface));
688
1/1
✓ Branch 1 taken 15 times.
15 const std::vector<uint32_t> order = OrderPatterns(reached);
689
1/1
✓ Branch 1 taken 15 times.
15 std::vector<uint32_t> position(reached.size(), kNoPrim);
690 15 material::ShadingGraph graph;
691
2/2
✓ Branch 0 taken 18 times.
✓ Branch 1 taken 15 times.
33 for (const uint32_t k : order) {
692
1/2
✓ Branch 0 taken 18 times.
✗ Branch 1 not taken.
18 position[k] = uint32_t(graph.nodes.size());
693
1/2
✓ Branch 0 taken 18 times.
✗ Branch 1 not taken.
36 graph.nodes.push_back({reached[k].node, reached[k].inputs});
694 }
695
2/2
✓ Branch 0 taken 18 times.
✓ Branch 1 taken 15 times.
33 for (material::ShadingNode& node : graph.nodes) {
696
2/2
✓ Branch 0 taken 28 times.
✓ Branch 1 taken 18 times.
46 for (material::InputBinding& binding : node.inputs) {
697
2/2
✓ Branch 0 taken 3 times.
✓ Branch 1 taken 25 times.
28 if (binding.kind == material::BindingKind::Pattern) {
698 3 binding.source = position[binding.source];
699 }
700 }
701 }
702
1/1
✓ Branch 1 taken 15 times.
15 const material::GraphLayout layout = material::LayOut(graph);
703 15 const uint32_t definition =
704
2/2
✓ Branch 1 taken 15 times.
✓ Branch 4 taken 15 times.
15 m_library.Intern(material::MaterialDefinitionKey(graph), [&](uint32_t id) {
705 14 material::MaterialDefinition module = material::GenerateMaterialDefinition(id, graph);
706
1/1
✓ Branch 1 taken 14 times.
14 Log(LogLevel::Debug,
707 "new material definition {} for {} with {}, {} in {} words and {} bytes of "
708 "generated "
709 "Slang",
710
2/2
✓ Branch 1 taken 14 times.
✓ Branch 4 taken 14 times.
28 id, graph.nodes.back().node->name, Counted(graph.nodes.size() - 1, "pattern"),
711
1/1
✓ Branch 1 taken 14 times.
14 Counted(layout.textureCount, "texture"), Word(layout.bytes), module.source.size());
712 14 return module;
713 15 });
714 15 m_wordCount = (m_wordCount + 1) & ~1u;
715 15 const uint32_t base = m_wordCount;
716 15 m_wordCount += Word(layout.bytes);
717 15 uint32_t coordinates = kNoPrim;
718 15 bool placed = false;
719
2/2
✓ Branch 0 taken 18 times.
✓ Branch 1 taken 15 times.
33 for (uint32_t n = 0; n < order.size(); ++n) {
720
1/1
✓ Branch 1 taken 18 times.
18 const Reached& node = reached[order[n]];
721
1/1
✓ Branch 1 taken 18 times.
18 const SchemaId schema = SchemaId(node.node->schema);
722
1/1
✓ Branch 1 taken 18 times.
18 const Span<const ColumnInfo> columns = Columns(schema);
723
2/2
✓ Branch 0 taken 28 times.
✓ Branch 1 taken 18 times.
46 for (uint32_t i = 0; i < node.node->inputs.size(); ++i) {
724
1/1
✓ Branch 1 taken 28 times.
28 CopyCell({schema, &columns[i], i}, node.prim, base + Word(layout.cells[n][i]));
725
2/2
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 26 times.
28 if (node.node->inputs[i].type == material::MaterialInputType::Spectral) {
726 2 uint32_t spectrum = kNoSpectrum;
727
2/2
✓ Branch 0 taken 1 time.
✓ Branch 1 taken 1 time.
2 if (node.spectra[i] != kNoPrim) {
728
1/1
✓ Branch 1 taken 1 time.
1 spectrum = prims.Row(node.spectra[i], SchemaId::SampledSpectrum);
729
1/1
✓ Branch 1 taken 1 time.
1 m_spectral.insert(material);
730 }
731
1/1
✓ Branch 1 taken 2 times.
2 m_pack.Literal(base + Word(layout.cells[n][i] + 12), spectrum);
732 }
733
2/2
✓ Branch 0 taken 26 times.
✓ Branch 1 taken 2 times.
28 if (node.inputs[i].kind != material::BindingKind::Texture) {
734 26 continue;
735 }
736 2 TextureChannel channel = TextureChannel::Rgb;
737 2 BindTexture(node.prim, node.textures[i], node.node->inputs[i].name,
738
1/1
✓ Branch 1 taken 2 times.
2 node.node->inputs[i].type != material::MaterialInputType::Float,
739
1/1
✓ Branch 1 taken 2 times.
2 base + Word(layout.textures[n][i] * material::kTextureInputBytes), channel);
740 2 uint32_t read = kNoPrim;
741
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 2 times.
2 for (const Connection& connection : Connections(node.textures[i].source)) {
742
0/2
✗ Branch 0 not taken.
✗ Branch 1 not taken.
✗ if (connection.input == "st") {
743 ✗ read = connection.source;
744 }
745 }
746
1/2
✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
2 if (!placed) {
747 2 coordinates = read;
748 2 placed = true;
749 }
750 }
751 }
752
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 15 times.
15 if (layout.detailed) {
753 ✗ const uint32_t read =
754
0/1
✗ Branch 1 not taken.
✗ PackDetail(shader, base + Word(layout.detail), base + Word(layout.detailTextures));
755
0/2
✗ Branch 0 not taken.
✗ Branch 1 not taken.
✗ if (!placed) {
756 ✗ coordinates = read;
757 ✗ placed = read != kNoPrim;
758 }
759 }
760
2/2
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 13 times.
15 if (layout.mapped) {
761
1/1
✓ Branch 1 taken 2 times.
2 ResolvePlacement(coordinates, base + Word(layout.mapping));
762 }
763
1/1
✓ Branch 1 taken 15 times.
15 Define(material, definition, base);
764
2/3
✓ Branch 1 taken 15 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 15 times.
15 if (GetLogLevel() <= LogLevel::Debug) {
765
0/1
✗ Branch 1 not taken.
✗ Log(LogLevel::Debug, "{} shades with node {} through {} as definition {}, in {} words",
766
0/1
✗ Branch 1 not taken.
✗ paths.Path(material), graph.nodes.back().node->name, paths.Path(shader), definition,
767
0/1
✗ Branch 1 not taken.
✗ Word(layout.bytes));
768 }
769 33 }
770
771 89 void MaterialTable::ResolveMaterial(uint32_t material) {
772 89 const Prims& prims = m_structure.GetPrims();
773 89 const Paths& paths = m_structure.GetPaths();
774 89 uint32_t surface = kNoPrim;
775 89 bool connected = false;
776
2/2
✓ Branch 1 taken 87 times.
✓ Branch 2 taken 89 times.
176 for (const Connection& connection : Connections(material)) {
777
2/2
✓ Branch 0 taken 86 times.
✓ Branch 1 taken 1 time.
87 if (connection.input == "surface") {
778 86 surface = connection.source;
779 86 connected = true;
780 }
781 }
782
4/4
✓ Branch 0 taken 85 times.
✓ Branch 1 taken 4 times.
✓ Branch 3 taken 52 times.
✓ Branch 4 taken 33 times.
89 if (surface != kNoPrim && prims.Record(surface).schema == SchemaId::UsdPreviewSurface) {
783 52 ResolvePreview(material, surface);
784 52 return;
785 }
786
2/2
✓ Branch 0 taken 33 times.
✓ Branch 1 taken 4 times.
37 if (surface != kNoPrim) {
787 66 if (std::shared_ptr<const material::MaterialNodeDefinition> node =
788
2/2
✓ Branch 2 taken 15 times.
✓ Branch 3 taken 18 times.
33 BuiltinSurface(prims.Record(surface).schema)) {
789
2/3
✓ Branch 1 taken 15 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 15 times.
15 if (!ResolvePlanetary(material, surface)) {
790
0/3
✗ Branch 1 not taken.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✗ ResolveNode(material, surface, std::move(node));
791 }
792
1/2
✓ Branch 0 taken 15 times.
✗ Branch 1 not taken.
15 return;
793 15 }
794 }
795
2/2
✓ Branch 1 taken 15 times.
✓ Branch 2 taken 3 times.
18 if (surface != kNoPrim && IsRuntime(prims.Record(surface).schema)) {
796 15 std::shared_ptr<const material::MaterialNodeDefinition> node =
797
1/2
✓ Branch 1 taken 15 times.
✗ Branch 2 not taken.
30 material::FindMaterialNode(Info(prims.Record(surface).schema).name);
798
2/4
✓ Branch 0 taken 15 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 15 times.
15 if (node && node->role != material::MaterialNodeRole::Pattern) {
799
1/3
✓ Branch 1 taken 15 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
15 ResolveNode(material, surface, std::move(node));
800
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 15 times.
15 return;
801 }
802 15 }
803
2/2
✓ Branch 0 taken 3 times.
✓ Branch 1 taken 4 times.
7 if (!connected) {
804
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 3 times.
3 if (GetLogLevel() <= LogLevel::Debug) {
805
0/1
✗ Branch 1 not taken.
✗ Log(LogLevel::Debug, "{} has no surface connected, so it shades as plain diffuse",
806 ✗ paths.Path(material));
807 }
808 } else {
809
1/1
✓ Branch 1 taken 4 times.
4 Log(LogLevel::Warning,
810 "{}'s surface connects to {}, which isn't a surface Chrono Batch Render knows, so it "
811 "shades as "
812 "plain diffuse",
813
1/1
✓ Branch 1 taken 4 times.
8 paths.Path(material),
814
2/2
✓ Branch 0 taken 1 time.
✓ Branch 1 taken 3 times.
8 surface == kNoPrim ? std::string("nothing") : paths.Path(surface));
815 }
816 7 Define(material, kDefinitionDiffuse, 0);
817 }
818
819 311 void MaterialTable::Resolve(const ChangeSet& changes) {
820
4/5
✓ Branch 0 taken 5 times.
✓ Branch 1 taken 306 times.
✓ Branch 3 taken 5 times.
✗ Branch 4 not taken.
✓ Branch 6 taken 5 times.
311 CHRONO_BATCH_RENDER_ZONE("Materials/Resolve");
821 311 ++m_resolves;
822
1/1
✓ Branch 1 taken 311 times.
311 const Prims& prims = m_structure.GetPrims();
823
1/1
✓ Branch 1 taken 311 times.
311 m_pack.Clear();
824 311 m_bindings.clear();
825 311 m_spectral.clear();
826 311 m_wordCount = 0;
827
1/1
✓ Branch 1 taken 311 times.
311 m_connections.Update(changes);
828
1/1
✓ Branch 1 taken 311 times.
311 m_touched.assign(prims.Size(), false);
829 311 std::vector<MaterialRecord> before;
830
2/2
✓ Branch 0 taken 298 times.
✓ Branch 1 taken 311 times.
609 for (uint32_t prim = 0; prim < m_generations.size(); ++prim) {
831
2/3
✓ Branch 0 taken 298 times.
✗ Branch 1 not taken.
✓ Branch 3 taken 298 times.
596 before.push_back(m_records.Row(prim));
832 }
833
2/3
✗ Branch 0 not taken.
✓ Branch 1 taken 311 times.
✓ Branch 3 taken 311 times.
311 m_records.Assign(std::max(prims.Size(), 1u), MaterialRecord{});
834
1/1
✓ Branch 1 taken 311 times.
311 ColumnTable& shaders = m_structure.GetColumnStore().Schema(SchemaId::Shader);
835
1/1
✓ Branch 1 taken 311 times.
311 const std::vector<uint32_t> ids =
836
2/2
✓ Branch 1 taken 311 times.
✓ Branch 4 taken 311 times.
622 Items<uint32_t>(shaders.Read(kShaderId.index, 0, shaders.Rows()));
837 311 uint32_t materials = 0;
838
2/2
✓ Branch 0 taken 2167 times.
✓ Branch 1 taken 311 times.
2478 for (uint32_t prim = 0; prim < prims.Size(); ++prim) {
839
3/3
✓ Branch 1 taken 2167 times.
✓ Branch 3 taken 3 times.
✓ Branch 4 taken 2164 times.
2167 if (!prims.Alive(prim)) {
840 3 continue;
841 }
842
1/1
✓ Branch 1 taken 2164 times.
2164 const SchemaId schema = prims.Record(prim).schema;
843
3/3
✓ Branch 1 taken 2164 times.
✓ Branch 3 taken 89 times.
✓ Branch 4 taken 2075 times.
2164 if (IsA(schema, SchemaId::Material)) {
844
1/1
✓ Branch 1 taken 89 times.
89 ResolveMaterial(prim);
845 89 ++materials;
846
3/3
✓ Branch 1 taken 2075 times.
✓ Branch 3 taken 287 times.
✓ Branch 4 taken 1788 times.
2075 } else if (IsA(schema, SchemaId::Shader)) {
847 287 const std::string_view id =
848
2/2
✓ Branch 1 taken 287 times.
✓ Branch 4 taken 287 times.
287 m_structure.GetTokens().Text(Token{ids[prims.Row(prim, SchemaId::Shader)]});
849
3/4
✓ Branch 0 taken 27 times.
✓ Branch 1 taken 260 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 287 times.
574 if (id != Info(schema).name) {
850
0/3
✗ Branch 0 not taken.
✗ Branch 1 not taken.
✗ Branch 3 not taken.
✗ Log(LogLevel::Warning, "{} has info:id {} but is a {}, so it runs {}",
851
0/2
✗ Branch 1 not taken.
✗ Branch 4 not taken.
✗ m_structure.GetPaths().Path(prim), id, Info(schema).name, Info(schema).name);
852 }
853 }
854 }
855
1/1
✓ Branch 1 taken 311 times.
311 CarryPrevious(before);
856
1/1
✓ Branch 1 taken 311 times.
311 ResolveRadios();
857
2/3
✓ Branch 1 taken 311 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 311 times.
311 if (const uint32_t twice = m_pack.Twice(m_wordCount); twice != kNoPrim) {
858
0/1
✗ Branch 1 not taken.
✗ Log(LogLevel::Error, "material word {} is packed twice, so its value is undefined", twice);
859 }
860 311 Log(LogLevel::Debug, "resolved {} materials into {} words, {} copies and {} textures",
861
1/1
✓ Branch 1 taken 311 times.
311 materials, m_wordCount, m_pack.CopyCount(), m_bindings.size());
862 311 }
863
864 311 void MaterialTable::CarryPrevious(const std::vector<MaterialRecord>& before) {
865 311 const Prims& prims = m_structure.GetPrims();
866 311 m_carries.clear();
867
1/1
✓ Branch 2 taken 311 times.
311 std::vector<uint32_t> generations(prims.Size(), 0);
868
1/1
✓ Branch 1 taken 311 times.
311 std::set<uint32_t> used;
869
1/1
✓ Branch 1 taken 311 times.
311 const auto library = m_library.Snapshot();
870 317 const auto definitionWords = [&](uint32_t id) -> uint32_t {
871
1/2
✓ Branch 0 taken 6 times.
✗ Branch 1 not taken.
6 if (id == kDefinitionPreviewConstant || id == kDefinitionPreviewTextured) {
872 return Word(sizeof(PreviewParams));
873 }
874 6 const std::shared_ptr<const material::MaterialDefinition> found = library->Find(id);
875
4/4
✓ Branch 0 taken 5 times.
✓ Branch 1 taken 1 time.
✓ Branch 2 taken 5 times.
✓ Branch 3 taken 1 time.
6 return found ? found->words : 0;
876 6 };
877
2/2
✓ Branch 0 taken 2167 times.
✓ Branch 1 taken 311 times.
2478 for (uint32_t prim = 0; prim < prims.Size(); ++prim) {
878
7/7
✓ Branch 1 taken 2167 times.
✓ Branch 3 taken 2164 times.
✓ Branch 4 taken 3 times.
✓ Branch 6 taken 2164 times.
✓ Branch 9 taken 2164 times.
✓ Branch 11 taken 2075 times.
✓ Branch 12 taken 89 times.
2167 if (!prims.Alive(prim) || !IsA(prims.Record(prim).schema, SchemaId::Material)) {
879 2078 continue;
880 }
881
2/3
✓ Branch 1 taken 89 times.
✓ Branch 3 taken 89 times.
✗ Branch 4 not taken.
89 generations[prim] = prims.Generation(prim) + 1;
882
1/2
✓ Branch 0 taken 89 times.
✗ Branch 1 not taken.
89 MaterialRecord record = m_records.Row(prim);
883
2/2
✓ Branch 0 taken 74 times.
✓ Branch 1 taken 15 times.
89 if (record.definition < material::kFirstGeneratedDefinition) {
884 74 record.previousDefinition = kDefinitionPlaceholder;
885 74 record.previousWords = 0;
886
1/1
✓ Branch 1 taken 74 times.
74 m_records.Set(prim, record);
887
3/4
✓ Branch 0 taken 6 times.
✓ Branch 1 taken 9 times.
✓ Branch 2 taken 6 times.
✗ Branch 3 not taken.
15 } else if (prim < before.size() && m_generations[prim] == generations[prim]) {
888
2/2
✓ Branch 0 taken 1 time.
✓ Branch 1 taken 5 times.
6 const MaterialRecord& old = before[prim];
889 6 const bool changed = old.definition != record.definition;
890
2/2
✓ Branch 0 taken 1 time.
✓ Branch 1 taken 5 times.
6 const uint32_t previous = changed ? old.definition : old.previousDefinition;
891
2/2
✓ Branch 0 taken 5 times.
✓ Branch 1 taken 1 time.
6 const uint32_t from = changed ? old.words : old.previousWords;
892 6 const uint32_t words = definitionWords(previous);
893 6 record.previousDefinition = previous;
894 6 record.previousWords = 0;
895
2/2
✓ Branch 0 taken 4 times.
✓ Branch 1 taken 2 times.
6 if (words > 0) {
896 4 m_wordCount = (m_wordCount + 1) & ~1u;
897 4 record.previousWords = m_wordCount;
898
1/1
✓ Branch 1 taken 4 times.
4 m_carries.push_back({from, m_wordCount, words});
899 4 m_wordCount += words;
900 }
901
4/5
✓ Branch 0 taken 5 times.
✓ Branch 1 taken 1 time.
✓ Branch 3 taken 5 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 5 times.
6 if (changed && GetLogLevel() <= LogLevel::Debug) {
902
0/1
✗ Branch 1 not taken.
✗ Log(LogLevel::Debug,
903 "{} changes from definition {} to {}, and shows {} until the renderer links it",
904 ✗ m_structure.GetPaths().Path(prim), old.definition, record.definition,
905
0/1
✗ Branch 1 not taken.
✗ old.definition);
906 }
907
1/1
✓ Branch 1 taken 6 times.
6 m_records.Set(prim, record);
908 }
909
1/1
✓ Branch 1 taken 89 times.
89 used.insert(record.definition);
910
1/1
✓ Branch 1 taken 89 times.
89 used.insert(record.previousDefinition);
911 }
912 311 m_generations = std::move(generations);
913
3/4
✓ Branch 0 taken 4 times.
✓ Branch 1 taken 307 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 4 times.
311 if (!m_carries.empty() && m_words.Buffer()) {
914
1/1
✓ Branch 1 taken 4 times.
4 const Ref<device::Buffer>& arena = m_words.Buffer();
915
1/1
✓ Branch 1 taken 4 times.
4 device::Device& device = m_structure.GetColumnStore().GetDevice();
916 4 m_carried =
917
3/3
✓ Branch 1 taken 4 times.
✓ Branch 3 taken 1 time.
✓ Branch 4 taken 3 times.
5 device.CreateBuffer({.bytes = arena->Bytes(), .label = "carried material words"});
918 4 device::Queue& queue = device.GetQueue();
919
1/1
✓ Branch 1 taken 4 times.
4 device::CommandEncoder encoder = queue.CreateEncoder();
920
3/3
✓ Branch 1 taken 4 times.
✓ Branch 4 taken 4 times.
✓ Branch 7 taken 4 times.
4 encoder.Copy(arena->Slice(0), m_carried->Slice(0));
921
2/2
✓ Branch 1 taken 4 times.
✓ Branch 4 taken 4 times.
4 queue.Submit(encoder.Finish());
922
1/2
✓ Branch 0 taken 307 times.
✗ Branch 1 not taken.
311 } else if (m_carries.empty()) {
923
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 307 times.
307 m_carried = nullptr;
924 }
925 311 std::vector<material::MaterialDefinition> definitions;
926
2/2
✓ Branch 0 taken 21 times.
✓ Branch 1 taken 311 times.
332 for (const auto& entry : library->Entries()) {
927
2/2
✓ Branch 1 taken 20 times.
✓ Branch 2 taken 1 time.
21 if (used.contains(entry.id)) {
928
1/1
✓ Branch 1 taken 20 times.
20 definitions.push_back(*entry.definition);
929 }
930 }
931 311 const bool planetary = std::ranges::any_of(used, [](uint32_t id) {
932
2/2
✓ Branch 0 taken 126 times.
✓ Branch 1 taken 15 times.
141 return id >= material::kFirstPlanetaryDefinition &&
933 id < material::kFirstGeneratedDefinition;
934 311 });
935 311 auto set = std::make_shared<const material::MaterialDefinitionSet>(
936
2/2
✓ Branch 1 taken 311 times.
✓ Branch 4 taken 311 times.
622 std::move(definitions), material::ClosurePrimitiveSources(material::ClosurePrimitives()),
937 311 planetary);
938
2/2
✓ Branch 0 taken 61 times.
✓ Branch 1 taken 250 times.
311 if (set->Hash() != m_definitions->Hash()) {
939
2/2
✓ Branch 1 taken 61 times.
✓ Branch 4 taken 61 times.
61 Log(LogLevel::Debug, "the scene now uses {}",
940
1/3
✓ Branch 1 taken 61 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
61 Counted(set->Definitions().size(), "generated material definition"));
941 61 m_definitions = std::move(set);
942 }
943 630 }
944
945 311 void MaterialTable::ResolveRadios() {
946 311 const Prims& prims = m_structure.GetPrims();
947 311 m_radarLinks.clear();
948
2/2
✓ Branch 1 taken 2167 times.
✓ Branch 2 taken 311 times.
2478 for (uint32_t material = 0; material < prims.Size(); ++material) {
949
4/4
✓ Branch 1 taken 2164 times.
✓ Branch 2 taken 3 times.
✓ Branch 5 taken 2075 times.
✓ Branch 6 taken 89 times.
2167 if (!prims.Alive(material) || !IsA(prims.Record(material).schema, SchemaId::Material)) {
950 2078 continue;
951 }
952
2/2
✓ Branch 1 taken 87 times.
✓ Branch 2 taken 89 times.
176 for (const Connection& connection : Connections(material)) {
953
2/2
✓ Branch 0 taken 86 times.
✓ Branch 1 taken 1 time.
87 if (connection.input != "radar") {
954 86 continue;
955 }
956
1/2
✓ Branch 0 taken 1 time.
✗ Branch 1 not taken.
1 if (connection.source == kNoPrim ||
957
1/2
✓ Branch 1 taken 1 time.
✗ Branch 2 not taken.
1 prims.Record(connection.source).schema != SchemaId::ItuMaterial) {
958
0/1
✗ Branch 1 not taken.
✗ Log(LogLevel::Warning,
959 "{}'s radar connects to {}, which isn't an ItuMaterial; radar sees its surface "
960 "instead",
961
0/1
✗ Branch 1 not taken.
✗ m_structure.GetPaths().Path(material),
962
0/2
✗ Branch 0 not taken.
✗ Branch 1 not taken.
✗ connection.source == kNoPrim ? std::string("nothing")
963 ✗ : m_structure.GetPaths().Path(connection.source));
964 ✗ continue;
965 }
966 1 m_radarLinks.push_back({material, prims.Row(connection.source, SchemaId::ItuMaterial)});
967 }
968 }
969 311 }
970
971 334 void MaterialTable::WriteRadios() {
972
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 334 times.
334 ColumnStore& store = m_structure.GetColumnStore();
973
3/4
✗ Branch 0 not taken.
✓ Branch 1 taken 334 times.
✓ Branch 3 taken 9 times.
✓ Branch 4 taken 325 times.
334 std::vector<RadioRecord> radios(std::max(m_structure.GetPrims().Size(), 1u));
974
2/2
✓ Branch 0 taken 9 times.
✓ Branch 1 taken 325 times.
334 if (!m_radarLinks.empty()) {
975
1/1
✓ Branch 1 taken 9 times.
9 ColumnTable& itu = store.Schema(SchemaId::ItuMaterial);
976 27 const auto column = [&](ColumnRef ref) { return itu.Read(ref.index, 0, itu.Rows()); };
977
2/2
✓ Branch 1 taken 9 times.
✓ Branch 5 taken 9 times.
9 const std::vector<uint32_t> kinds = Items<uint32_t>(column(kItuMaterialMaterial));
978
2/2
✓ Branch 1 taken 9 times.
✓ Branch 5 taken 9 times.
9 const std::vector<float> heights = Items<float>(column(kItuMaterialRmsHeight));
979
2/2
✓ Branch 1 taken 9 times.
✓ Branch 5 taken 9 times.
9 const std::vector<float> lengths = Items<float>(column(kItuMaterialCorrelationLength));
980
2/2
✓ Branch 0 taken 9 times.
✓ Branch 1 taken 9 times.
18 for (const RadarLink& link : m_radarLinks) {
981
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 9 times.
9 const uint32_t kind = std::min<uint32_t>(kinds[link.row], std::size(kItuMaterials) - 1);
982 9 radios[link.material] = {.kind = 1,
983 .valid = 1,
984 9 .rmsHeight = heights[link.row],
985 9 .correlationLength = lengths[link.row],
986 9 .data = kItuMaterials[kind]};
987 }
988 9 }
989
2/2
✓ Branch 0 taken 7701 times.
✓ Branch 1 taken 334 times.
8035 for (uint32_t row = 0; row < radios.size(); ++row) {
990
1/1
✓ Branch 1 taken 7701 times.
7701 m_radios.Set(row, radios[row]);
991 }
992
2/2
✓ Branch 0 taken 3969 times.
✓ Branch 1 taken 334 times.
4303 for (uint32_t row = uint32_t(radios.size()); row < m_radios.Rows(); ++row) {
993
1/1
✓ Branch 1 taken 3969 times.
3969 m_radios.Set(row, RadioRecord{});
994 }
995
1/1
✓ Branch 1 taken 334 times.
334 m_radios.Upload(store.GetDevice());
996 334 }
997
998 320 void MaterialTable::WarnOutsideVisible() {
999 320 const Prims& prims = m_structure.GetPrims();
1000 320 ColumnStore& store = m_structure.GetColumnStore();
1001 320 ColumnTable& detectors = store.Schema(SchemaId::DetectorAPI);
1002 320 const std::vector<Float2> ranges =
1003
1/1
✓ Branch 2 taken 320 times.
640 Items<Float2>(detectors.Read(kDetectorAPIDetectorSpectralRange.index, 0, detectors.Rows()));
1004
1/1
✓ Branch 1 taken 320 times.
320 const std::vector<uint32_t> owners = Items<uint32_t>(
1005
3/3
✓ Branch 1 taken 320 times.
✓ Branch 4 taken 320 times.
✓ Branch 7 taken 320 times.
640 detectors.Read(store.PrimsColumn(SchemaId::DetectorAPI), 0, detectors.Rows()));
1006
2/2
✓ Branch 0 taken 216 times.
✓ Branch 1 taken 320 times.
536 for (uint32_t row = 0; row < ranges.size(); ++row) {
1007
1/2
✓ Branch 0 taken 216 times.
✗ Branch 1 not taken.
216 const uint32_t sensor = owners[row];
1008
4/7
✓ Branch 0 taken 216 times.
✗ Branch 1 not taken.
✓ Branch 3 taken 216 times.
✓ Branch 5 taken 216 times.
✗ Branch 6 not taken.
✓ Branch 7 taken 216 times.
✗ Branch 8 not taken.
216 if (sensor == kNoPrim || !prims.Alive(sensor) ||
1009
3/4
✓ Branch 0 taken 216 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 205 times.
✓ Branch 3 taken 11 times.
216 (ranges[row].x >= kVisibleFirstNm && ranges[row].y <= kVisibleLastNm)) {
1010 205 continue;
1011 }
1012
2/2
✓ Branch 0 taken 78 times.
✓ Branch 1 taken 11 times.
89 for (uint32_t material = 0; material < prims.Size(); ++material) {
1013
7/7
✓ Branch 1 taken 78 times.
✓ Branch 4 taken 78 times.
✓ Branch 7 taken 78 times.
✓ Branch 9 taken 6 times.
✓ Branch 10 taken 72 times.
✓ Branch 11 taken 4 times.
✓ Branch 12 taken 2 times.
162 if (!prims.Alive(material) || !IsA(prims.Record(material).schema, SchemaId::Material) ||
1014
1/2
✓ Branch 0 taken 78 times.
✗ Branch 1 not taken.
84 m_spectral.contains(material) ||
1015 !m_warned
1016
1/1
✓ Branch 1 taken 4 times.
4 .emplace(uint64_t(prims.Generation(material)) << 32 | material,
1017
2/2
✓ Branch 1 taken 4 times.
✓ Branch 4 taken 4 times.
4 uint64_t(prims.Generation(sensor)) << 32 | sensor)
1018
2/2
✓ Branch 0 taken 3 times.
✓ Branch 1 taken 1 time.
4 .second) {
1019 75 continue;
1020 }
1021
1/1
✓ Branch 1 taken 3 times.
3 Log(LogLevel::Warning,
1022 "{} has only RGB colours, but {} sees {} to {} nm, so it sees them extended by a "
1023 "smooth spectrum; connect a SampledSpectrum for measured values",
1024
2/2
✓ Branch 1 taken 3 times.
✓ Branch 4 taken 3 times.
6 m_structure.GetPaths().Path(material), m_structure.GetPaths().Path(sensor),
1025
1/1
✓ Branch 1 taken 3 times.
3 ranges[row].x, ranges[row].y);
1026 }
1027 }
1028 320 }
1029
1030 350 void MaterialTable::BuildEntries() {
1031
2/2
✓ Branch 0 taken 25 times.
✓ Branch 1 taken 350 times.
375 for (const Binding& binding : m_bindings) {
1032 25 const uint64_t handle = m_textures.Handle(binding.texture, "file");
1033 25 m_pack.SetLiteral(binding.low, uint32_t(handle));
1034 25 m_pack.SetLiteral(binding.high, uint32_t(handle >> 32));
1035 }
1036 350 m_pack.Build(m_structure.GetColumnStore(), true);
1037 350 }
1038
1039 536 void MaterialTable::Run(const ChangeSet& changes, TableVersions&) {
1040
4/5
✓ Branch 0 taken 5 times.
✓ Branch 1 taken 531 times.
✓ Branch 3 taken 5 times.
✗ Branch 4 not taken.
✓ Branch 6 taken 5 times.
536 CHRONO_BATCH_RENDER_ZONE("Materials/Run");
1041
1/1
✓ Branch 1 taken 536 times.
536 ColumnStore& store = m_structure.GetColumnStore();
1042
1/1
✓ Branch 1 taken 536 times.
536 device::Device& device = store.GetDevice();
1043
1/1
✓ Branch 1 taken 536 times.
536 const uint64_t writes = store.WriteSequence();
1044
6/7
✓ Branch 1 taken 536 times.
✓ Branch 3 taken 226 times.
✓ Branch 4 taken 310 times.
✓ Branch 5 taken 226 times.
✗ Branch 6 not taken.
✓ Branch 7 taken 1 time.
✓ Branch 8 taken 225 times.
762 const bool resolve = Restructured(changes) || !m_records.Buffer() ||
1045
1/1
✓ Branch 1 taken 226 times.
226 m_nodeVersion != material::MaterialNodeVersion();
1046 311 if (resolve) {
1047
1/1
✓ Branch 1 taken 311 times.
311 m_nodeVersion = material::MaterialNodeVersion();
1048
1/1
✓ Branch 1 taken 311 times.
311 Resolve(changes);
1049 }
1050
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 536 times.
536 const uint32_t prims = m_structure.GetPrims().Size();
1051
2/3
✗ Branch 0 not taken.
✓ Branch 1 taken 536 times.
✓ Branch 3 taken 536 times.
536 m_records.Reserve(std::max(prims, 1u));
1052
1/1
✓ Branch 1 taken 536 times.
536 m_records.Upload(device);
1053
5/5
✓ Branch 0 taken 225 times.
✓ Branch 1 taken 311 times.
✓ Branch 3 taken 225 times.
✓ Branch 5 taken 39 times.
✓ Branch 6 taken 186 times.
536 const bool rebound = resolve || changes.Wrote(TableId::Textures);
1054 350 if (rebound) {
1055
1/1
✓ Branch 1 taken 350 times.
350 BuildEntries();
1056 }
1057
2/2
✓ Branch 0 taken 311 times.
✓ Branch 1 taken 225 times.
536 if (resolve) {
1058
3/3
✓ Branch 0 taken 247 times.
✓ Branch 1 taken 64 times.
✓ Branch 3 taken 311 times.
558 m_words.Reserve(device, std::max(m_wordCount, 1u));
1059
1/1
✓ Branch 1 taken 311 times.
311 device::Queue& queue = device.GetQueue();
1060
1/1
✓ Branch 1 taken 311 times.
311 device::CommandEncoder encoder = queue.CreateEncoder();
1061
2/2
✓ Branch 1 taken 311 times.
✓ Branch 4 taken 311 times.
311 encoder.Fill(m_words.Buffer()->Slice(0, uint64_t(m_words.Buffer()->Bytes())));
1062
3/4
✓ Branch 0 taken 4 times.
✓ Branch 1 taken 307 times.
✓ Branch 2 taken 4 times.
✗ Branch 3 not taken.
311 if (m_carried && !m_carries.empty()) {
1063
1/1
✓ Branch 1 taken 4 times.
4 encoder.Barrier();
1064
2/2
✓ Branch 0 taken 4 times.
✓ Branch 1 taken 4 times.
8 for (const Carry& carry : m_carries) {
1065
2/2
✓ Branch 1 taken 4 times.
✓ Branch 4 taken 4 times.
4 encoder.Copy(m_carried->Slice(uint64_t(carry.from) * sizeof(uint32_t),
1066
1/1
✓ Branch 1 taken 4 times.
4 uint64_t(carry.words) * sizeof(uint32_t)),
1067
1/1
✓ Branch 1 taken 4 times.
4 m_words.Buffer()->Slice(uint64_t(carry.to) * sizeof(uint32_t),
1068
1/1
✓ Branch 1 taken 4 times.
4 uint64_t(carry.words) * sizeof(uint32_t)));
1069 }
1070 }
1071
2/2
✓ Branch 1 taken 311 times.
✓ Branch 4 taken 311 times.
311 queue.Submit(encoder.Finish());
1072 311 }
1073
5/5
✓ Branch 0 taken 186 times.
✓ Branch 1 taken 350 times.
✓ Branch 3 taken 186 times.
✓ Branch 5 taken 19 times.
✓ Branch 6 taken 167 times.
536 if (rebound || m_pack.Stale(store, m_writes)) {
1074
2/3
✓ Branch 0 taken 369 times.
✗ Branch 1 not taken.
✓ Branch 3 taken 369 times.
738 m_pack.Pack(store, m_words.Address());
1075 }
1076
5/5
✓ Branch 0 taken 225 times.
✓ Branch 1 taken 311 times.
✓ Branch 3 taken 225 times.
✓ Branch 5 taken 9 times.
✓ Branch 6 taken 216 times.
536 if (resolve || Changed(SchemaId::DetectorAPI)) {
1077
1/1
✓ Branch 1 taken 320 times.
320 WarnOutsideVisible();
1078 }
1079
2/2
✓ Branch 1 taken 536 times.
✓ Branch 4 taken 536 times.
536 m_checkers.Reserve(device, store.Schema(SchemaId::CheckerboardAPI).Capacity());
1080
2/3
✗ Branch 0 not taken.
✓ Branch 1 taken 536 times.
✓ Branch 3 taken 536 times.
536 m_temperatures.Reserve(device, std::max(prims, 1u));
1081
1/1
✓ Branch 1 taken 536 times.
536 m_temperaturePrimvar = m_structure.GetTokens().Intern("chrono:temperature").id;
1082
7/7
✓ Branch 0 taken 225 times.
✓ Branch 1 taken 311 times.
✓ Branch 2 taken 210 times.
✓ Branch 3 taken 15 times.
✓ Branch 5 taken 210 times.
✓ Branch 7 taken 8 times.
✓ Branch 8 taken 202 times.
536 if (resolve || prims > m_radios.Rows() || Changed(SchemaId::ItuMaterial)) {
1083
1/1
✓ Branch 1 taken 334 times.
334 WriteRadios();
1084 }
1085 536 m_writes = writes;
1086
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 536 times.
536 if (prims == 0) {
1087 ✗ return;
1088 }
1089 536 MaterialParams params{};
1090
1/1
✓ Branch 1 taken 536 times.
536 params.scene.address = store.DeviceData();
1091
1/2
✓ Branch 0 taken 536 times.
✗ Branch 1 not taken.
536 params.checkers.address = m_checkers.Address();
1092
1/2
✓ Branch 0 taken 536 times.
✗ Branch 1 not taken.
536 params.temperatures.address = m_temperatures.Address();
1093
2/2
✓ Branch 0 taken 5 times.
✓ Branch 1 taken 531 times.
536 device::Queue& queue = device.GetQueue();
1094
4/5
✓ Branch 0 taken 5 times.
✓ Branch 1 taken 531 times.
✓ Branch 3 taken 5 times.
✗ Branch 4 not taken.
✓ Branch 6 taken 5 times.
536 static const profiler::ZoneId zone = profiler::ZoneId::Named("Materials/Write");
1095
1/1
✓ Branch 1 taken 536 times.
536 device::CommandEncoder encoder = queue.CreateEncoder();
1096 536 {
1097
1/1
✓ Branch 1 taken 536 times.
536 const device::ZoneScope scope(encoder, zone);
1098
3/3
✓ Branch 1 taken 536 times.
✓ Branch 4 taken 536 times.
✓ Branch 7 taken 536 times.
536 encoder.Dispatch(Kernels().Entry("WriteMaterials"), params,
1099
1/1
✓ Branch 1 taken 536 times.
536 {.x = device::GroupCount(prims, kMaterialGroup)});
1100 536 }
1101
2/2
✓ Branch 1 taken 536 times.
✓ Branch 4 taken 536 times.
536 queue.Submit(encoder.Finish());
1102 536 }
1103
1104 353 void MaterialTable::Prepare() {
1105 353 Kernels();
1106 353 }
1107
1108 889 device::Program& MaterialTable::Kernels() {
1109
2/2
✓ Branch 0 taken 353 times.
✓ Branch 1 taken 536 times.
889 if (!m_program) {
1110
2/2
✓ Branch 2 taken 353 times.
✓ Branch 5 taken 353 times.
1059 m_program = m_structure.GetColumnStore().GetDevice().CreateProgram(
1111 {.modules = {"chrono_batch_render.scene.tables.kernels.materials"},
1112 .library = &kSceneKernels,
1113 .entries = {"WriteMaterials"},
1114
2/3
✓ Branch 1 taken 353 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 353 times.
706 .label = "materials"});
1115 }
1116 889 return *m_program;
1117 706 }
1118
1119 }
1120