render/instances.cpp
| Line | Branch | Exec | Source |
|---|---|---|---|
| 1 | // SPDX-License-Identifier: BSD-3-Clause | ||
| 2 | /// @file instances.cpp | ||
| 3 | /// @brief Keeping instance rows dense as prims come and go, and uploading the rows that changed. | ||
| 4 | #include "ernie/render/instances.h" | ||
| 5 | |||
| 6 | #include "ernie/util/profiler.h" | ||
| 7 | |||
| 8 | #include <algorithm> | ||
| 9 | #include <limits> | ||
| 10 | |||
| 11 | namespace ernie { | ||
| 12 | namespace { | ||
| 13 | |||
| 14 | constexpr std::uint32_t kNoRow = std::numeric_limits<std::uint32_t>::max(); | ||
| 15 | |||
| 16 | } // namespace | ||
| 17 | |||
| 18 | 15 | InstanceTable::InstanceTable(DeviceContext& context, Arenas& arenas) | |
| 19 | 15 | : context_(context), uploader_(context, context.Main()) { | |
| 20 | 60 | const auto column = [&](std::string_view name, DType type, std::vector<std::int64_t> element) { | |
| 21 | 30 | return std::make_unique<DeviceColumn>(arenas, name, type, std::move(element), 0); | |
| 22 | 15 | }; | |
| 23 | 15 | nodes_ = column("instance nodes", DType::UInt32, {}); | |
| 24 | 15 | geometries_ = column("instance geometries", DType::UInt32, {}); | |
| 25 | 15 | shapes_ = column("instance shapes", DType::Float32, {3, 4}); | |
| 26 | 15 | colours_ = column("instance colours", DType::Float32, {3}); | |
| 27 | 15 | } | |
| 28 | |||
| 29 | 33 | std::optional<std::uint32_t> InstanceTable::Row(PrimId prim) const { | |
| 30 | 33 | if (prim.slot >= rows_.size() || rows_[prim.slot] == kNoRow || | |
| 31 | 9 | !(prims_[rows_[prim.slot]] == prim)) { | |
| 32 | 24 | return std::nullopt; | |
| 33 | } | ||
| 34 | 9 | return rows_[prim.slot]; | |
| 35 | } | ||
| 36 | |||
| 37 | 25 | void InstanceTable::Set(std::uint32_t row, const PrimGeometry& placed) { | |
| 38 | 25 | prims_[row] = placed.prim; | |
| 39 | 25 | hostNodes_[row] = placed.prim.slot; | |
| 40 | 25 | hostGeometries_[row] = placed.geometry; | |
| 41 | 25 | hostShapes_[row] = ToAffineRows(placed.shape); | |
| 42 | 25 | hostColours_[row] = placed.colour; | |
| 43 | 25 | rows_[placed.prim.slot] = row; | |
| 44 | 25 | dirty_.push_back(row); | |
| 45 | 25 | } | |
| 46 | |||
| 47 | 1 | void InstanceTable::Remove(PrimId prim) { | |
| 48 | 1 | const std::optional<std::uint32_t> row = Row(prim); | |
| 49 | 1 | if (!row) { | |
| 50 | ✗ | return; | |
| 51 | } | ||
| 52 | // The last row moves into the gap. | ||
| 53 | 1 | const auto last = static_cast<std::uint32_t>(prims_.size() - 1); | |
| 54 | 1 | if (*row != last) { | |
| 55 | 1 | prims_[*row] = prims_[last]; | |
| 56 | 1 | hostNodes_[*row] = hostNodes_[last]; | |
| 57 | 1 | hostGeometries_[*row] = hostGeometries_[last]; | |
| 58 | 1 | hostShapes_[*row] = hostShapes_[last]; | |
| 59 | 1 | hostColours_[*row] = hostColours_[last]; | |
| 60 | 1 | rows_[prims_[*row].slot] = *row; | |
| 61 | 1 | dirty_.push_back(*row); | |
| 62 | } | ||
| 63 | 1 | rows_[prim.slot] = kNoRow; | |
| 64 | 1 | prims_.pop_back(); | |
| 65 | 1 | hostNodes_.pop_back(); | |
| 66 | 1 | hostGeometries_.pop_back(); | |
| 67 | 1 | hostShapes_.pop_back(); | |
| 68 | 1 | hostColours_.pop_back(); | |
| 69 | } | ||
| 70 | |||
| 71 | 27 | void InstanceTable::Apply(const GeometryPool::Changes& changes) { | |
| 72 | 27 | ERNIE_ZONE("Update instances"); | |
| 73 | 28 | for (const PrimId prim : changes.removed) { | |
| 74 | 1 | Remove(prim); | |
| 75 | } | ||
| 76 | 52 | for (const PrimGeometry& placed : changes.placed) { | |
| 77 | 25 | if (placed.prim.slot >= rows_.size()) { | |
| 78 | 26 | rows_.resize(std::max<std::size_t>(placed.prim.slot + 1, 2 * rows_.size()), kNoRow); | |
| 79 | } | ||
| 80 | 25 | const std::optional<std::uint32_t> row = Row(placed.prim); | |
| 81 | 25 | if (row) { | |
| 82 | 1 | Set(*row, placed); | |
| 83 | 1 | continue; | |
| 84 | } | ||
| 85 | 24 | prims_.emplace_back(); | |
| 86 | 24 | hostNodes_.emplace_back(); | |
| 87 | 24 | hostGeometries_.emplace_back(); | |
| 88 | 24 | hostShapes_.emplace_back(); | |
| 89 | 24 | hostColours_.emplace_back(); | |
| 90 | 24 | Set(static_cast<std::uint32_t>(prims_.size() - 1), placed); | |
| 91 | } | ||
| 92 | 27 | } | |
| 93 | |||
| 94 | 27 | void InstanceTable::AddUploads(graph::Graph& graph) { | |
| 95 | 27 | const std::size_t rows = prims_.size(); | |
| 96 | 135 | for (DeviceColumn* column : {nodes_.get(), geometries_.get(), shapes_.get(), colours_.get()}) { | |
| 97 | 108 | column->Resize(rows, context_.Main()); | |
| 98 | } | ||
| 99 | 27 | StageRows(uploader_, *nodes_, hostNodes_, dirty_); | |
| 100 | 27 | StageRows(uploader_, *geometries_, hostGeometries_, dirty_); | |
| 101 | 27 | StageRows(uploader_, *shapes_, hostShapes_, dirty_); | |
| 102 | 27 | StageRows(uploader_, *colours_, hostColours_, dirty_); | |
| 103 | 27 | dirty_.clear(); | |
| 104 | 27 | std::array columns{graph.Import(*nodes_), graph.Import(*geometries_), graph.Import(*shapes_), | |
| 105 | 27 | graph.Import(*colours_)}; | |
| 106 | 27 | graph::AddUploadPass(graph, "Upload instances", uploader_, columns); | |
| 107 | 27 | } | |
| 108 | |||
| 109 | } // namespace ernie | ||
| 110 |