scene/tables/lights.cpp
| Line | Branch | Exec | Source |
|---|---|---|---|
| 1 | // SPDX-License-Identifier: BSD-3-Clause | ||
| 2 | #include "chrono_batch_render/scene/tables/lights.h" | ||
| 3 | |||
| 4 | #include <algorithm> | ||
| 5 | #include <cmath> | ||
| 6 | #include <cstring> | ||
| 7 | #include <iterator> | ||
| 8 | |||
| 9 | #include "chrono_batch_render/scene/kernels.h" | ||
| 10 | #include "chrono_batch_render/scene/records/render_tables.h" | ||
| 11 | #include "chrono_batch_render/scene/schema/columns.h" | ||
| 12 | #include "chrono_batch_render/scene/tables/kernels/instance_layout.h" | ||
| 13 | #include "chrono_batch_render/scene/tables/kernels/lights.h" | ||
| 14 | #include "chrono_batch_render/stage/publish/change_set.h" | ||
| 15 | #include "chrono_batch_render/util/profiler.h" | ||
| 16 | |||
| 17 | namespace chrono::batch_render { | ||
| 18 | |||
| 19 | namespace { | ||
| 20 | |||
| 21 | constexpr double kCmf[kSpectralRows][3] = { | ||
| 22 | {1.299000e-04, 3.917000e-06, 6.061000e-04}, {2.321000e-04, 6.965000e-06, 1.086000e-03}, | ||
| 23 | {4.149000e-04, 1.239000e-05, 1.946000e-03}, {7.416000e-04, 2.202000e-05, 3.486000e-03}, | ||
| 24 | {1.368000e-03, 3.900000e-05, 6.450001e-03}, {2.236000e-03, 6.400000e-05, 1.054999e-02}, | ||
| 25 | {4.243000e-03, 1.200000e-04, 2.005001e-02}, {7.650000e-03, 2.170000e-04, 3.621000e-02}, | ||
| 26 | {1.431000e-02, 3.960000e-04, 6.785001e-02}, {2.319000e-02, 6.400000e-04, 1.102000e-01}, | ||
| 27 | {4.351000e-02, 1.210000e-03, 2.074000e-01}, {7.763000e-02, 2.180000e-03, 3.713000e-01}, | ||
| 28 | {1.343800e-01, 4.000000e-03, 6.456000e-01}, {2.147700e-01, 7.300000e-03, 1.039050e+00}, | ||
| 29 | {2.839000e-01, 1.160000e-02, 1.385600e+00}, {3.285000e-01, 1.684000e-02, 1.622960e+00}, | ||
| 30 | {3.482800e-01, 2.300000e-02, 1.747060e+00}, {3.480600e-01, 2.980000e-02, 1.782600e+00}, | ||
| 31 | {3.362000e-01, 3.800000e-02, 1.772110e+00}, {3.187000e-01, 4.800000e-02, 1.744100e+00}, | ||
| 32 | {2.908000e-01, 6.000000e-02, 1.669200e+00}, {2.511000e-01, 7.390000e-02, 1.528100e+00}, | ||
| 33 | {1.953600e-01, 9.098000e-02, 1.287640e+00}, {1.421000e-01, 1.126000e-01, 1.041900e+00}, | ||
| 34 | {9.564000e-02, 1.390200e-01, 8.129501e-01}, {5.795001e-02, 1.693000e-01, 6.162000e-01}, | ||
| 35 | {3.201000e-02, 2.080200e-01, 4.651800e-01}, {1.470000e-02, 2.586000e-01, 3.533000e-01}, | ||
| 36 | {4.900000e-03, 3.230000e-01, 2.720000e-01}, {2.400000e-03, 4.073000e-01, 2.123000e-01}, | ||
| 37 | {9.300000e-03, 5.030000e-01, 1.582000e-01}, {2.910000e-02, 6.082000e-01, 1.117000e-01}, | ||
| 38 | {6.327000e-02, 7.100000e-01, 7.824999e-02}, {1.096000e-01, 7.932000e-01, 5.725001e-02}, | ||
| 39 | {1.655000e-01, 8.620000e-01, 4.216000e-02}, {2.257499e-01, 9.148501e-01, 2.984000e-02}, | ||
| 40 | {2.904000e-01, 9.540000e-01, 2.030000e-02}, {3.597000e-01, 9.803000e-01, 1.340000e-02}, | ||
| 41 | {4.334499e-01, 9.949501e-01, 8.749999e-03}, {5.120501e-01, 1.000000e+00, 5.749999e-03}, | ||
| 42 | {5.945000e-01, 9.950000e-01, 3.900000e-03}, {6.784000e-01, 9.786000e-01, 2.749999e-03}, | ||
| 43 | {7.621000e-01, 9.520000e-01, 2.100000e-03}, {8.425000e-01, 9.154000e-01, 1.800000e-03}, | ||
| 44 | {9.163000e-01, 8.700000e-01, 1.650001e-03}, {9.786000e-01, 8.163000e-01, 1.400000e-03}, | ||
| 45 | {1.026300e+00, 7.570000e-01, 1.100000e-03}, {1.056700e+00, 6.949000e-01, 1.000000e-03}, | ||
| 46 | {1.062200e+00, 6.310000e-01, 8.000000e-04}, {1.045600e+00, 5.668000e-01, 6.000000e-04}, | ||
| 47 | {1.002600e+00, 5.030000e-01, 3.400000e-04}, {9.384000e-01, 4.412000e-01, 2.400000e-04}, | ||
| 48 | {8.544499e-01, 3.810000e-01, 1.900000e-04}, {7.514000e-01, 3.210000e-01, 1.000000e-04}, | ||
| 49 | {6.424000e-01, 2.650000e-01, 4.999999e-05}, {5.419000e-01, 2.170000e-01, 3.000000e-05}, | ||
| 50 | {4.479000e-01, 1.750000e-01, 2.000000e-05}, {3.608000e-01, 1.382000e-01, 1.000000e-05}, | ||
| 51 | {2.835000e-01, 1.070000e-01, 0.000000e+00}, {2.187000e-01, 8.160000e-02, 0.000000e+00}, | ||
| 52 | {1.649000e-01, 6.100000e-02, 0.000000e+00}, {1.212000e-01, 4.458000e-02, 0.000000e+00}, | ||
| 53 | {8.740000e-02, 3.200000e-02, 0.000000e+00}, {6.360000e-02, 2.320000e-02, 0.000000e+00}, | ||
| 54 | {4.677000e-02, 1.700000e-02, 0.000000e+00}, {3.290000e-02, 1.192000e-02, 0.000000e+00}, | ||
| 55 | {2.270000e-02, 8.210000e-03, 0.000000e+00}, {1.584000e-02, 5.723000e-03, 0.000000e+00}, | ||
| 56 | {1.135916e-02, 4.102000e-03, 0.000000e+00}, {8.110916e-03, 2.929000e-03, 0.000000e+00}, | ||
| 57 | {5.790346e-03, 2.091000e-03, 0.000000e+00}, {4.109457e-03, 1.484000e-03, 0.000000e+00}, | ||
| 58 | {2.899327e-03, 1.047000e-03, 0.000000e+00}, {2.049190e-03, 7.400000e-04, 0.000000e+00}, | ||
| 59 | {1.439971e-03, 5.200000e-04, 0.000000e+00}, {9.999493e-04, 3.611000e-04, 0.000000e+00}, | ||
| 60 | {6.900786e-04, 2.492000e-04, 0.000000e+00}, {4.760213e-04, 1.719000e-04, 0.000000e+00}, | ||
| 61 | {3.323011e-04, 1.200000e-04, 0.000000e+00}, {2.348261e-04, 8.480000e-05, 0.000000e+00}, | ||
| 62 | {1.661505e-04, 6.000000e-05, 0.000000e+00}, {1.174130e-04, 4.240000e-05, 0.000000e+00}, | ||
| 63 | {8.307527e-05, 3.000000e-05, 0.000000e+00}, {5.870652e-05, 2.120000e-05, 0.000000e+00}, | ||
| 64 | {4.150994e-05, 1.499000e-05, 0.000000e+00}, {2.935326e-05, 1.060000e-05, 0.000000e+00}, | ||
| 65 | {2.067383e-05, 7.465700e-06, 0.000000e+00}, {1.455977e-05, 5.257800e-06, 0.000000e+00}, | ||
| 66 | {1.025398e-05, 3.702900e-06, 0.000000e+00}, {7.221456e-06, 2.607800e-06, 0.000000e+00}, | ||
| 67 | {5.085868e-06, 1.836600e-06, 0.000000e+00}, {3.581652e-06, 1.293400e-06, 0.000000e+00}, | ||
| 68 | {2.522525e-06, 9.109300e-07, 0.000000e+00}, {1.776509e-06, 6.415300e-07, 0.000000e+00}, | ||
| 69 | {1.251141e-06, 4.518100e-07, 0.000000e+00}, | ||
| 70 | }; | ||
| 71 | |||
| 72 | constexpr uint32_t kDaylightRows = 54; | ||
| 73 | constexpr double kDaylight[kDaylightRows][3] = { | ||
| 74 | {0.04, 0.02, 0.0}, {6.0, 4.5, 2.0}, {29.6, 22.4, 4.0}, {55.3, 42.0, 8.5}, | ||
| 75 | {57.3, 40.6, 7.8}, {61.8, 41.6, 6.7}, {61.5, 38.0, 5.3}, {68.8, 42.4, 6.1}, | ||
| 76 | {63.4, 38.5, 3.0}, {65.8, 35.0, 1.2}, {94.8, 43.4, -1.1}, {104.8, 46.3, -0.5}, | ||
| 77 | {105.9, 43.9, -0.7}, {96.8, 37.1, -1.2}, {113.9, 36.7, -2.6}, {125.6, 35.9, -2.9}, | ||
| 78 | {125.5, 32.6, -2.8}, {121.3, 27.9, -2.6}, {121.3, 24.3, -2.6}, {113.5, 20.1, -1.8}, | ||
| 79 | {113.1, 16.2, -1.5}, {110.8, 13.2, -1.3}, {106.5, 8.6, -1.2}, {108.8, 6.1, -1.0}, | ||
| 80 | {105.3, 4.2, -0.5}, {104.4, 1.9, -0.3}, {100.0, 0.0, 0.0}, {96.0, -1.6, 0.2}, | ||
| 81 | {95.1, -3.5, 0.5}, {89.1, -3.5, 2.1}, {90.5, -5.8, 3.2}, {90.3, -7.2, 4.1}, | ||
| 82 | {88.4, -8.6, 4.7}, {84.0, -9.5, 5.1}, {85.1, -10.9, 6.7}, {81.9, -10.7, 7.3}, | ||
| 83 | {82.6, -12.0, 8.6}, {84.9, -14.0, 9.8}, {81.3, -13.6, 10.2}, {71.9, -12.0, 8.3}, | ||
| 84 | {74.3, -13.3, 9.6}, {76.4, -12.9, 8.5}, {63.3, -10.6, 7.0}, {71.7, -11.6, 7.6}, | ||
| 85 | {77.0, -12.2, 8.0}, {65.2, -10.2, 6.7}, {47.7, -7.8, 5.2}, {68.6, -11.2, 7.4}, | ||
| 86 | {65.0, -10.4, 6.8}, {66.0, -10.6, 7.0}, {61.0, -9.7, 6.4}, {53.3, -8.3, 5.5}, | ||
| 87 | {58.9, -9.3, 6.1}, {61.9, -9.8, 6.5}, | ||
| 88 | }; | ||
| 89 | |||
| 90 | struct Rgb { | ||
| 91 | double r = 0; | ||
| 92 | double g = 0; | ||
| 93 | double b = 0; | ||
| 94 | }; | ||
| 95 | |||
| 96 | 8584960 | double Planck(double nm, double kelvin) { | |
| 97 | 8584960 | return 1.0 / (std::pow(nm, 5.0) * std::expm1(1.438776877e7 / (nm * kelvin))); | |
| 98 | } | ||
| 99 | |||
| 100 | 8618495 | double Daylight(double nm, double kelvin) { | |
| 101 | 8618495 | const double t = std::clamp(kelvin, 4000.0, 25000.0); | |
| 102 | 17236990 | const double x = t <= 7000 | |
| 103 | 8618495 | ? -4.6070e9 / (t * t * t) + 2.9678e6 / (t * t) + 99.11 / t + 0.244063 | |
| 104 | 1240795 | : -2.0064e9 / (t * t * t) + 1.9018e6 / (t * t) + 247.48 / t + 0.237040; | |
| 105 | 8618495 | const double y = -3 * x * x + 2.870 * x - 0.275; | |
| 106 | 8618495 | const double m = 0.0241 + 0.2562 * x - 0.7341 * y; | |
| 107 | 8618495 | const double m1 = (-1.3515 - 1.7703 * x + 5.9114 * y) / m; | |
| 108 | 8618495 | const double m2 = (0.0300 - 31.4424 * x + 30.0717 * y) / m; | |
| 109 | 8618495 | const double at = std::clamp((nm - 300) / 10, 0.0, double(kDaylightRows - 1)); | |
| 110 | 8618495 | const uint32_t row = std::min(uint32_t(at), kDaylightRows - 2); | |
| 111 | 8618495 | const double f = at - row; | |
| 112 | 8618495 | double value = 0; | |
| 113 | 8618495 | const double weights[3] = {1, m1, m2}; | |
| 114 | 34473980 | for (uint32_t component = 0; component < 3; ++component) { | |
| 115 | 25855485 | value += weights[component] * | |
| 116 | 25855485 | std::lerp(kDaylight[row][component], kDaylight[row + 1][component], f); | |
| 117 | } | ||
| 118 | 8618495 | return value; | |
| 119 | } | ||
| 120 | |||
| 121 | 181089 | template <class Spectrum> Rgb SpectrumRgb(const Spectrum& spectrum) { | |
| 122 | 181089 | double x = 0; | |
| 123 | 181089 | double y = 0; | |
| 124 | 181089 | double z = 0; | |
| 125 | 17384544 | for (uint32_t row = 0; row < kSpectralRows; ++row) { | |
| 126 | 25788415 | const double value = spectrum(kSpectralFirstNm + row * kSpectralStepNm); | |
| 127 | 17203455 | x += value * kCmf[row][0]; | |
| 128 | 17203455 | y += value * kCmf[row][1]; | |
| 129 | 17203455 | z += value * kCmf[row][2]; | |
| 130 | } | ||
| 131 | 181089 | return {(3.2404542 * x - 1.5371385 * y - 0.4985314 * z) / y, | |
| 132 | 181089 | (-0.9692660 * x + 1.8760108 * y + 0.0415560 * z) / y, | |
| 133 | 181089 | (0.0556434 * x - 0.2040259 * y + 1.0572252 * z) / y}; | |
| 134 | } | ||
| 135 | |||
| 136 | 180736 | Float3 UnitLuminance(Rgb rgb, Rgb white) { | |
| 137 | 180736 | rgb = {std::max(rgb.r / white.r, 0.0), std::max(rgb.g / white.g, 0.0), | |
| 138 | 180736 | std::max(rgb.b / white.b, 0.0)}; | |
| 139 | 180736 | const double luminance = 0.2126729 * rgb.r + 0.7151522 * rgb.g + 0.0721750 * rgb.b; | |
| 140 | 180736 | return {float(rgb.r / luminance), float(rgb.g / luminance), float(rgb.b / luminance)}; | |
| 141 | } | ||
| 142 | |||
| 143 | 353 | SpectralTables BuildSpectralTables() { | |
| 144 | 353 | SpectralTables tables{}; | |
| 145 | 33888 | for (uint32_t row = 0; row < kSpectralRows; ++row) { | |
| 146 | 33535 | tables.cmf[row] = {float(kCmf[row][0]), float(kCmf[row][1]), float(kCmf[row][2])}; | |
| 147 | } | ||
| 148 | 33535 | const Rgb white = SpectrumRgb([](double nm) { return Daylight(nm, 6504); }); | |
| 149 | 353 | tables.white = {float(white.r), float(white.g), float(white.b)}; | |
| 150 | 90721 | for (uint32_t row = 0; row < kTemperatureRows; ++row) { | |
| 151 | 90368 | const double mired = std::max(double(row) / (kTemperatureRows - 1) * kMaxMired, 1e-3); | |
| 152 | 90368 | const double kelvin = 1e6 / mired; | |
| 153 | 90368 | tables.blackbody[row] = | |
| 154 | 8675328 | UnitLuminance(SpectrumRgb([&](double nm) { return Planck(nm, kelvin); }), white); | |
| 155 | 90368 | tables.daylight[row] = | |
| 156 | 8675328 | UnitLuminance(SpectrumRgb([&](double nm) { return Daylight(nm, kelvin); }), white); | |
| 157 | } | ||
| 158 | 353 | return tables; | |
| 159 | } | ||
| 160 | |||
| 161 | 5 | std::vector<ColumnRef> LightInputs() { | |
| 162 | 5 | std::vector<ColumnRef> inputs = {kRenderProductCamera}; | |
| 163 | 5 | const SchemaId schemas[] = {SchemaId::LightAPI, | |
| 164 | SchemaId::DistantLight, | ||
| 165 | SchemaId::SphereLight, | ||
| 166 | SchemaId::RectLight, | ||
| 167 | SchemaId::DiskLight, | ||
| 168 | SchemaId::CylinderLight, | ||
| 169 | SchemaId::DomeLight, | ||
| 170 | SchemaId::PortalLight, | ||
| 171 | SchemaId::ShapingAPI, | ||
| 172 | SchemaId::OmniShapingAPI, | ||
| 173 | SchemaId::ShadowAPI, | ||
| 174 | SchemaId::PhysicalLightIlluminantAPI, | ||
| 175 | SchemaId::PhotometricAreaLightAPI, | ||
| 176 | SchemaId::PhotometricDistantLightAPI, | ||
| 177 | SchemaId::CollectionAPI, | ||
| 178 | SchemaId::PointInstancer, | ||
| 179 | SchemaId::PrototypeAPI}; | ||
| 180 | 90 | for (const SchemaId schema : schemas) { | |
| 181 | 85 | const Span<const ColumnInfo> columns = Columns(schema); | |
| 182 | 400 | for (uint32_t index = 0; index < columns.size(); ++index) { | |
| 183 | 315 | inputs.push_back({schema, &columns[index], index}); | |
| 184 | } | ||
| 185 | } | ||
| 186 | 5 | return inputs; | |
| 187 | ✗ | } | |
| 188 | |||
| 189 | } | ||
| 190 | |||
| 191 | 2545 | std::span<const ColumnRef> LightTable::Inputs() const { | |
| 192 | 2545 | static const std::vector<ColumnRef> inputs = LightInputs(); | |
| 193 | 2545 | return inputs; | |
| 194 | } | ||
| 195 | |||
| 196 | 691 | void LightTable::Describe(RenderTables& tables) const { | |
| 197 | 691 | tables.lights.address = m_layout ? m_layout->Address() : 0; | |
| 198 | 691 | tables.minimalLights.address = m_minimal ? m_minimal->Address() : 0; | |
| 199 | 691 | tables.collections.address = m_collections.Address(); | |
| 200 | 691 | tables.lightShapes.address = m_shapes.Address(); | |
| 201 | 691 | } | |
| 202 | |||
| 203 | 928 | void LightTable::Hold(std::vector<Ref<const RefCounted>>& held) const { | |
| 204 | 9280 | HoldBuffers(held, {m_layout, m_minimal, m_records, m_cdf, m_maps, m_spectra, | |
| 205 | m_collections.Buffer(), m_shapes.Buffer()}); | ||
| 206 | 1856 | } | |
| 207 | |||
| 208 | 353 | void LightTable::Prepare() { | |
| 209 | 353 | Kernels(); | |
| 210 | 353 | Spectra(); | |
| 211 | 353 | } | |
| 212 | |||
| 213 | 429 | const Ref<device::Buffer>& LightTable::Spectra() { | |
| 214 | 429 | if (!m_spectra) { | |
| 215 | 353 | const std::vector<SpectralTables> tables = {BuildSpectralTables()}; | |
| 216 | 706 | m_spectra = m_structure.GetColumnStore().GetDevice().CreateBuffer( | |
| 217 | 353 | {.label = "spectral tables"}, tables); | |
| 218 | 353 | } | |
| 219 | 429 | return m_spectra; | |
| 220 | 706 | } | |
| 221 | |||
| 222 | 417 | bool LightTable::Restructured(const ChangeSet& changes) const { | |
| 223 | 417 | if (m_structure.GetPrims().WorldCount() != m_worlds || changes.Wrote(TableId::Instances)) { | |
| 224 | 16 | return true; | |
| 225 | } | ||
| 226 | 539 | for (const StructureEntry& entry : changes.removed) { | |
| 227 | 140 | if (m_lights.contains(entry.prim) || m_worldPrims.contains(entry.prim)) { | |
| 228 | 2 | return true; | |
| 229 | } | ||
| 230 | } | ||
| 231 | 399 | const Prims& prims = m_structure.GetPrims(); | |
| 232 | 635 | for (const StructureEntry& entry : changes.added) { | |
| 233 | 446 | if (prims.Alive(entry.prim) && prims.Generation(entry.prim) == entry.generation && | |
| 234 | 420 | (prims.HasApi(entry.prim, SchemaId::LightAPI) || | |
| 235 | 210 | IsA(prims.Record(entry.prim).schema, SchemaId::World))) { | |
| 236 | ✗ | return true; | |
| 237 | } | ||
| 238 | } | ||
| 239 | 399 | return std::ranges::any_of( | |
| 240 | 410 | changes.apis, [](const StructureEntry& entry) { return entry.api == SchemaId::LightAPI; }); | |
| 241 | } | ||
| 242 | |||
| 243 | 293 | void LightTable::Relayout() { | |
| 244 | 293 | CHRONO_BATCH_RENDER_ZONE("Lights/Relayout"); | |
| 245 | 293 | const Prims& prims = m_structure.GetPrims(); | |
| 246 | 293 | device::Device& device = m_structure.GetColumnStore().GetDevice(); | |
| 247 | 293 | m_worlds = prims.WorldCount(); | |
| 248 | 293 | m_lights.clear(); | |
| 249 | 293 | m_worldPrims.clear(); | |
| 250 | 293 | std::vector<std::vector<uint32_t>> lists(m_worlds + 1); | |
| 251 | 7512 | for (uint32_t prim = 0; prim < prims.Size(); ++prim) { | |
| 252 | 7219 | if (!prims.Alive(prim)) { | |
| 253 | 258 | continue; | |
| 254 | } | ||
| 255 | 6961 | const PrimRecord& record = prims.Record(prim); | |
| 256 | 6961 | if (IsA(record.schema, SchemaId::World)) { | |
| 257 | 52 | m_worldPrims.insert(prim); | |
| 258 | } | ||
| 259 | 6961 | if (HasApi(record.apis, SchemaId::LightAPI)) { | |
| 260 | 76 | m_lights.insert(prim); | |
| 261 | 76 | if (!m_instances.Members().contains(prim)) { | |
| 262 | 127 | lists[TlasIndex(record.world, m_worlds)].push_back(prim); | |
| 263 | } | ||
| 264 | } | ||
| 265 | } | ||
| 266 | 293 | std::vector<uint32_t> domes; | |
| 267 | 369 | for (const uint32_t prim : m_lights) { | |
| 268 | 76 | if (prims.Record(prim).schema == SchemaId::DomeLight) { | |
| 269 | 14 | domes.push_back(prim); | |
| 270 | } | ||
| 271 | } | ||
| 272 | 293 | m_domes = uint32_t(domes.size()); | |
| 273 | 293 | std::vector<uint32_t> items; | |
| 274 | 293 | std::vector<uint32_t> copies; | |
| 275 | 293 | std::vector<uint32_t> starts = {0}; | |
| 276 | 293 | m_counts.clear(); | |
| 277 | 293 | m_compositions = m_structure.CompositionVersion(); | |
| 278 | 293 | m_shownIn.clear(); | |
| 279 | 293 | const std::map<uint32_t, ComposedWorldDesc>& composed = m_structure.Compositions(); | |
| 280 | 638 | for (uint32_t tlas = 0; tlas <= m_worlds; ++tlas) { | |
| 281 | 345 | items.insert(items.end(), lists[tlas].begin(), lists[tlas].end()); | |
| 282 | 397 | const auto composition = tlas < m_worlds ? composed.find(tlas) : composed.end(); | |
| 283 | 345 | const bool stage = composition == composed.end() || composition->second.stageLights; | |
| 284 | 345 | if (composition != composed.end() && stage) { | |
| 285 | 2 | for (const uint32_t world : composition->second.worlds) { | |
| 286 | 1 | if (world < m_worlds && world != tlas) { | |
| 287 | 1 | items.insert(items.end(), lists[world].begin(), lists[world].end()); | |
| 288 | 1 | m_shownIn[world].insert(tlas); | |
| 289 | } | ||
| 290 | } | ||
| 291 | } | ||
| 292 | 345 | if (tlas < m_worlds && stage) { | |
| 293 | 52 | items.insert(items.end(), lists[m_worlds].begin(), lists[m_worlds].end()); | |
| 294 | } | ||
| 295 | 355 | for (const auto& [block, offset] : m_instances.LightCopies(tlas)) { | |
| 296 | 10 | items.push_back(kInstancerSlot | uint32_t(copies.size() / 2)); | |
| 297 | 10 | copies.push_back(block); | |
| 298 | 10 | copies.push_back(offset); | |
| 299 | } | ||
| 300 | 345 | starts.push_back(uint32_t(items.size())); | |
| 301 | 345 | m_counts.push_back(starts.back() - starts[starts.size() - 2]); | |
| 302 | } | ||
| 303 | |||
| 304 | 586 | m_records = device.CreateBuffer( | |
| 305 | 293 | {.bytes = std::max<uint64_t>(items.size(), 1) * sizeof(LightRecord), .label = "lights"}); | |
| 306 | 586 | m_cdf = device.CreateBuffer( | |
| 307 | 293 | {.bytes = std::max<uint64_t>(items.size(), 1) * sizeof(float), .label = "light cdf"}); | |
| 308 | 586 | std::vector<uint32_t> words(sizeof(LightLayout) / sizeof(uint32_t)); | |
| 309 | 2344 | const auto append = [&](std::span<const uint32_t> values) { | |
| 310 | 2051 | const uint64_t at = words.size() * sizeof(uint32_t); | |
| 311 | 2051 | words.insert(words.end(), values.begin(), values.end()); | |
| 312 | 2051 | return at; | |
| 313 | 293 | }; | |
| 314 | 293 | const uint64_t first = append(starts); | |
| 315 | 293 | const uint64_t listed = append(items); | |
| 316 | 293 | std::vector<uint32_t> slots(items.size(), kNoRow); | |
| 317 | 384 | for (size_t slot = 0; slot < items.size(); ++slot) { | |
| 318 | 91 | const auto dome = std::ranges::find(domes, items[slot]); | |
| 319 | 91 | if (dome != domes.end()) { | |
| 320 | 14 | slots[slot] = uint32_t(dome - domes.begin()); | |
| 321 | } | ||
| 322 | } | ||
| 323 | 293 | const uint64_t domePrims = append(domes); | |
| 324 | 293 | const uint64_t domeSlots = append(slots); | |
| 325 | 293 | std::vector<uint32_t> bases(m_worlds + 1, kNoRow); | |
| 326 | 293 | std::vector<Float3> shifts; | |
| 327 | 294 | for (const auto& [world, desc] : composed) { | |
| 328 | 1 | if (world >= m_worlds) { | |
| 329 | ✗ | continue; | |
| 330 | } | ||
| 331 | 1 | bases[world] = uint32_t(shifts.size()); | |
| 332 | 1 | shifts.resize(shifts.size() + m_worlds, Float3{0, 0, 0}); | |
| 333 | 2 | for (size_t at = 0; at < desc.worlds.size() && at < desc.offsets.size(); ++at) { | |
| 334 | 1 | if (desc.worlds[at] < m_worlds) { | |
| 335 | 1 | const Double3& offset = desc.offsets[at]; | |
| 336 | 1 | shifts[bases[world] + desc.worlds[at]] = {float(offset.x), float(offset.y), | |
| 337 | 1 | float(offset.z)}; | |
| 338 | } | ||
| 339 | } | ||
| 340 | } | ||
| 341 | 293 | const uint64_t copiesStart = append(copies); | |
| 342 | 293 | const uint64_t offsetBases = append(bases); | |
| 343 | 293 | const uint64_t offsets = | |
| 344 | 293 | append(std::span(reinterpret_cast<const uint32_t*>(shifts.data()), shifts.size() * 3)); | |
| 345 | 586 | m_maps = device.CreateBuffer( | |
| 346 | 879 | {.bytes = std::max<uint64_t>(m_domes, 1) * kDomeMapFloats * sizeof(float), | |
| 347 | 586 | .label = "dome maps"}); | |
| 348 | 293 | m_layout = | |
| 349 | 312 | device.CreateBuffer({.bytes = words.size() * sizeof(uint32_t), .label = "light layout"}); | |
| 350 | 293 | LightLayout layout{}; | |
| 351 | 293 | layout.records.address = m_records->Address(); | |
| 352 | 293 | layout.firstLights.address = m_layout->Address() + first; | |
| 353 | 293 | layout.prims.address = m_layout->Address() + listed; | |
| 354 | 293 | layout.cdf.address = m_cdf->Address(); | |
| 355 | 293 | layout.maps.address = m_maps->Address(); | |
| 356 | 293 | layout.domePrims.address = m_layout->Address() + domePrims; | |
| 357 | 293 | layout.domeSlots.address = m_layout->Address() + domeSlots; | |
| 358 | 293 | layout.offsetBases.address = m_layout->Address() + offsetBases; | |
| 359 | 293 | layout.offsets.address = m_layout->Address() + offsets; | |
| 360 | 293 | layout.copies.address = m_layout->Address() + copiesStart; | |
| 361 | 293 | layout.worldCount = m_worlds; | |
| 362 | 293 | layout.domeCount = m_domes; | |
| 363 | 293 | std::memcpy(words.data(), &layout, sizeof(layout)); | |
| 364 | 293 | device.GetQueue().Write(*m_layout, words.data()); | |
| 365 | 293 | const std::vector<MinimalLightRecord> headlights(m_worlds + 1, {.headlight = 1}); | |
| 366 | 586 | m_minimal = device.CreateBuffer({.label = "minimal lights"}, | |
| 367 | 293 | std::span<const MinimalLightRecord>(headlights)); | |
| 368 | 3109 | } | |
| 369 | |||
| 370 | 691 | void LightTable::Run(const ChangeSet& changes, TableVersions&) { | |
| 371 | 691 | CHRONO_BATCH_RENDER_ZONE("Lights/Run"); | |
| 372 | 691 | m_written = 0; | |
| 373 | 691 | const bool relaid = changes.resync || !m_layout || Restructured(changes) || | |
| 374 | 398 | m_structure.CompositionVersion() != m_compositions; | |
| 375 | 293 | if (relaid) { | |
| 376 | 293 | Relayout(); | |
| 377 | } | ||
| 378 | 691 | ColumnStore& store = m_structure.GetColumnStore(); | |
| 379 | 691 | WriteCollectionsAndShapes(); | |
| 380 | 691 | const bool viewed = store.LastWrite(kRenderProductCamera) > m_seen; | |
| 381 | 691 | m_seen = store.WriteSequence(); | |
| 382 | 691 | std::vector<uint32_t> dirty; | |
| 383 | 691 | const bool global = relaid || viewed || changes.Global(); | |
| 384 | 691 | std::set<uint32_t> shown; | |
| 385 | 1437 | for (const uint32_t world : changes.worlds) { | |
| 386 | 746 | if (const auto found = m_shownIn.find(world); found != m_shownIn.end()) { | |
| 387 | 2 | shown.insert(found->second.begin(), found->second.end()); | |
| 388 | } | ||
| 389 | } | ||
| 390 | 1463 | for (uint32_t tlas = 0; tlas <= m_worlds; ++tlas) { | |
| 391 | 772 | const bool touched = | |
| 392 | 1528 | global || shown.contains(tlas) || std::ranges::binary_search(changes.worlds, tlas); | |
| 393 | 756 | if (touched && m_counts[tlas] > 0) { | |
| 394 | 100 | dirty.push_back(tlas); | |
| 395 | } | ||
| 396 | } | ||
| 397 | 691 | if (dirty.empty()) { | |
| 398 | 615 | return; | |
| 399 | } | ||
| 400 | 76 | uint32_t widest = 0; | |
| 401 | 176 | for (const uint32_t tlas : dirty) { | |
| 402 | 100 | widest = std::max(widest, m_counts[tlas]); | |
| 403 | 100 | m_written += m_counts[tlas]; | |
| 404 | } | ||
| 405 | |||
| 406 | 76 | device::Device& device = store.GetDevice(); | |
| 407 | 76 | const Ref<device::Buffer> tlases = device.CreateBuffer({.label = "dirty light lists"}, dirty); | |
| 408 | 76 | LightParams params{}; | |
| 409 | 152 | params.scene.address = store.DeviceData(); | |
| 410 | 76 | params.layout.address = m_layout->Address(); | |
| 411 | 76 | params.instances = m_tables.instances; | |
| 412 | 76 | params.origins = m_tables.origins; | |
| 413 | 76 | params.transforms = m_tables.transforms; | |
| 414 | 76 | params.imageables = m_tables.imageables; | |
| 415 | 76 | params.textures = m_tables.textures; | |
| 416 | 76 | params.textureBindings = m_tables.textureBindings; | |
| 417 | 76 | params.tlases.address = tlases->Address(); | |
| 418 | 76 | params.spectra.address = Spectra()->Address(); | |
| 419 | 76 | params.minimal.address = m_minimal->Address(); | |
| 420 | 76 | params.tlasCount = uint32_t(dirty.size()); | |
| 421 | 76 | params.lightLink = m_structure.GetTokens().Intern("lightLink").id; | |
| 422 | 76 | params.shadowLink = m_structure.GetTokens().Intern("shadowLink").id; | |
| 423 | 76 | device::Queue& queue = device.GetQueue(); | |
| 424 | 76 | device::CommandEncoder encoder = queue.CreateEncoder(); | |
| 425 | 76 | if (m_domes > 0) { | |
| 426 | 20 | encoder.Dispatch(Kernels().Entry("WriteDomeMaps"), params, | |
| 427 | {.x = device::GroupCount(kDomeMapCells, kLightGroup), .y = m_domes}); | ||
| 428 | 20 | encoder.Barrier(); | |
| 429 | 20 | encoder.Dispatch(Kernels().Entry("WriteDomeRows"), params, | |
| 430 | 20 | {.x = device::GroupCount(kDomeMapHeight, kLightGroup), .y = m_domes}); | |
| 431 | 20 | encoder.Barrier(); | |
| 432 | 20 | encoder.Dispatch(Kernels().Entry("WriteDomeMarginals"), params, | |
| 433 | 20 | {.x = device::GroupCount(m_domes, kLightGroup)}); | |
| 434 | 20 | encoder.Barrier(); | |
| 435 | } | ||
| 436 | 76 | encoder.Dispatch(Kernels().Entry("WriteLights"), params, | |
| 437 | 76 | {.x = device::GroupCount(widest, kLightGroup), .y = uint32_t(dirty.size())}); | |
| 438 | 76 | encoder.Barrier(); | |
| 439 | 76 | encoder.Dispatch(Kernels().Entry("WriteLightCdf"), params, | |
| 440 | 76 | {.x = device::GroupCount(uint32_t(dirty.size()), kLightGroup)}); | |
| 441 | 76 | encoder.Dispatch(Kernels().Entry("WriteMinimalLights"), params, | |
| 442 | 76 | {.x = device::GroupCount(uint32_t(dirty.size()), kLightGroup)}); | |
| 443 | 76 | queue.Submit(encoder.Finish()); | |
| 444 | 919 | } | |
| 445 | |||
| 446 | 691 | void LightTable::WriteCollectionsAndShapes() { | |
| 447 | 691 | ColumnStore& store = m_structure.GetColumnStore(); | |
| 448 | 691 | device::Device& device = store.GetDevice(); | |
| 449 | 691 | LightParams params{}; | |
| 450 | 691 | params.collectionRows = store.Schema(SchemaId::CollectionAPI).Rows(); | |
| 451 | 691 | params.shapeRows = store.Schema(SchemaId::LightAPI).Rows(); | |
| 452 | 691 | m_collections.Reserve(device, store.Schema(SchemaId::CollectionAPI).Capacity()); | |
| 453 | 691 | m_shapes.Reserve(device, store.Schema(SchemaId::LightAPI).Capacity()); | |
| 454 | 691 | params.scene.address = store.DeviceData(); | |
| 455 | 691 | params.collections.address = m_collections.Address(); | |
| 456 | 691 | params.shapes.address = m_shapes.Address(); | |
| 457 | 691 | device::Queue& queue = device.GetQueue(); | |
| 458 | 691 | device::CommandEncoder encoder = queue.CreateEncoder(); | |
| 459 | 691 | if (params.collectionRows > 0) { | |
| 460 | 154 | encoder.Dispatch(Kernels().Entry("WriteCollections"), params, | |
| 461 | 77 | {.x = device::GroupCount(params.collectionRows, kLightGroup)}); | |
| 462 | } | ||
| 463 | 691 | if (params.shapeRows > 0) { | |
| 464 | 154 | encoder.Dispatch(Kernels().Entry("WriteLightShapes"), params, | |
| 465 | 77 | {.x = device::GroupCount(params.shapeRows, kLightGroup)}); | |
| 466 | } | ||
| 467 | 691 | queue.Submit(encoder.Finish()); | |
| 468 | 691 | } | |
| 469 | |||
| 470 | 795 | device::Program& LightTable::Kernels() { | |
| 471 | 795 | if (!m_program) { | |
| 472 | 1059 | m_program = m_structure.GetColumnStore().GetDevice().CreateProgram( | |
| 473 | {.modules = {"chrono_batch_render.scene.tables.kernels.lights"}, | ||
| 474 | .library = &kSceneKernels, | ||
| 475 | .entries = {"WriteDomeMaps", "WriteDomeRows", "WriteDomeMarginals", "WriteLights", | ||
| 476 | "WriteLightCdf", "WriteMinimalLights", "WriteCollections", | ||
| 477 | "WriteLightShapes"}, | ||
| 478 | 706 | .label = "lights"}); | |
| 479 | } | ||
| 480 | 795 | return *m_program; | |
| 481 | 706 | } | |
| 482 | |||
| 483 | } | ||
| 484 |