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