1 // Copyright 2021 Intel Corporation 2 // SPDX-License-Identifier: Apache-2.0 3 4 #include "IteratorContext.h" 5 #include "../common/Data.h" 6 #include "../common/export_util.h" 7 #include "../sampler/Sampler.h" 8 #include "../volume/Volume.h" 9 #include "../volume/vdb/VdbVolume.h" 10 #include "../volume/vdb/DenseVdbVolume.h" 11 #include "IteratorContext_ispc.h" 12 #include "rkcommon/math/range.h" 13 #include "../volume/UnstructuredVolume.h" 14 #include "../volume/amr/AMRVolume.h" 15 #include "../volume/particle/ParticleVolume.h" 16 17 namespace openvkl { 18 namespace cpu_device { 19 20 /////////////////////////////////////////////////////////////////////////// 21 // Helpers //////////////////////////////////////////////////////////////// 22 /////////////////////////////////////////////////////////////////////////// 23 24 // this function is somewhat expensive, but only occurs on commit. also, 25 // generating the map on on commit handles cases where the volume may change 26 // or be committed again after construction of the context. 27 template <int W> mapToMaxIteratorDepth(IteratorContext<W> & iteratorContext,const float intervalResolutionHint)28 inline int mapToMaxIteratorDepth(IteratorContext<W> &iteratorContext, 29 const float intervalResolutionHint) 30 { 31 // each pair in this vector maps a intervalResolutionHint value to a 32 // maxIteratorDepth value. the mapping depends on the volume type. 33 std::vector<std::pair<float, int>> hintToDepth; 34 35 const Volume<W> &volume = iteratorContext.getSampler().getVolume(); 36 37 if (dynamic_cast<const VdbVolume<W> *>(&volume)) { 38 // handles both DenseVdbVolume (structuredRegular) and VdbVolume 39 40 hintToDepth.emplace_back(0.f, 0); 41 hintToDepth.emplace_back(0.2f, 1); 42 hintToDepth.emplace_back(0.4f, 2); 43 44 // max iterator depth is VKL_VDB_NUM_LEVELS - 1 45 assert(VKL_VDB_NUM_LEVELS == 4); 46 hintToDepth.emplace_back(0.8f, 3); 47 48 } else if (dynamic_cast<const AMRVolume<W> *>(&volume) || 49 dynamic_cast<const ParticleVolume<W> *>(&volume) || 50 dynamic_cast<const UnstructuredVolume<W> *>(&volume)) { 51 // these volume types all use a BVH-based iterator 52 53 // we should have these volume types inherit from a common base 54 int bvhDepth = 0; 55 56 if (dynamic_cast<const AMRVolume<W> *>(&volume)) { 57 const auto *v = dynamic_cast<const AMRVolume<W> *>(&volume); 58 bvhDepth = v->getBvhDepth(); 59 } else if (dynamic_cast<const ParticleVolume<W> *>(&volume)) { 60 const auto *v = dynamic_cast<const ParticleVolume<W> *>(&volume); 61 bvhDepth = v->getBvhDepth(); 62 } else if (dynamic_cast<const UnstructuredVolume<W> *>(&volume)) { 63 const auto *v = dynamic_cast<const UnstructuredVolume<W> *>(&volume); 64 bvhDepth = v->getBvhDepth(); 65 } else { 66 throw std::runtime_error( 67 "could not map intervalResolutionHint value"); 68 } 69 70 int defaultDepth = bvhDepth > 6 ? 6 : bvhDepth / 2; 71 const float defaultRangeBegin = 0.45f; 72 const float defaultRangeEnd = 0.55f; 73 74 // mapping defined for intervalResolutionHint in [0, defaultRangeBegin] 75 if (defaultDepth == 0) { 76 hintToDepth.emplace_back(0.f, 0); 77 } else { 78 for (int i = 0; i <= defaultDepth; i++) { 79 float f = float(i) / float(defaultDepth) * defaultRangeBegin; 80 hintToDepth.emplace_back(f, i); 81 } 82 } 83 84 // mapping defined for intervalResolutionHint in [defaultRangeEnd, 1) 85 for (int i = defaultDepth + 1; i <= bvhDepth - 1; i++) { 86 float f = defaultRangeEnd + float(i - (defaultDepth + 1)) / 87 float(bvhDepth - (defaultDepth + 1)) * 88 (1.f - defaultRangeEnd); 89 90 hintToDepth.emplace_back(f, i); 91 } 92 93 // mapping defined for intervalResolutionHint == 1 94 hintToDepth.emplace_back(1.f, bvhDepth); 95 } 96 97 else { 98 // volume type does not support maxIteratorDepth parameter (result will 99 // go unused) 100 return 0; 101 } 102 103 // sanity check populated hintToDepth map 104 if (!hintToDepth.size() || hintToDepth.front().first != 0.f || 105 hintToDepth.back().first > 1.f) { 106 throw std::runtime_error("could not map intervalResolutionHint value"); 107 } 108 109 // find the mapped value 110 int maxIteratorDepth = -1; 111 112 for (const auto &m : hintToDepth) { 113 // selects value for the [i, i+1) interval 114 if (intervalResolutionHint >= m.first) { 115 maxIteratorDepth = m.second; 116 } else { 117 break; 118 } 119 } 120 121 if (maxIteratorDepth == -1) { 122 throw std::runtime_error("could not map intervalResolutionHint value"); 123 } 124 125 return maxIteratorDepth; 126 } 127 128 /////////////////////////////////////////////////////////////////////////// 129 // Interval iterator context ////////////////////////////////////////////// 130 /////////////////////////////////////////////////////////////////////////// 131 132 template <int W> ~IntervalIteratorContext()133 IntervalIteratorContext<W>::~IntervalIteratorContext() 134 { 135 if (this->ispcEquivalent) { 136 CALL_ISPC(IntervalIteratorContext_Destructor, this->ispcEquivalent); 137 this->ispcEquivalent = nullptr; 138 } 139 } 140 141 template <int W> commit()142 void IntervalIteratorContext<W>::commit() 143 { 144 // attribute index 145 this->attributeIndex = this->template getParam<int>("attributeIndex", 0); 146 147 throwOnIllegalAttributeIndex(&this->getSampler().getVolume(), 148 this->attributeIndex); 149 150 // value ranges 151 Ref<const DataT<box1f>> valueRangesData = 152 this->template getParamDataT<box1f>("valueRanges", nullptr); 153 154 std::vector<range1f> valueRanges; 155 156 if (valueRangesData) { 157 for (const auto &r : *valueRangesData) { 158 valueRanges.push_back(r); 159 } 160 } 161 162 // interval resolution hint 163 float intervalResolutionHint = 164 this->template getParam<float>("intervalResolutionHint", 0.5f); 165 166 // clamp to range [0, 1] 167 intervalResolutionHint = 168 std::max(std::min(1.f, intervalResolutionHint), 0.f); 169 170 // map interval resolution hint into internally used values 171 const int maxIteratorDepth = 172 mapToMaxIteratorDepth(*this, intervalResolutionHint); 173 const bool elementaryCellIteration = (intervalResolutionHint == 1.f); 174 175 if (this->ispcEquivalent) { 176 CALL_ISPC(IntervalIteratorContext_Destructor, this->ispcEquivalent); 177 } 178 179 this->ispcEquivalent = CALL_ISPC(IntervalIteratorContext_Constructor, 180 this->getSampler().getISPCEquivalent(), 181 this->attributeIndex, 182 valueRanges.size(), 183 (const ispc::box1f *)valueRanges.data(), 184 maxIteratorDepth, 185 elementaryCellIteration); 186 } 187 188 template struct IntervalIteratorContext<VKL_TARGET_WIDTH>; 189 190 /////////////////////////////////////////////////////////////////////////// 191 // Hit iterator context /////////////////////////////////////////////////// 192 /////////////////////////////////////////////////////////////////////////// 193 194 template <int W> ~HitIteratorContext()195 HitIteratorContext<W>::~HitIteratorContext() 196 { 197 if (this->ispcEquivalent) { 198 CALL_ISPC(HitIteratorContext_Destructor, this->ispcEquivalent); 199 this->ispcEquivalent = nullptr; 200 } 201 } 202 203 template <int W> commit()204 void HitIteratorContext<W>::commit() 205 { 206 this->attributeIndex = this->template getParam<int>("attributeIndex", 0); 207 208 throwOnIllegalAttributeIndex(&this->getSampler().getVolume(), 209 this->attributeIndex); 210 211 Ref<const DataT<float>> valuesData = 212 this->template getParamDataT<float>("values", nullptr); 213 214 std::vector<float> values; 215 216 if (valuesData) { 217 for (const auto &r : *valuesData) { 218 values.push_back(r); 219 } 220 } 221 222 // default interval iterator depth used for hit iteration 223 int maxIteratorDepth; 224 225 const Volume<W> &volume = this->getSampler().getVolume(); 226 227 if (dynamic_cast<const VdbVolume<W> *>(&volume) && 228 !dynamic_cast<const DenseVdbVolume<W> *>(&volume)) { 229 // VdbVolume, but not DenseVdbVolume. 230 // For sparse VDB volumes (constant cell data), we use elementary cell 231 // iteration to avoid hit artifacts near boundaries. 232 maxIteratorDepth = mapToMaxIteratorDepth(*this, 1.f); 233 } else { 234 maxIteratorDepth = mapToMaxIteratorDepth(*this, 0.5f); 235 } 236 237 if (this->ispcEquivalent) { 238 CALL_ISPC(HitIteratorContext_Destructor, this->ispcEquivalent); 239 } 240 241 this->ispcEquivalent = CALL_ISPC(HitIteratorContext_Constructor, 242 this->getSampler().getISPCEquivalent(), 243 this->attributeIndex, 244 values.size(), 245 (const float *)values.data(), 246 maxIteratorDepth); 247 } 248 249 template struct HitIteratorContext<VKL_TARGET_WIDTH>; 250 251 } // namespace cpu_device 252 } // namespace openvkl 253