1 // Copyright 2019-2021 Intel Corporation
2 // SPDX-License-Identifier: Apache-2.0
3 
4 #pragma once
5 
6 // openvkl
7 #include "openvkl/openvkl.h"
8 // rkcommon
9 #include "rkcommon/math/range.h"
10 #include "rkcommon/math/vec.h"
11 // half
12 #include "../external/half.hpp"
13 
14 #include <vector>
15 
16 using namespace rkcommon::math;
17 
18 namespace openvkl {
19   namespace testing {
20 
21     struct TemporalConfig
22     {
23       enum Type
24       {
25         Constant,
26         Structured,
27         Unstructured,
28       };
29 
30       Type type{Constant};
31       std::vector<float> sampleTime;
32 
33       // Threshold for temporal compression on temporally unstructured volumes.
34       // Lossy if > 0, but will remove duplicate samples at 0.
35       bool useTemporalCompression = false;
36       float temporalCompressionThreshold = 0;
37 
38       TemporalConfig() = default;
39 
TemporalConfigTemporalConfig40       TemporalConfig(Type type, size_t numSamples)
41           : type(type), sampleTime(equidistantTime(numSamples))
42       {
43         assert(type == Constant || numSamples > 0);
44       }
45 
TemporalConfigTemporalConfig46       explicit TemporalConfig(const std::vector<float> &sampleTime)
47           : type(Unstructured), sampleTime(sampleTime)
48       {
49         assert(!sampleTime.empty());
50       }
51 
isCompatibleTemporalConfig52       bool isCompatible(const TemporalConfig &other) const
53       {
54         return (type == other.type) &&
55                (sampleTime.size() == other.sampleTime.size()) &&
56                // If two volumes are compressed differently they incompatible!
57                !(useTemporalCompression || other.useTemporalCompression);
58       }
59 
hasTimeTemporalConfig60       bool hasTime() const
61       {
62         return type != Constant;
63       }
64 
getNumSamplesTemporalConfig65       size_t getNumSamples() const
66       {
67         return type == Constant ? 1 : sampleTime.size();
68       }
69 
70      private:
equidistantTimeTemporalConfig71       static std::vector<float> equidistantTime(size_t numSamples)
72       {
73         std::vector<float> st(numSamples);
74         // Initialize to {} for numSamples 0, {0} for numSamples 1,
75         // and a regular grid between 0 and 1 for numSamples > 1.
76         const float dt =
77             1.f / static_cast<float>(std::max<size_t>(numSamples, 2) - 1);
78         for (size_t i = 0; i < numSamples; ++i)
79           st[i] = i * dt;
80         return st;
81       }
82     };
83 
84 
85     ///////////////////////////////////////////////////////////////////////////
86     // Helper functions ///////////////////////////////////////////////////////
87     ///////////////////////////////////////////////////////////////////////////
88 
89     template <typename T>
getVKLDataType()90     inline VKLDataType getVKLDataType()
91     {
92       if (std::is_same<T, unsigned char>::value) {
93         return VKL_UCHAR;
94       } else if (std::is_same<T, short>::value) {
95         return VKL_SHORT;
96       } else if (std::is_same<T, unsigned short>::value) {
97         return VKL_USHORT;
98       } else if (std::is_same<T, half_float::half>::value) {
99         return VKL_HALF;
100       } else if (std::is_same<T, float>::value) {
101         return VKL_FLOAT;
102       } else if (std::is_same<T, double>::value) {
103         return VKL_DOUBLE;
104       } else {
105         return VKL_UNKNOWN;
106       }
107     }
108 
sizeOfVKLDataType(VKLDataType dataType)109     inline size_t sizeOfVKLDataType(VKLDataType dataType)
110     {
111       switch (dataType) {
112       case VKL_UCHAR:
113         return sizeof(unsigned char);
114       case VKL_SHORT:
115         return sizeof(short);
116       case VKL_USHORT:
117         return sizeof(unsigned short);
118       case VKL_HALF:
119         return sizeof(half_float::half);
120       case VKL_FLOAT:
121         return sizeof(float);
122       case VKL_DOUBLE:
123         return sizeof(double);
124       case VKL_UNKNOWN:
125         break;
126       default:
127         break;
128       };
129 
130       throw std::runtime_error("cannot compute size of unknown VKLDataType");
131     }
132 
133     template <typename T>
computeValueRange(const void * data,size_t numValues)134     inline range1f computeValueRange(const void *data, size_t numValues)
135     {
136       const T *valuesTyped = (const T *)data;
137 
138       auto minmax = std::minmax_element(valuesTyped, valuesTyped + numValues);
139 
140       return range1f(*minmax.first, *minmax.second);
141     }
142 
computeValueRange(VKLDataType dataType,const void * data,size_t numValues)143     inline range1f computeValueRange(VKLDataType dataType,
144                                      const void *data,
145                                      size_t numValues)
146     {
147       if (dataType == VKL_UCHAR)
148         return computeValueRange<unsigned char>(data, numValues);
149       else if (dataType == VKL_SHORT)
150         return computeValueRange<short>(data, numValues);
151       else if (dataType == VKL_USHORT)
152         return computeValueRange<unsigned short>(data, numValues);
153       else if (dataType == VKL_HALF)
154         return computeValueRange<half_float::half>(data, numValues);
155       else if (dataType == VKL_FLOAT)
156         return computeValueRange<float>(data, numValues);
157       else if (dataType == VKL_DOUBLE)
158         return computeValueRange<double>(data, numValues);
159       else
160         throw std::runtime_error(
161             "computeValueRange() called with unsupported data type");
162     }
163 
164     ///////////////////////////////////////////////////////////////////////////
165     // TestingVolume //////////////////////////////////////////////////////////
166     ///////////////////////////////////////////////////////////////////////////
167 
168     struct TestingVolume
169     {
170       TestingVolume() = default;
171       virtual ~TestingVolume();
172 
173       void release();
174       VKLVolume getVKLVolume(VKLDevice device);
175 
176       // returns an application-side computed value range, for comparison with
177       // vklGetValueRange() results
178       virtual range1f getComputedValueRange() const = 0;
179 
180      protected:
181       virtual void generateVKLVolume(VKLDevice device) = 0;
182 
183       VKLVolume volume{nullptr};
184     };
185 
186     // Inlined definitions ////////////////////////////////////////////////////
187 
~TestingVolume()188     inline TestingVolume::~TestingVolume()
189     {
190       release();
191     }
192 
release()193     inline void TestingVolume::release()
194     {
195       if (volume) {
196         vklRelease(volume);
197         volume = nullptr;
198       }
199     }
200 
getVKLVolume(VKLDevice device)201     inline VKLVolume TestingVolume::getVKLVolume(VKLDevice device)
202     {
203       if (!volume) {
204         generateVKLVolume(device);
205       }
206 
207       return volume;
208     }
209 
210   }  // namespace testing
211 }  // namespace openvkl
212