scene/tables/tangents.cpp
| Line | Branch | Exec | Source |
|---|---|---|---|
| 1 | // SPDX-License-Identifier: BSD-3-Clause | ||
| 2 | #include "chrono_batch_render/scene/tables/tangents.h" | ||
| 3 | |||
| 4 | #include <algorithm> | ||
| 5 | #include <array> | ||
| 6 | #include <bit> | ||
| 7 | #include <cmath> | ||
| 8 | #include <map> | ||
| 9 | #include <set> | ||
| 10 | #include <vector> | ||
| 11 | |||
| 12 | #include <mikktspace.h> | ||
| 13 | |||
| 14 | #include "chrono_batch_render/scene/kernels.h" | ||
| 15 | #include "chrono_batch_render/scene/records/render_tables.h" | ||
| 16 | #include "chrono_batch_render/scene/schema/columns.h" | ||
| 17 | #include "chrono_batch_render/scene/tables/kernels/tangents.h" | ||
| 18 | #include "chrono_batch_render/stage/publish/change_set.h" | ||
| 19 | #include "chrono_batch_render/stage/structure/packing.h" | ||
| 20 | #include "chrono_batch_render/util/profiler.h" | ||
| 21 | #include "chrono_batch_render/util/span.h" | ||
| 22 | |||
| 23 | namespace chrono::batch_render { | ||
| 24 | namespace { | ||
| 25 | |||
| 26 | 39 | struct Faces { | |
| 27 | const TangentMesh* mesh = nullptr; | ||
| 28 | std::vector<std::array<uint32_t, 4>> corners; | ||
| 29 | std::vector<uint32_t> sizes; | ||
| 30 | std::vector<Float4>* tangents = nullptr; | ||
| 31 | }; | ||
| 32 | |||
| 33 | 15766 | Faces& ContextFaces(const SMikkTSpaceContext* context) { | |
| 34 | 15766 | return *static_cast<Faces*>(context->m_pUserData); | |
| 35 | } | ||
| 36 | |||
| 37 | 7705 | uint32_t Corner(const SMikkTSpaceContext* context, int face, int vertex) { | |
| 38 | 7705 | return ContextFaces(context).corners[face][vertex]; | |
| 39 | } | ||
| 40 | |||
| 41 | 146 | int FaceCount(const SMikkTSpaceContext* context) { | |
| 42 | 146 | return int(ContextFaces(context).corners.size()); | |
| 43 | } | ||
| 44 | |||
| 45 | 210 | int FaceSize(const SMikkTSpaceContext* context, int face) { | |
| 46 | 210 | return int(ContextFaces(context).sizes[face]); | |
| 47 | } | ||
| 48 | |||
| 49 | 4619 | void Position(const SMikkTSpaceContext* context, float out[], int face, int vertex) { | |
| 50 | 4619 | const Faces& faces = ContextFaces(context); | |
| 51 | 4619 | const Float3& p = faces.mesh->points[faces.mesh->indices[Corner(context, face, vertex)]]; | |
| 52 | 4619 | out[0] = p.x; | |
| 53 | 4619 | out[1] = p.y; | |
| 54 | 4619 | out[2] = p.z; | |
| 55 | 4619 | } | |
| 56 | |||
| 57 | 1476 | void Normal(const SMikkTSpaceContext* context, float out[], int face, int vertex) { | |
| 58 | 1476 | const Float3& n = ContextFaces(context).mesh->normals[Corner(context, face, vertex)]; | |
| 59 | 1476 | out[0] = n.x; | |
| 60 | 1476 | out[1] = n.y; | |
| 61 | 1476 | out[2] = n.z; | |
| 62 | 1476 | } | |
| 63 | |||
| 64 | 1333 | void TexCoord(const SMikkTSpaceContext* context, float out[], int face, int vertex) { | |
| 65 | 1333 | const Float2& st = ContextFaces(context).mesh->st[Corner(context, face, vertex)]; | |
| 66 | 1333 | out[0] = st.x; | |
| 67 | 1333 | out[1] = st.y; | |
| 68 | 1333 | } | |
| 69 | |||
| 70 | 277 | void StoreTangent(const SMikkTSpaceContext* context, const float tangent[], float sign, int face, | |
| 71 | int vertex) { | ||
| 72 | 277 | (*ContextFaces(context).tangents)[Corner(context, face, vertex)] = {tangent[0], tangent[1], | |
| 73 | 277 | tangent[2], sign}; | |
| 74 | 277 | } | |
| 75 | |||
| 76 | 512 | Float3 Sub(Float3 a, Float3 b) { | |
| 77 | 512 | return {a.x - b.x, a.y - b.y, a.z - b.z}; | |
| 78 | } | ||
| 79 | |||
| 80 | 256 | Float3 Cross(Float3 a, Float3 b) { | |
| 81 | 256 | return {a.y * b.z - a.z * b.y, a.z * b.x - a.x * b.z, a.x * b.y - a.y * b.x}; | |
| 82 | } | ||
| 83 | |||
| 84 | } | ||
| 85 | |||
| 86 | ✗ | std::vector<Float3> SmoothCornerNormals(std::span<const Float3> points, | |
| 87 | std::span<const int32_t> indices, | ||
| 88 | std::span<const int32_t> counts) { | ||
| 89 | ✗ | std::vector<Float3> sums(points.size()); | |
| 90 | ✗ | uint32_t first = 0; | |
| 91 | ✗ | for (const int32_t count : counts) { | |
| 92 | ✗ | for (int32_t k = 1; k + 1 < count && first + k + 1 < indices.size(); ++k) { | |
| 93 | ✗ | const uint32_t a = uint32_t(indices[first]); | |
| 94 | ✗ | const uint32_t b = uint32_t(indices[first + k]); | |
| 95 | ✗ | const uint32_t c = uint32_t(indices[first + k + 1]); | |
| 96 | ✗ | if (a >= points.size() || b >= points.size() || c >= points.size()) { | |
| 97 | ✗ | continue; | |
| 98 | } | ||
| 99 | ✗ | const Float3 n = Cross(Sub(points[b], points[a]), Sub(points[c], points[a])); | |
| 100 | ✗ | for (const uint32_t point : {a, b, c}) { | |
| 101 | ✗ | sums[point] = {sums[point].x + n.x, sums[point].y + n.y, sums[point].z + n.z}; | |
| 102 | } | ||
| 103 | } | ||
| 104 | ✗ | first += uint32_t(std::max(count, 0)); | |
| 105 | } | ||
| 106 | ✗ | std::vector<Float3> corners(indices.size()); | |
| 107 | ✗ | for (uint32_t corner = 0; corner < indices.size(); ++corner) { | |
| 108 | ✗ | const uint32_t point = uint32_t(indices[corner]); | |
| 109 | ✗ | const Float3 n = point < sums.size() ? sums[point] : Float3{0, 0, 1}; | |
| 110 | ✗ | const float length = std::sqrt(n.x * n.x + n.y * n.y + n.z * n.z); | |
| 111 | ✗ | corners[corner] = | |
| 112 | ✗ | length > 0 ? Float3{n.x / length, n.y / length, n.z / length} : Float3{0, 0, 1}; | |
| 113 | } | ||
| 114 | ✗ | return corners; | |
| 115 | ✗ | } | |
| 116 | |||
| 117 | 34 | std::vector<Float3> FacetedCornerNormals(std::span<const Float3> points, | |
| 118 | std::span<const int32_t> indices, | ||
| 119 | std::span<const int32_t> counts) { | ||
| 120 | 34 | std::vector<Float3> corners(indices.size(), Float3{0, 0, 1}); | |
| 121 | 34 | uint32_t first = 0; | |
| 122 | 98 | for (const int32_t count : counts) { | |
| 123 | 64 | const uint32_t size = uint32_t(std::max(count, 0)); | |
| 124 | 64 | if (first + size > indices.size()) { | |
| 125 | break; | ||
| 126 | } | ||
| 127 | 64 | Float3 n{}; | |
| 128 | 64 | bool valid = size >= 3 && uint32_t(indices[first]) < points.size(); | |
| 129 | 320 | for (uint32_t k = 0; valid && k < size; ++k) { | |
| 130 | 256 | const uint32_t a = uint32_t(indices[first + k]); | |
| 131 | 256 | const uint32_t b = uint32_t(indices[first + (k + 1) % size]); | |
| 132 | 256 | if (a >= points.size() || b >= points.size()) { | |
| 133 | ✗ | valid = false; | |
| 134 | ✗ | break; | |
| 135 | } | ||
| 136 | 256 | const Float3 origin = points[uint32_t(indices[first])]; | |
| 137 | 256 | const Float3 c = Cross(Sub(points[a], origin), Sub(points[b], origin)); | |
| 138 | 256 | n = {n.x + c.x, n.y + c.y, n.z + c.z}; | |
| 139 | } | ||
| 140 | 64 | const float length = std::sqrt(n.x * n.x + n.y * n.y + n.z * n.z); | |
| 141 | 64 | if (valid && length > 0) { | |
| 142 | 128 | std::fill_n(corners.begin() + first, size, | |
| 143 | 64 | Float3{n.x / length, n.y / length, n.z / length}); | |
| 144 | } | ||
| 145 | 64 | first += size; | |
| 146 | } | ||
| 147 | 34 | return corners; | |
| 148 | } | ||
| 149 | |||
| 150 | 39 | std::vector<Float4> MikkTangents(const TangentMesh& mesh) { | |
| 151 | 39 | std::vector<Float4> tangents(mesh.indices.size(), Float4{1, 0, 0, 1}); | |
| 152 | 39 | Faces faces{.mesh = &mesh, .tangents = &tangents}; | |
| 153 | 39 | uint32_t first = 0; | |
| 154 | 108 | for (const int32_t count : mesh.counts) { | |
| 155 | 69 | if (count == 3 || count == 4) { | |
| 156 | 68 | faces.corners.push_back({first, first + 1, first + 2, first + 3}); | |
| 157 | 68 | faces.sizes.push_back(uint32_t(count)); | |
| 158 | } else { | ||
| 159 | 4 | for (int32_t k = 1; k + 1 < count; ++k) { | |
| 160 | 3 | faces.corners.push_back({first, first + k, first + k + 1, 0}); | |
| 161 | 3 | faces.sizes.push_back(3); | |
| 162 | } | ||
| 163 | } | ||
| 164 | 69 | first += uint32_t(std::max(count, 0)); | |
| 165 | } | ||
| 166 | 39 | if (first > mesh.indices.size() || mesh.normals.size() != mesh.indices.size() || | |
| 167 | 39 | mesh.st.size() != mesh.indices.size()) { | |
| 168 | 1 | return {}; | |
| 169 | } | ||
| 170 | 38 | SMikkTSpaceInterface callbacks{}; | |
| 171 | 38 | callbacks.m_getNumFaces = FaceCount; | |
| 172 | 38 | callbacks.m_getNumVerticesOfFace = FaceSize; | |
| 173 | 38 | callbacks.m_getPosition = Position; | |
| 174 | 38 | callbacks.m_getNormal = Normal; | |
| 175 | 38 | callbacks.m_getTexCoord = TexCoord; | |
| 176 | 38 | callbacks.m_setTSpaceBasic = StoreTangent; | |
| 177 | 38 | SMikkTSpaceContext context{&callbacks, &faces}; | |
| 178 | 38 | genTangSpaceDefault(&context); | |
| 179 | 38 | return tangents; | |
| 180 | 39 | } | |
| 181 | |||
| 182 | namespace { | ||
| 183 | |||
| 184 | constexpr ColumnRef kInputs[] = { | ||
| 185 | kPointBasedPoints, kMeshFaceVertexIndices, kMeshFaceVertexCounts, kGprimPrimvars, | ||
| 186 | kPointBasedNormals, kPointBasedNormalsInterpolation, kMeshSubdivisionScheme}; | ||
| 187 | |||
| 188 | } | ||
| 189 | |||
| 190 | 2437 | std::span<const ColumnRef> TangentTable::Inputs() const { | |
| 191 | 2437 | return kInputs; | |
| 192 | } | ||
| 193 | |||
| 194 | 559 | void TangentTable::Describe(RenderTables& tables) const { | |
| 195 | 559 | tables.tangents.address = m_records.Buffer() ? m_records.Buffer()->Address() : 0; | |
| 196 | 559 | } | |
| 197 | |||
| 198 | 940 | void TangentTable::Hold(std::vector<Ref<const RefCounted>>& held) const { | |
| 199 | 1880 | HoldBuffers(held, {m_records.Buffer()}); | |
| 200 | 1880 | } | |
| 201 | |||
| 202 | 85 | void TangentTable::CacheCells(Mesh& mesh, const Cells& cells) const { | |
| 203 | 85 | mesh.interpolation = cells.interpolation[mesh.pointsRow]; | |
| 204 | 85 | mesh.scheme = cells.scheme[mesh.meshRow]; | |
| 205 | 85 | } | |
| 206 | |||
| 207 | 97 | TangentTable::Key TangentTable::KeyFor(const Mesh& mesh) const { | |
| 208 | 97 | ColumnStore& store = m_structure.GetColumnStore(); | |
| 209 | 97 | Key key; | |
| 210 | 97 | key.points = store.Array(kPointBasedPoints, mesh.pointsRow).address; | |
| 211 | 97 | key.indices = store.Array(kMeshFaceVertexIndices, mesh.meshRow).address; | |
| 212 | 97 | key.counts = store.Array(kMeshFaceVertexCounts, mesh.meshRow).address; | |
| 213 | 97 | key.primvars = store.Array(kGprimPrimvars, mesh.gprimRow).address; | |
| 214 | 97 | key.normals = store.Array(kPointBasedNormals, mesh.pointsRow).address; | |
| 215 | 97 | key.interpolation = mesh.interpolation; | |
| 216 | 97 | key.scheme = mesh.scheme; | |
| 217 | 97 | return key; | |
| 218 | } | ||
| 219 | |||
| 220 | 77 | void TangentTable::Compute(const Mesh& mesh, const Key& key, Set& set) { | |
| 221 | 77 | CHRONO_BATCH_RENDER_ZONE("Tangents/Compute"); | |
| 222 | 77 | ++m_stats.computed; | |
| 223 | 77 | ColumnStore& store = m_structure.GetColumnStore(); | |
| 224 | 77 | store.Arrays(ColumnType::IntArray).Free(set.tangents); | |
| 225 | 77 | set.tangents = {}; | |
| 226 | 77 | set.words = 0; | |
| 227 | 77 | set.deform.reset(); | |
| 228 | 77 | const ArrayRef points = store.Array(kPointBasedPoints, mesh.pointsRow); | |
| 229 | 77 | const ArrayRef indices = store.Array(kMeshFaceVertexIndices, mesh.meshRow); | |
| 230 | 77 | const ArrayRef counts = store.Array(kMeshFaceVertexCounts, mesh.meshRow); | |
| 231 | 77 | const ArrayRef table = store.Array(kGprimPrimvars, mesh.gprimRow); | |
| 232 | 77 | if (points.count == 0 || indices.count == 0 || counts.count == 0 || table.count == 0) { | |
| 233 | return; | ||
| 234 | } | ||
| 235 | 34 | ArrayPool& ints = store.Arrays(ColumnType::IntArray); | |
| 236 | 34 | ArrayPool& float3s = store.Arrays(ColumnType::Float3Array); | |
| 237 | 34 | const std::vector<Primvar> primvars = | |
| 238 | 68 | UnpackPrimvars(m_structure.GetTokens(), Items<int32_t>(ints.Read(table))); | |
| 239 | 34 | const auto st = std::ranges::find_if(primvars, [](const Primvar& primvar) { | |
| 240 | 34 | return primvar.name == "st" && primvar.type == PrimvarType::Float2; | |
| 241 | }); | ||
| 242 | 34 | if (st == primvars.end()) { | |
| 243 | return; | ||
| 244 | } | ||
| 245 | 33 | TangentMesh input; | |
| 246 | 99 | input.points = Items<Float3>(float3s.Read(points)); | |
| 247 | 99 | input.indices = Items<int32_t>(ints.Read(indices)); | |
| 248 | 99 | input.counts = Items<int32_t>(ints.Read(counts)); | |
| 249 | 33 | const std::optional<std::vector<Float2>> corners = CornerValues<Float2>( | |
| 250 | 33 | st->Values<Float2>(), st->indices, st->interpolation, input.indices, input.counts); | |
| 251 | 33 | if (!corners) { | |
| 252 | return; | ||
| 253 | } | ||
| 254 | 33 | input.st = *corners; | |
| 255 | 33 | const ArrayRef normals = store.Array(kPointBasedNormals, mesh.pointsRow); | |
| 256 | 33 | const SubdivisionScheme scheme = SubdivisionScheme(key.scheme); | |
| 257 | 33 | const bool smooth = | |
| 258 | scheme == SubdivisionScheme::CatmullClark || scheme == SubdivisionScheme::Loop; | ||
| 259 | 33 | const auto primvarNormals = std::ranges::find_if(primvars, [](const Primvar& primvar) { | |
| 260 | 33 | return primvar.name == "normals" && primvar.type == PrimvarType::Float3 && | |
| 261 | ✗ | primvar.elementSize == 1; | |
| 262 | }); | ||
| 263 | 33 | std::optional<std::vector<Float3>> authored; | |
| 264 | 33 | if (scheme == SubdivisionScheme::None && primvarNormals != primvars.end()) { | |
| 265 | ✗ | authored = CornerValues<Float3>(primvarNormals->Values<Float3>(), primvarNormals->indices, | |
| 266 | ✗ | primvarNormals->interpolation, input.indices, input.counts); | |
| 267 | 33 | } else if (scheme == SubdivisionScheme::None && normals.count > 0) { | |
| 268 | ✗ | authored = | |
| 269 | ✗ | CornerValues<Float3>(Items<Float3>(float3s.Read(normals)), {}, | |
| 270 | ✗ | Interpolation(key.interpolation), input.indices, input.counts); | |
| 271 | } | ||
| 272 | 66 | input.normals = authored ? *authored | |
| 273 | 33 | : smooth ? SmoothCornerNormals(input.points, input.indices, input.counts) | |
| 274 | 33 | : FacetedCornerNormals(input.points, input.indices, input.counts); | |
| 275 | 33 | const std::vector<Float4> tangents = MikkTangents(input); | |
| 276 | 33 | if (tangents.empty()) { | |
| 277 | return; | ||
| 278 | } | ||
| 279 | 33 | device::Device& device = store.GetDevice(); | |
| 280 | 33 | ArrayPool& words = store.Arrays(ColumnType::IntArray); | |
| 281 | 33 | set.words = uint32_t(tangents.size() * 4); | |
| 282 | 33 | set.tangents = words.Allocate(set.words); | |
| 283 | 33 | words.Write(set.tangents, std::as_bytes(std::span(tangents))); | |
| 284 | |||
| 285 | 33 | const bool faceted = !smooth && !authored; | |
| 286 | 33 | std::vector<uint32_t> triangles; | |
| 287 | 33 | std::vector<uint32_t> cornerFaces(input.indices.size()); | |
| 288 | 33 | uint32_t first = 0; | |
| 289 | 81 | for (uint32_t face = 0; face < input.counts.size(); ++face) { | |
| 290 | 48 | const int32_t count = input.counts[face]; | |
| 291 | 144 | for (int32_t k = 1; k + 1 < count; ++k) { | |
| 292 | 96 | triangles.insert(triangles.end(), | |
| 293 | 96 | {first, first + uint32_t(k), first + uint32_t(k) + 1}); | |
| 294 | } | ||
| 295 | 48 | const uint32_t size = uint32_t(std::max(count, 0)); | |
| 296 | 48 | std::fill_n(cornerFaces.begin() + first, std::min<size_t>(size, cornerFaces.size() - first), | |
| 297 | face); | ||
| 298 | 48 | first += size; | |
| 299 | } | ||
| 300 | 33 | std::map<std::array<uint32_t, 6>, uint32_t> keys; | |
| 301 | 33 | std::vector<uint32_t> groups(input.indices.size(), kNoRow); | |
| 302 | 321 | for (const uint32_t corner : triangles) { | |
| 303 | 288 | const Float2 uv = input.st[corner]; | |
| 304 | 288 | const Float3 normal = authored ? input.normals[corner] : Float3{}; | |
| 305 | 288 | const std::array<uint32_t, 6> weld = {uint32_t(input.indices[corner]), | |
| 306 | 288 | std::bit_cast<uint32_t>(uv.x), | |
| 307 | 288 | std::bit_cast<uint32_t>(uv.y), | |
| 308 | 288 | faceted ? cornerFaces[corner] | |
| 309 | ✗ | : std::bit_cast<uint32_t>(normal.x), | |
| 310 | 288 | std::bit_cast<uint32_t>(normal.y), | |
| 311 | 576 | std::bit_cast<uint32_t>(normal.z)}; | |
| 312 | 288 | groups[corner] = keys.try_emplace(weld, uint32_t(keys.size())).first->second; | |
| 313 | } | ||
| 314 | 33 | std::vector<uint32_t> starts(keys.size() + 1, 0); | |
| 315 | 321 | for (const uint32_t corner : triangles) { | |
| 316 | 288 | ++starts[groups[corner] + 1]; | |
| 317 | } | ||
| 318 | 225 | for (uint32_t group = 0; group < keys.size(); ++group) { | |
| 319 | 192 | starts[group + 1] += starts[group]; | |
| 320 | } | ||
| 321 | 33 | std::vector<uint32_t> entries(triangles.size()); | |
| 322 | 33 | std::vector<uint32_t> filled(starts.begin(), starts.end() - 1); | |
| 323 | 321 | for (uint32_t entry = 0; entry < triangles.size(); ++entry) { | |
| 324 | 288 | entries[filled[groups[triangles[entry]]]++] = entry; | |
| 325 | } | ||
| 326 | 33 | if (keys.empty()) { | |
| 327 | ✗ | return; | |
| 328 | } | ||
| 329 | 33 | Deform deform; | |
| 330 | 33 | deform.st = device.CreateBuffer({.label = "tangent st"}, input.st); | |
| 331 | 33 | if (authored) { | |
| 332 | ✗ | deform.normals = device.CreateBuffer({.label = "tangent normals"}, input.normals); | |
| 333 | } | ||
| 334 | 33 | deform.faceted = faceted; | |
| 335 | 33 | deform.triangles = device.CreateBuffer({.label = "tangent triangles"}, triangles); | |
| 336 | 33 | deform.starts = device.CreateBuffer({.label = "tangent group starts"}, starts); | |
| 337 | 33 | deform.corners = device.CreateBuffer({.label = "tangent group corners"}, entries); | |
| 338 | 33 | deform.groups = device.CreateBuffer({.label = "tangent corner groups"}, groups); | |
| 339 | 33 | deform.results = | |
| 340 | 33 | device.CreateBuffer({.bytes = keys.size() * sizeof(Float4), .label = "tangent groups"}); | |
| 341 | 33 | deform.groupCount = uint32_t(keys.size()); | |
| 342 | 33 | deform.cornerCount = uint32_t(input.indices.size()); | |
| 343 | 33 | set.deform = std::move(deform); | |
| 344 | 275 | } | |
| 345 | |||
| 346 | 3 | void TangentTable::Recompute(const Mesh& mesh, const Key& key, Set& set, | |
| 347 | device::CommandEncoder& encoder) { | ||
| 348 | 3 | ++m_stats.deformed; | |
| 349 | 3 | const Deform& deform = *set.deform; | |
| 350 | 3 | TangentParams params{}; | |
| 351 | 3 | params.points.address = key.points; | |
| 352 | 3 | params.smoothNormals.address = m_geometry.Record(mesh.meshRow).normals.address; | |
| 353 | 3 | params.cornerNormals.address = deform.normals ? deform.normals->Address() : 0; | |
| 354 | 3 | params.cornerSt.address = deform.st->Address(); | |
| 355 | 3 | params.indices.address = key.indices; | |
| 356 | 3 | params.triangles.address = deform.triangles->Address(); | |
| 357 | 3 | params.groupStarts.address = deform.starts->Address(); | |
| 358 | 3 | params.groupCorners.address = deform.corners->Address(); | |
| 359 | 3 | params.cornerGroups.address = deform.groups->Address(); | |
| 360 | 3 | params.groupTangents.address = deform.results->Address(); | |
| 361 | 3 | params.tangents.address = set.tangents.address; | |
| 362 | 3 | params.groupCount = deform.groupCount; | |
| 363 | 3 | params.cornerCount = deform.cornerCount; | |
| 364 | 3 | params.faceted = deform.faceted ? 1 : 0; | |
| 365 | 3 | encoder.Dispatch(Kernels().Entry("GroupTangents"), params, | |
| 366 | 3 | {.x = device::GroupCount(params.groupCount, kTangentsGroup)}); | |
| 367 | 3 | encoder.Dispatch(Kernels().Entry("CornerTangents"), params, | |
| 368 | 3 | {.x = device::GroupCount(params.cornerCount, kTangentsGroup)}); | |
| 369 | 3 | } | |
| 370 | |||
| 371 | 87 | void TangentTable::Link(uint32_t prim, const Mesh& mesh) { | |
| 372 | 87 | TangentRecord record{}; | |
| 373 | 87 | if (mesh.key) { | |
| 374 | 87 | const Set& set = m_sets.at(*mesh.key); | |
| 375 | 87 | if (set.words > 0) { | |
| 376 | 41 | record.tangents = set.tangents; | |
| 377 | 41 | record.primvar = m_structure.GetTokens().Find("st").id; | |
| 378 | } | ||
| 379 | } | ||
| 380 | 87 | const Prims& prims = m_structure.GetPrims(); | |
| 381 | 87 | if (m_records.Set(mesh.meshRow, record) && prims.Alive(prim)) { | |
| 382 | 36 | m_relinked.push_back(prims.Record(prim).world); | |
| 383 | } | ||
| 384 | 87 | } | |
| 385 | |||
| 386 | 81 | void TangentTable::Place(uint32_t prim, const Cells& cells) { | |
| 387 | 81 | const Prims& prims = m_structure.GetPrims(); | |
| 388 | 81 | Mesh mesh; | |
| 389 | 81 | mesh.generation = prims.Generation(prim); | |
| 390 | 81 | mesh.pointsRow = prims.Row(prim, SchemaId::PointBased); | |
| 391 | 81 | mesh.meshRow = prims.Row(prim, SchemaId::Mesh); | |
| 392 | 81 | mesh.gprimRow = prims.Row(prim, SchemaId::Gprim); | |
| 393 | 81 | m_byPointsRow[mesh.pointsRow] = prim; | |
| 394 | 81 | m_byMeshRow[mesh.meshRow] = prim; | |
| 395 | 81 | m_byGprimRow[mesh.gprimRow] = prim; | |
| 396 | 81 | CacheCells(mesh, cells); | |
| 397 | 81 | const Key key = KeyFor(mesh); | |
| 398 | 81 | const auto [entry, fresh] = m_sets.try_emplace(key); | |
| 399 | 81 | if (fresh) { | |
| 400 | 75 | Compute(mesh, key, entry->second); | |
| 401 | } | ||
| 402 | 81 | ++entry->second.users; | |
| 403 | 81 | mesh.key = key; | |
| 404 | 81 | mesh.set = &entry->second; | |
| 405 | 81 | Link(prim, mesh); | |
| 406 | 81 | m_meshes[prim] = mesh; | |
| 407 | 81 | } | |
| 408 | |||
| 409 | 1118 | void TangentTable::Prune() { | |
| 410 | 1118 | if (!m_stale) { | |
| 411 | return; | ||
| 412 | } | ||
| 413 | 11 | m_stale = false; | |
| 414 | 11 | ArrayPool& words = m_structure.GetColumnStore().Arrays(ColumnType::IntArray); | |
| 415 | 11 | std::erase_if(m_sets, [&](const auto& entry) { | |
| 416 | 14 | if (entry.second.users > 0) { | |
| 417 | return false; | ||
| 418 | } | ||
| 419 | 11 | words.Free(entry.second.tangents); | |
| 420 | return true; | ||
| 421 | }); | ||
| 422 | } | ||
| 423 | |||
| 424 | 13 | void TangentTable::Release(Mesh& mesh) { | |
| 425 | 13 | if (mesh.key) { | |
| 426 | 13 | --m_sets.at(*mesh.key).users; | |
| 427 | 13 | mesh.key.reset(); | |
| 428 | 13 | mesh.set = nullptr; | |
| 429 | 13 | m_stale = true; | |
| 430 | } | ||
| 431 | 13 | } | |
| 432 | |||
| 433 | 13 | void TangentTable::Forget(uint32_t prim) { | |
| 434 | 13 | Mesh& mesh = m_meshes.at(prim); | |
| 435 | 13 | Release(mesh); | |
| 436 | 13 | m_byPointsRow.erase(mesh.pointsRow); | |
| 437 | 13 | m_byMeshRow.erase(mesh.meshRow); | |
| 438 | 13 | m_byGprimRow.erase(mesh.gprimRow); | |
| 439 | 13 | m_meshes.erase(prim); | |
| 440 | 13 | } | |
| 441 | |||
| 442 | 559 | void TangentTable::Run(const ChangeSet& changes, TableVersions& versions) { | |
| 443 | 559 | Process(changes); | |
| 444 | 559 | m_records.Reserve(m_structure.GetColumnStore().Schema(SchemaId::Mesh).Capacity()); | |
| 445 | 559 | if (m_records.Upload(m_structure.GetColumnStore().GetDevice())) { | |
| 446 | 281 | m_relinked = {kGlobalWorld}; | |
| 447 | } | ||
| 448 | 559 | if (!m_relinked.empty()) { | |
| 449 | 283 | versions.Record(TableId::Tangents, m_relinked); | |
| 450 | } | ||
| 451 | 559 | m_relinked.clear(); | |
| 452 | 559 | } | |
| 453 | |||
| 454 | 559 | void TangentTable::Process(const ChangeSet& changes) { | |
| 455 | 559 | CHRONO_BATCH_RENDER_ZONE("Tangents/Run"); | |
| 456 | 559 | m_stats.computed = 0; | |
| 457 | 559 | ColumnStore& store = m_structure.GetColumnStore(); | |
| 458 | 559 | const Prims& prims = m_structure.GetPrims(); | |
| 459 | 837 | const bool rekey = changes.resync || | |
| 460 | 559 | store.LastWrite(kPointBasedNormalsInterpolation) > m_seen || | |
| 461 | 277 | store.LastWrite(kMeshSubdivisionScheme) > m_seen; | |
| 462 | 559 | m_seen = store.WriteSequence(); | |
| 463 | 559 | m_stats.deformed = 0; | |
| 464 | 559 | bool read = false; | |
| 465 | 644 | const auto cells = [&]() -> const Cells& { | |
| 466 | 85 | ColumnTable& points = store.Schema(SchemaId::PointBased); | |
| 467 | 85 | ColumnTable& meshes = store.Schema(SchemaId::Mesh); | |
| 468 | 8 | const bool stale = rekey || changes.resync || !changes.added.empty() || | |
| 469 | 93 | m_cells.interpolation.size() < points.Rows() || | |
| 470 | 8 | m_cells.scheme.size() < meshes.Rows(); | |
| 471 | 85 | if (!read && stale) { | |
| 472 | 183 | m_cells.interpolation = Items<uint32_t>( | |
| 473 | 122 | points.Read(kPointBasedNormalsInterpolation.index, 0, points.Rows())); | |
| 474 | 61 | m_cells.scheme = | |
| 475 | 122 | Items<uint32_t>(meshes.Read(kMeshSubdivisionScheme.index, 0, meshes.Rows())); | |
| 476 | 61 | read = true; | |
| 477 | } | ||
| 478 | 85 | return m_cells; | |
| 479 | 559 | }; | |
| 480 | 7677 | const auto isMesh = [&](uint32_t prim) { | |
| 481 | 7118 | return prims.Alive(prim) && IsA(prims.Record(prim).schema, SchemaId::Mesh); | |
| 482 | 559 | }; | |
| 483 | |||
| 484 | 559 | std::set<uint32_t> placed; | |
| 485 | 559 | if (changes.resync) { | |
| 486 | 281 | while (!m_meshes.empty()) { | |
| 487 | ✗ | Forget(m_meshes.begin()->first); | |
| 488 | } | ||
| 489 | 2152 | for (uint32_t prim = 0; prim < prims.Size(); ++prim) { | |
| 490 | 1871 | if (isMesh(prim)) { | |
| 491 | 69 | placed.insert(prim); | |
| 492 | } | ||
| 493 | } | ||
| 494 | } else { | ||
| 495 | 420 | for (const StructureEntry& entry : changes.removed) { | |
| 496 | 142 | const auto found = m_meshes.find(entry.prim); | |
| 497 | 144 | if (found != m_meshes.end() && found->second.generation == entry.generation) { | |
| 498 | 2 | Forget(entry.prim); | |
| 499 | } | ||
| 500 | } | ||
| 501 | 5525 | for (const StructureEntry& entry : changes.added) { | |
| 502 | 5247 | if (isMesh(entry.prim) && prims.Generation(entry.prim) == entry.generation) { | |
| 503 | 1 | if (m_meshes.contains(entry.prim)) { | |
| 504 | ✗ | Forget(entry.prim); | |
| 505 | } | ||
| 506 | 1 | placed.insert(entry.prim); | |
| 507 | } | ||
| 508 | } | ||
| 509 | } | ||
| 510 | |||
| 511 | 559 | std::set<uint32_t> affected; | |
| 512 | 559 | std::set<uint32_t> reshaped; | |
| 513 | 559 | if (rekey) { | |
| 514 | 293 | for (auto& [prim, mesh] : m_meshes) { | |
| 515 | 4 | CacheCells(mesh, cells()); | |
| 516 | 4 | affected.insert(prim); | |
| 517 | 4 | reshaped.insert(prim); | |
| 518 | } | ||
| 519 | } | ||
| 520 | 3354 | const auto collect = [&](ColumnRef column, const auto& owners, bool points) { | |
| 521 | 3272 | for (const uint32_t row : changes.ArrayRows(column)) { | |
| 522 | 477 | if (const auto owner = owners.find(row); owner != owners.end()) { | |
| 523 | 20 | if (points) { | |
| 524 | 15 | const Mesh& mesh = m_meshes.at(owner->second); | |
| 525 | 25 | if (mesh.set && mesh.set->words == 0 && | |
| 526 | 10 | store.Array(kPointBasedPoints, mesh.pointsRow).address == | |
| 527 | 10 | mesh.key->points) { | |
| 528 | 6 | continue; | |
| 529 | } | ||
| 530 | } | ||
| 531 | 14 | affected.insert(owner->second); | |
| 532 | 14 | if (!points) { | |
| 533 | 5 | reshaped.insert(owner->second); | |
| 534 | } | ||
| 535 | } | ||
| 536 | } | ||
| 537 | 3354 | }; | |
| 538 | 559 | collect(kPointBasedPoints, m_byPointsRow, true); | |
| 539 | 559 | collect(kPointBasedNormals, m_byPointsRow, false); | |
| 540 | 559 | collect(kMeshFaceVertexIndices, m_byMeshRow, false); | |
| 541 | 559 | collect(kMeshFaceVertexCounts, m_byMeshRow, false); | |
| 542 | 559 | collect(kGprimPrimvars, m_byGprimRow, false); | |
| 543 | |||
| 544 | 559 | std::set<Key> stale; | |
| 545 | 559 | std::set<Key> exact; | |
| 546 | 559 | std::vector<uint32_t> rekeyed; | |
| 547 | 575 | for (const uint32_t prim : affected) { | |
| 548 | 16 | if (placed.contains(prim) || !m_meshes.contains(prim)) { | |
| 549 | 5 | continue; | |
| 550 | } | ||
| 551 | 16 | Mesh& mesh = m_meshes.at(prim); | |
| 552 | 16 | const Key key = KeyFor(mesh); | |
| 553 | 16 | if (mesh.key && *mesh.key == key) { | |
| 554 | 5 | stale.insert(key); | |
| 555 | 5 | if (reshaped.contains(prim)) { | |
| 556 | 2 | exact.insert(key); | |
| 557 | } | ||
| 558 | 5 | continue; | |
| 559 | } | ||
| 560 | 11 | Forget(prim); | |
| 561 | 11 | rekeyed.push_back(prim); | |
| 562 | } | ||
| 563 | 559 | Prune(); | |
| 564 | 629 | for (const uint32_t prim : placed) { | |
| 565 | 70 | Place(prim, cells()); | |
| 566 | } | ||
| 567 | 570 | for (const uint32_t prim : rekeyed) { | |
| 568 | 11 | Place(prim, cells()); | |
| 569 | } | ||
| 570 | 559 | static const profiler::ZoneId zone = profiler::ZoneId::Named("Tangents/Recompute"); | |
| 571 | 559 | device::Device& device = store.GetDevice(); | |
| 572 | 559 | std::optional<device::CommandEncoder> encoder; | |
| 573 | 559 | std::optional<device::ZoneScope> scope; | |
| 574 | 559 | std::map<Key, std::vector<uint32_t>> users; | |
| 575 | 559 | if (!stale.empty()) { | |
| 576 | 19 | for (const auto& [prim, mesh] : m_meshes) { | |
| 577 | 7 | if (mesh.key && stale.contains(*mesh.key)) { | |
| 578 | 6 | users[*mesh.key].push_back(prim); | |
| 579 | } | ||
| 580 | } | ||
| 581 | } | ||
| 582 | 564 | for (const auto& [key, owners] : users) { | |
| 583 | 5 | Set& set = m_sets.at(key); | |
| 584 | 5 | bool computed = false; | |
| 585 | 11 | for (const uint32_t prim : owners) { | |
| 586 | 6 | const Mesh& mesh = m_meshes.at(prim); | |
| 587 | 6 | if (!computed) { | |
| 588 | 5 | const bool onDevice = | |
| 589 | 5 | set.deform && !exact.contains(key) && | |
| 590 | 6 | (set.deform->normals || m_geometry.Record(mesh.meshRow).normals.address != 0); | |
| 591 | 5 | if (onDevice) { | |
| 592 | 3 | if (!encoder) { | |
| 593 | 3 | encoder = device.GetQueue().CreateEncoder(); | |
| 594 | 3 | scope.emplace(*encoder, zone); | |
| 595 | } | ||
| 596 | 3 | Recompute(mesh, key, set, *encoder); | |
| 597 | } else { | ||
| 598 | 2 | Compute(mesh, key, set); | |
| 599 | } | ||
| 600 | computed = true; | ||
| 601 | } | ||
| 602 | 6 | Link(prim, mesh); | |
| 603 | } | ||
| 604 | } | ||
| 605 | 559 | Prune(); | |
| 606 | 559 | m_stats.meshes = uint32_t(m_meshes.size()); | |
| 607 | 559 | m_stats.sets = uint32_t(m_sets.size()); | |
| 608 | 559 | if (encoder) { | |
| 609 | 3 | scope.reset(); | |
| 610 | 3 | store.Flush(); | |
| 611 | 3 | device.GetQueue().Submit(encoder->Finish()); | |
| 612 | } | ||
| 613 | 1118 | } | |
| 614 | |||
| 615 | 353 | void TangentTable::Prepare() { | |
| 616 | 353 | Kernels(); | |
| 617 | 353 | } | |
| 618 | |||
| 619 | 359 | device::Program& TangentTable::Kernels() { | |
| 620 | 359 | if (!m_program) { | |
| 621 | 1062 | m_program = m_structure.GetColumnStore().GetDevice().CreateProgram( | |
| 622 | {.modules = {"chrono_batch_render.scene.tables.kernels.tangents"}, | ||
| 623 | .library = &kSceneKernels, | ||
| 624 | .entries = {"GroupTangents", "CornerTangents"}, | ||
| 625 | 708 | .label = "tangents"}); | |
| 626 | } | ||
| 627 | 359 | return *m_program; | |
| 628 | 708 | } | |
| 629 | |||
| 630 | } | ||
| 631 |