1 // Copyright 2019-2021 Intel Corporation
2 // SPDX-License-Identifier: Apache-2.0
3 
4 #pragma once
5 
6 #include "ProceduralVolume.h"
7 #include "TestingAMRVolume.h"
8 #include "procedural_functions.h"
9 // openvkl
10 #include "openvkl/openvkl.h"
11 // rkcommon
12 #include "rkcommon/math/vec.h"
13 #include "rkcommon/tasking/parallel_for.h"
14 // std
15 #include <algorithm>
16 #include <vector>
17 
18 using namespace rkcommon;
19 
20 namespace openvkl {
21   namespace testing {
22 
23     template <vec3f volumeGradientFunction(const vec3f &, float) =
24                   gradientNotImplemented>
25     struct ProceduralShellsAMRVolume : public TestingAMRVolume,
26                                        public ProceduralVolume
27     {
28       ProceduralShellsAMRVolume(const vec3i &_dimensions,
29                                 const vec3f &_gridOrigin,
30                                 const vec3f &_gridSpacing);
31 
32       std::vector<unsigned char> generateVoxels() override;  // unused
33 
34      protected:
35       void generateVKLVolume(VKLDevice device) override;
36 
37       float computeProceduralValueImpl(const vec3f &objectCoordinates,
38                                        float time) const override;
39 
40       vec3f computeProceduralGradientImpl(const vec3f &objectCoordinates,
41                                           float time) const override;
42 
43       int refFactor      = 4;
44       int blockSize      = 16;
45       const int numCells = blockSize * blockSize * blockSize;
46     };
47 
48     // Inlined definitions ////////////////////////////////////////////////////
49 
50     template <vec3f volumeGradientFunction(const vec3f &, float)>
51     inline ProceduralShellsAMRVolume<volumeGradientFunction>::
ProceduralShellsAMRVolume(const vec3i & _dimensions,const vec3f & _gridOrigin,const vec3f & _gridSpacing)52         ProceduralShellsAMRVolume(const vec3i &_dimensions,
53                                   const vec3f &_gridOrigin,
54                                   const vec3f &_gridSpacing)
55         : TestingAMRVolume(_dimensions, _gridOrigin, _gridSpacing),
56           ProceduralVolume(false)
57     {
58       if (dimensions.x % blockSize != 0 || dimensions.y % blockSize != 0 ||
59           dimensions.z % blockSize != 0) {
60         std::stringstream ss;
61         ss << "ProceduralShellsAMRVolume requires multiple-of-" << blockSize
62            << " dimensions";
63         throw std::runtime_error(ss.str());
64       }
65     }
66 
67     template <vec3f volumeGradientFunction(const vec3f &, float)>
68     inline std::vector<unsigned char>
generateVoxels()69     ProceduralShellsAMRVolume<volumeGradientFunction>::generateVoxels()
70     {
71       {
72         return std::vector<unsigned char>();
73       }
74     }
75 
76     template <vec3f volumeGradientFunction(const vec3f &, float)>
77     inline void
generateVKLVolume(VKLDevice device)78     ProceduralShellsAMRVolume<volumeGradientFunction>::generateVKLVolume(
79         VKLDevice device)
80     {
81       int numLevels = 0;
82       int minExtent = reduce_min(dimensions);
83       if (minExtent < 128) {
84         numLevels = 2;
85       } else if (minExtent < 256) {
86         numLevels = 3;
87         blockSize = 8;
88       } else {
89         numLevels = 3;
90       }
91 
92       std::vector<box3i> blockBounds;
93       std::vector<int> refinementLevels;
94       std::vector<float> cellWidths;
95       std::vector<std::vector<float>> blockDataVectors;
96       std::vector<VKLData> blockData;
97 
98       // block bound upper bounds are inclusive, hence subtracting 1
99 
100       float cellWidth = gridSpacing.x * float(minExtent) / blockSize;
101       int refLevel    = 0;
102       std::vector<float> voxels(numCells, -0.5f);
103 
104       // outer shell - takes entire world space region
105       blockBounds.emplace_back(vec3i(0), vec3i(blockSize - 1));
106       refinementLevels.emplace_back(refLevel);
107       cellWidths.emplace_back(cellWidth);
108       blockDataVectors.emplace_back(voxels);
109 
110       // for each subsequent shell, create 8 16^3 blocks with progressively
111       // smaller cell widths
112       for (int level = 1; level < numLevels; level++) {
113         cellWidth /= refFactor;
114         cellWidths.emplace_back(cellWidth);
115 
116         voxels = std::vector<float>(numCells, float(level - 1));
117 
118         int lb = minExtent / 2 / std::pow(refFactor, numLevels - level - 1);
119         int ub = lb + blockSize - 1;
120 
121         blockBounds.emplace_back(vec3i(lb), vec3i(ub));
122         refinementLevels.emplace_back(level);
123         blockDataVectors.emplace_back(voxels);
124 
125         blockBounds.emplace_back(vec3i(lb - blockSize, lb, lb),
126                                  vec3i(ub - blockSize, ub, ub));
127         refinementLevels.emplace_back(level);
128         blockDataVectors.emplace_back(voxels);
129 
130         blockBounds.emplace_back(vec3i(lb, lb - blockSize, lb),
131                                  vec3i(ub, ub - blockSize, ub));
132         refinementLevels.emplace_back(level);
133         blockDataVectors.emplace_back(voxels);
134 
135         blockBounds.emplace_back(vec3i(lb - blockSize, lb - blockSize, lb),
136                                  vec3i(ub - blockSize, ub - blockSize, ub));
137         refinementLevels.emplace_back(level);
138         blockDataVectors.emplace_back(voxels);
139 
140         blockBounds.emplace_back(vec3i(lb, lb, lb - blockSize),
141                                  vec3i(ub, ub, ub - blockSize));
142         refinementLevels.emplace_back(level);
143         blockDataVectors.emplace_back(voxels);
144 
145         blockBounds.emplace_back(vec3i(lb - blockSize, lb, lb - blockSize),
146                                  vec3i(ub - blockSize, ub, ub - blockSize));
147         refinementLevels.emplace_back(level);
148         blockDataVectors.emplace_back(voxels);
149 
150         blockBounds.emplace_back(vec3i(lb, lb - blockSize, lb - blockSize),
151                                  vec3i(ub, ub - blockSize, ub - blockSize));
152         refinementLevels.emplace_back(level);
153         blockDataVectors.emplace_back(voxels);
154 
155         blockBounds.emplace_back(vec3i(lb - blockSize), vec3i(ub - blockSize));
156         refinementLevels.emplace_back(level);
157         blockDataVectors.emplace_back(voxels);
158       }
159 
160       // convert the data above to VKLData objects
161 
162       for (const std::vector<float> &bd : blockDataVectors)
163         blockData.push_back(
164             vklNewData(device, bd.size(), VKL_FLOAT, bd.data()));
165 
166       VKLData blockDataData =
167           vklNewData(device, blockData.size(), VKL_DATA, blockData.data());
168 
169       VKLData blockBoundsData =
170           vklNewData(device, blockBounds.size(), VKL_BOX3I, blockBounds.data());
171 
172       VKLData refinementLevelsData = vklNewData(
173           device, refinementLevels.size(), VKL_INT, refinementLevels.data());
174 
175       VKLData cellWidthsData =
176           vklNewData(device, cellWidths.size(), VKL_FLOAT, cellWidths.data());
177 
178       // create the AMR volume
179 
180       volume = vklNewVolume(device, "amr");
181 
182       vklSetData(volume, "block.data", blockDataData);
183       vklSetData(volume, "block.bounds", blockBoundsData);
184       vklSetData(volume, "block.level", refinementLevelsData);
185       vklSetData(volume, "cellWidth", cellWidthsData);
186 
187       vklRelease(blockDataData);
188       vklRelease(blockBoundsData);
189       vklRelease(refinementLevelsData);
190       vklRelease(cellWidthsData);
191 
192       for (auto &d : blockData)
193         vklRelease(d);
194 
195       vklCommit(volume);
196 
197       for (const auto &bdv : blockDataVectors)
198         computedValueRange.extend(
199             computeValueRange(VKL_FLOAT, bdv.data(), bdv.size()));
200     }
201 
202     template <vec3f gradientFunction(const vec3f &, float)>
203     inline float
computeProceduralValueImpl(const vec3f & objectCoordinates,float time)204     ProceduralShellsAMRVolume<gradientFunction>::computeProceduralValueImpl(
205         const vec3f &objectCoordinates, float time) const
206     {
207       return getShellValue(objectCoordinates, dimensions);
208     }
209 
210     template <vec3f gradientFunction(const vec3f &, float)>
211     inline vec3f
computeProceduralGradientImpl(const vec3f & objectCoordinates,float time)212     ProceduralShellsAMRVolume<gradientFunction>::computeProceduralGradientImpl(
213         const vec3f &objectCoordinates, float time) const
214     {
215       return gradientFunction(objectCoordinates, time);
216     }
217 
218   }  // namespace testing
219 }  // namespace openvkl
220