stage/structure/prims.cpp
| Line | Branch | Exec | Source |
|---|---|---|---|
| 1 | // SPDX-License-Identifier: BSD-3-Clause | ||
| 2 | #include "chrono_batch_render/stage/structure/prims.h" | ||
| 3 | |||
| 4 | #include <algorithm> | ||
| 5 | #include <set> | ||
| 6 | |||
| 7 | #include "chrono_batch_render/util/error.h" | ||
| 8 | |||
| 9 | namespace chrono::batch_render { | ||
| 10 | |||
| 11 | namespace { | ||
| 12 | |||
| 13 | 242875 | std::span<const std::byte> Word(const uint32_t& value) { | |
| 14 | 242875 | return std::as_bytes(std::span(&value, 1)); | |
| 15 | } | ||
| 16 | |||
| 17 | 91298 | uint64_t BlockApis(const BlockShape& shape, uint32_t index) { | |
| 18 | 91298 | const uint64_t applied = shape.apis.empty() ? 0 : shape.apis[index]; | |
| 19 | 91298 | return (Info(shape.schemas[index]).apis | applied) & ~kMultipleApis; | |
| 20 | } | ||
| 21 | |||
| 22 | 7629 | uint64_t InstanceKey(uint32_t prim, SchemaId api) { | |
| 23 | 7629 | return (uint64_t(prim) << 6) | Info(api).api; | |
| 24 | } | ||
| 25 | |||
| 26 | } | ||
| 27 | |||
| 28 | 1053446 | bool Prims::Alive(uint32_t prim) const { | |
| 29 | 1053446 | return prim < m_records.size() && (m_records[prim].flags & kPrimAlive) != 0; | |
| 30 | } | ||
| 31 | |||
| 32 | 329583 | uint32_t Prims::Generation(uint32_t prim) const { | |
| 33 | 329583 | if (prim >= m_records.size()) { | |
| 34 | ✗ | throw Error("No prim {}", prim); | |
| 35 | } | ||
| 36 | 329583 | return m_records[prim].generation; | |
| 37 | } | ||
| 38 | |||
| 39 | 606929 | const PrimRecord& Prims::Record(uint32_t prim) const { | |
| 40 | 606929 | if (!Alive(prim)) { | |
| 41 | ✗ | throw Error("No prim {}", prim); | |
| 42 | } | ||
| 43 | 606929 | return m_records[prim]; | |
| 44 | } | ||
| 45 | |||
| 46 | 88414 | uint32_t Prims::FindRow(uint32_t prim, SchemaId table) const { | |
| 47 | 88414 | const PrimRecord& record = Record(prim); | |
| 48 | 88414 | const uint32_t slot = PrimSlot(record.schema, record.apis, table); | |
| 49 | 88414 | return slot == kNoSlot ? kNoRow : m_slotRows[record.slots + slot]; | |
| 50 | } | ||
| 51 | |||
| 52 | 63 | std::vector<uint32_t> Prims::Rows(std::span<const uint32_t> prims, SchemaId table) const { | |
| 53 | 63 | std::vector<uint32_t> rows; | |
| 54 | 63 | rows.reserve(prims.size()); | |
| 55 | 130 | for (const uint32_t prim : prims) { | |
| 56 | 67 | rows.push_back(Alive(prim) ? FindRow(prim, table) : kNoRow); | |
| 57 | } | ||
| 58 | 63 | return rows; | |
| 59 | ✗ | } | |
| 60 | |||
| 61 | 85857 | uint32_t Prims::Row(uint32_t prim, SchemaId table) const { | |
| 62 | 85857 | const uint32_t row = FindRow(prim, table); | |
| 63 | 85857 | if (row == kNoRow) { | |
| 64 | ✗ | throw Error("Prim {}, a {}, has no {} data", prim, Info(Record(prim).schema).name, | |
| 65 | ✗ | Info(table).name); | |
| 66 | } | ||
| 67 | 85857 | return row; | |
| 68 | } | ||
| 69 | |||
| 70 | 22728 | bool Prims::HasApi(uint32_t prim, SchemaId api) const { | |
| 71 | 22728 | return chrono::batch_render::HasApi(Record(prim).apis, api); | |
| 72 | } | ||
| 73 | |||
| 74 | 7441 | std::span<const ApiInstance> Prims::Instances(uint32_t prim, SchemaId api) const { | |
| 75 | 7441 | const auto found = m_instances.find(InstanceKey(prim, api)); | |
| 76 | 7696 | return found == m_instances.end() ? std::span<const ApiInstance>() : found->second; | |
| 77 | } | ||
| 78 | |||
| 79 | 95 | uint32_t Prims::InstanceRow(uint32_t prim, SchemaId api, uint32_t instance) const { | |
| 80 | 96 | for (const ApiInstance& entry : Instances(prim, api)) { | |
| 81 | 40 | if (entry.instance == instance) { | |
| 82 | 39 | return entry.row; | |
| 83 | } | ||
| 84 | } | ||
| 85 | 56 | return kNoRow; | |
| 86 | } | ||
| 87 | |||
| 88 | 2722 | void Prims::Check(const BlockShape& shape, std::span<const uint32_t> parents) const { | |
| 89 | 2722 | if (shape.parents.size() != shape.schemas.size()) { | |
| 90 | ✗ | throw Error("A block of {} prims with {} parents", shape.schemas.size(), | |
| 91 | ✗ | shape.parents.size()); | |
| 92 | } | ||
| 93 | 2722 | if (!shape.apis.empty() && shape.apis.size() != shape.schemas.size()) { | |
| 94 | ✗ | throw Error("A block of {} prims with {} API masks", shape.schemas.size(), | |
| 95 | ✗ | shape.apis.size()); | |
| 96 | } | ||
| 97 | 2722 | bool worlds = false; | |
| 98 | 5520 | for (uint32_t i = 0; i < shape.schemas.size(); ++i) { | |
| 99 | 2799 | const SchemaId schema = shape.schemas[i]; | |
| 100 | 2799 | if (uint32_t(schema) >= SchemaCount()) { | |
| 101 | ✗ | throw Error("No schema {}", uint32_t(schema)); | |
| 102 | } | ||
| 103 | 5598 | if (Info(schema).kind != SchemaKind::Concrete) { | |
| 104 | 2 | throw Error("{} is {}, so no prim is one", Info(schema).name, | |
| 105 | 4 | Info(schema).kind == SchemaKind::Api ? "an API schema" : "abstract"); | |
| 106 | } | ||
| 107 | 64354 | for (const SchemaId api : kApiSchemas) { | |
| 108 | 61589 | if (chrono::batch_render::HasApi(BlockApis(shape, i), api) && !Includes(schema, api) && | |
| 109 | 33 | !AppliesTo(api, schema)) { | |
| 110 | ✗ | throw Error("{} doesn't apply to {}", Info(api).name, Info(schema).name); | |
| 111 | } | ||
| 112 | } | ||
| 113 | 2798 | const uint32_t parent = shape.parents[i]; | |
| 114 | 2798 | if (parent != kNoPrim && parent >= i) { | |
| 115 | ✗ | throw Error("Prim {} of a block has parent {}, which doesn't come before it", i, | |
| 116 | ✗ | parent); | |
| 117 | } | ||
| 118 | 2798 | if (IsA(schema, SchemaId::World)) { | |
| 119 | 139 | worlds = true; | |
| 120 | 139 | for (uint32_t up = parent; up != kNoPrim; up = shape.parents[up]) { | |
| 121 | ✗ | if (IsA(shape.schemas[up], SchemaId::World)) { | |
| 122 | ✗ | throw Error("A World inside a World; worlds don't nest"); | |
| 123 | } | ||
| 124 | } | ||
| 125 | } | ||
| 126 | } | ||
| 127 | 26786 | for (const uint32_t parent : parents) { | |
| 128 | 24068 | if (parent == kNoPrim) { | |
| 129 | 22901 | continue; | |
| 130 | } | ||
| 131 | 1167 | const PrimRecord& record = Record(parent); | |
| 132 | 1167 | if (worlds && record.world != kGlobalWorld) { | |
| 133 | 3 | throw Error("Worlds added inside world {}; worlds don't nest", record.world); | |
| 134 | } | ||
| 135 | } | ||
| 136 | 2718 | } | |
| 137 | |||
| 138 | 2737 | uint32_t Prims::AcquireSlots(uint32_t count) { | |
| 139 | 2737 | if (count == 0) { | |
| 140 | return kNoRow; | ||
| 141 | } | ||
| 142 | 2737 | std::vector<uint32_t>& free = m_freeSlots[count]; | |
| 143 | 2737 | if (!free.empty()) { | |
| 144 | 164 | const uint32_t first = free.back(); | |
| 145 | 164 | free.pop_back(); | |
| 146 | 164 | return first; | |
| 147 | } | ||
| 148 | 2573 | const uint32_t first = m_store.Slots().Allocate(count); | |
| 149 | 2573 | m_slotRows.resize(m_store.Slots().Rows(), kNoRow); | |
| 150 | 2573 | return first; | |
| 151 | } | ||
| 152 | |||
| 153 | 2591 | PrimBlock Prims::AllocateOne(SchemaId schema, uint64_t apis) { | |
| 154 | 2591 | PrimBlock block; | |
| 155 | 2591 | block.count = 1; | |
| 156 | 2591 | block.copies = 1; | |
| 157 | 2591 | if (m_freePrims.empty()) { | |
| 158 | 2477 | block.firstPrim = m_store.Prims().Allocate(1); | |
| 159 | 2477 | m_records.resize(m_store.Prims().Rows()); | |
| 160 | } else { | ||
| 161 | 114 | block.firstPrim = m_freePrims.back(); | |
| 162 | 114 | m_freePrims.pop_back(); | |
| 163 | } | ||
| 164 | 2591 | const std::vector<SchemaId> tables = PrimTables(schema, apis); | |
| 165 | 2591 | block.slotsPerCopy = uint32_t(tables.size()); | |
| 166 | 2591 | block.firstSlot = AcquireSlots(block.slotsPerCopy); | |
| 167 | 12958 | for (const SchemaId table : tables) { | |
| 168 | 10367 | block.tables.push_back({table, m_store.Schema(table).Acquire(), 1}); | |
| 169 | } | ||
| 170 | 2591 | return block; | |
| 171 | 2591 | } | |
| 172 | |||
| 173 | 127 | PrimBlock Prims::AllocateBlock(const BlockShape& shape, uint32_t copies, bool pageAligned) { | |
| 174 | 127 | PrimBlock block; | |
| 175 | 127 | block.count = uint32_t(shape.schemas.size()); | |
| 176 | 127 | block.copies = copies; | |
| 177 | 127 | block.firstPrim = m_store.Prims().Allocate(block.count * copies); | |
| 178 | 127 | m_records.resize(m_store.Prims().Rows()); | |
| 179 | 127 | std::map<SchemaId, uint32_t> perCopy; | |
| 180 | 329 | for (uint32_t i = 0; i < block.count; ++i) { | |
| 181 | 202 | const std::vector<SchemaId> tables = PrimTables(shape.schemas[i], BlockApis(shape, i)); | |
| 182 | 1155 | for (const SchemaId table : tables) { | |
| 183 | 953 | ++perCopy[table]; | |
| 184 | } | ||
| 185 | 202 | block.slotsPerCopy += uint32_t(tables.size()); | |
| 186 | 202 | } | |
| 187 | 127 | if (block.slotsPerCopy > 0) { | |
| 188 | 127 | block.firstSlot = m_store.Slots().Allocate(block.slotsPerCopy * copies); | |
| 189 | } | ||
| 190 | 845 | for (const auto& [table, rows] : perCopy) { | |
| 191 | 718 | block.tables.push_back( | |
| 192 | 718 | {table, m_store.Schema(table).Allocate(rows * copies, pageAligned), rows}); | |
| 193 | } | ||
| 194 | 127 | return block; | |
| 195 | 127 | } | |
| 196 | |||
| 197 | 2722 | PrimBlock Prims::Add(const BlockShape& shape, std::span<const uint32_t> parents, | |
| 198 | uint32_t reuseWorld, bool pageAligned) { | ||
| 199 | 2722 | Check(shape, parents); | |
| 200 | 2718 | const uint32_t count = uint32_t(shape.schemas.size()); | |
| 201 | 2718 | const uint32_t copies = uint32_t(parents.size()); | |
| 202 | 2718 | if (count == 0 || copies == 0) { | |
| 203 | ✗ | return {}; | |
| 204 | } | ||
| 205 | 2591 | PrimBlock block = count == 1 && copies == 1 && !pageAligned | |
| 206 | 5309 | ? AllocateOne(shape.schemas[0], BlockApis(shape, 0)) | |
| 207 | 2718 | : AllocateBlock(shape, copies, pageAligned); | |
| 208 | 2718 | m_slotRows.resize(m_store.Slots().Rows(), kNoRow); | |
| 209 | |||
| 210 | 5511 | for (const SchemaId schema : shape.schemas) { | |
| 211 | 5450 | block.worldsPerCopy += IsA(schema, SchemaId::World) ? 1 : 0; | |
| 212 | } | ||
| 213 | 2718 | if (block.worldsPerCopy > 0) { | |
| 214 | 136 | block.firstWorld = reuseWorld == kGlobalWorld ? m_worldCount : reuseWorld; | |
| 215 | 272 | m_worldCount = std::max(m_worldCount, block.firstWorld + block.worldsPerCopy * copies); | |
| 216 | } | ||
| 217 | 2718 | m_store.SetCounts(Size(), m_worldCount); | |
| 218 | |||
| 219 | 2718 | std::vector<std::vector<uint32_t>> ordinals(count); | |
| 220 | 2718 | std::vector<uint32_t> slotOffsets(count); | |
| 221 | 2718 | std::map<SchemaId, uint32_t> used; | |
| 222 | 2718 | uint32_t offset = 0; | |
| 223 | 2718 | std::vector<std::vector<SchemaId>> primTables(count); | |
| 224 | 5511 | for (uint32_t i = 0; i < count; ++i) { | |
| 225 | 2793 | slotOffsets[i] = offset; | |
| 226 | 2793 | primTables[i] = PrimTables(shape.schemas[i], BlockApis(shape, i)); | |
| 227 | 14113 | for (const SchemaId table : primTables[i]) { | |
| 228 | 11320 | ordinals[i].push_back(used[table]++); | |
| 229 | 11320 | ++offset; | |
| 230 | } | ||
| 231 | } | ||
| 232 | 2718 | std::map<SchemaId, const BlockTable*> tables; | |
| 233 | 13803 | for (const BlockTable& table : block.tables) { | |
| 234 | 11085 | tables[table.table] = &table; | |
| 235 | } | ||
| 236 | |||
| 237 | 2718 | std::set<uint32_t> worlds; | |
| 238 | 2718 | std::vector<uint32_t> copyWorlds(count); | |
| 239 | 26782 | for (uint32_t copy = 0; copy < copies; ++copy) { | |
| 240 | uint32_t worldIndex = 0; | ||
| 241 | 48220 | for (uint32_t i = 0; i < count; ++i) { | |
| 242 | 24156 | const uint32_t up = shape.parents[i]; | |
| 243 | 24156 | PrimRecord record; | |
| 244 | 24156 | record.schema = shape.schemas[i]; | |
| 245 | 24156 | record.parent = up == kNoPrim ? parents[copy] : block.Prim(copy, up); | |
| 246 | 24156 | record.world = up != kNoPrim ? copyWorlds[up] | |
| 247 | 24064 | : parents[copy] == kNoPrim ? kGlobalWorld | |
| 248 | 1164 | : m_records[parents[copy]].world; | |
| 249 | 24156 | if (IsA(record.schema, SchemaId::World)) { | |
| 250 | 152 | record.world = block.firstWorld + copy * block.worldsPerCopy + worldIndex++; | |
| 251 | } | ||
| 252 | 24156 | copyWorlds[i] = record.world; | |
| 253 | 24156 | worlds.insert(record.world); | |
| 254 | 24156 | record.flags = kPrimAlive; | |
| 255 | 24156 | record.apis = BlockApis(shape, i); | |
| 256 | 24156 | const std::vector<SchemaId>& slots = primTables[i]; | |
| 257 | 48312 | record.slots = slots.empty() | |
| 258 | 24156 | ? kNoRow | |
| 259 | 24156 | : block.firstSlot + copy * block.slotsPerCopy + slotOffsets[i]; | |
| 260 | 24156 | std::vector<uint32_t> rows; | |
| 261 | 142284 | for (uint32_t slot = 0; slot < slots.size(); ++slot) { | |
| 262 | 118128 | const BlockTable& table = *tables.at(slots[slot]); | |
| 263 | 118128 | rows.push_back(table.first + copy * table.perCopy + ordinals[i][slot]); | |
| 264 | } | ||
| 265 | 24156 | Write(block.Prim(copy, i), record, rows); | |
| 266 | 24156 | } | |
| 267 | } | ||
| 268 | 2718 | if (block.slotsPerCopy > 0) { | |
| 269 | 2718 | const uint32_t slots = block.slotsPerCopy * copies; | |
| 270 | 2718 | m_store.Slots().Stage(0, block.firstSlot, | |
| 271 | 2718 | std::as_bytes(std::span(m_slotRows.data() + block.firstSlot, slots))); | |
| 272 | } | ||
| 273 | 5452 | for (const uint32_t world : worlds) { | |
| 274 | 2734 | m_store.StampWorld(world); | |
| 275 | } | ||
| 276 | 2718 | m_live += count * copies; | |
| 277 | 2718 | return block; | |
| 278 | 2718 | } | |
| 279 | |||
| 280 | 24156 | void Prims::Write(uint32_t prim, const PrimRecord& record, std::span<const uint32_t> rows) { | |
| 281 | 24156 | const uint32_t generation = m_records[prim].generation; | |
| 282 | 24156 | m_records[prim] = record; | |
| 283 | 24156 | m_records[prim].generation = generation; | |
| 284 | 24156 | const std::vector<SchemaId> tables = PrimTables(record.schema, record.apis); | |
| 285 | 142284 | for (uint32_t slot = 0; slot < tables.size(); ++slot) { | |
| 286 | 118128 | m_slotRows[record.slots + slot] = rows[slot]; | |
| 287 | 118128 | m_store.Schema(tables[slot]) | |
| 288 | 118128 | .Stage(m_store.PrimsColumn(tables[slot]), rows[slot], Word(prim)); | |
| 289 | } | ||
| 290 | 24156 | StageRecord(prim); | |
| 291 | 24156 | } | |
| 292 | |||
| 293 | 24554 | void Prims::StageRecord(uint32_t prim) { | |
| 294 | 24554 | const PrimRecord& record = m_records[prim]; | |
| 295 | 24554 | const uint32_t values[] = {uint32_t(record.schema), record.parent, record.world, record.flags, | |
| 296 | 24554 | record.slots}; | |
| 297 | 147324 | for (uint32_t column = kPrimSchema; column <= kPrimSlots; ++column) { | |
| 298 | 122770 | m_store.Prims().Stage(column, prim, Word(values[column])); | |
| 299 | } | ||
| 300 | 24554 | m_store.Prims().Stage(kPrimApis, prim, std::as_bytes(std::span(&record.apis, 1))); | |
| 301 | 24554 | } | |
| 302 | |||
| 303 | 485 | void Prims::CheckRemovable(uint32_t prim) const { | |
| 304 | 2860 | for (const SchemaId table : PrimTables(Record(prim).schema, Record(prim).apis)) { | |
| 305 | 2378 | if (m_store.Pinned(table)) { | |
| 306 | 3 | throw Error("{} data is mapped or attached, so prims with it can't be removed until " | |
| 307 | "the mapping closes or the attachment is released", | ||
| 308 | 6 | Info(table).name); | |
| 309 | } | ||
| 310 | 485 | } | |
| 311 | 482 | } | |
| 312 | |||
| 313 | 20 | std::vector<BlockTable> Prims::Retype(uint32_t prim, SchemaId schema, uint64_t apis) { | |
| 314 | 20 | const PrimRecord old = Record(prim); | |
| 315 | 20 | const std::vector<SchemaId> before = PrimTables(old.schema, old.apis); | |
| 316 | 20 | const std::vector<SchemaId> after = PrimTables(schema, apis); | |
| 317 | 239 | const auto kept = [](const std::vector<SchemaId>& tables, SchemaId table) { | |
| 318 | 219 | return std::ranges::find(tables, table) != tables.end(); | |
| 319 | }; | ||
| 320 | 94 | for (const SchemaId table : before) { | |
| 321 | 74 | if (!kept(after, table) && m_store.Pinned(table)) { | |
| 322 | ✗ | throw Error("{} data is mapped or attached, so prims with it can't change type until " | |
| 323 | "the mapping closes or the attachment is released", | ||
| 324 | ✗ | Info(table).name); | |
| 325 | } | ||
| 326 | } | ||
| 327 | 20 | const uint32_t first = AcquireSlots(uint32_t(after.size())); | |
| 328 | 20 | std::vector<BlockTable> added; | |
| 329 | 91 | for (uint32_t slot = 0; slot < after.size(); ++slot) { | |
| 330 | 71 | const SchemaId table = after[slot]; | |
| 331 | 71 | uint32_t row = kNoRow; | |
| 332 | 71 | if (kept(before, table)) { | |
| 333 | 50 | row = m_slotRows[old.slots + PrimSlot(old.schema, old.apis, table)]; | |
| 334 | } else { | ||
| 335 | 21 | row = m_store.Schema(table).Acquire(); | |
| 336 | 21 | m_store.Schema(table).Stage(m_store.PrimsColumn(table), row, Word(prim)); | |
| 337 | 21 | added.push_back({table, row, 1, 1}); | |
| 338 | } | ||
| 339 | 71 | m_slotRows[first + slot] = row; | |
| 340 | } | ||
| 341 | 94 | for (uint32_t slot = 0; slot < before.size(); ++slot) { | |
| 342 | 74 | const SchemaId table = before[slot]; | |
| 343 | 74 | if (kept(after, table)) { | |
| 344 | 74 | continue; | |
| 345 | } | ||
| 346 | 24 | if (!IsMultipleApi(table)) { | |
| 347 | 24 | ReleaseRow(table, m_slotRows[old.slots + slot]); | |
| 348 | 24 | continue; | |
| 349 | } | ||
| 350 | ✗ | const auto found = m_instances.find(InstanceKey(prim, table)); | |
| 351 | ✗ | for (const ApiInstance& entry : found->second) { | |
| 352 | ✗ | ReleaseRow(table, entry.row); | |
| 353 | } | ||
| 354 | ✗ | m_instances.erase(found); | |
| 355 | } | ||
| 356 | 20 | if (!before.empty()) { | |
| 357 | 20 | m_freeSlots[uint32_t(before.size())].push_back(old.slots); | |
| 358 | } | ||
| 359 | 20 | PrimRecord& record = m_records[prim]; | |
| 360 | 20 | record.schema = schema; | |
| 361 | 20 | record.apis = apis; | |
| 362 | 20 | record.slots = after.empty() ? kNoRow : first; | |
| 363 | 20 | if (!after.empty()) { | |
| 364 | 20 | m_store.Slots().Stage(0, first, | |
| 365 | 20 | std::as_bytes(std::span(m_slotRows.data() + first, after.size()))); | |
| 366 | } | ||
| 367 | 20 | StageRecord(prim); | |
| 368 | 20 | m_store.StampWorld(record.world); | |
| 369 | 20 | return added; | |
| 370 | 20 | } | |
| 371 | |||
| 372 | 8 | void Prims::Reparent(uint32_t prim, uint32_t parent) { | |
| 373 | 8 | m_records[prim].parent = parent; | |
| 374 | 8 | StageRecord(prim); | |
| 375 | 8 | m_store.StampWorld(m_records[prim].world); | |
| 376 | 8 | } | |
| 377 | |||
| 378 | 13 | void Prims::Renew(uint32_t prim) { | |
| 379 | 13 | ++m_records[prim].generation; | |
| 380 | 13 | } | |
| 381 | |||
| 382 | 241 | void Prims::Remove(uint32_t prim) { | |
| 383 | 241 | CheckRemovable(prim); | |
| 384 | 241 | const PrimRecord record = Record(prim); | |
| 385 | 241 | const std::vector<SchemaId> tables = PrimTables(record.schema, record.apis); | |
| 386 | 1424 | for (uint32_t slot = 0; slot < tables.size(); ++slot) { | |
| 387 | 1183 | if (!IsMultipleApi(tables[slot])) { | |
| 388 | 1177 | ReleaseRow(tables[slot], m_slotRows[record.slots + slot]); | |
| 389 | 1177 | continue; | |
| 390 | } | ||
| 391 | 6 | const auto found = m_instances.find(InstanceKey(prim, tables[slot])); | |
| 392 | 15 | for (const ApiInstance& entry : found->second) { | |
| 393 | 9 | ReleaseRow(tables[slot], entry.row); | |
| 394 | } | ||
| 395 | 6 | m_instances.erase(found); | |
| 396 | } | ||
| 397 | 241 | if (!tables.empty()) { | |
| 398 | 241 | m_freeSlots[uint32_t(tables.size())].push_back(record.slots); | |
| 399 | } | ||
| 400 | 241 | PrimRecord& removed = m_records[prim]; | |
| 401 | 241 | removed = {}; | |
| 402 | 241 | removed.generation = record.generation + 1; | |
| 403 | 241 | StageRecord(prim); | |
| 404 | 241 | m_freePrims.push_back(prim); | |
| 405 | 241 | m_store.StampWorld(record.world); | |
| 406 | 241 | --m_live; | |
| 407 | 241 | } | |
| 408 | |||
| 409 | 22 | BlockTable Prims::Apply(std::span<const uint32_t> prims, SchemaId api) { | |
| 410 | 22 | const BlockTable rows{api, m_store.Schema(api).Allocate(uint32_t(prims.size())), 1}; | |
| 411 | 44 | for (uint32_t i = 0; i < prims.size(); ++i) { | |
| 412 | 22 | const uint32_t row = rows.first + i; | |
| 413 | 22 | m_store.Schema(api).Stage(m_store.PrimsColumn(api), row, Word(prims[i])); | |
| 414 | 22 | Regroup(prims[i], m_records[prims[i]].apis | ApiBit(api), api, row); | |
| 415 | } | ||
| 416 | 22 | return rows; | |
| 417 | } | ||
| 418 | |||
| 419 | 4 | void Prims::Unapply(std::span<const uint32_t> prims, SchemaId api) { | |
| 420 | 8 | for (const uint32_t prim : prims) { | |
| 421 | 4 | ReleaseRow(api, Row(prim, api)); | |
| 422 | 4 | Regroup(prim, m_records[prim].apis & ~ApiBit(api), SchemaId::None, kNoRow); | |
| 423 | } | ||
| 424 | 4 | } | |
| 425 | |||
| 426 | 126 | void Prims::Regroup(uint32_t prim, uint64_t apis, SchemaId api, uint32_t row) { | |
| 427 | 126 | const std::vector<SchemaId> before = PrimTables(m_records[prim].schema, m_records[prim].apis); | |
| 428 | 126 | const std::vector<SchemaId> after = PrimTables(m_records[prim].schema, apis); | |
| 429 | 126 | const uint32_t first = AcquireSlots(uint32_t(after.size())); | |
| 430 | 126 | PrimRecord& record = m_records[prim]; | |
| 431 | 918 | for (uint32_t slot = 0; slot < after.size(); ++slot) { | |
| 432 | 792 | m_slotRows[first + slot] = | |
| 433 | 792 | after[slot] == api | |
| 434 | 1462 | ? row | |
| 435 | 670 | : m_slotRows[record.slots + PrimSlot(record.schema, record.apis, after[slot])]; | |
| 436 | } | ||
| 437 | 126 | if (!before.empty()) { | |
| 438 | 126 | m_freeSlots[uint32_t(before.size())].push_back(record.slots); | |
| 439 | } | ||
| 440 | 126 | record.slots = first; | |
| 441 | 126 | record.apis = apis; | |
| 442 | 126 | if (!after.empty()) { | |
| 443 | 126 | m_store.Slots().Stage(0, first, | |
| 444 | 126 | std::as_bytes(std::span(m_slotRows.data() + first, after.size()))); | |
| 445 | } | ||
| 446 | 126 | StageRecord(prim); | |
| 447 | 126 | m_store.StampWorld(record.world); | |
| 448 | 126 | } | |
| 449 | |||
| 450 | 3 | void Prims::MarkSession(uint32_t prim) { | |
| 451 | 3 | m_records[prim].flags |= kPrimSession; | |
| 452 | 3 | StageRecord(prim); | |
| 453 | 3 | } | |
| 454 | |||
| 455 | 5 | bool Prims::Session(uint32_t prim) const { | |
| 456 | 9 | for (uint32_t at = prim; at != kNoPrim; at = m_records[at].parent) { | |
| 457 | 7 | if ((m_records[at].flags & kPrimSession) != 0) { | |
| 458 | return true; | ||
| 459 | } | ||
| 460 | } | ||
| 461 | return false; | ||
| 462 | } | ||
| 463 | |||
| 464 | 1214 | void Prims::ReleaseRow(SchemaId table, uint32_t row) { | |
| 465 | 9306 | for (uint32_t index = 0; index < Info(table).columnCount; ++index) { | |
| 466 | 3439 | const ColumnRef column{table, &Columns(table)[index], index}; | |
| 467 | 3439 | if (IsArray(column.column->type) && m_store.Array(column, row).address != 0) { | |
| 468 | 40 | m_store.SetArray(column, row, {}); | |
| 469 | } | ||
| 470 | } | ||
| 471 | 1214 | m_store.DropOverrides(table, row); | |
| 472 | 1214 | m_store.Schema(table).Stage(m_store.PrimsColumn(table), row, Word(kNoPrim)); | |
| 473 | 1214 | m_store.Schema(table).Release(row); | |
| 474 | 1214 | } | |
| 475 | |||
| 476 | 182 | BlockTable Prims::ApplyInstance(std::span<const uint32_t> prims, SchemaId api, uint32_t instance) { | |
| 477 | 182 | const BlockTable rows{api, m_store.Schema(api).Allocate(uint32_t(prims.size())), 1}; | |
| 478 | 182 | const ColumnRef name = FindOwnColumn(api, "instance"); | |
| 479 | 364 | for (uint32_t i = 0; i < prims.size(); ++i) { | |
| 480 | 182 | const uint32_t prim = prims[i]; | |
| 481 | 182 | const uint32_t row = rows.first + i; | |
| 482 | 182 | m_store.Schema(api).Stage(m_store.PrimsColumn(api), row, Word(prim)); | |
| 483 | 182 | m_store.Stage(name, row, Word(instance)); | |
| 484 | 182 | std::vector<ApiInstance>& listed = m_instances[InstanceKey(prim, api)]; | |
| 485 | 182 | listed.insert(listed.begin(), {instance, row}); | |
| 486 | 182 | if (listed.size() == 1) { | |
| 487 | 100 | Regroup(prim, m_records[prim].apis | ApiBit(api), api, row); | |
| 488 | } else { | ||
| 489 | 82 | SetHead(prim, api, row); | |
| 490 | } | ||
| 491 | } | ||
| 492 | 182 | StageLinks(prims, api); | |
| 493 | 182 | return rows; | |
| 494 | } | ||
| 495 | |||
| 496 | ✗ | void Prims::UnapplyInstance(std::span<const uint32_t> prims, SchemaId api, uint32_t instance) { | |
| 497 | ✗ | for (const uint32_t prim : prims) { | |
| 498 | ✗ | const auto found = m_instances.find(InstanceKey(prim, api)); | |
| 499 | ✗ | if (found == m_instances.end()) { | |
| 500 | ✗ | continue; | |
| 501 | } | ||
| 502 | ✗ | std::vector<ApiInstance>& listed = found->second; | |
| 503 | ✗ | const auto entry = std::ranges::find(listed, instance, &ApiInstance::instance); | |
| 504 | ✗ | if (entry == listed.end()) { | |
| 505 | ✗ | continue; | |
| 506 | } | ||
| 507 | ✗ | const bool head = entry == listed.begin(); | |
| 508 | ✗ | ReleaseRow(api, entry->row); | |
| 509 | ✗ | listed.erase(entry); | |
| 510 | ✗ | if (listed.empty()) { | |
| 511 | ✗ | m_instances.erase(found); | |
| 512 | ✗ | Regroup(prim, m_records[prim].apis & ~ApiBit(api), SchemaId::None, kNoRow); | |
| 513 | ✗ | continue; | |
| 514 | } | ||
| 515 | ✗ | if (head) { | |
| 516 | ✗ | SetHead(prim, api, listed.front().row); | |
| 517 | } | ||
| 518 | ✗ | StageLinks(std::span(&prim, 1), api); | |
| 519 | } | ||
| 520 | ✗ | } | |
| 521 | |||
| 522 | 189 | void Prims::StageLinks(std::span<const uint32_t> prims, SchemaId api) { | |
| 523 | 189 | const ColumnRef next = FindOwnColumn(api, "next"); | |
| 524 | 378 | for (const uint32_t prim : prims) { | |
| 525 | 189 | const std::span<const ApiInstance> listed = Instances(prim, api); | |
| 526 | 463 | for (size_t i = 0; i < listed.size(); ++i) { | |
| 527 | 274 | const uint32_t following = i + 1 < listed.size() ? listed[i + 1].row : kNoRow; | |
| 528 | 274 | m_store.Stage(next, listed[i].row, Word(following)); | |
| 529 | } | ||
| 530 | } | ||
| 531 | 189 | } | |
| 532 | |||
| 533 | 82 | void Prims::SetHead(uint32_t prim, SchemaId api, uint32_t row) { | |
| 534 | 82 | const PrimRecord& record = m_records[prim]; | |
| 535 | 82 | const uint32_t slot = record.slots + ApiSlot(record.schema, record.apis, api); | |
| 536 | 82 | m_slotRows[slot] = row; | |
| 537 | 82 | m_store.Slots().Stage(0, slot, Word(row)); | |
| 538 | 82 | m_store.StampWorld(record.world); | |
| 539 | 82 | } | |
| 540 | |||
| 541 | } | ||
| 542 |