star-vx

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

commit 721120473da85bc99fe97da6f9855026c3a82f18
parent 45e23e32081ee0d22a984b3bf17fc2156a7d99d1
Author: Vincent Forest <vincent.forest@meso-star.com>
Date:   Wed, 19 Feb 2020 14:12:59 +0100

Add and test functions to [de]serialize trees

Diffstat:
Msrc/svx.h | 11+++++++++++
Msrc/svx_bintree.c | 2+-
Msrc/svx_buffer.c | 164+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++----
Msrc/svx_buffer.h | 25+++++++++++++++++++++++--
Msrc/svx_octree.c | 3+--
Msrc/svx_tree.c | 252+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Msrc/svx_tree.h | 10++++++++++
Msrc/svx_tree_builder.h | 27---------------------------
Msrc/test_svx_bintree.c | 17+++++++++++++++++
Msrc/test_svx_bintree_trace_ray.c | 40+++++++++++++++++++++++++++++-----------
Msrc/test_svx_octree.c | 16++++++++++++++++
Msrc/test_svx_octree_trace_ray.c | 38++++++++++++++++++++++++++++----------
Msrc/test_svx_utils.h | 28++++++++++++++++++++++++++++
13 files changed, 573 insertions(+), 60 deletions(-)

diff --git a/src/svx.h b/src/svx.h @@ -236,6 +236,12 @@ svx_bintree_create struct svx_tree** tree); SVX_API res_T +svx_tree_create_from_stream + (struct svx_device* dev, + FILE* stream, + struct svx_tree** tree); + +SVX_API res_T svx_tree_ref_get (struct svx_tree* tree); @@ -273,5 +279,10 @@ svx_tree_at void* context, /* Client data sent as the last argument of the filter func */ struct svx_voxel* voxel); +SVX_API res_T +svx_tree_write + (const struct svx_tree* tree, + FILE* stream); + #endif /* SVX_H */ diff --git a/src/svx_bintree.c b/src/svx_bintree.c @@ -120,7 +120,7 @@ svx_bintree_create #ifndef NDEBUG { size_t nleaves = 0; - bintree_check(&bintree->buffer, bintree->root, &nleaves); + CHK(buffer_check_tree(&bintree->buffer, bintree->root, 1, &nleaves) == RES_OK); CHK(nleaves == bintree->nleaves); } #endif diff --git a/src/svx_buffer.c b/src/svx_buffer.c @@ -15,13 +15,14 @@ #include "svx_buffer.h" +#include <rsys/math.h> #include <rsys/mem_allocator.h> #ifdef COMPILER_CL -# define WIN32_LEAN_AND_MEAN -# include <windows.h> + #define WIN32_LEAN_AND_MEAN + #include <windows.h> #else -#include <unistd.h> + #include <unistd.h> #endif /******************************************************************************* @@ -43,8 +44,8 @@ ensure_allocated_nodes(struct buffer* buf, const size_t nnodes) if(nnode_pages > UINT32_MAX) { res = RES_MEM_ERR; goto error; } ASSERT(nnode_pages == buf->node_head.ipage + 1); - /* Alloc and register a node page containing the node and the far indices */ - node_page = MEM_ALLOC(buf->allocator, buf->pagesize); + /* Alloc and register a node page containing the nodes and the far indices */ + node_page = MEM_CALLOC(buf->allocator, 1, buf->pagesize); if(!node_page) { res = RES_MEM_ERR; goto error; } res = darray_page_push_back(&buf->node_pages, &node_page); if(res != RES_OK) goto error; @@ -77,7 +78,7 @@ ensure_allocated_attrs(struct buffer* buf, const size_t nattrs) ASSERT(nattr_pages == buf->attr_head.ipage + 1); /* Alloc and register a attr page */ - attr_page = MEM_ALLOC(buf->allocator, buf->pagesize); + attr_page = MEM_CALLOC(buf->allocator, 1, buf->pagesize); if(!attr_page) { res = RES_MEM_ERR; goto error; } res = darray_page_push_back(&buf->attr_pages, &attr_page); if(res != RES_OK) goto error; @@ -147,7 +148,6 @@ buffer_alloc_nodes *first_node = buf->node_head; buf->node_head.inode = (uint16_t)(buf->node_head.inode + nnodes); return RES_OK; - } res_T @@ -207,3 +207,153 @@ buffer_clear(struct buffer* buf) buf->attr_head = BUFFER_INDEX_NULL; } +res_T +buffer_write(const struct buffer* buf, FILE* stream) +{ + size_t ipage = 0; + size_t npages = 0; + res_T res = RES_OK; + ASSERT(buf && stream); + + #define WRITE(Var, N) { \ + if(fwrite((Var), sizeof(*(Var)), (N), stream) != (N)) { \ + res = RES_IO_ERR; \ + goto error; \ + } \ + } (void)0 + WRITE(&BUFFER_VERSION, 1); + WRITE(&buf->pagesize, 1); + WRITE(&buf->voxsize, 1); + WRITE(&buf->node_head, 1); + WRITE(&buf->attr_head, 1); + + npages = darray_page_size_get(&buf->node_pages); + WRITE(&npages, 1); + FOR_EACH(ipage, 0, npages) { + WRITE(darray_page_cdata_get(&buf->node_pages)[ipage], buf->pagesize); + } + + npages = darray_page_size_get(&buf->attr_pages); + WRITE(&npages, 1); + FOR_EACH(ipage, 0, npages) { + WRITE(darray_page_cdata_get(&buf->attr_pages)[ipage], buf->pagesize); + } + #undef WRITE + +exit: + return res; +error: + goto exit; +} + +res_T +buffer_read(struct buffer* buf, FILE* stream) +{ + int version = 0; + char* page = NULL; + size_t ipage = 0; + size_t npages = 0; + res_T res = RES_OK; + ASSERT(buf && stream); + + buffer_clear(buf); + + #define READ(Var, N) { \ + if(fread((Var), sizeof(*(Var)), (N), stream) != (N)) { \ + if(feof(stream)) { \ + res = RES_BAD_ARG; \ + } else if(ferror(stream)) { \ + res = RES_IO_ERR; \ + } else { \ + res = RES_UNKNOWN_ERR; \ + } \ + goto error; \ + } \ + } (void)0 + + /* Currently only one version of the buffer data structure could be + * serialized. The version management is thus as simple as rejecting any + * buffer data structure whose version is not the current version. */ + READ(&version, 1); + if(version != BUFFER_VERSION) { + res = RES_BAD_ARG; + goto error; + } + + READ(&buf->pagesize, 1); + READ(&buf->voxsize, 1); + READ(&buf->node_head, 1); + READ(&buf->attr_head, 1); + + READ(&npages, 1); + res = darray_page_reserve(&buf->node_pages, npages); + if(res != RES_OK) goto error; + + /* Read the pages of nodes */ + FOR_EACH(ipage, 0, npages) { + page = MEM_ALLOC(buf->allocator, buf->pagesize); + if(!page) { res = RES_MEM_ERR; goto error; } + + READ(page, buf->pagesize); + CHK(darray_page_push_back(&buf->node_pages, &page) == RES_OK); + page = NULL; + } + + READ(&npages, 1); + res = darray_page_reserve(&buf->attr_pages, npages); + if(res != RES_OK) goto error; + + /* Read the pages of attribs */ + FOR_EACH(ipage, 0, npages) { + page = MEM_ALLOC(buf->allocator, buf->pagesize); + if(!page) { res = RES_MEM_ERR; goto error; } + + READ(page, buf->pagesize); + CHK(darray_page_push_back(&buf->attr_pages, &page) == RES_OK); + page = NULL; + } + #undef READ + +exit: + return res; +error: + if(page) MEM_RM(buf->allocator, page); + buffer_clear(buf); + goto exit; +} + +res_T +buffer_check_tree + (struct buffer* buf, + const struct buffer_index root, + const size_t tree_dimension, + size_t* nleaves) +{ + const struct buffer_xnode* node; + const int nchildren = BIT((int)tree_dimension); + int ichild; + res_T res = RES_OK; + ASSERT(buf); + ASSERT(0 < tree_dimension && tree_dimension <= 3); + + node = buffer_get_node(buf, root); + FOR_EACH(ichild, 0, nchildren) { + const int ichild_flag = BIT(ichild); + if((node->is_valid & ichild_flag) == 0) continue; + + if(node->is_leaf & ichild_flag) { + struct buffer_index iattr; + iattr = buffer_get_child_attr_index(buf, root, ichild); + if(buffer_get_attr(buf, iattr) == NULL) + return RES_BAD_ARG; + *nleaves += 1; + } else { + struct buffer_index child; + child = buffer_get_child_node_index(buf, root, ichild); + res = buffer_check_tree(buf, child, tree_dimension, nleaves); + if(res != RES_OK) return res; + } + } + return RES_OK; +} + diff --git a/src/svx_buffer.h b/src/svx_buffer.h @@ -47,7 +47,7 @@ struct buffer_xnode { /* Offset to retrieve the children. If the BUFFER_XNODE_FLAG_FAR_INDEX bit is * not set, the children are stored in the same page at the position `offset * & BUFFER_XNODE_MASK'. If BUFFER_XNODE_FLAG_FAR_INDEX is set, `offset & - * BUFFER_XNODE_MASK' reference an buffer_index toward the node children */ + * BUFFER_XNODE_MASK' reference a buffer_index toward the node children */ uint16_t node_offset; uint16_t attr_offset; uint8_t is_valid; /* Mask defining if the children are valid */ @@ -76,6 +76,9 @@ static const struct buffer_index BUFFER_INDEX_NULL = BUFFER_INDEX_NULL__; #define DARRAY_DATA char* #include <rsys/dynamic_array.h> +/* Current version the buffer index data structure */ +static const int BUFFER_VERSION = 0; + struct buffer { size_t pagesize; /* Memory page size in bytes */ size_t voxsize; /* Memory size of a voxel in bytes */ @@ -110,7 +113,7 @@ buffer_alloc_attrs const size_t nattrs, struct buffer_index* first_attr); /* Index toward the 1st allocated attrib */ -/* Allocate an buffer_index in the current buffer page. Return RES_MEM_ERR if +/* Allocate a buffer_index in the current buffer page. Return RES_MEM_ERR if * the node index cannot be allocated in the current page. In this case one * have to alloc new nodes */ extern LOCAL_SYM res_T @@ -122,6 +125,24 @@ extern LOCAL_SYM void buffer_clear (struct buffer* buf); +extern LOCAL_SYM res_T +buffer_write + (const struct buffer* buf, + FILE* stream); + +extern LOCAL_SYM res_T +buffer_read + (struct buffer* buf, + FILE* stream); + +/* Check buffer data regarding a given tree root and tree dimension */ +extern LOCAL_SYM res_T +buffer_check_tree + (struct buffer* buffer, + const struct buffer_index root, /* Root of the tree */ + const size_t tree_dimension, /* Dimension of the tree */ + size_t* nleaves); /* Overall #leaves of the tree */ + static FINLINE int buffer_is_empty(const struct buffer* buf) { diff --git a/src/svx_octree.c b/src/svx_octree.c @@ -154,7 +154,7 @@ svx_octree_create #ifndef NDEBUG { size_t nleaves = 0; - octree_check(&oct->buffer, oct->root, &nleaves); + CHK(buffer_check_tree(&oct->buffer, oct->root, 3, &nleaves) == RES_OK); CHK(nleaves == oct->nleaves); } #endif @@ -175,4 +175,3 @@ error: goto exit; } - diff --git a/src/svx_tree.c b/src/svx_tree.c @@ -31,6 +31,123 @@ /******************************************************************************* * Helper function ******************************************************************************/ +static res_T +check_octree(struct svx_tree* tree) +{ + ASSERT(tree && tree->type == SVX_OCTREE); + + if(tree->frame[0] != SVX_AXIS_X + && tree->frame[1] != SVX_AXIS_Y + && tree->frame[2] != SVX_AXIS_Z) + return RES_BAD_ARG; + + if(!IS_POW2(tree->definition)) + return RES_BAD_ARG; + + if(tree->lower[0] >= tree->upper[0] + || tree->lower[1] >= tree->upper[1] + || tree->lower[2] >= tree->upper[2]) + return RES_BAD_ARG; + + if(tree->tree_low[0] >= tree->tree_upp[0] + || tree->tree_low[1] >= tree->tree_upp[1] + || tree->tree_low[2] >= tree->tree_upp[2]) + return RES_BAD_ARG; + + if(tree->tree_low[0] != tree->lower[0] + || tree->tree_low[1] != tree->lower[1] + || tree->tree_low[2] != tree->lower[2]) + return RES_BAD_ARG; + + if(tree->tree_upp[0] < tree->upper[0] + || tree->tree_upp[1] < tree->upper[1] + || tree->tree_upp[2] < tree->upper[2]) + return RES_BAD_ARG; + + if(tree->tree_size[0] != tree->tree_upp[0] - tree->tree_low[0] + || tree->tree_size[1] != tree->tree_upp[1] - tree->tree_low[1] + || tree->tree_size[2] != tree->tree_upp[2] - tree->tree_low[2]) + return RES_BAD_ARG; + + #ifndef NDEBUG + { + size_t nleaves = 0; + res_T res = buffer_check_tree(&tree->buffer, tree->root, 3, &nleaves); + if(res != RES_OK) return res; + + if(nleaves != tree->nleaves) + return RES_BAD_ARG; + } + #endif + + return RES_OK; +} + +static res_T +check_bintree(struct svx_tree* tree) +{ + enum svx_axis axis = SVX_AXIS_NONE__; + ASSERT(tree && tree->type == SVX_BINTREE); + + if(tree->frame[0] == SVX_AXIS_NONE__ + || tree->frame[1] != SVX_AXIS_NONE__ + || tree->frame[2] != SVX_AXIS_NONE__) + return RES_BAD_ARG; + + if(!IS_POW2(tree->definition)) + return RES_BAD_ARG; + + axis = tree->frame[0]; + if(tree->lower[axis] >= tree->upper[axis]) + return RES_BAD_ARG; + + if(tree->tree_low[axis] >= tree->tree_upp[axis]) + return RES_BAD_ARG; + + if(tree->tree_low[axis] != tree->lower[axis]) + return RES_BAD_ARG; + + if(tree->tree_upp[axis] < tree->upper[axis]) + return RES_BAD_ARG; + + if(tree->tree_size[axis] != tree->tree_upp[axis] - tree->tree_low[axis]) + return RES_BAD_ARG; + + #ifndef NDEBUG + { + size_t nleaves = 0; + res_T res = buffer_check_tree(&tree->buffer, tree->root, 1, &nleaves); + if(res != RES_OK) return res; + + if(nleaves != tree->nleaves) + return RES_BAD_ARG; + } + #endif + + return RES_OK; +} + +static void +tree_clear(struct svx_tree* tree) +{ + ASSERT(tree); + tree->definition = 0; + d3_splat(tree->lower, DBL_MAX); + d3_splat(tree->upper,-DBL_MAX); + d3_splat(tree->tree_low, DBL_MAX); + d3_splat(tree->tree_upp,-DBL_MAX); + d3_splat(tree->tree_size, -1); + tree->root = BUFFER_INDEX_NULL; + buffer_clear(&tree->buffer); + memset(tree->root_attr, 0, sizeof(tree->root_attr)); + tree->nleaves = 0; + tree->depth = 0; + tree->frame[0] = SVX_AXIS_NONE__; + tree->frame[1] = SVX_AXIS_NONE__; + tree->frame[2] = SVX_AXIS_NONE__; + tree->type = -1; +} + static void tree_release(ref_T* ref) { @@ -48,6 +165,43 @@ tree_release(ref_T* ref) * Exported functions ******************************************************************************/ res_T +svx_tree_create_from_stream + (struct svx_device* dev, + FILE* stream, + struct svx_tree** out_tree) +{ + struct svx_tree* tree = NULL; + res_T res = RES_BAD_ARG; + + if(!stream || !out_tree) { + res = RES_BAD_ARG; + goto error; + } + + res = tree_create + (dev, -1/* Unknown tree type */, 1/* Dummy voxel size */, &tree); + if(res != RES_OK) goto error; + + /* Setup the tree data from the stream */ + res = tree_read(tree, stream); + if(res != RES_OK) goto error; + + switch(tree->type) { + case SVX_OCTREE: res = check_octree(tree); break; + case SVX_BINTREE: res = check_bintree(tree); break; + default: FATAL("Unreachable code.\n"); break; + } + if(res != RES_OK) goto error; + +exit: + if(out_tree) *out_tree = tree; + return res; +error: + if(tree) { SVX(tree_ref_put(tree)); tree = NULL; } + goto exit; +} + +res_T svx_tree_ref_get(struct svx_tree* tree) { if(!tree) return RES_BAD_ARG; @@ -139,6 +293,46 @@ svx_tree_at return res; } +res_T +svx_tree_write(const struct svx_tree* tree, FILE* stream) +{ + res_T res = RES_OK; + + if(!tree || !stream) { + res = RES_BAD_ARG; + goto error; + } + + #define WRITE(Var, N) { \ + if(fwrite((Var), sizeof(*(Var)), (N), stream) != (N)) { \ + res = RES_IO_ERR; \ + goto error; \ + } \ + } (void)0 + WRITE(&SVX_TREE_VERSION, 1); + WRITE(&tree->definition, 1); + WRITE(tree->lower, 3); + WRITE(tree->upper, 3); + WRITE(tree->tree_low, 3); + WRITE(tree->tree_upp, 3); + WRITE(tree->tree_size, 3); + WRITE(&tree->nleaves, 1); + WRITE(&tree->depth, 1); + WRITE(tree->frame, 3); + WRITE(&tree->type, 1); + WRITE(&tree->root, 1); + WRITE(tree->root_attr, SVX_MAX_SIZEOF_VOXEL); + #undef WRITE + + res = buffer_write(&tree->buffer, stream); + if(res != RES_OK) goto error; + +exit: + return res; +error: + goto exit; +} + /******************************************************************************* * Local functions ******************************************************************************/ @@ -187,3 +381,61 @@ error: goto exit; } +res_T +tree_read(struct svx_tree* tree, FILE* stream) +{ + int version = 0; + res_T res = RES_OK; + ASSERT(tree && stream); + + tree_clear(tree); + + #define READ(Var, N) { \ + if(fread((Var), sizeof(*(Var)), (N), stream) != (N)) { \ + if(feof(stream)) { \ + res = RES_BAD_ARG; \ + } else if(ferror(stream)) { \ + res = RES_IO_ERR; \ + } else { \ + res = RES_UNKNOWN_ERR; \ + } \ + goto error; \ + } \ + } (void)0 + + /* Currently only one version of the tree data structure could be serialized. + * The version management is thus as simple as rejecting any tree data + * structure whose version is not the current version. */ + READ(&version, 1); + if(version != SVX_TREE_VERSION) { + log_err(tree->dev, + "%s: unexpected tree version %d. Expecting a tree in version %d.\n", + FUNC_NAME, version, SVX_TREE_VERSION); + res = RES_BAD_ARG; + goto error; + } + + READ(&tree->definition, 1); + READ(tree->lower, 3); + READ(tree->upper, 3); + READ(tree->tree_low, 3); + READ(tree->tree_upp, 3); + READ(tree->tree_size, 3); + READ(&tree->nleaves, 1); + READ(&tree->depth, 1); + READ(tree->frame, 3); + READ(&tree->type, 1); + READ(&tree->root, 1); + READ(tree->root_attr, SVX_MAX_SIZEOF_VOXEL); + #undef READ + + res = buffer_read(&tree->buffer, stream); + if(res != RES_OK) goto error; + +exit: + return res; +error: + tree_clear(tree); + goto exit; +} + diff --git a/src/svx_tree.h b/src/svx_tree.h @@ -19,6 +19,11 @@ #include "svx_buffer.h" #include <rsys/ref_count.h> +/* Current version the tree data structure. One should increment it and perform + * a version management onto serialized data when the tree data structure is + * updated. */ +static const int SVX_TREE_VERSION = 0; + struct svx_tree { size_t definition; /* #voxels of the tree along its dimensions */ @@ -74,5 +79,10 @@ bintree_trace_ray void* context, struct svx_hit* hit); +extern LOCAL_SYM res_T +tree_read + (struct svx_tree* tree, + FILE* stream); + #endif /* SVX_TREE_H */ diff --git a/src/svx_tree_builder.h b/src/svx_tree_builder.h @@ -92,33 +92,6 @@ struct XD(builder) { /******************************************************************************* * Stack functions ******************************************************************************/ -#ifndef NDEBUG -static void -XD(check)(struct buffer* buf, const struct buffer_index root, size_t* nleaves) -{ - const struct buffer_xnode* node; - int ichild; - ASSERT(buf); - - node = buffer_get_node(buf, root); - FOR_EACH(ichild, 0, NCHILDREN) { - const int ichild_flag = BIT(ichild); - if((node->is_valid & ichild_flag) == 0) continue; - - if(node->is_leaf & ichild_flag) { - struct buffer_index iattr; - iattr = buffer_get_child_attr_index(buf, root, ichild); - ASSERT(buffer_get_attr(buf, iattr) != NULL); - *nleaves += 1; - } else { - struct buffer_index child; - child = buffer_get_child_node_index(buf, root, ichild); - XD(check)(buf, child, nleaves); - } - } -} -#endif - static INLINE void XD(stack_clear)(struct XD(stack)* stack) { diff --git a/src/test_svx_bintree.c b/src/test_svx_bintree.c @@ -303,6 +303,7 @@ main(int argc, char** argv) double low, upp; size_t nvxls; void* ptr = (void*)(intptr_t)0xDECAFBAD; + FILE* stream = NULL; (void)argc, (void)argv; CHK(mem_init_proxy_allocator(&allocator, &mem_default_allocator) == RES_OK); @@ -381,6 +382,22 @@ main(int argc, char** argv) CHK(tree_desc.upper[axis] == upp); test_at_accessor(tree, nvxls); + + CHK(stream = tmpfile()); + CHK(svx_tree_write(NULL, stream) == RES_BAD_ARG); + CHK(svx_tree_write(tree, NULL) == RES_BAD_ARG); + CHK(svx_tree_write(tree, stream) == RES_OK); + + CHK(svx_tree_ref_put(tree) == RES_OK); + + rewind(stream); + CHK(svx_tree_create_from_stream(NULL, stream, &tree) == RES_BAD_ARG); + CHK(svx_tree_create_from_stream(dev, NULL, &tree) == RES_BAD_ARG); + CHK(svx_tree_create_from_stream(dev, stream, NULL) == RES_BAD_ARG); + CHK(svx_tree_create_from_stream(dev, stream, &tree) == RES_OK); + fclose(stream); + + test_at_accessor(tree, nvxls); CHK(svx_tree_ref_put(tree) == RES_OK); build_ctx.max_depth = (size_t)log2i((int)round_up_pow2(nvxls)); diff --git a/src/test_svx_bintree_trace_ray.c b/src/test_svx_bintree_trace_ray.c @@ -157,7 +157,7 @@ hit_filter3 } static void -draw_image(FILE* stream, struct svx_tree* btree) +draw_image(struct image* img, struct svx_tree* btree) { struct camera cam; unsigned char* pixels = NULL; @@ -166,17 +166,15 @@ draw_image(FILE* stream, struct svx_tree* btree) const double pos[3] = { 0, 0, 0}; const double tgt[3] = { 0, 0, 1}; const double up[3] = { 1, 0, 0}; - struct image img; double pix[2]; size_t ix, iy; - CHK(btree); + CHK(btree && img); camera_init(&cam, pos, tgt, up, (double)width/(double)height); - image_init(NULL, &img); - CHK(image_setup(&img, width, height, sizeof_image_format(IMAGE_RGB8)*width, + CHK(image_setup(img, width, height, sizeof_image_format(IMAGE_RGB8)*width, IMAGE_RGB8, NULL) == RES_OK); - pixels = (unsigned char*)img.pixels; + pixels = (unsigned char*)img->pixels; FOR_EACH(iy, 0, height) { pix[1] = (double)iy / (double)height; @@ -204,16 +202,16 @@ draw_image(FILE* stream, struct svx_tree* btree) } } } - - CHK(image_write_ppm_stream(&img, 0, stream) == RES_OK); - image_release(&img); } int main(int argc, char** argv) { + struct image img, img2; + FILE* stream = NULL; struct svx_device* dev = NULL; struct svx_tree* btree = NULL; + struct svx_tree* btree2 = NULL; struct svx_voxel_desc voxel_desc = SVX_VOXEL_DESC_NULL; struct svx_voxel voxel = SVX_VOXEL_NULL; struct svx_hit hit; @@ -243,11 +241,19 @@ main(int argc, char** argv) CHK(svx_bintree_create(dev, lower, upper, definition, scn.axis, &voxel_desc, &btree) == RES_OK); + /* Duplicate the binary tree through serialization */ + CHK(stream = tmpfile()); + CHK(svx_tree_write(btree, stream) == RES_OK); + CHK(svx_tree_create_from_stream(dev, stream, &btree2) == RES_BAD_ARG); + rewind(stream); + CHK(svx_tree_create_from_stream(dev, stream, &btree2) == RES_OK); + fclose(stream); + + #define RT svx_tree_trace_ray d3(r.org, -1.01, 0, 0); d3(r.dir, -1, 0, 0); d2(r.range, 0, DBL_MAX); - #define RT svx_tree_trace_ray CHK(RT(NULL, r.org, r.dir, r.range, NULL, NULL, NULL, &hit) == RES_BAD_ARG); CHK(RT(btree, NULL, r.dir, r.range, NULL, NULL, NULL, &hit) == RES_BAD_ARG); CHK(RT(btree, r.org, NULL, r.range, NULL, NULL, NULL, &hit) == RES_BAD_ARG); @@ -441,9 +447,21 @@ main(int argc, char** argv) CHK(SVX_HIT_NONE(&hit)); CHK(accum == 1); - draw_image(stdout, btree); + image_init(NULL, &img); + image_init(NULL, &img2); + draw_image(&img, btree); + draw_image(&img2, btree2); + + /* Check that using oct or oct2 produces effectively the same image */ + check_img_eq(&img, &img2); + + /* Write the drawn image on stdout */ + CHK(image_write_ppm_stream(&img, 0/*binary*/, stdout) == RES_OK); + image_release(&img); + image_release(&img2); CHK(svx_tree_ref_put(btree) == RES_OK); + CHK(svx_tree_ref_put(btree2) == RES_OK); CHK(svx_device_ref_put(dev) == RES_OK); CHK(mem_allocated_size() == 0); return 0; diff --git a/src/test_svx_octree.c b/src/test_svx_octree.c @@ -395,6 +395,7 @@ main(int argc, char** argv) size_t nvxls[3]; struct leaves_context ctx; void* ptr = (void*)(intptr_t)0xDECAFBAD; + FILE* stream = NULL; (void)argc, (void)argv; CHK(mem_init_proxy_allocator(&allocator, &mem_default_allocator) == RES_OK); @@ -478,6 +479,21 @@ main(int argc, char** argv) test_at_accessor(oct, nvxls); + CHK(stream = tmpfile()); + CHK(svx_tree_write(NULL, stream) == RES_BAD_ARG); + CHK(svx_tree_write(oct, NULL) == RES_BAD_ARG); + CHK(svx_tree_write(oct, stream) == RES_OK); + + CHK(svx_tree_ref_put(oct) == RES_OK); + + rewind(stream); + CHK(svx_tree_create_from_stream(NULL, stream, &oct) == RES_BAD_ARG); + CHK(svx_tree_create_from_stream(dev, NULL, &oct) == RES_BAD_ARG); + CHK(svx_tree_create_from_stream(dev, stream, NULL) == RES_BAD_ARG); + CHK(svx_tree_create_from_stream(dev, stream, &oct) == RES_OK); + fclose(stream); + + test_at_accessor(oct, nvxls); CHK(svx_tree_ref_put(oct) == RES_OK); nvxls[0] = nvxls[1] = nvxls[2] = 32; diff --git a/src/test_svx_octree_trace_ray.c b/src/test_svx_octree_trace_ray.c @@ -188,7 +188,7 @@ hit_challenge(const struct svx_hit* hit, void* context) } static void -draw_image(FILE* stream, struct svx_tree* oct, const struct scene* scn) +draw_image(struct image* img, struct svx_tree* oct, const struct scene* scn) { char buf[32]; struct time t0, t1; @@ -199,17 +199,15 @@ draw_image(FILE* stream, struct svx_tree* oct, const struct scene* scn) const double pos[3] = { 0,-1.5, 0}; const double tgt[3] = { 0, 0, 0}; const double up[3] = { 0, 0, 1}; - struct image img; double pix[2]; size_t ix, iy; - CHK(oct); + CHK(oct && img); camera_init(&cam, pos, tgt, up, (double)width/(double)height); - image_init(NULL, &img); - CHK(image_setup(&img, width, height, sizeof_image_format(IMAGE_RGB8)*width, + CHK(image_setup(img, width, height, sizeof_image_format(IMAGE_RGB8)*width, IMAGE_RGB8, NULL) == RES_OK); - pixels = (unsigned char*)img.pixels; + pixels = (unsigned char*)img->pixels; time_current(&t0); FOR_EACH(iy, 0, height) { @@ -245,9 +243,6 @@ draw_image(FILE* stream, struct svx_tree* oct, const struct scene* scn) time_sub(&t0, time_current(&t1), &t0); time_dump(&t0, TIME_ALL, NULL, buf, sizeof(buf)); fprintf(stderr, "Render time: %s\n", buf); - - CHK(image_write_ppm_stream(&img, 0, stream) == RES_OK); - image_release(&img); } int @@ -255,8 +250,11 @@ main(int argc, char** argv) { struct scene scn; struct ray r; + struct image img, img2; + FILE* stream = NULL; struct svx_device* dev = NULL; struct svx_tree* oct = NULL; + struct svx_tree* oct2 = NULL; struct svx_tree_desc tree_desc = SVX_TREE_DESC_NULL; struct svx_voxel_desc voxel_desc = SVX_VOXEL_DESC_NULL; struct svx_hit hit = SVX_HIT_NULL; @@ -295,6 +293,14 @@ main(int argc, char** argv) CHK(svx_octree_create(dev, lower, upper, def, &voxel_desc, &oct) == RES_OK); + /* Duplicate the octree through serialization */ + CHK(stream = tmpfile()); + CHK(svx_tree_write(oct, stream) == RES_OK); + CHK(svx_tree_create_from_stream(dev, stream, &oct2) == RES_BAD_ARG); + rewind(stream); + CHK(svx_tree_create_from_stream(dev, stream, &oct2) == RES_OK); + fclose(stream); + /*dump_data(stdout, oct, CHAR__, 1, write_scalars);*/ #define RT svx_tree_trace_ray @@ -386,9 +392,21 @@ main(int argc, char** argv) CHK(SVX_HIT_NONE(&hit)); CHK(accum != 0); - draw_image(stdout, oct, &scn); + image_init(NULL, &img); + image_init(NULL, &img2); + draw_image(&img, oct, &scn); + draw_image(&img2, oct2, &scn); + + /* Check that using oct or oct2 produces effectively the same image */ + check_img_eq(&img, &img2); + + /* Write the drawn image on stdout */ + CHK(image_write_ppm_stream(&img, 0/*binary*/, stdout) == RES_OK); + image_release(&img); + image_release(&img2); CHK(svx_tree_ref_put(oct) == RES_OK); + CHK(svx_tree_ref_put(oct2) == RES_OK); CHK(svx_device_ref_put(dev) == RES_OK); CHK(mem_allocated_size() == 0); return 0; diff --git a/src/test_svx_utils.h b/src/test_svx_utils.h @@ -18,6 +18,7 @@ #include <rsys/double2.h> #include <rsys/double3.h> +#include <rsys/image.h> #include <rsys/mem_allocator.h> #include <stdio.h> @@ -176,6 +177,33 @@ dump_data CHK(svx_tree_for_each_leaf(tree, write_leaf_data, stream) == RES_OK); } +static INLINE void +check_img_eq(const struct image* img0, const struct image* img1) +{ + size_t ix, iy; + size_t pixsz; + + CHK(img0 && img1); + CHK(img0->format == IMAGE_RGB8); + CHK(img1->format == IMAGE_RGB8); + CHK(img0->width == img1->width); + CHK(img0->height == img1->height); + CHK(img0->pitch == img1->pitch); + + pixsz = sizeof_image_format(img0->format); + + FOR_EACH(iy, 0, img0->height) { + const char* row0 = img0->pixels + iy * img0->pitch; + const char* row1 = img1->pixels + iy * img1->pitch; + FOR_EACH(ix, 0, img0->width) { + const uint8_t* pix0 = (const uint8_t*)(row0 + ix*pixsz); + const uint8_t* pix1 = (const uint8_t*)(row1 + ix*pixsz); + CHK(pix0[0] == pix1[0]); + CHK(pix0[1] == pix1[1]); + CHK(pix0[2] == pix1[2]); + } + } +} static INLINE void check_memory_allocator(struct mem_allocator* allocator)