star-vx

Structuring voxels for ray-tracing
git clone git://git.meso-star.fr/star-vx.git
Log | Files | Refs | README | LICENSE

test_svx_octree.c (16145B)


      1 /* Copyright (C) 2018, 2020-2025 |Méso|Star> (contact@meso-star.com)
      2  * Copyright (C) 2018 Université Paul Sabatier
      3  *
      4  * This program is free software: you can redistribute it and/or modify
      5  * it under the terms of the GNU General Public License as published by
      6  * the Free Software Foundation, either version 3 of the License, or
      7  * (at your option) any later version.
      8  *
      9  * This program is distributed in the hope that it will be useful,
     10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
     11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
     12  * GNU General Public License for more details.
     13  *
     14  * You should have received a copy of the GNU General Public License
     15  * along with this program. If not, see <http://www.gnu.org/licenses/>. */
     16 
     17 #include "svx.h"
     18 #include "test_svx_utils.h"
     19 
     20 #include <rsys/double3.h>
     21 #include <rsys/morton.h>
     22 
     23 #include <string.h>
     24 
     25 struct leaves_context {
     26   double* lower;
     27   double* upper;
     28   size_t* nvoxels;
     29   size_t depth;
     30 };
     31 
     32 struct at_context {
     33   double position[3];
     34   size_t depth;
     35 };
     36 
     37 struct aabb {
     38   double lower[3];
     39   double upper[3];
     40   size_t depth;
     41 };
     42 
     43 struct build_context {
     44   double voxsz[3];
     45   double lower[3];
     46   double upper[3];
     47   size_t max_depth;
     48 };
     49 
     50 static int
     51 no_merge(const struct svx_voxel voxels[], const size_t nvoxels, void* ctx)
     52 {
     53   CHK(voxels != NULL);
     54   CHK(nvoxels != 0);
     55   CHK((intptr_t)ctx == 0xDECAFBAD);
     56   return 0; /* Merge nothing */
     57 }
     58 
     59 static int
     60 merge_level0(const struct svx_voxel voxels[], const size_t nvoxels, void* ctx)
     61 {
     62   double min_val = DBL_MAX;
     63   double max_val =-DBL_MAX;
     64   size_t i;
     65   CHK(voxels != NULL);
     66   CHK(nvoxels != 0);
     67   CHK((intptr_t)ctx == 0xDECAFBAD);
     68 
     69   FOR_EACH(i, 0, nvoxels) {
     70     const double* val = voxels[i].data;
     71     min_val = MMIN(min_val, *val);
     72     max_val = MMAX(max_val, *val);
     73   }
     74 
     75   FOR_EACH(i, 0, nvoxels) {
     76     if(max_val - min_val < 8) {
     77       CHK(voxels[i].depth == 5);
     78     }
     79   }
     80 
     81   return (max_val - min_val) < 8;
     82 }
     83 
     84 static void
     85 keep_max(void* dst, const void* voxels[], const size_t nvoxels,  void* ctx)
     86 {
     87   double* vox_dst = dst;
     88   double max_val = -DBL_MAX;
     89   size_t i;
     90 
     91   CHK(dst != NULL);
     92   CHK(voxels != NULL);
     93   CHK(nvoxels != 0);
     94   CHK(ctx != NULL);
     95 
     96   FOR_EACH(i, 0, nvoxels) {
     97     const double* val = voxels[i];
     98     max_val = MMAX(max_val, *val);
     99   }
    100   *vox_dst = max_val;
    101 }
    102 
    103 static void
    104 get(const size_t xyz[3], const uint64_t mcode, void* dst, void* ctx)
    105 {
    106   uint32_t ui3[3];
    107   double* val = dst;
    108   CHK(xyz != NULL);
    109   CHK(val != NULL);
    110   CHK((intptr_t)ctx == 0xDECAFBAD);
    111 
    112   ui3[0] = (uint32_t)xyz[0];
    113   ui3[1] = (uint32_t)xyz[1];
    114   ui3[2] = (uint32_t)xyz[2];
    115 
    116   CHK(mcode == morton_xyz_encode_u21(ui3));
    117   CHK(mcode == 
    118     ( morton3D_encode_u21(ui3[0]) << 2 
    119     | morton3D_encode_u21(ui3[1]) << 1
    120     | morton3D_encode_u21(ui3[2]) << 0));
    121   *val = (double)mcode;
    122 }
    123 
    124 static void
    125 check_leaves
    126   (const struct svx_voxel* leaf,
    127    const size_t ileaf,
    128    void* context)
    129 {
    130   const double* dbl = NULL;
    131   struct leaves_context* ctx = context;
    132   uint64_t mcode;
    133   uint32_t xyz[3];
    134   double lower[3];
    135   double delta[3];
    136 
    137   CHK(leaf != NULL);
    138   CHK(leaf->data != NULL);
    139   CHK(ctx != NULL);
    140   CHK(leaf->lower[0] < leaf->upper[0]);
    141   CHK(leaf->lower[1] < leaf->upper[1]);
    142   CHK(leaf->lower[2] < leaf->upper[2]);
    143   CHK(ileaf < ctx->nvoxels[0]*ctx->nvoxels[1]*ctx->nvoxels[2]);
    144 
    145   dbl = leaf->data;
    146   CHK(*dbl >= 0);
    147 
    148   mcode = (uint64_t)(*dbl);
    149   CHK(*dbl == (double)mcode);
    150 
    151   delta[0] = (ctx->upper[0] - ctx->lower[0]) / (double)ctx->nvoxels[0];
    152   delta[1] = (ctx->upper[1] - ctx->lower[1]) / (double)ctx->nvoxels[1];
    153   delta[2] = (ctx->upper[2] - ctx->lower[2]) / (double)ctx->nvoxels[2];
    154 
    155   morton_xyz_decode_u21(mcode, xyz);
    156   lower[0] = xyz[0] * delta[0];
    157   lower[1] = xyz[1] * delta[1];
    158   lower[2] = xyz[2] * delta[2];
    159 
    160   CHK(eq_eps(lower[0], leaf->lower[0], 1.e-6));
    161   CHK(eq_eps(lower[1], leaf->lower[1], 1.e-6));
    162   CHK(eq_eps(lower[2], leaf->lower[2], 1.e-6));
    163   CHK(eq_eps(lower[0] + delta[0], leaf->upper[0], 1.e-6));
    164   CHK(eq_eps(lower[1] + delta[1], leaf->upper[1], 1.e-6));
    165   CHK(eq_eps(lower[2] + delta[2], leaf->upper[2], 1.e-6));
    166 
    167   CHK(leaf->depth == ctx->depth - 1);
    168 }
    169 
    170 static void
    171 write_scalars
    172   (const struct svx_voxel* leaf,
    173    const size_t ileaf,
    174    void* context)
    175 {
    176   FILE* stream = context;
    177   (void)ileaf;
    178   CHK(stream != NULL);
    179   CHK(leaf != NULL);
    180   fprintf(stream, "%g\n", *(double*)leaf->data);
    181 }
    182 
    183 static int
    184 max_lod
    185   (const struct svx_voxel* vox,
    186    const double pos[3],
    187    void* context)
    188 {
    189   const struct at_context* ctx = context;
    190   CHK(vox != NULL);
    191   CHK(pos != NULL);
    192   CHK(ctx != NULL);
    193   CHK(vox->depth <= ctx->depth);
    194   CHK(d3_eq(pos, ctx->position));
    195   return vox->depth < ctx->depth;
    196 }
    197 
    198 static void
    199 get_aabb(const size_t xyz[3], const uint64_t mcode, void* dst, void* ctx)
    200 {
    201   const struct build_context* build_ctx = ctx;
    202   struct aabb* aabb = dst;
    203   uint32_t ui3[3];
    204 
    205   aabb->lower[0] = (double)xyz[0] * build_ctx->voxsz[0] + build_ctx->lower[0];
    206   aabb->lower[1] = (double)xyz[1] * build_ctx->voxsz[1] + build_ctx->lower[1];
    207   aabb->lower[2] = (double)xyz[2] * build_ctx->voxsz[2] + build_ctx->lower[2];
    208   
    209   ui3[0] = (uint32_t)xyz[0];
    210   ui3[1] = (uint32_t)xyz[1];
    211   ui3[2] = (uint32_t)xyz[2];
    212   CHK(mcode == morton_xyz_encode_u21(ui3));
    213   d3_add(aabb->upper, aabb->lower, build_ctx->voxsz);
    214   aabb->depth = build_ctx->max_depth;
    215 }
    216 
    217 static void
    218 merge_aabb(void* dst, const void* voxels[], const size_t nvoxels, void* ctx)
    219 {
    220   const struct build_context* build_ctx = ctx;
    221   double upper[3];
    222   double voxsz[3];
    223   struct aabb* aabb = dst;
    224   size_t depth = SIZE_MAX;
    225   size_t i;
    226   CHK(dst && voxels && nvoxels && ctx);
    227 
    228   d3_splat(aabb->lower, DBL_MAX);
    229   d3_splat(aabb->upper,-DBL_MAX);
    230   aabb->depth = 0;
    231 
    232   FOR_EACH(i, 0, nvoxels) {
    233     const struct aabb* vox_aabb = voxels[i];
    234     if(depth == SIZE_MAX) {
    235       depth = vox_aabb->depth;
    236     } else {
    237       CHK(depth == vox_aabb->depth);
    238     }
    239     aabb->lower[0] = MMIN(vox_aabb->lower[0], aabb->lower[0]);
    240     aabb->lower[1] = MMIN(vox_aabb->lower[1], aabb->lower[1]);
    241     aabb->lower[2] = MMIN(vox_aabb->lower[2], aabb->lower[2]);
    242     aabb->upper[0] = MMAX(vox_aabb->upper[0], aabb->upper[0]);
    243     aabb->upper[1] = MMAX(vox_aabb->upper[1], aabb->upper[1]);
    244     aabb->upper[2] = MMAX(vox_aabb->upper[2], aabb->upper[2]);
    245   }
    246 
    247   CHK(build_ctx->max_depth >= depth);
    248   CHK(depth > 0);
    249   aabb->depth = depth - 1;
    250 
    251   i = build_ctx->max_depth - aabb->depth;
    252   voxsz[0] = build_ctx->voxsz[0] * (double)(1<<i);
    253   voxsz[1] = build_ctx->voxsz[1] * (double)(1<<i);
    254   voxsz[2] = build_ctx->voxsz[2] * (double)(1<<i);
    255 
    256   /* Clamp voxel to grid size */
    257   upper[0] = MMIN(aabb->lower[0] + voxsz[0], build_ctx->upper[0]);
    258   upper[1] = MMIN(aabb->lower[1] + voxsz[1], build_ctx->upper[1]);
    259   upper[2] = MMIN(aabb->lower[2] + voxsz[2], build_ctx->upper[2]);
    260 
    261   /* Adjust the voxel size from the clampd voxel */
    262   voxsz[0] = upper[0] - aabb->lower[0];
    263   voxsz[1] = upper[1] - aabb->lower[1];
    264   voxsz[2] = upper[2] - aabb->lower[2];
    265 
    266   CHK(eq_eps(voxsz[0], aabb->upper[0] - aabb->lower[0], 1.e-6));
    267   CHK(eq_eps(voxsz[1], aabb->upper[1] - aabb->lower[1], 1.e-6));
    268   CHK(eq_eps(voxsz[2], aabb->upper[2] - aabb->lower[2], 1.e-6));
    269 }
    270 
    271 static int
    272 challenge_aabb(const struct svx_voxel voxels[], const size_t nvoxels, void* ctx)
    273 {
    274   const struct build_context* build_ctx = ctx;
    275   size_t depth = SIZE_MAX;
    276   size_t i;
    277   CHK(voxels && nvoxels && ctx);
    278 
    279   FOR_EACH(i, 0, nvoxels) {
    280     double voxsz[3];
    281     const struct aabb* aabb = voxels[i].data;
    282     const size_t n = build_ctx->max_depth - aabb->depth;
    283     CHK(build_ctx->max_depth >= aabb->depth);
    284 
    285     if(depth == SIZE_MAX) {
    286       depth = aabb->depth;
    287     } else {
    288       CHK(depth == aabb->depth);
    289     }
    290     CHK(depth == voxels[i].depth);
    291     CHK(d3_eq_eps(aabb->lower, voxels[i].lower, 1.e-6));
    292     CHK(d3_eq_eps(aabb->upper, voxels[i].upper, 1.e-6));
    293     voxsz[0] = build_ctx->voxsz[0] * (double)(1<<n);
    294     voxsz[1] = build_ctx->voxsz[1] * (double)(1<<n);
    295     voxsz[2] = build_ctx->voxsz[2] * (double)(1<<n);
    296     CHK(eq_eps(voxsz[0], aabb->upper[0] - aabb->lower[0], 1.e-6));
    297     CHK(eq_eps(voxsz[1], aabb->upper[1] - aabb->lower[1], 1.e-6));
    298     CHK(eq_eps(voxsz[2], aabb->upper[2] - aabb->lower[2], 1.e-6));
    299   }
    300 
    301   return 1;
    302 }
    303 
    304 static void
    305 test_at_accessor(struct svx_tree* oct, const size_t nvoxels[3])
    306 {
    307   struct svx_tree_desc tree_desc;
    308   struct at_context ctx;
    309   size_t nvxls;
    310   double delta[3];
    311   double ocsz[3];
    312   size_t x, y, z;
    313 
    314   CHK(nvoxels != NULL);
    315   CHK(svx_tree_get_desc(oct, &tree_desc) == RES_OK);
    316   CHK(tree_desc.type == SVX_OCTREE);
    317   CHK(tree_desc.frame[0] == SVX_AXIS_X);
    318   CHK(tree_desc.frame[1] == SVX_AXIS_Y);
    319   CHK(tree_desc.frame[2] == SVX_AXIS_Z);
    320 
    321   ocsz[0] = tree_desc.upper[0] - tree_desc.lower[0];
    322   ocsz[1] = tree_desc.upper[1] - tree_desc.lower[1];
    323   ocsz[2] = tree_desc.upper[2] - tree_desc.lower[2];
    324   delta[0] = ocsz[0]/(double)nvoxels[0];
    325   delta[1] = ocsz[1]/(double)nvoxels[1];
    326   delta[2] = ocsz[2]/(double)nvoxels[2];
    327 
    328   nvxls = nvoxels[0];
    329   nvxls = MMAX(nvxls, nvoxels[1]);
    330   nvxls = MMAX(nvxls, nvoxels[2]);
    331 
    332   ctx.depth = tree_desc.depth;
    333   CHK(ctx.depth > 0);
    334 
    335   while(ctx.depth-- != 0) {
    336     const size_t iter = tree_desc.depth - ctx.depth - 1;
    337     double pos[3];
    338     double low[3];
    339     double upp[3];
    340 
    341     FOR_EACH(x, 0, nvxls) {
    342       pos[0] = tree_desc.lower[0] + ((double)x+1.0/(1u<<(2+iter)))*delta[0];
    343       low[0] = tree_desc.lower[0] + (double)x * delta[0];
    344       upp[0] = low[0] + delta[0];
    345       if(x*(size_t)(1u<<iter) >= nvoxels[0]) break;
    346 
    347       FOR_EACH(y, 0, nvxls) {
    348         pos[1] = tree_desc.lower[1] + ((double)y+1.0/(1u<<(2+iter)))*delta[1];
    349         low[1] = tree_desc.lower[1] + (double)y * delta[1];
    350         upp[1] = low[1] + delta[1];
    351 
    352         if(y*(size_t)(1u<<iter) >= nvoxels[1]) break;
    353 
    354         FOR_EACH(z, 0, nvxls) {
    355           struct svx_voxel vox;
    356           uint32_t ui3[3];
    357           uint64_t mcode;
    358 
    359           pos[2] = tree_desc.lower[2] + ((double)z+1.0/(1u<<(2+iter)))*delta[2];
    360           low[2] = tree_desc.lower[2] + (double)z * delta[2];
    361           upp[2] = low[2] + delta[2];
    362 
    363           if(z*(size_t)(1u<<iter) >= nvoxels[2]) break;
    364 
    365           d3_set(ctx.position, pos);
    366           CHK(svx_tree_at(oct, pos, max_lod, &ctx, &vox) == RES_OK);
    367           CHK(!SVX_VOXEL_NONE(&vox));
    368 
    369           ui3[0] = (uint32_t)x * (1u << iter) + ((1u << iter) - 1);
    370           ui3[1] = (uint32_t)y * (1u << iter) + ((1u << iter) - 1);
    371           ui3[2] = (uint32_t)z * (1u << iter) + ((1u << iter) - 1);
    372           ui3[0] = MMIN(ui3[0], (uint32_t)(nvoxels[0]-1));
    373           ui3[1] = MMIN(ui3[1], (uint32_t)(nvoxels[1]-1));
    374           ui3[2] = MMIN(ui3[2], (uint32_t)(nvoxels[2]-1));
    375 
    376           mcode = morton_xyz_encode_u21(ui3);
    377           CHK(*((double*)vox.data) == mcode);
    378           CHK(vox.is_leaf == (tree_desc.depth-1==ctx.depth));
    379           CHK(vox.depth == ctx.depth);
    380           CHK(d3_eq_eps(vox.lower, low, 1.e-6));
    381           CHK(d3_eq_eps(vox.upper, upp, 1.e-6));
    382         }
    383       }
    384     }
    385 
    386     nvxls = nvxls == 1 ? 0 : (nvxls + 1/*ceil*/) / 2;
    387     delta[0] = MMIN(delta[0] * 2, ocsz[0]);
    388     delta[1] = MMIN(delta[1] * 2, ocsz[1]);
    389     delta[2] = MMIN(delta[2] * 2, ocsz[2]);
    390   }
    391   CHK(nvxls == 0);
    392 }
    393 
    394 int
    395 main(int argc, char** argv)
    396 {
    397   struct svx_device* dev = NULL;
    398   struct svx_tree* oct = NULL;
    399   struct mem_allocator allocator;
    400   struct svx_voxel_desc voxdesc = SVX_VOXEL_DESC_NULL;
    401   struct svx_tree_desc tree_desc = SVX_TREE_DESC_NULL;
    402   struct build_context build_ctx;
    403   double low[3];
    404   double upp[3];
    405   size_t nvxls[3];
    406   struct leaves_context ctx;
    407   void* ptr = (void*)(intptr_t)0xDECAFBAD;
    408   FILE* stream = NULL;
    409   (void)argc, (void)argv;
    410 
    411   CHK(mem_init_proxy_allocator(&allocator, &mem_default_allocator) == RES_OK);
    412 
    413   CHK(svx_device_create(NULL, &allocator, 1, &dev) == RES_OK);
    414 
    415   d3_splat(low, 0.0);
    416   d3_splat(upp, 1.0);
    417   nvxls[0] = nvxls[1] = nvxls[2] = 5;
    418 
    419   ctx.lower = low;
    420   ctx.upper = upp;
    421   ctx.nvoxels = nvxls;
    422   ctx.depth = 4;
    423 
    424   #define NEW_SCN svx_octree_create
    425 
    426   voxdesc.get = get;
    427   voxdesc.merge = keep_max;
    428   voxdesc.challenge_merge = no_merge;
    429   voxdesc.context = ptr;
    430   voxdesc.size = sizeof(double);
    431   CHK(NEW_SCN(dev, low, upp, nvxls, &voxdesc, &oct) == RES_OK);
    432 
    433   CHK(svx_tree_ref_get(NULL) == RES_BAD_ARG);
    434   CHK(svx_tree_ref_get(oct) == RES_OK);
    435   CHK(svx_tree_ref_put(NULL) == RES_BAD_ARG);
    436   CHK(svx_tree_ref_put(oct) == RES_OK);
    437   CHK(svx_tree_ref_put(oct) == RES_OK);
    438 
    439   upp[0] = low[0];
    440   CHK(NEW_SCN(dev, low, upp, nvxls, &voxdesc, &oct) == RES_BAD_ARG);
    441   upp[0] = 1.0;
    442 
    443   nvxls[2] = 0;
    444   CHK(NEW_SCN(dev, low, upp, nvxls, &voxdesc, &oct) == RES_BAD_ARG);
    445   nvxls[2] = nvxls[0];
    446 
    447   CHK(NEW_SCN(NULL, low, upp, nvxls, &voxdesc, &oct) == RES_BAD_ARG);
    448   CHK(NEW_SCN(dev, NULL, upp, nvxls, &voxdesc, &oct) == RES_BAD_ARG);
    449   CHK(NEW_SCN(dev, low, NULL, nvxls, &voxdesc, &oct) == RES_BAD_ARG);
    450   CHK(NEW_SCN(dev, low, upp, NULL, &voxdesc, &oct) == RES_BAD_ARG);
    451   CHK(NEW_SCN(dev, low, upp, nvxls, &voxdesc, NULL) == RES_BAD_ARG);
    452 
    453   voxdesc.get = NULL;
    454   CHK(NEW_SCN(dev, low, upp, nvxls, &voxdesc, &oct) == RES_BAD_ARG);
    455   voxdesc.get = get;
    456 
    457   voxdesc.merge = NULL;
    458   CHK(NEW_SCN(dev, low, upp, nvxls, &voxdesc, &oct) == RES_BAD_ARG);
    459   voxdesc.merge = keep_max;
    460 
    461   voxdesc.challenge_merge = NULL;
    462   CHK(NEW_SCN(dev, low, upp, nvxls, &voxdesc, &oct) == RES_BAD_ARG);
    463   voxdesc.challenge_merge = no_merge;
    464 
    465   voxdesc.size = 0;
    466   CHK(NEW_SCN(dev, low, upp, nvxls, &voxdesc, &oct) == RES_BAD_ARG);
    467   voxdesc.size = sizeof(double);
    468 
    469   voxdesc.size = SVX_MAX_SIZEOF_VOXEL + 1;
    470   CHK(NEW_SCN(dev, low, upp, nvxls, &voxdesc, &oct) == RES_BAD_ARG);
    471   voxdesc.size = sizeof(double);
    472 
    473   CHK(NEW_SCN(dev, low, upp, nvxls, &voxdesc, &oct) == RES_OK);
    474 
    475   CHK(svx_tree_for_each_leaf(oct, check_leaves, &ctx) == RES_OK);
    476 
    477   CHK(svx_tree_get_desc(NULL, &tree_desc) == RES_BAD_ARG);
    478   CHK(svx_tree_get_desc(oct, NULL) == RES_BAD_ARG);
    479   CHK(svx_tree_get_desc(oct, &tree_desc) == RES_OK);
    480   CHK(tree_desc.nleaves == 5*5*5);
    481   CHK(tree_desc.nvoxels == tree_desc.nleaves + 36/*#parents*/);
    482   CHK(tree_desc.depth == 4);
    483   CHK(tree_desc.type == SVX_OCTREE);
    484 
    485   d3_splat(low, 0);
    486   d3_splat(upp, 1);
    487   CHK(d3_eq(tree_desc.lower, low));
    488   CHK(d3_eq(tree_desc.upper, upp));
    489 
    490   test_at_accessor(oct, nvxls);
    491 
    492   CHK(stream = tmpfile());
    493   CHK(svx_tree_write(NULL, stream) == RES_BAD_ARG);
    494   CHK(svx_tree_write(oct, NULL) == RES_BAD_ARG);
    495   CHK(svx_tree_write(oct, stream) == RES_OK);
    496 
    497   CHK(svx_tree_ref_put(oct) == RES_OK);
    498 
    499   rewind(stream);
    500   CHK(svx_tree_create_from_stream(NULL, stream, &oct) == RES_BAD_ARG);
    501   CHK(svx_tree_create_from_stream(dev, NULL, &oct) == RES_BAD_ARG);
    502   CHK(svx_tree_create_from_stream(dev, stream, NULL) == RES_BAD_ARG);
    503   CHK(svx_tree_create_from_stream(dev, stream, &oct) == RES_OK);
    504   fclose(stream);
    505 
    506   test_at_accessor(oct, nvxls);
    507   CHK(svx_tree_ref_put(oct) == RES_OK);
    508 
    509   nvxls[0] = nvxls[1] = nvxls[2] = 32;
    510   voxdesc.challenge_merge = merge_level0;
    511   CHK(NEW_SCN(dev, low, upp, nvxls, &voxdesc, &oct) == RES_OK);
    512   CHK(svx_tree_get_desc(oct, &tree_desc) == RES_OK);
    513   CHK(tree_desc.nleaves == nvxls[0]*nvxls[1]*nvxls[2] / 8);
    514   CHK(tree_desc.nvoxels == (tree_desc.nleaves*8 - 1) / 7);
    515   CHK(tree_desc.depth == (size_t)log2i((int)(nvxls[0]/2))+1);
    516   CHK(tree_desc.type == SVX_OCTREE);
    517 
    518   dump_data(stdout, oct, TYPE_FLOAT, 1, write_scalars);
    519 
    520   CHK(svx_tree_ref_put(oct) == RES_OK);
    521 
    522   nvxls[0] = 32;
    523   nvxls[1] = 16;
    524   nvxls[2] = 33;
    525 
    526   build_ctx.max_depth = (size_t)log2i
    527     ((int)round_up_pow2(MMAX(MMAX(nvxls[0], nvxls[1]), nvxls[2])));
    528 
    529   d3_set(build_ctx.lower, low);
    530   d3_set(build_ctx.upper, upp);
    531   build_ctx.voxsz[0] = (upp[0]-low[0])/(double)nvxls[0];
    532   build_ctx.voxsz[1] = (upp[1]-low[1])/(double)nvxls[1];
    533   build_ctx.voxsz[2] = (upp[2]-low[2])/(double)nvxls[2];
    534 
    535   voxdesc.get = get_aabb;
    536   voxdesc.merge = merge_aabb;
    537   voxdesc.challenge_merge = challenge_aabb;
    538   voxdesc.context = &build_ctx;
    539   voxdesc.size = sizeof(struct aabb);
    540 
    541   CHK(NEW_SCN(dev, low, upp, nvxls, &voxdesc, &oct) == RES_OK);
    542 
    543   #undef NEW_SCN
    544 
    545   CHK(svx_device_ref_put(dev) == RES_OK);
    546   CHK(svx_tree_ref_put(oct) == RES_OK);
    547 
    548   check_memory_allocator(&allocator);
    549   mem_shutdown_proxy_allocator(&allocator);
    550   CHK(mem_allocated_size() == 0);
    551   return 0;
    552 }
    553