1 // Copyright Contributors to the OpenVDB Project
2 // SPDX-License-Identifier: MPL-2.0
3 
4 /*!
5     \file   pnanovdb_validate_strides.h
6 
7     \author Andrew Reidmeyer
8 
9     \brief  This header implements validation tests for the strides used
10             in PNanaoVDB.h (instead of pointers). It can be used both for
11             unit-testing (hence its location), but also to update PNanoVDB.h
12             if the ABI changes in NanoVDB.h.
13 */
14 
15 #ifndef NANOVDB_PNANOVDB_VALIDATE_STRIDES_H_HAS_BEEN_INCLUDED
16 #define NANOVDB_PNANOVDB_VALIDATE_STRIDES_H_HAS_BEEN_INCLUDED
17 
18 #include <nanovdb/PNanoVDB.h>
19 
allocate(pnanovdb_uint32_t * poffset,pnanovdb_uint32_t size,pnanovdb_uint32_t alignment)20 static pnanovdb_uint32_t allocate(pnanovdb_uint32_t* poffset, pnanovdb_uint32_t size, pnanovdb_uint32_t alignment)
21 {
22     if (alignment > 0u)
23     {
24         (*poffset) = alignment * (((*poffset) + (alignment - 1)) / alignment);
25     }
26     pnanovdb_uint32_t ret = (*poffset);
27     (*poffset) += size;
28     return ret;
29 }
30 
compute_root_strides(pnanovdb_uint32_t grid_type,pnanovdb_uint32_t * background_off,pnanovdb_uint32_t * min_off,pnanovdb_uint32_t * max_off,pnanovdb_uint32_t * ave_off,pnanovdb_uint32_t * stddev_off,pnanovdb_uint32_t * total_size)31 static void compute_root_strides(
32     pnanovdb_uint32_t grid_type,
33     pnanovdb_uint32_t* background_off,
34     pnanovdb_uint32_t* min_off, pnanovdb_uint32_t* max_off,
35     pnanovdb_uint32_t* ave_off, pnanovdb_uint32_t* stddev_off,
36     pnanovdb_uint32_t* total_size)
37 {
38     pnanovdb_uint32_t offset = 0u;
39     allocate(&offset, PNANOVDB_ROOT_BASE_SIZE, 32u);
40 
41     pnanovdb_uint32_t minmaxStride = pnanovdb_grid_type_minmax_strides_bits[grid_type] / 8u;
42     pnanovdb_uint32_t minmaxAlign = pnanovdb_grid_type_minmax_aligns_bits[grid_type] / 8u;
43     pnanovdb_uint32_t statStride = pnanovdb_grid_type_stat_strides_bits[grid_type] / 8u;
44 
45     *background_off = allocate(&offset, minmaxStride, minmaxAlign);
46     *min_off = allocate(&offset, minmaxStride, minmaxAlign);
47     *max_off = allocate(&offset, minmaxStride, minmaxAlign);
48     *ave_off = allocate(&offset, statStride, statStride);
49     *stddev_off = allocate(&offset, statStride, statStride);
50     *total_size = allocate(&offset, 0u, 32u);
51 }
52 
compute_tile_strides(pnanovdb_uint32_t grid_type,pnanovdb_uint32_t * value_off,pnanovdb_uint32_t * total_size)53 static void compute_tile_strides(pnanovdb_uint32_t grid_type, pnanovdb_uint32_t* value_off, pnanovdb_uint32_t* total_size)
54 {
55     pnanovdb_uint32_t offset = 0u;
56     allocate(&offset, PNANOVDB_ROOT_TILE_BASE_SIZE, 32u);
57 
58     pnanovdb_uint32_t valueStride = pnanovdb_grid_type_minmax_strides_bits[grid_type] / 8u;
59     pnanovdb_uint32_t valueAlign = pnanovdb_grid_type_minmax_aligns_bits[grid_type] / 8u;
60 
61     *value_off = allocate(&offset, valueStride, valueAlign);
62     *total_size = allocate(&offset, 0u, 32u);
63 }
64 
compute_node_strides(pnanovdb_uint32_t grid_type,pnanovdb_uint32_t nodeLevel,pnanovdb_uint32_t * min_off,pnanovdb_uint32_t * max_off,pnanovdb_uint32_t * ave_off,pnanovdb_uint32_t * stddev_off,pnanovdb_uint32_t * table_off,pnanovdb_uint32_t * total_size)65 static void compute_node_strides(
66     pnanovdb_uint32_t grid_type,
67     pnanovdb_uint32_t nodeLevel,
68     pnanovdb_uint32_t* min_off, pnanovdb_uint32_t* max_off,
69     pnanovdb_uint32_t* ave_off, pnanovdb_uint32_t* stddev_off,
70     pnanovdb_uint32_t* table_off,
71     pnanovdb_uint32_t* total_size)
72 {
73     static const pnanovdb_uint32_t node_size[3] = { PNANOVDB_LEAF_BASE_SIZE, PNANOVDB_LOWER_BASE_SIZE, PNANOVDB_UPPER_BASE_SIZE };
74     static const pnanovdb_uint32_t node_elements[3] = { PNANOVDB_LEAF_TABLE_COUNT, PNANOVDB_LOWER_TABLE_COUNT, PNANOVDB_UPPER_TABLE_COUNT };
75     pnanovdb_uint32_t offset = 0u;
76     allocate(&offset, node_size[nodeLevel], 32u);
77 
78     pnanovdb_uint32_t valueStrideBits = pnanovdb_grid_type_value_strides_bits[grid_type];
79     pnanovdb_uint32_t tableStrideBits = nodeLevel == 0u ? valueStrideBits : pnanovdb_grid_type_table_strides_bits[grid_type];
80     pnanovdb_uint32_t tableAlign = 32u;
81     pnanovdb_uint32_t tableFullStride = (tableStrideBits * node_elements[nodeLevel]) / 8u;
82 
83     pnanovdb_uint32_t minmaxStride = pnanovdb_grid_type_minmax_strides_bits[grid_type] / 8u;
84     pnanovdb_uint32_t minmaxAlign = pnanovdb_grid_type_minmax_aligns_bits[grid_type] / 8u;
85     pnanovdb_uint32_t statStride = pnanovdb_grid_type_stat_strides_bits[grid_type] / 8u;
86     if (nodeLevel == 0u && pnanovdb_grid_type_leaf_type[grid_type] == PNANOVDB_LEAF_TYPE_LITE)
87     {
88         minmaxStride = 0u;
89         minmaxAlign = 0u;
90         statStride = 0u;
91     }
92 
93     if (nodeLevel == 0u && pnanovdb_grid_type_leaf_type[grid_type] == PNANOVDB_LEAF_TYPE_FP)
94     {
95         minmaxStride = 2u;
96         minmaxAlign = 2u;
97         statStride = 2u;
98         // allocate minimum and quantum
99         allocate(&offset, 4u, 4u);
100         allocate(&offset, 4u, 4u);
101     }
102     *min_off = allocate(&offset, minmaxStride, minmaxAlign);
103     *max_off = allocate(&offset, minmaxStride, minmaxAlign);
104     *ave_off = allocate(&offset, statStride, statStride);
105     *stddev_off = allocate(&offset, statStride, statStride);
106     *table_off = allocate(&offset, tableFullStride, tableAlign);
107     *total_size = allocate(&offset, 0u, 32u);
108 }
109 
validate_strides(int (* local_printf)(const char * format,...))110 static bool validate_strides(int(*local_printf)(const char* format, ...))
111 {
112     pnanovdb_grid_type_constants_t constants[PNANOVDB_GRID_TYPE_END];
113 
114     for (pnanovdb_uint32_t idx = 0u; idx < PNANOVDB_GRID_TYPE_END; idx++)
115     {
116 
117         pnanovdb_uint32_t root_background, root_min, root_max, root_ave, root_stddev, root_size;
118         compute_root_strides(idx, &root_background, &root_min, &root_max, &root_ave, &root_stddev, &root_size);
119 
120         pnanovdb_uint32_t tile_value, tile_size;
121         compute_tile_strides(idx, &tile_value, &tile_size);
122 
123         pnanovdb_uint32_t upper_min, upper_max, upper_ave, upper_stddev, upper_table, upper_size;
124         compute_node_strides(idx, 2, &upper_min, &upper_max, &upper_ave, &upper_stddev, &upper_table, &upper_size);
125 
126         pnanovdb_uint32_t lower_min, lower_max, lower_ave, lower_stddev, lower_table, lower_size;
127         compute_node_strides(idx, 1, &lower_min, &lower_max, &lower_ave, &lower_stddev, &lower_table, &lower_size);
128 
129         pnanovdb_uint32_t leaf_min, leaf_max, leaf_ave, leaf_stddev, leaf_table, leaf_size;
130         compute_node_strides(idx, 0, &leaf_min, &leaf_max, &leaf_ave, &leaf_stddev, &leaf_table, &leaf_size);
131 
132         pnanovdb_uint32_t valueStrideBits = pnanovdb_grid_type_value_strides_bits[idx];
133         pnanovdb_uint32_t tableStrideBits = pnanovdb_grid_type_table_strides_bits[idx];
134         pnanovdb_uint32_t tableStride = tableStrideBits / 8u;
135 
136         // For FP, always return the base of the table
137         if (pnanovdb_grid_type_leaf_type[idx] == PNANOVDB_LEAF_TYPE_FP)
138         {
139             valueStrideBits = 0u;
140         }
141 
142         pnanovdb_grid_type_constants_t local_constants = {
143             root_background, root_min, root_max, root_ave, root_stddev, root_size,
144             valueStrideBits, tableStride, tile_value, tile_size,
145             upper_min, upper_max, upper_ave, upper_stddev, upper_table, upper_size,
146             lower_min, lower_max, lower_ave, lower_stddev, lower_table, lower_size,
147             leaf_min, leaf_max, leaf_ave, leaf_stddev, leaf_table, leaf_size
148         };
149         constants[idx] = local_constants;
150     }
151 
152     bool mismatch = false;
153     for (pnanovdb_uint32_t idx = 0u; idx < PNANOVDB_GRID_TYPE_END; idx++)
154     {
155         pnanovdb_grid_type_constants_t c = constants[idx];
156         pnanovdb_grid_type_constants_t t = pnanovdb_grid_type_constants[idx];
157         if (memcmp(&c, &t, sizeof(pnanovdb_grid_type_constants_t)) != 0)
158         {
159             mismatch = true;
160         }
161     }
162     if (mismatch)
163     {
164         local_printf("Error: Mismatch between constant tables.\n");
165         for (pnanovdb_uint32_t pass = 0u; pass < 2u; pass++)
166         {
167             if (pass == 0u)
168             {
169                 local_printf("Printing expected values:\n");
170             }
171             else
172             {
173                 local_printf("Printing current header values:\n");
174             }
175             for (pnanovdb_uint32_t idx = 0u; idx < PNANOVDB_GRID_TYPE_END; idx++)
176             {
177                 pnanovdb_grid_type_constants_t c = (pass == 0u) ? constants[idx] : pnanovdb_grid_type_constants[idx];
178                 local_printf("{%d, %d, %d, %d, %d, %d,  %d, %d, %d, %d,  %d, %d, %d, %d, %d, %d,  %d, %d, %d, %d, %d, %d,  %d, %d, %d, %d, %d, %d},\n",
179                     c.root_off_background, c.root_off_min, c.root_off_max, c.root_off_ave, c.root_off_stddev, c.root_size,
180                     c.value_stride_bits, c.table_stride, c.root_tile_off_value, c.root_tile_size,
181                     c.upper_off_min, c.upper_off_max, c.upper_off_ave, c.upper_off_stddev, c.upper_off_table, c.upper_size,
182                     c.lower_off_min, c.lower_off_max, c.lower_off_ave, c.lower_off_stddev, c.lower_off_table, c.lower_size,
183                     c.leaf_off_min, c.leaf_off_max, c.leaf_off_ave, c.leaf_off_stddev, c.leaf_off_table, c.leaf_size
184                 );
185             }
186         }
187     }
188     return !mismatch;
189 }
190 
191 #endif// end of NANOVDB_PNANOVDB_VALIDATE_STRIDES_H_HAS_BEEN_INCLUDED